26 #include "pism/external/calcalcs/calcalcs.h"
32 #include "pism/util/io/File.hh"
33 #include "pism/util/io/io_helpers.hh"
34 #include "pism/util/Logger.hh"
40 const std::string &time_name,
41 const std::string &default_value,
47 if (not time_units.empty()) {
49 size_t position = time_units.find(
"since");
51 if (position != std::string::npos) {
52 return string_strip(time_units.substr(position + strlen(
"since")));
58 "%s:units = \"%s\" in '%s' does not contain a reference date",
63 }
else if (stop_on_error) {
65 "the '%s' variable in '%s' has no units",
66 time_name.c_str(), file.
filename().c_str());
68 }
else if (stop_on_error) {
70 "'%s' variable is not present in '%s'.",
71 time_name.c_str(), file.
filename().c_str());
79 const std::string &time_name,
80 const std::string &default_value,
86 if (not calendar_name.empty()) {
92 "the '%s' variable in '%s' has no calendar attribute",
93 time_name.c_str(), file.
filename().c_str());
96 }
else if (stop_on_error) {
98 "'%s' variable is not present in '%s'.",
99 time_name.c_str(), file.
filename().c_str());
102 return default_value;
112 auto default_reference_date = config.
get_string(
"time.reference_date");
114 if (input_file !=
nullptr) {
117 auto time = config.
get_string(
"time.dimension_name");
119 if (not config.
get_flag(
"input.bootstrap")) {
121 bool stop_on_error =
true;
126 bool stop_on_error =
false;
129 if (ref_date != default_reference_date) {
131 "WARNING: Using reference date %s\n"
132 " instead of the one present in the input file '%s' (%s)\n",
133 default_reference_date.c_str(), input_file->
filename().c_str(), ref_date.c_str());
139 return default_reference_date;
148 auto default_calendar = config.
get_string(
"time.calendar");
150 if (input_file !=
nullptr) {
153 auto time = config.
get_string(
"time.dimension_name");
155 if (not config.
get_flag(
"input.bootstrap")) {
157 bool stop_on_error =
true;
162 bool stop_on_error =
false;
167 "WARNING: Using calendar %s\n"
168 " instead of the one present in the input file '%s' (%s)\n",
169 default_calendar.c_str(), input_file->
filename().c_str(),
calendar.c_str());
172 return default_calendar;
175 return default_calendar;
183 double T,
double years) {
185 double whole_years_double = std::floor(years);
189 "time offset of %f years does not fit in an 'int'",
193 int whole_years =
static_cast<int>(whole_years_double);
194 double year_fraction = years - whole_years;
195 const double day_length = 86400.0;
201 date.
year += whole_years;
208 int errcode =
ccs_isleap(cal, date.year, &leap);
217 if (leap == 0 and date.month == 2 and date.day == 29) {
222 result += day_length;
227 int year_length = 360;
229 year_length = (leap == 1) ? 366 : 365;
232 result += year_fraction * (year_length * day_length);
258 "got an empty date specification");
263 bool year_is_negative = (spec[0] ==
'-');
265 if (year_is_negative and not
member(
calendar, {
"365_day",
"360_day",
"noleap"})) {
267 "negative dates such as '%s' are not allowed with the '%s' calendar.\n"
268 "Please submit a bug report if this is a problem.",
272 auto parts =
split(spec,
'-');
274 if (parts.size() == 3) {
275 std::vector<int> numbers;
276 for (
const auto &p : parts) {
283 "%ld does not fit in an 'int'",
287 numbers.push_back(
static_cast<int>(
n));
295 if (year_is_negative) {
304 "calendar string '%s' is invalid",
309 int errcode =
ccs_date2jday(cal, numbers[0], numbers[1], numbers[2], &dummy);
313 "date %s is invalid in the %s calendar",
328 double t = strtod(spec.c_str(), &endptr);
329 if (*endptr ==
'\0') {
354 auto time_start = config.
get_string(
"time.start");
356 if (not time_start.empty()) {
360 if (file ==
nullptr) {
366 auto time_name = config.
get_string(
"time.dimension_name");
367 bool stop_on_error =
false;
373 "calendar in '%s' (%s) does not match the selected calendar (%s)",
379 "reference date in '%s' (%s) does not match the selected date (%s)",
387 time_axis[
"units"] = time_units.
format();
389 std::vector<double> time{};
402 auto time_end = config.
get_string(
"time.end");
404 if (not time_end.empty()) {
410 auto run_length = config.
get_number(
"time.run_length",
"seconds");
411 return time_start + run_length;
434 "unsupported calendar: %s", calendar_string.c_str());
440 if (calendar_string ==
"360_day") {
442 }
else if (
member(calendar_string, {
"365_day",
"noleap"})) {
505 const double sperd = 86400.0;
510 if (spec.find(
',') != std::string::npos) {
521 std::vector<double> result;
524 for (
const auto &s :
split(spec,
',')) {
528 e.
add_context(
"parsing a list of dates %s", spec.c_str());
544 if (spec ==
"hourly") {
545 return {3600.0, SIMPLE};
548 if (spec ==
"daily") {
549 return {86400.0, SIMPLE};
552 if (spec ==
"monthly") {
553 return {1.0, MONTHLY};
556 if (spec ==
"yearly") {
557 return {1.0, YEARLY};
560 if (not m_simple_calendar) {
561 if (spec.find(
"year") != std::string::npos or spec.find(
"month") != std::string::npos) {
563 "interval length '%s' with the calendar '%s' is not supported",
564 spec.c_str(), m_calendar_string.c_str());
580 return {c(1.0), SIMPLE};
588 return {years_to_seconds(c(1.0)), SIMPLE};
591 e.
add_context(
"processing interval length " + spec);
596 "interval length '%s' is invalid", spec.c_str());
607 if (spec ==
"hourly") {
609 }
else if (spec ==
"daily") {
611 }
else if (spec ==
"monthly") {
613 }
else if (spec ==
"yearly") {
619 if (parts.size() == 1) {
622 }
else if (parts.size() == 3) {
628 "a time range must consist of exactly 3 parts separated by colons (got '%s').",
633 std::vector<double> result;
650 std::vector<double> &result)
const {
651 if (time_start >= time_end) {
653 this->
date(time_start).c_str(), this->
date(time_end).c_str());
661 double t = time_start;
665 if (t >= this->
start() && t <= this->
end()) {
669 t = time_start +
k * delta;
670 }
while (t <= time_end);
691 m_unit_system(unit_system),
692 m_time_units(unit_system,
"seconds since 1-1-1") {
694 m_t_eps = config->get_number(
"time_stepping.resolution",
"seconds");
696 auto input_file = config->get_string(
"input.file");
698 std::unique_ptr<File> file{};
699 if (not input_file.empty()) {
734 "Negative run length. Start: %s, end: %s.",
740 auto time_file = config->get_string(
"time.file");
741 bool continue_run = config->get_flag(
"time.file.continue");
743 if (not time_file.empty()) {
745 "* Setting time from '%s'...\n",
763 const std::string &filename,
765 bool set_start_time) {
767 std::string time_name =
m_config->get_string(
"time.dimension_name");
773 if (not new_calendar.empty()) {
779 bool stop_on_error =
true;
782 "irrelevant (not used)",
788 std::vector<double> time;
790 if (not time_bounds_name.empty()) {
805 if (set_start_time) {
807 this->
set(time.front());
809 log.
message(2,
"* Using start time from an -i file to continue an interrupted run.\n");
813 e.
add_context(
"initializing model time from \"%s\"", filename.c_str());
829 return (T - year_start) / (next_year_start - year_start);
835 double hour =
date.hour +
date.minute / 60.0 +
date.second / 3600.0;
874 result.push_back(time);
904 result.push_back(time);
913 std::vector<double> &result)
const {
914 switch (interval.
type) {
933 double forcing_start,
934 double forcing_end) {
936 double run_start = time.
start();
937 double run_end = time.
end();
939 if (not (run_start >= forcing_start and
940 run_end <= forcing_end)) {
942 "A time-dependent forcing has to span the whole length of the simulation\n"
943 " Run time: [%s, %s]\n"
944 " Forcing data: [%s, %s]",
945 time.
date(run_start).c_str(),
946 time.
date(run_end).c_str(),
947 time.
date(forcing_start).c_str(),
948 time.
date(forcing_end).c_str());
calcalcs_cal * ccs_init_calendar(const char *calname)
char * ccs_err_str(int error_code)
int ccs_isleap(calcalcs_cal *calendar, int year, int *leap)
void ccs_free_calendar(calcalcs_cal *cc)
int ccs_date2jday(calcalcs_cal *calendar, int year, int month, int day, int *jday)
double get_number(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
std::shared_ptr< const Config > ConstPtr
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.
VariableLookupData find_variable(const std::string &short_name, const std::string &std_name) const
Find a variable using its standard name and/or short name.
std::string filename() const
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.
const Config::ConstPtr m_config
std::string units_string() const
Internal time units as a string.
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...
double m_year_length
number of seconds in a year, for "mod" and "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.
void compute_times(double time_start, double time_end, const Interval &interval, std::vector< double > &result) const
std::vector< double > parse_range(const std::string &spec) const
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 passed since the last beginning of a year. Only useful in codes with a...
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
Time(MPI_Comm com, Config::ConstPtr config, const Logger &log, units::System::Ptr unit_system)
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
DateTime date(double T, const std::string &calendar) const
std::string format() const
System::Ptr system() const
double time(const DateTime &d, const std::string &calendar) const
#define PISM_ERROR_LOCATION
void read_timeseries(const File &file, const VariableMetadata &metadata, const Logger &log, std::vector< double > &data)
Read a time-series variable from a NetCDF file to a vector of doubles.
void read_time_bounds(const File &file, const VariableMetadata &metadata, const Logger &log, std::vector< double > &data)
static double D2(double u_x, double u_y, double u_z, double v_x, double v_y, double v_z)
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)
double max(const IceModelVec2S &input)
Finds maximum over all the values in an IceModelVec2S object. Ignores ghosts.
static double start_time(const Config &config, const Logger &log, const File *file, const std::string &reference_date, const std::string &calendar, const units::Unit &time_units)
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)
bool member(const std::string &string, const std::set< std::string > &set)
double min(const IceModelVec2S &input)
Finds minimum over all the values in an IceModelVec2S object. Ignores ghosts.
@ PISM_READONLY
open an existing file for reading only
static double parse_date(const std::string &input, const units::Unit &time_units, const std::string &calendar)
long int parse_integer(const std::string &input)
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)
std::vector< std::string > split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a vector of strings.