Class QSSBase
- java.lang.Object
-
- org.ptolemy.qss.solver.QSSBase
-
- Direct Known Subclasses:
LIQSS1
,LIQSS2Fd
,QSS1
,QSS2Fd
,QSS2FdJac
,QSS2Pts
,QSS2Qts
,QSS3Fd
,QSS3Pts
public abstract class QSSBase extends java.lang.Object
Provide a base class for QSS methods for solving ordinary differential equations.A QSS integrator implements one of the "quantized state systems" methods for solving an initial-value problem (IVP) of a system of ordinary differential equations (ODEs).
A single QSS integrator may be responsible for an entire system of ODEs. Alternately, it may be responsible for a subset of a larger system. The latter approach allows building up a system from smaller components that represent interacting subsystems. Furthermore, each of those subsystems can be integrated by a different variation on the QSS family of methods.
The ODEs are represented using the
DerivativeFcn
interface.Class
QSSBase
provides a general framework for running a single QSS integrator. This framework must be supplemented in a number of ways:- Implementing a specific member of the QSS family requires extending the base class. The subclass must implement the abstract methods declared here. Those abstract "worker" methods provide the particulars associated with the specific member of the QSS family.
- Simulating a system comprising multiple QSS integrators requires the supervisor to connect the integrators. That is, it must match the states predicted by one integrator to the input variables used by another integrator.
- Simulating a system requires some supervisory control, for example to regulate the time steps, and to sequence the exchange of information between integrators. See the API notes below.
Background
Quantized State System (QSS) methods solve a system of ordinary differential equations of the form
xdot = f{t, x, u}
where
- t, simulation time.
- x, vector of state variables, x{t}.
- u, vector of input variables, u{t}.
- xdot, vector of time rates of change of the state variables. That is, xdot = dx/dt.
- f, vector-valued derivative function.
- The notation g{y} means that g is a function of y.
To solve this system, QSS methods rewrite the system as
xdot = f{t, q, mu}
where
- q, vector of quantized state variables, q{t}.
- mu, vector of quantized input variables, u{t}.
The quantized state, and the quantized input variables, are discretized versions of the state and input variables. The quantized version is a piecewise-continuous approximation, with a functional form chosen to simplify the integration of f.
The implementation here uses polynomial models to quantize the state and input variables. For example, QSS1 quantizes the state using a 0th-order polynomial (that is, as a constant). This means that for purposes of the integration, the state is held constant over discrete intervals of time. The method name, QSS1, arises since an internal, continuous version of the state is maintained as a 1st-order polynomial (that is, as a line).
The details of the methods are beyond the scope of this documentation. See:
- Kofman-2002
- Cellier-2006.
- Migoni-2009.
- Migoni-2013.
External vs internal state
Quantized state system methods expose a quantized state to users. Internally, however, they track a continuous state. Both are modeled using polynomials. The polynomial representing the internal, continuous state has one more coefficient than that for the external, quantized state (i.e., it is of order one greater). For example, QSS1 represents the quantized state as constant, but the continuous state as a linear function of time.
For the most part, the user does not need to be aware of this distinction. In fact, for the most part the user only has access to the quantized state. In particular, the user can access the
ModelPoly
objects used to track the quantized states. However, the QSS integrator reserves to itself the right to change the parameters that define those polynomial models.In general, the API refers to an abstract "state" rather than to the "quantized state" or the "continuous state". However, some methods distinguish between the quantized and continuous states. For example:
- Method
evaluateStateModel(int, Time)
evaluates the quantized state model. Since the user can access that model directly, this is mainly a convenience method. - Method
evaluateStateModelContinuous(int, Time)
evaluates the internal, continuous state model. This is intended mainly for testing. However, since the internal model does represent the state as a continuous function of time, for some reporting purposes the internal state may be preferred.
Terminology
The API and documentation presented here use some terms in specific ways. Furthermore these terms do not always correspond exactly with those used in the QSS method literature.
The QSS method literature refers to "states" and "quantized states". In order to avoid ambiguity, the code here refers to these as "continuous states" and "quantized states", respectively.
Variables for continuous states have names like
cStateModel
. Quantized states have names likeqStateModel
.TODO: Mention terms "rate-events", "state-events", and "quantization-events".
A tour of the public API
The simplest use of this class is as follows. First, initialize the solver as follows:
- Create an instance of a class that implements
DerivativeFunction
, and pass that into theinitialize(DerivativeFunction, Time, Time, double, double, int)
method. - Then set the initial values of the state variables using
setStateValue(int, double)
. - Then set initial input values by updating the models returned by
getInputVariableModel(int)
. - Then trigger quantization events using
triggerQuantizationEvents(boolean)
. - Finally, trigger rate events using
triggerRateEvent()
.
predictQuantizationEventTimeEarliest()
. Then, at each time step,- Advance to the current simulation time by calling
advanceToTime(Time)
. This will return a list of state indexes that experience a quantization event at the new simulation time, and you can retrieve the new values of those state variables usinggetStateModel(int)
. - Then set input values by updating the models returned by
getInputVariableModel(int)
. - Finally, trigger rate events using
triggerRateEvent()
.
predictQuantizationEventTimeEarliest()
. That should be the time of the next time step.The following methods initialize a new integrator. They must be called before doing any work with the integrator:
The following methods inquire about fixed integrator parameters:
The following methods set up the exchange of models between an integrator and the rest of the simulation environment. In general, they should be called before starting a simulation. However, they may also be called during an integration:
The following methods configure the integrator. In general, they should be called before its first use. However, they may also be called during an integration:
setQuantizationTolerance(int, double, double)
setQuantizationTolerances(double, double)
setCurrentSimulationTime(Time)
setStateValue(int, double)
setQuantizationEventTimeMaximum(Time)
validate()
The following methods inquire about current values during a simulation:
The following methods prepare the integrator to take the next time step:
needQuantizationEventIndex()
needQuantizationEventIndexes(boolean[])
triggerQuantizationEvent(int)
triggerQuantizationEvents(boolean)
needRateEvent()
triggerRateEvent()
predictQuantizationEventTime(int)
predictQuantizationEventTimeEarliest()
The following methods take a time step:
The following methods primarily facilitate testing:
TODO: Describe the general time-stepping model. Steps only accomplished via method
stepToTime(Time)
. All other methods elaborate on what happens between time steps.The abstract methods that each subclass must fill in have names ending in
_work
. This is meant to help distinguish them from the general entry-points provided by this base class.References
- [Kofman-Junco-2001]. Kofman, E. and S. Junco (2001). "Quantized-State Systems: A {DEVS} Approach for Continuous System Simulation." Trans. of The Society for Modeling and Simulation International 18(1): 2-8.
- [Kofman-2002]. Ernesto Kofman, "A second-order approximation for DEVS simulation of continuous systems", Simulation, v.78, n.2, pp.76-89, 2002.
- [Cellier-2006]. Francois E. Cellier and Ernesto Kofman, "Continuous System Simulation", Springer, 2006.
- [Migoni-2009]. G. Migoni and E. Kofman, "Linearly implicit discrete event methods for stiff ODE's", Latin American Applied Research, v.39, pp.245-254, 2009.
- Floros, X., et al. (2010). "Discretizing Time or States? A Comparative Study between DASSL and QSS - Work in Progress Paper," Workshop on Equation-Based Object-Oriented Modeling Languages and Tools (EOOLT), Oslo, Norway, Linkoping University.
- [Migoni-2013]. Gustavo Migoni, Mario Bortolotto, Ernesto Kofman, and Francois E. Cellier, "Linearly implicit quantization-based integration methods for stiff ordinary differential equations", Simulation Modelling Practice and Theory, v.35, pp.118-136, 2013.
- Since:
- Ptolemy II 11.0
- Version:
- $Id$
- Author:
- David M. Lorenzetti, Contributor: Thierry S. Nouidui, Edward A. Lee
- Pt.AcceptedRating:
- red (reviewmoderator) // FIXME: Fill in.
- Pt.ProposedRating:
- red (dmlorenzetti)
-
-
Field Summary
Fields Modifier and Type Field Description protected ModelPolynomial[]
_cStateModels
Internal continuous state models.protected Time
_currSimTime
The simulation time of the last call.protected DerivativeFunction
_derivFcn
Derivative function.protected double[]
_dqs
Quanta.protected int
_evtIndCt
The event indicator count.protected boolean
_exactInputs
Flag indicating that the solver should assume that inputs are exact, meaning that if derivatives are zero, then they are genuinely zero, not just unknown.protected int
_ivCt
The count of input variables.protected ModelPolynomial[]
_ivModels
Input variables.protected ModelPolynomial[]
_qStateModels
External, quantized state models.protected Time
_quantEvtTimeMax
Maximum Time for predicted quantization-event times.protected int
_stateCt
The state count.
-
Constructor Summary
Constructors Constructor Description QSSBase()
-
Method Summary
All Methods Static Methods Instance Methods Abstract Methods Concrete Methods Modifier and Type Method Description protected abstract void
_initializeWorker()
Initialize object fields (QSS-specific).protected static double
_predictQuantizationEventDeltaTimeQSS2General(ModelPolynomial qStateModel, ModelPolynomial cStateModel, double dq, boolean exactInputs)
Get the delta-time to the predicted quantization-event for a state under QSS2.protected static double
_predictQuantizationEventDeltaTimeQSS2QFromC(ModelPolynomial qStateModel, ModelPolynomial cStateModel, double dq, boolean exactInputs)
Get the delta-time to the predicted quantization-event for a state under QSS2.protected static double
_predictQuantizationEventDeltaTimeQSS3General(ModelPolynomial qStateModel, ModelPolynomial cStateModel, double dq)
Get the delta-time to the predicted quantization-event for a state under QSS3.protected static double
_predictQuantizationEventDeltaTimeQSS3QFromC(ModelPolynomial qStateModel, ModelPolynomial cStateModel, double dq, boolean exactInputs)
Get the delta-time to the predicted quantization-event for a state under QSS3.protected abstract Time
_predictQuantizationEventTimeWorker(int stateIdx, Time quantEvtTimeMax)
Get the predicted quantization-event time for a state (QSS-specific).protected abstract void
_triggerQuantizationEventWorker(int stateIdx)
Form a new external, quantized state model (QSS-specific).protected abstract void
_triggerRateEventWorker()
Form new internal, continuous state models (QSS-specific).protected abstract void
_triggerRateEventWorkerEventDetection()
Form new internal, continuous state models (QSS-specific).java.util.List<java.lang.Integer>
advanceToTime(Time nextSimTime)
Advance simulation time to the specified time.double
evaluateStateModel(int stateIdx, Time simTime)
Get the value of a state variable.double
evaluateStateModelContinuous(int stateIdx, Time simTime)
Get the internal value of a state variable.double
findQuantum(int stateIdx)
Find the quantum for a state.Time
getCurrentSimulationTime()
Get the current simulation time for the QSS integrator.int
getEventIndicatorCount()
Return the count of event indicators.boolean
getExactInputs()
Return whether inputs are assumed to be exact.int
getInputVariableCount()
Return the count of input variables to the integrator.ModelPolynomial
getInputVariableModel(int input)
Return the input variable model for the specified index.int
getStateCount()
Return the count of states predicted by the integrator.ModelPolynomial
getStateModel(int stateIdx)
Get the external, quantized state model for a state predicted by the integrator.abstract int
getStateModelOrder()
Get the order of the external, quantized state models exposed by the integrator.void
initialize(DerivativeFunction derivativeFunction, Time startTime, Time maximumTime, double absoluteTolerance, double relativeTolerance, int inputVariableOrder)
Initialize this solver, associating it with the specified derivativeFunction object, which determines the number of state variables and input variables and provides a method for calculating the derivatives of the state variables.void
initializeDerivativeFunction(DerivativeFunction derivFcn)
Initialize a QSS integrator to use aDerivativeFunction
object.void
initializeDerivativeFunction(DerivativeFunction derivFcn, int numEvtInds)
Initialize a QSS integrator to use aDerivativeFunction
object.void
initializeSimulationTime(Time initSimTime)
Initialize a QSS integrator with an initial time.Time
minimumTime(Time time1, Time time2)
Compare and return the smalltest time between two time objects.int
needInputVariableModelIndex()
Return the index of an input variable for which the user has yet to add a model.int
needQuantizationEventIndex()
Return the first index of a state that needs a quantization-event.void
needQuantizationEventIndexes(boolean[] needQuantEvtIdxs)
Return array of booleans indicating all states that need a quantization-event.boolean
needRateEvent()
Determine whether the integrator needs a rate-event.Time
predictQuantizationEventTime(int stateIdx)
Get the predicted quantization-event time for a state.Time
predictQuantizationEventTimeEarliest()
Get the earliest predicted quantization-event time for all states.Time
predictQuantizationEventTimeEarliest(boolean[] quantEvtElts)
Get the earliest predicted quantization-event time for all states.void
setCurrentSimulationTime(Time newSimTime)
Set or reset the integrator's current time.void
setExactInputs(boolean exact)
Indicate whether inputs are exact.void
setInputVariableModel(int ivIdx, ModelPolynomial ivModel)
Set the model for an input variable to the derivative function.void
setNumberOfEventIndicators(int numberEventIndicators)
Set the number of event indicators.void
setQuantizationEventTimeMaximum(Time quantEvtTimeMax)
Reset the maximum time for predicted quantization-events.void
setQuantizationTolerance(int stateIndex, double absoluteTolerance, double relativeTolerance)
Set the parameters used to determine the quantum for a state.void
setQuantizationTolerances(double absoluteTolerance, double relativeTolerance)
Set the parameters used to determine the quantum for all states.void
setStateValue(int stateIdx, double newValue)
Set the value of a state variable.void
stepToTime(Time nextSimTime)
Step to the next knot in the global simulation.void
stepToTime(Time nextSimTime, int numberEventIndicators)
Step to the next knot in the global simulation for event detection.java.lang.String
stringifyStateModel(int stateIdx)
Get a string representation of the model for a state.java.lang.String
stringifyStateModelContinuous(int stateIdx)
Get a string representation of the internal model for a state.void
triggerQuantizationEvent(int stateIdx)
Form a new external, quantized state model.void
triggerQuantizationEvents(boolean forceAll)
Form new external, quantized state models.void
triggerRateEvent()
Form new internal, continuous state models.void
triggerRateEvent(int numberEventIndicators)
Form new internal, continuous state models.java.lang.String
validate()
Validate the QSS integrator has been properly set up.
-
-
-
Field Detail
-
_exactInputs
protected boolean _exactInputs
Flag indicating that the solver should assume that inputs are exact, meaning that if derivatives are zero, then they are genuinely zero, not just unknown.
-
_derivFcn
protected DerivativeFunction _derivFcn
Derivative function.
-
_stateCt
protected int _stateCt
The state count.
-
_evtIndCt
protected int _evtIndCt
The event indicator count.
-
_ivCt
protected int _ivCt
The count of input variables.
-
_cStateModels
protected ModelPolynomial[] _cStateModels
Internal continuous state models.
-
_qStateModels
protected ModelPolynomial[] _qStateModels
External, quantized state models.
-
_ivModels
protected ModelPolynomial[] _ivModels
Input variables.
-
_dqs
protected double[] _dqs
Quanta.
-
_currSimTime
protected Time _currSimTime
The simulation time of the last call.
-
_quantEvtTimeMax
protected Time _quantEvtTimeMax
Maximum Time for predicted quantization-event times.
-
-
Method Detail
-
advanceToTime
public final java.util.List<java.lang.Integer> advanceToTime(Time nextSimTime) throws java.lang.Exception
Advance simulation time to the specified time. This is a convenience method that encapsulates a typical usage pattern of the other methods in this class and provides more error checking. This method will trigger rate events and quantization events if necessary.- Parameters:
nextSimTime
- Global simulation time to which to step.- Returns:
- A list of indexes of states for which new time matches a quantization event, or an empty list if there are none.
- Throws:
java.lang.IllegalArgumentException
- If the specified time is not strictly greater than the current simulation time, or if the specified time is past the next event time, or if there are states requiring a quantization event.java.lang.Exception
- If triggering a rate event causes an error (this is dependent on the concrete implementation of this class).
-
evaluateStateModel
public final double evaluateStateModel(int stateIdx, Time simTime)
Get the value of a state variable.Evaluate the external, quantized state model at a specified time.
Note this method evaluates the external, quantized state model. Alternately, the user could acquire the model, via method
getStateModel(int)
, and evaluate that model directly.- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCt().simTime
- Global simulation time.- Returns:
- Value of the state model at
simTime
.
-
evaluateStateModelContinuous
public final double evaluateStateModelContinuous(int stateIdx, Time simTime)
Get the internal value of a state variable.Evaluate the internal, continuous state model at a specified time.
- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCt().simTime
- Global simulation time.- Returns:
- Value of the state model at
simTime
.
-
findQuantum
public final double findQuantum(int stateIdx)
Find the quantum for a state.Finds the quantum, i.e., the maximum allowable difference between the external, quantized state model shared with the user, and the internal, continuous state model used by the integrator.
To change the parameters used to find the quantum, use method
setQuantizationTolerance(int, double, double)
or methodsetQuantizationTolerances(double, double)
.The user should never have to call this method directly. The QSS integrator invokes it as needed.
Implementation notes
This method does not store the quantum calculated. The QSS integrator is expected to take care of this.
The nominal policy is to find the quantum whenever the external, quantized state model gets updated. This is because, as calculated here, the quantum depends on the constant coefficient of the external, quantized state model.
In principle, the quantum could be based on the current value of the one of the state models, calculated at the time the quantum is needed. This differs from the policy outlined above, in that the current value of a state generally differs from its constant coefficient.
However, this more complicated policy would induce a lot of mostly needless calculations. If the external, quantized state changes by a large amount, but still tracks the internal, continuous state model well, then the quantum for this element doesn't matter much. If, on the other hand, the two models don't agree, then there will be a quantization-event, which will trigger a new call of this method.
- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCt().- Returns:
- Quantum for the state variable.
-
getCurrentSimulationTime
public final Time getCurrentSimulationTime()
Get the current simulation time for the QSS integrator.This is generally the last global simulation time for which method
stepToTime(Time)
was called. Exceptions:- When the integrator is first instantiated, it is set to 0.
- Method
setCurrentSimulationTime(Time)
changes the value outright.
- Returns:
- Current simulation time for the QSS integrator.
- See Also:
setCurrentSimulationTime(Time)
-
getEventIndicatorCount
public final int getEventIndicatorCount()
Return the count of event indicators.- Returns:
- Count of event indicators.
-
getExactInputs
public final boolean getExactInputs()
Return whether inputs are assumed to be exact. By default, they are not, but if you callsetExactInputs(boolean)
with argument true, then this will return true. A true value asserts that all non-zero derivatives of the input model are provided.- Returns:
- True to indicate that inputs are assumed exact, or false otherwise.
- See Also:
setExactInputs(boolean)
-
getInputVariableCount
public final int getInputVariableCount()
Return the count of input variables to the integrator.- Returns:
- Count of input variables.
-
getInputVariableModel
public final ModelPolynomial getInputVariableModel(int input)
Return the input variable model for the specified index.- Parameters:
input
- The index of the input variable.- Returns:
- the input variable model for the specified index.
- See Also:
setInputVariableModel(int, ModelPolynomial)
-
getStateCount
public final int getStateCount()
Return the count of states predicted by the integrator.- Returns:
- Count of states.
-
getStateModel
public final ModelPolynomial getStateModel(int stateIdx)
Get the external, quantized state model for a state predicted by the integrator.The QSS integrator uses this model to share the predicted state as a function of time, when integrating the derivative function.
The initial state model is constant at a value of 0. Use method
setStateValue(int, double)
to change this initial value.Never change the model parameters directly. The QSS integrator claims exclusive write access to the model.
Details
Notes on "write access" for the state model:
- "Write access" on a model means that some object has asserted that
it plans to change the parameters of the
ModelPoly
. This means it will control the trajectory of that model over time. See method ModelPoly.claimWriteAccess(). - The QSS integrator will change the model parameters as it integrates the derivative function. Therefore the integrator asserts write access on its own state models.
- The QSS integrator requires exclusive write access on its own state models. In order for the integrator to run, every state model must have exactly one claim of "write access" made against it. Therefore the user must not assert write access on any state model.
- The user should never write new parameters to the state model.
- The user should call method
setStateValue(int, double)
, in order to initialize the state, before starting the integration.
Notes on sharing models between roles in the integration:
- It is legal to use a state model as an input variable model of one or more QSS integrators. The model can even be added as an input variable model of the same integrator for which it is a state model (although this should be unusual).
- It is legal to add the same model to multiple input variables.
Design intent
The design intention behind exposing the state models directly to the rest of the system is to make sharing state predictions as cheap as possible.
From an encapsulation viewpoint, the integrator does not have to expose its state models to the rest of the simulation. It could, instead, force the user to evaluate the quantized state models using method
evaluateStateModel(int, Time)
. Alternately, it could copy the quantized state model to a user-supplied model object, thus keeping the integrator's private copy hidden. However, both these approaches are relatively high overhead, compared to simply exposing the model for the user to evaluate as needed.- Parameters:
stateIdx
- The state index, 0 ≤ stateIdx < this.getStateCt().- Returns:
- the external, quantized state model for a state predicted by the integrator.
- "Write access" on a model means that some object has asserted that
it plans to change the parameters of the
-
getStateModelOrder
public abstract int getStateModelOrder()
Get the order of the external, quantized state models exposed by the integrator.This method returns the order of the
ModelPolynomial
objects that the integrator exposes to the user.These states are the quantized state predictions (as opposed to the continuous state predictions the integrator uses internally). Therefore the order is one less than the nominal order of the QSS method.
For example, QSS1 uses a first-order (linear) polynomial to model each state variable. However, it does not expose that internal representation to the user. The external, quantized, representation of the state is a constant (i.e., a 0th-order polynomial). Therefore QSS1 should return
0
for this method.- Returns:
- Order of the external, quantized state models.
-
initialize
public final void initialize(DerivativeFunction derivativeFunction, Time startTime, Time maximumTime, double absoluteTolerance, double relativeTolerance, int inputVariableOrder)
Initialize this solver, associating it with the specified derivativeFunction object, which determines the number of state variables and input variables and provides a method for calculating the derivatives of the state variables. This method also initializes all input and state variables to zero. These can be the initialized to some other value by callingsetStateValue(int, double)
andsetInputVariableModel(int, ModelPolynomial)
. The caller of this method should then, after setting state and input values, calltriggerQuantizationEvents(boolean)
with argument true.This is a convenience method wrapping a sequence of calls to more detailed methods.
- Parameters:
derivativeFunction
- The object implementing the function that provides the derivatives for state variables that this solver is responsible for integrating. This object also provides a method specifying the number of state variables and the number of input variables.startTime
- The start time for the solver.maximumTime
- The maximum time for predicted events (e.g. the stop time of the simulation). This may be infinite.absoluteTolerance
- The absolute tolerance for all state variables (these can be modified later for individual states usingsetQuantizationTolerance(int, double, double)
).relativeTolerance
- The relative tolerance for all state variables (these can be modified later for individual states usingsetQuantizationTolerance(int, double, double)
).inputVariableOrder
- The order (the number of derivatives provided) for each input variable. If these differ by input variable, then the caller may later modify the input variable models by callingsetInputVariableModel(int, ModelPolynomial)
.
-
initializeDerivativeFunction
public final void initializeDerivativeFunction(DerivativeFunction derivFcn)
Initialize a QSS integrator to use aDerivativeFunction
object.This method must be called before doing any work with the integrator. Furthermore, it can be called only once.
Design intent
The derivative function is central to the integrator, and could very well be passed to the constructor. However, to accommodate a wide range of downstream users, a zero-argument constructor was desired. Of course, a constructor that takes the derivative function as an argument could be provided; it would simply call this method.
- Parameters:
derivFcn
- Object that implements the DerivativeFcn interface.
-
initializeDerivativeFunction
public final void initializeDerivativeFunction(DerivativeFunction derivFcn, int numEvtInds)
Initialize a QSS integrator to use aDerivativeFunction
object.This method must be called before doing any work with the integrator. Furthermore, it can be called only once. This methods is used to determine when zero crossing happen.
Design intent
The derivative function is central to the integrator, and could very well be passed to the constructor. However, to accommodate a wide range of downstream users, a zero-argument constructor was desired. Of course, a constructor that takes the derivative function as an argument could be provided; it would simply call this method.
- Parameters:
derivFcn
- Object that implements the DerivativeFcn interface.numEvtInds
- Number of event indicators.
-
initializeSimulationTime
public final void initializeSimulationTime(Time initSimTime)
Initialize a QSS integrator with an initial time.This method must be called before doing any work with the integrator. Furthermore, it can be called only once.
Design intent
For flexibility, the integrator represents time using objects of class
Time
(rather than, for example, a more traditional double-precision variable). This leaves the implementation open to the user. However, it also means that the integrator cannot assume an exact form of constructor for creatingTime
objects. Therefore the user has to construct and provide the initial time.- Parameters:
initSimTime
- The initial time.
-
minimumTime
public final Time minimumTime(Time time1, Time time2)
Compare and return the smalltest time between two time objects. TODO: Get this method under unit test.- Parameters:
time1
- Time object.time2
- Time object.- Returns:
- The smallest time.
-
needInputVariableModelIndex
public final int needInputVariableModelIndex()
Return the index of an input variable for which the user has yet to add a model.The user must call
setInputVariableModel(int, ModelPolynomial)
at least once for every input variable taken by the derivative function. This method checks whether that requirement has been met.- Returns:
- Index of an input variable for which the user has yet to add a model, 0 ≤ idx ≤ this.getArgCt(). Return -1 if all models have been added (or if the derivative function takes no input variables).
-
needQuantizationEventIndex
public final int needQuantizationEventIndex()
Return the first index of a state that needs a quantization-event. This method can be called afterstepToTime(Time)
repeatedly to iterate over the states on whichtriggerQuantizationEvent(int)
should be called. SeeadvanceToTime(Time)
for a typical usage pattern.The integrator tracks which states need a quantization-event as a result of a time step. This method returns the index, if any, of such states.
The user should trigger the quantization-event, e.g., using method
triggerQuantizationEvent(int)
.TODO: Put under unit test.
- Returns:
- Index of a state that needs a quantization-event, 0 <= idx < this.getStateCt(). Return -1 if all external, quantized state models are valid.
-
needQuantizationEventIndexes
public final void needQuantizationEventIndexes(boolean[] needQuantEvtIdxs)
Return array of booleans indicating all states that need a quantization-event.See comments to method
needQuantizationEventIndex()
.TODO: Put under unit test.
- Parameters:
needQuantEvtIdxs
- (output) Vector showingtrue
for each integrator state that needs a quantization-event.
-
needRateEvent
public final boolean needRateEvent()
Determine whether the integrator needs a rate-event.This method only checks whether the integrator needs a rate-event due to a quantization-event. The integrator does not track changes in the parameters of its input variable models, so the integrator cannot warn of the need to trigger a rate-event after an input variable changes.
TODO: Statement above will change if provide ability to install listeners on input variables.- Returns:
- true if the integrator needs a rate event.
-
predictQuantizationEventTime
public final Time predictQuantizationEventTime(int stateIdx)
Get the predicted quantization-event time for a state. TODO: Get this method under unit test.- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCt().- Returns:
- Next time at which, in the absence of other events, the external state model must be re-formed, 0 <= time <= Time.POSITIVE_INFINITY.
-
predictQuantizationEventTimeEarliest
public final Time predictQuantizationEventTimeEarliest()
Get the earliest predicted quantization-event time for all states. TODO: Get this method under unit test.- Returns:
- Earliest predicted quantization-event time from among all states predicted by the integrator.
-
predictQuantizationEventTimeEarliest
public final Time predictQuantizationEventTimeEarliest(boolean[] quantEvtElts)
Get the earliest predicted quantization-event time for all states. TODO: Get this method under unit test.- Parameters:
quantEvtElts
- (output) Vector showingtrue
for those elements whose predicted quantization-event time is the minimum from among all elements. At least one such element must be marked.- Returns:
- Earliest predicted quantization-event time from among all states predicted by the integrator.
-
setCurrentSimulationTime
public final void setCurrentSimulationTime(Time newSimTime)
Set or reset the integrator's current time. This method sets flags indicating that a rate event is needed and that quantization events are needed for all states.- Parameters:
newSimTime
- New time for the QSS integrator.- See Also:
getCurrentSimulationTime()
-
setExactInputs
public final void setExactInputs(boolean exact)
Indicate whether inputs are exact. Calling this with a true argument asserts that all non-zero derivatives of the input model are provided. By default, this solver will assume that a zero value for derivatives may simply mean that the derivatives are unknown.- Parameters:
exact
- True to indicate that inputs are exact.- See Also:
getExactInputs()
-
setInputVariableModel
public final void setInputVariableModel(int ivIdx, ModelPolynomial ivModel)
Set the model for an input variable to the derivative function.Add a
ModelPolynomial
for an input variable to the derivative function. The QSS method will use this model to evaluate the input variable as a function of time, when integrating the derivative function.The user must call this method at least once for every input variable taken by the derivative function. This must be done before starting the integration.
Details
Notes on "write access" for the state model:
- "Write access" means that some object has asserted that it plans to
change the parameters of the
ModelPolynomial
. This means it will control the trajectory of that model over time. SeeModelPolynomial.claimWriteAccess()
. - The user of each integrator is responsible for setting the parameters of each input variable model (i.e., of changing the trajectory of the input variable over time).
- That said, it is OK for the user to delegate the task of changing the input model parameters to some other agent. For example, an input variable can be the state predicted by an integrator.
- In order for the integrator to run, every input variable model must have exactly one claim of "write access" made against it. If the input variable model is also a state model (for this or any other QSS integrator), then the QSS integrator will automatically make that claim. Otherwise, the user may have to make that claim.
- Note that the QSS integrator should never change the parameters of a model added as an input variable. If it does, this is a bug.
Notes on selecting the input variable model order:
- The input variable model may have any valid order.
- In particular, its order does not need to match that of the QSS method for which it serves as an input variable.
Notes on sharing models between roles in the integration:
- See the notes for method
getStateModel(int)
.
Notes on providing a different model at a later time:
- It is OK to change the input variable model before starting the integration (e.g., to change the model's order).
- It is probably not OK to change the model after starting the integration. That is, even if the integrator appears to work, this capability is not a design goal, and may be lost in the future.
- Parameters:
ivIdx
- The index of input variable, 0 ≤ ivIdx < this.getInputVarCt().ivModel
- The model to use.- Throws:
java.lang.IllegalArgumentException
- If the argument is null.- See Also:
getInputVariableModel(int)
- "Write access" means that some object has asserted that it plans to
change the parameters of the
-
setNumberOfEventIndicators
public final void setNumberOfEventIndicators(int numberEventIndicators)
Set the number of event indicators.- Parameters:
numberEventIndicators
- Number of event indicators.
-
setQuantizationTolerance
public final void setQuantizationTolerance(int stateIndex, double absoluteTolerance, double relativeTolerance)
Set the parameters used to determine the quantum for a state.The quantum for each state variable gets calculated as
q[j] = max{Ta, Tr*abs{x[j]}}
where
- q[j] is the quantum for element j,
- Ta is the absoluteTolerance,
- Tr is the relativeTolerance, and
- x[j] is the value of element j the last time a new state model was formed.
This method sets the tolerances used to find the quantum. It also updates the quantum to reflect the new tolerances.
- Parameters:
stateIndex
- The state index, 0 ≤ stateIdx < this.getStateCt().absoluteTolerance
- The absolute tolerance, which is required to be > 0 [units of x[j]].relativeTolerance
- The relative tolerance, which is required to be ≥ 0 [1].- Throws:
java.lang.IllegalArgumentException
- If the absolute tolerance is not strictly positive, or if the relative tolerance is negative.
-
setQuantizationTolerances
public final void setQuantizationTolerances(double absoluteTolerance, double relativeTolerance)
Set the parameters used to determine the quantum for all states.Apply the same tolerances to all the states the integrator predicts. For details, see method
setQuantizationTolerance(int, double, double)
.- Parameters:
absoluteTolerance
- The absolute tolerance, which is required to be > 0 [units of x[j]].relativeTolerance
- The relative tolerance, which is required to be ≥ 0 [1].
-
setStateValue
public final void setStateValue(int stateIdx, double newValue)
Set the value of a state variable.Change the value component of the model for element stateIdx of the state vector. Note that this re-initializes the integrator for that component. That is, it discards any state models that the integrator might have formed, and creates a jump discontinuity in the state variable. This has the side effect of setting flags indicating that a new rate event is needed and that the specified state needs a quantization event.
Warning
The user may be tempted to set the value of the state variable directly in the state model, without going through the QSS integrator. However, doing so prevents the integrator from making the appropriate internal adjustments to the change in state.
- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCt().newValue
- The new value of x[stateIdx].
-
setQuantizationEventTimeMaximum
public final void setQuantizationEventTimeMaximum(Time quantEvtTimeMax)
Reset the maximum time for predicted quantization-events.The integrator will not predict quantization-event times past this time. Default value
Time.POSITIVE_INFINITY
.- Parameters:
quantEvtTimeMax
- The maximum time for predicted quantization-events.
-
stepToTime
public final void stepToTime(Time nextSimTime) throws java.lang.Exception
Step to the next knot in the global simulation.Don't complain if stepping past a predicted quantization-event time. Just report need to trigger a quantization-event before the next step.
- Parameters:
nextSimTime
- Global simulation time to which to step, nextSimTime > this.getCurrSimTime().- Throws:
java.lang.Exception
- If the simulation time must advance or if there are state models waiting to be quantized,
-
stepToTime
public final void stepToTime(Time nextSimTime, int numberEventIndicators) throws java.lang.Exception
Step to the next knot in the global simulation for event detection.Don't complain if stepping past a predicted quantization-event time. Just report need to trigger a quantization-event before the next step.
- Parameters:
nextSimTime
- Global simulation time to which to step, nextSimTime > this.getCurrSimTime().numberEventIndicators
- The number of event indicators.- Throws:
java.lang.Exception
- If the simulation time must advance or if there are state models waiting to be quantized,
-
stringifyStateModel
public final java.lang.String stringifyStateModel(int stateIdx)
Get a string representation of the model for a state.Invoke method ModelPoly.toString() on the external, quantized state model.
- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCt().- Returns:
- a string representation of the model for a state.
-
stringifyStateModelContinuous
public final java.lang.String stringifyStateModelContinuous(int stateIdx)
Get a string representation of the internal model for a state.Invoke method ModelPoly.toString() on the internal, continuous state model.
- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCt().- Returns:
- a string representation of the internal model for a state.
-
triggerQuantizationEvent
public final void triggerQuantizationEvent(int stateIdx)
Form a new external, quantized state model.Force the QSS integrator to form a new external, quantized state model (i.e., to experience a quantization-event). The new model will be available to the user immediately.
Note method
triggerQuantizationEvents(boolean)
can requantize multiple state models at once, and can requantize only those states that need it.Form the model about the current simulation time, as returned by method
getCurrentSimulationTime()
.Note this method can be invoked even if the external, quantized state model is still within quantum of the internal, continuous state model. The integrator will still update the quantized state model about the current time. Doing so can only improve the accuracy of the simulation (at the cost of some extra processing).
Note the state model of interest here is the external, quantized state model. In order to re-form the internal, continuous state model, use method
triggerRateEvent()
.The proper sequence in which to call method
triggerRateEvent()
and methodtriggerQuantizationEvent(int)
is a fraught topic. In general, should requantize all states first, then trigger rate events. Also, after triggering a rate event, get the new predicted quantization time. TODO: Write up a higher-level description of the problem.- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCt().
-
triggerQuantizationEvents
public final void triggerQuantizationEvents(boolean forceAll)
Form new external, quantized state models.Convenience method to call method
triggerQuantizationEvent(int)
on all states predicted by this integrator.Can apply only to those states that are marked for requantization, or can apply to all states.
Note that a state gets marked for requantization:
- At initialization.
- When a time step carries the integrator up to, or past, its predicted
quantization-event time.
See method
predictQuantizationEventTime(int)
. - TODO: Provide, probably in top-level comments, an overview of when
an integrator state needs to have a quantization-event.
Following this list.
Then just cross-reference that discussion here, and in places like
description of method
triggerRateEvent()
.
To determine state(s) that need to be requantized, use either method
needQuantizationEventIndex()
or methodneedQuantizationEventIndexes(boolean[])
.- Parameters:
forceAll
- If true, requantize all state models.
-
triggerRateEvent
public final void triggerRateEvent() throws java.lang.Exception
Form new internal, continuous state models.Force the QSS integrator to form new internal, continuous state models (i.e., to experience a rate-event). The rate models also get updated.
In general, this needs to be done whenever an argument to the derivative function has changed. A "change" in an argument to the derivative function means a change in any of the parameters to an argument's model. This may happen, for example, due to a quantization-event in the integrator that predicts an argument to this integrator. It may also happen due to a change in a boundary condition, for example provided by an external file.
Form the model about the current simulation time, as returned by method
getCurrentSimulationTime()
.Note this method can be invoked even if no argument to the derivative function has had a change in its parameters. The integrator will still update the rate and state models about the current time. Doing so can only improve the accuracy of the simulation (at the cost of some extra processing).
Note the state model of interest here is the internal, continuous state model. In order to re-form the external, quantized state model, use method
triggerQuantizationEvent(int)
.The proper sequence in which to call method
triggerRateEvent()
and methodtriggerQuantizationEvent(int)
is a fraught topic. In general, should requantize all states first, then trigger rate-events. Also, after trigger a rate-event, get new predicted quantization-time. TODO: Write up a higher-level description of the problem.Implementation notes
The way to handle a rate-event varies depending on the particular QSS method. Therefore this is an abstract method. Subclasses are expected take care of the following:
- Reset flags.
- Make consistent with the quantized state model, if there was also a quantization-event. Assuming that's possible. Probably better to phrase this in the inverse-- that method quantize() should be sure to refresh the state model, since in this implementation a quantization-event implies a rate-event.
- TODO: Finish out this list.
Note it's tempting to say should that handling a rate-event should force a quantization-event if one is needed, before handle any rate-event. The logic being that the quantization-event will then induce another rate-event that needs to be handled, anyway. The problem with that logic is that it imposes a policy on how to deal with potential loops in updating cycles. Thus it removes the user's ability to control when quantization happens, which might be important. Of course, if there are no loops, then it is certainly best to requantize before finding new rate and state models. That's because requantizing, always creates a rate-event (since quantized outputs are always arguments to the derivative function). TODO: Consider returning integer status, equal to return status of the derivative function (which should be zero if successful). Then get rid of the exception. TODO: Add a "force" flag so only triggers if needed.
- Throws:
java.lang.Exception
- If thrown while performing the work defined by a specific member of the QSS family.
-
triggerRateEvent
public final void triggerRateEvent(int numberEventIndicators) throws java.lang.Exception
Form new internal, continuous state models.Force the QSS integrator to form new internal, continuous state models (i.e., to experience a rate-event). The rate models also get updated.
This method is similar to method
triggerRateEvent()
. The only difference is that it calls method_triggerRateEventWorkerEventDetection()
to detect and handle state events.- Parameters:
numberEventIndicators
- The number of event indicators.- Throws:
java.lang.Exception
- If thrown while triggering the rate event.
-
validate
public final java.lang.String validate()
Validate the QSS integrator has been properly set up.This method diagnoses setup problems with the integrator. For example, if running the integrator causes a
NullPointerException
, then this method can pinpoint problems originating with the integrator.It is not necessary to run this method in order to run the integrator.
- Returns:
- `null` if the integrator is ready to be used in a simulation, or an error message diagnosing the problem.
-
_initializeWorker
protected abstract void _initializeWorker()
Initialize object fields (QSS-specific).Perform one-time initializations at the beginning of the object lifetime.
The implementation of this "worker" method depends on the specific member of the QSS family.
-
_predictQuantizationEventTimeWorker
protected abstract Time _predictQuantizationEventTimeWorker(int stateIdx, Time quantEvtTimeMax)
Get the predicted quantization-event time for a state (QSS-specific).See comments to method
predictQuantizationEventTime(int)
.The implementation of this "worker" method depends on the specific member of the QSS family.
Implementation notes
The method should not alter any instance variables.
- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCt().quantEvtTimeMax
- The maximum time for the return value. May be Time.POSITIVE_INFINITY.- Returns:
- The predicted quantization-event time for a state (QSS-specific).
-
_predictQuantizationEventDeltaTimeQSS2QFromC
protected static final double _predictQuantizationEventDeltaTimeQSS2QFromC(ModelPolynomial qStateModel, ModelPolynomial cStateModel, double dq, boolean exactInputs)
Get the delta-time to the predicted quantization-event for a state under QSS2.Utility method for use by
_predictQuantizationEventTimeWorker(int, Time)
.Find the time step, from the most recent quantization-event time, of the predicted quantization-event for a state under QSS2. Assume the quantized state model was derived from the continuous state model, and therefore has the same value and slope at the quantization-event time.
TODO: Put this method under direct unit test. Currently tested indirectly, through method
_predictQuantizationEventTimeWorker(int, Time)
of each solver. Testing directly will make it easier to check results, and will make it easier to add testing for slope-aware quant-evt predictions.- Parameters:
qStateModel
- The model of external, quantized state.cStateModel
- The model of internal, continuous state.dq
- The quantum, i.e., the critical difference between the models, at which the external state model must be re-formed.exactInputs
- If true, then the inputs are known to be exact. If true, then do not fall back to QSS1.exactInputs
- True if exact inputs are expected.- Returns:
- dt The delta-time at which, in the absence of other events, the external state model must be re-formed. Note 0 <= dt <= Double.POSITIVE_INFINITY. A value of 0 means need a quantization-event as soon as possible.
-
_predictQuantizationEventDeltaTimeQSS2General
protected static final double _predictQuantizationEventDeltaTimeQSS2General(ModelPolynomial qStateModel, ModelPolynomial cStateModel, double dq, boolean exactInputs)
Get the delta-time to the predicted quantization-event for a state under QSS2.Utility method for use by
_predictQuantizationEventTimeWorker(int, Time)
.Find the time step, from the most recent state-event time, of the predicted quantization-event for a state under QSS2. Do not assume the quantized state model bears any particular relationship to the continuous state model.
TODO: Put this method under direct unit test. Currently tested indirectly, through method
_predictQuantizationEventTimeWorker(int, Time)
of each solver. Testing directly will make it easier to check results, and will make it easier to add testing for slope-aware quant-evt predictions.- Parameters:
qStateModel
- The model of external, quantized state.cStateModel
- The model of internal, continuous state.dq
- The quantum, i.e., the critical difference between the models, at which the external state model must be re-formed.exactInputs
- True if exact inputs are expected.
-
_predictQuantizationEventDeltaTimeQSS3QFromC
protected static final double _predictQuantizationEventDeltaTimeQSS3QFromC(ModelPolynomial qStateModel, ModelPolynomial cStateModel, double dq, boolean exactInputs)
Get the delta-time to the predicted quantization-event for a state under QSS3.Utility method for use by
_predictQuantizationEventTimeWorker(int, Time)
.Find the time step, from the most recent quantization-event time, of the predicted quantization-event for a state under QSS3. Assume the quantized state model was derived from the continuous state model, and therefore has the same value slope, and second derivative at the quantization-event time.
TODO: Put this method under direct unit test. Currently tested indirectly, through method
_predictQuantizationEventTimeWorker(int, Time)
of each solver. Testing directly will make it easier to check results, and will make it easier to add testing for slope-aware quant-evt predictions.- Parameters:
cStateModel
- The model of internal, continuous state.qStateModel
- The model of external, quantized state.dq
- The quantum, i.e., the critical difference between the models, at which the external state model must be re-formed.- Returns:
- dt The delta-time at which, in the absence of other events, the external state model must be re-formed. Note 0 <= dt <= Double.POSITIVE_INFINITY. A value of 0 means need a quantization-event as soon as possible.
-
_predictQuantizationEventDeltaTimeQSS3General
protected static final double _predictQuantizationEventDeltaTimeQSS3General(ModelPolynomial qStateModel, ModelPolynomial cStateModel, double dq)
Get the delta-time to the predicted quantization-event for a state under QSS3.Utility method for use by
_predictQuantizationEventTimeWorker(int, Time)
.Find the time step, from the most recent state-event time, of the predicted quantization-event for a state under QSS3. Do not assume the quantized state model bears any particular relationship to the continuous state model.
TODO: Put this method under direct unit test. Currently tested indirectly, through method
_predictQuantizationEventTimeWorker(int, Time)
of each solver. Testing directly will make it easier to check results, and will make it easier to add testing for slope-aware quant-evt predictions.- Parameters:
cStateModel
- The model of internal, continuous state.qStateModel
- The model of external, quantized state.dq
- The quantum, i.e., the critical difference between the models, at which the external state model must be re-formed.- Returns:
- dt The delta-time at which, in the absence of other events, the external state model must be re-formed. Note 0 <= dt <= Double.POSITIVE_INFINITY. A value of 0 means need a quantization-event as soon as possible.
-
_triggerQuantizationEventWorker
protected abstract void _triggerQuantizationEventWorker(int stateIdx)
Form a new external, quantized state model (QSS-specific).See comments to method
triggerQuantizationEvent(int)
.The implementation of this "worker" method depends on the specific member of the QSS family.
- Parameters:
stateIdx
- The state index, 0 <= stateIdx < this.getStateCount().
-
_triggerRateEventWorker
protected abstract void _triggerRateEventWorker() throws java.lang.Exception
Form new internal, continuous state models (QSS-specific).See comments to method
triggerRateEvent()
.The implementation of this "worker" method depends on the specific member of the QSS family.
- Throws:
java.lang.Exception
- If the rate event worker fails.
-
_triggerRateEventWorkerEventDetection
protected abstract void _triggerRateEventWorkerEventDetection() throws java.lang.Exception
Form new internal, continuous state models (QSS-specific).See comments to method
triggerRateEvent()
.The implementation of this "worker" method depends on the specific member of the QSS family.
- Throws:
java.lang.Exception
- If the rate event worker event detection fails.
-
-