Skip to content

OpenGoddard

Type part of a name or a dotted path

Pseudospectral optimal control with NumPy and SciPy.

Classes

NameDescription
ConditionAccumulate dimensionless equality or inequality residuals.
DynamicsScaled state derivatives at a section's collocation nodes.
DynamicsModelPointwise physical dynamics function(x, u, t, obj, section, xp).
GuessConstruct physical initial guesses on a supplied time grid.
LinearCallbackDeclare a cost/boundary/path callback affine in the scaled vector p.
ProblemDefine a physical optimal-control problem and solve its LGL discretization.

Modules

  • derivativesStructured collocation derivatives and optional NumPy automatic differentiation.
  • hpResidual-guided h/hp adaptation inside unchanged logical phases.
  • meshPhase-preserving mesh refinement and independent, between-node defect checks.
  • optimizeOpenGoddard.optimize - Optimal Trajectories module with PseudoSpectral Method

Conditionclass#

Re-exported from OpenGoddard._helpersView source
class Condition(length=0, *, backend=np)

Accumulate dimensionless equality or inequality residuals.

Equality residuals must be zero; inequality residuals must be nonnegative. Scalar and one-dimensional inputs follow NumPy broadcasting rules. A positive reference unit scales each residual. Prefer problem.condition() inside callbacks so automatic differentiation selects the correct backend.

Parameters

NameTypeDefaultDescription
lengthint0Number of initial zero residuals, normally zero.
backendmodulenpNumPy-compatible module, supplied by the callback view.

Examples

>>> result = Condition()
>>> result.equal(5, 5)
>>> result()
array([0.])

For physical bounds 0 <= x <= 100 in an inequality callback, call result.lower_bound(x, 0) and result.upper_bound(x, 100).

addmethod#

def add(arg, unit=1.0)

Append scalar or one-dimensional residuals using a positive scale.

change_valuemethod#

def change_value(index, value)

Replace an existing dimensionless residual by its flattened index.

equalmethod#

def equal(arg1, arg2, unit=1.0)

Append (arg1 - arg2)/unit; enforces arg1 = arg2 in an equality callback.

Parameters

NameTypeDefaultDescription
arg1float or array_likerequiredLeft side of the equation.
arg2float or array_likerequiredRight side of the equation.
unitfloat1.0Positive reference unit for the residual.

lower_boundmethod#

def lower_bound(arg1, arg2, unit=1.0)

Append (arg1 - arg2)/unit; enforces arg1 >= arg2 in inequalities.

Parameters

NameTypeDefaultDescription
arg1float or array_likerequiredPhysical values to constrain.
arg2float or array_likerequiredPhysical lower bounds.
unitfloat1.0Positive reference unit for the residual.

upper_boundmethod#

def upper_bound(arg1, arg2, unit=1.0)

Append (arg2 - arg1)/unit; enforces arg1 <= arg2 in inequalities.

Parameters

NameTypeDefaultDescription
arg1float or array_likerequiredPhysical values to constrain.
arg2float or array_likerequiredPhysical upper bounds.
unitfloat1.0Positive reference unit for the residual.

Dynamicsclass#

Re-exported from OpenGoddard._helpersView source
class Dynamics(prob, section=0)

Scaled state derivatives at a section’s collocation nodes.

Assign physical dx/dt values with dx[state] = values. Calling dx() converts them to d(x/unit_states)/d(t/unit_time), flattened in state order for the LGL residual. Scalars are broadcast to every node. Unassigned states have zero derivative. For pointwise models that also support independent-grid error checks and structured derivatives, use DynamicsModel.

Examples

Dynamics class must be used in dynamics function. It is an example of the equation of motion of thrust and free fall. Thrust is controllable.

x˙=v,v˙=T/m−g\dot{x} = v, \qquad \dot{v} = T/m - g
>>> def dynamics(prob, obj, section):
>>> x = prob.states(0, section)
>>> v = prob.states(1, section)
>>> T = prob.controls(0, section)
>>> g = 9.8
>>> m = 1.0
>>> dx = Dynamics(prob, section)
>>> dx[0] = v
>>> dx[1] = T / m - g
>>> return dx()

