Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReduceQuadratureGrid number of radial points to match intended functionality #1188

Merged
merged 33 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c4b05d8
reduce quad grid number of radial points to match intended functional…
dpanici Aug 13, 2024
1b7fb6e
reduce some test tols slightly
dpanici Aug 14, 2024
3ec0628
Merge branch 'master' into dp/quad-grid
dpanici Aug 14, 2024
ddd0e15
fix tol
dpanici Aug 14, 2024
326906b
Merge branch 'dp/quad-grid' of github.com:PlasmaControl/DESC into dp/…
dpanici Aug 14, 2024
f9dcce9
Merge branch 'master' into dp/quad-grid
dpanici Aug 14, 2024
ab95773
udpate master data
dpanici Aug 14, 2024
c095799
update plot
dpanici Aug 14, 2024
47cbef0
update bmag test and plot
dpanici Aug 14, 2024
a1c80e4
Merge branch 'master' into dp/quad-grid
dpanici Aug 15, 2024
890cb62
Merge branch 'master' into dp/quad-grid
dpanici Aug 16, 2024
e140509
Merge branch 'master' into dp/quad-grid
dpanici Aug 20, 2024
0708b1c
Merge branch 'master' into dp/quad-grid
dpanici Aug 20, 2024
32583dc
Reduce tolerance until GitHub issue #1207 gets resolved
unalmis Aug 21, 2024
bd543a2
change num of pts
dpanici Aug 22, 2024
28d5bdd
small change to plot test
dpanici Aug 22, 2024
8449780
update plot
dpanici Aug 22, 2024
0147ef3
address comment
dpanici Aug 22, 2024
bf856cc
increase grid pts to correct value, my final answer to it
dpanici Aug 22, 2024
f93be1f
correct to L+2 // 2
dpanici Aug 22, 2024
dc0e155
Merge branch 'master' into dp/quad-grid
dpanici Aug 22, 2024
826fdc3
update map coords
dpanici Aug 22, 2024
028d650
Merge branch 'dp/quad-grid' of github.com:PlasmaControl/DESC into dp/…
dpanici Aug 22, 2024
40aecae
Use more accurate method in root finding for clebsch coordinates by d…
unalmis Aug 22, 2024
011e19b
Merge branch 'master' into dp/quad-grid
f0uriest Aug 22, 2024
bc8a5f6
Reduce resolution for map_coordinates test
unalmis Aug 22, 2024
907b2d1
Make sure Hermite spline is used in case rho values in coords differ …
unalmis Aug 22, 2024
e51901b
Make sure profile can be computed under jit
unalmis Aug 22, 2024
cfd71f8
Merge branch 'master' into dp/quad-grid
dpanici Aug 22, 2024
faeedef
update pkl
dpanici Aug 22, 2024
e323cf2
Merge branch 'master' into dp/quad-grid
f0uriest Aug 23, 2024
7793fdc
Merge branch 'master' into dp/quad-grid
dpanici Aug 23, 2024
7f93cea
Merge branch 'master' into dp/quad-grid...
unalmis Aug 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions desc/equilibrium/coords.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,28 @@
inbasis = tuple(inbasis)
outbasis = tuple(outbasis)

basis_derivs = tuple(f"{X}_{d}" for X in inbasis for d in ("r", "t", "z"))
for key in basis_derivs:
errorif(
key not in data_index["desc.equilibrium.equilibrium.Equilibrium"],
NotImplementedError,
f"don't have recipe to compute partial derivative {key}",
)

profiles = get_profiles(inbasis + basis_derivs, eq)

# TODO: make this work for permutations of in/out basis
if outbasis == ("rho", "theta", "zeta"):
# TODO: get iota if not supplied using below logic
if inbasis == ("rho", "alpha", "zeta") and "iota" in kwargs:
if inbasis == ("rho", "alpha", "zeta"):
if "iota" in kwargs:
iota = kwargs.pop("iota")

Check warning on line 108 in desc/equilibrium/coords.py

View check run for this annotation

Codecov / codecov/patch

desc/equilibrium/coords.py#L107-L108

Added lines #L107 - L108 were not covered by tests
else:
if profiles["iota"] is None:
profiles["iota"] = eq.get_profile(["iota", "iota_r"], params=params)
iota = profiles["iota"].compute(Grid(coords, sort=False, jitable=True))

Check warning on line 112 in desc/equilibrium/coords.py

View check run for this annotation

Codecov / codecov/patch

desc/equilibrium/coords.py#L110-L112

