你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-07 15:10:13 +08:00
Updated with ruff formatter
这个提交包含在:
@@ -91,11 +91,15 @@ class AddGrass(UserObjectGeometry):
|
||||
self._do_rotate()
|
||||
|
||||
# Get the correct fractal volume
|
||||
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
|
||||
volumes = [
|
||||
volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id
|
||||
]
|
||||
try:
|
||||
volume = volumes[0]
|
||||
except NameError:
|
||||
logger.exception(f"{self.__str__()} cannot find FractalBox {fractal_box_id}")
|
||||
logger.exception(
|
||||
f"{self.__str__()} cannot find FractalBox {fractal_box_id}"
|
||||
)
|
||||
raise
|
||||
|
||||
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
|
||||
@@ -103,7 +107,9 @@ class AddGrass(UserObjectGeometry):
|
||||
xf, yf, zf = p2
|
||||
|
||||
if frac_dim < 0:
|
||||
logger.exception(f"{self.__str__()} requires a positive value for the fractal dimension")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a positive value for the fractal dimension"
|
||||
)
|
||||
raise ValueError
|
||||
if limits[0] < 0 or limits[1] < 0:
|
||||
logger.exception(
|
||||
@@ -114,12 +120,19 @@ class AddGrass(UserObjectGeometry):
|
||||
# Check for valid orientations
|
||||
if xs == xf:
|
||||
if ys == yf or zs == zf:
|
||||
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
|
||||
logger.exception(
|
||||
f"{self.__str__()} dimensions are not specified correctly"
|
||||
)
|
||||
raise ValueError
|
||||
if xs not in [volume.xs, volume.xf]:
|
||||
logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box")
|
||||
logger.exception(
|
||||
f"{self.__str__()} must specify external surfaces on a fractal box"
|
||||
)
|
||||
raise ValueError
|
||||
fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
|
||||
fractalrange = (
|
||||
round_value(limits[0] / grid.dx),
|
||||
round_value(limits[1] / grid.dx),
|
||||
)
|
||||
# xminus surface
|
||||
if xs == volume.xs:
|
||||
logger.exception(
|
||||
@@ -139,12 +152,19 @@ class AddGrass(UserObjectGeometry):
|
||||
|
||||
elif ys == yf:
|
||||
if zs == zf:
|
||||
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
|
||||
logger.exception(
|
||||
f"{self.__str__()} dimensions are not specified correctly"
|
||||
)
|
||||
raise ValueError
|
||||
if ys not in [volume.ys, volume.yf]:
|
||||
logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box")
|
||||
logger.exception(
|
||||
f"{self.__str__()} must specify external surfaces on a fractal box"
|
||||
)
|
||||
raise ValueError
|
||||
fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
|
||||
fractalrange = (
|
||||
round_value(limits[0] / grid.dy),
|
||||
round_value(limits[1] / grid.dy),
|
||||
)
|
||||
# yminus surface
|
||||
if ys == volume.ys:
|
||||
logger.exception(
|
||||
@@ -164,9 +184,14 @@ class AddGrass(UserObjectGeometry):
|
||||
|
||||
elif zs == zf:
|
||||
if zs not in [volume.zs, volume.zf]:
|
||||
logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box")
|
||||
logger.exception(
|
||||
f"{self.__str__()} must specify external surfaces on a fractal box"
|
||||
)
|
||||
raise ValueError
|
||||
fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))
|
||||
fractalrange = (
|
||||
round_value(limits[0] / grid.dz),
|
||||
round_value(limits[1] / grid.dz),
|
||||
)
|
||||
# zminus surface
|
||||
if zs == volume.zs:
|
||||
logger.exception(
|
||||
@@ -219,7 +244,8 @@ class AddGrass(UserObjectGeometry):
|
||||
# probability values, and convert the 1D index back into a x, y index
|
||||
# for the original surface.
|
||||
bladesindex = np.unravel_index(
|
||||
np.digitize(A, probability1D), (surface.fractalsurface.shape[0], surface.fractalsurface.shape[1])
|
||||
np.digitize(A, probability1D),
|
||||
(surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]),
|
||||
)
|
||||
|
||||
# Set the fractal range to minimum and maximum heights of the grass blades
|
||||
@@ -227,7 +253,9 @@ class AddGrass(UserObjectGeometry):
|
||||
|
||||
# Set the fractal surface using the pre-calculated spatial distribution
|
||||
# and a random height
|
||||
surface.fractalsurface = np.zeros((surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]))
|
||||
surface.fractalsurface = np.zeros(
|
||||
(surface.fractalsurface.shape[0], surface.fractalsurface.shape[1])
|
||||
)
|
||||
for i in range(len(bladesindex[0])):
|
||||
surface.fractalsurface[bladesindex[0][i], bladesindex[1][i]] = R.randint(
|
||||
surface.fractalrange[0], surface.fractalrange[1], size=1
|
||||
|
@@ -91,11 +91,15 @@ class AddSurfaceRoughness(UserObjectGeometry):
|
||||
self._do_rotate()
|
||||
|
||||
# Get the correct fractal volume
|
||||
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
|
||||
volumes = [
|
||||
volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id
|
||||
]
|
||||
if volumes:
|
||||
volume = volumes[0]
|
||||
else:
|
||||
logger.exception(f"{self.__str__()} cannot find FractalBox {fractal_box_id}")
|
||||
logger.exception(
|
||||
f"{self.__str__()} cannot find FractalBox {fractal_box_id}"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
|
||||
@@ -103,7 +107,10 @@ class AddSurfaceRoughness(UserObjectGeometry):
|
||||
xf, yf, zf = p2
|
||||
|
||||
if frac_dim < 0:
|
||||
logger.exception(f"{self.__str__()} requires a positive value for the " + "fractal dimension")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a positive value for the "
|
||||
+ "fractal dimension"
|
||||
)
|
||||
raise ValueError
|
||||
if weighting[0] < 0:
|
||||
logger.exception(
|
||||
@@ -121,12 +128,19 @@ class AddSurfaceRoughness(UserObjectGeometry):
|
||||
# Check for valid orientations
|
||||
if xs == xf:
|
||||
if ys == yf or zs == zf:
|
||||
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
|
||||
logger.exception(
|
||||
f"{self.__str__()} dimensions are not specified correctly"
|
||||
)
|
||||
raise ValueError
|
||||
if xs not in [volume.xs, volume.xf]:
|
||||
logger.exception(f"{self.__str__()} can only be used on the external surfaces of a fractal box")
|
||||
logger.exception(
|
||||
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
|
||||
)
|
||||
raise ValueError
|
||||
fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
|
||||
fractalrange = (
|
||||
round_value(limits[0] / grid.dx),
|
||||
round_value(limits[1] / grid.dx),
|
||||
)
|
||||
# xminus surface
|
||||
if xs == volume.xs:
|
||||
if fractalrange[0] < 0 or fractalrange[1] > volume.xf:
|
||||
@@ -152,12 +166,20 @@ class AddSurfaceRoughness(UserObjectGeometry):
|
||||
|
||||
elif ys == yf:
|
||||
if zs == zf:
|
||||
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
|
||||
logger.exception(
|
||||
f"{self.__str__()} dimensions are not specified correctly"
|
||||
)
|
||||
raise ValueError
|
||||
if ys not in [volume.ys, volume.yf]:
|
||||
logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box")
|
||||
logger.exception(
|
||||
f"{self.__str__()} can only be used on the external "
|
||||
+ "surfaces of a fractal box"
|
||||
)
|
||||
raise ValueError
|
||||
fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
|
||||
fractalrange = (
|
||||
round_value(limits[0] / grid.dy),
|
||||
round_value(limits[1] / grid.dy),
|
||||
)
|
||||
# yminus surface
|
||||
if ys == volume.ys:
|
||||
if fractalrange[0] < 0 or fractalrange[1] > volume.yf:
|
||||
@@ -183,9 +205,15 @@ class AddSurfaceRoughness(UserObjectGeometry):
|
||||
|
||||
elif zs == zf:
|
||||
if zs not in [volume.zs, volume.zf]:
|
||||
logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box")
|
||||
logger.exception(
|
||||
f"{self.__str__()} can only be used on the external "
|
||||
+ "surfaces of a fractal box"
|
||||
)
|
||||
raise ValueError
|
||||
fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))
|
||||
fractalrange = (
|
||||
round_value(limits[0] / grid.dz),
|
||||
round_value(limits[1] / grid.dz),
|
||||
)
|
||||
# zminus surface
|
||||
if zs == volume.zs:
|
||||
if fractalrange[0] < 0 or fractalrange[1] > volume.zf:
|
||||
@@ -222,7 +250,9 @@ class AddSurfaceRoughness(UserObjectGeometry):
|
||||
# List of existing surfaces IDs
|
||||
existingsurfaceIDs = [x.surfaceID for x in volume.fractalsurfaces]
|
||||
if surface.surfaceID in existingsurfaceIDs:
|
||||
logger.exception(f"{self.__str__()} has already been used on the {surface.surfaceID} surface")
|
||||
logger.exception(
|
||||
f"{self.__str__()} has already been used on the {surface.surfaceID} surface"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
surface.generate_fractal_surface()
|
||||
|
@@ -74,10 +74,14 @@ class AddSurfaceWater(UserObjectGeometry):
|
||||
if self.do_rotate:
|
||||
self._do_rotate()
|
||||
|
||||
if volumes := [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]:
|
||||
if volumes := [
|
||||
volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id
|
||||
]:
|
||||
volume = volumes[0]
|
||||
else:
|
||||
logger.exception(f"{self.__str__()} cannot find FractalBox {fractal_box_id}")
|
||||
logger.exception(
|
||||
f"{self.__str__()} cannot find FractalBox {fractal_box_id}"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
|
||||
@@ -85,16 +89,22 @@ class AddSurfaceWater(UserObjectGeometry):
|
||||
xf, yf, zf = p2
|
||||
|
||||
if depth <= 0:
|
||||
logger.exception(f"{self.__str__()} requires a positive value for the depth of water")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a positive value for the depth of water"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Check for valid orientations
|
||||
if xs == xf:
|
||||
if ys == yf or zs == zf:
|
||||
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
|
||||
logger.exception(
|
||||
f"{self.__str__()} dimensions are not specified correctly"
|
||||
)
|
||||
raise ValueError
|
||||
if xs not in [volume.xs, volume.xf]:
|
||||
logger.exception(f"{self.__str__()} can only be used on the external surfaces of a fractal box")
|
||||
logger.exception(
|
||||
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
|
||||
)
|
||||
raise ValueError
|
||||
# xminus surface
|
||||
if xs == volume.xs:
|
||||
@@ -107,10 +117,14 @@ class AddSurfaceWater(UserObjectGeometry):
|
||||
|
||||
elif ys == yf:
|
||||
if zs == zf:
|
||||
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
|
||||
logger.exception(
|
||||
f"{self.__str__()} dimensions are not specified correctly"
|
||||
)
|
||||
raise ValueError
|
||||
if ys not in [volume.ys, volume.yf]:
|
||||
logger.exception(f"{self.__str__()} can only be used on the external surfaces of a fractal box")
|
||||
logger.exception(
|
||||
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
|
||||
)
|
||||
raise ValueError
|
||||
# yminus surface
|
||||
if ys == volume.ys:
|
||||
@@ -123,7 +137,9 @@ class AddSurfaceWater(UserObjectGeometry):
|
||||
|
||||
elif zs == zf:
|
||||
if zs not in [volume.zs, volume.zf]:
|
||||
logger.exception(f"{self.__str__()} can only be used on the external surfaces of a fractal box")
|
||||
logger.exception(
|
||||
f"{self.__str__()} can only be used on the external surfaces of a fractal box"
|
||||
)
|
||||
raise ValueError
|
||||
# zminus surface
|
||||
if zs == volume.zs:
|
||||
@@ -138,7 +154,9 @@ class AddSurfaceWater(UserObjectGeometry):
|
||||
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
|
||||
raise ValueError
|
||||
|
||||
surface = next((x for x in volume.fractalsurfaces if x.surfaceID == requestedsurface), None)
|
||||
surface = next(
|
||||
(x for x in volume.fractalsurfaces if x.surfaceID == requestedsurface), None
|
||||
)
|
||||
if not surface:
|
||||
logger.exception(
|
||||
f"{self.__str__()} specified surface {requestedsurface} does not have a rough surface applied"
|
||||
@@ -148,7 +166,10 @@ class AddSurfaceWater(UserObjectGeometry):
|
||||
surface.filldepth = filldepthcells
|
||||
|
||||
# Check that requested fill depth falls within range of surface roughness
|
||||
if surface.filldepth < surface.fractalrange[0] or surface.filldepth > surface.fractalrange[1]:
|
||||
if (
|
||||
surface.filldepth < surface.fractalrange[0]
|
||||
or surface.filldepth > surface.fractalrange[1]
|
||||
):
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a value for the depth of water that lies with the "
|
||||
f"range of the requested surface roughness"
|
||||
|
@@ -24,8 +24,7 @@ import gprMax.config as config
|
||||
|
||||
from ..cython.geometry_primitives import build_box
|
||||
from ..materials import Material
|
||||
from .cmds_geometry import (UserObjectGeometry, check_averaging,
|
||||
rotate_2point_object)
|
||||
from .cmds_geometry import UserObjectGeometry, check_averaging, rotate_2point_object
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -128,10 +127,18 @@ class Box(UserObjectGeometry):
|
||||
m = Material(numID, requiredID)
|
||||
m.type = "dielectric-smoothed"
|
||||
# Create dielectric-smoothed constituents for material
|
||||
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
|
||||
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
|
||||
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
|
||||
m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0)
|
||||
m.er = np.mean(
|
||||
(materials[0].er, materials[1].er, materials[2].er), axis=0
|
||||
)
|
||||
m.se = np.mean(
|
||||
(materials[0].se, materials[1].se, materials[2].se), axis=0
|
||||
)
|
||||
m.mr = np.mean(
|
||||
(materials[0].mr, materials[1].mr, materials[2].mr), axis=0
|
||||
)
|
||||
m.sm = np.mean(
|
||||
(materials[0].sm, materials[1].sm, materials[2].sm), axis=0
|
||||
)
|
||||
|
||||
# Append the new material object to the materials list
|
||||
grid.materials.append(m)
|
||||
|
@@ -55,7 +55,9 @@ class Cone(UserObjectGeometry):
|
||||
r1 = self.kwargs["r1"]
|
||||
r2 = self.kwargs["r2"]
|
||||
except KeyError:
|
||||
logger.exception(f"{self.__str__()} please specify two points and two radii")
|
||||
logger.exception(
|
||||
f"{self.__str__()} please specify two points and two radii"
|
||||
)
|
||||
raise
|
||||
|
||||
# Check averaging
|
||||
@@ -86,11 +88,15 @@ class Cone(UserObjectGeometry):
|
||||
x2, y2, z2 = uip.round_to_grid(p2)
|
||||
|
||||
if r1 < 0:
|
||||
logger.exception(f"{self.__str__()} the radius of the first face {r1:g} should be a positive value.")
|
||||
logger.exception(
|
||||
f"{self.__str__()} the radius of the first face {r1:g} should be a positive value."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
if r2 < 0:
|
||||
logger.exception(f"{self.__str__()} the radius of the second face {r2:g} should be a positive value.")
|
||||
logger.exception(
|
||||
f"{self.__str__()} the radius of the second face {r2:g} should be a positive value."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
if r1 == 0 and r2 == 0:
|
||||
@@ -125,10 +131,18 @@ class Cone(UserObjectGeometry):
|
||||
m = Material(numID, requiredID)
|
||||
m.type = "dielectric-smoothed"
|
||||
# Create dielectric-smoothed constituents for material
|
||||
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
|
||||
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
|
||||
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
|
||||
m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0)
|
||||
m.er = np.mean(
|
||||
(materials[0].er, materials[1].er, materials[2].er), axis=0
|
||||
)
|
||||
m.se = np.mean(
|
||||
(materials[0].se, materials[1].se, materials[2].se), axis=0
|
||||
)
|
||||
m.mr = np.mean(
|
||||
(materials[0].mr, materials[1].mr, materials[2].mr), axis=0
|
||||
)
|
||||
m.sm = np.mean(
|
||||
(materials[0].sm, materials[1].sm, materials[2].sm), axis=0
|
||||
)
|
||||
|
||||
# Append the new material object to the materials list
|
||||
grid.materials.append(m)
|
||||
|
@@ -83,7 +83,9 @@ class Cylinder(UserObjectGeometry):
|
||||
x2, y2, z2 = uip.round_to_grid(p2)
|
||||
|
||||
if r <= 0:
|
||||
logger.exception(f"{self.__str__()} the radius {r:g} should be a positive value.")
|
||||
logger.exception(
|
||||
f"{self.__str__()} the radius {r:g} should be a positive value."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Look up requested materials in existing list of material instances
|
||||
@@ -114,10 +116,18 @@ class Cylinder(UserObjectGeometry):
|
||||
m = Material(numID, requiredID)
|
||||
m.type = "dielectric-smoothed"
|
||||
# Create dielectric-smoothed constituents for material
|
||||
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
|
||||
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
|
||||
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
|
||||
m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0)
|
||||
m.er = np.mean(
|
||||
(materials[0].er, materials[1].er, materials[2].er), axis=0
|
||||
)
|
||||
m.se = np.mean(
|
||||
(materials[0].se, materials[1].se, materials[2].se), axis=0
|
||||
)
|
||||
m.mr = np.mean(
|
||||
(materials[0].mr, materials[1].mr, materials[2].mr), axis=0
|
||||
)
|
||||
m.sm = np.mean(
|
||||
(materials[0].sm, materials[1].sm, materials[2].sm), axis=0
|
||||
)
|
||||
|
||||
# Append the new material object to the materials list
|
||||
grid.materials.append(m)
|
||||
|
@@ -95,15 +95,23 @@ class CylindricalSector(UserObjectGeometry):
|
||||
sectorangle = 2 * np.pi * (end / 360)
|
||||
|
||||
if normal not in ["x", "y", "z"]:
|
||||
logger.exception(f"{self.__str__()} the normal direction must be either x, y or z.")
|
||||
logger.exception(
|
||||
f"{self.__str__()} the normal direction must be either x, y or z."
|
||||
)
|
||||
raise ValueError
|
||||
if r <= 0:
|
||||
logger.exception(f"{self.__str__()} the radius {r:g} should be a positive value.")
|
||||
logger.exception(
|
||||
f"{self.__str__()} the radius {r:g} should be a positive value."
|
||||
)
|
||||
if sectorstartangle < 0 or sectorangle <= 0:
|
||||
logger.exception(f"{self.__str__()} the starting angle and sector angle should be a positive values.")
|
||||
logger.exception(
|
||||
f"{self.__str__()} the starting angle and sector angle should be a positive values."
|
||||
)
|
||||
raise ValueError
|
||||
if sectorstartangle >= 2 * np.pi or sectorangle >= 2 * np.pi:
|
||||
logger.exception(f"{self.__str__()} the starting angle and sector angle must be less than 360 degrees.")
|
||||
logger.exception(
|
||||
f"{self.__str__()} the starting angle and sector angle must be less than 360 degrees."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Look up requested materials in existing list of material instances
|
||||
@@ -134,10 +142,18 @@ class CylindricalSector(UserObjectGeometry):
|
||||
m = Material(numID, requiredID)
|
||||
m.type = "dielectric-smoothed"
|
||||
# Create dielectric-smoothed constituents for material
|
||||
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
|
||||
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
|
||||
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
|
||||
m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0)
|
||||
m.er = np.mean(
|
||||
(materials[0].er, materials[1].er, materials[2].er), axis=0
|
||||
)
|
||||
m.se = np.mean(
|
||||
(materials[0].se, materials[1].se, materials[2].se), axis=0
|
||||
)
|
||||
m.mr = np.mean(
|
||||
(materials[0].mr, materials[1].mr, materials[2].mr), axis=0
|
||||
)
|
||||
m.sm = np.mean(
|
||||
(materials[0].sm, materials[1].sm, materials[2].sm), axis=0
|
||||
)
|
||||
|
||||
# Append the new material object to the materials list
|
||||
grid.materials.append(m)
|
||||
|
@@ -20,8 +20,7 @@ import logging
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..cython.geometry_primitives import (build_edge_x, build_edge_y,
|
||||
build_edge_z)
|
||||
from ..cython.geometry_primitives import build_edge_x, build_edge_y, build_edge_z
|
||||
from .cmds_geometry import UserObjectGeometry, rotate_2point_object
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -92,15 +91,21 @@ class Edge(UserObjectGeometry):
|
||||
raise ValueError
|
||||
elif xs != xf:
|
||||
for i in range(xs, xf):
|
||||
build_edge_x(i, ys, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID)
|
||||
build_edge_x(
|
||||
i, ys, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID
|
||||
)
|
||||
|
||||
elif ys != yf:
|
||||
for j in range(ys, yf):
|
||||
build_edge_y(xs, j, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID)
|
||||
build_edge_y(
|
||||
xs, j, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID
|
||||
)
|
||||
|
||||
elif zs != zf:
|
||||
for k in range(zs, zf):
|
||||
build_edge_z(xs, ys, k, material.numID, grid.rigidE, grid.rigidH, grid.ID)
|
||||
build_edge_z(
|
||||
xs, ys, k, material.numID, grid.rigidE, grid.rigidH, grid.ID
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"{self.grid_name(grid)}Edge from {p3[0]:g}m, {p3[1]:g}m, "
|
||||
|
@@ -53,7 +53,9 @@ class Ellipsoid(UserObjectGeometry):
|
||||
zr = self.kwargs["zr"]
|
||||
|
||||
except KeyError:
|
||||
logger.exception(f"{self.__str__()} please specify a point and the three semiaxes.")
|
||||
logger.exception(
|
||||
f"{self.__str__()} please specify a point and the three semiaxes."
|
||||
)
|
||||
raise
|
||||
|
||||
# Check averaging
|
||||
@@ -109,10 +111,18 @@ class Ellipsoid(UserObjectGeometry):
|
||||
m = Material(numID, requiredID)
|
||||
m.type = "dielectric-smoothed"
|
||||
# Create dielectric-smoothed constituents for material
|
||||
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
|
||||
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
|
||||
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
|
||||
m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0)
|
||||
m.er = np.mean(
|
||||
(materials[0].er, materials[1].er, materials[2].er), axis=0
|
||||
)
|
||||
m.se = np.mean(
|
||||
(materials[0].se, materials[1].se, materials[2].se), axis=0
|
||||
)
|
||||
m.mr = np.mean(
|
||||
(materials[0].mr, materials[1].mr, materials[2].mr), axis=0
|
||||
)
|
||||
m.sm = np.mean(
|
||||
(materials[0].sm, materials[1].sm, materials[2].sm), axis=0
|
||||
)
|
||||
|
||||
# Append the new material object to the materials list
|
||||
grid.materials.append(m)
|
||||
|
@@ -21,13 +21,14 @@ import logging
|
||||
import numpy as np
|
||||
|
||||
import gprMax.config as config
|
||||
from gprMax.cmds_geometry.cmds_geometry import (UserObjectGeometry,
|
||||
rotate_2point_object)
|
||||
from gprMax.cmds_geometry.cmds_geometry import UserObjectGeometry, rotate_2point_object
|
||||
from gprMax.fractals import FractalVolume
|
||||
from gprMax.materials import ListMaterial
|
||||
|
||||
from ..cython.geometry_primitives import (build_voxels_from_array,
|
||||
build_voxels_from_array_mask)
|
||||
from ..cython.geometry_primitives import (
|
||||
build_voxels_from_array,
|
||||
build_voxels_from_array_mask,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -118,40 +119,59 @@ class FractalBox(UserObjectGeometry):
|
||||
xf, yf, zf = p2
|
||||
|
||||
if frac_dim < 0:
|
||||
logger.exception(f"{self.__str__()} requires a positive value for the fractal dimension")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a positive value for the fractal dimension"
|
||||
)
|
||||
raise ValueError
|
||||
if weighting[0] < 0:
|
||||
logger.exception(f"{self.__str__()} requires a positive value for the fractal weighting in the x direction")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a positive value for the fractal weighting in the x direction"
|
||||
)
|
||||
raise ValueError
|
||||
if weighting[1] < 0:
|
||||
logger.exception(f"{self.__str__()} requires a positive value for the fractal weighting in the y direction")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a positive value for the fractal weighting in the y direction"
|
||||
)
|
||||
raise ValueError
|
||||
if weighting[2] < 0:
|
||||
logger.exception(f"{self.__str__()} requires a positive value for the fractal weighting in the z direction")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a positive value for the fractal weighting in the z direction"
|
||||
)
|
||||
if n_materials < 0:
|
||||
logger.exception(f"{self.__str__()} requires a positive value for the number of bins")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a positive value for the number of bins"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Find materials to use to build fractal volume, either from mixing
|
||||
# models or normal materials.
|
||||
mixingmodel = next((x for x in grid.mixingmodels if x.ID == mixing_model_id), None)
|
||||
mixingmodel = next(
|
||||
(x for x in grid.mixingmodels if x.ID == mixing_model_id), None
|
||||
)
|
||||
material = next((x for x in grid.materials if x.ID == mixing_model_id), None)
|
||||
nbins = n_materials
|
||||
|
||||
if mixingmodel:
|
||||
if nbins == 1:
|
||||
logger.exception(f"{self.__str__()} must be used with more than one material from the mixing model.")
|
||||
logger.exception(
|
||||
f"{self.__str__()} must be used with more than one material from the mixing model."
|
||||
)
|
||||
raise ValueError
|
||||
if isinstance(mixingmodel, ListMaterial) and nbins > len(mixingmodel.mat):
|
||||
logger.exception(
|
||||
f"{self.__str__()} too many materials/bins " "requested compared to materials in " "mixing model."
|
||||
f"{self.__str__()} too many materials/bins "
|
||||
"requested compared to materials in "
|
||||
"mixing model."
|
||||
)
|
||||
raise ValueError
|
||||
# Create materials from mixing model as number of bins now known
|
||||
# from fractal_box command.
|
||||
mixingmodel.calculate_properties(nbins, grid)
|
||||
elif not material:
|
||||
logger.exception(f"{self.__str__()} mixing model or material with " + "ID {mixing_model_id} does not exist")
|
||||
logger.exception(
|
||||
f"{self.__str__()} mixing model or material with "
|
||||
+ "ID {mixing_model_id} does not exist"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
self.volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim, seed)
|
||||
@@ -223,7 +243,11 @@ class FractalBox(UserObjectGeometry):
|
||||
(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)
|
||||
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()
|
||||
@@ -231,7 +255,9 @@ class FractalBox(UserObjectGeometry):
|
||||
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.fractalvolume[i, j, k] = (
|
||||
self.volume.mixingmodel.matID[int(numberinbin)]
|
||||
)
|
||||
|
||||
self.volume.generate_volume_mask()
|
||||
|
||||
@@ -240,32 +266,69 @@ class FractalBox(UserObjectGeometry):
|
||||
# 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 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]:
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
] = 1
|
||||
elif surface.filldepth > 0 and i < surface.filldepth:
|
||||
elif (
|
||||
surface.filldepth > 0 and i > surface.filldepth
|
||||
):
|
||||
self.volume.mask[
|
||||
i - self.volume.xs, j - self.volume.ys, k - self.volume.zs
|
||||
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
|
||||
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]
|
||||
@@ -273,17 +336,31 @@ class FractalBox(UserObjectGeometry):
|
||||
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]
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
]
|
||||
!= 1
|
||||
):
|
||||
y, z = g.calculate_blade_geometry(blade, height)
|
||||
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)
|
||||
@@ -297,7 +374,9 @@ class FractalBox(UserObjectGeometry):
|
||||
):
|
||||
break
|
||||
else:
|
||||
self.volume.mask[i - self.volume.xs, yy, zz] = 3
|
||||
self.volume.mask[
|
||||
i - self.volume.xs, yy, zz
|
||||
] = 3
|
||||
height += 1
|
||||
blade += 1
|
||||
|
||||
@@ -305,7 +384,12 @@ class FractalBox(UserObjectGeometry):
|
||||
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:
|
||||
if (
|
||||
surface.fractalsurface[
|
||||
j - surface.ys, k - surface.zs
|
||||
]
|
||||
> 0
|
||||
):
|
||||
depth = 0
|
||||
i = self.volume.xf - 1
|
||||
while i > self.volume.xs:
|
||||
@@ -313,15 +397,21 @@ class FractalBox(UserObjectGeometry):
|
||||
i
|
||||
> self.volume.originalxf
|
||||
- (
|
||||
surface.fractalsurface[j - surface.ys, k - surface.zs]
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
]
|
||||
== 1
|
||||
):
|
||||
y, z = g.calculate_root_geometry(root, depth)
|
||||
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)
|
||||
@@ -335,38 +425,77 @@ class FractalBox(UserObjectGeometry):
|
||||
):
|
||||
break
|
||||
else:
|
||||
self.volume.mask[i - self.volume.xs, yy, zz] = 3
|
||||
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 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
|
||||
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
|
||||
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 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]:
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
] = 1
|
||||
elif surface.filldepth > 0 and j < surface.filldepth:
|
||||
elif (
|
||||
surface.filldepth > 0
|
||||
and j < surface.filldepth
|
||||
):
|
||||
self.volume.mask[
|
||||
i - self.volume.xs, j - self.volume.ys, k - self.volume.zs
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
] = 0
|
||||
elif surface.ID == "grass":
|
||||
g = surface.grass[0]
|
||||
@@ -374,17 +503,31 @@ class FractalBox(UserObjectGeometry):
|
||||
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]
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
]
|
||||
!= 1
|
||||
):
|
||||
x, z = g.calculate_blade_geometry(blade, height)
|
||||
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)
|
||||
@@ -398,7 +541,9 @@ class FractalBox(UserObjectGeometry):
|
||||
):
|
||||
break
|
||||
else:
|
||||
self.volume.mask[xx, j - self.volume.ys, zz] = 3
|
||||
self.volume.mask[
|
||||
xx, j - self.volume.ys, zz
|
||||
] = 3
|
||||
height += 1
|
||||
blade += 1
|
||||
|
||||
@@ -406,7 +551,12 @@ class FractalBox(UserObjectGeometry):
|
||||
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:
|
||||
if (
|
||||
surface.fractalsurface[
|
||||
i - surface.xs, k - surface.zs
|
||||
]
|
||||
> 0
|
||||
):
|
||||
depth = 0
|
||||
j = self.volume.yf - 1
|
||||
while j > self.volume.ys:
|
||||
@@ -414,15 +564,21 @@ class FractalBox(UserObjectGeometry):
|
||||
j
|
||||
> self.volume.originalyf
|
||||
- (
|
||||
surface.fractalsurface[i - surface.xs, k - surface.zs]
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
]
|
||||
== 1
|
||||
):
|
||||
x, z = g.calculate_root_geometry(root, depth)
|
||||
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)
|
||||
@@ -436,7 +592,9 @@ class FractalBox(UserObjectGeometry):
|
||||
):
|
||||
break
|
||||
else:
|
||||
self.volume.mask[xx, j - self.volume.ys, zz] = 3
|
||||
self.volume.mask[
|
||||
xx, j - self.volume.ys, zz
|
||||
] = 3
|
||||
depth += 1
|
||||
j -= 1
|
||||
root += 1
|
||||
@@ -444,30 +602,67 @@ class FractalBox(UserObjectGeometry):
|
||||
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
|
||||
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
|
||||
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]:
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
] = 1
|
||||
elif surface.filldepth > 0 and k < surface.filldepth:
|
||||
elif (
|
||||
surface.filldepth > 0
|
||||
and k < surface.filldepth
|
||||
):
|
||||
self.volume.mask[
|
||||
i - self.volume.xs, j - self.volume.ys, k - self.volume.zs
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
] = 0
|
||||
elif surface.ID == "grass":
|
||||
g = surface.grass[0]
|
||||
@@ -475,17 +670,31 @@ class FractalBox(UserObjectGeometry):
|
||||
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]
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
]
|
||||
!= 1
|
||||
):
|
||||
x, y = g.calculate_blade_geometry(blade, height)
|
||||
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)
|
||||
@@ -499,7 +708,9 @@ class FractalBox(UserObjectGeometry):
|
||||
):
|
||||
break
|
||||
else:
|
||||
self.volume.mask[xx, yy, k - self.volume.zs] = 3
|
||||
self.volume.mask[
|
||||
xx, yy, k - self.volume.zs
|
||||
] = 3
|
||||
height += 1
|
||||
blade += 1
|
||||
|
||||
@@ -507,7 +718,12 @@ class FractalBox(UserObjectGeometry):
|
||||
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:
|
||||
if (
|
||||
surface.fractalsurface[
|
||||
i - surface.xs, j - surface.ys
|
||||
]
|
||||
> 0
|
||||
):
|
||||
depth = 0
|
||||
k = self.volume.zf - 1
|
||||
while k > self.volume.zs:
|
||||
@@ -515,15 +731,21 @@ class FractalBox(UserObjectGeometry):
|
||||
k
|
||||
> self.volume.originalzf
|
||||
- (
|
||||
surface.fractalsurface[i - surface.xs, j - surface.ys]
|
||||
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
|
||||
i - self.volume.xs,
|
||||
j - self.volume.ys,
|
||||
k - self.volume.zs,
|
||||
]
|
||||
== 1
|
||||
):
|
||||
x, y = g.calculate_root_geometry(root, depth)
|
||||
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)
|
||||
@@ -537,14 +759,20 @@ class FractalBox(UserObjectGeometry):
|
||||
):
|
||||
break
|
||||
else:
|
||||
self.volume.mask[xx, yy, k - self.volume.zs] = 3
|
||||
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)
|
||||
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(
|
||||
@@ -576,7 +804,9 @@ class FractalBox(UserObjectGeometry):
|
||||
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.fractalvolume[i, j, k] = (
|
||||
self.volume.mixingmodel.matID[int(numberinbin)]
|
||||
)
|
||||
|
||||
data = self.volume.fractalvolume.astype("int16", order="C")
|
||||
build_voxels_from_array(
|
||||
|
@@ -71,7 +71,11 @@ class GeometryObjectsRead(UserObjectGeometry):
|
||||
materials = [
|
||||
line.rstrip() + "{" + matstr + "}\n"
|
||||
for line in f
|
||||
if (line.startswith("#") and not line.startswith("##") and line.rstrip("\n"))
|
||||
if (
|
||||
line.startswith("#")
|
||||
and not line.startswith("##")
|
||||
and line.rstrip("\n")
|
||||
)
|
||||
]
|
||||
|
||||
# Build scene
|
||||
@@ -126,12 +130,26 @@ class GeometryObjectsRead(UserObjectGeometry):
|
||||
rigidE = f["/rigidE"][:]
|
||||
rigidH = f["/rigidH"][:]
|
||||
ID = f["/ID"][:]
|
||||
grid.solid[xs : xs + data.shape[0], ys : ys + data.shape[1], zs : zs + data.shape[2]] = (
|
||||
data + numexistmaterials
|
||||
)
|
||||
grid.rigidE[:, xs : xs + rigidE.shape[1], ys : ys + rigidE.shape[2], zs : zs + rigidE.shape[3]] = rigidE
|
||||
grid.rigidH[:, xs : xs + rigidH.shape[1], ys : ys + rigidH.shape[2], zs : zs + rigidH.shape[3]] = rigidH
|
||||
grid.ID[:, xs : xs + ID.shape[1], ys : ys + ID.shape[2], zs : zs + ID.shape[3]] = ID + numexistmaterials
|
||||
grid.solid[
|
||||
xs : xs + data.shape[0],
|
||||
ys : ys + data.shape[1],
|
||||
zs : zs + data.shape[2],
|
||||
] = data + numexistmaterials
|
||||
grid.rigidE[
|
||||
:,
|
||||
xs : xs + rigidE.shape[1],
|
||||
ys : ys + rigidE.shape[2],
|
||||
zs : zs + rigidE.shape[3],
|
||||
] = rigidE
|
||||
grid.rigidH[
|
||||
:,
|
||||
xs : xs + rigidH.shape[1],
|
||||
ys : ys + rigidH.shape[2],
|
||||
zs : zs + rigidH.shape[3],
|
||||
] = rigidH
|
||||
grid.ID[
|
||||
:, xs : xs + ID.shape[1], ys : ys + ID.shape[2], zs : zs + ID.shape[3]
|
||||
] = ID + numexistmaterials
|
||||
logger.info(
|
||||
f"{self.grid_name(grid)}Geometry objects from file {geofile} "
|
||||
f"inserted at {xs * grid.dx:g}m, {ys * grid.dy:g}m, "
|
||||
|
@@ -20,8 +20,7 @@ import logging
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..cython.geometry_primitives import (build_face_xy, build_face_xz,
|
||||
build_face_yz)
|
||||
from ..cython.geometry_primitives import build_face_xy, build_face_xz, build_face_yz
|
||||
from .cmds_geometry import UserObjectGeometry, rotate_2point_object
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -115,7 +114,9 @@ class Plate(UserObjectGeometry):
|
||||
|
||||
for j in range(ys, yf):
|
||||
for k in range(zs, zf):
|
||||
build_face_yz(xs, j, k, numIDy, numIDz, grid.rigidE, grid.rigidH, grid.ID)
|
||||
build_face_yz(
|
||||
xs, j, k, numIDy, numIDz, grid.rigidE, grid.rigidH, grid.ID
|
||||
)
|
||||
|
||||
# xz-plane plate
|
||||
elif ys == yf:
|
||||
@@ -130,7 +131,9 @@ class Plate(UserObjectGeometry):
|
||||
|
||||
for i in range(xs, xf):
|
||||
for k in range(zs, zf):
|
||||
build_face_xz(i, ys, k, numIDx, numIDz, grid.rigidE, grid.rigidH, grid.ID)
|
||||
build_face_xz(
|
||||
i, ys, k, numIDx, numIDz, grid.rigidE, grid.rigidH, grid.ID
|
||||
)
|
||||
|
||||
# xy-plane plate
|
||||
elif zs == zf:
|
||||
@@ -145,7 +148,9 @@ class Plate(UserObjectGeometry):
|
||||
|
||||
for i in range(xs, xf):
|
||||
for j in range(ys, yf):
|
||||
build_face_xy(i, j, zs, numIDx, numIDy, grid.rigidE, grid.rigidH, grid.ID)
|
||||
build_face_xy(
|
||||
i, j, zs, numIDx, numIDy, grid.rigidE, grid.rigidH, grid.ID
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"{self.grid_name(grid)}Plate from {p3[0]:g}m, {p3[1]:g}m, "
|
||||
|
@@ -104,10 +104,18 @@ class Sphere(UserObjectGeometry):
|
||||
m = Material(numID, requiredID)
|
||||
m.type = "dielectric-smoothed"
|
||||
# Create dielectric-smoothed constituents for material
|
||||
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
|
||||
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
|
||||
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
|
||||
m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0)
|
||||
m.er = np.mean(
|
||||
(materials[0].er, materials[1].er, materials[2].er), axis=0
|
||||
)
|
||||
m.se = np.mean(
|
||||
(materials[0].se, materials[1].se, materials[2].se), axis=0
|
||||
)
|
||||
m.mr = np.mean(
|
||||
(materials[0].mr, materials[1].mr, materials[2].mr), axis=0
|
||||
)
|
||||
m.sm = np.mean(
|
||||
(materials[0].sm, materials[1].sm, materials[2].sm), axis=0
|
||||
)
|
||||
|
||||
# Append the new material object to the materials list
|
||||
grid.materials.append(m)
|
||||
|
@@ -109,7 +109,9 @@ class Triangle(UserObjectGeometry):
|
||||
x3, y3, z3 = uip.round_to_grid(up3)
|
||||
|
||||
if thickness < 0:
|
||||
logger.exception(f"{self.__str__()} requires a positive value for thickness")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires a positive value for thickness"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Check for valid orientations
|
||||
@@ -123,7 +125,9 @@ class Triangle(UserObjectGeometry):
|
||||
elif z1 == z2 == z3:
|
||||
normal = "z"
|
||||
else:
|
||||
logger.exception(f"{self.__str__()} the triangle is not specified correctly")
|
||||
logger.exception(
|
||||
f"{self.__str__()} the triangle is not specified correctly"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Look up requested materials in existing list of material instances
|
||||
@@ -146,7 +150,9 @@ class Triangle(UserObjectGeometry):
|
||||
numIDx = materials[0].numID
|
||||
numIDy = materials[1].numID
|
||||
numIDz = materials[2].numID
|
||||
requiredID = materials[0].ID + "+" + materials[1].ID + "+" + materials[2].ID
|
||||
requiredID = (
|
||||
materials[0].ID + "+" + materials[1].ID + "+" + materials[2].ID
|
||||
)
|
||||
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
|
||||
if averagedmaterial:
|
||||
numID = averagedmaterial.numID
|
||||
@@ -155,10 +161,18 @@ class Triangle(UserObjectGeometry):
|
||||
m = Material(numID, requiredID)
|
||||
m.type = "dielectric-smoothed"
|
||||
# Create dielectric-smoothed constituents for material
|
||||
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
|
||||
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
|
||||
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
|
||||
m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0)
|
||||
m.er = np.mean(
|
||||
(materials[0].er, materials[1].er, materials[2].er), axis=0
|
||||
)
|
||||
m.se = np.mean(
|
||||
(materials[0].se, materials[1].se, materials[2].se), axis=0
|
||||
)
|
||||
m.mr = np.mean(
|
||||
(materials[0].mr, materials[1].mr, materials[2].mr), axis=0
|
||||
)
|
||||
m.sm = np.mean(
|
||||
(materials[0].sm, materials[1].sm, materials[2].sm), axis=0
|
||||
)
|
||||
|
||||
# Append the new material object to the materials list
|
||||
grid.materials.append(m)
|
||||
|
@@ -25,9 +25,11 @@ from scipy import interpolate
|
||||
|
||||
import gprMax.config as config
|
||||
|
||||
from .cmds_geometry.cmds_geometry import (UserObjectGeometry,
|
||||
from .cmds_geometry.cmds_geometry import (
|
||||
UserObjectGeometry,
|
||||
rotate_2point_object,
|
||||
rotate_polarisation)
|
||||
rotate_polarisation,
|
||||
)
|
||||
from .geometry_outputs import GeometryObjects as GeometryObjectsUser
|
||||
from .materials import DispersiveMaterial as DispersiveMaterialUser
|
||||
from .materials import ListMaterial as ListMaterialUser
|
||||
@@ -118,16 +120,22 @@ class ExcitationFile(UserObjectMulti):
|
||||
try:
|
||||
excitationfile = self.kwargs["filepath"]
|
||||
fullargspec = inspect.getfullargspec(interpolate.interp1d)
|
||||
kwargs = dict(zip(reversed(fullargspec.args), reversed(fullargspec.defaults)))
|
||||
kwargs = dict(
|
||||
zip(reversed(fullargspec.args), reversed(fullargspec.defaults))
|
||||
)
|
||||
except KeyError:
|
||||
logger.exception(f"{self.__str__()} requires either one or three parameter(s)")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires either one or three parameter(s)"
|
||||
)
|
||||
raise
|
||||
|
||||
# See if file exists at specified path and if not try input file directory
|
||||
excitationfile = Path(excitationfile)
|
||||
# excitationfile = excitationfile.resolve()
|
||||
if not excitationfile.exists():
|
||||
excitationfile = Path(config.sim_config.input_file_path.parent, excitationfile)
|
||||
excitationfile = Path(
|
||||
config.sim_config.input_file_path.parent, excitationfile
|
||||
)
|
||||
|
||||
logger.info(self.grid_name(grid) + f"Excitation file: {excitationfile}")
|
||||
|
||||
@@ -135,7 +143,11 @@ class ExcitationFile(UserObjectMulti):
|
||||
waveformIDs = np.loadtxt(excitationfile, max_rows=1, dtype=str)
|
||||
|
||||
# Read all waveform values into an array
|
||||
waveformvalues = np.loadtxt(excitationfile, skiprows=1, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
waveformvalues = np.loadtxt(
|
||||
excitationfile,
|
||||
skiprows=1,
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
|
||||
# Time array (if specified) for interpolation, otherwise use simulation time
|
||||
if waveformIDs[0].lower() == "time":
|
||||
@@ -156,7 +168,11 @@ class ExcitationFile(UserObjectMulti):
|
||||
w.type = "user"
|
||||
|
||||
# Select correct column of waveform values depending on array shape
|
||||
singlewaveformvalues = waveformvalues[:] if len(waveformvalues.shape) == 1 else waveformvalues[:, i]
|
||||
singlewaveformvalues = (
|
||||
waveformvalues[:]
|
||||
if len(waveformvalues.shape) == 1
|
||||
else waveformvalues[:, i]
|
||||
)
|
||||
|
||||
# Truncate waveform array if it is longer than time array
|
||||
if len(singlewaveformvalues) > len(waveformtime):
|
||||
@@ -171,10 +187,13 @@ class ExcitationFile(UserObjectMulti):
|
||||
)
|
||||
|
||||
# Interpolate waveform values
|
||||
w.userfunc = interpolate.interp1d(waveformtime, singlewaveformvalues, **kwargs)
|
||||
w.userfunc = interpolate.interp1d(
|
||||
waveformtime, singlewaveformvalues, **kwargs
|
||||
)
|
||||
|
||||
logger.info(
|
||||
self.grid_name(grid) + f"User waveform {w.ID} created using {timestr} and, if "
|
||||
self.grid_name(grid)
|
||||
+ f"User waveform {w.ID} created using {timestr} and, if "
|
||||
f"required, interpolation parameters (kind: {kwargs['kind']}, "
|
||||
f"fill value: {kwargs['fill_value']})."
|
||||
)
|
||||
@@ -222,15 +241,19 @@ class Waveform(UserObjectMulti):
|
||||
freq = self.kwargs["freq"]
|
||||
ID = self.kwargs["id"]
|
||||
except KeyError:
|
||||
logger.exception(self.params_str() + (" builtin waveforms " "require exactly four parameters."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" builtin waveforms require exactly four parameters.")
|
||||
)
|
||||
raise
|
||||
if freq <= 0:
|
||||
logger.exception(
|
||||
self.params_str() + (" requires an excitation " "frequency value of greater than zero.")
|
||||
self.params_str()
|
||||
+ (" requires an excitation frequency value of greater than zero.")
|
||||
)
|
||||
raise ValueError
|
||||
if any(x.ID == ID for x in grid.waveforms):
|
||||
logger.exception(self.params_str() + (f" with ID {ID} already " "exists."))
|
||||
logger.exception(self.params_str() + (f" with ID {ID} already exists."))
|
||||
raise ValueError
|
||||
|
||||
w = WaveformUser()
|
||||
@@ -253,9 +276,14 @@ class Waveform(UserObjectMulti):
|
||||
uservalues = self.kwargs["user_values"]
|
||||
ID = self.kwargs["id"]
|
||||
fullargspec = inspect.getfullargspec(interpolate.interp1d)
|
||||
kwargs = dict(zip(reversed(fullargspec.args), reversed(fullargspec.defaults)))
|
||||
kwargs = dict(
|
||||
zip(reversed(fullargspec.args), reversed(fullargspec.defaults))
|
||||
)
|
||||
except KeyError:
|
||||
logger.exception(self.params_str() + (" a user-defined " "waveform requires at least two parameters."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" a user-defined waveform requires at least two parameters.")
|
||||
)
|
||||
raise
|
||||
|
||||
if "user_time" in self.kwargs:
|
||||
@@ -270,7 +298,7 @@ class Waveform(UserObjectMulti):
|
||||
kwargs["fill_value"] = self.kwargs["fill_value"]
|
||||
|
||||
if any(x.ID == ID for x in grid.waveforms):
|
||||
logger.exception(self.params_str() + (f" with ID {ID} already " "exists."))
|
||||
logger.exception(self.params_str() + (f" with ID {ID} already exists."))
|
||||
raise ValueError
|
||||
|
||||
w = WaveformUser()
|
||||
@@ -278,7 +306,10 @@ class Waveform(UserObjectMulti):
|
||||
w.type = wavetype
|
||||
w.userfunc = interpolate.interp1d(waveformtime, uservalues, **kwargs)
|
||||
|
||||
logger.info(self.grid_name(grid) + (f"Waveform {w.ID} that is " "user-defined created."))
|
||||
logger.info(
|
||||
self.grid_name(grid)
|
||||
+ (f"Waveform {w.ID} that is user-defined created.")
|
||||
)
|
||||
|
||||
grid.waveforms.append(w)
|
||||
|
||||
@@ -323,7 +354,7 @@ class VoltageSource(UserObjectMulti):
|
||||
resistance = self.kwargs["resistance"]
|
||||
waveform_id = self.kwargs["waveform_id"]
|
||||
except KeyError:
|
||||
logger.exception(self.params_str() + (" requires at least six " "parameters."))
|
||||
logger.exception(self.params_str() + (" requires at least six parameters."))
|
||||
raise
|
||||
|
||||
if self.do_rotate:
|
||||
@@ -331,37 +362,49 @@ class VoltageSource(UserObjectMulti):
|
||||
|
||||
# Check polarity & position parameters
|
||||
if polarisation not in ("x", "y", "z"):
|
||||
logger.exception(self.params_str() + (" polarisation must be " "x, y, or z."))
|
||||
logger.exception(self.params_str() + (" polarisation must be x, y, or z."))
|
||||
raise ValueError
|
||||
if "2D TMx" in config.get_model_config().mode and polarisation in [
|
||||
"y",
|
||||
"z",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be x in " "2D TMx mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be x in 2D TMx mode.")
|
||||
)
|
||||
raise ValueError
|
||||
elif "2D TMy" in config.get_model_config().mode and polarisation in [
|
||||
"x",
|
||||
"z",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be y in " "2D TMy mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be y in 2D TMy mode.")
|
||||
)
|
||||
raise ValueError
|
||||
elif "2D TMz" in config.get_model_config().mode and polarisation in [
|
||||
"x",
|
||||
"y",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be z in " "2D TMz mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be z in 2D TMz mode.")
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
xcoord, ycoord, zcoord = uip.check_src_rx_point(p1, self.params_str())
|
||||
p2 = uip.round_to_grid_static_point(p1)
|
||||
|
||||
if resistance < 0:
|
||||
logger.exception(self.params_str() + (" requires a source " "resistance of zero " "or greater."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" requires a source resistance of zero or greater.")
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Check if there is a waveformID in the waveforms list
|
||||
if not any(x.ID == waveform_id for x in grid.waveforms):
|
||||
logger.exception(self.params_str() + (" there is no waveform with " "the identifier {waveform_id}."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" there is no waveform with the identifier {waveform_id}.")
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
v = VoltageSourceUser()
|
||||
@@ -369,7 +412,16 @@ class VoltageSource(UserObjectMulti):
|
||||
v.xcoord = xcoord
|
||||
v.ycoord = ycoord
|
||||
v.zcoord = zcoord
|
||||
v.ID = v.__class__.__name__ + "(" + str(v.xcoord) + "," + str(v.ycoord) + "," + str(v.zcoord) + ")"
|
||||
v.ID = (
|
||||
v.__class__.__name__
|
||||
+ "("
|
||||
+ str(v.xcoord)
|
||||
+ ","
|
||||
+ str(v.ycoord)
|
||||
+ ","
|
||||
+ str(v.zcoord)
|
||||
+ ")"
|
||||
)
|
||||
v.resistance = resistance
|
||||
v.waveformID = waveform_id
|
||||
|
||||
@@ -379,14 +431,25 @@ class VoltageSource(UserObjectMulti):
|
||||
# Check source start & source remove time parameters
|
||||
if start < 0:
|
||||
logger.exception(
|
||||
self.params_str() + (" delay of the initiation " "of the source should not " "be less than zero.")
|
||||
self.params_str()
|
||||
+ (
|
||||
" delay of the initiation "
|
||||
"of the source should not "
|
||||
"be less than zero."
|
||||
)
|
||||
)
|
||||
raise ValueError
|
||||
if stop < 0:
|
||||
logger.exception(self.params_str() + (" time to remove the " "source should not be " "less than zero."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" time to remove the source should not be less than zero.")
|
||||
)
|
||||
raise ValueError
|
||||
if stop - start <= 0:
|
||||
logger.exception(self.params_str() + (" duration of the source " "should not be zero or " "less."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" duration of the source should not be zero or less.")
|
||||
)
|
||||
raise ValueError
|
||||
v.start = start
|
||||
v.stop = min(stop, grid.timewindow)
|
||||
@@ -401,7 +464,9 @@ class VoltageSource(UserObjectMulti):
|
||||
logger.info(
|
||||
f"{self.grid_name(grid)}Voltage source with polarity "
|
||||
f"{v.polarisation} at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m, "
|
||||
f"resistance {v.resistance:.1f} Ohms," + startstop + f"using waveform {v.waveformID} created."
|
||||
f"resistance {v.resistance:.1f} Ohms,"
|
||||
+ startstop
|
||||
+ f"using waveform {v.waveformID} created."
|
||||
)
|
||||
|
||||
grid.voltagesources.append(v)
|
||||
@@ -454,25 +519,31 @@ class HertzianDipole(UserObjectMulti):
|
||||
|
||||
# Check polarity & position parameters
|
||||
if polarisation not in ("x", "y", "z"):
|
||||
logger.exception(self.params_str() + (" polarisation must be " "x, y, or z."))
|
||||
logger.exception(self.params_str() + (" polarisation must be x, y, or z."))
|
||||
raise ValueError
|
||||
if "2D TMx" in config.get_model_config().mode and polarisation in [
|
||||
"y",
|
||||
"z",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be x in " "2D TMx mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be x in 2D TMx mode.")
|
||||
)
|
||||
raise ValueError
|
||||
elif "2D TMy" in config.get_model_config().mode and polarisation in [
|
||||
"x",
|
||||
"z",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be y in " "2D TMy mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be y in 2D TMy mode.")
|
||||
)
|
||||
raise ValueError
|
||||
elif "2D TMz" in config.get_model_config().mode and polarisation in [
|
||||
"x",
|
||||
"y",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be z in " "2D TMz mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be z in 2D TMz mode.")
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
xcoord, ycoord, zcoord = uip.check_src_rx_point(p1, self.params_str())
|
||||
@@ -480,7 +551,9 @@ class HertzianDipole(UserObjectMulti):
|
||||
|
||||
# Check if there is a waveformID in the waveforms list
|
||||
if not any(x.ID == waveform_id for x in grid.waveforms):
|
||||
logger.exception(f"{self.params_str()} there is no waveform with the identifier {waveform_id}.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} there is no waveform with the identifier {waveform_id}."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
h = HertzianDipoleUser()
|
||||
@@ -500,7 +573,9 @@ class HertzianDipole(UserObjectMulti):
|
||||
h.xcoordorigin = xcoord
|
||||
h.ycoordorigin = ycoord
|
||||
h.zcoordorigin = zcoord
|
||||
h.ID = f"{h.__class__.__name__}({str(h.xcoord)},{str(h.ycoord)},{str(h.zcoord)})"
|
||||
h.ID = (
|
||||
f"{h.__class__.__name__}({str(h.xcoord)},{str(h.ycoord)},{str(h.zcoord)})"
|
||||
)
|
||||
h.waveformID = waveform_id
|
||||
|
||||
try:
|
||||
@@ -513,10 +588,14 @@ class HertzianDipole(UserObjectMulti):
|
||||
)
|
||||
raise ValueError
|
||||
if stop < 0:
|
||||
logger.exception(f"{self.params_str()} time to remove the source should not be less than zero.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} time to remove the source should not be less than zero."
|
||||
)
|
||||
raise ValueError
|
||||
if stop - start <= 0:
|
||||
logger.exception(f"{self.params_str()} duration of the source should not be zero or less.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} duration of the source should not be zero or less."
|
||||
)
|
||||
raise ValueError
|
||||
h.start = start
|
||||
h.stop = min(stop, grid.timewindow)
|
||||
@@ -595,25 +674,31 @@ class MagneticDipole(UserObjectMulti):
|
||||
|
||||
# Check polarity & position parameters
|
||||
if polarisation not in ("x", "y", "z"):
|
||||
logger.exception(self.params_str() + (" polarisation must be " "x, y, or z."))
|
||||
logger.exception(self.params_str() + (" polarisation must be x, y, or z."))
|
||||
raise ValueError
|
||||
if "2D TMx" in config.get_model_config().mode and polarisation in [
|
||||
"y",
|
||||
"z",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be x in " "2D TMx mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be x in 2D TMx mode.")
|
||||
)
|
||||
raise ValueError
|
||||
elif "2D TMy" in config.get_model_config().mode and polarisation in [
|
||||
"x",
|
||||
"z",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be y in " "2D TMy mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be y in 2D TMy mode.")
|
||||
)
|
||||
raise ValueError
|
||||
elif "2D TMz" in config.get_model_config().mode and polarisation in [
|
||||
"x",
|
||||
"y",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be z in " "2D TMz mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be z in 2D TMz mode.")
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
xcoord, ycoord, zcoord = uip.check_src_rx_point(p1, self.params_str())
|
||||
@@ -621,7 +706,9 @@ class MagneticDipole(UserObjectMulti):
|
||||
|
||||
# Check if there is a waveformID in the waveforms list
|
||||
if not any(x.ID == waveform_id for x in grid.waveforms):
|
||||
logger.exception(f"{self.params_str()} there is no waveform with the identifier {waveform_id}.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} there is no waveform with the identifier {waveform_id}."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
m = MagneticDipoleUser()
|
||||
@@ -632,7 +719,16 @@ class MagneticDipole(UserObjectMulti):
|
||||
m.xcoordorigin = xcoord
|
||||
m.ycoordorigin = ycoord
|
||||
m.zcoordorigin = zcoord
|
||||
m.ID = m.__class__.__name__ + "(" + str(m.xcoord) + "," + str(m.ycoord) + "," + str(m.zcoord) + ")"
|
||||
m.ID = (
|
||||
m.__class__.__name__
|
||||
+ "("
|
||||
+ str(m.xcoord)
|
||||
+ ","
|
||||
+ str(m.ycoord)
|
||||
+ ","
|
||||
+ str(m.zcoord)
|
||||
+ ")"
|
||||
)
|
||||
m.waveformID = waveform_id
|
||||
|
||||
try:
|
||||
@@ -641,14 +737,25 @@ class MagneticDipole(UserObjectMulti):
|
||||
stop = self.kwargs["stop"]
|
||||
if start < 0:
|
||||
logger.exception(
|
||||
self.params_str() + (" delay of the initiation " "of the source should not " "be less than zero.")
|
||||
self.params_str()
|
||||
+ (
|
||||
" delay of the initiation "
|
||||
"of the source should not "
|
||||
"be less than zero."
|
||||
)
|
||||
)
|
||||
raise ValueError
|
||||
if stop < 0:
|
||||
logger.exception(self.params_str() + (" time to remove the " "source should not be " "less than zero."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" time to remove the source should not be less than zero.")
|
||||
)
|
||||
raise ValueError
|
||||
if stop - start <= 0:
|
||||
logger.exception(self.params_str() + (" duration of the source " "should not be zero or " "less."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" duration of the source should not be zero or less.")
|
||||
)
|
||||
raise ValueError
|
||||
m.start = start
|
||||
m.stop = min(stop, grid.timewindow)
|
||||
@@ -728,25 +835,31 @@ class TransmissionLine(UserObjectMulti):
|
||||
|
||||
# Check polarity & position parameters
|
||||
if polarisation not in ("x", "y", "z"):
|
||||
logger.exception(self.params_str() + (" polarisation must be " "x, y, or z."))
|
||||
logger.exception(self.params_str() + (" polarisation must be x, y, or z."))
|
||||
raise ValueError
|
||||
if "2D TMx" in config.get_model_config().mode and polarisation in [
|
||||
"y",
|
||||
"z",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be x in " "2D TMx mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be x in 2D TMx mode.")
|
||||
)
|
||||
raise ValueError
|
||||
elif "2D TMy" in config.get_model_config().mode and polarisation in [
|
||||
"x",
|
||||
"z",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be y in " "2D TMy mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be y in 2D TMy mode.")
|
||||
)
|
||||
raise ValueError
|
||||
elif "2D TMz" in config.get_model_config().mode and polarisation in [
|
||||
"x",
|
||||
"y",
|
||||
]:
|
||||
logger.exception(self.params_str() + (" polarisation must be z in " "2D TMz mode."))
|
||||
logger.exception(
|
||||
self.params_str() + (" polarisation must be z in 2D TMz mode.")
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
xcoord, ycoord, zcoord = uip.check_src_rx_point(p1, self.params_str())
|
||||
@@ -762,7 +875,9 @@ class TransmissionLine(UserObjectMulti):
|
||||
|
||||
# Check if there is a waveformID in the waveforms list
|
||||
if not any(x.ID == waveform_id for x in grid.waveforms):
|
||||
logger.exception(f"{self.params_str()} there is no waveform with the identifier {waveform_id}.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} there is no waveform with the identifier {waveform_id}."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
t = TransmissionLineUser(grid)
|
||||
@@ -770,7 +885,16 @@ class TransmissionLine(UserObjectMulti):
|
||||
t.xcoord = xcoord
|
||||
t.ycoord = ycoord
|
||||
t.zcoord = zcoord
|
||||
t.ID = t.__class__.__name__ + "(" + str(t.xcoord) + "," + str(t.ycoord) + "," + str(t.zcoord) + ")"
|
||||
t.ID = (
|
||||
t.__class__.__name__
|
||||
+ "("
|
||||
+ str(t.xcoord)
|
||||
+ ","
|
||||
+ str(t.ycoord)
|
||||
+ ","
|
||||
+ str(t.zcoord)
|
||||
+ ")"
|
||||
)
|
||||
t.resistance = resistance
|
||||
t.waveformID = waveform_id
|
||||
|
||||
@@ -780,14 +904,25 @@ class TransmissionLine(UserObjectMulti):
|
||||
stop = self.kwargs["stop"]
|
||||
if start < 0:
|
||||
logger.exception(
|
||||
self.params_str() + (" delay of the initiation " "of the source should not " "be less than zero.")
|
||||
self.params_str()
|
||||
+ (
|
||||
" delay of the initiation "
|
||||
"of the source should not "
|
||||
"be less than zero."
|
||||
)
|
||||
)
|
||||
raise ValueError
|
||||
if stop < 0:
|
||||
logger.exception(self.params_str() + (" time to remove the " "source should not be " "less than zero."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" time to remove the source should not be less than zero.")
|
||||
)
|
||||
raise ValueError
|
||||
if stop - start <= 0:
|
||||
logger.exception(self.params_str() + (" duration of the source " "should not be zero or " "less."))
|
||||
logger.exception(
|
||||
self.params_str()
|
||||
+ (" duration of the source should not be zero or less.")
|
||||
)
|
||||
raise ValueError
|
||||
t.start = start
|
||||
t.stop = min(stop, grid.timewindow)
|
||||
@@ -839,7 +974,11 @@ class Rx(UserObjectMulti):
|
||||
|
||||
def _do_rotate(self, grid):
|
||||
"""Performs rotation."""
|
||||
new_pt = (self.kwargs["p1"][0] + grid.dx, self.kwargs["p1"][1] + grid.dy, self.kwargs["p1"][2] + grid.dz)
|
||||
new_pt = (
|
||||
self.kwargs["p1"][0] + grid.dx,
|
||||
self.kwargs["p1"][1] + grid.dy,
|
||||
self.kwargs["p1"][2] + grid.dz,
|
||||
)
|
||||
pts = np.array([self.kwargs["p1"], new_pt])
|
||||
rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin)
|
||||
self.kwargs["p1"] = tuple(rot_pts[0, :])
|
||||
@@ -878,7 +1017,9 @@ class Rx(UserObjectMulti):
|
||||
# If no ID or outputs are specified, use default
|
||||
r.ID = f"{r.__class__.__name__}({str(r.xcoord)},{str(r.ycoord)},{str(r.zcoord)})"
|
||||
for key in RxUser.defaultoutputs:
|
||||
r.outputs[key] = np.zeros(grid.iterations, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
r.outputs[key] = np.zeros(
|
||||
grid.iterations, dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
else:
|
||||
outputs.sort()
|
||||
# Get allowable outputs
|
||||
@@ -889,7 +1030,10 @@ class Rx(UserObjectMulti):
|
||||
# Check and add field output names
|
||||
for field in outputs:
|
||||
if field in allowableoutputs:
|
||||
r.outputs[field] = np.zeros(grid.iterations, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
r.outputs[field] = np.zeros(
|
||||
grid.iterations,
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
else:
|
||||
logger.exception(
|
||||
f"{self.params_str()} contains an output "
|
||||
@@ -940,10 +1084,14 @@ class RxArray(UserObjectMulti):
|
||||
dx, dy, dz = uip.discretise_point(dl)
|
||||
|
||||
if xs > xf or ys > yf or zs > zf:
|
||||
logger.exception(f"{self.params_str()} the lower coordinates should be less than the upper coordinates.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} the lower coordinates should be less than the upper coordinates."
|
||||
)
|
||||
raise ValueError
|
||||
if dx < 0 or dy < 0 or dz < 0:
|
||||
logger.exception(f"{self.params_str()} the step size should not be less than zero.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} the step size should not be less than zero."
|
||||
)
|
||||
raise ValueError
|
||||
if dx < 1:
|
||||
if dx == 0:
|
||||
@@ -993,7 +1141,10 @@ class RxArray(UserObjectMulti):
|
||||
p5 = uip.round_to_grid_static_point(p5)
|
||||
r.ID = f"{r.__class__.__name__}({str(x)},{str(y)},{str(z)})"
|
||||
for key in RxUser.defaultoutputs:
|
||||
r.outputs[key] = np.zeros(grid.iterations, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
r.outputs[key] = np.zeros(
|
||||
grid.iterations,
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
logger.info(
|
||||
f" Receiver at {p5[0]:g}m, {p5[1]:g}m, "
|
||||
f"{p5[2]:g}m with output component(s) "
|
||||
@@ -1066,7 +1217,9 @@ class Snapshot(UserObjectMulti):
|
||||
if time > 0:
|
||||
iterations = round_value((time / grid.dt)) + 1
|
||||
else:
|
||||
logger.exception(f"{self.params_str()} time value must be greater than zero.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} time value must be greater than zero."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
try:
|
||||
@@ -1101,16 +1254,34 @@ class Snapshot(UserObjectMulti):
|
||||
outputs = dict.fromkeys(SnapshotUser.allowableoutputs, True)
|
||||
|
||||
if dx < 0 or dy < 0 or dz < 0:
|
||||
logger.exception(f"{self.params_str()} the step size should not be less than zero.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} the step size should not be less than zero."
|
||||
)
|
||||
raise ValueError
|
||||
if dx < 1 or dy < 1 or dz < 1:
|
||||
logger.exception(f"{self.params_str()} the step size should not be less than the spatial discretisation.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} the step size should not be less than the spatial discretisation."
|
||||
)
|
||||
raise ValueError
|
||||
if iterations <= 0 or iterations > grid.iterations:
|
||||
logger.exception(f"{self.params_str()} time value is not valid.")
|
||||
raise ValueError
|
||||
|
||||
s = SnapshotUser(xs, ys, zs, xf, yf, zf, dx, dy, dz, iterations, filename, fileext=fileext, outputs=outputs)
|
||||
s = SnapshotUser(
|
||||
xs,
|
||||
ys,
|
||||
zs,
|
||||
xf,
|
||||
yf,
|
||||
zf,
|
||||
dx,
|
||||
dy,
|
||||
dz,
|
||||
iterations,
|
||||
filename,
|
||||
fileext=fileext,
|
||||
outputs=outputs,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Snapshot from {p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to "
|
||||
@@ -1160,7 +1331,9 @@ class Material(UserObjectMulti):
|
||||
if se != "inf":
|
||||
se = float(se)
|
||||
if se < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for electric conductivity.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for electric conductivity."
|
||||
)
|
||||
raise ValueError
|
||||
else:
|
||||
se = float("inf")
|
||||
@@ -1170,10 +1343,14 @@ class Material(UserObjectMulti):
|
||||
)
|
||||
raise ValueError
|
||||
if sm < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for magnetic loss.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for magnetic loss."
|
||||
)
|
||||
raise ValueError
|
||||
if any(x.ID == material_id for x in grid.materials):
|
||||
logger.exception(f"{self.params_str()} with ID {material_id} already exists")
|
||||
logger.exception(
|
||||
f"{self.params_str()} with ID {material_id} already exists"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Create a new instance of the Material class material
|
||||
@@ -1227,7 +1404,9 @@ class AddDebyeDispersion(UserObjectMulti):
|
||||
raise
|
||||
|
||||
if poles < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for number of poles.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for number of poles."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Look up requested materials in existing list of material instances
|
||||
@@ -1249,17 +1428,24 @@ class AddDebyeDispersion(UserObjectMulti):
|
||||
disp_material.averagable = False
|
||||
for i in range(poles):
|
||||
if tau[i] > 0:
|
||||
logger.debug("Not checking if relaxation times are " "greater than time-step.")
|
||||
logger.debug(
|
||||
"Not checking if relaxation times are greater than time-step."
|
||||
)
|
||||
disp_material.deltaer.append(er_delta[i])
|
||||
disp_material.tau.append(tau[i])
|
||||
else:
|
||||
logger.exception(f"{self.params_str()} requires positive values for the permittivity difference.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires positive values for the permittivity difference."
|
||||
)
|
||||
raise ValueError
|
||||
if disp_material.poles > config.get_model_config().materials["maxpoles"]:
|
||||
config.get_model_config().materials["maxpoles"] = disp_material.poles
|
||||
|
||||
# Replace original material with newly created DispersiveMaterial
|
||||
grid.materials = [disp_material if mat.numID == material.numID else mat for mat in grid.materials]
|
||||
grid.materials = [
|
||||
disp_material if mat.numID == material.numID else mat
|
||||
for mat in grid.materials
|
||||
]
|
||||
|
||||
logger.info(
|
||||
f"{self.grid_name(grid)}Debye disperion added to {disp_material.ID} "
|
||||
@@ -1300,7 +1486,9 @@ class AddLorentzDispersion(UserObjectMulti):
|
||||
raise
|
||||
|
||||
if poles < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for number of poles.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for number of poles."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Look up requested materials in existing list of material instances
|
||||
@@ -1338,7 +1526,10 @@ class AddLorentzDispersion(UserObjectMulti):
|
||||
config.get_model_config().materials["maxpoles"] = disp_material.poles
|
||||
|
||||
# Replace original material with newly created DispersiveMaterial
|
||||
grid.materials = [disp_material if mat.numID == material.numID else mat for mat in grid.materials]
|
||||
grid.materials = [
|
||||
disp_material if mat.numID == material.numID else mat
|
||||
for mat in grid.materials
|
||||
]
|
||||
|
||||
logger.info(
|
||||
f"{self.grid_name(grid)}Lorentz disperion added to {disp_material.ID} "
|
||||
@@ -1376,7 +1567,9 @@ class AddDrudeDispersion(UserObjectMulti):
|
||||
raise
|
||||
|
||||
if poles < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for number of poles.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for number of poles."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Look up requested materials in existing list of material instances
|
||||
@@ -1384,7 +1577,9 @@ class AddDrudeDispersion(UserObjectMulti):
|
||||
|
||||
if len(materials) != len(material_ids):
|
||||
notfound = [x for x in material_ids if x not in materials]
|
||||
logger.exception(f"{self.params_str()} material(s) {notfound} do not exist.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} material(s) {notfound} do not exist."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
for material in materials:
|
||||
@@ -1412,7 +1607,10 @@ class AddDrudeDispersion(UserObjectMulti):
|
||||
config.get_model_config().materials["maxpoles"] = disp_material.poles
|
||||
|
||||
# Replace original material with newly created DispersiveMaterial
|
||||
grid.materials = [disp_material if mat.numID == material.numID else mat for mat in grid.materials]
|
||||
grid.materials = [
|
||||
disp_material if mat.numID == material.numID else mat
|
||||
for mat in grid.materials
|
||||
]
|
||||
|
||||
logger.info(
|
||||
f"{self.grid_name(grid)}Drude disperion added to {disp_material.ID} "
|
||||
@@ -1452,20 +1650,30 @@ class SoilPeplinski(UserObjectMulti):
|
||||
water_fraction_upper = self.kwargs["water_fraction_upper"]
|
||||
ID = self.kwargs["id"]
|
||||
except KeyError:
|
||||
logger.exception(f"{self.params_str()} requires at exactly seven parameters.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires at exactly seven parameters."
|
||||
)
|
||||
raise
|
||||
|
||||
if sand_fraction < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for the sand fraction.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for the sand fraction."
|
||||
)
|
||||
raise ValueError
|
||||
if clay_fraction < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for the clay fraction.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for the clay fraction."
|
||||
)
|
||||
raise ValueError
|
||||
if bulk_density < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for the bulk density.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for the bulk density."
|
||||
)
|
||||
raise ValueError
|
||||
if sand_density < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for the sand particle density.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for the sand particle density."
|
||||
)
|
||||
raise ValueError
|
||||
if water_fraction_lower < 0:
|
||||
logger.exception(
|
||||
@@ -1486,7 +1694,12 @@ class SoilPeplinski(UserObjectMulti):
|
||||
# Create a new instance of the Material class material
|
||||
# (start index after pec & free_space)
|
||||
s = PeplinskiSoilUser(
|
||||
ID, sand_fraction, clay_fraction, bulk_density, sand_density, (water_fraction_lower, water_fraction_upper)
|
||||
ID,
|
||||
sand_fraction,
|
||||
clay_fraction,
|
||||
bulk_density,
|
||||
sand_density,
|
||||
(water_fraction_lower, water_fraction_upper),
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -1532,7 +1745,9 @@ class MaterialRange(UserObjectMulti):
|
||||
ro_upper = self.kwargs["ro_upper"]
|
||||
ID = self.kwargs["id"]
|
||||
except KeyError:
|
||||
logger.exception(f"{self.params_str()} requires at exactly nine parameters.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires at exactly nine parameters."
|
||||
)
|
||||
raise
|
||||
|
||||
if er_lower < 1:
|
||||
@@ -1548,10 +1763,14 @@ class MaterialRange(UserObjectMulti):
|
||||
)
|
||||
raise ValueError
|
||||
if sigma_lower < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for the lower limit of conductivity.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for the lower limit of conductivity."
|
||||
)
|
||||
raise ValueError
|
||||
if ro_lower < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for the lower range magnetic loss.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for the lower range magnetic loss."
|
||||
)
|
||||
raise ValueError
|
||||
if er_upper < 1:
|
||||
logger.exception(
|
||||
@@ -1566,17 +1785,25 @@ class MaterialRange(UserObjectMulti):
|
||||
)
|
||||
raise ValueError
|
||||
if sigma_upper < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for the upper range of conductivity.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for the upper range of conductivity."
|
||||
)
|
||||
raise ValueError
|
||||
if ro_upper < 0:
|
||||
logger.exception(f"{self.params_str()} requires a positive value for the upper range of magnetic loss.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires a positive value for the upper range of magnetic loss."
|
||||
)
|
||||
|
||||
if any(x.ID == ID for x in grid.mixingmodels):
|
||||
logger.exception(f"{self.params_str()} with ID {ID} already exists")
|
||||
raise ValueError
|
||||
|
||||
s = RangeMaterialUser(
|
||||
ID, (er_lower, er_upper), (sigma_lower, sigma_upper), (mr_lower, mr_upper), (ro_lower, ro_upper)
|
||||
ID,
|
||||
(er_lower, er_upper),
|
||||
(sigma_lower, sigma_upper),
|
||||
(mr_lower, mr_upper),
|
||||
(ro_lower, ro_upper),
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -1616,7 +1843,9 @@ class MaterialList(UserObjectMulti):
|
||||
|
||||
s = ListMaterialUser(ID, list_of_materials)
|
||||
|
||||
logger.info(f"{self.grid_name(grid)}A list of materials used to create {s.ID} that includes {s.mat}, created")
|
||||
logger.info(
|
||||
f"{self.grid_name(grid)}A list of materials used to create {s.ID} that includes {s.mat}, created"
|
||||
)
|
||||
|
||||
grid.mixingmodels.append(s)
|
||||
|
||||
@@ -1649,8 +1878,7 @@ class GeometryView(UserObjectMulti):
|
||||
"""
|
||||
|
||||
if output_type == "n":
|
||||
from .geometry_outputs import \
|
||||
GeometryViewVoxels as GeometryViewUser
|
||||
from .geometry_outputs import GeometryViewVoxels as GeometryViewUser
|
||||
else:
|
||||
from .geometry_outputs import GeometryViewLines as GeometryViewUser
|
||||
|
||||
@@ -1682,18 +1910,30 @@ class GeometryView(UserObjectMulti):
|
||||
dx, dy, dz = uip.discretise_static_point(dl)
|
||||
|
||||
if dx < 0 or dy < 0 or dz < 0:
|
||||
logger.exception(f"{self.params_str()} the step size should not be less than zero.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} the step size should not be less than zero."
|
||||
)
|
||||
raise ValueError
|
||||
if dx > grid.nx or dy > grid.ny or dz > grid.nz:
|
||||
logger.exception(f"{self.params_str()} the step size should be less than the domain size.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} the step size should be less than the domain size."
|
||||
)
|
||||
raise ValueError
|
||||
if dx < 1 or dy < 1 or dz < 1:
|
||||
logger.exception(f"{self.params_str()} the step size should not be less than the spatial discretisation.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} the step size should not be less than the spatial discretisation."
|
||||
)
|
||||
raise ValueError
|
||||
if output_type not in ["n", "f"]:
|
||||
logger.exception(f"{self.params_str()} requires type to be either n (normal) or f (fine).")
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires type to be either n (normal) or f (fine)."
|
||||
)
|
||||
raise ValueError
|
||||
if output_type == "f" and (dx * grid.dx != grid.dx or dy * grid.dy != grid.dy or dz * grid.dz != grid.dz):
|
||||
if output_type == "f" and (
|
||||
dx * grid.dx != grid.dx
|
||||
or dy * grid.dy != grid.dy
|
||||
or dz * grid.dz != grid.dz
|
||||
):
|
||||
logger.exception(
|
||||
f"{self.params_str()} requires the spatial "
|
||||
"discretisation for the geometry view to be the "
|
||||
@@ -1821,7 +2061,9 @@ class PMLCFS(UserObjectMulti):
|
||||
or kappascalingdirection not in CFSParameter.scalingdirections
|
||||
or sigmascalingdirection not in CFSParameter.scalingdirections
|
||||
):
|
||||
logger.exception(f"{self.params_str()} must have scaling type {','.join(CFSParameter.scalingdirections)}")
|
||||
logger.exception(
|
||||
f"{self.params_str()} must have scaling type {','.join(CFSParameter.scalingdirections)}"
|
||||
)
|
||||
raise ValueError
|
||||
if (
|
||||
float(alphamin) < 0
|
||||
@@ -1830,7 +2072,9 @@ class PMLCFS(UserObjectMulti):
|
||||
or float(kappamax) < 0
|
||||
or float(sigmamin) < 0
|
||||
):
|
||||
logger.exception(f"{self.params_str()} minimum and maximum scaling values must be greater than zero.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} minimum and maximum scaling values must be greater than zero."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
cfsalpha = CFSParameter()
|
||||
@@ -1874,7 +2118,9 @@ class PMLCFS(UserObjectMulti):
|
||||
grid.pmls["cfs"].append(cfs)
|
||||
|
||||
if len(grid.pmls["cfs"]) > 2:
|
||||
logger.exception(f"{self.params_str()} can only be used up to two times, for up to a 2nd order PML.")
|
||||
logger.exception(
|
||||
f"{self.params_str()} can only be used up to two times, for up to a 2nd order PML."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
|
||||
|
@@ -95,17 +95,20 @@ class Discretisation(UserObjectSingle):
|
||||
|
||||
if G.dl[0] <= 0:
|
||||
logger.exception(
|
||||
f"{self.__str__()} discretisation requires the " f"x-direction spatial step to be greater than zero"
|
||||
f"{self.__str__()} discretisation requires the "
|
||||
f"x-direction spatial step to be greater than zero"
|
||||
)
|
||||
raise ValueError
|
||||
if G.dl[1] <= 0:
|
||||
logger.exception(
|
||||
f"{self.__str__()} discretisation requires the " f"y-direction spatial step to be greater than zero"
|
||||
f"{self.__str__()} discretisation requires the "
|
||||
f"y-direction spatial step to be greater than zero"
|
||||
)
|
||||
raise ValueError
|
||||
if G.dl[2] <= 0:
|
||||
logger.exception(
|
||||
f"{self.__str__()} discretisation requires the " f"z-direction spatial step to be greater than zero"
|
||||
f"{self.__str__()} discretisation requires the "
|
||||
f"z-direction spatial step to be greater than zero"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
@@ -131,7 +134,9 @@ class Domain(UserObjectSingle):
|
||||
raise
|
||||
|
||||
if G.nx == 0 or G.ny == 0 or G.nz == 0:
|
||||
logger.exception(f"{self.__str__()} requires at least one cell in " f"every dimension")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires at least one cell in every dimension"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
logger.info(
|
||||
@@ -161,7 +166,10 @@ class Domain(UserObjectSingle):
|
||||
|
||||
# Sub-grids cannot be used with 2D models. There would typically be
|
||||
# minimal performance benefit with sub-gridding and 2D models.
|
||||
if "2D" in config.get_model_config().mode and config.sim_config.general["subgrid"]:
|
||||
if (
|
||||
"2D" in config.get_model_config().mode
|
||||
and config.sim_config.general["subgrid"]
|
||||
):
|
||||
logger.exception("Sub-gridding cannot be used with 2D models")
|
||||
raise ValueError
|
||||
|
||||
@@ -188,7 +196,8 @@ class TimeStepStabilityFactor(UserObjectSingle):
|
||||
|
||||
if f <= 0 or f > 1:
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires the value of the time " f"step stability factor to be between zero and one"
|
||||
f"{self.__str__()} requires the value of the time "
|
||||
f"step stability factor to be between zero and one"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
@@ -227,7 +236,9 @@ class TimeWindow(UserObjectSingle):
|
||||
G.timewindow = tmp
|
||||
G.iterations = int(np.ceil(tmp / G.dt)) + 1
|
||||
else:
|
||||
logger.exception(self.__str__() + " must have a value greater than zero")
|
||||
logger.exception(
|
||||
self.__str__() + " must have a value greater than zero"
|
||||
)
|
||||
raise ValueError
|
||||
except KeyError:
|
||||
pass
|
||||
@@ -261,7 +272,10 @@ class OMPThreads(UserObjectSingle):
|
||||
)
|
||||
raise
|
||||
if n < 1:
|
||||
logger.exception(f"{self.__str__()} requires the value to be an " f"integer not less than one")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires the value to be an "
|
||||
f"integer not less than one"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
config.get_model_config().ompthreads = set_omp_threads(n)
|
||||
@@ -290,7 +304,9 @@ class PMLProps(UserObjectSingle):
|
||||
G.pmls["formulation"] = self.kwargs["formulation"]
|
||||
if G.pmls["formulation"] not in PML.formulations:
|
||||
logger.exception(
|
||||
self.__str__() + f" requires the value to be " + f"one of {' '.join(PML.formulations)}"
|
||||
self.__str__()
|
||||
+ f" requires the value to be "
|
||||
+ f"one of {' '.join(PML.formulations)}"
|
||||
)
|
||||
except KeyError:
|
||||
pass
|
||||
@@ -309,7 +325,9 @@ class PMLProps(UserObjectSingle):
|
||||
G.pmls["thickness"]["ymax"] = int(self.kwargs["ymax"])
|
||||
G.pmls["thickness"]["zmax"] = int(self.kwargs["zmax"])
|
||||
except KeyError:
|
||||
logger.exception(f"{self.__str__()} requires either one or six parameter(s)")
|
||||
logger.exception(
|
||||
f"{self.__str__()} requires either one or six parameter(s)"
|
||||
)
|
||||
raise
|
||||
|
||||
if (
|
||||
|
@@ -87,7 +87,11 @@ class ModelConfig:
|
||||
except:
|
||||
deviceID = 0
|
||||
|
||||
self.device = {"dev": sim_config.set_model_device(deviceID), "deviceID": deviceID, "snapsgpu2cpu": False}
|
||||
self.device = {
|
||||
"dev": sim_config.set_model_device(deviceID),
|
||||
"deviceID": deviceID,
|
||||
"snapsgpu2cpu": False,
|
||||
}
|
||||
|
||||
# Total memory usage for all grids in the model. Starts with 50MB overhead.
|
||||
self.mem_overhead = 65e6
|
||||
@@ -96,11 +100,20 @@ class ModelConfig:
|
||||
self.reuse_geometry = False
|
||||
|
||||
# String to print at start of each model run
|
||||
s = f"\n--- Model {model_num + 1}/{sim_config.model_end}, " f"input file: {sim_config.input_file_path}"
|
||||
self.inputfilestr = Fore.GREEN + f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n" + Style.RESET_ALL
|
||||
s = (
|
||||
f"\n--- Model {model_num + 1}/{sim_config.model_end}, "
|
||||
f"input file: {sim_config.input_file_path}"
|
||||
)
|
||||
self.inputfilestr = (
|
||||
Fore.GREEN
|
||||
+ f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n"
|
||||
+ Style.RESET_ALL
|
||||
)
|
||||
|
||||
# Output file path and name for specific model
|
||||
self.appendmodelnumber = "" if sim_config.args.n == 1 else str(model_num + 1) # Indexed from 1
|
||||
self.appendmodelnumber = (
|
||||
"" if sim_config.args.n == 1 else str(model_num + 1)
|
||||
) # Indexed from 1
|
||||
self.set_output_file_path()
|
||||
|
||||
# Numerical dispersion analysis parameters
|
||||
@@ -111,7 +124,11 @@ class ModelConfig:
|
||||
# phase-velocity phase error.
|
||||
# mingridsampling: minimum grid sampling of smallest wavelength for
|
||||
# physical wave propagation.
|
||||
self.numdispersion = {"highestfreqthres": 40, "maxnumericaldisp": 2, "mingridsampling": 3}
|
||||
self.numdispersion = {
|
||||
"highestfreqthres": 40,
|
||||
"maxnumericaldisp": 2,
|
||||
"mingridsampling": 3,
|
||||
}
|
||||
|
||||
# General information to configure materials
|
||||
# maxpoles: Maximum number of dispersive material poles in a model.
|
||||
@@ -221,7 +238,11 @@ class SimulationConfig:
|
||||
# progressbars when logging level is greater than
|
||||
# info (20)
|
||||
|
||||
self.general = {"solver": "cpu", "precision": "single", "progressbars": args.log_level <= 20}
|
||||
self.general = {
|
||||
"solver": "cpu",
|
||||
"precision": "single",
|
||||
"progressbars": args.log_level <= 20,
|
||||
}
|
||||
|
||||
self.em_consts = {
|
||||
"c": c, # Speed of light in free space (m/s)
|
||||
@@ -239,7 +260,10 @@ class SimulationConfig:
|
||||
# Both single and double precision are possible on GPUs, but single
|
||||
# provides best performance.
|
||||
self.general["precision"] = "single"
|
||||
self.devices = {"devs": [], "nvcc_opts": None} # pycuda device objects; nvcc compiler options
|
||||
self.devices = {
|
||||
"devs": [],
|
||||
"nvcc_opts": None,
|
||||
} # pycuda device objects; nvcc compiler options
|
||||
# Suppress nvcc warnings on Microsoft Windows
|
||||
if sys.platform == "win32":
|
||||
self.devices["nvcc_opts"] = ["-w"]
|
||||
@@ -251,7 +275,10 @@ class SimulationConfig:
|
||||
if self.args.opencl is not None:
|
||||
self.general["solver"] = "opencl"
|
||||
self.general["precision"] = "single"
|
||||
self.devices = {"devs": [], "compiler_opts": None} # pyopencl device device(s); compiler options
|
||||
self.devices = {
|
||||
"devs": [],
|
||||
"compiler_opts": None,
|
||||
} # pyopencl device device(s); compiler options
|
||||
|
||||
# Suppress CompilerWarning (sub-class of UserWarning)
|
||||
warnings.filterwarnings("ignore", category=UserWarning)
|
||||
@@ -271,7 +298,8 @@ class SimulationConfig:
|
||||
self.general["subgrid"] and self.general["solver"] == "opencl"
|
||||
):
|
||||
logger.exception(
|
||||
"You cannot currently use CUDA or OpenCL-based " "solvers with models that contain sub-grids."
|
||||
"You cannot currently use CUDA or OpenCL-based "
|
||||
"solvers with models that contain sub-grids."
|
||||
)
|
||||
raise ValueError
|
||||
else:
|
||||
@@ -305,7 +333,9 @@ class SimulationConfig:
|
||||
return dev
|
||||
|
||||
if not found:
|
||||
logger.exception(f"Compute device with device ID {deviceID} does " "not exist.")
|
||||
logger.exception(
|
||||
f"Compute device with device ID {deviceID} does not exist."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
def _set_precision(self):
|
||||
|
@@ -31,8 +31,7 @@ import gprMax.config as config
|
||||
from ._version import __version__, codename
|
||||
from .model_build_run import ModelBuildRun
|
||||
from .solvers import create_G, create_solver
|
||||
from .utilities.host_info import (print_cuda_info, print_host_info,
|
||||
print_opencl_info)
|
||||
from .utilities.host_info import print_cuda_info, print_host_info, print_opencl_info
|
||||
from .utilities.utilities import get_terminal_width, logo, timer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -45,7 +44,9 @@ class Context:
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.model_range = range(config.sim_config.model_start, config.sim_config.model_end)
|
||||
self.model_range = range(
|
||||
config.sim_config.model_start, config.sim_config.model_end
|
||||
)
|
||||
self.tsimend = None
|
||||
self.tsimstart = None
|
||||
|
||||
@@ -181,7 +182,11 @@ class MPIContext(Context):
|
||||
print_opencl_info(config.sim_config.devices["devs"])
|
||||
|
||||
s = f"\n--- Input file: {config.sim_config.input_file_path}"
|
||||
logger.basic(Fore.GREEN + f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n" + Style.RESET_ALL)
|
||||
logger.basic(
|
||||
Fore.GREEN
|
||||
+ f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n"
|
||||
+ Style.RESET_ALL
|
||||
)
|
||||
|
||||
sys.stdout.flush()
|
||||
|
||||
|
@@ -45,7 +45,9 @@ def store_outputs(G):
|
||||
# Store current component
|
||||
else:
|
||||
func = globals()[output]
|
||||
rx.outputs[output][iteration] = func(rx.xcoord, rx.ycoord, rx.zcoord, Hx, Hy, Hz, G)
|
||||
rx.outputs[output][iteration] = func(
|
||||
rx.xcoord, rx.ycoord, rx.zcoord, Hx, Hy, Hz, G
|
||||
)
|
||||
|
||||
for tl in G.transmissionlines:
|
||||
tl.Vtotal[iteration] = tl.voltage[tl.antpos]
|
||||
@@ -92,7 +94,12 @@ def write_hd5_data(basegrp, grid, is_subgrid=False):
|
||||
basegrp.attrs["nx_ny_nz"] = (grid.nx, grid.ny, grid.nz)
|
||||
basegrp.attrs["dx_dy_dz"] = (grid.dx, grid.dy, grid.dz)
|
||||
basegrp.attrs["dt"] = grid.dt
|
||||
nsrc = len(grid.voltagesources + grid.hertziandipoles + grid.magneticdipoles + grid.transmissionlines)
|
||||
nsrc = len(
|
||||
grid.voltagesources
|
||||
+ grid.hertziandipoles
|
||||
+ grid.magneticdipoles
|
||||
+ grid.transmissionlines
|
||||
)
|
||||
basegrp.attrs["nsrc"] = nsrc
|
||||
basegrp.attrs["nrx"] = len(grid.rxs)
|
||||
basegrp.attrs["srcsteps"] = grid.srcsteps
|
||||
@@ -112,13 +119,21 @@ def write_hd5_data(basegrp, grid, is_subgrid=False):
|
||||
for srcindex, src in enumerate(srclist):
|
||||
grp = basegrp.create_group(f"srcs/src{str(srcindex + 1)}")
|
||||
grp.attrs["Type"] = type(src).__name__
|
||||
grp.attrs["Position"] = (src.xcoord * grid.dx, src.ycoord * grid.dy, src.zcoord * grid.dz)
|
||||
grp.attrs["Position"] = (
|
||||
src.xcoord * grid.dx,
|
||||
src.ycoord * grid.dy,
|
||||
src.zcoord * grid.dz,
|
||||
)
|
||||
|
||||
# Create group for transmission lines; add positional data, line resistance and
|
||||
# line discretisation attributes; write arrays for line voltages and currents
|
||||
for tlindex, tl in enumerate(grid.transmissionlines):
|
||||
grp = basegrp.create_group("tls/tl" + str(tlindex + 1))
|
||||
grp.attrs["Position"] = (tl.xcoord * grid.dx, tl.ycoord * grid.dy, tl.zcoord * grid.dz)
|
||||
grp.attrs["Position"] = (
|
||||
tl.xcoord * grid.dx,
|
||||
tl.ycoord * grid.dy,
|
||||
tl.zcoord * grid.dz,
|
||||
)
|
||||
grp.attrs["Resistance"] = tl.resistance
|
||||
grp.attrs["dl"] = tl.dl
|
||||
# Save incident voltage and current
|
||||
@@ -133,7 +148,11 @@ def write_hd5_data(basegrp, grid, is_subgrid=False):
|
||||
grp = basegrp.create_group("rxs/rx" + str(rxindex + 1))
|
||||
if rx.ID:
|
||||
grp.attrs["Name"] = rx.ID
|
||||
grp.attrs["Position"] = (rx.xcoord * grid.dx, rx.ycoord * grid.dy, rx.zcoord * grid.dz)
|
||||
grp.attrs["Position"] = (
|
||||
rx.xcoord * grid.dx,
|
||||
rx.ycoord * grid.dy,
|
||||
rx.zcoord * grid.dz,
|
||||
)
|
||||
|
||||
for output in rx.outputs:
|
||||
basegrp["rxs/rx" + str(rxindex + 1) + "/" + output] = rx.outputs[output]
|
||||
@@ -151,7 +170,9 @@ def Ix(x, y, z, Hx, Hy, Hz, G):
|
||||
if y == 0 or z == 0:
|
||||
Ix = 0
|
||||
else:
|
||||
Ix = G.dy * (Hy[x, y, z - 1] - Hy[x, y, z]) + G.dz * (Hz[x, y, z] - Hz[x, y - 1, z])
|
||||
Ix = G.dy * (Hy[x, y, z - 1] - Hy[x, y, z]) + G.dz * (
|
||||
Hz[x, y, z] - Hz[x, y - 1, z]
|
||||
)
|
||||
|
||||
return Ix
|
||||
|
||||
@@ -168,7 +189,9 @@ def Iy(x, y, z, Hx, Hy, Hz, G):
|
||||
if x == 0 or z == 0:
|
||||
Iy = 0
|
||||
else:
|
||||
Iy = G.dx * (Hx[x, y, z] - Hx[x, y, z - 1]) + G.dz * (Hz[x - 1, y, z] - Hz[x, y, z])
|
||||
Iy = G.dx * (Hx[x, y, z] - Hx[x, y, z - 1]) + G.dz * (
|
||||
Hz[x - 1, y, z] - Hz[x, y, z]
|
||||
)
|
||||
|
||||
return Iy
|
||||
|
||||
@@ -185,6 +208,8 @@ def Iz(x, y, z, Hx, Hy, Hz, G):
|
||||
if x == 0 or y == 0:
|
||||
Iz = 0
|
||||
else:
|
||||
Iz = G.dx * (Hx[x, y - 1, z] - Hx[x, y, z]) + G.dy * (Hy[x, y, z] - Hy[x - 1, y, z])
|
||||
Iz = G.dx * (Hx[x, y - 1, z] - Hx[x, y, z]) + G.dy * (
|
||||
Hy[x, y, z] - Hy[x - 1, y, z]
|
||||
)
|
||||
|
||||
return Iz
|
||||
|
@@ -82,7 +82,12 @@ class FractalSurface:
|
||||
self.fractalsurface = np.zeros(surfacedims, dtype=self.dtype)
|
||||
|
||||
# Positional vector at centre of array, scaled by weighting
|
||||
v1 = np.array([self.weighting[0] * (surfacedims[0]) / 2, self.weighting[1] * (surfacedims[1]) / 2])
|
||||
v1 = np.array(
|
||||
[
|
||||
self.weighting[0] * (surfacedims[0]) / 2,
|
||||
self.weighting[1] * (surfacedims[1]) / 2,
|
||||
]
|
||||
)
|
||||
|
||||
# 2D array of random numbers to be convolved with the fractal function
|
||||
rng = np.random.default_rng(seed=self.seed)
|
||||
@@ -119,9 +124,11 @@ class FractalSurface:
|
||||
fractalmax = np.amax(self.fractalsurface)
|
||||
fractalrange = fractalmax - fractalmin
|
||||
self.fractalsurface = (
|
||||
self.fractalsurface * ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange)
|
||||
self.fractalsurface
|
||||
* ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange)
|
||||
+ self.fractalrange[0]
|
||||
- ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) * fractalmin
|
||||
- ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange)
|
||||
* fractalmin
|
||||
)
|
||||
|
||||
|
||||
@@ -167,16 +174,24 @@ class FractalVolume:
|
||||
|
||||
# Scale filter according to size of fractal volume
|
||||
if self.nx == 1:
|
||||
filterscaling = np.amin(np.array([self.ny, self.nz])) / np.array([self.ny, self.nz])
|
||||
filterscaling = np.amin(np.array([self.ny, self.nz])) / np.array(
|
||||
[self.ny, self.nz]
|
||||
)
|
||||
filterscaling = np.insert(filterscaling, 0, 1)
|
||||
elif self.ny == 1:
|
||||
filterscaling = np.amin(np.array([self.nx, self.nz])) / np.array([self.nx, self.nz])
|
||||
filterscaling = np.amin(np.array([self.nx, self.nz])) / np.array(
|
||||
[self.nx, self.nz]
|
||||
)
|
||||
filterscaling = np.insert(filterscaling, 1, 1)
|
||||
elif self.nz == 1:
|
||||
filterscaling = np.amin(np.array([self.nx, self.ny])) / np.array([self.nx, self.ny])
|
||||
filterscaling = np.amin(np.array([self.nx, self.ny])) / np.array(
|
||||
[self.nx, self.ny]
|
||||
)
|
||||
filterscaling = np.insert(filterscaling, 2, 1)
|
||||
else:
|
||||
filterscaling = np.amin(np.array([self.nx, self.ny, self.nz])) / np.array([self.nx, self.ny, self.nz])
|
||||
filterscaling = np.amin(np.array([self.nx, self.ny, self.nz])) / np.array(
|
||||
[self.nx, self.ny, self.nz]
|
||||
)
|
||||
|
||||
# Adjust weighting to account for filter scaling
|
||||
self.weighting = np.multiply(self.weighting, filterscaling)
|
||||
@@ -185,7 +200,11 @@ class FractalVolume:
|
||||
|
||||
# Positional vector at centre of array, scaled by weighting
|
||||
v1 = np.array(
|
||||
[self.weighting[0] * self.nx / 2, self.weighting[1] * self.ny / 2, self.weighting[2] * self.nz / 2]
|
||||
[
|
||||
self.weighting[0] * self.nx / 2,
|
||||
self.weighting[1] * self.ny / 2,
|
||||
self.weighting[2] * self.nz / 2,
|
||||
]
|
||||
)
|
||||
|
||||
# 3D array of random numbers to be convolved with the fractal function
|
||||
@@ -222,10 +241,14 @@ class FractalVolume:
|
||||
)
|
||||
|
||||
# Bin fractal values
|
||||
bins = np.linspace(np.amin(self.fractalvolume), np.amax(self.fractalvolume), self.nbins)
|
||||
bins = np.linspace(
|
||||
np.amin(self.fractalvolume), np.amax(self.fractalvolume), self.nbins
|
||||
)
|
||||
for j in range(self.ny):
|
||||
for k in range(self.nz):
|
||||
self.fractalvolume[:, j, k] = np.digitize(self.fractalvolume[:, j, k], bins, right=True)
|
||||
self.fractalvolume[:, j, k] = np.digitize(
|
||||
self.fractalvolume[:, j, k], bins, right=True
|
||||
)
|
||||
|
||||
def generate_volume_mask(self):
|
||||
"""Generate a 3D volume to use as a mask for adding rough surfaces,
|
||||
@@ -254,7 +277,9 @@ class Grass:
|
||||
"""
|
||||
|
||||
self.numblades = numblades
|
||||
self.geometryparams = np.zeros((self.numblades, 6), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.geometryparams = np.zeros(
|
||||
(self.numblades, 6), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
self.seed = seed
|
||||
self.set_geometry_parameters()
|
||||
|
||||
|
@@ -52,7 +52,8 @@ def save_geometry_views(gvs):
|
||||
total=gv.nbytes,
|
||||
unit="byte",
|
||||
unit_scale=True,
|
||||
desc=f"Writing geometry view file {i + 1}/{len(gvs)}, " f"{gv.filename.name}{gv.vtkfiletype.ext}",
|
||||
desc=f"Writing geometry view file {i + 1}/{len(gvs)}, "
|
||||
f"{gv.filename.name}{gv.vtkfiletype.ext}",
|
||||
ncols=get_terminal_width() - 1,
|
||||
file=sys.stdout,
|
||||
disable=not config.sim_config.general["progressbars"],
|
||||
@@ -96,7 +97,9 @@ class GeometryView:
|
||||
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.filenamebase + config.get_model_config().appendmodelnumber)
|
||||
self.filename = Path(
|
||||
*parts[:-1], self.filenamebase + config.get_model_config().appendmodelnumber
|
||||
)
|
||||
|
||||
|
||||
class GeometryViewLines(GeometryView):
|
||||
@@ -122,7 +125,12 @@ class GeometryViewLines(GeometryView):
|
||||
):
|
||||
# Require contiguous for evtk library
|
||||
ID = np.ascontiguousarray(
|
||||
self.grid.ID[:, self.xs : self.xf : self.dx, self.ys : self.yf : self.dy, self.zs : self.zf : self.dz]
|
||||
self.grid.ID[
|
||||
:,
|
||||
self.xs : self.xf : self.dx,
|
||||
self.ys : self.yf : self.dy,
|
||||
self.zs : self.zf : self.dz,
|
||||
]
|
||||
)
|
||||
else:
|
||||
# This array is contiguous by design
|
||||
@@ -158,7 +166,15 @@ class GeometryViewLines(GeometryView):
|
||||
offsets_size = np.arange(start=2, step=2, stop=len(x) + 1, dtype="int32").nbytes
|
||||
connect_size = len(x) * np.dtype("int32").itemsize
|
||||
cell_type_size = len(x) * np.dtype("uint8").itemsize
|
||||
self.nbytes = x.nbytes + y.nbytes + z.nbytes + lines.nbytes + offsets_size + connect_size + cell_type_size
|
||||
self.nbytes = (
|
||||
x.nbytes
|
||||
+ y.nbytes
|
||||
+ z.nbytes
|
||||
+ lines.nbytes
|
||||
+ offsets_size
|
||||
+ connect_size
|
||||
+ cell_type_size
|
||||
)
|
||||
|
||||
vtk_data = {"x": x, "y": y, "z": z, "data": lines, "comments": comments}
|
||||
|
||||
@@ -205,7 +221,11 @@ class GeometryViewVoxels(GeometryView):
|
||||
):
|
||||
# Require contiguous for evtk library
|
||||
solid = np.ascontiguousarray(
|
||||
self.grid.solid[self.xs : self.xf : self.dx, self.ys : self.yf : self.dy, self.zs : self.zf : self.dz]
|
||||
self.grid.solid[
|
||||
self.xs : self.xf : self.dx,
|
||||
self.ys : self.yf : self.dy,
|
||||
self.zs : self.zf : self.dz,
|
||||
]
|
||||
)
|
||||
else:
|
||||
# This array is contiguous by design
|
||||
@@ -236,13 +256,21 @@ class GeometryViewVoxels(GeometryView):
|
||||
(self.grid.k0 * self.grid.dz * self.grid.ratio),
|
||||
)
|
||||
else:
|
||||
origin = ((self.xs * self.grid.dx), (self.ys * self.grid.dy), (self.zs * self.grid.dz))
|
||||
origin = (
|
||||
(self.xs * self.grid.dx),
|
||||
(self.ys * self.grid.dy),
|
||||
(self.zs * self.grid.dz),
|
||||
)
|
||||
|
||||
# Write the VTK file .vti
|
||||
imageToVTK(
|
||||
str(self.filename),
|
||||
origin=origin,
|
||||
spacing=((self.dx * self.grid.dx), (self.dy * self.grid.dy), (self.dz * self.grid.dz)),
|
||||
spacing=(
|
||||
(self.dx * self.grid.dx),
|
||||
(self.dy * self.grid.dy),
|
||||
(self.dz * self.grid.dz),
|
||||
),
|
||||
cellData={"Material": vtk_data["data"]},
|
||||
comments=[vtk_data["comments"]],
|
||||
)
|
||||
@@ -304,11 +332,17 @@ class Comments:
|
||||
if grid.pmls["thickness"]["z0"] - self.gv.zs > 0:
|
||||
pmlstorender["z0"] = int(grid.pmls["thickness"]["z0"] - self.gv.zs)
|
||||
if self.gv.xf > grid.nx - grid.pmls["thickness"]["xmax"]:
|
||||
pmlstorender["xmax"] = int(self.gv.xf - (grid.nx - grid.pmls["thickness"]["xmax"]))
|
||||
pmlstorender["xmax"] = int(
|
||||
self.gv.xf - (grid.nx - grid.pmls["thickness"]["xmax"])
|
||||
)
|
||||
if self.gv.yf > grid.ny - grid.pmls["thickness"]["ymax"]:
|
||||
pmlstorender["ymax"] = int(self.gv.yf - (grid.ny - grid.pmls["thickness"]["ymax"]))
|
||||
pmlstorender["ymax"] = int(
|
||||
self.gv.yf - (grid.ny - grid.pmls["thickness"]["ymax"])
|
||||
)
|
||||
if self.gv.zf > grid.nz - grid.pmls["thickness"]["zmax"]:
|
||||
pmlstorender["zmax"] = int(self.gv.zf - (grid.nz - grid.pmls["thickness"]["zmax"]))
|
||||
pmlstorender["zmax"] = int(
|
||||
self.gv.zf - (grid.nz - grid.pmls["thickness"]["zmax"])
|
||||
)
|
||||
|
||||
return list(pmlstorender.values())
|
||||
|
||||
@@ -316,7 +350,11 @@ class Comments:
|
||||
"""Used to name sources and/or receivers."""
|
||||
sc = []
|
||||
for src in srcs:
|
||||
p = (src.xcoord * self.grid.dx, src.ycoord * self.grid.dy, src.zcoord * self.grid.dz)
|
||||
p = (
|
||||
src.xcoord * self.grid.dx,
|
||||
src.ycoord * self.grid.dy,
|
||||
src.zcoord * self.grid.dz,
|
||||
)
|
||||
p = list(map(float, p))
|
||||
|
||||
s = {"name": src.ID, "position": p}
|
||||
@@ -332,7 +370,9 @@ class Comments:
|
||||
|
||||
def materials_comment(self):
|
||||
if not self.averaged_materials:
|
||||
return [m.ID for m in self.grid.materials if m.type != "dielectric-smoothed"]
|
||||
return [
|
||||
m.ID for m in self.grid.materials if m.type != "dielectric-smoothed"
|
||||
]
|
||||
else:
|
||||
return [m.ID for m in self.grid.materials]
|
||||
|
||||
@@ -340,7 +380,9 @@ class Comments:
|
||||
class GeometryObjects:
|
||||
"""Geometry objects to be written to file."""
|
||||
|
||||
def __init__(self, xs=None, ys=None, zs=None, xf=None, yf=None, zf=None, basefilename=None):
|
||||
def __init__(
|
||||
self, xs=None, ys=None, zs=None, xf=None, yf=None, zf=None, basefilename=None
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
xs, xf, ys, yf, zs, zf: ints for extent of the volume in cells.
|
||||
@@ -366,9 +408,23 @@ class GeometryObjects:
|
||||
self.filename_materials = self.filename_materials.with_suffix(".txt")
|
||||
|
||||
# Sizes of arrays to write necessary to update progress bar
|
||||
self.solidsize = (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.uint32).itemsize
|
||||
self.rigidsize = 18 * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.int8).itemsize
|
||||
self.IDsize = 6 * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.uint32).itemsize
|
||||
self.solidsize = (
|
||||
(self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.uint32).itemsize
|
||||
)
|
||||
self.rigidsize = (
|
||||
18
|
||||
* (self.nx + 1)
|
||||
* (self.ny + 1)
|
||||
* (self.nz + 1)
|
||||
* np.dtype(np.int8).itemsize
|
||||
)
|
||||
self.IDsize = (
|
||||
6
|
||||
* (self.nx + 1)
|
||||
* (self.ny + 1)
|
||||
* (self.nz + 1)
|
||||
* np.dtype(np.uint32).itemsize
|
||||
)
|
||||
self.datawritesize = self.solidsize + self.rigidsize + self.IDsize
|
||||
|
||||
def write_hdf5(self, G, pbar):
|
||||
@@ -385,16 +441,45 @@ class GeometryObjects:
|
||||
fdata.attrs["dx_dy_dz"] = (G.dx, G.dy, G.dz)
|
||||
|
||||
# Get minimum and maximum integers of materials in geometry objects volume
|
||||
minmat = np.amin(G.ID[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1])
|
||||
maxmat = np.amax(G.ID[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1])
|
||||
minmat = np.amin(
|
||||
G.ID[
|
||||
:,
|
||||
self.xs : self.xf + 1,
|
||||
self.ys : self.yf + 1,
|
||||
self.zs : self.zf + 1,
|
||||
]
|
||||
)
|
||||
maxmat = np.amax(
|
||||
G.ID[
|
||||
:,
|
||||
self.xs : self.xf + 1,
|
||||
self.ys : self.yf + 1,
|
||||
self.zs : self.zf + 1,
|
||||
]
|
||||
)
|
||||
fdata["/data"] = (
|
||||
G.solid[self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1].astype("int16") - minmat
|
||||
G.solid[
|
||||
self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1
|
||||
].astype("int16")
|
||||
- minmat
|
||||
)
|
||||
pbar.update(self.solidsize)
|
||||
fdata["/rigidE"] = G.rigidE[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1]
|
||||
fdata["/rigidH"] = G.rigidH[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1]
|
||||
fdata["/rigidE"] = G.rigidE[
|
||||
:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1
|
||||
]
|
||||
fdata["/rigidH"] = G.rigidH[
|
||||
:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1
|
||||
]
|
||||
pbar.update(self.rigidsize)
|
||||
fdata["/ID"] = G.ID[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1] - minmat
|
||||
fdata["/ID"] = (
|
||||
G.ID[
|
||||
:,
|
||||
self.xs : self.xf + 1,
|
||||
self.ys : self.yf + 1,
|
||||
self.zs : self.zf + 1,
|
||||
]
|
||||
- minmat
|
||||
)
|
||||
pbar.update(self.IDsize)
|
||||
|
||||
# Write materials list to a text file
|
||||
@@ -409,11 +494,18 @@ class GeometryObjects:
|
||||
)
|
||||
if hasattr(material, "poles"):
|
||||
if "debye" in material.type:
|
||||
dispersionstr = "#add_dispersion_debye: " f"{material.poles:g} "
|
||||
dispersionstr = (
|
||||
f"#add_dispersion_debye: {material.poles:g} "
|
||||
)
|
||||
for pole in range(material.poles):
|
||||
dispersionstr += f"{material.deltaer[pole]:g} " f"{material.tau[pole]:g} "
|
||||
dispersionstr += (
|
||||
f"{material.deltaer[pole]:g} "
|
||||
f"{material.tau[pole]:g} "
|
||||
)
|
||||
elif "lorenz" in material.type:
|
||||
dispersionstr = f"#add_dispersion_lorenz: " f"{material.poles:g} "
|
||||
dispersionstr = (
|
||||
f"#add_dispersion_lorenz: {material.poles:g} "
|
||||
)
|
||||
for pole in range(material.poles):
|
||||
dispersionstr += (
|
||||
f"{material.deltaer[pole]:g} "
|
||||
@@ -421,8 +513,13 @@ class GeometryObjects:
|
||||
f"{material.alpha[pole]:g} "
|
||||
)
|
||||
elif "drude" in material.type:
|
||||
dispersionstr = f"#add_dispersion_drude: " f"{material.poles:g} "
|
||||
dispersionstr = (
|
||||
f"#add_dispersion_drude: {material.poles:g} "
|
||||
)
|
||||
for pole in range(material.poles):
|
||||
dispersionstr += f"{material.tau[pole]:g} " f"{material.alpha[pole]:g} "
|
||||
dispersionstr += (
|
||||
f"{material.tau[pole]:g} "
|
||||
f"{material.alpha[pole]:g} "
|
||||
)
|
||||
dispersionstr += material.ID
|
||||
fmaterials.write(dispersionstr + "\n")
|
||||
|
@@ -47,7 +47,8 @@ help_msg = {
|
||||
"scenes": "(list, req): Scenes to run the model. Multiple scene objects "
|
||||
"can given in order to run multiple simulation runs. Each scene "
|
||||
"must contain the essential simulation objects",
|
||||
"inputfile": "(str, opt): Input file path. Can also run simulation by " "providing an input file.",
|
||||
"inputfile": "(str, opt): Input file path. Can also run simulation by "
|
||||
"providing an input file.",
|
||||
"outputfile": "(str, req): File path to the output data file.",
|
||||
"n": "(int, req): Number of required simulation runs.",
|
||||
"i": "(int, opt): Model number to start/restart simulation from. It would "
|
||||
@@ -59,16 +60,20 @@ help_msg = {
|
||||
"models to be farmed out using a MPI task farm, e.g. to create a "
|
||||
"B-scan with 60 traces and use MPI to farm out each trace. For "
|
||||
"further details see the performance section of the User Guide.",
|
||||
"gpu": "(list/bool, opt): Flag to use NVIDIA GPU or list of NVIDIA GPU " "device ID(s) for specific GPU card(s).",
|
||||
"opencl": "(list/bool, opt): Flag to use OpenCL or list of OpenCL device " "ID(s) for specific compute device(s).",
|
||||
"gpu": "(list/bool, opt): Flag to use NVIDIA GPU or list of NVIDIA GPU "
|
||||
"device ID(s) for specific GPU card(s).",
|
||||
"opencl": "(list/bool, opt): Flag to use OpenCL or list of OpenCL device "
|
||||
"ID(s) for specific compute device(s).",
|
||||
"subgrid": "(bool, opt): Flag to use sub-gridding.",
|
||||
"autotranslate": "(bool, opt): For sub-gridding - auto translate objects "
|
||||
"with main grid coordinates to their equivalent local "
|
||||
"grid coordinate within the subgrid. If this option is "
|
||||
"off users must specify sub-grid object point within the "
|
||||
"global subgrid space.",
|
||||
"geometry_only": "(bool, opt): Build a model and produce any geometry " "views but do not run the simulation.",
|
||||
"geometry_fixed": "(bool, opt): Run a series of models where the geometry " "does not change between models.",
|
||||
"geometry_only": "(bool, opt): Build a model and produce any geometry "
|
||||
"views but do not run the simulation.",
|
||||
"geometry_fixed": "(bool, opt): Run a series of models where the geometry "
|
||||
"does not change between models.",
|
||||
"write_processed": "(bool, opt): Writes another input file after any "
|
||||
"Python code (#python blocks) and in the original input "
|
||||
"file has been processed.",
|
||||
@@ -163,15 +168,26 @@ def cli():
|
||||
"""Entry point for command line interface (CLI)."""
|
||||
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(prog="gprMax", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="gprMax", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
parser.add_argument("inputfile", help=help_msg["inputfile"])
|
||||
parser.add_argument("-n", default=args_defaults["n"], type=int, help=help_msg["n"])
|
||||
parser.add_argument("-i", type=int, help=help_msg["i"])
|
||||
parser.add_argument("-mpi", action="store_true", default=args_defaults["mpi"], help=help_msg["mpi"])
|
||||
parser.add_argument("-gpu", type=int, action="append", nargs="*", help=help_msg["gpu"])
|
||||
parser.add_argument("-opencl", type=int, action="append", nargs="*", help=help_msg["opencl"])
|
||||
parser.add_argument(
|
||||
"--geometry-only", action="store_true", default=args_defaults["geometry_only"], help=help_msg["geometry_only"]
|
||||
"-mpi", action="store_true", default=args_defaults["mpi"], help=help_msg["mpi"]
|
||||
)
|
||||
parser.add_argument(
|
||||
"-gpu", type=int, action="append", nargs="*", help=help_msg["gpu"]
|
||||
)
|
||||
parser.add_argument(
|
||||
"-opencl", type=int, action="append", nargs="*", help=help_msg["opencl"]
|
||||
)
|
||||
parser.add_argument(
|
||||
"--geometry-only",
|
||||
action="store_true",
|
||||
default=args_defaults["geometry_only"],
|
||||
help=help_msg["geometry_only"],
|
||||
)
|
||||
parser.add_argument(
|
||||
"--geometry-fixed",
|
||||
@@ -185,8 +201,18 @@ def cli():
|
||||
default=args_defaults["write_processed"],
|
||||
help=help_msg["write_processed"],
|
||||
)
|
||||
parser.add_argument("--log-level", type=int, default=args_defaults["log_level"], help=help_msg["log_level"])
|
||||
parser.add_argument("--log-file", action="store_true", default=args_defaults["log_file"], help=help_msg["log_file"])
|
||||
parser.add_argument(
|
||||
"--log-level",
|
||||
type=int,
|
||||
default=args_defaults["log_level"],
|
||||
help=help_msg["log_level"],
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-file",
|
||||
action="store_true",
|
||||
default=args_defaults["log_file"],
|
||||
help=help_msg["log_file"],
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
results = run_main(args)
|
||||
|
116
gprMax/grid.py
116
gprMax/grid.py
@@ -128,30 +128,67 @@ class FDTDGrid:
|
||||
|
||||
def initialise_field_arrays(self):
|
||||
"""Initialise arrays for the electric and magnetic field components."""
|
||||
self.Ex = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Ey = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Ez = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Hx = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Hy = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Hz = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Ex = np.zeros(
|
||||
(self.nx + 1, self.ny + 1, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.Ey = np.zeros(
|
||||
(self.nx + 1, self.ny + 1, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.Ez = np.zeros(
|
||||
(self.nx + 1, self.ny + 1, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.Hx = np.zeros(
|
||||
(self.nx + 1, self.ny + 1, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.Hy = np.zeros(
|
||||
(self.nx + 1, self.ny + 1, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.Hz = np.zeros(
|
||||
(self.nx + 1, self.ny + 1, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
|
||||
def initialise_std_update_coeff_arrays(self):
|
||||
"""Initialise arrays for storing update coefficients."""
|
||||
self.updatecoeffsE = np.zeros((len(self.materials), 5), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.updatecoeffsH = np.zeros((len(self.materials), 5), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.updatecoeffsE = np.zeros(
|
||||
(len(self.materials), 5), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
self.updatecoeffsH = np.zeros(
|
||||
(len(self.materials), 5), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
|
||||
def initialise_dispersive_arrays(self):
|
||||
"""Initialise field arrays when there are dispersive materials present."""
|
||||
self.Tx = np.zeros(
|
||||
(config.get_model_config().materials["maxpoles"], self.nx + 1, self.ny + 1, self.nz + 1),
|
||||
(
|
||||
config.get_model_config().materials["maxpoles"],
|
||||
self.nx + 1,
|
||||
self.ny + 1,
|
||||
self.nz + 1,
|
||||
),
|
||||
dtype=config.get_model_config().materials["dispersivedtype"],
|
||||
)
|
||||
self.Ty = np.zeros(
|
||||
(config.get_model_config().materials["maxpoles"], self.nx + 1, self.ny + 1, self.nz + 1),
|
||||
(
|
||||
config.get_model_config().materials["maxpoles"],
|
||||
self.nx + 1,
|
||||
self.ny + 1,
|
||||
self.nz + 1,
|
||||
),
|
||||
dtype=config.get_model_config().materials["dispersivedtype"],
|
||||
)
|
||||
self.Tz = np.zeros(
|
||||
(config.get_model_config().materials["maxpoles"], self.nx + 1, self.ny + 1, self.nz + 1),
|
||||
(
|
||||
config.get_model_config().materials["maxpoles"],
|
||||
self.nx + 1,
|
||||
self.ny + 1,
|
||||
self.nz + 1,
|
||||
),
|
||||
dtype=config.get_model_config().materials["dispersivedtype"],
|
||||
)
|
||||
|
||||
@@ -185,7 +222,9 @@ class FDTDGrid:
|
||||
solidarray = self.nx * self.ny * self.nz * np.dtype(np.uint32).itemsize
|
||||
|
||||
# 12 x rigidE array components + 6 x rigidH array components
|
||||
rigidarrays = (12 + 6) * self.nx * self.ny * self.nz * np.dtype(np.int8).itemsize
|
||||
rigidarrays = (
|
||||
(12 + 6) * self.nx * self.ny * self.nz * np.dtype(np.int8).itemsize
|
||||
)
|
||||
|
||||
# 6 x field arrays + 6 x ID arrays
|
||||
fieldarrays = (
|
||||
@@ -288,14 +327,24 @@ class FDTDGrid:
|
||||
def calculate_dt(self):
|
||||
"""Calculate time step at the CFL limit."""
|
||||
if config.get_model_config().mode == "2D TMx":
|
||||
self.dt = 1 / (config.sim_config.em_consts["c"] * np.sqrt((1 / self.dy**2) + (1 / self.dz**2)))
|
||||
self.dt = 1 / (
|
||||
config.sim_config.em_consts["c"]
|
||||
* np.sqrt((1 / self.dy**2) + (1 / self.dz**2))
|
||||
)
|
||||
elif config.get_model_config().mode == "2D TMy":
|
||||
self.dt = 1 / (config.sim_config.em_consts["c"] * np.sqrt((1 / self.dx**2) + (1 / self.dz**2)))
|
||||
self.dt = 1 / (
|
||||
config.sim_config.em_consts["c"]
|
||||
* np.sqrt((1 / self.dx**2) + (1 / self.dz**2))
|
||||
)
|
||||
elif config.get_model_config().mode == "2D TMz":
|
||||
self.dt = 1 / (config.sim_config.em_consts["c"] * np.sqrt((1 / self.dx**2) + (1 / self.dy**2)))
|
||||
self.dt = 1 / (
|
||||
config.sim_config.em_consts["c"]
|
||||
* np.sqrt((1 / self.dx**2) + (1 / self.dy**2))
|
||||
)
|
||||
else:
|
||||
self.dt = 1 / (
|
||||
config.sim_config.em_consts["c"] * np.sqrt((1 / self.dx**2) + (1 / self.dy**2) + (1 / self.dz**2))
|
||||
config.sim_config.em_consts["c"]
|
||||
* np.sqrt((1 / self.dx**2) + (1 / self.dy**2) + (1 / self.dz**2))
|
||||
)
|
||||
|
||||
# Round down time step to nearest float with precision one less than
|
||||
@@ -322,7 +371,11 @@ class CUDAGrid(FDTDGrid):
|
||||
magnetic field arrays on a GPU.
|
||||
"""
|
||||
|
||||
self.bpg = (int(np.ceil(((self.nx + 1) * (self.ny + 1) * (self.nz + 1)) / self.tpb[0])), 1, 1)
|
||||
self.bpg = (
|
||||
int(np.ceil(((self.nx + 1) * (self.ny + 1) * (self.nz + 1)) / self.tpb[0])),
|
||||
1,
|
||||
1,
|
||||
)
|
||||
|
||||
def htod_geometry_arrays(self):
|
||||
"""Initialise an array for cell edge IDs (ID) on compute device."""
|
||||
@@ -342,7 +395,9 @@ class CUDAGrid(FDTDGrid):
|
||||
def htod_dispersive_arrays(self):
|
||||
"""Initialise dispersive material coefficient arrays on compute device."""
|
||||
|
||||
self.updatecoeffsdispersive_dev = self.gpuarray.to_gpu(self.updatecoeffsdispersive)
|
||||
self.updatecoeffsdispersive_dev = self.gpuarray.to_gpu(
|
||||
self.updatecoeffsdispersive
|
||||
)
|
||||
self.Tx_dev = self.gpuarray.to_gpu(self.Tx)
|
||||
self.Ty_dev = self.gpuarray.to_gpu(self.Ty)
|
||||
self.Tz_dev = self.gpuarray.to_gpu(self.Tz)
|
||||
@@ -386,7 +441,9 @@ class OpenCLGrid(FDTDGrid):
|
||||
queue: pyopencl queue.
|
||||
"""
|
||||
|
||||
self.updatecoeffsdispersive_dev = self.clarray.to_device(queue, self.updatecoeffsdispersive)
|
||||
self.updatecoeffsdispersive_dev = self.clarray.to_device(
|
||||
queue, self.updatecoeffsdispersive
|
||||
)
|
||||
# self.updatecoeffsdispersive_dev = self.clarray.to_device(queue, np.ones((95,95,95), dtype=np.float32))
|
||||
self.Tx_dev = self.clarray.to_device(queue, self.Tx)
|
||||
self.Ty_dev = self.clarray.to_device(queue, self.Ty)
|
||||
@@ -431,7 +488,9 @@ def dispersion_analysis(G):
|
||||
iterations = min(iterations, G.iterations)
|
||||
waveformvalues = np.zeros(G.iterations)
|
||||
for iteration in range(G.iterations):
|
||||
waveformvalues[iteration] = waveform.calculate_value(iteration * G.dt, G.dt)
|
||||
waveformvalues[iteration] = waveform.calculate_value(
|
||||
iteration * G.dt, G.dt
|
||||
)
|
||||
|
||||
# Ensure source waveform is not being overly truncated before attempting any FFT
|
||||
if np.abs(waveformvalues[-1]) < np.abs(np.amax(waveformvalues)) / 100:
|
||||
@@ -444,7 +503,10 @@ def dispersion_analysis(G):
|
||||
try:
|
||||
freqthres = (
|
||||
np.where(
|
||||
power[freqmaxpower:] < -config.get_model_config().numdispersion["highestfreqthres"]
|
||||
power[freqmaxpower:]
|
||||
< -config.get_model_config().numdispersion[
|
||||
"highestfreqthres"
|
||||
]
|
||||
)[0][0]
|
||||
+ freqmaxpower
|
||||
)
|
||||
@@ -463,7 +525,8 @@ def dispersion_analysis(G):
|
||||
# If waveform is truncated don't do any further analysis
|
||||
else:
|
||||
results["error"] = (
|
||||
"waveform does not fit within specified " + "time window and is therefore being truncated."
|
||||
"waveform does not fit within specified "
|
||||
+ "time window and is therefore being truncated."
|
||||
)
|
||||
else:
|
||||
results["error"] = "no waveform detected."
|
||||
@@ -511,9 +574,14 @@ def dispersion_analysis(G):
|
||||
results["N"] = minwavelength / delta
|
||||
|
||||
# Check grid sampling will result in physical wave propagation
|
||||
if int(np.floor(results["N"])) >= config.get_model_config().numdispersion["mingridsampling"]:
|
||||
if (
|
||||
int(np.floor(results["N"]))
|
||||
>= config.get_model_config().numdispersion["mingridsampling"]
|
||||
):
|
||||
# Numerical phase velocity
|
||||
vp = np.pi / (results["N"] * np.arcsin((1 / S) * np.sin((np.pi * S) / results["N"])))
|
||||
vp = np.pi / (
|
||||
results["N"] * np.arcsin((1 / S) * np.sin((np.pi * S) / results["N"]))
|
||||
)
|
||||
|
||||
# Physical phase velocity error (percentage)
|
||||
results["deltavp"] = (((vp * config.c) - config.c) / config.c) * 100
|
||||
|
@@ -48,7 +48,11 @@ def process_python_include_code(inputfile, usernamespace):
|
||||
"""
|
||||
|
||||
# Strip out any newline characters and comments that must begin with double hashes
|
||||
inputlines = [line.rstrip() for line in inputfile if (not line.startswith("##") and line.rstrip("\n"))]
|
||||
inputlines = [
|
||||
line.rstrip()
|
||||
for line in inputfile
|
||||
if (not line.startswith("##") and line.rstrip("\n"))
|
||||
]
|
||||
|
||||
# Rewind input file in preparation for any subsequent reading function
|
||||
inputfile.seek(0)
|
||||
@@ -74,7 +78,8 @@ def process_python_include_code(inputfile, usernamespace):
|
||||
x += 1
|
||||
if x == len(inputlines):
|
||||
logger.exception(
|
||||
"Cannot find the end of the Python code " + "block, i.e. missing #end_python: command."
|
||||
"Cannot find the end of the Python code "
|
||||
+ "block, i.e. missing #end_python: command."
|
||||
)
|
||||
raise SyntaxError
|
||||
# Compile code for faster execution
|
||||
@@ -147,7 +152,9 @@ def process_include_files(hashcmds):
|
||||
# See if file exists at specified path and if not try input file directory
|
||||
includefile = Path(includefile)
|
||||
if not includefile.exists():
|
||||
includefile = Path(config.sim_config.input_file_path.parent, includefile)
|
||||
includefile = Path(
|
||||
config.sim_config.input_file_path.parent, includefile
|
||||
)
|
||||
|
||||
with open(includefile, "r") as f:
|
||||
# Strip out any newline characters and comments that must begin with double hashes
|
||||
@@ -289,7 +296,9 @@ def check_cmd_names(processedlines, checkessential=True):
|
||||
# are no parameters for a command, e.g. for #taguchi:
|
||||
if " " not in cmdparams[0] and len(cmdparams.strip("\n")) != 0:
|
||||
logger.exception(
|
||||
"There must be a space between the command name " + "and parameters in " + processedlines[lindex]
|
||||
"There must be a space between the command name "
|
||||
+ "and parameters in "
|
||||
+ processedlines[lindex]
|
||||
)
|
||||
raise SyntaxError
|
||||
|
||||
@@ -312,7 +321,11 @@ def check_cmd_names(processedlines, checkessential=True):
|
||||
if singlecmds[cmdname] is None:
|
||||
singlecmds[cmdname] = cmd[1].strip(" \t\n")
|
||||
else:
|
||||
logger.exception("You can only have a single instance of " + cmdname + " in your model")
|
||||
logger.exception(
|
||||
"You can only have a single instance of "
|
||||
+ cmdname
|
||||
+ " in your model"
|
||||
)
|
||||
raise SyntaxError
|
||||
|
||||
elif cmdname in multiplecmds:
|
||||
@@ -384,7 +397,10 @@ def parse_hash_commands(scene):
|
||||
for key, value in sorted(usernamespace.items()):
|
||||
if key != "__builtins__":
|
||||
uservars += f"{key}: {value}, "
|
||||
logger.info(f"Constants/variables used/available for Python scripting: " + f"{{{uservars[:-2]}}}\n")
|
||||
logger.info(
|
||||
f"Constants/variables used/available for Python scripting: "
|
||||
+ f"{{{uservars[:-2]}}}\n"
|
||||
)
|
||||
|
||||
# Write a file containing the input commands after Python or include
|
||||
# file commands have been processed
|
||||
|
@@ -56,11 +56,12 @@ def process_geometrycmds(geometry):
|
||||
tmp = object.split()
|
||||
|
||||
if tmp[0] == "#geometry_objects_read:":
|
||||
from .cmds_geometry.geometry_objects_read import \
|
||||
GeometryObjectsRead
|
||||
from .cmds_geometry.geometry_objects_read import GeometryObjectsRead
|
||||
|
||||
if len(tmp) != 6:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires exactly five parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires exactly five parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -70,7 +71,9 @@ def process_geometrycmds(geometry):
|
||||
|
||||
elif tmp[0] == "#edge:":
|
||||
if len(tmp) != 8:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires exactly seven parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires exactly seven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
edge = Edge(
|
||||
@@ -83,7 +86,9 @@ def process_geometrycmds(geometry):
|
||||
|
||||
elif tmp[0] == "#plate:":
|
||||
if len(tmp) < 8:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least seven parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires at least seven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Isotropic case
|
||||
@@ -103,14 +108,18 @@ def process_geometrycmds(geometry):
|
||||
)
|
||||
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(plate)
|
||||
|
||||
elif tmp[0] == "#triangle:":
|
||||
if len(tmp) < 12:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least eleven parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires at least eleven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -120,26 +129,40 @@ def process_geometrycmds(geometry):
|
||||
|
||||
# Isotropic case with no user specified averaging
|
||||
if len(tmp) == 12:
|
||||
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11])
|
||||
triangle = Triangle(
|
||||
p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11]
|
||||
)
|
||||
|
||||
# Isotropic case with user specified averaging
|
||||
elif len(tmp) == 13:
|
||||
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness,
|
||||
material_id=tmp[11], averaging=tmp[12].lower())
|
||||
triangle = Triangle(
|
||||
p1=p1,
|
||||
p2=p2,
|
||||
p3=p3,
|
||||
thickness=thickness,
|
||||
material_id=tmp[11],
|
||||
averaging=tmp[12].lower(),
|
||||
)
|
||||
|
||||
# Uniaxial anisotropic case
|
||||
elif len(tmp) == 14:
|
||||
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_ids=tmp[11:])
|
||||
triangle = Triangle(
|
||||
p1=p1, p2=p2, p3=p3, thickness=thickness, material_ids=tmp[11:]
|
||||
)
|
||||
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(triangle)
|
||||
|
||||
elif tmp[0] == "#box:":
|
||||
if len(tmp) < 8:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least seven parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires at least seven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -158,14 +181,18 @@ def process_geometrycmds(geometry):
|
||||
box = Box(p1=p1, p2=p2, material_ids=tmp[7:])
|
||||
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(box)
|
||||
|
||||
elif tmp[0] == "#cylinder:":
|
||||
if len(tmp) < 9:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least eight parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires at least eight parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -178,21 +205,27 @@ def process_geometrycmds(geometry):
|
||||
|
||||
# Isotropic case with user specified averaging
|
||||
elif len(tmp) == 10:
|
||||
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8], averaging=tmp[9].lower())
|
||||
cylinder = Cylinder(
|
||||
p1=p1, p2=p2, r=r, material_id=tmp[8], averaging=tmp[9].lower()
|
||||
)
|
||||
|
||||
# Uniaxial anisotropic case
|
||||
elif len(tmp) == 11:
|
||||
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_ids=tmp[8:])
|
||||
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(cylinder)
|
||||
|
||||
elif tmp[0] == "#cone:":
|
||||
if len(tmp) < 10:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least nine parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires at least nine parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -206,21 +239,32 @@ def process_geometrycmds(geometry):
|
||||
|
||||
# Isotropic case with user specified averaging
|
||||
elif len(tmp) == 11:
|
||||
cone = Cone(p1=p1, p2=p2, r1=r1, r2=r2, material_id=tmp[9], averaging=tmp[10].lower())
|
||||
cone = Cone(
|
||||
p1=p1,
|
||||
p2=p2,
|
||||
r1=r1,
|
||||
r2=r2,
|
||||
material_id=tmp[9],
|
||||
averaging=tmp[10].lower(),
|
||||
)
|
||||
|
||||
# Uniaxial anisotropic case
|
||||
elif len(tmp) == 12:
|
||||
cone = Cone(p1=p1, p2=p2, r1=r1, r2=r2, material_ids=tmp[9:])
|
||||
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(cone)
|
||||
|
||||
elif tmp[0] == "#cylindrical_sector:":
|
||||
if len(tmp) < 10:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least nine parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires at least nine parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
normal = tmp[1].lower()
|
||||
@@ -276,14 +320,18 @@ def process_geometrycmds(geometry):
|
||||
)
|
||||
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(cylindrical_sector)
|
||||
|
||||
elif tmp[0] == "#sphere:":
|
||||
if len(tmp) < 6:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least five parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires at least five parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -295,21 +343,27 @@ def process_geometrycmds(geometry):
|
||||
|
||||
# Isotropic case with user specified averaging
|
||||
elif len(tmp) == 7:
|
||||
sphere = Sphere(p1=p1, r=r, material_id=tmp[5], averaging=tmp[6].lower())
|
||||
sphere = Sphere(
|
||||
p1=p1, r=r, material_id=tmp[5], averaging=tmp[6].lower()
|
||||
)
|
||||
|
||||
# Uniaxial anisotropic case
|
||||
elif len(tmp) == 8:
|
||||
sphere = Sphere(p1=p1, r=r, material_id=tmp[5:])
|
||||
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(sphere)
|
||||
|
||||
elif tmp[0] == "#ellipsoid:":
|
||||
if len(tmp) < 8:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least seven parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires at least seven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -323,15 +377,23 @@ def process_geometrycmds(geometry):
|
||||
|
||||
# Isotropic case with user specified averaging
|
||||
elif len(tmp) == 9:
|
||||
ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr,
|
||||
material_id=tmp[7], averaging=tmp[8].lower())
|
||||
ellipsoid = Ellipsoid(
|
||||
p1=p1,
|
||||
xr=xr,
|
||||
yr=yr,
|
||||
zr=zr,
|
||||
material_id=tmp[7],
|
||||
averaging=tmp[8].lower(),
|
||||
)
|
||||
|
||||
# Uniaxial anisotropic case
|
||||
elif len(tmp) == 8:
|
||||
ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr, material_id=tmp[7:])
|
||||
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(ellipsoid)
|
||||
@@ -340,7 +402,9 @@ def process_geometrycmds(geometry):
|
||||
# Default is no dielectric smoothing for a fractal box
|
||||
|
||||
if len(tmp) < 14:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least thirteen parameters")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " requires at least thirteen parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -385,7 +449,9 @@ def process_geometrycmds(geometry):
|
||||
averaging=tmp[15].lower(),
|
||||
)
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'" + " ".join(tmp) + "'" + " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(fb)
|
||||
@@ -396,7 +462,12 @@ def process_geometrycmds(geometry):
|
||||
|
||||
if tmp[0] == "#add_surface_roughness:":
|
||||
if len(tmp) < 13:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least twelve parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least twelve parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -426,14 +497,24 @@ def process_geometrycmds(geometry):
|
||||
seed=int(tmp[13]),
|
||||
)
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(asr)
|
||||
|
||||
if tmp[0] == "#add_surface_water:":
|
||||
if len(tmp) != 9:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires exactly eight parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires exactly eight parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -441,12 +522,19 @@ def process_geometrycmds(geometry):
|
||||
depth = float(tmp[7])
|
||||
fractal_box_id = tmp[8]
|
||||
|
||||
asf = AddSurfaceWater(p1=p1, p2=p2, depth=depth, fractal_box_id=fractal_box_id)
|
||||
asf = AddSurfaceWater(
|
||||
p1=p1, p2=p2, depth=depth, fractal_box_id=fractal_box_id
|
||||
)
|
||||
scene_objects.append(asf)
|
||||
|
||||
if tmp[0] == "#add_grass:":
|
||||
if len(tmp) < 12:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " requires at least eleven parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least eleven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
@@ -476,7 +564,12 @@ def process_geometrycmds(geometry):
|
||||
seed=int(tmp[12]),
|
||||
)
|
||||
else:
|
||||
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " too many parameters have been given"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(grass)
|
||||
|
@@ -18,13 +18,27 @@
|
||||
|
||||
import logging
|
||||
|
||||
from .cmds_multiuse import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion,
|
||||
AddLorentzDispersion, ExcitationFile,
|
||||
GeometryObjectsWrite, GeometryView, HertzianDipole,
|
||||
MagneticDipole, Material, MaterialList,
|
||||
MaterialRange, Rx, RxArray, Snapshot,
|
||||
SoilPeplinski, TransmissionLine, VoltageSource,
|
||||
Waveform)
|
||||
from .cmds_multiuse import (
|
||||
PMLCFS,
|
||||
AddDebyeDispersion,
|
||||
AddDrudeDispersion,
|
||||
AddLorentzDispersion,
|
||||
ExcitationFile,
|
||||
GeometryObjectsWrite,
|
||||
GeometryView,
|
||||
HertzianDipole,
|
||||
MagneticDipole,
|
||||
Material,
|
||||
MaterialList,
|
||||
MaterialRange,
|
||||
Rx,
|
||||
RxArray,
|
||||
Snapshot,
|
||||
SoilPeplinski,
|
||||
TransmissionLine,
|
||||
VoltageSource,
|
||||
Waveform,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -47,10 +61,19 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) != 4:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly four parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires exactly four parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
waveform = Waveform(wave_type=tmp[0], amp=float(tmp[1]), freq=float(tmp[2]), id=tmp[3])
|
||||
waveform = Waveform(
|
||||
wave_type=tmp[0], amp=float(tmp[1]), freq=float(tmp[2]), id=tmp[3]
|
||||
)
|
||||
scene_objects.append(waveform)
|
||||
|
||||
cmdname = "#voltage_source"
|
||||
@@ -74,7 +97,14 @@ def process_multicmds(multicmds):
|
||||
end=float(tmp[7]),
|
||||
)
|
||||
else:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least six parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least six parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(voltage_source)
|
||||
@@ -84,11 +114,20 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) < 5:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least five parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least five parameters"
|
||||
)
|
||||
raise ValueError
|
||||
if len(tmp) == 5:
|
||||
hertzian_dipole = HertzianDipole(
|
||||
polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4]
|
||||
polarisation=tmp[0],
|
||||
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
|
||||
waveform_id=tmp[4],
|
||||
)
|
||||
elif len(tmp) == 7:
|
||||
hertzian_dipole = HertzianDipole(
|
||||
@@ -99,7 +138,9 @@ def process_multicmds(multicmds):
|
||||
end=float(tmp[6]),
|
||||
)
|
||||
else:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " too many parameters")
|
||||
logger.exception(
|
||||
"'" + cmdname + ": " + " ".join(tmp) + "'" + " too many parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(hertzian_dipole)
|
||||
@@ -109,11 +150,20 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) < 5:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least five parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least five parameters"
|
||||
)
|
||||
raise ValueError
|
||||
if len(tmp) == 5:
|
||||
magnetic_dipole = MagneticDipole(
|
||||
polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4]
|
||||
polarisation=tmp[0],
|
||||
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
|
||||
waveform_id=tmp[4],
|
||||
)
|
||||
elif len(tmp) == 7:
|
||||
magnetic_dipole = MagneticDipole(
|
||||
@@ -124,7 +174,9 @@ def process_multicmds(multicmds):
|
||||
end=float(tmp[6]),
|
||||
)
|
||||
else:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " too many parameters")
|
||||
logger.exception(
|
||||
"'" + cmdname + ": " + " ".join(tmp) + "'" + " too many parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(magnetic_dipole)
|
||||
@@ -134,7 +186,14 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) < 6:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least six parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least six parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
if len(tmp) == 6:
|
||||
@@ -154,7 +213,9 @@ def process_multicmds(multicmds):
|
||||
end=tmp[7],
|
||||
)
|
||||
else:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " too many parameters")
|
||||
logger.exception(
|
||||
"'" + cmdname + ": " + " ".join(tmp) + "'" + " too many parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(tl)
|
||||
@@ -168,7 +229,9 @@ def process_multicmds(multicmds):
|
||||
raise ValueError
|
||||
|
||||
if len(tmp) > 1:
|
||||
ex_file = ExcitationFile(filepath=tmp[0], kind=tmp[1], fill_value=tmp[2])
|
||||
ex_file = ExcitationFile(
|
||||
filepath=tmp[0], kind=tmp[1], fill_value=tmp[2]
|
||||
)
|
||||
else:
|
||||
ex_file = ExcitationFile(filepath=tmp[0])
|
||||
|
||||
@@ -179,12 +242,23 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) != 3 and len(tmp) < 5:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " has an incorrect number of parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " has an incorrect number of parameters"
|
||||
)
|
||||
raise ValueError
|
||||
if len(tmp) == 3:
|
||||
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])))
|
||||
else:
|
||||
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])), id=tmp[3], outputs=[" ".join(tmp[4:])])
|
||||
rx = Rx(
|
||||
p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])),
|
||||
id=tmp[3],
|
||||
outputs=[" ".join(tmp[4:])],
|
||||
)
|
||||
|
||||
scene_objects.append(rx)
|
||||
|
||||
@@ -193,7 +267,14 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) != 9:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly nine parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires exactly nine parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2]))
|
||||
@@ -208,7 +289,14 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) != 11:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly eleven parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires exactly eleven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2]))
|
||||
@@ -218,7 +306,9 @@ def process_multicmds(multicmds):
|
||||
|
||||
try:
|
||||
iterations = int(tmp[9])
|
||||
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, iterations=iterations, filename=filename)
|
||||
snapshot = Snapshot(
|
||||
p1=p1, p2=p2, dl=dl, iterations=iterations, filename=filename
|
||||
)
|
||||
|
||||
except ValueError:
|
||||
time = float(tmp[9])
|
||||
@@ -231,10 +321,23 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) != 5:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly five parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires exactly five parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
material = Material(er=float(tmp[0]), se=float(tmp[1]), mr=float(tmp[2]), sm=float(tmp[3]), id=tmp[4])
|
||||
material = Material(
|
||||
er=float(tmp[0]),
|
||||
se=float(tmp[1]),
|
||||
mr=float(tmp[2]),
|
||||
sm=float(tmp[3]),
|
||||
id=tmp[4],
|
||||
)
|
||||
scene_objects.append(material)
|
||||
|
||||
cmdname = "#add_dispersion_debye"
|
||||
@@ -243,7 +346,14 @@ def process_multicmds(multicmds):
|
||||
tmp = cmdinstance.split()
|
||||
|
||||
if len(tmp) < 4:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least four parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least four parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
poles = int(tmp[0])
|
||||
@@ -255,7 +365,9 @@ def process_multicmds(multicmds):
|
||||
er_delta.append(float(tmp[pole]))
|
||||
tau.append(float(tmp[pole + 1]))
|
||||
|
||||
debye_dispersion = AddDebyeDispersion(poles=poles, er_delta=er_delta, tau=tau, material_ids=material_ids)
|
||||
debye_dispersion = AddDebyeDispersion(
|
||||
poles=poles, er_delta=er_delta, tau=tau, material_ids=material_ids
|
||||
)
|
||||
scene_objects.append(debye_dispersion)
|
||||
|
||||
cmdname = "#add_dispersion_lorentz"
|
||||
@@ -264,7 +376,14 @@ def process_multicmds(multicmds):
|
||||
tmp = cmdinstance.split()
|
||||
|
||||
if len(tmp) < 5:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least five parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least five parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
poles = int(tmp[0])
|
||||
@@ -279,7 +398,11 @@ def process_multicmds(multicmds):
|
||||
alpha.append(float(tmp[pole + 2]))
|
||||
|
||||
lorentz_dispersion = AddLorentzDispersion(
|
||||
poles=poles, material_ids=material_ids, er_delta=er_delta, tau=tau, alpha=alpha
|
||||
poles=poles,
|
||||
material_ids=material_ids,
|
||||
er_delta=er_delta,
|
||||
tau=tau,
|
||||
alpha=alpha,
|
||||
)
|
||||
scene_objects.append(lorentz_dispersion)
|
||||
|
||||
@@ -289,7 +412,14 @@ def process_multicmds(multicmds):
|
||||
tmp = cmdinstance.split()
|
||||
|
||||
if len(tmp) < 5:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least five parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least five parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
poles = int(tmp[0])
|
||||
@@ -301,7 +431,9 @@ def process_multicmds(multicmds):
|
||||
tau.append(float(tmp[pole]))
|
||||
alpha.append(float(tmp[pole + 1]))
|
||||
|
||||
drude_dispersion = AddDrudeDispersion(poles=poles, material_ids=material_ids, tau=tau, alpha=alpha)
|
||||
drude_dispersion = AddDrudeDispersion(
|
||||
poles=poles, material_ids=material_ids, tau=tau, alpha=alpha
|
||||
)
|
||||
scene_objects.append(drude_dispersion)
|
||||
|
||||
cmdname = "#soil_peplinski"
|
||||
@@ -310,7 +442,14 @@ def process_multicmds(multicmds):
|
||||
tmp = cmdinstance.split()
|
||||
|
||||
if len(tmp) != 7:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at exactly seven parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at exactly seven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
soil = SoilPeplinski(
|
||||
sand_fraction=float(tmp[0]),
|
||||
@@ -328,14 +467,23 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) != 11:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly eleven parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires exactly eleven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
|
||||
p2 = float(tmp[3]), float(tmp[4]), float(tmp[5])
|
||||
dl = float(tmp[6]), float(tmp[7]), float(tmp[8])
|
||||
|
||||
geometry_view = GeometryView(p1=p1, p2=p2, dl=dl, filename=tmp[9], output_type=tmp[10])
|
||||
geometry_view = GeometryView(
|
||||
p1=p1, p2=p2, dl=dl, filename=tmp[9], output_type=tmp[10]
|
||||
)
|
||||
scene_objects.append(geometry_view)
|
||||
|
||||
cmdname = "#geometry_objects_write"
|
||||
@@ -343,7 +491,14 @@ def process_multicmds(multicmds):
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
if len(tmp) != 7:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly seven parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires exactly seven parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
|
||||
@@ -357,7 +512,14 @@ def process_multicmds(multicmds):
|
||||
tmp = cmdinstance.split()
|
||||
|
||||
if len(tmp) != 9:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at exactly nine parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at exactly nine parameters"
|
||||
)
|
||||
raise ValueError
|
||||
material_range = MaterialRange(
|
||||
er_lower=float(tmp[0]),
|
||||
@@ -378,7 +540,14 @@ def process_multicmds(multicmds):
|
||||
tmp = cmdinstance.split()
|
||||
|
||||
if len(tmp) < 2:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least two parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires at least two parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
tokens = len(tmp)
|
||||
@@ -395,7 +564,14 @@ def process_multicmds(multicmds):
|
||||
tmp = cmdinstance.split()
|
||||
|
||||
if len(tmp) != 12:
|
||||
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly twelve parameters")
|
||||
logger.exception(
|
||||
"'"
|
||||
+ cmdname
|
||||
+ ": "
|
||||
+ " ".join(tmp)
|
||||
+ "'"
|
||||
+ " requires exactly twelve parameters"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
pml_cfs = PMLCFS(
|
||||
|
@@ -18,9 +18,18 @@
|
||||
|
||||
import logging
|
||||
|
||||
from .cmds_singleuse import (Discretisation, Domain, OMPThreads, OutputDir,
|
||||
PMLProps, RxSteps, SrcSteps,
|
||||
TimeStepStabilityFactor, TimeWindow, Title)
|
||||
from .cmds_singleuse import (
|
||||
Discretisation,
|
||||
Domain,
|
||||
OMPThreads,
|
||||
OutputDir,
|
||||
PMLProps,
|
||||
RxSteps,
|
||||
SrcSteps,
|
||||
TimeStepStabilityFactor,
|
||||
TimeWindow,
|
||||
Title,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -54,7 +63,9 @@ def process_singlecmds(singlecmds):
|
||||
if singlecmds[cmd] is not None:
|
||||
tmp = tuple(int(x) for x in singlecmds[cmd].split())
|
||||
if len(tmp) != 1:
|
||||
logger.exception(f"{cmd} requires exactly one parameter to specify the number of CPU OpenMP threads to use")
|
||||
logger.exception(
|
||||
f"{cmd} requires exactly one parameter to specify the number of CPU OpenMP threads to use"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
omp_threads = OMPThreads(n=tmp[0])
|
||||
@@ -144,7 +155,12 @@ def process_singlecmds(singlecmds):
|
||||
pml_props = PMLProps(thickness=int(tmp[0]))
|
||||
else:
|
||||
pml_props = PMLProps(
|
||||
x0=int(tmp[0]), y0=int(tmp[1]), z0=int(tmp[2]), xmax=int(tmp[3]), ymax=int(tmp[4]), zmax=int(tmp[5])
|
||||
x0=int(tmp[0]),
|
||||
y0=int(tmp[1]),
|
||||
z0=int(tmp[2]),
|
||||
xmax=int(tmp[3]),
|
||||
ymax=int(tmp[4]),
|
||||
zmax=int(tmp[5]),
|
||||
)
|
||||
|
||||
scene_objects.append(pml_props)
|
||||
|
@@ -168,7 +168,9 @@ class DispersiveMaterial(Material):
|
||||
# tau for Lorentz materials are pole frequencies
|
||||
# alpha for Lorentz materials are the damping coefficients
|
||||
wp2 = (2 * np.pi * self.tau[x]) ** 2
|
||||
self.w[x] = -1j * ((wp2 * self.deltaer[x]) / np.sqrt(wp2 - self.alpha[x] ** 2))
|
||||
self.w[x] = -1j * (
|
||||
(wp2 * self.deltaer[x]) / np.sqrt(wp2 - self.alpha[x] ** 2)
|
||||
)
|
||||
self.q[x] = -self.alpha[x] + (1j * np.sqrt(wp2 - self.alpha[x] ** 2))
|
||||
elif "drude" in self.type:
|
||||
# tau for Drude materials are pole frequencies
|
||||
@@ -239,7 +241,15 @@ class PeplinskiSoil:
|
||||
by Peplinski (http://dx.doi.org/10.1109/36.387598).
|
||||
"""
|
||||
|
||||
def __init__(self, ID, sandfraction, clayfraction, bulkdensity, sandpartdensity, watervolfraction):
|
||||
def __init__(
|
||||
self,
|
||||
ID,
|
||||
sandfraction,
|
||||
clayfraction,
|
||||
bulkdensity,
|
||||
sandpartdensity,
|
||||
watervolfraction,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
ID: string for name of the soil.
|
||||
@@ -282,7 +292,9 @@ class PeplinskiSoil:
|
||||
erealw = watereri + (waterdeltaer / (1 + (w * watertau) ** 2))
|
||||
|
||||
a = 0.65 # Experimentally derived constant
|
||||
es = (1.01 + 0.44 * self.rs) ** 2 - 0.062 # Relative permittivity of sand particles
|
||||
es = (
|
||||
1.01 + 0.44 * self.rs
|
||||
) ** 2 - 0.062 # Relative permittivity of sand particles
|
||||
b1 = 1.2748 - 0.519 * self.S - 0.152 * self.C
|
||||
b2 = 1.33797 - 0.603 * self.S - 0.166 * self.C
|
||||
|
||||
@@ -304,7 +316,12 @@ class PeplinskiSoil:
|
||||
muiter = np.nditer(mumaterials, flags=["c_index"])
|
||||
while not muiter.finished:
|
||||
# Real part for frequencies in the range 1.4GHz to 18GHz
|
||||
er = (1 + (self.rb / self.rs) * ((es**a) - 1) + (muiter[0] ** b1 * erealw**a) - muiter[0]) ** (1 / a)
|
||||
er = (
|
||||
1
|
||||
+ (self.rb / self.rs) * ((es**a) - 1)
|
||||
+ (muiter[0] ** b1 * erealw**a)
|
||||
- muiter[0]
|
||||
) ** (1 / a)
|
||||
# Real part for frequencies in the range 0.3GHz to 1.3GHz (linear
|
||||
# correction to 1.4-18GHz value)
|
||||
er = 1.15 * er - 0.68
|
||||
@@ -313,7 +330,9 @@ class PeplinskiSoil:
|
||||
eri = er - (muiter[0] ** (b2 / a) * waterdeltaer)
|
||||
|
||||
# Effective conductivity
|
||||
sig = muiter[0] ** (b2 / a) * ((sigf * (self.rs - self.rb)) / (self.rs * muiter[0]))
|
||||
sig = muiter[0] ** (b2 / a) * (
|
||||
(sigf * (self.rs - self.rb)) / (self.rs * muiter[0])
|
||||
)
|
||||
|
||||
# Create individual materials
|
||||
m = DispersiveMaterial(len(G.materials), None)
|
||||
@@ -406,7 +425,9 @@ class RangeMaterial:
|
||||
sm = romaterials[iter]
|
||||
|
||||
# Check to see if the material already exists before creating a new one
|
||||
requiredID = f"|{float(er):.4f}+{float(se):.4f}+{float(mr):.4f}+{float(sm):.4f}|"
|
||||
requiredID = (
|
||||
f"|{float(er):.4f}+{float(se):.4f}+{float(mr):.4f}+{float(sm):.4f}|"
|
||||
)
|
||||
material = next((x for x in G.materials if x.ID == requiredID), None)
|
||||
if iter == 0 and material:
|
||||
self.matID.append(material.numID)
|
||||
@@ -456,7 +477,9 @@ class ListMaterial:
|
||||
self.matID.append(material.numID)
|
||||
|
||||
if not material:
|
||||
logger.exception(self.__str__() + f" material(s) {material} do not exist")
|
||||
logger.exception(
|
||||
self.__str__() + f" material(s) {material} do not exist"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
|
||||
@@ -495,11 +518,16 @@ def calculate_water_properties(T=25, S=0):
|
||||
# Properties of water from: https://doi.org/10.1109/JOE.1977.1145319
|
||||
eri = 4.9
|
||||
er = 88.045 - 0.4147 * T + 6.295e-4 * T**2 + 1.075e-5 * T**3
|
||||
tau = (1 / (2 * np.pi)) * (1.1109e-10 - 3.824e-12 * T + 6.938e-14 * T**2 - 5.096e-16 * T**3)
|
||||
tau = (1 / (2 * np.pi)) * (
|
||||
1.1109e-10 - 3.824e-12 * T + 6.938e-14 * T**2 - 5.096e-16 * T**3
|
||||
)
|
||||
|
||||
delta = 25 - T
|
||||
beta = (
|
||||
2.033e-2 + 1.266e-4 * delta + 2.464e-6 * delta**2 - S * (1.849e-5 - 2.551e-7 * delta + 2.551e-8 * delta**2)
|
||||
2.033e-2
|
||||
+ 1.266e-4 * delta
|
||||
+ 2.464e-6 * delta**2
|
||||
- S * (1.849e-5 - 2.551e-7 * delta + 2.551e-8 * delta**2)
|
||||
)
|
||||
sig_25s = S * (0.182521 - 1.46192e-3 * S + 2.09324e-5 * S**2 - 1.28205e-7 * S**3)
|
||||
sig = sig_25s * np.exp(-delta * beta)
|
||||
@@ -608,8 +636,20 @@ def process_materials(G):
|
||||
material.calculate_update_coeffsH(G)
|
||||
|
||||
# Add update coefficients to overall storage for all materials
|
||||
G.updatecoeffsE[material.numID, :] = material.CA, material.CBx, material.CBy, material.CBz, material.srce
|
||||
G.updatecoeffsH[material.numID, :] = material.DA, material.DBx, material.DBy, material.DBz, material.srcm
|
||||
G.updatecoeffsE[material.numID, :] = (
|
||||
material.CA,
|
||||
material.CBx,
|
||||
material.CBy,
|
||||
material.CBz,
|
||||
material.srce,
|
||||
)
|
||||
G.updatecoeffsH[material.numID, :] = (
|
||||
material.DA,
|
||||
material.DBx,
|
||||
material.DBy,
|
||||
material.DBz,
|
||||
material.srcm,
|
||||
)
|
||||
|
||||
# Add update coefficients to overall storage for dispersive materials
|
||||
if hasattr(material, "poles"):
|
||||
@@ -632,11 +672,15 @@ def process_materials(G):
|
||||
]
|
||||
if config.get_model_config().materials["maxpoles"] > 0:
|
||||
if "debye" in material.type:
|
||||
materialtext.append("\n".join(f"{deltaer:g}" for deltaer in material.deltaer))
|
||||
materialtext.append(
|
||||
"\n".join(f"{deltaer:g}" for deltaer in material.deltaer)
|
||||
)
|
||||
materialtext.append("\n".join(f"{tau:g}" for tau in material.tau))
|
||||
materialtext.extend(["", "", ""])
|
||||
elif "lorentz" in material.type:
|
||||
materialtext.append(", ".join(f"{deltaer:g}" for deltaer in material.deltaer))
|
||||
materialtext.append(
|
||||
", ".join(f"{deltaer:g}" for deltaer in material.deltaer)
|
||||
)
|
||||
materialtext.append("")
|
||||
materialtext.append(", ".join(f"{tau:g}" for tau in material.tau))
|
||||
materialtext.append(", ".join(f"{alpha:g}" for alpha in material.alpha))
|
||||
@@ -649,7 +693,9 @@ def process_materials(G):
|
||||
else:
|
||||
materialtext.extend(["", "", "", "", ""])
|
||||
|
||||
materialtext.extend((f"{material.mr:g}", f"{material.sm:g}", material.averagable))
|
||||
materialtext.extend(
|
||||
(f"{material.mr:g}", f"{material.sm:g}", material.averagable)
|
||||
)
|
||||
materialsdata.append(materialtext)
|
||||
|
||||
return materialsdata
|
||||
|
@@ -33,8 +33,7 @@ from tqdm import tqdm
|
||||
|
||||
import gprMax.config as config
|
||||
|
||||
from .cython.yee_cell_build import (build_electric_components,
|
||||
build_magnetic_components)
|
||||
from .cython.yee_cell_build import build_electric_components, build_magnetic_components
|
||||
from .fields_outputs import write_hdf5_outputfile
|
||||
from .geometry_outputs import save_geometry_views
|
||||
from .grid import dispersion_analysis
|
||||
@@ -43,8 +42,7 @@ from .materials import process_materials
|
||||
from .pml import CFS, build_pml, print_pml_info
|
||||
from .scene import Scene
|
||||
from .snapshots import save_snapshots
|
||||
from .utilities.host_info import (mem_check_build_all, mem_check_run_all,
|
||||
set_omp_threads)
|
||||
from .utilities.host_info import mem_check_build_all, mem_check_run_all, set_omp_threads
|
||||
from .utilities.utilities import get_terminal_width
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -62,7 +60,9 @@ class ModelBuildRun:
|
||||
# used with threaded model building methods, e.g. fractals. Can be
|
||||
# changed by the user via #num_threads command in input file or via API
|
||||
# later for use with CPU solver.
|
||||
config.get_model_config().ompthreads = set_omp_threads(config.get_model_config().ompthreads)
|
||||
config.get_model_config().ompthreads = set_omp_threads(
|
||||
config.get_model_config().ompthreads
|
||||
)
|
||||
|
||||
def build(self):
|
||||
"""Builds the Yee cells for a model."""
|
||||
@@ -75,7 +75,9 @@ class ModelBuildRun:
|
||||
# Normal model reading/building process; bypassed if geometry information to be reused
|
||||
self.reuse_geometry() if config.get_model_config().reuse_geometry else self.build_geometry()
|
||||
|
||||
logger.info(f"\nOutput directory: {config.get_model_config().output_file_path.parent.resolve()}")
|
||||
logger.info(
|
||||
f"\nOutput directory: {config.get_model_config().output_file_path.parent.resolve()}"
|
||||
)
|
||||
|
||||
# Adjust position of simple sources and receivers if required
|
||||
if G.srcsteps[0] != 0 or G.srcsteps[1] != 0 or G.srcsteps[2] != 0:
|
||||
@@ -83,13 +85,20 @@ class ModelBuildRun:
|
||||
if config.model_num == 0:
|
||||
if (
|
||||
source.xcoord + G.srcsteps[0] * config.sim_config.model_end < 0
|
||||
or source.xcoord + G.srcsteps[0] * config.sim_config.model_end > G.nx
|
||||
or source.ycoord + G.srcsteps[1] * config.sim_config.model_end < 0
|
||||
or source.ycoord + G.srcsteps[1] * config.sim_config.model_end > G.ny
|
||||
or source.zcoord + G.srcsteps[2] * config.sim_config.model_end < 0
|
||||
or source.zcoord + G.srcsteps[2] * config.sim_config.model_end > G.nz
|
||||
or source.xcoord + G.srcsteps[0] * config.sim_config.model_end
|
||||
> G.nx
|
||||
or source.ycoord + G.srcsteps[1] * config.sim_config.model_end
|
||||
< 0
|
||||
or source.ycoord + G.srcsteps[1] * config.sim_config.model_end
|
||||
> G.ny
|
||||
or source.zcoord + G.srcsteps[2] * config.sim_config.model_end
|
||||
< 0
|
||||
or source.zcoord + G.srcsteps[2] * config.sim_config.model_end
|
||||
> G.nz
|
||||
):
|
||||
logger.exception("Source(s) will be stepped to a position outside the domain.")
|
||||
logger.exception(
|
||||
"Source(s) will be stepped to a position outside the domain."
|
||||
)
|
||||
raise ValueError
|
||||
source.xcoord = source.xcoordorigin + config.model_num * G.srcsteps[0]
|
||||
source.ycoord = source.ycoordorigin + config.model_num * G.srcsteps[1]
|
||||
@@ -99,21 +108,38 @@ class ModelBuildRun:
|
||||
if config.model_num == 0:
|
||||
if (
|
||||
receiver.xcoord + G.rxsteps[0] * config.sim_config.model_end < 0
|
||||
or receiver.xcoord + G.rxsteps[0] * config.sim_config.model_end > G.nx
|
||||
or receiver.ycoord + G.rxsteps[1] * config.sim_config.model_end < 0
|
||||
or receiver.ycoord + G.rxsteps[1] * config.sim_config.model_end > G.ny
|
||||
or receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end < 0
|
||||
or receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end > G.nz
|
||||
or receiver.xcoord + G.rxsteps[0] * config.sim_config.model_end
|
||||
> G.nx
|
||||
or receiver.ycoord + G.rxsteps[1] * config.sim_config.model_end
|
||||
< 0
|
||||
or receiver.ycoord + G.rxsteps[1] * config.sim_config.model_end
|
||||
> G.ny
|
||||
or receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end
|
||||
< 0
|
||||
or receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end
|
||||
> G.nz
|
||||
):
|
||||
logger.exception("Receiver(s) will be stepped to a position outside the domain.")
|
||||
logger.exception(
|
||||
"Receiver(s) will be stepped to a position outside the domain."
|
||||
)
|
||||
raise ValueError
|
||||
receiver.xcoord = receiver.xcoordorigin + config.model_num * G.rxsteps[0]
|
||||
receiver.ycoord = receiver.ycoordorigin + config.model_num * G.rxsteps[1]
|
||||
receiver.zcoord = receiver.zcoordorigin + config.model_num * G.rxsteps[2]
|
||||
receiver.xcoord = (
|
||||
receiver.xcoordorigin + config.model_num * G.rxsteps[0]
|
||||
)
|
||||
receiver.ycoord = (
|
||||
receiver.ycoordorigin + config.model_num * G.rxsteps[1]
|
||||
)
|
||||
receiver.zcoord = (
|
||||
receiver.zcoordorigin + config.model_num * G.rxsteps[2]
|
||||
)
|
||||
|
||||
# Write files for any geometry views and geometry object outputs
|
||||
gvs = G.geometryviews + [gv for sg in G.subgrids for gv in sg.geometryviews]
|
||||
if not gvs and not G.geometryobjectswrite and config.sim_config.args.geometry_only:
|
||||
if (
|
||||
not gvs
|
||||
and not G.geometryobjectswrite
|
||||
and config.sim_config.args.geometry_only
|
||||
):
|
||||
logger.exception("\nNo geometry views or geometry objects found.")
|
||||
raise ValueError
|
||||
save_geometry_views(gvs)
|
||||
@@ -154,7 +180,11 @@ class ModelBuildRun:
|
||||
for grid in grids:
|
||||
if config.get_model_config().materials["maxpoles"] != 0:
|
||||
config.get_model_config().materials["drudelorentz"] = any(
|
||||
[m for m in grid.materials if "drude" in m.type or "lorentz" in m.type]
|
||||
[
|
||||
m
|
||||
for m in grid.materials
|
||||
if "drude" in m.type or "lorentz" in m.type
|
||||
]
|
||||
)
|
||||
|
||||
# Set data type if any dispersive materials (must be done before memory checks)
|
||||
@@ -171,13 +201,13 @@ class ModelBuildRun:
|
||||
|
||||
if total_mem_build > total_mem_run:
|
||||
logger.info(
|
||||
f'\nMemory required (estimated): {" + ".join(mem_strs_build)} + '
|
||||
f"\nMemory required (estimated): {' + '.join(mem_strs_build)} + "
|
||||
f"~{humanize.naturalsize(config.get_model_config().mem_overhead)} "
|
||||
f"overhead = {humanize.naturalsize(total_mem_build)}"
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
f'\nMemory required (estimated): {" + ".join(mem_strs_run)} + '
|
||||
f"\nMemory required (estimated): {' + '.join(mem_strs_run)} + "
|
||||
f"~{humanize.naturalsize(config.get_model_config().mem_overhead)} "
|
||||
f"overhead = {humanize.naturalsize(total_mem_run)}"
|
||||
)
|
||||
@@ -206,9 +236,13 @@ class ModelBuildRun:
|
||||
results = dispersion_analysis(gb.grid)
|
||||
if results["error"]:
|
||||
logger.warning(
|
||||
f"\nNumerical dispersion analysis [{gb.grid.name}] " f"not carried out as {results['error']}"
|
||||
f"\nNumerical dispersion analysis [{gb.grid.name}] "
|
||||
f"not carried out as {results['error']}"
|
||||
)
|
||||
elif results["N"] < config.get_model_config().numdispersion["mingridsampling"]:
|
||||
elif (
|
||||
results["N"]
|
||||
< config.get_model_config().numdispersion["mingridsampling"]
|
||||
):
|
||||
logger.exception(
|
||||
f"\nNon-physical wave propagation in [{gb.grid.name}] "
|
||||
f"detected. Material '{results['material'].ID}' "
|
||||
@@ -220,7 +254,8 @@ class ModelBuildRun:
|
||||
raise ValueError
|
||||
elif (
|
||||
results["deltavp"]
|
||||
and np.abs(results["deltavp"]) > config.get_model_config().numdispersion["maxnumericaldisp"]
|
||||
and np.abs(results["deltavp"])
|
||||
> config.get_model_config().numdispersion["maxnumericaldisp"]
|
||||
):
|
||||
logger.warning(
|
||||
f"\n[{gb.grid.name}] has potentially significant "
|
||||
@@ -248,7 +283,9 @@ class ModelBuildRun:
|
||||
f"{config.sim_config.input_file_path}"
|
||||
)
|
||||
config.get_model_config().inputfilestr = (
|
||||
Fore.GREEN + f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n" + Style.RESET_ALL
|
||||
Fore.GREEN
|
||||
+ f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n"
|
||||
+ Style.RESET_ALL
|
||||
)
|
||||
logger.basic(config.get_model_config().inputfilestr)
|
||||
for grid in [self.G] + self.G.subgrids:
|
||||
@@ -280,7 +317,9 @@ class ModelBuildRun:
|
||||
sg_rxs = [True for sg in self.G.subgrids if sg.rxs]
|
||||
sg_tls = [True for sg in self.G.subgrids if sg.transmissionlines]
|
||||
if self.G.rxs or sg_rxs or self.G.transmissionlines or sg_tls:
|
||||
write_hdf5_outputfile(config.get_model_config().output_file_path_ext, self.G)
|
||||
write_hdf5_outputfile(
|
||||
config.get_model_config().output_file_path_ext, self.G
|
||||
)
|
||||
|
||||
# Write any snapshots to file for each grid
|
||||
for grid in [self.G] + self.G.subgrids:
|
||||
@@ -301,7 +340,10 @@ class ModelBuildRun:
|
||||
f"on {config.sim_config.hostinfo['hostname']} "
|
||||
f"with OpenMP backend using {config.get_model_config().ompthreads} thread(s)"
|
||||
)
|
||||
if config.get_model_config().ompthreads > config.sim_config.hostinfo["physicalcores"]:
|
||||
if (
|
||||
config.get_model_config().ompthreads
|
||||
> config.sim_config.hostinfo["physicalcores"]
|
||||
):
|
||||
logger.warning(
|
||||
f"You have specified more threads ({config.get_model_config().ompthreads}) "
|
||||
f"than available physical CPU cores ({config.sim_config.hostinfo['physicalcores']}). "
|
||||
@@ -310,17 +352,22 @@ class ModelBuildRun:
|
||||
elif config.sim_config.general["solver"] in ["cuda", "opencl"]:
|
||||
if config.sim_config.general["solver"] == "opencl":
|
||||
solvername = "OpenCL"
|
||||
platformname = " ".join(config.get_model_config().device["dev"].platform.name.split()) + " with "
|
||||
platformname = (
|
||||
" ".join(
|
||||
config.get_model_config().device["dev"].platform.name.split()
|
||||
)
|
||||
+ " with "
|
||||
)
|
||||
devicename = (
|
||||
f'Device {config.get_model_config().device["deviceID"]}: '
|
||||
f'{" ".join(config.get_model_config().device["dev"].name.split())}'
|
||||
f"Device {config.get_model_config().device['deviceID']}: "
|
||||
f"{' '.join(config.get_model_config().device['dev'].name.split())}"
|
||||
)
|
||||
else:
|
||||
solvername = "CUDA"
|
||||
platformname = ""
|
||||
devicename = (
|
||||
f'Device {config.get_model_config().device["deviceID"]}: '
|
||||
f'{" ".join(config.get_model_config().device["dev"].name().split())}'
|
||||
f"Device {config.get_model_config().device['deviceID']}: "
|
||||
f"{' '.join(config.get_model_config().device['dev'].name().split())}"
|
||||
)
|
||||
|
||||
logger.basic(
|
||||
@@ -355,9 +402,13 @@ class ModelBuildRun:
|
||||
elif config.sim_config.general["solver"] == "opencl":
|
||||
mem_str = f" host + unknown for device"
|
||||
|
||||
logger.info(f"\nMemory used (estimated): " + f"~{humanize.naturalsize(self.p.memory_full_info().uss)}{mem_str}")
|
||||
logger.info(
|
||||
f"Time taken: " + f"{humanize.precisedelta(datetime.timedelta(seconds=solver.solvetime), format='%0.4f')}"
|
||||
f"\nMemory used (estimated): "
|
||||
+ f"~{humanize.naturalsize(self.p.memory_full_info().uss)}{mem_str}"
|
||||
)
|
||||
logger.info(
|
||||
f"Time taken: "
|
||||
+ f"{humanize.precisedelta(datetime.timedelta(seconds=solver.solvetime), format='%0.4f')}"
|
||||
)
|
||||
|
||||
|
||||
@@ -390,9 +441,13 @@ class GridBuilder:
|
||||
file=sys.stdout,
|
||||
disable=not config.sim_config.general["progressbars"],
|
||||
)
|
||||
build_electric_components(self.grid.solid, self.grid.rigidE, self.grid.ID, self.grid)
|
||||
build_electric_components(
|
||||
self.grid.solid, self.grid.rigidE, self.grid.ID, self.grid
|
||||
)
|
||||
pbar.update()
|
||||
build_magnetic_components(self.grid.solid, self.grid.rigidH, self.grid.ID, self.grid)
|
||||
build_magnetic_components(
|
||||
self.grid.solid, self.grid.rigidH, self.grid.ID, self.grid
|
||||
)
|
||||
pbar.update()
|
||||
pbar.close()
|
||||
|
||||
|
@@ -165,7 +165,9 @@ class MPIExecutor(object):
|
||||
self.busy = [False] * len(self.workers)
|
||||
|
||||
if self.is_master():
|
||||
logger.basic(f"\n({self.comm.name}) - Master: {self.master}, Workers: {self.workers}")
|
||||
logger.basic(
|
||||
f"\n({self.comm.name}) - Master: {self.master}, Workers: {self.workers}"
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
"""Context manager enter. Only the master returns an executor, all other
|
||||
@@ -222,7 +224,9 @@ class MPIExecutor(object):
|
||||
"""Joins the workers."""
|
||||
if not self.is_master():
|
||||
return
|
||||
logger.debug(f"({self.comm.name}) - Terminating. Sending sentinel to all workers.")
|
||||
logger.debug(
|
||||
f"({self.comm.name}) - Terminating. Sending sentinel to all workers."
|
||||
)
|
||||
# Send sentinel to all workers
|
||||
for worker in self.workers:
|
||||
self.comm.send(None, dest=worker, tag=Tags.EXIT)
|
||||
@@ -269,7 +273,9 @@ class MPIExecutor(object):
|
||||
for i, worker in enumerate(self.workers):
|
||||
if self.comm.Iprobe(source=worker, tag=Tags.DONE):
|
||||
job_idx, result = self.comm.recv(source=worker, tag=Tags.DONE)
|
||||
logger.debug(f"({self.comm.name}) - Received finished job {job_idx} from worker {worker:d}.")
|
||||
logger.debug(
|
||||
f"({self.comm.name}) - Received finished job {job_idx} from worker {worker:d}."
|
||||
)
|
||||
results[job_idx] = result
|
||||
self.busy[i] = False
|
||||
elif self.comm.Iprobe(source=worker, tag=Tags.READY):
|
||||
@@ -277,10 +283,16 @@ class MPIExecutor(object):
|
||||
self.comm.recv(source=worker, tag=Tags.READY)
|
||||
self.busy[i] = True
|
||||
job_idx = num_jobs - len(my_jobs)
|
||||
logger.debug(f"({self.comm.name}) - Sending job {job_idx} to worker {worker:d}.")
|
||||
self.comm.send((job_idx, my_jobs.pop(0)), dest=worker, tag=Tags.START)
|
||||
logger.debug(
|
||||
f"({self.comm.name}) - Sending job {job_idx} to worker {worker:d}."
|
||||
)
|
||||
self.comm.send(
|
||||
(job_idx, my_jobs.pop(0)), dest=worker, tag=Tags.START
|
||||
)
|
||||
elif self.comm.Iprobe(source=worker, tag=Tags.EXIT):
|
||||
logger.debug(f"({self.comm.name}) - Worker on rank {worker:d} has terminated.")
|
||||
logger.debug(
|
||||
f"({self.comm.name}) - Worker on rank {worker:d} has terminated."
|
||||
)
|
||||
self.comm.recv(source=worker, tag=Tags.EXIT)
|
||||
self.busy[i] = False
|
||||
|
||||
@@ -304,16 +316,22 @@ class MPIExecutor(object):
|
||||
|
||||
while True:
|
||||
self.comm.send(None, dest=self.master, tag=Tags.READY)
|
||||
logger.debug(f"({self.comm.name}) - Worker on rank {self.rank} waiting for job.")
|
||||
logger.debug(
|
||||
f"({self.comm.name}) - Worker on rank {self.rank} waiting for job."
|
||||
)
|
||||
|
||||
data = self.comm.recv(source=self.master, tag=MPI.ANY_TAG, status=status)
|
||||
tag = status.tag
|
||||
|
||||
if tag == Tags.START:
|
||||
job_idx, work = data
|
||||
logger.debug(f"({self.comm.name}) - Received job {job_idx} (work={work}).")
|
||||
logger.debug(
|
||||
f"({self.comm.name}) - Received job {job_idx} (work={work})."
|
||||
)
|
||||
result = self.__guarded_work(work)
|
||||
logger.debug(f"({self.comm.name}) - Finished job. Sending results to master.")
|
||||
logger.debug(
|
||||
f"({self.comm.name}) - Finished job. Sending results to master."
|
||||
)
|
||||
self.comm.send((job_idx, result), dest=self.master, tag=Tags.DONE)
|
||||
elif tag == Tags.EXIT:
|
||||
logger.debug(f"({self.comm.name}) - Received sentinel from master.")
|
||||
|
251
gprMax/pml.py
251
gprMax/pml.py
@@ -45,7 +45,15 @@ class CFSParameter:
|
||||
}
|
||||
scalingdirections = ["forward", "reverse"]
|
||||
|
||||
def __init__(self, ID=None, scaling="polynomial", scalingprofile=None, scalingdirection="forward", min=0, max=0):
|
||||
def __init__(
|
||||
self,
|
||||
ID=None,
|
||||
scaling="polynomial",
|
||||
scalingprofile=None,
|
||||
scalingdirection="forward",
|
||||
min=0,
|
||||
max=0,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
ID: string identifier for CFS parameter, can be: 'alpha', 'kappa' or
|
||||
@@ -95,7 +103,9 @@ class CFS:
|
||||
|
||||
# Calculation of the maximum value of sigma from http://dx.doi.org/10.1109/8.546249
|
||||
m = CFSParameter.scalingprofiles[self.sigma.scalingprofile]
|
||||
self.sigma.max = (0.8 * (m + 1)) / (config.sim_config.em_consts["z0"] * d * np.sqrt(er * mr))
|
||||
self.sigma.max = (0.8 * (m + 1)) / (
|
||||
config.sim_config.em_consts["z0"] * d * np.sqrt(er * mr)
|
||||
)
|
||||
|
||||
def scaling_polynomial(self, order, Evalues, Hvalues):
|
||||
"""Applies the polynomial to be used for the scaling profile for
|
||||
@@ -115,7 +125,10 @@ class CFS:
|
||||
magnetic PML update.
|
||||
"""
|
||||
|
||||
tmp = (np.linspace(0, (len(Evalues) - 1) + 0.5, num=2 * len(Evalues)) / (len(Evalues) - 1)) ** order
|
||||
tmp = (
|
||||
np.linspace(0, (len(Evalues) - 1) + 0.5, num=2 * len(Evalues))
|
||||
/ (len(Evalues) - 1)
|
||||
) ** order
|
||||
Evalues = tmp[0:-1:2]
|
||||
Hvalues = tmp[1::2]
|
||||
|
||||
@@ -138,8 +151,12 @@ class CFS:
|
||||
|
||||
# Extra cell of thickness added to allow correct scaling of electric and
|
||||
# magnetic values
|
||||
Evalues = np.zeros(thickness + 1, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
Hvalues = np.zeros(thickness + 1, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
Evalues = np.zeros(
|
||||
thickness + 1, dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
Hvalues = np.zeros(
|
||||
thickness + 1, dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
|
||||
if parameter.scalingprofile == "constant":
|
||||
Evalues += parameter.max
|
||||
@@ -237,7 +254,10 @@ class PML:
|
||||
|
||||
kappamin = sum(cfs.kappa.min for cfs in self.CFS)
|
||||
if kappamin < 1:
|
||||
logger.exception(f"Sum of kappamin value(s) for PML is {kappamin} " "and must be greater than one.")
|
||||
logger.exception(
|
||||
f"Sum of kappamin value(s) for PML is {kappamin} "
|
||||
"and must be greater than one."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
def initialise_field_arrays(self):
|
||||
@@ -245,42 +265,54 @@ class PML:
|
||||
|
||||
if self.direction[0] == "x":
|
||||
self.EPhi1 = np.zeros(
|
||||
(len(self.CFS), self.nx + 1, self.ny, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx + 1, self.ny, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.EPhi2 = np.zeros(
|
||||
(len(self.CFS), self.nx + 1, self.ny + 1, self.nz), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx + 1, self.ny + 1, self.nz),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HPhi1 = np.zeros(
|
||||
(len(self.CFS), self.nx, self.ny + 1, self.nz), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx, self.ny + 1, self.nz),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HPhi2 = np.zeros(
|
||||
(len(self.CFS), self.nx, self.ny, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx, self.ny, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
elif self.direction[0] == "y":
|
||||
self.EPhi1 = np.zeros(
|
||||
(len(self.CFS), self.nx, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx, self.ny + 1, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.EPhi2 = np.zeros(
|
||||
(len(self.CFS), self.nx + 1, self.ny + 1, self.nz), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx + 1, self.ny + 1, self.nz),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HPhi1 = np.zeros(
|
||||
(len(self.CFS), self.nx + 1, self.ny, self.nz), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx + 1, self.ny, self.nz),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HPhi2 = np.zeros(
|
||||
(len(self.CFS), self.nx, self.ny, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx, self.ny, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
elif self.direction[0] == "z":
|
||||
self.EPhi1 = np.zeros(
|
||||
(len(self.CFS), self.nx, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx, self.ny + 1, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.EPhi2 = np.zeros(
|
||||
(len(self.CFS), self.nx + 1, self.ny, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx + 1, self.ny, self.nz + 1),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HPhi1 = np.zeros(
|
||||
(len(self.CFS), self.nx + 1, self.ny, self.nz), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx + 1, self.ny, self.nz),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HPhi2 = np.zeros(
|
||||
(len(self.CFS), self.nx, self.ny + 1, self.nz), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(self.CFS), self.nx, self.ny + 1, self.nz),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
|
||||
def calculate_update_coeffs(self, er, mr):
|
||||
@@ -291,14 +323,38 @@ class PML:
|
||||
mr: float of average permeability of underlying material
|
||||
"""
|
||||
|
||||
self.ERA = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.ERB = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.ERE = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.ERF = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.HRA = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.HRB = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.HRE = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.HRF = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.ERA = np.zeros(
|
||||
(len(self.CFS), self.thickness),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.ERB = np.zeros(
|
||||
(len(self.CFS), self.thickness),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.ERE = np.zeros(
|
||||
(len(self.CFS), self.thickness),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.ERF = np.zeros(
|
||||
(len(self.CFS), self.thickness),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HRA = np.zeros(
|
||||
(len(self.CFS), self.thickness),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HRB = np.zeros(
|
||||
(len(self.CFS), self.thickness),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HRE = np.zeros(
|
||||
(len(self.CFS), self.thickness),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.HRF = np.zeros(
|
||||
(len(self.CFS), self.thickness),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
|
||||
for x, cfs in enumerate(self.CFS):
|
||||
if not cfs.sigma.max:
|
||||
@@ -310,20 +366,30 @@ class PML:
|
||||
# Define different parameters depending on PML formulation
|
||||
if self.G.pmls["formulation"] == "HORIPML":
|
||||
# HORIPML electric update coefficients
|
||||
tmp = (2 * config.sim_config.em_consts["e0"] * Ekappa) + self.G.dt * (Ealpha * Ekappa + Esigma)
|
||||
self.ERA[x, :] = (2 * config.sim_config.em_consts["e0"] + self.G.dt * Ealpha) / tmp
|
||||
tmp = (2 * config.sim_config.em_consts["e0"] * Ekappa) + self.G.dt * (
|
||||
Ealpha * Ekappa + Esigma
|
||||
)
|
||||
self.ERA[x, :] = (
|
||||
2 * config.sim_config.em_consts["e0"] + self.G.dt * Ealpha
|
||||
) / tmp
|
||||
self.ERB[x, :] = (2 * config.sim_config.em_consts["e0"] * Ekappa) / tmp
|
||||
self.ERE[x, :] = (
|
||||
(2 * config.sim_config.em_consts["e0"] * Ekappa) - self.G.dt * (Ealpha * Ekappa + Esigma)
|
||||
(2 * config.sim_config.em_consts["e0"] * Ekappa)
|
||||
- self.G.dt * (Ealpha * Ekappa + Esigma)
|
||||
) / tmp
|
||||
self.ERF[x, :] = (2 * Esigma * self.G.dt) / (Ekappa * tmp)
|
||||
|
||||
# HORIPML magnetic update coefficients
|
||||
tmp = (2 * config.sim_config.em_consts["e0"] * Hkappa) + self.G.dt * (Halpha * Hkappa + Hsigma)
|
||||
self.HRA[x, :] = (2 * config.sim_config.em_consts["e0"] + self.G.dt * Halpha) / tmp
|
||||
tmp = (2 * config.sim_config.em_consts["e0"] * Hkappa) + self.G.dt * (
|
||||
Halpha * Hkappa + Hsigma
|
||||
)
|
||||
self.HRA[x, :] = (
|
||||
2 * config.sim_config.em_consts["e0"] + self.G.dt * Halpha
|
||||
) / tmp
|
||||
self.HRB[x, :] = (2 * config.sim_config.em_consts["e0"] * Hkappa) / tmp
|
||||
self.HRE[x, :] = (
|
||||
(2 * config.sim_config.em_consts["e0"] * Hkappa) - self.G.dt * (Halpha * Hkappa + Hsigma)
|
||||
(2 * config.sim_config.em_consts["e0"] * Hkappa)
|
||||
- self.G.dt * (Halpha * Hkappa + Hsigma)
|
||||
) / tmp
|
||||
self.HRF[x, :] = (2 * Hsigma * self.G.dt) / (Hkappa * tmp)
|
||||
|
||||
@@ -332,14 +398,18 @@ class PML:
|
||||
tmp = 2 * config.sim_config.em_consts["e0"] + self.G.dt * Ealpha
|
||||
self.ERA[x, :] = Ekappa + (self.G.dt * Esigma) / tmp
|
||||
self.ERB[x, :] = (2 * config.sim_config.em_consts["e0"]) / tmp
|
||||
self.ERE[x, :] = ((2 * config.sim_config.em_consts["e0"]) - self.G.dt * Ealpha) / tmp
|
||||
self.ERE[x, :] = (
|
||||
(2 * config.sim_config.em_consts["e0"]) - self.G.dt * Ealpha
|
||||
) / tmp
|
||||
self.ERF[x, :] = (2 * Esigma * self.G.dt) / tmp
|
||||
|
||||
# MRIPML magnetic update coefficients
|
||||
tmp = 2 * config.sim_config.em_consts["e0"] + self.G.dt * Halpha
|
||||
self.HRA[x, :] = Hkappa + (self.G.dt * Hsigma) / tmp
|
||||
self.HRB[x, :] = (2 * config.sim_config.em_consts["e0"]) / tmp
|
||||
self.HRE[x, :] = ((2 * config.sim_config.em_consts["e0"]) - self.G.dt * Halpha) / tmp
|
||||
self.HRE[x, :] = (
|
||||
(2 * config.sim_config.em_consts["e0"]) - self.G.dt * Halpha
|
||||
) / tmp
|
||||
self.HRF[x, :] = (2 * Hsigma * self.G.dt) / tmp
|
||||
|
||||
def update_electric(self):
|
||||
@@ -348,7 +418,10 @@ class PML:
|
||||
"""
|
||||
|
||||
pmlmodule = "gprMax.cython.pml_updates_electric_" + self.G.pmls["formulation"]
|
||||
func = getattr(import_module(pmlmodule), "order" + str(len(self.CFS)) + "_" + self.direction)
|
||||
func = getattr(
|
||||
import_module(pmlmodule),
|
||||
"order" + str(len(self.CFS)) + "_" + self.direction,
|
||||
)
|
||||
func(
|
||||
self.xs,
|
||||
self.xf,
|
||||
@@ -380,7 +453,10 @@ class PML:
|
||||
"""
|
||||
|
||||
pmlmodule = "gprMax.cython.pml_updates_magnetic_" + self.G.pmls["formulation"]
|
||||
func = getattr(import_module(pmlmodule), "order" + str(len(self.CFS)) + "_" + self.direction)
|
||||
func = getattr(
|
||||
import_module(pmlmodule),
|
||||
"order" + str(len(self.CFS)) + "_" + self.direction,
|
||||
)
|
||||
func(
|
||||
self.xs,
|
||||
self.xf,
|
||||
@@ -439,7 +515,11 @@ class CUDAPML(PML):
|
||||
self.bpg = (
|
||||
int(
|
||||
np.ceil(
|
||||
((self.EPhi1_dev.shape[1] + 1) * (self.EPhi1_dev.shape[2] + 1) * (self.EPhi1_dev.shape[3] + 1))
|
||||
(
|
||||
(self.EPhi1_dev.shape[1] + 1)
|
||||
* (self.EPhi1_dev.shape[2] + 1)
|
||||
* (self.EPhi1_dev.shape[3] + 1)
|
||||
)
|
||||
/ self.G.tpb[0]
|
||||
)
|
||||
),
|
||||
@@ -631,7 +711,9 @@ def print_pml_info(G):
|
||||
if all(value == 0 for value in G.pmls["thickness"].values()):
|
||||
return f"\nPML boundaries [{G.name}]: switched off"
|
||||
|
||||
if all(value == G.pmls["thickness"]["x0"] for value in G.pmls["thickness"].values()):
|
||||
if all(
|
||||
value == G.pmls["thickness"]["x0"] for value in G.pmls["thickness"].values()
|
||||
):
|
||||
pmlinfo = str(G.pmls["thickness"]["x0"])
|
||||
else:
|
||||
pmlinfo = ""
|
||||
@@ -673,29 +755,104 @@ def build_pml(G, pml_ID, thickness):
|
||||
pml_type = OpenCLPML
|
||||
|
||||
if pml_ID == "x0":
|
||||
pml = pml_type(G, ID=pml_ID, direction="xminus", xs=0, xf=thickness, ys=0, yf=G.ny, zs=0, zf=G.nz)
|
||||
pml = pml_type(
|
||||
G,
|
||||
ID=pml_ID,
|
||||
direction="xminus",
|
||||
xs=0,
|
||||
xf=thickness,
|
||||
ys=0,
|
||||
yf=G.ny,
|
||||
zs=0,
|
||||
zf=G.nz,
|
||||
)
|
||||
elif pml_ID == "xmax":
|
||||
pml = pml_type(G, ID=pml_ID, direction="xplus", xs=G.nx - thickness, xf=G.nx, ys=0, yf=G.ny, zs=0, zf=G.nz)
|
||||
pml = pml_type(
|
||||
G,
|
||||
ID=pml_ID,
|
||||
direction="xplus",
|
||||
xs=G.nx - thickness,
|
||||
xf=G.nx,
|
||||
ys=0,
|
||||
yf=G.ny,
|
||||
zs=0,
|
||||
zf=G.nz,
|
||||
)
|
||||
elif pml_ID == "y0":
|
||||
pml = pml_type(G, ID=pml_ID, direction="yminus", xs=0, xf=G.nx, ys=0, yf=thickness, zs=0, zf=G.nz)
|
||||
pml = pml_type(
|
||||
G,
|
||||
ID=pml_ID,
|
||||
direction="yminus",
|
||||
xs=0,
|
||||
xf=G.nx,
|
||||
ys=0,
|
||||
yf=thickness,
|
||||
zs=0,
|
||||
zf=G.nz,
|
||||
)
|
||||
elif pml_ID == "ymax":
|
||||
pml = pml_type(G, ID=pml_ID, direction="yplus", xs=0, xf=G.nx, ys=G.ny - thickness, yf=G.ny, zs=0, zf=G.nz)
|
||||
pml = pml_type(
|
||||
G,
|
||||
ID=pml_ID,
|
||||
direction="yplus",
|
||||
xs=0,
|
||||
xf=G.nx,
|
||||
ys=G.ny - thickness,
|
||||
yf=G.ny,
|
||||
zs=0,
|
||||
zf=G.nz,
|
||||
)
|
||||
elif pml_ID == "z0":
|
||||
pml = pml_type(G, ID=pml_ID, direction="zminus", xs=0, xf=G.nx, ys=0, yf=G.ny, zs=0, zf=thickness)
|
||||
pml = pml_type(
|
||||
G,
|
||||
ID=pml_ID,
|
||||
direction="zminus",
|
||||
xs=0,
|
||||
xf=G.nx,
|
||||
ys=0,
|
||||
yf=G.ny,
|
||||
zs=0,
|
||||
zf=thickness,
|
||||
)
|
||||
elif pml_ID == "zmax":
|
||||
pml = pml_type(G, ID=pml_ID, direction="zplus", xs=0, xf=G.nx, ys=0, yf=G.ny, zs=G.nz - thickness, zf=G.nz)
|
||||
pml = pml_type(
|
||||
G,
|
||||
ID=pml_ID,
|
||||
direction="zplus",
|
||||
xs=0,
|
||||
xf=G.nx,
|
||||
ys=0,
|
||||
yf=G.ny,
|
||||
zs=G.nz - thickness,
|
||||
zf=G.nz,
|
||||
)
|
||||
|
||||
if pml_ID[0] == "x":
|
||||
averageer, averagemr = pml_average_er_mr(
|
||||
G.ny, G.nz, config.get_model_config().ompthreads, G.solid[pml.xs, :, :], ers, mrs
|
||||
G.ny,
|
||||
G.nz,
|
||||
config.get_model_config().ompthreads,
|
||||
G.solid[pml.xs, :, :],
|
||||
ers,
|
||||
mrs,
|
||||
)
|
||||
elif pml_ID[0] == "y":
|
||||
averageer, averagemr = pml_average_er_mr(
|
||||
G.nx, G.nz, config.get_model_config().ompthreads, G.solid[:, pml.ys, :], ers, mrs
|
||||
G.nx,
|
||||
G.nz,
|
||||
config.get_model_config().ompthreads,
|
||||
G.solid[:, pml.ys, :],
|
||||
ers,
|
||||
mrs,
|
||||
)
|
||||
elif pml_ID[0] == "z":
|
||||
averageer, averagemr = pml_average_er_mr(
|
||||
G.nx, G.ny, config.get_model_config().ompthreads, G.solid[:, :, pml.zs], ers, mrs
|
||||
G.nx,
|
||||
G.ny,
|
||||
config.get_model_config().ompthreads,
|
||||
G.solid[:, :, pml.zs],
|
||||
ers,
|
||||
mrs,
|
||||
)
|
||||
|
||||
pml.calculate_update_coeffs(averageer, averagemr)
|
||||
|
@@ -63,7 +63,8 @@ def htod_rx_arrays(G, queue=None):
|
||||
# Array to store field components for receivers on compute device -
|
||||
# rows are field components; columns are iterations; pages are receivers
|
||||
rxs = np.zeros(
|
||||
(len(Rx.allowableoutputs_dev), G.iterations, len(G.rxs)), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(len(Rx.allowableoutputs_dev), G.iterations, len(G.rxs)),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
|
||||
# Copy arrays to compute device
|
||||
@@ -102,4 +103,6 @@ def dtoh_rx_array(rxs_dev, rxcoords_dev, G):
|
||||
and rx.zcoord == rxcoords_dev[rxd, 2]
|
||||
):
|
||||
for output in rx.outputs.keys():
|
||||
rx.outputs[output] = rxs_dev[Rx.allowableoutputs_dev.index(output), :, rxd]
|
||||
rx.outputs[output] = rxs_dev[
|
||||
Rx.allowableoutputs_dev.index(output), :, rxd
|
||||
]
|
||||
|
@@ -24,8 +24,7 @@ from gprMax.cmds_geometry.add_surface_water import AddSurfaceWater
|
||||
from gprMax.cmds_geometry.cmds_geometry import UserObjectGeometry
|
||||
from gprMax.cmds_geometry.fractal_box import FractalBox
|
||||
from gprMax.cmds_multiuse import UserObjectMulti
|
||||
from gprMax.cmds_singleuse import (Discretisation, Domain, TimeWindow,
|
||||
UserObjectSingle)
|
||||
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
|
||||
@@ -106,7 +105,9 @@ class Scene:
|
||||
# Check for fractal boxes and modifications and pre-process them first
|
||||
proc_cmds = []
|
||||
for obj in commands:
|
||||
if isinstance(obj, (FractalBox, AddGrass, AddSurfaceRoughness, AddSurfaceWater)):
|
||||
if isinstance(
|
||||
obj, (FractalBox, AddGrass, AddSurfaceRoughness, AddSurfaceWater)
|
||||
):
|
||||
self.build_obj(obj, grid)
|
||||
if isinstance(obj, (FractalBox)):
|
||||
proc_cmds.append(obj)
|
||||
|
@@ -55,7 +55,9 @@ def save_snapshots(grid):
|
||||
leave=True,
|
||||
unit="byte",
|
||||
unit_scale=True,
|
||||
desc=f"Writing snapshot file {i + 1} " f"of {len(grid.snapshots)}, " f"{snap.filename.name}",
|
||||
desc=f"Writing snapshot file {i + 1} "
|
||||
f"of {len(grid.snapshots)}, "
|
||||
f"{snap.filename.name}",
|
||||
ncols=get_terminal_width() - 1,
|
||||
file=sys.stdout,
|
||||
disable=not config.sim_config.general["progressbars"],
|
||||
@@ -68,7 +70,14 @@ def save_snapshots(grid):
|
||||
class Snapshot:
|
||||
"""Snapshots of the electric and magnetic field values."""
|
||||
|
||||
allowableoutputs = {"Ex": None, "Ey": None, "Ez": None, "Hx": None, "Hy": None, "Hz": None}
|
||||
allowableoutputs = {
|
||||
"Ex": None,
|
||||
"Ey": None,
|
||||
"Ez": None,
|
||||
"Hx": None,
|
||||
"Hy": None,
|
||||
"Hz": None,
|
||||
}
|
||||
|
||||
# Snapshots can be output as VTK ImageData (.vti) format or
|
||||
# HDF5 format (.h5) files
|
||||
@@ -136,13 +145,16 @@ class Snapshot:
|
||||
for k, v in self.outputs.items():
|
||||
if v:
|
||||
self.snapfields[k] = np.zeros(
|
||||
(self.nx, self.ny, self.nz), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(self.nx, self.ny, self.nz),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
self.nbytes += self.snapfields[k].nbytes
|
||||
else:
|
||||
# If output is not required for snapshot just use a mimimal
|
||||
# size of array - still required to pass to Cython function
|
||||
self.snapfields[k] = np.zeros((1, 1, 1), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.snapfields[k] = np.zeros(
|
||||
(1, 1, 1), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
|
||||
def store(self, G):
|
||||
"""Store (in memory) electric and magnetic field values for snapshot.
|
||||
@@ -208,11 +220,19 @@ class Snapshot:
|
||||
G: FDTDGrid class describing a grid in a model.
|
||||
"""
|
||||
|
||||
celldata = {k: self.snapfields[k] for k in ["Ex", "Ey", "Ez", "Hx", "Hy", "Hz"] if self.outputs.get(k)}
|
||||
celldata = {
|
||||
k: self.snapfields[k]
|
||||
for k in ["Ex", "Ey", "Ez", "Hx", "Hy", "Hz"]
|
||||
if self.outputs.get(k)
|
||||
}
|
||||
|
||||
imageToVTK(
|
||||
str(self.filename.with_suffix("")),
|
||||
origin=((self.xs * self.dx * G.dx), (self.ys * self.dy * G.dy), (self.zs * self.dz * G.dz)),
|
||||
origin=(
|
||||
(self.xs * self.dx * G.dx),
|
||||
(self.ys * self.dy * G.dy),
|
||||
(self.zs * self.dz * G.dz),
|
||||
),
|
||||
spacing=((self.dx * G.dx), (self.dy * G.dy), (self.dz * G.dz)),
|
||||
cellData=celldata,
|
||||
)
|
||||
@@ -271,35 +291,52 @@ def htod_snapshot_array(G, queue=None):
|
||||
if config.sim_config.general["solver"] == "cuda":
|
||||
# Blocks per grid - according to largest requested snapshot
|
||||
Snapshot.bpg = (
|
||||
int(np.ceil(((Snapshot.nx_max) * (Snapshot.ny_max) * (Snapshot.nz_max)) / Snapshot.tpb[0])),
|
||||
int(
|
||||
np.ceil(
|
||||
((Snapshot.nx_max) * (Snapshot.ny_max) * (Snapshot.nz_max))
|
||||
/ Snapshot.tpb[0]
|
||||
)
|
||||
),
|
||||
1,
|
||||
1,
|
||||
)
|
||||
elif config.sim_config.general["solver"] == "opencl":
|
||||
# Workgroup size - according to largest requested snapshot
|
||||
Snapshot.wgs = (int(np.ceil(((Snapshot.nx_max) * (Snapshot.ny_max) * (Snapshot.nz_max)))), 1, 1)
|
||||
Snapshot.wgs = (
|
||||
int(np.ceil(((Snapshot.nx_max) * (Snapshot.ny_max) * (Snapshot.nz_max)))),
|
||||
1,
|
||||
1,
|
||||
)
|
||||
|
||||
# 4D arrays to store snapshots on GPU, e.g. snapEx(time, x, y, z);
|
||||
# if snapshots are not being stored on the GPU during the simulation then
|
||||
# they are copied back to the host after each iteration, hence numsnaps = 1
|
||||
numsnaps = 1 if config.get_model_config().device["snapsgpu2cpu"] else len(G.snapshots)
|
||||
numsnaps = (
|
||||
1 if config.get_model_config().device["snapsgpu2cpu"] else len(G.snapshots)
|
||||
)
|
||||
snapEx = np.zeros(
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
snapEy = np.zeros(
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
snapEz = np.zeros(
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
snapHx = np.zeros(
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
snapHy = np.zeros(
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
snapHz = np.zeros(
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max),
|
||||
dtype=config.sim_config.dtypes["float_or_double"],
|
||||
)
|
||||
|
||||
# Copy arrays to compute device
|
||||
@@ -326,7 +363,9 @@ def htod_snapshot_array(G, queue=None):
|
||||
return snapEx_dev, snapEy_dev, snapEz_dev, snapHx_dev, snapHy_dev, snapHz_dev
|
||||
|
||||
|
||||
def dtoh_snapshot_array(snapEx_dev, snapEy_dev, snapEz_dev, snapHx_dev, snapHy_dev, snapHz_dev, i, snap):
|
||||
def dtoh_snapshot_array(
|
||||
snapEx_dev, snapEy_dev, snapEz_dev, snapHx_dev, snapHy_dev, snapHz_dev, i, snap
|
||||
):
|
||||
"""Copies snapshot array used on compute device back to snapshot objects and
|
||||
store in format for Paraview.
|
||||
|
||||
@@ -336,9 +375,21 @@ def dtoh_snapshot_array(snapEx_dev, snapEy_dev, snapEz_dev, snapHx_dev, snapHy_d
|
||||
snap: Snapshot class instance
|
||||
"""
|
||||
|
||||
snap.snapfields["Ex"] = snapEx_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf]
|
||||
snap.snapfields["Ey"] = snapEy_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf]
|
||||
snap.snapfields["Ez"] = snapEz_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf]
|
||||
snap.snapfields["Hx"] = snapHx_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf]
|
||||
snap.snapfields["Hy"] = snapHy_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf]
|
||||
snap.snapfields["Hz"] = snapHz_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf]
|
||||
snap.snapfields["Ex"] = snapEx_dev[
|
||||
i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf
|
||||
]
|
||||
snap.snapfields["Ey"] = snapEy_dev[
|
||||
i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf
|
||||
]
|
||||
snap.snapfields["Ez"] = snapEz_dev[
|
||||
i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf
|
||||
]
|
||||
snap.snapfields["Hx"] = snapHx_dev[
|
||||
i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf
|
||||
]
|
||||
snap.snapfields["Hy"] = snapHy_dev[
|
||||
i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf
|
||||
]
|
||||
snap.snapfields["Hz"] = snapHz_dev[
|
||||
i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf
|
||||
]
|
||||
|
@@ -78,7 +78,9 @@ class VoltageSource(Source):
|
||||
|
||||
if not src_match:
|
||||
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
|
||||
self.waveformvalues_wholedt = np.zeros((G.iterations), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.waveformvalues_wholedt = np.zeros(
|
||||
(G.iterations), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
|
||||
for iteration in range(G.iterations):
|
||||
time = G.dt * iteration
|
||||
@@ -86,7 +88,9 @@ class VoltageSource(Source):
|
||||
# Set the time of the waveform evaluation to account for any
|
||||
# delay in the start
|
||||
time -= self.start
|
||||
self.waveformvalues_wholedt[iteration] = waveform.calculate_value(time, G.dt)
|
||||
self.waveformvalues_wholedt[iteration] = waveform.calculate_value(
|
||||
time, G.dt
|
||||
)
|
||||
|
||||
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
|
||||
"""Updates electric field values for a voltage source.
|
||||
@@ -158,7 +162,9 @@ class VoltageSource(Source):
|
||||
newmaterial.ID = f"{material.ID}+{self.ID}"
|
||||
newmaterial.numID = len(G.materials)
|
||||
newmaterial.averagable = False
|
||||
newmaterial.type += ",\nvoltage-source" if newmaterial.type else "voltage-source"
|
||||
newmaterial.type += (
|
||||
",\nvoltage-source" if newmaterial.type else "voltage-source"
|
||||
)
|
||||
|
||||
# Add conductivity of voltage source to underlying conductivity
|
||||
if self.polarisation == "x":
|
||||
@@ -199,7 +205,9 @@ class HertzianDipole(Source):
|
||||
|
||||
if not src_match:
|
||||
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
|
||||
self.waveformvalues_halfdt = np.zeros((G.iterations), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.waveformvalues_halfdt = np.zeros(
|
||||
(G.iterations), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
|
||||
for iteration in range(G.iterations):
|
||||
time = G.dt * iteration
|
||||
@@ -207,7 +215,9 @@ class HertzianDipole(Source):
|
||||
# Set the time of the waveform evaluation to account for any
|
||||
# delay in the start
|
||||
time -= self.start
|
||||
self.waveformvalues_halfdt[iteration] = waveform.calculate_value(time + 0.5 * G.dt, G.dt)
|
||||
self.waveformvalues_halfdt[iteration] = waveform.calculate_value(
|
||||
time + 0.5 * G.dt, G.dt
|
||||
)
|
||||
|
||||
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
|
||||
"""Updates electric field values for a Hertzian dipole.
|
||||
@@ -275,7 +285,9 @@ class MagneticDipole(Source):
|
||||
|
||||
if not src_match:
|
||||
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
|
||||
self.waveformvalues_wholedt = np.zeros((G.iterations), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.waveformvalues_wholedt = np.zeros(
|
||||
(G.iterations), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
|
||||
for iteration in range(G.iterations):
|
||||
time = G.dt * iteration
|
||||
@@ -283,7 +295,9 @@ class MagneticDipole(Source):
|
||||
# Set the time of the waveform evaluation to account for any
|
||||
# delay in the start
|
||||
time -= self.start
|
||||
self.waveformvalues_wholedt[iteration] = waveform.calculate_value(time, G.dt)
|
||||
self.waveformvalues_wholedt[iteration] = waveform.calculate_value(
|
||||
time, G.dt
|
||||
)
|
||||
|
||||
def update_magnetic(self, iteration, updatecoeffsH, ID, Hx, Hy, Hz, G):
|
||||
"""Updates magnetic field values for a magnetic dipole.
|
||||
@@ -344,8 +358,12 @@ def htod_src_arrays(sources, G, queue=None):
|
||||
"""
|
||||
|
||||
srcinfo1 = np.zeros((len(sources), 4), dtype=np.int32)
|
||||
srcinfo2 = np.zeros((len(sources)), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
srcwaves = np.zeros((len(sources), G.iterations), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
srcinfo2 = np.zeros(
|
||||
(len(sources)), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
srcwaves = np.zeros(
|
||||
(len(sources), G.iterations), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
for i, src in enumerate(sources):
|
||||
srcinfo1[i, 0] = src.xcoord
|
||||
srcinfo1[i, 1] = src.ycoord
|
||||
@@ -422,12 +440,24 @@ class TransmissionLine(Source):
|
||||
# Cell position of where line connects to antenna/main grid
|
||||
self.antpos = 10
|
||||
|
||||
self.voltage = np.zeros(self.nl, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.current = np.zeros(self.nl, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Vinc = np.zeros(G.iterations, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Iinc = np.zeros(G.iterations, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Vtotal = np.zeros(G.iterations, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.Itotal = np.zeros(G.iterations, dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.voltage = np.zeros(
|
||||
self.nl, dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
self.current = np.zeros(
|
||||
self.nl, dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
self.Vinc = np.zeros(
|
||||
G.iterations, dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
self.Iinc = np.zeros(
|
||||
G.iterations, dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
self.Vtotal = np.zeros(
|
||||
G.iterations, dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
self.Itotal = np.zeros(
|
||||
G.iterations, dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
|
||||
def calculate_waveform_values(self, G):
|
||||
"""Calculates all waveform values for source for duration of simulation.
|
||||
@@ -450,8 +480,12 @@ class TransmissionLine(Source):
|
||||
|
||||
if not src_match:
|
||||
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
|
||||
self.waveformvalues_wholedt = np.zeros((G.iterations), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.waveformvalues_halfdt = np.zeros((G.iterations), dtype=config.sim_config.dtypes["float_or_double"])
|
||||
self.waveformvalues_wholedt = np.zeros(
|
||||
(G.iterations), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
self.waveformvalues_halfdt = np.zeros(
|
||||
(G.iterations), dtype=config.sim_config.dtypes["float_or_double"]
|
||||
)
|
||||
|
||||
for iteration in range(G.iterations):
|
||||
time = G.dt * iteration
|
||||
@@ -459,8 +493,12 @@ class TransmissionLine(Source):
|
||||
# Set the time of the waveform evaluation to account for any
|
||||
# delay in the start
|
||||
time -= self.start
|
||||
self.waveformvalues_wholedt[iteration] = waveform.calculate_value(time, G.dt)
|
||||
self.waveformvalues_halfdt[iteration] = waveform.calculate_value(time + 0.5 * G.dt, G.dt)
|
||||
self.waveformvalues_wholedt[iteration] = waveform.calculate_value(
|
||||
time, G.dt
|
||||
)
|
||||
self.waveformvalues_halfdt[iteration] = waveform.calculate_value(
|
||||
time + 0.5 * G.dt, G.dt
|
||||
)
|
||||
|
||||
def calculate_incident_V_I(self, G):
|
||||
"""Calculates the incident voltage and current with a long length
|
||||
@@ -503,11 +541,15 @@ class TransmissionLine(Source):
|
||||
|
||||
# Update all the voltage values along the line
|
||||
self.voltage[1 : self.nl] -= (
|
||||
self.resistance * (config.c * G.dt / self.dl) * (self.current[1 : self.nl] - self.current[0 : self.nl - 1])
|
||||
self.resistance
|
||||
* (config.c * G.dt / self.dl)
|
||||
* (self.current[1 : self.nl] - self.current[0 : self.nl - 1])
|
||||
)
|
||||
|
||||
# Update the voltage at the position of the one-way injector excitation
|
||||
self.voltage[self.srcpos] += (config.c * G.dt / self.dl) * self.waveformvalues_halfdt[iteration]
|
||||
self.voltage[self.srcpos] += (
|
||||
config.c * G.dt / self.dl
|
||||
) * self.waveformvalues_halfdt[iteration]
|
||||
|
||||
# Update ABC before updating current
|
||||
self.update_abc(G)
|
||||
@@ -529,7 +571,9 @@ class TransmissionLine(Source):
|
||||
|
||||
# Update the current one cell before the position of the one-way injector excitation
|
||||
self.current[self.srcpos - 1] += (
|
||||
(1 / self.resistance) * (config.c * G.dt / self.dl) * self.waveformvalues_wholedt[iteration]
|
||||
(1 / self.resistance)
|
||||
* (config.c * G.dt / self.dl)
|
||||
* self.waveformvalues_wholedt[iteration]
|
||||
)
|
||||
|
||||
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
|
||||
|
@@ -233,7 +233,9 @@ class PrecursorNodesBase:
|
||||
|
||||
def interpolate_to_sub_grid(self, field, coords):
|
||||
x, z, x_sg, z_sg = coords
|
||||
interp_f = interpolate.RectBivariateSpline(x, z, field, kx=self.interpolation, ky=self.interpolation)
|
||||
interp_f = interpolate.RectBivariateSpline(
|
||||
x, z, field, kx=self.interpolation, ky=self.interpolation
|
||||
)
|
||||
f_i = interp_f(x_sg, z_sg)
|
||||
return f_i
|
||||
|
||||
@@ -417,18 +419,78 @@ class PrecursorNodes(PrecursorNodesBase):
|
||||
|
||||
# Spatially interpolate nodes
|
||||
slices = [
|
||||
["ex_front_1", True, (slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), self.Ex],
|
||||
["ex_back_1", True, (slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), self.Ex],
|
||||
["ez_front_1", False, (slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), self.Ez],
|
||||
["ez_back_1", False, (slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), self.Ez],
|
||||
["ey_left_1", True, (self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey],
|
||||
["ey_right_1", True, (self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey],
|
||||
["ez_left_1", False, (self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez],
|
||||
["ez_right_1", False, (self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez],
|
||||
["ex_bottom_1", True, (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), self.Ex],
|
||||
["ex_top_1", True, (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), self.Ex],
|
||||
["ey_bottom_1", False, (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), self.Ey],
|
||||
["ey_top_1", False, (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), self.Ey],
|
||||
[
|
||||
"ex_front_1",
|
||||
True,
|
||||
(slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)),
|
||||
self.Ex,
|
||||
],
|
||||
[
|
||||
"ex_back_1",
|
||||
True,
|
||||
(slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)),
|
||||
self.Ex,
|
||||
],
|
||||
[
|
||||
"ez_front_1",
|
||||
False,
|
||||
(slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)),
|
||||
self.Ez,
|
||||
],
|
||||
[
|
||||
"ez_back_1",
|
||||
False,
|
||||
(slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)),
|
||||
self.Ez,
|
||||
],
|
||||
[
|
||||
"ey_left_1",
|
||||
True,
|
||||
(self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
|
||||
self.Ey,
|
||||
],
|
||||
[
|
||||
"ey_right_1",
|
||||
True,
|
||||
(self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
|
||||
self.Ey,
|
||||
],
|
||||
[
|
||||
"ez_left_1",
|
||||
False,
|
||||
(self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
|
||||
self.Ez,
|
||||
],
|
||||
[
|
||||
"ez_right_1",
|
||||
False,
|
||||
(self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
|
||||
self.Ez,
|
||||
],
|
||||
[
|
||||
"ex_bottom_1",
|
||||
True,
|
||||
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0),
|
||||
self.Ex,
|
||||
],
|
||||
[
|
||||
"ex_top_1",
|
||||
True,
|
||||
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1),
|
||||
self.Ex,
|
||||
],
|
||||
[
|
||||
"ey_bottom_1",
|
||||
False,
|
||||
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0),
|
||||
self.Ey,
|
||||
],
|
||||
[
|
||||
"ey_top_1",
|
||||
False,
|
||||
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1),
|
||||
self.Ey,
|
||||
],
|
||||
]
|
||||
|
||||
for obj in slices:
|
||||
|
@@ -20,7 +20,11 @@ import logging
|
||||
|
||||
import gprMax.config as config
|
||||
|
||||
from ..cython.fields_updates_hsg import update_electric_os, update_is, update_magnetic_os
|
||||
from ..cython.fields_updates_hsg import (
|
||||
update_electric_os,
|
||||
update_is,
|
||||
update_magnetic_os,
|
||||
)
|
||||
from .grid import SubGridBaseGrid
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -673,16 +677,27 @@ class SubGridHSG(SubGridBaseGrid):
|
||||
|
||||
# Working region
|
||||
xs, ys, zs = self.round_to_grid(
|
||||
(self.i0 * self.dx * self.ratio, self.j0 * self.dy * self.ratio, self.k0 * self.dz * self.ratio)
|
||||
(
|
||||
self.i0 * self.dx * self.ratio,
|
||||
self.j0 * self.dy * self.ratio,
|
||||
self.k0 * self.dz * self.ratio,
|
||||
)
|
||||
)
|
||||
xf, yf, zf = self.round_to_grid(
|
||||
(self.i1 * self.dx * self.ratio, self.j1 * self.dy * self.ratio, self.k1 * self.dz * self.ratio)
|
||||
(
|
||||
self.i1 * self.dx * self.ratio,
|
||||
self.j1 * self.dy * self.ratio,
|
||||
self.k1 * self.dz * self.ratio,
|
||||
)
|
||||
)
|
||||
|
||||
logger.info("")
|
||||
logger.debug(f"[{self.name}] Type: {self.__class__.__name__}")
|
||||
logger.info(f"[{self.name}] Ratio: 1:{self.ratio}")
|
||||
logger.info(f"[{self.name}] Spatial discretisation: {self.dx:g} x " + f"{self.dy:g} x {self.dz:g}m")
|
||||
logger.info(
|
||||
f"[{self.name}] Spatial discretisation: {self.dx:g} x "
|
||||
+ f"{self.dy:g} x {self.dz:g}m"
|
||||
)
|
||||
logger.info(
|
||||
f"[{self.name}] Extent (working region): {xs}m, {ys}m, {zs}m to {xf}m, {yf}m, {zf}m "
|
||||
+ f"(({self.nwx} x {self.nwy} x {self.nwz} = {self.nwx * self.nwy * self.nwz} cells)"
|
||||
|
@@ -131,7 +131,9 @@ class SubGridBase(UserObjectMulti):
|
||||
# Don't mix and match different subgrid types
|
||||
for sg_made in grid.subgrids:
|
||||
if type(sg) != type(sg_made):
|
||||
logger.exception(f"{self.__str__()} please only use one type of subgrid")
|
||||
logger.exception(
|
||||
f"{self.__str__()} please only use one type of subgrid"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Reference the subgrid under the main grid to which it belongs
|
||||
|
@@ -25,12 +25,14 @@ from jinja2 import Environment, PackageLoader
|
||||
|
||||
import gprMax.config as config
|
||||
|
||||
from .cuda_opencl import (knl_fields_updates, knl_snapshots,
|
||||
knl_source_updates, knl_store_outputs)
|
||||
from .cython.fields_updates_normal import \
|
||||
update_electric as update_electric_cpu
|
||||
from .cython.fields_updates_normal import \
|
||||
update_magnetic as update_magnetic_cpu
|
||||
from .cuda_opencl import (
|
||||
knl_fields_updates,
|
||||
knl_snapshots,
|
||||
knl_source_updates,
|
||||
knl_store_outputs,
|
||||
)
|
||||
from .cython.fields_updates_normal import update_electric as update_electric_cpu
|
||||
from .cython.fields_updates_normal import update_magnetic as update_magnetic_cpu
|
||||
from .fields_outputs import store_outputs as store_outputs_cpu
|
||||
from .receivers import dtoh_rx_array, htod_rx_arrays
|
||||
from .snapshots import Snapshot, dtoh_snapshot_array, htod_snapshot_array
|
||||
@@ -151,7 +153,11 @@ class CPUUpdates:
|
||||
"""Updates electric field components from sources -
|
||||
update any Hertzian dipole sources last.
|
||||
"""
|
||||
for source in self.grid.voltagesources + self.grid.transmissionlines + self.grid.hertziandipoles:
|
||||
for source in (
|
||||
self.grid.voltagesources
|
||||
+ self.grid.transmissionlines
|
||||
+ self.grid.hertziandipoles
|
||||
):
|
||||
source.update_electric(
|
||||
self.grid.iteration,
|
||||
self.grid.updatecoeffsE,
|
||||
@@ -191,10 +197,13 @@ class CPUUpdates:
|
||||
"""Sets dispersive update functions."""
|
||||
|
||||
poles = "multi" if config.get_model_config().materials["maxpoles"] > 1 else "1"
|
||||
precision = "float" if config.sim_config.general["precision"] == "single" else "double"
|
||||
precision = (
|
||||
"float" if config.sim_config.general["precision"] == "single" else "double"
|
||||
)
|
||||
dispersion = (
|
||||
"complex"
|
||||
if config.get_model_config().materials["dispersivedtype"] == config.sim_config.dtypes["complex"]
|
||||
if config.get_model_config().materials["dispersivedtype"]
|
||||
== config.sim_config.dtypes["complex"]
|
||||
else "real"
|
||||
)
|
||||
|
||||
@@ -202,8 +211,12 @@ class CPUUpdates:
|
||||
disp_a = update_f.format(poles, "A", precision, dispersion)
|
||||
disp_b = update_f.format(poles, "B", precision, dispersion)
|
||||
|
||||
disp_a_f = getattr(import_module("gprMax.cython.fields_updates_dispersive"), disp_a)
|
||||
disp_b_f = getattr(import_module("gprMax.cython.fields_updates_dispersive"), disp_b)
|
||||
disp_a_f = getattr(
|
||||
import_module("gprMax.cython.fields_updates_dispersive"), disp_a
|
||||
)
|
||||
disp_b_f = getattr(
|
||||
import_module("gprMax.cython.fields_updates_dispersive"), disp_b
|
||||
)
|
||||
|
||||
self.dispersive_update_a = disp_a_f
|
||||
self.dispersive_update_b = disp_b_f
|
||||
@@ -271,7 +284,11 @@ class CUDAUpdates:
|
||||
self._set_pml_knls()
|
||||
if self.grid.rxs:
|
||||
self._set_rx_knl()
|
||||
if self.grid.voltagesources + self.grid.hertziandipoles + self.grid.magneticdipoles:
|
||||
if (
|
||||
self.grid.voltagesources
|
||||
+ self.grid.hertziandipoles
|
||||
+ self.grid.magneticdipoles
|
||||
):
|
||||
self._set_src_knls()
|
||||
if self.grid.snapshots:
|
||||
self._set_snapshot_knl()
|
||||
@@ -344,11 +361,15 @@ class CUDAUpdates:
|
||||
gets kernel functions.
|
||||
"""
|
||||
|
||||
bld = self._build_knl(knl_fields_updates.update_electric, self.subs_name_args, self.subs_func)
|
||||
bld = self._build_knl(
|
||||
knl_fields_updates.update_electric, self.subs_name_args, self.subs_func
|
||||
)
|
||||
knlE = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
self.update_electric_dev = knlE.get_function("update_electric")
|
||||
|
||||
bld = self._build_knl(knl_fields_updates.update_magnetic, self.subs_name_args, self.subs_func)
|
||||
bld = self._build_knl(
|
||||
knl_fields_updates.update_magnetic, self.subs_name_args, self.subs_func
|
||||
)
|
||||
knlH = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
self.update_magnetic_dev = knlH.get_function("update_magnetic")
|
||||
|
||||
@@ -367,13 +388,25 @@ class CUDAUpdates:
|
||||
}
|
||||
)
|
||||
|
||||
bld = self._build_knl(knl_fields_updates.update_electric_dispersive_A, self.subs_name_args, self.subs_func)
|
||||
knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
bld = self._build_knl(
|
||||
knl_fields_updates.update_electric_dispersive_A,
|
||||
self.subs_name_args,
|
||||
self.subs_func,
|
||||
)
|
||||
knl = self.source_module(
|
||||
bld, options=config.sim_config.devices["nvcc_opts"]
|
||||
)
|
||||
self.dispersive_update_a = knl.get_function("update_electric_dispersive_A")
|
||||
self._copy_mat_coeffs(knl, knl)
|
||||
|
||||
bld = self._build_knl(knl_fields_updates.update_electric_dispersive_B, self.subs_name_args, self.subs_func)
|
||||
knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
bld = self._build_knl(
|
||||
knl_fields_updates.update_electric_dispersive_B,
|
||||
self.subs_name_args,
|
||||
self.subs_func,
|
||||
)
|
||||
knl = self.source_module(
|
||||
bld, options=config.sim_config.devices["nvcc_opts"]
|
||||
)
|
||||
self.dispersive_update_b = knl.get_function("update_electric_dispersive_B")
|
||||
self._copy_mat_coeffs(knl, knl)
|
||||
|
||||
@@ -387,10 +420,12 @@ class CUDAUpdates:
|
||||
def _set_pml_knls(self):
|
||||
"""PMLS - prepares kernels and gets kernel functions."""
|
||||
knl_pml_updates_electric = import_module(
|
||||
"gprMax.cuda_opencl.knl_pml_updates_electric_" + self.grid.pmls["formulation"]
|
||||
"gprMax.cuda_opencl.knl_pml_updates_electric_"
|
||||
+ self.grid.pmls["formulation"]
|
||||
)
|
||||
knl_pml_updates_magnetic = import_module(
|
||||
"gprMax.cuda_opencl.knl_pml_updates_magnetic_" + self.grid.pmls["formulation"]
|
||||
"gprMax.cuda_opencl.knl_pml_updates_magnetic_"
|
||||
+ self.grid.pmls["formulation"]
|
||||
)
|
||||
|
||||
# Initialise arrays on GPU, set block per grid, and get kernel functions
|
||||
@@ -402,12 +437,16 @@ class CUDAUpdates:
|
||||
|
||||
knl_electric = getattr(knl_pml_updates_electric, knl_name)
|
||||
bld = self._build_knl(knl_electric, self.subs_name_args, self.subs_func)
|
||||
knlE = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
knlE = self.source_module(
|
||||
bld, options=config.sim_config.devices["nvcc_opts"]
|
||||
)
|
||||
pml.update_electric_dev = knlE.get_function(knl_name)
|
||||
|
||||
knl_magnetic = getattr(knl_pml_updates_magnetic, knl_name)
|
||||
bld = self._build_knl(knl_magnetic, self.subs_name_args, self.subs_func)
|
||||
knlH = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
knlH = self.source_module(
|
||||
bld, options=config.sim_config.devices["nvcc_opts"]
|
||||
)
|
||||
pml.update_magnetic_dev = knlH.get_function(knl_name)
|
||||
|
||||
# Copy material coefficient arrays to constant memory of GPU - must
|
||||
@@ -430,7 +469,9 @@ class CUDAUpdates:
|
||||
}
|
||||
)
|
||||
|
||||
bld = self._build_knl(knl_store_outputs.store_outputs, self.subs_name_args, self.subs_func)
|
||||
bld = self._build_knl(
|
||||
knl_store_outputs.store_outputs, self.subs_name_args, self.subs_func
|
||||
)
|
||||
knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
self.store_outputs_dev = knl.get_function("store_outputs")
|
||||
|
||||
@@ -441,25 +482,49 @@ class CUDAUpdates:
|
||||
self.subs_func.update({"NY_SRCINFO": 4, "NY_SRCWAVES": self.grid.iteration})
|
||||
|
||||
if self.grid.hertziandipoles:
|
||||
self.srcinfo1_hertzian_dev, self.srcinfo2_hertzian_dev, self.srcwaves_hertzian_dev = htod_src_arrays(
|
||||
self.grid.hertziandipoles, self.grid
|
||||
(
|
||||
self.srcinfo1_hertzian_dev,
|
||||
self.srcinfo2_hertzian_dev,
|
||||
self.srcwaves_hertzian_dev,
|
||||
) = htod_src_arrays(self.grid.hertziandipoles, self.grid)
|
||||
bld = self._build_knl(
|
||||
knl_source_updates.update_hertzian_dipole,
|
||||
self.subs_name_args,
|
||||
self.subs_func,
|
||||
)
|
||||
knl = self.source_module(
|
||||
bld, options=config.sim_config.devices["nvcc_opts"]
|
||||
)
|
||||
bld = self._build_knl(knl_source_updates.update_hertzian_dipole, self.subs_name_args, self.subs_func)
|
||||
knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
self.update_hertzian_dipole_dev = knl.get_function("update_hertzian_dipole")
|
||||
if self.grid.magneticdipoles:
|
||||
self.srcinfo1_magnetic_dev, self.srcinfo2_magnetic_dev, self.srcwaves_magnetic_dev = htod_src_arrays(
|
||||
self.grid.magneticdipoles, self.grid
|
||||
(
|
||||
self.srcinfo1_magnetic_dev,
|
||||
self.srcinfo2_magnetic_dev,
|
||||
self.srcwaves_magnetic_dev,
|
||||
) = htod_src_arrays(self.grid.magneticdipoles, self.grid)
|
||||
bld = self._build_knl(
|
||||
knl_source_updates.update_magnetic_dipole,
|
||||
self.subs_name_args,
|
||||
self.subs_func,
|
||||
)
|
||||
knl = self.source_module(
|
||||
bld, options=config.sim_config.devices["nvcc_opts"]
|
||||
)
|
||||
bld = self._build_knl(knl_source_updates.update_magnetic_dipole, self.subs_name_args, self.subs_func)
|
||||
knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
self.update_magnetic_dipole_dev = knl.get_function("update_magnetic_dipole")
|
||||
if self.grid.voltagesources:
|
||||
self.srcinfo1_voltage_dev, self.srcinfo2_voltage_dev, self.srcwaves_voltage_dev = htod_src_arrays(
|
||||
self.grid.voltagesources, self.grid
|
||||
(
|
||||
self.srcinfo1_voltage_dev,
|
||||
self.srcinfo2_voltage_dev,
|
||||
self.srcwaves_voltage_dev,
|
||||
) = htod_src_arrays(self.grid.voltagesources, self.grid)
|
||||
bld = self._build_knl(
|
||||
knl_source_updates.update_voltage_source,
|
||||
self.subs_name_args,
|
||||
self.subs_func,
|
||||
)
|
||||
knl = self.source_module(
|
||||
bld, options=config.sim_config.devices["nvcc_opts"]
|
||||
)
|
||||
bld = self._build_knl(knl_source_updates.update_voltage_source, self.subs_name_args, self.subs_func)
|
||||
knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
self.update_voltage_source_dev = knl.get_function("update_voltage_source")
|
||||
|
||||
self._copy_mat_coeffs(knl, knl)
|
||||
@@ -486,7 +551,9 @@ class CUDAUpdates:
|
||||
}
|
||||
)
|
||||
|
||||
bld = self._build_knl(knl_snapshots.store_snapshot, self.subs_name_args, self.subs_func)
|
||||
bld = self._build_knl(
|
||||
knl_snapshots.store_snapshot, self.subs_name_args, self.subs_func
|
||||
)
|
||||
knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"])
|
||||
self.store_snapshot_dev = knl.get_function("store_snapshot")
|
||||
|
||||
@@ -805,12 +872,16 @@ class OpenCLUpdates:
|
||||
|
||||
# Import pyopencl module
|
||||
self.cl = import_module("pyopencl")
|
||||
self.elwiseknl = getattr(import_module("pyopencl.elementwise"), "ElementwiseKernel")
|
||||
self.elwiseknl = getattr(
|
||||
import_module("pyopencl.elementwise"), "ElementwiseKernel"
|
||||
)
|
||||
|
||||
# Select device, create context and command queue
|
||||
self.dev = config.get_model_config().device["dev"]
|
||||
self.ctx = self.cl.Context(devices=[self.dev])
|
||||
self.queue = self.cl.CommandQueue(self.ctx, properties=self.cl.command_queue_properties.PROFILING_ENABLE)
|
||||
self.queue = self.cl.CommandQueue(
|
||||
self.ctx, properties=self.cl.command_queue_properties.PROFILING_ENABLE
|
||||
)
|
||||
|
||||
# Enviroment for templating kernels
|
||||
self.env = Environment(loader=PackageLoader("gprMax", "cuda_opencl"))
|
||||
@@ -822,7 +893,11 @@ class OpenCLUpdates:
|
||||
self._set_pml_knls()
|
||||
if self.grid.rxs:
|
||||
self._set_rx_knl()
|
||||
if self.grid.voltagesources + self.grid.hertziandipoles + self.grid.magneticdipoles:
|
||||
if (
|
||||
self.grid.voltagesources
|
||||
+ self.grid.hertziandipoles
|
||||
+ self.grid.magneticdipoles
|
||||
):
|
||||
self._set_src_knls()
|
||||
if self.grid.snapshots:
|
||||
self._set_snapshot_knl()
|
||||
@@ -927,13 +1002,19 @@ class OpenCLUpdates:
|
||||
|
||||
self.dispersive_update_a = self.elwiseknl(
|
||||
self.ctx,
|
||||
knl_fields_updates.update_electric_dispersive_A["args_opencl"].substitute(
|
||||
knl_fields_updates.update_electric_dispersive_A[
|
||||
"args_opencl"
|
||||
].substitute(
|
||||
{
|
||||
"REAL": config.sim_config.dtypes["C_float_or_double"],
|
||||
"COMPLEX": config.get_model_config().materials["dispersiveCdtype"],
|
||||
"COMPLEX": config.get_model_config().materials[
|
||||
"dispersiveCdtype"
|
||||
],
|
||||
}
|
||||
),
|
||||
knl_fields_updates.update_electric_dispersive_A["func"].substitute(subs),
|
||||
knl_fields_updates.update_electric_dispersive_A["func"].substitute(
|
||||
subs
|
||||
),
|
||||
"update_electric_dispersive_A",
|
||||
preamble=self.knl_common,
|
||||
options=config.sim_config.devices["compiler_opts"],
|
||||
@@ -941,13 +1022,19 @@ class OpenCLUpdates:
|
||||
|
||||
self.dispersive_update_b = self.elwiseknl(
|
||||
self.ctx,
|
||||
knl_fields_updates.update_electric_dispersive_B["args_opencl"].substitute(
|
||||
knl_fields_updates.update_electric_dispersive_B[
|
||||
"args_opencl"
|
||||
].substitute(
|
||||
{
|
||||
"REAL": config.sim_config.dtypes["C_float_or_double"],
|
||||
"COMPLEX": config.get_model_config().materials["dispersiveCdtype"],
|
||||
"COMPLEX": config.get_model_config().materials[
|
||||
"dispersiveCdtype"
|
||||
],
|
||||
}
|
||||
),
|
||||
knl_fields_updates.update_electric_dispersive_B["func"].substitute(subs),
|
||||
knl_fields_updates.update_electric_dispersive_B["func"].substitute(
|
||||
subs
|
||||
),
|
||||
"update_electric_dispersive_B",
|
||||
preamble=self.knl_common,
|
||||
options=config.sim_config.devices["compiler_opts"],
|
||||
@@ -962,10 +1049,12 @@ class OpenCLUpdates:
|
||||
def _set_pml_knls(self):
|
||||
"""PMLS - prepares kernels and gets kernel functions."""
|
||||
knl_pml_updates_electric = import_module(
|
||||
"gprMax.cuda_opencl.knl_pml_updates_electric_" + self.grid.pmls["formulation"]
|
||||
"gprMax.cuda_opencl.knl_pml_updates_electric_"
|
||||
+ self.grid.pmls["formulation"]
|
||||
)
|
||||
knl_pml_updates_magnetic = import_module(
|
||||
"gprMax.cuda_opencl.knl_pml_updates_magnetic_" + self.grid.pmls["formulation"]
|
||||
"gprMax.cuda_opencl.knl_pml_updates_magnetic_"
|
||||
+ self.grid.pmls["formulation"]
|
||||
)
|
||||
|
||||
subs = {
|
||||
@@ -990,7 +1079,9 @@ class OpenCLUpdates:
|
||||
|
||||
pml.update_electric_dev = self.elwiseknl(
|
||||
self.ctx,
|
||||
knl_electric_name["args_opencl"].substitute({"REAL": config.sim_config.dtypes["C_float_or_double"]}),
|
||||
knl_electric_name["args_opencl"].substitute(
|
||||
{"REAL": config.sim_config.dtypes["C_float_or_double"]}
|
||||
),
|
||||
knl_electric_name["func"].substitute(subs),
|
||||
f"pml_updates_electric_{knl_name}",
|
||||
preamble=self.knl_common,
|
||||
@@ -999,7 +1090,9 @@ class OpenCLUpdates:
|
||||
|
||||
pml.update_magnetic_dev = self.elwiseknl(
|
||||
self.ctx,
|
||||
knl_magnetic_name["args_opencl"].substitute({"REAL": config.sim_config.dtypes["C_float_or_double"]}),
|
||||
knl_magnetic_name["args_opencl"].substitute(
|
||||
{"REAL": config.sim_config.dtypes["C_float_or_double"]}
|
||||
),
|
||||
knl_magnetic_name["func"].substitute(subs),
|
||||
f"pml_updates_magnetic_{knl_name}",
|
||||
preamble=self.knl_common,
|
||||
@@ -1027,48 +1120,63 @@ class OpenCLUpdates:
|
||||
gets kernel function.
|
||||
"""
|
||||
if self.grid.hertziandipoles:
|
||||
self.srcinfo1_hertzian_dev, self.srcinfo2_hertzian_dev, self.srcwaves_hertzian_dev = htod_src_arrays(
|
||||
self.grid.hertziandipoles, self.grid, self.queue
|
||||
)
|
||||
(
|
||||
self.srcinfo1_hertzian_dev,
|
||||
self.srcinfo2_hertzian_dev,
|
||||
self.srcwaves_hertzian_dev,
|
||||
) = htod_src_arrays(self.grid.hertziandipoles, self.grid, self.queue)
|
||||
self.update_hertzian_dipole_dev = self.elwiseknl(
|
||||
self.ctx,
|
||||
knl_source_updates.update_hertzian_dipole["args_opencl"].substitute(
|
||||
{"REAL": config.sim_config.dtypes["C_float_or_double"]}
|
||||
),
|
||||
knl_source_updates.update_hertzian_dipole["func"].substitute(
|
||||
{"CUDA_IDX": "", "REAL": config.sim_config.dtypes["C_float_or_double"]}
|
||||
{
|
||||
"CUDA_IDX": "",
|
||||
"REAL": config.sim_config.dtypes["C_float_or_double"],
|
||||
}
|
||||
),
|
||||
"update_hertzian_dipole",
|
||||
preamble=self.knl_common,
|
||||
options=config.sim_config.devices["compiler_opts"],
|
||||
)
|
||||
if self.grid.magneticdipoles:
|
||||
self.srcinfo1_magnetic_dev, self.srcinfo2_magnetic_dev, self.srcwaves_magnetic_dev = htod_src_arrays(
|
||||
self.grid.magneticdipoles, self.grid, self.queue
|
||||
)
|
||||
(
|
||||
self.srcinfo1_magnetic_dev,
|
||||
self.srcinfo2_magnetic_dev,
|
||||
self.srcwaves_magnetic_dev,
|
||||
) = htod_src_arrays(self.grid.magneticdipoles, self.grid, self.queue)
|
||||
self.update_magnetic_dipole_dev = self.elwiseknl(
|
||||
self.ctx,
|
||||
knl_source_updates.update_magnetic_dipole["args_opencl"].substitute(
|
||||
{"REAL": config.sim_config.dtypes["C_float_or_double"]}
|
||||
),
|
||||
knl_source_updates.update_magnetic_dipole["func"].substitute(
|
||||
{"CUDA_IDX": "", "REAL": config.sim_config.dtypes["C_float_or_double"]}
|
||||
{
|
||||
"CUDA_IDX": "",
|
||||
"REAL": config.sim_config.dtypes["C_float_or_double"],
|
||||
}
|
||||
),
|
||||
"update_magnetic_dipole",
|
||||
preamble=self.knl_common,
|
||||
options=config.sim_config.devices["compiler_opts"],
|
||||
)
|
||||
if self.grid.voltagesources:
|
||||
self.srcinfo1_voltage_dev, self.srcinfo2_voltage_dev, self.srcwaves_voltage_dev = htod_src_arrays(
|
||||
self.grid.voltagesources, self.grid, self.queue
|
||||
)
|
||||
(
|
||||
self.srcinfo1_voltage_dev,
|
||||
self.srcinfo2_voltage_dev,
|
||||
self.srcwaves_voltage_dev,
|
||||
) = htod_src_arrays(self.grid.voltagesources, self.grid, self.queue)
|
||||
self.update_voltage_source_dev = self.elwiseknl(
|
||||
self.ctx,
|
||||
knl_source_updates.update_voltage_source["args_opencl"].substitute(
|
||||
{"REAL": config.sim_config.dtypes["C_float_or_double"]}
|
||||
),
|
||||
knl_source_updates.update_voltage_source["func"].substitute(
|
||||
{"CUDA_IDX": "", "REAL": config.sim_config.dtypes["C_float_or_double"]}
|
||||
{
|
||||
"CUDA_IDX": "",
|
||||
"REAL": config.sim_config.dtypes["C_float_or_double"],
|
||||
}
|
||||
),
|
||||
"update_voltage_source",
|
||||
preamble=self.knl_common,
|
||||
@@ -1093,7 +1201,12 @@ class OpenCLUpdates:
|
||||
{"REAL": config.sim_config.dtypes["C_float_or_double"]}
|
||||
),
|
||||
knl_snapshots.store_snapshot["func"].substitute(
|
||||
{"CUDA_IDX": "", "NX_SNAPS": Snapshot.nx_max, "NY_SNAPS": Snapshot.ny_max, "NZ_SNAPS": Snapshot.nz_max}
|
||||
{
|
||||
"CUDA_IDX": "",
|
||||
"NX_SNAPS": Snapshot.nx_max,
|
||||
"NY_SNAPS": Snapshot.ny_max,
|
||||
"NZ_SNAPS": Snapshot.nz_max,
|
||||
}
|
||||
),
|
||||
"store_snapshot",
|
||||
preamble=self.knl_common,
|
||||
|
@@ -108,7 +108,9 @@ class MainGridUserInput(UserInput):
|
||||
p = self.check_point(p, cmd_str, name)
|
||||
|
||||
if self.grid.within_pml(p):
|
||||
logger.warning(f"'{cmd_str}' sources and receivers should not normally be positioned within the PML.")
|
||||
logger.warning(
|
||||
f"'{cmd_str}' sources and receivers should not normally be positioned within the PML."
|
||||
)
|
||||
|
||||
return p
|
||||
|
||||
@@ -117,7 +119,9 @@ class MainGridUserInput(UserInput):
|
||||
p2 = self.check_point(p2, cmd_str, name="upper")
|
||||
|
||||
if np.greater(p1, p2).any():
|
||||
logger.exception(f"'{cmd_str}' the lower coordinates should be less than the upper coordinates.")
|
||||
logger.exception(
|
||||
f"'{cmd_str}' the lower coordinates should be less than the upper coordinates."
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
return p1, p2
|
||||
@@ -152,7 +156,9 @@ class SubgridUserInput(MainGridUserInput):
|
||||
super().__init__(grid)
|
||||
|
||||
# Defines the region exposed to the user
|
||||
self.inner_bound = np.array([grid.n_boundary_cells_x, grid.n_boundary_cells_y, grid.n_boundary_cells_z])
|
||||
self.inner_bound = np.array(
|
||||
[grid.n_boundary_cells_x, grid.n_boundary_cells_y, grid.n_boundary_cells_z]
|
||||
)
|
||||
|
||||
self.outer_bound = np.subtract([grid.nx, grid.ny, grid.nz], self.inner_bound)
|
||||
|
||||
@@ -185,8 +191,13 @@ class SubgridUserInput(MainGridUserInput):
|
||||
|
||||
# Provide user within a warning if they have placed objects within
|
||||
# the OS non-working region.
|
||||
if np.less(p_t, self.inner_bound).any() or np.greater(p_t, self.outer_bound).any():
|
||||
logger.warning(f"'{cmd_str}' this object traverses the Outer Surface. This is an advanced feature.")
|
||||
if (
|
||||
np.less(p_t, self.inner_bound).any()
|
||||
or np.greater(p_t, self.outer_bound).any()
|
||||
):
|
||||
logger.warning(
|
||||
f"'{cmd_str}' this object traverses the Outer Surface. This is an advanced feature."
|
||||
)
|
||||
return p_t
|
||||
|
||||
def discretise_static_point(self, p):
|
||||
|
@@ -66,7 +66,13 @@ class Waveform:
|
||||
waveforms.
|
||||
"""
|
||||
|
||||
if self.type in ["gaussian", "gaussiandot", "gaussiandotnorm", "gaussianprime", "gaussiandoubleprime"]:
|
||||
if self.type in [
|
||||
"gaussian",
|
||||
"gaussiandot",
|
||||
"gaussiandotnorm",
|
||||
"gaussianprime",
|
||||
"gaussiandoubleprime",
|
||||
]:
|
||||
self.chi = 1 / self.freq
|
||||
self.zeta = 2 * np.pi**2 * self.freq**2
|
||||
elif self.type in ["gaussiandotdot", "gaussiandotdotnorm", "ricker"]:
|
||||
@@ -98,22 +104,41 @@ class Waveform:
|
||||
elif self.type == "gaussiandotnorm":
|
||||
delay = time - self.chi
|
||||
normalise = np.sqrt(np.exp(1) / (2 * self.zeta))
|
||||
ampvalue = -2 * self.zeta * delay * np.exp(-self.zeta * delay**2) * normalise
|
||||
ampvalue = (
|
||||
-2 * self.zeta * delay * np.exp(-self.zeta * delay**2) * normalise
|
||||
)
|
||||
|
||||
elif self.type in ["gaussiandotdot", "gaussiandoubleprime"]:
|
||||
delay = time - self.chi
|
||||
ampvalue = 2 * self.zeta * (2 * self.zeta * delay**2 - 1) * np.exp(-self.zeta * delay**2)
|
||||
ampvalue = (
|
||||
2
|
||||
* self.zeta
|
||||
* (2 * self.zeta * delay**2 - 1)
|
||||
* np.exp(-self.zeta * delay**2)
|
||||
)
|
||||
|
||||
elif self.type == "gaussiandotdotnorm":
|
||||
delay = time - self.chi
|
||||
normalise = 1 / (2 * self.zeta)
|
||||
ampvalue = 2 * self.zeta * (2 * self.zeta * delay**2 - 1) * np.exp(-self.zeta * delay**2) * normalise
|
||||
ampvalue = (
|
||||
2
|
||||
* self.zeta
|
||||
* (2 * self.zeta * delay**2 - 1)
|
||||
* np.exp(-self.zeta * delay**2)
|
||||
* normalise
|
||||
)
|
||||
|
||||
elif self.type == "ricker":
|
||||
delay = time - self.chi
|
||||
normalise = 1 / (2 * self.zeta)
|
||||
ampvalue = -(
|
||||
(2 * self.zeta * (2 * self.zeta * delay**2 - 1) * np.exp(-self.zeta * delay**2)) * normalise
|
||||
(
|
||||
2
|
||||
* self.zeta
|
||||
* (2 * self.zeta * delay**2 - 1)
|
||||
* np.exp(-self.zeta * delay**2)
|
||||
)
|
||||
* normalise
|
||||
)
|
||||
|
||||
elif self.type == "sine":
|
||||
|
41
setup.py
41
setup.py
@@ -33,11 +33,15 @@ from setuptools import Extension, find_packages, setup
|
||||
# Check Python version
|
||||
MIN_PYTHON_VERSION = (3, 7)
|
||||
if sys.version_info[:2] < MIN_PYTHON_VERSION:
|
||||
sys.exit("\nExited: Requires Python {MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]} or newer!\n")
|
||||
sys.exit(
|
||||
"\nExited: Requires Python {MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]} or newer!\n"
|
||||
)
|
||||
|
||||
# Importing gprMax _version__.py before building can cause issues.
|
||||
with open("gprMax/_version.py", "r") as fd:
|
||||
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE)[1]
|
||||
version = re.search(
|
||||
r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE
|
||||
)[1]
|
||||
|
||||
|
||||
def build_dispersive_material_templates():
|
||||
@@ -108,7 +112,9 @@ def build_dispersive_material_templates():
|
||||
]
|
||||
)
|
||||
|
||||
with open(os.path.join("gprMax", "cython", "fields_updates_dispersive.pyx"), "w") as f:
|
||||
with open(
|
||||
os.path.join("gprMax", "cython", "fields_updates_dispersive.pyx"), "w"
|
||||
) as f:
|
||||
f.write(r)
|
||||
|
||||
|
||||
@@ -144,9 +150,9 @@ if "cleanall" in sys.argv:
|
||||
except OSError:
|
||||
print(f"Could not remove: {filebase}.c")
|
||||
# Remove compiled Cython modules
|
||||
libfile = glob.glob(os.path.join(os.getcwd(), os.path.splitext(file)[0]) + "*.pyd") + glob.glob(
|
||||
os.path.join(os.getcwd(), os.path.splitext(file)[0]) + "*.so"
|
||||
)
|
||||
libfile = glob.glob(
|
||||
os.path.join(os.getcwd(), os.path.splitext(file)[0]) + "*.pyd"
|
||||
) + glob.glob(os.path.join(os.getcwd(), os.path.splitext(file)[0]) + "*.so")
|
||||
if libfile:
|
||||
libfile = libfile[0]
|
||||
try:
|
||||
@@ -182,7 +188,11 @@ else:
|
||||
elif sys.platform == "darwin":
|
||||
# Check for Intel or Apple M series CPU
|
||||
cpuID = (
|
||||
subprocess.check_output("sysctl -n machdep.cpu.brand_string", shell=True, stderr=subprocess.STDOUT)
|
||||
subprocess.check_output(
|
||||
"sysctl -n machdep.cpu.brand_string",
|
||||
shell=True,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
.decode("utf-8")
|
||||
.strip()
|
||||
)
|
||||
@@ -196,7 +206,11 @@ else:
|
||||
# Use newest gcc found
|
||||
os.environ["CC"] = gccpath[-1].split(os.sep)[-1]
|
||||
if "Apple" in cpuID:
|
||||
rpath = "/opt/homebrew/opt/gcc/lib/gcc/" + gccpath[-1].split(os.sep)[-1][-1] + "/"
|
||||
rpath = (
|
||||
"/opt/homebrew/opt/gcc/lib/gcc/"
|
||||
+ gccpath[-1].split(os.sep)[-1][-1]
|
||||
+ "/"
|
||||
)
|
||||
else:
|
||||
raise (
|
||||
f"Cannot find gcc in {gccbasepath}. gprMax requires gcc "
|
||||
@@ -214,7 +228,13 @@ else:
|
||||
pass
|
||||
os.environ["MIN_SUPPORTED_MACOSX_DEPLOYMENT_TARGET"] = MIN_MACOS_VERSION
|
||||
# Sometimes worth testing with '-fstrict-aliasing', '-fno-common'
|
||||
compile_args = ["-O3", "-w", "-fopenmp", "-march=native", f"-mmacosx-version-min={MIN_MACOS_VERSION}"]
|
||||
compile_args = [
|
||||
"-O3",
|
||||
"-w",
|
||||
"-fopenmp",
|
||||
"-march=native",
|
||||
f"-mmacosx-version-min={MIN_MACOS_VERSION}",
|
||||
]
|
||||
linker_args = ["-fopenmp", f"-mmacosx-version-min={MIN_MACOS_VERSION}"]
|
||||
libraries = ["gomp"]
|
||||
|
||||
@@ -261,7 +281,8 @@ else:
|
||||
version=version,
|
||||
author="Craig Warren, Antonis Giannopoulos, and John Hartley",
|
||||
url="http://www.gprmax.com",
|
||||
description="Electromagnetic Modelling Software based on the " + "Finite-Difference Time-Domain (FDTD) method",
|
||||
description="Electromagnetic Modelling Software based on the "
|
||||
+ "Finite-Difference Time-Domain (FDTD) method",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/x-rst",
|
||||
license="GPLv3+",
|
||||
|
@@ -157,14 +157,18 @@ def hertzian_dipole_fs(iterations, dt, dxdydz, rx):
|
||||
)
|
||||
|
||||
# Hx
|
||||
fields[timestep, 3] = -(Hx_y / (4 * np.pi * Hr_x**3)) * (f_Hx + (tau_Hx * fdot_Hx))
|
||||
fields[timestep, 3] = -(Hx_y / (4 * np.pi * Hr_x**3)) * (
|
||||
f_Hx + (tau_Hx * fdot_Hx)
|
||||
)
|
||||
|
||||
# Hy
|
||||
try:
|
||||
tmp = Hy_x / Hy_y
|
||||
except ZeroDivisionError:
|
||||
tmp = 0
|
||||
fields[timestep, 4] = -tmp * (-(Hy_y / (4 * np.pi * Hr_y**3)) * (f_Hy + (tau_Hy * fdot_Hy)))
|
||||
fields[timestep, 4] = -tmp * (
|
||||
-(Hy_y / (4 * np.pi * Hr_y**3)) * (f_Hy + (tau_Hy * fdot_Hy))
|
||||
)
|
||||
|
||||
# Hz
|
||||
fields[timestep, 5] = 0
|
||||
|
@@ -3,7 +3,6 @@
|
||||
receiver at the centre.
|
||||
"""
|
||||
|
||||
|
||||
import itertools
|
||||
from pathlib import Path
|
||||
|
||||
@@ -36,7 +35,9 @@ for d, threads in itertools.product(domains, ompthreads):
|
||||
dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
|
||||
time_window = gprMax.TimeWindow(time=3e-9)
|
||||
wv = gprMax.Waveform(wave_type="gaussiandotnorm", amp=1, freq=900e6, id="MySource")
|
||||
src = gprMax.HertzianDipole(p1=(x / 2, y / 2, z / 2), polarisation="x", waveform_id="MySource")
|
||||
src = gprMax.HertzianDipole(
|
||||
p1=(x / 2, y / 2, z / 2), polarisation="x", waveform_id="MySource"
|
||||
)
|
||||
|
||||
omp = gprMax.OMPThreads(n=threads)
|
||||
|
||||
|
@@ -60,9 +60,13 @@ def diff_output_files(filename1, filename2):
|
||||
|
||||
# Arrays for storing time
|
||||
time1 = np.zeros((file1.attrs["Iterations"]), dtype=floattype1)
|
||||
time1 = np.linspace(0, (file1.attrs["Iterations"] - 1), num=file1.attrs["Iterations"])
|
||||
time1 = np.linspace(
|
||||
0, (file1.attrs["Iterations"] - 1), num=file1.attrs["Iterations"]
|
||||
)
|
||||
time2 = np.zeros((file2.attrs["Iterations"]), dtype=floattype2)
|
||||
time2 = np.linspace(0, (file2.attrs["Iterations"] - 1), num=file2.attrs["Iterations"])
|
||||
time2 = np.linspace(
|
||||
0, (file2.attrs["Iterations"] - 1), num=file2.attrs["Iterations"]
|
||||
)
|
||||
|
||||
# Arrays for storing field data
|
||||
data1 = np.zeros((file1.attrs["Iterations"], len(outputs1)), dtype=floattype1)
|
||||
@@ -82,7 +86,10 @@ def diff_output_files(filename1, filename2):
|
||||
for i in range(len(outputs2)):
|
||||
maxi = np.amax(np.abs(data1[:, i]))
|
||||
datadiffs[:, i] = np.divide(
|
||||
np.abs(data2[:, i] - data1[:, i]), maxi, out=np.zeros_like(data1[:, i]), where=maxi != 0
|
||||
np.abs(data2[:, i] - data1[:, i]),
|
||||
maxi,
|
||||
out=np.zeros_like(data1[:, i]),
|
||||
where=maxi != 0,
|
||||
) # Replace any division by zero with zero
|
||||
|
||||
# Calculate power (ignore warning from taking a log of any zero values)
|
||||
|
@@ -46,12 +46,16 @@ maxerrors = []
|
||||
testmodels = [basename + "_" + s for s in PMLIDs]
|
||||
|
||||
fig, ax = plt.subplots(
|
||||
subplot_kw=dict(xlabel="Iterations", ylabel="Error [dB]"), figsize=(20, 10), facecolor="w", edgecolor="w"
|
||||
subplot_kw=dict(xlabel="Iterations", ylabel="Error [dB]"),
|
||||
figsize=(20, 10),
|
||||
facecolor="w",
|
||||
edgecolor="w",
|
||||
)
|
||||
|
||||
for x, model in enumerate(testmodels):
|
||||
time, datadiffs = diff_output_files(
|
||||
fn.parent.joinpath(basename + "_ref.h5"), fn.parent.joinpath(basename + str(x + 1) + ".h5")
|
||||
fn.parent.joinpath(basename + "_ref.h5"),
|
||||
fn.parent.joinpath(basename + str(x + 1) + ".h5"),
|
||||
)
|
||||
|
||||
# Print maximum error value
|
||||
@@ -60,7 +64,14 @@ for x, model in enumerate(testmodels):
|
||||
logger.info(f"{model}: Max. error {maxerrors[x]}")
|
||||
|
||||
# Plot diffs (select column to choose field component, 0-Ex, 1-Ey etc..)
|
||||
ax.plot(time[start::], datadiffs[start::, 1], color=next(colors), lw=2, ls=next(lines), label=model)
|
||||
ax.plot(
|
||||
time[start::],
|
||||
datadiffs[start::, 1],
|
||||
color=next(colors),
|
||||
lw=2,
|
||||
ls=next(lines),
|
||||
label=model,
|
||||
)
|
||||
ax.set_xticks(np.arange(0, 2200, step=100))
|
||||
ax.set_xlim([0, 2100])
|
||||
ax.set_yticks(np.arange(-160, 0, step=20))
|
||||
|
@@ -21,11 +21,17 @@ dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
|
||||
time_window = gprMax.TimeWindow(iterations=2100)
|
||||
tssf = gprMax.TimeStepStabilityFactor(f=0.99)
|
||||
|
||||
waveform = gprMax.Waveform(wave_type="gaussiandotnorm", amp=1, freq=9.42e9, id="mypulse")
|
||||
hertzian_dipole = gprMax.HertzianDipole(polarisation="z", p1=(0.013, 0.013, 0.014), waveform_id="mypulse")
|
||||
waveform = gprMax.Waveform(
|
||||
wave_type="gaussiandotnorm", amp=1, freq=9.42e9, id="mypulse"
|
||||
)
|
||||
hertzian_dipole = gprMax.HertzianDipole(
|
||||
polarisation="z", p1=(0.013, 0.013, 0.014), waveform_id="mypulse"
|
||||
)
|
||||
rx = gprMax.Rx(p1=(0.038, 0.114, 0.013))
|
||||
|
||||
plate = gprMax.Plate(p1=(0.013, 0.013, 0.013), p2=(0.038, 0.113, 0.013), material_id="pec")
|
||||
plate = gprMax.Plate(
|
||||
p1=(0.013, 0.013, 0.013), p2=(0.038, 0.113, 0.013), material_id="pec"
|
||||
)
|
||||
|
||||
gv1 = gprMax.GeometryView(
|
||||
p1=(0, 0, 0),
|
||||
|
@@ -21,11 +21,17 @@ dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
|
||||
time_window = gprMax.TimeWindow(iterations=2100)
|
||||
tssf = gprMax.TimeStepStabilityFactor(f=0.99)
|
||||
|
||||
waveform = gprMax.Waveform(wave_type="gaussiandotnorm", amp=1, freq=9.42e9, id="mypulse")
|
||||
hertzian_dipole = gprMax.HertzianDipole(polarisation="z", p1=(0.088, 0.088, 0.089), waveform_id="mypulse")
|
||||
waveform = gprMax.Waveform(
|
||||
wave_type="gaussiandotnorm", amp=1, freq=9.42e9, id="mypulse"
|
||||
)
|
||||
hertzian_dipole = gprMax.HertzianDipole(
|
||||
polarisation="z", p1=(0.088, 0.088, 0.089), waveform_id="mypulse"
|
||||
)
|
||||
rx = gprMax.Rx(p1=(0.113, 0.189, 0.088))
|
||||
|
||||
plate = gprMax.Plate(p1=(0.088, 0.088, 0.088), p2=(0.113, 0.188, 0.088), material_id="pec")
|
||||
plate = gprMax.Plate(
|
||||
p1=(0.088, 0.088, 0.088), p2=(0.113, 0.188, 0.088), material_id="pec"
|
||||
)
|
||||
|
||||
gv1 = gprMax.GeometryView(
|
||||
p1=(0, 0, 0),
|
||||
|
@@ -74,7 +74,13 @@ for x, PMLID in enumerate(PMLIDs):
|
||||
ax.set_ylabel(f"{output} error [dB]")
|
||||
|
||||
# Save a PDF/PNG of the figure
|
||||
fig.savefig(basename + "_diffs_" + PMLID + ".pdf", dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1)
|
||||
fig.savefig(
|
||||
basename + "_diffs_" + PMLID + ".pdf",
|
||||
dpi=None,
|
||||
format="pdf",
|
||||
bbox_inches="tight",
|
||||
pad_inches=0.1,
|
||||
)
|
||||
# fig.savefig(basename + "_diffs_" + PMLID + ".png", dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
|
||||
plt.show()
|
||||
|
@@ -18,7 +18,9 @@ dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
|
||||
time_window = gprMax.TimeWindow(time=3e-9)
|
||||
|
||||
waveform = gprMax.Waveform(wave_type="gaussian", amp=1, freq=1e9, id="mypulse")
|
||||
hertzian_dipole = gprMax.HertzianDipole(polarisation="z", p1=(0.050, 0.050, 0.050), waveform_id="mypulse")
|
||||
hertzian_dipole = gprMax.HertzianDipole(
|
||||
polarisation="z", p1=(0.050, 0.050, 0.050), waveform_id="mypulse"
|
||||
)
|
||||
rx = gprMax.Rx(p1=(0.070, 0.070, 0.070))
|
||||
|
||||
# PML cases
|
||||
|
@@ -31,7 +31,10 @@ tx_pos = (x / 2, y / 2, z / 2)
|
||||
# Source excitation and type
|
||||
wave = gprMax.Waveform(wave_type="gaussian", amp=1, freq=1.5e9, id="mypulse")
|
||||
tl = gprMax.TransmissionLine(
|
||||
p1=(tx_pos[0], tx_pos[1], tx_pos[2]), polarisation="x", resistance=50, waveform_id="mypulse"
|
||||
p1=(tx_pos[0], tx_pos[1], tx_pos[2]),
|
||||
polarisation="x",
|
||||
resistance=50,
|
||||
waveform_id="mypulse",
|
||||
)
|
||||
scene.add(wave)
|
||||
scene.add(tl)
|
||||
@@ -59,8 +62,16 @@ scene.add(t2)
|
||||
|
||||
# Detailed geometry view around bowtie and feed position
|
||||
gv1 = gprMax.GeometryView(
|
||||
p1=(tx_pos[0] - bowtie_dims[0] - 2 * dl, tx_pos[1] - bowtie_dims[1] / 2 - 2 * dl, tx_pos[2] - 2 * dl),
|
||||
p2=(tx_pos[0] + bowtie_dims[0] + 2 * dl, tx_pos[1] + bowtie_dims[1] / 2 + 2 * dl, tx_pos[2] + 2 * dl),
|
||||
p1=(
|
||||
tx_pos[0] - bowtie_dims[0] - 2 * dl,
|
||||
tx_pos[1] - bowtie_dims[1] / 2 - 2 * dl,
|
||||
tx_pos[2] - 2 * dl,
|
||||
),
|
||||
p2=(
|
||||
tx_pos[0] + bowtie_dims[0] + 2 * dl,
|
||||
tx_pos[1] + bowtie_dims[1] / 2 + 2 * dl,
|
||||
tx_pos[2] + 2 * dl,
|
||||
),
|
||||
dl=(dl, dl, dl),
|
||||
filename="antenna_bowtie_fs_pcb",
|
||||
output_type="f",
|
||||
|
@@ -32,11 +32,14 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Plots a comparison of fields between " + "given simulation output and experimental data files.",
|
||||
description="Plots a comparison of fields between "
|
||||
+ "given simulation output and experimental data files.",
|
||||
usage="cd gprMax; python -m testing.test_experimental modelfile realfile output",
|
||||
)
|
||||
parser.add_argument("modelfile", help="name of model output file including path")
|
||||
parser.add_argument("realfile", help="name of file containing experimental data including path")
|
||||
parser.add_argument(
|
||||
"realfile", help="name of file containing experimental data including path"
|
||||
)
|
||||
parser.add_argument("output", help="output to be plotted, i.e. Ex Ey Ez", nargs="+")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@@ -90,13 +90,19 @@ for i, model in enumerate(testmodels):
|
||||
# Arrays for storing time
|
||||
float_or_double = filetest[path + outputstest[0]].dtype
|
||||
timetest = (
|
||||
np.linspace(0, (filetest.attrs["Iterations"] - 1) * filetest.attrs["dt"], num=filetest.attrs["Iterations"])
|
||||
np.linspace(
|
||||
0,
|
||||
(filetest.attrs["Iterations"] - 1) * filetest.attrs["dt"],
|
||||
num=filetest.attrs["Iterations"],
|
||||
)
|
||||
/ 1e-9
|
||||
)
|
||||
timeref = timetest
|
||||
|
||||
# Arrays for storing field data
|
||||
datatest = np.zeros((filetest.attrs["Iterations"], len(outputstest)), dtype=float_or_double)
|
||||
datatest = np.zeros(
|
||||
(filetest.attrs["Iterations"], len(outputstest)), dtype=float_or_double
|
||||
)
|
||||
for ID, name in enumerate(outputstest):
|
||||
datatest[:, ID] = filetest[path + str(name)][:]
|
||||
if np.any(np.isnan(datatest[:, ID])):
|
||||
@@ -106,11 +112,18 @@ for i, model in enumerate(testmodels):
|
||||
# Tx/Rx position to feed to analytical solution
|
||||
rxpos = filetest[path].attrs["Position"]
|
||||
txpos = filetest["/srcs/src1/"].attrs["Position"]
|
||||
rxposrelative = ((rxpos[0] - txpos[0]), (rxpos[1] - txpos[1]), (rxpos[2] - txpos[2]))
|
||||
rxposrelative = (
|
||||
(rxpos[0] - txpos[0]),
|
||||
(rxpos[1] - txpos[1]),
|
||||
(rxpos[2] - txpos[2]),
|
||||
)
|
||||
|
||||
# Analytical solution of a dipole in free space
|
||||
dataref = hertzian_dipole_fs(
|
||||
filetest.attrs["Iterations"], filetest.attrs["dt"], filetest.attrs["dx_dy_dz"], rxposrelative
|
||||
filetest.attrs["Iterations"],
|
||||
filetest.attrs["dt"],
|
||||
filetest.attrs["dx_dy_dz"],
|
||||
rxposrelative,
|
||||
)
|
||||
filetest.close()
|
||||
|
||||
@@ -143,18 +156,30 @@ for i, model in enumerate(testmodels):
|
||||
# Arrays for storing time
|
||||
timeref = np.zeros((fileref.attrs["Iterations"]), dtype=float_or_doubleref)
|
||||
timeref = (
|
||||
np.linspace(0, (fileref.attrs["Iterations"] - 1) * fileref.attrs["dt"], num=fileref.attrs["Iterations"])
|
||||
np.linspace(
|
||||
0,
|
||||
(fileref.attrs["Iterations"] - 1) * fileref.attrs["dt"],
|
||||
num=fileref.attrs["Iterations"],
|
||||
)
|
||||
/ 1e-9
|
||||
)
|
||||
timetest = np.zeros((filetest.attrs["Iterations"]), dtype=float_or_doubletest)
|
||||
timetest = (
|
||||
np.linspace(0, (filetest.attrs["Iterations"] - 1) * filetest.attrs["dt"], num=filetest.attrs["Iterations"])
|
||||
np.linspace(
|
||||
0,
|
||||
(filetest.attrs["Iterations"] - 1) * filetest.attrs["dt"],
|
||||
num=filetest.attrs["Iterations"],
|
||||
)
|
||||
/ 1e-9
|
||||
)
|
||||
|
||||
# Arrays for storing field data
|
||||
dataref = np.zeros((fileref.attrs["Iterations"], len(outputsref)), dtype=float_or_doubleref)
|
||||
datatest = np.zeros((filetest.attrs["Iterations"], len(outputstest)), dtype=float_or_doubletest)
|
||||
dataref = np.zeros(
|
||||
(fileref.attrs["Iterations"], len(outputsref)), dtype=float_or_doubleref
|
||||
)
|
||||
datatest = np.zeros(
|
||||
(filetest.attrs["Iterations"], len(outputstest)), dtype=float_or_doubletest
|
||||
)
|
||||
for ID, name in enumerate(outputsref):
|
||||
dataref[:, ID] = fileref[path + str(name)][:]
|
||||
datatest[:, ID] = filetest[path + str(name)][:]
|
||||
@@ -170,7 +195,10 @@ for i, model in enumerate(testmodels):
|
||||
for i in range(len(outputstest)):
|
||||
maxi = np.amax(np.abs(dataref[:, i]))
|
||||
datadiffs[:, i] = np.divide(
|
||||
np.abs(dataref[:, i] - datatest[:, i]), maxi, out=np.zeros_like(dataref[:, i]), where=maxi != 0
|
||||
np.abs(dataref[:, i] - datatest[:, i]),
|
||||
maxi,
|
||||
out=np.zeros_like(dataref[:, i]),
|
||||
where=maxi != 0,
|
||||
) # Replace any division by zero with zero
|
||||
|
||||
# Calculate power (ignore warning from taking a log of any zero values)
|
||||
@@ -260,8 +288,20 @@ for i, model in enumerate(testmodels):
|
||||
# bbox_inches='tight', pad_inches=0.1)
|
||||
# fig2.savefig(savediffs.with_suffix('.pdf'), dpi=None, format='pdf',
|
||||
# bbox_inches='tight', pad_inches=0.1)
|
||||
fig1.savefig(file.with_suffix(".png"), dpi=150, format="png", bbox_inches="tight", pad_inches=0.1)
|
||||
fig2.savefig(filediffs.with_suffix(".png"), dpi=150, format="png", bbox_inches="tight", pad_inches=0.1)
|
||||
fig1.savefig(
|
||||
file.with_suffix(".png"),
|
||||
dpi=150,
|
||||
format="png",
|
||||
bbox_inches="tight",
|
||||
pad_inches=0.1,
|
||||
)
|
||||
fig2.savefig(
|
||||
filediffs.with_suffix(".png"),
|
||||
dpi=150,
|
||||
format="png",
|
||||
bbox_inches="tight",
|
||||
pad_inches=0.1,
|
||||
)
|
||||
|
||||
# Summary of results
|
||||
for name, data in sorted(testresults.items()):
|
||||
|
@@ -100,12 +100,18 @@ if epsr:
|
||||
ax.plot([0, np.deg2rad(180 + thetac)], [min, 0], color="0.7", lw=2)
|
||||
ax.plot([np.deg2rad(270), np.deg2rad(90)], [0, 0], color="0.7", lw=2)
|
||||
ax.annotate("Air", xy=(np.deg2rad(270), 0), xytext=(8, 8), textcoords="offset points")
|
||||
ax.annotate("Ground", xy=(np.deg2rad(270), 0), xytext=(8, -15), textcoords="offset points")
|
||||
ax.annotate(
|
||||
"Ground", xy=(np.deg2rad(270), 0), xytext=(8, -15), textcoords="offset points"
|
||||
)
|
||||
|
||||
# Plot patterns
|
||||
for patt in range(0, len(radii)):
|
||||
pattplot = np.append(patterns[patt, :], patterns[patt, 0]) # Append start value to close circle
|
||||
pattplot = pattplot / np.max(np.max(patterns)) # Normalise, based on set of patterns
|
||||
pattplot = np.append(
|
||||
patterns[patt, :], patterns[patt, 0]
|
||||
) # Append start value to close circle
|
||||
pattplot = pattplot / np.max(
|
||||
np.max(patterns)
|
||||
) # Normalise, based on set of patterns
|
||||
|
||||
# Calculate power (ignore warning from taking a log of any zero values)
|
||||
with np.errstate(divide="ignore"):
|
||||
@@ -140,7 +146,11 @@ ax.set_yticklabels(yticks)
|
||||
ax.grid(True)
|
||||
handles, existlabels = ax.get_legend_handles_labels()
|
||||
leg = ax.legend(
|
||||
[handles[0], handles[-1]], [existlabels[0], existlabels[-1]], ncol=2, loc=(0.27, -0.12), frameon=False
|
||||
[handles[0], handles[-1]],
|
||||
[existlabels[0], existlabels[-1]],
|
||||
ncol=2,
|
||||
loc=(0.27, -0.12),
|
||||
frameon=False,
|
||||
) # Plot just first and last legend entries
|
||||
# leg = ax.legend([handles[0], handles[-3], handles[-2], handles[-1]], [existlabels[0], existlabels[-3], existlabels[-2], existlabels[-1]], ncol=4, loc=(-0.13,-0.12), frameon=False)
|
||||
[legobj.set_linewidth(2) for legobj in leg.legendHandles]
|
||||
|
@@ -9,9 +9,13 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Writes a HDF5 file of AustinMan or AustinWoman head only.", usage="python head_only_hdf5 filename"
|
||||
description="Writes a HDF5 file of AustinMan or AustinWoman head only.",
|
||||
usage="python head_only_hdf5 filename",
|
||||
)
|
||||
parser.add_argument(
|
||||
"filename",
|
||||
help="name and path to (HDF5) file containing AustinMan or AustinWoman model",
|
||||
)
|
||||
parser.add_argument("filename", help="name and path to (HDF5) file containing AustinMan or AustinWoman model")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Read full body HDF5 file
|
||||
@@ -22,7 +26,9 @@ data = f["/data"][:, :, :]
|
||||
# Define head as last 1/8 of total body height
|
||||
nzhead = 7 * int(data.shape[2] / 8)
|
||||
|
||||
logger.info(f"Dimensions of head model: {data.shape[0]:g} x {data.shape[1]:g} x {data.shape[2] - nzhead:g} cells")
|
||||
logger.info(
|
||||
f"Dimensions of head model: {data.shape[0]:g} x {data.shape[1]:g} x {data.shape[2] - nzhead:g} cells"
|
||||
)
|
||||
|
||||
# Write HDF5 file
|
||||
headfile = os.path.splitext(args.filename)[0] + "_head.h5"
|
||||
|
@@ -100,7 +100,15 @@ class Relaxation(object):
|
||||
def check_inputs(self):
|
||||
"""Check the validity of the inputs."""
|
||||
try:
|
||||
d = [float(i) for i in [self.number_of_debye_poles, self.sigma, self.mu, self.mu_sigma]]
|
||||
d = [
|
||||
float(i)
|
||||
for i in [
|
||||
self.number_of_debye_poles,
|
||||
self.sigma,
|
||||
self.mu,
|
||||
self.mu_sigma,
|
||||
]
|
||||
]
|
||||
except ValueError:
|
||||
sys.exit("The inputs should be numeric.")
|
||||
if not isinstance(self.number_of_debye_poles, int):
|
||||
@@ -120,7 +128,9 @@ class Relaxation(object):
|
||||
Returns:
|
||||
s (str): Info about chosen function and its parameters.
|
||||
"""
|
||||
print(f"Approximating {self.name}" f" using {self.number_of_debye_poles} Debye poles")
|
||||
print(
|
||||
f"Approximating {self.name} using {self.number_of_debye_poles} Debye poles"
|
||||
)
|
||||
print(f"{self.name} parameters: ")
|
||||
s = "".join(f"{k:10s} = {v}\n" for k, v in self.params.items())
|
||||
print(s)
|
||||
@@ -172,7 +182,12 @@ class Relaxation(object):
|
||||
self.rl, self.im = q.real, q.imag
|
||||
|
||||
if self.number_of_debye_poles == -1:
|
||||
print("\n#########", "Try to automaticaly fit number of Debye poles, up to 20!", "##########\n", sep="")
|
||||
print(
|
||||
"\n#########",
|
||||
"Try to automaticaly fit number of Debye poles, up to 20!",
|
||||
"##########\n",
|
||||
sep="",
|
||||
)
|
||||
error = np.infty # artificial best error starting value
|
||||
self.number_of_debye_poles = 1
|
||||
iteration = 1
|
||||
@@ -195,7 +210,11 @@ class Relaxation(object):
|
||||
|
||||
# Print the results in gprMax format style
|
||||
properties = self.print_output(tau, weights, ee)
|
||||
print(f"The average fractional error for:\n" f"- real part: {err_real}\n" f"- imaginary part: {err_imag}\n")
|
||||
print(
|
||||
f"The average fractional error for:\n"
|
||||
f"- real part: {err_real}\n"
|
||||
f"- imaginary part: {err_imag}\n"
|
||||
)
|
||||
if self.save:
|
||||
self.save_result(properties)
|
||||
# Plot the actual and the approximate dielectric properties
|
||||
@@ -220,12 +239,16 @@ class Relaxation(object):
|
||||
print(f" |{'e_inf':^14s}|{'De':^14s}|{'log(tau_0)':^25s}|")
|
||||
print("_" * 65)
|
||||
for i in range(0, len(tau)):
|
||||
print(f"Debye {i + 1}|{ee / len(tau):^14.5f}|{weights[i]:^14.5f}|{tau[i]:^25.5f}|")
|
||||
print(
|
||||
f"Debye {i + 1}|{ee / len(tau):^14.5f}|{weights[i]:^14.5f}|{tau[i]:^25.5f}|"
|
||||
)
|
||||
print("_" * 65)
|
||||
|
||||
# Print the Debye expnasion in a gprMax format
|
||||
material_prop = []
|
||||
material_prop.append(f"#material: {ee} {self.sigma} {self.mu} {self.mu_sigma} {self.material_name}\n")
|
||||
material_prop.append(
|
||||
f"#material: {ee} {self.sigma} {self.mu} {self.mu_sigma} {self.material_name}\n"
|
||||
)
|
||||
print(material_prop[0], end="")
|
||||
dispersion_prop = f"#add_dispersion_debye: {len(tau)}"
|
||||
for i in range(len(tau)):
|
||||
@@ -251,10 +274,34 @@ class Relaxation(object):
|
||||
gs = gridspec.GridSpec(2, 1)
|
||||
ax = fig.add_subplot(gs[0])
|
||||
ax.grid(b=True, which="major", linewidth=0.2, linestyle="--")
|
||||
ax.semilogx(self.freq * 1e-6, rl_exp, "b-", linewidth=2.0, label="Debye Expansion: Real part")
|
||||
ax.semilogx(self.freq * 1e-6, -im_exp, "k-", linewidth=2.0, label="Debye Expansion: Imaginary part")
|
||||
ax.semilogx(self.freq * 1e-6, self.rl, "r.", linewidth=2.0, label=f"{self.name}: Real part")
|
||||
ax.semilogx(self.freq * 1e-6, -self.im, "g.", linewidth=2.0, label=f"{self.name}: Imaginary part")
|
||||
ax.semilogx(
|
||||
self.freq * 1e-6,
|
||||
rl_exp,
|
||||
"b-",
|
||||
linewidth=2.0,
|
||||
label="Debye Expansion: Real part",
|
||||
)
|
||||
ax.semilogx(
|
||||
self.freq * 1e-6,
|
||||
-im_exp,
|
||||
"k-",
|
||||
linewidth=2.0,
|
||||
label="Debye Expansion: Imaginary part",
|
||||
)
|
||||
ax.semilogx(
|
||||
self.freq * 1e-6,
|
||||
self.rl,
|
||||
"r.",
|
||||
linewidth=2.0,
|
||||
label=f"{self.name}: Real part",
|
||||
)
|
||||
ax.semilogx(
|
||||
self.freq * 1e-6,
|
||||
-self.im,
|
||||
"g.",
|
||||
linewidth=2.0,
|
||||
label=f"{self.name}: Imaginary part",
|
||||
)
|
||||
ax.set_ylim([-1, np.max(np.concatenate([self.rl, -self.im])) + 1])
|
||||
ax.legend()
|
||||
ax.set_xlabel("Frequency (MHz)")
|
||||
@@ -262,8 +309,20 @@ class Relaxation(object):
|
||||
|
||||
ax = fig.add_subplot(gs[1])
|
||||
ax.grid(b=True, which="major", linewidth=0.2, linestyle="--")
|
||||
ax.semilogx(self.freq * 1e-6, (rl_exp - self.rl) / (self.rl + 1), "b-", linewidth=2.0, label="Real part")
|
||||
ax.semilogx(self.freq * 1e-6, (-im_exp + self.im) / (self.im + 1), "k-", linewidth=2.0, label="Imaginary part")
|
||||
ax.semilogx(
|
||||
self.freq * 1e-6,
|
||||
(rl_exp - self.rl) / (self.rl + 1),
|
||||
"b-",
|
||||
linewidth=2.0,
|
||||
label="Real part",
|
||||
)
|
||||
ax.semilogx(
|
||||
self.freq * 1e-6,
|
||||
(-im_exp + self.im) / (self.im + 1),
|
||||
"k-",
|
||||
linewidth=2.0,
|
||||
label="Imaginary part",
|
||||
)
|
||||
ax.legend()
|
||||
ax.set_xlabel("Frequency (MHz)")
|
||||
ax.set_ylabel("Relative approximation error")
|
||||
@@ -284,8 +343,12 @@ class Relaxation(object):
|
||||
avg_err_imag (float): average fractional error
|
||||
for conductivity (imaginary part)
|
||||
"""
|
||||
avg_err_real = np.sum(np.abs((rl_exp - self.rl) / (self.rl + 1)) * 100) / len(rl_exp)
|
||||
avg_err_imag = np.sum(np.abs((-im_exp + self.im) / (self.im + 1)) * 100) / len(im_exp)
|
||||
avg_err_real = np.sum(np.abs((rl_exp - self.rl) / (self.rl + 1)) * 100) / len(
|
||||
rl_exp
|
||||
)
|
||||
avg_err_imag = np.sum(np.abs((-im_exp + self.im) / (self.im + 1)) * 100) / len(
|
||||
im_exp
|
||||
)
|
||||
return avg_err_real, avg_err_imag
|
||||
|
||||
@staticmethod
|
||||
@@ -306,7 +369,10 @@ class Relaxation(object):
|
||||
elif os.path.isdir("user_libs/materials"):
|
||||
file_path = os.path.join("user_libs", "materials", "my_materials.txt")
|
||||
else:
|
||||
sys.exit("Cannot save material properties " f"in {os.path.join(fdir, 'my_materials.txt')}!")
|
||||
sys.exit(
|
||||
"Cannot save material properties "
|
||||
f"in {os.path.join(fdir, 'my_materials.txt')}!"
|
||||
)
|
||||
with open(file_path, "a") as fileH:
|
||||
fileH.write(f"## {output[0].split(' ')[-1]}")
|
||||
fileH.writelines(output)
|
||||
@@ -382,7 +448,13 @@ class HavriliakNegami(Relaxation):
|
||||
self.f_min, self.f_max = f_min, f_max
|
||||
# Choosing n frequencies logarithmicaly equally spaced between the bounds given
|
||||
self.set_freq(self.f_min, self.f_max, self.f_n)
|
||||
self.e_inf, self.alpha, self.beta, self.de, self.tau_0 = e_inf, alpha, beta, de, tau_0
|
||||
self.e_inf, self.alpha, self.beta, self.de, self.tau_0 = (
|
||||
e_inf,
|
||||
alpha,
|
||||
beta,
|
||||
de,
|
||||
tau_0,
|
||||
)
|
||||
self.params = {
|
||||
"f_min": self.f_min,
|
||||
"f_max": self.f_max,
|
||||
@@ -412,7 +484,11 @@ class HavriliakNegami(Relaxation):
|
||||
def calculation(self):
|
||||
"""Calculates the Havriliak-Negami function for
|
||||
the given parameters."""
|
||||
return self.e_inf + self.de / (1 + (1j * 2 * np.pi * self.freq * self.tau_0) ** self.alpha) ** self.beta
|
||||
return (
|
||||
self.e_inf
|
||||
+ self.de
|
||||
/ (1 + (1j * 2 * np.pi * self.freq * self.tau_0) ** self.alpha) ** self.beta
|
||||
)
|
||||
|
||||
|
||||
class Jonscher(Relaxation):
|
||||
@@ -501,9 +577,9 @@ class Jonscher(Relaxation):
|
||||
|
||||
def calculation(self):
|
||||
"""Calculates the Q function for the given parameters"""
|
||||
return self.e_inf + (self.a_p * (2 * np.pi * self.freq / self.omega_p) ** (self.n_p - 1)) * (
|
||||
1 - 1j / np.tan(self.n_p * np.pi / 2)
|
||||
)
|
||||
return self.e_inf + (
|
||||
self.a_p * (2 * np.pi * self.freq / self.omega_p) ** (self.n_p - 1)
|
||||
) * (1 - 1j / np.tan(self.n_p * np.pi / 2))
|
||||
|
||||
|
||||
class Crim(Relaxation):
|
||||
@@ -583,7 +659,9 @@ class Crim(Relaxation):
|
||||
if (np.array(d) < 0).sum() != 0:
|
||||
sys.exit("The inputs should be positive.")
|
||||
if len(self.volumetric_fractions) != len(self.materials):
|
||||
sys.exit("Number of volumetric volumes does not match the dielectric properties")
|
||||
sys.exit(
|
||||
"Number of volumetric volumes does not match the dielectric properties"
|
||||
)
|
||||
# Check if the materials are at least two
|
||||
if len(self.volumetric_fractions) < 2:
|
||||
sys.exit("The materials should be at least 2")
|
||||
@@ -604,7 +682,10 @@ class Crim(Relaxation):
|
||||
|
||||
def print_info(self):
|
||||
"""Print information about chosen approximation settings"""
|
||||
print(f"Approximating Complex Refractive Index Model (CRIM)" f" using {self.number_of_debye_poles} Debye poles")
|
||||
print(
|
||||
f"Approximating Complex Refractive Index Model (CRIM)"
|
||||
f" using {self.number_of_debye_poles} Debye poles"
|
||||
)
|
||||
print("CRIM parameters: ")
|
||||
for i in range(len(self.volumetric_fractions)):
|
||||
print(f"Material {i + 1}.:")
|
||||
@@ -617,7 +698,9 @@ class Crim(Relaxation):
|
||||
def calculation(self):
|
||||
"""Calculates the Crim function for the given parameters"""
|
||||
return np.sum(
|
||||
np.repeat(self.volumetric_fractions, len(self.freq)).reshape((-1, len(self.materials)))
|
||||
np.repeat(self.volumetric_fractions, len(self.freq)).reshape(
|
||||
(-1, len(self.materials))
|
||||
)
|
||||
* (
|
||||
self.materials[:, 0]
|
||||
+ self.materials[:, 1]
|
||||
@@ -626,7 +709,9 @@ class Crim(Relaxation):
|
||||
+ 1j
|
||||
* 2
|
||||
* np.pi
|
||||
* np.repeat(self.freq, len(self.materials)).reshape((-1, len(self.materials)))
|
||||
* np.repeat(self.freq, len(self.materials)).reshape(
|
||||
(-1, len(self.materials))
|
||||
)
|
||||
* self.materials[:, 2]
|
||||
)
|
||||
)
|
||||
@@ -691,13 +776,19 @@ class Rawdata(Relaxation):
|
||||
# Read the file
|
||||
with open(self.filename) as f:
|
||||
try:
|
||||
array = np.array([[float(x) for x in line.split(self.delimiter)] for line in f])
|
||||
array = np.array(
|
||||
[[float(x) for x in line.split(self.delimiter)] for line in f]
|
||||
)
|
||||
except ValueError:
|
||||
sys.exit("Error: The inputs should be numeric")
|
||||
|
||||
self.set_freq(min(array[:, 0]), max(array[:, 0]), self.f_n)
|
||||
rl_interp = scipy.interpolate.interp1d(array[:, 0], array[:, 1], fill_value="extrapolate")
|
||||
im_interp = scipy.interpolate.interp1d(array[:, 0], array[:, 2], fill_value="extrapolate")
|
||||
rl_interp = scipy.interpolate.interp1d(
|
||||
array[:, 0], array[:, 1], fill_value="extrapolate"
|
||||
)
|
||||
im_interp = scipy.interpolate.interp1d(
|
||||
array[:, 0], array[:, 2], fill_value="extrapolate"
|
||||
)
|
||||
return rl_interp(self.freq) - 1j * im_interp(self.freq)
|
||||
|
||||
|
||||
@@ -774,17 +865,63 @@ if __name__ == "__main__":
|
||||
setup.run()
|
||||
# Testing setup
|
||||
setup = Rawdata(
|
||||
"examples/Test.txt", 0.1, 1, 0.1, "M1", number_of_debye_poles=3, plot=True, optimizer_options={"seed": 111}
|
||||
"examples/Test.txt",
|
||||
0.1,
|
||||
1,
|
||||
0.1,
|
||||
"M1",
|
||||
number_of_debye_poles=3,
|
||||
plot=True,
|
||||
optimizer_options={"seed": 111},
|
||||
)
|
||||
setup.run()
|
||||
np.random.seed(111)
|
||||
setup = HavriliakNegami(1e12, 1e-3, 0.5, 1, 10, 5, 1e-6, 0.1, 1, 0, "M2", number_of_debye_poles=6, plot=True)
|
||||
setup = HavriliakNegami(
|
||||
1e12,
|
||||
1e-3,
|
||||
0.5,
|
||||
1,
|
||||
10,
|
||||
5,
|
||||
1e-6,
|
||||
0.1,
|
||||
1,
|
||||
0,
|
||||
"M2",
|
||||
number_of_debye_poles=6,
|
||||
plot=True,
|
||||
)
|
||||
setup.run()
|
||||
setup = Jonscher(1e6, 1e-5, 50, 1, 1e5, 0.7, 0.1, 1, 0.1, "M3", number_of_debye_poles=4, plot=True)
|
||||
setup = Jonscher(
|
||||
1e6,
|
||||
1e-5,
|
||||
50,
|
||||
1,
|
||||
1e5,
|
||||
0.7,
|
||||
0.1,
|
||||
1,
|
||||
0.1,
|
||||
"M3",
|
||||
number_of_debye_poles=4,
|
||||
plot=True,
|
||||
)
|
||||
setup.run()
|
||||
f = np.array([0.5, 0.5])
|
||||
material1 = [3, 25, 1e6]
|
||||
material2 = [3, 0, 1e3]
|
||||
materials = np.array([material1, material2])
|
||||
setup = Crim(1 * 1e-1, 1e-9, 0.5, f, materials, 0.1, 1, 0, "M4", number_of_debye_poles=2, plot=True)
|
||||
setup = Crim(
|
||||
1 * 1e-1,
|
||||
1e-9,
|
||||
0.5,
|
||||
f,
|
||||
materials,
|
||||
0.1,
|
||||
1,
|
||||
0,
|
||||
"M4",
|
||||
number_of_debye_poles=2,
|
||||
plot=True,
|
||||
)
|
||||
setup.run()
|
||||
|
@@ -121,7 +121,16 @@ class PSO_DLS(Optimizer):
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, swarmsize=40, maxiter=50, omega=0.9, phip=0.9, phig=0.9, minstep=1e-8, minfun=1e-8, pflag=False, seed=None
|
||||
self,
|
||||
swarmsize=40,
|
||||
maxiter=50,
|
||||
omega=0.9,
|
||||
phip=0.9,
|
||||
phig=0.9,
|
||||
minstep=1e-8,
|
||||
minfun=1e-8,
|
||||
pflag=False,
|
||||
seed=None,
|
||||
):
|
||||
super(PSO_DLS, self).__init__(maxiter, seed)
|
||||
self.swarmsize = swarmsize
|
||||
@@ -159,7 +168,9 @@ class PSO_DLS(Optimizer):
|
||||
assert hasattr(func, "__call__"), "Invalid function handle"
|
||||
lb = np.array(lb)
|
||||
ub = np.array(ub)
|
||||
assert np.all(ub > lb), "All upper-bound values must be greater than lower-bound values"
|
||||
assert np.all(ub > lb), (
|
||||
"All upper-bound values must be greater than lower-bound values"
|
||||
)
|
||||
|
||||
vhigh = np.abs(ub - lb)
|
||||
vlow = -vhigh
|
||||
@@ -226,10 +237,16 @@ class PSO_DLS(Optimizer):
|
||||
tmp = x[i, :].copy()
|
||||
stepsize = np.sqrt(np.sum((g - tmp) ** 2))
|
||||
if np.abs(fg - fx) <= self.minfun:
|
||||
print(f"Stopping search: Swarm best objective " f"change less than {self.minfun}")
|
||||
print(
|
||||
f"Stopping search: Swarm best objective "
|
||||
f"change less than {self.minfun}"
|
||||
)
|
||||
return tmp, fx
|
||||
elif stepsize <= self.minstep:
|
||||
print(f"Stopping search: Swarm best position " f"change less than {self.minstep}")
|
||||
print(
|
||||
f"Stopping search: Swarm best position "
|
||||
f"change less than {self.minstep}"
|
||||
)
|
||||
return tmp, fx
|
||||
else:
|
||||
g = tmp.copy()
|
||||
@@ -471,7 +488,10 @@ def DLS(logt, rl, im, freq):
|
||||
# Solving the overdetermined system y=Ax
|
||||
x = np.abs(np.linalg.lstsq(d.imag, im, rcond=None)[0])
|
||||
# x - absolute damped least-squares solution
|
||||
rp, ip = np.matmul(d.real, x[np.newaxis].T).T[0], np.matmul(d.imag, x[np.newaxis].T).T[0]
|
||||
rp, ip = (
|
||||
np.matmul(d.real, x[np.newaxis].T).T[0],
|
||||
np.matmul(d.imag, x[np.newaxis].T).T[0],
|
||||
)
|
||||
cost_i = np.sum(np.abs(ip - im)) / len(im)
|
||||
ee = np.mean(rl - rp)
|
||||
ee = max(ee, 1)
|
||||
|
@@ -64,7 +64,9 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
patchheight = 0.016
|
||||
tx = x + 0.114, y + 0.052, z + skidthickness
|
||||
else:
|
||||
logger.exception("This antenna module can only be used with a spatial discretisation of 1mm or 2mm")
|
||||
logger.exception(
|
||||
"This antenna module can only be used with a spatial discretisation of 1mm or 2mm"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# If using parameters from an optimisation
|
||||
@@ -80,8 +82,12 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
hdpesig = kwargs["hdpesig"]
|
||||
sourceresistance = 195
|
||||
rxres = 50
|
||||
absorber1 = gprMax.Material(er=absorber1Er, se=absorber1sig, mr=1, sm=0, id="absorber1")
|
||||
absorber2 = gprMax.Material(er=absorber2Er, se=absorber2sig, mr=1, sm=0, id="absorber2")
|
||||
absorber1 = gprMax.Material(
|
||||
er=absorber1Er, se=absorber1sig, mr=1, sm=0, id="absorber1"
|
||||
)
|
||||
absorber2 = gprMax.Material(
|
||||
er=absorber2Er, se=absorber2sig, mr=1, sm=0, id="absorber2"
|
||||
)
|
||||
pcb = gprMax.Material(er=pcbEr, se=pcbsig, mr=1, sm=0, id="pcb")
|
||||
hdpe = gprMax.Material(er=hdpeEr, se=hdpesig, mr=1, sm=0, id="hdpe")
|
||||
scene_objects.extend((absorber1, absorber2, pcb, hdpe))
|
||||
@@ -95,19 +101,27 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
if optstate == "WarrenThesis":
|
||||
# Original optimised values from http://hdl.handle.net/1842/4074
|
||||
excitationfreq = 1.71e9
|
||||
sourceresistance = 230 # Correction for old (< 123) GprMax3D bug (optimised to 4)
|
||||
sourceresistance = (
|
||||
230 # Correction for old (< 123) GprMax3D bug (optimised to 4)
|
||||
)
|
||||
rxres = 925 # Resistance at Rx bowtie
|
||||
absorber1 = gprMax.Material(er=1.58, se=0.428, mr=1, sm=0, id="absorber1")
|
||||
absorber2 = gprMax.Material(er=3, se=0, mr=1, sm=0, id="absorber2") # Foam modelled as PCB material
|
||||
absorber2 = gprMax.Material(
|
||||
er=3, se=0, mr=1, sm=0, id="absorber2"
|
||||
) # Foam modelled as PCB material
|
||||
pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id="pcb")
|
||||
hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id="hdpe")
|
||||
rxres = gprMax.Material(er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id="rxres")
|
||||
rxres = gprMax.Material(
|
||||
er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id="rxres"
|
||||
)
|
||||
scene_objects.extend((absorber1, absorber2, pcb, hdpe, rxres))
|
||||
|
||||
elif optstate == "DebyeAbsorber":
|
||||
# Same values as WarrenThesis but uses dispersive absorber properties for Eccosorb LS22
|
||||
excitationfreq = 1.71e9
|
||||
sourceresistance = 230 # Correction for old (< 123) GprMax3D bug (optimised to 4)
|
||||
sourceresistance = (
|
||||
230 # Correction for old (< 123) GprMax3D bug (optimised to 4)
|
||||
)
|
||||
rxres = 925 # Resistance at Rx bowtie
|
||||
absorber1 = gprMax.Material(er=1, se=0, mr=1, sm=0, id="absorber1")
|
||||
# Eccosorb LS22 3-pole Debye model (https://bitbucket.org/uoyaeg/aegboxts/wiki/Home)
|
||||
@@ -117,11 +131,17 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
tau=[1.00723e-11, 1.55686e-10, 3.44129e-10],
|
||||
material_ids=["absorber1"],
|
||||
)
|
||||
absorber2 = gprMax.Material(er=3, se=0, mr=1, sm=0, id="absorber2") # Foam modelled as PCB material
|
||||
absorber2 = gprMax.Material(
|
||||
er=3, se=0, mr=1, sm=0, id="absorber2"
|
||||
) # Foam modelled as PCB material
|
||||
pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id="pcb")
|
||||
hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id="hdpe")
|
||||
rxres = gprMax.Material(er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id="rxres")
|
||||
scene_objects.extend((absorber1, absorber1_disp, absorber2, pcb, hdpe, rxres))
|
||||
rxres = gprMax.Material(
|
||||
er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id="rxres"
|
||||
)
|
||||
scene_objects.extend(
|
||||
(absorber1, absorber1_disp, absorber2, pcb, hdpe, rxres)
|
||||
)
|
||||
|
||||
elif optstate == "GiannakisPaper":
|
||||
# Further optimised values from https://doi.org/10.1109/TGRS.2018.2869027
|
||||
@@ -152,13 +172,21 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
# Metallic enclosure
|
||||
b3 = gprMax.Box(
|
||||
p1=(x + 0.025, y + casethickness, z + skidthickness),
|
||||
p2=(x + casesize[0] - 0.025, y + casesize[1] - casethickness, z + skidthickness + 0.027),
|
||||
p2=(
|
||||
x + casesize[0] - 0.025,
|
||||
y + casesize[1] - casethickness,
|
||||
z + skidthickness + 0.027,
|
||||
),
|
||||
material_id="pec",
|
||||
)
|
||||
|
||||
# Absorber material (absorber1) and foam (absorber2) around edge of absorber
|
||||
b4 = gprMax.Box(
|
||||
p1=(x + 0.025 + shieldthickness, y + casethickness + shieldthickness, z + skidthickness),
|
||||
p1=(
|
||||
x + 0.025 + shieldthickness,
|
||||
y + casethickness + shieldthickness,
|
||||
z + skidthickness,
|
||||
),
|
||||
p2=(
|
||||
x + 0.025 + shieldthickness + 0.057,
|
||||
y + casesize[1] - casethickness - shieldthickness,
|
||||
@@ -299,12 +327,24 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
scene_objects.extend((p9, p10))
|
||||
|
||||
# Edges that represent wire between bowtie halves in 1mm model
|
||||
e1 = gprMax.Edge(p1=(tx[0] - 0.059, tx[1] - dy, tx[2]), p2=(tx[0] - 0.059, tx[1], tx[2]), material_id="pec")
|
||||
e2 = gprMax.Edge(
|
||||
p1=(tx[0] - 0.059, tx[1] + dy, tx[2]), p2=(tx[0] - 0.059, tx[1] + 0.002, tx[2]), material_id="pec"
|
||||
e1 = gprMax.Edge(
|
||||
p1=(tx[0] - 0.059, tx[1] - dy, tx[2]),
|
||||
p2=(tx[0] - 0.059, tx[1], tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
e2 = gprMax.Edge(
|
||||
p1=(tx[0] - 0.059, tx[1] + dy, tx[2]),
|
||||
p2=(tx[0] - 0.059, tx[1] + 0.002, tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
e3 = gprMax.Edge(
|
||||
p1=(tx[0], tx[1] - dy, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec"
|
||||
)
|
||||
e4 = gprMax.Edge(
|
||||
p1=(tx[0], tx[1] + dz, tx[2]),
|
||||
p2=(tx[0], tx[1] + 0.002, tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec")
|
||||
e4 = gprMax.Edge(p1=(tx[0], tx[1] + dz, tx[2]), p2=(tx[0], tx[1] + 0.002, tx[2]), material_id="pec")
|
||||
scene_objects.extend((e1, e2, e3, e4))
|
||||
|
||||
elif resolution == 0.002:
|
||||
@@ -359,13 +399,21 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
scene_objects.extend((p11, p12))
|
||||
|
||||
# Skid
|
||||
b10 = gprMax.Box(p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + skidthickness), material_id="hdpe")
|
||||
b10 = gprMax.Box(
|
||||
p1=(x, y, z),
|
||||
p2=(x + casesize[0], y + casesize[1], z + skidthickness),
|
||||
material_id="hdpe",
|
||||
)
|
||||
scene_objects.append(b10)
|
||||
|
||||
# Geometry views
|
||||
gv1 = gprMax.GeometryView(
|
||||
p1=(x - dx, y - dy, z - dz),
|
||||
p2=(x + casesize[0] + dx, y + casesize[1] + dy, z + skidthickness + casesize[2] + dz),
|
||||
p2=(
|
||||
x + casesize[0] + dx,
|
||||
y + casesize[1] + dy,
|
||||
z + skidthickness + casesize[2] + dz,
|
||||
),
|
||||
dl=(dx, dy, dz),
|
||||
filename="antenna_like_GSSI_1500",
|
||||
output_type="n",
|
||||
@@ -382,19 +430,29 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
# Excitation
|
||||
if optstate == "WarrenThesis" or optstate == "DebyeAbsorber":
|
||||
# Gaussian pulse
|
||||
w1 = gprMax.Waveform(wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian")
|
||||
w1 = gprMax.Waveform(
|
||||
wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian"
|
||||
)
|
||||
vs1 = gprMax.VoltageSource(
|
||||
polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_gaussian"
|
||||
polarisation="y",
|
||||
p1=(tx[0], tx[1], tx[2]),
|
||||
resistance=sourceresistance,
|
||||
waveform_id="my_gaussian",
|
||||
)
|
||||
scene_objects.extend((w1, vs1))
|
||||
|
||||
elif optstate == "GiannakisPaper":
|
||||
# Optimised custom pulse
|
||||
exc1 = gprMax.ExcitationFile(
|
||||
filepath="toolboxes/GPRAntennaModels/GSSI_1500MHz_pulse.txt", kind="linear", fill_value="extrapolate"
|
||||
filepath="toolboxes/GPRAntennaModels/GSSI_1500MHz_pulse.txt",
|
||||
kind="linear",
|
||||
fill_value="extrapolate",
|
||||
)
|
||||
vs1 = gprMax.VoltageSource(
|
||||
polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_pulse"
|
||||
polarisation="y",
|
||||
p1=(tx[0], tx[1], tx[2]),
|
||||
resistance=sourceresistance,
|
||||
waveform_id="my_pulse",
|
||||
)
|
||||
scene_objects.extend((exc1, vs1))
|
||||
|
||||
@@ -402,7 +460,9 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
if resolution == 0.001:
|
||||
if optstate == "WarrenThesis" or optstate == "DebyeAbsorber":
|
||||
e1 = gprMax.Edge(
|
||||
p1=(tx[0] - 0.059, tx[1], tx[2]), p2=(tx[0] - 0.059, tx[1] + dy, tx[2]), material_id="rxres"
|
||||
p1=(tx[0] - 0.059, tx[1], tx[2]),
|
||||
p2=(tx[0] - 0.059, tx[1] + dy, tx[2]),
|
||||
material_id="rxres",
|
||||
)
|
||||
scene_objects.append(e1)
|
||||
r1 = gprMax.Rx(p1=(tx[0] - 0.059, tx[1], tx[2]), id="rxbowtie", outputs=["Ey"])
|
||||
@@ -411,7 +471,9 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
|
||||
elif resolution == 0.002:
|
||||
if optstate == "WarrenThesis" or optstate == "DebyeAbsorber":
|
||||
e1 = gprMax.Edge(
|
||||
p1=(tx[0] - 0.060, tx[1], tx[2]), p2=(tx[0] - 0.060, tx[1] + dy, tx[2]), material_id="rxres"
|
||||
p1=(tx[0] - 0.060, tx[1], tx[2]),
|
||||
p2=(tx[0] - 0.060, tx[1] + dy, tx[2]),
|
||||
material_id="rxres",
|
||||
)
|
||||
scene_objects.append(e1)
|
||||
r1 = gprMax.Rx(p1=(tx[0] - 0.060, tx[1], tx[2]), id="rxbowtie", outputs=["Ey"])
|
||||
@@ -492,7 +554,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
dz = 0.002
|
||||
foamsurroundthickness = 0.002
|
||||
metalboxheight = 0.088
|
||||
tx = x + 0.01 + 0.004 + 0.056, y + casethickness + 0.005 + 0.143 - 0.002, z + skidthickness - 0.002
|
||||
tx = (
|
||||
x + 0.01 + 0.004 + 0.056,
|
||||
y + casethickness + 0.005 + 0.143 - 0.002,
|
||||
z + skidthickness - 0.002,
|
||||
)
|
||||
|
||||
# Material definitions
|
||||
absorber = gprMax.Material(er=absorberEr, se=absorbersig, mr=1, sm=0, id="absorber")
|
||||
@@ -510,17 +576,28 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
)
|
||||
b2 = gprMax.Box(
|
||||
p1=(x + casethickness, y + casethickness, z + skidthickness - 0.002),
|
||||
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + casesize[2] - casethickness),
|
||||
p2=(
|
||||
x + casesize[0] - casethickness,
|
||||
y + casesize[1] - casethickness,
|
||||
z + casesize[2] - casethickness,
|
||||
),
|
||||
material_id="free_space",
|
||||
)
|
||||
|
||||
# Metallic enclosure
|
||||
b3 = gprMax.Box(
|
||||
p1=(x + casethickness, y + casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight)),
|
||||
p1=(
|
||||
x + casethickness,
|
||||
y + casethickness,
|
||||
z + skidthickness + (metalmiddleplateheight - metalboxheight),
|
||||
),
|
||||
p2=(
|
||||
x + casesize[0] - casethickness,
|
||||
y + casesize[1] - casethickness,
|
||||
z + skidthickness + (metalmiddleplateheight - metalboxheight) + metalboxheight,
|
||||
z
|
||||
+ skidthickness
|
||||
+ (metalmiddleplateheight - metalboxheight)
|
||||
+ metalboxheight,
|
||||
),
|
||||
material_id="pec",
|
||||
)
|
||||
@@ -552,7 +629,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
|
||||
# PCB
|
||||
b6 = gprMax.Box(
|
||||
p1=(x + 0.01 + 0.005 + 0.017, y + casethickness + 0.005 + 0.021, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.017,
|
||||
y + casethickness + 0.005 + 0.021,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.033 + bowtiebase,
|
||||
y + casethickness + 0.006 + 0.202 + patchheight,
|
||||
@@ -561,7 +642,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
material_id="pcb",
|
||||
)
|
||||
b7 = gprMax.Box(
|
||||
p1=(x + 0.01 + 0.005 + 0.179, y + casethickness + 0.005 + 0.021, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.179,
|
||||
y + casethickness + 0.005 + 0.021,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.195 + bowtiebase,
|
||||
y + casethickness + 0.006 + 0.202 + patchheight,
|
||||
@@ -581,18 +666,29 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
)
|
||||
b9 = gprMax.Box(
|
||||
p1=(x + casethickness, y + casethickness, z + skidthickness - 0.002),
|
||||
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + casesize[2] - casethickness),
|
||||
p2=(
|
||||
x + casesize[0] - casethickness,
|
||||
y + casesize[1] - casethickness,
|
||||
z + casesize[2] - casethickness,
|
||||
),
|
||||
material_id="free_space",
|
||||
averaging="n",
|
||||
)
|
||||
|
||||
# Metallic enclosure
|
||||
b10 = gprMax.Box(
|
||||
p1=(x + casethickness, y + casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight)),
|
||||
p1=(
|
||||
x + casethickness,
|
||||
y + casethickness,
|
||||
z + skidthickness + (metalmiddleplateheight - metalboxheight),
|
||||
),
|
||||
p2=(
|
||||
x + casesize[0] - casethickness,
|
||||
y + casesize[1] - casethickness,
|
||||
z + skidthickness + (metalmiddleplateheight - metalboxheight) + metalboxheight,
|
||||
z
|
||||
+ skidthickness
|
||||
+ (metalmiddleplateheight - metalboxheight)
|
||||
+ metalboxheight,
|
||||
),
|
||||
material_id="pec",
|
||||
)
|
||||
@@ -626,7 +722,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
|
||||
# PCB
|
||||
b13 = gprMax.Box(
|
||||
p1=(x + 0.01 + 0.005 + 0.017, y + casethickness + 0.005 + 0.021, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.017,
|
||||
y + casethickness + 0.005 + 0.021,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.033 + bowtiebase,
|
||||
y + casethickness + 0.006 + 0.202 + patchheight,
|
||||
@@ -636,7 +736,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
averaging="n",
|
||||
)
|
||||
b14 = gprMax.Box(
|
||||
p1=(x + 0.01 + 0.005 + 0.179, y + casethickness + 0.005 + 0.021, z + skidthickness),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.179,
|
||||
y + casethickness + 0.005 + 0.021,
|
||||
z + skidthickness,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.195 + bowtiebase,
|
||||
y + casethickness + 0.006 + 0.202 + patchheight,
|
||||
@@ -652,7 +756,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
# "left" side
|
||||
# extension plates
|
||||
p1 = gprMax.Plate(
|
||||
p1=(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.021, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.025,
|
||||
y + casethickness + 0.005 + 0.021,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.025 + bowtiebase,
|
||||
y + casethickness + 0.005 + 0.021 + patchheight,
|
||||
@@ -661,7 +769,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
material_id="pec",
|
||||
)
|
||||
p2 = gprMax.Plate(
|
||||
p1=(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.025,
|
||||
y + casethickness + 0.005 + 0.203,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.025 + bowtiebase,
|
||||
y + casethickness + 0.005 + 0.203 + patchheight,
|
||||
@@ -671,8 +783,16 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
)
|
||||
# triangles
|
||||
t1 = gprMax.Triangle(
|
||||
p1=(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.081, z + skidthickness - 0.002),
|
||||
p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.025,
|
||||
y + casethickness + 0.005 + 0.081,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.025 + bowtiebase,
|
||||
y + casethickness + 0.005 + 0.081,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p3=(
|
||||
x + 0.01 + 0.005 + 0.025 + (bowtiebase / 2),
|
||||
y + casethickness + 0.005 + 0.081 + bowtieheight,
|
||||
@@ -682,8 +802,16 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
material_id="pec",
|
||||
)
|
||||
t2 = gprMax.Triangle(
|
||||
p1=(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness - 0.002),
|
||||
p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.025,
|
||||
y + casethickness + 0.005 + 0.203,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.025 + bowtiebase,
|
||||
y + casethickness + 0.005 + 0.203,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p3=(
|
||||
x + 0.01 + 0.005 + 0.025 + (bowtiebase / 2),
|
||||
y + casethickness + 0.005 + 0.203 - bowtieheight,
|
||||
@@ -694,7 +822,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
)
|
||||
# "right" side
|
||||
p3 = gprMax.Plate(
|
||||
p1=(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.021, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.187,
|
||||
y + casethickness + 0.005 + 0.021,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.187 + bowtiebase,
|
||||
y + casethickness + 0.005 + 0.021 + patchheight,
|
||||
@@ -703,7 +835,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
material_id="pec",
|
||||
)
|
||||
p4 = gprMax.Plate(
|
||||
p1=(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.187,
|
||||
y + casethickness + 0.005 + 0.203,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.187 + bowtiebase,
|
||||
y + casethickness + 0.005 + 0.203 + patchheight,
|
||||
@@ -713,8 +849,16 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
)
|
||||
# triangles
|
||||
t3 = gprMax.Triangle(
|
||||
p1=(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.081, z + skidthickness - 0.002),
|
||||
p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.187,
|
||||
y + casethickness + 0.005 + 0.081,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.187 + bowtiebase,
|
||||
y + casethickness + 0.005 + 0.081,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p3=(
|
||||
x + 0.01 + 0.005 + 0.187 + (bowtiebase / 2),
|
||||
y + casethickness + 0.005 + 0.081 + bowtieheight,
|
||||
@@ -724,8 +868,16 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
material_id="pec",
|
||||
)
|
||||
t4 = gprMax.Triangle(
|
||||
p1=(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness - 0.002),
|
||||
p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness - 0.002),
|
||||
p1=(
|
||||
x + 0.01 + 0.005 + 0.187,
|
||||
y + casethickness + 0.005 + 0.203,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p2=(
|
||||
x + 0.01 + 0.005 + 0.187 + bowtiebase,
|
||||
y + casethickness + 0.005 + 0.203,
|
||||
z + skidthickness - 0.002,
|
||||
),
|
||||
p3=(
|
||||
x + 0.01 + 0.005 + 0.187 + (bowtiebase / 2),
|
||||
y + casethickness + 0.005 + 0.203 - bowtieheight,
|
||||
@@ -736,12 +888,24 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
)
|
||||
|
||||
# Edges that represent wire between bowtie halves in 2mm model
|
||||
e1 = gprMax.Edge(p1=(tx[0] + 0.162, tx[1] - dy, tx[2]), p2=(tx[0] + 0.162, tx[1], tx[2]), material_id="pec")
|
||||
e2 = gprMax.Edge(
|
||||
p1=(tx[0] + 0.162, tx[1] + dy, tx[2]), p2=(tx[0] + 0.162, tx[1] + 2 * dy, tx[2]), material_id="pec"
|
||||
e1 = gprMax.Edge(
|
||||
p1=(tx[0] + 0.162, tx[1] - dy, tx[2]),
|
||||
p2=(tx[0] + 0.162, tx[1], tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
e2 = gprMax.Edge(
|
||||
p1=(tx[0] + 0.162, tx[1] + dy, tx[2]),
|
||||
p2=(tx[0] + 0.162, tx[1] + 2 * dy, tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
e3 = gprMax.Edge(
|
||||
p1=(tx[0], tx[1] - dy, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec"
|
||||
)
|
||||
e4 = gprMax.Edge(
|
||||
p1=(tx[0], tx[1] + dy, tx[2]),
|
||||
p2=(tx[0], tx[1] + 2 * dy, tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec")
|
||||
e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]), p2=(tx[0], tx[1] + 2 * dy, tx[2]), material_id="pec")
|
||||
scene_objects.extend((p1, p2, t1, t2, p3, p4, t3, t4, e1, e2, e3, e4))
|
||||
|
||||
# Metallic plate extension
|
||||
@@ -758,7 +922,9 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
# Skid
|
||||
if smooth_dec == "yes":
|
||||
b16 = gprMax.Box(
|
||||
p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + skidthickness - 0.002), material_id="hdpe"
|
||||
p1=(x, y, z),
|
||||
p2=(x + casesize[0], y + casesize[1], z + skidthickness - 0.002),
|
||||
material_id="hdpe",
|
||||
)
|
||||
elif smooth_dec == "no":
|
||||
b16 = gprMax.Box(
|
||||
@@ -771,31 +937,48 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
|
||||
# Source
|
||||
if src_type == "voltage_source":
|
||||
w1 = gprMax.Waveform(wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian")
|
||||
w1 = gprMax.Waveform(
|
||||
wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian"
|
||||
)
|
||||
vs1 = gprMax.VoltageSource(
|
||||
polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_gaussian"
|
||||
polarisation="y",
|
||||
p1=(tx[0], tx[1], tx[2]),
|
||||
resistance=sourceresistance,
|
||||
waveform_id="my_gaussian",
|
||||
)
|
||||
scene_objects.extend((w1, vs1))
|
||||
elif src_type == "transmission_line":
|
||||
w1 = gprMax.Waveform(wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian")
|
||||
w1 = gprMax.Waveform(
|
||||
wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian"
|
||||
)
|
||||
tl1 = gprMax.TransmissionLine(
|
||||
polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_gaussian"
|
||||
polarisation="y",
|
||||
p1=(tx[0], tx[1], tx[2]),
|
||||
resistance=sourceresistance,
|
||||
waveform_id="my_gaussian",
|
||||
)
|
||||
scene_objects.extend((w1, tl1))
|
||||
else:
|
||||
# Optimised custom pulse
|
||||
exc1 = gprMax.ExcitationFile(
|
||||
filepath="toolboxes/GPRAntennaModels/GSSI_400MHz_pulse.txt", kind="linear", fill_value="extrapolate"
|
||||
filepath="toolboxes/GPRAntennaModels/GSSI_400MHz_pulse.txt",
|
||||
kind="linear",
|
||||
fill_value="extrapolate",
|
||||
)
|
||||
vs1 = gprMax.VoltageSource(
|
||||
polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_pulse"
|
||||
polarisation="y",
|
||||
p1=(tx[0], tx[1], tx[2]),
|
||||
resistance=sourceresistance,
|
||||
waveform_id="my_pulse",
|
||||
)
|
||||
scene_objects.extend((exc1, vs1))
|
||||
|
||||
# Receiver
|
||||
if src_type == "transmission_line":
|
||||
# Zero waveform to use with transmission line at receiver output
|
||||
w2 = gprMax.Waveform(wave_type="gaussian", amp=0, freq=excitationfreq, id="my_zero_wave")
|
||||
w2 = gprMax.Waveform(
|
||||
wave_type="gaussian", amp=0, freq=excitationfreq, id="my_zero_wave"
|
||||
)
|
||||
tl2 = gprMax.TransmissionLine(
|
||||
polarisation="y",
|
||||
p1=(tx[0] + 0.162, tx[1], tx[2]),
|
||||
@@ -810,7 +993,11 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.002, **kwargs):
|
||||
# Geometry views
|
||||
gv1 = gprMax.GeometryView(
|
||||
p1=(x - dx, y - dy, z - dz),
|
||||
p2=(x + casesize[0] + dx, y + casesize[1] + dy, z + skidthickness + casesize[2] + dz),
|
||||
p2=(
|
||||
x + casesize[0] + dx,
|
||||
y + casesize[1] + dy,
|
||||
z + skidthickness + casesize[2] + dz,
|
||||
),
|
||||
dl=(dx, dy, dz),
|
||||
filename="antenna_like_GSSI_400",
|
||||
output_type="n",
|
||||
|
@@ -84,20 +84,30 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
bowtieheight = 0.024
|
||||
tx = x + 0.062, y + 0.052, z + skidthickness
|
||||
else:
|
||||
logger.exception("This antenna module can only be used with a spatial resolution of 1mm or 2mm")
|
||||
logger.exception(
|
||||
"This antenna module can only be used with a spatial resolution of 1mm or 2mm"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# SMD resistors - 3 on each Tx & Rx bowtie arm
|
||||
txres = 470 # Ohms
|
||||
txrescellupper = txres / 3 # Resistor over 3 cells
|
||||
txsigupper = ((1 / txrescellupper) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
|
||||
txsigupper = (
|
||||
(1 / txrescellupper) * (dy / (dx * dz))
|
||||
) / 2 # Divide by number of parallel edges per resistor
|
||||
txrescelllower = txres / 4 # Resistor over 4 cells
|
||||
txsiglower = ((1 / txrescelllower) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
|
||||
txsiglower = (
|
||||
(1 / txrescelllower) * (dy / (dx * dz))
|
||||
) / 2 # Divide by number of parallel edges per resistor
|
||||
rxres = 150 # Ohms
|
||||
rxrescellupper = rxres / 3 # Resistor over 3 cells
|
||||
rxsigupper = ((1 / rxrescellupper) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
|
||||
rxsigupper = (
|
||||
(1 / rxrescellupper) * (dy / (dx * dz))
|
||||
) / 2 # Divide by number of parallel edges per resistor
|
||||
rxrescelllower = rxres / 4 # Resistor over 4 cells
|
||||
rxsiglower = ((1 / rxrescelllower) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
|
||||
rxsiglower = (
|
||||
(1 / rxrescelllower) * (dy / (dx * dz))
|
||||
) / 2 # Divide by number of parallel edges per resistor
|
||||
|
||||
# Material definitions
|
||||
absorber = gprMax.Material(er=absorberEr, se=absorbersig, mr=1, sm=0, id="absorber")
|
||||
@@ -108,7 +118,18 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
txresupper = gprMax.Material(er=3, se=txsigupper, mr=1, sm=0, id="txresupper")
|
||||
rxreslower = gprMax.Material(er=3, se=rxsiglower, mr=1, sm=0, id="rxreslower")
|
||||
rxresupper = gprMax.Material(er=3, se=rxsigupper, mr=1, sm=0, id="rxresupper")
|
||||
scene_objects.extend((absorber, pcb, hdpe, polypropylene, txreslower, txresupper, rxreslower, rxresupper))
|
||||
scene_objects.extend(
|
||||
(
|
||||
absorber,
|
||||
pcb,
|
||||
hdpe,
|
||||
polypropylene,
|
||||
txreslower,
|
||||
txresupper,
|
||||
rxreslower,
|
||||
rxresupper,
|
||||
)
|
||||
)
|
||||
|
||||
# Antenna geometry
|
||||
# Shield - metallic enclosure
|
||||
@@ -119,19 +140,31 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
)
|
||||
b2 = gprMax.Box(
|
||||
p1=(x + 0.020, y + casethickness, z + skidthickness),
|
||||
p2=(x + 0.100, y + casesize[1] - casethickness, z + skidthickness + casethickness),
|
||||
p2=(
|
||||
x + 0.100,
|
||||
y + casesize[1] - casethickness,
|
||||
z + skidthickness + casethickness,
|
||||
),
|
||||
material_id="free_space",
|
||||
)
|
||||
b3 = gprMax.Box(
|
||||
p1=(x + 0.100, y + casethickness, z + skidthickness),
|
||||
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + casethickness),
|
||||
p2=(
|
||||
x + casesize[0] - casethickness,
|
||||
y + casesize[1] - casethickness,
|
||||
z + skidthickness + casethickness,
|
||||
),
|
||||
material_id="free_space",
|
||||
)
|
||||
|
||||
# Absorber material
|
||||
b4 = gprMax.Box(
|
||||
p1=(x + 0.020, y + casethickness, z + skidthickness),
|
||||
p2=(x + 0.100, y + casesize[1] - casethickness, z + skidthickness + casesize[2] - casethickness),
|
||||
p2=(
|
||||
x + 0.100,
|
||||
y + casesize[1] - casethickness,
|
||||
z + skidthickness + casesize[2] - casethickness,
|
||||
),
|
||||
material_id="absorber",
|
||||
)
|
||||
b5 = gprMax.Box(
|
||||
@@ -148,7 +181,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
# Shield - cylindrical sections
|
||||
c1 = gprMax.Cylinder(
|
||||
p1=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness),
|
||||
p2=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness),
|
||||
p2=(
|
||||
x + 0.055,
|
||||
y + casesize[1] - 0.008,
|
||||
z + skidthickness + casesize[2] - casethickness,
|
||||
),
|
||||
r=0.008,
|
||||
material_id="pec",
|
||||
)
|
||||
@@ -160,7 +197,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
)
|
||||
c3 = gprMax.Cylinder(
|
||||
p1=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness),
|
||||
p2=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness),
|
||||
p2=(
|
||||
x + 0.147,
|
||||
y + casesize[1] - 0.008,
|
||||
z + skidthickness + casesize[2] - casethickness,
|
||||
),
|
||||
r=0.008,
|
||||
material_id="pec",
|
||||
)
|
||||
@@ -172,7 +213,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
)
|
||||
c5 = gprMax.Cylinder(
|
||||
p1=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness),
|
||||
p2=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness),
|
||||
p2=(
|
||||
x + 0.055,
|
||||
y + casesize[1] - 0.008,
|
||||
z + skidthickness + casesize[2] - casethickness,
|
||||
),
|
||||
r=0.007,
|
||||
material_id="free_space",
|
||||
)
|
||||
@@ -184,7 +229,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
)
|
||||
c7 = gprMax.Cylinder(
|
||||
p1=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness),
|
||||
p2=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness),
|
||||
p2=(
|
||||
x + 0.147,
|
||||
y + casesize[1] - 0.008,
|
||||
z + skidthickness + casesize[2] - casethickness,
|
||||
),
|
||||
r=0.007,
|
||||
material_id="free_space",
|
||||
)
|
||||
@@ -196,7 +245,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
)
|
||||
b6 = gprMax.Box(
|
||||
p1=(x + 0.054, y + casesize[1] - 0.016, z + skidthickness),
|
||||
p2=(x + 0.056, y + casesize[1] - 0.014, z + skidthickness + casesize[2] - casethickness),
|
||||
p2=(
|
||||
x + 0.056,
|
||||
y + casesize[1] - 0.014,
|
||||
z + skidthickness + casesize[2] - casethickness,
|
||||
),
|
||||
material_id="free_space",
|
||||
)
|
||||
b7 = gprMax.Box(
|
||||
@@ -206,7 +259,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
)
|
||||
b8 = gprMax.Box(
|
||||
p1=(x + 0.146, y + casesize[1] - 0.016, z + skidthickness),
|
||||
p2=(x + 0.148, y + casesize[1] - 0.014, z + skidthickness + casesize[2] - casethickness),
|
||||
p2=(
|
||||
x + 0.148,
|
||||
y + casesize[1] - 0.014,
|
||||
z + skidthickness + casesize[2] - casethickness,
|
||||
),
|
||||
material_id="free_space",
|
||||
)
|
||||
b9 = gprMax.Box(
|
||||
@@ -219,18 +276,30 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
# PCB
|
||||
b10 = gprMax.Box(
|
||||
p1=(x + 0.020, y + 0.018, z + skidthickness),
|
||||
p2=(x + casesize[0] - casethickness, y + casesize[1] - 0.018, z + skidthickness + pcbthickness),
|
||||
p2=(
|
||||
x + casesize[0] - casethickness,
|
||||
y + casesize[1] - 0.018,
|
||||
z + skidthickness + pcbthickness,
|
||||
),
|
||||
material_id="pcb",
|
||||
)
|
||||
|
||||
# Shield - Tx & Rx cavities
|
||||
b11 = gprMax.Box(
|
||||
p1=(x + 0.032, y + 0.022, z + skidthickness),
|
||||
p2=(x + 0.032 + cavitysize[0], y + 0.022 + cavitysize[1], z + skidthickness + cavitysize[2]),
|
||||
p2=(
|
||||
x + 0.032 + cavitysize[0],
|
||||
y + 0.022 + cavitysize[1],
|
||||
z + skidthickness + cavitysize[2],
|
||||
),
|
||||
material_id="pec",
|
||||
)
|
||||
b12 = gprMax.Box(
|
||||
p1=(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness),
|
||||
p1=(
|
||||
x + 0.032 + cavitythickness,
|
||||
y + 0.022 + cavitythickness,
|
||||
z + skidthickness,
|
||||
),
|
||||
p2=(
|
||||
x + 0.032 + cavitysize[0] - cavitythickness,
|
||||
y + 0.022 + cavitysize[1] - cavitythickness,
|
||||
@@ -240,11 +309,19 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
)
|
||||
b13 = gprMax.Box(
|
||||
p1=(x + 0.108, y + 0.022, z + skidthickness),
|
||||
p2=(x + 0.108 + cavitysize[0], y + 0.022 + cavitysize[1], z + skidthickness + cavitysize[2]),
|
||||
p2=(
|
||||
x + 0.108 + cavitysize[0],
|
||||
y + 0.022 + cavitysize[1],
|
||||
z + skidthickness + cavitysize[2],
|
||||
),
|
||||
material_id="pec",
|
||||
)
|
||||
b14 = gprMax.Box(
|
||||
p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness),
|
||||
p1=(
|
||||
x + 0.108 + cavitythickness,
|
||||
y + 0.022 + cavitythickness,
|
||||
z + skidthickness,
|
||||
),
|
||||
p2=(
|
||||
x + 0.108 + cavitysize[0] - cavitythickness,
|
||||
y + 0.022 + cavitysize[1] - cavitythickness,
|
||||
@@ -264,14 +341,22 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
material_id="pec",
|
||||
)
|
||||
b16 = gprMax.Box(
|
||||
p1=(x + 0.032 + cavitysize[0], y + 0.022, z + skidthickness + cavitysize[2] - casethickness),
|
||||
p1=(
|
||||
x + 0.032 + cavitysize[0],
|
||||
y + 0.022,
|
||||
z + skidthickness + cavitysize[2] - casethickness,
|
||||
),
|
||||
p2=(x + 0.108, y + 0.022 + 0.006, z + skidthickness + cavitysize[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
|
||||
# PCB - replace bits chopped by TX & Rx cavities
|
||||
b17 = gprMax.Box(
|
||||
p1=(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness),
|
||||
p1=(
|
||||
x + 0.032 + cavitythickness,
|
||||
y + 0.022 + cavitythickness,
|
||||
z + skidthickness,
|
||||
),
|
||||
p2=(
|
||||
x + 0.032 + cavitysize[0] - cavitythickness,
|
||||
y + 0.022 + cavitysize[1] - cavitythickness,
|
||||
@@ -280,7 +365,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
material_id="pcb",
|
||||
)
|
||||
b18 = gprMax.Box(
|
||||
p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness),
|
||||
p1=(
|
||||
x + 0.108 + cavitythickness,
|
||||
y + 0.022 + cavitythickness,
|
||||
z + skidthickness,
|
||||
),
|
||||
p2=(
|
||||
x + 0.108 + cavitysize[0] - cavitythickness,
|
||||
y + 0.022 + cavitysize[1] - cavitythickness,
|
||||
@@ -300,7 +389,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
thickness=0,
|
||||
material_id="pec",
|
||||
)
|
||||
e1 = gprMax.Edge(p1=(tx[0], tx[1] - 0.001, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec")
|
||||
e1 = gprMax.Edge(
|
||||
p1=(tx[0], tx[1] - 0.001, tx[2]),
|
||||
p2=(tx[0], tx[1], tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
t2 = gprMax.Triangle(
|
||||
p1=(tx[0], tx[1] + 0.002, tx[2]),
|
||||
p2=(tx[0] - 0.026, tx[1] + bowtieheight + 0.002, tx[2]),
|
||||
@@ -308,7 +401,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
thickness=0,
|
||||
material_id="pec",
|
||||
)
|
||||
e2 = gprMax.Edge(p1=(tx[0], tx[1] + 0.001, tx[2]), p2=(tx[0], tx[1] + 0.002, tx[2]), material_id="pec")
|
||||
e2 = gprMax.Edge(
|
||||
p1=(tx[0], tx[1] + 0.001, tx[2]),
|
||||
p2=(tx[0], tx[1] + 0.002, tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
scene_objects.extend((t1, t2, e1, e2))
|
||||
elif resolution == 0.002:
|
||||
t1 = gprMax.Triangle(
|
||||
@@ -336,7 +433,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
thickness=0,
|
||||
material_id="pec",
|
||||
)
|
||||
e3 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] - 0.001, tx[2]), p2=(tx[0] + 0.076, tx[1], tx[2]), material_id="pec")
|
||||
e3 = gprMax.Edge(
|
||||
p1=(tx[0] + 0.076, tx[1] - 0.001, tx[2]),
|
||||
p2=(tx[0] + 0.076, tx[1], tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
t4 = gprMax.Triangle(
|
||||
p1=(tx[0] + 0.076, tx[1] + 0.002, tx[2]),
|
||||
p2=(tx[0] + 0.076 - 0.026, tx[1] + bowtieheight + 0.002, tx[2]),
|
||||
@@ -345,7 +446,9 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
material_id="pec",
|
||||
)
|
||||
e4 = gprMax.Edge(
|
||||
p1=(tx[0] + 0.076, tx[1] + 0.001, tx[2]), p2=(tx[0] + 0.076, tx[1] + 0.002, tx[2]), material_id="pec"
|
||||
p1=(tx[0] + 0.076, tx[1] + 0.001, tx[2]),
|
||||
p2=(tx[0] + 0.076, tx[1] + 0.002, tx[2]),
|
||||
material_id="pec",
|
||||
)
|
||||
scene_objects.extend((t3, e3, t4, e4))
|
||||
elif resolution == 0.002:
|
||||
@@ -631,20 +734,31 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
|
||||
# Skid
|
||||
b19 = gprMax.Box(
|
||||
p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + polypropylenethickness), material_id="polypropylene"
|
||||
p1=(x, y, z),
|
||||
p2=(x + casesize[0], y + casesize[1], z + polypropylenethickness),
|
||||
material_id="polypropylene",
|
||||
)
|
||||
b20 = gprMax.Box(
|
||||
p1=(x, y, z + polypropylenethickness),
|
||||
p2=(x + casesize[0], y + casesize[1], z + polypropylenethickness + hdpethickness),
|
||||
p2=(
|
||||
x + casesize[0],
|
||||
y + casesize[1],
|
||||
z + polypropylenethickness + hdpethickness,
|
||||
),
|
||||
material_id="hdpe",
|
||||
)
|
||||
scene_objects.extend((b19, b20))
|
||||
|
||||
# Excitation
|
||||
w2 = gprMax.Waveform(wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian")
|
||||
w2 = gprMax.Waveform(
|
||||
wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian"
|
||||
)
|
||||
scene_objects.append(w2)
|
||||
vs1 = gprMax.VoltageSource(
|
||||
polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_gaussian"
|
||||
polarisation="y",
|
||||
p1=(tx[0], tx[1], tx[2]),
|
||||
resistance=sourceresistance,
|
||||
waveform_id="my_gaussian",
|
||||
)
|
||||
scene_objects.append(vs1)
|
||||
|
||||
@@ -655,7 +769,11 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
|
||||
# Geometry views
|
||||
gv1 = gprMax.GeometryView(
|
||||
p1=(x - dx, y - dy, z - dz),
|
||||
p2=(x + casesize[0] + dx, y + casesize[1] + dy, z + skidthickness + casesize[2] + dz),
|
||||
p2=(
|
||||
x + casesize[0] + dx,
|
||||
y + casesize[1] + dy,
|
||||
z + skidthickness + casesize[2] + dz,
|
||||
),
|
||||
dl=(dx, dy, dz),
|
||||
filename="antenna_like_MALA_1200",
|
||||
output_type="n",
|
||||
|
@@ -81,7 +81,9 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
|
||||
|
||||
# Check for single output component when doing a FFT
|
||||
if fft and not len(outputs) == 1:
|
||||
logger.exception("A single output must be specified when using " + "the -fft option")
|
||||
logger.exception(
|
||||
"A single output must be specified when using " + "the -fft option"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# New plot for each receiver
|
||||
@@ -120,7 +122,11 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
|
||||
# Set plotting range to -60dB from maximum power or 4 times
|
||||
# frequency at maximum power
|
||||
try:
|
||||
pltrange = np.where(power[freqmaxpower:] < -60)[0][0] + freqmaxpower + 1
|
||||
pltrange = (
|
||||
np.where(power[freqmaxpower:] < -60)[0][0]
|
||||
+ freqmaxpower
|
||||
+ 1
|
||||
)
|
||||
except:
|
||||
pltrange = freqmaxpower * 4
|
||||
|
||||
@@ -160,20 +166,27 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
|
||||
plt.setp(line2, color="g")
|
||||
plt.setp(ax1, ylabel=outputtext + " field strength [A/m]")
|
||||
plt.setp(stemlines, "color", "g")
|
||||
plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g")
|
||||
plt.setp(
|
||||
markerline, "markerfacecolor", "g", "markeredgecolor", "g"
|
||||
)
|
||||
elif "I" in outputs[0]:
|
||||
plt.setp(line1, color="b")
|
||||
plt.setp(line2, color="b")
|
||||
plt.setp(ax1, ylabel=outputtext + " current [A]")
|
||||
plt.setp(stemlines, "color", "b")
|
||||
plt.setp(markerline, "markerfacecolor", "b", "markeredgecolor", "b")
|
||||
plt.setp(
|
||||
markerline, "markerfacecolor", "b", "markeredgecolor", "b"
|
||||
)
|
||||
|
||||
plt.show()
|
||||
|
||||
# Plotting if no FFT required
|
||||
else:
|
||||
fig, ax = plt.subplots(
|
||||
subplot_kw=dict(xlabel="Time [s]", ylabel=outputtext + " field strength [V/m]"),
|
||||
subplot_kw=dict(
|
||||
xlabel="Time [s]",
|
||||
ylabel=outputtext + " field strength [V/m]",
|
||||
),
|
||||
num=rxpath + " - " + f[rxpath].attrs["Name"],
|
||||
figsize=(20, 10),
|
||||
facecolor="w",
|
||||
@@ -279,7 +292,13 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
|
||||
|
||||
if save:
|
||||
# Save a PDF of the figure
|
||||
fig.savefig(filename[:-3] + ".pdf", dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1)
|
||||
fig.savefig(
|
||||
filename[:-3] + ".pdf",
|
||||
dpi=None,
|
||||
format="pdf",
|
||||
bbox_inches="tight",
|
||||
pad_inches=0.1,
|
||||
)
|
||||
# Save a PNG of the figure
|
||||
# fig.savefig(filename[:-3] + '.png', dpi=150, format='png',
|
||||
# bbox_inches='tight', pad_inches=0.1)
|
||||
@@ -322,9 +341,17 @@ if __name__ == "__main__":
|
||||
],
|
||||
nargs="+",
|
||||
)
|
||||
parser.add_argument("-fft", action="store_true", default=False, help="plot FFT (single output must be specified)")
|
||||
parser.add_argument(
|
||||
"-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display"
|
||||
"-fft",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="plot FFT (single output must be specified)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-save",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="save plot directly to file, i.e. do not display",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@@ -46,7 +46,12 @@ def mpl_plot(filename, outputdata, dt, rxnumber, rxcomponent, save=False):
|
||||
|
||||
file = Path(filename)
|
||||
|
||||
fig = plt.figure(num=file.stem + " - rx" + str(rxnumber), figsize=(20, 10), facecolor="w", edgecolor="w")
|
||||
fig = plt.figure(
|
||||
num=file.stem + " - rx" + str(rxnumber),
|
||||
figsize=(20, 10),
|
||||
facecolor="w",
|
||||
edgecolor="w",
|
||||
)
|
||||
plt.imshow(
|
||||
outputdata,
|
||||
extent=[0, outputdata.shape[1], outputdata.shape[0] * dt, 0],
|
||||
@@ -73,7 +78,13 @@ def mpl_plot(filename, outputdata, dt, rxnumber, rxcomponent, save=False):
|
||||
|
||||
if save:
|
||||
# Save a PDF of the figure
|
||||
fig.savefig(filename[:-3] + ".pdf", dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1)
|
||||
fig.savefig(
|
||||
filename[:-3] + ".pdf",
|
||||
dpi=None,
|
||||
format="pdf",
|
||||
bbox_inches="tight",
|
||||
pad_inches=0.1,
|
||||
)
|
||||
# Save a PNG of the figure
|
||||
# fig.savefig(filename[:-3] + '.png', dpi=150, format='png',
|
||||
# bbox_inches='tight', pad_inches=0.1)
|
||||
@@ -94,10 +105,16 @@ if __name__ == "__main__":
|
||||
choices=["Ex", "Ey", "Ez", "Hx", "Hy", "Hz", "Ix", "Iy", "Iz"],
|
||||
)
|
||||
parser.add_argument(
|
||||
"-gather", action="store_true", default=False, help="gather together all receiver outputs in file"
|
||||
"-gather",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="gather together all receiver outputs in file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display"
|
||||
"-save",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="save plot directly to file, i.e. do not display",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -118,10 +135,14 @@ if __name__ == "__main__":
|
||||
rxsgather = outputdata
|
||||
rxsgather = np.column_stack((rxsgather, outputdata))
|
||||
else:
|
||||
plthandle = mpl_plot(args.outputfile, outputdata, dt, rx, args.rx_component, save=args.save)
|
||||
plthandle = mpl_plot(
|
||||
args.outputfile, outputdata, dt, rx, args.rx_component, save=args.save
|
||||
)
|
||||
|
||||
# Plot all receivers from single output file together if required
|
||||
if args.gather:
|
||||
plthandle = mpl_plot(args.outputfile, rxsgather, dt, rx, args.rx_component, save=args.save)
|
||||
plthandle = mpl_plot(
|
||||
args.outputfile, rxsgather, dt, rx, args.rx_component, save=args.save
|
||||
)
|
||||
|
||||
plthandle.show()
|
||||
|
@@ -28,7 +28,9 @@ import numpy as np
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=None, rxcomponent=None):
|
||||
def calculate_antenna_params(
|
||||
filename, tltxnumber=1, tlrxnumber=None, rxnumber=None, rxcomponent=None
|
||||
):
|
||||
"""Calculates antenna parameters - incident, reflected and total volatges
|
||||
and currents; s11, (s21) and input impedance.
|
||||
|
||||
@@ -160,7 +162,9 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N
|
||||
"yin": yin,
|
||||
}
|
||||
if tlrxnumber or rxnumber:
|
||||
with np.errstate(divide="ignore"): # Ignore warning from taking a log of any zero values
|
||||
with np.errstate(
|
||||
divide="ignore"
|
||||
): # Ignore warning from taking a log of any zero values
|
||||
s21 = 20 * np.log10(s21)
|
||||
s21[np.invert(np.isfinite(s21))] = 0
|
||||
antennaparams["s21"] = s21
|
||||
@@ -224,7 +228,10 @@ def mpl_plot(
|
||||
|
||||
# Print some useful values from s11, and input impedance
|
||||
s11minfreq = np.where(s11[pltrange] == np.amin(s11[pltrange]))[0][0]
|
||||
logger.info(f"s11 minimum: {np.amin(s11[pltrange]):g} dB at " + f"{freqs[s11minfreq + pltrangemin]:g} Hz")
|
||||
logger.info(
|
||||
f"s11 minimum: {np.amin(s11[pltrange]):g} dB at "
|
||||
+ f"{freqs[s11minfreq + pltrangemin]:g} Hz"
|
||||
)
|
||||
logger.info(f"At {freqs[s11minfreq + pltrangemin]:g} Hz...")
|
||||
logger.info(
|
||||
f"Input impedance: {np.abs(zin[s11minfreq + pltrangemin]):.1f}"
|
||||
@@ -236,7 +243,10 @@ def mpl_plot(
|
||||
# Figure 1
|
||||
# Plot incident voltage
|
||||
fig1, ax = plt.subplots(
|
||||
num="Transmitter transmission line parameters", figsize=(20, 12), facecolor="w", edgecolor="w"
|
||||
num="Transmitter transmission line parameters",
|
||||
figsize=(20, 12),
|
||||
facecolor="w",
|
||||
edgecolor="w",
|
||||
)
|
||||
gs1 = gridspec.GridSpec(4, 2, hspace=0.7)
|
||||
ax = plt.subplot(gs1[0, 0])
|
||||
@@ -368,7 +378,9 @@ def mpl_plot(
|
||||
|
||||
# Figure 2
|
||||
# Plot frequency spectra of s11
|
||||
fig2, ax = plt.subplots(num="Antenna parameters", figsize=(20, 12), facecolor="w", edgecolor="w")
|
||||
fig2, ax = plt.subplots(
|
||||
num="Antenna parameters", figsize=(20, 12), facecolor="w", edgecolor="w"
|
||||
)
|
||||
gs2 = gridspec.GridSpec(2, 2, hspace=0.3)
|
||||
ax = plt.subplot(gs2[0, 0])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], s11[pltrange], "-.")
|
||||
@@ -463,8 +475,20 @@ def mpl_plot(
|
||||
savename2 = filename.stem + "_ant_params"
|
||||
savename2 = filename.parent / savename2
|
||||
# Save a PDF of the figure
|
||||
fig1.savefig(savename1.with_suffix(".pdf"), dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1)
|
||||
fig2.savefig(savename2.with_suffix(".pdf"), dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1)
|
||||
fig1.savefig(
|
||||
savename1.with_suffix(".pdf"),
|
||||
dpi=None,
|
||||
format="pdf",
|
||||
bbox_inches="tight",
|
||||
pad_inches=0.1,
|
||||
)
|
||||
fig2.savefig(
|
||||
savename2.with_suffix(".pdf"),
|
||||
dpi=None,
|
||||
format="pdf",
|
||||
bbox_inches="tight",
|
||||
pad_inches=0.1,
|
||||
)
|
||||
# Save a PNG of the figure
|
||||
# fig1.savefig(savename1.with_suffix('.png'), dpi=150, format='png',
|
||||
# bbox_inches='tight', pad_inches=0.1)
|
||||
@@ -485,8 +509,15 @@ if __name__ == "__main__":
|
||||
usage="cd gprMax; python -m toolboxes.Plotting.plot_antenna_params outputfile",
|
||||
)
|
||||
parser.add_argument("outputfile", help="name of output file including path")
|
||||
parser.add_argument("--tltx-num", default=1, type=int, help="transmitter antenna - transmission line number")
|
||||
parser.add_argument("--tlrx-num", type=int, help="receiver antenna - transmission line number")
|
||||
parser.add_argument(
|
||||
"--tltx-num",
|
||||
default=1,
|
||||
type=int,
|
||||
help="transmitter antenna - transmission line number",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tlrx-num", type=int, help="receiver antenna - transmission line number"
|
||||
)
|
||||
parser.add_argument("--rx-num", type=int, help="receiver antenna - output number")
|
||||
parser.add_argument(
|
||||
"--rx-component",
|
||||
@@ -495,7 +526,10 @@ if __name__ == "__main__":
|
||||
choices=["Ex", "Ey", "Ez"],
|
||||
)
|
||||
parser.add_argument(
|
||||
"-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display"
|
||||
"-save",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="save plot directly to file, i.e. do not display",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@@ -90,7 +90,13 @@ def mpl_plot(w, timewindow, dt, iterations, fft=False, save=False):
|
||||
if w.freq and w.type != "gaussian" and w.type != "impulse":
|
||||
logging.info(f"Centre frequency: {w.freq:g} Hz")
|
||||
|
||||
if w.type in ["gaussian", "gaussiandot", "gaussiandotnorm", "gaussianprime", "gaussiandoubleprime"]:
|
||||
if w.type in [
|
||||
"gaussian",
|
||||
"gaussiandot",
|
||||
"gaussiandotnorm",
|
||||
"gaussianprime",
|
||||
"gaussiandoubleprime",
|
||||
]:
|
||||
delay = 1 / w.freq
|
||||
logging.info(f"Time to centre of pulse: {delay:g} s")
|
||||
elif w.type in ["gaussiandotdot", "gaussiandotdotnorm", "ricker"]:
|
||||
@@ -113,7 +119,9 @@ def mpl_plot(w, timewindow, dt, iterations, fft=False, save=False):
|
||||
pltrange = np.where(freqs > 4 * w.freq)[0][0]
|
||||
pltrange = np.s_[0:pltrange]
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, num=w.type, figsize=(20, 10), facecolor="w", edgecolor="w")
|
||||
fig, (ax1, ax2) = plt.subplots(
|
||||
nrows=1, ncols=2, num=w.type, figsize=(20, 10), facecolor="w", edgecolor="w"
|
||||
)
|
||||
|
||||
# Plot waveform
|
||||
ax1.plot(time, waveform, "r", lw=2)
|
||||
@@ -121,7 +129,9 @@ def mpl_plot(w, timewindow, dt, iterations, fft=False, save=False):
|
||||
ax1.set_ylabel("Amplitude")
|
||||
|
||||
# Plot frequency spectra
|
||||
markerline, stemlines, baseline = ax2.stem(freqs[pltrange], power[pltrange], "-.")
|
||||
markerline, stemlines, baseline = ax2.stem(
|
||||
freqs[pltrange], power[pltrange], "-."
|
||||
)
|
||||
plt.setp(baseline, "linewidth", 0)
|
||||
plt.setp(stemlines, "color", "r")
|
||||
plt.setp(markerline, "markerfacecolor", "r", "markeredgecolor", "r")
|
||||
@@ -130,7 +140,9 @@ def mpl_plot(w, timewindow, dt, iterations, fft=False, save=False):
|
||||
ax2.set_ylabel("Power [dB]")
|
||||
|
||||
else:
|
||||
fig, ax1 = plt.subplots(num=w.type, figsize=(10, 10), facecolor="w", edgecolor="w")
|
||||
fig, ax1 = plt.subplots(
|
||||
num=w.type, figsize=(10, 10), facecolor="w", edgecolor="w"
|
||||
)
|
||||
|
||||
# Plot waveform
|
||||
ax1.plot(time, waveform, "r", lw=2)
|
||||
@@ -143,9 +155,21 @@ def mpl_plot(w, timewindow, dt, iterations, fft=False, save=False):
|
||||
if save:
|
||||
savefile = Path(__file__).parent / w.type
|
||||
# Save a PDF of the figure
|
||||
fig.savefig(savefile.with_suffix(".pdf"), dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1)
|
||||
fig.savefig(
|
||||
savefile.with_suffix(".pdf"),
|
||||
dpi=None,
|
||||
format="pdf",
|
||||
bbox_inches="tight",
|
||||
pad_inches=0.1,
|
||||
)
|
||||
# Save a PNG of the figure
|
||||
fig.savefig(savefile.with_suffix(".png"), dpi=150, format="png", bbox_inches="tight", pad_inches=0.1)
|
||||
fig.savefig(
|
||||
savefile.with_suffix(".png"),
|
||||
dpi=150,
|
||||
format="png",
|
||||
bbox_inches="tight",
|
||||
pad_inches=0.1,
|
||||
)
|
||||
|
||||
return plt
|
||||
|
||||
@@ -161,18 +185,27 @@ if __name__ == "__main__":
|
||||
parser.add_argument("freq", type=float, help="centre frequency of waveform")
|
||||
parser.add_argument("timewindow", help="time window to view waveform")
|
||||
parser.add_argument("dt", type=float, help="time step to view waveform")
|
||||
parser.add_argument("-fft", action="store_true", default=False, help="plot FFT of waveform")
|
||||
parser.add_argument(
|
||||
"-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display"
|
||||
"-fft", action="store_true", default=False, help="plot FFT of waveform"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-save",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="save plot directly to file, i.e. do not display",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Check waveform parameters
|
||||
if args.type.lower() not in Waveform.types:
|
||||
logging.exception(f"The waveform must have one of the following types {', '.join(Waveform.types)}")
|
||||
logging.exception(
|
||||
f"The waveform must have one of the following types {', '.join(Waveform.types)}"
|
||||
)
|
||||
raise ValueError
|
||||
if args.freq <= 0:
|
||||
logging.exception("The waveform requires an excitation frequency value of greater than zero")
|
||||
logging.exception(
|
||||
"The waveform requires an excitation frequency value of greater than zero"
|
||||
)
|
||||
raise ValueError
|
||||
|
||||
# Create waveform instance
|
||||
@@ -182,5 +215,7 @@ if __name__ == "__main__":
|
||||
w.freq = args.freq
|
||||
|
||||
timewindow, iterations = check_timewindow(args.timewindow, args.dt)
|
||||
plthandle = mpl_plot(w, timewindow, args.dt, iterations, fft=args.fft, save=args.save)
|
||||
plthandle = mpl_plot(
|
||||
w, timewindow, args.dt, iterations, fft=args.fft, save=args.save
|
||||
)
|
||||
plthandle.show()
|
||||
|
@@ -21,12 +21,20 @@ def convert_file(input_file_path, discretization, pad=1, parallel=False):
|
||||
return convert_files([input_file_path], discretization, pad=pad, parallel=parallel)
|
||||
|
||||
|
||||
def convert_files(input_file_paths, discretization, colors=[(0, 0, 0)], pad=1, parallel=False):
|
||||
def convert_files(
|
||||
input_file_paths, discretization, colors=[(0, 0, 0)], pad=1, parallel=False
|
||||
):
|
||||
meshes = []
|
||||
|
||||
for input_file_path in input_file_paths:
|
||||
mesh_obj = mesh.Mesh.from_file(input_file_path)
|
||||
org_mesh = np.hstack((mesh_obj.v0[:, np.newaxis], mesh_obj.v1[:, np.newaxis], mesh_obj.v2[:, np.newaxis]))
|
||||
org_mesh = np.hstack(
|
||||
(
|
||||
mesh_obj.v0[:, np.newaxis],
|
||||
mesh_obj.v1[:, np.newaxis],
|
||||
mesh_obj.v2[:, np.newaxis],
|
||||
)
|
||||
)
|
||||
meshes.append(org_mesh)
|
||||
vol, scale, shift = convert_meshes(meshes, discretization, parallel)
|
||||
vol = np.transpose(vol)
|
||||
|
@@ -6,7 +6,9 @@ def lines_to_voxels(line_list, pixels):
|
||||
x = 0
|
||||
for event_x, status, line_ind in generate_line_events(line_list):
|
||||
while event_x - x >= 0:
|
||||
lines = reduce(lambda acc, cur: acc + [line_list[cur]], current_line_indices, [])
|
||||
lines = reduce(
|
||||
lambda acc, cur: acc + [line_list[cur]], current_line_indices, []
|
||||
)
|
||||
paint_y_axis(lines, pixels, x)
|
||||
x += 1
|
||||
|
||||
|
@@ -19,7 +19,12 @@ def mesh_to_plane(mesh, bounding_box, parallel):
|
||||
|
||||
current_mesh_indices = set()
|
||||
z = 0
|
||||
with tqdm(total=bounding_box[2], desc="Processing Layers", ncols=get_terminal_width() - 1, file=sys.stdout) as pbar:
|
||||
with tqdm(
|
||||
total=bounding_box[2],
|
||||
desc="Processing Layers",
|
||||
ncols=get_terminal_width() - 1,
|
||||
file=sys.stdout,
|
||||
) as pbar:
|
||||
for event_z, status, tri_ind in generate_tri_events(mesh):
|
||||
while event_z - z >= 0:
|
||||
mesh_subset = [mesh[ind] for ind in current_mesh_indices]
|
||||
|
@@ -17,9 +17,15 @@ if __name__ == "__main__":
|
||||
usage="cd gprMax; python -m toolboxes.STLtoVoxel.stltovoxel stlfilename -matindex -dxdydz",
|
||||
)
|
||||
parser.add_argument(
|
||||
"stlfiles", help="can be the filename of a single STL file, or the path to folder containing multiple STL files"
|
||||
"stlfiles",
|
||||
help="can be the filename of a single STL file, or the path to folder containing multiple STL files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-dxdydz",
|
||||
type=float,
|
||||
required=True,
|
||||
help="discretisation to use in voxelisation process",
|
||||
)
|
||||
parser.add_argument("-dxdydz", type=float, required=True, help="discretisation to use in voxelisation process")
|
||||
args = parser.parse_args()
|
||||
|
||||
if os.path.isdir(args.stlfiles):
|
||||
@@ -38,7 +44,9 @@ if __name__ == "__main__":
|
||||
newline = "\n\t"
|
||||
logger.info(f"\nConverting STL file(s): {newline.join(files)}")
|
||||
model_array = convert_files(files, dxdydz)
|
||||
logger.info(f"Number of voxels: {model_array.shape[0]} x {model_array.shape[1]} x {model_array.shape[2]}")
|
||||
logger.info(
|
||||
f"Number of voxels: {model_array.shape[0]} x {model_array.shape[1]} x {model_array.shape[2]}"
|
||||
)
|
||||
logger.info(f"Spatial discretisation: {dxdydz[0]} x {dxdydz[1]} x {dxdydz[2]}m")
|
||||
|
||||
# Write HDF5 file for gprMax using voxels
|
||||
|
@@ -55,7 +55,9 @@ class Cursor(object):
|
||||
) # Convert pixel values from float (0-1) to integer (0-255)
|
||||
match = pixel_match(materials, pixel)
|
||||
if match is False:
|
||||
logger.info(f"x, y: {int(x)} {int(y)} px; RGB: {pixel[:-1]}; material ID: {len(self.materials)}")
|
||||
logger.info(
|
||||
f"x, y: {int(x)} {int(y)} px; RGB: {pixel[:-1]}; material ID: {len(self.materials)}"
|
||||
)
|
||||
materials.append(pixel)
|
||||
|
||||
|
||||
@@ -85,10 +87,17 @@ if __name__ == "__main__":
|
||||
)
|
||||
parser.add_argument("imagefile", help="name of image file including path")
|
||||
parser.add_argument(
|
||||
"dxdydz", type=float, action="append", nargs=3, help="spatial resolution of model, e.g. dx dy dz"
|
||||
"dxdydz",
|
||||
type=float,
|
||||
action="append",
|
||||
nargs=3,
|
||||
help="spatial resolution of model, e.g. dx dy dz",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-zcells", default=1, type=int, help="number of cells for domain in z-direction (infinite direction)"
|
||||
"-zcells",
|
||||
default=1,
|
||||
type=int,
|
||||
help="number of cells for domain in z-direction (infinite direction)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -97,7 +106,9 @@ if __name__ == "__main__":
|
||||
|
||||
# Store image data to use for creating geometry
|
||||
imdata = np.rot90(im, k=3) # Rotate 90CW
|
||||
imdata = np.floor(imdata * 255).astype(np.int16) # Convert pixel values from float (0-1) to integer (0-255)
|
||||
imdata = np.floor(imdata * 255).astype(
|
||||
np.int16
|
||||
) # Convert pixel values from float (0-1) to integer (0-255)
|
||||
|
||||
logger.info(f"Reading PNG image file: {os.path.split(args.imagefile)[1]}")
|
||||
logger.info(
|
||||
|
@@ -33,7 +33,11 @@ logging.basicConfig(format="%(message)s", level=logging.INFO)
|
||||
|
||||
# Host machine info.
|
||||
hostinfo = get_host_info()
|
||||
hyperthreadingstr = f", {hostinfo['logicalcores']} cores with Hyper-Threading" if hostinfo["hyperthreading"] else ""
|
||||
hyperthreadingstr = (
|
||||
f", {hostinfo['logicalcores']} cores with Hyper-Threading"
|
||||
if hostinfo["hyperthreading"]
|
||||
else ""
|
||||
)
|
||||
hostname = f"\n=== {hostinfo['hostname']}"
|
||||
logging.info(f"{hostname} {'=' * (get_terminal_width() - len(hostname) - 1)}")
|
||||
logging.info(f"\n{'Mfr/model:':<12} {hostinfo['machineID']}")
|
||||
@@ -46,7 +50,8 @@ logging.info(f"{'OS/Version:':<12} {hostinfo['osversion']}")
|
||||
|
||||
# OpenMP
|
||||
logging.info(
|
||||
"\n\n=== OpenMP capabilities (gprMax will not use Hyper-Threading " + "as there is no performance advantage)\n"
|
||||
"\n\n=== OpenMP capabilities (gprMax will not use Hyper-Threading "
|
||||
+ "as there is no performance advantage)\n"
|
||||
)
|
||||
logging.info(f"{'OpenMP threads: '} {hostinfo['physicalcores']}")
|
||||
|
||||
|
@@ -107,7 +107,9 @@ def merge_files(outputfiles, removefiles=False):
|
||||
availableoutputs = list(fin[path].keys())
|
||||
for output in availableoutputs:
|
||||
grp.create_dataset(
|
||||
output, (fout.attrs["Iterations"], len(outputfiles)), dtype=fin[path + "/" + output].dtype
|
||||
output,
|
||||
(fout.attrs["Iterations"], len(outputfiles)),
|
||||
dtype=fin[path + "/" + output].dtype,
|
||||
)
|
||||
|
||||
# For all receivers
|
||||
@@ -134,9 +136,14 @@ if __name__ == "__main__":
|
||||
+ "optionally removes the series of output files.",
|
||||
usage="cd gprMax; python -m tools.outputfiles_merge basefilename",
|
||||
)
|
||||
parser.add_argument("basefilename", help="base name of output file series including path")
|
||||
parser.add_argument(
|
||||
"--remove-files", action="store_true", default=False, help="flag to remove individual output files after merge"
|
||||
"basefilename", help="base name of output file series including path"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--remove-files",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="flag to remove individual output files after merge",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
|
在新工单中引用
屏蔽一个用户