67 """Build full state vector from ullage pressures."""
71 V_copv = getattr(dynamics_params,
"V_copv", 0.006)
75 P_copv = getattr(cfg,
"reg_setpoint", 6.89476e6) * 1.5
if cfg.reg_setpoint
else 15e6
76 P_reg = cfg.reg_setpoint
if cfg.reg_setpoint
else P_copv * cfg.reg_ratio
80 m_gas_copv = (P_copv * V_copv * Z_gas) / (R_gas * T_gas)
81 m_gas_F = (P_u_F * V_u_F * Z_gas) / (R_gas * T_gas)
if V_u_F > 1e-10
else 0.0
82 m_gas_O = (P_u_O * V_u_O * Z_gas) / (R_gas * T_gas)
if V_u_O > 1e-10
else 0.0
85 P_copv, P_reg, P_u_F, P_u_O, P_d_F, P_d_O,
87 m_gas_copv, m_gas_F, m_gas_O,
92 parser = argparse.ArgumentParser(description=
"Build DDP controller policy LUT")
94 "--controller-config",
96 default=str(_ENGINE_SIM_ROOT /
"configs" /
"robust_ddp_default.yaml"),
97 help=
"Controller config YAML",
102 default=str(_ENGINE_SIM_ROOT /
"configs" /
"default.yaml"),
103 help=
"Engine config YAML",
108 default=str(_ENGINE_SIM_ROOT /
"configs" /
"policy_lut.npz"),
109 help=
"Output LUT .npz path",
115 default=[6, 6, 8, 5],
116 metavar=(
"P_F",
"P_O",
"F",
"MR"),
117 help=
"Grid resolution (n_P_u_F n_P_u_O n_F_ref n_MR_ref)",
123 help=
"Override DDP max iterations (default: use config)",
125 args = parser.parse_args()
127 print(
"Loading configs...")
128 cfg = load_controller_config(args.controller_config)
129 if args.max_iterations
is not None:
130 cfg.max_iterations = args.max_iterations
131 print(f
" Overriding max_iterations={args.max_iterations}")
132 engine_config = load_engine_config(args.engine_config)
135 engine_wrapper = EngineWrapper(engine_config)
136 dynamics_params = DynamicsParams.from_config(cfg)
138 n_P_F, n_P_O, n_F, n_MR = args.grid
140 np.linspace(P_U_F_MIN, P_U_F_MAX, n_P_F),
141 np.linspace(P_U_O_MIN, P_U_O_MAX, n_P_O),
142 np.linspace(F_REF_MIN, F_REF_MAX, n_F),
143 np.linspace(MR_REF_MIN, MR_REF_MAX, n_MR),
146 u_grid = np.zeros((n_P_F, n_P_O, n_F, n_MR, N_CONTROL), dtype=np.float64)
148 w_bar = get_w_bar_array(state)
150 total = n_P_F * n_P_O * n_F * n_MR
153 print(f
"Building LUT: grid {n_P_F}x{n_P_O}x{n_F}x{n_MR} = {total} points")
154 print(
"Axes: P_u_F [MPa], P_u_O [MPa], F_ref [kN], MR_ref")
156 for i, P_u_F
in enumerate(axes[0]):
157 for j, P_u_O
in enumerate(axes[1]):
158 for k, F_ref
in enumerate(axes[2]):
159 for m, MR_ref
in enumerate(axes[3]):
161 P_u_F, P_u_O, cfg, dynamics_params
163 F_ref_seq = np.full(cfg.N, F_ref, dtype=np.float64)
164 MR_ref_seq = np.full(cfg.N, MR_ref, dtype=np.float64)
165 u_init = np.full((cfg.N, N_CONTROL), 0.2, dtype=np.float64)
168 solution = solve_ddp(
174 dynamics_params=dynamics_params,
175 engine_wrapper=engine_wrapper,
177 use_robustification=
True,
179 u_grid[i, j, k, m, :] = solution.u_seq[0]
180 except Exception
as e:
181 u_grid[i, j, k, m, :] = 0.1
183 print(f
" Warning at ({i},{j},{k},{m}): {e}")
186 if done % 100 == 0
or done == total:
187 print(f
" Progress: {done}/{total} ({100*done/total:.1f}%)")
189 lut =
PolicyLUT(axes=axes, u_grid=u_grid, bounds_mode=
"clip")
190 lut.save(args.output)
191 print(f
"Saved LUT to {args.output}")