Added lines #L110 - L112 were not covered by tests
return _map_clebsch_coordinates(
coords,
kwargs.pop("iota"),
iota,
params["L_lmn"],
eq.L_basis,
guess[:, 1] if guess is not None else None,
Expand All @@ -118,29 +133,20 @@
**kwargs,
)

basis_derivs = tuple(f"{X}_{d}" for X in inbasis for d in ("r", "t", "z"))
for key in basis_derivs:
errorif(
key not in data_index["desc.equilibrium.equilibrium.Equilibrium"],
NotImplementedError,
f"don't have recipe to compute partial derivative {key}",
)
# do surface average to get iota once
if "iota" in profiles and profiles["iota"] is None:
profiles["iota"] = eq.get_profile(["iota", "iota_r"], params=params)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is iota_r being used anywhere or is it just there so you can use Hermite?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iota and iota_r get computed on QuadratureGrid then we use those values to construct Hermite spline for $\iota(\rho)$. $\iota(\rho)$ is then evaluated at whatever sequence of $\rho$ values the Newton iteration proceeds with. For $\rho, \alpha, \zeta$ coordinate mappings, the $\rho$ values don't need to change throughout Newton iteration since we can just do 1D root finding, which this PR now ensures is done.

params["i_l"] = profiles["iota"].params

Check warning on line 139 in desc/equilibrium/coords.py

View check run for this annotation

Codecov / codecov/patch

desc/equilibrium/coords.py#L138-L139

Added lines #L138 - L139 were not covered by tests

rhomin = kwargs.pop("rhomin", tol / 10)
warnif(period is None, msg="Assuming no periodicity.")
period = np.asarray(setdefault(period, (np.inf, np.inf, np.inf)))
coords = _periodic(coords, period)

profiles = get_profiles(inbasis + basis_derivs, eq)
p = "desc.equilibrium.equilibrium.Equilibrium"
names = inbasis + basis_derivs + outbasis
deps = list(set(get_data_deps(names, obj=p) + list(names)))

# do surface average to get iota once
if "iota" in profiles and profiles["iota"] is None:
profiles["iota"] = eq.get_profile(["iota", "iota_r"], params=params)
params["i_l"] = profiles["iota"].params

@functools.partial(jit, static_argnums=1)
def compute(y, basis):
grid = Grid(y, sort=False, jitable=True)
Expand Down
5 changes: 4 additions & 1 deletion desc/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -1295,7 +1295,10 @@ def _create_nodes(self, L=1, M=1, N=1, NFP=1):
self._N = check_nonnegint(N, "N", False)
self._NFP = check_posint(NFP, "NFP", False)
self._period = (np.inf, 2 * np.pi, 2 * np.pi / self._NFP)
L = L + 1
# floor divide (L+2) by 2 bc only need (L+1)/2 points to
# integrate L-th order jacobi polynomial exactly, so this
# ensures we have enough pts for both odd and even L
L = (L + 2) // 2
M = 2 * M + 1
N = 2 * N + 1

Expand Down
Binary file modified tests/baseline/test_plot_b_mag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/baseline/test_plot_grid_quad.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/inputs/master_compute_data_rpz.pkl
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/test_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -1196,7 +1196,7 @@ def test_BootstrapRedlConsistency_normalization(self):
)

# Results are not perfectly identical because ln(Lambda) is not quite invariant.
np.testing.assert_allclose(results, expected, rtol=2e-3)
np.testing.assert_allclose(results, expected, rtol=3e-3)
unalmis marked this conversation as resolved.
Show resolved Hide resolved

@pytest.mark.regression
def test_bootstrap_consistency_iota(self, TmpDir):
Expand Down
30 changes: 6 additions & 24 deletions tests/test_equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from desc.io import InputReader
from desc.objectives import ForceBalance, ObjectiveFunction, get_equilibrium_objective
from desc.profiles import PowerSeriesProfile
from desc.utils import errorif

from .utils import area_difference, compute_coords

Expand Down Expand Up @@ -50,33 +49,16 @@ def test_map_coordinates():
"""Test root finding for (rho,theta,zeta) for common use cases."""
# finding coordinates along a single field line
eq = get("NCSX")
with pytest.warns(UserWarning, match="Reducing radial"):
eq.change_resolution(3, 3, 3, 6, 6, 6)
n = 100
coords = np.array([np.ones(n), np.zeros(n), np.linspace(0, 10 * np.pi, n)]).T
grid = LinearGrid(rho=1, M=eq.M_grid, N=eq.N_grid, sym=eq.sym, NFP=eq.NFP)
iota = grid.compress(eq.compute("iota", grid=grid)["iota"])
iota = np.broadcast_to(iota, shape=(n,))

