Subversion Repositories public iLand

Rev

Rev 608 | Rev 639 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1
 
117 Werner 2
#include "global.h"
488 werner 3
#include "math.h"
117 Werner 4
#include "tree.h"
3 Werner 5
 
83 Werner 6
#include "grid.h"
3 Werner 7
 
83 Werner 8
#include "stamp.h"
90 Werner 9
#include "species.h"
189 iland 10
#include "resourceunit.h"
151 iland 11
#include "model.h"
468 werner 12
#include "snag.h"
38 Werner 13
 
110 Werner 14
// static varaibles
106 Werner 15
FloatGrid *Tree::mGrid = 0;
151 iland 16
HeightGrid *Tree::mHeightGrid = 0;
40 Werner 17
int Tree::m_statPrint=0;
48 Werner 18
int Tree::m_statAboveZ=0;
105 Werner 19
int Tree::m_statCreated=0;
40 Werner 20
int Tree::m_nextId=0;
21
 
158 werner 22
 
257 werner 23
 
158 werner 24
/** get distance and direction between two points.
25
  returns the distance (m), and the angle between PStart and PEnd (radians) in referenced param rAngle. */
26
float dist_and_direction(const QPointF &PStart, const QPointF &PEnd, float &rAngle)
151 iland 27
{
158 werner 28
    float dx = PEnd.x() - PStart.x();
29
    float dy = PEnd.y() - PStart.y();
30
    float d = sqrt(dx*dx + dy*dy);
31
    // direction:
32
    rAngle = atan2(dx, dy);
33
    return d;
151 iland 34
}
35
 
158 werner 36
 
110 Werner 37
// lifecycle
3 Werner 38
Tree::Tree()
39
{
149 werner 40
    mDbh = mHeight = 0;
41
    mRU = 0; mSpecies = 0;
169 werner 42
    mFlags = mAge = 0;
276 werner 43
    mOpacity=mFoliageMass=mWoodyMass=mCoarseRootMass=mFineRootMass=mLeafArea=0.;
159 werner 44
    mDbhDelta=mNPPReserve=mLRI=mStressIndex=0.;
264 werner 45
    mLightResponse = 0.;
106 Werner 46
    mId = m_nextId++;
105 Werner 47
    m_statCreated++;
3 Werner 48
}
38 Werner 49
 
407 werner 50
float Tree::crownRadius() const
51
{
52
    Q_ASSERT(mStamp!=0);
53
    return mStamp->crownRadius();
54
}
55
 
476 werner 56
float Tree::biomassBranch() const
57
{
58
    return mSpecies->biomassBranch(mDbh);
59
}
60
 
158 werner 61
void Tree::setGrid(FloatGrid* gridToStamp, Grid<HeightGridValue> *dominanceGrid)
3 Werner 62
{
158 werner 63
    mGrid = gridToStamp; mHeightGrid = dominanceGrid;
3 Werner 64
}
65
 
125 Werner 66
/// dumps some core variables of a tree to a string.
67
QString Tree::dump()
68
{
69
    QString result = QString("id %1 species %2 dbh %3 h %4 x/y %5/%6 ru# %7 LRI %8")
159 werner 70
                            .arg(mId).arg(species()->id()).arg(mDbh).arg(mHeight)
156 werner 71
                            .arg(position().x()).arg(position().y())
125 Werner 72
                            .arg(mRU->index()).arg(mLRI);
73
    return result;
74
}
3 Werner 75
 
129 Werner 76
void Tree::dumpList(DebugList &rTargetList)
77
{
159 werner 78
    rTargetList << mId << species()->id() << mDbh << mHeight  << position().x() << position().y()   << mRU->index() << mLRI
276 werner 79
                << mWoodyMass << mCoarseRootMass << mFoliageMass << mLeafArea;
129 Werner 80
}
81
 
38 Werner 82
void Tree::setup()
83
{
106 Werner 84
    if (mDbh<=0 || mHeight<=0)
38 Werner 85
        return;
86
    // check stamp
159 werner 87
    Q_ASSERT_X(species()!=0, "Tree::setup()", "species is NULL");
88
    mStamp = species()->stamp(mDbh, mHeight);
505 werner 89
    if (!mStamp) {
90
        throw IException("Tree::setup() with invalid stamp!");
91
    }
110 Werner 92
 
159 werner 93
    mFoliageMass = species()->biomassFoliage(mDbh);
276 werner 94
    mCoarseRootMass = species()->biomassRoot(mDbh); // coarse root (allometry)
95
    mFineRootMass = mFoliageMass * species()->finerootFoliageRatio(); //  fine root (size defined  by finerootFoliageRatio)
159 werner 96
    mWoodyMass = species()->biomassWoody(mDbh);
110 Werner 97
 
137 Werner 98
    // LeafArea[m2] = LeafMass[kg] * specificLeafArea[m2/kg]
159 werner 99
    mLeafArea = mFoliageMass * species()->specificLeafArea();
276 werner 100
    mOpacity = 1. - exp(- Model::settings().lightExtinctionCoefficientOpacity * mLeafArea / mStamp->crownArea());
101
    mNPPReserve = (1+species()->finerootFoliageRatio())*mFoliageMass; // initial value
137 Werner 102
    mDbhDelta = 0.1; // initial value: used in growth() to estimate diameter increment
376 werner 103
 
38 Werner 104
}
39 Werner 105
 
388 werner 106
void Tree::setAge(const int age, const float treeheight)
107
{
108
    mAge = age;
109
    if (age==0) {
110
        // estimate age using the tree height
111
        mAge = mSpecies->estimateAge(treeheight);
112
    }
113
}
114
 
110 Werner 115
//////////////////////////////////////////////////
116
////  Light functions (Pattern-stuff)
117
//////////////////////////////////////////////////
118
 
70 Werner 119
#define NOFULLDBG
77 Werner 120
//#define NOFULLOPT
39 Werner 121
 
40 Werner 122
 
