Subversion Repositories public iLand

Rev

Rev 123 | Rev 126 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 123 Rev 125
Line 39... Line 39...
39
    // direction:
39
    // direction:
40
    rAngle = atan2(dx, dy);
40
    rAngle = atan2(dx, dy);
41
    return d;
41
    return d;
42
}
42
}
43
43
-
 
44
/// dumps some core variables of a tree to a string.
-
 
45
QString Tree::dump()
-
 
46
{
-
 
47
    QString result = QString("id %1 species %2 dbh %3 h %4 x/y %5/%6 ru# %7 LRI %8")
-
 
48
                            .arg(mId).arg(mSpecies->id()).arg(mDbh).arg(mHeight)
-
 
49
                            .arg(mPosition.x()).arg(mPosition.y())
-
 
50
                            .arg(mRU->index()).arg(mLRI);
-
 
51
    return result;
-
 
52
}
44
53
45
void Tree::setup()
54
void Tree::setup()
46
{
55
{
47
    if (mDbh<=0 || mHeight<=0)
56
    if (mDbh<=0 || mHeight<=0)
48
        return;
57
        return;
Line 50... Line 59...
50
   Q_ASSERT_X(mSpecies!=0, "Tree::setup()", "species is NULL");
59
   Q_ASSERT_X(mSpecies!=0, "Tree::setup()", "species is NULL");
51
   mStamp = mSpecies->stamp(mDbh, mHeight);
60
   mStamp = mSpecies->stamp(mDbh, mHeight);
52
61
53
   calcBiomassCompartments();
62
   calcBiomassCompartments();
54
   mNPPReserve = mLeafMass; // initial value
63
   mNPPReserve = mLeafMass; // initial value
-
 
64
   mDbhDelta = 0.1; // initial value: used in growth() to estimate diameter increment
55
65
56
}
66
}
57
67
58
//////////////////////////////////////////////////
68
//////////////////////////////////////////////////
59
////  Light functions (Pattern-stuff)
69
////  Light functions (Pattern-stuff)
60
//////////////////////////////////////////////////
70
//////////////////////////////////////////////////
61
71
62
#define NOFULLDBG
72
#define NOFULLDBG
63
//#define NOFULLOPT
73
//#define NOFULLOPT
64
/*
-
 
65
void Tree::applyStamp()
-
 
66
{
-
 
67
    Q_ASSERT(m_grid!=0);
-
 
68
    if (!m_stamp)
-
 
69
        return;
-
 
70

74
71
    QPoint pos = m_grid->indexAt(m_Position);
-
 
72
    int offset = m_stamp->offset();
-
 
73
    pos-=QPoint(offset, offset);
-
 
74
    QPoint p;
-
 
75

-
 
76
    float local_dom; // height of Z* on the current position
-
 
77
    int x,y;
-
 
78
    float value;
-
 
79
    QPoint dbg(10,20);
-
 
80
    #ifndef NOFULLDBG
-
 
81
    qDebug() <<"indexstampx indexstamy gridx gridy local_dom_height stampvalue 1-value*la/dom grid_before gridvalue_after";
-
 
82
    #endif
-
 
83
    for (x=0;x<m_stamp->size();++x) {
-
 
84
        for (y=0;y<m_stamp->size(); ++y) {
-
 
85
           p = pos + QPoint(x,y);
-
 
86
           // debug pixel
-
 
87
           //if (p==dbg)
-
 
88
           //    qDebug() << "#" << id() << "value;"<<(*m_stamp)(x,y)<<"domH"<<dom;
-
 
89

-
 
90
           if (m_grid->isIndexValid(p)) {
-
 
91
               // mulitplicative:
-
 
92
               //m_grid->valueAtIndex(p)*=(*m_stamp)(x,y);
-
 
93
               // additiv:
-
 
94
               // multiplicatie, v2
-
 
95
               // a optimization for 2m vs 10m grids: local_dom = m_dominanceGrid->valueAtIndex(p.x()/5, p.y()/5);
-
 
96
               // effect is about 20% of the application time
-
 
97
               local_dom = m_dominanceGrid->valueAt( m_grid->cellCoordinates(p) ); // todo: could be done more effectively (here 2 transormations are performed)...
-
 
98
               if (local_dom<=0.f) {
-
 
99
                   //qDebug() << "invalid height at " << m_grid->cellCoordinates(p) << "of" << local_dom;
-
 
100
                   local_dom = 2.;
-
 
101
               }
-
 
102
               value = (*m_stamp)(x,y);
-
 
103
               value = 1. - value*lafactor / local_dom;
-
 
104
               value = qMax(value, 0.02f);
-
 
105
#ifndef NOFULLDBG
-
 
106
                qDebug() << x << y << p.x() << p.y() << local_dom << (*m_stamp)(x,y) << 1. - (*m_stamp)(x,y)*lafactor / local_dom  << m_grid->valueAtIndex(p) << m_grid->valueAtIndex(p)*value;
-
 
107
#endif
-
 
108

-
 
109
               m_grid->valueAtIndex(p)*= value;
-
 
110
           }
-
 
111
        }
-
 
112
    }
-
 
113

-
 
114
    m_statPrint++; // count # of stamp applications...
-
 
115
}
-
 
116
*/
-
 
