Added a pre-commit config file and reformatted all the files accordingly by using it.

这个提交包含在:
Sai-Suraj-27
2023-06-26 16:09:39 +05:30
父节点 c71e87e34f
当前提交 f9dd7f2420
共有 155 个文件被更改,包括 11383 次插入8802 次删除

14
.pre-commit-config.yaml 普通文件
查看文件

@@ -0,0 +1,14 @@
# See https://pre-commit.com for more information
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
args: ["--line-length", "120"] # Adjust the max line length value as needed.

查看文件

@@ -24,4 +24,3 @@ dependencies:
- terminaltables
- tqdm
- git+https://github.com/craig-warren/PyEVTK.git

查看文件

@@ -50,4 +50,3 @@ Results
Input impedance (resistive and reactive) of a bowtie antenna in free space using FDTD (gprMax) and MoM (MATLAB) models.
The results from the FDTD and MoM modelling techniques are in very good agreement. The biggest mismatch occurs in the resistive part of the input impedance at frequencies above 3GHz.

查看文件

@@ -9,30 +9,26 @@
import re
import time
project = 'gprMax'
project = "gprMax"
copyright = f'2015-{time.strftime("%Y")}, The University of Edinburgh, United Kingdom. Authors: Craig Warren, Antonis Giannopoulos, and John Hartley'
author = 'Craig Warren, Antonis Giannopoulos, and John Hartley'
with open('../../gprMax/_version.py', 'r') as fd:
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
fd.read(), re.MULTILINE).group(1)
author = "Craig Warren, Antonis Giannopoulos, and John Hartley"
with open("../../gprMax/_version.py", "r") as fd:
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1)
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = ['sphinx.ext.mathjax',
'sphinx.ext.autodoc',
'sphinx.ext.napoleon']
extensions = ["sphinx.ext.mathjax", "sphinx.ext.autodoc", "sphinx.ext.napoleon"]
# Figure numbering
numfig = True
templates_path = ['_templates']
templates_path = ["_templates"]
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
html_theme = "sphinx_rtd_theme"
html_static_path = ["_static"]

查看文件

@@ -123,13 +123,3 @@ The performance of each PML can be compared with a reference solution using the
:linenos:
In lines 43-122 a dictionary with different PML formulations and parameters is created.

查看文件

@@ -27,7 +27,7 @@ z = 0.220
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name)
title = gprMax.Title(name=fn.with_suffix("").name)
domain = gprMax.Domain(p1=(x, y, z))
dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
time_window = gprMax.TimeWindow(time=6e-9)
@@ -39,19 +39,21 @@ scene.add(time_window)
# Import antenna model and add to model
ant_pos = (0.125, 0.094, 0.100)
gssi_objects = antenna_like_GSSI_1500(ant_pos[0], ant_pos[1], ant_pos[2],
resolution=dl)
gssi_objects = antenna_like_GSSI_1500(ant_pos[0], ant_pos[1], ant_pos[2], resolution=dl)
for obj in gssi_objects:
# obj.rotate('z', 90, origin=(ant_pos[0], ant_pos[1], ant_pos[2]))
scene.add(obj)
gv1 = gprMax.GeometryView(p1=(0, 0, 0), p2=(x, y, z),
dl=(dl, dl, dl), filename='antenna_like_GSSI_1500',
output_type='n')
gv2 = gprMax.GeometryView(p1=(ant_pos[0] - 0.170/2, ant_pos[1] - 0.108/2, ant_pos[2] - 0.050),
gv1 = gprMax.GeometryView(
p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), filename="antenna_like_GSSI_1500", output_type="n"
)
gv2 = gprMax.GeometryView(
p1=(ant_pos[0] - 0.170 / 2, ant_pos[1] - 0.108 / 2, ant_pos[2] - 0.050),
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')
dl=(dl, dl, dl),
filename="antenna_like_GSSI_1500_pcb",
output_type="f",
)
# scene.add(gv1)
# scene.add(gv2)

查看文件

@@ -13,7 +13,7 @@ dl = 0.001
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name)
title = gprMax.Title(name=fn.with_suffix("").name)
dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
pml = gprMax.PMLProps(thickness=14)
scene.add(title)
@@ -25,20 +25,14 @@ radii = np.linspace(0.1, 0.3, 20)
theta = np.linspace(3, 357, 60)
fs = np.array([0.040, 0.040, 0.040])
domain_size = np.array([2 * fs[0] + 0.170,
2 * fs[1] + 2 * radii[-1],
2 * fs[2] + 2 * radii[-1]])
domain_size = np.array([2 * fs[0] + 0.170, 2 * fs[1] + 2 * radii[-1], 2 * fs[2] + 2 * radii[-1]])
domain = gprMax.Domain(p1=(domain_size[0], domain_size[1], domain_size[2]))
time_window = gprMax.TimeWindow(time=timewindow)
scene.add(domain)
scene.add(time_window)
antennaposition = np.array([domain_size[0] / 2,
fs[1] + radii[-1],
fs[2] + radii[-1]])
gssi_objects = antenna_like_GSSI_1500(antennaposition[0],
antennaposition[1],
antennaposition[2])
antennaposition = np.array([domain_size[0] / 2, fs[1] + radii[-1], fs[2] + radii[-1]])
gssi_objects = antenna_like_GSSI_1500(antennaposition[0], antennaposition[1], antennaposition[2])
for obj in gssi_objects:
scene.add(obj)
@@ -54,14 +48,13 @@ for obj in gssi_objects:
# mixing_model_id=soil.id, id='mySoilBox')
# scene.add(fbox)
mat = gprMax.Material(er=5, se=0, mr=1, sm=0, id='er5')
mat = gprMax.Material(er=5, se=0, mr=1, sm=0, id="er5")
scene.add(mat)
box = gprMax.Box(p1=(0, 0, 0), p2=(domain_size[0], domain_size[1], fs[2] + radii[-1]),
material_id='er5')
box = gprMax.Box(p1=(0, 0, 0), p2=(domain_size[0], domain_size[1], fs[2] + radii[-1]), material_id="er5")
scene.add(box)
## Save the position of the antenna to file for use when processing results
np.savetxt(fn.with_suffix('').name + '_rxsorigin.txt', antennaposition, fmt="%f")
np.savetxt(fn.with_suffix("").name + "_rxsorigin.txt", antennaposition, fmt="%f")
## Generate receiver points for pattern
for radius in range(len(radii)):
@@ -70,14 +63,16 @@ for radius in range(len(radii)):
y = radii[radius] * np.sin(theta * np.pi / 180) * np.sin(90 * np.pi / 180)
z = radii[radius] * np.cos(theta * np.pi / 180)
for rxpt in range(len(theta)):
rx = gprMax.Rx(p1=(x[rxpt] + antennaposition[0],
y[rxpt] + antennaposition[1],
z[rxpt] + antennaposition[2]))
rx = gprMax.Rx(p1=(x[rxpt] + antennaposition[0], y[rxpt] + antennaposition[1], z[rxpt] + antennaposition[2]))
scene.add(rx)
gv1 = gprMax.GeometryView(p1=(0, 0, 0), p2=(domain_size[0], domain_size[1], domain_size[2]),
dl=(dl, dl, dl), filename='antenna_like_GSSI_1500_patterns',
output_type='n')
gv1 = gprMax.GeometryView(
p1=(0, 0, 0),
p2=(domain_size[0], domain_size[1], domain_size[2]),
dl=(dl, dl, dl),
filename="antenna_like_GSSI_1500_patterns",
output_type="n",
)
scene.add(gv1)
gprMax.run(scenes=[scene], geometry_only=True, outputfile=fn, gpu=None)

查看文件

@@ -25,7 +25,7 @@ z = 0.220
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name)
title = gprMax.Title(name=fn.with_suffix("").name)
domain = gprMax.Domain(p1=(x, y, z))
dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
time_window = gprMax.TimeWindow(time=6e-9)
@@ -37,8 +37,7 @@ scene.add(time_window)
# Import antenna model and add to model
ant_pos = (0.132, 0.095, 0.100)
mala_objects = antenna_like_MALA_1200(ant_pos[0], ant_pos[1], ant_pos[2],
resolution=dl)
mala_objects = antenna_like_MALA_1200(ant_pos[0], ant_pos[1], ant_pos[2], resolution=dl)
for obj in mala_objects:
scene.add(obj)

查看文件

@@ -14,7 +14,7 @@ import gprMax
fn = Path(__file__)
title = gprMax.Title(name=fn.with_suffix('').name)
title = gprMax.Title(name=fn.with_suffix("").name)
# title: antenna_wire_dipole_fs
domain = gprMax.Domain(p1=(0.050, 0.050, 0.200))
@@ -26,32 +26,29 @@ dxdydz = gprMax.Discretisation(p1=(0.001, 0.001, 0.001))
time_window = gprMax.TimeWindow(time=10e-9)
# time_window: 10e-9
waveform = gprMax.Waveform(wave_type='gaussian', amp=1, freq=1e9, id='mypulse')
waveform = gprMax.Waveform(wave_type="gaussian", amp=1, freq=1e9, id="mypulse")
# waveform: gaussian 1 1e9 mypulse
transmission_line = gprMax.TransmissionLine(polarisation='z',
p1=(0.025, 0.025, 0.100),
resistance=73,
waveform_id='mypulse')
transmission_line = gprMax.TransmissionLine(
polarisation="z", p1=(0.025, 0.025, 0.100), resistance=73, waveform_id="mypulse"
)
# transmission_line: z 0.025 0.025 0.100 73 mypulse
## 150mm length wire
e1 = gprMax.Edge(p1=(0.025, 0.025, 0.025),
p2=(0.025, 0.025, 0.175),
material_id='pec')
e1 = gprMax.Edge(p1=(0.025, 0.025, 0.025), p2=(0.025, 0.025, 0.175), material_id="pec")
# edge: 0.025 0.025 0.025 0.025 0.025 0.175 pec
## 1mm gap at centre of dipole
e2 = gprMax.Edge(p1=(0.025, 0.025, 0.100),
p2=(0.025, 0.025, 0.101),
material_id='free_space')
e2 = gprMax.Edge(p1=(0.025, 0.025, 0.100), p2=(0.025, 0.025, 0.101), material_id="free_space")
# edge: 0.025 0.025 0.100 0.025 0.025 0.101 free_space
gv = gprMax.GeometryView(p1=(0.020, 0.020, 0.020),
gv = gprMax.GeometryView(
p1=(0.020, 0.020, 0.020),
p2=(0.030, 0.030, 0.180),
dl=(0.001, 0.001, 0.001),
filename=fn.with_suffix('').name,
output_type='n')
filename=fn.with_suffix("").name,
output_type="n",
)
# geometry_view: 0.020 0.020 0.020 0.030 0.030 0.180 0.001 0.001 0.001 antenna_wire_dipole_fs f
# Create a scene

查看文件

@@ -36,7 +36,7 @@ scenes = []
for i in range(1, 55):
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name)
title = gprMax.Title(name=fn.with_suffix("").name)
domain = gprMax.Domain(p1=(x, y, z))
dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
time_window = gprMax.TimeWindow(time=6e-9)
@@ -46,22 +46,20 @@ for i in range(1, 55):
scene.add(dxdydz)
scene.add(time_window)
mat = gprMax.Material(er=6, se=0, mr=1, sm=0, id='half_space')
c1 = gprMax.Cylinder(p1=(0.240, 0, 0.080), p2=(0.240, 0.148, 0.080), r=0.010,
material_id='pec')
mat = gprMax.Material(er=6, se=0, mr=1, sm=0, id="half_space")
c1 = gprMax.Cylinder(p1=(0.240, 0, 0.080), p2=(0.240, 0.148, 0.080), r=0.010, material_id="pec")
scene.add(mat)
scene.add(c1)
# Import antenna model and add to model
ant_pos = (0.125, 0.094, 0.100)
gssi_objects = antenna_like_GSSI_1500(0.105 + i * 0.005, 0.074, 0.170,
resolution=dl)
gssi_objects = antenna_like_GSSI_1500(0.105 + i * 0.005, 0.074, 0.170, resolution=dl)
for obj in gssi_objects:
scene.add(obj)
gv1 = gprMax.GeometryView(p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl),
filename=fn.with_suffix('').name,
output_type='n')
gv1 = gprMax.GeometryView(
p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), filename=fn.with_suffix("").name, output_type="n"
)
# scene.add(gv1)
scenes.append(scene)

查看文件

@@ -39,9 +39,8 @@ dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
domain = gprMax.Domain(p1=(x, y, z))
time_window = gprMax.TimeWindow(time=tw)
wf = gprMax.Waveform(wave_type='gaussiandot', amp=1, freq=1.5e9, id='mypulse')
hd = gprMax.HertzianDipole(polarisation='z', p1=(0.205, 0.400, 0.250),
waveform_id='mypulse')
wf = gprMax.Waveform(wave_type="gaussiandot", amp=1, freq=1.5e9, id="mypulse")
hd = gprMax.HertzianDipole(polarisation="z", p1=(0.205, 0.400, 0.250), waveform_id="mypulse")
rx = gprMax.Rx(p1=(0.245, 0.400, 0.250))
scene.add(title)
@@ -60,36 +59,40 @@ sg1 = (c1[0] - r, c1[1] - r, c1[2])
sg2 = (c2[0] + r, c2[1] + r, c2[2])
# Create subgrid
subgrid = gprMax.SubGridHSG(p1=sg1, p2=sg2, ratio=ratio, id='sg')
subgrid = gprMax.SubGridHSG(p1=sg1, p2=sg2, ratio=ratio, id="sg")
scene.add(subgrid)
# Create water material
eri, er, tau, sig = calculate_water_properties()
water = gprMax.Material(er=eri, se=sig, mr=1, sm=0, id='water')
water = gprMax.Material(er=eri, se=sig, mr=1, sm=0, id="water")
subgrid.add(water)
water = gprMax.AddDebyeDispersion(poles=1, er_delta=[er - eri], tau=[tau], material_ids=['water'])
water = gprMax.AddDebyeDispersion(poles=1, er_delta=[er - eri], tau=[tau], material_ids=["water"])
subgrid.add(water)
# Add cylinder to subgrid
cylinder = gprMax.Cylinder(p1=c1, p2=c2, r=r, material_id='water')
cylinder = gprMax.Cylinder(p1=c1, p2=c2, r=r, material_id="water")
subgrid.add(cylinder)
# Create some geometry views for both subgrid and main grid
gvsg = gprMax.GeometryView(p1=sg1, p2=sg2, dl=(dl_sg, dl_sg, dl_sg),
filename=fn.with_suffix('').parts[-1] + '_sg',
output_type='n')
gvsg = gprMax.GeometryView(
p1=sg1, p2=sg2, dl=(dl_sg, dl_sg, dl_sg), filename=fn.with_suffix("").parts[-1] + "_sg", output_type="n"
)
subgrid.add(gvsg)
gv1 = gprMax.GeometryView(p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl),
filename=fn.with_suffix('').parts[-1],
output_type='n')
gv1 = gprMax.GeometryView(
p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), filename=fn.with_suffix("").parts[-1], output_type="n"
)
scene.add(gv1)
# Create some snapshots of entire domain
for i in range(5):
s = gprMax.Snapshot(p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl),
s = gprMax.Snapshot(
p1=(0, 0, 0),
p2=(x, y, z),
dl=(dl, dl, dl),
time=(i + 0.5) * 1e-9,
filename=fn.with_suffix('').parts[-1] + '_' + str(i + 1))
filename=fn.with_suffix("").parts[-1] + "_" + str(i + 1),
)
scene.add(s)
gprMax.run(scenes=[scene], n=1, geometry_only=False, outputfile=fn, subgrid=True, autotranslate=True)

查看文件

@@ -72,15 +72,13 @@ sg_y1 = antenna_p[1] + antenna_case[1] / 2 + bounding_box
sg_z1 = antenna_p[2] + antenna_case[2] + bounding_box
# Create subgrid
sg = gprMax.SubGridHSG(p1=[sg_x0, sg_y0, sg_z0],
p2=[sg_x1, sg_y1, sg_z1],
ratio=ratio, id='sg')
sg = gprMax.SubGridHSG(p1=[sg_x0, sg_y0, sg_z0], p2=[sg_x1, sg_y1, sg_z1], ratio=ratio, id="sg")
scene.add(sg)
# Create and add a box of homogeneous material to main grid - sandy_soil
sandy_soil = gprMax.Material(er=3.2, se=0.397e-3, mr=1, sm=0, id='sandy_soil')
sandy_soil = gprMax.Material(er=3.2, se=0.397e-3, mr=1, sm=0, id="sandy_soil")
scene.add(sandy_soil)
b1 = gprMax.Box(p1=(0, 0, 0), p2=(x, y, antenna_p[2]), material_id='sandy_soil')
b1 = gprMax.Box(p1=(0, 0, 0), p2=(x, y, antenna_p[2]), material_id="sandy_soil")
scene.add(b1)
# Position box of sandy_soil in the subgrid.
@@ -101,8 +99,7 @@ h = antenna_p[2] - sg_z0 + (ps + pc + isos) * dl_sg
# Create and add a box of homogeneous material to subgrid - sandy_soil
sg.add(sandy_soil)
b2 = gprMax.Box(p1=(0, 0, 0),
p2=(411 * dl_sg, 411 * dl_sg, h), material_id='sandy_soil')
b2 = gprMax.Box(p1=(0, 0, 0), p2=(411 * dl_sg, 411 * dl_sg, h), material_id="sandy_soil")
# Set autotranslate for the box object to false
b2.autotranslate = False
sg.add(b2)
@@ -113,39 +110,49 @@ for obj in gssi_objects:
sg.add(obj)
# Create and add a homogeneous material with a rough surface
soil = gprMax.Material(er=9, se=0.397e-3, mr=1, sm=0, id='soil')
soil = gprMax.Material(er=9, se=0.397e-3, mr=1, sm=0, id="soil")
scene.add(soil)
fb = gprMax.FractalBox(p1=(0, 0, 0), p2=(3, 1, 1), frac_dim=1.5,
weighting=(1, 1, 1), n_materials=1, mixing_model_id='soil', id='fbox', seed=1)
fb = gprMax.FractalBox(
p1=(0, 0, 0),
p2=(3, 1, 1),
frac_dim=1.5,
weighting=(1, 1, 1),
n_materials=1,
mixing_model_id="soil",
id="fbox",
seed=1,
)
scene.add(fb)
rough_surf = gprMax.AddSurfaceRoughness(p1=(0, 0, 1), p2=(3, 1, 1),
frac_dim=1.5, weighting=(1, 1),
limits=(0.4, 1.2),
fractal_box_id='fbox', seed=1)
rough_surf = gprMax.AddSurfaceRoughness(
p1=(0, 0, 1), p2=(3, 1, 1), frac_dim=1.5, weighting=(1, 1), limits=(0.4, 1.2), fractal_box_id="fbox", seed=1
)
scene.add(rough_surf)
# Create some snapshots and geometry views
for i in range(1, 51):
snap = gprMax.Snapshot(p1=(0, y / 2, 0),
snap = gprMax.Snapshot(
p1=(0, y / 2, 0),
p2=(x, y / 2 + dl, z),
dl=(dl, dl, dl),
filename=Path(*parts[:-1], f'{parts[-1]}_{str(i)}').name,
time=i * tw / 50,)
filename=Path(*parts[:-1], f"{parts[-1]}_{str(i)}").name,
time=i * tw / 50,
)
scene.add(snap)
gvsg = gprMax.GeometryView(p1=(sg_x0, sg_y0, sg_z0), p2=(sg_x1, sg_y1, sg_z1),
gvsg = gprMax.GeometryView(
p1=(sg_x0, sg_y0, sg_z0),
p2=(sg_x1, sg_y1, sg_z1),
dl=(dl_sg, dl_sg, dl_sg),
filename=fn.with_suffix('').parts[-1] + '_sg',
output_type='n')
filename=fn.with_suffix("").parts[-1] + "_sg",
output_type="n",
)
sg.add(gvsg)
gv1 = gprMax.GeometryView(p1=(0, 0, 0),
p2=domain.props.p1,
dl=dl,
filename=fn.with_suffix('').parts[-1],
output_type='n')
gv1 = gprMax.GeometryView(
p1=(0, 0, 0), p2=domain.props.p1, dl=dl, filename=fn.with_suffix("").parts[-1], output_type="n"
)
scene.add(gv1)
gprMax.run(scenes=[scene], n=1, geometry_only=True, outputfile=fn, subgrid=True, autotranslate=True)

查看文件

@@ -22,18 +22,41 @@ from .cmds_geometry.geometry_objects_read import GeometryObjectsRead
from .cmds_geometry.plate import Plate
from .cmds_geometry.sphere import Sphere
from .cmds_geometry.triangle import Triangle
from .cmds_multiuse import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion,
AddLorentzDispersion, GeometryObjectsWrite,
GeometryView, HertzianDipole, MagneticDipole,
Material, MaterialList, MaterialRange, Rx, RxArray,
Snapshot, SoilPeplinski, TransmissionLine,
VoltageSource, Waveform)
from .cmds_singleuse import (Discretisation, Domain, ExcitationFile,
OMPThreads, PMLProps, RxSteps, SrcSteps,
TimeStepStabilityFactor, TimeWindow, Title)
from .cmds_multiuse import (
PMLCFS,
AddDebyeDispersion,
AddDrudeDispersion,
AddLorentzDispersion,
GeometryObjectsWrite,
GeometryView,
HertzianDipole,
MagneticDipole,
Material,
MaterialList,
MaterialRange,
Rx,
RxArray,
Snapshot,
SoilPeplinski,
TransmissionLine,
VoltageSource,
Waveform,
)
from .cmds_singleuse import (
Discretisation,
Domain,
ExcitationFile,
OMPThreads,
PMLProps,
RxSteps,
SrcSteps,
TimeStepStabilityFactor,
TimeWindow,
Title,
)
from .gprMax import run as run
from .hash_cmds_file import user_libs_fn_to_scene_obj
from .scene import Scene
from .subgrids.user_objects import SubGridHSG
__name__ = 'gprMax'
__name__ = "gprMax"

查看文件

@@ -2,7 +2,7 @@
import gprMax.gprMax
if __name__ == '__main__':
if __name__ == "__main__":
gprMax.gprMax.cli()
# Code profiling

查看文件

@@ -1,4 +1,4 @@
# This is where the version number is set and read by setup.py and conf.py (for the docs)
__version__ = '4.0.0b0'
codename = 'Càrn Mòr'
__version__ = "4.0.0b0"
codename = "Càrn Mòr"

查看文件

@@ -48,7 +48,7 @@ class AddGrass(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#add_grass'
self.hash = "#add_grass"
def rotate(self, axis, angle, origin=None):
"""Set parameters for rotation."""
@@ -59,26 +59,26 @@ class AddGrass(UserObjectGeometry):
def _do_rotate(self):
"""Perform rotation."""
pts = np.array([self.kwargs['p1'], self.kwargs['p2']])
pts = np.array([self.kwargs["p1"], self.kwargs["p2"]])
rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin)
self.kwargs['p1'] = tuple(rot_pts[0, :])
self.kwargs['p2'] = tuple(rot_pts[1, :])
self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip):
"""Add Grass to fractal box."""
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
fractal_box_id = self.kwargs['fractal_box_id']
frac_dim = self.kwargs['frac_dim']
limits = self.kwargs['limits']
n_blades = self.kwargs['n_blades']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
fractal_box_id = self.kwargs["fractal_box_id"]
frac_dim = self.kwargs["frac_dim"]
limits = self.kwargs["limits"]
n_blades = self.kwargs["n_blades"]
except KeyError:
logger.exception(f'{self.__str__()} requires at least eleven parameters')
logger.exception(f"{self.__str__()} requires at least eleven parameters")
raise
try:
seed = self.kwargs['seed']
seed = self.kwargs["seed"]
except KeyError:
seed = None
@@ -90,7 +90,7 @@ class AddGrass(UserObjectGeometry):
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__())
@@ -98,87 +98,96 @@ 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(f'{self.__str__()} requires a positive value for ' +
'the minimum and maximum heights for grass blades')
logger.exception(
f"{self.__str__()} requires a positive value for " + "the minimum and maximum heights for grass blades"
)
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 != volume.xs and 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))
# xminus surface
if xs == volume.xs:
logger.exception(f'{self.__str__()} grass can only be specified ' +
'on surfaces in the positive axis direction')
logger.exception(
f"{self.__str__()} grass can only be specified " + "on surfaces in the positive axis direction"
)
raise ValueError
# xplus surface
elif xf == volume.xf:
if fractalrange[1] > grid.nx:
logger.exception(f'{self.__str__()} cannot apply grass to ' +
'fractal box as it would exceed the domain ' +
'size in the x direction')
logger.exception(
f"{self.__str__()} cannot apply grass to "
+ "fractal box as it would exceed the domain "
+ "size in the x direction"
)
raise ValueError
requestedsurface = 'xplus'
requestedsurface = "xplus"
elif ys == yf:
if xs == xf 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 ys != volume.ys and 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))
# yminus surface
if ys == volume.ys:
logger.exception(f'{self.__str__()} grass can only be specified ' +
'on surfaces in the positive axis direction')
logger.exception(
f"{self.__str__()} grass can only be specified " + "on surfaces in the positive axis direction"
)
raise ValueError
# yplus surface
elif yf == volume.yf:
if fractalrange[1] > grid.ny:
logger.exception(f'{self.__str__()} cannot apply grass to ' +
'fractal box as it would exceed the domain ' +
'size in the y direction')
logger.exception(
f"{self.__str__()} cannot apply grass to "
+ "fractal box as it would exceed the domain "
+ "size in the y direction"
)
raise ValueError
requestedsurface = 'yplus'
requestedsurface = "yplus"
elif zs == zf:
if xs == xf or ys == yf:
logger.exception(f'{self.__str__()} dimensions are not specified correctly')
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if zs != volume.zs and 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))
# zminus surface
if zs == volume.zs:
logger.exception(f'{self.__str__()} grass can only be specified ' +
'on surfaces in the positive axis direction')
logger.exception(
f"{self.__str__()} grass can only be specified " + "on surfaces in the positive axis direction"
)
raise ValueError
# zplus surface
elif zf == volume.zf:
if fractalrange[1] > grid.nz:
logger.exception(f'{self.__str__()} cannot apply grass to ' +
'fractal box as it would exceed the domain ' +
'size in the z direction')
logger.exception(
f"{self.__str__()} cannot apply grass to "
+ "fractal box as it would exceed the domain "
+ "size in the z direction"
)
raise ValueError
requestedsurface = 'zplus'
requestedsurface = "zplus"
else:
logger.exception(f'{self.__str__()} dimensions are not specified correctly')
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim)
surface.ID = 'grass'
surface.ID = "grass"
surface.surfaceID = requestedsurface
surface.seed = seed
@@ -187,8 +196,10 @@ class AddGrass(UserObjectGeometry):
surface.operatingonID = volume.ID
surface.generate_fractal_surface()
if n_blades > surface.fractalsurface.shape[0] * surface.fractalsurface.shape[1]:
logger.exception(f'{self.__str__()} the specified surface is not large ' +
'enough for the number of grass blades/roots specified')
logger.exception(
f"{self.__str__()} the specified surface is not large "
+ "enough for the number of grass blades/roots specified"
)
raise ValueError
# Scale the distribution so that the summation is equal to one,
@@ -206,20 +217,20 @@ class AddGrass(UserObjectGeometry):
# Locate the random numbers in the bins created by the 1D vector of
# 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]))
bladesindex = np.unravel_index(
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
surface.fractalrange = fractalrange
# 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)
surface.fractalsurface[bladesindex[0][i], bladesindex[1][i]] = R.randint(
surface.fractalrange[0], surface.fractalrange[1], size=1
)
# Create grass geometry parameters
g = Grass(n_blades)
@@ -227,22 +238,26 @@ class AddGrass(UserObjectGeometry):
surface.grass.append(g)
# Check to see if grass has been already defined as a material
if not any(x.ID == 'grass' for x in grid.materials):
if not any(x.ID == "grass" for x in grid.materials):
create_grass(grid)
# Check if time step for model is suitable for using grass
grass = next((x for x in grid.materials if x.ID == 'grass'))
grass = next((x for x in grid.materials if x.ID == "grass"))
testgrass = next((x for x in grass.tau if x < grid.dt), None)
if testgrass:
logger.exception(f'{self.__str__()} requires the time step for the ' +
'model to be less than the relaxation time required to model grass.')
logger.exception(
f"{self.__str__()} requires the time step for the "
+ "model to be less than the relaxation time required to model grass."
)
raise ValueError
volume.fractalsurfaces.append(surface)
logger.info(f'{self.grid_name(grid)}{n_blades} blades of grass on surface from ' +
f'{xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, ' +
f'to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m ' +
f'with fractal dimension {surface.dimension:g}, fractal seeding ' +
f'{surface.seed}, and range {limits[0]:g}m to {limits[1]:g}m, ' +
f'added to {surface.operatingonID}.')
logger.info(
f"{self.grid_name(grid)}{n_blades} blades of grass on surface from "
+ f"{xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, "
+ f"to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m "
+ f"with fractal dimension {surface.dimension:g}, fractal seeding "
+ f"{surface.seed}, and range {limits[0]:g}m to {limits[1]:g}m, "
+ f"added to {surface.operatingonID}."
)

查看文件

@@ -49,7 +49,7 @@ class AddSurfaceRoughness(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#add_surface_roughness'
self.hash = "#add_surface_roughness"
def rotate(self, axis, angle, origin=None):
"""Set parameters for rotation."""
@@ -60,29 +60,31 @@ class AddSurfaceRoughness(UserObjectGeometry):
def _do_rotate(self):
"""Perform rotation."""
pts = np.array([self.kwargs['p1'], self.kwargs['p2']])
pts = np.array([self.kwargs["p1"], self.kwargs["p2"]])
rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin)
self.kwargs['p1'] = tuple(rot_pts[0, :])
self.kwargs['p2'] = tuple(rot_pts[1, :])
self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
frac_dim = self.kwargs['frac_dim']
weighting = np.array(self.kwargs['weighting'], dtype=np.float64)
limits = np.array(self.kwargs['limits'])
fractal_box_id = self.kwargs['fractal_box_id']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
frac_dim = self.kwargs["frac_dim"]
weighting = np.array(self.kwargs["weighting"], dtype=np.float64)
limits = np.array(self.kwargs["limits"])
fractal_box_id = self.kwargs["fractal_box_id"]
except KeyError:
logger.exception(f'{self.__str__()} incorrect parameters')
logger.exception(f"{self.__str__()} incorrect parameters")
raise
try:
seed = self.kwargs['seed']
seed = self.kwargs["seed"]
except KeyError:
logger.warning(f'{self.__str__()} no value for seed detected. This ' +
'means you will get a different fractal distribution ' +
'every time the model runs.')
logger.warning(
f"{self.__str__()} no value for seed detected. This "
+ "means you will get a different fractal distribution "
+ "every time the model runs."
)
seed = None
if self.do_rotate:
@@ -93,7 +95,7 @@ class AddSurfaceRoughness(UserObjectGeometry):
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__())
@@ -101,108 +103,117 @@ 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(f'{self.__str__()} requires a positive value for the ' +
'fractal weighting in the first direction of the surface')
logger.exception(
f"{self.__str__()} requires a positive value for the "
+ "fractal weighting in the first direction of the surface"
)
raise ValueError
if weighting[1] < 0:
logger.exception(f'{self.__str__()} requires a positive value for the ' +
'fractal weighting in the second direction of the surface')
logger.exception(
f"{self.__str__()} requires a positive value for the "
+ "fractal weighting in the second direction of the surface"
)
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 != volume.xs and 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:
logger.exception(f'{self.__str__()} cannot apply fractal surface ' +
'to fractal box as it would exceed either the ' +
'upper coordinates of the fractal box or the ' +
'domain in the x direction')
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
+ "to fractal box as it would exceed either the "
+ "upper coordinates of the fractal box or the "
+ "domain in the x direction"
)
raise ValueError
requestedsurface = 'xminus'
requestedsurface = "xminus"
# xplus surface
elif xf == volume.xf:
if fractalrange[0] < volume.xs or fractalrange[1] > grid.nx:
logger.exception(f'{self.__str__()} cannot apply fractal surface ' +
'to fractal box as it would exceed either the ' +
'lower coordinates of the fractal box or the ' +
'domain in the x direction')
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
+ "to fractal box as it would exceed either the "
+ "lower coordinates of the fractal box or the "
+ "domain in the x direction"
)
raise ValueError
requestedsurface = 'xplus'
requestedsurface = "xplus"
elif ys == yf:
if xs == xf 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 ys != volume.ys and 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:
logger.exception(f'{self.__str__()} cannot apply fractal surface ' +
'to fractal box as it would exceed either the ' +
'upper coordinates of the fractal box or the ' +
'domain in the y direction')
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
+ "to fractal box as it would exceed either the "
+ "upper coordinates of the fractal box or the "
+ "domain in the y direction"
)
raise ValueError
requestedsurface = 'yminus'
requestedsurface = "yminus"
# yplus surface
elif yf == volume.yf:
if fractalrange[0] < volume.ys or fractalrange[1] > grid.ny:
logger.exception(f'{self.__str__()} cannot apply fractal surface ' +
'to fractal box as it would exceed either the ' +
'lower coordinates of the fractal box or the ' +
'domain in the y direction')
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
+ "to fractal box as it would exceed either the "
+ "lower coordinates of the fractal box or the "
+ "domain in the y direction"
)
raise ValueError
requestedsurface = 'yplus'
requestedsurface = "yplus"
elif zs == zf:
if xs == xf or ys == yf:
logger.exception(f'{self.__str__()} dimensions are not specified correctly')
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
if zs != volume.zs and 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:
logger.exception(f'{self.__str__()} cannot apply fractal surface ' +
'to fractal box as it would exceed either the ' +
'upper coordinates of the fractal box or the ' +
'domain in the x direction')
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
+ "to fractal box as it would exceed either the "
+ "upper coordinates of the fractal box or the "
+ "domain in the x direction"
)
raise ValueError
requestedsurface = 'zminus'
requestedsurface = "zminus"
# zplus surface
elif zf == volume.zf:
if fractalrange[0] < volume.zs or fractalrange[1] > grid.nz:
logger.exception(f'{self.__str__()} cannot apply fractal surface ' +
'to fractal box as it would exceed either the ' +
'lower coordinates of the fractal box or the ' +
'domain in the z direction')
logger.exception(
f"{self.__str__()} cannot apply fractal surface "
+ "to fractal box as it would exceed either the "
+ "lower coordinates of the fractal box or the "
+ "domain in the z direction"
)
raise ValueError
requestedsurface = 'zplus'
requestedsurface = "zplus"
else:
logger.exception(f'{self.__str__()} dimensions are not specified correctly')
logger.exception(f"{self.__str__()} dimensions are not specified correctly")
raise ValueError
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim)
@@ -218,17 +229,18 @@ 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 ' +
f'{surface.surfaceID} surface')
logger.exception(f"{self.__str__()} has already been used on the " + f"{surface.surfaceID} surface")
raise ValueError
surface.generate_fractal_surface()
volume.fractalsurfaces.append(surface)
logger.info(f'{self.grid_name(grid)}Fractal surface from {xs * grid.dx:g}m, ' +
f'{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, ' +
f'{yf * grid.dy:g}m, {zf * grid.dz:g}m with fractal dimension ' +
f'{surface.dimension:g}, fractal weightings {surface.weighting[0]:g}, ' +
f'{surface.weighting[1]:g}, fractal seeding {surface.seed}, ' +
f'and range {limits[0]:g}m to {limits[1]:g}m, added to ' +
f'{surface.operatingonID}.')
logger.info(
f"{self.grid_name(grid)}Fractal surface from {xs * grid.dx:g}m, "
+ f"{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, "
+ f"{yf * grid.dy:g}m, {zf * grid.dz:g}m with fractal dimension "
+ f"{surface.dimension:g}, fractal weightings {surface.weighting[0]:g}, "
+ f"{surface.weighting[1]:g}, fractal seeding {surface.seed}, "
+ f"and range {limits[0]:g}m to {limits[1]:g}m, added to "
+ f"{surface.operatingonID}."
)

查看文件

@@ -44,7 +44,7 @@ class AddSurfaceWater(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#add_surface_water'
self.hash = "#add_surface_water"
def rotate(self, axis, angle, origin=None):
"""Set parameters for rotation."""
@@ -55,20 +55,20 @@ class AddSurfaceWater(UserObjectGeometry):
def _do_rotate(self):
"""Perform rotation."""
pts = np.array([self.kwargs['p1'], self.kwargs['p2']])
pts = np.array([self.kwargs["p1"], self.kwargs["p2"]])
rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin)
self.kwargs['p1'] = tuple(rot_pts[0, :])
self.kwargs['p2'] = tuple(rot_pts[1, :])
self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip):
""" "Create surface water on fractal box."""
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
fractal_box_id = self.kwargs['fractal_box_id']
depth = self.kwargs['depth']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
fractal_box_id = self.kwargs["fractal_box_id"]
depth = self.kwargs["depth"]
except KeyError:
logger.exception(f'{self.__str__()} requires exactly eight parameters')
logger.exception(f"{self.__str__()} requires exactly eight parameters")
raise
if self.do_rotate:
@@ -77,7 +77,7 @@ class AddSurfaceWater(UserObjectGeometry):
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,89 +85,92 @@ class AddSurfaceWater(UserObjectGeometry):
xf, yf, zf = p2
if depth <= 0:
logger.exception(f'{self.__str__()} requires a positive value for the ' +
f'depth of water')
logger.exception(f"{self.__str__()} requires a positive value for the " + f"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 '
f'of a fractal box')
logger.exception(f"{self.__str__()} can only be used on the external surfaces " f"of a fractal box")
raise ValueError
# xminus surface
if xs == volume.xs:
requestedsurface = 'xminus'
requestedsurface = "xminus"
# xplus surface
elif xf == volume.xf:
requestedsurface = 'xplus'
requestedsurface = "xplus"
filldepthcells = round_value(depth / grid.dx)
filldepth = filldepthcells * grid.dx
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 ' +
f'of a fractal box')
logger.exception(f"{self.__str__()} can only be used on the external surfaces " + f"of a fractal box")
raise ValueError
# yminus surface
if ys == volume.ys:
requestedsurface = 'yminus'
requestedsurface = "yminus"
# yplus surface
elif yf == volume.yf:
requestedsurface = 'yplus'
requestedsurface = "yplus"
filldepthcells = round_value(depth / grid.dy)
filldepth = filldepthcells * grid.dy
elif zs == zf:
if zs not in [volume.zs, volume.zf]:
logger.exception(f'{self.__str__()} can only be used on the external surfaces '
f'of a fractal box')
logger.exception(f"{self.__str__()} can only be used on the external surfaces " f"of a fractal box")
raise ValueError
# zminus surface
if zs == volume.zs:
requestedsurface = 'zminus'
requestedsurface = "zminus"
# zplus surface
elif zf == volume.zf:
requestedsurface = 'zplus'
requestedsurface = "zplus"
filldepthcells = round_value(depth / grid.dz)
filldepth = filldepthcells * grid.dz
else:
logger.exception(f'{self.__str__()} dimensions are not specified correctly')
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)
if not surface:
logger.exception(f'{self.__str__()} specified surface {requestedsurface} ' +
f'does not have a rough surface applied')
logger.exception(
f"{self.__str__()} specified surface {requestedsurface} " + f"does not have a rough surface applied"
)
raise ValueError
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]:
logger.exception(f'{self.__str__()} requires a value for the depth of water that lies with the ' +
f'range of the requested surface roughness')
logger.exception(
f"{self.__str__()} requires a value for the depth of water that lies with the "
+ f"range of the requested surface roughness"
)
raise ValueError
# Check to see if water has been already defined as a material
if all(x.ID != 'water' for x in grid.materials):
if all(x.ID != "water" for x in grid.materials):
create_water(grid)
# Check if time step for model is suitable for using water
water = next((x for x in grid.materials if x.ID == 'water'))
water = next((x for x in grid.materials if x.ID == "water"))
if testwater := next((x for x in water.tau if x < grid.dt), None):
logger.exception(f'{self.__str__()} requires the time step for the model '
f'to be less than the relaxation time required to model water.')
logger.exception(
f"{self.__str__()} requires the time step for the model "
f"to be less than the relaxation time required to model water."
)
raise ValueError
logger.info(f'{self.grid_name(grid)}Water on surface from {xs * grid.dx:g}m, ' +
f'{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, ' +
f'{yf * grid.dy:g}m, {zf * grid.dz:g}m with depth {filldepth:g}m, ' +
f'added to {surface.operatingonID}.')
logger.info(
f"{self.grid_name(grid)}Water on surface from {xs * grid.dx:g}m, "
+ f"{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, "
+ f"{yf * grid.dy:g}m, {zf * grid.dz:g}m with depth {filldepth:g}m, "
+ f"added to {surface.operatingonID}."
)

查看文件

@@ -44,7 +44,7 @@ class Box(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#box'
self.hash = "#box"
def rotate(self, axis, angle, origin=None):
"""Set parameters for rotation."""
@@ -55,17 +55,17 @@ class Box(UserObjectGeometry):
def _do_rotate(self):
"""Perform rotation."""
pts = np.array([self.kwargs['p1'], self.kwargs['p2']])
pts = np.array([self.kwargs["p1"], self.kwargs["p2"]])
rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin)
self.kwargs['p1'] = tuple(rot_pts[0, :])
self.kwargs['p2'] = tuple(rot_pts[1, :])
self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
except KeyError:
logger.exception(f'{self.__str__()} Please specify two points.')
logger.exception(f"{self.__str__()} Please specify two points.")
raise
if self.do_rotate:
@@ -74,19 +74,19 @@ class Box(UserObjectGeometry):
# Check materials have been specified
# Isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
materialsrequested = [self.kwargs["material_id"]]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
materialsrequested = self.kwargs["material_ids"]
except KeyError:
logger.exception(f'{self.__str__()} No materials have been specified')
logger.exception(f"{self.__str__()} No materials have been specified")
raise
# Check averaging
try:
# Try user-specified averaging
averagebox = self.kwargs['averaging']
averagebox = self.kwargs["averaging"]
except KeyError:
# Otherwise go with the grid default
averagebox = grid.averagevolumeobjects
@@ -103,7 +103,7 @@ class Box(UserObjectGeometry):
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
logger.exception(f'{self.__str__()} material(s) {notfound} do not exist')
logger.exception(f"{self.__str__()} material(s) {notfound} do not exist")
raise ValueError
# Isotropic case
@@ -117,34 +117,47 @@ class Box(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
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
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)
build_box(xs, xf, ys, yf, zs, zf, config.get_model_config().ompthreads,
numID, numIDx, numIDy, numIDz, averaging, grid.solid,
grid.rigidE, grid.rigidH, grid.ID)
build_box(
xs,
xf,
ys,
yf,
zs,
zf,
config.get_model_config().ompthreads,
numID,
numIDx,
numIDy,
numIDz,
averaging,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)
dielectricsmoothing = 'on' if averaging else 'off'
dielectricsmoothing = "on" if averaging else "off"
logger.info(f"{self.grid_name(grid)}Box from {p5[0]:g}m, {p5[1]:g}m, " +
f"{p5[2]:g}m, to {p6[0]:g}m, {p6[1]:g}m, {p6[2]:g}m of " +
f"material(s) {', '.join(materialsrequested)} created, " +
f"dielectric smoothing is {dielectricsmoothing}.")
logger.info(
f"{self.grid_name(grid)}Box from {p5[0]:g}m, {p5[1]:g}m, "
+ f"{p5[2]:g}m, to {p6[0]:g}m, {p6[1]:g}m, {p6[2]:g}m of "
+ f"material(s) {', '.join(materialsrequested)} created, "
+ f"dielectric smoothing is {dielectricsmoothing}."
)

查看文件

@@ -19,52 +19,53 @@
from jinja2 import Environment, PackageLoader
env = Environment(
loader=PackageLoader(__name__, 'templates'),
loader=PackageLoader(__name__, "templates"),
)
template = env.get_template('fields_updates_dispersive_template')
template = env.get_template("fields_updates_dispersive_template")
r = template.render(
functions=[
# name, double, real
{
'name_a': 'update_electric_dispersive_multipole_A_double_real',
'name_b': 'update_electric_dispersive_multipole_B_double_real',
'name_a_1': 'update_electric_dispersive_1pole_A_double_real',
'name_b_1': 'update_electric_dispersive_1pole_B_double_real',
'field_type': 'double',
'dispersive_type': 'double'
"name_a": "update_electric_dispersive_multipole_A_double_real",
"name_b": "update_electric_dispersive_multipole_B_double_real",
"name_a_1": "update_electric_dispersive_1pole_A_double_real",
"name_b_1": "update_electric_dispersive_1pole_B_double_real",
"field_type": "double",
"dispersive_type": "double",
},
# name, float, real
{
'name_a': 'update_electric_dispersive_multipole_A_float_real',
'name_b': 'update_electric_dispersive_multipole_B_float_real',
'name_a_1': 'update_electric_dispersive_1pole_A_float_real',
'name_b_1': 'update_electric_dispersive_1pole_B_float_real',
'field_type': 'float',
'dispersive_type': 'float'
"name_a": "update_electric_dispersive_multipole_A_float_real",
"name_b": "update_electric_dispersive_multipole_B_float_real",
"name_a_1": "update_electric_dispersive_1pole_A_float_real",
"name_b_1": "update_electric_dispersive_1pole_B_float_real",
"field_type": "float",
"dispersive_type": "float",
},
# name, double, complex
{
'name_a': 'update_electric_dispersive_multipole_A_double_complex',
'name_b': 'update_electric_dispersive_multipole_B_double_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_double_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_double_complex',
'field_type': 'double',
'dispersive_type': 'double complex',
'real_part': 'creal'
"name_a": "update_electric_dispersive_multipole_A_double_complex",
"name_b": "update_electric_dispersive_multipole_B_double_complex",
"name_a_1": "update_electric_dispersive_1pole_A_double_complex",
"name_b_1": "update_electric_dispersive_1pole_B_double_complex",
"field_type": "double",
"dispersive_type": "double complex",
"real_part": "creal",
},
# name, float, complex
{
'name_a': 'update_electric_dispersive_multipole_A_float_complex',
'name_b': 'update_electric_dispersive_multipole_B_float_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_float_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_float_complex',
'field_type': 'float',
'dispersive_type': 'float complex',
'real_part': 'crealf'
}]
"name_a": "update_electric_dispersive_multipole_A_float_complex",
"name_b": "update_electric_dispersive_multipole_B_float_complex",
"name_a_1": "update_electric_dispersive_1pole_A_float_complex",
"name_b_1": "update_electric_dispersive_1pole_B_float_complex",
"field_type": "float",
"dispersive_type": "float complex",
"real_part": "crealf",
},
]
)
with open('cython/dispersive_updates_test.pyx', 'w') as f:
with open("cython/dispersive_updates_test.pyx", "w") as f:
f.write(r)

查看文件

@@ -30,19 +30,19 @@ class UserObjectGeometry:
def __init__(self, **kwargs):
self.kwargs = kwargs
self.hash = '#example'
self.hash = "#example"
self.autotranslate = True
self.do_rotate = False
def __str__(self):
"""Readable string of parameters given to object."""
s = ''
s = ""
for _, v in self.kwargs.items():
if isinstance(v, (tuple, list)):
v = ' '.join([str(el) for el in v])
s += f'{str(v)} '
v = " ".join([str(el) for el in v])
s += f"{str(v)} "
return f'{self.hash}: {s[:-1]}'
return f"{self.hash}: {s[:-1]}"
def create(self, grid, uip):
"""Creates object and adds it to the grid."""
@@ -57,10 +57,10 @@ class UserObjectGeometry:
string if the grid is the main grid.
"""
if config.sim_config.general['subgrid'] and grid.name != 'main_grid':
return f'[{grid.name}] '
if config.sim_config.general["subgrid"] and grid.name != "main_grid":
return f"[{grid.name}] "
else:
return ''
return ""
def rotate_point(p, axis, angle, origin=(0, 0, 0)):
@@ -113,15 +113,15 @@ def rotate_2point_object(pts, axis, angle, origin=None):
# Check angle value is suitable
angle = int(angle)
if angle < 0 or angle > 360:
logger.exception('Angle of rotation must be between 0-360 degrees')
logger.exception("Angle of rotation must be between 0-360 degrees")
raise ValueError
if angle % 90 != 0:
logger.exception('Angle of rotation must be a multiple of 90 degrees')
logger.exception("Angle of rotation must be a multiple of 90 degrees")
raise ValueError
# Check axis is valid
if axis not in ['x', 'y', 'z']:
logger.exception('Axis of rotation must be x, y, or z')
if axis not in ["x", "y", "z"]:
logger.exception("Axis of rotation must be x, y, or z")
raise ValueError
# Save original points
@@ -138,13 +138,13 @@ def rotate_2point_object(pts, axis, angle, origin=None):
# Reset coordinates of invariant direction
# - only needed for 2D models, has no effect on 3D models.
if axis =='x':
if axis == "x":
new_pts[0, 0] = orig_pts[0, 0]
new_pts[1, 0] = orig_pts[1, 0]
elif axis == 'y':
elif axis == "y":
new_pts[0, 1] = orig_pts[0, 1]
new_pts[1, 1] = orig_pts[1, 1]
elif axis == 'z':
elif axis == "z":
new_pts[0, 2] = orig_pts[0, 2]
new_pts[1, 2] = orig_pts[1, 2]
@@ -166,26 +166,26 @@ def rotate_polarisation(p, polarisation, axis, angle, G):
new_polarisation: string defining the new polarisation (x, y, or z).
"""
if polarisation.lower() == 'x':
if polarisation.lower() == "x":
new_pt = (p[0] + G.dx, p[1], p[2])
if axis == 'y' and angle == 90 or angle == 270:
new_polarisation = 'z'
if axis == 'z' and angle == 90 or angle == 270:
new_polarisation = 'y'
if axis == "y" and angle == 90 or angle == 270:
new_polarisation = "z"
if axis == "z" and angle == 90 or angle == 270:
new_polarisation = "y"
elif polarisation.lower() == 'y':
elif polarisation.lower() == "y":
new_pt = (p[0], p[1] + G.dy, p[2])
if axis == 'x' and angle == 90 or angle == 270:
new_polarisation = 'z'
if axis == 'z' and angle == 90 or angle == 270:
new_polarisation = 'x'
if axis == "x" and angle == 90 or angle == 270:
new_polarisation = "z"
if axis == "z" and angle == 90 or angle == 270:
new_polarisation = "x"
elif polarisation.lower() == 'z':
elif polarisation.lower() == "z":
new_pt = (p[0], p[1], p[2] + G.dz)
if axis == 'x' and angle == 90 or angle == 270:
new_polarisation = 'y'
if axis == 'y' and angle == 90 or angle == 270:
new_polarisation = 'x'
if axis == "x" and angle == 90 or angle == 270:
new_polarisation = "y"
if axis == "y" and angle == 90 or angle == 270:
new_polarisation = "x"
pts = np.array([p, new_pt])

查看文件

@@ -46,22 +46,22 @@ class Cone(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#cone'
self.hash = "#cone"
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
r1 = self.kwargs['r1']
r2 = self.kwargs['r2']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
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
try:
# Try user-specified averaging
averagecylinder = self.kwargs['averaging']
averagecylinder = self.kwargs["averaging"]
except KeyError:
# Otherwise go with the grid default
averagecylinder = grid.averagevolumeobjects
@@ -69,13 +69,13 @@ class Cone(UserObjectGeometry):
# Check materials have been specified
# Isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
materialsrequested = [self.kwargs["material_id"]]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
materialsrequested = self.kwargs["material_ids"]
except KeyError:
logger.exception(f'{self.__str__()} no materials have been specified')
logger.exception(f"{self.__str__()} no materials have been specified")
raise
p3 = uip.round_to_grid_static_point(p1)
@@ -85,17 +85,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 ' +
f'{r1:g} should be a positive value.')
logger.exception(f"{self.__str__()} the radius of the first face " + f"{r1:g} should be a positive value.")
raise ValueError
if r2 < 0:
logger.exception(f'{self.__str__()} the radius of the second face ' +
f'{r2:g} should be a positive value.')
logger.exception(f"{self.__str__()} the radius of the second face " + f"{r2:g} should be a positive value.")
raise ValueError
if r1 == 0 and r2 == 0:
logger.exception(f'{self.__str__()} both radii cannot be zero.')
logger.exception(f"{self.__str__()} both radii cannot be zero.")
raise ValueError
# Look up requested materials in existing list of material instances
@@ -103,7 +101,7 @@ class Cone(UserObjectGeometry):
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
logger.exception(f'{self.__str__()} material(s) {notfound} do not exist')
logger.exception(f"{self.__str__()} material(s) {notfound} do not exist")
raise ValueError
# Isotropic case
@@ -117,33 +115,50 @@ class Cone(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
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
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)
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)
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(f"{self.grid_name(grid)}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}.")
dielectricsmoothing = "on" if averaging else "off"
logger.info(
f"{self.grid_name(grid)}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}."
)

查看文件

@@ -44,21 +44,21 @@ class Cylinder(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#cylinder'
self.hash = "#cylinder"
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
r = self.kwargs['r']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
r = self.kwargs["r"]
except KeyError:
logger.exception(f'{self.__str__()} please specify 2 points and a radius')
logger.exception(f"{self.__str__()} please specify 2 points and a radius")
raise
# Check averaging
try:
# Try user-specified averaging
averagecylinder = self.kwargs['averaging']
averagecylinder = self.kwargs["averaging"]
except KeyError:
# Otherwise go with the grid default
averagecylinder = grid.averagevolumeobjects
@@ -66,13 +66,13 @@ class Cylinder(UserObjectGeometry):
# Check materials have been specified
# Isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
materialsrequested = [self.kwargs["material_id"]]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
materialsrequested = self.kwargs["material_ids"]
except KeyError:
logger.exception(f'{self.__str__()} no materials have been specified')
logger.exception(f"{self.__str__()} no materials have been specified")
raise
p3 = uip.round_to_grid_static_point(p1)
@@ -82,7 +82,7 @@ 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
@@ -90,7 +90,7 @@ class Cylinder(UserObjectGeometry):
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
logger.exception(f'{self.__str__()} material(s) {notfound} do not exist')
logger.exception(f"{self.__str__()} material(s) {notfound} do not exist")
raise ValueError
# Isotropic case
@@ -104,33 +104,49 @@ class Cylinder(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
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
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)
build_cylinder(x1, y1, z1, x2, y2, z2, r, grid.dx, grid.dy, grid.dz,
numID, numIDx, numIDy, numIDz, averaging, grid.solid,
grid.rigidE, grid.rigidH, grid.ID)
build_cylinder(
x1,
y1,
z1,
x2,
y2,
z2,
r,
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(f"{self.grid_name(grid)}Cylinder 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 radius {r:g}m, of material(s) {', '.join(materialsrequested)} " +
f"created, dielectric smoothing is {dielectricsmoothing}.")
dielectricsmoothing = "on" if averaging else "off"
logger.info(
f"{self.grid_name(grid)}Cylinder 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 radius {r:g}m, of material(s) {', '.join(materialsrequested)} "
+ f"created, dielectric smoothing is {dielectricsmoothing}."
)

查看文件

@@ -53,19 +53,18 @@ class CylindricalSector(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#cylindrical_sector'
self.hash = "#cylindrical_sector"
def create(self, grid, uip):
try:
normal = self.kwargs['normal'].lower()
ctr1 = self.kwargs['ctr1']
ctr2 = self.kwargs['ctr2']
extent1 = self.kwargs['extent1']
extent2 = self.kwargs['extent2']
start = self.kwargs['start']
end = self.kwargs['end']
r = self.kwargs['r']
normal = self.kwargs["normal"].lower()
ctr1 = self.kwargs["ctr1"]
ctr2 = self.kwargs["ctr2"]
extent1 = self.kwargs["extent1"]
extent2 = self.kwargs["extent2"]
start = self.kwargs["start"]
end = self.kwargs["end"]
r = self.kwargs["r"]
thickness = extent2 - extent1
except KeyError:
logger.exception(self.__str__())
@@ -74,7 +73,7 @@ class CylindricalSector(UserObjectGeometry):
# Check averaging
try:
# Try user-specified averaging
averagecylindricalsector = self.kwargs['averaging']
averagecylindricalsector = self.kwargs["averaging"]
except KeyError:
# Otherwise go with the grid default
averagecylindricalsector = grid.averagevolumeobjects
@@ -82,31 +81,30 @@ class CylindricalSector(UserObjectGeometry):
# Check materials have been specified
# Isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
materialsrequested = [self.kwargs["material_id"]]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
materialsrequested = self.kwargs["material_ids"]
except KeyError:
logger.exception(f'{self.__str__()} No materials have been specified')
logger.exception(f"{self.__str__()} No materials have been specified")
raise
sectorstartangle = 2 * np.pi * (start / 360)
sectorangle = 2 * np.pi * (end / 360)
if normal not in ['x', 'y', 'z']:
logger.exception(f'{self.__str__()} the normal direction must be either ' +
f'x, y or z.')
if normal not in ["x", "y", "z"]:
logger.exception(f"{self.__str__()} the normal direction must be either " + f"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 ' +
f'a positive values.')
logger.exception(f"{self.__str__()} the starting angle and sector angle should be " + f"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 ' +
f'less than 360 degrees.')
logger.exception(
f"{self.__str__()} the starting angle and sector angle must be " + f"less than 360 degrees."
)
raise ValueError
# Look up requested materials in existing list of material instances
@@ -114,7 +112,7 @@ class CylindricalSector(UserObjectGeometry):
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
logger.exception(f'{self.__str__()} material(s) {notfound} do not exist')
logger.exception(f"{self.__str__()} material(s) {notfound} do not exist")
raise ValueError
if thickness > 0:
@@ -128,23 +126,19 @@ class CylindricalSector(UserObjectGeometry):
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = f'{materials[0].ID}+{materials[1].ID}+{materials[2].ID}'
requiredID = f"{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'
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)
@@ -163,34 +157,56 @@ class CylindricalSector(UserObjectGeometry):
numIDz = materials[2].numID
# yz-plane cylindrical sector
if normal == 'x':
if normal == "x":
level, ctr1, ctr2 = uip.round_to_grid((extent1, ctr1, ctr2))
# xz-plane cylindrical sector
elif normal == 'y':
elif normal == "y":
ctr1, level, ctr2 = uip.round_to_grid((ctr1, extent1, ctr2))
# xy-plane cylindrical sector
elif normal == 'z':
elif normal == "z":
ctr1, ctr2, level = uip.round_to_grid((ctr1, ctr2, extent1))
build_cylindrical_sector(ctr1, ctr2, level, sectorstartangle, sectorangle,
r, normal, thickness, grid.dx, grid.dy, grid.dz,
numID, numIDx, numIDy, numIDz, averaging,
grid.solid, grid.rigidE, grid.rigidH, grid.ID)
build_cylindrical_sector(
ctr1,
ctr2,
level,
sectorstartangle,
sectorangle,
r,
normal,
thickness,
grid.dx,
grid.dy,
grid.dz,
numID,
numIDx,
numIDy,
numIDz,
averaging,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)
if thickness > 0:
dielectricsmoothing = 'on' if averaging else 'off'
logger.info(f"{self.grid_name(grid)}Cylindrical sector with centre " +
f"{ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle " +
f"{(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, " +
f"sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} degrees, " +
f"thickness {thickness:g}m, of material(s) {', '.join(materialsrequested)} " +
f"created, dielectric smoothing is {dielectricsmoothing}.")
dielectricsmoothing = "on" if averaging else "off"
logger.info(
f"{self.grid_name(grid)}Cylindrical sector with centre "
+ f"{ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle "
+ f"{(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, "
+ f"sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} degrees, "
+ f"thickness {thickness:g}m, of material(s) {', '.join(materialsrequested)} "
+ f"created, dielectric smoothing is {dielectricsmoothing}."
)
else:
logger.info(f"{self.grid_name(grid)}Cylindrical sector with centre " +
f"{ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle " +
f"{(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, " +
f"sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} " +
f"degrees, of material(s) {', '.join(materialsrequested)} " +
f"created.")
logger.info(
f"{self.grid_name(grid)}Cylindrical sector with centre "
+ f"{ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle "
+ f"{(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, "
+ f"sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} "
+ f"degrees, of material(s) {', '.join(materialsrequested)} "
+ f"created."
)

查看文件

@@ -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__)
@@ -39,7 +38,7 @@ class Edge(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#edge'
self.hash = "#edge"
def rotate(self, axis, angle, origin=None):
"""Set parameters for rotation."""
@@ -50,19 +49,19 @@ class Edge(UserObjectGeometry):
def _do_rotate(self):
"""Performs rotation."""
pts = np.array([self.kwargs['p1'], self.kwargs['p2']])
pts = np.array([self.kwargs["p1"], self.kwargs["p2"]])
rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin)
self.kwargs['p1'] = tuple(rot_pts[0, :])
self.kwargs['p2'] = tuple(rot_pts[1, :])
self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip):
"""Creates edge and adds it to the grid."""
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
material_id = self.kwargs['material_id']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
material_id = self.kwargs["material_id"]
except KeyError:
logger.exception(f'{self.__str__()} requires exactly 3 parameters')
logger.exception(f"{self.__str__()} requires exactly 3 parameters")
raise
if self.do_rotate:
@@ -78,15 +77,17 @@ class Edge(UserObjectGeometry):
material = next((x for x in grid.materials if x.ID == material_id), None)
if not material:
logger.exception(f'Material with ID {material_id} does not exist')
logger.exception(f"Material with ID {material_id} does not exist")
raise ValueError
# Check for valid orientations
# x-orientated edge
if ((xs != xf and (ys != yf or zs != zf))
if (
(xs != xf and (ys != yf or zs != zf))
or (ys != yf and (xs != xf or zs != zf))
or (zs != zf and (xs != xf or ys != yf))):
logger.exception(f'{self.__str__()} the edge is not specified correctly')
or (zs != zf and (xs != xf or ys != yf))
):
logger.exception(f"{self.__str__()} the edge is not specified correctly")
raise ValueError
elif xs != xf:
for i in range(xs, xf):
@@ -100,6 +101,8 @@ class Edge(UserObjectGeometry):
for k in range(zs, zf):
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, ' +
f'{p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m of ' +
f'material {material_id} created.')
logger.info(
f"{self.grid_name(grid)}Edge from {p3[0]:g}m, {p3[1]:g}m, "
+ f"{p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m of "
+ f"material {material_id} created."
)

查看文件

@@ -43,24 +43,23 @@ class Ellipsoid(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#ellipsoid'
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']
p1 = self.kwargs["p1"]
xr = self.kwargs["xr"]
yr = self.kwargs["yr"]
zr = self.kwargs["zr"]
except KeyError:
logger.exception(f'{self.__str__()} please specify a point and ' +
f'the three semiaxes.')
logger.exception(f"{self.__str__()} please specify a point and " + f"the three semiaxes.")
raise
# Check averaging
try:
# Try user-specified averaging
averageellipsoid = self.kwargs['averaging']
averageellipsoid = self.kwargs["averaging"]
except KeyError:
# Otherwise go with the grid default
averageellipsoid = grid.averagevolumeobjects
@@ -68,13 +67,13 @@ class Ellipsoid(UserObjectGeometry):
# Check materials have been specified
# Isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
materialsrequested = [self.kwargs["material_id"]]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
materialsrequested = self.kwargs["material_ids"]
except KeyError:
logger.exception(f'{self.__str__()} no materials have been specified')
logger.exception(f"{self.__str__()} no materials have been specified")
raise
# Centre of ellipsoid
@@ -86,7 +85,7 @@ class Ellipsoid(UserObjectGeometry):
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
logger.exception(f'{self.__str__()} material(s) {notfound} do not exist')
logger.exception(f"{self.__str__()} material(s) {notfound} do not exist")
raise ValueError
# Isotropic case
@@ -100,34 +99,49 @@ class Ellipsoid(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
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
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)
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)
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(f"{self.grid_name(grid)}Ellipsoid with centre {p2[0]:g}m, " +
f"{p2[1]:g}m, {p2[2]:g}m, x-semiaxis {xr:g}m, " +
f"y-semiaxis {yr:g}m and z-semiaxis {zr:g}m of material(s) " +
f"{', '.join(materialsrequested)} created, dielectric " +
f"smoothing is {dielectricsmoothing}.")
dielectricsmoothing = "on" if averaging else "off"
logger.info(
f"{self.grid_name(grid)}Ellipsoid with centre {p2[0]:g}m, "
+ f"{p2[1]:g}m, {p2[2]:g}m, x-semiaxis {xr:g}m, "
+ f"y-semiaxis {yr:g}m and z-semiaxis {zr:g}m of material(s) "
+ f"{', '.join(materialsrequested)} created, dielectric "
+ f"smoothing is {dielectricsmoothing}."
)

查看文件

@@ -53,7 +53,7 @@ class FractalBox(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#fractal_box'
self.hash = "#fractal_box"
def rotate(self, axis, angle, origin=None):
"""Set parameters for rotation."""
@@ -64,30 +64,32 @@ class FractalBox(UserObjectGeometry):
def _do_rotate(self):
"""Performs rotation."""
pts = np.array([self.kwargs['p1'], self.kwargs['p2']])
pts = np.array([self.kwargs["p1"], self.kwargs["p2"]])
rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin)
self.kwargs['p1'] = tuple(rot_pts[0, :])
self.kwargs['p2'] = tuple(rot_pts[1, :])
self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
frac_dim = self.kwargs['frac_dim']
weighting = np.array(self.kwargs['weighting'])
n_materials = self.kwargs['n_materials']
mixing_model_id = self.kwargs['mixing_model_id']
ID = self.kwargs['id']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
frac_dim = self.kwargs["frac_dim"]
weighting = np.array(self.kwargs["weighting"])
n_materials = self.kwargs["n_materials"]
mixing_model_id = self.kwargs["mixing_model_id"]
ID = self.kwargs["id"]
except KeyError:
logger.exception(f'{self.__str__()} Incorrect parameters')
logger.exception(f"{self.__str__()} Incorrect parameters")
raise
try:
seed = self.kwargs['seed']
seed = self.kwargs["seed"]
except KeyError:
logger.warning(f'{self.__str__()} no value for seed detected. This ' +
'means you will get a different fractal distribution ' +
'every time the model runs.')
logger.warning(
f"{self.__str__()} no value for seed detected. This "
+ "means you will get a different fractal distribution "
+ "every time the model runs."
)
seed = None
if self.do_rotate:
@@ -96,7 +98,7 @@ class FractalBox(UserObjectGeometry):
# Check averaging
try:
# Go with user specified averaging
averagefractalbox = self.kwargs['averaging']
averagefractalbox = self.kwargs["averaging"]
except KeyError:
# If they havent specified - default is no dielectric smoothing for
# a fractal box.
@@ -110,23 +112,24 @@ 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
@@ -137,21 +140,23 @@ class FractalBox(UserObjectGeometry):
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):
if nbins > len(mixingmodel.mat):
logger.exception(f'{self.__str__()} too many materials/bins ' +
'requested compared to materials in ' +
'mixing model.')
logger.exception(
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
volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim)
@@ -166,14 +171,16 @@ class FractalBox(UserObjectGeometry):
volume.averaging = averagefractalbox
volume.mixingmodel = mixingmodel
dielectricsmoothing = 'on' if volume.averaging else 'off'
logger.info(f'{self.grid_name(grid)}Fractal box {volume.ID} from ' +
f'{p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, ' +
f'{p4[1]:g}m, {p4[2]:g}m with {volume.operatingonID}, ' +
f'fractal dimension {volume.dimension:g}, fractal weightings ' +
f'{volume.weighting[0]:g}, {volume.weighting[1]:g}, ' +
f'{volume.weighting[2]:g}, fractal seeding {volume.seed}, ' +
f'with {volume.nbins} material(s) created, dielectric smoothing ' +
f'is {dielectricsmoothing}.')
dielectricsmoothing = "on" if volume.averaging else "off"
logger.info(
f"{self.grid_name(grid)}Fractal box {volume.ID} from "
+ f"{p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, "
+ f"{p4[1]:g}m, {p4[2]:g}m with {volume.operatingonID}, "
+ f"fractal dimension {volume.dimension:g}, fractal weightings "
+ f"{volume.weighting[0]:g}, {volume.weighting[1]:g}, "
+ f"{volume.weighting[2]:g}, fractal seeding {volume.seed}, "
+ f"with {volume.nbins} material(s) created, dielectric smoothing "
+ f"is {dielectricsmoothing}."
)
grid.fractalvolumes.append(volume)

查看文件

@@ -21,8 +21,7 @@ import logging
import gprMax.config as config
import numpy as np
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
from .cmds_geometry import UserObjectGeometry
logger = logging.getLogger(__name__)
@@ -34,7 +33,7 @@ class FractalBoxBuilder(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#fractal_box_modifications'
self.hash = "#fractal_box_modifications"
def create(self, grid, uip):
for volume in grid.fractalvolumes:
@@ -49,27 +48,27 @@ class FractalBoxBuilder(UserObjectGeometry):
# Extend the volume to accomodate any rough surfaces, grass,
# or roots
for surface in volume.fractalsurfaces:
if surface.surfaceID == 'xminus':
if surface.surfaceID == "xminus":
if surface.fractalrange[0] < volume.xs:
volume.nx += volume.xs - surface.fractalrange[0]
volume.xs = surface.fractalrange[0]
elif surface.surfaceID == 'xplus':
elif surface.surfaceID == "xplus":
if surface.fractalrange[1] > volume.xf:
volume.nx += surface.fractalrange[1] - volume.xf
volume.xf = surface.fractalrange[1]
elif surface.surfaceID == 'yminus':
elif surface.surfaceID == "yminus":
if surface.fractalrange[0] < volume.ys:
volume.ny += volume.ys - surface.fractalrange[0]
volume.ys = surface.fractalrange[0]
elif surface.surfaceID == 'yplus':
elif surface.surfaceID == "yplus":
if surface.fractalrange[1] > volume.yf:
volume.ny += surface.fractalrange[1] - volume.yf
volume.yf = surface.fractalrange[1]
elif surface.surfaceID == 'zminus':
elif surface.surfaceID == "zminus":
if surface.fractalrange[0] < volume.zs:
volume.nz += volume.zs - surface.fractalrange[0]
volume.zs = surface.fractalrange[0]
elif surface.surfaceID == 'zplus':
elif surface.surfaceID == "zplus":
if surface.fractalrange[1] > volume.zf:
volume.nz += surface.fractalrange[1] - volume.zf
volume.zf = surface.fractalrange[1]
@@ -77,8 +76,9 @@ class FractalBoxBuilder(UserObjectGeometry):
# If there is only 1 bin then a normal material is being used,
# otherwise a mixing model
if volume.nbins == 1:
volume.fractalvolume = np.ones((volume.nx, volume.ny, volume.nz),
dtype=config.sim_config.dtypes['float_or_double'])
volume.fractalvolume = np.ones(
(volume.nx, volume.ny, volume.nz), dtype=config.sim_config.dtypes["float_or_double"]
)
materialnumID = next(x.numID for x in grid.materials if x.ID == volume.operatingonID)
volume.fractalvolume *= materialnumID
else:
@@ -95,7 +95,7 @@ class FractalBoxBuilder(UserObjectGeometry):
# 3D mask array
# TODO: Allow extract of rough surface profile (to print/file?)
for surface in volume.fractalsurfaces:
if surface.surfaceID == 'xminus':
if surface.surfaceID == "xminus":
for i in range(surface.fractalrange[0], surface.fractalrange[1]):
for j in range(surface.ys, surface.yf):
for k in range(surface.zs, surface.zf):
@@ -106,7 +106,7 @@ class FractalBoxBuilder(UserObjectGeometry):
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.surfaceID == 'xplus':
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):
@@ -117,7 +117,7 @@ class FractalBoxBuilder(UserObjectGeometry):
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.ID == 'grass':
elif surface.ID == "grass":
g = surface.grass[0]
# Build the blades of the grass
blade = 0
@@ -126,15 +126,22 @@ class FractalBoxBuilder(UserObjectGeometry):
if surface.fractalsurface[j - surface.ys, k - surface.zs] > 0:
height = 0
for i in range(volume.xs, surface.fractalrange[1]):
if (i < surface.fractalsurface[j - surface.ys, k - surface.zs] and
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1):
if (
i < surface.fractalsurface[j - surface.ys, k - surface.zs]
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1
):
y, z = g.calculate_blade_geometry(blade, height)
# Add y, z coordinates to existing location
yy = int(j - volume.ys + y)
zz = int(k - volume.zs + z)
# If these coordinates are outwith fractal volume stop building the blade,
# otherwise set the mask for grass.
if yy < 0 or yy >= volume.mask.shape[1] or zz < 0 or zz >= volume.mask.shape[2]:
if (
yy < 0
or yy >= volume.mask.shape[1]
or zz < 0
or zz >= volume.mask.shape[2]
):
break
else:
volume.mask[i - volume.xs, yy, zz] = 3
@@ -149,15 +156,27 @@ class FractalBoxBuilder(UserObjectGeometry):
depth = 0
i = volume.xf - 1
while i > volume.xs:
if (i > volume.originalxf - (surface.fractalsurface[j - surface.ys, k - surface.zs] -
volume.originalxf) and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1):
if (
i
> volume.originalxf
- (
surface.fractalsurface[j - surface.ys, k - surface.zs]
- volume.originalxf
)
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1
):
y, z = g.calculate_root_geometry(root, depth)
# Add y, z coordinates to existing location
yy = int(j - volume.ys + y)
zz = int(k - volume.zs + z)
# If these coordinates are outwith the fractal volume stop building the root,
# otherwise set the mask for grass.
if yy < 0 or yy >= volume.mask.shape[1] or zz < 0 or zz >= volume.mask.shape[2]:
if (
yy < 0
or yy >= volume.mask.shape[1]
or zz < 0
or zz >= volume.mask.shape[2]
):
break
else:
volume.mask[i - volume.xs, yy, zz] = 3
@@ -165,7 +184,7 @@ class FractalBoxBuilder(UserObjectGeometry):
i -= 1
root += 1
elif surface.surfaceID == 'yminus':
elif surface.surfaceID == "yminus":
for i in range(surface.xs, surface.xf):
for j in range(surface.fractalrange[0], surface.fractalrange[1]):
for k in range(surface.zs, surface.zf):
@@ -176,7 +195,7 @@ class FractalBoxBuilder(UserObjectGeometry):
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.surfaceID == 'yplus':
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]):
@@ -187,7 +206,7 @@ class FractalBoxBuilder(UserObjectGeometry):
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.ID == 'grass':
elif surface.ID == "grass":
g = surface.grass[0]
# Build the blades of the grass
blade = 0
@@ -196,15 +215,22 @@ class FractalBoxBuilder(UserObjectGeometry):
if surface.fractalsurface[i - surface.xs, k - surface.zs] > 0:
height = 0
for j in range(volume.ys, surface.fractalrange[1]):
if (j < surface.fractalsurface[i - surface.xs, k - surface.zs] and
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1):
if (
j < surface.fractalsurface[i - surface.xs, k - surface.zs]
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1
):
x, z = g.calculate_blade_geometry(blade, height)
# Add x, z coordinates to existing location
xx = int(i - volume.xs + x)
zz = int(k - volume.zs + z)
# If these coordinates are outwith fractal volume stop building the blade,
# otherwise set the mask for grass.
if xx < 0 or xx >= volume.mask.shape[0] or zz < 0 or zz >= volume.mask.shape[2]:
if (
xx < 0
or xx >= volume.mask.shape[0]
or zz < 0
or zz >= volume.mask.shape[2]
):
break
else:
volume.mask[xx, j - volume.ys, zz] = 3
@@ -219,15 +245,27 @@ class FractalBoxBuilder(UserObjectGeometry):
depth = 0
j = volume.yf - 1
while j > volume.ys:
if (j > volume.originalyf - (surface.fractalsurface[i - surface.xs, k - surface.zs] -
volume.originalyf) and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1):
if (
j
> volume.originalyf
- (
surface.fractalsurface[i - surface.xs, k - surface.zs]
- volume.originalyf
)
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1
):
x, z = g.calculate_root_geometry(root, depth)
# Add x, z coordinates to existing location
xx = int(i - volume.xs + x)
zz = int(k - volume.zs + z)
# If these coordinates are outwith the fractal volume stop building the root,
# otherwise set the mask for grass.
if xx < 0 or xx >= volume.mask.shape[0] or zz < 0 or zz >= volume.mask.shape[2]:
if (
xx < 0
or xx >= volume.mask.shape[0]
or zz < 0
or zz >= volume.mask.shape[2]
):
break
else:
volume.mask[xx, j - volume.ys, zz] = 3
@@ -235,7 +273,7 @@ class FractalBoxBuilder(UserObjectGeometry):
j -= 1
root += 1
elif surface.surfaceID == 'zminus':
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]):
@@ -246,7 +284,7 @@ class FractalBoxBuilder(UserObjectGeometry):
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.surfaceID == 'zplus':
elif surface.surfaceID == "zplus":
if not surface.ID:
for i in range(surface.xs, surface.xf):
for j in range(surface.ys, surface.yf):
@@ -257,7 +295,7 @@ class FractalBoxBuilder(UserObjectGeometry):
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2
else:
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0
elif surface.ID == 'grass':
elif surface.ID == "grass":
g = surface.grass[0]
# Build the blades of the grass
blade = 0
@@ -266,15 +304,22 @@ class FractalBoxBuilder(UserObjectGeometry):
if surface.fractalsurface[i - surface.xs, j - surface.ys] > 0:
height = 0
for k in range(volume.zs, surface.fractalrange[1]):
if (k < surface.fractalsurface[i - surface.xs, j - surface.ys] and
volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1):
if (
k < surface.fractalsurface[i - surface.xs, j - surface.ys]
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1
):
x, y = g.calculate_blade_geometry(blade, height)
# Add x, y coordinates to existing location
xx = int(i - volume.xs + x)
yy = int(j - volume.ys + y)
# If these coordinates are outwith the fractal volume stop building the blade,
# otherwise set the mask for grass.
if xx < 0 or xx >= volume.mask.shape[0] or yy < 0 or yy >= volume.mask.shape[1]:
if (
xx < 0
or xx >= volume.mask.shape[0]
or yy < 0
or yy >= volume.mask.shape[1]
):
break
else:
volume.mask[xx, yy, k - volume.zs] = 3
@@ -289,15 +334,27 @@ class FractalBoxBuilder(UserObjectGeometry):
depth = 0
k = volume.zf - 1
while k > volume.zs:
if (k > volume.originalzf - (surface.fractalsurface[i - surface.xs, j - surface.ys] -
volume.originalzf) and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1):
if (
k
> volume.originalzf
- (
surface.fractalsurface[i - surface.xs, j - surface.ys]
- volume.originalzf
)
and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1
):
x, y = g.calculate_root_geometry(root, depth)
# Add x, y coordinates to existing location
xx = int(i - volume.xs + x)
yy = int(j - volume.ys + y)
# If these coordinates are outwith the fractal volume stop building the root,
# otherwise set the mask for grass.
if xx < 0 or xx >= volume.mask.shape[0] or yy < 0 or yy >= volume.mask.shape[1]:
if (
xx < 0
or xx >= volume.mask.shape[0]
or yy < 0
or yy >= volume.mask.shape[1]
):
break
else:
volume.mask[xx, yy, k - volume.zs] = 3
@@ -306,21 +363,33 @@ class FractalBoxBuilder(UserObjectGeometry):
root += 1
# Build voxels from any true values of the 3D mask array
waternumID = next((x.numID for x in grid.materials if x.ID == 'water'), 0)
grassnumID = next((x.numID for x in grid.materials if x.ID == 'grass'), 0)
data = volume.fractalvolume.astype('int16', order='C')
mask = volume.mask.copy(order='C')
build_voxels_from_array_mask(volume.xs, volume.ys, volume.zs,
waternumID = next((x.numID for x in grid.materials if x.ID == "water"), 0)
grassnumID = next((x.numID for x in grid.materials if x.ID == "grass"), 0)
data = volume.fractalvolume.astype("int16", order="C")
mask = volume.mask.copy(order="C")
build_voxels_from_array_mask(
volume.xs,
volume.ys,
volume.zs,
config.get_model_config().ompthreads,
waternumID, grassnumID, volume.averaging,
mask, data, grid.solid, grid.rigidE,
grid.rigidH, grid.ID)
waternumID,
grassnumID,
volume.averaging,
mask,
data,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)
else:
if volume.nbins == 1:
logger.exception(f'{self.__str__()} is being used with a ' +
'single material and no modifications, ' +
'therefore please use a #box command instead.')
logger.exception(
f"{self.__str__()} is being used with a "
+ "single material and no modifications, "
+ "therefore please use a #box command instead."
)
raise ValueError
else:
volume.generate_fractal_volume()
@@ -330,9 +399,17 @@ class FractalBoxBuilder(UserObjectGeometry):
numberinbin = volume.fractalvolume[i, j, k]
volume.fractalvolume[i, j, k] = volume.mixingmodel.matID[int(numberinbin)]
data = volume.fractalvolume.astype('int16', order='C')
build_voxels_from_array(volume.xs, volume.ys, volume.zs,
config.get_model_config().ompthreads, 0,
volume.averaging, data, grid.solid,
grid.rigidE, grid.rigidH, grid.ID)
data = volume.fractalvolume.astype("int16", order="C")
build_voxels_from_array(
volume.xs,
volume.ys,
volume.zs,
config.get_model_config().ompthreads,
0,
volume.averaging,
data,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)

查看文件

@@ -31,10 +31,9 @@ logger = logging.getLogger(__name__)
class GeometryObjectsRead(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#geometry_objects_read'
self.hash = "#geometry_objects_read"
def rotate(self, axis, angle, origin=None):
pass
@@ -42,11 +41,11 @@ class GeometryObjectsRead(UserObjectGeometry):
def create(self, grid, uip):
"""Creates the object and adds it to the grid."""
try:
p1 = self.kwargs['p1']
geofile = self.kwargs['geofile']
matfile = self.kwargs['matfile']
p1 = self.kwargs["p1"]
geofile = self.kwargs["geofile"]
matfile = self.kwargs["matfile"]
except KeyError:
logger.exception(f'{self.__str__()} requires exactly five parameters')
logger.exception(f"{self.__str__()} requires exactly five parameters")
raise
# Discretise the point using uip object. This has different behaviour
@@ -61,14 +60,18 @@ class GeometryObjectsRead(UserObjectGeometry):
if not matfile.exists():
matfile = Path(config.sim_config.input_file_path.parent, matfile)
matstr = matfile.with_suffix('').name
matstr = matfile.with_suffix("").name
numexistmaterials = len(grid.materials)
# Read materials from file
with open(matfile, 'r') as f:
with open(matfile, "r") as f:
# Read any lines that begin with a hash. Strip out any newline
# characters and comments that must begin with double hashes.
materials = [line.rstrip() + '{' + matstr + '}\n' for line in f if(line.startswith('#') and not line.startswith('##') and line.rstrip('\n'))]
materials = [
line.rstrip() + "{" + matstr + "}\n"
for line in f
if (line.startswith("#") and not line.startswith("##") and line.rstrip("\n"))
]
# Build scene
# API for multiple scenes / model runs
@@ -84,9 +87,9 @@ class GeometryObjectsRead(UserObjectGeometry):
for material in grid.materials:
if material.numID >= numexistmaterials:
if material.type:
material.type += ',\nimported'
material.type += ",\nimported"
else:
material.type = 'imported'
material.type = "imported"
# See if geometry object file exists at specified path and if not try
# input file directory.
@@ -95,43 +98,63 @@ class GeometryObjectsRead(UserObjectGeometry):
geofile = Path(config.sim_config.input_file_path.parent, geofile)
# Open geometry object file and read/check spatial resolution attribute
f = h5py.File(geofile, 'r')
dx_dy_dz = f.attrs['dx_dy_dz']
if round_value((dx_dy_dz[0] / grid.dx) != 1 or
round_value(dx_dy_dz[1] / grid.dy) != 1 or
round_value(dx_dy_dz[2] / grid.dz) != 1):
logger.exception(f'{self.__str__()} requires the spatial resolution ' +
'of the geometry objects file to match the spatial ' +
'resolution of the model')
f = h5py.File(geofile, "r")
dx_dy_dz = f.attrs["dx_dy_dz"]
if round_value(
(dx_dy_dz[0] / grid.dx) != 1
or round_value(dx_dy_dz[1] / grid.dy) != 1
or round_value(dx_dy_dz[2] / grid.dz) != 1
):
logger.exception(
f"{self.__str__()} requires the spatial resolution "
+ "of the geometry objects file to match the spatial "
+ "resolution of the model"
)
raise ValueError
data = f['/data'][:]
data = f["/data"][:]
# Should be int16 to allow for -1 which indicates background, i.e.
# don't build anything, but AustinMan/Woman maybe uint16
if data.dtype != 'int16':
data = data.astype('int16')
if data.dtype != "int16":
data = data.astype("int16")
# Look to see if rigid and ID arrays are present (these should be
# present if the original geometry objects were written from gprMax)
try:
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
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
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, ' +
f'{zs * grid.dz:g}m, with corresponding materials file ' +
f'{matfile}.')
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, "
+ f"{zs * grid.dz:g}m, with corresponding materials file "
+ f"{matfile}."
)
except KeyError:
averaging = False
build_voxels_from_array(xs, ys, zs, config.get_model_config().ompthreads,
numexistmaterials, averaging, data,
grid.solid, grid.rigidE, grid.rigidH, grid.ID)
logger.info(f'{self.grid_name(grid)}Geometry objects from file ' +
f'(voxels only){geofile} inserted at {xs * grid.dx:g}m, ' +
f'{ys * grid.dy:g}m, {zs * grid.dz:g}m, with corresponding ' +
f'materials file {matfile}.')
build_voxels_from_array(
xs,
ys,
zs,
config.get_model_config().ompthreads,
numexistmaterials,
averaging,
data,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)
logger.info(
f"{self.grid_name(grid)}Geometry objects from file "
+ f"(voxels only){geofile} inserted at {xs * grid.dx:g}m, "
+ f"{ys * grid.dy:g}m, {zs * grid.dz:g}m, with corresponding "
+ f"materials file {matfile}."
)

查看文件

@@ -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__)
@@ -40,7 +39,7 @@ class Plate(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#plate'
self.hash = "#plate"
def rotate(self, axis, angle, origin=None):
"""Set parameters for rotation."""
@@ -51,28 +50,28 @@ class Plate(UserObjectGeometry):
def _do_rotate(self):
"""Performs rotation."""
pts = np.array([self.kwargs['p1'], self.kwargs['p2']])
pts = np.array([self.kwargs["p1"], self.kwargs["p2"]])
rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin)
self.kwargs['p1'] = tuple(rot_pts[0, :])
self.kwargs['p2'] = tuple(rot_pts[1, :])
self.kwargs["p1"] = tuple(rot_pts[0, :])
self.kwargs["p2"] = tuple(rot_pts[1, :])
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
except KeyError:
logger.exception(f'{self.__str__()} 2 points must be specified')
logger.exception(f"{self.__str__()} 2 points must be specified")
raise
# isotropic
try:
materialsrequested = [self.kwargs['material_id']]
materialsrequested = [self.kwargs["material_id"]]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
materialsrequested = self.kwargs["material_ids"]
except KeyError:
logger.exception(f'{self.__str__()} No materials have been specified')
logger.exception(f"{self.__str__()} No materials have been specified")
raise
if self.do_rotate:
@@ -86,10 +85,12 @@ class Plate(UserObjectGeometry):
xf, yf, zf = p2
# Check for valid orientations
if ((xs == xf and (ys == yf or zs == zf))
if (
(xs == xf and (ys == yf or zs == zf))
or (ys == yf and (xs == xf or zs == zf))
or (zs == zf and (xs == xf or ys == yf))):
logger.exception(f'{self.__str__()} the plate is not specified correctly')
or (zs == zf and (xs == xf or ys == yf))
):
logger.exception(f"{self.__str__()} the plate is not specified correctly")
raise ValueError
# Look up requested materials in existing list of material instances
@@ -97,7 +98,7 @@ class Plate(UserObjectGeometry):
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
logger.exception(f'{self.__str__()} material(s) {notfound} do not exist')
logger.exception(f"{self.__str__()} material(s) {notfound} do not exist")
raise ValueError
# yz-plane plate
@@ -113,8 +114,7 @@ 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:
@@ -129,8 +129,7 @@ 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,9 +144,10 @@ 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, " +
f"{p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m of " +
f"material(s) {', '.join(materialsrequested)} created.")
logger.info(
f"{self.grid_name(grid)}Plate from {p3[0]:g}m, {p3[1]:g}m, "
+ f"{p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m of "
+ f"material(s) {', '.join(materialsrequested)} created."
)

查看文件

@@ -41,20 +41,20 @@ class Sphere(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#sphere'
self.hash = "#sphere"
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
r = self.kwargs['r']
p1 = self.kwargs["p1"]
r = self.kwargs["r"]
except KeyError:
logger.exception(f'{self.__str__()} please specify a point and a radius.')
logger.exception(f"{self.__str__()} please specify a point and a radius.")
raise
# Check averaging
try:
# Try user-specified averaging
averagesphere = self.kwargs['averaging']
averagesphere = self.kwargs["averaging"]
except KeyError:
# Otherwise go with the grid default
averagesphere = grid.averagevolumeobjects
@@ -62,13 +62,13 @@ class Sphere(UserObjectGeometry):
# Check materials have been specified
# Isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
materialsrequested = [self.kwargs["material_id"]]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
materialsrequested = self.kwargs["material_ids"]
except KeyError:
logger.exception(f'{self.__str__()} no materials have been specified')
logger.exception(f"{self.__str__()} no materials have been specified")
raise
# Centre of sphere
@@ -80,7 +80,7 @@ class Sphere(UserObjectGeometry):
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
logger.exception(f'{self.__str__()} material(s) {notfound} do not exist')
logger.exception(f"{self.__str__()} material(s) {notfound} do not exist")
raise ValueError
# Isotropic case
@@ -94,33 +94,46 @@ class Sphere(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
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
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)
build_sphere(xc, yc, zc, r, grid.dx, grid.dy, grid.dz, numID,
numIDx, numIDy, numIDz, averaging, grid.solid,
grid.rigidE, grid.rigidH, grid.ID)
build_sphere(
xc,
yc,
zc,
r,
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(f"{self.grid_name(grid)}Sphere with centre {p2[0]:g}m, " +
f"{p2[1]:g}m, {p2[2]:g}m, radius {r:g}m, of material(s) " +
f"{', '.join(materialsrequested)} created, dielectric " +
f"smoothing is {dielectricsmoothing}.")
dielectricsmoothing = "on" if averaging else "off"
logger.info(
f"{self.grid_name(grid)}Sphere with centre {p2[0]:g}m, "
+ f"{p2[1]:g}m, {p2[2]:g}m, radius {r:g}m, of material(s) "
+ f"{', '.join(materialsrequested)} created, dielectric "
+ f"smoothing is {dielectricsmoothing}."
)

查看文件

@@ -45,7 +45,7 @@ class Triangle(UserObjectGeometry):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#triangle'
self.hash = "#triangle"
def rotate(self, axis, angle, origin=None):
"""Sets parameters for rotation."""
@@ -56,21 +56,21 @@ class Triangle(UserObjectGeometry):
def _do_rotate(self):
"""Performs rotation."""
p1 = rotate_point(self.kwargs['p1'], self.axis, self.angle, self.origin)
p2 = rotate_point(self.kwargs['p2'], self.axis, self.angle, self.origin)
p3 = rotate_point(self.kwargs['p3'], self.axis, self.angle, self.origin)
self.kwargs['p1'] = tuple(p1)
self.kwargs['p2'] = tuple(p2)
self.kwargs['p3'] = tuple(p3)
p1 = rotate_point(self.kwargs["p1"], self.axis, self.angle, self.origin)
p2 = rotate_point(self.kwargs["p2"], self.axis, self.angle, self.origin)
p3 = rotate_point(self.kwargs["p3"], self.axis, self.angle, self.origin)
self.kwargs["p1"] = tuple(p1)
self.kwargs["p2"] = tuple(p2)
self.kwargs["p3"] = tuple(p3)
def create(self, grid, uip):
try:
up1 = self.kwargs['p1']
up2 = self.kwargs['p2']
up3 = self.kwargs['p3']
thickness = self.kwargs['thickness']
up1 = self.kwargs["p1"]
up2 = self.kwargs["p2"]
up3 = self.kwargs["p3"]
thickness = self.kwargs["thickness"]
except KeyError:
logger.exception(f'{self.__str__()} specify 3 points and a thickness')
logger.exception(f"{self.__str__()} specify 3 points and a thickness")
raise
if self.do_rotate:
@@ -79,7 +79,7 @@ class Triangle(UserObjectGeometry):
# Check averaging
try:
# Try user-specified averaging
averagetriangularprism = self.kwargs['averaging']
averagetriangularprism = self.kwargs["averaging"]
except KeyError:
# Otherwise go with the grid default
averagetriangularprism = grid.averagevolumeobjects
@@ -87,13 +87,13 @@ class Triangle(UserObjectGeometry):
# Check materials have been specified
# Isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
materialsrequested = [self.kwargs["material_id"]]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
materialsrequested = self.kwargs["material_ids"]
except KeyError:
logger.exception(f'{self.__str__()} no materials have been specified')
logger.exception(f"{self.__str__()} no materials have been specified")
raise
p4 = uip.round_to_grid_static_point(up1)
@@ -108,21 +108,21 @@ 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
# yz-plane triangle
if x1 == x2 == x3:
normal = 'x'
normal = "x"
# xz-plane triangle
elif y1 == y2 == y3:
normal = 'y'
normal = "y"
# xy-plane triangle
elif z1 == z2 == z3:
normal = 'z'
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
@@ -130,7 +130,7 @@ class Triangle(UserObjectGeometry):
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
logger.exception(f'{self.__str__()} material(s) {notfound} do not exist')
logger.exception(f"{self.__str__()} material(s) {notfound} do not exist")
raise ValueError
if thickness > 0:
@@ -145,23 +145,19 @@ 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
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
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)
@@ -179,19 +175,45 @@ class Triangle(UserObjectGeometry):
numIDy = materials[1].numID
numIDz = materials[2].numID
build_triangle(x1, y1, z1, x2, y2, z2, x3, y3, z3, normal, thickness,
grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz,
averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
build_triangle(
x1,
y1,
z1,
x2,
y2,
z2,
x3,
y3,
z3,
normal,
thickness,
grid.dx,
grid.dy,
grid.dz,
numID,
numIDx,
numIDy,
numIDz,
averaging,
grid.solid,
grid.rigidE,
grid.rigidH,
grid.ID,
)
if thickness > 0:
dielectricsmoothing = 'on' if averaging else 'off'
logger.info(f"{self.grid_name(grid)}Triangle with coordinates " +
f"{p4[0]:g}m {p4[1]:g}m {p4[2]:g}m, {p5[0]:g}m {p5[1]:g}m " +
f"{p5[2]:g}m, {p6[0]:g}m {p6[1]:g}m {p6[2]:g}m and thickness " +
f"{thickness:g}m of material(s) {', '.join(materialsrequested)} " +
f"created, dielectric smoothing is {dielectricsmoothing}.")
dielectricsmoothing = "on" if averaging else "off"
logger.info(
f"{self.grid_name(grid)}Triangle with coordinates "
+ f"{p4[0]:g}m {p4[1]:g}m {p4[2]:g}m, {p5[0]:g}m {p5[1]:g}m "
+ f"{p5[2]:g}m, {p6[0]:g}m {p6[1]:g}m {p6[2]:g}m and thickness "
+ f"{thickness:g}m of material(s) {', '.join(materialsrequested)} "
+ f"created, dielectric smoothing is {dielectricsmoothing}."
)
else:
logger.info(f"{self.grid_name(grid)}Triangle with coordinates " +
f"{p4[0]:g}m {p4[1]:g}m {p4[2]:g}m, {p5[0]:g}m {p5[1]:g}m " +
f"{p5[2]:g}m, {p6[0]:g}m {p6[1]:g}m {p6[2]:g}m of material(s) " +
f"{', '.join(materialsrequested)} created.")
logger.info(
f"{self.grid_name(grid)}Triangle with coordinates "
+ f"{p4[0]:g}m {p4[1]:g}m {p4[2]:g}m, {p5[0]:g}m {p5[1]:g}m "
+ f"{p5[2]:g}m, {p6[0]:g}m {p6[1]:g}m {p6[2]:g}m of material(s) "
+ f"{', '.join(materialsrequested)} created."
)

文件差异内容过多而无法显示 加载差异

查看文件

@@ -70,9 +70,9 @@ class Title(UserObjectSingle):
def create(self, G, uip):
try:
title = self.kwargs['name']
title = self.kwargs["name"]
G.title = title
logger.info(f'Model title: {G.title}')
logger.info(f"Model title: {G.title}")
except KeyError:
pass
@@ -90,26 +90,29 @@ class Discretisation(UserObjectSingle):
def create(self, G, uip):
try:
G.dl = np.array(self.kwargs['p1'])
G.dx, G.dy, G.dz = self.kwargs['p1']
G.dl = np.array(self.kwargs["p1"])
G.dx, G.dy, G.dz = self.kwargs["p1"]
except KeyError:
logger.exception(f'{self.__str__()} discretisation requires a point')
logger.exception(f"{self.__str__()} discretisation requires a point")
raise
if G.dl[0] <= 0:
logger.exception(f'{self.__str__()} discretisation requires the '
f'x-direction spatial step to be greater than zero')
logger.exception(
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')
logger.exception(
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')
logger.exception(
f"{self.__str__()} discretisation requires the " f"z-direction spatial step to be greater than zero"
)
raise ValueError
logger.info(f'Spatial discretisation: {G.dl[0]:g} x {G.dl[1]:g} x {G.dl[2]:g}m')
logger.info(f"Spatial discretisation: {G.dl[0]:g} x {G.dl[1]:g} x {G.dl[2]:g}m")
class Domain(UserObjectSingle):
@@ -125,46 +128,47 @@ class Domain(UserObjectSingle):
def create(self, G, uip):
try:
G.nx, G.ny, G.nz = uip.discretise_point(self.kwargs['p1'])
G.nx, G.ny, G.nz = uip.discretise_point(self.kwargs["p1"])
except KeyError:
logger.exception(f'{self.__str__()} please specify a point')
logger.exception(f"{self.__str__()} please specify a point")
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 " f"every dimension")
raise ValueError
logger.info(f"Domain size: {self.kwargs['p1'][0]:g} x {self.kwargs['p1'][1]:g} x " +
f"{self.kwargs['p1'][2]:g}m ({G.nx:d} x {G.ny:d} x {G.nz:d} = " +
f"{(G.nx * G.ny * G.nz):g} cells)")
logger.info(
f"Domain size: {self.kwargs['p1'][0]:g} x {self.kwargs['p1'][1]:g} x "
+ f"{self.kwargs['p1'][2]:g}m ({G.nx:d} x {G.ny:d} x {G.nz:d} = "
+ f"{(G.nx * G.ny * G.nz):g} cells)"
)
# Calculate time step at CFL limit; switch off appropriate PMLs for 2D
if G.nx == 1:
config.get_model_config().mode = '2D TMx'
G.pmls['thickness']['x0'] = 0
G.pmls['thickness']['xmax'] = 0
config.get_model_config().mode = "2D TMx"
G.pmls["thickness"]["x0"] = 0
G.pmls["thickness"]["xmax"] = 0
elif G.ny == 1:
config.get_model_config().mode = '2D TMy'
G.pmls['thickness']['y0'] = 0
G.pmls['thickness']['ymax'] = 0
config.get_model_config().mode = "2D TMy"
G.pmls["thickness"]["y0"] = 0
G.pmls["thickness"]["ymax"] = 0
elif G.nz == 1:
config.get_model_config().mode = '2D TMz'
G.pmls['thickness']['z0'] = 0
G.pmls['thickness']['zmax'] = 0
config.get_model_config().mode = "2D TMz"
G.pmls["thickness"]["z0"] = 0
G.pmls["thickness"]["zmax"] = 0
else:
config.get_model_config().mode = '3D'
config.get_model_config().mode = "3D"
G.calculate_dt()
logger.info(f'Mode: {config.get_model_config().mode}')
logger.info(f"Mode: {config.get_model_config().mode}")
# 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']:
logger.exception('Sub-gridding cannot be used with 2D models')
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
logger.info(f'Time step (at CFL limit): {G.dt:g} secs')
logger.info(f"Time step (at CFL limit): {G.dt:g} secs")
class TimeStepStabilityFactor(UserObjectSingle):
@@ -180,20 +184,21 @@ class TimeStepStabilityFactor(UserObjectSingle):
def create(self, G, uip):
try:
f = self.kwargs['f']
f = self.kwargs["f"]
except KeyError:
logger.exception(f'{self.__str__()} requires exactly one parameter')
logger.exception(f"{self.__str__()} requires exactly one parameter")
raise
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')
logger.exception(
f"{self.__str__()} requires the value of the time " f"step stability factor to be between zero and one"
)
raise ValueError
G.dt_mod = f
G.dt = G.dt * G.dt_mod
logger.info(f'Time step (modified): {G.dt:g} secs')
logger.info(f"Time step (modified): {G.dt:g} secs")
class TimeWindow(UserObjectSingle):
@@ -213,28 +218,28 @@ class TimeWindow(UserObjectSingle):
# The +/- 1 used in calculating the number of iterations is to account for
# the fact that the solver (iterations) loop runs from 0 to < G.iterations
try:
iterations = int(self.kwargs['iterations'])
iterations = int(self.kwargs["iterations"])
G.timewindow = (iterations - 1) * G.dt
G.iterations = iterations
except KeyError:
pass
try:
tmp = float(self.kwargs['time'])
tmp = float(self.kwargs["time"])
if tmp > 0:
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
if not G.timewindow:
logger.exception(self.__str__() + ' specify a time or number of iterations')
logger.exception(self.__str__() + " specify a time or number of iterations")
raise ValueError
logger.info(f'Time window: {G.timewindow:g} secs ({G.iterations} iterations)')
logger.info(f"Time window: {G.timewindow:g} secs ({G.iterations} iterations)")
class OMPThreads(UserObjectSingle):
@@ -251,14 +256,15 @@ class OMPThreads(UserObjectSingle):
def create(self, G, uip):
try:
n = self.kwargs['n']
n = self.kwargs["n"]
except KeyError:
logger.exception(f'{self.__str__()} requires exactly one parameter '
f'to specify the number of CPU OpenMP threads to use')
logger.exception(
f"{self.__str__()} requires exactly one parameter "
f"to specify the number of CPU OpenMP threads to use"
)
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)
@@ -284,37 +290,40 @@ class PMLProps(UserObjectSingle):
def create(self, G, uip):
try:
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)}")
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)}"
)
except KeyError:
pass
try:
thickness = self.kwargs['thickness']
for key in G.pmls['thickness'].keys():
G.pmls['thickness'][key] = int(thickness)
thickness = self.kwargs["thickness"]
for key in G.pmls["thickness"].keys():
G.pmls["thickness"][key] = int(thickness)
except KeyError:
try:
G.pmls['thickness']['x0'] = int(self.kwargs['x0'])
G.pmls['thickness']['y0'] = int(self.kwargs['y0'])
G.pmls['thickness']['z0'] = int(self.kwargs['z0'])
G.pmls['thickness']['xmax'] = int(self.kwargs['xmax'])
G.pmls['thickness']['ymax'] = int(self.kwargs['ymax'])
G.pmls['thickness']['zmax'] = int(self.kwargs['zmax'])
G.pmls["thickness"]["x0"] = int(self.kwargs["x0"])
G.pmls["thickness"]["y0"] = int(self.kwargs["y0"])
G.pmls["thickness"]["z0"] = int(self.kwargs["z0"])
G.pmls["thickness"]["xmax"] = int(self.kwargs["xmax"])
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 (2 * G.pmls['thickness']['x0'] >= G.nx or
2 * G.pmls['thickness']['y0'] >= G.ny or
2 * G.pmls['thickness']['z0'] >= G.nz or
2 * G.pmls['thickness']['xmax'] >= G.nx or
2 * G.pmls['thickness']['ymax'] >= G.ny or
2 * G.pmls['thickness']['zmax'] >= G.nz):
logger.exception(f'{self.__str__()} has too many cells for the domain size')
if (
2 * G.pmls["thickness"]["x0"] >= G.nx
or 2 * G.pmls["thickness"]["y0"] >= G.ny
or 2 * G.pmls["thickness"]["z0"] >= G.nz
or 2 * G.pmls["thickness"]["xmax"] >= G.nx
or 2 * G.pmls["thickness"]["ymax"] >= G.ny
or 2 * G.pmls["thickness"]["zmax"] >= G.nz
):
logger.exception(f"{self.__str__()} has too many cells for the domain size")
raise ValueError
@@ -331,14 +340,16 @@ class SrcSteps(UserObjectSingle):
def create(self, G, uip):
try:
G.srcsteps = uip.discretise_point(self.kwargs['p1'])
G.srcsteps = uip.discretise_point(self.kwargs["p1"])
except KeyError:
logger.exception(f'{self.__str__()} requires exactly three parameters')
logger.exception(f"{self.__str__()} requires exactly three parameters")
raise
logger.info(f'Simple sources will step {G.srcsteps[0] * G.dx:g}m, ' +
f'{G.srcsteps[1] * G.dy:g}m, {G.srcsteps[2] * G.dz:g}m ' +
'for each model run.')
logger.info(
f"Simple sources will step {G.srcsteps[0] * G.dx:g}m, "
+ f"{G.srcsteps[1] * G.dy:g}m, {G.srcsteps[2] * G.dz:g}m "
+ "for each model run."
)
class RxSteps(UserObjectSingle):
@@ -354,14 +365,16 @@ class RxSteps(UserObjectSingle):
def create(self, G, uip):
try:
G.rxsteps = uip.discretise_point(self.kwargs['p1'])
G.rxsteps = uip.discretise_point(self.kwargs["p1"])
except KeyError:
logger.exception(f'{self.__str__()} requires exactly three parameters')
logger.exception(f"{self.__str__()} requires exactly three parameters")
raise
logger.info(f'All receivers will step {G.rxsteps[0] * G.dx:g}m, ' +
f'{G.rxsteps[1] * G.dy:g}m, {G.rxsteps[2] * G.dz:g}m ' +
'for each model run.')
logger.info(
f"All receivers will step {G.rxsteps[0] * G.dx:g}m, "
+ f"{G.rxsteps[1] * G.dy:g}m, {G.rxsteps[2] * G.dz:g}m "
+ "for each model run."
)
class ExcitationFile(UserObjectSingle):
@@ -382,54 +395,51 @@ class ExcitationFile(UserObjectSingle):
def create(self, G, uip):
try:
kwargs = {}
excitationfile = self.kwargs['filepath']
kwargs['kind'] = self.kwargs['kind']
kwargs['fill_value'] = self.kwargs['fill_value']
excitationfile = self.kwargs["filepath"]
kwargs["kind"] = self.kwargs["kind"]
kwargs["fill_value"] = self.kwargs["fill_value"]
except KeyError:
try:
excitationfile = self.kwargs['filepath']
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(f'Excitation file: {excitationfile}')
logger.info(f"Excitation file: {excitationfile}")
# Get waveform names
with open(excitationfile, 'r') as f:
with open(excitationfile, "r") as f:
waveformIDs = f.readline().split()
# 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':
if waveformIDs[0].lower() == "time":
waveformIDs = waveformIDs[1:]
waveformtime = waveformvalues[:, 0]
waveformvalues = waveformvalues[:, 1:]
timestr = 'user-defined time array'
timestr = "user-defined time array"
else:
waveformtime = np.arange(0, G.timewindow + G.dt, G.dt)
timestr = 'simulation time array'
timestr = "simulation time array"
for waveform in range(len(waveformIDs)):
if any(x.ID == waveformIDs[waveform] for x in G.waveforms):
logger.exception(f'Waveform with ID {waveformIDs[waveform]} already exists')
logger.exception(f"Waveform with ID {waveformIDs[waveform]} already exists")
raise ValueError
w = Waveform()
w.ID = waveformIDs[waveform]
w.type = 'user'
w.type = "user"
# Select correct column of waveform values depending on array shape
singlewaveformvalues = waveformvalues[:] if len(waveformvalues.shape) == 1 else waveformvalues[:, waveform]
@@ -439,17 +449,21 @@ class ExcitationFile(UserObjectSingle):
singlewaveformvalues = singlewaveformvalues[: len(waveformtime)]
# Zero-pad end of waveform array if it is shorter than time array
elif len(singlewaveformvalues) < len(waveformtime):
singlewaveformvalues = np.pad(singlewaveformvalues,
(0, len(waveformtime) -
len(singlewaveformvalues)),
'constant', constant_values=0)
singlewaveformvalues = np.pad(
singlewaveformvalues,
(0, len(waveformtime) - len(singlewaveformvalues)),
"constant",
constant_values=0,
)
# Interpolate waveform values
w.userfunc = interpolate.interp1d(waveformtime, singlewaveformvalues, **kwargs)
logger.info(f"User waveform {w.ID} created using {timestr} and, if " +
f"required, interpolation parameters (kind: {kwargs['kind']}, " +
f"fill value: {kwargs['fill_value']}).")
logger.info(
f"User waveform {w.ID} created using {timestr} and, if "
+ f"required, interpolation parameters (kind: {kwargs['kind']}, "
+ f"fill value: {kwargs['fill_value']})."
)
G.waveforms.append(w)
@@ -460,9 +474,10 @@ class OutputDir(UserObjectSingle):
Attributes:
dir: string of file path to directory.
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.order = 11
def create(self, grid, uip):
config.get_model_config().set_output_file_path(self.kwargs['dir'])
config.get_model_config().set_output_file_path(self.kwargs["dir"])

查看文件

@@ -48,6 +48,7 @@ model_configs = []
# ModelConfig is created
model_num = 0
def get_model_config():
"""Return ModelConfig instace for specific model."""
if sim_config.args.mpi:
@@ -55,14 +56,14 @@ def get_model_config():
else:
return model_configs[model_num]
class ModelConfig:
"""Configuration parameters for a model.
N.B. Multiple models can exist within a simulation
"""
def __init__(self):
self.mode = '3D'
self.mode = "3D"
self.grids = []
self.ompthreads = None
@@ -72,10 +73,10 @@ class ModelConfig:
# N.B. This will happen if the requested snapshots are too large to
# fit on the memory of the GPU. If True this will slow
# performance significantly.
if sim_config.general['solver'] == 'cuda' or sim_config.general['solver'] == 'opencl':
if sim_config.general['solver'] == 'cuda':
if sim_config.general["solver"] == "cuda" or sim_config.general["solver"] == "opencl":
if sim_config.general["solver"] == "cuda":
devs = sim_config.args.gpu
elif sim_config.general['solver'] == 'opencl':
elif sim_config.general["solver"] == "opencl":
devs = sim_config.args.opencl
# If a list of lists of deviceIDs is found, flatten it
@@ -89,8 +90,7 @@ class ModelConfig:
except:
deviceID = 0
self.device = {'dev': sim_config.set_model_device(deviceID),
'snapsgpu2cpu': False}
self.device = {"dev": sim_config.set_model_device(deviceID), "snapsgpu2cpu": False}
# Total memory usage for all grids in the model. Starts with 50MB overhead.
self.mem_overhead = 65e6
@@ -99,13 +99,11 @@ 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
@@ -116,9 +114,7 @@ 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.
@@ -127,37 +123,41 @@ class ModelConfig:
# drudelorentz: True/False model contains Drude or Lorentz materials.
# cudarealfunc: String to substitute into CUDA kernels for fields
# dependent on dispersive material type.
self.materials = {'maxpoles': 0,
'dispersivedtype': None,
'dispersiveCdtype': None,
'drudelorentz': None,
'cudarealfunc': ''}
self.materials = {
"maxpoles": 0,
"dispersivedtype": None,
"dispersiveCdtype": None,
"drudelorentz": None,
"cudarealfunc": "",
}
def get_scene(self):
if sim_config.scenes:
return sim_config.scenes[model_num]
else: return None
else:
return None
def get_usernamespace(self):
"""Namespace only used with #python blocks which are deprecated."""
tmp = {'number_model_runs': sim_config.model_end,
'current_model_run': model_num + 1,
'inputfile': sim_config.input_file_path.resolve()}
tmp = {
"number_model_runs": sim_config.model_end,
"current_model_run": model_num + 1,
"inputfile": sim_config.input_file_path.resolve(),
}
return dict(**sim_config.em_consts, **tmp)
def set_dispersive_material_types(self):
"""Sets data type for disperive materials. Complex if Drude or Lorentz
materials are present. Real if Debye materials.
"""
if self.materials['drudelorentz']:
self.materials['crealfunc'] = '.real()'
self.materials['dispersivedtype'] = sim_config.dtypes['complex']
self.materials['dispersiveCdtype'] = sim_config.dtypes['C_complex']
if self.materials["drudelorentz"]:
self.materials["crealfunc"] = ".real()"
self.materials["dispersivedtype"] = sim_config.dtypes["complex"]
self.materials["dispersiveCdtype"] = sim_config.dtypes["C_complex"]
else:
self.materials['crealfunc'] = ''
self.materials['dispersivedtype'] = sim_config.dtypes['float_or_double']
self.materials['dispersiveCdtype'] = sim_config.dtypes['C_float_or_double']
self.materials["crealfunc"] = ""
self.materials["dispersivedtype"] = sim_config.dtypes["float_or_double"]
self.materials["dispersiveCdtype"] = sim_config.dtypes["C_float_or_double"]
def set_output_file_path(self, outputdir=None):
"""Sets output file path. Can be provided by the user via the API or an
@@ -172,17 +172,17 @@ class ModelConfig:
try:
self.output_file_path = Path(self.args.outputfile)
except AttributeError:
self.output_file_path = sim_config.input_file_path.with_suffix('')
self.output_file_path = sim_config.input_file_path.with_suffix("")
else:
try:
Path(outputdir).mkdir(exist_ok=True)
self.output_file_path = Path(outputdir, sim_config.input_file_path.stem)
except AttributeError:
self.output_file_path = sim_config.input_file_path.with_suffix('')
self.output_file_path = sim_config.input_file_path.with_suffix("")
parts = self.output_file_path.parts
self.output_file_path = Path(*parts[:-1], parts[-1] + self.appendmodelnumber)
self.output_file_path_ext = self.output_file_path.with_suffix('.h5')
self.output_file_path_ext = self.output_file_path.with_suffix(".h5")
def set_snapshots_dir(self):
"""Sets directory to store any snapshots.
@@ -190,8 +190,8 @@ class ModelConfig:
Returns:
snapshot_dir: Path to directory to store snapshot files in.
"""
parts = self.output_file_path.with_suffix('').parts
snapshot_dir = Path(*parts[:-1], parts[-1] + '_snaps')
parts = self.output_file_path.with_suffix("").parts
snapshot_dir = Path(*parts[:-1], parts[-1] + "_snaps")
return snapshot_dir
@@ -210,11 +210,11 @@ class SimulationConfig:
self.args = args
if args.mpi and args.geometry_fixed:
logger.exception('The geometry fixed option cannot be used with MPI.')
logger.exception("The geometry fixed option cannot be used with MPI.")
raise ValueError
if args.gpu and args.opencl:
logger.exception('You cannot use both CUDA and OpenCl simultaneously.')
logger.exception("You cannot use both CUDA and OpenCl simultaneously.")
raise ValueError
# General settings for the simulation
@@ -228,58 +228,58 @@ class SimulationConfig:
# progressbars when logging level is greater than
# info (20)
self.general = {'solver': 'cpu',
'subgrid': False,
'precision': 'single',
'progressbars': args.log_level <= 20}
self.general = {"solver": "cpu", "subgrid": False, "precision": "single", "progressbars": args.log_level <= 20}
self.em_consts = {'c': c, # Speed of light in free space (m/s)
'e0': e0, # Permittivity of free space (F/m)
'm0': m0, # Permeability of free space (H/m)
'z0': np.sqrt(m0 / e0)} # Impedance of free space (Ohms)
self.em_consts = {
"c": c, # Speed of light in free space (m/s)
"e0": e0, # Permittivity of free space (F/m)
"m0": m0, # Permeability of free space (H/m)
"z0": np.sqrt(m0 / e0),
} # Impedance of free space (Ohms)
# Store information about host machine
self.hostinfo = get_host_info()
# CUDA
if self.args.gpu is not None:
self.general['solver'] = 'cuda'
self.general["solver"] = "cuda"
# Both single and double precision are possible on GPUs, but single
# provides best performance.
self.general['precision'] = 'single'
self.devices = {'devs': [], # pycuda device objects
'nvcc_opts': None} # nvcc compiler options
self.general["precision"] = "single"
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']
if sys.platform == "win32":
self.devices["nvcc_opts"] = ["-w"]
# Add pycuda available GPU(s)
self.devices['devs'] = detect_cuda_gpus()
self.devices["devs"] = detect_cuda_gpus()
# OpenCL
if self.args.opencl is not None:
self.general['solver'] = 'opencl'
self.general['precision'] = 'single'
self.devices = {'devs': [], # pyopencl available device(s)
'compiler_opts': None}
self.general["solver"] = "opencl"
self.general["precision"] = "single"
self.devices = {"devs": [], "compiler_opts": None} # pyopencl available device(s)
# Suppress unused variable warnings on gcc
# if sys.platform != 'win32': self.devices['compiler_opts'] = ['-w']
# Add pyopencl available device(s)
self.devices['devs'] = detect_opencl()
self.devices["devs"] = detect_opencl()
# Subgrid parameter may not exist if user enters via CLI
try:
self.general['subgrid'] = self.args.subgrid
self.general["subgrid"] = self.args.subgrid
# Double precision should be used with subgrid for best accuracy
self.general['precision'] = 'double'
if ((self.general['subgrid'] and self.general['solver'] == 'cuda') or
(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.')
self.general["precision"] = "double"
if (self.general["subgrid"] and self.general["solver"] == "cuda") or (
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."
)
raise ValueError
except AttributeError:
self.general['subgrid'] = False
self.general["subgrid"] = False
# Scenes parameter may not exist if user enters via CLI
try:
@@ -303,14 +303,13 @@ class SimulationConfig:
"""
found = False
for ID, dev in self.devices['devs'].items():
for ID, dev in self.devices["devs"].items():
if ID == deviceID:
found = True
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):
@@ -323,29 +322,33 @@ class SimulationConfig:
Main field arrays use floats.
"""
if self.general['precision'] == 'single':
self.dtypes = {'float_or_double': np.float32,
'complex': np.complex64,
'cython_float_or_double': cython.float,
'cython_complex': cython.floatcomplex,
'C_float_or_double': 'float',
'C_complex': None}
if self.general['solver'] == 'cuda':
self.dtypes['C_complex'] = 'pycuda::complex<float>'
elif self.general['solver'] == 'opencl':
self.dtypes['C_complex'] = 'cfloat'
if self.general["precision"] == "single":
self.dtypes = {
"float_or_double": np.float32,
"complex": np.complex64,
"cython_float_or_double": cython.float,
"cython_complex": cython.floatcomplex,
"C_float_or_double": "float",
"C_complex": None,
}
if self.general["solver"] == "cuda":
self.dtypes["C_complex"] = "pycuda::complex<float>"
elif self.general["solver"] == "opencl":
self.dtypes["C_complex"] = "cfloat"
elif self.general['precision'] == 'double':
self.dtypes = {'float_or_double': np.float64,
'complex': np.complex128,
'cython_float_or_double': cython.double,
'cython_complex': cython.doublecomplex,
'C_float_or_double': 'double',
'C_complex': None}
if self.general['solver'] == 'cuda':
self.dtypes['C_complex'] = 'pycuda::complex<double>'
elif self.general['solver'] == 'opencl':
self.dtypes['C_complex'] = 'cdouble'
elif self.general["precision"] == "double":
self.dtypes = {
"float_or_double": np.float64,
"complex": np.complex128,
"cython_float_or_double": cython.double,
"cython_complex": cython.doublecomplex,
"C_float_or_double": "double",
"C_complex": None,
}
if self.general["solver"] == "cuda":
self.dtypes["C_complex"] = "pycuda::complex<double>"
elif self.general["solver"] == "opencl":
self.dtypes["C_complex"] = "cdouble"
def _set_model_start_end(self):
"""Sets range for number of models to run (internally 0 index)."""

查看文件

@@ -27,8 +27,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__)
@@ -41,8 +40,7 @@ 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
@@ -57,10 +55,10 @@ class Context:
self.tsimstart = timer()
self.print_logo_copyright()
print_host_info(config.sim_config.hostinfo)
if config.sim_config.general['solver'] == 'cuda':
print_cuda_info(config.sim_config.devices['devs'])
elif config.sim_config.general['solver'] == 'opencl':
print_opencl_info(config.sim_config.devices['devs'])
if config.sim_config.general["solver"] == "cuda":
print_cuda_info(config.sim_config.devices["devs"])
elif config.sim_config.general["solver"] == "opencl":
print_opencl_info(config.sim_config.devices["devs"])
# Clear list of model configs. It can be retained when gprMax is
# called in a loop, and want to avoid this.
@@ -92,13 +90,15 @@ class Context:
def print_logo_copyright(self):
"""Prints gprMax logo, version, and copyright/licencing information."""
logo_copyright = logo(f'{__version__} ({codename})')
logo_copyright = logo(f"{__version__} ({codename})")
logger.basic(logo_copyright)
def print_sim_time_taken(self):
"""Prints the total simulation time based on context."""
s = (f"\n=== Simulation completed in " +
f"{humanize.precisedelta(datetime.timedelta(seconds=self.tsimend - self.tsimstart), format='%0.4f')}")
s = (
f"\n=== Simulation completed in "
+ f"{humanize.precisedelta(datetime.timedelta(seconds=self.tsimend - self.tsimstart), format='%0.4f')}"
)
logger.basic(f"{s} {'=' * (get_terminal_width() - 1 - len(s))}\n")
@@ -127,12 +127,11 @@ class MPIContext(Context):
"""
# Create configuration for model
config.model_num = work['i']
config.model_num = work["i"]
model_config = config.ModelConfig()
# Set GPU deviceID according to worker rank
if config.sim_config.general['solver'] == 'cuda':
model_config.device = {'dev': config.sim_config.devices['devs'][self.rank - 1],
'snapsgpu2cpu': False}
if config.sim_config.general["solver"] == "cuda":
model_config.device = {"dev": config.sim_config.devices["devs"][self.rank - 1], "snapsgpu2cpu": False}
config.model_configs = model_config
G = create_G()
@@ -154,25 +153,29 @@ class MPIContext(Context):
self.tsimstart = timer()
self.print_logo_copyright()
print_host_info(config.sim_config.hostinfo)
if config.sim_config.general['solver'] == 'cuda':
print_cuda_info(config.sim_config.devices['devs'])
elif config.sim_config.general['solver'] == 'opencl':
print_opencl_info(config.sim_config.devices['devs'])
if config.sim_config.general["solver"] == "cuda":
print_cuda_info(config.sim_config.devices["devs"])
elif config.sim_config.general["solver"] == "opencl":
print_opencl_info(config.sim_config.devices["devs"])
sys.stdout.flush()
# Contruct MPIExecutor
executor = self.MPIExecutor(self._run_model, comm=self.comm)
# Check GPU resources versus number of MPI tasks
if (executor.is_master() and
config.sim_config.general['solver'] == 'cuda' and
executor.size - 1 > len(config.sim_config.devices['devs'])):
logger.exception('Not enough GPU resources for number of '
'MPI tasks requested. Number of MPI tasks '
'should be equal to number of GPUs + 1.')
if (
executor.is_master()
and config.sim_config.general["solver"] == "cuda"
and executor.size - 1 > len(config.sim_config.devices["devs"])
):
logger.exception(
"Not enough GPU resources for number of "
"MPI tasks requested. Number of MPI tasks "
"should be equal to number of GPUs + 1."
)
raise ValueError
jobs = [{'i': i} for i in self.model_range]
jobs = [{"i": i} for i in self.model_range]
# Send the workers to their work loop
executor.start()
if executor.is_master():

查看文件

@@ -19,7 +19,8 @@
from string import Template
update_electric = {
'args_cuda': Template("""
"args_cuda": Template(
"""
__global__ void update_electric(int NX,
int NY,
int NZ,
@@ -30,8 +31,10 @@ update_electric = {
const $REAL* __restrict__ Hx,
const $REAL* __restrict__ Hy,
const $REAL* __restrict__ Hz)
"""),
'args_opencl': Template("""
"""
),
"args_opencl": Template(
"""
int NX,
int NY,
int NZ,
@@ -42,8 +45,10 @@ update_electric = {
__global const $REAL * restrict Hx,
__global const $REAL * restrict Hy,
__global const $REAL * restrict Hz
"""),
'func': Template("""
"""
),
"func": Template(
"""
// Electric field updates - normal materials.
//
// Args:
@@ -85,10 +90,13 @@ update_electric = {
updatecoeffsE[IDX2D_MAT(materialEz,1)] * (Hy[IDX3D_FIELDS(x,y,z)] - Hy[IDX3D_FIELDS(x-1,y,z)]) -
updatecoeffsE[IDX2D_MAT(materialEz,2)] * (Hx[IDX3D_FIELDS(x,y,z)] - Hx[IDX3D_FIELDS(x,y-1,z)]);
}
""")}
"""
),
}
update_magnetic = {
'args_cuda': Template("""
"args_cuda": Template(
"""
__global__ void update_magnetic(int NX,
int NY,
int NZ,
@@ -99,8 +107,10 @@ update_magnetic = {
const $REAL* __restrict__ Ex,
const $REAL* __restrict__ Ey,
const $REAL* __restrict__ Ez)
"""),
'args_opencl': Template("""
"""
),
"args_opencl": Template(
"""
int NX,
int NY,
int NZ,
@@ -111,8 +121,10 @@ update_magnetic = {
__global const $REAL * restrict Ex,
__global const $REAL * restrict Ey,
__global const $REAL * restrict Ez
"""),
'func': Template("""
"""
),
"func": Template(
"""
// Magnetic field updates - normal materials.
//
// Args:
@@ -154,10 +166,13 @@ update_magnetic = {
updatecoeffsH[IDX2D_MAT(materialHz,1)] * (Ey[IDX3D_FIELDS(x+1,y,z)] - Ey[IDX3D_FIELDS(x,y,z)]) +
updatecoeffsH[IDX2D_MAT(materialHz,2)] * (Ex[IDX3D_FIELDS(x,y+1,z)] - Ex[IDX3D_FIELDS(x,y,z)]);
}
""")}
"""
),
}
update_electric_dispersive_A = {
'args_cuda': Template("""
"args_cuda": Template(
"""
__global__ void update_electric_dispersive_A(int NX,
int NY,
int NZ,
@@ -173,8 +188,10 @@ update_electric_dispersive_A = {
const $REAL* __restrict__ Hx,
const $REAL* __restrict__ Hy,
const $REAL* __restrict__ Hz)
"""),
'args_opencl': Template("""
"""
),
"args_opencl": Template(
"""
int NX,
int NY,
int NZ,
@@ -190,8 +207,10 @@ update_electric_dispersive_A = {
__global const $REAL* restrict Hx,
__global const $REAL* restrict Hy,
__global const $REAL* restrict Hz
"""),
'func': Template("""
"""
),
"func": Template(
"""
// Electric field updates - dispersive materials - part A of updates to electric
// field values when dispersive materials
// (with multiple poles) are present.
@@ -265,10 +284,13 @@ update_electric_dispersive_A = {
updatecoeffsE[IDX2D_MAT(materialEz,2)] * (Hx[IDX3D_FIELDS(x,y,z)] - Hx[IDX3D_FIELDS(x,y-1,z)]) -
updatecoeffsE[IDX2D_MAT(materialEz,4)] * phi;
}
""")}
"""
),
}
update_electric_dispersive_B = {
'args_cuda': Template("""
"args_cuda": Template(
"""
__global__ void update_electric_dispersive_B(int NX,
int NY,
int NZ,
@@ -281,8 +303,10 @@ update_electric_dispersive_B = {
const $REAL* __restrict__ Ex,
const $REAL* __restrict__ Ey,
const $REAL* __restrict__ Ez)
"""),
'args_opencl': Template("""
"""
),
"args_opencl": Template(
"""
int NX,
int NY,
int NZ,
@@ -295,8 +319,10 @@ update_electric_dispersive_B = {
__global const $REAL* restrict Ex,
__global const $REAL* restrict Ey,
__global const $REAL* restrict Ez
"""),
'func': Template("""
"""
),
"func": Template(
"""
// Electric field updates - dispersive materials - part B of updates to electric
// field values when dispersive materials
// (with multiple poles) are present.
@@ -352,4 +378,6 @@ update_electric_dispersive_B = {
updatecoeffsdispersive[IDX2D_MATDISP(materialEz,2+(pole*3))] * Ez[IDX3D_FIELDS(x,y,z)];
}
}
""")}
"""
),
}

查看文件

@@ -18,7 +18,9 @@
from string import Template
x_args = {'cuda': Template("""
x_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -38,8 +40,10 @@ x_args = {'cuda': Template("""
const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -67,10 +71,13 @@ x_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
y_args = {'cuda': Template("""
y_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -91,8 +98,10 @@ y_args = {'cuda': Template("""
const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -120,10 +129,13 @@ y_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
z_args = {'cuda': Template("""
z_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -144,8 +156,10 @@ z_args = {'cuda': Template("""
const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -173,12 +187,15 @@ z_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
order1_xminus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order1_xminus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Ey and Ez field components for the xminus slab.
//
// Args:
@@ -248,11 +265,15 @@ order1_xminus = {'args_cuda': x_args['cuda'],
(RA01 * dHy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHy;
}
""")}
"""
),
}
order2_xminus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order2_xminus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Ey and Ez field components for the xminus slab.
//
// Args:
@@ -338,11 +359,15 @@ order2_xminus = {'args_cuda': x_args['cuda'],
(RA0 * dHy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHy;
}
""")}
"""
),
}
order1_xplus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order1_xplus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Ey and Ez field components for the xplus slab.
//
// Args:
@@ -412,11 +437,15 @@ order1_xplus = {'args_cuda': x_args['cuda'],
(RA01 * dHy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHy;
}
""")}
"""
),
}
order2_xplus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order2_xplus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Ey and Ez field components for the xplus slab.
//
// Args:
@@ -501,11 +530,15 @@ order2_xplus = {'args_cuda': x_args['cuda'],
(RA0 * dHy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHy;
}
""")}
"""
),
}
order1_yminus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order1_yminus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ez field components for the yminus slab.
//
// Args:
@@ -575,11 +608,15 @@ order1_yminus = {'args_cuda': y_args['cuda'],
(RA01 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx;
}
""")}
"""
),
}
order2_yminus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order2_yminus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ez field components for the yminus slab.
//
// Args:
@@ -665,11 +702,15 @@ order2_yminus = {'args_cuda': y_args['cuda'],
(RA0 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx;
}
""")}
"""
),
}
order1_yplus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order1_yplus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ez field components for the yplus slab.
//
// Args:
@@ -739,11 +780,15 @@ order1_yplus = {'args_cuda': y_args['cuda'],
(RA01 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx;
}
""")}
"""
),
}
order2_yplus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order2_yplus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ez field components for the yplus slab.
//
// Args:
@@ -829,11 +874,15 @@ order2_yplus = {'args_cuda': y_args['cuda'],
(RA0 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx;
}
""")}
"""
),
}
order1_zminus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order1_zminus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ey field components for the zminus slab.
//
// Args:
@@ -903,11 +952,15 @@ order1_zminus = {'args_cuda': z_args['cuda'],
(RA01 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx;
}
""")}
"""
),
}
order2_zminus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order2_zminus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ey field components for the zminus slab.
//
// Args:
@@ -993,11 +1046,15 @@ order2_zminus = {'args_cuda': z_args['cuda'],
(RA0 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx;
}
""")}
"""
),
}
order1_zplus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order1_zplus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ey field components for the zplus slab.
//
// Args:
@@ -1067,11 +1124,15 @@ order1_zplus = {'args_cuda': z_args['cuda'],
(RA01 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx;
}
""")}
"""
),
}
order2_zplus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order2_zplus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ey field components for the zplus slab.
//
// Args:
@@ -1157,4 +1218,6 @@ order2_zplus = {'args_cuda': z_args['cuda'],
(RA0 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx;
}
""")}
"""
),
}

查看文件

@@ -18,7 +18,9 @@
from string import Template
x_args = {'cuda': Template("""
x_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -38,8 +40,10 @@ x_args = {'cuda': Template("""
const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -67,10 +71,13 @@ x_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
y_args = {'cuda': Template("""
y_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -91,8 +98,10 @@ y_args = {'cuda': Template("""
const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -120,10 +129,13 @@ y_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
z_args = {'cuda': Template("""
z_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -144,8 +156,10 @@ z_args = {'cuda': Template("""
const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -173,12 +187,15 @@ z_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
order1_xminus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order1_xminus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Ey and Ez field components for the xminus slab.
//
// Args:
@@ -254,11 +271,15 @@ order1_xminus = {'args_cuda': x_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHy -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_xminus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order2_xminus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Ey and Ez field components for the xminus slab.
//
// Args:
@@ -344,11 +365,15 @@ order2_xminus = {'args_cuda': x_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHy - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHy - Psi2);
}
""")}
"""
),
}
order1_xplus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order1_xplus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Ey and Ez field components for the xplus slab.
//
// Args:
@@ -424,11 +449,15 @@ order1_xplus = {'args_cuda': x_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHy -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_xplus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order2_xplus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Ey and Ez field components for the xplus slab.
//
// Args:
@@ -514,11 +543,15 @@ order2_xplus = {'args_cuda': x_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHy - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHy - Psi2);
}
""")}
"""
),
}
order1_yminus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order1_yminus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ez field components for the yminus slab.
//
// Args:
@@ -594,11 +627,15 @@ order1_yminus = {'args_cuda': y_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_yminus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order2_yminus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ez field components for the yminus slab.
//
// Args:
@@ -684,11 +721,15 @@ order2_yminus = {'args_cuda': y_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHx - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHx - Psi2);
}
""")}
"""
),
}
order1_yplus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order1_yplus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ez field components for the yplus slab.
//
// Args:
@@ -764,11 +805,15 @@ order1_yplus = {'args_cuda': y_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_yplus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order2_yplus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ez field components for the yplus slab.
//
// Args:
@@ -854,11 +899,15 @@ order2_yplus = {'args_cuda': y_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHx - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHx - Psi2);
}
""")}
"""
),
}
order1_zminus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order1_zminus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ey field components for the zminus slab.
//
// Args:
@@ -934,11 +983,15 @@ order1_zminus = {'args_cuda': z_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_zminus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order2_zminus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ey field components for the zminus slab.
//
// Args:
@@ -1024,11 +1077,15 @@ order2_zminus = {'args_cuda': z_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHx - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHx - Psi2);
}
""")}
"""
),
}
order1_zplus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order1_zplus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ey field components for the zplus slab.
//
// Args:
@@ -1104,11 +1161,15 @@ order1_zplus = {'args_cuda': z_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_zplus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order2_zplus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Ex and Ey field components for the zplus slab.
//
// Args:
@@ -1194,4 +1255,6 @@ order2_zplus = {'args_cuda': z_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHx - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHx - Psi2);
}
""")}
"""
),
}

查看文件

@@ -18,7 +18,9 @@
from string import Template
x_args = {'cuda': Template("""
x_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -38,8 +40,10 @@ x_args = {'cuda': Template("""
$REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -67,10 +71,13 @@ x_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
y_args = {'cuda': Template("""
y_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -92,8 +99,10 @@ y_args = {'cuda': Template("""
$REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -121,10 +130,13 @@ y_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
z_args = {'cuda': Template("""
z_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -146,8 +158,10 @@ z_args = {'cuda': Template("""
$REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -175,12 +189,15 @@ z_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
order1_xminus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order1_xminus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Hy and Hz field components for the xminus slab.
//
// Args:
@@ -250,11 +267,15 @@ order1_xminus = {'args_cuda': x_args['cuda'],
(RA01 * dEy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEy;
}
""")}
"""
),
}
order2_xminus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order2_xminus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Hy and Hz field components for the xminus slab.
//
// Args:
@@ -340,11 +361,15 @@ order2_xminus = {'args_cuda': x_args['cuda'],
(RA0 * dEy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEy;
}
""")}
"""
),
}
order1_xplus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order1_xplus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Hy and Hz field components for the xplus slab.
//
// Args:
@@ -414,11 +439,15 @@ order1_xplus = {'args_cuda': x_args['cuda'],
(RA01 * dEy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEy;
}
""")}
"""
),
}
order2_xplus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order2_xplus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Hy and Hz field components for the xplus slab.
//
// Args:
@@ -504,11 +533,15 @@ order2_xplus = {'args_cuda': x_args['cuda'],
(RA0 * dEy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEy;
}
""")}
"""
),
}
order1_yminus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order1_yminus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hz field components for the yminus slab.
//
// Args:
@@ -578,11 +611,15 @@ order1_yminus = {'args_cuda': y_args['cuda'],
(RA01 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx;
}
""")}
"""
),
}
order2_yminus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order2_yminus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hz field components for the yminus slab.
//
// Args:
@@ -667,11 +704,15 @@ order2_yminus = {'args_cuda': y_args['cuda'],
(RA0 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx;
}
""")}
"""
),
}
order1_yplus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order1_yplus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hz field components for the yplus slab.
//
// Args:
@@ -741,11 +782,15 @@ order1_yplus = {'args_cuda': y_args['cuda'],
(RA01 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx;
}
""")}
"""
),
}
order2_yplus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order2_yplus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hz field components for the yplus slab.
//
// Args:
@@ -829,11 +874,15 @@ order2_yplus = {'args_cuda': y_args['cuda'],
(RA0 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx;
}
""")}
"""
),
}
order1_zminus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order1_zminus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hy field components for the zminus slab.
//
// Args:
@@ -903,11 +952,15 @@ order1_zminus = {'args_cuda': z_args['cuda'],
(RA01 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx;
}
""")}
"""
),
}
order2_zminus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order2_zminus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hy field components for the zminus slab.
//
// Args:
@@ -993,11 +1046,15 @@ order2_zminus = {'args_cuda': z_args['cuda'],
(RA0 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx;
}
""")}
"""
),
}
order1_zplus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order1_zplus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hy field components for the zplus slab.
//
// Args:
@@ -1067,11 +1124,15 @@ order1_zplus = {'args_cuda': z_args['cuda'],
(RA01 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx;
}
""")}
"""
),
}
order2_zplus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order2_zplus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hy field components for the zplus slab.
//
// Args:
@@ -1157,5 +1218,6 @@ order2_zplus = {'args_cuda': z_args['cuda'],
(RA0 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx;
}
""")}
"""
),
}

查看文件

@@ -18,7 +18,9 @@
from string import Template
x_args = {'cuda': Template("""
x_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -38,8 +40,10 @@ x_args = {'cuda': Template("""
$REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -67,10 +71,13 @@ x_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
y_args = {'cuda': Template("""
y_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -92,8 +99,10 @@ y_args = {'cuda': Template("""
$REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -121,10 +130,13 @@ y_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
z_args = {'cuda': Template("""
z_args = {
"cuda": Template(
"""
__global__ void $FUNC(int xs,
int xf,
int ys,
@@ -146,8 +158,10 @@ z_args = {'cuda': Template("""
$REAL *PHI1,
$REAL *PHI2,
const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d)
"""),
'opencl': Template("""
"""
),
"opencl": Template(
"""
int xs,
int xf,
int ys,
@@ -175,12 +189,15 @@ z_args = {'cuda': Template("""
__global const $REAL* restrict RE,
__global const $REAL* restrict RF,
$REAL d
""")
"""
),
}
order1_xminus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order1_xminus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Hy and Hz field components for the xminus slab.
//
// Args:
@@ -256,11 +273,15 @@ order1_xminus = {'args_cuda': x_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEy -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_xminus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order2_xminus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Hy and Hz field components for the xminus slab.
//
// Args:
@@ -346,11 +367,15 @@ order2_xminus = {'args_cuda': x_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEy - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEy - Psi2);
}
""")}
"""
),
}
order1_xplus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order1_xplus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Hy and Hz field components for the xplus slab.
//
// Args:
@@ -426,11 +451,15 @@ order1_xplus = {'args_cuda': x_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEy -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_xplus = {'args_cuda': x_args['cuda'],
'args_opencl': x_args['opencl'],
'func': Template("""
order2_xplus = {
"args_cuda": x_args["cuda"],
"args_opencl": x_args["opencl"],
"func": Template(
"""
// This function updates the Hy and Hz field components for the xplus slab.
//
// Args:
@@ -516,11 +545,15 @@ order2_xplus = {'args_cuda': x_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEy - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEy - Psi2);
}
""")}
"""
),
}
order1_yminus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order1_yminus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hz field components for the yminus slab.
//
// Args:
@@ -596,11 +629,15 @@ order1_yminus = {'args_cuda': y_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_yminus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order2_yminus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hz field components for the yminus slab.
//
// Args:
@@ -686,11 +723,15 @@ order2_yminus = {'args_cuda': y_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEx - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEx - Psi2);
}
""")}
"""
),
}
order1_yplus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order1_yplus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hz field components for the yplus slab.
//
// Args:
@@ -766,11 +807,15 @@ order1_yplus = {'args_cuda': y_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_yplus = {'args_cuda': y_args['cuda'],
'args_opencl': y_args['opencl'],
'func': Template("""
order2_yplus = {
"args_cuda": y_args["cuda"],
"args_opencl": y_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hz field components for the yplus slab.
//
// Args:
@@ -856,11 +901,15 @@ order2_yplus = {'args_cuda': y_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEx - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEx - Psi2);
}
""")}
"""
),
}
order1_zminus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order1_zminus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hy field components for the zminus slab.
//
// Args:
@@ -936,11 +985,15 @@ order1_zminus = {'args_cuda': z_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_zminus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order2_zminus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hy field components for the zminus slab.
//
// Args:
@@ -1026,11 +1079,15 @@ order2_zminus = {'args_cuda': z_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEx - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEx - Psi2);
}
""")}
"""
),
}
order1_zplus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order1_zplus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hy field components for the zplus slab.
//
// Args:
@@ -1106,11 +1163,15 @@ order1_zplus = {'args_cuda': z_args['cuda'],
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx -
RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)];
}
""")}
"""
),
}
order2_zplus = {'args_cuda': z_args['cuda'],
'args_opencl': z_args['opencl'],
'func': Template("""
order2_zplus = {
"args_cuda": z_args["cuda"],
"args_opencl": z_args["opencl"],
"func": Template(
"""
// This function updates the Hx and Hy field components for the zplus slab.
//
// Args:
@@ -1196,5 +1257,6 @@ order2_zplus = {'args_cuda': z_args['cuda'],
PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEx - Psi2);
PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEx - Psi2);
}
""")}
"""
),
}

查看文件

@@ -19,7 +19,9 @@
from string import Template
store_snapshot = {'args_cuda': Template("""
store_snapshot = {
"args_cuda": Template(
"""
__global__ void store_snapshot(int p,
int xs,
int xf,
@@ -42,8 +44,10 @@ store_snapshot = {'args_cuda': Template("""
$REAL *snapHx,
$REAL *snapHy,
$REAL *snapHz)
"""),
'args_opencl': Template("""
"""
),
"args_opencl": Template(
"""
int p,
int xs,
int xf,
@@ -66,8 +70,10 @@ store_snapshot = {'args_cuda': Template("""
__global $REAL *snapHx,
__global $REAL *snapHy,
__global $REAL *snapHz
"""),
'func': Template("""
"""
),
"func": Template(
"""
// Stores field values for a snapshot.
//
// Args:
@@ -118,5 +124,6 @@ store_snapshot = {'args_cuda': Template("""
snapHz[IDX4D_SNAPS(p,x,y,z)] = (Hz[IDX3D_FIELDS(xx,yy,zz)] +
Hz[IDX3D_FIELDS(xx,yy,zz+1)]) / 2;
}
""")
"""
),
}

查看文件

@@ -18,7 +18,9 @@
from string import Template
update_hertzian_dipole = {'args_cuda': Template("""
update_hertzian_dipole = {
"args_cuda": Template(
"""
__global__ void update_hertzian_dipole(int NHERTZDIPOLE,
int iteration,
$REAL dx,
@@ -31,8 +33,10 @@ update_hertzian_dipole = {'args_cuda': Template("""
$REAL *Ex,
$REAL *Ey,
$REAL *Ez)
"""),
'args_opencl': Template("""
"""
),
"args_opencl": Template(
"""
int NHERTZDIPOLE,
int iteration,
$REAL dx,
@@ -45,8 +49,10 @@ update_hertzian_dipole = {'args_cuda': Template("""
__global $REAL *Ex,
__global $REAL *Ey,
__global $REAL *Ez
"""),
'func': Template("""
"""
),
"func": Template(
"""
// Updates electric field values for Hertzian dipole sources.
//
// Args:
@@ -92,10 +98,13 @@ update_hertzian_dipole = {'args_cuda': Template("""
srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * dl * (1 / (dx * dy * dz));
}
}
""")
"""
),
}
update_magnetic_dipole = {'args_cuda': Template("""
update_magnetic_dipole = {
"args_cuda": Template(
"""
__global__ void update_magnetic_dipole(int NMAGDIPOLE,
int iteration,
$REAL dx,
@@ -108,8 +117,10 @@ update_magnetic_dipole = {'args_cuda': Template("""
$REAL *Hx,
$REAL *Hy,
$REAL *Hz)
"""),
'args_opencl': Template("""
"""
),
"args_opencl": Template(
"""
int NMAGDIPOLE,
int iteration,
$REAL dx,
@@ -122,8 +133,10 @@ update_magnetic_dipole = {'args_cuda': Template("""
__global $REAL *Hx,
__global $REAL *Hy,
__global $REAL *Hz
"""),
'func': Template("""
"""
),
"func": Template(
"""
// Updates electric field values for Hertzian dipole sources.
//
// Args:
@@ -167,10 +180,13 @@ update_magnetic_dipole = {'args_cuda': Template("""
srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * (1 / (dx * dy * dz));
}
}
""")
"""
),
}
update_voltage_source = {'args_cuda': Template("""
update_voltage_source = {
"args_cuda": Template(
"""
__global__ void update_voltage_source(int NVOLTSRC,
int iteration,
$REAL dx,
@@ -183,8 +199,10 @@ update_voltage_source = {'args_cuda': Template("""
$REAL *Ex,
$REAL *Ey,
$REAL *Ez)
"""),
'args_opencl': Template("""
"""
),
"args_opencl": Template(
"""
int NVOLTSRC,
int iteration,
$REAL dx,
@@ -197,8 +215,10 @@ update_voltage_source = {'args_cuda': Template("""
__global $REAL *Ex,
__global $REAL *Ey,
__global $REAL *Ez
"""),
'func': Template("""
"""
),
"func": Template(
"""
// Updates electric field values for voltage sources.
//
// Args:
@@ -259,5 +279,6 @@ update_voltage_source = {'args_cuda': Template("""
}
}
}
""")
"""
),
}

查看文件

@@ -19,7 +19,9 @@
from string import Template
store_outputs = {'args_cuda': Template("""
store_outputs = {
"args_cuda": Template(
"""
__global__ void store_outputs(int NRX,
int iteration,
const int* __restrict__ rxcoords,
@@ -30,8 +32,10 @@ store_outputs = {'args_cuda': Template("""
const $REAL* __restrict__ Hx,
const $REAL* __restrict__ Hy,
const $REAL* __restrict__ Hz)
"""),
'args_opencl': Template("""
"""
),
"args_opencl": Template(
"""
int NRX,
int iteration,
__global const int* restrict rxcoords,
@@ -42,8 +46,10 @@ store_outputs = {'args_cuda': Template("""
__global const $REAL* restrict Hx,
__global const $REAL* restrict Hy,
__global const $REAL* restrict Hz
"""),
'func': Template("""
"""
),
"func": Template(
"""
// Stores field component values for every receiver in the model.
//
// Args:
@@ -65,5 +71,6 @@ store_outputs = {'args_cuda': Template("""
rxs[IDX3D_RXS(4,iteration,i)] = Hy[IDX3D_FIELDS(x,y,z)];
rxs[IDX3D_RXS(5,iteration,i)] = Hz[IDX3D_FIELDS(x,y,z)];
}
""")
"""
),
}

查看文件

@@ -38,14 +38,13 @@ def store_outputs(G):
for rx in G.rxs:
for output in rx.outputs:
# Store electric or magnetic field components
if 'I' not in output:
if "I" not in output:
field = locals()[output]
rx.outputs[output][iteration] = field[rx.xcoord, rx.ycoord, rx.zcoord]
# 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]
@@ -65,9 +64,9 @@ def write_hdf5_outputfile(outputfile, G):
# Create output file and write top-level meta data
if G.rxs or sg_rxs:
f = h5py.File(outputfile, 'w')
f.attrs['gprMax'] = __version__
f.attrs['Title'] = G.title
f = h5py.File(outputfile, "w")
f.attrs["gprMax"] = __version__
f.attrs["Title"] = G.title
# Write meta data and data for main grid
if G.rxs:
@@ -76,11 +75,11 @@ def write_hdf5_outputfile(outputfile, G):
# Write meta data and data for any subgrids
if sg_rxs:
for sg in G.subgrids:
grp = f.create_group(f'/subgrids/{sg.name}')
grp = f.create_group(f"/subgrids/{sg.name}")
write_hd5_data(grp, sg, is_subgrid=True)
if G.rxs or sg_rxs:
logger.basic(f'Written output file: {outputfile.name}')
logger.basic(f"Written output file: {outputfile.name}")
def write_hd5_data(basegrp, G, is_subgrid=False):
@@ -93,55 +92,55 @@ def write_hd5_data(basegrp, G, is_subgrid=False):
"""
# Write meta data for grid
basegrp.attrs['Iterations'] = G.iterations
basegrp.attrs['nx_ny_nz'] = (G.nx, G.ny, G.nz)
basegrp.attrs['dx_dy_dz'] = (G.dx, G.dy, G.dz)
basegrp.attrs['dt'] = G.dt
basegrp.attrs["Iterations"] = G.iterations
basegrp.attrs["nx_ny_nz"] = (G.nx, G.ny, G.nz)
basegrp.attrs["dx_dy_dz"] = (G.dx, G.dy, G.dz)
basegrp.attrs["dt"] = G.dt
nsrc = len(G.voltagesources + G.hertziandipoles + G.magneticdipoles + G.transmissionlines)
basegrp.attrs['nsrc'] = nsrc
basegrp.attrs['nrx'] = len(G.rxs)
basegrp.attrs['srcsteps'] = G.srcsteps
basegrp.attrs['rxsteps'] = G.rxsteps
basegrp.attrs["nsrc"] = nsrc
basegrp.attrs["nrx"] = len(G.rxs)
basegrp.attrs["srcsteps"] = G.srcsteps
basegrp.attrs["rxsteps"] = G.rxsteps
if is_subgrid:
# Write additional meta data about subgrid
basegrp.attrs['is_os_sep'] = G.is_os_sep
basegrp.attrs['pml_separation'] = G.pml_separation
basegrp.attrs['subgrid_pml_thickness'] = G.pml['thickness']['x0']
basegrp.attrs['filter'] = G.filter
basegrp.attrs['ratio'] = G.ratio
basegrp.attrs['interpolation'] = G.interpolation
basegrp.attrs["is_os_sep"] = G.is_os_sep
basegrp.attrs["pml_separation"] = G.pml_separation
basegrp.attrs["subgrid_pml_thickness"] = G.pml["thickness"]["x0"]
basegrp.attrs["filter"] = G.filter
basegrp.attrs["ratio"] = G.ratio
basegrp.attrs["interpolation"] = G.interpolation
# Create group for sources (except transmission lines); add type and positional data attributes
srclist = G.voltagesources + G.hertziandipoles + G.magneticdipoles
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 * G.dx, src.ycoord * G.dy, src.zcoord * G.dz)
grp = basegrp.create_group(f"srcs/src{str(srcindex + 1)}")
grp.attrs["Type"] = type(src).__name__
grp.attrs["Position"] = (src.xcoord * G.dx, src.ycoord * G.dy, src.zcoord * G.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(G.transmissionlines):
grp = basegrp.create_group('tls/tl' + str(tlindex + 1))
grp.attrs['Position'] = (tl.xcoord * G.dx, tl.ycoord * G.dy, tl.zcoord * G.dz)
grp.attrs['Resistance'] = tl.resistance
grp.attrs['dl'] = tl.dl
grp = basegrp.create_group("tls/tl" + str(tlindex + 1))
grp.attrs["Position"] = (tl.xcoord * G.dx, tl.ycoord * G.dy, tl.zcoord * G.dz)
grp.attrs["Resistance"] = tl.resistance
grp.attrs["dl"] = tl.dl
# Save incident voltage and current
grp['Vinc'] = tl.Vinc
grp['Iinc'] = tl.Iinc
grp["Vinc"] = tl.Vinc
grp["Iinc"] = tl.Iinc
# Save total voltage and current
basegrp['tls/tl' + str(tlindex + 1) + '/Vtotal'] = tl.Vtotal
basegrp['tls/tl' + str(tlindex + 1) + '/Itotal'] = tl.Itotal
basegrp["tls/tl" + str(tlindex + 1) + "/Vtotal"] = tl.Vtotal
basegrp["tls/tl" + str(tlindex + 1) + "/Itotal"] = tl.Itotal
# Create group, add positional data and write field component arrays for receivers
for rxindex, rx in enumerate(G.rxs):
grp = basegrp.create_group('rxs/rx' + str(rxindex + 1))
grp = basegrp.create_group("rxs/rx" + str(rxindex + 1))
if rx.ID:
grp.attrs['Name'] = rx.ID
grp.attrs['Position'] = (rx.xcoord * G.dx, rx.ycoord * G.dy, rx.zcoord * G.dz)
grp.attrs["Name"] = rx.ID
grp.attrs["Position"] = (rx.xcoord * G.dx, rx.ycoord * G.dy, rx.zcoord * G.dz)
for output in rx.outputs:
basegrp['rxs/rx' + str(rxindex + 1) + '/' + output] = rx.outputs[output]
basegrp["rxs/rx" + str(rxindex + 1) + "/" + output] = rx.outputs[output]
def Ix(x, y, z, Hx, Hy, Hz, G):

查看文件

@@ -24,13 +24,13 @@ import gprMax.config as config
from .cython.fractals_generate import generate_fractal2D, generate_fractal3D
from .utilities.utilities import round_value
np.seterr(divide='raise')
np.seterr(divide="raise")
class FractalSurface:
"""Fractal surfaces."""
surfaceIDs = ['xminus', 'xplus', 'yminus', 'yplus', 'zminus', 'zplus']
surfaceIDs = ["xminus", "xplus", "yminus", "yplus", "zminus", "zplus"]
def __init__(self, xs, xf, ys, yf, zs, zf, dimension):
"""
@@ -83,8 +83,7 @@ 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)
@@ -96,8 +95,16 @@ class FractalSurface:
A = fftpack.fftshift(A)
# Generate fractal
generate_fractal2D(surfacedims[0], surfacedims[1], config.get_model_config().ompthreads,
self.b, self.weighting, v1, A, self.fractalsurface)
generate_fractal2D(
surfacedims[0],
surfacedims[1],
config.get_model_config().ompthreads,
self.b,
self.weighting,
v1,
A,
self.fractalsurface,
)
# Shift the zero frequency component to start of the array
self.fractalsurface = fftpack.ifftshift(self.fractalsurface)
# Set DC component of FFT to zero
@@ -105,16 +112,18 @@ class FractalSurface:
# Take the real part (numerical errors can give rise to an imaginary part)
# of the IFFT, and convert type to floattype. N.B calculation of fractals
# must always be carried out at double precision, i.e. float64, complex128
self.fractalsurface = np.real(fftpack.ifftn(self.fractalsurface)).astype(config.sim_config.dtypes['float_or_double'],
copy=False)
self.fractalsurface = np.real(fftpack.ifftn(self.fractalsurface)).astype(
config.sim_config.dtypes["float_or_double"], copy=False
)
# Scale the fractal volume according to requested range
fractalmin = np.amin(self.fractalsurface)
fractalmax = np.amax(self.fractalsurface)
fractalrange = fractalmax - fractalmin
self.fractalsurface = (self.fractalsurface * ((self.fractalrange[1]
- self.fractalrange[0]) / fractalrange)
+ self.fractalrange[0] - ((self.fractalrange[1]
- self.fractalrange[0]) / fractalrange) * fractalmin)
self.fractalsurface = (
self.fractalsurface * ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange)
+ self.fractalrange[0]
- ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) * fractalmin
)
class FractalVolume:
@@ -177,8 +186,9 @@ class FractalVolume:
self.fractalvolume = np.zeros((self.nx, self.ny, self.nz), dtype=self.dtype)
# 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])
v1 = np.array(
[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
rng = np.random.default_rng(seed=self.seed)
@@ -190,8 +200,17 @@ class FractalVolume:
A = fftpack.fftshift(A)
# Generate fractal
generate_fractal3D(self.nx, self.ny, self.nz, config.get_model_config().ompthreads,
self.b, self.weighting, v1, A, self.fractalvolume)
generate_fractal3D(
self.nx,
self.ny,
self.nz,
config.get_model_config().ompthreads,
self.b,
self.weighting,
v1,
A,
self.fractalvolume,
)
# Shift the zero frequency component to the start of the array
self.fractalvolume = fftpack.ifftshift(self.fractalvolume)
@@ -200,8 +219,9 @@ class FractalVolume:
# Take the real part (numerical errors can give rise to an imaginary part)
# of the IFFT, and convert type to floattype. N.B calculation of fractals
# must always be carried out at double precision, i.e. float64, complex128
self.fractalvolume = np.real(fftpack.ifftn(self.fractalvolume)).astype(config.sim_config.dtypes['float_or_double'],
copy=False)
self.fractalvolume = np.real(fftpack.ifftn(self.fractalvolume)).astype(
config.sim_config.dtypes["float_or_double"], copy=False
)
# Bin fractal values
bins = np.linspace(np.amin(self.fractalvolume), np.amax(self.fractalvolume), self.nbins)
@@ -235,8 +255,7 @@ 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 = None
# Randomly defined parameters that will be used to calculate geometry
@@ -264,10 +283,16 @@ class Grass:
x, y: floats for the x and y coordinates of grass blade.
"""
x = (self.geometryparams[blade, 2] * (height / self.geometryparams[blade, 0])
* (height / self.geometryparams[blade, 0]))
y = (self.geometryparams[blade, 3] * (height / self.geometryparams[blade, 1])
* (height / self.geometryparams[blade, 1]))
x = (
self.geometryparams[blade, 2]
* (height / self.geometryparams[blade, 0])
* (height / self.geometryparams[blade, 0])
)
y = (
self.geometryparams[blade, 3]
* (height / self.geometryparams[blade, 1])
* (height / self.geometryparams[blade, 1])
)
x = round_value(x)
y = round_value(y)

查看文件

@@ -32,9 +32,7 @@ import gprMax.config as config
from ._version import __version__
from .cython.geometry_outputs import write_lines
from .subgrids.grid import SubGridBaseGrid
from .utilities.utilities import (get_terminal_width,
numeric_list_to_float_list,
numeric_list_to_int_list)
from .utilities.utilities import get_terminal_width, numeric_list_to_float_list, numeric_list_to_int_list
logger = logging.getLogger(__name__)
@@ -46,23 +44,27 @@ def save_geometry_views(gvs):
gvs: list of all GeometryViews.
"""
logger.info('')
logger.info("")
for i, gv in enumerate(gvs):
gv.set_filename()
vtk_data = gv.prep_vtk()
pbar = tqdm(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}',
ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.sim_config.general['progressbars'])
pbar = tqdm(
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}",
ncols=get_terminal_width() - 1,
file=sys.stdout,
disable=not config.sim_config.general["progressbars"],
)
gv.write_vtk(vtk_data)
pbar.update(gv.nbytes)
pbar.close()
logger.info('')
logger.info("")
class GeometryView():
class GeometryView:
"""Base class for Geometry Views."""
def __init__(self, xs, ys, zs, xf, yf, zf, dx, dy, dz, filename, grid):
@@ -93,9 +95,7 @@ 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.filename +
config.get_model_config().appendmodelnumber)
self.filename = Path(*parts[:-1], self.filename + config.get_model_config().appendmodelnumber)
class GeometryViewLines(GeometryView):
@@ -114,30 +114,37 @@ class GeometryViewLines(GeometryView):
# Sample ID array according to geometry view spatial discretisation
# Only create a new array if subsampling is required
if (self.grid.ID.shape != (self.xf, self.yf, self.zf) or
(self.dx, self.dy, self.dz) != (1, 1, 1) or
(self.xs, self.ys, self.zs) != (0, 0, 0)):
if (
self.grid.ID.shape != (self.xf, self.yf, self.zf)
or (self.dx, self.dy, self.dz) != (1, 1, 1)
or (self.xs, self.ys, self.zs) != (0, 0, 0)
):
# 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])
ID = np.ascontiguousarray(
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
ID = self.grid.ID
x, y, z, lines = write_lines((self.xs * self.grid.dx),
x, y, z, lines = write_lines(
(self.xs * self.grid.dx),
(self.ys * self.grid.dy),
(self.zs * self.grid.dz),
self.nx, self.ny, self.nz,
self.nx,
self.ny,
self.nz,
(self.dx * self.grid.dx),
(self.dy * self.grid.dy),
(self.dz * self.grid.dz), ID)
(self.dz * self.grid.dz),
ID,
)
# Add offset to subgrid geometry to correctly locate within main grid
if isinstance(self.grid, SubGridBaseGrid):
x += (self.grid.i0 * self.grid.dx * self.grid.ratio)
y += (self.grid.j0 * self.grid.dy * self.grid.ratio)
z += (self.grid.k0 * self.grid.dz * self.grid.ratio)
x += self.grid.i0 * self.grid.dx * self.grid.ratio
y += self.grid.j0 * self.grid.dy * self.grid.ratio
z += self.grid.k0 * self.grid.dz * self.grid.ratio
# Write information about any PMLs, sources, receivers
comments = Comments(self.grid, self)
@@ -147,14 +154,12 @@ class GeometryViewLines(GeometryView):
comments = json.dumps(info)
# Number of bytes of data to be written to file
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)
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
vtk_data = {'x': x, 'y': y, 'z': z, 'data': lines, 'comments': comments}
vtk_data = {"x": x, "y": y, "z": z, "data": lines, "comments": comments}
return vtk_data
@@ -166,9 +171,14 @@ class GeometryViewLines(GeometryView):
"""
# Write the VTK file .vtu
linesToVTK(str(self.filename), vtk_data['x'], vtk_data['y'],
vtk_data['z'], cellData={"Material": vtk_data['data']},
comments=[vtk_data['comments']])
linesToVTK(
str(self.filename),
vtk_data["x"],
vtk_data["y"],
vtk_data["z"],
cellData={"Material": vtk_data["data"]},
comments=[vtk_data["comments"]],
)
class GeometryViewVoxels(GeometryView):
@@ -187,13 +197,15 @@ class GeometryViewVoxels(GeometryView):
# Sample solid array according to geometry view spatial discretisation
# Only create a new array if subsampling is required
if (self.grid.solid.shape != (self.xf, self.yf, self.zf) or
(self.dx, self.dy, self.dz) != (1, 1, 1) or
(self.xs, self.ys, self.zs) != (0, 0, 0)):
if (
self.grid.solid.shape != (self.xf, self.yf, self.zf)
or (self.dx, self.dy, self.dz) != (1, 1, 1)
or (self.xs, self.ys, self.zs) != (0, 0, 0)
):
# 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])
solid = np.ascontiguousarray(
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
solid = self.grid.solid
@@ -205,7 +217,7 @@ class GeometryViewVoxels(GeometryView):
self.nbytes = solid.nbytes
vtk_data = {'data': solid, 'comments': comments}
vtk_data = {"data": solid, "comments": comments}
return vtk_data
@@ -217,25 +229,25 @@ class GeometryViewVoxels(GeometryView):
"""
if isinstance(self.grid, SubGridBaseGrid):
origin = ((self.grid.i0 * self.grid.dx * self.grid.ratio),
origin = (
(self.grid.i0 * self.grid.dx * self.grid.ratio),
(self.grid.j0 * self.grid.dy * self.grid.ratio),
(self.grid.k0 * self.grid.dz * self.grid.ratio))
(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),
imageToVTK(
str(self.filename),
origin=origin,
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']])
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"]],
)
class Comments():
class Comments:
"""Comments can be strings included in the header of XML VTK file, and are
used to hold extra (gprMax) information about the VTK data.
"""
@@ -252,45 +264,50 @@ class Comments():
"""
# Comments for Paraview macro
comments = {'gprMax_version': __version__,
'dx_dy_dz': self.dx_dy_dz_comment(),
'nx_ny_nz': self.nx_ny_nz_comment(),
'Materials': self.materials_comment()} # Write the name and numeric ID for each material
comments = {
"gprMax_version": __version__,
"dx_dy_dz": self.dx_dy_dz_comment(),
"nx_ny_nz": self.nx_ny_nz_comment(),
"Materials": self.materials_comment(),
} # Write the name and numeric ID for each material
# Write information on PMLs, sources, and receivers
if not self.materials_only:
# Information on PML thickness
if self.grid.pmls['slabs']:
comments['PMLthickness'] = self.pml_gv_comment()
srcs = (self.grid.hertziandipoles + self.grid.magneticdipoles +
self.grid.voltagesources + self.grid.transmissionlines)
if self.grid.pmls["slabs"]:
comments["PMLthickness"] = self.pml_gv_comment()
srcs = (
self.grid.hertziandipoles
+ self.grid.magneticdipoles
+ self.grid.voltagesources
+ self.grid.transmissionlines
)
if srcs:
comments['Sources'] = self.srcs_rx_gv_comment(srcs)
comments["Sources"] = self.srcs_rx_gv_comment(srcs)
if self.grid.rxs:
comments['Receivers'] = self.srcs_rx_gv_comment(self.grid.rxs)
comments["Receivers"] = self.srcs_rx_gv_comment(self.grid.rxs)
return comments
def pml_gv_comment(self):
grid = self.grid
# Only render PMLs if they are in the geometry view
pmlstorender = dict.fromkeys(grid.pmls['thickness'], 0)
pmlstorender = dict.fromkeys(grid.pmls["thickness"], 0)
# Casting to int required as json does not handle numpy types
if grid.pmls['thickness']['x0'] - self.gv.xs > 0:
pmlstorender['x0'] = int(grid.pmls['thickness']['x0'] - self.gv.xs)
if grid.pmls['thickness']['y0'] - self.gv.ys > 0:
pmlstorender['y0'] = int(grid.pmls['thickness']['y0'] - self.gv.ys)
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']))
if 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']))
if grid.pmls["thickness"]["x0"] - self.gv.xs > 0:
pmlstorender["x0"] = int(grid.pmls["thickness"]["x0"] - self.gv.xs)
if grid.pmls["thickness"]["y0"] - self.gv.ys > 0:
pmlstorender["y0"] = int(grid.pmls["thickness"]["y0"] - self.gv.ys)
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"]))
if 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"]))
return list(pmlstorender.values())
@@ -298,13 +315,10 @@ 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 = numeric_list_to_float_list(p)
s = {'name': src.ID,
'position': p}
s = {"name": src.ID, "position": p}
sc.append(s)
return sc
@@ -317,7 +331,7 @@ 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]
@@ -344,25 +358,16 @@ class GeometryObjects:
self.basefilename = basefilename
# Set filenames
parts = config.sim_config.input_file_path.with_suffix('').parts
parts = config.sim_config.input_file_path.with_suffix("").parts
self.filename_hdf5 = Path(*parts[:-1], self.basefilename)
self.filename_hdf5 = self.filename_hdf5.with_suffix('.h5')
self.filename_materials = Path(*parts[:-1], f'{self.basefilename}_materials')
self.filename_materials = self.filename_materials.with_suffix('.txt')
self.filename_hdf5 = self.filename_hdf5.with_suffix(".h5")
self.filename_materials = Path(*parts[:-1], f"{self.basefilename}_materials")
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):
@@ -373,55 +378,50 @@ class GeometryObjects:
pbar: Progress bar class instance.
"""
with h5py.File(self.filename_hdf5, 'w') as fdata:
fdata.attrs['gprMax'] = __version__
fdata.attrs['Title'] = G.title
fdata.attrs['dx_dy_dz'] = (G.dx, G.dy, G.dz)
with h5py.File(self.filename_hdf5, "w") as fdata:
fdata.attrs["gprMax"] = __version__
fdata.attrs["Title"] = G.title
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])
fdata['/data'] = G.solid[self.xs:self.xf + 1, self.ys:self.yf +
1, self.zs:self.zf + 1].astype('int16') - minmat
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
)
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
# This includes all materials in range whether used in volume or not
with open(self.filename_materials, 'w') as fmaterials:
with open(self.filename_materials, "w") as fmaterials:
for numID in range(minmat, maxmat + 1):
for material in G.materials:
if material.numID == numID:
fmaterials.write(f'#material: {material.er:g} {material.se:g} '
f'{material.mr:g} {material.sm:g} {material.ID}\n')
if hasattr(material, 'poles'):
if 'debye' in material.type:
dispersionstr = ('#add_dispersion_debye: '
f'{material.poles:g} ')
fmaterials.write(
f"#material: {material.er:g} {material.se:g} "
f"{material.mr:g} {material.sm:g} {material.ID}\n"
)
if hasattr(material, "poles"):
if "debye" in material.type:
dispersionstr = "#add_dispersion_debye: " f"{material.poles:g} "
for pole in range(material.poles):
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"{material.deltaer[pole]:g} " f"{material.tau[pole]:g} "
elif "lorenz" in material.type:
dispersionstr = f"#add_dispersion_lorenz: " f"{material.poles:g} "
for pole in range(material.poles):
dispersionstr += (f'{material.deltaer[pole]:g} '
f'{material.tau[pole]:g} '
f'{material.alpha[pole]:g} ')
elif 'drude' in material.type:
dispersionstr = (f'#add_dispersion_drude: '
f'{material.poles:g} ')
dispersionstr += (
f"{material.deltaer[pole]:g} "
f"{material.tau[pole]:g} "
f"{material.alpha[pole]:g} "
)
elif "drude" in material.type:
dispersionstr = f"#add_dispersion_drude: " f"{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')
fmaterials.write(dispersionstr + "\n")

查看文件

@@ -24,78 +24,79 @@ from .contexts import Context, MPIContext
from .utilities.logging import logging_config
# Arguments (used for API) and their default values (used for API and CLI)
args_defaults = {'scenes': None,
'inputfile': None,
'outputfile': None,
'n': 1,
'i': None,
'mpi': False,
'gpu': None,
'opencl': None,
'subgrid': False,
'autotranslate': False,
'geometry_only': False,
'geometry_fixed': False,
'write_processed': False,
'log_level': 20, # Level DEBUG = 10; INFO = 20; BASIC = 25
'log_file': False}
args_defaults = {
"scenes": None,
"inputfile": None,
"outputfile": None,
"n": 1,
"i": None,
"mpi": False,
"gpu": None,
"opencl": None,
"subgrid": False,
"autotranslate": False,
"geometry_only": False,
"geometry_fixed": False,
"write_processed": False,
"log_level": 20, # Level DEBUG = 10; INFO = 20; BASIC = 25
"log_file": False,
}
# Argument help messages (used for CLI argparse)
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.',
'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 typically be used to restart a series of '
'models from a specific model number, with the n argument, '
'e.g. to restart from A-scan 45 when creating a B-scan '
'with 60 traces.',
'mpi': '(bool, opt): Flag to use Message Passing Interface (MPI) '
'task farm. This option is most usefully combined with n to '
'allow individual 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 parallel '
'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).',
'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.',
'write_processed': '(bool, opt): Writes another input file after '
'any Python code (#python blocks) and in the '
'original input file has been processed.',
'log_level': '(int, opt): Level of logging to use.',
'log_file': '(bool, opt): Write logging information to file.'}
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.",
"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 typically be used to restart a series of "
"models from a specific model number, with the n argument, "
"e.g. to restart from A-scan 45 when creating a B-scan "
"with 60 traces.",
"mpi": "(bool, opt): Flag to use Message Passing Interface (MPI) "
"task farm. This option is most usefully combined with n to "
"allow individual 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 parallel "
"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).",
"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.",
"write_processed": "(bool, opt): Writes another input file after "
"any Python code (#python blocks) and in the "
"original input file has been processed.",
"log_level": "(int, opt): Level of logging to use.",
"log_file": "(bool, opt): Write logging information to file.",
}
def run(scenes=args_defaults['scenes'],
inputfile=args_defaults['inputfile'],
outputfile=args_defaults['outputfile'],
n=args_defaults['n'],
i=args_defaults['i'],
mpi=args_defaults['mpi'],
gpu=args_defaults['gpu'],
opencl=args_defaults['opencl'],
subgrid=args_defaults['subgrid'],
autotranslate=args_defaults['autotranslate'],
geometry_only=args_defaults['geometry_only'],
geometry_fixed=args_defaults['geometry_fixed'],
write_processed=args_defaults['write_processed'],
log_level=args_defaults['log_level'],
log_file=args_defaults['log_file']):
def run(
scenes=args_defaults["scenes"],
inputfile=args_defaults["inputfile"],
outputfile=args_defaults["outputfile"],
n=args_defaults["n"],
i=args_defaults["i"],
mpi=args_defaults["mpi"],
gpu=args_defaults["gpu"],
opencl=args_defaults["opencl"],
subgrid=args_defaults["subgrid"],
autotranslate=args_defaults["autotranslate"],
geometry_only=args_defaults["geometry_only"],
geometry_fixed=args_defaults["geometry_fixed"],
write_processed=args_defaults["write_processed"],
log_level=args_defaults["log_level"],
log_file=args_defaults["log_file"],
):
"""Entry point for application programming interface (API). Runs the
simulation for the given list of scenes.
@@ -138,21 +139,25 @@ def run(scenes=args_defaults['scenes'],
log_file: optional boolean to write logging information to file.
"""
args = argparse.Namespace(**{'scenes': scenes,
'inputfile': inputfile,
'outputfile': outputfile,
'n': n,
'i': i,
'mpi': mpi,
'gpu': gpu,
'opencl': opencl,
'subgrid': subgrid,
'autotranslate': autotranslate,
'geometry_only': geometry_only,
'geometry_fixed': geometry_fixed,
'write_processed': write_processed,
'log_level': log_level,
'log_file': log_file})
args = argparse.Namespace(
**{
"scenes": scenes,
"inputfile": inputfile,
"outputfile": outputfile,
"n": n,
"i": i,
"mpi": mpi,
"gpu": gpu,
"opencl": opencl,
"subgrid": subgrid,
"autotranslate": autotranslate,
"geometry_only": geometry_only,
"geometry_fixed": geometry_fixed,
"write_processed": write_processed,
"log_level": log_level,
"log_file": log_file,
}
)
run_main(args)
@@ -161,32 +166,30 @@ def cli():
"""Entry point for command line interface (CLI)."""
# Parse command line arguments
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'])
parser.add_argument('--geometry-fixed', action='store_true',
default=args_defaults['geometry_fixed'],
help=help_msg['geometry_fixed'])
parser.add_argument('--write-processed', action='store_true',
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 = 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"]
)
parser.add_argument(
"--geometry-fixed",
action="store_true",
default=args_defaults["geometry_fixed"],
help=help_msg["geometry_fixed"],
)
parser.add_argument(
"--write-processed",
action="store_true",
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"])
args = parser.parse_args()
results = run_main(args)

查看文件

@@ -26,7 +26,7 @@ import gprMax.config as config
from .pml import PML
from .utilities.utilities import fft_power, round_value
np.seterr(invalid='raise')
np.seterr(invalid="raise")
class FDTDGrid:
@@ -35,8 +35,8 @@ class FDTDGrid:
"""
def __init__(self):
self.title = ''
self.name = 'main_grid'
self.title = ""
self.name = "main_grid"
self.mem_use = 0
self.nx = 0
@@ -53,14 +53,14 @@ class FDTDGrid:
# PML parameters - set some defaults to use if not user provided
self.pmls = {}
self.pmls['formulation'] = 'HORIPML'
self.pmls['cfs'] = []
self.pmls['slabs'] = []
self.pmls["formulation"] = "HORIPML"
self.pmls["cfs"] = []
self.pmls["slabs"] = []
# Ordered dictionary required so that PMLs are always updated in the
# same order. The order itself does not matter, however, if must be the
# same from model to model otherwise the numerical precision from adding
# the PML corrections will be different.
self.pmls['thickness'] = OrderedDict((key, 10) for key in PML.boundaryIDs)
self.pmls["thickness"] = OrderedDict((key, 10) for key in PML.boundaryIDs)
self.materials = []
self.mixingmodels = []
@@ -81,11 +81,11 @@ class FDTDGrid:
def within_bounds(self, p):
if p[0] < 0 or p[0] > self.nx:
raise ValueError('x')
raise ValueError("x")
if p[1] < 0 or p[1] > self.ny:
raise ValueError('y')
raise ValueError("y")
if p[2] < 0 or p[2] > self.nz:
raise ValueError('z')
raise ValueError("z")
def discretise_point(self, p):
x = round_value(float(p[0]) / self.dx)
@@ -95,18 +95,18 @@ class FDTDGrid:
def round_to_grid(self, p):
p = self.discretise_point(p)
p_r = (p[0] * self.dx,
p[1] * self.dy,
p[2] * self.dz)
p_r = (p[0] * self.dx, p[1] * self.dy, p[2] * self.dz)
return p_r
def within_pml(self, p):
if (p[0] < self.pmls['thickness']['x0'] or
p[0] > self.nx - self.pmls['thickness']['xmax'] or
p[1] < self.pmls['thickness']['y0'] or
p[1] > self.ny - self.pmls['thickness']['ymax'] or
p[2] < self.pmls['thickness']['z0'] or
p[2] > self.nz - self.pmls['thickness']['zmax']):
if (
p[0] < self.pmls["thickness"]["x0"]
or p[0] > self.nx - self.pmls["thickness"]["xmax"]
or p[1] < self.pmls["thickness"]["y0"]
or p[1] > self.ny - self.pmls["thickness"]["ymax"]
or p[2] < self.pmls["thickness"]["z0"]
or p[2] > self.nz - self.pmls["thickness"]["zmax"]
):
return True
else:
return False
@@ -122,59 +122,55 @@ class FDTDGrid:
self.rigidE = np.zeros((12, self.nx, self.ny, self.nz), dtype=np.int8)
self.rigidH = np.zeros((6, self.nx, self.ny, self.nz), dtype=np.int8)
self.ID = np.ones((6, self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.uint32)
self.IDlookup = {'Ex': 0, 'Ey': 1, 'Ez': 2, 'Hx': 3, 'Hy': 4, 'Hz': 5}
self.IDlookup = {"Ex": 0, "Ey": 1, "Ez": 2, "Hx": 3, "Hy": 4, "Hz": 5}
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),
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),
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),
dtype=config.get_model_config().materials['dispersivedtype'])
self.Tx = np.zeros(
(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),
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),
dtype=config.get_model_config().materials["dispersivedtype"],
)
def initialise_dispersive_update_coeff_array(self):
"""Initialise array for storing update coefficients when there are dispersive
materials present.
"""
self.updatecoeffsdispersive = np.zeros((len(self.materials), 3 *
config.get_model_config().materials['maxpoles']),
dtype=config.get_model_config().materials['dispersivedtype'])
self.updatecoeffsdispersive = np.zeros(
(len(self.materials), 3 * config.get_model_config().materials["maxpoles"]),
dtype=config.get_model_config().materials["dispersivedtype"],
)
def reset_fields(self):
"""Clear arrays for field components and PMLs."""
# Clear arrays for field components
self.initialise_field_arrays()
if config.get_model_config().materials['maxpoles'] > 0:
if config.get_model_config().materials["maxpoles"] > 0:
self.initialise_dispersive_arrays()
# Clear arrays for fields in PML
for pml in self.pmls['slabs']:
for pml in self.pmls["slabs"]:
pml.initialise_field_arrays()
def mem_est_basic(self):
@@ -190,28 +186,33 @@ class FDTDGrid:
rigidarrays = (12 + 6) * self.nx * self.ny * self.nz * np.dtype(np.int8).itemsize
# 6 x field arrays + 6 x ID arrays
fieldarrays = ((6 + 6) * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) *
np.dtype(config.sim_config.dtypes['float_or_double']).itemsize)
fieldarrays = (
(6 + 6)
* (self.nx + 1)
* (self.ny + 1)
* (self.nz + 1)
* np.dtype(config.sim_config.dtypes["float_or_double"]).itemsize
)
# PML arrays
pmlarrays = 0
for (k, v) in self.pmls['thickness'].items():
for k, v in self.pmls["thickness"].items():
if v > 0:
if 'x' in k:
pmlarrays += ((v + 1) * self.ny * (self.nz + 1))
pmlarrays += ((v + 1) * (self.ny + 1) * self.nz)
pmlarrays += (v * self.ny * (self.nz + 1))
pmlarrays += (v * (self.ny + 1) * self.nz)
elif 'y' in k:
pmlarrays += (self.nx * (v + 1) * (self.nz + 1))
pmlarrays += ((self.nx + 1) * (v + 1) * self.nz)
pmlarrays += ((self.nx + 1) * v * self.nz)
pmlarrays += (self.nx * v * (self.nz + 1))
elif 'z' in k:
pmlarrays += (self.nx * (self.ny + 1) * (v + 1))
pmlarrays += ((self.nx + 1) * self.ny * (v + 1))
pmlarrays += ((self.nx + 1) * self.ny * v)
pmlarrays += (self.nx * (self.ny + 1) * v)
if "x" in k:
pmlarrays += (v + 1) * self.ny * (self.nz + 1)
pmlarrays += (v + 1) * (self.ny + 1) * self.nz
pmlarrays += v * self.ny * (self.nz + 1)
pmlarrays += v * (self.ny + 1) * self.nz
elif "y" in k:
pmlarrays += self.nx * (v + 1) * (self.nz + 1)
pmlarrays += (self.nx + 1) * (v + 1) * self.nz
pmlarrays += (self.nx + 1) * v * self.nz
pmlarrays += self.nx * v * (self.nz + 1)
elif "z" in k:
pmlarrays += self.nx * (self.ny + 1) * (v + 1)
pmlarrays += (self.nx + 1) * self.ny * (v + 1)
pmlarrays += (self.nx + 1) * self.ny * v
pmlarrays += self.nx * (self.ny + 1) * v
mem_use = int(fieldarrays + solidarray + rigidarrays + pmlarrays)
@@ -224,9 +225,14 @@ class FDTDGrid:
mem_use: int of memory (bytes).
"""
mem_use = int(3 * config.get_model_config().materials['maxpoles'] *
(self.nx + 1) * (self.ny + 1) * (self.nz + 1) *
np.dtype(config.get_model_config().materials['dispersivedtype']).itemsize)
mem_use = int(
3
* config.get_model_config().materials["maxpoles"]
* (self.nx + 1)
* (self.ny + 1)
* (self.nz + 1)
* np.dtype(config.get_model_config().materials["dispersivedtype"]).itemsize
)
return mem_use
def mem_est_fractals(self):
@@ -240,12 +246,10 @@ class FDTDGrid:
mem_use = 0
for vol in self.fractalvolumes:
mem_use += (vol.nx * vol.ny * vol.nz * vol.dtype.itemsize)
mem_use += vol.nx * vol.ny * vol.nz * vol.dtype.itemsize
for surface in vol.fractalsurfaces:
surfacedims = surface.get_surface_dims()
mem_use += (surfacedims[0] *
surfacedims[1] *
surface.dtype.itemsize)
mem_use += surfacedims[0] * surfacedims[1] * surface.dtype.itemsize
return mem_use
@@ -281,18 +285,16 @@ 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)))
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)))
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)))
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)))
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)))
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)))
else:
self.dt = 1 / (config.sim_config.em_consts['c'] *
np.sqrt((1 / self.dx**2) + (1 / self.dy**2) + (1 / self.dz**2)))
self.dt = 1 / (
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
# hardware maximum. Avoids inadvertently exceeding the CFL due to
@@ -316,8 +318,7 @@ 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, queue=None):
"""Initialise an array for cell edge IDs (ID) on compute device.
@@ -326,12 +327,14 @@ class CUDAGrid(FDTDGrid):
queue: pyopencl queue.
"""
if config.sim_config.general['solver'] == 'cuda':
if config.sim_config.general["solver"] == "cuda":
import pycuda.gpuarray as gpuarray
self.ID_dev = gpuarray.to_gpu(self.ID)
elif config.sim_config.general['solver'] == 'opencl':
elif config.sim_config.general["solver"] == "opencl":
import pyopencl.array as clarray
self.ID_dev = clarray.to_device(queue, self.ID)
def htod_field_arrays(self, queue=None):
@@ -341,16 +344,18 @@ class CUDAGrid(FDTDGrid):
queue: pyopencl queue.
"""
if config.sim_config.general['solver'] == 'cuda':
if config.sim_config.general["solver"] == "cuda":
import pycuda.gpuarray as gpuarray
self.Ex_dev = gpuarray.to_gpu(self.Ex)
self.Ey_dev = gpuarray.to_gpu(self.Ey)
self.Ez_dev = gpuarray.to_gpu(self.Ez)
self.Hx_dev = gpuarray.to_gpu(self.Hx)
self.Hy_dev = gpuarray.to_gpu(self.Hy)
self.Hz_dev = gpuarray.to_gpu(self.Hz)
elif config.sim_config.general['solver'] == 'opencl':
elif config.sim_config.general["solver"] == "opencl":
import pyopencl.array as clarray
self.Ex_dev = clarray.to_device(queue, self.Ex)
self.Ey_dev = clarray.to_device(queue, self.Ey)
self.Ez_dev = clarray.to_device(queue, self.Ez)
@@ -365,14 +370,16 @@ class CUDAGrid(FDTDGrid):
queue: pyopencl queue.
"""
if config.sim_config.general['solver'] == 'cuda':
if config.sim_config.general["solver"] == "cuda":
import pycuda.gpuarray as gpuarray
self.Tx_dev = gpuarray.to_gpu(self.Tx)
self.Ty_dev = gpuarray.to_gpu(self.Ty)
self.Tz_dev = gpuarray.to_gpu(self.Tz)
self.updatecoeffsdispersive_dev = gpuarray.to_gpu(self.updatecoeffsdispersive)
elif config.sim_config.general['solver'] == 'opencl':
elif config.sim_config.general["solver"] == "opencl":
import pyopencl.array as clarray
self.Tx_dev = clarray.to_device(queue, self.Tx)
self.Ty_dev = clarray.to_device(queue, self.Ty)
self.Tz_dev = clarray.to_device(queue, self.Tz)
@@ -405,27 +412,23 @@ def dispersion_analysis(G):
# material: material with maximum permittivity
# maxfreq: maximum significant frequency
# error: error message
results = {'deltavp': None,
'N': None,
'material': None,
'maxfreq': [],
'error': ''}
results = {"deltavp": None, "N": None, "material": None, "maxfreq": [], "error": ""}
# Find maximum significant frequency
if G.waveforms:
for waveform in G.waveforms:
if waveform.type == 'sine' or waveform.type == 'contsine':
results['maxfreq'].append(4 * waveform.freq)
if waveform.type == "sine" or waveform.type == "contsine":
results["maxfreq"].append(4 * waveform.freq)
elif waveform.type == 'impulse':
results['error'] = 'impulse waveform used.'
elif waveform.type == "impulse":
results["error"] = "impulse waveform used."
elif waveform.type == 'user':
results['error'] = 'user waveform detected.'
elif waveform.type == "user":
results["error"] = "user waveform detected."
else:
# User-defined waveform
if waveform.type == 'user':
if waveform.type == "user":
iterations = G.iterations
# Built-in waveform
@@ -450,12 +453,19 @@ def dispersion_analysis(G):
# Set maximum frequency to a threshold drop from maximum power, ignoring DC value
try:
freqthres = np.where(power[freqmaxpower:] < -config.get_model_config().numdispersion['highestfreqthres'])[0][0] + freqmaxpower
results['maxfreq'].append(freqs[freqthres])
freqthres = (
np.where(
power[freqmaxpower:] < -config.get_model_config().numdispersion["highestfreqthres"]
)[0][0]
+ freqmaxpower
)
results["maxfreq"].append(freqs[freqthres])
except ValueError:
results['error'] = ('unable to calculate maximum power ' +
'from waveform, most likely due to ' +
'undersampling.')
results["error"] = (
"unable to calculate maximum power "
+ "from waveform, most likely due to "
+ "undersampling."
)
# Ignore case where someone is using a waveform with zero amplitude, i.e. on a receiver
elif waveform.amp == 0:
@@ -463,40 +473,41 @@ 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.')
results["error"] = (
"waveform does not fit within specified " + "time window and is therefore being truncated."
)
else:
results['error'] = 'no waveform detected.'
results["error"] = "no waveform detected."
if results['maxfreq']:
results['maxfreq'] = max(results['maxfreq'])
if results["maxfreq"]:
results["maxfreq"] = max(results["maxfreq"])
# Find minimum wavelength (material with maximum permittivity)
maxer = 0
matmaxer = ''
matmaxer = ""
for x in G.materials:
if x.se != float('inf'):
if x.se != float("inf"):
er = x.er
# If there are dispersive materials calculate the complex
# relative permittivity at maximum frequency and take the real part
if x.__class__.__name__ == 'DispersiveMaterial':
er = x.calculate_er(results['maxfreq'])
if x.__class__.__name__ == "DispersiveMaterial":
er = x.calculate_er(results["maxfreq"])
er = er.real
if er > maxer:
maxer = er
matmaxer = x.ID
results['material'] = next(x for x in G.materials if x.ID == matmaxer)
results["material"] = next(x for x in G.materials if x.ID == matmaxer)
# Minimum velocity
minvelocity = config.c / np.sqrt(maxer)
# Minimum wavelength
minwavelength = minvelocity / results['maxfreq']
minwavelength = minvelocity / results["maxfreq"]
# Maximum spatial step
if '3D' in config.get_model_config().mode:
if "3D" in config.get_model_config().mode:
delta = max(G.dx, G.dy, G.dz)
elif '2D' in config.get_model_config().mode:
elif "2D" in config.get_model_config().mode:
if G.nx == 1:
delta = max(G.dy, G.dz)
elif G.ny == 1:
@@ -508,17 +519,17 @@ def dispersion_analysis(G):
S = (config.c * G.dt) / delta
# Grid sampling density
results['N'] = minwavelength / delta
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
results["deltavp"] = (((vp * config.c) - config.c) / config.c) * 100
# Store rounded down value of grid sampling density
results['N'] = int(np.floor(results['N']))
results["N"] = int(np.floor(results["N"]))
return results

查看文件

@@ -48,7 +48,7 @@ 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)
@@ -57,32 +57,34 @@ def process_python_include_code(inputfile, usernamespace):
processedlines = []
x = 0
while(x < len(inputlines)):
while x < len(inputlines):
# Process any Python code
if(inputlines[x].startswith('#python:')):
logger.warning('#python blocks are deprecated and will be removed in ' +
'the next release of gprMax. Please convert your ' +
'model to use our Python API instead.\n')
if inputlines[x].startswith("#python:"):
logger.warning(
"#python blocks are deprecated and will be removed in "
+ "the next release of gprMax. Please convert your "
+ "model to use our Python API instead.\n"
)
# String to hold Python code to be executed
pythoncode = ''
pythoncode = ""
x += 1
while not inputlines[x].startswith('#end_python:'):
while not inputlines[x].startswith("#end_python:"):
# Add all code in current code block to string
pythoncode += inputlines[x] + '\n'
pythoncode += inputlines[x] + "\n"
x += 1
if x == len(inputlines):
logger.exception('Cannot find the end of the Python code ' +
'block, i.e. missing #end_python: command.')
logger.exception(
"Cannot find the end of the Python code " + "block, i.e. missing #end_python: command."
)
raise SyntaxError
# Compile code for faster execution
pythoncompiledcode = compile(pythoncode, '<string>', 'exec')
pythoncompiledcode = compile(pythoncode, "<string>", "exec")
# Redirect stdout to a text stream
sys.stdout = result = StringIO()
# Execute code block & make available only usernamespace
exec(pythoncompiledcode, usernamespace)
# String containing buffer of executed code
codeout = result.getvalue().split('\n')
codeout = result.getvalue().split("\n")
result.close()
# Reset stdio
@@ -92,8 +94,8 @@ def process_python_include_code(inputfile, usernamespace):
hashcmds = []
pythonout = []
for line in codeout:
if line.startswith('#'):
hashcmds.append(line + '\n')
if line.startswith("#"):
hashcmds.append(line + "\n")
elif line:
pythonout.append(line)
@@ -102,12 +104,12 @@ def process_python_include_code(inputfile, usernamespace):
# Print any generated output that is not commands
if pythonout:
logger.info(f'Python messages (from stdout/stderr): {pythonout}\n')
logger.info(f"Python messages (from stdout/stderr): {pythonout}\n")
# Add any other commands to list
elif(inputlines[x].startswith('#')):
elif inputlines[x].startswith("#"):
# Add gprMax command to list
inputlines[x] += ('\n')
inputlines[x] += "\n"
processedlines.append(inputlines[x])
x += 1
@@ -133,11 +135,11 @@ def process_include_files(hashcmds):
processedincludecmds = []
x = 0
while x < len(hashcmds):
if hashcmds[x].startswith('#include_file:'):
if hashcmds[x].startswith("#include_file:"):
includefile = hashcmds[x].split()
if len(includefile) != 2:
logger.exception('#include_file requires exactly one parameter')
logger.exception("#include_file requires exactly one parameter")
raise ValueError
includefile = includefile[1]
@@ -147,9 +149,13 @@ def process_include_files(hashcmds):
if not includefile.exists():
includefile = Path(config.sim_config.input_file_path.parent, includefile)
with open(includefile, 'r') as f:
with open(includefile, "r") as f:
# Strip out any newline characters and comments that must begin with double hashes
includelines = [includeline.rstrip() + '\n' for includeline in f if(not includeline.startswith('##') and includeline.rstrip('\n'))]
includelines = [
includeline.rstrip() + "\n"
for includeline in f
if (not includeline.startswith("##") and includeline.rstrip("\n"))
]
# Add lines from include file
processedincludecmds.extend(includelines)
@@ -172,14 +178,16 @@ def write_processed_file(processedlines):
"""
parts = config.get_model_config().output_file_path.parts
processedfile = (Path(*parts[:-1], parts[-1] + '_processed.in'))
processedfile = Path(*parts[:-1], parts[-1] + "_processed.in")
with open(processedfile, 'w') as f:
with open(processedfile, "w") as f:
for item in processedlines:
f.write(f'{item}')
f.write(f"{item}")
logger.info(f'Written input commands, after processing any Python ' +
f'code and include commands, to file: {processedfile}\n')
logger.info(
f"Written input commands, after processing any Python "
+ f"code and include commands, to file: {processedfile}\n"
)
def check_cmd_names(processedlines, checkessential=True):
@@ -199,35 +207,70 @@ def check_cmd_names(processedlines, checkessential=True):
# Dictionaries of available commands
# Essential commands neccessary to run a gprMax model
essentialcmds = ['#domain', '#dx_dy_dz', '#time_window']
essentialcmds = ["#domain", "#dx_dy_dz", "#time_window"]
# Commands that there should only be one instance of in a model
singlecmds = dict.fromkeys(['#domain', '#dx_dy_dz', '#time_window',
'#title', '#omp_threads',
'#time_step_stability_factor', '#pml_cells',
'#excitation_file', '#src_steps', '#rx_steps',
'#output_dir'], None)
singlecmds = dict.fromkeys(
[
"#domain",
"#dx_dy_dz",
"#time_window",
"#title",
"#omp_threads",
"#time_step_stability_factor",
"#pml_cells",
"#excitation_file",
"#src_steps",
"#rx_steps",
"#output_dir",
],
None,
)
# Commands that there can be multiple instances of in a model
# - these will be lists within the dictionary
multiplecmds = {key: [] for key in ['#geometry_view',
'#geometry_objects_write', '#material',
'#material_range', '#material_list',
'#soil_peplinski',
'#add_dispersion_debye',
'#add_dispersion_lorentz',
'#add_dispersion_drude',
'#waveform', '#voltage_source',
'#hertzian_dipole', '#magnetic_dipole',
'#transmission_line', '#rx', '#rx_array',
'#snapshot', '#include_file']}
multiplecmds = {
key: []
for key in [
"#geometry_view",
"#geometry_objects_write",
"#material",
"#material_range",
"#material_list",
"#soil_peplinski",
"#add_dispersion_debye",
"#add_dispersion_lorentz",
"#add_dispersion_drude",
"#waveform",
"#voltage_source",
"#hertzian_dipole",
"#magnetic_dipole",
"#transmission_line",
"#rx",
"#rx_array",
"#snapshot",
"#include_file",
]
}
# 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', '#ellipsoid', '#cone', '#cylinder',
'#cylindrical_sector', '#fractal_box',
'#add_surface_roughness', '#add_surface_water', '#add_grass']
geometrycmds = [
"#geometry_objects_read",
"#edge",
"#plate",
"#triangle",
"#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
geometry = []
@@ -235,26 +278,28 @@ def check_cmd_names(processedlines, checkessential=True):
# add command parameters to appropriate dictionary values or lists
countessentialcmds = 0
lindex = 0
while(lindex < len(processedlines)):
cmd = processedlines[lindex].split(':')
while lindex < len(processedlines):
cmd = processedlines[lindex].split(":")
cmdname = cmd[0]
cmdparams = cmd[1]
# Check if there is space between command name and parameters, i.e.
# check first character of parameter string. Ignore case when there
# 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])
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]
)
raise SyntaxError
# Check if command name is valid
if (cmdname not in essentialcmds and
cmdname not in singlecmds and
cmdname not in multiplecmds and
cmdname not in geometrycmds):
logger.exception('Your input file contains an invalid command: ' +
cmdname)
if (
cmdname not in essentialcmds
and cmdname not in singlecmds
and cmdname not in multiplecmds
and cmdname not in geometrycmds
):
logger.exception("Your input file contains an invalid command: " + cmdname)
raise SyntaxError
# Count essential commands
@@ -264,25 +309,26 @@ def check_cmd_names(processedlines, checkessential=True):
# Assign command parameters as values to dictionary keys
if cmdname in singlecmds:
if singlecmds[cmdname] is None:
singlecmds[cmdname] = cmd[1].strip(' \t\n')
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:
multiplecmds[cmdname].append(cmd[1].strip(' \t\n'))
multiplecmds[cmdname].append(cmd[1].strip(" \t\n"))
elif cmdname in geometrycmds:
geometry.append(processedlines[lindex].strip(' \t\n'))
geometry.append(processedlines[lindex].strip(" \t\n"))
lindex += 1
if checkessential:
if (countessentialcmds < len(essentialcmds)):
logger.exception('Your input file is missing essential commands ' +
'required to run a model. Essential commands are: ' +
', '.join(essentialcmds))
if countessentialcmds < len(essentialcmds):
logger.exception(
"Your input file is missing essential commands "
+ "required to run a model. Essential commands are: "
+ ", ".join(essentialcmds)
)
raise SyntaxError
return singlecmds, multiplecmds, geometry
@@ -328,19 +374,17 @@ def parse_hash_commands(scene):
"""
with open(config.sim_config.input_file_path) as inputfile:
usernamespace = config.get_model_config().get_usernamespace()
# Read input file and process any Python and include file commands
processedlines = process_python_include_code(inputfile, usernamespace)
# Print constants/variables in user-accessable namespace
uservars = ''
uservars = ""
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')
if key != "__builtins__":
uservars += f"{key}: {value}, "
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
@@ -356,6 +400,7 @@ def parse_hash_commands(scene):
class Capturing(list):
"""Context manager to capture standard output stream."""
# https://stackoverflow.com/questions/16571150/how-to-capture-stdout-output-from-a-python-function-call
def __enter__(self):

查看文件

@@ -49,12 +49,12 @@ def check_averaging(averaging):
averaging: boolean for geometry object material averaging.
"""
if averaging == 'y':
if averaging == "y":
averaging = True
elif averaging == 'n':
elif averaging == "n":
averaging = False
else:
logger.exception('Averaging should be either y or n')
logger.exception("Averaging should be either y or n")
return averaging
@@ -76,13 +76,11 @@ def process_geometrycmds(geometry):
for object in geometry:
tmp = object.split()
if tmp[0] == '#geometry_objects_read:':
from .cmds_geometry.geometry_objects_read import \
GeometryObjectsRead
if tmp[0] == "#geometry_objects_read:":
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]))
@@ -90,47 +88,49 @@ def process_geometrycmds(geometry):
gor = GeometryObjectsRead(p1=p1, geofile=tmp[4], matfile=tmp[5])
scene_objects.append(gor)
elif tmp[0] == '#edge:':
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(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
edge = Edge(
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])),
material_id=tmp[7])
material_id=tmp[7],
)
scene_objects.append(edge)
elif tmp[0] == '#plate:':
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
if len(tmp) == 8:
plate = Plate(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
plate = Plate(
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])),
material_id=tmp[7])
material_id=tmp[7],
)
# Anisotropic case
elif len(tmp) == 9:
plate = Plate(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
plate = Plate(
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])),
material_ids=tmp[7:])
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(plate)
elif tmp[0] == '#triangle:':
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]))
@@ -140,31 +140,26 @@ 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:
averaging = check_averaging(tmp[12].lower())
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness,
material_id=tmp[11], averaging=averaging)
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11], averaging=averaging)
# 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:':
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]))
@@ -184,16 +179,14 @@ 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:':
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]))
@@ -207,24 +200,21 @@ def process_geometrycmds(geometry):
# Isotropic case with user specified averaging
elif len(tmp) == 10:
averaging = check_averaging(tmp[9].lower())
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8],
averaging=averaging)
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8], averaging=averaging)
# 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:':
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]))
@@ -239,24 +229,21 @@ def process_geometrycmds(geometry):
# 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)
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')
logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given")
raise ValueError
scene_objects.append(cone)
elif tmp[0] == '#cylindrical_sector:':
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()
@@ -270,41 +257,57 @@ def process_geometrycmds(geometry):
# Isotropic case with no user specified averaging
if len(tmp) == 10:
cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1,
ctr2=ctr2, extent1=extent1,
extent2=extent2, r=r,
start=start, end=end,
material_id=tmp[9])
cylindrical_sector = CylindricalSector(
normal=normal,
ctr1=ctr1,
ctr2=ctr2,
extent1=extent1,
extent2=extent2,
r=r,
start=start,
end=end,
material_id=tmp[9],
)
# Isotropic case with user specified averaging
elif len(tmp) == 11:
averaging = check_averaging(tmp[10].lower())
cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1,
ctr2=ctr2, extent1=extent1,
extent2=extent2, r=r,
start=start, end=end,
cylindrical_sector = CylindricalSector(
normal=normal,
ctr1=ctr1,
ctr2=ctr2,
extent1=extent1,
extent2=extent2,
r=r,
start=start,
end=end,
averaging=averaging,
material_id=tmp[9])
material_id=tmp[9],
)
# Uniaxial anisotropic case
elif len(tmp) == 12:
cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1,
ctr2=ctr2, extent1=extent1,
extent2=extent2, r=r,
start=start, end=end,
material_ids=tmp[9:])
cylindrical_sector = CylindricalSector(
normal=normal,
ctr1=ctr1,
ctr2=ctr2,
extent1=extent1,
extent2=extent2,
r=r,
start=start,
end=end,
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(cylindrical_sector)
elif tmp[0] == '#sphere:':
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]))
@@ -317,24 +320,21 @@ def process_geometrycmds(geometry):
# Isotropic case with user specified averaging
elif len(tmp) == 7:
averaging = check_averaging(tmp[6].lower())
sphere = Sphere(p1=p1, r=r, material_id=tmp[5],
averaging=averaging)
sphere = Sphere(p1=p1, r=r, material_id=tmp[5], averaging=averaging)
# 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:':
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]))
@@ -344,33 +344,28 @@ def process_geometrycmds(geometry):
# 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])
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)
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:])
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)
elif tmp[0] == '#fractal_box:':
elif tmp[0] == "#fractal_box:":
# 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]))
@@ -382,22 +377,40 @@ def process_geometrycmds(geometry):
ID = tmp[13]
if len(tmp) == 14:
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim,
weighting=weighting, n_materials=n_materials,
mixing_model_id=mixing_model_id, id=ID)
fb = FractalBox(
p1=p1,
p2=p2,
frac_dim=frac_dim,
weighting=weighting,
n_materials=n_materials,
mixing_model_id=mixing_model_id,
id=ID,
)
elif len(tmp) == 15:
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim,
weighting=weighting, n_materials=n_materials,
mixing_model_id=mixing_model_id, id=ID,
seed=tmp[14])
fb = FractalBox(
p1=p1,
p2=p2,
frac_dim=frac_dim,
weighting=weighting,
n_materials=n_materials,
mixing_model_id=mixing_model_id,
id=ID,
seed=tmp[14],
)
elif len(tmp) == 16:
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim,
weighting=weighting, n_materials=n_materials,
mixing_model_id=mixing_model_id, id=ID,
seed=tmp[14], averaging=tmp[15].lower())
fb = FractalBox(
p1=p1,
p2=p2,
frac_dim=frac_dim,
weighting=weighting,
n_materials=n_materials,
mixing_model_id=mixing_model_id,
id=ID,
seed=tmp[14],
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)
@@ -406,10 +419,9 @@ def process_geometrycmds(geometry):
for object in geometry:
tmp = object.split()
if tmp[0] == '#add_surface_roughness:':
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]))
@@ -420,25 +432,33 @@ def process_geometrycmds(geometry):
fractal_box_id = tmp[12]
if len(tmp) == 13:
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim,
weighting=weighting, limits=limits,
fractal_box_id=fractal_box_id)
elif len(tmp) == 14:
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim,
weighting=weighting, limits=limits,
asr = AddSurfaceRoughness(
p1=p1,
p2=p2,
frac_dim=frac_dim,
weighting=weighting,
limits=limits,
fractal_box_id=fractal_box_id,
seed=int(tmp[13]))
)
elif len(tmp) == 14:
asr = AddSurfaceRoughness(
p1=p1,
p2=p2,
frac_dim=frac_dim,
weighting=weighting,
limits=limits,
fractal_box_id=fractal_box_id,
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 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]))
@@ -446,14 +466,12 @@ 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 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]))
@@ -464,17 +482,26 @@ def process_geometrycmds(geometry):
fractal_box_id = tmp[11]
if len(tmp) == 12:
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim,
limits=limits, n_blades=n_blades,
fractal_box_id=fractal_box_id)
elif len(tmp) == 13:
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim,
limits=limits, n_blades=n_blades,
grass = AddGrass(
p1=p1,
p2=p2,
frac_dim=frac_dim,
limits=limits,
n_blades=n_blades,
fractal_box_id=fractal_box_id,
seed=int(tmp[12]))
)
elif len(tmp) == 13:
grass = AddGrass(
p1=p1,
p2=p2,
frac_dim=frac_dim,
limits=limits,
n_blades=n_blades,
fractal_box_id=fractal_box_id,
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,12 +18,25 @@
import logging
from .cmds_multiuse import (AddDebyeDispersion, AddDrudeDispersion,
AddLorentzDispersion, GeometryObjectsWrite,
GeometryView, HertzianDipole, MagneticDipole,
Material, MaterialList, MaterialRange, Rx, RxArray,
Snapshot, SoilPeplinski, TransmissionLine,
VoltageSource, Waveform)
from .cmds_multiuse import (
AddDebyeDispersion,
AddDrudeDispersion,
AddLorentzDispersion,
GeometryObjectsWrite,
GeometryView,
HertzianDipole,
MagneticDipole,
Material,
MaterialList,
MaterialRange,
Rx,
RxArray,
Snapshot,
SoilPeplinski,
TransmissionLine,
VoltageSource,
Waveform,
)
logger = logging.getLogger(__name__)
@@ -41,137 +54,143 @@ def process_multicmds(multicmds):
scene_objects = []
cmdname = '#waveform'
cmdname = "#waveform"
if multicmds[cmdname] is not None:
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'
cmdname = "#voltage_source"
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) == 6:
voltage_source = VoltageSource(polarisation=tmp[0].lower(),
p1=(float(tmp[1]), float(tmp[2]),
float(tmp[3])), resistance=float(tmp[4]),
waveform_id=tmp[5])
voltage_source = VoltageSource(
polarisation=tmp[0].lower(),
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
resistance=float(tmp[4]),
waveform_id=tmp[5],
)
elif len(tmp) == 8:
voltage_source = VoltageSource(polarisation=tmp[0].lower(),
p1=(float(tmp[1]), float(tmp[2]),
float(tmp[3])), resistance=float(tmp[4]),
waveform_id=tmp[5], start=float(tmp[6]),
end=float(tmp[7]))
voltage_source = VoltageSource(
polarisation=tmp[0].lower(),
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
resistance=float(tmp[4]),
waveform_id=tmp[5],
start=float(tmp[6]),
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)
cmdname = '#hertzian_dipole'
cmdname = "#hertzian_dipole"
if multicmds[cmdname] is not None:
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])
hertzian_dipole = HertzianDipole(
polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4]
)
elif len(tmp) == 7:
hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]),
float(tmp[2]), float(tmp[3])),
waveform_id=tmp[4], start=float(tmp[5]),
end=float(tmp[6]))
hertzian_dipole = HertzianDipole(
polarisation=tmp[0],
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
waveform_id=tmp[4],
start=float(tmp[5]),
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)
cmdname = '#magnetic_dipole'
cmdname = "#magnetic_dipole"
if multicmds[cmdname] is not None:
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])
magnetic_dipole = MagneticDipole(
polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4]
)
elif len(tmp) == 7:
magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]),
float(tmp[2]), float(tmp[3])),
waveform_id=tmp[4], start=float(tmp[5]),
end=float(tmp[6]))
magnetic_dipole = MagneticDipole(
polarisation=tmp[0],
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
waveform_id=tmp[4],
start=float(tmp[5]),
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)
cmdname = '#transmission_line'
cmdname = "#transmission_line"
if multicmds[cmdname] is not None:
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:
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]),
float(tmp[2]), float(tmp[3])),
resistance=float(tmp[4]), waveform_id=tmp[5])
tl = TransmissionLine(
polarisation=tmp[0],
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
resistance=float(tmp[4]),
waveform_id=tmp[5],
)
elif len(tmp) == 8:
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]),
float(tmp[2]), float(tmp[3])),
resistance=float(tmp[4]), waveform_id=tmp[5],
start=tmp[6], end=tmp[7])
tl = TransmissionLine(
polarisation=tmp[0],
p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
resistance=float(tmp[4]),
waveform_id=tmp[5],
start=tmp[6],
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)
cmdname = '#rx'
cmdname = "#rx"
if multicmds[cmdname] is not None:
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)
cmdname = '#rx_array'
cmdname = "#rx_array"
if multicmds[cmdname] is not None:
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]))
@@ -181,13 +200,12 @@ def process_multicmds(multicmds):
rx_array = RxArray(p1=p1, p2=p2, dl=dl)
scene_objects.append(rx_array)
cmdname = '#snapshot'
cmdname = "#snapshot"
if multicmds[cmdname] is not None:
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]))
@@ -197,37 +215,32 @@ 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])
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, time=time,
filename=filename)
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, time=time, filename=filename)
scene_objects.append(snapshot)
cmdname = '#material'
cmdname = "#material"
if multicmds[cmdname] is not None:
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'
cmdname = "#add_dispersion_debye"
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
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])
@@ -239,18 +252,16 @@ 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'
cmdname = "#add_dispersion_lorentz"
if multicmds[cmdname] is not None:
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
poles = int(tmp[0])
@@ -264,20 +275,18 @@ def process_multicmds(multicmds):
tau.append(float(tmp[pole + 1]))
alpha.append(float(tmp[pole + 2]))
lorentz_dispersion = AddLorentzDispersion(poles=poles,
material_ids=material_ids,
er_delta=er_delta, tau=tau,
alpha=alpha)
lorentz_dispersion = AddLorentzDispersion(
poles=poles, material_ids=material_ids, er_delta=er_delta, tau=tau, alpha=alpha
)
scene_objects.append(lorentz_dispersion)
cmdname = '#add_dispersion_drude'
cmdname = "#add_dispersion_drude"
if multicmds[cmdname] is not None:
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
poles = int(tmp[0])
@@ -289,53 +298,49 @@ 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'
cmdname = "#soil_peplinski"
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
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]),
soil = SoilPeplinski(
sand_fraction=float(tmp[0]),
clay_fraction=float(tmp[1]),
bulk_density=float(tmp[2]),
sand_density=float(tmp[3]),
water_fraction_lower=float(tmp[4]),
water_fraction_upper=float(tmp[5]),
id=tmp[6])
id=tmp[6],
)
scene_objects.append(soil)
cmdname = '#geometry_view'
cmdname = "#geometry_view"
if multicmds[cmdname] is not None:
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'
cmdname = "#geometry_objects_write"
if multicmds[cmdname] is not None:
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])
@@ -343,16 +348,16 @@ def process_multicmds(multicmds):
gow = GeometryObjectsWrite(p1=p1, p2=p2, filename=tmp[6])
scene_objects.append(gow)
cmdname = '#material_range'
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')
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at exactly nine parameters")
raise ValueError
material_range = MaterialRange(er_lower=float(tmp[0]),
material_range = MaterialRange(
er_lower=float(tmp[0]),
er_upper=float(tmp[1]),
sigma_lower=float(tmp[2]),
sigma_upper=float(tmp[3]),
@@ -360,17 +365,17 @@ def process_multicmds(multicmds):
mr_upper=float(tmp[5]),
ro_lower=float(tmp[6]),
ro_upper=float(tmp[7]),
id=tmp[8])
id=tmp[8],
)
scene_objects.append(material_range)
cmdname = '#material_list'
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')
logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least 2 parameters")
raise ValueError
tokens = len(tmp)
@@ -378,8 +383,7 @@ def process_multicmds(multicmds):
for iter in range(tokens - 1):
lmats.append(tmp[iter])
material_list = MaterialList(list_of_materials=lmats,
id=tmp[tokens-1])
material_list = MaterialList(list_of_materials=lmats, id=tmp[tokens - 1])
scene_objects.append(material_list)
return scene_objects

查看文件

@@ -18,10 +18,19 @@
import logging
from .cmds_singleuse import (Discretisation, Domain, ExcitationFile,
OMPThreads, OutputDir, PMLProps, RxSteps,
SrcSteps, TimeStepStabilityFactor, TimeWindow,
Title)
from .cmds_singleuse import (
Discretisation,
Domain,
ExcitationFile,
OMPThreads,
OutputDir,
PMLProps,
RxSteps,
SrcSteps,
TimeStepStabilityFactor,
TimeWindow,
Title,
)
logger = logging.getLogger(__name__)
@@ -40,62 +49,65 @@ def process_singlecmds(singlecmds):
scene_objects = []
# Check validity of command parameters in order needed
cmd = '#title'
cmd = "#title"
if singlecmds[cmd] is not None:
title = Title(name=str(singlecmds[cmd]))
scene_objects.append(title)
cmd = '#output_dir'
cmd = "#output_dir"
if singlecmds[cmd] is not None:
output_dir = OutputDir(dir=singlecmds[cmd])
scene_objects.append(output_dir)
# Number of threads for CPU-based (OpenMP) parallelised parts of code
cmd = '#omp_threads'
cmd = "#omp_threads"
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 ' +
f'the number of CPU OpenMP threads to use')
logger.exception(
f"{cmd} requires exactly one parameter to specify " + f"the number of CPU OpenMP threads to use"
)
raise ValueError
omp_threads = OMPThreads(n=tmp[0])
scene_objects.append(omp_threads)
cmd = '#dx_dy_dz'
cmd = "#dx_dy_dz"
if singlecmds[cmd] is not None:
tmp = [float(x) for x in singlecmds[cmd].split()]
if len(tmp) != 3:
logger.exception(f'{cmd} requires exactly three parameters')
logger.exception(f"{cmd} requires exactly three parameters")
raise ValueError
dl = (tmp[0], tmp[1], tmp[2])
discretisation = Discretisation(p1=dl)
scene_objects.append(discretisation)
cmd = '#domain'
cmd = "#domain"
if singlecmds[cmd] is not None:
tmp = [float(x) for x in singlecmds[cmd].split()]
if len(tmp) != 3:
logger.exception(f'{cmd} requires exactly three parameters')
logger.exception(f"{cmd} requires exactly three parameters")
raise ValueError
p1 = (tmp[0], tmp[1], tmp[2])
domain = Domain(p1=p1)
scene_objects.append(domain)
cmd = '#time_step_stability_factor'
cmd = "#time_step_stability_factor"
if singlecmds[cmd] is not None:
tmp = tuple(float(x) for x in singlecmds[cmd].split())
tsf = TimeStepStabilityFactor(f=tmp[0])
scene_objects.append(tsf)
cmd = '#time_window'
cmd = "#time_window"
if singlecmds[cmd] is not None:
tmp = singlecmds[cmd].split()
if len(tmp) != 1:
logger.exception(f'{cmd} requires exactly one parameter to specify the ' +
f'time window. Either in seconds or number of iterations.')
logger.exception(
f"{cmd} requires exactly one parameter to specify the "
+ f"time window. Either in seconds or number of iterations."
)
raise ValueError
tmp = tmp[0].lower()
@@ -110,40 +122,37 @@ def process_singlecmds(singlecmds):
scene_objects.append(tw)
cmd = '#pml_cells'
cmd = "#pml_cells"
if singlecmds[cmd] is not None:
tmp = singlecmds[cmd].split()
if len(tmp) not in [1, 6]:
logger.exception(f'{cmd} requires either one or six parameter(s)')
logger.exception(f"{cmd} requires either one or six parameter(s)")
raise ValueError
if len(tmp) == 1:
pml_cells = PMLProps(thickness=int(tmp[0]))
else:
pml_cells = 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]))
pml_cells = 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])
)
scene_objects.append(pml_cells)
cmd = '#src_steps'
cmd = "#src_steps"
if singlecmds[cmd] is not None:
tmp = singlecmds[cmd].split()
if len(tmp) != 3:
logger.exception(f'{cmd} requires exactly three parameters')
logger.exception(f"{cmd} requires exactly three parameters")
raise ValueError
p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2]))
src_steps = SrcSteps(p1=p1)
scene_objects.append(src_steps)
cmd = '#rx_steps'
cmd = "#rx_steps"
if singlecmds[cmd] is not None:
tmp = singlecmds[cmd].split()
if len(tmp) != 3:
logger.exception(f'{cmd} requires exactly three parameters')
logger.exception(f"{cmd} requires exactly three parameters")
raise ValueError
p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2]))
@@ -151,11 +160,11 @@ def process_singlecmds(singlecmds):
scene_objects.append(rx_steps)
# Excitation file for user-defined source waveforms
cmd = '#excitation_file'
cmd = "#excitation_file"
if singlecmds[cmd] is not None:
tmp = singlecmds[cmd].split()
if len(tmp) not in [1, 3]:
logger.exception(f'{cmd} requires either one or three parameter(s)')
logger.exception(f"{cmd} requires either one or three parameter(s)")
raise ValueError
if len(tmp) > 1:

查看文件

@@ -25,6 +25,7 @@ import gprMax.config as config
logger = logging.getLogger(__name__)
class Material:
"""Super-class to describe generic, non-dispersive materials,
their properties and update coefficients.
@@ -39,7 +40,7 @@ class Material:
self.numID = numID
self.ID = ID
self.type = ''
self.type = ""
# Default material averaging
self.averagable = True
@@ -71,10 +72,10 @@ class Material:
G: FDTDGrid class describing a grid in a model.
"""
EA = (config.sim_config.em_consts['e0'] * self.er / G.dt) + 0.5 * self.se
EB = (config.sim_config.em_consts['e0'] * self.er / G.dt) - 0.5 * self.se
EA = (config.sim_config.em_consts["e0"] * self.er / G.dt) + 0.5 * self.se
EB = (config.sim_config.em_consts["e0"] * self.er / G.dt) - 0.5 * self.se
if self.ID == 'pec' or self.se == float('inf'):
if self.ID == "pec" or self.se == float("inf"):
self.CA = 0
self.CBx = 0
self.CBy = 0
@@ -135,30 +136,42 @@ class DispersiveMaterial(Material):
# The implementation of the dispersive material modelling comes from the
# derivation in: http://dx.doi.org/10.1109/TAP.2014.2308549
self.w = np.zeros(config.get_model_config().materials['maxpoles'],
dtype=config.get_model_config().materials['dispersivedtype'])
self.q = np.zeros(config.get_model_config().materials['maxpoles'],
dtype=config.get_model_config().materials['dispersivedtype'])
self.zt = np.zeros(config.get_model_config().materials['maxpoles'],
dtype=config.get_model_config().materials['dispersivedtype'])
self.zt2 = np.zeros(config.get_model_config().materials['maxpoles'],
dtype=config.get_model_config().materials['dispersivedtype'])
self.eqt = np.zeros(config.get_model_config().materials['maxpoles'],
dtype=config.get_model_config().materials['dispersivedtype'])
self.eqt2 = np.zeros(config.get_model_config().materials['maxpoles'],
dtype=config.get_model_config().materials['dispersivedtype'])
self.w = np.zeros(
config.get_model_config().materials["maxpoles"],
dtype=config.get_model_config().materials["dispersivedtype"],
)
self.q = np.zeros(
config.get_model_config().materials["maxpoles"],
dtype=config.get_model_config().materials["dispersivedtype"],
)
self.zt = np.zeros(
config.get_model_config().materials["maxpoles"],
dtype=config.get_model_config().materials["dispersivedtype"],
)
self.zt2 = np.zeros(
config.get_model_config().materials["maxpoles"],
dtype=config.get_model_config().materials["dispersivedtype"],
)
self.eqt = np.zeros(
config.get_model_config().materials["maxpoles"],
dtype=config.get_model_config().materials["dispersivedtype"],
)
self.eqt2 = np.zeros(
config.get_model_config().materials["maxpoles"],
dtype=config.get_model_config().materials["dispersivedtype"],
)
for x in range(self.poles):
if 'debye' in self.type:
if "debye" in self.type:
self.w[x] = self.deltaer[x] / self.tau[x]
self.q[x] = -1 / self.tau[x]
elif 'lorentz' in self.type:
elif "lorentz" in self.type:
# 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.q[x] = -self.alpha[x] + (1j * np.sqrt(wp2 - self.alpha[x] ** 2))
elif 'drude' in self.type:
elif "drude" in self.type:
# tau for Drude materials are pole frequencies
# alpha for Drude materials are the inverse of relaxation times
wp2 = (2 * np.pi * self.tau[x]) ** 2
@@ -171,10 +184,16 @@ class DispersiveMaterial(Material):
self.zt[x] = (self.w[x] / self.q[x]) * (1 - self.eqt[x]) / G.dt
self.zt2[x] = (self.w[x] / self.q[x]) * (1 - self.eqt2[x])
EA = ((config.sim_config.em_consts['e0'] * self.er / G.dt) + 0.5 * self.se -
(config.sim_config.em_consts['e0'] / G.dt) * np.sum(self.zt2.real))
EB = ((config.sim_config.em_consts['e0'] * self.er / G.dt) - 0.5 * self.se -
(config.sim_config.em_consts['e0'] / G.dt) * np.sum(self.zt2.real))
EA = (
(config.sim_config.em_consts["e0"] * self.er / G.dt)
+ 0.5 * self.se
- (config.sim_config.em_consts["e0"] / G.dt) * np.sum(self.zt2.real)
)
EB = (
(config.sim_config.em_consts["e0"] * self.er / G.dt)
- 0.5 * self.se
- (config.sim_config.em_consts["e0"] / G.dt) * np.sum(self.zt2.real)
)
self.CA = EB / EA
self.CBx = (1 / G.dx) * 1 / EA
@@ -198,15 +217,16 @@ class DispersiveMaterial(Material):
er = self.er
w = 2 * np.pi * freq
er += self.se / (1j * w * config.sim_config.em_consts['e0'])
if 'debye' in self.type:
er += self.se / (1j * w * config.sim_config.em_consts["e0"])
if "debye" in self.type:
for pole in range(self.poles):
er += self.deltaer[pole] / (1 + 1j * w * self.tau[pole])
elif 'lorentz' in self.type:
elif "lorentz" in self.type:
for pole in range(self.poles):
er += ((self.deltaer[pole] * self.tau[pole]**2)
/ (self.tau[pole]**2 + 2j * w * self.alpha[pole] - w**2))
elif 'drude' in self.type:
er += (self.deltaer[pole] * self.tau[pole] ** 2) / (
self.tau[pole] ** 2 + 2j * w * self.alpha[pole] - w**2
)
elif "drude" in self.type:
ersum = 0
for pole in range(self.poles):
ersum += self.tau[pole] ** 2 / (w**2 - 1j * w * self.alpha[pole])
@@ -220,8 +240,7 @@ 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,11 +301,10 @@ class PeplinskiSoil:
mumaterials = 0.5 * (mubins[1 : nbins + 1] + mubins[0:nbins])
# Create an iterator
muiter = np.nditer(mumaterials, flags=['c_index'])
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
@@ -298,18 +316,18 @@ class PeplinskiSoil:
sig = muiter[0] ** (b2 / a) * ((sigf * (self.rs - self.rb)) / (self.rs * muiter[0]))
# Check to see if the material already exists before creating a new one
requiredID = '|{:.4f}|'.format(float(muiter[0]))
requiredID = "|{:.4f}|".format(float(muiter[0]))
material = next((x for x in G.materials if x.ID == requiredID), None)
if muiter.index == 0:
if material:
self.matID.append(material.numID)
if not material:
m = DispersiveMaterial(len(G.materials), requiredID)
m.type = 'debye'
m.type = "debye"
m.averagable = False
m.poles = 1
if m.poles > config.get_model_config().materials['maxpoles']:
config.get_model_config().materials['maxpoles'] = m.poles
if m.poles > config.get_model_config().materials["maxpoles"]:
config.get_model_config().materials["maxpoles"] = m.poles
m.er = eri
m.se = sig
m.deltaer.append(er - eri)
@@ -393,14 +411,14 @@ 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:
if material:
self.matID.append(material.numID)
if not material:
m = Material(len(G.materials), requiredID)
m.type = ''
m.type = ""
m.averagable = True
m.er = er
m.se = se
@@ -444,7 +462,7 @@ 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
@@ -455,14 +473,14 @@ def create_built_in_materials(G):
G: FDTDGrid class describing a grid in a model.
"""
m = Material(0, 'pec')
m.se = float('inf')
m.type = 'builtin'
m = Material(0, "pec")
m.se = float("inf")
m.type = "builtin"
m.averagable = False
G.materials.append(m)
m = Material(1, 'free_space')
m.type = 'builtin'
m = Material(1, "free_space")
m.type = "builtin"
G.materials.append(m)
@@ -483,12 +501,12 @@ 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))
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)
)
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)
@@ -507,17 +525,17 @@ def create_water(G, T=25, S=0):
eri, er, tau, sig = calculate_water_properties(T, S)
m = DispersiveMaterial(len(G.materials), 'water')
m = DispersiveMaterial(len(G.materials), "water")
m.averagable = False
m.type = 'builtin, debye'
m.type = "builtin, debye"
m.poles = 1
m.er = eri
m.se = sig
m.deltaer.append(er - eri)
m.tau.append(tau)
G.materials.append(m)
if config.get_model_config().materials['maxpoles'] == 0:
config.get_model_config().materials['maxpoles'] = 1
if config.get_model_config().materials["maxpoles"] == 0:
config.get_model_config().materials["maxpoles"] = 1
def create_grass(G):
@@ -533,17 +551,17 @@ def create_grass(G):
tau = 1.0793e-11
sig = 0
m = DispersiveMaterial(len(G.materials), 'grass')
m = DispersiveMaterial(len(G.materials), "grass")
m.averagable = False
m.type = 'builtin, debye'
m.type = "builtin, debye"
m.poles = 1
m.er = eri
m.se = sig
m.deltaer.append(er - eri)
m.tau.append(tau)
G.materials.append(m)
if config.get_model_config().materials['maxpoles'] == 0:
config.get_model_config().materials['maxpoles'] = 1
if config.get_model_config().materials["maxpoles"] == 0:
config.get_model_config().materials["maxpoles"] = 1
def process_materials(G):
@@ -558,13 +576,37 @@ def process_materials(G):
print a table.
"""
if config.get_model_config().materials['maxpoles'] == 0:
materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]',
'\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']]
if config.get_model_config().materials["maxpoles"] == 0:
materialsdata = [
[
"\nID",
"\nName",
"\nType",
"\neps_r",
"sigma\n[S/m]",
"\nmu_r",
"sigma*\n[Ohm/m]",
"Dielectric\nsmoothable",
]
]
else:
materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]',
'Delta\neps_r', 'tau\n[s]', 'omega\n[Hz]', 'delta\n[Hz]',
'gamma\n[Hz]', '\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']]
materialsdata = [
[
"\nID",
"\nName",
"\nType",
"\neps_r",
"sigma\n[S/m]",
"Delta\neps_r",
"tau\n[s]",
"omega\n[Hz]",
"delta\n[Hz]",
"gamma\n[Hz]",
"\nmu_r",
"sigma*\n[Ohm/m]",
"Dielectric\nsmoothable",
]
]
for material in G.materials:
# Calculate update coefficients for specific material
@@ -576,39 +618,44 @@ def process_materials(G):
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'):
if hasattr(material, "poles"):
z = 0
for pole in range(config.get_model_config().materials['maxpoles']):
G.updatecoeffsdispersive[material.numID, z:z + 3] = (config.sim_config.em_consts['e0'] *
material.eqt2[pole], material.eqt[pole], material.zt[pole])
for pole in range(config.get_model_config().materials["maxpoles"]):
G.updatecoeffsdispersive[material.numID, z : z + 3] = (
config.sim_config.em_consts["e0"] * material.eqt2[pole],
material.eqt[pole],
material.zt[pole],
)
z += 3
# Construct information on material properties for printing table
materialtext = [str(material.numID),
materialtext = [
str(material.numID),
material.ID[:50] if len(material.ID) > 50 else material.ID,
material.type,
f'{material.er:g}',
f'{material.se:g}']
if config.get_model_config().materials['maxpoles'] > 0:
if 'debye' in material.type:
materialtext.append('\n'.join('{:g}'.format(deltaer) for deltaer in material.deltaer))
materialtext.append('\n'.join('{:g}'.format(tau) for tau in material.tau))
materialtext.extend(['', '', ''])
elif 'lorentz' in material.type:
materialtext.append(', '.join('{:g}'.format(deltaer) for deltaer in material.deltaer))
materialtext.append('')
materialtext.append(', '.join('{:g}'.format(tau) for tau in material.tau))
materialtext.append(', '.join('{:g}'.format(alpha) for alpha in material.alpha))
materialtext.append('')
elif 'drude' in material.type:
materialtext.extend(['', ''])
materialtext.append(', '.join('{:g}'.format(tau) for tau in material.tau))
materialtext.append('')
materialtext.append(', '.join('{:g}'.format(alpha) for alpha in material.alpha))
f"{material.er:g}",
f"{material.se:g}",
]
if config.get_model_config().materials["maxpoles"] > 0:
if "debye" in material.type:
materialtext.append("\n".join("{:g}".format(deltaer) for deltaer in material.deltaer))
materialtext.append("\n".join("{:g}".format(tau) for tau in material.tau))
materialtext.extend(["", "", ""])
elif "lorentz" in material.type:
materialtext.append(", ".join("{:g}".format(deltaer) for deltaer in material.deltaer))
materialtext.append("")
materialtext.append(", ".join("{:g}".format(tau) for tau in material.tau))
materialtext.append(", ".join("{:g}".format(alpha) for alpha in material.alpha))
materialtext.append("")
elif "drude" in material.type:
materialtext.extend(["", ""])
materialtext.append(", ".join("{:g}".format(tau) for tau in material.tau))
materialtext.append("")
materialtext.append(", ".join("{:g}".format(alpha) for alpha in material.alpha))
else:
materialtext.extend(['', '', '', '', ''])
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__)
@@ -75,19 +73,21 @@ 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:
for source in itertools.chain(G.hertziandipoles, G.magneticdipoles):
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):
logger.exception('Source(s) will be stepped to a position outside the domain.')
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
):
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]
@@ -95,13 +95,15 @@ class ModelBuildRun:
if G.rxsteps[0] != 0 or G.rxsteps[1] != 0 or G.rxsteps[2] != 0:
for receiver in G.rxs:
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):
logger.exception('Receiver(s) will be stepped to a position outside the domain.')
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
):
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]
@@ -109,29 +111,34 @@ class ModelBuildRun:
# 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):
logger.exception('\nNo geometry views or geometry objects found.')
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)
if G.geometryobjectswrite:
logger.info('')
logger.info("")
for i, go in enumerate(G.geometryobjectswrite):
pbar = tqdm(total=go.datawritesize, unit='byte', unit_scale=True,
desc=f'Writing geometry object file {i + 1}/{len(G.geometryobjectswrite)}, ' +
f'{go.filename_hdf5.name}',
ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.sim_config.general['progressbars'])
pbar = tqdm(
total=go.datawritesize,
unit="byte",
unit_scale=True,
desc=f"Writing geometry object file {i + 1}/{len(G.geometryobjectswrite)}, "
+ f"{go.filename_hdf5.name}",
ncols=get_terminal_width() - 1,
file=sys.stdout,
disable=not config.sim_config.general["progressbars"],
)
go.write_hdf5(G, pbar)
pbar.close()
logger.info('')
logger.info("")
def build_geometry(self):
G = self.G
logger.info(config.get_model_config().inputfilestr)
# Build objects in the scene and check memory for building
# Build objects in the scene and check memory for building
self.build_scene()
# Print info on any subgrids
@@ -143,11 +150,13 @@ class ModelBuildRun:
# Check for dispersive materials (and specific type)
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])
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]
)
# Set data type if any dispersive materials (must be done before memory checks)
if config.get_model_config().materials['maxpoles'] != 0:
if config.get_model_config().materials["maxpoles"] != 0:
config.get_model_config().set_dispersive_material_types()
# Check memory requirements to build model/scene (different to memory
@@ -159,22 +168,26 @@ class ModelBuildRun:
total_mem_run, mem_strs_run = mem_check_run_all(grids)
if total_mem_build > total_mem_run:
logger.info(f'\nMemory required (estimated): {" + ".join(mem_strs_build)} + '
f'~{humanize.naturalsize(config.get_model_config().mem_overhead)} '
f'overhead = {humanize.naturalsize(total_mem_build)}')
logger.info(
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'~{humanize.naturalsize(config.get_model_config().mem_overhead)} '
f'overhead = {humanize.naturalsize(total_mem_run)}')
logger.info(
f'\nMemory required (estimated): {" + ".join(mem_strs_run)} + '
f"~{humanize.naturalsize(config.get_model_config().mem_overhead)} "
f"overhead = {humanize.naturalsize(total_mem_run)}"
)
# Build grids
gridbuilders = [GridBuilder(grid) for grid in grids]
for gb in gridbuilders:
# Set default CFS parameter for PMLs if not user provided
if not gb.grid.pmls['cfs']:
gb.grid.pmls['cfs'] = [CFS()]
if not gb.grid.pmls["cfs"]:
gb.grid.pmls["cfs"] = [CFS()]
logger.info(print_pml_info(gb.grid))
if not all(value == 0 for value in gb.grid.pmls['thickness'].values()):
if not all(value == 0 for value in gb.grid.pmls["thickness"].values()):
gb.build_pmls()
if gb.grid.averagevolumeobjects:
gb.build_components()
@@ -182,47 +195,59 @@ class ModelBuildRun:
gb.update_voltage_source_materials()
gb.grid.initialise_field_arrays()
gb.grid.initialise_std_update_coeff_arrays()
if config.get_model_config().materials['maxpoles'] > 0:
if config.get_model_config().materials["maxpoles"] > 0:
gb.grid.initialise_dispersive_arrays()
gb.grid.initialise_dispersive_update_coeff_array()
gb.build_materials()
# Check to see if numerical dispersion might be a problem
results = dispersion_analysis(gb.grid)
if results['error']:
logger.warning(f"\nNumerical dispersion analysis [{gb.grid.name}] "
f"not carried out as {results['error']}")
elif results['N'] < config.get_model_config().numdispersion['mingridsampling']:
logger.exception(f"\nNon-physical wave propagation in [{gb.grid.name}] "
if results["error"]:
logger.warning(
f"\nNumerical dispersion analysis [{gb.grid.name}] " f"not carried out as {results['error']}"
)
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}' "
f"has wavelength sampled by {results['N']} cells, "
f"less than required minimum for physical wave "
f"propagation. Maximum significant frequency "
f"estimated as {results['maxfreq']:g}Hz")
f"estimated as {results['maxfreq']:g}Hz"
)
raise ValueError
elif (results['deltavp'] and np.abs(results['deltavp']) >
config.get_model_config().numdispersion['maxnumericaldisp']):
logger.warning(f"\n[{gb.grid.name}] has potentially significant "
elif (
results["deltavp"]
and np.abs(results["deltavp"]) > config.get_model_config().numdispersion["maxnumericaldisp"]
):
logger.warning(
f"\n[{gb.grid.name}] has potentially significant "
f"numerical dispersion. Estimated largest physical "
f"phase-velocity error is {results['deltavp']:.2f}% "
f"in material '{results['material'].ID}' whose "
f"wavelength sampled by {results['N']} cells. "
f"Maximum significant frequency estimated as "
f"{results['maxfreq']:g}Hz")
elif results['deltavp']:
logger.info(f"\nNumerical dispersion analysis [{gb.grid.name}]: "
f"{results['maxfreq']:g}Hz"
)
elif results["deltavp"]:
logger.info(
f"\nNumerical dispersion analysis [{gb.grid.name}]: "
f"estimated largest physical phase-velocity error is "
f"{results['deltavp']:.2f}% in material '{results['material'].ID}' "
f"whose wavelength sampled by {results['N']} cells. "
f"Maximum significant frequency estimated as "
f"{results['maxfreq']:g}Hz")
f"{results['maxfreq']:g}Hz"
)
def reuse_geometry(self):
s = (f'\n--- Model {config.get_model_config().appendmodelnumber}/{config.sim_config.model_end}, '
f'input file (not re-processed, i.e. geometry fixed): '
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)
s = (
f"\n--- Model {config.get_model_config().appendmodelnumber}/{config.sim_config.model_end}, "
f"input file (not re-processed, i.e. geometry fixed): "
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
)
logger.basic(config.get_model_config().inputfilestr)
for grid in [self.G] + self.G.subgrids:
grid.iteration = 0 # Reset current iteration number
@@ -262,32 +287,42 @@ class ModelBuildRun:
"""
# Print information about and check OpenMP threads
if config.sim_config.general['solver'] == 'cpu':
logger.basic(f"\nModel {config.model_num + 1}/{config.sim_config.model_end} "
if config.sim_config.general["solver"] == "cpu":
logger.basic(
f"\nModel {config.model_num + 1}/{config.sim_config.model_end} "
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']:
logger.warning(f"You have specified more threads ({config.get_model_config().ompthreads}) "
f"with OpenMP backend using {config.get_model_config().ompthreads} thread(s)"
)
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']}). "
f"This may lead to degraded performance.")
elif config.sim_config.general['solver'] in ['cuda', 'opencl']:
if config.sim_config.general['solver'] == 'opencl':
solvername = 'OpenCL'
platformname = ' on ' + ' '.join(config.get_model_config().device['dev'].platform.name.split())
devicename = ' '.join(config.get_model_config().device['dev'].name.split())
f"This may lead to degraded performance."
)
elif config.sim_config.general["solver"] in ["cuda", "opencl"]:
if config.sim_config.general["solver"] == "opencl":
solvername = "OpenCL"
platformname = " on " + " ".join(config.get_model_config().device["dev"].platform.name.split())
devicename = " ".join(config.get_model_config().device["dev"].name.split())
else:
solvername = 'CUDA'
platformname = ''
devicename = ' '.join(config.get_model_config().device['dev'].name().split())
logger.basic(f"\nModel {config.model_num + 1}/{config.sim_config.model_end} "
solvername = "CUDA"
platformname = ""
devicename = " ".join(config.get_model_config().device["dev"].name().split())
logger.basic(
f"\nModel {config.model_num + 1}/{config.sim_config.model_end} "
f"on {config.sim_config.hostinfo['hostname']} "
f"with {solvername} backend using {devicename}{platformname}")
f"with {solvername} backend using {devicename}{platformname}"
)
# Prepare iterator
if config.sim_config.general['progressbars']:
iterator = tqdm(range(self.G.iterations), desc='|--->',
ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.sim_config.general['progressbars'])
if config.sim_config.general["progressbars"]:
iterator = tqdm(
range(self.G.iterations),
desc="|--->",
ncols=get_terminal_width() - 1,
file=sys.stdout,
disable=not config.sim_config.general["progressbars"],
)
else:
iterator = range(self.G.iterations)
@@ -299,12 +334,16 @@ class ModelBuildRun:
# Print information about memory usage and solving time for a model
# Add a string on GPU memory usage if applicable
mem_str = f' host + ~{humanize.naturalsize(solver.memused)} GPU' if config.sim_config.general['solver'] == 'cuda' else ''
mem_str = (
f" host + ~{humanize.naturalsize(solver.memused)} GPU"
if config.sim_config.general["solver"] == "cuda"
else ""
)
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')}")
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')}"
)
class GridBuilder:
@@ -312,11 +351,14 @@ class GridBuilder:
self.grid = grid
def build_pmls(self):
pbar = tqdm(total=sum(1 for value in self.grid.pmls['thickness'].values() if value > 0),
desc=f'Building PML boundaries [{self.grid.name}]',
ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.sim_config.general['progressbars'])
for pml_id, thickness in self.grid.pmls['thickness'].items():
pbar = tqdm(
total=sum(1 for value in self.grid.pmls["thickness"].values() if value > 0),
desc=f"Building PML boundaries [{self.grid.name}]",
ncols=get_terminal_width() - 1,
file=sys.stdout,
disable=not config.sim_config.general["progressbars"],
)
for pml_id, thickness in self.grid.pmls["thickness"].items():
if thickness > 0:
build_pml(self.grid, pml_id, thickness)
pbar.update()
@@ -325,10 +367,14 @@ class GridBuilder:
def build_components(self):
# Build the model, i.e. set the material properties (ID) for every edge
# of every Yee cell
logger.info('')
pbar = tqdm(total=2, desc=f'Building Yee cells [{self.grid.name}]',
ncols=get_terminal_width() - 1, file=sys.stdout,
disable=not config.sim_config.general['progressbars'])
logger.info("")
pbar = tqdm(
total=2,
desc=f"Building Yee cells [{self.grid.name}]",
ncols=get_terminal_width() - 1,
file=sys.stdout,
disable=not config.sim_config.general["progressbars"],
)
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)
@@ -336,11 +382,11 @@ class GridBuilder:
pbar.close()
def tm_grid_update(self):
if config.get_model_config().mode == '2D TMx':
if config.get_model_config().mode == "2D TMx":
self.grid.tmx()
elif config.get_model_config().mode == '2D TMy':
elif config.get_model_config().mode == "2D TMy":
self.grid.tmy()
elif config.get_model_config().mode == '2D TMz':
elif config.get_model_config().mode == "2D TMz":
self.grid.tmz()
def update_voltage_source_materials(self):
@@ -355,7 +401,7 @@ class GridBuilder:
materialsdata = process_materials(self.grid)
materialstable = SingleTable(materialsdata)
materialstable.outer_border = False
materialstable.justify_columns[0] = 'right'
materialstable.justify_columns[0] = "right"
logger.info(f'\nMaterials [{self.grid.name}]:')
logger.info(f"\nMaterials [{self.grid.name}]:")
logger.info(materialstable.table)

查看文件

@@ -37,7 +37,7 @@ EXIT
Send by master to worker to initiate worker shutdown and then
send back to master to signal shutdown has completed.
"""
Tags = IntEnum('Tags', 'READY START DONE EXIT')
Tags = IntEnum("Tags", "READY START DONE EXIT")
class MPIExecutor(object):
@@ -136,22 +136,22 @@ class MPIExecutor(object):
if comm is None:
self.comm = MPI.COMM_WORLD
elif not comm.Is_intra():
raise TypeError('MPI.Intracomm expected')
raise TypeError("MPI.Intracomm expected")
else:
self.comm = comm
self.rank = self.comm.rank
self.size = self.comm.size
if self.size < 2:
raise RuntimeError('MPIExecutor must run with at least 2 processes')
raise RuntimeError("MPIExecutor must run with at least 2 processes")
self._up = False
master = int(master)
if master < 0:
raise ValueError('Master rank must be non-negative')
raise ValueError("Master rank must be non-negative")
elif master >= self.size:
raise ValueError('Master not in comm')
raise ValueError("Master not in comm")
else:
self.master = master
@@ -159,13 +159,13 @@ class MPIExecutor(object):
self.workers = tuple(set(range(self.size)) - {self.master})
# the worker function
if not callable(func):
raise TypeError('Func must be a callable')
raise TypeError("Func must be a callable")
self.func = func
# holds the state of workers on the master
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
@@ -211,10 +211,10 @@ class MPIExecutor(object):
"""
if self.is_master():
if self._up:
raise RuntimeError('Start has already been called')
raise RuntimeError("Start has already been called")
self._up = True
logger.debug(f'({self.comm.name}) - Starting up MPIExecutor master/workers...')
logger.debug(f"({self.comm.name}) - Starting up MPIExecutor master/workers...")
if self.is_worker():
self.__wait()
@@ -222,12 +222,12 @@ 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)
logger.debug(f'({self.comm.name}) - Waiting for all workers to terminate.')
logger.debug(f"({self.comm.name}) - Waiting for all workers to terminate.")
down = [False] * len(self.workers)
while True:
@@ -239,7 +239,7 @@ class MPIExecutor(object):
break
self._up = False
logger.debug(f'({self.comm.name}) - All workers terminated.')
logger.debug(f"({self.comm.name}) - All workers terminated.")
def submit(self, jobs, sleep=0.0):
"""Submits a list of jobs to the workers and returns the results.
@@ -257,10 +257,10 @@ class MPIExecutor(object):
results is identical to the order of `jobs`.
"""
if not self._up:
raise RuntimeError('Cannot run jobs without a call to start()')
raise RuntimeError("Cannot run jobs without a call to start()")
logger.basic(f'Running {len(jobs):d} jobs.')
assert self.is_master(), 'run() must not be called on a worker process'
logger.basic(f"Running {len(jobs):d} jobs.")
assert self.is_master(), "run() must not be called on a worker process"
my_jobs = jobs.copy()
num_jobs = len(my_jobs)
@@ -269,7 +269,7 @@ 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,16 +277,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}.')
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
time.sleep(sleep)
logger.debug(f'({self.comm.name}) - Finished all jobs.')
logger.debug(f"({self.comm.name}) - Finished all jobs.")
return results
@@ -300,26 +300,26 @@ class MPIExecutor(object):
status = MPI.Status()
logger.debug(f'({self.comm.name}) - Starting up worker.')
logger.debug(f"({self.comm.name}) - Starting up worker.")
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.')
logger.debug(f"({self.comm.name}) - Received sentinel from master.")
break
logger.debug(f'({self.comm.name}) - Terminating worker.')
logger.debug(f"({self.comm.name}) - Terminating worker.")
self.comm.send(None, dest=self.master, tag=Tags.EXIT)
def __guarded_work(self, work):

查看文件

@@ -29,13 +29,20 @@ class CFSParameter:
"""Individual CFS parameter (e.g. alpha, kappa, or sigma)."""
# Allowable scaling profiles and directions
scalingprofiles = {'constant': 0, 'linear': 1, 'quadratic': 2, 'cubic': 3,
'quartic': 4, 'quintic': 5, 'sextic': 6, 'septic': 7,
'octic': 8}
scalingdirections = ['forward', 'reverse']
scalingprofiles = {
"constant": 0,
"linear": 1,
"quadratic": 2,
"cubic": 3,
"quartic": 4,
"quintic": 5,
"sextic": 6,
"septic": 7,
"octic": 8,
}
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
@@ -68,9 +75,9 @@ class CFS:
sigma: CFSParameter sigma parameter for CFS.
"""
self.alpha = CFSParameter(ID='alpha', scalingprofile='constant')
self.kappa = CFSParameter(ID='kappa', scalingprofile='constant', min=1, max=1)
self.sigma = CFSParameter(ID='sigma', scalingprofile='quartic', min=0, max=None)
self.alpha = CFSParameter(ID="alpha", scalingprofile="constant")
self.kappa = CFSParameter(ID="kappa", scalingprofile="constant", min=1, max=1)
self.sigma = CFSParameter(ID="sigma", scalingprofile="quartic", min=0, max=None)
def calculate_sigmamax(self, d, er, mr, G):
"""Calculates an optimum value for sigma max based on underlying
@@ -85,7 +92,7 @@ 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
@@ -105,8 +112,7 @@ 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]
@@ -129,28 +135,28 @@ 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':
if parameter.scalingprofile == "constant":
Evalues += parameter.max
Hvalues += parameter.max
elif parameter.scaling == 'polynomial':
elif parameter.scaling == "polynomial":
Evalues, Hvalues = self.scaling_polynomial(
CFSParameter.scalingprofiles[parameter.scalingprofile],
Evalues, Hvalues)
if parameter.ID == 'alpha':
CFSParameter.scalingprofiles[parameter.scalingprofile], Evalues, Hvalues
)
if parameter.ID == "alpha":
Evalues = Evalues * (self.alpha.max - self.alpha.min) + self.alpha.min
Hvalues = Hvalues * (self.alpha.max - self.alpha.min) + self.alpha.min
elif parameter.ID == 'kappa':
elif parameter.ID == "kappa":
Evalues = Evalues * (self.kappa.max - self.kappa.min) + self.kappa.min
Hvalues = Hvalues * (self.kappa.max - self.kappa.min) + self.kappa.min
elif parameter.ID == 'sigma':
elif parameter.ID == "sigma":
Evalues = Evalues * (self.sigma.max - self.sigma.min) + self.sigma.min
Hvalues = Hvalues * (self.sigma.max - self.sigma.min) + self.sigma.min
if parameter.scalingdirection == 'reverse':
if parameter.scalingdirection == "reverse":
Evalues = Evalues[::-1]
Hvalues = Hvalues[::-1]
# Magnetic values must be shifted one element to the left after
@@ -171,17 +177,17 @@ class PML:
# Available PML formulations:
# Higher Order RIPML (HORIPML) see: https://doi.org/10.1109/TAP.2011.2180344
# Multipole RIPML (MRIPML) see: https://doi.org/10.1109/TAP.2018.2823864
formulations = ['HORIPML', 'MRIPML']
formulations = ["HORIPML", "MRIPML"]
# PML slabs IDs at boundaries of domain.
boundaryIDs = ['x0', 'y0', 'z0', 'xmax', 'ymax', 'zmax']
boundaryIDs = ["x0", "y0", "z0", "xmax", "ymax", "zmax"]
# Indicates direction of increasing absorption
# xminus, yminus, zminus - absorption increases in negative direction of
# x-axis, y-axis, or z-axis
# xplus, yplus, zplus - absorption increases in positive direction of
# x-axis, y-axis, or z-axis
directions = ['xminus', 'yminus', 'zminus', 'xplus', 'yplus', 'zplus']
directions = ["xminus", "yminus", "zminus", "xplus", "yplus", "zplus"]
def __init__(self, G, ID=None, direction=None, xs=0, xf=0, ys=0, yf=0, zs=0, zf=0):
"""
@@ -206,50 +212,62 @@ class PML:
self.nz = zf - zs
# Spatial discretisation and thickness
if self.direction[0] == 'x':
if self.direction[0] == "x":
self.d = self.G.dx
self.thickness = self.nx
elif self.direction[0] == 'y':
elif self.direction[0] == "y":
self.d = self.G.dy
self.thickness = self.ny
elif self.direction[0] == 'z':
elif self.direction[0] == "z":
self.d = self.G.dz
self.thickness = self.nz
self.CFS = self.G.pmls['cfs']
self.CFS = self.G.pmls["cfs"]
self.initialise_field_arrays()
def initialise_field_arrays(self):
"""Initialise arrays to store fields in 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'])
self.EPhi2 = np.zeros((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'])
self.HPhi2 = np.zeros((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'])
self.EPhi2 = np.zeros((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'])
self.HPhi2 = np.zeros((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'])
self.EPhi2 = np.zeros((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'])
self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz),
dtype=config.sim_config.dtypes['float_or_double'])
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"]
)
self.EPhi2 = np.zeros(
(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"]
)
self.HPhi2 = np.zeros(
(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"]
)
self.EPhi2 = np.zeros(
(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"]
)
self.HPhi2 = np.zeros(
(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"]
)
self.EPhi2 = np.zeros(
(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"]
)
self.HPhi2 = np.zeros(
(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):
"""Calculates electric and magnetic update coefficients for the PML.
@@ -259,22 +277,14 @@ 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:
@@ -284,44 +294,38 @@ class PML:
Esigma, Hsigma = cfs.calculate_values(self.thickness, cfs.sigma)
# Define different parameters depending on PML formulation
if self.G.pmls['formulation'] == 'HORIPML':
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)
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)) / 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)
) / 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)
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)) / 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)
) / tmp
self.HRF[x, :] = (2 * Hsigma * self.G.dt) / (Hkappa * tmp)
elif self.G.pmls['formulation'] == 'MRIPML':
elif self.G.pmls["formulation"] == "MRIPML":
# MRIPML electric update coefficients
tmp = 2 * config.sim_config.em_consts['e0'] + self.G.dt * Ealpha
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.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.ERF[x, :] = (2 * Esigma * self.G.dt) / tmp
# MRIPML magnetic update coefficients
tmp = 2 * config.sim_config.em_consts['e0'] + self.G.dt * Halpha
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.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.HRF[x, :] = (2 * Hsigma * self.G.dt) / tmp
def update_electric(self):
@@ -329,26 +333,64 @@ class PML:
correction.
"""
pmlmodule = 'gprMax.cython.pml_updates_electric_' + self.G.pmls['formulation']
func = getattr(import_module(pmlmodule),
'order' + str(len(self.CFS)) + '_' + self.direction)
func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf,
config.get_model_config().ompthreads, self.G.updatecoeffsE, self.G.ID,
self.G.Ex, self.G.Ey, self.G.Ez, self.G.Hx, self.G.Hy, self.G.Hz,
self.EPhi1, self.EPhi2, self.ERA, self.ERB, self.ERE, self.ERF, self.d)
pmlmodule = "gprMax.cython.pml_updates_electric_" + self.G.pmls["formulation"]
func = getattr(import_module(pmlmodule), "order" + str(len(self.CFS)) + "_" + self.direction)
func(
self.xs,
self.xf,
self.ys,
self.yf,
self.zs,
self.zf,
config.get_model_config().ompthreads,
self.G.updatecoeffsE,
self.G.ID,
self.G.Ex,
self.G.Ey,
self.G.Ez,
self.G.Hx,
self.G.Hy,
self.G.Hz,
self.EPhi1,
self.EPhi2,
self.ERA,
self.ERB,
self.ERE,
self.ERF,
self.d,
)
def update_magnetic(self):
"""This functions updates magnetic field components with the PML
correction.
"""
pmlmodule = 'gprMax.cython.pml_updates_magnetic_' + self.G.pmls['formulation']
func = getattr(import_module(pmlmodule),
'order' + str(len(self.CFS)) + '_' + self.direction)
func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf,
config.get_model_config().ompthreads, self.G.updatecoeffsH, self.G.ID,
self.G.Ex, self.G.Ey, self.G.Ez, self.G.Hx, self.G.Hy, self.G.Hz,
self.HPhi1, self.HPhi2, self.HRA, self.HRB, self.HRE, self.HRF, self.d)
pmlmodule = "gprMax.cython.pml_updates_magnetic_" + self.G.pmls["formulation"]
func = getattr(import_module(pmlmodule), "order" + str(len(self.CFS)) + "_" + self.direction)
func(
self.xs,
self.xf,
self.ys,
self.yf,
self.zs,
self.zf,
config.get_model_config().ompthreads,
self.G.updatecoeffsH,
self.G.ID,
self.G.Ex,
self.G.Ey,
self.G.Ez,
self.G.Hx,
self.G.Hy,
self.G.Hz,
self.HPhi1,
self.HPhi2,
self.HRA,
self.HRB,
self.HRE,
self.HRF,
self.d,
)
class CUDAPML(PML):
@@ -380,9 +422,16 @@ class CUDAPML(PML):
def set_blocks_per_grid(self):
"""Sets the blocks per grid size used for updating the PML field arrays
on a GPU."""
self.bpg = (int(np.ceil(((self.EPhi1_dev.shape[1] + 1) *
(self.EPhi1_dev.shape[2] + 1) *
(self.EPhi1_dev.shape[3] + 1)) / self.G.tpb[0])), 1, 1)
self.bpg = (
int(
np.ceil(
((self.EPhi1_dev.shape[1] + 1) * (self.EPhi1_dev.shape[2] + 1) * (self.EPhi1_dev.shape[3] + 1))
/ self.G.tpb[0]
)
),
1,
1,
)
def get_update_funcs(self, kernelselectric, kernelsmagnetic):
"""Gets update functions from PML kernels.
@@ -394,16 +443,13 @@ class CUDAPML(PML):
magnetic updates.
"""
self.update_electric_dev = kernelselectric.get_function('order' +
str(len(self.CFS)) +
'_' + self.direction)
self.update_magnetic_dev = kernelsmagnetic.get_function('order' +
str(len(self.CFS)) +
'_' + self.direction)
self.update_electric_dev = kernelselectric.get_function("order" + str(len(self.CFS)) + "_" + self.direction)
self.update_magnetic_dev = kernelsmagnetic.get_function("order" + str(len(self.CFS)) + "_" + self.direction)
def update_electric(self):
"""Updates electric field components with the PML correction on the GPU."""
self.update_electric_dev(np.int32(self.xs),
self.update_electric_dev(
np.int32(self.xs),
np.int32(self.xf),
np.int32(self.ys),
np.int32(self.yf),
@@ -429,12 +475,15 @@ class CUDAPML(PML):
self.ERB_dev.gpudata,
self.ERE_dev.gpudata,
self.ERF_dev.gpudata,
config.sim_config.dtypes['float_or_double'](self.d),
block=self.G.tpb, grid=self.bpg)
config.sim_config.dtypes["float_or_double"](self.d),
block=self.G.tpb,
grid=self.bpg,
)
def update_magnetic(self):
"""Updates magnetic field components with the PML correction on the GPU."""
self.update_magnetic_dev(np.int32(self.xs),
self.update_magnetic_dev(
np.int32(self.xs),
np.int32(self.xf),
np.int32(self.ys),
np.int32(self.yf),
@@ -460,8 +509,10 @@ class CUDAPML(PML):
self.HRB_dev.gpudata,
self.HRE_dev.gpudata,
self.HRF_dev.gpudata,
config.sim_config.dtypes['float_or_double'](self.d),
block=self.G.tpb, grid=self.bpg)
config.sim_config.dtypes["float_or_double"](self.d),
block=self.G.tpb,
grid=self.bpg,
)
class OpenCLPML(PML):
@@ -508,7 +559,8 @@ class OpenCLPML(PML):
"""Updates electric field components with the PML correction on the
compute device.
"""
event = self.update_electric_dev(np.int32(self.xs),
event = self.update_electric_dev(
np.int32(self.xs),
np.int32(self.xf),
np.int32(self.ys),
np.int32(self.yf),
@@ -534,14 +586,16 @@ class OpenCLPML(PML):
self.ERB_dev,
self.ERE_dev,
self.ERF_dev,
config.sim_config.dtypes['float_or_double'](self.d))
config.sim_config.dtypes["float_or_double"](self.d),
)
event.wait()
def update_magnetic(self):
"""Updates magnetic field components with the PML correction on the
compute device.
"""
event = self.update_magnetic_dev(np.int32(self.xs),
event = self.update_magnetic_dev(
np.int32(self.xs),
np.int32(self.xf),
np.int32(self.ys),
np.int32(self.yf),
@@ -567,7 +621,8 @@ class OpenCLPML(PML):
self.HRB_dev,
self.HRE_dev,
self.HRF_dev,
config.sim_config.dtypes['float_or_double'](self.d))
config.sim_config.dtypes["float_or_double"](self.d),
)
event.wait()
@@ -578,19 +633,21 @@ def print_pml_info(G):
G: FDTDGrid class describing a grid in a model.
"""
# No PML
if all(value == 0 for value in G.pmls['thickness'].values()):
return f'\nPML boundaries [{G.name}]: switched off'
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()):
pmlinfo = str(G.pmls['thickness']['x0'])
if all(value == G.pmls["thickness"]["x0"] for value in G.pmls["thickness"].values()):
pmlinfo = str(G.pmls["thickness"]["x0"])
else:
pmlinfo = ''
for key, value in G.pmls['thickness'].items():
pmlinfo += f'{key}: {value}, '
pmlinfo = ""
for key, value in G.pmls["thickness"].items():
pmlinfo += f"{key}: {value}, "
pmlinfo = pmlinfo[:-2]
return (f"\nPML boundaries [{G.name}]: {{formulation: {G.pmls['formulation']}, "
f"order: {len(G.pmls['cfs'])}, thickness (cells): {pmlinfo}}}")
return (
f"\nPML boundaries [{G.name}]: {{formulation: {G.pmls['formulation']}, "
f"order: {len(G.pmls['cfs'])}, thickness (cells): {pmlinfo}}}"
)
def build_pml(G, pml_ID, thickness):
@@ -604,7 +661,7 @@ def build_pml(G, pml_ID, thickness):
thickness: int with thickness of PML slab in cells.
"""
# Arrays to hold values of permittivity and permeability (avoids accessing
# Arrays to hold values of permittivity and permeability (avoids accessing
# Material class in Cython.)
ers = np.zeros(len(G.materials))
mrs = np.zeros(len(G.materials))
@@ -613,57 +670,39 @@ def build_pml(G, pml_ID, thickness):
ers[i] = m.er
mrs[i] = m.mr
if config.sim_config.general['solver'] == 'cpu':
if config.sim_config.general["solver"] == "cpu":
pml_type = PML
elif config.sim_config.general['solver'] == 'cuda':
elif config.sim_config.general["solver"] == "cuda":
pml_type = CUDAPML
elif config.sim_config.general['solver'] == 'opencl':
elif config.sim_config.general["solver"] == "opencl":
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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
)
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
)
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
)
pml.CFS = G.pmls['cfs']
pml.CFS = G.pmls["cfs"]
pml.calculate_update_coeffs(averageer, averagemr)
G.pmls['slabs'].append(pml)
G.pmls["slabs"].append(pml)

查看文件

@@ -23,14 +23,13 @@ import numpy as np
class Rx:
"""Receiver output points."""
allowableoutputs = ['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz', 'Ix', 'Iy', 'Iz']
allowableoutputs = ["Ex", "Ey", "Ez", "Hx", "Hy", "Hz", "Ix", "Iy", "Iz"]
defaultoutputs = allowableoutputs[:-3]
allowableoutputs_dev = allowableoutputs[:-3]
maxnumoutputs_dev = 0
def __init__(self):
self.ID = None
self.outputs = {}
self.xcoord = None
@@ -67,17 +66,20 @@ 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'])
rxs = np.zeros(
(len(Rx.allowableoutputs_dev), G.iterations, len(G.rxs)), dtype=config.sim_config.dtypes["float_or_double"]
)
# Copy arrays to compute device
if config.sim_config.general['solver'] == 'cuda':
if config.sim_config.general["solver"] == "cuda":
import pycuda.gpuarray as gpuarray
rxcoords_dev = gpuarray.to_gpu(rxcoords)
rxs_dev = gpuarray.to_gpu(rxs)
elif config.sim_config.general['solver'] == 'opencl':
elif config.sim_config.general["solver"] == "opencl":
import pyopencl.array as clarray
rxcoords_dev = clarray.to_device(queue, rxcoords)
rxs_dev = clarray.to_device(queue, rxs)
@@ -98,8 +100,10 @@ def dtoh_rx_array(rxs_dev, rxcoords_dev, G):
for rx in G.rxs:
for rxd in range(len(G.rxs)):
if (rx.xcoord == rxcoords_dev[rxd, 0] and
rx.ycoord == rxcoords_dev[rxd, 1] and
rx.zcoord == rxcoords_dev[rxd, 2]):
if (
rx.xcoord == rxcoords_dev[rxd, 0]
and rx.ycoord == rxcoords_dev[rxd, 1]
and rx.zcoord == rxcoords_dev[rxd, 2]
):
for output in rx.outputs.keys():
rx.outputs[output] = rxs_dev[Rx.allowableoutputs_dev.index(output), :, rxd]

查看文件

@@ -21,8 +21,7 @@ import logging
from .cmds_geometry.cmds_geometry import UserObjectGeometry
from .cmds_geometry.fractal_box_builder import FractalBoxBuilder
from .cmds_multiuse import UserObjectMulti
from .cmds_singleuse import (Discretisation, Domain, TimeWindow,
UserObjectSingle)
from .cmds_singleuse import Discretisation, Domain, TimeWindow, UserObjectSingle
from .materials import create_built_in_materials
from .subgrids.user_objects import SubGridBase as SubGridUserBase
from .user_inputs import create_user_input_points
@@ -53,7 +52,7 @@ class Scene:
elif isinstance(user_object, UserObjectSingle):
self.single_cmds.append(user_object)
else:
logger.exception('This object is unknown to gprMax')
logger.exception("This object is unknown to gprMax")
raise ValueError
def process_subgrid_commands(self):
@@ -91,7 +90,7 @@ class Scene:
try:
obj.create(grid, uip)
except ValueError:
logger.exception('Error creating user input object')
logger.exception("Error creating user input object")
raise
return self
@@ -100,16 +99,18 @@ class Scene:
# Check for duplicate commands and warn user if they exist
cmds_unique = list(set(self.single_cmds))
if len(cmds_unique) != len(self.single_cmds):
logger.exception('Duplicate single-use commands exist in the input.')
logger.exception("Duplicate single-use commands exist in the input.")
raise ValueError
# Check essential commands and warn user if missing
for cmd_type in self.essential_cmds:
d = any(isinstance(cmd, cmd_type) for cmd in cmds_unique)
if not d:
logger.exception('Your input file is missing essential commands ' +
'required to run a model. Essential commands ' +
'are: Domain, Discretisation, Time Window')
logger.exception(
"Your input file is missing essential commands "
+ "required to run a model. Essential commands "
+ "are: Domain, Discretisation, Time Window"
)
raise ValueError
self.process_cmds(cmds_unique, G)

查看文件

@@ -44,31 +44,35 @@ def save_snapshots(grid):
# Create directory for snapshots
snapshotdir = config.get_model_config().set_snapshots_dir()
snapshotdir.mkdir(exist_ok=True)
logger.info('')
logger.info(f'Snapshot directory: {snapshotdir.resolve()}')
logger.info("")
logger.info(f"Snapshot directory: {snapshotdir.resolve()}")
for i, snap in enumerate(grid.snapshots):
fn = snapshotdir / Path(snap.filename)
snap.filename = fn.with_suffix(snap.fileext)
pbar = tqdm(total=snap.nbytes, leave=True, unit='byte',
unit_scale=True, 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'])
pbar = tqdm(
total=snap.nbytes,
leave=True,
unit="byte",
unit_scale=True,
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"],
)
snap.write_file(pbar, grid)
pbar.close()
logger.info('')
logger.info("")
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
fileexts = ['.vti', '.h5']
fileexts = [".vti", ".h5"]
# Dimensions of largest requested snapshot
nx_max = 0
@@ -80,9 +84,22 @@ class Snapshot:
# GPU - blocks per grid - set according to largest requested snapshot
bpg = None
def __init__(self, xs=None, ys=None, zs=None, xf=None, yf=None, zf=None,
dx=None, dy=None, dz=None, time=None, filename=None,
fileext=None, outputs=None):
def __init__(
self,
xs=None,
ys=None,
zs=None,
xf=None,
yf=None,
zf=None,
dx=None,
dy=None,
dz=None,
time=None,
filename=None,
fileext=None,
outputs=None,
):
"""
Args:
xs, xf, ys, yf, zs, zf: ints for the extent of the volume in cells.
@@ -118,14 +135,14 @@ class Snapshot:
self.snapfields = {}
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.nbytes += (self.snapfields[k].nbytes)
self.snapfields[k] = np.zeros(
(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.
@@ -149,24 +166,24 @@ class Snapshot:
self.ny,
self.nz,
config.get_model_config().ompthreads,
self.outputs['Ex'],
self.outputs['Ey'],
self.outputs['Ez'],
self.outputs['Hx'],
self.outputs['Hy'],
self.outputs['Hz'],
self.outputs["Ex"],
self.outputs["Ey"],
self.outputs["Ez"],
self.outputs["Hx"],
self.outputs["Hy"],
self.outputs["Hz"],
Exslice,
Eyslice,
Ezslice,
Hxslice,
Hyslice,
Hzslice,
self.snapfields['Ex'],
self.snapfields['Ey'],
self.snapfields['Ez'],
self.snapfields['Hx'],
self.snapfields['Hy'],
self.snapfields['Hz']
self.snapfields["Ex"],
self.snapfields["Ey"],
self.snapfields["Ez"],
self.snapfields["Hx"],
self.snapfields["Hy"],
self.snapfields["Hz"],
)
def write_file(self, pbar, G):
@@ -178,9 +195,9 @@ class Snapshot:
G: FDTDGrid class describing a grid in a model.
"""
if self.fileext == '.vti':
if self.fileext == ".vti":
self.write_vtk(pbar, G)
elif self.fileext == '.h5':
elif self.fileext == ".h5":
self.write_hdf5(pbar, G)
def write_vtk(self, pbar, G):
@@ -195,31 +212,33 @@ class Snapshot:
for k, v in self.outputs.items():
if v:
if k == 'Ex':
celldata[k] = self.snapfields['Ex']
if k == 'Ey':
celldata[k] = self.snapfields['Ey']
if k == 'Ez':
celldata[k] = self.snapfields['Ez']
if k == 'Hx':
celldata[k] = self.snapfields['Hx']
if k == 'Hy':
celldata[k] = self.snapfields['Hy']
if k == 'Hz':
celldata[k] = self.snapfields['Hz']
if k == "Ex":
celldata[k] = self.snapfields["Ex"]
if k == "Ey":
celldata[k] = self.snapfields["Ey"]
if k == "Ez":
celldata[k] = self.snapfields["Ez"]
if k == "Hx":
celldata[k] = self.snapfields["Hx"]
if k == "Hy":
celldata[k] = self.snapfields["Hy"]
if k == "Hz":
celldata[k] = self.snapfields["Hz"]
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)),
spacing=((self.dx * G.dx),
(self.dy * G.dy),
(self.dz * G.dz)),
cellData=celldata)
pbar.update(n=len(celldata) * self.nx * self.ny * self.nz *
np.dtype(config.sim_config.dtypes['float_or_double']).itemsize)
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)),
spacing=((self.dx * G.dx), (self.dy * G.dy), (self.dz * G.dz)),
cellData=celldata,
)
pbar.update(
n=len(celldata)
* self.nx
* self.ny
* self.nz
* np.dtype(config.sim_config.dtypes["float_or_double"]).itemsize
)
def write_hdf5(self, pbar, G):
"""Writes snapshot file in HDF5 (.h5) format.
@@ -229,31 +248,31 @@ class Snapshot:
G: FDTDGrid class describing a grid in a model.
"""
f = h5py.File(self.filename, 'w')
f.attrs['gprMax'] = __version__
f.attrs['Title'] = G.title
f.attrs['nx_ny_nz'] = (self.nx, self.ny, self.nz)
f.attrs['dx_dy_dz'] = (self.dx * G.dx, self.dy * G.dy, self.dz * G.dz)
f.attrs['time'] = self.time * G.dt
f = h5py.File(self.filename, "w")
f.attrs["gprMax"] = __version__
f.attrs["Title"] = G.title
f.attrs["nx_ny_nz"] = (self.nx, self.ny, self.nz)
f.attrs["dx_dy_dz"] = (self.dx * G.dx, self.dy * G.dy, self.dz * G.dz)
f.attrs["time"] = self.time * G.dt
if self.outputs['Ex']:
f['Ex'] = self.snapfields['Ex']
pbar.update(n=self.snapfields['Ex'].nbytes)
if self.outputs['Ey']:
f['Ey'] = self.snapfields['Ey']
pbar.update(n=self.snapfields['Ey'].nbytes)
if self.outputs['Ez']:
f['Ez'] = self.snapfields['Ez']
pbar.update(n=self.snapfields['Ez'].nbytes)
if self.outputs['Hx']:
f['Hx'] = self.snapfields['Hx']
pbar.update(n=self.snapfields['Hx'].nbytes)
if self.outputs['Hy']:
f['Hy'] = self.snapfields['Hy']
pbar.update(n=self.snapfields['Hy'].nbytes)
if self.outputs['Hz']:
f['Hz'] = self.snapfields['Hz']
pbar.update(n=self.snapfields['Hz'].nbytes)
if self.outputs["Ex"]:
f["Ex"] = self.snapfields["Ex"]
pbar.update(n=self.snapfields["Ex"].nbytes)
if self.outputs["Ey"]:
f["Ey"] = self.snapfields["Ey"]
pbar.update(n=self.snapfields["Ey"].nbytes)
if self.outputs["Ez"]:
f["Ez"] = self.snapfields["Ez"]
pbar.update(n=self.snapfields["Ez"].nbytes)
if self.outputs["Hx"]:
f["Hx"] = self.snapfields["Hx"]
pbar.update(n=self.snapfields["Hx"].nbytes)
if self.outputs["Hy"]:
f["Hy"] = self.snapfields["Hy"]
pbar.update(n=self.snapfields["Hy"].nbytes)
if self.outputs["Hz"]:
f["Hz"] = self.snapfields["Hz"]
pbar.update(n=self.snapfields["Hz"].nbytes)
f.close()
@@ -278,37 +297,44 @@ def htod_snapshot_array(G, queue=None):
if snap.nz > Snapshot.nz_max:
Snapshot.nz_max = snap.nz
if config.sim_config.general['solver'] == 'cuda':
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])), 1, 1)
elif config.sim_config.general['solver'] == 'opencl':
Snapshot.bpg = (
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)
snapEx = np.zeros((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'])
snapEz = np.zeros((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'])
snapHy = np.zeros((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 = 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"]
)
snapEy = np.zeros(
(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"]
)
snapHx = np.zeros(
(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"]
)
snapHz = np.zeros(
(numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"]
)
# Copy arrays to compute device
if config.sim_config.general['solver'] == 'cuda':
if config.sim_config.general["solver"] == "cuda":
import pycuda.gpuarray as gpuarray
snapEx_dev = gpuarray.to_gpu(snapEx)
snapEy_dev = gpuarray.to_gpu(snapEy)
snapEz_dev = gpuarray.to_gpu(snapEz)
@@ -316,8 +342,9 @@ def htod_snapshot_array(G, queue=None):
snapHy_dev = gpuarray.to_gpu(snapHy)
snapHz_dev = gpuarray.to_gpu(snapHz)
elif config.sim_config.general['solver'] == 'opencl':
elif config.sim_config.general["solver"] == "opencl":
import pyopencl.array as clarray
snapEx_dev = clarray.to_device(queue, snapEx)
snapEy_dev = clarray.to_device(queue, snapEy)
snapEz_dev = clarray.to_device(queue, snapEz)
@@ -328,8 +355,7 @@ 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.
@@ -339,9 +365,9 @@ def dtoh_snapshot_array(snapEx_dev, snapEy_dev, snapEz_dev,
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]

查看文件

@@ -30,11 +30,11 @@ def create_G():
G: FDTDGrid class describing a grid in a model.
"""
if config.sim_config.general['solver'] == 'cpu':
if config.sim_config.general["solver"] == "cpu":
G = FDTDGrid()
elif config.sim_config.general['solver'] == 'cuda':
elif config.sim_config.general["solver"] == "cuda":
G = CUDAGrid()
elif config.sim_config.general['solver'] == 'opencl':
elif config.sim_config.general["solver"] == "opencl":
G = OpenCLGrid()
return G
@@ -58,23 +58,24 @@ def create_solver(G):
solver: Solver object.
"""
if config.sim_config.general['subgrid']:
if config.sim_config.general["subgrid"]:
updates = create_subgrid_updates(G)
if config.get_model_config().materials['maxpoles'] != 0:
if config.get_model_config().materials["maxpoles"] != 0:
# Set dispersive update functions for both SubgridUpdates and
# SubgridUpdaters subclasses
updates.set_dispersive_updates()
for u in updates.updaters: u.set_dispersive_updates()
for u in updates.updaters:
u.set_dispersive_updates()
solver = Solver(updates, hsg=True)
elif config.sim_config.general['solver'] == 'cpu':
elif config.sim_config.general["solver"] == "cpu":
updates = CPUUpdates(G)
if config.get_model_config().materials['maxpoles'] != 0:
if config.get_model_config().materials["maxpoles"] != 0:
updates.set_dispersive_updates()
solver = Solver(updates)
elif config.sim_config.general['solver'] == 'cuda':
elif config.sim_config.general["solver"] == "cuda":
updates = CUDAUpdates(G)
solver = Solver(updates)
elif config.sim_config.general['solver'] == 'opencl':
elif config.sim_config.general["solver"] == "opencl":
updates = OpenCLUpdates(G)
solver = Solver(updates)
@@ -119,7 +120,7 @@ class Solver:
if self.hsg:
self.updates.hsg_1()
self.updates.update_electric_b()
if config.sim_config.general['solver'] == 'cuda':
if config.sim_config.general["solver"] == "cuda":
self.memused = self.updates.calculate_memory_used(iteration)
self.updates.finalise()

查看文件

@@ -48,12 +48,10 @@ class Source:
G: FDTDGrid class describing a grid in a model.
"""
# Waveform values for sources that need to be calculated on whole timesteps
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"])
# Waveform values for sources that need to be calculated on half timesteps
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"])
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
@@ -67,7 +65,6 @@ class Source:
self.waveformvalues_halfdt[iteration] = waveform.calculate_value(time + 0.5 * G.dt, G.dt)
class VoltageSource(Source):
"""A voltage source can be a hard source if it's resistance is zero,
i.e. the time variation of the specified electric field component
@@ -96,29 +93,35 @@ class VoltageSource(Source):
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = f'E{self.polarisation}'
componentID = f"E{self.polarisation}"
if self.polarisation == 'x':
if self.polarisation == "x":
if self.resistance != 0:
Ex[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] *
self.waveformvalues_halfdt[iteration] *
(1 / (self.resistance * G.dy * G.dz)))
Ex[i, j, k] -= (
updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4]
* self.waveformvalues_halfdt[iteration]
* (1 / (self.resistance * G.dy * G.dz))
)
else:
Ex[i, j, k] = -1 * self.waveformvalues_wholedt[iteration] / G.dx
elif self.polarisation == 'y':
elif self.polarisation == "y":
if self.resistance != 0:
Ey[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] *
self.waveformvalues_halfdt[iteration] *
(1 / (self.resistance * G.dx * G.dz)))
Ey[i, j, k] -= (
updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4]
* self.waveformvalues_halfdt[iteration]
* (1 / (self.resistance * G.dx * G.dz))
)
else:
Ey[i, j, k] = -1 * self.waveformvalues_wholedt[iteration] / G.dy
elif self.polarisation == 'z':
elif self.polarisation == "z":
if self.resistance != 0:
Ez[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] *
self.waveformvalues_halfdt[iteration] *
(1 / (self.resistance * G.dx * G.dy)))
Ez[i, j, k] -= (
updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4]
* self.waveformvalues_halfdt[iteration]
* (1 / (self.resistance * G.dx * G.dy))
)
else:
Ez[i, j, k] = -1 * self.waveformvalues_wholedt[iteration] / G.dz
@@ -136,21 +139,21 @@ class VoltageSource(Source):
j = self.ycoord
k = self.zcoord
componentID = f'E{self.polarisation}'
componentID = f"E{self.polarisation}"
requirednumID = G.ID[G.IDlookup[componentID], i, j, k]
material = next(x for x in G.materials if x.numID == requirednumID)
newmaterial = deepcopy(material)
newmaterial.ID = f'{material.ID}+{self.ID}'
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':
if self.polarisation == "x":
newmaterial.se += G.dx / (self.resistance * G.dy * G.dz)
elif self.polarisation == 'y':
elif self.polarisation == "y":
newmaterial.se += G.dy / (self.resistance * G.dx * G.dz)
elif self.polarisation == 'z':
elif self.polarisation == "z":
newmaterial.se += G.dz / (self.resistance * G.dx * G.dy)
G.ID[G.IDlookup[componentID], i, j, k] = newmaterial.numID
@@ -181,21 +184,30 @@ class HertzianDipole(Source):
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = f'E{self.polarisation}'
if self.polarisation == 'x':
Ex[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] *
self.waveformvalues_halfdt[iteration] * self.dl *
(1 / (G.dx * G.dy * G.dz)))
componentID = f"E{self.polarisation}"
if self.polarisation == "x":
Ex[i, j, k] -= (
updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4]
* self.waveformvalues_halfdt[iteration]
* self.dl
* (1 / (G.dx * G.dy * G.dz))
)
elif self.polarisation == 'y':
Ey[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] *
self.waveformvalues_halfdt[iteration] * self.dl *
(1 / (G.dx * G.dy * G.dz)))
elif self.polarisation == "y":
Ey[i, j, k] -= (
updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4]
* self.waveformvalues_halfdt[iteration]
* self.dl
* (1 / (G.dx * G.dy * G.dz))
)
elif self.polarisation == 'z':
Ez[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] *
self.waveformvalues_halfdt[iteration] * self.dl *
(1 / (G.dx * G.dy * G.dz)))
elif self.polarisation == "z":
Ez[i, j, k] -= (
updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4]
* self.waveformvalues_halfdt[iteration]
* self.dl
* (1 / (G.dx * G.dy * G.dz))
)
class MagneticDipole(Source):
@@ -218,22 +230,28 @@ class MagneticDipole(Source):
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = f'H{self.polarisation}'
componentID = f"H{self.polarisation}"
if self.polarisation == 'x':
Hx[i, j, k] -= (updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] *
self.waveformvalues_wholedt[iteration] *
(1 / (G.dx * G.dy * G.dz)))
if self.polarisation == "x":
Hx[i, j, k] -= (
updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4]
* self.waveformvalues_wholedt[iteration]
* (1 / (G.dx * G.dy * G.dz))
)
elif self.polarisation == 'y':
Hy[i, j, k] -= (updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] *
self.waveformvalues_wholedt[iteration] *
(1 / (G.dx * G.dy * G.dz)))
elif self.polarisation == "y":
Hy[i, j, k] -= (
updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4]
* self.waveformvalues_wholedt[iteration]
* (1 / (G.dx * G.dy * G.dz))
)
elif self.polarisation == 'z':
Hz[i, j, k] -= (updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] *
self.waveformvalues_wholedt[iteration] *
(1 / (G.dx * G.dy * G.dz)))
elif self.polarisation == "z":
Hz[i, j, k] -= (
updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4]
* self.waveformvalues_wholedt[iteration]
* (1 / (G.dx * G.dy * G.dz))
)
def htod_src_arrays(sources, G, queue=None):
@@ -254,24 +272,24 @@ 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
srcinfo1[i, 2] = src.zcoord
if src.polarisation == 'x':
if src.polarisation == "x":
srcinfo1[i, 3] = 0
elif src.polarisation == 'y':
elif src.polarisation == "y":
srcinfo1[i, 3] = 1
elif src.polarisation == 'z':
elif src.polarisation == "z":
srcinfo1[i, 3] = 2
if src.__class__.__name__ == 'HertzianDipole':
if src.__class__.__name__ == "HertzianDipole":
srcinfo2[i] = src.dl
srcwaves[i, :] = src.waveformvalues_halfdt
elif src.__class__.__name__ == 'VoltageSource':
elif src.__class__.__name__ == "VoltageSource":
if src.resistance:
srcinfo2[i] = src.resistance
srcwaves[i, :] = src.waveformvalues_halfdt
@@ -280,17 +298,19 @@ def htod_src_arrays(sources, G, queue=None):
srcwaves[i, :] = src.waveformvalues_wholedt
srcinfo2[i] = src.resistance
srcwaves[i, :] = src.waveformvalues_halfdt
elif src.__class__.__name__ == 'MagneticDipole':
elif src.__class__.__name__ == "MagneticDipole":
srcwaves[i, :] = src.waveformvalues_wholedt
# Copy arrays to compute device
if config.sim_config.general['solver'] == 'cuda':
if config.sim_config.general["solver"] == "cuda":
import pycuda.gpuarray as gpuarray
srcinfo1_dev = gpuarray.to_gpu(srcinfo1)
srcinfo2_dev = gpuarray.to_gpu(srcinfo2)
srcwaves_dev = gpuarray.to_gpu(srcwaves)
elif config.sim_config.general['solver'] == 'opencl':
elif config.sim_config.general["solver"] == "opencl":
import pyopencl.array as clarray
srcinfo1_dev = clarray.to_device(queue, srcinfo1)
srcinfo2_dev = clarray.to_device(queue, srcinfo2)
srcwaves_dev = clarray.to_device(queue, srcwaves)
@@ -330,12 +350,12 @@ 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_incident_V_I(self, G):
"""Calculates the incident voltage and current with a long length
@@ -377,8 +397,9 @@ 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.voltage[1 : self.nl] -= (
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]
@@ -395,13 +416,16 @@ class TransmissionLine(Source):
"""
# Update all the current values along the line
self.current[0:self.nl - 1] -= ((1 / self.resistance) * (config.c * G.dt / self.dl) *
(self.voltage[1:self.nl] - self.voltage[0:self.nl - 1]))
self.current[0 : self.nl - 1] -= (
(1 / self.resistance)
* (config.c * G.dt / self.dl)
* (self.voltage[1 : self.nl] - self.voltage[0 : self.nl - 1])
)
# 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])
self.current[self.srcpos - 1] += (
(1 / self.resistance) * (config.c * G.dt / self.dl) * self.waveformvalues_wholedt[iteration]
)
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
"""Updates electric field value in the main grid from voltage value in
@@ -424,13 +448,13 @@ class TransmissionLine(Source):
self.update_voltage(iteration, G)
if self.polarisation == 'x':
if self.polarisation == "x":
Ex[i, j, k] = -self.voltage[self.antpos] / G.dx
elif self.polarisation == 'y':
elif self.polarisation == "y":
Ey[i, j, k] = -self.voltage[self.antpos] / G.dy
elif self.polarisation == 'z':
elif self.polarisation == "z":
Ez[i, j, k] = -self.voltage[self.antpos] / G.dz
def update_magnetic(self, iteration, updatecoeffsH, ID, Hx, Hy, Hz, G):
@@ -452,13 +476,13 @@ class TransmissionLine(Source):
j = self.ycoord
k = self.zcoord
if self.polarisation == 'x':
if self.polarisation == "x":
self.current[self.antpos] = Ix(i, j, k, G.Hx, G.Hy, G.Hz, G)
elif self.polarisation == 'y':
elif self.polarisation == "y":
self.current[self.antpos] = Iy(i, j, k, G.Hx, G.Hy, G.Hz, G)
elif self.polarisation == 'z':
elif self.polarisation == "z":
self.current[self.antpos] = Iz(i, j, k, G.Hx, G.Hy, G.Hz, G)
self.update_current(iteration, G)

查看文件

@@ -24,42 +24,41 @@ logger = logging.getLogger(__name__)
class SubGridBaseGrid(FDTDGrid):
def __init__(self, *args, **kwargs):
super().__init__()
self.ratio = kwargs['ratio']
self.ratio = kwargs["ratio"]
if self.ratio % 2 == 0:
logger.exception('Subgrid Error: Only odd ratios are supported')
logger.exception("Subgrid Error: Only odd ratios are supported")
raise ValueError
# Name of the grid
self.name = kwargs['id']
self.name = kwargs["id"]
self.filter = kwargs['filter']
self.filter = kwargs["filter"]
# Number of main grid cells between the IS and OS
self.is_os_sep = kwargs['is_os_sep']
self.is_os_sep = kwargs["is_os_sep"]
# Number of subgrid grid cells between the IS and OS
self.s_is_os_sep = self.is_os_sep * self.ratio
# Distance from OS to PML or the edge of the grid when PML is off
self.pml_separation = kwargs['pml_separation']
self.pml_separation = kwargs["pml_separation"]
self.pmls['thickness']['x0'] = kwargs['subgrid_pml_thickness']
self.pmls['thickness']['y0'] = kwargs['subgrid_pml_thickness']
self.pmls['thickness']['z0'] = kwargs['subgrid_pml_thickness']
self.pmls['thickness']['xmax'] = kwargs['subgrid_pml_thickness']
self.pmls['thickness']['ymax'] = kwargs['subgrid_pml_thickness']
self.pmls['thickness']['zmax'] = kwargs['subgrid_pml_thickness']
self.pmls["thickness"]["x0"] = kwargs["subgrid_pml_thickness"]
self.pmls["thickness"]["y0"] = kwargs["subgrid_pml_thickness"]
self.pmls["thickness"]["z0"] = kwargs["subgrid_pml_thickness"]
self.pmls["thickness"]["xmax"] = kwargs["subgrid_pml_thickness"]
self.pmls["thickness"]["ymax"] = kwargs["subgrid_pml_thickness"]
self.pmls["thickness"]["zmax"] = kwargs["subgrid_pml_thickness"]
# Number of sub cells to extend the sub grid beyond the IS boundary
d_to_pml = self.s_is_os_sep + self.pml_separation
# Index of the IS
self.n_boundary_cells = d_to_pml + self.pmls['thickness']['x0']
self.n_boundary_cells_x = d_to_pml + self.pmls['thickness']['x0']
self.n_boundary_cells_y = d_to_pml + self.pmls['thickness']['y0']
self.n_boundary_cells_z = d_to_pml + self.pmls['thickness']['z0']
self.n_boundary_cells = d_to_pml + self.pmls["thickness"]["x0"]
self.n_boundary_cells_x = d_to_pml + self.pmls["thickness"]["x0"]
self.n_boundary_cells_y = d_to_pml + self.pmls["thickness"]["y0"]
self.n_boundary_cells_z = d_to_pml + self.pmls["thickness"]["z0"]
self.interpolation = kwargs['interpolation']
self.interpolation = kwargs["interpolation"]

查看文件

@@ -30,7 +30,6 @@ def calculate_weighting_coefficients(x1, x):
class PrecursorNodesBase:
def __init__(self, fdtd_grid, sub_grid):
self.G = fdtd_grid
self.ratio = sub_grid.ratio
@@ -68,7 +67,6 @@ class PrecursorNodesBase:
self.initialize_electric_slices_array()
def _initialize_fields(self):
# Initialise the precursor arrays
# The precursors are divided up into the 6. Each represent 1
@@ -144,23 +142,34 @@ class PrecursorNodesBase:
self.hy_bottom_0 = np.copy(self.hy_top_1)
def _initialize_field_names(self):
self.fn_m = [
'hx_front', 'hz_front',
'hx_back', 'hz_back',
'hy_left', 'hz_left',
'hy_right', 'hz_right',
'hx_top', 'hy_top',
'hx_bottom', 'hy_bottom'
"hx_front",
"hz_front",
"hx_back",
"hz_back",
"hy_left",
"hz_left",
"hy_right",
"hz_right",
"hx_top",
"hy_top",
"hx_bottom",
"hy_bottom",
]
self.fn_e = [
'ex_front', 'ez_front',
'ex_back', 'ez_back',
'ey_left', 'ez_left',
'ey_right', 'ez_right',
'ex_top', 'ey_top',
'ex_bottom', 'ey_bottom'
"ex_front",
"ez_front",
"ex_back",
"ez_back",
"ey_left",
"ez_left",
"ey_right",
"ez_right",
"ex_top",
"ey_top",
"ex_bottom",
"ey_bottom",
]
def interpolate_magnetic_in_time(self, m):
@@ -174,7 +183,7 @@ class PrecursorNodesBase:
for f in field_names:
try:
val = c1 * getattr(self, f'{f}_0') + c2 * getattr(self, f'{f}_1')
val = c1 * getattr(self, f"{f}_0") + c2 * getattr(self, f"{f}_1")
except ValueError:
raise
setattr(self, f, val)
@@ -184,7 +193,7 @@ class PrecursorNodesBase:
current main time step, i.e. ey_left = copy.ey_left_1
"""
for f in field_names:
val = np.copy(getattr(self, f'{f}_1'))
val = np.copy(getattr(self, f"{f}_1"))
setattr(self, f, val)
def calc_exact_magnetic_in_time(self):
@@ -194,7 +203,6 @@ class PrecursorNodesBase:
self.calc_exact_field(self.fn_e)
def create_interpolated_coords(self, mid, field):
n_x = field.shape[0]
n_y = field.shape[1]
@@ -218,9 +226,9 @@ class PrecursorNodesBase:
def update_previous_timestep_fields(self, field_names):
for fn in field_names:
val = getattr(self, f'{fn}_1')
val = getattr(self, f"{fn}_1")
val_c = np.copy(val)
setattr(self, f'{fn}_0', val_c)
setattr(self, f"{fn}_0", val_c)
def interpolate_to_sub_grid(self, field, coords):
x, z, x_sg, z_sg = coords
@@ -229,7 +237,6 @@ class PrecursorNodesBase:
return f_i
def update_electric(self):
self.update_previous_timestep_fields(self.fn_e)
for obj in self.electric_slices:
@@ -240,20 +247,16 @@ class PrecursorNodesBase:
setattr(self, obj[0], f)
def update_magnetic(self):
# Copy previous time step magnetic field values to the previous
# time step variables
self.update_previous_timestep_fields(self.fn_m)
for obj in self.magnetic_slices:
# Grab the main grid fields used to interpolate across the IS
# f = self.Hi[slice]
f_1, f_2 = self.get_transverse_h(obj)
if ('left' in obj[0] or
'bottom' in obj[0] or
'front' in obj[0]):
if "left" in obj[0] or "bottom" in obj[0] or "front" in obj[0]:
w = self.l_weight
else:
w = self.r_weight
@@ -274,12 +277,10 @@ class PrecursorNodesBase:
class PrecursorNodes(PrecursorNodesBase):
def __init__(self, fdtd_grid, sub_grid):
super().__init__(fdtd_grid, sub_grid)
def initialize_magnetic_slices_array(self):
# Array contains the indices at which the main grid should be sliced
# to obtain the 2 2d array of H nodes required for interpolation
# across the IS boundary for each h field on each face of the subgrid
@@ -302,43 +303,91 @@ class PrecursorNodes(PrecursorNodesBase):
k1 = self.k1
slices = [
['hy_left_1', False,
[
"hy_left_1",
False,
(self.i0 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Hy],
['hy_right_1', False,
(self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
self.Hy,
],
[
"hy_right_1",
False,
(self.i1 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Hy],
['hz_left_1', True,
(self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
self.Hy,
],
[
"hz_left_1",
True,
(self.i0 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Hz],
['hz_right_1', True,
(self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
self.Hz,
],
[
"hz_right_1",
True,
(self.i1 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Hz],
['hx_front_1', False,
(self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
self.Hz,
],
[
"hx_front_1",
False,
(slice(i0, i1 + 1, 1), self.j0 - 1, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), self.Hx],
['hx_back_1', False,
(slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)),
self.Hx,
],
[
"hx_back_1",
False,
(slice(i0, i1 + 1, 1), self.j1 - 1, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), self.Hx],
['hz_front_1', True,
(slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)),
self.Hx,
],
[
"hz_front_1",
True,
(slice(i0, i1, 1), self.j0 - 1, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), self.Hz],
['hz_back_1', True,
(slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)),
self.Hz,
],
[
"hz_back_1",
True,
(slice(i0, i1, 1), self.j1 - 1, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), self.Hz],
['hx_bottom_1', False,
(slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)),
self.Hz,
],
[
"hx_bottom_1",
False,
# check these indexes
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 1),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), self.Hx],
['hx_top_1', False,
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0),
self.Hx,
],
[
"hx_top_1",
False,
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 1),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), self.Hx],
['hy_bottom_1', True,
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1),
self.Hx,
],
[
"hy_bottom_1",
True,
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 1),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), self.Hy],
['hy_top_1', True,
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0),
self.Hy,
],
[
"hy_top_1",
True,
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 1),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), self.Hy]
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1),
self.Hy,
],
]
for obj in slices:
@@ -367,30 +416,18 @@ 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:
@@ -410,12 +447,10 @@ class PrecursorNodes(PrecursorNodesBase):
class PrecursorNodesFiltered(PrecursorNodesBase):
def __init__(self, fdtd_grid, sub_grid):
super().__init__(fdtd_grid, sub_grid)
def initialize_magnetic_slices_array(self):
# Array contains the indices at which the main grid should be sliced
# to obtain the 2 2d array of H nodes required for interpolation
# across the IS boundary for each h field on each face of the subgrid
@@ -438,67 +473,115 @@ class PrecursorNodesFiltered(PrecursorNodesBase):
k1 = self.k1
slices = [
['hy_left_1', False,
[
"hy_left_1",
False,
(self.i0 - 2, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i0 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i0 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Hy],
['hy_right_1', False,
(self.i0 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
self.Hy,
],
[
"hy_right_1",
False,
(self.i1 - 2, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i1 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i1 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Hy],
['hz_left_1', True,
(self.i1 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
self.Hy,
],
[
"hz_left_1",
True,
(self.i0 - 2, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i0 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i0 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Hz],
['hz_right_1', True,
(self.i0 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
self.Hz,
],
[
"hz_right_1",
True,
(self.i1 - 2, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i1 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i1 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Hz],
['hx_front_1', False,
(self.i1 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
self.Hz,
],
[
"hx_front_1",
False,
(slice(i0, i1 + 1, 1), self.j0 - 2, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j0 - 1, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j0 + 1, slice(k0, k1, 1)), self.Hx],
['hx_back_1', False,
(slice(i0, i1 + 1, 1), self.j0 + 1, slice(k0, k1, 1)),
self.Hx,
],
[
"hx_back_1",
False,
(slice(i0, i1 + 1, 1), self.j1 - 2, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j1 - 1, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j1 + 1, slice(k0, k1, 1)), self.Hx],
['hz_front_1', True,
(slice(i0, i1 + 1, 1), self.j1 + 1, slice(k0, k1, 1)),
self.Hx,
],
[
"hz_front_1",
True,
(slice(i0, i1, 1), self.j0 - 2, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j0 - 1, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j0 + 1, slice(k0, k1 + 1, 1)), self.Hz],
['hz_back_1', True,
(slice(i0, i1, 1), self.j0 + 1, slice(k0, k1 + 1, 1)),
self.Hz,
],
[
"hz_back_1",
True,
(slice(i0, i1, 1), self.j1 - 2, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j1 - 1, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j1 + 1, slice(k0, k1 + 1, 1)), self.Hz],
['hx_bottom_1', False,
(slice(i0, i1, 1), self.j1 + 1, slice(k0, k1 + 1, 1)),
self.Hz,
],
[
"hx_bottom_1",
False,
# Check these indexes
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 2),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 1),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 + 1), self.Hx],
['hx_top_1', False,
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 + 1),
self.Hx,
],
[
"hx_top_1",
False,
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 2),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 1),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 + 1), self.Hx],
['hy_bottom_1', True,
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 + 1),
self.Hx,
],
[
"hy_bottom_1",
True,
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 2),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 1),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 + 1), self.Hy],
['hy_top_1', True,
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 + 1),
self.Hy,
],
[
"hy_top_1",
True,
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 2),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 1),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 + 1), self.Hy]
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 + 1),
self.Hy,
],
]
for obj in slices:
@@ -527,54 +610,102 @@ class PrecursorNodesFiltered(PrecursorNodesBase):
# Spatially interpolate nodes
slices = [
['ex_front_1', True,
[
"ex_front_1",
True,
(slice(i0, i1, 1), self.j0 - 1, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j0 + 1, slice(k0, k1 + 1, 1)), self.Ex],
['ex_back_1', True,
(slice(i0, i1, 1), self.j0 + 1, slice(k0, k1 + 1, 1)),
self.Ex,
],
[
"ex_back_1",
True,
(slice(i0, i1, 1), self.j1 - 1, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)),
(slice(i0, i1, 1), self.j1 + 1, slice(k0, k1 + 1, 1)), self.Ex],
['ez_front_1', False,
(slice(i0, i1, 1), self.j1 + 1, slice(k0, k1 + 1, 1)),
self.Ex,
],
[
"ez_front_1",
False,
(slice(i0, i1 + 1, 1), self.j0 - 1, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j0 + 1, slice(k0, k1, 1)), self.Ez],
['ez_back_1', False,
(slice(i0, i1 + 1, 1), self.j0 + 1, slice(k0, k1, 1)),
self.Ez,
],
[
"ez_back_1",
False,
(slice(i0, i1 + 1, 1), self.j1 - 1, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)),
(slice(i0, i1 + 1, 1), self.j1 + 1, slice(k0, k1, 1)), self.Ez],
['ey_left_1', True,
(slice(i0, i1 + 1, 1), self.j1 + 1, slice(k0, k1, 1)),
self.Ez,
],
[
"ey_left_1",
True,
(self.i0 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i0 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey],
['ey_right_1', True,
(self.i0 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
self.Ey,
],
[
"ey_right_1",
True,
(self.i1 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
(self.i1 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey],
['ez_left_1', False,
(self.i1 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)),
self.Ey,
],
[
"ez_left_1",
False,
(self.i0 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i0 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez],
['ez_right_1', False,
(self.i0 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
self.Ez,
],
[
"ez_right_1",
False,
(self.i1 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)),
(self.i1 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez],
['ex_bottom_1', True,
(self.i1 + 1, 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 - 1),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 + 1), self.Ex],
['ex_top_1', True,
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 + 1),
self.Ex,
],
[
"ex_top_1",
True,
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 1),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1),
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 + 1), self.Ex],
['ey_bottom_1', False,
(slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 + 1),
self.Ex,
],
[
"ey_bottom_1",
False,
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 1),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 + 1), self.Ey],
['ey_top_1', False,
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 + 1),
self.Ey,
],
[
"ey_top_1",
False,
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 1),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1),
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 + 1), self.Ey]
(slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 + 1),
self.Ey,
],
]
for obj in slices:

查看文件

@@ -20,15 +20,13 @@ 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__)
class SubGridHSG(SubGridBaseGrid):
def __init__(self, **kwargs):
super().__init__(**kwargs)
@@ -46,43 +44,139 @@ class SubGridHSG(SubGridBaseGrid):
# Hx = c0Hx - c2Ez + c3Ey
# Bottom and top
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID,
self.n_boundary_cells, -1, self.nwx, self.nwy + 1, self.nwz, 1,
self.Hy, precursors.ex_bottom, precursors.ex_top,
self.IDlookup['Hy'], 1, -1, 3,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsH,
self.ID,
self.n_boundary_cells,
-1,
self.nwx,
self.nwy + 1,
self.nwz,
1,
self.Hy,
precursors.ex_bottom,
precursors.ex_top,
self.IDlookup["Hy"],
1,
-1,
3,
config.get_model_config().ompthreads,
)
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID,
self.n_boundary_cells, -1, self.nwx + 1, self.nwy, self.nwz, 1,
self.Hx, precursors.ey_bottom, precursors.ey_top,
self.IDlookup['Hx'], -1, 1, 3,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsH,
self.ID,
self.n_boundary_cells,
-1,
self.nwx + 1,
self.nwy,
self.nwz,
1,
self.Hx,
precursors.ey_bottom,
precursors.ey_top,
self.IDlookup["Hx"],
-1,
1,
3,
config.get_model_config().ompthreads,
)
# Left and right
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID,
self.n_boundary_cells, -1, self.nwy, self.nwz + 1, self.nwx, 2,
self.Hz, precursors.ey_left, precursors.ey_right,
self.IDlookup['Hz'], 1, -1, 1,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsH,
self.ID,
self.n_boundary_cells,
-1,
self.nwy,
self.nwz + 1,
self.nwx,
2,
self.Hz,
precursors.ey_left,
precursors.ey_right,
self.IDlookup["Hz"],
1,
-1,
1,
config.get_model_config().ompthreads,
)
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID,
self.n_boundary_cells, -1, self.nwy + 1, self.nwz, self.nwx, 2,
self.Hy, precursors.ez_left, precursors.ez_right,
self.IDlookup['Hy'], -1, 1, 1,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsH,
self.ID,
self.n_boundary_cells,
-1,
self.nwy + 1,
self.nwz,
self.nwx,
2,
self.Hy,
precursors.ez_left,
precursors.ez_right,
self.IDlookup["Hy"],
-1,
1,
1,
config.get_model_config().ompthreads,
)
# Front and back
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID,
self.n_boundary_cells, -1, self.nwx, self.nwz + 1, self.nwy, 3,
self.Hz, precursors.ex_front, precursors.ex_back,
self.IDlookup['Hz'], -1, 1, 2,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsH,
self.ID,
self.n_boundary_cells,
-1,
self.nwx,
self.nwz + 1,
self.nwy,
3,
self.Hz,
precursors.ex_front,
precursors.ex_back,
self.IDlookup["Hz"],
-1,
1,
2,
config.get_model_config().ompthreads,
)
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID,
self.n_boundary_cells, -1, self.nwx + 1, self.nwz, self.nwy, 3,
self.Hx, precursors.ez_front, precursors.ez_back,
self.IDlookup['Hx'], 1, -1, 2,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsH,
self.ID,
self.n_boundary_cells,
-1,
self.nwx + 1,
self.nwz,
self.nwy,
3,
self.Hx,
precursors.ez_front,
precursors.ez_back,
self.IDlookup["Hx"],
1,
-1,
2,
config.get_model_config().ompthreads,
)
def update_electric_is(self, precursors):
"""Updates the subgrid nodes at the IS with the currents derived
@@ -98,43 +192,139 @@ class SubGridHSG(SubGridBaseGrid):
# Ez = c0(Ez) + c1(dHy) - c2(dHx)
# Bottom and top
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID,
self.n_boundary_cells, 0, self.nwx, self.nwy + 1, self.nwz, 1,
self.Ex, precursors.hy_bottom, precursors.hy_top,
self.IDlookup['Ex'], 1, -1, 3,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsE,
self.ID,
self.n_boundary_cells,
0,
self.nwx,
self.nwy + 1,
self.nwz,
1,
self.Ex,
precursors.hy_bottom,
precursors.hy_top,
self.IDlookup["Ex"],
1,
-1,
3,
config.get_model_config().ompthreads,
)
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID,
self.n_boundary_cells, 0, self.nwx + 1, self.nwy, self.nwz, 1,
self.Ey, precursors.hx_bottom, precursors.hx_top,
self.IDlookup['Ey'], -1, 1, 3,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsE,
self.ID,
self.n_boundary_cells,
0,
self.nwx + 1,
self.nwy,
self.nwz,
1,
self.Ey,
precursors.hx_bottom,
precursors.hx_top,
self.IDlookup["Ey"],
-1,
1,
3,
config.get_model_config().ompthreads,
)
# Left and right
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID,
self.n_boundary_cells, 0, self.nwy, self.nwz + 1, self.nwx, 2,
self.Ey, precursors.hz_left, precursors.hz_right,
self.IDlookup['Ey'], 1, -1, 1,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsE,
self.ID,
self.n_boundary_cells,
0,
self.nwy,
self.nwz + 1,
self.nwx,
2,
self.Ey,
precursors.hz_left,
precursors.hz_right,
self.IDlookup["Ey"],
1,
-1,
1,
config.get_model_config().ompthreads,
)
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID,
self.n_boundary_cells, 0, self.nwy + 1, self.nwz, self.nwx, 2,
self.Ez, precursors.hy_left, precursors.hy_right,
self.IDlookup['Ez'], -1, 1, 1,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsE,
self.ID,
self.n_boundary_cells,
0,
self.nwy + 1,
self.nwz,
self.nwx,
2,
self.Ez,
precursors.hy_left,
precursors.hy_right,
self.IDlookup["Ez"],
-1,
1,
1,
config.get_model_config().ompthreads,
)
# Front and back
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID,
self.n_boundary_cells, 0, self.nwx, self.nwz + 1, self.nwy, 3,
self.Ex, precursors.hz_front, precursors.hz_back,
self.IDlookup['Ex'], -1, 1, 2,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsE,
self.ID,
self.n_boundary_cells,
0,
self.nwx,
self.nwz + 1,
self.nwy,
3,
self.Ex,
precursors.hz_front,
precursors.hz_back,
self.IDlookup["Ex"],
-1,
1,
2,
config.get_model_config().ompthreads,
)
update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID,
self.n_boundary_cells, 0, self.nwx + 1, self.nwz, self.nwy, 3,
self.Ez, precursors.hx_front, precursors.hx_back,
self.IDlookup['Ez'], 1, -1, 2,
config.get_model_config().ompthreads)
update_is(
self.nwx,
self.nwy,
self.nwz,
self.updatecoeffsE,
self.ID,
self.n_boundary_cells,
0,
self.nwx + 1,
self.nwz,
self.nwy,
3,
self.Ez,
precursors.hx_front,
precursors.hx_back,
self.IDlookup["Ez"],
1,
-1,
2,
config.get_model_config().ompthreads,
)
def update_electric_os(self, main_grid):
"""
@@ -155,49 +345,151 @@ class SubGridHSG(SubGridBaseGrid):
# Ez = c0(Ez) + c1(dHy) - c2(dHx)
# Front and back
update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 3,
i_l, i_u, k_l, k_u + 1, j_l, j_u, self.nwy,
main_grid.IDlookup['Ex'], main_grid.Ex, self.Hz,
2, 1, -1, 1, self.ratio, self.is_os_sep,
update_electric_os(
main_grid.updatecoeffsE,
main_grid.ID,
3,
i_l,
i_u,
k_l,
k_u + 1,
j_l,
j_u,
self.nwy,
main_grid.IDlookup["Ex"],
main_grid.Ex,
self.Hz,
2,
1,
-1,
1,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 3,
i_l, i_u + 1, k_l, k_u, j_l, j_u, self.nwy,
main_grid.IDlookup['Ez'], main_grid.Ez, self.Hx,
2, -1, 1, 0, self.ratio, self.is_os_sep,
update_electric_os(
main_grid.updatecoeffsE,
main_grid.ID,
3,
i_l,
i_u + 1,
k_l,
k_u,
j_l,
j_u,
self.nwy,
main_grid.IDlookup["Ez"],
main_grid.Ez,
self.Hx,
2,
-1,
1,
0,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
# Left and right
update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 2,
j_l, j_u, k_l, k_u + 1, i_l, i_u, self.nwx,
main_grid.IDlookup['Ey'], main_grid.Ey, self.Hz,
1, -1, 1, 1, self.ratio, self.is_os_sep,
update_electric_os(
main_grid.updatecoeffsE,
main_grid.ID,
2,
j_l,
j_u,
k_l,
k_u + 1,
i_l,
i_u,
self.nwx,
main_grid.IDlookup["Ey"],
main_grid.Ey,
self.Hz,
1,
-1,
1,
1,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 2,
j_l, j_u + 1, k_l, k_u, i_l, i_u, self.nwx,
main_grid.IDlookup['Ez'], main_grid.Ez, self.Hy,
1, 1, -1, 0, self.ratio, self.is_os_sep,
update_electric_os(
main_grid.updatecoeffsE,
main_grid.ID,
2,
j_l,
j_u + 1,
k_l,
k_u,
i_l,
i_u,
self.nwx,
main_grid.IDlookup["Ez"],
main_grid.Ez,
self.Hy,
1,
1,
-1,
0,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
# Bottom and top
update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 1,
i_l, i_u, j_l, j_u + 1, k_l, k_u, self.nwz,
main_grid.IDlookup['Ex'], main_grid.Ex, self.Hy,
3, -1, 1, 1, self.ratio, self.is_os_sep,
update_electric_os(
main_grid.updatecoeffsE,
main_grid.ID,
1,
i_l,
i_u,
j_l,
j_u + 1,
k_l,
k_u,
self.nwz,
main_grid.IDlookup["Ex"],
main_grid.Ex,
self.Hy,
3,
-1,
1,
1,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 1,
i_l, i_u + 1, j_l, j_u, k_l, k_u, self.nwz,
main_grid.IDlookup['Ey'], main_grid.Ey, self.Hx,
3, 1, -1, 0, self.ratio, self.is_os_sep,
update_electric_os(
main_grid.updatecoeffsE,
main_grid.ID,
1,
i_l,
i_u + 1,
j_l,
j_u,
k_l,
k_u,
self.nwz,
main_grid.IDlookup["Ey"],
main_grid.Ey,
self.Hx,
3,
1,
-1,
0,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
def update_magnetic_os(self, main_grid):
"""
@@ -218,49 +510,151 @@ class SubGridHSG(SubGridBaseGrid):
# Hx = c0Hx - c2Ez + c3Ey
# Front and back
update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 3,
i_l, i_u, k_l, k_u + 1, j_l - 1, j_u, self.nwy,
main_grid.IDlookup['Hz'], main_grid.Hz, self.Ex,
2, 1, -1, 1, self.ratio, self.is_os_sep,
update_magnetic_os(
main_grid.updatecoeffsH,
main_grid.ID,
3,
i_l,
i_u,
k_l,
k_u + 1,
j_l - 1,
j_u,
self.nwy,
main_grid.IDlookup["Hz"],
main_grid.Hz,
self.Ex,
2,
1,
-1,
1,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 3,
i_l, i_u + 1, k_l, k_u, j_l - 1, j_u, self.nwy,
main_grid.IDlookup['Hx'], main_grid.Hx, self.Ez,
2, -1, 1, 0, self.ratio, self.is_os_sep,
update_magnetic_os(
main_grid.updatecoeffsH,
main_grid.ID,
3,
i_l,
i_u + 1,
k_l,
k_u,
j_l - 1,
j_u,
self.nwy,
main_grid.IDlookup["Hx"],
main_grid.Hx,
self.Ez,
2,
-1,
1,
0,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
# Left and right
update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 2,
j_l, j_u, k_l, k_u + 1, i_l - 1, i_u, self.nwx,
main_grid.IDlookup['Hz'], main_grid.Hz, self.Ey,
1, -1, 1, 1, self.ratio, self.is_os_sep,
update_magnetic_os(
main_grid.updatecoeffsH,
main_grid.ID,
2,
j_l,
j_u,
k_l,
k_u + 1,
i_l - 1,
i_u,
self.nwx,
main_grid.IDlookup["Hz"],
main_grid.Hz,
self.Ey,
1,
-1,
1,
1,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 2,
j_l, j_u + 1, k_l, k_u, i_l - 1, i_u, self.nwx,
main_grid.IDlookup['Hy'], main_grid.Hy, self.Ez,
1, 1, -1, 0, self.ratio, self.is_os_sep,
update_magnetic_os(
main_grid.updatecoeffsH,
main_grid.ID,
2,
j_l,
j_u + 1,
k_l,
k_u,
i_l - 1,
i_u,
self.nwx,
main_grid.IDlookup["Hy"],
main_grid.Hy,
self.Ez,
1,
1,
-1,
0,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
# Bottom and top
update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 1,
i_l, i_u, j_l, j_u + 1, k_l - 1, k_u, self.nwz,
main_grid.IDlookup['Hy'], main_grid.Hy, self.Ex,
3, -1, 1, 1, self.ratio, self.is_os_sep,
update_magnetic_os(
main_grid.updatecoeffsH,
main_grid.ID,
1,
i_l,
i_u,
j_l,
j_u + 1,
k_l - 1,
k_u,
self.nwz,
main_grid.IDlookup["Hy"],
main_grid.Hy,
self.Ex,
3,
-1,
1,
1,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 1,
i_l, i_u + 1, j_l, j_u, k_l - 1, k_u, self.nwz,
main_grid.IDlookup['Hx'], main_grid.Hx, self.Ey,
3, 1, -1, 0, self.ratio, self.is_os_sep,
update_magnetic_os(
main_grid.updatecoeffsH,
main_grid.ID,
1,
i_l,
i_u + 1,
j_l,
j_u,
k_l - 1,
k_u,
self.nwz,
main_grid.IDlookup["Hx"],
main_grid.Hx,
self.Ey,
3,
1,
-1,
0,
self.ratio,
self.is_os_sep,
self.n_boundary_cells,
config.get_model_config().ompthreads)
config.get_model_config().ompthreads,
)
def print_info(self):
"""Prints information about the subgrid.
@@ -278,20 +672,23 @@ 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))
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))
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)
)
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)
)
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}] 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)')
logger.debug(f'[{self.name}] Total region: {self.nx:d} x {self.ny:d} x {self.nz:d} = ' +
f'{(self.nx * self.ny * self.nz):g} cells')
logger.info(f'[{self.name}] Time step: {self.dt:g} secs')
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}] 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)"
)
logger.debug(
f"[{self.name}] Total region: {self.nx:d} x {self.ny:d} x {self.nz:d} = "
+ f"{(self.nx * self.ny * self.nz):g} cells"
)
logger.info(f"[{self.name}] Time step: {self.dt:g} secs")

查看文件

@@ -36,7 +36,7 @@ def create_updates(G):
elif sg_type == SubGridHSG:
precursors = PrecursorNodes(G, sg)
else:
logger.exception(f'{str(sg)} is not a subgrid type')
logger.exception(f"{str(sg)} is not a subgrid type")
raise ValueError
sgu = SubgridUpdater(sg, precursors, G)

查看文件

@@ -45,7 +45,7 @@ class SubGridBase(UserObjectMulti):
elif isinstance(node, UserObjectGeometry):
self.children_geometry.append(node)
else:
logger.exception(f'{str(node)} this Object can not be added to a sub grid')
logger.exception(f"{str(node)} this Object can not be added to a sub grid")
raise ValueError
def set_discretisation(self, sg, grid):
@@ -64,7 +64,7 @@ class SubGridBase(UserObjectMulti):
sg.x2, sg.y2, sg.z2 = uip.round_to_grid(p2)
def set_name(self, sg):
sg.name = self.kwargs['id']
sg.name = self.kwargs["id"]
def set_working_region_cells(self, sg):
"""Number of cells in each dimension for the working region."""
@@ -84,8 +84,8 @@ class SubGridBase(UserObjectMulti):
def setup(self, sg, grid, uip):
""" "Common setup to both all subgrid types."""
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
p1 = self.kwargs["p1"]
p2 = self.kwargs["p2"]
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
@@ -97,7 +97,6 @@ class SubGridBase(UserObjectMulti):
if grid.dt_mod:
sg.dt = sg.dt * grid.dt_mod
# Set the indices related to the subgrids main grid placement
self.set_main_grid_indices(sg, uip, p1, p2)
@@ -127,12 +126,12 @@ class SubGridBase(UserObjectMulti):
self.subgrid = sg
# Copy over built in materials
sg.materials = [copy(m) for m in grid.materials if m.type == 'builtin']
sg.materials = [copy(m) for m in grid.materials if m.type == "builtin"]
# 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
@@ -162,34 +161,36 @@ class SubGridHSG(SubGridBase):
filter: boolean to turn on the 3-pole filter. Increases numerical
stability. Defaults to True.
"""
def __init__(self,
def __init__(
self,
p1=None,
p2=None,
ratio=3,
id='',
id="",
is_os_sep=3,
pml_separation=4,
subgrid_pml_thickness=6,
interpolation=1,
filter=True,
**kwargs):
**kwargs,
):
pml_separation = ratio // 2 + 2
# Copy over the optional parameters
kwargs['p1'] = p1
kwargs['p2'] = p2
kwargs['ratio'] = ratio
kwargs['id'] = id
kwargs['is_os_sep'] = is_os_sep
kwargs['pml_separation'] = pml_separation
kwargs['subgrid_pml_thickness'] = subgrid_pml_thickness
kwargs['interpolation'] = interpolation
kwargs['filter'] = filter
kwargs["p1"] = p1
kwargs["p2"] = p2
kwargs["ratio"] = ratio
kwargs["id"] = id
kwargs["is_os_sep"] = is_os_sep
kwargs["pml_separation"] = pml_separation
kwargs["subgrid_pml_thickness"] = subgrid_pml_thickness
kwargs["interpolation"] = interpolation
kwargs["filter"] = filter
super().__init__(**kwargs)
self.order = 18
self.hash = '#subgrid_hsg'
self.hash = "#subgrid_hsg"
def create(self, grid, uip):
sg = SubGridHSGUser(**self.kwargs)

文件差异内容过多而无法显示 加载差异

查看文件

@@ -63,17 +63,15 @@ class UserInput:
try:
self.grid.within_bounds(p)
except ValueError as err:
v = ['x', 'y', 'z']
v = ["x", "y", "z"]
# Discretisation
dl = getattr(self.grid, f'd{err.args[0]}')
dl = getattr(self.grid, f"d{err.args[0]}")
# Incorrect index
i = p[v.index(err.args[0])]
if name:
s = (f"\n'{cmd_str}' {err.args[0]} {name}-coordinate {i * dl:g} " +
"is not within the model domain")
s = f"\n'{cmd_str}' {err.args[0]} {name}-coordinate {i * dl:g} " + "is not within the model domain"
else:
s = (f"\n'{cmd_str}' {err.args[0]}-coordinate {i * dl:g} is not " +
"within the model domain")
s = f"\n'{cmd_str}' {err.args[0]}-coordinate {i * dl:g} is not " + "within the model domain"
logger.exception(s)
raise
@@ -99,36 +97,34 @@ class MainGridUserInput(UserInput):
def __init__(self, grid):
super().__init__(grid)
def check_point(self, p, cmd_str, name=''):
def check_point(self, p, cmd_str, name=""):
"""Discretises point and check its within the domain"""
p = self.discretise_point(p)
self.point_within_bounds(p, cmd_str, name)
return p
def check_src_rx_point(self, p, cmd_str, name=''):
def check_src_rx_point(self, p, cmd_str, name=""):
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
def check_box_points(self, p1, p2, cmd_str):
p1 = self.check_point(p1, cmd_str, name='lower')
p2 = self.check_point(p2, cmd_str, name='upper')
p1 = self.check_point(p1, cmd_str, name="lower")
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
def check_tri_points(self, p1, p2, p3, cmd_str):
p1 = self.check_point(p1, cmd_str, name='vertex_1')
p2 = self.check_point(p2, cmd_str, name='vertex_2')
p3 = self.check_point(p3, cmd_str, name='vertex_3')
p1 = self.check_point(p1, cmd_str, name="vertex_1")
p2 = self.check_point(p2, cmd_str, name="vertex_2")
p3 = self.check_point(p3, cmd_str, name="vertex_3")
return p1, p2, p3
@@ -155,12 +151,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)
self.outer_bound = np.subtract([grid.nx, grid.ny, grid.nz], self.inner_bound)
def translate_to_gap(self, p):
"""Translates the user input point to the real point in the subgrid."""
@@ -186,15 +179,13 @@ class SubgridUserInput(MainGridUserInput):
p_m = p_t * self.grid.dl
return p_m
def check_point(self, p, cmd_str, name=''):
def check_point(self, p, cmd_str, name=""):
p_t = super().check_point(p, cmd_str, name)
# 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):
@@ -206,5 +197,3 @@ class SubgridUserInput(MainGridUserInput):
"""Gets the index of a continuous point regardless of the point of
origin of the grid."""
return super().discretise_point(p) * self.grid.dl

查看文件

@@ -41,43 +41,51 @@ def get_host_info():
"""
# Default to 'unknown' if any of the detection fails
manufacturer = model = cpuID = sockets = threadspercore = 'unknown'
manufacturer = model = cpuID = sockets = threadspercore = "unknown"
# Windows
if sys.platform == 'win32':
if sys.platform == "win32":
# Manufacturer/model
try:
manufacturer = subprocess.check_output(["wmic", "csproduct", "get", "vendor"],
shell=False,
stderr=subprocess.STDOUT).decode('utf-8').strip()
manufacturer = manufacturer.split('\n')
manufacturer = (
subprocess.check_output(["wmic", "csproduct", "get", "vendor"], shell=False, stderr=subprocess.STDOUT)
.decode("utf-8")
.strip()
)
manufacturer = manufacturer.split("\n")
if len(manufacturer) > 1:
manufacturer = manufacturer[1]
else:
manufacturer = manufacturer[0]
model = subprocess.check_output(["wmic", "computersystem", "get", "model"],
shell=False,
stderr=subprocess.STDOUT).decode('utf-8').strip()
model = model.split('\n')
model = (
subprocess.check_output(
["wmic", "computersystem", "get", "model"], shell=False, stderr=subprocess.STDOUT
)
.decode("utf-8")
.strip()
)
model = model.split("\n")
if len(model) > 1:
model = model[1]
else:
model = model[0]
except subprocess.CalledProcessError:
pass
machineID = ' '.join(manufacturer.split()) + ' ' + ' '.join(model.split())
machineID = " ".join(manufacturer.split()) + " " + " ".join(model.split())
# CPU information
try:
allcpuinfo = subprocess.check_output(["wmic", "cpu", "get", "Name"],
shell=False,
stderr=subprocess.STDOUT).decode('utf-8').strip()
allcpuinfo = allcpuinfo.split('\n')
allcpuinfo = (
subprocess.check_output(["wmic", "cpu", "get", "Name"], shell=False, stderr=subprocess.STDOUT)
.decode("utf-8")
.strip()
)
allcpuinfo = allcpuinfo.split("\n")
sockets = 0
for line in allcpuinfo:
if 'CPU' in line:
if "CPU" in line:
cpuID = line.strip()
cpuID = ' '.join(cpuID.split())
cpuID = " ".join(cpuID.split())
sockets += 1
except subprocess.CalledProcessError:
pass
@@ -89,33 +97,42 @@ def get_host_info():
hyperthreading = False
# OS version
if platform.machine().endswith('64'):
osbit = ' (64-bit)'
if platform.machine().endswith("64"):
osbit = " (64-bit)"
else:
osbit = ' (32-bit)'
osversion = 'Windows ' + platform.release() + osbit
osbit = " (32-bit)"
osversion = "Windows " + platform.release() + osbit
# Mac OS X/macOS
elif sys.platform == 'darwin':
elif sys.platform == "darwin":
# Manufacturer/model
manufacturer = 'Apple'
manufacturer = "Apple"
try:
model = subprocess.check_output(["sysctl", "-n", "hw.model"], shell=False,
stderr=subprocess.STDOUT).decode('utf-8').strip()
model = (
subprocess.check_output(["sysctl", "-n", "hw.model"], shell=False, stderr=subprocess.STDOUT)
.decode("utf-8")
.strip()
)
except subprocess.CalledProcessError:
pass
machineID = ' '.join(manufacturer.split()) + ' ' + ' '.join(model.split())
machineID = " ".join(manufacturer.split()) + " " + " ".join(model.split())
# CPU information
try:
sockets = subprocess.check_output(["sysctl", "-n", "hw.packages"],
shell=False,
stderr=subprocess.STDOUT).decode('utf-8').strip()
sockets = (
subprocess.check_output(["sysctl", "-n", "hw.packages"], shell=False, stderr=subprocess.STDOUT)
.decode("utf-8")
.strip()
)
sockets = int(sockets)
cpuID = subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"],
shell=False,
stderr=subprocess.STDOUT).decode('utf-8').strip()
cpuID = ' '.join(cpuID.split())
cpuID = (
subprocess.check_output(
["sysctl", "-n", "machdep.cpu.brand_string"], shell=False, stderr=subprocess.STDOUT
)
.decode("utf-8")
.strip()
)
cpuID = " ".join(cpuID.split())
except subprocess.CalledProcessError:
pass
@@ -126,40 +143,50 @@ def get_host_info():
hyperthreading = False
# OS version
osversion = 'macOS (' + platform.mac_ver()[0] + ')'
osversion = "macOS (" + platform.mac_ver()[0] + ")"
# Linux
elif sys.platform == 'linux':
elif sys.platform == "linux":
# Manufacturer/model
try:
manufacturer = subprocess.check_output(["cat", "/sys/class/dmi/id/sys_vendor"],
shell=False,
stderr=subprocess.STDOUT).decode('utf-8').strip()
model = subprocess.check_output(["cat", "/sys/class/dmi/id/product_name"],
shell=False,
stderr=subprocess.STDOUT).decode('utf-8').strip()
manufacturer = (
subprocess.check_output(["cat", "/sys/class/dmi/id/sys_vendor"], shell=False, stderr=subprocess.STDOUT)
.decode("utf-8")
.strip()
)
model = (
subprocess.check_output(
["cat", "/sys/class/dmi/id/product_name"], shell=False, stderr=subprocess.STDOUT
)
.decode("utf-8")
.strip()
)
except subprocess.CalledProcessError:
pass
machineID = ' '.join(manufacturer.split()) + ' ' + ' '.join(model.split())
machineID = " ".join(manufacturer.split()) + " " + " ".join(model.split())
# CPU information
try:
# Locale to ensure English
myenv = {**os.environ, 'LANG': 'en_US.utf8'}
cpuIDinfo = subprocess.check_output(["cat", "/proc/cpuinfo"], shell=False,
stderr=subprocess.STDOUT,
env=myenv).decode('utf-8').strip()
for line in cpuIDinfo.split('\n'):
if re.search('model name', line):
cpuID = re.sub('.*model name.*:', '', line, 1).strip()
cpuID = ' '.join(cpuID.split())
allcpuinfo = subprocess.check_output(["lscpu"], shell=False,
stderr=subprocess.STDOUT,
env=myenv).decode('utf-8').strip()
for line in allcpuinfo.split('\n'):
if 'Socket(s)' in line:
myenv = {**os.environ, "LANG": "en_US.utf8"}
cpuIDinfo = (
subprocess.check_output(["cat", "/proc/cpuinfo"], shell=False, stderr=subprocess.STDOUT, env=myenv)
.decode("utf-8")
.strip()
)
for line in cpuIDinfo.split("\n"):
if re.search("model name", line):
cpuID = re.sub(".*model name.*:", "", line, 1).strip()
cpuID = " ".join(cpuID.split())
allcpuinfo = (
subprocess.check_output(["lscpu"], shell=False, stderr=subprocess.STDOUT, env=myenv)
.decode("utf-8")
.strip()
)
for line in allcpuinfo.split("\n"):
if "Socket(s)" in line:
sockets = int(line.strip()[-1])
if 'Thread(s) per core' in line:
if "Thread(s) per core" in line:
threadspercore = int(line.strip()[-1])
except subprocess.CalledProcessError:
pass
@@ -172,25 +199,25 @@ def get_host_info():
# Dictionary of host information
hostinfo = {}
hostinfo['hostname'] = platform.node()
hostinfo['machineID'] = machineID.strip()
hostinfo['sockets'] = sockets
hostinfo['cpuID'] = cpuID
hostinfo['osversion'] = osversion
hostinfo['hyperthreading'] = hyperthreading
hostinfo['logicalcores'] = psutil.cpu_count()
hostinfo["hostname"] = platform.node()
hostinfo["machineID"] = machineID.strip()
hostinfo["sockets"] = sockets
hostinfo["cpuID"] = cpuID
hostinfo["osversion"] = osversion
hostinfo["hyperthreading"] = hyperthreading
hostinfo["logicalcores"] = psutil.cpu_count()
try:
# Get number of physical CPU cores, i.e. avoid hyperthreading with OpenMP
hostinfo['physicalcores'] = psutil.cpu_count(logical=False)
hostinfo["physicalcores"] = psutil.cpu_count(logical=False)
except ValueError:
hostinfo['physicalcores'] = hostinfo['logicalcores']
hostinfo["physicalcores"] = hostinfo["logicalcores"]
# Handle case where cpu_count returns None on some machines
if not hostinfo['physicalcores']:
hostinfo['physicalcores'] = hostinfo['logicalcores']
if not hostinfo["physicalcores"]:
hostinfo["physicalcores"] = hostinfo["logicalcores"]
hostinfo['ram'] = psutil.virtual_memory().total
hostinfo["ram"] = psutil.virtual_memory().total
return hostinfo
@@ -204,14 +231,19 @@ def print_host_info(hostinfo):
version of operating system.
"""
hyperthreadingstr = (f", {config.sim_config.hostinfo['logicalcores']} "
f"cores with Hyper-Threading" if config.sim_config.hostinfo['hyperthreading'] else '')
logger.basic(f"\n{config.sim_config.hostinfo['hostname']} | "
hyperthreadingstr = (
f", {config.sim_config.hostinfo['logicalcores']} " f"cores with Hyper-Threading"
if config.sim_config.hostinfo["hyperthreading"]
else ""
)
logger.basic(
f"\n{config.sim_config.hostinfo['hostname']} | "
f"{config.sim_config.hostinfo['machineID']} | "
f"{hostinfo['sockets']} x {hostinfo['cpuID']} "
f"({hostinfo['physicalcores']} cores{hyperthreadingstr}) | "
f"{humanize.naturalsize(hostinfo['ram'], True)} | "
f"{hostinfo['osversion']}")
f"{hostinfo['osversion']}"
)
logger.basic(f"|--->OpenMP: {hostinfo['physicalcores']} threads")
@@ -222,42 +254,42 @@ def set_omp_threads(nthreads=None):
nthreads: int for number of OpenMP threads.
"""
if sys.platform == 'darwin':
if sys.platform == "darwin":
# Should waiting threads consume CPU power (can drastically effect
# performance)
if 'Apple' in config.sim_config.hostinfo['cpuID']:
if "Apple" in config.sim_config.hostinfo["cpuID"]:
# https://developer.apple.com/documentation/apple-silicon/tuning-your-code-s-performance-for-apple-silicon
os.environ['OMP_WAIT_POLICY'] = 'PASSIVE'
os.environ["OMP_WAIT_POLICY"] = "PASSIVE"
else:
os.environ['OMP_WAIT_POLICY'] = 'ACTIVE'
os.environ["OMP_WAIT_POLICY"] = "ACTIVE"
# Number of threads may be adjusted by the run time environment to best
# utilize system resources
os.environ['OMP_DYNAMIC'] = 'FALSE'
os.environ["OMP_DYNAMIC"] = "FALSE"
# Each place corresponds to a single core (having one or more hardware threads)
os.environ['OMP_PLACES'] = 'cores'
os.environ["OMP_PLACES"] = "cores"
# Bind threads to physical cores
os.environ['OMP_PROC_BIND'] = 'TRUE'
os.environ["OMP_PROC_BIND"] = "TRUE"
# Prints OMP version and environment variables (useful for debug)
# os.environ['OMP_DISPLAY_ENV'] = 'TRUE'
# Catch bug with Windows Subsystem for Linux (https://github.com/Microsoft/BashOnWindows/issues/785)
if 'Microsoft' in config.sim_config.hostinfo['osversion']:
os.environ['KMP_AFFINITY'] = 'disabled'
del os.environ['OMP_PLACES']
del os.environ['OMP_PROC_BIND']
if "Microsoft" in config.sim_config.hostinfo["osversion"]:
os.environ["KMP_AFFINITY"] = "disabled"
del os.environ["OMP_PLACES"]
del os.environ["OMP_PROC_BIND"]
if nthreads:
os.environ['OMP_NUM_THREADS'] = str(nthreads)
elif os.environ.get('OMP_NUM_THREADS'):
nthreads = int(os.environ.get('OMP_NUM_THREADS'))
os.environ["OMP_NUM_THREADS"] = str(nthreads)
elif os.environ.get("OMP_NUM_THREADS"):
nthreads = int(os.environ.get("OMP_NUM_THREADS"))
else:
# Set number of threads to number of physical CPU cores
nthreads = config.sim_config.hostinfo['physicalcores']
os.environ['OMP_NUM_THREADS'] = str(nthreads)
nthreads = config.sim_config.hostinfo["physicalcores"]
os.environ["OMP_NUM_THREADS"] = str(nthreads)
return nthreads
@@ -268,10 +300,12 @@ def mem_check_host(mem):
Args:
mem: int for memory required (bytes).
"""
if mem > config.sim_config.hostinfo['ram']:
logger.exception(f"Memory (RAM) required ~{humanize.naturalsize(mem)} exceeds "
if mem > config.sim_config.hostinfo["ram"]:
logger.exception(
f"Memory (RAM) required ~{humanize.naturalsize(mem)} exceeds "
f"{humanize.naturalsize(config.sim_config.hostinfo['ram'], True)} "
"detected!\n")
"detected!\n"
)
raise ValueError
@@ -284,21 +318,23 @@ def mem_check_device_snaps(total_mem, snaps_mem):
snaps_mem: int for memory required for all snapshots (bytes).
"""
if config.sim_config.general['solver'] == 'cuda':
device_mem = config.get_model_config().device['dev'].total_memory()
elif config.sim_config.general['solver'] == 'opencl':
device_mem = config.get_model_config().device['dev'].global_mem_size
if config.sim_config.general["solver"] == "cuda":
device_mem = config.get_model_config().device["dev"].total_memory()
elif config.sim_config.general["solver"] == "opencl":
device_mem = config.get_model_config().device["dev"].global_mem_size
if total_mem - snaps_mem > device_mem:
logger.exception(f"Memory (RAM) required ~{humanize.naturalsize(total_mem)} exceeds "
logger.exception(
f"Memory (RAM) required ~{humanize.naturalsize(total_mem)} exceeds "
f"{humanize.naturalsize(device_mem, True)} "
f"detected on specified {' '.join(config.get_model_config().device['dev'].name.split())} device!\n")
f"detected on specified {' '.join(config.get_model_config().device['dev'].name.split())} device!\n"
)
raise ValueError
# If the required memory without the snapshots will fit on the GPU then
# transfer and store snaphots on host
if snaps_mem != 0 and total_mem - snaps_mem < device_mem:
config.get_model_config().device['snapsgpu2cpu'] = True
config.get_model_config().device["snapsgpu2cpu"] = True
def mem_check_run_all(grids):
@@ -328,7 +364,7 @@ def mem_check_run_all(grids):
grid.mem_use += grid.mem_est_basic()
# Additional memory required if there are any dispersive materials.
if config.get_model_config().materials['maxpoles'] != 0:
if config.get_model_config().materials["maxpoles"] != 0:
config.get_model_config().mem_use += grid.mem_est_dispersive()
grid.mem_use += grid.mem_est_dispersive()
@@ -339,7 +375,7 @@ def mem_check_run_all(grids):
grid.mem_use += snap.nbytes
total_mem_snaps += snap.nbytes
mem_strs.append(f'~{humanize.naturalsize(grid.mem_use)} [{grid.name}]')
mem_strs.append(f"~{humanize.naturalsize(grid.mem_use)} [{grid.name}]")
total_mem_model = config.get_model_config().mem_use
@@ -347,8 +383,11 @@ def mem_check_run_all(grids):
mem_check_host(total_mem_model)
# Check if there is sufficient memory for any snapshots on GPU
if (total_mem_snaps > 0 and config.sim_config.general['solver'] == 'cuda' or
config.sim_config.general['solver'] == 'opencl'):
if (
total_mem_snaps > 0
and config.sim_config.general["solver"] == "cuda"
or config.sim_config.general["solver"] == "opencl"
):
mem_check_device_snaps(total_mem_model, total_mem_snaps)
return total_mem_model, mem_strs
@@ -381,7 +420,7 @@ def mem_check_build_all(grids):
if grid.fractalvolumes:
grid_mem += grid.mem_est_fractals()
mem_strs.append(f'~{humanize.naturalsize(grid_mem)} [{grid.name}]')
mem_strs.append(f"~{humanize.naturalsize(grid_mem)} [{grid.name}]")
total_mem_model += grid_mem
# Check if there is sufficient memory on host
@@ -389,6 +428,7 @@ def mem_check_build_all(grids):
return total_mem_model, mem_strs
def has_pycuda():
"""Checks if pycuda module is installed."""
pycuda = True
@@ -419,22 +459,25 @@ def detect_cuda_gpus():
gpus = {}
cuda_reqs = ('To use gprMax with CUDA you must:'
'\n 1) install pycuda'
'\n 2) install NVIDIA CUDA Toolkit (https://developer.nvidia.com/cuda-toolkit)'
'\n 3) have an NVIDIA CUDA-Enabled GPU (https://developer.nvidia.com/cuda-gpus)')
cuda_reqs = (
"To use gprMax with CUDA you must:"
"\n 1) install pycuda"
"\n 2) install NVIDIA CUDA Toolkit (https://developer.nvidia.com/cuda-toolkit)"
"\n 3) have an NVIDIA CUDA-Enabled GPU (https://developer.nvidia.com/cuda-gpus)"
)
if has_pycuda():
import pycuda.driver as drv
drv.init()
# Check and list any CUDA-Enabled GPUs
deviceIDsavail = []
if drv.Device.count() == 0:
logger.warning('No NVIDIA CUDA-Enabled GPUs detected!\n' + cuda_reqs)
elif 'CUDA_VISIBLE_DEVICES' in os.environ:
deviceIDsavail = os.environ.get('CUDA_VISIBLE_DEVICES')
deviceIDsavail = [int(s) for s in deviceIDsavail.split(',')]
logger.warning("No NVIDIA CUDA-Enabled GPUs detected!\n" + cuda_reqs)
elif "CUDA_VISIBLE_DEVICES" in os.environ:
deviceIDsavail = os.environ.get("CUDA_VISIBLE_DEVICES")
deviceIDsavail = [int(s) for s in deviceIDsavail.split(",")]
else:
deviceIDsavail = range(drv.Device.count())
@@ -443,7 +486,7 @@ def detect_cuda_gpus():
gpus[ID] = drv.Device(ID)
else:
logger.warning('pycuda not detected!\n' + cuda_reqs)
logger.warning("pycuda not detected!\n" + cuda_reqs)
return gpus
@@ -458,12 +501,14 @@ def print_cuda_info(devs):
import pycuda
logger.basic('|--->CUDA:')
logger.debug(f'PyCUDA: {pycuda.VERSION_TEXT}')
logger.basic("|--->CUDA:")
logger.debug(f"PyCUDA: {pycuda.VERSION_TEXT}")
for ID, gpu in devs.items():
logger.basic(f" |--->Device {ID}: {' '.join(gpu.name().split())} | "
f"{humanize.naturalsize(gpu.total_memory(), True)}")
logger.basic(
f" |--->Device {ID}: {' '.join(gpu.name().split())} | "
f"{humanize.naturalsize(gpu.total_memory(), True)}"
)
def detect_opencl():
@@ -476,13 +521,16 @@ def detect_opencl():
devs = {}
ocl_reqs = ('To use gprMax with OpenCL you must:'
'\n 1) install pyopencl'
'\n 2) install appropriate OpenCL device driver(s)'
'\n 3) have at least one OpenCL-capable platform.')
ocl_reqs = (
"To use gprMax with OpenCL you must:"
"\n 1) install pyopencl"
"\n 2) install appropriate OpenCL device driver(s)"
"\n 3) have at least one OpenCL-capable platform."
)
if has_pyopencl():
import pyopencl as cl
try:
i = 0
for platform in cl.get_platforms():
@@ -490,10 +538,10 @@ def detect_opencl():
devs[i] = device
i += 1
except:
logger.warning('No OpenCL-capable platforms detected!\n' + ocl_reqs)
logger.warning("No OpenCL-capable platforms detected!\n" + ocl_reqs)
else:
logger.warning('pyopencl not detected!\n' + ocl_reqs)
logger.warning("pyopencl not detected!\n" + ocl_reqs)
return devs
@@ -508,19 +556,21 @@ def print_opencl_info(devs):
import pyopencl as cl
logger.basic('|--->OpenCL:')
logger.debug(f'PyOpenCL: {cl.VERSION_TEXT}')
logger.basic("|--->OpenCL:")
logger.debug(f"PyOpenCL: {cl.VERSION_TEXT}")
for i, (ID, dev) in enumerate(devs.items()):
if i == 0:
platform = dev.platform.name
logger.basic(f' |--->Platform: {platform}')
logger.basic(f" |--->Platform: {platform}")
if platform != dev.platform.name:
logger.basic(f' |--->Platform: {dev.platform.name}')
logger.basic(f" |--->Platform: {dev.platform.name}")
types = cl.device_type.to_string(dev.type)
if 'CPU' in types:
type = 'CPU'
if 'GPU' in types:
type = 'GPU'
logger.basic(f" |--->Device {ID}: {type} | {' '.join(dev.name.split())} | "
f"{humanize.naturalsize(dev.global_mem_size, True)}")
if "CPU" in types:
type = "CPU"
if "GPU" in types:
type = "GPU"
logger.basic(
f" |--->Device {ID}: {type} | {' '.join(dev.name.split())} | "
f"{humanize.naturalsize(dev.global_mem_size, True)}"
)

查看文件

@@ -28,23 +28,27 @@ logger = logging.getLogger(__name__)
BASIC_NUM = 25
logging.addLevelName(BASIC_NUM, "BASIC")
logging.BASIC = BASIC_NUM
def basic(self, message, *args, **kws):
if self.isEnabledFor(BASIC_NUM):
self._log(BASIC_NUM, message, args, **kws)
logging.Logger.basic = basic
# Colour mapping for different log levels
MAPPING = {
'DEBUG' : 37, # white
'BASIC' : 37, # white
'INFO' : 37, # white
'WARNING' : 33, # yellow
'ERROR' : 31, # red
'CRITICAL': 41, # white on red bg
"DEBUG": 37, # white
"BASIC": 37, # white
"INFO": 37, # white
"WARNING": 33, # yellow
"ERROR": 31, # red
"CRITICAL": 41, # white on red bg
}
PREFIX = '\033['
SUFFIX = '\033[0m'
PREFIX = "\033["
SUFFIX = "\033[0m"
class CustomFormatter(logging.Formatter):
@@ -58,13 +62,13 @@ class CustomFormatter(logging.Formatter):
colored_record = copy(record)
levelname = colored_record.levelname
seq = MAPPING.get(levelname, 37) # default white
colored_levelname = f'{PREFIX}{seq}m{levelname}{SUFFIX}'
colored_levelname = f"{PREFIX}{seq}m{levelname}{SUFFIX}"
colored_record.levelname = colored_levelname
colored_record.msg = f'{PREFIX}{seq}m{colored_record.getMessage()}{SUFFIX}'
colored_record.msg = f"{PREFIX}{seq}m{colored_record.getMessage()}{SUFFIX}"
return logging.Formatter.format(self, colored_record)
def logging_config(name='gprMax', level=logging.INFO, format_style='std', log_file=False):
def logging_config(name="gprMax", level=logging.INFO, format_style="std", log_file=False):
"""Setup and configure logging.
Args:
@@ -78,9 +82,9 @@ def logging_config(name='gprMax', level=logging.INFO, format_style='std', log_fi
format_full = "%(asctime)s:%(levelname)s:%(name)s:%(lineno)d: %(message)s"
# Set format style
if format_style == 'full' or level == logging.DEBUG:
if format_style == "full" or level == logging.DEBUG:
format = format_full
elif format_style == 'std':
elif format_style == "std":
format = format_std
# Create main top-level logger
@@ -92,15 +96,14 @@ def logging_config(name='gprMax', level=logging.INFO, format_style='std', log_fi
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(level)
handler.setFormatter(CustomFormatter(format))
if (logger.hasHandlers()):
if logger.hasHandlers():
logger.handlers.clear()
logger.addHandler(handler)
# Config for logging to file if required
if log_file:
filename = (name + '-log-' +
datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + '.txt')
handler = logging.FileHandler(filename, mode='w')
filename = name + "-log-" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + ".txt"
handler = logging.FileHandler(filename, mode="w")
formatter = logging.Formatter(format_full)
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)

查看文件

@@ -34,9 +34,12 @@ try:
from time import thread_time as timer_fn
except ImportError:
from time import perf_counter as timer_fn
logger.debug('"thread_time" not currently available in macOS and bug'
logger.debug(
'"thread_time" not currently available in macOS and bug'
' (https://bugs.python.org/issue36205) with "process_time", '
'so use "perf_counter".')
'so use "perf_counter".'
)
def get_terminal_width():
@@ -63,47 +66,53 @@ def logo(version):
str: string containing logo, version, and licencing/copyright info.
"""
description = ('\n=== Electromagnetic modelling software based on the '
'Finite-Difference Time-Domain (FDTD) method')
description = "\n=== Electromagnetic modelling software based on the " "Finite-Difference Time-Domain (FDTD) method"
current_year = datetime.datetime.now().year
copyright = (f'Copyright (C) 2015-{current_year}: The University of '
'Edinburgh, United Kingdom')
authors = 'Authors: Craig Warren, Antonis Giannopoulos, and John Hartley'
licenseinfo1 = ('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.\n')
licenseinfo2 = ('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.')
licenseinfo3 = ('You should have received a copy of the GNU General Public '
'License along with gprMax. If not, '
'see www.gnu.org/licenses.')
copyright = f"Copyright (C) 2015-{current_year}: The University of " "Edinburgh, United Kingdom"
authors = "Authors: Craig Warren, Antonis Giannopoulos, and John Hartley"
licenseinfo1 = (
"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.\n"
)
licenseinfo2 = (
"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."
)
licenseinfo3 = (
"You should have received a copy of the GNU General Public "
"License along with gprMax. If not, "
"see www.gnu.org/licenses."
)
logo = """ www.gprmax.com __ __
logo = (
""" www.gprmax.com __ __
__ _ _ __ _ __| \/ | __ ___ __
/ _` | '_ \| '__| |\/| |/ _` \ \/ /
| (_| | |_) | | | | | | (_| |> <
\__, | .__/|_| |_| |_|\__,_/_/\_\\
|___/|_|
v""" + version + '\n\n'
v"""
+ version
+ "\n\n"
)
str = f"{description} {'=' * (get_terminal_width() - len(description) - 1)}\n\n"
str += f'{Fore.CYAN}{logo}'
str += Style.RESET_ALL + textwrap.fill(copyright,
width=get_terminal_width() - 1,
initial_indent=' ') + '\n'
str += textwrap.fill(authors, width=get_terminal_width() - 1,
initial_indent=' ') + '\n\n'
str += textwrap.fill(licenseinfo1, width=get_terminal_width() - 1,
initial_indent=' ', subsequent_indent=' ') + '\n'
str += textwrap.fill(licenseinfo2, width=get_terminal_width() - 1,
initial_indent=' ', subsequent_indent=' ') + '\n'
str += textwrap.fill(licenseinfo3, width=get_terminal_width() - 1,
initial_indent=' ', subsequent_indent=' ')
str += f"{Fore.CYAN}{logo}"
str += Style.RESET_ALL + textwrap.fill(copyright, width=get_terminal_width() - 1, initial_indent=" ") + "\n"
str += textwrap.fill(authors, width=get_terminal_width() - 1, initial_indent=" ") + "\n\n"
str += (
textwrap.fill(licenseinfo1, width=get_terminal_width() - 1, initial_indent=" ", subsequent_indent=" ") + "\n"
)
str += (
textwrap.fill(licenseinfo2, width=get_terminal_width() - 1, initial_indent=" ", subsequent_indent=" ") + "\n"
)
str += textwrap.fill(licenseinfo3, width=get_terminal_width() - 1, initial_indent=" ", subsequent_indent=" ")
return str
@@ -122,14 +131,12 @@ def round_value(value, decimalplaces=0):
# Rounds to nearest integer (half values are rounded downwards)
if decimalplaces == 0:
rounded = int(d.Decimal(value).quantize(d.Decimal('1'),
rounding=d.ROUND_HALF_DOWN))
rounded = int(d.Decimal(value).quantize(d.Decimal("1"), rounding=d.ROUND_HALF_DOWN))
# Rounds down to nearest float represented by number of decimal places
else:
precision = f"1.{'0' * decimalplaces}"
rounded = float(d.Decimal(value).quantize(d.Decimal(precision),
rounding=d.ROUND_FLOOR))
rounded = float(d.Decimal(value).quantize(d.Decimal(precision), rounding=d.ROUND_FLOOR))
return rounded
@@ -154,7 +161,7 @@ def fft_power(waveform, dt):
# Calculate magnitude of frequency spectra of waveform (ignore warning from
# taking a log of any zero values)
with np.errstate(divide='ignore'):
with np.errstate(divide="ignore"):
power = 10 * np.log10(np.abs(np.fft.fft(waveform)) ** 2)
# Replace any NaNs or Infs from zero division
@@ -181,7 +188,7 @@ def atoi(text):
def natural_keys(text):
"""Human sorting of a string."""
return [atoi(c) for c in re.split(r'(\d+)', text)]
return [atoi(c) for c in re.split(r"(\d+)", text)]
def numeric_list_to_int_list(l):

查看文件

@@ -25,9 +25,20 @@ logger = logging.getLogger(__name__)
class Waveform:
"""Definitions of waveform shapes that can be used with sources."""
types = ['gaussian', 'gaussiandot', 'gaussiandotnorm', 'gaussiandotdot',
'gaussiandotdotnorm', 'gaussianprime', 'gaussiandoubleprime',
'ricker', 'sine', 'contsine', 'impulse', 'user']
types = [
"gaussian",
"gaussiandot",
"gaussiandotnorm",
"gaussiandotdot",
"gaussiandotdotnorm",
"gaussianprime",
"gaussiandoubleprime",
"ricker",
"sine",
"contsine",
"impulse",
"user",
]
# Information about specific waveforms:
#
@@ -54,14 +65,10 @@ 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']:
elif self.type in ["gaussiandotdot", "gaussiandotdotnorm", "ricker"]:
self.chi = np.sqrt(2) / self.freq
self.zeta = np.pi**2 * self.freq**2
@@ -79,56 +86,55 @@ class Waveform:
self.calculate_coefficients()
# Waveforms
if self.type == 'gaussian':
if self.type == "gaussian":
delay = time - self.chi
ampvalue = np.exp(-self.zeta * delay**2)
elif self.type in ['gaussiandot', 'gaussianprime']:
elif self.type in ["gaussiandot", "gaussianprime"]:
delay = time - self.chi
ampvalue = -2 * self.zeta * delay * np.exp(-self.zeta * delay**2)
elif self.type == 'gaussiandotnorm':
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
elif self.type in ['gaussiandotdot', 'gaussiandoubleprime']:
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':
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':
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)
ampvalue = -(
(2 * self.zeta * (2 * self.zeta * delay**2 - 1) * np.exp(-self.zeta * delay**2)) * normalise
)
elif self.type == 'sine':
elif self.type == "sine":
ampvalue = np.sin(2 * np.pi * self.freq * time)
if time * self.freq > 1:
ampvalue = 0
elif self.type == 'contsine':
elif self.type == "contsine":
rampamp = 0.25
ramp = rampamp * time * self.freq
ramp = min(ramp, 1)
ampvalue = ramp * np.sin(2 * np.pi * self.freq * time)
elif self.type == 'impulse':
elif self.type == "impulse":
# time < dt condition required to do impulsive magnetic dipole
if time == 0 or time < dt:
ampvalue = 1
elif time >= dt:
ampvalue = 0
elif self.type == 'user':
elif self.type == "user":
ampvalue = self.userfunc(time)
ampvalue *= self.amp

288
setup.py
查看文件

@@ -33,13 +33,12 @@ 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]
with open("gprMax/_version.py", "r") as fd:
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE)[1]
def build_dispersive_material_templates():
"""Function to generate Cython .pyx file for dispersive media update.
@@ -47,11 +46,13 @@ def build_dispersive_material_templates():
functions.
"""
iswin = (sys.platform == 'win32')
iswin = sys.platform == "win32"
env = Environment(loader = FileSystemLoader(os.path.join('gprMax', 'cython')), )
env = Environment(
loader=FileSystemLoader(os.path.join("gprMax", "cython")),
)
template = env.get_template('fields_updates_dispersive_template.jinja')
template = env.get_template("fields_updates_dispersive_template.jinja")
# Render dispersive template for different types
r = template.render(
@@ -59,234 +60,247 @@ def build_dispersive_material_templates():
# templates for Double precision and dispersive materials with
# real susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_double_real',
'name_b': 'update_electric_dispersive_multipole_B_double_real',
'name_a_1': 'update_electric_dispersive_1pole_A_double_real',
'name_b_1': 'update_electric_dispersive_1pole_B_double_real',
'field_type': 'double',
'dispersive_type': 'double',
'iswin': iswin
"name_a": "update_electric_dispersive_multipole_A_double_real",
"name_b": "update_electric_dispersive_multipole_B_double_real",
"name_a_1": "update_electric_dispersive_1pole_A_double_real",
"name_b_1": "update_electric_dispersive_1pole_B_double_real",
"field_type": "double",
"dispersive_type": "double",
"iswin": iswin,
},
# templates for Float precision and dispersive materials with
# real susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_float_real',
'name_b': 'update_electric_dispersive_multipole_B_float_real',
'name_a_1': 'update_electric_dispersive_1pole_A_float_real',
'name_b_1': 'update_electric_dispersive_1pole_B_float_real',
'field_type': 'float',
'dispersive_type': 'float',
'iswin': iswin
"name_a": "update_electric_dispersive_multipole_A_float_real",
"name_b": "update_electric_dispersive_multipole_B_float_real",
"name_a_1": "update_electric_dispersive_1pole_A_float_real",
"name_b_1": "update_electric_dispersive_1pole_B_float_real",
"field_type": "float",
"dispersive_type": "float",
"iswin": iswin,
},
# templates for Double precision and dispersive materials with
# complex susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_double_complex',
'name_b': 'update_electric_dispersive_multipole_B_double_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_double_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_double_complex',
'field_type': 'double',
'dispersive_type': 'double complex',
"name_a": "update_electric_dispersive_multipole_A_double_complex",
"name_b": "update_electric_dispersive_multipole_B_double_complex",
"name_a_1": "update_electric_dispersive_1pole_A_double_complex",
"name_b_1": "update_electric_dispersive_1pole_B_double_complex",
"field_type": "double",
"dispersive_type": "double complex",
# c function to take real part of complex double type
'real_part': 'creal',
'iswin': iswin
"real_part": "creal",
"iswin": iswin,
},
# templates for Float precision and dispersive materials with
# complex susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_float_complex',
'name_b': 'update_electric_dispersive_multipole_B_float_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_float_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_float_complex',
'field_type': 'float',
'dispersive_type': 'float complex',
"name_a": "update_electric_dispersive_multipole_A_float_complex",
"name_b": "update_electric_dispersive_multipole_B_float_complex",
"name_a_1": "update_electric_dispersive_1pole_A_float_complex",
"name_b_1": "update_electric_dispersive_1pole_B_float_complex",
"field_type": "float",
"dispersive_type": "float complex",
# c function to take real part of complex double type
'real_part': 'crealf',
'iswin': iswin
}]
"real_part": "crealf",
"iswin": iswin,
},
]
)
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)
# Generate Cython file for dispersive materials update functions
cython_disp_file = os.path.join('gprMax', 'cython', 'fields_updates_dispersive.pyx')
cython_disp_file = os.path.join("gprMax", "cython", "fields_updates_dispersive.pyx")
if not os.path.isfile(cython_disp_file):
build_dispersive_material_templates()
# Process 'build' command line argument
if 'build' in sys.argv:
if "build" in sys.argv:
print("Running 'build_ext --inplace'")
sys.argv.remove('build')
sys.argv.append('build_ext')
sys.argv.append('--inplace')
sys.argv.remove("build")
sys.argv.append("build_ext")
sys.argv.append("--inplace")
# Build a list of all the files that need to be Cythonized looking in gprMax
# directory
cythonfiles = []
for root, dirs, files in os.walk(os.path.join(os.getcwd(), 'gprMax'), topdown=True):
for root, dirs, files in os.walk(os.path.join(os.getcwd(), "gprMax"), topdown=True):
for file in files:
if file.endswith('.pyx'):
if file.endswith(".pyx"):
cythonfiles.append(os.path.relpath(os.path.join(root, file)))
# Process 'cleanall' command line argument
if 'cleanall' in sys.argv:
if "cleanall" in sys.argv:
for file in cythonfiles:
filebase = os.path.splitext(file)[0]
# Remove Cython C files
if os.path.isfile(f'{filebase}.c'):
if os.path.isfile(f"{filebase}.c"):
try:
os.remove(f'{filebase}.c')
print(f'Removed: {filebase}.c')
os.remove(f"{filebase}.c")
print(f"Removed: {filebase}.c")
except OSError:
print(f'Could not remove: {filebase}.c')
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:
os.remove(libfile)
print(f'Removed: {os.path.abspath(libfile)}')
print(f"Removed: {os.path.abspath(libfile)}")
except OSError:
print(f'Could not remove: {os.path.abspath(libfile)}')
print(f"Could not remove: {os.path.abspath(libfile)}")
# Remove build, dist, egg and __pycache__ directories
shutil.rmtree(Path.cwd().joinpath('build'), ignore_errors=True)
shutil.rmtree(Path.cwd().joinpath('dist'), ignore_errors=True)
shutil.rmtree(Path.cwd().joinpath('gprMax.egg-info'), ignore_errors=True)
for p in Path.cwd().rglob('__pycache__'):
shutil.rmtree(Path.cwd().joinpath("build"), ignore_errors=True)
shutil.rmtree(Path.cwd().joinpath("dist"), ignore_errors=True)
shutil.rmtree(Path.cwd().joinpath("gprMax.egg-info"), ignore_errors=True)
for p in Path.cwd().rglob("__pycache__"):
shutil.rmtree(p, ignore_errors=True)
print(f'Removed: {p}')
print(f"Removed: {p}")
# Remove 'gprMax/cython/fields_updates_dispersive.jinja' if its there
if os.path.isfile(cython_disp_file):
os.remove(cython_disp_file)
# Now do a normal clean
sys.argv[1] = 'clean' # this is what distutils understands
sys.argv[1] = "clean" # this is what distutils understands
else:
# Compiler options - Windows
if sys.platform == 'win32':
if sys.platform == "win32":
# No static linking as no static version of OpenMP library;
# /w disables warnings
compile_args = ['/O2', '/openmp', '/w']
compile_args = ["/O2", "/openmp", "/w"]
linker_args = []
libraries = []
elif sys.platform == 'darwin':
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).decode('utf-8').strip()
cpuID = ' '.join(cpuID.split())
if 'Apple' in cpuID:
gccpath = glob.glob('/opt/homebrew/bin/gcc-[4-9]*')
gccpath += glob.glob('/opt/homebrew/bin/gcc-[10-11]*')
cpuID = (
subprocess.check_output("sysctl -n machdep.cpu.brand_string", shell=True, stderr=subprocess.STDOUT)
.decode("utf-8")
.strip()
)
cpuID = " ".join(cpuID.split())
if "Apple" in cpuID:
gccpath = glob.glob("/opt/homebrew/bin/gcc-[4-9]*")
gccpath += glob.glob("/opt/homebrew/bin/gcc-[10-11]*")
if gccpath:
# Use newest gcc found
os.environ['CC'] = gccpath[-1].split(os.sep)[-1]
rpath = '/opt/homebrew/opt/gcc/lib/gcc/' + gccpath[-1].split(os.sep)[-1][-1] + '/'
os.environ["CC"] = gccpath[-1].split(os.sep)[-1]
rpath = "/opt/homebrew/opt/gcc/lib/gcc/" + gccpath[-1].split(os.sep)[-1][-1] + "/"
else:
raise('Cannot find gcc in /opt/homebrew/bin. gprMax requires gcc ' +
'to be installed - easily done through the Homebrew package ' +
'manager (http://brew.sh). Note: gcc with OpenMP support ' +
'is required.')
raise (
"Cannot find gcc in /opt/homebrew/bin. gprMax requires gcc "
+ "to be installed - easily done through the Homebrew package "
+ "manager (http://brew.sh). Note: gcc with OpenMP support "
+ "is required."
)
else:
gccpath = glob.glob('/usr/local/bin/gcc-[4-9]*')
gccpath += glob.glob('/usr/local/bin/gcc-[10-11]*')
gccpath = glob.glob("/usr/local/bin/gcc-[4-9]*")
gccpath += glob.glob("/usr/local/bin/gcc-[10-11]*")
if gccpath:
# Use newest gcc found
os.environ['CC'] = gccpath[-1].split(os.sep)[-1]
os.environ["CC"] = gccpath[-1].split(os.sep)[-1]
else:
raise('Cannot find gcc in /usr/local/bin. gprMax requires gcc ' +
'to be installed - easily done through the Homebrew package ' +
'manager (http://brew.sh). Note: gcc with OpenMP support ' +
'is required.')
raise (
"Cannot find gcc in /usr/local/bin. gprMax requires gcc "
+ "to be installed - easily done through the Homebrew package "
+ "manager (http://brew.sh). Note: gcc with OpenMP support "
+ "is required."
)
# Minimum supported macOS deployment target
MIN_MACOS_VERSION = '10.13'
MIN_MACOS_VERSION = "10.13"
try:
os.environ['MACOSX_DEPLOYMENT_TARGET']
del os.environ['MACOSX_DEPLOYMENT_TARGET']
os.environ["MACOSX_DEPLOYMENT_TARGET"]
del os.environ["MACOSX_DEPLOYMENT_TARGET"]
except:
pass
os.environ['MIN_SUPPORTED_MACOSX_DEPLOYMENT_TARGET'] = MIN_MACOS_VERSION
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}']
linker_args = ['-fopenmp', f'-mmacosx-version-min={MIN_MACOS_VERSION}']
libraries=['gomp']
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"]
elif sys.platform == 'linux':
compile_args = ['-O3', '-w', '-fopenmp', '-march=native']
linker_args = ['-fopenmp']
elif sys.platform == "linux":
compile_args = ["-O3", "-w", "-fopenmp", "-march=native"]
linker_args = ["-fopenmp"]
libraries = []
# Build list of all the extensions - Cython source files
extensions = []
for file in cythonfiles:
tmp = os.path.splitext(file)
extension = Extension(tmp[0].replace(os.sep, '.'),
extension = Extension(
tmp[0].replace(os.sep, "."),
[tmp[0] + tmp[1]],
language='c',
language="c",
include_dirs=[np.get_include()],
extra_compile_args=compile_args,
extra_link_args=linker_args,
libraries=libraries)
libraries=libraries,
)
extensions.append(extension)
# Cythonize - build .c files
extensions = cythonize(extensions,
compiler_directives={'boundscheck': False,
'wraparound': False,
'initializedcheck': False,
'embedsignature': True,
'language_level': 3},
extensions = cythonize(
extensions,
compiler_directives={
"boundscheck": False,
"wraparound": False,
"initializedcheck": False,
"embedsignature": True,
"language_level": 3,
},
nthreads=None,
annotate=False)
annotate=False,
)
# Parse long_description from README.rst file.
with open('README.rst','r') as fd:
with open("README.rst", "r") as fd:
long_description = fd.read()
setup(name='gprMax',
setup(
name="gprMax",
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',
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",
long_description=long_description,
long_description_content_type="text/x-rst",
license='GPLv3+',
python_requires=f'>{str(MIN_PYTHON_VERSION[0])}.{str(MIN_PYTHON_VERSION[1])}',
install_requires=['colorama',
'cython',
'h5py',
'jinja2',
'matplotlib',
'numpy',
'psutil',
'scipy',
'terminaltables',
'tqdm'],
license="GPLv3+",
python_requires=f">{str(MIN_PYTHON_VERSION[0])}.{str(MIN_PYTHON_VERSION[1])}",
install_requires=[
"colorama",
"cython",
"h5py",
"jinja2",
"matplotlib",
"numpy",
"psutil",
"scipy",
"terminaltables",
"tqdm",
],
ext_modules=extensions,
packages=find_packages(),
include_package_data=True,
include_dirs=[np.get_include()],
zip_safe=False,
classifiers=['Environment :: Console',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Operating System :: MacOS',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX :: Linux',
'Programming Language :: Cython',
'Programming Language :: Python :: 3',
'Topic :: Scientific/Engineering'],
classifiers=[
"Environment :: Console",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Operating System :: MacOS",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Programming Language :: Cython",
"Programming Language :: Python :: 3",
"Topic :: Scientific/Engineering",
],
)

查看文件

@@ -39,25 +39,25 @@ def hertzian_dipole_fs(iterations, dt, dxdydz, rx):
# Waveform
w = Waveform()
w.type = 'gaussianprime'
w.type = "gaussianprime"
w.amp = 1
w.freq = 1e9
# Waveform integral
wint = Waveform()
wint.type = 'gaussian'
wint.type = "gaussian"
wint.amp = w.amp
wint.freq = w.freq
# Waveform first derivative
wdot = Waveform()
wdot.type = 'gaussiandoubleprime'
wdot.type = "gaussiandoubleprime"
wdot.amp = w.amp
wdot.freq = w.freq
# Time
time = np.linspace(0, 1, iterations)
time *= (iterations * dt)
time *= iterations * dt
# Spatial resolution
dx = dxdydz[0]
@@ -77,42 +77,41 @@ def hertzian_dipole_fs(iterations, dt, dxdydz, rx):
Ex_y = y
Ex_z = z - 0.5 * dz
Er_x = np.sqrt((Ex_x**2 + Ex_y**2 + Ex_z**2))
tau_Ex = Er_x / config.sim_config.em_consts['c']
tau_Ex = Er_x / config.sim_config.em_consts["c"]
# Coordinates of Rx for Ey FDTD component
Ey_x = x
Ey_y = y + 0.5 * dy
Ey_z = z - 0.5 * dz
Er_y = np.sqrt((Ey_x**2 + Ey_y**2 + Ey_z**2))
tau_Ey = Er_y / config.sim_config.em_consts['c']
tau_Ey = Er_y / config.sim_config.em_consts["c"]
# Coordinates of Rx for Ez FDTD component
Ez_x = x
Ez_y = y
Ez_z = z
Er_z = np.sqrt((Ez_x**2 + Ez_y**2 + Ez_z**2))
tau_Ez = Er_z / config.sim_config.em_consts['c']
tau_Ez = Er_z / config.sim_config.em_consts["c"]
# Coordinates of Rx for Hx FDTD component
Hx_x = x
Hx_y = y + 0.5 * dy
Hx_z = z
Hr_x = np.sqrt((Hx_x**2 + Hx_y**2 + Hx_z**2))
tau_Hx = Hr_x / config.sim_config.em_consts['c']
tau_Hx = Hr_x / config.sim_config.em_consts["c"]
# Coordinates of Rx for Hy FDTD component
Hy_x = x + 0.5 * dx
Hy_y = y
Hy_z = z
Hr_y = np.sqrt((Hy_x**2 + Hy_y**2 + Hy_z**2))
tau_Hy = Hr_y / config.sim_config.em_consts['c']
tau_Hy = Hr_y / config.sim_config.em_consts["c"]
# Initialise fields
fields = np.zeros((iterations, 6))
# Calculate fields
for timestep in range(iterations):
# Calculate values for waveform, I * dl (current multiplied by dipole
# length) to match gprMax behaviour
fint_Ex = wint.calculate_value((timestep * dt) - tau_Ex, dt) * dl
@@ -134,21 +133,26 @@ def hertzian_dipole_fs(iterations, dt, dxdydz, rx):
fdot_Hy = wdot.calculate_value((timestep * dt) - tau_Hy, dt) * dl
# Ex
fields[timestep, 0] = (((Ex_x * Ex_z) / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_x**5)) *
(3 * (fint_Ex + (tau_Ex * f_Ex)) + (tau_Ex**2 * fdot_Ex)))
fields[timestep, 0] = ((Ex_x * Ex_z) / (4 * np.pi * config.sim_config.em_consts["e0"] * Er_x**5)) * (
3 * (fint_Ex + (tau_Ex * f_Ex)) + (tau_Ex**2 * fdot_Ex)
)
# Ey
try:
tmp = Ey_y / Ey_x
except ZeroDivisionError:
tmp = 0
fields[timestep, 1] = (tmp * ((Ey_x * Ey_z) / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_y**5)) *
(3 * (fint_Ey + (tau_Ey * f_Ey)) + (tau_Ey**2 * fdot_Ey)))
fields[timestep, 1] = (
tmp
* ((Ey_x * Ey_z) / (4 * np.pi * config.sim_config.em_consts["e0"] * Er_y**5))
* (3 * (fint_Ey + (tau_Ey * f_Ey)) + (tau_Ey**2 * fdot_Ey))
)
# Ez
fields[timestep, 2] = ((1 / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_z**5)) *
((2 * Ez_z**2 - (Ez_x**2 + Ez_y**2)) * (fint_Ez + (tau_Ez * f_Ez)) -
(Ez_x**2 + Ez_y**2) * tau_Ez**2 * fdot_Ez))
fields[timestep, 2] = (1 / (4 * np.pi * config.sim_config.em_consts["e0"] * Er_z**5)) * (
(2 * Ez_z**2 - (Ez_x**2 + Ez_y**2)) * (fint_Ez + (tau_Ez * f_Ez))
- (Ez_x**2 + Ez_y**2) * tau_Ez**2 * fdot_Ez
)
# Hx
fields[timestep, 3] = -(Hx_y / (4 * np.pi * Hr_x**3)) * (f_Hx + (tau_Hx * fdot_Hx))

查看文件

@@ -19,7 +19,6 @@ scenes = []
for d in domains:
for threads in ompthreads:
# Discretisation
dl = 0.001
@@ -30,14 +29,12 @@ for d in domains:
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name)
title = gprMax.Title(name=fn.with_suffix("").name)
domain = gprMax.Domain(p1=(x, y, z))
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')
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")
omp = gprMax.OMPThreads(n=threads)

查看文件

@@ -7,4 +7,3 @@
from user_libs.GPRAntennaModels import antenna_like_GSSI_1500
antenna_like_GSSI_1500(0.125, 0.094, 0.100)
#end_python:

查看文件

@@ -7,4 +7,3 @@
from user_libs.GPRAntennaModels import antenna_like_MALA_1200
antenna_like_MALA_1200(0.132, 0.095, 0.100)
#end_python:

查看文件

@@ -35,34 +35,38 @@ colorIDs = ["#79c72e", "#5774ff", "#ff7c2c", "#4b4e80", "#d7004e", "#007545", "#
colors = itertools.cycle(colorIDs)
# for i in range(2):
# next(colors)
lines = itertools.cycle(('--', ':', '-.', '-'))
markers = ['o', 'd', '^', 's', '*']
lines = itertools.cycle(("--", ":", "-.", "-"))
markers = ["o", "d", "^", "s", "*"]
parts = Path(__file__).parts
path = 'rxs/rx1/'
basename = 'pml_3D_pec_plate'
PMLIDs = ['CFS-PML', 'HORIPML-1', 'HORIPML-2', 'MRIPML-1', 'MRIPML-2']
path = "rxs/rx1/"
basename = "pml_3D_pec_plate"
PMLIDs = ["CFS-PML", "HORIPML-1", "HORIPML-2", "MRIPML-1", "MRIPML-2"]
maxerrors = []
testmodels = ['pml_3D_pec_plate_' + s for s in PMLIDs]
testmodels = ["pml_3D_pec_plate_" + s for s in PMLIDs]
fig, ax = plt.subplots(subplot_kw=dict(xlabel='Iterations', ylabel='Error [dB]'), figsize=(20, 10), facecolor='w', edgecolor='w')
fig, ax = plt.subplots(
subplot_kw=dict(xlabel="Iterations", ylabel="Error [dB]"), figsize=(20, 10), facecolor="w", edgecolor="w"
)
for x, model in enumerate(testmodels):
# Open output file and read iterations
fileref = h5py.File(Path(*parts[:-1], basename, basename + '_ref.h5'), 'r')
filetest = h5py.File(Path(*parts[:-1], basename, basename + str(x + 1) + '.h5'), 'r')
fileref = h5py.File(Path(*parts[:-1], basename, basename + "_ref.h5"), "r")
filetest = h5py.File(Path(*parts[:-1], basename, basename + str(x + 1) + ".h5"), "r")
# Get available field output component names
outputsref = list(fileref[path].keys())
outputstest = list(filetest[path].keys())
if outputsref != outputstest:
logger.exception('Field output components do not match reference solution')
logger.exception("Field output components do not match reference solution")
raise ValueError
# Check that type of float used to store fields matches
if filetest[path + outputstest[0]].dtype != fileref[path + outputsref[0]].dtype:
logger.warning(f'Type of floating point number in test model ({filetest[path + outputstest[0]].dtype}) '
f'does not match type in reference solution ({fileref[path + outputsref[0]].dtype})\n')
logger.warning(
f"Type of floating point number in test model ({filetest[path + outputstest[0]].dtype}) "
f"does not match type in reference solution ({fileref[path + outputsref[0]].dtype})\n"
)
floattyperef = fileref[path + outputsref[0]].dtype
floattypetest = filetest[path + outputstest[0]].dtype
# logger.info(f'Data type: {floattypetest}')
@@ -72,19 +76,19 @@ for x, model in enumerate(testmodels):
# timeref = np.linspace(0, (fileref.attrs['Iterations'] - 1) * fileref.attrs['dt'], num=fileref.attrs['Iterations']) / 1e-9
# timetest = np.zeros((filetest.attrs['Iterations']), dtype=floattypetest)
# timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'], num=filetest.attrs['Iterations']) / 1e-9
timeref = np.zeros((fileref.attrs['Iterations']), dtype=floattyperef)
timeref = np.linspace(0, (fileref.attrs['Iterations'] - 1), num=fileref.attrs['Iterations'])
timetest = np.zeros((filetest.attrs['Iterations']), dtype=floattypetest)
timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1), num=filetest.attrs['Iterations'])
timeref = np.zeros((fileref.attrs["Iterations"]), dtype=floattyperef)
timeref = np.linspace(0, (fileref.attrs["Iterations"] - 1), num=fileref.attrs["Iterations"])
timetest = np.zeros((filetest.attrs["Iterations"]), dtype=floattypetest)
timetest = np.linspace(0, (filetest.attrs["Iterations"] - 1), num=filetest.attrs["Iterations"])
# Arrays for storing field data
dataref = np.zeros((fileref.attrs['Iterations'], len(outputsref)), dtype=floattyperef)
datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), dtype=floattypetest)
dataref = np.zeros((fileref.attrs["Iterations"], len(outputsref)), dtype=floattyperef)
datatest = np.zeros((filetest.attrs["Iterations"], len(outputstest)), dtype=floattypetest)
for ID, name in enumerate(outputsref):
dataref[:, ID] = fileref[path + str(name)][:]
datatest[:, ID] = filetest[path + str(name)][:]
if np.any(np.isnan(datatest[:, ID])):
logger.exception('Test data contains NaNs')
logger.exception("Test data contains NaNs")
raise ValueError
fileref.close()
@@ -94,18 +98,20 @@ for x, model in enumerate(testmodels):
datadiffs = np.zeros(datatest.shape, dtype=np.float64)
for i in range(len(outputstest)):
maxi = np.amax(np.abs(dataref[:, i]))
datadiffs[:, i] = np.divide(np.abs(datatest[:, i] - dataref[:, i]), maxi, out=np.zeros_like(dataref[:, i]), where=maxi != 0) # Replace any division by zero with zero
datadiffs[:, i] = np.divide(
np.abs(datatest[:, i] - dataref[:, 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)
with np.errstate(divide='ignore'):
with np.errstate(divide="ignore"):
datadiffs[:, i] = 20 * np.log10(datadiffs[:, i])
# Replace any NaNs or Infs from zero division
datadiffs[:, i][np.invert(np.isfinite(datadiffs[:, i]))] = 0
# Print maximum error value
start = 210
maxerrors.append(f': {np.amax(datadiffs[start::, 1]):.1f} [dB]')
logger.info(f'{model}: Max. error {maxerrors[x]}')
maxerrors.append(f": {np.amax(datadiffs[start::, 1]):.1f} [dB]")
logger.info(f"{model}: Max. error {maxerrors[x]}")
# Plot diffs (select column to choose field component, 0-Ex, 1-Ey etc..)
ax.plot(timeref[start::], datadiffs[start::, 1], color=next(colors), lw=2, ls=next(lines), label=model)
@@ -114,12 +120,12 @@ for x, model in enumerate(testmodels):
ax.set_yticks(np.arange(-160, 0, step=20))
ax.set_ylim([-160, -20])
ax.set_axisbelow(True)
ax.grid(color=(0.75,0.75,0.75), linestyle='dashed')
ax.grid(color=(0.75, 0.75, 0.75), linestyle="dashed")
mylegend = list(map(add, PMLIDs, maxerrors))
legend = ax.legend(mylegend, loc=1, fontsize=14)
frame = legend.get_frame()
frame.set_edgecolor('white')
frame.set_edgecolor("white")
frame.set_alpha(0)
plt.show()

查看文件

@@ -20,112 +20,162 @@ 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),
gv1 = gprMax.GeometryView(
p1=(0, 0, 0),
p2=(x, y, z),
dl=(dl, dl, dl),
filename=Path(*parts[:-1], f'{parts[-1]}_n'),
output_type='n',)
gv2 = gprMax.GeometryView(p1=(0, 0, 0),
filename=Path(*parts[:-1], f"{parts[-1]}_n"),
output_type="n",
)
gv2 = gprMax.GeometryView(
p1=(0, 0, 0),
p2=(x, y, z),
dl=(dl, dl, dl),
filename=Path(*parts[:-1], f'{parts[-1]}_f'),
output_type='f',)
filename=Path(*parts[:-1], f"{parts[-1]}_f"),
output_type="f",
)
pmls = {'CFS-PML': {'pml': gprMax.PMLProps(formulation='HORIPML', thickness=10),
pmls = {
"CFS-PML": {
"pml": gprMax.PMLProps(formulation="HORIPML", thickness=10),
# Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864
'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='constant',
alphascalingdirection='forward',
alphamin=0.05, alphamax=0.05,
kappascalingprofile='quartic',
kappascalingdirection='forward',
kappamin=1, kappamax=8,
sigmascalingprofile='quartic',
sigmascalingdirection='forward',
"pml_cfs": [
gprMax.PMLCFS(
alphascalingprofile="constant",
alphascalingdirection="forward",
alphamin=0.05,
alphamax=0.05,
kappascalingprofile="quartic",
kappascalingdirection="forward",
kappamin=1,
kappamax=8,
sigmascalingprofile="quartic",
sigmascalingdirection="forward",
sigmamin=0,
sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)))]},
'HORIPML-1': {'pml': gprMax.PMLProps(formulation='HORIPML', thickness=10),
sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)),
)
],
},
"HORIPML-1": {
"pml": gprMax.PMLProps(formulation="HORIPML", thickness=10),
# Parameters from http://dx.doi.org/10.1109/TAP.2011.2180344
'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='constant',
alphascalingdirection='forward',
alphamin=0, alphamax=0,
kappascalingprofile='quartic',
kappascalingdirection='forward',
kappamin=1, kappamax=12,
sigmascalingprofile='quartic',
sigmascalingdirection='forward',
"pml_cfs": [
gprMax.PMLCFS(
alphascalingprofile="constant",
alphascalingdirection="forward",
alphamin=0,
alphamax=0,
kappascalingprofile="quartic",
kappascalingdirection="forward",
kappamin=1,
kappamax=12,
sigmascalingprofile="quartic",
sigmascalingdirection="forward",
sigmamin=0,
sigmamax=0.7 * ((4 + 1) / (150 * np.pi * dl)))]},
'HORIPML-2': {'pml': gprMax.PMLProps(formulation='HORIPML', thickness=10),
sigmamax=0.7 * ((4 + 1) / (150 * np.pi * dl)),
)
],
},
"HORIPML-2": {
"pml": gprMax.PMLProps(formulation="HORIPML", thickness=10),
# Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864
'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='constant',
alphascalingdirection='forward',
alphamin=0, alphamax=0,
kappascalingprofile='constant',
kappascalingdirection='forward',
kappamin=1, kappamax=1,
sigmascalingprofile='sextic',
sigmascalingdirection='forward',
"pml_cfs": [
gprMax.PMLCFS(
alphascalingprofile="constant",
alphascalingdirection="forward",
alphamin=0,
alphamax=0,
kappascalingprofile="constant",
kappascalingdirection="forward",
kappamin=1,
kappamax=1,
sigmascalingprofile="sextic",
sigmascalingdirection="forward",
sigmamin=0,
sigmamax=0.275 / (150 * np.pi * dl)),
gprMax.PMLCFS(alphascalingprofile='sextic',
alphascalingdirection='forward',
alphamin=0.07, alphamax=0.07 + (0.275 / (150 * np.pi * dl)),
kappascalingprofile='cubic',
kappascalingdirection='forward',
kappamin=1, kappamax=8,
sigmascalingprofile='quadratic',
sigmascalingdirection='forward',
sigmamax=0.275 / (150 * np.pi * dl),
),
gprMax.PMLCFS(
alphascalingprofile="sextic",
alphascalingdirection="forward",
alphamin=0.07,
alphamax=0.07 + (0.275 / (150 * np.pi * dl)),
kappascalingprofile="cubic",
kappascalingdirection="forward",
kappamin=1,
kappamax=8,
sigmascalingprofile="quadratic",
sigmascalingdirection="forward",
sigmamin=0,
sigmamax=2.75 / (150 * np.pi * dl))]},
'MRIPML-1': {'pml': gprMax.PMLProps(formulation='MRIPML', thickness=10),
sigmamax=2.75 / (150 * np.pi * dl),
),
],
},
"MRIPML-1": {
"pml": gprMax.PMLProps(formulation="MRIPML", thickness=10),
# Parameters from Antonis' MATLAB script (M3Dparams.m)
'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='constant',
alphascalingdirection='forward',
alphamin=0.05, alphamax=0.05,
kappascalingprofile='quartic',
kappascalingdirection='forward',
kappamin=1, kappamax=8,
sigmascalingprofile='quartic',
sigmascalingdirection='forward',
"pml_cfs": [
gprMax.PMLCFS(
alphascalingprofile="constant",
alphascalingdirection="forward",
alphamin=0.05,
alphamax=0.05,
kappascalingprofile="quartic",
kappascalingdirection="forward",
kappamin=1,
kappamax=8,
sigmascalingprofile="quartic",
sigmascalingdirection="forward",
sigmamin=0,
sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)))]},
'MRIPML-2': {'pml': gprMax.PMLProps(formulation='MRIPML', thickness=10),
sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)),
)
],
},
"MRIPML-2": {
"pml": gprMax.PMLProps(formulation="MRIPML", thickness=10),
# Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864
'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='quadratic',
alphascalingdirection='reverse',
alphamin=0, alphamax=0.15,
kappascalingprofile='quartic',
kappascalingdirection='forward',
kappamin=1, kappamax=12,
sigmascalingprofile='quartic',
sigmascalingdirection='forward',
"pml_cfs": [
gprMax.PMLCFS(
alphascalingprofile="quadratic",
alphascalingdirection="reverse",
alphamin=0,
alphamax=0.15,
kappascalingprofile="quartic",
kappascalingdirection="forward",
kappamin=1,
kappamax=12,
sigmascalingprofile="quartic",
sigmascalingdirection="forward",
sigmamin=0,
sigmamax=0.65 * ((4 + 1) / (150 * np.pi * dl))),
gprMax.PMLCFS(alphascalingprofile='linear',
alphascalingdirection='reverse',
alphamin=0.07, alphamax=0.8,
kappascalingprofile='constant',
kappascalingdirection='forward',
kappamin=0, kappamax=0,
sigmascalingprofile='quadratic',
sigmascalingdirection='forward',
sigmamax=0.65 * ((4 + 1) / (150 * np.pi * dl)),
),
gprMax.PMLCFS(
alphascalingprofile="linear",
alphascalingdirection="reverse",
alphamin=0.07,
alphamax=0.8,
kappascalingprofile="constant",
kappascalingdirection="forward",
kappamin=0,
kappamax=0,
sigmascalingprofile="quadratic",
sigmascalingdirection="forward",
sigmamin=0,
sigmamax=0.65 * ((2 + 1) / (150 * np.pi * dl)))]}
sigmamax=0.65 * ((2 + 1) / (150 * np.pi * dl)),
),
],
},
}
scenes = []
for k, v in pmls.items():
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name + '_' + k)
title = gprMax.Title(name=fn.with_suffix("").name + "_" + k)
scene.add(title)
scene.add(domain)
scene.add(dxdydz)
@@ -137,8 +187,8 @@ for k, v in pmls.items():
# scene.add(gv1)
# scene.add(gv2)
scene.add(v['pml'])
for pml_cfs in v['pml_cfs']:
scene.add(v["pml"])
for pml_cfs in v["pml_cfs"]:
scene.add(pml_cfs)
scenes.append(scene)

查看文件

@@ -20,42 +20,47 @@ 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),
gv1 = gprMax.GeometryView(
p1=(0, 0, 0),
p2=(x, y, z),
dl=(dl, dl, dl),
filename=Path(*parts[:-1], f'{parts[-1]}_n'),
output_type='n',)
gv2 = gprMax.GeometryView(p1=(0, 0, 0),
filename=Path(*parts[:-1], f"{parts[-1]}_n"),
output_type="n",
)
gv2 = gprMax.GeometryView(
p1=(0, 0, 0),
p2=(x, y, z),
dl=(dl, dl, dl),
filename=Path(*parts[:-1], f'{parts[-1]}_f'),
output_type='f',)
filename=Path(*parts[:-1], f"{parts[-1]}_f"),
output_type="f",
)
pml = gprMax.PMLProps(formulation='HORIPML', thickness=10)
pml = gprMax.PMLProps(formulation="HORIPML", thickness=10)
# Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864
pml_cfs = gprMax.PMLCFS(alphascalingprofile='constant',
alphascalingdirection='forward',
alphamin=0.05, alphamax=0.05,
kappascalingprofile='quartic',
kappascalingdirection='forward',
kappamin=1, kappamax=8,
sigmascalingprofile='quartic',
sigmascalingdirection='forward',
pml_cfs = gprMax.PMLCFS(
alphascalingprofile="constant",
alphascalingdirection="forward",
alphamin=0.05,
alphamax=0.05,
kappascalingprofile="quartic",
kappascalingdirection="forward",
kappamin=1,
kappamax=8,
sigmascalingprofile="quartic",
sigmascalingdirection="forward",
sigmamin=0,
sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)))
sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)),
)
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name + '_ref')
title = gprMax.Title(name=fn.with_suffix("").name + "_ref")
scene.add(title)
scene.add(domain)
scene.add(dxdydz)

查看文件

@@ -17,35 +17,40 @@ domain = gprMax.Domain(p1=(x, y, z))
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')
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")
rx = gprMax.Rx(p1=(0.070, 0.070, 0.070))
# PML cases
thick = 10 # thickness
cases = {'off': {'x0': 0, 'y0': 0, 'z0': 0, 'xmax': 0, 'ymax': 0, 'zmax':0},
'x0': {'x0': thick, 'y0': 0, 'z0': 0, 'xmax': 0, 'ymax': 0, 'zmax':0},
'y0': {'x0': 0, 'y0': thick, 'z0': 0, 'xmax': 0, 'ymax': 0, 'zmax':0},
'z0': {'x0': 0, 'y0': 0, 'z0': thick, 'xmax': 0, 'ymax': 0, 'zmax':0},
'xmax': {'x0': 0, 'y0': 0, 'z0': 0, 'xmax': thick, 'ymax': 0, 'zmax':0},
'ymax': {'x0': 0, 'y0': 0, 'z0': 0, 'xmax': 0, 'ymax': thick, 'zmax':0},
'zmax': {'x0': 0, 'y0': 0, 'z0': 0, 'xmax': 0, 'ymax': 0, 'zmax': thick}}
cases = {
"off": {"x0": 0, "y0": 0, "z0": 0, "xmax": 0, "ymax": 0, "zmax": 0},
"x0": {"x0": thick, "y0": 0, "z0": 0, "xmax": 0, "ymax": 0, "zmax": 0},
"y0": {"x0": 0, "y0": thick, "z0": 0, "xmax": 0, "ymax": 0, "zmax": 0},
"z0": {"x0": 0, "y0": 0, "z0": thick, "xmax": 0, "ymax": 0, "zmax": 0},
"xmax": {"x0": 0, "y0": 0, "z0": 0, "xmax": thick, "ymax": 0, "zmax": 0},
"ymax": {"x0": 0, "y0": 0, "z0": 0, "xmax": 0, "ymax": thick, "zmax": 0},
"zmax": {"x0": 0, "y0": 0, "z0": 0, "xmax": 0, "ymax": 0, "zmax": thick},
}
# PML formulation
pml_type = gprMax.PMLProps(formulation='HORIPML')
pml_type = gprMax.PMLProps(formulation="HORIPML")
## Built-in 1st order PML
pml_cfs = gprMax.PMLCFS(alphascalingprofile='constant',
alphascalingdirection='forward',
alphamin=0, alphamax=0,
kappascalingprofile='constant',
kappascalingdirection='forward',
kappamin=1, kappamax=1,
sigmascalingprofile='quartic',
sigmascalingdirection='forward',
sigmamin=0, sigmamax=None)
pml_cfs = gprMax.PMLCFS(
alphascalingprofile="constant",
alphascalingdirection="forward",
alphamin=0,
alphamax=0,
kappascalingprofile="constant",
kappascalingdirection="forward",
kappamin=1,
kappamax=1,
sigmascalingprofile="quartic",
sigmascalingdirection="forward",
sigmamin=0,
sigmamax=None,
)
## PMLs from http://dx.doi.org/10.1109/TAP.2011.2180344
## Standard PML
@@ -93,7 +98,7 @@ pml_cfs = gprMax.PMLCFS(alphascalingprofile='constant',
scenes = []
for k, v in cases.items():
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name + '_' + k)
title = gprMax.Title(name=fn.with_suffix("").name + "_" + k)
scene.add(title)
scene.add(domain)
scene.add(dxdydz)
@@ -102,7 +107,7 @@ for k, v in cases.items():
scene.add(hertzian_dipole)
scene.add(rx)
pml = gprMax.PMLProps(formulation='HORIPML', **v)
pml = gprMax.PMLProps(formulation="HORIPML", **v)
scene.add(pml)
scene.add(pml_cfs)

查看文件

@@ -15,7 +15,7 @@ z = 0.100
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name)
title = gprMax.Title(name=fn.with_suffix("").name)
domain = gprMax.Domain(p1=(x, y, z))
dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
time_window = gprMax.TimeWindow(time=30e-9)
@@ -29,44 +29,42 @@ bowtie_dims = (0.050, 0.100) # Length, height
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')
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"
)
scene.add(wave)
scene.add(tl)
# Bowtie - upper x half
t1 = gprMax.Triangle(p1=(tx_pos[0], tx_pos[1], tx_pos[2]),
p2=(tx_pos[0] + bowtie_dims[0] + 2 * dl,
tx_pos[1] - bowtie_dims[1]/2,
tx_pos[2]),
p3=(tx_pos[0] + bowtie_dims[0] + 2 * dl,
tx_pos[1] + bowtie_dims[1]/2,
tx_pos[2]),
thickness=0, material_id='pec')
t1 = gprMax.Triangle(
p1=(tx_pos[0], tx_pos[1], tx_pos[2]),
p2=(tx_pos[0] + bowtie_dims[0] + 2 * dl, tx_pos[1] - bowtie_dims[1] / 2, tx_pos[2]),
p3=(tx_pos[0] + bowtie_dims[0] + 2 * dl, tx_pos[1] + bowtie_dims[1] / 2, tx_pos[2]),
thickness=0,
material_id="pec",
)
# Bowtie - lower x half
t2 = gprMax.Triangle(p1=(tx_pos[0] + dl, tx_pos[1], tx_pos[2]),
p2=(tx_pos[0] - bowtie_dims[0],
tx_pos[1] - bowtie_dims[1]/2,
tx_pos[2]),
p3=(tx_pos[0] - bowtie_dims[0],
tx_pos[1] + bowtie_dims[1]/2,
tx_pos[2]),
thickness=0, material_id='pec')
t2 = gprMax.Triangle(
p1=(tx_pos[0] + dl, tx_pos[1], tx_pos[2]),
p2=(tx_pos[0] - bowtie_dims[0], tx_pos[1] - bowtie_dims[1] / 2, tx_pos[2]),
p3=(tx_pos[0] - bowtie_dims[0], tx_pos[1] + bowtie_dims[1] / 2, tx_pos[2]),
thickness=0,
material_id="pec",
)
scene.add(t1)
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),
dl=(dl, dl, dl), filename='antenna_bowtie_fs_pcb',
output_type='f')
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),
dl=(dl, dl, dl),
filename="antenna_bowtie_fs_pcb",
output_type="f",
)
scene.add(gv1)
# Run model

查看文件

@@ -31,49 +31,52 @@ 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.',
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('output', help='output to be plotted, i.e. Ex Ey Ez', nargs='+')
parser = argparse.ArgumentParser(
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("output", help="output to be plotted, i.e. Ex Ey Ez", nargs="+")
args = parser.parse_args()
modelfile = Path(args.modelfile)
realfile = Path(args.realfile)
# Model results
f = h5py.File(Path(modelfile), 'r')
path = '/rxs/rx1/'
f = h5py.File(Path(modelfile), "r")
path = "/rxs/rx1/"
availablecomponents = list(f[path].keys())
# Check for polarity of output and if requested output is in file
if args.output[0][0] == 'm':
if args.output[0][0] == "m":
polarity = -1
args.outputs[0] = args.output[0][1:]
else:
polarity = 1
if args.output[0] not in availablecomponents:
logger.exception(f"{args.output[0]} output requested to plot, but the " +
f"available output for receiver 1 is {', '.join(availablecomponents)}")
logger.exception(
f"{args.output[0]} output requested to plot, but the "
+ f"available output for receiver 1 is {', '.join(availablecomponents)}"
)
raise ValueError
floattype = f[path + args.output[0]].dtype
iterations = f.attrs['Iterations']
dt = f.attrs['dt']
iterations = f.attrs["Iterations"]
dt = f.attrs["dt"]
model = np.zeros(iterations, dtype=floattype)
model = f[path + args.output[0]][:] * polarity
model /= np.amax(np.abs(model))
timemodel = np.linspace(0, 1, iterations)
timemodel *= (iterations * dt)
timemodel *= iterations * dt
f.close()
# Find location of maximum value from model
modelmax = np.where(np.abs(model) == 1)[0][0]
# Real results
with open(realfile, 'r') as f:
with open(realfile, "r") as f:
real = np.loadtxt(f)
real[:, 1] = real[:, 1] / np.amax(np.abs(real[:, 1]))
realmax = np.where(np.abs(real[:, 1]) == 1)[0][0]
@@ -81,21 +84,23 @@ realmax = np.where(np.abs(real[:, 1]) == 1)[0][0]
difftime = -(timemodel[modelmax] - real[realmax, 0])
# Plot modelled and real data
fig, ax = plt.subplots(num=f'{modelfile.stem}_vs_{realfile.stem}',
fig, ax = plt.subplots(
num=f"{modelfile.stem}_vs_{realfile.stem}",
figsize=(20, 10),
facecolor='w',
edgecolor='w',)
ax.plot(timemodel + difftime, model, 'r', lw=2, label='Model')
ax.plot(real[:, 0], real[:, 1], 'r', ls='--', lw=2, label='Experiment')
ax.set_xlabel('Time [s]')
ax.set_ylabel('Amplitude')
facecolor="w",
edgecolor="w",
)
ax.plot(timemodel + difftime, model, "r", lw=2, label="Model")
ax.plot(real[:, 0], real[:, 1], "r", ls="--", lw=2, label="Experiment")
ax.set_xlabel("Time [s]")
ax.set_ylabel("Amplitude")
ax.set_xlim([0, timemodel[-1]])
# ax.set_ylim([-1, 1])
ax.legend()
ax.grid()
# Save a PDF/PNG of the figure
savename = f'{modelfile.stem}_vs_{realfile.stem}'
savename = f"{modelfile.stem}_vs_{realfile.stem}"
savename = modelfile.parent / savename
# fig.savefig(savename.with_suffix('.pdf'), dpi=None, format='pdf',
# bbox_inches='tight', pad_inches=0.1)

查看文件

@@ -29,8 +29,8 @@ from testing.analytical_solutions import hertzian_dipole_fs
logger = logging.getLogger(__name__)
if sys.platform == 'linux':
plt.switch_backend('agg')
if sys.platform == "linux":
plt.switch_backend("agg")
"""Compare field outputs
@@ -41,7 +41,7 @@ if sys.platform == 'linux':
"""
# Specify directoty with set of models to test
modelset = 'models_basic'
modelset = "models_basic"
# modelset += 'models_advanced'
# modelset += 'models_pmls'
@@ -49,9 +49,17 @@ basepath = Path(__file__).parents[0] / modelset
# List of available basic test models
testmodels = ['hertzian_dipole_fs_analytical', '2D_ExHyHz', '2D_EyHxHz', '2D_EzHxHy',
'cylinder_Ascan_2D', 'hertzian_dipole_fs', 'hertzian_dipole_hs',
'hertzian_dipole_dispersive', 'magnetic_dipole_fs']
testmodels = [
"hertzian_dipole_fs_analytical",
"2D_ExHyHz",
"2D_EyHxHz",
"2D_EzHxHy",
"cylinder_Ascan_2D",
"hertzian_dipole_fs",
"hertzian_dipole_hs",
"hertzian_dipole_dispersive",
"magnetic_dipole_fs",
]
# List of available advanced test models
# testmodels = ['antenna_GSSI_1500_fs', 'antenna_MALA_1200_fs']
@@ -63,97 +71,99 @@ testmodels = ['hertzian_dipole_fs_analytical', '2D_ExHyHz', '2D_EyHxHz', '2D_EzH
# testmodels = testmodels[:-1]
# testmodels = [testmodels[6]]
testresults = dict.fromkeys(testmodels)
path = '/rxs/rx1/'
path = "/rxs/rx1/"
# Minimum value of difference to plot (dB)
plotmin = -160
for i, model in enumerate(testmodels):
testresults[model] = {}
# Run model
file = basepath / model / model
gprMax.run(inputfile=file.with_suffix('.in'), gpu=None)
gprMax.run(inputfile=file.with_suffix(".in"), gpu=None)
# Special case for analytical comparison
if model == 'hertzian_dipole_fs_analytical':
if model == "hertzian_dipole_fs_analytical":
# Get output for model file
filetest = h5py.File(file.with_suffix('.h5'), 'r')
testresults[model]['Test version'] = filetest.attrs['gprMax']
filetest = h5py.File(file.with_suffix(".h5"), "r")
testresults[model]["Test version"] = filetest.attrs["gprMax"]
# Get available field output component names
outputstest = list(filetest[path].keys())
# 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']) / 1e-9
timetest = (
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])):
logger.exception('Test data contains NaNs')
logger.exception("Test data contains NaNs")
raise ValueError
# 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]))
rxpos = filetest[path].attrs["Position"]
txpos = filetest["/srcs/src1/"].attrs["Position"]
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)
dataref = hertzian_dipole_fs(
filetest.attrs["Iterations"], filetest.attrs["dt"], filetest.attrs["dx_dy_dz"], rxposrelative
)
else:
# Get output for model and reference files
fileref = f'{file.stem}_ref'
fileref = f"{file.stem}_ref"
fileref = file.parent / Path(fileref)
fileref = h5py.File(fileref.with_suffix('.h5'), 'r')
filetest = h5py.File(file.with_suffix('.h5'), 'r')
testresults[model]['Ref version'] = fileref.attrs['gprMax']
testresults[model]['Test version'] = filetest.attrs['gprMax']
fileref = h5py.File(fileref.with_suffix(".h5"), "r")
filetest = h5py.File(file.with_suffix(".h5"), "r")
testresults[model]["Ref version"] = fileref.attrs["gprMax"]
testresults[model]["Test version"] = filetest.attrs["gprMax"]
# Get available field output component names
outputsref = list(fileref[path].keys())
outputstest = list(filetest[path].keys())
if outputsref != outputstest:
logger.exception('Field output components do not match reference solution')
logger.exception("Field output components do not match reference solution")
raise ValueError
# Check that type of float used to store fields matches
if filetest[path + outputstest[0]].dtype != fileref[path + outputsref[0]].dtype:
logger.warning(f'Type of floating point number in test model ' +
f'({filetest[path + outputstest[0]].dtype}) does not ' +
f'match type in reference solution ({fileref[path + outputsref[0]].dtype})\n')
logger.warning(
f"Type of floating point number in test model "
+ f"({filetest[path + outputstest[0]].dtype}) does not "
+ f"match type in reference solution ({fileref[path + outputsref[0]].dtype})\n"
)
float_or_doubleref = fileref[path + outputsref[0]].dtype
float_or_doubletest = filetest[path + outputstest[0]].dtype
# 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']) / 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']) / 1e-9
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"])
/ 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"])
/ 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)][:]
if np.any(np.isnan(datatest[:, ID])):
logger.exception('Test data contains NaNs')
logger.exception("Test data contains NaNs")
raise ValueError
fileref.close()
@@ -163,43 +173,52 @@ for i, model in enumerate(testmodels):
datadiffs = np.zeros(datatest.shape, dtype=np.float64)
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) # Replace any division by zero with zero
datadiffs[:, i] = np.divide(
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)
with np.errstate(divide='ignore'):
with np.errstate(divide="ignore"):
datadiffs[:, i] = 20 * np.log10(datadiffs[:, i])
# Replace any NaNs or Infs from zero division
datadiffs[:, i][np.invert(np.isfinite(datadiffs[:, i]))] = 0
# Store max difference
maxdiff = np.amax(np.amax(datadiffs))
testresults[model]['Max diff'] = maxdiff
testresults[model]["Max diff"] = maxdiff
# Plot datasets
fig1, ((ex1, hx1), (ey1, hy1), (ez1, hz1)) = plt.subplots(nrows=3, ncols=2,
sharex=False, sharey='col',
subplot_kw=dict(xlabel='Time [ns]'),
num=model + '.in',
fig1, ((ex1, hx1), (ey1, hy1), (ez1, hz1)) = plt.subplots(
nrows=3,
ncols=2,
sharex=False,
sharey="col",
subplot_kw=dict(xlabel="Time [ns]"),
num=model + ".in",
figsize=(20, 10),
facecolor='w',
edgecolor='w')
ex1.plot(timetest, datatest[:, 0], 'r', lw=2, label=model)
ex1.plot(timeref, dataref[:, 0], 'g', lw=2, ls='--', label=f'{model}(Ref)')
ey1.plot(timetest, datatest[:, 1], 'r', lw=2, label=model)
ey1.plot(timeref, dataref[:, 1], 'g', lw=2, ls='--', label=f'{model}(Ref)')
ez1.plot(timetest, datatest[:, 2], 'r', lw=2, label=model)
ez1.plot(timeref, dataref[:, 2], 'g', lw=2, ls='--', label=f'{model}(Ref)')
hx1.plot(timetest, datatest[:, 3], 'r', lw=2, label=model)
hx1.plot(timeref, dataref[:, 3], 'g', lw=2, ls='--', label=f'{model}(Ref)')
hy1.plot(timetest, datatest[:, 4], 'r', lw=2, label=model)
hy1.plot(timeref, dataref[:, 4], 'g', lw=2, ls='--', label=f'{model}(Ref)')
hz1.plot(timetest, datatest[:, 5], 'r', lw=2, label=model)
hz1.plot(timeref, dataref[:, 5], 'g', lw=2, ls='--', label=f'{model}(Ref)')
ylabels = ['$E_x$, field strength [V/m]', '$H_x$, field strength [A/m]',
'$E_y$, field strength [V/m]', '$H_y$, field strength [A/m]',
'$E_z$, field strength [V/m]', '$H_z$, field strength [A/m]']
facecolor="w",
edgecolor="w",
)
ex1.plot(timetest, datatest[:, 0], "r", lw=2, label=model)
ex1.plot(timeref, dataref[:, 0], "g", lw=2, ls="--", label=f"{model}(Ref)")
ey1.plot(timetest, datatest[:, 1], "r", lw=2, label=model)
ey1.plot(timeref, dataref[:, 1], "g", lw=2, ls="--", label=f"{model}(Ref)")
ez1.plot(timetest, datatest[:, 2], "r", lw=2, label=model)
ez1.plot(timeref, dataref[:, 2], "g", lw=2, ls="--", label=f"{model}(Ref)")
hx1.plot(timetest, datatest[:, 3], "r", lw=2, label=model)
hx1.plot(timeref, dataref[:, 3], "g", lw=2, ls="--", label=f"{model}(Ref)")
hy1.plot(timetest, datatest[:, 4], "r", lw=2, label=model)
hy1.plot(timeref, dataref[:, 4], "g", lw=2, ls="--", label=f"{model}(Ref)")
hz1.plot(timetest, datatest[:, 5], "r", lw=2, label=model)
hz1.plot(timeref, dataref[:, 5], "g", lw=2, ls="--", label=f"{model}(Ref)")
ylabels = [
"$E_x$, field strength [V/m]",
"$H_x$, field strength [A/m]",
"$E_y$, field strength [V/m]",
"$H_y$, field strength [A/m]",
"$E_z$, field strength [V/m]",
"$H_z$, field strength [A/m]",
]
for i, ax in enumerate(fig1.axes):
ax.set_ylabel(ylabels[i])
ax.set_xlim(0, np.amax(timetest))
@@ -207,22 +226,31 @@ for i, model in enumerate(testmodels):
ax.legend()
# Plot diffs
fig2, ((ex2, hx2), (ey2, hy2), (ez2, hz2)) = plt.subplots(nrows=3, ncols=2,
sharex=False, sharey='col',
subplot_kw=dict(xlabel='Time [ns]'),
num='Diffs: ' + model + '.in',
fig2, ((ex2, hx2), (ey2, hy2), (ez2, hz2)) = plt.subplots(
nrows=3,
ncols=2,
sharex=False,
sharey="col",
subplot_kw=dict(xlabel="Time [ns]"),
num="Diffs: " + model + ".in",
figsize=(20, 10),
facecolor='w',
edgecolor='w')
ex2.plot(timeref, datadiffs[:, 0], 'r', lw=2, label='Ex')
ey2.plot(timeref, datadiffs[:, 1], 'r', lw=2, label='Ey')
ez2.plot(timeref, datadiffs[:, 2], 'r', lw=2, label='Ez')
hx2.plot(timeref, datadiffs[:, 3], 'r', lw=2, label='Hx')
hy2.plot(timeref, datadiffs[:, 4], 'r', lw=2, label='Hy')
hz2.plot(timeref, datadiffs[:, 5], 'r', lw=2, label='Hz')
ylabels = ['$E_x$, difference [dB]', '$H_x$, difference [dB]',
'$E_y$, difference [dB]', '$H_y$, difference [dB]',
'$E_z$, difference [dB]', '$H_z$, difference [dB]']
facecolor="w",
edgecolor="w",
)
ex2.plot(timeref, datadiffs[:, 0], "r", lw=2, label="Ex")
ey2.plot(timeref, datadiffs[:, 1], "r", lw=2, label="Ey")
ez2.plot(timeref, datadiffs[:, 2], "r", lw=2, label="Ez")
hx2.plot(timeref, datadiffs[:, 3], "r", lw=2, label="Hx")
hy2.plot(timeref, datadiffs[:, 4], "r", lw=2, label="Hy")
hz2.plot(timeref, datadiffs[:, 5], "r", lw=2, label="Hz")
ylabels = [
"$E_x$, difference [dB]",
"$H_x$, difference [dB]",
"$E_y$, difference [dB]",
"$H_y$, difference [dB]",
"$E_z$, difference [dB]",
"$H_z$, difference [dB]",
]
for i, ax in enumerate(fig2.axes):
ax.set_ylabel(ylabels[i])
ax.set_xlim(0, np.amax(timetest))
@@ -230,23 +258,25 @@ for i, model in enumerate(testmodels):
ax.grid()
# Save a PDF/PNG of the figure
filediffs = f'{file.stem}_diffs'
filediffs = f"{file.stem}_diffs"
filediffs = file.parent / Path(filediffs)
# fig1.savefig(file.with_suffix('.pdf'), dpi=None, format='pdf',
# 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()):
if 'analytical' in name:
logger.info(f"Test '{name}.in' using v.{data['Test version']} compared " +
f"to analytical solution. Max difference {data['Max diff']:.2f}dB.")
if "analytical" in name:
logger.info(
f"Test '{name}.in' using v.{data['Test version']} compared "
+ f"to analytical solution. Max difference {data['Max diff']:.2f}dB."
)
else:
logger.info(f"Test '{name}.in' using v.{data['Test version']} compared to " +
f"reference solution using v.{data['Ref version']}. Max difference " +
f"{data['Max diff']:.2f}dB.")
logger.info(
f"Test '{name}.in' using v.{data['Test version']} compared to "
+ f"reference solution using v.{data['Ref version']}. Max difference "
+ f"{data['Max diff']:.2f}dB."
)

查看文件

@@ -18,8 +18,11 @@ logger = logging.getLogger(__name__)
# Parse command line arguments
parser = argparse.ArgumentParser(description='Calculate and store (in a Numpy file) field patterns from a simulation with receivers positioned in circles around an antenna.', usage='cd gprMax; python -m user_libs.AntennaPatterns.initial_save outputfile')
parser.add_argument('outputfile', help='name of gprMax output file including path')
parser = argparse.ArgumentParser(
description="Calculate and store (in a Numpy file) field patterns from a simulation with receivers positioned in circles around an antenna.",
usage="cd gprMax; python -m user_libs.AntennaPatterns.initial_save outputfile",
)
parser.add_argument("outputfile", help="name of gprMax output file including path")
args = parser.parse_args()
outputfile = args.outputfile
@@ -27,7 +30,7 @@ outputfile = args.outputfile
# User configurable parameters
# Pattern type (E or H)
type = 'H'
type = "H"
# Antenna (true if using full antenna model; false for a theoretical Hertzian dipole
antenna = True
@@ -55,35 +58,47 @@ traceno = np.s_[:] # All traces
# Critical angle and velocity
if epsr:
mr = 1
z1 = np.sqrt(mr / epsr) * config.sim_config.em_consts['z0']
v1 = config.sim_config.em_consts['c'] / np.sqrt(epsr)
thetac = np.round(np.arcsin(v1 / config.sim_config.em_consts['c']) * (180 / np.pi))
z1 = np.sqrt(mr / epsr) * config.sim_config.em_consts["z0"]
v1 = config.sim_config.em_consts["c"] / np.sqrt(epsr)
thetac = np.round(np.arcsin(v1 / config.sim_config.em_consts["c"]) * (180 / np.pi))
wavelength = v1 / f
# Print some useful information
logger.info('Centre frequency: {} GHz'.format(f / 1e9))
logger.info("Centre frequency: {} GHz".format(f / 1e9))
if epsr:
logger.info('Critical angle for Er {} is {} degrees'.format(epsr, thetac))
logger.info('Wavelength: {:.3f} m'.format(wavelength))
logger.info('Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)'.format(radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength))
logger.info('Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m'.format(0.62 * np.sqrt((D**3) / wavelength)))
logger.info('Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m'.format((2 * D**2) / wavelength))
logger.info("Critical angle for Er {} is {} degrees".format(epsr, thetac))
logger.info("Wavelength: {:.3f} m".format(wavelength))
logger.info(
"Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)".format(
radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength
)
)
logger.info(
"Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m".format(
0.62 * np.sqrt((D**3) / wavelength)
)
)
logger.info(
"Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m".format(
(2 * D**2) / wavelength
)
)
# Load text file with coordinates of pattern origin
origin = np.loadtxt(os.path.splitext(outputfile)[0] + '_rxsorigin.txt')
origin = np.loadtxt(os.path.splitext(outputfile)[0] + "_rxsorigin.txt")
# Load output file and read some header information
f = h5py.File(outputfile, 'r')
iterations = f.attrs['Iterations']
dt = f.attrs['dt']
nrx = f.attrs['nrx']
f = h5py.File(outputfile, "r")
iterations = f.attrs["Iterations"]
dt = f.attrs["dt"]
nrx = f.attrs["nrx"]
if antenna:
nrx = nrx - 1 # Ignore first receiver point with full antenna model
start = 2
else:
start = 1
time = np.arange(0, dt * iterations, dt)
time = time / 1E-9
time = time / 1e-9
fs = 1 / dt # Sampling frequency
# Initialise arrays to store fields
@@ -105,15 +120,15 @@ Hthetasum = np.zeros(len(theta), dtype=np.float32)
patternsave = np.zeros((len(radii), len(theta)), dtype=np.float32)
for rx in range(0, nrx):
path = '/rxs/rx' + str(rx + start) + '/'
position = f[path].attrs['Position'][:]
path = "/rxs/rx" + str(rx + start) + "/"
position = f[path].attrs["Position"][:]
coords[rx, :] = position - origin
Ex[:, rx] = f[path + 'Ex'][:]
Ey[:, rx] = f[path + 'Ey'][:]
Ez[:, rx] = f[path + 'Ez'][:]
Hx[:, rx] = f[path + 'Hx'][:]
Hy[:, rx] = f[path + 'Hy'][:]
Hz[:, rx] = f[path + 'Hz'][:]
Ex[:, rx] = f[path + "Ex"][:]
Ey[:, rx] = f[path + "Ey"][:]
Ez[:, rx] = f[path + "Ez"][:]
Hx[:, rx] = f[path + "Hx"][:]
Hy[:, rx] = f[path + "Hy"][:]
Hz[:, rx] = f[path + "Hz"][:]
f.close()
# Plot traces for sanity checking
@@ -144,9 +159,18 @@ for radius in range(0, len(radii)):
r1 = coords[pt, 0] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2)
r2 = coords[pt, 1] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2)
r3 = coords[pt, 2] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2)
theta1 = (coords[pt, 0] * coords[pt, 2]) / (np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2) * np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2 + coords[pt, 2]**2))
theta2 = (coords[pt, 1] * coords[pt, 2]) / (np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2) * np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2 + coords[pt, 2]**2))
theta3 = -(np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2) / np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2 + coords[pt, 2]**2))
theta1 = (coords[pt, 0] * coords[pt, 2]) / (
np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2)
* np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2)
)
theta2 = (coords[pt, 1] * coords[pt, 2]) / (
np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2)
* np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2)
)
theta3 = -(
np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2)
/ np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2)
)
phi1 = -(coords[pt, 1] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2))
phi2 = coords[pt, 0] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2)
phi3 = 0
@@ -164,19 +188,19 @@ for radius in range(0, len(radii)):
Ethetasum[index] = np.sum(Etheta[:, index] ** 2) / z1
Hthetasum[index] = np.sum(Htheta[:, index] ** 2) / z1
else:
Ethetasum[index] = np.sum(Etheta[:, index]**2) / config.sim_config.em_consts['z0']
Hthetasum[index] = np.sum(Htheta[:, index]**2) / config.sim_config.em_consts['z0']
Ethetasum[index] = np.sum(Etheta[:, index] ** 2) / config.sim_config.em_consts["z0"]
Hthetasum[index] = np.sum(Htheta[:, index] ** 2) / config.sim_config.em_consts["z0"]
index += 1
if type == 'H':
if type == "H":
# Flip H-plane patterns as rx points are written CCW but always plotted CW
patternsave[radius, :] = Hthetasum[::-1]
elif type == 'E':
elif type == "E":
patternsave[radius, :] = Ethetasum
rxstart += len(theta)
# Save pattern to numpy file
np.save(os.path.splitext(outputfile)[0], patternsave)
logger.info('Written Numpy file: {}.npy'.format(os.path.splitext(outputfile)[0]))
logger.info("Written Numpy file: {}.npy".format(os.path.splitext(outputfile)[0]))

查看文件

@@ -17,8 +17,11 @@ logger = logging.getLogger(__name__)
# Parse command line arguments
parser = argparse.ArgumentParser(description='Plot field patterns from a simulation with receivers positioned in circles around an antenna. This module should be used after the field pattern data has been processed and stored using the initial_save.py module.', usage='cd gprMax; python -m user_libs.AntennaPatterns.plot_fields numpyfile')
parser.add_argument('numpyfile', help='name of numpy file including path')
parser = argparse.ArgumentParser(
description="Plot field patterns from a simulation with receivers positioned in circles around an antenna. This module should be used after the field pattern data has been processed and stored using the initial_save.py module.",
usage="cd gprMax; python -m user_libs.AntennaPatterns.plot_fields numpyfile",
)
parser.add_argument("numpyfile", help="name of numpy file including path")
# parser.add_argument('hertzian', help='name of numpy file including path')
args = parser.parse_args()
patterns = np.load(args.numpyfile)
@@ -28,7 +31,7 @@ patterns = np.load(args.numpyfile)
# User configurable parameters
# Pattern type (E or H)
type = 'H'
type = "H"
# Relative permittivity of half-space for homogeneous materials (set to None for inhomogeneous)
epsr = 5
@@ -52,33 +55,45 @@ step = 12
# Critical angle and velocity
if epsr:
mr = 1
z1 = np.sqrt(mr / epsr) * config.sim_config.em_consts['z0']
v1 = config.sim_config.em_consts['c'] / np.sqrt(epsr)
thetac = np.round(np.rad2deg(np.arcsin(v1 / config.sim_config.em_consts['c'])))
z1 = np.sqrt(mr / epsr) * config.sim_config.em_consts["z0"]
v1 = config.sim_config.em_consts["c"] / np.sqrt(epsr)
thetac = np.round(np.rad2deg(np.arcsin(v1 / config.sim_config.em_consts["c"])))
wavelength = v1 / f
# Print some useful information
logger.info('Centre frequency: {} GHz'.format(f / 1e9))
logger.info("Centre frequency: {} GHz".format(f / 1e9))
if epsr:
logger.info('Critical angle for Er {} is {} degrees'.format(epsr, thetac))
logger.info('Wavelength: {:.3f} m'.format(wavelength))
logger.info('Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)'.format(radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength))
logger.info('Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m'.format(0.62 * np.sqrt((D**3) / wavelength)))
logger.info('Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m'.format((2 * D**2) / wavelength))
logger.info("Critical angle for Er {} is {} degrees".format(epsr, thetac))
logger.info("Wavelength: {:.3f} m".format(wavelength))
logger.info(
"Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)".format(
radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength
)
)
logger.info(
"Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m".format(
0.62 * np.sqrt((D**3) / wavelength)
)
)
logger.info(
"Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m".format(
(2 * D**2) / wavelength
)
)
# Setup figure
fig = plt.figure(num=args.numpyfile, figsize=(8, 8), facecolor='w', edgecolor='w')
fig = plt.figure(num=args.numpyfile, figsize=(8, 8), facecolor="w", edgecolor="w")
ax = plt.subplot(111, polar=True)
cmap = plt.cm.get_cmap('rainbow')
ax.set_prop_cycle('color', [cmap(i) for i in np.linspace(0, 1, len(radii))])
cmap = plt.cm.get_cmap("rainbow")
ax.set_prop_cycle("color", [cmap(i) for i in np.linspace(0, 1, len(radii))])
# Critical angle window and air/subsurface interface lines
if epsr:
ax.plot([0, np.deg2rad(180 - thetac)], [min, 0], color='0.7', lw=2)
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.plot([0, np.deg2rad(180 - thetac)], [min, 0], color="0.7", lw=2)
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")
# Plot patterns
for patt in range(0, len(radii)):
@@ -86,12 +101,12 @@ for patt in range(0, len(radii)):
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'):
with np.errstate(divide="ignore"):
power = 10 * np.log10(pattplot)
# Replace any NaNs or Infs from zero division
power[np.invert(np.isfinite(power))] = 0
ax.plot(theta, power, label='{:.2f}m'.format(radii[patt]), marker='.', ms=6, lw=1.5)
ax.plot(theta, power, label="{:.2f}m".format(radii[patt]), marker=".", ms=6, lw=1.5)
# Add Hertzian dipole plot
# hertzplot1 = np.append(hertzian[0, :], hertzian[0, 0]) # Append start value to close circle
@@ -102,8 +117,8 @@ for patt in range(0, len(radii)):
# ax.plot(theta, 10 * np.log10(hertzplot2), label='Inf. dipole, 0.58m', color='black', ls='--', lw=3)
# Theta axis options
ax.set_theta_zero_location('N')
ax.set_theta_direction('clockwise')
ax.set_theta_zero_location("N")
ax.set_theta_direction("clockwise")
ax.set_thetagrids(np.arange(0, 360, 30))
# Radial axis options
@@ -111,19 +126,21 @@ ax.set_rmax(0)
ax.set_rlabel_position(45)
ax.set_yticks(np.arange(min, step, step))
yticks = ax.get_yticks().tolist()
yticks[-1] = '0 dB'
yticks[-1] = "0 dB"
ax.set_yticklabels(yticks)
# Grid and legend
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) # Plot just first and last legend entries
leg = ax.legend(
[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]
# Save a pdf of the plot
savename = os.path.splitext(args.numpyfile)[0] + '.pdf'
fig.savefig(savename, dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
savename = os.path.splitext(args.numpyfile)[0] + ".pdf"
fig.savefig(savename, dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1)
# savename = os.path.splitext(args.numpyfile)[0] + '.png'
# fig.savefig(savename, dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)

查看文件

@@ -8,22 +8,24 @@ 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')
parser.add_argument('filename', help='name and path to (HDF5) file containing AustinMan or AustinWoman model')
parser = argparse.ArgumentParser(
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")
args = parser.parse_args()
# Read full body HDF5 file
f = h5py.File(args.filename, 'r')
dx_dy_dz = f.attrs['dx_dy_dz']
data = f['/data'][:, :, :]
f = h5py.File(args.filename, "r")
dx_dy_dz = f.attrs["dx_dy_dz"]
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'
f = h5py.File(headfile, 'w')
f.attrs['dx_dy_dz'] = dx_dy_dz
f['/data'] = data[:, :, nzhead:data.shape[2]]
headfile = os.path.splitext(args.filename)[0] + "_head.h5"
f = h5py.File(headfile, "w")
f.attrs["dx_dy_dz"] = dx_dy_dz
f["/data"] = data[:, :, nzhead : data.shape[2]]

查看文件

@@ -52,13 +52,20 @@ class Relaxation(object):
:type optimizer_options: dict, optional, default: empty dict
"""
def __init__(self, sigma, mu, mu_sigma,
material_name, f_n=50,
def __init__(
self,
sigma,
mu,
mu_sigma,
material_name,
f_n=50,
number_of_debye_poles=-1,
plot=True, save=False,
plot=True,
save=False,
optimizer=PSO_DLS,
optimizer_options={}):
self.name = 'Relaxation function'
optimizer_options={},
):
self.name = "Relaxation function"
self.params = {}
self.number_of_debye_poles = number_of_debye_poles
self.f_n = f_n
@@ -84,19 +91,17 @@ class Relaxation(object):
f_min and f_max must satisfied f_min < f_max
"""
if abs(f_min - f_max) > 1e12:
warnings.warn(f'The chosen range is realy big. '
f'Consider setting greater number of points '
f'on the frequency grid!')
self.freq = np.logspace(np.log10(f_min),
np.log10(f_max),
int(f_n))
warnings.warn(
f"The chosen range is realy big. "
f"Consider setting greater number of points "
f"on the frequency grid!"
)
self.freq = np.logspace(np.log10(f_min), np.log10(f_max), int(f_n))
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):
@@ -116,14 +121,13 @@ 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}" f" using {self.number_of_debye_poles} Debye poles")
print(f"{self.name} parameters: ")
s = ''
s = ""
for k, v in self.params.items():
s += f"{k:10s} = {v}\n"
print(s)
return f'{self.name}:\n{s}'
return f"{self.name}:\n{s}"
def optimize(self):
"""Calling the main optimisation module with defined lower and upper boundaries of search.
@@ -138,16 +142,14 @@ class Relaxation(object):
for given frequency points.
"""
# Define the lower and upper boundaries of search
lb = np.full(self.number_of_debye_poles,
-np.log10(np.max(self.freq)) - 3)
ub = np.full(self.number_of_debye_poles,
-np.log10(np.min(self.freq)) + 3)
lb = np.full(self.number_of_debye_poles, -np.log10(np.max(self.freq)) - 3)
ub = np.full(self.number_of_debye_poles, -np.log10(np.min(self.freq)) + 3)
# Call optimizer to minimize the cost function
tau, weights, ee, rl, im = self.optimizer.fit(func=self.optimizer.cost_function,
lb=lb, ub=ub,
funckwargs={'rl': self.rl,
'im': self.im,
'freq': self.freq}
tau, weights, ee, rl, im = self.optimizer.fit(
func=self.optimizer.cost_function,
lb=lb,
ub=ub,
funckwargs={"rl": self.rl, "im": self.im, "freq": self.freq},
)
return tau, weights, ee, rl, im
@@ -173,9 +175,7 @@ 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
@@ -198,9 +198,7 @@ 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
@@ -225,24 +223,21 @@ class Relaxation(object):
print(f" |{'e_inf':^14s}|{'De':^14s}|{'log(tau_0)':^25s}|")
print("_" * 65)
for i in range(0, len(tau)):
print("Debye {0:}|{1:^14.5f}|{2:^14.5f}|{3:^25.5f}|"
.format(i + 1, ee/len(tau), weights[i],
tau[i]))
print("Debye {0:}|{1:^14.5f}|{2:^14.5f}|{3:^25.5f}|".format(i + 1, ee / len(tau), weights[i], tau[i]))
print("_" * 65)
# Print the Debye expnasion in a gprMax format
material_prop = []
material_prop.append("#material: {} {} {} {} {}\n".format(ee, self.sigma,
self.mu,
self.mu_sigma,
self.material_name))
material_prop.append(
"#material: {} {} {} {} {}\n".format(ee, self.sigma, self.mu, self.mu_sigma, self.material_name)
)
print(material_prop[0], end="")
dispersion_prop = "#add_dispersion_debye: {}".format(len(tau))
for i in range(len(tau)):
dispersion_prop += " {} {}".format(weights[i], 10 ** tau[i])
dispersion_prop += " {}".format(self.material_name)
print(dispersion_prop)
material_prop.append(dispersion_prop + '\n')
material_prop.append(dispersion_prop + "\n")
return material_prop
def plot_result(self, rl_exp, im_exp):
@@ -261,14 +256,10 @@ 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)")
@@ -276,10 +267,8 @@ 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")
@@ -316,17 +305,13 @@ class Relaxation(object):
if fdir != "../materials" and os.path.isdir(fdir):
file_path = os.path.join(fdir, "my_materials.txt")
elif os.path.isdir("../materials"):
file_path = os.path.join("../materials",
"my_materials.txt")
file_path = os.path.join("../materials", "my_materials.txt")
elif os.path.isdir("materials"):
file_path = os.path.join("materials",
"my_materials.txt")
file_path = os.path.join("materials", "my_materials.txt")
elif os.path.isdir("user_libs/materials"):
file_path = os.path.join("user_libs", "materials",
"my_materials.txt")
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')}!")
fileH = open(file_path, "a")
fileH.write(f"## {output[0].split(' ')[-1]}")
fileH.writelines(output)
@@ -362,20 +347,40 @@ class HavriliakNegami(Relaxation):
:param tau_0: Real positive float number, tau_0 is the relaxation time.
:type tau_0: float
"""
def __init__(self, f_min, f_max,
alpha, beta, e_inf, de, tau_0,
sigma, mu, mu_sigma, material_name,
number_of_debye_poles=-1, f_n=50,
plot=False, save=False,
def __init__(
self,
f_min,
f_max,
alpha,
beta,
e_inf,
de,
tau_0,
sigma,
mu,
mu_sigma,
material_name,
number_of_debye_poles=-1,
f_n=50,
plot=False,
save=False,
optimizer=PSO_DLS,
optimizer_options={}):
super(HavriliakNegami, self).__init__(sigma=sigma, mu=mu, mu_sigma=mu_sigma,
material_name=material_name, f_n=f_n,
optimizer_options={},
):
super(HavriliakNegami, self).__init__(
sigma=sigma,
mu=mu,
mu_sigma=mu_sigma,
material_name=material_name,
f_n=f_n,
number_of_debye_poles=number_of_debye_poles,
plot=plot, save=save,
plot=plot,
save=save,
optimizer=optimizer,
optimizer_options=optimizer_options)
self.name = 'Havriliak-Negami function'
optimizer_options=optimizer_options,
)
self.name = "Havriliak-Negami function"
# Place the lower frequency bound at f_min and the upper frequency bound at f_max
if f_min > f_max:
self.f_min, self.f_max = f_max, f_min
@@ -384,9 +389,15 @@ class HavriliakNegami(Relaxation):
# 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.params = {'f_min': self.f_min, 'f_max': self.f_max,
'eps_inf': self.e_inf, 'Delta_eps': self.de, 'tau_0': self.tau_0,
'alpha': self.alpha, 'beta': self.beta}
self.params = {
"f_min": self.f_min,
"f_max": self.f_max,
"eps_inf": self.e_inf,
"Delta_eps": self.de,
"tau_0": self.tau_0,
"alpha": self.alpha,
"beta": self.beta,
}
def check_inputs(self):
"""Check the validity of the Havriliak Negami model's inputs."""
@@ -407,10 +418,7 @@ 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):
@@ -433,20 +441,39 @@ class Jonscher(Relaxation):
:params n_p: Jonscher parameter, 0 < n_p < 1.
:type n_p: float, non-optional
"""
def __init__(self, f_min, f_max,
e_inf, a_p, omega_p, n_p,
sigma, mu, mu_sigma,
material_name, number_of_debye_poles=-1,
f_n=50, plot=False, save=False,
def __init__(
self,
f_min,
f_max,
e_inf,
a_p,
omega_p,
n_p,
sigma,
mu,
mu_sigma,
material_name,
number_of_debye_poles=-1,
f_n=50,
plot=False,
save=False,
optimizer=PSO_DLS,
optimizer_options={}):
super(Jonscher, self).__init__(sigma=sigma, mu=mu, mu_sigma=mu_sigma,
material_name=material_name, f_n=f_n,
optimizer_options={},
):
super(Jonscher, self).__init__(
sigma=sigma,
mu=mu,
mu_sigma=mu_sigma,
material_name=material_name,
f_n=f_n,
number_of_debye_poles=number_of_debye_poles,
plot=plot, save=save,
plot=plot,
save=save,
optimizer=optimizer,
optimizer_options=optimizer_options)
self.name = 'Jonsher function'
optimizer_options=optimizer_options,
)
self.name = "Jonsher function"
# Place the lower frequency bound at f_min and the upper frequency bound at f_max
if f_min > f_max:
self.f_min, self.f_max = f_max, f_min
@@ -455,9 +482,14 @@ class Jonscher(Relaxation):
# 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.a_p, self.omega_p, self.n_p = e_inf, a_p, omega_p, n_p
self.params = {'f_min': self.f_min, 'f_max': self.f_max,
'eps_inf': self.e_inf, 'n_p': self.n_p,
'omega_p': self.omega_p, 'a_p': self.a_p}
self.params = {
"f_min": self.f_min,
"f_max": self.f_max,
"eps_inf": self.e_inf,
"n_p": self.n_p,
"omega_p": self.omega_p,
"a_p": self.a_p,
}
def check_inputs(self):
"""Check the validity of the inputs."""
@@ -475,9 +507,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):
@@ -498,20 +530,37 @@ class Crim(Relaxation):
:type materials: ndarray, non-optional
"""
def __init__(self, f_min, f_max, a, volumetric_fractions,
materials, sigma, mu, mu_sigma, material_name,
number_of_debye_poles=-1, f_n=50,
plot=False, save=False,
def __init__(
self,
f_min,
f_max,
a,
volumetric_fractions,
materials,
sigma,
mu,
mu_sigma,
material_name,
number_of_debye_poles=-1,
f_n=50,
plot=False,
save=False,
optimizer=PSO_DLS,
optimizer_options={}):
super(Crim, self).__init__(sigma=sigma, mu=mu, mu_sigma=mu_sigma,
material_name=material_name, f_n=f_n,
optimizer_options={},
):
super(Crim, self).__init__(
sigma=sigma,
mu=mu,
mu_sigma=mu_sigma,
material_name=material_name,
f_n=f_n,
number_of_debye_poles=number_of_debye_poles,
plot=plot, save=save,
plot=plot,
save=save,
optimizer=optimizer,
optimizer_options=optimizer_options)
self.name = 'CRIM function'
optimizer_options=optimizer_options,
)
self.name = "CRIM function"
# Place the lower frequency bound at f_min and the upper frequency bound at f_max
if f_min > f_max:
self.f_min, self.f_max = f_max, f_min
@@ -522,16 +571,19 @@ class Crim(Relaxation):
self.a = a
self.volumetric_fractions = np.array(volumetric_fractions)
self.materials = np.array(materials)
self.params = {'f_min': self.f_min, 'f_max': self.f_max,
'a': self.a, 'volumetric_fractions': self.volumetric_fractions,
'materials': self.materials}
self.params = {
"f_min": self.f_min,
"f_max": self.f_max,
"a": self.a,
"volumetric_fractions": self.volumetric_fractions,
"materials": self.materials,
}
def check_inputs(self):
"""Check the validity of the inputs."""
super(Crim, self).check_inputs()
try:
d = [float(i) for i in
[self.f_min, self.f_max, self.a]]
d = [float(i) for i in [self.f_min, self.f_max, self.a]]
except ValueError:
sys.exit("The inputs should be numeric.")
if (np.array(d) < 0).sum() != 0:
@@ -558,8 +610,7 @@ 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("Material {}.:".format(i + 1))
@@ -571,12 +622,23 @@ 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)))*(
self.materials[:, 0] + self.materials[:, 1] / (
1 + 1j * 2 * np.pi * np.repeat(self.freq, len(self.materials)
).reshape((-1, len(self.materials))) * self.materials[:, 2]))**self.a,
axis=1)**(1 / self.a)
return np.sum(
np.repeat(self.volumetric_fractions, len(self.freq)).reshape((-1, len(self.materials)))
* (
self.materials[:, 0]
+ self.materials[:, 1]
/ (
1
+ 1j
* 2
* np.pi
* np.repeat(self.freq, len(self.materials)).reshape((-1, len(self.materials)))
* self.materials[:, 2]
)
)
** self.a,
axis=1,
) ** (1 / self.a)
class Rawdata(Relaxation):
@@ -588,23 +650,37 @@ class Rawdata(Relaxation):
:param delimiter: separator for three data columns
:type delimiter: str, optional (Deafult: ',')
"""
def __init__(self, filename,
sigma, mu, mu_sigma,
material_name, number_of_debye_poles=-1,
f_n=50, delimiter=',',
plot=False, save=False,
optimizer=PSO_DLS,
optimizer_options={}):
super(Rawdata, self).__init__(sigma=sigma, mu=mu, mu_sigma=mu_sigma,
material_name=material_name, f_n=f_n,
def __init__(
self,
filename,
sigma,
mu,
mu_sigma,
material_name,
number_of_debye_poles=-1,
f_n=50,
delimiter=",",
plot=False,
save=False,
optimizer=PSO_DLS,
optimizer_options={},
):
super(Rawdata, self).__init__(
sigma=sigma,
mu=mu,
mu_sigma=mu_sigma,
material_name=material_name,
f_n=f_n,
number_of_debye_poles=number_of_debye_poles,
plot=plot, save=save,
plot=plot,
save=save,
optimizer=optimizer,
optimizer_options=optimizer_options)
optimizer_options=optimizer_options,
)
self.delimiter = delimiter
self.filename = Path(filename).absolute()
self.params = {'filename': self.filename}
self.params = {"filename": self.filename}
def check_inputs(self):
"""Check the validity of the inputs."""
@@ -621,80 +697,100 @@ 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)
if __name__ == "__main__":
# Kelley et al. parameters
setup = HavriliakNegami(f_min=1e7, f_max=1e11,
alpha=0.91, beta=0.45,
e_inf=2.7, de=8.6-2.7, tau_0=9.4e-10,
sigma=0, mu=0, mu_sigma=0,
material_name="Kelley", f_n=100,
setup = HavriliakNegami(
f_min=1e7,
f_max=1e11,
alpha=0.91,
beta=0.45,
e_inf=2.7,
de=8.6 - 2.7,
tau_0=9.4e-10,
sigma=0,
mu=0,
mu_sigma=0,
material_name="Kelley",
f_n=100,
number_of_debye_poles=6,
plot=True, save=False,
optimizer_options={'swarmsize': 30,
'maxiter': 100,
'omega': 0.5,
'phip': 1.4,
'phig': 1.4,
'minstep': 1e-8,
'minfun': 1e-8,
'seed': 111,
'pflag': True})
plot=True,
save=False,
optimizer_options={
"swarmsize": 30,
"maxiter": 100,
"omega": 0.5,
"phip": 1.4,
"phig": 1.4,
"minstep": 1e-8,
"minfun": 1e-8,
"seed": 111,
"pflag": True,
},
)
setup.run()
setup = HavriliakNegami(f_min=1e7, f_max=1e11,
alpha=0.91, beta=0.45,
e_inf=2.7, de=8.6-2.7, tau_0=9.4e-10,
sigma=0, mu=0, mu_sigma=0,
material_name="Kelley", f_n=100,
setup = HavriliakNegami(
f_min=1e7,
f_max=1e11,
alpha=0.91,
beta=0.45,
e_inf=2.7,
de=8.6 - 2.7,
tau_0=9.4e-10,
sigma=0,
mu=0,
mu_sigma=0,
material_name="Kelley",
f_n=100,
number_of_debye_poles=6,
plot=True, save=False,
plot=True,
save=False,
optimizer=DA_DLS,
optimizer_options={'seed': 111})
optimizer_options={"seed": 111},
)
setup.run()
setup = HavriliakNegami(f_min=1e7, f_max=1e11,
alpha=0.91, beta=0.45,
e_inf=2.7, de=8.6-2.7, tau_0=9.4e-10,
sigma=0, mu=0, mu_sigma=0,
material_name="Kelley", f_n=100,
setup = HavriliakNegami(
f_min=1e7,
f_max=1e11,
alpha=0.91,
beta=0.45,
e_inf=2.7,
de=8.6 - 2.7,
tau_0=9.4e-10,
sigma=0,
mu=0,
mu_sigma=0,
material_name="Kelley",
f_n=100,
number_of_debye_poles=6,
plot=True, save=False,
plot=True,
save=False,
optimizer=DE_DLS,
optimizer_options={'seed': 111})
optimizer_options={"seed": 111},
)
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})
setup = Rawdata(
"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()

查看文件

@@ -3,26 +3,39 @@
# using a general Cole-Cole dispersion function,"
# 2012 14th International Conference on Ground Penetrating Radar (GPR), 2012, pp. 232-236
import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
from Debye_Fit import HavriliakNegami
if __name__ == "__main__":
# set Havrilak-Negami function with initial parameters
setup = HavriliakNegami(f_min=1e4, f_max=1e11,
alpha=0.3, beta=1,
e_inf=3.4, de=2.7, tau_0=.8e-10,
sigma=0.45e-3, mu=1, mu_sigma=0,
material_name="dry_sand", f_n=100,
plot=True, save=False,
optimizer_options={'swarmsize':30,
'maxiter':100,
'omega':0.5,
'phip':1.4,
'phig':1.4,
'minstep':1e-8,
'minfun':1e-8,
'seed': 111,
'pflag': True})
setup = HavriliakNegami(
f_min=1e4,
f_max=1e11,
alpha=0.3,
beta=1,
e_inf=3.4,
de=2.7,
tau_0=0.8e-10,
sigma=0.45e-3,
mu=1,
mu_sigma=0,
material_name="dry_sand",
f_n=100,
plot=True,
save=False,
optimizer_options={
"swarmsize": 30,
"maxiter": 100,
"omega": 0.5,
"phip": 1.4,
"phig": 1.4,
"minstep": 1e-8,
"minfun": 1e-8,
"seed": 111,
"pflag": True,
},
)
### Dry Sand in case of 3, 5
# and automatically set number of Debye poles (-1)
for number_of_debye_poles in [3, 5, -1]:
@@ -36,7 +49,7 @@ if __name__ == "__main__":
setup.beta = 1
setup.e_inf = 5.6
setup.de = 3.3
setup.tau_0 = 1.1e-10,
setup.tau_0 = (1.1e-10,)
setup.sigma = 2e-3
# calculate for different number of Debye poles
for number_of_debye_poles in [3, 5, -1]:

查看文件

@@ -22,6 +22,7 @@ class Optimizer(object):
unsigned integers (Default: None).
:type seed: int, NoneType, optional
"""
def __init__(self, maxiter=1000, seed=None):
self.maxiter = maxiter
self.seed = seed
@@ -58,8 +59,7 @@ class Optimizer(object):
# find the weights using a calc_weights method
if self.calc_weights is None:
raise NotImplementedError()
_, _, weights, ee, rl_exp, im_exp = \
self.calc_weights(tau, **funckwargs)
_, _, weights, ee, rl_exp, im_exp = self.calc_weights(tau, **funckwargs)
return tau, weights, ee, rl_exp, im_exp
def calc_relaxation_times(self):
@@ -119,11 +119,10 @@ class PSO_DLS(Optimizer):
value during optimization process (Default: False).
:type pflag: bool, optional
"""
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):
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
):
super(PSO_DLS, self).__init__(maxiter, seed)
self.swarmsize = swarmsize
self.omega = omega
@@ -156,13 +155,11 @@ class PSO_DLS(Optimizer):
"""
np.random.seed(self.seed)
# check input parameters
assert len(lb) == len(ub), \
'Lower- and upper-bounds must be the same length'
assert hasattr(func, '__call__'), 'Invalid function handle'
assert len(lb) == len(ub), "Lower- and upper-bounds must be the same length"
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
@@ -200,14 +197,16 @@ class PSO_DLS(Optimizer):
v[i, :] = vlow + np.random.rand(d) * (vhigh - vlow)
# Iterate until termination criterion met
for it in tqdm(range(self.maxiter), desc='Debye fitting'):
for it in tqdm(range(self.maxiter), desc="Debye fitting"):
rp = np.random.uniform(size=(self.swarmsize, d))
rg = np.random.uniform(size=(self.swarmsize, d))
for i in range(self.swarmsize):
# Update the particle's velocity
v[i, :] = self.omega * v[i, :] + self.phip * rp[i, :] * \
(p[i, :] - x[i, :]) + \
self.phig * rg[i, :] * (g - x[i, :])
v[i, :] = (
self.omega * v[i, :]
+ self.phip * rp[i, :] * (p[i, :] - x[i, :])
+ self.phig * rg[i, :] * (g - x[i, :])
)
# Update the particle's position,
# correcting lower and upper bound
# violations, then update the objective function value
@@ -227,12 +226,10 @@ 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()
@@ -261,11 +258,9 @@ class PSO_DLS(Optimizer):
# it clears an axes
plt.cla()
plt.plot(x, y, "b-", linewidth=1.0)
plt.ylim(min(y) - 0.1 * min(y),
max(y) + 0.1 * max(y))
plt.ylim(min(y) - 0.1 * min(y), max(y) + 0.1 * max(y))
plt.xlim(min(x) - 0.1, max(x) + 0.1)
plt.grid(b=True, which="major", color="k",
linewidth=0.2, linestyle="--")
plt.grid(b=True, which="major", color="k", linewidth=0.2, linestyle="--")
plt.suptitle("Debye fitting process")
plt.xlabel("Iteration")
plt.ylabel("Average Error")
@@ -279,11 +274,21 @@ class DA_DLS(Optimizer):
https://docs.scipy.org/doc/scipy/reference/generated/
scipy.optimize.dual_annealing.html#scipy.optimize.dual_annealing
"""
def __init__(self, maxiter=1000,
local_search_options={}, initial_temp=5230.0,
restart_temp_ratio=2e-05, visit=2.62, accept=-5.0,
maxfun=1e7, no_local_search=False,
callback=None, x0=None, seed=None):
def __init__(
self,
maxiter=1000,
local_search_options={},
initial_temp=5230.0,
restart_temp_ratio=2e-05,
visit=2.62,
accept=-5.0,
maxfun=1e7,
no_local_search=False,
callback=None,
x0=None,
seed=None,
):
super(DA_DLS, self).__init__(maxiter, seed)
self.local_search_options = local_search_options
self.initial_temp = initial_temp
@@ -330,7 +335,8 @@ class DA_DLS(Optimizer):
maxfun=self.maxfun,
no_local_search=self.no_local_search,
callback=self.callback,
x0=self.x0)
x0=self.x0,
)
print(result.message)
return result.x, result.fun
@@ -344,12 +350,25 @@ class DE_DLS(Optimizer):
https://docs.scipy.org/doc/scipy/reference/generated/
scipy.optimize.differential_evolution.html#scipy.optimize.differential_evolution
"""
def __init__(self, maxiter=1000,
strategy='best1bin', popsize=15, tol=0.01, mutation=(0.5, 1),
recombination=0.7, callback=None, disp=False, polish=True,
init='latinhypercube', atol=0,
updating='immediate', workers=1,
constraints=(), seed=None):
def __init__(
self,
maxiter=1000,
strategy="best1bin",
popsize=15,
tol=0.01,
mutation=(0.5, 1),
recombination=0.7,
callback=None,
disp=False,
polish=True,
init="latinhypercube",
atol=0,
updating="immediate",
workers=1,
constraints=(),
seed=None,
):
super(DE_DLS, self).__init__(maxiter, seed)
self.strategy = strategy
self.popsize = popsize
@@ -403,7 +422,8 @@ class DE_DLS(Optimizer):
atol=self.atol,
updating=self.updating,
workers=self.workers,
constraints=self.constraints)
constraints=self.constraints,
)
print(result.message)
return result.x, result.fun
@@ -446,14 +466,12 @@ def DLS(logt, rl, im, freq):
# Here they are transformed back t0=10**logt
tt = 10**logt
# y = Ax, here the A matrix for the real and the imaginary part is builded
d = 1 / (1 + 1j * 2 * np.pi * np.repeat(
freq, len(tt)).reshape((-1, len(tt))) * tt)
d = 1 / (1 + 1j * 2 * np.pi * np.repeat(freq, len(tt)).reshape((-1, len(tt))) * tt)
# Adding dumping (Levenberg–Marquardt algorithm)
# 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)
if ee < 1:

文件差异内容过多而无法显示 加载差异

查看文件

@@ -50,10 +50,10 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
# If using parameters from an optimisation
try:
kwargs
excitationfreq = kwargs['excitationfreq']
sourceresistance = kwargs['sourceresistance']
absorberEr = kwargs['absorberEr']
absorbersig = kwargs['absorbersig']
excitationfreq = kwargs["excitationfreq"]
sourceresistance = kwargs["sourceresistance"]
absorberEr = kwargs["absorberEr"]
absorbersig = kwargs["absorbersig"]
# Otherwise choose pre-set optimised parameters
except:
@@ -84,7 +84,7 @@ 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
@@ -100,382 +100,573 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
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')
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')
polypropylene = gprMax.Material(er=2.26, se=0, mr=1, sm=0, id='polypropylene')
txreslower = gprMax.Material(er=3, se=txsiglower, mr=1, sm=0, id='txreslower')
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))
absorber = gprMax.Material(er=absorberEr, se=absorbersig, mr=1, sm=0, id="absorber")
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")
polypropylene = gprMax.Material(er=2.26, se=0, mr=1, sm=0, id="polypropylene")
txreslower = gprMax.Material(er=3, se=txsiglower, mr=1, sm=0, id="txreslower")
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))
# Antenna geometry
# Shield - metallic enclosure
b1 = gprMax.Box(p1=(x, y, z + skidthickness),
b1 = gprMax.Box(
p1=(x, y, z + skidthickness),
p2=(x + casesize[0], y + casesize[1], z + skidthickness + casesize[2]),
material_id='pec')
b2 = gprMax.Box(p1=(x + 0.020, y + casethickness, z + skidthickness),
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), material_id='free_space')
material_id="pec",
)
b2 = gprMax.Box(
p1=(x + 0.020, y + casethickness, z + skidthickness),
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),
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),
material_id='absorber')
b5 = gprMax.Box(p1=(x + 0.100, y + casethickness, z + skidthickness),
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness,
z + skidthickness + casesize[2] - casethickness),
material_id='absorber')
b4 = gprMax.Box(
p1=(x + 0.020, y + casethickness, z + skidthickness),
p2=(x + 0.100, y + casesize[1] - casethickness, z + skidthickness + casesize[2] - casethickness),
material_id="absorber",
)
b5 = gprMax.Box(
p1=(x + 0.100, y + casethickness, z + skidthickness),
p2=(
x + casesize[0] - casethickness,
y + casesize[1] - casethickness,
z + skidthickness + casesize[2] - casethickness,
),
material_id="absorber",
)
scene_objects.extend((b1, b2, b3, b4, b5))
# 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),
r=0.008, material_id='pec')
c2 = gprMax.Cylinder(p1=(x + 0.055, y + 0.008, z + skidthickness),
p2=(x + 0.055, y + 0.008,
z + skidthickness + casesize[2] - casethickness),
r=0.008, material_id='pec')
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),
r=0.008, material_id='pec')
c4 = gprMax.Cylinder(p1=(x + 0.147, y + 0.008, z + skidthickness),
p2=(x + 0.147, y + 0.008,
z + skidthickness + casesize[2] - casethickness),
r=0.008, material_id='pec')
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),
r=0.007, material_id='free_space')
c6 = gprMax.Cylinder(p1=(x + 0.055, y + 0.008, z + skidthickness),
p2=(x + 0.055, y + 0.008,
z + skidthickness + casesize[2] - casethickness),
r=0.007, material_id='free_space')
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),
r=0.007, material_id='free_space')
c8 = gprMax.Cylinder(p1=(x + 0.147, y + 0.008, z + skidthickness),
p2=(x + 0.147, y + 0.008,
z + skidthickness + casesize[2] - casethickness),
r=0.007, material_id='free_space')
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),
material_id='free_space')
b7 = gprMax.Box(p1=(x + 0.054, y + 0.014, z + skidthickness),
p2=(x + 0.056, y + 0.016,
z + skidthickness + casesize[2] - casethickness),
material_id='free_space')
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),
material_id='free_space')
b9 = gprMax.Box(p1=(x + 0.146, y + 0.014, z + skidthickness),
p2=(x + 0.148, y + 0.016,
z + skidthickness + casesize[2] - casethickness),
material_id='free_space')
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),
r=0.008,
material_id="pec",
)
c2 = gprMax.Cylinder(
p1=(x + 0.055, y + 0.008, z + skidthickness),
p2=(x + 0.055, y + 0.008, z + skidthickness + casesize[2] - casethickness),
r=0.008,
material_id="pec",
)
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),
r=0.008,
material_id="pec",
)
c4 = gprMax.Cylinder(
p1=(x + 0.147, y + 0.008, z + skidthickness),
p2=(x + 0.147, y + 0.008, z + skidthickness + casesize[2] - casethickness),
r=0.008,
material_id="pec",
)
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),
r=0.007,
material_id="free_space",
)
c6 = gprMax.Cylinder(
p1=(x + 0.055, y + 0.008, z + skidthickness),
p2=(x + 0.055, y + 0.008, z + skidthickness + casesize[2] - casethickness),
r=0.007,
material_id="free_space",
)
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),
r=0.007,
material_id="free_space",
)
c8 = gprMax.Cylinder(
p1=(x + 0.147, y + 0.008, z + skidthickness),
p2=(x + 0.147, y + 0.008, z + skidthickness + casesize[2] - casethickness),
r=0.007,
material_id="free_space",
)
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),
material_id="free_space",
)
b7 = gprMax.Box(
p1=(x + 0.054, y + 0.014, z + skidthickness),
p2=(x + 0.056, y + 0.016, z + skidthickness + casesize[2] - casethickness),
material_id="free_space",
)
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),
material_id="free_space",
)
b9 = gprMax.Box(
p1=(x + 0.146, y + 0.014, z + skidthickness),
p2=(x + 0.148, y + 0.016, z + skidthickness + casesize[2] - casethickness),
material_id="free_space",
)
scene_objects.extend((c1, c2, c3, c4, c5, c6, c7, c8, b6, b7, b8, b9))
# 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), material_id='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),
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]), material_id='pec')
b12 = gprMax.Box(p1=(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness,
z + skidthickness), p2=(x + 0.032 + cavitysize[0] - cavitythickness,
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]),
material_id="pec",
)
b12 = gprMax.Box(
p1=(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness),
p2=(
x + 0.032 + cavitysize[0] - cavitythickness,
y + 0.022 + cavitysize[1] - cavitythickness,
z + skidthickness + cavitysize[2]), material_id='absorber')
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]), material_id='pec')
b14 = gprMax.Box(p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness,
z + skidthickness), p2=(x + 0.108 + cavitysize[0] - cavitythickness,
z + skidthickness + cavitysize[2],
),
material_id="absorber",
)
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]),
material_id="pec",
)
b14 = gprMax.Box(
p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness),
p2=(
x + 0.108 + cavitysize[0] - cavitythickness,
y + 0.022 + cavitysize[1] - cavitythickness,
z + skidthickness + cavitysize[2]), material_id='free_space')
z + skidthickness + cavitysize[2],
),
material_id="free_space",
)
# Shield - Tx & Rx cavities - joining strips
b15 = gprMax.Box(p1=(x + 0.032 + cavitysize[0], y + 0.022 + cavitysize[1] - 0.006,
z + skidthickness + cavitysize[2] - casethickness),
p2=(x + 0.108, y + 0.022 + cavitysize[1],
z + skidthickness + cavitysize[2]), material_id='pec')
b16 = gprMax.Box(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')
b15 = gprMax.Box(
p1=(
x + 0.032 + cavitysize[0],
y + 0.022 + cavitysize[1] - 0.006,
z + skidthickness + cavitysize[2] - casethickness,
),
p2=(x + 0.108, y + 0.022 + cavitysize[1], z + skidthickness + cavitysize[2]),
material_id="pec",
)
b16 = gprMax.Box(
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), p2=(x + 0.032 + cavitysize[0] - cavitythickness,
b17 = gprMax.Box(
p1=(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness),
p2=(
x + 0.032 + cavitysize[0] - cavitythickness,
y + 0.022 + cavitysize[1] - cavitythickness,
z + skidthickness + pcbthickness), material_id='pcb')
b18 = gprMax.Box(p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness,
z + skidthickness), p2=(x + 0.108 + cavitysize[0] - cavitythickness,
z + skidthickness + pcbthickness,
),
material_id="pcb",
)
b18 = gprMax.Box(
p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness),
p2=(
x + 0.108 + cavitysize[0] - cavitythickness,
y + 0.022 + cavitysize[1] - cavitythickness,
z + skidthickness + pcbthickness), material_id='pcb')
z + skidthickness + pcbthickness,
),
material_id="pcb",
)
scene_objects.extend((b10, b11, b12, b13, b14, b15, b16, b17, b18))
# PCB components
# Tx bowtie
if resolution == 0.001:
t1 = gprMax.Triangle(p1=(tx[0], tx[1] - 0.001, tx[2]),
t1 = gprMax.Triangle(
p1=(tx[0], tx[1] - 0.001, tx[2]),
p2=(tx[0] - 0.026, tx[1] - bowtieheight - 0.001, tx[2]),
p3=(tx[0] + 0.026, tx[1] - bowtieheight - 0.001, tx[2]),
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')
t2 = gprMax.Triangle(p1=(tx[0], tx[1] + 0.002, tx[2]),
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")
t2 = gprMax.Triangle(
p1=(tx[0], tx[1] + 0.002, tx[2]),
p2=(tx[0] - 0.026, tx[1] + bowtieheight + 0.002, tx[2]),
p3=(tx[0] + 0.026, tx[1] + bowtieheight + 0.002, tx[2]),
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')
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")
scene_objects.extend((t1, t2, e1, e2))
elif resolution == 0.002:
t1 = gprMax.Triangle(p1=(tx[0], tx[1], tx[2]),
t1 = gprMax.Triangle(
p1=(tx[0], tx[1], tx[2]),
p2=(tx[0] - 0.026, tx[1] - bowtieheight, tx[2]),
p3=(tx[0] + 0.026, tx[1] - bowtieheight, tx[2]),
thickness=0, material_id='pec')
t2 = gprMax.Triangle(p1=(tx[0], tx[1] + 0.002, tx[2]),
thickness=0,
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]),
p3=(tx[0] + 0.026, tx[1] + bowtieheight + 0.002, tx[2]),
thickness=0, material_id='pec')
thickness=0,
material_id="pec",
)
scene_objects.extend((t1, t2))
# Rx bowtie
if resolution == 0.001:
t3 = gprMax.Triangle(p1=(tx[0] + 0.076, tx[1] - 0.001, tx[2]),
t3 = gprMax.Triangle(
p1=(tx[0] + 0.076, tx[1] - 0.001, tx[2]),
p2=(tx[0] + 0.076 - 0.026, tx[1] - bowtieheight - 0.001, tx[2]),
p3=(tx[0] + 0.076 + 0.026, tx[1] - bowtieheight - 0.001, tx[2]),
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')
t4 = gprMax.Triangle(p1=(tx[0] + 0.076, tx[1] + 0.002, tx[2]),
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")
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]),
p3=(tx[0] + 0.076 + 0.026, tx[1] + bowtieheight + 0.002, tx[2]),
thickness=0, 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')
thickness=0,
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"
)
scene_objects.extend((t3, e3, t4, e4))
elif resolution == 0.002:
t3 = gprMax.Triangle(p1=(tx[0] + 0.076, tx[1], tx[2]),
t3 = gprMax.Triangle(
p1=(tx[0] + 0.076, tx[1], tx[2]),
p2=(tx[0] + 0.076 - 0.026, tx[1] - bowtieheight, tx[2]),
p3=(tx[0] + 0.076 + 0.026, tx[1] - bowtieheight, tx[2]),
thickness=0, material_id='pec')
t4 = gprMax.Triangle(p1=(tx[0] + 0.076, tx[1] + 0.002, tx[2]),
thickness=0,
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]),
p3=(tx[0] + 0.076 + 0.026, tx[1] + bowtieheight + 0.002, tx[2]),
thickness=0, material_id='pec')
thickness=0,
material_id="pec",
)
scene_objects.extend((t3, t4))
# Tx surface mount resistors (lower y coordinate)
if resolution == 0.001:
e5 = gprMax.Edge(p1=(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2]),
e5 = gprMax.Edge(
p1=(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] - 0.023, tx[1] - bowtieheight - dy, tx[2]),
material_id='txreslower')
e6 = gprMax.Edge(p1=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e6 = gprMax.Edge(
p1=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - dy, tx[2]),
material_id='txreslower')
e7 = gprMax.Edge(p1=(tx[0], tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e7 = gprMax.Edge(
p1=(tx[0], tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0], tx[1] - bowtieheight - dy, tx[2]),
material_id='txreslower')
e8 = gprMax.Edge(p1=(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e8 = gprMax.Edge(
p1=(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + dx, tx[1] - bowtieheight - dy, tx[2]),
material_id='txreslower')
e9 = gprMax.Edge(p1=(tx[0] + 0.022, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e9 = gprMax.Edge(
p1=(tx[0] + 0.022, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.022, tx[1] - bowtieheight - dy, tx[2]),
material_id='txreslower')
e10 = gprMax.Edge(p1=(tx[0] + 0.022 + dx, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e10 = gprMax.Edge(
p1=(tx[0] + 0.022 + dx, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.022 + dx, tx[1] - bowtieheight - dy, tx[2]),
material_id='txreslower')
material_id="txreslower",
)
scene_objects.extend((e5, e6, e7, e8, e9, e10))
elif resolution == 0.002:
e5 = gprMax.Edge(p1=(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2]),
e5 = gprMax.Edge(
p1=(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] - 0.023, tx[1] - bowtieheight, tx[2]),
material_id='txreslower')
e6 = gprMax.Edge(p1=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e6 = gprMax.Edge(
p1=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] - 0.023 + dx, tx[1] - bowtieheight, tx[2]),
material_id='txreslower')
e7 = gprMax.Edge(p1=(tx[0], tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e7 = gprMax.Edge(
p1=(tx[0], tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0], tx[1] - bowtieheight, tx[2]),
material_id='txreslower')
e8 = gprMax.Edge(p1=(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e8 = gprMax.Edge(
p1=(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + dx, tx[1] - bowtieheight, tx[2]),
material_id='txreslower')
e9 = gprMax.Edge(p1=(tx[0] + 0.020, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e9 = gprMax.Edge(
p1=(tx[0] + 0.020, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.020, tx[1] - bowtieheight, tx[2]),
material_id='txreslower')
e10 = gprMax.Edge(p1=(tx[0] + 0.020 + dx, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="txreslower",
)
e10 = gprMax.Edge(
p1=(tx[0] + 0.020 + dx, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.020 + dx, tx[1] - bowtieheight, tx[2]),
material_id='txreslower')
material_id="txreslower",
)
scene_objects.extend((e5, e6, e7, e8, e9, e10))
# Tx surface mount resistors (upper y coordinate)
if resolution == 0.001:
e11 = gprMax.Edge(p1=(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2]),
e11 = gprMax.Edge(
p1=(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] - 0.023, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e12 = gprMax.Edge(p1=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e12 = gprMax.Edge(
p1=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e13 = gprMax.Edge(p1=(tx[0], tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e13 = gprMax.Edge(
p1=(tx[0], tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0], tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e14 = gprMax.Edge(p1=(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e14 = gprMax.Edge(
p1=(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + dx, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e15 = gprMax.Edge(p1=(tx[0] + 0.022, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e15 = gprMax.Edge(
p1=(tx[0] + 0.022, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.022, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e16 = gprMax.Edge(p1=(tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e16 = gprMax.Edge(
p1=(tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
material_id="txresupper",
)
scene_objects.extend((e11, e12, e13, e14, e15, e16))
elif resolution == 0.002:
e11 = gprMax.Edge(p1=(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2]),
e11 = gprMax.Edge(
p1=(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] - 0.023, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e12 = gprMax.Edge(p1=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e12 = gprMax.Edge(
p1=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e13 = gprMax.Edge(p1=(tx[0], tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e13 = gprMax.Edge(
p1=(tx[0], tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0], tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e14 = gprMax.Edge(p1=(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e14 = gprMax.Edge(
p1=(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + dx, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e15 = gprMax.Edge(p1=(tx[0] + 0.020, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e15 = gprMax.Edge(
p1=(tx[0] + 0.020, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.020, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
e16 = gprMax.Edge(p1=(tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="txresupper",
)
e16 = gprMax.Edge(
p1=(tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='txresupper')
material_id="txresupper",
)
scene_objects.extend((e11, e12, e13, e14, e15, e16))
# Rx surface mount resistors (lower y coordinate)
if resolution == 0.001:
e17 = gprMax.Edge(p1=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
e17 = gprMax.Edge(
p1=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - dy, tx[2]),
material_id='rxreslower')
e18 = gprMax.Edge(p1=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e18 = gprMax.Edge(
p1=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]),
material_id='rxreslower')
e19 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e19 = gprMax.Edge(
p1=(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.076, tx[1] - bowtieheight - dy, tx[2]),
material_id='rxreslower')
e20 = gprMax.Edge(p1=(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e20 = gprMax.Edge(
p1=(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]),
material_id='rxreslower')
e21 = gprMax.Edge(p1=(tx[0] + 0.022 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e21 = gprMax.Edge(
p1=(tx[0] + 0.022 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.022 + 0.076, tx[1] - bowtieheight - dy, tx[2]),
material_id='rxreslower')
e22 = gprMax.Edge(p1=(tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e22 = gprMax.Edge(
p1=(tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]),
material_id='rxreslower')
material_id="rxreslower",
)
scene_objects.extend((e17, e18, e19, e20, e21, e22))
elif resolution == 0.002:
e17 = gprMax.Edge(p1=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
e17 = gprMax.Edge(
p1=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight, tx[2]),
material_id='rxreslower')
e18 = gprMax.Edge(p1=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e18 = gprMax.Edge(
p1=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight, tx[2]),
material_id='rxreslower')
e19 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e19 = gprMax.Edge(
p1=(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.076, tx[1] - bowtieheight, tx[2]),
material_id='rxreslower')
e20 = gprMax.Edge(p1=(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e20 = gprMax.Edge(
p1=(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + dx + 0.076, tx[1] - bowtieheight, tx[2]),
material_id='rxreslower')
e21 = gprMax.Edge(p1=(tx[0] + 0.020 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e21 = gprMax.Edge(
p1=(tx[0] + 0.020 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.020 + 0.076, tx[1] - bowtieheight, tx[2]),
material_id='rxreslower')
e22 = gprMax.Edge(p1=(tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
material_id="rxreslower",
)
e22 = gprMax.Edge(
p1=(tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]),
p2=(tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight, tx[2]),
material_id='rxreslower')
material_id="rxreslower",
)
scene_objects.extend((e17, e18, e19, e20, e21, e22))
# Rx surface mount resistors (upper y coordinate)
if resolution == 0.001:
e23 = gprMax.Edge(p1=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
e23 = gprMax.Edge(
p1=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e24 = gprMax.Edge(p1=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e24 = gprMax.Edge(
p1=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e25 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e25 = gprMax.Edge(
p1=(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e26 = gprMax.Edge(p1=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e26 = gprMax.Edge(
p1=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e27 = gprMax.Edge(p1=(tx[0] + 0.022 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e27 = gprMax.Edge(
p1=(tx[0] + 0.022 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.022 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e28 = gprMax.Edge(p1=(tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e28 = gprMax.Edge(
p1=(tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
material_id="rxresupper",
)
scene_objects.extend((e23, e24, e25, e26, e27, e28))
elif resolution == 0.002:
e23 = gprMax.Edge(p1=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
e23 = gprMax.Edge(
p1=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e24 = gprMax.Edge(p1=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e24 = gprMax.Edge(
p1=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e25 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e25 = gprMax.Edge(
p1=(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e26 = gprMax.Edge(p1=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e26 = gprMax.Edge(
p1=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e27 = gprMax.Edge(p1=(tx[0] + 0.020 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e27 = gprMax.Edge(
p1=(tx[0] + 0.020 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.020 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
e28 = gprMax.Edge(p1=(tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
material_id="rxresupper",
)
e28 = gprMax.Edge(
p1=(tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]),
p2=(tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
material_id='rxresupper')
material_id="rxresupper",
)
scene_objects.extend((e23, e24, e25, e26, e27, e28))
# Skid
b19 = gprMax.Box(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),
material_id='hdpe')
b19 = gprMax.Box(
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),
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')
vs1 = gprMax.VoltageSource(
polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_gaussian"
)
scene_objects.append(vs1)
# Output point - receiver bowtie
r1 = gprMax.Rx(p1=(tx[0] + 0.076, tx[1], tx[2]), id='rxbowtie', outputs='Ey')
r1 = gprMax.Rx(p1=(tx[0] + 0.076, tx[1], tx[2]), id="rxbowtie", outputs="Ey")
scene_objects.append(r1)
# 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),
dl=(dx, dy, dz), filename='antenna_like_MALA_1200',
output_type='n')
gv2 = gprMax.GeometryView(p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + 0.010),
dl=(dx, dy, dz), filename='antenna_like_MALA_1200_pcb',
output_type='f')
gv1 = gprMax.GeometryView(
p1=(x - dx, y - dy, z - 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",
)
gv2 = gprMax.GeometryView(
p1=(x, y, z),
p2=(x + casesize[0], y + casesize[1], z + 0.010),
dl=(dx, dy, dz),
filename="antenna_like_MALA_1200_pcb",
output_type="f",
)
# scene_objects.extend((gv1, gv2))
return scene_objects

查看文件

@@ -48,55 +48,53 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
file = Path(filename)
# Open output file and read iterations
f = h5py.File(file, 'r')
f = h5py.File(file, "r")
# Paths to grid(s) to traverse for outputs
paths = ['/']
paths = ["/"]
# Check if any subgrids and add path(s)
is_subgrids = "/subgrids" in f
if is_subgrids:
paths = paths + ['/subgrids/' + path + '/' for path in f['/subgrids'].keys()]
paths = paths + ["/subgrids/" + path + "/" for path in f["/subgrids"].keys()]
# Get number of receivers in grid(s)
nrxs = []
for path in paths:
if f[path].attrs['nrx'] > 0:
nrxs.append(f[path].attrs['nrx'])
if f[path].attrs["nrx"] > 0:
nrxs.append(f[path].attrs["nrx"])
else:
paths.remove(path)
# Check there are any receivers
if not paths:
logger.exception(f'No receivers found in {file}')
logger.exception(f"No receivers found in {file}")
raise ValueError
# Loop through all grids
for path in paths:
iterations = f[path].attrs['Iterations']
nrx = f[path].attrs['nrx']
dt = f[path].attrs['dt']
iterations = f[path].attrs["Iterations"]
nrx = f[path].attrs["nrx"]
dt = f[path].attrs["dt"]
time = np.linspace(0, (iterations - 1) * dt, num=iterations)
# Check for single output component when doing a FFT
if fft:
if 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
for rx in range(1, nrx + 1):
rxpath = path + 'rxs/rx' + str(rx) + '/'
rxpath = path + "rxs/rx" + str(rx) + "/"
availableoutputs = list(f[rxpath].keys())
# If only a single output is required, create one subplot
if len(outputs) == 1:
# Check for polarity of output and if requested output is in file
if outputs[0][-1] == '-':
if outputs[0][-1] == "-":
polarity = -1
outputtext = '-' + outputs[0][0:-1]
outputtext = "-" + outputs[0][0:-1]
output = outputs[0][0:-1]
else:
polarity = 1
@@ -104,9 +102,11 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
output = outputs[0]
if output not in availableoutputs:
logger.exception(f"{output} output requested to plot, but " +
f"the available output for receiver 1 is " +
f"{', '.join(availableoutputs)}")
logger.exception(
f"{output} output requested to plot, but "
+ f"the available output for receiver 1 is "
+ f"{', '.join(availableoutputs)}"
)
raise ValueError
outputdata = f[rxpath + output][:] * polarity
@@ -118,7 +118,7 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
freqmaxpower = np.where(np.isclose(power, 0))[0][0]
# Set plotting range to -60dB from maximum power or 4 times
# frequency at maximum power
# frequency at maximum power
try:
pltrange = np.where(power[freqmaxpower:] < -60)[0][0] + freqmaxpower + 1
except:
@@ -127,71 +127,80 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
pltrange = np.s_[0:pltrange]
# Plot time history of output component
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2,
num=rxpath + ' - ' + f[rxpath].attrs['Name'],
figsize=(20, 10), facecolor='w',
edgecolor='w')
line1 = ax1.plot(time, outputdata, 'r', lw=2, label=outputtext)
ax1.set_xlabel('Time [s]')
ax1.set_ylabel(outputtext + ' field strength [V/m]')
fig, (ax1, ax2) = plt.subplots(
nrows=1,
ncols=2,
num=rxpath + " - " + f[rxpath].attrs["Name"],
figsize=(20, 10),
facecolor="w",
edgecolor="w",
)
line1 = ax1.plot(time, outputdata, "r", lw=2, label=outputtext)
ax1.set_xlabel("Time [s]")
ax1.set_ylabel(outputtext + " field strength [V/m]")
ax1.set_xlim([0, np.amax(time)])
ax1.grid(which='both', axis='both', linestyle='-.')
ax1.grid(which="both", axis="both", linestyle="-.")
# Plot frequency spectra
markerline, stemlines, baseline = ax2.stem(freqs[pltrange],
power[pltrange], '-.',
use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'r')
plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r')
line2 = ax2.plot(freqs[pltrange], power[pltrange], 'r', lw=2)
ax2.set_xlabel('Frequency [Hz]')
ax2.set_ylabel('Power [dB]')
ax2.grid(which='both', axis='both', linestyle='-.')
markerline, stemlines, baseline = ax2.stem(
freqs[pltrange], power[pltrange], "-.", use_line_collection=True
)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "r")
plt.setp(markerline, "markerfacecolor", "r", "markeredgecolor", "r")
line2 = ax2.plot(freqs[pltrange], power[pltrange], "r", lw=2)
ax2.set_xlabel("Frequency [Hz]")
ax2.set_ylabel("Power [dB]")
ax2.grid(which="both", axis="both", linestyle="-.")
# Change colours and labels for magnetic field components
# or currents
if 'H' in outputs[0]:
plt.setp(line1, color='g')
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')
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')
if "H" in outputs[0]:
plt.setp(line1, color="g")
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")
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.show()
# Plotting if no FFT required
else:
fig, ax = plt.subplots(subplot_kw=dict(xlabel='Time [s]',
ylabel=outputtext + ' field strength [V/m]'),
num=rxpath + ' - ' + f[rxpath].attrs['Name'],
figsize=(20, 10), facecolor='w', edgecolor='w')
line = ax.plot(time, outputdata, 'r', lw=2, label=outputtext)
fig, ax = plt.subplots(
subplot_kw=dict(xlabel="Time [s]", ylabel=outputtext + " field strength [V/m]"),
num=rxpath + " - " + f[rxpath].attrs["Name"],
figsize=(20, 10),
facecolor="w",
edgecolor="w",
)
line = ax.plot(time, outputdata, "r", lw=2, label=outputtext)
ax.set_xlim([0, np.amax(time)])
# ax.set_ylim([-15, 20])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
if 'H' in output:
plt.setp(line, color='g')
plt.setp(ax, ylabel=outputtext + ', field strength [A/m]')
elif 'I' in output:
plt.setp(line, color='b')
plt.setp(ax, ylabel=outputtext + ', current [A]')
if "H" in output:
plt.setp(line, color="g")
plt.setp(ax, ylabel=outputtext + ", field strength [A/m]")
elif "I" in output:
plt.setp(line, color="b")
plt.setp(ax, ylabel=outputtext + ", current [A]")
# If multiple outputs required, create all nine subplots and
# populate only the specified ones
else:
fig, ax = plt.subplots(subplot_kw=dict(xlabel='Time [s]'),
num=rxpath + ' - ' + f[rxpath].attrs['Name'],
figsize=(20, 10), facecolor='w', edgecolor='w')
fig, ax = plt.subplots(
subplot_kw=dict(xlabel="Time [s]"),
num=rxpath + " - " + f[rxpath].attrs["Name"],
figsize=(20, 10),
facecolor="w",
edgecolor="w",
)
if len(outputs) == 9:
gs = gridspec.GridSpec(3, 3, hspace=0.3, wspace=0.3)
else:
@@ -200,9 +209,9 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
for output in outputs:
# Check for polarity of output and if requested output
# is in file
if output[-1] == 'm':
if output[-1] == "m":
polarity = -1
outputtext = '-' + output[0:-1]
outputtext = "-" + output[0:-1]
output = output[0:-1]
else:
polarity = 1
@@ -210,66 +219,67 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
# Check if requested output is in file
if output not in availableoutputs:
logger.exception(f"Output(s) requested to plot: " +
f"{', '.join(outputs)}, but available output(s) " +
f"for receiver {rx} in the file: " +
f"{', '.join(availableoutputs)}")
logger.exception(
f"Output(s) requested to plot: "
+ f"{', '.join(outputs)}, but available output(s) "
+ f"for receiver {rx} in the file: "
+ f"{', '.join(availableoutputs)}"
)
raise ValueError
outputdata = f[rxpath + output][:] * polarity
if output == 'Ex':
if output == "Ex":
ax = plt.subplot(gs[0, 0])
ax.plot(time, outputdata, 'r', lw=2, label=outputtext)
ax.set_ylabel(outputtext + ', field strength [V/m]')
ax.plot(time, outputdata, "r", lw=2, label=outputtext)
ax.set_ylabel(outputtext + ", field strength [V/m]")
# ax.set_ylim([-15, 20])
elif output == 'Ey':
elif output == "Ey":
ax = plt.subplot(gs[1, 0])
ax.plot(time, outputdata, 'r', lw=2, label=outputtext)
ax.set_ylabel(outputtext + ', field strength [V/m]')
ax.plot(time, outputdata, "r", lw=2, label=outputtext)
ax.set_ylabel(outputtext + ", field strength [V/m]")
# ax.set_ylim([-15, 20])
elif output == 'Ez':
elif output == "Ez":
ax = plt.subplot(gs[2, 0])
ax.plot(time, outputdata, 'r', lw=2, label=outputtext)
ax.set_ylabel(outputtext + ', field strength [V/m]')
ax.plot(time, outputdata, "r", lw=2, label=outputtext)
ax.set_ylabel(outputtext + ", field strength [V/m]")
# ax.set_ylim([-15, 20])
elif output == 'Hx':
elif output == "Hx":
ax = plt.subplot(gs[0, 1])
ax.plot(time, outputdata, 'g', lw=2, label=outputtext)
ax.set_ylabel(outputtext + ', field strength [A/m]')
ax.plot(time, outputdata, "g", lw=2, label=outputtext)
ax.set_ylabel(outputtext + ", field strength [A/m]")
# ax.set_ylim([-0.03, 0.03])
elif output == 'Hy':
elif output == "Hy":
ax = plt.subplot(gs[1, 1])
ax.plot(time, outputdata, 'g', lw=2, label=outputtext)
ax.set_ylabel(outputtext + ', field strength [A/m]')
ax.plot(time, outputdata, "g", lw=2, label=outputtext)
ax.set_ylabel(outputtext + ", field strength [A/m]")
# ax.set_ylim([-0.03, 0.03])
elif output == 'Hz':
elif output == "Hz":
ax = plt.subplot(gs[2, 1])
ax.plot(time, outputdata, 'g', lw=2, label=outputtext)
ax.set_ylabel(outputtext + ', field strength [A/m]')
ax.plot(time, outputdata, "g", lw=2, label=outputtext)
ax.set_ylabel(outputtext + ", field strength [A/m]")
# ax.set_ylim([-0.03, 0.03])
elif output == 'Ix':
elif output == "Ix":
ax = plt.subplot(gs[0, 2])
ax.plot(time, outputdata, 'b', lw=2, label=outputtext)
ax.set_ylabel(outputtext + ', current [A]')
elif output == 'Iy':
ax.plot(time, outputdata, "b", lw=2, label=outputtext)
ax.set_ylabel(outputtext + ", current [A]")
elif output == "Iy":
ax = plt.subplot(gs[1, 2])
ax.plot(time, outputdata, 'b', lw=2, label=outputtext)
ax.set_ylabel(outputtext + ', current [A]')
elif output == 'Iz':
ax.plot(time, outputdata, "b", lw=2, label=outputtext)
ax.set_ylabel(outputtext + ", current [A]")
elif output == "Iz":
ax = plt.subplot(gs[2, 2])
ax.plot(time, outputdata, 'b', lw=2, label=outputtext)
ax.set_ylabel(outputtext + ', current [A]')
ax.plot(time, outputdata, "b", lw=2, label=outputtext)
ax.set_ylabel(outputtext + ", current [A]")
for ax in fig.axes:
ax.set_xlim([0, np.amax(time)])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
f.close()
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)
@@ -278,23 +288,44 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False):
if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(description='Plots electric and magnetic fields and ' +
'currents from all receiver points in the given output file. ' +
'Each receiver point is plotted in a new figure window.',
usage='cd gprMax; python -m toolboxes.Plotting.plot_Ascan outputfile')
parser.add_argument('outputfile', help='name of output file including path')
parser.add_argument('--outputs', help='outputs to be plotted',
parser = argparse.ArgumentParser(
description="Plots electric and magnetic fields and "
+ "currents from all receiver points in the given output file. "
+ "Each receiver point is plotted in a new figure window.",
usage="cd gprMax; python -m toolboxes.Plotting.plot_Ascan outputfile",
)
parser.add_argument("outputfile", help="name of output file including path")
parser.add_argument(
"--outputs",
help="outputs to be plotted",
default=Rx.defaultoutputs,
choices=['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz',
'Ix', 'Iy', 'Iz', 'Ex-', 'Ey-', 'Ez-',
'Hx-', 'Hy-', 'Hz-', 'Ix-', 'Iy-', 'Iz-'],
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')
choices=[
"Ex",
"Ey",
"Ez",
"Hx",
"Hy",
"Hz",
"Ix",
"Iy",
"Iz",
"Ex-",
"Ey-",
"Ez-",
"Hx-",
"Hy-",
"Hz-",
"Ix-",
"Iy-",
"Iz-",
],
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"
)
args = parser.parse_args()
plthandle = mpl_plot(args.outputfile, args.outputs, fft=args.fft, save=args.save)

查看文件

@@ -46,30 +46,34 @@ 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')
plt.imshow(outputdata, extent=[0, outputdata.shape[1], outputdata.shape[0] * dt, 0],
interpolation='nearest', aspect='auto', cmap='seismic',
vmin=-np.amax(np.abs(outputdata)), vmax=np.amax(np.abs(outputdata)))
plt.xlabel('Trace number')
plt.ylabel('Time [s]')
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],
interpolation="nearest",
aspect="auto",
cmap="seismic",
vmin=-np.amax(np.abs(outputdata)),
vmax=np.amax(np.abs(outputdata)),
)
plt.xlabel("Trace number")
plt.ylabel("Time [s]")
# Grid properties
ax = fig.gca()
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
cb = plt.colorbar()
if 'E' in rxcomponent:
cb.set_label('Field strength [V/m]')
elif 'H' in rxcomponent:
cb.set_label('Field strength [A/m]')
elif 'I' in rxcomponent:
cb.set_label('Current [A]')
if "E" in rxcomponent:
cb.set_label("Field strength [V/m]")
elif "H" in rxcomponent:
cb.set_label("Field strength [A/m]")
elif "I" in rxcomponent:
cb.set_label("Current [A]")
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)
@@ -78,27 +82,33 @@ def mpl_plot(filename, outputdata, dt, rxnumber, rxcomponent, save=False):
if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(description='Plots a B-scan image.',
usage='cd gprMax; python -m toolboxes.Plotting.plot_Bscan outputfile output')
parser.add_argument('outputfile', help='name of output file including path')
parser.add_argument('rx_component', help='name of output component to be plotted',
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')
parser.add_argument('-save', action='store_true', default=False,
help='save plot directly to file, i.e. do not display')
parser = argparse.ArgumentParser(
description="Plots a B-scan image.",
usage="cd gprMax; python -m toolboxes.Plotting.plot_Bscan outputfile output",
)
parser.add_argument("outputfile", help="name of output file including path")
parser.add_argument(
"rx_component",
help="name of output component to be plotted",
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"
)
parser.add_argument(
"-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display"
)
args = parser.parse_args()
# Open output file and read number of outputs (receivers)
f = h5py.File(args.outputfile, 'r')
nrx = f.attrs['nrx']
f = h5py.File(args.outputfile, "r")
nrx = f.attrs["nrx"]
f.close()
# Check there are any receivers
if nrx == 0:
logger.exception(f'No receivers found in {args.outputfile}')
logger.exception(f"No receivers found in {args.outputfile}")
raise ValueError
for rx in range(1, nrx + 1):
@@ -108,12 +118,10 @@ 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()

查看文件

@@ -45,29 +45,29 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N
# Open output file and read some attributes
file = Path(filename)
f = h5py.File(file, 'r')
dxdydz = f.attrs['dx_dy_dz']
dt = f.attrs['dt']
iterations = f.attrs['Iterations']
f = h5py.File(file, "r")
dxdydz = f.attrs["dx_dy_dz"]
dt = f.attrs["dt"]
iterations = f.attrs["Iterations"]
# Calculate time array and frequency bin spacing
time = np.linspace(0, (iterations - 1) * dt, num=iterations)
df = 1 / np.amax(time)
logger.info(f'Time window: {np.amax(time):g} s ({iterations} iterations)')
logger.info(f'Time step: {dt:g} s')
logger.info(f'Frequency bin spacing: {df:g} Hz')
logger.info(f"Time window: {np.amax(time):g} s ({iterations} iterations)")
logger.info(f"Time step: {dt:g} s")
logger.info(f"Frequency bin spacing: {df:g} Hz")
# Read/calculate voltages and currents from transmitter antenna
tltxpath = '/tls/tl' + str(tltxnumber) + '/'
tltxpath = "/tls/tl" + str(tltxnumber) + "/"
# Incident voltages/currents
Vinc = f[tltxpath + 'Vinc'][:]
Iinc = f[tltxpath + 'Iinc'][:]
Vinc = f[tltxpath + "Vinc"][:]
Iinc = f[tltxpath + "Iinc"][:]
# Total (incident + reflected) voltages/currents
Vtotal = f[tltxpath + 'Vtotal'][:]
Itotal = f[tltxpath + 'Itotal'][:]
Vtotal = f[tltxpath + "Vtotal"][:]
Itotal = f[tltxpath + "Itotal"][:]
# Reflected voltages/currents
Vref = Vtotal - Vinc
@@ -76,27 +76,29 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N
# If a receiver antenna is used (with a transmission line or receiver),
# get received voltage for s21
if tlrxnumber:
tlrxpath = '/tls/tl' + str(tlrxnumber) + '/'
Vrec = f[tlrxpath + 'Vtotal'][:]
tlrxpath = "/tls/tl" + str(tlrxnumber) + "/"
Vrec = f[tlrxpath + "Vtotal"][:]
elif rxnumber:
rxpath = '/rxs/rx' + str(rxnumber) + '/'
rxpath = "/rxs/rx" + str(rxnumber) + "/"
availableoutputs = list(f[rxpath].keys())
if rxcomponent not in availableoutputs:
logger.exception(f"{rxcomponent} output requested, but the available " +
f"output for receiver {rxnumber} is " +
f"{', '.join(availableoutputs)}")
logger.exception(
f"{rxcomponent} output requested, but the available "
+ f"output for receiver {rxnumber} is "
+ f"{', '.join(availableoutputs)}"
)
raise ValueError
rxpath += rxcomponent
# Received voltage
if rxcomponent == 'Ex':
if rxcomponent == "Ex":
Vrec = f[rxpath][:] * -1 * dxdydz[0]
elif rxcomponent == 'Ey':
elif rxcomponent == "Ey":
Vrec = f[rxpath][:] * -1 * dxdydz[1]
elif rxcomponent == 'Ez':
elif rxcomponent == "Ez":
Vrec = f[rxpath][:] * -1 * dxdydz[2]
f.close()
@@ -119,7 +121,7 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N
yin = np.fft.fft(Itotal) / (np.fft.fft(Vtotal) * delaycorrection)
# Convert to decibels (ignore warning from taking a log of any zero values)
with np.errstate(divide='ignore'):
with np.errstate(divide="ignore"):
Vincp = 20 * np.log10(np.abs((np.fft.fft(Vinc) * delaycorrection)))
Iincp = 20 * np.log10(np.abs(np.fft.fft(Iinc)))
Vrefp = 20 * np.log10(np.abs((np.fft.fft(Vref) * delaycorrection)))
@@ -138,23 +140,56 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N
s11[np.invert(np.isfinite(s11))] = 0
# Create dictionary of antenna parameters
antennaparams = {'time': time, 'freqs': freqs, 'Vinc': Vinc, 'Vincp': Vincp,
'Iinc': Iinc, 'Iincp': Iincp, 'Vref': Vref, 'Vrefp': Vrefp,
'Iref': Iref, 'Irefp': Irefp, 'Vtotal': Vtotal,
'Vtotalp': Vtotalp, 'Itotal': Itotal, 'Itotalp': Itotalp,
's11': s11, 'zin': zin, 'yin': yin}
antennaparams = {
"time": time,
"freqs": freqs,
"Vinc": Vinc,
"Vincp": Vincp,
"Iinc": Iinc,
"Iincp": Iincp,
"Vref": Vref,
"Vrefp": Vrefp,
"Iref": Iref,
"Irefp": Irefp,
"Vtotal": Vtotal,
"Vtotalp": Vtotalp,
"Itotal": Itotal,
"Itotalp": Itotalp,
"s11": s11,
"zin": zin,
"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
antennaparams["s21"] = s21
return antennaparams
def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp,
Iref, Irefp, Vtotal, Vtotalp, Itotal, Itotalp, s11, zin, yin,
s21=None, save=False):
def mpl_plot(
filename,
time,
freqs,
Vinc,
Vincp,
Iinc,
Iincp,
Vref,
Vrefp,
Iref,
Irefp,
Vtotal,
Vtotalp,
Itotal,
Itotalp,
s11,
zin,
yin,
s21=None,
save=False,
):
"""Plots antenna parameters - incident, reflected and total voltages and
currents; s11, (s21) and input impedance.
@@ -189,105 +224,103 @@ def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp,
# 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'At {freqs[s11minfreq + pltrangemin]:g} Hz...')
logger.info(f'Input impedance: {np.abs(zin[s11minfreq + pltrangemin]):.1f}' +
f'{zin[s11minfreq + pltrangemin].imag:+.1f}j Ohms')
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}"
+ f"{zin[s11minfreq + pltrangemin].imag:+.1f}j Ohms"
)
# logger.info(f'Input admittance (mag): {np.abs(yin[s11minfreq + pltrangemin]):g} S')
# logger.info(f'Input admittance (phase): {np.angle(yin[s11minfreq + pltrangemin], deg=True):.1f} deg')
# Figure 1
# Plot incident voltage
fig1, ax = plt.subplots(num='Transmitter transmission line parameters',
figsize=(20, 12), facecolor='w', edgecolor='w')
fig1, ax = plt.subplots(
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])
ax.plot(time, Vinc, 'r', lw=2, label='Vinc')
ax.set_title('Incident voltage')
ax.set_xlabel('Time [s]')
ax.set_ylabel('Voltage [V]')
ax.plot(time, Vinc, "r", lw=2, label="Vinc")
ax.set_title("Incident voltage")
ax.set_xlabel("Time [s]")
ax.set_ylabel("Voltage [V]")
ax.set_xlim([0, np.amax(time)])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
# Plot frequency spectra of incident voltage
ax = plt.subplot(gs1[0, 1])
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vincp[pltrange],
'-.', use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'r')
plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r')
ax.plot(freqs[pltrange], Vincp[pltrange], 'r', lw=2)
ax.set_title('Incident voltage')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Power [dB]')
ax.grid(which='both', axis='both', linestyle='-.')
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vincp[pltrange], "-.", use_line_collection=True)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "r")
plt.setp(markerline, "markerfacecolor", "r", "markeredgecolor", "r")
ax.plot(freqs[pltrange], Vincp[pltrange], "r", lw=2)
ax.set_title("Incident voltage")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Power [dB]")
ax.grid(which="both", axis="both", linestyle="-.")
# Plot incident current
ax = plt.subplot(gs1[1, 0])
ax.plot(time, Iinc, 'b', lw=2, label='Vinc')
ax.set_title('Incident current')
ax.set_xlabel('Time [s]')
ax.set_ylabel('Current [A]')
ax.plot(time, Iinc, "b", lw=2, label="Vinc")
ax.set_title("Incident current")
ax.set_xlabel("Time [s]")
ax.set_ylabel("Current [A]")
ax.set_xlim([0, np.amax(time)])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
# Plot frequency spectra of incident current
ax = plt.subplot(gs1[1, 1])
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Iincp[pltrange],
'-.', use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'b')
plt.setp(markerline, 'markerfacecolor', 'b', 'markeredgecolor', 'b')
ax.plot(freqs[pltrange], Iincp[pltrange], 'b', lw=2)
ax.set_title('Incident current')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Power [dB]')
ax.grid(which='both', axis='both', linestyle='-.')
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Iincp[pltrange], "-.", use_line_collection=True)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "b")
plt.setp(markerline, "markerfacecolor", "b", "markeredgecolor", "b")
ax.plot(freqs[pltrange], Iincp[pltrange], "b", lw=2)
ax.set_title("Incident current")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Power [dB]")
ax.grid(which="both", axis="both", linestyle="-.")
# Plot total voltage
ax = plt.subplot(gs1[2, 0])
ax.plot(time, Vtotal, 'r', lw=2, label='Vinc')
ax.set_title('Total (incident + reflected) voltage')
ax.set_xlabel('Time [s]')
ax.set_ylabel('Voltage [V]')
ax.plot(time, Vtotal, "r", lw=2, label="Vinc")
ax.set_title("Total (incident + reflected) voltage")
ax.set_xlabel("Time [s]")
ax.set_ylabel("Voltage [V]")
ax.set_xlim([0, np.amax(time)])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
# Plot frequency spectra of total voltage
ax = plt.subplot(gs1[2, 1])
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vtotalp[pltrange],
'-.', use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'r')
plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r')
ax.plot(freqs[pltrange], Vtotalp[pltrange], 'r', lw=2)
ax.set_title('Total (incident + reflected) voltage')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Power [dB]')
ax.grid(which='both', axis='both', linestyle='-.')
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vtotalp[pltrange], "-.", use_line_collection=True)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "r")
plt.setp(markerline, "markerfacecolor", "r", "markeredgecolor", "r")
ax.plot(freqs[pltrange], Vtotalp[pltrange], "r", lw=2)
ax.set_title("Total (incident + reflected) voltage")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Power [dB]")
ax.grid(which="both", axis="both", linestyle="-.")
# Plot total current
ax = plt.subplot(gs1[3, 0])
ax.plot(time, Itotal, 'b', lw=2, label='Vinc')
ax.set_title('Total (incident + reflected) current')
ax.set_xlabel('Time [s]')
ax.set_ylabel('Current [A]')
ax.plot(time, Itotal, "b", lw=2, label="Vinc")
ax.set_title("Total (incident + reflected) current")
ax.set_xlabel("Time [s]")
ax.set_ylabel("Current [A]")
ax.set_xlim([0, np.amax(time)])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
# Plot frequency spectra of total current
ax = plt.subplot(gs1[3, 1])
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Itotalp[pltrange],
'-.', use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'b')
plt.setp(markerline, 'markerfacecolor', 'b', 'markeredgecolor', 'b')
ax.plot(freqs[pltrange], Itotalp[pltrange], 'b', lw=2)
ax.set_title('Total (incident + reflected) current')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Power [dB]')
ax.grid(which='both', axis='both', linestyle='-.')
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Itotalp[pltrange], "-.", use_line_collection=True)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "b")
plt.setp(markerline, "markerfacecolor", "b", "markeredgecolor", "b")
ax.plot(freqs[pltrange], Itotalp[pltrange], "b", lw=2)
ax.set_title("Total (incident + reflected) current")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Power [dB]")
ax.grid(which="both", axis="both", linestyle="-.")
# Plot reflected (reflected) voltage
# ax = plt.subplot(gs1[4, 0])
@@ -335,69 +368,64 @@ def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp,
# 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],
'-.', use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'g')
plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g')
ax.plot(freqs[pltrange], s11[pltrange], 'g', lw=2)
ax.set_title('s11')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Power [dB]')
markerline, stemlines, baseline = ax.stem(freqs[pltrange], s11[pltrange], "-.", use_line_collection=True)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "g")
plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g")
ax.plot(freqs[pltrange], s11[pltrange], "g", lw=2)
ax.set_title("s11")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Power [dB]")
# ax.set_xlim([0, 5e9])
# ax.set_ylim([-25, 0])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
# Plot frequency spectra of s21
if s21 is not None:
ax = plt.subplot(gs2[0, 1])
markerline, stemlines, baseline = ax.stem(freqs[pltrange], s21[pltrange],
'-.', use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'g')
plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g')
ax.plot(freqs[pltrange], s21[pltrange], 'g', lw=2)
ax.set_title('s21')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Power [dB]')
markerline, stemlines, baseline = ax.stem(freqs[pltrange], s21[pltrange], "-.", use_line_collection=True)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "g")
plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g")
ax.plot(freqs[pltrange], s21[pltrange], "g", lw=2)
ax.set_title("s21")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Power [dB]")
# ax.set_xlim([0.88e9, 1.02e9])
# ax.set_ylim([-25, 50])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
# Plot input resistance (real part of impedance)
ax = plt.subplot(gs2[1, 0])
markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].real,
'-.', use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'g')
plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g')
ax.plot(freqs[pltrange], zin[pltrange].real, 'g', lw=2)
ax.set_title('Input impedance (resistive)')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Resistance [Ohms]')
markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].real, "-.", use_line_collection=True)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "g")
plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g")
ax.plot(freqs[pltrange], zin[pltrange].real, "g", lw=2)
ax.set_title("Input impedance (resistive)")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Resistance [Ohms]")
# ax.set_xlim([0.88e9, 1.02e9])
ax.set_ylim(bottom=0)
# ax.set_ylim([0, 300])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
# Plot input reactance (imaginery part of impedance)
ax = plt.subplot(gs2[1, 1])
markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].imag,
'-.', use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'g')
plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g')
ax.plot(freqs[pltrange], zin[pltrange].imag, 'g', lw=2)
ax.set_title('Input impedance (reactive)')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Reactance [Ohms]')
markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].imag, "-.", use_line_collection=True)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "g")
plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g")
ax.plot(freqs[pltrange], zin[pltrange].imag, "g", lw=2)
ax.set_title("Input impedance (reactive)")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Reactance [Ohms]")
# ax.set_xlim([0.88e9, 1.02e9])
# ax.set_ylim([-300, 300])
ax.grid(which='both', axis='both', linestyle='-.')
ax.grid(which="both", axis="both", linestyle="-.")
# Plot input admittance (magnitude)
# ax = plt.subplot(gs2[2, 0])
@@ -430,15 +458,13 @@ def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp,
# ax.grid(which='both', axis='both', linestyle='-.')
if save:
savename1 = filename.stem + '_tl_params'
savename1 = filename.stem + "_tl_params"
savename1 = filename.parent / savename1
savename2 = filename.stem + '_ant_params'
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)
@@ -449,30 +475,32 @@ def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp,
if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(description='Plots antenna parameters - ' +
'incident, reflected and total voltages ' +
'and currents; s11, (s21) and input impedance ' +
'from an output file containing a transmission ' +
'line source.',
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('--rx-num', type=int,
help='receiver antenna - output number')
parser.add_argument('--rx-component', type=str,
help='receiver antenna - output electric field component',
choices=['Ex', 'Ey', 'Ez'])
parser.add_argument('-save', action='store_true', default=False,
help='save plot directly to file, i.e. do not display')
parser = argparse.ArgumentParser(
description="Plots antenna parameters - "
+ "incident, reflected and total voltages "
+ "and currents; s11, (s21) and input impedance "
+ "from an output file containing a transmission "
+ "line source.",
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("--rx-num", type=int, help="receiver antenna - output number")
parser.add_argument(
"--rx-component",
type=str,
help="receiver antenna - output electric field component",
choices=["Ex", "Ey", "Ez"],
)
parser.add_argument(
"-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display"
)
args = parser.parse_args()
antennaparams = calculate_antenna_params(args.outputfile, args.tltx_num,
args.tlrx_num, args.rx_num,
args.rx_component)
antennaparams = calculate_antenna_params(
args.outputfile, args.tltx_num, args.tlrx_num, args.rx_num, args.rx_component
)
plthandle = mpl_plot(args.outputfile, **antennaparams, save=args.save)
plthandle.show()

查看文件

@@ -25,7 +25,7 @@ import numpy as np
from gprMax.utilities.utilities import fft_power, round_value
from gprMax.waveforms import Waveform
logging.basicConfig(format='%(message)s', level=logging.INFO)
logging.basicConfig(format="%(message)s", level=logging.INFO)
def check_timewindow(timewindow, dt):
@@ -53,7 +53,7 @@ def check_timewindow(timewindow, dt):
if timewindow > 0:
iterations = round_value((timewindow / dt)) + 1
else:
logging.exception('Time window must have a value greater than zero')
logging.exception("Time window must have a value greater than zero")
raise ValueError
return timewindow, iterations
@@ -76,36 +76,41 @@ def mpl_plot(w, timewindow, dt, iterations, fft=False, save=False):
time = np.linspace(0, (iterations - 1) * dt, num=iterations)
waveform = np.zeros(len(time))
timeiter = np.nditer(time, flags=['c_index'])
timeiter = np.nditer(time, flags=["c_index"])
while not timeiter.finished:
waveform[timeiter.index] = w.calculate_value(timeiter[0], dt)
timeiter.iternext()
logging.info('Waveform characteristics...')
logging.info(f'Type: {w.type}')
logging.info(f'Maximum (absolute) amplitude: {np.max(np.abs(waveform)):g}')
logging.info("Waveform characteristics...")
logging.info(f"Type: {w.type}")
logging.info(f"Maximum (absolute) amplitude: {np.max(np.abs(waveform)):g}")
if w.freq and not w.type == 'gaussian' and not w.type == 'impulse':
logging.info(f'Centre frequency: {w.freq:g} Hz')
if w.freq and not w.type == "gaussian" and not w.type == "impulse":
logging.info(f"Centre frequency: {w.freq:g} Hz")
if (w.type == 'gaussian' or w.type == 'gaussiandot' or w.type == 'gaussiandotnorm'
or w.type == 'gaussianprime' or w.type == 'gaussiandoubleprime'):
if (
w.type == "gaussian"
or w.type == "gaussiandot"
or w.type == "gaussiandotnorm"
or w.type == "gaussianprime"
or w.type == "gaussiandoubleprime"
):
delay = 1 / w.freq
logging.info(f'Time to centre of pulse: {delay:g} s')
elif w.type == 'gaussiandotdot' or w.type == 'gaussiandotdotnorm' or w.type == 'ricker':
logging.info(f"Time to centre of pulse: {delay:g} s")
elif w.type == "gaussiandotdot" or w.type == "gaussiandotdotnorm" or w.type == "ricker":
delay = np.sqrt(2) / w.freq
logging.info(f'Time to centre of pulse: {delay:g} s')
logging.info(f"Time to centre of pulse: {delay:g} s")
logging.info(f'Time window: {timewindow:g} s ({iterations} iterations)')
logging.info(f'Time step: {dt:g} s')
logging.info(f"Time window: {timewindow:g} s ({iterations} iterations)")
logging.info(f"Time step: {dt:g} s")
if fft:
# FFT
freqs, power = fft_power(waveform, dt)
# Set plotting range to 4 times frequency at max power of waveform or
# 4 times the centre frequency
# 4 times the centre frequency
freqmaxpower = np.where(np.isclose(power, 0))[0][0]
if freqs[freqmaxpower] > w.freq:
pltrange = np.where(freqs > 4 * freqs[freqmaxpower])[0][0]
@@ -113,73 +118,66 @@ 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)
ax1.set_xlabel('Time [s]')
ax1.set_ylabel('Amplitude')
ax1.plot(time, waveform, "r", lw=2)
ax1.set_xlabel("Time [s]")
ax1.set_ylabel("Amplitude")
# Plot frequency spectra
markerline, stemlines, baseline = ax2.stem(freqs[pltrange], power[pltrange],
'-.', use_line_collection=True)
plt.setp(baseline, 'linewidth', 0)
plt.setp(stemlines, 'color', 'r')
plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r')
ax2.plot(freqs[pltrange], power[pltrange], 'r', lw=2)
ax2.set_xlabel('Frequency [Hz]')
ax2.set_ylabel('Power [dB]')
markerline, stemlines, baseline = ax2.stem(freqs[pltrange], power[pltrange], "-.", use_line_collection=True)
plt.setp(baseline, "linewidth", 0)
plt.setp(stemlines, "color", "r")
plt.setp(markerline, "markerfacecolor", "r", "markeredgecolor", "r")
ax2.plot(freqs[pltrange], power[pltrange], "r", lw=2)
ax2.set_xlabel("Frequency [Hz]")
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)
ax1.set_xlabel('Time [s]')
ax1.set_ylabel('Amplitude')
ax1.plot(time, waveform, "r", lw=2)
ax1.set_xlabel("Time [s]")
ax1.set_ylabel("Amplitude")
# Turn on grid
[ax.grid(which='both', axis='both', linestyle='-.') for ax in fig.axes]
[ax.grid(which="both", axis="both", linestyle="-.") for ax in fig.axes]
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
if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(description='Plot built-in waveforms that can be used for sources.',
usage='cd gprMax; python -m toolboxes.Plotting.plot_source_wave type amp freq timewindow dt')
parser.add_argument('type', help='type of waveform', choices=Waveform.types)
parser.add_argument('amp', type=float, help='amplitude of waveform')
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')
parser = argparse.ArgumentParser(
description="Plot built-in waveforms that can be used for sources.",
usage="cd gprMax; python -m toolboxes.Plotting.plot_source_wave type amp freq timewindow dt",
)
parser.add_argument("type", help="type of waveform", choices=Waveform.types)
parser.add_argument("amp", type=float, help="amplitude of waveform")
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"
)
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 " +
f"{', '.join(Waveform.types)}")
logging.exception(f"The waveform must have one of the following types " + f"{', '.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
@@ -189,6 +187,5 @@ 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()

查看文件

@@ -15,7 +15,7 @@ z = 0.148
scene = gprMax.Scene()
title = gprMax.Title(name=fn.with_suffix('').name)
title = gprMax.Title(name=fn.with_suffix("").name)
domain = gprMax.Domain(p1=(x, y, z))
dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
time_window = gprMax.TimeWindow(time=10e-9)
@@ -25,11 +25,11 @@ scene.add(domain)
scene.add(dxdydz)
scene.add(time_window)
go = gprMax.GeometryObjectsRead(p1=(0.020, 0.020, 0.020), geofile='stl/Stanford_Bunny.h5', matfile= 'materials.txt')
go = gprMax.GeometryObjectsRead(p1=(0.020, 0.020, 0.020), geofile="stl/Stanford_Bunny.h5", matfile="materials.txt")
gv = gprMax.GeometryView(p1=(0, 0, 0), p2=domain.props.p1,
dl=(dl, dl, dl), filename=fn.with_suffix('').name,
output_type='n')
gv = gprMax.GeometryView(
p1=(0, 0, 0), p2=domain.props.p1, dl=(dl, dl, dl), filename=fn.with_suffix("").name, output_type="n"
)
scene.add(go)
scene.add(gv)

查看文件

@@ -4,16 +4,16 @@ from functools import reduce
def lines_to_voxels(line_list, pixels):
current_line_indices = set()
x = 0
for (event_x, status, line_ind) in generate_line_events(line_list):
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, [])
paint_y_axis(lines, pixels, x)
x += 1
if status == 'start':
if status == "start":
assert line_ind not in current_line_indices
current_line_indices.add(line_ind)
elif status == 'end':
elif status == "end":
assert line_ind in current_line_indices
current_line_indices.remove(line_ind)
@@ -54,7 +54,7 @@ def paint_y_axis(lines, pixels, x):
pixels[target_y][x] = True
is_black = not is_black
yi = target_y
assert is_black is False, 'an error has occured at x%s' % x
assert is_black is False, "an error has occured at x%s" % x
def generate_line_events(line_list):
@@ -62,7 +62,7 @@ def generate_line_events(line_list):
for i, line in enumerate(line_list):
first, second = sorted(line, key=lambda pt: pt[0])
events.append((first[0], 'start', i))
events.append((second[0], 'end', i))
events.append((first[0], "start", i))
events.append((second[0], "end", i))
return sorted(events, key=lambda tup: tup[0])

查看文件

@@ -30,10 +30,10 @@ def mesh_to_plane(mesh, bounding_box, parallel):
vol[z] = pixels
z += 1
if status == 'start':
if status == "start":
assert tri_ind not in current_mesh_indices
current_mesh_indices.add(tri_ind)
elif status == 'end':
elif status == "end":
assert tri_ind in current_mesh_indices
current_mesh_indices.remove(tri_ind)
@@ -61,17 +61,17 @@ def paint_z_plane(mesh, height, plane_shape):
def linear_interpolation(p1, p2, distance):
'''
"""
:param p1: Point 1
:param p2: Point 2
:param distance: Between 0 and 1, Lower numbers return points closer to p1.
:return: A point on the line between p1 and p2
'''
"""
return p1 * (1 - distance) + p2 * distance
def triangle_to_intersecting_lines(triangle, height, pixels, lines):
assert (len(triangle) == 3)
assert len(triangle) == 3
above = list(filter(lambda pt: pt[2] > height, triangle))
below = list(filter(lambda pt: pt[2] < height, triangle))
same = list(filter(lambda pt: pt[2] == height, triangle))
@@ -100,7 +100,7 @@ def triangle_to_intersecting_lines(triangle, height, pixels, lines):
def where_line_crosses_z(p1, p2, z):
if (p1[2] > p2[2]):
if p1[2] > p2[2]:
p1, p2 = p2, p1
# now p1 is below p2 in z
if p2[2] == p1[2]:
@@ -142,7 +142,7 @@ def generate_tri_events(mesh):
events = []
for i, tri in enumerate(mesh):
bottom, middle, top = sorted(tri, key=lambda pt: pt[2])
events.append((bottom[2], 'start', i))
events.append((top[2], 'end', i))
events.append((bottom[2], "start", i))
events.append((top[2], "end", i))
return sorted(events, key=lambda tup: tup[0])

某些文件未显示,因为此 diff 中更改的文件太多 显示更多