Interplanetary transfer legs#

class pykep.leg.sims_flanagan(rvs=[[1, 0, 0], [0, 1, 0]], ms=1., throttles=[0, 0, 0, 0, 0, 0], rvf=[[0, 1, 0], [-1, 0, 0]], mf=1., tof=pi / 2, max_thrust=1., veff=1., mu=1., cut=0.5)#

This class represents an interplanetary low-thrust transfer between a starting and a final point in the augmented state-space \([\mathbf r, \mathbf v, m]\). The low-thrust transfer is described by a sequence of equally spaced impulses as described in:

Sims, J., Finlayson, P., Rinderle, E., Vavrina, M. and Kowalkowski, T., 2006, August. Implementation of a low-thrust trajectory optimization algorithm for preliminary design. In AIAA/AAS Astrodynamics specialist conference and exhibit (p. 6746).

The low-thrust transfer will be feasible is the state mismatch equality constraints and the throttle mismatch inequality constraints are satisfied.

Args:

rvs (2D array-like): Cartesian components of the initial position vector and velocity [[xs, ys, zs], [vxs, vys, vzs]]. Defaults to [[1,0,0], [0,1,0]].

ms (float): initial mass. Defaults to 1.

throttles (1D array-like): the Cartesan components of the throttle history [ux1, uy1, uz1, ux2, uy2, uz2, …..]. Defaults to a ballistic, two segments profile [0,0,0,0,0,0].

rvf (2D array-like): Cartesian components of the final position vector and velocity [[xf, yf, zf], [vxf, vyf, vzf]]. Defaults to [[0,1,0], [-1,0,0]].

mf (float): final mass. Defaults to 1.

tof (float): time of flight. Defaults to \(\frac{\pi}{2}\).

max_thrust (float): maximum level for the spacecraft thrust. Defaults to 1.

veff (float): effective velocity of the propulsion system. Defaults to 1.

mu (float): gravitational parameter. Defaults to 1.

cut (float): the leg cut, in [0,1]. It determines the number of forward and backward segments. Defaults to 0.5.

Note

Units need to be consistent.

Examples:
>>> import pykep as pk
>>> import numpy as np
>>> sf = pk.leg.sims_flanagan()
compute_mc_grad()#

Computes the gradients of the mismatch constraints. Indicating the initial augmented state with \(\mathbf x_s = [\mathbf r_s, \mathbf v_s, m_s]\), the final augmented state with \(\mathbf x_f = [\mathbf r_f, \mathbf v_f, m_f]\), the total time of flight with \(T\) and introducing the throttle vector \(\mathbf u = [u_{x0}, u_{y0}, u_{z0}, u_{x1}, u_{y1}, u_{z1} ]\) and \(\mathbf {\tilde u} = [\mathbf u, T]\) (note the time of flight at the end), this method computes the following gradients:

\[\frac{\partial \mathbf {mc}}{\partial \mathbf x_s} \rightarrow (7\times7)\]
\[\frac{\partial \mathbf {mc}}{\partial \mathbf x_f} \rightarrow (7\times7)\]
\[\frac{\partial \mathbf {mc}}{\partial \mathbf {\tilde u}} \rightarrow (7\times(3\mathbf{nseg} + 1))\]
Returns:

tuple [numpy.ndarray, numpy.ndarray, numpy.ndarray]: The three gradients. sizes will be (7,7), (7,7) and (7, 3nseg + 1)

Examples:
>>> import pykep as pk
>>> import numpy as np
>>> sf = pk.leg.sims_flanagan()
>>  sf.throttles = [0.8]*3
>>> sf.compute_mc_grad()
compute_mismatch_constraints()#

In the Sims-Flanagan trajectory leg model, a forward propagation is performed from the starting state as well as a backward from the final state. The state values thus computed need to match in some middle control point. This is typically imposed as 7 independent constraints called mismatch-constraints computed by this method.

Returns:

