PiecewisePolynomialAcceleration#

class PiecewisePolynomialAcceleration(origin: Body, spacecraft: Spacecraft, base_frame: Frame, t0: EpochArray | float, batch_length: float, n_batches: int, order: int, coefficients: ndarray | None = None, frame: str = 'inertial', body_fixed_frame=None, relative_body=None)#

Bases: DynamicModel

Models an empirical acceleration as a piecewise polynomial in time over a fixed batch grid.

The arc is divided into \(N_b\) batches of length \(L\) starting at \(t_0\), exactly as in PiecewiseFirstOrderGaussMarkov. Within batch \(k\), with the local time \(\tau = (t - t_0 - kL)/L \in [0, 1]\), the acceleration is a polynomial of the chosen order:

order=0 (zero-order hold)

One constant 3-vector per batch,

\[\mathbf a(t) = \mathbf c_k ,\]

which jumps at the batch boundaries.

order=1 (first-order hold)

One 3-vector per batch boundary (node), linearly interpolated in between,

\[\mathbf a(t) = (1 - \tau)\,\mathbf n_k + \tau\,\mathbf n_{k+1} ,\]

so the acceleration is continuous across batches by construction: the end of batch \(k\) and the start of batch \(k+1\) share the node \(\mathbf n_{k+1}\).

The coefficients are components in the frame chosen with frame, and the applied acceleration is \(\mathbf T\,\mathbf a(t)\), where \(\mathbf T\) rotates that frame into the propagation frame.

Unlike the Gauss-Markov models, nothing here is integrated: the coefficients are constants of the dynamics, estimated as a 'static' parameter. The acceleration is linear in them, so the partial the filter needs is the interpolation matrix itself,

\[\begin{split}\frac{\partial \mathbf a}{\partial \mathbf c} = \begin{cases} [\,\mathbf 0 \;\cdots\; \mathbf I_3 \;\cdots\; \mathbf 0\,] & \text{order } 0 \\ [\,\mathbf 0 \;\cdots\; (1-\tau)\mathbf I_3 \;\; \tau\mathbf I_3 \;\cdots\; \mathbf 0\,] & \text{order } 1 \end{cases}\end{split}\]

which is exact rather than a difference approximation.

Parameters:
originBody

Propagation origin.

spacecraftSpacecraft

Spacecraft the empirical acceleration acts on.

base_frameFrame

Reference frame of the propagator.

t0EpochArray or float

Reference epoch at which batch 0 starts, as an EpochArray or as raw ephemeris seconds (TDB). It is the origin of the batch grid, not the start of the current propagation: in a multi-leg MissionSequence every leg must be given the same t0 so the batches tile the whole arc and a coefficient means the same thing in every leg.

batch_lengthfloat

Duration of each batch, in seconds.

n_batchesint

Number of batches.

orderint

Polynomial order within a batch: 0 for a zero-order hold (constant per batch), 1 for a first-order hold (linear per batch, continuous across batches). Required, since the two orders size the coefficient state differently.

coefficientsnp.ndarray, optional

Default coefficient vector, used when the propagated state carries no a_ppoly entry. This is what makes the model usable as a plain force model — a known piecewise acceleration applied without estimating anything.

framestr, optional

Frame the coefficients are expressed in: "inertial" (the propagation frame, the default), "RTN" (radial / along-track / cross-track, the RTN triad) or "body_fixed".

body_fixed_framestr or Frame, optional

Frame the coefficients are expressed in, for frame="body_fixed" (for example 'IAU_BENNU').

relative_bodystr or Body, optional

Body the RTN triad is built about, when that is not the propagation origin. Defaults to the propagation origin.

Attributes:
batch_length

Duration of one batch, in seconds.

body_fixed_frame

Body-fixed frame the coefficients are expressed in, if any.

default_coefficients

Coefficients applied when the state carries no a_ppoly entry.

depends_on_velocity

Whether the applied acceleration is a function of velocity.

frame

Frame the coefficients are expressed in.

n_batches

Number of batches.

n_coefficients

Length of the stacked a_ppoly state: 3*n_batches or 3*(n_batches+1).

order

Polynomial order within a batch: 0 or 1.

origin

Celestial body serving as origin of the state vector.

origin_name

Name of the origin body.

ref_frame

Reference frame used by the model.

relative_body

Body the RTN triad is referred to, if any.

t0

Reference epoch of the first batch, in ephemeris seconds.

Methods

clear_batch_window()

Drop any pinned batch, restoring the per-epoch lookup.

coefficient_labels()

Names of the stacked coefficients, in state order.

compute_acceleration(position, ...)

Piecewise polynomial acceleration applied to the body.

get_active_batch_bounds(epoch)

Return the active batch index and its time bounds.

pin_batch_window(t_start, t_end)

Pin the batch that owns an integration arc, so the dynamics stay continuous across it.

Raises:
InvalidOrderError

If order is not one of ORDERS.

InvalidBatchGridError

If batch_length <= 0 or n_batches < 1.

InvalidEpochError

If t0 is neither an EpochArray nor a real number.

CoefficientSizeError

If coefficients does not match the order and batch count.

DynamicalUtils.InvalidFrameError

If frame is not one of the supported frames.

DynamicalUtils.MissingBodyFixedFrameError

If frame="body_fixed" is requested without body_fixed_frame.

See also

scarabaeus.PiecewiseFirstOrderGaussMarkov

Batch-segmented stochastic variant.

scarabaeus.FourierAcceleration

Periodic empirical acceleration.

scarabaeus.StateArray

State vector used to initialize the model.

scarabaeus.DynamicModel

