Subversion Repositories public iLand

Rev

Rev 151 | Rev 155 | 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"
107 Werner 9
#include "ressourceunit.h"
151 iland 10
#include "model.h"
38 Werner 11
 
110 Werner 12
// static varaibles
106 Werner 13
FloatGrid *Tree::mGrid = 0;
151 iland 14
HeightGrid *Tree::mHeightGrid = 0;
40 Werner 15
int Tree::m_statPrint=0;
48 Werner 16
int Tree::m_statAboveZ=0;
105 Werner 17
int Tree::m_statCreated=0;
40 Werner 18
int Tree::m_nextId=0;
19
 
151 iland 20
void Tree::setGrid(FloatGrid* gridToStamp, Grid<HeightGridValue> *dominanceGrid)
21
{
22
    mGrid = gridToStamp; mHeightGrid = dominanceGrid;
23
}
24
 
110 Werner 25
// lifecycle
3 Werner 26
Tree::Tree()
27
{
149 werner 28
    mDbh = mHeight = 0;
29
    mRU = 0; mSpecies = 0;
30
    mOpacity=mFoliageMass=mWoodyMass=mRootMass=mLeafArea=0.;
31
    mDbhDelta=mNPPReserve=mLRI=0.;
137 Werner 32
    mDebugging = false;
106 Werner 33
    mId = m_nextId++;
105 Werner 34
    m_statCreated++;
3 Werner 35
}
38 Werner 36
 
15 Werner 37
/** get distance and direction between two points.
38 Werner 38
  returns the distance (m), and the angle between PStart and PEnd (radians) in referenced param rAngle. */
3 Werner 39
float dist_and_direction(const QPointF &PStart, const QPointF &PEnd, float &rAngle)
40
{
41
    float dx = PEnd.x() - PStart.x();
42
    float dy = PEnd.y() - PStart.y();
43
    float d = sqrt(dx*dx + dy*dy);
44
    // direction:
45
    rAngle = atan2(dx, dy);
46
    return d;
47
}
48
 
125 Werner 49
/// dumps some core variables of a tree to a string.
50
QString Tree::dump()
51
{
52
    QString result = QString("id %1 species %2 dbh %3 h %4 x/y %5/%6 ru# %7 LRI %8")
53
                            .arg(mId).arg(mSpecies->id()).arg(mDbh).arg(mHeight)
54
                            .arg(mPosition.x()).arg(mPosition.y())
55
                            .arg(mRU->index()).arg(mLRI);
56
    return result;
57
}
3 Werner 58
 
129 Werner 59
void Tree::dumpList(DebugList &rTargetList)
60
{
61
    rTargetList << mId << mSpecies->id() << mDbh << mHeight  << mPosition.x() << mPosition.y()   << mRU->index() << mLRI
136 Werner 62
                << mWoodyMass << mRootMass << mFoliageMass << mLeafArea;
129 Werner 63
}
64
 
38 Werner 65
void Tree::setup()
66
{
106 Werner 67
    if (mDbh<=0 || mHeight<=0)
38 Werner 68
        return;
69
    // check stamp
137 Werner 70
    Q_ASSERT_X(mSpecies!=0, "Tree::setup()", "species is NULL");
71
    mStamp = mSpecies->stamp(mDbh, mHeight);
110 Werner 72
 
137 Werner 73
    mFoliageMass = mSpecies->biomassFoliage(mDbh);
74
    mRootMass = mSpecies->biomassRoot(mDbh) + mFoliageMass; // coarse root (allometry) + fine root (estimated size: foliage)
75
    mWoodyMass = mSpecies->biomassWoody(mDbh);
110 Werner 76
 
137 Werner 77
    // LeafArea[m2] = LeafMass[kg] * specificLeafArea[m2/kg]
78
    mLeafArea = mFoliageMass * mSpecies->specificLeafArea();
149 werner 79
    mOpacity = 1. - exp(-0.5 * mLeafArea / mStamp->crownArea());
137 Werner 80
    mNPPReserve = 2*mFoliageMass; // initial value
81
    mDbhDelta = 0.1; // initial value: used in growth() to estimate diameter increment
38 Werner 82
}
39 Werner 83
 
110 Werner 84
//////////////////////////////////////////////////
85
////  Light functions (Pattern-stuff)
86
//////////////////////////////////////////////////
87
 
70 Werner 88
#define NOFULLDBG
77 Werner 89
//#define NOFULLOPT
39 Werner 90
 
40 Werner 91
 
