Subversion Repositories public iLand

Rev

Rev 1164 | Rev 1168 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1
 
1111 werner 2
#include "global.h"
3
#include "saplings.h"
4
 
5
#include "globalsettings.h"
6
#include "model.h"
7
#include "resourceunit.h"
8
#include "resourceunitspecies.h"
9
#include "establishment.h"
10
#include "species.h"
11
#include "seeddispersal.h"
12
 
1113 werner 13
double Saplings::mRecruitmentVariation = 0.1; // +/- 10%
14
double Saplings::mBrowsingPressure = 0.;
1111 werner 15
 
1113 werner 16
 
1111 werner 17
Saplings::Saplings()
18
{
19
 
20
}
21
 
22
void Saplings::setup()
23
{
1159 werner 24
    //mGrid.setup(GlobalSettings::instance()->model()->grid()->metricRect(), GlobalSettings::instance()->model()->grid()->cellsize());
25
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
1111 werner 26
    // mask out out-of-project areas
27
    HeightGrid *hg = GlobalSettings::instance()->model()->heightGrid();
1159 werner 28
    for (int i=0; i<lif_grid->count(); ++i) {
29
        SaplingCell *s = cell(lif_grid->indexOf(i), false); // false: retrieve also invalid cells
30
        if (s) {
31
            if (!hg->valueAtIndex(lif_grid->index5(i)).isValid())
32
                s->state = SaplingCell::CellInvalid;
33
            else
34
                s->state = SaplingCell::CellFree;
35
        }
36
 
1111 werner 37
    }
38
 
39
}
40
 