number_of_stateattributeinstance attribute#

number_of_state = prob.number_of_states[self.section]

sectionattributeinstance attribute#

section = index(section, prob.number_of_section, 'section')

unit_statesattributeinstance attribute#

unit_states = prob.unit_states

unit_timeattributeinstance attribute#

unit_time = prob.unit_time

DynamicsModelclass#

Re-exported from OpenGoddard._modelsView source
class DynamicsModel(function)

Pointwise physical dynamics function(x, u, t, obj, section, xp).

x has shape (states, nodes), u (controls, nodes), and t (nodes,). Return an array with shape (states, nodes). Each output column must depend ONLY on the corresponding input column. Use xp for array operations; it is NumPy for values and autograd.numpy for automatic differentiation. Reductions or interpolation across nodes violate this contract.

Instances are callable with the legacy (problem, obj, section) signature. Local derivatives use simultaneous central differences, or reverse-mode AD of each state’s sum over independent nodes. No full dense dynamics Jacobian is formed. Non-smooth expressions require the user’s choice of formulation.

functionattributeinstance attribute#

function = function

inputsmethodstaticmethod#

def inputs(problem, section)

linearizemethod#

def linearize(problem, obj, section, mode)

physicalmethod#

def physical(x, u, time, obj, section)

Guessclass#

Re-exported from OpenGoddard._helpersView source
class Guess

Construct physical initial guesses on a supplied time grid.

constantmethodclassmethod#

def constant(time, const: float) -> NDArray[np.float64]

Return one constant physical value per time node.

Parameters

NameTypeDescription
timearray_likeTime nodes; only their count is used.
constfloatConstant physical value.

Returns

ndarrayNDArray[np.float64]
Values of shape (N,).

cubicmethodclassmethod#

def cubic(time, y0: float, yprime0: float, yf: float, yprimef: float) -> NDArray[np.float64]

Interpolate endpoint values and physical-time slopes with a cubic.

Parameters

NameTypeDescription
timearray_likeFinite, nondecreasing physical times with a positive span. Repeated interface times are supported.
y0floatPhysical value at the first time.
yprime0floatPhysical dy/dt at the first time.
yffloatPhysical value at the last time.
yprimeffloatPhysical dy/dt at the last time.

Returns

ndarrayNDArray[np.float64]
Values of shape (N,).

linearmethodclassmethod#

def linear(time, y0: float, yf: float) -> NDArray[np.float64]

Interpolate physical endpoint values linearly in time.

Parameters

NameTypeDescription
timearray_likeFinite, nondecreasing physical times with a positive span. Repeated interface times are supported.
y0floatPhysical value at the first time.
yffloatPhysical value at the last time.

Returns

ndarrayNDArray[np.float64]
Values of shape (N,).

plotmethodclassmethod#

def plot(x, y, title='', xlabel='', ylabel='')

Plot physical initial-guess values against time.

Parameters

NameTypeDefaultDescription
xarray_likerequiredarray on the horizontal axis of the plot
yarray_likerequiredarray on the vertical axis of the plot
titlestr''title
xlabelstr''xlabel
ylabelstr''ylabel

zerosmethodclassmethod#

def zeros(time) -> NDArray[np.float64]

Return one zero per time node.

Parameters

NameTypeDescription
timearray_likeTime nodes; only their count is used.

Returns

ndarrayNDArray[np.float64]
Values of shape (N,).

LinearCallbackclass#

Re-exported from OpenGoddard._modelsView source
class LinearCallback(function)

Declare a cost/boundary/path callback affine in the scaled vector p.

Its Jacobian is computed once per solve and cached. This is an explicit mathematical contract, not a heuristic: nonlinear expressions must not be wrapped. Coefficients, units and obj must remain constant during a solve.

functionattributeinstance attribute#

function = function

Problemclass#