158 werner 123
void Tree::applyLIP()
77 Werner 124
{
144 Werner 125
    if (!mStamp)
126
        return;
106 Werner 127
    Q_ASSERT(mGrid!=0 && mStamp!=0 && mRU!=0);
156 werner 128
    QPoint pos = mPositionIndex;
106 Werner 129
    int offset = mStamp->offset();
77 Werner 130
    pos-=QPoint(offset, offset);
131
 
132
    float local_dom; // height of Z* on the current position
133
    int x,y;
401 werner 134
    float value, z, z_zstar;
106 Werner 135
    int gr_stamp = mStamp->size();
77 Werner 136
    int grid_x, grid_y;
399 werner 137
    float *grid_value_ptr;
106 Werner 138
    if (!mGrid->isIndexValid(pos) || !mGrid->isIndexValid(pos+QPoint(gr_stamp, gr_stamp))) {
407 werner 139
        // this should not happen because of the buffer
77 Werner 140
        return;
141
    }
399 werner 142
    grid_y = pos.y();
77 Werner 143
    for (y=0;y<gr_stamp; ++y) {
403 werner 144
 
399 werner 145
        grid_value_ptr = mGrid->ptr(pos.x(), grid_y);
146
        grid_x = pos.x();
77 Werner 147
        for (x=0;x<gr_stamp;++x) {
148
            // suppose there is no stamping outside
149
 
399 werner 150
            local_dom = mHeightGrid->valueAtIndex(grid_x/cPxPerHeight, grid_y/cPxPerHeight).height;
403 werner 151
            z = std::max(mHeight - (*mStamp).distanceToCenter(x,y), 0.f); // distance to center = height (45° line)
401 werner 152
            z_zstar = (z>=local_dom)?1.f:z/local_dom;
106 Werner 153
            value = (*mStamp)(x,y); // stampvalue
401 werner 154
            value = 1. - value*mOpacity * z_zstar; // calculated value
155
            // old: value = 1. - value*mOpacity / local_dom; // calculated value
77 Werner 156
            value = qMax(value, 0.02f); // limit value
157
 
399 werner 158
            *grid_value_ptr++ *= value;
403 werner 159
 
160
            grid_x++;
77 Werner 161
        }
403 werner 162
        grid_y++;
77 Werner 163
    }
164
 
165
    m_statPrint++; // count # of stamp applications...
166
}
167
 
155 werner 168
/// helper function for gluing the edges together
169
/// index: index at grid
170
/// count: number of pixels that are the simulation area (e.g. 100m and 2m pixel -> 50)
171
/// buffer: size of buffer around simulation area (in pixels)
295 werner 172
inline int torusIndex(int index, int count, int buffer, int ru_index)
155 werner 173
{
295 werner 174
    return buffer + ru_index + (index-buffer+count)%count;
155 werner 175
}
62 Werner 176
 
155 werner 177
 
178
/** Apply LIPs. This "Torus" functions wraps the influence at the edges of a 1ha simulation area.
179
  */
158 werner 180
void Tree::applyLIP_torus()
155 werner 181
{
182
    if (!mStamp)
183
        return;
184
    Q_ASSERT(mGrid!=0 && mStamp!=0 && mRU!=0);
295 werner 185
    int bufferOffset = mGrid->indexAt(QPointF(0.,0.)).x(); // offset of buffer
387 werner 186
    QPoint pos = QPoint((mPositionIndex.x()-bufferOffset)%cPxPerRU  + bufferOffset,
187
                        (mPositionIndex.y()-bufferOffset)%cPxPerRU + bufferOffset); // offset within the ha
295 werner 188
    QPoint ru_offset = QPoint(mPositionIndex.x() - pos.x(), mPositionIndex.y() - pos.y()); // offset of the corner of the resource index
155 werner 189
 
190
    int offset = mStamp->offset();
191
    pos-=QPoint(offset, offset);
192
 
193
    float local_dom; // height of Z* on the current position
194
    int x,y;
195
    float value;
196
    int gr_stamp = mStamp->size();
197
    int grid_x, grid_y;
198
    float *grid_value;
199
    if (!mGrid->isIndexValid(pos) || !mGrid->isIndexValid(pos+QPoint(gr_stamp, gr_stamp))) {
200
        // todo: in this case we should use another algorithm!!! necessary????
201
        return;
202
    }
407 werner 203
    float z, z_zstar;
155 werner 204
    int xt, yt; // wraparound coordinates
205
    for (y=0;y<gr_stamp; ++y) {
206
        grid_y = pos.y() + y;
387 werner 207
        yt = torusIndex(grid_y, cPxPerRU,bufferOffset, ru_offset.y()); // 50 cells per 100m
155 werner 208
        for (x=0;x<gr_stamp;++x) {
209
            // suppose there is no stamping outside
210
            grid_x = pos.x() + x;
387 werner 211
            xt = torusIndex(grid_x,cPxPerRU,bufferOffset, ru_offset.x());
155 werner 212
 
387 werner 213
            local_dom = mHeightGrid->valueAtIndex(xt/cPxPerHeight,yt/cPxPerHeight).height;
407 werner 214
 
215
            z = std::max(mHeight - (*mStamp).distanceToCenter(x,y), 0.f); // distance to center = height (45° line)
216
            z_zstar = (z>=local_dom)?1.f:z/local_dom;
155 werner 217
            value = (*mStamp)(x,y); // stampvalue
407 werner 218
            value = 1. - value*mOpacity * z_zstar; // calculated value
219
            // old: value = 1. - value*mOpacity / local_dom; // calculated value
155 werner 220
            value = qMax(value, 0.02f); // limit value
221
 
222
            grid_value = mGrid->ptr(xt, yt); // use wraparound coordinates
223
            *grid_value *= value;
224
        }
225
    }
226
 
227
    m_statPrint++; // count # of stamp applications...
228
}
229
 