117
75
118
void Tree::applyStamp()
76
void Tree::applyStamp()
119
{
77
{
120
    Q_ASSERT(mGrid!=0 && mStamp!=0 && mRU!=0);
78
    Q_ASSERT(mGrid!=0 && mStamp!=0 && mRU!=0);
121
79
Line 151... Line 109...
151
    }
109
    }
152
110
153
    m_statPrint++; // count # of stamp applications...
111
    m_statPrint++; // count # of stamp applications...
154
}
112
}
155
113
156
 /*
-
 
157
void Tree::heightGrid_old()
-
 
158
{
-
 
159
    // height of Z*
-
 
160
    const float cellsize = m_dominanceGrid->cellsize();
-
 
161
    if (m_Height < cellsize/2.) {
-
 
162
        float &dom = m_dominanceGrid->valueAt(m_Position); // modifyable reference
-
 
163
        dom = qMax(dom, m_Height/2.f);
-
 
164
    } else {
-
 
165
        QPoint p = m_dominanceGrid->indexAt(m_Position); // pos of tree on height grid
-
 
166

-
 
167
        int ringcount = int(floor(m_Height / cellsize));
-
 
168
        int ix, iy;
-
 
169
        int ring;
-
 
170
        QPoint pos;
-
 
171
        float hdom;
-
 
172
        float h_out = fmod(m_Height, cellsize) / 2.;
-
 
173
        for (ix=-ringcount;ix<=ringcount;ix++)
-
 
174
            for (iy=-ringcount; iy<=+ringcount; iy++) {
-
 
175
            ring = qMax(abs(ix), abs(iy));
-
 
176
            QPoint pos(ix+p.x(), iy+p.y());
-
 
177
            if (m_dominanceGrid->isIndexValid(pos)) {
-
 
178
                // apply value....
-
 
179
                if (ring==0) {
-
 
180
                    hdom = m_Height- cellsize/4.;
-
 
181
                } else if (ring==abs(ringcount)) {
-
 
182
                    // outermost ring: use again height/2.
-
 
183
                    hdom = h_out;
-
 
184
                } else {
-
 
185
                    hdom = m_Height- ring*cellsize;
-
 
186
                }
-
 
187
                m_dominanceGrid->valueAtIndex(pos) = qMax(m_dominanceGrid->valueAtIndex(pos), hdom);
-
 
188
            }
-
 
189

-
 
190
        }
-
 
191
    }
-
 
192

-
 
193
} */
-
 
194
114
195
/** heightGrid()
115
/** heightGrid()
196
  This function calculates the "dominant height field". This grid is coarser as the fine-scaled light-grid.
116
  This function calculates the "dominant height field". This grid is coarser as the fine-scaled light-grid.
197

117

198
*/
118
*/
Line 288... Line 208...
288
        mLRI = 0.f;
208
        mLRI = 0.f;
289
    qDebug() << "Tree #"<< id() << "value" << sum << "eigenvalue" << eigenvalue << "Impact" << mLRI;