41
void Saplings::establishment(const ResourceUnit *ru)
42
{
43
    HeightGrid *height_grid = GlobalSettings::instance()->model()->heightGrid();
44
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
45
 
1118 werner 46
    QPoint imap = ru->cornerPointOffset(); // offset on LIF/saplings grid
47
    QPoint iseedmap = QPoint(imap.x()/10, imap.y()/10); // seed-map has 20m resolution, LIF 2m -> factor 10
1111 werner 48
 
1158 werner 49
    for (QList<ResourceUnitSpecies*>::const_iterator i=ru->ruSpecies().constBegin(); i!=ru->ruSpecies().constEnd(); ++i)
50
        (*i)->saplingStat().clearStatistics();
51
 
52
    double lif_corr[cPxPerHectare];
53
    for (int i=0;i<cPxPerHectare;++i)
54
        lif_corr[i]=-1.;
55
 
1164 werner 56
    int species_idx;
57
    QVector<int>::const_iterator sbegin, send;
58
    ru->speciesSet()->randomSpeciesOrder(sbegin, send);
59
    for (QVector<int>::const_iterator s_idx=sbegin; s_idx!=send;++s_idx) {
1111 werner 60
 
61
        // start from a random species (and cycle through the available species)
1164 werner 62
        species_idx = *s_idx;
1111 werner 63
 
64
        ResourceUnitSpecies *rus = ru->ruSpecies()[species_idx];
65
        // check if there are seeds of the given species on the resource unit
66
        float seeds = 0.f;
1118 werner 67
        Grid<float> &seedmap =  const_cast<Grid<float>& >(rus->species()->seedDispersal()->seedMap());
1111 werner 68
        for (int iy=0;iy<5;++iy) {
69
            float *p = seedmap.ptr(iseedmap.x(), iseedmap.y());
70
            for (int ix=0;ix<5;++ix)
71
                seeds += *p++;
72
        }
73
        // if there are no seeds: no need to do more
74
        if (seeds==0.f)
75
            continue;
76
 
77
        // calculate the abiotic environment (TACA)
78
        rus->establishment().calculateAbioticEnvironment();
79
        double abiotic_env = rus->establishment().abioticEnvironment();
80
        if (abiotic_env==0.)
81
            continue;
82
 
83
        // loop over all 2m cells on this resource unit
1159 werner 84
        SaplingCell *sap_cells = ru->saplingCellArray();
1111 werner 85
        SaplingCell *s;
86
        int isc = 0; // index on 2m cell
87
        for (int iy=0; iy<cPxPerRU; ++iy) {
1159 werner 88
            //s = mGrid.ptr(imap.x(), imap.y()+iy); // ptr to the row
89
            s = &sap_cells[iy*cPxPerRU]; // pointer to a row
90
            isc = lif_grid->index(imap.x(), imap.y()+iy);
1111 werner 91
 
1158 werner 92
            for (int ix=0;ix<cPxPerRU; ++ix, ++s, ++isc) {
1111 werner 93
                if (s->state == SaplingCell::CellFree) {
94
                    // is a sapling of the current species already on the pixel?
95
                    // * test for sapling height already in cell state
96
                    // * test for grass-cover already in cell state
1158 werner 97
                    SaplingTree *stree=0;
98
                    SaplingTree *slot=s->saplings;
99
                    for (int i=0;i<NSAPCELLS;++i, ++slot) {
100
                        if (!stree && !slot->is_occupied())
101
                            stree=slot;
102
                        if (slot->species_index == species_idx) {
103
                            stree=0;
104
                            break;
1111 werner 105
                        }
106
                    }
107
 
1158 werner 108
                    if (stree) {
1111 werner 109
                        // grass cover?
1159 werner 110
                        float seed_map_value = seedmap[lif_grid->index10(isc)];
1111 werner 111
                        if (seed_map_value==0.f)
112
                            continue;
1159 werner 113
                        const HeightGridValue &hgv = (*height_grid)[lif_grid->index5(isc)];
1111 werner 114
                        float lif_value = (*lif_grid)[isc];
1158 werner 115
 
116
                        double &lif_corrected = lif_corr[iy*cPxPerRU+ix];
117
                        // calculate the LIFcorrected only once per pixel
118
                        if (lif_corrected<0.)
119
                            lif_corrected = rus->species()->speciesSet()->LRIcorrection(lif_value, 4. / hgv.height);
120
 
1111 werner 121
                        // check for the combination of seed availability and light on the forest floor
1158 werner 122
                        if (drandom() < seed_map_value*lif_corrected*abiotic_env ) {
123
                            // ok, lets add a sapling at the given position (age is incremented later)
124
                            stree->setSapling(0.05f, 0, species_idx);
125
                            s->checkState();
126
                            rus->saplingStat().mAdded++;
1111 werner 127
 
1158 werner 128
                        }
1111 werner 129
 
130
                    }
131
 
132
                }
133
            }
134
        }
135
 
136
    }
137
 
138
}
1113 werner 139
 
140
void Saplings::saplingGrowth(const ResourceUnit *ru)
141
{
142
    HeightGrid *height_grid = GlobalSettings::instance()->model()->heightGrid();
143
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
144
 
1159 werner 145
    QPoint imap = ru->cornerPointOffset();
1115 werner 146
    bool need_check=false;
1159 werner 147
    SaplingCell *sap_cells = ru->saplingCellArray();
1113 werner 148
    for (int iy=0; iy<cPxPerRU; ++iy) {
1159 werner 149
        //SaplingCell *s = mGrid.ptr(imap.x(), imap.y()+iy); // ptr to the row
150
        SaplingCell *s = &sap_cells[iy*cPxPerRU]; // ptr to row
151
        int isc = lif_grid->index(imap.x(), imap.y()+iy);
1113 werner 152
 
153
        for (int ix=0;ix<cPxPerRU; ++ix, ++s, ++isc) {
154
            if (s->state != SaplingCell::CellInvalid) {
1115 werner 155
                need_check=false;
1113 werner 156
                for (int i=0;i<NSAPCELLS;++i) {
157
                    if (s->saplings[i].is_occupied()) {
158
                        // growth of this sapling tree
159
                        const HeightGridValue &hgv = (*height_grid)[height_grid->index5(isc)];
160
                        float lif_value = (*lif_grid)[isc];
161
 
1159 werner 162
                        need_check |= growSapling(ru, *s, s->saplings[i], isc, hgv.height, lif_value);
1113 werner 163
                    }
164
                }
1115 werner 165
                if (need_check)
166
                    s->checkState();
1113 werner 167
            }
168
        }
169
    }
170
 
1158 werner 171
 
172
 
173
 
174
    // store statistics on saplings/regeneration
175
    for (QList<ResourceUnitSpecies*>::const_iterator i=ru->ruSpecies().constBegin(); i!=ru->ruSpecies().constEnd(); ++i) {
176
        (*i)->saplingStat().calculate((*i)->species(), const_cast<ResourceUnit*>(ru));
177
        (*i)->statistics().add(&((*i)->saplingStat()));
178
    }
1113 werner 179
}
180
 
