cfinput.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  1. ///////////////////////////////////////////////////////////////////////////////////////
  2. /// \file cfinput.cpp
  3. /// \brief Input module for CF conforming NetCDF files
  4. ///
  5. /// \author Joe Siltberg
  6. /// $Date$
  7. ///
  8. ///////////////////////////////////////////////////////////////////////////////////////
  9. #include "config.h"
  10. #include "cfinput.h"
  11. #ifdef HAVE_NETCDF
  12. #include "guess.h"
  13. #include "driver.h"
  14. #include "guessstring.h"
  15. #include <fstream>
  16. #include <sstream>
  17. #include <algorithm>
  18. REGISTER_INPUT_MODULE("cf", CFInput)
  19. using namespace GuessNC::CF;
  20. namespace {
  21. const int SECONDS_PER_DAY = 24*60*60;
  22. // Converts a CF standard name to one of our insolation types
  23. // Calls fail() if the standard name is invalid
  24. insoltype cf_standard_name_to_insoltype(const std::string& standard_name) {
  25. if (standard_name == "surface_downwelling_shortwave_flux_in_air" ||
  26. standard_name == "surface_downwelling_shortwave_flux") {
  27. return SWRAD_TS;
  28. }
  29. else if (standard_name == "surface_net_downward_shortwave_flux") {
  30. return NETSWRAD_TS;
  31. }
  32. else if (standard_name == "cloud_area_fraction") {
  33. return SUNSHINE;
  34. }
  35. else {
  36. fail("Unknown insolation type: %s", standard_name.c_str());
  37. return SUNSHINE; // To avoid compiler warning
  38. }
  39. }
  40. // Gives the maximum allowed value for insolation, given an insolation type
  41. // Used as an upper limit when interpolating from monthly to daily values
  42. double max_insolation(insoltype instype) {
  43. if (instype == SUNSHINE) {
  44. return 100;
  45. }
  46. else {
  47. return std::numeric_limits<double>::max();
  48. }
  49. }
  50. // Checks if a DateTime is at the first day of the year
  51. bool first_day_of_year(GuessNC::CF::DateTime dt) {
  52. return dt.get_month() == 1 && dt.get_day() == 1;
  53. }
  54. // Checks if a DateTime is in January
  55. bool first_month_of_year(GuessNC::CF::DateTime dt) {
  56. return dt.get_month() == 1;
  57. }
  58. // Compares a Date with a GuessNC::CF::DateTime to see if the Date is on an earlier day
  59. bool earlier_day(const Date& date, int calendar_year,
  60. const GuessNC::CF::DateTime& date_time) {
  61. std::vector<int> d1(3),d2(3);
  62. d1[0] = calendar_year;
  63. d2[0] = date_time.get_year();
  64. d1[1] = date.month+1;
  65. d2[1] = date_time.get_month();
  66. d1[2] = date.dayofmonth+1;
  67. d2[2] = date_time.get_day();
  68. return d1 < d2;
  69. }
  70. // Compares a Date with a GuessNC::CF::DateTime to see if the Date is on a later day
  71. // The date object must know about its calendar years (i.e. set_first_calendar_year must
  72. // have been called)
  73. bool later_day(const Date& date,
  74. const GuessNC::CF::DateTime& date_time) {
  75. std::vector<int> d1(3),d2(3);
  76. d1[0] = date.get_calendar_year();
  77. d2[0] = date_time.get_year();
  78. d1[1] = date.month+1;
  79. d2[1] = date_time.get_month();
  80. d1[2] = date.dayofmonth+1;
  81. d2[2] = date_time.get_day();
  82. return d1 > d2;
  83. }
  84. // Checks if the variable contains daily data
  85. bool is_daily(const GuessNC::CF::GridcellOrderedVariable* cf_var) {
  86. // Check if first and second timestep is one day apart
  87. DateTime dt1 = cf_var->get_date_time(0);
  88. DateTime dt2 = cf_var->get_date_time(1);
  89. dt1.add_time(1, GuessNC::CF::DAYS, cf_var->get_calendar_type());
  90. return dt1 == dt2;
  91. }
  92. // Returns a DateTime in the last day for which the variable has data.
  93. // For daily data, this is simply the day of the last timestep, for monthly data
  94. // we need to find the last day of the last timestep's month.
  95. GuessNC::CF::DateTime last_day_to_simulate(const GuessNC::CF::GridcellOrderedVariable* cf_var) {
  96. GuessNC::CF::DateTime last = cf_var->get_date_time(cf_var->get_timesteps()-1);
  97. if (is_daily(cf_var)) {
  98. return last;
  99. }
  100. else {
  101. // Not daily, assume monthly.
  102. GuessNC::CF::DateTime prev = last;
  103. GuessNC::CF::DateTime next = last;
  104. do {
  105. prev = next;
  106. next.add_time(1, GuessNC::CF::DAYS, cf_var->get_calendar_type());
  107. } while (next.get_month() == last.get_month());
  108. return prev;
  109. }
  110. }
  111. // Verifies that a CF variable with air temperature data contains what we expect
  112. void check_temp_variable(const GuessNC::CF::GridcellOrderedVariable* cf_var) {
  113. if (cf_var->get_standard_name() != "air_temperature") {
  114. fail("Temperature variable doesn't seem to contain air temperature data");
  115. }
  116. if (cf_var->get_units() != "K") {
  117. fail("Temperature variable doesn't seem to be in Kelvin");
  118. }
  119. }
  120. // Verifies that a CF variable with precipitation data contains what we expect
  121. void check_prec_variable(const GuessNC::CF::GridcellOrderedVariable* cf_var) {
  122. if (cf_var->get_standard_name() == "precipitation_flux") {
  123. if (cf_var->get_units() != "kg m-2 s-1") {
  124. fail("Precipitation is given as flux but does not have the correct unit (kg m-2 s-1)");
  125. }
  126. }
  127. else if (cf_var->get_standard_name() == "precipitation_amount") {
  128. if (cf_var->get_units() != "kg m-2") {
  129. fail("Precipitation is given as amount but does not have the correct unit (kg m-2)");
  130. }
  131. }
  132. else {
  133. fail("Unrecognized precipitation type");
  134. }
  135. }
  136. // Verifies that a CF variable with insolation data contains what we expect
  137. void check_insol_variable(const GuessNC::CF::GridcellOrderedVariable* cf_var) {
  138. if (cf_var->get_standard_name() != "surface_downwelling_shortwave_flux_in_air" &&
  139. cf_var->get_standard_name() != "surface_downwelling_shortwave_flux" &&
  140. cf_var->get_standard_name() != "surface_net_downward_shortwave_flux" &&
  141. cf_var->get_standard_name() != "cloud_area_fraction") {
  142. fail("Insolation variable doesn't seem to contain insolation data");
  143. }
  144. if (cf_var->get_standard_name() == "cloud_area_fraction") {
  145. if (cf_var->get_units() != "1") {
  146. fail("Unrecognized unit for cloud cover");
  147. }
  148. }
  149. else {
  150. if (cf_var->get_units() != "W m-2") {
  151. fail("Insolation variable given as radiation but unit doesn't seem to be in W m-2");
  152. }
  153. }
  154. }
  155. // Verifies that a CF variable with wetdays data contains what we expect
  156. void check_wetdays_variable(const GuessNC::CF::GridcellOrderedVariable* cf_var) {
  157. const char* wetdays_standard_name =
  158. "number_of_days_with_lwe_thickness_of_precipitation_amount_above_threshold";
  159. if (cf_var && cf_var->get_standard_name() != wetdays_standard_name) {
  160. fail("Wetdays variable should have standard name %s", wetdays_standard_name);
  161. }
  162. }
  163. // Checks if two variables contain data for the same time period
  164. //
  165. // Compares start and end of time series, the day numbers are only compared if
  166. // both variables are daily.
  167. void check_compatible_timeseries(const GuessNC::CF::GridcellOrderedVariable* var1,
  168. const GuessNC::CF::GridcellOrderedVariable* var2) {
  169. GuessNC::CF::DateTime start1, start2, end1, end2;
  170. const std::string error_message = format_string("%s and %s have incompatible timeseries",
  171. var1->get_variable_name().c_str(), var2->get_variable_name().c_str());
  172. start1 = var1->get_date_time(0);
  173. start2 = var2->get_date_time(0);
  174. end1 = var1->get_date_time(var1->get_timesteps() - 1);
  175. end2 = var2->get_date_time(var2->get_timesteps() - 1);
  176. if (start1.get_year() != start2.get_year() ||
  177. start1.get_month() != start2.get_month()) {
  178. fail(error_message.c_str());
  179. }
  180. if (end1.get_year() != end2.get_year() ||
  181. end1.get_month() != end2.get_month()) {
  182. fail(error_message.c_str());
  183. }
  184. if (is_daily(var1) && is_daily(var2)) {
  185. if (start1.get_day() != start2.get_day() ||
  186. end1.get_day() != end2.get_day()) {
  187. fail(error_message.c_str());
  188. }
  189. }
  190. }
  191. // Makes sure all variables have compatible time series
  192. void check_compatible_timeseries(const std::vector<GuessNC::CF::GridcellOrderedVariable*> variables) {
  193. for (size_t i = 0; i < variables.size(); ++i) {
  194. for (size_t j = i + 1; j < variables.size(); ++j) {
  195. check_compatible_timeseries(variables[i], variables[j]);
  196. }
  197. }
  198. }
  199. void check_same_spatial_domains(const std::vector<GuessNC::CF::GridcellOrderedVariable*> variables) {
  200. for (size_t i = 1; i < variables.size(); ++i) {
  201. if (!variables[0]->same_spatial_domain(*variables[i])) {
  202. fail("%s and %s don't have the same spatial domain",
  203. variables[0]->get_variable_name().c_str(),
  204. variables[i]->get_variable_name().c_str());
  205. }
  206. }
  207. }
  208. }
  209. CFInput::CFInput()
  210. : cf_temp(0),
  211. cf_prec(0),
  212. cf_insol(0),
  213. cf_wetdays(0),
  214. cf_min_temp(0),
  215. cf_max_temp(0),
  216. ndep_timeseries("historic") {
  217. declare_parameter("ndep_timeseries", &ndep_timeseries, 10, "Nitrogen deposition time series to use (historic, rcp26, rcp45, rcp60 or rcp85");
  218. }
  219. CFInput::~CFInput() {
  220. delete cf_temp;
  221. delete cf_prec;
  222. delete cf_insol;
  223. delete cf_wetdays;
  224. delete cf_min_temp;
  225. delete cf_max_temp;
  226. cf_temp = 0;
  227. cf_prec = 0;
  228. cf_insol = 0;
  229. cf_wetdays = 0;
  230. cf_min_temp = 0;
  231. cf_max_temp = 0;
  232. }
  233. void CFInput::init() {
  234. // Read CO2 data from file
  235. co2.load_file(param["file_co2"].str);
  236. file_cru = param["file_cru"].str;
  237. // Try to open the NetCDF files
  238. try {
  239. cf_temp = new GridcellOrderedVariable(param["file_temp"].str, param["variable_temp"].str);
  240. cf_prec = new GridcellOrderedVariable(param["file_prec"].str, param["variable_prec"].str);
  241. cf_insol = new GridcellOrderedVariable(param["file_insol"].str, param["variable_insol"].str);
  242. if (param["file_wetdays"].str != "") {
  243. cf_wetdays = new GridcellOrderedVariable(param["file_wetdays"].str, param["variable_wetdays"].str);
  244. }
  245. if (param["file_min_temp"].str != "") {
  246. cf_min_temp = new GridcellOrderedVariable(param["file_min_temp"].str, param["variable_min_temp"].str);
  247. }
  248. if (param["file_max_temp"].str != "") {
  249. cf_max_temp = new GridcellOrderedVariable(param["file_max_temp"].str, param["variable_max_temp"].str);
  250. }
  251. }
  252. catch (const std::runtime_error& e) {
  253. fail(e.what());
  254. }
  255. // Make sure they contain what we expect
  256. check_temp_variable(cf_temp);
  257. check_prec_variable(cf_prec);
  258. check_insol_variable(cf_insol);
  259. check_wetdays_variable(cf_wetdays);
  260. if (cf_min_temp) {
  261. check_temp_variable(cf_min_temp);
  262. }
  263. if (cf_max_temp) {
  264. check_temp_variable(cf_max_temp);
  265. }
  266. check_compatible_timeseries(all_variables());
  267. check_same_spatial_domains(all_variables());
  268. extensive_precipitation = cf_prec->get_standard_name() == "precipitation_amount";
  269. // Read list of localities and store in gridlist member variable
  270. // Retrieve name of grid list file as read from ins file
  271. xtring file_gridlist=param["file_gridlist_cf"].str;
  272. std::ifstream ifs(file_gridlist, std::ifstream::in);
  273. if (!ifs.good()) fail("CFInput::init: could not open %s for input",(char*)file_gridlist);
  274. std::string line;
  275. while (getline(ifs, line)) {
  276. // Read next record in file
  277. int rlat, rlon;
  278. int landid;
  279. std::string descrip;
  280. Coord c;
  281. std::istringstream iss(line);
  282. if (cf_temp->is_reduced()) {
  283. if (iss >> landid) {
  284. getline(iss, descrip);
  285. c.landid = landid;
  286. }
  287. }
  288. else {
  289. if (iss >> rlon >> rlat) {
  290. getline(iss, descrip);
  291. c.rlat = rlat;
  292. c.rlon = rlon;
  293. }
  294. }
  295. c.descrip = trim(descrip);
  296. gridlist.push_back(c);
  297. }
  298. current_gridcell = gridlist.begin();
  299. // Open landcover files
  300. landcover_input.init();
  301. // Open management files
  302. management_input.init();
  303. date.set_first_calendar_year(cf_temp->get_date_time(0).get_year() - nyear_spinup);
  304. // Set timers
  305. tprogress.init();
  306. tmute.init();
  307. tprogress.settimer();
  308. tmute.settimer(MUTESEC);
  309. }
  310. bool CFInput::getgridcell(Gridcell& gridcell) {
  311. double lon, lat;
  312. double cru_lon, cru_lat;
  313. int soilcode;
  314. // Load data for next gridcell, or if that fails, skip ahead until
  315. // we find one that works.
  316. while (current_gridcell != gridlist.end() &&
  317. !load_data_from_files(lon, lat, cru_lon, cru_lat, soilcode)) {
  318. ++current_gridcell;
  319. }
  320. if (current_gridcell == gridlist.end()) {
  321. // simulation finished
  322. return false;
  323. }
  324. if (run_landcover) {
  325. bool LUerror = false;
  326. LUerror = landcover_input.loadlandcover(lon, lat);
  327. if (!LUerror)
  328. LUerror = management_input.loadmanagement(lon, lat);
  329. if (LUerror) {
  330. dprintf("\nError: could not find stand at (%g,%g) in landcover/management data file(s)\n", lon, lat);
  331. return false;
  332. }
  333. }
  334. gridcell.set_coordinates(lon, lat);
  335. // Load spinup data for all variables
  336. load_spinup_data(cf_temp, spinup_temp);
  337. load_spinup_data(cf_prec, spinup_prec);
  338. load_spinup_data(cf_insol, spinup_insol);
  339. if (cf_wetdays) {
  340. load_spinup_data(cf_wetdays, spinup_wetdays);
  341. }
  342. if (cf_min_temp) {
  343. load_spinup_data(cf_min_temp, spinup_min_temp);
  344. }
  345. if (cf_max_temp) {
  346. load_spinup_data(cf_max_temp, spinup_max_temp);
  347. }
  348. spinup_temp.detrend_data();
  349. gridcell.climate.instype = cf_standard_name_to_insoltype(cf_insol->get_standard_name());
  350. // Get nitrogen deposition, using the found CRU coordinates
  351. ndep.getndep(param["file_ndep"].str, cru_lon, cru_lat,
  352. Lamarque::parse_timeseries(ndep_timeseries));
  353. // Setup the soil type
  354. soilparameters(gridcell.soiltype, soilcode);
  355. historic_timestep_temp = -1;
  356. historic_timestep_prec = -1;
  357. historic_timestep_insol = -1;
  358. historic_timestep_wetdays = -1;
  359. historic_timestep_min_temp = -1;
  360. historic_timestep_max_temp = -1;
  361. dprintf("\nCommencing simulation for gridcell at (%g,%g)\n", lon, lat);
  362. if (current_gridcell->descrip != "") {
  363. dprintf("Description: %s\n", current_gridcell->descrip.c_str());
  364. }
  365. dprintf("Using soil code and Nitrogen deposition for (%3.1f,%3.1f)\n", cru_lon, cru_lat);
  366. return true;
  367. }
  368. bool CFInput::load_data_from_files(double& lon, double& lat,
  369. double& cru_lon, double& cru_lat,
  370. int& soilcode) {
  371. int rlon = current_gridcell->rlon;
  372. int rlat = current_gridcell->rlat;
  373. int landid = current_gridcell->landid;
  374. // Try to load the data from the NetCDF files
  375. if (cf_temp->is_reduced()) {
  376. if (!cf_temp->load_data_for(landid) ||
  377. !cf_prec->load_data_for(landid) ||
  378. !cf_insol->load_data_for(landid) ||
  379. (cf_wetdays && !cf_wetdays->load_data_for(landid)) ||
  380. (cf_min_temp && !cf_min_temp->load_data_for(landid)) ||
  381. (cf_max_temp && !cf_max_temp->load_data_for(landid))) {
  382. dprintf("Failed to load data for (%d) from NetCDF files, skipping.\n", landid);
  383. return false;
  384. }
  385. }
  386. else {
  387. if (!cf_temp->load_data_for(rlon, rlat) ||
  388. !cf_prec->load_data_for(rlon, rlat) ||
  389. !cf_insol->load_data_for(rlon, rlat) ||
  390. (cf_wetdays && !cf_wetdays->load_data_for(rlon, rlat)) ||
  391. (cf_min_temp && !cf_min_temp->load_data_for(rlon, rlat)) ||
  392. (cf_max_temp && !cf_max_temp->load_data_for(rlon, rlat))) {
  393. dprintf("Failed to load data for (%d, %d) from NetCDF files, skipping.\n", rlon, rlat);
  394. return false;
  395. }
  396. }
  397. // Get lon/lat for the gridcell
  398. if (cf_temp->is_reduced()) {
  399. cf_temp->get_coords_for(landid, lon, lat);
  400. }
  401. else {
  402. cf_temp->get_coords_for(rlon, rlat, lon, lat);
  403. }
  404. // Find nearest CRU grid cell in order to get the soilcode
  405. cru_lon = lon;
  406. cru_lat = lat;
  407. double dummy[CRU_TS30::NYEAR_HIST][12];
  408. const double searchradius = 1;
  409. if (!CRU_TS30::findnearestCRUdata(searchradius, file_cru, cru_lon, cru_lat, soilcode,
  410. dummy, dummy, dummy)) {
  411. dprintf("Failed to find soil code from CRU archive, close to coordinates (%g,%g), skipping.\n",
  412. cru_lon, cru_lat);
  413. return false;
  414. }
  415. return true;
  416. }
  417. void CFInput::get_yearly_data(std::vector<double>& data,
  418. const GenericSpinupData& spinup,
  419. GridcellOrderedVariable* cf_historic,
  420. int& historic_timestep) {
  421. // Extract all values for this year, for one variable,
  422. // either from spinup dataset or historical dataset
  423. int calendar_year = date.get_calendar_year();
  424. if (is_daily(cf_historic)) {
  425. data.resize(date.year_length());
  426. // This function is called at the first day of the year, so current_day
  427. // starts at Jan 1, then we step through the whole year, getting data
  428. // either from spinup or historical period.
  429. Date current_day = date;
  430. while (current_day.year == date.year) {
  431. // In the spinup?
  432. if (earlier_day(current_day, calendar_year, cf_historic->get_date_time(0))) {
  433. int spinup_day = current_day.day;
  434. // spinup object always has 365 days, deal with leap years
  435. if (current_day.ndaymonth[1] == 29 && current_day.month > 1) {
  436. --spinup_day;
  437. }
  438. data[current_day.day] = spinup[spinup_day];
  439. }
  440. else {
  441. // Historical period
  442. if (historic_timestep + 1 < cf_historic->get_timesteps()) {
  443. ++historic_timestep;
  444. GuessNC::CF::DateTime dt = cf_historic->get_date_time(historic_timestep);
  445. // Deal with calendar mismatch
  446. // Leap day in NetCDF variable but not in LPJ-GUESS?
  447. if (dt.get_month() == 2 && dt.get_day() == 29 &&
  448. current_day.ndaymonth[1] == 28) {
  449. ++historic_timestep;
  450. }
  451. // Leap day in LPJ-GUESS but not in NetCDF variable?
  452. else if (current_day.month == 1 && current_day.dayofmonth == 28 &&
  453. cf_historic->get_calendar_type() == NO_LEAP) {
  454. --historic_timestep;
  455. }
  456. }
  457. if (historic_timestep < cf_historic->get_timesteps()) {
  458. data[current_day.day] = cf_historic->get_value(max(0, historic_timestep));
  459. }
  460. else {
  461. // Past the end of the historical period, these days wont be simulated.
  462. data[current_day.day] = data[max(0, current_day.day-1)];
  463. }
  464. }
  465. current_day.next();
  466. }
  467. }
  468. else {
  469. // for now, assume that data set must be monthly since it isn't daily
  470. data.resize(12);
  471. for (int m = 0; m < 12; ++m) {
  472. GuessNC::CF::DateTime first_date = cf_historic->get_date_time(0);
  473. // In the spinup?
  474. if (calendar_year < first_date.get_year() ||
  475. (calendar_year == first_date.get_year() &&
  476. m+1 < first_date.get_month())) {
  477. data[m] = spinup[m];
  478. }
  479. else {
  480. // Historical period
  481. if (historic_timestep + 1 < cf_historic->get_timesteps()) {
  482. ++historic_timestep;
  483. }
  484. if (historic_timestep < cf_historic->get_timesteps()) {
  485. data[m] = cf_historic->get_value(historic_timestep);
  486. }
  487. else {
  488. // Past the end of the historical period, these months wont be simulated.
  489. data[m] = data[max(0, m-1)];
  490. }
  491. }
  492. }
  493. }
  494. }
  495. void CFInput::populate_daily_array(double* daily,
  496. const GenericSpinupData& spinup,
  497. GridcellOrderedVariable* cf_historic,
  498. int& historic_timestep,
  499. double minimum,
  500. double maximum) {
  501. // Get the data from spinup and/or historic
  502. std::vector<double> data;
  503. get_yearly_data(data, spinup, cf_historic, historic_timestep);
  504. if (is_daily(cf_historic)) {
  505. // Simply copy from data to daily
  506. std::copy(data.begin(), data.end(), daily);
  507. }
  508. else {
  509. // for now, assume that data set must be monthly since it isn't daily
  510. // Interpolate from monthly to daily values
  511. interp_monthly_means_conserve(&data.front(), daily, date.is_leap(date.year), minimum, maximum);
  512. }
  513. }
  514. void CFInput::populate_daily_prec_array(long& seed) {
  515. // Get the data from spinup and/or historic
  516. std::vector<double> prec_data;
  517. get_yearly_data(prec_data, spinup_prec, cf_prec, historic_timestep_prec);
  518. std::vector<double> wetdays_data;
  519. if (cf_wetdays) {
  520. get_yearly_data(wetdays_data, spinup_wetdays, cf_wetdays, historic_timestep_wetdays);
  521. }
  522. if (is_daily(cf_prec)) {
  523. // Simply copy from data to daily, and if needed convert from
  524. // precipitation rate to precipitation amount
  525. for (size_t i = 0; i < prec_data.size(); ++i) {
  526. dprec[i] = prec_data[i];
  527. if (!extensive_precipitation) {
  528. dprec[i] *= SECONDS_PER_DAY;
  529. }
  530. }
  531. }
  532. else {
  533. // for now, assume that data set must be monthly since it isn't daily
  534. // If needed convert from precipitation rate to precipitation amount
  535. if (!extensive_precipitation) {
  536. for (int m = 0; m < 12; ++m) {
  537. // TODO: use the dataset's calendar type to figure out number of days in month?
  538. prec_data[m] *= SECONDS_PER_DAY * date.ndaymonth[m];
  539. }
  540. }
  541. if (cf_wetdays) {
  542. prdaily(&prec_data.front(), dprec, &wetdays_data.front(), seed);
  543. }
  544. else {
  545. interp_monthly_totals_conserve(&prec_data.front(), dprec, date.is_leap(date.year), 0);
  546. }
  547. }
  548. }
  549. void CFInput::populate_daily_arrays(long& seed) {
  550. // Extract daily values for all days in this year, either from
  551. // spinup dataset or historical dataset
  552. populate_daily_array(dtemp, spinup_temp, cf_temp, historic_timestep_temp, 0);
  553. populate_daily_prec_array(seed);
  554. populate_daily_array(dinsol, spinup_insol, cf_insol, historic_timestep_insol, 0,
  555. max_insolation(cf_standard_name_to_insoltype(cf_insol->get_standard_name())));
  556. if (cf_min_temp) {
  557. populate_daily_array(dmin_temp, spinup_min_temp, cf_min_temp, historic_timestep_min_temp, 0);
  558. }
  559. if (cf_max_temp) {
  560. populate_daily_array(dmax_temp, spinup_max_temp, cf_max_temp, historic_timestep_max_temp, 0);
  561. }
  562. // Convert to units the model expects
  563. bool cloud_fraction_to_sunshine = (cf_standard_name_to_insoltype(cf_insol->get_standard_name()) == SUNSHINE);
  564. for (int i = 0; i < date.year_length(); ++i) {
  565. dtemp[i] -= K2degC;
  566. if (cf_min_temp) {
  567. dmin_temp[i] -= K2degC;
  568. }
  569. if (cf_max_temp) {
  570. dmax_temp[i] -= K2degC;
  571. }
  572. if (cloud_fraction_to_sunshine) {
  573. // Invert from cloudiness to sunshine,
  574. // and convert fraction (0-1) to percent (0-100)
  575. dinsol[i] = (1-dinsol[i]) * 100.0;
  576. }
  577. }
  578. // Move to next year in spinup dataset
  579. spinup_temp.nextyear();
  580. spinup_prec.nextyear();
  581. spinup_insol.nextyear();
  582. if (cf_wetdays) {
  583. spinup_wetdays.nextyear();
  584. }
  585. if (cf_min_temp) {
  586. spinup_min_temp.nextyear();
  587. }
  588. if (cf_max_temp) {
  589. spinup_max_temp.nextyear();
  590. }
  591. // Get monthly ndep values and convert to daily
  592. double mndrydep[12];
  593. double mnwetdep[12];
  594. ndep.get_one_calendar_year(date.get_calendar_year(),
  595. mndrydep, mnwetdep);
  596. // Distribute N deposition
  597. distribute_ndep(mndrydep, mnwetdep, dprec, date.ndaymonth, dndep);
  598. }
  599. void CFInput::getlandcover(Gridcell& gridcell) {
  600. landcover_input.getlandcover(gridcell);
  601. landcover_input.get_land_transitions(gridcell);
  602. }
  603. bool CFInput::getclimate(Gridcell& gridcell) {
  604. Climate& climate = gridcell.climate;
  605. GuessNC::CF::DateTime last_date = last_day_to_simulate(cf_temp);
  606. if (later_day(date, last_date)) {
  607. ++current_gridcell;
  608. return false;
  609. }
  610. climate.co2 = co2[date.get_calendar_year()];
  611. if (date.day == 0) {
  612. populate_daily_arrays(gridcell.seed);
  613. }
  614. climate.temp = dtemp[date.day];
  615. climate.prec = dprec[date.day];
  616. climate.insol = dinsol[date.day];
  617. // Nitrogen deposition
  618. climate.dndep = dndep[date.day];
  619. // bvoc
  620. if(ifbvoc){
  621. if (cf_min_temp && cf_max_temp) {
  622. climate.dtr = dmax_temp[date.day] - dmin_temp[date.day];
  623. }
  624. else {
  625. fail("When BVOC is switched on, valid paths for minimum and maximum temperature must be given.");
  626. }
  627. }
  628. // First day of year only ...
  629. if (date.day == 0) {
  630. // Progress report to user and update timer
  631. if (tmute.getprogress()>=1.0) {
  632. int first_historic_year = cf_temp->get_date_time(0).get_year();
  633. int last_historic_year = cf_temp->get_date_time(cf_temp->get_timesteps()-1).get_year();
  634. int historic_years = last_historic_year - first_historic_year + 1;
  635. int years_to_simulate = nyear_spinup + historic_years;
  636. int cells_done = distance(gridlist.begin(), current_gridcell);
  637. double progress=(double)(cells_done*years_to_simulate+date.year)/
  638. (double)(gridlist.size()*years_to_simulate);
  639. tprogress.setprogress(progress);
  640. dprintf("%3d%% complete, %s elapsed, %s remaining\n",(int)(progress*100.0),
  641. tprogress.elapsed.str,tprogress.remaining.str);
  642. tmute.settimer(MUTESEC);
  643. }
  644. }
  645. return true;
  646. }
  647. void CFInput::load_spinup_data(const GuessNC::CF::GridcellOrderedVariable* cf_var,
  648. GenericSpinupData& spinup_data) {
  649. const std::string error_message =
  650. format_string("Not enough data to build spinup, at least %d years needed",
  651. NYEAR_SPINUP_DATA);
  652. GenericSpinupData::RawData source;
  653. int timestep = 0;
  654. bool daily = is_daily(cf_var);
  655. // for now, assume that each data set is either daily or monthly
  656. bool monthly = !daily;
  657. // Skip the first year if data doesn't start at the beginning of the year
  658. while ((daily && !first_day_of_year(cf_var->get_date_time(timestep))) ||
  659. (monthly && !first_month_of_year(cf_var->get_date_time(timestep)))) {
  660. ++timestep;
  661. if (timestep >= cf_var->get_timesteps()) {
  662. fail(error_message.c_str());
  663. }
  664. }
  665. // Get all the values for the first NYEAR_SPINUP_DATA years,
  666. // and put them into source
  667. for (int i = 0; i < NYEAR_SPINUP_DATA; ++i) {
  668. std::vector<double> year(daily ? GenericSpinupData::DAYS_PER_YEAR : 12);
  669. for (size_t i = 0; i < year.size(); ++i) {
  670. if (timestep < cf_var->get_timesteps()) {
  671. GuessNC::CF::DateTime dt = cf_var->get_date_time(timestep);
  672. if (daily && dt.get_month() == 2 && dt.get_day() == 29) {
  673. ++timestep;
  674. }
  675. }
  676. if (timestep >= cf_var->get_timesteps()) {
  677. fail(error_message.c_str());
  678. }
  679. year[i] = cf_var->get_value(timestep);
  680. ++timestep;
  681. }
  682. source.push_back(year);
  683. }
  684. spinup_data.get_data_from(source);
  685. }
  686. namespace {
  687. // Help function for call to remove_if below - checks if a pointer is null
  688. bool is_null(const GuessNC::CF::GridcellOrderedVariable* ptr) {
  689. return ptr == 0;
  690. }
  691. }
  692. std::vector<GuessNC::CF::GridcellOrderedVariable*> CFInput::all_variables() const {
  693. std::vector<GuessNC::CF::GridcellOrderedVariable*> result;
  694. result.push_back(cf_temp);
  695. result.push_back(cf_prec);
  696. result.push_back(cf_insol);
  697. result.push_back(cf_wetdays);
  698. result.push_back(cf_min_temp);
  699. result.push_back(cf_max_temp);
  700. // Get rid of null pointers
  701. result.erase(std::remove_if(result.begin(), result.end(), is_null),
  702. result.end());
  703. return result;
  704. }
  705. #endif // HAVE_NETCDF