77 Werner 92
void Tree::applyStamp()
93
{
144 Werner 94
    if (!mStamp)
95
        return;
106 Werner 96
    Q_ASSERT(mGrid!=0 && mStamp!=0 && mRU!=0);
77 Werner 97
 
106 Werner 98
    QPoint pos = mGrid->indexAt(mPosition);
99
    int offset = mStamp->offset();
77 Werner 100
    pos-=QPoint(offset, offset);
101
 
102
    float local_dom; // height of Z* on the current position
103
    int x,y;
104
    float value;
106 Werner 105
    int gr_stamp = mStamp->size();
77 Werner 106
    int grid_x, grid_y;
107
    float *grid_value;
106 Werner 108
    if (!mGrid->isIndexValid(pos) || !mGrid->isIndexValid(pos+QPoint(gr_stamp, gr_stamp))) {
77 Werner 109
        // todo: in this case we should use another algorithm!!!
110
        return;
111
    }
112
 
113
    for (y=0;y<gr_stamp; ++y) {
114
        grid_y = pos.y() + y;
106 Werner 115
        grid_value = mGrid->ptr(pos.x(), grid_y);
77 Werner 116
        for (x=0;x<gr_stamp;++x) {
117
            // suppose there is no stamping outside
118
            grid_x = pos.x() + x;
119
 
151 iland 120
            local_dom = mHeightGrid->valueAtIndex(grid_x/5, grid_y/5).height;
106 Werner 121
            value = (*mStamp)(x,y); // stampvalue
149 werner 122
            value = 1. - value*mOpacity / local_dom; // calculated value
77 Werner 123
            value = qMax(value, 0.02f); // limit value
124
 
125
            *grid_value++ *= value;
126
        }
127
    }
128
 
129
    m_statPrint++; // count # of stamp applications...
130
}
131
 
62 Werner 132
 
74 Werner 133
/** heightGrid()
134
  This function calculates the "dominant height field". This grid is coarser as the fine-scaled light-grid.
135
 
136
*/
137
void Tree::heightGrid()
138
{
139
    // height of Z*
106 Werner 140
    const float cellsize = mHeightGrid->cellsize();
74 Werner 141
 
106 Werner 142
    QPoint p = mHeightGrid->indexAt(mPosition); // pos of tree on height grid
143
    QPoint competition_grid = mGrid->indexAt(mPosition);
74 Werner 144
 
151 iland 145
    // count trees that are on height-grid cells (used for stockable area)
146
    mHeightGrid->valueAtIndex(p).count++;
147
 
74 Werner 148
    int index_eastwest = competition_grid.x() % 5; // 4: very west, 0 east edge
149
    int index_northsouth = competition_grid.y() % 5; // 4: northern edge, 0: southern edge
150
    int dist[9];
151
    dist[3] = index_northsouth * 2 + 1; // south
152
    dist[1] = index_eastwest * 2 + 1; // west
153
    dist[5] = 10 - dist[3]; // north
154
    dist[7] = 10 - dist[1]; // east
155
    dist[8] = qMax(dist[5], dist[7]); // north-east
156
    dist[6] = qMax(dist[3], dist[7]); // south-east
157
    dist[0] = qMax(dist[3], dist[1]); // south-west
158
    dist[2] = qMax(dist[5], dist[1]); // north-west
75 Werner 159
    dist[4] = 0; // center cell
76 Werner 160
    /* 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:
161
       index = 4 + 3*sign(ix) + sign(iy) transforms combinations of directions to unique ids (0..8), which are used above.
162
        e.g.: sign(ix) = -1, sign(iy) = 1 (=north-west) -> index = 4 + -3 + 1 = 2
163
    */
74 Werner 164
 
165
 
106 Werner 166
    int ringcount = int(floor(mHeight / cellsize)) + 1;
74 Werner 167
    int ix, iy;
168
    int ring;
169
    QPoint pos;
170
    float hdom;
171
 
172
    for (ix=-ringcount;ix<=ringcount;ix++)
173
        for (iy=-ringcount; iy<=+ringcount; iy++) {
174
        ring = qMax(abs(ix), abs(iy));
175
        QPoint pos(ix+p.x(), iy+p.y());
106 Werner 176
        if (mHeightGrid->isIndexValid(pos)) {
151 iland 177
            float &rHGrid = mHeightGrid->valueAtIndex(pos).height;
106 Werner 178
            if (rHGrid > mHeight) // skip calculation if grid is higher than tree
74 Werner 179
                continue;
180
            int direction = 4 + (ix?(ix<0?-3:3):0) + (iy?(iy<0?-1:1):0); // 4 + 3*sgn(x) + sgn(y)
106 Werner 181
            hdom = mHeight - dist[direction];
74 Werner 182
            if (ring>1)
183
                hdom -= (ring-1)*10;
184
 
185
            rHGrid = qMax(rHGrid, hdom); // write value
186
        } // is valid
187
    } // for (y)
39 Werner 188
}
40 Werner 189
 
