Subversion Repositories public iLand

Rev

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

Rev Author Line No. Line
1
 
468 werner 2
#include "snag.h"
3
#include "tree.h"
4
#include "species.h"
5
#include "globalsettings.h"
6
#include "expression.h"
490 werner 7
// for calculation of climate decomposition
8
#include "resourceunit.h"
9
#include "watercycle.h"
10
#include "climate.h"
468 werner 11
 
12
/** @class Snag
13
  Snag deals with carbon / nitrogen fluxes from the forest until the reach soil pools.
490 werner 14
  Snag lives on the level of the ResourceUnit; carbon fluxes from trees enter Snag, and parts of the biomass of snags
468 werner 15
  is subsequently forwarded to the soil sub model.
522 werner 16
  Carbon is stored in three classes (depending on the size)
468 werner 17
 
18
  */
19
// static variables
522 werner 20
double Snag::mDBHLower = 0.;
21
double Snag::mDBHHigher = 0.;
22
double Snag::mCarbonThreshold[] = {0., 0., 0.};
23
 
468 werner 24
double CNPool::biomassCFraction = biomassCFraction; // get global from globalsettings.h
25
 
522 werner 26
void Snag::setupThresholds(const double lower, const double upper)
27
{
28
    if (mDBHLower == lower)
29
        return;
30
    mDBHLower = lower;
31
    mDBHHigher = upper;
32
    mCarbonThreshold[0] = lower / 2.;
33
    mCarbonThreshold[1] = lower + (upper - lower)/2.;
34
    mCarbonThreshold[2] = upper + (upper - lower)/2.;
35
    //# threshold levels for emptying out the dbh-snag-classes
36
    //# derived from Psme woody allometry, converted to C, with a threshold level set to 10%
37
    //# values in kg!
38
    for (int i=0;i<3;i++)
39
        mCarbonThreshold[i] = 0.10568*pow(mCarbonThreshold[i],2.4247)*0.5*0.1;
40
}
41
 
42
 
468 werner 43
// species specific soil paramters
44
struct SoilParameters
45
{
522 werner 46
    SoilParameters(): kyl(0.15), kyr(0.0807), ksw(0.015), cnFoliage(75.), cnFineroot(40.), cnWood(300.), halfLife(15.) {}
468 werner 47
    double kyl; // litter decomposition rate
48
    double kyr; // downed woody debris (dwd) decomposition rate
49
    double ksw; // standing woody debris (swd) decomposition rate
50
    double cnFoliage; //  C/N foliage litter
51
    double cnFineroot; // C/N ratio fine root
52
    double cnWood; // C/N Wood: used for brances, stem and coarse root
522 werner 53
    double halfLife; // half-life period of the given species (for calculation of SWD->DWD transition)
468 werner 54
} soilparams;
55
 
56
 
57
Snag::Snag()
58
{
490 werner 59
    mRU = 0;
476 werner 60
    CNPool::setCFraction(biomassCFraction);
523 werner 61
    Snag::setupThresholds(20., 60.);
468 werner 62
}
63
 
490 werner 64
void Snag::setup( const ResourceUnit *ru)
468 werner 65
{
490 werner 66
    mRU = ru;
67
    mClimateFactor = 0.;
468 werner 68
    // branches
69
    mBranchCounter=0;
70
    for (int i=0;i<3;i++) {
71
        mTimeSinceDeath[i] = 0.;
72
        mNumberOfSnags[i] = 0.;
522 werner 73
        mAvgDbh[i] = 0.;
74
        mAvgHeight[i] = 0.;
75
        mAvgVolume[i] = 0.;
76
        mKSW[i] = 0.;
77
        mCurrentKSW[i] = 0.;
468 werner 78
    }
475 werner 79
    mTotalSnagCarbon = 0.;
468 werner 80
}
81
 
