Merge branch 'devel' into mpi

这个提交包含在:
nmannall
2023-12-06 18:14:05 +00:00
当前提交 0e67588415
共有 24 个文件被更改,包括 541 次插入538 次删除

查看文件

@@ -205,3 +205,9 @@ Periodically you should update conda and the required Python packages. With the
$ conda update conda $ conda update conda
$ conda env update -f conda_env.yml $ conda env update -f conda_env.yml
Thanks To Our Contributors ✨🔗
==========================
.. image:: https://contrib.rocks/image?repo=gprMax/gprMax
:target: https://github.com/gprMax/gprMax/graphs/contributors

查看文件

@@ -765,11 +765,17 @@ For example, to specify the normalised first derivative of a Gaussian waveform w
#excitation_file: #excitation_file:
----------------- -----------------
Allows you to specify an ASCII file that contains columns of amplitude values that specify custom waveform shapes that can be used with sources in the model. Allows you to specify an ASCII file that contains amplitude values that specify custom waveform(s) that can be used with sources in the model.
The first row of each column must begin with a identifier string that will be used as the name of each waveform. Optionally, the first column of the file may contain a time vector of values (which must use the identifier ``time``) to interpolate the amplitude values of the waveform. If a time vector is not given, a vector of time values corresponding to the simulation time step and number of iterations will be used. The first row of each column must begin with a identifier string that will be used as the name of each waveform. Subsequent rows should contain amplitude values for the custom waveform you want to use. You can import multiple different waveforms (as columns of amplitude data) in a single file.
If there are less amplitude values than the number of iterations that are going to be performed, the end of the sequence of amplitude values will be padded with zero values up to the number of iterations. If extra amplitude values are specified than needed then they are ignored. The syntax of the command is: Ideally, there should be the same number of amplitude values as number of iterations in your model. If there are less amplitude values than the number of iterations in the model, the end of the sequence of amplitude values will be padded with zero values up to the number of iterations. If extra amplitude values are specified than needed then they are ignored.
Optionally, in the first column of the file you may specify your own time vector of values (which must use the identifier ``time``) to use with the amplitude values of the waveform.
The amplitude values will be interpolated using either the aforementioned user specified time vector, or if none was supplied, a vector of time values corresponding to the simulation time step and number of iterations will be used. Key parameters used for the interpolation can be specified in the command.
The syntax of the command is:
.. code-block:: none .. code-block:: none

查看文件

@@ -64,7 +64,7 @@ class AddGrass(UserObjectGeometry):
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :]) self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip): def build(self, grid, uip):
"""Add Grass to fractal box.""" """Add Grass to fractal box."""
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]

查看文件

@@ -65,7 +65,7 @@ class AddSurfaceRoughness(UserObjectGeometry):
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :]) self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"] p2 = self.kwargs["p2"]

查看文件

@@ -60,7 +60,7 @@ class AddSurfaceWater(UserObjectGeometry):
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :]) self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip): def build(self, grid, uip):
""" "Create surface water on fractal box.""" """ "Create surface water on fractal box."""
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]

查看文件

@@ -60,7 +60,7 @@ class Box(UserObjectGeometry):
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :]) self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"] p2 = self.kwargs["p2"]

查看文件

@@ -45,7 +45,7 @@ class UserObjectGeometry:
return f"{self.hash}: {s[:-1]}" return f"{self.hash}: {s[:-1]}"
def create(self, grid, uip): def build(self, grid, uip):
"""Creates object and adds it to the grid.""" """Creates object and adds it to the grid."""
pass pass

查看文件

@@ -48,7 +48,7 @@ class Cone(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = "#cone" self.hash = "#cone"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"] p2 = self.kwargs["p2"]

查看文件

@@ -46,7 +46,7 @@ class Cylinder(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = "#cylinder" self.hash = "#cylinder"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"] p2 = self.kwargs["p2"]

查看文件

@@ -55,7 +55,7 @@ class CylindricalSector(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = "#cylindrical_sector" self.hash = "#cylindrical_sector"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
normal = self.kwargs["normal"].lower() normal = self.kwargs["normal"].lower()
ctr1 = self.kwargs["ctr1"] ctr1 = self.kwargs["ctr1"]

查看文件

@@ -54,7 +54,7 @@ class Edge(UserObjectGeometry):
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :]) self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip): def build(self, grid, uip):
"""Creates edge and adds it to the grid.""" """Creates edge and adds it to the grid."""
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]

查看文件

