Skip to content

Commit 9decec2

Browse files
authored
Merge pull request #61 from AI-RICE/57-parameterize-phase-count
57 parameterize phase count
2 parents f7c12e5 + 91204fb commit 9decec2

4 files changed

Lines changed: 44 additions & 43 deletions

File tree

‎machine_design/designs/computation.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
class ComputationBase(ABC):
1414
def __init__(self, geometry: GeometryBase) -> None:
1515
self.geometry = geometry
16+
self.phases = "ABCDEFGHIJK"[: self.geometry.n_phases]
1617
self.setup_name = "Setup1"
1718
self.rotor_mesh = None
1819
self.set_oper_params()
@@ -60,6 +61,24 @@ def compute_phase_groups(self):
6061
Q = int(g.geom_params["SlotNumber"])
6162
return phase_groups(Q, g.PolePairs, g.n_phases, belt_offset=g.belt_offset)
6263

64+
def compute_solution_expressions(self):
65+
return [
66+
"Moving1.Position",
67+
"Moving1.Torque",
68+
*[f"FluxLinkage(Phase{p})" for p in self.phases],
69+
*[f"InducedVoltage(Phase{p})" for p in self.phases],
70+
*[f"InputCurrent(Phase{p})" for p in self.phases],
71+
*[f"L(Phase{x},Phase{y})" for x in self.phases for y in self.phases],
72+
]
73+
74+
def extract_phase_results(self, solutions):
75+
# SI units
76+
flux_phases = np.stack([self.extract_expression(solutions, f"FluxLinkage(Phase{p})") for p in self.phases], axis=-1)
77+
vind_phases = np.stack([self.extract_expression(solutions, f"InducedVoltage(Phase{p})") for p in self.phases], axis=-1)
78+
current_phases = np.stack([self.extract_expression(solutions, f"InputCurrent(Phase{p})") for p in self.phases], axis=-1)
79+
L_raw = [np.stack([self.extract_expression(solutions, f"L(Phase{x},Phase{y})") for y in self.phases], axis=-1) for x in self.phases]
80+
return flux_phases, vind_phases, current_phases, L_raw
81+
6382
def push_variables(self, m2d: Maxwell2d) -> None:
6483
for k, v in self.oper_params.items():
6584
m2d[k] = v

‎motors/synrm_3f_36s.py‎

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,7 @@ def set_oper_params(self):
190190
self.set_initpos()
191191

192192
def set_solution_expressions(self):
193-
self.solution_expressions = [
194-
"Moving1.Position",
195-
"Moving1.Torque",
196-
*[f"FluxLinkage(Phase{p})" for p in "ABC"],
197-
*[f"InducedVoltage(Phase{p})" for p in "ABC"],
198-
*[f"InputCurrent(Phase{p})" for p in "ABC"],
199-
*[f"L(Phase{x},Phase{y})" for x in "ABC" for y in "ABC"],
200-
]
193+
self.solution_expressions = self.compute_solution_expressions()
201194

202195
def set_output_vars(self):
203196
self.output_vars = {}
@@ -259,20 +252,17 @@ def extract_results(self, solutions):
259252
torque = self.extract_expression(solutions, "Moving1.Torque")
260253

261254
theta_el = (position - np.deg2rad(self.InitPos)) * self.geometry.PolePairs
262-
flux_phases = np.stack([self.extract_expression(solutions, f"FluxLinkage(Phase{p})") for p in "ABC"], axis=-1)
263-
vind_phases = np.stack([self.extract_expression(solutions, f"InducedVoltage(Phase{p})") for p in "ABC"], axis=-1)
264-
current_phases = np.stack([self.extract_expression(solutions, f"InputCurrent(Phase{p})") for p in "ABC"], axis=-1)
255+
256+
flux_phases, vind_phases, current_phases, L_raw = self.extract_phase_results(solutions)
265257

266258
Flux_d, Flux_q = to_dq(flux_phases, theta_el, harmonic=1)
267259
Ui_d, Ui_q = to_dq(vind_phases, theta_el, harmonic=1)
268260
I_d, I_q = to_dq(current_phases, theta_el, harmonic=1)
269261

