Subversion Repositories public iLand

Rev

Rev 534 | Rev 541 | 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
 
540 werner 120
    // branch/coarse wood pools (5 yrs)
121
    for (int i=0;i<5;i++) {
122
        list << mOtherWood[i].C << mOtherWood[i].N;
123
    }
124
//    list << mOtherWood[mBranchCounter].C << mOtherWood[mBranchCounter].N
125
//            << mOtherWood[(mBranchCounter+1)%5].C << mOtherWood[(mBranchCounter+1)%5].N
126
//            << mOtherWood[(mBranchCounter+2)%5].C << mOtherWood[(mBranchCounter+2)%5].N
127
//            << mOtherWood[(mBranchCounter+3)%5].C << mOtherWood[(mBranchCounter+3)%5].N
128
//            << mOtherWood[(mBranchCounter+4)%5].C << mOtherWood[(mBranchCounter+4)%5].N;
475 werner 129
    return list;
130
}
131
 
468 werner 132
void Snag::newYear()
133
{
134
    for (int i=0;i<3;i++) {
135
        mToSWD[i].clear(); // clear transfer pools to standing-woody-debris
522 werner 136
        mCurrentKSW[i] = 0.;
468 werner 137
    }
138
    mLabileFlux.clear();
139
    mRefractoryFlux.clear();
476 werner 140
    mTotalToAtm.clear();
141
    mTotalToExtern.clear();
142
    mTotalIn.clear();
477 werner 143
    mSWDtoSoil.clear();
468 werner 144
}
145
 
490 werner 146
/// calculate the dynamic climate modifier for decomposition 're'
522 werner 147
/// calculation is done on the level of ResourceUnit because the water content per day is needed.
490 werner 148
double Snag::calculateClimateFactors()
149
{
540 werner 150
    double psi_kpa;
490 werner 151
    double ft, fw;
540 werner 152
    const double min_kpa = -1500.;
490 werner 153
    double f_sum = 0.;
154
    for (const ClimateDay *day=mRU->climate()->begin(); day!=mRU->climate()->end(); ++day)
155
    {
540 werner 156
        psi_kpa = mRU->waterCycle()->psi_kPa(day->day);
157
 
490 werner 158
        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)
540 werner 159
        fw = 1. - limit(psi_kpa / min_kpa, 0., 1.);
160
 
490 werner 161
        f_sum += ft*fw;
162
    }
163
    // the climate factor is defined as the arithmentic annual mean value
164
    mClimateFactor = f_sum / double(mRU->climate()->daysOfYear());
165
    return mClimateFactor;
166
}
167
 