74 Werner 230
/** heightGrid()
231
  This function calculates the "dominant height field". This grid is coarser as the fine-scaled light-grid.
232
*/
233
void Tree::heightGrid()
234
{
235
 
387 werner 236
    QPoint p = QPoint(mPositionIndex.x()/cPxPerHeight, mPositionIndex.y()/cPxPerHeight); // pos of tree on height grid
74 Werner 237
 
151 iland 238
    // count trees that are on height-grid cells (used for stockable area)
285 werner 239
    mHeightGrid->valueAtIndex(p).increaseCount();
401 werner 240
    if (mHeight > mHeightGrid->valueAtIndex(p).height)
241
        mHeightGrid->valueAtIndex(p).height=mHeight;
406 werner 242
 
243
    int r = mStamp->reader()->offset(); // distance between edge and the center pixel. e.g.: if r = 2 -> stamp=5x5
244
    int index_eastwest = mPositionIndex.x() % cPxPerHeight; // 4: very west, 0 east edge
245
    int index_northsouth = mPositionIndex.y() % cPxPerHeight; // 4: northern edge, 0: southern edge
246
    if (index_eastwest - r < 0) { // east
410 werner 247
        mHeightGrid->valueAtIndex(p.x()-1, p.y()).height=qMax(mHeightGrid->valueAtIndex(p.x()-1, p.y()).height,mHeight);
406 werner 248
    }
249
    if (index_eastwest + r >= cPxPerHeight) {  // west
410 werner 250
        mHeightGrid->valueAtIndex(p.x()+1, p.y()).height=qMax(mHeightGrid->valueAtIndex(p.x()+1, p.y()).height,mHeight);
406 werner 251
    }
252
    if (index_northsouth - r < 0) {  // south
410 werner 253
        mHeightGrid->valueAtIndex(p.x(), p.y()-1).height=qMax(mHeightGrid->valueAtIndex(p.x(), p.y()-1).height,mHeight);
406 werner 254
    }
255
    if (index_northsouth + r >= cPxPerHeight) {  // north
410 werner 256
        mHeightGrid->valueAtIndex(p.x(), p.y()+1).height=qMax(mHeightGrid->valueAtIndex(p.x(), p.y()+1).height,mHeight);
406 werner 257
    }
258
 
259
 
401 werner 260
    // without spread of the height grid
151 iland 261
 
401 werner 262
//    // height of Z*
263
//    const float cellsize = mHeightGrid->cellsize();
264
//
265
//    int index_eastwest = mPositionIndex.x() % cPxPerHeight; // 4: very west, 0 east edge
266
//    int index_northsouth = mPositionIndex.y() % cPxPerHeight; // 4: northern edge, 0: southern edge
267
//    int dist[9];
268
//    dist[3] = index_northsouth * 2 + 1; // south
269
//    dist[1] = index_eastwest * 2 + 1; // west
270
//    dist[5] = 10 - dist[3]; // north
271
//    dist[7] = 10 - dist[1]; // east
272
//    dist[8] = qMax(dist[5], dist[7]); // north-east
273
//    dist[6] = qMax(dist[3], dist[7]); // south-east
274
//    dist[0] = qMax(dist[3], dist[1]); // south-west
275
//    dist[2] = qMax(dist[5], dist[1]); // north-west
276
//    dist[4] = 0; // center cell
277
//    /* the scheme of indices is as follows:  if sign(ix)= -1, if ix<0, 0 for ix=0, 1 for ix>0 (detto iy), then:
278
//       index = 4 + 3*sign(ix) + sign(iy) transforms combinations of directions to unique ids (0..8), which are used above.
279
//        e.g.: sign(ix) = -1, sign(iy) = 1 (=north-west) -> index = 4 + -3 + 1 = 2
280
//    */
281
//
282
//
283
//    int ringcount = int(floor(mHeight / cellsize)) + 1;
284
//    int ix, iy;
285
//    int ring;
286
//    float hdom;
287
//
288
//    for (ix=-ringcount;ix<=ringcount;ix++)
289
//        for (iy=-ringcount; iy<=+ringcount; iy++) {
290
//        ring = qMax(abs(ix), abs(iy));
291
//        QPoint pos(ix+p.x(), iy+p.y());
292
//        if (mHeightGrid->isIndexValid(pos)) {
293
//            float &rHGrid = mHeightGrid->valueAtIndex(pos).height;
294
//            if (rHGrid > mHeight) // skip calculation if grid is higher than tree
295
//                continue;
296
//            int direction = 4 + (ix?(ix<0?-3:3):0) + (iy?(iy<0?-1:1):0); // 4 + 3*sgn(x) + sgn(y)
297
//            hdom = mHeight - dist[direction];
298
//            if (ring>1)
299
//                hdom -= (ring-1)*10;
300
//
301
//            rHGrid = qMax(rHGrid, hdom); // write value
302
//        } // is valid
303
//    } // for (y)
39 Werner 304
}
40 Werner 305
 
407 werner 306
void Tree::heightGrid_torus()
307
{
308
    // height of Z*
155 werner 309
 
407 werner 310
    QPoint p = QPoint(mPositionIndex.x()/cPxPerHeight, mPositionIndex.y()/cPxPerHeight); // pos of tree on height grid
311
    int bufferOffset = mHeightGrid->indexAt(QPointF(0.,0.)).x(); // offset of buffer (i.e.: size of buffer in height-pixels)
312
    p.setX((p.x()-bufferOffset)%10 + bufferOffset); // 10: 10 x 10m pixeln in 100m
313
    p.setY((p.y()-bufferOffset)%10 + bufferOffset);
155 werner 314
 
407 werner 315
 
316
    // torus coordinates: ru_offset = coords of lower left corner of 1ha patch
317
    QPoint ru_offset =QPoint(mPositionIndex.x()/cPxPerHeight - p.x(), mPositionIndex.y()/cPxPerHeight - p.y());
318
 
319
    // count trees that are on height-grid cells (used for stockable area)
320
    HeightGridValue &v = mHeightGrid->valueAtIndex(torusIndex(p.x(),10,bufferOffset,ru_offset.x()),
321
                                                   torusIndex(p.y(),10,bufferOffset,ru_offset.y()));
322
    v.increaseCount();
323
    v.height = qMax(v.height, mHeight);
324
 
325
 
326
    int r = mStamp->reader()->offset(); // distance between edge and the center pixel. e.g.: if r = 2 -> stamp=5x5
327
    int index_eastwest = mPositionIndex.x() % cPxPerHeight; // 4: very west, 0 east edge
328
    int index_northsouth = mPositionIndex.y() % cPxPerHeight; // 4: northern edge, 0: southern edge
329
    if (index_eastwest - r < 0) { // east
330
        HeightGridValue &v = mHeightGrid->valueAtIndex(torusIndex(p.x()-1,10,bufferOffset,ru_offset.x()),
331
                                                       torusIndex(p.y(),10,bufferOffset,ru_offset.y()));
410 werner 332
        v.height = qMax(v.height, mHeight);
407 werner 333
    }
334
    if (index_eastwest + r >= cPxPerHeight) {  // west
335
        HeightGridValue &v = mHeightGrid->valueAtIndex(torusIndex(p.x()+1,10,bufferOffset,ru_offset.x()),
336
                                                       torusIndex(p.y(),10,bufferOffset,ru_offset.y()));
410 werner 337
        v.height = qMax(v.height, mHeight);
407 werner 338
    }
339
    if (index_northsouth - r < 0) {  // south
340
        HeightGridValue &v = mHeightGrid->valueAtIndex(torusIndex(p.x(),10,bufferOffset,ru_offset.x()),
341
                                                       torusIndex(p.y()-1,10,bufferOffset,ru_offset.y()));
410 werner 342
        v.height = qMax(v.height, mHeight);
407 werner 343
    }
344
    if (index_northsouth + r >= cPxPerHeight) {  // north
345
        HeightGridValue &v = mHeightGrid->valueAtIndex(torusIndex(p.x(),10,bufferOffset,ru_offset.x()),
346
                                                       torusIndex(p.y()+1,10,bufferOffset,ru_offset.y()));
410 werner 347
        v.height = qMax(v.height, mHeight);
407 werner 348
    }
349
 
350
 
351
 
352
 
353
//    int index_eastwest = mPositionIndex.x() % cPxPerHeight; // 4: very west, 0 east edge
354
//    int index_northsouth = mPositionIndex.y() % cPxPerHeight; // 4: northern edge, 0: southern edge
355
//    int dist[9];
356
//    dist[3] = index_northsouth * 2 + 1; // south
357
//    dist[1] = index_eastwest * 2 + 1; // west
358
//    dist[5] = 10 - dist[3]; // north
359
//    dist[7] = 10 - dist[1]; // east
360
//    dist[8] = qMax(dist[5], dist[7]); // north-east
361
//    dist[6] = qMax(dist[3], dist[7]); // south-east
362
//    dist[0] = qMax(dist[3], dist[1]); // south-west
363
//    dist[2] = qMax(dist[5], dist[1]); // north-west
364
//    dist[4] = 0; // center cell
365
//    /* the scheme of indices is as follows:  if sign(ix)= -1, if ix<0, 0 for ix=0, 1 for ix>0 (detto iy), then:
366
//       index = 4 + 3*sign(ix) + sign(iy) transforms combinations of directions to unique ids (0..8), which are used above.
367
//        e.g.: sign(ix) = -1, sign(iy) = 1 (=north-west) -> index = 4 + -3 + 1 = 2
368
//    */
369
//
370
//
371
//    int ringcount = int(floor(mHeight / cellsize)) + 1;
372
//    int ix, iy;
373
//    int ring;
374
//    float hdom;
375
//    for (ix=-ringcount;ix<=ringcount;ix++)
376
//        for (iy=-ringcount; iy<=+ringcount; iy++) {
377
//        ring = qMax(abs(ix), abs(iy));
378
//        QPoint pos(ix+p.x(), iy+p.y());
379
//        QPoint p_torus(torusIndex(pos.x(),10,bufferOffset,ru_offset.x()),
380
//                       torusIndex(pos.y(),10,bufferOffset,ru_offset.y()));
381
//        if (mHeightGrid->isIndexValid(p_torus)) {
382
//            float &rHGrid = mHeightGrid->valueAtIndex(p_torus.x(),p_torus.y()).height;
383
//            if (rHGrid > mHeight) // skip calculation if grid is higher than tree
384
//                continue;
385
//            int direction = 4 + (ix?(ix<0?-3:3):0) + (iy?(iy<0?-1:1):0); // 4 + 3*sgn(x) + sgn(y)
386
//            hdom = mHeight - dist[direction];
387
//            if (ring>1)
388
//                hdom -= (ring-1)*10;
389
//
390
//            rHGrid = qMax(rHGrid, hdom); // write value
391
//        } // is valid
392
//    } // for (y)
393
}
394
 