Abstract base class for all force model components.

Notes

The estimated state is one stacked ("a_ppoly", spice_id) entry, ordered batch-major (order=0) or node-major (order=1):

order 0:  [ c0_x c0_y c0_z | c1_x c1_y c1_z | ... | c(N-1)_x ... ]   3*N
order 1:  [ n0_x n0_y n0_z | n1_x n1_y n1_z | ... | nN_x     ... ]   3*(N+1)

with \(N\) the number of batches. coefficient_labels() returns the matching names. The state vector must contain an entry of the form

  • ('a_ppoly', n_coefficients, 'estimated', 'static', owner, value)

for the coefficients to be estimated.

Batch membership follows the PFOGM convention: interior boundaries belong to the batch that starts there, and the closing endpoint belongs to the last batch. The propagator splits its arcs at the batch boundaries and pins each arc to its batch with pin_batch_window(), so the zero-order hold’s jump never falls inside an integration step.

In the "RTN" frame the coefficients are radial, along-track and cross-track components, and the coefficient partial above is left-multiplied by \(\mathbf T\). The RTN triad is built from the state, so the acceleration also gains the position and velocity partials frame_partials() returns; the inertial and body-fixed rotations depend on the epoch alone.

Examples

A first-order hold over four one-day batches, estimated:

import scarabaeus as scb

state = [
    ("position", 3, "estimated", "dynamic", sc, [7000, 0, 0]),
    ("velocity", 3, "estimated", "dynamic", sc, [0, 7.5, 0]),
    ("a_ppoly", 15, "estimated", "static", sc, [0.0] * 15),
]

force_model = scb.ForceModelTranslation(
    primary_body=sc,
    t0=arc_start,
    piecewise_polynomial=True,
    ppoly_order=1,
    ppoly_batch_length=86400.0,
    ppoly_n_batches=4,
)
clear_batch_window() → None#

Drop any pinned batch, restoring the per-epoch lookup.

coefficient_labels() → list[str]#

Names of the stacked coefficients, in state order.

Returns:
list of str

['batch0_x', 'batch0_y', ...] for order=0 or ['node0_x', 'node0_y', ...] for order=1, matching the layout of the a_ppoly state. The axes follow frame: R, T, N in the RTN frame.

compute_acceleration(position: ndarray, current_state: dict, epoch: float, body: Body) → ndarray#

Piecewise polynomial acceleration applied to the body.

Parameters:
positionnp.ndarray

Spacecraft position vector, used only to build the RTN triad.

current_statedict

Dictionary containing the propagated state.

epochfloat

Current epoch in ephemeris seconds (TDB).

bodyBody

Body the acceleration is evaluated for.

Returns:
np.ndarray

Acceleration 3-vector in km/s^2, in the propagation frame. Zero when neither the state nor the model configuration supplies coefficients.

get_active_batch_bounds(epoch: float) → tuple[int, float, float]#

Return the active batch index and its time bounds.

Parameters:
epochfloat

Current epoch in ephemeris seconds (TDB).

Returns:
tuple[int, float, float]

Tuple (batch_idx, batch_start, batch_end).

pin_batch_window(t_start: float, t_end: float) → int | None#

Pin the batch that owns an integration arc, so the dynamics stay continuous across it.

Batch membership is right-continuous, so the epoch closing an arc that ends on a batch boundary belongs to the next batch. For a zero-order hold the acceleration jumps there; for a first-order hold its slope does. Either way an integrator evaluating its final stage on the boundary sees a discontinuity at the very point it has to land on. Pinning the batch for the arc removes it from the arc’s interior and endpoint alike.

Only arcs that lie within a single batch are pinned; the propagator already splits arcs at interior batch boundaries, so that is the normal case. Anything else clears the pin and leaves the per-epoch lookup in charge.

Parameters:
t_start, t_endfloat

Arc bounds in ephemeris seconds (TDB); order does not matter.

Returns:
int or None

The pinned batch index, or None if the arc was not pinned.

ORDERS = (0, 1)#

Polynomial orders the model supports: zero-order and first-order hold.

property batch_length: float#

Duration of one batch, in seconds.

property body_fixed_frame: str | None#

Body-fixed frame the coefficients are expressed in, if any.

property default_coefficients#

Coefficients applied when the state carries no a_ppoly entry.

property depends_on_velocity: bool#

Whether the applied acceleration is a function of velocity.

True for the RTN frame, whose triad is built from the velocity. The force model reads this to decide whether a \(\partial \mathbf a / \partial \mathbf v\) block is needed.

property frame: str#

Frame the coefficients are expressed in.

property n_batches: int#

Number of batches.

property n_coefficients: int#

Length of the stacked a_ppoly state: 3*n_batches or 3*(n_batches+1).

property order: int#

Polynomial order within a batch: 0 or 1.

property origin: Body#

Celestial body serving as origin of the state vector.

property origin_name: str#

Name of the origin body.

property ref_frame#

Reference frame used by the model.

property relative_body: str | None#

Body the RTN triad is referred to, if any.

property t0: float#

Reference epoch of the first batch, in ephemeris seconds.

exception CoefficientSizeError#

Bases: ValueError

Raised when the a_ppoly state does not match the order and grid.

exception EpochOutsideGridError#

Bases: ValueError

Raised when an epoch falls outside the batch grid.

exception InvalidBatchGridError#

Bases: ValueError

Raised when batch_length or n_batches cannot form a grid.

exception InvalidEpochError#

Bases: TypeError

Raised when t0 is neither an EpochArray nor a real number.

exception InvalidOrderError#

Bases: ValueError

Raised when order is not a supported polynomial order.