How to extract the mass before each maneuver during propagation without interruptions

Dear all,

I would ask you some advice regarding the following problem. I am propagating an orbit with several finite maneuvers having an initial maneuver date and an assigned deltaV. I would like to compute the duration corresponding to each maneuver and therefore I would need to extract the mass before each maneuver during propagation, without interrupting the propagation.

Do you know if and how I can use some kind of handler to have the capability to extract the mass before each maneuver at a specified date during the propagation? Or if you have any other advice on how to do that without interrupting the propagation?

Many thanks for your help.

Best Regards

Hi @fpan,

In case you’re using a ConstantThrustManeuver:

I think the best would be to design your own ManeuverTriggers class. In the EventHandler of these triggers you can store the mass before the event occurred (i.e. before the maneuver is triggered).
Then after propagation, you can access the triggers of each maneuver and retrieve the mass you want.

Hope this helps,
Maxime

Hello @fpan,

In case you approximate with ImpulsiveManeuver : a simple solution to this need would be to create a custom EventHandler (or lambda expression) that would be your EventDetector triggers event handlers and whose goal would be to register the spacecraft mass before performing each maneuver.

Focus on solution :

        DateDetector detector = new DateDetector(ignitionEpoch).withHandler((state, detector, increasing) -> {
            massList.add(state.getMass());
            return Action.CONTINUE;
        });

Complete example:

import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.ode.ODEIntegrator;
import org.hipparchus.ode.events.Action;
import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
import org.hipparchus.util.FastMath;
import org.orekit.attitudes.LofOffset;
import org.orekit.data.DataContext;
import org.orekit.data.DataProvider;
import org.orekit.data.DirectoryCrawler;
import org.orekit.forces.maneuvers.ImpulseManeuver;
import org.orekit.frames.FramesFactory;
import org.orekit.frames.LOFType;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.orbits.Orbit;
import org.orekit.orbits.OrbitType;
import org.orekit.orbits.PositionAngleType;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.ToleranceProvider;
import org.orekit.propagation.events.DateDetector;
import org.orekit.propagation.numerical.NumericalPropagator;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.Constants;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class RegisterStateMassBeforeManeuver {
    public static void main(String[] args) {
        // Load Orekit data
        loadOrekitData();

        // Create state
        final SpacecraftState state = getSpacecraftState();

        // Create propagator
        final NumericalPropagator propagator = getNumericalPropagator(state);

        // Store mass before each maneuver
        final List<Double> massList = new ArrayList<>();


        // Create some maneuvers
        final AbsoluteDate          initialDate = state.getDate();
        int                         isp         = 400;
        final ImpulseManeuver       maneuver1   = getManeuver(initialDate.shiftedBy(60), state, isp, massList);
        final ImpulseManeuver       maneuver2   = getManeuver(initialDate.shiftedBy(120), state, isp, massList);
        final ImpulseManeuver       maneuver3   = getManeuver(initialDate.shiftedBy(180), state, isp, massList);
        final List<ImpulseManeuver> maneuvers   = List.of(maneuver1, maneuver2, maneuver3);


        maneuvers.forEach(propagator::addEventDetector);

        // Propagate
        propagator.propagate(initialDate.shiftedBy(500));

        // Display masses
        System.out.println(massList);
    }

    private static ImpulseManeuver getManeuver(AbsoluteDate ignitionEpoch,
                                               SpacecraftState state,
                                               int isp,
                                               List<Double> massList) {
        // Create base event detector
        DateDetector detector = new DateDetector(ignitionEpoch).withHandler((s, d, b) -> {
            massList.add(s.getMass());
            return Action.CONTINUE;
        });

        return new ImpulseManeuver(detector,
                                   new LofOffset(state.getFrame(),
                                                 LOFType.TNW), new Vector3D(1, 0, 0), isp);
    }

    private static SpacecraftState getSpacecraftState() {
        final Orbit orbit = new KeplerianOrbit(6378e3 + 400e3,
                                               1e-3,
                                               FastMath.toRadians(60),
                                               0.,
                                               0.,
                                               0.,
                                               PositionAngleType.TRUE,
                                               FramesFactory.getGCRF(),
                                               new AbsoluteDate(),
                                               Constants.IERS2010_EARTH_MU);
        return new SpacecraftState(orbit);
    }

    private static NumericalPropagator getNumericalPropagator(final SpacecraftState state) {
        final ODEIntegrator integrator = getODEIntegrator(state);
        NumericalPropagator propagator = new NumericalPropagator(integrator);
        propagator.setInitialState(state);
        return propagator;

    }

    private static ODEIntegrator getODEIntegrator(final SpacecraftState state) {
        final double      minStep           = 1e-3;
        final double      maxStep           = 60;
        final double      dP                = 1;
        ToleranceProvider toleranceProvider = ToleranceProvider.getDefaultToleranceProvider(dP);
        final double[][]  tolerances        = toleranceProvider.getTolerances(state.getOrbit(), OrbitType.EQUINOCTIAL);
        return new DormandPrince853Integrator(minStep, maxStep, tolerances[0], tolerances[1]);
    }

    public static void loadOrekitData() {

        //Loading Data
        final File rootFile   = new File(System.getProperty("user.home"));
        final File orekitData = new File(rootFile, "orekit-data");
        if (!orekitData.exists()) {
            System.err.format("Orekit data file not found: %s\n", orekitData);
            System.out.format("Le fichier %s n'a pas été trouvé.%n", orekitData.getAbsolutePath());
        }
        final DataProvider dirCrawler = new DirectoryCrawler(orekitData);
        DataContext.getDefault().getDataProvidersManager().addProvider(dirCrawler);
    }
}

EDIT :
@MaximeJ did not see your answer :folded_hands:

Cheers,
Vincent

Dear Maxime and Vincent,

many thanks for your replies.

Best regards.