Rev 1221 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1221 | Rev 1222 | ||
---|---|---|---|
1 | Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/modelcontroller.cpp': |
1 | Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/modelcontroller.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 | /** ModelController is a helper class used to control the flow of operations during a model run.
|
21 | /** ModelController is a helper class used to control the flow of operations during a model run.
|
22 | The ModelController encapsulates the Model class and is the main control unit. It is used by the
|
22 | The ModelController encapsulates the Model class and is the main control unit. It is used by the
|
23 | iLand GUI as well as the command line version (ilandc).
|
23 | iLand GUI as well as the command line version (ilandc).
|
24 | 24 | ||
25 | */
|
25 | */
|
26 | 26 | ||
27 | #include "global.h"
|
27 | #include "global.h"
|
28 | #include "modelcontroller.h"
|
28 | #include "modelcontroller.h"
|
29 | #include <QObject>
|
29 | #include <QObject>
|
30 | 30 | ||
31 | #include "model.h"
|
31 | #include "model.h"
|
32 | #include "debugtimer.h"
|
32 | #include "debugtimer.h"
|
33 | #include "helper.h"
|
33 | #include "helper.h"
|
34 | #include "version.h"
|
34 | #include "version.h"
|
35 | #include "expression.h"
|
35 | #include "expression.h"
|
36 | #include "expressionwrapper.h"
|
36 | #include "expressionwrapper.h"
|
37 | #include "../output/outputmanager.h"
|
37 | #include "../output/outputmanager.h"
|
38 | 38 | ||
39 | #include "species.h"
|
39 | #include "species.h"
|
40 | #include "speciesset.h"
|
40 | #include "speciesset.h"
|
41 | #include "mapgrid.h"
|
41 | #include "mapgrid.h"
|
42 | #include "statdata.h"
|
42 | #include "statdata.h"
|
43 | 43 | ||
44 | #ifdef ILAND_GUI
|
44 | #ifdef ILAND_GUI
|
45 | #include "mainwindow.h" // for the debug message buffering
|
45 | #include "mainwindow.h" // for the debug message buffering
|
46 | #endif
|
46 | #endif
|
47 | 47 | ||
48 | ModelController::ModelController() |
48 | ModelController::ModelController() |
49 | {
|
49 | {
|
50 | mModel = NULL; |
50 | mModel = NULL; |
51 | mPaused = false; |
51 | mPaused = false; |
52 | mRunning = false; |
52 | mRunning = false; |
53 | mHasError = false; |
53 | mHasError = false; |
54 | mYearsToRun = 0; |
54 | mYearsToRun = 0; |
55 | mViewerWindow = 0; |
55 | mViewerWindow = 0; |
56 | mDynamicOutputEnabled = false; |
56 | mDynamicOutputEnabled = false; |
57 | }
|
57 | }
|
58 | 58 | ||
59 | ModelController::~ModelController() |
59 | ModelController::~ModelController() |
60 | {
|
60 | {
|
61 | destroy(); |
61 | destroy(); |
62 | }
|
62 | }
|
63 | 63 | ||
64 | void ModelController::connectSignals() |
64 | void ModelController::connectSignals() |
65 | {
|
65 | {
|
66 | if (!mViewerWindow) |
66 | if (!mViewerWindow) |
67 | return; |
67 | return; |
68 | #ifdef ILAND_GUI
|
68 | #ifdef ILAND_GUI
|
69 | connect(this,SIGNAL(bufferLogs(bool)), mViewerWindow, SLOT(bufferedLog(bool))); |
69 | connect(this,SIGNAL(bufferLogs(bool)), mViewerWindow, SLOT(bufferedLog(bool))); |
70 | #endif
|
70 | #endif
|
71 | }
|
71 | }
|
72 | 72 | ||
73 | /// prepare a list of all (active) species
|
73 | /// prepare a list of all (active) species
|
74 | QList<const Species*> ModelController::availableSpecies() |
74 | QList<const Species*> ModelController::availableSpecies() |
75 | {
|
75 | {
|
76 | QList<const Species*> list; |
76 | QList<const Species*> list; |
77 | if (mModel) { |
77 | if (mModel) { |
78 | SpeciesSet *set = mModel->speciesSet(); |
78 | SpeciesSet *set = mModel->speciesSet(); |
79 | if (!set) |
79 | if (!set) |
80 | throw IException("there are 0 or more than one species sets."); |
80 | throw IException("there are 0 or more than one species sets."); |
81 | foreach (const Species *s, set->activeSpecies()) { |
81 | foreach (const Species *s, set->activeSpecies()) { |
82 | list.append(s); |
82 | list.append(s); |
83 | }
|
83 | }
|
84 | }
|
84 | }
|
85 | return list; |
85 | return list; |
86 | }
|
86 | }
|
87 | 87 | ||
88 | bool ModelController::canCreate() |
88 | bool ModelController::canCreate() |
89 | {
|
89 | {
|
90 | if (mModel) |
90 | if (mModel) |
91 | return false; |
91 | return false; |
92 | return true; |
92 | return true; |
93 | }
|
93 | }
|
94 | 94 | ||
95 | bool ModelController::canDestroy() |
95 | bool ModelController::canDestroy() |
96 | {
|
96 | {
|
97 | return mModel != NULL; |
97 | return mModel != NULL; |
98 | }
|
98 | }
|
99 | 99 | ||
100 | bool ModelController::canRun() |
100 | bool ModelController::canRun() |
101 | {
|
101 | {
|
102 | if (mModel && mModel->isSetup()) |
102 | if (mModel && mModel->isSetup()) |
103 | return true; |
103 | return true; |
104 | return false; |
104 | return false; |
105 | }
|
105 | }
|
106 | 106 | ||
107 | bool ModelController::isRunning() |
107 | bool ModelController::isRunning() |
108 | {
|
108 | {
|
109 | return mRunning; |
109 | return mRunning; |
110 | }
|
110 | }
|
111 | 111 | ||
112 | bool ModelController::isFinished() |
112 | bool ModelController::isFinished() |
113 | {
|
113 | {
|
114 | if (!mModel) |
114 | if (!mModel) |
115 | return false; |
115 | return false; |
116 | return canRun() && !isRunning() && mFinished; |
116 | return canRun() && !isRunning() && mFinished; |
117 | }
|
117 | }
|
118 | 118 | ||
119 | bool ModelController::isPaused() |
119 | bool ModelController::isPaused() |
120 | {
|
120 | {
|
121 | return mPaused; |
121 | return mPaused; |
122 | }
|
122 | }
|
123 | 123 | ||
124 | int ModelController::currentYear() const |
124 | int ModelController::currentYear() const |
125 | {
|
125 | {
|
126 | return GlobalSettings::instance()->currentYear(); |
126 | return GlobalSettings::instance()->currentYear(); |
127 | }
|
127 | }
|
128 | 128 | ||
129 | void ModelController::setFileName(QString initFileName) |
129 | void ModelController::setFileName(QString initFileName) |
130 | {
|
130 | {
|
131 | mInitFile = initFileName; |
131 | mInitFile = initFileName; |
132 | try { |
132 | try { |
133 | GlobalSettings::instance()->loadProjectFile(mInitFile); |
133 | GlobalSettings::instance()->loadProjectFile(mInitFile); |
134 | } catch(const IException &e) { |
134 | } catch(const IException &e) { |
135 | QString error_msg = e.message(); |
135 | QString error_msg = e.message(); |
136 | Helper::msg(error_msg); |
136 | Helper::msg(error_msg); |
137 | mHasError = true; |
137 | mHasError = true; |
138 | mLastError = error_msg; |
138 | mLastError = error_msg; |
139 | qDebug() << error_msg; |
139 | qDebug() << error_msg; |
140 | }
|
140 | }
|
141 | }
|
141 | }
|
142 | 142 | ||
143 | void ModelController::create() |
143 | void ModelController::create() |
144 | {
|
144 | {
|
145 | if (!canCreate()) |
145 | if (!canCreate()) |
146 | return; |
146 | return; |
147 | emit bufferLogs(true); |
147 | emit bufferLogs(true); |
148 | qDebug() << "**************************************************"; |
148 | qDebug() << "**************************************************"; |
149 | qDebug() << "project-file:" << mInitFile; |
149 | qDebug() << "project-file:" << mInitFile; |
150 | qDebug() << "started at: " << QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss"); |
150 | qDebug() << "started at: " << QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss"); |
151 | qDebug() << "iLand " << currentVersion() << " (" << svnRevision() << ")"; |
151 | qDebug() << "iLand " << currentVersion() << " (" << svnRevision() << ")"; |
152 | qDebug() << "**************************************************"; |
152 | qDebug() << "**************************************************"; |
153 | 153 | ||
154 | 154 | ||
155 | try { |
155 | try { |
156 | mHasError = false; |
156 | mHasError = false; |
157 | DebugTimer::clearAllTimers(); |
157 | DebugTimer::clearAllTimers(); |
158 | mModel = new Model(); |
158 | mModel = new Model(); |
159 | mModel->loadProject(); |
159 | mModel->loadProject(); |
160 | if (!mModel->isSetup()) { |
160 | if (!mModel->isSetup()) { |
161 | mHasError = true; |
161 | mHasError = true; |
162 | mLastError = "An error occured during the loading of the project. Please check the logs."; |
162 | mLastError = "An error occured during the loading of the project. Please check the logs."; |
163 | return; |
163 | return; |
164 | }
|
164 | }
|
165 | 165 | ||
166 | // reset clock...
|
166 | // reset clock...
|
167 | GlobalSettings::instance()->setCurrentYear(1); // reset clock |
167 | GlobalSettings::instance()->setCurrentYear(1); // reset clock |
168 | // initialization of trees, output on startup
|
168 | // initialization of trees, output on startup
|
169 | mModel->beforeRun(); |
169 | mModel->beforeRun(); |
170 | GlobalSettings::instance()->executeJSFunction("onAfterCreate"); |
170 | GlobalSettings::instance()->executeJSFunction("onAfterCreate"); |
171 | } catch(const IException &e) { |
171 | } catch(const IException &e) { |
172 | QString error_msg = e.message(); |
172 | QString error_msg = e.message(); |
173 | Helper::msg(error_msg); |
173 | Helper::msg(error_msg); |
174 | mLastError = error_msg; |
174 | mLastError = error_msg; |
175 | mHasError = true; |
175 | mHasError = true; |
176 | qDebug() << error_msg; |
176 | qDebug() << error_msg; |
177 | }
|
177 | }
|
178 | emit bufferLogs(false); |
178 | emit bufferLogs(false); |
179 | 179 | ||
180 | qDebug() << "Model created."; |
180 | qDebug() << "Model created."; |
181 | }
|
181 | }
|
182 | 182 | ||
183 | void ModelController::destroy() |
183 | void ModelController::destroy() |
184 | {
|
184 | {
|
185 | if (canDestroy()) { |
185 | if (canDestroy()) { |
186 | GlobalSettings::instance()->executeJSFunction("onBeforeDestroy"); |
186 | GlobalSettings::instance()->executeJSFunction("onBeforeDestroy"); |
187 | Model *m = mModel; |
187 | Model *m = mModel; |
188 | mModel = 0; |
188 | mModel = 0; |
189 | delete m; |
189 | delete m; |
190 | GlobalSettings::instance()->setCurrentYear(0); |
190 | GlobalSettings::instance()->setCurrentYear(0); |
191 | qDebug() << "ModelController: Model destroyed."; |
191 | qDebug() << "ModelController: Model destroyed."; |
192 | }
|
192 | }
|
193 | }
|
193 | }
|
194 | 194 | ||
195 | void ModelController::runloop() |
195 | void ModelController::runloop() |
196 | {
|
196 | {
|
197 | static QTime sLastTime = QTime::currentTime(); |
197 | static QTime sLastTime = QTime::currentTime(); |
198 | #ifdef ILAND_GUI
|
198 | #ifdef ILAND_GUI
|
199 | // QApplication::processEvents();
|
199 | // QApplication::processEvents();
|
200 | #else
|
200 | #else
|
201 | // QCoreApplication::processEvents();
|
201 | // QCoreApplication::processEvents();
|
202 | #endif
|
202 | #endif
|
203 | if (mPaused) |
203 | if (mPaused) |
204 | return; |
204 | return; |
205 | bool doStop = false; |
205 | bool doStop = false; |
206 | mHasError = false; |
206 | mHasError = false; |
207 | if (GlobalSettings::instance()->currentYear()<=1) { |
207 | if (GlobalSettings::instance()->currentYear()<=1) { |
208 | sLastTime = QTime::currentTime(); // reset clock at the beginning of the simulation |
208 | sLastTime = QTime::currentTime(); // reset clock at the beginning of the simulation |
209 | }
|
209 | }
|
210 | 210 | ||
211 | if (!mCanceled && GlobalSettings::instance()->currentYear() < mYearsToRun) { |
211 | if (!mCanceled && GlobalSettings::instance()->currentYear() < mYearsToRun) { |
212 | emit bufferLogs(true); |
212 | emit bufferLogs(true); |
213 | 213 | ||
214 | mHasError = runYear(); // do the work! |
214 | mHasError = runYear(); // do the work! |
215 | 215 | ||
216 | mRunning = true; |
216 | mRunning = true; |
217 | emit year(GlobalSettings::instance()->currentYear()); |
217 | emit year(GlobalSettings::instance()->currentYear()); |
218 | if (!mHasError) { |
218 | if (!mHasError) { |
219 | int elapsed = sLastTime.msecsTo(QTime::currentTime()); |
219 | int elapsed = sLastTime.msecsTo(QTime::currentTime()); |
220 | int time=0; |
220 | int time=0; |
221 | if (currentYear()%50==0 && elapsed>10000) |
221 | if (currentYear()%50==0 && elapsed>10000) |
222 | time = 100; // a 100ms pause... |
222 | time = 100; // a 100ms pause... |
223 | if (currentYear()%100==0 && elapsed>10000) { |
223 | if (currentYear()%100==0 && elapsed>10000) { |
224 | time = 500; // a 500ms pause... |
224 | time = 500; // a 500ms pause... |
225 | }
|
225 | }
|
226 | if (time>0) { |
226 | if (time>0) { |
227 | sLastTime = QTime::currentTime(); // reset clock |
227 | sLastTime = QTime::currentTime(); // reset clock |
228 | qDebug() << "--- little break ---- (after " << elapsed << "ms)."; |
228 | qDebug() << "--- little break ---- (after " << elapsed << "ms)."; |
229 | //QTimer::singleShot(time,this, SLOT(runloop()));
|
229 | //QTimer::singleShot(time,this, SLOT(runloop()));
|
230 | }
|
230 | }
|
231 | 231 | ||
232 | } else { |
232 | } else { |
233 | doStop = true; // an error occured |
233 | doStop = true; // an error occured |
234 | mLastError = "An error occured while running the model. Please check the logs."; |
234 | mLastError = "An error occured while running the model. Please check the logs."; |
235 | mHasError = true; |
235 | mHasError = true; |
236 | }
|
236 | }
|
237 | 237 | ||
238 | } else { |
238 | } else { |
239 | doStop = true; // all years simulated |
239 | doStop = true; // all years simulated |
240 | }
|
240 | }
|
241 | 241 | ||
242 | if (doStop || mCanceled) { |
242 | if (doStop || mCanceled) { |
243 | // finished
|
243 | // finished
|
244 | internalStop(); |
244 | internalStop(); |
245 | }
|
245 | }
|
246 | 246 | ||
247 | #ifdef ILAND_GUI
|
247 | #ifdef ILAND_GUI
|
248 | QApplication::processEvents(); |
248 | QApplication::processEvents(); |
249 | #else
|
249 | #else
|
250 | QCoreApplication::processEvents(); |
250 | QCoreApplication::processEvents(); |
251 | #endif
|
251 | #endif
|
252 | }
|
252 | }
|
253 | 253 | ||
254 | bool ModelController::internalRun() |
254 | bool ModelController::internalRun() |
255 | {
|
255 | {
|
256 | // main loop
|
256 | // main loop
|
257 | try { |
257 | try { |
258 | while (mRunning && !mPaused && !mFinished) { |
258 | while (mRunning && !mPaused && !mFinished) { |
259 | runloop(); // start the running loop |
259 | runloop(); // start the running loop |
260 | }
|
260 | }
|
261 | } catch (IException &e) { |
261 | } catch (IException &e) { |
262 | #ifdef ILAND_GUI
|
262 | #ifdef ILAND_GUI
|
263 | Helper::msg(e.message()); |
263 | Helper::msg(e.message()); |
264 | #else
|
264 | #else
|
265 | qDebug() << e.message(); |
265 | qDebug() << e.message(); |
266 | #endif
|
266 | #endif
|
267 | 267 | ||
268 | }
|
268 | }
|
269 | return isFinished(); |
269 | return isFinished(); |
270 | }
|
270 | }
|
271 | 271 | ||
272 | void ModelController::internalStop() |
272 | void ModelController::internalStop() |
273 | {
|
273 | {
|
274 | if (mRunning) { |
274 | if (mRunning) { |
275 | GlobalSettings::instance()->outputManager()->save(); |
275 | GlobalSettings::instance()->outputManager()->save(); |
276 | DebugTimer::printAllTimers(); |
276 | DebugTimer::printAllTimers(); |
277 | saveDebugOutputs(); |
277 | saveDebugOutputs(); |
278 | //if (GlobalSettings::instance()->dbout().isOpen())
|
278 | //if (GlobalSettings::instance()->dbout().isOpen())
|
279 | // GlobalSettings::instance()->dbout().close();
|
279 | // GlobalSettings::instance()->dbout().close();
|
280 | 280 | ||
281 | mFinished = true; |
281 | mFinished = true; |
282 | }
|
282 | }
|
283 | mRunning = false; |
283 | mRunning = false; |
284 | mPaused = false; // in any case |
284 | mPaused = false; // in any case |
285 | emit bufferLogs(false); // stop buffering |
285 | emit bufferLogs(false); // stop buffering |
286 | emit finished(QString()); |
286 | emit finished(QString()); |
287 | emit stateChanged(); |
287 | emit stateChanged(); |
288 | 288 | ||
289 | }
|
289 | }
|
290 | 290 | ||
291 | void ModelController::run(int years) |
291 | void ModelController::run(int years) |
292 | {
|
292 | {
|
293 | if (!canRun()) |
293 | if (!canRun()) |
294 | return; |
294 | return; |
295 | emit bufferLogs(true); // start buffering |
295 | emit bufferLogs(true); // start buffering |
296 | 296 | ||
297 | DebugTimer many_runs(QString("Timer for %1 runs").arg(years)); |
297 | DebugTimer many_runs(QString("Timer for %1 runs").arg(years)); |
298 | mPaused = false; |
298 | mPaused = false; |
299 | mFinished = false; |
299 | mFinished = false; |
300 | mCanceled = false; |
300 | mCanceled = false; |
301 | mYearsToRun = years; |
301 | mYearsToRun = years; |
302 | //GlobalSettings::instance()->setCurrentYear(1); // reset clock
|
302 | //GlobalSettings::instance()->setCurrentYear(1); // reset clock
|
303 | 303 | ||
304 | DebugTimer::clearAllTimers(); |
304 | DebugTimer::clearAllTimers(); |
305 | 305 | ||
306 | mRunning = true; |
306 | mRunning = true; |
307 | emit stateChanged(); |
307 | emit stateChanged(); |
308 | 308 | ||
309 | qDebug() << "ModelControler: runloop started."; |
309 | qDebug() << "ModelControler: runloop started."; |
310 | internalRun(); |
310 | internalRun(); |
311 | emit stateChanged(); |
311 | emit stateChanged(); |
312 | }
|
312 | }
|
313 | 313 | ||
314 | bool ModelController::runYear() |
314 | bool ModelController::runYear() |
315 | {
|
315 | {
|
316 | if (!canRun()) return false; |
316 | if (!canRun()) return false; |
317 | DebugTimer t("ModelController:runYear"); |
317 | DebugTimer t("ModelController:runYear"); |
318 | qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:") << "ModelController: run year" << currentYear(); |
318 | qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:") << "ModelController: run year" << currentYear(); |
319 | 319 | ||
320 | if (GlobalSettings::instance()->settings().paramValueBool("debug_clear")) |
320 | if (GlobalSettings::instance()->settings().paramValueBool("debug_clear")) |
321 | GlobalSettings::instance()->clearDebugLists(); // clear debug data |
321 | GlobalSettings::instance()->clearDebugLists(); // clear debug data |
322 | bool err=false; |
322 | bool err=false; |
323 | try { |
323 | try { |
324 | emit bufferLogs(true); |
324 | emit bufferLogs(true); |
325 | GlobalSettings::instance()->executeJSFunction("onYearBegin"); |
325 | GlobalSettings::instance()->executeJSFunction("onYearBegin"); |
326 | mModel->runYear(); |
326 | mModel->runYear(); |
327 | 327 | ||
328 | fetchDynamicOutput(); |
328 | fetchDynamicOutput(); |
329 | } catch(const IException &e) { |
329 | } catch(const IException &e) { |
330 | QString error_msg = e.message(); |
330 | QString error_msg = e.message(); |
331 | Helper::msg(error_msg); |
331 | Helper::msg(error_msg); |
332 | qDebug() << error_msg; |
332 | qDebug() << error_msg; |
333 | err=true; |
333 | err=true; |
334 | }
|
334 | }
|
335 | emit bufferLogs(false); |
335 | emit bufferLogs(false); |
336 | #ifdef ILAND_GUI
|
336 | #ifdef ILAND_GUI
|
337 | QApplication::processEvents(); |
337 | QApplication::processEvents(); |
338 | #else
|
338 | #else
|
339 | QCoreApplication::processEvents(); |
339 | QCoreApplication::processEvents(); |
340 | #endif
|
340 | #endif
|
341 | 341 | ||
342 | return err; |
342 | return err; |
343 | }
|
343 | }
|
344 | 344 | ||
345 | bool ModelController::pause() |
345 | bool ModelController::pause() |
346 | {
|
346 | {
|
347 | if(!isRunning()) |
347 | if(!isRunning()) |
348 | return mPaused; |
348 | return mPaused; |
349 | 349 | ||
350 | if (mPaused) { |
350 | if (mPaused) { |
351 | // currently in pause - mode -> continue
|
351 | // currently in pause - mode -> continue
|
352 | mPaused = false; |
352 | mPaused = false; |
353 | 353 | ||
354 | } else { |
354 | } else { |
355 | // currently running -> set to pause mode
|
355 | // currently running -> set to pause mode
|
356 | GlobalSettings::instance()->outputManager()->save(); |
356 | GlobalSettings::instance()->outputManager()->save(); |
357 | mPaused = true; |
357 | mPaused = true; |
358 | emit bufferLogs(false); |
358 | emit bufferLogs(false); |
359 | }
|
359 | }
|
360 | emit stateChanged(); |
360 | emit stateChanged(); |
361 | return mPaused; |
361 | return mPaused; |
362 | }
|
362 | }
|
363 | 363 | ||
364 | bool ModelController::continueRun() |
364 | bool ModelController::continueRun() |
365 | {
|
365 | {
|
366 | mRunning = true; |
366 | mRunning = true; |
367 | emit stateChanged(); |
367 | emit stateChanged(); |
368 | return internalRun(); |
368 | return internalRun(); |
369 | }
|
369 | }
|
370 | 370 | ||
371 | void ModelController::cancel() |
371 | void ModelController::cancel() |
372 | {
|
372 | {
|
373 | mCanceled = true; |
373 | mCanceled = true; |
374 | internalStop(); |
374 | internalStop(); |
375 | emit stateChanged(); |
375 | emit stateChanged(); |
376 | }
|
376 | }
|
377 | 377 | ||
378 | 378 | ||
379 | // this function is called when exceptions occur in multithreaded code.
|
379 | // this function is called when exceptions occur in multithreaded code.
|
380 | QMutex error_mutex;
|
380 | QMutex error_mutex;
|
381 | void ModelController::throwError(const QString msg) |
381 | void ModelController::throwError(const QString msg) |
382 | {
|
382 | {
|
383 | QMutexLocker lock(&error_mutex); // serialize access |
383 | QMutexLocker lock(&error_mutex); // serialize access |
384 | qDebug() << "ModelController: throwError reached:"; |
384 | qDebug() << "ModelController: throwError reached:"; |
385 | qDebug() << msg; |
385 | qDebug() << msg; |
386 | mLastError = msg; |
386 | mLastError = msg; |
387 | mHasError = true; |
387 | mHasError = true; |
388 | emit bufferLogs(false); |
388 | emit bufferLogs(false); |
389 | emit bufferLogs(true); // start buffering again |
389 | emit bufferLogs(true); // start buffering again |
390 | 390 | ||
391 | emit finished(msg); |
391 | emit finished(msg); |
392 | throw IException(msg); // raise error again |
392 | throw IException(msg); // raise error again |
393 | 393 | ||
394 | }
|
394 | }
|
395 | //////////////////////////////////////
|
395 | //////////////////////////////////////
|
396 | // dynamic outut
|
396 | // dynamic outut
|
397 | //////////////////////////////////////
|
397 | //////////////////////////////////////
|
398 | //////////////////////////////////////
|
398 | //////////////////////////////////////
|
399 | void ModelController::setupDynamicOutput(QString fieldList) |
399 | void ModelController::setupDynamicOutput(QString fieldList) |
400 | {
|
400 | {
|
401 | mDynFieldList.clear(); |
401 | mDynFieldList.clear(); |
402 | if (!fieldList.isEmpty()) { |
402 | if (!fieldList.isEmpty()) { |
403 | QRegExp rx("((?:\\[.+\\]|\\w+)\\.\\w+)"); |
403 | QRegExp rx("((?:\\[.+\\]|\\w+)\\.\\w+)"); |
404 | int pos=0; |
404 | int pos=0; |
405 | while ((pos = rx.indexIn(fieldList, pos)) != -1) { |
405 | while ((pos = rx.indexIn(fieldList, pos)) != -1) { |
406 | mDynFieldList.append(rx.cap(1)); |
406 | mDynFieldList.append(rx.cap(1)); |
407 | pos += rx.matchedLength(); |
407 | pos += rx.matchedLength(); |
408 | }
|
408 | }
|
409 | 409 | ||
410 | //mDynFieldList = fieldList.split(QRegExp("(?:\\[.+\\]|\\w+)\\.\\w+"), QString::SkipEmptyParts);
|
410 | //mDynFieldList = fieldList.split(QRegExp("(?:\\[.+\\]|\\w+)\\.\\w+"), QString::SkipEmptyParts);
|
411 | mDynFieldList.prepend("count"); |
411 | mDynFieldList.prepend("count"); |
412 | mDynFieldList.prepend("year"); // fixed fields. |
412 | mDynFieldList.prepend("year"); // fixed fields. |
413 | }
|
413 | }
|
414 | mDynData.clear(); |
414 | mDynData.clear(); |
415 | mDynData.append(mDynFieldList.join(";")); |
415 | mDynData.append(mDynFieldList.join(";")); |
416 | mDynamicOutputEnabled = true; |
416 | mDynamicOutputEnabled = true; |
417 | }
|
417 | }
|
418 | 418 | ||
419 | QString ModelController::dynamicOutput() |
419 | QString ModelController::dynamicOutput() |
420 | {
|
420 | {
|
421 | return mDynData.join("\n"); |
421 | return mDynData.join("\n"); |
422 | }
|
422 | }
|
423 | 423 | ||
424 | const QStringList aggList = QStringList() << "mean" << "sum" << "min" << "max" << "p25" << "p50" << "p75" << "p5"<< "p10" << "p90" << "p95"; |
424 | const QStringList aggList = QStringList() << "mean" << "sum" << "min" << "max" << "p25" << "p50" << "p75" << "p5"<< "p10" << "p90" << "p95"; |
425 | void ModelController::fetchDynamicOutput() |
425 | void ModelController::fetchDynamicOutput() |
426 | {
|
426 | {
|
427 | if (!mDynamicOutputEnabled || mDynFieldList.isEmpty()) |
427 | if (!mDynamicOutputEnabled || mDynFieldList.isEmpty()) |
428 | return; |
428 | return; |
429 | DebugTimer t("dynamic output"); |
429 | DebugTimer t("dynamic output"); |
430 | QStringList var;
|
430 | QStringList var;
|
431 | QString lastVar = ""; |
431 | QString lastVar = ""; |
432 | QVector<double> data; |
432 | QVector<double> data; |
433 | AllTreeIterator at(mModel); |
433 | AllTreeIterator at(mModel); |
434 | TreeWrapper tw;
|
434 | TreeWrapper tw;
|
435 | int var_index; |
435 | int var_index; |
436 | StatData stat;
|
436 | StatData stat;
|
437 | double value; |
437 | double value; |
438 | QStringList line;
|
438 | QStringList line;
|
439 | Expression custom_expr;
|
439 | Expression custom_expr;
|
440 | bool simple_expression; |
440 | bool simple_expression; |
441 | foreach (QString field, mDynFieldList) { |
441 | foreach (QString field, mDynFieldList) { |
442 | if (field=="count" || field=="year") |
442 | if (field=="count" || field=="year") |
443 | continue; |
443 | continue; |
444 | if (field.count()>0 && field.at(0)=='[') { |
444 | if (field.count()>0 && field.at(0)=='[') { |
445 | QRegExp rex("\\[(.+)\\]\\.(\\w+)"); |
445 | QRegExp rex("\\[(.+)\\]\\.(\\w+)"); |
446 | rex.indexIn(field); |
446 | rex.indexIn(field); |
447 | var = rex.capturedTexts(); |
447 | var = rex.capturedTexts(); |
448 | var.pop_front(); // drop first element (contains the full string) |
448 | var.pop_front(); // drop first element (contains the full string) |
449 | simple_expression = false; |
449 | simple_expression = false; |
450 | } else { |
450 | } else { |
451 | var = field.split(QRegExp("\\W+"), QString::SkipEmptyParts); |
451 | var = field.split(QRegExp("\\W+"), QString::SkipEmptyParts); |
452 | simple_expression = true; |
452 | simple_expression = true; |
453 | }
|
453 | }
|
454 | if (var.count()!=2) |
454 | if (var.count()!=2) |
455 | throw IException(QString("Invalid variable name for dynamic output:") + field); |
455 | throw IException(QString("Invalid variable name for dynamic output:") + field); |
456 | if (var.first()!=lastVar) { |
456 | if (var.first()!=lastVar) { |
457 | // load new field
|
457 | // load new field
|
458 | data.clear(); |
458 | data.clear(); |
459 | at.reset(); var_index = 0; |
459 | at.reset(); var_index = 0; |
460 | if (simple_expression) { |
460 | if (simple_expression) { |
461 | var_index = tw.variableIndex(var.first()); |
461 | var_index = tw.variableIndex(var.first()); |
462 | if (var_index<0) |
462 | if (var_index<0) |
463 | throw IException(QString("Invalid variable name for dynamic output:") + var.first()); |
463 | throw IException(QString("Invalid variable name for dynamic output:") + var.first()); |
464 | 464 | ||
465 | } else { |
465 | } else { |
466 | custom_expr.setExpression(var.first()); |
466 | custom_expr.setExpression(var.first()); |
467 | custom_expr.setModelObject(&tw); |
467 | custom_expr.setModelObject(&tw); |
468 | }
|
468 | }
|
469 | while (Tree *t = at.next()) { |
469 | while (Tree *t = at.next()) { |
470 | tw.setTree(t); |
470 | tw.setTree(t); |
471 | if (simple_expression) |
471 | if (simple_expression) |
472 | value = tw.value(var_index); |
472 | value = tw.value(var_index); |
473 | else
|
473 | else
|
474 | value = custom_expr.execute(); |
474 | value = custom_expr.execute(); |
475 | data.push_back(value); |
475 | data.push_back(value); |
476 | }
|
476 | }
|
477 | stat.setData(data); |
477 | stat.setData(data); |
478 | }
|
478 | }
|
479 | // fetch data
|
479 | // fetch data
|
480 | var_index = aggList.indexOf(var[1]); |
480 | var_index = aggList.indexOf(var[1]); |
481 | switch (var_index) { |
481 | switch (var_index) { |
482 | case 0: value = stat.mean(); break; |
482 | case 0: value = stat.mean(); break; |
483 | case 1: value = stat.sum(); break; |
483 | case 1: value = stat.sum(); break; |
484 | case 2: value = stat.min(); break; |
484 | case 2: value = stat.min(); break; |
485 | case 3: value = stat.max(); break; |
485 | case 3: value = stat.max(); break; |
486 | case 4: value = stat.percentile25(); break; |
486 | case 4: value = stat.percentile25(); break; |
487 | case 5: value = stat.median(); break; |
487 | case 5: value = stat.median(); break; |
488 | case 6: value = stat.percentile75(); break; |
488 | case 6: value = stat.percentile75(); break; |
489 | case 7: value = stat.percentile(5); break; |
489 | case 7: value = stat.percentile(5); break; |
490 | case 8: value = stat.percentile(10); break; |
490 | case 8: value = stat.percentile(10); break; |
491 | case 9: value = stat.percentile(90); break; |
491 | case 9: value = stat.percentile(90); break; |
492 | case 10: value = stat.percentile(95); break; |
492 | case 10: value = stat.percentile(95); break; |
493 | default: throw IException(QString("Invalid aggregate expression for dynamic output: %1\nallowed:%2") |
493 | default: throw IException(QString("Invalid aggregate expression for dynamic output: %1\nallowed:%2") |
494 | .arg(var[1]).arg(aggList.join(" "))); |
494 | .arg(var[1]).arg(aggList.join(" "))); |
495 | }
|
495 | }
|
496 | line+=QString::number(value); |
496 | line+=QString::number(value); |
497 | }
|
497 | }
|
498 | line.prepend( QString::number(data.size()) ); |
498 | line.prepend( QString::number(data.size()) ); |
499 | line.prepend( QString::number(GlobalSettings::instance()->currentYear()) ); |
499 | line.prepend( QString::number(GlobalSettings::instance()->currentYear()) ); |
500 | mDynData.append(line.join(";")); |
500 | mDynData.append(line.join(";")); |
501 | }
|
501 | }
|
502 | 502 | ||
503 | void ModelController::saveDebugOutputs() |
503 | void ModelController::saveDebugOutputs() |
504 | {
|
504 | {
|
505 | // save to files if switch is true
|
505 | // save to files if switch is true
|
506 | if (!GlobalSettings::instance()->settings().valueBool("system.settings.debugOutputAutoSave")) |
506 | if (!GlobalSettings::instance()->settings().valueBool("system.settings.debugOutputAutoSave")) |
507 | return; |
507 | return; |
508 | 508 | ||
509 | QString p = GlobalSettings::instance()->path("debug_", "temp"); |
509 | QString p = GlobalSettings::instance()->path("debug_", "temp"); |
510 | 510 | ||
511 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dTreePartition, ";", p + "tree_partition.csv"); |
511 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dTreePartition, ";", p + "tree_partition.csv"); |
512 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dTreeGrowth, ";", p + "tree_growth.csv"); |
512 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dTreeGrowth, ";", p + "tree_growth.csv"); |
513 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dTreeNPP, ";", p + "tree_npp.csv"); |
513 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dTreeNPP, ";", p + "tree_npp.csv"); |
514 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dStandGPP, ";", p + "stand_gpp.csv"); |
514 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dStandGPP, ";", p + "stand_gpp.csv"); |
515 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dWaterCycle, ";", p + "water_cycle.csv"); |
515 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dWaterCycle, ";", p + "water_cycle.csv"); |
516 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dDailyResponses, ";", p + "daily_responses.csv"); |
516 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dDailyResponses, ";", p + "daily_responses.csv"); |
517 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dEstablishment, ";", p + "establishment.csv"); |
517 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dEstablishment, ";", p + "establishment.csv"); |
518 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dSaplingGrowth, ";", p + "saplinggrowth.csv"); |
518 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dSaplingGrowth, ";", p + "saplinggrowth.csv"); |
519 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dCarbonCycle, ";", p + "carboncycle.csv"); |
519 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dCarbonCycle, ";", p + "carboncycle.csv"); |
520 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dPerformance, ";", p + "performance.csv"); |
520 | GlobalSettings::instance()->debugDataTable(GlobalSettings::dPerformance, ";", p + "performance.csv"); |
521 | Helper::saveToTextFile(p+"dynamic.csv", dynamicOutput()); |
521 | Helper::saveToTextFile(p+"dynamic.csv", dynamicOutput()); |
522 | Helper::saveToTextFile(p+ "version.txt", verboseVersion()); |
522 | Helper::saveToTextFile(p+ "version.txt", verboseVersion()); |
523 | 523 | ||
524 | 524 | ||
525 | qDebug() << "saved debug outputs to" << p; |
525 | qDebug() << "saved debug outputs to" << p; |
526 | 526 | ||
527 | }
|
527 | }
|
528 | 528 | ||
529 | void ModelController::saveScreenshot(QString file_name) |
529 | void ModelController::saveScreenshot(QString file_name) |
530 | {
|
530 | {
|
531 | #ifdef ILAND_GUI
|
531 | #ifdef ILAND_GUI
|
532 | if (!mViewerWindow) |
532 | if (!mViewerWindow) |
533 | return; |
533 | return; |
534 | QImage img = mViewerWindow->screenshot(); |
534 | QImage img = mViewerWindow->screenshot(); |
535 | img.save(GlobalSettings::instance()->path(file_name)); |
535 | img.save(GlobalSettings::instance()->path(file_name)); |
536 | #else
|
536 | #else
|
537 | Q_UNUSED(file_name); |
537 | Q_UNUSED(file_name); |
538 | #endif
|
538 | #endif
|
539 | }
|
539 | }
|
540 | 540 | ||
541 | void ModelController::paintMap(MapGrid *map, double min_value, double max_value) |
541 | void ModelController::paintMap(MapGrid *map, double min_value, double max_value) |
542 | {
|
542 | {
|
543 | #ifdef ILAND_GUI
|
543 | #ifdef ILAND_GUI
|
544 | if (mViewerWindow) { |
544 | if (mViewerWindow) { |
545 | mViewerWindow->paintGrid(map, "", GridViewRainbow, min_value, max_value); |
545 | mViewerWindow->paintGrid(map, "", GridViewRainbow, min_value, max_value); |
546 | qDebug() << "painted map grid" << map->name() << "min-value (blue):" << min_value << "max-value(red):" << max_value; |
546 | qDebug() << "painted map grid" << map->name() << "min-value (blue):" << min_value << "max-value(red):" << max_value; |
547 | }
|
547 | }
|
548 | #else
|
548 | #else
|
549 | Q_UNUSED(map);Q_UNUSED(min_value);Q_UNUSED(max_value); |
549 | Q_UNUSED(map);Q_UNUSED(min_value);Q_UNUSED(max_value); |
550 | #endif
|
550 | #endif
|
551 | }
|
551 | }
|
552 | 552 | ||
553 | void ModelController::addGrid(const FloatGrid *grid, const QString &name, const GridViewType view_type, double min_value, double max_value) |
553 | void ModelController::addGrid(const FloatGrid *grid, const QString &name, const GridViewType view_type, double min_value, double max_value) |
554 | {
|
554 | {
|
555 | #ifdef ILAND_GUI
|
555 | #ifdef ILAND_GUI
|
556 | 556 | ||
557 | if (mViewerWindow) { |
557 | if (mViewerWindow) { |
558 | mViewerWindow->paintGrid(grid, name, view_type, min_value, max_value); |
558 | mViewerWindow->paintGrid(grid, name, view_type, min_value, max_value); |
559 | qDebug() << "painted grid min-value (blue):" << min_value << "max-value(red):" << max_value; |
559 | qDebug() << "painted grid min-value (blue):" << min_value << "max-value(red):" << max_value; |
560 | }
|
560 | }
|
561 | #else
|
561 | #else
|
562 | Q_UNUSED(grid); Q_UNUSED(name); Q_UNUSED(view_type); Q_UNUSED(min_value);Q_UNUSED(max_value); |
562 | Q_UNUSED(grid); Q_UNUSED(name); Q_UNUSED(view_type); Q_UNUSED(min_value);Q_UNUSED(max_value); |
563 | #endif
|
563 | #endif
|
564 | }
|
564 | }
|
565 | 565 | ||
566 | void ModelController::addLayers(const LayeredGridBase *layers, const QString &name) |
566 | void ModelController::addLayers(const LayeredGridBase *layers, const QString &name) |
567 | {
|
567 | {
|
568 | #ifdef ILAND_GUI
|
568 | #ifdef ILAND_GUI
|
569 | if (mViewerWindow) |
569 | if (mViewerWindow) |
570 | mViewerWindow->addLayers(layers, name); |
570 | mViewerWindow->addLayers(layers, name); |
571 | //qDebug() << layers->names();
|
571 | //qDebug() << layers->names();
|
572 | #else
|
572 | #else
|
573 | Q_UNUSED(layers); Q_UNUSED(name); |
573 | Q_UNUSED(layers); Q_UNUSED(name); |
574 | #endif
|
574 | #endif
|
575 | }
|
575 | }
|
576 | void ModelController::removeLayers(const LayeredGridBase *layers) |
576 | void ModelController::removeLayers(const LayeredGridBase *layers) |
577 | {
|
577 | {
|
578 | #ifdef ILAND_GUI
|
578 | #ifdef ILAND_GUI
|
579 | if (mViewerWindow) |
579 | if (mViewerWindow) |
580 | mViewerWindow->removeLayers(layers); |
580 | mViewerWindow->removeLayers(layers); |
581 | //qDebug() << layers->names();
|
581 | //qDebug() << layers->names();
|
582 | #else
|
582 | #else
|
583 | Q_UNUSED(layers); |
583 | Q_UNUSED(layers); |
584 | #endif
|
584 | #endif
|
585 | }
|
585 | }
|
586 | 586 | ||
587 | void ModelController::setViewport(QPointF center_point, double scale_px_per_m) |
587 | void ModelController::setViewport(QPointF center_point, double scale_px_per_m) |
588 | {
|
588 | {
|
589 | #ifdef ILAND_GUI
|
589 | #ifdef ILAND_GUI
|
590 | if (mViewerWindow) |
590 | if (mViewerWindow) |
591 | mViewerWindow->setViewport(center_point, scale_px_per_m); |
591 | mViewerWindow->setViewport(center_point, scale_px_per_m); |
592 | #else
|
592 | #else
|
593 | Q_UNUSED(center_point);Q_UNUSED(scale_px_per_m); |
593 | Q_UNUSED(center_point);Q_UNUSED(scale_px_per_m); |
594 | #endif
|
594 | #endif
|
595 | }
|
595 | }
|
596 | 596 | ||
597 | void ModelController::setUIShortcuts(QVariantMap shortcuts) |
597 | void ModelController::setUIShortcuts(QVariantMap shortcuts) |
598 | {
|
598 | {
|
599 | #ifdef ILAND_GUI
|
599 | #ifdef ILAND_GUI
|
600 | if (mViewerWindow) |
600 | if (mViewerWindow) |
601 | mViewerWindow->setUIshortcuts(shortcuts); |
601 | mViewerWindow->setUIshortcuts(shortcuts); |
602 | #else
|
602 | #else
|
603 | Q_UNUSED(shortcuts); |
603 | Q_UNUSED(shortcuts); |
604 | #endif
|
604 | #endif
|
605 | }
|
605 | }
|
606 | 606 | ||
607 | void ModelController::repaint() |
607 | void ModelController::repaint() |
608 | {
|
608 | {
|
609 | #ifdef ILAND_GUI
|
609 | #ifdef ILAND_GUI
|
610 | if (mViewerWindow) |
610 | if (mViewerWindow) |
611 | mViewerWindow->repaint(); |
611 | mViewerWindow->repaint(); |
612 | #endif
|
612 | #endif
|
613 | }
|
613 | }
|
614 | 614 | ||
615 | 615 | ||
616 | 616 | ||
617 | 617 | ||
618 | 618 | ||
619 | 619 |