Subversion Repositories public iLand

Rev

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