Subversion Repositories public iLand

Rev

Rev 1085 | Rev 1089 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1085 Rev 1087
1
Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/model.cpp':
1
Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/model.cpp':
2
/********************************************************************************************
2
/********************************************************************************************
3
**    iLand - an individual based forest landscape and disturbance model
3
**    iLand - an individual based forest landscape and disturbance model
4
**    http://iland.boku.ac.at
4
**    http://iland.boku.ac.at
5
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
5
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
6
**
6
**
7
**    This program is free software: you can redistribute it and/or modify
7
**    This program is free software: you can redistribute it and/or modify
8
**    it under the terms of the GNU General Public License as published by
8
**    it under the terms of the GNU General Public License as published by
9
**    the Free Software Foundation, either version 3 of the License, or
9
**    the Free Software Foundation, either version 3 of the License, or
10
**    (at your option) any later version.
10
**    (at your option) any later version.
11
**
11
**
12
**    This program is distributed in the hope that it will be useful,
12
**    This program is distributed in the hope that it will be useful,
13
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
**    GNU General Public License for more details.
15
**    GNU General Public License for more details.
16
**
16
**
17
**    You should have received a copy of the GNU General Public License
17
**    You should have received a copy of the GNU General Public License
18
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
********************************************************************************************/
19
********************************************************************************************/
20
20
21
/** @class Model
21
/** @class Model
22
  Main object of the iLand model composited of various sub models / sub components.
22
  Main object of the iLand model composited of various sub models / sub components.
23
  @ingroup core
23
  @ingroup core
24
  The class Model is the top level container of iLand. The Model holds a collection of ResourceUnits, links to SpeciesSet and Climate.
24
  The class Model is the top level container of iLand. The Model holds a collection of ResourceUnits, links to SpeciesSet and Climate.
25
  ResourceUnit are grid cells with (currently) a size of 1 ha (100x100m). Many stand level processes (NPP produciton, WaterCycle) operate on this
25
  ResourceUnit are grid cells with (currently) a size of 1 ha (100x100m). Many stand level processes (NPP produciton, WaterCycle) operate on this
26
  level.
26
  level.
27
  The Model also contain the landscape-wide 2m LIF-grid (http://iland.boku.ac.at/competition+for+light).
27
  The Model also contain the landscape-wide 2m LIF-grid (http://iland.boku.ac.at/competition+for+light).
28

28

29
  */
29
  */
30
#include "global.h"
30
#include "global.h"
31
#include "model.h"
31
#include "model.h"
32
#include "sqlhelper.h"
32
#include "sqlhelper.h"
33
33
34
#include "xmlhelper.h"
34
#include "xmlhelper.h"
35
#include "debugtimer.h"
35
#include "debugtimer.h"
36
#include "environment.h"
36
#include "environment.h"
37
#include "timeevents.h"
37
#include "timeevents.h"
38
#include "helper.h"
38
#include "helper.h"
39
#include "resourceunit.h"
39
#include "resourceunit.h"
40
#include "climate.h"
40
#include "climate.h"
41
#include "speciesset.h"
41
#include "speciesset.h"
42
#include "standloader.h"
42
#include "standloader.h"
43
#include "tree.h"
43
#include "tree.h"
44
#include "management.h"
44
#include "management.h"
45
#include "modelsettings.h"
45
#include "modelsettings.h"
46
#include "standstatistics.h"
46
#include "standstatistics.h"
47
#include "mapgrid.h"
47
#include "mapgrid.h"
48
#include "modelcontroller.h"
48
#include "modelcontroller.h"
49
#include "modules.h"
49
#include "modules.h"
50
#include "dem.h"
50
#include "dem.h"
51
#include "grasscover.h"
51
#include "grasscover.h"
52
52
53
#include "outputmanager.h"
53
#include "outputmanager.h"
54
54
55
#include "forestmanagementengine.h"
55
#include "forestmanagementengine.h"
56
56
57
#include <QtCore>
57
#include <QtCore>
58
#include <QtXml>
58
#include <QtXml>
59
59
60
/** iterate over all trees of the model. return NULL if all trees processed.
60
/** iterate over all trees of the model. return NULL if all trees processed.
61
  Usage:
61
  Usage:
62
  @code
62
  @code
63
  AllTreeIterator trees(model);
63
  AllTreeIterator trees(model);
64
  while (Tree *tree = trees.next()) { // returns NULL when finished.
64
  while (Tree *tree = trees.next()) { // returns NULL when finished.
65
     tree->something(); // do something
65
     tree->something(); // do something
66
  }
66
  }
67
  @endcode  */
