Rev 1068 | Rev 1085 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1068 | Rev 1071 | ||
---|---|---|---|
1 | Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/model.cpp': |
1 | Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/model.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 Model
|
21 | /** @class Model
|
22 | Main object of the iLand model composited of various sub models / sub components.
|
22 | Main object of the iLand model composited of various sub models / sub components.
|
23 | @ingroup core
|
23 | @ingroup core
|
24 | The class Model is the top level container of iLand. The Model holds a collection of ResourceUnits, links to SpeciesSet and Climate.
|
24 | The class Model is the top level container of iLand. The Model holds a collection of ResourceUnits, links to SpeciesSet and Climate.
|
25 | ResourceUnit are grid cells with (currently) a size of 1 ha (100x100m). Many stand level processes (NPP produciton, WaterCycle) operate on this
|
25 | ResourceUnit are grid cells with (currently) a size of 1 ha (100x100m). Many stand level processes (NPP produciton, WaterCycle) operate on this
|
26 | level.
|
26 | level.
|
27 | The Model also contain the landscape-wide 2m LIF-grid (http://iland.boku.ac.at/competition+for+light).
|
27 | The Model also contain the landscape-wide 2m LIF-grid (http://iland.boku.ac.at/competition+for+light).
|
28 | 28 | ||
29 | */
|
29 | */
|
30 | #include "global.h"
|
30 | #include "global.h"
|
31 | #include "model.h"
|
31 | #include "model.h"
|
32 | #include "sqlhelper.h"
|
32 | #include "sqlhelper.h"
|
33 | 33 | ||
34 | #include "xmlhelper.h"
|
34 | #include "xmlhelper.h"
|
35 | #include "debugtimer.h"
|
35 | #include "debugtimer.h"
|
36 | #include "environment.h"
|
36 | #include "environment.h"
|
37 | #include "timeevents.h"
|
37 | #include "timeevents.h"
|
38 | #include "helper.h"
|
38 | #include "helper.h"
|
39 | #include "resourceunit.h"
|
39 | #include "resourceunit.h"
|
40 | #include "climate.h"
|
40 | #include "climate.h"
|
41 | #include "speciesset.h"
|
41 | #include "speciesset.h"
|
42 | #include "standloader.h"
|
42 | #include "standloader.h"
|
43 | #include "tree.h"
|
43 | #include "tree.h"
|
44 | #include "management.h"
|
44 | #include "management.h"
|
45 | #include "modelsettings.h"
|
45 | #include "modelsettings.h"
|
46 | #include "standstatistics.h"
|
46 | #include "standstatistics.h"
|
47 | #include "mapgrid.h"
|
47 | #include "mapgrid.h"
|
48 | #include "modelcontroller.h"
|
48 | #include "modelcontroller.h"
|
49 | #include "modules.h"
|
49 | #include "modules.h"
|
50 | #include "dem.h"
|
50 | #include "dem.h"
|
51 | #include "grasscover.h"
|
51 | #include "grasscover.h"
|
52 | 52 | ||
53 | #include "outputmanager.h"
|
53 | #include "outputmanager.h"
|
54 | 54 | ||
55 | #include "forestmanagementengine.h"
|
55 | #include "forestmanagementengine.h"
|
56 | 56 | ||
57 | #include <QtCore>
|
57 | #include <QtCore>
|
58 | #include <QtXml>
|
58 | #include <QtXml>
|
59 | 59 | ||
60 | /** iterate over all trees of the model. return NULL if all trees processed.
|
60 | /** iterate over all trees of the model. return NULL if all trees processed.
|
61 | Usage:
|
61 | Usage:
|
62 | @code
|
62 | @code
|
63 | AllTreeIterator trees(model);
|
63 | AllTreeIterator trees(model);
|
64 | while (Tree *tree = trees.next()) { // returns NULL when finished.
|
64 | while (Tree *tree = trees.next()) { // returns NULL when finished.
|
65 | tree->something(); // do something
|
65 | tree->something(); // do something
|
66 | }
|
66 | }
|
67 | @endcode */
|
67 | @endcode */
|
68 | Tree *AllTreeIterator::next() |
68 | Tree *AllTreeIterator::next() |
69 | {
|
69 | {
|
70 | 70 | ||
71 | if (!mTreeEnd) { |
71 | if (!mTreeEnd) { |
72 | // initialize to first ressource unit
|
72 | // initialize to first ressource unit
|
73 | mRUIterator = mModel->ruList().constBegin(); |
73 | mRUIterator = mModel->ruList().constBegin(); |
74 | // fast forward to the first RU with trees
|
74 | // fast forward to the first RU with trees
|
75 | while (mRUIterator!=mModel->ruList().constEnd()) { |
75 | while (mRUIterator!=mModel->ruList().constEnd()) { |
76 | if ((*mRUIterator)->trees().count()>0) |
76 | if ((*mRUIterator)->trees().count()>0) |
77 | break; |
77 | break; |
78 | ++mRUIterator; |
78 | ++mRUIterator; |
79 | }
|
79 | }
|
80 | // finished if all RU processed
|
80 | // finished if all RU processed
|
81 | if (mRUIterator == mModel->ruList().constEnd()) |
81 | if (mRUIterator == mModel->ruList().constEnd()) |
82 | return NULL; |
82 | return NULL; |
83 | mTreeEnd = &((*mRUIterator)->trees().back()) + 1; // let end point to "1 after end" (STL-style) |
83 | mTreeEnd = &((*mRUIterator)->trees().back()) + 1; // let end point to "1 after end" (STL-style) |
84 | mCurrent = &((*mRUIterator)->trees().front()); |
84 | mCurrent = &((*mRUIterator)->trees().front()); |
85 | }
|
85 | }
|
86 | if (mCurrent==mTreeEnd) { |
86 | if (mCurrent==mTreeEnd) { |
87 | ++mRUIterator; // switch to next RU (loop until RU with trees is found) |
87 | ++mRUIterator; // switch to next RU (loop until RU with trees is found) |
88 | while (mRUIterator!=mModel->ruList().constEnd()) { |
88 | while (mRUIterator!=mModel->ruList().constEnd()) { |
89 | if ((*mRUIterator)->trees().count()>0) { |
89 | if ((*mRUIterator)->trees().count()>0) { |
90 | break; |
90 | break; |
91 | }
|
91 | }
|
92 | ++mRUIterator; |
92 | ++mRUIterator; |
93 | }
|
93 | }
|
94 | if (mRUIterator == mModel->ruList().constEnd()) { |
94 | if (mRUIterator == mModel->ruList().constEnd()) { |
95 | mCurrent = NULL; |
95 | mCurrent = NULL; |
96 | return NULL; // finished!! |
96 | return NULL; // finished!! |
97 | }else { |
97 | }else { |
98 | mTreeEnd = &((*mRUIterator)->trees().back()) + 1; |
98 | mTreeEnd = &((*mRUIterator)->trees().back()) + 1; |
99 | mCurrent = &((*mRUIterator)->trees().front()); |
99 | mCurrent = &((*mRUIterator)->trees().front()); |
100 | }
|
100 | }
|
101 | }
|
101 | }
|
102 | 102 | ||
103 | return mCurrent++; |
103 | return mCurrent++; |
104 | }
|
104 | }
|
105 | Tree *AllTreeIterator::nextLiving() |
105 | Tree *AllTreeIterator::nextLiving() |
106 | {
|
106 | {
|
107 | while (Tree *t = next()) |
107 | while (Tree *t = next()) |
108 | if (!t->isDead()) return t; |
108 | if (!t->isDead()) return t; |
109 | return NULL; |
109 | return NULL; |
110 | }
|
110 | }
|
111 | Tree *AllTreeIterator::current() const |
111 | Tree *AllTreeIterator::current() const |
112 | {
|
112 | {
|
113 | return mCurrent?mCurrent-1:NULL; |
113 | return mCurrent?mCurrent-1:NULL; |
114 | }
|
114 | }
|
115 | 115 | ||
116 | 116 | ||
117 | ModelSettings Model::mSettings; |
117 | ModelSettings Model::mSettings; |
118 | Model::Model() |
118 | Model::Model() |
119 | {
|
119 | {
|
120 | initialize(); |
120 | initialize(); |
121 | GlobalSettings::instance()->setModel(this); |
121 | GlobalSettings::instance()->setModel(this); |
122 | GlobalSettings::instance()->resetScriptEngine(); // clear the script |
122 | GlobalSettings::instance()->resetScriptEngine(); // clear the script |
123 | QString dbg="running in release mode."; |
123 | QString dbg="running in release mode."; |
124 | DBGMODE( dbg="running in debug mode."; ); |
124 | DBGMODE( dbg="running in debug mode."; ); |
125 | qDebug() << dbg; |
125 | qDebug() << dbg; |
126 | }
|
126 | }
|
127 | 127 | ||
128 | Model::~Model() |
128 | Model::~Model() |
129 | {
|
129 | {
|
130 | clear(); |
130 | clear(); |
131 | GlobalSettings::instance()->setModel(NULL); |
131 | GlobalSettings::instance()->setModel(NULL); |
132 | }
|
132 | }
|
133 | 133 | ||
134 | /** Initial setup of the Model.
|
134 | /** Initial setup of the Model.
|
135 | */
|
135 | */
|
136 | void Model::initialize() |
136 | void Model::initialize() |
137 | {
|
137 | {
|
138 | mSetup = false; |
138 | mSetup = false; |
139 | GlobalSettings::instance()->setCurrentYear(0); |
139 | GlobalSettings::instance()->setCurrentYear(0); |
140 | mGrid = 0; |
140 | mGrid = 0; |
141 | mHeightGrid = 0; |
141 | mHeightGrid = 0; |
142 | mManagement = 0; |
142 | mManagement = 0; |
143 | mABEManagement = 0; |
143 | mABEManagement = 0; |
144 | mEnvironment = 0; |
144 | mEnvironment = 0; |
145 | mTimeEvents = 0; |
145 | mTimeEvents = 0; |
146 | mStandGrid = 0; |
146 | mStandGrid = 0; |
147 | mModules = 0; |
147 | mModules = 0; |
148 | mDEM = 0; |
148 | mDEM = 0; |
149 | mGrassCover = 0; |
149 | mGrassCover = 0; |
150 | }
|
150 | }
|
151 | 151 | ||
152 | /** sets up the simulation space.
|
152 | /** sets up the simulation space.
|
153 | */
|
153 | */
|
154 | void Model::setupSpace() |
154 | void Model::setupSpace() |
155 | {
|
155 | {
|
156 | XmlHelper xml(GlobalSettings::instance()->settings().node("model.world")); |
156 | XmlHelper xml(GlobalSettings::instance()->settings().node("model.world")); |
157 | double cellSize = xml.value("cellSize", "2").toDouble(); |
157 | double cellSize = xml.value("cellSize", "2").toDouble(); |
158 | double width = xml.value("width", "100").toDouble(); |
158 | double width = xml.value("width", "100").toDouble(); |
159 | double height = xml.value("height", "100").toDouble(); |
159 | double height = xml.value("height", "100").toDouble(); |
160 | double buffer = xml.value("buffer", "60").toDouble(); |
160 | double buffer = xml.value("buffer", "60").toDouble(); |
161 | mModelRect = QRectF(0., 0., width, height); |
161 | mModelRect = QRectF(0., 0., width, height); |
162 | 162 | ||
163 | qDebug() << QString("setup of the world: %1x%2m with cell-size=%3m and %4m buffer").arg(width).arg(height).arg(cellSize).arg(buffer); |
163 | qDebug() << QString("setup of the world: %1x%2m with cell-size=%3m and %4m buffer").arg(width).arg(height).arg(cellSize).arg(buffer); |
164 | 164 | ||
165 | QRectF total_grid(QPointF(-buffer, -buffer), QPointF(width+buffer, height+buffer)); |
165 | QRectF total_grid(QPointF(-buffer, -buffer), QPointF(width+buffer, height+buffer)); |
166 | qDebug() << "setup grid rectangle:" << total_grid; |
166 | qDebug() << "setup grid rectangle:" << total_grid; |
167 | 167 | ||
168 | if (mGrid) |
168 | if (mGrid) |
169 | delete mGrid; |
169 | delete mGrid; |
170 | mGrid = new FloatGrid(total_grid, cellSize); |
170 | mGrid = new FloatGrid(total_grid, cellSize); |
171 | mGrid->initialize(1.f); |
171 | mGrid->initialize(1.f); |
172 | if (mHeightGrid) |
172 | if (mHeightGrid) |
173 | delete mHeightGrid; |
173 | delete mHeightGrid; |
174 | mHeightGrid = new HeightGrid(total_grid, cellSize*5); |
174 | mHeightGrid = new HeightGrid(total_grid, cellSize*5); |
175 | mHeightGrid->wipe(); // set all to zero |
175 | mHeightGrid->wipe(); // set all to zero |
176 | Tree::setGrid(mGrid, mHeightGrid); |
176 | Tree::setGrid(mGrid, mHeightGrid); |
177 | 177 | ||
178 | // setup the spatial location of the project area
|
178 | // setup the spatial location of the project area
|
179 | if (xml.hasNode("location")) { |
179 | if (xml.hasNode("location")) { |
180 | // setup of spatial location
|
180 | // setup of spatial location
|
181 | double loc_x = xml.valueDouble("location.x"); |
181 | double loc_x = xml.valueDouble("location.x"); |
182 | double loc_y = xml.valueDouble("location.y"); |
182 | double loc_y = xml.valueDouble("location.y"); |
183 | double loc_z = xml.valueDouble("location.z"); |
183 | double loc_z = xml.valueDouble("location.z"); |
184 | double loc_rot = xml.valueDouble("location.rotation"); |
184 | double loc_rot = xml.valueDouble("location.rotation"); |
185 | setupGISTransformation(loc_x, loc_y, loc_z, loc_rot); |
185 | setupGISTransformation(loc_x, loc_y, loc_z, loc_rot); |
186 | qDebug() << "setup of spatial location: x/y/z" << loc_x << loc_y << loc_z << "rotation:" << loc_rot; |
186 | qDebug() << "setup of spatial location: x/y/z" << loc_x << loc_y << loc_z << "rotation:" << loc_rot; |
187 | } else { |
187 | } else { |
188 | setupGISTransformation(0., 0., 0., 0.); |
188 | setupGISTransformation(0., 0., 0., 0.); |
189 | }
|
189 | }
|
190 | 190 | ||
191 | // load environment (multiple climates, speciesSets, ...
|
191 | // load environment (multiple climates, speciesSets, ...
|
192 | if (mEnvironment) |
192 | if (mEnvironment) |
193 | delete mEnvironment; |
193 | delete mEnvironment; |
194 | mEnvironment = new Environment(); |
194 | mEnvironment = new Environment(); |
195 | 195 | ||
196 | if (xml.valueBool("environmentEnabled", false)) { |
196 | if (xml.valueBool("environmentEnabled", false)) { |
197 | QString env_file = GlobalSettings::instance()->path(xml.value("environmentFile")); |
197 | QString env_file = GlobalSettings::instance()->path(xml.value("environmentFile")); |
198 | bool grid_mode = (xml.value("environmentMode")=="grid"); |
198 | bool grid_mode = (xml.value("environmentMode")=="grid"); |
199 | QString grid_file = GlobalSettings::instance()->path(xml.value("environmentGrid")); |
199 | QString grid_file = GlobalSettings::instance()->path(xml.value("environmentGrid")); |
200 | if (grid_mode) { |
200 | if (grid_mode) { |
201 | if (QFile::exists(grid_file)) |
201 | if (QFile::exists(grid_file)) |
202 | mEnvironment->setGridMode(grid_file); |
202 | mEnvironment->setGridMode(grid_file); |
203 | else
|
203 | else
|
204 | throw IException(QString("File '%1' specified in key 'environmentGrid' does not exit ('environmentMode' is 'grid').").arg(grid_file) ); |
204 | throw IException(QString("File '%1' specified in key 'environmentGrid' does not exit ('environmentMode' is 'grid').").arg(grid_file) ); |
205 | }
|
205 | }
|
206 | 206 | ||
207 | if (!mEnvironment->loadFromFile(env_file)) |
207 | if (!mEnvironment->loadFromFile(env_file)) |
208 | return; |
208 | return; |
209 | } else { |
209 | } else { |
210 | // load and prepare default values
|
210 | // load and prepare default values
|
211 | // (2) SpeciesSets: currently only one a global species set.
|
211 | // (2) SpeciesSets: currently only one a global species set.
|
212 | SpeciesSet *speciesSet = new SpeciesSet(); |
212 | SpeciesSet *speciesSet = new SpeciesSet(); |
213 | mSpeciesSets.push_back(speciesSet); |
213 | mSpeciesSets.push_back(speciesSet); |
214 | speciesSet->setup(); |
214 | speciesSet->setup(); |
215 | // Climate...
|
215 | // Climate...
|
216 | Climate *c = new Climate(); |
216 | Climate *c = new Climate(); |
217 | mClimates.push_back(c); |
217 | mClimates.push_back(c); |
218 | mEnvironment->setDefaultValues(c, speciesSet); |
218 | mEnvironment->setDefaultValues(c, speciesSet); |
219 | } // environment? |
219 | } // environment? |
220 | 220 | ||
221 | // time series data
|
221 | // time series data
|
222 | if (xml.valueBool(".timeEventsEnabled", false)) { |
222 | if (xml.valueBool(".timeEventsEnabled", false)) { |
223 | mTimeEvents = new TimeEvents(); |
223 | mTimeEvents = new TimeEvents(); |
224 | mTimeEvents->loadFromFile(GlobalSettings::instance()->path(xml.value("timeEventsFile"), "script")); |
224 | mTimeEvents->loadFromFile(GlobalSettings::instance()->path(xml.value("timeEventsFile"), "script")); |
225 | }
|
225 | }
|
226 | 226 | ||
227 | 227 | ||
228 | // simple case: create ressource units in a regular grid.
|
228 | // simple case: create ressource units in a regular grid.
|
229 | if (xml.valueBool("resourceUnitsAsGrid")) { |
229 | if (xml.valueBool("resourceUnitsAsGrid")) { |
230 | 230 | ||
231 | mRUmap.setup(QRectF(0., 0., width, height),100.); // Grid, that holds positions of resource units |
231 | mRUmap.setup(QRectF(0., 0., width, height),100.); // Grid, that holds positions of resource units |
232 | mRUmap.wipe(); |
232 | mRUmap.wipe(); |
233 | 233 | ||
234 | bool mask_is_setup = false; |
234 | bool mask_is_setup = false; |
235 | if (xml.valueBool("standGrid.enabled")) { |
235 | if (xml.valueBool("standGrid.enabled")) { |
236 | QString fileName = GlobalSettings::instance()->path(xml.value("standGrid.fileName")); |
236 | QString fileName = GlobalSettings::instance()->path(xml.value("standGrid.fileName")); |
237 | mStandGrid = new MapGrid(fileName,false); // create stand grid index later |
237 | mStandGrid = new MapGrid(fileName,false); // create stand grid index later |
238 | 238 | ||
239 | if (mStandGrid->isValid()) { |
239 | if (mStandGrid->isValid()) { |
240 | for (int i=0;i<mStandGrid->grid().count();i++) { |
240 | for (int i=0;i<mStandGrid->grid().count();i++) { |
241 | const int &grid_value = mStandGrid->grid().constValueAtIndex(i); |
241 | const int &grid_value = mStandGrid->grid().constValueAtIndex(i); |
242 | mHeightGrid->valueAtIndex(i).setValid( grid_value > -1 ); |
242 | mHeightGrid->valueAtIndex(i).setValid( grid_value > -1 ); |
243 | if (grid_value>-1) |
243 | if (grid_value>-1) |
244 | mRUmap.valueAt(mStandGrid->grid().cellCenterPoint(i)) = (ResourceUnit*)1; |
244 | mRUmap.valueAt(mStandGrid->grid().cellCenterPoint(i)) = (ResourceUnit*)1; |
245 | if (grid_value < -1) |
245 | if (grid_value < -1) |
246 | mHeightGrid->valueAtIndex(i).setForestOutside(true); |
246 | mHeightGrid->valueAtIndex(i).setForestOutside(true); |
247 | }
|
247 | }
|
248 | }
|
248 | }
|
249 | mask_is_setup = true; |
249 | mask_is_setup = true; |
250 | } else { |
250 | } else { |
251 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
251 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
252 | // in the case we have no stand grid but only a large rectangle (without the torus option)
|
252 | // in the case we have no stand grid but only a large rectangle (without the torus option)
|
253 | // we assume a forest outside
|
253 | // we assume a forest outside
|
254 | for (int i=0;i<mHeightGrid->count();++i) { |
254 | for (int i=0;i<mHeightGrid->count();++i) { |
255 | const QPointF &p = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i)); |
255 | const QPointF &p = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i)); |
256 | if (p.x() < 0. || p.x()>width || p.y()<0. || p.y()>height) { |
256 | if (p.x() < 0. || p.x()>width || p.y()<0. || p.y()>height) { |
257 | mHeightGrid->valueAtIndex(i).setForestOutside(true); |
257 | mHeightGrid->valueAtIndex(i).setForestOutside(true); |
258 | mHeightGrid->valueAtIndex(i).setValid(false); |
258 | mHeightGrid->valueAtIndex(i).setValid(false); |
259 | }
|
259 | }
|
260 | }
|
260 | }
|
261 | 261 | ||
262 | }
|
262 | }
|
263 | }
|
263 | }
|
264 | 264 | ||
265 | ResourceUnit **p; // ptr to ptr! |
265 | ResourceUnit **p; // ptr to ptr! |
266 | ResourceUnit *new_ru; |
266 | ResourceUnit *new_ru; |
267 | 267 | ||
268 | int ru_index = 0; |
268 | int ru_index = 0; |
269 | for (p=mRUmap.begin(); p!=mRUmap.end(); ++p) { |
269 | for (p=mRUmap.begin(); p!=mRUmap.end(); ++p) { |
270 | QRectF r = mRUmap.cellRect(mRUmap.indexOf(p)); |
270 | QRectF r = mRUmap.cellRect(mRUmap.indexOf(p)); |
271 | if (!mStandGrid || !mStandGrid->isValid() || *p>NULL) { |
271 | if (!mStandGrid || !mStandGrid->isValid() || *p>NULL) { |
272 | mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used. |
272 | mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used. |
273 | // create resource units for valid positions only
|
273 | // create resource units for valid positions only
|
274 | new_ru = new ResourceUnit(ru_index++); // create resource unit |
274 | new_ru = new ResourceUnit(ru_index++); // create resource unit |
275 | new_ru->setClimate( mEnvironment->climate() ); |
275 | new_ru->setClimate( mEnvironment->climate() ); |
276 | new_ru->setSpeciesSet( mEnvironment->speciesSet() ); |
276 | new_ru->setSpeciesSet( mEnvironment->speciesSet() ); |
277 | new_ru->setup(); |
277 | new_ru->setup(); |
278 | new_ru->setID( mEnvironment->currentID() ); // set id of resource unit in grid mode |
278 | new_ru->setID( mEnvironment->currentID() ); // set id of resource unit in grid mode |
279 | new_ru->setBoundingBox(r); |
279 | new_ru->setBoundingBox(r); |
280 | mRU.append(new_ru); |
280 | mRU.append(new_ru); |
281 | *p = new_ru; // save in the RUmap grid |
281 | *p = new_ru; // save in the RUmap grid |
282 | }
|
282 | }
|
283 | }
|
283 | }
|
284 | if (mEnvironment) { |
284 | if (mEnvironment) { |
285 | // retrieve species sets and climates (that were really used)
|
285 | // retrieve species sets and climates (that were really used)
|
286 | mSpeciesSets << mEnvironment->speciesSetList(); |
286 | mSpeciesSets << mEnvironment->speciesSetList(); |
287 | mClimates << mEnvironment->climateList(); |
287 | mClimates << mEnvironment->climateList(); |
288 | QString climate_file_list;
|
288 | QString climate_file_list;
|
289 | for (int i=0, c=0;i<mClimates.count();++i) { |
289 | for (int i=0, c=0;i<mClimates.count();++i) { |
290 | climate_file_list += mClimates[i]->name() + ", "; |
290 | climate_file_list += mClimates[i]->name() + ", "; |
291 | if (++c>5) { |
291 | if (++c>5) { |
292 | climate_file_list += "..."; |
292 | climate_file_list += "..."; |
293 | break; |
293 | break; |
294 | }
|
294 | }
|
295 | 295 | ||
296 | }
|
296 | }
|
297 | qDebug() << "Setup of climates: #loaded:" << mClimates.count() << "tables:" << climate_file_list; |
297 | qDebug() << "Setup of climates: #loaded:" << mClimates.count() << "tables:" << climate_file_list; |
298 | 298 | ||
299 | 299 | ||
300 | }
|
300 | }
|
301 | 301 | ||
302 | qDebug() << "setup of" << mEnvironment->climateList().size() << "climates performed."; |
302 | qDebug() << "setup of" << mEnvironment->climateList().size() << "climates performed."; |
303 | 303 | ||
304 | if (mStandGrid && mStandGrid->isValid()) |
304 | if (mStandGrid && mStandGrid->isValid()) |
305 | mStandGrid->createIndex(); |
305 | mStandGrid->createIndex(); |
306 | // now store the pointers in the grid.
|
306 | // now store the pointers in the grid.
|
307 | // Important: This has to be done after the mRU-QList is complete - otherwise pointers would
|
307 | // Important: This has to be done after the mRU-QList is complete - otherwise pointers would
|
308 | // point to invalid memory when QList's memory is reorganized (expanding)
|
308 | // point to invalid memory when QList's memory is reorganized (expanding)
|
309 | // ru_index = 0;
|
309 | // ru_index = 0;
|
310 | // for (p=mRUmap.begin();p!=mRUmap.end(); ++p) {
|
310 | // for (p=mRUmap.begin();p!=mRUmap.end(); ++p) {
|
311 | // *p = mRU.value(ru_index++);
|
311 | // *p = mRU.value(ru_index++);
|
312 | // }
|
312 | // }
|
313 | qDebug() << "created a grid of ResourceUnits: count=" << mRU.count() << "number of RU-map-cells:" << mRUmap.count(); |
313 | qDebug() << "created a grid of ResourceUnits: count=" << mRU.count() << "number of RU-map-cells:" << mRUmap.count(); |
314 | 314 | ||
315 | 315 | ||
316 | calculateStockableArea(); |
316 | calculateStockableArea(); |
317 | 317 | ||
318 | // setup of the project area mask
|
318 | // setup of the project area mask
|
319 | if (!mask_is_setup && xml.valueBool("areaMask.enabled", false) && xml.hasNode("areaMask.imageFile")) { |
319 | if (!mask_is_setup && xml.valueBool("areaMask.enabled", false) && xml.hasNode("areaMask.imageFile")) { |
320 | // to be extended!!! e.g. to load ESRI-style text files....
|
320 | // to be extended!!! e.g. to load ESRI-style text files....
|
321 | // setup a grid with the same size as the height grid...
|
321 | // setup a grid with the same size as the height grid...
|
322 | FloatGrid tempgrid((int)mHeightGrid->cellsize(), mHeightGrid->sizeX(), mHeightGrid->sizeY()); |
322 | FloatGrid tempgrid((int)mHeightGrid->cellsize(), mHeightGrid->sizeX(), mHeightGrid->sizeY()); |
323 | QString fileName = GlobalSettings::instance()->path(xml.value("areaMask.imageFile")); |
323 | QString fileName = GlobalSettings::instance()->path(xml.value("areaMask.imageFile")); |
324 | loadGridFromImage(fileName, tempgrid); // fetch from image |
324 | loadGridFromImage(fileName, tempgrid); // fetch from image |
325 | for (int i=0;i<tempgrid.count(); i++) |
325 | for (int i=0;i<tempgrid.count(); i++) |
326 | mHeightGrid->valueAtIndex(i).setValid( tempgrid.valueAtIndex(i)>0.99 ); |
326 | mHeightGrid->valueAtIndex(i).setValid( tempgrid.valueAtIndex(i)>0.99 ); |
327 | qDebug() << "loaded project area mask from" << fileName; |
327 | qDebug() << "loaded project area mask from" << fileName; |
328 | }
|
328 | }
|
329 | 329 | ||
330 | // list of "valid" resource units
|
330 | // list of "valid" resource units
|
331 | QList<ResourceUnit*> valid_rus; |
331 | QList<ResourceUnit*> valid_rus; |
332 | foreach(ResourceUnit* ru, mRU) |
332 | foreach(ResourceUnit* ru, mRU) |
333 | if (ru->id()!=-1) |
333 | if (ru->id()!=-1) |
334 | valid_rus.append(ru); |
334 | valid_rus.append(ru); |
335 | 335 | ||
336 | // setup of the digital elevation map (if present)
|
336 | // setup of the digital elevation map (if present)
|
337 | QString dem_file = xml.value("DEM"); |
337 | QString dem_file = xml.value("DEM"); |
338 | if (!dem_file.isEmpty()) { |
338 | if (!dem_file.isEmpty()) { |
339 | mDEM = new DEM(GlobalSettings::instance()->path(dem_file)); |
339 | mDEM = new DEM(GlobalSettings::instance()->path(dem_file)); |
340 | // add them to the visuals...
|
340 | // add them to the visuals...
|
341 | GlobalSettings::instance()->controller()->addGrid(mDEM, "DEM height", GridViewRainbow, 0, 1000); |
341 | GlobalSettings::instance()->controller()->addGrid(mDEM, "DEM height", GridViewRainbow, 0, 1000); |
342 | GlobalSettings::instance()->controller()->addGrid(mDEM->slopeGrid(), "DEM slope", GridViewRainbow, 0, 3); |
342 | GlobalSettings::instance()->controller()->addGrid(mDEM->slopeGrid(), "DEM slope", GridViewRainbow, 0, 3); |
343 | GlobalSettings::instance()->controller()->addGrid(mDEM->aspectGrid(), "DEM aspect", GridViewRainbow, 0, 360); |
343 | GlobalSettings::instance()->controller()->addGrid(mDEM->aspectGrid(), "DEM aspect", GridViewRainbow, 0, 360); |
344 | GlobalSettings::instance()->controller()->addGrid(mDEM->viewGrid(), "DEM view", GridViewGray, 0, 1); |
344 | GlobalSettings::instance()->controller()->addGrid(mDEM->viewGrid(), "DEM view", GridViewGray, 0, 1); |
345 | 345 | ||
346 | }
|
346 | }
|
347 | 347 | ||
348 | // setup of the grass cover
|
348 | // setup of the grass cover
|
349 | if (!mGrassCover) |
349 | if (!mGrassCover) |
350 | mGrassCover = new GrassCover(); |
350 | mGrassCover = new GrassCover(); |
351 | mGrassCover->setup(); |
351 | mGrassCover->setup(); |
352 | 352 | ||
353 | // setup of external modules
|
353 | // setup of external modules
|
354 | mModules->setup(); |
354 | mModules->setup(); |
355 | if (mModules->hasSetupResourceUnits()) { |
355 | if (mModules->hasSetupResourceUnits()) { |
356 | for (ResourceUnit **p=mRUmap.begin(); p!=mRUmap.end(); ++p) { |
356 | for (ResourceUnit **p=mRUmap.begin(); p!=mRUmap.end(); ++p) { |
357 | QRectF r = mRUmap.cellRect(mRUmap.indexOf(p)); |
357 | QRectF r = mRUmap.cellRect(mRUmap.indexOf(p)); |
358 | mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used. |
358 | mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used. |
359 | mModules->setupResourceUnit( *p ); |
359 | mModules->setupResourceUnit( *p ); |
360 | }
|
360 | }
|
361 | }
|
361 | }
|
362 | 362 | ||
363 | // setup of scripting environment
|
363 | // setup of scripting environment
|
364 | ScriptGlobal::setupGlobalScripting(); |
364 | ScriptGlobal::setupGlobalScripting(); |
365 | 365 | ||
366 | // setup the helper that does the multithreading
|
366 | // setup the helper that does the multithreading
|
367 | threadRunner.setup(valid_rus); |
367 | threadRunner.setup(valid_rus); |
368 | threadRunner.setMultithreading(GlobalSettings::instance()->settings().valueBool("system.settings.multithreading")); |
368 | threadRunner.setMultithreading(GlobalSettings::instance()->settings().valueBool("system.settings.multithreading")); |
369 | threadRunner.print(); |
369 | threadRunner.print(); |
- | 370 | ||
370 | 371 | ||
371 | } else { |
372 | } else { |
372 | throw IException("resourceUnitsAsGrid MUST be set to true - at least currently :)"); |
373 | throw IException("resourceUnitsAsGrid MUST be set to true - at least currently :)"); |
373 | }
|
374 | }
|
374 | mSetup = true; |
375 | mSetup = true; |
375 | }
|
376 | }
|
376 | 377 | ||
377 | 378 | ||
378 | /** clear() frees all ressources allocated with the run of a simulation.
|
379 | /** clear() frees all ressources allocated with the run of a simulation.
|
379 | 380 | ||
380 | */
|
381 | */
|
381 | void Model::clear() |
382 | void Model::clear() |
382 | {
|
383 | {
|
383 | mSetup = false; |
384 | mSetup = false; |
384 | qDebug() << "Model clear: attempting to clear" << mRU.count() << "RU, " << mSpeciesSets.count() << "SpeciesSets."; |
385 | qDebug() << "Model clear: attempting to clear" << mRU.count() << "RU, " << mSpeciesSets.count() << "SpeciesSets."; |
385 | // clear ressource units
|
386 | // clear ressource units
|
386 | qDeleteAll(mRU); // delete ressource units (and trees) |
387 | qDeleteAll(mRU); // delete ressource units (and trees) |
387 | mRU.clear(); |
388 | mRU.clear(); |
388 | 389 | ||
389 | qDeleteAll(mSpeciesSets); // delete species sets |
390 | qDeleteAll(mSpeciesSets); // delete species sets |
390 | mSpeciesSets.clear(); |
391 | mSpeciesSets.clear(); |
391 | 392 | ||
392 | // delete climate data
|
393 | // delete climate data
|
393 | qDeleteAll(mClimates); |
394 | qDeleteAll(mClimates); |
394 | 395 | ||
395 | // delete the grids
|
396 | // delete the grids
|
396 | if (mGrid) |
397 | if (mGrid) |
397 | delete mGrid; |
398 | delete mGrid; |
398 | if (mHeightGrid) |
399 | if (mHeightGrid) |
399 | delete mHeightGrid; |
400 | delete mHeightGrid; |
400 | if (mManagement) |
401 | if (mManagement) |
401 | delete mManagement; |
402 | delete mManagement; |
402 | if (mEnvironment) |
403 | if (mEnvironment) |
403 | delete mEnvironment; |
404 | delete mEnvironment; |
404 | if (mTimeEvents) |
405 | if (mTimeEvents) |
405 | delete mTimeEvents; |
406 | delete mTimeEvents; |
406 | if (mStandGrid) |
407 | if (mStandGrid) |
407 | delete mStandGrid; |
408 | delete mStandGrid; |
408 | if (mModules) |
409 | if (mModules) |
409 | delete mModules; |
410 | delete mModules; |
410 | if (mDEM) |
411 | if (mDEM) |
411 | delete mDEM; |
412 | delete mDEM; |
412 | if (mGrassCover) |
413 | if (mGrassCover) |
413 | delete mGrassCover; |
414 | delete mGrassCover; |
414 | if (mABEManagement) |
415 | if (mABEManagement) |
415 | delete mABEManagement; |
416 | delete mABEManagement; |
416 | 417 | ||
417 | mGrid = 0; |
418 | mGrid = 0; |
418 | mHeightGrid = 0; |
419 | mHeightGrid = 0; |
419 | mManagement = 0; |
420 | mManagement = 0; |
420 | mEnvironment = 0; |
421 | mEnvironment = 0; |
421 | mTimeEvents = 0; |
422 | mTimeEvents = 0; |
422 | mStandGrid = 0; |
423 | mStandGrid = 0; |
423 | mModules = 0; |
424 | mModules = 0; |
424 | mDEM = 0; |
425 | mDEM = 0; |
425 | mGrassCover = 0; |
426 | mGrassCover = 0; |
426 | mABEManagement = 0; |
427 | mABEManagement = 0; |
427 | 428 | ||
428 | GlobalSettings::instance()->outputManager()->close(); |
429 | GlobalSettings::instance()->outputManager()->close(); |
429 | 430 | ||
430 | qDebug() << "Model ressources freed."; |
431 | qDebug() << "Model ressources freed."; |
431 | }
|
432 | }
|
432 | 433 | ||
433 | /** Setup of the Simulation.
|
434 | /** Setup of the Simulation.
|
434 | This really creates the simulation environment and does the setup of various aspects.
|
435 | This really creates the simulation environment and does the setup of various aspects.
|
435 | */
|
436 | */
|
436 | void Model::loadProject() |
437 | void Model::loadProject() |
437 | {
|
438 | {
|
438 | DebugTimer dt("load project"); |
439 | DebugTimer dt("load project"); |
439 | GlobalSettings *g = GlobalSettings::instance(); |
440 | GlobalSettings *g = GlobalSettings::instance(); |
440 | g->printDirecories(); |
441 | g->printDirecories(); |
441 | const XmlHelper &xml = g->settings(); |
442 | const XmlHelper &xml = g->settings(); |
442 | 443 | ||
443 | g->clearDatabaseConnections(); |
444 | g->clearDatabaseConnections(); |
444 | // database connections: reset
|
445 | // database connections: reset
|
445 | GlobalSettings::instance()->clearDatabaseConnections(); |
446 | GlobalSettings::instance()->clearDatabaseConnections(); |
446 | // input and climate connection
|
447 | // input and climate connection
|
447 | // see initOutputDatabase() for output database
|
448 | // see initOutputDatabase() for output database
|
448 | QString dbPath = g->path( xml.value("system.database.in"), "database"); |
449 | QString dbPath = g->path( xml.value("system.database.in"), "database"); |
449 | GlobalSettings::instance()->setupDatabaseConnection("in", dbPath, true); |
450 | GlobalSettings::instance()->setupDatabaseConnection("in", dbPath, true); |
450 | dbPath = g->path( xml.value("system.database.climate"), "database"); |
451 | dbPath = g->path( xml.value("system.database.climate"), "database"); |
451 | GlobalSettings::instance()->setupDatabaseConnection("climate", dbPath, true); |
452 | GlobalSettings::instance()->setupDatabaseConnection("climate", dbPath, true); |
452 | 453 | ||
453 | mSettings.loadModelSettings(); |
454 | mSettings.loadModelSettings(); |
454 | mSettings.print(); |
455 | mSettings.print(); |
455 | // random seed: if stored value is <> 0, use this as the random seed (and produce hence always an equal sequence of random numbers)
|
456 | // random seed: if stored value is <> 0, use this as the random seed (and produce hence always an equal sequence of random numbers)
|
456 | uint seed = xml.value("system.settings.randomSeed","0").toUInt(); |
457 | uint seed = xml.value("system.settings.randomSeed","0").toUInt(); |
457 | RandomGenerator::setup(RandomGenerator::ergMersenneTwister, seed); // use the MersenneTwister as default |
458 | RandomGenerator::setup(RandomGenerator::ergMersenneTwister, seed); // use the MersenneTwister as default |
458 | // linearization of expressions: if true *and* linearize() is explicitely called, then
|
459 | // linearization of expressions: if true *and* linearize() is explicitely called, then
|
459 | // function results will be cached over a defined range of values.
|
460 | // function results will be cached over a defined range of values.
|
460 | bool do_linearization = xml.valueBool("system.settings.expressionLinearizationEnabled", false); |
461 | bool do_linearization = xml.valueBool("system.settings.expressionLinearizationEnabled", false); |
461 | Expression::setLinearizationEnabled(do_linearization); |
462 | Expression::setLinearizationEnabled(do_linearization); |
462 | if (do_linearization) |
463 | if (do_linearization) |
463 | qDebug() << "The linearization of certains expressions is enabled (performance optimization)."; |
464 | qDebug() << "The linearization of certains expressions is enabled (performance optimization)."; |
464 | 465 | ||
465 | // log level
|
466 | // log level
|
466 | QString log_level = xml.value("system.settings.logLevel", "debug").toLower(); |
467 | QString log_level = xml.value("system.settings.logLevel", "debug").toLower(); |
467 | if (log_level=="debug") setLogLevel(0); |
468 | if (log_level=="debug") setLogLevel(0); |
468 | if (log_level=="info") setLogLevel(1); |
469 | if (log_level=="info") setLogLevel(1); |
469 | if (log_level=="warning") setLogLevel(2); |
470 | if (log_level=="warning") setLogLevel(2); |
470 | if (log_level=="error") setLogLevel(3); |
471 | if (log_level=="error") setLogLevel(3); |
471 | 472 | ||
472 | // snag dynamics / soil model enabled? (info used during setup of world)
|
473 | // snag dynamics / soil model enabled? (info used during setup of world)
|
473 | changeSettings().carbonCycleEnabled = xml.valueBool("model.settings.carbonCycleEnabled", false); |
474 | changeSettings().carbonCycleEnabled = xml.valueBool("model.settings.carbonCycleEnabled", false); |
474 | // class size of snag classes
|
475 | // class size of snag classes
|
475 | Snag::setupThresholds(xml.valueDouble("model.settings.soil.swdDBHClass12"), |
476 | Snag::setupThresholds(xml.valueDouble("model.settings.soil.swdDBHClass12"), |
476 | xml.valueDouble("model.settings.soil.swdDBHClass23")); |
477 | xml.valueDouble("model.settings.soil.swdDBHClass23")); |
477 | 478 | ||
478 | // setup of modules
|
479 | // setup of modules
|
479 | if (mModules) |
480 | if (mModules) |
480 | delete mModules; |
481 | delete mModules; |
481 | mModules = new Modules(); |
482 | mModules = new Modules(); |
482 | 483 | ||
483 | setupSpace(); |
484 | setupSpace(); |
484 | if (mRU.isEmpty()) |
485 | if (mRU.isEmpty()) |
485 | throw IException("Setup of Model: no resource units present!"); |
486 | throw IException("Setup of Model: no resource units present!"); |
486 | 487 | ||
487 | // (3) additional issues
|
488 | // (3) additional issues
|
488 | // (3.1) load javascript code into the engine
|
489 | // (3.1) load javascript code into the engine
|
489 | QString script_file = xml.value("system.javascript.fileName"); |
490 | QString script_file = xml.value("system.javascript.fileName"); |
490 | if (!script_file.isEmpty()) { |
491 | if (!script_file.isEmpty()) { |
491 | script_file = g->path(script_file, "script"); |
492 | script_file = g->path(script_file, "script"); |
492 | ScriptGlobal::loadScript(script_file); |
493 | ScriptGlobal::loadScript(script_file); |
493 | g->controller()->setLoadedJavascriptFile(script_file); |
494 | g->controller()->setLoadedJavascriptFile(script_file); |
494 | }
|
495 | }
|
495 | 496 | ||
496 | // (3.2) setup of regeneration
|
497 | // (3.2) setup of regeneration
|
497 | changeSettings().regenerationEnabled = xml.valueBool("model.settings.regenerationEnabled", false); |
498 | changeSettings().regenerationEnabled = xml.valueBool("model.settings.regenerationEnabled", false); |
498 | if (settings().regenerationEnabled) { |
499 | if (settings().regenerationEnabled) { |
499 | foreach(SpeciesSet *ss, mSpeciesSets) |
500 | foreach(SpeciesSet *ss, mSpeciesSets) |
500 | ss->setupRegeneration(); |
501 | ss->setupRegeneration(); |
501 | }
|
502 | }
|
502 | Sapling::setRecruitmentVariation(xml.valueDouble("model.settings.seedDispersal.recruitmentDimensionVariation",0.1)); |
503 | Sapling::setRecruitmentVariation(xml.valueDouble("model.settings.seedDispersal.recruitmentDimensionVariation",0.1)); |
503 | 504 | ||
504 | // (3.3) management
|
505 | // (3.3) management
|
505 | bool use_abe = xml.valueBool("model.management.abeEnabled"); |
506 | bool use_abe = xml.valueBool("model.management.abeEnabled"); |
506 | if (use_abe) { |
507 | if (use_abe) { |
507 | // use the agent based forest management engine
|
508 | // use the agent based forest management engine
|
508 | mABEManagement = new ABE::ForestManagementEngine(); |
509 | mABEManagement = new ABE::ForestManagementEngine(); |
509 | // setup of ABE after loading of trees.
|
510 | // setup of ABE after loading of trees.
|
510 | 511 | ||
511 | }
|
512 | }
|
512 | // use the standard management
|
513 | // use the standard management
|
513 | QString mgmtFile = xml.value("model.management.file"); |
514 | QString mgmtFile = xml.value("model.management.file"); |
514 | if (!mgmtFile.isEmpty() && xml.valueBool("model.management.enabled")) { |
515 | if (!mgmtFile.isEmpty() && xml.valueBool("model.management.enabled")) { |
515 | mManagement = new Management(); |
516 | mManagement = new Management(); |
516 | QString path = GlobalSettings::instance()->path(mgmtFile, "script"); |
517 | QString path = GlobalSettings::instance()->path(mgmtFile, "script"); |
517 | mManagement->loadScript(path); |
518 | mManagement->loadScript(path); |
518 | qDebug() << "setup management using script" << path; |
519 | qDebug() << "setup management using script" << path; |
519 | }
|
520 | }
|
520 | 521 | ||
521 | 522 | ||
522 | 523 | ||
523 | }
|
524 | }
|
524 | 525 | ||
525 | void Model::reloadABE() |
526 | void Model::reloadABE() |
526 | {
|
527 | {
|
527 | // delete firest
|
528 | // delete firest
|
528 | if (mABEManagement) |
529 | if (mABEManagement) |
529 | delete mABEManagement; |
530 | delete mABEManagement; |
530 | mABEManagement = new ABE::ForestManagementEngine(); |
531 | mABEManagement = new ABE::ForestManagementEngine(); |
531 | // and setup
|
532 | // and setup
|
532 | mABEManagement->setup(); |
533 | mABEManagement->setup(); |
533 | mABEManagement->runOnInit(); |
534 | mABEManagement->runOnInit(); |
534 | 535 | ||
535 | mABEManagement->initialize(); |
536 | mABEManagement->initialize(); |
536 | 537 | ||
537 | }
|
538 | }
|
538 | 539 | ||
539 | 540 | ||
540 | ResourceUnit *Model::ru(QPointF coord) |
541 | ResourceUnit *Model::ru(QPointF coord) |
541 | {
|
542 | {
|
542 | if (!mRUmap.isEmpty() && mRUmap.coordValid(coord)) |
543 | if (!mRUmap.isEmpty() && mRUmap.coordValid(coord)) |
543 | return mRUmap.valueAt(coord); |
544 | return mRUmap.valueAt(coord); |
544 | return ru(); // default RU if there is only one |
545 | return ru(); // default RU if there is only one |
545 | }
|
546 | }
|
546 | 547 | ||
547 | void Model::initOutputDatabase() |
548 | void Model::initOutputDatabase() |
548 | {
|
549 | {
|
549 | GlobalSettings *g = GlobalSettings::instance(); |
550 | GlobalSettings *g = GlobalSettings::instance(); |
550 | QString dbPath = g->path(g->settings().value("system.database.out"), "output"); |
551 | QString dbPath = g->path(g->settings().value("system.database.out"), "output"); |
551 | // create run-metadata
|
552 | // create run-metadata
|
552 | int maxid = SqlHelper::queryValue("select max(id) from runs", g->dbin()).toInt(); |
553 | int maxid = SqlHelper::queryValue("select max(id) from runs", g->dbin()).toInt(); |
553 | 554 | ||
554 | maxid++; |
555 | maxid++; |
555 | QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"); |
556 | QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"); |
556 | SqlHelper::executeSql(QString("insert into runs (id, timestamp) values (%1, '%2')").arg(maxid).arg(timestamp), g->dbin()); |
557 | SqlHelper::executeSql(QString("insert into runs (id, timestamp) values (%1, '%2')").arg(maxid).arg(timestamp), g->dbin()); |
557 | // replace path information
|
558 | // replace path information
|
558 | dbPath.replace("$id$", QString::number(maxid)); |
559 | dbPath.replace("$id$", QString::number(maxid)); |
559 | dbPath.replace("$date$", timestamp); |
560 | dbPath.replace("$date$", timestamp); |
560 | // setup final path
|
561 | // setup final path
|
561 | g->setupDatabaseConnection("out", dbPath, false); |
562 | g->setupDatabaseConnection("out", dbPath, false); |
562 | 563 | ||
563 | }
|
564 | }
|
564 | 565 | ||
565 | 566 | ||
566 | /// multithreaded running function for the resource unit level establishment
|
567 | /// multithreaded running function for the resource unit level establishment
|
567 | ResourceUnit *nc_sapling_growth_establishment(ResourceUnit *unit) |
568 | ResourceUnit *nc_sapling_growth_establishment(ResourceUnit *unit) |
568 | {
|
569 | {
|
569 | try { |
570 | try { |
570 | { // DebugTimer t("nc_saplingGrowth"); t.setSilent(); |
571 | { // DebugTimer t("nc_saplingGrowth"); t.setSilent(); |
571 | // define a height map for the current resource unit on the stack
|
572 | // define a height map for the current resource unit on the stack
|
572 | float sapling_map[cPxPerRU*cPxPerRU]; |
573 | float sapling_map[cPxPerRU*cPxPerRU]; |
573 | // set the map and initialize it:
|
574 | // set the map and initialize it:
|
574 | unit->setSaplingHeightMap(sapling_map); |
575 | unit->setSaplingHeightMap(sapling_map); |
575 | 576 | ||
576 | 577 | ||
577 | // (1) calculate the growth of (already established) saplings (populate sapling map)
|
578 | // (1) calculate the growth of (already established) saplings (populate sapling map)
|
578 | QList<ResourceUnitSpecies*>::const_iterator rus; |
579 | QList<ResourceUnitSpecies*>::const_iterator rus; |
579 | for (rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus) |
580 | for (rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus) |
580 | (*rus)->calclulateSaplingGrowth(); |
581 | (*rus)->calclulateSaplingGrowth(); |
581 | 582 | ||
582 | } { // DebugTimer t("nc_Establishment"); t.setSilent(); |
583 | } { // DebugTimer t("nc_Establishment"); t.setSilent(); |
583 | 584 | ||
584 | // (2) calculate the establishment probabilities of new saplings
|
585 | // (2) calculate the establishment probabilities of new saplings
|
585 | for (QList<ResourceUnitSpecies*>::const_iterator rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus) |
586 | for (QList<ResourceUnitSpecies*>::const_iterator rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus) |
586 | (*rus)->calculateEstablishment(); |
587 | (*rus)->calculateEstablishment(); |
587 | 588 | ||
588 | }
|
589 | }
|
589 | 590 | ||
590 | } catch (const IException& e) { |
591 | } catch (const IException& e) { |
591 | GlobalSettings::instance()->controller()->throwError(e.message()); |
592 | GlobalSettings::instance()->controller()->throwError(e.message()); |
592 | }
|
593 | }
|
593 | 594 | ||
594 | unit->setSaplingHeightMap(0); // invalidate again |
595 | unit->setSaplingHeightMap(0); // invalidate again |
595 | return unit; |
596 | return unit; |
596 | 597 | ||
597 | }
|
598 | }
|
598 | 599 | ||
599 | 600 | ||
600 | /// multithreaded execution of the carbon cycle routine
|
601 | /// multithreaded execution of the carbon cycle routine
|
601 | ResourceUnit *nc_carbonCycle(ResourceUnit *unit) |
602 | ResourceUnit *nc_carbonCycle(ResourceUnit *unit) |
602 | {
|
603 | {
|
603 | try { |
604 | try { |
604 | // (1) do calculations on snag dynamics for the resource unit
|
605 | // (1) do calculations on snag dynamics for the resource unit
|
605 | unit->calculateCarbonCycle(); |
606 | unit->calculateCarbonCycle(); |
606 | // (2) do the soil carbon and nitrogen dynamics calculations (ICBM/2N)
|
607 | // (2) do the soil carbon and nitrogen dynamics calculations (ICBM/2N)
|
607 | } catch (const IException& e) { |
608 | } catch (const IException& e) { |
608 | GlobalSettings::instance()->controller()->throwError(e.message()); |
609 | GlobalSettings::instance()->controller()->throwError(e.message()); |
609 | }
|
610 | }
|
610 | 611 | ||
611 | return unit; |
612 | return unit; |
612 | }
|
613 | }
|
613 | 614 | ||
614 | /// beforeRun performs several steps before the models starts running.
|
615 | /// beforeRun performs several steps before the models starts running.
|
615 | /// inter alia: * setup of the stands
|
616 | /// inter alia: * setup of the stands
|
616 | /// * setup of the climates
|
617 | /// * setup of the climates
|
617 | void Model::beforeRun() |
618 | void Model::beforeRun() |
618 | {
|
619 | {
|
619 | // setup outputs
|
620 | // setup outputs
|
620 | // setup output database
|
621 | // setup output database
|
621 | if (GlobalSettings::instance()->dbout().isOpen()) |
622 | if (GlobalSettings::instance()->dbout().isOpen()) |
622 | GlobalSettings::instance()->dbout().close(); |
623 | GlobalSettings::instance()->dbout().close(); |
623 | initOutputDatabase(); |
624 | initOutputDatabase(); |
624 | GlobalSettings::instance()->outputManager()->setup(); |
625 | GlobalSettings::instance()->outputManager()->setup(); |
625 | GlobalSettings::instance()->clearDebugLists(); |
626 | GlobalSettings::instance()->clearDebugLists(); |
626 | 627 | ||
627 | // initialize stands
|
628 | // initialize stands
|
628 | StandLoader loader(this); |
629 | StandLoader loader(this); |
629 | {
|
630 | {
|
630 | DebugTimer loadtrees("load trees"); |
631 | DebugTimer loadtrees("load trees"); |
631 | loader.processInit(); |
632 | loader.processInit(); |
632 | }
|
633 | }
|
633 | // initalization of ABE
|
634 | // initalization of ABE
|
634 | if (mABEManagement) { |
635 | if (mABEManagement) { |
635 | mABEManagement->setup(); |
636 | mABEManagement->setup(); |
636 | mABEManagement->runOnInit(); |
637 | mABEManagement->runOnInit(); |
637 | }
|
638 | }
|
638 | 639 | ||
639 | // load climate
|
640 | // load climate
|
640 | {
|
641 | {
|
641 | if (logLevelDebug()) qDebug() << "attempting to load climate..." ; |
642 | if (logLevelDebug()) qDebug() << "attempting to load climate..." ; |
642 | DebugTimer loadclim("load climate"); |
643 | DebugTimer loadclim("load climate"); |
643 | foreach(Climate *c, mClimates) { |
644 | foreach(Climate *c, mClimates) { |
644 | if (!c->isSetup()) |
645 | if (!c->isSetup()) |
645 | c->setup(); |
646 | c->setup(); |
646 | }
|
647 | }
|
647 | // load the first year of the climate database
|
648 | // load the first year of the climate database
|
648 | foreach(Climate *c, mClimates) |
649 | foreach(Climate *c, mClimates) |
649 | c->nextYear(); |
650 | c->nextYear(); |
650 | 651 | ||
651 | }
|
652 | }
|
652 | 653 | ||
653 | 654 | ||
654 | { DebugTimer loadinit("load standstatistics"); |
655 | { DebugTimer loadinit("load standstatistics"); |
655 | if (logLevelDebug()) qDebug() << "attempting to calculate initial stand statistics (incl. apply and read pattern)..." ; |
656 | if (logLevelDebug()) qDebug() << "attempting to calculate initial stand statistics (incl. apply and read pattern)..." ; |
656 | Tree::setGrid(mGrid, mHeightGrid); |
657 | Tree::setGrid(mGrid, mHeightGrid); |
657 | // debugCheckAllTrees(); // introduced for debugging session (2012-04-06)
|
658 | // debugCheckAllTrees(); // introduced for debugging session (2012-04-06)
|
658 | applyPattern(); |
659 | applyPattern(); |
659 | readPattern(); |
660 | readPattern(); |
660 | loader.processAfterInit(); // e.g. initialization of saplings |
661 | loader.processAfterInit(); // e.g. initialization of saplings |
661 | 662 | ||
662 | // force the compilation of initial stand statistics
|
663 | // force the compilation of initial stand statistics
|
663 | createStandStatistics(); |
664 | createStandStatistics(); |
664 | }
|
665 | }
|
665 | 666 | ||
666 | // initalization of ABE (now all stands are properly set up)
|
667 | // initalization of ABE (now all stands are properly set up)
|
667 | if (mABEManagement) { |
668 | if (mABEManagement) { |
668 | mABEManagement->initialize(); |
669 | mABEManagement->initialize(); |
669 | }
|
670 | }
|
670 | 671 | ||
671 | 672 | ||
672 | // outputs to create with inital state (without any growth) are called here:
|
673 | // outputs to create with inital state (without any growth) are called here:
|
673 | GlobalSettings::instance()->setCurrentYear(0); // set clock to "0" (for outputs with initial state) |
674 | GlobalSettings::instance()->setCurrentYear(0); // set clock to "0" (for outputs with initial state) |
674 | 675 | ||
675 | GlobalSettings::instance()->outputManager()->execute("stand"); // year=0 |
676 | GlobalSettings::instance()->outputManager()->execute("stand"); // year=0 |
676 | GlobalSettings::instance()->outputManager()->execute("landscape"); // year=0 |
677 | GlobalSettings::instance()->outputManager()->execute("landscape"); // year=0 |
677 | GlobalSettings::instance()->outputManager()->execute("sapling"); // year=0 |
678 | GlobalSettings::instance()->outputManager()->execute("sapling"); // year=0 |
678 | GlobalSettings::instance()->outputManager()->execute("tree"); // year=0 |
679 | GlobalSettings::instance()->outputManager()->execute("tree"); // year=0 |
679 | GlobalSettings::instance()->outputManager()->execute("dynamicstand"); // year=0 |
680 | GlobalSettings::instance()->outputManager()->execute("dynamicstand"); // year=0 |
680 | 681 | ||
681 | GlobalSettings::instance()->setCurrentYear(1); // set to first year |
682 | GlobalSettings::instance()->setCurrentYear(1); // set to first year |
682 | 683 | ||
683 | }
|
684 | }
|
684 | 685 | ||
685 | /** Main model runner.
|
686 | /** Main model runner.
|
686 | The sequence of actions is as follows:
|
687 | The sequence of actions is as follows:
|
687 | (1) Load the climate of the new year
|
688 | (1) Load the climate of the new year
|
688 | (2) Reset statistics for resource unit as well as for dead/managed trees
|
689 | (2) Reset statistics for resource unit as well as for dead/managed trees
|
689 | (3) Invoke Management.
|
690 | (3) Invoke Management.
|
690 | (4) *after* that, calculate Light patterns
|
691 | (4) *after* that, calculate Light patterns
|
691 | (5) 3PG on stand level, tree growth. Clear stand-statistcs before they are filled by single-tree-growth. calculate water cycle (with LAIs before management)
|
692 | (5) 3PG on stand level, tree growth. Clear stand-statistcs before they are filled by single-tree-growth. calculate water cycle (with LAIs before management)
|
692 | (6) execute Regeneration
|
693 | (6) execute Regeneration
|
693 | (7) invoke disturbance modules
|
694 | (7) invoke disturbance modules
|
694 | (8) calculate statistics for the year
|
695 | (8) calculate statistics for the year
|
695 | (9) write database outputs
|
696 | (9) write database outputs
|
696 | */
|
697 | */
|
697 | void Model::runYear() |
698 | void Model::runYear() |
698 | {
|
699 | {
|
699 | DebugTimer t("Model::runYear()"); |
700 | DebugTimer t("Model::runYear()"); |
700 | GlobalSettings::instance()->systemStatistics()->reset(); |
701 | GlobalSettings::instance()->systemStatistics()->reset(); |
701 | RandomGenerator::checkGenerator(); // see if we need to generate new numbers... |
702 | RandomGenerator::checkGenerator(); // see if we need to generate new numbers... |
702 | // initalization at start of year for external modules
|
703 | // initalization at start of year for external modules
|
703 | mModules->yearBegin(); |
704 | mModules->yearBegin(); |
704 | 705 | ||
705 | // execute scheduled events for the current year
|
706 | // execute scheduled events for the current year
|
706 | if (mTimeEvents) |
707 | if (mTimeEvents) |
707 | mTimeEvents->run(); |
708 | mTimeEvents->run(); |
708 | 709 | ||
709 | // load the next year of the climate database (except for the first year - the first climate year is loaded immediately
|
710 | // load the next year of the climate database (except for the first year - the first climate year is loaded immediately
|
710 | if (GlobalSettings::instance()->currentYear()>1) { |
711 | if (GlobalSettings::instance()->currentYear()>1) { |
711 | foreach(Climate *c, mClimates) |
712 | foreach(Climate *c, mClimates) |
712 | c->nextYear(); |
713 | c->nextYear(); |
713 | }
|
714 | }
|
714 | 715 | ||
715 | // reset statistics
|
716 | // reset statistics
|
716 | foreach(ResourceUnit *ru, mRU) |
717 | foreach(ResourceUnit *ru, mRU) |
717 | ru->newYear(); |
718 | ru->newYear(); |
718 | 719 | ||
719 | foreach(SpeciesSet *set, mSpeciesSets) |
720 | foreach(SpeciesSet *set, mSpeciesSets) |
720 | set->newYear(); |
721 | set->newYear(); |
721 | // management classic
|
722 | // management classic
|
722 | if (mManagement) { |
723 | if (mManagement) { |
723 | DebugTimer t("management"); |
724 | DebugTimer t("management"); |
724 | mManagement->run(); |
725 | mManagement->run(); |
725 | GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed(); |
726 | GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed(); |
726 | }
|
727 | }
|
727 | // ... or ABE (the agent based variant)
|
728 | // ... or ABE (the agent based variant)
|
728 | if (mABEManagement) { |
729 | if (mABEManagement) { |
729 | DebugTimer t("ABE:run"); |
730 | DebugTimer t("ABE:run"); |
730 | mABEManagement->run(); |
731 | mABEManagement->run(); |
731 | GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed(); |
732 | GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed(); |
732 | }
|
733 | }
|
733 | 734 | ||
734 | // if trees are dead/removed because of management, the tree lists
|
735 | // if trees are dead/removed because of management, the tree lists
|
735 | // need to be cleaned (and the statistics need to be recreated)
|
736 | // need to be cleaned (and the statistics need to be recreated)
|
736 | cleanTreeLists(); |
737 | cleanTreeLists(); |
737 | 738 | ||
738 | // process a cycle of individual growth
|
739 | // process a cycle of individual growth
|
739 | applyPattern(); // create Light Influence Patterns |
740 | applyPattern(); // create Light Influence Patterns |
740 | readPattern(); // readout light state of individual trees |
741 | readPattern(); // readout light state of individual trees |
741 | grow(); // let the trees grow (growth on stand-level, tree-level, mortality) |
742 | grow(); // let the trees grow (growth on stand-level, tree-level, mortality) |
742 | mGrassCover->execute(); // evaluate the grass / herb cover (and its effect on regeneration) |
743 | mGrassCover->execute(); // evaluate the grass / herb cover (and its effect on regeneration) |
743 | 744 | ||
744 | // regeneration
|
745 | // regeneration
|
745 | if (settings().regenerationEnabled) { |
746 | if (settings().regenerationEnabled) { |
746 | // seed dispersal
|
747 | // seed dispersal
|
747 | DebugTimer tseed("Regeneration and Establishment"); |
748 | DebugTimer tseed("Regeneration and Establishment"); |
748 | foreach(SpeciesSet *set, mSpeciesSets) |
749 | foreach(SpeciesSet *set, mSpeciesSets) |
749 | set->regeneration(); // parallel execution for each species set |
750 | set->regeneration(); // parallel execution for each species set |
750 | 751 | ||
751 | GlobalSettings::instance()->systemStatistics()->tSeedDistribution+=tseed.elapsed(); |
752 | GlobalSettings::instance()->systemStatistics()->tSeedDistribution+=tseed.elapsed(); |
752 | // establishment
|
753 | // establishment
|
753 | Sapling::updateBrowsingPressure(); |
754 | Sapling::updateBrowsingPressure(); |
754 | 755 | ||
755 | { DebugTimer t("saplingGrowthEstablishment"); |
756 | { DebugTimer t("saplingGrowthEstablishment"); |
756 | executePerResourceUnit( nc_sapling_growth_establishment, false /* true: force single thraeded operation */); |
757 | executePerResourceUnit( nc_sapling_growth_establishment, false /* true: force single thraeded operation */); |
757 | GlobalSettings::instance()->systemStatistics()->tSaplingAndEstablishment+=t.elapsed(); |
758 | GlobalSettings::instance()->systemStatistics()->tSaplingAndEstablishment+=t.elapsed(); |
758 | }
|
759 | }
|
759 | 760 | ||
760 | Establishment::debugInfo(); // debug test |
761 | Establishment::debugInfo(); // debug test |
761 | 762 | ||
762 | }
|
763 | }
|
763 | 764 | ||
764 | // calculate soil / snag dynamics
|
765 | // calculate soil / snag dynamics
|
765 | if (settings().carbonCycleEnabled) { |
766 | if (settings().carbonCycleEnabled) { |
766 | DebugTimer ccycle("carbon cylce"); |
767 | DebugTimer ccycle("carbon cylce"); |
767 | executePerResourceUnit( nc_carbonCycle, false /* true: force single threaded operation */); |
768 | executePerResourceUnit( nc_carbonCycle, false /* true: force single threaded operation */); |
768 | GlobalSettings::instance()->systemStatistics()->tCarbonCycle+=ccycle.elapsed(); |
769 | GlobalSettings::instance()->systemStatistics()->tCarbonCycle+=ccycle.elapsed(); |
769 | 770 | ||
770 | }
|
771 | }
|
771 | 772 | ||
772 | // external modules/disturbances
|
773 | // external modules/disturbances
|
773 | mModules->run(); |
774 | mModules->run(); |
774 | 775 | ||
775 | // cleanup of tree lists if external modules removed trees.
|
776 | // cleanup of tree lists if external modules removed trees.
|
776 | cleanTreeLists(); |
777 | cleanTreeLists(); |
777 | 778 | ||
778 | 779 | ||
779 | DebugTimer toutput("outputs"); |
780 | DebugTimer toutput("outputs"); |
780 | // calculate statistics
|
781 | // calculate statistics
|
781 | foreach(ResourceUnit *ru, mRU) |
782 | foreach(ResourceUnit *ru, mRU) |
782 | ru->yearEnd(); |
783 | ru->yearEnd(); |
783 | 784 | ||
784 | // create outputs
|
785 | // create outputs
|
785 | OutputManager *om = GlobalSettings::instance()->outputManager(); |
786 | OutputManager *om = GlobalSettings::instance()->outputManager(); |
786 | om->execute("tree"); // single tree output |
787 | om->execute("tree"); // single tree output |
787 | om->execute("stand"); //resource unit level x species |
788 | om->execute("stand"); //resource unit level x species |
788 | om->execute("landscape"); //landscape x species |
789 | om->execute("landscape"); //landscape x species |
789 | om->execute("sapling"); // sapling layer per RU x species |
790 | om->execute("sapling"); // sapling layer per RU x species |
790 | om->execute("production_month"); // 3pg responses growth per species x RU x month |
791 | om->execute("production_month"); // 3pg responses growth per species x RU x month |
791 | om->execute("dynamicstand"); // output with user-defined columns (based on species x RU) |
792 | om->execute("dynamicstand"); // output with user-defined columns (based on species x RU) |
792 | om->execute("standdead"); // resource unit level x species |
793 | om->execute("standdead"); // resource unit level x species |
793 | om->execute("management"); // resource unit level x species |
794 | om->execute("management"); // resource unit level x species |
794 | om->execute("carbon"); // resource unit level, carbon pools above and belowground |
795 | om->execute("carbon"); // resource unit level, carbon pools above and belowground |
795 | om->execute("carbonflow"); // resource unit level, GPP, NPP and total carbon flows (atmosphere, harvest, ...) |
796 | om->execute("carbonflow"); // resource unit level, GPP, NPP and total carbon flows (atmosphere, harvest, ...) |
796 | 797 | ||
797 | GlobalSettings::instance()->systemStatistics()->tWriteOutput+=toutput.elapsed(); |
798 | GlobalSettings::instance()->systemStatistics()->tWriteOutput+=toutput.elapsed(); |
798 | GlobalSettings::instance()->systemStatistics()->tTotalYear+=t.elapsed(); |
799 | GlobalSettings::instance()->systemStatistics()->tTotalYear+=t.elapsed(); |
799 | GlobalSettings::instance()->systemStatistics()->writeOutput(); |
800 | GlobalSettings::instance()->systemStatistics()->writeOutput(); |
800 | 801 | ||
801 | GlobalSettings::instance()->setCurrentYear(GlobalSettings::instance()->currentYear()+1); |
802 | GlobalSettings::instance()->setCurrentYear(GlobalSettings::instance()->currentYear()+1); |
802 | }
|
803 | }
|
803 | 804 | ||
804 | 805 | ||
805 | 806 | ||
806 | void Model::afterStop() |
807 | void Model::afterStop() |
807 | {
|
808 | {
|
808 | // do some cleanup
|
809 | // do some cleanup
|
809 | }
|
810 | }
|
810 | 811 | ||
811 | /// multithreaded running function for LIP printing
|
812 | /// multithreaded running function for LIP printing
|
812 | ResourceUnit* nc_applyPattern(ResourceUnit *unit) |
813 | ResourceUnit* nc_applyPattern(ResourceUnit *unit) |
813 | {
|
814 | {
|
814 | 815 | ||
815 | QVector<Tree>::iterator tit; |
816 | QVector<Tree>::iterator tit; |
816 | QVector<Tree>::iterator tend = unit->trees().end(); |
817 | QVector<Tree>::iterator tend = unit->trees().end(); |
817 | 818 | ||
818 | try { |
819 | try { |
819 | 820 | ||
820 | // light concurrence influence
|
821 | // light concurrence influence
|
821 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
822 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
822 | // height dominance grid
|
823 | // height dominance grid
|
823 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
824 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
824 | (*tit).heightGrid(); // just do it ;) |
825 | (*tit).heightGrid(); // just do it ;) |
825 | 826 | ||
826 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
827 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
827 | (*tit).applyLIP(); // just do it ;) |
828 | (*tit).applyLIP(); // just do it ;) |
828 | 829 | ||
829 | } else { |
830 | } else { |
830 | // height dominance grid
|
831 | // height dominance grid
|
831 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
832 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
832 | (*tit).heightGrid_torus(); // just do it ;) |
833 | (*tit).heightGrid_torus(); // just do it ;) |
833 | 834 | ||
834 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
835 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
835 | (*tit).applyLIP_torus(); // do it the wraparound way |
836 | (*tit).applyLIP_torus(); // do it the wraparound way |
836 | }
|
837 | }
|
837 | return unit; |
838 | return unit; |
838 | } catch (const IException &e) { |
839 | } catch (const IException &e) { |
839 | GlobalSettings::instance()->controller()->throwError(e.message()); |
840 | GlobalSettings::instance()->controller()->throwError(e.message()); |
840 | }
|
841 | }
|
841 | return unit; |
842 | return unit; |
842 | }
|
843 | }
|
843 | 844 | ||
844 | /// multithreaded running function for LIP value extraction
|
845 | /// multithreaded running function for LIP value extraction
|
845 | ResourceUnit *nc_readPattern(ResourceUnit *unit) |
846 | ResourceUnit *nc_readPattern(ResourceUnit *unit) |
846 | {
|
847 | {
|
847 | QVector<Tree>::iterator tit; |
848 | QVector<Tree>::iterator tit; |
848 | QVector<Tree>::iterator tend = unit->trees().end(); |
849 | QVector<Tree>::iterator tend = unit->trees().end(); |
849 | try { |
850 | try { |
850 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
851 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
851 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
852 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
852 | (*tit).readLIF(); // multipliactive approach |
853 | (*tit).readLIF(); // multipliactive approach |
853 | } else { |
854 | } else { |
854 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
855 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
855 | (*tit).readLIF_torus(); // do it the wraparound way |
856 | (*tit).readLIF_torus(); // do it the wraparound way |
856 | }
|
857 | }
|
857 | } catch (const IException &e) { |
858 | } catch (const IException &e) { |
858 | GlobalSettings::instance()->controller()->throwError(e.message()); |
859 | GlobalSettings::instance()->controller()->throwError(e.message()); |
859 | }
|
860 | }
|
860 | return unit; |
861 | return unit; |
861 | }
|
862 | }
|
862 | 863 | ||
863 | /// multithreaded running function for the growth of individual trees
|
864 | /// multithreaded running function for the growth of individual trees
|
864 | ResourceUnit *nc_grow(ResourceUnit *unit) |
865 | ResourceUnit *nc_grow(ResourceUnit *unit) |
865 | {
|
866 | {
|
866 | QVector<Tree>::iterator tit; |
867 | QVector<Tree>::iterator tit; |
867 | QVector<Tree>::iterator tend = unit->trees().end(); |
868 | QVector<Tree>::iterator tend = unit->trees().end(); |
868 | try { |
869 | try { |
869 | unit->beforeGrow(); // reset statistics |
870 | unit->beforeGrow(); // reset statistics |
870 | // calculate light responses
|
871 | // calculate light responses
|
871 | // responses are based on *modified* values for LightResourceIndex
|
872 | // responses are based on *modified* values for LightResourceIndex
|
872 | for (tit=unit->trees().begin(); tit!=tend; ++tit) { |
873 | for (tit=unit->trees().begin(); tit!=tend; ++tit) { |
873 | (*tit).calcLightResponse(); |
874 | (*tit).calcLightResponse(); |
874 | }
|
875 | }
|
875 | 876 | ||
876 | unit->calculateInterceptedArea(); |
877 | unit->calculateInterceptedArea(); |
877 | 878 | ||
878 | for (tit=unit->trees().begin(); tit!=tend; ++tit) { |
879 | for (tit=unit->trees().begin(); tit!=tend; ++tit) { |
879 | (*tit).grow(); // actual growth of individual trees |
880 | (*tit).grow(); // actual growth of individual trees |
880 | }
|
881 | }
|
881 | } catch (const IException &e) { |
882 | } catch (const IException &e) { |
882 | GlobalSettings::instance()->controller()->throwError(e.message()); |
883 | GlobalSettings::instance()->controller()->throwError(e.message()); |
883 | }
|
884 | }
|
884 | 885 | ||
885 | GlobalSettings::instance()->systemStatistics()->treeCount+=unit->trees().count(); |
886 | GlobalSettings::instance()->systemStatistics()->treeCount+=unit->trees().count(); |
886 | return unit; |
887 | return unit; |
887 | }
|
888 | }
|
888 | 889 | ||
889 | /// multithreaded running function for the resource level production
|
890 | /// multithreaded running function for the resource level production
|
890 | ResourceUnit *nc_production(ResourceUnit *unit) |
891 | ResourceUnit *nc_production(ResourceUnit *unit) |
891 | {
|
892 | {
|
892 | try { |
893 | try { |
893 | unit->production(); |
894 | unit->production(); |
894 | } catch (const IException &e) { |
895 | } catch (const IException &e) { |
895 | GlobalSettings::instance()->controller()->throwError(e.message()); |
896 | GlobalSettings::instance()->controller()->throwError(e.message()); |
896 | }
|
897 | }
|
897 | return unit; |
898 | return unit; |
898 | }
|
899 | }
|
899 | 900 | ||
900 | 901 | ||
901 | void Model::test() |
902 | void Model::test() |
902 | {
|
903 | {
|
903 | // Test-funktion: braucht 1/3 time von readGrid()
|
904 | // Test-funktion: braucht 1/3 time von readGrid()
|
904 | DebugTimer t("test"); |
905 | DebugTimer t("test"); |
905 | FloatGrid averaged = mGrid->averaged(10); |
906 | FloatGrid averaged = mGrid->averaged(10); |
906 | int count = 0; |
907 | int count = 0; |
907 | float *end = averaged.end(); |
908 | float *end = averaged.end(); |
908 | for (float *p=averaged.begin(); p!=end; ++p) |
909 | for (float *p=averaged.begin(); p!=end; ++p) |
909 | if (*p > 0.9) |
910 | if (*p > 0.9) |
910 | count++; |
911 | count++; |
911 | qDebug() << count << "LIF>0.9 of " << averaged.count(); |
912 | qDebug() << count << "LIF>0.9 of " << averaged.count(); |
912 | }
|
913 | }
|
913 | 914 | ||
914 | void Model::debugCheckAllTrees() |
915 | void Model::debugCheckAllTrees() |
915 | {
|
916 | {
|
916 | AllTreeIterator at(this); |
917 | AllTreeIterator at(this); |
917 | bool has_errors = false; double dummy=0.; |
918 | bool has_errors = false; double dummy=0.; |
918 | while (Tree *t = at.next()) { |
919 | while (Tree *t = at.next()) { |
919 | // plausibility
|
920 | // plausibility
|
920 | if (t->dbh()<0 || t->dbh()>10000. || t->biomassFoliage()<0. || t->height()>1000. || t->height() < 0. |
921 | if (t->dbh()<0 || t->dbh()>10000. || t->biomassFoliage()<0. || t->height()>1000. || t->height() < 0. |
921 | || t->biomassFoliage() <0.) |
922 | || t->biomassFoliage() <0.) |
922 | has_errors = true; |
923 | has_errors = true; |
923 | // check for objects....
|
924 | // check for objects....
|
924 | dummy = t->stamp()->offset() + t->ru()->ruSpecies()[1]->statistics().count(); |
925 | dummy = t->stamp()->offset() + t->ru()->ruSpecies()[1]->statistics().count(); |
925 | }
|
926 | }
|
926 | if (has_errors) |
927 | if (has_errors) |
927 | qDebug() << "model: debugCheckAllTrees found problems" << dummy; |
928 | qDebug() << "model: debugCheckAllTrees found problems" << dummy; |
928 | }
|
929 | }
|
929 | 930 | ||
930 | void Model::applyPattern() |
931 | void Model::applyPattern() |
931 | {
|
932 | {
|
932 | 933 | ||
933 | DebugTimer t("applyPattern()"); |
934 | DebugTimer t("applyPattern()"); |
934 | // intialize grids...
|
935 | // intialize grids...
|
935 | initializeGrid(); |
936 | initializeGrid(); |
936 | 937 | ||
937 | // initialize height grid with a value of 4m. This is the height of the regeneration layer
|
938 | // initialize height grid with a value of 4m. This is the height of the regeneration layer
|
938 | for (HeightGridValue *h=mHeightGrid->begin();h!=mHeightGrid->end();++h) { |
939 | for (HeightGridValue *h=mHeightGrid->begin();h!=mHeightGrid->end();++h) { |
939 | h->resetCount(); // set count = 0, but do not touch the flags |
940 | h->resetCount(); // set count = 0, but do not touch the flags |
940 | h->height = 4.f; |
941 | h->height = 4.f; |
941 | }
|
942 | }
|
942 | 943 | ||
943 | threadRunner.run(nc_applyPattern); |
944 | threadRunner.run(nc_applyPattern); |
944 | GlobalSettings::instance()->systemStatistics()->tApplyPattern+=t.elapsed(); |
945 | GlobalSettings::instance()->systemStatistics()->tApplyPattern+=t.elapsed(); |
945 | }
|
946 | }
|
946 | 947 | ||
947 | void Model::readPattern() |
948 | void Model::readPattern() |
948 | {
|
949 | {
|
949 | DebugTimer t("readPattern()"); |
950 | DebugTimer t("readPattern()"); |
950 | threadRunner.run(nc_readPattern); |
951 | threadRunner.run(nc_readPattern); |
951 | GlobalSettings::instance()->systemStatistics()->tReadPattern+=t.elapsed(); |
952 | GlobalSettings::instance()->systemStatistics()->tReadPattern+=t.elapsed(); |
952 | 953 | ||
953 | }
|
954 | }
|
954 | 955 | ||
955 | /** Main function for the growth of stands and trees.
|
956 | /** Main function for the growth of stands and trees.
|
956 | This includes several steps.
|
957 | This includes several steps.
|
957 | (1) calculate the stocked area (i.e. count pixels in height grid)
|
958 | (1) calculate the stocked area (i.e. count pixels in height grid)
|
958 | (2) 3PG production (including response calculation, water cycle)
|
959 | (2) 3PG production (including response calculation, water cycle)
|
959 | (3) single tree growth (including mortality)
|
960 | (3) single tree growth (including mortality)
|
960 | (4) cleanup of tree lists (remove dead trees)
|
961 | (4) cleanup of tree lists (remove dead trees)
|
961 | */
|
962 | */
|
962 | void Model::grow() |
963 | void Model::grow() |
963 | {
|
964 | {
|
964 | 965 | ||
965 | if (!settings().growthEnabled) |
966 | if (!settings().growthEnabled) |
966 | return; |
967 | return; |
967 | { DebugTimer t("growRU()"); |
968 | { DebugTimer t("growRU()"); |
968 | calculateStockedArea(); |
969 | calculateStockedArea(); |
969 | 970 | ||
970 | // multithreaded: mutex for the message handler in mainwindow solved the crashes.
|
971 | // multithreaded: mutex for the message handler in mainwindow solved the crashes.
|
971 | threadRunner.run(nc_production); |
972 | threadRunner.run(nc_production); |
972 | }
|
973 | }
|
973 | 974 | ||
974 | DebugTimer t("growTrees()"); |
975 | DebugTimer t("growTrees()"); |
975 | threadRunner.run(nc_grow); // actual growth of individual trees |
976 | threadRunner.run(nc_grow); // actual growth of individual trees |
976 | 977 | ||
977 | foreach(ResourceUnit *ru, mRU) { |
978 | foreach(ResourceUnit *ru, mRU) { |
978 | ru->cleanTreeList(); |
979 | ru->cleanTreeList(); |
979 | ru->afterGrow(); |
980 | ru->afterGrow(); |
980 | //qDebug() << (b-n) << "trees died (of" << b << ").";
|
981 | //qDebug() << (b-n) << "trees died (of" << b << ").";
|
981 | }
|
982 | }
|
982 | GlobalSettings::instance()->systemStatistics()->tTreeGrowth+=t.elapsed(); |
983 | GlobalSettings::instance()->systemStatistics()->tTreeGrowth+=t.elapsed(); |
983 | }
|
984 | }
|
984 | 985 | ||
985 | /** calculate for each resource unit the fraction of area which is stocked.
|
986 | /** calculate for each resource unit the fraction of area which is stocked.
|
986 | This is done by checking the pixels of the global height grid.
|
987 | This is done by checking the pixels of the global height grid.
|
987 | */
|
988 | */
|
988 | void Model::calculateStockedArea() |
989 | void Model::calculateStockedArea() |
989 | {
|
990 | {
|
990 | // iterate over the whole heightgrid and count pixels for each ressource unit
|
991 | // iterate over the whole heightgrid and count pixels for each ressource unit
|
991 | HeightGridValue *end = mHeightGrid->end(); |
992 | HeightGridValue *end = mHeightGrid->end(); |
992 | QPointF cp;
|
993 | QPointF cp;
|
993 | ResourceUnit *ru; |
994 | ResourceUnit *ru; |
994 | for (HeightGridValue *i=mHeightGrid->begin(); i!=end; ++i) { |
995 | for (HeightGridValue *i=mHeightGrid->begin(); i!=end; ++i) { |
995 | cp = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i)); |
996 | cp = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i)); |
996 | if (mRUmap.coordValid(cp)) { |
997 | if (mRUmap.coordValid(cp)) { |
997 | ru = mRUmap.valueAt(cp); |
998 | ru = mRUmap.valueAt(cp); |
998 | if (ru) { |
999 | if (ru) { |
999 | ru->countStockedPixel( (*i).count()>0 ); |
1000 | ru->countStockedPixel( (*i).count()>0 ); |
1000 | }
|
1001 | }
|
1001 | }
|
1002 | }
|
1002 | 1003 | ||
1003 | }
|
1004 | }
|
1004 | }
|
1005 | }
|
1005 | 1006 | ||
1006 | /** calculate for each resource unit the stockable area.
|
1007 | /** calculate for each resource unit the stockable area.
|
1007 | "stockability" is determined by the isValid flag of resource units which in turn
|
1008 | "stockability" is determined by the isValid flag of resource units which in turn
|
1008 | is derived from stand grid values.
|
1009 | is derived from stand grid values.
|
1009 | */
|
1010 | */
|
1010 | void Model::calculateStockableArea() |
1011 | void Model::calculateStockableArea() |
1011 | {
|
1012 | {
|
1012 | 1013 | ||
1013 | foreach(ResourceUnit *ru, mRU) { |
1014 | foreach(ResourceUnit *ru, mRU) { |
1014 | // //
|
1015 | // //
|
1015 | // if (ru->id()==-1) {
|
1016 | // if (ru->id()==-1) {
|
1016 | // ru->setStockableArea(0.);
|
1017 | // ru->setStockableArea(0.);
|
1017 | // continue;
|
1018 | // continue;
|
1018 | // }
|
1019 | // }
|
1019 | GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox()); |
1020 | GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox()); |
1020 | int valid=0, total=0; |
1021 | int valid=0, total=0; |
1021 | while (runner.next()) { |
1022 | while (runner.next()) { |
1022 | if ( runner.current()->isValid() ) |
1023 | if ( runner.current()->isValid() ) |
1023 | valid++; |
1024 | valid++; |
1024 | total++; |
1025 | total++; |
1025 | }
|
1026 | }
|
1026 | if (total) { |
1027 | if (total) { |
1027 | ru->setStockableArea( cHeightPixelArea * valid); |
1028 | ru->setStockableArea( cHeightPixelArea * valid); |
1028 | if (valid==0 && ru->id()>-1) { |
1029 | if (valid==0 && ru->id()>-1) { |
1029 | // invalidate this resource unit
|
1030 | // invalidate this resource unit
|
1030 | ru->setID(-1); |
1031 | ru->setID(-1); |
1031 | }
|
1032 | }
|
1032 | if (valid>0 && ru->id()==-1) { |
1033 | if (valid>0 && ru->id()==-1) { |
1033 | qDebug() << "Warning: a resource unit has id=-1 but stockable area (id was set to 0)!!! ru: " << ru->boundingBox() << "with index" << ru->index(); |
1034 | qDebug() << "Warning: a resource unit has id=-1 but stockable area (id was set to 0)!!! ru: " << ru->boundingBox() << "with index" << ru->index(); |
1034 | ru->setID(0); |
1035 | ru->setID(0); |
1035 | // test-code
|
1036 | // test-code
|
1036 | //GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox());
|
1037 | //GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox());
|
1037 | //while (runner.next()) {
|
1038 | //while (runner.next()) {
|
1038 | // qDebug() << mHeightGrid->cellCenterPoint(mHeightGrid->indexOf( runner.current() )) << ": " << runner.current()->isValid();
|
1039 | // qDebug() << mHeightGrid->cellCenterPoint(mHeightGrid->indexOf( runner.current() )) << ": " << runner.current()->isValid();
|
1039 | //}
|
1040 | //}
|
1040 | 1041 | ||
1041 | }
|
1042 | }
|
1042 | } else |
1043 | } else |
1043 | throw IException("calculateStockableArea: resource unit without pixels!"); |
1044 | throw IException("calculateStockableArea: resource unit without pixels!"); |
1044 | 1045 | ||
1045 | }
|
1046 | }
|
1046 | // mark those pixels that are at the edge of a "forest-out-of-area"
|
1047 | // mark those pixels that are at the edge of a "forest-out-of-area"
|
1047 | GridRunner<HeightGridValue> runner(*mHeightGrid, mHeightGrid->metricRect()); |
1048 | GridRunner<HeightGridValue> runner(*mHeightGrid, mHeightGrid->metricRect()); |
1048 | HeightGridValue* neighbors[8]; |
1049 | HeightGridValue* neighbors[8]; |
1049 | while (runner.next()) { |
1050 | while (runner.next()) { |
1050 | if (runner.current()->isForestOutside()) { |
1051 | if (runner.current()->isForestOutside()) { |
1051 | // if the current pixel is a "radiating" border pixel,
|
1052 | // if the current pixel is a "radiating" border pixel,
|
1052 | // then check the neighbors and set a flag if the pixel is a neighbor of a in-project-area pixel.
|
1053 | // then check the neighbors and set a flag if the pixel is a neighbor of a in-project-area pixel.
|
1053 | runner.neighbors8(neighbors); |
1054 | runner.neighbors8(neighbors); |
1054 | for (int i=0;i<8;++i) |
1055 | for (int i=0;i<8;++i) |
1055 | if (neighbors[i] && neighbors[i]->isValid()) |
1056 | if (neighbors[i] && neighbors[i]->isValid()) |
1056 | runner.current()->setIsRadiating(); |
1057 | runner.current()->setIsRadiating(); |
1057 | 1058 | ||
1058 | }
|
1059 | }
|
1059 | }
|
1060 | }
|
1060 | 1061 | ||
1061 | }
|
1062 | }
|
1062 | 1063 | ||
1063 | void Model::initializeGrid() |
1064 | void Model::initializeGrid() |
1064 | {
|
1065 | {
|
1065 | // fill the whole grid with a value of "1."
|
1066 | // fill the whole grid with a value of "1."
|
1066 | mGrid->initialize(1.f); |
1067 | mGrid->initialize(1.f); |
1067 | 1068 | ||
1068 | // apply special values for grid cells border regions where out-of-area cells
|
1069 | // apply special values for grid cells border regions where out-of-area cells
|
1069 | // radiate into the main LIF grid.
|
1070 | // radiate into the main LIF grid.
|
1070 | QPoint p;
|
1071 | QPoint p;
|
1071 | int ix_min, ix_max, iy_min, iy_max, ix_center, iy_center; |
1072 | int ix_min, ix_max, iy_min, iy_max, ix_center, iy_center; |
1072 | const int px_offset = cPxPerHeight / 2; // for 5 px per height grid cell, the offset is 2 |
1073 | const int px_offset = cPxPerHeight / 2; // for 5 px per height grid cell, the offset is 2 |
1073 | const int max_radiate_distance = 7; |
1074 | const int max_radiate_distance = 7; |
1074 | const float step_width = 1.f / (float)max_radiate_distance; |
1075 | const float step_width = 1.f / (float)max_radiate_distance; |
1075 | int c_rad = 0; |
1076 | int c_rad = 0; |
1076 | for (HeightGridValue *hgv=mHeightGrid->begin(); hgv!=mHeightGrid->end(); ++hgv) { |
1077 | for (HeightGridValue *hgv=mHeightGrid->begin(); hgv!=mHeightGrid->end(); ++hgv) { |
1077 | if (hgv->isRadiating()) { |
1078 | if (hgv->isRadiating()) { |
1078 | p=mHeightGrid->indexOf(hgv); |
1079 | p=mHeightGrid->indexOf(hgv); |
1079 | ix_min = p.x() * cPxPerHeight - max_radiate_distance + px_offset; |
1080 | ix_min = p.x() * cPxPerHeight - max_radiate_distance + px_offset; |
1080 | ix_max = ix_min + 2*max_radiate_distance + 1; |
1081 | ix_max = ix_min + 2*max_radiate_distance + 1; |
1081 | ix_center = ix_min + max_radiate_distance; |
1082 | ix_center = ix_min + max_radiate_distance; |
1082 | iy_min = p.y() * cPxPerHeight - max_radiate_distance + px_offset; |
1083 | iy_min = p.y() * cPxPerHeight - max_radiate_distance + px_offset; |
1083 | iy_max = iy_min + 2*max_radiate_distance + 1; |
1084 | iy_max = iy_min + 2*max_radiate_distance + 1; |
1084 | iy_center = iy_min + max_radiate_distance; |
1085 | iy_center = iy_min + max_radiate_distance; |
1085 | for (int y=iy_min; y<=iy_max; ++y) { |
1086 | for (int y=iy_min; y<=iy_max; ++y) { |
1086 | for (int x=ix_min; x<=ix_max; ++x) { |
1087 | for (int x=ix_min; x<=ix_max; ++x) { |
1087 | if (!mGrid->isIndexValid(x,y) || !(*mHeightGrid)(x/cPxPerHeight, y/cPxPerHeight).isValid()) |
1088 | if (!mGrid->isIndexValid(x,y) || !(*mHeightGrid)(x/cPxPerHeight, y/cPxPerHeight).isValid()) |
1088 | continue; |
1089 | continue; |
1089 | float value = qMax(qAbs(x-ix_center), qAbs(y-iy_center)) * step_width; |
1090 | float value = qMax(qAbs(x-ix_center), qAbs(y-iy_center)) * step_width; |
1090 | float &v = mGrid->valueAtIndex(x, y); |
1091 | float &v = mGrid->valueAtIndex(x, y); |
1091 | if (value>=0.f && v>value) |
1092 | if (value>=0.f && v>value) |
1092 | v = value; |
1093 | v = value; |
1093 | }
|
1094 | }
|
1094 | }
|
1095 | }
|
1095 | c_rad++; |
1096 | c_rad++; |
1096 | }
|
1097 | }
|
1097 | }
|
1098 | }
|
1098 | if (logLevelDebug()) |
1099 | if (logLevelDebug()) |
1099 | qDebug() << "initialize grid:" << c_rad << "radiating pixels..."; |
1100 | qDebug() << "initialize grid:" << c_rad << "radiating pixels..."; |
1100 | 1101 | ||
1101 | }
|
1102 | }
|
1102 | 1103 | ||
1103 | 1104 | ||
1104 | /// Force the creation of stand statistics.
|
1105 | /// Force the creation of stand statistics.
|
1105 | /// - stocked area (for resourceunit-areas)
|
1106 | /// - stocked area (for resourceunit-areas)
|
1106 | /// - ru - statistics
|
1107 | /// - ru - statistics
|
1107 | void Model::createStandStatistics() |
1108 | void Model::createStandStatistics() |
1108 | {
|
1109 | {
|
1109 | calculateStockedArea(); |
1110 | calculateStockedArea(); |
1110 | foreach(ResourceUnit *ru, mRU) { |
1111 | foreach(ResourceUnit *ru, mRU) { |
1111 | ru->addTreeAgingForAllTrees(); |
1112 | ru->addTreeAgingForAllTrees(); |
1112 | ru->createStandStatistics(); |
1113 | ru->createStandStatistics(); |
1113 | }
|
1114 | }
|
1114 | }
|
1115 | }
|
1115 | 1116 | ||
1116 | void Model::cleanTreeLists() |
1117 | void Model::cleanTreeLists() |
1117 | {
|
1118 | {
|
1118 | foreach(ResourceUnit *ru, GlobalSettings::instance()->model()->ruList()) { |
1119 | foreach(ResourceUnit *ru, GlobalSettings::instance()->model()->ruList()) { |
1119 | if (ru->hasDiedTrees()) { |
1120 | if (ru->hasDiedTrees()) { |
1120 | ru->cleanTreeList(); |
1121 | ru->cleanTreeList(); |
1121 | ru->recreateStandStatistics(); |
1122 | ru->recreateStandStatistics(); |
1122 | }
|
1123 | }
|
1123 | }
|
1124 | }
|
1124 | }
|
1125 | }
|
1125 | 1126 | ||
1126 | 1127 | ||
1127 | 1128 |