list [float]: The seven mismatch constraints in the same units used to construct the leg.

Examples:
>>> import pykep as pk
>>> import numpy as np
>>> sf = pk.leg.sims_flanagan()
>>> sf.compute_mismatch_constraints()
compute_tc_grad()#

Computes the gradients of the throttles constraints. Indicating the total time of flight with \(T\) and introducing the throttle vector \(\mathbf u = [u_{x0}, u_{y0}, u_{z0}, u_{x1}, u_{y1}, u_{z1} ]\), this method computes the following gradient:

\[\frac{\partial \mathbf {tc}}{\partial \mathbf u} \rightarrow (\mathbf{nseg} \times3\mathbf{nseg})\]
Returns:

tuple [numpy.ndarray]: The gradient. Size will be (nseg,nseg*3).

Examples:
>>> import pykep as pk
>>> import numpy as np
>>> sf = pk.leg.sims_flanagan()
>>  sf.throttles = [0.8]*3
>>> sf.compute_tc_grad()
compute_throttle_constraints()#

In the Sims-Flanagan trajectory leg model implemented in pykep, we introduce the concept of throttles. Each throttle is defined by three numbers \([u_x, u_y, u_z] \in [0,1]\) indicating that a certain component of the thrust vector has reached a fraction of its maximum allowed value. As a consequence, along the segment along which the throttle is applied, the constraint \(u_x ^2 + u_y ^2 + u_z^2 = 1\), called a throttle constraint, has to be met.

Returns:

list [float]: The throttle constraints.

Examples:
>>> import pykep as pk
>>> import numpy as np
>>> sf = pk.leg.sims_flanagan()
>>  sf.throttles = [0.8]*3
>>> sf.compute_throttle_constraints()
property cut#

The leg cut: it determines the number of forward and backward segments.

property max_thrust#

Maximum spacecraft thruet.

property mf#

Final mass.

property ms#

Initial mass.

property mu#

Central body gravitational parameter.

property nseg#

The total number of segments

property nseg_bck#

The total number of backward segments

property nseg_fwd#

The total number of forward segments

property rvf#

The final position vector and velocity: [[xs, ys, zs], [vxs, vys, vzs]].

property rvs#

The initial position vector and velocity: [[xs, ys, zs], [vxs, vys, vzs]].

property throttles#

The Cartesan components of the throttle history [ux1, uy1, uz1, ux2, uy2, uz2, …..].

property tof#

Time of flight.

property veff#

Effective velocity of the propulsion system (Isp*G0 in the V units of the dynamics)


class pykep.leg.zoh(state0, controls, state1, tgrid, cut, tas, max_steps=None, dim_dynamics=7, dim_controls=4)#

This class implements an interplanetary low-thrust transfer between a starting and final state in the augmented state-space \([\mathbf{r}, \mathbf{v}, m]\). The transfer is modelled as a sequence of non-uniform segments along which a continuous and constant (zero-order hold) control acts. The time intervals defining these segments are also provided in tgrid.

The formulation generalises pykep.leg.sims_flanagan to arbitrary dynamics and non-uniform time grids. The dynamics are assumed to be zero-order hold and must be provided as compatible Taylor-adaptive integrators (tas).

Note

The requirements on tas are: a) the first dim_controls heyoka parameters are the controls, b) the system dimension is dim_dynamics, c) when using a variational integrator, variations are considered with respect to state and controls only. Typical 7D/4D and 6D/2D configurations are fulfilled by pykep.ta.zoh_kep, pykep.ta.zoh_eq, pykep.ta.zoh_cr3bp, pykep.ta.zoh_ss and their variational versions.

A transfer is feasible when the state mismatch equality constraints are satisfied. Any additional constraints on controls (e.g. throttle constraints) are to be enforced by the caller.

