Subversion Repositories public iLand

Rev

Rev 1104 | Rev 1114 | 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"
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
130 Werner 124
    QString dbg="running in release mode.";
442 werner 125
    DBGMODE( dbg="running in debug mode."; );
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;
176
    mHeightGrid = new HeightGrid(total_grid, cellSize*5);
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) {
203
            if (QFile::exists(grid_file))
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 {
253
            if (!GlobalSettings::instance()->settings().paramValueBool("torus")) {
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();
679 werner 457
    g->printDirecories();
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)
480
        qDebug() << "The linearization of certains expressions is enabled (performance optimization).";
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
    }
493 werner 521
    Sapling::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");
533
    if (!mgmtFile.isEmpty() && xml.valueBool("model.management.enabled")) {
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);
281 werner 565
    return ru(); // default RU if there is only one
105 Werner 566
}
567
 
286 werner 568
void Model::initOutputDatabase()
569
{
570
    GlobalSettings *g = GlobalSettings::instance();
571
    QString dbPath = g->path(g->settings().value("system.database.out"), "output");
572
    // create run-metadata
573
    int maxid = SqlHelper::queryValue("select max(id) from runs", g->dbin()).toInt();
574
 
575
    maxid++;
576
    QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
287 werner 577
    SqlHelper::executeSql(QString("insert into runs (id, timestamp) values (%1, '%2')").arg(maxid).arg(timestamp), g->dbin());
286 werner 578
    // replace path information
579
    dbPath.replace("$id$", QString::number(maxid));
580
    dbPath.replace("$date$", timestamp);
581
    // setup final path
582
   g->setupDatabaseConnection("out", dbPath, false);
583
 
584
}
585
 
903 werner 586
 
824 werner 587
/// multithreaded running function for the resource unit level establishment
588
ResourceUnit *nc_sapling_growth_establishment(ResourceUnit *unit)
440 werner 589
{
632 werner 590
    try {
1001 werner 591
        { // DebugTimer t("nc_saplingGrowth"); t.setSilent();
824 werner 592
        // define a height map for the current resource unit on the stack
632 werner 593
        float sapling_map[cPxPerRU*cPxPerRU];
824 werner 594
        // set the map and initialize it:
632 werner 595
        unit->setSaplingHeightMap(sapling_map);
444 werner 596
 
450 werner 597
 
451 werner 598
        // (1) calculate the growth of (already established) saplings (populate sapling map)
824 werner 599
        QList<ResourceUnitSpecies*>::const_iterator rus;
600
        for (rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus)
601
            (*rus)->calclulateSaplingGrowth();
615 werner 602
 
1001 werner 603
        } { // DebugTimer t("nc_Establishment"); t.setSilent();
998 werner 604
 
824 werner 605
        // (2) calculate the establishment probabilities of new saplings
998 werner 606
        for (QList<ResourceUnitSpecies*>::const_iterator rus=unit->ruSpecies().cbegin(); rus!=unit->ruSpecies().cend(); ++rus)
864 werner 607
            (*rus)->calculateEstablishment();
615 werner 608
 
998 werner 609
        }
615 werner 610
 
440 werner 611
    } catch (const IException& e) {
632 werner 612
        GlobalSettings::instance()->controller()->throwError(e.message());
440 werner 613
    }
632 werner 614
 
824 werner 615
    unit->setSaplingHeightMap(0); // invalidate again
440 werner 616
    return unit;
824 werner 617
 
440 werner 618
}
619
 
824 werner 620
 
475 werner 621
/// multithreaded execution of the carbon cycle routine
622
ResourceUnit *nc_carbonCycle(ResourceUnit *unit)
623
{
611 werner 624
    try {
632 werner 625
        // (1) do calculations on snag dynamics for the resource unit
626
        unit->calculateCarbonCycle();
627
        // (2) do the soil carbon and nitrogen dynamics calculations (ICBM/2N)
628
    } catch (const IException& e) {
629
        GlobalSettings::instance()->controller()->throwError(e.message());
611 werner 630
    }
631
 
475 werner 632
    return unit;
633
}
634
 
