Rev 450 | Rev 452 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 450 | Rev 451 | ||
---|---|---|---|
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 | }
|
|
- | 46 | void ResourceUnit::setBoundingBox(const QRectF &bb) |
|
- | 47 | {
|
|
- | 48 | mBoundingBox = bb; |
|
- | 49 | mCornerCoord = GlobalSettings::instance()->model()->grid()->indexAt(bb.topLeft()); |
|
45 | }
|
50 | }
|
46 | 51 | ||
47 | /// set species and setup the species-per-RU-data
|
52 | /// set species and setup the species-per-RU-data
|
48 | void ResourceUnit::setSpeciesSet(SpeciesSet *set) |
53 | void ResourceUnit::setSpeciesSet(SpeciesSet *set) |
49 | {
|
54 | {
|
50 | mSpeciesSet = set; |
55 | mSpeciesSet = set; |
51 | mRUSpecies.clear(); |
56 | mRUSpecies.clear(); |
52 | 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 |
53 | for (int i=0;i<set->count();i++) { |
58 | for (int i=0;i<set->count();i++) { |
54 | Species *s = const_cast<Species*>(mSpeciesSet->species(i)); |
59 | Species *s = const_cast<Species*>(mSpeciesSet->species(i)); |
55 | if (!s) |
60 | if (!s) |
56 | throw IException("ResourceUnit::setSpeciesSet: invalid index!"); |
61 | throw IException("ResourceUnit::setSpeciesSet: invalid index!"); |
57 | 62 | ||
58 | /* 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.
|
59 | If the container memory is relocated (QVector), the pointer gets invalid!!!
|
64 | If the container memory is relocated (QVector), the pointer gets invalid!!!
|
60 | 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)! */
|
61 | mRUSpecies[i].setup(s,this); // setup this element |
66 | mRUSpecies[i].setup(s,this); // setup this element |
62 | 67 | ||
63 | }
|
68 | }
|
64 | }
|
69 | }
|
65 | 70 | ||
66 | ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species) |
71 | ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species) |
67 | {
|
72 | {
|
68 | return mRUSpecies[species->index()]; |
73 | return mRUSpecies[species->index()]; |
69 | }
|
74 | }
|
70 | 75 | ||
71 | Tree &ResourceUnit::newTree() |
76 | Tree &ResourceUnit::newTree() |
72 | {
|
77 | {
|
73 | // start simple: just append to the vector...
|
78 | // start simple: just append to the vector...
|
74 | mTrees.append(Tree()); |
79 | mTrees.append(Tree()); |
75 | return mTrees.back(); |
80 | return mTrees.back(); |
76 | }
|
81 | }
|
77 | int ResourceUnit::newTreeIndex() |
82 | int ResourceUnit::newTreeIndex() |
78 | {
|
83 | {
|
79 | // start simple: just append to the vector...
|
84 | // start simple: just append to the vector...
|
80 | mTrees.append(Tree()); |
85 | mTrees.append(Tree()); |
81 | return mTrees.count()-1; |
86 | return mTrees.count()-1; |
82 | }
|
87 | }
|
83 | 88 | ||
84 | /// remove dead trees from tree list
|
89 | /// remove dead trees from tree list
|
85 | /// reduce size of vector if lots of space is free
|
90 | /// reduce size of vector if lots of space is free
|
86 | /// tests showed that this way of cleanup is very fast,
|
91 | /// tests showed that this way of cleanup is very fast,
|
87 | /// because no memory allocations are performed (simple memmove())
|
92 | /// because no memory allocations are performed (simple memmove())
|
88 | /// when trees are moved.
|
93 | /// when trees are moved.
|
89 | void ResourceUnit::cleanTreeList() |
94 | void ResourceUnit::cleanTreeList() |
90 | {
|
95 | {
|
91 | QVector<Tree>::iterator last=mTrees.end()-1; |
96 | QVector<Tree>::iterator last=mTrees.end()-1; |
92 | QVector<Tree>::iterator current = mTrees.begin(); |
97 | QVector<Tree>::iterator current = mTrees.begin(); |
93 | while (last>=current && (*last).isDead()) |
98 | while (last>=current && (*last).isDead()) |
94 | --last; |
99 | --last; |
95 | 100 | ||
96 | while (current<last) { |
101 | while (current<last) { |
97 | if ((*current).isDead()) { |
102 | if ((*current).isDead()) { |
98 | *current = *last; // copy data! |
103 | *current = *last; // copy data! |
99 | --last; // |
104 | --last; // |
100 | while (last>=current && (*last).isDead()) |
105 | while (last>=current && (*last).isDead()) |
101 | --last; |
106 | --last; |
102 | }
|
107 | }
|
103 | ++current; |
108 | ++current; |
104 | }
|
109 | }
|
105 | ++last; // last points now to the first dead tree |
110 | ++last; // last points now to the first dead tree |
106 | 111 | ||
107 | // free ressources
|
112 | // free ressources
|
108 | if (last!=mTrees.end()) { |
113 | if (last!=mTrees.end()) { |
109 | mTrees.erase(last, mTrees.end()); |
114 | mTrees.erase(last, mTrees.end()); |
110 | if (mTrees.capacity()>100) { |
115 | if (mTrees.capacity()>100) { |
111 | if (mTrees.count() / double(mTrees.capacity()) < 0.2) { |
116 | if (mTrees.count() / double(mTrees.capacity()) < 0.2) { |
112 | int target_size = mTrees.count()*2; |
117 | int target_size = mTrees.count()*2; |
113 | qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size; |
118 | qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size; |
114 | mTrees.reserve(qMax(target_size, 100)); |
119 | mTrees.reserve(qMax(target_size, 100)); |
115 | }
|
120 | }
|
116 | }
|
121 | }
|
117 | }
|
122 | }
|
118 | }
|
123 | }
|
119 | 124 | ||
120 | void ResourceUnit::newYear() |
125 | void ResourceUnit::newYear() |
121 | {
|
126 | {
|
122 | mAggregatedWLA = 0.; |
127 | mAggregatedWLA = 0.; |
123 | mAggregatedLA = 0.; |
128 | mAggregatedLA = 0.; |
124 | mAggregatedLR = 0.; |
129 | mAggregatedLR = 0.; |
125 | mEffectiveArea = 0.; |
130 | mEffectiveArea = 0.; |
126 | mPixelCount = mStockedPixelCount = 0; |
131 | mPixelCount = mStockedPixelCount = 0; |
127 | // clear statistics global and per species...
|
132 | // clear statistics global and per species...
|
128 | ResourceUnitSpecies *i; |
133 | ResourceUnitSpecies *i; |
129 | QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end(); |
134 | QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end(); |
130 | mStatistics.clear(); |
135 | mStatistics.clear(); |
131 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
136 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
132 | i->statisticsDead().clear(); |
137 | i->statisticsDead().clear(); |
133 | i->statisticsMgmt().clear(); |
138 | i->statisticsMgmt().clear(); |
134 | }
|
139 | }
|
135 | 140 | ||
136 | }
|
141 | }
|
137 | 142 | ||
138 | /** production() is the "stand-level" part of the biomass production (3PG).
|
143 | /** production() is the "stand-level" part of the biomass production (3PG).
|
139 | - The amount of radiation intercepted by the stand is calculated
|
144 | - The amount of radiation intercepted by the stand is calculated
|
140 | - the water cycle is calculated
|
145 | - the water cycle is calculated
|
141 | - statistics for each species are cleared
|
146 | - statistics for each species are cleared
|
142 | - 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)
|
143 | see also: http://iland.boku.ac.at/individual+tree+light+availability */
|
148 | see also: http://iland.boku.ac.at/individual+tree+light+availability */
|
144 | void ResourceUnit::production() |
149 | void ResourceUnit::production() |
145 | {
|
150 | {
|
146 | 151 | ||
147 | if (mAggregatedWLA==0 || mPixelCount==0) { |
152 | if (mAggregatedWLA==0 || mPixelCount==0) { |
148 | // nothing to do...
|
153 | // nothing to do...
|
149 | return; |
154 | return; |
150 | }
|
155 | }
|
151 | 156 | ||
152 | // the pixel counters are filled during the height-grid-calculations
|
157 | // the pixel counters are filled during the height-grid-calculations
|
153 | mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m) |
158 | mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m) |
154 | 159 | ||
155 | // calculate the leaf area index (LAI)
|
160 | // calculate the leaf area index (LAI)
|
156 | double LAI = mAggregatedLA / mStockedArea; |
161 | double LAI = mAggregatedLA / mStockedArea; |
157 | // calculate the intercepted radiation fraction using the law of Beer Lambert
|
162 | // calculate the intercepted radiation fraction using the law of Beer Lambert
|
158 | const double k = Model::settings().lightExtinctionCoefficient; |
163 | const double k = Model::settings().lightExtinctionCoefficient; |
159 | double interception_fraction = 1. - exp(-k * LAI); |
164 | double interception_fraction = 1. - exp(-k * LAI); |
160 | mEffectiveArea = mStockedArea * interception_fraction; // m2 |
165 | mEffectiveArea = mStockedArea * interception_fraction; // m2 |
161 | 166 | ||
162 | // calculate the total weighted leaf area on this RU:
|
167 | // calculate the total weighted leaf area on this RU:
|
163 | mLRI_modification = interception_fraction * mStockedArea / mAggregatedWLA; |
168 | mLRI_modification = interception_fraction * mStockedArea / mAggregatedWLA; |
164 | if (mLRI_modification == 0.) |
169 | if (mLRI_modification == 0.) |
165 | qDebug() << "lri modifaction==0!"; |
170 | qDebug() << "lri modifaction==0!"; |
166 | 171 | ||
167 | 172 | ||
168 | 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") |
169 | .arg(LAI) |
174 | .arg(LAI) |
170 | .arg(interception_fraction) |
175 | .arg(interception_fraction) |
171 | .arg(mLRI_modification) |
176 | .arg(mLRI_modification) |
172 | .arg(mStockedArea); |
177 | .arg(mStockedArea); |
173 | ); |
178 | ); |
174 | 179 | ||
175 | // calculate LAI fractions
|
180 | // calculate LAI fractions
|
176 | ResourceUnitSpecies *i; |
181 | ResourceUnitSpecies *i; |
177 | QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end(); |
182 | QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end(); |
178 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
183 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
179 | i->setLAIfactor(i->statistics().leafAreaIndex() / leafAreaIndex()); |
184 | i->setLAIfactor(i->statistics().leafAreaIndex() / leafAreaIndex()); |
180 | }
|
185 | }
|
181 | 186 | ||
182 | // 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
|
183 | {
|
188 | {
|
184 | DebugTimer tw("water:run"); |
189 | DebugTimer tw("water:run"); |
185 | mWater->run(); |
190 | mWater->run(); |
186 | }
|
191 | }
|
187 | 192 | ||
188 | // invoke species specific calculation (3PG)
|
193 | // invoke species specific calculation (3PG)
|
189 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
194 | for (i=mRUSpecies.begin(); i!=iend; ++i) { |
190 | i->calculate(); |
195 | i->calculate(); |
191 | if (logLevelInfo() && i->LAIfactor()>0) |
196 | if (logLevelInfo() && i->LAIfactor()>0) |
192 | 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" |
193 | << i->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:" |
198 | << i->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:" |
194 | << productiveArea()*i->prod3PG().GPPperArea() |
199 | << productiveArea()*i->prod3PG().GPPperArea() |
195 | << "aging(lastyear):" << averageAging() << "f_env,yr:" << i->prod3PG().fEnvYear(); |
200 | << "aging(lastyear):" << averageAging() << "f_env,yr:" << i->prod3PG().fEnvYear(); |
196 | }
|
201 | }
|
197 | }
|
202 | }
|
198 | 203 | ||
199 | void ResourceUnit::calculateInterceptedArea() |
204 | void ResourceUnit::calculateInterceptedArea() |
200 | {
|
205 | {
|
201 | if (mAggregatedLR==0) { |
206 | if (mAggregatedLR==0) { |
202 | mEffectiveArea_perWLA = 0.; |
207 | mEffectiveArea_perWLA = 0.; |
203 | return; |
208 | return; |
204 | }
|
209 | }
|
205 | Q_ASSERT(mAggregatedLR>0.); |
210 | Q_ASSERT(mAggregatedLR>0.); |
206 | mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR; |
211 | mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR; |
207 | 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; |
208 | }
|
213 | }
|
209 | 214 | ||
210 | // function is called immediately before the growth of individuals
|
215 | // function is called immediately before the growth of individuals
|
211 | void ResourceUnit::beforeGrow() |
216 | void ResourceUnit::beforeGrow() |
212 | {
|
217 | {
|
213 | mAverageAging = 0.; |
218 | mAverageAging = 0.; |
214 | }
|
219 | }
|
215 | 220 | ||
216 | // function is called after finishing the indivdual growth / mortality.
|
221 | // function is called after finishing the indivdual growth / mortality.
|
217 | void ResourceUnit::afterGrow() |
222 | void ResourceUnit::afterGrow() |
218 | {
|
223 | {
|
219 | 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) |
220 | if (mAverageAging>0. && mAverageAging<0.00001) |
225 | if (mAverageAging>0. && mAverageAging<0.00001) |
221 | qDebug() << "ru" << mIndex << "aging <0.00001"; |
226 | qDebug() << "ru" << mIndex << "aging <0.00001"; |
222 | }
|
227 | }
|
223 | 228 | ||
224 | void ResourceUnit::yearEnd() |
229 | void ResourceUnit::yearEnd() |
225 | {
|
230 | {
|
226 | // calculate statistics for all tree species of the ressource unit
|
231 | // calculate statistics for all tree species of the ressource unit
|
227 | int c = mRUSpecies.count(); |
232 | int c = mRUSpecies.count(); |
228 | for (int i=0;i<c; i++) { |
233 | for (int i=0;i<c; i++) { |
229 | mRUSpecies[i].statisticsDead().calculate(); // calculate the dead trees |
234 | mRUSpecies[i].statisticsDead().calculate(); // calculate the dead trees |
230 | mRUSpecies[i].statisticsMgmt().calculate(); // stats of removed trees |
235 | mRUSpecies[i].statisticsMgmt().calculate(); // stats of removed trees |
231 | mRUSpecies[i].updateGWL(); // get sum of dead trees (died + removed) |
236 | mRUSpecies[i].updateGWL(); // get sum of dead trees (died + removed) |
232 | 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) |
233 | mStatistics.add(mRUSpecies[i].statistics()); |
238 | mStatistics.add(mRUSpecies[i].statistics()); |
234 | }
|
239 | }
|
235 | mStatistics.calculate(); // aggreagte on stand level |
240 | mStatistics.calculate(); // aggreagte on stand level |
236 | }
|
241 | }
|
237 | 242 | ||
238 | /// refresh of tree based statistics.
|
243 | /// refresh of tree based statistics.
|
239 | void ResourceUnit::createStandStatistics() |
244 | void ResourceUnit::createStandStatistics() |
240 | {
|
245 | {
|
241 | // clear statistics (ru-level and ru-species level)
|
246 | // clear statistics (ru-level and ru-species level)
|
242 | mStatistics.clear(); |
247 | mStatistics.clear(); |
243 | for (int i=0;i<mRUSpecies.count();i++) { |
248 | for (int i=0;i<mRUSpecies.count();i++) { |
244 | mRUSpecies[i].statistics().clear(); |
249 | mRUSpecies[i].statistics().clear(); |
245 | mRUSpecies[i].statisticsDead().clear(); |
250 | mRUSpecies[i].statisticsDead().clear(); |
246 | mRUSpecies[i].statisticsMgmt().clear(); |
251 | mRUSpecies[i].statisticsMgmt().clear(); |
247 | }
|
252 | }
|
248 | 253 | ||
249 | // add all trees to the statistics objects of the species
|
254 | // add all trees to the statistics objects of the species
|
250 | foreach(const Tree &t, mTrees) { |
255 | foreach(const Tree &t, mTrees) { |
251 | if (!t.isDead()) |
256 | if (!t.isDead()) |
252 | resourceUnitSpecies(t.species()).statistics().add(&t, 0); |
257 | resourceUnitSpecies(t.species()).statistics().add(&t, 0); |
253 | }
|
258 | }
|
254 | // summarize statistics for the whole resource unit
|
259 | // summarize statistics for the whole resource unit
|
255 | for (int i=0;i<mRUSpecies.count();i++) { |
260 | for (int i=0;i<mRUSpecies.count();i++) { |
256 | mRUSpecies[i].statistics().calculate(); |
261 | mRUSpecies[i].statistics().calculate(); |
257 | mStatistics.add(mRUSpecies[i].statistics()); |
262 | mStatistics.add(mRUSpecies[i].statistics()); |
258 | }
|
263 | }
|
259 | mStatistics.calculate(); |
264 | mStatistics.calculate(); |
260 | mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*area()):0.; |
265 | mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*area()):0.; |
261 | }
|
266 | }
|
262 | 267 |