Subversion Repositories public iLand

Rev

Rev 779 | Rev 808 | 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"
281 werner 35
#include "environment.h"
340 werner 36
#include "timeevents.h"
106 Werner 37
#include "helper.h"
189 iland 38
#include "resourceunit.h"
208 werner 39
#include "climate.h"
92 Werner 40
#include "speciesset.h"
106 Werner 41
#include "standloader.h"
42
#include "tree.h"
185 werner 43
#include "management.h"
200 werner 44
#include "modelsettings.h"
615 werner 45
#include "standstatistics.h"
543 werner 46
#include "mapgrid.h"
632 werner 47
#include "modelcontroller.h"
641 werner 48
#include "modules.h"
654 werner 49
#include "dem.h"
92 Werner 50
 
202 werner 51
#include "outputmanager.h"
176 werner 52
 
105 Werner 53
#include <QtCore>
54
#include <QtXml>
55
 
107 Werner 56
/** iterate over all trees of the model. return NULL if all trees processed.
57
  Usage:
58
  @code
59
  AllTreeIterator trees(model);
60
  while (Tree *tree = trees.next()) { // returns NULL when finished.
61
     tree->something(); // do something
62
  }
281 werner 63
  @endcode  */
107 Werner 64
Tree *AllTreeIterator::next()
65
{
143 Werner 66
 
107 Werner 67
    if (!mTreeEnd) {
68
        // initialize to first ressource unit
69
        mRUIterator = mModel->ruList().constBegin();
314 werner 70
        // fast forward to the first RU with trees
71
        while (mRUIterator!=mModel->ruList().constEnd()) {
72
            if ((*mRUIterator)->trees().count()>0)
73
                break;
753 werner 74
            ++mRUIterator;
314 werner 75
        }
76
            // finished if all RU processed
77
        if (mRUIterator == mModel->ruList().constEnd())
78
            return NULL;
143 Werner 79
        mTreeEnd = &((*mRUIterator)->trees().back()) + 1; // let end point to "1 after end" (STL-style)
107 Werner 80
        mCurrent = &((*mRUIterator)->trees().front());
81
    }
82
    if (mCurrent==mTreeEnd) {
753 werner 83
        ++mRUIterator; // switch to next RU (loop until RU with trees is found)
314 werner 84
        while (mRUIterator!=mModel->ruList().constEnd()) {
85
            if ((*mRUIterator)->trees().count()>0) {
86
                break;
87
            }
753 werner 88
            ++mRUIterator;
314 werner 89
        }
107 Werner 90
        if (mRUIterator == mModel->ruList().constEnd()) {
143 Werner 91
            mCurrent = NULL;
92
            return NULL; // finished!!
107 Werner 93
        }else {
143 Werner 94
            mTreeEnd = &((*mRUIterator)->trees().back()) + 1;
107 Werner 95
            mCurrent = &((*mRUIterator)->trees().front());
96
        }
97
    }
143 Werner 98
 
99
    return mCurrent++;
107 Werner 100
}
157 werner 101
Tree *AllTreeIterator::nextLiving()
102
{
103
    while (Tree *t = next())
158 werner 104
        if (!t->isDead()) return t;
157 werner 105
    return NULL;
106
}
107
Tree *AllTreeIterator::current() const
108
{
109
    return mCurrent?mCurrent-1:NULL;
110
}
107 Werner 111
 
112
 
200 werner 113
ModelSettings Model::mSettings;
92 Werner 114
Model::Model()
115
{
116
    initialize();
137 Werner 117
    GlobalSettings::instance()->setModel(this);
767 werner 118
    GlobalSettings::instance()->resetScriptEngine(); // clear the script
130 Werner 119
    QString dbg="running in release mode.";
442 werner 120
    DBGMODE( dbg="running in debug mode."; );
130 Werner 121
    qDebug() << dbg;
92 Werner 122
}
123
 
124
Model::~Model()
125
{
126
    clear();
137 Werner 127
    GlobalSettings::instance()->setModel(NULL);
92 Werner 128
}
129
 
130
/** Initial setup of the Model.
131
  */
