你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-07 23:14:03 +08:00
Merge branch 'devel' into mpi
这个提交包含在:
@@ -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()
|
在新工单中引用
屏蔽一个用户