cropsowing.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  1. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. /// \file cropsowing.h
  3. /// \brief Seasonality and sowing date calculations
  4. /// \author Mats Lindeskog, Stefan Olin
  5. /// $Date: $
  6. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  7. #include "config.h"
  8. #include "landcover.h"
  9. #include "cropsowing.h"
  10. /// Use temperature-dependent sowing date for irrigated crops at site with PRECTEMP seasonality.
  11. const bool IRRIGATED_USE_TEMP_SDATE = true;
  12. /// Use lower temperature limit for sowing for all crops
  13. const bool LOW_SOWING_TEMPERATURE_LIMIT = false;
  14. /// Autumn sowing types for crops
  15. enum {NOFORCING, AUTUMNSOWING, SPRINGSOWING};
  16. /// Monitors whether temperature limits have been attained for this pft this day
  17. /** Called from crop_sowing_gridcell() each day
  18. */
  19. void check_crop_temp_limits(Climate& climate, Gridcellpft& gridcellpft) {
  20. Pft& pft = gridcellpft.pft;
  21. // check if spring conditions are present this day:
  22. if (climate.temp > pft.tempspring && climate.dtemp_31[29] <= pft.tempspring) { // NB. after updating dtemp_31 with today's value
  23. // TeWW,TeCo,TeSf,TeRa: 12,14,13,12 (NB 5,14,15,5 in Bondeau 2007);
  24. if (climate.lat >= 0.0 && date.day > 300) {
  25. gridcellpft.last_springdate = date.day - date.year_length();
  26. } else {
  27. gridcellpft.last_springdate = date.day;
  28. }
  29. gridcellpft.springoccurred = true;
  30. }
  31. // check if autumn conditions are present this day:
  32. if (!pft.ifsdautumn) {
  33. return;
  34. }
  35. if (climate.temp<pft.tempautumn && climate.dtemp_31[29]>=pft.tempautumn && !gridcellpft.autumnoccurred) { //TeWW,TeRa: 12,17
  36. if (climate.lat >= 0.0 && date.day<100) {
  37. gridcellpft.first_autumndate = date.day + date.year_length();
  38. } else {
  39. gridcellpft.first_autumndate = date.day;
  40. }
  41. gridcellpft.autumnoccurred = true;
  42. }
  43. // check if vernilisation conditions are present this day:
  44. if (climate.temp < pft.trg && climate.dtemp_31[29] >= pft.trg && !gridcellpft.vernstartoccurred) { //TeWW,TeRa: 12,12
  45. gridcellpft.vernstartoccurred = true;
  46. }
  47. if (climate.temp>pft.trg && climate.dtemp_31[29] <= pft.trg) { //TeWW,TeRa: 12,12
  48. if (climate.lat >= 0.0 && date.day > 300) {
  49. gridcellpft.last_verndate = date.day - date.year_length();
  50. } else {
  51. gridcellpft.last_verndate = date.day;
  52. }
  53. gridcellpft.vernendoccurred = true;
  54. }
  55. }
  56. /// Updates 20-year mean of dates when temperature limits obtained, used for sowing date calculation
  57. /** Called from crop_sowing_gridcell() once a year
  58. */
  59. void calc_crop_dates_20y_mean(Climate& climate, Gridcellpft& gridcellpft, int thisyear) { // ecev3 - replaced date.year below with thisyear
  60. Pft& pft = gridcellpft.pft;
  61. /////////////////////////////////////////////////////////////////////////////////
  62. // Check if spring and frost conditions occurred during the past year. //
  63. // If not, set this year's date to either sdate_default or climate.coldestday: //
  64. /////////////////////////////////////////////////////////////////////////////////
  65. // if no spring occured during last year
  66. if (!gridcellpft.springoccurred) {
  67. if (climate.temp <= pft.tempspring)
  68. gridcellpft.last_springdate = date.day;
  69. else
  70. gridcellpft.last_springdate = climate.coldestday;
  71. }
  72. // if no autumn occured during last year
  73. if (pft.ifsdautumn && !gridcellpft.autumnoccurred) { //TeWW,TeRa
  74. if (climate.maxtemp < pft.tempautumn || climate.maxtemp >= pft.tempautumn && climate.mtemp_min < pft.tempautumn) {
  75. gridcellpft.first_autumndate = date.day;
  76. }
  77. else { //too warm
  78. gridcellpft.first_autumndate = climate.coldestday;
  79. if (climate.lat >= 0.0 && gridcellpft.first_autumndate < 180)
  80. gridcellpft.first_autumndate += date.year_length();
  81. }
  82. }
  83. // if temperature did not pass above the vernalisation temperature last year
  84. // to avoid last_verndate20 to precede last_springdate20 (Bondeau used coldest day)
  85. // 60 days is the maximum number of vernalization days
  86. if (pft.ifsdautumn && !gridcellpft.vernendoccurred) { //TeWW,TeRa
  87. gridcellpft.last_verndate = gridcellpft.last_springdate + def_verndate_ndays_after_last_springdate;
  88. }
  89. ///////////////////////////////////////////////////////////////////////////////////////
  90. // Update spring and frost date 20-year arrays and calculate 20 years average means: //
  91. ///////////////////////////////////////////////////////////////////////////////////////
  92. // 1) this year
  93. gridcellpft.last_springdate20=gridcellpft.last_springdate;
  94. if (pft.ifsdautumn) { // TeWW,TeRa
  95. gridcellpft.first_autumndate20 = gridcellpft.first_autumndate;
  96. // ecev3, was: if (date.year == 1 && climate.lat >= 0.0) // No autumn first half of first year, set value to same as for second year
  97. if (thisyear == 1 && climate.lat >= 0.0) // No autumn first half of first year, set value to same as for second year
  98. gridcellpft.first_autumndate_20[19] = gridcellpft.first_autumndate;
  99. gridcellpft.last_verndate20 = gridcellpft.last_verndate;
  100. }
  101. // 2) starting year (1st of 20 or less)
  102. // ecev3, was: int startyear = 20 - (int)min(19, date.year);
  103. int startyear = 20 - (int)min(19, thisyear);
  104. // 3) past 20 years or less
  105. for (int y=startyear; y<20; y++) {
  106. gridcellpft.last_springdate_20[y-1] = gridcellpft.last_springdate_20[y];
  107. gridcellpft.last_springdate20 += gridcellpft.last_springdate_20[y];
  108. if (pft.ifsdautumn) { // TeWW,TeRa
  109. gridcellpft.first_autumndate_20[y-1] = gridcellpft.first_autumndate_20[y];
  110. gridcellpft.first_autumndate20 += gridcellpft.first_autumndate_20[y];
  111. gridcellpft.last_verndate_20[y-1] = gridcellpft.last_verndate_20[y];
  112. gridcellpft.last_verndate20 += gridcellpft.last_verndate_20[y];
  113. }
  114. }
  115. // 4) 20 years average means:
  116. // ecev3, was: gridcellpft.last_springdate20 /= (int)min(20,date.year + 1);
  117. gridcellpft.last_springdate20 /= (int)min(20,thisyear + 1);
  118. if (gridcellpft.last_springdate20 < 0) {
  119. gridcellpft.last_springdate20 += date.year_length();
  120. }
  121. gridcellpft.last_springdate_20[19] = gridcellpft.last_springdate;
  122. if (pft.ifsdautumn) { // TeWW,TeRa
  123. // ecev3, was: gridcellpft.first_autumndate20 /= (int)min(20,date.year + 1);
  124. gridcellpft.first_autumndate20 /= (int)min(20,thisyear + 1);
  125. if (gridcellpft.first_autumndate20 > date.year_length() - 1) {
  126. gridcellpft.first_autumndate20 -= date.year_length();
  127. }
  128. gridcellpft.first_autumndate_20[19] = gridcellpft.first_autumndate;
  129. // ecev3, was: gridcellpft.last_verndate20 /= (int)min(20,date.year + 1);
  130. gridcellpft.last_verndate20 /= (int)min(20,thisyear + 1);
  131. if (gridcellpft.last_verndate20 < 0) {
  132. gridcellpft.last_verndate20 += date.year_length();
  133. }
  134. gridcellpft.last_verndate_20[19] = gridcellpft.last_verndate;
  135. }
  136. }
  137. /// Calculates sdatecalc_temp
  138. /** Called from crop_sowing_gridcell() once a year (June 30(180) in the north, December 31(364) in the south)
  139. */
  140. void set_sdatecalc_temp(Climate& climate, Gridcellpft& gridcellpft) {
  141. Pft& pft = gridcellpft.pft;
  142. if (pft.ifsdautumn && pft.forceautumnsowing != SPRINGSOWING) { // TeWW,TeRa:
  143. // Use autumn sowing if first_autumndate20 is set (autumn conditions met during the past 20 years):
  144. if (!((gridcellpft.first_autumndate20 == climate.testday_temp || gridcellpft.first_autumndate20 == climate.coldestday) &&
  145. gridcellpft.first_autumndate % date.year_length() == gridcellpft.first_autumndate20)) {
  146. gridcellpft.sdatecalc_temp = gridcellpft.first_autumndate20;
  147. gridcellpft.wintertype = true;
  148. }
  149. // if not, use spring sowing
  150. else { // if (gridcellpft.first_autumndate20==climate.coldestday)
  151. if (!((gridcellpft.last_springdate20 == climate.testday_temp || gridcellpft.last_springdate20 == climate.coldestday) &&
  152. gridcellpft.last_springdate == gridcellpft.last_springdate20)
  153. && pft.forceautumnsowing != AUTUMNSOWING) {
  154. gridcellpft.sdatecalc_temp = gridcellpft.last_springdate20;
  155. gridcellpft.wintertype = false;
  156. }
  157. else { // If neither spring nor autumn occurred during the past 20 years.
  158. if (climate.maxtemp < pft.tempspring) { // Too cold to sow at all.
  159. gridcellpft.sdatecalc_temp = -1;
  160. }
  161. else { // Too warm; avoid warmest period.
  162. gridcellpft.sdatecalc_temp = climate.coldestday;
  163. gridcellpft.wintertype = true;
  164. }
  165. }
  166. }
  167. // If autumn first_autumndate20 is earlier than hlimitdate, use last_springdate20 (winter is too long):
  168. if (dayinperiod(gridcellpft.sdatecalc_temp, climate.testday_temp, gridcellpft.hlimitdate_default)
  169. && pft.forceautumnsowing != AUTUMNSOWING) {
  170. gridcellpft.sdatecalc_temp = gridcellpft.last_springdate20; // use last_springdate20 disregarding earlier choices
  171. gridcellpft.wintertype = false;
  172. }
  173. // Forced sowing date read from input file.
  174. // Calculated value used if value for pft not found in file.
  175. if (gridcellpft.sdate_force >= 0) {
  176. if ((abs(gridcellpft.sdate_force - gridcellpft.first_autumndate20) <= abs(gridcellpft.sdate_force - gridcellpft.last_springdate20)))
  177. gridcellpft.wintertype = true;
  178. else
  179. gridcellpft.wintertype = false;
  180. gridcellpft.sdatecalc_temp = gridcellpft.sdate_force;
  181. }
  182. } else {
  183. if (pft.sd_adjust) { // will force sdate towards a certain day (71 for TeCo)
  184. gridcellpft.sdatecalc_temp = (int)(pft.sd_adjust_par1 / pft.sd_adjust_par2 * (gridcellpft.last_springdate20 - climate.adjustlat)
  185. + pft.sd_adjust_par3 + climate.adjustlat);
  186. }
  187. else {
  188. gridcellpft.sdatecalc_temp = gridcellpft.last_springdate20;
  189. }
  190. //Same lower temperature limit for sowing as for winter crops above.
  191. if (LOW_SOWING_TEMPERATURE_LIMIT &&
  192. gridcellpft.last_springdate20 == climate.testday_temp &&
  193. gridcellpft.last_springdate == gridcellpft.last_springdate20 &&
  194. climate.maxtemp < pft.tempspring) {
  195. // If spring has not occurred during the past 20 years or too cold to sow.
  196. gridcellpft.sdatecalc_temp = -1;
  197. }
  198. }
  199. // Climatic limits for crop sowing:
  200. if (climate.mtemp_min20 > pft.maxtemp_sowing) {
  201. gridcellpft.sdatecalc_temp = -1;
  202. }
  203. gridcellpft.springoccurred = false;
  204. gridcellpft.vernstartoccurred = false;
  205. gridcellpft.vernendoccurred = false;
  206. gridcellpft.autumnoccurred = false;
  207. }
  208. /// Calculates the Julian start and end day of a month.
  209. /** January 1st is set to 0.
  210. */
  211. void monthdates(int& start, int& end, int month) {
  212. int months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  213. start = 0;
  214. int m = 0;
  215. while (m < month){
  216. start += months[m];
  217. m++;
  218. }
  219. end = start + months[m] - 1;
  220. }
  221. /// Calculates sowing window for each crop pft
  222. /** Called from crop_sowing_gridcell() once a year
  223. */
  224. void calc_sowing_windows(Gridcell& gridcell) {
  225. Climate& climate = gridcell.climate;
  226. seasonality_type seasonality = climate.seasonality;
  227. int sow_month = 0;
  228. // Find the wettest month
  229. if (seasonality == SEASONALITY_PREC || seasonality == SEASONALITY_PRECTEMP) {
  230. double max = 0.0;
  231. for (int m=0; m<12; m++) {
  232. double sum = 0.0;
  233. for (int i=0; i<4; i++) {
  234. int mm = m + i;
  235. if (mm >= 12)
  236. mm -= 12;
  237. // Implement a check later to see if it makes any difference to to use the precipitation only
  238. if (true) {
  239. if (climate.mpet20[mm] > 0.0)
  240. sum += climate.mprec20[mm] / climate.mpet20[mm];
  241. }
  242. else
  243. sum += climate.mprec20[mm];
  244. }
  245. if (sum > max) {
  246. max = sum;
  247. // Months are stored as, 0-11
  248. sow_month=m;
  249. }
  250. }
  251. }
  252. // Set gridcell-level sowing windows for crop pft:s
  253. pftlist.firstobj();
  254. while(pftlist.isobj) {
  255. Pft& pft = pftlist.getobj();
  256. Gridcellpft& gridcellpft = gridcell.pft[pft.id];
  257. if (pft.phenology == CROPGREEN) {
  258. int swindow_temp[2];
  259. // ecev3 - extra initialisation
  260. swindow_temp[0] = 0;
  261. swindow_temp[1] = 0;
  262. int swindow_prec[2];
  263. // Calculate temperature-dependent sowing windows
  264. if (gridcellpft.sdatecalc_temp != -1) {
  265. // Set sowing window around sdatecalc_temp
  266. swindow_temp[0] = stepfromdate(gridcellpft.sdatecalc_temp, -15);
  267. swindow_temp[1] = stepfromdate(gridcellpft.sdatecalc_temp, 15);
  268. if (!gridcellpft.wintertype && dayinperiod(swindow_temp[0], stepfromdate(climate.coldestday, -100), climate.coldestday)) {
  269. swindow_temp[0] = climate.coldestday;
  270. if (dayinperiod(swindow_temp[1], stepfromdate(climate.coldestday, -100), climate.coldestday)) {
  271. swindow_temp[1] = climate.coldestday;
  272. }
  273. }
  274. if (gridcellpft.wintertype && dayinperiod(swindow_temp[1], climate.coldestday, stepfromdate(climate.coldestday, 100))) {
  275. swindow_temp[1] = climate.coldestday;
  276. if (dayinperiod(swindow_temp[0], climate.coldestday, stepfromdate(climate.coldestday, 100))) {
  277. swindow_temp[0] = climate.coldestday;
  278. }
  279. }
  280. }
  281. // Calculate precipitation-dependent sowing windows
  282. monthdates(swindow_prec[0],swindow_prec[1],sow_month);
  283. // A conservative choice to expand the sowing window
  284. swindow_prec[0] = stepfromdate(swindow_prec[0], -15);
  285. // Determine, based upon site climate seasonality, if sowing in rainfed stands should be triggered by
  286. // temperature or precipitation, or whether to use a default sowing date.
  287. bool temp_sdate = false, prec_sdate = false, def_sdate = false;
  288. if (seasonality == SEASONALITY_TEMP || seasonality == SEASONALITY_TEMPPREC)
  289. temp_sdate = true;
  290. else if ((seasonality == SEASONALITY_PREC || seasonality == SEASONALITY_PRECTEMP) && climate.prec_range != WET)
  291. prec_sdate = true;
  292. else // if (seasonality == SEASONALITY_NO) || (seasonality == SEASONALITY_PREC || seasonality == SEASONALITY_PRECTEMP) && climate.prec_range == WET)
  293. def_sdate = true;
  294. if (temp_sdate && gridcellpft.sdatecalc_temp != -1) { // ecev3 - added gridcellpft.sdatecalc_temp != -1
  295. gridcellpft.swindow[0] = swindow_temp[0];
  296. gridcellpft.swindow[1] = swindow_temp[1];
  297. }
  298. else if (prec_sdate) {
  299. gridcellpft.swindow[0] = swindow_prec[0];
  300. gridcellpft.swindow[1] = swindow_prec[1];
  301. }
  302. else if (def_sdate) {
  303. gridcellpft.swindow[0] = gridcellpft.sdate_default;
  304. gridcellpft.swindow[1] = stepfromdate(gridcellpft.sdate_default, 15);
  305. }
  306. // Rules for irrigated crops:
  307. // Different sowing date options for irrigated crops at sites with climate.seasonality == SEASONALITY_PRECTEMP:
  308. // 1. use temperature-dependent sowing limits (IRRIGATED_USE_TEMP_SDATE == true)
  309. // 2. use precipitation-triggered sowing (IRRIGATED_USE_TEMP_SDATE == false)
  310. if (IRRIGATED_USE_TEMP_SDATE && seasonality == SEASONALITY_PRECTEMP && gridcellpft.sdatecalc_temp != -1) { // ecev3 - added gridcellpft.sdatecalc_temp != -1
  311. gridcellpft.swindow_irr[0] = swindow_temp[0];
  312. gridcellpft.swindow_irr[1] = swindow_temp[1];
  313. }
  314. else {
  315. gridcellpft.swindow_irr[0] = gridcellpft.swindow[0];
  316. gridcellpft.swindow_irr[1] = gridcellpft.swindow[1];
  317. }
  318. // Includes all temperature limits for sowing set in set_sdatecalc_temp() also for sites with any type of temperature seasonality
  319. if (gridcellpft.sdatecalc_temp == -1) {
  320. gridcellpft.swindow[0] = -1;
  321. gridcellpft.swindow[1] = -1;
  322. }
  323. }
  324. pftlist.nextobj();
  325. }
  326. }
  327. /// Updates various climate 20-year means, used for sowing date calculation
  328. /** Called from crop_sowing_gridcell() once a year
  329. */
  330. void calc_m_climate_20y_mean(Climate& climate, int thisyear) { // ecev3 - replaced date.year below with thisyear
  331. int startyear = 20 - (int)min(19, thisyear);
  332. double mprec_petmin_thisyear = 1.0;
  333. double mprec_petmax_thisyear = 0.0;
  334. climate.aprec = 0.0;
  335. for(int m=0; m<12; m++) {
  336. // 1) this year
  337. climate.mtemp20[m] = climate.hmtemp_20[m].lastadd();
  338. climate.mprec20[m] = climate.hmprec_20[m].lastadd();
  339. climate.aprec += climate.hmprec_20[m].lastadd();
  340. climate.mpet_year[m] = climate.hmeet_20[m].lastadd()*PRIESTLEY_TAYLOR;
  341. //
  342. climate.mpet20[m] = climate.mpet_year[m];
  343. if (climate.mpet_year[m] > 0.0) {
  344. climate.mprec_pet20[m] = climate.hmprec_20[m].lastadd() / climate.mpet_year[m];
  345. } else {
  346. climate.mprec_pet20[m] = 0.0;
  347. }
  348. if (climate.hmprec_20[m].lastadd() / climate.mpet_year[m] < mprec_petmin_thisyear) {
  349. mprec_petmin_thisyear = climate.hmprec_20[m].lastadd() / climate.mpet_year[m];
  350. }
  351. if (climate.hmprec_20[m].lastadd() / climate.mpet_year[m] > mprec_petmax_thisyear) {
  352. mprec_petmax_thisyear = climate.hmprec_20[m].lastadd() / climate.mpet_year[m];
  353. }
  354. // 2) past 20 years or less
  355. for (int y=startyear; y<20; y++) {
  356. climate.mtemp_20[y-1][m] = climate.mtemp_20[y][m];
  357. climate.mtemp20[m] += climate.mtemp_20[y][m];
  358. climate.mprec_20[y-1][m] = climate.mprec_20[y][m];
  359. climate.mprec20[m] += climate.mprec_20[y][m];
  360. climate.mpet_20[y-1][m] = climate.mpet_20[y][m];
  361. climate.mpet20[m] += climate.mpet_20[y][m];
  362. climate.mprec_pet_20[y-1][m] = climate.mprec_pet_20[y][m];
  363. climate.mprec_pet20[m] += climate.mprec_pet_20[y][m];
  364. }
  365. // 3) 20 years average means:
  366. climate.mtemp20[m] /= min(20, thisyear + 1); // ecev3 - changed these 4 lines
  367. climate.mprec20[m] /= min(20, thisyear + 1);
  368. climate.mpet20[m] /= min(20, thisyear + 1);
  369. climate.mprec_pet20[m] /= min(20, thisyear + 1);
  370. climate.mtemp_20[19][m] = climate.hmtemp_20[m].lastadd();
  371. climate.mprec_20[19][m] = climate.hmprec_20[m].lastadd();
  372. climate.mpet_20[19][m] = climate.mpet_year[m];
  373. if (climate.mpet_year[m] > 0.0) {
  374. climate.mprec_pet_20[19][m] = climate.hmprec_20[m].lastadd() / climate.mpet_year[m];
  375. } else {
  376. climate.mprec_pet_20[19][m] = 0.0;
  377. }
  378. }
  379. climate.mprec_petmin20 = mprec_petmin_thisyear;
  380. climate.mprec_petmax20 = mprec_petmax_thisyear;
  381. for (int y=startyear; y<20; y++) {
  382. climate.mprec_petmin_20[y-1] = climate.mprec_petmin_20[y];
  383. climate.mprec_petmin20 += climate.mprec_petmin_20[y];
  384. climate.mprec_petmax_20[y-1] = climate.mprec_petmax_20[y];
  385. climate.mprec_petmax20 += climate.mprec_petmax_20[y];
  386. }
  387. climate.mprec_petmin20 /= min(20, thisyear + 1); // ecev3
  388. climate.mprec_petmin_20[19] = mprec_petmin_thisyear;
  389. climate.mprec_petmax20 /= min(20, thisyear + 1); // ecev3
  390. climate.mprec_petmax_20[19] = mprec_petmax_thisyear;
  391. }
  392. /// Determines climate seasonality of gridcell
  393. /** Called from crop_sowing_gridcell() last day of the year
  394. */
  395. void calc_seasonality(Gridcell& gridcell) {
  396. Climate& climate = gridcell.climate;
  397. double var_temp = 0, var_prec = 0;
  398. const double TEMPMIN = 10.0; // temperature limit of coldest month used to determine type of temperature seasonality
  399. const int NMONTH = 12;
  400. double mtempKelvin[NMONTH], prec_pet_ratio20[NMONTH];
  401. double maxprec_pet20 = 0.0;
  402. double minprec_pet20 = 1000;
  403. for(int m=0;m<NMONTH;m++) {
  404. mtempKelvin[m] = 0.0;
  405. prec_pet_ratio20[m] = 0.0;
  406. }
  407. // calculate absolute temperature and prec/pet ratio for each month this year
  408. for(int i=0; i < NMONTH; ++i) {
  409. // The temperature has got to be in Kelvin, the limit 0.010 is based on that.
  410. mtempKelvin[i] = gridcell.climate.mtemp20[i] + K2degC;
  411. // Calculate precipitation/PET ratio if monthly PET is above zero
  412. prec_pet_ratio20[i] = (gridcell.climate.mpet20[i] > 0) ? gridcell.climate.mprec20[i] / gridcell.climate.mpet20[i] : 0;
  413. }
  414. // calculate variation coeffecients of temperature and prec/pet ratio for this year
  415. var_temp = variation_coefficient(mtempKelvin, NMONTH);
  416. var_prec = variation_coefficient(prec_pet_ratio20, NMONTH);
  417. gridcell.climate.var_prec = var_prec;
  418. gridcell.climate.var_temp = var_temp;
  419. if (var_prec <= 0.4 && var_temp <= 0.010) { // no seasonality
  420. climate.seasonality_lastyear = SEASONALITY_NO; // 0
  421. } else if (var_prec > 0.4) {
  422. if (var_temp <= 0.010) { // precipitation seasonality only
  423. climate.seasonality_lastyear = SEASONALITY_PREC; // 1
  424. } else if (var_temp > 0.010) {
  425. if (gridcell.climate.mtemp_min20 > TEMPMIN) { // both seasonalities, but "weak" temperature seasonality (coldest month > 10degC)
  426. climate.seasonality_lastyear = SEASONALITY_PRECTEMP;// 2
  427. } else if (gridcell.climate.mtemp_min20 < TEMPMIN) { // both seasonalities, but temperature most important
  428. climate.seasonality_lastyear = SEASONALITY_TEMPPREC;// 4
  429. }
  430. }
  431. }
  432. else if (var_prec <= 0.4 && var_temp > .01) {
  433. // Temperature seasonality only
  434. climate.seasonality_lastyear = SEASONALITY_TEMP; // 3
  435. }
  436. for(int m=0; m<NMONTH; m++) {
  437. if (climate.mprec_pet20[m] > maxprec_pet20)
  438. maxprec_pet20 = climate.mprec_pet20[m];
  439. if (climate.mprec_pet20[m] < minprec_pet20)
  440. minprec_pet20 = climate.mprec_pet20[m];
  441. }
  442. if (minprec_pet20 <= 0.5 && maxprec_pet20 <= 0.5) //Extremes of monthly means
  443. climate.prec_seasonality_lastyear = DRY; // 0
  444. else if (minprec_pet20 <= 0.5 && maxprec_pet20>0.5 && maxprec_pet20 <= 1.0)
  445. climate.prec_seasonality_lastyear = DRY_INTERMEDIATE; // 1
  446. else if (minprec_pet20 <= 0.5 && maxprec_pet20 > 1.0)
  447. climate.prec_seasonality_lastyear = DRY_WET; // 2
  448. else if (minprec_pet20 > 0.5 && minprec_pet20 <= 1.0 && maxprec_pet20 > 0.5 && maxprec_pet20 <= 1.0)
  449. climate.prec_seasonality_lastyear = INTERMEDIATE; // 3
  450. else if (minprec_pet20 > 1.0 && maxprec_pet20 > 1.0)
  451. climate.prec_seasonality_lastyear = WET; // 5
  452. else if (minprec_pet20 > 0.5 && minprec_pet20 <= 1.0 && maxprec_pet20 > 1.0)
  453. climate.prec_seasonality_lastyear = INTERMEDIATE_WET; // 4
  454. else
  455. dprintf("Problem with calculating precipitation seasonality !\n");
  456. if (climate.mprec_petmin20 <= 0.5 && climate.mprec_petmax20 <= 0.5) //Average of extremes
  457. climate.prec_range_lastyear = DRY; //0
  458. else if (climate.mprec_petmin20 <= 0.5 && climate.mprec_petmax20 > 0.5 && climate.mprec_petmax20 <= 1.0)
  459. climate.prec_range_lastyear = DRY_INTERMEDIATE; //1
  460. else if (climate.mprec_petmin20 <= 0.5 && climate.mprec_petmax20 > 1.0)
  461. climate.prec_range_lastyear = DRY_WET; //2
  462. else if (climate.mprec_petmin20 > 0.5 && climate.mprec_petmin20 <= 1.0 && climate.mprec_petmax20 > 0.5 && climate.mprec_petmax20 <= 1.0)
  463. climate.prec_range_lastyear = INTERMEDIATE; //3
  464. else if (climate.mprec_petmin20 > 1.0 && climate.mprec_petmax20 > 1.0)
  465. climate.prec_range_lastyear = WET; //5
  466. else if (climate.mprec_petmin20 > 0.5 && climate.mprec_petmin20 <= 1.0 && climate.mprec_petmax20 > 1.0)
  467. climate.prec_range_lastyear = INTERMEDIATE_WET; //4
  468. else
  469. dprintf("Problem with calculating precipitation range !\n");
  470. if (climate.mtemp_max20 <= 10)
  471. climate.temp_seasonality_lastyear = COLD; //0
  472. else if (climate.mtemp_min20 <= 10 && climate.mtemp_max20 > 10 && climate.mtemp_max20 <= 30)
  473. climate.temp_seasonality_lastyear = COLD_WARM; //1
  474. else if (climate.mtemp_min20 <= 10 && climate.mtemp_max20 > 30)
  475. climate.temp_seasonality_lastyear = COLD_HOT; //2
  476. else if (climate.mtemp_min20 > 10 && climate.mtemp_max20 <= 30)
  477. climate.temp_seasonality_lastyear = WARM; //3
  478. else if (climate.mtemp_min20 > 30)
  479. climate.temp_seasonality_lastyear = HOT; //5
  480. else if (climate.mtemp_min20 > 10 && climate.mtemp_max20 > 30)
  481. climate.temp_seasonality_lastyear = WARM_HOT; //4
  482. else
  483. dprintf("Problem with calculating temperature seasonality !\n");
  484. }
  485. void update_seasonality(Climate& climate) {
  486. climate.seasonality = climate.seasonality_lastyear;
  487. climate.temp_seasonality = climate.temp_seasonality_lastyear;
  488. climate.prec_seasonality = climate.prec_seasonality_lastyear;
  489. climate.prec_range = climate.prec_range_lastyear;
  490. }
  491. /// Monitors climate history relevant for sowing date calculation. Calculates initial sowing dates/windows.
  492. void crop_sowing_gridcell(Gridcell& gridcell, bool firstsimulationday) {
  493. Climate& climate = gridcell.climate;
  494. // ecev3 - added firstsimulationday check, otherwise this isn't done when coupled in EC-Earth
  495. if ((date.year==0 && date.day == 0) || firstsimulationday) {
  496. for (int d=0; d<10; d++)
  497. climate.dprec_10[d] = climate.prec;
  498. for (int d=0; d<2; d++)
  499. climate.sprec_2[d] = climate.prec;
  500. }
  501. climate.sprec_2[0] = climate.sprec_2[1];
  502. climate.sprec_2[1] = climate.prec;
  503. for (int d=0; d<9; d++) {
  504. climate.dprec_10[d] = climate.dprec_10[d+1];
  505. climate.sprec_2[1] += climate.dprec_10[d];
  506. }
  507. climate.dprec_10[9] = climate.prec;
  508. if (climate.temp > climate.maxtemp) //To know if temperature rises over vernalization limit
  509. climate.maxtemp = climate.temp;
  510. // Loop through PFTs
  511. pftlist.firstobj();
  512. while (pftlist.isobj) {
  513. Pft& pft = pftlist.getobj();
  514. Gridcellpft& gridcellpft = gridcell.pft[pft.id];
  515. if (pft.landcover == CROPLAND) {
  516. // Check whether temperature limits have been attained today
  517. check_crop_temp_limits(climate, gridcellpft);
  518. if (date.day == climate.testday_temp) { // June 30(180) in the north, December 31(364) in the south
  519. // Update 20-year mean of dates when temperature limits obtained
  520. calc_crop_dates_20y_mean(climate, gridcellpft, gridcell.getsimulationyear(date.year)); // ecev3
  521. // Determine sdatecalc_temp:
  522. set_sdatecalc_temp(climate, gridcellpft);
  523. // Reset maxtemp to today's value
  524. climate.maxtemp = climate.temp;
  525. }
  526. }
  527. pftlist.nextobj();
  528. }
  529. if (date.islastmonth && date.islastday) {
  530. // Update various climate 20-year means
  531. calc_m_climate_20y_mean(climate, gridcell.getsimulationyear(date.year)); // ecev3
  532. // Determines climate seasonality of gridcell
  533. calc_seasonality(gridcell);
  534. }
  535. if (date.day == climate.testday_temp) { // day 180/364
  536. update_seasonality(climate);
  537. // Calculate sowing window for each crop pft
  538. calc_sowing_windows(gridcell);
  539. }
  540. }
  541. /// Sowing date method from Waha et al. 2010
  542. /** Enters here every day when growingseason==false
  543. */
  544. void crop_sowing_date(Patch& patch, Pft& pft) {
  545. Patchpft& patchpft = patch.pft[pft.id];
  546. cropphen_struct& ppftcrop = *(patchpft.get_cropphen());
  547. Gridcell& gridcell = patch.stand.get_gridcell();
  548. Gridcellpft& gridcellpft = gridcell.pft[pft.id];
  549. Standpft& standpft = patch.stand.pft[pft.id];
  550. Climate& climate = gridcell.climate;
  551. seasonality_type seasonality = climate.seasonality;
  552. bool temp_sdate = false, prec_sdate = false, def_sdate = false;
  553. const double prec_limit = 0.1;
  554. // Different sowing date options for irrigated crops at sites with climate.seasonality == SEASONALITY_PRECTEMP:
  555. // 1. use temperature-dependent sowing limits (IRRIGATED_USE_TEMP_SDATE == true)
  556. // 2. use precipitation-triggered sowing (IRRIGATED_USE_TEMP_SDATE == false)
  557. // Determine, based upon site climate seasonality and sowing preferences for irrigated crops, if sowing should be
  558. // triggered by temperature or precipitation, or whether to use a default sowing date.
  559. if (seasonality == SEASONALITY_TEMP || seasonality == SEASONALITY_TEMPPREC ||
  560. (IRRIGATED_USE_TEMP_SDATE && seasonality == SEASONALITY_PRECTEMP && standpft.irrigated)){
  561. temp_sdate = true;
  562. } else if ((seasonality == SEASONALITY_PREC || seasonality == SEASONALITY_PRECTEMP &&
  563. (IRRIGATED_USE_TEMP_SDATE && !standpft.irrigated)) && climate.prec_range != WET) {
  564. prec_sdate = true;
  565. } else {
  566. def_sdate = true;
  567. }
  568. // adjust sowing window if too close to hlimitdate (before)
  569. if (temp_sdate && patchpft.swindow[0] != -1) {
  570. if (dayinperiod(patchpft.swindow[0], stepfromdate(ppftcrop.hlimitdate, -100), ppftcrop.hlimitdate)) {
  571. patchpft.swindow[0] = ppftcrop.hlimitdate + 1;
  572. if (dayinperiod(patchpft.swindow[1], stepfromdate(ppftcrop.hlimitdate, -100), ppftcrop.hlimitdate))
  573. patchpft.swindow[1] = ppftcrop.hlimitdate + 1;
  574. }
  575. }
  576. // monitor climate triggers within the sowing window
  577. if (dayinperiod(date.day, patchpft.swindow[0],patchpft.swindow[1])) {
  578. if (date.day != patchpft.swindow[1]) {
  579. if (temp_sdate) {
  580. if (gridcellpft.wintertype && climate.temp < pft.tempautumn || !gridcellpft.wintertype && climate.temp > pft.tempspring)
  581. ppftcrop.sdate = date.day;
  582. }
  583. else if (prec_sdate) {
  584. if (climate.prec > prec_limit || standpft.irrigated)
  585. ppftcrop.sdate = date.day;
  586. }
  587. else // if (def_sdate)
  588. ppftcrop.sdate = date.day; // first day of sowing window
  589. }
  590. else // last day of sowing window
  591. ppftcrop.sdate = date.day;
  592. }
  593. if (standpft.sdate_force >= 0) {
  594. patchpft.cropphen->sdate = standpft.sdate_force;
  595. }
  596. // calculation of hucountend (last day of heat unit sampling period); sdate is first day
  597. // NB. currently the sampling periods (which roughly correspond to growing periods) are unrealistically long,
  598. // Since shorter growing periods result in lower yield, a revision of this section
  599. // will also make a revision of crop productivity necessary
  600. if (date.day != ppftcrop.sdate) {
  601. return;
  602. }
  603. int max_lgp = gridcellpft.hlimitdate_default - gridcellpft.sdate_default;
  604. if (gridcellpft.sdate_default > gridcellpft.hlimitdate_default) {
  605. max_lgp += date.year_length();
  606. }
  607. if (prec_sdate)
  608. ppftcrop.hlimitdate = stepfromdate(date.day, max_lgp);
  609. else
  610. ppftcrop.hlimitdate = gridcellpft.hlimitdate_default;
  611. // default growth period length for crops in a rotation with several growing periods
  612. const int lgp_def_multicrop = 150;
  613. // reduced hucountend for winter crops relative to hlimitdate (days)
  614. const int reduce_hucountend_winter_crops = 20;
  615. // upper water stress lgp limit
  616. const int max_lgp_wstress = 210;
  617. if (stlist[patch.stand.stid].get_management(patch.stand.current_rot).multicrop) {
  618. ppftcrop.hucountend = stepfromdate(date.day, lgp_def_multicrop);
  619. }
  620. else if (pft.ifsdautumn && temp_sdate) {
  621. ppftcrop.hucountend = stepfromdate(ppftcrop.hlimitdate, -reduce_hucountend_winter_crops);
  622. }
  623. else {
  624. if (!(prec_sdate && climate.prec_seasonality <= DRY_WET) || standpft.irrigated) {// No water stress
  625. ppftcrop.hucountend = stepfromdate(date.day, pft.lgp_def);
  626. }
  627. else {
  628. ppftcrop.hucountend = stepfromdate(date.day, min(pft.lgp_def, max_lgp_wstress));// upper limit for growing period when risk for water stress.
  629. }
  630. }
  631. if (standpft.sdate_force >= 0 && standpft.hdate_force >= 0) {
  632. patchpft.cropphen->hlimitdate = standpft.hdate_force;
  633. patchpft.cropphen->hucountend = standpft.hdate_force;
  634. }
  635. }
  636. /// handles sowing date calculations for crop pft:s on patch level
  637. void crop_sowing_patch(Patch& patch) {
  638. pftlist.firstobj();
  639. Gridcell& gridcell = patch.stand.get_gridcell();
  640. Climate& climate = gridcell.climate;
  641. // Loop through PFTs
  642. while(pftlist.isobj) {
  643. Pft& pft = pftlist.getobj();
  644. Patchpft& patchpft = patch.pft[pft.id];
  645. Gridcellpft& gridcellpft = gridcell.pft[pft.id];
  646. if (patch.stand.pft[pft.id].active && pft.phenology == CROPGREEN) {
  647. cropphen_struct& ppftcrop = *(patchpft.get_cropphen());
  648. if (date.day == climate.testday_temp) {
  649. if (patch.stand.pft[pft.id].irrigated) {
  650. patchpft.swindow[0] = gridcellpft.swindow_irr[0];
  651. patchpft.swindow[1] = gridcellpft.swindow_irr[1];
  652. }
  653. else {
  654. patchpft.swindow[0] = gridcellpft.swindow[0];
  655. patchpft.swindow[1] = gridcellpft.swindow[1];
  656. }
  657. }
  658. if (patch.stand.pftid == pft.id && !ppftcrop.growingseason) {
  659. // copy sowing window from gridcellpft
  660. if (date.day == stepfromdate(ppftcrop.hdate, 1) && ppftcrop.hdate != -1 || date.day == climate.testday_temp) {
  661. ppftcrop.hdate = -1;
  662. if(ppftcrop.intercropseason)
  663. ppftcrop.bicdate = -1;
  664. int thisyear = gridcell.getsimulationyear(date.year);
  665. if (gridcellpft.swindow[0] == -1 && thisyear) {
  666. gridcellpft.sowing_restriction = true;
  667. ppftcrop.hdate = -1;
  668. ppftcrop.eicdate = -1; //redundant
  669. patch.stand.isrotationday = true;
  670. }
  671. else {
  672. if (!patch.stand.infallow)
  673. gridcellpft.sowing_restriction = false;
  674. else if (patch.stand.ndays_inrotation > 180)
  675. patch.stand.isrotationday = true;
  676. }
  677. }
  678. if (!gridcellpft.sowing_restriction) {
  679. // new sowing date method (Waha et al. 2010)
  680. crop_sowing_date(patch, pft);
  681. }
  682. }
  683. // set eicdate (last intercrop day)
  684. if (ppftcrop.sdate != -1) {
  685. if (!ppftcrop.growingseason)
  686. ppftcrop.eicdate = stepfromdate(ppftcrop.sdate, - 15);
  687. if (dayinperiod(date.day, ppftcrop.eicdate, ppftcrop.sdate)) {
  688. if (ppftcrop.intercropseason)
  689. ppftcrop.eicdate = date.day;
  690. else if (ppftcrop.bicdate == ppftcrop.sdate)
  691. ppftcrop.bicdate = -1;
  692. }
  693. }
  694. }
  695. pftlist.nextobj();
  696. }
  697. }
  698. //////////////////////////////////////////////////////////////////////////////////////////
  699. // REFERENCES
  700. //
  701. // Bondeau A, Smith PC, Zaehle S, Schaphoff S, Lucht W, Cramer W, Gerten D, Lotze-Campen H,
  702. // Müller C, Reichstein M & Smith B 2007. Modelling the role of agriculture for the
  703. // 20th century global terrestrial carbon balance. Global Change Biology, 13:679-706.
  704. // Waha K, van Bussel LGJ, Müller C, and Bondeau A.2012. Climate-driven simulation of global
  705. // crop sowing dates, Global Ecol Biogeogr 21:247-259