Rev 720 | Rev 738 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 720 | Rev 734 | ||
---|---|---|---|
1 | Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/resourceunit.cpp': |
1 | Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/resourceunit.cpp': |
2 | /********************************************************************************************
|
2 | /********************************************************************************************
|
3 | ** iLand - an individual based forest landscape and disturbance model
|
3 | ** iLand - an individual based forest landscape and disturbance model
|
4 | ** http://iland.boku.ac.at
|
4 | ** http://iland.boku.ac.at
|
5 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl
|
5 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl
|
6 | **
|
6 | **
|
7 | ** This program is free software: you can redistribute it and/or modify
|
7 | ** This program is free software: you can redistribute it and/or modify
|
8 | ** it under the terms of the GNU General Public License as published by
|
8 | ** it under the terms of the GNU General Public License as published by
|
9 | ** the Free Software Foundation, either version 3 of the License, or
|
9 | ** the Free Software Foundation, either version 3 of the License, or
|
10 | ** (at your option) any later version.
|
10 | ** (at your option) any later version.
|
11 | **
|
11 | **
|
12 | ** This program is distributed in the hope that it will be useful,
|
12 | ** This program is distributed in the hope that it will be useful,
|
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 | ** GNU General Public License for more details.
|
15 | ** GNU General Public License for more details.
|
16 | **
|
16 | **
|
17 | ** You should have received a copy of the GNU General Public License
|
17 | ** You should have received a copy of the GNU General Public License
|
18 | ** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18 | ** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19 | ********************************************************************************************/
|
19 | ********************************************************************************************/
|
20 | 20 | ||
21 | /** @class ResourceUnit
|
21 | /** @class ResourceUnit
|
22 | ResourceUnit is the spatial unit that encapsulates a forest stand and links to several environmental components
|
22 | ResourceUnit is the spatial unit that encapsulates a forest stand and links to several environmental components
|
23 | (Climate, Soil, Water, ...).
|
23 | (Climate, Soil, Water, ...).
|
24 | @ingroup core
|
24 | @ingroup core
|
25 | A resource unit has a size of (currently) 100x100m. Many processes in iLand operate on the level of a ResourceUnit.
|
25 | A resource unit has a size of (currently) 100x100m. Many processes in iLand operate on the level of a ResourceUnit.
|
26 | Each resource unit has the same Climate and other properties (e.g. available nitrogen).
|
26 | Each resource unit has the same Climate and other properties (e.g. available nitrogen).
|
27 | Proceses on this level are, inter alia, NPP Production (see Production3PG), water calculations (WaterCycle), the modeling
|
27 | Proceses on this level are, inter alia, NPP Production (see Production3PG), water calculations (WaterCycle), the modeling
|
28 | of dead trees (Snag) and soil processes (Soil).
|
28 | of dead trees (Snag) and soil processes (Soil).
|
29 | 29 | ||
30 | */
|
30 | */
|
31 | #include <QtCore>
|
31 | #include <QtCore>
|
32 | #include "global.h"
|
32 | #include "global.h"
|
33 | 33 | ||
34 | #include "resourceunit.h"
|
34 | #include "resourceunit.h"
|
35 | #include "resourceunitspecies.h"
|
35 | #include "resourceunitspecies.h"
|
36 | #include "speciesset.h"
|
36 | #include "speciesset.h"
|
37 | #include "species.h"
|
37 | #include "species.h"
|
38 | #include "production3pg.h"
|
38 | #include "production3pg.h"
|
39 | #include "model.h"
|
39 | #include "model.h"
|
40 | #include "climate.h"
|
40 | #include "climate.h"
|
41 | #include "watercycle.h"
|
41 | #include "watercycle.h"
|
42 | #include "snag.h"
|
42 | #include "snag.h"
|
43 | #include "soil.h"
|
43 | #include "soil.h"
|
44 | #include "helper.h"
|
44 | #include "helper.h"
|
45 | 45 | ||
46 | ResourceUnit::~ResourceUnit() |
46 | ResourceUnit::~ResourceUnit() |
47 | {
|
47 | {
|
48 | if (mWater) |
48 | if (mWater) |
49 | delete mWater; |
49 | delete mWater; |
50 | mWater = 0; |
50 | mWater = 0; |
51 | if (mSnag) |
51 | if (mSnag) |
52 | delete mSnag; |
52 | delete mSnag; |
53 | if (mSoil) |
53 | if (mSoil) |
54 | delete mSoil; |
54 | delete mSoil; |
55 | 55 | ||
56 | mSnag = 0; |
56 | mSnag = 0; |
57 | mSoil = 0; |
57 | mSoil = 0; |
58 | }
|
58 | }
|
59 | 59 | ||
60 | ResourceUnit::ResourceUnit(const int index) |
60 | ResourceUnit::ResourceUnit(const int index) |
61 | {
|
61 | {
|
62 | qDeleteAll(mRUSpecies); |
62 | qDeleteAll(mRUSpecies); |
63 | mSpeciesSet = 0; |
63 | mSpeciesSet = 0; |
64 | mClimate = 0; |
64 | mClimate = 0; |
65 | mPixelCount=0; |
65 | mPixelCount=0; |
66 | mStockedArea = 0; |
66 | mStockedArea = 0; |
67 | mStockedPixelCount = 0; |
67 | mStockedPixelCount = 0; |
68 | mIndex = index; |
68 | mIndex = index; |
69 | mSaplingHeightMap = 0; |
69 | mSaplingHeightMap = 0; |
70 | mEffectiveArea_perWLA = 0.; |
70 | mEffectiveArea_perWLA = 0.; |
71 | mWater = new WaterCycle(); |
71 | mWater = new WaterCycle(); |
72 | mSnag = 0; |
72 | mSnag = 0; |
73 | mSoil = 0; |
73 | mSoil = 0; |
74 | mID = 0; |
74 | mID = 0; |
75 | }
|
75 | }
|
76 | 76 | ||
77 | void ResourceUnit::setup() |
77 | void ResourceUnit::setup() |
78 | {
|
78 | {
|
79 | mWater->setup(this); |
79 | mWater->setup(this); |
80 | 80 | ||
81 | if (mSnag) |
81 | if (mSnag) |
82 | delete mSnag; |
82 | delete mSnag; |
83 | mSnag=0; |
83 | mSnag=0; |
84 | if (mSoil) |
84 | if (mSoil) |
85 | delete mSoil; |
85 | delete mSoil; |
86 | mSoil=0; |
86 | mSoil=0; |
87 | if (Model::settings().carbonCycleEnabled) { |
87 | if (Model::settings().carbonCycleEnabled) { |
88 | mSoil = new Soil(this); |
88 | mSoil = new Soil(this); |
89 | mSnag = new Snag; |
89 | mSnag = new Snag; |
90 | mSnag->setup(this); |
90 | mSnag->setup(this); |
91 | const XmlHelper &xml=GlobalSettings::instance()->settings(); |
91 | const XmlHelper &xml=GlobalSettings::instance()->settings(); |
92 | 92 | ||
93 | // setup contents of the soil of the RU; use values for C and N (kg/ha)
|
93 | // setup contents of the soil of the RU; use values for C and N (kg/ha)
|
94 | mSoil->setInitialState(CNPool(xml.valueDouble("model.site.youngLabileC", -1), |
94 | mSoil->setInitialState(CNPool(xml.valueDouble("model.site.youngLabileC", -1), |
95 | xml.valueDouble("model.site.youngLabileN", -1), |
95 | xml.valueDouble("model.site.youngLabileN", -1), |
96 | xml.valueDouble("model.site.youngLabileDecompRate", -1)), |
96 | xml.valueDouble("model.site.youngLabileDecompRate", -1)), |
97 | CNPool(xml.valueDouble("model.site.youngRefractoryC", -1), |
97 | CNPool(xml.valueDouble("model.site.youngRefractoryC", -1), |
98 | xml.valueDouble("model.site.youngRefractoryN", -1), |
98 | xml.valueDouble("model.site.youngRefractoryN", -1), |
99 | xml.valueDouble("model.site.youngRefractoryDecompRate", -1)), |
99 | xml.valueDouble("model.site.youngRefractoryDecompRate", -1)), |
100 | CNPair(xml.valueDouble("model.site.somC", -1), xml.valueDouble("model.site.somN", -1))); |
100 | CNPair(xml.valueDouble("model.site.somC", -1), xml.valueDouble("model.site.somN", -1))); |
101 | }
|
101 | }
|
102 | 102 | ||
103 | // setup variables
|
103 | // setup variables
|
104 | mUnitVariables.nitrogenAvailable = GlobalSettings::instance()->settings().valueDouble("model.site.availableNitrogen", 40); |
104 | mUnitVariables.nitrogenAvailable = GlobalSettings::instance()->settings().valueDouble("model.site.availableNitrogen", 40); |
105 | 105 | ||
106 | // if dynamic coupling of soil nitrogen is enabled, the calculate a starting value for available n.
|
106 | // if dynamic coupling of soil nitrogen is enabled, the calculate a starting value for available n.
|
107 | if (mSoil && Model::settings().useDynamicAvailableNitrogen && Model::settings().carbonCycleEnabled) { |
107 | if (mSoil && Model::settings().useDynamicAvailableNitrogen && Model::settings().carbonCycleEnabled) { |
108 | mSoil->setClimateFactor(1.); |
108 | mSoil->setClimateFactor(1.); |
109 | mSoil->calculateYear(); |
109 | mSoil->calculateYear(); |
110 | mUnitVariables.nitrogenAvailable = mSoil->availableNitrogen(); |
110 | mUnitVariables.nitrogenAvailable = mSoil->availableNitrogen(); |
111 | }
|
111 | }
|
112 | mHasDeadTrees = false; |
112 | mHasDeadTrees = false; |
113 | mAverageAging = 0.; |
113 | mAverageAging = 0.; |
114 | 114 | ||
115 | }
|
115 | }
|
116 | void ResourceUnit::setBoundingBox(const QRectF &bb) |
116 | void ResourceUnit::setBoundingBox(const QRectF &bb) |
117 | {
|
117 | {
|
118 | mBoundingBox = bb; |
118 | mBoundingBox = bb; |
119 | mCornerCoord = GlobalSettings::instance()->model()->grid()->indexAt(bb.topLeft()); |
119 | mCornerCoord = GlobalSettings::instance()->model()->grid()->indexAt(bb.topLeft()); |
120 | }
|
120 | }
|
121 | 121 | ||
122 | /// set species and setup the species-per-RU-data
|
122 | /// set species and setup the species-per-RU-data
|
123 | void ResourceUnit::setSpeciesSet(SpeciesSet *set) |
123 | void ResourceUnit::setSpeciesSet(SpeciesSet *set) |
124 | {
|
124 | {
|
125 | mSpeciesSet = set; |
125 | mSpeciesSet = set; |
126 | qDeleteAll(mRUSpecies); |
126 | qDeleteAll(mRUSpecies); |
127 | 127 | ||
128 | //mRUSpecies.resize(set->count()); // ensure that the vector space is not relocated
|
128 | //mRUSpecies.resize(set->count()); // ensure that the vector space is not relocated
|
129 | for (int i=0;i<set->count();i++) { |
129 | for (int i=0;i<set->count();i++) { |
130 | Species *s = const_cast<Species*>(mSpeciesSet->species(i)); |
130 | Species *s = const_cast<Species*>(mSpeciesSet->species(i)); |
131 | if (!s) |
131 | if (!s) |
132 | throw IException("ResourceUnit::setSpeciesSet: invalid index!"); |
132 | throw IException("ResourceUnit::setSpeciesSet: invalid index!"); |
133 | 133 | ||
134 | ResourceUnitSpecies *rus = new ResourceUnitSpecies(); |
134 | ResourceUnitSpecies *rus = new ResourceUnitSpecies(); |
135 | mRUSpecies.push_back(rus); |
135 | mRUSpecies.push_back(rus); |
136 | rus->setup(s, this); |
136 | rus->setup(s, this); |
137 | /* be careful: setup() is called with a pointer somewhere to the content of the mRUSpecies container.
|
137 | /* be careful: setup() is called with a pointer somewhere to the content of the mRUSpecies container.
|
138 | If the container memory is relocated (QVector), the pointer gets invalid!!!
|
138 | If the container memory is relocated (QVector), the pointer gets invalid!!!
|
139 | Therefore, a resize() is called before the loop (no resize()-operations during the loop)! */
|
139 | Therefore, a resize() is called before the loop (no resize()-operations during the loop)! */
|
140 | //mRUSpecies[i].setup(s,this); // setup this element
|
140 | //mRUSpecies[i].setup(s,this); // setup this element
|
141 | 141 | ||
142 | }
|
142 | }
|
143 | }
|
143 | }
|
144 | 144 | ||
145 | ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species) |
145 | ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species) |
146 | {
|
146 | {
|
147 | return *mRUSpecies[species->index()]; |
147 | return *mRUSpecies[species->index()]; |
148 | }
|
148 | }
|
149 | 149 | ||
150 | Tree &ResourceUnit::newTree() |
150 | Tree &ResourceUnit::newTree() |
151 | {
|
151 | {
|
152 | // start simple: just append to the vector...
|
152 | // start simple: just append to the vector...
|
153 | if (mTrees.isEmpty()) |
153 | if (mTrees.isEmpty()) |
154 | mTrees.reserve(100); // reserve a junk of memory for trees |
154 | mTrees.reserve(100); // reserve a junk of memory for trees |
155 | 155 | ||
156 | mTrees.append(Tree()); |
156 | mTrees.append(Tree()); |
157 | return mTrees.back(); |
157 | return mTrees.back(); |
158 | }
|
158 | }
|
159 | int ResourceUnit::newTreeIndex() |
159 | int ResourceUnit::newTreeIndex() |
160 | {
|
160 | {
|
161 | // start simple: just append to the vector...
|
- | |
162 | mTrees.append(Tree()); |
- | |
163 | return mTrees.count()-1; |
- | |
- | 161 | newTree(); |
|
- | 162 | return mTrees.count()-1; // return index of the last tree |
|
164 | }
|
163 | }
|
165 | 164 | ||
166 | /// remove dead trees from tree list
|
165 | /// remove dead trees from tree list
|
167 | /// reduce size of vector if lots of space is free
|
166 | /// reduce size of vector if lots of space is free
|
168 | /// tests showed that this way of cleanup is very fast,
|
167 | /// tests showed that this way of cleanup is very fast,
|
169 | /// because no memory allocations are performed (simple memmove())
|
168 | /// because no memory allocations are performed (simple memmove())
|
170 | /// when trees are moved.
|
169 | /// when trees are moved.
|
171 | void ResourceUnit::cleanTreeList() |
170 | void ResourceUnit::cleanTreeList() |
172 | {
|
171 | {
|
173 | if (!mHasDeadTrees) |
172 | if (!mHasDeadTrees) |
174 | return; |
173 | return; |
175 | 174 | ||
176 | QVector<Tree>::iterator last=mTrees.end()-1; |
175 | QVector<Tree>::iterator last=mTrees.end()-1; |
177 | QVector<Tree>::iterator current = mTrees.begin(); |
176 | QVector<Tree>::iterator current = mTrees.begin(); |
178 | while (last>=current && (*last).isDead()) |
177 | while (last>=current && (*last).isDead()) |
179 | --last; |
178 | --last; |
180 | 179 | ||
181 | while (current<last) { |
180 | while (current<last) { |
182 | if ((*current).isDead()) { |
181 | if ((*current).isDead()) { |
183 | *current = *last; // copy data! |
182 | *current = *last; // copy data! |
184 | --last; // |
183 | --last; // |
185 | while (last>=current && (*last).isDead()) |
184 | while (last>=current && (*last).isDead()) |
186 | --last; |
185 | --last; |
187 | }
|
186 | }
|
188 | ++current; |
187 | ++current; |
189 | }
|
188 | }
|
190 | ++last; // last points now to the first dead tree |
189 | ++last; // last points now to the first dead tree |
191 | 190 | ||
192 | // free ressources
|
191 | // free ressources
|
193 | if (last!=mTrees.end()) { |
192 | if (last!=mTrees.end()) { |
194 | mTrees.erase(last, mTrees.end()); |
193 | mTrees.erase(last, mTrees.end()); |
195 | if (mTrees.capacity()>100) { |
194 | if (mTrees.capacity()>100) { |
196 | if (mTrees.count() / double(mTrees.capacity()) < 0.2) { |
195 | if (mTrees.count() / double(mTrees.capacity()) < 0.2) { |
197 | //int target_size = mTrees.count()*2;
|
196 | //int target_size = mTrees.count()*2;
|
198 | //qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size;
|
197 | //qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size;
|
199 | //mTrees.reserve(qMax(target_size, 100));
|
198 | //mTrees.reserve(qMax(target_size, 100));
|
200 | if (logLevelDebug()) |
199 | if (logLevelDebug()) |
201 | qDebug() << "reduce tree storage of RU" << index() << " from " << mTrees.capacity() << "to" << mTrees.count(); |
200 | qDebug() << "reduce tree storage of RU" << index() << " from " << mTrees.capacity() << "to" << mTrees.count(); |
202 | mTrees.squeeze(); |
201 | mTrees.squeeze(); |
203 | }
|
202 | }
|
204 | }
|
203 | }
|
205 | }
|
204 | }
|
206 | mHasDeadTrees = false; // reset flag |
205 | mHasDeadTrees = false; // reset flag |
207 | }
|
206 | }
|
208 | 207 | ||
209 | void ResourceUnit::newYear() |
208 | void ResourceUnit::newYear() |
210 | {
|
209 | {
|
211 | mAggregatedWLA = 0.; |
210 | mAggregatedWLA = 0.; |
212 | mAggregatedLA = 0.; |
211 | mAggregatedLA = 0.; |
213 | mAggregatedLR = 0.; |
212 | mAggregatedLR = 0.; |
214 | mEffectiveArea = 0.; |
213 | mEffectiveArea = 0.; |
215 | mPixelCount = mStockedPixelCount = 0; |
214 | mPixelCount = mStockedPixelCount = 0; |
216 | snagNewYear(); |
215 | snagNewYear(); |
217 | if (mSoil) |
216 | if (mSoil) |
218 | mSoil->newYear(); |
217 | mSoil->newYear(); |
219 | // clear statistics global and per species...
|
218 | // clear statistics global and per species...
|
220 | QList<ResourceUnitSpecies*>::const_iterator i; |
219 | QList<ResourceUnitSpecies*>::const_iterator i; |
221 | QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd(); |
220 | QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd(); |
222 | mStatistics.clear(); |
221 | mStatistics.clear(); |
223 | for (i=mRUSpecies.constBegin(); i!=iend; ++i) { |
222 | for (i=mRUSpecies.constBegin(); i!=iend; ++i) { |
224 | (*i)->statisticsDead().clear(); |
223 | (*i)->statisticsDead().clear(); |
225 | (*i)->statisticsMgmt().clear(); |
224 | (*i)->statisticsMgmt().clear(); |
226 | (*i)->changeSapling().newYear(); |
225 | (*i)->changeSapling().newYear(); |
227 | }
|
226 | }
|
228 | 227 | ||
229 | }
|
228 | }
|
230 | 229 | ||
231 | /** production() is the "stand-level" part of the biomass production (3PG).
|
230 | /** production() is the "stand-level" part of the biomass production (3PG).
|
232 | - The amount of radiation intercepted by the stand is calculated
|
231 | - The amount of radiation intercepted by the stand is calculated
|
233 | - the water cycle is calculated
|
232 | - the water cycle is calculated
|
234 | - statistics for each species are cleared
|
233 | - statistics for each species are cleared
|
235 | - The 3PG production for each species and ressource unit is called (calculates species-responses and NPP production)
|
234 | - The 3PG production for each species and ressource unit is called (calculates species-responses and NPP production)
|
236 | see also: http://iland.boku.ac.at/individual+tree+light+availability */
|
235 | see also: http://iland.boku.ac.at/individual+tree+light+availability */
|
237 | void ResourceUnit::production() |
236 | void ResourceUnit::production() |
238 | {
|
237 | {
|
239 | 238 | ||
240 | if (mAggregatedWLA==0 || mPixelCount==0) { |
239 | if (mAggregatedWLA==0 || mPixelCount==0) { |
241 | // nothing to do...
|
240 | // nothing to do...
|
242 | return; |
241 | return; |
243 | }
|
242 | }
|
244 | 243 | ||
245 | // the pixel counters are filled during the height-grid-calculations
|
244 | // the pixel counters are filled during the height-grid-calculations
|
246 | mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m) |
245 | mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m) |
247 | 246 | ||
248 | // calculate the leaf area index (LAI)
|
247 | // calculate the leaf area index (LAI)
|
249 | double LAI = mAggregatedLA / mStockedArea; |
248 | double LAI = mAggregatedLA / mStockedArea; |
250 | // calculate the intercepted radiation fraction using the law of Beer Lambert
|
249 | // calculate the intercepted radiation fraction using the law of Beer Lambert
|
251 | const double k = Model::settings().lightExtinctionCoefficient; |
250 | const double k = Model::settings().lightExtinctionCoefficient; |
252 | double interception_fraction = 1. - exp(-k * LAI); |
251 | double interception_fraction = 1. - exp(-k * LAI); |
253 | mEffectiveArea = mStockedArea * interception_fraction; // m2 |
252 | mEffectiveArea = mStockedArea * interception_fraction; // m2 |
254 | 253 | ||
255 | // calculate the total weighted leaf area on this RU:
|
254 | // calculate the total weighted leaf area on this RU:
|
256 | mLRI_modification = interception_fraction * mStockedArea / mAggregatedWLA; // p_WLA |
255 | mLRI_modification = interception_fraction * mStockedArea / mAggregatedWLA; // p_WLA |
257 | if (mLRI_modification == 0.) |
256 | if (mLRI_modification == 0.) |
258 | qDebug() << "lri modifaction==0!"; |
257 | qDebug() << "lri modifaction==0!"; |
259 | 258 | ||
260 | if (logLevelDebug()) { |
259 | if (logLevelDebug()) { |
261 | DBGMODE(qDebug() << QString("production: LAI: %1 (intercepted fraction: %2, stocked area: %4). LRI-Multiplier: %3") |
260 | DBGMODE(qDebug() << QString("production: LAI: %1 (intercepted fraction: %2, stocked area: %4). LRI-Multiplier: %3") |
262 | .arg(LAI) |
261 | .arg(LAI) |
263 | .arg(interception_fraction) |
262 | .arg(interception_fraction) |
264 | .arg(mLRI_modification) |
263 | .arg(mLRI_modification) |
265 | .arg(mStockedArea); |
264 | .arg(mStockedArea); |
266 | ); |
265 | ); |
267 | }
|
266 | }
|
268 | 267 | ||
269 | // calculate LAI fractions
|
268 | // calculate LAI fractions
|
270 | QList<ResourceUnitSpecies*>::const_iterator i; |
269 | QList<ResourceUnitSpecies*>::const_iterator i; |
271 | QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd(); |
270 | QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd(); |
272 | double ru_lai = leafAreaIndex(); |
271 | double ru_lai = leafAreaIndex(); |
273 | if (ru_lai < 1.) |
272 | if (ru_lai < 1.) |
274 | ru_lai = 1.; |
273 | ru_lai = 1.; |
275 | // note: LAIFactors are only 1 if sum of LAI is > 1. (see WaterCycle)
|
274 | // note: LAIFactors are only 1 if sum of LAI is > 1. (see WaterCycle)
|
276 | for (i=mRUSpecies.constBegin(); i!=iend; ++i) { |
275 | for (i=mRUSpecies.constBegin(); i!=iend; ++i) { |
277 | double lai_factor = (*i)->statistics().leafAreaIndex() / ru_lai; |
276 | double lai_factor = (*i)->statistics().leafAreaIndex() / ru_lai; |
278 | DBGMODE(
|
277 | DBGMODE(
|
279 | if (lai_factor > 1.) |
278 | if (lai_factor > 1.) |
280 | qDebug() << "LAI factor > 1"; |
279 | qDebug() << "LAI factor > 1"; |
281 | ); |
280 | ); |
282 | (*i)->setLAIfactor( lai_factor ); |
281 | (*i)->setLAIfactor( lai_factor ); |
283 | }
|
282 | }
|
284 | 283 | ||
285 | // soil water model - this determines soil water contents needed for response calculations
|
284 | // soil water model - this determines soil water contents needed for response calculations
|
286 | {
|
285 | {
|
287 | mWater->run(); |
286 | mWater->run(); |
288 | }
|
287 | }
|
289 | 288 | ||
290 | // invoke species specific calculation (3PG)
|
289 | // invoke species specific calculation (3PG)
|
291 | for (i=mRUSpecies.constBegin(); i!=iend; ++i) { |
290 | for (i=mRUSpecies.constBegin(); i!=iend; ++i) { |
292 | (*i)->calculate(); // CALCULATE 3PG |
291 | (*i)->calculate(); // CALCULATE 3PG |
293 | if (logLevelInfo() && (*i)->LAIfactor()>0) |
292 | if (logLevelInfo() && (*i)->LAIfactor()>0) |
294 | qDebug() << "ru" << mIndex << "species" << (*i)->species()->id() << "LAIfraction" << (*i)->LAIfactor() << "raw_gpp_m2" |
293 | qDebug() << "ru" << mIndex << "species" << (*i)->species()->id() << "LAIfraction" << (*i)->LAIfactor() << "raw_gpp_m2" |
295 | << (*i)->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:" |
294 | << (*i)->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:" |
296 | << productiveArea()*(*i)->prod3PG().GPPperArea() |
295 | << productiveArea()*(*i)->prod3PG().GPPperArea() |
297 | << "aging(lastyear):" << averageAging() << "f_env,yr:" << (*i)->prod3PG().fEnvYear(); |
296 | << "aging(lastyear):" << averageAging() << "f_env,yr:" << (*i)->prod3PG().fEnvYear(); |
298 | }
|
297 | }
|
299 | }
|
298 | }
|
300 | 299 | ||
301 | void ResourceUnit::calculateInterceptedArea() |
300 | void ResourceUnit::calculateInterceptedArea() |
302 | {
|
301 | {
|
303 | if (mAggregatedLR==0) { |
302 | if (mAggregatedLR==0) { |
304 | mEffectiveArea_perWLA = 0.; |
303 | mEffectiveArea_perWLA = 0.; |
305 | return; |
304 | return; |
306 | }
|
305 | }
|
307 | Q_ASSERT(mAggregatedLR>0.); |
306 | Q_ASSERT(mAggregatedLR>0.); |
308 | mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR; |
307 | mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR; |
309 | if (logLevelDebug()) qDebug() << "RU: aggregated lightresponse:" << mAggregatedLR << "eff.area./wla:" << mEffectiveArea_perWLA; |
308 | if (logLevelDebug()) qDebug() << "RU: aggregated lightresponse:" << mAggregatedLR << "eff.area./wla:" << mEffectiveArea_perWLA; |
310 | }
|
309 | }
|
311 | 310 | ||
312 | // function is called immediately before the growth of individuals
|
311 | // function is called immediately before the growth of individuals
|
313 | void ResourceUnit::beforeGrow() |
312 | void ResourceUnit::beforeGrow() |
314 | {
|
313 | {
|
315 | mAverageAging = 0.; |
314 | mAverageAging = 0.; |
316 | }
|
315 | }
|
317 | 316 | ||
318 | // function is called after finishing the indivdual growth / mortality.
|
317 | // function is called after finishing the indivdual growth / mortality.
|
319 | void ResourceUnit::afterGrow() |
318 | void ResourceUnit::afterGrow() |
320 | {
|
319 | {
|
321 | mAverageAging = leafArea()>0.?mAverageAging/leafArea():0; // calculate aging value (calls to addAverageAging() by individual trees) |
320 | mAverageAging = leafArea()>0.?mAverageAging/leafArea():0; // calculate aging value (calls to addAverageAging() by individual trees) |
322 | if (mAverageAging>0. && mAverageAging<0.00001) |
321 | if (mAverageAging>0. && mAverageAging<0.00001) |
323 | qDebug() << "ru" << mIndex << "aging <0.00001"; |
322 | qDebug() << "ru" << mIndex << "aging <0.00001"; |
324 | if (mAverageAging<0. || mAverageAging>1.) |
323 | if (mAverageAging<0. || mAverageAging>1.) |
325 | qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex(); |
324 | qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex(); |
326 | }
|
325 | }
|
327 | 326 | ||
328 | void ResourceUnit::yearEnd() |
327 | void ResourceUnit::yearEnd() |
329 | {
|
328 | {
|
330 | // calculate statistics for all tree species of the ressource unit
|
329 | // calculate statistics for all tree species of the ressource unit
|
331 | int c = mRUSpecies.count(); |
330 | int c = mRUSpecies.count(); |
332 | for (int i=0;i<c; i++) { |
331 | for (int i=0;i<c; i++) { |
333 | mRUSpecies[i]->statisticsDead().calculate(); // calculate the dead trees |
332 | mRUSpecies[i]->statisticsDead().calculate(); // calculate the dead trees |
334 | mRUSpecies[i]->statisticsMgmt().calculate(); // stats of removed trees |
333 | mRUSpecies[i]->statisticsMgmt().calculate(); // stats of removed trees |
335 | mRUSpecies[i]->updateGWL(); // get sum of dead trees (died + removed) |
334 | mRUSpecies[i]->updateGWL(); // get sum of dead trees (died + removed) |
336 | mRUSpecies[i]->statistics().calculate(); // calculate the living (and add removed volume to gwl) |
335 | mRUSpecies[i]->statistics().calculate(); // calculate the living (and add removed volume to gwl) |
337 | mStatistics.add(mRUSpecies[i]->statistics()); |
336 | mStatistics.add(mRUSpecies[i]->statistics()); |
338 | }
|
337 | }
|
339 | mStatistics.calculate(); // aggreagte on stand level |
338 | mStatistics.calculate(); // aggreagte on stand level |
340 | 339 | ||
341 | }
|
340 | }
|
342 | 341 | ||
343 | void ResourceUnit::addTreeAgingForAllTrees() |
342 | void ResourceUnit::addTreeAgingForAllTrees() |
344 | {
|
343 | {
|
345 | mAverageAging = 0.; |
344 | mAverageAging = 0.; |
346 | foreach(const Tree &t, mTrees) { |
345 | foreach(const Tree &t, mTrees) { |
347 | addTreeAging(t.leafArea(), t.species()->aging(t.height(), t.age())); |
346 | addTreeAging(t.leafArea(), t.species()->aging(t.height(), t.age())); |
348 | }
|
347 | }
|
349 | 348 | ||
350 | }
|
349 | }
|
351 | 350 | ||
352 | /// refresh of tree based statistics.
|
351 | /// refresh of tree based statistics.
|
353 | /// WARNING: this function is only called once (during startup).
|
352 | /// WARNING: this function is only called once (during startup).
|
354 | /// see function "yearEnd()" above!!!
|
353 | /// see function "yearEnd()" above!!!
|
355 | void ResourceUnit::createStandStatistics() |
354 | void ResourceUnit::createStandStatistics() |
356 | {
|
355 | {
|
357 | // clear statistics (ru-level and ru-species level)
|
356 | // clear statistics (ru-level and ru-species level)
|
358 | mStatistics.clear(); |
357 | mStatistics.clear(); |
359 | for (int i=0;i<mRUSpecies.count();i++) { |
358 | for (int i=0;i<mRUSpecies.count();i++) { |
360 | mRUSpecies[i]->statistics().clear(); |
359 | mRUSpecies[i]->statistics().clear(); |
361 | mRUSpecies[i]->statisticsDead().clear(); |
360 | mRUSpecies[i]->statisticsDead().clear(); |
362 | mRUSpecies[i]->statisticsMgmt().clear(); |
361 | mRUSpecies[i]->statisticsMgmt().clear(); |
363 | }
|
362 | }
|
364 | 363 | ||
365 | // add all trees to the statistics objects of the species
|
364 | // add all trees to the statistics objects of the species
|
366 | foreach(const Tree &t, mTrees) { |
365 | foreach(const Tree &t, mTrees) { |
367 | if (!t.isDead()) |
366 | if (!t.isDead()) |
368 | resourceUnitSpecies(t.species()).statistics().add(&t, 0); |
367 | resourceUnitSpecies(t.species()).statistics().add(&t, 0); |
369 | }
|
368 | }
|
370 | // summarize statistics for the whole resource unit
|
369 | // summarize statistics for the whole resource unit
|
371 | for (int i=0;i<mRUSpecies.count();i++) { |
370 | for (int i=0;i<mRUSpecies.count();i++) { |
372 | mRUSpecies[i]->statistics().calculate(); |
371 | mRUSpecies[i]->statistics().calculate(); |
373 | mStatistics.add(mRUSpecies[i]->statistics()); |
372 | mStatistics.add(mRUSpecies[i]->statistics()); |
374 | }
|
373 | }
|
375 | mStatistics.calculate(); |
374 | mStatistics.calculate(); |
376 | mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*stockableArea()):0.; |
375 | mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*stockableArea()):0.; |
377 | if (mAverageAging<0. || mAverageAging>1.) |
376 | if (mAverageAging<0. || mAverageAging>1.) |
378 | qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex(); |
377 | qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex(); |
379 | }
|
378 | }
|
380 | 379 | ||
381 | /** recreate statistics. This is necessary after events that changed the structure
|
380 | /** recreate statistics. This is necessary after events that changed the structure
|
382 | of the stand *after* the growth of trees (where stand statistics are updated).
|
381 | of the stand *after* the growth of trees (where stand statistics are updated).
|
383 | An example is after disturbances. */
|
382 | An example is after disturbances. */
|
384 | void ResourceUnit::recreateStandStatistics() |
383 | void ResourceUnit::recreateStandStatistics() |
385 | {
|
384 | {
|
386 | for (int i=0;i<mRUSpecies.count();i++) { |
385 | for (int i=0;i<mRUSpecies.count();i++) { |
387 | mRUSpecies[i]->statistics().clear(); |
386 | mRUSpecies[i]->statistics().clear(); |
388 | }
|
387 | }
|
389 | foreach(const Tree &t, mTrees) { |
388 | foreach(const Tree &t, mTrees) { |
390 | resourceUnitSpecies(t.species()).statistics().add(&t, 0); |
389 | resourceUnitSpecies(t.species()).statistics().add(&t, 0); |
391 | }
|
390 | }
|
392 | 391 | ||
393 | }
|
392 | }
|
394 | 393 | ||
395 | void ResourceUnit::setMaxSaplingHeightAt(const QPoint &position, const float height) |
394 | void ResourceUnit::setMaxSaplingHeightAt(const QPoint &position, const float height) |
396 | {
|
395 | {
|
397 | Q_ASSERT(mSaplingHeightMap); |
396 | Q_ASSERT(mSaplingHeightMap); |
398 | int pixel_index = cPxPerRU*(position.x()-mCornerCoord.x())+(position.y()-mCornerCoord.y()); |
397 | int pixel_index = cPxPerRU*(position.x()-mCornerCoord.x())+(position.y()-mCornerCoord.y()); |
399 | if (pixel_index<0 || pixel_index>=cPxPerRU*cPxPerRU) { |
398 | if (pixel_index<0 || pixel_index>=cPxPerRU*cPxPerRU) { |
400 | qDebug() << "setSaplingHeightAt-Error for position" << position << "for RU at" << boundingBox() << "with corner" << mCornerCoord; |
399 | qDebug() << "setSaplingHeightAt-Error for position" << position << "for RU at" << boundingBox() << "with corner" << mCornerCoord; |
401 | } else { |
400 | } else { |
402 | if (mSaplingHeightMap[pixel_index]<height) |
401 | if (mSaplingHeightMap[pixel_index]<height) |
403 | mSaplingHeightMap[pixel_index]=height; |
402 | mSaplingHeightMap[pixel_index]=height; |
404 | }
|
403 | }
|
405 | }
|
404 | }
|
406 | 405 | ||
407 | /// clear all saplings of all species on a given position (after recruitment)
|
406 | /// clear all saplings of all species on a given position (after recruitment)
|
408 | void ResourceUnit::clearSaplings(const QPoint &position) |
407 | void ResourceUnit::clearSaplings(const QPoint &position) |
409 | {
|
408 | {
|
410 | foreach(ResourceUnitSpecies* rus, mRUSpecies) |
409 | foreach(ResourceUnitSpecies* rus, mRUSpecies) |
411 | rus->clearSaplings(position); |
410 | rus->clearSaplings(position); |
412 | 411 | ||
413 | }
|
412 | }
|
414 | 413 | ||
415 | /// kill all saplings within a given rect
|
414 | /// kill all saplings within a given rect
|
416 | void ResourceUnit::clearSaplings(const QRectF pixel_rect, const bool remove_from_soil) |
415 | void ResourceUnit::clearSaplings(const QRectF pixel_rect, const bool remove_from_soil) |
417 | {
|
416 | {
|
418 | foreach(ResourceUnitSpecies* rus, mRUSpecies) { |
417 | foreach(ResourceUnitSpecies* rus, mRUSpecies) { |
419 | rus->changeSapling().clearSaplings(pixel_rect, remove_from_soil); |
418 | rus->changeSapling().clearSaplings(pixel_rect, remove_from_soil); |
420 | }
|
419 | }
|
421 | 420 | ||
422 | }
|
421 | }
|
423 | 422 | ||
424 | float ResourceUnit::saplingHeightForInit(const QPoint &position) const |
423 | float ResourceUnit::saplingHeightForInit(const QPoint &position) const |
425 | {
|
424 | {
|
426 | double maxh = 0.; |
425 | double maxh = 0.; |
427 | foreach(ResourceUnitSpecies* rus, mRUSpecies) |
426 | foreach(ResourceUnitSpecies* rus, mRUSpecies) |
428 | maxh = qMax(maxh, rus->sapling().heightAt(position)); |
427 | maxh = qMax(maxh, rus->sapling().heightAt(position)); |
429 | return maxh; |
428 | return maxh; |
430 | }
|
429 | }
|
431 | 430 | ||
432 | void ResourceUnit::calculateCarbonCycle() |
431 | void ResourceUnit::calculateCarbonCycle() |
433 | {
|
432 | {
|
434 | if (!snag()) |
433 | if (!snag()) |
435 | return; |
434 | return; |
436 | 435 | ||
437 | // (1) calculate the snag dynamics
|
436 | // (1) calculate the snag dynamics
|
438 | // because all carbon/nitrogen-flows from trees to the soil are routed through the snag-layer,
|
437 | // because all carbon/nitrogen-flows from trees to the soil are routed through the snag-layer,
|
439 | // all soil inputs (litter + deadwood) are collected in the Snag-object.
|
438 | // all soil inputs (litter + deadwood) are collected in the Snag-object.
|
440 | snag()->calculateYear(); |
439 | snag()->calculateYear(); |
441 | soil()->setClimateFactor( snag()->climateFactor() ); // the climate factor is only calculated once |
440 | soil()->setClimateFactor( snag()->climateFactor() ); // the climate factor is only calculated once |
442 | soil()->setSoilInput( snag()->labileFlux(), snag()->refractoryFlux()); |
441 | soil()->setSoilInput( snag()->labileFlux(), snag()->refractoryFlux()); |
443 | soil()->calculateYear(); // update the ICBM/2N model |
442 | soil()->calculateYear(); // update the ICBM/2N model |
444 | // use available nitrogen?
|
443 | // use available nitrogen?
|
445 | if (Model::settings().useDynamicAvailableNitrogen) |
444 | if (Model::settings().useDynamicAvailableNitrogen) |
446 | mUnitVariables.nitrogenAvailable = soil()->availableNitrogen(); |
445 | mUnitVariables.nitrogenAvailable = soil()->availableNitrogen(); |
447 | 446 | ||
448 | // debug output
|
447 | // debug output
|
449 | if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dCarbonCycle) && !snag()->isEmpty()) { |
448 | if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dCarbonCycle) && !snag()->isEmpty()) { |
450 | DebugList &out = GlobalSettings::instance()->debugList(index(), GlobalSettings::dCarbonCycle); |
449 | DebugList &out = GlobalSettings::instance()->debugList(index(), GlobalSettings::dCarbonCycle); |
451 | out << index() << id(); // resource unit index and id |
450 | out << index() << id(); // resource unit index and id |
452 | out << snag()->debugList(); // snag debug outs |
451 | out << snag()->debugList(); // snag debug outs |
453 | out << soil()->debugList(); // ICBM/2N debug outs |
452 | out << soil()->debugList(); // ICBM/2N debug outs |
454 | }
|
453 | }
|
455 | 454 | ||
456 | }
|
455 | }
|
457 | 456 | ||
458 | 457 | ||
459 | 458 |