1162 werner 181
SaplingCell *Saplings::cell(QPoint lif_coords, bool only_valid, ResourceUnit **rRUPtr)
1159 werner 182
{
183
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
184
 
185
    // in this case, getting the actual cell is quite cumbersome: first, retrieve the resource unit, then the
186
    // cell based on the offset of the given coordiantes relative to the corner of the resource unit.
187
    ResourceUnit *ru = GlobalSettings::instance()->model()->ru(lif_grid->cellCenterPoint(lif_coords));
1162 werner 188
    if (rRUPtr)
189
        *rRUPtr = ru;
1160 werner 190
 
1159 werner 191
    if (ru) {
192
        QPoint local_coords = lif_coords - ru->cornerPointOffset();
193
        int idx = local_coords.y() * cPxPerRU + local_coords.x();
194
        DBGMODE( if (idx<0 || idx>=cPxPerHectare)
195
                 qDebug("invalid coords in Saplings::cell");
196
                    );
197
        SaplingCell *s=&ru->saplingCellArray()[idx];
198
        if (s && (!only_valid || s->state!=SaplingCell::CellInvalid))
199
            return s;
200
    }
201
    return 0;
202
}
203
 
1162 werner 204
void Saplings::clearSaplings(const QRectF &rectangle, const bool remove_biomass)
205
{
206
    GridRunner<float> runner(GlobalSettings::instance()->model()->grid(), rectangle);
207
    ResourceUnit *ru;
208
    while (runner.next()) {
209
        SaplingCell *s = cell(runner.currentIndex(), true, &ru);
210
        if (s) {
1165 werner 211
            clearSaplings(s, ru, remove_biomass);
212
        }
1162 werner 213
 
1165 werner 214
    }
215
}
216
 
217
void Saplings::clearSaplings(SaplingCell *s, ResourceUnit *ru, const bool remove_biomass)
218
{
219
    if (s) {
220
        for (int i=0;i<NSAPCELLS;++i)
221
            if (s->saplings[i].is_occupied()) {
222
                if (!remove_biomass) {
223
                    ResourceUnitSpecies *rus = ru->resourceUnitSpecies(s->saplings[i].species_index);
224
                    if (!rus && !rus->species()) {
225
                        qDebug() << "Saplings::clearSaplings(): invalid resource unit!!!";
226
                        return;
1162 werner 227
                    }
1165 werner 228
                    rus->saplingStat().addCarbonOfDeadSapling( s->saplings[i].height / rus->species()->saplingGrowthParameters().hdSapling * 100.f );
1162 werner 229
                }
1165 werner 230
                s->saplings[i].clear();
231
            }
232
        s->checkState();
1162 werner 233
 
1165 werner 234
    }
235
 
236
}
237
 
