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 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:
-----------------
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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

@@ -54,7 +54,7 @@ class Edge(UserObjectGeometry):
self.kwargs["p1"] = tuple(rot_pts[0, :])
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."""
try:
p1 = self.kwargs["p1"]

查看文件

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

查看文件

@@ -20,9 +20,12 @@ import logging
import numpy as np
from ..fractals import FractalVolume
from ..materials import ListMaterial
from .cmds_geometry import UserObjectGeometry, rotate_2point_object
import gprMax.config as config
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__)
@@ -53,6 +56,7 @@ class FractalBox(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.do_pre_build = True
self.hash = "#fractal_box"
def rotate(self, axis, angle, origin=None):
@@ -69,7 +73,7 @@ class FractalBox(UserObjectGeometry):
self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip):
def pre_build(self, grid, uip):
try:
p1 = self.kwargs["p1"]
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")
raise ValueError
volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim, seed)
volume.ID = ID
volume.operatingonID = mixing_model_id
volume.nbins = nbins
volume.weighting = weighting
volume.averaging = averagefractalbox
volume.mixingmodel = mixingmodel
self.volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim, seed)
self.volume.ID = ID
self.volume.operatingonID = mixing_model_id
self.volume.nbins = nbins
self.volume.weighting = weighting
self.volume.averaging = averagefractalbox
self.volume.mixingmodel = mixingmodel
dielectricsmoothing = "on" if volume.averaging else "off"
dielectricsmoothing = "on" if self.volume.averaging else "off"
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"{p4[1]:g}m, {p4[2]:g}m with {volume.operatingonID}, "
f"fractal dimension {volume.dimension:g}, fractal weightings "
f"{volume.weighting[0]:g}, {volume.weighting[1]:g}, "
f"{volume.weighting[2]:g}, fractal seeding {volume.seed}, "
f"with {volume.nbins} material(s) created, dielectric smoothing "
f"{p4[1]:g}m, {p4[2]:g}m with {self.volume.operatingonID}, "
f"fractal dimension {self.volume.dimension:g}, fractal weightings "
f"{self.volume.weighting[0]:g}, {self.volume.weighting[1]:g}, "
f"{self.volume.weighting[2]:g}, fractal seeding {self.volume.seed}, "
f"with {self.volume.nbins} material(s) created, dielectric smoothing "
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):
pass
def create(self, grid, uip):
def build(self, grid, uip):
"""Creates the object and adds it to the grid."""
try:
p1 = self.kwargs["p1"]

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

@@ -47,7 +47,7 @@ class UserObjectSingle:
for k, v in kwargs.items():
setattr(self.props, k, v)
def create(self, grid, uip):
def build(self, grid, uip):
pass
def rotate(self, axis, angle, origin=None):
@@ -65,7 +65,7 @@ class Title(UserObjectSingle):
super().__init__(**kwargs)
self.order = 1
def create(self, G, uip):
def build(self, G, uip):
try:
title = self.kwargs["name"]
G.title = title
@@ -85,7 +85,7 @@ class Discretisation(UserObjectSingle):
super().__init__(**kwargs)
self.order = 2
def create(self, G, uip):
def build(self, G, uip):
try:
G.dl = np.array(self.kwargs["p1"])
G.dx, G.dy, G.dz = self.kwargs["p1"]
@@ -123,7 +123,7 @@ class Domain(UserObjectSingle):
super().__init__(**kwargs)
self.order = 3
def create(self, G, uip):
def build(self, G, uip):
try:
G.nx, G.ny, G.nz = uip.discretise_point(self.kwargs["p1"])
except KeyError:
@@ -179,7 +179,7 @@ class TimeStepStabilityFactor(UserObjectSingle):
super().__init__(**kwargs)
self.order = 4
def create(self, G, uip):
def build(self, G, uip):
try:
f = self.kwargs["f"]
except KeyError:
@@ -210,7 +210,7 @@ class TimeWindow(UserObjectSingle):
super().__init__(**kwargs)
self.order = 5
def create(self, G, uip):
def build(self, G, uip):
# If number of iterations given
# 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
@@ -251,7 +251,7 @@ class OMPThreads(UserObjectSingle):
super().__init__(**kwargs)
self.order = 6
def create(self, G, uip):
def build(self, G, uip):
try:
n = self.kwargs["n"]
except KeyError:
@@ -285,7 +285,7 @@ class PMLProps(UserObjectSingle):
super().__init__(**kwargs)
self.order = 7
def create(self, G, uip):
def build(self, G, uip):
try:
G.pmls["formulation"] = self.kwargs["formulation"]
if G.pmls["formulation"] not in PML.formulations:
@@ -335,7 +335,7 @@ class SrcSteps(UserObjectSingle):
super().__init__(**kwargs)
self.order = 8
def create(self, G, uip):
def build(self, G, uip):
try:
G.srcsteps = uip.discretise_point(self.kwargs["p1"])
except KeyError:
@@ -360,7 +360,7 @@ class RxSteps(UserObjectSingle):
super().__init__(**kwargs)
self.order = 9
def create(self, G, uip):
def build(self, G, uip):
try:
G.rxsteps = uip.discretise_point(self.kwargs["p1"])
except KeyError:
@@ -385,5 +385,5 @@ class OutputDir(UserObjectSingle):
super().__init__(**kwargs)
self.order = 10
def create(self, grid, uip):
def build(self, grid, uip):
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 .cython.geometry_outputs import write_lines
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__)
@@ -89,13 +89,16 @@ class GeometryView:
self.dy = dy
self.dz = dz
self.filename = filename
self.filenamebase = filename
self.grid = grid
self.nbytes = None
def set_filename(self):
"""Constructs filename from user-supplied name and model run number."""
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):
@@ -316,7 +319,7 @@ class Comments:
sc = []
for src in srcs:
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}
sc.append(s)
@@ -324,10 +327,10 @@ class Comments:
return sc
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):
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):
if not self.averaged_materials:

查看文件

@@ -18,13 +18,16 @@
import logging
from .cmds_geometry.cmds_geometry import UserObjectGeometry
from .cmds_geometry.fractal_box_builder import FractalBoxBuilder
from .cmds_multiuse import UserObjectMulti
from .cmds_singleuse import Discretisation, Domain, TimeWindow, UserObjectSingle
from .materials import create_built_in_materials
from .subgrids.user_objects import SubGridBase as SubGridUserBase
from .user_inputs import create_user_input_points
from gprMax.cmds_geometry.cmds_geometry import UserObjectGeometry
from gprMax.cmds_geometry.fractal_box import FractalBox
from gprMax.cmds_geometry.add_grass import AddGrass
from gprMax.cmds_geometry.add_surface_roughness import AddSurfaceRoughness
from gprMax.cmds_geometry.add_surface_water import AddSurfaceWater
from gprMax.cmds_multiuse import UserObjectMulti
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__)
@@ -54,9 +57,23 @@ class Scene:
else:
logger.exception("This object is unknown to gprMax")
raise ValueError
def process_subgrid_commands(self):
# Check for subgrid user objects
def build_obj(self, obj, grid):
"""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):
if isinstance(obj, SubGridUserBase):
return True
@@ -73,25 +90,31 @@ class Scene:
# to build in the correct subgrid.
sg = sg_cmd.subgrid
self.process_cmds(sg_cmd.children_multiple, sg)
self.process_cmds(sg_cmd.children_geometry, sg, sort=False)
def process_cmds(self, commands, grid, sort=True):
if sort:
cmds_sorted = sorted(commands, key=lambda cmd: cmd.order)
else:
cmds_sorted = commands
self.process_geocmds(sg_cmd.children_geometry, sg)
def process_cmds(self, commands, grid):
"""Process list of commands."""
cmds_sorted = sorted(commands, key=lambda cmd: cmd.order)
for obj in cmds_sorted:
# in the first level all objects belong to the main grid
uip = create_user_input_points(grid, obj)
# Create an instance to check the geometry points provided by the
# user. The way the point are checked depends on which grid the
# points belong to.
try:
obj.create(grid, uip)
except ValueError:
logger.exception("Error creating user input object")
raise
self.build_obj(obj, grid)
return self
def process_geocmds(self, commands, grid):
# Check for fractal boxes and modifications and pre-process them first
proc_cmds = []
for obj in commands:
if isinstance(obj, (FractalBox, AddGrass,
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
@@ -116,16 +139,11 @@ class Scene:
self.process_cmds(cmds_unique, 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
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_built_in_materials(G)
@@ -140,7 +158,7 @@ class Scene:
grid.initialise_geometry_arrays()
# 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
self.process_subgrid_commands()
self.process_subgrid_cmds()

查看文件

@@ -121,7 +121,7 @@ class SubGridBase(UserObjectMulti):
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.
self.subgrid = sg
@@ -192,7 +192,7 @@ class SubGridHSG(SubGridBase):
self.order = 18
self.hash = "#subgrid_hsg"
def create(self, grid, uip):
def build(self, grid, uip):
sg = SubGridHSGUser(**self.kwargs)
self.setup(sg, grid, uip)
return sg

查看文件

@@ -19,6 +19,7 @@
import datetime
import decimal as d
import logging
import re
import textwrap
from shutil import get_terminal_size
@@ -50,6 +51,16 @@ def get_terminal_width():
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):
"""Prints gprMax logo, version, and licencing/copyright information.
@@ -172,14 +183,4 @@ def fft_power(waveform, dt):
def timer():
"""Time in fractional seconds."""
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))
return timer_fn()