Skip to content

Commit 7d29b0d

Browse files
committed
added iteration check
1 parent b65e1cb commit 7d29b0d

2 files changed

Lines changed: 129 additions & 45 deletions

File tree

src/MTPCL_Testcase.ipynb

Lines changed: 41 additions & 30 deletions
Large diffs are not rendered by default.

src/gem_controllers/stages/operation_point_selection/eesm_ops.py

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,11 @@ def _calculate_luts(self):
135135
import numpy as np
136136
import scipy.interpolate as sp_interpolate
137137
from scipy.optimize import minimize
138-
SLSQP_MAXITER = 2000
139-
SLSQP_FTOL = 1e-12
140-
TORQUE_BAND_REL = 1e-3
138+
SLSQP_MAXITER = 300
139+
SLSQP_FTOL = 5e-13
140+
TORQUE_BAND_REL = 5e-4
141141
TORQUE_BAND_ABS = 1e-2
142+
SLSQP_MAXITER_FBK = 600
142143
# ---- pull essentials ----
143144
p = float(self.p)
144145
Ld = float(self.l_d)
@@ -225,30 +226,102 @@ def solve_mtpc_for_T(Tref, x_prev):
225226

226227
bnds = [(-i_s_lim, i_s_lim), (-i_s_lim, i_s_lim), (0.0, i_f_lim)]
227228
#x0 = x_prev if x_prev is not None else _seed_by_hand(Tref)
228-
cons = cons_for_T(Tref)
229-
x0 = np.asarray(x_prev, float)
230-
res = minimize(obj_loss, x0, method='SLSQP',
231-
bounds=bnds, constraints=cons_for_T(Tref),
232-
options=dict(maxiter=SLSQP_MAXITER, ftol=SLSQP_FTOL, disp=False))
233-
if res.success and feasible(*res.x):
234-
return res.x
235-
return x0
236-
229+
cons_eq = cons_for_T(Tref)
230+
x0 = np.asarray(x_prev, float) if x_prev is not None else _seed_by_hand(Tref)
231+
x_keep = np.asarray(x0, float)
232+
stats = {
233+
"nit_eq": 0, "nfev_eq": None,
234+
"nit_fbk1": 0, "nfev_fbk1": None,
235+
"nit_fbk2": 0, "nfev_fbk2": None
236+
}
237+
238+
# --- equality-constrained loss minimization ---
239+
_ctr_eq = {"k": 0}
240+
def _cb_eq(_xk): _ctr_eq["k"] += 1
241+
try:
242+
res = minimize(
243+
obj_loss, x0, method='SLSQP',
244+
bounds=bnds, constraints=cons_for_T(Tref),
245+
options=dict(maxiter=SLSQP_MAXITER, ftol=SLSQP_FTOL, disp=False)
246+
)
247+
stats["nit_eq"] = getattr(res, "nit", None)
248+
stats["nfev_eq"] = getattr(res, "nfev", None)
249+
if res.success and feasible(*res.x):
250+
return res.x, stats
251+
except Exception:
252+
pass
253+
def obj_torque_err(x):
254+
return (torque(x[0], x[1], x[2]) - Tref)**2
255+
256+
cons_soft = [
257+
{'type': 'ineq', 'fun': lambda x: i_s_lim - np.hypot(x[0], x[1])}, # |is| <=
258+
{'type': 'ineq', 'fun': lambda x: x[2]}, # i_f >= 0
259+
{'type': 'ineq', 'fun': lambda x: i_f_lim - x[2]}, # i_f <=
260+
{'type': 'ineq', 'fun': lambda x: V_over_omega**2
261+
- ((Lq * x[1])**2 + (Lm * x[2] + Ld * x[0])**2)},
262+
]
263+
264+
# 2a) minimize torque error
265+
try:
266+
res1 = minimize(
267+
obj_torque_err, x0, method='SLSQP',
268+
bounds=bnds, constraints=cons_soft,
269+
options=dict(maxiter=SLSQP_MAXITER_FBK, ftol=SLSQP_FTOL, disp=False)
270+
)
271+
stats["nit_fbk1"] = getattr(res, "nit", None)
272+
stats["nfev_fbk1"] = getattr(res1, "nfev", None)
273+
if res1.success and feasible(*res1.x):
274+
T_reach = torque(*res1.x)
275+
band = max(abs(T_reach)*TORQUE_BAND_REL, TORQUE_BAND_ABS)
276+
lo, hi = T_reach - band, T_reach + band
277+
278+
# 2b) keep torque in [lo, hi] while minimizing loss
279+
cons_keep = cons_soft + [
280+
{'type': 'ineq', 'fun': lambda x, lo=lo: torque(x[0], x[1], x[2]) - lo},
281+
{'type': 'ineq', 'fun': lambda x, hi=hi: hi - torque(x[0], x[1], x[2])},
282+
]
283+
284+
285+
res2 = minimize(
286+
obj_loss, res1.x, method='SLSQP',
287+
bounds=bnds, constraints=cons_keep,
288+
options=dict(maxiter=SLSQP_MAXITER_FBK, ftol=SLSQP_FTOL, disp=False)
289+
)
290+
stats["nit_fbk2"] = getattr(res, "nit", None)
291+
stats["nfev_fbk2"] = getattr(res2, "nfev", None)
292+
if res2.success and feasible(*res2.x):
293+
T2 = torque(*res2.x)
294+
if lo <= T2 <= hi:
295+
return res2.x, stats
296+
# if loss-min step fails the band, keep the torque-closest point
297+
return res1.x, stats
298+
except Exception:
299+
pass
300+
301+
# as a last resort, keep previous point (keeps curves continuous)
302+
return x_keep, stats
237303
# ---- solve MTPCL, collect optimal points ----
238304
T_curr_cap = 1.5 * p * Lm * i_f_lim * i_s_lim
239305
T_cap = float(min(t_lim, T_curr_cap))
306+
iters_eq, iters_f1, iters_f2 = [], [], []
240307
T_vec = np.linspace(0.0, T_cap, t_count)
241308
x_prev = _seed_by_hand(max(1e-3, 0.05*T_cap))
242309
rows = [] # [T, psi, id, |iq|, if]
243310
for T in T_vec:
244-
x_star = solve_mtpc_for_T(T, x_prev)
311+
x_star, st = solve_mtpc_for_T(T, x_prev)
245312
id_opt, iq_opt, if_opt = x_star
246313
x_prev = x_star
314+
iters_eq.append(st["nit_eq"] or 0)
315+
iters_f1.append(st["nit_fbk1"] or 0)
316+
iters_f2.append(st["nit_fbk2"] or 0)
247317
psi_d = Lm * if_opt + Ld * id_opt
248318
psi_q = Lq * iq_opt
249319
psi = np.hypot(psi_d, psi_q)
250320
rows.append([T, psi, id_opt, abs(iq_opt), if_opt])
251-
321+
print(
322+
f"SLSQP iters — eq mean={np.mean(iters_eq):.1f} max={np.max(iters_eq)}; "
323+
f"fbk1 mean={np.mean(iters_f1):.1f}; fbk2 mean={np.mean(iters_f2):.1f}"
324+
)
252325
bp = np.array(rows, dtype=float)
253326
bp = bp[np.all(np.isfinite(bp), axis=1)]
254327
if bp.shape[0] == 0:
@@ -366,7 +439,7 @@ def _get_t_idx(self, torque):
366439
torque = float(np.clip(torque, 0.0, t_max))
367440
rows = int(self.t_grid_count) - 1
368441
return int(round((torque / t_max) * rows))
369-
442+
370443
def _select_operating_point(self, state, reference):
371444
import numpy as np
372445

0 commit comments

Comments
 (0)