Source code for mrfmsim.component.grid

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
from mrfmsim.component import ComponentBase
from dataclasses import dataclass, field


[docs]@dataclass class Grid(ComponentBase): """Instantiate a rectangular grid with shape, step, and origin. The resulting grid has equal spacing in each dimension. The grid array uses numpy's open mesh-grid, which has speed and storage benefits. :param np.array length: array of lengths along (x, y, z) :param np.array step: a list of step sizes :param np.array origin: the grid origin :param tuple[int] shape: grid dimension (number of points in x, y, z direction) :param list step: grid setup size in x, y, z direction :param list origin: grid origin :param float voxel: the volume of each grid voxel :param np.array range: range in (x, y, z direction), shape (3, 2) :param np.array length: actual lengths of the grid. This is recalculated based on the rounded value of grid shape and step size. """ shape: tuple[int] step: list[float] = field(metadata={"unit": "nm"}) origin: list[float] = field(metadata={"unit": "nm"}) voxel: float = field(init=False, metadata={"unit": "nm^3"}) range: np.array = field(init=False, metadata={"unit": "nm"}) length: np.array = field(init=False, metadata={"unit": "nm"}) def __post_init__(self): """Calculate grid parameters.""" self.voxel = np.array(self.step).prod() self.range = (np.array(self.shape) - [1, 1, 1]) * self.step self.length = np.array(self.shape) * np.array(self.step) self.extents = self.grid_extents(self.range, self.origin)
[docs] @staticmethod def grid_extents(length, origin): """Calculate grid extents based on the grid length and origin. The result is column stacked into a dimension of (3, 2) """ return np.column_stack((-length / 2 + origin, length / 2 + origin))
@property def grid_array(self): """Generate an open mesh-grid of the given grid dimensions. The benefit of the property is that it generates the grid array at run time. """ # extents = self.grid_extents(self.range, self.origin) return np.ogrid[ self.extents[0][0] : self.extents[0][1] : self.shape[0] * 1j, self.extents[1][0] : self.extents[1][1] : self.shape[1] * 1j, self.extents[2][0] : self.extents[2][1] : self.shape[2] * 1j, ]
[docs] def extend_grid_by_points(self, ext_pts): """Extend the grid by the number of points in the x direction. :param int ext_pts: points (one side) to extend along x direction (cantilever motion direction). The points should be a list of three dimensions. """ ext_shape = self.shape + np.array(ext_pts) * 2 ext_range = (ext_shape - [1, 1, 1]) * self.step extents = self.grid_extents(ext_range, self.origin) return np.ogrid[ extents[0][0] : extents[0][1] : ext_shape[0] * 1j, extents[1][0] : extents[1][1] : ext_shape[1] * 1j, extents[2][0] : extents[2][1] : ext_shape[2] * 1j, ]
[docs] def extend_grid_by_length(self, ext_length): """Extend the grid by the number of points in the x direction. This is used to extend the grid by the cantilever motion. The length needs to be more than the step size to count. :param int ext_pts: distance (one side) to extend along x direction (cantilever motion direction). The length should be a list of three dimensions. """ pts = np.floor(np.array(ext_length) / self.step).astype(int) return self.extend_grid_by_points(pts)