class Problem(time_init, nodes, number_of_states, number_of_controls, maxIterator=100, method='LGL', *, mesh=None)

Define a physical optimal-control problem and solve its LGL discretization.

Parameters

NameTypeDefaultDescription
time_initsequence of floatrequiredStrictly increasing physical phase times.
nodessequence of intrequiredLGL node counts, at least two per phase.
number_of_statessequence of intrequiredPositive state counts per phase.
number_of_controlssequence of intrequiredNonnegative control counts per phase.
maxIteratorint100Maximum solver attempts, including the first attempt. Another attempt is made only after an iteration-limit status. solve(maxiter=...) limits iterations within each attempt.
methodstr'LGL'Only "LGL" is supported for optimal control.
meshoptionalNonePer-section lists of (left, right, nodes) elements partitioning the phase coordinate [-1, 1]. Logical section numbers do not change. Node counts include both sides of internal interfaces.

All state, control and time getters and setters use physical units. Only p and bounds use scaled units. Setters for units preserve physical guesses and bounds. Node, state, control and section indices follow Python’s negative indexing convention and raise IndexError outside their range.

Attributes

NameTypeDescription
nodeslist[int]Node counts per phase, including duplicated interfaces.
number_of_stateslist[int]State counts per phase.
number_of_controlslist[int]Control counts per phase.
number_of_sectionintNumber of logical phases.
number_of_paramndarrayState plus control counts per phase.
number_of_variablesintLength of p, including phase end times.
divlist[list[int]]Exclusive ends of state/control blocks in p.
taulist[ndarray]LGL phase coordinates in [-1, 1].
wlist[ndarray]Quadrature weights in phase coordinates.
Dlist[ndarray]Differentiation matrices in phase coordinates.
timelist[ndarray]Physical node times, derived from current p.
time_all_sectionndarrayTimes concatenated in phase order.
unit_stateslist[list[float]]State reference units per phase.
unit_controlslist[list[float]]Control reference units per phase.
unit_timefloatTime reference unit.
pndarrayScaled states, controls and phase end times, shape (N,).
boundslist[tuple]Scaled lower/upper bounds for every entry of p.
dynamicslist[Callable]One scaled-derivative callback per phase, called as dynamics(problem, obj, section). Use Dynamics to assemble physical derivatives, or DynamicsModel for a pointwise physical model supporting derivatives and error estimation.
knot_states_smoothlist[bool]Enforce continuity at phase knots with equal state counts. Disable it when supplying jump conditions.
costCallable | NoneTerminal cost, called as cost(problem, obj).
running_costCallable | NonePhysical-time integrand, shape (sum(nodes),), called as running_cost(problem, obj).
cost_derivativefunctionComplete objective gradient in p.
equalityCallable | NoneOne-dimensional residuals equal to zero.
inequalityCallable | NoneOne-dimensional residuals >= 0.
array_moduleNumPy; differentiable callback views supply autograd.numpy.
metadatadictUser metadata, shallow-copied on refine/remesh.
resultOptimizeResult | NoneLatest solver result, or None before solve.
result_historylist[OptimizeResult]Attempts of the latest solve.

Callback views share states/controls (including all-section getters), time_start/time_final, time_knots, time/time_all_section, dimensions, units, array_module and condition(). Use this reading interface for portable callbacks; mutations and direct p indices do not support path sampling. Use unit setters and refine/remesh rather than editing configuration lists. Construct a new Problem to change state/control counts. Refine/remesh create a new mesh and reset solver results.

Dattributeinstance attribute#

D = [rule[2] for rule in rules]

array_moduleattributeclass attributeinstance attribute#

array_module = np

boundsattributeinstance attribute#

bounds = [(None, None)] * self.number_of_variables

costattributeinstance attribute#

cost: Callable[[Problem, Any], float] | None = None

cost_derivativeattributeinstance attribute#

cost_derivative: Callable[[Problem, Any], NDArray[np.float64]] | None = None

divattributeinstance attribute#

