你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-07 15:10:13 +08:00
Merge branch 'devel' of https://github.com/gprMax/gprMax into beta_1
这个提交包含在:
二进制
examples/antenna_like_GSSI_1500.vti
普通文件
二进制
examples/antenna_like_GSSI_1500.vti
普通文件
二进制文件未显示。
二进制文件未显示。
@@ -41,7 +41,7 @@ gv2 = gprMax.GeometryView(p1=(ant_pos[0] - 0.170/2, ant_pos[1] - 0.108/2, ant_po
|
||||
p2=(ant_pos[0] + 0.170/2, ant_pos[1] + 0.108/2, ant_pos[2] + 0.010),
|
||||
dl=(dl, dl, dl), filename='antenna_like_GSSI_1500_pcb',
|
||||
output_type='f')
|
||||
#scene.add(gv1)
|
||||
scene.add(gv1)
|
||||
#scene.add(gv2)
|
||||
|
||||
# Run model
|
||||
|
二进制
examples/heterogeneous_soil_v2.h5
普通文件
二进制
examples/heterogeneous_soil_v2.h5
普通文件
二进制文件未显示。
@@ -0,0 +1,34 @@
|
||||
#title: Heterogeneous soil using a stochastic distribution of dielectric properties given by a mixing model from Peplinski
|
||||
#domain: 0.15 0.15 0.15
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 1e-9
|
||||
|
||||
#waveform: ricker 1 1.5e9 my_ricker
|
||||
#hertzian_dipole: z 0.045 0.075 0.07 my_ricker
|
||||
#rx: 0.105 0.075 0.07
|
||||
|
||||
#material: 3 0 1 0 sand
|
||||
#material: 4 0.1 1 0 sand2
|
||||
#material: 6 0.001 1 0 asphalt
|
||||
#material: 4.9 0.001 1 0 my_water
|
||||
#add_dispersion_debye: 1 75.2 9.231e-12 my_water
|
||||
|
||||
ellipsoid: 0.045 0.045 0.045 0.03 0.03 0.03 sand
|
||||
sphere: 0.075 0.075 0.075 0.03 sand2
|
||||
box: 0.029 0.029 0.029 0.03 0.03 0.03 sand
|
||||
box: 0.08 0.08 0.08 0.081 0.081 0.081 sand
|
||||
cylinder: 0.03 0.03 0.03 0.08 0.08 0.08 0.03 sand
|
||||
cone: 0.02 0.075 0.075 0.08 0.075 0.075 0.03 0.0 sand
|
||||
cone: 0.075 0.02 0.075 0.075 0.08 0.075 0.03 0.0 sand
|
||||
cone: 0.075 0.075 0.02 0.075 0.075 0.08 0.03 0.0 sand
|
||||
cone: 0.03 0.03 0.03 0.08 0.08 0.08 0.028 0.005 free_space
|
||||
|
||||
|
||||
|
||||
#material_list: sand pec sand2 asphalt my_water my_list
|
||||
#material_range: 2 6 0.1 0.001 1.0 1.0 0.0 0.0 test
|
||||
soil_peplinski: 0.5 0.5 2.0 2.66 0.001 0.25 my_soil
|
||||
#fractal_box: 0 0 0 0.15 0.15 0.1 1.5 1 1 1 5 test my_soil_box
|
||||
add_surface_roughness: 0 0 0.070 0.15 0.15 0.070 1.5 1 1 0.065 0.080 my_soil_box
|
||||
|
||||
#geometry_view: 0 0 0 0.15 0.15 0.15 0.001 0.001 0.001 heterogeneous_soil_v2 n
|
二进制
examples/heterogeneous_soil_v2.vti
普通文件
二进制
examples/heterogeneous_soil_v2.vti
普通文件
二进制文件未显示。
@@ -26,7 +26,7 @@ from .cmds_multiuse import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion,
|
||||
AddLorentzDispersion, GeometryObjectsWrite,
|
||||
GeometryView, HertzianDipole, MagneticDipole,
|
||||
Material, Rx, RxArray, Snapshot, SoilPeplinski,
|
||||
TransmissionLine, VoltageSource, Waveform)
|
||||
TransmissionLine, VoltageSource, Waveform, MaterialRange, MaterialList)
|
||||
from .cmds_singleuse import (Discretisation, Domain, ExcitationFile,
|
||||
OMPThreads, PMLProps, RxSteps, SrcSteps,
|
||||
TimeStepStabilityFactor, TimeWindow, Title)
|
||||
|
@@ -132,8 +132,8 @@ class Box(UserObjectGeometry):
|
||||
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].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)
|
||||
|
147
gprMax/cmds_geometry/cone.py
普通文件
147
gprMax/cmds_geometry/cone.py
普通文件
@@ -0,0 +1,147 @@
|
||||
# Copyright (C) 2015-2023: The University of Edinburgh, United Kingdom
|
||||
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
|
||||
#
|
||||
# This file is part of gprMax.
|
||||
#
|
||||
# gprMax is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gprMax is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..cython.geometry_primitives import build_cone
|
||||
from ..materials import Material
|
||||
from .cmds_geometry import UserObjectGeometry
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Cone(UserObjectGeometry):
|
||||
"""Introduces a circular cone into the model. The difference with the cylinder is that the faces of the cone
|
||||
can have different radii and one of them can be zero.
|
||||
|
||||
Attributes:
|
||||
p1: list of the coordinates (x,y,z) of the centre of the first face
|
||||
of the cone.
|
||||
p2: list of the coordinates (x,y,z) of the centre of the second face
|
||||
of the cone.
|
||||
r1: float of the radius of the first face of the cone.
|
||||
r2: float of the radius of the second face of the cone.
|
||||
material_id: string for the material identifier that must correspond
|
||||
to material that has already been defined.
|
||||
material_ids: list of material identifiers in the x, y, z directions.
|
||||
averaging: string (y or n) used to switch on and off dielectric smoothing.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.hash = '#cylinder'
|
||||
|
||||
def create(self, grid, uip):
|
||||
try:
|
||||
p1 = self.kwargs['p1']
|
||||
p2 = self.kwargs['p2']
|
||||
r1 = self.kwargs['r1']
|
||||
r2 = self.kwargs['r2']
|
||||
except KeyError:
|
||||
logger.exception(self.__str__() + ' please specify 2 points and two radii')
|
||||
raise
|
||||
|
||||
# Check averaging
|
||||
try:
|
||||
# Try user-specified averaging
|
||||
averagecylinder = self.kwargs['averaging']
|
||||
except KeyError:
|
||||
# Otherwise go with the grid default
|
||||
averagecylinder = grid.averagevolumeobjects
|
||||
|
||||
# Check materials have been specified
|
||||
# Isotropic case
|
||||
try:
|
||||
materialsrequested = [self.kwargs['material_id']]
|
||||
except KeyError:
|
||||
# Anisotropic case
|
||||
try:
|
||||
materialsrequested = self.kwargs['material_ids']
|
||||
except KeyError:
|
||||
logger.exception(self.__str__() + ' no materials have been specified')
|
||||
raise
|
||||
|
||||
p3 = uip.round_to_grid_static_point(p1)
|
||||
p4 = uip.round_to_grid_static_point(p2)
|
||||
|
||||
x1, y1, z1 = uip.round_to_grid(p1)
|
||||
x2, y2, z2 = uip.round_to_grid(p2)
|
||||
|
||||
if r1 < 0:
|
||||
logger.exception(self.__str__() + f' the radius of the first face {r1:g} should be a positive value.')
|
||||
raise ValueError
|
||||
|
||||
if r2 < 0:
|
||||
logger.exception(self.__str__() + f' the radius of the second face {r2:g} should be a positive value.')
|
||||
raise ValueError
|
||||
|
||||
if r1 == 0 and r2 == 0:
|
||||
logger.exception(self.__str__() + f' not both radii can be zero.')
|
||||
raise ValueError
|
||||
|
||||
# Look up requested materials in existing list of material instances
|
||||
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
|
||||
|
||||
if len(materials) != len(materialsrequested):
|
||||
notfound = [x for x in materialsrequested if x not in materials]
|
||||
logger.exception(self.__str__() + f' material(s) {notfound} do not exist')
|
||||
raise ValueError
|
||||
|
||||
# Isotropic case
|
||||
if len(materials) == 1:
|
||||
averaging = materials[0].averagable and averagecylinder
|
||||
numID = numIDx = numIDy = numIDz = materials[0].numID
|
||||
|
||||
# Uniaxial anisotropic case
|
||||
elif len(materials) == 3:
|
||||
averaging = False
|
||||
numIDx = materials[0].numID
|
||||
numIDy = materials[1].numID
|
||||
numIDz = materials[2].numID
|
||||
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
|
||||
else:
|
||||
numID = len(grid.materials)
|
||||
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)
|
||||
|
||||
# Append the new material object to the materials list
|
||||
grid.materials.append(m)
|
||||
|
||||
build_cone(x1, y1, z1, x2, y2, z2, r1, r2, grid.dx, grid.dy, grid.dz,
|
||||
numID, numIDx, numIDy, numIDz, averaging, grid.solid,
|
||||
grid.rigidE, grid.rigidH, grid.ID)
|
||||
|
||||
dielectricsmoothing = 'on' if averaging else 'off'
|
||||
logger.info(self.grid_name(grid) + f"Cone with face centres {p3[0]:g}m, " +
|
||||
f"{p3[1]:g}m, {p3[2]:g}m and {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m, " +
|
||||
f"with radii {r1:g}m and {r2:g}, of material(s) {', '.join(materialsrequested)} " +
|
||||
f"created, dielectric smoothing is {dielectricsmoothing}.")
|
@@ -119,8 +119,8 @@ class Cylinder(UserObjectGeometry):
|
||||
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].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)
|
||||
|
@@ -143,8 +143,8 @@ class CylindricalSector(UserObjectGeometry):
|
||||
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].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)
|
||||
|
132
gprMax/cmds_geometry/ellipsoid.py
普通文件
132
gprMax/cmds_geometry/ellipsoid.py
普通文件
@@ -0,0 +1,132 @@
|
||||
# Copyright (C) 2015-2023: The University of Edinburgh, United Kingdom
|
||||
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
|
||||
#
|
||||
# This file is part of gprMax.
|
||||
#
|
||||
# gprMax is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gprMax is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..cython.geometry_primitives import build_ellipsoid
|
||||
from ..materials import Material
|
||||
from .cmds_geometry import UserObjectGeometry
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Ellipsoid(UserObjectGeometry):
|
||||
"""Introduces an ellipsoidal object with specific parameters into the model.
|
||||
|
||||
Attributes:
|
||||
p1: list of the coordinates (x,y,z) of the centre of the ellipsoid.
|
||||
xr: float of x semiaxis of the ellipsoid.
|
||||
xy: float of y semiaxis of the ellipsoid.
|
||||
xz: float of z semiaxis of the ellipsoid.
|
||||
material_id: string for the material identifier that must correspond
|
||||
to material that has already been defined.
|
||||
material_ids: list of material identifiers in the x, y, z directions.
|
||||
averaging: string (y or n) used to switch on and off dielectric smoothing.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.hash = '#ellipsoid'
|
||||
|
||||
def create(self, grid, uip):
|
||||
try:
|
||||
p1 = self.kwargs['p1']
|
||||
xr = self.kwargs['xr']
|
||||
yr = self.kwargs['yr']
|
||||
zr = self.kwargs['zr']
|
||||
|
||||
except KeyError:
|
||||
logger.exception(self.__str__() + ' please specify a point and the three semiaxes.')
|
||||
raise
|
||||
|
||||
# Check averaging
|
||||
try:
|
||||
# Try user-specified averaging
|
||||
averageellipsoid = self.kwargs['averaging']
|
||||
except KeyError:
|
||||
# Otherwise go with the grid default
|
||||
averageellipsoid = grid.averagevolumeobjects
|
||||
|
||||
# Check materials have been specified
|
||||
# Isotropic case
|
||||
try:
|
||||
materialsrequested = [self.kwargs['material_id']]
|
||||
except KeyError:
|
||||
# Anisotropic case
|
||||
try:
|
||||
materialsrequested = self.kwargs['material_ids']
|
||||
except KeyError:
|
||||
logger.exception(self.__str__() + ' no materials have been specified')
|
||||
raise
|
||||
|
||||
# Centre of sphere
|
||||
p2 = uip.round_to_grid_static_point(p1)
|
||||
#xc, yc, zc = uip.round_to_grid(p1)
|
||||
xc, yc, zc = uip.discretise_point(p1)
|
||||
|
||||
# Look up requested materials in existing list of material instances
|
||||
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
|
||||
|
||||
if len(materials) != len(materialsrequested):
|
||||
notfound = [x for x in materialsrequested if x not in materials]
|
||||
logger.exception(self.__str__() + f' material(s) {notfound} do not exist')
|
||||
raise ValueError
|
||||
|
||||
# Isotropic case
|
||||
if len(materials) == 1:
|
||||
averaging = materials[0].averagable and averageellipsoid
|
||||
numID = numIDx = numIDy = numIDz = materials[0].numID
|
||||
|
||||
# Uniaxial anisotropic case
|
||||
elif len(materials) == 3:
|
||||
averaging = False
|
||||
numIDx = materials[0].numID
|
||||
numIDy = materials[1].numID
|
||||
numIDz = materials[2].numID
|
||||
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
|
||||
else:
|
||||
numID = len(grid.materials)
|
||||
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)
|
||||
|
||||
# Append the new material object to the materials list
|
||||
grid.materials.append(m)
|
||||
|
||||
build_ellipsoid(xc, yc, zc, xr, yr, zr, grid.dx, grid.dy, grid.dz, numID,
|
||||
numIDx, numIDy, numIDz, averaging, grid.solid,
|
||||
grid.rigidE, grid.rigidH, grid.ID)
|
||||
|
||||
dielectricsmoothing = 'on' if averaging else 'off'
|
||||
logger.info(self.grid_name(grid) + f"Ellipsoid with centre {p2[0]:g}m, " +
|
||||
f"{p2[1]:g}m, {p2[2]:g}m, x-semiaxis {xr:g}m, y-semiaxis {yr:g}m and z-semiaxis {zr:g}m of material(s) " +
|
||||
f"{', '.join(materialsrequested)} created, dielectric " +
|
||||
f"smoothing is {dielectricsmoothing}.")
|
@@ -141,7 +141,7 @@ class FractalBox(UserObjectGeometry):
|
||||
raise ValueError
|
||||
# Create materials from mixing model as number of bins now known
|
||||
# from fractal_box command.
|
||||
mixingmodel.calculate_debye_properties(nbins, grid)
|
||||
mixingmodel.calculate_properties(nbins, grid)
|
||||
elif not material:
|
||||
logger.exception(self.__str__() + f' mixing model or material with ' +
|
||||
'ID {mixing_model_id} does not exist')
|
||||
|
@@ -83,7 +83,12 @@ class FractalBoxBuilder(UserObjectGeometry):
|
||||
volume.fractalvolume *= materialnumID
|
||||
else:
|
||||
volume.generate_fractal_volume()
|
||||
volume.fractalvolume += volume.mixingmodel.startmaterialnum
|
||||
#volume.fractalvolume += volume.mixingmodel.startmaterialnum
|
||||
for i in range(0, volume.nx):
|
||||
for j in range(0, volume.ny):
|
||||
for k in range(0, volume.nz):
|
||||
numberinbin = volume.fractalvolume[i,j,k]
|
||||
volume.fractalvolume[i,j,k] = volume.mixingmodel.matID[int(numberinbin)]
|
||||
|
||||
volume.generate_volume_mask()
|
||||
|
||||
@@ -320,7 +325,13 @@ class FractalBoxBuilder(UserObjectGeometry):
|
||||
raise ValueError
|
||||
else:
|
||||
volume.generate_fractal_volume()
|
||||
volume.fractalvolume += volume.mixingmodel.startmaterialnum
|
||||
#volume.fractalvolume += volume.mixingmodel.startmaterialnum
|
||||
for i in range(0, volume.nx):
|
||||
for j in range(0, volume.ny):
|
||||
for k in range(0, volume.nz):
|
||||
numberinbin = volume.fractalvolume[i,j,k]
|
||||
volume.fractalvolume[i,j,k] = volume.mixingmodel.matID[int(numberinbin)]
|
||||
|
||||
|
||||
data = volume.fractalvolume.astype('int16', order='C')
|
||||
build_voxels_from_array(volume.xs, volume.ys, volume.zs,
|
||||
|
@@ -72,8 +72,13 @@ class Sphere(UserObjectGeometry):
|
||||
raise
|
||||
|
||||
# Centre of sphere
|
||||
|
||||
p2 = uip.round_to_grid_static_point(p1)
|
||||
xc, yc, zc = uip.round_to_grid(p1)
|
||||
#xc, yc, zc = uip.round_to_grid(p1)
|
||||
xc, yc, zc = uip.discretise_point(p1)
|
||||
|
||||
|
||||
|
||||
|
||||
# Look up requested materials in existing list of material instances
|
||||
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
|
||||
@@ -109,8 +114,8 @@ class Sphere(UserObjectGeometry):
|
||||
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].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)
|
||||
|
@@ -160,8 +160,8 @@ class Triangle(UserObjectGeometry):
|
||||
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].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)
|
||||
|
@@ -31,6 +31,8 @@ from .geometry_outputs import GeometryObjects as GeometryObjectsUser
|
||||
from .materials import DispersiveMaterial as DispersiveMaterialUser
|
||||
from .materials import Material as MaterialUser
|
||||
from .materials import PeplinskiSoil as PeplinskiSoilUser
|
||||
from .materials import RangeMaterial as RangeMaterialUser
|
||||
from .materials import ListMaterial as ListMaterialUser
|
||||
from .pml import CFS, CFSParameter
|
||||
from .receivers import Rx as RxUser
|
||||
from .snapshots import Snapshot as SnapshotUser
|
||||
@@ -1438,6 +1440,137 @@ class SoilPeplinski(UserObjectMulti):
|
||||
grid.mixingmodels.append(s)
|
||||
|
||||
|
||||
class MaterialRange(UserObjectMulti):
|
||||
"""Simple model for varying material properties to create stochastic models
|
||||
|
||||
|
||||
Attributes:
|
||||
er_lower_range: float required for lower relative permittivity value.
|
||||
er_upper_range: float required for upper relative permittivity value.
|
||||
sigma_lower_range: float required for lower conductivity value.
|
||||
sigma_upper_range: float required for upper conductivity value.
|
||||
mr_lower_range: float required for lower relative magnetic permeability value.
|
||||
mr_upper_range: float required for upper relative magnetic permeability value.
|
||||
ro_lower_range: float required for lower magnetic loss value.
|
||||
ro_upper_range: float required for upper magnetic loss value.
|
||||
|
||||
id: string used as identifier for this variable material.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.order = 17
|
||||
self.hash = '#material_range'
|
||||
|
||||
def create(self, grid, uip):
|
||||
try:
|
||||
er_lower = self.kwargs['er_lower']
|
||||
er_upper = self.kwargs['er_upper']
|
||||
sigma_lower = self.kwargs['sigma_lower']
|
||||
sigma_upper = self.kwargs['sigma_upper']
|
||||
mr_lower = self.kwargs['mr_lower']
|
||||
mr_upper = self.kwargs['mr_upper']
|
||||
ro_lower = self.kwargs['ro_lower']
|
||||
ro_upper = self.kwargs['ro_upper']
|
||||
ID = self.kwargs['id']
|
||||
except KeyError:
|
||||
logger.exception(self.params_str() + ' requires at exactly nine '
|
||||
'parameters.')
|
||||
raise
|
||||
|
||||
if er_lower < 1:
|
||||
logger.exception(self.params_str() + ' requires a value greater or equal to 1 '
|
||||
'for the lower range of relative permittivity.')
|
||||
raise ValueError
|
||||
if mr_lower < 1:
|
||||
logger.exception(self.params_str() + ' requires a value greater or equal to 1 '
|
||||
'for the lower range of relative magnetic permeability.')
|
||||
raise ValueError
|
||||
if sigma_lower < 0:
|
||||
logger.exception(self.params_str() + ' requires a positive value '
|
||||
'for the lower limit of conductivity.')
|
||||
raise ValueError
|
||||
if ro_lower < 0:
|
||||
logger.exception(self.params_str() + ' requires a positive value '
|
||||
'for the lower range magnetic loss.')
|
||||
raise ValueError
|
||||
if er_upper < 1:
|
||||
logger.exception(self.params_str() + ' requires a value greater or equal to 1'
|
||||
'for the upper range of relative permittivity.')
|
||||
raise ValueError
|
||||
|
||||
if mr_upper < 1:
|
||||
logger.exception(self.params_str() + ' requires a value greater or equal to 1'
|
||||
'for the upper range of relative magnetic permeability')
|
||||
raise ValueError
|
||||
|
||||
if sigma_upper < 0:
|
||||
logger.exception(self.params_str() + ' requires a positive value '
|
||||
'for the upper range of conductivity.')
|
||||
raise ValueError
|
||||
if ro_upper < 0:
|
||||
logger.exception(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(self.params_str() + f' with ID {ID} already exists')
|
||||
raise ValueError
|
||||
|
||||
# Create a new instance of the Material class material
|
||||
# (start index after pec & free_space)
|
||||
s = RangeMaterialUser(ID, (er_lower, er_upper), (sigma_lower, sigma_upper), (mr_lower, mr_upper), (ro_lower, ro_upper))
|
||||
|
||||
logger.info(self.grid_name(grid) + 'Material properties used to '
|
||||
f'create {s.ID} with range(s) {s.er[0]:g} to {s.er[1]:g}, relative permittivity '
|
||||
f'{s.sig[0]:g} to {s.sig[1]:g}, S/m conductivity, {s.mu[0]:g} to {s.mu[1]:g} relative magnetic permeability '
|
||||
f'{s.ro[0]:g} to {s.ro[1]:g} Ohm/m magnetic loss, created')
|
||||
|
||||
|
||||
grid.mixingmodels.append(s)
|
||||
|
||||
|
||||
class MaterialList(UserObjectMulti):
|
||||
"""Simple model for varying material properties to create stochastic models
|
||||
|
||||
|
||||
Attributes:
|
||||
list_of_materials: list of material IDs
|
||||
|
||||
id: string used as identifier for this variable material.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.order = 18
|
||||
self.hash = '#material_list'
|
||||
|
||||
def create(self, grid, uip):
|
||||
try:
|
||||
list_of_materials = self.kwargs['list_of_materials']
|
||||
ID = self.kwargs['id']
|
||||
except KeyError:
|
||||
logger.exception(self.params_str() + ' requires at at least 2 '
|
||||
'parameters.')
|
||||
raise
|
||||
|
||||
|
||||
if any(x.ID == ID for x in grid.mixingmodels):
|
||||
logger.exception(self.params_str() + f' with ID {ID} already exists')
|
||||
raise ValueError
|
||||
|
||||
# Create a new instance of the Material class material
|
||||
# (start index after pec & free_space)
|
||||
s = ListMaterialUser(ID, list_of_materials)
|
||||
|
||||
logger.info(self.grid_name(grid) + 'A list of materials used to '
|
||||
f'create {s.ID} that includes {s.mat}, created')
|
||||
|
||||
|
||||
grid.mixingmodels.append(s)
|
||||
|
||||
|
||||
|
||||
class GeometryView(UserObjectMulti):
|
||||
"""Outputs to file(s) information about the geometry (mesh) of model.
|
||||
|
||||
|
@@ -917,6 +917,222 @@ cpdef void build_cylinder(
|
||||
averaging, solid, rigidE, rigidH, ID)
|
||||
|
||||
|
||||
|
||||
cpdef void build_cone(
|
||||
float x1,
|
||||
float y1,
|
||||
float z1,
|
||||
float x2,
|
||||
float y2,
|
||||
float z2,
|
||||
float r1,
|
||||
float r2,
|
||||
float dx,
|
||||
float dy,
|
||||
float dz,
|
||||
int numID,
|
||||
int numIDx,
|
||||
int numIDy,
|
||||
int numIDz,
|
||||
bint averaging,
|
||||
np.uint32_t[:, :, ::1] solid,
|
||||
np.int8_t[:, :, :, ::1] rigidE,
|
||||
np.int8_t[:, :, :, ::1] rigidH,
|
||||
np.uint32_t[:, :, :, ::1] ID
|
||||
):
|
||||
"""Builds cones which sets values in the solid, rigid and ID arrays for
|
||||
a Yee voxel.
|
||||
|
||||
Args:
|
||||
x1, y1, z1, x2, y2, z2: floats for coordinates of the centres of the cone
|
||||
faces.
|
||||
r1: float for radius of the first face of the cone.
|
||||
r2: float for radius of the second face of the cone.
|
||||
dx, dy, dz: floats for spatial discretisation.
|
||||
numID, numIDx, numIDy, numIDz: ints for numeric ID of material.
|
||||
averaging: bint for whether material property averaging will occur for
|
||||
the object.
|
||||
solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays.
|
||||
"""
|
||||
|
||||
cdef Py_ssize_t i, j, k
|
||||
cdef int xs, xf, ys, yf, zs, zf, xc, yc, zc
|
||||
cdef float f1f2mag, f2f1mag, f1ptmag, f2ptmag, dot1, dot2, factor1, factor2
|
||||
cdef float theta1, theta2, distance1, distance2, R1, R2
|
||||
cdef float height, distance_axis_1, distance_axis_2
|
||||
cdef bint build, x_align, y_align, z_align
|
||||
cdef np.ndarray f1f2, f2f1, f1pt, f2pt
|
||||
cdef float Rmax
|
||||
|
||||
Rmax = np.amax([r1, r2])
|
||||
|
||||
# Check if cylinder is aligned with an axis
|
||||
x_align = y_align = z_align = 0
|
||||
# x-aligned
|
||||
if (round_value(y1 / dy) == round_value(y2 / dy) and
|
||||
round_value(z1 / dz) == round_value(z2 / dz)):
|
||||
x_align = 1
|
||||
|
||||
# y-aligned
|
||||
elif (round_value(x1 / dx) == round_value(x2 / dx) and
|
||||
round_value(z1 / dz) == round_value(z2 / dz)):
|
||||
y_align = 1
|
||||
|
||||
# z-aligned
|
||||
elif (round_value(x1 / dx) == round_value(x2 / dx) and
|
||||
round_value(y1 / dy) == round_value(y2 / dy)):
|
||||
z_align = 1
|
||||
|
||||
# Calculate a bounding box for the cylinder
|
||||
if x1 < x2:
|
||||
if x_align:
|
||||
xs = round_value(x1 / dx)
|
||||
xf = round_value(x2 / dx)
|
||||
else:
|
||||
xs = round_value((x1 - Rmax) / dx) - 1
|
||||
xf = round_value((x2 + Rmax) / dx) + 1
|
||||
else:
|
||||
if x_align:
|
||||
xs = round_value(x2 / dx)
|
||||
xf = round_value(x1 / dx)
|
||||
else:
|
||||
xs = round_value((x2 - Rmax) / dx) - 1
|
||||
xf = round_value((x1 + Rmax) / dx) + 1
|
||||
if y1 < y2:
|
||||
if y_align:
|
||||
ys = round_value(y1 / dy)
|
||||
yf = round_value(y2 / dy)
|
||||
else:
|
||||
ys = round_value((y1 - Rmax) / dy) - 1
|
||||
yf = round_value((y2 + Rmax) / dy) + 1
|
||||
else:
|
||||
if y_align:
|
||||
ys = round_value(y2 / dy)
|
||||
yf = round_value(y1 / dy)
|
||||
else:
|
||||
ys = round_value((y2 - Rmax) / dy) - 1
|
||||
yf = round_value((y1 + Rmax) / dy) + 1
|
||||
if z1 < z2:
|
||||
if z_align:
|
||||
zs = round_value(z1 / dz)
|
||||
zf = round_value(z2 / dz)
|
||||
else:
|
||||
zs = round_value((z1 - Rmax) / dz) - 1
|
||||
zf = round_value((z2 + Rmax) / dz) + 1
|
||||
else:
|
||||
if z_align:
|
||||
zs = round_value(z2 / dz)
|
||||
zf = round_value(z1 / dz)
|
||||
else:
|
||||
zs = round_value((z2 - Rmax) / dz) - 1
|
||||
zf = round_value((z1 + Rmax) / dz) + 1
|
||||
|
||||
# Set bounds to domain if they outside
|
||||
if xs < 0:
|
||||
xs = 0
|
||||
if xf > solid.shape[0]:
|
||||
xf = solid.shape[0]
|
||||
if ys < 0:
|
||||
ys = 0
|
||||
if yf > solid.shape[1]:
|
||||
yf = solid.shape[1]
|
||||
if zs < 0:
|
||||
zs = 0
|
||||
if zf > solid.shape[2]:
|
||||
zf = solid.shape[2]
|
||||
|
||||
# x-aligned cylinder
|
||||
if x_align:
|
||||
for j in range(ys, yf):
|
||||
for k in range(zs, zf):
|
||||
for i in range(xs, xf):
|
||||
if np.sqrt((j * dy + 0.5 * dy - y1)**2 + (k * dz + 0.5 * dz - z1)**2) <= ((i-xs)/(xf-xs))*(r2-r1) + r1:
|
||||
build_voxel(i, j, k, numID, numIDx, numIDy, numIDz,
|
||||
averaging, solid, rigidE, rigidH, ID)
|
||||
# y-aligned cylinder
|
||||
elif y_align:
|
||||
for i in range(xs, xf):
|
||||
for k in range(zs, zf):
|
||||
for j in range(ys, yf):
|
||||
if np.sqrt((i * dx + 0.5 * dx - x1)**2 + (k * dz + 0.5 * dz - z1)**2) <= ((j-ys)/(yf-ys))*(r2-r1) + r1:
|
||||
build_voxel(i, j, k, numID, numIDx, numIDy, numIDz,
|
||||
averaging, solid, rigidE, rigidH, ID)
|
||||
# z-aligned cylinder
|
||||
elif z_align:
|
||||
for i in range(xs, xf):
|
||||
for j in range(ys, yf):
|
||||
for k in range(zs, zf):
|
||||
if np.sqrt((i * dx + 0.5 * dx - x1)**2 + (j * dy + 0.5 * dy - y1)**2) <= ((k-zs)/(zf-zs))*(r2-r1) + r1:
|
||||
build_voxel(i, j, k, numID, numIDx, numIDy, numIDz,
|
||||
averaging, solid, rigidE, rigidH, ID)
|
||||
|
||||
# Not aligned with any axis
|
||||
else:
|
||||
# Vectors between centres of cylinder faces
|
||||
f1f2 = np.array([x2 - x1, y2 - y1, z2 - z1], dtype=np.float32)
|
||||
f2f1 = np.array([x1 - x2, y1 - y2, z1 - z2], dtype=np.float32)
|
||||
|
||||
# Magnitudes
|
||||
f1f2mag = np.sqrt((f1f2*f1f2).sum(axis=0))
|
||||
f2f1mag = np.sqrt((f2f1*f2f1).sum(axis=0))
|
||||
|
||||
height = f1f2mag
|
||||
|
||||
|
||||
for i in range(xs, xf):
|
||||
for j in range(ys, yf):
|
||||
for k in range(zs, zf):
|
||||
# Build flag - default false, set to True if point is in cylinder
|
||||
build = 0
|
||||
# Vector from centre of first cylinder face to test point
|
||||
f1pt = np.array([i * dx + 0.5 * dx - x1,
|
||||
j * dy + 0.5 * dy - y1,
|
||||
k * dz + 0.5 * dz - z1], dtype=np.float32)
|
||||
# Vector from centre of second cylinder face to test point
|
||||
f2pt = np.array([i * dx + 0.5 * dx - x2,
|
||||
j * dy + 0.5 * dy - y2,
|
||||
k * dz + 0.5 * dz - z2], dtype=np.float32)
|
||||
# Magnitudes
|
||||
f1ptmag = np.sqrt((f1pt*f1pt).sum(axis=0))
|
||||
f2ptmag = np.sqrt((f2pt*f2pt).sum(axis=0))
|
||||
# Dot products
|
||||
dot1 = np.dot(f1f2, f1pt)
|
||||
dot2 = np.dot(f2f1, f2pt)
|
||||
|
||||
if f1ptmag == 0 or f2ptmag == 0:
|
||||
build = 1
|
||||
else:
|
||||
factor1 = dot1 / (f1f2mag * f1ptmag)
|
||||
factor2 = dot2 / (f2f1mag * f2ptmag)
|
||||
# Catch cases where either factor1 or factor2 are 1
|
||||
try:
|
||||
theta1 = np.arccos(factor1)
|
||||
except FloatingPointError:
|
||||
theta1 = 0
|
||||
try:
|
||||
theta2 = np.arccos(factor2)
|
||||
except FloatingPointError:
|
||||
theta2 = 0
|
||||
distance1 = f1ptmag * np.sin(theta1)
|
||||
distance2 = f2ptmag * np.sin(theta2)
|
||||
distance_axis_1 = f1ptmag * np.cos(theta1)
|
||||
distance_axis_2 = f2ptmag * np.cos(theta2)
|
||||
R1 = r1
|
||||
R2 = r2
|
||||
|
||||
if ((distance1 <= (distance_axis_1/height)*(R2 - R1) + R1 or distance2 <= (distance_axis_2/height)*(R1 - R2) + R2) and
|
||||
theta1 <= np.pi/2 and theta2 <= np.pi/2):
|
||||
build = 1
|
||||
|
||||
if build:
|
||||
build_voxel(i, j, k, numID, numIDx, numIDy, numIDz,
|
||||
averaging, solid, rigidE, rigidH, ID)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cpdef void build_sphere(
|
||||
int xc,
|
||||
int yc,
|
||||
@@ -983,6 +1199,77 @@ cpdef void build_sphere(
|
||||
averaging, solid, rigidE, rigidH, ID)
|
||||
|
||||
|
||||
cpdef void build_ellipsoid(
|
||||
int xc,
|
||||
int yc,
|
||||
int zc,
|
||||
float xr,
|
||||
float yr,
|
||||
float zr,
|
||||
float dx,
|
||||
float dy,
|
||||
float dz,
|
||||
int numID,
|
||||
int numIDx,
|
||||
int numIDy,
|
||||
int numIDz,
|
||||
bint averaging,
|
||||
np.uint32_t[:, :, ::1] solid,
|
||||
np.int8_t[:, :, :, ::1] rigidE,
|
||||
np.int8_t[:, :, :, ::1] rigidH,
|
||||
np.uint32_t[:, :, :, ::1] ID
|
||||
):
|
||||
"""Builds ellipsoids which sets values in the solid, rigid and ID arrays for
|
||||
a Yee voxel.
|
||||
|
||||
Args:
|
||||
xc, yc, zc: ints for cell coordinates of the centre of the ellipsoid.
|
||||
xr: float for x-semiaxis of the elliposid.
|
||||
yr: float for y-semiaxis of the elliposid.
|
||||
zr: float for z-semiaxis of the elliposid.
|
||||
dx, dy, dz: floats for spatial discretisation.
|
||||
numID, numIDx, numIDy, numIDz: ints for numeric ID of material.
|
||||
averaging: bint for whether material property averaging will occur for
|
||||
the object.
|
||||
solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays.
|
||||
"""
|
||||
|
||||
cdef Py_ssize_t i, j, k
|
||||
cdef int xs, xf, ys, yf, zs, zf
|
||||
|
||||
# Calculate a bounding box for sphere
|
||||
xs = round_value(((xc * dx) - xr) / dx) - 1
|
||||
xf = round_value(((xc * dx) + xr) / dx) + 1
|
||||
ys = round_value(((yc * dy) - yr) / dy) - 1
|
||||
yf = round_value(((yc * dy) + yr) / dy) + 1
|
||||
zs = round_value(((zc * dz) - zr) / dz) - 1
|
||||
zf = round_value(((zc * dz) + zr) / dz) + 1
|
||||
|
||||
# Set bounds to domain if they outside
|
||||
if xs < 0:
|
||||
xs = 0
|
||||
if xf > solid.shape[0]:
|
||||
xf = solid.shape[0]
|
||||
if ys < 0:
|
||||
ys = 0
|
||||
if yf > solid.shape[1]:
|
||||
yf = solid.shape[1]
|
||||
if zs < 0:
|
||||
zs = 0
|
||||
if zf > solid.shape[2]:
|
||||
zf = solid.shape[2]
|
||||
|
||||
for i in range(xs, xf):
|
||||
for j in range(ys, yf):
|
||||
for k in range(zs, zf):
|
||||
if (((i + 0.5 - xc)**2 * dx**2)/xr**2 +
|
||||
((j + 0.5 - yc)**2 * dy**2)/yr**2 +
|
||||
((k + 0.5 - zc)**2 * dz**2)/zr**2 <= 1):
|
||||
build_voxel(i, j, k, numID, numIDx, numIDy, numIDz,
|
||||
averaging, solid, rigidE, rigidH, ID)
|
||||
|
||||
|
||||
|
||||
cpdef void build_voxels_from_array(
|
||||
int xs,
|
||||
int ys,
|
||||
|
@@ -219,12 +219,12 @@ def check_cmd_names(processedlines, checkessential=True):
|
||||
'#waveform', '#voltage_source',
|
||||
'#hertzian_dipole', '#magnetic_dipole',
|
||||
'#transmission_line', '#rx', '#rx_array',
|
||||
'#snapshot', '#include_file']}
|
||||
'#snapshot', '#include_file', '#material_range', '#material_list']}
|
||||
|
||||
# Geometry object building commands that there can be multiple instances
|
||||
# of in a model - these will be lists within the dictionary
|
||||
geometrycmds = ['#geometry_objects_read', '#edge', '#plate', '#triangle',
|
||||
'#box', '#sphere', '#cylinder', '#cylindrical_sector',
|
||||
'#box', '#sphere', '#ellipsoid', '#cone', '#cylinder', '#cylindrical_sector',
|
||||
'#fractal_box', '#add_surface_roughness',
|
||||
'#add_surface_water', '#add_grass']
|
||||
# List to store all geometry object commands in order from input file
|
||||
|
@@ -24,12 +24,14 @@ from .cmds_geometry.add_surface_roughness import AddSurfaceRoughness
|
||||
from .cmds_geometry.add_surface_water import AddSurfaceWater
|
||||
from .cmds_geometry.box import Box
|
||||
from .cmds_geometry.cylinder import Cylinder
|
||||
from .cmds_geometry.cone import Cone
|
||||
from .cmds_geometry.cylindrical_sector import CylindricalSector
|
||||
from .cmds_geometry.edge import Edge
|
||||
from .cmds_geometry.fractal_box import FractalBox
|
||||
from .cmds_geometry.plate import Plate
|
||||
from .cmds_geometry.sphere import Sphere
|
||||
from .cmds_geometry.triangle import Triangle
|
||||
from .cmds_geometry.ellipsoid import Ellipsoid
|
||||
from .utilities.utilities import round_value
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -217,6 +219,40 @@ def process_geometrycmds(geometry):
|
||||
|
||||
scene_objects.append(cylinder)
|
||||
|
||||
|
||||
elif tmp[0] == '#cone:':
|
||||
if len(tmp) < 10:
|
||||
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||
' requires at least nine parameters')
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
|
||||
r1 = float(tmp[7])
|
||||
r2 = float(tmp[8])
|
||||
|
||||
# Isotropic case with no user specified averaging
|
||||
if len(tmp) == 10:
|
||||
cone = Cone(p1=p1, p2=p2, r1=r1, r2=r2, material_id=tmp[9])
|
||||
|
||||
# Isotropic case with user specified averaging
|
||||
elif len(tmp) == 11:
|
||||
averaging = check_averaging(tmp[10].lower())
|
||||
cone = Cone(p1=p1, p2=p2, r1=r1, r2=r2, material_id=tmp[9],
|
||||
averaging=averaging)
|
||||
|
||||
# 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')
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(cone)
|
||||
|
||||
|
||||
elif tmp[0] == '#cylindrical_sector:':
|
||||
if len(tmp) < 10:
|
||||
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||
@@ -295,6 +331,41 @@ def process_geometrycmds(geometry):
|
||||
|
||||
scene_objects.append(sphere)
|
||||
|
||||
|
||||
elif tmp[0] == '#ellipsoid:':
|
||||
if len(tmp) < 8:
|
||||
logger.exception("'" + ' '.join(tmp) + "'" +
|
||||
' requires at least seven parameters')
|
||||
raise ValueError
|
||||
|
||||
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
|
||||
xr = float(tmp[4])
|
||||
yr = float(tmp[5])
|
||||
zr = float(tmp[6])
|
||||
|
||||
# Isotropic case with no user specified averaging
|
||||
if len(tmp) == 8:
|
||||
ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr, material_id=tmp[7])
|
||||
|
||||
# Isotropic case with user specified averaging
|
||||
elif len(tmp) == 9:
|
||||
averaging = check_averaging(tmp[8].lower())
|
||||
ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr, material_id=tmp[7],
|
||||
averaging=averaging)
|
||||
|
||||
# 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')
|
||||
raise ValueError
|
||||
|
||||
scene_objects.append(ellipsoid)
|
||||
|
||||
|
||||
|
||||
elif tmp[0] == '#fractal_box:':
|
||||
# Default is no dielectric smoothing for a fractal box
|
||||
|
||||
|
@@ -22,7 +22,7 @@ from .cmds_multiuse import (AddDebyeDispersion, AddDrudeDispersion,
|
||||
AddLorentzDispersion, GeometryObjectsWrite,
|
||||
GeometryView, HertzianDipole, MagneticDipole,
|
||||
Material, Rx, RxArray, Snapshot, SoilPeplinski,
|
||||
TransmissionLine, VoltageSource, Waveform)
|
||||
TransmissionLine, VoltageSource, Waveform, MaterialRange, MaterialList)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -342,4 +342,47 @@ def process_multicmds(multicmds):
|
||||
gow = GeometryObjectsWrite(p1=p1, p2=p2, filename=tmp[6])
|
||||
scene_objects.append(gow)
|
||||
|
||||
cmdname = '#material_range'
|
||||
if multicmds[cmdname] is not None:
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
|
||||
if len(tmp) != 9:
|
||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||
' requires at exactly nine parameters')
|
||||
raise ValueError
|
||||
material_range = MaterialRange(er_lower=float(tmp[0]),
|
||||
er_upper=float(tmp[1]),
|
||||
sigma_lower=float(tmp[2]),
|
||||
sigma_upper=float(tmp[3]),
|
||||
mr_lower=float(tmp[4]),
|
||||
mr_upper=float(tmp[5]),
|
||||
ro_lower=float(tmp[6]),
|
||||
ro_upper=float(tmp[7]),
|
||||
id=tmp[8])
|
||||
scene_objects.append(material_range)
|
||||
|
||||
|
||||
cmdname = '#material_list'
|
||||
if multicmds[cmdname] is not None:
|
||||
for cmdinstance in multicmds[cmdname]:
|
||||
tmp = cmdinstance.split()
|
||||
|
||||
if len(tmp) < 2:
|
||||
logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" +
|
||||
' requires at least 2 parameters')
|
||||
raise ValueError
|
||||
|
||||
tokens = len(tmp)
|
||||
lmats = []
|
||||
for iter in range(0,tokens-1):
|
||||
lmats.append(tmp[iter])
|
||||
|
||||
|
||||
|
||||
material_list = MaterialList(list_of_materials=lmats,
|
||||
id=tmp[tokens-1])
|
||||
scene_objects.append(material_list)
|
||||
|
||||
|
||||
return scene_objects
|
||||
|
@@ -17,10 +17,14 @@
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import numpy as np
|
||||
import logging
|
||||
|
||||
|
||||
import gprMax.config as config
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Material:
|
||||
"""Super-class to describe generic, non-dispersive materials,
|
||||
their properties and update coefficients.
|
||||
@@ -309,9 +313,12 @@ class PeplinskiSoil:
|
||||
self.rb = bulkdensity
|
||||
self.rs = sandpartdensity
|
||||
self.mu = watervolfraction
|
||||
self.startmaterialnum = 0
|
||||
self.startmaterialnum = 0 #This is not used anymore and code that uses it can be removed
|
||||
# store all of the material IDs in a list instead of storing only the first number of the material
|
||||
# and assume that all must be sequentially numbered. This allows for more general mixing models
|
||||
self.matID = []
|
||||
|
||||
def calculate_debye_properties(self, nbins, G):
|
||||
def calculate_properties(self, nbins, G):
|
||||
"""Calculates the real and imaginery part of a Debye model for the soil
|
||||
as well as a conductivity. It uses an approximation to a semi-empirical
|
||||
model (http://dx.doi.org/10.1109/36.387598).
|
||||
@@ -340,11 +347,16 @@ class PeplinskiSoil:
|
||||
# sigf = -1.645 + 1.939 * self.rb - 2.25622 * self.S + 1.594 * self.C
|
||||
|
||||
# Generate a set of bins based on the given volumetric water fraction
|
||||
# values
|
||||
mubins = np.linspace(self.mu[0], self.mu[1], nbins)
|
||||
# values. Changed to make sure mid points are contained completely within the ranges.
|
||||
# The limiting values of the ranges are not included in this.
|
||||
|
||||
#mubins = np.linspace(self.mu[0], self.mu[1], nbins)
|
||||
mubins = np.linspace(self.mu[0], self.mu[1], nbins+1)
|
||||
# Generate a range of volumetric water fraction values the mid-point of
|
||||
# each bin to make materials from
|
||||
mumaterials = mubins + (mubins[1] - mubins[0]) / 2
|
||||
#mumaterials = mubins + (mubins[1] - mubins[0]) / 2
|
||||
mumaterials = 0.5*(mubins[1:nbins+1] + mubins[0:nbins])
|
||||
|
||||
|
||||
# Create an iterator
|
||||
muiter = np.nditer(mumaterials, flags=['c_index'])
|
||||
@@ -368,8 +380,9 @@ class PeplinskiSoil:
|
||||
if muiter.index == 0:
|
||||
if material:
|
||||
self.startmaterialnum = material.numID
|
||||
self.matID.append(material.numID)
|
||||
else:
|
||||
self.startmaterialnum = len(G.materials)
|
||||
self.startmaterialnum = len(G.materials)
|
||||
if not material:
|
||||
m = DispersiveMaterial(len(G.materials), requiredID)
|
||||
m.type = 'debye'
|
||||
@@ -382,8 +395,174 @@ class PeplinskiSoil:
|
||||
m.deltaer.append(er - eri)
|
||||
m.tau.append(DispersiveMaterial.watertau)
|
||||
G.materials.append(m)
|
||||
self.matID.append(m.numID)
|
||||
|
||||
muiter.iternext()
|
||||
|
||||
|
||||
|
||||
class RangeMaterial:
|
||||
"""Material defined with a given range of parameters to be used for
|
||||
factal spatial disttibutions
|
||||
"""
|
||||
|
||||
def __init__(self, ID, er_range, sigma_range, mu_range, ro_range):
|
||||
"""
|
||||
Args:
|
||||
ID: string for name of the material.
|
||||
er_range: tuple of floats for relative permittivity range of the material.
|
||||
sigma_range: tuple of floats for electric conductivity range of the material.
|
||||
mu_range: tuple of floats for magnetic permeability of material.
|
||||
ro_range: tuple of floats for magnetic loss range of material.
|
||||
"""
|
||||
|
||||
self.ID = ID
|
||||
self.er = er_range
|
||||
self.sig = sigma_range
|
||||
self.mu = mu_range
|
||||
self.ro = ro_range
|
||||
self.startmaterialnum = 0 #This is not really needed anymore and code that uses it can be removed.
|
||||
# store all of the material IDs in a list instead of storing only the first number of the material
|
||||
# and assume that all must be sequentially numbered. This allows for more general mixing models
|
||||
self.matID = []
|
||||
|
||||
|
||||
def calculate_properties(self, nbins, G):
|
||||
"""Calculates the properties of the materials.
|
||||
|
||||
Args:
|
||||
nbins: int for number of bins to use to create the different materials.
|
||||
G: FDTDGrid class describing a grid in a model.
|
||||
"""
|
||||
|
||||
# Generate a set of relative permittivity bins based on the given range
|
||||
erbins = np.linspace(self.er[0], self.er[1], nbins+1)
|
||||
# Generate a range of relative permittivity values the mid-point of
|
||||
# each bin to make materials from
|
||||
#ermaterials = erbins + np.abs((erbins[1] - erbins[0])) / 2
|
||||
ermaterials = 0.5*(erbins[1:nbins+1] + erbins[0:nbins])
|
||||
|
||||
# Generate a set of conductivity bins based on the given range
|
||||
sigmabins = np.linspace(self.sig[0], self.sig[1], nbins+1)
|
||||
# Generate a range of conductivity values the mid-point of
|
||||
# each bin to make materials from
|
||||
#sigmamaterials = sigmabins + (sigmabins[1] - sigmabins[0]) / 2
|
||||
sigmamaterials = 0.5*(sigmabins[1:nbins+1] + sigmabins[0:nbins])
|
||||
|
||||
# Generate a set of magnetic permeability bins based on the given range
|
||||
mubins = np.linspace(self.mu[0], self.mu[1], nbins+1)
|
||||
# Generate a range of magnetic permeability values the mid-point of
|
||||
# each bin to make materials from
|
||||
#mumaterials = mubins + np.abs((mubins[1] - mubins[0])) / 2
|
||||
mumaterials = 0.5*(mubins[1:nbins+1] + mubins[0:nbins])
|
||||
|
||||
# Generate a set of magnetic loss bins based on the given range
|
||||
robins = np.linspace(self.ro[0], self.ro[1], nbins+1)
|
||||
# Generate a range of magnetic loss values the mid-point of
|
||||
# each bin to make materials from
|
||||
#romaterials = robins + np.abs((robins[1] - robins[0])) / 2
|
||||
romaterials = 0.5*(robins[1:nbins+1] + robins[0:nbins])
|
||||
|
||||
|
||||
# Iterate over the bins
|
||||
for iter in np.arange(0,nbins):
|
||||
|
||||
# Relative permittivity
|
||||
er = ermaterials[iter]
|
||||
|
||||
# Effective conductivity
|
||||
se = sigmamaterials[iter]
|
||||
|
||||
# magnetic permeability
|
||||
mr = mumaterials[iter]
|
||||
|
||||
# magnetic loss
|
||||
sm = romaterials[iter]
|
||||
|
||||
# Check to see if the material already exists before creating a new one
|
||||
requiredID = '|{:.4f}+{:.4f}+{:.4f}+{:.4f}|'.format(float(er),float(se),float(mr),float(sm))
|
||||
material = next((x for x in G.materials if x.ID == requiredID), None)
|
||||
if iter == 0:
|
||||
if material:
|
||||
self.startmaterialnum = material.numID
|
||||
self.matID.append(material.numID)
|
||||
else:
|
||||
self.startmaterialnum = len(G.materials)
|
||||
if not material:
|
||||
m = Material(len(G.materials), requiredID)
|
||||
m.type = ''
|
||||
m.averagable = True
|
||||
m.er = er
|
||||
m.se = se
|
||||
m.mr = mr
|
||||
m.sm = sm
|
||||
G.materials.append(m)
|
||||
self.matID.append(m.numID)
|
||||
|
||||
|
||||
|
||||
class ListMaterial:
|
||||
"""A list of predefined materials to be used for
|
||||
factal spatial disttibutions. This command does not create new materials but collects them to be used in a
|
||||
stochastic distribution by a fractal box.
|
||||
"""
|
||||
|
||||
def __init__(self, ID, listofmaterials):
|
||||
"""
|
||||
Args:
|
||||
ID: string for name of the material.
|
||||
listofmaterials: A list of material IDs.
|
||||
|
||||
"""
|
||||
|
||||
self.ID = ID
|
||||
self.mat = listofmaterials
|
||||
self.startmaterialnum = 0 #This is not really needed anymore
|
||||
# store all of the material IDs in a list instead of storing only the first number of the material
|
||||
# and assume that all must be sequentially numbered. This allows for more general mixing models
|
||||
# this is important here as this model assumes predefined materials.
|
||||
self.matID = []
|
||||
|
||||
|
||||
def calculate_properties(self, nbins, G):
|
||||
"""Calculates the properties of the materials. No Debye is used but name kept the same as used in other
|
||||
class that needs Debye
|
||||
|
||||
Args:
|
||||
nbins: int for number of bins to use to create the different materials.
|
||||
G: FDTDGrid class describing a grid in a model.
|
||||
"""
|
||||
|
||||
|
||||
# Iterate over the bins
|
||||
for iter in np.arange(0,nbins):
|
||||
|
||||
# Check to see if the material already exists before creating a new one
|
||||
#requiredID = '|{:}_in_{:}|'.format((self.mat[iter]),(self.ID))
|
||||
requiredID = self.mat[iter]
|
||||
material = next((x for x in G.materials if x.ID == requiredID), None)
|
||||
self.matID.append(material.numID)
|
||||
|
||||
#if iter == 0:
|
||||
# if material:
|
||||
# self.startmaterialnum = material.numID
|
||||
# else:
|
||||
# self.startmaterialnum = len(G.materials)
|
||||
|
||||
#if not material:
|
||||
# temp = next((x for x in G.materials if x.ID == self.mat[iter]), None)
|
||||
# m = copy.deepcopy(temp) #This needs to import copy in order to work
|
||||
# m.ID = requiredID
|
||||
# m.numID = len(G.materials)
|
||||
# G.materials.append(m)
|
||||
|
||||
|
||||
if not material:
|
||||
logger.exception(self.__str__() + f' material(s) {material} do not exist')
|
||||
raise ValueError
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def create_built_in_materials(G):
|
||||
|
在新工单中引用
屏蔽一个用户