Source code for torx.analysis.lineouts.lineout_set_m

"""Defines 3D Lineouts made up of collections of 2D Lineouts."""
from abc import ABC
import numpy as np
import xarray as xr
import dask.bag as db
from functools import cached_property
from typing import Union

from torx.arrays import combine_obj_list
from torx.units import check_units
from torx.decorators import autodoc_class
from torx.grid import Grid2D, Grid3D

[docs] @autodoc_class class LineoutSet(ABC): """Groups a set of 2D lineouts for a given dimension. Defaults to phi."""
[docs] def __init__(self, lineout_array: xr.DataArray, dim_name: str = 'phi'): """Initialize the 3D lineout.""" self.lineout_array = lineout_array self.coords = lineout_array.coords self.dims = lineout_array.dims self.attrs = lineout_array.attrs self.name = 'LineoutSet' self.encoding = lineout_array.encoding self.sizes = lineout_array.sizes self.dim_name = dim_name self.len = lineout_array.sizes[self.dim_name] self.lineout_db = db.from_sequence( [lineout_array.isel( {self.dim_name: i} ).item() for i in range(self.len)], npartitions=self.len )
@property def lineout_array(self): """Return the arrays of values in the grid.""" return self._lineout_array @lineout_array.setter def lineout_array(self, value): """Set the arrays of values in the lineout.""" self._lineout_array = value @cached_property def r_source(self) -> xr.DataArray: """Source R coordinates for the lineout (normalized to R0).""" return combine_obj_list(self.lineout_array.values, "r_source", self.dim_name, self.lineout_array.coords[self.dim_name].values ).rename({"dim_0": "points"}) @cached_property def z_source(self) -> xr.DataArray: """Source Z coordinates for the lineout (normalized to R0).""" return combine_obj_list(self.lineout_array.values, "z_source", self.dim_name, self.lineout_array.coords[self.dim_name].values ).rename({"dim_0": "points"}) @cached_property def r_points(self) -> xr.DataArray: """R coordinates for the lineout (normalized to R0).""" try: return combine_obj_list(self.lineout_array.values, "r_points", self.dim_name, self.lineout_array.coords[self.dim_name].values ).rename({"dim_0": "points"}) except AttributeError as e: msg = "r_points are not defined. "+\ "You likely need to run find_points_on_grid first." raise AttributeError(msg) from e @cached_property def z_points(self) -> xr.DataArray: """Z coordinates for the lineout (normalized to R0).""" try: return combine_obj_list(self.lineout_array.values, "z_points", self.dim_name, self.lineout_array.coords[self.dim_name].values ).rename({"dim_0": "points"}) except AttributeError as e: msg = "z_points are not defined. "+\ "You likely need to run find_points_on_grid first." raise AttributeError(msg) from e @cached_property def is_periodic(self): """Determine whether all lineouts are periodic, i.e. a closed polygon.""" return all( self.lineout_db.map(lambda lineout: lineout.is_periodic ).compute())
[docs] def find_points_on_grid(self, grid: Union[Grid2D, Grid3D], n_samples: int=500): """Run find_points_on_grid for each lineout in parallel using Dask.""" self.n_samples = n_samples if isinstance(grid, Grid3D): self.is_3D = True grid_db = db.from_sequence( [ grid.grid_array.isel({self.dim_name: i}).item() \ for i in range(len(grid.grid_array)) ], npartitions=self.len ) # Zip delivers a (lineout, grid_slice) tuple to the helper updated_lineouts = db.zip( self.lineout_db, grid_db ).map(self._find_points_helper).compute() elif isinstance(grid, Grid2D): self.is_3D = False # Pass the single grid as a keyword argument to the helper updated_lineouts = self.lineout_db.map(self._find_points_helper, grid_2d=grid).compute() else: raise TypeError(f"Expected Grid3D or Grid2D, got {type(grid)}") # Update the saved lineout xarray and dask bag self.lineout_array.values = np.array(updated_lineouts) self.lineout_db = db.from_sequence( self.lineout_array.values, npartitions=self.len )
[docs] def interpolate(self, input_array: xr.DataArray) -> xr.DataArray: """Interpolates the input array using each lineout for a given dimension.""" if not hasattr(self, 'is_3D'): msg = "is_3D is not defined. Likely must run find_points_on_grid." raise AttributeError(msg) if self.is_3D: input_array_db = db.from_sequence( [input_array.isel({self.dim_name: i}) for i in range(self.len)], npartitions=self.len ) zipped_bag = db.zip(self.lineout_db, input_array_db) interpolated_bag = zipped_bag.map( lambda p: p[0].interpolate( p[1].isel(points=slice(None, p[0].grid_size)) ) ).compute() else: interpolated_bag = self.lineout_db.map( lambda lineout: lineout.interpolate(input_array) ).compute() return xr.concat( interpolated_bag, dim=self.dim_name ).assign_coords({self.dim_name: self.lineout_array.coords[self.dim_name]})
def _find_points_helper(self, data, grid_2d=None): """Call find_points_on_grid for a single lineout on a 2D or 3D grid.""" # If it's a tuple (from db.zip in 3D), unpack it if isinstance(data, tuple): lineout, grid = data else: # If it's just the lineout (2D), use the static grid provided lineout = data grid = grid_2d lineout.find_points_on_grid(grid, n_samples=self.n_samples) return lineout