Subversion Repositories public iLand

Rev

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