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 |