67
  @endcode  */
68
Tree *AllTreeIterator::next()
68
Tree *AllTreeIterator::next()
69
{
69
{
70
70
71
    if (!mTreeEnd) {
71
    if (!mTreeEnd) {
72
        // initialize to first ressource unit
72
        // initialize to first ressource unit
73
        mRUIterator = mModel->ruList().constBegin();
73
        mRUIterator = mModel->ruList().constBegin();
74
        // fast forward to the first RU with trees
74
        // fast forward to the first RU with trees
75
        while (mRUIterator!=mModel->ruList().constEnd()) {
75
        while (mRUIterator!=mModel->ruList().constEnd()) {
76
            if ((*mRUIterator)->trees().count()>0)
76
            if ((*mRUIterator)->trees().count()>0)
77
                break;
77
                break;
78
            ++mRUIterator;
78
            ++mRUIterator;
79
        }
79
        }
80
            // finished if all RU processed
80
            // finished if all RU processed
81
        if (mRUIterator == mModel->ruList().constEnd())
81
        if (mRUIterator == mModel->ruList().constEnd())
82
            return NULL;
82
            return NULL;
83
        mTreeEnd = &((*mRUIterator)->trees().back()) + 1; // let end point to "1 after end" (STL-style)
83
        mTreeEnd = &((*mRUIterator)->trees().back()) + 1; // let end point to "1 after end" (STL-style)
84
        mCurrent = &((*mRUIterator)->trees().front());
84
        mCurrent = &((*mRUIterator)->trees().front());
85
    }
85
    }
86
    if (mCurrent==mTreeEnd) {
86
    if (mCurrent==mTreeEnd) {
87
        ++mRUIterator; // switch to next RU (loop until RU with trees is found)
87
        ++mRUIterator; // switch to next RU (loop until RU with trees is found)
88
        while (mRUIterator!=mModel->ruList().constEnd()) {
88
        while (mRUIterator!=mModel->ruList().constEnd()) {
89
            if ((*mRUIterator)->trees().count()>0) {
89
            if ((*mRUIterator)->trees().count()>0) {
90
                break;
90
                break;
91
            }
91
            }
92
            ++mRUIterator;
92
            ++mRUIterator;
93
        }
93
        }
94
        if (mRUIterator == mModel->ruList().constEnd()) {
94
        if (mRUIterator == mModel->ruList().constEnd()) {
95
            mCurrent = NULL;
95
            mCurrent = NULL;
96
            return NULL; // finished!!
96
            return NULL; // finished!!
97
        }else {
97
        }else {
98
            mTreeEnd = &((*mRUIterator)->trees().back()) + 1;
98
            mTreeEnd = &((*mRUIterator)->trees().back()) + 1;
99
            mCurrent = &((*mRUIterator)->trees().front());
99
            mCurrent = &((*mRUIterator)->trees().front());
100
        }
100
        }
101
    }
101
    }
102
102
103
    return mCurrent++;
103
    return mCurrent++;
104
}
104
}
105
Tree *AllTreeIterator::nextLiving()
105
Tree *AllTreeIterator::nextLiving()
106
{
106
{
107
    while (Tree *t = next())
107
    while (Tree *t = next())
108
        if (!t->isDead()) return t;
108
        if (!t->isDead()) return t;
109
    return NULL;
109
    return NULL;
110
}
110
}
111
Tree *AllTreeIterator::current() const
111
Tree *AllTreeIterator::current() const
112
{
112
{
113
    return mCurrent?mCurrent-1:NULL;
113
    return mCurrent?mCurrent-1:NULL;
114
}
114
}
115
115
116
116
117
ModelSettings Model::mSettings;
117
ModelSettings Model::mSettings;
118
Model::Model()
118
Model::Model()
119
{
119
{
120
    initialize();
120
    initialize();
121
    GlobalSettings::instance()->setModel(this);
121
    GlobalSettings::instance()->setModel(this);
122
    GlobalSettings::instance()->resetScriptEngine(); // clear the script
122
    GlobalSettings::instance()->resetScriptEngine(); // clear the script
123
    QString dbg="running in release mode.";
123
    QString dbg="running in release mode.";
124
    DBGMODE( dbg="running in debug mode."; );
124
    DBGMODE( dbg="running in debug mode."; );
125
    qDebug() << dbg;
125
    qDebug() << dbg;
126
}
126
}
127
127
128
Model::~Model()
128
Model::~Model()
129
{
129
{
130
    clear();
130
    clear();
131
    GlobalSettings::instance()->setModel(NULL);
131
    GlobalSettings::instance()->setModel(NULL);
132
}
132
}
133
133
134
/** Initial setup of the Model.
134
/** Initial setup of the Model.
135
  */