440 werner 635
/// beforeRun performs several steps before the models starts running.
636
/// inter alia: * setup of the stands
637
///             * setup of the climates
105 Werner 638
void Model::beforeRun()
639
{
395 werner 640
    // setup outputs
641
    // setup output database
539 werner 642
    if (GlobalSettings::instance()->dbout().isOpen())
643
        GlobalSettings::instance()->dbout().close();
395 werner 644
    initOutputDatabase();
645
    GlobalSettings::instance()->outputManager()->setup();
646
    GlobalSettings::instance()->clearDebugLists();
647
 
105 Werner 648
    // initialize stands
967 werner 649
    StandLoader loader(this);
251 werner 650
    {
106 Werner 651
    DebugTimer loadtrees("load trees");
652
    loader.processInit();
251 werner 653
    }
934 werner 654
    // initalization of ABE
655
    if (mABEManagement) {
656
        mABEManagement->setup();
1089 werner 657
        mABEManagement->runOnInit(true);
934 werner 658
    }
106 Werner 659
 
214 werner 660
    // load climate
251 werner 661
    {
1024 werner 662
        if (logLevelDebug()) qDebug() << "attempting to load climate..." ;
663
        DebugTimer loadclim("load climate");
664
        foreach(Climate *c, mClimates) {
665
            if (!c->isSetup())
666
                c->setup();
667
        }
668
        // load the first year of the climate database
669
        foreach(Climate *c, mClimates)
670
            c->nextYear();
214 werner 671
 
251 werner 672
    }
673
 
934 werner 674
 
251 werner 675
    { DebugTimer loadinit("load standstatistics");
734 werner 676
    if (logLevelDebug()) qDebug() << "attempting to calculate initial stand statistics (incl. apply and read pattern)..." ;
135 Werner 677
    Tree::setGrid(mGrid, mHeightGrid);
737 werner 678
    // debugCheckAllTrees(); // introduced for debugging session (2012-04-06)
135 Werner 679
    applyPattern();
680
    readPattern();
967 werner 681
    loader.processAfterInit(); // e.g. initialization of saplings
106 Werner 682
 
376 werner 683
    // force the compilation of initial stand statistics
241 werner 684
    createStandStatistics();
251 werner 685
    }
286 werner 686
 
934 werner 687
    // initalization of ABE (now all stands are properly set up)
909 werner 688
    if (mABEManagement) {
934 werner 689
        mABEManagement->initialize();
1089 werner 690
        mABEManagement->runOnInit(false);
890 werner 691
    }
692
 
693
 
264 werner 694
    // outputs to create with inital state (without any growth) are called here:
936 werner 695
    GlobalSettings::instance()->setCurrentYear(0); // set clock to "0" (for outputs with initial state)
696
 
257 werner 697
    GlobalSettings::instance()->outputManager()->execute("stand"); // year=0
837 werner 698
    GlobalSettings::instance()->outputManager()->execute("landscape"); // year=0
504 werner 699
    GlobalSettings::instance()->outputManager()->execute("sapling"); // year=0
264 werner 700
    GlobalSettings::instance()->outputManager()->execute("tree"); // year=0
395 werner 701
    GlobalSettings::instance()->outputManager()->execute("dynamicstand"); // year=0
264 werner 702
 
257 werner 703
    GlobalSettings::instance()->setCurrentYear(1); // set to first year
1024 werner 704
 
105 Werner 705
}
706
 
331 werner 707
/** Main model runner.
708
  The sequence of actions is as follows:
709
  (1) Load the climate of the new year
710
  (2) Reset statistics for resource unit as well as for dead/managed trees
714 werner 711
  (3) Invoke Management.
331 werner 712
  (4) *after* that, calculate Light patterns
713
  (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 714
  (6) execute Regeneration
714 werner 715
  (7) invoke disturbance modules
716
  (8) calculate statistics for the year
717
  (9) write database outputs
331 werner 718
  */
