Skip to content

Commit

Permalink
Fix some plot issues when NFP differs from one for objects, or when p…
Browse files Browse the repository at this point in the history
…assed-in phi exceeds 2pi/nfp (#1204)

- in `plot_surfaces` (and `plot_section` and `plot_boundary`) when `phi`
is supplied which exceeds `NFP`, the grid used for the map coordinates
(a lineargrid with `NFP=eq.NFP` would truncate the `phi`. This is fine
unless the new truncated phi has some duplicates. The grid is still a
meshgrid of the supplied rho, theta, zeta, however, then the
`grid.num_zeta` value, which gives the unique zeta values, might not
correspond to the original length of `phi`. This causes reshaping errors
like those seen in #1202. These grids though dont need to have the NFP
arg at all as they always supply `phi` as an array (instead of supplying
`N`), so we can set them to `1` to avoid this issie
- in `plot_comparison` and `plot_boundaries`, there is some ambiguity on
what should happen by default when things of differing NFP are passed
in. This changes to by default, throw an error if multiple
nonaxisymmetric objects with difering NFP are passed in (as it is not
clear what phis to do, and even then the plot title "phi*NFP/2pi" is
ambiguous (which NFP does it refer to?)). If there are differing NFPs
but the only ones with nonzero NFPs have the same NFP, and the remaining
are axisymmetric objects, it will change the axisymmetric object NFP to
match the nonaxisymetric ones (we can do that and it does not affect the
plot since they are the same at every zeta).

The change in `plot_boundaries` behavior is something I am not 100% sure
on, what do people think it should be? we could leave as is and just
plot a bunch of XS, maybe with the phi being determined inside of the
for loop instead of outside, so that if the default is used you get
(0,2np.pi,4,endpoint=False). The only weird thing is now you have a
bunch of XS where they are at differing angles and it is not clear how
they should be compared to eachother.

Resolves #1202
  • Loading branch information
YigitElma authored Aug 28, 2024
2 parents 9f5cb55 + 3223d50 commit 8351c43
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 7 deletions.
50 changes: 44 additions & 6 deletions desc/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -1352,10 +1352,9 @@ def plot_section(
phi = np.atleast_1d(phi)
nphi = len(phi)
if grid is None:
nfp = eq.NFP
grid_kwargs = {
"L": 25,
"NFP": nfp,
"NFP": 1,
"axis": False,
"theta": np.linspace(0, 2 * np.pi, 91, endpoint=True),
"zeta": phi,
Expand Down Expand Up @@ -1610,9 +1609,14 @@ def plot_surfaces(eq, rho=8, theta=8, phi=None, ax=None, return_data=False, **kw
phi = np.atleast_1d(phi)
nphi = len(phi)

# do not need NFP supplied to these grids as
# the above logic takes care of the correct phi range
# if defaults are requested. Setting NFP here instead
# can create reshaping issues when phi is supplied and gets
# truncated by 2pi/NFP. See PR #1204
grid_kwargs = {
"rho": rho,
"NFP": nfp,
"NFP": 1,
"theta": np.linspace(0, 2 * np.pi, NT, endpoint=True),
"zeta": phi,
}
Expand All @@ -1631,7 +1635,7 @@ def plot_surfaces(eq, rho=8, theta=8, phi=None, ax=None, return_data=False, **kw
)
grid_kwargs = {
"rho": np.linspace(0, 1, NR),
"NFP": nfp,
"NFP": 1,
"theta": theta,
"zeta": phi,
}
Expand Down Expand Up @@ -1960,7 +1964,7 @@ def plot_boundary(eq, phi=None, plot_axis=True, ax=None, return_data=False, **kw
plot_axis = plot_axis and eq.L > 0
rho = np.array([0.0, 1.0]) if plot_axis else np.array([1.0])

grid_kwargs = {"NFP": eq.NFP, "rho": rho, "theta": 100, "zeta": phi}
grid_kwargs = {"NFP": 1, "rho": rho, "theta": 100, "zeta": phi}
grid = _get_grid(**grid_kwargs)
nr, nt, nz = grid.num_rho, grid.num_theta, grid.num_zeta
grid = Grid(
Expand Down Expand Up @@ -2030,6 +2034,9 @@ def plot_boundaries(
):
"""Plot stellarator boundaries at multiple toroidal coordinates.
NOTE: If attempting to plot objects with differing NFP, `phi` must
be given explicitly.
Parameters
----------
eqs : array-like of Equilibrium, Surface or EquilibriaFamily
Expand Down Expand Up @@ -2085,7 +2092,21 @@ def plot_boundaries(
fig, ax = plot_boundaries((eq1, eq2, eq3))
"""
# if NFPs are not all equal, means there are
# objects with differing NFPs, which it is not clear
# how to choose the phis for by default, so we will throw an error
# unless phi was given.
phi = parse_argname_change(phi, kwargs, "zeta", "phi")
errorif(
not np.allclose([thing.NFP for thing in eqs], eqs[0].NFP) and phi is None,
ValueError,
"supplied objects must have the same number of field periods, "
"or if there are differing field periods, `phi` must be given explicitly."
f" Instead, supplied objects have NFPs {[t.NFP for t in eqs]}."
" If attempting to plot an axisymmetric object with non-axisymmetric objects,"
" you must use the `change_resolution` method to make the axisymmetric "
"object have the same NFP as the non-axisymmetric objects.",
)

figsize = kwargs.pop("figsize", None)
cmap = kwargs.pop("cmap", "rainbow")
Expand Down Expand Up @@ -2129,7 +2150,7 @@ def plot_boundaries(
plot_axis_i = plot_axis and eqs[i].L > 0
rho = np.array([0.0, 1.0]) if plot_axis_i else np.array([1.0])

grid_kwargs = {"NFP": eqs[i].NFP, "theta": 100, "zeta": phi, "rho": rho}
grid_kwargs = {"NFP": 1, "theta": 100, "zeta": phi, "rho": rho}
grid = _get_grid(**grid_kwargs)
nr, nt, nz = grid.num_rho, grid.num_theta, grid.num_zeta
grid = Grid(
Expand Down Expand Up @@ -2198,6 +2219,9 @@ def plot_comparison(
):
"""Plot comparison between flux surfaces of multiple equilibria.
NOTE: If attempting to plot objects with differing NFP, `phi` must
be given explicitly.
Parameters
----------
eqs : array-like of Equilibrium or EquilibriaFamily
Expand Down Expand Up @@ -2266,7 +2290,21 @@ def plot_comparison(
)
"""
# if NFPs are not all equal, means there are
# objects with differing NFPs, which it is not clear
# how to choose the phis for by default, so we will throw an error
# unless phi was given.
phi = parse_argname_change(phi, kwargs, "zeta", "phi")
errorif(
not np.allclose([thing.NFP for thing in eqs], eqs[0].NFP) and phi is None,
ValueError,
"supplied objects must have the same number of field periods, "
"or if there are differing field periods, `phi` must be given explicitly."
f" Instead, supplied objects have NFPs {[t.NFP for t in eqs]}."
" If attempting to plot an axisymmetric object with non-axisymmetric objects,"
" you must use the `change_resolution` method to make the axisymmetric "
"object have the same NFP as the non-axisymmetric objects.",
)
color = parse_argname_change(color, kwargs, "colors", "color")
ls = parse_argname_change(ls, kwargs, "linestyles", "ls")
lw = parse_argname_change(lw, kwargs, "lws", "lw")
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 24 additions & 1 deletion tests/test_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,14 @@ def test_plot_boundaries(self):
eq1 = get("SOLOVEV")
eq2 = get("DSHAPE")
eq3 = get("W7-X")
fig, ax, data = plot_boundaries((eq1, eq2, eq3), return_data=True)
eq4 = get("ESTELL")
with pytest.raises(ValueError, match="differing field periods"):
fig, ax = plot_boundaries([eq3, eq4], theta=0)
fig, ax, data = plot_boundaries(
(eq1, eq2, eq3),
phi=np.linspace(0, 2 * np.pi / eq3.NFP, 4, endpoint=False),
return_data=True,
)
assert "R" in data.keys()
assert "Z" in data.keys()
assert len(data["R"]) == 3
Expand Down Expand Up @@ -550,6 +557,22 @@ def test_plot_comparison_no_theta(self):
fig, ax = plot_comparison(eqf, theta=0)
return fig

@pytest.mark.unit
@pytest.mark.mpl_image_compare(remove_text=True, tolerance=tol_2d)
def test_plot_comparison_different_NFPs(self):
"""Test plotting comparison of flux surfaces with differing NFPs."""
eq = get("SOLOVEV")
eq_nonax = get("HELIOTRON")
eq_nonax2 = get("ESTELL")
with pytest.raises(ValueError, match="differing field periods"):
fig, ax = plot_comparison([eq_nonax, eq_nonax2], theta=0)
fig, ax = plot_comparison(
[eq, eq_nonax],
phi=np.linspace(0, 2 * np.pi / eq_nonax.NFP, 6, endpoint=False),
theta=0,
)
return fig


class TestPlotGrid:
"""Tests for the plot_grid function."""
Expand Down

0 comments on commit 8351c43

Please sign in to comment.