MultiSatStepHandler NullPointerException

Hello everyone,

I’m trying to setup a MultiSatSetpHandler for my PropagatorParallelizer. I’ve looked at this and I’ve tried to modify the handler provided there to fit my needs, but I get a NullPointer error when I run the propagation at parallelizer level.
What I need to do is propagate a set of satellites and eventually add maneuvers to their propagators and see the evolution of their orbits (in particular I want to see how the mean elements evolve).
This is my handler:

class GlobalCustomStepHandler(PythonMultiSatStepHandler):
    def __init__(self, dsstModel):
        self.dsstModel = dsstModel;
        
    def init(self, states0, t):
        pass;
        
    def handleStep(self, interpolators):
        raanDiff = [];
        interpolators = list(interpolators);
        
        stateChief = OrekitStepInterpolator.cast_(interpolators[0]).getCurrentState();
        stateChief = DSSTPropagator.computeMeanState(stateChief, None, self.dsstModel, 1e-6, 1000);
        orbitChief = CircularOrbit(stateChief.getOrbit());
        raanChief = orbitChief.getRightAscensionOfAscendingNode();
        
        for interpolator in interpolators[1:]:
            stateNow = OrekitStepInterpolator.cast_(interpolator).getCurrentState();
            stateNow = self.propagatorDsst.computeMeanState(stateNow, None, self.propagatorDsst.getAllForceModels(), 1e-6, 1000);
            orbitNow = CircularOrbit(stateNow.getOrbit());
            raanDiff.append(orbitNow.getRightAscensionOfAscendingNode() - raanChief);
            
        self.raanDiff = raanDiff;
        
    def getRaanDiff(self) -> list:
        return self.raanDiff;
    
    def finish(self, finalStates):
        pass;

I’ve tried to use this handler by removing the __init__ method, and by removing the DSST conversion in handleStep and it works. I therefore suspect that it’s the initialization that is messing up the handler. I don’t really know how the Python wrapper works, so I have no idea of how to solve this issue.
I’ve tried using the super() Python function (I saw this working on a custom event detector, so I gave it a shot) but it still raises the NullPointerException and also gives a warning related to the usage of the super() function itself (it’s an autoreload warning).
Does anyone have any suggestion on how to get the handler to work properly?

Best regards,
Emiliano

Hi @Emiliano,

Do you have a stack trace of the NPE or an indication on where it is thrown ?
Could it be the None in place of the attitude provider in DSSTPropagator.computeMeanState
Also, are you aware that with DSST you can directly propagate in mean propagation type ?
It would be much faster than doing a conversion from osculating to mean at each step.

Hello @MaximeJ,

This is the error:

JavaError: <super: <class 'JavaError'>, <JavaError object>>
    Java stacktrace:
java.lang.NullPointerException
	at org.orekit.propagation.PropagatorsParallelizer.propagate(PropagatorsParallelizer.java:160)

I tried to replace the None with an AttitudeProvider given to the handler during creation, but I still get the error.

Also, are you aware that with DSST you can directly propagate in mean propagation type ?
It would be much faster than doing a conversion from osculating to mean at each step.

Yes I know this can be done, but since I need to add maneuvers to the propagators later on, I think I should use a NumericalPropagator and then estimate the mean orbit. On this note, I found another issue, probably because of my lack of understanding of how the parallelizer works.
As I said before, if I remove the call to DSSTPropagator and the __init__ method in the handler, the propagation works fine. So what I tried to do is this:

  1. Setup the parallelizer with the setup that works (described above)
  2. Propagate for 1 day
  3. Create a maneuver and add it to one of the propagators that is handled by the parallelizer
  4. Propagate again for 1 more day

However, for some reason when I try to run the second propagation, the code stalls. It seems like the propagation doesn’t even start. Indeed, I tried to add a print statement in the handleStep method just to make sure, and it confirmed that the second propagation (step 4) doesn’t run. The first propagation (step 2) works perfectly fine instead. Since this is a separate issue maybe it’s worth opening another thread, so as to not create too much confusion in this one?

Best regards,
Emiliano

Probably yes.
I think we’ll also need a piece of runnable code reproducing the error(s)
It’s hard to help you debug the code without it.

Best regards,
Maxime

This is a very simple code that runs my handler class and runs into the error (beware, there is a small modification to GlobalCustomStepHandler with respect to the one I provided before, since I added the attitude providers as you suggested)

import orekit;
orekit.initVM();

from orekit.pyhelpers import setup_orekit_curdir;
setup_orekit_curdir();

from org.hipparchus.ode.nonstiff import DormandPrince853Integrator;