105 Werner 719
void Model::runYear()
720
{
223 werner 721
    DebugTimer t("Model::runYear()");
615 werner 722
    GlobalSettings::instance()->systemStatistics()->reset();
707 werner 723
    RandomGenerator::checkGenerator(); // see if we need to generate new numbers...
649 werner 724
    // initalization at start of year for external modules
725
    mModules->yearBegin();
903 werner 726
 
341 werner 727
    // execute scheduled events for the current year
728
    if (mTimeEvents)
729
        mTimeEvents->run();
730
 
1024 werner 731
    // load the next year of the climate database (except for the first year - the first climate year is loaded immediately
732
    if (GlobalSettings::instance()->currentYear()>1) {
733
        foreach(Climate *c, mClimates)
734
            c->nextYear();
735
    }
214 werner 736
 
278 werner 737
    // reset statistics
738
    foreach(ResourceUnit *ru, mRU)
739
        ru->newYear();
740
 
391 werner 741
    foreach(SpeciesSet *set, mSpeciesSets)
742
        set->newYear();
890 werner 743
    // management classic
615 werner 744
    if (mManagement) {
745
        DebugTimer t("management");
278 werner 746
        mManagement->run();
615 werner 747
        GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed();
748
    }
909 werner 749
    // ... or ABE (the agent based variant)
750
    if (mABEManagement) {
751
        DebugTimer t("ABE:run");
752
        mABEManagement->run();
890 werner 753
        GlobalSettings::instance()->systemStatistics()->tManagement+=t.elapsed();
754
    }
278 werner 755
 
937 werner 756
    // if trees are dead/removed because of management, the tree lists
757
    // need to be cleaned (and the statistics need to be recreated)
758
    cleanTreeLists();
759
 
214 werner 760
    // process a cycle of individual growth
277 werner 761
    applyPattern(); // create Light Influence Patterns
762
    readPattern(); // readout light state of individual trees
763
    grow(); // let the trees grow (growth on stand-level, tree-level, mortality)
1062 werner 764
    mGrassCover->execute(); // evaluate the grass / herb cover (and its effect on regeneration)
106 Werner 765
 
391 werner 766
    // regeneration
767
    if (settings().regenerationEnabled) {
440 werner 768
        // seed dispersal
483 werner 769
        DebugTimer tseed("Regeneration and Establishment");
391 werner 770
        foreach(SpeciesSet *set, mSpeciesSets)
475 werner 771
            set->regeneration(); // parallel execution for each species set
440 werner 772
 
615 werner 773
        GlobalSettings::instance()->systemStatistics()->tSeedDistribution+=tseed.elapsed();
440 werner 774
        // establishment
1063 werner 775
        Sapling::updateBrowsingPressure();
776
 
824 werner 777
        { DebugTimer t("saplingGrowthEstablishment");
778
        executePerResourceUnit( nc_sapling_growth_establishment, false /* true: force single thraeded operation */);
864 werner 779
        GlobalSettings::instance()->systemStatistics()->tSaplingAndEstablishment+=t.elapsed();
615 werner 780
        }
824 werner 781
 
1068 werner 782
        Establishment::debugInfo(); // debug test
783
 
615 werner 784
    }
440 werner 785
 
468 werner 786
    // calculate soil / snag dynamics
475 werner 787
    if (settings().carbonCycleEnabled) {
788
        DebugTimer ccycle("carbon cylce");
1063 werner 789
        executePerResourceUnit( nc_carbonCycle, false /* true: force single threaded operation */);
615 werner 790
        GlobalSettings::instance()->systemStatistics()->tCarbonCycle+=ccycle.elapsed();
791
 
475 werner 792
    }
649 werner 793
 
794
    // external modules/disturbances
795
    mModules->run();
796
 
937 werner 797
    // cleanup of tree lists if external modules removed trees.
798
    cleanTreeLists();
664 werner 799
 
937 werner 800
 
615 werner 801
    DebugTimer toutput("outputs");
278 werner 802
    // calculate statistics
803
    foreach(ResourceUnit *ru, mRU)
804
        ru->yearEnd();
124 Werner 805
 
277 werner 806
    // create outputs
184 werner 807
    OutputManager *om = GlobalSettings::instance()->outputManager();
285 werner 808
    om->execute("tree"); // single tree output
1102 werner 809
    om->execute("treeremoval"); // single removed tree output
278 werner 810
    om->execute("stand"); //resource unit level x species
837 werner 811
    om->execute("landscape"); //landscape x species
504 werner 812
    om->execute("sapling"); // sapling layer per RU x species
278 werner 813
    om->execute("production_month"); // 3pg responses growth per species x RU x month
285 werner 814
    om->execute("dynamicstand"); // output with user-defined columns (based on species x RU)
278 werner 815
    om->execute("standdead"); // resource unit level x species
816
    om->execute("management"); // resource unit level x species
587 werner 817
    om->execute("carbon"); // resource unit level, carbon pools above and belowground
609 werner 818
    om->execute("carbonflow"); // resource unit level, GPP, NPP and total carbon flows (atmosphere, harvest, ...)
185 werner 819
 
615 werner 820
    GlobalSettings::instance()->systemStatistics()->tWriteOutput+=toutput.elapsed();
821
    GlobalSettings::instance()->systemStatistics()->tTotalYear+=t.elapsed();
822
    GlobalSettings::instance()->systemStatistics()->writeOutput();
823
 
162 werner 824
    GlobalSettings::instance()->setCurrentYear(GlobalSettings::instance()->currentYear()+1);
1085 werner 825
 
826
    // try to clean up a bit of memory (useful if many large JS objects (e.g., grids) are used)
827
    GlobalSettings::instance()->scriptEngine()->collectGarbage();
105 Werner 828
}
829
 
