Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions machine_design/designs/computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
class ComputationBase(ABC):
def __init__(self, geometry: GeometryBase) -> None:
self.geometry = geometry
self.phases = "ABCDEFGHIJK"[: self.geometry.n_phases]
self.setup_name = "Setup1"
self.rotor_mesh = None
self.set_oper_params()
Expand Down Expand Up @@ -60,6 +61,24 @@ def compute_phase_groups(self):
Q = int(g.geom_params["SlotNumber"])
return phase_groups(Q, g.PolePairs, g.n_phases, belt_offset=g.belt_offset)

def compute_solution_expressions(self):
return [
"Moving1.Position",
"Moving1.Torque",
*[f"FluxLinkage(Phase{p})" for p in self.phases],
*[f"InducedVoltage(Phase{p})" for p in self.phases],
*[f"InputCurrent(Phase{p})" for p in self.phases],
*[f"L(Phase{x},Phase{y})" for x in self.phases for y in self.phases],
]

def extract_phase_results(self, solutions):
# SI units
flux_phases = np.stack([self.extract_expression(solutions, f"FluxLinkage(Phase{p})") for p in self.phases], axis=-1)
vind_phases = np.stack([self.extract_expression(solutions, f"InducedVoltage(Phase{p})") for p in self.phases], axis=-1)
current_phases = np.stack([self.extract_expression(solutions, f"InputCurrent(Phase{p})") for p in self.phases], axis=-1)
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]
return flux_phases, vind_phases, current_phases, L_raw

def push_variables(self, m2d: Maxwell2d) -> None:
for k, v in self.oper_params.items():
m2d[k] = v
Expand Down
22 changes: 6 additions & 16 deletions motors/synrm_3f_36s.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,7 @@ def set_oper_params(self):
self.set_initpos()

def set_solution_expressions(self):
self.solution_expressions = [
"Moving1.Position",
"Moving1.Torque",
*[f"FluxLinkage(Phase{p})" for p in "ABC"],
*[f"InducedVoltage(Phase{p})" for p in "ABC"],
*[f"InputCurrent(Phase{p})" for p in "ABC"],
*[f"L(Phase{x},Phase{y})" for x in "ABC" for y in "ABC"],
]
self.solution_expressions = self.compute_solution_expressions()

def set_output_vars(self):
self.output_vars = {}
Expand Down Expand Up @@ -259,20 +252,17 @@ def extract_results(self, solutions):
torque = self.extract_expression(solutions, "Moving1.Torque")

theta_el = (position - np.deg2rad(self.InitPos)) * self.geometry.PolePairs
flux_phases = np.stack([self.extract_expression(solutions, f"FluxLinkage(Phase{p})") for p in "ABC"], axis=-1)
vind_phases = np.stack([self.extract_expression(solutions, f"InducedVoltage(Phase{p})") for p in "ABC"], axis=-1)
current_phases = np.stack([self.extract_expression(solutions, f"InputCurrent(Phase{p})") for p in "ABC"], axis=-1)

flux_phases, vind_phases, current_phases, L_raw = self.extract_phase_results(solutions)

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

L_raw = [np.stack([self.extract_expression(solutions, f"L(Phase{x},Phase{y})") for y in "ABC"], axis=-1) for x in "ABC"]

L_d_row = np.zeros((len(position), 3))
L_q_row = np.zeros((len(position), 3))
L_d_row = np.zeros((len(position), self.geometry.n_phases))
L_q_row = np.zeros((len(position), self.geometry.n_phases))
for i, L_row in enumerate(L_raw):
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)))
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)))

L_d, _ = to_dq(L_d_row, theta_el, harmonic=1)
_, L_q_raw = to_dq(L_q_row, theta_el, harmonic=1)
Expand Down
36 changes: 13 additions & 23 deletions motors/synrm_5f_40s.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,7 @@ def set_oper_params(self):
self.set_initpos()

def set_solution_expressions(self):
self.solution_expressions = [
"Moving1.Position",
"Moving1.Torque",
*[f"FluxLinkage(Phase{p})" for p in "ABCDE"],
*[f"InducedVoltage(Phase{p})" for p in "ABCDE"],
*[f"InputCurrent(Phase{p})" for p in "ABCDE"],
*[f"L(Phase{x},Phase{y})" for x in "ABCDE" for y in "ABCDE"],
]
self.solution_expressions = self.compute_solution_expressions()

def set_output_vars(self):
self.output_vars = {}
Expand Down Expand Up @@ -231,9 +224,8 @@ def extract_results(self, solutions):
torque = self.extract_expression(solutions, "Moving1.Torque")

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

flux_phases, vind_phases, current_phases, L_raw = self.extract_phase_results(solutions)

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

time = np.array(solutions.primary_sweep_values)
dI_dt_phases = np.zeros((len(time), 5))
for k in range(5):
phase_offset = -2 * np.pi * k / 5 # 5 angles, 0deg, -72deg, -144deg, -216deg, -288deg
dI_dt_phases = np.zeros((len(time), self.geometry.n_phases))
for k in range(self.geometry.n_phases):
phase_offset = -2 * np.pi * k / self.geometry.n_phases
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)

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

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

L_raw = [np.stack([self.extract_expression(solutions, f"L(Phase{x},Phase{y})") for y in "ABCDE"], axis=-1) for x in "ABCDE"]

L_d1_row = np.zeros((len(time), 5))
L_q1_row = np.zeros((len(time), 5))
L_d3_row = np.zeros((len(time), 5))
L_q3_row = np.zeros((len(time), 5))
L_d1_row = np.zeros((len(time), self.geometry.n_phases))
L_q1_row = np.zeros((len(time), self.geometry.n_phases))
L_d3_row = np.zeros((len(time), self.geometry.n_phases))
L_q3_row = np.zeros((len(time), self.geometry.n_phases))
for i, L_row in enumerate(L_raw):
L_d1_row[:, i], L_q1_row[:, i] = (v * 5 / 2 for v in to_dq(L_row, theta_el, harmonic=1))
L_d3_row[:, i], L_q3_row[:, i] = (v * 5 / 2 for v in to_dq(L_row, theta_el, harmonic=3))
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))
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))

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

# Torque_dq = 5 / 2 * self.geometry.PolePairs * ((Flux_d1 * I_q1 - Flux_q1 * I_d1) + 3 * (Flux_d3 * I_q3 - Flux_q3 * I_d3))
# 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))

out = {
"V_d1": V_d1,
Expand Down
10 changes: 6 additions & 4 deletions tests/test_computation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from types import SimpleNamespace

import pytest

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


def test_no_reserved_names_does_not_raise():
_DummyComputation(object(), {"Im": "1A"}, {"pos": "Moving1.Position"})
_DummyComputation(SimpleNamespace(n_phases=3), {"Im": "1A"}, {"pos": "Moving1.Position"})


def test_reserved_name_in_oper_params_raises():
with pytest.raises(ValueError, match="F"):
_DummyComputation(object(), {"F": "50Hz"}, {})
_DummyComputation(SimpleNamespace(n_phases=3), {"F": "50Hz"}, {})


def test_reserved_name_in_output_vars_raises():
with pytest.raises(ValueError, match="Time"):
_DummyComputation(object(), {}, {"Time": "0s"})
_DummyComputation(SimpleNamespace(n_phases=3), {}, {"Time": "0s"})


def test_reserved_name_check_is_case_insensitive():
with pytest.raises(ValueError, match="f"):
_DummyComputation(object(), {"f": "50Hz"}, {})
_DummyComputation(SimpleNamespace(n_phases=3), {"f": "50Hz"}, {})
Loading