522 werner 168
/// do the yearly calculation
169
/// see http://iland.boku.ac.at/snag+dynamics
526 werner 170
void Snag::calculateYear()
468 werner 171
{
522 werner 172
    mSWDtoSoil.clear();
532 werner 173
    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 174
    if (isEmpty()) // nothing to do
475 werner 175
        return;
176
 
468 werner 177
    // process branches: every year one of the five baskets is emptied and transfered to the refractory soil pool
540 werner 178
    mRefractoryFlux+=mOtherWood[mBranchCounter];
179
 
180
    mOtherWood[mBranchCounter].clear();
468 werner 181
    mBranchCounter= (mBranchCounter+1) % 5; // increase index, roll over to 0.
540 werner 182
    // decay of branches/coarse roots
183
    for (int i=0;i<5;i++) {
184
        if (mOtherWood[i].C>0.) {
185
            double survive_rate = exp(- climate_factor_re * mOtherWood[i].parameter() ); // parameter: the "kyr" value...
186
            mOtherWood[i].C *= survive_rate;
187
        }
188
    }
468 werner 189
 
190
    // process standing snags.
191
    // the input of the current year is in the mToSWD-Pools
192
    for (int i=0;i<3;i++) {
193
 
522 werner 194
        // update the swd-pool with this years' input
195
        if (!mToSWD[i].isEmpty()) {
196
            // update decay rate (apply average yearly input to the state parameters)
197
            mKSW[i] = mKSW[i]*(mSWD[i].C/(mSWD[i].C+mToSWD[i].C)) + mCurrentKSW[i]*(mToSWD[i].C/(mSWD[i].C+mToSWD[i].C));
198
            //move content to the SWD pool
199
            mSWD[i] += mToSWD[i];
200
        }
475 werner 201
 
522 werner 202
        if (mSWD[i].C > 0) {
203
            // reduce the Carbon (note: the N stays, thus the CN ratio changes)
204
            // use the decay rate that is derived as a weighted average of all standing woody debris
523 werner 205
            double survive_rate = exp(-mKSW[i] *climate_factor_re * 1. ); // 1: timestep
206
            mTotalToAtm.C += mSWD[i].C * (1. - survive_rate);
207
            mSWD[i].C *= survive_rate;
468 werner 208
 
522 werner 209
            // transition to downed woody debris
210
            // update: use negative exponential decay, species parameter: half-life
211
            // modified for the climatic effect on decomposition, i.e. if decomp is slower, snags stand longer and vice versa
212
            // this is loosely oriented on Standcarb2 (http://andrewsforest.oregonstate.edu/pubs/webdocs/models/standcarb2.htm),
213
            // where lag times for cohort transitions are linearly modified with re although here individual good or bad years have
214
            // an immediate effect, the average climatic influence should come through (and it is inherently transient)
215
            // note that swd.hl is species-specific, and thus a weighted average over the species in the input (=mortality)
216
            // needs to be calculated, followed by a weighted update of the previous swd.hl.
217
            // As weights here we use stem number, as the processes here pertain individual snags
218
            // calculate the transition probability of SWD to downed dead wood
468 werner 219
 
522 werner 220
            double half_life = mHalfLife[i] / climate_factor_re;
221
            double rate = -M_LN2 / half_life; // M_LN2: math. constant
222
 
223
            // higher decay rate for the class with smallest diameters
224
            if (i==0)
225
                rate*=2.;
226
 
523 werner 227
            double transfer = 1. - exp(rate);
522 werner 228
 
468 werner 229
            // calculate flow to soil pool...
522 werner 230
            mSWDtoSoil += mSWD[i] * transfer;
231
            mRefractoryFlux += mSWD[i] * transfer;
232
            mSWD[i] *= (1.-transfer); // reduce pool
468 werner 233
            // calculate the stem number of remaining snags
522 werner 234
            mNumberOfSnags[i] = mNumberOfSnags[i] * (1. - transfer);
523 werner 235
 
236
            mTimeSinceDeath[i] += 1.;
522 werner 237
            // if stems<0.5, empty the whole cohort into DWD, i.e. release the last bit of C and N and clear the stats
238
            // also, if the Carbon of an average snag is less than 10% of the original average tree
239
            // (derived from allometries for the three diameter classes), the whole cohort is emptied out to DWD
240
            if (mNumberOfSnags[i] < 0.5 || mSWD[i].C / mNumberOfSnags[i] < mCarbonThreshold[i]) {
241
                // clear the pool: add the rest to the soil, clear statistics of the pool
468 werner 242
                mRefractoryFlux += mSWD[i];
522 werner 243
                mSWDtoSoil += mSWD[i];
468 werner 244
                mSWD[i].clear();
522 werner 245
                mAvgDbh[i] = 0.;
246
                mAvgHeight[i] = 0.;
247
                mAvgVolume[i] = 0.;
248
                mKSW[i] = 0.;
249
                mCurrentKSW[i] = 0.;
250
                mHalfLife[i] = 0.;
251
                mTimeSinceDeath[i] = 0.;
468 werner 252
            }
522 werner 253
 
468 werner 254
        }
522 werner 255
 
468 werner 256
    }
522 werner 257
    // total carbon in the snag-container on the RU *after* processing is the content of the
475 werner 258
    // standing woody debris pools + the branches
259
    mTotalSnagCarbon = mSWD[0].C + mSWD[1].C + mSWD[2].C +
540 werner 260
                       mOtherWood[0].C + mOtherWood[1].C + mOtherWood[2].C + mOtherWood[3].C + mOtherWood[4].C;
468 werner 261
}
262
 
263
/// foliage and fineroot litter is transferred during tree growth.
264
void Snag::addTurnoverLitter(const Tree *tree, const double litter_foliage, const double litter_fineroot)
265
{
534 werner 266
    mLabileFlux.addBiomass(litter_foliage, tree->species()->cnFoliage(), tree->species()->snagKyl());
267
    mLabileFlux.addBiomass(litter_fineroot, tree->species()->cnFineroot(), tree->species()->snagKyl());
468 werner 268
}
269
 
