Source code for torx.analysis.lineouts.lineout_set_m

"""Defines 3D Lineouts made up of collections of 2D Lineouts."""
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 AutoDocClass
from torx.grid import Grid2D, Grid3D

[docs] class LineoutSet(AutoDocClass): """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()) 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 ) 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