@@ -45,7 +45,7 @@ class Ellipsoid(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = "#ellipsoid" self.hash = "#ellipsoid"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
xr = self.kwargs["xr"] xr = self.kwargs["xr"]

查看文件

@@ -20,9 +20,12 @@ import logging
import numpy as np import numpy as np
from ..fractals import FractalVolume import gprMax.config as config
from ..materials import ListMaterial
from .cmds_geometry import UserObjectGeometry, rotate_2point_object from gprMax.fractals import FractalVolume
from gprMax.materials import ListMaterial
from ..cython.geometry_primitives import build_voxels_from_array, build_voxels_from_array_mask
from gprMax.cmds_geometry.cmds_geometry import UserObjectGeometry, rotate_2point_object
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -53,6 +56,7 @@ class FractalBox(UserObjectGeometry):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.do_pre_build = True
self.hash = "#fractal_box" self.hash = "#fractal_box"
def rotate(self, axis, angle, origin=None): def rotate(self, axis, angle, origin=None):
@@ -69,7 +73,7 @@ class FractalBox(UserObjectGeometry):
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :]) self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip): def pre_build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"] p2 = self.kwargs["p2"]
@@ -148,24 +152,405 @@ class FractalBox(UserObjectGeometry):
logger.exception(f"{self.__str__()} mixing model or material with " + "ID {mixing_model_id} does not exist") logger.exception(f"{self.__str__()} mixing model or material with " + "ID {mixing_model_id} does not exist")
raise ValueError raise ValueError
volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim, seed) self.volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim, seed)
volume.ID = ID self.volume.ID = ID
volume.operatingonID = mixing_model_id self.volume.operatingonID = mixing_model_id
volume.nbins = nbins self.volume.nbins = nbins
volume.weighting = weighting self.volume.weighting = weighting
volume.averaging = averagefractalbox self.volume.averaging = averagefractalbox
volume.mixingmodel = mixingmodel self.volume.mixingmodel = mixingmodel
dielectricsmoothing = "on" if volume.averaging else "off" dielectricsmoothing = "on" if self.volume.averaging else "off"
logger.info( logger.info(
f"{self.grid_name(grid)}Fractal box {volume.ID} from " f"{self.grid_name(grid)}Fractal box {self.volume.ID} from "
f"{p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, " f"{p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, "
f"{p4[1]:g}m, {p4[2]:g}m with {volume.operatingonID}, " f"{p4[1]:g}m, {p4[2]:g}m with {self.volume.operatingonID}, "
f"fractal dimension {volume.dimension:g}, fractal weightings " f"fractal dimension {self.volume.dimension:g}, fractal weightings "
f"{volume.weighting[0]:g}, {volume.weighting[1]:g}, " f"{self.volume.weighting[0]:g}, {self.volume.weighting[1]:g}, "
f"{volume.weighting[2]:g}, fractal seeding {volume.seed}, " f"{self.volume.weighting[2]:g}, fractal seeding {self.volume.seed}, "
f"with {volume.nbins} material(s) created, dielectric smoothing " f"with {self.volume.nbins} material(s) created, dielectric smoothing "
f"is {dielectricsmoothing}." f"is {dielectricsmoothing}."
) )
grid.fractalvolumes.append(self.volume)
def build(self, grid, uip):
if self.do_pre_build:
self.pre_build(grid, uip)
self.do_pre_build = False
else:
if self.volume.fractalsurfaces:
self.volume.originalxs = self.volume.xs
self.volume.originalxf = self.volume.xf
self.volume.originalys = self.volume.ys
self.volume.originalyf = self.volume.yf
self.volume.originalzs = self.volume.zs
self.volume.originalzf = self.volume.zf
grid.fractalvolumes.append(volume) # Extend the volume to accomodate any rough surfaces, grass,
# or roots
for surface in self.volume.fractalsurfaces:
if surface.surfaceID == "xminus":
if surface.fractalrange[0] < self.volume.xs:
self.volume.nx += self.volume.xs - surface.fractalrange[0]
self.volume.xs = surface.fractalrange[0]
elif surface.surfaceID == "xplus":
if surface.fractalrange[1] > self.volume.xf:
self.volume.nx += surface.fractalrange[1] - self.volume.xf
self.volume.xf = surface.fractalrange[1]
elif surface.surfaceID == "yminus":
if surface.fractalrange[0] < self.volume.ys:
self.volume.ny += self.volume.ys - surface.fractalrange[0]
self.volume.ys = surface.fractalrange[0]
elif surface.surfaceID == "yplus":
if surface.fractalrange[1] > self.volume.yf:
self.volume.ny += surface.fractalrange[1] - self.volume.yf
self.volume.yf = surface.fractalrange[1]
elif surface.surfaceID == "zminus":
if surface.fractalrange[0] < self.volume.zs:
self.volume.nz += self.volume.zs - surface.fractalrange[0]
self.volume.zs = surface.fractalrange[0]
elif surface.surfaceID == "zplus":
if surface.fractalrange[1] > self.volume.zf:
self.volume.nz += surface.fractalrange[1] - self.volume.zf
self.volume.zf = surface.fractalrange[1]
# If there is only 1 bin then a normal material is being used,
# otherwise a mixing model
if self.volume.nbins == 1:
self.volume.fractalvolume = np.ones(
(self.volume.nx, self.volume.ny, self.volume.nz), dtype=config.sim_config.dtypes["float_or_double"]
)
materialnumID = next(x.numID for x in grid.materials if x.ID == self.volume.operatingonID)
self.volume.fractalvolume *= materialnumID
else:
self.volume.generate_fractal_volume()
for i in range(0, self.volume.nx):
for j in range(0, self.volume.ny):
for k in range(0, self.volume.nz):
numberinbin = self.volume.fractalvolume[i, j, k]
self.volume.fractalvolume[i, j, k] = self.volume.mixingmodel.matID[int(numberinbin)]
self.volume.generate_volume_mask()
# Apply any rough surfaces and add any surface water to the
# 3D mask array
# TODO: Allow extract of rough surface profile (to print/file?)
for surface in self.volume.fractalsurfaces:
if surface.surfaceID == "xminus":
for i in range(surface.fractalrange[0], surface.fractalrange[1]):
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
if i > surface.fractalsurface[j - surface.ys, k - surface.zs]:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 1
elif surface.filldepth > 0 and i > surface.filldepth:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 2
else:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 0
elif surface.surfaceID == "xplus":
if not surface.ID:
for i in range(surface.fractalrange[0], surface.fractalrange[1]):
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
if i < surface.fractalsurface[j - surface.ys, k - surface.zs]:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 1
elif surface.filldepth > 0 and i < surface.filldepth:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 2
else:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 0
elif surface.ID == "grass":
g = surface.grass[0]
# Build the blades of the grass
blade = 0
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
if surface.fractalsurface[j - surface.ys, k - surface.zs] > 0:
height = 0
for i in range(self.volume.xs, surface.fractalrange[1]):
if (
i < surface.fractalsurface[j - surface.ys, k - surface.zs]
and self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] != 1
):
y, z = g.calculate_blade_geometry(blade, height)
# Add y, z coordinates to existing location
yy = int(j - self.volume.ys + y)
zz = int(k - self.volume.zs + z)
# If these coordinates are outwith fractal volume stop building the blade,
# otherwise set the mask for grass.
if (
yy < 0
or yy >= self.volume.mask.shape[1]
or zz < 0
or zz >= self.volume.mask.shape[2]
):
break
else:
self.volume.mask[i - self.volume.xs, yy, zz] = 3
height += 1
blade += 1
# Build the roots of the grass
root = 0
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
if surface.fractalsurface[j - surface.ys, k - surface.zs] > 0:
depth = 0
i = self.volume.xf - 1
while i > self.volume.xs:
if (
i
> self.volume.originalxf
- (
surface.fractalsurface[j - surface.ys, k - surface.zs]
- self.volume.originalxf
)
and self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] == 1
):
y, z = g.calculate_root_geometry(root, depth)
# Add y, z coordinates to existing location
yy = int(j - self.volume.ys + y)
zz = int(k - self.volume.zs + z)
# If these coordinates are outwith the fractal volume stop building the root,
# otherwise set the mask for grass.
if (
yy < 0
or yy >= self.volume.mask.shape[1]
or zz < 0
or zz >= self.volume.mask.shape[2]
):
break
else:
self.volume.mask[i - self.volume.xs, yy, zz] = 3
depth += 1
i -= 1
root += 1
elif surface.surfaceID == "yminus":
for i in range(surface.xs, surface.xf):
for j in range(surface.fractalrange[0], surface.fractalrange[1]):
for k in range(surface.zs, surface.zf):
if j > surface.fractalsurface[i - surface.xs, k - surface.zs]:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 1
elif surface.filldepth > 0 and j > surface.filldepth:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 2
else:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 0
elif surface.surfaceID == "yplus":
if not surface.ID:
for i in range(surface.xs, surface.xf):
for j in range(surface.fractalrange[0], surface.fractalrange[1]):
for k in range(surface.zs, surface.zf):
if j < surface.fractalsurface[i - surface.xs, k - surface.zs]:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 1
elif surface.filldepth > 0 and j < surface.filldepth:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 2
else:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 0
elif surface.ID == "grass":
g = surface.grass[0]
# Build the blades of the grass
blade = 0
for i in range(surface.xs, surface.xf):
for k in range(surface.zs, surface.zf):
if surface.fractalsurface[i - surface.xs, k - surface.zs] > 0:
height = 0
for j in range(self.volume.ys, surface.fractalrange[1]):
if (
j < surface.fractalsurface[i - surface.xs, k - surface.zs]
and self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] != 1
):
x, z = g.calculate_blade_geometry(blade, height)
# Add x, z coordinates to existing location
xx = int(i - self.volume.xs + x)
zz = int(k - self.volume.zs + z)
# If these coordinates are outwith fractal volume stop building the blade,
# otherwise set the mask for grass.
if (
xx < 0
or xx >= self.volume.mask.shape[0]
or zz < 0
or zz >= self.volume.mask.shape[2]
):
break
else:
self.volume.mask[xx, j - self.volume.ys, zz] = 3
height += 1
blade += 1
# Build the roots of the grass
root = 0
for i in range(surface.xs, surface.xf):
for k in range(surface.zs, surface.zf):
if surface.fractalsurface[i - surface.xs, k - surface.zs] > 0:
depth = 0
j = self.volume.yf - 1
while j > self.volume.ys:
if (
j
> self.volume.originalyf
- (
surface.fractalsurface[i - surface.xs, k - surface.zs]
- self.volume.originalyf
)
and self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] == 1
):
x, z = g.calculate_root_geometry(root, depth)
# Add x, z coordinates to existing location
xx = int(i - self.volume.xs + x)
zz = int(k - self.volume.zs + z)
# If these coordinates are outwith the fractal volume stop building the root,
# otherwise set the mask for grass.
if (
xx < 0
or xx >= self.volume.mask.shape[0]
or zz < 0
or zz >= self.volume.mask.shape[2]
):
break
else:
self.volume.mask[xx, j - self.volume.ys, zz] = 3
depth += 1
j -= 1
root += 1
elif surface.surfaceID == "zminus":
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
for k in range(surface.fractalrange[0], surface.fractalrange[1]):
if k > surface.fractalsurface[i - surface.xs, j - surface.ys]:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 1
elif surface.filldepth > 0 and k > surface.filldepth:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 2
else:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 0
elif surface.surfaceID == "zplus":
if not surface.ID:
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
for k in range(surface.fractalrange[0], surface.fractalrange[1]):
if k < surface.fractalsurface[i - surface.xs, j - surface.ys]:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 1
elif surface.filldepth > 0 and k < surface.filldepth:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 2
else:
self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] = 0
elif surface.ID == "grass":
g = surface.grass[0]
# Build the blades of the grass
blade = 0
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
if surface.fractalsurface[i - surface.xs, j - surface.ys] > 0:
height = 0
for k in range(self.volume.zs, surface.fractalrange[1]):
if (
k < surface.fractalsurface[i - surface.xs, j - surface.ys]
and self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] != 1
):
x, y = g.calculate_blade_geometry(blade, height)
# Add x, y coordinates to existing location
xx = int(i - self.volume.xs + x)
yy = int(j - self.volume.ys + y)
# If these coordinates are outwith the fractal volume stop building the blade,
# otherwise set the mask for grass.
if (
xx < 0
or xx >= self.volume.mask.shape[0]
or yy < 0
or yy >= self.volume.mask.shape[1]
):
break
else:
self.volume.mask[xx, yy, k - self.volume.zs] = 3
height += 1
blade += 1
# Build the roots of the grass
root = 0
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
if surface.fractalsurface[i - surface.xs, j - surface.ys] > 0:
depth = 0
k = self.volume.zf - 1
while k > self.volume.zs:
if (
k
> self.volume.originalzf
- (
surface.fractalsurface[i - surface.xs, j - surface.ys]
- self.volume.originalzf
)
and self.volume.mask[i - self.volume.xs, j - self.volume.ys, k - self.volume.zs] == 1
):
x, y = g.calculate_root_geometry(root, depth)
# Add x, y coordinates to existing location
xx = int(i - self.volume.xs + x)
yy = int(j - self.volume.ys + y)
# If these coordinates are outwith the fractal volume stop building the root,
# otherwise set the mask for grass.
if (
xx < 0
or xx >= self.volume.mask.shape[0]
or yy < 0
or yy >= self.volume.mask.shape[1]
):
break
else:
self.volume.mask[xx, yy, k - self.volume.zs] = 3
depth += 1
k -= 1
root += 1
# Build voxels from any true values of the 3D mask array
waternumID = next((x.numID for x in grid.materials if x.ID == "water"), 0)
grassnumID = next((x.numID for x in grid.materials if x.ID == "grass"), 0)
data = self.volume.fractalvolume.astype("int16", order="C")
mask = self.volume.mask.copy(order="C")
build_voxels_from_array_mask(
self.volume.xs,
self.volume.ys,
self.volume.zs,
config.get_model_config().ompthreads,
waternumID,
grassnumID,
self.volume.averaging,
mask,
data,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)
else:
if self.volume.nbins == 1:
logger.exception(
f"{self.__str__()} is being used with a "
"single material and no modifications, "
"therefore please use a #box command instead."
)
raise ValueError
else:
self.volume.generate_fractal_volume()
for i in range(0, self.volume.nx):
for j in range(0, self.volume.ny):
for k in range(0, self.volume.nz):
numberinbin = self.volume.fractalvolume[i, j, k]
self.volume.fractalvolume[i, j, k] = self.volume.mixingmodel.matID[int(numberinbin)]
data = self.volume.fractalvolume.astype("int16", order="C")
build_voxels_from_array(
self.volume.xs,
self.volume.ys,
self.volume.zs,
config.get_model_config().ompthreads,
0,
self.volume.averaging,
data,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)

