Generic zero-order-hold leg
pykep_core::leg::ZohLeg transcribes a transfer between two fixed endpoint
states as non-uniform segments with continuous, piecewise-constant controls.
It is generic over any Rust model implementing ZeroOrderHoldModel; aliases
cover every built-in ZOH dynamics family:
| Alias | State | Control | Constants |
|---|---|---|---|
ZohKeplerLeg | [x,y,z,vx,vy,vz,m] | [thrust,ix,iy,iz] | [c] |
ZohCr3bpLeg | [x,y,z,vx,vy,vz,m] | [thrust,ix,iy,iz] | [c,mu] |
ZohEquinoctialLeg | [p,f,g,h,k,L,m] | [thrust,ir,it,in] | [c] |
ZohSolarSailLeg | [x,y,z,vx,vy,vz] | [cone,clock] | [c] |
The units and frame conventions of each row are those of its model in
zero-order-hold.md. c is the normalized mass-flow
coefficient for the three low-thrust models and the normalized lightness
coefficient for the solar sail.
Grid, controls, and cut
For S segments, construction requires exactly S + 1 finite,
strictly-increasing time-grid nodes and S finite control vectors. Controls
are stored in chronological order and own the half-open interval
[time_grid[i], time_grid[i+1]); the final endpoint belongs to the final
segment.
The cut uses the same convention as the upstream leg:
forward_segments = floor(S * cut)
backward_segments = S - forward_segments
The forward state starts at the initial endpoint and the backward state starts at the final endpoint. The mismatch is their component-wise difference at the cut. Cuts zero and one are supported without special placeholder states.
The leg is immutable after construction. It rejects invalid endpoint states, model constants, dimensions, grids, cuts, tolerances, maximum steps, and maximum-step magnitudes before evaluation.
mismatch_constraints() preserves the established DOP853 propagation.
Built-in models additionally provide
mismatch_constraints_with_method(IntegrationMethod), so repeated
derivative-free objective evaluations can select the accelerated Taylor
backend explicitly.
Sensitivity layout
mismatch_jacobian() returns four output-by-input matrices:
| Group | Shape | Column order |
|---|---|---|
| initial state | N × N | model state order |
| final state | N × N | model state order |
| controls | N × (C*S) | segment 0 controls, then segment 1, and so on |
| time grid | N × (S+1) | every grid node, including both endpoints |
Each segment propagates a local state-transition and active-control matrix. The leg composes those matrices from each side of the cut and includes the dynamics jump at every switching time. Constant model parameters are fixed leg configuration and therefore are not a returned derivative group.
The four built-in models compute their local RHS Jacobians with fixed-size
centered differences using a relative step of 3e-6. Consequently,
integrator tolerances such as 1e-12 do not imply 1e-12 derivative
accuracy: the pinned end-to-end validation uses scaled tolerances up to
3e-5. See zero-order-hold.md for the
model-level contract and validation evidence.
Integration and failures
IntegratorOptions applies independently to every segment, including
relative and absolute tolerances, optional initial and maximum step sizes,
maximum steps, and maximum rejections. A propagation failure is reported as
an IntegrationFailure containing the direction, chronological segment
index, and attempted time interval.
state_history(samples_per_segment) uses one DOP853 dense solve per segment
for uniformly spaced states including both endpoints.
state_history_with_method(samples_per_segment, IntegrationMethod) selects
DOP853 or Taylor for built-in models. DOP853 backward segments are evaluated
through the equivalent increasing coordinate tau = segment_start - time to
avoid a decreasing-time dense-output boundary issue in the numerical backend;
Taylor supports the decreasing grid directly. At least two samples are
required. Backward histories list the final segment first, matching
propagation order. evaluate_zoh_mismatch_batch() evaluates validated Rust
legs in input order.
Python
pykep_rust.ZohLeg accepts a ZohModel value: Kepler, Cr3bp,
Equinoctial, or SolarSail. The constructor validates dynamic state,
control, and constant dimensions for that selection. Mismatch, Jacobian, and
history evaluation release the Python GIL.
The ZOH-leg batch methods clone the small immutable leg descriptors, release
the GIL once, and return results in input order. workers=0 uses the shared
pool, workers=1 is serial, and a larger value selects a cached pool of that
exact size.