Source code for fdsreader.fds_classes.mesh

import math
from typing import Dict, List, Sequence, Tuple, Union

import numpy as np
from typing_extensions import Literal

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


[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 isinstance(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 math.fabs(co - coords[idx - 1]) < 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 isinstance(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 isinstance(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)})'