tol = 1e-5
out_1 = eq.map_coordinates(
coords, inbasis=["rho", "alpha", "zeta"], iota=iota, tol=tol
)
assert np.isfinite(out_1).all()
out_2 = eq.map_coordinates(
out = eq.map_coordinates(
coords,
inbasis=["rho", "alpha", "zeta"],
period=(np.inf, 2 * np.pi, np.inf),
tol=tol,
)
assert np.isfinite(out_2).all()
diff = out_1 - out_2
errorif(
not np.all(
np.isclose(diff, 0, atol=2 * tol)
| np.isclose(np.abs(diff), 2 * np.pi, atol=2 * tol)
),
AssertionError,
f"diff: {diff}",
)
assert np.isfinite(out).all()

eq = get("DSHAPE")

Expand All @@ -89,9 +71,9 @@ def test_map_coordinates():

grid = Grid(np.vstack([rho, theta, zeta]).T, sort=False)
in_data = eq.compute(inbasis, grid=grid)
in_coords = np.stack([in_data[k] for k in inbasis], axis=-1)
in_coords = np.column_stack([in_data[k] for k in inbasis])
out_data = eq.compute(outbasis, grid=grid)
out_coords = np.stack([out_data[k] for k in outbasis], axis=-1)
out_coords = np.column_stack([out_data[k] for k in outbasis])

out = eq.map_coordinates(
in_coords,
Expand Down
10 changes: 6 additions & 4 deletions tests/test_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,14 +482,16 @@ def test_quadrature_grid(self):
N = 0
NFP = 1
grid_quad = QuadratureGrid(L, M, N, NFP)
roots, weights = special.js_roots(3, 2, 2)
roots, weights = special.js_roots(2, 2, 2)
unalmis marked this conversation as resolved.
Show resolved Hide resolved
quadrature_nodes = np.stack(
[
np.array([roots[0]] * 5 + [roots[1]] * 5 + [roots[2]] * 5),
np.array(
[0, 2 * np.pi / 5, 4 * np.pi / 5, 6 * np.pi / 5, 8 * np.pi / 5] * 3
[roots[0]] * grid_quad.num_theta + [roots[1]] * grid_quad.num_theta
),
np.zeros(15),
np.array(
[0, 2 * np.pi / 5, 4 * np.pi / 5, 6 * np.pi / 5, 8 * np.pi / 5] * 2
),
np.zeros(10),
]
).T
np.testing.assert_allclose(grid_quad.spacing.prod(axis=1), grid_quad.weights)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_objective_funs.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ def test(eq):
obj.build()
f = obj.compute_unscaled(*obj.xs(eq))
f_scaled = obj.compute_scaled_error(*obj.xs(eq))
np.testing.assert_allclose(f, 1.3 / 0.7, rtol=5e-3)
np.testing.assert_allclose(f_scaled, 2 * (1.3 / 0.7), rtol=5e-3)
np.testing.assert_allclose(f, 1.3 / 0.7, rtol=8e-3)
np.testing.assert_allclose(f_scaled, 2 * (1.3 / 0.7), rtol=8e-3)
rahulgaur104 marked this conversation as resolved.
Show resolved Hide resolved

test(get("HELIOTRON"))
test(get("HELIOTRON").surface)
Expand Down
9 changes: 3 additions & 6 deletions tests/test_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import matplotlib.pyplot as plt
import numpy as np
import pytest
from scipy.interpolate import interp1d

from desc.basis import (
DoubleFourierSeries,
Expand Down Expand Up @@ -823,17 +822,15 @@ def flatten_coils(coilset):
def test_plot_b_mag():
"""Test plot of |B| on longer field lines for gyrokinetic simulations."""
psi = 0.5
rho = np.sqrt(psi)
npol = 2
nzgrid = 128
alpha = 0
# compute and fit iota profile
# compute iota
eq = get("W7-X")
data = eq.compute("iota")
fi = interp1d(data["rho"], data["iota"])
iota = eq.compute("iota", grid=LinearGrid(rho=rho, NFP=eq.NFP))["iota"][0]

# get flux tube coordinate system
rho = np.sqrt(psi)
iota = fi(rho)
zeta = np.linspace(
-np.pi * npol / np.abs(iota), np.pi * npol / np.abs(iota), 2 * nzgrid + 1
)
Expand Down
Loading