395
 
396
 
158 werner 397
void Tree::readLIF()
40 Werner 398
{
106 Werner 399
    if (!mStamp)
155 werner 400
        return;
401
    const Stamp *reader = mStamp->reader();
402
    if (!reader)
403
        return;
156 werner 404
    QPoint pos_reader = mPositionIndex;
155 werner 405
 
406
    int offset_reader = reader->offset();
407
    int offset_writer = mStamp->offset();
408
    int d_offset = offset_writer - offset_reader; // offset on the *stamp* to the crown-cells
409
 
410
    pos_reader-=QPoint(offset_reader, offset_reader);
40 Werner 411
 
155 werner 412
    float local_dom;
413
 
40 Werner 414
    int x,y;
415
    double sum=0.;
155 werner 416
    double value, own_value;
417
    float *grid_value;
403 werner 418
    float z, z_zstar;
155 werner 419
    int reader_size = reader->size();
420
    int rx = pos_reader.x();
421
    int ry = pos_reader.y();
422
    for (y=0;y<reader_size; ++y, ++ry) {
423
        grid_value = mGrid->ptr(rx, ry);
424
        for (x=0;x<reader_size;++x) {
425
 
387 werner 426
            local_dom = mHeightGrid->valueAtIndex((rx+x)/cPxPerHeight, ry/cPxPerHeight).height; // ry: gets ++ed in outer loop, rx not
403 werner 427
            z = std::max(mHeight - reader->distanceToCenter(x,y), 0.f); // distance to center = height (45° line)
428
            z_zstar = (z>=local_dom)?1.f:z/local_dom;
155 werner 429
 
403 werner 430
            own_value = 1. - mStamp->offsetValue(x,y,d_offset)*mOpacity * z_zstar;
431
            // old: own_value = 1. - mStamp->offsetValue(x,y,d_offset)*mOpacity / local_dom; // old: dom_height;
155 werner 432
            own_value = qMax(own_value, 0.02);
433
            value =  *grid_value++ / own_value; // remove impact of focal tree
403 werner 434
 
435
            //qDebug() << x << y << local_dom << z << z_zstar << own_value << value << *(grid_value-1) << (*reader)(x,y) << mStamp->offsetValue(x,y,d_offset);
155 werner 436
            //if (value>0.)
437
            sum += value * (*reader)(x,y);
438
 
40 Werner 439
        }
440
    }
155 werner 441
    mLRI = sum;
426 werner 442
    // LRI correction...
443
    double hrel = mHeight / mHeightGrid->valueAtIndex(mPositionIndex.x()/cPxPerHeight, mPositionIndex.y()/cPxPerHeight).height;
444
    if (hrel<1.)
445
        mLRI = species()->speciesSet()->LRIcorrection(mLRI, hrel);
446
 
48 Werner 447
    // read dominance field...
155 werner 448
    // this applies only if some trees are potentially *higher* than the dominant height grid
449
    //if (dom_height < m_Height) {
48 Werner 450
        // if tree is higher than Z*, the dominance height
451
        // a part of the crown is in "full light".
155 werner 452
    //    m_statAboveZ++;
453
    //    mImpact = 1. - (1. - mImpact)*dom_height/m_Height;
454
    //}
403 werner 455
 
155 werner 456
    if (mLRI > 1.)
457
        mLRI = 1.;
206 werner 458
 
459
    // Finally, add LRI of this Tree to the ResourceUnit!
251 werner 460
    mRU->addWLA(mLeafArea, mLRI);
206 werner 461
 
212 werner 462
 
155 werner 463
    //qDebug() << "Tree #"<< id() << "value" << sum << "Impact" << mImpact;
206 werner 464
    //mRU->addWLA(mLRI*mLeafArea, mLeafArea);
40 Werner 465
}
466
 