div = [list(phase) for phase in self._layout.divisions]

dynamicsattributeinstance attribute#

dynamics: list[Callable[[Problem, Any, int], NDArray[np.float64]] | None] = [None] * self.number_of_section

equalityattributeinstance attribute#

equality: Callable[[Problem, Any], NDArray[np.float64]] | None = None

inequalityattributeinstance attribute#

inequality: Callable[[Problem, Any], NDArray[np.float64]] | None = None

iteratorattributeinstance attribute#

iterator = 0

knot_states_smoothattributeinstance attribute#

knot_states_smooth = [True] * self.number_of_section - 1

maxIteratorattributeinstance attribute#

maxIterator = integer(maxIterator, 'maxIterator', 1)

meshattributeinstance attribute#

mesh = normalize_mesh(self.nodes, mesh)

metadataattributeinstance attribute#

metadata: dict[str, Any] = {}

methodattributeinstance attribute#

method = method

nodesattributeinstance attribute#

nodes = [integer(n, 'nodes', 2) for n in nodes]

number_of_controlsattributeinstance attribute#

number_of_controls = [integer(n, 'number_of_controls') for n in number_of_controls]

number_of_paramattributeinstance attribute#

number_of_param = np.array(self.number_of_states) + self.number_of_controls

number_of_sectionattributeinstance attribute#

number_of_section = len(self.nodes)

number_of_statesattributeinstance attribute#

number_of_states = [integer(n, 'number_of_states', 1) for n in number_of_states]

number_of_variablesattributeinstance attribute#

number_of_variables = self._layout.size

pattributeinstance attribute#

p = np.zeros(self.number_of_variables)

resultattributeinstance attribute#

result: optimize.OptimizeResult | None = None

result_historyattributeinstance attribute#

result_history: list[optimize.OptimizeResult] = []

running_costattributeinstance attribute#

running_cost: Callable[[Problem, Any], NDArray[np.float64]] | None = None

t0attributeinstance attribute#

t0 = float(times[0])

tauattributeinstance attribute#

tau = [rule[0] for rule in rules]

time_initattributeinstance attribute#

time_init = times.tolist()

unit_controlsattributeinstance attribute#

unit_controls = [[1.0] * n for n in self.number_of_controls]

unit_statesattributeinstance attribute#

unit_states = [[1.0] * n for n in self.number_of_states]

unit_timeattributeinstance attribute#

unit_time = 1.0

wattributeinstance attribute#

w = [rule[1] for rule in rules]

timeproperty#

time: list[NDArray[np.float64]]

Physical node times per phase, derived from the current p.

Returned arrays are independent values. Change phase end times with set_time_final(); editing these arrays does not change the problem. Both sides of phase and element interfaces are included.

time_all_sectionproperty#

time_all_section: NDArray[np.float64]

Physical node times concatenated in phase order, including interfaces.

conditionmethod#

def condition(length: int = 0) -> Condition

Build equality or inequality residuals in this view’s array backend.

Parameters

NameTypeDefaultDescription
lengthint0Number of initial zero residuals, normally zero.

Returns

ConditionCondition
A residual builder. Use equal() for equality callbacks; lower_bound()/upper_bound() produce nonnegative feasible residuals.

controlsmethod#

def controls(control: int, section: int) -> NDArray[np.float64]

Return one control’s physical values in a phase.

Parameters

NameTypeDescription
controlintVariable index, supporting negative indices.
sectionintPhase index, supporting negative indices.

Returns

ndarrayNDArray[np.float64]
Independent array of shape (nodes[section],). Both sides of internal element interfaces are present. Use set_controls() to edit.

controls_all_sectionmethod#

def controls_all_section(control: int) -> NDArray[np.float64]

Return a control’s physical values concatenated in phase order.

Parameters

NameTypeDescription
controlintVariable index, which must exist in every phase.

Returns

ndarrayNDArray[np.float64]
Shape (sum(nodes),), including duplicated interface nodes.

estimate_errormethod#