209
    qDebug() << "Tree #"<< id() << "value" << sum << "eigenvalue" << eigenvalue << "Impact" << mLRI;
290
    return mLRI;
210
    return mLRI;
291
}
211
}
292
212
293
/*
-
 
294
double Tree::readStampMul()
-
 
295
{
-
 
296
    if (!m_stamp)
-
 
297
        return 0.;
-
 
298
    const Stamp *reader = m_stamp->reader();
-
 
299
    if (!reader)
-
 
300
        return 0.;
-
 
301
    QPoint pos_reader = m_grid->indexAt(m_Position);
-
 
302

-
 
303
    int offset_reader = reader->offset();
-
 
304
    int offset_writer = m_stamp->offset();
-
 
305
    int d_offset = offset_writer - offset_reader;
-
 
306

-
 
307
    QPoint pos_writer=pos_reader - QPoint(offset_writer, offset_writer);
-
 
308
    pos_reader-=QPoint(offset_reader, offset_reader);
-
 
309
    QPoint p;
-
 
310

-
 
311
    float dom_height = (*m_dominanceGrid)[m_Position];
-
 
312
    float local_dom;
-
 
313

-
 
314
    int x,y;
-
 
315
    double sum=0.;
-
 
316
    double value, own_value;
-
 
317
    for (x=0;x<reader->size();++x) {
-
 
318
        for (y=0;y<reader->size(); ++y) {
-
 
319
            p = pos_reader + QPoint(x,y);
-
 
320
            if (m_grid->isIndexValid(p)) {
-
 
321
                local_dom = m_dominanceGrid->valueAt( m_grid->cellCoordinates(p) );
-
 
322
                own_value = 1. - m_stamp->offsetValue(x,y,d_offset)*lafactor /local_dom; // old: dom_height;
-
 
323
                own_value = qMax(own_value, 0.02);
-
 
324
                value =  m_grid->valueAtIndex(p) / own_value; // remove impact of focal tree
-
 
325
                if (value>0.)
-
 
326
                    sum += value * (*reader)(x,y);
-
 
327
                //value = (1. - m_stamp->offsetValue(x,y,d_offset)/dom_height);
-
 
328
                //value = (1 - m_grid->valueAtIndex(p)) / (m_stamp->offsetValue(x,y,d_offset)/dom_height * lafactor);
-
 
329
                if (isDebugging()) {
-
 
330
                    qDebug() << "Tree" << id() << "Coord" << p << "gridvalue" << m_grid->valueAtIndex(p) << "value" << value << "reader" << (*reader)(x,y) << "writer" << m_stamp->offsetValue(x,y,d_offset);
-
 
331
                }
-
 
332

-
 
333
            }
-
 
334
        }
-
 
335
    }
-
 
336
    mImpact = sum;
-
 
337
    // read dominance field...
-
 
338
    if (dom_height < m_Height) {
-
 
339
        // if tree is higher than Z*, the dominance height
-
 
340
        // a part of the crown is in "full light".
-
 
341
        m_statAboveZ++;
-
 
342
        mImpact = 1. - (1. - mImpact)*dom_height/m_Height;
-
 
343
    }
-
 
344
    if (mImpact > 1)
-
 
345
        mImpact = 1.f;
-
 
346
    //qDebug() << "Tree #"<< id() << "value" << sum << "Impact" << mImpact;
-
 
347
    return mImpact;
-
 
348
}*/
-
 
