In ephemeris, additional states managed by AdditionalStateProvider should be saved
In reference to discussion https://forum.orekit.org/t/additionnal-states-in-ephemerides-not-reinitilialized-and-or-updated/502/3
Code to reproduce the problem :
package space.exotrail.exoops.numerical.orekit;
import java.io.IOException;
import org.hipparchus.ode.AbstractIntegrator;
import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator;
import org.hipparchus.util.FastMath;
import org.junit.Assert;
import org.junit.Test;
import org.orekit.errors.OrekitException;
import org.orekit.frames.Frame;
import org.orekit.frames.FramesFactory;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.orbits.Orbit;
import org.orekit.orbits.PositionAngle;
import org.orekit.propagation.AdditionalStateProvider;
import org.orekit.propagation.BoundedPropagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.events.DateDetector;
import org.orekit.propagation.events.EventDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.propagation.numerical.NumericalPropagator;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScale;
import org.orekit.time.TimeScalesFactory;
public class AdditionalStatesTest {
public static final String STATE_NAME = "STATE";
public static final int STATE_INDEX = 0;
public static final int STATE_START = 10;
public static final int STATE_END = -10;
public class Handler implements EventHandler<EventDetector> {
private StateProviderTest additionalStateProvider;
public Handler (StateProviderTest additionalStateProvider) {
this.additionalStateProvider = additionalStateProvider;
}
@Override
public void init(final SpacecraftState s0, final AbsoluteDate t) {
additionalStateProvider.setFirstPart(true);
}
@Override
public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing)
throws OrekitException {
additionalStateProvider.setFirstPart(false);
return Action.CONTINUE;
}
}
public class StateProviderTest implements AdditionalStateProvider {
public boolean firstPart = true;
@Override
public String getName() {
return STATE_NAME;
}
@Override
public double[] getAdditionalState(SpacecraftState state) throws OrekitException {
if (isFirstPart()) {
double[] ret = { STATE_START };
return ret;
} else {
double[] ret = { STATE_END };
return ret;
}
}
public boolean isFirstPart() {
return firstPart;
}
public void setFirstPart(boolean firstPart) {
this.firstPart = firstPart;
}
}
@Test
public void TestAdditionalStates() throws OrekitException, IOException {
// Initial orbit parameters
double a = 24396159; // semi major axis in meters
double e = 0.72831215; // eccentricity
double i = FastMath.toRadians(7); // inclination
double omega = FastMath.toRadians(180); // perigee argument
double raan = FastMath.toRadians(261); // right ascension of ascending node
double lM = 0; // mean anomaly
// Inertial frame
Frame inertialFrame = FramesFactory.getEME2000();
// Initial date in UTC time scale
TimeScale utc = TimeScalesFactory.getUTC();
AbsoluteDate initialDate = new AbsoluteDate(2004, 01, 01, 23, 30, 00.000, utc);
// gravitation coefficient
double mu = 3.986004415e+14;
// Orbit construction as Keplerian
Orbit initialOrbit = new KeplerianOrbit(a, e, i, omega, raan, lM, PositionAngle.MEAN,
inertialFrame, initialDate, mu);
// Initialize state
SpacecraftState initialState = new SpacecraftState(initialOrbit);
// Numerical propagation with no perturbation (only Keplerian movement)
// Using a very simple integrator with a fixed step: classical Runge-Kutta
double stepSize = 10; // the step is ten seconds
AbstractIntegrator integrator = new ClassicalRungeKuttaIntegrator(stepSize);
NumericalPropagator propagator = new NumericalPropagator(integrator);
// Set the propagator to ephemeris mode
propagator.setEphemerisMode();
StateProviderTest stateProvider = new StateProviderTest();
propagator.addEventDetector(new DateDetector(initialDate.shiftedBy(3000))
.withHandler(new Handler(stateProvider)));
propagator.addAdditionalStateProvider(stateProvider);
propagator.setInitialState(initialState);
// Propagation with storage of the results in an integrated ephemeris
SpacecraftState firstPartState = propagator.propagate(initialDate.shiftedBy(2000));
propagator.resetInitialState(initialState);
SpacecraftState finalState = propagator.propagate(initialDate.shiftedBy(6000));
BoundedPropagator ephemeris = propagator.getGeneratedEphemeris();
double stateAfterPropagation = finalState.getAdditionalState(STATE_NAME)[STATE_INDEX];
double stateInFirstPartOfPropagation = firstPartState
.getAdditionalState(STATE_NAME)[STATE_INDEX];
double stateInFirstPartOfEphemerides = ephemeris.propagate(initialDate.shiftedBy(1000))
.getAdditionalState(STATE_NAME)[STATE_INDEX];
double stateInSecondPartOfEphemerides = ephemeris.propagate(initialDate.shiftedBy(4000))
.getAdditionalState(STATE_NAME)[STATE_INDEX];
Assert.assertEquals(STATE_START, stateInFirstPartOfPropagation, 1e-12);
Assert.assertEquals(STATE_END, stateAfterPropagation, 1e-12);
Assert.assertEquals(STATE_START, stateInFirstPartOfEphemerides, 1e-12);
Assert.assertEquals(STATE_END, stateInSecondPartOfEphemerides, 1e-12);
}
}
Analysis from Luc :
They should be stored, so I think you found a bug here.
I looked at the internal class AbstractIntegratedPropagator.EphemerisModeHandler which handles >ephemeris generation, at the end of the handleStep method, in the part performed only for the >last step, just before the propagation stops. In this part, there are two separate handlings >of additional states:
states managed by additional equations (i.e. differential equations), they will be >retrieved from the underlying integrator additional states states labelled as “unmanaged”, only their initial values as present in the initial state >will be retrieved
It now seems to me that we missed a third category: states manged by an analytical >AdditionalStateProvider, which is your case. I think we considered that as we can’t dump an >unknown additional state provider, we cannot store it in an ephemeris. If ephemerides were >used only on the exact sample points, this would not be a problem, we could simply dump the >value. However, ephemerides can be interpolated, and how do we interpolate states for which we >don’t have any equation?
There may be two different answers to this:
if the ephemeris is indeed used whay we are still in the same process, and the >AdditionalStateProvider instances are still available: then we could store a reference to them in the ephemeris >and call it so it computes the additional state just as it would do in the original propagation if the ephemeris was dumped on file and reloaded, then we could use a n points >interpolation (n to be defined at loading time) from the sampled states.