Subversion Repositories public iLand

Rev

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