Source code for fdsreader.fds_classes.mesh

from typing import Dict, Tuple, Sequence, List, Union
from typing_extensions import Literal
import numpy as np

from fdsreader import settings
from fdsreader.utils import Dimension, Extent, Quantity
from fdsreader.bndf import Boundary, Patch


[docs] class Mesh: """3-dimensional Mesh of fixed, defined size. :ivar coordinates: Coordinate values for each of the 3 dimension. :ivar dimensions: :class:`Dimension` describing the size of the 3 dimensions regarding indices. :ivar extent: :class:`Extent` object containing 3-dimensional extent information. :ivar n: Number of elements for each of the 3 dimensions. :ivar n_size: Total number of blocks in this mesh. :var id: Mesh id/short_name assigned to this mesh. """ def __init__(self, coordinates: Dict[Literal['x', 'y', 'z'], np.ndarray], extents: Dict[Literal['x', 'y', 'z'], Tuple[float, float]], mesh_id: str): """ :param coordinates: Coordinate values of the three axes. :param extents: Extent of the mesh in each dimension. :param mesh_id: ID of this mesh. """ self.id = mesh_id self.coordinates = coordinates self.dimension = Dimension( coordinates['x'].size if coordinates['x'].size > 0 else 1, coordinates['y'].size if coordinates['y'].size > 0 else 1, coordinates['z'].size if coordinates['z'].size > 0 else 1) self.n_size = self.dimension.size() self.extent = Extent(extents['x'][0], extents['x'][1], extents['y'][0], extents['y'][1], extents['z'][0], extents['z'][1]) self.obstructions = list() self._boundary_data: Dict[int, Boundary] = dict()
[docs] def get_obstruction_mask(self, times: Sequence[float], cell_centered=False) -> np.ndarray: """Marks all cells which are blocked by an obstruction. :param times: All timesteps of the simulation. :returns: A 4-dimensional array with time as first and x,y,z as last dimensions. The array depends on time as obstructions may be hidden as specific points in time. """ shape = self.dimension.shape(cell_centered=cell_centered) mask = np.ones((len(times), shape[0], shape[1], shape[2]), dtype=bool) c = 1 if cell_centered else 0 for obst in self.obstructions: subobst = obst[self] x1, x2 = subobst.bound_indices['x'] y1, y2 = subobst.bound_indices['y'] z1, z2 = subobst.bound_indices['z'] t_idx = 0 for t in subobst.get_visible_times(times): while not np.isclose(t, times[t_idx]): t_idx += 1 mask[t_idx, x1:max(x2 + c, x1 + 1), y1:max(y2 + c, y1 + 1), z1:max(z2 + c, z1 + 1)] = False return mask
[docs] def get_obstruction_mask_slice(self, subslice): """Marks all cells of a single subslice which are blocked by an obstruction. :returns: A 4-dimensional array with time as first and x,y,z as last dimensions. The array depends on time as obstructions may be hidden at specific points in time. """ orientation = subslice.orientation value = subslice.extent[orientation][0] cell_centered = subslice.cell_centered slc_index = self.coordinate_to_index((value,), dimension=(orientation,), cell_centered=cell_centered)[0] mask_indices = [slice(None)] * 4 mask_indices[orientation] = slice(slc_index, slc_index + 1, 1) mask_indices = tuple(mask_indices) return self.get_obstruction_mask(subslice.times, cell_centered=cell_centered)[mask_indices]
[docs] def coordinate_to_index(self, coordinate: Tuple[float, ...], dimension: Tuple[Literal[1, 2, 3, 'x', 'y', 'z'], ...] = ('x', 'y', 'z'), cell_centered=False) -> Tuple[int, ...]: """Finds the nearest point in the mesh's grid and returns its indices. :param coordinate: Tuple of 3 floats. If the dimension parameter is supplied, up to 2 dimensions can be left out from the tuple. :param dimension: The dimensions in which to return the indices (1=x, 2=y, 3=z). :param cell_centered: Instead of finding the nearest point on the mesh, find the center of the nearest cell. """ # Convert possible integer input to chars dimension = tuple(('x', 'y', 'z')[dim - 1] if type(dim) == int else dim for dim in dimension) ret = list() for i, dim in enumerate(dimension): co = coordinate[i] coords = self.coordinates[dim] if cell_centered: coords = coords[:-1] + (coords[1] - coords[0]) / 2 idx = np.searchsorted(coords, co, side="left") if co > 0 and (idx == len(coords) or np.math.fabs(co - coords[idx - 1]) < np.math.fabs( co - coords[idx])): ret.append(idx - 1) else: ret.append(idx) return tuple(ret)
[docs] def get_nearest_coordinate(self, coordinate: Tuple[float, ...], dimension: Tuple[Literal[1, 2, 3, 'x', 'y', 'z'], ...] = ('x', 'y', 'z'), cell_centered=False) -> Tuple[float, ...]: """Finds the nearest point in the mesh's grid. :param coordinate: Tuple of 3 floats. If the dimension parameter is supplied, up to 2 dimensions can be left out from the tuple. :param dimension: The dimensions in which to return the indices (1=x, 2=y, 3=z). :param cell_centered: Instead of finding the nearest point on the mesh, find the center of the nearest cell. """ indices = self.coordinate_to_index(coordinate, dimension, cell_centered) ret = list() for i, dim in enumerate(dimension): coords = self.coordinates[dim] if cell_centered: coords = coords[:-1] + (coords[1] - coords[0]) / 2 ret.append(coords[indices[i]]) return tuple(ret)
def _add_patches(self, bid: int, cell_centered: bool, quantity: str, short_name: str, unit: str, patches: List[Patch], times: np.ndarray, lower_bounds: np.ndarray, upper_bounds: np.ndarray): if bid not in self._boundary_data: self._boundary_data[bid] = Boundary(Quantity(quantity, short_name, unit), cell_centered, times, patches, lower_bounds, upper_bounds) # Add reference to parent boundary class in patches for patch in patches: patch._boundary_parent = self._boundary_data[bid] if not settings.LAZY_LOAD: _ = self._boundary_data[bid].data
[docs] def get_boundary_data(self, quantity: Union[str, Quantity]): if type(quantity) == Quantity: quantity = quantity.name return next(b for b in self._boundary_data.values() if b.quantity.name.lower() == quantity.lower() or b.quantity.short_name.lower() == quantity.lower())
def __getitem__(self, dimension: Literal[0, 1, 2, 'x', 'y', 'z']) -> np.ndarray: """Get all values in given dimension. :param dimension: The dimension in which to return all grid values (0=x, 1=y, 2=z). """ # Convert possible integer input to chars if type(dimension) == int: dimension = ('x', 'y', 'z')[dimension] return self.coordinates[dimension] def __eq__(self, other): return self.id == other.id def __repr__(self, *args, **kwargs): return f'Mesh(id="{self.id}", extent={str(self.extent)}, dimension={str(self.dimension)})'