Subversion Repositories public iLand

Rev

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