DSST propagation leads to hyperbolic orbits when used with certain detectors

Hello everyone,

I came accross a diverging behaviour of the DSST propagator when certain detectors are used.
It is likely linked to DSST propagator in osculating elements failed with several detectors (I will discuss it below), that we raised last year. I believe to have now a more complete view of the issue, as detailed hereafter.

Typical encounter is the following:

  1. A detector (e.g. ApsideDetector), associated with a handler with Action.RESET_STATE, is in use.
  2. In AbstractIntegrator#acceptStep, the detection of the even leads to the call of handleStep (comment in the code is “// handle the first part of the step, up to the event”). The handleStep function leads to an update of the short period via DSSTPropagator.ShortPeriodicsHandler#handleStep.
  3. Since a “reset state” action is called, the integration is resumed at the epoch of the event Te. However, it can happen that the event is (falsely) detected a second time (likely due to the reset of the state?), very close the current epoch (time difference is DT ~ 5e-11 s in the test below). This second occurence also triggers the update of the short periods, but the corresponding time span is [Te, Te+DT]. This timespan is infinitesimal, likely too short too make sense for an interpolation grid, and the next interpolation of the short periods breaks (typically in DSSTTesseral$getCijm or equivalent) and leads to unrealistic orbit.

Our understanding is that the short periods should not be updated by default in DSSTPropagator.ShortPeriodicsHandler#handleStep, which is called several times before, between and after the events during a single integration step within AbstractIntegrator#acceptStep.

I believe that the issue in DSST propagator in osculating elements failed with several detectors is similar except that, in this case, two detectors are used, which also triggers the update of the short period on infinitesimal time spans.

Beyond the bug itself, does anybody have an idea on how to workaround this (except avoiding RESET_STATE in the handlers)?

Please find below a script to reproduce the bug (typically added in DSSTPropagatorTest):slight_smile:

    
@Test
public void testApsideDetectorAndDsstLeadsToHyperbolicOrbit() {

   // Spacecraft state
   final SpacecraftState state = getLEOState();

   // Body frame
    final Frame itrf = FramesFactory .getITRF(IERSConventions.IERS_2010, true);

    // Earth
    final UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(4, 4);
    final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                                                        Constants.WGS84_EARTH_FLATTENING, itrf);
    // Detectors
    final List<EventDetector> events = new ArrayList<>();
    events.add(new ApsideDetector(state.getOrbit()).withHandler(new ApsideHandlerWithResetState()));

    // Force models
    final List<DSSTForceModel> forceModels = new ArrayList<>();
    forceModels.add(new DSSTZonal(provider));
    forceModels.add(new DSSTTesseral(itrf, Constants.WGS84_EARTH_ANGULAR_VELOCITY, provider));

    // Set up DSST propagator
    final double[][] tol = ToleranceProvider.getDefaultToleranceProvider(10.).getTolerances(state.getOrbit(), OrbitType.EQUINOCTIAL);
    final ODEIntegrator integrator = new DormandPrince54Integrator(60.0, 3600.0, tol[0], tol[1]);
    final DSSTPropagator propagator = new DSSTPropagator(integrator, PropagationType.OSCULATING);
    for (DSSTForceModel force : forceModels) {
        propagator.addForceModel(force);
    }
    for (EventDetector event : events) {
        propagator.addEventDetector(event);
    }
    propagator.setInitialState(state);

    // Propagation
    propagator.propagate(state.getDate().shiftedBy(3600));
}

public class ApsideHandlerWithResetState implements EventHandler {
    @Override
    public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing) {
        return Action.RESET_STATE;
    }
}

Expected stacktrace is:

org.orekit.errors.OrekitIllegalArgumentException: hyperbolic orbits cannot be handled as org.orekit.orbits.EquinoctialOrbit instances

	at org.orekit.orbits.EquinoctialOrbit.<init>(EquinoctialOrbit.java:199)
	at org.orekit.orbits.EquinoctialOrbit.<init>(EquinoctialOrbit.java:250)
	at org.orekit.orbits.OrbitType$3.mapArrayToOrbit(OrbitType.java:462)
	at org.orekit.orbits.OrbitType$3.mapArrayToOrbit(OrbitType.java:1)
	at org.orekit.propagation.semianalytical.dsst.DSSTPropagator$MeanPlusShortPeriodicMapper.mapArrayToState(DSSTPropagator.java:1036)
	at org.orekit.propagation.integration.StateMapper.mapArrayToState(StateMapper.java:169)
	at org.orekit.propagation.integration.AbstractIntegratedPropagator.convertToOrekitWithoutAdditional(AbstractIntegratedPropagator.java:723)
	at org.orekit.propagation.integration.AbstractIntegratedPropagator.convertToOrekitWithAdditional(AbstractIntegratedPropagator.java:713)

Hello, I likely find a fix, following the idea suggested in the first message.
The idea is to update the short period once, at the beginning of the AbstractIntegrator#acceptStep instead of each time in DSSTPropagator.ShortPeriodicsHandler#handleStep.

In practice, this means:

  • Create a new routine update(ODEStateInterpolator interpolator) in ODEStepHandler which does nothing by default (in Hipparchus).
  • Migrate the update of the short period terms from handleStep to update in DSSTPropagator.ShortPeriodicsHandler (in Orekit).
  • Call updateStep at the beginning of AbstractIntegrator#acceptStep (in Hipparchus).

This solves the failing test described above, while keeping all other Orekit tests green.

I do believe that the DSST will benefit from this fix, both in terms of robustness and performance, since the short period update can be time consuming if called at each event.

Do you think this fix could be integrated in the next Orekit/Hipparchus patch? I can contribute the fix next week.

Hi Bastien,

thanks for the analysis.
I believe the root cause of all this is issue #834.
Could your solution be adapted to actually solve this in the first place? And hopefully all its side effects will disappear.

Cheers,
Romain.

Hello Romain,

Thanks for your answer! Yes, I agree this is definitely the same issue as #834. In the later, Evan mentionned this in its last post:

Perhaps the way to fix it is to add a new category of step handler that are called before event detectors with the whole step. That way the mapping would be updated only once at the beginning of acceptStep(...). And it’s natural for the g functions to change then.

I do believe that my solution is equivalent to this, so #834 should also be solved without any adaptation. The only remaining question on my side is related to the last sentence of Evan:

The complication would be what to do if the step is truncated.

@evan.ward do you remember what you mean by this? I did not see any pitfalls when launching the Orekit tests, but maybe a dedicated test should be designed? I can definitely test anything on branch should you design one.

1 Like

Hi Bastien,

Yes, consider the scenario, similar to yours, where a step is passed to the RawStepHandler (before event detection), then an event is detected in the middle of the step that resets the state (or any other reset action), this causes the integrator to truncate the step and restart with the new state.

How is the RawStepHandler notified that the step was truncated? For DSST, some short period terms would have been calculated, and would need to be discarded (or perhaps restricted to a certain domain) since the vehicle is now on a different trajectory.

Those notifications are also useful for generating ephemeris that allows smooth interpolation. The CCSDS NDM USEABLE_{START,STOP}_TIME feature.

Not a blocker, just some software engineering to think through during the implementation.

Best Regards,
Evan

1 Like

Hello Evan,

Thank you for your thorough answer! I understand that I need to put more thoughts into the fix to handle truncated steps. I’ll come back to this thread when I have something substantial to show.

Talk with you soon,

Bastien