270-
L_raw = [np.stack([self.extract_expression(solutions, f"L(Phase{x},Phase{y})") for y in "ABC"], axis=-1) for x in "ABC"]
271-
272-
L_d_row = np.zeros((len(position), 3))
273-
L_q_row = np.zeros((len(position), 3))
262+
L_d_row = np.zeros((len(position), self.geometry.n_phases))
263+
L_q_row = np.zeros((len(position), self.geometry.n_phases))
274264
for i, L_row in enumerate(L_raw):
275-
L_d_row[:, i], L_q_row[:, i] = (v * m for v, m in zip(to_dq(L_row, theta_el, harmonic=1), (3 / 2, -3 / 2)))
265+
L_d_row[:, i], L_q_row[:, i] = (v * m for v, m in zip(to_dq(L_row, theta_el, harmonic=1), (self.geometry.n_phases / 2, -self.geometry.n_phases / 2)))
276266

277267
L_d, _ = to_dq(L_d_row, theta_el, harmonic=1)
278268
_, L_q_raw = to_dq(L_q_row, theta_el, harmonic=1)

‎motors/synrm_5f_40s.py‎

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,7 @@ def set_oper_params(self):
6060
self.set_initpos()
6161

6262
def set_solution_expressions(self):
63-
self.solution_expressions = [
64-
"Moving1.Position",
65-
"Moving1.Torque",
66-
*[f"FluxLinkage(Phase{p})" for p in "ABCDE"],
67-
*[f"InducedVoltage(Phase{p})" for p in "ABCDE"],
68-
*[f"InputCurrent(Phase{p})" for p in "ABCDE"],
69-
*[f"L(Phase{x},Phase{y})" for x in "ABCDE" for y in "ABCDE"],
70-
]
63+
self.solution_expressions = self.compute_solution_expressions()
7164

7265
def set_output_vars(self):
7366
self.output_vars = {}
@@ -231,9 +224,8 @@ def extract_results(self, solutions):
231224
torque = self.extract_expression(solutions, "Moving1.Torque")
232225

233226
theta_el = electrical_angle(position, np.deg2rad(self.InitPos), self.geometry.PolePairs, self.RotSign, degrees=False)
234-
flux_phases = np.stack([self.extract_expression(solutions, f"FluxLinkage(Phase{p})") for p in "ABCDE"], axis=-1)
235-
vind_phases = np.stack([self.extract_expression(solutions, f"InducedVoltage(Phase{p})") for p in "ABCDE"], axis=-1)
236-
current_phases = np.stack([self.extract_expression(solutions, f"InputCurrent(Phase{p})") for p in "ABCDE"], axis=-1)
227+
228+
flux_phases, vind_phases, current_phases, L_raw = self.extract_phase_results(solutions)
237229

238230
Flux_d1, Flux_q1 = to_dq(flux_phases, theta_el, harmonic=1)
239231
Flux_d3, Flux_q3 = to_dq(flux_phases, theta_el, harmonic=3)
@@ -248,25 +240,23 @@ def extract_results(self, solutions):
248240
epsI3 = np.atan2(self.Iq3, self.Id3)
249241

250242
time = np.array(solutions.primary_sweep_values)
251-
dI_dt_phases = np.zeros((len(time), 5))
252-
for k in range(5):
253-
phase_offset = -2 * np.pi * k / 5 # 5 angles, 0deg, -72deg, -144deg, -216deg, -288deg
243+
dI_dt_phases = np.zeros((len(time), self.geometry.n_phases))
244+
for k in range(self.geometry.n_phases):
245+
phase_offset = -2 * np.pi * k / self.geometry.n_phases
254246
dI_dt_phases[:, k] = -Im1 * self.w * np.sin(self.w * time + phase_offset + epsI1 - np.pi) - Im3 * 3 * self.w * np.sin(3 * (self.w * time + phase_offset) + epsI3 - np.pi)
255247

256248
V_phases = vind_phases + self.Rstat * current_phases + self.Lew * dI_dt_phases
257249

258250
V_d1, V_q1 = to_dq(V_phases, theta_el, harmonic=1)
259251
V_d3, V_q3 = to_dq(V_phases, theta_el, harmonic=3)
260252

