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