#include #include #include #include #include #include #include #include "config.h" #include "outputter.h" #include "grid_cell.h" using namespace std; using namespace glm; /******************************************************************************/ void Outputter::init (string baseFileName, int startYear, double lat[], double lon[], Config& config) { now_ = 0; startYear_ = startYear; index_[0] = 0; index_[1] = 0; index_[2] = 0; count_[0] = NT; count_[1] = NY; count_[2] = NX; stateFile_ = new OutputFile(baseFileName+"states.nc", config); flowsFile_ = new OutputFile(baseFileName+"transitions.nc", config); mngmtFile_ = new OutputFile(baseFileName+"management.nc", config); diagnFile_ = new OutputFile(baseFileName+"diagnostics.nc", config); initVars(config); stateFile_->addLatLon(lat, lon); flowsFile_->addLatLon(lat, lon); mngmtFile_->addLatLon(lat, lon); diagnFile_->addLatLon(lat, lon); } /******************************************************************************/ Outputter::OutputFile::OutputFile(string fileName, Config& config) { int rv; if ((rv = nc_create(fileName.c_str(), NC_NETCDF4 | NC_CLASSIC_MODEL, &fileID_))) { NCERR(fileName.c_str(), rv); } if ((rv = nc_def_dim(fileID_, "time", NC_UNLIMITED, &timeDimID_))) { NCERR(fileName.c_str(), rv); } if ((rv = nc_def_dim(fileID_, "lat", NY, &latDimID_))) { NCERR(fileName.c_str(), rv); } if ((rv = nc_def_dim(fileID_, "lon", NX, &lonDimID_))) { NCERR(fileName.c_str(), rv); } // Time if ((rv = nc_def_var(fileID_, "time", NC_DOUBLE, 1, &timeDimID_, &timeVarID_))) { NCERR("time", rv); } if (!config.futureRun){ if ((rv = nc_put_att_text(fileID_, timeVarID_, "units", strlen("years since 850-01-01 0:0:0"), "years since 850-01-01 0:0:0"))) { NCERR("time", rv); } } else { if ((rv = nc_put_att_text(fileID_, timeVarID_, "units", strlen("years since 2015-01-01 0:0:0"), "years since 2015-01-01 0:0:0"))) { NCERR("time", rv); } } if ((rv = nc_put_att_text(fileID_, timeVarID_, "calendar", strlen("noleap"), "noleap"))) { NCERR("time", rv); } if ((rv = nc_put_att_text(fileID_, timeVarID_, "realtopology", strlen("linear"), "linear"))) { NCERR("time", rv); } if ((rv = nc_put_att_text(fileID_, timeVarID_, "long_name", strlen("time"), "time"))) { NCERR("time", rv); } if ((rv = nc_put_att_text(fileID_, timeVarID_, "standard_name", strlen("time"), "time"))) { NCERR("time", rv); } if ((rv = nc_put_att_text(fileID_, timeVarID_, "axis", strlen("T"), "T"))) { NCERR("time", rv); } // Latitude if ((rv = nc_def_var(fileID_, "lat", NC_DOUBLE, 1, &latDimID_, &latVarID_))) { NCERR("lat", rv); } if ((rv = nc_put_att_text(fileID_, latVarID_, "realtopology", strlen("linear"), "linear"))) { NCERR("lat", rv); } if ((rv = nc_put_att_text(fileID_, latVarID_, "units", strlen("degrees_north"), "degrees_north"))) { NCERR("lat", rv); } if ((rv = nc_put_att_text(fileID_, latVarID_, "long_name", strlen("latitude"), "latitude"))) { NCERR("lat", rv); } if ((rv = nc_put_att_text(fileID_, latVarID_, "standard_name", strlen("latitude"), "latitude"))) { NCERR("lat", rv); } if ((rv = nc_put_att_text(fileID_, latVarID_, "axis", strlen("Y"), "Y"))) { NCERR("lat", rv); } // Longitude if ((rv = nc_def_var(fileID_, "lon", NC_DOUBLE, 1, &lonDimID_, &lonVarID_))) { NCERR("lon", rv); } if ((rv = nc_put_att_text(fileID_, lonVarID_, "realtopology", strlen("circular"), "circular"))) { NCERR("lon", rv); } if ((rv = nc_put_att_text(fileID_, lonVarID_, "topology", strlen("circular"), "circular"))) { NCERR("lon", rv); } double modulo_val = (double)360.; if ((rv = nc_put_att_double(fileID_, lonVarID_, "modulo", NC_DOUBLE, 1, &modulo_val))) { NCERR("lon", rv); } if ((rv = nc_put_att_text(fileID_, lonVarID_, "units", strlen("degrees_east"), "degrees_east"))) { NCERR("lon", rv); } if ((rv = nc_put_att_text(fileID_, lonVarID_, "long_name", strlen("longitude"), "longitude"))) { NCERR("lon", rv); } if ((rv = nc_put_att_text(fileID_, lonVarID_, "standard_name", strlen("longitude"), "longitude"))) { NCERR("lon", rv); } if ((rv = nc_put_att_text(fileID_, lonVarID_, "axis", strlen("X"), "X"))) { NCERR("lon", rv); } // Global attributes std::time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); std::stringstream str_time; str_time << std::put_time(std::localtime(&t), "%FT%TZ"); if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "host", strlen("UMD College Park"), "UMD College Park"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "comment", strlen("LUH2"), "LUH2"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "contact", strlen("gchurtt@umd.edu, lchini@umd.edu, steve.frolking@unh.edu, ritvik@umd.edu"), "gchurtt@umd.edu, lchini@umd.edu, steve.frolking@unh.edu, ritvik@umd.edu"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "creation_date", strlen(str_time.str().c_str()), str_time.str().c_str()))) { NCERR("Global", rv); } if (!config.futureRun){ if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "title", strlen("UofMD LUH2h dataset prepared for input4MIPs"), "UofMD LUH2h dataset prepared for input4MIPs"))) { NCERR("Global", rv); } } else{ if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "title", strlen("UofMD LUH2f dataset prepared for input4MIPs"), "UofMD LUH2f dataset prepared for input4MIPs"))) { NCERR("Global", rv); } } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "activity_id", strlen("input4MIPs"), "input4MIPs"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "Conventions", strlen("CF-1.6"), "CF-1.6"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "data_structure", strlen("grid"), "grid"))) { NCERR("Global", rv); } if (!config.futureRun){ if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "source", strlen("LUH2 v2.1h: Land-Use Harmonization Data Set"), "LUH2 v2.1h: Land-Use Harmonization Data Set"))) { NCERR("Global", rv); } } else{ if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "source", strlen("LUH2 v2.1f: Land-Use Harmonization Data Set"), "LUH2 v2.1f: Land-Use Harmonization Data Set"))) { NCERR("Global", rv); } } if (!config.futureRun){ if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "dataset_version_number", strlen("2.1h"), "2.1h"))) { NCERR("Global", rv); } } else { if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "dataset_version_number", strlen("2.1f"), "2.1f"))) { NCERR("Global", rv); } } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "dataset_category", strlen("landState"), "landState"))) { NCERR("Global", rv); } if (!config.futureRun){ if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "source_id", strlen("UofMD-landState-2-1-h"), "UofMD-landState-2-1-h"))) { NCERR("Global", rv); } } else { if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "source_id", strlen("UofMD-landState-2-1-f"), "UofMD-landState-2-1-f"))) { NCERR("Global", rv); } } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "variable_id", strlen("multiple"), "multiple"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "target_mip", strlen("CMIP"), "CMIP"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "grid_label", strlen("gn"), "gn"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "mip_era", strlen("CMIP6"), "CMIP6"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "license", strlen("Land-Use Harmonization data produced by the University of Maryland is licensed under a Creative Commons Attribution \\\"Share Alike\\\" 4.0 International License (http://creativecommons.org/licenses/by/4.0/). The data producers and data providers make no warranty, either express or implied, including but not limited to, warranties of merchantability and fitness for a particular purpose. All liabilities arising from the supply of the information (including any liability arising in negligence) are excluded to the fullest extent permitted by law."), "Land-Use Harmonization data produced by the University of Maryland is licensed under a Creative Commons Attribution \\\"Share Alike\\\" 4.0 International License (http://creativecommons.org/licenses/by/4.0/). The data producers and data providers make no warranty, either express or implied, including but not limited to, warranties of merchantability and fitness for a particular purpose. All liabilities arising from the supply of the information (including any liability arising in negligence) are excluded to the fullest extent permitted by law."))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "further_info_url", strlen("http://luh.umd.edu"), "http://luh.umd.edu"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "frequency", strlen("yr"), "yr"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "institution_id", strlen("UofMD"), "UofMD"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "institution", strlen("University of Maryland (UofMD), College Park, MD 20742, USA"), "University of Maryland (UofMD), College Park, MD 20742, USA"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "realm", strlen("land"), "land"))) { NCERR("Global", rv); } if ((rv = nc_put_att_text(fileID_, NC_GLOBAL, "references", strlen("Hurtt, Chini et al. 2011"), "Hurtt, Chini et al. 2011"))) { NCERR("Global", rv); } } /******************************************************************************/ void Outputter::OutputFile::addLatLon(double lat[], double lon[]) { int rv; if ((rv = nc_enddef(fileID_))) { NCERR("enddef", rv); } if ((rv = nc_put_var_double(fileID_, latVarID_, &lat[0]))) { NCERR("lat", rv); } if ((rv = nc_put_var_double(fileID_, lonVarID_, &lon[0]))) { NCERR("lon", rv); } } /******************************************************************************/ void Outputter::OutputFile::addYear(size_t index, int year) { // Add a single year into open netCDF dataset int rv; size_t loc[1] = { index }; if ((rv = nc_put_var1_int(fileID_, timeDimID_, &loc[0], &year))) { NCERR("lat", rv); } } /******************************************************************************/ void Outputter::initVars(Config& config) { for (size_t lu=PRIMF; lu void Outputter::VarTemplateBase::createFileVar (OutputFile* file) { int rv; int dims[3] = { file->timeDimID_, file->latDimID_, file->lonDimID_ }; if (typeid(T) == typeid(float)) { float fill = (float)fill_; if ((rv = nc_def_var(file->fileID_, name_.c_str(), NC_FLOAT, 3, dims, &varID_))) { NCERR(name_.c_str(), rv); } if ((rv = nc_put_att_float(file->fileID_, varID_, "missing_value", NC_FLOAT, 1, &fill))) { NCERR(name_.c_str(), rv); } if ((rv = nc_put_att_float(file->fileID_, varID_, "_Fillvalue", NC_FLOAT, 1, &fill))) { NCERR(name_.c_str(), rv); } } else if (typeid(T) == typeid(int)) { int fill = (int)fill_; if ((rv = nc_def_var(file->fileID_, name_.c_str(), NC_INT, 3, dims, &varID_))) { NCERR(name_.c_str(), rv); } if ((rv = nc_put_att_int(file->fileID_, varID_, "missing_value", NC_INT, 1, &fill))) { NCERR(name_.c_str(), rv); } if ((rv = nc_put_att_int(file->fileID_, varID_, "_Fillvalue", NC_INT, 1, &fill))) { NCERR(name_.c_str(), rv); } } else if (typeid(T) == typeid(double)) { double fill = (double)fill_; if ((rv = nc_def_var(file->fileID_, name_.c_str(), NC_DOUBLE, 3, dims, &varID_))) { NCERR(name_.c_str(), rv); } if ((rv = nc_put_att_double(file->fileID_, varID_, "missing_value", NC_DOUBLE, 1, &fill))) { NCERR(name_.c_str(), rv); } if ((rv = nc_put_att_double(file->fileID_, varID_, "_Fillvalue", NC_DOUBLE, 1, &fill))) { NCERR(name_.c_str(), rv); } } else { cerr << "unimplemented datatype for output var " << name_ << endl; exit(1); } if ((rv = nc_put_att_text(file->fileID_, varID_, "standard_name", standardName_.length(), standardName_.c_str()))) { NCERR(standardName_.c_str(), rv); } if ((rv = nc_put_att_text(file->fileID_, varID_, "long_name", longName_.length(), longName_.c_str()))) { NCERR(longName_.c_str(), rv); } if (!units_.empty()) { if ((rv = nc_put_att_text(file->fileID_, varID_, "units", units_.length(), units_.c_str()))) { NCERR(name_.c_str(), rv); } } if ((rv = nc_put_att_text(fileID_, varID_, "cell_methods", strlen("time:mean"), "time:mean"))) { NCERR("Global", rv); } nc_def_var_deflate(file->fileID_, varID_, 0, 1, 1); } /******************************************************************************/ void Outputter::updateOutputs (GridCell& gc, size_t gcNow) { vector::iterator v; for (v=registeredVars_.begin(); v!=registeredVars_.end(); ++v) { (*v)->updateVal(now_, gc, gcNow); } } /******************************************************************************/ void Outputter::flush (bool writeFlows) { vector::iterator v; // Add a single year into open netCDF dataset stateFile_->addYear(index_[0], startYear_ + index_[0]); if (writeFlows) { flowsFile_->addYear(index_[0], startYear_ + index_[0]); } mngmtFile_->addYear(index_[0], startYear_ + index_[0]); diagnFile_->addYear(index_[0], startYear_ + index_[0]); for (v=registeredVars_.begin(); v!=registeredVars_.end(); ++v) { // don't write flows if we don't want them (ie final year) if (writeFlows || flowsFile_->fileID_ != (*v)->fileID_) { (*v)->flush(index_, count_); } } // Synchronize the disk copy of a netCDF dataset with in-memory buffers. // From: https://www.unidata.ucar.edu/software/netcdf/netcdf-4/newdocs/netcdf-c/nc_005fsync.html // There are two reasons you might want to synchronize after writes: // To minimize data loss in case of abnormal termination, or // to make data available to other processes for reading immediately after it is written. stateFile_->sync(); flowsFile_->sync(); mngmtFile_->sync(); diagnFile_->sync(); index_[0]++; }