155 werner 467
/// Torus version of read stamp (glued edges)
158 werner 468
void Tree::readLIF_torus()
78 Werner 469
{
106 Werner 470
    if (!mStamp)
107 Werner 471
        return;
106 Werner 472
    const Stamp *reader = mStamp->reader();
78 Werner 473
    if (!reader)
107 Werner 474
        return;
295 werner 475
    int bufferOffset = mGrid->indexAt(QPointF(0.,0.)).x(); // offset of buffer
78 Werner 476
 
387 werner 477
    QPoint pos_reader = QPoint((mPositionIndex.x()-bufferOffset)%cPxPerRU + bufferOffset,
478
                               (mPositionIndex.y()-bufferOffset)%cPxPerRU + bufferOffset); // offset within the ha
295 werner 479
    QPoint ru_offset = QPoint(mPositionIndex.x() - pos_reader.x(), mPositionIndex.y() - pos_reader.y()); // offset of the corner of the resource index
480
 
78 Werner 481
    int offset_reader = reader->offset();
106 Werner 482
    int offset_writer = mStamp->offset();
78 Werner 483
    int d_offset = offset_writer - offset_reader; // offset on the *stamp* to the crown-cells
484
 
485
    pos_reader-=QPoint(offset_reader, offset_reader);
486
 
487
    float local_dom;
488
 
489
    int x,y;
490
    double sum=0.;
491
    double value, own_value;
492
    float *grid_value;
407 werner 493
    float z, z_zstar;
78 Werner 494
    int reader_size = reader->size();
495
    int rx = pos_reader.x();
496
    int ry = pos_reader.y();
155 werner 497
    int xt, yt; // wrapped coords
498
 
397 werner 499
    for (y=0;y<reader_size; ++y) {
500
        yt = torusIndex(ry+y,cPxPerRU, bufferOffset, ru_offset.y());
78 Werner 501
        for (x=0;x<reader_size;++x) {
387 werner 502
            xt = torusIndex(rx+x,cPxPerRU, bufferOffset, ru_offset.x());
155 werner 503
            grid_value = mGrid->ptr(xt,yt);
407 werner 504
 
387 werner 505
            local_dom = mHeightGrid->valueAtIndex(xt/cPxPerHeight, yt/cPxPerHeight).height; // ry: gets ++ed in outer loop, rx not
407 werner 506
            z = std::max(mHeight - reader->distanceToCenter(x,y), 0.f); // distance to center = height (45° line)
507
            z_zstar = (z>=local_dom)?1.f:z/local_dom;
125 Werner 508
 
407 werner 509
            own_value = 1. - mStamp->offsetValue(x,y,d_offset)*mOpacity * z_zstar;
510
            // old: own_value = 1. - mStamp->offsetValue(x,y,d_offset)*mOpacity / local_dom; // old: dom_height;
78 Werner 511
            own_value = qMax(own_value, 0.02);
407 werner 512
            value =  *grid_value++ / own_value; // remove impact of focal tree
513
 
397 werner 514
            // debug for one tree in HJA
515
            //if (id()==178020)
516
            //    qDebug() << x << y << xt << yt << *grid_value << local_dom << own_value << value << (*reader)(x,y);
321 werner 517
            //if (_isnan(value))
518
            //    qDebug() << "isnan" << id();
397 werner 519
            if (value * (*reader)(x,y)>1.)
520
                qDebug() << "LIFTorus: value>1.";
78 Werner 521
            sum += value * (*reader)(x,y);
522
 
523
            //} // isIndexValid
524
        }
525
    }
106 Werner 526
    mLRI = sum;
426 werner 527
 
528
    // LRI correction...
529
    double hrel = mHeight / mHeightGrid->valueAtIndex(mPositionIndex.x()/cPxPerHeight, mPositionIndex.y()/cPxPerHeight).height;
530
    if (hrel<1.)
531
        mLRI = species()->speciesSet()->LRIcorrection(mLRI, hrel);
532
 
533
 
615 werner 534
    if (isnan(mLRI)) {
321 werner 535
        qDebug() << "LRI invalid (nan)!" << id();
536
        mLRI=0.;
537
        //qDebug() << reader->dump();
538
    }
148 iland 539
    if (mLRI > 1.)
540
        mLRI = 1.;
78 Werner 541
    //qDebug() << "Tree #"<< id() << "value" << sum << "Impact" << mImpact;
205 werner 542
 
543
    // Finally, add LRI of this Tree to the ResourceUnit!
251 werner 544
    mRU->addWLA(mLeafArea, mLRI);
58 Werner 545
}
546
 
155 werner 547
 
40 Werner 548
void Tree::resetStatistics()
549
{
550
    m_statPrint=0;
105 Werner 551
    m_statCreated=0;
48 Werner 552
    m_statAboveZ=0;
40 Werner 553
    m_nextId=1;
554
}
107 Werner 555
 
251 werner 556
void Tree::calcLightResponse()
557
{
558
    // calculate a light response from lri:
298 werner 559
    // http://iland.boku.ac.at/individual+tree+light+availability
470 werner 560
    double lri = limit(mLRI * mRU->LRImodifier(), 0., 1.); // Eq. (3)
561
    mLightResponse = mSpecies->lightResponse(lri); // Eq. (4)
251 werner 562
    mRU->addLR(mLeafArea, mLightResponse);
563
 
564
}
565
 
110 Werner 566
//////////////////////////////////////////////////
567
////  Growth Functions
568
//////////////////////////////////////////////////
107 Werner 569
 
227 werner 570
/** grow() is the main function of the yearly tree growth.
571
  The main steps are:
298 werner 572
  - Production of GPP/NPP   @sa http://iland.boku.ac.at/primary+production http://iland.boku.ac.at/individual+tree+light+availability
573
  - Partitioning of NPP to biomass compartments of the tree @sa http://iland.boku.ac.at/allocation
227 werner 574
  - Growth of the stem http://iland.boku.ac.at/stem+growth (???)
387 werner 575
  Further activties: * the age of the tree is increased
576
                     * the mortality sub routine is executed
577
                     * seeds are produced */