135
  */
136
void Model::initialize()
136
void Model::initialize()
137
{
137
{
138
   mSetup = false;
138
   mSetup = false;
139
   GlobalSettings::instance()->setCurrentYear(0);
139
   GlobalSettings::instance()->setCurrentYear(0);
140
   mGrid = 0;
140
   mGrid = 0;
141
   mHeightGrid = 0;
141
   mHeightGrid = 0;
142
   mManagement = 0;
142
   mManagement = 0;
143
   mABEManagement = 0;
143
   mABEManagement = 0;
144
   mEnvironment = 0;
144
   mEnvironment = 0;
145
   mTimeEvents = 0;
145
   mTimeEvents = 0;
146
   mStandGrid = 0;
146
   mStandGrid = 0;
147
   mModules = 0;
147
   mModules = 0;
148
   mDEM = 0;
148
   mDEM = 0;
149
   mGrassCover = 0;
149
   mGrassCover = 0;
150
}
150
}
151
151
152
/** sets up the simulation space.
152
/** sets up the simulation space.
153
*/
153
*/
154
void Model::setupSpace()
154
void Model::setupSpace()
155
{
155
{
156
    XmlHelper xml(GlobalSettings::instance()->settings().node("model.world"));
156
    XmlHelper xml(GlobalSettings::instance()->settings().node("model.world"));
157
    double cellSize = xml.value("cellSize", "2").toDouble();
157
    double cellSize = xml.value("cellSize", "2").toDouble();
158
    double width = xml.value("width", "100").toDouble();
158
    double width = xml.value("width", "100").toDouble();
159
    double height = xml.value("height", "100").toDouble();
159
    double height = xml.value("height", "100").toDouble();
160
    double buffer = xml.value("buffer", "60").toDouble();
160
    double buffer = xml.value("buffer", "60").toDouble();
161
    mModelRect = QRectF(0., 0., width, height);
161
    mModelRect = QRectF(0., 0., width, height);
162
162
163
    qDebug() << QString("setup of the world: %1x%2m with cell-size=%3m and %4m buffer").arg(width).arg(height).arg(cellSize).arg(buffer);
163
    qDebug() << QString("setup of the world: %1x%2m with cell-size=%3m and %4m buffer").arg(width).arg(height).arg(cellSize).arg(buffer);
164
164
165
    QRectF total_grid(QPointF(-buffer, -buffer), QPointF(width+buffer, height+buffer));
165
    QRectF total_grid(QPointF(-buffer, -buffer), QPointF(width+buffer, height+buffer));
166
    qDebug() << "setup grid rectangle:" << total_grid;
166
    qDebug() << "setup grid rectangle:" << total_grid;
167
167
168
    if (mGrid)
168
    if (mGrid)
169
        delete mGrid;
169
        delete mGrid;
170
    mGrid = new FloatGrid(total_grid, cellSize);
170
    mGrid = new FloatGrid(total_grid, cellSize);
171
    mGrid->initialize(1.f);
171
    mGrid->initialize(1.f);
172
    if (mHeightGrid)
172
    if (mHeightGrid)
173
        delete mHeightGrid;
173
        delete mHeightGrid;
174
    mHeightGrid = new HeightGrid(total_grid, cellSize*5);
174
    mHeightGrid = new HeightGrid(total_grid, cellSize*5);
175
    mHeightGrid->wipe(); // set all to zero
175
    mHeightGrid->wipe(); // set all to zero
176
    Tree::setGrid(mGrid, mHeightGrid);
176
    Tree::setGrid(mGrid, mHeightGrid);
177
177
178
    // setup the spatial location of the project area
178
    // setup the spatial location of the project area
179
    if (xml.hasNode("location")) {
179
    if (xml.hasNode("location")) {
180
        // setup of spatial location
180
        // setup of spatial location
181
        double loc_x = xml.valueDouble("location.x");
181
        double loc_x = xml.valueDouble("location.x");
182
        double loc_y = xml.valueDouble("location.y");
182
        double loc_y = xml.valueDouble("location.y");
183
        double loc_z = xml.valueDouble("location.z");
183
        double loc_z = xml.valueDouble("location.z");
184
        double loc_rot = xml.valueDouble("location.rotation");
184
        double loc_rot = xml.valueDouble("location.rotation");
185
        setupGISTransformation(loc_x, loc_y, loc_z, loc_rot);
185
        setupGISTransformation(loc_x, loc_y, loc_z, loc_rot);
186
        qDebug() << "setup of spatial location: x/y/z" << loc_x << loc_y << loc_z << "rotation:" << loc_rot;
186
        qDebug() << "setup of spatial location: x/y/z" << loc_x << loc_y << loc_z << "rotation:" << loc_rot;
187
    } else {
187
    } else {
188
        setupGISTransformation(0., 0., 0., 0.);
188
        setupGISTransformation(0., 0., 0., 0.);
189
    }
189
    }
190
190
191
    // load environment (multiple climates, speciesSets, ...
191
    // load environment (multiple climates, speciesSets, ...
192
    if (mEnvironment)
192
    if (mEnvironment)
193
        delete mEnvironment;
193
        delete mEnvironment;
194
    mEnvironment = new Environment();
194
    mEnvironment = new Environment();
195
195
196
    if (xml.valueBool("environmentEnabled", false)) {
196
    if (xml.valueBool("environmentEnabled", false)) {
197
        QString env_file = GlobalSettings::instance()->path(xml.value("environmentFile"));
197
        QString env_file = GlobalSettings::instance()->path(xml.value("environmentFile"));
198
        bool grid_mode = (xml.value("environmentMode")=="grid");
198
        bool grid_mode = (xml.value("environmentMode")=="grid");
199
        QString grid_file = GlobalSettings::instance()->path(xml.value("environmentGrid"));
199
        QString grid_file = GlobalSettings::instance()->path(xml.value("environmentGrid"));
200
        if (grid_mode) {
200
        if (grid_mode) {
201
            if (QFile::exists(grid_file))
201
            if (QFile::exists(grid_file))
202
                mEnvironment->setGridMode(grid_file);
202
                mEnvironment->setGridMode(grid_file);
203
            else
203
            else
204
                throw IException(QString("File '%1' specified in key 'environmentGrid' does not exit ('environmentMode' is 'grid').").arg(grid_file) );
204
                throw IException(QString("File '%1' specified in key 'environmentGrid' does not exit ('environmentMode' is 'grid').").arg(grid_file) );
205
        }
205
        }
206
206
207
        if (!mEnvironment->loadFromFile(env_file))
207
        if (!mEnvironment->loadFromFile(env_file))
208
            return;
208
            return;
209
    } else {
209
    } else {
210
        // load and prepare default values
210
        // load and prepare default values
211
        // (2) SpeciesSets: currently only one a global species set.
211
        // (2) SpeciesSets: currently only one a global species set.
212
        SpeciesSet *speciesSet = new SpeciesSet();
212
        SpeciesSet *speciesSet = new SpeciesSet();
213
        mSpeciesSets.push_back(speciesSet);
213
        mSpeciesSets.push_back(speciesSet);
214
        speciesSet->setup();
214
        speciesSet->setup();
215
        // Climate...
215
        // Climate...
216
        Climate *c = new Climate();
216
        Climate *c = new Climate();
217
        mClimates.push_back(c);
217
        mClimates.push_back(c);
218
        mEnvironment->setDefaultValues(c, speciesSet);
218
        mEnvironment->setDefaultValues(c, speciesSet);
219
    } // environment?
219
    } // environment?
220
220
221
    // time series data
221
    // time series data
222
    if (xml.valueBool(".timeEventsEnabled", false)) {
222
    if (xml.valueBool(".timeEventsEnabled", false)) {
223
        mTimeEvents = new TimeEvents();
223
        mTimeEvents = new TimeEvents();
224
        mTimeEvents->loadFromFile(GlobalSettings::instance()->path(xml.value("timeEventsFile"), "script"));
224
        mTimeEvents->loadFromFile(GlobalSettings::instance()->path(xml.value("timeEventsFile"), "script"));
225
    }
225
    }
226
226
227
227
228
    // simple case: create ressource units in a regular grid.
228
    // simple case: create ressource units in a regular grid.
229
    if (xml.valueBool("resourceUnitsAsGrid")) {
229
    if (xml.valueBool("resourceUnitsAsGrid")) {
230
230
231
        mRUmap.setup(QRectF(0., 0., width, height),100.); // Grid, that holds positions of resource units
231
        mRUmap.setup(QRectF(0., 0., width, height),100.); // Grid, that holds positions of resource units
232
        mRUmap.wipe();
232
        mRUmap.wipe();
233
233
234
        bool mask_is_setup = false;
234
        bool mask_is_setup = false;
235
        if (xml.valueBool("standGrid.enabled")) {
235
        if (xml.valueBool("standGrid.enabled")) {
236
            QString fileName = GlobalSettings::instance()->path(xml.value("standGrid.fileName"));
236
            QString fileName = GlobalSettings::instance()->path(xml.value("standGrid.fileName"));
237
            mStandGrid = new MapGrid(fileName,false); // create stand grid index later
237
            mStandGrid = new MapGrid(fileName,false); // create stand grid index later
238
238
239
            if (mStandGrid->isValid()) {
239
            if (mStandGrid->isValid()) {
240
                for (int i=0;i<mStandGrid->grid().count();i++) {
240
                for (int i=0;i<mStandGrid->grid().count();i++) {
241
                    const int &grid_value = mStandGrid->grid().constValueAtIndex(i);
241
                    const int &grid_value = mStandGrid->grid().constValueAtIndex(i);
242
                    mHeightGrid->valueAtIndex(i).setValid( grid_value > -1 );
242
                    mHeightGrid->valueAtIndex(i).setValid( grid_value > -1 );
243
                    if (grid_value>-1)
243
                    if (grid_value>-1)
244
                        mRUmap.valueAt(mStandGrid->grid().cellCenterPoint(i)) = (ResourceUnit*)1;
244
                        mRUmap.valueAt(mStandGrid->grid().cellCenterPoint(i)) = (ResourceUnit*)1;
245
                    if (grid_value < -1)
245
                    if (grid_value < -1)
246
                        mHeightGrid->valueAtIndex(i).setForestOutside(true);
246
                        mHeightGrid->valueAtIndex(i).setForestOutside(true);
247
                }
247
                }
248
            }
248
            }
249
            mask_is_setup = true;
249
            mask_is_setup = true;
250
        } else {
250
        } else {
251
            if (!GlobalSettings::instance()->settings().paramValueBool("torus")) {
251
            if (!GlobalSettings::instance()->settings().paramValueBool("torus")) {
252
                // in the case we have no stand grid but only a large rectangle (without the torus option)
252
                // in the case we have no stand grid but only a large rectangle (without the torus option)
253
                // we assume a forest outside
253
                // we assume a forest outside
254
                for (int i=0;i<mHeightGrid->count();++i) {
254
                for (int i=0;i<mHeightGrid->count();++i) {
255
                    const QPointF &p = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i));
255
                    const QPointF &p = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i));
256
                    if (p.x() < 0. || p.x()>width || p.y()<0. || p.y()>height) {
256
                    if (p.x() < 0. || p.x()>width || p.y()<0. || p.y()>height) {
257
                        mHeightGrid->valueAtIndex(i).setForestOutside(true);
257
                        mHeightGrid->valueAtIndex(i).setForestOutside(true);
258
                        mHeightGrid->valueAtIndex(i).setValid(false);
258
                        mHeightGrid->valueAtIndex(i).setValid(false);
259
                    }
259
                    }
260
                }
260
                }
261
261
262
            }
262
            }
263
        }
263
        }
