From 24fdbc62e6c8718c09a026ae8acd0fe0012a3424 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sat, 3 Feb 2024 01:39:23 -0500 Subject: [PATCH 01/56] initial commit --- desc/objectives/_geometry.py | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 2d1cef6950..8da4284567 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -459,6 +459,74 @@ def compute(self, params, constants=None): return data["V"] +class CoilLength(_Objective): + """Coil length.""" + + def __init__( + self, + coil, + target=0, + bounds=None, + weight=1, + normalize=None, + normalize_target=None, + loss_function=None, + deriv_mode=None, + name="coil-length", + ): + super().__init__( + things=coil, + target=target, + bounds=bounds, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + loss_function=loss_function, + deriv_mode=deriv_mode, + name=name, + ) + + def build(self, use_jit=True, verbose=1): + """Bob be building.""" + coil = self.things[0] + self._data_keys = ["length"] + + timer = Timer() + if verbose > 0: + print("Precomputing transforms") + timer.start("Precomputing transforms") + + grid = LinearGrid() + profiles = get_profiles(self._data_keys, obj=coil, grid=grid) + transforms = get_transforms(self._data_keys, obj=coil, grid=grid) + self._constants = { + "transforms": transforms, + "profiles": profiles, + } + + timer.stop("Precomputing transforms") + if verbose > 1: + timer.disp("Precomputing transforms") + + super().build() + + def compute(self, params, constants=None): + """Compute length of coil.""" + if constants is None: + constants = self._constants + + data = compute_fun( + self.things[0], + self._data_keys, + params=params, + transforms=constants["transforms"], + profiles=constants["profiles"], + ) + + f = data["length"] + return f + + class PlasmaVesselDistance(_Objective): """Target the distance between the plasma and a surrounding surface. From 68301c058f2f1a809e65f1c6d7a04b845217b5ef Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sun, 4 Feb 2024 15:41:53 -0500 Subject: [PATCH 02/56] add simple test tests that coil length of radius 1 is 2pi --- tests/test_objective_funs.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index 43a94e7112..909629003f 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -12,6 +12,7 @@ import desc.examples from desc.backend import jnp +from desc.coils import FourierPlanarCoil from desc.compute import get_transforms from desc.equilibrium import Equilibrium from desc.examples import get @@ -21,6 +22,7 @@ AspectRatio, BootstrapRedlConsistency, BScaleLength, + CoilLength, CurrentDensity, Elongation, Energy, @@ -493,6 +495,15 @@ def test(eq): test(get("DSHAPE")) test(get("HELIOTRON")) + @pytest.mark.unit + def test_coil_length(self): + """Tests coil length.""" + coil = FourierPlanarCoil(r_n=1) + obj = CoilLength(coil) + obj.build() + f = obj.compute(params=coil.params_dict) + np.testing.assert_allclose(f, 2 * np.pi, rtol=1e-8) + @pytest.mark.unit def test_derivative_modes(): From 834b6d083c0a9e60a5ba5e40b45d9591dff59461 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sun, 4 Feb 2024 17:09:37 -0500 Subject: [PATCH 03/56] add default args plus todos --- desc/objectives/_geometry.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 8da4284567..71e450ffaa 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -465,13 +465,13 @@ class CoilLength(_Objective): def __init__( self, coil, - target=0, + target=2 * np.pi, bounds=None, weight=1, - normalize=None, - normalize_target=None, + normalize=True, + normalize_target=True, loss_function=None, - deriv_mode=None, + deriv_mode="auto", name="coil-length", ): super().__init__( @@ -487,8 +487,11 @@ def __init__( ) def build(self, use_jit=True, verbose=1): - """Bob be building.""" + """Build objective function.""" coil = self.things[0] + self._dim_f = 1 + # TODO: is this the correct data key? + # seems like it because it gives the circumference of a circular coil? self._data_keys = ["length"] timer = Timer() @@ -496,7 +499,9 @@ def build(self, use_jit=True, verbose=1): print("Precomputing transforms") timer.start("Precomputing transforms") - grid = LinearGrid() + grid = LinearGrid(L=4, M=4) + # TODO: what grid is supposed to be used here? + # TODO: am I supposed to use obj=coil? profiles = get_profiles(self._data_keys, obj=coil, grid=grid) transforms = get_transforms(self._data_keys, obj=coil, grid=grid) self._constants = { @@ -508,13 +513,15 @@ def build(self, use_jit=True, verbose=1): if verbose > 1: timer.disp("Precomputing transforms") - super().build() + super().build(use_jit=use_jit, verbose=verbose) def compute(self, params, constants=None): """Compute length of coil.""" if constants is None: constants = self._constants + # TODO: gets KeyError with 'rotmat' when computing 'x_s'. + # why does coil.compute("length") work and not this? data = compute_fun( self.things[0], self._data_keys, From 06bef534688d345b6c69459f915cb71f9c45c2c5 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 5 Feb 2024 12:19:02 -0500 Subject: [PATCH 04/56] add to objectives __init__ --- desc/objectives/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/desc/objectives/__init__.py b/desc/objectives/__init__.py index e8ecf0b9d0..2b08c89f09 100644 --- a/desc/objectives/__init__.py +++ b/desc/objectives/__init__.py @@ -13,6 +13,7 @@ from ._geometry import ( AspectRatio, BScaleLength, + CoilLength, Elongation, GoodCoordinates, MeanCurvature, From aea78c10313ea7569c22dcd222e02d8304907234 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 5 Feb 2024 12:28:37 -0500 Subject: [PATCH 05/56] merge PR#840 into ko/coil_length --- desc/coils.py | 24 ++-- desc/compute/_curve.py | 132 +++++++++++++----- desc/compute/geom_utils.py | 44 +++--- desc/examples/ARIES-CS_output.h5 | Bin 1095063 -> 1086839 bytes desc/examples/ATF_output.h5 | Bin 688194 -> 680978 bytes desc/examples/DSHAPE_CURRENT_output.h5 | Bin 317424 -> 315800 bytes desc/examples/DSHAPE_output.h5 | Bin 317360 -> 315800 bytes desc/examples/ESTELL_output.h5 | Bin 467717 -> 461541 bytes desc/examples/HELIOTRON_output.h5 | Bin 339940 -> 338380 bytes desc/examples/HSX_output.h5 | Bin 748044 -> 740948 bytes desc/examples/NCSX_output.h5 | Bin 1101078 -> 1092854 bytes desc/examples/SOLOVEV_output.h5 | Bin 215488 -> 214448 bytes desc/examples/W7-X_output.h5 | Bin 496069 -> 494509 bytes desc/examples/WISTELL-A_output.h5 | Bin 721884 -> 714668 bytes desc/examples/precise_QA_output.h5 | Bin 1097497 -> 1092849 bytes desc/examples/precise_QH_output.h5 | Bin 1097719 -> 1093071 bytes desc/geometry/core.py | 55 ++++++-- .../DSHAPE_output_saved_without_current.h5 | Bin 316544 -> 315016 bytes ...andremanPaul2022_QA_reactorScale_lowRes.h5 | Bin 594423 -> 591823 bytes ...andremanPaul2022_QH_reactorScale_lowRes.h5 | Bin 594427 -> 591827 bytes tests/inputs/circular_model_tokamak_output.h5 | Bin 218672 -> 217632 bytes tests/inputs/iotest_HELIOTRON.h5 | Bin 126574 -> 126054 bytes tests/test_coils.py | 10 +- tests/test_compute_utils.py | 15 ++ tests/test_curves.py | 55 +++++--- tests/test_plotting.py | 2 +- 26 files changed, 221 insertions(+), 116 deletions(-) diff --git a/desc/coils.py b/desc/coils.py index afef9a87fc..226ad5789b 100644 --- a/desc/coils.py +++ b/desc/coils.py @@ -817,17 +817,17 @@ def compute( return tree_unstack(data) - def translate(self, *args, **kwargs): + def translate(self, displacement): """Translate the coils along an axis.""" - [coil.translate(*args, **kwargs) for coil in self.coils] + [coil.translate(displacement) for coil in self.coils] - def rotate(self, *args, **kwargs): + def rotate(self, axis): """Rotate the coils about an axis.""" - [coil.rotate(*args, **kwargs) for coil in self.coils] + [coil.rotate(axis) for coil in self.coils] - def flip(self, *args, **kwargs): + def flip(self, normal): """Flip the coils across a plane.""" - [coil.flip(*args, **kwargs) for coil in self.coils] + [coil.flip(normal) for coil in self.coils] def compute_magnetic_field(self, coords, params=None, basis="rpz", grid=None): """Compute magnetic field at a set of points. @@ -883,21 +883,23 @@ def linspaced_angular( axis : array-like, shape(3,) axis to rotate about angle : float - total rotational extend of coil set. + total rotational extent of coil set n : int number of copies of original coil endpoint : bool whether to include a coil at final angle + """ assert isinstance(coil, _Coil) and not isinstance(coil, CoilSet) if current is None: current = coil.current currents = jnp.broadcast_to(current, (n,)) + axis = jnp.asarray(axis) + phi = jnp.linspace(0, angle, n, endpoint=endpoint) coils = [] - phis = jnp.linspace(0, angle, n, endpoint=endpoint) for i in range(n): coili = coil.copy() - coili.rotate(axis, angle=phis[i]) + coili.rotate(axis / jnp.linalg.norm(axis) * phi[i]) coili.current = currents[i] coils.append(coili) return cls(*coils) @@ -920,6 +922,7 @@ def linspaced_linear( number of copies of original coil endpoint : bool whether to include a coil at final point + """ assert isinstance(coil, _Coil) and not isinstance(coil, CoilSet) if current is None: @@ -953,6 +956,7 @@ def from_symmetry(cls, coils, NFP, sym=False): number of field periods sym : bool whether coils should be stellarator symmetric + """ if not isinstance(coils, CoilSet): coils = CoilSet(coils) @@ -976,7 +980,7 @@ def from_symmetry(cls, coils, NFP, sym=False): coils = coils + flipped_coils for k in range(0, NFP): coil = coils.copy() - coil.rotate(axis=[0, 0, 1], angle=2 * jnp.pi * k / NFP) + coil.rotate([0, 0, 2 * jnp.pi * k / NFP]) coilset.append(coil) return cls(*coilset) diff --git a/desc/compute/_curve.py b/desc/compute/_curve.py index 2fefa33143..4dd1aaf310 100644 --- a/desc/compute/_curve.py +++ b/desc/compute/_curve.py @@ -1,15 +1,9 @@ from interpax import interp1d -from desc.backend import jnp +from desc.backend import cond, jnp from .data_index import register_compute_fun -from .geom_utils import ( - _rotation_matrix_from_normal, - rpz2xyz, - rpz2xyz_vec, - xyz2rpz, - xyz2rpz_vec, -) +from .geom_utils import rotation_matrix, rpz2xyz, rpz2xyz_vec, xyz2rpz, xyz2rpz_vec from .utils import cross, dot @@ -184,16 +178,26 @@ def _Z_Curve(params, transforms, profiles, data, **kwargs): basis="basis", ) def _x_FourierPlanarCurve(params, transforms, profiles, data, **kwargs): - # create planar curve at z==0 + # create planar curve at Z==0 r = transforms["r"].transform(params["r_n"], dz=0) Z = jnp.zeros_like(r) X = r * jnp.cos(data["s"]) Y = r * jnp.sin(data["s"]) coords = jnp.array([X, Y, Z]).T # rotate into place - R = _rotation_matrix_from_normal(params["normal"]) - coords = jnp.matmul(coords, R.T) + params["center"] - coords = jnp.matmul(coords, params["rotmat"].T) + params["shift"] + Zaxis = jnp.array([0.0, 0.0, 1.0]) # 2D curve in X-Y plane has normal = +Z axis + norm = jnp.linalg.norm(params["normal"]) + angle = jnp.arccos(dot(Zaxis, params["normal"]) / norm) + eps = 1e2 * jnp.finfo(angle.dtype).eps + axis = cond( + jnp.all(jnp.abs(angle) < eps), + lambda _: jnp.zeros_like(Zaxis), + lambda _: cross(Zaxis, params["normal"]) / (norm * jnp.sin(angle)) * angle, + None, + ) + A = rotation_matrix(axis=axis) + coords = jnp.matmul(coords, A.T) + params["center"] + coords = jnp.matmul(coords, params["rotmat"].reshape((3, 3)).T) + params["shift"] if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz(coords) data["x"] = coords @@ -224,16 +228,29 @@ def _x_s_FourierPlanarCurve(params, transforms, profiles, data, **kwargs): dY = dr * jnp.sin(data["s"]) + r * jnp.cos(data["s"]) dZ = jnp.zeros_like(dX) coords = jnp.array([dX, dY, dZ]).T - A = _rotation_matrix_from_normal(params["normal"]) + # rotate into place + Zaxis = jnp.array([0.0, 0.0, 1.0]) # 2D curve in X-Y plane has normal = +Z axis + norm = jnp.linalg.norm(params["normal"]) + angle = jnp.arccos(dot(Zaxis, params["normal"]) / norm) + eps = 1e2 * jnp.finfo(angle.dtype).eps + axis = cond( + jnp.all(jnp.abs(angle) < eps), + lambda _: jnp.zeros_like(Zaxis), + lambda _: cross(Zaxis, params["normal"]) / (norm * jnp.sin(angle)) * angle, + None, + ) + A = rotation_matrix(axis=axis) coords = jnp.matmul(coords, A.T) - coords = jnp.matmul(coords, params["rotmat"].T) + coords = jnp.matmul(coords, params["rotmat"].reshape((3, 3)).T) if kwargs.get("basis", "rpz").lower() == "rpz": X = r * jnp.cos(data["s"]) Y = r * jnp.sin(data["s"]) Z = jnp.zeros_like(X) xyzcoords = jnp.array([X, Y, Z]).T xyzcoords = jnp.matmul(xyzcoords, A.T) + params["center"] - xyzcoords = jnp.matmul(xyzcoords, params["rotmat"].T) + params["shift"] + xyzcoords = ( + jnp.matmul(xyzcoords, params["rotmat"].reshape((3, 3)).T) + params["shift"] + ) x, y, z = xyzcoords.T coords = xyz2rpz_vec(coords, x=x, y=y) data["x_s"] = coords @@ -269,16 +286,29 @@ def _x_ss_FourierPlanarCurve(params, transforms, profiles, data, **kwargs): ) d2Z = jnp.zeros_like(d2X) coords = jnp.array([d2X, d2Y, d2Z]).T - A = _rotation_matrix_from_normal(params["normal"]) + # rotate into place + Zaxis = jnp.array([0.0, 0.0, 1.0]) # 2D curve in X-Y plane has normal = +Z axis + norm = jnp.linalg.norm(params["normal"]) + angle = jnp.arccos(dot(Zaxis, params["normal"]) / norm) + eps = 1e2 * jnp.finfo(angle.dtype).eps + axis = cond( + jnp.all(jnp.abs(angle) < eps), + lambda _: jnp.zeros_like(Zaxis), + lambda _: cross(Zaxis, params["normal"]) / (norm * jnp.sin(angle)) * angle, + None, + ) + A = rotation_matrix(axis=axis) coords = jnp.matmul(coords, A.T) - coords = jnp.matmul(coords, params["rotmat"].T) + coords = jnp.matmul(coords, params["rotmat"].reshape((3, 3)).T) if kwargs.get("basis", "rpz").lower() == "rpz": X = r * jnp.cos(data["s"]) Y = r * jnp.sin(data["s"]) Z = jnp.zeros_like(X) xyzcoords = jnp.array([X, Y, Z]).T xyzcoords = jnp.matmul(xyzcoords, A.T) + params["center"] - xyzcoords = jnp.matmul(xyzcoords, params["rotmat"].T) + params["shift"] + xyzcoords = ( + jnp.matmul(xyzcoords, params["rotmat"].reshape((3, 3)).T) + params["shift"] + ) x, y, z = xyzcoords.T coords = xyz2rpz_vec(coords, x=x, y=y) data["x_ss"] = coords @@ -321,16 +351,29 @@ def _x_sss_FourierPlanarCurve(params, transforms, profiles, data, **kwargs): ) d3Z = jnp.zeros_like(d3X) coords = jnp.array([d3X, d3Y, d3Z]).T - A = _rotation_matrix_from_normal(params["normal"]) + # rotate into place + Zaxis = jnp.array([0.0, 0.0, 1.0]) # 2D curve in X-Y plane has normal = +Z axis + norm = jnp.linalg.norm(params["normal"]) + angle = jnp.arccos(dot(Zaxis, params["normal"]) / norm) + eps = 1e2 * jnp.finfo(angle.dtype).eps + axis = cond( + jnp.all(jnp.abs(angle) < eps), + lambda _: jnp.zeros_like(Zaxis), + lambda _: cross(Zaxis, params["normal"]) / (norm * jnp.sin(angle)) * angle, + None, + ) + A = rotation_matrix(axis=axis) coords = jnp.matmul(coords, A.T) - coords = jnp.matmul(coords, params["rotmat"].T) + coords = jnp.matmul(coords, params["rotmat"].reshape((3, 3)).T) if kwargs.get("basis", "rpz").lower() == "rpz": X = r * jnp.cos(data["s"]) Y = r * jnp.sin(data["s"]) Z = jnp.zeros_like(X) xyzcoords = jnp.array([X, Y, Z]).T xyzcoords = jnp.matmul(xyzcoords, A.T) + params["center"] - xyzcoords = jnp.matmul(xyzcoords, params["rotmat"].T) + params["shift"] + xyzcoords = ( + jnp.matmul(xyzcoords, params["rotmat"].reshape((3, 3)).T) + params["shift"] + ) x, y, z = xyzcoords.T coords = xyz2rpz_vec(coords, x=x, y=y) data["x_sss"] = coords @@ -363,7 +406,9 @@ def _x_FourierRZCurve(params, transforms, profiles, data, **kwargs): coords = jnp.stack([R, phi, Z], axis=1) # convert to xyz for displacement and rotation coords = rpz2xyz(coords) - coords = coords @ params["rotmat"].T + params["shift"][jnp.newaxis, :] + coords = ( + coords @ params["rotmat"].reshape((3, 3)).T + params["shift"][jnp.newaxis, :] + ) if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz(coords) data["x"] = coords @@ -397,7 +442,7 @@ def _x_s_FourierRZCurve(params, transforms, profiles, data, **kwargs): coords = jnp.stack([dR, dphi, dZ], axis=1) # convert to xyz for displacement and rotation coords = rpz2xyz_vec(coords, phi=transforms["grid"].nodes[:, 2]) - coords = coords @ params["rotmat"].T + coords = coords @ params["rotmat"].reshape((3, 3)).T if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz_vec(coords, phi=transforms["grid"].nodes[:, 2]) data["x_s"] = coords @@ -435,7 +480,7 @@ def _x_ss_FourierRZCurve(params, transforms, profiles, data, **kwargs): coords = jnp.stack([R, phi, Z], axis=1) # convert to xyz for displacement and rotation coords = rpz2xyz_vec(coords, phi=transforms["grid"].nodes[:, 2]) - coords = coords @ params["rotmat"].T + coords = coords @ params["rotmat"].reshape((3, 3)).T if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz_vec(coords, phi=transforms["grid"].nodes[:, 2]) data["x_ss"] = coords @@ -473,7 +518,7 @@ def _x_sss_FourierRZCurve(params, transforms, profiles, data, **kwargs): coords = jnp.stack([R, phi, Z], axis=1) # convert to xyz for displacement and rotation coords = rpz2xyz_vec(coords, phi=transforms["grid"].nodes[:, 2]) - coords = coords @ params["rotmat"].T + coords = coords @ params["rotmat"].reshape((3, 3)).T if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz_vec(coords, phi=transforms["grid"].nodes[:, 2]) data["x_sss"] = coords @@ -504,7 +549,9 @@ def _x_FourierXYZCurve(params, transforms, profiles, data, **kwargs): Y = transforms["Y"].transform(params["Y_n"], dz=0) Z = transforms["Z"].transform(params["Z_n"], dz=0) coords = jnp.stack([X, Y, Z], axis=1) - coords = coords @ params["rotmat"].T + params["shift"][jnp.newaxis, :] + coords = ( + coords @ params["rotmat"].reshape((3, 3)).T + params["shift"][jnp.newaxis, :] + ) if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz(coords) data["x"] = coords @@ -535,7 +582,7 @@ def _x_s_FourierXYZCurve(params, transforms, profiles, data, **kwargs): dY = transforms["Y"].transform(params["Y_n"], dz=1) dZ = transforms["Z"].transform(params["Z_n"], dz=1) coords = jnp.stack([dX, dY, dZ], axis=1) - coords = coords @ params["rotmat"].T + coords = coords @ params["rotmat"].reshape((3, 3)).T if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz_vec( coords, @@ -570,7 +617,7 @@ def _x_ss_FourierXYZCurve(params, transforms, profiles, data, **kwargs): d2Y = transforms["Y"].transform(params["Y_n"], dz=2) d2Z = transforms["Z"].transform(params["Z_n"], dz=2) coords = jnp.stack([d2X, d2Y, d2Z], axis=1) - coords = coords @ params["rotmat"].T + coords = coords @ params["rotmat"].reshape((3, 3)).T if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz_vec( coords, @@ -605,7 +652,7 @@ def _x_sss_FourierXYZCurve(params, transforms, profiles, data, **kwargs): d3Y = transforms["Y"].transform(params["Y_n"], dz=3) d3Z = transforms["Z"].transform(params["Z_n"], dz=3) coords = jnp.stack([d3X, d3Y, d3Z], axis=1) - coords = coords @ params["rotmat"].T + coords = coords @ params["rotmat"].reshape((3, 3)).T if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz_vec( coords, @@ -662,7 +709,9 @@ def _x_SplineXYZCurve(params, transforms, profiles, data, **kwargs): ) coords = jnp.stack([Xq, Yq, Zq], axis=1) - coords = coords @ params["rotmat"].T + params["shift"][jnp.newaxis, :] + coords = ( + coords @ params["rotmat"].reshape((3, 3)).T + params["shift"][jnp.newaxis, :] + ) if kwargs.get("basis", "rpz").lower() == "rpz": coords = xyz2rpz(coords) data["x"] = coords @@ -715,7 +764,7 @@ def _x_s_SplineXYZCurve(params, transforms, profiles, data, **kwargs): ) coords_s = jnp.stack([dXq, dYq, dZq], axis=1) - coords_s = coords_s @ params["rotmat"].T + coords_s = coords_s @ params["rotmat"].reshape((3, 3)).T if kwargs.get("basis", "rpz").lower() == "rpz": # calculate the xy coordinates to rotate to rpz @@ -745,7 +794,10 @@ def _x_s_SplineXYZCurve(params, transforms, profiles, data, **kwargs): ) coords = jnp.stack([Xq, Yq, Zq], axis=1) - coords = coords @ params["rotmat"].T + params["shift"][jnp.newaxis, :] + coords = ( + coords @ params["rotmat"].reshape((3, 3)).T + + params["shift"][jnp.newaxis, :] + ) coords_s = xyz2rpz_vec(coords_s, x=coords[:, 0], y=coords[:, 1]) data["x_s"] = coords_s @@ -798,7 +850,7 @@ def _x_ss_SplineXYZCurve(params, transforms, profiles, data, **kwargs): ) coords_ss = jnp.stack([d2Xq, d2Yq, d2Zq], axis=1) - coords_ss = coords_ss @ params["rotmat"].T + coords_ss = coords_ss @ params["rotmat"].reshape((3, 3)).T if kwargs.get("basis", "rpz").lower() == "rpz": # calculate the xy coordinates to rotate to rpz @@ -827,7 +879,10 @@ def _x_ss_SplineXYZCurve(params, transforms, profiles, data, **kwargs): period=2 * jnp.pi, ) coords = jnp.stack([Xq, Yq, Zq], axis=1) - coords = coords @ params["rotmat"].T + params["shift"][jnp.newaxis, :] + coords = ( + coords @ params["rotmat"].reshape((3, 3)).T + + params["shift"][jnp.newaxis, :] + ) coords_ss = xyz2rpz_vec(coords_ss, x=coords[:, 0], y=coords[:, 1]) data["x_ss"] = coords_ss @@ -880,7 +935,7 @@ def _x_sss_SplineXYZCurve(params, transforms, profiles, data, **kwargs): ) coords_sss = jnp.stack([d3Xq, d3Yq, d3Zq], axis=1) - coords_sss = coords_sss @ params["rotmat"].T + coords_sss = coords_sss @ params["rotmat"].reshape((3, 3)).T if kwargs.get("basis", "rpz").lower() == "rpz": # calculate the xy coordinates to rotate to rpz @@ -909,7 +964,10 @@ def _x_sss_SplineXYZCurve(params, transforms, profiles, data, **kwargs): period=2 * jnp.pi, ) coords = jnp.stack([Xq, Yq, Zq], axis=1) - coords = coords @ params["rotmat"].T + params["shift"][jnp.newaxis, :] + coords = ( + coords @ params["rotmat"].reshape((3, 3)).T + + params["shift"][jnp.newaxis, :] + ) coords_sss = xyz2rpz_vec(coords_sss, x=coords[:, 0], y=coords[:, 1]) data["x_sss"] = coords_sss @@ -976,7 +1034,7 @@ def _frenet_normal(params, transforms, profiles, data, **kwargs): def _frenet_binormal(params, transforms, profiles, data, **kwargs): data["frenet_binormal"] = cross( data["frenet_tangent"], data["frenet_normal"] - ) * jnp.linalg.det(params["rotmat"]) + ) * jnp.linalg.det(params["rotmat"].reshape((3, 3))) return data diff --git a/desc/compute/geom_utils.py b/desc/compute/geom_utils.py index 62806f0184..f4da5bdf18 100644 --- a/desc/compute/geom_utils.py +++ b/desc/compute/geom_utils.py @@ -2,7 +2,7 @@ import functools -from desc.backend import jnp +from desc.backend import cond, jnp def reflection_matrix(normal): @@ -23,29 +23,33 @@ def reflection_matrix(normal): return R -def rotation_matrix(axis, angle=None): +def rotation_matrix(axis): """Matrix to rotate points about axis by given angle. Parameters ---------- axis : array-like, shape(3,) - Axis of rotation, in cartesian (X,Y,Z) coordinates - angle : float or None - Angle to rotate by, in radians. If None, use norm of axis vector. + Axis of rotation, in cartesian (X,Y,Z) coordinates. + The norm of the vector is the angle of rotation, in radians. Returns ------- rot : ndarray, shape(3,3) - Matrix to rotate points in cartesian (X,Y,Z) coordinates + Matrix to rotate points in cartesian (X,Y,Z) coordinates. + """ axis = jnp.asarray(axis) - if angle is None: - angle = jnp.linalg.norm(axis) - axis = axis / jnp.linalg.norm(axis) - R1 = jnp.cos(angle) * jnp.eye(3) - R2 = jnp.sin(angle) * jnp.cross(axis, jnp.identity(axis.shape[0]) * -1) - R3 = (1 - jnp.cos(angle)) * jnp.outer(axis, axis) - return R1 + R2 + R3 + eps = 1e2 * jnp.finfo(axis.dtype).eps + return cond( + jnp.all(jnp.abs(axis) < eps), + lambda axis: jnp.eye(3), + lambda axis: jnp.cos(jnp.linalg.norm(axis)) * jnp.eye(3) # R1 + + jnp.sin(jnp.linalg.norm(axis)) # R2 + * jnp.cross(axis / jnp.linalg.norm(axis), jnp.identity(axis.shape[0]) * -1) + + (1 - jnp.cos(jnp.linalg.norm(axis))) # R3 + * jnp.outer(axis / jnp.linalg.norm(axis), axis / jnp.linalg.norm(axis)), + axis, + ) def xyz2rpz(pts): @@ -152,17 +156,3 @@ def inner(vec, phi): return cart return inner(vec, phi) - - -def _rotation_matrix_from_normal(normal): - nx, ny, nz = normal - nxny = jnp.sqrt(nx**2 + ny**2) - R = jnp.array( - [ - [ny / nxny, -nx / nxny, 0], - [nx * nx / nxny, ny * nz / nxny, -nxny], - [nx, ny, nz], - ] - ).T - R = jnp.where(nxny == 0, jnp.eye(3), R) - return R diff --git a/desc/examples/ARIES-CS_output.h5 b/desc/examples/ARIES-CS_output.h5 index 43751708eb2c17e4bbb344f6f5939b725f2f50e9..cfdfa3ca87580d16985199271c5aa63030ed0494 100644 GIT binary patch delta 46196 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 QKs>$z;PDl50M~c_A1^w+!~g&Q delta 45967 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 diff --git a/desc/examples/ATF_output.h5 b/desc/examples/ATF_output.h5 index 4c7c2200491259caa1c631325e3f8b0ad6422084..3b81e4ddc7c69b8d9980b621fab71d0df0953c0e 100644 GIT binary patch delta 36447 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 KIUYrM{Qm%-9P`Ei diff --git a/desc/examples/DSHAPE_CURRENT_output.h5 b/desc/examples/DSHAPE_CURRENT_output.h5 index 72a4d9544fd6bf8ea1c28092020453945c77657b..5c0968ee218a88cd5fe49d78699cd119e303defa 100644 GIT binary patch delta 14160 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 diff --git a/desc/examples/DSHAPE_output.h5 b/desc/examples/DSHAPE_output.h5 index 9bb9e2e2c9b8161ec1b0398231e4a4c4822b55d2..90663aef656fcf9d90158e4f8189822a4f54100b 100644 GIT binary patch delta 14130 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 delta 21286 zcmb7M4P2E)*1ylpy_X9LUO*Hz6+b`(!Hbrmu5j@y+EP)=FjHwu!wSW$)_qN*EsfUI z1#dEzVOVLT;$kF^SrVF(n!040*@uRT!To2<2J?>T2?F3+Xi_eFj`pL@_bqiz>(hpuB7Czw%I6Nw?%#&Y)NY8+2=44F3(jhzAfX`- zwYlyHUwBAoUu8&68|zMLcUzn5?m}``wri{0-P!R;c#BFWNIX$xN@=is0S9NT=UrzUJYv0*d)Ul14@tx>+Ut7hFYP;CE zzFuLSS9NNu5aT*M-9{nKbsA-KtqPBRmfzj5$&e#ph5U_|oUPz9$*Z@v+6+!_U>v1i zkIGlLOY%D3jARGBDGBj?->o^aJVU-{QrUA~HnwfvW%y$F-Bl4ilMN6fBW9ZsiUX$G zB{K#4*7uI}_3fQ+boH(7y)NKgIJ}$d+uEluy!*J%OYqF^`wToU^<4$e!q_|Dc_Q{f zcxJ}UgXfO8VetHCTp>K4=!dk~?*OZOT}+JUJ$z;H*{rr)((cz>RWbb=`?LBt)XOh} zt5PQad0e})2-%0)j)us1mCt15CBEC&A8Hrp$xzu2?sj%*SDqzP_fJyUvLBKO3-eUA2fw%$&5tJ==BY;e zIYW4!X3@MUT;(obq>3+A2^PhYFusI;)iGO|>g?s{u9j{Q?p}$$t-gcIlLL7yveVm4 zm8Zc?d8#Tef7`Ak*&Y@yjl*X1|N1UYdoXy7Lkh)3O1Sf3k-v(0ifdhBzr%1FysFoS zMu_m_*uzBai|p6tpfKaC(P2UEoY{{;E04^3H1FO=ZQ|#bSaD=#Cs~hIHGeq)Kqr-@U&;CQ3@WrixqX2Rb)M21RFQV^vX|N ztg*jLFN1WE70-pQGAkz1lM>jA^5MD{$5T}=c8rrkAD^Ilk^O*7SU87?^))<7RKCqz zqG5oM>C1V|#hQL48*Lio5}qU@QOqfj*2Q8xvPh0SSlo*RJP*2z{bC{%@Ikkrnx~SCo+4`r3pR#g))HUC;7R7N z;l_D`+eVXNMlqfRcD=2}o+m2Pjd)i73RNoV4>1?3T`!p!ui;^38zqzPHKMy%teuqX zl33ZY#r#rev({_4SmtUffXWdgGoX)6BV`Q@=K=pQlFk}wM0d3~6V;|iwd}Dnljn0N za0}8Ur0N$(8B%PWp!wDO%F)A0MZ*~BVA&8a}5uuW}a5f8W>AJHPa=84y642$v}cW8RU7d3PxY zn-0qacfE%2354(|5u;sF>c|C2C6zcSy_lZ+>2C#2b8JiBT20A=Kc7npjg}m{()@0*=t~9guu@io@Xh zOAPVRCKv^3U*nf~eE@e{b7lP;b)yQmO1N`KcQ#w4n$GpKZF^h3S5vO7uwFu~=jqHL zoaSk7o~vZD;byhNgl7-$(XQ|-nXmkm(oyP9G9mkl)Ddea5r;Md2o2Q|%dB`;LPJN{ znta-9mcg3jD^M?r(tH_qoR+~24$I=C7E{tq@h%v^Pu<3Q`e3$ZnL9CgEJ4(yA+iY# z(N6mjUzuYNpsvu37KSf$!xzF^WejFITaL$ZnR^sx77TKf89Y_L1ekyH=LBY>uV9a> z#02lKO!d@*e1-Fxy-L(UE@rDzUAXLG3oDSW!SRx3>gv56>~$jlZDT=j?z<9!6dHNN z!ktDm%M#|Qokp=MxO|^{nIK>8c;ARNYRvG%#?L{bXpi9wZafl@;qoEK&`zXWF*;Z6 z&vdyWU0=%gUM1v?ChdEbUrXN$DGZS_cX@lMXUlR=?7qPP^UkM+%ZL#v6)<_=tgY^_!|dc?{At|Wk4r@5xrd7aqNU@QvG`I|!B zgyqSNhR8o{!~}0#D-&&EcLMZ!6zQxDhXO};h62frIvQ9`E2a~eX zl18g7zaVKGOxB!7x`dVi<(C4rksZR+VkYaV8VO6*OKnufD=%~Il?icmoE9^DItYTO zfw}*nP;>t%+e6f1#5NbB1&!z5m`bxe&x3BzII{R9D{4yvxD^51YRwU9**^hlwrf)U z1cjRW3ZPc{l1A;`pEPO%OBHJAr4qF@KS&7?wWelCBWm6&k_OaxW-u$>*4+o7@l+R( zt3}y(qgO-+Im&1YzT8J+*3?Nt6teM*gsATC(t^hZ*~#)|2iqlN6F>@t43>bQj0}xe zMley_&mL>kiiy1mFJ9jf8r?;fITPe3xaO5w;reX=_hbO~tmazaD(fso4}j}WrN4x$ z*E$7P$~uCJr-l&}fEF)uBPnp5$)YF~v1;s1skq8yy?|6$C8!ZuIi7bkV1+vdUqM+k z_f*SGjImIh3bt?esR5qP=;xrQ7^|)T&+b`VI{`6Z8SBI!`?0+M(E4}<(9JEPq>?D$ z`o?9kdnAAg!*XIlvh17cq0O?UC5f_>Tuno%G%RBi%Ucz(ta>SPwkqNoMMZD~C>y|h zi+L4H!7n7Rb?}p#$W}RM8!0m0;E`hg^Ds0$xfTY(tBDM@f+BAKyCYyPIkR|MH_Q>@ z3yX^qt|SJVN|BUAnM;${aNs^mVyoe2UNRd_MGM{1$N{>(q0oGLptU+sHk}>{w3QDN=LWKY z5&CrpcrB|D=RGWWs7?=ubgQ@#!1sWE`hBFooJ6amn9*$S=Pw@Bs(7bX#XGku9! z)wOqNRU9QuhJX5fw^qe(03QGJ`|jf98b`djB8AN|c;x^leY|!6vn;Ll2XOTP+;Pp( z^zBMz3z%)VSe(i>vxWg?0$8<+6^;?RGT3q>Rm?}X$UzrQMB6P%f@u_&hmhK^-NjwM zCyRzzk5}FVBOl)}SvLl|x>FNC1eh!M@jEdUrgGjm1t>Z8rGo%8_Dx8#;&N92H#2~H zKyx(qxu>v2Y{yh{#S}KpVDpm9Z|`E+A^a7nmzKJ3f=n=z8d)Oaeu)rlO^Z<`m=XBp zyGT+TZhfN6F(TJz!FxcVd!H7DK5(^-aG>5qO3Laa; zvKN?1`2Y)N$*gMHZ|@4>Hv&Phj810a>NyZ;4z-TEGLeEJ)z`6LHebql0YqAYBu$e= zi(!cxV&CPfPa4GUfbz6MaQC-2@q07}dx9;#lQmr;WlrrYchVG8L)341Tf{$UK^%A62liXOhi_pJnL=OIuM*U;#}vxP z&dY@6CWZ3bfHJyn!Ryq294Cs(<)Vd`y@6@yxRslLk_LJJI!=!JnbtiS=&1nin*i>j z=E!mDUkAr6s4&NsqlNwm?pJSNF4df>xe}379JoHN50(s)CE)2_qTiaGpdQ#Yj@fW{zu(lOryPqHykC0hA^B5 zq9z!IGwQ`t$03APpAON8%AvS#lfk&MRH7jk=Spat6zGSRa27d2zFIyMjG!_QvU4S5 zsv`sS%4P`#P#GP@c{VR#*`JETN5L}p4mAJgW7fkJ>G?X)3LE~Vt?>A_ z$_huUlvY?BVizBM&3lBDOaAaChm0t{GMZOIM8Itnij*I~_|e$boK*%@{&$oOy>mw+ zP~@HB%`yBq=}_z$%cCibBaYs}qe~k3kLL1Q`C&seJPo_IZxaX@ZKjUi(FtNc^pUe8mFu}E+Ckn(5Ci(LnPW%eKV0@Q?R6; z-<$WOh_Nw+Y7kj-j2PZhti+>L7bdIpDl2Fh#N`H>9goGm zU(b+(Qc-z_)ZO~SQg>`kFEUP86tVK&NP_m*jy_V#mr2Su zsi4U3&!gLX63g&l*xuo%%e z@H(t#FrHJ3EDvwvNwp~T@X5a8Veu)$qlEWos$mM%a2I)|j>fcAhN-GW{c|_@)wJuB;~s(oE@r?eI)iGM zd7U-nP*3i~8hq|37y0LX*ICE?RKtVUS;IrrgNLaH1)W^v zLUXUPj(O;;Mn1Zb@TPKCo9Bo}(0@chD)+34cKKXJ2LrgH0o>=BqkV>4 z;>2;a#5w)TC63F42xSLjb-cYA*ETFBl%{7moi;iUg|XxPeTjs%4XaNf64o}X)&rzo zT8LBwYJfF{C=9Z{E7E%N5LR?E=A{!l9{(dQXG9Sk&?UN;1g?WEN05Ybwq6JKqcROp zCczaISjKYmfXe8lPeUb!x#`ryYzVeD8lrX=A4*|ZxD{bGtT!UKQD|Nau?HD^O^Sjw zrw-1bG?V{D|<2R2sFCJ)TH7yW?pCCvcb2V{=3lHL$_b1dK+M?1U)0g%9>)fhTu({*hqsEYIx>mkCS<1LD5$ zy9>|id}V`N3AsN6>c?`JLLlTTD3TtQlEV2(uDIxhQ$`3ku!d1m(wFh<&VVz-1p9j; zCI>fBwNtHi6>L(5!<;^QKIT{Ri(8Psn4);VcD6oqZIh(F@tEY%x0JC46s7q z9S19ATplmmM6JW}xMZ^|MayIE86@}1hF7T~Q~hSC-)xCrQxf1;UCtMU%-$+{nP61y zI?&4y_iW;W*t#8}XcLcP`R|xJHu3x2U3Bagqs%z%XuhXd(PZx~CcVRVrRuaqcyA5H z4*(vQqxyXk(AGM9rzUmuwU2d)vL<_o$a|MB;yKU_3diLG3V7ak?KMIH;<#+e>qO$% zvvCWNXysdX6NzKbx>_JSvTR#ZY;*7uP&B~O&$b+g`?NM5rZE2WO#%~>QG9fnJ zDYkWUHWh*}R`f z)aBSCM4~Roe@!InGVL^wT9*;yzX{akHHX>I$L?gTxk;*W->p(}XKs_K|F&a4`gA~$|V4WJ^c|3M@wvhEy`sK{gIWvfz=O-hm62DcVuZ1)?O1vC@$t%adj zU$lB<79HRjM##pC|3R%M|1H(}r)%crb9}Gcf0Q*4P_(KQ=Mg>d1Brj$7xY1HEv@eK|3Bn3QTW@UTa}yAfzrRu~uTll`_e1YTH$xEXk>_EUk6fq%N9 zp}zT!dwI-Y<+Yx{(reQi3EUjebji0wqNdMYAQCjq&)e-dV}YjmNe(0hC?m$Vms6x( z=Z@`RHZkv6Vf=J6-I{d0IOu9z-`wLXo6M?Zz-l#gnhFdx*rCS#<{>SFaipElRrCqj}L_ zW@cS;bazF}PL!JPsy=t}N{8cNgH^yhX_fT2anGy4^QJ$`ggdu6SAS&BW$$r{pVuVm z*4U9`QPj^K$4?H#B-F`y4^T3Ep86h_c!(R7v(;|^|9X#0&uiFdVF-_G{fZW~VafJI ztY)yeJlPJXUTeQH8wc9q)N9BoF6Wc6r#QuarzDU#V3~aD03y*KuB8x(mg!6<5-qcN z1d!4)onr#=sGh3u*m}Fd<8ZC){<%F0kHufegwhj`FcJU(3GhH+#x2wUjzM!XB_K;~ z#Uun|<*$K~fXoKbDFXiZDTM)bdr1JdEPz|7If7*CEda@kab{vBK*EO`b^*GLGS}T| zhx3FI4a`ObO!Fub3xOFwmPiC<-EBl7FdMUpL|{tpvbP+3RqICS;HyS+`rs>~E=NMA z*TFr?rBlmuFXz)5zke(It~!gi-Gqx%UI(J#LD!Pqy5+>$Ie`vmIawr2)%IGwkcyyN zXDo1cf1y?^Ut8?Oo zv`1m@;;SDR|DSGdKPLV%+0ju{FSbWJUVRT5^BJ*`RZZ0{SH#Lc1)6f&Ys!>|2)!=5782|iHciyd5IdWpHbmS!~Ni?+AV?{)wy~dRgiT0ZODv@ZfJzfD)I`ZS2 z0(&3#UiC725Mysbj_JOvZf+j-8!ShIW{>-*3cT+V<#(Q_VZgY3DLUtXP_inw?SXXv!~T6w7hS?Xs;Op>!AQ{f#S@pP4)~U_{f{e z4Aez1XgJ2Ps8*@j*L2!Nx91p~UBOIwOl19y!;B$kO+2fZ{RRn&*wt(%60r;0MkHd_ zsfI|z&bf}F)}%Or2ViW;2k6*vE3DHVyIa)M*mGFwSe)_c0E~~1 zgOkL5_8$6hDotd-o=M;#SmK&iZf&_iKz5)H!FnX%j!WPnSYqR=fqrc{!KE^~onyVI z3?72Re$NoZqJP5?zV#zu1!WN;e%4lXq&Z!X^xz}+9fJ*>GFkBnT9UDmV@j-eO;lVq_3;0@nb)bYusvGO%H=oco|GD zbcX}|-&WJVf_K)kBLeTJrNL{Q+jLdwKqH!(_qO6QTJa-W@uOPtqg(M~B;RsJZLBzP z!QOI5?G|`%6&EK!z0tsNSIc!P&{qD}bVtpTfnXG*s z54?4+I-yl1S#;yfzZsp_s`#W<#V3oc&GrY`SyG^OvTMr8ntxDE7I`E@oShT6+@Jt% znCAR$#^WL#Di}NR(l7ihm*hGoD)+!>qGTjL3nm$J(9hZvNq!biB>7nnBFWFXJ8;2v zffcIC@bsFkk+q2&_%?zk_r=^YE?ajFokYfE3tBCoEZ30;OC6V87EEPiTsAClXtvx4 zrZRf)%)AAr2iWAx8?WU`wycx7qsK(fSDJpyAE{-H8uZHwN<748%}N1o+@ouTBDE`1 zr22EHahq*=k>qiQjepKxN!}${+u?0;DcYp?6`g~0q?WmxX{?}7Ek8Sy5XMmL{`Tl9 zEsiv<aAB9@ZY3fwc#HY60Qf;Dl3wTC8Zf zg+X1)r>D*Kaklah=MdQ;SV1rG0J)1kx>%^&ExXwPtB|?r;)_(rX14AYvv8=Rt_%Bm zs>qrQ36Z}suTFM6(S?OQiU~duHrEl&(w=x7Qe9^JVwwWH z^)B;Wz*{Ft9m&y?<5UywGN&5;+3@aG`KM9P)jtp31Fx-Xfa$4XL2SlPtoT&p+n#85U zfhMuxTWu1hpFk#Q5^q~fVq85{gC;TkBV`hKM==deV)4g7Ns}14*L>;#u2v6N)y^jE zHP^Ryh8gT`P5frB_^5;POF?1tamW6H87ys3W-#PyGJ_jCn1{Y{{F|`{p^bkyptAqj zD=v0-?qi2YI0RwDUx-8yR{WJn1Yz|zL?Q@F8-Y|HG|5J_L7Zbw%SRqVhB{=BGB?AV z-{`0|*dO+aj9}+Z-`rP2c-|$fk^0_DbyJJ~A`*j<8*YKvM|b*`V{BdEd}2AP0A;rX zBTFw+X$(frZB@i_nn6Xh#3u}D1vf!2YD_jM1+bkO={=e_sEMP5{So=$xYKQ4z$<=q zh$r0kP5kq{4zqu_y~xFHvOC4QUF?70XD&#=5Z>>Rq#gCRe^M%jA-89e7-SqjK&n3s z$xfd!CtP#DRl!iVoQS<7zsw7HHNnaQ7r5!O1 z0bd;jlr*+~?~}&H+SMuZiqu3mP4uZV!Ore7_^fEB`Nw3q3dp}X>oC6=Xum6jCDtiR z8eWH%w3lBEqo&c6?hGdq6}zDmk!VU4eTYO;D(wxVG^L9Ef!&)vR(J2t6v!mIcUzt8 zo?`%2fpA47sqV!lV;Xkvrh!2165d>6ngPkL% zy%E21`s0I*!Dj3bXR#5iN4xv!&_{fAv-9`Xww|sQ+0OYd{ zeK^lX!#`d3oL2QbSiCJV;8ZZ?N$W1;p;pyB47}dG0EvIz7~MtxWjSOPMZ=tT_|Iw{ffv?3Edbs+o8bSW5Ci}68l;9%KommrxUj@IN^1-e6kXAf?!eq^l7a^@Ye!2vFh`)g_ zGjF&vg0tn?E!HZxD2*qRMYp(=KqR`w7!Q%?7URYciEc4sw9_9_W}E8-jQgDgMwD3< z)Hc%jWn|ts*?sH}{_T5S4ZJdtTrm&ok}mTO6dwz28M{?ipBUB{IGn=qAXQTFKl0a0 z9Bd6{Q4x$AU+a&(ZZR&)fd{2$($i)uol8aV1BvOk11Z;wA8IfTju6JP&T724@Ekug z(J4d6pO0|_*5Or+pDEG<)`_Q~=Mw$L(!{+B0R0t92p1f%N}p5%Rz(4(;efU4DWK$dyfn>R zU*v=V>kBmv zq^l-+Ya$_C05o6vj-Or>{MgGq;XBe5Q1_=xph8PXA_QvI8X^&> z?WIH_P`fq}i9l7nLEVuNVfJXc09~CFZrE|MC(^~e8=ZgUv2T%Y!nY8QZG)>6@GS&C zHV9{a84AACHHbpNA!^`0(*?I{G8BCF?}46bIoT&|>rn8%9#n=x!F8hoUDa~*MrCv; zcyT8xL!scjD5PoY^o#-_s>%;S-gc>$#C5HzwjA?O)$#PS*%nt*SycB5Q>puTjq+>h zatxqr^`$$=NjLGg(#b#JTf@ip2HIQfXWHJnAA)M7U)2J(=vRm9{4JgMKpD@u{g{Tv zbM*jF(s=5%@ib^tY1BlMCN8HdzdC%#sX|aTuN21OS8;Hui2Q1ZSDMSL_elmcmtFga zL~{xKkVrI_sN+PUxi~&maE$c|SQ5V_zjcWHN?XV-I3LyDIK8jDmuc86Ewl8C}*Lab+koy5`sNE*ptMX5=K2%qWsbGNXtf z8N!UtP{tUafj!wt*Gl;((U_CMmty;;&KMcIj7tbK8Ot37(vuEeCY`6j34@o3O|2q% vmQPryjQ04mgQ1L^>{GQ!nVQJXFjIKY5zZE7DD(<56naIPSd)Px=ez$4K4s6A diff --git a/desc/examples/HELIOTRON_output.h5 b/desc/examples/HELIOTRON_output.h5 index befea2fb2975b352a07372a08b90f6fd1e6e330e..d42f43641a655e332a1bfbbd9aa5d856846eef44 100644 GIT binary patch delta 14035 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 WbUq*zxz7Mn94~J~D?v%9+x>rrnxP#4 delta 14336 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 diff --git a/desc/examples/HSX_output.h5 b/desc/examples/HSX_output.h5 index ad3ab6ff4ee0f0665fa97f03eec30a53c4401a8e..07be6fe4f7407441d59bc38f9d319d86d3f60b12 100644 GIT binary patch delta 32954 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Ŋ5x>V7N z4!T-;RtBAG*_fW#Y>Azc5<7K`?~>fT)BQcC^~y@@mYCGVv)VDSjvQgmId$aNRA-Ka z;JWHK9K58CRwjoGt0TwBkh)r#96CSdPx*mN^&~Zp^ti(k|GYIl?3tk6o|3TGKhNg5 z6jm2eOT+8x*ro8t>Lk0Hr#dpyQ|3ynGiOiFZEHrwU*MPZn|c93d18kRZZ#^?i>`ZCna)L9eg_(*+1?fF*DRPBIaYA$^uVe zSfsMh6XS|h9;WX_^qoiF`TFQst+mrFtMo)f-K8Zz9PX{T5q?SWL~9c~&o#R>;q-Qe za$McX|6@h|ye}E&Z=0=bphiztc2enoqRdyxxXhQlG=QGukI^Hx1x6NCJfol6sYH7Y zwn%JPGMIDKin5uT6mLafaHP7wLF-P6(s+MrbcL!k_NZ;%hVP?o-hl5%Z^!SxZBN2? zXuHSZyQtl>@O^vxd*D02{p>JgmfOKqwl6Ag?kQ>S(A_N+ho^b$40Zcfy>P42%2N_M zUOl*tVfPlt{^+<1amq1IVQgzp$>+}fNn`en2-)Cf`VKZw^zyE*sajZXMc@3q{WZ^1 zflWf(XZdO3mkLk%SAXS%sOyTZ^l;t3|CPTkYZcO~1E-GfD6%;QazgB2k?p<8z@{N3 z<2bcqyvUZG%LzI2MYh@hycOlB>d2{{^2b`2R~%P7DRbTWh@MV|cEKeImK2Jb9uo;B zk?N)IyJmUfe|=ZI+F$hjYE&~*KZ$KA%skt%FwT<+c}(4)r$a=Z)8Uk7f7#XVL-Ycd zJKD4Ow+!co0H(pISKjGdrK?;`Uzq)c&s^-QC0G)=Pd15BJu(ltk4vb^fgE znG0;TsSiE4U~bj|y>kPls9{PYu0v8&8>@IP-|Y%ewF}Kf&o)G`vK?%wvIu&`bHexp zl|-OF+gLfOZfL^&C>i5Ww(CjpN^4JT7l(Q0ChtP z@6wja(_w02L%{>PLtWXJ;l$Yv^>j0a%l_d|&$VZ`0Me`4AzbV!OOI*L+1i1URM~;5 z9-;$fW0>Bmv7!U%b2%aBK~=xJ#no6p*GWl7YW2*{$`DWH7Kd^{Pa2@K)~gmNAL@$} zp?d{M+&p$~tzOd~I#Mv!si!6>t)XvfSvP!q1V^~{!!C+DNL$-W(MMj__QS}q>o44) zbWSMIPQ3`|@F1Jkrf?#%sh}sq#opQh%4|g|gF0XuYOL3; zb05*JrYYX?_3p;1HUx%9;Y9ab{a~6Bs~#(%^2I|Hkll*}>+VTG4n1)wJl(K`Q!+87 zV5s75W*R}HtPU23E&T<~Sq3@Pl;;US?q~;PS1t_UMC?k+FosD6!=YN!NTq6H#YjjW z&KW@rYUT)pi>cOQy%g`1zuChS?KIRMzA)q?J-xp=U(Fe*-yEgv(k?)1M7g8oOI1T0 z4Tnsy>de)7QfF$jgwD8Ev5F)`>^pYpkiE6Uqg~=^I(HbQSah!Y8byaI&!EU!;9VOyi`JaI>`HVc|#w&cM>(x6@$Ok&Oe)u~71f|94l z1F_{mEGf{1xuOe@syu)&b9g0GfcmVv2SZy|7fAJ43sWbk&tyHNItaVRtz4K-wRlLl z4>L@{U4+8*O`cZaF5-+N+&o{?+l^ivg!)aw&DU=(2H_rqRG)BH_z8ETA>6-6;r>ks zw^9iATs0@yJ{H1_yF&{1NgqjND+w0DeNqVbDNLk+uO;6hg_{QDX{?cOM_aHA3zj7@ zlWG{BhmndGm8f9f0_*+iMI~0z&Kgx*GN79PEn6IZQqx-YmaIvD z<=w}c?iQ?%#1jAQ+U?ogqFMif-OP(jG5)uU9g^G$T|L0h9pZW3$1F*?+v(Lo!`;qd zrFf4LrImHkcnQn+>P|(klYRBCVtW+Nn&ye}Brc~BIO|_7`wm;>7J&~Gn5OWNoS!}Gqr5TbF zYwvZR(lh<~&C-(q0PCvZupgu3Ig0RjhLz^;9Kv(bmh9vbC;lo~q8*wM{aGRB@{b z+g5x?iEd`vP*Epx1Si+N#gxOSRj%qWcWIzOS7FiFg~>l_G~)^0uDvE+kZ)@P78L(#ZwOn)4Io0Q{@?1d`8v%#Vki z@;3X2(#p}$_7!t7MbJ`qMULU^7sVLP3+Juz2DcEYX|Jqjb#b+X%}~}M7^ARIlng*_ zKl`>*d4>9NEWqBF|(+x1xv7C-6cjW%CxIX)T^5JtX%~b9j)@-5vT%-`as1z zU`9Z8oE~B0maExY)1q|Y1`~!H>ZKB4G()Di7)A~yh9lg%sA3T@DWx&b5h+4ZYU3o6 zF0>F#S=EV|vLn&X6HQz?C#2mixHK7PAuer*_sS*=(`LLm!?YQX zWtcYO?Gg5IDUrD}UT~>bKh>MG22T{Rukr~`-I}hCxURiQQ*!dd0c508H{OCl3bl-) znXne9Su-9)c2F-JpyC!Ajt`dsB9%VfgPTtW4Xb^EVVFzf^2EVSPSB>EKT(q|Xo3;Z z1*(FLeuomjBBrweWtQEDP32~yVX^#3^2`ko@8+UEGP6F`f+bq8UJ|28vnbFKIi#?gIxs^n{x-U$|ARc9($sr9E01tVloQo*&b1P$v|kMY?w+rrv2$X&IRcQN2s3#(1~8p zs&qBfe=G`g=v6671O1(mY7WfO-AAc#@YkOjrCLw^)8OP^+)D}eOo$HF6Vp{ZYc0lthxE~-f+jDQwuAXVdLaBs9L}dU95E7K=!ZY-hcEKO^Zf99 zz_I?EB%Z!dGob!pBSF>QyIXCYDxYI6iaDn=kEfW* z#bVA$eU=ll)`>aiWl%YqbILMUK~O6yCNPZadzGG>L4!;^H%aKqi!=+GLFeY^)At4=JAMEYV}H>kY?zsvpEOOJr}YCxr_AlW zcusPFc=cBcw$p;WA~E6>9qrQdxexMam$L*o_B!PA+x`D&uL~wCe zpf?NXCC+VlT=Zf9><}Ik#4iFt$I{+0bc7IUK@V50)<|No7e5+x@Gnf2+-GkW7RdiL zkyUI_^@5(xR>3!zuZ6b+qqCc`k#be@0NOC9Ek+=tU)TS3*dDA_RWWT*PMnExEAZ4EVIET-;fm;l_lW^QB{ zr-JCU48!I@t@r?8A_h!b>gR&xm(;fKkn3XArZ}<&*|yCTuG{E=VdsCD%t?qDp?S#K2?PUF&fM!XhBqu?V(MzUNI5O z1tkf1>Ow;z?7LHYYpuzS7G-$39kH|V7o(~ z*f?xbN1G!Y|5BU4HVNf2T(F|z6J|x(DbSNlyf z$9@tjdm#?D12)^nF=FlvU58X752C(%rW zjU|o})eywPcwD-lAI)SeAil~CGYB+5V}S-hVMCsMN6D37A$z)a`fFyhhXylIR_v z!6?xtoCE@mWIB1v@c~f4^86C$^qCjFvtU14uwNx+l4mmww`VY2{|*)M&a8X7oJa<4PK5lDw;vZzSr5ZSP+l-bu=t0QR|Cfb^x=Y zql4pd>qs|QW{JaM^04J4koxHE5-4PAXf0%NP*EW%?$dj5dYXp{q%D+WX)6q9JwRZt zoUiGLPX$GJ4!Yfns#nHnk)T>N?w)bHq2Zl9R{V!|NgzD{J?%;$`n8oHPpY$XoZL3y znE*fktE7`k27AigkxB@a<3Pk0H^XtHHgJ8`ZOz ztX5>VHSy$zbdHOxXXz;FXj5{gH=~lx>shh|b+jdwY~?8m?HSj)o+YF4N{bTXi3n>S zcUwKnv>`fgr`Fn1$#(TD*`DZ(rIK-;nDEYV9qL&!9LP z66q>J61CZ-p5?pZy%~LNYpw3P6+N)2;%KdxC)VZmq`T8;p!DT|au*GhlzO(BO5E*7 zoC1qD43q)&EIE)$4kC3QOeN(8U+U%m8e(bG+E6U1=iH|Cs`Gxz2r4iV3lz7}95hIz zA*rs$MvLyqe|M9rkEv%T@1ahPC83U^lK0lLeYZUEm2(#}HN==M^3jpmg-))9ZY zA#Yg4CfjB*a#MNf_Hr%`C@ODBZm%~EQ)xWB4Y`{@@Oo)Ymrw@udX?M*sjx?APmA#- zqu)1W-_Y;nDGBJJ1&o$^vBm(g->dCt!5#E_=_PlAcTA=jey{E_g?6}oOc?sT+#NEn zTQ?0Re+Km`NATwBRZKq|1ILfxUR?#7%06;myrrk~bR}v7W$m!15{|HQ8QOfUVw|SC z)`dT-AI}Pn(rTw@-jc}R--EpBH0|+t_1{nFnQv(xz40u|d7vdXUzGB^KmNv9|3lm*86*X@ZQV0GnE$$KIv!0n8*d$se4j^$x;jgAxJ*|@z;>*lGn^NPLurr_*+xF$>ZMCBS)M84L5WBa!k87pVyDrRrMlsB>A<#9Ps-<~0)LVW&c!t(R;&^Qs zcjFQd2e_39cLB`h^*LkIPTs2X!OtrCUBg2Mq{$)?ki}d@1He0(s%U6b!Sa*aELpy> zKJ{X78&7%;B z1iGBOS4C00eN6#E@ROQg7iW~@XmQGPmthZJclwGrqm&)zgsKyKM$s}t>}*NC*vx1P z_261_14UhMwv;I9L(^6Qdzp@I*&*Id&)WkP?d37%IW8Zn_MKn~E!ZlFkv>#S0)4m` zZTF6xjQUUn4^v7&`Zb|;J!UHC!|h>qeJ!S}3%7f7r@Fp$s{aW0PW~Gl(6#Tv?R-_D zFjCS;{=hR$4HVfL*V;glwMiWf6j>V+FHmM}QWq<0Yeq`eo=Fz0%{kAUT>G71ZDIrT z5!h_$je^*H`YRAdM*3CVjJA-q8@n4QvUW!gi6Uzc^%NMhHpnGd8|gALv$X|lZ^1fC z%*WbQcSzRugoni1C*6Xzl>-E8-*el&5&5oFPVXl}?EeWchFTNolX<8`>NQ1yZS)Ge zVo#R39tC;KW2GtJ=Hb@Nua4%vI*fK&_|*X#H%g6+lTBcCW-{=MfI^bbqrnZOM(ee& zxq|eIqwLQc^~U%WysfCbpNLSV_Bo5_c88%yKtheOxO6DY|?Jhm;K~L-M%M?yr`O^M_^I; zTpDKy5&G)!U;x~aAzLDinIJLpq#mDWpy*7k*>9j|RIeRtUHMI}CSVgNFMZo=XC@iN z(5D(T&RWdmkfH*zS50Rc#mcr!R0KoS5fMU*Cj)1FQ^E`jHrIkJlo&0*70J1d(JIWPdRATlI6!PR&kxPpqG4XW{HpPs#@~2> zE=TX_#s}~97dI(IvC4pmz!N?U-Fkh;NW3srFRMgv90Wj!ZRWL)m^0%OLt z9VQsp9V+uNu8##9Xu(DZ%v*K_8JD`*+o=>7r{!-4E&UnplcszYi1$g)IlUvlb=^|* zC->TWB}?7IGe$K0NjwQ)v(}>p0{bbSchzfqhTL!HYB#^^-T8ii%-sVXoAtCO;ATDU z<%cKx;dl7qz5Vbz0XNs*hoAfI2lUnN`q9;VAWRTO5AFik+`|;W<=F@~carv0fX#S6 z>m7mq@Yr0<0P7urM^%Hx10koG$U%Uc*UQ0vH4WjKLSD5GPLRZ;`PDJhua04WN9lLn z(599P*E{9c#9nA}+`jMk~ zsl>?9yyFlLB%I#xbL|%6|Lth5IU@(wYB8{$5d&-ctDJD*QL$}Z0ZqWbIz+B!EhCDK zp#`&@aSvJ-D$`v6YCMI$ZMLEhj4t7_nEtf2SeDP#O$COCV%2bWl)`a>=<4Lgg95#m z*5~z-&0B}r+1sqf9MShy+V^X(0lk0VY|i>S^TOn8Rx5^ifB4ki%AuzO1@-y#?2rAXu4!7$sF0nWE-kj(J$5i&WT)pgd)8FJA7V(o{ z^}<}1i{mk|jXnP~Cj_i<@HRFF>ZCcW@`BNMoWrUw8Ys?THQ!4V=djB^2#n{jg5hEg zTML!>=CBPGti*zCml$nh%Wj8DCzU_hy`9>kS0ize-TQPqxNoAC4Hv3CX^~LvjPsHZ zQtk5Z3=~ycxn!WI+Rp0+imH9>sz7-S7_G2XMevGd}RR!{my-{JLz$@J1_d$|OBy6&&l2N|gNi5r+*F_Fr0BBq;cLR5I-W=qHLp z+o6%tgDA8^cDN$Y9nML|127E)pq&W>=rEOd1ei5)S$LqLLQx;{*IBR^EZ7!-d7}rr zVFB6U^llChfCXeXq=SC7q*w&?lSP8}=OKCXh;Z=S6G^ZM(>AIE0?W0FK?DN3wabkN z1XgP`4FO#5DY*A;?HAD+`=oCQjH6mIS_KGi;SRWELO$u(p?E+hm$VkQkb2%qU$)5^ zq%~cD)st5`ey!h3V7ewmgIRi|ujNr0^h(E{e%U$=n66+@8R?ZS&!IBtmDWlhM3@*I zkjE;hrM09qQoHAQo5uyfJ?kBvtQwoqUux{=9ztU`{mL48_!?_$p9s9IX0uIh=;Q&P z3QdrPDM#baQl|6_ z5I)O=JMHYVR15Qjh8016T)4E22}BdJ_Ht(eQN>cb6NoC7){j6`vE)<$nSZ->QlCVu zOGcVhs>^V>M!KN2Hy{%=k~W0uAo&dy@=N!N&{TYvP>6ko{Dx6klHYLIzItM(=*T~z z4U_yv=<~*a{C?Oe<(J_nzj=oIvczSVn;ltREjv4TyM8!>6V6T)0vxtW3UHD^OT=U; zz$utU0-Ora;o{uDiOMc9J<9!0|*A z32*{|B*07pNq}<+Bmq7EAPG>dkriErifHA>a2CxuA2XBFSt}htFnOKx{1$Z6RW*{9 z^g3tXE$3NsJjdA@S!V##ZEuT|d+C?&)gGi(U)jxFR3mnKm*xk!6m_?qV`S9S-GZ;9 zcboZ|VZpK_W*Gmo3s?y1xLH3lj!ZU1@YK{&5L$cxtg7b=aysa{1O+WuF*LK$HD zq{PAPYrH!XOAN`EyjI**{Ney% z^Y^?EFehG}_Ezz+fL)sBy#QhD-%}OPHeTy_B|y)5OM6zo{{|Gg|CW4OXb*fbzF}r3 zmS~Eg%sniSZT^a)mre}A7#D5Be&jC>)XOV~Tr~L~^y{kWUJ+F_OH`PwCxJw8E1^|C z2Bpb>q@95t(#(D^5sXSL^|cwQQ%!;`HSQfwccHX%nAR0Qp4u-;VO|s5DgK-5n!3lV z&Q1#!Au*a5(9*9S?BU(h8)g};DLf-Xf5Uyk&|jMp$cFy$`^eA_*2mDnfurnn!(5(p zhO=?sIQfi#PMS|oBLO#C=0^dp6!&n=QlH-^rk`EVAWc8%3DnD!dC<$w2%=>`yV{LX z*JA2WO2z4?b_js$$<}U5w+gImdZ5?|ZogM-oF~ucX?W>EF)zJX2=?dQLuq)EP#p^(v;V|CF@yXQ%KB#JuPxXQ7VO^=BLSdkT^&E(J7O{l zAf+D=i+1$iAQM^E*Y*!&%erk~ps=j>EfY)|StgiPJCf;z3y11x1c_-I#&Ie{A*s_S z6`8j7J^-0%CD~S{r7x3A8rHbxq@j? z^Dqs^NbG|E5zn;#&=dB#&V#Z(ri~dG$XLOrpu!Sa`wtW1W7*YQ!Lm7!ky!S|pg?BX zEdai=qnN|)EMl>uZYRuj|o^5qT=w(0&0M2f>3an$uo_0;?9d=sh~Xuwb#Evg8cAoe^< zjS&eR0%11M(!VAUrttW!=7EO5#WR6*2jiD6{Os%1u-R z4=-1r2t;A-Cx^Qn-m+?mk4EP}bMKJVfdzKMs&qG0X|}D5@j|8~pGD;!cy68(#sY4( zsEqR~fA1f7Adp~l9bjfRdmtwGRe={UkZF?cN#ucO8D&bE>{n!po_a&OPjfseHM9mq z<^nL$jB{w`T(>&Ej+BZD7pLIS33gTzhF*#3Uz9Hv{kHtLk4>3=4zrhI$ z-xFqv-A|Khy{)+q82yR%@;m_%Rz;@*G3HE5IRsC6o_Ob9*f#=W zleFf3Gtm3YlXkF3{0No(xY7<0FjrQ~xgcn>?!%ZTZ6SO(egc{R zh6=;uu~D=K2EyVmCt4>^lRKCsltSO|^*i+i=iHjb)*>cH3}qf7iljKG6$o(A92cN8 zZ{~^(Ms5R$gIm^K3gqCH13=~<*c+>^FiTAKMy(P`LvTw=HaKc-TabR&r}mxNs=bsB z^NB4uNMHV?edizB96#S1Xtp_?Zp>Ux5yY?T5F~a=1uX@a<2vK@4Vx`3iC^;;a(oCU zTpTL6{1J2cZ7ng1x{hj_(iEI3xKV|;FtMs-HV23Y^ViS|Ui91VGplW93)a(u-6b(v z_R++sCbse>v<(6u)8KvHr`iQU(DD3zQePi~?6ikG)`*D0X*D{MK-AZq76hWc=EWhX zCw>GwqgViWI9DWEIg>L`a%NU9!I@RlnGri?3eKbzaYD8y#Ai;_GQUS1GA8q!AH;yJ z_=R)}xmEBZfoMEDIFd-f_16Pkpy?p0D^Zwv6e#Q__}Z&Grr{(xqz6FE*O#FC%-8*r zTXYJ0!-BnS!A=N_t%q7va*+4M@Bo+&K7t2^`B1wdIjbHVL^~P`hSkbCBNen}Il2%C z%vHwgr6Yn`>m^k|PIUmK;$)ECAHeltsMB&0o^JKC&VWbeZNp5!aXygQ@3;C{XZdB% zCf`+t%sm?(n;V-0xOoTg0N~N^C-ct**xdR&z)=U}^9RXu)EGLw(dkYTPS(CRYlt9lpT6SPiAmie)9WQD|0mblH6IBp8e^#%JJXDwwO z98u;v@Iro+{^0N0B<i<+v?BVLA zUM1v!UXtrB(o!Y_VK~iB%`rGgMAAgfw9b0R7Ry<$Vy>9=F7D(>&bdd-dYAvs2@&t{ zdZ0EM%o9YxTW&39mO)cNwwSus&c-yHx=Q8%L{pb~`(Q7eli*rfx4|q`oo22(E!Y4H zHe6z~BA{7X&6($&yAW4|dj@;odl*&(^(i9eQ@!Ltcd{Y~!ppucXAaR9CfT9NGMq*+#!&bY4vqeGY6Mbh1iVyQSN_y>Orf* zfkjp6CyW^D{!EI9Gr;$ACD$_ldK}Pn=r~ac+={*cq1I zj(-YqCjW;MGOh@54jp0=r$>^c6|9ruT#soa&SHRAoD+r!an2rM7Ux42Y_SD#TrII{f0+8i-&RkzD*Qcjf2Dz0y zJ?023Vi+N%xgVjjXsnKRF0o3_bpM8=Cymvo3vo>+Lv{Q)KQpc=_{<{l%14FuZ=ynG z1JO^n8Hr|tLQ#doTlH>G15Hgy6Rb{8GqZZU1-+CS!$>O(m)E;*az^9ZnzY8(={an|wGAIZ?E^VJ?1DQnj!ntDiV*RC^m<`D}*GLM`h@z5sH55N}m25^7H>Z*sh8F1-v9sYn>=6tTVNlSkLlF6#3GH+U#1-GTo@n?$l-vYO^PO_o9HC#At}1;4yM+IP{}kZIkcW7 zhf&GlRB{B4rZ{v#;HNIF9_7iM9qGvniES$b$^Nv2zWJAsHlCGXZoT@Kkmh=et09f! z#&Sz_HZ_ijyqCl_o(51xJxflY0W^_@!K8YYf%q~QKvSqSh#rFhlv&S`Sw!c3RB{?A z^YnU_oIwL zf%J};GMU|Qz#fz-h6A=jrqBlZmcS-m(o0Fz3sTZoC9_B@wxQ;|n!|#*B}N-z3cnh-%)#MTYSd8u z#U~u?vb=ADpE2R(5kVMywZchm{qINMrd{kGuM_erT5Qy_@G2$TVZlaO z?Y@X%u-T|99$^@EUh3*q41?81UAhwCV)Co5e#XlEKD!-kEmXVfY|&n2$Md<(wj)sd zNJM2AQsmNk-NBJdYRkXsca;F=hC(KEuc~uj(x1VEvs)Y(T(c7Tu*YF zriL?)#KYOsohES%Q!|??+<>E}TRz7aHFJ5nsguteVhmb)qd5#wGiPt?bAlNb77X_> zH;M88W(x~?ddzh+_0?gtgW*2rIv|#86a+s=sxZ>WTm^3IZ~~FfTj}wyz-!S~lU#+u zZ4yF8gEhdkefMJ6a+5cJ7MTpR|noZ%d@bcG(PL_zTYH7ad?x@ zJ9-5*lv<1dYC_BPH`fF-#h5dk5R8?%u6GJEGK0 zKYRc7vtw_#F^t4GGmdOdu}m|6Tso$2?l-FPTGLqY`|)@L+Dwj z9YW7~NT9C-2JP^Ip53cxn>45JEPHAk>{(VvLIpIur|jV2-5p-JBqzevHyDP2rJYqs z$S}IK^Wg-dx8##B0NE9Fz7bnqQium&sfm+ZSlvQ!p=LLCTG=PKPaYh-teC9qin+je+sZbJt2Y3XDqvfAQ5 zI_PB+8MJgqu)0?=;s`TB`=lX}fTp^HCIliImNq94*{~v(KxD(h_5d0lWcAbz9j4zV zdPT15vI5n0bABGRHIdoFssc)~kn#Wa9G^`W>`MJNb0_N;!LBqn+h(#Gg!8KL1vJ#l z`HnXB8A(cf_IT?|XS&Htf|24`J&1~+&v?T~93(zpi|N)Gr4ONRo9*N%Dgu**KBJ2> zhjws35!+zG+o31@)%meL;!furtz~y7yNttkI{wIIJh?CT-Rv^n^fjoY&t<%6u#;WJ z`#2q3#;Zp=|HNgie&aB^jAu-?YGdPvq&9}lklGl-+*EE4=RsO@CnuEm73>}H4UWlo z)v45PRK}B;LK)9yVHzsqrTYLnOm8tzz7eBw?l)pIE|kDh39R^r7h;M&IXuHDqEA+T z;}Fp&hoBM~k;lKm5&5nc zhMLAj@C>o-9c}AkBjamJ1mE6$1k;djpDYE42k}M8w`-D1%6Y*+rv##|ct@|qLAP^I-1>Y1k#Xmj5r~Yt^fZCUxU)|INRcCI_7!jC z24@pR&Aq~-nBHSJcD++LQMSU>ON<%OqL1UbjbldI{)hS}Vn!PO8!KjCU8>)BG+ni) zVI_Tazx+yN$XAy+%Q_%U7Z|CG^wsV8naYr_ZqrW)^JQtlfPw2M2^p}dnE4;y%dcwF zWl5?!R*dyMI+aBsAKn1qdOc?^yf|I*c;gi>4EMo`c5mORID~g@>sG7gjeT7X zy%BrF(CcvYSCpc?n#)!eftOLvHSIirA1}fOLAFof`kE(4pSHvJS1^Ld_!!FKr_b zb*Rs70#Szs9wHERsQWhW%mGXH9gM4%$uyP98D{C zH5hDb-Zgax+`Mb*;fKp;TUoxB-*XvlE1zSut+{>?Z5wQCtM2^os{t5sYbF_Oz;Z8d zz6yX*w`M%WdNm-`ubO^!Tn*?C`OTyb0Ni|V9q3ozAga#`Z}9dgVP-l8>xGYnB{tl8 z+HYuFGKR)EF*F8E;gK*i)5SyM+vS`P_^22fPyZ^0#%G2KgncfD##v0Gq45PkVp%%r z&Hcs+hmpA_y@eVEd@Yo~QVFa$sh2hmKBXtFaIeyCLJ&8<>T&f+rx+SBgLKoOY`0$2{)m(KE$QQXx5p65m798BLkmDbc%z5@JhY7ss3uT%0I@X{YqkfZ)&j&G?U&tvXlFC3mD@N?G5xPwvKs`uE5 zT`TakXz$+wgQHY+&MD^cz9+(iwZlPh0B%XYEmQWLb#^e_Eu<<6#F!{b)RnjZcu7GZ z1>5vlKsBH|PC5t#hoxGYWMo38W!n&VGLX2jM0WM*Q#vTj7kn$jBU$s(DQ{Lds?mPP z=R)8O;DkH)fTF%FS#p9(zKJi9vAr39wBQfKly{N*g7F10ztvS>f(1K6 zeC-Kq|9lr{SQ}zSI z%iRxBM88RnYaa-;43R*?4|q(&o1a%{)vpDMS7SB*z{wFajjLfz!dGKW0DxYNwev%# z$vS)KLoq0BerOu<*}I8E9P*WW3B(~E@hX9+VofUv#3Apj0Fd{iP5*A?@bRyM#UZ)! zRIr{|1Mh<0&J*3Tonq#@Rmlmh-Vrlj*hfqUu`2gCHG|wNd{3~d=me(W^jG>mK+LL^ zABlmR@R8t9cM0^7z`&1~Rqn~as@xBP#ap9Bd<3&H#BiHRu8tYZYCFw0L8h{8Pd6@e(s9W?;5Fb`d@ z3N!nr6lPYfxaEB0JPYrO?}Rj$HEEyRM9{_2h##m8lw{10LXt_BFbyS{ z`V&ATN$nBfIb9r8Yd>aDYJY)Bd>6{sS+Ex*<}<5DT$ZBU0uM>F|M@(aMcebN5beuf z1her|`yL>^SbqHr!Lc3R5wXZI<$D5=W21j25IL4{gFxh1+HU{~j%hsAOQRuNwH21n zu%KXmS5rji5ab8HtEuVSa1O%BzbR&&aX2+=!CwY*98QuWL^8;U5QR^T4ki$dpV{{j zi0Av9Mtny`ZkSAWrAZ>FGx*60p(1dai{Ic^#B`gQvdV6hwxA+NS+g%CuQ~w4_j~UE zYJJk_C$aSR+}BizWS%e~1#=V8#G$9&i}7 zV;%ux(V5WJyQNLYEyXY#j`OSJUaL0&yJxNgz1-$4@B|9W;TMD5)-L3EryPwF^;VU3 zc#+vlf%CO%twP9-s_jsjuc$Hs2pYGnBLe9O8d3A75lDj|2tW#%nX=6lrNy-m;gFg9 zy{;){KARvhW;$&DA{sxY<@a zm){^Hv(NL({-E{DgIf@D>$!lNTgU6tW?WvEmetGa(h^_jm;YhE`tiE-?eIFWkB+o$ zSlxxEh7?G{PvPJYu`=f0A#OXi>d13v-%f5m6tqj@gh9jHdifalEF4WVm&SEB)F`rsi@ETK@L?q#i*|ZyhIAdlf6Nocr?jQnj#>^TBAkUb&!>w$I$&zeodXHeskqOL& zFD41L%v;C_YZi$$<_;i&_;P$CwSmLy%qYQ^i|Lq#e7QCnAm+;X%p~{I*dp|{u~-XAo9mKhCt*` zL?(iI=Ff1)cPfB}p;;@2Du_f1RoW~u3ROy)#r(q|iJ!tfjQ_X6IeVq#=L*5kC*4Km z-D$nq26X}hMQl>dQKY1}q3Y})0-xlXVhSpu)>7!(W^1~Px`E43*>vkNWV)hFnQ`ZY zpNuD|2;Sm|xgS6dO*$a?rXFqX4OAcW1#p8}D85SeswA@|s9X0Cy1OlIb| zP3ERoI@a5lB?eZkn99nG>}M~OM1n~nYv$;9{nn&A?LDQ1Bf}7 z2;Jl1n*wEh!*_@UOSfR-1twyPk`9Il19K)k@WmFbGB~#p(kW`|_+BClITuq-AaXAE z0D;K4{I>~2&Sk#^;CfH(>M^Qlk0V0L1;!B}HSdQAJ982QDv+Idj}14JWM@{(+sHF0 z{q}b0Z4xD8a|gHIs5HfX(a@5{=EC+yIr{0G)6ZCkrs;M)m6HbNIv#KvL)^mODn$2uZdN@8~xNoEPdN-$sbTz)QPkz;uyLVj(7Bq=ot!b zrj9y{{f~9}<(!P_5Vk#E1j+DktR$Va>SWDjsgq~EfyyT96{Ya9X&{rdYSouUTN}>_ zt=dt8X{c3)z5RpSJ+R<*QXZ7o=W#ArN{$$8cJ5MfvA4i)lGsLTO~j~Gj! z0BKds=R_8=FaHYyQLB!BO(1GjbuEFYRh2&j$b;o+C=cBd?<1=DRVq?GghmLP@8W$7$Y%yZ-YeWy$upEiOn4<#5M=2W3iP2#9}Ll0w^{$1|m%aUBFf~ zA55|W1@WoJ!KWxt5TRP#+(1E$YGpHlE+euljoPcBb`YxgrR|~^S&B`iasNh1(2wH8 zpY~f`O#x5DXK5g{O{p^XR&}56m4=XSQ@j6^%kapRCl+GdI?LpvdoFoq`4*>Fy@-j{%fwu7c1CbzjJu#V51_O zrpy&#$I$E}8$!>&xuS5rbf9~@3MK-d5|_q?ny?)bGh$~-dosH+p)UMne9d?wqqe5~ z80S%Mv(+>Rrzf@IdFiZh{#kS|g2S(byY;xcL;t0fLp${257+LJ%80h<_;>eNXAjez zH>$5d2>N19IOr_J(ZDYan#Y7Q)j%SM(*JLi^KC%)gSZCZvcFGhw1x1)Jx{s`9ObK;2J08L}GA@Sk{~W=oEqOl5 zVM=Oxr-8yQRxRow&~5ZisHU=RGIsdxBNq+~HT@1m`H0Y$;ddY$-fyx)AB)#s%@1Yc z_Ko%MBN5*eh6>|$lg*(C@!Hxw;_2z*@U-GJ`Sf4ap?ZA7^7Aa`6hUoaaG3Zth~&PK zP`r}toR190Sv0PXOh*HEhbq)m_{gD}T*DQ{sJzWh!0Y))c98_m-fwJi;2KtT5 zuIjvC$(DT0Kyf}#yDm|rB;z-M@qC^XCncXMxd`&wR#Xwq(N;s8vH$02E4(59e~q?+ zA7k;PI2{z6^8XWWg;7&H=y(8W((bhacqTOpmEwV@7AR9JLeG?lEo zYz&2c$cQWaH7&5Q(5J3e+fvv^I|{*QU(YhJ6!sBEltzbBz>*SAfgc^~SwjK^esrQ% zJJ+*JBDI=Ca_mC!9$o3X8+~`DZy8aAn%#?IKaweQ3SIB`sf!)GNeFimJNr-=Q(rDv zXGs}!g-uh?Roi+6U3n%)w4uTD{}XP7gK20z=^Q5V$$!IXFrl*rKXvhEB=Khy2_csbL8+^3HVvTJ)ZQF!uFm!zpaC?OBruPL z%Y*ctL*Kdd{SbZ6r(nDVG=TJx_qiQPZBhAmw1%gdR-dcdXbYAhF*-p|U`*VtP~m*d zf`|0F!in*r?0hW&fSj+j9bws)p)p$51i6ptpAgCijM8u9%1CiB*SJkg~m<4-UV#FqjOE?}GCLFIGcu3y4uRDdZcTTx2 zj2*A-I)@6!YvWE~iFT*lF}vuuk-UFx6LLe*=XMJl)l@!dF0@eME%ci1A;Y>#6hlSK zTXEdaqqjspmzVAV_g%X8c}rxr7{6+6BX3E9eB1^eqdH3*S0>yrOB^r8N9lcUXfw2I z=pk=m^E-#Ki8!1Dfv5FDZUWIF;A}!5N;@JRK{gS00Fb2}kz|#2JS4dXgE~Fir7%F_RqX1&AOic{crG{AaZ3#PXduE((Nsr~%3>yKrqf z6+t)ba$wV%#Q|_Br(KwkQfj*}la$gd*k}uuAu&3dP+-dOd&2bAO+t36*Cu&Gvci^x z8pFyBB$zci0l39$W8j#1Tw&C|`K&A>$7Fs}hvWTFbN%9eXs|(UFW>!6NmDYE zF_VlNRe9P(>y6BZcSX(komRt;ye2BM_eH}{`#6c>?NBpiDS(?bGL>&>#x1c~Vc_T9}v@-=7x72IQuTi6opDa~~lPXQj$Q0&!wIzJ@@Y7!R!m zkSE6D>#Y6G-y!>*`vPR*et!;)&~g!@Qyn-sk~WBbr*6bF?Dy#B0OEfC2n6zSaRbWw z`fZ#kjvT6g&ygNfUr3~62Im((-Vy7F^E>ndw74YjWND}Y$*4#rBY`%%h} zEPKbGCZ7&gS+LU*qfH9AP)pB;2^Z=)sE}Q#p>a~-TEq#3Oa7e5LWL{(f{2?s5s3MPI)swDTJ6{tTF81SReun}kA8K_uR$#1BHLr-yCmp7Wks+7& z6No<4xNuN2b_;a`8i{qQt{XWw{w7NAxPfWNx_Sa$3?c99-Hlak6~xJ*3st??OP-B5T*vtUcw@S|M7Yki**GRSTxY4@ zpfrUn(%6z7)H4rK84A~FJIOjKO>tyYMtV<+?x!*ou2VP@VX|hwD>)I@h|*Ar3Y!pt zZR6obm#7Z87}ofL)+imf68I^Kq_U_6rBMK~>_0KseI6R|{YFT6Q+;In@Lk%C`C-B* zx2i3hAycR}Sa9!0I@2KUm#WK7bzne zg|nb0pJL@$usn&;C?v;eO+vWvz!gA+?14J~5T6H*LIF~x{M(5vWMFkW0#TJ>IueMZ zFfN%u9ED1^aDG>v_9+yH0V4dS$thBMsv!)}@SA=s+laf%XrnMy2x*gu7ZDfD4kQ42ntu}f6Dn2nj)9a`9%V0r=G48Nt)6gx{Yk?zp6 zG@NKCaHPW}KNptiEe#eqUP~6!2|G4AO48IjANEep3x{h84||s`4nL)755Obl>?1(6 zkF$?ku)j#maNQNYNbE%=nlB7M*c+Y;Y(B+b?90lf#^s4bSGti z{gXEOaY&^Y7j26nnO7zE4Ikp0hCts3K|UQ06n-u<87{B6_ets-GO8fk+aN#<Xj=MEG4#Ep9myh5o{CcOheOE@`@q2*qHSK7O4vg?ds=vqXJaE2s$0h1ZqO^&sn7abwz}5m%1vg@o4#nMF?X-6Bn1ZeK=m-$cT#XjV@pU_t^o_5M7Oa~EyHjE` zbCG>Eu(M0pXZt3*$Udv~>a7>_fo7NXc9D5jJ$a|Ms*h`Y7==#C6JKs3#so7)m&aQ3{?o1V@UqAwP3uGc-aR88n^hK#q)?K^&Pm6H|~Q58n?EtK(iE zkEi=1P|~N4M=jVX3-+nRd@LC>Te4)?0>P3mQkW&D77CX1UF3D=yHyu!8AZrHAd`xP4DiO{mOm{iI2!bO7XUt|(Pkn26B5r|xW zVitkO^)*WgL^WLY2!O1HKRwP&mxjx4i@k|!Pz{p{!rAm1{Dn(jwmAF^`r(KoXpYRR zGgo14#O!i~}9Qk-GRB1LHjx+4D_coMy?)5dRZ+_C=0?)|} z0(V;n>wyaI+6P=gs=6O~%47BJN5oj2vR5)fowtw1>asFSLG4*n4$xt}b`C5xk4U*a z1SNfvIt@=O;5M}PIck*6n>n#-0D3{!7g_? z0T545@h{6R_lAQk{ z0FTYB&jsAv`aD1UK|dV#kmmd{kV#UDflTK5MIe*UIKRLPKV_b+Y7;?McC%}&^Gvg$t#RePXDH`Q>rzEzHPRVzZ0#vVq_=rH8y#C zH$_`fW?WhEv&KQKpjCC;1t`r{)#nXH?E;9+s$X1kv03#EAoCB*s;z$IE}PA&N1?RI ztUAT!X0z&PP6xB<$YA%Mm{q%c<}#aA+cjY>rV!~i+ov7e!m@a(rNPDc?P3C17DZjd z#bZrVh@C=D|K?_xhEvGi<^b^&(tEp{LWV(E-xM;&f-MDFe9u-$jCMF=R$bQ0Enj@} znM;^e4*|p)cN_|k#x-e7WTD2*izE;=?nny)QR9xr5r`UhAQnJ&WP~SK`+j7g?E8UU zkjZ3K-TOUif$>(mBR|mO8GeOy1G#bPM*`8N8aS95z}5c@5J(g6%&ycv=6txD;M)`3 zF%9`v+yfxy+ePRc?-H&FZqScj{?p7Srv-};n6RkMOm+*4YEyXNv#9p_r{vn*kWO5? z)tQJwu8r$LAaZS6PXdu^dj}ART-)3qK>o?>`$U6mQ=Kg^vZ<;~MtaNEyPK%$%wc-L zME5-HOgH6R2Id$Y&3vusC(34 z^|R23VNKQ6-0jv!wGZE^{S`8Bh{Mb&Rt;=2pENKK7zowz6!sHTLP~Tb(`fBfmQbQE z?!z=xqMxP##7cAv>SrYi|JYNITL1+} zjmAwTvXFs$rxJ*2bZa_+s78Sg5r}GZB?my}(&JDby2a*HwMo8EoV|-sai)-r*2yLU z^QZ=r(PAN^<1?-DKjI5Yw904+l_eQHVzj=+=*O$jh)G6E-E2&K2Qmm5t@4x6W+|g0 zsE%cH`&lWYr;XN5{YA=XHKvh_)&RsZ>ULJjXaJP;O@hNM*gY0(lEh3hDv~mq4i&O3 z^*Ml8MmwPZ%V;H$MKXGdK$6ij1d@z45lAw69zd4S>qbrQK~1!d(obI>{VV2XW9pOz z1d}nf$Zv%-g&&hlq%pPKqc~ggTX<&p+3!s6F|x?<;$pP^kY0RdkoCO*T?YuxgC{ku W*%y+t9pH@U<7^iTc83Yu_WuBVsVp@B delta 49289 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 delta 7833 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

SNsFhnQt63zFdUY zt=uz|O@1=eT8K`fcQ+Av(^zyCz1RESG!~a-?{zDUdv_zYo7SUfe4E;0vBdjNyL0+W zt!^OpWuAVH)SD7K0?|VI+pkz*fHVX7Bt4t0y`53hNRv)ENiK=LiOM(|B$~gEV+u`Y z2NJ2CSGS&Tx7d(H6LG$^pF|5{N&GrBQzA^K9Q-(d!jS?l52TEoI|`r}CCe zre$h_`kry!c(RsfLf+lt>uG0ku*3Lj3?m~t6#ICm!=U23RK2z=q*+-EE*F5f^&SlY Mzi=@9ROZ3|1=^%rBLDyZ diff --git a/desc/examples/W7-X_output.h5 b/desc/examples/W7-X_output.h5 index e2432f8270b4c1bdfa13819ad3a907436b9a1bec..e1a79ce065b5b97ae806397ac225c639f144e5a4 100644 GIT binary patch delta 15395 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 delta 15698 zcmb7L30zgx*1u=(%ej{e$UF!#IV6f8;B09?Y6fEJ^C>A9konRi6^Bg4M8(RS@QS-Z z11znFnBsH_hXlO`lQ z)hmAd#DW=i@TaieWCXQb^c1JBcd+wn zP>f;`R@H8Y6R~W$hSFNYU>rZEl1%eYoRDScKv~GF+Cw zs=p7I&a^w+Etu`_i;P#4P-jZi6lkM5YK!?q7AGp9T5*_?XjMXm7O6ZAx3(zd+i~3w}bUE?N4DD?O?TXggKAIbzGb6)>nMDQwbJZPpb39mV;`D zwr-c=XBV}(yp`Dak?JS5ex%yzN3YPwpI7ad6d`~3&u$SZFYvLed#b``w$)GZFR6|c zr}ry9qG}hIYYRxwytb%cyRWU|5#sh?Wjed@shIY$vYE9%rbT?BB)Ee^JYKK74wsAd zN(Nkd9aDf9CP2t$RPI|)0x1?u`+AZ&A zeIPx9%}f#J<|qewz$N6*nQ+C<+g~PlH3>|51>x#Tc0S;HK>M6gd<-j2(UKWc6;=vh zzS2rz>kY6`2g(+0@x~EmFDpA__zr&>0B@-7G%gA7+ zvoXXW{m|jKD0|=evSg#uVWq-HJguTSllzODe5*r@ysHcrSMEY<>q5{&oxp^62OgHx z&L#o|9haQlw$f|l%u_W=@-{Yv$XORVAj#RN<2q-D45}u;6C&?91I#)urWPruMPV!3 zTz1=`)!W!uh0Q;%#k;di2itaBr>pk3xY~vtVhS~hY+d09C0h@<6DGC>1Q8~-x(5O- zb)M@sf*mzl{09(Ur5q4PU$Tbssz{1vuyd`Ch@Z>t&TY&tyxQ9A{5Ir=VHvh;k(>rj zUkHyxRv3%n^JbxZkKWP^Et6+bbDxE=1U_UA;WllUoqschirMvI)&hDp8DHI)ikMhE zr+dM=2zrG5x)DVX`L{+epD>riM0PVq0c%?-hlU7f2e6a`UeyVMp(J+<-{rj8JxX+m zX46^w_M#%1ZDu|vw4rSo1k&xUOaw}FqKwk7|R5mnvU zT=$W^rIt2L@wRnW#Q2^piamEiKp%p%bv;?CLs=swLX;}+AlxHzo>mmDIYuCgo@Fa_4tM_3!a?y)~~jP5WXfMaONPM6k}4MtH;Z0(ygf@r`S zkAeYRMusK^ypc}j(16wqY#a@!9Bk~(2?0rWd3=>{?qNxejJ!hD3O|EXj zW6w#8U7xC(@YJjF!Oi8;gsgRg4D10}raB|DjUMpv;}D_^XFLf=26m?gJ+S*UXys!W zn6N?HmL;z;zK1txN4v4niu-`P-?85l4KDU$FE;2gzOq54#M7D7JaXB6EJA6y#W{q@ za?A1wljY`31}we5%n-HJ0Do%`$=j5@qTd1t>8u%4>!E2Z#L+msi*eIfgrjjok zVifkJA>yn7euPfVU|(?NeNyj#Bml~19SzX+q|7em+%5Ex?h9q-bI9%%%VB-umZ0BP zEtBu{8Iq-7g|v+^aZ9>w#fjTKMZBf)5MF6oo>`#C()=i z;;8E8;LDa`x1Gac?6D)imH9}|N|wfx@zzncTHNvHAuK#X1PAa4-u6MRJzLMt+O*&{ylY>-h-^t?vaZz^FKEdxaFwyc z=WBnT{QjD>;_a%B-izCsXC%8{oA75btM+08O^$4fca#RcgY(LvRO}}De)~^l0y)Z6kD5%5Sn3|@&HAa)Rs{#&9LLg zhDE+Kz~2pU_M{xXzsTit*{r9v^gKRR;oqH9L{_-kf!+GMIQkTSfc5x?_!Y1J{-=l< z57+mnh{1TRohqi{^~O~38eZKCwA%4}qX*0hCrssQVY#8*nZdcj`xS7lXocdX@Qea3 zN>(Z#u~u2s1IQ9=!Z?JKCD`t#2&22Q{j&*UmSFEc4;cFPyfJt3+dsdlv7UQVH`atD zWGqMBb-AuO{Gl{nVxUSi4-Zyp^yAr}%lSE=OHdIuM8rO@kRTGx5atX9-+~mWbYXT|Z09S;E_R!{ocLls^iWL#6yl zxO7;{VJa?4*YY@@=F2!yzn0Gs*Dv!i{KPE75Z}zw4e@><^%xDIE~N&K}0RVKh`!{mdK5xOOAfHGu>-5anavc%!H z38E!VzJm}g@$E+0y0nD#dEF8`01+9WjfvWusLS)Nrr7(IzZEe;0BNWeu=6ar&W{ zGF)}AlYhhZh{9TG8J8*S=Q_fen3Ob;a@#GWq}G;{qqlnFUenR0aVa9T8>W|2KO!l( z{OH`qBF98ygmPJ~P-&w_&op2iOjLZaN#Ghl6j{6iY&Y#9F0}CFB^^i$?}Uz}EMs4Z zh&n!<2OZ>M{s^U#4f;$=KEQYQ^4h<1ac;b_lnt$+Hoy$ZCK27hBONii#=G~C##c{3 znjX`^s^?sGct1RrhLCG#akK#j?yG0Hb~j(`f+QbGOR%8XI#%`g(^&(zOQ` z>%M;I9r@tGCf(QfO1pf-96yqJn9f755=39`d<`KQs_%8Fjr8@;Om0piB47W;L|067 z0}!O0Q3`YXq@BG6DTk%r6TnQ6D0~IhvH7p68@$DBW}OS^Vm7xr;o@nvR>0+bs}s+wOiH3x-~1k-^y;+#5T*&n2s?5~+ao;H2DovlJED~wI+Jx1 zuy#6i{AQp&4JxjcRBb$a>e2{-l=dKOtf_Z{h`WQyzHNFIaJAT9uA#le}`2(Wa zF7NW1;ZzSHBH{*^Xo!hM0`h$miK`6KUi7yDah;iVI>4Ig;92lavN2g_gQt2?v%m&_ z!y6&7!7l|6CN{1|6Q)`2*HM6JDa+@XO)f@ZRmxZXew`&b#-wM`G^^__)rcWFL*LtN zM4y+3O=i-cI>-lsF*Z@Lhd;wWV#+ok=*UvD?byBXO=Hq!8A#GhlcZvuZaMVOVp*Ko zw3^FWgh~w7%cZ7cHFLtoI>D_>LF-J~sz5Iwa2IVk&D*K?8T(yqX}q`>YYlAN^5T1; zR@pAa`~TVYI?_vWh7pPXes9~0e+=z{tia#f_2PbySjC$s)k=N}T8#WexnraC_Cx%4J*q+7oTD#%>95)rwzFj1L_-UU>08QnT5 zSGzYB+?sEN=AgTQE*o3?-%w0?_jS#6!djXK7(qA>GY=?-bj5Sd=6FmsB zX-0>;T|9K2RlC*7Hb&u(K#8vIY_x;V1SDB3hO)%s_5x}gS#-=qC|MjchcL02_Yz@Z zal{h9GEbD64X;OGRZ5-Q^g6PNlEd$9dWEjAHrw>#^A_QfLd*+;ovSzQVMFHL*V4Px zF6@2TW7jmx5iWa26y{U)0euq3MU9|6GWHvZGxo^Z-Xd|dN9K1EF%9n<4JK+<(lcbj zZ#6+MW9(=8>4-65a9JEv!-f~Mby5{HchVYw(%idD!Vf_+W`^0j&n3g8mWqdaK!!OR zV_VA`pu*Yx5bDM{!aua4siPj*XE^GC_n@6@>coFQIt@GpZ<}f?e1{w1qt>UxiZ zlMFu~M@>&Hui`s?|-$T@SDc zMYW@xY>&R2w*jGy>h13mrl_vjOPHd%au49f-N~8*e;C0dU<(L$C;2Z%Q`V8xAoRrDS?`a1T~IoWp&B6;Ee{y;mZ- zA^x@Wdy`i9QGnu2$M?yO-YwSDPk!&=h;sIJk4X%ajkdVeAZ$+wChbFUI zP?8Wv652RR{2opTzfTDmJFb6`&J>TXiBbBamhu-({Iw;EwId1b?^8kt>d6Bnp`&xS zXRk!V>szWrC;T=+>Fg{C=$IILpRIQx{o|Indp7&X|7e!2OYP}D&-uFHwJcu7YC=XHV{qD1Fe`@_Gu)( z9YDVW>30zM>!TR2IK0r`YpOPFwsnPyo5Q@DIiAjgTqQ@_#U@$?Nrk3vFviYPF6%?= z8hA=WtlJhf)xqn`tXK~(D9_)lr(}hBe#8lcGS6?gNSN|`<`&lusM1M&fk#t3HtqrKzVQ1@0Z8OCBaqwK4Xb?o4bWD{({`^`cNK;B>Cekl#WDx zehcRajWY`IuhZp zm_2?SEjV8}Lx&=~0-nq1el;Man85~c9U$K&ush5@m1XznCnLNbr4U>H)ZZ9qBeC-f zn|8uwtD3Z#s_UfXNNsUZ=#9`^bsuquTRJA;=TSQDkJb3QO=mSBqHBQLW_Bq`oDOh{ zV4o*zw*%bz4`v_difbF)oZ`!D;P;n_;$k+u-SdjJf0Nren|ASOH#M2ftHd&!S=Dy& z)+TuU!7+U8KiI#|FVb$wx?&goRon>sUU4Q^Rp21VYogXBir#IN+2Z_fZgW}5J#E}gH`os9v|H@C<@Nv@u8Du+b*UyiZo@VA zL(vVdZ+1Uz>6Iao%2xa1`zeAW@|DS#; zKBvU>sW0Hh#rr=)P`$><-79fqUieCDHf3rS6R>h!q5Q*>V_NIWrzukf`&Q}x|Z4$*YJ2SK1o zxh6{FwmUMV^oHle6btp$au=(r!pjU&SZ7~tn!^UlCmEoxuc+{{ofgDWeP5j=_koh7 zRA0$buR)TfdA^dR36Dya&JC6d(@ljAIh#R24UZA@COZxyg?xSLKQ7XKXu5 zYTv`999x}+-c>`VJp9@dD49HIEVV%&qxohL#-z)i-jBFj4)g~9(k?e0cwCMz=>0JI zwOFFFNd)e}#TGQH<8s15r48K|O~E>(H`8O(sE8z2)0D)|m z!yV=^N4t}+_V)Gvd`Z(-E_~aF<(Vs>nvCVIiS2{D!z-rH9!m*g${YA9LZtuC%K*th zzHW4xrR!aG7{Jc}q3JS?hb&a)@(wFi?c6o^mWXG+t_tXLpm}n0-P??wF2J>G^u)nE zjvI#mW8AvNYFF3qbseIg)!O|DqijMmS&49O?8wM*-eQS@XGZxXz^<}!*@81T<9&CL zwodI}hE9)W#e0frgP1=|i*Vn{Q*2$Q_B2)L1!Y~k)f?`k;GZG74`A1(`@+3zSU(8& zIJ_F=9}@l7s}ot%e;T666H|dZ`e!_edR(GLiClNwg8g8G9q0Mk37ROCK;f>0CRL- zx$-9X%Aswlw!j}gR^u3jCX!Lcp-q-ARsa5RjuD>FbeRbPPnEL9qmoWA9 zP7PrB)#V1G(aLlY9ceot?!srk{&5Fo@6qJ=SL|VPJ*~?B7V1k1wi5+4!Y9V+;1}BC zERg0Kv3_21_sH-vmy05F-@zvoQF|`!Fsjq3s}vZ9EX1dxAI$> z0|q(A(nb9N^;_c{if{ZxUE9SP;BpT2b-LP;bEpgTk_}9x`0sbmp}tR7|L`1&#hnpv zoK!b+%Mi8MF%%2`PMkWc{{BbRd^(hur%*p#Z|aX5Z8rTo;`KL8WBhA~^v8^!kR0;I z?4JpuKj!~}5dE?6S3uGqe;A_s8?usnaK}Q30Et9!x@)hvMj9V&wJI#BQsm;%r z*t2DN5Y8==L3rUBHII(j?*>BYn9F}7OpaNqnoh%u-2gWR;TbQvR!r87zPzo`>Eb{r znB91wH1iM&G)ITx!z7;$lp5Nhd^%8SIEs&nm(q^`(mI~KS|(ZR;X?&+7#Zkm*7>s^ zLS*e}e?XG8H_LR^HkWB7o;LWt(%@>30X{0zV2m5BupU9$%~m$p?*E@MRURU-I|fZ& z<&vh)J*hpUse?B{Nz=>#!bDSHG+~P0{3yUOe#6UkYjr5c_&v=pbf!@KowFm>BSt)) zVH?-@y) zTzAHda;>PN?NNngcNIrsY@2y@xgLh*5mAo^n|j)X#|=+AlL`W5uA5sf9c6hxv$^&C z31WC{c^Dx&%D!Yk(otS5*B#|evJ*H8=;DD6Q?5Hsxl3#xD|`r{^qkLA2$SbD3?)pSb0`Ba1sHuq-7xYGohAQI=qw*FU%jD2 ze?lpAND)4YAX><8T2l+T9QcxD3=74LrYdNmewnBOSwFf$w@_k*ZlR1PM9m1WP+_hO z6XiXd_{&&Zwcr-yy%Le>D^t{`L6`phNmsN`>^V!YL+M%cl}V|5`r{ zA^O*r0zlHgw&!ZFRDf@Ehgax%;6Scc|1vHij~L)su7D@|#kO4d&p?#J3un6e*{NLH zer?|QFji*xC3Lj5WhT4ESb3gqgKc>_^&c3ZHV>)a`+tbd B_QC)F diff --git a/desc/examples/WISTELL-A_output.h5 b/desc/examples/WISTELL-A_output.h5 index 45e1f590e71d7e5c2163a9c3d7f1abb2a48cd413..b45b544cfce49409d2ed23001e104a891d271674 100644 GIT binary patch delta 33350 zcma)F2Ygh;*57+)H(LTBg%XgGK!6lV2#BF4NC~JIib&N25$Og*rHP=3fQW>}T$&IN z5dkq2SwKpt4-heyrwE9lh=_mzeHan&J7wnX-F@#f?C;0&pSe^2=S(|i?&kFC4c332 zG%sGP|H-p4sf|;dSfLqX66`g+Go8gXD%tpx+^9)%qozqIE$(mh%mYuQW+yjGZj$1C z)7AfHlPvZaarnx-<11^rU8po&Hai>B9PRSf%UhapIFC+oDUHAx$PsaD@<33H3WJ z3b#M+ZC`zLRDL^tP$H9Oy`%Zwbh@kPnUJf<#qwHaBrL}JFn7;nNW1k&2Dtnv}G1&FIk#^NxJu0mtm!w)( z60iBb*e@KqxOAWF=|VRv__k1;MUtAt5q%o!)OBNiCGC*Durl{6zo$!EV>C(9YI#di zT>7?Xik`pUr5!e=Rn}_d7Up|nL_govgNH@>K0 zuVfTA(lW8ZMpk1j!<)6rrCl4W61z0@0mON}lGa+9E=toi zU+y(~C0%5IPG=vmk21EUYw`Nwe9Dp6UITKM6E@=8gOnGp;=uU!T68swqLWU!m?B%| zQcC1jvqze7y-G+eUrBo{M$<2)(L*n`Htj*iL`erNyqaY!QHk+VS2H_sLf}Nt>WDax z2w2%s>#OTIoqTsXYoEHJGI|6GsdR`EQb}Jar0{7hljK*WkaFJPh(N^H==Z;63jf9ML;kENkO`f1_ik^f-*9L zqZt!}&mx2VIV6z5XE`A;m}A!FTbV-)j`7_YOANjg#NZ;6!Rd-YpJZ^mWU%-MN7#vcE2V{A@fq(Q zlmSI$fA0RoThut#SF66`x-N#oLs*&|nonN?v#S^n;oRS_-l>rIY^-Omk?eF=dvNM{ z7EP9{e2v^Ys69gIR(>uQWZU>-EEY8iQ)>%c!*^+UA8OH}pq}7~cKj($w(xr?&iI=g zkx|Ox6f<${EOBo)vOI2&cEX4_3Cv~2yAn@Ca*h0Lvsor_YU5P-U}{Tmx*Zp8m{C6^ z#*lcJ`}TeZC6z$*yjGL~mUBIA8>Z-l>Mj;4YQ!37L;76Ld2hZWMHHBH7D6=0=#;mp zVTve1x+=mn8{XVeDo)6dFFDnbuZd!l)l2XI`28eryB1T;vhq`tb0oR*_HjF5Txq37 z>;nRHr4**ubr`+dDDi1Mbtq@=P4{QC1{&i((Vo+@b{JPb(bnmuxxU3awOp5;zu#zh z5F&hP{HeAHriuPD=m7wcteR z`hzjD5?x9d_aO1c+5h-isb(lv5&_x){TrK{mUl!-K_p1ZMz7f2DwApqYdPSVQrAXjQv60115 zR8dmU=qyvv?YvaG<2oNMuG#I($!*DrSXhDTz#R+h%Hnj=6R|AeWr;mk14eRT@0v4brb2r|H3B37n4r6P5y*VVhT3>$#{(CH5hZBsE8?VE0#;Bj5 zw+hHP)@af;^|3<9a1WI6A9Hg<9Q9h`Qu%I$3_{->`>J1MnBLc#W;GcRP z8B|6Tgz=-ES2T(%xtkc>8t5OJ{l|7&+g{noKIo`pv}>rxMx`b5pzK4X*lelobzg+j z4(Fu!EwKFU6p)|w1l6Daggy+Z~FmE`Y^m6rP(_J#sV$ZF;|@)ZJV*8gN_FW zI2m0BgtMkN(VPZBW>1$WnL-d&-h}b;gsP-!InPjoGG((a8PigrAUM7xPdV$)l&Ftf4z<_$Lvr*wC@pT%ppG>;&?)sYNDCYAOAWV-coY>dxq^_NJTo+q&L`XY1kN&_C+b;^bQxmY>!X5;1|50R&=}saP#l0YM7fJb7 zlfAzIZTycrOw;2Oc@<1lC635DWkmPUC%FqA=hl|@#gw|6X+}|BJ;q%tjw5YGY#+VK z=HyRXdPEN>fM?c0Pyo%W8QrKsNL{`WJA^e!D>3#$+i*K4N(WH_oLMhr1LTFKbZ8(! zYhF?W<>yF(R`+2(4)v1+4S9_tX1p#58Vij8mEJEhM^GI|P}WF-NKlRuF@g!2IvOIG zpjV&^tX}g?HWr&`rHR%`hP+dspCmHJ=$r38-%>Mjp3`F-n~)ij_U4=y+k39MNdEo?91|$D+g3 zMvzgGt2>W#HQFubV5&HczHPSr#gqjmh@|34fmy=+CqYV_A+r=Lp)9ar7HeJwIEpkj zuC8LQ88m03;rYrqmf#s7l4t2g%S6v6(E$?tn&_$E`d`+locYX)#TpgyXkCtz)!N)a zhA9IKQ(Xlt>_oH|Mz*hCxOPbU2UAt_3NA8DmU;MNrLKi*l)8Q<)%9~pckfFa@xoMDYuu43MM2ysfzHzIcU_eB$+|E#gI0P!@KYobLaS^>r5Mlr93 zHc8}sp!=rOfNhcZ0HWKq4XNZSsi}doZh`cytOt+)+#I>`sST*CSt|+RVo>ltK^(%{ zwh+W2yk;{%9>TVrft-v#q&Qi#UvW|@Ik_b{u^6aL)0a!wW?=c2IXC=ust{Sp$|Zt6zADDu=VrJTdx z7pdZ5ll1|BV5;b(iHxsw<8Ehdtr!WFFMrxo$yb=6!3rGnL!fpR{id|@@I|Sei(Y5v zN81poo$i$!k+90aCzzYJ>k6j2ehpu#jT&`sMBu!0w7*pW{Wa^Wo-_g^rFK_Jtu0jUms(_uhmRX+QWc77`il085M3OOkf+TL z_OpzR9CYxusmqTXG3+NPw60Jj3QZqh4iy#j2!xgbDhfi=!Z_-*TdM#F2H|qj+HnrNa|plplWB88OOY)HX!D*(iDE0nzI;cl!HaRRw4;Rzm-@bmxWbYV`T;to%^ zF*QPjdo$ee389>&%)I5WK4YBB>q2#bnk(F6?r_TewV6B8d)?Eng*yI~)$WS27WE&v zLqi)URKCZ$s}Nt&XfsFH@DmaE$~o{pR!t9i<6n(G2Yzr^%G)c zxUAf@sl|1u-Z;ux_a1ZBqZZevoNzy+LgR#ld(4?g_XV^hZ*Exqga-GRsUdm!8&P$Q z$(x@{l{UG@{3+ht3RSeGRA;k$%yU20*_`Tp0P8fuqeYB2H9QlQ+NHJe?uFY1ZFx+! zyT?M)i1Bn{uRUq@gZG%T1C6Qts?Y=+PMz;DTLx9th3-CRU8%;0?lJ4bl=TtL+6@O% z-AC^wt1;%PJ4)+LBcR8JbSllV?-J( zZM+G{Fi|F~XnwC-aT+gVS+XB(mvcB-hWk9kVV4ZA-pwbW5*Z#+-N83!L?2u!l^ab@ z?ZW$lcvsSiP~O4Hh1+H^yi*~{jkY1q0V_JW(QZzo+;C>oGhn#E<_ElJVs~9ifL^rx zhanbr81-p~F%&w-yPomT2<1mJCZ5x`;&bE63;GGNN*E`9)T7CNW?a6gN3-wDck3tp zxCZXB@WiYefg1^nn}nLGBzNZ-ZWQ=M`gG)`mV)p6Ble0EH|-$`p&@>&UU7HvoG9!j zjKW&(`NpLlB3hL67QRt2?!TQry}wu%A<7RE#;<)G^NssQ!P7QlgfDxj6P`Z$yyzMs zLSB_m8@&!so4+C6ho>%Im$!r?GBR%k%P39Cq|hHx26~%TnTD>7YzmC0YsmXPmP2Ff_`QtLEOqV zT0;<(uK6y4xRxL;G1V>oq2~u&Cu*x9Gn&# zVx&K5AF4weXj4D;AXR~!EjvUIIot3rgviKGj=iNOO zjXj#Zwkg+okjz?4edCdHPQP67*kmmQH>bD`s9IqY8m~v0r6mO=Z$PfUzAAM?cx;;xPrp~1!1>VU*ROTme}rvIhkz7#{B!mP6Pc}qFXtv1$0b2QJ(69R*2DI z1ks;0qc=e`SC>2zxUO0pG0;()Et9@&wkZ`T3;MI7y9H*kxN0b|>cZ}5%;KB(fM~B9 zF>6G!7VYc4MpV$W82Nv^{6E=%_&vtFD0{A$8*Vo|Ct-oxTG1XYQWJHb@1%R4OH{tk zZgf5Go-GcAa7*kiBczTa#$qUKD8ynNsiSLc2f4C=!PL1upYNhUe zTIj$XJT*acjEzm~mDmYY`iYRUpKB-F!axi5nYz>jv|KfhCy3&o(U2f2;oO!4K?%i_ z7693TJvS|oj<$~}I+|xlI%2nR|I>F69bbIO!Rueksjto{!4|@c?WqQ&|K@`Pk&uv% z2$7J=odE6f7R>6epAvG~i{2^Zn)D#V@#^%DiFyLc4)^rd#KjD|Z%2d&)}#JV9f-1$ z@x()R7t?X+w4$T*w4`H7J8A&Z@l85Gq@!MEf=EZBUIdYjnmz3n@006$D8lqUg<}Z4 zPu&JEQEE54Yz2q^+xs+QvLf!hB<@EdPTn}j3+F~|TUgJbXmUN>-65OeNeCQc7H=OZ zWEy?jY|Y0~1{}$muLfqZc)TcahRpK9BY$d0G@?-g;W&=|kt=}?xP9ZwmM z;Qw_zg$!p3td6Ii-<$eMHKXIHg3Ft{?m5xvIXjxE=Nkjsd7{PBP@%GVUUGUfw|GVK zmtYde@T~n`PN)@P|7~Hc*aRb>WUI>+88$0WZ+E}r%Y4u7)b)ANkllVF8_Tu>+p(IX zre`UKzg*_z6?+XdfDGG1-XLmFZC{u}5Y=|Zn+Q>D^X39#+xFetN^iFTF|4g&cND5@ zqMCPv#YQ?L*Y2D5B3yqJ^&kb?NFM^k`1F?L|)Vlf!su1b8 zIg}uhwDn1X=!3epU5*^`O^kq^5To5zn~9%z-z;ME8idH`ln($gqmzIK-T=K}vgb9? z5)-`(=!=(dCg`@_?n`_bXM%MQ2O0sMg3kako8K!oFRrADk80Jk2Incky6t0cDbEt^Uev($=q4 zhv1BY5LN4&Zve4sb%Ca_YV`%ggEiYk!%Q>=%Jw@nR)1%g4vh(rf(PqzK&)OLL0THD zZHuUKRIj`*38H#k{FWf9*R=}-QN7Nbw{o`!ia=kKe)kutRC$+Csh%LoeXc6FcG;|; z(^Y~fxy+!qomTt3%Oo*Va@QyeN$yu`?LU}Z`4y0qT#1z2-%z=llTmWFgCrO2kjJ8% zLVBK#-p_)%{D73)>_Hr{cCeIO=-*0mcg-ek{abZN9I~O(lH@c%EV=vsR+38s#FA@j zqE;q)5X!bl&hC(sI}_$W$=z`qH!C_4Usl`x=0FklNQEB=3a18zTLgt$28CM%g)8Yi*UG$wOQ&jF8enTUJ@B$%dw6Uu=0P7^7W|LyYgE;- zPB#yG9Uzsp#~mSTy)M`(sFcqBtA1t~89`-q2`Zy&P#F*T%Xma}=HZ|+9)Ym+x?s1U zGL)HH<;U}g<_#Ca#X}%`9*TB28Szm3i?oS`;vIrC6m^;nX((19NJFu*gSUFLU{$Q( zkcGuTVd!2I8){(M&=XK0emebx1+sf_e9%_Qa+iqGD)(ZTgYuwzF~L?Ya4NT46sA0C zyrvr09~IBi4A9JMR0k9Fg60Kz7stUvp56MJ<6%0)@o1TYlqxJ7JC z5HEAxO#rydi{{(gP->9*_FAfg&9^n%Iif|)bcZzGrgnC0iO{?EG_Lh=tP|7vJEY-u zNw(wV2>sNYA@2%tM-!`?V587*+r^Dc z(hfG68VW{$q-{anv|Iq8qK3)>&pbd`Xj9w>AfFfP;Uxb3V%S{s@r1Vs;wEv{JcL-| zqFg{cnO6)~iW&!q)w6+#nwh9IlueU4*=&a`a7deN2Z%?Tt*9IYecT}a?M98zcKKrC z9B_>O%*d|kI4HC?s1-N~^nH^cP68jyCy0~4#^nTY5?HYeAa9;Gz8A=2_CB@5_5Bde z4d2*ndM5Lz=sHENa4Cy8qQ?>!zZD1de_|BR01J$J4b_39RsDb!k=M9^{C{|B{=OHqZ##UVdOcu@KS~fKc;r)rD8cWJ z0b;#=1iC;Iur^T2>uLG_DEa^M7WsYfxsu=6@1*?ZpLY0GTo=tM`1<_lXsDYE4+x@g zpx=t?Ry00g6t5OT#Y-lQZz>vrUT7=5=CSB7L8S5L&j=!o*Uu3|8qb~uC~3URG%7P* zm)j0`!G3EB%#!$`_^K8m26UKBk@`&+n<59zfLVrThj*C8?QD)bat-St(tf2HsU0N* z(HyCzhhRg|h;e*x;NszTgz!3q%RhepyiU2%9C|{#&Z@w^Eh;zfRyZ zy2m(%>U*Rn?+rDp`=T5{)a0|}5u#A8hXG4(cMXE5m4Ds` zkhRiXCy*97RZWU8({3A5)JY5;Cnm zLL?+N9uO0<6gt3rtM!1m`&&%3(?kaVv3W8(DMXql7u_EcV4f^?`!==;$<<7k*OSoP zX!xgwau#6gbe)aEJ>8rQ2Ek+Nbp2GIqh)YV?x!J)Dxyjs66or9CMfyPKu60kcxk$|mj$14qJSyInO2iE><4=%SYkw{%JT55w zd{9`u0jA1VZ-A-rcuwzs!)pS*;Z@5#s!fFF*t@e8*jk2rTeZK%2}{M}#?cxE4>G>j zr5w~rgabzuO6JjxRU%?_wL-+3(LPPhFPS_yUBK*JV91j1Np#p@HUi9mhK7fskS8Loe!MzEgn^@)s{xzHrvJllm(ZG8Fi5+zYP3- z8g5~R%yRu{?mX@y@tKf+uyr>&>+svUqh>wFb*IVxwV0`9!6uVP0u|3Q z;mv-O64-K^Bfh>XB`^#Mqz%s5=ZPDf1+TwA5NAPcJVKlWBPIaiS#ZKxH48of%5x<_2;R`>x`mY94VhE*jr~wAmI^gZGm07i^k6!V$e5^;m4( z5gAyzWu=boLe(J~V|o%qj+%5Oh_>!wtK{Gz(?N6S2oZW@o|*W&T(gLu=OaWy|MUW4 zLOVSWBA1nKO!9g|925G4iG~1TTlbN?5NYck4RLJio(G7@%vWS)zeyD%ncLqYh;xAZ zZGt!lM64u;Txu(*8$XExD2Hv`+prvE>lPE%L5t|6izDFh#Fs8a?*_E3!FMi17adQP zWbJODA#7SmeA{fJgGRLf$^?r{S-Y!-P_|;3ty{amQEl-rQVM15{^Aby!L)Yoy-i)h zja{MQVc1%VLmu|7CkWz9suxs&-=E3tikA)f;zP=UVn4gVD(fPX@nS%1Grj&14e+(1 z1S+@KOpkmLsGk#xlzxucPx|@w5LUmfKJwsaEW6vq4SvsP4eo z2O+9f_q~8vwaTR`)v6AN6)Mg|4NTN5HN-MjV-JK#i)m{}!DDq0AXcxjke0^k_8n9? z(i*XgAgWjPK7y!T!#*d7>ectSmAO}-2zu{g!Z%8(BK}QEHIU3Y^HmkY{%uyV_gjJ} zxr_Y*RU_7Ps0K;y1Z5$~eP^wIrP-NHfTZM3O3Cem%B_;S7$mvBOvzp2ndNOcvlvox zaRnUFVUv{HaVU``_p{lcCYM!rMEWlXNpe>JvEH2rit#FC0I}pcKw6gEk5oBH?h-+g+*N`kxmyHDa(`Nx>tz-;7z!iPXo3yLt!WzF z`vqoY)98kvmDzG-f(Dl|jn4XsCK5D_CKU%+3@tCQP#!fbhctvdyo8T48#UcT3!r&H zrqM0%kSRR?h$%g0qOXx0(=x9Ei)7Jn!E};01=QBj;*F&um1*?LKaGuj9rR-`;_efa9Ykd4t6eelOEBkd z@pCkdc1eKeY#P0S^}?|nUo(j=^LHP0y2XC+DSNm#&EwMScp<&6+Q#566+i#;wG#Cs zp5exLe5EAKomysP9=d29N|%hiMefuxeiyN!r*^hnf9woot3>z5E8`VuZ%%00ovDhQF^Zyu%j*gYpI^@9U05!;MRnPLvTDPNj@D02 zy0Na_9w{q#3`woHB3$8KSU$#^T;5eC_i@zRoJx4>yFz?IeOY-~HO2D{_XN6ao_LR$ zlJK^Dbqz5`wpiQ(5o zTG8e9*8G|Xy5#UvR-@XIa*rA#9m?R*`p+jMoB(q!BO%QRtlf8Y8uR= zpWSv8KNQ8J1u9L-tE%=0Xbk{lW(V`zMgrSISu zLii<+7jc{zS9W+@#?o2Nh3w0MwS}B*Sz+L8YZ)T<7ZqEE7_ie|XImTu{LZ!pG9;aC z%^(jiz^&l{Aa=GLe?4$xx5S`EKuL;mZ(>dplo=5Mf#J zS*M}!tnE-AJgZ$TYR44b0;I`C4PWAvzP8vnY!m*t*p0tvZN|M_L>+1A{ckf`c#6rWA5=9 zLEJI-TTBpl%v~1&WV1xS{6HP?>~hMhGWr^6nI2atj}QAixf}1N)a-$x&z zfU80@zRV#mZ*rpZ@Pyk}{u8I$Y044##dDD26yLqI$`Rj_vC05`Uk{v%CfoQ z!W+Ksp-RKIL9Dse?nynbfv$ou=TO0fpyY|{Wl)dt zZ4axG;7iEXagVQi#K1q59_IjSA8==hVpPwQ18Z**lstvK3@UX~cxK=cmQr6GXkAeHx(D`wOi1YD0G8rc=6io78o(dpGVhXuWdp zt}phmd$-&ew_1<7LL2CSOK+Qu{Tb__QE-`Rq;~v55Y|3@ZBz5Knso7VB-n4bx7_!n zc(o~Ad4;m0e>bcsFpK3zC}mL{IrksRf@Z*eCR@V+@nD>24!l=PG}}b;c`(xMVVc-< z)yeOmT>cMi92P?=cJ#I^b+V)P@_B9^a(@w_#eCf3Cwc~sUVTS1d5*_v;}S%pr}yo# zu4vJJp;J0~BkgQPNTbf!Z2O{J($Tvk6mvge6toci^+h}p>BGSUL#6>BcA}FpOhx)^ z7h#Zsz8HQ#xvztMGg!ngQvFW_#QU!_6LsFE;vNOWF5ThfT+*fc_%_xQXqd&Ndt=xaTUK^gT;RTzUd>MJWF=HDINR3otagHlxY`_;u% zd>1wns=|3#_r!29FcIt3Vi{xi6KfO3?zgOikh@=dhw6R`Antxk6SX%{S3vB(_0)4o z_igiUodND!{KYp&#-nhM>if#-+z9OZt(uH+0qXRPJ>1yS*Fhc(y+;#%g1v8_j5yDT z`B>{G#$q|9=WQZ}(DOF71+&86pcGRW{kP|BZC@;nS$YMsqjOEKz0j;KG3>9v;bXa$ zOB$#2uHU1a2W(1-T~@IjNZF|p{WqT|PX>M4Y>WGF9@vT-DXm;NY{@lAS_$p(d)F?v zaj|!86D0V5y=#e`xu;g|+O8d@B2vW`@7nN8m&Lob|Af=D<4W&ZzkaTN@UDe?=d^g& zQigEdijs2^l`ftfd2=t#ym z0>7NV70;fW(>2p9%&%v9}C|A%_Zi>zUoi>%5=t9$o$s`BXcWfAY|rtLx*`1KW?)0jfu{f z=prEYmW`d|lHRgw5XauK@RN$Rn3Iw=&s44(X&dwkW29}<>x_}M9SayEZEL-V^XNdF z48^h2EES70oo2O{xunxNE%HXLVW%h=<=Q6RkJV7AW68 z5RY#4eZB_=;!a4w1MwUn9*8#-dxJjY>QSS1e8db1)5epR2r<{jGbj;s?+NV^1 zQUFO2eI`ZJ9%{FW=(`{hT{A^=7E5Fib%RVSqEr4VPo8E>BKn>%iRcVM7SR(>KZ|HI zAQsUJCYoZR8Bo4OL}#Ul=0XA%(H1}~q63P(K_|F+646PY)sHXYPm5?5>ei;lyfvgkT+~}kdw&rhF*Xa&(?xG zY#;jFY?l4B>TgBZ#0Po9n!!V6wF96P6VYG*fUo&Ma?=;qKE=kjMzW|zxODUkhm&P^ zZ6t?T2W+-+GuWb`W|n5BeRG`dXk84zqlD=Sn;i~E2y%rbWp~7rm;WGOxjIDg$`v;G z0@z6SErAcaP;QWqSR6qR-x8SfBPBpr*o`QNWmnipv#XP#1!%a$KZ>BQwHwLLySL<} zYIgI8+7{}w-q~swcxNjO9$OD(>3C<$e7!~;-P%JcoV-=|LBLjHNr#|P;Lo#|zv!*< zbP6h?b5I!>L1lFDmth*tx(1c;5QMFUGkpEV+8gCUP;Kbu1H;+Fn!dws{k%whyn z%Gz=)T=@GDwg1Kuv>R7h9eypg@TU8?->9ocRyOw3a<9|d*Yu5yaUZQJ-bi+{9rx3g z?r8Bzs$0G~@nd`Us}W*XSNZf#FL-*j2R+qXzN!P=_eDkx9>#i{CL2_73OuKmCys_; zd+_Cnr591bV1^Pq%2ONBp1UHbhnCx?RMb#epk9fxz)UL^3xK1%IlcR9poJyw17hb0 z^Wm|P1Yt@Pr$-^g`mT%y#6~6E;r3l$?FrE|hvd7O0<{%LgjY*$x=*tqcjp-2-idDb z(+F^9yXItfJz;l2*VH=``Z|vf(e$?C<2)5n_Mm%mhUJ z)lYeRFi=A@{TGky?{5nHrGNV3=;k~1#(nR(3(e8~ zC&1g)&LES5py4Qn@t*k)-M$AViWHtOdj*wF;FaJrpWQ z>Zw3~1)j2!bbGxdshN*S`fo_@}ZyLI)dc}aS>Z^REG*Kb>Y z43b!j=pAV7#aBhFtfS4wI$ieVANPqm;(k{jp;(_JSzquj(SWR9d5<8n-g+HDWW7Tn zL1ewz9)Obde=+OIKD6{Zw={H}tN?%a3*KYUhiCY*K6h``^f!q79sIgN^nRU^PPx&X)g>s4<$@+K+}AC-$=v}#5c~SI9U=Dhf&&oub>mLi*S$Ms7mq0L zg#zF0^ks#6ps!n8zU>hnnCZ^$G(ZHE{l9*qkKQf&Ib}EZ^Wnd#8`#fP8nprY`FjXK z?C0$$g4oY1ksiyH@VUF?qL074ujfH|v7Xb>)vGfBYTq|<7>EDME+u-khJso6%XY)x zhJhvK*ej~8q@Uji95rK=!vE)w>VKWUeY9GnzT0i88C7brNL6g=u~?*j zf$G$$i7isPy^Z0{@T~jaKW);iqemH}E;)_C-97Ehl?&gWv(BOYrT?Zy3$=N}*Tt-p z@IVMh!$0->iJ%hT5As-l&`TxDf@IcWvI9LpgMv!In-ZuGDm&bWuzG-=4oW^G&;#@g zJhoPb8z<|^Fbu*m@K2RK9I%xIyfuN=LG^rOVD0!CiZ%J@Ko8Kf@Yq^AZcMGU<9jI9 zuzC+g)rMD7tk2bZDC+t1LC@uTD1Hyni|7Hm&llfQ%(v6uoI0J!+Li`6@iXUXHN{Nt z1KLa@N<0f%g2!sntS2cu&ZixE5yUyB*JB8Ajv3q=5X~{-<&GYC_<)VP{dAa?dZJVB&jdM$!@MStfs zIhm19|98b#_0eYH2G5#Bw9Y|@d_6n{5c6g4r1+``i215+qBw>02AzCGFX6mgeVi}h zWuQtlgJ*1V>xni`MIgoe@m{OU6xCEm}UsNsaqzecn42P}#1=g&EVuxTNwl+Cs`X!^ChDM2Ze zp>gDS%2q71&H53?ivP@Rx%Eu(%FY;f3bUE_^DCajAo&JnC>nOYPH{*>#n}WwmPy$` z6^!%Wv!)8->Dy+DdxNr|%$v=z3OWx7uK{)qt&^|7@O0Khhc{_DUaN2IWPk!!mVXeT z`l3MHJNBW{y#x7BHV?CX&{`U1>$}#xnN4C})u6_DY~owo(=n ziqBHJ7^=DgKP9WAvQ0{5X6FE@90-!i2~#ScLg53w;W^Y=7Rn*BzUlu`trZInBP5|5 z0mMRC1ubBqYyreV*=eE!COQU8ScGy+3gv4hlv|LJ$20sm8P9#?_EXg)l!F9GC`SpB zP`)KdLixr@OSKGHn4Xwn6#M}jq{-i7QuA7U#g8z^hQwJxyC2J|A|wQ5NIZRj#y=Sn zX9bzqEHC2)2yV6xdDxKH(ri=L3}60D5Bvl{h5<*Uot8hvIuX(^nX>^gne$Dw7>I$m GPyP=~Of}&E delta 34061 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 S`ul*mUoWxHen<~-AN)Uv=cYOU diff --git a/desc/examples/precise_QA_output.h5 b/desc/examples/precise_QA_output.h5 index dc63e62b6b02697a8cefce22c0d6088e6ada092c..ed844f2b5864fd30df4f33424b7b12ffff45f5cf 100644 GIT binary patch delta 51094 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 XIB^pvN+^?nVkqnBR5Qo}al8K?`H#$* delta 53395 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 diff --git a/desc/geometry/core.py b/desc/geometry/core.py index 00b6b0bfb2..0e7bf9c226 100644 --- a/desc/geometry/core.py +++ b/desc/geometry/core.py @@ -5,7 +5,6 @@ import numpy as np -from desc.backend import jnp from desc.compute import compute as compute_fun from desc.compute import data_index from desc.compute.geom_utils import reflection_matrix, rotation_matrix @@ -17,18 +16,44 @@ ) from desc.grid import LinearGrid, QuadratureGrid, _Grid from desc.io import IOAble -from desc.optimizable import Optimizable +from desc.optimizable import Optimizable, optimizable_parameter class Curve(IOAble, Optimizable, ABC): """Abstract base class for 1D curves in 3D space.""" - _io_attrs_ = ["_name", "shift", "rotmat"] + _io_attrs_ = ["_name", "_shift", "_rotmat"] def __init__(self, name=""): - self.shift = jnp.array([0, 0, 0]).astype(float) - self.rotmat = jnp.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]).astype(float) - self.name = name + self._shift = np.array([0, 0, 0], dtype=float) + self._rotmat = np.eye(3, dtype=float).flatten() + self._name = name + + @optimizable_parameter + @property + def shift(self): + """Displacement of curve in X, Y, Z.""" + return self._shift + + @shift.setter + def shift(self, new): + if len(new) == 3: + self._shift = np.asarray(new) + else: + raise ValueError("shift should be a 3 element vector, got {}".format(new)) + + @optimizable_parameter + @property + def rotmat(self): + """Rotation matrix of curve in X, Y, Z.""" + return self._rotmat + + @rotmat.setter + def rotmat(self, new): + if len(new) == 9: + self._rotmat = np.asarray(new) + else: + self._rotmat = np.asarray(new.flatten()) @property def name(self): @@ -142,20 +167,20 @@ def compute( ) return data - def translate(self, displacement=[0, 0, 0]): - """Translate the curve by a rigid displacement in x, y, z.""" - self.shift += jnp.asarray(displacement) + def translate(self, displacement): + """Translate the curve by a rigid displacement in X, Y, Z.""" + self.shift = self.shift + np.asarray(displacement) - def rotate(self, axis=[0, 0, 1], angle=0): - """Rotate the curve by a fixed angle about axis in xyz coordinates.""" - R = rotation_matrix(axis, angle) - self.rotmat = R @ self.rotmat + def rotate(self, axis): + """Rotate the curve about axis in X, Y, Z coordinates.""" + R = rotation_matrix(np.asarray(axis)) + self.rotmat = (R @ self.rotmat.reshape(3, 3)).flatten() self.shift = self.shift @ R.T def flip(self, normal): """Flip the curve about the plane with specified normal.""" - F = reflection_matrix(normal) - self.rotmat = F @ self.rotmat + F = reflection_matrix(np.asarray(normal)) + self.rotmat = (F @ self.rotmat.reshape(3, 3)).flatten() self.shift = self.shift @ F.T def __repr__(self): diff --git a/tests/inputs/DSHAPE_output_saved_without_current.h5 b/tests/inputs/DSHAPE_output_saved_without_current.h5 index a8f9cda7aeacd187f029ce7a178a2399a96a326f..6ace46fd24f3b2c3a36ee053d26a96f01a894f1e 100644 GIT binary patch delta 14484 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 delta 13777 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 H&PD$VRq=KR diff --git a/tests/inputs/LandremanPaul2022_QA_reactorScale_lowRes.h5 b/tests/inputs/LandremanPaul2022_QA_reactorScale_lowRes.h5 index 0d66cb21ad093c79f2ff71983f0382eb1162d249..015d8e71de114672d0f8533ee96374f5fc4ce96b 100644 GIT binary patch delta 25947 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 delta 26370 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 I4nMv1e}~c^IRF3v diff --git a/tests/inputs/LandremanPaul2022_QH_reactorScale_lowRes.h5 b/tests/inputs/LandremanPaul2022_QH_reactorScale_lowRes.h5 index 1fd44cce4336884518cea5adf726b041b3d3d6c4..dfa7dd4a30b5db99cf43861abc21b38cad6e5232 100644 GIT binary patch delta 28139 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 d`V12eg>R|1^v;>}mYnH4gE~NYBhj@0r%4=XL4# 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%0D+(4CueDpG03ySoh3!vZ%RK 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 ZkOPc803|}ee+1400)Enlt*~J0{|DvU3TXfU diff --git a/tests/inputs/circular_model_tokamak_output.h5 b/tests/inputs/circular_model_tokamak_output.h5 index b827251cab0b79dab88d7de1019713a9b23b7d78..0d37b0eaa3dde220b65d23b4173ed39a26f7a576 100644 GIT binary patch delta 7014 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 a+Y)*nGwDNRV;d*9jqM@A@gXmi$Nm>>^|#vq delta 7626 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 diff --git a/tests/inputs/iotest_HELIOTRON.h5 b/tests/inputs/iotest_HELIOTRON.h5 index 92d5998510f80220ebfd685c3b827fea9cfdf48c..452eac9fb1c414995c41b1546756efb8d27f95b7 100644 GIT binary patch delta 1962 zcmai!ZA@EL7{__;Vf1Yj#$=W)lxo&e=5|wX)R&kJVuCOdB4(@G5O6W+n9HDyS};?> zGDQMXsE6z(&SjgMGh~U{HS~-v5SExOh^s>l62)4r0-~$?D^DJ^ z?X?w`;C^+TLPnNmG3)c9v}^&|i57&UVz z-lBOJcV$*8)OwJqq~}e&64L{{VvQ`t8d-6e2u^43Q~4)4AUC+h@Ll>=u@B^Zv7Fo< zi5LW~j{!EJBf*>k5g-?rn_6ggz%-5M;Ox#u`i#rZXU#)2Fl%4istu&6Q8jbV8@_3Xz|pR(%g<=FP;9^@Irw$QOi~64+LYA6(*hU z1RsBL5^$|AUw5vDk3+p6wfdkbcxP!-o=$w9f35id@ZX;rwLv~ii~xq$j**wsq5UDq zm1Yf4PG8&e;0^kc@}L58Ni+|-bQ()DpW+S#7C@>g1Ow7n@s`HVdB_o_2&K#q$z+8v z>VAhINzIRfTGAIm_KtP!d_q~$$#I9A=qv)mjtx9=v@)5q@*K+{*NR|CCq2I#^cqbv zqQU-MR^0gQ(;A;nkbylwRj_%|d>OO}k$t4D92^>9yh=L1 zAFgLLwly7hdD7AvPqlbX9`%re)o@3hbM9cO(u!AaEM^GEQBTFx%cP4=N}70)-X2~v zS~&zb@P}E@A9>GX5sY;tyA2FL$_|5nlaQz<-!wp9Ch9%__~%|TiaHsRZk14c32dEzuZJnj@G^zN8Ny5lx&1oR*4)CigjnY6rs8B^*E)ePm8?r3QJz`0%<3GwF(MP$X zKgprhqZWy>q3rjpkNWL>m#d8UWEv?kKzG-MJuX+YChJBAWON}hC{Zt#iOmm(1!2Wc z`Y=qXb7n52THAd?wzl&l+LAA^^&)L4Ug8EiE^`BGK4bJm3!tW2()Gg{rQrOYn{SH2ynyV#$Pr5%4BE)rT_jb;!Khuo Lz=%5zE8716YHJmx delta 2159 zcma)+Urbw79LM{+m$l{I0UesIbb~c!Ev+Z+_g3}fO7gRW&$ z8;uzvu#R#zt})6sb?CBaEY;nigVdJ}GOf&q%EPQMwHT&s@u4q9;yL$}KYjA{;kKve zd%wTW_ji8xre`%vcQk`m#=8`G)Tv>;3xIZnN>Q(7?h&JkejN544*RahcJ8XMA89^Z zxwFM__$m8KM>~Bd#l7{QNAGHD%>S>&Ug%$Kn-bTi8SJXd*P@I0dlfntaBVh43>c#r zZQ?egFSsU!-cRtgioHt^#(WNM%$hrhYIiwSq4DyYtn(g(BcUwu{ZrWwyhmXRoTYHt4LJQZ3DxW zbur}Knyf~>hiIfx8!Yn3^mm~s1mIOJBv2<-g$}*mHY6CtQ2`9$m{yz=Ku0vXXd-ww zUyL~+%q6R-I!}dS4gFI3X}GH8;uE1G%ITGZ8gw8e)@ceeYEHTzW3v%BG2 zZSlzdT=2SA#{N(B-67rC7U~6$tXD*BI zw#;HbC9^p4)Z*h_c%JXb_Al5gvxq%js0qiqW!R~944d*%*rgK`cG*v03%gfgO`QZb zbxMi`2(14&g-vwjz?Pk)u$CSQt3Qopfdy%*2hR!aGGF<(j^CJ-1Hedck8BJ zHnnE0(_tDtGX&_(<@=KLEF5Gw^*LH<1Dz865L9Z5Qx|f(9r{x4cF!lcJUw~S2e`cO zA$4?pM4RdUm^8EHGFH)(@4i5qDSRsNmq{}d_&FpeZ@DC=S~+?DC~amiN}K5&!`tNK z#rJw)D-#^gc2j?ybkifpk6pv{z1OK<{A(INDdG6*p3R*2$!{oZ`UZ_}7$@;F6ErDD zokdP`BC~OLE=wNQC$74 socJv>G`{y|8sBp#3z&j5!#IB%o(F+jdv%Fb^H9dEz3-&fd06KD11?fh_W%F@ diff --git a/tests/test_coils.py b/tests/test_coils.py index a2f8287059..55ac668e63 100644 --- a/tests/test_coils.py +++ b/tests/test_coils.py @@ -209,7 +209,7 @@ def test_from_symmetry(self): # with stellarator symmetry NFP = 4 coil = FourierXYZCoil() - coil.rotate(angle=np.pi / N) + coil.rotate([0, 0, np.pi / N]) coils = CoilSet.linspaced_angular( coil, I, [0, 0, 1], np.pi / NFP, N // NFP // 2 ) @@ -518,8 +518,8 @@ def test_save_and_load_makegrid_coils_rotated(tmpdir_factory): Z2 = coords2[:, 2] np.testing.assert_allclose(c1.current, c2.current, err_msg=f"Coil {i}") - np.testing.assert_allclose(X1, X2, err_msg=f"Coil {i}") - np.testing.assert_allclose(Y1, Y2, err_msg=f"Coil {i}") + np.testing.assert_allclose(X1, X2, err_msg=f"Coil {i}", atol=1e-16) + np.testing.assert_allclose(Y1, Y2, err_msg=f"Coil {i}", atol=1e-16) np.testing.assert_allclose(Z1, Z2, atol=2e-7, err_msg=f"Coil {i}") # check Bnormal on torus and ensure is near zero @@ -593,8 +593,8 @@ def test_save_and_load_makegrid_coils_rotated_int_grid(tmpdir_factory): Z2 = coords2[:, 2] np.testing.assert_allclose(c1.current, c2.current, err_msg=f"Coil {i}") - np.testing.assert_allclose(X1, X2, err_msg=f"Coil {i}") - np.testing.assert_allclose(Y1, Y2, err_msg=f"Coil {i}") + np.testing.assert_allclose(X1, X2, err_msg=f"Coil {i}", atol=1e-16) + np.testing.assert_allclose(Y1, Y2, err_msg=f"Coil {i}", atol=1e-16) np.testing.assert_allclose(Z1, Z2, atol=2e-7, err_msg=f"Coil {i}") # check Bnormal on torus and ensure is near zero diff --git a/tests/test_compute_utils.py b/tests/test_compute_utils.py index e1b8aa4cb8..02a6947b83 100644 --- a/tests/test_compute_utils.py +++ b/tests/test_compute_utils.py @@ -1,9 +1,12 @@ """Tests compute utilities.""" +import jax import numpy as np import pytest +from desc.backend import jnp from desc.basis import FourierZernikeBasis +from desc.compute.geom_utils import rotation_matrix from desc.compute.utils import ( _get_grid_surface, line_integrals, @@ -568,3 +571,15 @@ def test_surface_min_max(self): Bmin_alt[j] = np.min(B[mask]) np.testing.assert_allclose(Bmax_alt, grid.compress(surface_max(grid, B))) np.testing.assert_allclose(Bmin_alt, grid.compress(surface_min(grid, B))) + + +@pytest.mark.unit +def test_rotation_matrix(): + """Test that rotation_matrix works with fwd & rev AD for axis=[0, 0, 0].""" + dfdx_fwd = jax.jacfwd(rotation_matrix) + dfdx_rev = jax.jacrev(rotation_matrix) + x0 = jnp.array([0.0, 0.0, 0.0]) + + np.testing.assert_allclose(rotation_matrix(x0), np.eye(3)) + np.testing.assert_allclose(dfdx_fwd(x0), np.zeros((3, 3, 3))) + np.testing.assert_allclose(dfdx_rev(x0), np.zeros((3, 3, 3))) diff --git a/tests/test_curves.py b/tests/test_curves.py index bc39a608a5..267a147af3 100644 --- a/tests/test_curves.py +++ b/tests/test_curves.py @@ -25,7 +25,7 @@ def test_length(self): c.compute("length", grid=20)["length"], 10 * 2 * np.pi ) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=20)["length"], 10 * 2 * np.pi @@ -37,7 +37,7 @@ def test_curvature(self): c = FourierRZCurve() np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 10) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 10) @@ -47,7 +47,7 @@ def test_torsion(self): c = FourierRZCurve() np.testing.assert_allclose(c.compute("torsion", grid=20)["torsion"], 0) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose(c.compute("torsion", grid=20)["torsion"], 0) @@ -62,7 +62,7 @@ def test_frenet(self): np.testing.assert_allclose(T, np.array([[0, 1, 0]]), atol=1e-12) np.testing.assert_allclose(N, np.array([[-1, 0, 0]]), atol=1e-12) np.testing.assert_allclose(B, np.array([[0, 0, 1]]), atol=1e-12) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) c.translate([1, 1, 1]) data = c.compute( @@ -81,7 +81,7 @@ def test_coords(self): np.testing.assert_allclose(x, 10) np.testing.assert_allclose(y, 0) np.testing.assert_allclose(z, 0) - c.rotate(angle=np.pi / 2) + c.rotate([0, 0, np.pi / 2]) c.flip([0, 1, 0]) c.translate([1, 1, 1]) r, p, z = c.compute("x", grid=0, basis="rpz")["x"].T @@ -265,7 +265,7 @@ def test_length(self): c.compute("length", grid=20)["length"], 2 * 2 * np.pi ) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=20)["length"], 2 * 2 * np.pi @@ -277,7 +277,7 @@ def test_curvature(self): c = FourierXYZCurve() np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 2) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 2) @@ -289,7 +289,7 @@ def test_torsion(self): c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 ) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 @@ -306,7 +306,7 @@ def test_frenet(self): np.testing.assert_allclose(T, np.array([[0, 0, -1]]), atol=1e-12) np.testing.assert_allclose(N, np.array([[-1, 0, 0]]), atol=1e-12) np.testing.assert_allclose(B, np.array([[0, 1, 0]]), atol=1e-12) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) c.translate([1, 1, 1]) data = c.compute( @@ -325,7 +325,7 @@ def test_coords(self): np.testing.assert_allclose(x, 12) np.testing.assert_allclose(y, 0) np.testing.assert_allclose(z, 0) - c.rotate(angle=np.pi / 2) + c.rotate([0, 0, np.pi / 2]) c.flip([0, 1, 0]) c.translate([1, 1, 1]) r, p, z = c.compute("x", grid=0, basis="rpz")["x"].T @@ -402,6 +402,19 @@ def test_asserts(self): class TestPlanarCurve: """Tests for FourierPlanarCurve class.""" + @pytest.mark.unit + def test_rotation(self): + """Test rotation of planar curve.""" + cx = FourierPlanarCurve(center=[0, 0, 0], normal=[1, 0, 0], r_n=1) + cy = FourierPlanarCurve(center=[0, 0, 0], normal=[0, 1, 0], r_n=1) + cz = FourierPlanarCurve(center=[0, 0, 0], normal=[0, 0, 1], r_n=1) + datax = cx.compute("x", grid=20, basis="xyz") + datay = cy.compute("x", grid=20, basis="xyz") + dataz = cz.compute("x", grid=20, basis="xyz") + np.testing.assert_allclose(datax["x"][:, 0], 0, atol=1e-16) # only in Y-Z plane + np.testing.assert_allclose(datay["x"][:, 1], 0, atol=1e-16) # only in X-Z plane + np.testing.assert_allclose(dataz["x"][:, 2], 0, atol=1e-16) # only in X-Y plane + @pytest.mark.unit def test_length(self): """Test length of circular curve.""" @@ -410,7 +423,7 @@ def test_length(self): c.compute("length", grid=20)["length"], 2 * 2 * np.pi ) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=20)["length"], 2 * 2 * np.pi @@ -422,7 +435,7 @@ def test_curvature(self): c = FourierPlanarCurve() np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 2) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 2) @@ -434,7 +447,7 @@ def test_torsion(self): c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 ) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 @@ -451,7 +464,7 @@ def test_frenet(self): np.testing.assert_allclose(T, np.array([[0, 0, -1]]), atol=1e-12) np.testing.assert_allclose(N, np.array([[-1, 0, 0]]), atol=1e-12) np.testing.assert_allclose(B, np.array([[0, 1, 0]]), atol=1e-12) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) c.translate([1, 1, 1]) data = c.compute( @@ -472,9 +485,9 @@ def test_coords(self): np.testing.assert_allclose(z, 0) dr, dp, dz = c.compute("x_sss", grid=0, basis="rpz")["x_sss"].T np.testing.assert_allclose(dr, 0) - np.testing.assert_allclose(dp, 0) + np.testing.assert_allclose(dp, 0, atol=1e-12) np.testing.assert_allclose(dz, 2) - c.rotate(angle=np.pi / 2) + c.rotate([0, 0, np.pi / 2]) c.flip([0, 1, 0]) c.translate([1, 1, 1]) x, y, z = c.compute("x", grid=0, basis="xyz")["x"].T @@ -558,7 +571,7 @@ def test_length(self): err_msg=f"Failed at {method}", ) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=npts)["length"], @@ -583,7 +596,7 @@ def test_length(self): err_msg=f"Failed at {method}", ) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=npts)["length"], @@ -629,7 +642,7 @@ def test_coords(self): np.testing.assert_allclose(x, R) np.testing.assert_allclose(y, 0, atol=1e-15) np.testing.assert_allclose(z, 0, atol=1e-15) - c.rotate(angle=np.pi / 2) + c.rotate([0, 0, np.pi / 2]) c.flip([0, 1, 0]) c.translate([1, 1, 1]) r, p, z = c.compute("x", grid=Grid(np.array([[0.0, 0.0, 0.0]])), basis="rpz")[ @@ -650,7 +663,7 @@ def test_curvature(self): c.compute("curvature", grid=10)["curvature"][1:-1], 1 / 10, atol=1e-3 ) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("curvature", grid=10)["curvature"][1:-1], 1 / 10, atol=1e-3 @@ -667,7 +680,7 @@ def test_torsion(self): c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 ) c.translate([1, 1, 1]) - c.rotate(angle=np.pi) + c.rotate([0, 0, np.pi]) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 3be1eefb39..873b4fcf1d 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -885,7 +885,7 @@ def test_plot_coils(): NFP = 4 I = 1 coil = FourierXYZCoil() - coil.rotate(angle=np.pi / N) + coil.rotate([0, 0, np.pi / N]) coils = CoilSet.linspaced_angular(coil, I, [0, 0, 1], np.pi / NFP, N // NFP // 2) coils.grid = 100 coils2 = MixedCoilSet.from_symmetry(coils, NFP, True) From 2d64ab5db3701e9ba0cbaf934181d82c9c27c04f Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 5 Feb 2024 12:30:46 -0500 Subject: [PATCH 06/56] delete comments and change grid changed grid to N=32. Everything is passing --- desc/objectives/_geometry.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 71e450ffaa..a1c7aae7fe 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -490,8 +490,6 @@ def build(self, use_jit=True, verbose=1): """Build objective function.""" coil = self.things[0] self._dim_f = 1 - # TODO: is this the correct data key? - # seems like it because it gives the circumference of a circular coil? self._data_keys = ["length"] timer = Timer() @@ -499,9 +497,7 @@ def build(self, use_jit=True, verbose=1): print("Precomputing transforms") timer.start("Precomputing transforms") - grid = LinearGrid(L=4, M=4) - # TODO: what grid is supposed to be used here? - # TODO: am I supposed to use obj=coil? + grid = LinearGrid(N=32) profiles = get_profiles(self._data_keys, obj=coil, grid=grid) transforms = get_transforms(self._data_keys, obj=coil, grid=grid) self._constants = { @@ -520,8 +516,6 @@ def compute(self, params, constants=None): if constants is None: constants = self._constants - # TODO: gets KeyError with 'rotmat' when computing 'x_s'. - # why does coil.compute("length") work and not this? data = compute_fun( self.things[0], self._data_keys, From a64ce35f707fef26806aaf3e765d347c0a34af92 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 5 Feb 2024 19:22:52 -0500 Subject: [PATCH 07/56] add docstrings and grid option --- desc/objectives/_geometry.py | 74 +++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index a1c7aae7fe..eaceb250ae 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -460,20 +460,60 @@ def compute(self, params, constants=None): class CoilLength(_Objective): - """Coil length.""" + """Plasma volume. + + Parameters + ---------- + coil : FourierPlanarCoil or FourierXYZCoil + Coil for which the length will be found. + target : {float, ndarray}, optional + Target value(s) of the objective. Only used if bounds is None. + Must be broadcastable to Objective.dim_f. + bounds : tuple of {float, ndarray}, optional + Lower and upper bounds on the objective. Overrides target. + Both bounds must be broadcastable to to Objective.dim_f + weight : {float, ndarray}, optional + Weighting to apply to the Objective, relative to other Objectives. + Must be broadcastable to to Objective.dim_f + normalize : bool, optional + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool, optional + Whether target and bounds should be normalized before comparing to computed + values. If `normalize` is `True` and the target is in physical units, + this should also be set to True. + be set to True. + loss_function : {None, 'mean', 'min', 'max'}, optional + Loss function to apply to the objective values once computed. This loss function + is called on the raw compute value, before any shifting, scaling, or + normalization. Note: Has no effect for this objective. + deriv_mode : {"auto", "fwd", "rev"} + Specify how to compute jacobian matrix, either forward mode or reverse mode AD. + "auto" selects forward or reverse mode based on the size of the input and output + of the objective. Has no effect on self.grad or self.hess which always use + reverse mode and forward over reverse mode respectively. + grid : Grid, optional + Collocation grid containing the nodes to evaluate at. + name : str, optional + Name of the objective function. + + """ def __init__( self, coil, - target=2 * np.pi, + target=None, bounds=None, weight=1, normalize=True, normalize_target=True, loss_function=None, deriv_mode="auto", + grid=None, name="coil-length", ): + if target is None and bounds is None: + target = 2 * np.pi + self._grid = grid super().__init__( things=coil, target=target, @@ -487,17 +527,27 @@ def __init__( ) def build(self, use_jit=True, verbose=1): - """Build objective function.""" + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ coil = self.things[0] self._dim_f = 1 self._data_keys = ["length"] + if self._grid is None: + grid = LinearGrid(N=32) timer = Timer() if verbose > 0: print("Precomputing transforms") timer.start("Precomputing transforms") - grid = LinearGrid(N=32) profiles = get_profiles(self._data_keys, obj=coil, grid=grid) transforms = get_transforms(self._data_keys, obj=coil, grid=grid) self._constants = { @@ -512,7 +562,21 @@ def build(self, use_jit=True, verbose=1): super().build(use_jit=use_jit, verbose=verbose) def compute(self, params, constants=None): - """Compute length of coil.""" + """Compute length of coil. + + Parameters + ---------- + params : dict + Dictionary of the coil's degrees of freedom. + constants : dict + Dictionary of constant data, eg transforms, profiles etc. Defaults to + self._constants. + + Returns + ------- + f : float + Coil length. + """ if constants is None: constants = self._constants From 2ddc10f780a8a29d40372aa43ca5d03d3b6b59b8 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sat, 3 Feb 2024 01:39:23 -0500 Subject: [PATCH 08/56] initial commit --- desc/objectives/_geometry.py | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 2d1cef6950..8da4284567 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -459,6 +459,74 @@ def compute(self, params, constants=None): return data["V"] +class CoilLength(_Objective): + """Coil length.""" + + def __init__( + self, + coil, + target=0, + bounds=None, + weight=1, + normalize=None, + normalize_target=None, + loss_function=None, + deriv_mode=None, + name="coil-length", + ): + super().__init__( + things=coil, + target=target, + bounds=bounds, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + loss_function=loss_function, + deriv_mode=deriv_mode, + name=name, + ) + + def build(self, use_jit=True, verbose=1): + """Bob be building.""" + coil = self.things[0] + self._data_keys = ["length"] + + timer = Timer() + if verbose > 0: + print("Precomputing transforms") + timer.start("Precomputing transforms") + + grid = LinearGrid() + profiles = get_profiles(self._data_keys, obj=coil, grid=grid) + transforms = get_transforms(self._data_keys, obj=coil, grid=grid) + self._constants = { + "transforms": transforms, + "profiles": profiles, + } + + timer.stop("Precomputing transforms") + if verbose > 1: + timer.disp("Precomputing transforms") + + super().build() + + def compute(self, params, constants=None): + """Compute length of coil.""" + if constants is None: + constants = self._constants + + data = compute_fun( + self.things[0], + self._data_keys, + params=params, + transforms=constants["transforms"], + profiles=constants["profiles"], + ) + + f = data["length"] + return f + + class PlasmaVesselDistance(_Objective): """Target the distance between the plasma and a surrounding surface. From 6326552c3ec993981603a4860260767d33c35a50 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sun, 4 Feb 2024 15:41:53 -0500 Subject: [PATCH 09/56] add simple test tests that coil length of radius 1 is 2pi --- tests/test_objective_funs.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index 0e4096b7a1..90c1c51882 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -12,6 +12,7 @@ import desc.examples from desc.backend import jnp +from desc.coils import FourierPlanarCoil from desc.compute import get_transforms from desc.equilibrium import Equilibrium from desc.examples import get @@ -21,6 +22,7 @@ AspectRatio, BootstrapRedlConsistency, BScaleLength, + CoilLength, CurrentDensity, Elongation, Energy, @@ -493,6 +495,15 @@ def test(eq): test(get("DSHAPE")) test(get("HELIOTRON")) + @pytest.mark.unit + def test_coil_length(self): + """Tests coil length.""" + coil = FourierPlanarCoil(r_n=1) + obj = CoilLength(coil) + obj.build() + f = obj.compute(params=coil.params_dict) + np.testing.assert_allclose(f, 2 * np.pi, rtol=1e-8) + @pytest.mark.unit def test_derivative_modes(): From c424ec184490b3f48d5c2a2bc4feb02cdbb0cec0 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sun, 4 Feb 2024 17:09:37 -0500 Subject: [PATCH 10/56] add default args plus todos --- desc/objectives/_geometry.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 8da4284567..71e450ffaa 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -465,13 +465,13 @@ class CoilLength(_Objective): def __init__( self, coil, - target=0, + target=2 * np.pi, bounds=None, weight=1, - normalize=None, - normalize_target=None, + normalize=True, + normalize_target=True, loss_function=None, - deriv_mode=None, + deriv_mode="auto", name="coil-length", ): super().__init__( @@ -487,8 +487,11 @@ def __init__( ) def build(self, use_jit=True, verbose=1): - """Bob be building.""" + """Build objective function.""" coil = self.things[0] + self._dim_f = 1 + # TODO: is this the correct data key? + # seems like it because it gives the circumference of a circular coil? self._data_keys = ["length"] timer = Timer() @@ -496,7 +499,9 @@ def build(self, use_jit=True, verbose=1): print("Precomputing transforms") timer.start("Precomputing transforms") - grid = LinearGrid() + grid = LinearGrid(L=4, M=4) + # TODO: what grid is supposed to be used here? + # TODO: am I supposed to use obj=coil? profiles = get_profiles(self._data_keys, obj=coil, grid=grid) transforms = get_transforms(self._data_keys, obj=coil, grid=grid) self._constants = { @@ -508,13 +513,15 @@ def build(self, use_jit=True, verbose=1): if verbose > 1: timer.disp("Precomputing transforms") - super().build() + super().build(use_jit=use_jit, verbose=verbose) def compute(self, params, constants=None): """Compute length of coil.""" if constants is None: constants = self._constants + # TODO: gets KeyError with 'rotmat' when computing 'x_s'. + # why does coil.compute("length") work and not this? data = compute_fun( self.things[0], self._data_keys, From 22b4b635e4da903888d41d16ac852fda46e4b04f Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 5 Feb 2024 12:19:02 -0500 Subject: [PATCH 11/56] add to objectives __init__ --- desc/objectives/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/desc/objectives/__init__.py b/desc/objectives/__init__.py index 3dab7ef1b7..2d779ef3ed 100644 --- a/desc/objectives/__init__.py +++ b/desc/objectives/__init__.py @@ -13,6 +13,7 @@ from ._geometry import ( AspectRatio, BScaleLength, + CoilLength, Elongation, GoodCoordinates, MeanCurvature, From c874e848d37781f08538749573c1b65ed1499170 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 5 Feb 2024 12:30:46 -0500 Subject: [PATCH 12/56] delete comments and change grid changed grid to N=32. Everything is passing --- desc/objectives/_geometry.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 71e450ffaa..a1c7aae7fe 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -490,8 +490,6 @@ def build(self, use_jit=True, verbose=1): """Build objective function.""" coil = self.things[0] self._dim_f = 1 - # TODO: is this the correct data key? - # seems like it because it gives the circumference of a circular coil? self._data_keys = ["length"] timer = Timer() @@ -499,9 +497,7 @@ def build(self, use_jit=True, verbose=1): print("Precomputing transforms") timer.start("Precomputing transforms") - grid = LinearGrid(L=4, M=4) - # TODO: what grid is supposed to be used here? - # TODO: am I supposed to use obj=coil? + grid = LinearGrid(N=32) profiles = get_profiles(self._data_keys, obj=coil, grid=grid) transforms = get_transforms(self._data_keys, obj=coil, grid=grid) self._constants = { @@ -520,8 +516,6 @@ def compute(self, params, constants=None): if constants is None: constants = self._constants - # TODO: gets KeyError with 'rotmat' when computing 'x_s'. - # why does coil.compute("length") work and not this? data = compute_fun( self.things[0], self._data_keys, From b1e34eb66667a300bb8f8c90c1da2156d0ce0c45 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 5 Feb 2024 19:22:52 -0500 Subject: [PATCH 13/56] add docstrings and grid option --- desc/objectives/_geometry.py | 74 +++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index a1c7aae7fe..eaceb250ae 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -460,20 +460,60 @@ def compute(self, params, constants=None): class CoilLength(_Objective): - """Coil length.""" + """Plasma volume. + + Parameters + ---------- + coil : FourierPlanarCoil or FourierXYZCoil + Coil for which the length will be found. + target : {float, ndarray}, optional + Target value(s) of the objective. Only used if bounds is None. + Must be broadcastable to Objective.dim_f. + bounds : tuple of {float, ndarray}, optional + Lower and upper bounds on the objective. Overrides target. + Both bounds must be broadcastable to to Objective.dim_f + weight : {float, ndarray}, optional + Weighting to apply to the Objective, relative to other Objectives. + Must be broadcastable to to Objective.dim_f + normalize : bool, optional + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool, optional + Whether target and bounds should be normalized before comparing to computed + values. If `normalize` is `True` and the target is in physical units, + this should also be set to True. + be set to True. + loss_function : {None, 'mean', 'min', 'max'}, optional + Loss function to apply to the objective values once computed. This loss function + is called on the raw compute value, before any shifting, scaling, or + normalization. Note: Has no effect for this objective. + deriv_mode : {"auto", "fwd", "rev"} + Specify how to compute jacobian matrix, either forward mode or reverse mode AD. + "auto" selects forward or reverse mode based on the size of the input and output + of the objective. Has no effect on self.grad or self.hess which always use + reverse mode and forward over reverse mode respectively. + grid : Grid, optional + Collocation grid containing the nodes to evaluate at. + name : str, optional + Name of the objective function. + + """ def __init__( self, coil, - target=2 * np.pi, + target=None, bounds=None, weight=1, normalize=True, normalize_target=True, loss_function=None, deriv_mode="auto", + grid=None, name="coil-length", ): + if target is None and bounds is None: + target = 2 * np.pi + self._grid = grid super().__init__( things=coil, target=target, @@ -487,17 +527,27 @@ def __init__( ) def build(self, use_jit=True, verbose=1): - """Build objective function.""" + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ coil = self.things[0] self._dim_f = 1 self._data_keys = ["length"] + if self._grid is None: + grid = LinearGrid(N=32) timer = Timer() if verbose > 0: print("Precomputing transforms") timer.start("Precomputing transforms") - grid = LinearGrid(N=32) profiles = get_profiles(self._data_keys, obj=coil, grid=grid) transforms = get_transforms(self._data_keys, obj=coil, grid=grid) self._constants = { @@ -512,7 +562,21 @@ def build(self, use_jit=True, verbose=1): super().build(use_jit=use_jit, verbose=verbose) def compute(self, params, constants=None): - """Compute length of coil.""" + """Compute length of coil. + + Parameters + ---------- + params : dict + Dictionary of the coil's degrees of freedom. + constants : dict + Dictionary of constant data, eg transforms, profiles etc. Defaults to + self._constants. + + Returns + ------- + f : float + Coil length. + """ if constants is None: constants = self._constants From ffc857c1cfcdb36a4a060615764bd9e5481dbfc9 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 8 Feb 2024 18:30:40 -0500 Subject: [PATCH 14/56] Revert "Merge branch 'ko/coil_length' into ko/coil_length" This reverts commit 0e2315d2c42033e1e492cc57039d344c59512eaf, reversing changes made to b1e34eb66667a300bb8f8c90c1da2156d0ce0c45. --- desc/coils.py | 14 ++++++------- desc/compute/geom_utils.py | 5 +++-- tests/test_coils.py | 2 +- tests/test_curves.py | 40 +++++++++++++++++++------------------- tests/test_plotting.py | 2 +- 5 files changed, 32 insertions(+), 31 deletions(-) diff --git a/desc/coils.py b/desc/coils.py index 81fc8710ec..4fcb7248b1 100644 --- a/desc/coils.py +++ b/desc/coils.py @@ -817,17 +817,17 @@ def compute( return tree_unstack(data) - def translate(self, displacement): + def translate(self, *args, **kwargs): """Translate the coils along an axis.""" - [coil.translate(displacement) for coil in self.coils] + [coil.translate(*args, **kwargs) for coil in self.coils] - def rotate(self, axis): + def rotate(self, *args, **kwargs): """Rotate the coils about an axis.""" - [coil.rotate(axis) for coil in self.coils] + [coil.rotate(*args, **kwargs) for coil in self.coils] - def flip(self, normal): + def flip(self, *args, **kwargs): """Flip the coils across a plane.""" - [coil.flip(normal) for coil in self.coils] + [coil.flip(*args, **kwargs) for coil in self.coils] def compute_magnetic_field(self, coords, params=None, basis="rpz", grid=None): """Compute magnetic field at a set of points. @@ -979,7 +979,7 @@ def from_symmetry(cls, coils, NFP, sym=False): coils = coils + flipped_coils for k in range(0, NFP): coil = coils.copy() - coil.rotate([0, 0, 2 * jnp.pi * k / NFP]) + coil.rotate(axis=[0, 0, 1], angle=2 * jnp.pi * k / NFP) coilset.append(coil) return cls(*coilset) diff --git a/desc/compute/geom_utils.py b/desc/compute/geom_utils.py index a18e6deab1..15c62cb4ce 100644 --- a/desc/compute/geom_utils.py +++ b/desc/compute/geom_utils.py @@ -31,8 +31,9 @@ def rotation_matrix(axis, angle=None): Parameters ---------- axis : array-like, shape(3,) - Axis of rotation, in cartesian (X,Y,Z) coordinates. - The norm of the vector is the angle of rotation, in radians. + Axis of rotation, in cartesian (X,Y,Z) coordinates + angle : float or None + Angle to rotate by, in radians. If None, use norm of axis vector. Returns ------- diff --git a/tests/test_coils.py b/tests/test_coils.py index 55ac668e63..e9393f045a 100644 --- a/tests/test_coils.py +++ b/tests/test_coils.py @@ -209,7 +209,7 @@ def test_from_symmetry(self): # with stellarator symmetry NFP = 4 coil = FourierXYZCoil() - coil.rotate([0, 0, np.pi / N]) + coil.rotate(angle=np.pi / N) coils = CoilSet.linspaced_angular( coil, I, [0, 0, 1], np.pi / NFP, N // NFP // 2 ) diff --git a/tests/test_curves.py b/tests/test_curves.py index ec16850b2f..b4ff6d8740 100644 --- a/tests/test_curves.py +++ b/tests/test_curves.py @@ -25,7 +25,7 @@ def test_length(self): c.compute("length", grid=20)["length"], 10 * 2 * np.pi ) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=20)["length"], 10 * 2 * np.pi @@ -37,7 +37,7 @@ def test_curvature(self): c = FourierRZCurve() np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 10) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 10) @@ -47,7 +47,7 @@ def test_torsion(self): c = FourierRZCurve() np.testing.assert_allclose(c.compute("torsion", grid=20)["torsion"], 0) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose(c.compute("torsion", grid=20)["torsion"], 0) @@ -62,7 +62,7 @@ def test_frenet(self): np.testing.assert_allclose(T, np.array([[0, 1, 0]]), atol=1e-12) np.testing.assert_allclose(N, np.array([[-1, 0, 0]]), atol=1e-12) np.testing.assert_allclose(B, np.array([[0, 0, 1]]), atol=1e-12) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) c.translate([1, 1, 1]) data = c.compute( @@ -81,7 +81,7 @@ def test_coords(self): np.testing.assert_allclose(x, 10) np.testing.assert_allclose(y, 0) np.testing.assert_allclose(z, 0) - c.rotate([0, 0, np.pi / 2]) + c.rotate(angle=np.pi / 2) c.flip([0, 1, 0]) c.translate([1, 1, 1]) r, p, z = c.compute("x", grid=0, basis="rpz")["x"].T @@ -265,7 +265,7 @@ def test_length(self): c.compute("length", grid=20)["length"], 2 * 2 * np.pi ) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=20)["length"], 2 * 2 * np.pi @@ -277,7 +277,7 @@ def test_curvature(self): c = FourierXYZCurve() np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 2) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 2) @@ -289,7 +289,7 @@ def test_torsion(self): c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 ) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 @@ -306,7 +306,7 @@ def test_frenet(self): np.testing.assert_allclose(T, np.array([[0, 0, -1]]), atol=1e-12) np.testing.assert_allclose(N, np.array([[-1, 0, 0]]), atol=1e-12) np.testing.assert_allclose(B, np.array([[0, 1, 0]]), atol=1e-12) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) c.translate([1, 1, 1]) data = c.compute( @@ -325,7 +325,7 @@ def test_coords(self): np.testing.assert_allclose(x, 12) np.testing.assert_allclose(y, 0) np.testing.assert_allclose(z, 0) - c.rotate([0, 0, np.pi / 2]) + c.rotate(angle=np.pi / 2) c.flip([0, 1, 0]) c.translate([1, 1, 1]) r, p, z = c.compute("x", grid=0, basis="rpz")["x"].T @@ -423,7 +423,7 @@ def test_length(self): c.compute("length", grid=20)["length"], 2 * 2 * np.pi ) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=20)["length"], 2 * 2 * np.pi @@ -435,7 +435,7 @@ def test_curvature(self): c = FourierPlanarCurve() np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 2) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose(c.compute("curvature", grid=20)["curvature"], 1 / 2) @@ -447,7 +447,7 @@ def test_torsion(self): c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 ) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 @@ -464,7 +464,7 @@ def test_frenet(self): np.testing.assert_allclose(T, np.array([[0, 0, -1]]), atol=1e-12) np.testing.assert_allclose(N, np.array([[-1, 0, 0]]), atol=1e-12) np.testing.assert_allclose(B, np.array([[0, 1, 0]]), atol=1e-12) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) c.translate([1, 1, 1]) data = c.compute( @@ -487,7 +487,7 @@ def test_coords(self): np.testing.assert_allclose(dr, 0) np.testing.assert_allclose(dp, 0, atol=1e-14) np.testing.assert_allclose(dz, 2) - c.rotate([0, 0, np.pi / 2]) + c.rotate(angle=np.pi / 2) c.flip([0, 1, 0]) c.translate([1, 1, 1]) x, y, z = c.compute("x", grid=0, basis="xyz")["x"].T @@ -571,7 +571,7 @@ def test_length(self): err_msg=f"Failed at {method}", ) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=npts)["length"], @@ -596,7 +596,7 @@ def test_length(self): err_msg=f"Failed at {method}", ) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("length", grid=npts)["length"], @@ -642,7 +642,7 @@ def test_coords(self): np.testing.assert_allclose(x, R) np.testing.assert_allclose(y, 0, atol=1e-15) np.testing.assert_allclose(z, 0, atol=1e-15) - c.rotate([0, 0, np.pi / 2]) + c.rotate(angle=np.pi / 2) c.flip([0, 1, 0]) c.translate([1, 1, 1]) r, p, z = c.compute("x", grid=Grid(np.array([[0.0, 0.0, 0.0]])), basis="rpz")[ @@ -663,7 +663,7 @@ def test_curvature(self): c.compute("curvature", grid=10)["curvature"][1:-1], 1 / 10, atol=1e-3 ) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("curvature", grid=10)["curvature"][1:-1], 1 / 10, atol=1e-3 @@ -680,7 +680,7 @@ def test_torsion(self): c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 ) c.translate([1, 1, 1]) - c.rotate([0, 0, np.pi]) + c.rotate(angle=np.pi) c.flip([0, 1, 0]) np.testing.assert_allclose( c.compute("torsion", grid=20)["torsion"], 0, atol=1e-12 diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 873b4fcf1d..3be1eefb39 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -885,7 +885,7 @@ def test_plot_coils(): NFP = 4 I = 1 coil = FourierXYZCoil() - coil.rotate([0, 0, np.pi / N]) + coil.rotate(angle=np.pi / N) coils = CoilSet.linspaced_angular(coil, I, [0, 0, 1], np.pi / NFP, N // NFP // 2) coils.grid = 100 coils2 = MixedCoilSet.from_symmetry(coils, NFP, True) From 09fe488be32b00fd4d9085bb7e0715fcd8468a06 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Fri, 9 Feb 2024 14:33:42 -0500 Subject: [PATCH 15/56] add logic for CoilSet This doesn't work for MixedCoilSet yet --- desc/objectives/_geometry.py | 39 ++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index eaceb250ae..a16dee7592 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -515,7 +515,7 @@ def __init__( target = 2 * np.pi self._grid = grid super().__init__( - things=coil, + things=[coil], target=target, bounds=bounds, weight=weight, @@ -537,22 +537,36 @@ def build(self, use_jit=True, verbose=1): Level of output. """ - coil = self.things[0] + # local import to avoid circular import + from desc.coils import CoilSet, MixedCoilSet + + is_multiple_coils = False + if isinstance(self.things[0], CoilSet): + if isinstance(self.things[0], MixedCoilSet): + is_multiple_coils = True + coil = self.things[0] + else: + coil = self.things[0].coils[0] + else: + coil = self.things[0] + self._dim_f = 1 self._data_keys = ["length"] if self._grid is None: - grid = LinearGrid(N=32) + # TODO: have list of grid if mixed coil set + self._grid = LinearGrid(N=32) timer = Timer() if verbose > 0: print("Precomputing transforms") timer.start("Precomputing transforms") - profiles = get_profiles(self._data_keys, obj=coil, grid=grid) - transforms = get_transforms(self._data_keys, obj=coil, grid=grid) + # TODO: make list of transforms for MixedCoilSet + transforms = get_transforms(self._data_keys, obj=coil, grid=self._grid) + self._constants = { "transforms": transforms, - "profiles": profiles, + "is_coil_set": is_multiple_coils, } timer.stop("Precomputing transforms") @@ -580,16 +594,19 @@ def compute(self, params, constants=None): if constants is None: constants = self._constants - data = compute_fun( - self.things[0], + coils = self.things[0] + + data = coils.compute( self._data_keys, params=params, transforms=constants["transforms"], - profiles=constants["profiles"], + grid=self._grid, ) - f = data["length"] - return f + if isinstance(data, list): + return [data[i]["length"] for i, dat in enumerate(data)] + else: + return data["length"] class PlasmaVesselDistance(_Objective): From e612fb47ea196f120cbbd8c80144ffa8267635cc Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Fri, 9 Feb 2024 14:34:04 -0500 Subject: [PATCH 16/56] add test for CoilSet --- tests/test_objective_funs.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index 90c1c51882..10728407f1 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -12,7 +12,7 @@ import desc.examples from desc.backend import jnp -from desc.coils import FourierPlanarCoil +from desc.coils import CoilSet, FourierPlanarCoil from desc.compute import get_transforms from desc.equilibrium import Equilibrium from desc.examples import get @@ -498,11 +498,17 @@ def test(eq): @pytest.mark.unit def test_coil_length(self): """Tests coil length.""" + + def test(coil): + obj = CoilLength(coil) + obj.build() + f = obj.compute(params=coil.params_dict) + np.testing.assert_allclose(f, 2 * np.pi, rtol=1e-8) + coil = FourierPlanarCoil(r_n=1) - obj = CoilLength(coil) - obj.build() - f = obj.compute(params=coil.params_dict) - np.testing.assert_allclose(f, 2 * np.pi, rtol=1e-8) + coils = CoilSet.linspaced_linear(coil, n=4) + test(coil) + test(coils) @pytest.mark.unit From c21305cd92d4dea17be72fa11cb58841baf5e046 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Fri, 9 Feb 2024 20:33:34 -0500 Subject: [PATCH 17/56] add mixed coil functionality This treats mixed coils and single coils separately. As in, mixed coils are a list and single coils are not. --- desc/objectives/_geometry.py | 54 +++++++++++++++++++++++++----------- tests/test_objective_funs.py | 5 +++- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index a16dee7592..45fde687d5 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -540,10 +540,10 @@ def build(self, use_jit=True, verbose=1): # local import to avoid circular import from desc.coils import CoilSet, MixedCoilSet - is_multiple_coils = False + is_mixed_coils = False if isinstance(self.things[0], CoilSet): if isinstance(self.things[0], MixedCoilSet): - is_multiple_coils = True + is_mixed_coils = True coil = self.things[0] else: coil = self.things[0].coils[0] @@ -553,20 +553,29 @@ def build(self, use_jit=True, verbose=1): self._dim_f = 1 self._data_keys = ["length"] if self._grid is None: - # TODO: have list of grid if mixed coil set - self._grid = LinearGrid(N=32) + # TODO: raise error if grid, transforms are not the same size as mixed coils + self._grid = ( + LinearGrid(N=32) if not is_mixed_coils else [LinearGrid(N=32)] * 4 + ) + + assert self._grid timer = Timer() if verbose > 0: print("Precomputing transforms") timer.start("Precomputing transforms") - # TODO: make list of transforms for MixedCoilSet - transforms = get_transforms(self._data_keys, obj=coil, grid=self._grid) + if is_mixed_coils: + transforms = [ + get_transforms(self._data_keys, obj=coil[i], grid=self._grid[i]) + for i, c in enumerate(coil) + ] + else: + transforms = get_transforms(self._data_keys, obj=coil, grid=self._grid) self._constants = { "transforms": transforms, - "is_coil_set": is_multiple_coils, + "is_mixed_coils": is_mixed_coils, } timer.stop("Precomputing transforms") @@ -588,20 +597,33 @@ def compute(self, params, constants=None): Returns ------- - f : float + f : float or array of floats Coil length. """ if constants is None: constants = self._constants - coils = self.things[0] - - data = coils.compute( - self._data_keys, - params=params, - transforms=constants["transforms"], - grid=self._grid, - ) + # TODO: could simplify this by making coils always a list, + # but then it would always be returning a list + if constants["is_mixed_coils"]: + coils = self.things[0].coils + data = [ + coils[i].compute( + self._data_keys, + params=params[i], + transforms=constants["transforms"][i], + grid=self._grid[i], + ) + for i, coil in enumerate(coils) + ] + else: + coils = self.things[0] + data = coils.compute( + self._data_keys, + params=params, + transforms=constants["transforms"], + grid=self._grid, + ) if isinstance(data, list): return [data[i]["length"] for i, dat in enumerate(data)] diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index 10728407f1..aa702acff1 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -12,7 +12,7 @@ import desc.examples from desc.backend import jnp -from desc.coils import CoilSet, FourierPlanarCoil +from desc.coils import CoilSet, FourierPlanarCoil, MixedCoilSet from desc.compute import get_transforms from desc.equilibrium import Equilibrium from desc.examples import get @@ -507,8 +507,11 @@ def test(coil): coil = FourierPlanarCoil(r_n=1) coils = CoilSet.linspaced_linear(coil, n=4) + mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) + test(coil) test(coils) + test(mixed_coils) @pytest.mark.unit From edae7b847161457d0087307b0e40bb5034eed6af Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Fri, 9 Feb 2024 23:37:45 -0500 Subject: [PATCH 18/56] correct dim_f still really yucky. --- desc/objectives/_geometry.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 45fde687d5..8082e2c5a5 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -544,22 +544,24 @@ def build(self, use_jit=True, verbose=1): if isinstance(self.things[0], CoilSet): if isinstance(self.things[0], MixedCoilSet): is_mixed_coils = True - coil = self.things[0] + coil = self.things[0].coils + self._dim_f = len(coil) else: coil = self.things[0].coils[0] + self._dim_f = 1 else: coil = self.things[0] + self._dim_f = 1 - self._dim_f = 1 self._data_keys = ["length"] if self._grid is None: # TODO: raise error if grid, transforms are not the same size as mixed coils self._grid = ( - LinearGrid(N=32) if not is_mixed_coils else [LinearGrid(N=32)] * 4 + LinearGrid(N=32) + if not is_mixed_coils + else [LinearGrid(N=32)] * self._dim_f ) - assert self._grid - timer = Timer() if verbose > 0: print("Precomputing transforms") @@ -568,7 +570,7 @@ def build(self, use_jit=True, verbose=1): if is_mixed_coils: transforms = [ get_transforms(self._data_keys, obj=coil[i], grid=self._grid[i]) - for i, c in enumerate(coil) + for i in range(self._dim_f) ] else: transforms = get_transforms(self._data_keys, obj=coil, grid=self._grid) @@ -603,8 +605,6 @@ def compute(self, params, constants=None): if constants is None: constants = self._constants - # TODO: could simplify this by making coils always a list, - # but then it would always be returning a list if constants["is_mixed_coils"]: coils = self.things[0].coils data = [ From 1ab2e751f74a7c333794a669e3c71d5a5dc6db7c Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sat, 10 Feb 2024 20:55:04 -0500 Subject: [PATCH 19/56] add base class for coil objectives This seems reasonable to do since the code for curvature and length (and maybe more?) are very similar. --- desc/objectives/__init__.py | 1 + desc/objectives/_geometry.py | 130 ++++++++++++++++++++++++++++++++--- tests/test_objective_funs.py | 19 +++++ 3 files changed, 141 insertions(+), 9 deletions(-) diff --git a/desc/objectives/__init__.py b/desc/objectives/__init__.py index 2d779ef3ed..fd998ef1ee 100644 --- a/desc/objectives/__init__.py +++ b/desc/objectives/__init__.py @@ -13,6 +13,7 @@ from ._geometry import ( AspectRatio, BScaleLength, + CoilCurvature, CoilLength, Elongation, GoodCoordinates, diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 8082e2c5a5..1b5fb935e6 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -459,13 +459,15 @@ def compute(self, params, constants=None): return data["V"] -class CoilLength(_Objective): - """Plasma volume. +class _CoilObjective(_Objective): + """Base class for calculating coil objectives. Parameters ---------- - coil : FourierPlanarCoil or FourierXYZCoil - Coil for which the length will be found. + coil : CoilSet or Coil + Coil for which the data keys will be optimized. + data_keys : list of str + data keys that will be optimized when this class is inherited. target : {float, ndarray}, optional Target value(s) of the objective. Only used if bounds is None. Must be broadcastable to Objective.dim_f. @@ -501,6 +503,7 @@ class CoilLength(_Objective): def __init__( self, coil, + data_keys, target=None, bounds=None, weight=1, @@ -509,11 +512,10 @@ def __init__( loss_function=None, deriv_mode="auto", grid=None, - name="coil-length", + name=None, ): - if target is None and bounds is None: - target = 2 * np.pi self._grid = grid + self._data_keys = data_keys super().__init__( things=[coil], target=target, @@ -553,7 +555,6 @@ def build(self, use_jit=True, verbose=1): coil = self.things[0] self._dim_f = 1 - self._data_keys = ["length"] if self._grid is None: # TODO: raise error if grid, transforms are not the same size as mixed coils self._grid = ( @@ -587,7 +588,7 @@ def build(self, use_jit=True, verbose=1): super().build(use_jit=use_jit, verbose=verbose) def compute(self, params, constants=None): - """Compute length of coil. + """Compute data of coil for given data key. Parameters ---------- @@ -625,12 +626,123 @@ def compute(self, params, constants=None): grid=self._grid, ) + return data + + +class CoilLength(_CoilObjective): + """Coil length. + + Parameters + ---------- + coil : CoilSet or Coil + Coil(s) that are to be optimized + target : {float, ndarray}, optional + Target value(s) of the objective. Only used if bounds is None. + Must be broadcastable to Objective.dim_f. + bounds : tuple of {float, ndarray}, optional + Lower and upper bounds on the objective. Overrides target. + Both bounds must be broadcastable to to Objective.dim_f + """ + + def __init__(self, coil, target=None, bounds=None): + if target is None and bounds is None: + target = 2 * np.pi + + super().__init__(coil, ["length"], target=target, bounds=bounds) + + def build(self, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + super().build(use_jit=use_jit, verbose=verbose) + + def compute(self, params, constants=None): + """Compute coil length. + + Parameters + ---------- + params : dict + Dictionary of the coil's degrees of freedom. + constants : dict + Dictionary of constant data, eg transforms, profiles etc. Defaults to + self._constants. + + Returns + ------- + f : float or array of floats + Coil length. + """ + data = super().compute(params, constants=constants) if isinstance(data, list): return [data[i]["length"] for i, dat in enumerate(data)] else: return data["length"] +class CoilCurvature(_CoilObjective): + """Coil curvature. + + Parameters + ---------- + coil : CoilSet or Coil + Coil(s) that are to be optimized + target : {float, ndarray}, optional + Target value(s) of the objective. Only used if bounds is None. + Must be broadcastable to Objective.dim_f. + bounds : tuple of {float, ndarray}, optional + Lower and upper bounds on the objective. Overrides target. + Both bounds must be broadcastable to to Objective.dim_f + """ + + def __init__(self, coil, target=None, bounds=None): + if target is None and bounds is None: + target = 1 / 2 + + super().__init__(coil, ["curvature"], target=target, bounds=bounds) + + def build(self, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + super().build(use_jit=use_jit, verbose=verbose) + + def compute(self, params, constants=None): + """Compute coil curvature. + + Parameters + ---------- + params : dict + Dictionary of the coil's degrees of freedom. + constants : dict + Dictionary of constant data, eg transforms, profiles etc. Defaults to + self._constants. + + Returns + ------- + f : float or array of floats + Coil curvature. + """ + data = super().compute(params, constants=constants) + if isinstance(data, list): + return [data[i]["curvature"] for i, dat in enumerate(data)] + else: + return data["curvature"] + + class PlasmaVesselDistance(_Objective): """Target the distance between the plasma and a surrounding surface. diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index aa702acff1..fe28525bc2 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -22,6 +22,7 @@ AspectRatio, BootstrapRedlConsistency, BScaleLength, + CoilCurvature, CoilLength, CurrentDensity, Elongation, @@ -513,6 +514,24 @@ def test(coil): test(coils) test(mixed_coils) + @pytest.mark.unit + def test_coil_curvature(self): + """Tests coil curvature.""" + + def test(coil): + obj = CoilCurvature(coil) + obj.build() + f = obj.compute(params=coil.params_dict) + np.testing.assert_allclose(f, 1 / 2, rtol=1e-8) + + coil = FourierPlanarCoil() + coils = CoilSet.linspaced_linear(coil, n=4) + mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) + + test(coil) + test(coils) + test(mixed_coils) + @pytest.mark.unit def test_derivative_modes(): From dbfb77d1ffcd849ce6126977db27324bf80e9abd Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sat, 10 Feb 2024 20:59:36 -0500 Subject: [PATCH 20/56] add kwargs --- desc/objectives/_geometry.py | 8 ++++---- tests/test_objective_funs.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 1b5fb935e6..e8a8f2c580 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -644,11 +644,11 @@ class CoilLength(_CoilObjective): Both bounds must be broadcastable to to Objective.dim_f """ - def __init__(self, coil, target=None, bounds=None): + def __init__(self, coil, target=None, bounds=None, **kwargs): if target is None and bounds is None: target = 2 * np.pi - super().__init__(coil, ["length"], target=target, bounds=bounds) + super().__init__(coil, ["length"], target=target, bounds=bounds, **kwargs) def build(self, use_jit=True, verbose=1): """Build constant arrays. @@ -701,11 +701,11 @@ class CoilCurvature(_CoilObjective): Both bounds must be broadcastable to to Objective.dim_f """ - def __init__(self, coil, target=None, bounds=None): + def __init__(self, coil, target=None, bounds=None, **kwargs): if target is None and bounds is None: target = 1 / 2 - super().__init__(coil, ["curvature"], target=target, bounds=bounds) + super().__init__(coil, ["curvature"], target=target, bounds=bounds, **kwargs) def build(self, use_jit=True, verbose=1): """Build constant arrays. diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index fe28525bc2..eca4190be1 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -501,7 +501,7 @@ def test_coil_length(self): """Tests coil length.""" def test(coil): - obj = CoilLength(coil) + obj = CoilLength(coil, weight=1) obj.build() f = obj.compute(params=coil.params_dict) np.testing.assert_allclose(f, 2 * np.pi, rtol=1e-8) @@ -519,7 +519,7 @@ def test_coil_curvature(self): """Tests coil curvature.""" def test(coil): - obj = CoilCurvature(coil) + obj = CoilCurvature(coil, weight=1) obj.build() f = obj.compute(params=coil.params_dict) np.testing.assert_allclose(f, 1 / 2, rtol=1e-8) From 01c872baa56f76a6dab1e29b90ea75eb22319675 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Wed, 21 Feb 2024 13:39:08 -0500 Subject: [PATCH 21/56] address easy reviews docstrings, add args, don't use loop for compute --- desc/objectives/_geometry.py | 154 ++++++++++++++++++++++++----------- 1 file changed, 105 insertions(+), 49 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index e8a8f2c580..a16326415f 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -606,25 +606,13 @@ def compute(self, params, constants=None): if constants is None: constants = self._constants - if constants["is_mixed_coils"]: - coils = self.things[0].coils - data = [ - coils[i].compute( - self._data_keys, - params=params[i], - transforms=constants["transforms"][i], - grid=self._grid[i], - ) - for i, coil in enumerate(coils) - ] - else: - coils = self.things[0] - data = coils.compute( - self._data_keys, - params=params, - transforms=constants["transforms"], - grid=self._grid, - ) + coils = self.things[0] + data = coils.compute( + self._data_keys, + params=params, + transforms=constants["transforms"], + grid=self._grid, + ) return data @@ -642,26 +630,60 @@ class CoilLength(_CoilObjective): bounds : tuple of {float, ndarray}, optional Lower and upper bounds on the objective. Overrides target. Both bounds must be broadcastable to to Objective.dim_f + weight : {float, ndarray}, optional + Weighting to apply to the Objective, relative to other Objectives. + Must be broadcastable to to Objective.dim_f + normalize : bool, optional + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool, optional + Whether target and bounds should be normalized before comparing to computed + values. If `normalize` is `True` and the target is in physical units, + this should also be set to True. + be set to True. + loss_function : {None, 'mean', 'min', 'max'}, optional + Loss function to apply to the objective values once computed. This loss function + is called on the raw compute value, before any shifting, scaling, or + normalization. Note: Has no effect for this objective. + deriv_mode : {"auto", "fwd", "rev"} + Specify how to compute jacobian matrix, either forward mode or reverse mode AD. + "auto" selects forward or reverse mode based on the size of the input and output + of the objective. Has no effect on self.grad or self.hess which always use + reverse mode and forward over reverse mode respectively. + grid : Grid, optional + Collocation grid containing the nodes to evaluate at. + name : str, optional + Name of the objective function. """ - def __init__(self, coil, target=None, bounds=None, **kwargs): + def __init__( + self, + coil, + target=None, + bounds=None, + weight=1, + normalize=True, + normalize_target=True, + loss_function=None, + deriv_mode="auto", + grid=None, + name=None, + ): if target is None and bounds is None: target = 2 * np.pi - super().__init__(coil, ["length"], target=target, bounds=bounds, **kwargs) - - def build(self, use_jit=True, verbose=1): - """Build constant arrays. - - Parameters - ---------- - use_jit : bool, optional - Whether to just-in-time compile the objective and derivatives. - verbose : int, optional - Level of output. - - """ - super().build(use_jit=use_jit, verbose=verbose) + super().__init__( + coil, + ["length"], + target=target, + bounds=bounds, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + loss_function=loss_function, + deriv_mode=deriv_mode, + grid=grid, + name=name, + ) def compute(self, params, constants=None): """Compute coil length. @@ -699,26 +721,60 @@ class CoilCurvature(_CoilObjective): bounds : tuple of {float, ndarray}, optional Lower and upper bounds on the objective. Overrides target. Both bounds must be broadcastable to to Objective.dim_f + weight : {float, ndarray}, optional + Weighting to apply to the Objective, relative to other Objectives. + Must be broadcastable to to Objective.dim_f + normalize : bool, optional + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool, optional + Whether target and bounds should be normalized before comparing to computed + values. If `normalize` is `True` and the target is in physical units, + this should also be set to True. + be set to True. + loss_function : {None, 'mean', 'min', 'max'}, optional + Loss function to apply to the objective values once computed. This loss function + is called on the raw compute value, before any shifting, scaling, or + normalization. Note: Has no effect for this objective. + deriv_mode : {"auto", "fwd", "rev"} + Specify how to compute jacobian matrix, either forward mode or reverse mode AD. + "auto" selects forward or reverse mode based on the size of the input and output + of the objective. Has no effect on self.grad or self.hess which always use + reverse mode and forward over reverse mode respectively. + grid : Grid, optional + Collocation grid containing the nodes to evaluate at. + name : str, optional + Name of the objective function. """ - def __init__(self, coil, target=None, bounds=None, **kwargs): + def __init__( + self, + coil, + target=None, + bounds=None, + weight=1, + normalize=True, + normalize_target=True, + loss_function=None, + deriv_mode="auto", + grid=None, + name=None, + ): if target is None and bounds is None: target = 1 / 2 - super().__init__(coil, ["curvature"], target=target, bounds=bounds, **kwargs) - - def build(self, use_jit=True, verbose=1): - """Build constant arrays. - - Parameters - ---------- - use_jit : bool, optional - Whether to just-in-time compile the objective and derivatives. - verbose : int, optional - Level of output. - - """ - super().build(use_jit=use_jit, verbose=verbose) + super().__init__( + coil, + ["curvature"], + target=target, + bounds=bounds, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + loss_function=loss_function, + deriv_mode=deriv_mode, + grid=grid, + name=name, + ) def compute(self, params, constants=None): """Compute coil curvature. From a6ef1c20daa728d325990258909910434a1524ee Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sat, 24 Feb 2024 01:27:21 -0500 Subject: [PATCH 22/56] add tree_flatten for nested coilsets nested coilsets aren't actually tested yet --- desc/objectives/_geometry.py | 50 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 5469c667e7..19923d62a4 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -574,20 +574,18 @@ def build(self, use_jit=True, verbose=1): """ # local import to avoid circular import - from desc.coils import CoilSet, MixedCoilSet - - is_mixed_coils = False - if isinstance(self.things[0], CoilSet): - if isinstance(self.things[0], MixedCoilSet): - is_mixed_coils = True - coil = self.things[0].coils - self._dim_f = len(coil) - else: - coil = self.things[0].coils[0] - self._dim_f = 1 - else: - coil = self.things[0] - self._dim_f = 1 + # TODO: import this from desc backend? + import jax.tree_util as jtu + + from desc.coils import CoilSet, MixedCoilSet, _Coil + + coils = jtu.tree_flatten( + self.things[0], + is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), + )[0] + self._dim_f = len(coils) + + is_mixed_coils = isinstance(self.things[0], MixedCoilSet) if self._grid is None: # TODO: raise error if grid, transforms are not the same size as mixed coils @@ -604,15 +602,14 @@ def build(self, use_jit=True, verbose=1): if is_mixed_coils: transforms = [ - get_transforms(self._data_keys, obj=coil[i], grid=self._grid[i]) + get_transforms(self._data_keys, obj=coils[i], grid=self._grid[i]) for i in range(self._dim_f) ] else: - transforms = get_transforms(self._data_keys, obj=coil, grid=self._grid) + transforms = get_transforms(self._data_keys, obj=coils[0], grid=self._grid) self._constants = { "transforms": transforms, - "is_mixed_coils": is_mixed_coils, } timer.stop("Precomputing transforms") @@ -735,11 +732,13 @@ def compute(self, params, constants=None): f : float or array of floats Coil length. """ + # TODO: import this from desc backend? + import jax.tree_util as jtu + data = super().compute(params, constants=constants) - if isinstance(data, list): - return [data[i]["length"] for i, dat in enumerate(data)] - else: - return data["length"] + data = jtu.tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] + out = jnp.array([dat["length"] for dat in data]) + return out class CoilCurvature(_CoilObjective): @@ -826,11 +825,12 @@ def compute(self, params, constants=None): f : float or array of floats Coil curvature. """ + import jax.tree_util as jtu + data = super().compute(params, constants=constants) - if isinstance(data, list): - return [data[i]["curvature"] for i, dat in enumerate(data)] - else: - return data["curvature"] + data = jtu.tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] + out = jnp.array([dat["curvature"] for dat in data]) + return out class PlasmaVesselDistance(_Objective): From 209ea6d3d22d76ca2a45fd9a6db630f446af8a91 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sat, 24 Feb 2024 01:31:26 -0500 Subject: [PATCH 23/56] fix import bug from messing up merge conflict --- tests/test_objective_funs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index 5aba93b417..c67205f147 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -12,7 +12,7 @@ import desc.examples from desc.backend import jnp -from desc.coils import CoilSet, FourierPlanarCoil, MixedCoilSet +from desc.coils import CoilSet, FourierPlanarCoil, FourierXYZCoil, MixedCoilSet from desc.compute import get_transforms from desc.equilibrium import Equilibrium from desc.examples import get From 2559aa19ec719d632e87f95b156ec11bd51c7399 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 13:38:54 -0500 Subject: [PATCH 24/56] add nested coil sets functionality functionality for transforms, grids and tests --- desc/objectives/_geometry.py | 28 +++++++++++++++++----------- tests/test_objective_funs.py | 4 ++++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 19923d62a4..4d8d0ec94a 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -573,10 +573,10 @@ def build(self, use_jit=True, verbose=1): Level of output. """ - # local import to avoid circular import # TODO: import this from desc backend? import jax.tree_util as jtu + # local import to avoid circular import from desc.coils import CoilSet, MixedCoilSet, _Coil coils = jtu.tree_flatten( @@ -589,28 +589,34 @@ def build(self, use_jit=True, verbose=1): if self._grid is None: # TODO: raise error if grid, transforms are not the same size as mixed coils - self._grid = ( - LinearGrid(N=32) - if not is_mixed_coils - else [LinearGrid(N=32)] * self._dim_f - ) + self._grid = [LinearGrid(N=32)] * self._dim_f timer = Timer() if verbose > 0: print("Precomputing transforms") timer.start("Precomputing transforms") + transforms = jtu.tree_map( + lambda coil_obj, grid: get_transforms( + self._data_keys, obj=coil_obj, grid=grid + ), + coils, + self._grid, + is_leaf=lambda coil_obj: isinstance(coil_obj, _Coil) + and not isinstance(coil_obj, MixedCoilSet), + ) + if is_mixed_coils: transforms = [ get_transforms(self._data_keys, obj=coils[i], grid=self._grid[i]) for i in range(self._dim_f) ] else: - transforms = get_transforms(self._data_keys, obj=coils[0], grid=self._grid) + transforms = get_transforms( + self._data_keys, obj=coils[0], grid=self._grid[0] + ) - self._constants = { - "transforms": transforms, - } + self._constants = {"transforms": transforms, "is_mixed_coils": is_mixed_coils} timer.stop("Precomputing transforms") if verbose > 1: @@ -642,7 +648,7 @@ def compute(self, params, constants=None): self._data_keys, params=params, transforms=constants["transforms"], - grid=self._grid, + grid=self._grid if constants["is_mixed_coils"] else self._grid[0], ) return data diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index 334640242f..ca11eed74f 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -584,10 +584,12 @@ def test(coil): coil = FourierPlanarCoil(r_n=1) coils = CoilSet.linspaced_linear(coil, n=4) mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) + nested_coils = MixedCoilSet.from_symmetry(mixed_coils, NFP=4) test(coil) test(coils) test(mixed_coils) + test(nested_coils) @pytest.mark.unit def test_coil_curvature(self): @@ -602,10 +604,12 @@ def test(coil): coil = FourierPlanarCoil() coils = CoilSet.linspaced_linear(coil, n=4) mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) + nested_coils = MixedCoilSet.from_symmetry(mixed_coils, NFP=4) test(coil) test(coils) test(mixed_coils) + test(nested_coils) @pytest.mark.unit From af3a424728e91c2fc086b758ce2c3bd43c69968a Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 13:43:58 -0500 Subject: [PATCH 25/56] use tree_flatten from backend and import jax --- desc/objectives/_geometry.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 4d8d0ec94a..ef7a4c74e6 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -2,9 +2,10 @@ import warnings +import jax import numpy as np -from desc.backend import jnp +from desc.backend import jnp, tree_flatten from desc.compute import compute as compute_fun from desc.compute import get_profiles, get_transforms, rpz2xyz from desc.compute.utils import safenorm @@ -573,13 +574,10 @@ def build(self, use_jit=True, verbose=1): Level of output. """ - # TODO: import this from desc backend? - import jax.tree_util as jtu - # local import to avoid circular import from desc.coils import CoilSet, MixedCoilSet, _Coil - coils = jtu.tree_flatten( + coils = tree_flatten( self.things[0], is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), )[0] @@ -596,7 +594,7 @@ def build(self, use_jit=True, verbose=1): print("Precomputing transforms") timer.start("Precomputing transforms") - transforms = jtu.tree_map( + transforms = jax.tree_util.tree_map( lambda coil_obj, grid: get_transforms( self._data_keys, obj=coil_obj, grid=grid ), @@ -738,11 +736,8 @@ def compute(self, params, constants=None): f : float or array of floats Coil length. """ - # TODO: import this from desc backend? - import jax.tree_util as jtu - data = super().compute(params, constants=constants) - data = jtu.tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] + data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] out = jnp.array([dat["length"] for dat in data]) return out @@ -831,10 +826,8 @@ def compute(self, params, constants=None): f : float or array of floats Coil curvature. """ - import jax.tree_util as jtu - data = super().compute(params, constants=constants) - data = jtu.tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] + data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] out = jnp.array([dat["curvature"] for dat in data]) return out From 024f0f22e8cec36cfa4808bcef82e7ee2c519862 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 14:38:35 -0500 Subject: [PATCH 26/56] use tree_map for transforms --- desc/objectives/_geometry.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index ef7a4c74e6..cba5624f79 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -577,17 +577,23 @@ def build(self, use_jit=True, verbose=1): # local import to avoid circular import from desc.coils import CoilSet, MixedCoilSet, _Coil + is_mixed_coils = isinstance(self.things[0], MixedCoilSet) + coils = tree_flatten( self.things[0], is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), )[0] self._dim_f = len(coils) - is_mixed_coils = isinstance(self.things[0], MixedCoilSet) - + # make single coils and grid a list so they can be used with tree_map + coils = [coils[0]] if not is_mixed_coils else coils if self._grid is None: # TODO: raise error if grid, transforms are not the same size as mixed coils - self._grid = [LinearGrid(N=32)] * self._dim_f + self._grid = ( + [LinearGrid(N=32)] + if not is_mixed_coils + else [LinearGrid(N=32)] * self._dim_f + ) timer = Timer() if verbose > 0: @@ -595,24 +601,16 @@ def build(self, use_jit=True, verbose=1): timer.start("Precomputing transforms") transforms = jax.tree_util.tree_map( - lambda coil_obj, grid: get_transforms( - self._data_keys, obj=coil_obj, grid=grid - ), + lambda x, y: get_transforms(self._data_keys, obj=x, grid=y), coils, self._grid, - is_leaf=lambda coil_obj: isinstance(coil_obj, _Coil) - and not isinstance(coil_obj, MixedCoilSet), + is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, MixedCoilSet), ) - - if is_mixed_coils: - transforms = [ - get_transforms(self._data_keys, obj=coils[i], grid=self._grid[i]) - for i in range(self._dim_f) - ] - else: - transforms = get_transforms( - self._data_keys, obj=coils[0], grid=self._grid[0] - ) + # tree map always returns a list so take first transform and grid + # because they are the same for single coil + if not is_mixed_coils: + transforms = transforms[0] + self._grid = self._grid[0] self._constants = {"transforms": transforms, "is_mixed_coils": is_mixed_coils} @@ -646,7 +644,7 @@ def compute(self, params, constants=None): self._data_keys, params=params, transforms=constants["transforms"], - grid=self._grid if constants["is_mixed_coils"] else self._grid[0], + grid=self._grid, ) return data From 7535497e59c83478c52dd29feb76e4881ff1ecf0 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 16:00:23 -0500 Subject: [PATCH 27/56] add logic for default grid - the grid is based on the coils resolution - raises errors where appropriate --- desc/objectives/_geometry.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index cba5624f79..7e4229915f 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -1,5 +1,6 @@ """Objectives for targeting geometrical quantities.""" +import numbers import warnings import jax @@ -587,13 +588,15 @@ def build(self, use_jit=True, verbose=1): # make single coils and grid a list so they can be used with tree_map coils = [coils[0]] if not is_mixed_coils else coils + if self._grid is None: - # TODO: raise error if grid, transforms are not the same size as mixed coils - self._grid = ( - [LinearGrid(N=32)] - if not is_mixed_coils - else [LinearGrid(N=32)] * self._dim_f - ) + NFP = self.NFP if hasattr(self, "NFP") else 1 + grid = lambda x: LinearGrid(N=2 * x + 5, NFP=NFP, endpoint=False) + self._grid = [grid(coil.N) for coil in coils] + elif self._grid.num_rho > 1 or self._grid.num_theta > 1: + raise TypeError("Only use toroidal resolution for coil grids.") + elif isinstance(self._grid, numbers.Integral): + raise TypeError("The inputted grid should be of type `Grid`.") timer = Timer() if verbose > 0: From 00c7bcdab961736136dcdef245212260103f9252 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 16:21:41 -0500 Subject: [PATCH 28/56] add torsion objective and tests --- desc/objectives/__init__.py | 1 + desc/objectives/_geometry.py | 90 ++++++++++++++++++++++++++++++++++++ tests/test_objective_funs.py | 21 +++++++++ 3 files changed, 112 insertions(+) diff --git a/desc/objectives/__init__.py b/desc/objectives/__init__.py index 67f3efb68d..3310096164 100644 --- a/desc/objectives/__init__.py +++ b/desc/objectives/__init__.py @@ -16,6 +16,7 @@ BScaleLength, CoilCurvature, CoilLength, + CoilTorsion, Elongation, GoodCoordinates, MeanCurvature, diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 7e4229915f..11822a755e 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -833,6 +833,96 @@ def compute(self, params, constants=None): return out +class CoilTorsion(_CoilObjective): + """Coil curvature. + + Parameters + ---------- + coil : CoilSet or Coil + Coil(s) that are to be optimized + target : {float, ndarray}, optional + Target value(s) of the objective. Only used if bounds is None. + Must be broadcastable to Objective.dim_f. + bounds : tuple of {float, ndarray}, optional + Lower and upper bounds on the objective. Overrides target. + Both bounds must be broadcastable to to Objective.dim_f + weight : {float, ndarray}, optional + Weighting to apply to the Objective, relative to other Objectives. + Must be broadcastable to to Objective.dim_f + normalize : bool, optional + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool, optional + Whether target and bounds should be normalized before comparing to computed + values. If `normalize` is `True` and the target is in physical units, + this should also be set to True. + be set to True. + loss_function : {None, 'mean', 'min', 'max'}, optional + Loss function to apply to the objective values once computed. This loss function + is called on the raw compute value, before any shifting, scaling, or + normalization. Note: Has no effect for this objective. + deriv_mode : {"auto", "fwd", "rev"} + Specify how to compute jacobian matrix, either forward mode or reverse mode AD. + "auto" selects forward or reverse mode based on the size of the input and output + of the objective. Has no effect on self.grad or self.hess which always use + reverse mode and forward over reverse mode respectively. + grid : Grid, optional + Collocation grid containing the nodes to evaluate at. + name : str, optional + Name of the objective function. + """ + + def __init__( + self, + coil, + target=None, + bounds=None, + weight=1, + normalize=True, + normalize_target=True, + loss_function=None, + deriv_mode="auto", + grid=None, + name=None, + ): + if target is None and bounds is None: + target = 0 + + super().__init__( + coil, + ["torsion"], + target=target, + bounds=bounds, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + loss_function=loss_function, + deriv_mode=deriv_mode, + grid=grid, + name=name, + ) + + def compute(self, params, constants=None): + """Compute coil curvature. + + Parameters + ---------- + params : dict + Dictionary of the coil's degrees of freedom. + constants : dict + Dictionary of constant data, eg transforms, profiles etc. Defaults to + self._constants. + + Returns + ------- + f : float or array of floats + Coil torsion. + """ + data = super().compute(params, constants=constants) + data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] + out = jnp.array([dat["torsion"] for dat in data]) + return out + + class PlasmaVesselDistance(_Objective): """Target the distance between the plasma and a surrounding surface. diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index ca11eed74f..23d94fa554 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -26,6 +26,7 @@ BScaleLength, CoilCurvature, CoilLength, + CoilTorsion, CurrentDensity, Elongation, Energy, @@ -611,6 +612,26 @@ def test(coil): test(mixed_coils) test(nested_coils) + @pytest.mark.unit + def test_coil_torsion(self): + """Tests coil torsion.""" + + def test(coil): + obj = CoilTorsion(coil, weight=1) + obj.build() + f = obj.compute(params=coil.params_dict) + np.testing.assert_allclose(f, 0, atol=1e-8) + + coil = FourierPlanarCoil() + coils = CoilSet.linspaced_linear(coil, n=4) + mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) + nested_coils = MixedCoilSet.from_symmetry(mixed_coils, NFP=4) + + test(coil) + test(coils) + test(mixed_coils) + test(nested_coils) + @pytest.mark.unit def test_derivative_modes(): From 73eafa4d3a8e8952ad1128fbdde6f74db9a0a388 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 16:26:19 -0500 Subject: [PATCH 29/56] update docstrings --- desc/objectives/_geometry.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 11822a755e..b467c55c43 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -662,7 +662,8 @@ class CoilLength(_CoilObjective): Coil(s) that are to be optimized target : {float, ndarray}, optional Target value(s) of the objective. Only used if bounds is None. - Must be broadcastable to Objective.dim_f. + Must be broadcastable to Objective.dim_f. If array, it has to + be flattened according to the number of inputs. bounds : tuple of {float, ndarray}, optional Lower and upper bounds on the objective. Overrides target. Both bounds must be broadcastable to to Objective.dim_f @@ -752,7 +753,8 @@ class CoilCurvature(_CoilObjective): Coil(s) that are to be optimized target : {float, ndarray}, optional Target value(s) of the objective. Only used if bounds is None. - Must be broadcastable to Objective.dim_f. + Must be broadcastable to Objective.dim_f. If array, it has to + be flattened according to the number of inputs. bounds : tuple of {float, ndarray}, optional Lower and upper bounds on the objective. Overrides target. Both bounds must be broadcastable to to Objective.dim_f @@ -834,7 +836,7 @@ def compute(self, params, constants=None): class CoilTorsion(_CoilObjective): - """Coil curvature. + """Coil torsion. Parameters ---------- @@ -842,7 +844,8 @@ class CoilTorsion(_CoilObjective): Coil(s) that are to be optimized target : {float, ndarray}, optional Target value(s) of the objective. Only used if bounds is None. - Must be broadcastable to Objective.dim_f. + Must be broadcastable to Objective.dim_f. If array, it has to + be flattened according to the number of inputs. bounds : tuple of {float, ndarray}, optional Lower and upper bounds on the objective. Overrides target. Both bounds must be broadcastable to to Objective.dim_f @@ -885,6 +888,7 @@ def __init__( name=None, ): if target is None and bounds is None: + # TODO: use bounds instead target = 0 super().__init__( @@ -902,7 +906,7 @@ def __init__( ) def compute(self, params, constants=None): - """Compute coil curvature. + """Compute coil torsion. Parameters ---------- From dabdba198ebeec943254039300e946731fe17c53 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 16:59:28 -0500 Subject: [PATCH 30/56] change curvature target to bounds --- desc/objectives/_geometry.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index b467c55c43..8e489fa353 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -586,7 +586,8 @@ def build(self, use_jit=True, verbose=1): )[0] self._dim_f = len(coils) - # make single coils and grid a list so they can be used with tree_map + # if using single coil, make coils and grid a list so they can be + # used with tree_map coils = [coils[0]] if not is_mixed_coils else coils if self._grid is None: @@ -610,7 +611,7 @@ def build(self, use_jit=True, verbose=1): is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, MixedCoilSet), ) # tree map always returns a list so take first transform and grid - # because they are the same for single coil + # for when we are only using a single coil if not is_mixed_coils: transforms = transforms[0] self._grid = self._grid[0] @@ -797,7 +798,7 @@ def __init__( name=None, ): if target is None and bounds is None: - target = 1 / 2 + bounds = (0, 1) super().__init__( coil, @@ -832,6 +833,8 @@ def compute(self, params, constants=None): data = super().compute(params, constants=constants) data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] out = jnp.array([dat["curvature"] for dat in data]) + print(out) + print(self._dim_f) return out @@ -888,7 +891,6 @@ def __init__( name=None, ): if target is None and bounds is None: - # TODO: use bounds instead target = 0 super().__init__( From 2acba22cf614d59e3d75e5e2c34ca7cde698450e Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 17:55:44 -0500 Subject: [PATCH 31/56] correct self._dim_f dim_f is now the total number of values within the array. --- desc/objectives/_geometry.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 8e489fa353..92c0e6841e 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -579,16 +579,16 @@ def build(self, use_jit=True, verbose=1): from desc.coils import CoilSet, MixedCoilSet, _Coil is_mixed_coils = isinstance(self.things[0], MixedCoilSet) + is_coil_set = isinstance(self.things[0], CoilSet) coils = tree_flatten( self.things[0], is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), )[0] - self._dim_f = len(coils) # if using single coil, make coils and grid a list so they can be # used with tree_map - coils = [coils[0]] if not is_mixed_coils else coils + coils = [coils[0]] if not is_coil_set else coils if self._grid is None: NFP = self.NFP if hasattr(self, "NFP") else 1 @@ -599,6 +599,8 @@ def build(self, use_jit=True, verbose=1): elif isinstance(self._grid, numbers.Integral): raise TypeError("The inputted grid should be of type `Grid`.") + self._dim_f = np.sum([grid.num_zeta for grid in self._grid]) + timer = Timer() if verbose > 0: print("Precomputing transforms") @@ -616,7 +618,7 @@ def build(self, use_jit=True, verbose=1): transforms = transforms[0] self._grid = self._grid[0] - self._constants = {"transforms": transforms, "is_mixed_coils": is_mixed_coils} + self._constants = {"transforms": transforms} timer.stop("Precomputing transforms") if verbose > 1: @@ -695,7 +697,7 @@ class CoilLength(_CoilObjective): def __init__( self, - coil, + coils, target=None, bounds=None, weight=1, @@ -706,11 +708,12 @@ def __init__( grid=None, name=None, ): + self._coils = coils if target is None and bounds is None: target = 2 * np.pi super().__init__( - coil, + coils, ["length"], target=target, bounds=bounds, @@ -723,6 +726,22 @@ def __init__( name=name, ) + def build(self, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + from desc.coils import CoilSet + + super().build(use_jit=use_jit, verbose=verbose) + self._dim_f = len(self._coils.coils) if isinstance(self._coils, CoilSet) else 1 + def compute(self, params, constants=None): """Compute coil length. @@ -832,9 +851,7 @@ def compute(self, params, constants=None): """ data = super().compute(params, constants=constants) data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] - out = jnp.array([dat["curvature"] for dat in data]) - print(out) - print(self._dim_f) + out = jnp.concatenate([dat["curvature"] for dat in data]) return out @@ -925,7 +942,7 @@ def compute(self, params, constants=None): """ data = super().compute(params, constants=constants) data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] - out = jnp.array([dat["torsion"] for dat in data]) + out = jnp.concatenate([dat["torsion"] for dat in data]) return out From be8fcb4e1a1a3e316d4d33f5589a1d6030c09e2a Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 18:24:02 -0500 Subject: [PATCH 32/56] add normalization --- desc/objectives/_geometry.py | 41 ++++++++++++++++++++++++++++++++ desc/objectives/normalization.py | 4 ++++ 2 files changed, 45 insertions(+) diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index 92c0e6841e..dcac0c4328 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -552,6 +552,7 @@ def __init__( ): self._grid = grid self._data_keys = data_keys + self._normalize = normalize super().__init__( things=[coil], target=target, @@ -624,6 +625,9 @@ def build(self, use_jit=True, verbose=1): if verbose > 1: timer.disp("Precomputing transforms") + if self._normalize: + self._scales = compute_scaling_factors(coils[0]) + super().build(use_jit=use_jit, verbose=verbose) def compute(self, params, constants=None): @@ -708,6 +712,7 @@ def __init__( grid=None, name=None, ): + self._coils = coils if target is None and bounds is None: target = 2 * np.pi @@ -740,6 +745,10 @@ def build(self, use_jit=True, verbose=1): from desc.coils import CoilSet super().build(use_jit=use_jit, verbose=verbose) + + if self._normalize: + self._normalization = self._scales["a"] + self._dim_f = len(self._coils.coils) if isinstance(self._coils, CoilSet) else 1 def compute(self, params, constants=None): @@ -833,6 +842,22 @@ def __init__( name=name, ) + def build(self, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + super().build(use_jit=use_jit, verbose=verbose) + + if self._normalize: + self._normalization = 1 / self._scales["a"] + def compute(self, params, constants=None): """Compute coil curvature. @@ -924,6 +949,22 @@ def __init__( name=name, ) + def build(self, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + super().build(use_jit=use_jit, verbose=verbose) + + if self._normalize: + self._normalization = 1 / self._scales["a"] ** 2 + def compute(self, params, constants=None): """Compute coil torsion. diff --git a/desc/objectives/normalization.py b/desc/objectives/normalization.py index 8b9743d86a..9ab44cad16 100644 --- a/desc/objectives/normalization.py +++ b/desc/objectives/normalization.py @@ -3,6 +3,8 @@ import numpy as np from scipy.constants import elementary_charge, mu_0 +from desc.geometry import Curve + def compute_scaling_factors(thing): """Compute dimensional quantities for normalizations.""" @@ -65,6 +67,8 @@ def get_lowest_mode(basis, coeffs): scales["A"] = np.pi * scales["a"] ** 2 scales["V"] = 2 * np.pi * scales["R0"] * scales["A"] + elif isinstance(thing, Curve): + scales["a"] = thing.compute("length")["length"] / (2 * np.pi) # replace 0 scales to avoid normalizing by zero for scale in scales.keys(): if np.isclose(scales[scale], 0): From bc129d0368802a928c0d95ac29d31f07902aa61b Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 18:27:32 -0500 Subject: [PATCH 33/56] move everything to _coils.py --- desc/objectives/_coils.py | 504 +++++++++++++++++++++++++++++++++++ desc/objectives/_geometry.py | 496 +--------------------------------- 2 files changed, 505 insertions(+), 495 deletions(-) create mode 100644 desc/objectives/_coils.py diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py new file mode 100644 index 0000000000..6071d52449 --- /dev/null +++ b/desc/objectives/_coils.py @@ -0,0 +1,504 @@ +import numbers + +import jax +import numpy as np + +from desc.backend import jnp, tree_flatten +from desc.compute import get_transforms +from desc.grid import LinearGrid +from desc.utils import Timer + +from .normalization import compute_scaling_factors +from .objective_funs import _Objective + + +class _CoilObjective(_Objective): + """Base class for calculating coil objectives. + + Parameters + ---------- + coil : CoilSet or Coil + Coil for which the data keys will be optimized. + data_keys : list of str + data keys that will be optimized when this class is inherited. + target : {float, ndarray}, optional + Target value(s) of the objective. Only used if bounds is None. + Must be broadcastable to Objective.dim_f. + bounds : tuple of {float, ndarray}, optional + Lower and upper bounds on the objective. Overrides target. + Both bounds must be broadcastable to to Objective.dim_f + weight : {float, ndarray}, optional + Weighting to apply to the Objective, relative to other Objectives. + Must be broadcastable to to Objective.dim_f + normalize : bool, optional + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool, optional + Whether target and bounds should be normalized before comparing to computed + values. If `normalize` is `True` and the target is in physical units, + this should also be set to True. + be set to True. + loss_function : {None, 'mean', 'min', 'max'}, optional + Loss function to apply to the objective values once computed. This loss function + is called on the raw compute value, before any shifting, scaling, or + normalization. Note: Has no effect for this objective. + deriv_mode : {"auto", "fwd", "rev"} + Specify how to compute jacobian matrix, either forward mode or reverse mode AD. + "auto" selects forward or reverse mode based on the size of the input and output + of the objective. Has no effect on self.grad or self.hess which always use + reverse mode and forward over reverse mode respectively. + grid : Grid, optional + Collocation grid containing the nodes to evaluate at. + name : str, optional + Name of the objective function. + + """ + + def __init__( + self, + coil, + data_keys, + target=None, + bounds=None, + weight=1, + normalize=True, + normalize_target=True, + loss_function=None, + deriv_mode="auto", + grid=None, + name=None, + ): + self._grid = grid + self._data_keys = data_keys + self._normalize = normalize + super().__init__( + things=[coil], + target=target, + bounds=bounds, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + loss_function=loss_function, + deriv_mode=deriv_mode, + name=name, + ) + + def build(self, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + # local import to avoid circular import + from desc.coils import CoilSet, MixedCoilSet, _Coil + + is_mixed_coils = isinstance(self.things[0], MixedCoilSet) + is_coil_set = isinstance(self.things[0], CoilSet) + + coils = tree_flatten( + self.things[0], + is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), + )[0] + + # if using single coil, make coils and grid a list so they can be + # used with tree_map + coils = [coils[0]] if not is_coil_set else coils + + if self._grid is None: + NFP = self.NFP if hasattr(self, "NFP") else 1 + grid = lambda x: LinearGrid(N=2 * x + 5, NFP=NFP, endpoint=False) + self._grid = [grid(coil.N) for coil in coils] + elif self._grid.num_rho > 1 or self._grid.num_theta > 1: + raise TypeError("Only use toroidal resolution for coil grids.") + elif isinstance(self._grid, numbers.Integral): + raise TypeError("The inputted grid should be of type `Grid`.") + + self._dim_f = np.sum([grid.num_zeta for grid in self._grid]) + + timer = Timer() + if verbose > 0: + print("Precomputing transforms") + timer.start("Precomputing transforms") + + transforms = jax.tree_util.tree_map( + lambda x, y: get_transforms(self._data_keys, obj=x, grid=y), + coils, + self._grid, + is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, MixedCoilSet), + ) + # tree map always returns a list so take first transform and grid + # for when we are only using a single coil + if not is_mixed_coils: + transforms = transforms[0] + self._grid = self._grid[0] + + self._constants = {"transforms": transforms} + + timer.stop("Precomputing transforms") + if verbose > 1: + timer.disp("Precomputing transforms") + + if self._normalize: + self._scales = compute_scaling_factors(coils[0]) + + super().build(use_jit=use_jit, verbose=verbose) + + def compute(self, params, constants=None): + """Compute data of coil for given data key. + + Parameters + ---------- + params : dict + Dictionary of the coil's degrees of freedom. + constants : dict + Dictionary of constant data, eg transforms, profiles etc. Defaults to + self._constants. + + Returns + ------- + f : float or array of floats + Coil length. + """ + if constants is None: + constants = self._constants + + coils = self.things[0] + data = coils.compute( + self._data_keys, + params=params, + transforms=constants["transforms"], + grid=self._grid, + ) + + return data + + +class CoilLength(_CoilObjective): + """Coil length. + + Parameters + ---------- + coil : CoilSet or Coil + Coil(s) that are to be optimized + target : {float, ndarray}, optional + Target value(s) of the objective. Only used if bounds is None. + Must be broadcastable to Objective.dim_f. If array, it has to + be flattened according to the number of inputs. + bounds : tuple of {float, ndarray}, optional + Lower and upper bounds on the objective. Overrides target. + Both bounds must be broadcastable to to Objective.dim_f + weight : {float, ndarray}, optional + Weighting to apply to the Objective, relative to other Objectives. + Must be broadcastable to to Objective.dim_f + normalize : bool, optional + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool, optional + Whether target and bounds should be normalized before comparing to computed + values. If `normalize` is `True` and the target is in physical units, + this should also be set to True. + be set to True. + loss_function : {None, 'mean', 'min', 'max'}, optional + Loss function to apply to the objective values once computed. This loss function + is called on the raw compute value, before any shifting, scaling, or + normalization. Note: Has no effect for this objective. + deriv_mode : {"auto", "fwd", "rev"} + Specify how to compute jacobian matrix, either forward mode or reverse mode AD. + "auto" selects forward or reverse mode based on the size of the input and output + of the objective. Has no effect on self.grad or self.hess which always use + reverse mode and forward over reverse mode respectively. + grid : Grid, optional + Collocation grid containing the nodes to evaluate at. + name : str, optional + Name of the objective function. + """ + + def __init__( + self, + coils, + target=None, + bounds=None, + weight=1, + normalize=True, + normalize_target=True, + loss_function=None, + deriv_mode="auto", + grid=None, + name=None, + ): + + self._coils = coils + if target is None and bounds is None: + target = 2 * np.pi + + super().__init__( + coils, + ["length"], + target=target, + bounds=bounds, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + loss_function=loss_function, + deriv_mode=deriv_mode, + grid=grid, + name=name, + ) + + def build(self, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + from desc.coils import CoilSet + + super().build(use_jit=use_jit, verbose=verbose) + + if self._normalize: + self._normalization = self._scales["a"] + + self._dim_f = len(self._coils.coils) if isinstance(self._coils, CoilSet) else 1 + + def compute(self, params, constants=None): + """Compute coil length. + + Parameters + ---------- + params : dict + Dictionary of the coil's degrees of freedom. + constants : dict + Dictionary of constant data, eg transforms, profiles etc. Defaults to + self._constants. + + Returns + ------- + f : float or array of floats + Coil length. + """ + data = super().compute(params, constants=constants) + data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] + out = jnp.array([dat["length"] for dat in data]) + return out + + +class CoilCurvature(_CoilObjective): + """Coil curvature. + + Parameters + ---------- + coil : CoilSet or Coil + Coil(s) that are to be optimized + target : {float, ndarray}, optional + Target value(s) of the objective. Only used if bounds is None. + Must be broadcastable to Objective.dim_f. If array, it has to + be flattened according to the number of inputs. + bounds : tuple of {float, ndarray}, optional + Lower and upper bounds on the objective. Overrides target. + Both bounds must be broadcastable to to Objective.dim_f + weight : {float, ndarray}, optional + Weighting to apply to the Objective, relative to other Objectives. + Must be broadcastable to to Objective.dim_f + normalize : bool, optional + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool, optional + Whether target and bounds should be normalized before comparing to computed + values. If `normalize` is `True` and the target is in physical units, + this should also be set to True. + be set to True. + loss_function : {None, 'mean', 'min', 'max'}, optional + Loss function to apply to the objective values once computed. This loss function + is called on the raw compute value, before any shifting, scaling, or + normalization. Note: Has no effect for this objective. + deriv_mode : {"auto", "fwd", "rev"} + Specify how to compute jacobian matrix, either forward mode or reverse mode AD. + "auto" selects forward or reverse mode based on the size of the input and output + of the objective. Has no effect on self.grad or self.hess which always use + reverse mode and forward over reverse mode respectively. + grid : Grid, optional + Collocation grid containing the nodes to evaluate at. + name : str, optional + Name of the objective function. + """ + + def __init__( + self, + coil, + target=None, + bounds=None, + weight=1, + normalize=True, + normalize_target=True, + loss_function=None, + deriv_mode="auto", + grid=None, + name=None, + ): + if target is None and bounds is None: + bounds = (0, 1) + + super().__init__( + coil, + ["curvature"], + target=target, + bounds=bounds, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + loss_function=loss_function, + deriv_mode=deriv_mode, + grid=grid, + name=name, + ) + + def build(self, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + super().build(use_jit=use_jit, verbose=verbose) + + if self._normalize: + self._normalization = 1 / self._scales["a"] + + def compute(self, params, constants=None): + """Compute coil curvature. + + Parameters + ---------- + params : dict + Dictionary of the coil's degrees of freedom. + constants : dict + Dictionary of constant data, eg transforms, profiles etc. Defaults to + self._constants. + + Returns + ------- + f : float or array of floats + Coil curvature. + """ + data = super().compute(params, constants=constants) + data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] + out = jnp.concatenate([dat["curvature"] for dat in data]) + return out + + +class CoilTorsion(_CoilObjective): + """Coil torsion. + + Parameters + ---------- + coil : CoilSet or Coil + Coil(s) that are to be optimized + target : {float, ndarray}, optional + Target value(s) of the objective. Only used if bounds is None. + Must be broadcastable to Objective.dim_f. If array, it has to + be flattened according to the number of inputs. + bounds : tuple of {float, ndarray}, optional + Lower and upper bounds on the objective. Overrides target. + Both bounds must be broadcastable to to Objective.dim_f + weight : {float, ndarray}, optional + Weighting to apply to the Objective, relative to other Objectives. + Must be broadcastable to to Objective.dim_f + normalize : bool, optional + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool, optional + Whether target and bounds should be normalized before comparing to computed + values. If `normalize` is `True` and the target is in physical units, + this should also be set to True. + be set to True. + loss_function : {None, 'mean', 'min', 'max'}, optional + Loss function to apply to the objective values once computed. This loss function + is called on the raw compute value, before any shifting, scaling, or + normalization. Note: Has no effect for this objective. + deriv_mode : {"auto", "fwd", "rev"} + Specify how to compute jacobian matrix, either forward mode or reverse mode AD. + "auto" selects forward or reverse mode based on the size of the input and output + of the objective. Has no effect on self.grad or self.hess which always use + reverse mode and forward over reverse mode respectively. + grid : Grid, optional + Collocation grid containing the nodes to evaluate at. + name : str, optional + Name of the objective function. + """ + + def __init__( + self, + coil, + target=None, + bounds=None, + weight=1, + normalize=True, + normalize_target=True, + loss_function=None, + deriv_mode="auto", + grid=None, + name=None, + ): + if target is None and bounds is None: + target = 0 + + super().__init__( + coil, + ["torsion"], + target=target, + bounds=bounds, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + loss_function=loss_function, + deriv_mode=deriv_mode, + grid=grid, + name=name, + ) + + def build(self, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + super().build(use_jit=use_jit, verbose=verbose) + + if self._normalize: + self._normalization = 1 / self._scales["a"] ** 2 + + def compute(self, params, constants=None): + """Compute coil torsion. + + Parameters + ---------- + params : dict + Dictionary of the coil's degrees of freedom. + constants : dict + Dictionary of constant data, eg transforms, profiles etc. Defaults to + self._constants. + + Returns + ------- + f : float or array of floats + Coil torsion. + """ + data = super().compute(params, constants=constants) + data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] + out = jnp.concatenate([dat["torsion"] for dat in data]) + return out diff --git a/desc/objectives/_geometry.py b/desc/objectives/_geometry.py index dcac0c4328..43e79283e8 100644 --- a/desc/objectives/_geometry.py +++ b/desc/objectives/_geometry.py @@ -1,12 +1,10 @@ """Objectives for targeting geometrical quantities.""" -import numbers import warnings -import jax import numpy as np -from desc.backend import jnp, tree_flatten +from desc.backend import jnp from desc.compute import compute as compute_fun from desc.compute import get_profiles, get_transforms, rpz2xyz from desc.compute.utils import safenorm @@ -495,498 +493,6 @@ def compute(self, params, constants=None): return data["V"] -class _CoilObjective(_Objective): - """Base class for calculating coil objectives. - - Parameters - ---------- - coil : CoilSet or Coil - Coil for which the data keys will be optimized. - data_keys : list of str - data keys that will be optimized when this class is inherited. - target : {float, ndarray}, optional - Target value(s) of the objective. Only used if bounds is None. - Must be broadcastable to Objective.dim_f. - bounds : tuple of {float, ndarray}, optional - Lower and upper bounds on the objective. Overrides target. - Both bounds must be broadcastable to to Objective.dim_f - weight : {float, ndarray}, optional - Weighting to apply to the Objective, relative to other Objectives. - Must be broadcastable to to Objective.dim_f - normalize : bool, optional - Whether to compute the error in physical units or non-dimensionalize. - normalize_target : bool, optional - Whether target and bounds should be normalized before comparing to computed - values. If `normalize` is `True` and the target is in physical units, - this should also be set to True. - be set to True. - loss_function : {None, 'mean', 'min', 'max'}, optional - Loss function to apply to the objective values once computed. This loss function - is called on the raw compute value, before any shifting, scaling, or - normalization. Note: Has no effect for this objective. - deriv_mode : {"auto", "fwd", "rev"} - Specify how to compute jacobian matrix, either forward mode or reverse mode AD. - "auto" selects forward or reverse mode based on the size of the input and output - of the objective. Has no effect on self.grad or self.hess which always use - reverse mode and forward over reverse mode respectively. - grid : Grid, optional - Collocation grid containing the nodes to evaluate at. - name : str, optional - Name of the objective function. - - """ - - def __init__( - self, - coil, - data_keys, - target=None, - bounds=None, - weight=1, - normalize=True, - normalize_target=True, - loss_function=None, - deriv_mode="auto", - grid=None, - name=None, - ): - self._grid = grid - self._data_keys = data_keys - self._normalize = normalize - super().__init__( - things=[coil], - target=target, - bounds=bounds, - weight=weight, - normalize=normalize, - normalize_target=normalize_target, - loss_function=loss_function, - deriv_mode=deriv_mode, - name=name, - ) - - def build(self, use_jit=True, verbose=1): - """Build constant arrays. - - Parameters - ---------- - use_jit : bool, optional - Whether to just-in-time compile the objective and derivatives. - verbose : int, optional - Level of output. - - """ - # local import to avoid circular import - from desc.coils import CoilSet, MixedCoilSet, _Coil - - is_mixed_coils = isinstance(self.things[0], MixedCoilSet) - is_coil_set = isinstance(self.things[0], CoilSet) - - coils = tree_flatten( - self.things[0], - is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), - )[0] - - # if using single coil, make coils and grid a list so they can be - # used with tree_map - coils = [coils[0]] if not is_coil_set else coils - - if self._grid is None: - NFP = self.NFP if hasattr(self, "NFP") else 1 - grid = lambda x: LinearGrid(N=2 * x + 5, NFP=NFP, endpoint=False) - self._grid = [grid(coil.N) for coil in coils] - elif self._grid.num_rho > 1 or self._grid.num_theta > 1: - raise TypeError("Only use toroidal resolution for coil grids.") - elif isinstance(self._grid, numbers.Integral): - raise TypeError("The inputted grid should be of type `Grid`.") - - self._dim_f = np.sum([grid.num_zeta for grid in self._grid]) - - timer = Timer() - if verbose > 0: - print("Precomputing transforms") - timer.start("Precomputing transforms") - - transforms = jax.tree_util.tree_map( - lambda x, y: get_transforms(self._data_keys, obj=x, grid=y), - coils, - self._grid, - is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, MixedCoilSet), - ) - # tree map always returns a list so take first transform and grid - # for when we are only using a single coil - if not is_mixed_coils: - transforms = transforms[0] - self._grid = self._grid[0] - - self._constants = {"transforms": transforms} - - timer.stop("Precomputing transforms") - if verbose > 1: - timer.disp("Precomputing transforms") - - if self._normalize: - self._scales = compute_scaling_factors(coils[0]) - - super().build(use_jit=use_jit, verbose=verbose) - - def compute(self, params, constants=None): - """Compute data of coil for given data key. - - Parameters - ---------- - params : dict - Dictionary of the coil's degrees of freedom. - constants : dict - Dictionary of constant data, eg transforms, profiles etc. Defaults to - self._constants. - - Returns - ------- - f : float or array of floats - Coil length. - """ - if constants is None: - constants = self._constants - - coils = self.things[0] - data = coils.compute( - self._data_keys, - params=params, - transforms=constants["transforms"], - grid=self._grid, - ) - - return data - - -class CoilLength(_CoilObjective): - """Coil length. - - Parameters - ---------- - coil : CoilSet or Coil - Coil(s) that are to be optimized - target : {float, ndarray}, optional - Target value(s) of the objective. Only used if bounds is None. - Must be broadcastable to Objective.dim_f. If array, it has to - be flattened according to the number of inputs. - bounds : tuple of {float, ndarray}, optional - Lower and upper bounds on the objective. Overrides target. - Both bounds must be broadcastable to to Objective.dim_f - weight : {float, ndarray}, optional - Weighting to apply to the Objective, relative to other Objectives. - Must be broadcastable to to Objective.dim_f - normalize : bool, optional - Whether to compute the error in physical units or non-dimensionalize. - normalize_target : bool, optional - Whether target and bounds should be normalized before comparing to computed - values. If `normalize` is `True` and the target is in physical units, - this should also be set to True. - be set to True. - loss_function : {None, 'mean', 'min', 'max'}, optional - Loss function to apply to the objective values once computed. This loss function - is called on the raw compute value, before any shifting, scaling, or - normalization. Note: Has no effect for this objective. - deriv_mode : {"auto", "fwd", "rev"} - Specify how to compute jacobian matrix, either forward mode or reverse mode AD. - "auto" selects forward or reverse mode based on the size of the input and output - of the objective. Has no effect on self.grad or self.hess which always use - reverse mode and forward over reverse mode respectively. - grid : Grid, optional - Collocation grid containing the nodes to evaluate at. - name : str, optional - Name of the objective function. - """ - - def __init__( - self, - coils, - target=None, - bounds=None, - weight=1, - normalize=True, - normalize_target=True, - loss_function=None, - deriv_mode="auto", - grid=None, - name=None, - ): - - self._coils = coils - if target is None and bounds is None: - target = 2 * np.pi - - super().__init__( - coils, - ["length"], - target=target, - bounds=bounds, - weight=weight, - normalize=normalize, - normalize_target=normalize_target, - loss_function=loss_function, - deriv_mode=deriv_mode, - grid=grid, - name=name, - ) - - def build(self, use_jit=True, verbose=1): - """Build constant arrays. - - Parameters - ---------- - use_jit : bool, optional - Whether to just-in-time compile the objective and derivatives. - verbose : int, optional - Level of output. - - """ - from desc.coils import CoilSet - - super().build(use_jit=use_jit, verbose=verbose) - - if self._normalize: - self._normalization = self._scales["a"] - - self._dim_f = len(self._coils.coils) if isinstance(self._coils, CoilSet) else 1 - - def compute(self, params, constants=None): - """Compute coil length. - - Parameters - ---------- - params : dict - Dictionary of the coil's degrees of freedom. - constants : dict - Dictionary of constant data, eg transforms, profiles etc. Defaults to - self._constants. - - Returns - ------- - f : float or array of floats - Coil length. - """ - data = super().compute(params, constants=constants) - data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] - out = jnp.array([dat["length"] for dat in data]) - return out - - -class CoilCurvature(_CoilObjective): - """Coil curvature. - - Parameters - ---------- - coil : CoilSet or Coil - Coil(s) that are to be optimized - target : {float, ndarray}, optional - Target value(s) of the objective. Only used if bounds is None. - Must be broadcastable to Objective.dim_f. If array, it has to - be flattened according to the number of inputs. - bounds : tuple of {float, ndarray}, optional - Lower and upper bounds on the objective. Overrides target. - Both bounds must be broadcastable to to Objective.dim_f - weight : {float, ndarray}, optional - Weighting to apply to the Objective, relative to other Objectives. - Must be broadcastable to to Objective.dim_f - normalize : bool, optional - Whether to compute the error in physical units or non-dimensionalize. - normalize_target : bool, optional - Whether target and bounds should be normalized before comparing to computed - values. If `normalize` is `True` and the target is in physical units, - this should also be set to True. - be set to True. - loss_function : {None, 'mean', 'min', 'max'}, optional - Loss function to apply to the objective values once computed. This loss function - is called on the raw compute value, before any shifting, scaling, or - normalization. Note: Has no effect for this objective. - deriv_mode : {"auto", "fwd", "rev"} - Specify how to compute jacobian matrix, either forward mode or reverse mode AD. - "auto" selects forward or reverse mode based on the size of the input and output - of the objective. Has no effect on self.grad or self.hess which always use - reverse mode and forward over reverse mode respectively. - grid : Grid, optional - Collocation grid containing the nodes to evaluate at. - name : str, optional - Name of the objective function. - """ - - def __init__( - self, - coil, - target=None, - bounds=None, - weight=1, - normalize=True, - normalize_target=True, - loss_function=None, - deriv_mode="auto", - grid=None, - name=None, - ): - if target is None and bounds is None: - bounds = (0, 1) - - super().__init__( - coil, - ["curvature"], - target=target, - bounds=bounds, - weight=weight, - normalize=normalize, - normalize_target=normalize_target, - loss_function=loss_function, - deriv_mode=deriv_mode, - grid=grid, - name=name, - ) - - def build(self, use_jit=True, verbose=1): - """Build constant arrays. - - Parameters - ---------- - use_jit : bool, optional - Whether to just-in-time compile the objective and derivatives. - verbose : int, optional - Level of output. - - """ - super().build(use_jit=use_jit, verbose=verbose) - - if self._normalize: - self._normalization = 1 / self._scales["a"] - - def compute(self, params, constants=None): - """Compute coil curvature. - - Parameters - ---------- - params : dict - Dictionary of the coil's degrees of freedom. - constants : dict - Dictionary of constant data, eg transforms, profiles etc. Defaults to - self._constants. - - Returns - ------- - f : float or array of floats - Coil curvature. - """ - data = super().compute(params, constants=constants) - data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] - out = jnp.concatenate([dat["curvature"] for dat in data]) - return out - - -class CoilTorsion(_CoilObjective): - """Coil torsion. - - Parameters - ---------- - coil : CoilSet or Coil - Coil(s) that are to be optimized - target : {float, ndarray}, optional - Target value(s) of the objective. Only used if bounds is None. - Must be broadcastable to Objective.dim_f. If array, it has to - be flattened according to the number of inputs. - bounds : tuple of {float, ndarray}, optional - Lower and upper bounds on the objective. Overrides target. - Both bounds must be broadcastable to to Objective.dim_f - weight : {float, ndarray}, optional - Weighting to apply to the Objective, relative to other Objectives. - Must be broadcastable to to Objective.dim_f - normalize : bool, optional - Whether to compute the error in physical units or non-dimensionalize. - normalize_target : bool, optional - Whether target and bounds should be normalized before comparing to computed - values. If `normalize` is `True` and the target is in physical units, - this should also be set to True. - be set to True. - loss_function : {None, 'mean', 'min', 'max'}, optional - Loss function to apply to the objective values once computed. This loss function - is called on the raw compute value, before any shifting, scaling, or - normalization. Note: Has no effect for this objective. - deriv_mode : {"auto", "fwd", "rev"} - Specify how to compute jacobian matrix, either forward mode or reverse mode AD. - "auto" selects forward or reverse mode based on the size of the input and output - of the objective. Has no effect on self.grad or self.hess which always use - reverse mode and forward over reverse mode respectively. - grid : Grid, optional - Collocation grid containing the nodes to evaluate at. - name : str, optional - Name of the objective function. - """ - - def __init__( - self, - coil, - target=None, - bounds=None, - weight=1, - normalize=True, - normalize_target=True, - loss_function=None, - deriv_mode="auto", - grid=None, - name=None, - ): - if target is None and bounds is None: - target = 0 - - super().__init__( - coil, - ["torsion"], - target=target, - bounds=bounds, - weight=weight, - normalize=normalize, - normalize_target=normalize_target, - loss_function=loss_function, - deriv_mode=deriv_mode, - grid=grid, - name=name, - ) - - def build(self, use_jit=True, verbose=1): - """Build constant arrays. - - Parameters - ---------- - use_jit : bool, optional - Whether to just-in-time compile the objective and derivatives. - verbose : int, optional - Level of output. - - """ - super().build(use_jit=use_jit, verbose=verbose) - - if self._normalize: - self._normalization = 1 / self._scales["a"] ** 2 - - def compute(self, params, constants=None): - """Compute coil torsion. - - Parameters - ---------- - params : dict - Dictionary of the coil's degrees of freedom. - constants : dict - Dictionary of constant data, eg transforms, profiles etc. Defaults to - self._constants. - - Returns - ------- - f : float or array of floats - Coil torsion. - """ - data = super().compute(params, constants=constants) - data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] - out = jnp.concatenate([dat["torsion"] for dat in data]) - return out - - class PlasmaVesselDistance(_Objective): """Target the distance between the plasma and a surrounding surface. From 7beb15727228b31eb77cc5fb39f4bbb29ec1e6b8 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Thu, 7 Mar 2024 21:17:51 -0500 Subject: [PATCH 34/56] import from _coils --- desc/objectives/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/desc/objectives/__init__.py b/desc/objectives/__init__.py index 3310096164..43800c4cdb 100644 --- a/desc/objectives/__init__.py +++ b/desc/objectives/__init__.py @@ -1,6 +1,7 @@ """Classes defining objectives for equilibrium and optimization.""" from ._bootstrap import BootstrapRedlConsistency +from ._coils import CoilCurvature, CoilLength, CoilTorsion from ._equilibrium import ( CurrentDensity, Energy, @@ -14,9 +15,6 @@ from ._geometry import ( AspectRatio, BScaleLength, - CoilCurvature, - CoilLength, - CoilTorsion, Elongation, GoodCoordinates, MeanCurvature, From 3523178010d1575b8d76e86d6254bf7c0bf39205 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sat, 9 Mar 2024 03:22:13 -0500 Subject: [PATCH 35/56] addressed reviews - import tree_map in backend - use coil for NFP - value error instead of type error - updated docstrings with more details - check shape in tests - add list of grids to tests --- desc/backend.py | 11 ++++++- desc/objectives/_coils.py | 63 ++++++++++++++++++++---------------- tests/test_objective_funs.py | 24 ++++++++------ 3 files changed, 61 insertions(+), 37 deletions(-) diff --git a/desc/backend.py b/desc/backend.py index 1433abe73e..89687f4670 100644 --- a/desc/backend.py +++ b/desc/backend.py @@ -77,7 +77,12 @@ from jax.experimental.ode import odeint from jax.scipy.linalg import block_diag, cho_factor, cho_solve, qr, solve_triangular from jax.scipy.special import gammaln, logsumexp - from jax.tree_util import register_pytree_node, tree_flatten, tree_unflatten + from jax.tree_util import ( + register_pytree_node, + tree_flatten, + tree_map, + tree_unflatten, + ) def put(arr, inds, vals): """Functional interface for array "fancy indexing". @@ -393,6 +398,10 @@ def tree_unflatten(*args, **kwargs): """Unflatten pytree for numpy backend.""" raise NotImplementedError + def tree_map(*args, **kwargs): + """Map pytree for numpy backend.""" + raise NotImplementedError + def register_pytree_node(foo, *args): """Dummy decorator for non-jax pytrees.""" return foo diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 6071d52449..99422586c6 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -1,9 +1,8 @@ import numbers -import jax import numpy as np -from desc.backend import jnp, tree_flatten +from desc.backend import jnp, tree_flatten, tree_map from desc.compute import get_transforms from desc.grid import LinearGrid from desc.utils import Timer @@ -21,13 +20,13 @@ class _CoilObjective(_Objective): Coil for which the data keys will be optimized. data_keys : list of str data keys that will be optimized when this class is inherited. - target : {float, ndarray}, optional + target : float, ndarray, optional Target value(s) of the objective. Only used if bounds is None. Must be broadcastable to Objective.dim_f. - bounds : tuple of {float, ndarray}, optional + bounds : tuple of float, ndarray, optional Lower and upper bounds on the objective. Overrides target. Both bounds must be broadcastable to to Objective.dim_f - weight : {float, ndarray}, optional + weight : float, ndarray, optional Weighting to apply to the Objective, relative to other Objectives. Must be broadcastable to to Objective.dim_f normalize : bool, optional @@ -46,8 +45,9 @@ class _CoilObjective(_Objective): "auto" selects forward or reverse mode based on the size of the input and output of the objective. Has no effect on self.grad or self.hess which always use reverse mode and forward over reverse mode respectively. - grid : Grid, optional - Collocation grid containing the nodes to evaluate at. + grid : Grid, list, optional + Collocation grid containing the nodes to evaluate at. If list, has to adhere to + Objective.dim_f name : str, optional Name of the objective function. @@ -107,15 +107,18 @@ def build(self, use_jit=True, verbose=1): # if using single coil, make coils and grid a list so they can be # used with tree_map coils = [coils[0]] if not is_coil_set else coils + if not isinstance(self._grid, list) and self._grid is not None: + self._grid = [self._grid] if self._grid is None: - NFP = self.NFP if hasattr(self, "NFP") else 1 - grid = lambda x: LinearGrid(N=2 * x + 5, NFP=NFP, endpoint=False) - self._grid = [grid(coil.N) for coil in coils] - elif self._grid.num_rho > 1 or self._grid.num_theta > 1: - raise TypeError("Only use toroidal resolution for coil grids.") + get_grid = lambda x: LinearGrid( + N=2 * x.N + 5, NFP=getattr(x, "NFP", 1), endpoint=False + ) + self._grid = [get_grid(coil) for coil in coils] + elif np.any([grid.num_rho > 1 or grid.num_theta > 1 for grid in self._grid]): + raise ValueError("Only use toroidal resolution for coil grids.") elif isinstance(self._grid, numbers.Integral): - raise TypeError("The inputted grid should be of type `Grid`.") + self._grid = LinearGrid(N=self._grid) self._dim_f = np.sum([grid.num_zeta for grid in self._grid]) @@ -124,7 +127,7 @@ def build(self, use_jit=True, verbose=1): print("Precomputing transforms") timer.start("Precomputing transforms") - transforms = jax.tree_util.tree_map( + transforms = tree_map( lambda x, y: get_transforms(self._data_keys, obj=x, grid=y), coils, self._grid, @@ -184,14 +187,14 @@ class CoilLength(_CoilObjective): ---------- coil : CoilSet or Coil Coil(s) that are to be optimized - target : {float, ndarray}, optional + target : float, ndarray, optional Target value(s) of the objective. Only used if bounds is None. Must be broadcastable to Objective.dim_f. If array, it has to be flattened according to the number of inputs. - bounds : tuple of {float, ndarray}, optional + bounds : tuple of float, ndarray, optional Lower and upper bounds on the objective. Overrides target. Both bounds must be broadcastable to to Objective.dim_f - weight : {float, ndarray}, optional + weight : float, ndarray, optional Weighting to apply to the Objective, relative to other Objectives. Must be broadcastable to to Objective.dim_f normalize : bool, optional @@ -229,7 +232,6 @@ def __init__( grid=None, name=None, ): - self._coils = coils if target is None and bounds is None: target = 2 * np.pi @@ -293,18 +295,21 @@ def compute(self, params, constants=None): class CoilCurvature(_CoilObjective): """Coil curvature. + Targets the local curvature value per grid node for each coil. A smaller curvature + value indicates straighter coils. All curvature values are positive. + Parameters ---------- coil : CoilSet or Coil Coil(s) that are to be optimized - target : {float, ndarray}, optional + target : float, ndarray, optional Target value(s) of the objective. Only used if bounds is None. Must be broadcastable to Objective.dim_f. If array, it has to be flattened according to the number of inputs. - bounds : tuple of {float, ndarray}, optional + bounds : tuple of float, ndarray, optional Lower and upper bounds on the objective. Overrides target. Both bounds must be broadcastable to to Objective.dim_f - weight : {float, ndarray}, optional + weight : float, ndarray, optional Weighting to apply to the Objective, relative to other Objectives. Must be broadcastable to to Objective.dim_f normalize : bool, optional @@ -388,8 +393,8 @@ def compute(self, params, constants=None): Returns ------- - f : float or array of floats - Coil curvature. + f : array of floats + 1D array of coil curvature values. """ data = super().compute(params, constants=constants) data = tree_flatten(data, is_leaf=lambda x: isinstance(x, dict))[0] @@ -400,18 +405,22 @@ def compute(self, params, constants=None): class CoilTorsion(_CoilObjective): """Coil torsion. + Targets the local torsion value per grid node for each coil. Indicative + of how much the coil goes out of the poloidal plane. e.g. a torsion + value of 0 means the coil is completely planar. + Parameters ---------- coil : CoilSet or Coil Coil(s) that are to be optimized - target : {float, ndarray}, optional + target : float, ndarray, optional Target value(s) of the objective. Only used if bounds is None. Must be broadcastable to Objective.dim_f. If array, it has to be flattened according to the number of inputs. - bounds : tuple of {float, ndarray}, optional + bounds : tuple of float, ndarray, optional Lower and upper bounds on the objective. Overrides target. Both bounds must be broadcastable to to Objective.dim_f - weight : {float, ndarray}, optional + weight : float, ndarray, optional Weighting to apply to the Objective, relative to other Objectives. Must be broadcastable to to Objective.dim_f normalize : bool, optional @@ -480,7 +489,7 @@ def build(self, use_jit=True, verbose=1): super().build(use_jit=use_jit, verbose=verbose) if self._normalize: - self._normalization = 1 / self._scales["a"] ** 2 + self._normalization = 1 / self._scales["a"] def compute(self, params, constants=None): """Compute coil torsion. diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index 23d94fa554..c01fa11916 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -576,60 +576,66 @@ def test(eq): def test_coil_length(self): """Tests coil length.""" - def test(coil): - obj = CoilLength(coil, weight=1) + def test(coil, grid=None): + obj = CoilLength(coil, grid=grid) obj.build() f = obj.compute(params=coil.params_dict) np.testing.assert_allclose(f, 2 * np.pi, rtol=1e-8) + assert len(f) == obj.dim_f coil = FourierPlanarCoil(r_n=1) coils = CoilSet.linspaced_linear(coil, n=4) mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) + mixed_coils_grid = [LinearGrid(N=5)] * len(mixed_coils.coils) nested_coils = MixedCoilSet.from_symmetry(mixed_coils, NFP=4) test(coil) test(coils) - test(mixed_coils) + test(mixed_coils, grid=mixed_coils_grid) test(nested_coils) @pytest.mark.unit def test_coil_curvature(self): """Tests coil curvature.""" - def test(coil): - obj = CoilCurvature(coil, weight=1) + def test(coil, grid=None): + obj = CoilCurvature(coil, grid=grid) obj.build() f = obj.compute(params=coil.params_dict) np.testing.assert_allclose(f, 1 / 2, rtol=1e-8) + assert len(f) == obj.dim_f coil = FourierPlanarCoil() coils = CoilSet.linspaced_linear(coil, n=4) mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) + mixed_coils_grid = [LinearGrid(N=5)] * len(mixed_coils.coils) nested_coils = MixedCoilSet.from_symmetry(mixed_coils, NFP=4) test(coil) test(coils) - test(mixed_coils) + test(mixed_coils, grid=mixed_coils_grid) test(nested_coils) @pytest.mark.unit def test_coil_torsion(self): """Tests coil torsion.""" - def test(coil): - obj = CoilTorsion(coil, weight=1) + def test(coil, grid=None): + obj = CoilTorsion(coil, grid=grid) obj.build() f = obj.compute(params=coil.params_dict) np.testing.assert_allclose(f, 0, atol=1e-8) + assert len(f) == obj.dim_f coil = FourierPlanarCoil() coils = CoilSet.linspaced_linear(coil, n=4) mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) + mixed_coils_grid = [LinearGrid(N=5)] * len(mixed_coils.coils) nested_coils = MixedCoilSet.from_symmetry(mixed_coils, NFP=4) test(coil) test(coils) - test(mixed_coils) + test(mixed_coils, grid=mixed_coils_grid) test(nested_coils) From cdd348e97171e11a8f2f1c5286930384b95a8c55 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Sat, 9 Mar 2024 22:13:20 -0500 Subject: [PATCH 36/56] fix logic for type checking --- desc/objectives/_coils.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 99422586c6..26eda9e0d3 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -107,18 +107,21 @@ def build(self, use_jit=True, verbose=1): # if using single coil, make coils and grid a list so they can be # used with tree_map coils = [coils[0]] if not is_coil_set else coils - if not isinstance(self._grid, list) and self._grid is not None: - self._grid = [self._grid] + # check type + if isinstance(self._grid, numbers.Integral): + self._grid = LinearGrid(N=self._grid, endpoint=False) if self._grid is None: get_grid = lambda x: LinearGrid( N=2 * x.N + 5, NFP=getattr(x, "NFP", 1), endpoint=False ) self._grid = [get_grid(coil) for coil in coils] - elif np.any([grid.num_rho > 1 or grid.num_theta > 1 for grid in self._grid]): + + if not isinstance(self._grid, (tuple, list)): + self._grid = [self._grid] + + if np.any([grid.num_rho > 1 or grid.num_theta > 1 for grid in self._grid]): raise ValueError("Only use toroidal resolution for coil grids.") - elif isinstance(self._grid, numbers.Integral): - self._grid = LinearGrid(N=self._grid) self._dim_f = np.sum([grid.num_zeta for grid in self._grid]) From fdb6cae7a1d4f55daec19f0f136165be36260176 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 11 Mar 2024 13:42:35 -0400 Subject: [PATCH 37/56] add actual nested coilset trying to use tree_map for grids but not working --- desc/objectives/_coils.py | 13 +++++++++---- tests/test_objective_funs.py | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 26eda9e0d3..9138cd6245 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -112,10 +112,14 @@ def build(self, use_jit=True, verbose=1): if isinstance(self._grid, numbers.Integral): self._grid = LinearGrid(N=self._grid, endpoint=False) if self._grid is None: - get_grid = lambda x: LinearGrid( - N=2 * x.N + 5, NFP=getattr(x, "NFP", 1), endpoint=False + self._grid = tree_map( + lambda x: LinearGrid( + N=2 * x.N + 5, NFP=getattr(x, "NFP", 1), endpoint=False + ), + self.things[0], + is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), ) - self._grid = [get_grid(coil) for coil in coils] + print(self._grid) if not isinstance(self._grid, (tuple, list)): self._grid = [self._grid] @@ -132,10 +136,11 @@ def build(self, use_jit=True, verbose=1): transforms = tree_map( lambda x, y: get_transforms(self._data_keys, obj=x, grid=y), - coils, + self.things[0], self._grid, is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, MixedCoilSet), ) + # tree map always returns a list so take first transform and grid # for when we are only using a single coil if not is_mixed_coils: diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index c01fa11916..dfc9c48865 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -587,7 +587,7 @@ def test(coil, grid=None): coils = CoilSet.linspaced_linear(coil, n=4) mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) mixed_coils_grid = [LinearGrid(N=5)] * len(mixed_coils.coils) - nested_coils = MixedCoilSet.from_symmetry(mixed_coils, NFP=4) + nested_coils = MixedCoilSet(coils, coils) test(coil) test(coils) @@ -609,7 +609,7 @@ def test(coil, grid=None): coils = CoilSet.linspaced_linear(coil, n=4) mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) mixed_coils_grid = [LinearGrid(N=5)] * len(mixed_coils.coils) - nested_coils = MixedCoilSet.from_symmetry(mixed_coils, NFP=4) + nested_coils = MixedCoilSet(coils, coils) test(coil) test(coils) @@ -631,7 +631,7 @@ def test(coil, grid=None): coils = CoilSet.linspaced_linear(coil, n=4) mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) mixed_coils_grid = [LinearGrid(N=5)] * len(mixed_coils.coils) - nested_coils = MixedCoilSet.from_symmetry(mixed_coils, NFP=4) + nested_coils = MixedCoilSet(coils, coils) test(coil) test(coils) From 7c06e13592ba87d3b27f9e0bf8222fbda56b9917 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Tue, 12 Mar 2024 15:19:24 -0400 Subject: [PATCH 38/56] change grid logic - have a case for a single LinearGrid - have a case for nested grids - get dim_f correctly --- desc/backend.py | 10 ++++ desc/objectives/_coils.py | 108 ++++++++++++++++++++++++++--------- tests/test_objective_funs.py | 48 ++++++++++------ 3 files changed, 122 insertions(+), 44 deletions(-) diff --git a/desc/backend.py b/desc/backend.py index 89687f4670..721920190c 100644 --- a/desc/backend.py +++ b/desc/backend.py @@ -80,7 +80,9 @@ from jax.tree_util import ( register_pytree_node, tree_flatten, + tree_leaves, tree_map, + tree_structure, tree_unflatten, ) @@ -402,6 +404,14 @@ def tree_map(*args, **kwargs): """Map pytree for numpy backend.""" raise NotImplementedError + def tree_structure(*args, **kwargs): + """Get structure of pytree for numpy backend.""" + raise NotImplementedError + + def tree_leaves(*args, **kwargs): + """Get leaves of pytree for numpy backend.""" + raise NotImplementedError + def register_pytree_node(foo, *args): """Dummy decorator for non-jax pytrees.""" return foo diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 9138cd6245..5bf445f636 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -2,7 +2,14 @@ import numpy as np -from desc.backend import jnp, tree_flatten, tree_map +from desc.backend import ( + jnp, + tree_flatten, + tree_leaves, + tree_map, + tree_structure, + tree_unflatten, +) from desc.compute import get_transforms from desc.grid import LinearGrid from desc.utils import Timer @@ -82,7 +89,7 @@ def __init__( name=name, ) - def build(self, use_jit=True, verbose=1): + def build(self, use_jit=True, verbose=1): # noqa:C901 """Build constant arrays. Parameters @@ -96,17 +103,33 @@ def build(self, use_jit=True, verbose=1): # local import to avoid circular import from desc.coils import CoilSet, MixedCoilSet, _Coil - is_mixed_coils = isinstance(self.things[0], MixedCoilSet) - is_coil_set = isinstance(self.things[0], CoilSet) - - coils = tree_flatten( - self.things[0], - is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), - )[0] + self._dim_f = 0 + + def get_dim_f(coilset): + """Turn a coilset into nested lists.""" + if isinstance(coilset, list): + [get_dim_f(x) for x in coilset] + elif isinstance(coilset, MixedCoilSet): + [get_dim_f(x) for x in coilset] + elif isinstance(coilset, CoilSet): + get_dim_f(coilset.coils) + elif isinstance(coilset, LinearGrid): + self._dim_f += coilset.num_zeta + + def to_list(coilset): + """Turn a coilset into nested lists.""" + if isinstance(coilset, list): + return [to_list(x) for x in coilset] + if isinstance(coilset, MixedCoilSet): + return [to_list(x) for x in coilset] + if isinstance(coilset, CoilSet): + # use the same grid/transform for CoilSet + return to_list(coilset.coils[0]) + else: + return coilset - # if using single coil, make coils and grid a list so they can be - # used with tree_map - coils = [coils[0]] if not is_coil_set else coils + is_mixed_coils = isinstance(self.things[0], MixedCoilSet) + is_single_coil = lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet) # check type if isinstance(self._grid, numbers.Integral): @@ -117,17 +140,25 @@ def build(self, use_jit=True, verbose=1): N=2 * x.N + 5, NFP=getattr(x, "NFP", 1), endpoint=False ), self.things[0], - is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), + is_leaf=lambda x: is_single_coil(x), ) - print(self._grid) - - if not isinstance(self._grid, (tuple, list)): - self._grid = [self._grid] - - if np.any([grid.num_rho > 1 or grid.num_theta > 1 for grid in self._grid]): - raise ValueError("Only use toroidal resolution for coil grids.") - - self._dim_f = np.sum([grid.num_zeta for grid in self._grid]) + elif isinstance(self._grid, LinearGrid): + treedef = tree_structure( + self.things[0], + is_leaf=lambda x: is_single_coil(x), + ) + leaves = tree_leaves(self.things[0], is_leaf=lambda x: is_single_coil(x)) + self._grid = [self._grid] * len(leaves) + self._grid = tree_unflatten(treedef, self._grid) + else: + flattened_grid = tree_flatten( + self._grid, is_leaf=lambda x: isinstance(x, LinearGrid) + )[0] + treedef = tree_structure( + self.things[0], + is_leaf=lambda x: is_single_coil(x), + ) + self._grid = tree_unflatten(treedef, flattened_grid) timer = Timer() if verbose > 0: @@ -138,9 +169,20 @@ def build(self, use_jit=True, verbose=1): lambda x, y: get_transforms(self._data_keys, obj=x, grid=y), self.things[0], self._grid, - is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, MixedCoilSet), + is_leaf=lambda x: is_single_coil(x), ) + get_dim_f(self._grid) + self._grid = to_list(self._grid) + transforms = to_list(transforms) + + if not isinstance(self._grid, (tuple, list)): + self._grid = [self._grid] + transforms = [transforms] + + if np.any([grid.num_rho > 1 or grid.num_theta > 1 for grid in self._grid]): + raise ValueError("Only use toroidal resolution for coil grids.") + # tree map always returns a list so take first transform and grid # for when we are only using a single coil if not is_mixed_coils: @@ -153,8 +195,12 @@ def build(self, use_jit=True, verbose=1): if verbose > 1: timer.disp("Precomputing transforms") + flattened_coils = tree_flatten( + self.things[0], + is_leaf=lambda x: is_single_coil(x), + )[0] if self._normalize: - self._scales = compute_scaling_factors(coils[0]) + self._scales = compute_scaling_factors(flattened_coils[0]) super().build(use_jit=use_jit, verbose=verbose) @@ -269,14 +315,24 @@ def build(self, use_jit=True, verbose=1): Level of output. """ - from desc.coils import CoilSet + from desc.coils import CoilSet, _Coil super().build(use_jit=use_jit, verbose=verbose) if self._normalize: self._normalization = self._scales["a"] - self._dim_f = len(self._coils.coils) if isinstance(self._coils, CoilSet) else 1 + # TODO: repeated code but maybe it's fine + flattened_coils = tree_flatten( + self._coils, + is_leaf=lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet), + )[0] + flattened_coils = ( + [flattened_coils[0]] + if not isinstance(self._coils, CoilSet) + else flattened_coils + ) + self._dim_f = len(flattened_coils) def compute(self, params, constants=None): """Compute coil length. diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index dfc9c48865..bfdc7d2730 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -584,15 +584,19 @@ def test(coil, grid=None): assert len(f) == obj.dim_f coil = FourierPlanarCoil(r_n=1) - coils = CoilSet.linspaced_linear(coil, n=4) - mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) - mixed_coils_grid = [LinearGrid(N=5)] * len(mixed_coils.coils) + coils = CoilSet.linspaced_linear(coil, n=2) + mixed_coils = MixedCoilSet.linspaced_linear(coil, n=2) nested_coils = MixedCoilSet(coils, coils) - test(coil) + nested_grids = [ + [LinearGrid(N=5), LinearGrid(N=5)], + [LinearGrid(N=5), LinearGrid(N=5)], + ] + + test(coil, grid=LinearGrid(N=5)) test(coils) - test(mixed_coils, grid=mixed_coils_grid) - test(nested_coils) + test(mixed_coils, grid=[LinearGrid(N=5)] * len(mixed_coils.coils)) + test(nested_coils, grid=nested_grids) @pytest.mark.unit def test_coil_curvature(self): @@ -606,15 +610,19 @@ def test(coil, grid=None): assert len(f) == obj.dim_f coil = FourierPlanarCoil() - coils = CoilSet.linspaced_linear(coil, n=4) - mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) - mixed_coils_grid = [LinearGrid(N=5)] * len(mixed_coils.coils) + coils = CoilSet.linspaced_linear(coil, n=2) + mixed_coils = MixedCoilSet.linspaced_linear(coil, n=2) nested_coils = MixedCoilSet(coils, coils) - test(coil) + nested_grids = [ + [LinearGrid(N=5), LinearGrid(N=5)], + [LinearGrid(N=5), LinearGrid(N=5)], + ] + + test(coil, grid=LinearGrid(N=5)) test(coils) - test(mixed_coils, grid=mixed_coils_grid) - test(nested_coils) + test(mixed_coils, grid=[LinearGrid(N=5)] * len(mixed_coils.coils)) + test(nested_coils, grid=nested_grids) @pytest.mark.unit def test_coil_torsion(self): @@ -628,15 +636,19 @@ def test(coil, grid=None): assert len(f) == obj.dim_f coil = FourierPlanarCoil() - coils = CoilSet.linspaced_linear(coil, n=4) - mixed_coils = MixedCoilSet.linspaced_linear(coil, n=4) - mixed_coils_grid = [LinearGrid(N=5)] * len(mixed_coils.coils) + coils = CoilSet.linspaced_linear(coil, n=2) + mixed_coils = MixedCoilSet.linspaced_linear(coil, n=2) nested_coils = MixedCoilSet(coils, coils) - test(coil) + nested_grids = [ + [LinearGrid(N=5), LinearGrid(N=5)], + [LinearGrid(N=5), LinearGrid(N=5)], + ] + + test(coil, grid=LinearGrid(N=5)) test(coils) - test(mixed_coils, grid=mixed_coils_grid) - test(nested_coils) + test(mixed_coils, grid=[LinearGrid(N=5)] * len(mixed_coils.coils)) + test(nested_coils, grid=nested_grids) @pytest.mark.unit From 7e7c1c918b563fa47f4ba16fbfc5df42bee9c790 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Tue, 12 Mar 2024 17:03:29 -0400 Subject: [PATCH 39/56] refactor --- desc/objectives/_coils.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 5bf445f636..80466b96c6 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -128,8 +128,11 @@ def to_list(coilset): else: return coilset - is_mixed_coils = isinstance(self.things[0], MixedCoilSet) is_single_coil = lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet) + coil_structure = tree_structure( + self.things[0], + is_leaf=lambda x: is_single_coil(x), + ) # check type if isinstance(self._grid, numbers.Integral): @@ -143,22 +146,14 @@ def to_list(coilset): is_leaf=lambda x: is_single_coil(x), ) elif isinstance(self._grid, LinearGrid): - treedef = tree_structure( - self.things[0], - is_leaf=lambda x: is_single_coil(x), - ) leaves = tree_leaves(self.things[0], is_leaf=lambda x: is_single_coil(x)) self._grid = [self._grid] * len(leaves) - self._grid = tree_unflatten(treedef, self._grid) + self._grid = tree_unflatten(coil_structure, self._grid) else: flattened_grid = tree_flatten( self._grid, is_leaf=lambda x: isinstance(x, LinearGrid) )[0] - treedef = tree_structure( - self.things[0], - is_leaf=lambda x: is_single_coil(x), - ) - self._grid = tree_unflatten(treedef, flattened_grid) + self._grid = tree_unflatten(coil_structure, flattened_grid) timer = Timer() if verbose > 0: @@ -185,7 +180,7 @@ def to_list(coilset): # tree map always returns a list so take first transform and grid # for when we are only using a single coil - if not is_mixed_coils: + if not isinstance(self.things[0], MixedCoilSet): transforms = transforms[0] self._grid = self._grid[0] From 77645c54cda47b150853546cea8a08302bca28d8 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Wed, 13 Mar 2024 11:27:43 -0400 Subject: [PATCH 40/56] add comments --- desc/objectives/_coils.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 80466b96c6..26c643183a 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -106,7 +106,7 @@ def build(self, use_jit=True, verbose=1): # noqa:C901 self._dim_f = 0 def get_dim_f(coilset): - """Turn a coilset into nested lists.""" + """Get dim_f.""" if isinstance(coilset, list): [get_dim_f(x) for x in coilset] elif isinstance(coilset, MixedCoilSet): @@ -117,7 +117,7 @@ def get_dim_f(coilset): self._dim_f += coilset.num_zeta def to_list(coilset): - """Turn a coilset into nested lists.""" + """Turn a MixedCoilSet container into a list of what it's containing.""" if isinstance(coilset, list): return [to_list(x) for x in coilset] if isinstance(coilset, MixedCoilSet): @@ -129,6 +129,8 @@ def to_list(coilset): return coilset is_single_coil = lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet) + # gives structure of coils, e.g. MixedCoilSet(coils, coils) would give a + # a structure of [[*, *], [*, *]] if n = 2 coils coil_structure = tree_structure( self.things[0], is_leaf=lambda x: is_single_coil(x), @@ -137,7 +139,10 @@ def to_list(coilset): # check type if isinstance(self._grid, numbers.Integral): self._grid = LinearGrid(N=self._grid, endpoint=False) + # all of these cases return a container MixedCoilSet that contains + # LinearGrids. i.e. MixedCoilSet.coils = list of LinearGrid if self._grid is None: + # map default grid to structure of inputted coils self._grid = tree_map( lambda x: LinearGrid( N=2 * x.N + 5, NFP=getattr(x, "NFP", 1), endpoint=False @@ -146,10 +151,13 @@ def to_list(coilset): is_leaf=lambda x: is_single_coil(x), ) elif isinstance(self._grid, LinearGrid): + # map inputted single LinearGrid to structure of inputted coils leaves = tree_leaves(self.things[0], is_leaf=lambda x: is_single_coil(x)) self._grid = [self._grid] * len(leaves) self._grid = tree_unflatten(coil_structure, self._grid) else: + # this case covers an inputted list of grids that matches the size + # of the inputted coils. Can be a 1D list or nested list. flattened_grid = tree_flatten( self._grid, is_leaf=lambda x: isinstance(x, LinearGrid) )[0] @@ -168,6 +176,7 @@ def to_list(coilset): ) get_dim_f(self._grid) + # get only needed grids (1 per CoilSet) self._grid = to_list(self._grid) transforms = to_list(transforms) From d071f8c4615ea268082affd0b10b3408bc11600a Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Wed, 13 Mar 2024 11:28:49 -0400 Subject: [PATCH 41/56] change if to elif statements for the case of MixedCoilSet(coils), it would go through the 2nd and 3rd if statements --- desc/objectives/_coils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 26c643183a..0fc7e856b0 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -120,9 +120,9 @@ def to_list(coilset): """Turn a MixedCoilSet container into a list of what it's containing.""" if isinstance(coilset, list): return [to_list(x) for x in coilset] - if isinstance(coilset, MixedCoilSet): + elif isinstance(coilset, MixedCoilSet): return [to_list(x) for x in coilset] - if isinstance(coilset, CoilSet): + elif isinstance(coilset, CoilSet): # use the same grid/transform for CoilSet return to_list(coilset.coils[0]) else: From 752fec99ca922a12cc0d961585b0127a46883275 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Wed, 13 Mar 2024 12:34:02 -0400 Subject: [PATCH 42/56] refactor - moves leaves out of if statement - flatten grid/transforms --- desc/objectives/_coils.py | 40 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 0fc7e856b0..247b97ea8f 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -12,7 +12,7 @@ ) from desc.compute import get_transforms from desc.grid import LinearGrid -from desc.utils import Timer +from desc.utils import Timer, errorif from .normalization import compute_scaling_factors from .objective_funs import _Objective @@ -126,7 +126,7 @@ def to_list(coilset): # use the same grid/transform for CoilSet return to_list(coilset.coils[0]) else: - return coilset + return [coilset] is_single_coil = lambda x: isinstance(x, _Coil) and not isinstance(x, CoilSet) # gives structure of coils, e.g. MixedCoilSet(coils, coils) would give a @@ -135,6 +135,7 @@ def to_list(coilset): self.things[0], is_leaf=lambda x: is_single_coil(x), ) + coil_leaves = tree_leaves(self.things[0], is_leaf=lambda x: is_single_coil(x)) # check type if isinstance(self._grid, numbers.Integral): @@ -152,8 +153,7 @@ def to_list(coilset): ) elif isinstance(self._grid, LinearGrid): # map inputted single LinearGrid to structure of inputted coils - leaves = tree_leaves(self.things[0], is_leaf=lambda x: is_single_coil(x)) - self._grid = [self._grid] * len(leaves) + self._grid = [self._grid] * len(coil_leaves) self._grid = tree_unflatten(coil_structure, self._grid) else: # this case covers an inputted list of grids that matches the size @@ -176,22 +176,24 @@ def to_list(coilset): ) get_dim_f(self._grid) - # get only needed grids (1 per CoilSet) - self._grid = to_list(self._grid) - transforms = to_list(transforms) - - if not isinstance(self._grid, (tuple, list)): - self._grid = [self._grid] - transforms = [transforms] + # get only needed grids (1 per CoilSet) and flatten that list + self._grid = tree_leaves( + to_list(self._grid), is_leaf=lambda x: isinstance(x, LinearGrid) + ) + transforms = tree_leaves( + to_list(transforms), is_leaf=lambda x: isinstance(x, dict) + ) - if np.any([grid.num_rho > 1 or grid.num_theta > 1 for grid in self._grid]): - raise ValueError("Only use toroidal resolution for coil grids.") + errorif( + np.any([grid.num_rho > 1 or grid.num_theta > 1 for grid in self._grid]), + ValueError, + "Only use toroidal resolution for coil grids.", + ) - # tree map always returns a list so take first transform and grid - # for when we are only using a single coil + # CoilSet and _Coil have one grid/transform if not isinstance(self.things[0], MixedCoilSet): - transforms = transforms[0] self._grid = self._grid[0] + transforms = transforms[0] self._constants = {"transforms": transforms} @@ -199,12 +201,8 @@ def to_list(coilset): if verbose > 1: timer.disp("Precomputing transforms") - flattened_coils = tree_flatten( - self.things[0], - is_leaf=lambda x: is_single_coil(x), - )[0] if self._normalize: - self._scales = compute_scaling_factors(flattened_coils[0]) + self._scales = compute_scaling_factors(coil_leaves[0]) super().build(use_jit=use_jit, verbose=verbose) From e6712f0dddadd1740af3afd88aa445e3821f10b4 Mon Sep 17 00:00:00 2001 From: Dario Panici Date: Thu, 14 Mar 2024 21:41:00 -0400 Subject: [PATCH 43/56] add missing names --- desc/objectives/_coils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 247b97ea8f..12a40ee83f 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -286,7 +286,7 @@ def __init__( loss_function=None, deriv_mode="auto", grid=None, - name=None, + name="coil length", ): self._coils = coils if target is None and bounds is None: @@ -411,7 +411,7 @@ def __init__( loss_function=None, deriv_mode="auto", grid=None, - name=None, + name="coil curvature", ): if target is None and bounds is None: bounds = (0, 1) @@ -522,7 +522,7 @@ def __init__( loss_function=None, deriv_mode="auto", grid=None, - name=None, + name="coil torsion", ): if target is None and bounds is None: target = 0 From 118b1cc72ed8d6c56f9b430376d74d9d9564b2b3 Mon Sep 17 00:00:00 2001 From: Dario Panici Date: Thu, 14 Mar 2024 21:51:23 -0400 Subject: [PATCH 44/56] fix current setting of coils, which could fail if a size 1 dim 1 ndarray was passed in --- desc/coils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desc/coils.py b/desc/coils.py index b81a7ac24f..6430a8b458 100644 --- a/desc/coils.py +++ b/desc/coils.py @@ -133,7 +133,7 @@ class _Coil(_MagneticField, Optimizable, ABC): _io_attrs_ = _MagneticField._io_attrs_ + ["_current"] def __init__(self, current, *args, **kwargs): - self._current = float(current) + self._current = float(np.squeeze(current)) super().__init__(*args, **kwargs) @optimizable_parameter @@ -145,7 +145,7 @@ def current(self): @current.setter def current(self, new): assert jnp.isscalar(new) or new.size == 1 - self._current = float(new) + self._current = float(np.squeeze(new)) def compute_magnetic_field( self, coords, params=None, basis="rpz", source_grid=None From cf0ddefb24adf4315043ccb50c3fd0dcd67ea71e Mon Sep 17 00:00:00 2001 From: Dario Panici Date: Thu, 14 Mar 2024 21:55:14 -0400 Subject: [PATCH 45/56] add missing print value fmt, units and whether coil objs are scalar --- desc/objectives/_coils.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 12a40ee83f..a1c864e65b 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -275,6 +275,10 @@ class CoilLength(_CoilObjective): Name of the objective function. """ + _scalar = False # Not always a scalar, if a coilset is passed in + _units = "(m)" + _print_value_fmt = "Coil length: {:10.3e} " + def __init__( self, coils, @@ -400,6 +404,10 @@ class CoilCurvature(_CoilObjective): Name of the objective function. """ + _scalar = False # Not always a scalar, if a coilset is passed in + _units = "(m^-1)" + _print_value_fmt = "Coil curvature: {:10.3e} " + def __init__( self, coil, @@ -511,6 +519,10 @@ class CoilTorsion(_CoilObjective): Name of the objective function. """ + _scalar = False # Not always a scalar, if a coilset is passed in + _units = "(m^-1)" + _print_value_fmt = "Coil torsion: {:10.3e} " + def __init__( self, coil, From 0de46f1dd200c112cb35f1ea4ef475bcc567c82f Mon Sep 17 00:00:00 2001 From: Dario Panici Date: Thu, 14 Mar 2024 22:22:47 -0400 Subject: [PATCH 46/56] add test for objectives, though torsion test is not working --- tests/test_examples.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test_examples.py b/tests/test_examples.py index 57ad8e1e34..f888ff1056 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -10,6 +10,7 @@ from qsc import Qsc import desc.examples +from desc.coils import FourierRZCoil from desc.continuation import _solve_axisym, solve_continuation_automatic from desc.equilibrium import EquilibriaFamily, Equilibrium from desc.geometry import FourierRZToroidalSurface @@ -19,6 +20,9 @@ from desc.objectives import ( AspectRatio, BoundaryError, + CoilCurvature, + CoilLength, + CoilTorsion, CurrentDensity, FixBoundaryR, FixBoundaryZ, @@ -1050,3 +1054,37 @@ def test_example_get_current(self): -1.36284423e07, ], ) + + +@pytest.mark.unit +def test_single_coil_optimization(): + """Test that single coil (not coilset) optimization works.""" + # testing that the objectives work and that the optimization framework + # works when a single coil is passed in. + + opt = Optimizer("fmintr") + coil = FourierRZCoil() + # length + target = 60 + obj = ObjectiveFunction(CoilLength(coil, target=target)) + opt.optimize([coil], obj) + np.testing.assert_allclose(coil.compute("length")["length"], target) + + # curvature + grid = LinearGrid(N=10) + target = 2 * coil.compute("curvature", grid=grid)["curvature"] + obj = ObjectiveFunction(CoilCurvature(coil, target=target, grid=grid)) + opt.optimize([coil], obj, maxiter=300) + np.testing.assert_allclose( + coil.compute("curvature", grid=grid)["curvature"], target, rtol=1e-3 + ) + + # torsion + # this test is not working very well... unsure of why + coil.change_resolution(N=6) + target = 0.05 + obj = ObjectiveFunction(CoilTorsion(coil, target=target, loss_function="mean")) + opt.optimize([coil], obj, maxiter=300) + np.testing.assert_allclose( + np.mean(coil.compute("torsion")["torsion"]), target, rtol=1e-3 + ) From e6814b38aa409d4b53a2318bcb30c667f5223014 Mon Sep 17 00:00:00 2001 From: Dario Panici Date: Thu, 14 Mar 2024 22:32:21 -0400 Subject: [PATCH 47/56] fix torsion test --- tests/test_examples.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_examples.py b/tests/test_examples.py index f888ff1056..829f214257 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1080,11 +1080,12 @@ def test_single_coil_optimization(): ) # torsion - # this test is not working very well... unsure of why coil.change_resolution(N=6) - target = 0.05 - obj = ObjectiveFunction(CoilTorsion(coil, target=target, loss_function="mean")) + coil.Z_n = coil.Z_n.at[0:6].set(1) + print(coil.compute("torsion", grid=grid)["torsion"]) + target = 3 + obj = ObjectiveFunction(CoilTorsion(coil, target=target, loss_function="max")) opt.optimize([coil], obj, maxiter=300) np.testing.assert_allclose( - np.mean(coil.compute("torsion")["torsion"]), target, rtol=1e-3 + np.max(coil.compute("torsion", grid=grid)["torsion"]), target, rtol=1e-3 ) From 51d2f025905090a7d57e4582920af1280870929a Mon Sep 17 00:00:00 2001 From: Dario Panici Date: Thu, 14 Mar 2024 22:36:27 -0400 Subject: [PATCH 48/56] remove unneeded print --- tests/test_examples.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_examples.py b/tests/test_examples.py index 829f214257..3d401b60f9 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1081,8 +1081,8 @@ def test_single_coil_optimization(): # torsion coil.change_resolution(N=6) + # initialize with some torsion coil.Z_n = coil.Z_n.at[0:6].set(1) - print(coil.compute("torsion", grid=grid)["torsion"]) target = 3 obj = ObjectiveFunction(CoilTorsion(coil, target=target, loss_function="max")) opt.optimize([coil], obj, maxiter=300) From 3c59c7369be71306ad90fb3d75c24f6d80a816bc Mon Sep 17 00:00:00 2001 From: Dario Panici Date: Thu, 14 Mar 2024 22:58:38 -0400 Subject: [PATCH 49/56] make test a little faster and more compact --- tests/test_examples.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/tests/test_examples.py b/tests/test_examples.py index 3d401b60f9..0cc166adab 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1064,28 +1064,32 @@ def test_single_coil_optimization(): opt = Optimizer("fmintr") coil = FourierRZCoil() - # length - target = 60 - obj = ObjectiveFunction(CoilLength(coil, target=target)) - opt.optimize([coil], obj) - np.testing.assert_allclose(coil.compute("length")["length"], target) - - # curvature - grid = LinearGrid(N=10) - target = 2 * coil.compute("curvature", grid=grid)["curvature"] - obj = ObjectiveFunction(CoilCurvature(coil, target=target, grid=grid)) - opt.optimize([coil], obj, maxiter=300) + coil.change_resolution(N=1) + target_R = 9 + # length and curvature + target_length = 2 * np.pi * target_R + target_curvature = 1 / target_R + grid = LinearGrid(N=2) + obj = ObjectiveFunction( + ( + CoilLength(coil, target=target_length), + CoilCurvature(coil, target=target_curvature, grid=grid), + ), + ) + opt.optimize([coil], obj, maxiter=200) + np.testing.assert_allclose( + coil.compute("length")["length"], target_length, rtol=1e-4 + ) np.testing.assert_allclose( - coil.compute("curvature", grid=grid)["curvature"], target, rtol=1e-3 + coil.compute("curvature", grid=grid)["curvature"], target_curvature, rtol=1e-4 ) # torsion - coil.change_resolution(N=6) # initialize with some torsion - coil.Z_n = coil.Z_n.at[0:6].set(1) - target = 3 - obj = ObjectiveFunction(CoilTorsion(coil, target=target, loss_function="max")) - opt.optimize([coil], obj, maxiter=300) + coil.Z_n = coil.Z_n.at[0].set(0.1) + target = 0 + obj = ObjectiveFunction(CoilTorsion(coil, target=target)) + opt.optimize([coil], obj, maxiter=200, ftol=0) np.testing.assert_allclose( - np.max(coil.compute("torsion", grid=grid)["torsion"]), target, rtol=1e-3 + coil.compute("torsion", grid=grid)["torsion"], target, atol=1e-5 ) From 14095f6b6c4de25256096a5e3f135d9af7fd49e3 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 18 Mar 2024 18:14:28 -0400 Subject: [PATCH 50/56] adress reviews - docstring - LinearGrid to _Grid --- desc/objectives/_coils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 247b97ea8f..4a4519b85f 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -11,7 +11,7 @@ tree_unflatten, ) from desc.compute import get_transforms -from desc.grid import LinearGrid +from desc.grid import LinearGrid, _Grid from desc.utils import Timer, errorif from .normalization import compute_scaling_factors @@ -46,7 +46,7 @@ class _CoilObjective(_Objective): loss_function : {None, 'mean', 'min', 'max'}, optional Loss function to apply to the objective values once computed. This loss function is called on the raw compute value, before any shifting, scaling, or - normalization. Note: Has no effect for this objective. + normalization. Affects all coils and not just a single coil. deriv_mode : {"auto", "fwd", "rev"} Specify how to compute jacobian matrix, either forward mode or reverse mode AD. "auto" selects forward or reverse mode based on the size of the input and output @@ -113,7 +113,7 @@ def get_dim_f(coilset): [get_dim_f(x) for x in coilset] elif isinstance(coilset, CoilSet): get_dim_f(coilset.coils) - elif isinstance(coilset, LinearGrid): + elif isinstance(coilset, _Grid): self._dim_f += coilset.num_zeta def to_list(coilset): @@ -151,7 +151,7 @@ def to_list(coilset): self.things[0], is_leaf=lambda x: is_single_coil(x), ) - elif isinstance(self._grid, LinearGrid): + elif isinstance(self._grid, _Grid): # map inputted single LinearGrid to structure of inputted coils self._grid = [self._grid] * len(coil_leaves) self._grid = tree_unflatten(coil_structure, self._grid) @@ -159,7 +159,7 @@ def to_list(coilset): # this case covers an inputted list of grids that matches the size # of the inputted coils. Can be a 1D list or nested list. flattened_grid = tree_flatten( - self._grid, is_leaf=lambda x: isinstance(x, LinearGrid) + self._grid, is_leaf=lambda x: isinstance(x, _Grid) )[0] self._grid = tree_unflatten(coil_structure, flattened_grid) @@ -178,7 +178,7 @@ def to_list(coilset): get_dim_f(self._grid) # get only needed grids (1 per CoilSet) and flatten that list self._grid = tree_leaves( - to_list(self._grid), is_leaf=lambda x: isinstance(x, LinearGrid) + to_list(self._grid), is_leaf=lambda x: isinstance(x, _Grid) ) transforms = tree_leaves( to_list(transforms), is_leaf=lambda x: isinstance(x, dict) From 53a2a20c5e992b6d31180bf1f18dcc0f24ba0da3 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Mon, 18 Mar 2024 23:14:30 -0400 Subject: [PATCH 51/56] add quad_weights to constants --- desc/objectives/_coils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index cf080b1e8c..cfdfa5501c 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -195,7 +195,7 @@ def to_list(coilset): self._grid = self._grid[0] transforms = transforms[0] - self._constants = {"transforms": transforms} + self._constants = {"transforms": transforms, "quad_weights": None} timer.stop("Precomputing transforms") if verbose > 1: From 24c393e3b1cc13878e7a543bb91b1abce156118c Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Tue, 19 Mar 2024 13:03:00 -0400 Subject: [PATCH 52/56] add quad_weights with spacing --- desc/objectives/_coils.py | 12 +++++++++++- tests/test_objective_funs.py | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index cfdfa5501c..c712dcfb60 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -104,6 +104,7 @@ def build(self, use_jit=True, verbose=1): # noqa:C901 from desc.coils import CoilSet, MixedCoilSet, _Coil self._dim_f = 0 + quad_weights = [] def get_dim_f(coilset): """Get dim_f.""" @@ -115,6 +116,7 @@ def get_dim_f(coilset): get_dim_f(coilset.coils) elif isinstance(coilset, _Grid): self._dim_f += coilset.num_zeta + quad_weights.append(coilset.spacing[:, 2]) def to_list(coilset): """Turn a MixedCoilSet container into a list of what it's containing.""" @@ -183,6 +185,9 @@ def to_list(coilset): transforms = tree_leaves( to_list(transforms), is_leaf=lambda x: isinstance(x, dict) ) + quad_weights = tree_leaves( + to_list(quad_weights), is_leaf=lambda x: isinstance(x, jnp.ndarray) + ) errorif( np.any([grid.num_rho > 1 or grid.num_theta > 1 for grid in self._grid]), @@ -194,8 +199,12 @@ def to_list(coilset): if not isinstance(self.things[0], MixedCoilSet): self._grid = self._grid[0] transforms = transforms[0] + quad_weights = quad_weights[0] - self._constants = {"transforms": transforms, "quad_weights": None} + self._constants = { + "transforms": transforms, + "quad_weights": jnp.array(quad_weights), + } timer.stop("Precomputing transforms") if verbose > 1: @@ -339,6 +348,7 @@ def build(self, use_jit=True, verbose=1): else flattened_coils ) self._dim_f = len(flattened_coils) + self._constants["quad_weights"] = 1 def compute(self, params, constants=None): """Compute coil length. diff --git a/tests/test_objective_funs.py b/tests/test_objective_funs.py index 7fc6a4fcaf..86a25027a1 100644 --- a/tests/test_objective_funs.py +++ b/tests/test_objective_funs.py @@ -597,7 +597,6 @@ def test(coil, grid=None): [LinearGrid(N=5), LinearGrid(N=5)], [LinearGrid(N=5), LinearGrid(N=5)], ] - test(coil, grid=LinearGrid(N=5)) test(coils) test(mixed_coils, grid=[LinearGrid(N=5)] * len(mixed_coils.coils)) From 2a4dd3b32c3bdd4f415e9fb70cac8158b419070f Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Tue, 19 Mar 2024 13:51:59 -0400 Subject: [PATCH 53/56] concatenate quad_weights --- desc/objectives/_coils.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index c712dcfb60..53f1587828 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -104,19 +104,21 @@ def build(self, use_jit=True, verbose=1): # noqa:C901 from desc.coils import CoilSet, MixedCoilSet, _Coil self._dim_f = 0 - quad_weights = [] + self._quad_weights = jnp.array([]) - def get_dim_f(coilset): - """Get dim_f.""" + def get_dim_f_and_weights(coilset): + """Get dim_f and quad_weights from grid.""" if isinstance(coilset, list): - [get_dim_f(x) for x in coilset] + [get_dim_f_and_weights(x) for x in coilset] elif isinstance(coilset, MixedCoilSet): - [get_dim_f(x) for x in coilset] + [get_dim_f_and_weights(x) for x in coilset] elif isinstance(coilset, CoilSet): - get_dim_f(coilset.coils) + get_dim_f_and_weights(coilset.coils) elif isinstance(coilset, _Grid): self._dim_f += coilset.num_zeta - quad_weights.append(coilset.spacing[:, 2]) + self._quad_weights = jnp.concatenate( + (self._quad_weights, coilset.spacing[:, 2]) + ) def to_list(coilset): """Turn a MixedCoilSet container into a list of what it's containing.""" @@ -177,7 +179,7 @@ def to_list(coilset): is_leaf=lambda x: is_single_coil(x), ) - get_dim_f(self._grid) + get_dim_f_and_weights(self._grid) # get only needed grids (1 per CoilSet) and flatten that list self._grid = tree_leaves( to_list(self._grid), is_leaf=lambda x: isinstance(x, _Grid) @@ -185,9 +187,6 @@ def to_list(coilset): transforms = tree_leaves( to_list(transforms), is_leaf=lambda x: isinstance(x, dict) ) - quad_weights = tree_leaves( - to_list(quad_weights), is_leaf=lambda x: isinstance(x, jnp.ndarray) - ) errorif( np.any([grid.num_rho > 1 or grid.num_theta > 1 for grid in self._grid]), @@ -199,11 +198,10 @@ def to_list(coilset): if not isinstance(self.things[0], MixedCoilSet): self._grid = self._grid[0] transforms = transforms[0] - quad_weights = quad_weights[0] self._constants = { "transforms": transforms, - "quad_weights": jnp.array(quad_weights), + "quad_weights": self._quad_weights, } timer.stop("Precomputing transforms") From c630ca9fb9b8ce370f55de4b58c9fa28df34a1ec Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Wed, 20 Mar 2024 14:14:42 -0400 Subject: [PATCH 54/56] update docstrings --- desc/objectives/_coils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 53f1587828..5894c04e7d 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -270,7 +270,7 @@ class CoilLength(_CoilObjective): loss_function : {None, 'mean', 'min', 'max'}, optional Loss function to apply to the objective values once computed. This loss function is called on the raw compute value, before any shifting, scaling, or - normalization. Note: Has no effect for this objective. + normalization. Affects all coils and not just a single coil. deriv_mode : {"auto", "fwd", "rev"} Specify how to compute jacobian matrix, either forward mode or reverse mode AD. "auto" selects forward or reverse mode based on the size of the input and output @@ -400,7 +400,7 @@ class CoilCurvature(_CoilObjective): loss_function : {None, 'mean', 'min', 'max'}, optional Loss function to apply to the objective values once computed. This loss function is called on the raw compute value, before any shifting, scaling, or - normalization. Note: Has no effect for this objective. + normalization. Affects all coils and not just a single coil. deriv_mode : {"auto", "fwd", "rev"} Specify how to compute jacobian matrix, either forward mode or reverse mode AD. "auto" selects forward or reverse mode based on the size of the input and output @@ -515,7 +515,7 @@ class CoilTorsion(_CoilObjective): loss_function : {None, 'mean', 'min', 'max'}, optional Loss function to apply to the objective values once computed. This loss function is called on the raw compute value, before any shifting, scaling, or - normalization. Note: Has no effect for this objective. + normalization. Affects all coils and not just a single coil. deriv_mode : {"auto", "fwd", "rev"} Specify how to compute jacobian matrix, either forward mode or reverse mode AD. "auto" selects forward or reverse mode based on the size of the input and output From 321851b4e9a7e56f49e8459940d2828291deef1c Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Wed, 20 Mar 2024 20:25:13 -0400 Subject: [PATCH 55/56] fix docstring --- desc/objectives/_coils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index 5894c04e7d..de785bf2c9 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -46,7 +46,7 @@ class _CoilObjective(_Objective): loss_function : {None, 'mean', 'min', 'max'}, optional Loss function to apply to the objective values once computed. This loss function is called on the raw compute value, before any shifting, scaling, or - normalization. Affects all coils and not just a single coil. + normalization. Operates over all coils, not each individial coil. deriv_mode : {"auto", "fwd", "rev"} Specify how to compute jacobian matrix, either forward mode or reverse mode AD. "auto" selects forward or reverse mode based on the size of the input and output @@ -270,7 +270,7 @@ class CoilLength(_CoilObjective): loss_function : {None, 'mean', 'min', 'max'}, optional Loss function to apply to the objective values once computed. This loss function is called on the raw compute value, before any shifting, scaling, or - normalization. Affects all coils and not just a single coil. + normalization. Operates over all coils, not each individial coil. deriv_mode : {"auto", "fwd", "rev"} Specify how to compute jacobian matrix, either forward mode or reverse mode AD. "auto" selects forward or reverse mode based on the size of the input and output @@ -400,7 +400,7 @@ class CoilCurvature(_CoilObjective): loss_function : {None, 'mean', 'min', 'max'}, optional Loss function to apply to the objective values once computed. This loss function is called on the raw compute value, before any shifting, scaling, or - normalization. Affects all coils and not just a single coil. + normalization. Operates over all coils, not each individial coil. deriv_mode : {"auto", "fwd", "rev"} Specify how to compute jacobian matrix, either forward mode or reverse mode AD. "auto" selects forward or reverse mode based on the size of the input and output @@ -515,7 +515,7 @@ class CoilTorsion(_CoilObjective): loss_function : {None, 'mean', 'min', 'max'}, optional Loss function to apply to the objective values once computed. This loss function is called on the raw compute value, before any shifting, scaling, or - normalization. Affects all coils and not just a single coil. + normalization. Operates over all coils, not each individial coil. deriv_mode : {"auto", "fwd", "rev"} Specify how to compute jacobian matrix, either forward mode or reverse mode AD. "auto" selects forward or reverse mode based on the size of the input and output From 01a6bc2fc07ff607d013a8f6e87b62939da1c1e0 Mon Sep 17 00:00:00 2001 From: Kian Orr Date: Wed, 27 Mar 2024 14:17:40 -0400 Subject: [PATCH 56/56] delete scalar comments --- desc/objectives/_coils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desc/objectives/_coils.py b/desc/objectives/_coils.py index de785bf2c9..38cb32ad29 100644 --- a/desc/objectives/_coils.py +++ b/desc/objectives/_coils.py @@ -412,7 +412,7 @@ class CoilCurvature(_CoilObjective): Name of the objective function. """ - _scalar = False # Not always a scalar, if a coilset is passed in + _scalar = False _units = "(m^-1)" _print_value_fmt = "Coil curvature: {:10.3e} " @@ -527,7 +527,7 @@ class CoilTorsion(_CoilObjective): Name of the objective function. """ - _scalar = False # Not always a scalar, if a coilset is passed in + _scalar = False _units = "(m^-1)" _print_value_fmt = "Coil torsion: {:10.3e} "