TimeManager
1 Overview
When simulating the Earth System, a model must track the simulation time as it steps forward with a given time step. Time must be kept in the appropriate calendar system, accounting for leap years and other adjustments as needed. Because these simulations may extend over millions of time steps or more, time must be accumulated without roundoff so that accumulated time does not drift and events will occur at precise time intervals. This document describes various classes needed to track the model’s simulation time accurately and manage events in the appropriate calendar conventions.
2 Requirements
2.1 Required: Simulation time
The time manager must be able to track the current simulation time as the model integration steps forward.
2.2 Required: No accumulation roundoff
Because integrations may extend over millions of time steps or longer, the model must track time without accumulating roundoff.
2.3 Required: Calendar support
The time manager must be able to track time in supported calendars, starting with:
Gregorian
Gregorian with no leap year
Idealized 360-day calendar with equal 30-day months
No calendar (some test cases only need elapsed time in sec) Time for standard calendars will assume Coordinated Universal Time (UTC).
2.4 Required: Time intervals
The model must be able to compute time intervals, not only to represent the model time step itself, but also to be able to compute time between events, track periodic events or perform time averaging. These intervals can be both absolute time (seconds) or represented as calendar intervals (monthly, yearly, number of days). Conversion from a calendar interval to absolute interval (in seconds) will be needed to compute time-related quantities like time averaging.
2.5 Required: Events or alarms
The time manager must be able to trigger events that occur at specific times or at specified periodic time intervals. This requires being able to compare time instants and trigger an alarm if a time instant or interval has been reached. It will also be useful to track when a previous or next event occurs in a periodic sequence.
2.6 Required: Long times
To support very long time integrations or potential paleoclimate simulations, the model must support years that extend beyond the 4-digit representation and support negative years to represent years Before the Common Era (BCE)
2.7 Required: Time string representation
For model output or metadata, the model must be able to
represent a time in accepted formats. Following ISO standards
(eg ISO 8601 and prior), these will typically represent time
in decreasing intervals, eg YYYYYY-MM-DD HH:MM:SS.SSSSS
though
variations on this format can be supported to use, for example,
different separators and different widths for years and fractional
seconds.
3 Algorithmic Formulation
The requirement for no roundoff accumulation implies the need to use an integer fraction representation as in the ESMF time manager. Similarly, calendar days are best tracked using the Julian Day that counts days since a specified start time (Noon UTC on 1 Jan, 4713 BCE in the Gregorian calendar - a beautiful Monday by most accounts) with conversion to various calendars following the algorithms in:
Fliegel, H. F., and Van Flandern, T. C., 1968, A Machine Algorithm for Processing Calendar Dates, Communications of the Association of Computing Machines, 11, 657.
Hatcher, D.A., 1984. Simple Formulae for Julian Day Numbers and Calendar Dates, Quart. J. of R. Astr. Soc., 25, 53-55.
4 Design
The time manager design will follow an ESMF-like approach, though simplified and not requiring linking to the full ESMF library. Previous simplifications were performed for WRF by John Michalakes in Fortran and are used in MPAS, E3SM. A similar simplification was performed by Phil Jones from the ESMF C++ version for the SciDAC CANGA project and will be used here.
The time manager will consist of a number of classes/modules:
TimeFrac
: a base fraction representationTimeInstant
: representation of a point in timeTimeInterval
: a time step or difference between time instantsCalendar
: support for various calendarsAlarm
: alarms that trigger at time instants or periodic intervalsClock
: a clock that keeps track of model time as it marches forward
4.1 Data types and parameters
4.1.1 Parameters
A number of parameters are defined among the above classes. For all classes, it will be useful to define times and intervals with a number and units, so we define an enum class for time units:
enum class TimeUnits{
None = 0, ///< value for undefined units
Seconds, ///< time units in seconds (typical)
Minutes, ///< time units in minutes
Hours, ///< time units in hours
Days, ///< time units in days
Months, ///< time units in months
Years, ///< time units in years
};
For calendars, there will be an enum for supported calendars as well as a string name for each.
#define NUM_SUPPORTED_CALENDARS=9
enum CalendarKind {
CalendarGregorian=1, ///< usual Gregorian calendar
CalendarNoLeap, ///< Gregorian, but without leap yrs
CalendarJulian, ///< Julian
CalendarJulianDay, ///< Julian day
CalendarModJulianDay, ///< modified Julian day
Calendar360Day, ///< 12 months, 30 days each
CalendarCustom, ///< user defined
CalendarNoCalendar, ///< track elapsed time only
CalendarUnknown}; ///< uninitialized or invalid
const std::string CalendarKindName[CALENDAR_KIND_COUNT] = {
"Gregorian",
"No Leap",
"Julian",
"Julian Day",
"Modified Julian Day",
"360 Day",
"Custom",
"No Calendar",
"Invalid" };
4.1.2 Class/structs/data types
There are six classes that will make up the time interval.
4.1.2.1 TimeFrac class
There will be a TimeFrac class for the base fractional time representation:
class TimeFrac {
// private variables
private:
long long whole; ///< whole seconds
long long numer; ///< fractional second (n/d) numerator
long long denom; ///< fractional second (n/d) denominator
public:
[methods described below]
};
4.1.2.2 Calendar class
The calendar class holds useful information for the calendar to be used:
#define MONTHS_PER_YEAR=12
class Calendar {
// private variables
private:
int id; ///< unique id for quick checks
static int numCalendars; ///< number of calendars created
std::string name; ///< name of calendar
CalendarKind calKind; ///< enum for calendar kind
std::string calKindName; ///< name of calendar kind
// variables defining calendar characteristics for time
int daysPerMonth[MONTHS_PER_YEAR]; ///< days in each month
int monthsPerYear; ///< num months in year
int secondsPerDay; ///< seconds per day
int secondsPerYear; ///< seconds per normal year
int daysPerYear; ///< days per normal year
// public methods
public:
[methods described below]
};
4.1.2.3 TimeInstant class
The time instant class represents a point in time within a given calendar:
class TimeInstant {
// private variables
private:
TimeFrac elapsedTime; ///< Fractional seconds since reference time
Calendar *calPtr; ///< Pointer to calendar in which time is based
public:
[methods described below]
};
4.1.2.4 TimeInterval class
The time interval is used for both time steps and to compute differences between time instants. The time interval can either be an time interval in seconds or it can be used to hold a time interval in calendar units (eg number of days, months or years). The latter is useful for periodic events that occur once per year, month or ndays.
class TimeInterval {
// private variables
private:
TimeFrac interval; ///< Non-calendar interval in fractional seconds
bool isCalendar; ///< True if calendar interval
long long calInterval; ///< Calendar interval length
TimeUnits units; ///< Calendar interval units
public:
[methods described below]
};
4.1.2.5 Alarm class
The alarm class allows a user to set either one-time or periodic alarms to trigger events, like forcing updates, I/O, etc. that occur at specific times.
class Alarm {
// private variables
private:
std::string name; ///< name for the alarm
bool ringing; ///< alarm is currently ringing
bool periodic; ///< alarm rings periodically on interval
bool stopped; ///< alarm has been stopped and not reset
TimeInstant ringTime; ///< time at/after which alarm rings
TimeInterval ringInterval; ///< interval at which this alarm rings
TimeInstant ringTimePrev; ///< previous alarm time for interval alarms
public:
[methods described below]
};
4.1.2.6 Clock class
The clock class is meant to track and manage the time for a model advancing in time. Alarms can be attached to the clock so that the ringing status can be updated as the clock marches forward.
class Clock {
// private variables
private:
TimeInstant startTime; ///< initial time for this clock
TimeInstant currTime; ///< current time
TimeInstant prevTime; ///< time at previous timestep
TimeInstant nextTime; ///< time at next timestep
TimeInterval timeStep; ///< interval at which this clock advances
int numAlarms; ///< current number of attached alarms
std::vector<Alarm *> alarms; ///< pointers to alarms associated with this clock
public:
[methods described below]
};
4.2 Methods
The classes above have a number of methods associated with them for managing time.
4.2.1 TimeFrac class
The TimeFrac class is a base class that is not meant to be accessed by users but provides the integer fraction time capability needed for accumulating time. It contains a number of operators to perform arithmetic on fractional integers and a number of accessor functions to convert units into a fractional time.
// Accessor methods
/// Single call to set all native base time components
/// \return error code
int set(const long long whole, ///< [in] whole seconds
const long long numer, ///< [in] fractional second numerator
const long long denom ///< [in] fractional second denominator
);
/// Set base time by converting from integer hours, minutes, seconds
/// \return error code
int setHMS(const int hours, ///< [in] integer hours
const int minutes, ///< [in] integer minutes
const int seconds ///< [in] integer seconds
/// Set base time by converting from a real number of seconds
/// \return error code
int setSeconds(const double seconds ///< [in] Time in real seconds
);
/// Set base time by converting from a real number of hours
/// \return error code
int setHours(const double hours ///< [in] Time in real hours
);
/// Set base time by converting from a real number of minutes
/// \return error code
int setMinutes(const double minutes ///< [in] Time in real minutes
);
/// Set whole seconds separately
/// \return error code
int setWhole(
const long long whole ///< [in] Whole number of seconds
);
/// Set numerator of fractional seconds separately
/// \return error code
int setNumer(
const long long numer ///< [in] Numerator of fractional seconds
);
/// Set denominator of fractional seconds separately
/// \return error code
int setDenom(
const long long denom ///< [in] Denominator of fractional seconds
);
/// Single call to retrieve native base time components
/// \return error code
int get(long long &whole, ///< [out] whole seconds
long long &numer, ///< [out] fractional second numerator
long long &denom ///< [out] fractional second denominator
) const;
/// Get base time converted to integer hours, minutes, seconds
/// \return error code
int getHMS(int &hours, ///< [out] integer hours
int &minutes, ///< [out] integer minutes
int &seconds ///< [out] integer seconds
) const;
/// Get base time and convert to a real number of seconds
/// \return Time in real seconds
double getSeconds(void) const;
/// Get base time and convert to a real number of hours
/// \return Time in real hours
double getHours(void) const;
/// Get base time and convert to a real number of minutes
/// \return Time in real minutes
double getMinutes(void) const;
/// Retrieve the whole seconds component of base time
/// \return Whole number of seconds
long long getWhole(void) const;
/// Retrieve the numerator component of fractional base time
/// \return Numerator of fractional seconds
long long getNumer(void) const;
/// Retrieve the denominator component of fractional base time
/// \return Denominator of fractional seconds
long long getDenom(void) const;
// constructors/destructors
/// Default base time constructor
TimeFrac(void);
/// Copy constructor for base time
TimeFrac(const TimeFrac& ///< [in] existing base time to be copied
);
/// Construct base time by component
TimeFrac(
const long long whole, ///< [in] whole seconds
const long long numer, ///< [in] fractional second numerator
const long long denom ///< [in] fractional second denominator
);
/// Construct base time by converting from a real number of seconds
TimeFrac(const double seconds ///< [in] Time in real seconds
);
/// Destructor for base time
~TimeFrac(void);
// operators
/// Equivalence comparison operator for TimeFrac
bool operator==(const TimeFrac &) const;
/// Non-equivalence comparison operator for TimeFrac
bool operator!=(const TimeFrac &) const;
/// Less than comparison operator for TimeFrac
bool operator< (const TimeFrac &) const;
/// Greater than comparison operator for TimeFrac
bool operator> (const TimeFrac &) const;
/// Less than or equal comparison operator for TimeFrac
bool operator<=(const TimeFrac &) const;
/// Greater than or equal comparison operator for TimeFrac
bool operator>=(const TimeFrac &) const;
/// Addition operator for TimeFrac
TimeFrac operator+ (const TimeFrac &) const;
/// Subtraction operator for TimeFrac
TimeFrac operator- (const TimeFrac &) const;
/// Increment operator for TimeFrac
TimeFrac& operator+=(const TimeFrac &);
/// Decrement operator for TimeFrac
TimeFrac& operator-=(const TimeFrac &);
/// Multiplication by integer scalar
TimeFrac operator* (const int multiplier) const;
/// Multiplication in place by integer scalar
TimeFrac& operator*=(const int multiplier);
/// Multiplication by real scalar
TimeFrac operator* (const double multiplier) const;
/// Multiplication in place by real scalar
TimeFrac& operator*=(const double multiplier);
/// Divide TimeFrac by integer scalar
TimeFrac operator/ (const int divisor) const;
/// Divide TimeFrac in place by integer scalar
TimeFrac& operator/=(const int divisor);
/// Divide two TimeFracs and return a real result
double operator/ (const TimeFrac &) const;
/// Modulus method for TimeFrac
TimeFrac operator% (const TimeFrac &) const;
/// Modulus method in place
TimeFrac& operator%=(const TimeFrac &);
/// Assignment operator for TimeFrac
TimeFrac& operator=(const TimeFrac &);
// Other utility methods
/// Convert a time fraction to new denominator
/// \return error code
int convert(const long long denom ///< [in] new denominator
);
/// Reduce a time fraction to simplest form
int simplify(void);
4.2.2 Calendar class
The calendar class is a mostly immutable class that holds information about the chosen calendar for use by other time manager classes. It mostly constructs an instance based on the user-selected calendar. Retrieval functions and query functions are provided. An equivalence/non-equivalence operator is needed for the later TimeInstant equivalence. Utility functions to convert between elapsed time and calendar dates and incrementing calendar time are supplied for use by other time manager routines.
// accessor functions
// this is (mostly) an immutable class so use constructors
// and provide only one set accessor for renaming
// the only set function is for renaming
/// Renames a Calendar to the input string
/// \return Error code
int rename(const std::string inName ///< [in] name to use for calendar
);
/// Retrieve any/all calendar properties
/// \return Error code
int get(int *outId, ///< [out] id assigned to calendar
std::string *outName, ///< [out] Name of calendar
CalendarKind *outKind, ///< [out] Kind of calendar
int *outDaysPerMonth, ///< [out] Days per month
int *outMonthsPerYear, ///< [out] Months per year
int *outSecondsPerDay, ///< [out] Seconds per day
int *outSecondsPerYear,///< [out] Seconds per year
int *outDaysPerYear ///< [out] Days per year (DPY)
) const;
// Might also add specific retrievals for individual
// components, eg seconds per day/year?
/// Default constructor
Calendar(void);
/// Copy constructor
Calendar(const Calendar &calendar);
/// Constructor based on kind of calendar
Calendar(std::string inName, ///< [in] name of calendar
CalendarKind calKind ///< [in] choice of calendar kind
);
/// Constructs custom calendar based in inputs
Calendar(const std::string inName, ///< [in] name of calendar
int *inDaysPerMonth, ///< [in] array of days per month
int inSecondsPerDay, ///< [in] seconds per day
int inSecondsPerYear, ///< [in] seconds per year
int inDaysPerYear ///< [in] days per year (dpy)
);
/// Calendar destructor
~Calendar(void);
/// Calendar equivalence operator
bool operator==(const Calendar &calendar) const;
/// Calendar non-equivalence operator
bool operator!=(const Calendar &calendar) const;
/// Checks whether input year is a leap year
/// \return true if year is a leap year, false otherwise
bool isLeapYear(long long year, ///< [in] year to check
int &rc ///< [out] return code to flag errors
) const;
/// Computes the total elapsed time in seconds (in TimeFrac form)
/// since the calendar reference time, given a calendar date, time.
/// \return Elapsed time in TimeFrac form
TimeFrac getElapsedTime(
const long long year, ///< [in] calendar year
const long long month, ///< [in] calendar month
const long long day, ///< [in] calendar day
const long long hour, ///< [in] time of day-hour
const long long minute, ///< [in] time of day-min
const long long whole, ///< [in] time of day-whole seconds
const long long numer, ///< [in] time of day-frac secs (numerator)
const long long denom ///< [in] time of day-frac secs (denom)
) const;
/// Determines the calendar date and time of day, given an
/// elapsed time since the calendar reference time.
/// \return error code
int getDateTime(
const TimeFrac elapsedTime, ///< [in] time in secs from ref time
long long &year, ///< [out] calendar year
long long &month, ///< [out] calendar month
long long &day, ///< [out] calendar day
long long &hour, ///< [out] time of day-hours
long long &minute, ///< [out] time of day-minutes
long long &whole, ///< [out] time of day-whole seconds
long long &numer, ///< [out] time of day-frac secs (numerator)
long long &denom ///< [out] time of day-frac secs (denom)
) const;
/// Increments (or decrements) a calendar date by a specified
/// interval, supplied by an integer interval in given time units.
/// Only calendar based intervals (years, months or days) are
/// supported. This is primarily meant to be called by other time
/// manager routines (eg to add/subtract time instants) for those
/// time intervals that are dependent on date and sensitive to
/// calendar features like leap years and varying days of the month.
/// \return error code
int incrementDate(
const long long interval, ///< [in] time interval to advance date
const TimeUnits units, ///< [in] time units for interval
long long &year, ///< [in,out] calendar year of time to be changed
long long &month, ///< [in,out] calendar month of ...
long long &day ///< [in,out] calendar day
) const;
4.2.3 TimeInstant class
The time instant class represents a single instant in time. Most of the methods are accessor methods for getting/setting a time instant or its components. In addition, a number of operators are defined for performing basic arithmetic with time instants and time intervals (see following interval class). Finally, a method for creating a time string for a time instant is supplied.
// constructors/destructors
/// Default constructor creates empty time instant
TimeInstant(void);
/// Construct time instant from date, time, calendar
/// Where seconds is supplied as real number.
TimeInstant(Calendar *Cal, ///< [in] Calendar to use
const long long year, ///< [in] year
const long long month, ///< [in] month
const long long day, ///< [in] day
const long long hour, ///< [in] hour
const long long minute, ///< [in] minute
const double rSecond ///< [in] second (real)
);
/// Construct time instant from date, time, calendar
/// Where seconds is supplied in integer fractional seconds.
TimeInstant( Calendar *Cal, ///< [in] Calendar to use
const long long year, ///< [in] year
const long long month, ///< [in] month
const long long day, ///< [in] day
const long long hour, ///< [in] hour
const long long minute, ///< [in] minute
const long long whole, ///< [in] second (whole integer)
const long long numer, ///< [in] second (fraction numerator)
const long long denom ///< [in] second (fraction denominator)
);
/// Destructor for time interval
~TimeInstant(void);
// Accessor methods
/// Set time instant calendar
/// \return error code
int set(Calendar *Cal ///< [in] Calendar to use for this time
);
/// Set time instant from date and time, where seconds is supplied
/// as a real number.
/// \return error code
int set(const long long year, ///< [in] year
const long long month, ///< [in] month
const long long day, ///< [in] day
const long long hour, ///< [in] hour
const long long minute, ///< [in] minute
const double rSecond ///< [in] second (real)
);
/// Set time instant from date and time, where seconds is supplied
/// in integer fractional seconds.
/// \return error code
int set(const long long year, ///< [in] year
const long long month, ///< [in] month
const long long day, ///< [in] day
const long long hour, ///< [in] hour
const long long minute, ///< [in] minute
const long long whole, ///< [in] second (whole integer)
const long long numer, ///< [in] second (fraction numerator)
const long long denom ///< [in] second (fraction denominator)
);
/// Retrieve calendar from time instant
/// \return error code
int get(Calendar *&cal ///< [out] Calendar ptr in which instant defined
) const;
/// Retrieve time in date, time form with real seconds.
/// \return error code
int get(long long &year, ///< [out] year of this time instant
long long &month, ///< [out] month of this time instant
long long &day, ///< [out] day of this time instant
long long &hour, ///< [out] hour of this time instant
long long &minute, ///< [out] minute of this time instant
double &second ///< [out] second of this time instant
) const;
/// Retrieve time in date, time form with fractional integer seconds.
/// \return error code
int get(long long &year, ///< [out] year of this time instant
long long &month, ///< [out] month of this time instant
long long &day, ///< [out] day of this time instant
long long &hour, ///< [out] hour of this time instant
long long &minute, ///< [out] minute of this time instant
long long &whole, ///< [out] whole seconds of this time
long long &numer, ///< [out] frac second numerator
long long &denom ///< [out] frac second denominator
) const;
// Operators on time instants
/// Equivalence comparison for TimeInstant
bool operator==(const TimeInstant &) const;
/// Non-equivalence comparison operator for TimeInstant
bool operator!=(const TimeInstant &) const;
/// Less than comparison operator for TimeInstant
bool operator< (const TimeInstant &) const;
/// Greater than comparison operator for TimeInstant
bool operator> (const TimeInstant &) const;
/// Less than or equal comparison operator for TimeInstant
bool operator<=(const TimeInstant &) const;
/// Greater than or equal comparison operator for TimeInstant
bool operator>=(const TimeInstant &) const;
/// Increment time by adding a time interval
TimeInstant operator+ (const TimeInterval &) const;
/// Decrement time by subtracting a time interval
TimeInstant operator- (const TimeInterval &) const;
/// Create a time interval by subtracting two time instants
TimeInterval operator- (const TimeInstant &) const;
/// Increment time in place by adding time interval
/// Increment time in place by adding time interval
TimeInstant& operator+=(const TimeInterval &);
/// Decrement time in place by subtracting time interval
TimeInstant& operator-=(const TimeInterval &);
// Other utility methods
/// Get time as a string in the format
/// 'YYYYYY-MM-DD{separator}HH:MM:SS.SSSSSS' where the number
/// of digits in year, number of digits after the decimal in
/// seconds and the character to use as separator are all input
/// by the user.
/// \return time string
std::string getString(
const int yearWidth, ///< [in] number of digits in year
const int secondWidth, ///< [in] num digits after decimal in seconds
std::string separator ///< [in] string(char) to separate date/time
) const;
4.2.4 TimeInterval class
The time interval class manages differences or increments in time. The user can create or set a time interval with a value and units. Retrieval functions for the time interval in various forms are also supplied. A number of operators are defined for a variety of mathematical operations on time intervals (add, subtract, multiply, equivalence, absolute value). Finally, the time instant class is treated as a friend class so that time instants can be incremented by a time interval and time intervals can be created by subtracting two time instants.
// constructors/destructors
/// Default time interval constructor
TimeInterval(void);
/// Construct time interval from base time fractional integer seconds
TimeInterval(const long long whole, ///< Whole integer seconds
const long long numer, ///< Fractional seconds numerator
const long long denom ///< Fractional seconds denominator
);
/// Construct time interval from an integer length and units
TimeInterval(const int length, ///< length of time interval
const TimeUnits units ///< unit of time for interval
);
/// Construct time interval from a long long integer length and units
TimeInterval(const long long length, ///< length of time interval
const TimeUnits units ///< unit of time for interval
);
/// Construct time interval from an real length and units
TimeInterval(const double length, ///< length of time interval
const TimeUnits units ///< unit of time for interval
);
/// Destructor for time interval
~TimeInterval(void);
// Accessor methods
/// Set a non-calendar interval in native fractional integer seconds
/// \return error code
int set(const long long whole, ///< Whole integer seconds
const long long numer, ///< Fractional seconds numerator
const long long denom ///< Fractional seconds denominator
);
/// Set a time interval from integer length and units
/// \return error code
int set(const int length, ///< length of time interval
const TimeUnits units ///< unit of time for interval
);
/// Set a time interval from long long integer length and units
/// \return error code
int set(const long long length, ///< length of time interval
const TimeUnits units ///< unit of time for interval
);
/// Set a time interval from an real length and units
/// Real length is only supported for non-calendar intervals since
/// a non-integral length has ambiguous meaning when months, years
/// have variable length.
/// \return error code
int set(const double length, ///< length of time interval
const TimeUnits units ///< unit of time for interval
);
/// Retrieve non-calendar interval in native fractional integer form
/// \return error code
int get(long long &whole, ///< [out] whole seconds
long long &numer, ///< [out] fractional second numerator
long long &denom ///< [out] fractional second denominator
) const;
/// Retrieve a time interval in integer length in specified units.
/// To avoid roundoff issues during conversions, integer retrieval
/// is only permitted in the same units in which the interval was
/// defined.
/// \return error code
int get(long long &length, ///< [out] requested integer length of interval
const TimeUnits units ///< [in] unit of time for interval
) const;
/// Retrieve a time interval in real length and specified units.
/// For calendar intervals, the units must match the units in which
/// the interval was defined. For non-calendar intervals, only conversions
/// between hours, minutes and seconds are allowed.
/// \return error code
int get(double &length, ///< [out] Requested time interval length
const TimeUnits units ///< [in] unit of time for interval
) const;
/// Equivalence comparison operator for TimeInterval
bool operator==(const TimeInterval &) const;
/// Non-equivalence comparison operator for TimeInterval
bool operator!=(const TimeInterval &) const;
/// Less than comparison operator for TimeInterval
bool operator< (const TimeInterval &) const;
/// Greater than comparison operator for TimeInterval
bool operator> (const TimeInterval &) const;
/// Less than or equal comparison operator for TimeInterval
bool operator<=(const TimeInterval &) const;
/// Greater than or equal comparison operator for TimeInterval
bool operator>=(const TimeInterval &) const;
/// Addition operator for TimeInterval
TimeInterval operator+ (const TimeInterval &) const;
/// Subtraction operator for TimeInterval
TimeInterval operator- (const TimeInterval &) const;
/// Increment operator for TimeInterval
TimeInterval& operator+=(const TimeInterval &);
/// Decrement operator for TimeInterval
TimeInterval& operator-=(const TimeInterval &);
/// Multiplication by integer scalar
TimeInterval operator* (const int multiplier) const;
/// Multiplication by real scalar
/// This is primarily meant for non-calendar intervals, but
/// for calendar intervals, it will multiply the year, month or
/// day interval and convert to the nearest integer.
TimeInterval operator* (const double multiplier) const;
/// Multiplication in place by integer scalar
TimeInterval& operator*=(const int multiplier);
/// Multiplication in place by real scalar
/// This is primarily meant for non-calendar intervals, but
/// for calendar intervals, it will multiply the year, month or
/// day interval and convert to the nearest integer.
TimeInterval& operator*=(const double multiplier);
/// Divide interval by integer scalar
TimeInterval operator/ (const int divisor) const;
/// Divide interval in place by integer scalar
TimeInterval& operator/=(const int divisor);
/// Absolute value
static TimeInterval absValue(const TimeInterval &);
/// Negative absolute value
static TimeInterval negAbsValue(const TimeInterval &);
// Other utility methods
/// Check whether a time interval is positive
bool isPositive(void);
/// Give the Time Instant access to Time Interval so
/// that incrementing/decrementing time is easier.
friend class TimeInstant;
}
4.2.5 Alarm Class
The alarm class allows the creation of both one-time and periodic alarms. There are also methods for querying the state of an alarm and for resetting.
// constructors/destructors
/// Constructs a one-time alarm using the input ring time.
Alarm(const std::string inName, ///< [in] Name of alarm
const TimeInstant alarmTime ///< [in] Time at/after which alarm rings
);
/// Constructs a periodic/interval alarm based on an input periodic
/// time interval and a start time for the interval period.
Alarm(const std::string inName, ///< [in] Name of alarm
const TimeInterval alarmInterval, ///< [in] interval at which alarm rings
const TimeInstant intervalStart ///< [in] start time of first interval
);
/// Destructor for alarm
~Alarm(void);
/// Check whether an alarm is ringing
/// \return true if alarm is ringing, false otherwise
bool isRinging(void);
/// Checks whether the alarm should ring based on the current
/// (or supplied) time instant (returns error code)
int updateStatus(const TimeInstant currentTime ///< [in] current time
);
/// Stops a ringing alarm and sets next a new ring time. If the alarm
/// is a periodic/interval alarm, the next ring time is set to be the
/// next interval boundary after the input time. If the alarm is a
/// single instance, the input time is used as the next alarm time.
/// (returns error code)
int reset(const TimeInstant inTime ///< [in] new basis for alarm time
);
/// Stops a ringing alarm (returns error code).
int stop(void);
/// Rename an alarm (returns error code)
int rename(const std::string newName ///< [in] new name for alarm
);
/// Get alarm name
std::string getName(void) const;
4.2.6 Clock Class
The Clock class defines the model clock and is created based on a start time and time step. There are a number of retrieval functions to get the current, past, and next times. A number of alarms can be attached to a clock. Finally, there is a method to advance the clock one time and update the state of all attached alarms.
// constructors/destructors
/// Construct a clock from start time and time step
Clock(const TimeInstant startTime, ///< [in] Start time for clock
const TimeInterval timeStep ///< [in] Time step to advance clock
);
/// Destructor for clocks
~Clock(void);
// Accessor Methods
/// Set the current time (returns error code)
int setCurrentTime(
const TimeInstant currTime ///< [in] new value for current time
);
/// Changes the time step for this clock (returns error code)
int changeTimeStep(
const TimeInterval newTimeStep ///< [in] new value for time step
);
/// Retrieves current time of this clock
TimeInstant getCurrentTime(void) const;
/// Retrieves time at the previous time step
TimeInstant getPreviousTime(void) const;
/// Retrieves time at the next time step
TimeInstant getNextTime(void) const;
/// Retrieves start time for this clock
TimeInstant getStartTime(void) const;
/// Retrieves time step for clock
TimeInterval getTimeStep(void) const;
/// Attaches an alarm to this clock. The clock simply stores a
/// pointer to this alarm. (returns error code)
int attachAlarm(Alarm* inAlarm ///< [in] pointer to alarm to attach
);
/// Advance a clock one timestep and update status of any attached
/// alarms. (returns error code)
int advance(void);
5 Verification and Testing
5.1 Test TimeFrac
We will test the base TimeFrac class by creating some reference times, one of which will be a fractional time that cannot be exactly represented by a float (eg 1/3 seconds). Constructors and set functions will be paired with get functions to make sure both set/get are working. All operators (equivalence, relational, algebraic) operators will be tested with known values. We will try to anticipate values that could fail.
tests parts of requirement 2.2, 2.5, 2.6
5.2 Test TimeInstant
Similar to TimeFrac, create reference times in each of the supported calendars using constructor and set functions. Test retrievals (get) to verify they return the reference value. Test all the supported TimeInstant operators with known values in each of the calendars.
tests parts of requirement 2.1, 2.3
5.3 Test Calendar
For each of the supported calendars (including a custom calendar), create an instance of the calendar and use the retrieval (get) functions to verify that various calendar properties are as expected (eg days per year, days per month, leap years/days, etc.). For some calendars, known reference dates exist from the references above and we will verify that these result in correct elapsed time or Julian day. Test the few operators (equivalence, copy constructors).
tests most of requirement 2.3
5.4 Test Time Intervals
As in the others, test constructors and set functions by pairing with retrieval (get) functions for a given reference time interval. Test assignment constructors by creating a time interval for a given time step in various time units (seconds, hours, days, etc.). Test equivalence/non-equivalence by comparing known time intervals. Test algebraic operators (addition, subtraction, increment, decrement, multiply, divide, etc.) using reference values. Test creation of time interval by subtraction of time instants.
tests requirement 2.4, parts of 2.1
5.5 Test Alarms
Test both one-time and periodic alarms at various supported intervals, including annual, monthly/nmonths, daily/ndays, hourly/nhours, nminutes, seconds. This is accomplished by choosing a start time in a calendar and integrating over a time period that covers the alarm. Use the Gregorian calendar (as most complex) and choose an interval that includes leap years/days. Also verify that alarms are not ringing at arbitrary selected times in between. Check the reset function and verify alarms are reset appropriately.
tests requirement 2.5
5.6 Test Clock
This is the primary test of putting it all together and verifying that we can advance a model clock. Construct a clock with a given calendar and time step. Test various retrieval functions to get known quantities - also test the ability to change time step with set/get pair. Set a number of alarms at various times and periodic intervals. Integrate forward in time for N years and verify all alarms are triggered at proper times. Also verify that the final time is exact with no roundoff error accumulated. Create another clock with large time steps (years) that extend over a potential paleoclimate interval to test large number representations are supported correctly.
tests requirement 2.1, 2.2, 2.3, 2.4, 2.5, 2.6