Skip to content

Commit

Permalink
Merge pull request #305 from boutproject/cherab-interface
Browse files Browse the repository at this point in the history
Cherab interface for xBOUT
  • Loading branch information
bendudson authored Nov 27, 2024
2 parents d8b79ee + 37e168c commit d113e0e
Show file tree
Hide file tree
Showing 6 changed files with 409 additions and 15 deletions.
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ calc = [
"xrft",
"xhistogram",
]
cherab = [
"cherab",
]
docs = [
"sphinx >= 5.3",
"sphinx-book-theme >= 0.4.0rc1",
Expand Down
68 changes: 68 additions & 0 deletions xbout/boutdataarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -1101,3 +1101,71 @@ def plot3d(self, ax=None, **kwargs):
See plotfuncs.plot3d()
"""
return plotfuncs.plot3d(self.data, **kwargs)

def with_cherab_grid(self):
"""
Returns a new DataArray with a 'cherab_grid' attribute.
If called then the `cherab` package must be available.
"""
# Import here so Cherab is required only if this method is called
from .cherab import grid

return grid.da_with_cherab_grid(self.data)

def as_cherab_data(self):
"""
Returns a new cherab.TriangularData object.
If a Cherab grid has not been calculated then it will be created.
It is more efficient to first compute a Cherab grid for a whole
DataSet (using `with_cherab_grid`) and then call this function
on individual DataArrays.
"""
if "cherab_grid" not in self.data.attrs:
# Calculate the Cherab triangulation
da = self.with_cherab_grid()
else:
da = self.data

return da.attrs["cherab_grid"].with_data(da)

def as_cherab_emitter(
self,
parent=None,
cylinder_zmin=None,
cylinder_zmax=None,
cylinder_rmin=None,
cylinder_rmax=None,
step: float = 0.01,
):
"""
Make a Cherab emitter (RadiationFunction), rotating a 2D mesh about the Z axis
Cherab (https://www.cherab.info/) is a python library for forward
modelling diagnostics based on spectroscopic plasma emission.
It is based on the Raysect (http://www.raysect.org/) scientific
ray-tracing framework.
Parameters
----------
parent : Cherab scene (default None)
The Cherab scene to attach the emitter to
step : float (default 0.01 meters)
Volume integration step length [m]
Returns
-------
A cherab.tools.emitters.RadiationFunction
"""

return self.as_cherab_data().to_emitter(
parent=parent,
cylinder_zmin=cylinder_zmin,
cylinder_zmax=cylinder_zmax,
cylinder_rmin=cyliner_rmin,
cylinder_rmax=cyliner_rmax,
step=step,
)
29 changes: 14 additions & 15 deletions xbout/boutdataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -614,9 +614,7 @@ def interpolate_to_cartesian(
# Define newzeta in range 0->2*pi
newzeta = np.where(newzeta < 0.0, newzeta + 2.0 * np.pi, newzeta)

from scipy.interpolate import (
RegularGridInterpolator,
)
from scipy.interpolate import RegularGridInterpolator

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

print(" do 3d interpolation")
return interp(
(newR, newZ, newzeta),
method="linear",
)
return interp((newR, newZ, newzeta), method="linear")

for name, da in ds.data_vars.items():
print(f"\ninterpolating {name}")
Expand Down Expand Up @@ -993,14 +988,7 @@ def to_restart(
# Is this even possible without saving the guard cells?
# Can they be recreated?
restart_datasets, paths = _split_into_restarts(
self.data,
variables,
savepath,
nxpe,
nype,
tind,
prefix,
overwrite,
self.data, variables, savepath, nxpe, nype, tind, prefix, overwrite
)

with ProgressBar():
Expand Down Expand Up @@ -1357,6 +1345,17 @@ def is_list(variable):

return anim

def with_cherab_grid(self):
"""
Returns a new DataSet with a 'cherab_grid' attribute.
If called then the `cherab` package must be available.
"""
# Import here so Cherab is required only if this method is called
from .cherab import grid

return grid.ds_with_cherab_grid(self.data)


def _find_major_vars(data):
"""
Expand Down
Empty file added xbout/cherab/__init__.py
Empty file.
93 changes: 93 additions & 0 deletions xbout/cherab/grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import numpy as np
import xarray as xr

from .triangulate import Triangulate


def da_with_cherab_grid(da):
"""
Convert an BOUT++ DataArray to a format that Cherab can use:
- A 'cell_number' coordinate
- A 'cherab_grid` attribute
The cell_number coordinate enables the DataArray to be sliced
before input to Cherab.
Parameters
----------
ds : xarray.DataArray
Returns
-------
updated_da
"""
if "cherab_grid" in da.attrs:
# Already has required attribute
return da

if da.attrs["geometry"] != "toroidal":
raise ValueError("xhermes.plotting.cherab: Geometry must be toroidal")

if da.sizes["zeta"] != 1:
raise ValueError("xhermes.plotting.cherab: Zeta index must have size 1")

nx = da.sizes["x"]
ny = da.sizes["theta"]

# Cell corners
rm = np.stack(
(
da.coords["Rxy_upper_right_corners"],
da.coords["Rxy_upper_left_corners"],
da.coords["Rxy_lower_right_corners"],
da.coords["Rxy_lower_left_corners"],
),
axis=-1,
)
zm = np.stack(
(
da.coords["Zxy_upper_right_corners"],
da.coords["Zxy_upper_left_corners"],
da.coords["Zxy_lower_right_corners"],
da.coords["Zxy_lower_left_corners"],
),
axis=-1,
)

grid = Triangulate(rm, zm)

# Store the cell number as a coordinate.
# This allows slicing of arrays before passing to Cherab

# Create a DataArray for the vertices and triangles
cell_number = xr.DataArray(
grid.cell_number, dims=["x", "theta"], name="cell_number"
)

result = da.assign_coords(cell_number=cell_number)
result.attrs.update(cherab_grid=grid)
return result


def ds_with_cherab_grid(ds):
"""
Create an xarray DataSet with a Cherab grid
Parameters
----------
ds : xarray.Dataset
Returns
-------
updated_ds
"""

# The same operation works on a DataSet
ds = da_with_cherab_grid(ds)

# Add the Cherab grid as an attribute to all variables
grid = ds.attrs["cherab_grid"]
for var in ds.data_vars:
ds[var].attrs.update(cherab_grid=grid)

return ds
Loading

0 comments on commit d113e0e

Please sign in to comment.