Rev 1085 | Rev 1089 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1085 | Rev 1087 | ||
---|---|---|---|
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)); |
- | |
358 | mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used. |
- | |
359 | mModules->setupResourceUnit( *p ); |
- | |
- | 357 | if (*p) { |
|
- | 358 | QRectF r = mRUmap.cellRect(mRUmap.indexOf(p)); |
|
- | 359 | mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used. |
|
- | 360 | mModules->setupResourceUnit( *p ); |
|
- | 361 | }
|
|
360 | }
|
362 | }
|
361 | }
|
363 | }
|
362 | 364 | ||
363 | // setup of scripting environment
|
365 | // setup of scripting environment
|
364 | ScriptGlobal::setupGlobalScripting(); |
366 | ScriptGlobal::setupGlobalScripting(); |
365 | 367 | ||
366 | // setup the helper that does the multithreading
|
368 | // setup the helper that does the multithreading
|
367 | threadRunner.setup(valid_rus); |
369 | threadRunner.setup(valid_rus); |
368 | threadRunner.setMultithreading(GlobalSettings::instance()->settings().valueBool("system.settings.multithreading")); |
370 | threadRunner.setMultithreading(GlobalSettings::instance()->settings().valueBool("system.settings.multithreading")); |
369 | threadRunner.print(); |
371 | threadRunner.print(); |
370 | 372 | ||
371 | 373 | ||
372 | } else { |
374 | } else { |
373 | throw IException("resourceUnitsAsGrid MUST be set to true - at least currently :)"); |
375 | throw IException("resourceUnitsAsGrid MUST be set to true - at least currently :)"); |
374 | }
|
376 | }
|
375 | mSetup = true; |
377 | mSetup = true; |
376 | }
|
378 | }
|
377 | 379 | ||
378 | 380 | ||
379 | /** clear() frees all ressources allocated with the run of a simulation.
|
381 | /** clear() frees all ressources allocated with the run of a simulation.
|
380 | 382 | ||
381 | */
|
383 | */
|
382 | void Model::clear() |
384 | void Model::clear() |
383 | {
|
385 | {
|
384 | mSetup = false; |
386 | mSetup = false; |
385 | qDebug() << "Model clear: attempting to clear" << mRU.count() << "RU, " << mSpeciesSets.count() << "SpeciesSets."; |
387 | qDebug() << "Model clear: attempting to clear" << mRU.count() << "RU, " << mSpeciesSets.count() << "SpeciesSets."; |
386 | // clear ressource units
|
388 | // clear ressource units
|
387 | qDeleteAll(mRU); // delete ressource units (and trees) |
389 | qDeleteAll(mRU); // delete ressource units (and trees) |
388 | mRU.clear(); |
390 | mRU.clear(); |
389 | 391 | ||
390 | qDeleteAll(mSpeciesSets); // delete species sets |
392 | qDeleteAll(mSpeciesSets); // delete species sets |
391 | mSpeciesSets.clear(); |
393 | mSpeciesSets.clear(); |
392 | 394 | ||
393 | // delete climate data
|
395 | // delete climate data
|
394 | qDeleteAll(mClimates); |
396 | qDeleteAll(mClimates); |
395 | 397 | ||
396 | // delete the grids
|
398 | // delete the grids
|
397 | if (mGrid) |
399 | if (mGrid) |
398 | delete mGrid; |
400 | delete mGrid; |
399 | if (mHeightGrid) |
401 | if (mHeightGrid) |
400 | delete mHeightGrid; |
402 | delete mHeightGrid; |
401 | if (mManagement) |
403 | if (mManagement) |
402 | delete mManagement; |
404 | delete mManagement; |
403 | if (mEnvironment) |
405 | if (mEnvironment) |
404 | delete mEnvironment; |
406 | delete mEnvironment; |
405 | if (mTimeEvents) |
407 | if (mTimeEvents) |
406 | delete mTimeEvents; |
408 | delete mTimeEvents; |
407 | if (mStandGrid) |
409 | if (mStandGrid) |
408 | delete mStandGrid; |
410 | delete mStandGrid; |
409 | if (mModules) |
411 | if (mModules) |
410 | delete mModules; |
412 | delete mModules; |
411 | if (mDEM) |
413 | if (mDEM) |
412 | delete mDEM; |
414 | delete mDEM; |
413 | if (mGrassCover) |
415 | if (mGrassCover) |
414 | delete mGrassCover; |
416 | delete mGrassCover; |
415 | if (mABEManagement) |
417 | if (mABEManagement) |
416 | delete mABEManagement; |
418 | delete mABEManagement; |
417 | 419 | ||
418 | mGrid = 0; |
420 | mGrid = 0; |
419 | mHeightGrid = 0; |
421 | mHeightGrid = 0; |
420 | mManagement = 0; |
422 | mManagement = 0; |
421 | mEnvironment = 0; |
423 | mEnvironment = 0; |
422 | mTimeEvents = 0; |
424 | mTimeEvents = 0; |
423 | mStandGrid = 0; |
425 | mStandGrid = 0; |
424 | mModules = 0; |
426 | mModules = 0; |
425 | mDEM = 0; |
427 | mDEM = 0; |
426 | mGrassCover = 0; |
428 | mGrassCover = 0; |
427 | mABEManagement = 0; |
429 | mABEManagement = 0; |
428 | 430 | ||
429 | GlobalSettings::instance()->outputManager()->close(); |
431 | GlobalSettings::instance()->outputManager()->close(); |
430 | 432 | ||
431 | qDebug() << "Model ressources freed."; |
433 | qDebug() << "Model ressources freed."; |
432 | }
|
434 | }
|
433 | 435 | ||
434 | /** Setup of the Simulation.
|
436 | /** Setup of the Simulation.
|
435 | This really creates the simulation environment and does the setup of various aspects.
|
437 | This really creates the simulation environment and does the setup of various aspects.
|
436 | */
|
438 | */
|
437 | void Model::loadProject() |
439 | void Model::loadProject() |
438 | {
|
440 | {
|
439 | DebugTimer dt("load project"); |
441 | DebugTimer dt("load project"); |
440 | GlobalSettings *g = GlobalSettings::instance(); |
442 | GlobalSettings *g = GlobalSettings::instance(); |
441 | g->printDirecories(); |
443 | g->printDirecories(); |
442 | const XmlHelper &xml = g->settings(); |
444 | const XmlHelper &xml = g->settings(); |
443 | 445 | ||
444 | g->clearDatabaseConnections(); |
446 | g->clearDatabaseConnections(); |
445 | // database connections: reset
|
447 | // database connections: reset
|
446 | GlobalSettings::instance()->clearDatabaseConnections(); |
448 | GlobalSettings::instance()->clearDatabaseConnections(); |
447 | // input and climate connection
|
449 | // input and climate connection
|
448 | // see initOutputDatabase() for output database
|
450 | // see initOutputDatabase() for output database
|
449 | QString dbPath = g->path( xml.value("system.database.in"), "database"); |
451 | QString dbPath = g->path( xml.value("system.database.in"), "database"); |
450 | GlobalSettings::instance()->setupDatabaseConnection("in", dbPath, true); |
452 | GlobalSettings::instance()->setupDatabaseConnection("in", dbPath, true); |
451 | dbPath = g->path( xml.value("system.database.climate"), "database"); |
453 | dbPath = g->path( xml.value("system.database.climate"), "database"); |
452 | GlobalSettings::instance()->setupDatabaseConnection("climate", dbPath, true); |
454 | GlobalSettings::instance()->setupDatabaseConnection("climate", dbPath, true); |
453 | 455 | ||
454 | mSettings.loadModelSettings(); |
456 | mSettings.loadModelSettings(); |
455 | mSettings.print(); |
457 | mSettings.print(); |
456 | // random seed: if stored value is <> 0, use this as the random seed (and produce hence always an equal sequence of random numbers)
|
458 | // random seed: if stored value is <> 0, use this as the random seed (and produce hence always an equal sequence of random numbers)
|
457 | uint seed = xml.value("system.settings.randomSeed","0").toUInt(); |
459 | uint seed = xml.value("system.settings.randomSeed","0").toUInt(); |
458 | RandomGenerator::setup(RandomGenerator::ergMersenneTwister, seed); // use the MersenneTwister as default |
460 | RandomGenerator::setup(RandomGenerator::ergMersenneTwister, seed); // use the MersenneTwister as default |
459 | // linearization of expressions: if true *and* linearize() is explicitely called, then
|
461 | // linearization of expressions: if true *and* linearize() is explicitely called, then
|
460 | // function results will be cached over a defined range of values.
|
462 | // function results will be cached over a defined range of values.
|
461 | bool do_linearization = xml.valueBool("system.settings.expressionLinearizationEnabled", false); |
463 | bool do_linearization = xml.valueBool("system.settings.expressionLinearizationEnabled", false); |
462 | Expression::setLinearizationEnabled(do_linearization); |
464 | Expression::setLinearizationEnabled(do_linearization); |
463 | if (do_linearization) |
465 | if (do_linearization) |
464 | qDebug() << "The linearization of certains expressions is enabled (performance optimization)."; |
466 | qDebug() << "The linearization of certains expressions is enabled (performance optimization)."; |
465 | 467 | ||
466 | // log level
|
468 | // log level
|
467 | QString log_level = xml.value("system.settings.logLevel", "debug").toLower(); |
469 | QString log_level = xml.value("system.settings.logLevel", "debug").toLower(); |
468 | if (log_level=="debug") setLogLevel(0); |
470 | if (log_level=="debug") setLogLevel(0); |
469 | if (log_level=="info") setLogLevel(1); |
471 | if (log_level=="info") setLogLevel(1); |
470 | if (log_level=="warning") setLogLevel(2); |
472 | if (log_level=="warning") setLogLevel(2); |
471 | if (log_level=="error") setLogLevel(3); |
473 | if (log_level=="error") setLogLevel(3); |
472 | 474 | ||
473 | // snag dynamics / soil model enabled? (info used during setup of world)
|
475 | // snag dynamics / soil model enabled? (info used during setup of world)
|
474 | changeSettings().carbonCycleEnabled = xml.valueBool("model.settings.carbonCycleEnabled", false); |
476 | changeSettings().carbonCycleEnabled = xml.valueBool("model.settings.carbonCycleEnabled", false); |
475 | // class size of snag classes
|
477 | // class size of snag classes
|
476 | Snag::setupThresholds(xml.valueDouble("model.settings.soil.swdDBHClass12"), |
478 | Snag::setupThresholds(xml.valueDouble("model.settings.soil.swdDBHClass12"), |
477 | xml.valueDouble("model.settings.soil.swdDBHClass23")); |
479 | xml.valueDouble("model.settings.soil.swdDBHClass23")); |
478 | 480 | ||
479 | // setup of modules
|
481 | // setup of modules
|
480 | if (mModules) |
482 | if (mModules) |
481 | delete mModules; |
483 | delete mModules; |
482 | mModules = new Modules(); |
484 | mModules = new Modules(); |
483 | 485 | ||
484 | setupSpace(); |
486 | setupSpace(); |
485 | if (mRU.isEmpty()) |
487 | if (mRU.isEmpty()) |
486 | throw IException("Setup of Model: no resource units present!"); |
488 | throw IException("Setup of Model: no resource units present!"); |
487 | 489 | ||
488 | // (3) additional issues
|
490 | // (3) additional issues
|
489 | // (3.1) load javascript code into the engine
|
491 | // (3.1) load javascript code into the engine
|
490 | QString script_file = xml.value("system.javascript.fileName"); |
492 | QString script_file = xml.value("system.javascript.fileName"); |
491 | if (!script_file.isEmpty()) { |
493 | if (!script_file.isEmpty()) { |
492 | script_file = g->path(script_file, "script"); |
494 | script_file = g->path(script_file, "script"); |
493 | ScriptGlobal::loadScript(script_file); |
495 | ScriptGlobal::loadScript(script_file); |
494 | g->controller()->setLoadedJavascriptFile(script_file); |
496 | g->controller()->setLoadedJavascriptFile(script_file); |
495 | }
|
497 | }
|
496 | 498 | ||
497 | // (3.2) setup of regeneration
|
499 | // (3.2) setup of regeneration
|
498 | changeSettings().regenerationEnabled = xml.valueBool("model.settings.regenerationEnabled", false); |
500 | changeSettings().regenerationEnabled = xml.valueBool("model.settings.regenerationEnabled", false); |
499 | if (settings().regenerationEnabled) { |
501 | if (settings().regenerationEnabled) { |
500 | foreach(SpeciesSet *ss, mSpeciesSets) |
502 | foreach(SpeciesSet *ss, mSpeciesSets) |
501 | ss->setupRegeneration(); |
503 | ss->setupRegeneration(); |
502 | }
|
504 | }
|
503 | Sapling::setRecruitmentVariation(xml.valueDouble("model.settings.seedDispersal.recruitmentDimensionVariation",0.1)); |
505 | Sapling::setRecruitmentVariation(xml.valueDouble("model.settings.seedDispersal.recruitmentDimensionVariation",0.1)); |
504 | 506 | ||
505 | // (3.3) management
|
507 | // (3.3) management
|
506 | bool use_abe = xml.valueBool("model.management.abeEnabled"); |
508 | bool use_abe = xml.valueBool("model.management.abeEnabled"); |
507 | if (use_abe) { |
509 | if (use_abe) { |
508 | // use the agent based forest management engine
|
510 | // use the agent based forest management engine
|
509 | mABEManagement = new ABE::ForestManagementEngine(); |
511 | mABEManagement = new ABE::ForestManagementEngine(); |
510 | // setup of ABE after loading of trees.
|
512 | // setup of ABE after loading of trees.
|
511 | 513 | ||
512 | }
|
514 | }
|
513 | // use the standard management
|
515 | // use the standard management
|
514 | QString mgmtFile = xml.value("model.management.file"); |
516 | QString mgmtFile = xml.value("model.management.file"); |
515 | if (!mgmtFile.isEmpty() && xml.valueBool("model.management.enabled")) { |
517 | if (!mgmtFile.isEmpty() && xml.valueBool("model.management.enabled")) { |
516 | mManagement = new Management(); |
518 | mManagement = new Management(); |
517 | QString path = GlobalSettings::instance()->path(mgmtFile, "script"); |
519 | QString path = GlobalSettings::instance()->path(mgmtFile, "script"); |
518 | mManagement->loadScript(path); |
520 | mManagement->loadScript(path); |
519 | qDebug() << "setup management using script" << path; |
521 | qDebug() << "setup management using script" << path; |
520 | }
|
522 | }
|
521 | 523 | ||
522 | 524 | ||
523 | 525 | ||
524 | }
|
526 | }
|
525 | 527 | ||
526 | void Model::reloadABE() |
528 | void Model::reloadABE() |
527 | {
|
529 | {
|
528 | // delete firest
|
530 | // delete firest
|
529 | if (mABEManagement) |
531 | if (mABEManagement) |
530 | delete mABEManagement; |
532 | delete mABEManagement; |
531 | mABEManagement = new ABE::ForestManagementEngine(); |
533 | mABEManagement = new ABE::ForestManagementEngine(); |
532 | // and setup
|
534 | // and setup
|
533 | mABEManagement->setup(); |
535 | mABEManagement->setup(); |
534 | mABEManagement->runOnInit(); |
536 | mABEManagement->runOnInit(); |
535 | 537 | ||
536 | mABEManagement->initialize(); |
538 | mABEManagement->initialize(); |
537 | 539 | ||
538 | }
|
540 | }
|
539 | 541 | ||
540 | 542 | ||
541 | ResourceUnit *Model::ru(QPointF coord) |
543 | ResourceUnit *Model::ru(QPointF coord) |
542 | {
|
544 | {
|
543 | if (!mRUmap.isEmpty() && mRUmap.coordValid(coord)) |
545 | if (!mRUmap.isEmpty() && mRUmap.coordValid(coord)) |
544 | return mRUmap.valueAt(coord); |
546 | return mRUmap.valueAt(coord); |
545 | return ru(); // default RU if there is only one |
547 | return ru(); // default RU if there is only one |
546 | }
|
548 | }
|
547 | 549 | ||
548 | void Model::initOutputDatabase() |
550 | void Model::initOutputDatabase() |
549 | {
|
551 | {
|
550 | GlobalSettings *g = GlobalSettings::instance(); |
552 | GlobalSettings *g = GlobalSettings::instance(); |
551 | QString dbPath = g->path(g->settings().value("system.database.out"), "output"); |
553 | QString dbPath = g->path(g->settings().value("system.database.out"), "output"); |
552 | // create run-metadata
|
554 | // create run-metadata
|
553 | int maxid = SqlHelper::queryValue("select max(id) from runs", g->dbin()).toInt(); |
555 | int maxid = SqlHelper::queryValue("select max(id) from runs", g->dbin()).toInt(); |
554 | 556 | ||
555 | maxid++; |
557 | maxid++; |
556 | QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"); |
558 | QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"); |
557 | SqlHelper::executeSql(QString("insert into runs (id, timestamp) values (%1, '%2')").arg(maxid).arg(timestamp), g->dbin()); |
559 | SqlHelper::executeSql(QString("insert into runs (id, timestamp) values (%1, '%2')").arg(maxid).arg(timestamp), g->dbin()); |
558 | // replace path information
|
560 | // replace path information
|
559 | dbPath.replace("$id$", QString::number(maxid)); |
561 | dbPath.replace("$id$", QString::number(maxid)); |
560 | dbPath.replace("$date$", timestamp); |
562 | dbPath.replace("$date$", timestamp); |
561 | // setup final path
|
563 | // setup final path
|
562 | g->setupDatabaseConnection("out", dbPath, false); |
564 | g->setupDatabaseConnection("out", dbPath, false); |
563 | 565 | ||
564 | }
|
566 | }
|
565 | 567 | ||
566 | 568 | ||
567 | /// multithreaded running function for the resource unit level establishment
|
569 | /// multithreaded running function for the resource unit level establishment
|
568 | ResourceUnit *nc_sapling_growth_establishment(ResourceUnit *unit) |
570 | ResourceUnit *nc_sapling_growth_establishment(ResourceUnit *unit) |
569 | {
|
571 | {
|
570 | try { |
572 | try { |
571 | { // DebugTimer t("nc_saplingGrowth"); t.setSilent(); |
573 | { // DebugTimer t("nc_saplingGrowth"); t.setSilent(); |
572 | // define a height map for the current resource unit on the stack
|
574 | // define a height map for the current resource unit on the stack
|
573 | float sapling_map[cPxPerRU*cPxPerRU]; |
575 | float sapling_map[cPxPerRU*cPxPerRU]; |
574 | // set the map and initialize it:
|
576 | // set the map and initialize it:
|
575 | unit->setSaplingHeightMap(sapling_map); |
577 | unit->setSaplingHeightMap(sapling_map); |
576 | 578 | ||
577 | 579 | ||
578 | // (1) calculate the growth of (already established) saplings (populate sapling map)
|
580 | // (1) calculate the growth of (already established) saplings (populate sapling map)
|
579 | QList<ResourceUnitSpecies*>::const_iterator rus; |
581 | QList<ResourceUnitSpecies*>::const_iterator rus; |
580 | for (rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus) |
582 | for (rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus) |
581 | (*rus)->calclulateSaplingGrowth(); |
583 | (*rus)->calclulateSaplingGrowth(); |
582 | 584 | ||
583 | } { // DebugTimer t("nc_Establishment"); t.setSilent(); |
585 | } { // DebugTimer t("nc_Establishment"); t.setSilent(); |
584 | 586 | ||
585 | // (2) calculate the establishment probabilities of new saplings
|
587 | // (2) calculate the establishment probabilities of new saplings
|
586 | for (QList<ResourceUnitSpecies*>::const_iterator rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus) |
588 | for (QList<ResourceUnitSpecies*>::const_iterator rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus) |
587 | (*rus)->calculateEstablishment(); |
589 | (*rus)->calculateEstablishment(); |
588 | 590 | ||
589 | }
|
591 | }
|
590 | 592 | ||
591 | } catch (const IException& e) { |
593 | } catch (const IException& e) { |
592 | GlobalSettings::instance()->controller()->throwError(e.message()); |
594 | GlobalSettings::instance()->controller()->throwError(e.message()); |
593 | }
|
595 | }
|
594 | 596 | ||
595 | unit->setSaplingHeightMap(0); // invalidate again |
597 | unit->setSaplingHeightMap(0); // invalidate again |
596 | return unit; |
598 | return unit; |
597 | 599 | ||
598 | }
|
600 | }
|
599 | 601 | ||
600 | 602 | ||
601 | /// multithreaded execution of the carbon cycle routine
|
603 | /// multithreaded execution of the carbon cycle routine
|
602 | ResourceUnit *nc_carbonCycle(ResourceUnit *unit) |
604 | ResourceUnit *nc_carbonCycle(ResourceUnit *unit) |
603 | {
|
605 | {
|
604 | try { |
606 | try { |
605 | // (1) do calculations on snag dynamics for the resource unit
|
607 | // (1) do calculations on snag dynamics for the resource unit
|
606 | unit->calculateCarbonCycle(); |
608 | unit->calculateCarbonCycle(); |
607 | // (2) do the soil carbon and nitrogen dynamics calculations (ICBM/2N)
|
609 | // (2) do the soil carbon and nitrogen dynamics calculations (ICBM/2N)
|
608 | } catch (const IException& e) { |
610 | } catch (const IException& e) { |
609 | GlobalSettings::instance()->controller()->throwError(e.message()); |
611 | GlobalSettings::instance()->controller()->throwError(e.message()); |
610 | }
|
612 | }
|
611 | 613 | ||
612 | return unit; |
614 | return unit; |
613 | }
|
615 | }
|
614 | 616 | ||
615 | /// beforeRun performs several steps before the models starts running.
|
617 | /// beforeRun performs several steps before the models starts running.
|
616 | /// inter alia: * setup of the stands
|
618 | /// inter alia: * setup of the stands
|
617 | /// * setup of the climates
|
619 | /// * setup of the climates
|
618 | void Model::beforeRun() |
620 | void Model::beforeRun() |
619 | {
|
621 | {
|
620 | // setup outputs
|
622 | // setup outputs
|
621 | // setup output database
|
623 | // setup output database
|
622 | if (GlobalSettings::instance()->dbout().isOpen()) |
624 | if (GlobalSettings::instance()->dbout().isOpen()) |
623 | GlobalSettings::instance()->dbout().close(); |
625 | GlobalSettings::instance()->dbout().close(); |
624 | initOutputDatabase(); |
626 | initOutputDatabase(); |
625 | GlobalSettings::instance()->outputManager()->setup(); |
627 | GlobalSettings::instance()->outputManager()->setup(); |
626 | GlobalSettings::instance()->clearDebugLists(); |
628 | GlobalSettings::instance()->clearDebugLists(); |
627 | 629 | ||
628 | // initialize stands
|
630 | // initialize stands
|
629 | StandLoader loader(this); |
631 | StandLoader loader(this); |
630 | {
|
632 | {
|
631 | DebugTimer loadtrees("load trees"); |
633 | DebugTimer loadtrees("load trees"); |
632 | loader.processInit(); |
634 | loader.processInit(); |
633 | }
|
635 | }
|
634 | // initalization of ABE
|
636 | // initalization of ABE
|
635 | if (mABEManagement) { |
637 | if (mABEManagement) { |
636 | mABEManagement->setup(); |
638 | mABEManagement->setup(); |
637 | mABEManagement->runOnInit(); |
639 | mABEManagement->runOnInit(); |
638 | }
|
640 | }
|
639 | 641 | ||
640 | // load climate
|
642 | // load climate
|
641 | {
|
643 | {
|
642 | if (logLevelDebug()) qDebug() << "attempting to load climate..." ; |
644 | if (logLevelDebug()) qDebug() << "attempting to load climate..." ; |
643 | DebugTimer loadclim("load climate"); |
645 | DebugTimer loadclim("load climate"); |
644 | foreach(Climate *c, mClimates) { |
646 | foreach(Climate *c, mClimates) { |
645 | if (!c->isSetup()) |
647 | if (!c->isSetup()) |
646 | c->setup(); |
648 | c->setup(); |
647 | }
|
649 | }
|
648 | // load the first year of the climate database
|
650 | // load the first year of the climate database
|
649 | foreach(Climate *c, mClimates) |
651 | foreach(Climate *c, mClimates) |
650 | c->nextYear(); |
652 | c->nextYear(); |
651 | 653 | ||
652 | }
|
654 | }
|
653 | 655 | ||
654 | 656 | ||
655 | { DebugTimer loadinit("load standstatistics"); |
657 | { DebugTimer loadinit("load standstatistics"); |
656 | if (logLevelDebug()) qDebug() << "attempting to calculate initial stand statistics (incl. apply and read pattern)..." ; |
658 | if (logLevelDebug()) qDebug() << "attempting to calculate initial stand statistics (incl. apply and read pattern)..." ; |
657 | Tree::setGrid(mGrid, mHeightGrid); |
659 | Tree::setGrid(mGrid, mHeightGrid); |
658 | // debugCheckAllTrees(); // introduced for debugging session (2012-04-06)
|
660 | // debugCheckAllTrees(); // introduced for debugging session (2012-04-06)
|
659 | applyPattern(); |
661 | applyPattern(); |
660 | readPattern(); |
662 | readPattern(); |
661 | loader.processAfterInit(); // e.g. initialization of saplings |
663 | loader.processAfterInit(); // e.g. initialization of saplings |
662 | 664 | ||
663 | // force the compilation of initial stand statistics
|
665 | // force the compilation of initial stand statistics
|
664 | createStandStatistics(); |
666 | createStandStatistics(); |
665 | }
|
667 | }
|
666 | 668 | ||
667 | // initalization of ABE (now all stands are properly set up)
|
669 | // initalization of ABE (now all stands are properly set up)
|
668 | if (mABEManagement) { |
670 | if (mABEManagement) { |
669 | mABEManagement->initialize(); |
671 | mABEManagement->initialize(); |
670 | }
|
672 | }
|
671 | 673 | ||
672 | 674 | ||
673 | // outputs to create with inital state (without any growth) are called here:
|
675 | // outputs to create with inital state (without any growth) are called here:
|
674 | GlobalSettings::instance()->setCurrentYear(0); // set clock to "0" (for outputs with initial state) |
676 | GlobalSettings::instance()->setCurrentYear(0); // set clock to "0" (for outputs with initial state) |
675 | 677 | ||
676 | GlobalSettings::instance()->outputManager()->execute("stand"); // year=0 |
678 | GlobalSettings::instance()->outputManager()->execute("stand"); // year=0 |
677 | GlobalSettings::instance()->outputManager()->execute("landscape"); // year=0 |
679 | GlobalSettings::instance()->outputManager()->execute("landscape"); // year=0 |
678 | GlobalSettings::instance()->outputManager()->execute("sapling"); // year=0 |
680 | GlobalSettings::instance()->outputManager()->execute("sapling"); // year=0 |
679 | GlobalSettings::instance()->outputManager()->execute("tree"); // year=0 |
681 | GlobalSettings::instance()->outputManager()->execute("tree"); // year=0 |
680 | GlobalSettings::instance()->outputManager()->execute("dynamicstand"); // year=0 |
682 | GlobalSettings::instance()->outputManager()->execute("dynamicstand"); // year=0 |
681 | 683 | ||
682 | GlobalSettings::instance()->setCurrentYear(1); // set to first year |
684 | GlobalSettings::instance()->setCurrentYear(1); // set to first year |
683 | 685 | ||
684 | }
|
686 | }
|
685 | 687 | ||
686 | /** Main model runner.
|
688 | /** Main model runner.
|
687 | The sequence of actions is as follows:
|
689 | The sequence of actions is as follows:
|
688 | (1) Load the climate of the new year
|
690 | (1) Load the climate of the new year
|
689 | (2) Reset statistics for resource unit as well as for dead/managed trees
|
691 | (2) Reset statistics for resource unit as well as for dead/managed trees
|
690 | (3) Invoke Management.
|
692 | (3) Invoke Management.
|
691 | (4) *after* that, calculate Light patterns
|
693 | (4) *after* that, calculate Light patterns
|
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)
|
694 | (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)
|
693 | (6) execute Regeneration
|
695 | (6) execute Regeneration
|
694 | (7) invoke disturbance modules
|
696 | (7) invoke disturbance modules
|
695 | (8) calculate statistics for the year
|
697 | (8) calculate statistics for the year
|
696 | (9) write database outputs
|
698 | (9) write database outputs
|
697 | */
|
699 | */
|
698 | void Model::runYear() |
700 | void Model::runYear() |
699 | {
|
701 | {
|
700 | DebugTimer t("Model::runYear()"); |
702 | DebugTimer t("Model::runYear()"); |
701 | GlobalSettings::instance()->systemStatistics()->reset(); |
703 | GlobalSettings::instance()->systemStatistics()->reset(); |
702 | RandomGenerator::checkGenerator(); // see if we need to generate new numbers... |
704 | RandomGenerator::checkGenerator(); // see if we need to generate new numbers... |
703 | // initalization at start of year for external modules
|
705 | // initalization at start of year for external modules
|
704 | mModules->yearBegin(); |
706 | mModules->yearBegin(); |
705 | 707 | ||
706 | // execute scheduled events for the current year
|
708 | // execute scheduled events for the current year
|
707 | if (mTimeEvents) |
709 | if (mTimeEvents) |
708 | mTimeEvents->run(); |
710 | mTimeEvents->run(); |
709 | 711 | ||
710 | // load the next year of the climate database (except for the first year - the first climate year is loaded immediately
|
712 | // load the next year of the climate database (except for the first year - the first climate year is loaded immediately
|
711 | if (GlobalSettings::instance()->currentYear()>1) { |
713 | if (GlobalSettings::instance()->currentYear()>1) { |
712 | foreach(Climate *c, mClimates) |
714 | foreach(Climate *c, mClimates) |
713 | c->nextYear(); |
715 | c->nextYear(); |
714 | }
|
716 | }
|
715 | 717 | ||
716 | // reset statistics
|
718 | // reset statistics
|
717 | foreach(ResourceUnit *ru, mRU) |
719 | foreach(ResourceUnit *ru, mRU) |
718 | ru->newYear(); |
720 | ru->newYear(); |
719 | 721 | ||
720 | foreach(SpeciesSet *set, mSpeciesSets) |
722 | foreach(SpeciesSet *set, mSpeciesSets) |
721 | set->newYear(); |
723 | set->newYear(); |
722 | // management classic
|
724 | // management classic
|
723 | if (mManagement) { |
725 | if (mManagement) { |
724 | DebugTimer t("management"); |
726 | DebugTimer t("management"); |
725 | mManagement->run(); |
727 | mManagement->run(); |
726 | GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed(); |
728 | GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed(); |
727 | }
|
729 | }
|
728 | // ... or ABE (the agent based variant)
|
730 | // ... or ABE (the agent based variant)
|
729 | if (mABEManagement) { |
731 | if (mABEManagement) { |
730 | DebugTimer t("ABE:run"); |
732 | DebugTimer t("ABE:run"); |
731 | mABEManagement->run(); |
733 | mABEManagement->run(); |
732 | GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed(); |
734 | GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed(); |
733 | }
|
735 | }
|
734 | 736 | ||
735 | // if trees are dead/removed because of management, the tree lists
|
737 | // if trees are dead/removed because of management, the tree lists
|
736 | // need to be cleaned (and the statistics need to be recreated)
|
738 | // need to be cleaned (and the statistics need to be recreated)
|
737 | cleanTreeLists(); |
739 | cleanTreeLists(); |
738 | 740 | ||
739 | // process a cycle of individual growth
|
741 | // process a cycle of individual growth
|
740 | applyPattern(); // create Light Influence Patterns |
742 | applyPattern(); // create Light Influence Patterns |
741 | readPattern(); // readout light state of individual trees |
743 | readPattern(); // readout light state of individual trees |
742 | grow(); // let the trees grow (growth on stand-level, tree-level, mortality) |
744 | grow(); // let the trees grow (growth on stand-level, tree-level, mortality) |
743 | mGrassCover->execute(); // evaluate the grass / herb cover (and its effect on regeneration) |
745 | mGrassCover->execute(); // evaluate the grass / herb cover (and its effect on regeneration) |
744 | 746 | ||
745 | // regeneration
|
747 | // regeneration
|
746 | if (settings().regenerationEnabled) { |
748 | if (settings().regenerationEnabled) { |
747 | // seed dispersal
|
749 | // seed dispersal
|
748 | DebugTimer tseed("Regeneration and Establishment"); |
750 | DebugTimer tseed("Regeneration and Establishment"); |
749 | foreach(SpeciesSet *set, mSpeciesSets) |
751 | foreach(SpeciesSet *set, mSpeciesSets) |
750 | set->regeneration(); // parallel execution for each species set |
752 | set->regeneration(); // parallel execution for each species set |
751 | 753 | ||
752 | GlobalSettings::instance()->systemStatistics()->tSeedDistribution+=tseed.elapsed(); |
754 | GlobalSettings::instance()->systemStatistics()->tSeedDistribution+=tseed.elapsed(); |
753 | // establishment
|
755 | // establishment
|
754 | Sapling::updateBrowsingPressure(); |
756 | Sapling::updateBrowsingPressure(); |
755 | 757 | ||
756 | { DebugTimer t("saplingGrowthEstablishment"); |
758 | { DebugTimer t("saplingGrowthEstablishment"); |
757 | executePerResourceUnit( nc_sapling_growth_establishment, false /* true: force single thraeded operation */); |
759 | executePerResourceUnit( nc_sapling_growth_establishment, false /* true: force single thraeded operation */); |
758 | GlobalSettings::instance()->systemStatistics()->tSaplingAndEstablishment+=t.elapsed(); |
760 | GlobalSettings::instance()->systemStatistics()->tSaplingAndEstablishment+=t.elapsed(); |
759 | }
|
761 | }
|
760 | 762 | ||
761 | Establishment::debugInfo(); // debug test |
763 | Establishment::debugInfo(); // debug test |
762 | 764 | ||
763 | }
|
765 | }
|
764 | 766 | ||
765 | // calculate soil / snag dynamics
|
767 | // calculate soil / snag dynamics
|
766 | if (settings().carbonCycleEnabled) { |
768 | if (settings().carbonCycleEnabled) { |
767 | DebugTimer ccycle("carbon cylce"); |
769 | DebugTimer ccycle("carbon cylce"); |
768 | executePerResourceUnit( nc_carbonCycle, false /* true: force single threaded operation */); |
770 | executePerResourceUnit( nc_carbonCycle, false /* true: force single threaded operation */); |
769 | GlobalSettings::instance()->systemStatistics()->tCarbonCycle+=ccycle.elapsed(); |
771 | GlobalSettings::instance()->systemStatistics()->tCarbonCycle+=ccycle.elapsed(); |
770 | 772 | ||
771 | }
|
773 | }
|
772 | 774 | ||
773 | // external modules/disturbances
|
775 | // external modules/disturbances
|
774 | mModules->run(); |
776 | mModules->run(); |
775 | 777 | ||
776 | // cleanup of tree lists if external modules removed trees.
|
778 | // cleanup of tree lists if external modules removed trees.
|
777 | cleanTreeLists(); |
779 | cleanTreeLists(); |
778 | 780 | ||
779 | 781 | ||
780 | DebugTimer toutput("outputs"); |
782 | DebugTimer toutput("outputs"); |
781 | // calculate statistics
|
783 | // calculate statistics
|
782 | foreach(ResourceUnit *ru, mRU) |
784 | foreach(ResourceUnit *ru, mRU) |
783 | ru->yearEnd(); |
785 | ru->yearEnd(); |
784 | 786 | ||
785 | // create outputs
|
787 | // create outputs
|
786 | OutputManager *om = GlobalSettings::instance()->outputManager(); |
788 | OutputManager *om = GlobalSettings::instance()->outputManager(); |
787 | om->execute("tree"); // single tree output |
789 | om->execute("tree"); // single tree output |
788 | om->execute("stand"); //resource unit level x species |
790 | om->execute("stand"); //resource unit level x species |
789 | om->execute("landscape"); //landscape x species |
791 | om->execute("landscape"); //landscape x species |
790 | om->execute("sapling"); // sapling layer per RU x species |
792 | om->execute("sapling"); // sapling layer per RU x species |
791 | om->execute("production_month"); // 3pg responses growth per species x RU x month |
793 | om->execute("production_month"); // 3pg responses growth per species x RU x month |
792 | om->execute("dynamicstand"); // output with user-defined columns (based on species x RU) |
794 | om->execute("dynamicstand"); // output with user-defined columns (based on species x RU) |
793 | om->execute("standdead"); // resource unit level x species |
795 | om->execute("standdead"); // resource unit level x species |
794 | om->execute("management"); // resource unit level x species |
796 | om->execute("management"); // resource unit level x species |
795 | om->execute("carbon"); // resource unit level, carbon pools above and belowground |
797 | om->execute("carbon"); // resource unit level, carbon pools above and belowground |
796 | om->execute("carbonflow"); // resource unit level, GPP, NPP and total carbon flows (atmosphere, harvest, ...) |
798 | om->execute("carbonflow"); // resource unit level, GPP, NPP and total carbon flows (atmosphere, harvest, ...) |
797 | 799 | ||
798 | GlobalSettings::instance()->systemStatistics()->tWriteOutput+=toutput.elapsed(); |
800 | GlobalSettings::instance()->systemStatistics()->tWriteOutput+=toutput.elapsed(); |
799 | GlobalSettings::instance()->systemStatistics()->tTotalYear+=t.elapsed(); |
801 | GlobalSettings::instance()->systemStatistics()->tTotalYear+=t.elapsed(); |
800 | GlobalSettings::instance()->systemStatistics()->writeOutput(); |
802 | GlobalSettings::instance()->systemStatistics()->writeOutput(); |
801 | 803 | ||
802 | GlobalSettings::instance()->setCurrentYear(GlobalSettings::instance()->currentYear()+1); |
804 | GlobalSettings::instance()->setCurrentYear(GlobalSettings::instance()->currentYear()+1); |
803 | 805 | ||
804 | // try to clean up a bit of memory (useful if many large JS objects (e.g., grids) are used)
|
806 | // try to clean up a bit of memory (useful if many large JS objects (e.g., grids) are used)
|
805 | GlobalSettings::instance()->scriptEngine()->collectGarbage(); |
807 | GlobalSettings::instance()->scriptEngine()->collectGarbage(); |
806 | }
|
808 | }
|
807 | 809 | ||
808 | 810 | ||
809 | 811 | ||
810 | void Model::afterStop() |
812 | void Model::afterStop() |
811 | {
|
813 | {
|
812 | // do some cleanup
|
814 | // do some cleanup
|
813 | }
|
815 | }
|
814 | 816 | ||
815 | /// multithreaded running function for LIP printing
|
817 | /// multithreaded running function for LIP printing
|
816 | ResourceUnit* nc_applyPattern(ResourceUnit *unit) |
818 | ResourceUnit* nc_applyPattern(ResourceUnit *unit) |
817 | {
|
819 | {
|
818 | 820 | ||
819 | QVector<Tree>::iterator tit; |
821 | QVector<Tree>::iterator tit; |
820 | QVector<Tree>::iterator tend = unit->trees().end(); |
822 | QVector<Tree>::iterator tend = unit->trees().end(); |
821 | 823 | ||
822 | try { |
824 | try { |
823 | 825 | ||
824 | // light concurrence influence
|
826 | // light concurrence influence
|
825 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
827 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
826 | // height dominance grid
|
828 | // height dominance grid
|
827 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
829 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
828 | (*tit).heightGrid(); // just do it ;) |
830 | (*tit).heightGrid(); // just do it ;) |
829 | 831 | ||
830 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
832 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
831 | (*tit).applyLIP(); // just do it ;) |
833 | (*tit).applyLIP(); // just do it ;) |
832 | 834 | ||
833 | } else { |
835 | } else { |
834 | // height dominance grid
|
836 | // height dominance grid
|
835 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
837 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
836 | (*tit).heightGrid_torus(); // just do it ;) |
838 | (*tit).heightGrid_torus(); // just do it ;) |
837 | 839 | ||
838 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
840 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
839 | (*tit).applyLIP_torus(); // do it the wraparound way |
841 | (*tit).applyLIP_torus(); // do it the wraparound way |
840 | }
|
842 | }
|
841 | return unit; |
843 | return unit; |
842 | } catch (const IException &e) { |
844 | } catch (const IException &e) { |
843 | GlobalSettings::instance()->controller()->throwError(e.message()); |
845 | GlobalSettings::instance()->controller()->throwError(e.message()); |
844 | }
|
846 | }
|
845 | return unit; |
847 | return unit; |
846 | }
|
848 | }
|
847 | 849 | ||
848 | /// multithreaded running function for LIP value extraction
|
850 | /// multithreaded running function for LIP value extraction
|
849 | ResourceUnit *nc_readPattern(ResourceUnit *unit) |
851 | ResourceUnit *nc_readPattern(ResourceUnit *unit) |
850 | {
|
852 | {
|
851 | QVector<Tree>::iterator tit; |
853 | QVector<Tree>::iterator tit; |
852 | QVector<Tree>::iterator tend = unit->trees().end(); |
854 | QVector<Tree>::iterator tend = unit->trees().end(); |
853 | try { |
855 | try { |
854 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
856 | if (!GlobalSettings::instance()->settings().paramValueBool("torus")) { |
855 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
857 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
856 | (*tit).readLIF(); // multipliactive approach |
858 | (*tit).readLIF(); // multipliactive approach |
857 | } else { |
859 | } else { |
858 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
860 | for (tit=unit->trees().begin(); tit!=tend; ++tit) |
859 | (*tit).readLIF_torus(); // do it the wraparound way |
861 | (*tit).readLIF_torus(); // do it the wraparound way |
860 | }
|
862 | }
|
861 | } catch (const IException &e) { |
863 | } catch (const IException &e) { |
862 | GlobalSettings::instance()->controller()->throwError(e.message()); |
864 | GlobalSettings::instance()->controller()->throwError(e.message()); |
863 | }
|
865 | }
|
864 | return unit; |
866 | return unit; |
865 | }
|
867 | }
|
866 | 868 | ||
867 | /// multithreaded running function for the growth of individual trees
|
869 | /// multithreaded running function for the growth of individual trees
|
868 | ResourceUnit *nc_grow(ResourceUnit *unit) |
870 | ResourceUnit *nc_grow(ResourceUnit *unit) |
869 | {
|
871 | {
|
870 | QVector<Tree>::iterator tit; |
872 | QVector<Tree>::iterator tit; |
871 | QVector<Tree>::iterator tend = unit->trees().end(); |
873 | QVector<Tree>::iterator tend = unit->trees().end(); |
872 | try { |
874 | try { |
873 | unit->beforeGrow(); // reset statistics |
875 | unit->beforeGrow(); // reset statistics |
874 | // calculate light responses
|
876 | // calculate light responses
|
875 | // responses are based on *modified* values for LightResourceIndex
|
877 | // responses are based on *modified* values for LightResourceIndex
|
876 | for (tit=unit->trees().begin(); tit!=tend; ++tit) { |
878 | for (tit=unit->trees().begin(); tit!=tend; ++tit) { |
877 | (*tit).calcLightResponse(); |
879 | (*tit).calcLightResponse(); |
878 | }
|
880 | }
|
879 | 881 | ||
880 | unit->calculateInterceptedArea(); |
882 | unit->calculateInterceptedArea(); |
881 | 883 | ||
882 | for (tit=unit->trees().begin(); tit!=tend; ++tit) { |
884 | for (tit=unit->trees().begin(); tit!=tend; ++tit) { |
883 | (*tit).grow(); // actual growth of individual trees |
885 | (*tit).grow(); // actual growth of individual trees |
884 | }
|
886 | }
|
885 | } catch (const IException &e) { |
887 | } catch (const IException &e) { |
886 | GlobalSettings::instance()->controller()->throwError(e.message()); |
888 | GlobalSettings::instance()->controller()->throwError(e.message()); |
887 | }
|
889 | }
|
888 | 890 | ||
889 | GlobalSettings::instance()->systemStatistics()->treeCount+=unit->trees().count(); |
891 | GlobalSettings::instance()->systemStatistics()->treeCount+=unit->trees().count(); |
890 | return unit; |
892 | return unit; |
891 | }
|
893 | }
|
892 | 894 | ||
893 | /// multithreaded running function for the resource level production
|
895 | /// multithreaded running function for the resource level production
|
894 | ResourceUnit *nc_production(ResourceUnit *unit) |
896 | ResourceUnit *nc_production(ResourceUnit *unit) |
895 | {
|
897 | {
|
896 | try { |
898 | try { |
897 | unit->production(); |
899 | unit->production(); |
898 | } catch (const IException &e) { |
900 | } catch (const IException &e) { |
899 | GlobalSettings::instance()->controller()->throwError(e.message()); |
901 | GlobalSettings::instance()->controller()->throwError(e.message()); |
900 | }
|
902 | }
|
901 | return unit; |
903 | return unit; |
902 | }
|
904 | }
|
903 | 905 | ||
904 | 906 | ||
905 | void Model::test() |
907 | void Model::test() |
906 | {
|
908 | {
|
907 | // Test-funktion: braucht 1/3 time von readGrid()
|
909 | // Test-funktion: braucht 1/3 time von readGrid()
|
908 | DebugTimer t("test"); |
910 | DebugTimer t("test"); |
909 | FloatGrid averaged = mGrid->averaged(10); |
911 | FloatGrid averaged = mGrid->averaged(10); |
910 | int count = 0; |
912 | int count = 0; |
911 | float *end = averaged.end(); |
913 | float *end = averaged.end(); |
912 | for (float *p=averaged.begin(); p!=end; ++p) |
914 | for (float *p=averaged.begin(); p!=end; ++p) |
913 | if (*p > 0.9) |
915 | if (*p > 0.9) |
914 | count++; |
916 | count++; |
915 | qDebug() << count << "LIF>0.9 of " << averaged.count(); |
917 | qDebug() << count << "LIF>0.9 of " << averaged.count(); |
916 | }
|
918 | }
|
917 | 919 | ||
918 | void Model::debugCheckAllTrees() |
920 | void Model::debugCheckAllTrees() |
919 | {
|
921 | {
|
920 | AllTreeIterator at(this); |
922 | AllTreeIterator at(this); |
921 | bool has_errors = false; double dummy=0.; |
923 | bool has_errors = false; double dummy=0.; |
922 | while (Tree *t = at.next()) { |
924 | while (Tree *t = at.next()) { |
923 | // plausibility
|
925 | // plausibility
|
924 | if (t->dbh()<0 || t->dbh()>10000. || t->biomassFoliage()<0. || t->height()>1000. || t->height() < 0. |
926 | if (t->dbh()<0 || t->dbh()>10000. || t->biomassFoliage()<0. || t->height()>1000. || t->height() < 0. |
925 | || t->biomassFoliage() <0.) |
927 | || t->biomassFoliage() <0.) |
926 | has_errors = true; |
928 | has_errors = true; |
927 | // check for objects....
|
929 | // check for objects....
|
928 | dummy = t->stamp()->offset() + t->ru()->ruSpecies()[1]->statistics().count(); |
930 | dummy = t->stamp()->offset() + t->ru()->ruSpecies()[1]->statistics().count(); |
929 | }
|
931 | }
|
930 | if (has_errors) |
932 | if (has_errors) |
931 | qDebug() << "model: debugCheckAllTrees found problems" << dummy; |
933 | qDebug() << "model: debugCheckAllTrees found problems" << dummy; |
932 | }
|
934 | }
|
933 | 935 | ||
934 | void Model::applyPattern() |
936 | void Model::applyPattern() |
935 | {
|
937 | {
|
936 | 938 | ||
937 | DebugTimer t("applyPattern()"); |
939 | DebugTimer t("applyPattern()"); |
938 | // intialize grids...
|
940 | // intialize grids...
|
939 | initializeGrid(); |
941 | initializeGrid(); |
940 | 942 | ||
941 | // initialize height grid with a value of 4m. This is the height of the regeneration layer
|
943 | // initialize height grid with a value of 4m. This is the height of the regeneration layer
|
942 | for (HeightGridValue *h=mHeightGrid->begin();h!=mHeightGrid->end();++h) { |
944 | for (HeightGridValue *h=mHeightGrid->begin();h!=mHeightGrid->end();++h) { |
943 | h->resetCount(); // set count = 0, but do not touch the flags |
945 | h->resetCount(); // set count = 0, but do not touch the flags |
944 | h->height = 4.f; |
946 | h->height = 4.f; |
945 | }
|
947 | }
|
946 | 948 | ||
947 | threadRunner.run(nc_applyPattern); |
949 | threadRunner.run(nc_applyPattern); |
948 | GlobalSettings::instance()->systemStatistics()->tApplyPattern+=t.elapsed(); |
950 | GlobalSettings::instance()->systemStatistics()->tApplyPattern+=t.elapsed(); |
949 | }
|
951 | }
|
950 | 952 | ||
951 | void Model::readPattern() |
953 | void Model::readPattern() |
952 | {
|
954 | {
|
953 | DebugTimer t("readPattern()"); |
955 | DebugTimer t("readPattern()"); |
954 | threadRunner.run(nc_readPattern); |
956 | threadRunner.run(nc_readPattern); |
955 | GlobalSettings::instance()->systemStatistics()->tReadPattern+=t.elapsed(); |
957 | GlobalSettings::instance()->systemStatistics()->tReadPattern+=t.elapsed(); |
956 | 958 | ||
957 | }
|
959 | }
|
958 | 960 | ||
959 | /** Main function for the growth of stands and trees.
|
961 | /** Main function for the growth of stands and trees.
|
960 | This includes several steps.
|
962 | This includes several steps.
|
961 | (1) calculate the stocked area (i.e. count pixels in height grid)
|
963 | (1) calculate the stocked area (i.e. count pixels in height grid)
|
962 | (2) 3PG production (including response calculation, water cycle)
|
964 | (2) 3PG production (including response calculation, water cycle)
|
963 | (3) single tree growth (including mortality)
|
965 | (3) single tree growth (including mortality)
|
964 | (4) cleanup of tree lists (remove dead trees)
|
966 | (4) cleanup of tree lists (remove dead trees)
|
965 | */
|
967 | */
|
966 | void Model::grow() |
968 | void Model::grow() |
967 | {
|
969 | {
|
968 | 970 | ||
969 | if (!settings().growthEnabled) |
971 | if (!settings().growthEnabled) |
970 | return; |
972 | return; |
971 | { DebugTimer t("growRU()"); |
973 | { DebugTimer t("growRU()"); |
972 | calculateStockedArea(); |
974 | calculateStockedArea(); |
973 | 975 | ||
974 | // multithreaded: mutex for the message handler in mainwindow solved the crashes.
|
976 | // multithreaded: mutex for the message handler in mainwindow solved the crashes.
|
975 | threadRunner.run(nc_production); |
977 | threadRunner.run(nc_production); |
976 | }
|
978 | }
|
977 | 979 | ||
978 | DebugTimer t("growTrees()"); |
980 | DebugTimer t("growTrees()"); |
979 | threadRunner.run(nc_grow); // actual growth of individual trees |
981 | threadRunner.run(nc_grow); // actual growth of individual trees |
980 | 982 | ||
981 | foreach(ResourceUnit *ru, mRU) { |
983 | foreach(ResourceUnit *ru, mRU) { |
982 | ru->cleanTreeList(); |
984 | ru->cleanTreeList(); |
983 | ru->afterGrow(); |
985 | ru->afterGrow(); |
984 | //qDebug() << (b-n) << "trees died (of" << b << ").";
|
986 | //qDebug() << (b-n) << "trees died (of" << b << ").";
|
985 | }
|
987 | }
|
986 | GlobalSettings::instance()->systemStatistics()->tTreeGrowth+=t.elapsed(); |
988 | GlobalSettings::instance()->systemStatistics()->tTreeGrowth+=t.elapsed(); |
987 | }
|
989 | }
|
988 | 990 | ||
989 | /** calculate for each resource unit the fraction of area which is stocked.
|
991 | /** calculate for each resource unit the fraction of area which is stocked.
|
990 | This is done by checking the pixels of the global height grid.
|
992 | This is done by checking the pixels of the global height grid.
|
991 | */
|
993 | */
|
992 | void Model::calculateStockedArea() |
994 | void Model::calculateStockedArea() |
993 | {
|
995 | {
|
994 | // iterate over the whole heightgrid and count pixels for each ressource unit
|
996 | // iterate over the whole heightgrid and count pixels for each ressource unit
|
995 | HeightGridValue *end = mHeightGrid->end(); |
997 | HeightGridValue *end = mHeightGrid->end(); |
996 | QPointF cp;
|
998 | QPointF cp;
|
997 | ResourceUnit *ru; |
999 | ResourceUnit *ru; |
998 | for (HeightGridValue *i=mHeightGrid->begin(); i!=end; ++i) { |
1000 | for (HeightGridValue *i=mHeightGrid->begin(); i!=end; ++i) { |
999 | cp = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i)); |
1001 | cp = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i)); |
1000 | if (mRUmap.coordValid(cp)) { |
1002 | if (mRUmap.coordValid(cp)) { |
1001 | ru = mRUmap.valueAt(cp); |
1003 | ru = mRUmap.valueAt(cp); |
1002 | if (ru) { |
1004 | if (ru) { |
1003 | ru->countStockedPixel( (*i).count()>0 ); |
1005 | ru->countStockedPixel( (*i).count()>0 ); |
1004 | }
|
1006 | }
|
1005 | }
|
1007 | }
|
1006 | 1008 | ||
1007 | }
|
1009 | }
|
1008 | }
|
1010 | }
|
1009 | 1011 | ||
1010 | /** calculate for each resource unit the stockable area.
|
1012 | /** calculate for each resource unit the stockable area.
|
1011 | "stockability" is determined by the isValid flag of resource units which in turn
|
1013 | "stockability" is determined by the isValid flag of resource units which in turn
|
1012 | is derived from stand grid values.
|
1014 | is derived from stand grid values.
|
1013 | */
|
1015 | */
|
1014 | void Model::calculateStockableArea() |
1016 | void Model::calculateStockableArea() |
1015 | {
|
1017 | {
|
1016 | 1018 | ||
1017 | foreach(ResourceUnit *ru, mRU) { |
1019 | foreach(ResourceUnit *ru, mRU) { |
1018 | // //
|
1020 | // //
|
1019 | // if (ru->id()==-1) {
|
1021 | // if (ru->id()==-1) {
|
1020 | // ru->setStockableArea(0.);
|
1022 | // ru->setStockableArea(0.);
|
1021 | // continue;
|
1023 | // continue;
|
1022 | // }
|
1024 | // }
|
1023 | GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox()); |
1025 | GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox()); |
1024 | int valid=0, total=0; |
1026 | int valid=0, total=0; |
1025 | while (runner.next()) { |
1027 | while (runner.next()) { |
1026 | if ( runner.current()->isValid() ) |
1028 | if ( runner.current()->isValid() ) |
1027 | valid++; |
1029 | valid++; |
1028 | total++; |
1030 | total++; |
1029 | }
|
1031 | }
|
1030 | if (total) { |
1032 | if (total) { |
1031 | ru->setStockableArea( cHeightPixelArea * valid); |
1033 | ru->setStockableArea( cHeightPixelArea * valid); |
1032 | if (valid==0 && ru->id()>-1) { |
1034 | if (valid==0 && ru->id()>-1) { |
1033 | // invalidate this resource unit
|
1035 | // invalidate this resource unit
|
1034 | ru->setID(-1); |
1036 | ru->setID(-1); |
1035 | }
|
1037 | }
|
1036 | if (valid>0 && ru->id()==-1) { |
1038 | if (valid>0 && ru->id()==-1) { |
1037 | qDebug() << "Warning: a resource unit has id=-1 but stockable area (id was set to 0)!!! ru: " << ru->boundingBox() << "with index" << ru->index(); |
1039 | qDebug() << "Warning: a resource unit has id=-1 but stockable area (id was set to 0)!!! ru: " << ru->boundingBox() << "with index" << ru->index(); |
1038 | ru->setID(0); |
1040 | ru->setID(0); |
1039 | // test-code
|
1041 | // test-code
|
1040 | //GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox());
|
1042 | //GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox());
|
1041 | //while (runner.next()) {
|
1043 | //while (runner.next()) {
|
1042 | // qDebug() << mHeightGrid->cellCenterPoint(mHeightGrid->indexOf( runner.current() )) << ": " << runner.current()->isValid();
|
1044 | // qDebug() << mHeightGrid->cellCenterPoint(mHeightGrid->indexOf( runner.current() )) << ": " << runner.current()->isValid();
|
1043 | //}
|
1045 | //}
|
1044 | 1046 | ||
1045 | }
|
1047 | }
|
1046 | } else |
1048 | } else |
1047 | throw IException("calculateStockableArea: resource unit without pixels!"); |
1049 | throw IException("calculateStockableArea: resource unit without pixels!"); |
1048 | 1050 | ||
1049 | }
|
1051 | }
|
1050 | // mark those pixels that are at the edge of a "forest-out-of-area"
|
1052 | // mark those pixels that are at the edge of a "forest-out-of-area"
|
1051 | GridRunner<HeightGridValue> runner(*mHeightGrid, mHeightGrid->metricRect()); |
1053 | GridRunner<HeightGridValue> runner(*mHeightGrid, mHeightGrid->metricRect()); |
1052 | HeightGridValue* neighbors[8]; |
1054 | HeightGridValue* neighbors[8]; |
1053 | while (runner.next()) { |
1055 | while (runner.next()) { |
1054 | if (runner.current()->isForestOutside()) { |
1056 | if (runner.current()->isForestOutside()) { |
1055 | // if the current pixel is a "radiating" border pixel,
|
1057 | // if the current pixel is a "radiating" border pixel,
|
1056 | // then check the neighbors and set a flag if the pixel is a neighbor of a in-project-area pixel.
|
1058 | // then check the neighbors and set a flag if the pixel is a neighbor of a in-project-area pixel.
|
1057 | runner.neighbors8(neighbors); |
1059 | runner.neighbors8(neighbors); |
1058 | for (int i=0;i<8;++i) |
1060 | for (int i=0;i<8;++i) |
1059 | if (neighbors[i] && neighbors[i]->isValid()) |
1061 | if (neighbors[i] && neighbors[i]->isValid()) |
1060 | runner.current()->setIsRadiating(); |
1062 | runner.current()->setIsRadiating(); |
1061 | 1063 | ||
1062 | }
|
1064 | }
|
1063 | }
|
1065 | }
|
1064 | 1066 | ||
1065 | }
|
1067 | }
|
1066 | 1068 | ||
1067 | void Model::initializeGrid() |
1069 | void Model::initializeGrid() |
1068 | {
|
1070 | {
|
1069 | // fill the whole grid with a value of "1."
|
1071 | // fill the whole grid with a value of "1."
|
1070 | mGrid->initialize(1.f); |
1072 | mGrid->initialize(1.f); |
1071 | 1073 | ||
1072 | // apply special values for grid cells border regions where out-of-area cells
|
1074 | // apply special values for grid cells border regions where out-of-area cells
|
1073 | // radiate into the main LIF grid.
|
1075 | // radiate into the main LIF grid.
|
1074 | QPoint p;
|
1076 | QPoint p;
|
1075 | int ix_min, ix_max, iy_min, iy_max, ix_center, iy_center; |
1077 | int ix_min, ix_max, iy_min, iy_max, ix_center, iy_center; |
1076 | const int px_offset = cPxPerHeight / 2; // for 5 px per height grid cell, the offset is 2 |
1078 | const int px_offset = cPxPerHeight / 2; // for 5 px per height grid cell, the offset is 2 |
1077 | const int max_radiate_distance = 7; |
1079 | const int max_radiate_distance = 7; |
1078 | const float step_width = 1.f / (float)max_radiate_distance; |
1080 | const float step_width = 1.f / (float)max_radiate_distance; |
1079 | int c_rad = 0; |
1081 | int c_rad = 0; |
1080 | for (HeightGridValue *hgv=mHeightGrid->begin(); hgv!=mHeightGrid->end(); ++hgv) { |
1082 | for (HeightGridValue *hgv=mHeightGrid->begin(); hgv!=mHeightGrid->end(); ++hgv) { |
1081 | if (hgv->isRadiating()) { |
1083 | if (hgv->isRadiating()) { |
1082 | p=mHeightGrid->indexOf(hgv); |
1084 | p=mHeightGrid->indexOf(hgv); |
1083 | ix_min = p.x() * cPxPerHeight - max_radiate_distance + px_offset; |
1085 | ix_min = p.x() * cPxPerHeight - max_radiate_distance + px_offset; |
1084 | ix_max = ix_min + 2*max_radiate_distance + 1; |
1086 | ix_max = ix_min + 2*max_radiate_distance + 1; |
1085 | ix_center = ix_min + max_radiate_distance; |
1087 | ix_center = ix_min + max_radiate_distance; |
1086 | iy_min = p.y() * cPxPerHeight - max_radiate_distance + px_offset; |
1088 | iy_min = p.y() * cPxPerHeight - max_radiate_distance + px_offset; |
1087 | iy_max = iy_min + 2*max_radiate_distance + 1; |
1089 | iy_max = iy_min + 2*max_radiate_distance + 1; |
1088 | iy_center = iy_min + max_radiate_distance; |
1090 | iy_center = iy_min + max_radiate_distance; |
1089 | for (int y=iy_min; y<=iy_max; ++y) { |
1091 | for (int y=iy_min; y<=iy_max; ++y) { |
1090 | for (int x=ix_min; x<=ix_max; ++x) { |
1092 | for (int x=ix_min; x<=ix_max; ++x) { |
1091 | if (!mGrid->isIndexValid(x,y) || !(*mHeightGrid)(x/cPxPerHeight, y/cPxPerHeight).isValid()) |
1093 | if (!mGrid->isIndexValid(x,y) || !(*mHeightGrid)(x/cPxPerHeight, y/cPxPerHeight).isValid()) |
1092 | continue; |
1094 | continue; |
1093 | float value = qMax(qAbs(x-ix_center), qAbs(y-iy_center)) * step_width; |
1095 | float value = qMax(qAbs(x-ix_center), qAbs(y-iy_center)) * step_width; |
1094 | float &v = mGrid->valueAtIndex(x, y); |
1096 | float &v = mGrid->valueAtIndex(x, y); |
1095 | if (value>=0.f && v>value) |
1097 | if (value>=0.f && v>value) |
1096 | v = value; |
1098 | v = value; |
1097 | }
|
1099 | }
|
1098 | }
|
1100 | }
|
1099 | c_rad++; |
1101 | c_rad++; |
1100 | }
|
1102 | }
|
1101 | }
|
1103 | }
|
1102 | if (logLevelDebug()) |
1104 | if (logLevelDebug()) |
1103 | qDebug() << "initialize grid:" << c_rad << "radiating pixels..."; |
1105 | qDebug() << "initialize grid:" << c_rad << "radiating pixels..."; |
1104 | 1106 | ||
1105 | }
|
1107 | }
|
1106 | 1108 | ||
1107 | 1109 | ||
1108 | /// Force the creation of stand statistics.
|
1110 | /// Force the creation of stand statistics.
|
1109 | /// - stocked area (for resourceunit-areas)
|
1111 | /// - stocked area (for resourceunit-areas)
|
1110 | /// - ru - statistics
|
1112 | /// - ru - statistics
|
1111 | void Model::createStandStatistics() |
1113 | void Model::createStandStatistics() |
1112 | {
|
1114 | {
|
1113 | calculateStockedArea(); |
1115 | calculateStockedArea(); |
1114 | foreach(ResourceUnit *ru, mRU) { |
1116 | foreach(ResourceUnit *ru, mRU) { |
1115 | ru->addTreeAgingForAllTrees(); |
1117 | ru->addTreeAgingForAllTrees(); |
1116 | ru->createStandStatistics(); |
1118 | ru->createStandStatistics(); |
1117 | }
|
1119 | }
|
1118 | }
|
1120 | }
|
1119 | 1121 | ||
1120 | void Model::cleanTreeLists() |
1122 | void Model::cleanTreeLists() |
1121 | {
|
1123 | {
|
1122 | foreach(ResourceUnit *ru, GlobalSettings::instance()->model()->ruList()) { |
1124 | foreach(ResourceUnit *ru, GlobalSettings::instance()->model()->ruList()) { |
1123 | if (ru->hasDiedTrees()) { |
1125 | if (ru->hasDiedTrees()) { |
1124 | ru->cleanTreeList(); |
1126 | ru->cleanTreeList(); |
1125 | ru->recreateStandStatistics(); |
1127 | ru->recreateStandStatistics(); |
1126 | }
|
1128 | }
|
1127 | }
|
1129 | }
|
1128 | }
|
1130 | }
|
1129 | 1131 | ||
1130 | 1132 | ||
1131 | 1133 |