349
213
350
void Tree::readStampMul()
214
void Tree::readStampMul()
351
{
215
{
352
    if (!mStamp)
216
    if (!mStamp)
353
        return;
217
        return;
Line 378... Line 242...
378
        grid_value = mGrid->ptr(rx, ry);
242
        grid_value = mGrid->ptr(rx, ry);
379
        for (x=0;x<reader_size;++x) {
243
        for (x=0;x<reader_size;++x) {
380
244
381
            //p = pos_reader + QPoint(x,y);
245
            //p = pos_reader + QPoint(x,y);
382
            //if (m_grid->isIndexValid(p)) {
246
            //if (m_grid->isIndexValid(p)) {
383
            local_dom = mHeightGrid->valueAtIndex((rx+x)/5, ry/5);
-
 
-
 
247
            local_dom = mHeightGrid->valueAtIndex((rx+x)/5, ry/5); // ry: gets ++ed in outer loop, rx not
384
            //local_dom = m_dominanceGrid->valueAt( m_grid->cellCoordinates(p) );
248
            //local_dom = m_dominanceGrid->valueAt( m_grid->cellCoordinates(p) );
385
            own_value = 1. - mStamp->offsetValue(x,y,d_offset)*lafactor /local_dom; // old: dom_height;
-
 
-
 
249
-
 
250
            own_value = 1. - mStamp->offsetValue(x,y,d_offset)*lafactor / local_dom; // old: dom_height;
386
            own_value = qMax(own_value, 0.02);
251
            own_value = qMax(own_value, 0.02);
387
            value =  *grid_value++ / own_value; // remove impact of focal tree
252
            value =  *grid_value++ / own_value; // remove impact of focal tree
388
            //if (value>0.)
253
            //if (value>0.)
389
            sum += value * (*reader)(x,y);
254
            sum += value * (*reader)(x,y);
390
255
Line 446... Line 311...
446
    double gpp = raw_gpp * 0.6; // aging
311
    double gpp = raw_gpp * 0.6; // aging
447
    double npp = gpp * 0.47; // respiration loss
312
    double npp = gpp * 0.47; // respiration loss
448
313
449
    partitioning(npp);
314
    partitioning(npp);
450
315
451
    calcBiomassCompartments();
-
 
-
 
316
     mStamp = mSpecies->stamp(mDbh, mHeight); // get new stamp for updated dimensions
452
317
453
}
318
}
454
319
455
320
456
// just used to test the DBG_IF_x macros...
321
// just used to test the DBG_IF_x macros...
Line 510... Line 375...
510
    double net_stem = gross_stem - senescence_stem;
375
    double net_stem = gross_stem - senescence_stem;
511
376
512
    // flow back to reserve pool:
377
    // flow back to reserve pool:
513
    double to_reserve = qMin(reserve_size, net_stem);
378
    double to_reserve = qMin(reserve_size, net_stem);
514
    net_stem -= to_reserve;
379
    net_stem -= to_reserve;
-
 
380
-
 
381
    DBG_IF_X(mId == 1 , "Tree::partitioning", "dump", dump()
-
 
382
             + 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")
-
 
383
               .arg(npp).arg(senescence_foliage).arg(senescence_stem).arg(senescence_root)
-
 
384
               .arg(net_foliage).arg(net_stem).arg(net_root).arg(to_reserve).arg(mNPPReserve) );
515
385
516
    // update of compartments
386
    // update of compartments
517
    mLeafMass += net_foliage;
387
    mLeafMass += net_foliage;
-
 
388
    mLeafMass = qMax(mLeafMass, 0.f);
-
 
389
    mLeafArea = mLeafMass * mSpecies->specificLeafArea();
-
 
390
518
    mRootMass += net_root;
391
    mRootMass += net_root;
-
 
392
    mRootMass = qMax(mRootMass, 0.f);
519
393
520
    // calculate the dimension of growth
-
 
-
 
394
    // calculate the dimensions of growth (diameter, height)
521
    grow_diameter(net_stem);
395
    grow_diameter(net_stem);
522
396
523
    //DBG_IF(apct_foliage<0, "Tree::partitioning", "foliage out of range");
-
 
524
    //DBG_IF_X(mId==1, "Tree::partitioning", "foliage out of range", test_cntr());
-
 
-
 
397
    // calculate stem biomass using the allometric equation
-
 
398
    mStemMass = mSpecies->biomassStem(mDbh);
525
399
526
}
400
}
527
401
-
 
402
-
 
403
/** Determination of diamter and height growth based on increment of the stem mass (@net_stem_npp).
-
 
404
    Refer to XXX for equations and variables.
-
 
405
    This function updates the dbh and height of the tree.
-
 
406
  */