from org.orekit.attitudes import BodyCenterPointing;
from org.orekit.bodies import CelestialBodyFactory, OneAxisEllipsoid;
from org.orekit.forces.drag import DragForce, IsotropicDrag;
from org.orekit.forces.gravity import HolmesFeatherstoneAttractionModel, OceanTides, SolidTides, ThirdBodyAttraction;
from org.orekit.forces.gravity.potential import GravityFieldFactory;
from org.orekit.forces.radiation import SolarRadiationPressure, IsotropicRadiationSingleCoefficient;
from org.orekit.frames import FramesFactory;
from org.orekit.models.earth.atmosphere import HarrisPriester;
from org.orekit.orbits import CircularOrbit, PositionAngle;
from org.orekit.propagation import PropagatorsParallelizer, SpacecraftState;
from org.orekit.propagation.numerical import NumericalPropagator;
from org.orekit.propagation.semianalytical.dsst.forces import DSSTAtmosphericDrag, DSSTSolarRadiationPressure, DSSTTesseral, DSSTThirdBody, DSSTZonal;
from org.orekit.time import AbsoluteDate, TimeScalesFactory;
from org.orekit.utils import Constants, IERSConventions;

from globalStepHandlers import GlobalCustomStepHandler;

from math import pi;
from numpy import degrees;
from java.util import Arrays;

# %% INITIAL STATES
# =============================================================================
# =============================================================================
Re = Constants.WGS84_EARTH_EQUATORIAL_RADIUS;
mu = Constants.WGS84_EARTH_MU;
earth = OneAxisEllipsoid(Re,
                          Constants.WGS84_EARTH_FLATTENING,
                          FramesFactory.getITRF(IERSConventions.IERS_2010, True));
earthCenterAttitudeLaw0 = BodyCenterPointing(FramesFactory.getEME2000(), earth);
earthCenterAttitudeLaw1 = BodyCenterPointing(FramesFactory.getEME2000(), earth);

mass = 150.;
area = 1.;
CD = 2.2;
CR = 1.2;

a0 = Constants.WGS84_EARTH_EQUATORIAL_RADIUS + 500e3;
a1 = a0 + 5e3;
ex = 1e-4;
ey = 1e-4;
i = 1.5;
raan = pi/6;
u = pi/18;

orbit0 = CircularOrbit(a0, ex, ey, i, raan, u, PositionAngle.MEAN,
                        FramesFactory.getEME2000(), AbsoluteDate.ARBITRARY_EPOCH, Constants.WGS84_EARTH_MU);
initState0 = SpacecraftState(orbit0, mass);
orbit1 = CircularOrbit(a1, ex, ey, i, raan, u, PositionAngle.MEAN,
                        FramesFactory.getEME2000(), AbsoluteDate.ARBITRARY_EPOCH, Constants.WGS84_EARTH_MU);
initState1 = SpacecraftState(orbit1, mass);

# %% DYNAMICAL MODEL
# =============================================================================
# =============================================================================
n, m = 4, 4;
nonSphericalGravity = HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010, True),
                                                        GravityFieldFactory.getConstantNormalizedProvider(n,m));        
sunThirdBody = ThirdBodyAttraction(CelestialBodyFactory.getSun());
moonThirdBody = ThirdBodyAttraction(CelestialBodyFactory.getMoon());
dragForce = DragForce(HarrisPriester(CelestialBodyFactory.getSun(), earth), IsotropicDrag(area, CD));
srpForce = SolarRadiationPressure(CelestialBodyFactory.getSun(),
                                  Re, IsotropicRadiationSingleCoefficient(area, CR));
oceanicTides = OceanTides(FramesFactory.getITRF(IERSConventions.IERS_2010, True),
                          Re, mu, n, m, IERSConventions.IERS_2010, 
                          TimeScalesFactory.getUT1(IERSConventions.IERS_2010, True));
solidTides = SolidTides(FramesFactory.getITRF(IERSConventions.IERS_2010, True),
                        Re, mu, GravityFieldFactory.getConstantNormalizedProvider(n, m).getTideSystem(),
                        IERSConventions.IERS_2010, TimeScalesFactory.getUT1(IERSConventions.IERS_2010, True),
                        [CelestialBodyFactory.getSun(), CelestialBodyFactory.getMoon()]);
forces = [nonSphericalGravity, sunThirdBody, moonThirdBody, dragForce, srpForce, oceanicTides, solidTides];

zonalDsst = DSSTZonal(GravityFieldFactory.getConstantUnnormalizedProvider(n,m));
tesseralDsst = DSSTTesseral(FramesFactory.getITRF(IERSConventions.IERS_2010, True),
                            Constants.WGS84_EARTH_ANGULAR_VELOCITY,
                            GravityFieldFactory.getConstantUnnormalizedProvider(n,m));
sunThirdBodyDsst = DSSTThirdBody(CelestialBodyFactory.getSun(), mu);
moonThirdBodyDsst = DSSTThirdBody(CelestialBodyFactory.getMoon(), mu);
dragForceDsst = DSSTAtmosphericDrag(HarrisPriester(CelestialBodyFactory.getSun(), earth),
                                    IsotropicDrag(area, CD), mu);
srpForceDsst = DSSTSolarRadiationPressure(CelestialBodyFactory.getSun(), Re,
                                          IsotropicRadiationSingleCoefficient(area, CR), mu);