查看文件

@@ -1,416 +0,0 @@
# Copyright (C) 2015-2023: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import logging
import numpy as np
import gprMax.config as config
from ..cython.geometry_primitives import build_voxels_from_array, build_voxels_from_array_mask
from .cmds_geometry import UserObjectGeometry
logger = logging.getLogger(__name__)
class FractalBoxBuilder(UserObjectGeometry):
"""Internal class for fractal box modifications. This class should be used
internally only when surface modification have been made to a fractal box."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = "#fractal_box_modifications"
def create(self, grid, uip):
for volume in grid.fractalvolumes:
if volume.fractalsurfaces:
volume.originalxs = volume.xs
volume.originalxf = volume.xf
volume.originalys = volume.ys
volume.originalyf = volume.yf
volume.originalzs = volume.zs
volume.originalzf = volume.zf
# Extend the volume to accomodate any rough surfaces, grass,
# or roots
for surface in volume.fractalsurfaces:
if surface.surfaceID == "xminus":
if surface.fractalrange[0] < volume.xs:
volume.nx += volume.xs - surface.fractalrange[0]
volume.xs = surface.fractalrange[0]
elif surface.surfaceID == "xplus":
if surface.fractalrange[1] > volume.xf:
volume.nx += surface.fractalrange[1] - volume.xf
volume.xf = surface.fractalrange[1]
elif surface.surfaceID == "yminus":
if surface.fractalrange[0] < volume.ys:
volume.ny += volume.ys - surface.fractalrange[0]
volume.ys = surface.fractalrange[0]
elif surface.surfaceID == "yplus":
if surface.fractalrange[1] > volume.yf:
volume.ny += surface.fractalrange[1] - volume.yf
volume.yf = surface.fractalrange[1]
elif surface.surfaceID == "zminus":
if surface.fractalrange[0] < volume.zs:
volume.nz += volume.zs - surface.fractalrange[0]
volume.zs = surface.fractalrange[0]
elif surface.surfaceID == "zplus":
if surface.fractalrange[1] > volume.zf:
volume.nz += surface.fractalrange[1] - volume.zf
volume.zf = surface.fractalrange[1]
# If there is only 1 bin then a normal material is being used,
# otherwise a mixing model
if volume.nbins == 1:
volume.fractalvolume = np.ones(
(volume.nx, volume.ny, volume.nz), dtype=config.sim_config.dtypes["float_or_double"]
)
materialnumID = next(x.numID for x in grid.materials if x.ID == volume.operatingonID)
volume.fractalvolume *= materialnumID
else:
volume.generate_fractal_volume()
for i in range(0, volume.nx):
for j in range(0, volume.ny):
for k in range(0, volume.nz):
numberinbin = volume.fractalvolume[i, j, k]
volume.fractalvolume[i, j, k] = volume.mixingmodel.matID[int(numberinbin)]
volume.generate_volume_mask()
# Apply any rough surfaces and add any surface water to the
# 3D mask array
# TODO: Allow extract of rough surface profile (to print/file?)
for surface in volume.fractalsurfaces:
if surface.surfaceID == "xminus":
for i in range(surface.fractalrange[0], surface.fractalrange[1]):
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
if i > surface.fractalsurface[j - surface.ys, k - surface.zs]:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 1
elif surface.filldepth > 0 and i > surface.filldepth:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.surfaceID == "xplus":
if not surface.ID:
for i in range(surface.fractalrange[0], surface.fractalrange[1]):
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
if i < surface.fractalsurface[j - surface.ys, k - surface.zs]:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 1
elif surface.filldepth > 0 and i < surface.filldepth:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.ID == "grass":
g = surface.grass[0]
# Build the blades of the grass
blade = 0
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
if surface.fractalsurface[j - surface.ys, k - surface.zs] > 0:
height = 0
for i in range(volume.xs, surface.fractalrange[1]):
if (
i < surface.fractalsurface[j - surface.ys, k - surface.zs]
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1
):
y, z = g.calculate_blade_geometry(blade, height)
# Add y, z coordinates to existing location
yy = int(j - volume.ys + y)
zz = int(k - volume.zs + z)
# If these coordinates are outwith fractal volume stop building the blade,
# otherwise set the mask for grass.
if (
yy < 0
or yy >= volume.mask.shape[1]
or zz < 0
or zz >= volume.mask.shape[2]
):
break
else:
volume.mask[i - volume.xs, yy, zz] = 3
height += 1
blade += 1
# Build the roots of the grass
root = 0
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
if surface.fractalsurface[j - surface.ys, k - surface.zs] > 0:
depth = 0
i = volume.xf - 1
while i > volume.xs:
if (
i
> volume.originalxf
- (
surface.fractalsurface[j - surface.ys, k - surface.zs]
- volume.originalxf
)
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1
):
y, z = g.calculate_root_geometry(root, depth)
# Add y, z coordinates to existing location
yy = int(j - volume.ys + y)
zz = int(k - volume.zs + z)
# If these coordinates are outwith the fractal volume stop building the root,
# otherwise set the mask for grass.
if (
yy < 0
or yy >= volume.mask.shape[1]
or zz < 0
or zz >= volume.mask.shape[2]
):
break
else:
volume.mask[i - volume.xs, yy, zz] = 3
depth += 1
i -= 1
root += 1
elif surface.surfaceID == "yminus":
for i in range(surface.xs, surface.xf):
for j in range(surface.fractalrange[0], surface.fractalrange[1]):
for k in range(surface.zs, surface.zf):
if j > surface.fractalsurface[i - surface.xs, k - surface.zs]:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 1
elif surface.filldepth > 0 and j > surface.filldepth:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.surfaceID == "yplus":
if not surface.ID:
for i in range(surface.xs, surface.xf):
for j in range(surface.fractalrange[0], surface.fractalrange[1]):
for k in range(surface.zs, surface.zf):
if j < surface.fractalsurface[i - surface.xs, k - surface.zs]:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 1
elif surface.filldepth > 0 and j < surface.filldepth:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.ID == "grass":
g = surface.grass[0]
# Build the blades of the grass
blade = 0
for i in range(surface.xs, surface.xf):
for k in range(surface.zs, surface.zf):
if surface.fractalsurface[i - surface.xs, k - surface.zs] > 0:
height = 0
for j in range(volume.ys, surface.fractalrange[1]):
if (
j < surface.fractalsurface[i - surface.xs, k - surface.zs]
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1
):
x, z = g.calculate_blade_geometry(blade, height)
# Add x, z coordinates to existing location
xx = int(i - volume.xs + x)
zz = int(k - volume.zs + z)
# If these coordinates are outwith fractal volume stop building the blade,
# otherwise set the mask for grass.
if (
xx < 0
or xx >= volume.mask.shape[0]
or zz < 0
or zz >= volume.mask.shape[2]
):
break
else:
volume.mask[xx, j - volume.ys, zz] = 3
height += 1
blade += 1
# Build the roots of the grass
root = 0
for i in range(surface.xs, surface.xf):
for k in range(surface.zs, surface.zf):
if surface.fractalsurface[i - surface.xs, k - surface.zs] > 0:
depth = 0
j = volume.yf - 1
while j > volume.ys:
if (
j
> volume.originalyf
- (
surface.fractalsurface[i - surface.xs, k - surface.zs]
- volume.originalyf
)
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1
):
x, z = g.calculate_root_geometry(root, depth)
# Add x, z coordinates to existing location
xx = int(i - volume.xs + x)
zz = int(k - volume.zs + z)
# If these coordinates are outwith the fractal volume stop building the root,
# otherwise set the mask for grass.
if (
xx < 0
or xx >= volume.mask.shape[0]
or zz < 0
or zz >= volume.mask.shape[2]
):
break
else:
volume.mask[xx, j - volume.ys, zz] = 3
depth += 1
j -= 1
root += 1
elif surface.surfaceID == "zminus":
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
for k in range(surface.fractalrange[0], surface.fractalrange[1]):
if k > surface.fractalsurface[i - surface.xs, j - surface.ys]:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 1
elif surface.filldepth > 0 and k > surface.filldepth:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.surfaceID == "zplus":
if not surface.ID:
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
for k in range(surface.fractalrange[0], surface.fractalrange[1]):
if k < surface.fractalsurface[i - surface.xs, j - surface.ys]:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 1
elif surface.filldepth > 0 and k < surface.filldepth:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.ID == "grass":
g = surface.grass[0]
# Build the blades of the grass
blade = 0
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
if surface.fractalsurface[i - surface.xs, j - surface.ys] > 0:
height = 0
for k in range(volume.zs, surface.fractalrange[1]):
if (
k < surface.fractalsurface[i - surface.xs, j - surface.ys]
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1
):
x, y = g.calculate_blade_geometry(blade, height)
# Add x, y coordinates to existing location
xx = int(i - volume.xs + x)
yy = int(j - volume.ys + y)
# If these coordinates are outwith the fractal volume stop building the blade,
# otherwise set the mask for grass.
if (
xx < 0
or xx >= volume.mask.shape[0]
or yy < 0
or yy >= volume.mask.shape[1]
):
break
else:
volume.mask[xx, yy, k - volume.zs] = 3
height += 1
blade += 1
# Build the roots of the grass
root = 0
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
if surface.fractalsurface[i - surface.xs, j - surface.ys] > 0:
depth = 0
k = volume.zf - 1
while k > volume.zs:
if (
k
> volume.originalzf
- (
surface.fractalsurface[i - surface.xs, j - surface.ys]
- volume.originalzf
)
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1
):
x, y = g.calculate_root_geometry(root, depth)
# Add x, y coordinates to existing location
xx = int(i - volume.xs + x)
yy = int(j - volume.ys + y)
# If these coordinates are outwith the fractal volume stop building the root,
# otherwise set the mask for grass.
if (
xx < 0
or xx >= volume.mask.shape[0]
or yy < 0
or yy >= volume.mask.shape[1]
):
break
else:
volume.mask[xx, yy, k - volume.zs] = 3
depth += 1
k -= 1
root += 1
# Build voxels from any true values of the 3D mask array
waternumID = next((x.numID for x in grid.materials if x.ID == "water"), 0)
grassnumID = next((x.numID for x in grid.materials if x.ID == "grass"), 0)
data = volume.fractalvolume.astype("int16", order="C")
mask = volume.mask.copy(order="C")
build_voxels_from_array_mask(
volume.xs,
volume.ys,
volume.zs,
config.get_model_config().ompthreads,
waternumID,
grassnumID,
volume.averaging,
mask,
data,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)
else:
if volume.nbins == 1:
logger.exception(
f"{self.__str__()} is being used with a "
"single material and no modifications, "
"therefore please use a #box command instead."
)
raise ValueError
else:
volume.generate_fractal_volume()
for i in range(0, volume.nx):
for j in range(0, volume.ny):
for k in range(0, volume.nz):
numberinbin = volume.fractalvolume[i, j, k]
volume.fractalvolume[i, j, k] = volume.mixingmodel.matID[int(numberinbin)]
data = volume.fractalvolume.astype("int16", order="C")
build_voxels_from_array(
volume.xs,
volume.ys,
volume.zs,
config.get_model_config().ompthreads,
0,
volume.averaging,
data,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)

查看文件

@@ -39,7 +39,7 @@ class GeometryObjectsRead(UserObjectGeometry):
def rotate(self, axis, angle, origin=None): def rotate(self, axis, angle, origin=None):
pass pass
def create(self, grid, uip): def build(self, grid, uip):
"""Creates the object and adds it to the grid.""" """Creates the object and adds it to the grid."""
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]

查看文件

@@ -55,7 +55,7 @@ class Plate(UserObjectGeometry):
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :]) self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"] p2 = self.kwargs["p2"]

查看文件

@@ -43,7 +43,7 @@ class Sphere(UserObjectGeometry):
super().__init__(**kwargs) super().__init__(**kwargs)
self.hash = "#sphere" self.hash = "#sphere"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
r = self.kwargs["r"] r = self.kwargs["r"]

查看文件

@@ -63,7 +63,7 @@ class Triangle(UserObjectGeometry):
self.kwargs["p2"] = tuple(p2) self.kwargs["p2"] = tuple(p2)
self.kwargs["p3"] = tuple(p3) self.kwargs["p3"] = tuple(p3)
def create(self, grid, uip): def build(self, grid, uip):
try: try:
up1 = self.kwargs["p1"] up1 = self.kwargs["p1"]
up2 = self.kwargs["p2"] up2 = self.kwargs["p2"]

查看文件

@@ -66,7 +66,7 @@ class UserObjectMulti:
return f"{self.hash}: {s[:-1]}" return f"{self.hash}: {s[:-1]}"
def create(self, grid, uip): def build(self, grid, uip):
"""Creates object and adds it to grid.""" """Creates object and adds it to grid."""
pass pass
@@ -105,7 +105,7 @@ class ExcitationFile(UserObjectMulti):
self.order = 1 self.order = 1
self.hash = "#excitation_file" self.hash = "#excitation_file"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
kwargs = {} kwargs = {}
excitationfile = self.kwargs["filepath"] excitationfile = self.kwargs["filepath"]
@@ -200,7 +200,7 @@ class Waveform(UserObjectMulti):
self.order = 2 self.order = 2
self.hash = "#waveform" self.hash = "#waveform"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
wavetype = self.kwargs["wave_type"].lower() wavetype = self.kwargs["wave_type"].lower()
except KeyError: except KeyError:
@@ -314,7 +314,7 @@ class VoltageSource(UserObjectMulti):
rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, self.origin) rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, self.origin)
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
polarisation = self.kwargs["polarisation"].lower() polarisation = self.kwargs["polarisation"].lower()
@@ -438,7 +438,7 @@ class HertzianDipole(UserObjectMulti):
rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, self.origin) rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, self.origin)
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
def create(self, grid, uip): def build(self, grid, uip):
try: try:
polarisation = self.kwargs["polarisation"].lower() polarisation = self.kwargs["polarisation"].lower()
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
@@ -579,7 +579,7 @@ class MagneticDipole(UserObjectMulti):
rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, self.origin) rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, self.origin)
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
def create(self, grid, uip): def build(self, grid, uip):
try: try:
polarisation = self.kwargs["polarisation"].lower() polarisation = self.kwargs["polarisation"].lower()
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
@@ -702,7 +702,7 @@ class TransmissionLine(UserObjectMulti):
rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, self.origin) rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, self.origin)
self.kwargs["p1"] = tuple(rot_pts[0, :]) self.kwargs["p1"] = tuple(rot_pts[0, :])
def create(self, grid, uip): def build(self, grid, uip):
try: try:
polarisation = self.kwargs["polarisation"].lower() polarisation = self.kwargs["polarisation"].lower()
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
@@ -852,7 +852,7 @@ class Rx(UserObjectMulti):
except KeyError: except KeyError:
pass pass
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
except KeyError: except KeyError:
@@ -922,7 +922,7 @@ class RxArray(UserObjectMulti):
self.order = 8 self.order = 8
self.hash = "#rx_array" self.hash = "#rx_array"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"] p2 = self.kwargs["p2"]
@@ -1026,7 +1026,7 @@ class Snapshot(UserObjectMulti):
self.order = 9 self.order = 9
self.hash = "#snapshot" self.hash = "#snapshot"
def create(self, grid, uip): def build(self, grid, uip):
if isinstance(grid, SubGridBaseGrid): if isinstance(grid, SubGridBaseGrid):
logger.exception(f"{self.params_str()} do not add snapshots to subgrids.") logger.exception(f"{self.params_str()} do not add snapshots to subgrids.")
raise ValueError raise ValueError
@@ -1139,7 +1139,7 @@ class Material(UserObjectMulti):
self.order = 10 self.order = 10
self.hash = "#material" self.hash = "#material"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
er = self.kwargs["er"] er = self.kwargs["er"]
se = self.kwargs["se"] se = self.kwargs["se"]
@@ -1214,7 +1214,7 @@ class AddDebyeDispersion(UserObjectMulti):
self.order = 11 self.order = 11
self.hash = "#add_dispersion_debye" self.hash = "#add_dispersion_debye"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
poles = self.kwargs["poles"] poles = self.kwargs["poles"]
er_delta = self.kwargs["er_delta"] er_delta = self.kwargs["er_delta"]
@@ -1286,7 +1286,7 @@ class AddLorentzDispersion(UserObjectMulti):
self.order = 12 self.order = 12
self.hash = "#add_dispersion_lorentz" self.hash = "#add_dispersion_lorentz"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
poles = self.kwargs["poles"] poles = self.kwargs["poles"]
er_delta = self.kwargs["er_delta"] er_delta = self.kwargs["er_delta"]
@@ -1363,7 +1363,7 @@ class AddDrudeDispersion(UserObjectMulti):
self.order = 13 self.order = 13
self.hash = "#add_dispersion_drude" self.hash = "#add_dispersion_drude"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
poles = self.kwargs["poles"] poles = self.kwargs["poles"]
omega = self.kwargs["omega"] omega = self.kwargs["omega"]
@@ -1440,7 +1440,7 @@ class SoilPeplinski(UserObjectMulti):
self.order = 14 self.order = 14
self.hash = "#soil_peplinski" self.hash = "#soil_peplinski"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
sand_fraction = self.kwargs["sand_fraction"] sand_fraction = self.kwargs["sand_fraction"]
clay_fraction = self.kwargs["clay_fraction"] clay_fraction = self.kwargs["clay_fraction"]
@@ -1518,7 +1518,7 @@ class MaterialRange(UserObjectMulti):
self.order = 15 self.order = 15
self.hash = "#material_range" self.hash = "#material_range"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
er_lower = self.kwargs["er_lower"] er_lower = self.kwargs["er_lower"]
er_upper = self.kwargs["er_upper"] er_upper = self.kwargs["er_upper"]
@@ -1600,7 +1600,7 @@ class MaterialList(UserObjectMulti):
self.order = 16 self.order = 16
self.hash = "#material_list" self.hash = "#material_list"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
list_of_materials = self.kwargs["list_of_materials"] list_of_materials = self.kwargs["list_of_materials"]
ID = self.kwargs["id"] ID = self.kwargs["id"]
@@ -1641,7 +1641,7 @@ class GeometryView(UserObjectMulti):
self.order = 17 self.order = 17
self.hash = "#geometry_view" self.hash = "#geometry_view"
def geometry_view_constructor(self, grid, output_type): def geometry_view_constructor(self, output_type):
"""Selects appropriate class for geometry view dependent on geometry """Selects appropriate class for geometry view dependent on geometry
view type, i.e. normal or fine. view type, i.e. normal or fine.
""" """
@@ -1653,7 +1653,7 @@ class GeometryView(UserObjectMulti):
return GeometryViewUser return GeometryViewUser
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"] p2 = self.kwargs["p2"]
@@ -1664,7 +1664,7 @@ class GeometryView(UserObjectMulti):
logger.exception(f"{self.params_str()} requires exactly eleven parameters.") logger.exception(f"{self.params_str()} requires exactly eleven parameters.")
raise raise
GeometryViewUser = self.geometry_view_constructor(grid, output_type) GeometryViewUser = self.geometry_view_constructor(output_type)
try: try:
p3 = uip.round_to_grid_static_point(p1) p3 = uip.round_to_grid_static_point(p1)
@@ -1730,7 +1730,7 @@ class GeometryObjectsWrite(UserObjectMulti):
self.order = 18 self.order = 18
self.hash = "#geometry_objects_write" self.hash = "#geometry_objects_write"
def create(self, grid, uip): def build(self, grid, uip):
try: try:
p1 = self.kwargs["p1"] p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"] p2 = self.kwargs["p2"]
@@ -1786,7 +1786,7 @@ class PMLCFS(UserObjectMulti):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 19 self.order = 19
def create(self, grid, uip): def build(self, grid, uip):
try: try:
alphascalingprofile = self.kwargs["alphascalingprofile"] alphascalingprofile = self.kwargs["alphascalingprofile"]
alphascalingdirection = self.kwargs["alphascalingdirection"] alphascalingdirection = self.kwargs["alphascalingdirection"]

查看文件

@@ -47,7 +47,7 @@ class UserObjectSingle:
for k, v in kwargs.items(): for k, v in kwargs.items():
setattr(self.props, k, v) setattr(self.props, k, v)
def create(self, grid, uip): def build(self, grid, uip):
pass pass
def rotate(self, axis, angle, origin=None): def rotate(self, axis, angle, origin=None):
@@ -65,7 +65,7 @@ class Title(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 1 self.order = 1
def create(self, G, uip): def build(self, G, uip):
try: try:
title = self.kwargs["name"] title = self.kwargs["name"]
G.title = title G.title = title
@@ -85,7 +85,7 @@ class Discretisation(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 2 self.order = 2
def create(self, G, uip): def build(self, G, uip):
try: try:
G.dl = np.array(self.kwargs["p1"]) G.dl = np.array(self.kwargs["p1"])
G.dx, G.dy, G.dz = self.kwargs["p1"] G.dx, G.dy, G.dz = self.kwargs["p1"]
@@ -123,7 +123,7 @@ class Domain(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 3 self.order = 3
def create(self, G, uip): def build(self, G, uip):
try: try:
G.nx, G.ny, G.nz = uip.discretise_point(self.kwargs["p1"]) G.nx, G.ny, G.nz = uip.discretise_point(self.kwargs["p1"])
except KeyError: except KeyError:
@@ -179,7 +179,7 @@ class TimeStepStabilityFactor(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 4 self.order = 4
def create(self, G, uip): def build(self, G, uip):
try: try:
f = self.kwargs["f"] f = self.kwargs["f"]
except KeyError: except KeyError:
@@ -210,7 +210,7 @@ class TimeWindow(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 5 self.order = 5
def create(self, G, uip): def build(self, G, uip):
# If number of iterations given # If number of iterations given
# The +/- 1 used in calculating the number of iterations is to account for # The +/- 1 used in calculating the number of iterations is to account for
# the fact that the solver (iterations) loop runs from 0 to < G.iterations # the fact that the solver (iterations) loop runs from 0 to < G.iterations
@@ -251,7 +251,7 @@ class OMPThreads(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 6 self.order = 6
def create(self, G, uip): def build(self, G, uip):
try: try:
n = self.kwargs["n"] n = self.kwargs["n"]
except KeyError: except KeyError:
@@ -285,7 +285,7 @@ class PMLProps(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 7 self.order = 7
def create(self, G, uip): def build(self, G, uip):
try: try:
G.pmls["formulation"] = self.kwargs["formulation"] G.pmls["formulation"] = self.kwargs["formulation"]
if G.pmls["formulation"] not in PML.formulations: if G.pmls["formulation"] not in PML.formulations:
@@ -335,7 +335,7 @@ class SrcSteps(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 8 self.order = 8
def create(self, G, uip): def build(self, G, uip):
try: try:
G.srcsteps = uip.discretise_point(self.kwargs["p1"]) G.srcsteps = uip.discretise_point(self.kwargs["p1"])
except KeyError: except KeyError:
@@ -360,7 +360,7 @@ class RxSteps(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 9 self.order = 9
def create(self, G, uip): def build(self, G, uip):
try: try:
G.rxsteps = uip.discretise_point(self.kwargs["p1"]) G.rxsteps = uip.discretise_point(self.kwargs["p1"])
except KeyError: except KeyError:
@@ -385,5 +385,5 @@ class OutputDir(UserObjectSingle):
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 10 self.order = 10
def create(self, grid, uip): def build(self, grid, uip):
config.get_model_config().set_output_file_path(self.kwargs["dir"]) config.get_model_config().set_output_file_path(self.kwargs["dir"])

查看文件

@@ -32,7 +32,7 @@ import gprMax.config as config
from ._version import __version__ from ._version import __version__
from .cython.geometry_outputs import write_lines from .cython.geometry_outputs import write_lines
from .subgrids.grid import SubGridBaseGrid from .subgrids.grid import SubGridBaseGrid
from .utilities.utilities import get_terminal_width, numeric_list_to_float_list, numeric_list_to_int_list from .utilities.utilities import get_terminal_width
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -89,13 +89,16 @@ class GeometryView:
self.dy = dy self.dy = dy
self.dz = dz self.dz = dz
self.filename = filename self.filename = filename
self.filenamebase = filename
self.grid = grid self.grid = grid
self.nbytes = None self.nbytes = None
def set_filename(self): def set_filename(self):
"""Constructs filename from user-supplied name and model run number.""" """Constructs filename from user-supplied name and model run number."""
parts = config.get_model_config().output_file_path.parts parts = config.get_model_config().output_file_path.parts
self.filename = Path(*parts[:-1], self.filename + config.get_model_config().appendmodelnumber) self.filename = Path(*parts[:-1],
self.filenamebase +
config.get_model_config().appendmodelnumber)
class GeometryViewLines(GeometryView): class GeometryViewLines(GeometryView):
@@ -316,7 +319,7 @@ class Comments:
sc = [] sc = []
for src in srcs: for src in srcs:
p = (src.xcoord * self.grid.dx, src.ycoord * self.grid.dy, src.zcoord * self.grid.dz) p = (src.xcoord * self.grid.dx, src.ycoord * self.grid.dy, src.zcoord * self.grid.dz)
p = numeric_list_to_float_list(p) p = list(map(float, p))
s = {"name": src.ID, "position": p} s = {"name": src.ID, "position": p}
sc.append(s) sc.append(s)
@@ -324,10 +327,10 @@ class Comments:
return sc return sc
def dx_dy_dz_comment(self): def dx_dy_dz_comment(self):
return numeric_list_to_float_list([self.grid.dx, self.grid.dy, self.grid.dz]) return list(map(float, [self.grid.dx, self.grid.dy, self.grid.dz]))
def nx_ny_nz_comment(self): def nx_ny_nz_comment(self):
return numeric_list_to_int_list([self.grid.nx, self.grid.ny, self.grid.nz]) return list(map(int, [self.grid.nx, self.grid.ny, self.grid.nz]))
def materials_comment(self): def materials_comment(self):
if not self.averaged_materials: if not self.averaged_materials:

查看文件

@@ -18,13 +18,16 @@
import logging import logging
from .cmds_geometry.cmds_geometry import UserObjectGeometry from gprMax.cmds_geometry.cmds_geometry import UserObjectGeometry
from .cmds_geometry.fractal_box_builder import FractalBoxBuilder from gprMax.cmds_geometry.fractal_box import FractalBox
from .cmds_multiuse import UserObjectMulti from gprMax.cmds_geometry.add_grass import AddGrass
from .cmds_singleuse import Discretisation, Domain, TimeWindow, UserObjectSingle from gprMax.cmds_geometry.add_surface_roughness import AddSurfaceRoughness
from .materials import create_built_in_materials from gprMax.cmds_geometry.add_surface_water import AddSurfaceWater
from .subgrids.user_objects import SubGridBase as SubGridUserBase from gprMax.cmds_multiuse import UserObjectMulti
from .user_inputs import create_user_input_points from gprMax.cmds_singleuse import Discretisation, Domain, TimeWindow, UserObjectSingle
from gprMax.materials import create_built_in_materials
from gprMax.subgrids.user_objects import SubGridBase as SubGridUserBase
from gprMax.user_inputs import create_user_input_points
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -54,9 +57,23 @@ class Scene:
else: else:
logger.exception("This object is unknown to gprMax") logger.exception("This object is unknown to gprMax")
raise ValueError raise ValueError
def process_subgrid_commands(self): def build_obj(self, obj, grid):
# Check for subgrid user objects """Builds objects.
Args:
obj: user object
grid: FDTDGrid class describing a grid in a model.
"""
uip = create_user_input_points(grid, obj)
try:
obj.build(grid, uip)
except ValueError:
logger.exception("Error creating user input object")
raise
def process_subgrid_cmds(self):
"""Process all commands in any sub-grids."""
def func(obj): def func(obj):
if isinstance(obj, SubGridUserBase): if isinstance(obj, SubGridUserBase):
return True return True
@@ -73,25 +90,31 @@ class Scene:
# to build in the correct subgrid. # to build in the correct subgrid.
sg = sg_cmd.subgrid sg = sg_cmd.subgrid
self.process_cmds(sg_cmd.children_multiple, sg) self.process_cmds(sg_cmd.children_multiple, sg)
self.process_cmds(sg_cmd.children_geometry, sg, sort=False) self.process_geocmds(sg_cmd.children_geometry, sg)
def process_cmds(self, commands, grid, sort=True):
if sort:
cmds_sorted = sorted(commands, key=lambda cmd: cmd.order)
else:
cmds_sorted = commands
def process_cmds(self, commands, grid):
"""Process list of commands."""
cmds_sorted = sorted(commands, key=lambda cmd: cmd.order)
for obj in cmds_sorted: for obj in cmds_sorted:
# in the first level all objects belong to the main grid self.build_obj(obj, grid)
uip = create_user_input_points(grid, obj)
# Create an instance to check the geometry points provided by the return self
# user. The way the point are checked depends on which grid the
# points belong to. def process_geocmds(self, commands, grid):
try: # Check for fractal boxes and modifications and pre-process them first
obj.create(grid, uip) proc_cmds = []
except ValueError: for obj in commands:
logger.exception("Error creating user input object") if isinstance(obj, (FractalBox, AddGrass,
raise AddSurfaceRoughness, AddSurfaceWater)):
self.build_obj(obj, grid)
if isinstance(obj, (FractalBox)):
proc_cmds.append(obj)
else:
proc_cmds.append(obj)
# Process all geometry commands
for obj in proc_cmds:
self.build_obj(obj, grid)
return self return self
@@ -116,16 +139,11 @@ class Scene:
self.process_cmds(cmds_unique, G) self.process_cmds(cmds_unique, G)
def create_internal_objects(self, G): def create_internal_objects(self, G):
"""Calls the UserObject.create() function in the correct way - API """Calls the UserObject.build() function in the correct way - API
presents the user with UserObjects in order to build the internal presents the user with UserObjects in order to build the internal
Rx(), Cylinder() etc... objects. Rx(), Cylinder() etc... objects.
""" """
# Fractal box commands have an additional nonuser object which
# processes modifications
fbb = FractalBoxBuilder()
self.add(fbb)
# Create pre-defined (built-in) materials # Create pre-defined (built-in) materials
create_built_in_materials(G) create_built_in_materials(G)
@@ -140,7 +158,7 @@ class Scene:
grid.initialise_geometry_arrays() grid.initialise_geometry_arrays()
# Process the main grid geometry commands # Process the main grid geometry commands
self.process_cmds(self.geometry_cmds, G, sort=False) self.process_geocmds(self.geometry_cmds, G)
# Process all the commands for subgrids # Process all the commands for subgrids
self.process_subgrid_commands() self.process_subgrid_cmds()

查看文件

@@ -121,7 +121,7 @@ class SubGridBase(UserObjectMulti):
sg.timewindow = grid.timewindow sg.timewindow = grid.timewindow
# Copy a subgrid reference to self so that children.create(grid, uip) # Copy a subgrid reference to self so that children.build(grid, uip)
# can access the correct grid. # can access the correct grid.
self.subgrid = sg self.subgrid = sg
@@ -192,7 +192,7 @@ class SubGridHSG(SubGridBase):
self.order = 18 self.order = 18
self.hash = "#subgrid_hsg" self.hash = "#subgrid_hsg"
def create(self, grid, uip): def build(self, grid, uip):
sg = SubGridHSGUser(**self.kwargs) sg = SubGridHSGUser(**self.kwargs)
self.setup(sg, grid, uip) self.setup(sg, grid, uip)
return sg return sg

查看文件

@@ -19,6 +19,7 @@
import datetime import datetime
import decimal as d import decimal as d
import logging import logging
import re
import textwrap import textwrap
from shutil import get_terminal_size from shutil import get_terminal_size
@@ -50,6 +51,16 @@ def get_terminal_width():
return terminalwidth return terminalwidth
def atoi(text):
"""Converts a string into an integer."""
return int(text) if text.isdigit() else text
def natural_keys(text):
"""Human sorting of a string."""
return [atoi(c) for c in re.split(r"(\d+)", text)]
def logo(version): def logo(version):
"""Prints gprMax logo, version, and licencing/copyright information. """Prints gprMax logo, version, and licencing/copyright information.
@@ -172,14 +183,4 @@ def fft_power(waveform, dt):
def timer(): def timer():
"""Time in fractional seconds.""" """Time in fractional seconds."""
return timer_fn() return timer_fn()
def numeric_list_to_int_list(l):
"""List of int from a numerical list."""
return list(map(int, l))
def numeric_list_to_float_list(l):
"""List of float from a numerical list."""
return list(map(float, l))