264
264
265
        ResourceUnit **p; // ptr to ptr!
265
        ResourceUnit **p; // ptr to ptr!
266
        ResourceUnit *new_ru;
266
        ResourceUnit *new_ru;
267
267
268
        int ru_index = 0;
268
        int ru_index = 0;
269
        for (p=mRUmap.begin(); p!=mRUmap.end(); ++p) {
269
        for (p=mRUmap.begin(); p!=mRUmap.end(); ++p) {
270
            QRectF r = mRUmap.cellRect(mRUmap.indexOf(p));
270
            QRectF r = mRUmap.cellRect(mRUmap.indexOf(p));
271
            if (!mStandGrid || !mStandGrid->isValid() || *p>NULL) {
271
            if (!mStandGrid || !mStandGrid->isValid() || *p>NULL) {
272
                mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used.
272
                mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used.
273
                // create resource units for valid positions only
273
                // create resource units for valid positions only
274
                new_ru = new ResourceUnit(ru_index++); // create resource unit
274
                new_ru = new ResourceUnit(ru_index++); // create resource unit
275
                new_ru->setClimate( mEnvironment->climate() );
275
                new_ru->setClimate( mEnvironment->climate() );
276
                new_ru->setSpeciesSet( mEnvironment->speciesSet() );
276
                new_ru->setSpeciesSet( mEnvironment->speciesSet() );
277
                new_ru->setup();
277
                new_ru->setup();
278
                new_ru->setID( mEnvironment->currentID() ); // set id of resource unit in grid mode
278
                new_ru->setID( mEnvironment->currentID() ); // set id of resource unit in grid mode
279
                new_ru->setBoundingBox(r);
279
                new_ru->setBoundingBox(r);
280
                mRU.append(new_ru);
280
                mRU.append(new_ru);
281
                *p = new_ru; // save in the RUmap grid
281
                *p = new_ru; // save in the RUmap grid
282
            }
282
            }
283
        }
283
        }
