Subversion Repositories public iLand

Rev

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

Rev Author Line No. Line
1
 
1033 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
 
908 werner 21
#include "abe_global.h"
807 werner 22
#include "globalsettings.h"
23
 
24
 
25
#include "forestmanagementengine.h"
808 werner 26
#include "activity.h"
811 werner 27
#include "fmunit.h"
28
#include "fmstand.h"
867 werner 29
#include "fmstp.h"
811 werner 30
#include "agent.h"
31
#include "agenttype.h"
813 werner 32
#include "fomescript.h"
33
#include "scriptglobal.h"
815 werner 34
#include "fomescript.h"
892 werner 35
#include "scheduler.h"
807 werner 36
 
915 werner 37
#include "unitout.h"
922 werner 38
#include "abestandout.h"
932 werner 39
#include "abestandremovalout.h"
915 werner 40
 
811 werner 41
#include "debugtimer.h"
42
 
863 werner 43
// general iLand stuff
44
#include "xmlhelper.h"
45
#include "csvfile.h"
46
#include "model.h"
47
#include "mapgrid.h"
867 werner 48
#include "helper.h"
878 werner 49
#include "threadrunner.h"
915 werner 50
#include "outputmanager.h"
863 werner 51
 
904 werner 52
#include "tree.h"
1070 werner 53
#include "resourceunit.h"
904 werner 54
 
915 werner 55
 
56
 
909 werner 57
Q_LOGGING_CATEGORY(abe, "abe")
870 werner 58
 
909 werner 59
Q_LOGGING_CATEGORY(abeSetup, "abe.setup")
884 werner 60
 