190
double Tree::readStamp()
191
{
106 Werner 192
    if (!mStamp)
51 Werner 193
        return 0.;
106 Werner 194
    const Stamp *stamp = mStamp->reader();
40 Werner 195
    if (!stamp)
196
        return 0.;
106 Werner 197
    QPoint pos = mGrid->indexAt(mPosition);
40 Werner 198
    int offset = stamp->offset();
199
    pos-=QPoint(offset, offset);
200
    QPoint p;
201
 
202
    int x,y;
203
    double sum=0.;
204
    for (x=0;x<stamp->size();++x) {
205
        for (y=0;y<stamp->size(); ++y) {
206
           p = pos + QPoint(x,y);
106 Werner 207
           if (mGrid->isIndexValid(p))
208
               sum += mGrid->valueAtIndex(p) * (*stamp)(x,y);
40 Werner 209
        }
210
    }
149 werner 211
    float eigenvalue = mStamp->readSum() * mOpacity;
106 Werner 212
    mLRI = sum - eigenvalue;// additive
151 iland 213
    float dom_height = (*mHeightGrid)[mPosition].height;
53 Werner 214
    if (dom_height>0.)
106 Werner 215
       mLRI = mLRI / dom_height;
53 Werner 216
 
217
    //mImpact = sum + eigenvalue;// multiplicative
48 Werner 218
    // read dominance field...
53 Werner 219
 
106 Werner 220
    if (dom_height < mHeight) {
48 Werner 221
        // if tree is higher than Z*, the dominance height
222
        // a part of the crown is in "full light".
223
        // total value = zstar/treeheight*value + 1-zstar/height
224
        // reformulated to:
106 Werner 225
        mLRI =  mLRI * dom_height/mHeight ;
48 Werner 226
        m_statAboveZ++;
227
    }
106 Werner 228
    if (fabs(mLRI < 0.000001))
229
        mLRI = 0.f;
230
    qDebug() << "Tree #"<< id() << "value" << sum << "eigenvalue" << eigenvalue << "Impact" << mLRI;
231
    return mLRI;
40 Werner 232
}
233
 
58 Werner 234
 
107 Werner 235
void Tree::readStampMul()
78 Werner 236
{
106 Werner 237
    if (!mStamp)
107 Werner 238
        return;
106 Werner 239
    const Stamp *reader = mStamp->reader();
78 Werner 240
    if (!reader)
107 Werner 241
        return;
106 Werner 242
    QPoint pos_reader = mGrid->indexAt(mPosition);
78 Werner 243
 
244
    int offset_reader = reader->offset();
106 Werner 245
    int offset_writer = mStamp->offset();
78 Werner 246
    int d_offset = offset_writer - offset_reader; // offset on the *stamp* to the crown-cells
247
 
248
    QPoint pos_writer=pos_reader - QPoint(offset_writer, offset_writer);
249
    pos_reader-=QPoint(offset_reader, offset_reader);
250
    QPoint p;
251
 
252
    //float dom_height = (*m_dominanceGrid)[m_Position];
253
    float local_dom;
254
 
255
    int x,y;
256
    double sum=0.;
257
    double value, own_value;
258
    float *grid_value;
259
    int reader_size = reader->size();
260
    int rx = pos_reader.x();
261
    int ry = pos_reader.y();
262
    for (y=0;y<reader_size; ++y, ++ry) {
106 Werner 263
        grid_value = mGrid->ptr(rx, ry);
78 Werner 264
        for (x=0;x<reader_size;++x) {
265
 
266
            //p = pos_reader + QPoint(x,y);
267
            //if (m_grid->isIndexValid(p)) {
151 iland 268
            local_dom = mHeightGrid->valueAtIndex((rx+x)/5, ry/5).height; // ry: gets ++ed in outer loop, rx not
78 Werner 269
            //local_dom = m_dominanceGrid->valueAt( m_grid->cellCoordinates(p) );
125 Werner 270
 
149 werner 271
            own_value = 1. - mStamp->offsetValue(x,y,d_offset)*mOpacity / local_dom; // old: dom_height;
78 Werner 272
            own_value = qMax(own_value, 0.02);
273
            value =  *grid_value++ / own_value; // remove impact of focal tree
274
            //if (value>0.)
275
            sum += value * (*reader)(x,y);
276
 
277
            //} // isIndexValid
278
        }
279
    }
106 Werner 280
    mLRI = sum;
78 Werner 281
    // read dominance field...
282
    // this applies only if some trees are potentially *higher* than the dominant height grid
283
    //if (dom_height < m_Height) {
284
        // if tree is higher than Z*, the dominance height
285
        // a part of the crown is in "full light".
286
    //    m_statAboveZ++;
287
    //    mImpact = 1. - (1. - mImpact)*dom_height/m_Height;
288
    //}
148 iland 289
    if (mLRI > 1.)
290
        mLRI = 1.;
78 Werner 291
    //qDebug() << "Tree #"<< id() << "value" << sum << "Impact" << mImpact;
148 iland 292
    mRU->addWLA(mLRI*mLeafArea, mLeafArea);
58 Werner 293
}
294
 
