Refactor single use commands

这个提交包含在:
nmannall
2024-12-09 17:33:52 +00:00
父节点 0869010fc6
当前提交 f9b73406ca
共有 4 个文件被更改,包括 571 次插入417 次删除

查看文件

@@ -362,7 +362,7 @@ class FDTDGrid:
logger.info(f"Materials [{self.name}]:\n{materialstable.table}\n") logger.info(f"Materials [{self.name}]:\n{materialstable.table}\n")
def _update_positions( def _update_positions(
self, items: Iterable[Union[Source, Rx]], step_size: List[int], step_number: int self, items: Iterable[Union[Source, Rx]], step_size: npt.NDArray[np.int32], step_number: int
) -> None: ) -> None:
"""Update the grid positions of the provided items. """Update the grid positions of the provided items.
@@ -387,11 +387,11 @@ class FDTDGrid:
or item.zcoord + step_size[2] * config.sim_config.model_end > self.nz or item.zcoord + step_size[2] * config.sim_config.model_end > self.nz
): ):
raise ValueError raise ValueError
item.xcoord = item.xcoordorigin + step_number * step_size[0] item.coord = item.coordorigin + step_number * step_size
item.ycoord = item.ycoordorigin + step_number * step_size[1]
item.zcoord = item.zcoordorigin + step_number * step_size[2]
def update_simple_source_positions(self, step_size: List[int], step: int = 0) -> None: def update_simple_source_positions(
self, step_size: npt.NDArray[np.int32], step: int = 0
) -> None:
"""Update the positions of sources in the grid. """Update the positions of sources in the grid.
Move hertzian dipole and magnetic dipole sources. Transmission Move hertzian dipole and magnetic dipole sources. Transmission
@@ -414,7 +414,7 @@ class FDTDGrid:
logger.exception("Source(s) will be stepped to a position outside the domain.") logger.exception("Source(s) will be stepped to a position outside the domain.")
raise ValueError from e raise ValueError from e
def update_receiver_positions(self, step_size: List[int], step: int = 0) -> None: def update_receiver_positions(self, step_size: npt.NDArray[np.int32], step: int = 0) -> None:
"""Update the positions of receivers in the grid. """Update the positions of receivers in the grid.
Args: Args:

查看文件

@@ -59,8 +59,8 @@ class Model:
self.iterations = 0 # Total number of iterations self.iterations = 0 # Total number of iterations
self.timewindow = 0.0 self.timewindow = 0.0
self.srcsteps: List[int] = [0, 0, 0] self.srcsteps = np.zeros(3, dtype=np.int32)
self.rxsteps: List[int] = [0, 0, 0] self.rxsteps = np.zeros(3, dtype=np.int32)
self.G = self._create_grid() self.G = self._create_grid()
self.subgrids: List[SubGridBaseGrid] = [] self.subgrids: List[SubGridBaseGrid] = []

查看文件