238
int Saplings::addSprout(const Tree *t)
239
{
240
    if (t->species()->saplingGrowthParameters().sproutGrowth==0.)
241
        return 0;
242
    SaplingCell *sc = cell(t->positionIndex());
243
    if (!sc)
244
        return 0;
245
    clearSaplings(sc, const_cast<ResourceUnit*>(t->ru()), false );
246
    SaplingTree *st=sc->addSapling(0.05f, 0, t->species()->index());
247
    if (st)
248
        st->set_sprout(true);
249
 
250
    // neighboring cells
251
    double crown_area = t->crownRadius()*t->crownRadius() * M_PI; //m2
252
    // calculate how many cells on the ground are covered by the crown (this is a rather rough estimate)
253
    // n_cells: in addition to the original cell
254
    int n_cells = static_cast<int>(round( crown_area / static_cast<double>(cPxSize*cPxSize) - 1.));
255
    if (n_cells>0) {
256
        ResourceUnit *ru;
257
        static const int offsets_x[8] = {1,1,0,-1,-1,-1,0,1};
258
        static const int offsets_y[8] = {0,1,1,1,0,-1,-1,-1};
259
        int s=irandom(0,8);
260
        while(n_cells) {
261
            sc = cell(t->positionIndex()+QPoint(offsets_x[s], offsets_y[s]),true,&ru);
262
            if (sc) {
263
                clearSaplings(sc, ru, false );
264
                SaplingTree *st=sc->addSapling(0.05f, 0, t->species()->index());
265
                if (st)
266
                    st->set_sprout(true);
267
            }
268
 
269
            s = (s+1)%8; --n_cells;
1162 werner 270
        }
271
    }
1165 werner 272
    return 1;
1162 werner 273
}
274
 
1113 werner 275
void Saplings::updateBrowsingPressure()
276
{
277
    if (GlobalSettings::instance()->settings().valueBool("model.settings.browsing.enabled"))
278
        Saplings::mBrowsingPressure = GlobalSettings::instance()->settings().valueDouble("model.settings.browsing.browsingPressure");
279
    else
280
        Saplings::mBrowsingPressure = 0.;
281
}
282
 
