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