Subversion Repositories public iLand

Rev

Rev 808 | Rev 824 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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