Subversion Repositories public iLand

Rev

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