1"""Controller policy look-up table (LUT) for DDP stack.
3Precomputes optimal control u = [u_F, u_O] over a grid of (P_u_F, P_u_O, F_ref, MR_ref)
4and provides fast runtime interpolation. Use when online DDP is too slow.
7from __future__
import annotations
9from pathlib
import Path
10from typing
import Optional, Tuple, List
13from .dynamics
import N_CONTROL, IDX_P_U_F, IDX_P_U_O
17 axes: List[np.ndarray],
19 bounds_mode: str =
"clip",
20) ->
"RegularGridInterpolator":
21 """Create RegularGridInterpolator for u lookup."""
22 from scipy.interpolate
import RegularGridInterpolator
24 return RegularGridInterpolator(
34 """Look-up table for controller policy u = f(P_u_F, P_u_O, F_ref, MR_ref)."""
38 axes: List[np.ndarray],
40 bounds_mode: str =
"clip",
45 axes : List[np.ndarray]
46 Grid axes [P_u_F, P_u_O, F_ref, MR_ref], each 1D sorted
48 Control values shape (n_P_u_F, n_P_u_O, n_F_ref, n_MR_ref, N_CONTROL)
50 "clip" = clamp query to grid bounds; "nearest" = use nearest boundary value
58 axes, u_grid[..., 0], bounds_mode
61 axes, u_grid[..., 1], bounds_mode
72 Interpolate optimal control at query point.
77 Fuel ullage pressure [Pa]
79 Oxidizer ullage pressure [Pa]
83 Reference mixture ratio
87 u : np.ndarray, shape (N_CONTROL,)
90 point = np.array([[P_u_F, P_u_O, F_ref, MR_ref]], dtype=np.float64)
93 for i, ax
in enumerate(self.
axes):
94 point[0, i] = np.clip(point[0, i], ax.min(), ax.max())
100 if not np.isfinite(u_F):
102 if not np.isfinite(u_O):
105 u = np.array([u_F, u_O], dtype=np.float64)
106 return np.clip(u, 0.0, 1.0)
108 def save(self, filepath: str) ->
None:
109 """Save LUT to .npz file."""
110 path = Path(filepath)
111 path.parent.mkdir(parents=
True, exist_ok=
True)
120 bounds_mode=np.array([self.
bounds_mode], dtype=object),
124 def load(cls, filepath: str) ->
"PolicyLUT":
125 """Load LUT from .npz file."""
126 data = np.load(filepath, allow_pickle=
True)
133 u_grid = data[
"u_grid"]
134 bounds_mode = str(data[
"bounds_mode"][0])
if "bounds_mode" in data
else "clip"
135 return cls(axes=axes, u_grid=u_grid, bounds_mode=bounds_mode)
139 """Return grid shape (n_P_u_F, n_P_u_O, n_F_ref, n_MR_ref)."""
140 return self.
u_grid.shape[:-1]
__init__(self, List[np.ndarray] axes, np.ndarray u_grid, str bounds_mode="clip")
None save(self, str filepath)
"PolicyLUT" load(cls, str filepath)
Tuple[int,...] grid_shape(self)
np.ndarray lookup(self, float P_u_F, float P_u_O, float F_ref, float MR_ref)
"RegularGridInterpolator" _make_interpolator(List[np.ndarray] axes, np.ndarray u_grid, str bounds_mode="clip")