Subversion Repositories public iLand

Rev

Rev 802 | Rev 821 | 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
        tseed.showElapsed();
646
        // establishment
615 werner 647
        { DebugTimer t("saplingGrowth");
648
        executePerResourceUnit( nc_sapling_growth, false /* true: force single thraeded operation */);
649
        GlobalSettings::instance()->systemStatistics()->tSaplingGrowth+=t.elapsed();
650
        }
651
        {
440 werner 652
        DebugTimer t("establishment");
475 werner 653
        executePerResourceUnit( nc_establishment, false /* true: force single thraeded operation */);
615 werner 654
        GlobalSettings::instance()->systemStatistics()->tEstablishment+=t.elapsed();
655
        }
656
    }
440 werner 657
 
468 werner 658
    // calculate soil / snag dynamics
475 werner 659
    if (settings().carbonCycleEnabled) {
660
        DebugTimer ccycle("carbon cylce");
661
        executePerResourceUnit( nc_carbonCycle, false /* true: force single thraeded operation */);
615 werner 662
        GlobalSettings::instance()->systemStatistics()->tCarbonCycle+=ccycle.elapsed();
663
 
475 werner 664
    }
649 werner 665
 
666
    // external modules/disturbances
667
    mModules->run();
668
 
664 werner 669
    foreach(ResourceUnit *ru, mRU) {
720 werner 670
        // remove trees that died during disturbances.
671
        if (ru->hasDiedTrees()) {
672
            ru->cleanTreeList(); // clean up the died trees
673
            ru->recreateStandStatistics(); // re-evaluate the stand statistics for this resource unit
674
        }
664 werner 675
    }
676
 
615 werner 677
    DebugTimer toutput("outputs");
278 werner 678
    // calculate statistics
679
    foreach(ResourceUnit *ru, mRU)
680
        ru->yearEnd();
124 Werner 681
 
277 werner 682
    // create outputs
184 werner 683
    OutputManager *om = GlobalSettings::instance()->outputManager();
285 werner 684
    om->execute("tree"); // single tree output
278 werner 685
    om->execute("stand"); //resource unit level x species
504 werner 686
    om->execute("sapling"); // sapling layer per RU x species
278 werner 687
    om->execute("production_month"); // 3pg responses growth per species x RU x month
285 werner 688
    om->execute("dynamicstand"); // output with user-defined columns (based on species x RU)
278 werner 689
    om->execute("standdead"); // resource unit level x species
690
    om->execute("management"); // resource unit level x species
587 werner 691
    om->execute("carbon"); // resource unit level, carbon pools above and belowground
609 werner 692
    om->execute("carbonflow"); // resource unit level, GPP, NPP and total carbon flows (atmosphere, harvest, ...)
185 werner 693
 
615 werner 694
    GlobalSettings::instance()->systemStatistics()->tWriteOutput+=toutput.elapsed();
695
    GlobalSettings::instance()->systemStatistics()->tTotalYear+=t.elapsed();
696
    GlobalSettings::instance()->systemStatistics()->writeOutput();
697
 
162 werner 698
    GlobalSettings::instance()->setCurrentYear(GlobalSettings::instance()->currentYear()+1);
105 Werner 699
}
700
 
278 werner 701
 
702
 
105 Werner 703
void Model::afterStop()
704
{
705
    // do some cleanup
706
}
106 Werner 707
 
369 werner 708
/// multithreaded running function for LIP printing
187 iland 709
ResourceUnit* nc_applyPattern(ResourceUnit *unit)
106 Werner 710
{
711
 
712
    QVector<Tree>::iterator tit;
118 Werner 713
    QVector<Tree>::iterator tend = unit->trees().end();
107 Werner 714
 
632 werner 715
    try {
107 Werner 716
 
632 werner 717
        // light concurrence influence
718
        if (!GlobalSettings::instance()->settings().paramValueBool("torus")) {
719
            // height dominance grid
720
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
721
                (*tit).heightGrid(); // just do it ;)
155 werner 722
 
632 werner 723
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
724
                (*tit).applyLIP(); // just do it ;)
155 werner 725
 
632 werner 726
        } else {
727
            // height dominance grid
728
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
729
                (*tit).heightGrid_torus(); // just do it ;)
155 werner 730
 
632 werner 731
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
732
                (*tit).applyLIP_torus(); // do it the wraparound way
733
        }
734
        return unit;
735
    } catch (const IException &e) {
736
        GlobalSettings::instance()->controller()->throwError(e.message());
118 Werner 737
    }
738
    return unit;
739
}
106 Werner 740
 