@@ -1,409 +1,558 @@
# Copyright (C) 2015-2024: The University of Edinburgh, United Kingdom # Copyright (C) 2015-2024: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley # Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
# #
# This file is part of gprMax. # This file is part of gprMax.
# #
# gprMax is free software: you can redistribute it and/or modify # gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# gprMax is distributed in the hope that it will be useful, # gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>. # along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import logging import logging
from abc import ABC, abstractmethod from typing import Optional, Tuple, Union
import numpy as np import numpy as np
import numpy.typing as npt
import gprMax.config as config
from gprMax.grid.mpi_grid import MPIGrid from gprMax import config
from gprMax.model import Model from gprMax.grid.mpi_grid import MPIGrid
from gprMax.user_inputs import MainGridUserInput from gprMax.model import Model
from gprMax.pml import PML
from ..pml import PML from gprMax.user_objects.user_objects import ModelUserObject
from ..utilities.host_info import set_omp_threads from gprMax.utilities.host_info import set_omp_threads
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Properties: class Title(ModelUserObject):
pass """Title of the model.
Attributes:
class UserObjectSingle(ABC): title (str): Model title.
"""Object that can only occur a single time in a model.""" """
def __init__(self, **kwargs): @property
# Each single command has an order to specify the order in which def order(self):
# the commands are constructed, e.g. discretisation must be return 1
# created before the domain
self.order = 0 @property
self.kwargs = kwargs def hash(self):
self.props = Properties() return "#title"
self.autotranslate = True
def __init__(self, name: str):
for k, v in kwargs.items(): """Create a Title user object.
setattr(self.props, k, v)
Args:
@abstractmethod name: Title of the model.
def build(self, model: Model, uip: MainGridUserInput): """
pass super().__init__(name=name)
self.title = name
# TODO: Check if this is actually needed
def rotate(self, axis, angle, origin=None): def build(self, model: Model):
pass model.title = self.title
logger.info(f"Model title: {model.title}")
class Title(UserObjectSingle):
"""Includes a title for your model. class Discretisation(ModelUserObject):
"""Spatial discretisation of the model in the x, y, and z dimensions.
Attributes:
name: string for model title. Attributes:
""" discretisation (np.array): Spatial discretisation of the model
(x, y, z)
def __init__(self, **kwargs): """
super().__init__(**kwargs)
self.order = 1 @property
def order(self):
def build(self, model, uip): return 2
try:
title = self.kwargs["name"] @property
model.title = title def hash(self):
logger.info(f"Model title: {model.title}") return "#dx_dy_dz"
except KeyError:
pass def __init__(self, p1: Tuple[float, float, float]):
"""Create a Discretisation user object.
class Discretisation(UserObjectSingle): Args:
"""Specifies the discretization of space in the x, y, and z directions. p1: Spatial discretisation in the x, y, and z dimensions.
"""
Attributes: super().__init__(p1=p1)
p1: tuple of floats to specify spatial discretisation in x, y, z direction. self.discretisation = p1
"""
def build(self, model: Model):
def __init__(self, **kwargs): if any(self.discretisation) <= 0:
super().__init__(**kwargs) raise ValueError(
self.order = 2 f"{self} discretisation requires the spatial step to be"
" greater than zero in all dimensions"
def build(self, model, uip): )
try:
model.dl = np.array(self.kwargs["p1"], dtype=np.float64) model.dl = np.array(self.discretisation, dtype=np.float64)
except KeyError: logger.info(f"Spatial discretisation: {model.dl[0]:g} x {model.dl[1]:g} x {model.dl[2]:g}m")
logger.exception(f"{self.__str__()} discretisation requires a point")
raise
class Domain(ModelUserObject):
if model.dl[0] <= 0: """Size of the model.
logger.exception(
f"{self.__str__()} discretisation requires the " Attributes:
f"x-direction spatial step to be greater than zero" domain_size (tuple): Extent of the model domain (x, y, z).
) """
raise ValueError
if model.dl[1] <= 0: @property
logger.exception( def order(self):
f"{self.__str__()} discretisation requires the " return 3
f"y-direction spatial step to be greater than zero"
) @property
raise ValueError def hash(self):
if model.dl[2] <= 0: return "#domain"
logger.exception(
f"{self.__str__()} discretisation requires the " def __init__(self, p1: Tuple[float, float, float]):
f"z-direction spatial step to be greater than zero" """Create a Domain user object.
)
raise ValueError Args:
p1: Model extent in the x, y, and z dimensions.
logger.info(f"Spatial discretisation: {model.dl[0]:g} x {model.dl[1]:g} x {model.dl[2]:g}m") """
super().__init__(p1=p1)
self.domain_size = p1
class Domain(UserObjectSingle):
"""Specifies the size of the model. def build(self, model: Model):
uip = self._create_uip(model.G)
Attributes: model.nx, model.ny, model.nz = uip.discretise_point(self.domain_size)
p1: tuple of floats specifying extent of model domain (x, y, z). # TODO: Remove when distribute full build for MPI
""" if isinstance(model.G, MPIGrid):
model.G.nx = model.nx
def __init__(self, **kwargs): model.G.ny = model.ny
super().__init__(**kwargs) model.G.nz = model.nz
self.order = 3
if model.nx == 0 or model.ny == 0 or model.nz == 0:
def build(self, model, uip): raise ValueError(f"{self} requires at least one cell in every dimension")
try:
model.nx, model.ny, model.nz = uip.discretise_point(self.kwargs["p1"]) logger.info(
# TODO: Remove when distribute full build for MPI f"Domain size: {self.domain_size[0]:g} x {self.domain_size[1]:g} x "
if isinstance(model.G, MPIGrid): + f"{self.domain_size[2]:g}m ({model.nx:d} x {model.ny:d} x {model.nz:d} = "
model.G.nx = model.nx + f"{(model.nx * model.ny * model.nz):g} cells)"
model.G.ny = model.ny )
model.G.nz = model.nz
# Set mode and switch off appropriate PMLs for 2D models
except KeyError: grid = model.G
logger.exception(f"{self.__str__()} please specify a point") if model.nx == 1:
raise config.get_model_config().mode = "2D TMx"
grid.pmls["thickness"]["x0"] = 0
if model.nx == 0 or model.ny == 0 or model.nz == 0: grid.pmls["thickness"]["xmax"] = 0
logger.exception(f"{self.__str__()} requires at least one cell in every dimension") elif model.ny == 1:
raise ValueError config.get_model_config().mode = "2D TMy"
grid.pmls["thickness"]["y0"] = 0
logger.info( grid.pmls["thickness"]["ymax"] = 0
f"Domain size: {self.kwargs['p1'][0]:g} x {self.kwargs['p1'][1]:g} x " elif model.nz == 1:
+ f"{self.kwargs['p1'][2]:g}m ({model.nx:d} x {model.ny:d} x {model.nz:d} = " config.get_model_config().mode = "2D TMz"
+ f"{(model.nx * model.ny * model.nz):g} cells)" grid.pmls["thickness"]["z0"] = 0
) grid.pmls["thickness"]["zmax"] = 0
else:
# Calculate time step at CFL limit; switch off appropriate PMLs for 2D config.get_model_config().mode = "3D"
G = model.G
if model.nx == 1: logger.info(f"Mode: {config.get_model_config().mode}")
config.get_model_config().mode = "2D TMx"
G.pmls["thickness"]["x0"] = 0 # Sub-grids cannot be used with 2D models. There would typically be
G.pmls["thickness"]["xmax"] = 0 # minimal performance benefit with sub-gridding and 2D models.
elif model.ny == 1: if "2D" in config.get_model_config().mode and config.sim_config.general["subgrid"]:
config.get_model_config().mode = "2D TMy" raise ValueError("Sub-gridding cannot be used with 2D models")
G.pmls["thickness"]["y0"] = 0
G.pmls["thickness"]["ymax"] = 0 # Calculate time step at CFL limit
elif model.nz == 1: grid.calculate_dt()
config.get_model_config().mode = "2D TMz"
G.pmls["thickness"]["z0"] = 0 logger.info(f"Time step (at CFL limit): {grid.dt:g} secs")
G.pmls["thickness"]["zmax"] = 0
else:
config.get_model_config().mode = "3D" class TimeStepStabilityFactor(ModelUserObject):
G.calculate_dt() """Factor by which to reduce the time step from the CFL limit.
logger.info(f"Mode: {config.get_model_config().mode}") Attributes:
stability_factor (flaot): Factor to multiply time step by.
# Sub-grids cannot be used with 2D models. There would typically be """
# minimal performance benefit with sub-gridding and 2D models.
if "2D" in config.get_model_config().mode and config.sim_config.general["subgrid"]: @property
logger.exception("Sub-gridding cannot be used with 2D models") def order(self):
raise ValueError return 4
logger.info(f"Time step (at CFL limit): {G.dt:g} secs") @property
def hash(self):
return "#time_step_stability_factor"
class TimeStepStabilityFactor(UserObjectSingle):
"""Factor by which to reduce the time step from the CFL limit. def __init__(self, f: float):
"""Create a TimeStepStabilityFactor user object.
Attributes:
f: float for factor to multiply time step. Args:
""" f: Factor to multiply the model time step by.
"""
def __init__(self, **kwargs): super().__init__(f=f)
super().__init__(**kwargs) self.stability_factor = f
self.order = 4
def build(self, model: Model):
def build(self, model, uip): if self.stability_factor <= 0 or self.stability_factor > 1:
try: raise ValueError(
f = self.kwargs["f"] f"{self} requires the value of the time step stability"
except KeyError: " factor to be between zero and one"
logger.exception(f"{self.__str__()} requires exactly one parameter") )
raise
model.dt_mod = self.stability_factor
if f <= 0 or f > 1: model.dt *= model.dt_mod
logger.exception(
f"{self.__str__()} requires the value of the time " logger.info(f"Time step (modified): {model.dt:g} secs")
f"step stability factor to be between zero and one"
)
raise ValueError class TimeWindow(ModelUserObject):
"""Specifies the total required simulated time.
model.dt_mod = f
model.dt *= model.dt_mod Either time or iterations must be specified. If both are specified,
time takes precedence.
logger.info(f"Time step (modified): {model.dt:g} secs")
Attributes:
time: float of required simulated time in seconds.
class TimeWindow(UserObjectSingle): iterations: int of required number of iterations.
"""Specifies the total required simulated time. """
Attributes: @property
time: float of required simulated time in seconds. def order(self):
iterations: int of required number of iterations. return 5
"""
@property
def __init__(self, **kwargs): def hash(self):
super().__init__(**kwargs) return "#time_window"
self.order = 5
def __init__(self, time: Optional[float] = None, iterations: Optional[int] = None):
def build(self, model, uip): """Create a TimeWindow user object.
# If number of iterations given
# The +/- 1 used in calculating the number of iterations is to account for Args:
# the fact that the solver (iterations) loop runs from 0 to < G.iterations time: Optional simulation time in seconds. Default None.
try: iterations: Optional number of iterations. Default None.
iterations = int(self.kwargs["iterations"]) """
model.timewindow = (iterations - 1) * model.dt super().__init__(time=time, iterations=iterations)
model.iterations = iterations self.time = time
except KeyError: self.iterations = iterations
pass
def build(self, model: Model):
try: if self.time is not None:
tmp = float(self.kwargs["time"]) if self.time > 0:
if tmp > 0: model.timewindow = self.time
model.timewindow = tmp model.iterations = np.ceil(self.time / model.dt, dtype=np.int32) + 1
model.iterations = int(np.ceil(tmp / model.dt)) + 1 else:
else: raise ValueError(f"{self} must have a value greater than zero")
logger.exception(self.__str__() + " must have a value greater than zero") elif self.iterations is not None:
raise ValueError # The +/- 1 used in calculating the number of iterations is
except KeyError: # to account for the fact that the solver (iterations) loop
pass # runs from 0 to < G.iterations
model.timewindow = (self.iterations - 1) * model.dt
if not model.timewindow: model.iterations = self.iterations
logger.exception(self.__str__() + " specify a time or number of iterations") else:
raise ValueError raise ValueError(f"{self} specify a time or number of iterations")
logger.info(f"Time window: {model.timewindow:g} secs ({model.iterations} iterations)") if self.time is not None and self.iterations is not None:
logger.warning(
f"{self._params_str()} Time and iterations were both specified, using 'time'"
class OMPThreads(UserObjectSingle): )
"""Controls how many OpenMP threads (usually the number of physical CPU
cores available) are used when running the model. logger.info(f"Time window: {model.timewindow:g} secs ({model.iterations} iterations)")
Attributes:
n: int for number of threads. class OMPThreads(ModelUserObject):
""" """Set the number of OpenMP threads to use when running the model.
def __init__(self, **kwargs): Usually this should match the number of physical CPU cores
super().__init__(**kwargs) available.
self.order = 6
Attributes:
def build(self, model, uip): omp_threads (int): Number of OpenMP threads.
try: """
n = self.kwargs["n"]
except KeyError: @property
logger.exception( def order(self):
f"{self.__str__()} requires exactly one parameter " return 6
f"to specify the number of CPU OpenMP threads to use"
) @property
raise def hash(self):
if n < 1: return "#num_threads"
logger.exception(
f"{self.__str__()} requires the value to be an " f"integer not less than one" def __init__(self, n: int):
) """Create an OMPThreads user object.
raise ValueError
Args:
config.get_model_config().ompthreads = set_omp_threads(n) n: Number of OpenMP threads.
"""
super().__init__(n=n)
class PMLProps(UserObjectSingle): self.omp_threads = n
"""Specifies the formulation used and thickness (number of cells) of PML
that are used on the six sides of the model domain. Current options are def build(self, model: Model):
to use the Higher Order RIPML (HORIPML) - https://doi.org/10.1109/TAP.2011.2180344, if self.omp_threads < 1:
or Multipole RIPML (MRIPML) - https://doi.org/10.1109/TAP.2018.2823864. raise ValueError(f"{self} requires the value to be an integer not less than one")
Attributes: config.get_model_config().ompthreads = set_omp_threads(self.omp_threads)
formulation: string specifying formulation to be used for all PMLs
either 'HORIPML' or 'MRIPML'. logger.info(f"Simulation will use {config.get_model_config().ompthreads} OpenMP threads")
thickness or x0, y0, z0, xmax, ymax, zmax: ints for thickness of PML
on all 6 sides or individual
sides of the model domain. class PMLFormulation(ModelUserObject):
""" """Set the formulation of the PMLs.
def __init__(self, **kwargs): Current options are to use the Higher Order RIPML (HORIPML) -
super().__init__(**kwargs) https://doi.org/10.1109/TAP.2011.2180344, or Multipole RIPML
self.order = 7 (MRIPML) - https://doi.org/10.1109/TAP.2018.2823864.
def build(self, model, uip): Attributes:
G = model.G formulation (str): Formulation to be used for all PMLs. Either
try: 'HORIPML' or 'MRIPML'.
G.pmls["formulation"] = self.kwargs["formulation"] """
if G.pmls["formulation"] not in PML.formulations:
logger.exception( @property
self.__str__() def order(self):
+ f" requires the value to be " return 7
+ f"one of {' '.join(PML.formulations)}"
) @property
except KeyError: def hash(self):
pass return "#pml_formulation"
try: def __init__(self, formulation: str):
thickness = self.kwargs["thickness"] """Create a PMLFormulation user object.
for key in G.pmls["thickness"].keys():
G.pmls["thickness"][key] = int(thickness) Args:
formulation: Formulation to be used for all PMLs. Either
except KeyError: 'HORIPML' or 'MRIPML'.
try: """
G.pmls["thickness"]["x0"] = int(self.kwargs["x0"]) super().__init__(formulation=formulation)
G.pmls["thickness"]["y0"] = int(self.kwargs["y0"]) self.formulation = formulation
G.pmls["thickness"]["z0"] = int(self.kwargs["z0"])
G.pmls["thickness"]["xmax"] = int(self.kwargs["xmax"]) def build(self, model: Model):
G.pmls["thickness"]["ymax"] = int(self.kwargs["ymax"]) if self.formulation not in PML.formulations:
G.pmls["thickness"]["zmax"] = int(self.kwargs["zmax"]) logger.exception(f"{self} requires the value to be one of {' '.join(PML.formulations)}")
except KeyError:
logger.exception(f"{self.__str__()} requires either one or six parameter(s)") model.G.pmls["formulation"] = self.formulation
raise
logger.info(f"PML formulation set to {model.G.pmls['formulation']}")
if (
2 * G.pmls["thickness"]["x0"] >= G.nx
or 2 * G.pmls["thickness"]["y0"] >= G.ny class PMLThickness(ModelUserObject):
or 2 * G.pmls["thickness"]["z0"] >= G.nz """Set the thickness of the PMLs.
or 2 * G.pmls["thickness"]["xmax"] >= G.nx
or 2 * G.pmls["thickness"]["ymax"] >= G.ny The thickness can be set globally, or individually for each of the
or 2 * G.pmls["thickness"]["zmax"] >= G.nz six sides of the model domain. Either thickness must be set, or all
): of x0, y0, z0, xmax, ymax, zmax.
logger.exception(f"{self.__str__()} has too many cells for the domain size")
raise ValueError Attributes:
thickness (int | Tuple[int]): Thickness of the PML on all 6
sides or individual sides of the model domain.
class SrcSteps(UserObjectSingle): """
"""Moves the location of all simple sources.
@property
Attributes: def order(self):
p1: tuple of float increments (x,y,z) to move all simple sources. return 7
"""
@property
def __init__(self, **kwargs): def hash(self):
super().__init__(**kwargs) return "#pml_cells"
self.order = 8
def __init__(self, thickness: Union[int, Tuple[int, int, int, int, int, int]]):
def build(self, model, uip): """Create a PMLThickness user object.
try:
model.srcsteps = uip.discretise_point(self.kwargs["p1"]) Args:
except KeyError: thickness: Thickness of the PML on all 6 sides or individual
logger.exception(f"{self.__str__()} requires exactly three parameters") sides of the model domain.
raise """
super().__init__(thickness=thickness)
logger.info( self.thickness = thickness
f"Simple sources will step {model.srcsteps[0] * model.dx:g}m, "
f"{model.srcsteps[1] * model.dy:g}m, {model.srcsteps[2] * model.dz:g}m " def build(self, model: Model):
"for each model run." grid = model.G
)
if isinstance(self.thickness, int) or len(self.thickness) == 1:
for key in grid.pmls["thickness"].keys():
class RxSteps(UserObjectSingle): grid.pmls["thickness"][key] = int(self.thickness)
"""Moves the location of all receivers. elif len(self.thickness) == 6:
grid.pmls["thickness"]["x0"] = int(self.thickness[0])
Attributes: grid.pmls["thickness"]["y0"] = int(self.thickness[1])
p1: tuple of float increments (x,y,z) to move all receivers. grid.pmls["thickness"]["z0"] = int(self.thickness[2])
""" grid.pmls["thickness"]["xmax"] = int(self.thickness[3])
grid.pmls["thickness"]["ymax"] = int(self.thickness[4])
def __init__(self, **kwargs): grid.pmls["thickness"]["zmax"] = int(self.thickness[5])
super().__init__(**kwargs) else:
self.order = 9 raise ValueError(f"{self} requires either one or six parameter(s)")
def build(self, model, uip): # Check each PML does not take up more than half the grid
try: if (
model.rxsteps = uip.discretise_point(self.kwargs["p1"]) 2 * grid.pmls["thickness"]["x0"] >= grid.nx
except KeyError: or 2 * grid.pmls["thickness"]["y0"] >= grid.ny
logger.exception(f"{self.__str__()} requires exactly three parameters") or 2 * grid.pmls["thickness"]["z0"] >= grid.nz
raise or 2 * grid.pmls["thickness"]["xmax"] >= grid.nx
or 2 * grid.pmls["thickness"]["ymax"] >= grid.ny
logger.info( or 2 * grid.pmls["thickness"]["zmax"] >= grid.nz
f"All receivers will step {model.rxsteps[0] * model.dx:g}m, " ):
f"{model.rxsteps[1] * model.dy:g}m, {model.rxsteps[2] * model.dz:g}m " raise ValueError(f"{self} has too many cells for the domain size")
"for each model run."
) logger.info(
f"PML thickness: x0={model.G.pmls['x0']}, y0={model.G.pmls['y0']},"
f" z0={model.G.pmls['z0']}, xmax={model.G.pmls['xmax']},"
class OutputDir(UserObjectSingle): f" ymax={model.G.pmls['yxmax']}, zmax={model.G.pmls['zmax']}"
"""Controls the directory where output file(s) will be stored. )
Attributes:
dir: string of file path to directory. class PMLProps(ModelUserObject):
""" """Specify the formulation and thickness of the PMLs.
def __init__(self, **kwargs): A PML can be set on each of the six sides of the model domain.
super().__init__(**kwargs) Current options are to use the Higher Order RIPML (HORIPML) -
self.order = 10 https://doi.org/10.1109/TAP.2011.2180344, or Multipole RIPML
(MRIPML) - https://doi.org/10.1109/TAP.2018.2823864.
def build(self, grid, uip):
config.get_model_config().set_output_file_path(self.kwargs["dir"]) Deprecated: PMLProps is deprecated and may be removed in future
releases of gprMax. Use the new PMLFormulation and PMLThickness
user objects instead.
Attributes:
pml_formulation (PMLFormulation): User object to set the PML
formulation.
pml_thickness (PMLThickness): User object to set the PML
thickness.
"""
@property
def order(self):
return 7
@property
def hash(self):
return "#pml_properties"
def __init__(
self,
formulation: str,
thickness: Optional[int] = None,
x0: Optional[int] = None,
y0: Optional[int] = None,
z0: Optional[int] = None,
xmax: Optional[int] = None,
ymax: Optional[int] = None,
zmax: Optional[int] = None,
):
"""Create a PMLProps user object.
If 'thickness' is set, it will take precendence over any
individual thicknesses set. Additionally, if 'thickness' is not
set, the individual thickness must be set for all six sides of
the model domain.
Deprecated: PMLProps is deprecated and may be removed in future
releases of gprMax. Use the new PMLFormulation and PMLThickness
user objects instead.
Args:
formulation (str): Formulation to be used for all PMLs. Either
'HORIPML' or 'MRIPML'.
thickness: Optional thickness of the PML on all 6 sides of
the model domain. Default None.
x0, y0, z0, xmax, ymax, zmax: Optional thickness of the PML
on individual sides of the model domain. Default None.
"""
super().__init__()
logger.warning(
"PMLProps is deprecated and may be removed in future"
" releases of gprMax. Use the new PMLFormulation and"
" PMLThickness user objects instead."
)
self.pml_formulation = PMLFormulation(formulation)
if thickness is not None:
self.pml_thickness = PMLThickness(thickness)
elif (
x0 is not None
and y0 is not None
and z0 is not None
and xmax is not None
and ymax is not None
and zmax is not None
):
self.pml_thickness = PMLThickness((x0, y0, z0, xmax, ymax, zmax))
else:
raise ValueError("Either set thickness, or all of x0, y0, z0, xmax, ymax, zmax.")
def build(self, model):
self.pml_formulation.build(model)
self.pml_thickness.build(model)
class SrcSteps(ModelUserObject):
"""Move the location of all simple sources.
Attributes:
step_size (Tuple[float]): Increment (x, y, z) to move all
simple sources by for each step.
"""
@property
def order(self):
return 8
@property
def hash(self):
return "#src_steps"
def __init__(self, p1: Tuple[float, float, float]):
"""Create a SrcSteps user object.
Args:
p1: Increment (x, y, z) to move all simple sources by for
each step.
"""
super().__init__(p1=p1)
self.step_size = p1
def build(self, model: Model):
uip = self._create_uip(model.G)
model.srcsteps = np.array(uip.discretise_point(self.step_size), dtype=np.int32)
logger.info(
f"Simple sources will step {model.srcsteps[0] * model.dx:g}m, "
f"{model.srcsteps[1] * model.dy:g}m, {model.srcsteps[2] * model.dz:g}m "
"for each model run."
)
class RxSteps(ModelUserObject):
"""Move the location of all receivers.
Attributes:
step_size (Tuple[float]): Increment (x, y, z) to move all
receivers by for each step.
"""
@property
def order(self):
return 9
@property
def hash(self):
return "#rx_steps"
def __init__(self, p1: Tuple[float, float, float]):
"""Create a RxSteps user object.
Args:
p1: Increment (x, y, z) to move all receivers by for each
step.
"""
super().__init__(p1=p1)
self.step_size = p1
def build(self, model: Model):
uip = self._create_uip(model.G)
model.rxsteps = np.array(uip.discretise_point(self.step_size), dtype=np.int32)
logger.info(
f"All receivers will step {model.rxsteps[0] * model.dx:g}m, "
f"{model.rxsteps[1] * model.dy:g}m, {model.rxsteps[2] * model.dz:g}m "
"for each model run."
)

查看文件

@@ -100,6 +100,11 @@ class ModelUserObject(UserObject):
@abstractmethod @abstractmethod
def build(self, model: Model): def build(self, model: Model):
"""Build user object and set model properties.
Args:
model: Model to set the properties of.
"""
pass pass