132
void Model::initialize()
133
{
151 iland 134
   mSetup = false;
162 werner 135
   GlobalSettings::instance()->setCurrentYear(0);
151 iland 136
   mGrid = 0;
137
   mHeightGrid = 0;
185 werner 138
   mManagement = 0;
281 werner 139
   mEnvironment = 0;
340 werner 140
   mTimeEvents = 0;
549 werner 141
   mStandGrid = 0;
641 werner 142
   mModules = 0;
654 werner 143
   mDEM = 0;
92 Werner 144
}
145
 
261 werner 146
/** sets up the simulation space.
147
*/
103 Werner 148
void Model::setupSpace()
149
{
194 werner 150
    XmlHelper xml(GlobalSettings::instance()->settings().node("model.world"));
192 werner 151
    double cellSize = xml.value("cellSize", "2").toDouble();
152
    double width = xml.value("width", "100").toDouble();
153
    double height = xml.value("height", "100").toDouble();
549 werner 154
    double buffer = xml.value("buffer", "60").toDouble();
155
    mModelRect = QRectF(0., 0., width, height);
156
 
103 Werner 157
    qDebug() << QString("setup of the world: %1x%2m with cell-size=%3m and %4m buffer").arg(width).arg(height).arg(cellSize).arg(buffer);
158
 
159
    QRectF total_grid(QPointF(-buffer, -buffer), QPointF(width+buffer, height+buffer));
160
    qDebug() << "setup grid rectangle:" << total_grid;
161
 
151 iland 162
    if (mGrid)
163
        delete mGrid;
103 Werner 164
    mGrid = new FloatGrid(total_grid, cellSize);
156 werner 165
    mGrid->initialize(1.f);
151 iland 166
    if (mHeightGrid)
167
        delete mHeightGrid;
168
    mHeightGrid = new HeightGrid(total_grid, cellSize*5);
285 werner 169
    mHeightGrid->wipe(); // set all to zero
156 werner 170
    Tree::setGrid(mGrid, mHeightGrid);
105 Werner 171
 
569 werner 172
    // setup the spatial location of the project area
173
    if (xml.hasNode("location")) {
174
        // setup of spatial location
175
        double loc_x = xml.valueDouble("location.x");
176
        double loc_y = xml.valueDouble("location.y");
177
        double loc_z = xml.valueDouble("location.z");
178
        double loc_rot = xml.valueDouble("location.rotation");
179
        setupGISTransformation(loc_x, loc_y, loc_z, loc_rot);
180
        qDebug() << "setup of spatial location: x/y/z" << loc_x << loc_y << loc_z << "rotation:" << loc_rot;
181
    } else {
182
        setupGISTransformation(0., 0., 0., 0.);
183
    }
184
 
567 werner 185
    // load environment (multiple climates, speciesSets, ...
186
    if (mEnvironment)
187
        delete mEnvironment;
188
    mEnvironment = new Environment();
281 werner 189
 
567 werner 190
    if (xml.valueBool("environmentEnabled", false)) {
191
        QString env_file = GlobalSettings::instance()->path(xml.value("environmentFile"));
192
        bool grid_mode = (xml.value("environmentMode")=="grid");
193
        QString grid_file = GlobalSettings::instance()->path(xml.value("environmentGrid"));
194
        if (grid_mode && QFile::exists(grid_file))
195
            mEnvironment->setGridMode(grid_file);
196
 
197
        if (!mEnvironment->loadFromFile(env_file))
198
            return;
199
        // retrieve species sets and climates:
200
        mSpeciesSets << mEnvironment->speciesSetList();
201
        mClimates << mEnvironment->climateList();
202
    } else {
203
        // load and prepare default values
204
        // (2) SpeciesSets: currently only one a global species set.
205
        SpeciesSet *speciesSet = new SpeciesSet();
206
        mSpeciesSets.push_back(speciesSet);
207
        speciesSet->setup();
208
        // Climate...
209
        Climate *c = new Climate();
210
        mClimates.push_back(c);
211
        mEnvironment->setDefaultValues(c, speciesSet);
212
    } // environment?
213
 
214
    // time series data
215
    if (xml.valueBool(".timeEventsEnabled", false)) {
216
        mTimeEvents = new TimeEvents();
584 werner 217
        mTimeEvents->loadFromFile(GlobalSettings::instance()->path(xml.value("timeEventsFile"), "script"));
567 werner 218
    }
219
 
646 werner 220
 
105 Werner 221
    // simple case: create ressource units in a regular grid.
106 Werner 222
    mRUmap.clear();
194 werner 223
    if (xml.valueBool("resourceUnitsAsGrid")) {
281 werner 224
        mRUmap.setup(QRectF(0., 0., width, height),100.); // Grid, that holds positions of resource units
646 werner 225
        ResourceUnit **p; // ptr to ptr!
226
        ResourceUnit *new_ru;
227
 
281 werner 228
        int ru_index = 0;
229
        for (p=mRUmap.begin(); p!=mRUmap.end(); ++p) {
106 Werner 230
            QRectF r = mRUmap.cellRect(mRUmap.indexOf(p));
281 werner 231
            mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used.
232
            new_ru = new ResourceUnit(ru_index++); // create resource unit
233
            new_ru->setClimate( mEnvironment->climate() );
234
            new_ru->setSpeciesSet( mEnvironment->speciesSet() );
241 werner 235
            new_ru->setup();
569 werner 236
            new_ru->setID( mEnvironment->currentID() ); // set id of resource unit in grid mode
105 Werner 237
            new_ru->setBoundingBox(r);
281 werner 238
            mRU.append(new_ru);
285 werner 239
            *p = new_ru; // save also in the RUmap grid
105 Werner 240
        }
281 werner 241
 
143 Werner 242
        // now store the pointers in the grid.
243
        // Important: This has to be done after the mRU-QList is complete - otherwise pointers would
244
        // point to invalid memory when QList's memory is reorganized (expanding)
285 werner 245
        ru_index = 0;
143 Werner 246
        for (p=mRUmap.begin();p!=mRUmap.end(); ++p) {
247
            *p = mRU.value(ru_index++);
248
        }
187 iland 249
        qDebug() << "created a grid of ResourceUnits: count=" << mRU.count();
569 werner 250
 
539 werner 251
        bool mask_is_setup = false;
549 werner 252
 
539 werner 253
        if (xml.valueBool("standGrid.enabled")) {
254
            QString fileName = GlobalSettings::instance()->path(xml.value("standGrid.fileName"));
549 werner 255
            mStandGrid = new MapGrid(fileName);
543 werner 256
 
549 werner 257
            if (mStandGrid->isValid()) {
714 werner 258
                for (int i=0;i<mStandGrid->grid().count();i++) {
259
                    const int &grid_value = mStandGrid->grid().constValueAtIndex(i);
260
                    mHeightGrid->valueAtIndex(i).setValid( grid_value > -1 );
261
                    if (grid_value < -1)
718 werner 262
                        mHeightGrid->valueAtIndex(i).setForestOutside(true);
714 werner 263
                }
539 werner 264
            }
265
            mask_is_setup = true;
802 werner 266
        } else {
267
            if (!GlobalSettings::instance()->settings().paramValueBool("torus")) {
268
                // in the case we have no stand grid but only a large rectangle (without the torus option)
269
                // we assume a forest outside
270
                for (int i=0;i<mHeightGrid->count();++i) {
271
                    const QPointF &p = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i));
272
                    if (p.x() < 0. || p.x()>width || p.y()<0. || p.y()>height) {
273
                        mHeightGrid->valueAtIndex(i).setForestOutside(true);
274
                        mHeightGrid->valueAtIndex(i).setValid(false);
275
                    }
276
                }
277
 
278
            }
539 werner 279
        }