261-
L_raw = [np.stack([self.extract_expression(solutions, f"L(Phase{x},Phase{y})") for y in "ABCDE"], axis=-1) for x in "ABCDE"]
262-
263-
L_d1_row = np.zeros((len(time), 5))
264-
L_q1_row = np.zeros((len(time), 5))
265-
L_d3_row = np.zeros((len(time), 5))
266-
L_q3_row = np.zeros((len(time), 5))
253+
L_d1_row = np.zeros((len(time), self.geometry.n_phases))
254+
L_q1_row = np.zeros((len(time), self.geometry.n_phases))
255+
L_d3_row = np.zeros((len(time), self.geometry.n_phases))
256+
L_q3_row = np.zeros((len(time), self.geometry.n_phases))
267257
for i, L_row in enumerate(L_raw):
268-
L_d1_row[:, i], L_q1_row[:, i] = (v * 5 / 2 for v in to_dq(L_row, theta_el, harmonic=1))
269-
L_d3_row[:, i], L_q3_row[:, i] = (v * 5 / 2 for v in to_dq(L_row, theta_el, harmonic=3))
258+
L_d1_row[:, i], L_q1_row[:, i] = (v * self.geometry.n_phases / 2 for v in to_dq(L_row, theta_el, harmonic=1))
259+
L_d3_row[:, i], L_q3_row[:, i] = (v * self.geometry.n_phases / 2 for v in to_dq(L_row, theta_el, harmonic=3))
270260

271261
Ld1, Ld1q1 = to_dq(L_d1_row, theta_el, harmonic=1)
272262
Lq1d1, Lq1 = to_dq(L_q1_row, theta_el, harmonic=1)
@@ -282,7 +272,7 @@ def extract_results(self, solutions):
282272
Flux_e_d3 = Flux_d3 - (Ld3d1 * I_d1 + Ld3q1 * I_q1 + Ld3 * I_d3 + Ld3q3 * I_q3)
283273
Flux_e_q3 = Flux_q3 - (Lq3d1 * I_d1 + Lq3q1 * I_q1 + Lq3d3 * I_d3 + Lq3 * I_q3)
284274

285-
# Torque_dq = 5 / 2 * self.geometry.PolePairs * ((Flux_d1 * I_q1 - Flux_q1 * I_d1) + 3 * (Flux_d3 * I_q3 - Flux_q3 * I_d3))
275+
# Torque_dq = self.geometry.n_phases / 2 * self.geometry.PolePairs * ((Flux_d1 * I_q1 - Flux_q1 * I_d1) + 3 * (Flux_d3 * I_q3 - Flux_q3 * I_d3))
286276

287277
out = {
288278
"V_d1": V_d1,

‎tests/test_computation.py‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from types import SimpleNamespace
2+
13
import pytest
24

35
from machine_design.designs.computation import ComputationBase
@@ -35,19 +37,19 @@ def extract_results(self, solutions):
3537

3638

3739
def test_no_reserved_names_does_not_raise():
38-
_DummyComputation(object(), {"Im": "1A"}, {"pos": "Moving1.Position"})
40+
_DummyComputation(SimpleNamespace(n_phases=3), {"Im": "1A"}, {"pos": "Moving1.Position"})
3941

4042

4143
def test_reserved_name_in_oper_params_raises():
4244
with pytest.raises(ValueError, match="F"):
43-
_DummyComputation(object(), {"F": "50Hz"}, {})
45+
_DummyComputation(SimpleNamespace(n_phases=3), {"F": "50Hz"}, {})
4446

4547

4648
def test_reserved_name_in_output_vars_raises():
4749
with pytest.raises(ValueError, match="Time"):
48-
_DummyComputation(object(), {}, {"Time": "0s"})
50+
_DummyComputation(SimpleNamespace(n_phases=3), {}, {"Time": "0s"})
4951

5052

5153
def test_reserved_name_check_is_case_insensitive():
5254
with pytest.raises(ValueError, match="f"):
53-
_DummyComputation(object(), {"f": "50Hz"}, {})
55+
_DummyComputation(SimpleNamespace(n_phases=3), {"f": "50Hz"}, {})

0 commit comments

Comments
 (0)