Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
671 | werner | 1 | /******************************************************************************************** |
2 | ** iLand - an individual based forest landscape and disturbance model |
||
3 | ** http://iland.boku.ac.at |
||
4 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl |
||
5 | ** |
||
6 | ** This program is free software: you can redistribute it and/or modify |
||
7 | ** it under the terms of the GNU General Public License as published by |
||
8 | ** the Free Software Foundation, either version 3 of the License, or |
||
9 | ** (at your option) any later version. |
||
10 | ** |
||
11 | ** This program is distributed in the hope that it will be useful, |
||
12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | ** GNU General Public License for more details. |
||
15 | ** |
||
16 | ** You should have received a copy of the GNU General Public License |
||
17 | ** along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
18 | ********************************************************************************************/ |
||
19 | |||
468 | werner | 20 | #include "snag.h" |
21 | #include "tree.h" |
||
22 | #include "species.h" |
||
23 | #include "globalsettings.h" |
||
24 | #include "expression.h" |
||
490 | werner | 25 | // for calculation of climate decomposition |
26 | #include "resourceunit.h" |
||
27 | #include "watercycle.h" |
||
28 | #include "climate.h" |
||
541 | werner | 29 | #include "model.h" |
468 | werner | 30 | |
31 | /** @class Snag |
||
697 | werner | 32 | @ingroup core |
468 | werner | 33 | Snag deals with carbon / nitrogen fluxes from the forest until the reach soil pools. |
490 | werner | 34 | Snag lives on the level of the ResourceUnit; carbon fluxes from trees enter Snag, and parts of the biomass of snags |
468 | werner | 35 | is subsequently forwarded to the soil sub model. |
522 | werner | 36 | Carbon is stored in three classes (depending on the size) |
528 | werner | 37 | The Snag dynamics class uses the following species parameter: |
38 | cnFoliage, cnFineroot, cnWood, snagHalflife, snagKSW |
||
468 | werner | 39 | |
40 | */ |
||
41 | // static variables |
||
528 | werner | 42 | double Snag::mDBHLower = -1.; |
522 | werner | 43 | double Snag::mDBHHigher = 0.; |
44 | double Snag::mCarbonThreshold[] = {0., 0., 0.}; |
||
45 | |||
534 | werner | 46 | double CNPair::biomassCFraction = biomassCFraction; // get global from globalsettings.h |
468 | werner | 47 | |
534 | werner | 48 | /// add biomass and weigh the parameter_value with the current C-content of the pool |
49 | void CNPool::addBiomass(const double biomass, const double CNratio, const double parameter_value) |
||
50 | { |
||
51 | if (biomass==0.) |
||
52 | return; |
||
53 | double new_c = biomass*biomassCFraction; |
||
54 | double p_old = C / (new_c + C); |
||
55 | mParameter = mParameter*p_old + parameter_value*(1.-p_old); |
||
56 | CNPair::addBiomass(biomass, CNratio); |
||
57 | } |
||
58 | |||
59 | // increase pool (and weigh the value) |
||
60 | void CNPool::operator+=(const CNPool &s) |
||
61 | { |
||
62 | if (s.C==0.) |
||
63 | return; |
||
64 | mParameter = parameter(s); // calculate weighted parameter |
||
65 | C+=s.C; |
||
66 | N+=s.N; |
||
67 | } |
||
68 | |||
69 | double CNPool::parameter(const CNPool &s) const |
||
70 | { |
||
71 | if (s.C==0.) |
||
72 | return parameter(); |
||
73 | double p_old = C / (s.C + C); |
||
74 | double result = mParameter*p_old + s.parameter()*(1.-p_old); |
||
75 | return result; |
||
76 | } |
||
77 | |||
78 | |||
522 | werner | 79 | void Snag::setupThresholds(const double lower, const double upper) |
80 | { |
||
81 | if (mDBHLower == lower) |
||
82 | return; |
||
83 | mDBHLower = lower; |
||
84 | mDBHHigher = upper; |
||
85 | mCarbonThreshold[0] = lower / 2.; |
||
86 | mCarbonThreshold[1] = lower + (upper - lower)/2.; |
||
87 | mCarbonThreshold[2] = upper + (upper - lower)/2.; |
||
88 | //# threshold levels for emptying out the dbh-snag-classes |
||
89 | //# derived from Psme woody allometry, converted to C, with a threshold level set to 10% |
||
90 | //# values in kg! |
||
91 | for (int i=0;i<3;i++) |
||
92 | mCarbonThreshold[i] = 0.10568*pow(mCarbonThreshold[i],2.4247)*0.5*0.1; |
||
93 | } |
||
94 | |||
95 | |||
468 | werner | 96 | Snag::Snag() |
97 | { |
||
490 | werner | 98 | mRU = 0; |
534 | werner | 99 | CNPair::setCFraction(biomassCFraction); |
468 | werner | 100 | } |
101 | |||
490 | werner | 102 | void Snag::setup( const ResourceUnit *ru) |
468 | werner | 103 | { |
490 | werner | 104 | mRU = ru; |
105 | mClimateFactor = 0.; |
||
468 | werner | 106 | // branches |
107 | mBranchCounter=0; |
||
108 | for (int i=0;i<3;i++) { |
||
109 | mTimeSinceDeath[i] = 0.; |
||
110 | mNumberOfSnags[i] = 0.; |
||
522 | werner | 111 | mAvgDbh[i] = 0.; |
112 | mAvgHeight[i] = 0.; |
||
113 | mAvgVolume[i] = 0.; |
||
114 | mKSW[i] = 0.; |
||
115 | mCurrentKSW[i] = 0.; |
||
557 | werner | 116 | mHalfLife[i] = 0.; |
468 | werner | 117 | } |
475 | werner | 118 | mTotalSnagCarbon = 0.; |
528 | werner | 119 | if (mDBHLower<=0) |
120 | throw IException("Snag::setupThresholds() not called or called with invalid parameters."); |
||
557 | werner | 121 | |
122 | // Inital values from XML file |
||
123 | XmlHelper xml=GlobalSettings::instance()->settings(); |
||
574 | werner | 124 | double kyr = xml.valueDouble("model.site.youngRefractoryDecompRate", -1); |
557 | werner | 125 | // put carbon of snags to the middle size class |
126 | xml.setCurrentNode("model.initialization.snags"); |
||
127 | mSWD[1].C = xml.valueDouble(".swdC"); |
||
128 | mSWD[1].N = mSWD[1].C / xml.valueDouble(".swdCN", 50.); |
||
129 | mSWD[1].setParameter(kyr); |
||
130 | mKSW[1] = xml.valueDouble(".swdDecompRate"); |
||
131 | mNumberOfSnags[1] = xml.valueDouble(".swdCount"); |
||
132 | mHalfLife[1] = xml.valueDouble(".swdHalfLife"); |
||
133 | // and for the Branch/coarse root pools: split the init value into five chunks |
||
134 | CNPool other(xml.valueDouble(".otherC"), xml.valueDouble(".otherC")/xml.valueDouble(".otherCN", 50.), kyr ); |
||
135 | |||
136 | mTotalSnagCarbon = other.C + mSWD[1].C; |
||
137 | |||
138 | other *= 0.2; |
||
139 | for (int i=0;i<5;i++) |
||
140 | mOtherWood[i] = other; |
||
468 | werner | 141 | } |
142 | |||
1157 | werner | 143 | void Snag::scaleInitialState() |
144 | { |
||
145 | double area_factor = mRU->stockableArea() / cRUArea; // fraction stockable area |
||
146 | // avoid huge snag pools on very small resource units (see also soil.cpp) |
||
147 | // area_factor = std::max(area_factor, 0.1); |
||
148 | mSWD[1] *= area_factor; |
||
149 | mNumberOfSnags[1] *= area_factor; |
||
150 | for (int i=0;i<5;i++) |
||
151 | mOtherWood[i]*= area_factor; |
||
152 | mTotalSnagCarbon *= area_factor; |
||
153 | |||
154 | } |
||
155 | |||
475 | werner | 156 | // debug outputs |
157 | QList<QVariant> Snag::debugList() |
||
158 | { |
||
159 | // list columns |
||
160 | // for three pools |
||
161 | QList<QVariant> list; |
||
162 | |||
523 | werner | 163 | // totals |
164 | list << mTotalSnagCarbon << mTotalIn.C << mTotalToAtm.C << mSWDtoSoil.C << mSWDtoSoil.N; |
||
477 | werner | 165 | // fluxes to labile soil pool and to refractory soil pool |
524 | werner | 166 | list << mLabileFlux.C << mLabileFlux.N << mRefractoryFlux.C << mRefractoryFlux.N; |
475 | werner | 167 | |
168 | for (int i=0;i<3;i++) { |
||
169 | // pools "swdx_c", "swdx_n", "swdx_count", "swdx_tsd", "toswdx_c", "toswdx_n" |
||
170 | list << mSWD[i].C << mSWD[i].N << mNumberOfSnags[i] << mTimeSinceDeath[i] << mToSWD[i].C << mToSWD[i].N; |
||
524 | werner | 171 | list << mAvgDbh[i] << mAvgHeight[i] << mAvgVolume[i]; |
475 | werner | 172 | } |
173 | |||
540 | werner | 174 | // branch/coarse wood pools (5 yrs) |
175 | for (int i=0;i<5;i++) { |
||
176 | list << mOtherWood[i].C << mOtherWood[i].N; |
||
177 | } |
||
178 | // list << mOtherWood[mBranchCounter].C << mOtherWood[mBranchCounter].N |
||
179 | // << mOtherWood[(mBranchCounter+1)%5].C << mOtherWood[(mBranchCounter+1)%5].N |
||
180 | // << mOtherWood[(mBranchCounter+2)%5].C << mOtherWood[(mBranchCounter+2)%5].N |
||
181 | // << mOtherWood[(mBranchCounter+3)%5].C << mOtherWood[(mBranchCounter+3)%5].N |
||
182 | // << mOtherWood[(mBranchCounter+4)%5].C << mOtherWood[(mBranchCounter+4)%5].N; |
||
475 | werner | 183 | return list; |
184 | } |
||
185 | |||
713 | werner | 186 | |
468 | werner | 187 | void Snag::newYear() |
188 | { |
||
189 | for (int i=0;i<3;i++) { |
||
190 | mToSWD[i].clear(); // clear transfer pools to standing-woody-debris |
||
522 | werner | 191 | mCurrentKSW[i] = 0.; |
468 | werner | 192 | } |
193 | mLabileFlux.clear(); |
||
194 | mRefractoryFlux.clear(); |
||
476 | werner | 195 | mTotalToAtm.clear(); |
196 | mTotalToExtern.clear(); |
||
609 | werner | 197 | mTotalToDisturbance.clear(); |
476 | werner | 198 | mTotalIn.clear(); |
477 | werner | 199 | mSWDtoSoil.clear(); |
468 | werner | 200 | } |
201 | |||
490 | werner | 202 | /// calculate the dynamic climate modifier for decomposition 're' |
522 | werner | 203 | /// calculation is done on the level of ResourceUnit because the water content per day is needed. |
490 | werner | 204 | double Snag::calculateClimateFactors() |
205 | { |
||
770 | werner | 206 | // the calculation of climate factors requires calculated evapotranspiration. In cases without vegetation (trees or saplings) |
207 | // we have to trigger the water cycle calculation for ourselves [ the waterCycle checks if it has already been run in a year and doesn't run twice in that case ] |
||
208 | const_cast<WaterCycle*>(mRU->waterCycle())->run(); |
||
490 | werner | 209 | double ft, fw; |
210 | double f_sum = 0.; |
||
552 | werner | 211 | int iday=0; |
553 | werner | 212 | // calculate the water-factor for each month (see Adair et al 2008) |
213 | double fw_month[12]; |
||
214 | double ratio; |
||
215 | for (int m=0;m<12;m++) { |
||
562 | werner | 216 | if (mRU->waterCycle()->referenceEvapotranspiration()[m]>0.) |
217 | ratio = mRU->climate()->precipitationMonth()[m] / mRU->waterCycle()->referenceEvapotranspiration()[m]; |
||
553 | werner | 218 | else |
219 | ratio = 0; |
||
220 | fw_month[m] = 1. / (1. + 30.*exp(-8.5*ratio)); |
||
564 | werner | 221 | if (logLevelDebug()) qDebug() <<"month"<< m << "PET" << mRU->waterCycle()->referenceEvapotranspiration()[m] << "prec" <<mRU->climate()->precipitationMonth()[m]; |
553 | werner | 222 | } |
223 | |||
552 | werner | 224 | for (const ClimateDay *day=mRU->climate()->begin(); day!=mRU->climate()->end(); ++day, ++iday) |
490 | werner | 225 | { |
226 | 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 | 227 | fw = fw_month[day->month-1]; |
540 | werner | 228 | |
490 | werner | 229 | f_sum += ft*fw; |
230 | } |
||
231 | // the climate factor is defined as the arithmentic annual mean value |
||
232 | mClimateFactor = f_sum / double(mRU->climate()->daysOfYear()); |
||
233 | return mClimateFactor; |
||
234 | } |
||
235 | |||
522 | werner | 236 | /// do the yearly calculation |
237 | /// see http://iland.boku.ac.at/snag+dynamics |
||
526 | werner | 238 | void Snag::calculateYear() |
468 | werner | 239 | { |
522 | werner | 240 | mSWDtoSoil.clear(); |
925 | werner | 241 | |
242 | // calculate anyway, because also the soil module needs it (and currently one can have Snag and Soil only as a couple) |
||
243 | calculateClimateFactors(); |
||
244 | const double climate_factor_re = mClimateFactor; |
||
245 | |||
477 | werner | 246 | if (isEmpty()) // nothing to do |
475 | werner | 247 | return; |
248 | |||
468 | werner | 249 | // process branches: every year one of the five baskets is emptied and transfered to the refractory soil pool |
540 | werner | 250 | mRefractoryFlux+=mOtherWood[mBranchCounter]; |
251 | mOtherWood[mBranchCounter].clear(); |
||
468 | werner | 252 | mBranchCounter= (mBranchCounter+1) % 5; // increase index, roll over to 0. |
1202 | werner | 253 | |
540 | werner | 254 | // decay of branches/coarse roots |
255 | for (int i=0;i<5;i++) { |
||
256 | if (mOtherWood[i].C>0.) { |
||
257 | double survive_rate = exp(- climate_factor_re * mOtherWood[i].parameter() ); // parameter: the "kyr" value... |
||
1202 | werner | 258 | mTotalToAtm.C += mOtherWood[i].C * (1. - survive_rate); // flux to atmosphere (decayed carbon) |
540 | werner | 259 | mOtherWood[i].C *= survive_rate; |
260 | } |
||
261 | } |
||
468 | werner | 262 | |
263 | // process standing snags. |
||
264 | // the input of the current year is in the mToSWD-Pools |
||
265 | for (int i=0;i<3;i++) { |
||
266 | |||
522 | werner | 267 | // update the swd-pool with this years' input |
268 | if (!mToSWD[i].isEmpty()) { |
||
269 | // update decay rate (apply average yearly input to the state parameters) |
||
270 | mKSW[i] = mKSW[i]*(mSWD[i].C/(mSWD[i].C+mToSWD[i].C)) + mCurrentKSW[i]*(mToSWD[i].C/(mSWD[i].C+mToSWD[i].C)); |
||
271 | //move content to the SWD pool |
||
272 | mSWD[i] += mToSWD[i]; |
||
273 | } |
||
475 | werner | 274 | |
522 | werner | 275 | if (mSWD[i].C > 0) { |
276 | // reduce the Carbon (note: the N stays, thus the CN ratio changes) |
||
277 | // use the decay rate that is derived as a weighted average of all standing woody debris |
||
523 | werner | 278 | double survive_rate = exp(-mKSW[i] *climate_factor_re * 1. ); // 1: timestep |
279 | mTotalToAtm.C += mSWD[i].C * (1. - survive_rate); |
||
280 | mSWD[i].C *= survive_rate; |
||
468 | werner | 281 | |
522 | werner | 282 | // transition to downed woody debris |
283 | // update: use negative exponential decay, species parameter: half-life |
||
284 | // modified for the climatic effect on decomposition, i.e. if decomp is slower, snags stand longer and vice versa |
||
285 | // this is loosely oriented on Standcarb2 (http://andrewsforest.oregonstate.edu/pubs/webdocs/models/standcarb2.htm), |
||
286 | // where lag times for cohort transitions are linearly modified with re although here individual good or bad years have |
||
287 | // an immediate effect, the average climatic influence should come through (and it is inherently transient) |
||
288 | // note that swd.hl is species-specific, and thus a weighted average over the species in the input (=mortality) |
||
289 | // needs to be calculated, followed by a weighted update of the previous swd.hl. |
||
290 | // As weights here we use stem number, as the processes here pertain individual snags |
||
291 | // calculate the transition probability of SWD to downed dead wood |
||
468 | werner | 292 | |
522 | werner | 293 | double half_life = mHalfLife[i] / climate_factor_re; |
294 | double rate = -M_LN2 / half_life; // M_LN2: math. constant |
||
295 | |||
296 | // higher decay rate for the class with smallest diameters |
||
297 | if (i==0) |
||
298 | rate*=2.; |
||
299 | |||
523 | werner | 300 | double transfer = 1. - exp(rate); |
522 | werner | 301 | |
468 | werner | 302 | // calculate flow to soil pool... |
522 | werner | 303 | mSWDtoSoil += mSWD[i] * transfer; |
304 | mRefractoryFlux += mSWD[i] * transfer; |
||
305 | mSWD[i] *= (1.-transfer); // reduce pool |
||
468 | werner | 306 | // calculate the stem number of remaining snags |
522 | werner | 307 | mNumberOfSnags[i] = mNumberOfSnags[i] * (1. - transfer); |
523 | werner | 308 | |
309 | mTimeSinceDeath[i] += 1.; |
||
522 | werner | 310 | // if stems<0.5, empty the whole cohort into DWD, i.e. release the last bit of C and N and clear the stats |
311 | // also, if the Carbon of an average snag is less than 10% of the original average tree |
||
312 | // (derived from allometries for the three diameter classes), the whole cohort is emptied out to DWD |
||
313 | if (mNumberOfSnags[i] < 0.5 || mSWD[i].C / mNumberOfSnags[i] < mCarbonThreshold[i]) { |
||
314 | // clear the pool: add the rest to the soil, clear statistics of the pool |
||
468 | werner | 315 | mRefractoryFlux += mSWD[i]; |
522 | werner | 316 | mSWDtoSoil += mSWD[i]; |
468 | werner | 317 | mSWD[i].clear(); |
522 | werner | 318 | mAvgDbh[i] = 0.; |
319 | mAvgHeight[i] = 0.; |
||
320 | mAvgVolume[i] = 0.; |
||
321 | mKSW[i] = 0.; |
||
322 | mCurrentKSW[i] = 0.; |
||
323 | mHalfLife[i] = 0.; |
||
324 | mTimeSinceDeath[i] = 0.; |
||
468 | werner | 325 | } |
522 | werner | 326 | |
468 | werner | 327 | } |
522 | werner | 328 | |
468 | werner | 329 | } |
522 | werner | 330 | // total carbon in the snag-container on the RU *after* processing is the content of the |
475 | werner | 331 | // standing woody debris pools + the branches |
332 | mTotalSnagCarbon = mSWD[0].C + mSWD[1].C + mSWD[2].C + |
||
540 | werner | 333 | mOtherWood[0].C + mOtherWood[1].C + mOtherWood[2].C + mOtherWood[3].C + mOtherWood[4].C; |
587 | werner | 334 | mTotalSWD = mSWD[0] + mSWD[1] + mSWD[2]; |
335 | mTotalOther = mOtherWood[0] + mOtherWood[1] + mOtherWood[2] + mOtherWood[3] + mOtherWood[4]; |
||
468 | werner | 336 | } |
337 | |||
338 | /// foliage and fineroot litter is transferred during tree growth. |
||
588 | werner | 339 | void Snag::addTurnoverLitter(const Species *species, const double litter_foliage, const double litter_fineroot) |
468 | werner | 340 | { |
588 | werner | 341 | mLabileFlux.addBiomass(litter_foliage, species->cnFoliage(), species->snagKyl()); |
342 | mLabileFlux.addBiomass(litter_fineroot, species->cnFineroot(), species->snagKyl()); |
||
1160 | werner | 343 | DBGMODE( |
344 | if (isnan(mLabileFlux.C)) |
||
345 | qDebug("Snag::addTurnoverLitter: NaN"); |
||
346 | ); |
||
468 | werner | 347 | } |
348 | |||
595 | werner | 349 | void Snag::addTurnoverWood(const Species *species, const double woody_biomass) |
350 | { |
||
351 | mRefractoryFlux.addBiomass(woody_biomass, species->cnWood(), species->snagKyr()); |
||
1160 | werner | 352 | DBGMODE( |
353 | if (isnan(mRefractoryFlux.C)) |
||
354 | qDebug("Snag::addTurnoverWood: NaN"); |
||
355 | ); |
||
356 | |||
595 | werner | 357 | } |
358 | |||
713 | werner | 359 | |
360 | /** process the remnants of a single tree. |
||
361 | The part of the stem / branch not covered by snag/soil fraction is removed from the system (e.g. harvest, fire) |
||
362 | @param tree the tree to process |
||
363 | @param stem_to_snag fraction (0..1) of the stem biomass that should be moved to a standing snag |
||
364 | @param stem_to_soil fraction (0..1) of the stem biomass that should go directly to the soil |
||
365 | @param branch_to_snag fraction (0..1) of the branch biomass that should be moved to a standing snag |
||
366 | @param branch_to_soil fraction (0..1) of the branch biomass that should go directly to the soil |
||
367 | @param foliage_to_soil fraction (0..1) of the foliage biomass that should go directly to the soil |
||
368 | |||
369 | */ |
||
370 | void Snag::addBiomassPools(const Tree *tree, |
||
371 | const double stem_to_snag, const double stem_to_soil, |
||
372 | const double branch_to_snag, const double branch_to_soil, |
||
373 | const double foliage_to_soil) |
||
468 | werner | 374 | { |
528 | werner | 375 | const Species *species = tree->species(); |
468 | werner | 376 | |
713 | werner | 377 | double branch_biomass = tree->biomassBranch(); |
378 | // fine roots go to the labile pool |
||
379 | mLabileFlux.addBiomass(tree->biomassFineRoot(), species->cnFineroot(), species->snagKyl()); |
||
468 | werner | 380 | |
713 | werner | 381 | // a part of the foliage goes to the soil |
382 | mLabileFlux.addBiomass(tree->biomassFoliage() * foliage_to_soil, species->cnFoliage(), species->snagKyl()); |
||
383 | |||
384 | //coarse roots and a part of branches are equally distributed over five years: |
||
385 | double biomass_rest = (tree->biomassCoarseRoot() + branch_to_snag*branch_biomass) * 0.2; |
||
468 | werner | 386 | for (int i=0;i<5; i++) |
713 | werner | 387 | mOtherWood[i].addBiomass(biomass_rest, species->cnWood(), species->snagKyr()); |
468 | werner | 388 | |
713 | werner | 389 | // the other part of the branches goes directly to the soil |
390 | mRefractoryFlux.addBiomass(branch_biomass*branch_to_soil, species->cnWood(), species->snagKyr() ); |
||
391 | // a part of the stem wood goes directly to the soil |
||
392 | mRefractoryFlux.addBiomass(tree->biomassStem()*stem_to_soil, species->cnWood(), species->snagKyr() ); |
||
393 | |||
394 | // just for book-keeping: keep track of all inputs of branches / roots / swd into the "snag" pools |
||
395 | mTotalIn.addBiomass(tree->biomassBranch()*branch_to_snag + tree->biomassCoarseRoot() + tree->biomassStem()*stem_to_snag, species->cnWood()); |
||
468 | werner | 396 | // stem biomass is transferred to the standing woody debris pool (SWD), increase stem number of pool |
522 | werner | 397 | int pi = poolIndex(tree->dbh()); // get right transfer pool |
398 | |||
713 | werner | 399 | if (stem_to_snag>0.) { |
400 | // update statistics - stemnumber-weighted averages |
||
401 | // note: here the calculations are repeated for every died trees (i.e. consecutive weighting ... but delivers the same results) |
||
402 | double p_old = mNumberOfSnags[pi] / (mNumberOfSnags[pi] + 1); // weighting factor for state vars (based on stem numbers) |
||
403 | double p_new = 1. / (mNumberOfSnags[pi] + 1); // weighting factor for added tree (p_old + p_new = 1). |
||
404 | mAvgDbh[pi] = mAvgDbh[pi]*p_old + tree->dbh()*p_new; |
||
405 | mAvgHeight[pi] = mAvgHeight[pi]*p_old + tree->height()*p_new; |
||
406 | mAvgVolume[pi] = mAvgVolume[pi]*p_old + tree->volume()*p_new; |
||
407 | mTimeSinceDeath[pi] = mTimeSinceDeath[pi]*p_old + 1.*p_new; |
||
408 | mHalfLife[pi] = mHalfLife[pi]*p_old + species->snagHalflife()* p_new; |
||
522 | werner | 409 | |
713 | werner | 410 | // average the decay rate (ksw); this is done based on the carbon content |
411 | // aggregate all trees that die in the current year (and save weighted decay rates to CurrentKSW) |
||
412 | p_old = mToSWD[pi].C / (mToSWD[pi].C + tree->biomassStem()* biomassCFraction); |
||
413 | p_new =tree->biomassStem()* biomassCFraction / (mToSWD[pi].C + tree->biomassStem()* biomassCFraction); |
||
414 | mCurrentKSW[pi] = mCurrentKSW[pi]*p_old + species->snagKsw() * p_new; |
||
415 | mNumberOfSnags[pi]++; |
||
416 | } |
||
523 | werner | 417 | |
713 | werner | 418 | // finally add the biomass of the stem to the standing snag pool |
534 | werner | 419 | CNPool &to_swd = mToSWD[pi]; |
713 | werner | 420 | to_swd.addBiomass(tree->biomassStem()*stem_to_snag, species->cnWood(), species->snagKyr()); |
421 | |||
422 | // the biomass that is not routed to snags or directly to the soil |
||
423 | // is removed from the system (to atmosphere or harvested) |
||
424 | mTotalToExtern.addBiomass(tree->biomassFoliage()* (1. - foliage_to_soil) + |
||
425 | branch_biomass*(1. - branch_to_snag - branch_to_soil) + |
||
426 | tree->biomassStem()*(1. - stem_to_snag - stem_to_soil), species->cnWood()); |
||
427 | |||
468 | werner | 428 | } |
429 | |||
713 | werner | 430 | |
431 | /// after the death of the tree the five biomass compartments are processed. |
||
432 | void Snag::addMortality(const Tree *tree) |
||
433 | { |
||
434 | addBiomassPools(tree, 1., 0., // all stem biomass goes to snag |
||
435 | 1., 0., // all branch biomass to snag |
||
436 | 1.); // all foliage to soil |
||
437 | |||
438 | // const Species *species = tree->species(); |
||
439 | |||
440 | // // immediate flows: 100% of foliage, 100% of fine roots: they go to the labile pool |
||
441 | // mLabileFlux.addBiomass(tree->biomassFoliage(), species->cnFoliage(), tree->species()->snagKyl()); |
||
442 | // mLabileFlux.addBiomass(tree->biomassFineRoot(), species->cnFineroot(), tree->species()->snagKyl()); |
||
443 | |||
444 | // // branches and coarse roots are equally distributed over five years: |
||
445 | // double biomass_rest = (tree->biomassBranch()+tree->biomassCoarseRoot()) * 0.2; |
||
446 | // for (int i=0;i<5; i++) |
||
447 | // mOtherWood[i].addBiomass(biomass_rest, species->cnWood(), tree->species()->snagKyr()); |
||
448 | |||
449 | // // just for book-keeping: keep track of all inputs into branches / roots / swd |
||
450 | // mTotalIn.addBiomass(tree->biomassBranch() + tree->biomassCoarseRoot() + tree->biomassStem(), species->cnWood()); |
||
451 | // // stem biomass is transferred to the standing woody debris pool (SWD), increase stem number of pool |
||
452 | // int pi = poolIndex(tree->dbh()); // get right transfer pool |
||
453 | |||
454 | // // update statistics - stemnumber-weighted averages |
||
455 | // // note: here the calculations are repeated for every died trees (i.e. consecutive weighting ... but delivers the same results) |
||
456 | // double p_old = mNumberOfSnags[pi] / (mNumberOfSnags[pi] + 1); // weighting factor for state vars (based on stem numbers) |
||
457 | // double p_new = 1. / (mNumberOfSnags[pi] + 1); // weighting factor for added tree (p_old + p_new = 1). |
||
458 | // mAvgDbh[pi] = mAvgDbh[pi]*p_old + tree->dbh()*p_new; |
||
459 | // mAvgHeight[pi] = mAvgHeight[pi]*p_old + tree->height()*p_new; |
||
460 | // mAvgVolume[pi] = mAvgVolume[pi]*p_old + tree->volume()*p_new; |
||
461 | // mTimeSinceDeath[pi] = mTimeSinceDeath[pi]*p_old + 1.*p_new; |
||
462 | // mHalfLife[pi] = mHalfLife[pi]*p_old + species->snagHalflife()* p_new; |
||
463 | |||
464 | // // average the decay rate (ksw); this is done based on the carbon content |
||
465 | // // aggregate all trees that die in the current year (and save weighted decay rates to CurrentKSW) |
||
466 | // if (tree->biomassStem()==0) |
||
467 | // throw IException("Snag::addMortality: tree without stem biomass!!"); |
||
468 | // p_old = mToSWD[pi].C / (mToSWD[pi].C + tree->biomassStem()* biomassCFraction); |
||
469 | // p_new =tree->biomassStem()* biomassCFraction / (mToSWD[pi].C + tree->biomassStem()* biomassCFraction); |
||
470 | // mCurrentKSW[pi] = mCurrentKSW[pi]*p_old + species->snagKsw() * p_new; |
||
471 | // mNumberOfSnags[pi]++; |
||
472 | |||
473 | // // finally add the biomass |
||
474 | // CNPool &to_swd = mToSWD[pi]; |
||
475 | // to_swd.addBiomass(tree->biomassStem(), species->cnWood(), tree->species()->snagKyr()); |
||
476 | } |
||
477 | |||
468 | werner | 478 | /// add residual biomass of 'tree' after harvesting. |
1088 | werner | 479 | /// remove_{stem, branch, foliage}_fraction: percentage of biomass compartment that is *removed* by the harvest operation [0..1] (i.e.: not to stay in the system) |
528 | werner | 480 | /// records on harvested biomass is collected (mTotalToExtern-pool). |
468 | werner | 481 | void Snag::addHarvest(const Tree* tree, const double remove_stem_fraction, const double remove_branch_fraction, const double remove_foliage_fraction ) |
482 | { |
||
713 | werner | 483 | addBiomassPools(tree, |
484 | 0., 1.-remove_stem_fraction, // "remove_stem_fraction" is removed -> the rest goes to soil |
||
485 | 0., 1.-remove_branch_fraction, // "remove_branch_fraction" is removed -> the rest goes directly to the soil |
||
486 | 1.-remove_foliage_fraction); // the rest of foliage is routed to the soil |
||
487 | // const Species *species = tree->species(); |
||
468 | werner | 488 | |
713 | werner | 489 | // // immediate flows: 100% of residual foliage, 100% of fine roots: they go to the labile pool |
490 | // mLabileFlux.addBiomass(tree->biomassFoliage() * (1. - remove_foliage_fraction), species->cnFoliage(), tree->species()->snagKyl()); |
||
491 | // mLabileFlux.addBiomass(tree->biomassFineRoot(), species->cnFineroot(), tree->species()->snagKyl()); |
||
540 | werner | 492 | |
713 | werner | 493 | // // for branches, add all biomass that remains in the forest to the soil |
494 | // mRefractoryFlux.addBiomass(tree->biomassBranch()*(1.-remove_branch_fraction), species->cnWood(), tree->species()->snagKyr()); |
||
495 | // // the same treatment for stem residuals |
||
496 | // mRefractoryFlux.addBiomass(tree->biomassStem() * (1. - remove_stem_fraction), species->cnWood(), tree->species()->snagKyr()); |
||
468 | werner | 497 | |
713 | werner | 498 | // // split the corase wood biomass into parts (slower decay) |
499 | // double biomass_rest = (tree->biomassCoarseRoot()) * 0.2; |
||
500 | // for (int i=0;i<5; i++) |
||
501 | // mOtherWood[i].addBiomass(biomass_rest, species->cnWood(), tree->species()->snagKyr()); |
||
540 | werner | 502 | |
503 | |||
713 | werner | 504 | // // for book-keeping... |
505 | // mTotalToExtern.addBiomass(tree->biomassFoliage()*remove_foliage_fraction + |
||
506 | // tree->biomassBranch()*remove_branch_fraction + |
||
507 | // tree->biomassStem()*remove_stem_fraction, species->cnWood()); |
||
468 | werner | 508 | } |
509 | |||
588 | werner | 510 | // add flow from regeneration layer (dead trees) to soil |
595 | werner | 511 | void Snag::addToSoil(const Species *species, const CNPair &woody_pool, const CNPair &litter_pool) |
588 | werner | 512 | { |
513 | mLabileFlux.add(litter_pool, species->snagKyl()); |
||
514 | mRefractoryFlux.add(woody_pool, species->snagKyr()); |
||
1160 | werner | 515 | DBGMODE( |
516 | if (isnan(mLabileFlux.C) || isnan(mRefractoryFlux.C)) |
||
517 | qDebug("Snag::addToSoil: NaN in C Pool"); |
||
518 | ); |
||
588 | werner | 519 | } |
534 | werner | 520 | |
607 | werner | 521 | /// disturbance function: remove the fraction of 'factor' of biomass from the SWD pools; 0: remove nothing, 1: remove all |
522 | /// biomass removed by this function goes to the atmosphere |
||
523 | void Snag::removeCarbon(const double factor) |
||
524 | { |
||
525 | // reduce pools of currently standing dead wood and also of pools that are added |
||
526 | // during (previous) management operations of the current year |
||
527 | for (int i=0;i<3;i++) { |
||
609 | werner | 528 | mTotalToDisturbance += (mSWD[i] + mToSWD[i]) * factor; |
607 | werner | 529 | mSWD[i] *= (1. - factor); |
530 | mToSWD[i] *= (1. - factor); |
||
531 | } |
||
534 | werner | 532 | |
607 | werner | 533 | for (int i=0;i<5;i++) { |
609 | werner | 534 | mTotalToDisturbance += mOtherWood[i]*factor; |
607 | werner | 535 | mOtherWood[i] *= (1. - factor); |
536 | } |
||
537 | } |
||
538 | |||
539 | |||
540 | /// cut down swd (and branches) and move to soil pools |
||
541 | /// @param factor 0: cut 0%, 1: cut and slash 100% of the wood |
||
542 | void Snag::management(const double factor) |
||
543 | { |
||
544 | if (factor<0. || factor>1.) |
||
545 | throw IException(QString("Invalid factor in Snag::management: '%1'").arg(factor)); |
||
546 | // swd pools |
||
547 | for (int i=0;i<3;i++) { |
||
548 | mSWDtoSoil += mSWD[i] * factor; |
||
1202 | werner | 549 | mRefractoryFlux += mSWD[i] * factor; |
607 | werner | 550 | mSWD[i] *= (1. - factor); |
1202 | werner | 551 | //mSWDtoSoil += mToSWD[i] * factor; |
552 | //mToSWD[i] *= (1. - factor); |
||
607 | werner | 553 | } |
554 | // what to do with the branches: now move also all wood to soil (note: this is note |
||
555 | // very good w.r.t the coarse roots... |
||
556 | for (int i=0;i<5;i++) { |
||
557 | mRefractoryFlux+=mOtherWood[i]*factor; |
||
558 | mOtherWood[i]*=(1. - factor); |
||
559 | } |
||
560 | |||
561 | } |
||
562 | |||
563 |