280
 
574 werner 281
        calculateStockableArea();
282
 
285 werner 283
        // setup of the project area mask
539 werner 284
        if (!mask_is_setup && xml.valueBool("areaMask.enabled", false) && xml.hasNode("areaMask.imageFile")) {
285 werner 285
            // to be extended!!! e.g. to load ESRI-style text files....
286
            // setup a grid with the same size as the height grid...
287
            FloatGrid tempgrid((int)mHeightGrid->cellsize(), mHeightGrid->sizeX(), mHeightGrid->sizeY());
288
            QString fileName = GlobalSettings::instance()->path(xml.value("areaMask.imageFile"));
289
            loadGridFromImage(fileName, tempgrid); // fetch from image
290
            for (int i=0;i<tempgrid.count(); i++)
291
                mHeightGrid->valueAtIndex(i).setValid( tempgrid.valueAtIndex(i)>0.99 );
292
            qDebug() << "loaded project area mask from" << fileName;
293
        }
294
 
590 werner 295
        // list of "valid" resource units
296
        QList<ResourceUnit*> valid_rus;
297
        foreach(ResourceUnit* ru, mRU)
298
            if (ru->id()!=-1)
299
                valid_rus.append(ru);
300
 
654 werner 301
        // setup of the digital elevation map (if present)