369 werner 741
/// multithreaded running function for LIP value extraction
187 iland 742
ResourceUnit *nc_readPattern(ResourceUnit *unit)
118 Werner 743
{
744
    QVector<Tree>::iterator tit;
745
    QVector<Tree>::iterator  tend = unit->trees().end();
632 werner 746
    try {
747
        if (!GlobalSettings::instance()->settings().paramValueBool("torus")) {
748
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
749
                (*tit).readLIF(); // multipliactive approach
750
        } else {
751
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
752
                (*tit).readLIF_torus(); // do it the wraparound way
753
        }
754
    } catch (const IException &e) {
755
        GlobalSettings::instance()->controller()->throwError(e.message());
118 Werner 756
    }
757
    return unit;
106 Werner 758
}
759
 
369 werner 760
/// multithreaded running function for the growth of individual trees
187 iland 761
ResourceUnit *nc_grow(ResourceUnit *unit)
118 Werner 762
{
763
    QVector<Tree>::iterator tit;
764
    QVector<Tree>::iterator  tend = unit->trees().end();
632 werner 765
    try {
766
        unit->beforeGrow(); // reset statistics
767
        // calculate light responses
768
        // responses are based on *modified* values for LightResourceIndex
769
        for (tit=unit->trees().begin(); tit!=tend; ++tit) {
770
            (*tit).calcLightResponse();
771
        }
118 Werner 772
 
632 werner 773
        unit->calculateInterceptedArea();
251 werner 774
 
632 werner 775
        for (tit=unit->trees().begin(); tit!=tend; ++tit) {
776
            (*tit).grow(); // actual growth of individual trees
777
        }
778
    } catch (const IException &e) {
779
        GlobalSettings::instance()->controller()->throwError(e.message());
118 Werner 780
    }
615 werner 781
 
782
    GlobalSettings::instance()->systemStatistics()->treeCount+=unit->trees().count();
118 Werner 783
    return unit;
784
}
785
 
369 werner 786
/// multithreaded running function for the resource level production
787
ResourceUnit *nc_production(ResourceUnit *unit)
788
{
632 werner 789
    try {
790
        unit->production();
791
    } catch (const IException &e) {
792
        GlobalSettings::instance()->controller()->throwError(e.message());
793
    }
369 werner 794
    return unit;
795
}
796
 
440 werner 797
 
124 Werner 798
void Model::test()
799
{
800
    // Test-funktion: braucht 1/3 time von readGrid()
801
    DebugTimer t("test");
802
    FloatGrid averaged = mGrid->averaged(10);
803
    int count = 0;
804
    float *end = averaged.end();
805
    for (float *p=averaged.begin(); p!=end; ++p)
806
        if (*p > 0.9)
807
            count++;
808
    qDebug() << count << "LIF>0.9 of " << averaged.count();
809
}
810
 
734 werner 811
void Model::debugCheckAllTrees()
812
{
813
    AllTreeIterator at(this);
814
    bool has_errors = false; double dummy=0.;
815
    while (Tree *t = at.next()) {
816
        // plausibility
817
        if (t->dbh()<0 || t->dbh()>10000. || t->biomassFoliage()<0. || t->height()>1000. || t->height() < 0.
818
                || t->biomassFoliage() <0.)
819
            has_errors = true;
820
        // check for objects....
821
        dummy = t->stamp()->offset() + t->ru()->ruSpecies()[1]->statistics().count();
822
    }
823
    if (has_errors)
824
        qDebug() << "model: debugCheckAllTrees found problems" << dummy;
825
}
826
 