Examples:
>>> import numpy as np
>>> import heyoka as hy
>>> state0 = np.array([1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0])
>>> state1 = np.array([1.2, 0.1, 0.0, 0.0, 0.9, 0.1, 0.95])
>>> controls = np.array([0.022, 0.7, 0.7, 0.1, 0.025, -0.3, 0.8, 0.4, 0.015, -0.2, 0.8, 0.4])
>>> tgrid = np.array([0.0, 0.5, 1.0, 1.23])
>>> ta = pk.ta.get_zoh_eq(tol=1e-16)
>>> ta_var = pk.ta.get_zoh_eq_var(tol=1e-16)
>>> leg = pk.leg.zoh(state0, controls, state1, tgrid, cut=0.5, tas=(ta, ta_var))
compute_mc_grad()#

Computes the gradients of the mismatch constraints. Indicating the initial augmented state with \(\mathbf x_s = [\mathbf r_s, \mathbf v_s, m_s]\), the final augmented state with \(\mathbf x_f = [\mathbf r_f, \mathbf v_f, m_f]\), the time grid as \(T_{grid}\) and the introducing the control vector \(\mathbf u = [T_0, i_{x0}, i_{y0}, i_{z0}, T_1, i_{x1}, i_{y1}, i_{z1}, \ldots]\), this method computes the following gradients:

\[\frac{\partial \mathbf {mc}}{\partial \mathbf x_s} \rightarrow (7\times7)\]
\[\frac{\partial \mathbf {mc}}{\partial \mathbf x_f} \rightarrow (7\times7)\]
\[\frac{\partial \mathbf {mc}}{\partial \mathbf u} \rightarrow (7\times(4\mathbf{nseg}))\]
\[\frac{\partial \mathbf {mc}}{\partial \mathbf T_{grid}} \rightarrow (7\times(\mathbf{nseg} + 1))\]
Returns:

tuple [numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray]: The four gradients. Sizes will be (7,7), (7,7), (7,4nseg), and (7,nseg+1).

get_state_info()#

This method returns state histories sampled along each ZOH segment, for both the forward and backward propagation parts of the leg. The sampling is performed by calling heyoka.taylor_adaptive.propagate_grid() on a uniformly-spaced grid of N points within each segment.

Args:

N (int): Number of sampling points per segment (including the segment endpoints). The default (N=2) returns only the segment endpoints.

Returns:

tuple: (state_fwd, state_bck, success) where:

  • state_fwd (list): List of length nseg_fwd (or less on failure). Each entry contains the sampled dim_dynamics state history over the corresponding forward segment.

  • state_bck (list): List of length nseg_bck (or less on failure). Each entry contains the sampled dim_dynamics state history over the corresponding backward segment.

  • success (bool): True when all requested segment propagations succeed.

Note

The backward propagation is carried out by integrating from the final time toward earlier times; depending on the backend conventions, the returned grids may thus be time-reversed with respect to a forward-time plot.

Note

This method uses the nominal integrator self.ta and overwrites its internal time, state and the first four parameters (T, i_x, i_y, i_z). If the integrator state must be preserved, call this method on a dedicated copy.

Examples:

ax = pk.plot.make_3Daxis()
fwd, bck, success = leg.get_state_info(N=100)
for segment in fwd:
  ax.scatter(segment[0,0], segment[0,1], segment[0,2], c='blue')
  ax.plot(segment[:,0], segment[:,1], segment[:,2], c='blue')
for segment in bck:
  ax.scatter(segment[0,0], segment[0,1], segment[0,2], c='darkorange')
  ax.plot(segment[:,0], segment[:,1], segment[:,2], c='darkorange')
ax.view_init(90, -90)

“


class pykep.leg.zoh_ms(states, controls, tgrid, cut, tas, max_steps=None, dim_dynamics=7, dim_controls=4)#

Generic zero-order-hold multiple-shooting leg. Each segment is propagated independently from its mesh node, forward or backward according to cut. A transfer is feasible when all defects vanish. Additional control constraints are left to the caller.

Args:

