Modules must be able to implement a least three capabilities:
In addition, modules generally should be able to implement the following
It turns out that implementing these capabilities is not as easy as it at first seems. In particular, the synchronization has typically been one of the most challenging concepts for programmers to understand. Given the amount of time spend in sync calls, it is recommended that considerable time and effort be put into its design.
Basic Synchronization
An object's sync method actually performs two essential functions. First, it updates the state of an object to a designated point in time, and second it lets the core know when the object is next expected to change state. This is vital for the core to know because the core's clock will be advanced to the time of the next expected state change, and all objects will be synchronized to that time.
In general a sync() function should be aware of three times:
is the time from which the object is being advanced. This is not the current time, because it is presumed that the object has not yet advanced to the current time and this is why sync() is being called.
is the time to which the object is being advanced. Think of this as now from the object's perspective. This is usually the current time from the core's perspective (but don't assume it always is).
is the time at which the object expects to have its next state change. This time must be computed by the object during or immediately after the state is advanced to
. This is the time returned to the core should the sync() call succeed.
If no state change is ever anticipated then
TS_NEVER is returned, indicating that barring any changes to its boundary condition, the object is in steady state.
If an object's sync() method determines that the object is not yet in steady state (i.e., the module has not converged), then
is returned.
If an object's sync() method determines that it cannot update to
as required, the simulation has failed. It can either throw an exception using GL_THROW() or return
to indicate the time at which the problem is believed to have occurred.
The time window
is the past window and the sync() method must implement all behaviors during that time as though they have already occurred.
The time window
is the future window and the sync function must not implement behaviors in this window yet, as they have not yet occurred.
It is a non-trivial fact that if all objects in all modules in GridLAB model return
TS_NEVER, then the simulation stops successfully. This is because the system has completely settle to a steady state and there is nothing further to compute.
Control Code
One very important aspect of synchronization behavior is how control code is handled when object behavior goes beyond the mere physics of its response to its boundary condition. It is quite easy to implement control code that is integrated with the physical model. However, this would prevent users from altering the control code without altering the source code of the object's implementation.
To address this problem, objects can implement default control code that is disabled if a PLC object is attached later. The ability to alter control code should be made available when implemented for any object for which this is a realistic possibility, which is very nearly always.
To implement default PLC code for an object, the module must expose a plc() method that will be called immediately before sync() is called, but only if not external PLC method is provided in the model. This plc() method may be written as though it was integrated with the physics implemented in sync(), but the physics must be able to update even when the default PLC code is not run.