270
/// after the death of the tree the five biomass compartments are processed.
271
void Snag::addMortality(const Tree *tree)
272
{
528 werner 273
    const Species *species = tree->species();
468 werner 274
 
275
    // immediate flows: 100% of foliage, 100% of fine roots: they go to the labile pool
534 werner 276
    mLabileFlux.addBiomass(tree->biomassFoliage(), species->cnFoliage(), tree->species()->snagKyl());
277
    mLabileFlux.addBiomass(tree->biomassFineRoot(), species->cnFineroot(), tree->species()->snagKyl());
468 werner 278
 
540 werner 279
    // branches and coarse roots are equally distributed over five years:
280
    double biomass_rest = (tree->biomassBranch()+tree->biomassCoarseRoot()) * 0.2;
468 werner 281
    for (int i=0;i<5; i++)
540 werner 282
        mOtherWood[i].addBiomass(biomass_rest, species->cnWood(), tree->species()->snagKyr());
468 werner 283
 
540 werner 284
    // just for book-keeping: keep track of all inputs into branches / roots / swd
285
    mTotalIn.addBiomass(tree->biomassBranch() + tree->biomassCoarseRoot() + tree->biomassStem(), species->cnWood());
468 werner 286
    // stem biomass is transferred to the standing woody debris pool (SWD), increase stem number of pool
522 werner 287
    int pi = poolIndex(tree->dbh()); // get right transfer pool
288
 
289
    // update statistics - stemnumber-weighted averages
290
    // note: here the calculations are repeated for every died trees (i.e. consecutive weighting ... but delivers the same results)
291
    double p_old = mNumberOfSnags[pi] / (mNumberOfSnags[pi] + 1); // weighting factor for state vars (based on stem numbers)
292
    double p_new = 1. / (mNumberOfSnags[pi] + 1); // weighting factor for added tree (p_old + p_new = 1).
293
    mAvgDbh[pi] = mAvgDbh[pi]*p_old + tree->dbh()*p_new;
294
    mAvgHeight[pi] = mAvgHeight[pi]*p_old + tree->height()*p_new;
295
    mAvgVolume[pi] = mAvgVolume[pi]*p_old + tree->volume()*p_new;
296
    mTimeSinceDeath[pi] = mTimeSinceDeath[pi]*p_old + 1.*p_new;
528 werner 297
    mHalfLife[pi] = mHalfLife[pi]*p_old + species->snagHalflife()* p_new;
522 werner 298
 
299
    // average the decay rate (ksw); this is done based on the carbon content
300
    // aggregate all trees that die in the current year (and save weighted decay rates to CurrentKSW)
301
    if (tree->biomassStem()==0)
302
        throw IException("Snag::addMortality: tree without stem biomass!!");
303
    p_old = mToSWD[pi].C / (mToSWD[pi].C + tree->biomassStem()* biomassCFraction);
304
    p_new =tree->biomassStem()* biomassCFraction / (mToSWD[pi].C + tree->biomassStem()* biomassCFraction);
534 werner 305
    mCurrentKSW[pi] = mCurrentKSW[pi]*p_old + species->snagKsw() * p_new;
522 werner 306
    mNumberOfSnags[pi]++;
523 werner 307
 
308
    // finally add the biomass
534 werner 309
    CNPool &to_swd = mToSWD[pi];
310
    to_swd.addBiomass(tree->biomassStem(), species->cnWood(), tree->species()->snagKyr());
468 werner 311
}
312
 
313
/// add residual biomass of 'tree' after harvesting.
522 werner 314
/// 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 315
/// records on harvested biomass is collected (mTotalToExtern-pool).
468 werner 316
void Snag::addHarvest(const Tree* tree, const double remove_stem_fraction, const double remove_branch_fraction, const double remove_foliage_fraction )
317
{
528 werner 318
    const Species *species = tree->species();
468 werner 319
 
320
    // immediate flows: 100% of residual foliage, 100% of fine roots: they go to the labile pool
534 werner 321
    mLabileFlux.addBiomass(tree->biomassFoliage() * (1. - remove_foliage_fraction), species->cnFoliage(), tree->species()->snagKyl());
322
    mLabileFlux.addBiomass(tree->biomassFineRoot(), species->cnFineroot(), tree->species()->snagKyl());
540 werner 323
 
528 werner 324
    // for branches, add all biomass that remains in the forest to the soil
534 werner 325
    mRefractoryFlux.addBiomass(tree->biomassBranch()*(1.-remove_branch_fraction), species->cnWood(), tree->species()->snagKyr());
528 werner 326
    // the same treatment for stem residuals
534 werner 327
    mRefractoryFlux.addBiomass(tree->biomassStem() * remove_stem_fraction, species->cnWood(), tree->species()->snagKyr());
468 werner 328
 
540 werner 329
    // split the corase wood biomass into parts (slower decay)
330
    double biomass_rest = (tree->biomassCoarseRoot()) * 0.2;
331
    for (int i=0;i<5; i++)
332
        mOtherWood[i].addBiomass(biomass_rest, species->cnWood(), tree->species()->snagKyr());
333
 
334
 
528 werner 335
    // for book-keeping...
336
    mTotalToExtern.addBiomass(tree->biomassFoliage()*remove_foliage_fraction +
337
                              tree->biomassBranch()*remove_branch_fraction +
338
                              tree->biomassStem()*remove_stem_fraction, species->cnWood());
468 werner 339
}
340
 
534 werner 341
 
342
 
343