def estimate_error(obj: Any = None, check_factor: int = 2) -> dict[str, Any]

Estimate dimensionless residuals between the LGL nodes.

Parameters

NameTypeDefaultDescription
objAnyNoneUser data passed to the model and inequality callback.
check_factorint2Integer >= 2. Check each element at check_factor * element.nodes + 1 Gauss points.

Returns

dictdict[str, Any]
section_errors (per-phase ODE indicators), max_error (their maximum), path_violation (scaled inequality/box-bound violation), and elements (per-element error, smoothness, overshoot and split point). Indicators are residual estimates, not rigorous state-error bounds. Nonlinear path constraints are checked by sampling, not certified.

Raises

ValueError
A phase lacks DynamicsModel, has nonpositive duration, or uses varying nodal box bounds.

index_controlsmethod#

def index_controls(control: int, section: int, index: int | None = None) -> int

Return the index of a control node in the scaled vector p.

Parameters

NameTypeDefaultDescription
controlintrequiredVariable index, supporting negative indices.
sectionintrequiredPhase index, supporting negative indices.
indexint | NoneNoneLocal node index, supporting negative indices; None selects the first node.

Returns

intint
Index into p, whose values use reference-unit scaling.

index_statesmethod#

def index_states(state: int, section: int, index: int | None = None) -> int

Return the index of a state node in the scaled vector p.

Parameters

NameTypeDefaultDescription
stateintrequiredVariable index, supporting negative indices.
sectionintrequiredPhase index, supporting negative indices.
indexint | NoneNoneLocal node index, supporting negative indices; None selects the first node.

Returns

intint
Index into p, whose values use reference-unit scaling.

index_time_finalmethod#

def index_time_final(section: int) -> int

Return the index of a phase end time in the scaled vector p.

method_LGmethod#

def method_LG(n) -> tuple[NDArray[np.float64], ...]

Legendre-Gauss Pseudospectral method Gauss nodes are roots of Pn(x)P_n(x).

Parameters

NameTypeDescription
nintnumber of nodes

Returns

tuple[NDArray[np.float64], ...]
tuple[ndarray, ndarray, ndarray]: Nodes (n,), weights (n,), and D (n, n).

method_LGLmethod#

def method_LGL(n) -> tuple[NDArray[np.float64], ...]

Legendre-Gauss-Lobatto Pseudospectral method Gauss-Lobatto nodes are roots of Pn−1′(x)P'_{n-1}(x), plus -1 and 1.

Parameters

NameTypeDescription
nintnumber of nodes

Returns

tuple[NDArray[np.float64], ...]
tuple[ndarray, ndarray, ndarray]: Nodes (n,), weights (n,), and D (n, n).

method_LGRmethod#

def method_LGR(n) -> tuple[NDArray[np.float64], ...]

Legendre-Gauss-Radau Pseudospectral method Gauss-Radau nodes are roots of Pn(x)+Pn−1(x)P_n(x) + P_{n-1}(x).

Parameters

NameTypeDescription
nintnumber of nodes

Returns

tuple[NDArray[np.float64], ...]
tuple[ndarray, ndarray, ndarray]: Nodes (n,), weights (n,), and D (n, n).

plotmethod#

def plot(title_comment='')

plot inner variables that to be optimized

Parameters

NameTypeDefaultDescription
title_commentstr''string for title

refinemethod#

def refine(nodes) -> Problem

Return a new problem with interpolated guesses and new node counts.

Parameters

NameTypeDescription
nodessequence of intTotal node counts per phase, including duplicated interfaces. At least two nodes per element are required.

Returns

ProblemProblem
A new problem preserving element boundaries, physical units, constant box bounds and callbacks. Solver results are reset. Metadata containers are shallow-copied; nested objects remain shared.

Raises

ValueError
Counts are invalid or box bounds vary between nodes.

remeshmethod#

def remesh(mesh) -> Problem

Return a warm start on per-phase lists of (left, right, nodes) elements.

