Subversion Repositories public iLand

Rev

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

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