302
        QString dem_file = xml.value("DEM");
303
        if (!dem_file.isEmpty()) {
656 werner 304
            mDEM = new DEM(GlobalSettings::instance()->path(dem_file));
305
            // add them to the visuals...
306
            GlobalSettings::instance()->controller()->addGrid(mDEM, "DEM height", GridViewRainbow, 0, 1000);
307
            GlobalSettings::instance()->controller()->addGrid(mDEM->slopeGrid(), "DEM slope", GridViewRainbow, 0, 3);
308
            GlobalSettings::instance()->controller()->addGrid(mDEM->aspectGrid(), "DEM aspect", GridViewRainbow, 0, 360);
309
            GlobalSettings::instance()->controller()->addGrid(mDEM->viewGrid(), "DEM view", GridViewGray, 0, 1);
310
 
654 werner 311
        }
312
 
646 werner 313
        // setup of external modules
314
        mModules->setup();
315
        if (mModules->hasSetupResourceUnits()) {
316
            for (ResourceUnit **p=mRUmap.begin(); p!=mRUmap.end(); ++p) {
317
                QRectF r = mRUmap.cellRect(mRUmap.indexOf(p));
318
                mEnvironment->setPosition( r.center() ); // if environment is 'disabled' default values from the project file are used.
319
                mModules->setupResourceUnit( *p );
320
            }
321
        }
322
 
767 werner 323
        // setup of scripting environment
324
        ScriptGlobal::setupGlobalScripting();
325
 
123 Werner 326
        // setup the helper that does the multithreading
590 werner 327
        threadRunner.setup(valid_rus);
194 werner 328
        threadRunner.setMultithreading(GlobalSettings::instance()->settings().valueBool("system.settings.multithreading"));
123 Werner 329
        threadRunner.print();
105 Werner 330
 
194 werner 331
    } else  {
332
        throw IException("resourceUnitsAsGrid MUST be set to true - at least currently :)");
105 Werner 333
    }
120 Werner 334
    mSetup = true;
103 Werner 335
}
336
 
337
 
92 Werner 338
/** clear() frees all ressources allocated with the run of a simulation.
339
 
340
  */
341
void Model::clear()
342
{
143 Werner 343
    mSetup = false;
151 iland 344
    qDebug() << "Model clear: attempting to clear" << mRU.count() << "RU, " << mSpeciesSets.count() << "SpeciesSets.";
92 Werner 345
    // clear ressource units
103 Werner 346
    qDeleteAll(mRU); // delete ressource units (and trees)
92 Werner 347
    mRU.clear();
103 Werner 348
 
349
    qDeleteAll(mSpeciesSets); // delete species sets
92 Werner 350
    mSpeciesSets.clear();
103 Werner 351
 
208 werner 352
    // delete climate data
353
    qDeleteAll(mClimates);
354
 
151 iland 355
    // delete the grids
356
    if (mGrid)
357
        delete mGrid;
358
    if (mHeightGrid)
359
        delete mHeightGrid;
185 werner 360
    if (mManagement)
361
        delete mManagement;
281 werner 362
    if (mEnvironment)
363
        delete mEnvironment;
340 werner 364
    if (mTimeEvents)
365
        delete mTimeEvents;
549 werner 366
    if (mStandGrid)
367
        delete mStandGrid;
641 werner 368
    if (mModules)
369
        delete mModules;
654 werner 370
    if (mDEM)
371
        delete mDEM;
103 Werner 372
 
185 werner 373
    mGrid = 0;
374
    mHeightGrid = 0;
375
    mManagement = 0;
281 werner 376
    mEnvironment = 0;
340 werner 377
    mTimeEvents = 0;
549 werner 378
    mStandGrid  = 0;
641 werner 379
    mModules = 0;
654 werner 380
    mDEM = 0;
185 werner 381
 
583 werner 382
    GlobalSettings::instance()->outputManager()->close();
383
 
92 Werner 384
    qDebug() << "Model ressources freed.";
385
}
386
 