Element endpoints are phase coordinates in [-1, 1]. Logical section indices remain unchanged. States are continuous at internal interfaces; controls have independent left and right limits.

Parameters

NameTypeDescription
meshsequencePer-phase lists of (left, right, nodes) elements partitioning [-1, 1]. Each element includes both endpoints.

Returns

ProblemProblem
A new problem with interpolated guesses, preserved physical units, constant bounds and callbacks, and reset solver results. Metadata containers are shallow-copied. Private extension caches are not copied.

Raises

ValueError
The mesh is invalid or box bounds vary between nodes.

set_controlsmethod#

def set_controls(control: int, section: int, value: ArrayLike) -> None

Set one control’s initial guess in physical units.

Parameters

NameTypeDescription
controlintVariable index, supporting negative indices.
sectionintPhase index, supporting negative indices.
valueArrayLikeFinite one-dimensional values of shape (nodes[section],).

Raises

ValueError
Values have the wrong shape or are not finite.

set_controls_all_sectionmethod#

def set_controls_all_section(control: int, value_all_section: ArrayLike) -> None

Set a control’s physical initial guess across all phases.

Parameters

NameTypeDescription
controlintVariable index, which must exist in every phase.
value_all_sectionArrayLikeFinite values of shape (sum(nodes),), in phase order and including duplicated interface nodes.

set_controls_boundsmethod#

def set_controls_bounds(control: int, section: int, lb: float | None, ub: float | None) -> None

Set constant physical box bounds at every node of a phase.

Parameters

NameTypeDescription
controlintVariable index, supporting negative indices.
sectionintPhase index, supporting negative indices.
lbfloat | NonePhysical lower bound, or None for no lower bound.
ubfloat | NonePhysical upper bound, or None for no upper bound.

set_controls_bounds_all_sectionmethod#

def set_controls_bounds_all_section(control: int, lb: float | None, ub: float | None) -> None

set value to bounds of control at all sections

Parameters

NameTypeDescription
controlintcontrol
lbfloat or Nonelower bound
ubfloat or Noneupper bound

set_statesmethod#

def set_states(state: int, section: int, value: ArrayLike) -> None

Set one state’s initial guess in physical units.

Parameters

NameTypeDescription
stateintVariable index, supporting negative indices.
sectionintPhase index, supporting negative indices.
valueArrayLikeFinite one-dimensional values of shape (nodes[section],).

Raises

ValueError
Values have the wrong shape or are not finite.

set_states_all_sectionmethod#

def set_states_all_section(state: int, value_all_section: ArrayLike) -> None

Set a state’s physical initial guess across all phases.

Parameters

NameTypeDescription
stateintVariable index, which must exist in every phase.
value_all_sectionArrayLikeFinite values of shape (sum(nodes),), in phase order and including duplicated interface nodes.

set_states_boundsmethod#

def set_states_bounds(state: int, section: int, lb: float | None, ub: float | None) -> None

Set constant physical box bounds at every node of a phase.

Parameters

NameTypeDescription
stateintVariable index, supporting negative indices.
sectionintPhase index, supporting negative indices.
lbfloat | NonePhysical lower bound, or None for no lower bound.
ubfloat | NonePhysical upper bound, or None for no upper bound.

set_states_bounds_all_sectionmethod#

def set_states_bounds_all_section(state: int, lb: float | None, ub: float | None) -> None

set value to bounds of state at all sections

Parameters

NameTypeDescription
stateintstate
lbfloat or Nonelower bound
ubfloat or Noneupper bound

set_time_finalmethod#

def set_time_final(section: int, value: float) -> None

Set a phase’s end-time guess in physical units.

Parameters

NameTypeDescription
sectionintPhase index, supporting negative indices.
valuefloatFinite physical end time. Times are immediately reflected in time and time_all_section. Forward ordering is enforced at solve.

Raises

ValueError
The end time is not finite.

set_time_final_boundsmethod#

def set_time_final_bounds(section: int, lb: float | None, ub: float | None) -> None