284
        if (mEnvironment) {
284
        if (mEnvironment) {
285
            // retrieve species sets and climates (that were really used)
285
            // retrieve species sets and climates (that were really used)
286
            mSpeciesSets << mEnvironment->speciesSetList();
286
            mSpeciesSets << mEnvironment->speciesSetList();
287
            mClimates << mEnvironment->climateList();
287
            mClimates << mEnvironment->climateList();
288
            QString climate_file_list;
288
            QString climate_file_list;
289
            for (int i=0, c=0;i<mClimates.count();++i) {
289
            for (int i=0, c=0;i<mClimates.count();++i) {
290
                climate_file_list += mClimates[i]->name() + ", ";
290
                climate_file_list += mClimates[i]->name() + ", ";
291
                if (++c>5) {
291
                if (++c>5) {
292
                    climate_file_list += "...";
292
                    climate_file_list += "...";
293
                    break;
293
                    break;
294
                }
294
                }
295
295
296
            }
296
            }
297
            qDebug() << "Setup of climates: #loaded:" << mClimates.count() << "tables:" << climate_file_list;
297
            qDebug() << "Setup of climates: #loaded:" << mClimates.count() << "tables:" << climate_file_list;
298
298
299
299
300
        }
300
        }
301
301
302
        qDebug() << "setup of" << mEnvironment->climateList().size() << "climates performed.";
