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:
- The
NumericalPropagatormaps the Orbit & Mass as Primary Equations and my extra states as Secondary Equations. - Inside
ExpandableODE.computeDerivatives()the primary equations are evaluated using only the extracted primary state array - 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!