set value to bounds of time_final at specific section

Parameters

NameTypeDescription
sectionintsection
lbfloat or Nonelower bound
ubfloat or Noneupper bound

set_unit_controlsmethod#

def set_unit_controls(control: int, section: int, value: float) -> None

Change a control’s reference unit, preserving physical values and bounds.

Parameters

NameTypeDescription
controlintVariable index, supporting negative indices.
sectionintPhase index, supporting negative indices.
valuefloatFinite, strictly positive reference unit.

set_unit_controls_all_sectionmethod#

def set_unit_controls_all_section(control: int, value: float) -> None

set a canonical unit value to the control at all sections

Parameters

NameTypeDescription
controlintcontrol
valuefloatvalue

set_unit_statesmethod#

def set_unit_states(state: int, section: int, value: float) -> None

Change a state’s reference unit, preserving physical values and bounds.

Parameters

NameTypeDescription
stateintVariable index, supporting negative indices.
sectionintPhase index, supporting negative indices.
valuefloatFinite, strictly positive reference unit.

set_unit_states_all_sectionmethod#

def set_unit_states_all_section(state: int, value: float) -> None

set a canonical unit value to the state at all sections

Parameters

NameTypeDescription
stateintstate
valuefloatvalue

set_unit_timemethod#

def set_unit_time(value: float) -> None

set a canonical unit value to the time

Parameters

NameTypeDescription
valuefloatvalue

solvemethod#

def solve(obj: Any = None, display_func: Callable[[], None] = _dummy_func, *, solver: str = 'SLSQP', derivatives: str = 'finite-difference', **options: Any) -> optimize.OptimizeResult

Solve the LGL transcription and return a SciPy OptimizeResult.

solver selects SLSQP (default) or sparse trust-constr. derivatives='finite-difference' preserves arbitrary legacy callbacks. 'structured' assembles exact spectral blocks and local central differences, and 'autograd' uses optional automatic differentiation. Both structured modes require DynamicsModel in every section. AD callbacks use view.array_module and view.condition(); they must not mutate arrays or coerce traced values to Python floats. With trust-constr, AD also supplies matrix-free Hessian-vector products.

dynamics must contain a callable for each section; equality and inequality may be None. running_cost returns one integrand value per node, concatenated in section order, in physical time. The solver applies each section’s (tf - ti) / 2 quadrature factor.

cost_derivative, when set, must differentiate the complete objective (including running cost) with respect to the scaled vector p. All SLSQP options, including ftol, maxiter, eps and disp, are forwarded. Concurrent workers are unsupported because callbacks share this mutable Problem instance.

Solver-specific options are forwarded: SLSQP accepts ftol/eps; trust-constr accepts gtol/xtol/barrier_tol. They are not interchangeable. cost_derivative is used in finite-difference and structured modes; autograd differentiates the complete objective itself.

maxIterator limits total attempts, including the first. A restart occurs only after an iteration limit (SLSQP status 9, trust-constr status 0). Other failures stop immediately and emit RuntimeWarning. Each call resets the restart counter. The final result is also saved in result and every attempt in result_history. display_func is called without arguments after each attempt, as in the legacy API. time and time_all_section always reflect the current parameters.

Parameters

NameTypeDefaultDescription
objAnyNoneUser data passed unchanged to every model/cost/constraint callback.
display_funcCallable[[], None]_dummy_funcCalled without arguments after each solver attempt.
solverstr'SLSQP'“SLSQP” or “trust-constr”.
derivativesstr'finite-difference'“finite-difference”, “structured”, or “autograd”.
**optionsAny{}SciPy solver options, e.g. maxiter, ftol (SLSQP) or gtol (trust-constr).

Returns

OptimizeResultoptimize.OptimizeResult
SciPy result with solver, derivatives and scaled equality_violation/inequality_violation diagnostics. The solution also replaces this problem’s p. Failure returns success=False and emits RuntimeWarning; it does not raise a convergence exception.

solve_adaptivemethod#