107 Werner 578
void Tree::grow()
579
{
159 werner 580
    TreeGrowthData d;
169 werner 581
    mAge++; // increase age
230 werner 582
    // step 1: get "interception area" of the tree individual [m2]
583
    // the sum of all area of all trees of a unit equal the total stocked area * interception_factor(Beer-Lambert)
584
    double effective_area = mRU->interceptedArea(mLeafArea, mLightResponse);
107 Werner 585
 
230 werner 586
    // step 2: calculate GPP of the tree based
587
    // (1) get the amount of GPP for a "unit area" of the tree species
588
    double raw_gpp_per_area = mRU->resourceUnitSpecies(species()).prod3PG().GPPperArea();
589
    // (2) GPP (without aging-effect) in kg Biomass / year
590
    double raw_gpp = raw_gpp_per_area * effective_area;
161 werner 591
 
227 werner 592
    // apply aging according to the state of the individuum
388 werner 593
    const double aging_factor = mSpecies->aging(mHeight, mAge);
376 werner 594
    mRU->addTreeAging(mLeafArea, aging_factor);
227 werner 595
    double gpp = raw_gpp * aging_factor; //
608 werner 596
    d.NPP = gpp * cAutotrophicRespiration; // respiration loss (0.47), cf. Waring et al 1998.
113 Werner 597
 
279 werner 598
    //DBGMODE(
137 Werner 599
        if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dTreeNPP) && isDebugging()) {
133 Werner 600
            DebugList &out = GlobalSettings::instance()->debugList(mId, GlobalSettings::dTreeNPP);
601
            dumpList(out); // add tree headers
299 werner 602
            out << mLRI * mRU->LRImodifier() << mLightResponse << effective_area << raw_gpp << gpp << d.NPP << aging_factor;
133 Werner 603
        }
279 werner 604
    //); // DBGMODE()
217 werner 605
    if (d.NPP>0.)
606
        partitioning(d); // split npp to compartments and grow (diameter, height)
133 Werner 607
 
387 werner 608
    // mortality
200 werner 609
    if (Model::settings().mortalityEnabled)
610
        mortality(d);
110 Werner 611
 
159 werner 612
    mStressIndex = d.stress_index;
180 werner 613
 
614
    if (!isDead())
257 werner 615
        mRU->resourceUnitSpecies(species()).statistics().add(this, &d);
277 werner 616
 
387 werner 617
    // regeneration
460 werner 618
    mSpecies->seedProduction(mAge, mHeight, mPositionIndex);
387 werner 619
 
107 Werner 620
}
621
 
227 werner 622
/** partitioning of this years assimilates (NPP) to biomass compartments.
298 werner 623
  Conceptionally, the algorithm is based on Duursma, 2007.
624
  @sa http://iland.boku.ac.at/allocation */
159 werner 625
inline void Tree::partitioning(TreeGrowthData &d)
115 Werner 626
{
159 werner 627
    double npp = d.NPP;
115 Werner 628
    // add content of reserve pool
116 Werner 629
    npp += mNPPReserve;
159 werner 630
    const double foliage_mass_allo = species()->biomassFoliage(mDbh);
276 werner 631
    const double reserve_size = foliage_mass_allo * (1. + mSpecies->finerootFoliageRatio());
297 werner 632
    double refill_reserve = qMin(reserve_size, (1. + mSpecies->finerootFoliageRatio())*mFoliageMass); // not always try to refill reserve 100%
119 Werner 633
 
136 Werner 634
    double apct_wood, apct_root, apct_foliage; // allocation percentages (sum=1) (eta)
468 werner 635
    ResourceUnitSpecies &rus = mRU->resourceUnitSpecies(species());
117 Werner 636
    // turnover rates
159 werner 637
    const double &to_fol = species()->turnoverLeaf();
638
    const double &to_root = species()->turnoverRoot();
136 Werner 639
    // the turnover rate of wood depends on the size of the reserve pool:
116 Werner 640
 
136 Werner 641
 
163 werner 642
    double to_wood = refill_reserve / (mWoodyMass + refill_reserve);
643
 
468 werner 644
    apct_root = rus.prod3PG().rootFraction();
261 werner 645
    d.NPP_above = d.NPP * (1. - apct_root); // aboveground: total NPP - fraction to roots
298 werner 646
    double b_wf = species()->allometricRatio_wf(); // ratio of allometric exponents (b_woody / b_foliage)
117 Werner 647
 
648
    // Duursma 2007, Eq. (20)
167 werner 649
    apct_wood = (foliage_mass_allo*to_wood/npp + b_wf*(1.-apct_root) - b_wf*foliage_mass_allo*to_fol/npp) / ( foliage_mass_allo/mWoodyMass + b_wf );
163 werner 650
    if (apct_wood<0)
651
        apct_wood = 0.;
117 Werner 652
    apct_foliage = 1. - apct_root - apct_wood;
653
 
163 werner 654
 
655
    //DBGMODE(
656
            if (apct_foliage<0 || apct_wood<0)
657
                qDebug() << "transfer to foliage or wood < 0";
658
             if (npp<0)
659
                 qDebug() << "NPP < 0";
660
         //   );
661
 
136 Werner 662
    // Change of biomass compartments
276 werner 663
    double sen_root = mFineRootMass * to_root;
664
    double sen_foliage = mFoliageMass * to_fol;
521 werner 665
    if (ru()->snag())
588 werner 666
        ru()->snag()->addTurnoverLitter(this->species(), sen_foliage, sen_root);
298 werner 667
 
136 Werner 668
    // Roots
298 werner 669
    // http://iland.boku.ac.at/allocation#belowground_NPP
276 werner 670
    mFineRootMass -= sen_root; // reduce only fine root pool
671
    double delta_root = apct_root * npp;
672
    // 1st, refill the fine root pool
673
    double fineroot_miss = mFoliageMass * mSpecies->finerootFoliageRatio() - mFineRootMass;
674
    if (fineroot_miss>0.){
675
        double delta_fineroot = qMin(fineroot_miss, delta_root);
676
        mFineRootMass += delta_fineroot;
677
        delta_root -= delta_fineroot;
678
    }
679
    // 2nd, the rest of NPP allocated to roots go to coarse roots
595 werner 680
    double max_coarse_root = species()->biomassRoot(mDbh);
276 werner 681
    mCoarseRootMass += delta_root;
595 werner 682
    if (mCoarseRootMass > max_coarse_root) {
683
        // if the coarse root pool exceeds the value given by the allometry, then the
684
        // surplus is accounted as turnover
685
        if (ru()->snag())
686
            ru()->snag()->addTurnoverWood(species(), mCoarseRootMass-max_coarse_root);
119 Werner 687
 
595 werner 688
        mCoarseRootMass = max_coarse_root;
689
    }
690
 
136 Werner 691
    // Foliage
159 werner 692
    double delta_foliage = apct_foliage * npp - sen_foliage;
137 Werner 693
    mFoliageMass += delta_foliage;
615 werner 694
    if (isnan(mFoliageMass))
217 werner 695
        qDebug() << "foliage mass invalid!";
163 werner 696
    if (mFoliageMass<0.) mFoliageMass=0.; // limit to zero
697
 
159 werner 698
    mLeafArea = mFoliageMass * species()->specificLeafArea(); // update leaf area
119 Werner 699
 
271 werner 700
    // stress index: different varaints at denominator: to_fol*foliage_mass = leafmass to rebuild,
198 werner 701
    // foliage_mass_allo: simply higher chance for stress
271 werner 702
    // note: npp = NPP + reserve (see above)
276 werner 703
    d.stress_index =qMax(1. - (npp) / ( to_fol*foliage_mass_allo + to_root*foliage_mass_allo*species()->finerootFoliageRatio() + reserve_size), 0.);
198 werner 704
 
136 Werner 705
    // Woody compartments
298 werner 706
    // see also: http://iland.boku.ac.at/allocation#reserve_and_allocation_to_stem_growth
136 Werner 707
    // (1) transfer to reserve pool
708
    double gross_woody = apct_wood * npp;
709
    double to_reserve = qMin(reserve_size, gross_woody);
710
    mNPPReserve = to_reserve;
711
    double net_woody = gross_woody - to_reserve;
137 Werner 712
    double net_stem = 0.;
164 werner 713
    mDbhDelta = 0.;
165 werner 714
 
715
 
136 Werner 716
    if (net_woody > 0.) {
717
        // (2) calculate part of increment that is dedicated to the stem (which is a function of diameter)
159 werner 718
        net_stem = net_woody * species()->allometricFractionStem(mDbh);
719
        d.NPP_stem = net_stem;
137 Werner 720
        mWoodyMass += net_woody;
136 Werner 721
        //  (3) growth of diameter and height baseed on net stem increment
159 werner 722
        grow_diameter(d);
136 Werner 723
    }
119 Werner 724
 
279 werner 725
    //DBGMODE(
137 Werner 726
     if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dTreePartition)