302
        qDebug() << "setup of" << mEnvironment->climateList().size() << "climates performed.";
303
303
304
        if (mStandGrid && mStandGrid->isValid())
304
        if (mStandGrid && mStandGrid->isValid())
305
            mStandGrid->createIndex();
305
            mStandGrid->createIndex();
306
        // now store the pointers in the grid.
306
        // now store the pointers in the grid.
307
        // Important: This has to be done after the mRU-QList is complete - otherwise pointers would
307
        // Important: This has to be done after the mRU-QList is complete - otherwise pointers would
308
        // point to invalid memory when QList's memory is reorganized (expanding)
308
        // point to invalid memory when QList's memory is reorganized (expanding)
309
//        ru_index = 0;
309
//        ru_index = 0;
310
//        for (p=mRUmap.begin();p!=mRUmap.end(); ++p) {
310
//        for (p=mRUmap.begin();p!=mRUmap.end(); ++p) {
311
//            *p = mRU.value(ru_index++);
311
//            *p = mRU.value(ru_index++);
312
//        }
312
//        }
313
        qDebug() << "created a grid of ResourceUnits: count=" << mRU.count() << "number of RU-map-cells:" << mRUmap.count();
313
        qDebug() << "created a grid of ResourceUnits: count=" << mRU.count() << "number of RU-map-cells:" << mRUmap.count();
