PropagatorsParallelizer

Hi Everyone, I’m starting to get my bearings with Orekit (using python) and am now pushing into more complex scenarios with multiple satellites. I’ve tried using the parallelizer like so:

iniDate = datetime_to_absolutedate(sce.time[0])
finDate = datetime_to_absolutedate(sce.time[1])

propagators = [sce.spacecraft[0].state,
               sce.spacecraft[1].state]

step_handler = MultiSatStepHandler(propagators, iniDate)
master_loop = PropagatorsParallelizer(propagators, step_handler)
master_loop.propagate(iniDate, finDate)

But get a “not implemented” error when calling the MultiSatStepHandler. I’ve also tried calling PropagatorParallelizer without the 2nd argument but that gave me an invalid argument error. Could someone point me in the right direction with an example? Thanks!

Hi @Sydra,

I am a big noob in Python. But I can try to help you.

MultiSatStepHandler is an Orekit Java interface. Therefore, you must implement it. This interface has 2 methods. A default one, init(), that you may not implement and another one that must be implemented handleStep(). That is probably the reason why you have a “not implemented” error. Please note that the implementation of the handleStep() method can be empty. if you don’t want to perform any action at the integration steps (an action can be printing satellites orbits for instance).

To implement the Orekit MultiSatStepHandler class in Python, you will need to implement the Python class PythonMultiSatStepHandler (Python wrapping). Here an example developed by @petrus.hyvonen showing how to implement a Python handler. The example is for a PythonOrekitFixedStepHandler but this is the same method for the PythonMultiSatStepHandler.

Kind regards,
Bryan

1 Like

Hi!

Yes, @Brian is likely on the right path. I haven’t used MutiSatStepHandler (yet but it looks very interesting). In the java test examples (which is a good source of examples) there is for example one java usage like:

      MultiSatStepHandler handler = (interpolators, isLast) -> {
        Vector3D p0 = interpolators.get(0).getCurrentState().getPVCoordinates().getPosition();
        Vector3D p1 = interpolators.get(1).getCurrentState().getPVCoordinates().getPosition();
        Assert.assertEquals(0.0, Vector3D.distance(p0, p1), positionTolerance);
    };

This on-the-fly creation of classes do not exist in Python so a full class needs to be implemented, based on the Python wrapped class PythonMultiStepHandler, where the interface methods marked native needs to be implemented. See

And when looking at that file i realize they are not exposed. Will fix it in short.

This is updated now in the latest version of the python-orekit-wrapper java source, and in build 2 of orekit 10.2 on conda forge (availabel through the “conda install -c conda-forge orekit” command).

Awesome - thank you Petrus! I will take a look this weekend and see if I can make it work :slight_smile:

I updated my orekit install, took another run at this, and did get a little bit further, but … still no cigar. In the code below, PropagatorParallelizer expects a “globalHandler” object as the 2nd input. However, the call to PythonMultiSatStepHandler returns a “FinalizerProxy” object instead, and when trying to create the master_loop I get an invalid argument error. In the error message itself the object types seem to be correct though. Any ideas?

from orekit.pyhelpers import datetime_to_absolutedate
from org.orekit.propagation import PropagatorsParallelizer
from org.orekit.propagation.sampling import PythonMultiSatStepHandler

iniDate = datetime.datetime(2023, 6, 1,  0, 0, 0)
finDate = datetime.datetime(2023, 6, 1, 12, 0, 0)
iniDate = datetime_to_absolutedate(iniDate)
finDate = datetime_to_absolutedate(finDate)

#KeplerianPropagator objects
propagators = [sce.spacecraft[0].state, 
               sce.spacecraft[1].state] 

step_handler = PythonMultiSatStepHandler(propagators, iniDate)
master_loop  = PropagatorsParallelizer(propagators, step_handler)
master_loop.propagate(iniDate, finDate)

I get the following error when defining the master_loop:

