Subversion Repositories public iLand

Rev

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