314
314
315
315
316
        calculateStockableArea();
316
        calculateStockableArea();
317
317
318
        // setup of the project area mask
318
        // setup of the project area mask
319
        if (!mask_is_setup && xml.valueBool("areaMask.enabled", false) && xml.hasNode("areaMask.imageFile")) {
319
        if (!mask_is_setup && xml.valueBool("areaMask.enabled", false) && xml.hasNode("areaMask.imageFile")) {
320
            // to be extended!!! e.g. to load ESRI-style text files....
320
            // to be extended!!! e.g. to load ESRI-style text files....
321
            // setup a grid with the same size as the height grid...
321
            // setup a grid with the same size as the height grid...
322
            FloatGrid tempgrid((int)mHeightGrid->cellsize(), mHeightGrid->sizeX(), mHeightGrid->sizeY());
322
            FloatGrid tempgrid((int)mHeightGrid->cellsize(), mHeightGrid->sizeX(), mHeightGrid->sizeY());
323
            QString fileName = GlobalSettings::instance()->path(xml.value("areaMask.imageFile"));
323
            QString fileName = GlobalSettings::instance()->path(xml.value("areaMask.imageFile"));
324
            loadGridFromImage(fileName, tempgrid); // fetch from image
324
            loadGridFromImage(fileName, tempgrid); // fetch from image
325
            for (int i=0;i<tempgrid.count(); i++)
325
            for (int i=0;i<tempgrid.count(); i++)
326
                mHeightGrid->valueAtIndex(i).setValid( tempgrid.valueAtIndex(i)>0.99 );
326
                mHeightGrid->valueAtIndex(i).setValid( tempgrid.valueAtIndex(i)>0.99 );
327
            qDebug() << "loaded project area mask from" << fileName;
327
            qDebug() << "loaded project area mask from" << fileName;
328
        }
328
        }
329
329
330
        // list of "valid" resource units
330
        // list of "valid" resource units
331
        QList<ResourceUnit*> valid_rus;
331
        QList<ResourceUnit*> valid_rus;
332
        foreach(ResourceUnit* ru, mRU)
332
        foreach(ResourceUnit* ru, mRU)
333
            if (ru->id()!=-1)
333
            if (ru->id()!=-1)
334
                valid_rus.append(ru);
334
                valid_rus.append(ru);
335
335
336
        // setup of the digital elevation map (if present)
336
        // setup of the digital elevation map (if present)
337
        QString dem_file = xml.value("DEM");
