PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
Time.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2025 Constantine Khroulev
2//
3// This file is part of PISM.
4//
5// PISM is free software; you can redistribute it and/or modify it under the
6// terms of the GNU General Public License as published by the Free Software
7// Foundation; either version 3 of the License, or (at your option) any later
8// version.
9//
10// PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13// details.
14//
15// You should have received a copy of the GNU General Public License
16// along with PISM; if not, write to the Free Software
17// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19#include <cmath>
20#include <cassert> // assert
21#include <cstring> // strlen()
22#include <limits>
23
24#include "pism/util/Time.hh"
25
26#include "pism/external/calcalcs/calcalcs.h"
27
28#include "pism/util/Config.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"
36
37namespace pism {
38
39//! Get the reference date from a file.
40static std::string reference_date_from_file(const File &file,
41 const std::string &time_name,
42 const std::string &default_value,
43 bool stop_on_error) {
44
45 if (file.variable_exists(time_name)) {
46 std::string time_units = file.read_text_attribute(time_name, "units");
47
48 if (not time_units.empty()) {
49 // Check if the time_units includes a reference date.
50 size_t position = time_units.find("since");
51
52 if (position != std::string::npos) {
53 return string_strip(time_units.substr(position + strlen("since")));
54 }
55
56 if (stop_on_error) {
57
59 "%s:units = \"%s\" in '%s' does not contain a reference date",
60 time_name.c_str(),
61 time_units.c_str(),
62 file.name().c_str());
63 }
64 } else if (stop_on_error) {
66 "the '%s' variable in '%s' has no units",
67 time_name.c_str(), file.name().c_str());
68 }
69 } else if (stop_on_error) {
71 "'%s' variable is not present in '%s'.",
72 time_name.c_str(), file.name().c_str());
73 }
74
75 return default_value;
76}
77
78//! Get the calendar name from a file.
79static std::string calendar_from_file(const File &file,
80 const std::string &time_name,
81 const std::string &default_value,
82 bool stop_on_error) {
83
84 if (file.variable_exists(time_name)) {
85 std::string calendar_name = file.read_text_attribute(time_name, "calendar");
86
87 if (not calendar_name.empty()) {
88 return calendar_name;
89 }
90
91 if (stop_on_error) {
93 "the '%s' variable in '%s' has no calendar attribute",
94 time_name.c_str(), file.name().c_str());
95 }
96
97 } else if (stop_on_error) {
99 "'%s' variable is not present in '%s'.",
100 time_name.c_str(), file.name().c_str());
101 }
102
103 return default_value;
104}
105
106/*!
107 * Get the reference date from a file or the configuration parameter.
108 */
109static std::string reference_date(const File *input_file,
110 const Config &config,
111 const Logger &log) {
112
113 auto default_reference_date = config.get_string("time.reference_date");
114
115 if (input_file != nullptr) {
116 // input file is not empty
117
118 auto time = config.get_string("time.dimension_name");
119
120 if (not config.get_flag("input.bootstrap")) {
121 // restarting from a file: use the reference date in this file
122 bool stop_on_error = true;
123 return reference_date_from_file(*input_file, time, default_reference_date, stop_on_error);
124 }
125
126 // Bootstrapping: use the configuration parameter and warn about mismatches
127 bool stop_on_error = false;
128 auto ref_date = reference_date_from_file(*input_file, time, default_reference_date, stop_on_error);
129
130 if (ref_date != default_reference_date) {
131 log.message(2,
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->name().c_str(), ref_date.c_str());
135 }
136
137 return ref_date;
138 }
139
140 return default_reference_date;
141}
142
143/*!
144 * Get the calendar name from a file or a configuration parameter.
145 */
146static std::string calendar(const File *input_file,
147 const Config &config,
148 const Logger &log) {
149 auto default_calendar = config.get_string("time.calendar");
150
151 if (input_file != nullptr) {
152 // input file is not empty
153
154 auto time = config.get_string("time.dimension_name");
155
156 if (not config.get_flag("input.bootstrap")) {
157 // restarting from a file: use the calendar in this file
158 bool stop_on_error = true;
159 return calendar_from_file(*input_file, time, default_calendar, stop_on_error);
160 }
161
162 // Bootstrapping: use the configuration parameter and warn about mismatches
163 bool stop_on_error = false;
164 auto calendar = calendar_from_file(*input_file, time, default_calendar, stop_on_error);
165
166 if (calendar != default_calendar) {
167 log.message(2,
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->name().c_str(), calendar.c_str());
171 }
172
173 return default_calendar;
174 }
175
176 return default_calendar;
177}
178
179/*!
180 * Increment the date corresponding to `T` by `years` years.
181 */
182static double increment_date(const units::Unit &time_units,
183 const std::string &calendar,
184 double T, double years) {
185
186 double whole_years_double = std::floor(years);
187 if (whole_years_double > std::numeric_limits<int>::max() or
188 whole_years_double < std::numeric_limits<int>::min()) {
190 "time offset of %f years does not fit in an 'int'",
191 whole_years_double);
192 }
193
194 int whole_years = static_cast<int>(whole_years_double);
195 double year_fraction = years - whole_years;
196 const double day_length = 86400.0;
197
198 // Get the date corresponding to time T:
199 auto date = time_units.date(T, calendar);
200
201 // shift the date by the number of whole years requested
202 date.year += whole_years;
203
204 // check if the resulting year is a leap year:
205 int leap = 0;
206 {
208 assert(cal != NULL);
209 int errcode = ccs_isleap(cal, date.year, &leap);
210 if (errcode != 0) {
211 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "CalCalcs error: %s",
212 ccs_err_str(errcode));
213 }
215 }
216
217 double result = 0.0;
218 if (leap == 0 and date.month == 2 and date.day == 29) {
219 // avoid passing an impossible date to UDUNITS (no February 29 in non-leap years):
220 date.day -= 1;
221 result = time_units.time(date, calendar);
222 // add back the day we substracted above
223 result += day_length;
224 } else {
225 result = time_units.time(date, calendar);
226 }
227
228 int year_length = 360;
229 if (calendar != "360_day") {
230 year_length = (leap == 1) ? 366 : 365;
231 }
232
233 result += year_fraction * (year_length * day_length);
234
235 return result;
236}
237
238/*!
239 * Parse the date.
240 *
241 * `input` can be
242 *
243 * - a YYYY-MM-DD date (YYYY can be negative)
244 *
245 * - a number (interpreted as the number of years since the reference date in
246 * `time_units`)
247 *
248 * - a number with units attached ("1 day", etc) interpreted as time since the reference
249 * date in `time_units`
250 */
251static double parse_date(const std::string &input,
252 const units::Unit &time_units,
253 const std::string &calendar) {
254
255 std::string spec = string_strip(input);
256
257 if (spec.empty()) {
259 "got an empty date specification: '%s'",
260 input.c_str());
261 }
262
263 // We need to remember if the year was negative in the input string: split() will ignore
264 // empty tokens separated by "-", so "-1000-1-1" will produce ["1000", "1", "1"].
265 bool year_is_negative = (spec[0] == '-');
266
267 if (year_is_negative and not set_member(calendar, {"365_day", "360_day", "noleap"})) {
269 "negative dates such as '%s' are not allowed with the '%s' calendar.\n"
270 "Please submit a bug report if this is a problem.",
271 spec.c_str(), calendar.c_str());
272 }
273
274 auto parts = split(spec, '-');
275
276 if (parts.size() == 3) {
277 std::vector<int> numbers;
278 for (const auto &p : parts) {
279 try {
280 long int n = parse_integer(p);
281
282 if (n > std::numeric_limits<int>::max() or
283 n < std::numeric_limits<int>::min()) {
285 "%ld does not fit in an 'int'",
286 n);
287 }
288
289 numbers.push_back(static_cast<int>(n));
290 } catch (RuntimeError &e) {
291 e.add_context("parsing a date specification %s",
292 spec.c_str());
293 throw;
294 }
295 }
296
297 if (year_is_negative) {
298 numbers[0] *= -1;
299 }
300
301 // Validate the calendar string and the date in this calendar:
302 {
304 if (cal == NULL) {
306 "calendar string '%s' is invalid",
307 calendar.c_str());
308 }
309
310 int dummy = 0;
311 int errcode = ccs_date2jday(cal, numbers[0], numbers[1], numbers[2], &dummy);
312 if (errcode != 0) {
315 "date %s is invalid in the %s calendar",
316 spec.c_str(), calendar.c_str());
317 }
319 }
320
321 units::DateTime d{numbers[0], numbers[1], numbers[2], 0, 0, 0.0};
322
323 return time_units.time(d, calendar);
324 } // end of the block processing dates written as Y-M-D
325
326 // "spec" must be a number or a number with units attached to it
327 try {
328 // check if strtod() can parse it:
329 char *endptr = NULL;
330 double t = strtod(spec.c_str(), &endptr);
331 if (*endptr == '\0') {
332 // strtod() parsed it successfully: assume that it is in years. This will return
333 // time in seconds.
334 return increment_date(time_units, calendar, 0, t);
335 }
336
337 // strtod() failed -- assume that this is a number followed by units compatible
338 // with seconds
339 return units::convert(time_units.system(), 1.0, spec, "seconds");
340 } catch (RuntimeError &e) {
341 e.add_context("parsing the date " + spec);
342 throw;
343 }
344}
345
346/*!
347 * Return the start time.
348 */
349static double start_time(const Config &config,
350 const File *file,
351 const std::string &reference_date,
352 const std::string &calendar,
353 const units::Unit &time_units) {
354
355 auto time_start = config.get_string("time.start");
356
357 if (not time_start.empty()) {
358 return parse_date(time_start, time_units, calendar);
359 }
360
361 if (file == nullptr) {
362 // 0.0 corresponds to the reference date
363 return 0.0;
364 }
365
366 // get the calendar in this file
367 auto time_name = config.get_string("time.dimension_name");
368 bool stop_on_error = false;
369 auto file_calendar = calendar_from_file(*file, time_name, calendar, stop_on_error);
370 auto ref_date = reference_date_from_file(*file, time_name, reference_date, stop_on_error);
371
372 if (file_calendar != calendar) {
374 "calendar in '%s' (%s) does not match the selected calendar (%s)",
375 file->name().c_str(), file_calendar.c_str(), calendar.c_str());
376 }
377
378 if (ref_date != reference_date) {
380 "reference date in '%s' (%s) does not match the selected date (%s)",
381 file->name().c_str(), ref_date.c_str(), reference_date.c_str());
382 }
383
384 // FIXME: it would make sense to get the length of the time dimension and read the last
385 // number instead.
386 if (file->dimension_length(time_name) > 0) {
387 auto time = io::read_1d_variable(*file, time_name, time_units, time_units.system());
388
389 return time.back();
390 }
391
392 return 0.0;
393}
394
395static double end_time(const Config &config,
396 double time_start,
397 const std::string &calendar,
398 const units::Unit &time_units) {
399 auto time_end = config.get_string("time.end");
400
401 if (not time_end.empty()) {
402 // parse use time_end and use it
403 return parse_date(time_end, time_units, calendar);
404 }
405
406 // use time_start and run_length
407 auto run_length = config.get_number("time.run_length", "seconds");
408 return time_start + run_length;
409}
410
411//! Convert model years into seconds using the year length
412//! corresponding to the current calendar.
413/*! Do not use this to convert quantities other than time intervals!
414 */
415double Time::years_to_seconds(double input) const {
416 return input * m_year_length;
417}
418
419//! Convert seconds into model years using the year length
420//! corresponding to the current calendar.
421/*! Do not use this to convert quantities other than time intervals!
422 */
423double Time::seconds_to_years(double input) const {
424 return input / m_year_length;
425}
426
427void Time::init_calendar(const std::string &calendar_string) {
428
429 if (not pism_is_valid_calendar_name(calendar_string)) {
431 "unsupported calendar: %s", calendar_string.c_str());
432 }
433
434 m_calendar_string = calendar_string;
435
436 double seconds_per_day = convert(m_unit_system, 1.0, "day", "seconds");
437 if (calendar_string == "360_day") {
438 m_year_length = 360 * seconds_per_day;
439 } else if (set_member(calendar_string, {"365_day", "noleap"})) {
440 m_year_length = 365 * seconds_per_day;
441 } else {
442 // use the ~365.2524-day year
443 m_year_length = convert(m_unit_system, 1.0, "year", "seconds");
444 }
445}
446
447//! \brief Sets the current time (in seconds since the reference time).
448void Time::set(double new_time) {
449 m_time_in_seconds = new_time;
450}
451
452void Time::set_start(double new_start) {
453 m_run_start = new_start;
454}
455
456void Time::set_end(double new_end) {
457 m_run_end = new_end;
458}
459
460//! \brief Current time, in seconds.
461double Time::current() const {
462 return m_time_in_seconds;
463}
464
465double Time::start() const {
466 return m_run_start;
467}
468
469double Time::end() const {
470 return m_run_end;
471}
472
473std::string Time::variable_name() const {
474 return m_variable_name;
475}
476
477VariableMetadata Time::metadata(bool with_bounds) const {
479 units().system());
480 result.long_name("time").units(units());
481 result["axis"] = "T";
482 result["calendar"] = calendar();
483
484 if (with_bounds) {
485 auto bounds_name = variable_name() + "_bounds";
486 result["bounds"] = bounds_name;
487 }
488
489 return result;
490}
491
493 auto bounds_name = variable_name() + "_bounds";
494 VariableMetadata result(bounds_name, { { "nv", 2 } }, m_unit_system);
495
496 result.units(units()).set_time_dependent(true);
497
498 return result;
499}
500
502 return m_time_units;
503}
504
505//! \brief Returns the calendar string.
506std::string Time::calendar() const {
507 return m_calendar_string;
508}
509
510void Time::step(double delta_t) {
511 m_time_in_seconds += delta_t;
512
513 // If we are less than m_t_eps seconds from the end of the run, reset
514 // m_time_in_seconds to avoid taking a very small (and useless) time step.
518 }
519}
520
521std::string Time::run_length() const {
523}
524
525double Time::day_of_the_year_to_year_fraction(unsigned int day) const {
526 const double sperd = 86400.0;
527 return (sperd / m_year_length) * (double) day;
528}
529
530std::vector<double> Time::parse_times(const std::string &spec) const {
531 if (spec.find(',') != std::string::npos) {
532 // a list will always contain a comma because at least two numbers are
533 // needed to specify reporting intervals
534 return parse_list(spec);
535 }
536
537 // it must be a range specification
538 return parse_range(spec);
539}
540
541std::vector<double> Time::parse_list(const std::string &spec) const {
542 std::vector<double> result;
543
544 try {
545 for (const auto &s : split(spec, ',')) {
546 result.emplace_back(parse_date(s, m_time_units, m_calendar_string));
547 }
548 } catch (RuntimeError &e) {
549 e.add_context("parsing a list of dates %s", spec.c_str());
550 throw;
551 }
552
553 return result;
554}
555
556/**
557 * Parses an interval specification string.
558 *
559 * @param[in] spec specification string
560 *
561 */
562auto Time::parse_interval_length(const std::string &spec) const -> Interval {
563
564 // check if it is a keyword
565 if (spec == "hourly") {
566 return {3600.0, SIMPLE};
567 }
568
569 if (spec == "daily") {
570 return {86400.0, SIMPLE};
571 }
572
573 if (spec == "monthly") {
574 return {1.0, MONTHLY};
575 }
576
577 if (spec == "yearly") {
578 return {1.0, YEARLY};
579 }
580
581 if (not m_simple_calendar) {
582 if (spec.find("year") != std::string::npos or spec.find("month") != std::string::npos) {
584 "interval length '%s' with the calendar '%s' is not supported",
585 spec.c_str(), m_calendar_string.c_str());
586 }
587 }
588
589 try {
590 units::Unit seconds(m_unit_system, "seconds");
591 units::Unit one(m_unit_system, "1");
592 units::Unit tmp(m_unit_system, spec);
593
594 // Check if these units are compatible with "seconds" or "1". The
595 // latter allows intervals of the form "0.5", which stands for "half
596 // of a model year". This also discards interval specs such as "days
597 // since 1-1-1", even though "days" is compatible with "seconds".
598 if (units::are_convertible(tmp, seconds)) {
599 units::Converter c(tmp, seconds);
600
601 return {c(1.0), SIMPLE};
602 }
603
604 if (units::are_convertible(tmp, one)) {
605 units::Converter c(tmp, one);
606
607 // convert from years to seconds without using UDUNITS-2 (this
608 // way we handle 360-day and 365-day years correctly)
609 return {years_to_seconds(c(1.0)), SIMPLE};
610 }
611 } catch (RuntimeError &e) {
612 e.add_context("processing interval length " + spec);
613 throw;
614 }
615
617 "interval length '%s' is invalid", spec.c_str());
618}
619
620
621std::vector<double> Time::parse_range(const std::string &spec) const {
622 double
623 time_start = m_run_start,
624 time_end = m_run_end;
625
626 Interval I{0.0, SIMPLE};
627
628 if (spec == "hourly") {
629 I = {3600.0, SIMPLE};
630 } else if (spec == "daily") {
631 I = {86400.0, SIMPLE};
632 } else if (spec == "monthly") {
633 I = {1.0, MONTHLY};
634 } else if (spec == "yearly") {
635 I = {1.0, YEARLY};
636 } else {
637
638 auto parts = pism::split(spec, ':');
639
640 if (parts.size() == 1) {
641 I = parse_interval_length(parts[0]);
642
643 } else if (parts.size() == 3) {
644 time_start = parse_date(parts[0], m_time_units, m_calendar_string);
645 I = parse_interval_length(parts[1]);
646 time_end = parse_date(parts[2], m_time_units, m_calendar_string);
647 } else {
649 "a time range must consist of exactly 3 parts separated by colons (got '%s').",
650 spec.c_str());
651 }
652 }
653
654 std::vector<double> result;
655 compute_times(time_start, time_end, I, result);
656 return result;
657}
658
659/**
660 * Compute times corresponding to a "simple" time range.
661 *
662 * This is a time range with a fixed step ("hourly" is an example).
663 *
664 * @param[in] time_start beginning of the time interval, in seconds
665 * @param[in] delta step, in seconds
666 * @param[in] time_end end of the interval, in seconds
667 * @param[out] result list of model times
668 *
669 */
670void Time::compute_times_simple(double time_start, double delta, double time_end,
671 std::vector<double> &result) const {
672 if (time_start >= time_end) {
673 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "a >= b in time range a:dt:b (got a = %s, b = %s)",
674 this->date(time_start).c_str(), this->date(time_end).c_str());
675 }
676
677 if (delta <= 0) {
678 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "dt <= 0 in time range a:dt:b (got dt = %f seconds)", delta);
679 }
680
681 int k = 0;
682 double t = time_start;
683
684 result.clear();
685 do {
686 if (t >= this->start() && t <= this->end()) {
687 result.push_back(t);
688 }
689 k += 1;
690 t = time_start + k * delta;
691 } while (t <= time_end);
692}
693
694double Time::convert_time_interval(double T, const std::string &units) const {
695 if (set_member(units, {"year", "years", "yr", "a"})) {
696 return this->seconds_to_years(T); // uses year length here
697 }
698 return convert(m_unit_system, T, "seconds", units);
699}
700
701/*!
702
703 See http://meteora.ucsd.edu/~pierce/calcalcs/index.html and
704 http://cfconventions.org/Data/cf-conventions/cf-conventions-1.8/cf-conventions.html#calendar
705
706 for more details about supported calendars.
707 */
708Time::Time(MPI_Comm com,
709 std::shared_ptr<const Config> config,
710 const Logger &log,
711 units::System::Ptr unit_system)
712 : m_config(config),
713 m_unit_system(unit_system),
714 m_time_units(unit_system, "seconds since 1-1-1") {
715
716 m_variable_name = config->get_string("time.dimension_name");
717
718 m_t_eps = config->get_number("time_stepping.resolution", "seconds");
719
720 auto input_file = config->get_string("input.file");
721
722 std::unique_ptr<File> file{};
723 if (not input_file.empty()) {
724 file.reset(new File(com, input_file, io::PISM_NETCDF3, io::PISM_READONLY));
725 }
726
727 // set the reference date
728 auto ref_date = reference_date(file.get(), *config, log);
729
730 try {
731 // this will validate the reference date
732 m_time_units = units::Unit(m_unit_system, "seconds since " + ref_date);
733 } catch (RuntimeError &e) {
734 e.add_context("setting time units");
735 throw;
736 }
737
738 m_calendar_string = ::pism::calendar(file.get(), *config, log);
740 m_simple_calendar = set_member(m_calendar_string, {"360_day", "365_day", "no_leap"});
741
742 m_run_start = start_time(*config,
743 file.get(),
744 ref_date,
747
748 m_run_end = end_time(*config,
752
753 if (m_run_start > m_run_end) {
754 auto start = date(m_run_start);
755 auto end = date(m_run_end);
757 "Negative run length. Start: %s, end: %s.",
758 start.c_str(), end.c_str());
759 }
760
762
763 auto time_file = config->get_string("time.file");
764 bool continue_run = config->get_flag("time.file.continue");
765
766 if (not time_file.empty()) {
767 log.message(2,
768 "* Setting time from '%s'...\n",
769 time_file.c_str());
770 init_from_file(com, time_file, log, not continue_run);
771 }
772}
773
774//! \brief Sets the time from a NetCDF file with a time dimension (`-time_file`).
775/*!
776 * Sets
777 * - calendar
778 * - reference date
779 * - start time
780 * - current time
781 * - end time
782 *
783 * This allows running PISM for the duration of the available forcing.
784 */
785void Time::init_from_file(MPI_Comm com,
786 const std::string &filename,
787 const Logger &log,
788 bool set_start_time) {
789 try {
790 std::string time_name = m_config->get_string("time.dimension_name");
791
792 File file(com, filename, io::PISM_NETCDF3, io::PISM_READONLY); // OK to use netcdf3
793
794 // Set the calendar name from file.
795 std::string new_calendar = file.read_text_attribute(time_name, "calendar");
796 if (not new_calendar.empty()) {
797 init_calendar(new_calendar);
798 }
799
800 // Set the reference date of internal units.
801 {
802 bool stop_on_error = true;
803 std::string date_string = reference_date_from_file(file,
804 time_name,
805 "irrelevant (not used)",
806 stop_on_error);
807 m_time_units = units::Unit(m_unit_system, "seconds since " + date_string);
808 }
809
810 // Read time information from the file.
811 std::vector<double> time;
812 std::string time_bounds_name = file.read_text_attribute(time_name, "bounds");
813 if (not time_bounds_name.empty()) {
814 // use the time bounds
815 time = io::read_bounds(file, time_bounds_name, m_time_units, m_unit_system);
816 } else {
817 // use the time axis
818 time = io::read_1d_variable(file, time_name, m_time_units, m_unit_system);
819 }
820
821 // Set time.
822 if (set_start_time) {
823 this->set_start(time.front());
824 this->set(time.front());
825 } else {
826 log.message(2, "* Using start time from an -i file to continue an interrupted run.\n");
827 }
828 this->set_end(time.back());
829 } catch (RuntimeError &e) {
830 e.add_context("initializing model time from \"%s\"", filename.c_str());
831 throw;
832 }
833}
834
835double Time::year_fraction(double T) const {
836
838
839 units::DateTime D2{D.year, 1, 1, 0, 0, 0.0};
840
841 auto year_start = m_time_units.time(D2, m_calendar_string);
842
843 D2.year += 1;
844 auto next_year_start = m_time_units.time(D2, m_calendar_string);
845
846 return (T - year_start) / (next_year_start - year_start);
847}
848
849std::string Time::date(double T) const {
851
852 double hour = date.hour + date.minute / 60.0 + date.second / 3600.0;
853
854 return pism::printf("%04d-%02d-%02d %06.3fh",
855 date.year, date.month, date.day, hour);
856}
857
858double Time::calendar_year_start(double T) const {
860
861 units::DateTime D2{D.year, 1, 1, 0, 0, 0.0};
862
864}
865
866
867double Time::increment_date(double T, double years) const {
868 return ::pism::increment_date(m_time_units, m_calendar_string, T, years);
869}
870
871void Time::compute_times_monthly(std::vector<double> &result) const {
872
873 double time = m_run_start;
874
875 // get the date corresponding to the current time
877
878 // beginning of the current month
879 units::DateTime d{date.year, date.month, 1, 0, 0, 0.0};
880
881 result.clear();
882 while (true) {
883 // find the time corresponding to the beginning of the current month
885
886 if (time > m_run_end) {
887 break;
888 }
889
890 if (time >= m_run_start and time <= m_run_end) {
891 result.push_back(time);
892 }
893
894 if (d.month == 12) {
895 d.year += 1;
896 d.month = 1;
897 } else {
898 d.month += 1;
899 }
900 }
901}
902
903void Time::compute_times_yearly(std::vector<double> &result) const {
904
905 double time = m_run_start;
906 // get the date corresponding to the current time
908
909 units::DateTime d{date.year, 1, 1, 0, 0, 0.0};
910
911 result.clear();
912 while (true) {
913 // find the time corresponding to the beginning of the current year
915
916 if (time > m_run_end) {
917 break;
918 }
919
920 if (time >= m_run_start and time <= m_run_end) {
921 result.push_back(time);
922 }
923
924 d.year += 1;
925 }
926}
927
928void Time::compute_times(double time_start, double time_end,
929 const Interval &interval,
930 std::vector<double> &result) const {
931 switch (interval.type) {
932 case SIMPLE:
933 compute_times_simple(time_start, interval.dt, time_end, result);
934 break;
935 case MONTHLY:
936 compute_times_monthly(result);
937 break;
938 case YEARLY:
939 compute_times_yearly(result);
940 break;
941 }
942}
943
944/*!
945 * Check if the modeled time interval is a subset of the time interval in a forcing file.
946 *
947 * Returns silently if it is, otherwise throws an exception with an error message.
948 */
950 double forcing_start,
951 double forcing_end) {
952
953 double run_start = time.start();
954 double run_end = time.end();
955
956 if (not (run_start >= forcing_start and
957 run_end <= forcing_end)) {
959 "A time-dependent forcing has to span the whole length of the simulation\n"
960 " Run time: [%s, %s]\n"
961 " Forcing data: [%s, %s]",
962 time.date(run_start).c_str(),
963 time.date(run_end).c_str(),
964 time.date(forcing_start).c_str(),
965 time.date(forcing_end).c_str());
966 }
967}
968
969} // end of namespace pism
char * ccs_err_str(int error_code)
Definition calcalcs.c:908
int ccs_isleap(calcalcs_cal *calendar, int year, int *leap)
Definition calcalcs.c:336
void ccs_free_calendar(calcalcs_cal *cc)
Definition calcalcs.c:1338
int ccs_date2jday(calcalcs_cal *calendar, int year, int month, int day, int *jday)
Definition calcalcs.c:442
calcalcs_cal * ccs_init_calendar(const char *calname)
Definition calcalcs.c:104
double get_number(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
Definition Config.cc:188
std::string get_string(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
Definition Config.cc:322
bool get_flag(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
Definition Config.cc:351
A class for storing and accessing PISM configuration flags and parameters.
Definition Config.hh:56
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
Definition File.cc:378
std::string name() const
Definition File.cc:274
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition File.cc:420
std::string read_text_attribute(const std::string &var_name, const std::string &att_name) const
Get a text attribute.
Definition File.cc:650
High-level PISM I/O class.
Definition File.hh:57
void message(int threshold, const char format[],...) const __attribute__((format(printf
Print a message to the log.
Definition Logger.cc:50
A basic logging class.
Definition Logger.hh:40
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
Definition Time.cc:867
std::vector< double > parse_list(const std::string &spec) const
Definition Time.cc:541
std::string run_length() const
Returns the length of the current run, in years.
Definition Time.cc:521
void compute_times_monthly(std::vector< double > &result) const
Definition Time.cc:871
double years_to_seconds(double input) const
Definition Time.cc:415
void set(double new_time)
Sets the current time (in seconds since the reference time).
Definition Time.cc:448
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...
Definition Time.cc:694
std::shared_ptr< const Config > m_config
Definition Time.hh:155
double m_year_length
number of seconds in a year, for "year fraction"
Definition Time.hh:165
void set_end(double new_end)
Definition Time.cc:456
void compute_times_simple(double time_start, double delta, double time_end, std::vector< double > &result) const
Definition Time.cc:670
void init_calendar(const std::string &calendar)
Definition Time.cc:427
double start() const
Definition Time.cc:465
double calendar_year_start(double T) const
Returns the model time in seconds corresponding to the beginning of the year T falls into.
Definition Time.cc:858
std::string m_variable_name
Definition Time.hh:159
void compute_times(double time_start, double time_end, const Interval &interval, std::vector< double > &result) const
Definition Time.cc:928
VariableMetadata metadata(bool with_bounds=false) const
Metadata of the NetCDF variable for writing to an output file.
Definition Time.cc:477
@ MONTHLY
Definition Time.hh:133
double end() const
Definition Time.cc:469
std::vector< double > parse_range(const std::string &spec) const
Definition Time.cc:621
VariableMetadata bounds_metadata() const
Metadata of the NetCDF variable containing time bounds.
Definition Time.cc:492
Time(MPI_Comm com, std::shared_ptr< const Config > config, const Logger &log, units::System::Ptr unit_system)
Definition Time.cc:708
double m_run_end
run end tim, in seconds since the reference time
Definition Time.hh:174
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...
Definition Time.cc:835
double m_time_in_seconds
current time, in seconds since the reference time
Definition Time.hh:168
Interval parse_interval_length(const std::string &spec) const
Definition Time.cc:562
std::string m_calendar_string
CF calendar string.
Definition Time.hh:177
bool m_simple_calendar
Definition Time.hh:179
double seconds_to_years(double input) const
Definition Time.cc:423
double current() const
Current time, in seconds.
Definition Time.cc:461
units::Unit units() const
Definition Time.cc:501
std::vector< double > parse_times(const std::string &spec) const
Definition Time.cc:530
std::string variable_name() const
Name of the NetCDF variable to use.
Definition Time.cc:473
void compute_times_yearly(std::vector< double > &result) const
Definition Time.cc:903
double day_of_the_year_to_year_fraction(unsigned int day) const
Convert the day number to the year fraction.
Definition Time.cc:525
void step(double delta_t)
Advance by delta_t seconds.
Definition Time.cc:510
void set_start(double new_start)
Definition Time.cc:452
std::string date(double T) const
Returns the date corresponding to time T.
Definition Time.cc:849
double m_run_start
run start time, in seconds since the reference time
Definition Time.hh:171
const units::System::Ptr m_unit_system
Definition Time.hh:156
units::Unit m_time_units
Definition Time.hh:157
std::string calendar() const
Returns the calendar string.
Definition Time.cc:506
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).
Definition Time.cc:785
double m_t_eps
Time resolution, in seconds.
Definition Time.hh:162
Time management class.
Definition Time.hh:56
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
VariableMetadata & set_time_dependent(bool flag)
std::shared_ptr< System > Ptr
Definition Units.hh:47
DateTime date(double T, const std::string &calendar) const
Definition Units.cc:158
System::Ptr system() const
Definition Units.cc:154
double time(const DateTime &d, const std::string &calendar) const
Definition Units.cc:173
#define PISM_ERROR_LOCATION
#define n
Definition exactTestM.c:37
@ PISM_NETCDF3
Definition IO_Flags.hh:58
@ PISM_READONLY
open an existing file for reading only
Definition IO_Flags.hh:69
@ PISM_UNLIMITED
Definition IO_Flags.hh:80
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)
Definition Units.cc:247
double convert(System::Ptr system, double input, const std::string &spec1, const std::string &spec2)
Convert a quantity from unit1 to unit2.
Definition Units.cc:70
static double increment_date(const units::Unit &time_units, const std::string &calendar, double T, double years)
Definition Time.cc:182
std::string printf(const char *format,...)
static const double k
Definition exactTestP.cc:42
static double end_time(const Config &config, double time_start, const std::string &calendar, const units::Unit &time_units)
Definition Time.cc:395
std::string string_strip(const std::string &input)
void check_forcing_duration(const Time &time, double forcing_start, double forcing_end)
Definition Time.cc:949
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.
Definition Time.cc:79
static std::string reference_date(const File *input_file, const Config &config, const Logger &log)
Definition Time.cc:109
static std::string calendar(const File *input_file, const Config &config, const Logger &log)
Definition Time.cc:146
static double start_time(const Config &config, const File *file, const std::string &reference_date, const std::string &calendar, const units::Unit &time_units)
Definition Time.cc:349
static double parse_date(const std::string &input, const units::Unit &time_units, const std::string &calendar)
Definition Time.cc:251
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.
Definition Time.cc:40
bool pism_is_valid_calendar_name(const std::string &name)
Definition Time.hh:35
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.
IntervalType type
Definition Time.hh:137