Class 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 like qStateModel.

    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:

    1. Create an instance of a class that implements DerivativeFunction, and pass that into the initialize(DerivativeFunction, Time, Time, double, double, int) method.
    2. Then set the initial values of the state variables using setStateValue(int, double).
    3. Then set initial input values by updating the models returned by getInputVariableModel(int).
    4. Then trigger quantization events using triggerQuantizationEvents(boolean).
    5. Finally, trigger rate events using triggerRateEvent().
    At this point, you can determine the first time at which a quantization event will occur by calling predictQuantizationEventTimeEarliest(). Then, at each time step,
    1. 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 using getStateModel(int).
    2. Then set input values by updating the models returned by getInputVariableModel(int).
    3. Finally, trigger rate events using triggerRateEvent().
    At this point, you can determine the next time at which a quantization event will occur by calling 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:

    The following methods inquire about current values during a simulation:

    The following methods prepare the integrator to take the next time step:

    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

    1. [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.
    2. [Kofman-2002]. Ernesto Kofman, "A second-order approximation for DEVS simulation of continuous systems", Simulation, v.78, n.2, pp.76-89, 2002.
    3. [Cellier-2006]. Francois E. Cellier and Ernesto Kofman, "Continuous System Simulation", Springer, 2006.
    4. [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.
    5. 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.
    6. [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 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.
      • _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.
      • _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.
    • Constructor Detail

      • QSSBase

        public QSSBase()
    • 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 method setQuantizationTolerances(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:

        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 call setExactInputs(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.
      • 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 calling setStateValue(int, double) and setInputVariableModel(int, ModelPolynomial). The caller of this method should then, after setting state and input values, call triggerQuantizationEvents(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 using setQuantizationTolerance(int, double, double)).
        relativeTolerance - The relative tolerance for all state variables (these can be modified later for individual states using setQuantizationTolerance(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 calling setInputVariableModel(int, ModelPolynomial).
      • initializeDerivativeFunction

        public final void initializeDerivativeFunction​(DerivativeFunction derivFcn)
        Initialize a QSS integrator to use a DerivativeFunction 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 a DerivativeFunction 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 creating Time 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 after stepToTime(Time) repeatedly to iterate over the states on which triggerQuantizationEvent(int) should be called. See advanceToTime(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 showing true 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 showing true 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. See ModelPolynomial.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:

        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)
      • 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 method triggerQuantizationEvent(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 method needQuantizationEventIndexes(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 method triggerQuantizationEvent(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.