states (list): Flat mesh nodes, of length (nseg + 1) * dim_dynamics.

controls (list): Flat segment controls, of length nseg * dim_controls.

tgrid (list): Node times, of length nseg + 1, in the integrator’s time units.

cut (float): Fraction of segments propagated forward, in [0, 1].

tas (tuple): Pair (ta, ta_var) of compatible heyoka.taylor_adaptive

integrators. The nominal integrator has dim_dynamics states and at least dim_controls parameters, with controls first. The optional variational integrator varies states and controls only; it may be None.

max_steps (int, optional): Maximum integration steps per segment. Default is None (no limit).

dim_dynamics (int, optional): State dimension. Default is 7.

dim_controls (int, optional): Control dimension. Default is 4.

Raises:

RuntimeError: If the mesh, dimensions, cut or integrators are inconsistent.

Notes:

Mesh data and integrators are copied. Propagation mutates the leg’s nominal integrator, not the supplied integrators. Vector properties return list copies; reassign a property to update its values. Setters validate immediately. Dimensions and segment counts are readonly.

compute_defects()#

Propagates each segment independently and returns its continuity defect. Let \(n = \mathrm{nseg}\), \(\mathbf{x}_k\) be the state at mesh node \(k\), \(\mathbf{u}_k\) the constant control on segment \(k\), and \(\Phi_k(\mathbf{x}_k;\mathbf{u}_k)\) the flow from \(t_k\) to \(t_{k+1}\). With \(n_{\mathrm{fwd}} = \lfloor n\,\mathrm{cut}\rfloor\), the defects are:

\[\begin{split}\mathbf{d}_k = \begin{cases} \Phi_k(\mathbf{x}_k;\mathbf{u}_k) - \mathbf{x}_{k+1}, & 0 \leq k < n_{\mathrm{fwd}}, \\ \mathbf{x}_k - \Phi_k^{-1}(\mathbf{x}_{k+1};\mathbf{u}_k), & n_{\mathrm{fwd}} \leq k < n. \end{cases}\end{split}\]

Here \(\Phi_k^{-1}\) means integrating the same dynamics backward from \(t_{k+1}\) to \(t_k\). The returned vector stacks the defects in segment order:

\[\mathbf{D} = [\mathbf{d}_0^\mathsf{T}, \ldots, \mathbf{d}_{n-1}^\mathsf{T}]^\mathsf{T}.\]

A transfer is feasible when \(\mathbf{D} = \mathbf{0}\). For invertible flows, feasibility is independent of cut, but off-feasibility defects depend on it.

Notes:

Failed propagation restores the segment starting state, which replaces the flow in the defect. This method modifies the leg’s nominal integrator.

Returns:

list: Flat defects of length nseg * dim_dynamics, ordered by segment then state component.

get_state_info(N=5)#

Samples each segment independently from its corresponding mesh node. Forward segments are ordered from node 0 toward the cut and sampled in increasing time. Backward segments are ordered from the final node toward the cut and sampled in decreasing time.

Args:

N (int, optional): Sampling points per segment, including endpoints. Default is 5.

Raises:

RuntimeError: If N is zero.

Notes:

The nominal integrator is modified. Each direction stops at its first incomplete segment. With N=1, only each segment’s starting node is returned. Returned arrays own their data independently of the leg.

Returns:

tuple: (state_fwd, state_bck, success). Each direction is a list of float64 numpy.ndarray objects of shape (N, dim_dynamics), possibly shorter on failure. Success is True only if all requested segments return all N samples.

set_initial_guess(ballistic=False)#

Replaces interior nodes by propagating inward from the endpoint states.

Args:
ballistic (bool, optional): Use zero controls without changing stored controls.

Default is False.

Notes:

Endpoints, time grid and stored controls are preserved. With stored controls and successful propagation, only the segment where the forward and backward meshes meet may have a defect. Ballistic propagation may leave defects on every segment. Failed propagation restores its starting state.