Simulation Time
SystemsOfSystems distinguishes exact, official simulation times from the floating-point times used for numerical integration. User-requested times, schedule occurrences, and model-requested t_next values are official times. This allows unrelated periodic systems to remain aligned without accumulating floating-point roundoff errors from step to step.
The numerical solver evaluates rates_fcn at floating-point times between official samples. In contrast, init_fcn, updates_fcn, schedules, and t_next use exact times at the simulation boundary.
ExactTime
ExactTime is the rational representation used for official simulation times. Values supplied as integers, rationals, or floating-point numbers are converted to this representation when they enter the scheduler. Rational values such as 1//10 are useful when an event time must be represented explicitly and exactly.
The exact_time function performs this conversion. Floating-point inputs use Julia's rationalize behavior, which recovers simple values such as converting 0.1 to 1//10. The type and conversion function live in the SimulationTimes module because most models can rely on automatic conversion.
SystemsOfSystems.SimulationTimes.ExactTime — Type
The concrete representation currently used for every official simulation time.
This alias gives the representation one home and makes a future Int128 experiment local rather than requiring another search for hard-coded rational types.
SystemsOfSystems.SimulationTimes.exact_time — Function
exact_time(t)Converts a user- or model-provided time to the official exact representation.
Rational inputs retain their mathematical value. Floating-point inputs use Julia's normal rationalize semantics, which intentionally recover simple values such as 0.1 == 1//10. An explicitly rational input is available for cases where the event time must be stated exactly.
Two special exact-time values are used with a dynamic ModelDescription.t_next:
SystemsOfSystems.KEEP_T_NEXTretains the model's previous request when returned fromUpdatesOutput.SystemsOfSystems.NO_T_NEXTindicates that the model has no finite upcoming event.
These sentinels are public but not exported because their meaning depends on the simulation-time interface. Model code can refer to them with the SystemsOfSystems. prefix. KEEP_T_NEXT is the default when UpdatesOutput.t_next is omitted, while NO_T_NEXT is useful for cancelling a finite request explicitly:
UpdatesOutput(; t_next = SystemsOfSystems.NO_T_NEXT)Triggering
The updates_fcn is called on every step, and the step may result from a schedule, a model's t_next, the integrator's adaptive step method, etc. A schedule's is_triggering function indicates whether the current official time is one of that schedule's occurrences.
function updates(t, model)
if is_triggering(model.sample_schedule, t)
return UpdatesOutput(;
updates = (;
count = model.count + 1,
),
)
end
return nothing
endon_triggering provides the same check in Julia's do-block form. It evaluates the block when the schedule is triggering and otherwise returns nothing, which is also a valid result from updates_fcn.
function updates(t, model)
on_triggering(model.sample_schedule, t) do
return UpdatesOutput(;
updates = (;
count = model.count + 1,
),
)
end
endInitialization establishes the model at t_start; it does not call updates_fcn there. Schedule queries apply to the accepted samples that follow initialization.
SystemsOfSystems.Schedules.is_triggering — Function
is_triggering(schedule::AbstractSchedule, t)Returns whether schedule has an occurrence at official simulation time t.
Implementations should use exact-time comparisons. The simulation calls every model's update function after every accepted step, so models use this predicate to distinguish their scheduled samples from unrelated solver, user, or model event times.
SystemsOfSystems.on_triggering — Function
on_triggering(f, schedule::AbstractSchedule, t)Runs f and returns its result when schedule is triggering at official time t; otherwise, returns nothing without evaluating f.
The function argument comes first so model update code can use Julia's do syntax:
function updates_fcn(t, model)
on_triggering(model.schedule, t) do
return UpdatesOutput(; updates = (; count = model.count + 1,))
end
endFinding Future Times
next_trigger_time returns the first occurrence of a schedule strictly later than a given official time. SystemsOfSystems uses this function internally when combining the next occurrences from every declared schedule.
next_regular_time provides the corresponding calculation for a regular sequence described directly by a period and offset. It can be useful for a dynamic t_next implementation whose next event follows a regular clock but is not declared in the model's schedules.
function updates(t, model)
return UpdatesOutput(;
updates = (;
count = model.count + 1,
),
t_next = next_regular_time(t, model.period, model.offset),
)
endThe lower-level Schedules.is_regular_step_triggering function tests membership in the same periodic sequence without constructing a schedule. A RegularSchedule or OffsetRegularSchedule is generally more convenient for model code because it also requests the necessary simulation times. This function exists for backwards compatibility and is public but not exported.
SystemsOfSystems.Schedules.next_trigger_time — Function
next_trigger_time(schedule::AbstractSchedule, t)Returns the first occurrence of schedule strictly later than official simulation time t.
The strict inequality is part of the interface: initialization establishes the model at t_start without performing a discrete update there. A finite schedule may return NO_T_NEXT when it has no remaining occurrences.
SystemsOfSystems.Schedules.next_regular_time — Function
next_regular_time(t, period, offset = 0//1)Returns the first time in offset + n * period, for nonnegative integer n, that is strictly later than t.
This function is intentionally not inclusive: init_fcn establishes the model at the simulation start time, and SystemsOfSystems does not perform a discrete update at t_start. The calculation is closed-form and therefore needs no mutable sample index or accumulated floating-point clock.
SystemsOfSystems.Schedules.is_regular_step_triggering — Function
is_regular_step_triggering(t, period, offset = 0//1)Returns whether exact time t belongs to the periodic sequence offset + n * period.
A zero period retains the existing convention of triggering at every accepted sample. For a positive period, widened exact arithmetic avoids floating-point tolerances and bounded rational intermediate overflow.