Skip to content
Projects

beamtracer.field_map

Gridded external fields read from and written to xarray/netCDF field maps.

A field map holds b_field_<i> (T) and/or e_field_<i> (V/m) on node coordinates x, y, z (m): magnet and coil maps, or fields computed offline on fitted meshes (NGSolve, FEniCSx). FieldMap reads and writes one, checks it with validate_field_map and interpolates it multilinearly to any position. It gives

  • beamtracer.model.BeamOptics an imposed E and/or B, added to the solved field and the solenoids (field_map: {file: magnets.nc} in its configuration, beamtracer.config.FieldMapParams);
  • beamtracer.beamline.FieldRegion its electric and magnetic fields (FieldRegion(..., magnetic=FieldMap.read("stray.nc"))).

The format is shared. It is the field-map layout of the max-models output format, Conventions = "max-models-field-map-1.0", whose specification and reference validator live in picnic (picnic.input_output.output_format: the module docstring and validate_field_map; picnic reads and writes maps with picnic.parameters.field_map.FieldMap). beamtracer does not depend on picnic, so this module implements the same layout and the same checks, and a map written by either code is read by the other. A shared home for the specification and the validator (a module of an existing package or a small interfaces package) is still to be decided; until then, a change to the layout is made in both places.

The layout:

  • dims an ordered subset of (x, y, z), each with an increasing coordinate in m; a map may have fewer dims than the positions it is evaluated at and is then uniform along the others (a 1D profile B_z(x) in a 3D region);
  • variables b_field_<i> (units = "T") and/or e_field_<i> (units = "V/m"), <i> in x, y, z, each on all the map’s dims in that order;
  • attributes Conventions (FIELD_MAP_FORMAT), frame ("cartesian", or "cylindrical" for the axisymmetric (x, r) plane: x axial, y the radius, and here the z component the azimuthal one, x, r, theta right-handed), optionally frame_axes (the dims in the device frame of the spokes3d conventions, see parse_frame_axes) and source (what produced it).

Outside the map the field is that of the nearest boundary node (outside="clip"), zero ("zero") or an error ("error")::

field_map = FieldMap.from_function(
lambda p: np.stack([0 * p[:, 0], 0 * p[:, 0], 0.01 * np.exp(-p[:, 0] ** 2 / 1e-4)], 1),
{"x": np.linspace(0, 0.05, 101)},
kind="magnetic",
source="analytic",
)
field_map.write("magnets.nc")

Attributes

NameDescription
FIELD_MAP_FORMATNo description.
FRAMESNo description.

Classes

NameDescription
FieldMapA gridded external field in the shared field-map layout, interpolated to positions.
FieldMapErrorA dataset does not follow the field-map layout.

Functions

