Subversion Repositories public iLand

Rev

Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed

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