387
/** Setup of the Simulation.
388
  This really creates the simulation environment and does the setup of various aspects.
389
  */
102 Werner 390
void Model::loadProject()
92 Werner 391
{
109 Werner 392
    DebugTimer dt("load project");
93 Werner 393
    GlobalSettings *g = GlobalSettings::instance();
679 werner 394
    g->printDirecories();
102 Werner 395
    const XmlHelper &xml = g->settings();
189 iland 396
 
93 Werner 397
    g->clearDatabaseConnections();
92 Werner 398
    // database connections: reset
94 Werner 399
    GlobalSettings::instance()->clearDatabaseConnections();
286 werner 400
    // input and climate connection
401
    // see initOutputDatabase() for output database
191 werner 402
    QString dbPath = g->path( xml.value("system.database.in"), "database");
194 werner 403
    GlobalSettings::instance()->setupDatabaseConnection("in", dbPath, true);
193 werner 404
    dbPath = g->path( xml.value("system.database.climate"), "database");
194 werner 405
    GlobalSettings::instance()->setupDatabaseConnection("climate", dbPath, true);
92 Werner 406
 
200 werner 407
    mSettings.loadModelSettings();
408
    mSettings.print();
416 werner 409
    // random seed: if stored value is <> 0, use this as the random seed (and produce hence always an equal sequence of random numbers)
410
    uint seed = xml.value("system.settings.randomSeed","0").toUInt();
708 werner 411
    RandomGenerator::setup(RandomGenerator::ergMersenneTwister, seed); // use the MersenneTwister as default
428 werner 412
    // linearization of expressions: if true *and* linearize() is explicitely called, then
413
    // function results will be cached over a defined range of values.
414
    bool do_linearization = xml.valueBool("system.settings.expressionLinearizationEnabled", false);
415
    Expression::setLinearizationEnabled(do_linearization);
431 werner 416
    if (do_linearization)
417
        qDebug() << "The linearization of certains expressions is enabled (performance optimization).";
418
 
419
    // log level
420
    QString log_level = xml.value("system.settings.logLevel", "debug").toLower();
421
    if (log_level=="debug") setLogLevel(0);
422
    if (log_level=="info") setLogLevel(1);
423
    if (log_level=="warning") setLogLevel(2);
424
    if (log_level=="error") setLogLevel(3);
425
 
475 werner 426
    // snag dynamics / soil model enabled? (info used during setup of world)
427
    changeSettings().carbonCycleEnabled = xml.valueBool("model.settings.carbonCycleEnabled", false);
528 werner 428
    // class size of snag classes
429
    Snag::setupThresholds(xml.valueDouble("model.settings.soil.swdDBHClass12"),
430
                          xml.valueDouble("model.settings.soil.swdDBHClass23"));
475 werner 431
 
646 werner 432
    // setup of modules
433
    if (mModules)
434
        delete mModules;
435
    mModules = new Modules();
436
 
103 Werner 437
    setupSpace();
281 werner 438
    if (mRU.isEmpty())
439
        throw IException("Setup of Model: no resource units present!");
185 werner 440
 
441
    // (3) additional issues
387 werner 442
    // (3.1) setup of regeneration
391 werner 443
    changeSettings().regenerationEnabled = xml.valueBool("model.settings.regenerationEnabled", false);
444
    if (settings().regenerationEnabled) {
445
        foreach(SpeciesSet *ss, mSpeciesSets)
446
            ss->setupRegeneration();
387 werner 447
    }
448
 
493 werner 449
    Sapling::setRecruitmentVariation(xml.valueDouble("model.settings.seedDispersal.recruitmentDimensionVariation",0.1));
468 werner 450
 
285 werner 451
    // (3.2) management
191 werner 452
    QString mgmtFile = xml.value("model.management.file");
453
    if (!mgmtFile.isEmpty() && xml.valueBool("model.management.enabled")) {
185 werner 454
        mManagement = new Management();
455
        QString path = GlobalSettings::instance()->path(mgmtFile, "script");
456
        mManagement->loadScript(path);
457
        qDebug() << "setup management using script" << path;
458
    }
468 werner 459
 
460
 
92 Werner 461
}
105 Werner 462
 
