Rev 451 | Rev 453 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 451 | Rev 452 | ||
---|---|---|---|
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 | /** @class ResourceUnit
|
2 | /** @class ResourceUnit
|
3 | ResourceUnit is the spatial unit that encapsulates a forest stand and links to several environmental components
|
3 | ResourceUnit is the spatial unit that encapsulates a forest stand and links to several environmental components
|
4 | (Climate, Soil, Water, ...).
|
4 | (Climate, Soil, Water, ...).
|
5 | 5 | ||
6 | */
|
6 | */
|
7 | #include <QtCore>
|
7 | #include <QtCore>
|
8 | #include "global.h"
|
8 | #include "global.h"
|
9 | 9 | ||
10 | #include "resourceunit.h"
|
10 | #include "resourceunit.h"
|
11 | #include "resourceunitspecies.h"
|
11 | #include "resourceunitspecies.h"
|
12 | #include "speciesset.h"
|
12 | #include "speciesset.h"
|
13 | #include "species.h"
|
13 | #include "species.h"
|
14 | #include "production3pg.h"
|
14 | #include "production3pg.h"
|
15 | #include "model.h"
|
15 | #include "model.h"
|
16 | #include "climate.h"
|
16 | #include "climate.h"
|
17 | #include "watercycle.h"
|
17 | #include "watercycle.h"
|
18 | #include "helper.h"
|
18 | #include "helper.h"
|
19 | 19 | ||
20 | ResourceUnit::~ResourceUnit() |
20 | ResourceUnit::~ResourceUnit() |
21 | {
|
21 | {
|
22 | if (mWater) |
22 | if (mWater) |
23 | delete mWater; |
23 | delete mWater; |
24 | }
|
24 | }
|
25 | 25 | ||
26 | ResourceUnit::ResourceUnit(const int index) |
26 | ResourceUnit::ResourceUnit(const int index) |
27 | {
|
27 | {
|
28 | mSpeciesSet = 0; |
28 | mSpeciesSet = 0; |
29 | mClimate = 0; |
29 | mClimate = 0; |
30 | mPixelCount=0; |
30 | mPixelCount=0; |
31 | mIndex = index; |
31 | mIndex = index; |
32 | mSaplingHeightMap = 0; |
32 | mSaplingHeightMap = 0; |
33 | mWater = new WaterCycle(); |
33 | mWater = new WaterCycle(); |
34 | 34 | ||
35 | mTrees.reserve(100); // start with space for 100 trees. |
35 | mTrees.reserve(100); // start with space for 100 trees. |
36 | }
|
36 | }
|
37 | 37 | ||
38 | void ResourceUnit::setup() |
38 | void ResourceUnit::setup() |
39 | {
|
39 | {
|
40 | mWater->setup(this); |
40 | mWater->setup(this); |
41 | // setup variables
|
41 | // setup variables
|
42 | mUnitVariables.nitrogenAvailable = GlobalSettings::instance()->settings().valueDouble("model.site.availableNitrogen", 40); |
42 | mUnitVariables.nitrogenAvailable = GlobalSettings::instance()->settings().valueDouble("model.site.availableNitrogen", 40); |
43 | mAverageAging = 0.; |
43 | mAverageAging = 0.; |
44 | 44 | ||
45 | }
|
45 | }
|
46 | void ResourceUnit::setBoundingBox(const QRectF &bb) |
46 | void ResourceUnit::setBoundingBox(const QRectF &bb) |
47 | {
|
47 | {
|
48 | mBoundingBox = bb; |
48 | mBoundingBox = bb; |
49 | mCornerCoord = GlobalSettings::instance()->model()->grid()->indexAt(bb.topLeft()); |
49 | mCornerCoord = GlobalSettings::instance()->model()->grid()->indexAt(bb.topLeft()); |
50 | }
|
50 | }
|
51 | 51 | ||
52 | /// set species and setup the species-per-RU-data
|
52 | /// set species and setup the species-per-RU-data
|
53 | void ResourceUnit::setSpeciesSet(SpeciesSet *set) |
53 | void ResourceUnit::setSpeciesSet(SpeciesSet *set) |
54 | {
|
54 | {
|
55 | mSpeciesSet = set; |
55 | mSpeciesSet = set; |
56 | mRUSpecies.clear(); |
56 | mRUSpecies.clear(); |
57 | mRUSpecies.resize(set->count()); // ensure that the vector space is not relocated |
57 | mRUSpecies.resize(set->count()); // ensure that the vector space is not relocated |
58 | for (int i=0;i<set->count();i++) { |
58 | for (int i=0;i<set->count();i++) { |
59 | Species *s = const_cast<Species*>(mSpeciesSet->species(i)); |
59 | Species *s = const_cast<Species*>(mSpeciesSet->species(i)); |
60 | if (!s) |
60 | if (!s) |
61 | throw IException("ResourceUnit::setSpeciesSet: invalid index!"); |
61 | throw IException("ResourceUnit::setSpeciesSet: invalid index!"); |
62 | 62 | ||
63 | /* be careful: setup() is called with a pointer somewhere to the content of the mRUSpecies container.
|
63 | /* be careful: setup() is called with a pointer somewhere to the content of the mRUSpecies container.
|
64 | If the container memory is relocated (QVector), the pointer gets invalid!!!
|
64 | If the container memory is relocated (QVector), the pointer gets invalid!!!
|
65 | Therefore, a resize() is called before the loop (no resize()-operations during the loop)! */
|
65 | Therefore, a resize() is called before the loop (no resize()-operations during the loop)! */
|
66 | mRUSpecies[i].setup(s,this); // setup this element |
66 | mRUSpecies[i].setup(s,this); // setup this element |
67 | 67 | ||
68 | }
|
68 | }
|
69 | }
|
69 | }
|
70 | 70 | ||
71 | ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species) |
71 | ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species) |
72 | {
|
72 | {
|
73 | return mRUSpecies[species->index()]; |
73 | return mRUSpecies[species->index()]; |
74 | }
|
74 | }
|
75 | 75 | ||
76 | Tree &ResourceUnit::newTree() |
76 | Tree &ResourceUnit::newTree() |
77 | {
|
77 | {
|
78 | // start simple: just append to the vector...
|
78 | // start simple: just append to the vector...
|
79 | mTrees.append(Tree()); |
79 | mTrees.append(Tree()); |
80 | return mTrees.back(); |
80 | return mTrees.back(); |
81 | }
|
81 | }
|
82 | int ResourceUnit::newTreeIndex() |
82 | int ResourceUnit::newTreeIndex() |
83 | {
|
83 | {
|
84 | // start simple: just append to the vector...
|
84 | // start simple: just append to the vector...
|
85 | mTrees.append(Tree()); |
85 | mTrees.append(Tree()); |
86 | return mTrees.count()-1; |
86 | return mTrees.count()-1; |
87 | }
|
87 | }
|
88 | 88 | ||
89 | /// remove dead trees from tree list
|
89 | /// remove dead trees from tree list
|
90 | /// reduce size of vector if lots of space is free
|
90 | /// reduce size of vector if lots of space is free
|
91 | /// tests showed that this way of cleanup is very fast,
|
91 | /// tests showed that this way of cleanup is very fast,
|
92 | /// because no memory allocations are performed (simple memmove())
|
92 | /// because no memory allocations are performed (simple memmove())
|
93 | /// when trees are moved.
|
93 | /// when trees are moved.
|
94 | void ResourceUnit::cleanTreeList() |
94 | void ResourceUnit::cleanTreeList() |
95 | {
|
95 | {
|
96 | QVector<Tree>::iterator last=mTrees.end()-1; |
96 | QVector<Tree>::iterator last=mTrees.end()-1; |
97 | QVector<Tree>::iterator current = mTrees.begin(); |
97 | QVector<Tree>::iterator current = mTrees.begin(); |
98 | while (last>=current && (*last).isDead()) |
98 | while (last>=current && (*last).isDead()) |
99 | --last; |
99 | --last; |
100 | 100 | ||
101 | while (current<last) { |
101 | while (current<last) { |
102 | if ((*current).isDead()) { |
102 | if ((*current).isDead()) { |
103 | *current = *last; // copy data! |
103 | *current = *last; // copy data! |
104 | --last; // |
104 | --last; // |
105 | while (last>=current && (*last).isDead()) |
105 | while (last>=current && (*last).isDead()) |
106 | --last; |
106 | --last; |
107 | }
|
107 | }
|
108 | ++current; |
108 | ++current; |
109 | }
|
109 | }
|
110 | ++last; // last points now to the first dead tree |
110 | ++last; // last points now to the first dead tree |
111 | 111 | ||
112 | // free ressources
|
112 | // free ressources
|
113 | if (last!=mTrees.end()) { |
113 | if (last!=mTrees.end()) { |
114 | mTrees.erase(last, mTrees.end()); |
114 | mTrees.erase(last, mTrees.end()); |
115 | if (mTrees.capacity()>100) { |
115 | if (mTrees.capacity()>100) { |
116 | if (mTrees.count() / double(mTrees.capacity()) < 0.2) { |
116 | if (mTrees.count() / double(mTrees.capacity()) < 0.2) { |
117 | int target_size = mTrees.count()*2; |
117 | int target_size = mTrees.count()*2; |
118 | qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size; |
118 | qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size; |
119 | mTrees.reserve(qMax(target_size, 100)); |
119 | mTrees.reserve(qMax(target_size, 100)); |
120 | }
|
120 | }
|
121 | }
|
121 | }
|
122 | }
|
122 | }
|
123 | }
|
123 | }
|
124 | 124 | ||
125 | void ResourceUnit::newYear() |
125 | void ResourceUnit::newYear() |
126 | {
|
126 | {
|
127 | mAggregatedWLA = 0.; |
127 | mAggregatedWLA = 0.; |
128 | mAggregatedLA = 0.; |
128 | mAggregatedLA = 0.; |
129 | mAggregatedLR = 0.; |
129 | mAggregatedLR = 0.; |
130 | mEffectiveArea = 0.; |
130 | mEffectiveArea = 0.; |
131 | mPixelCount = mStockedPixelCount = 0; |
131 | mPixelCount = mStockedPixelCount = 0; |
132 | // clear statistics global and per species...
|
132 | // clear statistics global and per species...
|
133 | ResourceUnitSpecies *i; |
133 | ResourceUnitSpecies *i; |
134 | QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end(); |
134 | QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end(); |
135 | mStatistics.clear(); |
135 | mStatistics.clear(); |
136 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
136 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
137 | i->statisticsDead().clear(); |
137 | i->statisticsDead().clear(); |
138 | i->statisticsMgmt().clear(); |
138 | i->statisticsMgmt().clear(); |
139 | }
|
139 | }
|
140 | 140 | ||
141 | }
|
141 | }
|
142 | 142 | ||
143 | /** production() is the "stand-level" part of the biomass production (3PG).
|
143 | /** production() is the "stand-level" part of the biomass production (3PG).
|
144 | - The amount of radiation intercepted by the stand is calculated
|
144 | - The amount of radiation intercepted by the stand is calculated
|
145 | - the water cycle is calculated
|
145 | - the water cycle is calculated
|
146 | - statistics for each species are cleared
|
146 | - statistics for each species are cleared
|
147 | - The 3PG production for each species and ressource unit is called (calculates species-responses and NPP production)
|
147 | - The 3PG production for each species and ressource unit is called (calculates species-responses and NPP production)
|
148 | see also: http://iland.boku.ac.at/individual+tree+light+availability */
|
148 | see also: http://iland.boku.ac.at/individual+tree+light+availability */
|
149 | void ResourceUnit::production() |
149 | void ResourceUnit::production() |
150 | {
|
150 | {
|
151 | 151 | ||
152 | if (mAggregatedWLA==0 || mPixelCount==0) { |
152 | if (mAggregatedWLA==0 || mPixelCount==0) { |
153 | // nothing to do...
|
153 | // nothing to do...
|
154 | return; |
154 | return; |
155 | }
|
155 | }
|
156 | 156 | ||
157 | // the pixel counters are filled during the height-grid-calculations
|
157 | // the pixel counters are filled during the height-grid-calculations
|
158 | mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m) |
158 | mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m) |
159 | 159 | ||
160 | // calculate the leaf area index (LAI)
|
160 | // calculate the leaf area index (LAI)
|
161 | double LAI = mAggregatedLA / mStockedArea; |
161 | double LAI = mAggregatedLA / mStockedArea; |
162 | // calculate the intercepted radiation fraction using the law of Beer Lambert
|
162 | // calculate the intercepted radiation fraction using the law of Beer Lambert
|
163 | const double k = Model::settings().lightExtinctionCoefficient; |
163 | const double k = Model::settings().lightExtinctionCoefficient; |
164 | double interception_fraction = 1. - exp(-k * LAI); |
164 | double interception_fraction = 1. - exp(-k * LAI); |
165 | mEffectiveArea = mStockedArea * interception_fraction; // m2 |
165 | mEffectiveArea = mStockedArea * interception_fraction; // m2 |
166 | 166 | ||
167 | // calculate the total weighted leaf area on this RU:
|
167 | // calculate the total weighted leaf area on this RU:
|
168 | mLRI_modification = interception_fraction * mStockedArea / mAggregatedWLA; |
168 | mLRI_modification = interception_fraction * mStockedArea / mAggregatedWLA; |
169 | if (mLRI_modification == 0.) |
169 | if (mLRI_modification == 0.) |
170 | qDebug() << "lri modifaction==0!"; |
170 | qDebug() << "lri modifaction==0!"; |
171 | 171 | ||
172 | 172 | ||
173 | DBGMODE(qDebug() << QString("production: LAI: %1 (intercepted fraction: %2, stocked area: %4). LRI-Multiplier: %3") |
173 | DBGMODE(qDebug() << QString("production: LAI: %1 (intercepted fraction: %2, stocked area: %4). LRI-Multiplier: %3") |
174 | .arg(LAI) |
174 | .arg(LAI) |
175 | .arg(interception_fraction) |
175 | .arg(interception_fraction) |
176 | .arg(mLRI_modification) |
176 | .arg(mLRI_modification) |
177 | .arg(mStockedArea); |
177 | .arg(mStockedArea); |
178 | ); |
178 | ); |
179 | 179 | ||
180 | // calculate LAI fractions
|
180 | // calculate LAI fractions
|
181 | ResourceUnitSpecies *i; |
181 | ResourceUnitSpecies *i; |
182 | QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end(); |
182 | QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end(); |
183 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
183 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
184 | i->setLAIfactor(i->statistics().leafAreaIndex() / leafAreaIndex()); |
184 | i->setLAIfactor(i->statistics().leafAreaIndex() / leafAreaIndex()); |
185 | }
|
185 | }
|
186 | 186 | ||
187 | // soil water model - this determines soil water contents needed for response calculations
|
187 | // soil water model - this determines soil water contents needed for response calculations
|
188 | {
|
188 | {
|
189 | DebugTimer tw("water:run"); |
189 | DebugTimer tw("water:run"); |
190 | mWater->run(); |
190 | mWater->run(); |
191 | }
|
191 | }
|
192 | 192 | ||
193 | // invoke species specific calculation (3PG)
|
193 | // invoke species specific calculation (3PG)
|
194 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
194 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
195 | i->calculate(); |
195 | i->calculate(); |
196 | if (logLevelInfo() && i->LAIfactor()>0) |
196 | if (logLevelInfo() && i->LAIfactor()>0) |
197 | qDebug() << "ru" << mIndex << "species" << (*i).species()->id() << "LAIfraction" << i->LAIfactor() << "raw_gpp_m2" |
197 | qDebug() << "ru" << mIndex << "species" << (*i).species()->id() << "LAIfraction" << i->LAIfactor() << "raw_gpp_m2" |
198 | << i->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:" |
198 | << i->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:" |
199 | << productiveArea()*i->prod3PG().GPPperArea() |
199 | << productiveArea()*i->prod3PG().GPPperArea() |
200 | << "aging(lastyear):" << averageAging() << "f_env,yr:" << i->prod3PG().fEnvYear(); |
200 | << "aging(lastyear):" << averageAging() << "f_env,yr:" << i->prod3PG().fEnvYear(); |
201 | }
|
201 | }
|
202 | }
|
202 | }
|
203 | 203 | ||
204 | void ResourceUnit::calculateInterceptedArea() |
204 | void ResourceUnit::calculateInterceptedArea() |
205 | {
|
205 | {
|
206 | if (mAggregatedLR==0) { |
206 | if (mAggregatedLR==0) { |
207 | mEffectiveArea_perWLA = 0.; |
207 | mEffectiveArea_perWLA = 0.; |
208 | return; |
208 | return; |
209 | }
|
209 | }
|
210 | Q_ASSERT(mAggregatedLR>0.); |
210 | Q_ASSERT(mAggregatedLR>0.); |
211 | mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR; |
211 | mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR; |
212 | if (logLevelDebug()) qDebug() << "RU: aggregated lightresponse:" << mAggregatedLR << "eff.area./wla:" << mEffectiveArea_perWLA; |
212 | if (logLevelDebug()) qDebug() << "RU: aggregated lightresponse:" << mAggregatedLR << "eff.area./wla:" << mEffectiveArea_perWLA; |
213 | }
|
213 | }
|
214 | 214 | ||
215 | // function is called immediately before the growth of individuals
|
215 | // function is called immediately before the growth of individuals
|
216 | void ResourceUnit::beforeGrow() |
216 | void ResourceUnit::beforeGrow() |
217 | {
|
217 | {
|
218 | mAverageAging = 0.; |
218 | mAverageAging = 0.; |
219 | }
|
219 | }
|
220 | 220 | ||
221 | // function is called after finishing the indivdual growth / mortality.
|
221 | // function is called after finishing the indivdual growth / mortality.
|
222 | void ResourceUnit::afterGrow() |
222 | void ResourceUnit::afterGrow() |
223 | {
|
223 | {
|
224 | mAverageAging = leafArea()>0.?mAverageAging/leafArea():0; // calculate aging value (calls to addAverageAging() by individual trees) |
224 | mAverageAging = leafArea()>0.?mAverageAging/leafArea():0; // calculate aging value (calls to addAverageAging() by individual trees) |
225 | if (mAverageAging>0. && mAverageAging<0.00001) |
225 | if (mAverageAging>0. && mAverageAging<0.00001) |
226 | qDebug() << "ru" << mIndex << "aging <0.00001"; |
226 | qDebug() << "ru" << mIndex << "aging <0.00001"; |
227 | }
|
227 | }
|
228 | 228 | ||
229 | void ResourceUnit::yearEnd() |
229 | void ResourceUnit::yearEnd() |
230 | {
|
230 | {
|
231 | // calculate statistics for all tree species of the ressource unit
|
231 | // calculate statistics for all tree species of the ressource unit
|
232 | int c = mRUSpecies.count(); |
232 | int c = mRUSpecies.count(); |
233 | for (int i=0;i<c; i++) { |
233 | for (int i=0;i<c; i++) { |
234 | mRUSpecies[i].statisticsDead().calculate(); // calculate the dead trees |
234 | mRUSpecies[i].statisticsDead().calculate(); // calculate the dead trees |
235 | mRUSpecies[i].statisticsMgmt().calculate(); // stats of removed trees |
235 | mRUSpecies[i].statisticsMgmt().calculate(); // stats of removed trees |
236 | mRUSpecies[i].updateGWL(); // get sum of dead trees (died + removed) |
236 | mRUSpecies[i].updateGWL(); // get sum of dead trees (died + removed) |
237 | mRUSpecies[i].statistics().calculate(); // calculate the living (and add removed volume to gwl) |
237 | mRUSpecies[i].statistics().calculate(); // calculate the living (and add removed volume to gwl) |
238 | mStatistics.add(mRUSpecies[i].statistics()); |
238 | mStatistics.add(mRUSpecies[i].statistics()); |
239 | }
|
239 | }
|
240 | mStatistics.calculate(); // aggreagte on stand level |
240 | mStatistics.calculate(); // aggreagte on stand level |
241 | }
|
241 | }
|
242 | 242 | ||
243 | /// refresh of tree based statistics.
|
243 | /// refresh of tree based statistics.
|
244 | void ResourceUnit::createStandStatistics() |
244 | void ResourceUnit::createStandStatistics() |
245 | {
|
245 | {
|
246 | // clear statistics (ru-level and ru-species level)
|
246 | // clear statistics (ru-level and ru-species level)
|
247 | mStatistics.clear(); |
247 | mStatistics.clear(); |
248 | for (int i=0;i<mRUSpecies.count();i++) { |
248 | for (int i=0;i<mRUSpecies.count();i++) { |
249 | mRUSpecies[i].statistics().clear(); |
249 | mRUSpecies[i].statistics().clear(); |
250 | mRUSpecies[i].statisticsDead().clear(); |
250 | mRUSpecies[i].statisticsDead().clear(); |
251 | mRUSpecies[i].statisticsMgmt().clear(); |
251 | mRUSpecies[i].statisticsMgmt().clear(); |
252 | }
|
252 | }
|
253 | 253 | ||
254 | // add all trees to the statistics objects of the species
|
254 | // add all trees to the statistics objects of the species
|
255 | foreach(const Tree &t, mTrees) { |
255 | foreach(const Tree &t, mTrees) { |
256 | if (!t.isDead()) |
256 | if (!t.isDead()) |
257 | resourceUnitSpecies(t.species()).statistics().add(&t, 0); |
257 | resourceUnitSpecies(t.species()).statistics().add(&t, 0); |
258 | }
|
258 | }
|
259 | // summarize statistics for the whole resource unit
|
259 | // summarize statistics for the whole resource unit
|
260 | for (int i=0;i<mRUSpecies.count();i++) { |
260 | for (int i=0;i<mRUSpecies.count();i++) { |
261 | mRUSpecies[i].statistics().calculate(); |
261 | mRUSpecies[i].statistics().calculate(); |
262 | mStatistics.add(mRUSpecies[i].statistics()); |
262 | mStatistics.add(mRUSpecies[i].statistics()); |
263 | }
|
263 | }
|
264 | mStatistics.calculate(); |
264 | mStatistics.calculate(); |
265 | mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*area()):0.; |
265 | mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*area()):0.; |
266 | }
|
266 | }
|
- | 267 | ||
- | 268 | void ResourceUnit::setSaplingHeightAt(const QPoint &position, const float height) |
|
- | 269 | {
|
|
- | 270 | Q_ASSERT(mSaplingHeightMap); |
|
- | 271 | int pixel_index = cPxPerRU*(position.x()-mCornerCoord.x())+(position.y()-mCornerCoord.y()); |
|
- | 272 | if (pixel_index<0 || pixel_index>=cPxPerRU*cPxPerRU) |
|
- | 273 | qDebug() << "setSaplingHeightAt-Error"; |
|
- | 274 | else
|
|
- | 275 | mSaplingHeightMap[pixel_index]=height; |
|
- | 276 | }
|
|
- | 277 | ||
267 | 278 |