Rev 639 | Rev 697 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | |||
671 | 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 | |||
211 | werner | 21 | #include "global.h" |
22 | #include "phenology.h" |
||
23 | |||
24 | #include "climate.h" |
||
214 | werner | 25 | #include "floatingaverage.h" |
26 | |||
27 | /// "ramp" function: value < min: 0, value>max: 1: in between linear interpolation |
||
28 | double ramp(const double &value, const double minValue, const double maxValue) |
||
29 | { |
||
30 | Q_ASSERT(minValue!=maxValue); |
||
31 | if (value<minValue) return 0.; |
||
32 | if (value>maxValue) return 1.; |
||
33 | return (value-minValue) / (maxValue - minValue); |
||
34 | } |
||
35 | |||
36 | /** calculates the phenology according to Jolly et al. 2005. |
||
226 | werner | 37 | The calculation is performed for a given "group" and a present "climate". |
214 | werner | 38 | */ |
39 | void Phenology::calculate() |
||
40 | { |
||
436 | werner | 41 | if (id()==0) { |
42 | // for needles: just calculate the chilling requirement for the establishment |
||
440 | werner | 43 | // i.e.: use the "bottom line" of 10.5 hrs daylength for the end of the vegetation season |
44 | calculateChillDays(mClimate->sun().dayShorter10_5hrs()); |
||
214 | werner | 45 | return; |
436 | werner | 46 | } |
214 | werner | 47 | const ClimateDay *end = mClimate->end(); |
48 | double vpd,temp,daylength; |
||
49 | double gsi; // combined factor of effect of vpd, temperature and day length |
||
50 | int iday = 0; |
||
51 | bool inside_period = !mClimate->sun().northernHemishere(); // on northern hemisphere 1.1. is in winter |
||
52 | int day_start=-1, day_stop=-1; |
||
53 | int day_wait_for=-1; |
||
54 | |||
55 | FloatingAverage floater(21); // a three-week floating average |
||
56 | for (const ClimateDay *day = mClimate->begin(); day!=end; ++day, ++iday) { |
||
57 | |||
58 | if (day_wait_for>=0 && iday<day_wait_for) |
||
59 | continue; |
||
60 | vpd = 1. - ramp(day->vpd, mMinVpd, mMaxVpd); // high value for low vpd |
||
441 | werner | 61 | temp = ramp(day->min_temperature, mMinTemp, mMaxTemp); |
214 | werner | 62 | daylength = ramp( mClimate->sun().daylength(iday), mMinDayLength, mMaxDayLength); |
63 | gsi = vpd * temp * daylength; |
||
64 | gsi = floater.add(gsi); |
||
65 | if (!inside_period && gsi>0.5) { |
||
66 | // switch from winter -> summer |
||
67 | inside_period = true; |
||
68 | day_start = iday; |
||
69 | if (day_stop!=-1) |
||
70 | break; |
||
71 | day_wait_for = mClimate->sun().longestDay(); |
||
72 | } else if (inside_period && gsi<0.5) { |
||
73 | // switch from summer to winter |
||
74 | day_stop = iday; |
||
75 | if (day_start!=-1) |
||
76 | break; // finished |
||
77 | day_wait_for = mClimate->sun().longestDay(); |
||
78 | inside_period = false; |
||
79 | } |
||
80 | } |
||
81 | day_start -= 10; // three-week-floating average: subtract 10 days |
||
82 | day_stop -= 10; |
||
83 | if (day_start < -1 || day_stop<-1) |
||
84 | throw IException(QString("Phenology::calculation(): was not able to determine the length of the vegetation period for group %1." ).arg(id())); |
||
611 | werner | 85 | if (logLevelDebug()) |
86 | qDebug() << "Jolly-phenology. start" << mClimate->dayOfYear(day_start)->toString() << "stop" << mClimate->dayOfYear(day_stop)->toString(); |
||
214 | werner | 87 | iday = 0; |
226 | werner | 88 | mDayStart = day_start; |
89 | mDayEnd = day_stop; |
||
214 | werner | 90 | int bDay, bMon, eDay, eMon; |
91 | // convert yeardays to dates |
||
92 | mClimate->toDate(day_start, &bDay, &bMon); |
||
93 | mClimate->toDate(day_stop, &eDay, &eMon); |
||
94 | for (int i=0;i<12;i++) { |
||
95 | if (i<bMon || i>eMon) { |
||
96 | mPhenoFraction[i] = 0; // out of season |
||
97 | } else if (i>bMon && i<eMon) { |
||
98 | mPhenoFraction[i] = 1.; // full inside of season |
||
99 | } else { |
||
100 | // fractions of month |
||
101 | mPhenoFraction[i] = 1.; |
||
102 | if (i==bMon) |
||
103 | mPhenoFraction[i] -= (bDay+1) / double(mClimate->days(bMon)); |
||
104 | if (i==eMon) |
||
105 | mPhenoFraction[i] -= (mClimate->days(eMon) - (bDay+1)) / double(mClimate->days(eMon)); |
||
106 | } |
||
107 | } |
||
434 | werner | 108 | |
109 | calculateChillDays(); |
||
214 | werner | 110 | } |
434 | werner | 111 | |
112 | |||
113 | // *********** Chill-day calculations ******** |
||
436 | werner | 114 | void Phenology::calculateChillDays(const int end_of_season) |
434 | werner | 115 | { |
116 | int iday = 0; |
||
117 | mChillDaysBefore = 0; |
||
118 | int days_after = 0; |
||
436 | werner | 119 | int last_day = end_of_season>0?end_of_season:mDayEnd; |
434 | werner | 120 | for (const ClimateDay *day = mClimate->begin(); day!=mClimate->end(); ++day, ++iday) { |
121 | if (day->temperature>=-5 && day->temperature<5) { |
||
122 | if (iday<mDayStart) |
||
123 | mChillDaysBefore++; |
||
436 | werner | 124 | if (iday>last_day) |
434 | werner | 125 | days_after++; |
126 | } |
||
127 | } |
||
128 | if (GlobalSettings::instance()->currentYear()==1) { |
||
129 | // for the first simulation year, use the value of this autumn for the last years autumn |
||
130 | mChillDaysAfterLastYear = days_after; |
||
131 | } else { |
||
132 | mChillDaysAfterLastYear = mChillDaysAfter; |
||
133 | } |
||
134 | mChillDaysAfter = days_after; |
||
135 | } |