118 Werner 827
void Model::applyPattern()
828
{
829
 
830
    DebugTimer t("applyPattern()");
831
    // intialize grids...
720 werner 832
    initializeGrid();
551 werner 833
 
406 werner 834
    // initialize height grid with a value of 4m. This is the height of the regeneration layer
551 werner 835
    for (HeightGridValue *h=mHeightGrid->begin();h!=mHeightGrid->end();++h) {
836
        h->resetCount(); // set count = 0, but do not touch the flags
837
        h->height = 4.f;
838
    }
118 Werner 839
 
123 Werner 840
    threadRunner.run(nc_applyPattern);
615 werner 841
    GlobalSettings::instance()->systemStatistics()->tApplyPattern+=t.elapsed();
118 Werner 842
}
843
 
106 Werner 844
void Model::readPattern()
845
{
846
    DebugTimer t("readPattern()");
123 Werner 847
    threadRunner.run(nc_readPattern);
615 werner 848
    GlobalSettings::instance()->systemStatistics()->tReadPattern+=t.elapsed();
849
 
106 Werner 850
}
851
 
331 werner 852
/** Main function for the growth of stands and trees.
853
   This includes several steps.
854
   (1) calculate the stocked area (i.e. count pixels in height grid)
855
   (2) 3PG production (including response calculation, water cycle)
856
   (3) single tree growth (including mortality)
857
   (4) cleanup of tree lists (remove dead trees)
858
  */
106 Werner 859
void Model::grow()
860
{
151 iland 861
 
369 werner 862
    if (!settings().growthEnabled)
863
        return;
864
    { DebugTimer t("growRU()");
865
    calculateStockedArea();
113 Werner 866
 
374 werner 867
    // multithreaded: mutex for the message handler in mainwindow solved the crashes.
868
    threadRunner.run(nc_production);
370 werner 869
    }
870
 
871
    DebugTimer t("growTrees()");
251 werner 872
    threadRunner.run(nc_grow); // actual growth of individual trees
159 werner 873
 
187 iland 874
    foreach(ResourceUnit *ru, mRU) {
159 werner 875
       ru->cleanTreeList();
376 werner 876
       ru->afterGrow();
168 werner 877
       //qDebug() << (b-n) << "trees died (of" << b << ").";
159 werner 878
   }
615 werner 879
   GlobalSettings::instance()->systemStatistics()->tTreeGrowth+=t.elapsed();
123 Werner 880
}
151 iland 881
 
240 werner 882
/** calculate for each resource unit the fraction of area which is stocked.
883
  This is done by checking the pixels of the global height grid.
151 iland 884
  */
885
void Model::calculateStockedArea()
886
{
887
    // iterate over the whole heightgrid and count pixels for each ressource unit
888
    HeightGridValue *end = mHeightGrid->end();
889
    QPointF cp;
187 iland 890
    ResourceUnit *ru;
151 iland 891
    for (HeightGridValue *i=mHeightGrid->begin(); i!=end; ++i) {
892
        cp = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i));
893
        if (mRUmap.coordValid(cp)) {
894
            ru = mRUmap.valueAt(cp);
895
            if (ru) {
285 werner 896
                ru->countStockedPixel( (*i).count()>0 );
151 iland 897
            }
898
        }
899
 
900
    }
901
}
240 werner 902
 
664 werner 903
/** calculate for each resource unit the stockable area.
574 werner 904
  "stockability" is determined by the isValid flag of resource units which in turn
905
  is derived from stand grid values.
906
  */
907
void Model::calculateStockableArea()
908
{
909
 
910
    foreach(ResourceUnit *ru, mRU) {
720 werner 911
        // //
912
        //        if (ru->id()==-1) {
913
        //            ru->setStockableArea(0.);
914
        //            continue;
915
        //        }
574 werner 916
        GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox());
917
        int valid=0, total=0;
918
        while (runner.next()) {
919
            if ( runner.current()->isValid() )
920
                valid++;
921
            total++;
922
        }
575 werner 923
        if (total) {
574 werner 924
            ru->setStockableArea( cHeightPixelArea * valid);
734 werner 925
            if (valid==0 && ru->id()>-1) {
926
                // invalidate this resource unit
927
                ru->setID(-1);
928
            }
575 werner 929
            if (valid>0 && ru->id()==-1) {
930
                qDebug() << "Warning: a resource unit has id=-1 but stockable area (id was set to 0)!!! ru: " << ru->boundingBox() << "with index" << ru->index();
931
                ru->setID(0);
664 werner 932
                // test-code
933
                //GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox());
934
                //while (runner.next()) {
935
                //    qDebug() << mHeightGrid->cellCenterPoint(mHeightGrid->indexOf( runner.current() )) << ": " << runner.current()->isValid();
936
                //}
585 werner 937
 
575 werner 938
            }
939
        } else
574 werner 940
            throw IException("calculateStockableArea: resource unit without pixels!");
941
 
942
    }
