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 |