From 3d0d0ce91d4b06aa57dd131fb4f3797a6c911fa1 Mon Sep 17 00:00:00 2001 From: haitao-ZCU Date: Tue, 8 Sep 2026 10:14:47 +0200 Subject: [PATCH 1/7] Add golden-master regression test for motor2 computation --- tests/golden/motor2_baseline/readme.md | 18 ++++++++ tests/golden/motor2_baseline/tor.npy | Bin 0 -> 936 bytes tests/test_motor2_golden.py | 61 +++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 tests/golden/motor2_baseline/readme.md create mode 100644 tests/golden/motor2_baseline/tor.npy create mode 100644 tests/test_motor2_golden.py diff --git a/tests/golden/motor2_baseline/readme.md b/tests/golden/motor2_baseline/readme.md new file mode 100644 index 0000000..4589ddf --- /dev/null +++ b/tests/golden/motor2_baseline/readme.md @@ -0,0 +1,18 @@ +Golden baseline for motor2 (now `motors/synrm_5f_40s.py`), for issue #20. + +`tor.npy` is the raw torque waveform (101 points) from Ansys 2025.1 solve of +`machine_design/design2.py::Design2` at commit `c438e18` (dropped the unsupported +deriv() voltage terms), the earliest version confirmed to be free of known bugs +(Rstat, set_derived_params, deriv()) at the time of writing. + +Reproduce with (in a worktree checked out at c438e18): + + python notebooks/run2.py --nper 1 --aedt-version 2025.1 + +Setup used: +- generator: HacklGenerator_OneLambda, seed=0 +- current setpoint: Id1=7.0711, Iq1=7.0711, Id3=0.0, Iq3=0.0 +- Nper=1 (full electrical period), PointPer=101 +- r_stator_end=0.7, offset=0.35 + +Result at capture time: T_mean=35.6431 Nm, ripple=11.374 %, n_pts=101. diff --git a/tests/golden/motor2_baseline/tor.npy b/tests/golden/motor2_baseline/tor.npy new file mode 100644 index 0000000000000000000000000000000000000000..29f181131d72fe43b95e618acfcbe6be8398c992 GIT binary patch literal 936 zcmbV~|5MX-9LN1~b{^6Qq?#ma$d@^ygSARAdhhvp^Vt$>q8)Y{qr3?tJO&aA2}AtC z1XqE4b82u(7o)`B96u~}I_1m6Ga?jk47!XBI^v428YF?xr+=Z>Z?ET1kJsz*tlqs( zxktTv)!|j2iqdq3)LhXHp(rkWyC_O1NoKo(Q*4$ zNsJ^)93?y|{NLj0%Rd&SyFMXz~ zydFznl+`_%l2TMONGq2uEXGSC2ReHgyrbwZU{ZkKrKIrXGJ=kxy+&I^JQ4;II!sPE ztm_)Y3%e=wsxLLiT!8rbxEbFHf^F&(SAJYW;bv0nj=NYmrGm0EVu~AET!wc&GMwoN z>=`;t@XvyzwLCN)1Jf_OezIH+XWOgRVm}JrC+DZ@Z9t^DaPFp#VDXe{OTYw!?opfj z8x0G^TPkWEWpJ*Sw`OKYhNAtoaj!KJ2xd!e-{k(+-1uSdd=AIwGB|w|6ie-4ZxlO# zHt$7G-v!5psN>oc{%j`m4>R7S6|d?;jS0 zTL$yYrfY#lg5iS)M3zSsNs&(pSN6)$>JFI|*cc4ozfdTt1PX7oBzA=mq<(*`;LcwR zE=6Ce4B~ubl=#-%;rRY8NPPL&xC{@w9ntsB6T})XPiM_inA3fJ7&aGYZG4-+dgOqfdKpOfHEJqaSvx*pAb8(d8%a87Ss7hgB&HVhM_{R*I z_+f=guMEoxXOGM`5QH{6lEvJ;N6pFp%6vIYefL(U2`EyWv!u=n^e08A()JMi3m0sR AVgLXD literal 0 HcmV?d00001 diff --git a/tests/test_motor2_golden.py b/tests/test_motor2_golden.py new file mode 100644 index 0000000..c21ab90 --- /dev/null +++ b/tests/test_motor2_golden.py @@ -0,0 +1,61 @@ +import os + +import numpy as np +import pytest + +from machine_design.config import load_config +from machine_design.designs import load_design +from machine_design.optimization.generators import HacklGenerator_OneLambda +from motors.synrm_5f_40s import Computation, Geometry + +pytestmark = pytest.mark.ansys + +aedt_version = load_config()["aedt_version"] +r_stator_end = 0.7 +offset = 0.35 +seed = 0 +current_setpoint = (7.0711, 7.0711, 0.0, 0.0) +num_cores = 4 + +golden_tor = np.load(os.path.join(os.path.dirname(__file__), "golden", "motor2_baseline", "tor.npy")) + + +def test_torque_matches_golden_baseline(tmp_path): + geometry = Geometry() + computation = Computation(geometry) + design = load_design( + str(tmp_path / "motor2_golden_check.aedt"), + "Motor2GoldenCheck", + "Design01", + aedt_version, + geometry, + computation, + non_graphical=True, + new_desktop=True, + close_on_exit=False, + ) + try: + design.m2d["Nper"] = "1" + + np.random.seed(seed) + generator = HacklGenerator_OneLambda(design, r_stator_end, offset=offset) + while True: + params = generator.random_parameters() + generator.set_parameters(params) + barriers = generator.generate_barriers() + barriers = generator.split_barriers(barriers) + if generator.feasible_barriers(barriers): + break + + design.add_rotor() + for barrier in barriers: + design.add_rotor_barrier(barrier) + + result = design.compute(*current_setpoint, NUM_CORES=num_cores) + design.delete_rotor() + + assert result is not None + Tor = np.asarray(result["Moving1.Torque"], dtype=float) + np.testing.assert_allclose(Tor, golden_tor, rtol=0.10, err_msg="torque waveform drifted from the golden baseline") + finally: + design.close_project() \ No newline at end of file From 644f27ca1c7efe069f4f72c4ee4d0ef718376c84 Mon Sep 17 00:00:00 2001 From: haitao-ZCU Date: Wed, 9 Sep 2026 10:58:36 +0200 Subject: [PATCH 2/7] Store motor2 golden baseline as text-readable CSV instead of npy --- tests/golden/motor2_baseline/readme.md | 4 +- tests/golden/motor2_baseline/tor.csv | 102 +++++++++++++++++++++++++ tests/golden/motor2_baseline/tor.npy | Bin 936 -> 0 bytes tests/test_motor2_golden.py | 4 +- 4 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 tests/golden/motor2_baseline/tor.csv delete mode 100644 tests/golden/motor2_baseline/tor.npy diff --git a/tests/golden/motor2_baseline/readme.md b/tests/golden/motor2_baseline/readme.md index 4589ddf..20a6903 100644 --- a/tests/golden/motor2_baseline/readme.md +++ b/tests/golden/motor2_baseline/readme.md @@ -1,11 +1,11 @@ Golden baseline for motor2 (now `motors/synrm_5f_40s.py`), for issue #20. -`tor.npy` is the raw torque waveform (101 points) from Ansys 2025.1 solve of +`tor.csv` is the raw torque waveform (101 points) from Ansys 2025.1 solve of `machine_design/design2.py::Design2` at commit `c438e18` (dropped the unsupported deriv() voltage terms), the earliest version confirmed to be free of known bugs (Rstat, set_derived_params, deriv()) at the time of writing. -Reproduce with (in a worktree checked out at c438e18): +Reproduce with (checked out at commit c438e18, with Ansys 2025.1 installed): python notebooks/run2.py --nper 1 --aedt-version 2025.1 diff --git a/tests/golden/motor2_baseline/tor.csv b/tests/golden/motor2_baseline/tor.csv new file mode 100644 index 0000000..e3a3585 --- /dev/null +++ b/tests/golden/motor2_baseline/tor.csv @@ -0,0 +1,102 @@ +torque +3.043239716233519943e+01 +3.591620613263980033e+01 +4.147767354234220250e+01 +3.574584679986870128e+01 +3.222814259667190129e+01 +3.095928780826810112e+01 +3.786632943847219934e+01 +4.303809933097770113e+01 +3.662076900515599931e+01 +3.281725230483630185e+01 +3.007174401871689895e+01 +3.573888228731269834e+01 +4.120300585270319971e+01 +3.599792664357870109e+01 +3.253304600890449905e+01 +3.077961012251270034e+01 +3.772157506313229902e+01 +4.269723783739590317e+01 +3.616353533590159941e+01 +3.238096010848430240e+01 +3.022553356865880048e+01 +3.640573091630270142e+01 +4.129683127423879796e+01 +3.559188344074919996e+01 +3.272074816481890025e+01 +3.055465920758080145e+01 +3.830244497279409899e+01 +4.353545394998270268e+01 +3.661697884091280031e+01 +3.366794915233990082e+01 +3.056269742457439875e+01 +3.602187103095860010e+01 +4.166798739286939934e+01 +3.632469333624570140e+01 +3.187625812486179910e+01 +3.046426076181120024e+01 +3.827779223861939784e+01 +4.311334873546699953e+01 +3.612120642732909914e+01 +3.342678637451909651e+01 +3.039746031080100153e+01 +3.584430341074399706e+01 +4.071605965070880018e+01 +3.577802304563969926e+01 +3.185131848203090144e+01 +3.064924982827459843e+01 +3.786522764290769771e+01 +4.288253181038940198e+01 +3.654726462989800240e+01 +3.348056735319730137e+01 +3.037035945776889889e+01 +3.585879004684589688e+01 +4.103728202628079913e+01 +3.619210085481090289e+01 +3.212317336525379829e+01 +2.945254472539020085e+01 +3.733891343842709887e+01 +4.307352752108909755e+01 +3.611056883879589918e+01 +3.353969974209889671e+01 +3.039529832749450122e+01 +3.610660709819040193e+01 +4.109449944947869682e+01 +3.599751397114710016e+01 +3.184092175468779828e+01 +3.051594132788940072e+01 +3.790347759538030203e+01 +4.264059829999010276e+01 +3.542281927254170171e+01 +3.313432271491159753e+01 +3.013188646331990128e+01 +3.591239854493939987e+01 +4.128987331650500181e+01 +3.619365168201039751e+01 +3.190819056692449962e+01 +3.059694193251829830e+01 +3.788616748597309680e+01 +4.303724301345680203e+01 +3.650051295735659806e+01 +3.256492181153360121e+01 +3.029326519453649880e+01 +3.575983958061750201e+01 +4.139503045675009929e+01 +3.610917538349340106e+01 +3.266359520960779861e+01 +3.072515190675330032e+01 +3.785815632864949976e+01 +4.321610873451739820e+01 +3.581399530789410335e+01 +3.335051847721790352e+01 +3.022432928828660081e+01 +3.591319643000360173e+01 +4.072146448594769907e+01 +3.593955821330690270e+01 +3.188008857613809965e+01 +3.064438596863509900e+01 +3.789969953786410173e+01 +4.293203942225679981e+01 +3.623742697472250285e+01 +3.338758074574499801e+01 +3.031403113303969832e+01 diff --git a/tests/golden/motor2_baseline/tor.npy b/tests/golden/motor2_baseline/tor.npy deleted file mode 100644 index 29f181131d72fe43b95e618acfcbe6be8398c992..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 936 zcmbV~|5MX-9LN1~b{^6Qq?#ma$d@^ygSARAdhhvp^Vt$>q8)Y{qr3?tJO&aA2}AtC z1XqE4b82u(7o)`B96u~}I_1m6Ga?jk47!XBI^v428YF?xr+=Z>Z?ET1kJsz*tlqs( zxktTv)!|j2iqdq3)LhXHp(rkWyC_O1NoKo(Q*4$ zNsJ^)93?y|{NLj0%Rd&SyFMXz~ zydFznl+`_%l2TMONGq2uEXGSC2ReHgyrbwZU{ZkKrKIrXGJ=kxy+&I^JQ4;II!sPE ztm_)Y3%e=wsxLLiT!8rbxEbFHf^F&(SAJYW;bv0nj=NYmrGm0EVu~AET!wc&GMwoN z>=`;t@XvyzwLCN)1Jf_OezIH+XWOgRVm}JrC+DZ@Z9t^DaPFp#VDXe{OTYw!?opfj z8x0G^TPkWEWpJ*Sw`OKYhNAtoaj!KJ2xd!e-{k(+-1uSdd=AIwGB|w|6ie-4ZxlO# zHt$7G-v!5psN>oc{%j`m4>R7S6|d?;jS0 zTL$yYrfY#lg5iS)M3zSsNs&(pSN6)$>JFI|*cc4ozfdTt1PX7oBzA=mq<(*`;LcwR zE=6Ce4B~ubl=#-%;rRY8NPPL&xC{@w9ntsB6T})XPiM_inA3fJ7&aGYZG4-+dgOqfdKpOfHEJqaSvx*pAb8(d8%a87Ss7hgB&HVhM_{R*I z_+f=guMEoxXOGM`5QH{6lEvJ;N6pFp%6vIYefL(U2`EyWv!u=n^e08A()JMi3m0sR AVgLXD diff --git a/tests/test_motor2_golden.py b/tests/test_motor2_golden.py index c21ab90..ea3b2f1 100644 --- a/tests/test_motor2_golden.py +++ b/tests/test_motor2_golden.py @@ -17,7 +17,7 @@ current_setpoint = (7.0711, 7.0711, 0.0, 0.0) num_cores = 4 -golden_tor = np.load(os.path.join(os.path.dirname(__file__), "golden", "motor2_baseline", "tor.npy")) +golden_tor = np.loadtxt(os.path.join(os.path.dirname(__file__), "golden", "motor2_baseline", "tor.csv"), delimiter=",", skiprows=1) def test_torque_matches_golden_baseline(tmp_path): @@ -58,4 +58,4 @@ def test_torque_matches_golden_baseline(tmp_path): Tor = np.asarray(result["Moving1.Torque"], dtype=float) np.testing.assert_allclose(Tor, golden_tor, rtol=0.10, err_msg="torque waveform drifted from the golden baseline") finally: - design.close_project() \ No newline at end of file + design.close_project() From 27d88148080ee040190af5a20379d04a778f6360 Mon Sep 17 00:00:00 2001 From: haitao-ZCU Date: Thu, 10 Sep 2026 15:43:50 +0200 Subject: [PATCH 3/7] Use each motor's default Nper --- tests/golden/motor1_baseline/readme.md | 11 +++ tests/golden/motor1_baseline/tor1.csv | 19 +++++ tests/golden/motor2_baseline/readme.md | 16 ++-- tests/golden/motor2_baseline/tor.csv | 102 ------------------------- tests/golden/motor2_baseline/tor2.csv | 12 +++ tests/test_motor1_golden.py | 58 ++++++++++++++ tests/test_motor2_golden.py | 6 +- 7 files changed, 107 insertions(+), 117 deletions(-) create mode 100644 tests/golden/motor1_baseline/readme.md create mode 100644 tests/golden/motor1_baseline/tor1.csv delete mode 100644 tests/golden/motor2_baseline/tor.csv create mode 100644 tests/golden/motor2_baseline/tor2.csv create mode 100644 tests/test_motor1_golden.py diff --git a/tests/golden/motor1_baseline/readme.md b/tests/golden/motor1_baseline/readme.md new file mode 100644 index 0000000..2b96013 --- /dev/null +++ b/tests/golden/motor1_baseline/readme.md @@ -0,0 +1,11 @@ +Golden baseline for motor1 (`motors/synrm_3f_36s.py`), for issue #20. + +`tor1.csv` is the raw torque waveform from an Ansys 2025.1 solve of +`motors/synrm_3f_36s.py::Computation` on main. + +Setup used: +- generator: HacklGenerator_OneLambda, seed=0 +- Nper and PointPer left at their motor defaults (Nper=1/6, PointPer=101) +- r_stator_end=0.7, offset=0.35 + +Result at capture time: T_mean=4.2946 Nm, n_pts=18. diff --git a/tests/golden/motor1_baseline/tor1.csv b/tests/golden/motor1_baseline/tor1.csv new file mode 100644 index 0000000..c9ee096 --- /dev/null +++ b/tests/golden/motor1_baseline/tor1.csv @@ -0,0 +1,19 @@ +torque +4.113935267348939995e+00 +5.355352554939270426e+00 +5.292647321340860245e+00 +5.119598115206679623e+00 +3.229005486635140088e+00 +2.822246893115389899e+00 +5.054925543141499666e+00 +5.355955967976529841e+00 +5.269456593120070309e+00 +4.036096645443270070e+00 +2.171481428648470047e+00 +4.086431515543630155e+00 +5.422105281551719891e+00 +5.284750826048020045e+00 +5.073622704777569936e+00 +2.920958029298220016e+00 +2.650604073421409801e+00 +4.043900427932560326e+00 diff --git a/tests/golden/motor2_baseline/readme.md b/tests/golden/motor2_baseline/readme.md index 20a6903..ba1cdcd 100644 --- a/tests/golden/motor2_baseline/readme.md +++ b/tests/golden/motor2_baseline/readme.md @@ -1,18 +1,12 @@ -Golden baseline for motor2 (now `motors/synrm_5f_40s.py`), for issue #20. +Golden baseline for motor2 (`motors/synrm_5f_40s.py`), for issue #20. -`tor.csv` is the raw torque waveform (101 points) from Ansys 2025.1 solve of -`machine_design/design2.py::Design2` at commit `c438e18` (dropped the unsupported -deriv() voltage terms), the earliest version confirmed to be free of known bugs -(Rstat, set_derived_params, deriv()) at the time of writing. - -Reproduce with (checked out at commit c438e18, with Ansys 2025.1 installed): - - python notebooks/run2.py --nper 1 --aedt-version 2025.1 +`tor2.csv` is the raw torque waveform from an Ansys 2025.1 solve of +`motors/synrm_5f_40s.py::Computation` on main. Setup used: - generator: HacklGenerator_OneLambda, seed=0 - current setpoint: Id1=7.0711, Iq1=7.0711, Id3=0.0, Iq3=0.0 -- Nper=1 (full electrical period), PointPer=101 +- Nper and PointPer left at their motor defaults (Nper=1/10, PointPer=101) - r_stator_end=0.7, offset=0.35 -Result at capture time: T_mean=35.6431 Nm, ripple=11.374 %, n_pts=101. +Result at capture time: T_mean=35.1976 Nm, n_pts=11. diff --git a/tests/golden/motor2_baseline/tor.csv b/tests/golden/motor2_baseline/tor.csv deleted file mode 100644 index e3a3585..0000000 --- a/tests/golden/motor2_baseline/tor.csv +++ /dev/null @@ -1,102 +0,0 @@ -torque -3.043239716233519943e+01 -3.591620613263980033e+01 -4.147767354234220250e+01 -3.574584679986870128e+01 -3.222814259667190129e+01 -3.095928780826810112e+01 -3.786632943847219934e+01 -4.303809933097770113e+01 -3.662076900515599931e+01 -3.281725230483630185e+01 -3.007174401871689895e+01 -3.573888228731269834e+01 -4.120300585270319971e+01 -3.599792664357870109e+01 -3.253304600890449905e+01 -3.077961012251270034e+01 -3.772157506313229902e+01 -4.269723783739590317e+01 -3.616353533590159941e+01 -3.238096010848430240e+01 -3.022553356865880048e+01 -3.640573091630270142e+01 -4.129683127423879796e+01 -3.559188344074919996e+01 -3.272074816481890025e+01 -3.055465920758080145e+01 -3.830244497279409899e+01 -4.353545394998270268e+01 -3.661697884091280031e+01 -3.366794915233990082e+01 -3.056269742457439875e+01 -3.602187103095860010e+01 -4.166798739286939934e+01 -3.632469333624570140e+01 -3.187625812486179910e+01 -3.046426076181120024e+01 -3.827779223861939784e+01 -4.311334873546699953e+01 -3.612120642732909914e+01 -3.342678637451909651e+01 -3.039746031080100153e+01 -3.584430341074399706e+01 -4.071605965070880018e+01 -3.577802304563969926e+01 -3.185131848203090144e+01 -3.064924982827459843e+01 -3.786522764290769771e+01 -4.288253181038940198e+01 -3.654726462989800240e+01 -3.348056735319730137e+01 -3.037035945776889889e+01 -3.585879004684589688e+01 -4.103728202628079913e+01 -3.619210085481090289e+01 -3.212317336525379829e+01 -2.945254472539020085e+01 -3.733891343842709887e+01 -4.307352752108909755e+01 -3.611056883879589918e+01 -3.353969974209889671e+01 -3.039529832749450122e+01 -3.610660709819040193e+01 -4.109449944947869682e+01 -3.599751397114710016e+01 -3.184092175468779828e+01 -3.051594132788940072e+01 -3.790347759538030203e+01 -4.264059829999010276e+01 -3.542281927254170171e+01 -3.313432271491159753e+01 -3.013188646331990128e+01 -3.591239854493939987e+01 -4.128987331650500181e+01 -3.619365168201039751e+01 -3.190819056692449962e+01 -3.059694193251829830e+01 -3.788616748597309680e+01 -4.303724301345680203e+01 -3.650051295735659806e+01 -3.256492181153360121e+01 -3.029326519453649880e+01 -3.575983958061750201e+01 -4.139503045675009929e+01 -3.610917538349340106e+01 -3.266359520960779861e+01 -3.072515190675330032e+01 -3.785815632864949976e+01 -4.321610873451739820e+01 -3.581399530789410335e+01 -3.335051847721790352e+01 -3.022432928828660081e+01 -3.591319643000360173e+01 -4.072146448594769907e+01 -3.593955821330690270e+01 -3.188008857613809965e+01 -3.064438596863509900e+01 -3.789969953786410173e+01 -4.293203942225679981e+01 -3.623742697472250285e+01 -3.338758074574499801e+01 -3.031403113303969832e+01 diff --git a/tests/golden/motor2_baseline/tor2.csv b/tests/golden/motor2_baseline/tor2.csv new file mode 100644 index 0000000..910486e --- /dev/null +++ b/tests/golden/motor2_baseline/tor2.csv @@ -0,0 +1,12 @@ +torque +3.043239716744589884e+01 +3.591620613144719698e+01 +4.147767354018399999e+01 +3.574584668206470184e+01 +3.222814261073609998e+01 +3.095928204470029854e+01 +3.786632959133380183e+01 +4.303810830963350043e+01 +3.662076975749660335e+01 +3.281725130168840110e+01 +3.007173280414900063e+01 diff --git a/tests/test_motor1_golden.py b/tests/test_motor1_golden.py new file mode 100644 index 0000000..7621ffc --- /dev/null +++ b/tests/test_motor1_golden.py @@ -0,0 +1,58 @@ +import os + +import numpy as np +import pytest + +from machine_design.config import load_config +from machine_design.designs import load_design +from machine_design.optimization.generators import HacklGenerator_OneLambda +from motors.synrm_3f_36s import Computation, Geometry + +pytestmark = pytest.mark.ansys + +aedt_version = load_config()["aedt_version"] +r_stator_end = 0.7 +offset = 0.35 +seed = 0 +num_cores = 4 + +golden_tor = np.loadtxt(os.path.join(os.path.dirname(__file__), "golden", "motor1_baseline", "tor1.csv"), delimiter=",", skiprows=1) + + +def test_torque_matches_golden_baseline(tmp_path): + geometry = Geometry() + computation = Computation(geometry) + design = load_design( + str(tmp_path / "motor1_golden_check.aedt"), + "Motor1GoldenCheck", + "Design01", + aedt_version, + geometry, + computation, + non_graphical=True, + new_desktop=True, + close_on_exit=False, + ) + try: + np.random.seed(seed) + generator = HacklGenerator_OneLambda(design, r_stator_end, offset=offset) + while True: + params = generator.random_parameters() + generator.set_parameters(params) + barriers = generator.generate_barriers() + barriers = generator.split_barriers(barriers) + if generator.feasible_barriers(barriers): + break + + design.add_rotor() + for barrier in barriers: + design.add_rotor_barrier(barrier) + + result = design.compute(NUM_CORES=num_cores) + design.delete_rotor() + + assert result is not None + Tor = np.asarray(result, dtype=float) + np.testing.assert_allclose(Tor, golden_tor, rtol=0.01, err_msg="torque waveform drifted from the golden baseline") + finally: + design.close_project() diff --git a/tests/test_motor2_golden.py b/tests/test_motor2_golden.py index ea3b2f1..30cc247 100644 --- a/tests/test_motor2_golden.py +++ b/tests/test_motor2_golden.py @@ -17,7 +17,7 @@ current_setpoint = (7.0711, 7.0711, 0.0, 0.0) num_cores = 4 -golden_tor = np.loadtxt(os.path.join(os.path.dirname(__file__), "golden", "motor2_baseline", "tor.csv"), delimiter=",", skiprows=1) +golden_tor = np.loadtxt(os.path.join(os.path.dirname(__file__), "golden", "motor2_baseline", "tor2.csv"), delimiter=",", skiprows=1) def test_torque_matches_golden_baseline(tmp_path): @@ -35,8 +35,6 @@ def test_torque_matches_golden_baseline(tmp_path): close_on_exit=False, ) try: - design.m2d["Nper"] = "1" - np.random.seed(seed) generator = HacklGenerator_OneLambda(design, r_stator_end, offset=offset) while True: @@ -56,6 +54,6 @@ def test_torque_matches_golden_baseline(tmp_path): assert result is not None Tor = np.asarray(result["Moving1.Torque"], dtype=float) - np.testing.assert_allclose(Tor, golden_tor, rtol=0.10, err_msg="torque waveform drifted from the golden baseline") + np.testing.assert_allclose(Tor, golden_tor, rtol=0.03, err_msg="torque waveform drifted from the golden baseline") finally: design.close_project() From 2768f5a1de4a011809a7dfc1afdfa81c14ab2412 Mon Sep 17 00:00:00 2001 From: haitao-ZCU Date: Mon, 14 Sep 2026 15:01:33 +0200 Subject: [PATCH 4/7] tests for motor2 with ansys --- tests/legacy/__init__.py | 0 tests/legacy/design2.py | 331 ++++++++++++++++++++ tests/test_ansys_regression_synrm_5f_40s.py | 108 +++++++ 3 files changed, 439 insertions(+) create mode 100644 tests/legacy/__init__.py create mode 100644 tests/legacy/design2.py create mode 100644 tests/test_ansys_regression_synrm_5f_40s.py diff --git a/tests/legacy/__init__.py b/tests/legacy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/legacy/design2.py b/tests/legacy/design2.py new file mode 100644 index 0000000..96c3e85 --- /dev/null +++ b/tests/legacy/design2.py @@ -0,0 +1,331 @@ +import numpy as np + +from .design import Design + + +class Design2(Design): + def set_geom_params(self): + super().set_geom_params() + self.geom_params["SlotNumber"] = "40" + + def set_slot_params(self): + super().set_slot_params() + self.slot_params["Bs1"] = "3.0mm" + self.slot_params["Bs2"] = "4.3mm" + self.slot_params["SetAngle"] = "9deg" + + def set_winds_params(self): + super().set_winds_params() + self.wind_params["Nc"] = "113" + + def set_oper_params(self): + fr = 50 # [Hz] + RotSpeed = 60 * fr / self.PolePairs # [rpm] + self.oper_params = { + "Id1": "0.0A", + "Iq1": "0.0A", + "Id3": "0.0A", + "Iq3": "0.0A", + "epsI1": "atan2(Iq1,Id1)", #current angle, 1st harmonic + "epsI3": "atan2(Iq3,Id3)", #current angle, 1st harmonic + "Im1": "sqrt(Id1^2+Iq1^2)", + "Im3": "sqrt(Id3^2+Iq3^2)", + "InitPos": "-45deg", + "fr": f"{fr}Hz", + "f": "fr", # replace f in design.py with fr (line 355-356 in design.py) + "RotSpeed": f"{RotSpeed}rpm", + "Nper": "1/10", # number of included periods + "PointPer": "101", # number of time points per period + } + + def set_derived_params(self): + super().set_derived_params() + # pass + + def set_solution_expressions(self): + self.solution_expressions = [ + "V_d1", "V_q1", "V_d3", "V_q3", + + "Flux_e_d1", "Flux_e_q1", "Flux_e_d3", "Flux_e_q3", + + "I_d1", "I_q1", "I_d3", "I_q3", + + "Ld1", "Ld1q1", "Ld1d3", "Ld1q3", + "Lq1", "Lq1d3", "Lq1q3", + "Ld3", "Ld3q3", + "Lq3", + + "Moving1.Torque", + ] + + def set_output_vars(self): + self.output_vars = { + 'PolePairs': "2", + 'RotSign': "1", + 'Rstat': "19", + 'Lew': "0", + 'theta_el': "RotSign*(Moving1.Position - InitPos) * PolePairs - pi", + 'cos0_1': "cos(1*(theta_el - 2*PI*0/5))", + 'sin0_1': "sin(-1*(theta_el - 2*PI*0/5))", + 'cos1_1': "cos(1*(theta_el - 2*PI*1/5))", + 'sin1_1': "sin(-1*(theta_el - 2*PI*1/5))", + 'cos2_1': "cos(1*(theta_el - 2*PI*2/5))", + 'sin2_1': "sin(-1*(theta_el - 2*PI*2/5))", + 'cos3_1': "cos(1*(theta_el - 2*PI*3/5))", + 'sin3_1': "sin(-1*(theta_el - 2*PI*3/5))", + 'cos4_1': "cos(1*(theta_el - 2*PI*4/5))", + 'sin4_1': "sin(-1*(theta_el - 2*PI*4/5))", + 'cos0_3': "cos(3*(theta_el - 2*PI*0/5))", + 'sin0_3': "sin(-3*(theta_el - 2*PI*0/5))", + 'cos1_3': "cos(3*(theta_el - 2*PI*1/5))", + 'sin1_3': "sin(-3*(theta_el - 2*PI*1/5))", + 'cos2_3': "cos(3*(theta_el - 2*PI*2/5))", + 'sin2_3': "sin(-3*(theta_el - 2*PI*2/5))", + 'cos3_3': "cos(3*(theta_el - 2*PI*3/5))", + 'sin3_3': "sin(-3*(theta_el - 2*PI*3/5))", + 'cos4_3': "cos(3*(theta_el - 2*PI*4/5))", + 'sin4_3': "sin(-3*(theta_el - 2*PI*4/5))", + 'Flux_d1': "(FluxLinkage(PhaseA)*cos0_1 + FluxLinkage(PhaseB)*cos1_1 + FluxLinkage(PhaseC)*cos2_1 + FluxLinkage(PhaseD)*cos3_1 + FluxLinkage(PhaseE)*cos4_1) * 2/5", + 'Flux_q1': "(FluxLinkage(PhaseA)*sin0_1 + FluxLinkage(PhaseB)*sin1_1 + FluxLinkage(PhaseC)*sin2_1 + FluxLinkage(PhaseD)*sin3_1 + FluxLinkage(PhaseE)*sin4_1) * 2/5", + 'Flux_d3': "(FluxLinkage(PhaseA)*cos0_3 + FluxLinkage(PhaseB)*cos1_3 + FluxLinkage(PhaseC)*cos2_3 + FluxLinkage(PhaseD)*cos3_3 + FluxLinkage(PhaseE)*cos4_3) * 2/5", + 'Flux_q3': "(FluxLinkage(PhaseA)*sin0_3 + FluxLinkage(PhaseB)*sin1_3 + FluxLinkage(PhaseC)*sin2_3 + FluxLinkage(PhaseD)*sin3_3 + FluxLinkage(PhaseE)*sin4_3) * 2/5", + 'Vind_d1': "(InducedVoltage(PhaseA)*cos0_1 + InducedVoltage(PhaseB)*cos1_1 + InducedVoltage(PhaseC)*cos2_1 + InducedVoltage(PhaseD)*cos3_1 + InducedVoltage(PhaseE)*cos4_1) * 2/5", + 'Vind_q1': "(InducedVoltage(PhaseA)*sin0_1 + InducedVoltage(PhaseB)*sin1_1 + InducedVoltage(PhaseC)*sin2_1 + InducedVoltage(PhaseD)*sin3_1 + InducedVoltage(PhaseE)*sin4_1) * 2/5", + 'Vind_d3': "(InducedVoltage(PhaseA)*cos0_3 + InducedVoltage(PhaseB)*cos1_3 + InducedVoltage(PhaseC)*cos2_3 + InducedVoltage(PhaseD)*cos3_3 + InducedVoltage(PhaseE)*cos4_3) * 2/5", + 'Vind_q3': "(InducedVoltage(PhaseA)*sin0_3 + InducedVoltage(PhaseB)*sin1_3 + InducedVoltage(PhaseC)*sin2_3 + InducedVoltage(PhaseD)*sin3_3 + InducedVoltage(PhaseE)*sin4_3) * 2/5", + 'dIA_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time)+epsI3-pi)", + 'dIB_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time-72deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-72deg)+epsI3-pi)", + 'dIC_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time-144deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-144deg)+epsI3-pi)", + 'dID_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time-216deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-216deg)+epsI3-pi)", + 'dIE_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time-288deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-288deg)+epsI3-pi)", + 'V_A': "InducedVoltage(PhaseA) + Rstat*InputCurrent(PhaseA) + Lew*dIA_dt", + 'V_B': "InducedVoltage(PhaseB) + Rstat*InputCurrent(PhaseB) + Lew*dIB_dt", + 'V_C': "InducedVoltage(PhaseC) + Rstat*InputCurrent(PhaseC) + Lew*dIC_dt", + 'V_D': "InducedVoltage(PhaseD) + Rstat*InputCurrent(PhaseD) + Lew*dID_dt", + 'V_E': "InducedVoltage(PhaseE) + Rstat*InputCurrent(PhaseE) + Lew*dIE_dt", + 'V_AC': "V_A - V_C", + 'V_BD': "V_B - V_D", + 'V_CE': "V_C - V_E", + 'V_DA': "V_D - V_A", + 'V_EB': "V_E - V_B", + 'Vterm_A': "1/5*(2*V_AC + -1*V_BD + 1*V_CE + -2*V_DA)", + 'Vterm_B': "1/5*(2*V_AC + 4*V_BD + 1*V_CE + 3*V_DA)", + 'Vterm_C': "1/5*(-3*V_AC + -1*V_BD + 1*V_CE + -2*V_DA)", + 'Vterm_D': "1/5*(2*V_AC + -1*V_BD + 1*V_CE + 3*V_DA)", + 'Vterm_E': "1/5*(-3*V_AC + -1*V_BD + -4*V_CE + -2*V_DA)", + 'I_d1': "(InputCurrent(PhaseA)*cos0_1 + InputCurrent(PhaseB)*cos1_1 + InputCurrent(PhaseC)*cos2_1 + InputCurrent(PhaseD)*cos3_1 + InputCurrent(PhaseE)*cos4_1) * 2/5", + 'I_q1': "(InputCurrent(PhaseA)*sin0_1 + InputCurrent(PhaseB)*sin1_1 + InputCurrent(PhaseC)*sin2_1 + InputCurrent(PhaseD)*sin3_1 + InputCurrent(PhaseE)*sin4_1) * 2/5", + 'I_d3': "(InputCurrent(PhaseA)*cos0_3 + InputCurrent(PhaseB)*cos1_3 + InputCurrent(PhaseC)*cos2_3 + InputCurrent(PhaseD)*cos3_3 + InputCurrent(PhaseE)*cos4_3) * 2/5", + 'I_q3': "(InputCurrent(PhaseA)*sin0_3 + InputCurrent(PhaseB)*sin1_3 + InputCurrent(PhaseC)*sin2_3 + InputCurrent(PhaseD)*sin3_3 + InputCurrent(PhaseE)*sin4_3) * 2/5", + 'V_d1': "(V_A*cos0_1 + V_B*cos1_1 + V_C*cos2_1 + V_D*cos3_1 + V_E*cos4_1) * 2/5", + 'V_q1': "(V_A*sin0_1 + V_B*sin1_1 + V_C*sin2_1 + V_D*sin3_1 + V_E*sin4_1) * 2/5", + 'V_d3': "(V_A*cos0_3 + V_B*cos1_3 + V_C*cos2_3 + V_D*cos3_3 + V_E*cos4_3) * 2/5", + 'V_q3': "(V_A*sin0_3 + V_B*sin1_3 + V_C*sin2_3 + V_D*sin3_3 + V_E*sin4_3) * 2/5", + 'L0d_1': "L(PhaseA,PhaseA)*cos0_1 + L(PhaseA,PhaseB)*cos1_1 + L(PhaseA,PhaseC)*cos2_1 + L(PhaseA,PhaseD)*cos3_1 + L(PhaseA,PhaseE)*cos4_1", + 'L0q_1': "L(PhaseA,PhaseA)*sin0_1 + L(PhaseA,PhaseB)*sin1_1 + L(PhaseA,PhaseC)*sin2_1 + L(PhaseA,PhaseD)*sin3_1 + L(PhaseA,PhaseE)*sin4_1", + 'L1d_1': "L(PhaseB,PhaseA)*cos0_1 + L(PhaseB,PhaseB)*cos1_1 + L(PhaseB,PhaseC)*cos2_1 + L(PhaseB,PhaseD)*cos3_1 + L(PhaseB,PhaseE)*cos4_1", + 'L1q_1': "L(PhaseB,PhaseA)*sin0_1 + L(PhaseB,PhaseB)*sin1_1 + L(PhaseB,PhaseC)*sin2_1 + L(PhaseB,PhaseD)*sin3_1 + L(PhaseB,PhaseE)*sin4_1", + 'L2d_1': "L(PhaseC,PhaseA)*cos0_1 + L(PhaseC,PhaseB)*cos1_1 + L(PhaseC,PhaseC)*cos2_1 + L(PhaseC,PhaseD)*cos3_1 + L(PhaseC,PhaseE)*cos4_1", + 'L2q_1': "L(PhaseC,PhaseA)*sin0_1 + L(PhaseC,PhaseB)*sin1_1 + L(PhaseC,PhaseC)*sin2_1 + L(PhaseC,PhaseD)*sin3_1 + L(PhaseC,PhaseE)*sin4_1", + 'L3d_1': "L(PhaseD,PhaseA)*cos0_1 + L(PhaseD,PhaseB)*cos1_1 + L(PhaseD,PhaseC)*cos2_1 + L(PhaseD,PhaseD)*cos3_1 + L(PhaseD,PhaseE)*cos4_1", + 'L3q_1': "L(PhaseD,PhaseA)*sin0_1 + L(PhaseD,PhaseB)*sin1_1 + L(PhaseD,PhaseC)*sin2_1 + L(PhaseD,PhaseD)*sin3_1 + L(PhaseD,PhaseE)*sin4_1", + 'L4d_1': "L(PhaseE,PhaseA)*cos0_1 + L(PhaseE,PhaseB)*cos1_1 + L(PhaseE,PhaseC)*cos2_1 + L(PhaseE,PhaseD)*cos3_1 + L(PhaseE,PhaseE)*cos4_1", + 'L4q_1': "L(PhaseE,PhaseA)*sin0_1 + L(PhaseE,PhaseB)*sin1_1 + L(PhaseE,PhaseC)*sin2_1 + L(PhaseE,PhaseD)*sin3_1 + L(PhaseE,PhaseE)*sin4_1", + 'L0d_3': "L(PhaseA,PhaseA)*cos0_3 + L(PhaseA,PhaseB)*cos1_3 + L(PhaseA,PhaseC)*cos2_3 + L(PhaseA,PhaseD)*cos3_3 + L(PhaseA,PhaseE)*cos4_3", + 'L0q_3': "L(PhaseA,PhaseA)*sin0_3 + L(PhaseA,PhaseB)*sin1_3 + L(PhaseA,PhaseC)*sin2_3 + L(PhaseA,PhaseD)*sin3_3 + L(PhaseA,PhaseE)*sin4_3", + 'L1d_3': "L(PhaseB,PhaseA)*cos0_3 + L(PhaseB,PhaseB)*cos1_3 + L(PhaseB,PhaseC)*cos2_3 + L(PhaseB,PhaseD)*cos3_3 + L(PhaseB,PhaseE)*cos4_3", + 'L1q_3': "L(PhaseB,PhaseA)*sin0_3 + L(PhaseB,PhaseB)*sin1_3 + L(PhaseB,PhaseC)*sin2_3 + L(PhaseB,PhaseD)*sin3_3 + L(PhaseB,PhaseE)*sin4_3", + 'L2d_3': "L(PhaseC,PhaseA)*cos0_3 + L(PhaseC,PhaseB)*cos1_3 + L(PhaseC,PhaseC)*cos2_3 + L(PhaseC,PhaseD)*cos3_3 + L(PhaseC,PhaseE)*cos4_3", + 'L2q_3': "L(PhaseC,PhaseA)*sin0_3 + L(PhaseC,PhaseB)*sin1_3 + L(PhaseC,PhaseC)*sin2_3 + L(PhaseC,PhaseD)*sin3_3 + L(PhaseC,PhaseE)*sin4_3", + 'L3d_3': "L(PhaseD,PhaseA)*cos0_3 + L(PhaseD,PhaseB)*cos1_3 + L(PhaseD,PhaseC)*cos2_3 + L(PhaseD,PhaseD)*cos3_3 + L(PhaseD,PhaseE)*cos4_3", + 'L3q_3': "L(PhaseD,PhaseA)*sin0_3 + L(PhaseD,PhaseB)*sin1_3 + L(PhaseD,PhaseC)*sin2_3 + L(PhaseD,PhaseD)*sin3_3 + L(PhaseD,PhaseE)*sin4_3", + 'L4d_3': "L(PhaseE,PhaseA)*cos0_3 + L(PhaseE,PhaseB)*cos1_3 + L(PhaseE,PhaseC)*cos2_3 + L(PhaseE,PhaseD)*cos3_3 + L(PhaseE,PhaseE)*cos4_3", + 'L4q_3': "L(PhaseE,PhaseA)*sin0_3 + L(PhaseE,PhaseB)*sin1_3 + L(PhaseE,PhaseC)*sin2_3 + L(PhaseE,PhaseD)*sin3_3 + L(PhaseE,PhaseE)*sin4_3", + 'Ld1': "(L0d_1*cos0_1 + L1d_1*cos1_1 + L2d_1*cos2_1 + L3d_1*cos3_1 + L4d_1*cos4_1) * 2/5", + 'Ld1q1': "(L0d_1*sin0_1 + L1d_1*sin1_1 + L2d_1*sin2_1 + L3d_1*sin3_1 + L4d_1*sin4_1) * 2/5", + 'Lq1d1': "(L0q_1*cos0_1 + L1q_1*cos1_1 + L2q_1*cos2_1 + L3q_1*cos3_1 + L4q_1*cos4_1) * 2/5", + 'Lq1': "(L0q_1*sin0_1 + L1q_1*sin1_1 + L2q_1*sin2_1 + L3q_1*sin3_1 + L4q_1*sin4_1) * 2/5", + 'Ld1d3': "(L0d_1*cos0_3 + L1d_1*cos1_3 + L2d_1*cos2_3 + L3d_1*cos3_3 + L4d_1*cos4_3) * 2/5", + 'Ld1q3': "(L0d_1*sin0_3 + L1d_1*sin1_3 + L2d_1*sin2_3 + L3d_1*sin3_3 + L4d_1*sin4_3) * 2/5", + 'Lq1d3': "(L0q_1*cos0_3 + L1q_1*cos1_3 + L2q_1*cos2_3 + L3q_1*cos3_3 + L4q_1*cos4_3) * 2/5", + 'Lq1q3': "(L0q_1*sin0_3 + L1q_1*sin1_3 + L2q_1*sin2_3 + L3q_1*sin3_3 + L4q_1*sin4_3) * 2/5", + 'Ld3d1': "(L0d_3*cos0_1 + L1d_3*cos1_1 + L2d_3*cos2_1 + L3d_3*cos3_1 + L4d_3*cos4_1) * 2/5", + 'Ld3q1': "(L0d_3*sin0_1 + L1d_3*sin1_1 + L2d_3*sin2_1 + L3d_3*sin3_1 + L4d_3*sin4_1) * 2/5", + 'Lq3d1': "(L0q_3*cos0_1 + L1q_3*cos1_1 + L2q_3*cos2_1 + L3q_3*cos3_1 + L4q_3*cos4_1) * 2/5", + 'Lq3q1': "(L0q_3*sin0_1 + L1q_3*sin1_1 + L2q_3*sin2_1 + L3q_3*sin3_1 + L4q_3*sin4_1) * 2/5", + 'Ld3': "(L0d_3*cos0_3 + L1d_3*cos1_3 + L2d_3*cos2_3 + L3d_3*cos3_3 + L4d_3*cos4_3) * 2/5", + 'Ld3q3': "(L0d_3*sin0_3 + L1d_3*sin1_3 + L2d_3*sin2_3 + L3d_3*sin3_3 + L4d_3*sin4_3) * 2/5", + 'Lq3d3': "(L0q_3*cos0_3 + L1q_3*cos1_3 + L2q_3*cos2_3 + L3q_3*cos3_3 + L4q_3*cos4_3) * 2/5", + 'Lq3': "(L0q_3*sin0_3 + L1q_3*sin1_3 + L2q_3*sin2_3 + L3q_3*sin3_3 + L4q_3*sin4_3) * 2/5", + 'Flux_e_d1': "Flux_d1 - (Ld1*I_d1 + Ld1q1*I_q1 + Ld1d3*I_d3 + Ld1q3*I_q3)", + 'Flux_e_q1': "Flux_q1 - (Lq1d1*I_d1 + Lq1*I_q1 + Lq1d3*I_d3 + Lq1q3*I_q3)", + '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*PolePairs*(1*(Flux_d1*I_q1 - Flux_q1*I_d1) + 3*(Flux_d3*I_q3 - Flux_q3*I_d3))", + } + + def set_post_params(self): + self.post_params = { # reports + ("InducedVoltage(PhaseA)","InducedVoltage(PhaseB)","InducedVoltage(PhaseC)","InducedVoltage(PhaseD)","InducedVoltage(PhaseE)"): "InducedVoltage", + ("Moving1.Torque"): "Torque", + ("InputCurrent(PhaseA)","InputCurrent(PhaseB)","InputCurrent(PhaseC)","InputCurrent(PhaseD)","InputCurrent(PhaseE)"): "Current", + ("FluxLinkage(PhaseA)","FluxLinkage(PhaseB)","FluxLinkage(PhaseC)","FluxLinkage(PhaseD)","FluxLinkage(PhaseE)"): "FluxLinkage", + ("I_d1", "I_q1", "I_d3", "I_q3"): "Current_dq", + ("Flux_d1", "Flux_q1", "Flux_d3", "Flux_q3"): "FluxLinkage_dq", + ("Flux_e_d1", "Flux_e_q1", "Flux_e_d3", "Flux_e_q3"): "FluxLinkage excitation_dq", + ("Vind_d1", "Vind_q1", "Vind_d3", "Vind_q3"): "InducedVoltage_dq", + ("V_d1", "V_q1", "V_d3", "V_q3"): "TerminalVoltage_dq", + ("Ld1", "Lq1", "Ld3", "Lq3"): "Inductance_dq main", + ("Ld1q1", "Ld1d3", "Ld1q3", "Lq1d3", "Lq1q3", "Ld3q3"): "Inductance_dq cross-coupling", + } + + def assign_stator_coils(self): + m2d = self.m2d + + # Excitations + I_A = "Im1*cos(2*pi*fr*time+epsI1-pi) + Im3*cos(3*(2*pi*fr*time)+epsI3-pi)" + I_B = "Im1*cos(2*pi*fr*time-72deg+epsI1-pi) + Im3*cos(3*(2*pi*fr*time-72deg)+epsI3-pi)" + I_C = "Im1*cos(2*pi*fr*time-144deg+epsI1-pi) + Im3*cos(3*(2*pi*fr*time-144deg)+epsI3-pi)" + I_D = "Im1*cos(2*pi*fr*time-216deg+epsI1-pi) + Im3*cos(3*(2*pi*fr*time-216deg)+epsI3-pi)" + I_E = "Im1*cos(2*pi*fr*time-288deg+epsI1-pi) + Im3*cos(3*(2*pi*fr*time-288deg)+epsI3-pi)" + m2d.assign_coil + #Define phase windings + m2d.assign_coil( + assignment=["Coil"], + conductors_number="Nc", + polarity="Positive", + name="CS1", + ) + m2d.assign_coil( + assignment=["Coil_1"], + conductors_number="Nc", + polarity="Negative", + name="CS2", + ) + m2d.assign_coil( + assignment=["Coil_2"], + conductors_number="Nc", + polarity="Negative", + name="CS3", + ) + m2d.assign_coil( + assignment=["Coil_3"], + conductors_number="Nc", + polarity="Positive", + name="CS4", + ) + m2d.assign_coil( + assignment=["Coil_4"], + conductors_number="Nc", + polarity="Positive", + name="CS5", + ) + m2d.assign_coil( + assignment=["Coil_5"], + conductors_number="Nc", + polarity="Negative", + name="CS6", + ) + m2d.assign_coil( + assignment=["Coil_6"], + conductors_number="Nc", + polarity="Negative", + name="CS7", + ) + m2d.assign_coil( + assignment=["Coil_7"], + conductors_number="Nc", + polarity="Positive", + name="CS8", + ) + m2d.assign_coil( + assignment=["Coil_8"], + conductors_number="Nc", + polarity="Positive", + name="CS9", + ) + m2d.assign_coil( + assignment=["Coil_9"], + conductors_number="Nc", + polarity="Negative", + name="CS10", + ) + + m2d.assign_winding( + assignment=None, + winding_type="Current", + is_solid=False, + current=I_A, + parallel_branches="ParallelPaths", + name="PhaseA", + ) + m2d.assign_winding( + assignment=None, + winding_type="Current", + is_solid=False, + current=I_B, + parallel_branches="ParallelPaths", + name="PhaseB", + ) + m2d.assign_winding( + assignment=None, + winding_type="Current", + is_solid=False, + current=I_C, + parallel_branches="ParallelPaths", + name="PhaseC", + ) + m2d.assign_winding( + assignment=None, + winding_type="Current", + is_solid=False, + current=I_D, + parallel_branches="ParallelPaths", + name="PhaseD", + ) + m2d.assign_winding( + assignment=None, + winding_type="Current", + is_solid=False, + current=I_E, + parallel_branches="ParallelPaths", + name="PhaseE", + ) + + m2d.add_winding_coils( + assignment="PhaseA", coils=["CS1", "CS10"] + ) + m2d.add_winding_coils( + assignment="PhaseB", coils=["CS4", "CS5"] + ) + m2d.add_winding_coils( + assignment="PhaseC", coils=["CS8", "CS9"] + ) + m2d.add_winding_coils( + assignment="PhaseD", coils=["CS2", "CS3"] + ) + m2d.add_winding_coils( + assignment="PhaseE", coils=["CS6", "CS7"] + ) + + def inductance_computation(self): + self.m2d.change_inductance_computation(compute_transient_inductance=True, incremental_matrix=True) + + def set_variables(self, Id1, Iq1, Id3, Iq3): + self.m2d.variable_manager["Id1"] = f"{Id1}A" + self.m2d.variable_manager["Iq1"] = f"{Iq1}A" + self.m2d.variable_manager["Id3"] = f"{Id3}A" + self.m2d.variable_manager["Iq3"] = f"{Iq3}A" + + def extract_results(self, solutions): + out = {} + for expr in self.solution_expressions: + val = solutions.data_real(expr) + + if expr.startswith("Ld") or expr.startswith("Lq"): + val = np.array(val) / 1e9 + elif expr.startswith("I_"): + val = np.array(val) / 1e3 + + out[expr] = val + return out diff --git a/tests/test_ansys_regression_synrm_5f_40s.py b/tests/test_ansys_regression_synrm_5f_40s.py new file mode 100644 index 0000000..e89eefb --- /dev/null +++ b/tests/test_ansys_regression_synrm_5f_40s.py @@ -0,0 +1,108 @@ +"""Golden-master check: tests/legacy/design2.py vs. the live machine_design/design.py. + +Generates two feasible rotor designs (different seeds) with HacklGenerator_OneLambda and +runs each through add_rotor -> compute -> delete_rotor on the SAME Design instance, both +with the frozen reference implementation and the current one - mirroring how an +optimization loop reuses one Design across many candidate rotors. Asserts the resulting +torque waveforms match at each step. Run explicitly with: + + pytest -m ansys tests/test_ansys_regression_synrm_5f_40s.py + +Requires a running Ansys Electronics Desktop session and a free license seat. +""" + +import numpy as np +import pytest + +from legacy.design2 import Design2 +from machine_design.config import load_config +from machine_design.designs.design import Design as LiveDesign +from machine_design.optimization.generators import HacklGenerator_OneLambda +from motors.synrm_5f_40s import Computation, Geometry + +pytestmark = pytest.mark.ansys + +AEDT_VERSION = load_config()["aedt_version"] +R_STATOR_END = 0.7 +OFFSET = 0.35 +SEEDS = (42, 43) +NUM_CORES = 1 +current_setpoint = (7.0711, 7.0711, 0.0, 0.0) + + +def _generate_one_lambda_barriers(seed): + # rotor_r_min/rotor_r_max are pure-Python derived params, so a throwaway + # instance (no Ansys) is enough to size the generator. + geometry = Geometry() + dummy = LiveDesign(m2d=None, geometry=geometry, computation=Computation(geometry)) + generator = HacklGenerator_OneLambda(dummy, R_STATOR_END, offset=OFFSET) + + np.random.seed(seed) + while True: + params = generator.random_parameters() + generator.set_parameters(params) + barriers = generator.generate_barriers() + barriers = generator.split_barriers(barriers) + if generator.feasible_barriers(barriers): + return barriers + + +def _add_rotor(design, barriers): + design.add_rotor() + for barrier in barriers: + design.add_rotor_barrier(barrier) + + +def _create_design(design_cls, project_name, file_name, extra_args=()): + return design_cls.create( + project_name, + "Design01", + file_name, + *extra_args, + version=AEDT_VERSION, + non_graphical=True, + new_desktop=False, + close_on_exit=False, + ) + + +def _compute_torque(design, barriers, add_rotor, setpoint=current_setpoint): + add_rotor(design, barriers) + torque = design.compute(*setpoint, NUM_CORES=NUM_CORES) + design.delete_rotor() + return torque + + +def test_legacy_and_live_design_match_across_rotor_rebuilds(tmp_path): + barrier_sets = [_generate_one_lambda_barriers(seed) for seed in SEEDS] + + legacy_design, live_design = None, None + try: + legacy_design = _create_design(Design2, "RegressionTest_legacy", str(tmp_path / "legacy.aedt")) + + live_geometry = Geometry() + live_computation = Computation(live_geometry) + live_design = _create_design( + LiveDesign, + "RegressionTest_live", + str(tmp_path / "live.aedt"), + extra_args=(live_geometry, live_computation), + ) + + for seed, barriers in zip(SEEDS, barrier_sets): + torque_legacy = _compute_torque(legacy_design, barriers, _add_rotor) + torque_live = _compute_torque(live_design, barriers, _add_rotor) + + assert torque_legacy is not None, f"legacy torque is None for seed {seed}" + assert torque_live is not None, f"live torque is None for seed {seed}" + + for expr in live_computation.solution_expressions: + np.testing.assert_allclose(torque_live[expr], torque_legacy[expr], rtol=1e-3, atol=2e-4, err_msg=f"mismatch for {expr}, seed {seed}") + # rtol is a relative tolerance, but when current_setpoint = (7.0711, 7.0711, 0.0, 0.0) is used, + # I_d3 is 1e-15 and 2.2e-16 for legacy and live, respectively, rtol is not meaningful when comparing such small numbers, + # so atol (absolute tolerance) is used to avoid false failures. + finally: + if live_design is not None: + live_design.close_project() + elif legacy_design is not None: + legacy_design.close_project() From 97be71fcea761de455628ae5d23314fae759faae Mon Sep 17 00:00:00 2001 From: haitao-ZCU Date: Mon, 14 Sep 2026 15:32:37 +0200 Subject: [PATCH 5/7] use old tests structure --- tests/golden/motor1_baseline/readme.md | 11 ----- tests/golden/motor1_baseline/tor1.csv | 19 --------- tests/golden/motor2_baseline/readme.md | 12 ------ tests/golden/motor2_baseline/tor2.csv | 12 ------ tests/test_motor1_golden.py | 58 ------------------------- tests/test_motor2_golden.py | 59 -------------------------- 6 files changed, 171 deletions(-) delete mode 100644 tests/golden/motor1_baseline/readme.md delete mode 100644 tests/golden/motor1_baseline/tor1.csv delete mode 100644 tests/golden/motor2_baseline/readme.md delete mode 100644 tests/golden/motor2_baseline/tor2.csv delete mode 100644 tests/test_motor1_golden.py delete mode 100644 tests/test_motor2_golden.py diff --git a/tests/golden/motor1_baseline/readme.md b/tests/golden/motor1_baseline/readme.md deleted file mode 100644 index 2b96013..0000000 --- a/tests/golden/motor1_baseline/readme.md +++ /dev/null @@ -1,11 +0,0 @@ -Golden baseline for motor1 (`motors/synrm_3f_36s.py`), for issue #20. - -`tor1.csv` is the raw torque waveform from an Ansys 2025.1 solve of -`motors/synrm_3f_36s.py::Computation` on main. - -Setup used: -- generator: HacklGenerator_OneLambda, seed=0 -- Nper and PointPer left at their motor defaults (Nper=1/6, PointPer=101) -- r_stator_end=0.7, offset=0.35 - -Result at capture time: T_mean=4.2946 Nm, n_pts=18. diff --git a/tests/golden/motor1_baseline/tor1.csv b/tests/golden/motor1_baseline/tor1.csv deleted file mode 100644 index c9ee096..0000000 --- a/tests/golden/motor1_baseline/tor1.csv +++ /dev/null @@ -1,19 +0,0 @@ -torque -4.113935267348939995e+00 -5.355352554939270426e+00 -5.292647321340860245e+00 -5.119598115206679623e+00 -3.229005486635140088e+00 -2.822246893115389899e+00 -5.054925543141499666e+00 -5.355955967976529841e+00 -5.269456593120070309e+00 -4.036096645443270070e+00 -2.171481428648470047e+00 -4.086431515543630155e+00 -5.422105281551719891e+00 -5.284750826048020045e+00 -5.073622704777569936e+00 -2.920958029298220016e+00 -2.650604073421409801e+00 -4.043900427932560326e+00 diff --git a/tests/golden/motor2_baseline/readme.md b/tests/golden/motor2_baseline/readme.md deleted file mode 100644 index ba1cdcd..0000000 --- a/tests/golden/motor2_baseline/readme.md +++ /dev/null @@ -1,12 +0,0 @@ -Golden baseline for motor2 (`motors/synrm_5f_40s.py`), for issue #20. - -`tor2.csv` is the raw torque waveform from an Ansys 2025.1 solve of -`motors/synrm_5f_40s.py::Computation` on main. - -Setup used: -- generator: HacklGenerator_OneLambda, seed=0 -- current setpoint: Id1=7.0711, Iq1=7.0711, Id3=0.0, Iq3=0.0 -- Nper and PointPer left at their motor defaults (Nper=1/10, PointPer=101) -- r_stator_end=0.7, offset=0.35 - -Result at capture time: T_mean=35.1976 Nm, n_pts=11. diff --git a/tests/golden/motor2_baseline/tor2.csv b/tests/golden/motor2_baseline/tor2.csv deleted file mode 100644 index 910486e..0000000 --- a/tests/golden/motor2_baseline/tor2.csv +++ /dev/null @@ -1,12 +0,0 @@ -torque -3.043239716744589884e+01 -3.591620613144719698e+01 -4.147767354018399999e+01 -3.574584668206470184e+01 -3.222814261073609998e+01 -3.095928204470029854e+01 -3.786632959133380183e+01 -4.303810830963350043e+01 -3.662076975749660335e+01 -3.281725130168840110e+01 -3.007173280414900063e+01 diff --git a/tests/test_motor1_golden.py b/tests/test_motor1_golden.py deleted file mode 100644 index 7621ffc..0000000 --- a/tests/test_motor1_golden.py +++ /dev/null @@ -1,58 +0,0 @@ -import os - -import numpy as np -import pytest - -from machine_design.config import load_config -from machine_design.designs import load_design -from machine_design.optimization.generators import HacklGenerator_OneLambda -from motors.synrm_3f_36s import Computation, Geometry - -pytestmark = pytest.mark.ansys - -aedt_version = load_config()["aedt_version"] -r_stator_end = 0.7 -offset = 0.35 -seed = 0 -num_cores = 4 - -golden_tor = np.loadtxt(os.path.join(os.path.dirname(__file__), "golden", "motor1_baseline", "tor1.csv"), delimiter=",", skiprows=1) - - -def test_torque_matches_golden_baseline(tmp_path): - geometry = Geometry() - computation = Computation(geometry) - design = load_design( - str(tmp_path / "motor1_golden_check.aedt"), - "Motor1GoldenCheck", - "Design01", - aedt_version, - geometry, - computation, - non_graphical=True, - new_desktop=True, - close_on_exit=False, - ) - try: - np.random.seed(seed) - generator = HacklGenerator_OneLambda(design, r_stator_end, offset=offset) - while True: - params = generator.random_parameters() - generator.set_parameters(params) - barriers = generator.generate_barriers() - barriers = generator.split_barriers(barriers) - if generator.feasible_barriers(barriers): - break - - design.add_rotor() - for barrier in barriers: - design.add_rotor_barrier(barrier) - - result = design.compute(NUM_CORES=num_cores) - design.delete_rotor() - - assert result is not None - Tor = np.asarray(result, dtype=float) - np.testing.assert_allclose(Tor, golden_tor, rtol=0.01, err_msg="torque waveform drifted from the golden baseline") - finally: - design.close_project() diff --git a/tests/test_motor2_golden.py b/tests/test_motor2_golden.py deleted file mode 100644 index 30cc247..0000000 --- a/tests/test_motor2_golden.py +++ /dev/null @@ -1,59 +0,0 @@ -import os - -import numpy as np -import pytest - -from machine_design.config import load_config -from machine_design.designs import load_design -from machine_design.optimization.generators import HacklGenerator_OneLambda -from motors.synrm_5f_40s import Computation, Geometry - -pytestmark = pytest.mark.ansys - -aedt_version = load_config()["aedt_version"] -r_stator_end = 0.7 -offset = 0.35 -seed = 0 -current_setpoint = (7.0711, 7.0711, 0.0, 0.0) -num_cores = 4 - -golden_tor = np.loadtxt(os.path.join(os.path.dirname(__file__), "golden", "motor2_baseline", "tor2.csv"), delimiter=",", skiprows=1) - - -def test_torque_matches_golden_baseline(tmp_path): - geometry = Geometry() - computation = Computation(geometry) - design = load_design( - str(tmp_path / "motor2_golden_check.aedt"), - "Motor2GoldenCheck", - "Design01", - aedt_version, - geometry, - computation, - non_graphical=True, - new_desktop=True, - close_on_exit=False, - ) - try: - np.random.seed(seed) - generator = HacklGenerator_OneLambda(design, r_stator_end, offset=offset) - while True: - params = generator.random_parameters() - generator.set_parameters(params) - barriers = generator.generate_barriers() - barriers = generator.split_barriers(barriers) - if generator.feasible_barriers(barriers): - break - - design.add_rotor() - for barrier in barriers: - design.add_rotor_barrier(barrier) - - result = design.compute(*current_setpoint, NUM_CORES=num_cores) - design.delete_rotor() - - assert result is not None - Tor = np.asarray(result["Moving1.Torque"], dtype=float) - np.testing.assert_allclose(Tor, golden_tor, rtol=0.03, err_msg="torque waveform drifted from the golden baseline") - finally: - design.close_project() From d273bc7c543aba15e6b213b8b02c03220c2e3113 Mon Sep 17 00:00:00 2001 From: haitao-ZCU Date: Mon, 14 Sep 2026 15:41:05 +0200 Subject: [PATCH 6/7] use old test structure --- tests/test_ansys_regression.py | 2 +- tests/test_ansys_regression_synrm_5f_40s.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_ansys_regression.py b/tests/test_ansys_regression.py index 158f597..8c7b89e 100644 --- a/tests/test_ansys_regression.py +++ b/tests/test_ansys_regression.py @@ -1,4 +1,4 @@ -"""Golden-master check: tests/legacy/design.py vs. the live machine_design/design.py. +"""Golden-master check: tests/legacy/design.py vs. the live machine_design/designs/design.py. Generates two feasible rotor designs (different seeds) with HacklGenerator_OneLambda and runs each through add_rotor -> compute -> delete_rotor on the SAME Design instance, both diff --git a/tests/test_ansys_regression_synrm_5f_40s.py b/tests/test_ansys_regression_synrm_5f_40s.py index e89eefb..fd48d7c 100644 --- a/tests/test_ansys_regression_synrm_5f_40s.py +++ b/tests/test_ansys_regression_synrm_5f_40s.py @@ -1,4 +1,4 @@ -"""Golden-master check: tests/legacy/design2.py vs. the live machine_design/design.py. +"""Golden-master check: tests/legacy/design2.py vs. the live machine_design/designs/design.py. Generates two feasible rotor designs (different seeds) with HacklGenerator_OneLambda and runs each through add_rotor -> compute -> delete_rotor on the SAME Design instance, both @@ -13,8 +13,8 @@ import numpy as np import pytest - from legacy.design2 import Design2 + from machine_design.config import load_config from machine_design.designs.design import Design as LiveDesign from machine_design.optimization.generators import HacklGenerator_OneLambda From 1bd22cda8a9a81698b94b4fe7cb0a77a685c8c6d Mon Sep 17 00:00:00 2001 From: haitao-ZCU Date: Mon, 14 Sep 2026 15:46:19 +0200 Subject: [PATCH 7/7] ruff format --- tests/legacy/design2.py | 271 ++++++++++++++++++++-------------------- 1 file changed, 136 insertions(+), 135 deletions(-) diff --git a/tests/legacy/design2.py b/tests/legacy/design2.py index 96c3e85..84b6753 100644 --- a/tests/legacy/design2.py +++ b/tests/legacy/design2.py @@ -26,13 +26,13 @@ def set_oper_params(self): "Iq1": "0.0A", "Id3": "0.0A", "Iq3": "0.0A", - "epsI1": "atan2(Iq1,Id1)", #current angle, 1st harmonic - "epsI3": "atan2(Iq3,Id3)", #current angle, 1st harmonic + "epsI1": "atan2(Iq1,Id1)", # current angle, 1st harmonic + "epsI3": "atan2(Iq3,Id3)", # current angle, 1st harmonic "Im1": "sqrt(Id1^2+Iq1^2)", "Im3": "sqrt(Id3^2+Iq3^2)", "InitPos": "-45deg", "fr": f"{fr}Hz", - "f": "fr", # replace f in design.py with fr (line 355-356 in design.py) + "f": "fr", # replace f in design.py with fr (line 355-356 in design.py) "RotSpeed": f"{RotSpeed}rpm", "Nper": "1/10", # number of included periods "PointPer": "101", # number of time points per period @@ -44,132 +44,143 @@ def set_derived_params(self): def set_solution_expressions(self): self.solution_expressions = [ - "V_d1", "V_q1", "V_d3", "V_q3", - - "Flux_e_d1", "Flux_e_q1", "Flux_e_d3", "Flux_e_q3", - - "I_d1", "I_q1", "I_d3", "I_q3", - - "Ld1", "Ld1q1", "Ld1d3", "Ld1q3", - "Lq1", "Lq1d3", "Lq1q3", - "Ld3", "Ld3q3", + "V_d1", + "V_q1", + "V_d3", + "V_q3", + "Flux_e_d1", + "Flux_e_q1", + "Flux_e_d3", + "Flux_e_q3", + "I_d1", + "I_q1", + "I_d3", + "I_q3", + "Ld1", + "Ld1q1", + "Ld1d3", + "Ld1q3", + "Lq1", + "Lq1d3", + "Lq1q3", + "Ld3", + "Ld3q3", "Lq3", - "Moving1.Torque", ] def set_output_vars(self): self.output_vars = { - 'PolePairs': "2", - 'RotSign': "1", - 'Rstat': "19", - 'Lew': "0", - 'theta_el': "RotSign*(Moving1.Position - InitPos) * PolePairs - pi", - 'cos0_1': "cos(1*(theta_el - 2*PI*0/5))", - 'sin0_1': "sin(-1*(theta_el - 2*PI*0/5))", - 'cos1_1': "cos(1*(theta_el - 2*PI*1/5))", - 'sin1_1': "sin(-1*(theta_el - 2*PI*1/5))", - 'cos2_1': "cos(1*(theta_el - 2*PI*2/5))", - 'sin2_1': "sin(-1*(theta_el - 2*PI*2/5))", - 'cos3_1': "cos(1*(theta_el - 2*PI*3/5))", - 'sin3_1': "sin(-1*(theta_el - 2*PI*3/5))", - 'cos4_1': "cos(1*(theta_el - 2*PI*4/5))", - 'sin4_1': "sin(-1*(theta_el - 2*PI*4/5))", - 'cos0_3': "cos(3*(theta_el - 2*PI*0/5))", - 'sin0_3': "sin(-3*(theta_el - 2*PI*0/5))", - 'cos1_3': "cos(3*(theta_el - 2*PI*1/5))", - 'sin1_3': "sin(-3*(theta_el - 2*PI*1/5))", - 'cos2_3': "cos(3*(theta_el - 2*PI*2/5))", - 'sin2_3': "sin(-3*(theta_el - 2*PI*2/5))", - 'cos3_3': "cos(3*(theta_el - 2*PI*3/5))", - 'sin3_3': "sin(-3*(theta_el - 2*PI*3/5))", - 'cos4_3': "cos(3*(theta_el - 2*PI*4/5))", - 'sin4_3': "sin(-3*(theta_el - 2*PI*4/5))", - 'Flux_d1': "(FluxLinkage(PhaseA)*cos0_1 + FluxLinkage(PhaseB)*cos1_1 + FluxLinkage(PhaseC)*cos2_1 + FluxLinkage(PhaseD)*cos3_1 + FluxLinkage(PhaseE)*cos4_1) * 2/5", - 'Flux_q1': "(FluxLinkage(PhaseA)*sin0_1 + FluxLinkage(PhaseB)*sin1_1 + FluxLinkage(PhaseC)*sin2_1 + FluxLinkage(PhaseD)*sin3_1 + FluxLinkage(PhaseE)*sin4_1) * 2/5", - 'Flux_d3': "(FluxLinkage(PhaseA)*cos0_3 + FluxLinkage(PhaseB)*cos1_3 + FluxLinkage(PhaseC)*cos2_3 + FluxLinkage(PhaseD)*cos3_3 + FluxLinkage(PhaseE)*cos4_3) * 2/5", - 'Flux_q3': "(FluxLinkage(PhaseA)*sin0_3 + FluxLinkage(PhaseB)*sin1_3 + FluxLinkage(PhaseC)*sin2_3 + FluxLinkage(PhaseD)*sin3_3 + FluxLinkage(PhaseE)*sin4_3) * 2/5", - 'Vind_d1': "(InducedVoltage(PhaseA)*cos0_1 + InducedVoltage(PhaseB)*cos1_1 + InducedVoltage(PhaseC)*cos2_1 + InducedVoltage(PhaseD)*cos3_1 + InducedVoltage(PhaseE)*cos4_1) * 2/5", - 'Vind_q1': "(InducedVoltage(PhaseA)*sin0_1 + InducedVoltage(PhaseB)*sin1_1 + InducedVoltage(PhaseC)*sin2_1 + InducedVoltage(PhaseD)*sin3_1 + InducedVoltage(PhaseE)*sin4_1) * 2/5", - 'Vind_d3': "(InducedVoltage(PhaseA)*cos0_3 + InducedVoltage(PhaseB)*cos1_3 + InducedVoltage(PhaseC)*cos2_3 + InducedVoltage(PhaseD)*cos3_3 + InducedVoltage(PhaseE)*cos4_3) * 2/5", - 'Vind_q3': "(InducedVoltage(PhaseA)*sin0_3 + InducedVoltage(PhaseB)*sin1_3 + InducedVoltage(PhaseC)*sin2_3 + InducedVoltage(PhaseD)*sin3_3 + InducedVoltage(PhaseE)*sin4_3) * 2/5", - 'dIA_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time)+epsI3-pi)", - 'dIB_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time-72deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-72deg)+epsI3-pi)", - 'dIC_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time-144deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-144deg)+epsI3-pi)", - 'dID_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time-216deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-216deg)+epsI3-pi)", - 'dIE_dt': "-Im1*2*pi*fr*sin(2*pi*fr*Time-288deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-288deg)+epsI3-pi)", - 'V_A': "InducedVoltage(PhaseA) + Rstat*InputCurrent(PhaseA) + Lew*dIA_dt", - 'V_B': "InducedVoltage(PhaseB) + Rstat*InputCurrent(PhaseB) + Lew*dIB_dt", - 'V_C': "InducedVoltage(PhaseC) + Rstat*InputCurrent(PhaseC) + Lew*dIC_dt", - 'V_D': "InducedVoltage(PhaseD) + Rstat*InputCurrent(PhaseD) + Lew*dID_dt", - 'V_E': "InducedVoltage(PhaseE) + Rstat*InputCurrent(PhaseE) + Lew*dIE_dt", - 'V_AC': "V_A - V_C", - 'V_BD': "V_B - V_D", - 'V_CE': "V_C - V_E", - 'V_DA': "V_D - V_A", - 'V_EB': "V_E - V_B", - 'Vterm_A': "1/5*(2*V_AC + -1*V_BD + 1*V_CE + -2*V_DA)", - 'Vterm_B': "1/5*(2*V_AC + 4*V_BD + 1*V_CE + 3*V_DA)", - 'Vterm_C': "1/5*(-3*V_AC + -1*V_BD + 1*V_CE + -2*V_DA)", - 'Vterm_D': "1/5*(2*V_AC + -1*V_BD + 1*V_CE + 3*V_DA)", - 'Vterm_E': "1/5*(-3*V_AC + -1*V_BD + -4*V_CE + -2*V_DA)", - 'I_d1': "(InputCurrent(PhaseA)*cos0_1 + InputCurrent(PhaseB)*cos1_1 + InputCurrent(PhaseC)*cos2_1 + InputCurrent(PhaseD)*cos3_1 + InputCurrent(PhaseE)*cos4_1) * 2/5", - 'I_q1': "(InputCurrent(PhaseA)*sin0_1 + InputCurrent(PhaseB)*sin1_1 + InputCurrent(PhaseC)*sin2_1 + InputCurrent(PhaseD)*sin3_1 + InputCurrent(PhaseE)*sin4_1) * 2/5", - 'I_d3': "(InputCurrent(PhaseA)*cos0_3 + InputCurrent(PhaseB)*cos1_3 + InputCurrent(PhaseC)*cos2_3 + InputCurrent(PhaseD)*cos3_3 + InputCurrent(PhaseE)*cos4_3) * 2/5", - 'I_q3': "(InputCurrent(PhaseA)*sin0_3 + InputCurrent(PhaseB)*sin1_3 + InputCurrent(PhaseC)*sin2_3 + InputCurrent(PhaseD)*sin3_3 + InputCurrent(PhaseE)*sin4_3) * 2/5", - 'V_d1': "(V_A*cos0_1 + V_B*cos1_1 + V_C*cos2_1 + V_D*cos3_1 + V_E*cos4_1) * 2/5", - 'V_q1': "(V_A*sin0_1 + V_B*sin1_1 + V_C*sin2_1 + V_D*sin3_1 + V_E*sin4_1) * 2/5", - 'V_d3': "(V_A*cos0_3 + V_B*cos1_3 + V_C*cos2_3 + V_D*cos3_3 + V_E*cos4_3) * 2/5", - 'V_q3': "(V_A*sin0_3 + V_B*sin1_3 + V_C*sin2_3 + V_D*sin3_3 + V_E*sin4_3) * 2/5", - 'L0d_1': "L(PhaseA,PhaseA)*cos0_1 + L(PhaseA,PhaseB)*cos1_1 + L(PhaseA,PhaseC)*cos2_1 + L(PhaseA,PhaseD)*cos3_1 + L(PhaseA,PhaseE)*cos4_1", - 'L0q_1': "L(PhaseA,PhaseA)*sin0_1 + L(PhaseA,PhaseB)*sin1_1 + L(PhaseA,PhaseC)*sin2_1 + L(PhaseA,PhaseD)*sin3_1 + L(PhaseA,PhaseE)*sin4_1", - 'L1d_1': "L(PhaseB,PhaseA)*cos0_1 + L(PhaseB,PhaseB)*cos1_1 + L(PhaseB,PhaseC)*cos2_1 + L(PhaseB,PhaseD)*cos3_1 + L(PhaseB,PhaseE)*cos4_1", - 'L1q_1': "L(PhaseB,PhaseA)*sin0_1 + L(PhaseB,PhaseB)*sin1_1 + L(PhaseB,PhaseC)*sin2_1 + L(PhaseB,PhaseD)*sin3_1 + L(PhaseB,PhaseE)*sin4_1", - 'L2d_1': "L(PhaseC,PhaseA)*cos0_1 + L(PhaseC,PhaseB)*cos1_1 + L(PhaseC,PhaseC)*cos2_1 + L(PhaseC,PhaseD)*cos3_1 + L(PhaseC,PhaseE)*cos4_1", - 'L2q_1': "L(PhaseC,PhaseA)*sin0_1 + L(PhaseC,PhaseB)*sin1_1 + L(PhaseC,PhaseC)*sin2_1 + L(PhaseC,PhaseD)*sin3_1 + L(PhaseC,PhaseE)*sin4_1", - 'L3d_1': "L(PhaseD,PhaseA)*cos0_1 + L(PhaseD,PhaseB)*cos1_1 + L(PhaseD,PhaseC)*cos2_1 + L(PhaseD,PhaseD)*cos3_1 + L(PhaseD,PhaseE)*cos4_1", - 'L3q_1': "L(PhaseD,PhaseA)*sin0_1 + L(PhaseD,PhaseB)*sin1_1 + L(PhaseD,PhaseC)*sin2_1 + L(PhaseD,PhaseD)*sin3_1 + L(PhaseD,PhaseE)*sin4_1", - 'L4d_1': "L(PhaseE,PhaseA)*cos0_1 + L(PhaseE,PhaseB)*cos1_1 + L(PhaseE,PhaseC)*cos2_1 + L(PhaseE,PhaseD)*cos3_1 + L(PhaseE,PhaseE)*cos4_1", - 'L4q_1': "L(PhaseE,PhaseA)*sin0_1 + L(PhaseE,PhaseB)*sin1_1 + L(PhaseE,PhaseC)*sin2_1 + L(PhaseE,PhaseD)*sin3_1 + L(PhaseE,PhaseE)*sin4_1", - 'L0d_3': "L(PhaseA,PhaseA)*cos0_3 + L(PhaseA,PhaseB)*cos1_3 + L(PhaseA,PhaseC)*cos2_3 + L(PhaseA,PhaseD)*cos3_3 + L(PhaseA,PhaseE)*cos4_3", - 'L0q_3': "L(PhaseA,PhaseA)*sin0_3 + L(PhaseA,PhaseB)*sin1_3 + L(PhaseA,PhaseC)*sin2_3 + L(PhaseA,PhaseD)*sin3_3 + L(PhaseA,PhaseE)*sin4_3", - 'L1d_3': "L(PhaseB,PhaseA)*cos0_3 + L(PhaseB,PhaseB)*cos1_3 + L(PhaseB,PhaseC)*cos2_3 + L(PhaseB,PhaseD)*cos3_3 + L(PhaseB,PhaseE)*cos4_3", - 'L1q_3': "L(PhaseB,PhaseA)*sin0_3 + L(PhaseB,PhaseB)*sin1_3 + L(PhaseB,PhaseC)*sin2_3 + L(PhaseB,PhaseD)*sin3_3 + L(PhaseB,PhaseE)*sin4_3", - 'L2d_3': "L(PhaseC,PhaseA)*cos0_3 + L(PhaseC,PhaseB)*cos1_3 + L(PhaseC,PhaseC)*cos2_3 + L(PhaseC,PhaseD)*cos3_3 + L(PhaseC,PhaseE)*cos4_3", - 'L2q_3': "L(PhaseC,PhaseA)*sin0_3 + L(PhaseC,PhaseB)*sin1_3 + L(PhaseC,PhaseC)*sin2_3 + L(PhaseC,PhaseD)*sin3_3 + L(PhaseC,PhaseE)*sin4_3", - 'L3d_3': "L(PhaseD,PhaseA)*cos0_3 + L(PhaseD,PhaseB)*cos1_3 + L(PhaseD,PhaseC)*cos2_3 + L(PhaseD,PhaseD)*cos3_3 + L(PhaseD,PhaseE)*cos4_3", - 'L3q_3': "L(PhaseD,PhaseA)*sin0_3 + L(PhaseD,PhaseB)*sin1_3 + L(PhaseD,PhaseC)*sin2_3 + L(PhaseD,PhaseD)*sin3_3 + L(PhaseD,PhaseE)*sin4_3", - 'L4d_3': "L(PhaseE,PhaseA)*cos0_3 + L(PhaseE,PhaseB)*cos1_3 + L(PhaseE,PhaseC)*cos2_3 + L(PhaseE,PhaseD)*cos3_3 + L(PhaseE,PhaseE)*cos4_3", - 'L4q_3': "L(PhaseE,PhaseA)*sin0_3 + L(PhaseE,PhaseB)*sin1_3 + L(PhaseE,PhaseC)*sin2_3 + L(PhaseE,PhaseD)*sin3_3 + L(PhaseE,PhaseE)*sin4_3", - 'Ld1': "(L0d_1*cos0_1 + L1d_1*cos1_1 + L2d_1*cos2_1 + L3d_1*cos3_1 + L4d_1*cos4_1) * 2/5", - 'Ld1q1': "(L0d_1*sin0_1 + L1d_1*sin1_1 + L2d_1*sin2_1 + L3d_1*sin3_1 + L4d_1*sin4_1) * 2/5", - 'Lq1d1': "(L0q_1*cos0_1 + L1q_1*cos1_1 + L2q_1*cos2_1 + L3q_1*cos3_1 + L4q_1*cos4_1) * 2/5", - 'Lq1': "(L0q_1*sin0_1 + L1q_1*sin1_1 + L2q_1*sin2_1 + L3q_1*sin3_1 + L4q_1*sin4_1) * 2/5", - 'Ld1d3': "(L0d_1*cos0_3 + L1d_1*cos1_3 + L2d_1*cos2_3 + L3d_1*cos3_3 + L4d_1*cos4_3) * 2/5", - 'Ld1q3': "(L0d_1*sin0_3 + L1d_1*sin1_3 + L2d_1*sin2_3 + L3d_1*sin3_3 + L4d_1*sin4_3) * 2/5", - 'Lq1d3': "(L0q_1*cos0_3 + L1q_1*cos1_3 + L2q_1*cos2_3 + L3q_1*cos3_3 + L4q_1*cos4_3) * 2/5", - 'Lq1q3': "(L0q_1*sin0_3 + L1q_1*sin1_3 + L2q_1*sin2_3 + L3q_1*sin3_3 + L4q_1*sin4_3) * 2/5", - 'Ld3d1': "(L0d_3*cos0_1 + L1d_3*cos1_1 + L2d_3*cos2_1 + L3d_3*cos3_1 + L4d_3*cos4_1) * 2/5", - 'Ld3q1': "(L0d_3*sin0_1 + L1d_3*sin1_1 + L2d_3*sin2_1 + L3d_3*sin3_1 + L4d_3*sin4_1) * 2/5", - 'Lq3d1': "(L0q_3*cos0_1 + L1q_3*cos1_1 + L2q_3*cos2_1 + L3q_3*cos3_1 + L4q_3*cos4_1) * 2/5", - 'Lq3q1': "(L0q_3*sin0_1 + L1q_3*sin1_1 + L2q_3*sin2_1 + L3q_3*sin3_1 + L4q_3*sin4_1) * 2/5", - 'Ld3': "(L0d_3*cos0_3 + L1d_3*cos1_3 + L2d_3*cos2_3 + L3d_3*cos3_3 + L4d_3*cos4_3) * 2/5", - 'Ld3q3': "(L0d_3*sin0_3 + L1d_3*sin1_3 + L2d_3*sin2_3 + L3d_3*sin3_3 + L4d_3*sin4_3) * 2/5", - 'Lq3d3': "(L0q_3*cos0_3 + L1q_3*cos1_3 + L2q_3*cos2_3 + L3q_3*cos3_3 + L4q_3*cos4_3) * 2/5", - 'Lq3': "(L0q_3*sin0_3 + L1q_3*sin1_3 + L2q_3*sin2_3 + L3q_3*sin3_3 + L4q_3*sin4_3) * 2/5", - 'Flux_e_d1': "Flux_d1 - (Ld1*I_d1 + Ld1q1*I_q1 + Ld1d3*I_d3 + Ld1q3*I_q3)", - 'Flux_e_q1': "Flux_q1 - (Lq1d1*I_d1 + Lq1*I_q1 + Lq1d3*I_d3 + Lq1q3*I_q3)", - '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*PolePairs*(1*(Flux_d1*I_q1 - Flux_q1*I_d1) + 3*(Flux_d3*I_q3 - Flux_q3*I_d3))", + "PolePairs": "2", + "RotSign": "1", + "Rstat": "19", + "Lew": "0", + "theta_el": "RotSign*(Moving1.Position - InitPos) * PolePairs - pi", + "cos0_1": "cos(1*(theta_el - 2*PI*0/5))", + "sin0_1": "sin(-1*(theta_el - 2*PI*0/5))", + "cos1_1": "cos(1*(theta_el - 2*PI*1/5))", + "sin1_1": "sin(-1*(theta_el - 2*PI*1/5))", + "cos2_1": "cos(1*(theta_el - 2*PI*2/5))", + "sin2_1": "sin(-1*(theta_el - 2*PI*2/5))", + "cos3_1": "cos(1*(theta_el - 2*PI*3/5))", + "sin3_1": "sin(-1*(theta_el - 2*PI*3/5))", + "cos4_1": "cos(1*(theta_el - 2*PI*4/5))", + "sin4_1": "sin(-1*(theta_el - 2*PI*4/5))", + "cos0_3": "cos(3*(theta_el - 2*PI*0/5))", + "sin0_3": "sin(-3*(theta_el - 2*PI*0/5))", + "cos1_3": "cos(3*(theta_el - 2*PI*1/5))", + "sin1_3": "sin(-3*(theta_el - 2*PI*1/5))", + "cos2_3": "cos(3*(theta_el - 2*PI*2/5))", + "sin2_3": "sin(-3*(theta_el - 2*PI*2/5))", + "cos3_3": "cos(3*(theta_el - 2*PI*3/5))", + "sin3_3": "sin(-3*(theta_el - 2*PI*3/5))", + "cos4_3": "cos(3*(theta_el - 2*PI*4/5))", + "sin4_3": "sin(-3*(theta_el - 2*PI*4/5))", + "Flux_d1": "(FluxLinkage(PhaseA)*cos0_1 + FluxLinkage(PhaseB)*cos1_1 + FluxLinkage(PhaseC)*cos2_1 + FluxLinkage(PhaseD)*cos3_1 + FluxLinkage(PhaseE)*cos4_1) * 2/5", + "Flux_q1": "(FluxLinkage(PhaseA)*sin0_1 + FluxLinkage(PhaseB)*sin1_1 + FluxLinkage(PhaseC)*sin2_1 + FluxLinkage(PhaseD)*sin3_1 + FluxLinkage(PhaseE)*sin4_1) * 2/5", + "Flux_d3": "(FluxLinkage(PhaseA)*cos0_3 + FluxLinkage(PhaseB)*cos1_3 + FluxLinkage(PhaseC)*cos2_3 + FluxLinkage(PhaseD)*cos3_3 + FluxLinkage(PhaseE)*cos4_3) * 2/5", + "Flux_q3": "(FluxLinkage(PhaseA)*sin0_3 + FluxLinkage(PhaseB)*sin1_3 + FluxLinkage(PhaseC)*sin2_3 + FluxLinkage(PhaseD)*sin3_3 + FluxLinkage(PhaseE)*sin4_3) * 2/5", + "Vind_d1": "(InducedVoltage(PhaseA)*cos0_1 + InducedVoltage(PhaseB)*cos1_1 + InducedVoltage(PhaseC)*cos2_1 + InducedVoltage(PhaseD)*cos3_1 + InducedVoltage(PhaseE)*cos4_1) * 2/5", + "Vind_q1": "(InducedVoltage(PhaseA)*sin0_1 + InducedVoltage(PhaseB)*sin1_1 + InducedVoltage(PhaseC)*sin2_1 + InducedVoltage(PhaseD)*sin3_1 + InducedVoltage(PhaseE)*sin4_1) * 2/5", + "Vind_d3": "(InducedVoltage(PhaseA)*cos0_3 + InducedVoltage(PhaseB)*cos1_3 + InducedVoltage(PhaseC)*cos2_3 + InducedVoltage(PhaseD)*cos3_3 + InducedVoltage(PhaseE)*cos4_3) * 2/5", + "Vind_q3": "(InducedVoltage(PhaseA)*sin0_3 + InducedVoltage(PhaseB)*sin1_3 + InducedVoltage(PhaseC)*sin2_3 + InducedVoltage(PhaseD)*sin3_3 + InducedVoltage(PhaseE)*sin4_3) * 2/5", + "dIA_dt": "-Im1*2*pi*fr*sin(2*pi*fr*Time+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time)+epsI3-pi)", + "dIB_dt": "-Im1*2*pi*fr*sin(2*pi*fr*Time-72deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-72deg)+epsI3-pi)", + "dIC_dt": "-Im1*2*pi*fr*sin(2*pi*fr*Time-144deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-144deg)+epsI3-pi)", + "dID_dt": "-Im1*2*pi*fr*sin(2*pi*fr*Time-216deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-216deg)+epsI3-pi)", + "dIE_dt": "-Im1*2*pi*fr*sin(2*pi*fr*Time-288deg+epsI1-pi) - Im3*6*pi*fr*sin(3*(2*pi*fr*Time-288deg)+epsI3-pi)", + "V_A": "InducedVoltage(PhaseA) + Rstat*InputCurrent(PhaseA) + Lew*dIA_dt", + "V_B": "InducedVoltage(PhaseB) + Rstat*InputCurrent(PhaseB) + Lew*dIB_dt", + "V_C": "InducedVoltage(PhaseC) + Rstat*InputCurrent(PhaseC) + Lew*dIC_dt", + "V_D": "InducedVoltage(PhaseD) + Rstat*InputCurrent(PhaseD) + Lew*dID_dt", + "V_E": "InducedVoltage(PhaseE) + Rstat*InputCurrent(PhaseE) + Lew*dIE_dt", + "V_AC": "V_A - V_C", + "V_BD": "V_B - V_D", + "V_CE": "V_C - V_E", + "V_DA": "V_D - V_A", + "V_EB": "V_E - V_B", + "Vterm_A": "1/5*(2*V_AC + -1*V_BD + 1*V_CE + -2*V_DA)", + "Vterm_B": "1/5*(2*V_AC + 4*V_BD + 1*V_CE + 3*V_DA)", + "Vterm_C": "1/5*(-3*V_AC + -1*V_BD + 1*V_CE + -2*V_DA)", + "Vterm_D": "1/5*(2*V_AC + -1*V_BD + 1*V_CE + 3*V_DA)", + "Vterm_E": "1/5*(-3*V_AC + -1*V_BD + -4*V_CE + -2*V_DA)", + "I_d1": "(InputCurrent(PhaseA)*cos0_1 + InputCurrent(PhaseB)*cos1_1 + InputCurrent(PhaseC)*cos2_1 + InputCurrent(PhaseD)*cos3_1 + InputCurrent(PhaseE)*cos4_1) * 2/5", + "I_q1": "(InputCurrent(PhaseA)*sin0_1 + InputCurrent(PhaseB)*sin1_1 + InputCurrent(PhaseC)*sin2_1 + InputCurrent(PhaseD)*sin3_1 + InputCurrent(PhaseE)*sin4_1) * 2/5", + "I_d3": "(InputCurrent(PhaseA)*cos0_3 + InputCurrent(PhaseB)*cos1_3 + InputCurrent(PhaseC)*cos2_3 + InputCurrent(PhaseD)*cos3_3 + InputCurrent(PhaseE)*cos4_3) * 2/5", + "I_q3": "(InputCurrent(PhaseA)*sin0_3 + InputCurrent(PhaseB)*sin1_3 + InputCurrent(PhaseC)*sin2_3 + InputCurrent(PhaseD)*sin3_3 + InputCurrent(PhaseE)*sin4_3) * 2/5", + "V_d1": "(V_A*cos0_1 + V_B*cos1_1 + V_C*cos2_1 + V_D*cos3_1 + V_E*cos4_1) * 2/5", + "V_q1": "(V_A*sin0_1 + V_B*sin1_1 + V_C*sin2_1 + V_D*sin3_1 + V_E*sin4_1) * 2/5", + "V_d3": "(V_A*cos0_3 + V_B*cos1_3 + V_C*cos2_3 + V_D*cos3_3 + V_E*cos4_3) * 2/5", + "V_q3": "(V_A*sin0_3 + V_B*sin1_3 + V_C*sin2_3 + V_D*sin3_3 + V_E*sin4_3) * 2/5", + "L0d_1": "L(PhaseA,PhaseA)*cos0_1 + L(PhaseA,PhaseB)*cos1_1 + L(PhaseA,PhaseC)*cos2_1 + L(PhaseA,PhaseD)*cos3_1 + L(PhaseA,PhaseE)*cos4_1", + "L0q_1": "L(PhaseA,PhaseA)*sin0_1 + L(PhaseA,PhaseB)*sin1_1 + L(PhaseA,PhaseC)*sin2_1 + L(PhaseA,PhaseD)*sin3_1 + L(PhaseA,PhaseE)*sin4_1", + "L1d_1": "L(PhaseB,PhaseA)*cos0_1 + L(PhaseB,PhaseB)*cos1_1 + L(PhaseB,PhaseC)*cos2_1 + L(PhaseB,PhaseD)*cos3_1 + L(PhaseB,PhaseE)*cos4_1", + "L1q_1": "L(PhaseB,PhaseA)*sin0_1 + L(PhaseB,PhaseB)*sin1_1 + L(PhaseB,PhaseC)*sin2_1 + L(PhaseB,PhaseD)*sin3_1 + L(PhaseB,PhaseE)*sin4_1", + "L2d_1": "L(PhaseC,PhaseA)*cos0_1 + L(PhaseC,PhaseB)*cos1_1 + L(PhaseC,PhaseC)*cos2_1 + L(PhaseC,PhaseD)*cos3_1 + L(PhaseC,PhaseE)*cos4_1", + "L2q_1": "L(PhaseC,PhaseA)*sin0_1 + L(PhaseC,PhaseB)*sin1_1 + L(PhaseC,PhaseC)*sin2_1 + L(PhaseC,PhaseD)*sin3_1 + L(PhaseC,PhaseE)*sin4_1", + "L3d_1": "L(PhaseD,PhaseA)*cos0_1 + L(PhaseD,PhaseB)*cos1_1 + L(PhaseD,PhaseC)*cos2_1 + L(PhaseD,PhaseD)*cos3_1 + L(PhaseD,PhaseE)*cos4_1", + "L3q_1": "L(PhaseD,PhaseA)*sin0_1 + L(PhaseD,PhaseB)*sin1_1 + L(PhaseD,PhaseC)*sin2_1 + L(PhaseD,PhaseD)*sin3_1 + L(PhaseD,PhaseE)*sin4_1", + "L4d_1": "L(PhaseE,PhaseA)*cos0_1 + L(PhaseE,PhaseB)*cos1_1 + L(PhaseE,PhaseC)*cos2_1 + L(PhaseE,PhaseD)*cos3_1 + L(PhaseE,PhaseE)*cos4_1", + "L4q_1": "L(PhaseE,PhaseA)*sin0_1 + L(PhaseE,PhaseB)*sin1_1 + L(PhaseE,PhaseC)*sin2_1 + L(PhaseE,PhaseD)*sin3_1 + L(PhaseE,PhaseE)*sin4_1", + "L0d_3": "L(PhaseA,PhaseA)*cos0_3 + L(PhaseA,PhaseB)*cos1_3 + L(PhaseA,PhaseC)*cos2_3 + L(PhaseA,PhaseD)*cos3_3 + L(PhaseA,PhaseE)*cos4_3", + "L0q_3": "L(PhaseA,PhaseA)*sin0_3 + L(PhaseA,PhaseB)*sin1_3 + L(PhaseA,PhaseC)*sin2_3 + L(PhaseA,PhaseD)*sin3_3 + L(PhaseA,PhaseE)*sin4_3", + "L1d_3": "L(PhaseB,PhaseA)*cos0_3 + L(PhaseB,PhaseB)*cos1_3 + L(PhaseB,PhaseC)*cos2_3 + L(PhaseB,PhaseD)*cos3_3 + L(PhaseB,PhaseE)*cos4_3", + "L1q_3": "L(PhaseB,PhaseA)*sin0_3 + L(PhaseB,PhaseB)*sin1_3 + L(PhaseB,PhaseC)*sin2_3 + L(PhaseB,PhaseD)*sin3_3 + L(PhaseB,PhaseE)*sin4_3", + "L2d_3": "L(PhaseC,PhaseA)*cos0_3 + L(PhaseC,PhaseB)*cos1_3 + L(PhaseC,PhaseC)*cos2_3 + L(PhaseC,PhaseD)*cos3_3 + L(PhaseC,PhaseE)*cos4_3", + "L2q_3": "L(PhaseC,PhaseA)*sin0_3 + L(PhaseC,PhaseB)*sin1_3 + L(PhaseC,PhaseC)*sin2_3 + L(PhaseC,PhaseD)*sin3_3 + L(PhaseC,PhaseE)*sin4_3", + "L3d_3": "L(PhaseD,PhaseA)*cos0_3 + L(PhaseD,PhaseB)*cos1_3 + L(PhaseD,PhaseC)*cos2_3 + L(PhaseD,PhaseD)*cos3_3 + L(PhaseD,PhaseE)*cos4_3", + "L3q_3": "L(PhaseD,PhaseA)*sin0_3 + L(PhaseD,PhaseB)*sin1_3 + L(PhaseD,PhaseC)*sin2_3 + L(PhaseD,PhaseD)*sin3_3 + L(PhaseD,PhaseE)*sin4_3", + "L4d_3": "L(PhaseE,PhaseA)*cos0_3 + L(PhaseE,PhaseB)*cos1_3 + L(PhaseE,PhaseC)*cos2_3 + L(PhaseE,PhaseD)*cos3_3 + L(PhaseE,PhaseE)*cos4_3", + "L4q_3": "L(PhaseE,PhaseA)*sin0_3 + L(PhaseE,PhaseB)*sin1_3 + L(PhaseE,PhaseC)*sin2_3 + L(PhaseE,PhaseD)*sin3_3 + L(PhaseE,PhaseE)*sin4_3", + "Ld1": "(L0d_1*cos0_1 + L1d_1*cos1_1 + L2d_1*cos2_1 + L3d_1*cos3_1 + L4d_1*cos4_1) * 2/5", + "Ld1q1": "(L0d_1*sin0_1 + L1d_1*sin1_1 + L2d_1*sin2_1 + L3d_1*sin3_1 + L4d_1*sin4_1) * 2/5", + "Lq1d1": "(L0q_1*cos0_1 + L1q_1*cos1_1 + L2q_1*cos2_1 + L3q_1*cos3_1 + L4q_1*cos4_1) * 2/5", + "Lq1": "(L0q_1*sin0_1 + L1q_1*sin1_1 + L2q_1*sin2_1 + L3q_1*sin3_1 + L4q_1*sin4_1) * 2/5", + "Ld1d3": "(L0d_1*cos0_3 + L1d_1*cos1_3 + L2d_1*cos2_3 + L3d_1*cos3_3 + L4d_1*cos4_3) * 2/5", + "Ld1q3": "(L0d_1*sin0_3 + L1d_1*sin1_3 + L2d_1*sin2_3 + L3d_1*sin3_3 + L4d_1*sin4_3) * 2/5", + "Lq1d3": "(L0q_1*cos0_3 + L1q_1*cos1_3 + L2q_1*cos2_3 + L3q_1*cos3_3 + L4q_1*cos4_3) * 2/5", + "Lq1q3": "(L0q_1*sin0_3 + L1q_1*sin1_3 + L2q_1*sin2_3 + L3q_1*sin3_3 + L4q_1*sin4_3) * 2/5", + "Ld3d1": "(L0d_3*cos0_1 + L1d_3*cos1_1 + L2d_3*cos2_1 + L3d_3*cos3_1 + L4d_3*cos4_1) * 2/5", + "Ld3q1": "(L0d_3*sin0_1 + L1d_3*sin1_1 + L2d_3*sin2_1 + L3d_3*sin3_1 + L4d_3*sin4_1) * 2/5", + "Lq3d1": "(L0q_3*cos0_1 + L1q_3*cos1_1 + L2q_3*cos2_1 + L3q_3*cos3_1 + L4q_3*cos4_1) * 2/5", + "Lq3q1": "(L0q_3*sin0_1 + L1q_3*sin1_1 + L2q_3*sin2_1 + L3q_3*sin3_1 + L4q_3*sin4_1) * 2/5", + "Ld3": "(L0d_3*cos0_3 + L1d_3*cos1_3 + L2d_3*cos2_3 + L3d_3*cos3_3 + L4d_3*cos4_3) * 2/5", + "Ld3q3": "(L0d_3*sin0_3 + L1d_3*sin1_3 + L2d_3*sin2_3 + L3d_3*sin3_3 + L4d_3*sin4_3) * 2/5", + "Lq3d3": "(L0q_3*cos0_3 + L1q_3*cos1_3 + L2q_3*cos2_3 + L3q_3*cos3_3 + L4q_3*cos4_3) * 2/5", + "Lq3": "(L0q_3*sin0_3 + L1q_3*sin1_3 + L2q_3*sin2_3 + L3q_3*sin3_3 + L4q_3*sin4_3) * 2/5", + "Flux_e_d1": "Flux_d1 - (Ld1*I_d1 + Ld1q1*I_q1 + Ld1d3*I_d3 + Ld1q3*I_q3)", + "Flux_e_q1": "Flux_q1 - (Lq1d1*I_d1 + Lq1*I_q1 + Lq1d3*I_d3 + Lq1q3*I_q3)", + "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*PolePairs*(1*(Flux_d1*I_q1 - Flux_q1*I_d1) + 3*(Flux_d3*I_q3 - Flux_q3*I_d3))", } def set_post_params(self): self.post_params = { # reports - ("InducedVoltage(PhaseA)","InducedVoltage(PhaseB)","InducedVoltage(PhaseC)","InducedVoltage(PhaseD)","InducedVoltage(PhaseE)"): "InducedVoltage", + ("InducedVoltage(PhaseA)", "InducedVoltage(PhaseB)", "InducedVoltage(PhaseC)", "InducedVoltage(PhaseD)", "InducedVoltage(PhaseE)"): "InducedVoltage", ("Moving1.Torque"): "Torque", - ("InputCurrent(PhaseA)","InputCurrent(PhaseB)","InputCurrent(PhaseC)","InputCurrent(PhaseD)","InputCurrent(PhaseE)"): "Current", - ("FluxLinkage(PhaseA)","FluxLinkage(PhaseB)","FluxLinkage(PhaseC)","FluxLinkage(PhaseD)","FluxLinkage(PhaseE)"): "FluxLinkage", + ("InputCurrent(PhaseA)", "InputCurrent(PhaseB)", "InputCurrent(PhaseC)", "InputCurrent(PhaseD)", "InputCurrent(PhaseE)"): "Current", + ("FluxLinkage(PhaseA)", "FluxLinkage(PhaseB)", "FluxLinkage(PhaseC)", "FluxLinkage(PhaseD)", "FluxLinkage(PhaseE)"): "FluxLinkage", ("I_d1", "I_q1", "I_d3", "I_q3"): "Current_dq", ("Flux_d1", "Flux_q1", "Flux_d3", "Flux_q3"): "FluxLinkage_dq", ("Flux_e_d1", "Flux_e_q1", "Flux_e_d3", "Flux_e_q3"): "FluxLinkage excitation_dq", @@ -178,7 +189,7 @@ def set_post_params(self): ("Ld1", "Lq1", "Ld3", "Lq3"): "Inductance_dq main", ("Ld1q1", "Ld1d3", "Ld1q3", "Lq1d3", "Lq1q3", "Ld3q3"): "Inductance_dq cross-coupling", } - + def assign_stator_coils(self): m2d = self.m2d @@ -189,7 +200,7 @@ def assign_stator_coils(self): I_D = "Im1*cos(2*pi*fr*time-216deg+epsI1-pi) + Im3*cos(3*(2*pi*fr*time-216deg)+epsI3-pi)" I_E = "Im1*cos(2*pi*fr*time-288deg+epsI1-pi) + Im3*cos(3*(2*pi*fr*time-288deg)+epsI3-pi)" m2d.assign_coil - #Define phase windings + # Define phase windings m2d.assign_coil( assignment=["Coil"], conductors_number="Nc", @@ -292,21 +303,11 @@ def assign_stator_coils(self): name="PhaseE", ) - m2d.add_winding_coils( - assignment="PhaseA", coils=["CS1", "CS10"] - ) - m2d.add_winding_coils( - assignment="PhaseB", coils=["CS4", "CS5"] - ) - m2d.add_winding_coils( - assignment="PhaseC", coils=["CS8", "CS9"] - ) - m2d.add_winding_coils( - assignment="PhaseD", coils=["CS2", "CS3"] - ) - m2d.add_winding_coils( - assignment="PhaseE", coils=["CS6", "CS7"] - ) + m2d.add_winding_coils(assignment="PhaseA", coils=["CS1", "CS10"]) + m2d.add_winding_coils(assignment="PhaseB", coils=["CS4", "CS5"]) + m2d.add_winding_coils(assignment="PhaseC", coils=["CS8", "CS9"]) + m2d.add_winding_coils(assignment="PhaseD", coils=["CS2", "CS3"]) + m2d.add_winding_coils(assignment="PhaseE", coils=["CS6", "CS7"]) def inductance_computation(self): self.m2d.change_inductance_computation(compute_transient_inductance=True, incremental_matrix=True)