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
********************************************************************************************/
908 werner 19
#include "abe_global.h"
873 werner 20
#include "global.h"
21
 
811 werner 22
#include "fmunit.h"
889 werner 23
 
24
#include "forestmanagementengine.h"
25
#include "fmstand.h"
26
#include "scheduler.h"
27
#include "agent.h"
904 werner 28
#include "agenttype.h"
932 werner 29
#include "fomescript.h"
30
#include "fmtreelist.h"
904 werner 31
 
907 werner 32
namespace ABE {
811 werner 33
 
1095 werner 34
/** @class FMUnit
35
    @ingroup abe
36
    The FMUnit class encapsulates a forest management unit, comprised of forest stands. Units are the base level at which
37
    the scheduling works.
38
 
39
  */
40
 
41
 
889 werner 42
void FMUnit::aggregate()
811 werner 43
{
44
    // loop over all stands
889 werner 45
    // collect some data....
46
    double age=0.;
47
    double volume = 0.;
48
    double harvest = 0.;
49
    double totalarea = 0.;
50
    const QMultiMap<FMUnit*, FMStand*> &stands = ForestManagementEngine::instance()->stands();
51
    QMultiMap<FMUnit*, FMStand*>::const_iterator it = stands.constFind(this);
52
    while (it != stands.constEnd() && it.key()==this) {
53
        const FMStand *s = it.value();
54
        age += s->age() * s->area();
55
        volume += s->volume() * s->area();
56
 
57
        totalarea += s->area();
58
        ++it;
59
    }
60
    if (totalarea>0.) {
61
        age /= totalarea;
62
        volume /= totalarea;
63
        harvest /= totalarea;
64
    }
909 werner 65
    qCDebug(abe) << "unit" << id() << "volume (m3/ha)" << volume << "age" << age << "planned harvest: todo";
889 werner 66
 
811 werner 67
}
870 werner 68
 
896 werner 69
QStringList FMUnit::info() const
70
{
915 werner 71
    return QStringList() << QString("(accumulated) harvest: %1").arg(mRealizedHarvest)
72
                         << QString("MAI: %1").arg(mMAI)
73
                         << QString("HDZ: %1").arg(mHDZ)
74
                         << QString("average age: %1").arg(mMeanAge)
75
                         << QString("decadal plan: %1").arg(mAnnualHarvestTarget)
76
                         << QString("current plan: %1").arg(constScheduler()!=0?constScheduler()->harvestTarget():0.);
77
 
896 werner 78
}
79
 
921 werner 80
double FMUnit::annualThinningHarvest() const
81
{
82
    const QMultiMap<FMUnit*, FMStand*> &stands = ForestManagementEngine::instance()->stands();
83
    QMultiMap<FMUnit*, FMStand*>::const_iterator it = stands.constFind(const_cast<FMUnit*>(this));
84
    double harvested=0.;
85
    while (it != stands.constEnd() && it.key()==this) {
86
        FMStand *stand = it.value();
87
        harvested += stand->totalThinningHarvest();
88
        ++it;
89
    }
90
    return harvested;
91
}
92
 
889 werner 93
FMUnit::FMUnit(const Agent *agent)
94
{
95
    mAgent = agent;
904 werner 96
    mScheduler = 0;
915 werner 97
    mAnnualHarvestTarget = -1.;
98
    mRealizedHarvest = 0.;
99
    mMAI = 0.; mHDZ = 0.; mMeanAge = 0.;
100
    mTotalArea = 0.; mTotalPlanDeviation = 0.;
916 werner 101
    mTotalVolume = 0.;
102
    mAnnualHarvest = 0.;
944 werner 103
    mNumberOfStands = 0;
940 werner 104
    mU = 100, mThinningIntensityClass = 2, mSpeciesCompositionIndex = 0;
977 werner 105
    mAverageMAI = 0.;
889 werner 106
 
978 werner 107
 
939 werner 108
    //if (agent->type()->schedulerOptions().useScheduler)
109
    // explicit scheduler only for stands/units that include more than one stand
110
    mScheduler = new Scheduler(this);
889 werner 111
}
112
 
113
FMUnit::~FMUnit()
114
{
115
    if (mScheduler)
116
        delete mScheduler;
117
}
118
 
873 werner 119
void FMUnit::setId(const QString &id)
120
{
121
    mId = id;
122
}
123
 
1157 werner 124
void FMUnit::resetHarvestCounter()
125
{
126
    if (scheduler())
127
        scheduler()->resetHarvestCounter();
128
}
129
 
904 werner 130
void FMUnit::managementPlanUpdate()
903 werner 131
{
921 werner 132
    const double period_length = 10.;
915 werner 133
    // calculate the planned harvest in the next planning period (i.e., 10yrs).
134
    // this is the sum of planned operations that are already in the scheduler.
921 werner 135
    double plan_thinning, plan_final;
136
    mScheduler->plannedHarvests(plan_final, plan_thinning);
915 werner 137
    // the actual harvests of the last planning period
1032 werner 138
    //double realized = mRealizedHarvest;
915 werner 139
 
140
    mRealizedHarvest = 0.; // reset
141
    mRealizedHarvestLastYear = 0.;
142
 
143
 
903 werner 144
    // preparations:
145
    // MAI-calculation for all stands:
904 werner 146
    double total_area = 0.;
147
    double age = 0.;
148
    double mai = 0.;
149
    double hdz = 0.;
150
    double volume = 0.;
903 werner 151
    const QMultiMap<FMUnit*, FMStand*> &stands = ForestManagementEngine::instance()->stands();
152
    QMultiMap<FMUnit*, FMStand*>::const_iterator it = stands.constFind(this);
153
    while (it != stands.constEnd() && it.key()==this) {
904 werner 154
        FMStand *stand = it.value();
155
        stand->reload();
156
        stand->calculateMAI();
157
        // calculate sustainable total harvest (following Breymann)
158
        double area = stand->area();
915 werner 159
        mai += stand->meanAnnualIncrementTotal() * area; // m3/yr
160
        age += stand->absoluteAge() * area;
904 werner 161
        volume += stand->volume() * area;
915 werner 162
        // HDZ: "haubarer" average increment: timber that is ready for final harvest
163
        if (stand->readyForFinalHarvest())
956 werner 164
            hdz += stand->volume() / stand->absoluteAge() * area; //(0.1* stand->U()) * area; // note: changed!!!! was: volume/age * area
904 werner 165
        total_area += area;
903 werner 166
        ++it;
167
    }
932 werner 168
    // reset
169
    ForestManagementEngine::instance()->scriptBridge()->treesObj()->setStand(0);
915 werner 170
    mTotalArea = total_area;
904 werner 171
    if (total_area==0.)
172
        return;
903 werner 173
 
904 werner 174
    mai /= total_area; // m3/ha*yr area weighted average of annual increment
175
    age /= total_area; // area weighted mean age
176
    hdz /= total_area; // =sum(Vol/age * share)
177
 
915 werner 178
    mMAI = mai;
1057 werner 179
    //mMAI = mMAI * 1.15; // 15% increase, hack WR
915 werner 180
    mHDZ = hdz;
181
    mMeanAge = age;
916 werner 182
    mTotalVolume = volume;
915 werner 183
 
956 werner 184
    double rotation_length = U();
904 werner 185
    double h_tot = mai * 2.*age / rotation_length;  //
186
    double h_reg = hdz * 2.*age / rotation_length;
956 werner 187
    h_reg = h_tot * 0.85; // hack!
188
    h_reg *= agent()->schedulerOptions().harvestIntensity;
189
    h_tot *= agent()->schedulerOptions().harvestIntensity;
953 werner 190
    double h_thi = qMax(h_tot - h_reg, 0.);
904 werner 191
 
909 werner 192
    qCDebug(abe) << "plan-update for unit" << id() << ": h-tot:" << h_tot << "h_reg:" << h_reg << "h_thi:" << h_thi << "of total volume:" << volume;
921 werner 193
    double sf = mAgent->useSustainableHarvest();
194
    // we do not calculate sustainable harvest levels.
195
    // do a pure bottom up calculation
196
    double bottom_up_harvest = (plan_final / period_length) / total_area; // m3/ha*yr
197
 
198
    // the sustainable harvest yield is the current yield and some carry over from the last period
199
    double sustainable_harvest = h_reg;
953 werner 200
//    if (mAnnualHarvestTarget>0.) {
201
//        double delta = realized/(total_area*period_length) - mAnnualHarvestTarget;
202
//        // if delta > 0: timber removal was too high -> plan less for the current period, and vice versa.
203
//        sustainable_harvest -= delta;
204
//    }
921 werner 205
    mAnnualHarvestTarget = sustainable_harvest * sf + bottom_up_harvest * (1.-sf);
206
    mAnnualHarvestTarget = qMax(mAnnualHarvestTarget, 0.);
207
 
208
    mAnnualThinningTarget = (plan_thinning / period_length) / total_area; // m3/ha*yr
209
 
210
 
916 werner 211
    if (scheduler())
921 werner 212
        scheduler()->setHarvestTarget(mAnnualHarvestTarget, mAnnualThinningTarget);
903 werner 213
}
214
 
977 werner 215
QMutex _protect_agent_exec;
216
void FMUnit::runAgent()
217
{
218
    QMutexLocker m(&_protect_agent_exec); // avoid parallel execution of agent-code....
219
 
220
    // we need to set an execution context
221
    FMStand *stand = *ForestManagementEngine::instance()->stands().find(this);
222
    if (!stand)
223
        throw IException("Invalid stand in FMUnit::runAgent");
224
 
225
    FomeScript::setExecutionContext(stand, true); // true: add also agent as 'agent'
226
 
227
    QJSValue val;
228
    QJSValue agent_type = agent()->type()->jsObject();
229
    if (agent_type.property("run").isCallable()) {
230
        val = agent_type.property("run").callWithInstance(agent_type);
231
        qCDebug(abe) << "running agent-function 'run' for unit" <<  id() << ":" << val.toString();
232
    } else {
233
       qCDebug(abe) << "function 'run' is not a valid function of agent-type" << agent()->type()->name();
234
    }
235
 
236
}
237
 
907 werner 238
void FMUnit::updatePlanOfCurrentYear()
904 werner 239
{
915 werner 240
    if (!scheduler())
241
        return;
242
 
243
    if (mTotalArea==0.)
244
        throw IException("FMUnit:updatePlan: unit area = 0???");
245
 
246
    // compare the harvests of the last year to the plan:
247
    double harvests = mRealizedHarvest - mRealizedHarvestLastYear;
248
    mRealizedHarvestLastYear = mRealizedHarvest;
916 werner 249
    mAnnualHarvest = harvests;
915 werner 250
 
251
    // difference in m3/ha
252
    double delta = harvests/mTotalArea - mAnnualHarvestTarget;
253
    mTotalPlanDeviation += delta;
254
 
255
    // apply decay function for deviation
939 werner 256
    mTotalPlanDeviation *= mAgent->schedulerOptions().deviationDecayRate;
915 werner 257
 
258
    // relative deviation: >0: too many harvests
916 werner 259
    double rel_deviation = mAnnualHarvestTarget? mTotalPlanDeviation / mAnnualHarvestTarget : 0;
915 werner 260
 
922 werner 261
    // the current deviation is reduced to 50% in rebounce_yrs years.
939 werner 262
    double rebounce_yrs = mAgent->schedulerOptions().scheduleRebounceDuration;
915 werner 263
 
264
    double new_harvest = mAnnualHarvestTarget * (1. - rel_deviation/rebounce_yrs);
265
 
266
    // limit to minimum/maximum parameter
939 werner 267
    new_harvest = qMax(new_harvest, mAgent->schedulerOptions().minScheduleHarvest);
268
    new_harvest = qMin(new_harvest, mAgent->schedulerOptions().maxScheduleHarvest);
921 werner 269
    scheduler()->setHarvestTarget(new_harvest, mAnnualThinningTarget);
915 werner 270
 
904 werner 271
}
272
 
870 werner 273
} // namesapce