Subversion Repositories public iLand

Rev

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