463
 
543 werner 464
ResourceUnit *Model::ru(QPointF coord)
105 Werner 465
{
106 Werner 466
    if (!mRUmap.isEmpty() && mRUmap.coordValid(coord))
467
        return mRUmap.valueAt(coord);
281 werner 468
    return ru(); // default RU if there is only one
105 Werner 469
}
470
 
286 werner 471
void Model::initOutputDatabase()
472
{
473
    GlobalSettings *g = GlobalSettings::instance();
474
    QString dbPath = g->path(g->settings().value("system.database.out"), "output");
475
    // create run-metadata
476
    int maxid = SqlHelper::queryValue("select max(id) from runs", g->dbin()).toInt();
477
 
478
    maxid++;
479
    QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
287 werner 480
    SqlHelper::executeSql(QString("insert into runs (id, timestamp) values (%1, '%2')").arg(maxid).arg(timestamp), g->dbin());
286 werner 481
    // replace path information
482
    dbPath.replace("$id$", QString::number(maxid));
483
    dbPath.replace("$date$", timestamp);
484
    // setup final path
485
   g->setupDatabaseConnection("out", dbPath, false);
486
 
487
}
488
 
615 werner 489
ResourceUnit *nc_sapling_growth(ResourceUnit *unit)
440 werner 490
{
632 werner 491
    try {
492
        // define a height map for the current resource unit on the stack and clear it
493
        float sapling_map[cPxPerRU*cPxPerRU];
494
        for (int i=0;i<cPxPerRU*cPxPerRU;i++) sapling_map[i]=0.f;
495
        unit->setSaplingHeightMap(sapling_map);
444 werner 496
 
450 werner 497
 
451 werner 498
        // (1) calculate the growth of (already established) saplings (populate sapling map)
455 werner 499
        foreach (const ResourceUnitSpecies *rus, unit->ruSpecies()) {
500
            const_cast<ResourceUnitSpecies*>(rus)->calclulateSaplingGrowth();
440 werner 501
        }
615 werner 502
 
503
    } catch (const IException& e) {
632 werner 504
        GlobalSettings::instance()->controller()->throwError(e.message());
615 werner 505
    }
506
    return unit;
507
 
508
}
509
 
510
/// multithreaded running function for the resource unit level establishment
511
ResourceUnit *nc_establishment(ResourceUnit *unit)
512
{
513
    try {
514
 
450 werner 515
        // (2) calculate the establishment probabilities of new saplings
455 werner 516
        foreach (const ResourceUnitSpecies *rus, unit->ruSpecies()) {
517
            const_cast<ResourceUnitSpecies*>(rus)->calclulateEstablishment();
450 werner 518
        }
615 werner 519
 
440 werner 520
    } catch (const IException& e) {
632 werner 521
        GlobalSettings::instance()->controller()->throwError(e.message());
440 werner 522
    }
632 werner 523
 
440 werner 524
    return unit;
525
}
526
 
475 werner 527
/// multithreaded execution of the carbon cycle routine
528
ResourceUnit *nc_carbonCycle(ResourceUnit *unit)
529
{
611 werner 530
    try {
632 werner 531
        // (1) do calculations on snag dynamics for the resource unit
532
        unit->calculateCarbonCycle();
533
        // (2) do the soil carbon and nitrogen dynamics calculations (ICBM/2N)
534
    } catch (const IException& e) {
535
        GlobalSettings::instance()->controller()->throwError(e.message());
611 werner 536
    }
537
 
475 werner 538
    return unit;
539
}
540
 