def solve_adaptive(obj: Any = None, *, tolerance: float = 1e-05, max_refinements: int = 4, max_nodes: int = 100, growth: float = 1.5, strategy: str = 'p', max_elements: int = 24, min_element_nodes: int = 6, max_order: int = 14, path_check_factor: int = 4, **solve_options: Any) -> optimize.OptimizeResult

Solve and adapt the mesh, updating this problem to the final mesh.

Requires DynamicsModel in every phase. Callbacks must use physical getters and work with changing node counts. For h/hp path sampling, avoid direct p indices and mesh-dependent global reductions.

Parameters

NameTypeDefaultDescription
objAnyNoneUser data passed to all callbacks.
tolerancefloat1e-05Positive tolerance for ODE and scaled path residuals.
max_refinementsint4Maximum changes after the initial solve.
max_nodesint100Maximum total nodes per phase, including interfaces.
growthfloat1.5Node growth factor > 1 for p refinement.
strategystr'p'“p” increases orders; “h” splits elements; “hp” chooses between increasing orders and splitting using error indicators.
max_elementsint24Maximum elements per phase for h/hp.
min_element_nodesint6Minimum child node count for hp, at least four.
max_orderint14Node threshold for choosing h instead of p in hp.
path_check_factorint4Sampling density for h/hp path constraints, at least two. Not used by the p strategy.
**solve_optionsAny{}Options forwarded to solve(), including solver, derivatives and SciPy tolerances.

Returns

OptimizeResultoptimize.OptimizeResult
Final solver result. success reports optimizer convergence; mesh_converged separately reports meeting the mesh tolerance. mesh_history records every solve and residual estimate. h/hp also supply mesh_strategy and mesh_stop_reason. Stopping at a limit emits RuntimeWarning when the mesh has not converged.

statesmethod#

def states(state: int, section: int) -> NDArray[np.float64]

Return one state’s physical values in a phase.

Parameters

NameTypeDescription
stateintVariable index, supporting negative indices.
sectionintPhase index, supporting negative indices.

Returns

ndarrayNDArray[np.float64]
Independent array of shape (nodes[section],). Both sides of internal element interfaces are present. Use set_states() to edit.

states_all_sectionmethod#

def states_all_section(state: int) -> NDArray[np.float64]

Return a state’s physical values concatenated in phase order.

Parameters

NameTypeDescription
stateintVariable index, which must exist in every phase.

Returns

ndarrayNDArray[np.float64]
Shape (sum(nodes),), including duplicated interface nodes.

time_finalmethod#

def time_final(section: int) -> float

Return a phase’s physical end time from the current scaled p.

Parameters

NameTypeDescription
sectionintPhase index; negative indices count from the last phase.

Returns

float
Physical time, equal to p[index_time_final(section)] * unit_time.

time_final_all_sectionmethod#

def time_final_all_section() -> list[float]

Return physical phase end times in phase order.

time_knotsmethod#

def time_knots() -> list[float]

Return physical phase boundaries [t0, tf0, tf1, …].

time_startmethod#

def time_start(section: int) -> float

Return a phase’s physical start time.

Parameters

NameTypeDescription
sectionintPhase index; negative indices count from the last phase.

Returns

float
Physical time. The first phase starts at the fixed t0; later phases start at the preceding phase’s current end time.

time_to_taumethod#

def time_to_tau(time)

Map a finite, nonconstant time vector to [-1, 1].

time_updatemethod#

def time_update() -> NDArray[np.float64]

Return current physical node times concatenated in phase order.

Kept for compatibility. Times are derived from p on access, so explicit refreshes are unnecessary even after direct changes to p.

to_csvmethod#

def to_csv(filename='OpenGoddard_output.csv', delimiter: str = ',') -> None

Write physical times, states and controls in phase/node order.

Parameters

NameTypeDefaultDescription
filenamestr or PathLike'OpenGoddard_output.csv'Output path. Missing components in phases with fewer states or controls are written as NaN.
delimiterstr','Column separator; a comma by default.