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
114 changes: 114 additions & 0 deletions tests/test_generators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import numpy as np
import pytest

from machine_design.designs.design import Design
from machine_design.optimization.generators import (
HacklGenerator_3BrokenLines,
HacklGenerator_OneLambda,
HacklGenerator_SixLambdas,
check_barrier,
get_arc,
signed_distance,
)
from motors.synrm_3f_36s import Computation, Geometry

r_stator_end = 0.7
offset = 0.35

# feasible design
known_good_designs = {
"OneLambda": (HacklGenerator_OneLambda, np.array([7.1, 20.4, 33.7, 13.7, 27.1, 40.65, 0.35])),
"SixLambdas": (
HacklGenerator_SixLambdas,
np.array([8.95052421, 23.01115583, 33.03271713, 14.34330278, 27.92171436, 37.3, 0.21260669, 0.31, 0.41, 0.3, 0.4, 0.46]),
),
"3BrokenLines": (
HacklGenerator_3BrokenLines,
np.array([6.0538801, 23.5, 35.74582285, 10.6, 28.5920024, 40.7191182, 0.36646249, 22.65142559, 30.7050295, 38.0, 9.49161503, 2.0, 1.5]),
),
}


def test_get_arc_endpoints():
arc = get_arc(R=2.0, start_deg=0, end_deg=90, n_points=3)
assert arc.shape == (3, 2)
assert arc[0] == pytest.approx([2.0, 0.0], abs=1e-10)
assert arc[-1] == pytest.approx([0.0, 2.0], abs=1e-10)


def test_signed_distance():
P = np.array([[0.0, 0.0], [1.0, 1.0]])
dist = signed_distance(P, a=1, b=1, c=-1)
assert dist == pytest.approx([-1.0, 1.0])


def test_check_barrier_closed():
# closed, should not raise
barrier = np.array([[0.0, 0.0], [1.0, 1.0], [0.0, 0.0]])
check_barrier(barrier)


def test_check_barrier_open():
# open, should raise
barrier = np.array([[0.0, 0.0], [1.0, 1.0], [2.0, 2.0]])
with pytest.raises(ValueError):
check_barrier(barrier)


@pytest.fixture
def six_lambdas_generator():
geometry = Geometry()
dummy_design = Design(m2d=None, geometry=geometry, computation=Computation(geometry))
return HacklGenerator_SixLambdas(dummy_design, r_stator_end, offset=offset)


@pytest.fixture
def configured_generator(six_lambdas_generator):
x_best = known_good_designs["SixLambdas"][1]
params = six_lambdas_generator.X_to_params(x_best)
six_lambdas_generator.set_parameters(params)
return six_lambdas_generator


def test_x_to_params_splits_groups(six_lambdas_generator):
# 4 groups of 3
x_best = known_good_designs["SixLambdas"][1]
phis_inner, phis_outer, lam_inner, lam_outer = six_lambdas_generator.X_to_params(x_best)
assert list(phis_inner) == pytest.approx([8.95052421, 23.01115583, 33.03271713])
assert list(phis_outer) == pytest.approx([14.34330278, 27.92171436, 37.3])
assert list(lam_inner) == pytest.approx([0.21260669, 0.31, 0.41])
assert list(lam_outer) == pytest.approx([0.3, 0.4, 0.46])


def test_set_parameters_stores_values(configured_generator):
assert configured_generator.phis_inner == pytest.approx([8.95052421, 23.01115583, 33.03271713])
assert configured_generator.lam_outer == pytest.approx([0.3, 0.4, 0.46])


@pytest.fixture(params=list(known_good_designs))
def any_configured_generator(request):
generator_cls, x_best = known_good_designs[request.param]
geometry = Geometry()
generator = generator_cls(Design(m2d=None, geometry=geometry, computation=Computation(geometry)), r_stator_end, offset=offset)
generator.set_parameters(generator.X_to_params(x_best))
return generator


def test_barrier_count(any_configured_generator):
# one barrier per pole pair
barriers = any_configured_generator.generate_barriers()
assert len(barriers) == 3


def test_split_barriers_closed(any_configured_generator):
# each barrier should be closed
barriers = any_configured_generator.generate_barriers()
barriers = any_configured_generator.split_barriers(barriers)
for barrier in barriers:
assert barrier[0] == pytest.approx(barrier[-1])


def test_known_good_design_is_feasible(any_configured_generator):
barriers = any_configured_generator.generate_barriers()
barriers = any_configured_generator.split_barriers(barriers)
assert any_configured_generator.feasible_barriers(barriers) is True
48 changes: 48 additions & 0 deletions tests/test_optimization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import pickle