40 Werner 295
void Tree::resetStatistics()
296
{
297
    m_statPrint=0;
105 Werner 298
    m_statCreated=0;
48 Werner 299
    m_statAboveZ=0;
40 Werner 300
    m_nextId=1;
301
}
107 Werner 302
 
110 Werner 303
//////////////////////////////////////////////////
304
////  Growth Functions
305
//////////////////////////////////////////////////
107 Werner 306
 
110 Werner 307
 
107 Werner 308
void Tree::grow()
309
{
147 werner 310
    // step 1: get radiation from ressource unit: radiation (MJ/tree/year) total intercepted radiation for this tree per year!
311
    double radiation = mRU->interceptedRadiation(mLeafArea, mLRI);
113 Werner 312
    // step 2: get fraction of PARutilized, i.e. fraction of intercepted rad that is utiliziable (per year)
107 Werner 313
 
115 Werner 314
    double raw_gpp_per_rad = mRU->ressourceUnitSpecies(mSpecies).prod3PG().GPPperRad();
133 Werner 315
    // GPP (without aging-effect) [gC] / year -> kg/GPP (*0.001)
316
    double raw_gpp = raw_gpp_per_rad * radiation * 0.001;
113 Werner 317
    /*
318
    if (mRU->index()==3) {
319
        qDebug() << "tree production: radiation: " << radiation << "gpp/rad:" << raw_gpp_per_rad << "gpp" << raw_gpp << "LRI:" << mLRI << "LeafArea:" << mLeafArea;
320
    }*/
115 Werner 321
    // apply aging
322
    double gpp = raw_gpp * 0.6; // aging
323
    double npp = gpp * 0.47; // respiration loss
113 Werner 324
 
133 Werner 325
    DBGMODE(
137 Werner 326
        if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dTreeNPP) && isDebugging()) {
133 Werner 327
            DebugList &out = GlobalSettings::instance()->debugList(mId, GlobalSettings::dTreeNPP);
328
            dumpList(out); // add tree headers
329
            out << radiation << raw_gpp << gpp << npp;
330
        }
331
    ); // DBGMODE()
332
 
115 Werner 333
    partitioning(npp);
334
 
143 Werner 335
    mStamp = mSpecies->stamp(mDbh, mHeight); // get new stamp for updated dimensions
149 werner 336
    // calculate the CrownFactor which reflects the opacity of the crown
153 werner 337
    mOpacity = 1. - exp(-0.7 * mLeafArea / mStamp->crownArea());
110 Werner 338
 
149 werner 339
 
107 Werner 340
}
341
 
117 Werner 342
 
343
// just used to test the DBG_IF_x macros...
344
QString test_cntr()
345
{
346
    static int cnt = 0;
347
    cnt++;
348
    return QString::number(cnt);
349
}
350
 
