Covariance and Uncertainty
In addition to propagating the state elements forward in time, a NumericalPropagator can be used to calculate the values of the state covariance over time. The state covariance describes the uncertainty of the state elements and is not directly propagated. Instead, a StateTransitionMatrix is added to NumericalPropagatorDefinition.IntegrationElements (get) prior to creating the NumericalPropagator. The propagated StateTransitionMatrix data can then be used to find the state covariance at each time step.
The state transition matrix represents the transformation from the state at one time to the state at another time. This can be used as an alternative to propagating a state in time with NumericalPropagator, but is more often used in order to find the time-varying covariance of the state. This is because the construction of a StateTransitionMatrix whose equations of motion are a linearization of the equations of motion for the state, and in most cases calculating the derivatives of the state transition matrix takes as long or longer as calculating the derivatives of the state. The NumericalPropagator integrates the state transition matrix completely in parallel to the other integration elements. In addition, the overhead from propagating both the state transition matrix and other state elements simultaneously is minimal since the redundant calculations. See the Evaluators And Evaluator Groups topic for more information on this optimization.
The StateTransitionMatrix is actually an expanded state transition matrix, also known as a complete consider covariance matrix. In addition to adding state elements to the StateTransitionMatrix you can also add consider parameters. For example, if you wanted to model uncertainty in the drag force in your covariance, you could add the coefficient of drag, or the reference area, as a consider parameter. If no consider parameters are added, the StateTransitionMatrix is identical to a typical state transition matrix. If consider parameters are added, then the StateTransitionMatrix takes the following form:
If the cumulative dimension of the state elements is N, and the cumulative dimension of the consider parameters is M, then Φ is an ordinary state transition matrix: an NxN matrix equal to the partials of the state at time tj with respect to the state at time ti. Θ is an NxM matrix equal to the partials of the state at time tj with respect to the consider parameters at time ti, and the zero and identity matrices are size MxN and MxM, respectively. The complete consider covariance matrix is a square matrix with a dimension of N+M.
The derivative of a StateTransitionMatrix is calculated using the IPartialDifferentiable and PartialDerivativesEvaluator types. State elements and derivatives which implement the IPartialDifferentiable interface can be added to the StateTransitionMatrix. The partial derivatives of these types are used to calculate the derivative of the state transition matrix and propagate it forward in time.
In creating the derivative evaluator, the dependencies of the state elements also calculate their partial derivatives, so they too must implement IPartialDifferentiable. All of the included ForceModels have partial derivatives, as do many STK Components geometry types, such as ScalarFixed, ScalarSum, ScalarProduct, and ScalarExponent. However, if propagating with additional types, such as user-defined forces, those types will have to implement IPartialDifferentiable as well, or else an exception will be thrown.
To add a variable as a state parameter to the StateTransitionMatrix, call the addStateParameter method, providing the state parameter along with its derivative. When calculating partial derivatives, the derivatives of a value are seen as entirely separate and independent entities. For example, a force could have non-zero partial derivatives with respect to position, with zero partial derivatives with respect to velocity, or vice-versa.
To attain a higher order representation of a Components geometry type, you can call the appropriate method to create a instance that represents the derivative:
These derivative creation methods are virtual methods on the Point, Vector, and Scalar base classes. The default implementations do not produce types which implement IPartialDifferentiable, however, derived classes which themselves implement IPartialDifferentiable (such as the PointPropagationParameter stored as PropagationNewtonianPoint.IntegrationPoint (get)) do create derivatives which implement IPartialDifferentiable. When defining your own geometry type for use with partial derivatives, the base class methods to create derivatives must be overridden to produce an instance with the proper partial derivatives for your custom type.
As a last note, when taking partial derivatives of an object being propagated, the PropagationStateElement is the definitional object responsible for the partial derivatives of the order of the object that is acting as a derivative for the NumericalPropagator. So, a PropagationNewtonianPoint is the definitional object that produces a partial derivatives evaluator for the acceleration of the corresponding position.
Adding a consider parameter is a similar process which is done via the StateTransitionMatrix.addConsiderParameter method. Unlike a state parameter, no derivative is required.
Here is an example where the position and velocity of the point represented by a PropagationNewtonianPoint are added to a StateTransitionMatrix as state parameters. The coefficient of drag is then added as a consider parameter.
// propagationPoint is a previously initialized PropagationNewtonianPoint. // drag is a previously initialized AtmosphericDragForce. StateTransitionMatrix stateMatrix = new StateTransitionMatrix(); IPartialDifferentiable position = (IPartialDifferentiable) propagationPoint.getIntegrationPoint(); IPartialDifferentiable velocity = (IPartialDifferentiable) propagationPoint.getIntegrationPoint().createVectorVelocity(propagationPoint.getIntegrationFrame()); stateMatrix.addStateParameter(position, velocity); stateMatrix.addStateParameter(velocity, propagationPoint); stateMatrix.addConsiderParameter((IPartialDifferentiable) drag.getCoefficientOfDrag());
The state at tj can be found by premultiplying the state vector at ti by the (ti -> tj) state transition matrix. Similarly, the covariance matrix at time tj can be found by pre and post-multiplying the covariance matrix at ti by the (ti -> tj) state transition matrix. So, by propagating the state transition matrix, the covariance can be found at matching times from an initial covariance, even though the covariance is not itself directly propagated.
The StateTransitionMatrix.populateCovarianceCollection method does this transformation, accepting an initial covariance matrix and a DateMotionCollection1<Matrix>, of state transition matrices and returning a corresponding DateMotionCollection1<Matrix>, of covariance matrices, as seen in the following example:
// propagator is a previously initialized NumericalPropagator. // stateMatrix is the StateTransitionMatrix declared and initialized in the last code snippet. NumericalPropagationStateHistory history = propagator.propagate(propagationTime, 1); DateMotionCollection1<Matrix> stateTransitionMatrices = history.getDateMotionCollection(stateMatrix.getIdentification()); DateMotionCollection1<Matrix> covarianceMatrices = StateTransitionMatrix.populateCovarianceCollection(initialCovariance, stateTransitionMatrices, stateMatrix.getTransitionType());