import numpy as np
import pandas as pd
import pytest

from machine_design.optimization.optimization import init_points, objective_transform


def test_objective_transform_normal_case():
f1, f2 = objective_transform(TorAvg=4.5, TorRippleRms=8.0)
assert f1 == pytest.approx(4.5)
assert f2 == pytest.approx(-0.08)


def test_objective_transform_fallback():
# torque null, uses fallback
f1, f2 = objective_transform(TorAvg=np.nan, TorRippleRms=np.nan, objective_fallback=(3.0, 5.0))
assert f1 == pytest.approx(3.0)
assert f2 == pytest.approx(-0.05)


def test_objective_transform_no_fallback():
# torque null, no fallback given, should raise
with pytest.raises(ValueError):
objective_transform(np.nan, np.nan)


def test_init_points_filters_and_flattens(tmp_path):
# filters by method and non-null T, flattens nested params into one X row
metadata = pd.DataFrame(
{
"method": ["A", "A", "B"],
"design": [0, 1, 0],
"T": [5.0, None, 6.0],
"ripple": [10.0, 20.0, 15.0],
}
)
metadata.to_csv(tmp_path / "metadata.csv", index=False)

with open(tmp_path / "design_A_0.pkl", "wb") as f:
pickle.dump((1.0, 2.0, [3.0, 4.0]), f)

Xs, Ys = init_points(str(tmp_path), "A")

assert Xs.shape == (1, 4)
assert Xs[0].tolist() == pytest.approx([1.0, 2.0, 3.0, 4.0])
assert Ys[0].tolist() == pytest.approx([5.0, -0.1])
34 changes: 34 additions & 0 deletions tests/test_optimization_geometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import numpy as np
import pytest

from machine_design.optimization.geometry import analyze_results, rotate, rotation_matrix


def test_rotation_matrix_zero_degrees():
R = rotation_matrix(0)
assert R == pytest.approx(np.eye(2))


def test_rotation_matrix_90_degrees():
R = rotation_matrix(90)
assert R == pytest.approx(np.array([[0, -1], [1, 0]]), abs=1e-10)


def test_rotation_matrix_rad_matches_deg():
R_deg = rotation_matrix(180)
R_rad = rotation_matrix(np.pi, rad=True)
assert R_deg == pytest.approx(R_rad)


def test_rotate_90_degrees():
x, y = rotate(1, 0, 90)
assert x == pytest.approx(0, abs=1e-10)
assert y == pytest.approx(1)


def test_analyze_results_drops_last_point():
Tor = np.array([9.0, 10.0, 11.0, 12.0, 999.0]) # last point excluded from the average
TorAvg, TorRmsAC, TorRippleRms = analyze_results(Tor)
assert TorAvg == pytest.approx(10.5)
assert TorRmsAC == pytest.approx(1.1180339887498949)
assert TorRippleRms == pytest.approx(10.647942749998999)
6 changes: 6 additions & 0 deletions tests/test_synrm_3f_36s.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
from types import SimpleNamespace

import numpy as np
import pytest
Expand Down Expand Up @@ -149,3 +150,8 @@ def test_mm_to_str(geometry):
def test_mm_to_str_raises_without_mm_suffix(geometry):
with pytest.raises(Exception):
geometry.mm_to_str("geom_params", "SlotNumber")


def test_extract_results_returns_data_magnitude(computation):
solutions = SimpleNamespace(data_magnitude=lambda: [1.0, 2.0, 3.0])
assert computation.extract_results(solutions) == [1.0, 2.0, 3.0]
10 changes: 10 additions & 0 deletions tests/test_synrm_5f_40s.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from types import SimpleNamespace

import numpy as np
import pytest

Expand Down Expand Up @@ -77,3 +79,11 @@ def test_post_params_plot_names_unique(computation2):
def test_output_vars_known_values(computation2):
assert computation2.output_vars["Rstat"] == "19"
assert computation2.output_vars["Lew"] == "0"


def test_extract_results_converts_units_by_prefix(computation2):
solutions = SimpleNamespace(data_real=lambda expr: 100.0)
out = computation2.extract_results(solutions)
assert out["Ld1"] == pytest.approx(100.0 / 1e9) # Ld*/Lq* , nH
assert out["I_d1"] == pytest.approx(100.0 / 1e3) # I_* , A
assert out["V_d1"] == pytest.approx(100.0)
Loading