1159 werner 283
bool Saplings::growSapling(const ResourceUnit *ru, SaplingCell &scell, SaplingTree &tree, int isc, float dom_height, float lif_value)
1113 werner 284
{
285
    ResourceUnitSpecies *rus = const_cast<ResourceUnitSpecies*>(ru->ruSpecies()[tree.species_index]);
286
    const Species *species = rus->species();
287
 
288
    // (1) calculate height growth potential for the tree (uses linerization of expressions...)
289
    double h_pot = species->saplingGrowthParameters().heightGrowthPotential.calculate(tree.height);
290
    double delta_h_pot = h_pot - tree.height;
291
 
292
    // (2) reduce height growth potential with species growth response f_env_yr and with light state (i.e. LIF-value) of home-pixel.
293
    if (dom_height==0.f)
294
        throw IException(QString("growSapling: height grid at %1/%2 has value 0").arg(isc));
295
 
296
    double rel_height = tree.height / dom_height;
297
 
298
    double lif_corrected = species->speciesSet()->LRIcorrection(lif_value, rel_height); // correction based on height
299
 
300
    double lr = species->lightResponse(lif_corrected); // species specific light response (LUI, light utilization index)
301
 
1118 werner 302
    rus->calculate(true); // calculate the 3pg module (this is done only if that did not happen up to now); true: call comes from regeneration
303
    double f_env_yr = rus->prod3PG().fEnvYear();
1113 werner 304
 
1118 werner 305
    double delta_h_factor = f_env_yr * lr; // relative growth
306
 
1113 werner 307
    if (h_pot<0. || delta_h_pot<0. || lif_corrected<0. || lif_corrected>1. || delta_h_factor<0. || delta_h_factor>1. )
308
        qDebug() << "invalid values in Sapling::growSapling";
309
 
1165 werner 310
    // sprouts grow faster. Sprouts therefore are less prone to stress (threshold), and can grow higher than the growth potential.
311
    if (tree.is_sprout())
312
        delta_h_factor = delta_h_factor *species->saplingGrowthParameters().sproutGrowth;
313
 
1113 werner 314
    // check browsing
315
    if (mBrowsingPressure>0. && tree.height<=2.f) {
316
        double p = rus->species()->saplingGrowthParameters().browsingProbability;
317
        // calculate modifed annual browsing probability via odds-ratios
318
        // odds = p/(1-p) -> odds_mod = odds * browsingPressure -> p_mod = odds_mod /( 1 + odds_mod) === p*pressure/(1-p+p*pressure)
319
        double p_browse = p*mBrowsingPressure / (1. - p + p*mBrowsingPressure);
320
        if (drandom() < p_browse) {
321
            delta_h_factor = 0.;
322
        }
323
    }
324
 
325
    // check mortality of saplings
326
    if (delta_h_factor < species->saplingGrowthParameters().stressThreshold) {
327
        tree.stress_years++;
328
        if (tree.stress_years > species->saplingGrowthParameters().maxStressYears) {
329
            // sapling dies...
1160 werner 330
            rus->saplingStat().addCarbonOfDeadSapling( tree.height / species->saplingGrowthParameters().hdSapling * 100.f );
1113 werner 331
            tree.clear();
1115 werner 332
            return true; // need cleanup
1113 werner 333
        }
334
    } else {
335
        tree.stress_years=0; // reset stress counter
336
    }
1165 werner 337
    DBG_IF(delta_h_pot*delta_h_factor < 0.f || (!tree.is_sprout() && delta_h_pot*delta_h_factor > 2.), "Sapling::growSapling", "inplausible height growth.");
1113 werner 338
 
339
    // grow
340
    tree.height += delta_h_pot * delta_h_factor;
341
    tree.age++; // increase age of sapling by 1
342
 
343
    // recruitment?
344
    if (tree.height > 4.f) {
345
        rus->saplingStat().mRecruited++;
346
 
347
        float dbh = tree.height / species->saplingGrowthParameters().hdSapling * 100.f;
348
        // the number of trees to create (result is in trees per pixel)
349
        double n_trees = species->saplingGrowthParameters().representedStemNumber(dbh);
350
        int to_establish = static_cast<int>( n_trees );
351
 
352
        // if n_trees is not an integer, choose randomly if we should add a tree.
353
        // e.g.: n_trees = 2.3 -> add 2 trees with 70% probability, and add 3 trees with p=30%.
354
        if (drandom() < (n_trees-to_establish) || to_establish==0)
355
            to_establish++;
356
 
357
        // add a new tree
358
        for (int i=0;i<to_establish;i++) {
359
            Tree &bigtree = const_cast<ResourceUnit*>(ru)->newTree();
360
 
1159 werner 361
            bigtree.setPosition(GlobalSettings::instance()->model()->grid()->indexOf(isc));
1113 werner 362
            // add variation: add +/-10% to dbh and *independently* to height.
1158 werner 363
            bigtree.setDbh(static_cast<float>(dbh * nrandom(1. - mRecruitmentVariation, 1. + mRecruitmentVariation)));
364
            bigtree.setHeight(static_cast<float>(tree.height * nrandom(1. - mRecruitmentVariation, 1. + mRecruitmentVariation)));
1113 werner 365
            bigtree.setSpecies( const_cast<Species*>(species) );
366
            bigtree.setAge(tree.age,tree.height);
367
            bigtree.setRU(const_cast<ResourceUnit*>(ru));
368
            bigtree.setup();
369
            const Tree *t = &bigtree;
370
            const_cast<ResourceUnitSpecies*>(rus)->statistics().add(t, 0); // count the newly created trees already in the stats
371
        }
372
        // clear all regeneration from this pixel (including this tree)
373
        tree.clear(); // clear this tree (no carbon flow to the ground)
374
        for (int i=0;i<NSAPCELLS;++i) {
1159 werner 375
            if (scell.saplings[i].is_occupied()) {
1113 werner 376
                // add carbon to the ground
1159 werner 377
                rus->saplingStat().addCarbonOfDeadSapling( scell.saplings[i].height / species->saplingGrowthParameters().hdSapling * 100.f );
378
                scell.saplings[i].clear();
1113 werner 379
            }
380
        }
1115 werner 381
        return true; // need cleanup
1113 werner 382
    }
383
    // book keeping (only for survivors) for the sapling of the resource unit / species
384
    SaplingStat &ss = rus->saplingStat();
385
    ss.mLiving++;
386
    ss.mAvgHeight+=tree.height;
387
    ss.mAvgAge+=tree.age;
388
    ss.mAvgDeltaHPot+=delta_h_pot;
389
    ss.mAvgHRealized += delta_h_pot * delta_h_factor;
1115 werner 390
    return false;
1113 werner 391
}
392
 
