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