Subversion Repositories public iLand

Rev

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