393
void SaplingStat::clearStatistics()
394
{
395
    mRecruited=mDied=mLiving=0;
396
    mSumDbhDied=0.;
397
    mAvgHeight=0.;
398
    mAvgAge=0.;
399
    mAvgDeltaHPot=mAvgHRealized=0.;
1158 werner 400
    mAdded=0;
1113 werner 401
 
402
}
1158 werner 403
 
404
void SaplingStat::calculate(const Species *species, ResourceUnit *ru)
405
{
406
    if (mLiving) {
407
        mAvgHeight /= double(mLiving);
408
        mAvgAge /= double(mLiving);
409
        mAvgDeltaHPot /= double(mLiving);
410
        mAvgHRealized /= double(mLiving);
411
    }
412
 
413
    // calculate carbon balance
414
    CNPair old_state = mCarbonLiving;
415
    mCarbonLiving.clear();
416
 
417
    CNPair dead_wood, dead_fine; // pools for mortality
418
    // average dbh
419
    if (mLiving>0) {
420
        // calculate the avg dbh and number of stems
421
        double avg_dbh = mAvgHeight / species->saplingGrowthParameters().hdSapling * 100.;
422
        double n = mLiving * species->saplingGrowthParameters().representedStemNumber( avg_dbh );
423
        // woody parts: stem, branchse and coarse roots
424
        double woody_bm = species->biomassWoody(avg_dbh) + species->biomassBranch(avg_dbh) + species->biomassRoot(avg_dbh);
425
        double foliage = species->biomassFoliage(avg_dbh);
426
        double fineroot = foliage*species->finerootFoliageRatio();
427
 
428
        mCarbonLiving.addBiomass( woody_bm*n, species->cnWood()  );
429
        mCarbonLiving.addBiomass( foliage*n, species->cnFoliage()  );
430
        mCarbonLiving.addBiomass( fineroot*n, species->cnFineroot()  );
431
 
1160 werner 432
        DBGMODE(
433
        if (isnan(mCarbonLiving.C))
434
            qDebug("carbon NaN in SaplingStat::calculate (living trees).");
435
                );
436
 
1158 werner 437
        // turnover
438
        if (ru->snag())
439
            ru->snag()->addTurnoverLitter(species, foliage*species->turnoverLeaf(), fineroot*species->turnoverRoot());
440
 
441
        // calculate the "mortality from competition", i.e. carbon that stems from reduction of stem numbers
442
        // from Reinekes formula.
443
        //
444
        if (avg_dbh>1.) {
445
            double avg_dbh_before = (mAvgHeight - mAvgHRealized) / species->saplingGrowthParameters().hdSapling * 100.;
446
            double n_before = mLiving * species->saplingGrowthParameters().representedStemNumber( qMax(1.,avg_dbh_before) );
447
            if (n<n_before) {
448
                dead_wood.addBiomass( woody_bm * (n_before-n), species->cnWood() );
449
                dead_fine.addBiomass( foliage * (n_before-n), species->cnFoliage()  );
450
                dead_fine.addBiomass( fineroot * (n_before-n), species->cnFineroot()  );
1160 werner 451
                DBGMODE(
452
                if (isnan(dead_fine.C))
453
                    qDebug("carbon NaN in SaplingStat::calculate (self thinning).");
454
                        );
455
 
1158 werner 456
            }
457
        }
458
 
459
    }
460
    if (mDied) {
461
        double avg_dbh_dead = mSumDbhDied / double(mDied);
462
        double n = mDied * species->saplingGrowthParameters().representedStemNumber( avg_dbh_dead );
463
        // woody parts: stem, branchse and coarse roots
464
 
465
        dead_wood.addBiomass( ( species->biomassWoody(avg_dbh_dead) + species->biomassBranch(avg_dbh_dead) + species->biomassRoot(avg_dbh_dead)) * n, species->cnWood()  );
466
        double foliage = species->biomassFoliage(avg_dbh_dead)*n;
467
 
468
        dead_fine.addBiomass( foliage, species->cnFoliage()  );
469
        dead_fine.addBiomass( foliage*species->finerootFoliageRatio(), species->cnFineroot()  );
1160 werner 470
        DBGMODE(
471
        if (isnan(dead_fine.C))
472
            qDebug("carbon NaN in SaplingStat::calculate (died trees).");
473
                );
474
 
1158 werner 475
    }
476
    if (!dead_wood.isEmpty() || !dead_fine.isEmpty())
477
        if (ru->snag())
478
            ru->snag()->addToSoil(species, dead_wood, dead_fine);
479
 
480
    // calculate net growth:
481
    // delta of stocks
482
    mCarbonGain = mCarbonLiving + dead_fine + dead_wood - old_state;
483
    if (mCarbonGain.C < 0)
484
        mCarbonGain.clear();
485
 
486
 
487
    GlobalSettings::instance()->systemStatistics()->saplingCount+=mLiving;
488
    GlobalSettings::instance()->systemStatistics()->newSaplings+=mAdded;
489
 
490
}
1162 werner 491
 
