Skip to content

Commit 7b166a8

Browse files
committed
Merge branch 'master' into fix-gridfile-loading
Conflicts: xbout/load.py
2 parents 3bad418 + 0761754 commit 7b166a8

File tree

16 files changed

+682
-305
lines changed

16 files changed

+682
-305
lines changed

.github/workflows/master.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
if: always()
2121
strategy:
2222
matrix:
23-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.x"]
23+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
2424
fail-fast: false
2525

2626
steps:
@@ -61,7 +61,7 @@ jobs:
6161
run: |
6262
sudo apt-get update && sudo apt-get install libhdf5-dev libnetcdf-dev
6363
python -m pip install --upgrade pip
64-
pip install xarray~=0.18.0 pandas~=1.4.0
64+
pip install xarray~=2023.1.0 pandas~=1.4.0
6565
- name: Install package
6666
run: |
6767
pip install -e .[tests]

.github/workflows/pythonpackage.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ jobs:
3131
python-version: ${{ matrix.python-version }}
3232
- name: Install dependencies
3333
run: |
34-
sudo apt-get update && sudo apt-get install libhdf5-dev libnetcdf-dev
3534
python -m pip install --upgrade pip
3635
- name: Install package
3736
run: |
@@ -59,9 +58,8 @@ jobs:
5958
python-version: ${{ matrix.python-version }}
6059
- name: Install dependencies
6160
run: |
62-
sudo apt-get update && sudo apt-get install libhdf5-dev libnetcdf-dev
6361
python -m pip install --upgrade pip
64-
pip install xarray~=0.18.0 pandas~=1.4.0
62+
pip install xarray~=2023.1.0 pandas~=1.4.0
6563
- name: Install package
6664
run: |
6765
pip install -e .[tests]

.github/workflows/ruff.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ jobs:
55
dependency-review:
66
runs-on: ubuntu-latest
77
steps:
8-
- uses: actions/checkout@v3
9-
- uses: actions/setup-python@v4
8+
- uses: actions/checkout@v4
9+
- uses: actions/setup-python@v5
1010
with:
1111
python-version: '3.x'
1212
- name: Install ruff
1313
run: pip install ruff
1414
- name: Run ruff
15-
run: ruff xbout
15+
run: ruff check xbout

pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ classifiers = [
3333
]
3434
requires-python = ">=3.8"
3535
dependencies = [
36-
"xarray>=0.18.0,<2022.9.0",
36+
"xarray>=2023.01.0",
3737
"boutdata>=0.1.4",
3838
"dask[array]>=2.10.0",
3939
"gelidum>=0.5.3",
@@ -53,6 +53,9 @@ calc = [
5353
"xrft",
5454
"xhistogram",
5555
]
56+
cherab = [
57+
"cherab",
58+
]
5659
docs = [
5760
"sphinx >= 5.3",
5861
"sphinx-book-theme >= 0.4.0rc1",
@@ -79,5 +82,5 @@ write_to = "xbout/_version.py"
7982
[tool.setuptools]
8083
packages = ["xbout"]
8184

82-
[tool.ruff]
85+
[tool.ruff.lint]
8386
ignore = ["E501"]

xbout/boutdataarray.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,3 +1101,71 @@ def plot3d(self, ax=None, **kwargs):
11011101
See plotfuncs.plot3d()
11021102
"""
11031103
return plotfuncs.plot3d(self.data, **kwargs)
1104+
1105+
def with_cherab_grid(self):
1106+
"""
1107+
Returns a new DataArray with a 'cherab_grid' attribute.
1108+
1109+
If called then the `cherab` package must be available.
1110+
"""
1111+
# Import here so Cherab is required only if this method is called
1112+
from .cherab import grid
1113+
1114+
return grid.da_with_cherab_grid(self.data)
1115+
1116+
def as_cherab_data(self):
1117+
"""
1118+
Returns a new cherab.TriangularData object.
1119+
1120+
If a Cherab grid has not been calculated then it will be created.
1121+
It is more efficient to first compute a Cherab grid for a whole
1122+
DataSet (using `with_cherab_grid`) and then call this function
1123+
on individual DataArrays.
1124+
"""
1125+
if "cherab_grid" not in self.data.attrs:
1126+
# Calculate the Cherab triangulation
1127+
da = self.with_cherab_grid()
1128+
else:
1129+
da = self.data
1130+
1131+
return da.attrs["cherab_grid"].with_data(da)
1132+
1133+
def as_cherab_emitter(
1134+
self,
1135+
parent=None,
1136+
cylinder_zmin=None,
1137+
cylinder_zmax=None,
1138+
cylinder_rmin=None,
1139+
cylinder_rmax=None,
1140+
step: float = 0.01,
1141+
):
1142+
"""
1143+
Make a Cherab emitter (RadiationFunction), rotating a 2D mesh about the Z axis
1144+
1145+
Cherab (https://www.cherab.info/) is a python library for forward
1146+
modelling diagnostics based on spectroscopic plasma emission.
1147+
It is based on the Raysect (http://www.raysect.org/) scientific
1148+
ray-tracing framework.
1149+
1150+
Parameters
1151+
----------
1152+
parent : Cherab scene (default None)
1153+
The Cherab scene to attach the emitter to
1154+
step : float (default 0.01 meters)
1155+
Volume integration step length [m]
1156+
1157+
Returns
1158+
-------
1159+
1160+
A cherab.tools.emitters.RadiationFunction
1161+
1162+
"""
1163+
1164+
return self.as_cherab_data().to_emitter(
1165+
parent=parent,
1166+
cylinder_zmin=cylinder_zmin,
1167+
cylinder_zmax=cylinder_zmax,
1168+
cylinder_rmin=cyliner_rmin,
1169+
cylinder_rmax=cyliner_rmax,
1170+
step=step,
1171+
)

xbout/boutdataset.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -597,12 +597,12 @@ def interpolate_to_cartesian(
597597
n_toroidal = ds.sizes[zdim]
598598

599599
# Create Cartesian grid to interpolate to
600-
Xmin = ds["X_cartesian"].min()
601-
Xmax = ds["X_cartesian"].max()
602-
Ymin = ds["Y_cartesian"].min()
603-
Ymax = ds["Y_cartesian"].max()
604-
Zmin = ds["Z_cartesian"].min()
605-
Zmax = ds["Z_cartesian"].max()
600+
Xmin = ds["X_cartesian"].min().data[()]
601+
Xmax = ds["X_cartesian"].max().data[()]
602+
Ymin = ds["Y_cartesian"].min().data[()]
603+
Ymax = ds["Y_cartesian"].max().data[()]
604+
Zmin = ds["Z_cartesian"].min().data[()]
605+
Zmax = ds["Z_cartesian"].max().data[()]
606606
newX_1d = xr.DataArray(np.linspace(Xmin, Xmax, nX), dims="X")
607607
newX = newX_1d.expand_dims({"Y": nY, "Z": nZ}, axis=[1, 2])
608608
newY_1d = xr.DataArray(np.linspace(Ymin, Ymax, nY), dims="Y")
@@ -614,9 +614,7 @@ def interpolate_to_cartesian(
614614
# Define newzeta in range 0->2*pi
615615
newzeta = np.where(newzeta < 0.0, newzeta + 2.0 * np.pi, newzeta)
616616

617-
from scipy.interpolate import (
618-
RegularGridInterpolator,
619-
)
617+
from scipy.interpolate import RegularGridInterpolator
620618

621619
# Create Cylindrical coordinates for intermediate grid
622620
Rcyl_min = float_type(ds["R"].min())
@@ -664,10 +662,7 @@ def interp_single_time(da):
664662
)
665663

666664
print(" do 3d interpolation")
667-
return interp(
668-
(newR, newZ, newzeta),
669-
method="linear",
670-
)
665+
return interp((newR, newZ, newzeta), method="linear")
671666

672667
for name, da in ds.data_vars.items():
673668
print(f"\ninterpolating {name}")
@@ -993,14 +988,7 @@ def to_restart(
993988
# Is this even possible without saving the guard cells?
994989
# Can they be recreated?
995990
restart_datasets, paths = _split_into_restarts(
996-
self.data,
997-
variables,
998-
savepath,
999-
nxpe,
1000-
nype,
1001-
tind,
1002-
prefix,
1003-
overwrite,
991+
self.data, variables, savepath, nxpe, nype, tind, prefix, overwrite
1004992
)
1005993

1006994
with ProgressBar():
@@ -1357,6 +1345,17 @@ def is_list(variable):
13571345

13581346
return anim
13591347

1348+
def with_cherab_grid(self):
1349+
"""
1350+
Returns a new DataSet with a 'cherab_grid' attribute.
1351+
1352+
If called then the `cherab` package must be available.
1353+
"""
1354+
# Import here so Cherab is required only if this method is called
1355+
from .cherab import grid
1356+
1357+
return grid.ds_with_cherab_grid(self.data)
1358+
13601359

13611360
def _find_major_vars(data):
13621361
"""

xbout/cherab/__init__.py

Whitespace-only changes.

xbout/cherab/grid.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import numpy as np
2+
import xarray as xr
3+
4+
from .triangulate import Triangulate
5+
6+
7+
def da_with_cherab_grid(da):
8+
"""
9+
Convert an BOUT++ DataArray to a format that Cherab can use:
10+
- A 'cell_number' coordinate
11+
- A 'cherab_grid` attribute
12+
13+
The cell_number coordinate enables the DataArray to be sliced
14+
before input to Cherab.
15+
16+
Parameters
17+
----------
18+
ds : xarray.DataArray
19+
20+
Returns
21+
-------
22+
updated_da
23+
"""
24+
if "cherab_grid" in da.attrs:
25+
# Already has required attribute
26+
return da
27+
28+
if da.attrs["geometry"] != "toroidal":
29+
raise ValueError("xhermes.plotting.cherab: Geometry must be toroidal")
30+
31+
if da.sizes["zeta"] != 1:
32+
raise ValueError("xhermes.plotting.cherab: Zeta index must have size 1")
33+
34+
nx = da.sizes["x"]
35+
ny = da.sizes["theta"]
36+
37+
# Cell corners
38+
rm = np.stack(
39+
(
40+
da.coords["Rxy_upper_right_corners"],
41+
da.coords["Rxy_upper_left_corners"],
42+
da.coords["Rxy_lower_right_corners"],
43+
da.coords["Rxy_lower_left_corners"],
44+
),
45+
axis=-1,
46+
)
47+
zm = np.stack(
48+
(
49+
da.coords["Zxy_upper_right_corners"],
50+
da.coords["Zxy_upper_left_corners"],
51+
da.coords["Zxy_lower_right_corners"],
52+
da.coords["Zxy_lower_left_corners"],
53+
),
54+
axis=-1,
55+
)
56+
57+
grid = Triangulate(rm, zm)
58+
59+
# Store the cell number as a coordinate.
60+
# This allows slicing of arrays before passing to Cherab
61+
62+
# Create a DataArray for the vertices and triangles
63+
cell_number = xr.DataArray(
64+
grid.cell_number, dims=["x", "theta"], name="cell_number"
65+
)
66+
67+
result = da.assign_coords(cell_number=cell_number)
68+
result.attrs.update(cherab_grid=grid)
69+
return result
70+
71+
72+
def ds_with_cherab_grid(ds):
73+
"""
74+
Create an xarray DataSet with a Cherab grid
75+
76+
Parameters
77+
----------
78+
ds : xarray.Dataset
79+
80+
Returns
81+
-------
82+
updated_ds
83+
"""
84+
85+
# The same operation works on a DataSet
86+
ds = da_with_cherab_grid(ds)
87+
88+
# Add the Cherab grid as an attribute to all variables
89+
grid = ds.attrs["cherab_grid"]
90+
for var in ds.data_vars:
91+
ds[var].attrs.update(cherab_grid=grid)
92+
93+
return ds

0 commit comments

Comments
 (0)