727
         && isDebugging() ) {
129 Werner 728
            DebugList &out = GlobalSettings::instance()->debugList(mId, GlobalSettings::dTreePartition);
729
            dumpList(out); // add tree headers
136 Werner 730
            out << npp << apct_foliage << apct_wood << apct_root
276 werner 731
                    << delta_foliage << net_woody << delta_root << mNPPReserve << net_stem << d.stress_index;
137 Werner 732
     }
144 Werner 733
 
279 werner 734
    //); // DBGMODE()
497 werner 735
    DBGMODE(
428 werner 736
      if (mWoodyMass<0. || mWoodyMass>50000 || mFoliageMass<0. || mFoliageMass>2000. || mCoarseRootMass<0. || mCoarseRootMass>30000
393 werner 737
         || mNPPReserve>4000.) {
389 werner 738
         qDebug() << "Tree:partitioning: invalid or unlikely pools.";
144 Werner 739
         qDebug() << GlobalSettings::instance()->debugListCaptions(GlobalSettings::DebugOutputs(0));
740
         DebugList dbg; dumpList(dbg);
741
         qDebug() << dbg;
497 werner 742
     } );
144 Werner 743
 
136 Werner 744
    /*DBG_IF_X(mId == 1 , "Tree::partitioning", "dump", dump()
745
             + QString("npp %1 npp_reserve %9 sen_fol %2 sen_stem %3 sen_root %4 net_fol %5 net_stem %6 net_root %7 to_reserve %8")
746
               .arg(npp).arg(senescence_foliage).arg(senescence_stem).arg(senescence_root)
747
               .arg(net_foliage).arg(net_stem).arg(net_root).arg(to_reserve).arg(mNPPReserve) );*/
129 Werner 748
 
115 Werner 749
}
750
 
125 Werner 751
 
134 Werner 752
/** Determination of diamter and height growth based on increment of the stem mass (@p net_stem_npp).
125 Werner 753
    Refer to XXX for equations and variables.
754
    This function updates the dbh and height of the tree.
227 werner 755
    The equations are based on dbh in meters! */
159 werner 756
inline void Tree::grow_diameter(TreeGrowthData &d)
119 Werner 757
{
758
    // determine dh-ratio of increment
759
    // height increment is a function of light competition:
125 Werner 760
    double hd_growth = relative_height_growth(); // hd of height growth
153 werner 761
    double d_m = mDbh / 100.; // current diameter in [m]
159 werner 762
    double net_stem_npp = d.NPP_stem;
763
 
153 werner 764
    const double d_delta_m = mDbhDelta / 100.; // increment of last year in [m]
115 Werner 765
 
159 werner 766
    const double mass_factor = species()->volumeFactor() * species()->density();
153 werner 767
    double stem_mass = mass_factor * d_m*d_m * mHeight; // result: kg, dbh[cm], h[meter]
123 Werner 768
 
153 werner 769
    // factor is in diameter increment per NPP [m/kg]
770
    double factor_diameter = 1. / (  mass_factor * (d_m + d_delta_m)*(d_m + d_delta_m) * ( 2. * mHeight/d_m + hd_growth) );
125 Werner 771
    double delta_d_estimate = factor_diameter * net_stem_npp; // estimated dbh-inc using last years increment
772
 
773
    // using that dbh-increment we estimate a stem-mass-increment and the residual (Eq. 9)
153 werner 774
    double stem_estimate = mass_factor * (d_m + delta_d_estimate)*(d_m + delta_d_estimate)*(mHeight + delta_d_estimate*hd_growth);
137 Werner 775
    double stem_residual = stem_estimate - (stem_mass + net_stem_npp);
125 Werner 776
 
777
    // the final increment is then:
778
    double d_increment = factor_diameter * (net_stem_npp - stem_residual); // Eq. (11)
463 werner 779
    double res_final  = 0.;
780
    if (fabs(stem_residual) > 1.) {
465 werner 781
 
463 werner 782
        // calculate final residual in stem
783
        res_final = mass_factor * (d_m + d_increment)*(d_m + d_increment)*(mHeight + d_increment*hd_growth)-((stem_mass + net_stem_npp));
784
        if (fabs(res_final)>1.) {
465 werner 785
            // for large errors in stem biomass due to errors in diameter increment (> 1kg), we solve the increment iteratively.
463 werner 786
            // first, increase increment with constant step until we overestimate the first time
787
            // then,
788
            d_increment = 0.02; // start with 2cm increment
789
            bool reached_error = false;
790
            double step=0.01; // step-width 1cm
791
            double est_stem;
792
            do {
793
                est_stem = mass_factor * (d_m + d_increment)*(d_m + d_increment)*(mHeight + d_increment*hd_growth); // estimate with current increment
794
                stem_residual = est_stem - (stem_mass + net_stem_npp);
795
 
796
                if (fabs(stem_residual) <1.) // finished, if stem residual below 1kg
797
                    break;
798
                if (stem_residual > 0.) {
799
                    d_increment -= step;
800
                    reached_error=true;
801
                } else {
802
                    d_increment += step;
803
                }
804
                if (reached_error)
805
                    step /= 2.;
806
            } while (step>0.00001); // continue until diameter "accuracy" falls below 1/100mm
807
        }
808
    }
809
 
810
    if (d_increment<0.f)
811
        qDebug() << "Tree::grow_diameter: d_inc < 0.";
144 Werner 812
    DBG_IF_X(d_increment<0. || d_increment>0.1, "Tree::grow_dimater", "increment out of range.", dump()
125 Werner 813
             + QString("\nhdz %1 factor_diameter %2 stem_residual %3 delta_d_estimate %4 d_increment %5 final residual(kg) %6")
814
               .arg(hd_growth).arg(factor_diameter).arg(stem_residual).arg(delta_d_estimate).arg(d_increment)
142 Werner 815
               .arg( mass_factor * (mDbh + d_increment)*(mDbh + d_increment)*(mHeight + d_increment*hd_growth)-((stem_mass + net_stem_npp)) ));
125 Werner 816
 
303 werner 817
    //DBGMODE(
463 werner 818
        // do not calculate res_final twice if already done
819
        DBG_IF_X( (res_final==0.?fabs(mass_factor * (d_m + d_increment)*(d_m + d_increment)*(mHeight + d_increment*hd_growth)-((stem_mass + net_stem_npp))):res_final) > 1, "Tree::grow_diameter", "final residual stem estimate > 1kg", dump());
153 werner 820
        DBG_IF_X(d_increment > 10. || d_increment*hd_growth >10., "Tree::grow_diameter", "growth out of bound:",QString("d-increment %1 h-increment %2 ").arg(d_increment).arg(d_increment*hd_growth/100.) + dump());
158 werner 821
 
137 Werner 822
        if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dTreeGrowth) && isDebugging() ) {
126 Werner 823
            DebugList &out = GlobalSettings::instance()->debugList(mId, GlobalSettings::dTreeGrowth);
129 Werner 824
            dumpList(out); // add tree headers
143 Werner 825
            out << net_stem_npp << stem_mass << hd_growth << factor_diameter << delta_d_estimate*100 << d_increment*100;
126 Werner 826
        }
