Accessing Additional States from ForceModels (Hipparchus Primary Equations)

Hi everyone,

First time posting here, first time using Orekit.

Context

My goal is to have a Spacecraft with multiple Fuel Tanks, each feeding one or more Engines and having a differential relationship between fuel & thrust.

I added several tank fuel levels as a double[] array called “fuel” to the initialState, with an AdditionalDerivativesProvider in the NumericPropagator to govern their depletion. This part works perfectly, and I can see the fuel level(s) diminishing.

The Problem

I need the thrust to depend on these fuel levels (e.g., individual engine starvation, or pressure-fed engine modeling). When I attempt to access the “fuel” additionalData in my PropulsionModel, it fails. The SpacecraftState passed to it only contains the orbital elements + total mass, the “fuel” map is missing.

I have separately encountered the exact same issue as Trouble with implementing an AdditionalDerivativesProvider in Python

Root Cause Analysis

I believe I have traced the root cause of the issue to how AbstractIntegratedPropagator and Hipparchus’s ExpandableODE handle state mapping:

  1. The NumericalPropagator maps the Orbit & Mass as Primary Equations and my extra states as Secondary Equations.
  2. Inside ExpandableODE.computeDerivatives() the primary equations are evaluated using only the extracted primary state array
  3. Secondary data is only injected after the primary derivatives (forces) are computed.

This effectively isolates the ForceModels (Primary Equations) from the AdditionalStates (secondary data).

While I understand this separation likely exists to ensure DerivativeProviders have a fully formed state to work with, it seems to prevent any coupled dynamics where an integrated state (like fuel, engine temperature, solar panel deployment) influences the external forces (thrust, drag coefficient, etc.).

Proposed Logic

In Hipparchus ExpandableODE.java, the current logic extracts the primary state before computation:

    public double[] computeDerivatives(double t, double[] y) throws MathIllegalArgumentException, MathIllegalStateException {
        double[] yDot = new double[this.mapper.getTotalDimension()];
        double[] primaryState = this.mapper.extractEquationData(0, y);
        double[] primaryStateDot = this.primary.computeDerivatives(t, primaryState); // Force models only see 'primary states'
        // AdditionalDerivativesProviders see everything

If we instead passed the full y array to the primary equations, ForceModels would see the secondary states. I realize this would require changes to Hipparchus, now receiving extra states in a longer vector. I cannot assess the extents of that change. Alternatively Orekit might allow some additional states to become Primary akin to SpacecraftState.addPrimaryData() but that may also be a significant refactor.

Workaround

I cannot use a Maneuver, as they only see the one ‘mass’ state, and I need the individual tank fuel levels. I thought of masquerading the fuel states as a multidimensional Mass Field to access them there anyway, but that is wasteful and bound to fail as all other physical quantities would receive useless extra dimensions.

My only workaround for now, is to use GoldenKerbal’s method (thank you!) and bypass ForceModel entirely, for all my engines. I use a “coupled” AdditionalDerivativesProvider that computes both the fuel consumption and the thrust physics and returns it via CombinedDerivatives (especially using mainStateDerivativesIncrements)

While this works, it forces me to abandon the standard PropulsionModel / Maneuver architecture, which is not ideal.

Is there a more recommended “Orekit way” to have integrated states drive forces without bypassing the standard ForceModel interface? Some class I could subclass?

Thanks!

Hi there

I think CombinedDerivatives with AdditionalStateDerivative is the right way to go.
PropulsionModel has been thought for more conventional propagation model parameters I would say. You might still be able to use it though, if you keep track there of which tank got used and when. And just sum all contributions via the total mass rate.

Cheers,
Romain.

At Hipparchus level, I think this separation is still relevant. If some equations have an influence on the initial part of that state, they belong to the primary equations set. There is however a trick that could perhaps be used in your case to work around that. Since 2017, secondary equations are allowed to have an effect on primary equations (see issue 31). The way it works now is explained in the javadoc for SecondaryODE.computeDerivatives(t, primary, primaryDot, secondary) method, which explicitly allows the method to modify the content of the primaryDot array.

We may perhaps find a way to use that trick from Orekit level. Any idea?