492
double SaplingStat::livingStemNumber(const Species *species, double &rAvgDbh, double &rAvgHeight, double &rAvgAge) const
493
{
494
     rAvgHeight = averageHeight();
495
     rAvgDbh = rAvgHeight / species->saplingGrowthParameters().hdSapling * 100.f;
496
     rAvgAge = averageAge();
497
     double n= species->saplingGrowthParameters().representedStemNumber(rAvgDbh);
498
     return n;
499
// *** old code (sapling.cpp) ***
500
//    double total = 0.;
501
//    double dbh_sum = 0.;
502
//    double h_sum = 0.;
503
//    double age_sum = 0.;
504
//    const SaplingGrowthParameters &p = mRUS->species()->saplingGrowthParameters();
505
//    for (QVector<SaplingTreeOld>::const_iterator it = mSaplingTrees.constBegin(); it!=mSaplingTrees.constEnd(); ++it) {
506
//        float dbh = it->height / p.hdSapling * 100.f;
507
//        if (dbh<1.) // minimum size: 1cm
508
//            continue;
509
//        double n = p.representedStemNumber(dbh); // one cohort on the pixel represents that number of trees
510
//        dbh_sum += n*dbh;
511
//        h_sum += n*it->height;
512
//        age_sum += n*it->age.age;
513
//        total += n;
514
//    }
515
//    if (total>0.) {
516
//        dbh_sum /= total;
517
//        h_sum /= total;
518
//        age_sum /= total;
519
//    }
520
//    rAvgDbh = dbh_sum;
521
//    rAvgHeight = h_sum;
522
//    rAvgAge = age_sum;
523
//    return total;
524
}