Subversion Repositories public iLand

Rev

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