440 werner 541
/// beforeRun performs several steps before the models starts running.
542
/// inter alia: * setup of the stands
543
///             * setup of the climates
105 Werner 544
void Model::beforeRun()
545
{
395 werner 546
    // setup outputs
547
    // setup output database
539 werner 548
    if (GlobalSettings::instance()->dbout().isOpen())
549
        GlobalSettings::instance()->dbout().close();
395 werner 550
    initOutputDatabase();
551
    GlobalSettings::instance()->outputManager()->setup();
552
    GlobalSettings::instance()->clearDebugLists();
553
 
105 Werner 554
    // initialize stands
251 werner 555
    {
106 Werner 556
    DebugTimer loadtrees("load trees");
557
    StandLoader loader(this);
558
    loader.processInit();
251 werner 559
    }
106 Werner 560
 
214 werner 561
    // load climate
251 werner 562
    {
734 werner 563
    if (logLevelDebug()) qDebug() << "attempting to load climate..." ;
251 werner 564
    DebugTimer loadclim("load climate");
214 werner 565
    foreach(Climate *c, mClimates)
280 werner 566
        if (!c->isSetup())
567
            c->setup();
214 werner 568
 
251 werner 569
    }
570
 
571
    { DebugTimer loadinit("load standstatistics");
734 werner 572
    if (logLevelDebug()) qDebug() << "attempting to calculate initial stand statistics (incl. apply and read pattern)..." ;
135 Werner 573
    Tree::setGrid(mGrid, mHeightGrid);
737 werner 574
    // debugCheckAllTrees(); // introduced for debugging session (2012-04-06)
135 Werner 575
    applyPattern();
576
    readPattern();
106 Werner 577
 
376 werner 578
    // force the compilation of initial stand statistics
241 werner 579
    createStandStatistics();
251 werner 580
    }
286 werner 581
 
264 werner 582
    // outputs to create with inital state (without any growth) are called here:
257 werner 583
    GlobalSettings::instance()->outputManager()->execute("stand"); // year=0
504 werner 584
    GlobalSettings::instance()->outputManager()->execute("sapling"); // year=0
264 werner 585
    GlobalSettings::instance()->outputManager()->execute("tree"); // year=0
395 werner 586
    GlobalSettings::instance()->outputManager()->execute("dynamicstand"); // year=0
264 werner 587
 
257 werner 588
    GlobalSettings::instance()->setCurrentYear(1); // set to first year
105 Werner 589
}
590
 
331 werner 591
/** Main model runner.
592
  The sequence of actions is as follows:
593
  (1) Load the climate of the new year
594
  (2) Reset statistics for resource unit as well as for dead/managed trees
714 werner 595
  (3) Invoke Management.
331 werner 596
  (4) *after* that, calculate Light patterns
597
  (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 598
  (6) execute Regeneration
714 werner 599
  (7) invoke disturbance modules
600
  (8) calculate statistics for the year
601
  (9) write database outputs
331 werner 602
  */
105 Werner 603
void Model::runYear()
604
{
223 werner 605
    DebugTimer t("Model::runYear()");
615 werner 606
    GlobalSettings::instance()->systemStatistics()->reset();
707 werner 607
    RandomGenerator::checkGenerator(); // see if we need to generate new numbers...
649 werner 608
    // initalization at start of year for external modules
609
    mModules->yearBegin();
341 werner 610
    // execute scheduled events for the current year
611
    if (mTimeEvents)
612
        mTimeEvents->run();
613
 
614
    // load the next year of the climate database
214 werner 615
    foreach(Climate *c, mClimates)
616
        c->nextYear();
617
 
278 werner 618
    // reset statistics
619
    foreach(ResourceUnit *ru, mRU)
620
        ru->newYear();
621
 
391 werner 622
    foreach(SpeciesSet *set, mSpeciesSets)
623
        set->newYear();
278 werner 624
    // management
615 werner 625
    if (mManagement) {
626
        DebugTimer t("management");
278 werner 627
        mManagement->run();
615 werner 628
        GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed();
629
    }
278 werner 630
 
214 werner 631
    // process a cycle of individual growth
277 werner 632
    applyPattern(); // create Light Influence Patterns
633
    readPattern(); // readout light state of individual trees
634
    grow(); // let the trees grow (growth on stand-level, tree-level, mortality)
106 Werner 635
 
391 werner 636
    // regeneration
637
    if (settings().regenerationEnabled) {
440 werner 638
        // seed dispersal
483 werner 639
        DebugTimer tseed("Regeneration and Establishment");
391 werner 640
        foreach(SpeciesSet *set, mSpeciesSets)
475 werner 641
            set->regeneration(); // parallel execution for each species set
440 werner 642
 
615 werner 643
        GlobalSettings::instance()->systemStatistics()->tSeedDistribution+=tseed.elapsed();
440 werner 644
        tseed.showElapsed();
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