25#include "pism/util/Time.hh"
27#include "pism/external/calcalcs/calcalcs.h"
29#include "pism/util/Config.hh"
30#include "pism/util/Logger.hh"
31#include "pism/util/Units.hh"
32#include "pism/util/VariableMetadata.hh"
33#include "pism/util/error_handling.hh"
34#include "pism/util/io/File.hh"
35#include "pism/util/io/IO_Flags.hh"
36#include "pism/util/io/io_helpers.hh"
37#include "pism/util/pism_utilities.hh"
43 size_t position = units.find(
"since");
45 if (position != std::string::npos) {
46 return string_strip(units.substr(position + strlen(
"since")));
57 const std::string &time_name,
58 const std::string &default_value,
64 if (time_units.empty()) {
67 time_name.c_str(), file.
name().c_str());
75 e.
add_context(
"extracting reference date from \"%s\" in \"%s\"", time_name.c_str(),
84 time_name.c_str(), file.
name().c_str());
92 const std::string &time_name,
93 const std::string &default_value,
99 if (not calendar_name.empty()) {
100 return calendar_name;
105 "the '%s' variable in '%s' has no calendar attribute",
106 time_name.c_str(), file.
name().c_str());
109 }
else if (stop_on_error) {
111 "'%s' variable is not present in '%s'.",
112 time_name.c_str(), file.
name().c_str());
115 return default_value;
125 auto default_reference_date = config.
get_string(
"time.reference_date");
127 if (input_file !=
nullptr) {
130 auto time = config.
get_string(
"time.dimension_name");
132 if (not config.
get_flag(
"input.bootstrap")) {
134 bool stop_on_error =
true;
139 bool stop_on_error =
false;
142 if (ref_date != default_reference_date) {
144 "WARNING: Using reference date %s\n"
145 " instead of the one present in the input file '%s' (%s)\n",
146 default_reference_date.c_str(), input_file->
name().c_str(), ref_date.c_str());
152 return default_reference_date;
161 auto default_calendar = config.
get_string(
"time.calendar");
163 if (input_file !=
nullptr) {
166 auto time = config.
get_string(
"time.dimension_name");
168 if (not config.
get_flag(
"input.bootstrap")) {
170 bool stop_on_error =
true;
175 bool stop_on_error =
false;
180 "WARNING: Using calendar %s\n"
181 " instead of the one present in the input file '%s' (%s)\n",
182 default_calendar.c_str(), input_file->
name().c_str(),
calendar.c_str());
185 return default_calendar;
188 return default_calendar;
196 double T,
double years) {
198 double whole_years_double = std::floor(years);
199 if (whole_years_double > std::numeric_limits<int>::max() or
200 whole_years_double < std::numeric_limits<int>::min()) {
202 "time offset of %f years does not fit in an 'int'",
206 int whole_years =
static_cast<int>(whole_years_double);
207 double year_fraction = years - whole_years;
208 const double day_length = 86400.0;
214 date.
year += whole_years;
221 int errcode =
ccs_isleap(cal, date.year, &leap);
230 if (leap == 0 and date.month == 2 and date.day == 29) {
233 result = time_units.
time(date);
235 result += day_length;
237 result = time_units.
time(date);
240 int year_length = 360;
242 year_length = (leap == 1) ? 366 : 365;
245 result += year_fraction * (year_length * day_length);
271 "got an empty date specification: '%s'",
275 if (
split(spec,
'-').size() == 3) {
284 double t = strtod(spec.c_str(), &endptr);
285 if (*endptr ==
'\0') {
309 auto time_start = config.
get_string(
"time.start");
311 if (not time_start.empty()) {
315 if (file ==
nullptr) {
321 auto time_name = config.
get_string(
"time.dimension_name");
322 bool stop_on_error =
false;
328 "calendar in '%s' (%s) does not match the selected calendar (%s)",
329 file->
name().c_str(), file_calendar.c_str(),
calendar.c_str());
334 "reference date in '%s' (%s) does not match the selected date (%s)",
353 auto time_end = config.
get_string(
"time.end");
355 if (not time_end.empty()) {
361 auto run_length = config.
get_number(
"time.run_length",
"seconds");
362 return time_start + run_length;
385 "unsupported calendar: %s", calendar_string.c_str());
390 double seconds_per_day = convert(
m_unit_system, 1.0,
"day",
"seconds");
391 if (calendar_string ==
"360_day") {
393 }
else if (
set_member(calendar_string, {
"365_day",
"noleap"})) {
435 result[
"axis"] =
"T";
440 result[
"bounds"] = bounds_name;
480 const double sperd = 86400.0;
485 if (spec.find(
',') != std::string::npos) {
496 std::vector<double> result;
499 for (
const auto &s :
split(spec,
',')) {
503 e.
add_context(
"parsing a list of dates %s", spec.c_str());
519 if (spec ==
"hourly") {
520 return {3600.0, SIMPLE};
523 if (spec ==
"daily") {
524 return {86400.0, SIMPLE};
527 if (spec ==
"monthly") {
528 return {1.0, MONTHLY};
531 if (spec ==
"yearly") {
532 return {1.0, YEARLY};
535 if (not m_simple_calendar) {
536 if (spec.find(
"year") != std::string::npos or spec.find(
"month") != std::string::npos) {
538 "interval length '%s' with the calendar '%s' is not supported",
539 spec.c_str(), m_calendar_string.c_str());
555 return {c(1.0), SIMPLE};
563 return {years_to_seconds(c(1.0)), SIMPLE};
566 e.
add_context(
"processing interval length " + spec);
571 "interval length '%s' is invalid", spec.c_str());
582 if (spec ==
"hourly") {
584 }
else if (spec ==
"daily") {
586 }
else if (spec ==
"monthly") {
588 }
else if (spec ==
"yearly") {
594 if (parts.size() == 1) {
597 }
else if (parts.size() == 3) {
603 "a time range must consist of exactly 3 parts separated by colons (got '%s').",
608 std::vector<double> result;
625 std::vector<double> &result)
const {
626 if (time_start >= time_end) {
628 this->
date(time_start).c_str(), this->
date(time_end).c_str());
636 double t = time_start;
640 if (t >= this->
start() && t <= this->
end()) {
644 t = time_start +
k * delta;
645 }
while (t <= time_end);
663 std::shared_ptr<const Config> config,
667 m_unit_system(unit_system),
668 m_time_units(unit_system,
"seconds since 1-1-1") {
672 m_t_eps = config->get_number(
"time_stepping.resolution",
"seconds");
674 auto input_file = config->get_string(
"input.file");
676 std::unique_ptr<File> file{};
677 if (not input_file.empty()) {
711 "Negative run length. Start: %s, end: %s.",
717 auto time_file = config->get_string(
"time.file");
718 bool continue_run = config->get_flag(
"time.file.continue");
720 if (not time_file.empty()) {
722 "* Setting time from '%s'...\n",
740 const std::string &filename,
742 bool set_start_time) {
744 std::string time_name =
m_config->get_string(
"time.dimension_name");
750 if (not new_calendar.empty()) {
756 bool stop_on_error =
true;
759 "irrelevant (not used)",
765 std::vector<double> time;
767 if (not time_bounds_name.empty()) {
776 if (set_start_time) {
778 this->
set(time.front());
780 log.
message(2,
"* Using start time from an -i file to continue an interrupted run.\n");
784 e.
add_context(
"initializing model time from \"%s\"", filename.c_str());
800 return (T - year_start) / (next_year_start - year_start);
845 result.push_back(time);
875 result.push_back(time);
884 std::vector<double> &result)
const {
885 switch (interval.
type) {
904 double forcing_start,
905 double forcing_end) {
907 double run_start = time.
start();
908 double run_end = time.
end();
910 if (not (run_start >= forcing_start and
911 run_end <= forcing_end)) {
913 "A time-dependent forcing has to span the whole length of the simulation\n"
914 " Run time: [%s, %s]\n"
915 " Forcing data: [%s, %s]",
916 time.
date(run_start).c_str(),
917 time.
date(run_end).c_str(),
918 time.
date(forcing_start).c_str(),
919 time.
date(forcing_end).c_str());
char * ccs_err_str(int error_code)
int ccs_isleap(calcalcs_cal *calendar, int year, int *leap)
void ccs_free_calendar(calcalcs_cal *cc)
calcalcs_cal * ccs_init_calendar(const char *calname)
double get_number(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
std::string get_string(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
bool get_flag(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
A class for storing and accessing PISM configuration flags and parameters.
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
std::string read_text_attribute(const std::string &var_name, const std::string &att_name) const
Get a text attribute.
High-level PISM I/O class.
void message(int threshold, const char format[],...) const __attribute__((format(printf
Print a message to the log.
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
double increment_date(double T, double years) const
std::vector< double > parse_list(const std::string &spec) const
std::string run_length() const
Returns the length of the current run, in years.
void compute_times_monthly(std::vector< double > &result) const
double years_to_seconds(double input) const
void set(double new_time)
Sets the current time (in seconds since the reference time).
double convert_time_interval(double T, const std::string &units) const
Convert time interval from seconds to given units. Handle 'years' using the year length corresponding...
std::shared_ptr< const Config > m_config
double m_year_length
number of seconds in a year, for "year fraction"
void set_end(double new_end)
void compute_times_simple(double time_start, double delta, double time_end, std::vector< double > &result) const
void init_calendar(const std::string &calendar)
double calendar_year_start(double T) const
Returns the model time in seconds corresponding to the beginning of the year T falls into.
std::string m_variable_name
void compute_times(double time_start, double time_end, const Interval &interval, std::vector< double > &result) const
VariableMetadata metadata(bool with_bounds=false) const
Metadata of the NetCDF variable for writing to an output file.
std::vector< double > parse_range(const std::string &spec) const
VariableMetadata bounds_metadata() const
Metadata of the NetCDF variable containing time bounds.
Time(MPI_Comm com, std::shared_ptr< const Config > config, const Logger &log, units::System::Ptr unit_system)
double m_run_end
run end tim, in seconds since the reference time
double year_fraction(double T) const
Returns the fraction of a year that passed since the last beginning of a year. Only useful in codes w...
double m_time_in_seconds
current time, in seconds since the reference time
Interval parse_interval_length(const std::string &spec) const
std::string m_calendar_string
CF calendar string.
double seconds_to_years(double input) const
double current() const
Current time, in seconds.
units::Unit units() const
std::vector< double > parse_times(const std::string &spec) const
std::string variable_name() const
Name of the NetCDF variable to use.
void compute_times_yearly(std::vector< double > &result) const
double day_of_the_year_to_year_fraction(unsigned int day) const
Convert the day number to the year fraction.
void step(double delta_t)
Advance by delta_t seconds.
void set_start(double new_start)
std::string date(double T) const
Returns the date corresponding to time T.
double m_run_start
run start time, in seconds since the reference time
const units::System::Ptr m_unit_system
std::string calendar() const
Returns the calendar string.
void init_from_file(MPI_Comm com, const std::string &filename, const Logger &log, bool set_start_time)
Sets the time from a NetCDF file with a time dimension (-time_file).
double m_t_eps
Time resolution, in seconds.
std::shared_ptr< System > Ptr
double time(const DateTime &d) const
DateTime date(double T, const std::string &calendar) const
System::Ptr system() const
#define PISM_ERROR_LOCATION
@ PISM_READONLY
open an existing file for reading only
std::vector< double > read_1d_variable(const File &file, const std::string &variable_name, const std::string &units, std::shared_ptr< units::System > unit_system)
std::vector< double > read_bounds(const File &file, const std::string &bounds_variable_name, const std::string &internal_units, std::shared_ptr< units::System > unit_system)
bool are_convertible(const Unit &u1, const Unit &u2)
double convert(System::Ptr system, double input, const std::string &spec1, const std::string &spec2)
Convert a quantity from unit1 to unit2.
static double increment_date(const units::Unit &time_units, const std::string &calendar, double T, double years)
static double parse_time_spec(const std::string &input, const units::Unit &time_units, const std::string &calendar)
std::string printf(const char *format,...)
static double end_time(const Config &config, double time_start, const std::string &calendar, const units::Unit &time_units)
std::string string_strip(const std::string &input)
void check_forcing_duration(const Time &time, double forcing_start, double forcing_end)
static std::string calendar_from_file(const File &file, const std::string &time_name, const std::string &default_value, bool stop_on_error)
Get the calendar name from a file.
static std::string reference_date(const File *input_file, const Config &config, const Logger &log)
static std::string calendar(const File *input_file, const Config &config, const Logger &log)
static std::string extract_reference_date(const std::string &units, bool stop_on_error)
static double start_time(const Config &config, const File *file, const std::string &reference_date, const std::string &calendar, const units::Unit &time_units)
static std::string reference_date_from_file(const File &file, const std::string &time_name, const std::string &default_value, bool stop_on_error)
Get the reference date from a file.
bool pism_is_valid_calendar_name(const std::string &name)
bool set_member(const std::string &string, const std::set< std::string > &set)
std::vector< std::string > split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a vector of strings.