plasma_plots.arrays
Small xarray metadata helpers used by plasma_plots.
They intentionally live here rather than in Struphy so the plotting package can operate on labeled xarray data from any producer.
Attributes
| Name | Description |
|---|---|
DIM_LABELS | No description. |
LOGICAL_DIMS | No description. |
SCALARS_EXCLUDE | No description. |
Functions
| Name | Description |
|---|---|
angle_period | The period of an angle coordinate, from its period attribute. |
axis_label | The axis label of a dimension, with its units. |
close_periodic | Repeat the first slice at the end of every direction that wraps around in physical space. |
logical_derivative | The derivative ∂ values / ∂ coordinate along axis. |
logical_dims | The names of the three logical dimensions of data, radial first. |
map_coordinate | Replace a coordinate by a function of it, e.g. a logical one by a physical length. |
mapping_jacobian | The Jacobian J[a, i] = ∂X_a / ∂η_i of the mapping, shape (3, 3, n1, n2, n3). |
periodicity | How a direction of a (..., 3) array of physical points wraps around, if it does. |
save_scalars | Save scalar time series to a CSV or NPZ file; under MPI only rank 0 writes it. |
scalar_names | The names of the scalar time series to use from a collection. |
validate_array | Check that data is an xarray.DataArray with the required dimensions. |
value_label | The label of an array's values, with their units. |
DIM_LABELSattributemodule attribute#
DIM_LABELS = {
't': '$t$',
'eta1': '$\\eta_1$',
'eta2': '$\\eta_2$',
'eta3': '$\\eta_3$',
'v1': '$v_1$',
'v2': '$v_2$',
'v3': '$v_3$',
'x': '$x$',
'y': '$y$',
'z': '$z$',
'R': '$R$',
'Z': '$Z$',
'component': 'component',
'marker': 'marker',
'quantity': 'quantity',
'rho': '$\\rho$',
'theta': '$\\theta$',
'zeta': '$\\zeta$',
'theta_B': '$\\theta_B$',
'zeta_B': '$\\zeta_B$',
'theta_P': '$\\theta_P$'
}LOGICAL_DIMSattributemodule attribute#
LOGICAL_DIMS = (
('eta1', 'eta2', 'eta3'),
('rho', 'theta', 'zeta'),
('rho', 'theta_B', 'zeta_B'),
('rho', 'theta_P', 'zeta')
)SCALARS_EXCLUDEattributemodule attribute#
SCALARS_EXCLUDE = ('time')angle_periodfunction#
def angle_period(data: xr.DataArray | xr.Dataset, dim: str) -> float | NoneThe period of an angle coordinate, from its period attribute.
GVEC’s angles carry one (2π for the poloidal angles, 2π/nfp for the toroidal ones, see
plasma_plots.gvec.from_gvec()); Struphy’s logical coordinates have none.
Parameters
| Name | Type | Description |
|---|---|---|
data | xarray.DataArray or xarray.Dataset | The field. |
dim | str | One of its coordinates. |
axis_labelfunction#
def axis_label(data: xr.DataArray, dim: str) -> strThe axis label of a dimension, with its units.
The label is the coordinate’s label attribute, else its long_name, else a default for
the known dimensions (matplotlib mathtext such as $\eta_1$ for eta1), else the
dimension name.
Parameters
| Name | Type | Description |
|---|---|---|
data | xarray.DataArray | The array. |
dim | str | One of its dimensions. |
Returns
str- The label, followed by
[units]when the coordinate has aunitsattribute.
Raises
KeyError- If
dimis not a dimension ofdata.
close_periodicfunction#
def close_periodic(data: xr.DataArray, dims=None) -> xr.DataArrayRepeat the first slice at the end of every direction that wraps around in physical space.
Struphy evaluates fields at cell centers, which leave out the seam of a periodic direction
(e.g. the poloidal angle), so a surface or pcolormesh drawn through the points has a gap
there. A direction counts as periodic when its last slice lies about one grid step from its
first one ("open" in periodicity()); a radius, or the ends of a torus sector, do
not. The repeated slice gets the logical coordinate one step past the last.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
data | xarray.DataArray | required | The field, with the physical X, Y, Z coordinates; without them data
comes back unchanged. |
dims | sequence of str | None | The directions to close, if periodic. Default: those of the logical dimensions (see
logical_dims()) that data has. |
Returns
xarray.DataArraydata, one point longer along each periodic direction indims.
logical_derivativefunction#
def logical_derivative(values: np.ndarray, coordinate: np.ndarray, axis: int, kind: str | None) -> np.ndarrayThe derivative ∂ values / ∂ coordinate along axis.
Around a periodic direction on a uniform grid (kind "open" or "closed", see
periodicity()) the derivative is spectral (an FFT), which is exact for the smooth,
periodic angles of Struphy’s mappings; the Nyquist mode of an even number of points is
dropped. Elsewhere numpy’s second-order differences (one-sided at the ends; first order with
only two points).
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
values | numpy.ndarray | required | The values to differentiate. |
coordinate | numpy.ndarray | required | The coordinate along axis, one value per point. |
axis | int | required | The axis of values to differentiate along. |
kind | ('open', 'closed', None) | "open" | How the direction wraps around, from periodicity(): "open" (the seam is left
out), "closed" (the last point repeats the first) or None (not periodic). |
Returns
numpy.ndarray- The derivative, of the same shape as
values.
logical_dimsfunction#
def logical_dims(data: xr.DataArray | xr.Dataset) -> tuple[str, str, str]The names of the three logical dimensions of data, radial first.
One of LOGICAL_DIMS: the one with the most of its names among the dimensions of
data (then among its coordinates, for a slice that selected some away), Struphy’s
("eta1", "eta2", "eta3") when none match.
Parameters
| Name | Type | Description |
|---|---|---|
data | xarray.DataArray or xarray.Dataset | The field. |
Returns
tuple of str- Three dimension names, e.g.
("eta1", "eta2", "eta3")or("rho", "theta_B", "zeta_B");dataneed not have all three.
Examples
>>> logical_dims(phi) # Struphy('eta1', 'eta2', 'eta3')>>> # GVEC, Boozer>>> logical_dims(ev.mod_B.plasma.data.slice(x="zeta_B", y="theta_B", rho=0.5))('rho', 'theta_B', 'zeta_B')map_coordinatefunction#
def map_coordinate(data: xr.DataArray, dim: str, mapping, *, name: str | None = None, units: str | None = None, label: str | None = None) -> xr.DataArrayReplace a coordinate by a function of it, e.g. a logical one by a physical length.
Every plot then draws and labels the new coordinate, e.g. the minor radius
r = a1 + (a2 - a1) * eta1 in meters instead of eta1. With name, the dimension is
renamed too, so r is what selections (r=0.5) and plots (x="r") name; without it,
dim keeps its name and only its values, label and units change.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
data | xarray.DataArray | required | The array. |
dim | str | required | The dimension whose coordinate is mapped, e.g. "eta1". |
mapping | callable or float | required | A function of the coordinate’s values (lambda eta1: 0.1 + 0.9 * eta1), or a factor
(a length, for length * eta1). |
name | str | None | The name of the new dimension, e.g. "r". Default: keep dim. |
units | str | None | The new coordinate’s units, e.g. "m". Default: none. |
label | str | None | The new coordinate’s axis label (its long_name), mathtext allowed. Default: name. |
Returns
xarray.DataArray- The array over the new coordinate; the data itself is unchanged.
Raises
KeyError- If
dimis not a dimension ofdata.
Examples
>>> map_coordinate(... T, "eta1", lambda eta1: 0.1 + 0.9 * eta1, name="r", units="m"... ).plasma.plot.lineout(x="r", t=-1)>>> # a length of 2π along eta1>>> map_coordinate(n, "eta1", 2 * np.pi, units="m")mapping_jacobianfunction#
def mapping_jacobian(data: xr.DataArray) -> np.ndarrayThe Jacobian J[a, i] = ∂X_a / ∂η_i of the mapping, shape (3, 3, n1, n2, n3).
Differentiated numerically from the X, Y, Z coordinates of data on its
logical grid ((eta1, eta2, eta3), or GVEC’s, see logical_dims()): spectrally around periodic directions (see periodicity()),
second order elsewhere (see logical_derivative()).
Parameters
| Name | Type | Description |
|---|---|---|
data | xarray.DataArray | An array with the three logical dimensions, each with at least two points, and the
X, Y, Z coordinates over them. |
Returns
numpy.ndarrayJof shape(3, 3, n1, n2, n3): the Cartesian componentafirst, the logical directionisecond.
Raises
ValueError- If a logical dimension is missing or has fewer than two points (pass a struphy domain to the calling function instead).
periodicityfunction#
def periodicity(points: np.ndarray, axis: int) -> str | NoneHow a direction of a (..., 3) array of physical points wraps around, if it does.
The direction is "closed" when its last slice coincides with its first (to 1e-9 of the
median step), and "open" when the seam between them is at most 1.5 times the last grid
step everywhere.
Parameters
| Name | Type | Description |
|---|---|---|
points | numpy.ndarray | Physical points, with the Cartesian coordinates (X, Y, Z) along the last axis. |
axis | int | The axis of points to examine; it needs at least three points to be periodic. |
Returns
{'closed', 'open', None}"closed"(the last slice repeats the first),"open"(it stops one step short of the seam, as on Struphy’s cell centers), orNone(not periodic, e.g. a radius or the ends of a torus sector).
save_scalarsfunction#
def save_scalars(scalars: xr.Dataset | Mapping, path: str, *, names=None, exclude=SCALARS_EXCLUDE, fmt=None) -> strSave scalar time series to a CSV or NPZ file; under MPI only rank 0 writes it.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
scalars | xarray.Dataset or mapping | required | The scalars, e.g. out.scalars. Each selected one must have t as its only
dimension, with identical time coordinates. |
path | str | required | The file to write. |
names | sequence of str | None | The scalars to save. Default: every one not in exclude; see scalar_names(). |
exclude | sequence of str | SCALARS_EXCLUDE | Names left out when names is not given. Default: ("time",). |
fmt | ('csv', 'npz') | "csv" | The file format. Default: from the extension of path, else "csv". A CSV has a
header line t,<names> and one row per time; an NPZ has the arrays t and one per
name. |
Returns
strpath, on every rank.
Raises
ValueError- If a scalar has dimensions other than
t, the time coordinates differ, or the format is unknown. KeyError- If one of
namesis not inscalars.
Examples
>>> save_scalars(out.scalars, "scalars.csv", names=["en_E", "en_tot"])scalar_namesfunction#
def scalar_names(scalars: xr.Dataset | Mapping, *, names=None, exclude=SCALARS_EXCLUDE) -> list[str]The names of the scalar time series to use from a collection.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
scalars | xarray.Dataset or mapping | required | The scalars, e.g. out.scalars. |
names | sequence of str | None | Explicit names, which must all be present; exclude then does not apply. Default:
every variable not in exclude. |
exclude | sequence of str | SCALARS_EXCLUDE | Names left out when names is not given. Default: ("time",). |
Returns
list of str- The selected names, in order.
Raises
KeyError- If one of
namesis not inscalars.
validate_arrayfunction#
def validate_array(data: xr.DataArray, *, required_dims: Sequence[str] = ()) -> xr.DataArrayCheck that data is an xarray.DataArray with the required dimensions.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
data | xarray.DataArray | required | The array to check. |
required_dims | sequence of str | () | Dimensions data must have. Default: none. |
Returns
xarray.DataArraydataitself, unchanged.
Raises
TypeError- If
datais not an xarray.DataArray. ValueError- If one of
required_dimsis missing.
value_labelfunction#
def value_label(data: xr.DataArray) -> strThe label of an array’s values, with their units.
Parameters
| Name | Type | Description |
|---|---|---|
data | xarray.DataArray | The array. The label is its label attribute, else long_name, else its name. |
Returns
str"label [units]", with theunitsattribute ora.u.; only"[units]"when there is no label.