278 werner 830
 
831
 
105 Werner 832
void Model::afterStop()
833
{
834
    // do some cleanup
835
}
106 Werner 836
 
369 werner 837
/// multithreaded running function for LIP printing
187 iland 838
ResourceUnit* nc_applyPattern(ResourceUnit *unit)
106 Werner 839
{
840
 
841
    QVector<Tree>::iterator tit;
118 Werner 842
    QVector<Tree>::iterator tend = unit->trees().end();
107 Werner 843
 
632 werner 844
    try {
107 Werner 845
 
632 werner 846
        // light concurrence influence
847
        if (!GlobalSettings::instance()->settings().paramValueBool("torus")) {
848
            // height dominance grid
849
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
850
                (*tit).heightGrid(); // just do it ;)
155 werner 851
 
632 werner 852
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
853
                (*tit).applyLIP(); // just do it ;)
155 werner 854
 
632 werner 855
        } else {
856
            // height dominance grid
857
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
858
                (*tit).heightGrid_torus(); // just do it ;)
155 werner 859
 
632 werner 860
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
861
                (*tit).applyLIP_torus(); // do it the wraparound way
862
        }
863
        return unit;
864
    } catch (const IException &e) {
865
        GlobalSettings::instance()->controller()->throwError(e.message());
118 Werner 866
    }
867
    return unit;
868
}
106 Werner 869
 
369 werner 870
/// multithreaded running function for LIP value extraction
187 iland 871
ResourceUnit *nc_readPattern(ResourceUnit *unit)
118 Werner 872
{
873
    QVector<Tree>::iterator tit;
874
    QVector<Tree>::iterator  tend = unit->trees().end();
632 werner 875
    try {
876
        if (!GlobalSettings::instance()->settings().paramValueBool("torus")) {
877
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
878
                (*tit).readLIF(); // multipliactive approach
879
        } else {
880
            for (tit=unit->trees().begin(); tit!=tend; ++tit)
881
                (*tit).readLIF_torus(); // do it the wraparound way
882
        }
883
    } catch (const IException &e) {
884
        GlobalSettings::instance()->controller()->throwError(e.message());
118 Werner 885
    }
886
    return unit;
106 Werner 887
}
888
 
