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