forcesDsst = [zonalDsst, tesseralDsst, sunThirdBodyDsst, moonThirdBodyDsst, dragForceDsst, srpForceDsst];

# %% PROPAGATORS
# =============================================================================
# =============================================================================
minStep = 1e-2;
maxStep = 1e+2;
absTol = 1e-9;
relTol = 1e-9;

integrator0 = DormandPrince853Integrator(minStep, maxStep, absTol, relTol);
integrator0.setInitialStepSize(60.0);

integrator1 = DormandPrince853Integrator(minStep, maxStep, absTol, relTol);
integrator1.setInitialStepSize(60.0);

propagator0 = NumericalPropagator(integrator0);
propagator0.setAttitudeProvider(earthCenterAttitudeLaw0);
for force in forces:
    propagator0.addForceModel(force);
propagator0.setInitialState(initState0);

propagator1 = NumericalPropagator(integrator1);
propagator1.setAttitudeProvider(earthCenterAttitudeLaw1);
for force in forces:
    propagator1.addForceModel(force);
propagator1.setInitialState(initState1);

myHandler = GlobalCustomStepHandler(Arrays.asList(forcesDsst), [prop.getAttitudeProvider() for prop in [propagator0, propagator1]]);
parallelizer = PropagatorsParallelizer(Arrays.asList([propagator0, propagator1]), myHandler);

day = 24*60*60.;
initDate = orbit0.getDate();
finalDate = initDate.shiftedBy(day);

parallelizer.propagate(initDate, finalDate);

print(degrees(myHandler.getRaanDiff()));

I’ve read in some other thread that it’s probably better to not reuse the force models for the different propagators, but since they didn’t cause any issue with the simpler version of the handler I don’t think that’s what is causing my error.

The new GlobalCustomStepHandler is defined as:

from org.orekit.orbits import CircularOrbit;
from org.orekit.propagation.sampling import PythonMultiSatStepHandler, OrekitStepInterpolator;
from org.orekit.propagation.semianalytical.dsst import DSSTPropagator;

class GlobalCustomStepHandler(PythonMultiSatStepHandler):
    def __init__(self, dsstModel, attProv):
        self.dsstModel = dsstModel;
        self.attProv = attProv;
        
    def init(self, states0, t):
        pass;
        
    def handleStep(self, interpolators):
        raanDiff = [];
        interpolators = list(interpolators);

        stateChief = OrekitStepInterpolator.cast_(interpolators[0]).getCurrentState();
        stateChief = DSSTPropagator.computeMeanState(stateChief, self.attProv[0], self.dsstModel, 1e-6, 1000);
        orbitChief = CircularOrbit(stateChief.getOrbit());
        raanChief = orbitChief.getRightAscensionOfAscendingNode();
        
        for ii in range(1, len(interpolators)):
            stateNow = OrekitStepInterpolator.cast_(interpolators[ii]).getCurrentState();
            stateNow = self.propagatorDsst.computeMeanState(stateNow, self.attProv[ii], self.propagatorDsst.getAllForceModels(), 1e-6, 1000);
            orbitNow = CircularOrbit(stateNow.getOrbit());
            raanDiff.append(orbitNow.getRightAscensionOfAscendingNode() - raanChief);
        self.raanDiff = raanDiff;
        
    def getRaanDiff(self) -> list:
        return self.raanDiff;
        
    def finish(self, finalStates):
        pass;

Thank you very much for your help.

Best regards,
Emiliano

Hi Emiliano,

Haven’t got time to investigate deeper, but I think you should run the super in the __init__ method to finalize the setting up of the class.

super(yourclassname, self).__init__()

Hello @petrus.hyvonen,
I did try that yesterday, but when I did I got a weird warning about autoreload and then still bumped into the same NullPointerException. Today, unexpectedly, with the addition of the same call to super(GlobalCustomStepHandler, self).__init__() it does work. It doesn’t give the autoreload warning and it also doesn’t raise the NullPointerException.
I think there might be something wrong with my laptop since it’s the second time that the same script happens to work after the good old switch-off/switch-on :sweat_smile:

Hi @Emiliano,

I meant to ask, given your multiple problems in Python, how do you run your code? In a terminal, with an IDE, with a Jupyter NB, or else?

Cheers,
Romain.

Hi @Serrof,

I use Spyder. Actually, I’ve encountered weird Python problems only recently. Although, I remember that when I first installed Orekit, about a year ago, it wouldn’t let me use Python 3.10 but I had to go with 3.9 for some reason. I don’t exactly remember what was the issue, but the only way I found to solve it was to revert to an older Python version.

Best regards,
Emiliano

Ok well I haven’t used Spyder in a while so I can’t say much. But with Jupyter NBs, I know sometimes some stuff stays in cache and can cause surprise.

About the install, with conda and the like, if you don’t update it beforehand, you might not be able to retrieve the latest version of Orekit for the latest version of Python. But Petrus does an amazing job and is pretty reactive with new releases.

Best,
Romain.