369 werner 889
/// multithreaded running function for the growth of individual trees
187 iland 890
ResourceUnit *nc_grow(ResourceUnit *unit)
118 Werner 891
{
892
    QVector<Tree>::iterator tit;
893
    QVector<Tree>::iterator  tend = unit->trees().end();
632 werner 894
    try {
895
        unit->beforeGrow(); // reset statistics
896
        // calculate light responses
897
        // responses are based on *modified* values for LightResourceIndex
898
        for (tit=unit->trees().begin(); tit!=tend; ++tit) {
899
            (*tit).calcLightResponse();
900
        }
118 Werner 901
 
632 werner 902
        unit->calculateInterceptedArea();
251 werner 903
 
632 werner 904
        for (tit=unit->trees().begin(); tit!=tend; ++tit) {
905
            (*tit).grow(); // actual growth of individual trees
906
        }
907
    } catch (const IException &e) {
908
        GlobalSettings::instance()->controller()->throwError(e.message());
118 Werner 909
    }
615 werner 910
 
911
    GlobalSettings::instance()->systemStatistics()->treeCount+=unit->trees().count();
118 Werner 912
    return unit;
913
}
914
 
369 werner 915
/// multithreaded running function for the resource level production
916
ResourceUnit *nc_production(ResourceUnit *unit)
917
{
632 werner 918
    try {
919
        unit->production();
920
    } catch (const IException &e) {
921
        GlobalSettings::instance()->controller()->throwError(e.message());
922
    }
369 werner 923
    return unit;
924
}
925
 
440 werner 926
 
124 Werner 927
void Model::test()
928
{
929
    // Test-funktion: braucht 1/3 time von readGrid()
930
    DebugTimer t("test");
931
    FloatGrid averaged = mGrid->averaged(10);
932
    int count = 0;
933
    float *end = averaged.end();
934
    for (float *p=averaged.begin(); p!=end; ++p)
935
        if (*p > 0.9)
936
            count++;
937
    qDebug() << count << "LIF>0.9 of " << averaged.count();
938
}
939
 
734 werner 940
void Model::debugCheckAllTrees()
941
{
942
    AllTreeIterator at(this);
943
    bool has_errors = false; double dummy=0.;
944
    while (Tree *t = at.next()) {
945
        // plausibility
946
        if (t->dbh()<0 || t->dbh()>10000. || t->biomassFoliage()<0. || t->height()>1000. || t->height() < 0.
947
                || t->biomassFoliage() <0.)
948
            has_errors = true;
949
        // check for objects....
950
        dummy = t->stamp()->offset() + t->ru()->ruSpecies()[1]->statistics().count();
951
    }
952
    if (has_errors)
953
        qDebug() << "model: debugCheckAllTrees found problems" << dummy;
954
}
955
 
118 Werner 956
void Model::applyPattern()
957
{
958
 
959
    DebugTimer t("applyPattern()");
960
    // intialize grids...
720 werner 961
    initializeGrid();
551 werner 962
 
406 werner 963
    // initialize height grid with a value of 4m. This is the height of the regeneration layer
551 werner 964
    for (HeightGridValue *h=mHeightGrid->begin();h!=mHeightGrid->end();++h) {
965
        h->resetCount(); // set count = 0, but do not touch the flags
966
        h->height = 4.f;
967
    }
118 Werner 968
 
123 Werner 969
    threadRunner.run(nc_applyPattern);
615 werner 970
    GlobalSettings::instance()->systemStatistics()->tApplyPattern+=t.elapsed();
118 Werner 971
}
972
 
106 Werner 973
void Model::readPattern()
974
{
975
    DebugTimer t("readPattern()");
123 Werner 976
    threadRunner.run(nc_readPattern);
615 werner 977
    GlobalSettings::instance()->systemStatistics()->tReadPattern+=t.elapsed();
978
 
106 Werner 979
}
980
 
331 werner 981
/** Main function for the growth of stands and trees.
982
   This includes several steps.
983
   (1) calculate the stocked area (i.e. count pixels in height grid)
984
   (2) 3PG production (including response calculation, water cycle)
985
   (3) single tree growth (including mortality)
986
   (4) cleanup of tree lists (remove dead trees)
987
  */