720 werner 943
    // mark those pixels that are at the edge of a "forest-out-of-area"
944
    GridRunner<HeightGridValue> runner(*mHeightGrid, mHeightGrid->metricRect());
945
    HeightGridValue* neighbors[8];
946
    while (runner.next()) {
947
        if (runner.current()->isForestOutside()) {
948
            // if the current pixel is a "radiating" border pixel,
949
            // then check the neighbors and set a flag if the pixel is a neighbor of a in-project-area pixel.
950
            runner.neighbors8(neighbors);
951
            for (int i=0;i<8;++i)
952
                if (neighbors[i] &&  neighbors[i]->isValid())
953
                    runner.current()->setIsRadiating();
954
 
955
        }
956
    }
957
 
574 werner 958
}
959
 
720 werner 960
void Model::initializeGrid()
961
{
962
    // fill the whole grid with a value of "1."
963
    mGrid->initialize(1.f);
574 werner 964
 
720 werner 965
    // apply special values for grid cells border regions where out-of-area cells
966
    // radiate into the main LIF grid.
967
    QPoint p;
968
    int ix_min, ix_max, iy_min, iy_max, ix_center, iy_center;
969
    const int px_offset = cPxPerHeight / 2; // for 5 px per height grid cell, the offset is 2
970
    const int max_radiate_distance = 7;
971
    const float step_width = 1.f / (float)max_radiate_distance;
972
    int c_rad = 0;
973
    for (HeightGridValue *hgv=mHeightGrid->begin(); hgv!=mHeightGrid->end(); ++hgv) {
974
        if (hgv->isRadiating()) {
975
            p=mHeightGrid->indexOf(hgv);
976
            ix_min = p.x() * cPxPerHeight - max_radiate_distance + px_offset;
977
            ix_max = ix_min + 2*max_radiate_distance + 1;
978
            ix_center = ix_min + max_radiate_distance;
979
            iy_min = p.y() * cPxPerHeight - max_radiate_distance + px_offset;
980
            iy_max = iy_min + 2*max_radiate_distance + 1;
981
            iy_center = iy_min + max_radiate_distance;
982
            for (int y=iy_min; y<=iy_max; ++y) {
983
                for (int x=ix_min; x<=ix_max; ++x) {
802 werner 984
                    if (!mGrid->isIndexValid(x,y) ||  !(*mHeightGrid)(x/cPxPerHeight, y/cPxPerHeight).isValid())
720 werner 985
                        continue;
986
                    float value = qMax(qAbs(x-ix_center), qAbs(y-iy_center)) * step_width;
987
                    float &v = mGrid->valueAtIndex(x, y);
988
                    if (value>=0.f && v>value)
989
                        v = value;
990
                }
991
            }
992
            c_rad++;
993
        }
994
    }
721 werner 995
    if (logLevelDebug())
996
        qDebug() << "initialize grid:" << c_rad << "radiating pixels...";
720 werner 997
 
998
}
999
 
1000
 
241 werner 1001
/// Force the creation of stand statistics.
1002
/// - stocked area (for resourceunit-areas)
1003
/// - ru - statistics
240 werner 1004
void Model::createStandStatistics()
1005
{
241 werner 1006
    calculateStockedArea();
482 werner 1007
    foreach(ResourceUnit *ru, mRU) {
1008
        ru->addTreeAgingForAllTrees();
240 werner 1009
        ru->createStandStatistics();
482 werner 1010
    }
240 werner 1011
}
584 werner 1012
 
1013