24 #include "pism/util/Time.hh"
26 #include "pism/external/calcalcs/calcalcs.h"
28 #include "pism/util/ConfigInterface.hh"
29 #include "pism/util/VariableMetadata.hh"
30 #include "pism/util/pism_utilities.hh"
31 #include "pism/util/error_handling.hh"
32 #include "pism/util/io/File.hh"
33 #include "pism/util/io/io_helpers.hh"
34 #include "pism/util/Logger.hh"
35 #include "pism/util/io/IO_Flags.hh"
41 const std::string &time_name,
42 const std::string &default_value,
48 if (not time_units.empty()) {
50 size_t position = time_units.find(
"since");
52 if (position != std::string::npos) {
53 return string_strip(time_units.substr(position + strlen(
"since")));
59 "%s:units = \"%s\" in '%s' does not contain a reference date",
64 }
else if (stop_on_error) {
66 "the '%s' variable in '%s' has no units",
67 time_name.c_str(), file.
filename().c_str());
69 }
else if (stop_on_error) {
71 "'%s' variable is not present in '%s'.",
72 time_name.c_str(), file.
filename().c_str());
80 const std::string &time_name,
81 const std::string &default_value,
87 if (not calendar_name.empty()) {
93 "the '%s' variable in '%s' has no calendar attribute",
94 time_name.c_str(), file.
filename().c_str());
97 }
else if (stop_on_error) {
99 "'%s' variable is not present in '%s'.",
100 time_name.c_str(), file.
filename().c_str());
103 return default_value;
113 auto default_reference_date = config.
get_string(
"time.reference_date");
115 if (input_file !=
nullptr) {
118 auto time = config.
get_string(
"time.dimension_name");
120 if (not config.
get_flag(
"input.bootstrap")) {
122 bool stop_on_error =
true;
127 bool stop_on_error =
false;
130 if (ref_date != default_reference_date) {
132 "WARNING: Using reference date %s\n"
133 " instead of the one present in the input file '%s' (%s)\n",
134 default_reference_date.c_str(), input_file->
filename().c_str(), ref_date.c_str());
140 return default_reference_date;
149 auto default_calendar = config.
get_string(
"time.calendar");
151 if (input_file !=
nullptr) {
154 auto time = config.
get_string(
"time.dimension_name");
156 if (not config.
get_flag(
"input.bootstrap")) {
158 bool stop_on_error =
true;
163 bool stop_on_error =
false;
168 "WARNING: Using calendar %s\n"
169 " instead of the one present in the input file '%s' (%s)\n",
170 default_calendar.c_str(), input_file->
filename().c_str(),
calendar.c_str());
173 return default_calendar;
176 return default_calendar;
184 double T,
double years) {
186 double whole_years_double = std::floor(years);
190 "time offset of %f years does not fit in an 'int'",
194 int whole_years =
static_cast<int>(whole_years_double);
195 double year_fraction = years - whole_years;
196 const double day_length = 86400.0;
202 date.
year += whole_years;
209 int errcode =
ccs_isleap(cal, date.year, &leap);
218 if (leap == 0 and date.month == 2 and date.day == 29) {
223 result += day_length;
228 int year_length = 360;
230 year_length = (leap == 1) ? 366 : 365;
233 result += year_fraction * (year_length * day_length);
259 "got an empty date specification");
264 bool year_is_negative = (spec[0] ==
'-');
266 if (year_is_negative and not
member(
calendar, {
"365_day",
"360_day",
"noleap"})) {
268 "negative dates such as '%s' are not allowed with the '%s' calendar.\n"
269 "Please submit a bug report if this is a problem.",
273 auto parts =
split(spec,
'-');
275 if (parts.size() == 3) {
276 std::vector<int> numbers;
277 for (
const auto &p : parts) {
284 "%ld does not fit in an 'int'",
288 numbers.push_back(
static_cast<int>(
n));
296 if (year_is_negative) {
305 "calendar string '%s' is invalid",
310 int errcode =
ccs_date2jday(cal, numbers[0], numbers[1], numbers[2], &dummy);
314 "date %s is invalid in the %s calendar",
329 double t = strtod(spec.c_str(), &endptr);
330 if (*endptr ==
'\0') {
355 auto time_start = config.
get_string(
"time.start");
357 if (not time_start.empty()) {
361 if (file ==
nullptr) {
367 auto time_name = config.
get_string(
"time.dimension_name");
368 bool stop_on_error =
false;
374 "calendar in '%s' (%s) does not match the selected calendar (%s)",
380 "reference date in '%s' (%s) does not match the selected date (%s)",
388 time_axis[
"units"] = time_units.
format();
390 std::vector<double> time{};
403 auto time_end = config.
get_string(
"time.end");
405 if (not time_end.empty()) {
411 auto run_length = config.
get_number(
"time.run_length",
"seconds");
412 return time_start + run_length;
435 "unsupported calendar: %s", calendar_string.c_str());
441 if (calendar_string ==
"360_day") {
443 }
else if (
member(calendar_string, {
"365_day",
"noleap"})) {
506 const double sperd = 86400.0;
511 if (spec.find(
',') != std::string::npos) {
522 std::vector<double> result;
525 for (
const auto &s :
split(spec,
',')) {
529 e.
add_context(
"parsing a list of dates %s", spec.c_str());
545 if (spec ==
"hourly") {
546 return {3600.0, SIMPLE};
549 if (spec ==
"daily") {
550 return {86400.0, SIMPLE};
553 if (spec ==
"monthly") {
554 return {1.0, MONTHLY};
557 if (spec ==
"yearly") {
558 return {1.0, YEARLY};
561 if (not m_simple_calendar) {
562 if (spec.find(
"year") != std::string::npos or spec.find(
"month") != std::string::npos) {
564 "interval length '%s' with the calendar '%s' is not supported",
565 spec.c_str(), m_calendar_string.c_str());
581 return {c(1.0), SIMPLE};
589 return {years_to_seconds(c(1.0)), SIMPLE};
592 e.
add_context(
"processing interval length " + spec);
597 "interval length '%s' is invalid", spec.c_str());
608 if (spec ==
"hourly") {
610 }
else if (spec ==
"daily") {
612 }
else if (spec ==
"monthly") {
614 }
else if (spec ==
"yearly") {
620 if (parts.size() == 1) {
623 }
else if (parts.size() == 3) {
629 "a time range must consist of exactly 3 parts separated by colons (got '%s').",
634 std::vector<double> result;
651 std::vector<double> &result)
const {
652 if (time_start >= time_end) {
654 this->
date(time_start).c_str(), this->
date(time_end).c_str());
662 double t = time_start;
666 if (t >= this->
start() && t <= this->
end()) {
670 t = time_start +
k * delta;
671 }
while (t <= time_end);
693 m_unit_system(unit_system),
694 m_time_units(unit_system,
"seconds since 1-1-1") {
696 m_t_eps = config->get_number(
"time_stepping.resolution",
"seconds");
698 auto input_file = config->get_string(
"input.file");
700 std::unique_ptr<File> file{};
701 if (not input_file.empty()) {
736 "Negative run length. Start: %s, end: %s.",
742 auto time_file = config->get_string(
"time.file");
743 bool continue_run = config->get_flag(
"time.file.continue");
745 if (not time_file.empty()) {
747 "* Setting time from '%s'...\n",
765 const std::string &filename,
767 bool set_start_time) {
769 std::string time_name =
m_config->get_string(
"time.dimension_name");
775 if (not new_calendar.empty()) {
781 bool stop_on_error =
true;
784 "irrelevant (not used)",
790 std::vector<double> time;
792 if (not time_bounds_name.empty()) {
807 if (set_start_time) {
809 this->
set(time.front());
811 log.
message(2,
"* Using start time from an -i file to continue an interrupted run.\n");
815 e.
add_context(
"initializing model time from \"%s\"", filename.c_str());
831 return (T - year_start) / (next_year_start - year_start);
837 double hour =
date.hour +
date.minute / 60.0 +
date.second / 3600.0;
876 result.push_back(time);
906 result.push_back(time);
915 std::vector<double> &result)
const {
916 switch (interval.
type) {
935 double forcing_start,
936 double forcing_end) {
938 double run_start = time.
start();
939 double run_end = time.
end();
941 if (not (run_start >= forcing_start and
942 run_end <= forcing_end)) {
944 "A time-dependent forcing has to span the whole length of the simulation\n"
945 " Run time: [%s, %s]\n"
946 " Forcing data: [%s, %s]",
947 time.
date(run_start).c_str(),
948 time.
date(run_end).c_str(),
949 time.
date(forcing_start).c_str(),
950 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 "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 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
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
double max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
double min(const array::Scalar &input)
Finds minimum over all the values in an array::Scalar object. Ignores ghosts.
@ PISM_READONLY
open an existing file for reading only
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)
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)
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.