Hello everyone, I have several questions about using NumericalPropagator.
I have a scenario where I have to add/remove a Maneuver ForceModel in loop. For example, I want to find maneuver duration such that osculating semimajor axis after maneuver is close to desired one.
NumericalPropagator supports addForceModel(ForceModel), but not something like removeForceModel(ForceModel) (I see a problem with implementation of this method, because it can be several ForceModel of same type) or removeLastAddedForceModel(). So I have to do some workaround and write some extra code allowing me to do that.
So, my questions are:
Is there a way solve my problem for scenario I described above by the means of Orekit (or maybe you have some recommended approach to do that)?
Do you have any plans about add such a feature (to remove last force/specific force)? Maybe there were some reasons not to implement such methods?
You can look at the DateBasedManeuverTriggers to build your maneuver. This trigger exposes several ParameterDriver instances thanks to its getParametersDrivers method, and these drivers allow you to change the maneuver dates. You could either use the first two drivers that correspond to start and stop date, or you can use the last two drivers that correspond to median date and duration. When you use setValue in this drivers, you change the corresponding date/duration.
This feature can be used either to estimate maneuvers in orbit determination or to optimize maneuvers.
We wanted to add a tutorial showing this last use case, but did not wrote it yet.
I wonder however if in this specific case another approach would be faster.
As you don’t change the start date but only the duration of the maneuver and as you check the semi-major axis at maneuver end only, I would use an event detector.
The idea would be to run just one propagation, with a duration set to durationMax, and to add to the propagator an event detector checking the difference between current semi-major axis and desired semi-major axis, and an event handler that would return Action.STOP when the event occurs. Then during the propagation, the semi-major axis will change due to the maneuver, but as soon as it reaches the expected value, the propagation will stop. Then comparing the date of final state and the date of maneuver start, you will get the duration you seek.
You could even use this as a maneuver trigger to stop just the maneuver itself and continue propagation when you have reached your desired semi major axis.
I’ve implemented your idea. In this particular case, it works faster than with BracketingNthOrderBrentSolver, but just a little bit. However, I have some use cases, where the F is more costly to compute. I’ll try to change my old solution to this new idea and see how it affects performance.
In fact, I have a scenario where I need to change both Maneuver start date and duration. The code I wrote looks similar to one I wrote above. The difference it that I additionally use startTimeDriver (drivers.get(0)).
Beware when mixing one parameter driver from the first pair (start/stop) and from the second pair (middle/duration). These pairs are intended to work as pair of independent parameters. This is important when computing partial derivatives. If for example you compute ∂a/∂tₛ where tₛ is the start time, it assumes the end time is fixed, hence it has an effect on duration has duration will be end minus start. If on the other hand you compute ∂a/∂d where d is duration, it assumes middle time is fixed, hence it moves both start and end time symmetrically.
I have removed the “solved” flag in a previous post in this thread as I now think this solution was flawed. The problem is linked to the mix between start and duration.
As I explained in the previous post, start/stop work as a pair, and median/duration work as another pair. In fact, what is really done under the hood is that start and stop are the raw parameters, whereas median date and duration are derived parameters. What really happens when median date is changed by Δt is that both start and stop are shifted by Δt. What really happens when duration is changed by Δt is that start is shifted by -Δt/2 and stop is shifted by +Δt/2. So when we just reset duration, we change start date. If we change duration by Δt and reset initial state as if start date was not changed, we miss Δt/2 worth of propulsion time.
In order to match your needs, the solution would be to use start and stop, not start and duration. So I would suggest that you build the raw maneuver as if it was zero duration with
DateBasedManeuverTriggers trigger = new DateBasedManeuverTriggers(maneuverStartDate, 0.0);
and then to select the parameter driver that ends with ParameterDrivenDateIntervalDetector.STOP_SUFFIX and set the value of this parameter to the duration of the maneuver. As the zero value for this parameter is to have a zero duration maneuver, then setting it to Δt implies you have a maneuver with a total duration of Δt, so it is important that the raw maneuver really has zero duration.
With this change, you properly have a maneuver with fixed start date and parameterized stop date.
So, in my example above, to make things right, I should change:
DateBasedManeuverTriggers duration to 0:
DateBasedManeuverTriggers trigger = new DateBasedManeuverTriggers(
maneuverStartDate, 0.0); // zero duration!
durationDriver to stopDriver:
final ParameterDriver stopDriver = trigger.
getParametersDrivers().
stream().
filter(d -> d.getName().endsWith(ParameterDrivenDateIntervalDetector.STOP_SUFFIX)). // instead of DURATION_SUFFIX
findFirst().
get();
durationDriver.setValue(*) to stopDriver.setValue(*):
final double finalDuration = solver.solve(maxEval,
d -> {
stopDriver.setValue(d); // instead of durationDriver.setValue(d);
propagator.resetInitialState(initialState);
SpacecraftState s = propagator.propagate(maneuverStartDate.shiftedBy(d));
return s.getA() - desiredSemimajorAxis;
},
durationMin,
durationMax);
If so, I have a problem with SpacecraftState s = propagator.propagate(maneuverStartDate.shiftedBy(d)); propagate is only called once, and seems propagation becomes eternal. It never finishes.