Skip to content

Commit

Permalink
Merge branch 'dd/external' of https://github.com/PlasmaControl/DESC i…
Browse files Browse the repository at this point in the history
…nto dd/external
  • Loading branch information
daniel-dudt committed Jul 26, 2024
2 parents aa570d4 + f6a395b commit d62d9ca
Show file tree
Hide file tree
Showing 13 changed files with 1,287 additions and 321 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

New Features

- adds ``from_values`` method that was present in ``FourierRZCurve`` but missing in ``FourierRZCoil``
- also adds new ``from_values`` method for ``FourierPlanarCurve`` and ``FourierPlanarCoil``


v0.12.0
-------
Expand Down
178 changes: 165 additions & 13 deletions desc/coils.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,83 @@ def to_SplineXYZ(self, knots=None, grid=None, method="cubic", name=""):
self.current, coords, knots=knots, method=method, name=name, basis="xyz"
)

def to_FourierRZ(self, N=10, grid=None, NFP=None, sym=False, name=""):
"""Convert Coil to FourierRZCoil representation.
Note that some types of coils may not be representable in this basis.
Parameters
----------
N : int
Fourier resolution of the new R,Z representation.
grid : Grid, int or None
Grid used to evaluate curve coordinates on to fit with FourierRZCoil.
If an integer, uses that many equally spaced points.
NFP : int
Number of field periods, the coil will have a discrete toroidal symmetry
according to NFP.
sym : bool, optional
whether the curve is stellarator-symmetric or not. Default
is False.
name : str
name for this coil
Returns
-------
curve : FourierRZCoil
New representation of the coil parameterized by Fourier series for R,Z.
"""
NFP = 1 or NFP
if grid is None:
grid = LinearGrid(N=2 * N + 1)
coords = self.compute("x", grid=grid, basis="xyz")["x"]
return FourierRZCoil.from_values(
self.current, coords, N=N, NFP=NFP, basis="xyz", sym=sym, name=name
)

def to_FourierPlanar(self, N=10, grid=None, basis="xyz", name=""):
"""Convert Coil to FourierPlanarCoil representation.
Note that some types of coils may not be representable in this basis.
In this case, a least-squares fit will be done to find the
planar coil that best represents the coil.
Parameters
----------
N : int
Fourier resolution of the new FourierPlanarCoil representation.
grid : Grid, int or None
Grid used to evaluate curve coordinates on to fit with FourierPlanarCoil.
If an integer, uses that many equally spaced points.
basis : {'xyz', 'rpz'}
Coordinate system for center and normal vectors. Default = 'xyz'.
name : str
name for this coil
Returns
-------
coil : FourierPlanarCoil
New representation of the coil parameterized by Fourier series for
minor radius r in a plane specified by a center position and normal
vector.
"""
if grid is None:
grid = LinearGrid(N=2 * N + 1)
coords = self.compute("x", grid=grid, basis=basis)["x"]
return FourierPlanarCoil.from_values(
self.current, coords, N=N, basis=basis, name=name
)


