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