153 werner 827
 
303 werner 828
    //); // DBGMODE()
125 Werner 829
 
830
    d_increment = qMax(d_increment, 0.);
831
 
832
    // update state variables
153 werner 833
    mDbh += d_increment*100; // convert from [m] to [cm]
834
    mDbhDelta = d_increment*100; // save for next year's growth
835
    mHeight += d_increment * hd_growth;
158 werner 836
 
837
    // update state of LIP stamp and opacity
159 werner 838
    mStamp = species()->stamp(mDbh, mHeight); // get new stamp for updated dimensions
158 werner 839
    // calculate the CrownFactor which reflects the opacity of the crown
200 werner 840
    const double k=Model::settings().lightExtinctionCoefficientOpacity;
841
    mOpacity = 1. - exp(-k * mLeafArea / mStamp->crownArea());
158 werner 842
 
119 Werner 843
}
844
 
125 Werner 845
 
846
/// return the HD ratio of this year's increment based on the light status.
119 Werner 847
inline double Tree::relative_height_growth()
848
{
849
    double hd_low, hd_high;
850
    mSpecies->hdRange(mDbh, hd_low, hd_high);
851
 
125 Werner 852
    DBG_IF_X(hd_low>hd_high, "Tree::relative_height_growth", "hd low higher dann hd_high for ", dump());
853
    DBG_IF_X(hd_low < 20 || hd_high>250, "Tree::relative_height_growth", "hd out of range ", dump() + QString(" hd-low: %1 hd-high: %2").arg(hd_low).arg(hd_high));
854
 
855
    // scale according to LRI: if receiving much light (LRI=1), the result is hd_low (for open grown trees)
326 werner 856
    // use the corrected LRI (see tracker#11)
857
    double lri = limit(mLRI * mRU->LRImodifier(),0.,1.);
858
    double hd_ratio = hd_high - (hd_high-hd_low)*lri;
125 Werner 859
    return hd_ratio;
119 Werner 860
}
141 Werner 861
 
278 werner 862
/** This function is called if a tree dies.
863
  @sa ResourceUnit::cleanTreeList(), remove() */
277 werner 864
void Tree::die(TreeGrowthData *d)
865
{
866
    setFlag(Tree::TreeDead, true); // set flag that tree is dead
468 werner 867
    ResourceUnitSpecies &rus = mRU->resourceUnitSpecies(species());
868
    rus.statisticsDead().add(this, d); // add tree to statistics
521 werner 869
    if (ru()->snag())
870
        ru()->snag()->addMortality(this);
277 werner 871
}
872
 
564 werner 873
void Tree::remove(double removeFoliage, double removeBranch, double removeStem )
278 werner 874
{
875
    setFlag(Tree::TreeDead, true); // set flag that tree is dead
468 werner 876
    ResourceUnitSpecies &rus = mRU->resourceUnitSpecies(species());
877
    rus.statisticsMgmt().add(this, 0);
521 werner 878
    if (ru()->snag())
564 werner 879
        ru()->snag()->addHarvest(this, removeStem, removeBranch, removeFoliage);
278 werner 880
}
881
 
159 werner 882
void Tree::mortality(TreeGrowthData &d)
883
{
163 werner 884
    // death if leaf area is 0
885
    if (mFoliageMass<0.00001)
886
        die();
887
 
308 werner 888
    double p_death,  p_stress, p_intrinsic;
889
    p_intrinsic = species()->deathProb_intrinsic();
890
    p_stress = species()->deathProb_stress(d.stress_index);
891
    p_death =  p_intrinsic + p_stress;
444 werner 892
    double p = drandom(ru()->randomGenerator()); //0..1
159 werner 893
    if (p<p_death) {
894
        // die...
895
        die();
896
    }
897
}
141 Werner 898
 
899
//////////////////////////////////////////////////
900
////  value functions
901
//////////////////////////////////////////////////
902
 
145 Werner 903
double Tree::volume() const
141 Werner 904
{
905
    /// @see Species::volumeFactor() for details
159 werner 906
    const double volume_factor = species()->volumeFactor();
157 werner 907
    const double volume =  volume_factor * mDbh*mDbh*mHeight * 0.0001; // dbh in cm: cm/100 * cm/100 = cm*cm * 0.0001 = m2
141 Werner 908
    return volume;
909
}
180 werner 910
 
579 werner 911
/// return the basal area in m2
180 werner 912
double Tree::basalArea() const
913
{
914
    // A = r^2 * pi = d/2*pi; from cm->m: d/200
915
    const double b = (mDbh/200.)*(mDbh/200.)*M_PI;
916
    return b;
917
}