class FourierRZCoil(_Coil, FourierRZCurve):
"""Coil parameterized by fourier series for R,Z in terms of toroidal angle phi.
Parameters
----------
current : float
current through coil, in Amperes
Current through the coil, in Amperes.
R_n, Z_n: array-like
fourier coefficients for R, Z
modes_R : array-like
Expand Down Expand Up @@ -414,14 +483,58 @@ def __init__(
):
super().__init__(current, R_n, Z_n, modes_R, modes_Z, NFP, sym, name)

@classmethod
def from_values(cls, current, coords, N=10, NFP=1, basis="rpz", sym=False, name=""):
"""Fit coordinates to FourierRZCoil representation.
Parameters
----------
current : float
Current through the coil, in Amperes.
coords: ndarray, shape (num_coords,3)
coordinates to fit a FourierRZCurve object with each column
corresponding to xyz or rpz depending on the basis argument.
N : int
Fourier resolution of the new R,Z representation.
NFP : int
Number of field periods, the curve will have a discrete toroidal symmetry
according to NFP.
basis : {"rpz", "xyz"}
basis for input coordinates. Defaults to "rpz"
sym : bool
Whether to enforce stellarator symmetry.
name : str
name for this coil
Returns
-------
coil : FourierRZCoil
New representation of the coil parameterized by Fourier series for R,Z.
"""
curve = super().from_values(
coords=coords, N=N, NFP=NFP, basis=basis, sym=sym, name=name
)
return FourierRZCoil(
current=current,
R_n=curve.R_n,
Z_n=curve.Z_n,
modes_R=curve.R_basis.modes[:, 2],
modes_Z=curve.Z_basis.modes[:, 2],
NFP=NFP,
sym=curve.sym,
name=name,
)


class FourierXYZCoil(_Coil, FourierXYZCurve):
"""Coil parameterized by fourier series for X,Y,Z in terms of arbitrary angle s.
Parameters
----------
current : float
current through coil, in Amperes
Current through the coil, in Amperes.
X_n, Y_n, Z_n: array-like
fourier coefficients for X, Y, Z
modes : array-like
Expand Down Expand Up @@ -484,7 +597,7 @@ def from_values(cls, current, coords, N=10, s=None, basis="xyz", name=""):
Parameters
----------
current : float
Current through the coil, in Amps.
Current through the coil, in Amperes.
coords: ndarray
Coordinates to fit a FourierXYZCoil object with.
N : int
Expand All @@ -503,12 +616,13 @@ def from_values(cls, current, coords, N=10, s=None, basis="xyz", name=""):
New representation of the coil parameterized by Fourier series for X,Y,Z.
"""
curve = super().from_values(coords, N, s, basis)
return cls(
current,
curve = super().from_values(coords=coords, N=N, s=s, basis=basis, name=name)
return FourierXYZCoil(
current=current,
X_n=curve.X_n,
Y_n=curve.Y_n,
Z_n=curve.Z_n,
modes=curve.X_basis.modes[:, 2],
name=name,
)

Expand Down Expand Up @@ -586,14 +700,49 @@ def __init__(
):
super().__init__(current, center, normal, r_n, modes, basis, name)

@classmethod
def from_values(cls, current, coords, N=10, basis="xyz", name=""):
"""Fit coordinates to FourierPlanarCoil representation.
Parameters
----------
current : float
Current through the coil, in Amperes.
coords: ndarray, shape (num_coords,3)
Coordinates to fit a FourierPlanarCurve object with each column
corresponding to xyz or rpz depending on the basis argument.
N : int
Fourier resolution of the new r representation.
basis : {"rpz", "xyz"}
Basis for input coordinates. Defaults to "xyz".
name : str
Name for this curve.
Returns
-------
curve : FourierPlanarCoil
New representation of the coil parameterized by a Fourier series for r.
"""
curve = super().from_values(coords=coords, N=N, basis=basis, name=name)
return FourierPlanarCoil(
current=current,
center=curve.center,
normal=curve.normal,
r_n=curve.r_n,
modes=curve.r_basis.modes[:, 2],
basis="xyz",
name=name,
)


class SplineXYZCoil(_Coil, SplineXYZCurve):
"""Coil parameterized by spline points in X,Y,Z.
Parameters
----------
current : float
current through coil, in Amperes
Current through the coil, in Amperes.
X, Y, Z: array-like
Points for X, Y, Z describing the curve. If the endpoint is included
(ie, X[0] == X[-1]), then the final point will be dropped.
Expand Down Expand Up @@ -705,7 +854,7 @@ def from_values(
Parameters
----------
current : float
Current through the coil, in Amps.
Current through the coil, in Amperes.
coords: ndarray
Points for X, Y, Z describing the curve. If the endpoint is included
(ie, X[0] == X[-1]), then the final point will be dropped.
Expand Down Expand Up @@ -737,9 +886,11 @@ def from_values(
New representation of the coil parameterized by splines in X,Y,Z.
"""
curve = super().from_values(coords, knots, method, basis=basis)
return cls(
current,
curve = super().from_values(
coords=coords, knots=knots, method=method, basis=basis, name=name
)
return SplineXYZCoil(
current=current,
X=curve.X,
Y=curve.Y,
Z=curve.Z,
Expand Down Expand Up @@ -770,7 +921,7 @@ def _check_type(coil0, coil):
FourierRZCoil: ["R_basis", "Z_basis", "NFP", "sym"],
FourierXYZCoil: ["X_basis", "Y_basis", "Z_basis"],
FourierPlanarCoil: ["r_basis"],
SplineXYZCoil: ["method", "N"],
SplineXYZCoil: ["method", "N", "knots"],
}

for attr in attrs[coil0.__class__]:
Expand All @@ -781,7 +932,8 @@ def _check_type(coil0, coil):
ValueError,
(
"coils in a CoilSet must have the same parameterization, got a "
+ f"mismatch between attr {attr}, with values {a0} and {a1}"
+ f"mismatch between attr {attr}, with values {a0} and {a1}."
+ " Consider using a MixedCoilSet"
),
)

Expand Down
2 changes: 1 addition & 1 deletion desc/equilibrium/equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -1954,7 +1954,7 @@ def from_near_axis(
idx = np.nonzero(grid.nodes[:, 0] == rho_i)[0]
R_1D[idx] = R_2D.flatten(order="F")
Z_1D[idx] = Z_2D.flatten(order="F")
L_1D[idx] = nu_B.flatten(order="F") * na_eq.iota
L_1D[idx] = -nu_B.flatten(order="F") * na_eq.iota

inputs["R_lmn"] = np.linalg.lstsq(A_Rw, R_1D * W, rcond=None)[0]
inputs["Z_lmn"] = np.linalg.lstsq(A_Zw, Z_1D * W, rcond=None)[0]
Expand Down
46 changes: 42 additions & 4 deletions desc/geometry/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def __repr__(self):
+ " (name={})".format(self.name)
)

def to_FourierXYZ(self, N=None, grid=None, s=None, name=""):
def to_FourierXYZ(self, N=10, grid=None, s=None, name=""):
"""Convert Curve to FourierXYZCurve representation.
Parameters
Expand Down Expand Up @@ -284,7 +284,7 @@ def to_SplineXYZ(self, knots=None, grid=None, method="cubic", name=""):
coords, knots=knots, method=method, name=name, basis="xyz"
)

def to_FourierRZ(self, N=None, grid=None, NFP=None, name=""):
def to_FourierRZ(self, N=10, grid=None, NFP=None, sym=False, name=""):
"""Convert Curve to FourierRZCurve representation.
Note that some types of curves may not be representable in this basis.
Expand All @@ -299,6 +299,8 @@ def to_FourierRZ(self, N=None, grid=None, NFP=None, name=""):
NFP : int
Number of field periods, the curve will have a discrete toroidal symmetry
according to NFP.
sym : bool, optional
Whether the curve is stellarator-symmetric or not. Default is False.
name : str
name for this curve
Expand All @@ -312,9 +314,45 @@ def to_FourierRZ(self, N=None, grid=None, NFP=None, name=""):

NFP = 1 or NFP
if grid is None:
grid = LinearGrid(N=2 * N + 1, NFP=NFP)
grid = LinearGrid(N=2 * N + 1)
coords = self.compute("x", grid=grid, basis="xyz")["x"]
return FourierRZCurve.from_values(coords, N=N, NFP=NFP, basis="xyz", name=name)
return FourierRZCurve.from_values(
coords, N=N, NFP=NFP, basis="xyz", name=name, sym=sym
)

def to_FourierPlanar(self, N=10, grid=None, basis="xyz", name=""):
"""Convert Curve to FourierPlanarCurve representation.
Note that some types of curves may not be representable in this basis.
In this case, a least-squares fit will be done to find the
planar curve that best represents the curve.
Parameters
----------
N : int
Fourier resolution of the new FourierPlanarCurve representation.
grid : Grid, int or None
Grid used to evaluate curve coordinates on to fit with FourierPlanarCurve.
If an integer, uses that many equally spaced points.
basis : {'xyz', 'rpz'}
Coordinate system for center and normal vectors. Default = 'xyz'.
name : str
name for this curve
Returns
-------
curve : FourierPlanarCurve
New representation of the curve parameterized by Fourier series for
minor radius r in a plane specified by a center position and normal
vector.
"""
from .curve import FourierPlanarCurve

if grid is None:
grid = LinearGrid(N=2 * N + 1)
coords = self.compute("x", grid=grid, basis=basis)["x"]
return FourierPlanarCurve.from_values(coords, N=N, basis=basis, name=name)


class Surface(IOAble, Optimizable, ABC):
Expand Down
Loading

0 comments on commit d62d9ca

Please sign in to comment.