106 Werner 988
void Model::grow()
989
{
151 iland 990
 
369 werner 991
    if (!settings().growthEnabled)
992
        return;
993
    { DebugTimer t("growRU()");
994
    calculateStockedArea();
113 Werner 995
 
374 werner 996
    // multithreaded: mutex for the message handler in mainwindow solved the crashes.
997
    threadRunner.run(nc_production);
370 werner 998
    }
999
 
1000
    DebugTimer t("growTrees()");
251 werner 1001
    threadRunner.run(nc_grow); // actual growth of individual trees
159 werner 1002
 
187 iland 1003
    foreach(ResourceUnit *ru, mRU) {
159 werner 1004
       ru->cleanTreeList();
376 werner 1005
       ru->afterGrow();
168 werner 1006
       //qDebug() << (b-n) << "trees died (of" << b << ").";
159 werner 1007
   }
615 werner 1008
   GlobalSettings::instance()->systemStatistics()->tTreeGrowth+=t.elapsed();
123 Werner 1009
}
151 iland 1010
 
240 werner 1011
/** calculate for each resource unit the fraction of area which is stocked.
1012
  This is done by checking the pixels of the global height grid.
151 iland 1013
  */
1014
void Model::calculateStockedArea()
1015
{
1016
    // iterate over the whole heightgrid and count pixels for each ressource unit
1017
    HeightGridValue *end = mHeightGrid->end();
1018
    QPointF cp;
187 iland 1019
    ResourceUnit *ru;
151 iland 1020
    for (HeightGridValue *i=mHeightGrid->begin(); i!=end; ++i) {
1021
        cp = mHeightGrid->cellCenterPoint(mHeightGrid->indexOf(i));
1022
        if (mRUmap.coordValid(cp)) {
1023
            ru = mRUmap.valueAt(cp);
1024
            if (ru) {
285 werner 1025
                ru->countStockedPixel( (*i).count()>0 );
151 iland 1026
            }
1027
        }
1028
 
1029
    }
1030
}
240 werner 1031
 
664 werner 1032
/** calculate for each resource unit the stockable area.
574 werner 1033
  "stockability" is determined by the isValid flag of resource units which in turn
1034
  is derived from stand grid values.
1035
  */
1036
void Model::calculateStockableArea()
1037
{
1038
 
1039
    foreach(ResourceUnit *ru, mRU) {
720 werner 1040
        // //
1041
        //        if (ru->id()==-1) {
1042
        //            ru->setStockableArea(0.);
1043
        //            continue;
1044
        //        }
574 werner 1045
        GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox());
1046
        int valid=0, total=0;
1047
        while (runner.next()) {
1048
            if ( runner.current()->isValid() )
1049
                valid++;
1050
            total++;
1051
        }
575 werner 1052
        if (total) {
574 werner 1053
            ru->setStockableArea( cHeightPixelArea * valid);
734 werner 1054
            if (valid==0 && ru->id()>-1) {
1055
                // invalidate this resource unit
1056
                ru->setID(-1);
1057
            }
575 werner 1058
            if (valid>0 && ru->id()==-1) {
1059
                qDebug() << "Warning: a resource unit has id=-1 but stockable area (id was set to 0)!!! ru: " << ru->boundingBox() << "with index" << ru->index();
1060
                ru->setID(0);
664 werner 1061
                // test-code
1062
                //GridRunner<HeightGridValue> runner(*mHeightGrid, ru->boundingBox());
1063
                //while (runner.next()) {
1064
                //    qDebug() << mHeightGrid->cellCenterPoint(mHeightGrid->indexOf( runner.current() )) << ": " << runner.current()->isValid();
1065
                //}
585 werner 1066
 
575 werner 1067
            }
1068
        } else
574 werner 1069
            throw IException("calculateStockableArea: resource unit without pixels!");
1070
 
1071
    }