337
        QString dem_file = xml.value("DEM");
338
        if (!dem_file.isEmpty()) {
338
        if (!dem_file.isEmpty()) {
339
            mDEM = new DEM(GlobalSettings::instance()->path(dem_file));
339
            mDEM = new DEM(GlobalSettings::instance()->path(dem_file));
340
            // add them to the visuals...
340
            // add them to the visuals...
341
            GlobalSettings::instance()->controller()->addGrid(mDEM, "DEM height", GridViewRainbow, 0, 1000);
341
            GlobalSettings::instance()->controller()->addGrid(mDEM, "DEM height", GridViewRainbow, 0, 1000);
342
            GlobalSettings::instance()->controller()->addGrid(mDEM->slopeGrid(), "DEM slope", GridViewRainbow, 0, 3);
342
            GlobalSettings::instance()->controller()->addGrid(mDEM->slopeGrid(), "DEM slope", GridViewRainbow, 0, 3);
343
            GlobalSettings::instance()->controller()->addGrid(mDEM->aspectGrid(), "DEM aspect", GridViewRainbow, 0, 360);
343
            GlobalSettings::instance()->controller()->addGrid(mDEM->aspectGrid(), "DEM aspect", GridViewRainbow, 0, 360);
344
            GlobalSettings::instance()->controller()->addGrid(mDEM->viewGrid(), "DEM view", GridViewGray, 0, 1);
344
            GlobalSettings::instance()->controller()->addGrid(mDEM->viewGrid(), "DEM view", GridViewGray, 0, 1);
345
345
346
        }
346
        }
347
347
348
        // setup of the grass cover
348
        // setup of the grass cover
349
        if (!mGrassCover)
349
        if (!mGrassCover)
350
            mGrassCover = new GrassCover();
350
            mGrassCover = new GrassCover();
351
        mGrassCover->setup();
351
        mGrassCover->setup();
352
352
353
        // setup of external modules
353
        // setup of external modules
354
        mModules->setup();
354
        mModules->setup();
355
        if (mModules->hasSetupResourceUnits()) {
355
        if (mModules->hasSetupResourceUnits()) {
356
            for (ResourceUnit **p=mRUmap.begin(); p!=mRUmap.end(); ++p) {
356
            for (ResourceUnit **p=mRUmap.begin(); p!=mRUmap.end(); ++p) {
357
                QRectF r = mRUmap.cellRect(mRUmap.indexOf(p));
-
 
358
                mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used.
-
 
359
                mModules->setupResourceUnit( *p );
-
 
-
 
357
                if (*p) {
-
 
358
                    QRectF r = mRUmap.cellRect(mRUmap.indexOf(p));
-
 
359
                    mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used.
-
 
360
                    mModules->setupResourceUnit( *p );
-
 
361
                }
360
            }
362
            }
361
        }
363
        }
362
364
363
        // setup of scripting environment
365
        // setup of scripting environment
364
        ScriptGlobal::setupGlobalScripting();
366
        ScriptGlobal::setupGlobalScripting();
365
367
366
        // setup the helper that does the multithreading
368
        // setup the helper that does the multithreading
367
        threadRunner.setup(valid_rus);
369
        threadRunner.setup(valid_rus);
368
        threadRunner.setMultithreading(GlobalSettings::instance()->settings().valueBool("system.settings.multithreading"));
370
        threadRunner.setMultithreading(GlobalSettings::instance()->settings().valueBool("system.settings.multithreading"));
369
        threadRunner.print();
371
        threadRunner.print();
370
372
371
373
372
    } else  {
374
    } else  {
373
        throw IException("resourceUnitsAsGrid MUST be set to true - at least currently :)");
375
        throw IException("resourceUnitsAsGrid MUST be set to true - at least currently :)");
374
    }
376
    }
375
    mSetup = true;
377
    mSetup = true;
376
}
378
}
377
379
378
380
379
/** clear() frees all ressources allocated with the run of a simulation.
381
/** clear() frees all ressources allocated with the run of a simulation.
380

382

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