115 Werner 351
void Tree::partitioning(double npp)
352
{
119 Werner 353
    DBGMODE(
354
        if (mId==1)
355
            test_cntr();
356
    );
115 Werner 357
    double harshness = mRU->ressourceUnitSpecies(mSpecies).prod3PG().harshness();
358
    // add content of reserve pool
116 Werner 359
    npp += mNPPReserve;
136 Werner 360
    const double foliage_mass_allo = mSpecies->biomassFoliage(mDbh);
361
    const double reserve_size = 2 * foliage_mass_allo;
119 Werner 362
 
136 Werner 363
    double apct_wood, apct_root, apct_foliage; // allocation percentages (sum=1) (eta)
117 Werner 364
    // turnover rates
365
    const double &to_fol = mSpecies->turnoverLeaf();
366
    const double &to_root = mSpecies->turnoverRoot();
136 Werner 367
    // the turnover rate of wood depends on the size of the reserve pool:
116 Werner 368
 
136 Werner 369
    double to_wood = reserve_size / (mWoodyMass + reserve_size);
370
 
117 Werner 371
    apct_root = harshness;
136 Werner 372
    double b_wf = mSpecies->allometricRatio_wf(); // ratio of allometric exponents... now fixed
117 Werner 373
 
374
    // Duursma 2007, Eq. (20)
136 Werner 375
    apct_wood = (foliage_mass_allo * to_wood / npp + b_wf*(1.-apct_root) - b_wf * to_fol/npp) / ( foliage_mass_allo / mWoodyMass + b_wf );
117 Werner 376
    apct_foliage = 1. - apct_root - apct_wood;
377
 
136 Werner 378
    // Change of biomass compartments
379
    // Roots
137 Werner 380
    double delta_root = apct_root * npp - mRootMass * to_root;
381
    mRootMass += delta_root;
119 Werner 382
 
136 Werner 383
    // Foliage
137 Werner 384
    double delta_foliage = apct_foliage * npp - mFoliageMass * to_fol;
385
    mFoliageMass += delta_foliage;
386
    mLeafArea = mFoliageMass * mSpecies->specificLeafArea(); // update leaf area
119 Werner 387
 
136 Werner 388
    // Woody compartments
389
    // (1) transfer to reserve pool
390
    double gross_woody = apct_wood * npp;
391
    double to_reserve = qMin(reserve_size, gross_woody);
392
    mNPPReserve = to_reserve;
393
    double net_woody = gross_woody - to_reserve;
137 Werner 394
    double net_stem = 0.;
136 Werner 395
    if (net_woody > 0.) {
396
        // (2) calculate part of increment that is dedicated to the stem (which is a function of diameter)
137 Werner 397
        net_stem = net_woody * mSpecies->allometricFractionStem(mDbh);
398
        mWoodyMass += net_woody;
136 Werner 399
        //  (3) growth of diameter and height baseed on net stem increment
400
        grow_diameter(net_stem);
401
    }
119 Werner 402
 
129 Werner 403
    DBGMODE(
137 Werner 404
     if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dTreePartition)
405
         && isDebugging() ) {
129 Werner 406
            DebugList &out = GlobalSettings::instance()->debugList(mId, GlobalSettings::dTreePartition);
407
            dumpList(out); // add tree headers
136 Werner 408
            out << npp << apct_foliage << apct_wood << apct_root
137 Werner 409
                    << delta_foliage << net_woody << delta_root << mNPPReserve << net_stem;
410
     }
144 Werner 411
 
129 Werner 412
    ); // DBGMODE()
144 Werner 413
    //DBGMODE(
414
      if (mWoodyMass<0. || mWoodyMass>10000 || mFoliageMass<0. || mFoliageMass>1000. || mRootMass<0. || mRootMass>10000
415
         || mNPPReserve>2000.) {
416
         qDebug() << "Tree:partitioning: invalid pools.";
417
         qDebug() << GlobalSettings::instance()->debugListCaptions(GlobalSettings::DebugOutputs(0));
418
         DebugList dbg; dumpList(dbg);
419
         qDebug() << dbg;
420
     } //);
421
 
136 Werner 422
    /*DBG_IF_X(mId == 1 , "Tree::partitioning", "dump", dump()
423
             + 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")
424
               .arg(npp).arg(senescence_foliage).arg(senescence_stem).arg(senescence_root)
425
               .arg(net_foliage).arg(net_stem).arg(net_root).arg(to_reserve).arg(mNPPReserve) );*/
129 Werner 426
 
115 Werner 427
}
428
 
125 Werner 429
 
134 Werner 430
/** Determination of diamter and height growth based on increment of the stem mass (@p net_stem_npp).
125 Werner 431
    Refer to XXX for equations and variables.
432
    This function updates the dbh and height of the tree.
153 werner 433
    The equations are based on dbh in meters!
125 Werner 434
  */