720 werner 1072
    // mark those pixels that are at the edge of a "forest-out-of-area"
1073
    GridRunner<HeightGridValue> runner(*mHeightGrid, mHeightGrid->metricRect());
1074
    HeightGridValue* neighbors[8];
1075
    while (runner.next()) {
1076
        if (runner.current()->isForestOutside()) {
1077
            // if the current pixel is a "radiating" border pixel,
1078
            // then check the neighbors and set a flag if the pixel is a neighbor of a in-project-area pixel.
1079
            runner.neighbors8(neighbors);
1080
            for (int i=0;i<8;++i)
1081
                if (neighbors[i] &&  neighbors[i]->isValid())
1082
                    runner.current()->setIsRadiating();
1083
 
1084
        }
1085
    }
1086
 
574 werner 1087
}
1088
 
720 werner 1089
void Model::initializeGrid()
1090
{
1091
    // fill the whole grid with a value of "1."
1092
    mGrid->initialize(1.f);
574 werner 1093
 
720 werner 1094
    // apply special values for grid cells border regions where out-of-area cells
1095
    // radiate into the main LIF grid.
1096
    QPoint p;
1097
    int ix_min, ix_max, iy_min, iy_max, ix_center, iy_center;
1098
    const int px_offset = cPxPerHeight / 2; // for 5 px per height grid cell, the offset is 2
1099
    const int max_radiate_distance = 7;
1100
    const float step_width = 1.f / (float)max_radiate_distance;
1101
    int c_rad = 0;
1102
    for (HeightGridValue *hgv=mHeightGrid->begin(); hgv!=mHeightGrid->end(); ++hgv) {
1103
        if (hgv->isRadiating()) {
1104
            p=mHeightGrid->indexOf(hgv);
1105
            ix_min = p.x() * cPxPerHeight - max_radiate_distance + px_offset;
1106
            ix_max = ix_min + 2*max_radiate_distance + 1;
1107
            ix_center = ix_min + max_radiate_distance;
1108
            iy_min = p.y() * cPxPerHeight - max_radiate_distance + px_offset;
1109
            iy_max = iy_min + 2*max_radiate_distance + 1;
1110
            iy_center = iy_min + max_radiate_distance;
1111
            for (int y=iy_min; y<=iy_max; ++y) {
1112
                for (int x=ix_min; x<=ix_max; ++x) {
802 werner 1113
                    if (!mGrid->isIndexValid(x,y) ||  !(*mHeightGrid)(x/cPxPerHeight, y/cPxPerHeight).isValid())
720 werner 1114
                        continue;
1115
                    float value = qMax(qAbs(x-ix_center), qAbs(y-iy_center)) * step_width;
1116
                    float &v = mGrid->valueAtIndex(x, y);
1117
                    if (value>=0.f && v>value)
1118
                        v = value;
1119
                }
1120
            }
1121
            c_rad++;
1122
        }
1123
    }
721 werner 1124
    if (logLevelDebug())
1125
        qDebug() << "initialize grid:" << c_rad << "radiating pixels...";
720 werner 1126
 
1127
}
1128
 
1129
 
241 werner 1130
/// Force the creation of stand statistics.
1131
/// - stocked area (for resourceunit-areas)
1132
/// - ru - statistics
240 werner 1133
void Model::createStandStatistics()
1134
{
241 werner 1135
    calculateStockedArea();
482 werner 1136
    foreach(ResourceUnit *ru, mRU) {
1137
        ru->addTreeAgingForAllTrees();
240 werner 1138
        ru->createStandStatistics();
482 werner 1139
    }
240 werner 1140
}
584 werner 1141
 
936 werner 1142
void Model::cleanTreeLists()
1143
{
937 werner 1144
    foreach(ResourceUnit *ru, GlobalSettings::instance()->model()->ruList()) {
1145
        if (ru->hasDiedTrees()) {
1146
            ru->cleanTreeList();
1147
            ru->recreateStandStatistics();
1148
        }
1149
    }
936 werner 1150
}
584 werner 1151
 
936 werner 1152