NameDescription
parse_frame_axesParse a frame_axes attribute such as "x=z y=-s z=r".
validate_field_mapWhat ds gets wrong against the field-map layout (picnic's checks).

FIELD_MAP_FORMATattributemodule attribute#

FIELD_MAP_FORMAT = 'max-models-field-map-1.0'

FRAMESattributemodule attribute#

FRAMES = ('cartesian', 'cylindrical')

FieldMapclass#

class FieldMap(dataset: xr.Dataset, path: str | Path | None = None, outside: str = 'clip')

A gridded external field in the shared field-map layout, interpolated to positions.

Attributes

NameDescription
datasetThe map, checked with validate_field_map.
pathThe file it was read from or written to, if any.
outsideWhat the field is outside the map: "clip" (the nearest boundary node’s), "zero" or "error".
dimsThe map’s dims, an ordered subset of ("x", "y", "z").

Numerics

multilinear

F(x)=∑c∈{0,1}dFi+c∏k=1d(ckwk+(1−ck)(1−wk)),wk=xk−xk,ikxk,ik+1−xk,ikF(\mathbf x) = \sum_{\mathbf c \in \{0, 1\}^d} F_{\mathbf i + \mathbf c} \prod_{k=1}^{d} \left(c_k w_k + (1 - c_k)(1 - w_k)\right), \qquad w_k = \frac{x_k - x_{k, i_k}}{x_{k, i_k + 1} - x_{k, i_k}}

outside

F(x)=F(clip⁡(x)) (clip),0 (zero)F(\mathbf x) = F(\operatorname{clip}(\mathbf x)) \ \text{(clip)}, \quad 0 \ \text{(zero)}

Order of accuracy: 2.

Assumptions:

  • each component on the nodes of a tensor-product mesh of d≤3d \le 3 increasing (not necessarily uniform) coordinates
  • a map with fewer dims than the positions is uniform along the missing axes
  • the order is in the node spacing, for a smooth field

datasetattributeinstance attribute#

dataset = dataset

dimsattributeinstance attribute#

dims = tuple((dim for dim in AXES if dim in dataset.dims))

outsideattributeinstance attribute#

outside = outside

pathattributeinstance attribute#

path = None if path is None else Path(path)

frameproperty#

frame: str

The frame attribute: "cartesian" or "cylindrical".

componentsmethod#

def components(kind: str) -> tuple[str, ...]

The names of the components of kind in the map, e.g. ("b_field_z",).

Parameters

NameTypeDescription
kindstr"magnetic" or "electric".

electric_fieldmethod#

def electric_field(positions: Any) -> np.ndarray

E (V/m) at positions (N, ndim), as (N, ndim).

evaluatemethod#

def evaluate(coordinates: tuple[Any, ...], kind: str = 'magnetic') -> np.ndarray

The field on a mesh, (*mesh, num_components).

Parameters

NameTypeDefaultDescription
coordinatestuple[Any, ...]requiredThe (up to three) indexing="ij" coordinate arrays in m.
kindstr'magnetic'"magnetic" (three components) or "electric" (one per coordinate).

fieldmethod#

def field(kind: str, positions: Any, num_components: int) -> np.ndarray

The field of kind at positions, num_components of it.

Parameters

NameTypeDescription
kindstr"magnetic" or "electric".
positionsAny(N, ndim) in m, the columns x, y, z.
num_componentsintThe components returned, x, y, ...; those not in the map are zero.

Returns

np.ndarray
(N, num_components) in T or V/m.

from_arraysmethodclassmethod#

def from_arrays(coordinates: Mapping[str, Any], components: Mapping[str, Any], frame: str = 'cartesian', frame_axes: str | None = None, source: str = '', outside: str = 'clip') -> FieldMap

Build a map from node coordinates and component arrays.

Parameters

NameTypeDefaultDescription
coordinatesMapping[str, Any]required{"x": nodes, ...} in m, increasing.
componentsMapping[str, Any]required{"b_field_z": values, ...} (T or V/m), each of the shape of the mesh of coordinates (in the order x, y, z).
framestr'cartesian'The frame attribute.
frame_axesstr | NoneNoneThe frame_axes attribute, if any.
sourcestr''What produced the map (a script, a solver and its mesh, a measurement).
outsidestr'clip'See FieldMap.

from_functionmethodclassmethod#

def from_function(function: Callable[[np.ndarray], np.ndarray], coordinates: Mapping[str, Any], kind: str = 'magnetic', **kwargs: Any) -> FieldMap

Sample a field function on the mesh of coordinates.

Parameters

NameTypeDefaultDescription
functionCallable[[np.ndarray], np.ndarray]requiredThe field at positions (N, ndim) (m), as (N, num_components), the components x, y, ... (beyond the third ignored).
coordinatesMapping[str, Any]required{"x": nodes, ...} in m.
kindstr'magnetic'"magnetic" (b_field_*) or "electric" (e_field_*).
**kwargsAny{}Passed to from_arrays.

magnetic_fieldmethod#

def magnetic_field(positions: Any) -> np.ndarray

B (T) at positions (N, ndim), as (N, 3).

plane_functionmethod#

def plane_function(kind: str) -> Callable[[np.ndarray, np.ndarray], tuple[np.ndarray, ...]]

The field of kind as F(x, y) -> (F_x, F_y, F_z) on a 2D plane.

The form of beamtracer.solver.BeamTracer’s imposed fields: (x, y) planar, or (x, r) -> (F_x, F_r, F_theta) in a "cylindrical" map.

points_functionmethod#

def points_function(kind: str) -> Callable[[np.ndarray], np.ndarray]

The field of kind as F(points), (3, N) at (3, N) points.

The form of beamtracer.beamline.FieldRegion’s fields, in a "cartesian" map.

readmethodclassmethod#

def read(path: str | Path, outside: str = 'clip') -> FieldMap

Read a field map from a netCDF file (loaded into memory).

Parameters

NameTypeDefaultDescription
pathstr | PathrequiredThe file.
outsidestr'clip'See FieldMap.

writemethod#

def write(path: str | Path, **kwargs: Any) -> Path

Write the map to the netCDF file path and remember it there.

Parameters

NameTypeDefaultDescription
pathstr | PathrequiredThe file.
**kwargsAny{}Passed to xarray.Dataset.to_netcdf.

FieldMapErrorclass#

class FieldMapError(ValueError)

Bases: ValueError

A dataset does not follow the field-map layout.

parse_frame_axesfunction#

def parse_frame_axes(text: str) -> dict[str, tuple[int, str]]

Parse a frame_axes attribute such as "x=z y=-s z=r".

Space-separated <dim>=[-]<device axis> items; the device axes are r, theta (or s, the arc length along it) and z of the spokes3d conventions (z normal to the target into the plasma).

Parameters

NameTypeDescription
textstrThe attribute.

Returns

dict[str, tuple[int, str]]
{dim: (sign, device axis)}, with s reported as theta.

Raises

FieldMapError
On a malformed item, a repeated dim or axis, or three mapped axes that are not right-handed.

validate_field_mapfunction#

def validate_field_map(ds: xr.Dataset) -> list[str]

What ds gets wrong against the field-map layout (picnic’s checks).

Parameters

NameTypeDescription
dsxr.DatasetA field map: b_field_<i> and/or e_field_<i> on (x[, y[, z]]).

Returns

list[str]
One message per problem; empty when the map follows the layout.