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 | ********************************************************************************************/ |
||
20 | |||
964 | werner | 21 | #include "bbgenerations.h" |
22 | |||
1007 | werner | 23 | #include "resourceunit.h" |
24 | #include "climate.h" |
||
1095 | werner | 25 | /** @class BBGenerations |
26 | @ingroup beetlemodule |
||
27 | BBGenerations calculates potential bark beetle generations based on climate data (including bark temperature). |
||
28 | */ |
||
964 | werner | 29 | BBGenerations::BBGenerations() |
30 | { |
||
31 | } |
||
1007 | werner | 32 | |
1008 | werner | 33 | /** |
34 | * @brief BBGenerations::calculateGenerations |
||
35 | * @param ru |
||
1085 | werner | 36 | * @return the number of filial generation (i.e, main generations) + 0.5 if a sister brood develops also for the last generation |
1008 | werner | 37 | */ |
38 | double BBGenerations::calculateGenerations(const ResourceUnit *ru) |
||
39 | { |
||
40 | calculateBarkTemperature(ru); |
||
1007 | werner | 41 | |
1008 | werner | 42 | // start at the 1. of April, and wait for 140.3 degree days (with a threhsold of 8.3 degrees) |
43 | const ClimateDay *clim = ru->climate()->day(4-1,1-1); // 0-based indices |
||
44 | const ClimateDay *last_day = ru->climate()->day(10-1,31-1); // 0-based indices -> Oct 31 |
||
45 | const ClimateDay * day_too_short = ru->climate()->dayOfYear(ru->climate()->sun().dayShorter14_5hrs()); // the first doy where the day is shorter than 14.5 hours |
||
46 | |||
47 | double dd=0.; |
||
48 | while (dd<140.3 && clim<last_day) { |
||
49 | dd+=std::max(clim->max_temperature-8.3, 0.); |
||
50 | ++clim; |
||
51 | } |
||
52 | // now wait for a decent warm day with tmax > 16.5 degrees |
||
53 | while (clim->max_temperature<=16.5 && clim<last_day) |
||
54 | ++clim; |
||
55 | |||
56 | mGenerations.clear(); |
||
57 | // start with the first generation.... |
||
58 | BBGeneration bbgen; |
||
59 | bbgen.start_day = ru->climate()->whichDayOfYear(clim); |
||
1010 | werner | 60 | bbgen.is_sister_brood = false; |
1008 | werner | 61 | bbgen.gen = 1; // the first generation |
62 | mGenerations.append(bbgen); |
||
63 | |||
64 | // process the generations |
||
65 | int i=0; |
||
66 | while (i<mGenerations.count()) { |
||
67 | BBGeneration bb = mGenerations[i]; // deliberately make a copy and not a ref |
||
68 | const ClimateDay *c = ru->climate()->dayOfYear(bb.start_day); |
||
69 | int doy = bb.start_day; |
||
70 | double base_temp = mEffectiveBarkTemp[doy]; |
||
71 | |||
1032 | werner | 72 | double t_sum=0.; |
1010 | werner | 73 | bool added_sister_brood = false; |
1008 | werner | 74 | while (c < last_day) { |
75 | t_sum = (mEffectiveBarkTemp[doy]-base_temp) / 557.; |
||
76 | if (t_sum>=1.) { |
||
77 | if (c<day_too_short) { |
||
1082 | werner | 78 | // start a new parental generation (a full cycle), only if the current |
79 | // generation is also a filial generation. |
||
80 | if (bb.is_sister_brood) |
||
81 | mGenerations.append(BBGeneration(doy, true, bb.gen)); // sisterbrood: (true), keep counter of filial generation |
||
82 | else |
||
83 | mGenerations.append(BBGeneration(doy, false, bb.gen+1)); // new filial brood (false), start a new gen |
||
84 | |||
1008 | werner | 85 | } |
86 | break; |
||
1010 | werner | 87 | } else if (t_sum>0.5 && !added_sister_brood) { |
88 | // start a sister brood, *if* the maximum air temperature is high enough, and if the |
||
1008 | werner | 89 | // length of the day > 14.5 hours |
90 | if (c->max_temperature>16.5 && c<day_too_short) { |
||
1082 | werner | 91 | mGenerations.append(BBGeneration(doy, true, bb.gen)); // add a sister brood generation (true), keep gen. of originating brood |
1010 | werner | 92 | added_sister_brood = true; |
1008 | werner | 93 | } |
94 | } |
||
95 | ++c; ++doy; |
||
96 | } |
||
97 | mGenerations[i].value = std::min(t_sum, 1.); |
||
98 | ++i; // now process the next generation |
||
99 | } |
||
100 | |||
1082 | werner | 101 | |
102 | mNSisterBroods = 0; mNFilialBroods = 0; |
||
103 | |||
1008 | werner | 104 | for (i=0;i<mGenerations.count();++i) { |
1082 | werner | 105 | if (mGenerations[i].value>0.6 && mGenerations[i].is_sister_brood==true) |
106 | mNSisterBroods = std::max( mGenerations[i].gen, mNSisterBroods ); |
||
1008 | werner | 107 | |
1082 | werner | 108 | if (mGenerations[i].value>0.6 && mGenerations[i].is_sister_brood==false) |
109 | mNFilialBroods = mGenerations[i].gen; |
||
1008 | werner | 110 | } |
1010 | werner | 111 | |
1082 | werner | 112 | // return the number of filial broods, and increase by 0.5 if a sister brood of the last filial generation has successfully developed. |
113 | return mNFilialBroods + ( hasSisterBrood() ? 0.5 : 0.); |
||
114 | |||
115 | // // now accumulate the generations |
||
116 | // double filial_generations = 0.; |
||
117 | // double sister_generations = 0.; |
||
118 | // // accumulate possible number of offspring |
||
119 | // const double offspring_factor = 25.; // assuming sex-ratio of 1:1 and 50 offspring per female (see p. 59) |
||
120 | // int n=0; |
||
121 | // int total_offspring = 0; |
||
122 | // for (i=0;i<mGenerations.count();++i) { |
||
123 | // if (mGenerations[i].value>0.6) { |
||
124 | // ++n; |
||
125 | // total_offspring+= pow(offspring_factor, mGenerations[i].gen-1)*2.*offspring_factor; |
||
126 | // } |
||
127 | // if (mGenerations[i].value>0.6 && mGenerations[i].is_sister_brood==true) |
||
128 | // sister_generations+=mGenerations[i].value; |
||
129 | // if (mGenerations[i].value>0.6 && mGenerations[i].is_sister_brood==false) |
||
130 | // filial_generations+=mGenerations[i].value; |
||
131 | |||
132 | // } |
||
133 | // if (logLevelDebug()) |
||
134 | // qDebug() << "rid" <<ru->id() << "filial/sister:" << filial_generations << sister_generations << "offspring:" << total_offspring << "started generations:" << n; |
||
135 | // mNSisterBroods = sister_generations; |
||
136 | // mNFilialBroods = filial_generations; |
||
137 | |||
138 | // return filial_generations + (sister_generations>0?0.5:0); |
||
1008 | werner | 139 | } |
140 | |||
141 | |||
1007 | werner | 142 | /** |
143 | * Calculate the bark temperatures for this year and a given resource unit. |
||
1024 | werner | 144 | * Input: climate data (tmax (C), tmean (C), radiation (MJ/m2)) |
1007 | werner | 145 | * the LAI to estimate the radiation on the ground (Wh/m2) |
1008 | werner | 146 | * Output: calculates for each day of the year the "effective" bark-temperature and saves a cumulative sum |
1007 | werner | 147 | * Source: Schopf et al 2004: Risikoabschaetzung von Borkenkaefermassenkalamitaeten im Nationalpark Kalkalpen |
148 | */ |
||
149 | void BBGenerations::calculateBarkTemperature(const ResourceUnit *ru) |
||
150 | { |
||
151 | // estimate the fraction of light on the ground (multiplier from 0..1) |
||
152 | const double k = 0.5; // constant for the beer lambert function |
||
153 | double ground_light_fraction = exp(-k * ru->leafAreaIndex() ); |
||
154 | |||
1010 | werner | 155 | mFrostDaysEarly=0; |
156 | mFrostDaysLate=0; |
||
1007 | werner | 157 | |
158 | for (int i=0;i<ru->climate()->daysOfYear();++i) { |
||
159 | const ClimateDay *clim = ru->climate()->dayOfYear(i); |
||
160 | // radiation: MJ/m2/day -> the regression uses Wh/m2/day -> conversion-factor: 1/0.0036 |
||
1008 | werner | 161 | double rad_wh = clim->radiation*ground_light_fraction/0.0036; |
162 | // calc. maximum bark temperature |
||
163 | double bt_max=1.656 + 0.002955*rad_wh + 0.534*clim->max_temperature + 0.01884 * clim->max_temperature*clim->max_temperature; |
||
164 | double diff_bt=0.; |
||
1007 | werner | 165 | |
1008 | werner | 166 | |
167 | if (bt_max>=30.4) |
||
168 | diff_bt=std::max(-310.667 + 9.603 * bt_max, 0.); // degree * hours |
||
169 | |||
170 | // mean bark temperature |
||
171 | double bt_mean=-0.173+0.0008518*rad_wh+1.054*clim->mean_temp(); |
||
172 | |||
173 | // effective: |
||
174 | double bt_sum = std::max(bt_mean - 8.3, 0.)*24.; // degree * hours |
||
175 | |||
176 | // corrected for days with very high bark temperature (>30.4 degrees) |
||
177 | double bt_sum_eff = (bt_sum - diff_bt) / 24.; // degree * days |
||
178 | |||
179 | mEffectiveBarkTemp[i] = (i>0? mEffectiveBarkTemp[i-1] : 0.) + bt_sum_eff; |
||
180 | |||
1010 | werner | 181 | // frost days: |
182 | if (clim->min_temperature < -15.) { |
||
183 | if (i < ru->climate()->sun().longestDay()) |
||
184 | mFrostDaysEarly++; |
||
185 | else |
||
186 | mFrostDaysLate++; |
||
187 | } |
||
188 | |||
1007 | werner | 189 | } |
190 | |||
191 | } |