475 werner 82
// debug outputs
83
QList<QVariant> Snag::debugList()
84
{
85
    // list columns
86
    // for three pools
87
    QList<QVariant> list;
88
 
523 werner 89
    // totals
90
    list << mTotalSnagCarbon << mTotalIn.C << mTotalToAtm.C << mSWDtoSoil.C << mSWDtoSoil.N;
477 werner 91
    // fluxes to labile soil pool and to refractory soil pool
524 werner 92
    list << mLabileFlux.C << mLabileFlux.N << mRefractoryFlux.C << mRefractoryFlux.N;
475 werner 93
 
94
    for (int i=0;i<3;i++) {
95
        // pools "swdx_c", "swdx_n", "swdx_count", "swdx_tsd", "toswdx_c", "toswdx_n"
96
        list << mSWD[i].C << mSWD[i].N << mNumberOfSnags[i] << mTimeSinceDeath[i] << mToSWD[i].C << mToSWD[i].N;
524 werner 97
        list << mAvgDbh[i] << mAvgHeight[i] << mAvgVolume[i];
475 werner 98
    }
99
 
100
    // branch pools (5 yrs)
101
    list << mBranches[mBranchCounter].C << mBranches[mBranchCounter].N
102
            << mBranches[(mBranchCounter+1)%5].C << mBranches[(mBranchCounter+1)%5].N
103
            << mBranches[(mBranchCounter+2)%5].C << mBranches[(mBranchCounter+2)%5].N
104
            << mBranches[(mBranchCounter+3)%5].C << mBranches[(mBranchCounter+3)%5].N
105
            << mBranches[(mBranchCounter+4)%5].C << mBranches[(mBranchCounter+4)%5].N;
106
    return list;
107
}
108
 
468 werner 109
void Snag::newYear()
110
{
111
    for (int i=0;i<3;i++) {
112
        mToSWD[i].clear(); // clear transfer pools to standing-woody-debris
522 werner 113
        mCurrentKSW[i] = 0.;
468 werner 114
    }
115
    mLabileFlux.clear();
116
    mRefractoryFlux.clear();
476 werner 117
    mTotalToAtm.clear();
118
    mTotalToExtern.clear();
119
    mTotalIn.clear();
477 werner 120
    mSWDtoSoil.clear();
468 werner 121
}
122
 
490 werner 123
/// calculate the dynamic climate modifier for decomposition 're'
522 werner 124
/// calculation is done on the level of ResourceUnit because the water content per day is needed.
490 werner 125
double Snag::calculateClimateFactors()
126
{
127
    double rel_wc;
128
    double ft, fw;
129
    double f_sum = 0.;
130
    for (const ClimateDay *day=mRU->climate()->begin(); day!=mRU->climate()->end(); ++day)
131
    {
523 werner 132
        rel_wc = mRU->waterCycle()->relContent(day->day)*100.; // relative water content in per cent of the day
490 werner 133
        ft = exp(308.56*(1./56.02-1./((273.+day->temperature)-227.13)));  // empirical variable Q10 model of Lloyd and Taylor (1994), see also Adair et al. (2008)
134
        fw = pow(1.-exp(-0.2*rel_wc),5.); //  #  see Standcarb for the 'stable soil' pool
135
        f_sum += ft*fw;
136
    }
137
    // the climate factor is defined as the arithmentic annual mean value
138
    mClimateFactor = f_sum / double(mRU->climate()->daysOfYear());
139
    return mClimateFactor;
140
}
141
 