528
inline void Tree::grow_diameter(const double &net_stem_npp)
407
inline void Tree::grow_diameter(const double &net_stem_npp)
529
{
408
{
530
    // determine dh-ratio of increment
409
    // determine dh-ratio of increment
531
    // height increment is a function of light competition:
410
    // height increment is a function of light competition:
532
    double rel_height_growth = relative_height_growth(); // [0..1]
-
 
-
 
411
    double hd_growth = relative_height_growth(); // hd of height growth
-
 
412
    //DBG_IF_X(rel_height_growth<0 || rel_height_growth>1., "Tree::grow_dimater", "rel_height_growth out of bound.", dump());
533
413
-
 
414
    const double volume_factor = mSpecies->volumeFactor();
534
415
-
 
416
    double factor_diameter = 1. / (  volume_factor * (mDbh + mDbhDelta)*(mDbh + mDbhDelta) * ( 2. * mHeight/mDbh + hd_growth) );
-
 
417
    double delta_d_estimate = factor_diameter * net_stem_npp; // estimated dbh-inc using last years increment
-
 
418
-
 
419
    // using that dbh-increment we estimate a stem-mass-increment and the residual (Eq. 9)
-
 
420
    double stem_estimate = volume_factor * (mDbh + delta_d_estimate)*(mDbh + delta_d_estimate)*(mHeight + delta_d_estimate*hd_growth);
-
 
421
    double stem_residual = stem_estimate - (mStemMass + net_stem_npp);
-
 
422
-
 
423
    // the final increment is then:
-
 
424
    double d_increment = factor_diameter * (net_stem_npp - stem_residual); // Eq. (11)
-
 
425
    DBG_IF_X(mId == 1 || d_increment<0., "Tree::grow_dimater", "increment < 0.", dump()
-
 
426
             + QString("\nhdz %1 factor_diameter %2 stem_residual %3 delta_d_estimate %4 d_increment %5 final residual(kg) %6")
-
 
427
               .arg(hd_growth).arg(factor_diameter).arg(stem_residual).arg(delta_d_estimate).arg(d_increment)
-
 
428
               .arg( volume_factor * (mDbh + d_increment)*(mDbh + d_increment)*(mHeight + d_increment*hd_growth)-((mStemMass + net_stem_npp)) ));
-
 
429
-
 
430
    DBGMODE(
-
 
431
        double res_final = volume_factor * (mDbh + d_increment)*(mDbh + d_increment)*(mHeight + d_increment*hd_growth)-((mStemMass + net_stem_npp));
-
 
432
        DBG_IF_X(res_final > 1, "Tree::grow_diameter", "final residual stem estimate > 1kg", dump());
-
 
433
        DBG_IF_X(d_increment > 10. || d_increment*hd_growth/100. >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());
-
 
434
        //dbgstruct["sen_demand"]=sen_demand;
-
 
435
-
 
436
-
 
437
            );
-
 
438
    d_increment = qMax(d_increment, 0.);
-
 
439
-
 
440
    // update state variables
-
 
441
    mDbh += d_increment;
-
 
442
    mDbhDelta = d_increment; // save for next year's growth
-
 
443
    mHeight += d_increment * hd_growth * 0.01;
535
}
444
}
536
445
-
 
446
-
 
447
/// return the HD ratio of this year's increment based on the light status.
537
inline double Tree::relative_height_growth()
448
inline double Tree::relative_height_growth()
538
{
449
{
539
    double hd_low, hd_high;
450
    double hd_low, hd_high;
540
    mSpecies->hdRange(mDbh, hd_low, hd_high);
451
    mSpecies->hdRange(mDbh, hd_low, hd_high);
541
452
542
    // scale according to LRI: if having much light (LRI=1), the result is hd_low (for open grown trees)
-
 
543
    double result = hd_high - (hd_high-hd_low)*mLRI;
-
 
544
    return result;
-
 
-
 
453
    DBG_IF_X(hd_low>hd_high, "Tree::relative_height_growth", "hd low higher dann hd_high for ", dump());
-
 
454
    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));
-
 
455
-
 
456
    // scale according to LRI: if receiving much light (LRI=1), the result is hd_low (for open grown trees)
-
 
457
    double hd_ratio = hd_high - (hd_high-hd_low)*mLRI;
-
 
458
    return hd_ratio;
545
}
459
}