OpenGoddard.optimize
OpenGoddard.optimize - Optimal Trajectories module with PseudoSpectral Method
Conditionクラス#
OpenGoddard._helpersソースを表示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.
パラメーター
| 名前 | 型 | デフォルト | 説明 |
|---|---|---|---|
length | int | 0 | Number of initial zero residuals, normally zero. |
backend | module | np | NumPy-compatible module, supplied by the callback view. |
例
>>> 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).
addメソッド#
def add(arg, unit=1.0)Append scalar or one-dimensional residuals using a positive scale.
change_valueメソッド#
def change_value(index, value)Replace an existing dimensionless residual by its flattened index.
equalメソッド#
def equal(arg1, arg2, unit=1.0)Append (arg1 - arg2)/unit; enforces arg1 = arg2 in an equality callback.
lower_boundメソッド#
def lower_bound(arg1, arg2, unit=1.0)Append (arg1 - arg2)/unit; enforces arg1 >= arg2 in inequalities.
upper_boundメソッド#
def upper_bound(arg1, arg2, unit=1.0)Append (arg2 - arg1)/unit; enforces arg1 <= arg2 in inequalities.
Dynamicsクラス#
OpenGoddard._helpersソースを表示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.
例
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.
>>> 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()Guessクラス#
OpenGoddard._helpersソースを表示class GuessConstruct physical initial guesses on a supplied time grid.
constantメソッドclassmethod#
def constant(time, const: float) -> NDArray[np.float64]Return one constant physical value per time node.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
time | array_like | Time nodes; only their count is used. |
const | float | Constant physical value. |
戻り値
ndarrayNDArray[np.float64]- Values of shape (N,).
cubicメソッドclassmethod#
def cubic(time, y0: float, yprime0: float, yf: float, yprimef: float) -> NDArray[np.float64]Interpolate endpoint values and physical-time slopes with a cubic.
パラメーター
戻り値
ndarrayNDArray[np.float64]- Values of shape (N,).
linearメソッドclassmethod#
def linear(time, y0: float, yf: float) -> NDArray[np.float64]Interpolate physical endpoint values linearly in time.
パラメーター
戻り値
ndarrayNDArray[np.float64]- Values of shape (N,).
plotメソッドclassmethod#
def plot(x, y, title='', xlabel='', ylabel='')Plot physical initial-guess values against time.
zerosメソッドclassmethod#
def zeros(time) -> NDArray[np.float64]Return one zero per time node.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
time | array_like | Time nodes; only their count is used. |
戻り値
ndarrayNDArray[np.float64]- Values of shape (N,).
Problemクラス#
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.
パラメーター
| 名前 | 型 | デフォルト | 説明 |
|---|---|---|---|
time_init | sequence of float | 必須 | Strictly increasing physical phase times. |
nodes | sequence of int | 必須 | LGL node counts, at least two per phase. |
number_of_states | sequence of int | 必須 | Positive state counts per phase. |
number_of_controls | sequence of int | 必須 | Nonnegative control counts per phase. |
maxIterator | int | 100 | Maximum solver attempts, including the first attempt.
Another attempt is made only after an iteration-limit status.
solve(maxiter=...) limits iterations within each attempt. |
method | str | 'LGL' | Only "LGL" is supported for optimal control. |
mesh | optional | None | Per-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.
属性
| 名前 | 型 | 説明 |
|---|---|---|
nodes | list[int] | Node counts per phase, including duplicated interfaces. |
number_of_states | list[int] | State counts per phase. |
number_of_controls | list[int] | Control counts per phase. |
number_of_section | int | Number of logical phases. |
number_of_param | ndarray | State plus control counts per phase. |
number_of_variables | int | Length of p, including phase end times. |
div | list[list[int]] | Exclusive ends of state/control blocks in p. |
tau | list[ndarray] | LGL phase coordinates in [-1, 1]. |
w | list[ndarray] | Quadrature weights in phase coordinates. |
D | list[ndarray] | Differentiation matrices in phase coordinates. |
time | list[ndarray] | Physical node times, derived from current p. |
time_all_section | ndarray | Times concatenated in phase order. |
unit_states | list[list[float]] | State reference units per phase. |
unit_controls | list[list[float]] | Control reference units per phase. |
unit_time | float | Time reference unit. |
p | ndarray | Scaled states, controls and phase end times, shape (N,). |
bounds | list[tuple] | Scaled lower/upper bounds for every entry of p. |
dynamics | list[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_smooth | list[bool] | Enforce continuity at phase knots with equal state counts. Disable it when supplying jump conditions. |
cost | Callable | None | Terminal cost, called as cost(problem, obj). |
running_cost | Callable | None | Physical-time integrand, shape
(sum(nodes),), called as running_cost(problem, obj). |
cost_derivative | function | Complete objective gradient in p. |
equality | Callable | None | One-dimensional residuals equal to zero. |
inequality | Callable | None | One-dimensional residuals >= 0. |
array_module | NumPy; differentiable callback views supply autograd.numpy. | |
metadata | dict | User metadata, shallow-copied on refine/remesh. |
result | OptimizeResult | None | Latest solver result, or None before solve. |
result_history | list[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.
D属性インスタンス属性#
D = [rule[2] for rule in rules]array_module属性クラス属性インスタンス属性#
array_module = npbounds属性インスタンス属性#
bounds = [(None, None)] * self.number_of_variablescost属性インスタンス属性#
cost: Callable[[Problem, Any], float] | None = Nonecost_derivative属性インスタンス属性#
cost_derivative: Callable[[Problem, Any], NDArray[np.float64]] | None = Nonediv属性インスタンス属性#
div = [list(phase) for phase in self._layout.divisions]dynamics属性インスタンス属性#
dynamics: list[Callable[[Problem, Any, int], NDArray[np.float64]] | None] = [None] * self.number_of_sectionequality属性インスタンス属性#
equality: Callable[[Problem, Any], NDArray[np.float64]] | None = Noneinequality属性インスタンス属性#
inequality: Callable[[Problem, Any], NDArray[np.float64]] | None = Noneiterator属性インスタンス属性#
iterator = 0knot_states_smooth属性インスタンス属性#
knot_states_smooth = [True] * self.number_of_section - 1maxIterator属性インスタンス属性#
maxIterator = integer(maxIterator, 'maxIterator', 1)mesh属性インスタンス属性#
mesh = normalize_mesh(self.nodes, mesh)metadata属性インスタンス属性#
metadata: dict[str, Any] = {}method属性インスタンス属性#
method = methodnodes属性インスタンス属性#
nodes = [integer(n, 'nodes', 2) for n in nodes]number_of_controls属性インスタンス属性#
number_of_controls = [integer(n, 'number_of_controls') for n in number_of_controls]number_of_param属性インスタンス属性#
number_of_param = np.array(self.number_of_states) + self.number_of_controlsnumber_of_section属性インスタンス属性#
number_of_section = len(self.nodes)number_of_states属性インスタンス属性#
number_of_states = [integer(n, 'number_of_states', 1) for n in number_of_states]number_of_variables属性インスタンス属性#
number_of_variables = self._layout.sizep属性インスタンス属性#
p = np.zeros(self.number_of_variables)result属性インスタンス属性#
result: optimize.OptimizeResult | None = Noneresult_history属性インスタンス属性#
result_history: list[optimize.OptimizeResult] = []running_cost属性インスタンス属性#
running_cost: Callable[[Problem, Any], NDArray[np.float64]] | None = Nonet0属性インスタンス属性#
t0 = float(times[0])tau属性インスタンス属性#
tau = [rule[0] for rule in rules]time_init属性インスタンス属性#
time_init = times.tolist()unit_controls属性インスタンス属性#
unit_controls = [[1.0] * n for n in self.number_of_controls]unit_states属性インスタンス属性#
unit_states = [[1.0] * n for n in self.number_of_states]unit_time属性インスタンス属性#
unit_time = 1.0w属性インスタンス属性#
w = [rule[1] for rule in rules]timeプロパティ#
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_sectionプロパティ#
time_all_section: NDArray[np.float64]Physical node times concatenated in phase order, including interfaces.
conditionメソッド#
def condition(length: int = 0) -> ConditionBuild equality or inequality residuals in this view’s array backend.
パラメーター
| 名前 | 型 | デフォルト | 説明 |
|---|---|---|---|
length | int | 0 | Number of initial zero residuals, normally zero. |
戻り値
ConditionCondition- A residual builder. Use equal() for equality callbacks; lower_bound()/upper_bound() produce nonnegative feasible residuals.
controlsメソッド#
def controls(control: int, section: int) -> NDArray[np.float64]Return one control’s physical values in a phase.
パラメーター
戻り値
ndarrayNDArray[np.float64]- Independent array of shape (nodes[section],). Both sides of internal element interfaces are present. Use set_controls() to edit.
controls_all_sectionメソッド#
def controls_all_section(control: int) -> NDArray[np.float64]Return a control’s physical values concatenated in phase order.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
control | int | Variable index, which must exist in every phase. |
戻り値
ndarrayNDArray[np.float64]- Shape (sum(nodes),), including duplicated interface nodes.
estimate_errorメソッド#
def estimate_error(obj: Any = None, check_factor: int = 2) -> dict[str, Any]Estimate dimensionless residuals between the LGL nodes.
パラメーター
戻り値
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.
発生する例外
ValueError- A phase lacks DynamicsModel, has nonpositive duration, or uses varying nodal box bounds.
index_controlsメソッド#
def index_controls(control: int, section: int, index: int | None = None) -> intReturn the index of a control node in the scaled vector p.
パラメーター
戻り値
intint- Index into p, whose values use reference-unit scaling.
index_statesメソッド#
def index_states(state: int, section: int, index: int | None = None) -> intReturn the index of a state node in the scaled vector p.
パラメーター
戻り値
intint- Index into p, whose values use reference-unit scaling.
index_time_finalメソッド#
def index_time_final(section: int) -> intReturn the index of a phase end time in the scaled vector p.
method_LGメソッド#
def method_LG(n) -> tuple[NDArray[np.float64], ...]Legendre-Gauss Pseudospectral method Gauss nodes are roots of .
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
n | int | number of nodes |
戻り値
tuple[NDArray[np.float64], ...]- tuple[ndarray, ndarray, ndarray]: Nodes (n,), weights (n,), and D (n, n).
method_LGLメソッド#
def method_LGL(n) -> tuple[NDArray[np.float64], ...]Legendre-Gauss-Lobatto Pseudospectral method Gauss-Lobatto nodes are roots of , plus -1 and 1.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
n | int | number of nodes |
戻り値
tuple[NDArray[np.float64], ...]- tuple[ndarray, ndarray, ndarray]: Nodes (n,), weights (n,), and D (n, n).
method_LGRメソッド#
def method_LGR(n) -> tuple[NDArray[np.float64], ...]Legendre-Gauss-Radau Pseudospectral method Gauss-Radau nodes are roots of .
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
n | int | number of nodes |
戻り値
tuple[NDArray[np.float64], ...]- tuple[ndarray, ndarray, ndarray]: Nodes (n,), weights (n,), and D (n, n).
plotメソッド#
def plot(title_comment='')plot inner variables that to be optimized
パラメーター
| 名前 | 型 | デフォルト | 説明 |
|---|---|---|---|
title_comment | str | '' | string for title |
refineメソッド#
def refine(nodes) -> ProblemReturn a new problem with interpolated guesses and new node counts.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
nodes | sequence of int | Total node counts per phase, including duplicated interfaces. At least two nodes per element are required. |
戻り値
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.
発生する例外
ValueError- Counts are invalid or box bounds vary between nodes.
remeshメソッド#
def remesh(mesh) -> ProblemReturn 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.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
mesh | sequence | Per-phase lists of (left, right, nodes) elements partitioning [-1, 1]. Each element includes both endpoints. |
戻り値
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.
発生する例外
ValueError- The mesh is invalid or box bounds vary between nodes.
set_controlsメソッド#
def set_controls(control: int, section: int, value: ArrayLike) -> NoneSet one control’s initial guess in physical units.
パラメーター
発生する例外
ValueError- Values have the wrong shape or are not finite.
set_controls_all_sectionメソッド#
def set_controls_all_section(control: int, value_all_section: ArrayLike) -> NoneSet a control’s physical initial guess across all phases.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
control | int | Variable index, which must exist in every phase. |
value_all_section | ArrayLike | Finite values of shape (sum(nodes),), in phase order and including duplicated interface nodes. |
set_controls_boundsメソッド#
def set_controls_bounds(control: int, section: int, lb: float | None, ub: float | None) -> NoneSet constant physical box bounds at every node of a phase.
set_controls_bounds_all_sectionメソッド#
def set_controls_bounds_all_section(control: int, lb: float | None, ub: float | None) -> Noneset value to bounds of control at all sections
set_statesメソッド#
def set_states(state: int, section: int, value: ArrayLike) -> NoneSet one state’s initial guess in physical units.
パラメーター
発生する例外
ValueError- Values have the wrong shape or are not finite.
set_states_all_sectionメソッド#
def set_states_all_section(state: int, value_all_section: ArrayLike) -> NoneSet a state’s physical initial guess across all phases.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
state | int | Variable index, which must exist in every phase. |
value_all_section | ArrayLike | Finite values of shape (sum(nodes),), in phase order and including duplicated interface nodes. |
set_states_boundsメソッド#
def set_states_bounds(state: int, section: int, lb: float | None, ub: float | None) -> NoneSet constant physical box bounds at every node of a phase.
set_states_bounds_all_sectionメソッド#
def set_states_bounds_all_section(state: int, lb: float | None, ub: float | None) -> Noneset value to bounds of state at all sections
set_time_finalメソッド#
def set_time_final(section: int, value: float) -> NoneSet a phase’s end-time guess in physical units.
パラメーター
発生する例外
ValueError- The end time is not finite.
set_time_final_boundsメソッド#
def set_time_final_bounds(section: int, lb: float | None, ub: float | None) -> Noneset value to bounds of time_final at specific section
set_unit_controlsメソッド#
def set_unit_controls(control: int, section: int, value: float) -> NoneChange a control’s reference unit, preserving physical values and bounds.
set_unit_controls_all_sectionメソッド#
def set_unit_controls_all_section(control: int, value: float) -> Noneset a canonical unit value to the control at all sections
set_unit_statesメソッド#
def set_unit_states(state: int, section: int, value: float) -> NoneChange a state’s reference unit, preserving physical values and bounds.
set_unit_states_all_sectionメソッド#
def set_unit_states_all_section(state: int, value: float) -> Noneset a canonical unit value to the state at all sections
set_unit_timeメソッド#
def set_unit_time(value: float) -> Noneset a canonical unit value to the time
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
value | float | value |
solveメソッド#
def solve(obj: Any = None, display_func: Callable[[], None] = _dummy_func, *, solver: str = 'SLSQP', derivatives: str = 'finite-difference', **options: Any) -> optimize.OptimizeResultSolve 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.
パラメーター
| 名前 | 型 | デフォルト | 説明 |
|---|---|---|---|
obj | Any | None | User data passed unchanged to every model/cost/constraint callback. |
display_func | Callable[[], None] | _dummy_func | Called without arguments after each solver attempt. |
solver | str | 'SLSQP' | “SLSQP” or “trust-constr”. |
derivatives | str | 'finite-difference' | “finite-difference”, “structured”, or “autograd”. |
**options | Any | {} | SciPy solver options, e.g. maxiter, ftol (SLSQP) or gtol (trust-constr). |
戻り値
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_adaptiveメソッド#
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.OptimizeResultSolve 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.
パラメーター
| 名前 | 型 | デフォルト | 説明 |
|---|---|---|---|
obj | Any | None | User data passed to all callbacks. |
tolerance | float | 1e-05 | Positive tolerance for ODE and scaled path residuals. |
max_refinements | int | 4 | Maximum changes after the initial solve. |
max_nodes | int | 100 | Maximum total nodes per phase, including interfaces. |
growth | float | 1.5 | Node growth factor > 1 for p refinement. |
strategy | str | 'p' | “p” increases orders; “h” splits elements; “hp” chooses between increasing orders and splitting using error indicators. |
max_elements | int | 24 | Maximum elements per phase for h/hp. |
min_element_nodes | int | 6 | Minimum child node count for hp, at least four. |
max_order | int | 14 | Node threshold for choosing h instead of p in hp. |
path_check_factor | int | 4 | Sampling density for h/hp path constraints, at least two. Not used by the p strategy. |
**solve_options | Any | {} | Options forwarded to solve(), including solver, derivatives and SciPy tolerances. |
戻り値
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.
statesメソッド#
def states(state: int, section: int) -> NDArray[np.float64]Return one state’s physical values in a phase.
パラメーター
戻り値
ndarrayNDArray[np.float64]- Independent array of shape (nodes[section],). Both sides of internal element interfaces are present. Use set_states() to edit.
states_all_sectionメソッド#
def states_all_section(state: int) -> NDArray[np.float64]Return a state’s physical values concatenated in phase order.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
state | int | Variable index, which must exist in every phase. |
戻り値
ndarrayNDArray[np.float64]- Shape (sum(nodes),), including duplicated interface nodes.
time_finalメソッド#
def time_final(section: int) -> floatReturn a phase’s physical end time from the current scaled p.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
section | int | Phase index; negative indices count from the last phase. |
戻り値
float- Physical time, equal to p[index_time_final(section)] * unit_time.
time_final_all_sectionメソッド#
def time_final_all_section() -> list[float]Return physical phase end times in phase order.
time_knotsメソッド#
def time_knots() -> list[float]Return physical phase boundaries [t0, tf0, tf1, …].
time_startメソッド#
def time_start(section: int) -> floatReturn a phase’s physical start time.
パラメーター
| 名前 | 型 | 説明 |
|---|---|---|
section | int | Phase index; negative indices count from the last phase. |
戻り値
float- Physical time. The first phase starts at the fixed t0; later phases start at the preceding phase’s current end time.
time_to_tauメソッド#
def time_to_tau(time)Map a finite, nonconstant time vector to [-1, 1].
time_updateメソッド#
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_csvメソッド#
def to_csv(filename='OpenGoddard_output.csv', delimiter: str = ',') -> NoneWrite physical times, states and controls in phase/node order.