907 werner 61
namespace ABE {
870 werner 62
 
1095 werner 63
/** @defgroup abe iLand agent based forest management engine (ABE)
64
  ABE is the Agent Based management Engine that allows the simulation of both forest management activties (e.g., harvesting of trees)
65
  and forest managers (e.g., deciding when and where to execute an activity).
66
  The ABE framework relies heavily on a blend of C++ (for low-level management activties) and Javascript (for higher level definition of
67
  management programs).
68
 
69
  The smallest spatial entity is a forest stand (FMStand), which may be grouped into forest management unit (FMUnit). Forest managers (Agent) can select
70
  stand treatment programs (FMSTP) for a unit. The management activities derive from a basic activity (Activity); specialized code exists
71
  for various activities such as planting or thinning. A scheduler (Scheduler) keeps track of where and when to execute activities following
72
  guidelines given by the management agent (Agent). Agents represent individual foresters that may be grouped into AgentTypes (e.g., farmers).
73
 
74
 
75
 */
76
 
77
 
807 werner 78
/** @class ForestManagementEngine
1095 werner 79
 * @ingroup abe
807 werner 80
*/
815 werner 81
 
82
ForestManagementEngine *ForestManagementEngine::singleton_fome_engine = 0;
914 werner 83
int ForestManagementEngine::mMaxStandId = -1;
807 werner 84
ForestManagementEngine::ForestManagementEngine()
85
{
815 werner 86
    mScriptBridge = 0;
87
    singleton_fome_engine = this;
901 werner 88
    mCancel = false;
915 werner 89
    setupOutputs(); // add ABE output definitions
807 werner 90
}
91
 
815 werner 92
ForestManagementEngine::~ForestManagementEngine()
93
{
817 werner 94
    clear();
890 werner 95
    // script bridge: script ownership?
96
    //if (mScriptBridge)
97
    //    delete mScriptBridge;
815 werner 98
    singleton_fome_engine = 0;
99
}
100
 
873 werner 101
const MapGrid *ForestManagementEngine::standGrid()
813 werner 102
{
873 werner 103
    return GlobalSettings::instance()->model()->standGrid();
104
}
863 werner 105
 
873 werner 106
 
107
void ForestManagementEngine::setupScripting()
108
{
909 werner 109
    // setup the ABE system
863 werner 110
    const XmlHelper &xml = GlobalSettings::instance()->settings();
111
 
813 werner 112
    ScriptGlobal::setupGlobalScripting(); // general iLand scripting helper functions and such
113
 
909 werner 114
    // the link between the scripting and the C++ side of ABE
815 werner 115
    if (mScriptBridge)
116
        delete mScriptBridge;
117
    mScriptBridge = new FomeScript;
118
    mScriptBridge->setupScriptEnvironment();
863 werner 119
 
890 werner 120
    QString file_name = GlobalSettings::instance()->path(xml.value("model.management.abe.file"));
873 werner 121
    QString code = Helper::loadTextFile(file_name);
909 werner 122
    qCDebug(abeSetup) << "Loading script file" << file_name;
873 werner 123
    QJSValue result = GlobalSettings::instance()->scriptEngine()->evaluate(code,file_name);
124
    if (result.isError()) {
125
        int lineno = result.property("lineNumber").toInt();
126
        QStringList code_lines = code.replace('\r', "").split('\n'); // remove CR, split by LF
127
        QString code_part;
128
        for (int i=std::max(0, lineno - 5); i<std::min(lineno+5, code_lines.count()); ++i)
129
            code_part.append(QString("%1: %2 %3\n").arg(i).arg(code_lines[i]).arg(i==lineno?"  <---- [ERROR]":""));
909 werner 130
        qCDebug(abeSetup) << "Javascript Error in file" << result.property("fileName").toString() << ":" << result.property("lineNumber").toInt() << ":" << result.toString() << ":\n" << code_part;
873 werner 131
    }
132
}
133
 
903 werner 134
void ForestManagementEngine::prepareRun()
135
{
914 werner 136
    mStandLayoutChanged = false; // can be changed by salvage operations / stand polygon changes
903 werner 137
}
138
 
904 werner 139
void ForestManagementEngine::finalizeRun()
140
{
141
    // empty the harvest counter; it will be filled again
142
    // during the (next) year.
937 werner 143
 
936 werner 144
    foreach (FMStand *stand, mStands) {
904 werner 145
        stand->resetHarvestCounter();
936 werner 146
    }
914 werner 147
 
1157 werner 148
    foreach (FMUnit *unit, mUnits) {
149
        unit->resetHarvestCounter();
150
    }
151
 
914 werner 152
    //
153
    if (mStandLayoutChanged) {
154
        DebugTimer timer("ABE:stand_layout_update");
155
        // renew the internal stand grid
156
        FMStand **fm = mFMStandGrid.begin();
157
        for (int *p = standGrid()->grid().begin(); p!=standGrid()->grid().end(); ++p, ++fm)
158
            *fm = *p<0?0:mStandHash[*p];
159
        // renew neigborhood information in the stand grid
160
        const_cast<MapGrid*>(standGrid())->updateNeighborList();
161
        // renew the spatial indices
162
        const_cast<MapGrid*>(standGrid())->createIndex();
163
        mStandLayoutChanged = false;
164
 
165
        // now check the stands
1157 werner 166
        for (QVector<FMStand*>::iterator it=mStands.begin(); it!=mStands.end(); ++it) {
167
            // renew area
168
            (*it)->checkArea();
169
            // initial activity (if missing)
934 werner 170
            if (!(*it)->currentActivity()) {
171
                (*it)->initialize();
172
            }
1157 werner 173
        }
914 werner 174
    }
936 werner 175
 
904 werner 176
}
177
 
915 werner 178
void ForestManagementEngine::setupOutputs()
179
{
180
    if (GlobalSettings::instance()->outputManager()->find("abeUnit"))
181
        return; // already set up
182
    GlobalSettings::instance()->outputManager()->addOutput(new UnitOut);
922 werner 183
    GlobalSettings::instance()->outputManager()->addOutput(new ABEStandOut);
1074 werner 184
    GlobalSettings::instance()->outputManager()->addOutput(new ABEStandDetailsOut);
932 werner 185
    GlobalSettings::instance()->outputManager()->addOutput(new ABEStandRemovalOut);
915 werner 186
}
187
 
958 werner 188
void ForestManagementEngine::runJavascript()
189
{
190
    QJSValue handler = scriptEngine()->globalObject().property("run");
191
    if (handler.isCallable()) {
1088 werner 192
        scriptBridge()->setExecutionContext(0, false);
958 werner 193
        QJSValue result = handler.call(QJSValueList() << mCurrentYear);
194
        if (FMSTP::verbose())
195
            qCDebug(abe) << "executing 'run' function for year" << mCurrentYear << ", result:" << result.toString();
196
    }
197
 
198
    handler = scriptEngine()->globalObject().property("runStand");
199
    if (handler.isCallable()) {
200
        qCDebug(abe) << "running the 'runStand' javascript function for" << mStands.size() << "stands.";
201
        foreach (FMStand *stand, mStands) {
202
            scriptBridge()->setExecutionContext(stand, true);
203
            handler.call(QJSValueList() << mCurrentYear);
204
        }
205
    }
206
}
207
 
876 werner 208
AgentType *ForestManagementEngine::agentType(const QString &name)
209
{
210
    for (int i=0;i<mAgentTypes.count();++i)
211
        if (mAgentTypes[i]->name()==name)
212
            return mAgentTypes[i];
213
    return 0;
214
}
215
 
938 werner 216
Agent *ForestManagementEngine::agent(const QString &name)
217
{
218
    for (int i=0;i<mAgents.count();++i)
219
        if (mAgents[i]->name()==name)
220
            return mAgents[i];
221
    return 0;
222
}
915 werner 223
 
938 werner 224
 
915 werner 225
/*---------------------------------------------------------------------
226
 * multithreaded execution routines
227
---------------------------------------------------------------------*/
228
 
229
FMUnit *nc_execute_unit(FMUnit *unit)
230
{
231
    if (ForestManagementEngine::instance()->isCancel())
232
        return unit;
233
 
234
    //qDebug() << "called for unit" << unit;
235
    const QMultiMap<FMUnit*, FMStand*> &stand_map = ForestManagementEngine::instance()->stands();
236
    QMultiMap<FMUnit*, FMStand*>::const_iterator it = stand_map.constFind(unit);
237
    int executed = 0;
238
    int total = 0;
239
    while (it!=stand_map.constEnd() && it.key()==unit) {
240
        it.value()->stp()->executeRepeatingActivities(it.value());
241
        if (it.value()->execute())
242
            ++executed;
933 werner 243
        //MapGrid::freeLocksForStand( it.value()->id() );
915 werner 244
        if (ForestManagementEngine::instance()->isCancel())
245
            break;
246
 
247
        ++it;
248
        ++total;
249
    }
250
    if (ForestManagementEngine::instance()->isCancel())
251
        return unit;
252
 
253
    if (FMSTP::verbose())
254
        qCDebug(abe) << "execute unit'" << unit->id() << "', ran" << executed << "of" << total;
255
 
256
    // now run the scheduler
257
    unit->scheduler()->run();
258
 
259
    // collect the harvests
260
    it = stand_map.constFind(unit);
261
    while (it!=stand_map.constEnd() && it.key()==unit) {
262
        unit->addRealizedHarvest(it.value()->totalHarvest());
263
        ++it;
264
    }
265
 
266
 
267
    return unit;
268
}
269
 
270
FMUnit *nc_plan_update_unit(FMUnit *unit)
271
{
272
    if (ForestManagementEngine::instance()->isCancel())
273
        return unit;
274
 
275
    if (ForestManagementEngine::instance()->currentYear() % 10 == 0) {
276
        qCDebug(abe) << "*** execute decadal plan update ***";
277
        unit->managementPlanUpdate();
977 werner 278
        unit->runAgent();
915 werner 279
    }
280
 
281
 
282
    // first update happens *after* a full year of running ABE.
283
    if (ForestManagementEngine::instance()->currentYear()>1)
284
        unit->updatePlanOfCurrentYear();
285
 
286
    return unit;
287
}
288
 
289
 
290
 
873 werner 291
void ForestManagementEngine::setup()
292
{
909 werner 293
    QLoggingCategory::setFilterRules("abe.debug=true\n" \
294
                                     "abe.setup.debug=true"); // enable *all*
884 werner 295
 
934 werner 296
    DebugTimer time_setup("ABE:setupScripting");
873 werner 297
    clear();
298
 
299
    // (1) setup the scripting environment and load all the javascript code
300
    setupScripting();
901 werner 301
    if (isCancel()) {
909 werner 302
        throw IException(QString("ABE-Error (setup): %1").arg(mLastErrorMessage));
901 werner 303
    }
873 werner 304
 
305
    if (!GlobalSettings::instance()->model())
306
        throw IException("No model created.... invalid operation.");
934 werner 307
 
873 werner 308
    // (2) spatial data (stands, units, ...)
863 werner 309
    const MapGrid *stand_grid = GlobalSettings::instance()->model()->standGrid();
889 werner 310
 
863 werner 311
    if (stand_grid==NULL || stand_grid->isValid()==false)
909 werner 312
        throw IException("The ABE management model requires a valid stand grid.");
863 werner 313
 
934 werner 314
    const XmlHelper &xml = GlobalSettings::instance()->settings();
315
 
890 werner 316
    QString data_file_name = GlobalSettings::instance()->path(xml.value("model.management.abe.agentDataFile"));
938 werner 317
    qCDebug(abeSetup) << "loading ABE agentDataFile" << data_file_name << "...";
863 werner 318
    CSVFile data_file(data_file_name);
1208 werner 319
    if (data_file.isEmpty())
863 werner 320
        throw IException(QString("Stand-Initialization: the standDataFile file %1 is empty or missing!").arg(data_file_name));
321
    int ikey = data_file.columnIndex("id");
322
    int iunit = data_file.columnIndex("unit");
323
    int iagent = data_file.columnIndex("agent");
938 werner 324
    int iagent_type = data_file.columnIndex("agentType");
890 werner 325
    int istp = data_file.columnIndex("stp");
940 werner 326
    // unit properties
327
    int ispeciescomp = data_file.columnIndex("speciesComposition");
328
    int ithinning = data_file.columnIndex("thinningIntensity");
329
    int irotation = data_file.columnIndex("U");
977 werner 330
    int iMAI = data_file.columnIndex("MAI");
940 werner 331
    int iharvest_mode = data_file.columnIndex("harvestMode");
332
 
333
 
938 werner 334
    if (ikey<0 || iunit<0)
939 werner 335
        throw IException("setup ABE agentDataFile: one (or two) of the required columns 'id' or 'unit' not available.");
938 werner 336
    if (iagent<0 && iagent_type<0)
337
        throw IException("setup ABE agentDataFile: the columns 'agent' or 'agentType' are not available. You have to include at least one of the columns.");
863 werner 338
 
938 werner 339
 
863 werner 340
    QList<QString> unit_codes;
890 werner 341
    QHash<FMStand*, QString> initial_stps;
863 werner 342
    for (int i=0;i<data_file.rowCount();++i) {
343
        int stand_id = data_file.value(i,ikey).toInt();
344
        if (!stand_grid->isValid(stand_id))
345
            continue; // skip stands that are not in the map (e.g. when a smaller extent is simulated)
890 werner 346
        if (FMSTP::verbose())
909 werner 347
            qCDebug(abeSetup) << "setting up stand" << stand_id;
863 werner 348
 
349
        // check agents
938 werner 350
        QString agent_code = iagent>-1 ? data_file.value(i, iagent).toString() : QString();
351
        QString agent_type_code = iagent_type>-1 ? data_file.value(i, iagent_type).toString() : QString();
939 werner 352
        QString unit_id = data_file.value(i, iunit).toString();
353
 
938 werner 354
        Agent *ag=0;
873 werner 355
        AgentType *at=0;
938 werner 356
        if (agent_code.isEmpty() && agent_type_code.isEmpty())
357
            throw IException(QString("setup ABE agentDataFile row '%1': no code for columns 'agent' and 'agentType' available.").arg(i) );
358
 
359
        if (!agent_code.isEmpty()) {
360
            // search for a specific agent
361
            ag = agent(agent_code);
362
            if (!ag)
363
                throw IException(QString("Agent '%1' is not set up (row '%2')! Use the 'newAgent()' JS function of agent-types to add agent definitions.").arg(agent_code).arg(i));
364
            at = ag->type();
365
 
366
        } else {
367
            // look up the agent type and create the agent on the fly
863 werner 368
            // create the agent / agent type
938 werner 369
            at = agentType(agent_type_code);
876 werner 370
            if (!at)
942 werner 371
                throw IException(QString("Agent type '%1' is not set up (row '%2')! Use the 'addAgentType()' JS function to add agent-type definitions.").arg(agent_type_code).arg(i));
873 werner 372
 
977 werner 373
            if (!unit_codes.contains(unit_id)) {
374
                // we create an agent for the unit only once (per unit)
375
                ag = at->createAgent();
939 werner 376
            }
863 werner 377
        }
378
 
938 werner 379
 
863 werner 380
        // check units
381
        FMUnit *unit = 0;
382
        if (!unit_codes.contains(unit_id)) {
383
            // create the unit
938 werner 384
            unit = new FMUnit(ag);
863 werner 385
            unit->setId(unit_id);
940 werner 386
            if (iharvest_mode>-1)
387
                unit->setHarvestMode( data_file.value(i, iharvest_mode).toString());
388
            if (ithinning>-1)
389
                unit->setThinningIntensity( data_file.value(i, ithinning).toInt() );
390
            if (irotation>-1)
391
                unit->setU( data_file.value(i, irotation).toDouble() );
1070 werner 392
            if (iMAI>-1)
977 werner 393
                unit->setAverageMAI(data_file.value(i, iMAI).toDouble());
940 werner 394
            if (ispeciescomp>-1) {
395
                int index;
396
                index = at->speciesCompositionIndex( data_file.value(i, ispeciescomp).toString() );
397
                if (index==-1)
398
                    throw IException(QString("The species composition '%1' for unit '%2' is not a valid composition type (agent type: '%3').").arg(data_file.value(i, ispeciescomp).toString()).arg(unit->id()).arg(at->name()));
399
                unit->setTargetSpeciesCompositionIndex( index );
400
            }
863 werner 401
            mUnits.append(unit);
402
            unit_codes.append(unit_id);
939 werner 403
            ag->addUnit(unit); // add the unit to the list of managed units of the agent
863 werner 404
        } else {
405
            // get unit by id ... in this case we have the same order of appending values
406
            unit = mUnits[unit_codes.indexOf(unit_id)];
407
        }
408
 
409
        // create stand
410
        FMStand *stand = new FMStand(unit,stand_id);
890 werner 411
        if (istp>-1) {
412
            QString stp = data_file.value(i, istp).toString();
413
            initial_stps[stand] = stp;
414
        }
914 werner 415
        mMaxStandId = qMax(mMaxStandId, stand_id);
863 werner 416
 
417
        mUnitStandMap.insertMulti(unit,stand);
873 werner 418
        mStands.append(stand);
863 werner 419
 
420
    }
944 werner 421
 
422
    // count the number of stands within each unit
423
    foreach(FMUnit *unit, mUnits)
424
        unit->setNumberOfStands( mUnitStandMap.count(unit) );
425
 
873 werner 426
    // set up the stand grid (visualizations)...
427
    // set up a hash for helping to establish stand-id <-> fmstand-link
914 werner 428
    mStandHash.clear();
970 werner 429
    for (int i=0;i<mStands.size(); ++i) {
914 werner 430
        mStandHash[mStands[i]->id()] = mStands[i];
970 werner 431
    }
873 werner 432
 
896 werner 433
    mFMStandGrid.setup(standGrid()->grid().metricRect(), standGrid()->grid().cellsize());
882 werner 434
    mFMStandGrid.initialize(0);
435
    FMStand **fm = mFMStandGrid.begin();
873 werner 436
    for (int *p = standGrid()->grid().begin(); p!=standGrid()->grid().end(); ++p, ++fm)
914 werner 437
        *fm = *p<0?0:mStandHash[*p];
873 werner 438
 
882 werner 439
    mStandLayers.setGrid(mFMStandGrid);
878 werner 440
    mStandLayers.clearClasses();
873 werner 441
    mStandLayers.registerLayers();
442
 
890 werner 443
    // now initialize STPs (if they are defined in the init file)
444
    for (QHash<FMStand*,QString>::iterator it=initial_stps.begin(); it!=initial_stps.end(); ++it) {
445
        FMStand *s = it.key();
446
        FMSTP* stp = s->unit()->agent()->type()->stpByName(it.value());
447
        if (stp) {
934 werner 448
            s->setSTP(stp);
1058 werner 449
        } else {
450
            qCDebug(abeSetup) << "Warning during reading of CSV setup file: the STP '" << it.value() << "' is not valid for Agenttype: " << s->unit()->agent()->type()->name();
890 werner 451
        }
934 werner 452
    }
938 werner 453
    qCDebug(abeSetup) << "ABE setup completed.";
934 werner 454
}
455
 
456
void ForestManagementEngine::initialize()
457
{
458
 
459
    DebugTimer time_setup("ABE:setup");
460
 
461
    foreach (FMStand* stand, mStands) {
462
        if (stand->stp()) {
940 werner 463
 
464
            stand->setU( stand->unit()->U() );
465
            stand->setThinningIntensity( stand->unit()->thinningIntensity() );
466
            stand->setTargetSpeciesIndex( stand->unit()->targetSpeciesIndex() );
467
 
934 werner 468
            stand->initialize();
469
            if (isCancel()) {
470
                throw IException(QString("ABE-Error: init of stand %2: %1").arg(mLastErrorMessage).arg(stand->id()));
471
            }
901 werner 472
        }
890 werner 473
    }
474
 
873 werner 475
    // now initialize the agents....
939 werner 476
    foreach(Agent *ag, mAgents) {
477
        ag->setup();
901 werner 478
        if (isCancel()) {
939 werner 479
            throw IException(QString("ABE-Error: setup of agent '%2': %1").arg(mLastErrorMessage).arg(ag->name()));
901 werner 480
        }
481
    }
915 werner 482
 
483
    // run the initial planning unit setup
484
    GlobalSettings::instance()->model()->threadExec().run(nc_plan_update_unit, mUnits);
485
 
486
 
909 werner 487
    qCDebug(abeSetup) << "ABE setup complete." << mUnitStandMap.size() << "stands on" << mUnits.count() << "units, managed by" << mAgents.size() << "agents.";
863 werner 488
 
813 werner 489
}
490
 
811 werner 491
void ForestManagementEngine::clear()
492
{
873 werner 493
    qDeleteAll(mStands); // delete the stands
494
    mStands.clear();
811 werner 495
    qDeleteAll(mUnits); // deletes the units
496
    mUnits.clear();
873 werner 497
    mUnitStandMap.clear();
498
 
811 werner 499
    qDeleteAll(mAgents);
500
    mAgents.clear();
501
    qDeleteAll(mAgentTypes);
502
    mAgentTypes.clear();
870 werner 503
    qDeleteAll(mSTP);
504
    mSTP.clear();
878 werner 505
    mCurrentYear = 0;
901 werner 506
    mCancel = false;
507
    mLastErrorMessage = QString();
811 werner 508
}
509
 
901 werner 510
void ForestManagementEngine::abortExecution(const QString &message)
511
{
512
    mLastErrorMessage = message;
513
    mCancel = true;
514
}
878 werner 515
 
1089 werner 516
void ForestManagementEngine::runOnInit(bool before_init)
934 werner 517
{
1089 werner 518
    QString handler = before_init ? QStringLiteral("onInit") : QStringLiteral("onAfterInit");
519
    if (GlobalSettings::instance()->scriptEngine()->globalObject().hasProperty(handler)) {
520
        QJSValue result = GlobalSettings::instance()->scriptEngine()->evaluate(QString("%1()").arg(handler));
934 werner 521
        if (result.isError())
1089 werner 522
            qCDebug(abeSetup) << "Javascript Error in global"<< handler << "-Handler:" << result.toString();
901 werner 523
 
934 werner 524
    }
525
}
901 werner 526
 
527
 
934 werner 528
 
529
 
875 werner 530
/// this is the main function of the forest management engine.
531
/// the function is called every year.
876 werner 532
void ForestManagementEngine::run(int debug_year)
875 werner 533
{
876 werner 534
    if (debug_year>-1) {
535
        mCurrentYear++;
536
    } else {
537
        mCurrentYear = GlobalSettings::instance()->currentYear();
538
    }
539
    // now re-evaluate stands
909 werner 540
    if (FMSTP::verbose()) qCDebug(abe) << "ForestManagementEngine: run year" << mCurrentYear;
875 werner 541
 
958 werner 542
 
903 werner 543
    prepareRun();
544
 
958 werner 545
    // execute an event handler before invoking the ABE core
546
    runJavascript();
547
 
907 werner 548
    {
549
    // launch the planning unit level update (annual and thorough analysis every ten years)
909 werner 550
    DebugTimer plu("ABE:planUpdate");
977 werner 551
    GlobalSettings::instance()->model()->threadExec().run(nc_plan_update_unit, mUnits, true);
903 werner 552
    }
553
 
977 werner 554
    GlobalSettings::instance()->model()->threadExec().run(nc_execute_unit, mUnits, true); // force single thread operation for now
901 werner 555
    if (isCancel()) {
556
        throw IException(QString("ABE-Error: %1").arg(mLastErrorMessage));
557
    }
878 werner 558
 
916 werner 559
    // create outputs
950 werner 560
    {
561
    DebugTimer plu("ABE:outputs");
916 werner 562
    GlobalSettings::instance()->outputManager()->execute("abeUnit");
922 werner 563
    GlobalSettings::instance()->outputManager()->execute("abeStand");
1074 werner 564
    GlobalSettings::instance()->outputManager()->execute("abeStandDetail");
929 werner 565
    GlobalSettings::instance()->outputManager()->execute("abeStandRemoval");
950 werner 566
    }
916 werner 567
 
904 werner 568
    finalizeRun();
569
 
875 werner 570
}
571
 
811 werner 572
 
813 werner 573
 
811 werner 574
 
867 werner 575
void ForestManagementEngine::test()
576
{
577
    // test code
578
    try {
579
        //Activity::setVerbose(true);
580
        // setup the activities and the javascript environment...
581
        GlobalSettings::instance()->resetScriptEngine(); // clear the script
582
        ScriptGlobal::setupGlobalScripting(); // general iLand scripting helper functions and such
869 werner 583
        if (mScriptBridge)
584
            delete mScriptBridge;
585
        mScriptBridge = new FomeScript;
586
        mScriptBridge->setupScriptEnvironment();
867 werner 587
 
588
        //setup();
589
 
590
    } catch (const IException &e) {
591
        qDebug() << "An error occured:" << e.message();
592
    }
872 werner 593
    QString file_name = "E:/Daten/iLand/modeling/abm/knowledge_base/test/test_stp.js";
594
    QString code = Helper::loadTextFile(file_name);
595
    QJSValue result = GlobalSettings::instance()->scriptEngine()->evaluate(code,file_name);
867 werner 596
    if (result.isError()) {
872 werner 597
        int lineno = result.property("lineNumber").toInt();
598
        QStringList code_lines = code.replace('\r', "").split('\n'); // remove CR, split by LF
599
        QString code_part;
600
        for (int i=std::max(0, lineno - 5); i<std::min(lineno+5, code_lines.count()); ++i)
601
            code_part.append(QString("%1: %2 %3\n").arg(i).arg(code_lines[i]).arg(i==lineno?"  <---- [ERROR]":""));
602
        qDebug() << "Javascript Error in file" << result.property("fileName").toString() << ":" << result.property("lineNumber").toInt() << ":" << result.toString() << ":\n" << code_part;
867 werner 603
    }
604
 
605
 
870 werner 606
//    try {
607
//        qDebug() << "*** test 1 ***";
608
//        FMSTP stp;
609
//        stp.setVerbose(true);
610
//        stp.setup(GlobalSettings::instance()->scriptEngine()->globalObject().property("stp"), "stp");
611
//        stp.dumpInfo();
867 werner 612
 
870 werner 613
//    } catch (const IException &e) {
614
//        qDebug() << "An error occured:" << e.message();
615
//    }
616
//    try {
617
//        qDebug() << "*** test 2 ***";
618
//        FMSTP stp2;
619
//        stp2.setVerbose(true);
620
//        stp2.setup(GlobalSettings::instance()->scriptEngine()->globalObject().property("degenerated"), "degenerated");
621
//        stp2.dumpInfo();
622
//    } catch (const IException &e) {
623
//        qDebug() << "An error occured:" << e.message();
624
//    }
867 werner 625
 
870 werner 626
    // dump all objects:
627
    foreach(FMSTP *stp, mSTP)
628
        stp->dumpInfo();
867 werner 629
 
873 werner 630
    setup();
867 werner 631
    qDebug() << "finished";
870 werner 632
 
867 werner 633
}
634
 
896 werner 635
QStringList ForestManagementEngine::evaluateClick(const QPointF coord, const QString &grid_name)
636
{
901 werner 637
    Q_UNUSED(grid_name); // for the moment
896 werner 638
    // find the stand at coord.
639
    FMStand *stand = mFMStandGrid.constValueAt(coord);
640
    if (stand)
641
        return stand->info();
642
    return QStringList();
643
}
644
 
808 werner 645
QJSEngine *ForestManagementEngine::scriptEngine()
646
{
807 werner 647
    // use global engine from iLand
648
    return GlobalSettings::instance()->scriptEngine();
649
}
870 werner 650
 
873 werner 651
FMSTP *ForestManagementEngine::stp(QString stp_name) const
652
{
653
    for (QVector<FMSTP*>::const_iterator it = mSTP.constBegin(); it!=mSTP.constEnd(); ++it)
654
        if ( (*it)->name() == stp_name )
655
            return *it;
656
    return 0;
657
}
870 werner 658
 
884 werner 659
FMStand *ForestManagementEngine::stand(int stand_id) const
660
{
944 werner 661
    if (mStandHash.contains(stand_id))
662
        return mStandHash[stand_id];
663
 
664
    // exhaustive search... should not happen
665
    qCDebug(abe) << "ForestManagementEngine::stand() fallback to exhaustive search.";
884 werner 666
    for (QVector<FMStand*>::const_iterator it=mStands.constBegin(); it!=mStands.constEnd(); ++it)
667
        if ( (*it)->id() == stand_id)
668
            return *it;
669
    return 0;
670
}
873 werner 671
 
1059 werner 672
QStringList ForestManagementEngine::standIds() const
673
{
674
    QStringList standids;
675
    foreach(FMStand *s, mStands)
676
        standids.push_back(QString::number(s->id()));
677
    return standids;
678
}
679
 
1064 werner 680
void ForestManagementEngine::notifyTreeRemoval(Tree *tree, int reason)
904 werner 681
{
682
    // we use an 'int' instead of Tree:TreeRemovalType because it does not work
911 werner 683
    // with forward declaration (and I dont want to include the tree.h header in this class header).
1070 werner 684
    FMStand *stand = mFMStandGrid[tree->position()];
929 werner 685
    if (stand)
1064 werner 686
        stand->notifyTreeRemoval(tree, reason);
1157 werner 687
    else
688
        qDebug() << "ForestManagementEngine::notifyTreeRemoval(): tree not on stand at (metric coords): " << tree->position() << "ID:" << tree->id();
904 werner 689
}
878 werner 690
 
1157 werner 691
bool ForestManagementEngine::notifyBarkbeetleAttack(const ResourceUnit *ru, const double generations, int n_infested_px)
1070 werner 692
{
1088 werner 693
    // find out which stands are within the resource unit
1070 werner 694
    GridRunner<FMStand*> gr(mFMStandGrid, ru->boundingBox());
695
    QHash<FMStand*, bool> processed_items;
696
    bool forest_changed = false;
697
    while (FMStand **s=gr.next()) {
698
        if (*s && !processed_items.contains(*s)) {
699
            processed_items[*s] = true;
1157 werner 700
            forest_changed |=  (*s)->notifyBarkBeetleAttack(generations, n_infested_px);
1070 werner 701
        }
702
    }
703
    return forest_changed;
704
}
705
 
914 werner 706
QMutex protect_split;
707
FMStand *ForestManagementEngine::splitExistingStand(FMStand *stand)
708
{
709
    // get a new stand-id
710
    // make sure that the Id is only used once.
711
    QMutexLocker protector(&protect_split);
712
    int new_stand_id = ++mMaxStandId;
884 werner 713
 
914 werner 714
    FMUnit *unit = const_cast<FMUnit*> (stand->unit());
715
    FMStand *new_stand = new FMStand(unit,new_stand_id);
904 werner 716
 
914 werner 717
    mUnitStandMap.insertMulti(unit,new_stand);
718
    mStands.append(new_stand);
719
    mStandHash[new_stand_id] = new_stand;
720
 
944 werner 721
    unit->setNumberOfStands( mUnitStandMap.count(unit) );
722
 
914 werner 723
    mStandLayoutChanged = true;
724
 
725
    return new_stand;
726
}
727
 
728
 
729
 
870 werner 730
} // namespace