Subversion Repositories public iLand

Rev

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