Commit 6c2266ae authored by Luc Maisonobe's avatar Luc Maisonobe
Browse files

Allow Jacobians columns names extraction before start.

The names should *not* be extracted too early, at least the force models
must be completed before they can be retrived reliably. However, once
they are set up we can ask the MatricesHarvester to give the columns
names, even if propagation has not started yet.
parent 985cfd2b
Pipeline #1599 failed with stages
in 39 minutes and 17 seconds
......@@ -62,10 +62,11 @@ public interface MatricesHarvester {
/** Get the names of the parameters in the matrix returned by {@link #getParametersJacobian}.
* <p>
* Beware that the names of the parameters are fully known only at propagation start,
* since applications may configure the propagator, retrieve the matrices harvester first
* and select the force model parameters to retrieve afterwards (but obviously before
* starting propagation). So the method may return wrong results if called too early.
* Beware that the names of the parameters are fully known only once all force models have
* been set up and their parameters properly selected. Applications that retrieve the matrices
* harvester first and select the force model parameters to retrieve afterwards (but obviously
* before starting propagation) must take care to wait until the parameters have been set up
* before they call this method. Calling the method too early would return wrong results.
* </p>
* <p>
* The names are returned in the Jacobians matrix columns order
......
......@@ -16,7 +16,6 @@
*/
package org.orekit.propagation.numerical;
import java.util.Collections;
import java.util.List;
import org.hipparchus.linear.MatrixUtils;
......@@ -38,6 +37,9 @@ class NumericalPropagationHarvester implements MatricesHarvester {
/** State dimension, fixed to 6. */
public static final int STATE_DIMENSION = 6;
/** Propagator bound to this harvester. */
private final NumericalPropagator propagator;
/** Initial State Transition Matrix. */
private final RealMatrix initialStm;
......@@ -50,18 +52,13 @@ class NumericalPropagationHarvester implements MatricesHarvester {
/** Columns names for parameters. */
private List<String> columnsNames;
/** Orbit type. */
private OrbitType orbitType;
/** Position angle type. */
private PositionAngle positionAngleType;
/** Simple constructor.
* <p>
* The arguments for initial matrices <em>must</em> be compatible with the {@link #setOrbitType(OrbitType) orbit type}
* and {@link #setPositionAngleType(PositionAngle) position angle} that will be ultimately
* selected when propagation starts
* </p>
* @param propagator propagator bound to this harvester
* @param stmName State Transition Matrix state name
* @param initialStm initial State Transition Matrix ∂Y/∂Y₀,
* if null (which is the most frequent case), assumed to be 6x6 identity
......@@ -69,12 +66,13 @@ class NumericalPropagationHarvester implements MatricesHarvester {
* if null or if some selected parameters are missing from the dictionary, the corresponding
* initial column is assumed to be 0
*/
NumericalPropagationHarvester(final String stmName, final RealMatrix initialStm,
final DoubleArrayDictionary initialJacobianColumns) {
NumericalPropagationHarvester(final NumericalPropagator propagator, final String stmName,
final RealMatrix initialStm, final DoubleArrayDictionary initialJacobianColumns) {
this.propagator = propagator;
this.stmName = stmName;
this.initialStm = initialStm == null ? MatrixUtils.createRealIdentityMatrix(STATE_DIMENSION) : initialStm;
this.initialJacobianColumns = initialJacobianColumns == null ? new DoubleArrayDictionary() : initialJacobianColumns;
this.columnsNames = Collections.emptyList();
this.columnsNames = null;
}
/** Get the State Transition Matrix state name.
......@@ -84,27 +82,6 @@ class NumericalPropagationHarvester implements MatricesHarvester {
return stmName;
}
/** Set Jacobian matrix columns names.
* @param columnsNames names of the parameters for Jacobian columns, in desired matrix order
*/
void setColumnsNames(final List<String> columnsNames) {
this.columnsNames = columnsNames;
}
/** Set orbit type.
* @param orbitType orbit type
*/
void setOrbitType(final OrbitType orbitType) {
this.orbitType = orbitType;
}
/** Set position angle type.
* @param positionAngleType angle type
*/
void setPositionAngleType(final PositionAngle positionAngleType) {
this.positionAngleType = positionAngleType;
}
/** Get the initial State Transition Matrix.
* @return initial State Transition Matrix
*/
......@@ -140,10 +117,10 @@ class NumericalPropagationHarvester implements MatricesHarvester {
if (state.isOrbitDefined()) {
// make sure the state is in the desired orbit type
final Orbit orbit = orbitType.convertType(state.getOrbit());
final Orbit orbit = propagator.getOrbitType().convertType(state.getOrbit());
// compute the Jacobian, taking the position angle type into account
orbit.getJacobianWrtCartesian(positionAngleType, dYdC);
orbit.getJacobianWrtCartesian(propagator.getPositionAngleType(), dYdC);
} else {
// for absolute PVA, parameters are already Cartesian
for (int i = 0; i < STATE_DIMENSION; ++i) {
......@@ -198,7 +175,7 @@ class NumericalPropagationHarvester implements MatricesHarvester {
@Override
public RealMatrix getParametersJacobian(final SpacecraftState state) {
if (columnsNames.isEmpty()) {
if (columnsNames == null || columnsNames.isEmpty()) {
return null;
}
......@@ -223,10 +200,19 @@ class NumericalPropagationHarvester implements MatricesHarvester {
}
/** Freeze the names of the Jacobian columns.
* <p>
* This method is called when proagation starts, i.e. when configuration is completed
* </p>
*/
void freezeColumnsNames() {
columnsNames = getJacobiansColumnsNames();
}
/** {@inheritDoc} */
@Override
public List<String> getJacobiansColumnsNames() {
return Collections.unmodifiableList(columnsNames);
return columnsNames == null ? propagator.getJacobiansColumnsNames() : columnsNames;
}
}
......@@ -395,10 +395,26 @@ public class NumericalPropagator extends AbstractIntegratedPropagator {
if (stmName == null) {
throw new OrekitException(OrekitMessages.NULL_ARGUMENT, "stmName");
}
harvester = new NumericalPropagationHarvester(stmName, initialStm, initialJacobianColumns);
harvester = new NumericalPropagationHarvester(this, stmName, initialStm, initialJacobianColumns);
return harvester;
}
/** Get the names of the parameters in the matrix returned by {@link #getParametersJacobian}.
* @return names of the parameters (i.e. columns) of the Jacobian matrix
*/
protected List<String> getJacobiansColumnsNames() {
final List<String> columnsNames = new ArrayList<>();
for (final ForceModel forceModel : getAllForceModels()) {
for (final ParameterDriver driver : forceModel.getParametersDrivers()) {
if (driver.isSelected() && !columnsNames.contains(driver.getName())) {
columnsNames.add(driver.getName());
}
}
}
Collections.sort(columnsNames);
return columnsNames;
}
/** {@inheritDoc} */
@Override
protected void setUpStmAndJacobianGenerators() {
......@@ -407,18 +423,12 @@ public class NumericalPropagator extends AbstractIntegratedPropagator {
// set up the additional equations and additional state providers
final StateTransitionMatrixGenerator stmGenerator = setUpStmGenerator();
final List<String> triggersDates = setUpTriggerDatesJacobiansColumns(stmGenerator.getName());
final List<String> regularParameters = setUpRegularParametersJacobiansColumns(stmGenerator, triggersDates);
// sort the Jacobians columns
final List<String> columnsNames = new ArrayList<>(triggersDates.size() + regularParameters.size());
columnsNames.addAll(triggersDates);
columnsNames.addAll(regularParameters);
Collections.sort(columnsNames);
final List<String> triggersDates = setUpTriggerDatesJacobiansColumns(stmGenerator.getName());
setUpRegularParametersJacobiansColumns(stmGenerator, triggersDates);
harvester.setColumnsNames(columnsNames);
harvester.setOrbitType(getOrbitType());
harvester.setPositionAngleType(getPositionAngleType());
// as we are now starting the propagation, everything is configured
// we can freeze the names in the harvester
harvester.freezeColumnsNames();
}
......@@ -558,11 +568,10 @@ public class NumericalPropagator extends AbstractIntegratedPropagator {
/** Set up the Jacobians columns generator for regular parameters.
* @param stmGenerator generator for the State Transition Matrix
* @param triggerDates names of the columns already managed as trigger dates
* @return names of the columns corresponding to regular parameters
* @since 11.1
*/
private List<String> setUpRegularParametersJacobiansColumns(final StateTransitionMatrixGenerator stmGenerator,
final List<String> triggerDates) {
private void setUpRegularParametersJacobiansColumns(final StateTransitionMatrixGenerator stmGenerator,
final List<String> triggerDates) {
// first pass: gather all parameters (excluding trigger dates), binding similar names together
final ParameterDriversList selected = new ParameterDriversList();
......@@ -582,10 +591,8 @@ public class NumericalPropagator extends AbstractIntegratedPropagator {
selected.sort();
// add the Jacobians column generators corresponding to parameters, and setup state accordingly
final List<String> names = new ArrayList<>(selected.getNbParams());
for (final DelegatingDriver driver : selected.getDrivers()) {
names.add(driver.getName());
IntegrableJacobianColumnGenerator generator = null;
// check if we already have set up the providers
......@@ -615,8 +622,6 @@ public class NumericalPropagator extends AbstractIntegratedPropagator {
}
return names;
}
/** {@inheritDoc} */
......
......@@ -16,6 +16,9 @@
*/
package org.orekit.propagation.numerical;
import java.util.List;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.linear.MatrixUtils;
import org.hipparchus.linear.RealMatrix;
import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator;
......@@ -26,6 +29,10 @@ import org.junit.Before;
import org.junit.Test;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.forces.maneuvers.Maneuver;
import org.orekit.forces.maneuvers.propulsion.BasicConstantThrustPropulsionModel;
import org.orekit.forces.maneuvers.propulsion.PropulsionModel;
import org.orekit.forces.maneuvers.trigger.DateBasedManeuverTriggers;
import org.orekit.frames.FramesFactory;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.orbits.Orbit;
......@@ -97,11 +104,34 @@ public class NumericalPropagationHarvesterTest {
doTestInitialStm(null, 0.0);
}
@Test
public void testColumnsNames() {
NumericalPropagationHarvester harvester =
(NumericalPropagationHarvester) propagator.setupMatricesComputation("stm",
MatrixUtils.createRealIdentityMatrix(6),
new DoubleArrayDictionary());
Assert.assertTrue(harvester.getJacobiansColumnsNames().isEmpty());
DateBasedManeuverTriggers triggers = new DateBasedManeuverTriggers("apogee_boost", propagator.getInitialState().getDate().shiftedBy(60.0), 120.0);
PropulsionModel propulsion = new BasicConstantThrustPropulsionModel(400.0, 350.0, Vector3D.PLUS_I, "ABM-");
propagator.addForceModel(new Maneuver(null, triggers, propulsion));
Assert.assertTrue(harvester.getJacobiansColumnsNames().isEmpty());
triggers.getParametersDrivers().get(1).setSelected(true);
propulsion.getParametersDrivers().get(0).setSelected(true);
List<String> columnsNames = harvester.getJacobiansColumnsNames();
Assert.assertEquals(2, columnsNames.size());
Assert.assertEquals("ABM-" + BasicConstantThrustPropulsionModel.THRUST, columnsNames.get(0));
Assert.assertEquals("apogee_boost_STOP", columnsNames.get(1));
}
private void doTestInitialStm(OrbitType type, double deltaId) {
NumericalPropagationHarvester harvester =
(NumericalPropagationHarvester) propagator.setupMatricesComputation("stm", null, null);
harvester.setOrbitType(type);
harvester.setPositionAngleType(PositionAngle.TRUE);
propagator.setOrbitType(type);
propagator.setPositionAngleType(PositionAngle.TRUE);
double[] p = new double[36];
for (int i = 0; i < p.length; i += 7) {
p[i] = 1.0;
......
......@@ -48,7 +48,12 @@ class PickUpHandler implements OrekitStepHandler, StateTransitionMatrixGenerator
this.columnName = columnName;
this.s0 = null;
this.accPartial = null;
Assert.assertTrue(harvester.getJacobiansColumnsNames().isEmpty());
if (columnName == null) {
Assert.assertTrue(harvester.getJacobiansColumnsNames().isEmpty());
} else {
Assert.assertEquals(1, harvester.getJacobiansColumnsNames().size());
Assert.assertEquals(columnName, harvester.getJacobiansColumnsNames().get(0));
}
}
public SpacecraftState getState() {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment