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