Skip to content

Commit 2cd4fce

Browse files
Merge pull request #48 from AI-RICE/pl_mag
Adding magnet class
2 parents 7b8e3cd + 1c3cd00 commit 2cd4fce

3 files changed

Lines changed: 178 additions & 5 deletions

File tree

‎machine_design/optimization/__init__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
HacklGenerator_OneLambda,
55
HacklGenerator_SixLambdas,
66
HacklGenerator_TwoLambdas,
7+
MagnetGenerator,
78
save_params,
89
)
910
from .geometry import analyze_results, plot_barriers, rotate

‎machine_design/optimization/generators.py‎

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,3 +427,179 @@ def _get_bezier_curve(self, phi_deg, is_inner, i):
427427

428428
# Stack segments and drop duplicate joint points
429429
return np.vstack((seg1, seg2[1:], seg3[1:]))
430+
431+
432+
class MagnetGenerator(BarrierGenerator):
433+
def __init__(self, design, r_stator_end, der1=1.0, der2=1.0, symmetric=True, **kwargs):
434+
self.n_designs = kwargs.get("n_designs", 1)
435+
self.der1 = der1
436+
self.der2 = der2
437+
self.symmetric = symmetric
438+
self.n_barriers = kwargs.get("n_barriers", 1)
439+
super().__init__(design, r_stator_end, **kwargs)
440+
441+
def _create_barrier(
442+
self,
443+
y_min,
444+
w_min,
445+
y_mid,
446+
w_mid,
447+
theta,
448+
w_max,
449+
):
450+
451+
theta1 = (theta + 45) / 180 * np.pi
452+
x_max1 = self.R * np.cos(theta1)
453+
y_max1 = self.R * np.sin(theta1)
454+
455+
x1 = [0, x_max1 / 2, x_max1]
456+
y1 = [y_min, y_mid, y_max1]
457+
f1 = CubicSpline(x1, y1, bc_type=((1, 0), (1, self.der1)))
458+
459+
theta2 = theta1 + w_max / self.r_max
460+
x_max2 = self.R * np.cos(theta2)
461+
y_max2 = self.R * np.sin(theta2)
462+
463+
s = w_mid / np.sqrt(1 + self.der1**2 / 4)
464+
x2 = [0, x1[1] - s * self.der1 / 2, x_max2]
465+
y2 = [y_min + w_min, y1[1] + s, y_max2]
466+
f2 = CubicSpline(x2, y2, bc_type=((1, 0), (1, self.der2)))
467+
468+
x_interp1 = np.linspace(x1[0], x1[-1], self.n_curve)
469+
x_interp2 = np.linspace(x2[0], x2[-1], self.n_curve)
470+
# TODO: use more points for connecting?
471+
x_all = np.concatenate((x_interp1, x_interp2[::-1]))
472+
y_all = np.concatenate((f1(x_interp1), f2(x_interp2)[::-1]))
473+
if self.symmetric:
474+
x_all = np.concatenate((x_all, -x_all[::-1][1:]))
475+
y_all = np.concatenate((y_all, y_all[::-1][1:]))
476+
x_all, y_all = rotate(x_all, y_all, -45)
477+
return x_all, y_all
478+
479+
@property
480+
def bounds(self) -> tuple[np.ndarray, np.ndarray]:
481+
n = self.n_barriers
482+
483+
lb = np.concatenate(
484+
[
485+
np.full(n, 0.7), # rand1
486+
np.array([12]), # rand2
487+
np.full(n - 1, 1), # spacings
488+
]
489+
)
490+
491+
ub = np.concatenate([np.full(n, 1.0), np.array([20]), np.full(n - 1, 5)])
492+
493+
return lb, ub
494+
495+
def generate_w_mins_base(self, rotor_r_min, rotor_r_max):
496+
n_barriers = self.n_barriers
497+
available_height = rotor_r_max - rotor_r_min
498+
gap_ratio = 0.5
499+
total_dap = available_height * gap_ratio
500+
501+
total_height_barrier = available_height - total_dap
502+
503+
if n_barriers == 1:
504+
weights = np.array([1.0])
505+
else:
506+
weights = np.array([0.99**i for i in range(n_barriers)])
507+
508+
weights = weights / np.sum(weights)
509+
510+
return total_height_barrier * weights
511+
512+
def random_parameters(self):
513+
514+
rand1 = np.random.uniform(0.7, 1.0, self.n_barriers)
515+
rand2 = np.random.uniform(12, 20)
516+
spacings = np.random.dirichlet(np.ones(self.n_barriers - 1))
517+
518+
return rand1, rand2, spacings
519+
520+
def set_parameters(self, params) -> None:
521+
rand1, rand2, spacings = params
522+
523+
w_mins_base = self.generate_w_mins_base(self.r_min, self.r_max)
524+
525+
w_mins = w_mins_base * rand1
526+
w_mids = w_mins.copy()
527+
528+
total_height = np.sum(w_mins)
529+
total_spacing = np.sum(spacings)
530+
531+
available_height = self.r_max - rand2
532+
533+
eps = 1e-8
534+
denom = total_height + total_spacing
535+
scale = (available_height) / max(denom, eps)
536+
537+
if scale < 1.0:
538+
w_mins *= scale
539+
spacings *= scale
540+
541+
y_mins = np.zeros(self.n_barriers)
542+
y_mins[0] = rand2
543+
current_pos = y_mins[0]
544+
545+
for i in range(1, self.n_barriers):
546+
current_pos += w_mins[i - 1] + spacings[i - 1]
547+
y_mins[i] = current_pos
548+
549+
offsets = w_mins * 0.1
550+
y_mids = y_mins + offsets
551+
552+
w_maxs = np.clip(w_mins * 0.7, 0.01, None)
553+
554+
w_maxs = np.clip(w_mins * 0.7, 0.01, None)
555+
thetas = np.linspace(1, 20, self.n_barriers)
556+
thetas[0] = 1
557+
558+
self.y_mins = y_mins
559+
self.w_mins = w_mins
560+
self.y_mids = y_mids
561+
self.w_mids = w_mids
562+
self.thetas = thetas
563+
self.w_maxs = w_maxs
564+
565+
return y_mins, w_mins, y_mids, w_mids, thetas, w_maxs
566+
567+
def X_to_params(self, X: np.ndarray, barrier=None):
568+
n = self.n_barriers if barrier is None else barrier
569+
570+
if len(X) != 2 * n:
571+
raise ValueError(f"X length {len(X)} does not match expected 2*n={2 * n}")
572+
573+
rand1 = X[:n]
574+
rand2 = X[n]
575+
spacings = X[n + 1 : n + 1 + (n - 1)]
576+
577+
return rand1, rand2, spacings
578+
579+
def generate_barriers(self) -> list[np.ndarray]:
580+
barriers = []
581+
for args in zip(self.y_mins, self.w_mins, self.y_mids, self.w_mids, self.thetas, self.w_maxs):
582+
x_all, y_all = self._create_barrier(*args)
583+
xy_all = np.vstack((x_all, y_all)).T
584+
barriers.append(xy_all)
585+
return barriers
586+
587+
def generate_magnets(self, barriers: list[np.ndarray]) -> list[np.ndarray]:
588+
magnets = []
589+
margin = 0.0
590+
591+
for barrier in barriers:
592+
x, y = barrier[:, 0], barrier[:, 1]
593+
mag_pts = np.column_stack((x, y))
594+
595+
angles = np.degrees(np.arctan2(mag_pts[:, 1], mag_pts[:, 0]))
596+
597+
center_angle = (angles.max() + angles.min()) / 2
598+
599+
full_half_span = (angles.max() - angles.min()) / 2
600+
center_half_span = full_half_span * (1 - 2 * margin)
601+
602+
center_mask = np.abs(angles - center_angle) < center_half_span
603+
magnets.append([mag_pts[center_mask]])
604+
605+
return magnets

‎scripts/3f_torque_optimization/run.py‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@
3636
geometry = Geometry()
3737
computation = Computation(geometry)
3838
design = load_design(file_name_aedt, project_name, design_name, aedt_version, geometry, computation)
39-
generators = [
40-
HacklGenerator_OneLambda(design, r_stator_end, offset=offset),
41-
HacklGenerator_SixLambdas(design, r_stator_end, offset=offset),
42-
HacklGenerator_3BrokenLines(design, r_stator_end, offset=offset),
43-
]
39+
generators = [HacklGenerator_OneLambda(design, r_stator_end, offset=offset), HacklGenerator_SixLambdas(design, r_stator_end, offset=offset), HacklGenerator_3BrokenLines(design, r_stator_end, offset=offset)]
4440

4541
metadata = pd.DataFrame()
4642
for i in range(0, n_designs):

0 commit comments

Comments
 (0)