522 werner 142
/// do the yearly calculation
143
/// see http://iland.boku.ac.at/snag+dynamics
468 werner 144
void Snag::processYear()
145
{
522 werner 146
    mSWDtoSoil.clear();
477 werner 147
    if (isEmpty()) // nothing to do
475 werner 148
        return;
149
 
468 werner 150
    // process branches: every year one of the five baskets is emptied and transfered to the refractory soil pool
151
    mRefractoryFlux+=mBranches[mBranchCounter];
523 werner 152
    mSWDtoSoil += mBranches[mBranchCounter];
468 werner 153
    mBranches[mBranchCounter].clear();
154
    mBranchCounter= (mBranchCounter+1) % 5; // increase index, roll over to 0.
155
 
156
    // process standing snags.
157
    // the input of the current year is in the mToSWD-Pools
522 werner 158
 
490 werner 159
    const double climate_factor_re = calculateClimateFactors();
468 werner 160
    for (int i=0;i<3;i++) {
161
 
522 werner 162
        // update the swd-pool with this years' input
163
        if (!mToSWD[i].isEmpty()) {
164
            // update decay rate (apply average yearly input to the state parameters)
165
            mKSW[i] = mKSW[i]*(mSWD[i].C/(mSWD[i].C+mToSWD[i].C)) + mCurrentKSW[i]*(mToSWD[i].C/(mSWD[i].C+mToSWD[i].C));
166
            //move content to the SWD pool
167
            mSWD[i] += mToSWD[i];
168
        }
475 werner 169
 
522 werner 170
        if (mSWD[i].C > 0) {
171
            // reduce the Carbon (note: the N stays, thus the CN ratio changes)
172
            // use the decay rate that is derived as a weighted average of all standing woody debris
523 werner 173
            double survive_rate = exp(-mKSW[i] *climate_factor_re * 1. ); // 1: timestep
174
            mTotalToAtm.C += mSWD[i].C * (1. - survive_rate);
175
            mSWD[i].C *= survive_rate;
468 werner 176
 
522 werner 177
            // transition to downed woody debris
178
            // update: use negative exponential decay, species parameter: half-life
179
            // modified for the climatic effect on decomposition, i.e. if decomp is slower, snags stand longer and vice versa
180
            // this is loosely oriented on Standcarb2 (http://andrewsforest.oregonstate.edu/pubs/webdocs/models/standcarb2.htm),
181
            // where lag times for cohort transitions are linearly modified with re although here individual good or bad years have
182
            // an immediate effect, the average climatic influence should come through (and it is inherently transient)
183
            // note that swd.hl is species-specific, and thus a weighted average over the species in the input (=mortality)
184
            // needs to be calculated, followed by a weighted update of the previous swd.hl.
185
            // As weights here we use stem number, as the processes here pertain individual snags
186
            // calculate the transition probability of SWD to downed dead wood
468 werner 187
 
522 werner 188
            double half_life = mHalfLife[i] / climate_factor_re;
189
            double rate = -M_LN2 / half_life; // M_LN2: math. constant
190
 
191
            // higher decay rate for the class with smallest diameters
192
            if (i==0)
193
                rate*=2.;
194
 
523 werner 195
            double transfer = 1. - exp(rate);
522 werner 196
 
468 werner 197
            // calculate flow to soil pool...
522 werner 198
            mSWDtoSoil += mSWD[i] * transfer;
199
            mRefractoryFlux += mSWD[i] * transfer;
200
            mSWD[i] *= (1.-transfer); // reduce pool
468 werner 201
            // calculate the stem number of remaining snags
522 werner 202
            mNumberOfSnags[i] = mNumberOfSnags[i] * (1. - transfer);
523 werner 203
 
204
            mTimeSinceDeath[i] += 1.;
522 werner 205
            // if stems<0.5, empty the whole cohort into DWD, i.e. release the last bit of C and N and clear the stats
206
            // also, if the Carbon of an average snag is less than 10% of the original average tree
207
            // (derived from allometries for the three diameter classes), the whole cohort is emptied out to DWD
208
            if (mNumberOfSnags[i] < 0.5 || mSWD[i].C / mNumberOfSnags[i] < mCarbonThreshold[i]) {
209
                // clear the pool: add the rest to the soil, clear statistics of the pool
468 werner 210
                mRefractoryFlux += mSWD[i];
522 werner 211
                mSWDtoSoil += mSWD[i];
468 werner 212
                mSWD[i].clear();
522 werner 213
                mAvgDbh[i] = 0.;
214
                mAvgHeight[i] = 0.;
215
                mAvgVolume[i] = 0.;
216
                mKSW[i] = 0.;
217
                mCurrentKSW[i] = 0.;
218
                mHalfLife[i] = 0.;
219
                mTimeSinceDeath[i] = 0.;
468 werner 220
            }
522 werner 221
 
468 werner 222
        }
522 werner 223
 
468 werner 224
    }
522 werner 225
    // total carbon in the snag-container on the RU *after* processing is the content of the
475 werner 226
    // standing woody debris pools + the branches
227
    mTotalSnagCarbon = mSWD[0].C + mSWD[1].C + mSWD[2].C +
228
                       mBranches[0].C + mBranches[1].C + mBranches[2].C + mBranches[3].C + mBranches[4].C;
468 werner 229
}
230
 
231
/// foliage and fineroot litter is transferred during tree growth.
232
void Snag::addTurnoverLitter(const Tree *tree, const double litter_foliage, const double litter_fineroot)
233
{
234
    const SoilParameters &soil_params = soilparams; // to be updated
235
    mLabileFlux.addBiomass(litter_foliage, soil_params.cnFoliage);
236
    mLabileFlux.addBiomass(litter_fineroot, soil_params.cnFineroot);
237
}
238
 
