JacobiansMapper.java
/* Copyright 2002-2015 CS Systèmes d'Information
* Licensed to CS Systèmes d'Information (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.propagation.numerical;
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.DecompositionSolver;
import org.apache.commons.math3.linear.QRDecomposition;
import org.apache.commons.math3.linear.RealMatrix;
import org.orekit.errors.OrekitException;
import org.orekit.orbits.Orbit;
import org.orekit.orbits.OrbitType;
import org.orekit.orbits.PositionAngle;
import org.orekit.propagation.SpacecraftState;
/** Mapper between two-dimensional Jacobian matrices and one-dimensional {@link
* SpacecraftState#getAdditionalState(String) additional state arrays}.
* <p>
* This class does not hold the states by itself. Instances of this class are guaranteed
* to be immutable.
* </p>
* @author Luc Maisonobe
* @see org.orekit.propagation.numerical.PartialDerivativesEquations
* @see org.orekit.propagation.numerical.NumericalPropagator
* @see SpacecraftState#getAdditionalState(String)
* @see org.orekit.propagation.AbstractPropagator
*/
public class JacobiansMapper {
/** Name. */
private String name;
/** State vector dimension without additional parameters
* (either 6 or 7 depending on mass being included or not). */
private final int stateDimension;
/** Number of Parameters. */
private final int parameters;
/** Orbit type. */
private final OrbitType orbitType;
/** Position angle type. */
private final PositionAngle angleType;
/** Simple constructor.
* @param name name of the Jacobians
* @param stateDimension dimension of the state (either 6 or 7 depending on mass
* being included or not)
* @param parameters number of parameters
* @param orbitType orbit type
* @param angleType position angle type
*/
JacobiansMapper(final String name, final int stateDimension, final int parameters,
final OrbitType orbitType, final PositionAngle angleType) {
this.name = name;
this.stateDimension = stateDimension;
this.parameters = parameters;
this.orbitType = orbitType;
this.angleType = angleType;
}
/** Get the name of the partial Jacobians.
* @return name of the Jacobians
*/
public String getName() {
return name;
}
/** Compute the length of the one-dimensional additional state array needed.
* @return length of the one-dimensional additional state array
*/
public int getAdditionalStateDimension() {
return stateDimension * (stateDimension + parameters);
}
/** Get the state vector dimension.
* @return state vector dimension
*/
public int getStateDimension() {
return stateDimension;
}
/** Get the number of parameters.
* @return number of parameters
*/
public int getParameters() {
return parameters;
}
/** Get the conversion Jacobian between state parameters and cartesian parameters.
* @param state spacecraft state
* @return conversion Jacobian
*/
private double[][] getdYdC(final SpacecraftState state) {
final double[][] dYdC = new double[stateDimension][stateDimension];
// make sure the state is in the desired orbit type
final Orbit orbit = orbitType.convertType(state.getOrbit());
// compute the Jacobian, taking the position angle type into account
orbit.getJacobianWrtCartesian(angleType, dYdC);
for (int i = 6; i < stateDimension; ++i) {
dYdC[i][i] = 1.0;
}
return dYdC;
}
/** Set the Jacobian with respect to state into a one-dimensional additional state array.
* <p>
* This method converts the Jacobians to cartesian parameters and put the converted data
* in the one-dimensional {@code p} array.
* </p>
* @param state spacecraft state
* @param dY1dY0 Jacobian of current state at time t₁
* with respect to state at some previous time t₀
* @param dY1dP Jacobian of current state at time t₁
* with respect to parameters (may be null if there are no parameters)
* @param p placeholder where to put the one-dimensional additional state
* @see #getStateJacobian(SpacecraftState, double[][])
*/
void setInitialJacobians(final SpacecraftState state, final double[][] dY1dY0,
final double[][] dY1dP, final double[] p) {
// set up a converter between state parameters and cartesian parameters
final RealMatrix dY1dC1 = new Array2DRowRealMatrix(getdYdC(state), false);
final DecompositionSolver solver = new QRDecomposition(dY1dC1).getSolver();
// convert the provided state Jacobian to cartesian parameters
final RealMatrix dC1dY0 = solver.solve(new Array2DRowRealMatrix(dY1dY0, false));
// map the converted state Jacobian to one-dimensional array
int index = 0;
for (int i = 0; i < stateDimension; ++i) {
for (int j = 0; j < stateDimension; ++j) {
p[index++] = dC1dY0.getEntry(i, j);
}
}
if (parameters > 0) {
// convert the provided state Jacobian to cartesian parameters
final RealMatrix dC1dP = solver.solve(new Array2DRowRealMatrix(dY1dP, false));
// map the converted parameters Jacobian to one-dimensional array
for (int i = 0; i < stateDimension; ++i) {
for (int j = 0; j < parameters; ++j) {
p[index++] = dC1dP.getEntry(i, j);
}
}
}
}
/** Get the Jacobian with respect to state from a one-dimensional additional state array.
* <p>
* This method extract the data from the {@code state} and put it in the
* {@code dYdY0} array.
* </p>
* @param state spacecraft state
* @param dYdY0 placeholder where to put the Jacobian with respect to state
* @exception OrekitException if state does not contain the Jacobian additional state
* @see #getParametersJacobian(SpacecraftState, double[][])
*/
public void getStateJacobian(final SpacecraftState state, final double[][] dYdY0)
throws OrekitException {
// get the conversion Jacobian between state parameters and cartesian parameters
final double[][] dYdC = getdYdC(state);
// extract the additional state
final double[] p = state.getAdditionalState(name);
// compute dYdY0 = dYdC * dCdY0, without allocating new arrays
for (int i = 0; i < stateDimension; i++) {
final double[] rowC = dYdC[i];
final double[] rowD = dYdY0[i];
for (int j = 0; j < stateDimension; ++j) {
double sum = 0;
int pIndex = j;
for (int k = 0; k < stateDimension; ++k) {
sum += rowC[k] * p[pIndex];
pIndex += stateDimension;
}
rowD[j] = sum;
}
}
}
/** Get theJacobian with respect to parameters from a one-dimensional additional state array.
* <p>
* This method extract the data from the {@code p} array and put it in the
* {@code dYdP} array.
* </p>
* <p>
* If no parameters have been set in the constructor, the method returns immediately and
* does not reference {@code dYdP} which can safely be null in this case.
* </p>
* @param state spacecraft state
* @param dYdP placeholder where to put the Jacobian with respect to parameters
* @exception OrekitException if state does not contain the Jacobian additional state
* @see #getStateJacobian(SpacecraftState, double[][])
*/
public void getParametersJacobian(final SpacecraftState state, final double[][] dYdP)
throws OrekitException {
if (parameters > 0) {
// get the conversion Jacobian between state parameters and cartesian parameters
final double[][] dYdC = getdYdC(state);
// extract the additional state
final double[] p = state.getAdditionalState(name);
// compute dYdP = dYdC * dCdP, without allocating new arrays
for (int i = 0; i < stateDimension; i++) {
final double[] rowC = dYdC[i];
final double[] rowD = dYdP[i];
for (int j = 0; j < parameters; ++j) {
double sum = 0;
int pIndex = j + stateDimension * stateDimension;
for (int k = 0; k < stateDimension; ++k) {
sum += rowC[k] * p[pIndex];
pIndex += parameters;
}
rowD[j] = sum;
}
}
}
}
}