diff --git a/gprMax/cmds_geometry/add_grass.py b/gprMax/cmds_geometry/add_grass.py index 3fa7a9f9..256b22ab 100644 --- a/gprMax/cmds_geometry/add_grass.py +++ b/gprMax/cmds_geometry/add_grass.py @@ -64,7 +64,7 @@ class AddGrass(UserObjectGeometry): self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p2"] = tuple(rot_pts[1, :]) - def build(self, grid, uip): + def build(self, model, uip): """Add Grass to fractal box.""" try: p1 = self.kwargs["p1"] @@ -91,7 +91,7 @@ class AddGrass(UserObjectGeometry): self._do_rotate() # Get the correct fractal volume - volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id] + volumes = [volume for volume in model.fractalvolumes if volume.ID == fractal_box_id] try: volume = volumes[0] except NameError: @@ -103,7 +103,9 @@ class AddGrass(UserObjectGeometry): xf, yf, zf = p2 if frac_dim < 0: - logger.exception(f"{self.__str__()} requires a positive value for the fractal dimension") + logger.exception( + f"{self.__str__()} requires a positive value for the fractal dimension" + ) raise ValueError if limits[0] < 0 or limits[1] < 0: logger.exception( @@ -112,12 +114,15 @@ class AddGrass(UserObjectGeometry): raise ValueError # Check for valid orientations + grid = uip.grid if xs == xf: if ys == yf or zs == zf: logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if xs not in [volume.xs, volume.xf]: - logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box") + logger.exception( + f"{self.__str__()} must specify external surfaces on a fractal box" + ) raise ValueError fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx)) # xminus surface @@ -142,7 +147,9 @@ class AddGrass(UserObjectGeometry): logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if ys not in [volume.ys, volume.yf]: - logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box") + logger.exception( + f"{self.__str__()} must specify external surfaces on a fractal box" + ) raise ValueError fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy)) # yminus surface @@ -164,7 +171,9 @@ class AddGrass(UserObjectGeometry): elif zs == zf: if zs not in [volume.zs, volume.zf]: - logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box") + logger.exception( + f"{self.__str__()} must specify external surfaces on a fractal box" + ) raise ValueError fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz)) # zminus surface @@ -219,7 +228,8 @@ class AddGrass(UserObjectGeometry): # probability values, and convert the 1D index back into a x, y index # for the original surface. bladesindex = np.unravel_index( - np.digitize(A, probability1D), (surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]) + np.digitize(A, probability1D), + (surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]), ) # Set the fractal range to minimum and maximum heights of the grass blades @@ -227,7 +237,9 @@ class AddGrass(UserObjectGeometry): # Set the fractal surface using the pre-calculated spatial distribution # and a random height - surface.fractalsurface = np.zeros((surface.fractalsurface.shape[0], surface.fractalsurface.shape[1])) + surface.fractalsurface = np.zeros( + (surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]) + ) for i in range(len(bladesindex[0])): surface.fractalsurface[bladesindex[0][i], bladesindex[1][i]] = R.randint( surface.fractalrange[0], surface.fractalrange[1], size=1 @@ -238,11 +250,11 @@ class AddGrass(UserObjectGeometry): surface.grass.append(g) # Check to see if grass has been already defined as a material - if not any(x.ID == "grass" for x in grid.materials): - create_grass(grid) + if not any(x.ID == "grass" for x in model.materials): + create_grass(model) # Check if time step for model is suitable for using grass - grass = next((x for x in grid.materials if x.ID == "grass")) + grass = next((x for x in model.materials if x.ID == "grass")) testgrass = next((x for x in grass.tau if x < grid.dt), None) if testgrass: logger.exception( diff --git a/gprMax/fractals.py b/gprMax/fractals.py index 0e59f913..39b5048d 100644 --- a/gprMax/fractals.py +++ b/gprMax/fractals.py @@ -16,6 +16,8 @@ # You should have received a copy of the GNU General Public License # along with gprMax. If not, see . +from typing import Optional, Tuple + import numpy as np from scipy import fftpack @@ -32,7 +34,17 @@ class FractalSurface: surfaceIDs = ["xminus", "xplus", "yminus", "yplus", "zminus", "zplus"] - def __init__(self, xs, xf, ys, yf, zs, zf, dimension, seed): + def __init__( + self, + xs: float, + xf: float, + ys: float, + yf: float, + zs: float, + zf: float, + dimension: float, + seed: Optional[int] = None, + ): """ Args: xs, xf, ys, yf, zs, zf: floats for the extent of the fractal surface @@ -43,8 +55,9 @@ class FractalSurface: seed: int for seed value for random number generator. """ - self.ID = None - self.surfaceID = None + self.ID: str + self.surfaceID: str + self.operatingonID: str self.xs = xs self.xf = xf self.ys = ys @@ -56,9 +69,11 @@ class FractalSurface: self.nz = zf - zs self.dtype = np.dtype(np.complex128) self.seed = seed - self.dimension = dimension # Fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695 + self.dimension = ( + dimension # Fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695 + ) self.weighting = np.array([1, 1], dtype=np.float64) - self.fractalrange = (0, 0) + self.fractalrange: Tuple[int, int] = (0, 0) self.filldepth = 0 self.grass = [] @@ -82,7 +97,9 @@ class FractalSurface: self.fractalsurface = np.zeros(surfacedims, dtype=self.dtype) # Positional vector at centre of array, scaled by weighting - v1 = np.array([self.weighting[0] * (surfacedims[0]) / 2, self.weighting[1] * (surfacedims[1]) / 2]) + v1 = np.array( + [self.weighting[0] * (surfacedims[0]) / 2, self.weighting[1] * (surfacedims[1]) / 2] + ) # 2D array of random numbers to be convolved with the fractal function rng = np.random.default_rng(seed=self.seed) @@ -137,8 +154,8 @@ class FractalVolume: seed: int for seed value for random number generator. """ - self.ID = None - self.operatingonID = None + self.ID: str + self.operatingonID: str self.xs = xs self.xf = xf self.ys = ys @@ -157,7 +174,9 @@ class FractalVolume: self.averaging = False self.dtype = np.dtype(np.complex128) self.seed = seed - self.dimension = dimension # Fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695 + self.dimension = ( + dimension # Fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695 + ) self.weighting = np.array([1, 1, 1], dtype=np.float64) self.nbins = 0 self.fractalsurfaces = [] @@ -176,7 +195,9 @@ class FractalVolume: filterscaling = np.amin(np.array([self.nx, self.ny])) / np.array([self.nx, self.ny]) filterscaling = np.insert(filterscaling, 2, 1) else: - filterscaling = np.amin(np.array([self.nx, self.ny, self.nz])) / np.array([self.nx, self.ny, self.nz]) + filterscaling = np.amin(np.array([self.nx, self.ny, self.nz])) / np.array( + [self.nx, self.ny, self.nz] + ) # Adjust weighting to account for filter scaling self.weighting = np.multiply(self.weighting, filterscaling) @@ -185,7 +206,11 @@ class FractalVolume: # Positional vector at centre of array, scaled by weighting v1 = np.array( - [self.weighting[0] * self.nx / 2, self.weighting[1] * self.ny / 2, self.weighting[2] * self.nz / 2] + [ + self.weighting[0] * self.nx / 2, + self.weighting[1] * self.ny / 2, + self.weighting[2] * self.nz / 2, + ] ) # 3D array of random numbers to be convolved with the fractal function @@ -225,7 +250,9 @@ class FractalVolume: bins = np.linspace(np.amin(self.fractalvolume), np.amax(self.fractalvolume), self.nbins) for j in range(self.ny): for k in range(self.nz): - self.fractalvolume[:, j, k] = np.digitize(self.fractalvolume[:, j, k], bins, right=True) + self.fractalvolume[:, j, k] = np.digitize( + self.fractalvolume[:, j, k], bins, right=True + ) def generate_volume_mask(self): """Generate a 3D volume to use as a mask for adding rough surfaces, @@ -254,7 +281,9 @@ class Grass: """ self.numblades = numblades - self.geometryparams = np.zeros((self.numblades, 6), dtype=config.sim_config.dtypes["float_or_double"]) + self.geometryparams = np.zeros( + (self.numblades, 6), dtype=config.sim_config.dtypes["float_or_double"] + ) self.seed = seed self.set_geometry_parameters() diff --git a/gprMax/grid/fdtd_grid.py b/gprMax/grid/fdtd_grid.py index a1ce5368..3b411023 100644 --- a/gprMax/grid/fdtd_grid.py +++ b/gprMax/grid/fdtd_grid.py @@ -73,9 +73,7 @@ class FDTDGrid: # corrections will be different. self.pmls["thickness"] = OrderedDict((key, 10) for key in PML.boundaryIDs) - # TODO: Add type information. self.averagevolumeobjects = True - self.fractalvolumes = [] self.waveforms: List[Waveform] = [] self.voltagesources: List[VoltageSource] = [] self.hertziandipoles: List[HertzianDipole] = [] diff --git a/gprMax/model.py b/gprMax/model.py index d4f1ad5b..1f163b12 100644 --- a/gprMax/model.py +++ b/gprMax/model.py @@ -26,6 +26,7 @@ import numpy as np import psutil from colorama import Fore, Style, init +from gprMax.fractals import FractalVolume from gprMax.grid.cuda_grid import CUDAGrid from gprMax.grid.opencl_grid import OpenCLGrid from gprMax.materials import ListMaterial, Material, PeplinskiSoil, RangeMaterial @@ -67,6 +68,7 @@ class Model: self.subgrids: List[SubGridBaseGrid] = [] self.materials: List[Material] = [] self.mixingmodels: List[Union[PeplinskiSoil, RangeMaterial, ListMaterial]] = [] + self.fractalvolumes: List[FractalVolume] = [] self.geometryviews: List[GeometryView] = [] self.geometryobjects: List[GeometryObjects] = []