119 Werner 435
inline void Tree::grow_diameter(const double &net_stem_npp)
436
{
437
    // determine dh-ratio of increment
438
    // height increment is a function of light competition:
125 Werner 439
    double hd_growth = relative_height_growth(); // hd of height growth
153 werner 440
    double d_m = mDbh / 100.; // current diameter in [m]
441
    const double d_delta_m = mDbhDelta / 100.; // increment of last year in [m]
115 Werner 442
 
142 Werner 443
    const double mass_factor = mSpecies->volumeFactor() * mSpecies->density();
153 werner 444
    double stem_mass = mass_factor * d_m*d_m * mHeight; // result: kg, dbh[cm], h[meter]
123 Werner 445
 
153 werner 446
    // factor is in diameter increment per NPP [m/kg]
447
    double factor_diameter = 1. / (  mass_factor * (d_m + d_delta_m)*(d_m + d_delta_m) * ( 2. * mHeight/d_m + hd_growth) );
125 Werner 448
    double delta_d_estimate = factor_diameter * net_stem_npp; // estimated dbh-inc using last years increment
449
 
450
    // using that dbh-increment we estimate a stem-mass-increment and the residual (Eq. 9)
153 werner 451
    double stem_estimate = mass_factor * (d_m + delta_d_estimate)*(d_m + delta_d_estimate)*(mHeight + delta_d_estimate*hd_growth);
137 Werner 452
    double stem_residual = stem_estimate - (stem_mass + net_stem_npp);
125 Werner 453
 
454
    // the final increment is then:
455
    double d_increment = factor_diameter * (net_stem_npp - stem_residual); // Eq. (11)
144 Werner 456
    DBG_IF_X(d_increment<0. || d_increment>0.1, "Tree::grow_dimater", "increment out of range.", dump()
125 Werner 457
             + QString("\nhdz %1 factor_diameter %2 stem_residual %3 delta_d_estimate %4 d_increment %5 final residual(kg) %6")
458
               .arg(hd_growth).arg(factor_diameter).arg(stem_residual).arg(delta_d_estimate).arg(d_increment)
142 Werner 459
               .arg( mass_factor * (mDbh + d_increment)*(mDbh + d_increment)*(mHeight + d_increment*hd_growth)-((stem_mass + net_stem_npp)) ));
125 Werner 460
 
461
    DBGMODE(
153 werner 462
        double res_final = mass_factor * (d_m + d_increment)*(d_m + d_increment)*(mHeight + d_increment*hd_growth)-((stem_mass + net_stem_npp));
125 Werner 463
        DBG_IF_X(res_final > 1, "Tree::grow_diameter", "final residual stem estimate > 1kg", dump());
153 werner 464
        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());
125 Werner 465
        //dbgstruct["sen_demand"]=sen_demand;
137 Werner 466
        if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dTreeGrowth) && isDebugging() ) {
126 Werner 467
            DebugList &out = GlobalSettings::instance()->debugList(mId, GlobalSettings::dTreeGrowth);
129 Werner 468
            dumpList(out); // add tree headers
143 Werner 469
            out << net_stem_npp << stem_mass << hd_growth << factor_diameter << delta_d_estimate*100 << d_increment*100;
126 Werner 470
        }
153 werner 471
 
142 Werner 472
    ); // DBGMODE()
125 Werner 473
 
474
    d_increment = qMax(d_increment, 0.);
475
 
476
    // update state variables
153 werner 477
    mDbh += d_increment*100; // convert from [m] to [cm]
478
    mDbhDelta = d_increment*100; // save for next year's growth
479
    mHeight += d_increment * hd_growth;
119 Werner 480
}
481
 
125 Werner 482
 
483
/// return the HD ratio of this year's increment based on the light status.
119 Werner 484
inline double Tree::relative_height_growth()
485
{
486
    double hd_low, hd_high;
487
    mSpecies->hdRange(mDbh, hd_low, hd_high);
488
 
125 Werner 489
    DBG_IF_X(hd_low>hd_high, "Tree::relative_height_growth", "hd low higher dann hd_high for ", dump());
490
    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));
491
 
492
    // scale according to LRI: if receiving much light (LRI=1), the result is hd_low (for open grown trees)
493
    double hd_ratio = hd_high - (hd_high-hd_low)*mLRI;
494
    return hd_ratio;
119 Werner 495
}
141 Werner 496
 
497
 
498
//////////////////////////////////////////////////
499
////  value functions
500
//////////////////////////////////////////////////
501
 
145 Werner 502
double Tree::volume() const
141 Werner 503
{
504
    /// @see Species::volumeFactor() for details
142 Werner 505
    const double volume_factor = mSpecies->volumeFactor();
506
    const double volume =  volume_factor * mDbh*mDbh*mHeight;
141 Werner 507
    return volume;
508
}