123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873 |
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- /// \file cropsowing.h
- /// \brief Seasonality and sowing date calculations
- /// \author Mats Lindeskog, Stefan Olin
- /// $Date: $
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #include "config.h"
- #include "landcover.h"
- #include "cropsowing.h"
- /// Use temperature-dependent sowing date for irrigated crops at site with PRECTEMP seasonality.
- const bool IRRIGATED_USE_TEMP_SDATE = true;
- /// Use lower temperature limit for sowing for all crops
- const bool LOW_SOWING_TEMPERATURE_LIMIT = false;
- /// Autumn sowing types for crops
- enum {NOFORCING, AUTUMNSOWING, SPRINGSOWING};
- /// Monitors whether temperature limits have been attained for this pft this day
- /** Called from crop_sowing_gridcell() each day
- */
- void check_crop_temp_limits(Climate& climate, Gridcellpft& gridcellpft) {
- Pft& pft = gridcellpft.pft;
- // check if spring conditions are present this day:
- if (climate.temp > pft.tempspring && climate.dtemp_31[29] <= pft.tempspring) { // NB. after updating dtemp_31 with today's value
- // TeWW,TeCo,TeSf,TeRa: 12,14,13,12 (NB 5,14,15,5 in Bondeau 2007);
- if (climate.lat >= 0.0 && date.day > 300) {
- gridcellpft.last_springdate = date.day - date.year_length();
- } else {
- gridcellpft.last_springdate = date.day;
- }
- gridcellpft.springoccurred = true;
- }
- // check if autumn conditions are present this day:
- if (!pft.ifsdautumn) {
- return;
- }
- if (climate.temp<pft.tempautumn && climate.dtemp_31[29]>=pft.tempautumn && !gridcellpft.autumnoccurred) { //TeWW,TeRa: 12,17
- if (climate.lat >= 0.0 && date.day<100) {
- gridcellpft.first_autumndate = date.day + date.year_length();
- } else {
- gridcellpft.first_autumndate = date.day;
- }
- gridcellpft.autumnoccurred = true;
- }
- // check if vernilisation conditions are present this day:
- if (climate.temp < pft.trg && climate.dtemp_31[29] >= pft.trg && !gridcellpft.vernstartoccurred) { //TeWW,TeRa: 12,12
- gridcellpft.vernstartoccurred = true;
- }
- if (climate.temp>pft.trg && climate.dtemp_31[29] <= pft.trg) { //TeWW,TeRa: 12,12
- if (climate.lat >= 0.0 && date.day > 300) {
- gridcellpft.last_verndate = date.day - date.year_length();
- } else {
- gridcellpft.last_verndate = date.day;
- }
- gridcellpft.vernendoccurred = true;
- }
- }
- /// Updates 20-year mean of dates when temperature limits obtained, used for sowing date calculation
- /** Called from crop_sowing_gridcell() once a year
- */
- void calc_crop_dates_20y_mean(Climate& climate, Gridcellpft& gridcellpft, int thisyear) { // ecev3 - replaced date.year below with thisyear
- Pft& pft = gridcellpft.pft;
- /////////////////////////////////////////////////////////////////////////////////
- // Check if spring and frost conditions occurred during the past year. //
- // If not, set this year's date to either sdate_default or climate.coldestday: //
- /////////////////////////////////////////////////////////////////////////////////
- // if no spring occured during last year
- if (!gridcellpft.springoccurred) {
- if (climate.temp <= pft.tempspring)
- gridcellpft.last_springdate = date.day;
- else
- gridcellpft.last_springdate = climate.coldestday;
- }
- // if no autumn occured during last year
- if (pft.ifsdautumn && !gridcellpft.autumnoccurred) { //TeWW,TeRa
- if (climate.maxtemp < pft.tempautumn || climate.maxtemp >= pft.tempautumn && climate.mtemp_min < pft.tempautumn) {
- gridcellpft.first_autumndate = date.day;
- }
- else { //too warm
- gridcellpft.first_autumndate = climate.coldestday;
- if (climate.lat >= 0.0 && gridcellpft.first_autumndate < 180)
- gridcellpft.first_autumndate += date.year_length();
- }
- }
- // if temperature did not pass above the vernalisation temperature last year
- // to avoid last_verndate20 to precede last_springdate20 (Bondeau used coldest day)
- // 60 days is the maximum number of vernalization days
- if (pft.ifsdautumn && !gridcellpft.vernendoccurred) { //TeWW,TeRa
- gridcellpft.last_verndate = gridcellpft.last_springdate + def_verndate_ndays_after_last_springdate;
- }
- ///////////////////////////////////////////////////////////////////////////////////////
- // Update spring and frost date 20-year arrays and calculate 20 years average means: //
- ///////////////////////////////////////////////////////////////////////////////////////
- // 1) this year
- gridcellpft.last_springdate20=gridcellpft.last_springdate;
- if (pft.ifsdautumn) { // TeWW,TeRa
- gridcellpft.first_autumndate20 = gridcellpft.first_autumndate;
- // 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
- if (thisyear == 1 && climate.lat >= 0.0) // No autumn first half of first year, set value to same as for second year
- gridcellpft.first_autumndate_20[19] = gridcellpft.first_autumndate;
- gridcellpft.last_verndate20 = gridcellpft.last_verndate;
- }
- // 2) starting year (1st of 20 or less)
- // ecev3, was: int startyear = 20 - (int)min(19, date.year);
- int startyear = 20 - (int)min(19, thisyear);
- // 3) past 20 years or less
- for (int y=startyear; y<20; y++) {
- gridcellpft.last_springdate_20[y-1] = gridcellpft.last_springdate_20[y];
- gridcellpft.last_springdate20 += gridcellpft.last_springdate_20[y];
- if (pft.ifsdautumn) { // TeWW,TeRa
- gridcellpft.first_autumndate_20[y-1] = gridcellpft.first_autumndate_20[y];
- gridcellpft.first_autumndate20 += gridcellpft.first_autumndate_20[y];
- gridcellpft.last_verndate_20[y-1] = gridcellpft.last_verndate_20[y];
- gridcellpft.last_verndate20 += gridcellpft.last_verndate_20[y];
- }
- }
- // 4) 20 years average means:
- // ecev3, was: gridcellpft.last_springdate20 /= (int)min(20,date.year + 1);
- gridcellpft.last_springdate20 /= (int)min(20,thisyear + 1);
- if (gridcellpft.last_springdate20 < 0) {
- gridcellpft.last_springdate20 += date.year_length();
- }
- gridcellpft.last_springdate_20[19] = gridcellpft.last_springdate;
- if (pft.ifsdautumn) { // TeWW,TeRa
- // ecev3, was: gridcellpft.first_autumndate20 /= (int)min(20,date.year + 1);
- gridcellpft.first_autumndate20 /= (int)min(20,thisyear + 1);
- if (gridcellpft.first_autumndate20 > date.year_length() - 1) {
- gridcellpft.first_autumndate20 -= date.year_length();
- }
- gridcellpft.first_autumndate_20[19] = gridcellpft.first_autumndate;
- // ecev3, was: gridcellpft.last_verndate20 /= (int)min(20,date.year + 1);
- gridcellpft.last_verndate20 /= (int)min(20,thisyear + 1);
- if (gridcellpft.last_verndate20 < 0) {
- gridcellpft.last_verndate20 += date.year_length();
- }
- gridcellpft.last_verndate_20[19] = gridcellpft.last_verndate;
- }
- }
- /// Calculates sdatecalc_temp
- /** Called from crop_sowing_gridcell() once a year (June 30(180) in the north, December 31(364) in the south)
- */
- void set_sdatecalc_temp(Climate& climate, Gridcellpft& gridcellpft) {
- Pft& pft = gridcellpft.pft;
- if (pft.ifsdautumn && pft.forceautumnsowing != SPRINGSOWING) { // TeWW,TeRa:
- // Use autumn sowing if first_autumndate20 is set (autumn conditions met during the past 20 years):
- if (!((gridcellpft.first_autumndate20 == climate.testday_temp || gridcellpft.first_autumndate20 == climate.coldestday) &&
- gridcellpft.first_autumndate % date.year_length() == gridcellpft.first_autumndate20)) {
- gridcellpft.sdatecalc_temp = gridcellpft.first_autumndate20;
- gridcellpft.wintertype = true;
- }
- // if not, use spring sowing
- else { // if (gridcellpft.first_autumndate20==climate.coldestday)
- if (!((gridcellpft.last_springdate20 == climate.testday_temp || gridcellpft.last_springdate20 == climate.coldestday) &&
- gridcellpft.last_springdate == gridcellpft.last_springdate20)
- && pft.forceautumnsowing != AUTUMNSOWING) {
- gridcellpft.sdatecalc_temp = gridcellpft.last_springdate20;
- gridcellpft.wintertype = false;
- }
- else { // If neither spring nor autumn occurred during the past 20 years.
- if (climate.maxtemp < pft.tempspring) { // Too cold to sow at all.
- gridcellpft.sdatecalc_temp = -1;
- }
- else { // Too warm; avoid warmest period.
- gridcellpft.sdatecalc_temp = climate.coldestday;
- gridcellpft.wintertype = true;
- }
- }
- }
- // If autumn first_autumndate20 is earlier than hlimitdate, use last_springdate20 (winter is too long):
- if (dayinperiod(gridcellpft.sdatecalc_temp, climate.testday_temp, gridcellpft.hlimitdate_default)
- && pft.forceautumnsowing != AUTUMNSOWING) {
- gridcellpft.sdatecalc_temp = gridcellpft.last_springdate20; // use last_springdate20 disregarding earlier choices
- gridcellpft.wintertype = false;
- }
- // Forced sowing date read from input file.
- // Calculated value used if value for pft not found in file.
- if (gridcellpft.sdate_force >= 0) {
- if ((abs(gridcellpft.sdate_force - gridcellpft.first_autumndate20) <= abs(gridcellpft.sdate_force - gridcellpft.last_springdate20)))
- gridcellpft.wintertype = true;
- else
- gridcellpft.wintertype = false;
- gridcellpft.sdatecalc_temp = gridcellpft.sdate_force;
- }
- } else {
- if (pft.sd_adjust) { // will force sdate towards a certain day (71 for TeCo)
- gridcellpft.sdatecalc_temp = (int)(pft.sd_adjust_par1 / pft.sd_adjust_par2 * (gridcellpft.last_springdate20 - climate.adjustlat)
- + pft.sd_adjust_par3 + climate.adjustlat);
- }
- else {
- gridcellpft.sdatecalc_temp = gridcellpft.last_springdate20;
- }
- //Same lower temperature limit for sowing as for winter crops above.
- if (LOW_SOWING_TEMPERATURE_LIMIT &&
- gridcellpft.last_springdate20 == climate.testday_temp &&
- gridcellpft.last_springdate == gridcellpft.last_springdate20 &&
- climate.maxtemp < pft.tempspring) {
- // If spring has not occurred during the past 20 years or too cold to sow.
- gridcellpft.sdatecalc_temp = -1;
- }
- }
- // Climatic limits for crop sowing:
- if (climate.mtemp_min20 > pft.maxtemp_sowing) {
- gridcellpft.sdatecalc_temp = -1;
- }
- gridcellpft.springoccurred = false;
- gridcellpft.vernstartoccurred = false;
- gridcellpft.vernendoccurred = false;
- gridcellpft.autumnoccurred = false;
- }
- /// Calculates the Julian start and end day of a month.
- /** January 1st is set to 0.
- */
- void monthdates(int& start, int& end, int month) {
- int months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- start = 0;
- int m = 0;
- while (m < month){
- start += months[m];
- m++;
- }
- end = start + months[m] - 1;
- }
- /// Calculates sowing window for each crop pft
- /** Called from crop_sowing_gridcell() once a year
- */
- void calc_sowing_windows(Gridcell& gridcell) {
- Climate& climate = gridcell.climate;
- seasonality_type seasonality = climate.seasonality;
- int sow_month = 0;
- // Find the wettest month
- if (seasonality == SEASONALITY_PREC || seasonality == SEASONALITY_PRECTEMP) {
- double max = 0.0;
- for (int m=0; m<12; m++) {
- double sum = 0.0;
- for (int i=0; i<4; i++) {
- int mm = m + i;
- if (mm >= 12)
- mm -= 12;
- // Implement a check later to see if it makes any difference to to use the precipitation only
- if (true) {
- if (climate.mpet20[mm] > 0.0)
- sum += climate.mprec20[mm] / climate.mpet20[mm];
- }
- else
- sum += climate.mprec20[mm];
- }
- if (sum > max) {
- max = sum;
- // Months are stored as, 0-11
- sow_month=m;
- }
- }
- }
- // Set gridcell-level sowing windows for crop pft:s
- pftlist.firstobj();
- while(pftlist.isobj) {
- Pft& pft = pftlist.getobj();
- Gridcellpft& gridcellpft = gridcell.pft[pft.id];
- if (pft.phenology == CROPGREEN) {
- int swindow_temp[2];
- // ecev3 - extra initialisation
- swindow_temp[0] = 0;
- swindow_temp[1] = 0;
- int swindow_prec[2];
- // Calculate temperature-dependent sowing windows
- if (gridcellpft.sdatecalc_temp != -1) {
- // Set sowing window around sdatecalc_temp
- swindow_temp[0] = stepfromdate(gridcellpft.sdatecalc_temp, -15);
- swindow_temp[1] = stepfromdate(gridcellpft.sdatecalc_temp, 15);
- if (!gridcellpft.wintertype && dayinperiod(swindow_temp[0], stepfromdate(climate.coldestday, -100), climate.coldestday)) {
- swindow_temp[0] = climate.coldestday;
- if (dayinperiod(swindow_temp[1], stepfromdate(climate.coldestday, -100), climate.coldestday)) {
- swindow_temp[1] = climate.coldestday;
- }
- }
- if (gridcellpft.wintertype && dayinperiod(swindow_temp[1], climate.coldestday, stepfromdate(climate.coldestday, 100))) {
- swindow_temp[1] = climate.coldestday;
- if (dayinperiod(swindow_temp[0], climate.coldestday, stepfromdate(climate.coldestday, 100))) {
- swindow_temp[0] = climate.coldestday;
- }
- }
- }
- // Calculate precipitation-dependent sowing windows
- monthdates(swindow_prec[0],swindow_prec[1],sow_month);
- // A conservative choice to expand the sowing window
- swindow_prec[0] = stepfromdate(swindow_prec[0], -15);
- // Determine, based upon site climate seasonality, if sowing in rainfed stands should be triggered by
- // temperature or precipitation, or whether to use a default sowing date.
- bool temp_sdate = false, prec_sdate = false, def_sdate = false;
- if (seasonality == SEASONALITY_TEMP || seasonality == SEASONALITY_TEMPPREC)
- temp_sdate = true;
- else if ((seasonality == SEASONALITY_PREC || seasonality == SEASONALITY_PRECTEMP) && climate.prec_range != WET)
- prec_sdate = true;
- else // if (seasonality == SEASONALITY_NO) || (seasonality == SEASONALITY_PREC || seasonality == SEASONALITY_PRECTEMP) && climate.prec_range == WET)
- def_sdate = true;
- if (temp_sdate && gridcellpft.sdatecalc_temp != -1) { // ecev3 - added gridcellpft.sdatecalc_temp != -1
- gridcellpft.swindow[0] = swindow_temp[0];
- gridcellpft.swindow[1] = swindow_temp[1];
- }
- else if (prec_sdate) {
- gridcellpft.swindow[0] = swindow_prec[0];
- gridcellpft.swindow[1] = swindow_prec[1];
- }
- else if (def_sdate) {
- gridcellpft.swindow[0] = gridcellpft.sdate_default;
- gridcellpft.swindow[1] = stepfromdate(gridcellpft.sdate_default, 15);
- }
- // Rules for irrigated crops:
- // Different sowing date options for irrigated crops at sites with climate.seasonality == SEASONALITY_PRECTEMP:
- // 1. use temperature-dependent sowing limits (IRRIGATED_USE_TEMP_SDATE == true)
- // 2. use precipitation-triggered sowing (IRRIGATED_USE_TEMP_SDATE == false)
- if (IRRIGATED_USE_TEMP_SDATE && seasonality == SEASONALITY_PRECTEMP && gridcellpft.sdatecalc_temp != -1) { // ecev3 - added gridcellpft.sdatecalc_temp != -1
- gridcellpft.swindow_irr[0] = swindow_temp[0];
- gridcellpft.swindow_irr[1] = swindow_temp[1];
- }
- else {
- gridcellpft.swindow_irr[0] = gridcellpft.swindow[0];
- gridcellpft.swindow_irr[1] = gridcellpft.swindow[1];
- }
- // Includes all temperature limits for sowing set in set_sdatecalc_temp() also for sites with any type of temperature seasonality
- if (gridcellpft.sdatecalc_temp == -1) {
- gridcellpft.swindow[0] = -1;
- gridcellpft.swindow[1] = -1;
- }
- }
- pftlist.nextobj();
- }
- }
- /// Updates various climate 20-year means, used for sowing date calculation
- /** Called from crop_sowing_gridcell() once a year
- */
- void calc_m_climate_20y_mean(Climate& climate, int thisyear) { // ecev3 - replaced date.year below with thisyear
- int startyear = 20 - (int)min(19, thisyear);
- double mprec_petmin_thisyear = 1.0;
- double mprec_petmax_thisyear = 0.0;
- climate.aprec = 0.0;
- for(int m=0; m<12; m++) {
- // 1) this year
- climate.mtemp20[m] = climate.hmtemp_20[m].lastadd();
- climate.mprec20[m] = climate.hmprec_20[m].lastadd();
- climate.aprec += climate.hmprec_20[m].lastadd();
- climate.mpet_year[m] = climate.hmeet_20[m].lastadd()*PRIESTLEY_TAYLOR;
- //
- climate.mpet20[m] = climate.mpet_year[m];
- if (climate.mpet_year[m] > 0.0) {
- climate.mprec_pet20[m] = climate.hmprec_20[m].lastadd() / climate.mpet_year[m];
- } else {
- climate.mprec_pet20[m] = 0.0;
- }
- if (climate.hmprec_20[m].lastadd() / climate.mpet_year[m] < mprec_petmin_thisyear) {
- mprec_petmin_thisyear = climate.hmprec_20[m].lastadd() / climate.mpet_year[m];
- }
- if (climate.hmprec_20[m].lastadd() / climate.mpet_year[m] > mprec_petmax_thisyear) {
- mprec_petmax_thisyear = climate.hmprec_20[m].lastadd() / climate.mpet_year[m];
- }
- // 2) past 20 years or less
- for (int y=startyear; y<20; y++) {
- climate.mtemp_20[y-1][m] = climate.mtemp_20[y][m];
- climate.mtemp20[m] += climate.mtemp_20[y][m];
- climate.mprec_20[y-1][m] = climate.mprec_20[y][m];
- climate.mprec20[m] += climate.mprec_20[y][m];
- climate.mpet_20[y-1][m] = climate.mpet_20[y][m];
- climate.mpet20[m] += climate.mpet_20[y][m];
- climate.mprec_pet_20[y-1][m] = climate.mprec_pet_20[y][m];
- climate.mprec_pet20[m] += climate.mprec_pet_20[y][m];
- }
- // 3) 20 years average means:
- climate.mtemp20[m] /= min(20, thisyear + 1); // ecev3 - changed these 4 lines
- climate.mprec20[m] /= min(20, thisyear + 1);
- climate.mpet20[m] /= min(20, thisyear + 1);
- climate.mprec_pet20[m] /= min(20, thisyear + 1);
- climate.mtemp_20[19][m] = climate.hmtemp_20[m].lastadd();
- climate.mprec_20[19][m] = climate.hmprec_20[m].lastadd();
- climate.mpet_20[19][m] = climate.mpet_year[m];
- if (climate.mpet_year[m] > 0.0) {
- climate.mprec_pet_20[19][m] = climate.hmprec_20[m].lastadd() / climate.mpet_year[m];
- } else {
- climate.mprec_pet_20[19][m] = 0.0;
- }
- }
- climate.mprec_petmin20 = mprec_petmin_thisyear;
- climate.mprec_petmax20 = mprec_petmax_thisyear;
- for (int y=startyear; y<20; y++) {
- climate.mprec_petmin_20[y-1] = climate.mprec_petmin_20[y];
- climate.mprec_petmin20 += climate.mprec_petmin_20[y];
- climate.mprec_petmax_20[y-1] = climate.mprec_petmax_20[y];
- climate.mprec_petmax20 += climate.mprec_petmax_20[y];
- }
- climate.mprec_petmin20 /= min(20, thisyear + 1); // ecev3
- climate.mprec_petmin_20[19] = mprec_petmin_thisyear;
- climate.mprec_petmax20 /= min(20, thisyear + 1); // ecev3
- climate.mprec_petmax_20[19] = mprec_petmax_thisyear;
- }
- /// Determines climate seasonality of gridcell
- /** Called from crop_sowing_gridcell() last day of the year
- */
- void calc_seasonality(Gridcell& gridcell) {
- Climate& climate = gridcell.climate;
- double var_temp = 0, var_prec = 0;
- const double TEMPMIN = 10.0; // temperature limit of coldest month used to determine type of temperature seasonality
- const int NMONTH = 12;
- double mtempKelvin[NMONTH], prec_pet_ratio20[NMONTH];
- double maxprec_pet20 = 0.0;
- double minprec_pet20 = 1000;
- for(int m=0;m<NMONTH;m++) {
- mtempKelvin[m] = 0.0;
- prec_pet_ratio20[m] = 0.0;
- }
- // calculate absolute temperature and prec/pet ratio for each month this year
- for(int i=0; i < NMONTH; ++i) {
- // The temperature has got to be in Kelvin, the limit 0.010 is based on that.
- mtempKelvin[i] = gridcell.climate.mtemp20[i] + K2degC;
- // Calculate precipitation/PET ratio if monthly PET is above zero
- prec_pet_ratio20[i] = (gridcell.climate.mpet20[i] > 0) ? gridcell.climate.mprec20[i] / gridcell.climate.mpet20[i] : 0;
- }
-
- // calculate variation coeffecients of temperature and prec/pet ratio for this year
- var_temp = variation_coefficient(mtempKelvin, NMONTH);
- var_prec = variation_coefficient(prec_pet_ratio20, NMONTH);
- gridcell.climate.var_prec = var_prec;
- gridcell.climate.var_temp = var_temp;
- if (var_prec <= 0.4 && var_temp <= 0.010) { // no seasonality
- climate.seasonality_lastyear = SEASONALITY_NO; // 0
- } else if (var_prec > 0.4) {
- if (var_temp <= 0.010) { // precipitation seasonality only
- climate.seasonality_lastyear = SEASONALITY_PREC; // 1
- } else if (var_temp > 0.010) {
- if (gridcell.climate.mtemp_min20 > TEMPMIN) { // both seasonalities, but "weak" temperature seasonality (coldest month > 10degC)
- climate.seasonality_lastyear = SEASONALITY_PRECTEMP;// 2
- } else if (gridcell.climate.mtemp_min20 < TEMPMIN) { // both seasonalities, but temperature most important
- climate.seasonality_lastyear = SEASONALITY_TEMPPREC;// 4
- }
- }
- }
- else if (var_prec <= 0.4 && var_temp > .01) {
- // Temperature seasonality only
- climate.seasonality_lastyear = SEASONALITY_TEMP; // 3
- }
- for(int m=0; m<NMONTH; m++) {
- if (climate.mprec_pet20[m] > maxprec_pet20)
- maxprec_pet20 = climate.mprec_pet20[m];
- if (climate.mprec_pet20[m] < minprec_pet20)
- minprec_pet20 = climate.mprec_pet20[m];
- }
- if (minprec_pet20 <= 0.5 && maxprec_pet20 <= 0.5) //Extremes of monthly means
- climate.prec_seasonality_lastyear = DRY; // 0
- else if (minprec_pet20 <= 0.5 && maxprec_pet20>0.5 && maxprec_pet20 <= 1.0)
- climate.prec_seasonality_lastyear = DRY_INTERMEDIATE; // 1
- else if (minprec_pet20 <= 0.5 && maxprec_pet20 > 1.0)
- climate.prec_seasonality_lastyear = DRY_WET; // 2
- else if (minprec_pet20 > 0.5 && minprec_pet20 <= 1.0 && maxprec_pet20 > 0.5 && maxprec_pet20 <= 1.0)
- climate.prec_seasonality_lastyear = INTERMEDIATE; // 3
- else if (minprec_pet20 > 1.0 && maxprec_pet20 > 1.0)
- climate.prec_seasonality_lastyear = WET; // 5
- else if (minprec_pet20 > 0.5 && minprec_pet20 <= 1.0 && maxprec_pet20 > 1.0)
- climate.prec_seasonality_lastyear = INTERMEDIATE_WET; // 4
- else
- dprintf("Problem with calculating precipitation seasonality !\n");
- if (climate.mprec_petmin20 <= 0.5 && climate.mprec_petmax20 <= 0.5) //Average of extremes
- climate.prec_range_lastyear = DRY; //0
- else if (climate.mprec_petmin20 <= 0.5 && climate.mprec_petmax20 > 0.5 && climate.mprec_petmax20 <= 1.0)
- climate.prec_range_lastyear = DRY_INTERMEDIATE; //1
- else if (climate.mprec_petmin20 <= 0.5 && climate.mprec_petmax20 > 1.0)
- climate.prec_range_lastyear = DRY_WET; //2
- else if (climate.mprec_petmin20 > 0.5 && climate.mprec_petmin20 <= 1.0 && climate.mprec_petmax20 > 0.5 && climate.mprec_petmax20 <= 1.0)
- climate.prec_range_lastyear = INTERMEDIATE; //3
- else if (climate.mprec_petmin20 > 1.0 && climate.mprec_petmax20 > 1.0)
- climate.prec_range_lastyear = WET; //5
- else if (climate.mprec_petmin20 > 0.5 && climate.mprec_petmin20 <= 1.0 && climate.mprec_petmax20 > 1.0)
- climate.prec_range_lastyear = INTERMEDIATE_WET; //4
- else
- dprintf("Problem with calculating precipitation range !\n");
- if (climate.mtemp_max20 <= 10)
- climate.temp_seasonality_lastyear = COLD; //0
- else if (climate.mtemp_min20 <= 10 && climate.mtemp_max20 > 10 && climate.mtemp_max20 <= 30)
- climate.temp_seasonality_lastyear = COLD_WARM; //1
- else if (climate.mtemp_min20 <= 10 && climate.mtemp_max20 > 30)
- climate.temp_seasonality_lastyear = COLD_HOT; //2
- else if (climate.mtemp_min20 > 10 && climate.mtemp_max20 <= 30)
- climate.temp_seasonality_lastyear = WARM; //3
- else if (climate.mtemp_min20 > 30)
- climate.temp_seasonality_lastyear = HOT; //5
- else if (climate.mtemp_min20 > 10 && climate.mtemp_max20 > 30)
- climate.temp_seasonality_lastyear = WARM_HOT; //4
- else
- dprintf("Problem with calculating temperature seasonality !\n");
- }
- void update_seasonality(Climate& climate) {
- climate.seasonality = climate.seasonality_lastyear;
- climate.temp_seasonality = climate.temp_seasonality_lastyear;
- climate.prec_seasonality = climate.prec_seasonality_lastyear;
- climate.prec_range = climate.prec_range_lastyear;
- }
- /// Monitors climate history relevant for sowing date calculation. Calculates initial sowing dates/windows.
- void crop_sowing_gridcell(Gridcell& gridcell, bool firstsimulationday) {
- Climate& climate = gridcell.climate;
- // ecev3 - added firstsimulationday check, otherwise this isn't done when coupled in EC-Earth
- if ((date.year==0 && date.day == 0) || firstsimulationday) {
- for (int d=0; d<10; d++)
- climate.dprec_10[d] = climate.prec;
- for (int d=0; d<2; d++)
- climate.sprec_2[d] = climate.prec;
- }
- climate.sprec_2[0] = climate.sprec_2[1];
- climate.sprec_2[1] = climate.prec;
- for (int d=0; d<9; d++) {
- climate.dprec_10[d] = climate.dprec_10[d+1];
- climate.sprec_2[1] += climate.dprec_10[d];
- }
- climate.dprec_10[9] = climate.prec;
- if (climate.temp > climate.maxtemp) //To know if temperature rises over vernalization limit
- climate.maxtemp = climate.temp;
- // Loop through PFTs
- pftlist.firstobj();
- while (pftlist.isobj) {
- Pft& pft = pftlist.getobj();
- Gridcellpft& gridcellpft = gridcell.pft[pft.id];
- if (pft.landcover == CROPLAND) {
- // Check whether temperature limits have been attained today
- check_crop_temp_limits(climate, gridcellpft);
- if (date.day == climate.testday_temp) { // June 30(180) in the north, December 31(364) in the south
- // Update 20-year mean of dates when temperature limits obtained
- calc_crop_dates_20y_mean(climate, gridcellpft, gridcell.getsimulationyear(date.year)); // ecev3
- // Determine sdatecalc_temp:
- set_sdatecalc_temp(climate, gridcellpft);
- // Reset maxtemp to today's value
- climate.maxtemp = climate.temp;
- }
- }
- pftlist.nextobj();
- }
- if (date.islastmonth && date.islastday) {
- // Update various climate 20-year means
- calc_m_climate_20y_mean(climate, gridcell.getsimulationyear(date.year)); // ecev3
- // Determines climate seasonality of gridcell
- calc_seasonality(gridcell);
- }
- if (date.day == climate.testday_temp) { // day 180/364
- update_seasonality(climate);
- // Calculate sowing window for each crop pft
- calc_sowing_windows(gridcell);
- }
- }
- /// Sowing date method from Waha et al. 2010
- /** Enters here every day when growingseason==false
- */
- void crop_sowing_date(Patch& patch, Pft& pft) {
- Patchpft& patchpft = patch.pft[pft.id];
- cropphen_struct& ppftcrop = *(patchpft.get_cropphen());
- Gridcell& gridcell = patch.stand.get_gridcell();
- Gridcellpft& gridcellpft = gridcell.pft[pft.id];
- Standpft& standpft = patch.stand.pft[pft.id];
- Climate& climate = gridcell.climate;
- seasonality_type seasonality = climate.seasonality;
- bool temp_sdate = false, prec_sdate = false, def_sdate = false;
- const double prec_limit = 0.1;
- // Different sowing date options for irrigated crops at sites with climate.seasonality == SEASONALITY_PRECTEMP:
- // 1. use temperature-dependent sowing limits (IRRIGATED_USE_TEMP_SDATE == true)
- // 2. use precipitation-triggered sowing (IRRIGATED_USE_TEMP_SDATE == false)
- // Determine, based upon site climate seasonality and sowing preferences for irrigated crops, if sowing should be
- // triggered by temperature or precipitation, or whether to use a default sowing date.
- if (seasonality == SEASONALITY_TEMP || seasonality == SEASONALITY_TEMPPREC ||
- (IRRIGATED_USE_TEMP_SDATE && seasonality == SEASONALITY_PRECTEMP && standpft.irrigated)){
- temp_sdate = true;
- } else if ((seasonality == SEASONALITY_PREC || seasonality == SEASONALITY_PRECTEMP &&
- (IRRIGATED_USE_TEMP_SDATE && !standpft.irrigated)) && climate.prec_range != WET) {
- prec_sdate = true;
- } else {
- def_sdate = true;
- }
- // adjust sowing window if too close to hlimitdate (before)
- if (temp_sdate && patchpft.swindow[0] != -1) {
- if (dayinperiod(patchpft.swindow[0], stepfromdate(ppftcrop.hlimitdate, -100), ppftcrop.hlimitdate)) {
- patchpft.swindow[0] = ppftcrop.hlimitdate + 1;
- if (dayinperiod(patchpft.swindow[1], stepfromdate(ppftcrop.hlimitdate, -100), ppftcrop.hlimitdate))
- patchpft.swindow[1] = ppftcrop.hlimitdate + 1;
- }
- }
- // monitor climate triggers within the sowing window
- if (dayinperiod(date.day, patchpft.swindow[0],patchpft.swindow[1])) {
- if (date.day != patchpft.swindow[1]) {
- if (temp_sdate) {
- if (gridcellpft.wintertype && climate.temp < pft.tempautumn || !gridcellpft.wintertype && climate.temp > pft.tempspring)
- ppftcrop.sdate = date.day;
- }
- else if (prec_sdate) {
- if (climate.prec > prec_limit || standpft.irrigated)
- ppftcrop.sdate = date.day;
- }
- else // if (def_sdate)
- ppftcrop.sdate = date.day; // first day of sowing window
- }
- else // last day of sowing window
- ppftcrop.sdate = date.day;
- }
- if (standpft.sdate_force >= 0) {
- patchpft.cropphen->sdate = standpft.sdate_force;
- }
- // calculation of hucountend (last day of heat unit sampling period); sdate is first day
- // NB. currently the sampling periods (which roughly correspond to growing periods) are unrealistically long,
- // Since shorter growing periods result in lower yield, a revision of this section
- // will also make a revision of crop productivity necessary
- if (date.day != ppftcrop.sdate) {
- return;
- }
- int max_lgp = gridcellpft.hlimitdate_default - gridcellpft.sdate_default;
- if (gridcellpft.sdate_default > gridcellpft.hlimitdate_default) {
- max_lgp += date.year_length();
- }
- if (prec_sdate)
- ppftcrop.hlimitdate = stepfromdate(date.day, max_lgp);
- else
- ppftcrop.hlimitdate = gridcellpft.hlimitdate_default;
- // default growth period length for crops in a rotation with several growing periods
- const int lgp_def_multicrop = 150;
- // reduced hucountend for winter crops relative to hlimitdate (days)
- const int reduce_hucountend_winter_crops = 20;
- // upper water stress lgp limit
- const int max_lgp_wstress = 210;
- if (stlist[patch.stand.stid].get_management(patch.stand.current_rot).multicrop) {
- ppftcrop.hucountend = stepfromdate(date.day, lgp_def_multicrop);
- }
- else if (pft.ifsdautumn && temp_sdate) {
- ppftcrop.hucountend = stepfromdate(ppftcrop.hlimitdate, -reduce_hucountend_winter_crops);
- }
- else {
- if (!(prec_sdate && climate.prec_seasonality <= DRY_WET) || standpft.irrigated) {// No water stress
- ppftcrop.hucountend = stepfromdate(date.day, pft.lgp_def);
- }
- else {
- ppftcrop.hucountend = stepfromdate(date.day, min(pft.lgp_def, max_lgp_wstress));// upper limit for growing period when risk for water stress.
- }
- }
- if (standpft.sdate_force >= 0 && standpft.hdate_force >= 0) {
- patchpft.cropphen->hlimitdate = standpft.hdate_force;
- patchpft.cropphen->hucountend = standpft.hdate_force;
- }
- }
- /// handles sowing date calculations for crop pft:s on patch level
- void crop_sowing_patch(Patch& patch) {
- pftlist.firstobj();
- Gridcell& gridcell = patch.stand.get_gridcell();
- Climate& climate = gridcell.climate;
- // Loop through PFTs
- while(pftlist.isobj) {
- Pft& pft = pftlist.getobj();
- Patchpft& patchpft = patch.pft[pft.id];
- Gridcellpft& gridcellpft = gridcell.pft[pft.id];
- if (patch.stand.pft[pft.id].active && pft.phenology == CROPGREEN) {
- cropphen_struct& ppftcrop = *(patchpft.get_cropphen());
- if (date.day == climate.testday_temp) {
- if (patch.stand.pft[pft.id].irrigated) {
- patchpft.swindow[0] = gridcellpft.swindow_irr[0];
- patchpft.swindow[1] = gridcellpft.swindow_irr[1];
- }
- else {
- patchpft.swindow[0] = gridcellpft.swindow[0];
- patchpft.swindow[1] = gridcellpft.swindow[1];
- }
- }
- if (patch.stand.pftid == pft.id && !ppftcrop.growingseason) {
- // copy sowing window from gridcellpft
- if (date.day == stepfromdate(ppftcrop.hdate, 1) && ppftcrop.hdate != -1 || date.day == climate.testday_temp) {
- ppftcrop.hdate = -1;
- if(ppftcrop.intercropseason)
- ppftcrop.bicdate = -1;
- int thisyear = gridcell.getsimulationyear(date.year);
- if (gridcellpft.swindow[0] == -1 && thisyear) {
- gridcellpft.sowing_restriction = true;
- ppftcrop.hdate = -1;
- ppftcrop.eicdate = -1; //redundant
- patch.stand.isrotationday = true;
- }
- else {
- if (!patch.stand.infallow)
- gridcellpft.sowing_restriction = false;
- else if (patch.stand.ndays_inrotation > 180)
- patch.stand.isrotationday = true;
- }
- }
- if (!gridcellpft.sowing_restriction) {
- // new sowing date method (Waha et al. 2010)
- crop_sowing_date(patch, pft);
- }
- }
- // set eicdate (last intercrop day)
- if (ppftcrop.sdate != -1) {
- if (!ppftcrop.growingseason)
- ppftcrop.eicdate = stepfromdate(ppftcrop.sdate, - 15);
- if (dayinperiod(date.day, ppftcrop.eicdate, ppftcrop.sdate)) {
- if (ppftcrop.intercropseason)
- ppftcrop.eicdate = date.day;
- else if (ppftcrop.bicdate == ppftcrop.sdate)
- ppftcrop.bicdate = -1;
- }
- }
- }
- pftlist.nextobj();
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////
- // REFERENCES
- //
- // Bondeau A, Smith PC, Zaehle S, Schaphoff S, Lucht W, Cramer W, Gerten D, Lotze-Campen H,
- // Müller C, Reichstein M & Smith B 2007. Modelling the role of agriculture for the
- // 20th century global terrestrial carbon balance. Global Change Biology, 13:679-706.
- // Waha K, van Bussel LGJ, Müller C, and Bondeau A.2012. Climate-driven simulation of global
- // crop sowing dates, Global Ecol Biogeogr 21:247-259
|