master_loop  = PropagatorsParallelizer(propagators, step_handler)
InvalidArgsError: (<class 'org.orekit.propagation.PropagatorsParallelizer'>, '__init__', (
    [<KeplerianPropagator: org.orekit.propagation.analytical.KeplerianPropagator@5460cf3a>,
     <KeplerianPropagator: org.orekit.propagation.analytical.KeplerianPropagator@7e4204e2>], 
    <PythonMultiSatStepHandler: org.orekit.propagation.sampling.PythonMultiSatStepHandler@daae1e8>))

Thanks for the help!

Hi Sydra,

Yes, you need to define your own PythonMultiStepHandler in python, by subclassing this class and implementing the needed methods init(states0, t) and handleStep(interpolators, isLast).

There is a way in java to specify this “inline” where you only specify one of the methods (handleStep in this case), and init is the default implementation. This is not really possible in Python so you need to subclass class

i.e.

  PythonMultiStepHandler():

    init(it(self, states0, t):
     pass
    
   handleStep((self, interpolators, isLast):
       # do something

Regards
/Petrus

Hello,

I’m digging up this post because I am new to Orekit and I am also having trouble with the PropagatorsParallelizer.
I tried on a simple example of two KeplerianPropagators and I have sub-classed PythonMultiSatStepHandler but I still encounter an InvalidArgsError on initialization of the parallelizer:

AE = Constants.WGS84_EARTH_EQUATORIAL_RADIUS
MU = Constants.WGS84_EARTH_MU
UTC = TimeScalesFactory.getUTC()

 class SimpleStepHandler(PythonMultiSatStepHandler):
    
    def init(self, states0, t):
        pass
    
    def handleStep(self, interpolators, isLast):
        for interpolator in interpolators:
            orbit = interpolator.getCurrentState().getOrbit()
            print(orbit)
        
ra = 500 * 1000
rp = 400 * 1000
i = radians(87.0)
omega = radians(90.0)
raan = radians(0.0)
lv = radians(0.0)
a = (ra + rp + 2*AE) / 2.0
e = 1.0 - (rp + AE) / a
epoch_date = AbsoluteDate(2004, 1, 1, 23, 30, 00.000, UTC)
inertial_frame = FramesFactory.getEME2000()
duration = 60.0 * 60 * 1
orbit1 = KeplerianOrbit(a, e, i, omega, raan, lv, PositionAngle.TRUE,
                        inertial_frame, epoch_date, MU)
orbit2 = KeplerianOrbit(a, e, i, omega, raan+pi, lv, PositionAngle.TRUE,
                        inertial_frame, epoch_date, MU)
prop1 = KeplerianPropagator(orbit1)
prop2 = KeplerianPropagator(orbit2)
step_handler = SimpleStepHandler()
parallelizer = PropagatorsParallelizer([prop1, prop2], step_handler)
parallelizer.propagate(epoch_date, epoch_date.shiftedBy(duration))

Have I missed something obvious :sweat_smile: ? Thank you in advance !

Hi Alegrand

You have not missed anything obvious at all, but there are some conversion issues here between python and java that are far from obvious.

The first one is the list of propagators. I am not sure how java lists work, but sometimes it is better to define lists as java lists first, and use them from there. See code below.

The second issue is of similar nature. The interpolator that is actually used in the handler is a private class and the conversion to the “specified” type that we would have in the java defintion of the method does not occur as we don’t have typed methods in Python (this is at least what I think is happening). So we need to cast this to what is expected. This is a bit odd and is very seldom needed practically, but seems to be in this case…

import orekit
orekit.initVM()
from orekit.pyhelpers import setup_orekit_curdir
setup_orekit_curdir()

from math import radians, pi

from org.orekit.utils import Constants

from org.orekit.time import TimeScalesFactory, AbsoluteDate
from org.orekit.frames import FramesFactory
from org.orekit.propagation.sampling import PythonMultiSatStepHandler, OrekitStepInterpolator
from org.orekit.orbits import KeplerianOrbit, PositionAngle
from org.orekit.propagation.analytical import KeplerianPropagator
from org.orekit.propagation import PropagatorsParallelizer
from java.util import Arrays

AE = Constants.WGS84_EARTH_EQUATORIAL_RADIUS

MU = Constants.WGS84_EARTH_MU

UTC = TimeScalesFactory.getUTC()

class SimpleStepHandler(PythonMultiSatStepHandler):

def init(self, states0, t):

    pass



def handleStep(self, interpolators, isLast):
    for interpolator in interpolators:
        orbit = OrekitStepInterpolator.cast_(interpolator).getCurrentState().getOrbit()

        print(orbit)

ra = 500 * 1000

rp = 400 * 1000

i = radians(87.0)

omega = radians(90.0)

raan = radians(0.0)

lv = radians(0.0)

a = (ra + rp + 2*AE) / 2.0

e = 1.0 - (rp + AE) / a

epoch_date = AbsoluteDate(2004, 1, 1, 23, 30, 00.000, UTC)

inertial_frame = FramesFactory.getEME2000()

duration = 60.0 * 60 * 1

orbit1 = KeplerianOrbit(a, e, i, omega, raan, lv, PositionAngle.TRUE,

                    inertial_frame, epoch_date, MU)

orbit2 = KeplerianOrbit(a, e, i, omega, raan+pi, lv, PositionAngle.TRUE,

                    inertial_frame, epoch_date, MU)

prop1 = KeplerianPropagator(orbit1)

prop2 = KeplerianPropagator(orbit2)

step_handler = SimpleStepHandler()

parallelizer = PropagatorsParallelizer(Arrays.asList([prop1, prop2]), step_handler)

parallelizer.propagate(epoch_date, epoch_date.shiftedBy(duration))

Thank you very much for your help, it is now indeed working !

Hi Petrus

  1. When I ran your sample code, I found that the result was printed on the command window. How can I disable it?
  2. And the result seems like java list format is it right? If so, could you tell me how to change it to Python data type(i.e. list)?

Hi Siwoo,

I am not sure i understand your questions, it is easier to help if you provide a testable example of your results and what you get / would like to get for results.

1 - The result is printed by the print statement. Where this is printed in your system will depend on a number of factors, in my anaconda installation and jupyter notebook it is printed in the notebook.

2 - In the example above no results are stored in any list, they are just printed. Few orekit functions returns java lists, but these can be transformed into Python lists.

Hello @petrus.hyvonen,

I know that this is a somewhat old topic, but I figured it would be the best to continue here rather than creating a new topic.

For the parallelizer, what method should one use in order to get positional numbers for each of the propagators? For only 1 propagator, I usually do something like (assuming that I’ve already created a propagator, and specified the step):

while(startTime.compareTo(endTime) <= 0):
    pv = propagator.propagate(startTime).getPVCoordinates()
    pvPosition = pv.getPosition()
    startTime = startTime.shiftedBy(step)

However, I wasn’t successful in extracting the same kind of information from the parallelizer. I tried iterating the “propagate” function, but with no success. The only way I could do it was to iterate on each of the propagators propagation, but then the PropagatorsParallelizer has no use anymore.

Is there a method to extract the positional number for each of the individual propagators at every step?

Many thanks in advance.

Best regards,
/Leonardo.

Hi,

What you are doing here could be replace by an OrekitFixedStepHandler close to the TutorialStepHandler used the NumericalPropagation tutorial. Then, the step handler can be easily added to the propagator using:

propagator.getMultiplexer().add(step, new TutorialStepHandler());

Two things to note: (1) these are Java examples but they can easily adapted to Python using PythonOrekitFixedStepHandler I guess and (2) this example is based on Orekit 11.0.

The order of the propagators (i.e. interpolators in this case) in the handleStep(self, interpolators, isLast) method of the PythonMultiSatStepHandler must be the same as the order of the propagators when you initialized the PropagatorsParallelizer. Therefore, if you initialize the PropagatorsParallelizer like that:

parallelizer = PropagatorsParallelizer(Arrays.asList([prop1, prop2]), mulit_sat_step_handler)

I guess the first interpolator in the handleStep method will be related to prop1 and the second one to prop2.

Best regards,
Bryan