239
/// after the death of the tree the five biomass compartments are processed.
240
void Snag::addMortality(const Tree *tree)
241
{
242
    const SoilParameters &soil_params = soilparams; // to be updated
243
 
244
    // immediate flows: 100% of foliage, 100% of fine roots: they go to the labile pool
245
    // 100% of coarse root biomass: that goes to the refractory pool
246
    mLabileFlux.addBiomass(tree->biomassFoliage(), soil_params.cnFoliage);
247
    mLabileFlux.addBiomass(tree->biomassFineRoot(), soil_params.cnFineroot);
248
    mRefractoryFlux.addBiomass(tree->biomassCoarseRoot(), soil_params.cnWood);
249
 
250
    // branches are equally distributed over five years:
477 werner 251
    double biomass_branch = tree->biomassBranch() * 0.2;
468 werner 252
    for (int i=0;i<5; i++)
477 werner 253
        mBranches[i].addBiomass(biomass_branch, soil_params.cnWood);
468 werner 254
 
523 werner 255
    // just for book-keeping: keep track of all inputs into branches / swd
256
    mTotalIn.addBiomass(tree->biomassBranch() + tree->biomassStem(), soil_params.cnWood);
468 werner 257
    // stem biomass is transferred to the standing woody debris pool (SWD), increase stem number of pool
522 werner 258
    int pi = poolIndex(tree->dbh()); // get right transfer pool
259
 
260
    // update statistics - stemnumber-weighted averages
261
    // note: here the calculations are repeated for every died trees (i.e. consecutive weighting ... but delivers the same results)
262
    double p_old = mNumberOfSnags[pi] / (mNumberOfSnags[pi] + 1); // weighting factor for state vars (based on stem numbers)
263
    double p_new = 1. / (mNumberOfSnags[pi] + 1); // weighting factor for added tree (p_old + p_new = 1).
264
    mAvgDbh[pi] = mAvgDbh[pi]*p_old + tree->dbh()*p_new;
265
    mAvgHeight[pi] = mAvgHeight[pi]*p_old + tree->height()*p_new;
266
    mAvgVolume[pi] = mAvgVolume[pi]*p_old + tree->volume()*p_new;
267
    mTimeSinceDeath[pi] = mTimeSinceDeath[pi]*p_old + 1.*p_new;
268
    mHalfLife[pi] = mHalfLife[pi]*p_old + soil_params.halfLife * p_new;
269
 
270
    // average the decay rate (ksw); this is done based on the carbon content
271
    // aggregate all trees that die in the current year (and save weighted decay rates to CurrentKSW)
272
    if (tree->biomassStem()==0)
273
        throw IException("Snag::addMortality: tree without stem biomass!!");
274
    p_old = mToSWD[pi].C / (mToSWD[pi].C + tree->biomassStem()* biomassCFraction);
275
    p_new =tree->biomassStem()* biomassCFraction / (mToSWD[pi].C + tree->biomassStem()* biomassCFraction);
276
    mCurrentKSW[pi] = mCurrentKSW[pi]*p_old + soil_params.ksw * p_new;
277
    mNumberOfSnags[pi]++;
523 werner 278
 
279
    // finally add the biomass
280
    CNPool &swd = mToSWD[pi];
281
    swd.addBiomass(tree->biomassStem(), soil_params.cnWood);
468 werner 282
}
283
 
284
/// add residual biomass of 'tree' after harvesting.
522 werner 285
/// remove_{stem, branch, foliage}_fraction: percentage of biomass compartment that is *removed* by the harvest operation (i.e.: not to stay in the system)
468 werner 286
/// the harvested biomass is collected.
287
void Snag::addHarvest(const Tree* tree, const double remove_stem_fraction, const double remove_branch_fraction, const double remove_foliage_fraction )
288
{
289
    const SoilParameters &soil_params = soilparams; // to be updated
290
 
291
    // immediate flows: 100% of residual foliage, 100% of fine roots: they go to the labile pool
292
    // 100% of coarse root biomass: that goes to the refractory pool
293
    mLabileFlux.addBiomass(tree->biomassFoliage() * (1. - remove_foliage_fraction), soil_params.cnFoliage);
294
    mLabileFlux.addBiomass(tree->biomassFineRoot(), soil_params.cnFineroot);
295
    mRefractoryFlux.addBiomass(tree->biomassCoarseRoot(), soil_params.cnWood);
296
 
297
    // residual branches are equally distributed over five years:
298
    for (int i=0;i<5; i++)
523 werner 299
        mBranches[i].addBiomass(tree->biomassBranch() * (1. - remove_branch_fraction) * 0.2, soil_params.cnWood);
468 werner 300
 
523 werner 301
    mTotalToExtern.addBiomass(tree->biomassBranch()*remove_branch_fraction + tree->biomassStem()*remove_stem_fraction, soil_params.cnWood);
468 werner 302
    // stem biomass is transferred to the standing woody debris pool (SWD), increase stem number of pool
522 werner 303
    // TODO: what to do with harvest and stems??? I think harvested stems (that are not removed) should
304
    // go directly to the DWD??
468 werner 305
    CNPool &swd = mToSWD[poolIndex(tree->dbh())]; // get right transfer pool
306
    swd.addBiomass(tree->biomassStem() * remove_stem_fraction, soil_params.cnWood);
307
    if (remove_stem_fraction < 1.)
308
        mNumberOfSnags[poolIndex(tree->dbh())]++;
309
}
310