Re-structuring package layout

这个提交包含在:
Craig Warren
2022-11-09 09:29:23 +00:00
父节点 50d17f33ef
当前提交 16df30968e
共有 269 个文件被更改,包括 14 次插入261 次删除

查看文件

@@ -0,0 +1,54 @@
User libraries is a sub-package where useful Python modules contributed by users are stored.
****************
Antenna Patterns
****************
Information
===========
**Author/Contact**: Craig Warren (craig.warren@northumbria.ac.uk), Northumbria University, UK
**License**: `Creative Commons Attribution-ShareAlike 4.0 International License <http://creativecommons.org/licenses/by-sa/4.0/>`_
**Attribution/cite**: Warren, C., Giannopoulos, A. (2016). Characterisation of a Ground Penetrating Radar Antenna in Lossless Homogeneous and Lossy Heterogeneous Environments. *Signal Processing* (http://dx.doi.org/10.1016/j.sigpro.2016.04.010)
The package contains scripts to help calculate, process, and visualise field patterns from simulations that contain models of antennas.
.. warning::
Although the principals of calculating and visualising field patterns are straightforward, this package should be used with care. The package:
* Does not calculate/plot conventional field patterns, i.e. at a single frequency. It uses a measure of the total energy of the electric field at a certain angle and radius, see http://dx.doi.org/10.1016/j.jappgeo.2013.08.001
* Requires knowledge of Python to contruct input files with antenna models and positioning of receivers, as well as to edit/modify the saving and processing modules
* Can require simulations that demand significant computational resource depending on the distance from the antenna at which the field patterns are observed, e.g. the example models, set with a maximum observation distance of 0.6m, require ~30GB of RAM
Package contents
================
* ``initial_save.py`` is a module that calculates and stores (in a Numpy file) the field patterns from the output file of a simulation.
* ``plot_fields.py`` is a module that plots the field patterns. It should be used after the field pattern data has been processed and stored using the ``initial_save.py`` module.
The package has been designed to work with input files that follow examples found in the ``user_models`` directory:
* ``antenna_like_GSSI_1500_patterns_E.in`` is an input file that includes an antenna model similar to a GSSI 1.5 GHz antenna and receivers to calculate a field pattern in the principal E-plane of the antenna
* ``antenna_like_GSSI_1500_patterns_H.in`` is an input file that includes an antenna model similar to a GSSI 1.5 GHz antenna and receivers to calculate a field pattern in the principal H-plane of the antenna
How to use the package
======================
* Firstly you should familiarise yourself with the example model input file. Edit the input file as desired and run one of the simulations for either E-plane or H-plane patterns.
* Whilst the simulation is running edit the 'user configurable parameters' sections of the ``initial_save.py`` and ``plot_fields.py`` modules to match the setup of the simulation.
* Once the simulation has completed, run the ``initial_save.py`` module on the output file, e.g. for the E-plane ``python -m user_libs.AntennaPatterns.initial_save user_models/antenna_like_GSSI_1500_patterns_E_Er5.h5``. This will produce a Numpy file containing the field pattern data.
* Plot the field pattern data by running the ``plot_fields.py`` module on the Numpy file, e.g. for the E-plane ``python -m user_libs.AntennaPatterns.plot_fields user_models/antenna_like_GSSI_1500_patterns_E_Er5.npy``
.. tip::
If you want to create different plots you just need to edit and re-run the ``plot_fields.py`` module on the Numpy file, i.e. you don't have to re-process the output file.
.. figure:: ../../images_shared/antenna_like_GSSI_1500_patterns_E_Er5.png
:width: 600 px
Example of the E-plane pattern from a simulation containing an antenna model similar to a GSSI 1.5 GHz antenna over a homogeneous, lossless half-space with a relative permittivity of five.

查看文件

@@ -0,0 +1,183 @@
# Copyright (C) 2016, Craig Warren
#
# This module is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
#
# Please use the attribution at http://dx.doi.org/10.1016/j.sigpro.2016.04.010
import argparse
import logging
import os
import sys
import gprMax.config as config
import h5py
import matplotlib.pyplot as plt
import numpy as np
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')
args = parser.parse_args()
outputfile = args.outputfile
########################################
# User configurable parameters
# Pattern type (E or H)
type = 'H'
# Antenna (true if using full antenna model; false for a theoretical Hertzian dipole
antenna = True
# Relative permittivity of half-space for homogeneous materials (set to None for inhomogeneous)
epsr = 5
# Observation radii and angles
radii = np.linspace(0.1, 0.3, 20)
theta = np.linspace(3, 357, 60) * (180 / np.pi)
# Scaling of time-domain field pattern values by material impedance
impscaling = False
# Centre frequency of modelled antenna
f = 1.5e9 # GSSI 1.5GHz antenna model
# Largest dimension of antenna transmitting element
D = 0.060 # GSSI 1.5GHz antenna model
# Traces to plot for sanity checking
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))
wavelength = v1 / f
# Print some useful information
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))
# Load text file with coordinates of pattern origin
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']
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
fs = 1 / dt # Sampling frequency
# Initialise arrays to store fields
coords = np.zeros((nrx, 3), dtype=np.float32)
Ex = np.zeros((iterations, nrx), dtype=np.float32)
Ey = np.zeros((iterations, nrx), dtype=np.float32)
Ez = np.zeros((iterations, nrx), dtype=np.float32)
Hx = np.zeros((iterations, nrx), dtype=np.float32)
Hy = np.zeros((iterations, nrx), dtype=np.float32)
Hz = np.zeros((iterations, nrx), dtype=np.float32)
Er = np.zeros((iterations, nrx), dtype=np.float32)
Etheta = np.zeros((iterations, nrx), dtype=np.float32)
Ephi = np.zeros((iterations, nrx), dtype=np.float32)
Hr = np.zeros((iterations, nrx), dtype=np.float32)
Htheta = np.zeros((iterations, nrx), dtype=np.float32)
Hphi = np.zeros((iterations, nrx), dtype=np.float32)
Ethetasum = np.zeros(len(theta), dtype=np.float32)
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'][:]
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'][:]
f.close()
# Plot traces for sanity checking
# fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6)) = plt.subplots(num=outputfile, nrows=3, ncols=2, sharex=False, sharey='col', subplot_kw=dict(xlabel='Time [ns]'), figsize=(20, 10), facecolor='w', edgecolor='w')
# ax1.plot(time, Ex[:, traceno],'r', lw=2)
# ax1.set_ylabel('$E_x$, field strength [V/m]')
# ax3.plot(time, Ey[:, traceno],'r', lw=2)
# ax3.set_ylabel('$E_y$, field strength [V/m]')
# ax5.plot(time, Ez[:, traceno],'r', lw=2)
# ax5.set_ylabel('$E_z$, field strength [V/m]')
# ax2.plot(time, Hx[:, traceno],'b', lw=2)
# ax2.set_ylabel('$H_x$, field strength [A/m]')
# ax4.plot(time, Hy[:, traceno],'b', lw=2)
# ax4.set_ylabel('$H_y$, field strength [A/m]')
# ax6.plot(time, Hz[:, traceno],'b', lw=2)
# ax6.set_ylabel('$H_z$, field strength [A/m]')
# Turn on grid
# [ax.grid() for ax in fig.axes]
# plt.show()
# Calculate fields for patterns
rxstart = 0 # Index for rx points
for radius in range(0, len(radii)):
index = 0
# Observation points
for pt in range(rxstart, rxstart + len(theta)):
# Cartesian to spherical coordinate transform coefficients from (Kraus,1991,Electromagnetics,p.34)
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))
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
# Fields in spherical coordinates
Er[:, index] = Ex[:, pt] * r1 + Ey[:, pt] * r2 + Ez[:, pt] * r3
Etheta[:, index] = Ex[:, pt] * theta1 + Ey[:, pt] * theta2 + Ez[:, pt] * theta3
Ephi[:, index] = Ex[:, pt] * phi1 + Ey[:, pt] * phi2 + Ez[:, pt] * phi3
Hr[:, index] = Hx[:, pt] * r1 + Hy[:, pt] * r2 + Hz[:, pt] * r3
Htheta[:, index] = Hx[:, pt] * theta1 + Hy[:, pt] * theta2 + Hz[:, pt] * theta3
Hphi[:, index] = Hx[:, pt] * phi1 + Hy[:, pt] * phi2 + Hz[:, pt] * phi3
# Calculate metric for time-domain field pattern values
if impscaling and coords[pt, 2] < 0:
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']
index += 1
if type == 'H':
# Flip H-plane patterns as rx points are written CCW but always plotted CW
patternsave[radius, :] = Hthetasum[::-1]
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]))

查看文件

@@ -0,0 +1,131 @@
# Copyright (C) 2016, Craig Warren
#
# This module is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
#
# Please use the attribution at http://dx.doi.org/10.1016/j.sigpro.2016.04.010
import argparse
import logging
import os
import sys
import gprMax.config as config
import matplotlib.pyplot as plt
import numpy as np
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.add_argument('hertzian', help='name of numpy file including path')
args = parser.parse_args()
patterns = np.load(args.numpyfile)
# hertzian = np.load(args.hertzian)
########################################
# User configurable parameters
# Pattern type (E or H)
type = 'H'
# Relative permittivity of half-space for homogeneous materials (set to None for inhomogeneous)
epsr = 5
# Observation radii and angles
radii = np.linspace(0.1, 0.3, 20)
theta = np.linspace(3, 357, 60)
theta = np.deg2rad(np.append(theta, theta[0])) # Append start value to close circle
# Centre frequency of modelled antenna
f = 1.5e9 # GSSI 1.5GHz antenna model
# Largest dimension of antenna transmitting element
D = 0.060 # GSSI 1.5GHz antenna model
# Minimum value for plotting energy and ring steps (dB)
min = -72
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'])))
wavelength = v1 / f
# Print some useful information
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))
# Setup figure
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))])
# 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')
# Plot patterns
for patt in range(0, len(radii)):
pattplot = np.append(patterns[patt, :], patterns[patt, 0]) # Append start value to close circle
pattplot = pattplot / np.max(np.max(patterns)) # Normalise, based on set of patterns
# Calculate power (ignore warning from taking a log of any zero values)
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)
# Add Hertzian dipole plot
# hertzplot1 = np.append(hertzian[0, :], hertzian[0, 0]) # Append start value to close circle
# hertzplot1 = hertzplot1 / np.max(np.max(hertzian))
# ax.plot(theta, 10 * np.log10(hertzplot1), label='Inf. dipole, 0.1m', color='black', ls='-.', lw=3)
# hertzplot2 = np.append(hertzian[-1, :], hertzian[-1, 0]) # Append start value to close circle
# hertzplot2 = hertzplot2 / np.max(np.max(hertzian))
# 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_thetagrids(np.arange(0, 360, 30))
# Radial axis options
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'
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[-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] + '.png'
# fig.savefig(savename, dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
plt.show()

查看文件

@@ -0,0 +1,58 @@
## Tissue properties
## Non-dispersive properties (900MHz) from http://niremf.ifac.cnr.it/tissprop/htmlclie/htmlclie.php
#material: 1 0 1 0 Air
#material: 44.77 0.6961 1 0 Aorta
#material: 18.94 0.3831 1 0 Bladder
#material: 61.36 1.538 1 0 Blood
#material: 44.77 0.6961 1 0 BloodVessel
#material: 68.9 1.636 1 0 BodyFluid
#material: 20.79 0.34 1 0 BoneCancellous
#material: 12.45 0.1433 1 0 BoneCortical
#material: 5.504 0.04021 1 0 BoneMarrow
#material: 52.73 0.9423 1 0 BrainGreyMatter
#material: 38.89 0.5908 1 0 BrainWhiteMatter
#material: 5.424 0.04896 1 0 BreastFat
#material: 42.65 0.7824 1 0 Cartilage
#material: 49.44 1.263 1 0 Cerebellum
#material: 68.64 2.413 1 0 CerebroSpinalFluid
#material: 49.78 0.9591 1 0 Cervix
#material: 57.94 1.08 1 0 Colon
#material: 55.23 1.394 1 0 Cornea
#material: 65.06 1.187 1 0 Duodenum
#material: 44.43 0.9611 1 0 Dura
#material: 55.27 1.167 1 0 EyeSclera
#material: 5.462 0.05104 1 0 Fat
#material: 59.14 1.257 1 0 GallBladder
#material: 70.19 1.838 1 0 GallBladderBile
#material: 59.68 1.038 1 0 Gland
#material: 59.89 1.23 1 0 Heart
#material: 58.67 1.392 1 0 Kidney
#material: 46.57 0.7934 1 0 Lens
#material: 46.83 0.855 1 0 Liver
#material: 51.42 0.858 1 0 LungDeflated
#material: 22 0.4567 1 0 LungInflated
#material: 59.68 1.038 1 0 Lymph
#material: 46.08 0.8447 1 0 MucousMembrane
#material: 55.03 0.9429 1 0 Muscle
#material: 12.45 0.1433 1 0 Nail
#material: 32.53 0.5737 1 0 Nerve
#material: 65.06 1.187 1 0 Oesophagus
#material: 50.47 1.29 1 0 Ovary
#material: 59.68 1.038 1 0 Pancreas
#material: 60.55 1.21 1 0 Prostate
#material: 55.27 1.167 1 0 Retina
#material: 41.41 0.8667 1 0 SkinDry
#material: 46.08 0.8447 1 0 SkinWet
#material: 59.49 2.165 1 0 SmallIntestine
#material: 32.53 0.5737 1 0 SpinalCord
#material: 57.18 1.273 1 0 Spleen
#material: 65.06 1.187 1 0 Stomach
#material: 45.83 0.7184 1 0 Tendon
#material: 60.55 1.21 1 0 Testis
#material: 59.68 1.038 1 0 Thymus
#material: 59.68 1.038 1 0 Thyroid
#material: 55.27 0.9363 1 0 Tongue
#material: 12.45 0.1433 1 0 Tooth
#material: 42.01 0.7711 1 0 Trachea
#material: 61.12 1.27 1 0 Uterus
#material: 68.9 1.636 1 0 VitreousHumor

查看文件

@@ -0,0 +1,124 @@
## Tissue properties
## 3-Pole Debye model (1MHz-100GHz) from http://dx.doi.org/10.1109/LMWC.2011.2180371
## Non-dispersive properties (900MHz) from http://niremf.ifac.cnr.it/tissprop/htmlclie/htmlclie.php
#material: 1 0 1 0 Air
#material: 44.77 0.6961 1 0 Aorta
#material: 3.4969 0.2261 1 0 Bladder
#add_dispersion_debye: 3 370.48 9.1145e-8 21.866 4.6778e-10 15.38 0.94981e-11 Bladder
#material: 7.5425 0.73265 1 0 Blood
#add_dispersion_debye: 3 3529.4 8.8589e-8 110.21 5.4229e-9 53.637 0.89892 Blood
#material: 44.77 0.6961 1 0 BloodVessel
#material: 68.9 1.636 1 0 BodyFluid
## Avg. Bone used for BoneCancellous & BoneMarrow
#material: 3.8518 0.095687 1 0 BoneCancellous
#add_dispersion_debye: 3 125.82 3.2802e-8 18.125 2.4901e-9 11.748 1.379e-11 BoneCancellous
#material: 3.4498 0.022411 1 0 BoneCortical
#add_dispersion_debye: 3 118.19 4.6747e-8 9.5114 2.2822e-9 8.807 1.3731e-11 BoneCortical
## Avg. Bone used for BoneCancellous & BoneMarrow
#material: 3.8518 0.095687 1 0 BoneMarrow
#add_dispersion_debye: 3 125.82 3.2802e-8 18.125 2.4901e-9 11.748 1.379e-11 BoneMarrow
## Avg. Brain used for BrainGreyMatter & BrainWhiteMatter
#material: 6.9992 0.17911 1 0 BrainGreyMatter
#add_dispersion_debye: 3 787.41 2.2589e-8 142.26 2.5012e-9 40.264 0.91939e-11 BrainGreyMatter
#material: 6.9992 0.17911 1 0 BrainWhiteMatter
#add_dispersion_debye: 3 787.41 2.2589e-8 142.26 2.5012e-9 40.264 0.91939e-11 BrainWhiteMatter
#material: 5.424 0.04896 1 0 BreastFat
#material: 42.65 0.7824 1 0 Cartilage
#material: 6.9992 0.17911 1 0 Cerebellum
#add_dispersion_debye: 3 787.41 2.2589e-8 142.26 2.5012e-9 40.264 0.91939e-11 Cerebellum
#material: 5.7772 2.0002 1 0 CerebroSpinalFluid
#add_dispersion_debye: 3 40.84 0.15293e-8 27.951 0.014519e-9 34.334 0.50631e-11 CerebroSpinalFluid
#material: 49.78 0.9591 1 0 Cervix
#material: 57.94 1.08 1 0 Colon
#material: 55.23 1.394 1 0 Cornea
#material: 7.8582 0.55004 1 0 Duodenum
#add_dispersion_debye: 3 1674.5 6.273e-8 74.699 4.4123e-9 56.797 0.85287e-11 Duodenum
#material: 44.43 0.9611 1 0 Dura
#material: 55.27 1.167 1 0 EyeSclera
#material: 3.261 0.043999 1 0 Fat
#add_dispersion_debye: 3 34.522 1.3971e-8 2.2452 0.04806e-9 6.2928 0.60675e-11 Fat
#material: 4.7563 0.90026 1 0 GallBladder
#add_dispersion_debye: 3 40.681 0.1583e-8 20.066 0.01249e-9 33.876 0.57641e-11 GallBladder
#material: 70.19 1.838 1 0 GallBladderBile
#material: 59.68 1.038 1 0 Gland
#material: 7.8989 0.27449 1 0 Heart
#add_dispersion_debye: 3 1935.3 7.9786e-8 138.97 3.2768e-9 50.762 0.93944e-11 Heart
#material: 7.8691 0.23004 1 0 Kidney
#add_dispersion_debye: 3 2025 6.5466e-8 167.3 3.0341e-9 48.945 0.96655e-11 Kidney
## Avg. Lens
#material: 5.8421 0.27596 1 0 Lens
#add_dispersion_debye: 3 630.99 5.9556e-8 62.733 3.6647e-9 30.811 0.89718e-11 Lens
#material: 6.809 0.14243 1 0 Liver
#add_dispersion_debye: 3 1529.8 8.4314e-8 112.04 3.7562e-9 39.593 1.0293e-11 Liver
## Avg. Lung used for LungDeflated & LungInflated
#material: 5.5014 0.21296 1 0 LungDeflated
#add_dispersion_debye: 3 899.49 6.9396e-8 62.733 3.6647e-9 30.811 0.89718e-11 LungDeflated
#material: 5.5014 0.21296 1 0 LungInflated
#add_dispersion_debye: 3 899.49 6.9396e-8 62.733 3.6647e-9 30.811 0.89718e-11 LungInflated
#material: 59.68 1.038 1 0 Lymph
#material: 46.08 0.8447 1 0 MucousMembrane
## Avg. Muscle
#material: 7.5933 0.42066 1 0 Muscle
#add_dispersion_debye: 3 2646.6 13.044e-8 87.588 6.1808e-9 47.546 0.79767e-11 Muscle
#material: 12.45 0.1433 1 0 Nail
#material: 32.53 0.5737 1 0 Nerve
#material: 65.06 1.187 1 0 Oesophagus
#material: 50.47 1.29 1 0 Ovary
#material: 59.68 1.038 1 0 Pancreas
#material: 60.55 1.21 1 0 Prostate
#material: 55.27 1.167 1 0 Retina
## Avg. Skin used from SkinDry & SkinWet
#material: 5.8661 0.092415 1 0 SkinDry
#add_dispersion_debye: 3 1123 5.4817e-8 144.69 4.0078e-9 37.434 0.88726e-11 SkinDry
#material: 5.8661 0.092415 1 0 SkinWet
#add_dispersion_debye: 3 1123 5.4817e-8 144.69 4.0078e-9 37.434 0.88726e-11 SkinWet
#material: 7.7811 0.6884 1 0 SmallIntestine
#add_dispersion_debye: 3 6590.7 9.123e-8 211.31 4.2003e-9 50.6 0.92249e-11 SmallIntestine
#material: 32.53 0.5737 1 0 SpinalCord
#material: 57.18 1.273 1 0 Spleen
#material: 7.8582 0.55004 1 0 Stomach
#add_dispersion_debye: 3 1674.5 6.273e-8 74.699 4.4123e-9 56.797 0.85287e-11 Stomach
#material: 45.83 0.7184 1 0 Tendon
#material: 60.55 1.21 1 0 Testis
#material: 59.68 1.038 1 0 Thymus
#material: 59.68 1.038 1 0 Thyroid
#material: 55.27 0.9363 1 0 Tongue
#material: 12.45 0.1433 1 0 Tooth
#material: 42.01 0.7711 1 0 Trachea
#material: 61.12 1.27 1 0 Uterus
#material: 4.0041 1.5001 1 0 VitreousHumor
#add_dispersion_debye: 3 21.766 12.46e-8 1.364 15.742e-9 65.024 0.72371e-11 VitreousHumor

查看文件

@@ -0,0 +1,82 @@
User libraries is a sub-package where useful Python modules contributed by users are stored.
*********************
AustinMan/AustinWoman
*********************
Information
===========
**Authors**: Jackson W. Massey, Cemil S. Geyik, Jungwook Choi, Hyun-Jae Lee, Natcha Techachainiran, Che-Lun Hsu, Robin Q. Nguyen, Trevor Latson, Madison Ball, and Ali E. Yılmaz
**Contact**: Ali E. Yılmaz (ayilmaz@mail.utexas.edu), The University of Texas at Austin
**License**: `Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License <http://creativecommons.org/licenses/by-nc-nd/3.0/>`_
**Attribution/cite**: Please follow the instructions at http://web.corral.tacc.utexas.edu/AustinManEMVoxels/AustinMan/citing_the_model/index.html
`AustinMan and AustinWoman <http://bit.ly/AustinMan>`_ are open source electromagnetic voxel models of the human body, which are developed by the `Computational Electromagnetics Group <http://www.ece.utexas.edu/research/areas/electromagnetics-acoustics>`_ at `The University of Texas at Austin <http://www.utexas.edu>`_. The models are based on data from the `National Library of Medicine’s Visible Human Project <https://www.nlm.nih.gov/research/visible/visible_human.html>`_.
.. figure:: ../../images_shared/AustinMan_head.png
:width: 600 px
FDTD geometry mesh showing the head of the AustinMan model (2x2x2mm :math:`^3`).
The following whole body models are available.
=========== ========================== ==================
Model Resolution (mm :math:`^3`) Dimensions (cells)
=========== ========================== ==================
AustinMan 8x8x8 86 x 47 x 235
AustinMan 4x4x4 171 x 94 x 470
AustinMan 2x2x2 342 x 187 x 939
AustinMan 1x1x1 683 x 374 x 1877
AustinWoman 8x8x8 86 x 47 x 217
AustinWoman 4x4x4 171 x 94 x 433
AustinWoman 2x2x2 342 x 187 x 865
AustinWoman 1x1x1 683 x 374 x 1730
=========== ========================== ==================
Package contents
================
.. code-block:: none
AustinManWoman_materials.txt
AustinManWoman_materials_dispersive.txt
head_only_h5.py
* ``AustinManWoman_materials.txt`` is a text file containing `non-dispersive material properties at 900 MHz <http://niremf.ifac.cnr.it/tissprop/>`_.
* ``AustinManWoman_materials_dispersive.txt`` is a text file containing `dispersive material properties using a 3-pole Debye model <http://dx.doi.org/10.1109/LMWC.2011.2180371>`_.
.. note::
* The main body tissues are described using a 3-pole Debye model, but not all materials have a dispersive description.
* The dispersive material properties can only be used with the 1x1x1mm or 2x2x2mm AustinMan/Woman models. This is because the time step of the model must always be less than any of the relaxation times of the poles of the Debye models used for the dispersive material properties.
* ``head_only_h5.py`` is a script to assist with creating a model of only the head from a full body AustinMan/Woman model.
How to use the package
======================
The AustinMan and AustinWoman models themselves are not included in the user libraries sub-package.
* `Download a HDF5 file (.h5) of AustinMan or AustinWoman <http://bit.ly/AustinMan>`_ at the resolution you wish to use
To insert either AustinMan or AustinWoman models into a simulation use the ``#geometry_objects_read``.
Example
-------
To insert a 2x2x2mm :math:`^3` AustinMan with the lower left corner 40mm from the origin of the domain, using disperive material properties, and with no dielectric smoothing, use the command:
.. code-block:: none
#geometry_objects_read: 0.04 0.04 0.04 ../user_libs/AustinManWoman/AustinMan_v2.3_2x2x2.h5 ../user_libs/AustinManWoman/AustinManWoman_materials_dispersive.txt
For further information on the ``#geometry_objects_read`` see the section on object contruction commands in the :ref:`Input commands section <commands>`.
.. figure:: ../../images_shared/AustinMan.png
:width: 300 px
FDTD geometry mesh showing the AustinMan body model (2x2x2mm :math:`^3`).

查看文件

@@ -0,0 +1,29 @@
import argparse
import logging
import os
import h5py
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')
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'][:, :, :]
# 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')
# 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]]

查看文件

@@ -0,0 +1,700 @@
# Copyright (C) 2015-2022, Iraklis Giannakis and Sylwia Majchrowska
#
# This module is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
#
# Please use the attribution at http://dx.doi.org/10.1109/TAP.2014.2308549
import os
import sys
import warnings
from pathlib import Path
import matplotlib.gridspec as gridspec
import numpy as np
import scipy.interpolate
from matplotlib import pylab as plt
from optimization import DA_DLS, DE_DLS, PSO_DLS
class Relaxation(object):
""" Create Relaxation function object for complex material.
:param sigma: The conductivity (Siemens/metre).
:type sigma: float, non-optional
:param mu: The relative permeability.
:type mu: float, non-optional
:param mu_sigma: The magnetic loss.
:type mu_sigma: float, non-optional
:param material_name: A string containing the given name of
the material (e.g. "Clay").
:type material_name: str, non-optional
:param: number_of_debye_poles: Number of Debye functions used to
approximate the given electric
permittivity.
:type number_of_debye_poles: int, optional
:param: fn: Number of frequency points in frequency grid.
:type fn: int, optional (Default: 50)
:param plot: if True will plot the actual and the approximated
permittivity at the end (neglected as default: False).
:type plot: bool, optional, default:False
:param save: if True will save approximated material parameters
(not neglected as default: True).
:type save: bool, optional, default:True
:param optimizer: chosen optimization method:
Hybrid Particle Swarm-Damped Least-Squares (PSO_DLS),
Dual Annealing (DA) or Differential Evolution (DE)
(Default: PSO_DLS).
:type optimizer: Optimizer class, optional
:param optimizer_options: Additional keyword arguments passed to
optimizer class (Default: empty dict).
:type optimizer_options: dict, optional, default: empty dict
"""
def __init__(self, sigma, mu, mu_sigma,
material_name, f_n=50,
number_of_debye_poles=-1,
plot=True, save=False,
optimizer=PSO_DLS,
optimizer_options={}):
self.name = 'Relaxation function'
self.params = {}
self.number_of_debye_poles = number_of_debye_poles
self.f_n = f_n
self.sigma = sigma
self.mu = mu
self.mu_sigma = mu_sigma
self.material_name = material_name
self.plot = plot
self.save = save
self.optimizer = optimizer(**optimizer_options)
def set_freq(self, f_min, f_max, f_n=50):
""" Interpolate frequency vector using n equally logarithmicaly spaced frequencies.
Args:
f_min (float): First bound of the frequency range
used to approximate the given function (Hz).
f_max (float): Second bound of the frequency range
used to approximate the given function (Hz).
f_n (int): Number of frequency points in frequency grid
(Default: 50).
Note:
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))
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]]
except ValueError:
sys.exit("The inputs should be numeric.")
if not isinstance(self.number_of_debye_poles, int):
sys.exit("The number of Debye poles must be integer.")
if (np.array(d[1:]) < 0).sum() != 0:
sys.exit("The inputs should be positive.")
def calculation(self):
""" Approximate the given relaxation function
(Havriliak-Negami function, Crim, Jonscher) or based on raw data.
"""
raise NotImplementedError()
def print_info(self):
"""Readable string of parameters for given approximation settings.
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"{self.name} parameters: ")
s = ''
for k, v in self.params.items():
s += f"{k:10s} = {v}\n"
print(s)
return f'{self.name}:\n{s}'
def optimize(self):
""" Calling the main optimisation module with defined lower and upper boundaries of search.
Returns:
tau (ndarray): The optimised relaxation times.
weights (ndarray): Resulting optimised weights for the given relaxation times.
ee (float): Average error between the actual and the approximated real part.
rl (ndarray): Real parts of chosen relaxation function
for given frequency points.
im (ndarray): Imaginary parts of chosen relaxation function
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)
# 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}
)
return tau, weights, ee, rl, im
def run(self):
""" Solve the problem described by the given relaxation function
(Havriliak-Negami function, Crim, Jonscher)
or data given from a text file.
Returns:
avg_err (float): average fractional error
for relative permittivity (sum)
properties (list(str)): Given material nad Debye expnasion parameters
in a gprMax format.
"""
# Check the validity of the inputs
self.check_inputs()
# Print information about chosen approximation settings
self.print_info()
# Calculate both real and imaginary parts
# for the frequencies included in the vector freq
q = self.calculation()
# Set the real and the imaginary part of the relaxation function
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="")
error = np.infty # artificial best error starting value
self.number_of_debye_poles = 1
iteration = 1
# stop increasing number of Debye poles if error is smaller then 5%
# or 20 debye poles is reached
while error > 5 and iteration < 21:
# Calling the main optimisation module
tau, weights, ee, rl, im = self.optimize()
err_real, err_imag = self.error(rl + ee, im)
error = err_real + err_imag
self.number_of_debye_poles += 1
iteration += 1
else:
# Calling the main optimisation module
# for choosen number of debye poles
# if one of the weights is negative increase the stabiliser
# and repeat the optimisation
tau, weights, ee, rl, im = self.optimize()
err_real, err_imag = self.error(rl + ee, im)
# 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')
if self.save:
self.save_result(properties)
# Plot the actual and the approximate dielectric properties
if self.plot:
self.plot_result(rl + ee, im)
return err_real + err_imag, properties
def print_output(self, tau, weights, ee):
""" Print out the resulting Debye parameters in a gprMax format.
Args:
tau (ndarray): The best known position form optimization module
(optimal design).
weights (ndarray): Resulting optimised weights for the given relaxation times.
ee (float): Average error between the actual and the approximated real part.
Returns:
material_prop (list(str)): Given material nad Debye expnasion parameters
in a gprMax format.
"""
print("Debye expansion parameters: ")
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("_" * 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))
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')
return material_prop
def plot_result(self, rl_exp, im_exp):
""" Plot the actual and the approximated electric permittivity,
along with relative error for real and imaginary parts
using a semilogarithm X axes.
Args:
rl_exp (ndarray): Real parts of optimised Debye expansion
for given frequency points (plus average error).
im_exp (ndarray): Imaginary parts of optimised Debye expansion
for given frequency points.
"""
plt.close("all")
fig = plt.figure(figsize=(16, 8), tight_layout=True)
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.set_ylim([-1, np.max(np.concatenate([self.rl, -self.im])) + 1])
ax.legend()
ax.set_xlabel("Frequency (MHz)")
ax.set_ylabel("Relative permittivity")
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.legend()
ax.set_xlabel("Frequency (MHz)")
ax.set_ylabel("Relative approximation error")
plt.show()
def error(self, rl_exp, im_exp):
""" Calculate the average fractional error separately for
relative permittivity (real part) and conductivity (imaginary part)
Args:
rl_exp (ndarray): Real parts of optimised Debye expansion
for given frequency points (plus average error).
im_exp (ndarray): Imaginary parts of optimised Debye expansion
for given frequency points.
Returns:
avg_err_real (float): average fractional error
for relative permittivity (real part)
avg_err_imag (float): average fractional error
for conductivity (imaginary part)
"""
avg_err_real = np.sum(np.abs((rl_exp - self.rl)/(self.rl + 1)) * 100)/len(rl_exp)
avg_err_imag = np.sum(np.abs((-im_exp + self.im)/(self.im + 1)) * 100)/len(im_exp)
return avg_err_real, avg_err_imag
@staticmethod
def save_result(output, fdir="../materials"):
""" Save the resulting Debye parameters in a gprMax format.
Args:
output (list(str)): Material and resulting Debye parameters
in a gprMax format.
fdir (str): Path to saving directory.
"""
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")
elif os.path.isdir("materials"):
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")
else:
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)
fileH.write("\n")
fileH.close()
print(f"Material properties save at: {file_path}")
class HavriliakNegami(Relaxation):
""" Approximate a given Havriliak-Negami function
Havriliak-Negami function = ε_∞ + Δ‎ε / (1 + (2πfjτ)**α)**β,
where f is the frequency in Hz.
:param f_min: First bound of the frequency range
used to approximate the given function (Hz).
:type f_min: float
:param f_max: Second bound of the frequency range
used to approximate the given function (Hz).
:type f_max: float
:param e_inf: The real relative permittivity at infinity frequency
:type e_inf: float
:param alpha: Real positive float number which varies 0 < alpha < 1.
For alpha = 1 and beta !=0 & beta !=1 Havriliak-Negami
transforms to Cole-Davidson function.
:type alpha: float
:param beta: Real positive float number which varies 0 < beta < 1.
For beta = 1 and alpha !=0 & alpha !=1 Havriliak-Negami
transforms to Cole-Cole function.
:type beta: float
:param de: The difference of relative permittivity at infinite frequency
and the relative permittivity at zero frequency.
:type de: float
: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,
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,
number_of_debye_poles=number_of_debye_poles,
plot=plot, save=save,
optimizer=optimizer,
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
else:
self.f_min, self.f_max = f_min, f_max
# Choosing n frequencies logarithmicaly equally spaced between the bounds given
self.set_freq(self.f_min, self.f_max, self.f_n)
self.e_inf, self.alpha, self.beta, self.de, self.tau_0 = e_inf, alpha, beta, de, tau_0
self.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. """
super(HavriliakNegami, self).check_inputs()
try:
d = [float(i) for i in self.params.values()]
except ValueError:
sys.exit("The inputs should be numeric.")
if (np.array(d) < 0).sum() != 0:
sys.exit("The inputs should be positive.")
if self.alpha > 1:
sys.exit("Alpha value must range between 0-1 (0 <= alpha <= 1)")
if self.beta > 1:
sys.exit("Beta value must range between 0-1 (0 <= beta <= 1)")
if self.f_min == self.f_max:
sys.exit("Null frequency range")
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
class Jonscher(Relaxation):
""" Approximate a given Jonsher function
Jonscher function = ε_∞ - ap * (-1j * 2πf / omegap)**n_p,
where f is the frequency in Hz
:param f_min: First bound of the frequency range
used to approximate the given function (Hz).
:type f_min: float
:param f_max: Second bound of the frequency range
used to approximate the given function (Hz).
:type f_max: float
:params e_inf: The real relative permittivity at infinity frequency.
:type e_inf: float, non-optional
:params a_p: Jonscher parameter. Real positive float number.
:type a_p: float, non-optional
:params omega_p: Jonscher parameter. Real positive float number.
:type omega_p: float, non-optional
: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,
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,
number_of_debye_poles=number_of_debye_poles,
plot=plot, save=save,
optimizer=optimizer,
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
else:
self.f_min, self.f_max = f_min, f_max
# Choosing n frequencies logarithmicaly equally spaced between the bounds given
self.set_freq(self.f_min, self.f_max, self.f_n)
self.e_inf, self.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}
def check_inputs(self):
""" Check the validity of the inputs. """
super(Jonscher, self).check_inputs()
try:
d = [float(i) for i in self.params.values()]
except ValueError:
sys.exit("The inputs should be numeric.")
if (np.array(d) < 0).sum() != 0:
sys.exit("The inputs should be positive.")
if self.n_p > 1:
sys.exit("n_p value must range between 0-1 (0 <= n_p <= 1)")
if self.f_min == self.f_max:
sys.exit("Error: Null frequency range!")
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))
class Crim(Relaxation):
""" Approximate a given CRIM function
CRIM = (Σ frac_i * (ε_∞_i + Δε_i/(1 + 2πfj*τ_i))^a)^(1/a)
:param f_min: First bound of the frequency range
used to approximate the given function (Hz).
:type f_min: float
:param f_max: Second bound of the frequency range
used to approximate the given function (Hz).
:type f_max: float
:param a: Shape factor.
:type a: float, non-optional
:param: volumetric_fractions: Volumetric fraction for each material.
:type volumetric_fractions: ndarray, non-optional
:param materials: Arrays of materials properties, for each material [e_inf, de, tau_0].
: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,
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,
number_of_debye_poles=number_of_debye_poles,
plot=plot, save=save,
optimizer=optimizer,
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
else:
self.f_min, self.f_max = f_min, f_max
# Choosing n frequencies logarithmicaly equally spaced between the bounds given
self.set_freq(self.f_min, self.f_max, self.f_n)
self.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}
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]]
except ValueError:
sys.exit("The inputs should be numeric.")
if (np.array(d) < 0).sum() != 0:
sys.exit("The inputs should be positive.")
if len(self.volumetric_fractions) != len(self.materials):
sys.exit("Number of volumetric volumes does not match the dielectric properties")
# Check if the materials are at least two
if len(self.volumetric_fractions) < 2:
sys.exit("The materials should be at least 2")
# Check if the frequency range is null
if self.f_min == self.f_max:
sys.exit("Null frequency range")
# Check if the inputs are positive
f = [i for i in self.volumetric_fractions if i < 0]
if len(f) != 0:
sys.exit("Error: The inputs should be positive")
for i in range(len(self.volumetric_fractions)):
f = [i for i in self.materials[i][:] if i < 0]
if len(f) != 0:
sys.exit("Error: The inputs should be positive")
# Check if the summation of the volumetric fractions equal to one
if np.sum(self.volumetric_fractions) != 1:
sys.exit("Error: The summation of volumetric volumes should be equal to 1")
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("CRIM parameters: ")
for i in range(len(self.volumetric_fractions)):
print("Material {}.:".format(i+1))
print("---------------------------------")
print(f"{'Vol. fraction':>27s} = {self.volumetric_fractions[i]}")
print(f"{'e_inf':>27s} = {self.materials[i][0]}")
print(f"{'De':>27s} = {self.materials[i][1]}")
print(f"{'log(tau_0)':>27s} = {np.log10(self.materials[i][2])}")
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)
class Rawdata(Relaxation):
""" Interpolate data given from a text file.
:param filename: text file which contains three columns:
frequency (Hz),Real,Imaginary (separated by comma).
:type filename: str, non-optional
: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,
number_of_debye_poles=number_of_debye_poles,
plot=plot, save=save,
optimizer=optimizer,
optimizer_options=optimizer_options)
self.delimiter = delimiter
self.filename = Path(filename).absolute()
self.params = {'filename': self.filename}
def check_inputs(self):
""" Check the validity of the inputs. """
super(Rawdata, self).check_inputs()
if not os.path.isfile(self.filename):
sys.exit("File doesn't exists!")
def calculation(self):
""" Interpolate real and imaginary part from data.
Column framework of the input file three columns comma-separated
Frequency(Hz),Real,Imaginary
"""
# 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]
)
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")
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,
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})
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,
number_of_debye_poles=6,
plot=True, save=False,
optimizer=DA_DLS,
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,
number_of_debye_poles=6,
plot=True, save=False,
optimizer=DE_DLS,
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.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.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.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.run()

查看文件

@@ -0,0 +1,248 @@
User libraries is a sub-package where useful Python modules contributed by users are stored.
********
DebyeFit
********
Information
===========
**Author/Contact**: Iraklis Giannakis (iraklis.giannakis@abdn.ac.uk), University of Aberdeen, UK and Sylwia Majchrowska (Sylwia.Majchrowska1993@gmail.com)
This module was created as part of the `Google Summer of Code <https://summerofcode.withgoogle.com/>`_ programme 2021 which gprMax participated.
**License**: `Creative Commons Attribution-ShareAlike 4.0 International License <http://creativecommons.org/licenses/by-sa/4.0/>`_
**Attribution/cite**: Giannakis, I., & Giannopoulos, A. (2014). A novel piecewise linear recursive convolution approach for dispersive media using the finite-difference time-domain method. *IEEE Transactions on Antennas and Propagation*, 62(5), 2669-2678. (http://dx.doi.org/10.1109/TAP.2014.2308549)
Electric permittivity is a complex function with both real and imaginary parts.
In general, as a hard and fast rule, the real part dictates the velocity of the medium while the imaginary part is related to the electromagnetic losses.
The generic form of dispersive media is
.. math::
\epsilon(\omega) = \epsilon^{'}(\omega) - j\epsilon^{''}(\omega),
where :math:`\omega` is the angular frequency, :math:`\epsilon^{'}` and :math:`\epsilon^{''}` are the real and imaginary parts of the permittivity respectively.
This package provides scripts and tools which can be used to fit a multi-Debye expansion to dielectric data, defined as
.. math::
\epsilon(\omega) = \epsilon_{\infty} + \sum_{i=1}^{N}\frac{\Delta\epsilon_{i}}{1+j\omega t_{0,i}},
where :math:`\epsilon(\omega)` is frequency dependent dielectric permittivity, :math:`\Delta\epsilon` - difference between the real permittivity at zero and infinite frequency.
:math:`\tau_{0}` is relaxation time (seconds), :math:`\epsilon_{\infty}` - real part of relative permittivity at infinite frequency, and :math:`N` is number of the Debye poles.
To fit the data to a multi-Debye expansion, you can choose between Havriliak-Negami, Jonscher, or Complex Refractive Index Mixing (CRIM) models, as well as arbitrary dielectric data derived experimentally or calculated using a different function.
.. figure:: ../../images_shared/epsilon.png
:width: 600 px
Real and imaginary parts of frequency-dependent permittivity
Package contents
================
There are two main scripts:
* ```Debye_fit.py``` contains definitions of all Relaxation functions classes
* ```optimization.py``` contains definitions of three choosen global optimization methods
Relaxation Class
----------------
This class is designed for modelling different relaxation functions, like Havriliak-Negami (```HavriliakNegami```), Jonscher (```Jonscher```), Complex Refractive Index Mixing (```CRIM```) models, and arbitrary dielectric data derived experimentally or calculated using some other function (```Rawdata```).
More about the ``Relaxation`` class structure can be found in the :doc:`Relaxation doc <relaxation.rst>`.
Havriliak-Negami Function
^^^^^^^^^^^^^^^^^^^^^^^^^
The Havriliak-Negami relaxation is an empirical modification of the Debye relaxation model in electromagnetism, which in addition to the Debye equation has two exponential parameters
.. math::
\epsilon(\omega) = \epsilon_{\infty} + \frac{\Delta\epsilon}{\left(1+\left(j\omega t_{0}\right)^{a}\right)^{b}}
The ``HavriliakNegami`` class has the following structure:
.. code-block:: none
HavriliakNegami(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=True,
optimizer=PSO_DLS,
optimizer_options={})
* ``f_min`` is first bound of the frequency range used to approximate the given function (Hz),
* ``f_max`` is second bound of the frequency range used to approximate the given function (Hz),
* ``alpha`` is real positive float number which varies :math:`0 < \alpha < 1`,
* ``beta`` is real positive float number which varies :math:`0 < \beta < 1`,
* ``e_inf`` is a real part of relative permittivity at infinite frequency,
* ``de`` is a difference between the real permittivity at zero and infinite frequency,
* ``tau_0`` is a relaxation time (seconds),
* ``sigma`` is a conductivity (Siemens/metre),
* ``mu`` is a relative permeability,
* ``mu_sigma`` is a magnetic loss (Ohms/metre),
* ``material_name`` is the material name,
* ``number_of_debye_poles`` is the chosen number of Debye poles,
* ``f_n`` is the chosen number of frequences,
* ``plot`` is a switch to turn on the plotting,
* ``save`` is a switch to turn on saving final material properties,
* ``optimizer`` is a chosen optimizer to fit model to dielectric data,
* ``optimizer_options`` is a dict for options of chosen optimizer.
Jonscher Function
^^^^^^^^^^^^^^^^^
Jonscher function is mainly used to describe the dielectric properties of concrete and soils. The frequency domain expression of Jonscher function is given by
.. math::
\epsilon(\omega) = \epsilon_{\infty} + a_{p}*\left( -j*\frac{\omega}{\omega_{p}} \right)^{n}
The ``Jonscher`` class has the following structure:
.. code-block:: none
Jonscher(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=True,
optimizer=PSO_DLS,
optimizer_options={})
* ``f_min`` is first bound of the frequency range used to approximate the given function (Hz),
* ``f_max`` is second bound of the frequency range used to approximate the given function (Hz),
* ``e_inf`` is a real part of relative permittivity at infinite frequency,
* ``a_p``` is a Jonscher parameter. Real positive float number,
* ``omega_p`` is a Jonscher parameter. Real positive float number,
* ``n_p`` Jonscher parameter, 0 < n_p < 1.
Complex Refractive Index Mixing (CRIM) Function
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CRIM is the most mainstream approach for estimating the bulk permittivity of heterogeneous materials and has been widely applied for GPR applications. The function takes form of
.. math::
\epsilon(\omega)^{d} = \sum_{i=1}^{m}f_{i}\epsilon_{m,i}(\omega)^{d}
The ``CRIM`` class has the following structure:
.. code-block:: none
CRIM(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=True,
optimizer=PSO_DLS,
optimizer_options={})
* ``f_min`` is first bound of the frequency range used to approximate the given function (Hz),
* ``f_max`` is second bound of the frequency range used to approximate the given function (Hz),
* ``a`` is a shape factor,
* ``volumetric_fractions`` is a volumetric fraction for each material,
* ``materials`` are arrays of materials properties, for each material [e_inf, de, tau_0].
Rawdata Class
^^^^^^^^^^^^^
This package also has the ability to model dielectric properties obtained experimentally by fitting multi-Debye functions to data given from a file.
The format of the file should be three columns: the first column contains the frequencies (Hz) associated with the electric permittivity; the second column contains the real part of the relative permittivity; the third column contains the imaginary part of the relative permittivity. The columns should separated by a coma by default, but it is also possible to define a different separator.
The ``Rawdata`` class has the following structure:
.. code-block:: none
Rawdata(self, filename,
sigma, mu, mu_sigma,
material_name, number_of_debye_poles=-1,
f_n=50, delimiter =',',
plot=False, save=True,
optimizer=PSO_DLS,
optimizer_options={})
* ``filename`` is a path to text file which contains three columns,
* ``delimiter`` is a separator for three data columns.
Class Optimizer
---------------
This class supports global optimization algorithms (particle swarm, dual annealing, evolutionary algorithms) for finding an optimal set of relaxation times that minimise the error between the actual and the approximated electric permittivity, and calculates optimised weights for the given relaxation times.
Code written here is mainly based on external libraries, like ```scipy``` and ```pyswarm```.
More about the ``Optimizer`` class structure can be found in the :doc:`Optimisation doc <optimisation.rst>`.
PSO_DLS Class
^^^^^^^^^^^^^
Creation of hybrid Particle Swarm-Damped Least Squares optimisation object with predefined parameters.
The code is a modified version of the pyswarm package which can be found at https://pythonhosted.org/pyswarm/.
DA_DLS Class
^^^^^^^^^^^^
Creation of Dual Annealing-Damped Least Squares optimisation object with predefined parameters. The class is a modified version of the scipy.optimize package which can be found at:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.dual_annealing.html#scipy.optimize.dual_annealing.
DE_DLS Class
^^^^^^^^^^^^
Creation of Differential Evolution-Damped Least Squares object with predefined parameters. The class is a modified version of the scipy.optimize package which can be found at:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.differential_evolution.html#scipy.optimize.differential_evolution.
DLS function
^^^^^^^^^^^^
Finding the weights using a non-linear least squares (LS) method, the Levenberg-Marquardt algorithm (LMA or just LM), also known as the damped least-squares (DLS) method.
How to use the package
======================
Examples
--------
In the examples directory you will find Jupyter notebooks, scripts, and data that demonstrate different cases of how to use the main script ```DebyeFit.py```:
* ```example_DebyeFitting.ipynb```: simple cases of using all available implemented relaxation functions,
* ```example_BiologicalTissues.ipynb```: simple cases of using Cole-Cole function for biological tissues,
* ```example_ColeCole.py```: simple cases of using Cole-Cole function in case of 3, 5 and automatically chosen number of Debye poles,
* ```Test.txt```: raw data for testing ```Rawdata``` class, file contains 3 columns: the first column contains the frequencies (Hz) associated with the value of the permittivity; the second column contains the real part of the relative permittivity; and the third column contains the imaginary part of the relative permittivity.
The following code shows a basic example of how to use the Havriliak-Negami function:
.. code-block:: python
# 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,
number_of_debye_poles=3,
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})
# run optimization
setup.run()

查看文件

查看文件

@@ -0,0 +1,41 @@
10000000,29.9213536481435,1.25169556541143
11220184.5430196,29.9010908161542,1.40299702432563
12589254.1179417,29.8756399586587,1.57217537410568
14125375.4462276,29.8436916400072,1.76117429474009
15848931.9246111,29.8036168365262,1.972079339829
17782794.1003892,29.7533952203391,2.20709802135445
19952623.1496888,29.6905309907513,2.46852367160776
22387211.3856834,29.611956285414,2.75867656083315
25118864.3150958,29.5139238169428,3.0798138913592
28183829.3126445,29.3918930811742,3.43399830398795
31622776.6016838,29.2404187150832,3.82291275991739
35481338.9233575,29.0530558254938,4.24760876499865
39810717.0553497,28.8223057349563,4.70817602888384
44668359.2150963,28.5396365567361,5.20332657814755
50118723.3627272,28.1956253280391,5.729897510637
56234132.5190349,27.7802793753847,6.28229678071347
63095734.4480193,27.283598801889,6.85194778826586
70794578.4384137,26.6964309897057,7.42683054876774
79432823.4724282,26.0116311645087,7.99126340085894
89125093.8133746,25.2254715223637,8.52610387978976
100000000,24.339136006498,9.00954486736777
112201845.430197,23.3600192468154,9.41861299029905
125892541.179417,22.302462791172,9.73132391277098
141253754.462276,21.1875720188749,9.92923323827099
158489319.246111,20.0419098344834,9.99991217790304
177827941.003892,18.8951464050085,9.93877751706085
199526231.496888,17.7770654023544,9.74979803763085
223872113.856834,16.714546101342,9.44488182455412
251188643.150958,15.7291491674702,9.04211441899954
281838293.126445,14.8357276987863,8.56331078490773
316227766.016838,14.0421664572876,8.03145189099272
354813389.233576,13.3500622522389,7.46848900052093
398107170.553497,12.7559948799797,6.89379357256362
446683592.150963,12.2530185094144,6.32331224790652
501187233.627271,11.8320839792422,5.76932819987279
562341325.190349,11.4832204965472,5.24065519659195
630957344.480194,11.1964125953287,4.74308431385235
707945784.384137,10.9621824341848,4.27993617324392
794328234.724282,10.7719266477577,3.85261757843109
891250938.133744,10.6180694756136,3.46112404221322
1000000000,10.4940904606372,3.10446192269295

文件差异因一行或多行过长而隐藏

查看文件

@@ -0,0 +1,44 @@
# I. Giannakis, A. Giannopoulos and N. Davidson,
# "Incorporating dispersive electrical properties in FDTD GPR models
# 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__), '..'))
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})
### Dry Sand in case of 3, 5
# and automatically set number of Debye poles (-1)
for number_of_debye_poles in [3, 5, -1]:
setup.number_of_debye_poles = number_of_debye_poles
setup.run()
### Moist sand
# set Havrilak-Negami function parameters
setup.material_name="moist_sand"
setup.alpha = 0.25
setup.beta = 1
setup.e_inf = 5.6
setup.de = 3.3
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]:
setup.number_of_debye_poles = number_of_debye_poles
setup.run()

文件差异因一行或多行过长而隐藏

查看文件

@@ -0,0 +1,462 @@
# Copyright (C) 2015-2022, Iraklis Giannakis and Sylwia Majchrowska
#
# This module is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
#
# Please use the attribution at http://dx.doi.org/10.1109/TAP.2014.2308549
import numpy as np
import scipy.optimize
from matplotlib import pylab as plt
from tqdm import tqdm
class Optimizer(object):
"""
Create choosen optimisation object.
:param maxiter: The maximum number of iterations for the
optimizer (Default: 1000).
:type maxiter: int, optional
:param seed: Seed for RandomState. Must be convertible to 32 bit
unsigned integers (Default: None).
:type seed: int, NoneType, optional
"""
def __init__(self, maxiter=1000, seed=None):
self.maxiter = maxiter
self.seed = seed
self.calc_weights = None
def fit(self, func, lb, ub, funckwargs={}):
"""Call the optimization function that tries to find an optimal set
of relaxation times that minimise the error
between the actual and the approximated electric permittivity
and calculate optimised weights for the given relaxation times.
Args:
func (function): The function to be minimized.
lb (ndarray): The lower bounds of the design variable(s).
ub (ndarray): The upper bounds of the design variable(s).
funckwargs (dict): Additional keyword arguments passed to
objective and constraint function
(Default: empty dict).
Returns:
tau (ndarray): The the best relaxation times.
weights (ndarray): Resulting optimised weights for the given
relaxation times.
ee (float): Average error between the actual and the approximated
real part.
rl (ndarray): Real parts of chosen relaxation function
for given frequency points.
im (ndarray): Imaginary parts of chosen relaxation function
for given frequency points.
"""
np.random.seed(self.seed)
# find the relaxation frequencies using choosen optimization alghoritm
tau, _ = self.calc_relaxation_times(func, lb, ub, funckwargs)
# 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)
return tau, weights, ee, rl_exp, im_exp
def calc_relaxation_times(self):
""" Optimisation method that tries to find an optimal set
of relaxation times that minimise the error
between the actual and the approximated electric permittivity.
"""
raise NotImplementedError()
@staticmethod
def cost_function(x, rl, im, freq):
"""
The cost function is the average error between
the actual and the approximated electric permittivity.
Args:
x (ndarray): The logarithm with base 10 of relaxation times
of the Debyes poles.
rl (ndarray): Real parts of chosen relaxation function
for given frequency points.
im (ndarray): Imaginary parts of chosen relaxation function
for given frequency points.
freq (ndarray): The frequencies vector for defined grid.
Returns:
cost (float): Sum of mean absolute errors for real and
imaginary parts.
"""
cost_i, cost_r, _, _, _, _ = DLS(x, rl, im, freq)
return cost_i + cost_r
class PSO_DLS(Optimizer):
""" Create hybrid Particle Swarm-Damped Least Squares optimisation
object with predefined parameters.
:param swarmsize: The number of particles in the swarm (Default: 40).
:type swarmsize: int, optional
:param maxiter: The maximum number of iterations for the swarm
to search (Default: 50).
:type maxiter: int, optional
:param omega: Particle velocity scaling factor (Default: 0.9).
:type omega: float, optional
:param phip: Scaling factor to search away from the particle's
best known position (Default: 0.9).
:type phip: float, optional
:param phig: Scaling factor to search away from the swarm's
best known position (Default: 0.9).
:type phig: float, optional
:param minstep: The minimum stepsize of swarm's best position
before the search terminates (Default: 1e-8).
:type minstep: float, optional
:param minfun: The minimum change of swarm's best objective value
before the search terminates (Default: 1e-8)
:type minfun: float, optional
:param pflag: if True will plot the actual and the approximated
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):
super(PSO_DLS, self).__init__(maxiter, seed)
self.swarmsize = swarmsize
self.omega = omega
self.phip = phip
self.phig = phig
self.minstep = minstep
self.minfun = minfun
self.pflag = pflag
self.calc_weights = DLS
def calc_relaxation_times(self, func, lb, ub, funckwargs={}):
"""
A particle swarm optimisation that tries to find an optimal set
of relaxation times that minimise the error
between the actual and the approximated electric permittivity.
The current code is a modified edition of the pyswarm package
which can be found at https://pythonhosted.org/pyswarm/
Args:
func (function): The function to be minimized.
lb (ndarray): The lower bounds of the design variable(s).
ub (ndarray): The upper bounds of the design variable(s).
funckwargs (dict): Additional keyword arguments passed to
objective and constraint function
(Default: empty dict).
Returns:
g (ndarray): The swarm's best known position (relaxation times).
fg (float): The objective value at ``g``.
"""
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'
lb = np.array(lb)
ub = np.array(ub)
assert np.all(ub > lb), \
'All upper-bound values must be greater than lower-bound values'
vhigh = np.abs(ub - lb)
vlow = -vhigh
# Initialize objective function
obj = lambda x: func(x=x, **funckwargs)
# Initialize the particle swarm
d = len(lb) # the number of dimensions each particle has
x = np.random.rand(self.swarmsize, d) # particle positions
v = np.zeros_like(x) # particle velocities
p = np.zeros_like(x) # best particle positions
fp = np.zeros(self.swarmsize) # best particle function values
g = [] # best swarm position
fg = np.inf # artificial best swarm position starting value
for i in range(self.swarmsize):
# Initialize the particle's position
x[i, :] = lb + x[i, :] * (ub - lb)
# Initialize the particle's best known position
p[i, :] = x[i, :]
# Calculate the objective's value at the current particle's
fp[i] = obj(p[i, :])
# At the start, there may not be any feasible starting point,
# so just
# give it a temporary "best" point since it's likely to change
if i == 0:
g = p[0, :].copy()
# If the current particle's position is better than the swarm's,
# update the best swarm position
if fp[i] < fg:
fg = fp[i]
g = p[i, :].copy()
# Initialize the particle's velocity
v[i, :] = vlow + np.random.rand(d) * (vhigh - vlow)
# Iterate until termination criterion met
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, :])
# Update the particle's position,
# correcting lower and upper bound
# violations, then update the objective function value
x[i, :] = x[i, :] + v[i, :]
mark1 = x[i, :] < lb
mark2 = x[i, :] > ub
x[i, mark1] = lb[mark1]
x[i, mark2] = ub[mark2]
fx = obj(x[i, :])
# Compare particle's best position
if fx < fp[i]:
p[i, :] = x[i, :].copy()
fp[i] = fx
# Compare swarm's best position to current
# particle's position
if fx < fg:
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}')
return tmp, fx
elif stepsize <= self.minstep:
print(f'Stopping search: Swarm best position '
f'change less than {self.minstep}')
return tmp, fx
else:
g = tmp.copy()
fg = fx
# Dynamically plot the error as the optimisation takes place
if self.pflag:
if it == 0:
xpp = [it]
ypp = [fg]
else:
xpp.append(it)
ypp.append(fg)
PSO_DLS.plot(xpp, ypp)
return g, fg
@staticmethod
def plot(x, y):
"""
Dynamically plot the error as the optimisation takes place.
Args:
x (array): The number of current iterations.
y (array): The objective value at for all x points.
"""
# 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.xlim(min(x) - 0.1, max(x) + 0.1)
plt.grid(b=True, which="major", color="k",
linewidth=0.2, linestyle="--")
plt.suptitle("Debye fitting process")
plt.xlabel("Iteration")
plt.ylabel("Average Error")
plt.pause(0.0001)
class DA_DLS(Optimizer):
""" Create Dual Annealing object with predefined parameters.
The current class is a modified edition of the scipy.optimize
package which can be found at:
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):
super(DA_DLS, self).__init__(maxiter, seed)
self.local_search_options = local_search_options
self.initial_temp = initial_temp
self.restart_temp_ratio = restart_temp_ratio
self.visit = visit
self.accept = accept
self.maxfun = maxfun
self.no_local_search = no_local_search
self.callback = callback
self.x0 = x0
self.calc_weights = DLS
def calc_relaxation_times(self, func, lb, ub, funckwargs={}):
"""
Find the global minimum of a function using Dual Annealing.
The current class is a modified edition of the scipy.optimize
package which can be found at:
https://docs.scipy.org/doc/scipy/reference/generated/
scipy.optimize.dual_annealing.html#scipy.optimize.dual_annealing
Args:
func (function): The function to be minimized
lb (ndarray): The lower bounds of the design variable(s)
ub (ndarray): The upper bounds of the design variable(s)
funckwargs (dict): Additional keyword arguments passed to
objective and constraint function
(Default: empty dict)
Returns:
x (ndarray): The solution array (relaxation times).
fun (float): The objective value at the best solution.
"""
np.random.seed(self.seed)
result = scipy.optimize.dual_annealing(
func,
bounds=list(zip(lb, ub)),
args=funckwargs.values(),
maxiter=self.maxiter,
local_search_options=self.local_search_options,
initial_temp=self.initial_temp,
restart_temp_ratio=self.restart_temp_ratio,
visit=self.visit,
accept=self.accept,
maxfun=self.maxfun,
no_local_search=self.no_local_search,
callback=self.callback,
x0=self.x0)
print(result.message)
return result.x, result.fun
class DE_DLS(Optimizer):
"""
Create Differential Evolution-Damped Least Squares object
with predefined parameters.
The current class is a modified edition of the scipy.optimize
package which can be found at:
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):
super(DE_DLS, self).__init__(maxiter, seed)
self.strategy = strategy
self.popsize = popsize
self.tol = tol
self.mutation = mutation
self.recombination = recombination
self.callback = callback
self.disp = disp
self.polish = polish
self.init = init
self.atol = atol
self.updating = updating
self.workers = workers
self.constraints = constraints
self.calc_weights = DLS
def calc_relaxation_times(self, func, lb, ub, funckwargs={}):
"""
Find the global minimum of a function using Differential Evolution.
The current class is a modified edition of the scipy.optimize
package which can be found at:
https://docs.scipy.org/doc/scipy/reference/generated/
scipy.optimize.differential_evolution.html#scipy.optimize.differential_evolution
Args:
func (function): The function to be minimized
lb (ndarray): The lower bounds of the design variable(s)
ub (ndarray): The upper bounds of the design variable(s)
funckwargs (dict): Additional keyword arguments passed to
objective and constraint function
(Default: empty dict)
Returns:
x (ndarray): The solution array (relaxation times).
fun (float): The objective value at the best solution.
"""
np.random.seed(self.seed)
result = scipy.optimize.differential_evolution(
func,
bounds=list(zip(lb, ub)),
args=funckwargs.values(),
strategy=self.strategy,
popsize=self.popsize,
tol=self.tol,
mutation=self.mutation,
recombination=self.recombination,
callback=self.callback,
disp=self.disp,
polish=self.polish,
init=self.init,
atol=self.atol,
updating=self.updating,
workers=self.workers,
constraints=self.constraints)
print(result.message)
return result.x, result.fun
def DLS(logt, rl, im, freq):
"""
Find the weights using a non-linear least squares (LS) method,
the Levenberg–Marquardt algorithm (LMA or just LM),
also known as the damped least-squares (DLS) method.
Args:
logt (ndarray): The best known position form optimization module
(optimal design),
the logarithm with base 10 of relaxation times
of the Debyes poles.
rl (ndarray): Real parts of chosen relaxation function
for given frequency points.
im (ndarray): Imaginary parts of chosen relaxation function
for given frequency points.
freq (ndarray): The frequencies vector for defined grid.
Returns:
cost_i (float): Mean absolute error between the actual and
the approximated imaginary part.
cost_r (float): Mean absolute error between the actual and
the approximated real part (plus average error).
x (ndarray): Resulting optimised weights for the given
relaxation times.
ee (float): Average error between the actual and the approximated
real part.
rp (ndarray): The real part of the permittivity for the optimised
relaxation times and weights for the frequnecies included
in freq.
ip (ndarray): The imaginary part of the permittivity for the optimised
relaxation times and weights for the frequnecies included
in freq.
"""
# The relaxation time of the Debyes are given at as logarithms
# logt=log10(t0) for efficiency during the optimisation
# 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)
# 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]
cost_i = np.sum(np.abs(ip-im))/len(im)
ee = np.mean(rl - rp)
if ee < 1:
ee = 1
cost_r = np.sum(np.abs(rp + ee - rl))/len(im)
return cost_i, cost_r, x, ee, rp, ip

查看文件

@@ -0,0 +1,50 @@
Optimization methods of multi-Debye fitting
-------------------------------------------
The ``Optimizer`` class supports global optimization algorithms (particle swarm, dual annealing, evolutionary algorithms) for finding an optimal set of relaxation times that minimise the error between the actual and the approximated electric permittivity, and calculates optimised weights for the given relaxation times.
Code written here is mainly based on external libraries, like ```scipy``` and ```pyswarm```.
Supported methods:
- [x] hybrid Particle Swarm-Damped Least Squares
- [x] hybrid Dual Annealing-Damped Least Squares
- [x] hybrid Differential Evolution-Damped Least Squares
Methods
^^^^^^^
1. __constructor__ - is called in all child classes.
It takes the following arguments:
- `maxiter`: maximum number of iterations for the optimizer,
- `seed`: Seed for RandomState.
In constructor the attributes:
- `maxiter`,
- `seed`,
- `calc_weights` (used to fit weight, non-linear least squares (LS) method is used as a default)
are set.
2. __fit__ - is inherited by all children classes. It calls the optimization function that tries to find an optimal set of relaxation times that minimise the error between the actual and the approximated electric permittivity and calculate optimised weights for the given relaxation times.
It takes the following arguments:
- `func`: objective function to be optimized,
- `lb`: the lower bounds of the design variable(s),
- `ub`: the upper bounds of the design variable(s),
- `funckwargs`: optional arguments takien by objective function.
3. __cost_function__ - is inherited by all child classes. It calculates the cost function as the average error between the actual and the approximated electric permittivity (sum of real and imaginary part).
It takes the following arguments:
- `x`: the logarithm with base 10 of relaxation times of the Debyes poles,
- `rl`: real parts of chosen relaxation function for given frequency points,
- `im`: imaginary parts of chosen relaxation function for given frequency points,
- `freq`: the frequencies vector for defined grid.
4. __calc_relaxation_times__ - it finds an optimal set of relaxation times that minimise an objective function using appropriate optimization procedure.
It takes the following arguments:
- `func`: objective function to be optimized,
- `lb`: the lower bounds of the design variable(s),
- `ub`: the upper bounds of the design variable(s),
- `funckwargs`: optional arguments takien by objective function.
Each new class of optimizer should:
- define constructor with appropriate arguments,
- overload __calc_relaxation_times__ method (and optional define __calc_weights__ function in case of hybrid optimization procedure).

查看文件

@@ -0,0 +1,71 @@
Relaxation classes for multi-Debye fitting
------------------------------------------
This class is designed for modelling different relaxation functions, like Havriliak-Negami (```HavriliakNegami```), Jonscher (```Jonscher```), Complex Refractive Index Mixing (```CRIM```) models, and arbitrary dielectric data derived experimentally or calculated using some other function (```Rawdata```).
Supported relaxation classes:
- [x] Havriliak-Negami,
- [x] Jonscher,
- [x] Complex Refractive Index Mixing,
- [x] Experimental data,
Methods
^^^^^^^
1. __constructor__ - is called in all child classes, creates Relaxation function object for complex material.
It takes the following arguments:
- ``sigma`` is a conductivity (Siemens/metre),
- ``mu`` is a relative permeability,
- ``mu_sigma`` is a magnetic loss (Ohms/metre),
- ``material_name`` is definition of material name,
- ``number_of_debye_poles`` is choosen number of Debye poles,
- ``f_n`` is the chosen number of frequences,
- ``plot`` is a switch to turn on the plotting,
- ``save`` is a switch to turn on the saving final material properties,
- ``optimizer`` is a chosen optimizer to fit model to dielectric data,
- ``optimizer_options`` is a dict for options of chosen optimizer.
Additional parameters:
- ``rl`` calculated real part of chosen relaxation function for given frequency points,
- ``im`` calculated imaginary part of chosen relaxation function for given frequency points.
2. __set_freq__ - is inherited by all child classes, interpolates frequency vector using n equally logarithmicaly spaced frequencies.
It takes the following arguments:
- `f_min_`: first bound of the frequency range used to approximate the given function (Hz),
- `f_max`: second bound of the frequency range used to approximate the given function (Hz),
- `f_n`: the number of frequency points in frequency grid (Default: 50).
3. __run__ - is inherited by all child classes, solves the problem described by the given relaxation function (main operational method).
It consists of following steps:
1) Check the validity of the inputs using ```check_inputs``` method.
2) Print information about chosen approximation settings using ```print_info``` method.
3) Calculate both real and imaginary parts using ```calculation``` method, and then set ```self.rl``` and ```self.im``` properties.
4) Calling the main optimisation module using ```optimize``` method and calculate error based on ```error``` method.
a) [OPTIONAL] If number of debye poles is set to -1 optimization procedure is repeated until the percentage error is les than 5% or 20 Debye poles is reached.
5) Print the results in gprMax format style using ```print_output``` method.
6) [OPTIONAL] Save results in gprMax style using ```save_result``` method.
7) [OPTIONAL] Plot the actual and the approximate dielectric properties using ```plot_result``` method.
4. __check_inputs__ - is called in all child classes, finds an optimal set of relaxation times that minimise an objective function using appropriate optimization procedure.
5. __calculation__ - is inherited by all child classes, should be definied in all new chil classes, approximates the given relaxation function.
6. __print_info__ - is inherited by all child classes, prints readable string of parameters for given approximation settings.
7. __optimize__ - is inherited by all child classes, calls the main optimisation module with defined lower and upper boundaries of search.
8. __print_output__ - is inherited by all child classes, prints out the resulting Debye parameters in a gprMax format.
9. __plot_result__ - is inherited by all child classes, plots the actual and the approximated electric permittivity, along with relative error for real and imaginary parts using a semilogarithm X axes.
10. __save_result__ - is inherited by all child classes, saves the resulting Debye parameters in a gprMax format.
11. __error__ -is inherited by all child classes, calculates the average fractional error separately for relative permittivity (real part) and conductivity (imaginary part).
Each new class of relaxation object should:
- define constructor with appropriate arguments,
- define __check_inputs__ method to check relaxation class specific parameters,
- overload __calculation__ method.

查看文件

@@ -0,0 +1,836 @@
# Copyright (C) 2015-2021, Craig Warren
#
# This module is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
#
# Please use the attribution at http://dx.doi.org/10.1190/1.3548506
import logging
import gprMax
logger = logging.getLogger(__name__)
def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs):
"""Inserts a description of an antenna similar to the GSSI 1.5GHz antenna.
Can be used with 1mm (default) or 2mm spatial resolution. The external
dimensions of the antenna are 170x108x45mm. One output point is defined
between the arms of the receiver bowtie. The bowties are aligned with
the y axis so the output is the y component of the electric field.
Args:
x, y, z (float): Coordinates of a location in the model to insert the
antenna. Coordinates are relative to the geometric
centre of the antenna in the x-y plane and the
bottom of the antenna skid in the z direction.
resolution (float): Spatial resolution for the antenna model.
kwargs (dict): Optional variables, e.g. can be fed from an optimisation
process.
Returns:
scene_objects (list): All model objects that will be part of a scene.
"""
# All model objects that will be returned by function
scene_objects = []
# Antenna geometry properties
casesize = (0.170, 0.108, 0.043)
casethickness = 0.002
shieldthickness = 0.002
foamsurroundthickness = 0.003
pcbthickness = 0.002
skidthickness = 0.004
bowtiebase = 0.022
bowtieheight = 0.014
patchheight = 0.015
x = x - (casesize[0] / 2)
y = y - (casesize[1] / 2)
# Coordinates of source excitation point in antenna
tx = x + 0.114, y + 0.053, z + skidthickness
if resolution == 0.001:
dx = 0.001
dy = 0.001
dz = 0.001
elif resolution == 0.002:
dx = 0.002
dy = 0.002
dz = 0.002
foamsurroundthickness = 0.002
patchheight = 0.016
tx = x + 0.114, y + 0.052, z + skidthickness
else:
logger.exception('This antenna module can only be used with a spatial discretisation of 1mm or 2mm')
raise ValueError
# If using parameters from an optimisation
try:
kwargs
absorber1Er = kwargs['absorber1Er']
absorber1sig = kwargs['absorber1sig']
absorber2Er = kwargs['absorber2Er']
absorber2sig = kwargs['absorber2sig']
pcbEr = kwargs['pcbEr']
pcbsig = kwargs['pcbsig']
hdpeEr = kwargs['hdpeEr']
hdpesig = kwargs['hdpesig']
sourceresistance = 195
rxres = 50
absorber1 = gprMax.Material(er=absorber1Er, se=absorber1sig, mr=1, sm=0, id='absorber1')
absorber2 = gprMax.Material(er=absorber2Er, se=absorber2sig, mr=1, sm=0, id='absorber2')
pcb = gprMax.Material(er=pcbEr, se=pcbsig, mr=1, sm=0, id='pcb')
hdpe = gprMax.Material(er=hdpeEr, se=hdpesig, mr=1, sm=0, id='hdpe')
scene_objects.extend((absorber1, absorber2, pcb, hdpe))
# Otherwise choose parameters for different optimisation models
except:
# Specify optimisation model
optstate = ['WarrenThesis', 'DebyeAbsorber', 'GiannakisPaper']
optstate = optstate[0]
if optstate == 'WarrenThesis':
# Original optimised values from http://hdl.handle.net/1842/4074
excitationfreq = 1.71e9
sourceresistance = 230 # Correction for old (< 123) GprMax3D bug (optimised to 4)
rxres = 925 # Resistance at Rx bowtie
absorber1 = gprMax.Material(er=1.58, se=0.428, mr=1, sm=0, id='absorber1')
absorber2 = gprMax.Material(er=3, se=0, mr=1, sm=0, id='absorber2') # Foam modelled as PCB material
pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id='pcb')
hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id='hdpe')
rxres = gprMax.Material(er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id='rxres')
scene_objects.extend((absorber1, absorber2, pcb, hdpe, rxres))
elif optstate == 'DebyeAbsorber':
# Same values as WarrenThesis but uses dispersive absorber properties for Eccosorb LS22
excitationfreq = 1.71e9
sourceresistance = 230 # Correction for old (< 123) GprMax3D bug (optimised to 4)
rxres = 925 # Resistance at Rx bowtie
absorber1 = gprMax.Material(er=1, se=0, mr=1, sm=0, id='absorber1')
# Eccosorb LS22 3-pole Debye model (https://bitbucket.org/uoyaeg/aegboxts/wiki/Home)
absorber1_disp = gprMax.AddDebyeDispersion(poles=3, er_delta=[3.7733, 3.14418, 20.2441],
tau=[1.00723e-11, 1.55686e-10, 3.44129e-10],
material_ids=['absorber1'])
absorber2 = gprMax.Material(er=3, se=0, mr=1, sm=0, id='absorber2') # Foam modelled as PCB material
pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id='pcb')
hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id='hdpe')
rxres = gprMax.Material(er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id='rxres')
scene_objects.extend((absorber1, absorber1_disp, absorber2, pcb, hdpe, rxres))
elif optstate == 'GiannakisPaper':
# Further optimised values from https://doi.org/10.1109/TGRS.2018.2869027
sourceresistance = 195
absorber1 = gprMax.Material(er=3.96, se=0.31, mr=1, sm=0, id='absorber1')
absorber2 = gprMax.Material(er=1.05, se=1.01, mr=1, sm=0, id='absorber2')
pcb = gprMax.Material(er=1.37, se=0.0002, mr=1, sm=0, id='pcb')
hdpe = gprMax.Material(er=1.99, se=0.013, mr=1, sm=0, id='hdpe')
scene_objects.extend((absorber1, absorber2, pcb, hdpe))
# Antenna geometry
# Plastic case
b1 = gprMax.Box(p1=(x, y, z + skidthickness),
p2=(x + casesize[0], y + casesize[1], z + skidthickness + casesize[2]),
material_id='hdpe')
b2 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness),
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness,
z + skidthickness + casesize[2] - casethickness),
material_id='free_space')
# Metallic enclosure
b3 = gprMax.Box(p1=(x + 0.025, y + casethickness, z + skidthickness),
p2=(x + casesize[0] - 0.025, y + casesize[1] - casethickness,
z + skidthickness + 0.027), material_id='pec')
# Absorber material (absorber1) and foam (absorber2) around edge of absorber
b4 = gprMax.Box(p1=(x + 0.025 + shieldthickness, y + casethickness + shieldthickness,
z + skidthickness), p2=(x + 0.025 + shieldthickness + 0.057,
y + casesize[1] - casethickness - shieldthickness,
z + skidthickness + 0.027 - shieldthickness - 0.001),
material_id='absorber2')
b5 = gprMax.Box(p1=(x + 0.025 + shieldthickness + foamsurroundthickness,
y + casethickness + shieldthickness + foamsurroundthickness,
z + skidthickness),
p2=(x + 0.025 + shieldthickness + 0.057 - foamsurroundthickness,
y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness,
z + skidthickness + 0.027 - shieldthickness),
material_id='absorber1')
b6 = gprMax.Box(p1=(x + 0.086, y + casethickness + shieldthickness, z + skidthickness),
p2=(x + 0.086 + 0.057, y + casesize[1] - casethickness - shieldthickness,
z + skidthickness + 0.027 - shieldthickness - 0.001),
material_id='absorber2')
b7 = gprMax.Box(p1=(x + 0.086 + foamsurroundthickness,
y + casethickness + shieldthickness + foamsurroundthickness,
z + skidthickness),
p2=(x + 0.086 + 0.057 - foamsurroundthickness,
y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness,
z + skidthickness + 0.027 - shieldthickness),
material_id='absorber1')
# PCB
b8 = gprMax.Box(p1=(x + 0.025 + shieldthickness + foamsurroundthickness,
y + casethickness + shieldthickness + foamsurroundthickness,
z + skidthickness),
p2=(x + 0.086 - shieldthickness - foamsurroundthickness,
y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness,
z + skidthickness + pcbthickness), material_id='pcb')
b9 = gprMax.Box(p1=(x + 0.086 + foamsurroundthickness,
y + casethickness + shieldthickness + foamsurroundthickness,
z + skidthickness),
p2=(x + 0.086 + 0.057 - foamsurroundthickness,
y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness,
z + skidthickness + pcbthickness), material_id='pcb')
scene_objects.extend((b1, b2, b3, b4, b5, b6, b7, b8, b9))
# PCB components
if resolution == 0.001:
# Rx & Tx bowties
a = 0
b = 0
while b < 13:
p1 = gprMax.Plate(p1=(x + 0.045 + a * dx, y + 0.039 + b * dx, z + skidthickness),
p2=(x + 0.065 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness),
material_id='pec')
p2 = gprMax.Plate(p1=(x + 0.045 + a * dx, y + 0.067 - b * dx, z + skidthickness),
p2=(x + 0.065 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness),
material_id='pec')
p3 = gprMax.Plate(p1=(x + 0.104 + a * dx, y + 0.039 + b * dx, z + skidthickness),
p2=(x + 0.124 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness),
material_id='pec')
p4 = gprMax.Plate(p1=(x + 0.104 + a * dx, y + 0.067 - b * dx, z + skidthickness),
p2=(x + 0.124 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness),
material_id='pec')
scene_objects.extend((p1, p2, p3, p4))
b += 1
if a == 2 or a == 4 or a == 7:
p5 = gprMax.Plate(p1=(x + 0.045 + a * dx, y + 0.039 + b * dx, z + skidthickness),
p2=(x + 0.065 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness),
material_id='pec')
p6 = gprMax.Plate(p1=(x + 0.045 + a * dx, y + 0.067 - b * dx, z + skidthickness),
p2=(x + 0.065 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness),
material_id='pec')
p7 = gprMax.Plate(p1=(x + 0.104 + a * dx, y + 0.039 + b * dx, z + skidthickness),
p2=(x + 0.124 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness),
material_id='pec')
p8 = gprMax.Plate(p1=(x + 0.104 + a * dx, y + 0.067 - b * dx, z + skidthickness),
p2=(x + 0.124 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness),
material_id='pec')
b += 1
scene_objects.extend((p5, p6, p7, p8))
a += 1
# Rx extension section (upper y)
p9 = gprMax.Plate(p1=(x + 0.044, y + 0.068, z + skidthickness),
p2=(x + 0.044 + bowtiebase, y + 0.068 + patchheight, z + skidthickness),
material_id='pec')
# Tx extension section (upper y)
p10 = gprMax.Plate(p1=(x + 0.103, y + 0.068, z + skidthickness),
p2=(x + 0.103 + bowtiebase, y + 0.068 + patchheight, z + skidthickness),
material_id='pec')
scene_objects.extend((p9, p10))
# Edges that represent wire between bowtie halves in 1mm model
e1 = gprMax.Edge(p1=(tx[0] - 0.059, tx[1] - dy, tx[2]),
p2=(tx[0] - 0.059, tx[1], tx[2]), material_id='pec')
e2 = gprMax.Edge(p1=(tx[0] - 0.059, tx[1] + dy, tx[2]),
p2=(tx[0] - 0.059, tx[1] + 0.002, tx[2]), material_id='pec')
e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]),
p2=(tx[0], tx[1], tx[2]), material_id='pec')
e4 = gprMax.Edge(p1=(tx[0], tx[1] + dz, tx[2]),
p2=(tx[0], tx[1] + 0.002, tx[2]), material_id='pec')
scene_objects.extend((e1, e2, e3, e4))
elif resolution == 0.002:
# Rx & Tx bowties
for a in range(0, 6):
p1 = gprMax.Plate(p1=(x + 0.044 + a * dx, y + 0.040 + a * dx, z + skidthickness),
p2=(x + 0.066 - a * dx, y + 0.040 + a * dx + dy, z + skidthickness),
material_id='pec')
p2 = gprMax.Plate(p1=(x + 0.044 + a * dx, y + 0.064 - a * dx, z + skidthickness),
p2=(x + 0.066 - a * dx, y + 0.064 - a * dx + dy, z + skidthickness),
material_id='pec')
p3 = gprMax.Plate(p1=(x + 0.103 + a * dx, y + 0.040 + a * dx, z + skidthickness),
p2=(x + 0.125 - a * dx, y + 0.040 + a * dx + dy, z + skidthickness),
material_id='pec')
p4 = gprMax.Plate(p1=(x + 0.103 + a * dx, y + 0.064 - a * dx, z + skidthickness),
p2=(x + 0.125 - a * dx, y + 0.064 - a * dx + dy, z + skidthickness),
material_id='pec')
# Rx extension section (upper y)
p5 = gprMax.Plate(p1=(x + 0.044, y + 0.066, z + skidthickness),
p2=(x + 0.044 + bowtiebase, y + 0.066 + patchheight, z + skidthickness),
material_id='pec')
# Tx extension section (upper y)
p6 = gprMax.Plate(p1=(x + 0.103, y + 0.066, z + skidthickness),
p2=(x + 0.103 + bowtiebase, y + 0.066 + patchheight, z + skidthickness),
material_id='pec')
scene_objects.extend((p1, p2, p3, p4, p5, p6))
# Rx extension section (lower y)
p11 = gprMax.Plate(p1=(x + 0.044, y + 0.024, z + skidthickness),
p2=(x + 0.044 + bowtiebase, y + 0.024 + patchheight, z + skidthickness),
material_id='pec')
# Tx extension section (lower y)
p12 = gprMax.Plate(p1=(x + 0.103, y + 0.024, z + skidthickness),
p2=(x + 0.103 + bowtiebase, y + 0.024 + patchheight, z + skidthickness),
material_id='pec')
scene_objects.extend((p11, p12))
# Skid
b10 = gprMax.Box(p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + skidthickness),
material_id='hdpe')
scene_objects.append(b10)
# Geometry views
gv1 = gprMax.GeometryView(p1=(x - dx, y - dy, z - dz), p2=(x + casesize[0] + dx,
y + casesize[1] + dy, z + skidthickness + casesize[2] + dz),
dl=(dx, dy, dz), filename='antenna_like_GSSI_1500',
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_GSSI_1500_pcb',
output_type='f')
# scene_objects.extend((gv1, gv2))
# Excitation
if optstate == 'WarrenThesis' or optstate == 'DebyeAbsorber':
# Gaussian pulse
w1 = gprMax.Waveform(wave_type='gaussian', amp=1,
freq=excitationfreq, id='my_gaussian')
vs1 = gprMax.VoltageSource(polarisation='y', p1=(tx[0], tx[1], tx[2]),
resistance=sourceresistance, waveform_id='my_gaussian')
scene_objects.extend((w1, vs1))
elif optstate == 'GiannakisPaper':
# Optimised custom pulse
exc1 = gprMax.ExcitationFile(filepath='user_libs/GPRAntennaModels/GSSI1p5optpulse.txt',
kind='linear', fill_value='extrapolate')
vs1 = gprMax.VoltageSource(polarisation='y', p1=(tx[0], tx[1], tx[2]),
resistance=sourceresistance, waveform_id='GSSI1p5optpulse')
scene_objects.extend((exc1, vs1))
# Output point - receiver bowtie
if resolution == 0.001:
if optstate == 'WarrenThesis' or optstate == 'DebyeAbsorber':
e1 = gprMax.Edge(p1=(tx[0] - 0.059, tx[1], tx[2]),
p2=(tx[0] - 0.059, tx[1] + dy, tx[2]),
material_id='rxres')
scene_objects.append(e1)
r1 = gprMax.Rx(p1=(tx[0] - 0.059, tx[1], tx[2]), id='rxbowtie',
outputs='Ey')
scene_objects.append(r1)
elif resolution == 0.002:
if optstate == 'WarrenThesis' or optstate == 'DebyeAbsorber':
e1 = gprMax.Edge(p1=(tx[0] - 0.060, tx[1], tx[2]),
p2=(tx[0] - 0.060, tx[1] + dy, tx[2]),
material_id='rxres')
scene_objects.append(e1)
r1 = gprMax.Rx(p1=(tx[0] - 0.060, tx[1], tx[2]), id='rxbowtie',
outputs='Ey')
scene_objects.append(r1)
return scene_objects
def antenna_like_GSSI_400(x, y, z, resolution=0.001, **kwargs):
"""Inserts a description of an antenna similar to the GSSI 400MHz antenna.
Can be used with 0.5mm, 1mm (default) or 2mm spatial resolution.
The external dimensions of the antenna are 300x300x178mm.
One output point is defined between the arms of the receiver bowtie.
The bowties are aligned with the y axis so the output is the y component
of the electric field.
Args:
x, y, z (float): Coordinates of a location in the model to insert the
antenna. Coordinates are relative to the geometric
centre of the antenna in the x-y plane and the
bottom of the antenna skid in the z direction.
resolution (float): Spatial resolution for the antenna model.
kwargs (dict): Optional variables, e.g. can be fed from an optimisation
process.
Returns:
scene_objects (list): All model objects that will be part of a scene.
"""
# All model objects that will be returned by function
scene_objects = []
# Antenna geometry properties
casesize = (0.3, 0.3, 0.178) # original
casethickness = 0.002
shieldthickness = 0.002
foamsurroundthickness = 0.003
pcbthickness = 0.002
bowtiebase = 0.06
bowtieheight = 0.06 # original 0.056
patchheight = 0.06 # original 0.056
metalboxheight = 0.089
metalmiddleplateheight = 0.11
smooth_dec = 'yes' # choose to use dielectric smoothing or not
src_type = 'voltage_source' # source type. "voltage_source" or "transmission_line"
pcber = 2.35
hdper = 2.35
skidthickness = 0.01
# If using parameters from an optimisation
try:
kwargs
excitationfreq = kwargs['excitationfreq']
sourceresistance = kwargs['sourceresistance']
receiverresistance = sourceresistance
absorberEr = kwargs['absorberEr']
absorbersig = kwargs['absorbersig']
# Otherwise choose pre-set optimised parameters
except:
excitationfreq = 0.39239891e9 # GHz
sourceresistance = 111.59927 # Ohms
receiverresistance = sourceresistance # Ohms
absorberEr = 1.1
absorbersig = 0.062034689 # S/m
x = x - (casesize[0] / 2)
y = y - (casesize[1] / 2)
# Coordinates of source excitation point in antenna
tx = x + 0.01 + 0.005 + 0.056, y + casethickness + 0.005 + 0.143, z + skidthickness
if resolution == 0.0005:
dx = 0.0005
dy = 0.0005
dz = 0.0005
tx = x + 0.01 + 0.005 + 0.056, y + casethickness + 0.005 + 0.1435, z + skidthickness
elif resolution == 0.001:
dx = 0.001
dy = 0.001
dz = 0.001
elif resolution == 0.002:
dx = 0.002
dy = 0.002
dz = 0.002
foamsurroundthickness = 0.002
metalboxheight = 0.088
tx = x + 0.01 + 0.004 + 0.056, y + casethickness + 0.005 + 0.143 - 0.002, z + skidthickness
else:
logger.exception('This antenna module can only be used with a spatial discretisation of 0.5mm, 1mm, 2mm')
raise ValueError
# Material definitions
absorber = gprMax.Material(er=absorberEr, se=absorbersig, mr=1, sm=0, id='absorber')
pcb = gprMax.Material(er=pcber, se=0, mr=1, sm=0, id='pcb')
hdpe = gprMax.Material(er=hdper, se=0, mr=1, sm=0, id='hdpe')
scene_objects.extend((absorber, pcb, hdpe))
# Antenna geometry
if smooth_dec == 'yes':
# Plastic case
b1 = gprMax.Box(p1=(x, y, z + skidthickness - 0.002),
p2=(x + casesize[0], y + casesize[1], z + casesize[2]),
material_id='hdpe')
b2 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness - 0.002),
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness,
z + casesize[2] - casethickness), material_id='free_space')
# Metallic enclosure
b3 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness +
(metalmiddleplateheight - metalboxheight)),
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness,
z + skidthickness + (metalmiddleplateheight - metalboxheight) +
metalboxheight), material_id='pec')
# Absorber, and foam (modelled as PCB material) around edge of absorber
b4 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness),
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness,
z + skidthickness + (metalmiddleplateheight - metalboxheight)),
material_id='absorber')
b5 = gprMax.Box(p1=(x + casethickness + shieldthickness, y + casethickness +
shieldthickness, z + skidthickness + (metalmiddleplateheight - metalboxheight)),
p2=(x + casesize[0] - casethickness - shieldthickness,
y + casesize[1] - casethickness - shieldthickness,
z + skidthickness - shieldthickness + metalmiddleplateheight),
material_id='absorber')
scene_objects.extend((b1, b2, b3, b4, b5))
# PCB
if resolution == 0.0005:
b6 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.018, y + casethickness +
0.005 + 0.0235, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.034 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb')
b7 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.178, y + casethickness +
0.005 + 0.0235, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.194 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb')
scene_objects.extend((b6, b7))
elif resolution == 0.001:
b6 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.023,
z + skidthickness), p2=(x + 0.01 + 0.005 + 0.034 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb')
b7 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.023,
z + skidthickness), p2=(x + 0.01 + 0.005 + 0.194 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb')
scene_objects.extend((b6, b7))
elif resolution == 0.002:
b6 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.017, y + casethickness + 0.005 + 0.021,
z + skidthickness), p2=(x + 0.01 + 0.005 + 0.033 + bowtiebase,
y + casethickness + 0.006 + 0.202 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb')
b7 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.179, y + casethickness + 0.005 + 0.021,
z + skidthickness), p2=(x + 0.01 + 0.005 + 0.195 + bowtiebase,
y + casethickness + 0.006 + 0.202 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb')
scene_objects.extend((b6, b7))
elif smooth_dec == 'no':
# Plastic case
b8 = gprMax.Box(p1=(x, y, z + skidthickness - 0.002),
p2=(x + casesize[0], y + casesize[1], z + casesize[2]),
material_id='hdpe', averaging='n')
b9 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness - 0.002),
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness,
z + casesize[2] - casethickness), material_id='free_space',
averaging='n')
# Metallic enclosure
b10 = gprMax.Box(p1=(x + casethickness, y + casethickness,
z + skidthickness + (metalmiddleplateheight - metalboxheight)),
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness,
z + skidthickness + (metalmiddleplateheight - metalboxheight) +
metalboxheight), material_id='pec')
# Absorber, and foam (modelled as PCB material) around edge of absorber
b11 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness),
p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness,
z + skidthickness + (metalmiddleplateheight - metalboxheight)),
material_id='absorber', averaging='n')
b12 = gprMax.Box(p1=(x + casethickness + shieldthickness,
y + casethickness + shieldthickness,
z + skidthickness + (metalmiddleplateheight - metalboxheight)),
p2=(x + casesize[0] - casethickness - shieldthickness,
y + casesize[1] - casethickness - shieldthickness,
z + skidthickness - shieldthickness + metalmiddleplateheight),
material_id='absorber', averaging='n')
scene_objects.extend((b8, b9, b10, b11, b12))
# PCB
if resolution == 0.0005:
b13 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.018,
y + casethickness + 0.005 + 0.0235, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.034 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb',
averaging='n')
b14 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.178,
y + casethickness + 0.005 + 0.0235, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.194 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb',
averaging='n')
scene_objects.extend((b13, b14))
elif resolution == 0.001:
b13 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.018,
y + casethickness + 0.005 + 0.023, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.034 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb',
averaging='n')
b14 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.178,
y + casethickness + 0.005 + 0.023, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.194 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb',
averaging='n')
scene_objects.extend((b13, b14))
elif resolution == 0.002:
b13 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.017,
y + casethickness + 0.005 + 0.021, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.033 + bowtiebase,
y + casethickness + 0.006 + 0.202 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb',
averaging='n')
b14 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.179,
y + casethickness + 0.005 + 0.021, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.195 + bowtiebase,
y + casethickness + 0.006 + 0.202 + patchheight,
z + skidthickness + pcbthickness), material_id='pcb',
averaging='n')
scene_objects.extend((b13, b14))
# PCB components
# My own bowties with triangle commands
if resolution == 0.0005:
# "left" side
# extension plates
p1 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.026,
y + casethickness + 0.005 + 0.0235, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase,
y + casethickness + 0.005 + 0.0235 + patchheight,
z + skidthickness), material_id='pec')
p2 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.026,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness), material_id='pec')
# triangles
t1 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.026,
y + casethickness + 0.005 + 0.0835, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase,
y + casethickness + 0.005 + 0.0835, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.026 + (bowtiebase/2),
y + casethickness + 0.005 + 0.0835 + bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
t2 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.026,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.026 + (bowtiebase/2),
y + casethickness + 0.005 + 0.204 - bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
# "right" side
p3 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.186,
y + casethickness + 0.005 + 0.0235, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase,
y + casethickness + 0.005 + 0.0235 + patchheight,
z + skidthickness), material_id='pec')
p4 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.186,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness), material_id='pec')
# triangles
t3 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.186,
y + casethickness + 0.005 + 0.0835, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase,
y + casethickness + 0.005 + 0.0835, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.186 + (bowtiebase/2),
y + casethickness + 0.005 + 0.0835 + bowtieheight,
z + skidthickness), thickness=0, material_ID='pec')
t4 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.186,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.186 + (bowtiebase/2),
y + casethickness + 0.005 + 0.204 - bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
# Edges that represent wire between bowtie halves in 1mm model
e1 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] - dy, tx[2]),
p2=(tx[0] + 0.16, tx[1], tx[2]), material_id='pec')
e2 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] + dy, tx[2]),
p2=(tx[0] + 0.16, tx[1] + 2*dy, tx[2]), material_id='pec')
e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]),
p2=(tx[0], tx[1], tx[2]), material_id='pec')
e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]),
p2=(tx[0], tx[1] + 2*dy, tx[2]), material_id='pec')
scene_objects.extend((p1, p2, t1, t2, p3, p4, t3, t4, e1, e2, e3, e4))
elif resolution == 0.001:
# "left" side
# extension plates
p1 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.026,
y + casethickness + 0.005 + 0.023, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase,
y + casethickness + 0.005 + 0.023 + patchheight,
z + skidthickness), material_id='pec')
p2 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.026,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness), material_id='pec')
# triangles
t1 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.026,
y + casethickness + 0.005 + 0.083, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase,
y + casethickness + 0.005 + 0.083, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.026 + (bowtiebase/2),
y + casethickness + 0.005 + 0.083 + bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
t2 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.026,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.026 + (bowtiebase/2),
y + casethickness + 0.005 + 0.204 - bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
# "right" side
p3 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.186,
y + casethickness + 0.005 + 0.023, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase,
y + casethickness + 0.005 + 0.023 + patchheight,
z + skidthickness), material_id='pec')
p4 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.186,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase,
y + casethickness + 0.005 + 0.204 + patchheight,
z + skidthickness), material_id='pec')
# triangles
t3 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.186,
y + casethickness + 0.005 + 0.083, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase,
y + casethickness + 0.005 + 0.083, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.186 + (bowtiebase/2),
y + casethickness + 0.005 + 0.083 + bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
t4 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.186,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase,
y + casethickness + 0.005 + 0.204, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.186 + (bowtiebase/2),
y + casethickness + 0.005 + 0.204 - bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
# Edges that represent wire between bowtie halves in 1mm model
e1 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] - dy, tx[2]),
p2=(tx[0] + 0.16, tx[1], tx[2]), material_id='pec')
e2 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] + dy, tx[2]),
p2=(tx[0] + 0.16, tx[1] + 2*dy, tx[2]), material_id='pec')
e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]),
p2=(tx[0], tx[1], tx[2]), material_id='pec')
e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]),
p2=(tx[0], tx[1] + 2*dy, tx[2]), material_id='pec')
scene_objects.extend((p1, p2, t1, t2, p3, p4, t3, t4, e1, e2, e3, e4))
elif resolution == 0.002:
# "left" side
# extension plates
p1 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.025,
y + casethickness + 0.005 + 0.021, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase,
y + casethickness + 0.005 + 0.021 + patchheight,
z + skidthickness), material_id='pec')
p2 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.025,
y + casethickness + 0.005 + 0.203, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase,
y + casethickness + 0.005 + 0.203 + patchheight,
z + skidthickness), material_id='pec')
# triangles
t1 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.025,
y + casethickness + 0.005 + 0.081, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase,
y + casethickness + 0.005 + 0.081, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.025 + (bowtiebase/2),
y + casethickness + 0.005 + 0.081 + bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
t2 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.025,
y + casethickness + 0.005 + 0.203, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase,
y + casethickness + 0.005 + 0.203, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.025 + (bowtiebase/2),
y + casethickness + 0.005 + 0.203 - bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
# "right" side
p3 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.187,
y + casethickness + 0.005 + 0.021, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase,
y + casethickness + 0.005 + 0.021 + patchheight,
z + skidthickness), material_id='pec')
p4 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.187,
y + casethickness + 0.005 + 0.203, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase,
y + casethickness + 0.005 + 0.203 + patchheight,
z + skidthickness), material_id='pec')
# triangles
t3 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.187,
y + casethickness + 0.005 + 0.081, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase,
y + casethickness + 0.005 + 0.081, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.187 + (bowtiebase/2),
y + casethickness + 0.005 + 0.081 + bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
t4 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.187,
y + casethickness + 0.005 + 0.203, z + skidthickness),
p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase,
y + casethickness + 0.005 + 0.203, z + skidthickness),
p3=(x + 0.01 + 0.005 + 0.187 + (bowtiebase/2),
y + casethickness + 0.005 + 0.203 - bowtieheight,
z + skidthickness), thickness=0, material_id='pec')
# Edges that represent wire between bowtie halves in 2mm model
e1 = gprMax.Edge(p1=(tx[0] + 0.162, tx[1] - dy, tx[2]),
p2=(tx[0] + 0.162, tx[1], tx[2]), material_id='pec')
e2 = gprMax.Edge(p1=(tx[0] + 0.162, tx[1] + dy, tx[2]),
p2=(tx[0] + 0.162, tx[1] + 2*dy, tx[2]), material_id='pec')
e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]),
p2=(tx[0], tx[1], tx[2]), material_id='pec')
e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]),
p2=(tx[0], tx[1] + 2*dy, tx[2]), material_id='pec')
scene_objects.extend((p1, p2, t1, t2, p3, p4, t3, t4, e1, e2, e3, e4))
# Metallic plate extension
b15 = gprMax.Box(p1=(x + (casesize[0] / 2), y + casethickness, z + skidthickness),
p2=(x + (casesize[0] / 2) + shieldthickness,
y + casesize[1] - casethickness, z + skidthickness + metalmiddleplateheight),
material_id='pec')
# Skid
if smooth_dec == 'yes':
b16 = gprMax.Box(p1=(x, y, z),
p2=(x + casesize[0], y + casesize[1], z + skidthickness - 0.002),
material_id='hdpe')
elif smooth_dec == 'no':
b16 = gprMax.Box(p1=(x, y, z),
p2=(x + casesize[0], y + casesize[1], z + skidthickness - 0.002),
material_id='hdpe', averaging='n')
scene_objects.extend((b15, b16))
# Source
# Excitation - Gaussian pulse
w1 = gprMax.Waveform(wave_type='gaussian', amp=1, freq=excitationfreq, id='my_gaussian')
scene_objects.append(w1)
if src_type == 'voltage_source':
vs1 = gprMax.VoltageSource(polarisation='y', p1=(tx[0], tx[1], tx[2]),
resistance=sourceresistance, waveform_id='my_gaussian')
scene_objects.append(vs1)
elif src_type == 'transmission_line':
tl1 = gprMax.TransmissionLine(polarisation='y', p1=(tx[0], tx[1], tx[2]),
resistance=sourceresistance, waveform_id='my_gaussian')
scene_objects.append(tl1)
# Receiver
# Zero waveform to use with transmission line at receiver output
w2 = gprMax.Waveform(wave_type='gaussian', amp=0, freq=excitationfreq, id='my_zero_wave')
scene_objects.append(w2)
if resolution == 0.001 or resolution == 0.0005:
if src_type == 'transmission_line':
tl2 = gprMax.TransmissionLine(polarisation='y', p1=(tx[0] + 0.16, tx[1], tx[2]),
resistance=receiverresistance, waveform_id='my_zero_wave')
scene_objects.append(tl2)
elif src_type == 'voltage_source':
r1 = gprMax.Rx(p1=(tx[0] + 0.16, tx[1], tx[2]), id='rxbowtie', outputs='Ey')
scene_objects.append(r1)
elif resolution == 0.002:
if src_type == 'transmission_line':
tl2 = gprMax.TransmissionLine(polarisation='y', p1=(tx[0] + 0.162, tx[1], tx[2]),
resistance=receiverresistance, waveform_id='my_zero_wave')
scene_objects.append(tl2)
elif src_type == 'voltage_source':
r1 = gprMax.Rx(p1=(tx[0] + 0.162, 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_GSSI_400',
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_GSSI_400_pcb',
output_type='f')
# scene_objects.extend((gv1, gv2))
return scene_objects

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

查看文件

@@ -0,0 +1,481 @@
# Copyright (C) 2015-2021, Craig Warren
#
# This module is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
#
# Please use the attribution at http://dx.doi.org/10.1190/1.3548506
import logging
import gprMax
logger = logging.getLogger(__name__)
def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs):
"""Inserts a description of an antenna similar to the MALA 1.2GHz antenna.
Can be used with 1mm (default) or 2mm spatial resolution.
The external dimensions of the antenna are 184x109x46mm.
One output point is defined between the arms of the receiver bowtie.
The bowties are aligned with the y axis so the output is the y component
of the electric field (x component if the antenna is rotated 90 degrees).
Args:
x, y, z (float): Coordinates of a location in the model to insert the
antenna. Coordinates are relative to the geometric
centre of the antenna in the x-y plane and the
bottom of the antenna skid in the z direction.
resolution (float): Spatial resolution for the antenna model.
kwargs (dict): Optional variables, e.g. can be fed from an optimisation
process.
Returns:
scene_objects (list): All model objects that will be part of a scene.
"""
# All model objects that will be returned by function
scene_objects = []
# Antenna geometry properties
casesize = (0.184, 0.109, 0.040)
casethickness = 0.002
cavitysize = (0.062, 0.062, 0.037)
cavitythickness = 0.001
pcbthickness = 0.002
polypropylenethickness = 0.003
hdpethickness = 0.003
skidthickness = 0.006
bowtieheight = 0.025
# If using parameters from an optimisation
try:
kwargs
excitationfreq = kwargs['excitationfreq']
sourceresistance = kwargs['sourceresistance']
absorberEr = kwargs['absorberEr']
absorbersig = kwargs['absorbersig']
# Otherwise choose pre-set optimised parameters
except:
# Original optimised values from http://hdl.handle.net/1842/4074
excitationfreq = 0.978e9
sourceresistance = 1000
absorberEr = 6.49
absorbersig = 0.252
x = x - (casesize[0] / 2)
y = y - (casesize[1] / 2)
# Coordinates of source excitation point in antenna
tx = x + 0.063, y + 0.052, z + skidthickness
if resolution == 0.001:
dx = 0.001
dy = 0.001
dz = 0.001
elif resolution == 0.002:
dx = 0.002
dy = 0.002
dz = 0.002
cavitysize = (0.062, 0.062, 0.036)
cavitythickness = 0.002
polypropylenethickness = 0.002
hdpethickness = 0.004
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')
raise ValueError
# SMD resistors - 3 on each Tx & Rx bowtie arm
txres = 470 # Ohms
txrescellupper = txres / 3 # Resistor over 3 cells
txsigupper = ((1 / txrescellupper) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
txrescelllower = txres / 4 # Resistor over 4 cells
txsiglower = ((1 / txrescelllower) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
rxres = 150 # Ohms
rxrescellupper = rxres / 3 # Resistor over 3 cells
rxsigupper = ((1 / rxrescellupper) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor
rxrescelllower = rxres / 4 # Resistor over 4 cells
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))
# Antenna geometry
# Shield - metallic enclosure
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')
# 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')
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')
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')
# 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,
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,
y + 0.022 + cavitysize[1] - cavitythickness,
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')
# 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,
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,
y + 0.022 + cavitysize[1] - cavitythickness,
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]),
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]),
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')
scene_objects.extend((t1, t2, e1, e2))
elif resolution == 0.002:
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]),
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')
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]),
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]),
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')
scene_objects.extend((t3, e3, t4, e4))
elif resolution == 0.002:
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]),
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')
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]),
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]),
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]),
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]),
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]),
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]),
p2=(tx[0] + 0.022 + dx, tx[1] - bowtieheight - dy, tx[2]),
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]),
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]),
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]),
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]),
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]),
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]),
p2=(tx[0] + 0.020 + dx, tx[1] - bowtieheight, tx[2]),
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]),
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]),
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]),
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]),
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]),
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]),
p2=(tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.006, tx[2]),
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]),
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]),
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]),
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]),
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]),
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]),
p2=(tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.006, tx[2]),
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]),
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]),
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]),
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]),
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]),
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]),
p2=(tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]),
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]),
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]),
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]),
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]),
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]),
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]),
p2=(tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight, tx[2]),
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]),
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]),
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]),
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]),
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]),
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]),
p2=(tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
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]),
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]),
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]),
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]),
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]),
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]),
p2=(tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]),
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')
scene_objects.extend((b19, b20))
# Excitation
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')
scene_objects.append(vs1)
# Output point - receiver bowtie
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')
scene_objects.extend((gv1, gv2))
return scene_objects

查看文件

@@ -0,0 +1,70 @@
User libraries is a sub-package where useful Python modules contributed by users are stored.
******************
GPR Antenna Models
******************
Information
===========
The package features models of antennas similar to commercial GPR antennas. The following antenna models are included:
======================== ============= ============= ========================================================================================================================================================================================================================= ================
Manufacturer/Model Dimensions Resolution(s) Author/Contact Attribution/Cite
======================== ============= ============= ========================================================================================================================================================================================================================= ================
GSSI 1.5GHz (Model 5100) 170x108x45mm 1, 2mm Craig Warren (craig.warren@northumbria.ac.uk), Northumbria University, UK 1,2
MALA 1.2GHz 184x109x46mm 1, 2mm Craig Warren (craig.warren@northumbria.ac.uk), Northumbria University, UK 1
GSSI 400MHz 300x300x170mm 0.5, 1, 2mm Sam Stadler (Sam.Stadler@liag-hannover.de), `Leibniz Institute for Applied Geophysics <https://www.leibniz-liag.de/en/research/methods/electromagnetic-methods/ground-penetrating-radar/guided-gpr-waves.html>`_, Germany 3
======================== ============= ============= ========================================================================================================================================================================================================================= ================
**License**: `Creative Commons Attribution-ShareAlike 4.0 International License <http://creativecommons.org/licenses/by-sa/4.0/>`_
**Attributions/citations**:
1. Warren, C., Giannopoulos, A. (2011). Creating finite-difference time-domain models of commercial ground-penetrating radar antennas using Taguchi's optimization method. *Geophysics*, 76(2), G37-G47. (http://dx.doi.org/10.1190/1.3548506)
2. Giannakis, I., Giannopoulos, A., & Warren, C. (2019). Realistic FDTD GPR antenna models optimised using a novel linear/non-linear Full Waveform Inversion. *IEEE Transactions on Geoscience and Remote Sensing*, 57(3), 1768-1778. (https://doi.org/10.1109/TGRS.2018.2869027)
3. Stadler. S., Igel J. (2018). A Numerical Study on Using Guided GPR Waves Along Metallic Cylinders in Boreholes for Permittivity Sounding. 17th International Conference on GPR. (https://tinyurl.com/y6vdab22)
Package contents
================
* ``GSSI.py`` is a module containing models of antennas similar to those manufactured by `Geophysical Survey Systems, Inc. (GSSI) <http://www.geophysical.com>`_.
* ``MALA.py`` is a module containing models of antennas similar to those manufactured by `MALA Geoscience <http://www.malags.com/>`_.
Descriptions of how the models were created can be found in the aforementioned attributions.
How to use the package
======================
The antenna models can be accessed from within a block of Python code in an input file. The models are inserted at location x,y,z. The coordinates are relative to the geometric centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction. The models must be used with cubic spatial resolutions of either 0.5mm (GSSI 400MHz antenna only), 1mm (default), or 2mm by setting the keyword argument, e.g. ``resolution=0.002``. The antenna models can be rotated 90 degrees counter-clockwise (CCW) in the x-y plane by setting the keyword argument ``rotate90=True``.
.. note::
If you are moving an antenna model within a simulation, e.g. to generate a B-scan, you should ensure that the step size you choose is a multiple of the spatial resolution of the simulation. Otherwise when the position of antenna is converted to cell coordinates the geometry maybe altered.
Example
-------
To include an antenna model similar to a GSSI 1.5 GHz antenna at a location 0.125m, 0.094m, 0.100m (x,y,z) using a 2mm cubic spatial resolution:
.. code-block:: none
#python:
from user_libs.GPRAntennaModels.GSSI import antenna_like_GSSI_1500
antenna_like_GSSI_1500(0.125, 0.094, 0.100, resolution=0.002)
#end_python:
.. figure:: ../../images_shared/antenna_like_GSSI_1500.png
:width: 600 px
FDTD geometry mesh showing an antenna model similar to a GSSI 1.5 GHz antenna (skid removed for illustrative purposes).
.. figure:: ../../images_shared/antenna_like_GSSI_400.png
:width: 600 px
FDTD geometry mesh showing an antenna model similar to a GSSI 400 MHz antenna (skid removed for illustrative purposes).
.. figure:: ../../images_shared/antenna_like_MALA_1200.png
:width: 600 px
FDTD geometry mesh showing an antenna model similar to a MALA 1.2GHz antenna (skid removed for illustrative purposes).

查看文件

二进制
toolboxes/LandmineModels/PMA_1x1x1.h5 普通文件

二进制文件未显示。

二进制
toolboxes/LandmineModels/PMA_2x2x2.h5 普通文件

二进制文件未显示。

查看文件

@@ -0,0 +1,5 @@
## PMA landmine material properties
#material: 1 inf 1 0 pec
#material: 1 0 1 0 freespace
#material: 2.5 0 1 0 plastic
#material: 6 0 1 0 rubber

二进制
toolboxes/LandmineModels/PMN_1x1x1.h5 普通文件

二进制文件未显示。

二进制
toolboxes/LandmineModels/PMN_2x2x2.h5 普通文件

二进制文件未显示。

查看文件

@@ -0,0 +1,6 @@
## PMN landmine material properties
#material: 1 inf 1 0 pec
#material: 1 0 1 0 freespace
#material: 3 0 1 0 plastic
#material: 3.5 0 1 0 bakelite
#material: 6 0 1 0 rubber

查看文件

@@ -0,0 +1,6 @@
## PMN landmine material properties
#material: 1 inf 1 0 pec
#material: 1 0 1 0 freespace
#material: 3 0 1 0 plastic
#material: 3.5 0 1 0 bakelite
#material: 6 0 1 0 rubber

查看文件

@@ -0,0 +1,87 @@
User libraries is a sub-package where useful Python modules contributed by users are stored.
***************
Landmine Models
***************
Information
===========
**Author/Contact**: Iraklis Giannakis (iraklis.giannakis@abdn.ac.uk), University of Aberdeen, UK
**License**: `Creative Commons Attribution-ShareAlike 4.0 International License <http://creativecommons.org/licenses/by-sa/4.0/>`_
**Attribution/cite**: Giannakis, I., Giannopoulos, A., Warren, C. (2016). A Realistic FDTD Numerical Modeling Framework of Ground Penetrating Radar for Landmine Detection. *IEEE Journal of Selected Topics in Applied Earth Observations and Remote Sensing*, 9(1), 37-51. (http://dx.doi.org/10.1109/JSTARS.2015.2468597)
The package currently features models of different anti-personnel (AP) landmines and a metal can which can be used as a false target. They are:
* **PMA-1**: a blast AP landmine with minimum metal content, manufactured in the former Yugoslavia. It is possible to detect the PMA-1 with a metal detector because it contains a metal fuse, but there are reports of types of PMA-1 with plastic fuses. The PMA-1 contains 200g of high explosive (TNT). The dimensions of the PMA-1 model are: 140x64x34mm.
* **PMN**: one of the oldest and most widely used landmines, it is a palm shaped cylindrical blast AP landmine, manufactured in Russia. Similar to PMA-1, the PMN contains a large amount, 240g, of high explosive (TNT). It has a minimum metal content which can make it detectable with a metal detector. The dimensions of the PMN model are: 116x156x50mm.
* **TS-50**: a blast AP landmine with minimum metal content, manufactured in Italy. The dimensions of the TS-50 model are: 90x90x44mm.
* **Metal can**: a cylindrical metal can which is can be useful as a false target. The dimensions of the metal can model are: 76x76x108mm.
The landmine models and the metal can be used with a cubic spatial resolution of either 1mm or 2mm.
The dielectric properties of the landmines were obtained through an iterative process of matching numerical and laboratory measurements of scattered electromagnetic fields in free space. A full description of how the models were created can be found at the reference given by the aforementioned attribution/cite.
Package contents
================
.. code-block:: none
can_1x1x1.h5
can_2x2x2.h5
can_materials.txt
PMA_1x1x1.h5
PMA_2x2x2.h5
PMA_materials.txt
PMN_1x1x1.h5
PMN_2x2x2.h5
PMN_materials.txt
TS50_1x1x1.h5
TS50_2x2x2.h5
TS50_materials.txt
* ``can_1x1x1.h5`` is a HDF5 file containing a description of the geometry of the metal can (false target) with a cubic spatial resolution of 1mm
* ``can_2x2x2.h5`` is a HDF5 file containing a description of the geometry of the metal can (false target) with a cubic spatial resolution of 2mm
* ``can_materials.txt`` is a text file containing material properties associated with the metal can
* ``PMA_1x1x1.h5`` is a HDF5 file containing a description of the geometry of the PMA landmine with a cubic spatial resolution of 1mm
* ``PMA_2x2x2.h5`` is a HDF5 file containing a description of the geometry of the PMA landmine with a cubic spatial resolution of 2mm
* ``PMA_materials.txt`` is a text file containing material properties associated with the PMA landmine
* ``PMN_1x1x1.h5`` is a HDF5 file containing a description of the geometry of the PMN landmine with a cubic spatial resolution of 1mm
* ``PMN_2x2x2.h5`` is a HDF5 file containing a description of the geometry of the PMN landmine with a cubic spatial resolution of 2mm
* ``PMN_materials.txt`` is a text file containing material properties associated with the PMN landmine
* ``TS50_1x1x1.h5`` is a HDF5 file containing a description of the geometry of the TS-50 landmine with a cubic spatial resolution of 1mm
* ``TS50_2x2x2.h5`` is a HDF5 file containing a description of the geometry of the TS-50 landmine with a cubic spatial resolution of 2mm
* ``TS50_materials.txt`` is a text file containing material properties associated with the TS-50 landmine
How to use the package
======================
To insert any of the landmine models or metal can into a simulation use the ``#geometry_objects_read`` command.
Example
-------
The input file for inserting the PMN landmine, with the lower left corner 10mm from the origin of the domain, into an empty domain (free-space) would be:
.. code-block:: none
#title: PMN landmine (116x156x50mm) in free space
#domain: 0.136 0.176 0.070
#dx_dy_dz: 0.001 0.001 0.001
#time_window: 5e-9
#geometry_objects_read: 0.010 0.010 0.010 ../user_libs/LandmineModels/PMN_1x1x1.h5 ../user_libs/LandmineModels/PMN_materials.txt
#geometry_view: 0 0 0 0.136 0.176 0.070 0.001 0.001 0.001 landmine_PMN_fs n
For further information on the ``#geometry_objects_read`` see the section on object contruction commands in the :ref:`Input commands section <commands>`.
.. figure:: ../../images_shared/PMA.png
:width: 600 px
FDTD geometry mesh showing the PMA-1 landmine model.
.. figure:: ../../images_shared/PMN.png
:width: 600 px
FDTD geometry mesh showing the PMN landmine model.

二进制文件未显示。

二进制文件未显示。

查看文件

@@ -0,0 +1,7 @@
## TS50 landmine material properties
#material: 1 inf 1 0 pec
#material: 1 0 1 0 freespace
#material: 2.5 0 1 0 mat25
#material: 3 0 1 0 mat3
#material: 4 0 1 0 mat4
#material: 5 0 1 0 mat5

二进制
toolboxes/LandmineModels/can_1x1x1.h5 普通文件

二进制文件未显示。

二进制
toolboxes/LandmineModels/can_2x2x2.h5 普通文件

二进制文件未显示。

查看文件

@@ -0,0 +1,2 @@
## can landmine material properties
#material: 1 inf 1 0 pec

查看文件

@@ -0,0 +1,70 @@
User libraries is a sub-package where useful Python modules contributed by users are stored.
*********
Materials
*********
Information
===========
The package is intended to provide a database of electromagnetic properties of different materials. It currently includes the following material libraries:
* ``eccosorb.txt`` contains information on some of the `Eccosorb LS series <http://www.eccosorb.com/products-eccosorb-ls.htm>`_ of electromagnetic absorber materials manufactured by `Laird NV <http://www.eccosorb.eu>`_ (formerly Emerson & Cuming Microwave Products NV). LS 14, 16, 18, 20, 22, 26, 28, and 30 are included. They are simulated using a 3-pole Debye model.
How to use the package
======================
Example
-------
The simplest way to access any of the material libraries is to use the ``#include_file`` command, after which the name of the material can then be used with any object construction command:
.. code-block:: none
#include_file: user_libs/Materials/eccosorb.txt
#box: 0 0 0 0.5 0.5 0.5 eccosorb_ls22
Eccosorb
========
`Eccosorb <http://www.eccosorb.eu>`_ are electromagnetic absorber materials manufactured by `Laird NV <http://www.eccosorb.eu>`_ (formerly Emerson & Cuming Microwave Products NV). Currently models for some of the LS series (14, 16, 18, 20, 22, 26, 28, and 30) are included in this library. The models were created by fitting a 3-pole Debye model to the real and imaginary parts of the relative permittivity taken from the `manufacturers datasheet <http://www.eccosorb.com/Collateral/Documents/English-US/Electrical%20Parameters/ls%20parameters.pdf>`_. The following figures show the fitting.
.. figure:: ../../images_shared/eccosorb_ls14.png
:width: 600 px
3-pole Debye fit for Eccosorb LS14 absorber (HN indicates data from manufacturer datasheet)
.. figure:: ../../images_shared/eccosorb_ls16.png
:width: 600 px
3-pole Debye fit for Eccosorb LS16 absorber (HN indicates data from manufacturer datasheet)
.. figure:: ../../images_shared/eccosorb_ls18.png
:width: 600 px
3-pole Debye fit for Eccosorb LS18 absorber (HN indicates data from manufacturer datasheet)
.. figure:: ../../images_shared/eccosorb_ls20.png
:width: 600 px
3-pole Debye fit for Eccosorb LS20 absorber (HN indicates data from manufacturer datasheet)
.. figure:: ../../images_shared/eccosorb_ls22.png
:width: 600 px
3-pole Debye fit for Eccosorb LS22 absorber (HN indicates data from manufacturer datasheet)
.. figure:: ../../images_shared/eccosorb_ls26.png
:width: 600 px
3-pole Debye fit for Eccosorb LS26 absorber (HN indicates data from manufacturer datasheet)
.. figure:: ../../images_shared/eccosorb_ls28.png
:width: 600 px
3-pole Debye fit for Eccosorb LS28 absorber (HN indicates data from manufacturer datasheet)
.. figure:: ../../images_shared/eccosorb_ls30.png
:width: 600 px
3-pole Debye fit for Eccosorb LS30 absorber (HN indicates data from manufacturer datasheet)

查看文件

@@ -0,0 +1,34 @@
## Eccosorb - series of electromagnetic absorbers (http://www.eccosorb.eu/products/eccosorb)
## Models based on 3-pole Debye
## LS14
#material: 1.0003252658762176 0 1 0 eccosorb_ls14
#add_dispersion_debye: 3 98.07107793080615 8.504245706576436e-08 0.17461124247006998 2.0239418236665636e-12 0.2391403235994493 5.0816292715563385e-11 eccosorb_ls14
## LS16
#material: 1.0823217177533218 0 1 0 eccosorb_ls16
#add_dispersion_debye: 3 0.743862637908311 1.2759602772812056e-10 2851.286930923839 6.326708272278631e-07 0.22078549212073995 1.0289674723407745e-11 eccosorb_ls16
## LS18
#material: 1.1313538656018765 0 1 0 eccosorb_ls18
#add_dispersion_debye: 3 0.4945710081118222 3.761526166333626e-11 46.436300997069175 4.710527761805339e-09 0.15794968774325469 3.766506348985577e-12 eccosorb_ls18
## LS20
#material: 1 0 1 0 eccosorb_ls20
#add_dispersion_debye: 3 13.743139824223658 2.0641089739387818e-09 0.43008497836321924 4.448132243057998e-12 3.0096475124312767 1.6001537943308492e-10 eccosorb_ls20
## LS22
#material: 1.086511586945162 0 1 0 eccosorb_ls22
#add_dispersion_debye: 3 26.45657825754802 2.4212038082641953e-10 0.7245919832366302 3.515027637111512e-13 0.25012538033270815 1.3216929445939351e-12 eccosorb_ls22
## LS26
#material: 1 0 1 0 eccosorb_ls26
#add_dispersion_debye: 3 68442.60232717071 1.1385473760814684e-06 39.33808907708006 8.971990079406309e-11 7.255050883574533 1.1284385113395946e-10 eccosorb_ls26
## LS28
#material: 1 0 1 0 eccosorb_ls28
#add_dispersion_debye: 3 4.87884505822887 1.0484993776024144e-11 0.9889518773555892 1.507667346114368e-14 299.87807971869313 2.6440175851701485e-10 eccosorb_ls28
## LS30
#material: 1 0 1 0 eccosorb_ls30
#add_dispersion_debye: 3 2.7374320698081696 1.7149567754908894e-12 34.04790963290593 5.095657346525705e-11 116589.2604827239 1.1495799850611809e-07 eccosorb_ls30

查看文件

@@ -0,0 +1,282 @@
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import argparse
import logging
from pathlib import Path
import h5py
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import numpy as np
from gprMax.receivers import Rx
from gprMax.utilities.utilities import fft_power
logger = logging.getLogger(__name__)
def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False):
"""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.
Args:
filename (string): Filename (including path) of output file.
outputs (list): List of field/current components to plot.
fft (boolean): Plot FFT switch.
Returns:
plt (object): matplotlib plot object.
"""
file = Path(filename)
# Open output file and read iterations
f = h5py.File(file, 'r')
# Paths to grid(s) to traverse for outputs
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()]
# Get number of receivers in grid(s)
nrxs = []
for path in paths:
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}')
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']
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')
raise ValueError
# New plot for each receiver
for rx in range(1, nrx + 1):
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] == '-':
polarity = -1
outputtext = '-' + outputs[0][0:-1]
output = outputs[0][0:-1]
else:
polarity = 1
outputtext = outputs[0]
output = outputs[0]
if output not in availableoutputs:
logger.exception(f"{output} output requested to plot, but the available output for receiver 1 is {', '.join(availableoutputs)}")
raise ValueError
outputdata = f[rxpath + output][:] * polarity
# Plotting if FFT required
if fft:
# FFT
freqs, power = fft_power(outputdata, dt)
freqmaxpower = np.where(np.isclose(power, 0))[0][0]
# Set plotting range to -60dB from maximum power or 4 times
# frequency at maximum power
try:
pltrange = np.where(power[freqmaxpower:] < -60)[0][0] + freqmaxpower + 1
except:
pltrange = freqmaxpower * 4
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]')
ax1.set_xlim([0, np.amax(time)])
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='-.')
# 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')
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)
ax.set_xlim([0, np.amax(time)])
# ax.set_ylim([-15, 20])
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 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')
if len(outputs) == 9:
gs = gridspec.GridSpec(3, 3, hspace=0.3, wspace=0.3)
else:
gs = gridspec.GridSpec(3, 2, hspace=0.3, wspace=0.3)
for output in outputs:
# Check for polarity of output and if requested output is in file
if output[-1] == 'm':
polarity = -1
outputtext = '-' + output[0:-1]
output = output[0:-1]
else:
polarity = 1
outputtext = output
# Check if requested output is in file
if output not in availableoutputs:
logger.exception(f"Output(s) requested to plot: {', '.join(outputs)}, but available output(s) for receiver {rx} in the file: {', '.join(availableoutputs)}")
raise ValueError
outputdata = f[rxpath + output][:] * polarity
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.set_ylim([-15, 20])
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.set_ylim([-15, 20])
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.set_ylim([-15, 20])
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.set_ylim([-0.03, 0.03])
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.set_ylim([-0.03, 0.03])
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.set_ylim([-0.03, 0.03])
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 = plt.subplot(gs[1, 2])
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]')
for ax in fig.axes:
ax.set_xlim([0, np.amax(time)])
ax.grid(which='both', axis='both', linestyle='-.')
# Save a PDF/PNG of the figure
savename = file.stem + '_rx' + str(rx)
savename = file.parent / savename
# fig.savefig(savename.with_suffix('.pdf'), dpi=None, format='pdf',
# bbox_inches='tight', pad_inches=0.1)
# fig.savefig(savename.with_suffix('.png'), dpi=150, format='png',
# bbox_inches='tight', pad_inches=0.1)
f.close()
return plt
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 tools.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', help='plot FFT (single output must be specified)',
default=False)
args = parser.parse_args()
plthandle = mpl_plot(args.outputfile, args.outputs, fft=args.fft)
plthandle.show()

查看文件

@@ -0,0 +1,101 @@
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import argparse
import logging
from pathlib import Path
import h5py
import matplotlib.pyplot as plt
import numpy as np
from .outputfiles_merge import get_output_data
logger = logging.getLogger(__name__)
def mpl_plot(filename, outputdata, dt, rxnumber, rxcomponent):
"""Creates a plot (with matplotlib) of the B-scan.
Args:
filename (string): Filename (including path) of output file.
outputdata (array): Array of A-scans, i.e. B-scan data.
dt (float): Temporal resolution of the model.
rxnumber (int): Receiver output number.
rxcomponent (str): Receiver output field/current component.
Returns:
plt (object): matplotlib plot object.
"""
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]')
# Grid properties
ax = fig.gca()
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]')
# Save a PDF/PNG of the figure
# fig.savefig(file.with_suffix('.pdf'), dpi=None, format='pdf',
# bbox_inches='tight', pad_inches=0.1)
# fig.savefig(file.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='Plots a B-scan image.',
usage='cd gprMax; python -m tools.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'])
args = parser.parse_args()
# Open output file and read number of outputs (receivers)
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}')
raise ValueError
for rx in range(1, nrx + 1):
outputdata, dt = get_output_data(args.outputfile, rx, args.rx_component)
plthandle = mpl_plot(args.outputfile, outputdata, dt, rx, args.rx_component)
plthandle.show()

查看文件

@@ -0,0 +1,446 @@
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import argparse
import logging
from pathlib import Path
import h5py
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import numpy as np
logger = logging.getLogger(__name__)
def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=None, rxcomponent=None):
"""Calculates antenna parameters - incident, reflected and total volatges
and currents; s11, (s21) and input impedance.
Args:
filename (string): Filename (including path) of output file.
tltxnumber (int): Transmitter antenna - transmission line number
tlrxnumber (int): Receiver antenna - transmission line number
rxnumber (int): Receiver antenna - output number
rxcomponent (str): Receiver antenna - output electric field component
Returns:
antennaparams (dict): Antenna parameters.
"""
# 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']
# 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')
# Read/calculate voltages and currents from transmitter antenna
tltxpath = '/tls/tl' + str(tltxnumber) + '/'
# Incident voltages/currents
Vinc = f[tltxpath + 'Vinc'][:]
Iinc = f[tltxpath + 'Iinc'][:]
# Total (incident + reflected) voltages/currents
Vtotal = f[tltxpath + 'Vtotal'][:]
Itotal = f[tltxpath + 'Itotal'][:]
# Reflected voltages/currents
Vref = Vtotal - Vinc
Iref = Itotal - Iinc
# 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'][:]
elif 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 output for receiver {rxnumber} is {', '.join(availableoutputs)}")
raise ValueError
rxpath += rxcomponent
# Received voltage
if rxcomponent == 'Ex':
Vrec = f[rxpath][:] * -1 * dxdydz[0]
elif rxcomponent == 'Ey':
Vrec = f[rxpath][:] * -1 * dxdydz[1]
elif rxcomponent == 'Ez':
Vrec = f[rxpath][:] * -1 * dxdydz[2]
f.close()
# Frequency bins
freqs = np.fft.fftfreq(Vinc.size, d=dt)
# Delay correction - current lags voltage, so delay voltage to match current timestep
delaycorrection = np.exp(1j * 2 * np.pi * freqs * (dt / 2))
# Calculate s11 and (optionally) s21
s11 = np.abs(np.fft.fft(Vref) / np.fft.fft(Vinc))
if tlrxnumber or rxnumber:
s21 = np.abs(np.fft.fft(Vrec) / np.fft.fft(Vinc))
# Calculate input impedance
zin = (np.fft.fft(Vtotal) * delaycorrection) / np.fft.fft(Itotal)
# Calculate input admittance
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'):
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)))
Irefp = 20 * np.log10(np.abs(np.fft.fft(Iref)))
Vtotalp = 20 * np.log10(np.abs((np.fft.fft(Vtotal) * delaycorrection)))
Itotalp = 20 * np.log10(np.abs(np.fft.fft(Itotal)))
s11 = 20 * np.log10(s11)
# Replace any NaNs or Infs from zero division
Vincp[np.invert(np.isfinite(Vincp))] = 0
Iincp[np.invert(np.isfinite(Iincp))] = 0
Vrefp[np.invert(np.isfinite(Vrefp))] = 0
Irefp[np.invert(np.isfinite(Irefp))] = 0
Vtotalp[np.invert(np.isfinite(Vtotalp))] = 0
Itotalp[np.invert(np.isfinite(Itotalp))] = 0
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}
if tlrxnumber or rxnumber:
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
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):
"""Plots antenna parameters - incident, reflected and total volatges and
currents; s11, (s21) and input impedance.
Args:
filename (string): Filename (including path) of output file.
time (array): Simulation time.
freq (array): Frequencies for FFTs.
Vinc, Vincp, Iinc, Iincp (array): Time and frequency domain representations of incident voltage and current.
Vref, Vrefp, Iref, Irefp (array): Time and frequency domain representations of reflected voltage and current.
Vtotal, Vtotalp, Itotal, Itotalp (array): Time and frequency domain representations of total voltage and current.
s11, s21 (array): s11 and, optionally, s21 parameters.
zin, yin (array): Input impedance and input admittance parameters.
Returns:
plt (object): matplotlib plot object.
"""
# Set plotting range
pltrangemin = 1
# To a certain drop from maximum power
pltrangemax = np.where((np.amax(Vincp[1::]) - Vincp[1::]) > 60)[0][0] + 1
# To a maximum frequency
pltrangemax = np.where(freqs > 3e9)[0][0]
pltrange = np.s_[pltrangemin:pltrangemax]
# 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 {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}{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')
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.set_xlim([0, np.amax(time)])
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='-.')
# 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.set_xlim([0, np.amax(time)])
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='-.')
# 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.set_xlim([0, np.amax(time)])
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='-.')
# 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.set_xlim([0, np.amax(time)])
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='-.')
# Plot reflected (reflected) voltage
# ax = plt.subplot(gs1[4, 0])
# ax.plot(time, Vref, 'r', lw=2, label='Vref')
# ax.set_title('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='-.')
# Plot frequency spectra of reflected voltage
# ax = plt.subplot(gs1[4, 1])
# markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vrefp[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], Vrefp[pltrange], 'r', lw=2)
# ax.set_title('Reflected voltage')
# ax.set_xlabel('Frequency [Hz]')
# ax.set_ylabel('Power [dB]')
# ax.grid(which='both', axis='both', linestyle='-.')
# Plot reflected (reflected) current
# ax = plt.subplot(gs1[5, 0])
# ax.plot(time, Iref, 'b', lw=2, label='Iref')
# ax.set_title('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='-.')
# Plot frequency spectra of reflected current
# ax = plt.subplot(gs1[5, 1])
# markerline, stemlines, baseline = ax.stem(freqs[pltrange], Irefp[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], Irefp[pltrange], 'b', lw=2)
# ax.set_title('Reflected current')
# ax.set_xlabel('Frequency [Hz]')
# ax.set_ylabel('Power [dB]')
# ax.grid(which='both', axis='both', linestyle='-.')
# Figure 2
# Plot frequency spectra of s11
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]')
# ax.set_xlim([0, 5e9])
# ax.set_ylim([-25, 0])
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]')
# ax.set_xlim([0.88e9, 1.02e9])
# ax.set_ylim([-25, 50])
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]')
# ax.set_xlim([0.88e9, 1.02e9])
ax.set_ylim(bottom=0)
# ax.set_ylim([0, 300])
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]')
# ax.set_xlim([0.88e9, 1.02e9])
# ax.set_ylim([-300, 300])
ax.grid(which='both', axis='both', linestyle='-.')
# Plot input admittance (magnitude)
# ax = plt.subplot(gs2[2, 0])
# markerline, stemlines, baseline = ax.stem(freqs[pltrange], np.abs(yin[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], np.abs(yin[pltrange]), 'g', lw=2)
# ax.set_title('Input admittance (magnitude)')
# ax.set_xlabel('Frequency [Hz]')
# ax.set_ylabel('Admittance [Siemens]')
# ax.set_xlim([0.88e9, 1.02e9])
# ax.set_ylim([0, 0.035])
# ax.grid(which='both', axis='both', linestyle='-.')
# Plot input admittance (phase)
# ax = plt.subplot(gs2[2, 1])
# markerline, stemlines, baseline = ax.stem(freqs[pltrange], np.angle(yin[pltrange], deg=True),
# '-.', 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], np.angle(yin[pltrange], deg=True), 'g', lw=2)
# ax.set_title('Input admittance (phase)')
# ax.set_xlabel('Frequency [Hz]')
# ax.set_ylabel('Phase [degrees]')
# ax.set_xlim([0.88e9, 1.02e9])
# ax.set_ylim([-40, 100])
# ax.grid(which='both', axis='both', linestyle='-.')
# Save a PDF/PNG of the figure
savename1 = filename.stem + '_tl_params'
savename1 = filename.parent / savename1
savename2 = filename.stem + '_ant_params'
savename2 = filename.parent / savename2
# fig1.savefig(savename1.with_suffix('.png'), dpi=150, format='png',
# bbox_inches='tight', pad_inches=0.1)
# fig2.savefig(savename2.with_suffix('.png'), dpi=150, format='png',
# 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)
return plt
if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(description='Plots antenna parameters (s11, s21 parameters and input impedance) from an output file containing a transmission line source.', usage='cd gprMax; python -m tools.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'])
args = parser.parse_args()
antennaparams = calculate_antenna_params(args.outputfile, args.tltx_num, args.tlrx_num, args.rx_num, args.rx_component)
plthandle = mpl_plot(args.outputfile, **antennaparams)
plthandle.show()

查看文件

@@ -0,0 +1,184 @@
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import argparse
import logging
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from gprMax.utilities.utilities import fft_power, round_value
from gprMax.waveforms import Waveform
logger = logging.getLogger(__name__)
def check_timewindow(timewindow, dt):
"""Checks and sets time window and number of iterations.
Args:
timewindow (float): Time window.
dt (float): Time discretisation.
Returns:
timewindow (float): Time window.
iterations (int): Number of interations.
"""
# Time window could be a string, float or int, so convert to string then check
timewindow = str(timewindow)
try:
timewindow = int(timewindow)
iterations = timewindow
timewindow = (timewindow - 1) * dt
except:
timewindow = float(timewindow)
if timewindow > 0:
iterations = round_value((timewindow / dt)) + 1
else:
logger.exception('Time window must have a value greater than zero')
raise ValueError
return timewindow, iterations
def mpl_plot(w, timewindow, dt, iterations, fft=False):
"""Plots waveform and prints useful information about its properties.
Args:
w (class): Waveform class instance.
timewindow (float): Time window.
dt (float): Time discretisation.
iterations (int): Number of iterations.
fft (boolean): Plot FFT switch.
Returns:
plt (object): matplotlib plot object.
"""
time = np.linspace(0, (iterations - 1) * dt, num=iterations)
waveform = np.zeros(len(time))
timeiter = np.nditer(time, flags=['c_index'])
while not timeiter.finished:
waveform[timeiter.index] = w.calculate_value(timeiter[0], dt)
timeiter.iternext()
logger.info('Waveform characteristics...')
logger.info(f'Type: {w.type}')
logger.info(f'Maximum (absolute) amplitude: {np.max(np.abs(waveform)):g}')
if w.freq and not w.type == 'gaussian':
logger.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'):
delay = 1 / w.freq
logger.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
logger.info(f'Time to centre of pulse: {delay:g} s')
logger.info(f'Time window: {timewindow:g} s ({iterations} iterations)')
logger.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
freqmaxpower = np.where(np.isclose(power, 0))[0][0]
if freqs[freqmaxpower] > w.freq:
pltrange = np.where(freqs > 4 * freqs[freqmaxpower])[0][0]
else:
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')
# Plot waveform
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]')
else:
fig, ax1 = plt.subplots(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')
# Turn on grid
[ax.grid(which='both', axis='both', linestyle='-.') for ax in fig.axes]
# Save a PDF/PNG of the figure
savefile = Path(__file__).parent / w.type
# fig.savefig(savefile.with_suffix('.pdf'), dpi=None, format='pdf',
# 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 tools.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', help='plot FFT of waveform', default=False)
args = parser.parse_args()
# Check waveform parameters
if args.type.lower() not in Waveform.types:
logger.exception(f"The waveform must have one of the following types {', '.join(Waveform.types)}")
raise ValueError
if args.freq <= 0:
logger.exception('The waveform requires an excitation frequency value of greater than zero')
raise ValueError
# Create waveform instance
w = Waveform()
w.type = args.type
w.amp = args.amp
w.freq = args.freq
timewindow, iterations = check_timewindow(args.timewindow, args.dt)
plthandle = mpl_plot(w, timewindow, args.dt, iterations, args.fft)
plthandle.show()

查看文件

@@ -0,0 +1,70 @@
User libraries is a sub-package where useful Python modules contributed by users are stored.
**********
STLtoVoxel
**********
Information
===========
**Author/Contact**: Kartik Bansal (kartikbn21000@gmail.com)
This package provides the ability to directly model real objects without having to build their geometries manually using geometry primitives such as the ``#edge``, ``#plate``, ``#box`` etc.. commands. It specifically provides a tool to convert a `STL file <https://en.wikipedia.org/wiki/STL_(file_format)>`_, which can be produced by many CAD software packages, to a voxelised mesh (FDTD Yee cells) which is saved as a geometry file in HDF5 format suitable for directly importing into gprMax.
This package was created as part of the `Google Summer of Code <https://summerofcode.withgoogle.com/>`_ programme 2021 which gprMax participated. The package uses the `stl-to-voxel <https://github.com/cpederkoff/stl-to-voxel>`_ Python library by Christian Pederkoff.
**License**: `Creative Commons Attribution-ShareAlike 4.0 International License <http://creativecommons.org/licenses/by-sa/4.0/>`_
**Attribution/cite**: TBC
Package contents
================
* ``STLtoVoxel.py`` is the main script which should be executed to convert a STL file to a voxelised mesh which is saved as a geometry file in HDF5 format suitable for directly importing into gprMax.
* ``examples`` is a folder containing example STL files as well as gprMax input files that can be used to import the resulting HDF5 geometry files.
* ``convert.py``, ``perimeter.py``, ``slice.py`` are modules adapted from the `stl-to-voxel <https://github.com/cpederkoff/stl-to-voxel>`_ Python library by Christian Pederkoff.
* ``license.md`` is the license for the `stl-to-voxel <https://github.com/cpederkoff/stl-to-voxel>`_ Python library by Christian Pederkoff.
How to use the package
======================
The main script is ``stltovoxel.py`` which should be run at the command line and takes three arguments:
* ``stlfilename`` is name of STL file to convert including the path.
* ``-matindex`` is an integer which represents the index of the material to be used from the materials file which will accompany the generated geometry file (HDF5 format).
* ``-dxdyz`` is the spatial discretisation of the generated voxelised mesh. It should be given as three floating point numbers.
The physical dimensions of the voxelised object will depend on the size of the object in the original STL file and the spatial discretisation chosen.
Example
-------
To create a voxelised mesh (HDF5 geometry file) from the ubiquitous `Stanford bunny <https://en.wikipedia.org/wiki/Stanford_bunny>`_ STL file, using a spatial discretisation of 1mm and selecting material index 2 from a materials file:
.. code-block:: none
python -m user_libs.STLtoVoxel.stltovoxel user_libs/STLtoVoxel/examples/stl/Stanford_Bunny.stl -matindex 2 -dxdydz 0.001 0.001 0.001
Since the number of voxels are 108 x 88 108 and the spatial discretisation chosen is 1mm, the physical dimensions of the Stanford bunny when imported into gprMax will be 0.108 x 0.088 x 0.108mm.
The following is an example of a ``materials.txt`` file that can be used with the generated geometry file (HDF5 format) when importing into gprMax. Since ``-matindex`` is set to 2 the material with name ``hdpe``, i.e. a plastic, will be used.
.. literalinclude:: examples/materials.txt
:language: none
:linenos:
The following Python script (using the gprMax API) can be used to import the generated geometry file ``Stanford_Bunny.h5`` and materials file ``materials.txt`` into a gprMax model:
.. literalinclude:: examples/bunny.py
:language: python
:linenos:
.. figure:: ../../images_shared/stanford_bunny_stl.png
:width: 600 px
Image of the Stanford bunny STL file
.. figure:: ../../images_shared/stanford_bunny.png
:width: 600 px
FDTD geometry mesh showing the Stanford bunny

查看文件

查看文件

@@ -0,0 +1,33 @@
import numpy as np
from stl import mesh
from . import slice
def convert_meshes(meshes, discretization, parallel=True):
scale, shift, shape = slice.calculate_scale_shift(meshes, discretization)
vol = np.zeros(shape[::-1], dtype=np.int32)
for mesh_ind, org_mesh in enumerate(meshes):
slice.scale_and_shift_mesh(org_mesh, scale, shift)
cur_vol = slice.mesh_to_plane(org_mesh, shape, parallel)
vol[cur_vol] = mesh_ind + 1
return vol, scale, shift
def convert_file(input_file_path, discretization, pad=1, parallel=False):
return convert_files([input_file_path], discretization, pad=pad, parallel=parallel)
def convert_files(input_file_paths, discretization, colors=[(0, 0, 0)], pad=1, parallel=False):
meshes = []
for input_file_path in input_file_paths:
mesh_obj = mesh.Mesh.from_file(input_file_path)
org_mesh = np.hstack((mesh_obj.v0[:, np.newaxis], mesh_obj.v1[:, np.newaxis], mesh_obj.v2[:, np.newaxis]))
meshes.append(org_mesh)
vol, scale, shift = convert_meshes(meshes, discretization, parallel)
vol = np.transpose(vol)
return vol

查看文件

@@ -0,0 +1,37 @@
from pathlib import Path
import gprMax
# File path for output
fn = Path(__file__)
# Discretisation
dl = 0.001
# Domain
x = 0.148
y = 0.128
z = 0.148
scene = gprMax.Scene()
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)
scene.add(title)
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')
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)
gprMax.run(scenes=[scene], geometry_only=True, outputfile=fn)

二进制文件未显示。

查看文件

@@ -0,0 +1,4 @@
#material: 3 0 1 0 sand
#material: 5 0.001 1 0 concrete
#material: 2.35 0 1 0 hdpe
#material: 3 0 1 0 pcb

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

查看文件

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Christian Pederkoff
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

查看文件

@@ -0,0 +1,68 @@
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):
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':
assert line_ind not in current_line_indices
current_line_indices.add(line_ind)
elif status == 'end':
assert line_ind in current_line_indices
current_line_indices.remove(line_ind)
def slope_intercept(p1, p2):
x1, y1 = p1[:2]
x2, y2 = p2[:2]
slope = (y2 - y1) / (x2 - x1)
intercept = y1 - slope * x1
return slope, intercept
def generate_y(p1, p2, x):
slope, intercept = slope_intercept(p1, p2)
y = slope * x + intercept
return y
def paint_y_axis(lines, pixels, x):
is_black = False
target_ys = list(map(lambda line: int(generate_y(line[0], line[1], x)), lines))
target_ys.sort()
if len(target_ys) % 2:
distances = []
for i in range(len(target_ys) - 1):
distances.append(target_ys[i+1] - target_ys[i])
# https://stackoverflow.com/a/17952763
min_idx = -min((x, -i) for i, x in enumerate(distances))[1]
del target_ys[min_idx]
yi = 0
for target_y in target_ys:
if is_black:
# Bulk assign all pixels between yi -> target_y
pixels[yi:target_y, x] = True
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
def generate_line_events(line_list):
events = []
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))
return sorted(events, key=lambda tup: tup[0])

查看文件

@@ -0,0 +1,148 @@
import multiprocessing as mp
import sys
import numpy as np
from gprMax.utilities.utilities import get_terminal_width
from tqdm import tqdm
from . import perimeter
def mesh_to_plane(mesh, bounding_box, parallel):
if parallel:
pool = mp.Pool(mp.cpu_count())
result_ids = []
vol = np.zeros(bounding_box[::-1], dtype=bool)
current_mesh_indices = set()
z = 0
with tqdm(total=bounding_box[2], desc="Processing Layers", ncols=get_terminal_width() - 1, file=sys.stdout) as pbar:
for event_z, status, tri_ind in generate_tri_events(mesh):
while event_z - z >= 0:
mesh_subset = [mesh[ind] for ind in current_mesh_indices]
if parallel:
pass
else:
pbar.update(1)
_, pixels = paint_z_plane(mesh_subset, z, bounding_box[1::-1])
vol[z]=pixels
z += 1
if status == 'start':
assert tri_ind not in current_mesh_indices
current_mesh_indices.add(tri_ind)
elif status == 'end':
assert tri_ind in current_mesh_indices
current_mesh_indices.remove(tri_ind)
if parallel:
results = [r.get() for r in result_ids]
for z, pixels in results:
vol[z] = pixels
pool.close()
pool.join()
return vol
def paint_z_plane(mesh, height, plane_shape):
pixels = np.zeros(plane_shape, dtype=bool)
lines = []
for triangle in mesh:
triangle_to_intersecting_lines(triangle, height, pixels, lines)
perimeter.lines_to_voxels(lines, pixels)
return height, pixels
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)
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))
if len(same) == 3:
for i in range(0, len(same) - 1):
for j in range(i + 1, len(same)):
lines.append((same[i], same[j]))
elif len(same) == 2:
lines.append((same[0], same[1]))
elif len(same) == 1:
if above and below:
side1 = where_line_crosses_z(above[0], below[0], height)
lines.append((side1, same[0]))
else:
x = int(same[0][0])
y = int(same[0][1])
pixels[y][x] = True
else:
cross_lines = []
for a in above:
for b in below:
cross_lines.append((b, a))
side1 = where_line_crosses_z(cross_lines[0][0], cross_lines[0][1], height)
side2 = where_line_crosses_z(cross_lines[1][0], cross_lines[1][1], height)
lines.append((side1, side2))
def where_line_crosses_z(p1, p2, z):
if (p1[2] > p2[2]):
p1, p2 = p2, p1
# now p1 is below p2 in z
if p2[2] == p1[2]:
distance = 0
else:
distance = (z - p1[2]) / (p2[2] - p1[2])
return linear_interpolation(p1, p2, distance)
def calculate_scale_shift(meshes, discretization):
mesh_min = meshes[0].min(axis=(0, 1))
mesh_max = meshes[0].max(axis=(0, 1))
for mesh in meshes[1:]:
mesh_min = np.minimum(mesh_min, mesh.min(axis=(0, 1)))
mesh_max = np.maximum(mesh_max, mesh.max(axis=(0, 1)))
amplitude = mesh_max - mesh_min
#Standard Unit of STL is mm
vx=discretization[0]*1000
vy=discretization[1]*1000
vz=discretization[2]*1000
bx=int(amplitude[0]/vx)
by=int(amplitude[1]/vy)
bz=int(amplitude[2]/vz)
bounding_box = [bx+1, by+1, bz+1]
return max(1/vx,1/vy,1/vz), mesh_min, bounding_box
def scale_and_shift_mesh(mesh, scale, shift):
for i, dim_shift in enumerate(shift):
mesh[..., i] = (mesh[..., i] - dim_shift) * scale
def generate_tri_events(mesh):
# Create data structure for plane sweep
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))
return sorted(events, key=lambda tup: tup[0])

查看文件

@@ -0,0 +1,39 @@
import argparse
import logging
from pathlib import Path
import h5py
from .convert import convert_file
logger = logging.getLogger(__name__)
logging.basicConfig(format='%(message)s', level=logging.INFO)
if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(description='Allows the user to convert a STL files to voxelized mesh.', usage='cd gprMax; python -m user_libs.STLtoVoxel.stltovoxel stlfilename matindex dx_dy_dz')
parser.add_argument('stlfilename', help='name of STL file to convert including path')
parser.add_argument('-matindex', type=int, required=True,
help='index of material to extract from STL file')
parser.add_argument('-dxdydz', nargs='+', type=float, required=True,
help='discretisation to use in voxelisation process')
args = parser.parse_args()
filename_stl = Path(args.stlfilename)
dxdydz = tuple(args.dxdydz)
logger.info(f'\nConverting STL file: {filename_stl.name}')
model_array = convert_file(filename_stl, dxdydz)
model_array[model_array==0] = -1
model_array[model_array==1] = args.matindex
logger.info(f'Number of voxels: {model_array.shape[0]} x {model_array.shape[1]} x {model_array.shape[2]}')
logger.info(f'Spatial discretisation: {dxdydz[0]} x {dxdydz[1]} x {dxdydz[2]}m')
# Write HDF5 file for gprMax using voxels
filename_hdf5 = filename_stl.with_suffix('.h5')
with h5py.File(filename_hdf5, 'w') as f:
f.create_dataset('data', data=model_array)
f.attrs['dx_dy_dz'] = (dxdydz[0], dxdydz[1], dxdydz[2])
logger.info(f'Written geometry object file: {filename_hdf5.name}')

查看文件

@@ -0,0 +1,31 @@
#!/bin/sh
#####################################################################################
### Change to current working directory:
#$ -cwd
### Specify runtime (hh:mm:ss):
#$ -l h_rt=01:00:00
### Email options:
#$ -m ea -M joe.bloggs@email.com
### Parallel environment ($NSLOTS):
#$ -pe sharedmem 16
### Job script name:
#$ -N gprmax_omp.sh
#####################################################################################
### Initialise environment module
. /etc/profile.d/modules.sh
### Load and activate Anaconda environment for gprMax, i.e. Python 3 and required packages
module load anaconda
source activate gprMax
### Set number of OpenMP threads for each gprMax model
export OMP_NUM_THREADS=16
### Run gprMax with input file
cd $HOME/gprMax
python -m gprMax mymodel.in -n 10

查看文件

@@ -0,0 +1,31 @@
#!/bin/sh
#####################################################################################
### Change to current working directory:
#$ -cwd
### Specify runtime (hh:mm:ss):
#$ -l h_rt=01:00:00
### Parallel environment ($NSLOTS):
#$ -pe sharedmem 16
### Job array and task IDs
#$ -t 1-11
### Job script name:
#$ -N gprmax_omp_jobarray.sh
#####################################################################################
### Initialise environment module
. /etc/profile.d/modules.sh
### Load and activate Anaconda environment for gprMax, i.e. Python 3 and required packages
module load anaconda
source activate gprMax
### Set number of OpenMP threads for each gprMax model
export OMP_NUM_THREADS=16
### Run gprMax with input file
cd $HOME/gprMax
python -m gprMax mymodel.in -n 10 -task $SGE_TASK_ID

查看文件

@@ -0,0 +1,37 @@
#!/bin/sh
#####################################################################################
### Change to current working directory:
#$ -cwd
### Specify runtime (hh:mm:ss):
#$ -l h_rt=01:00:00
### Email options:
#$ -m ea -M joe.bloggs@email.com
### Resource reservation:
#$ -R y
### Parallel environment ($NSLOTS):
#$ -pe mpi 176
### Job script name:
#$ -N gprmax_omp_mpi_no_spawn.sh
#####################################################################################
### Initialise environment module
. /etc/profile.d/modules.sh
### Load and activate Anaconda environment for gprMax, i.e. Python 3 and required packages
module load anaconda
source activate gprMax
### Load OpenMPI
module load openmpi
### Set number of OpenMP threads per MPI task (each gprMax model)
export OMP_NUM_THREADS=16
### Run gprMax with input file
cd $HOME/gprMax
mpirun -n 11 python -m gprMax mymodel.in -n 10 -mpi

查看文件

@@ -0,0 +1 @@
These MATLAB scripts are designed as a base to help you get started with plotting data (A-scans and B-scans) from simulations. They do not feature extensive error checking.

查看文件

@@ -0,0 +1,669 @@
% outputfile_converter.m - converts gprMax merged output HDF5 file to RD3,
% DZT, DT1 and IPRB files.
%
% Author: Dimitrios Angelis
% Copyright: 2017-2018
% Last modified: 18/07/2018
clear, clc, close all;
% Select file =============================================================
[infile, path] = uigetfile('*.out', 'Select gprMax output file', ...
'Multiselect', 'Off');
if isequal(infile, 0)
infile = [];
path = [];
HDR = [];
data = [];
return
end
% File name, path name and file extension =================================
HDR.fname = strrep(lower(infile), '.out', '');
HDR.pname = path;
HDR.fext = 'out';
% Read data from HDF5 file ================================================
infile = [HDR.pname infile];
dataex = h5read(infile, '/rxs/rx1/Ex');
dataey = h5read(infile, '/rxs/rx1/Ey');
dataez = h5read(infile, '/rxs/rx1/Ez');
% Field check =============================================================
if dataey == 0 & dataez == 0
data = dataex';
elseif dataex == 0 & dataez == 0
data = dataey';
elseif dataex == 0 & dataey == 0
data = dataez';
else
maxex = max(max(dataex));
maxey = max(max(dataey));
maxez = max(max(dataez));
if maxex > maxey & maxex > maxez
data = dataex';
elseif maxey > maxex & maxey > maxez
data = dataey';
elseif maxez > maxex & maxez > maxey
data = dataez';
end
end
% Sigle to double precision ===============================================
data = double(data);
% The HDF5 file does not contain information about the centre frequency of
% the waveform, the Tx-Rx separation distance and the trace step. The user
% needs to provide this information.
while 1
prompt = {'Waveform Centre Frequency (MHz)', ...
'Source-Receiver Distance (m)', 'Trace Step (m)'};
dlg_title = 'Additional Information';
answer = inputdlg(prompt, dlg_title, [1 50]);
answer = str2double(answer);
if isempty(answer)
HDR = [];
data = [];
return
elseif isnan(answer(1)) || isnan(answer(2)) || isnan(answer(3))...
|| answer(1) <= 0 || answer(2) < 0 || answer(3) <=0
continue
else
break
end
end
% Create header with basic information ====================================
HDR.centre_freq = answer(1); % Centre frequency (MHz)
HDR.ant_sep = answer(2); % Antenna seperation / Tx-Rx distance (m)
HDR.trac_int = answer(3); % Trace interval / step (m)
HDR.samp_int = h5readatt(infile, '/', 'dt') * 10^9; % Sampling interval / step (ns)
HDR.samp_freq = (1 / HDR.samp_int) * 10^3; % Sampling frequency (MHz)
[HDR.num_samp, HDR.num_trac] = size(data); % Number of samples & traces
HDR.time_window = HDR.num_samp * HDR.samp_int; % Time window (ns)
HDR.antenna = ['gprMax ', num2str(HDR.centre_freq), 'MHz']; % Antenna name
%**************************************************************************
%******************************** Optional ********************************
% Resample to 1024 samples ================================================
% I usually perform this step for either 512 or 1024 samples (line 100)
% because many GPR processing software cannot load files with more samples.
tx1 = 1 : HDR.num_samp;
fs1 = 1024 / HDR.num_samp; % <------- 1024 samples
data = resample(data, tx1, fs1, 'spline');
[HDR.num_samp, ~] = size(data); % New number of samples after resampling
HDR.samp_int = HDR.time_window / HDR.num_samp; % New sampling interval (ns) after resampling
HDR.samp_freq = (1 / HDR.samp_int) * 10^3; % New sampling frequency (MHz) after resampling
%**************************************************************************
%**************************************************************************
%**************************************************************************
%******************************** Optional ********************************
% Data scale ==============================================================
data = data * 32767.5 ./ max(max(abs(data))); %signal * ((1 - 1 / 2^bitrate) * 32768) / max(signal)
data(data >= 32767) = 32767;
data(data <= -32768) = -32768;
data = round(data);
%**************************************************************************
%**************************************************************************
% Plots ===================================================================
pmin = min(data(:)); %Minimun plot scale
pmax = max(data(:)); %Maximum plot scale
x = 0 : HDR.trac_int : (HDR.num_trac - 1) * HDR.trac_int; %Distance of each trace (m)
t = HDR.samp_int : HDR.samp_int : HDR.num_samp * HDR.samp_int; %Time of each sample (ns)
% Bscan
f1 = figure('Name', 'Bscan', ...
'NumberTitle', 'off', ...
'Menubar', 'None', ...
'Toolbar', 'Figure');
clims = [pmin pmax];
colormap (bone(256)); %Black(negative) to white(positive)
im1 = imagesc(x, t, data, clims);
set(im1, 'cdatamapping', 'scaled');
title(HDR.fname);
xlabel('Distance (m)');
ylabel('Time (ns)');
ax1 = gca;
ax1.XAxisLocation = 'Top';
ax1.FontSize = 12;
box off;
movegui(f1, 'northeast');
% Frequency Spectrum
m = 2.^nextpow2(HDR.num_samp);
amp = fft(data, m);
amp = (abs(amp(1 : m / 2, :)) / m) * 2;
amp = mean(amp.');
freq = HDR.samp_freq .* (0 : (m / 2) - 1) / m;
f2 = figure('Name', 'Frequency Spectrum', ...
'NumberTitle', 'off', ...
'Menubar', 'None', ...
'Toolbar', 'Figure');
area(freq, amp, 'FaceColor', 'black');
title(HDR.fname);
xlabel('Frequency (MHz)');
ylabel('Amplitude');
ax2 = gca;
ax2.FontSize = 12;
box off;
movegui(f2, 'southeast');
% Export option: RD3/RAD or DZT or DT1/HD or IPRB/IPRH ====================
while 1
prompt = {'1 = RD3, 2 = DZT, 3 = DT1, 4 = IPRB'};
dlg_title = 'Choose GPR Format';
answer = inputdlg(prompt, dlg_title, [1 45]);
answer = str2double(answer);
if isempty(answer)
return
elseif ~isnumeric(answer) || answer ~= 1 && answer ~= 2 ...
&& answer ~= 3 && answer ~= 4
continue
else
gpr_format = answer;
break
end
end
wb = waitbar(0, 'Exporting...', 'Name', 'Exporting File');
% RAD / RD3, Mala GeoScience ==============================================
% Rad is the header file. In this file is all the important information
% such as the number of samples, traces, measurement intervals can be
% found.
% Rd3 is the data file. This file contains only the data (amplitude values)
% in a binary form.
if gpr_format == 1
% Header structure
HDR.fname = HDR.fname; % File name
HDR.num_samp = HDR.num_samp; % Number of samples
HDR.samp_freq = HDR.samp_freq; % Sampling frequency (MHz)
HDR.frequency_steps = 1; % Frequency steps
HDR.signal_pos = 0; % Signal position
HDR.raw_signal_pos = 0; % Raw signal position
HDR.distance_flag = 1; % Distance flag: 0 time interval, 1 distance interval
HDR.time_flag = 0; % Time flag : 0 distance interval, 1 time interval
HDR.program_flag = 0; % Program flag
HDR.external_flag = 0; % External flag
HDR.trac_int_sec = 0; % Trace interval in seconds(only if Time flag = 1)
HDR.trac_int = HDR.trac_int; % Trace interval in meters (only if Distance flag = 1)
HDR.operator = 'Unknown'; % Operator
HDR.customer = 'Unknown'; % Customer
HDR.site = 'gprMax'; % Site
HDR.antenna = HDR.antenna; % Antenna name
HDR.antenna_orientation = 'NOT VALID FIELD'; % Antenna orientation
HDR.ant_sep = HDR.ant_sep; % Antenna seperation / Tx-Rx distance (m)
HDR.comment = '----'; % Comment
HDR.time_window = HDR.time_window; % Time window (ns)
HDR.stacks = 1; % Stacks
HDR.stack_exponent = 0; % Stack exponent
HDR.stacking_time = 0; % Stacking Time
HDR.num_trac = HDR.num_trac; % Number of traces
HDR.stop_pos = HDR.num_trac * HDR.trac_int; % Stop position (m)
HDR.system_calibration = 0;
HDR.start_pos = 0; % Start position (m)
HDR.short_flag = 1;
HDR.intermediate_flag = 0;
HDR.long_flag = 0;
HDR.preprocessing = 0;
HDR.high = 0;
HDR.low = 0;
HDR.fixed_increment = 0;
HDR.fixed_moves_up = 0;
HDR.fixed_moves_down = 1;
HDR.fixed_position = 0;
HDR.wheel_calibration = 0;
HDR.positive_direction = 1;
% RAD file
fid = fopen([HDR.fname '.rad'], 'w');
fprintf(fid, 'SAMPLES:%i\r\n', HDR.num_samp);
fprintf(fid, 'FREQUENCY:%0.6f\r\n', HDR.samp_freq);
fprintf(fid, 'FREQUENCY STEPS:%i\r\n', HDR.frequency_steps);
fprintf(fid, 'SIGNAL POSITION:%0.6f\r\n', HDR.signal_pos);
fprintf(fid, 'RAW SIGNAL POSITION:%i\r\n', HDR.raw_signal_pos);
fprintf(fid, 'DISTANCE FLAG:%i\r\n', HDR.distance_flag);
fprintf(fid, 'TIME FLAG:%i\r\n', HDR.time_flag);
fprintf(fid, 'PROGRAM FLAG:%i\r\n', HDR.program_flag);
fprintf(fid, 'EXTERNAL FLAG:%i\r\n', HDR.external_flag);
fprintf(fid, 'TIME INTERVAL:%0.6f\r\n', HDR.trac_int_sec);
fprintf(fid, 'DISTANCE INTERVAL:%0.6f\r\n', HDR.trac_int);
fprintf(fid, 'OPERATOR:%s\r\n', HDR.operator);
fprintf(fid, 'CUSTOMER:%s\r\n', HDR.customer);
fprintf(fid, 'SITE:%s\r\n', HDR.site);
fprintf(fid, 'ANTENNAS:%s\r\n', HDR.antenna);
fprintf(fid, 'ANTENNA ORIENTATION:%s\r\n', HDR.antenna_orientation);
fprintf(fid, 'ANTENNA SEPARATION:%0.6f\r\n', HDR.ant_sep);
fprintf(fid, 'COMMENT:%s\r\n', HDR.comment);
fprintf(fid, 'TIMEWINDOW:%0.6f\r\n', HDR.time_window);
fprintf(fid, 'STACKS:%i\r\n', HDR.stacks);
fprintf(fid, 'STACK EXPONENT:%i\r\n', HDR.stack_exponent);
fprintf(fid, 'STACKING TIME:%0.6f\r\n', HDR.stacking_time);
fprintf(fid, 'LAST TRACE:%i\r\n', HDR.num_trac);
fprintf(fid, 'STOP POSITION:%0.6f\r\n', HDR.stop_pos);
fprintf(fid, 'SYSTEM CALIBRATION:%0.6f\r\n', HDR.system_calibration);
fprintf(fid, 'START POSITION:%0.6f\r\n', HDR.start_pos);
fprintf(fid, 'SHORT FLAG:%i\r\n', HDR.short_flag);
fprintf(fid, 'INTERMEDIATE FLAG:%i\r\n', HDR.intermediate_flag);
fprintf(fid, 'LONG FLAG:%i\r\n', HDR.long_flag);
fprintf(fid, 'PREPROCESSING:%i\r\n', HDR.preprocessing);
fprintf(fid, 'HIGH:%i\r\n', HDR.high);
fprintf(fid, 'LOW:%i\r\n', HDR.low);
fprintf(fid, 'FIXED INCREMENT:%0.6f\r\n', HDR.fixed_increment);
fprintf(fid, 'FIXED MOVES UP:%i\r\n', HDR.fixed_moves_up);
fprintf(fid, 'FIXED MOVES DOWN:%i\r\n', 1);
fprintf(fid, 'FIXED POSITION:%0.6f\r\n', HDR.fixed_moves_down);
fprintf(fid, 'WHEEL CALIBRATION:%0.6f\r\n', HDR.wheel_calibration);
fprintf(fid, 'POSITIVE DIRECTION:%i\r\n', HDR.positive_direction);
fclose(fid);
% RD3 file
fid = fopen([HDR.fname '.rd3'], 'w');
fwrite(fid, data, 'short');
fclose(fid);
% DZT, Geophysical Survey Systems Inc. (GSSI) =============================
% Dzt is a binary file that consists of the file header with all the
% important information (number of samples, traces, channels, etc.)
% followed by the data section.
% All the information is contained in this file except the TxRx distance
% (antenna separation). There is a possibility that the official GSSI
% software has stored this information and by using the antenna name
% presents the correct one. All the other software does not detect the TxRx
% distance.
elseif gpr_format == 2
% Header structure
HDR.fname = HDR.fname; % File name
HDR.tag = 255; % Header = 255
HDR.data_offset = 1024; % Offset to data from the beginning of file
HDR.num_samp = HDR.num_samp; % Number of samples
HDR.data_format = 16; % Bits per data word (8, 16, 32)
HDR.binary_offset = 32768; % Binary offset, 8 bit = 128, 16 bit = 32768
HDR.scans_per_second = 0; % Scans per second
HDR.scans_per_meter = 1 / HDR.trac_int; % Scans per meter
HDR.meters_per_mark = 0; % Meters per mark
HDR.zero_time_adjustment = 0; % Time zero adjustment (ns)
HDR.time_window = HDR.time_window; % Time window (with no corrections i.e zero time)
HDR.scans_per_pass = 0; % Scan per pass for 2D files
HDR.createdate.sec = 0 / 2; % Structure, date created
HDR.createdate.min = 0;
HDR.createdate.hour = 0;
HDR.createdate.day = 0;
HDR.createdate.month = 0;
HDR.createdate.year = 0 - 1980;
date_time = clock;
HDR.modifydate.sec = date_time(6) / 2; % Structure, date modified
HDR.modifydate.min = date_time(5);
HDR.modifydate.hour = date_time(4);
HDR.modifydate.day = date_time(3);
HDR.modifydate.month = date_time(2);
HDR.modifydate.year = date_time(1) - 1980;
HDR.offset_to_range_gain = 0; % Offset to range gain
HDR.size_of_range_gain = 0; % Size of range gain
HDR.offset_to_text = 0; % Offset to text
HDR.size_of_text = 0; % Size of text
HDR.offset_to_proc_his = 0; % Offset to processing history
HDR.size_of_proc_his = 0; % Size of processing history
HDR.num_channels = 1; % Number of channels
HDR.dielectric_constant = 8; % Dielectric constant (8 is a random number)
HDR.top_position = 0; % Top position
c = 299792458;
v = (c / sqrt(HDR.dielectric_constant)) * 10^-9;
HDR.range_depth = v * (HDR.time_window / 2); % Range depth (m)
HDR.reserved = zeros(31, 1); % Reserved
HDR.data_type = 0; % Data type
if length(HDR.antenna) == 14 % Antenna name
HDR.antenna = HDR.antenna;
elseif length(HDR.antenna) < 14
if verLessThan('matlab', '9.1')
HDR.antenna = [HDR.antenna repmat(' ', ...
1, 14 - length(HDR.antenna))];
else
HDR.antenna = pad(HDR.antenna, 14, 'right');
end
elseif length(HDR.antenna) > 14
HDR.antenna = HDR.antenna(1 : 14);
end
HDR.channel_mask = 0; % Channel mask
if length(HDR.fname) == 12 % Raw file name (File name during survey)
HDR.raw_file_name = HDR.fname;
elseif length(HDR.fname) < 12
if verLessThan('matlab', '9.1')
HDR.raw_file_name = [HDR.raw_file_name repmat(' ', ...
1, 12 - length(HDR.raw_file_name))];
else
HDR.raw_file_name = pad(HDR.fname, 12, 'right');
end
elseif length(HDR.fname) > 12
HDR.raw_file_name = HDR.fname(1 : 12);
end
HDR.checksum = 0; % Checksum
HDR.num_gain_points = 0; % Number of gain points
HDR.range_gain_db = []; % Range gain in db
HDR.variable = zeros(896, 1);
% DZT file
fid = fopen([HDR.fname '.dzt'], 'w');
fwrite(fid, HDR.tag, 'ushort');
fwrite(fid, HDR.data_offset, 'ushort');
fwrite(fid, HDR.num_samp, 'ushort');
fwrite(fid, HDR.data_format, 'ushort');
fwrite(fid, HDR.binary_offset, 'ushort');
fwrite(fid, HDR.scans_per_second, 'float');
fwrite(fid, HDR.scans_per_meter, 'float');
fwrite(fid, HDR.meters_per_mark, 'float');
fwrite(fid, HDR.zero_time_adjustment, 'float');
fwrite(fid, HDR.time_window, 'float');
fwrite(fid, HDR.scans_per_pass, 'ushort');
fwrite(fid, HDR.createdate.sec, 'ubit5');
fwrite(fid, HDR.createdate.min, 'ubit6');
fwrite(fid, HDR.createdate.hour, 'ubit5');
fwrite(fid, HDR.createdate.day, 'ubit5');
fwrite(fid, HDR.createdate.month, 'ubit4');
fwrite(fid, HDR.createdate.year, 'ubit7');
fwrite(fid, HDR.modifydate.sec, 'ubit5');
fwrite(fid, HDR.modifydate.min, 'ubit6');
fwrite(fid, HDR.modifydate.hour, 'ubit5');
fwrite(fid, HDR.modifydate.day, 'ubit5');
fwrite(fid, HDR.modifydate.month, 'ubit4');
fwrite(fid, HDR.modifydate.year, 'ubit7');
fwrite(fid, HDR.offset_to_range_gain, 'ushort');
fwrite(fid, HDR.size_of_range_gain, 'ushort');
fwrite(fid, HDR.offset_to_text, 'ushort');
fwrite(fid, HDR.size_of_text, 'ushort');
fwrite(fid, HDR.offset_to_proc_his, 'ushort');
fwrite(fid, HDR.size_of_proc_his, 'ushort');
fwrite(fid, HDR.num_channels, 'ushort');
fwrite(fid, HDR.dielectric_constant, 'float');
fwrite(fid, HDR.top_position, 'float');
fwrite(fid, HDR.range_depth, 'float');
fwrite(fid, HDR.reserved, 'char');
fwrite(fid, HDR.data_type, 'char');
fwrite(fid, HDR.antenna, 'char');
fwrite(fid, HDR.channel_mask, 'ushort');
fwrite(fid, HDR.raw_file_name, 'char');
fwrite(fid, HDR.checksum, 'ushort');
fwrite(fid, HDR.num_gain_points, 'ushort');
fwrite(fid, HDR.range_gain_db, 'float');
fwrite(fid, HDR.variable, 'char');
fseek(fid, HDR.data_offset, 'bof');
data = data + 2^15;
fwrite(fid, data, 'ushort');
fclose(fid);
% HD / DT1, Sensors & Software Inc. =======================================
% Hd is the header file. In this file all the important information such as
% the number of samples, traces, stacks, etc. can be found.
% Dt1 is the data file written in binary form. This file contains as many
% records as there are traces. Each record consists of a header and a data
% section. This means that also in this file there are stored information
% such as the number of samples, traces, etc.
elseif gpr_format == 3
%Header structure of HD
HDR.fname = HDR.fname; % File name
HDR.file_tag = 1234; % File tag = 1234
HDR.system = 'gprMax'; % The system the data collected with
date_time = clock;
HDR.date = ([num2str(date_time(1)), '-' ...
num2str(date_time(2)), '-' ...
num2str(date_time(3))]);% Date
HDR.num_trac = HDR.num_trac; % Number of traces
HDR.num_samp = HDR.num_samp; % Number of samples
HDR.time_zero_point = 0; % Time zero point
HDR.time_window = HDR.time_window; % Total time window (ns)
HDR.start_position = 0; % Start position (m)
HDR.final_position = (HDR.num_trac - 1) * HDR.trac_int; % Stop position (m)
HDR.trac_int = HDR.trac_int; % Trace interval (m)
HDR.pos_units = 'm'; % Position units
HDR.nominal_freq = HDR.centre_freq; % Nominal freq. / Centre freq. (MHz)
HDR.ant_sep = HDR.ant_sep; % Antenna seperation / Tx-Rx distance (m)
HDR.pulser_voltage = 0; % Pulser voltage (V)
HDR.stacks = 1; % Number of stacks
HDR.survey_mode = 'Reflection'; % Survey mode
HDR.odometer = 0; % Odometer Cal (t/m)
HDR.stacking_type = 'F1'; % Stacking type
HDR.dvl_serial = '0000-0000-0000'; % DVL serial
HDR.console_serial = '000000000000'; % Console serial
HDR.tx_serial = '0000-0000-0000'; % Transmitter serial
HDR.rx_serial = '0000-0000-0000'; % Receiver Serial
% Header structure of DT1
HDR.num_each_trac = 1 : 1 : HDR.num_trac; % Number of each trace 1, 2, 3, ... num_trac
HDR.position = 0 : HDR.trac_int : ...
(HDR.num_trac - 1) * HDR.trac_int; % Position of each trace (m)
HDR.num_samp_each_trac = zeros(1, HDR.num_trac) + HDR.num_samp; % Number of samples of each trace
HDR.elevation = zeros(1, HDR.num_trac); % Elevation / topography of each trace
HDR.not_used1 = zeros(1, HDR.num_trac); % Not used
HDR.bytes = zeros(1, HDR.num_trac) + 2; % Always 2 for Rev 3 firmware
HDR.time_window_each_trac = zeros(1, HDR.num_trac) + HDR.time_window; % Time window of each trace (ns)
HDR.stacks_each_trac = ones(1, HDR.num_trac); % Number of stacks each trace
HDR.not_used2 = zeros(1, HDR.num_trac); % Not used
HDR.rsv_gps_x = zeros(1, HDR.num_trac); % Reserved for GPS X position (double*8 number)
HDR.rsv_gps_y = zeros(1, HDR.num_trac); % Reserved for GPS Y position (double*8 number)
HDR.rsv_gps_z = zeros(1, HDR.num_trac); % Reserved for GPS Z position (double*8 number)
HDR.rsv_rx_x = zeros(1, HDR.num_trac); % Reserved for receiver x position
HDR.rsv_rx_y = zeros(1, HDR.num_trac); % Reserved for receiver y position
HDR.rsv_rx_z = zeros(1, HDR.num_trac); % Reserved for receiver z position
HDR.rsv_tx_x = zeros(1, HDR.num_trac); % Reserved for transmitter x position
HDR.rsv_tx_y = zeros(1, HDR.num_trac); % Reserved for transmitter y position
HDR.rsv_tx_z = zeros(1, HDR.num_trac); % Reserved for transmitter z position
HDR.time_zero = zeros(1, HDR.num_trac); % Time zero adjustment where: point(x) = point(x + adjustment)
HDR.zero_flag = zeros(1, HDR.num_trac); % 0 = data ok, 1 = zero data
HDR.num_channels = zeros(1, HDR.num_trac); % Number of channels
HDR.time = zeros(1, HDR.num_trac); % Time of day data collected in seconds past midnight
HDR.comment_flag = zeros(1, HDR.num_trac); % Comment flag
HDR.comment = zeros(1, 24); % Comment
% HD file
fid = fopen([HDR.fname '.hd'], 'w');
fprintf(fid, '%i\r\n', HDR.file_tag);
fprintf(fid, 'Data Collected with %s\r\n', HDR.system);
fprintf(fid, '%s\r\n', HDR.date);
fprintf(fid, 'NUMBER OF TRACES = %i\r\n', HDR.num_trac);
fprintf(fid, 'NUMBER OF PTS/TRC = %i\r\n', HDR.num_samp);
fprintf(fid, 'TIMEZERO AT POINT = %i\r\n', HDR.time_zero_point);
fprintf(fid, 'TOTAL TIME WINDOW = %0.6f\r\n', HDR.time_window);
fprintf(fid, 'STARTING POSITION = %0.6f\r\n', HDR.start_position);
fprintf(fid, 'FINAL POSITION = %0.6f\r\n', HDR.final_position);
fprintf(fid, 'STEP SIZE USED = %0.6f\r\n', HDR.trac_int);
fprintf(fid, 'POSITION UNITS = %s\r\n', HDR.pos_units);
fprintf(fid, 'NOMINAL FREQUENCY = %0.6f\r\n', HDR.nominal_freq);
fprintf(fid, 'ANTENNA SEPARATION = %0.6f\r\n', HDR.ant_sep);
fprintf(fid, 'PULSER VOLTAGE (V) = %0.6f\r\n', HDR.pulser_voltage);
fprintf(fid, 'NUMBER OF STACKS = %i\r\n', HDR.stacks);
fprintf(fid, 'SURVEY MODE = %s\r\n', HDR.survey_mode);
fprintf(fid, 'ODOMETER CAL (t/m) = %0.6f\r\n', HDR.odometer);
fprintf(fid, 'STACKING TYPE = %s\r\n', HDR.stacking_type);
fprintf(fid, 'DVL Serial# = %s\r\n', HDR.dvl_serial);
fprintf(fid, 'Console Serial# = %s\r\n', HDR.console_serial);
fprintf(fid, 'Transmitter Serial#= %s\r\n', HDR.tx_serial);
fprintf(fid, 'Receiver Serial# = %s\r\n', HDR.rx_serial);
fclose(fid);
% DT1 file
fid = fopen([HDR.fname '.dt1'], 'w');
for i = 1 : HDR.num_trac
fwrite(fid, HDR.num_each_trac(i), 'float');
fwrite(fid, HDR.position(i), 'float');
fwrite(fid, HDR.num_samp_each_trac(i), 'float');
fwrite(fid, HDR.elevation(i), 'float');
fwrite(fid, HDR.not_used1(i), 'float');
fwrite(fid, HDR.bytes(i), 'float');
fwrite(fid, HDR.time_window_each_trac(i), 'float');
fwrite(fid, HDR.stacks_each_trac(i), 'float');
fwrite(fid, HDR.not_used2(i), 'float');
fwrite(fid, HDR.rsv_gps_x(i), 'double');
fwrite(fid, HDR.rsv_gps_y(i), 'double');
fwrite(fid, HDR.rsv_gps_z(i), 'double');
fwrite(fid, HDR.rsv_rx_x(i), 'float');
fwrite(fid, HDR.rsv_rx_y(i), 'float');
fwrite(fid, HDR.rsv_rx_z(i), 'float');
fwrite(fid, HDR.rsv_tx_x(i), 'float');
fwrite(fid, HDR.rsv_tx_y(i), 'float');
fwrite(fid, HDR.rsv_tx_z(i), 'float');
fwrite(fid, HDR.time_zero(i), 'float');
fwrite(fid, HDR.zero_flag(i), 'float');
fwrite(fid, HDR.num_channels(i), 'float');
fwrite(fid, HDR.time(i), 'float');
fwrite(fid, HDR.comment_flag(i), 'float');
fwrite(fid, HDR.comment, 'char');
fwrite(fid, data(:, i), 'short');
if mod(i, 100) == 0
waitbar(i / HDR.num_trac, wb, sprintf('Exporting... %.f%%', ...
i / HDR.num_trac * 100))
end
end
fclose(fid);
% IPRH / IPRB, Impulse Radar ==============================================
% IPRH is the header file. In this file is all the important information
% such as the number of samples, traces, measurement intervals can be
% found.
% IPRB is the data file. This file contains only the data (amplitude values)
% in a binary form.
elseif gpr_format == 4
% Header structure
HDR.fname = HDR.fname; % File name
HDR.hdr_version = 20; % Header version
HDR.data_format = 16; % Data format 16 or 32 bit
date_time = clock;
HDR.date = ([num2str(date_time(1)), '-' ...
num2str(date_time(2)), '-' ...
num2str(date_time(3))]);% Date
HDR.start_time = '00:00:00'; % Measurement start time
HDR.stop_time = '00:00:00'; % Measurement end time
HDR.antenna = [num2str(HDR.centre_freq) ' MHz']; % Antenna frequency (MHz)
HDR.ant_sep = HDR.ant_sep; % Antenna seperation / Tx-Rx distance (m)
HDR.num_samp = HDR.num_samp; % Number of samples
HDR.signal_pos = 0; % Signal position
HDR.clipped_samps = 0; % Clipped samples
HDR.runs = 0; % Number of runs
HDR.stacks = 1; % Maximum number of stacks
HDR.auto_stacks = 1; % Autostacks (1 = On)
HDR.samp_freq = HDR.samp_freq; % Sampling frequency (MHz)
HDR.time_window = HDR.time_window; % Total time window (ns)
HDR.num_trac = HDR.num_trac; % Number of traces
HDR.trig_source = 'wheel'; % Trig source (wheel or time)
HDR.trac_int_sec = 0; % Trace interval if trig source is time (sec)
HDR.trac_int_met = HDR.trac_int; % Trace interval if trig source is wheel (m)
HDR.user_trac_int = HDR.trac_int; % User defined trace interval if trig source is wheel (m)
HDR.stop_pos = HDR.num_trac * HDR.trac_int; % Stop position (meters or seconds) -> num_trac * trac_int
HDR.wheel_name = 'Cart'; % Wheel name
HDR.wheel_calibration = 0; % Wheel calibration
HDR.zero_lvl = 0; % Zero level
HDR.vel = 100; % The soil velocity (Selected in field m/usec). 100 is a random number
HDR.preprocessing = 'Unknown Preprocessing'; % Not in use
HDR.comment = '----'; % Not in use
HDR.antenna_FW = '----'; % Receiver firmware version
HDR.antenna_HW = '----'; % Not in use
HDR.antenna_FPGA = '----'; % Receiver FPGA version
HDR.antenna_serial = '----'; % Receiver serial number
HDR.software_version = '----'; % Software version
HDR.positioning = 0; % Positioning: (0 = no, 1 = TS, 2 = GPS)
HDR.num_channel = 1; % Number of channels
HDR.channel_config = 1; % This channel configuration
HDR.ch_x_offset = 0; % Channel position relative to ext.positioning
HDR.ch_y_offset = 0; % Channel position relative to ext.positioning
HDR.meas_direction = 1; % Meas. direction forward or backward
HDR.relative_direction = 0; % Direction to RL start(clockwise 360)
HDR.relative_distance = 0; % Distance from RL start to cross section
HDR.relative_start = 0; % DIstance from profile start to cross section
% IPRH file
fid = fopen([HDR.fname '.iprh'], 'w');
fprintf(fid, 'HEADER VERSION: %i\r\n', HDR.hdr_version);
fprintf(fid, 'DATA VERSION: %i\r\n', HDR.data_format);
fprintf(fid, 'DATE: %s\r\n', HDR.date);
fprintf(fid, 'START TIME: %s\r\n', HDR.start_time);
fprintf(fid, 'STOP TIME: %s\r\n', HDR.stop_time);
fprintf(fid, 'ANTENNA: %s\r\n', HDR.antenna);
fprintf(fid, 'ANTENNA SEPARATION: %0.6f\r\n', HDR.ant_sep);
fprintf(fid, 'SAMPLES: %i\r\n', HDR.num_samp);
fprintf(fid, 'SIGNAL POSITION: %0.6f\r\n', HDR.signal_pos);
fprintf(fid, 'CLIPPED SAMPLES: %i\r\n', HDR.clipped_samps);
fprintf(fid, 'RUNS: %i\r\n', HDR.runs);
fprintf(fid, 'MAX STACKS: %i\r\n', HDR.stacks);
fprintf(fid, 'AUTOSTACKS: %i\r\n', HDR.auto_stacks);
fprintf(fid, 'FREQUENCY: %0.6f\r\n', HDR.samp_freq);
fprintf(fid, 'TIMEWINDOW: %0.6f\r\n', HDR.time_window);
fprintf(fid, 'LAST TRACE: %i\r\n', HDR.num_trac);
fprintf(fid, 'TRIG SOURCE: %s\r\n', HDR.trig_source);
fprintf(fid, 'TIME INTERVAL: %0.6f\r\n', HDR.trac_int_sec);
fprintf(fid, 'DISTANCE INTERVAL: %0.6f\r\n', HDR.trac_int_met);
fprintf(fid, 'USER DISTANCE INTERVAL: %0.6f\r\n', HDR.user_trac_int);
fprintf(fid, 'STOP POSITION: %0.6f\r\n', HDR.stop_pos);
fprintf(fid, 'WHEEL NAME: %s\r\n', HDR.wheel_name);
fprintf(fid, 'WHEEL CALIBRATION: %0.6f\r\n', HDR.wheel_calibration);
fprintf(fid, 'ZERO LEVEL: %i\r\n', HDR.zero_lvl);
fprintf(fid, 'SOIL VELOCITY: %i\r\n', HDR.vel);
fprintf(fid, 'PREPROCESSING: %s\r\n', HDR.preprocessing);
fprintf(fid, 'OPERATOR COMMENT: %s\r\n', HDR.comment);
fprintf(fid, 'ANTENNA F/W: %s\r\n', HDR.antenna_FW);
fprintf(fid, 'ANTENNA H/W: %s\r\n', HDR.antenna_HW);
fprintf(fid, 'ANTENNA FPGA: %s\r\n', HDR.antenna_FPGA);
fprintf(fid, 'ANTENNA SERIAL: %s\r\n', HDR.antenna_serial);
fprintf(fid, 'SOFTWARE VERSION: %s\r\n', HDR.software_version);
fprintf(fid, 'POSITIONING: %i\r\n', HDR.positioning);
fprintf(fid, 'CHANNELS: %i\r\n', HDR.num_channel);
fprintf(fid, 'CHANNEL CONFIGURATION: %i\r\n', HDR.channel_config);
fprintf(fid, 'CH_X_OFFSET: %0.6f\r\n', HDR.ch_x_offset);
fprintf(fid, 'CH_Y_OFFSET: %0.6f\r\n', HDR.ch_y_offset);
fprintf(fid, 'MEASUREMENT DIRECTION: %i\r\n', HDR.meas_direction);
fprintf(fid, 'RELATIVE DIRECTION: %i\r\n', HDR.relative_direction);
fprintf(fid, 'RELATIVE DISTANCE: %0.6f\r\n', HDR.relative_distance);
fprintf(fid, 'RELATIVE START: %0.6f\r\n', HDR.relative_start);
fclose(fid);
% IPRB file
fid = fopen([HDR.fname '.iprb'], 'w');
fwrite(fid, data, 'short');
fclose(fid);
end
waitbar(1, wb, 'Done!!!');
pause(1);
close(wb);

查看文件

@@ -0,0 +1,76 @@
% plot_Ascan.m
% Script to save and plot EM fields from a gprMax A-scan
%
% Craig Warren
clear all, clc
[filename, pathname] = uigetfile('*.out', 'Select gprMax A-scan output file to plot');
fullfilename = strcat(pathname, filename);
if filename ~= 0
header.title = h5readatt(fullfilename, '/', 'Title');
header.iterations = double(h5readatt(fullfilename,'/', 'Iterations'));
tmp = h5readatt(fullfilename, '/', 'dx_dy_dz');
header.dx = tmp(1);
header.dy = tmp(2);
header.dz = tmp(3);
header.dt = h5readatt(fullfilename, '/', 'dt');
header.nsrc = h5readatt(fullfilename, '/', 'nsrc');
header.nrx = h5readatt(fullfilename, '/', 'nrx');
% Time vector for plotting
time = linspace(0, (header.iterations - 1) * header.dt, header.iterations)';
% Initialise structure for field arrays
fields.ex = zeros(header.iterations, header.nrx);
fields.ey = zeros(header.iterations, header.nrx);
fields.ez = zeros(header.iterations, header.nrx);
fields.hx = zeros(header.iterations, header.nrx);
fields.hy = zeros(header.iterations, header.nrx);
fields.hz = zeros(header.iterations, header.nrx);
% Save and plot fields from each receiver
for n=1:header.nrx
path = strcat('/rxs/rx', num2str(n));
tmp = h5readatt(fullfilename, path, 'Position');
header.rx(n) = tmp(1);
header.ry(n) = tmp(2);
header.rz(n) = tmp(3);
path = strcat(path, '/');
fields.ex(:,n) = h5read(fullfilename, strcat(path, 'Ex'));
fields.ey(:,n) = h5read(fullfilename, strcat(path, 'Ey'));
fields.ez(:,n) = h5read(fullfilename, strcat(path, 'Ez'));
fields.hx(:,n) = h5read(fullfilename, strcat(path, 'Hx'));
fields.hy(:,n) = h5read(fullfilename, strcat(path, 'Hy'));
fields.hz(:,n) = h5read(fullfilename, strcat(path, 'Hz'));
fh1=figure('Name', strcat('rx', num2str(n)));
ax(1) = subplot(3,2,1); plot(time, fields.ex(:,n), 'r', 'LineWidth', 2), grid on, xlabel('Time [s]'), ylabel('Field strength [V/m]'), title('E_x')
ax(2) = subplot(3,2,3); plot(time, fields.ey(:,n), 'r', 'LineWidth', 2), grid on, xlabel('Time [s]'), ylabel('Field strength [V/m]'), title('E_y')
ax(3) = subplot(3,2,5); plot(time, fields.ez(:,n), 'r', 'LineWidth', 2), grid on, xlabel('Time [s]'), ylabel('Field strength [V/m]'), title('E_z')
ax(4) = subplot(3,2,2); plot(time, fields.hx(:,n), 'b', 'LineWidth', 2), grid on, xlabel('Time [s]'), ylabel('Field strength [A/m]'), title('H_x')
ax(5) = subplot(3,2,4); plot(time, fields.hy(:,n), 'b', 'LineWidth', 2), grid on, xlabel('Time [s]'), ylabel('Field strength [A/m]'), title('H_y')
ax(6) = subplot(3,2,6); plot(time, fields.hz(:,n), 'b', 'LineWidth', 2), grid on, xlabel('Time [s]'), ylabel('Field strength [A/m]'), title('H_z')
set(ax,'FontSize', 16, 'xlim', [0 time(end)]);
% Options to create a nice looking figure for display and printing
set(fh1,'Color','white','Menubar','none');
X = 60; % Paper size
Y = 30; % Paper size
xMargin = 0; % Left/right margins from page borders
yMargin = 0; % Bottom/top margins from page borders
xSize = X - 2*xMargin; % Figure size on paper (width & height)
ySize = Y - 2*yMargin; % Figure size on paper (width & height)
% Figure size displayed on screen
set(fh1, 'Units','centimeters', 'Position', [0 0 xSize ySize])
movegui(fh1, 'center')
% Figure size printed on paper
set(fh1,'PaperUnits', 'centimeters')
set(fh1,'PaperSize', [X Y])
set(fh1,'PaperPosition', [xMargin yMargin xSize ySize])
set(fh1,'PaperOrientation', 'portrait')
end
end

查看文件

@@ -0,0 +1,53 @@
% plot_Bscan.m
% Script to plot EM fields from a gprMax B-scan
%
% Craig Warren
clear all, clc
[filename, pathname] = uigetfile('*.out', 'Select gprMax output file to plot B-scan', 'MultiSelect', 'on');
filename = fullfile(pathname, filename);
% Open file and read fields
if filename ~= 0
iterations = double(h5readatt(filename, '/', 'Iterations'));
dt = h5readatt(filename, '/', 'dt');
prompt = 'Which field do you want to view? Ex, Ey, or Ez: ';
field = input(prompt,'s');
fieldpath = strcat('/rxs/rx1/', field);
field = h5read(filename, fieldpath)';
time = linspace(0, (iterations - 1) * dt, iterations)';
traces = 0:size(field, 2);
fh1=figure('Name', filename);
clims = [-max(max(abs(field))) max(max(abs(field)))];
im = imagesc(traces, time, field, clims);
xlabel('Trace number');
ylabel('Time [s]');
c = colorbar;
c.Label.String = 'Field strength [V/m]';
ax = gca;
ax.FontSize = 16;
xlim([0 traces(end)]);
% Options to create a nice looking figure for display and printing
set(fh1,'Color','white','Menubar','none');
X = 60; % Paper size
Y = 30; % Paper size
xMargin = 0; % Left/right margins from page borders
yMargin = 0; % Bottom/top margins from page borders
xSize = X - 2*xMargin; % Figure size on paper (width & height)
ySize = Y - 2*yMargin; % Figure size on paper (width & height)
% Figure size displayed on screen
set(fh1, 'Units','centimeters', 'Position', [0 0 xSize ySize])
movegui(fh1, 'center')
% Figure size printed on paper
set(fh1,'PaperUnits', 'centimeters')
set(fh1,'PaperSize', [X Y])
set(fh1,'PaperPosition', [xMargin yMargin xSize ySize])
set(fh1,'PaperOrientation', 'portrait')
end

查看文件

@@ -0,0 +1,346 @@
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import json
import mmap
import os
from xml.etree import ElementTree as ET
from paraview.simple import *
# Read Paraview version number to set threshold filter method
pvv = GetParaViewVersion()
if pvv.major == 5 and pvv.minor < 10:
new_thres = False
else:
new_thres = True
def threshold_filt(input, lt, ut, scalars):
"""Create threshold filter according to Paraview version.
Args:
input (array): input data to threshold filter
lt, ut (int): lower and upper bounds of thresholding operation
scalars (list/str): name of scalar array to perform thresholding
Returns:
threshold (object): threshold filter
"""
threshold = Threshold(Input=input)
threshold.Scalars = scalars
if new_thres:
threshold.LowerThreshold = lt
threshold.UpperThreshold = ut
else:
threshold.ThresholdRange = [lt, ut]
return threshold
def display_src_rx(srcs_rxs, dl):
"""Display sources and receivers as Paraview box sources.
Only suitable for gprMax >= v4
Args:
srcs_rxs (list): source/receiver names and positions
dl (tuple): spatial discretisation
"""
for item in srcs_rxs:
pos = item['position']
name = item['name']
src_rx = Box(Center=[pos[0] + dl[0]/2,
pos[1] + dl[1]/2,
pos[2] + dl[2]/2],
XLength=dl[0], YLength=dl[1], ZLength=dl[2])
RenameSource(name, src_rx)
Show(src_rx)
def display_pmls(pmlthick, dx_dy_dz, nx_ny_nz):
"""Display PMLs as box sources using PML thickness values.
Only suitable for gprMax >= v4
Args:
pmlthick (tuple): PML thickness values for each slab (cells)
dx_dy_dz (tuple): Spatial resolution (m)
nx_ny_dz (tuple): Domain size (cells)
"""
pml_names = ['x0', 'y0', 'z0', 'xmax', 'ymax', 'zmax']
pmls = dict.fromkeys(pml_names, None)
if pmlthick[0] != 0:
x0 = Box(Center=[pmlthick[0] * dx_dy_dz[0] / 2,
nx_ny_nz[1] * dx_dy_dz[1] / 2,
nx_ny_nz[2] * dx_dy_dz[2] / 2],
XLength=pmlthick[0] * dx_dy_dz[0],
YLength=nx_ny_nz[1] * dx_dy_dz[1],
ZLength=nx_ny_nz[2] * dx_dy_dz[2])
pmls['x0'] = x0
if pmlthick[3] != 0:
xmax = Box(Center=[dx_dy_dz[0] * (nx_ny_nz[0] - pmlthick[3] / 2),
nx_ny_nz[1] * dx_dy_dz[1] / 2,
nx_ny_nz[2] * dx_dy_dz[2] / 2],
XLength=pmlthick[3] * dx_dy_dz[0],
YLength=nx_ny_nz[1] * dx_dy_dz[1],
ZLength=nx_ny_nz[2] * dx_dy_dz[2])
pmls['xmax'] = xmax
if pmlthick[1] != 0:
y0 = Box(Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2,
pmlthick[1] * dx_dy_dz[1] / 2,
nx_ny_nz[2] * dx_dy_dz[2] / 2],
XLength=nx_ny_nz[0] * dx_dy_dz[0],
YLength=pmlthick[1] * dx_dy_dz[1],
ZLength=nx_ny_nz[2] * dx_dy_dz[2])
pmls['y0'] = y0
if pmlthick[4] != 0:
ymax = Box(Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2,
dx_dy_dz[1] * (nx_ny_nz[1] - pmlthick[4] / 2),
nx_ny_nz[2] * dx_dy_dz[2] / 2],
XLength=nx_ny_nz[0] * dx_dy_dz[0],
YLength=pmlthick[4] * dx_dy_dz[1],
ZLength=nx_ny_nz[2] * dx_dy_dz[2])
pmls['ymax'] = ymax
if pmlthick[2] != 0:
z0 = Box(Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2,
nx_ny_nz[1] * dx_dy_dz[1] / 2,
pmlthick[2] * dx_dy_dz[2] / 2],
XLength=nx_ny_nz[0] * dx_dy_dz[0],
YLength=nx_ny_nz[1] * dx_dy_dz[1],
ZLength=pmlthick[2] * dx_dy_dz[2])
pmls['z0'] = z0
if pmlthick[5] != 0:
zmax = Box(Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2,
nx_ny_nz[1] * dx_dy_dz[1] / 2,
dx_dy_dz[2] * (nx_ny_nz[2] - pmlthick[5] / 2)],
XLength=nx_ny_nz[0] * dx_dy_dz[0],
YLength=nx_ny_nz[1] * dx_dy_dz[1],
ZLength=pmlthick[5] * dx_dy_dz[2])
pmls['zmax'] = zmax
# Name PML sources and set opacity
tmp = []
for pml in pmls:
if pmls[pml]:
RenameSource('PML - ' + pml, pmls[pml])
Hide(pmls[pml], renderview)
tmp.append(pmls[pml])
# Create a group of PMLs to switch on/off easily
if tmp:
pml_gp = AppendDatasets(Input=tmp)
RenameSource('PML - All', pml_gp)
pml_view = Show(pml_gp)
pml_view.Opacity = 0.5
# Get whatever source is loaded (should be model)
model = GetActiveSource()
# Get active view
renderview = GetActiveView()
# Show Data Axes Grid
renderview.AxesGrid.Visibility = 1
# Hide display of root data
Hide(model)
#####################################
# Get filename or list of filenames #
#####################################
# Single .vti or .vtu file
if len(model.FileName) == 1:
files = model.FileName
dirname = os.path.dirname(files[0])
# Multiple .vti or .vtu files referenced in a .pvd file
else:
files = []
dirname = os.path.dirname(model.FileName)
tree = ET.parse(model.FileName)
root = tree.getroot()
for elem in root:
for subelem in elem.findall('DataSet'):
tmp = os.path.join(dirname, subelem.get('file'))
files.append(tmp)
# Dictionaries to hold data - mainly for <v4 behaviour
materials = {}
srcs = {}
rxs = {}
pmls = {}
# To hold the maximum numerical ID for materials across multiple files
material_ID_max = 0
#################################################################
# Read and display data from file(s), i.e. materials, sources, #
# receivers, and PMLs #
# Method depends on gprMax version #
#################################################################
for file in files:
with open(file, 'rb') as f:
#######################
# Read data from file #
#######################
# Determine gprMax version
# Read XML data
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
# Look for <gprMax> tag which indicates version <4
try:
xml_pos = mm.find(b'<gprMax')
mm.seek(xml_pos)
xml = mm.read(mm.size() - xml_pos)
root = ET.fromstring(xml)
# ET.dump(root)
v4 = False
print('\ngprMax version: < v.4.0.0')
print(file)
# Read material names and numeric IDs into a dict
for elem in root.findall('Material'):
materials[elem.get('name')] = int(elem.text)
if int(elem.text) > material_ID_max:
material_ID_max = int(elem.text)
# Read sources
for elem in root.findall('Sources'):
srcs[elem.get('name')] = int(elem.text)
# Read receivers
for elem in root.findall('Receivers'):
rxs[elem.get('name')] = int(elem.text)
# Read PMLs
for elem in root.findall('PML'):
pmls[elem.get('name')] = int(elem.text)
except:
v4 = True
# Comments () embedded in line 3 of file
f.readline()
f.readline()
c = f.readline().decode()
# Strip comment tags
c = c[5:-5]
# Model information
c = json.loads(c)
print('\ngprMax version: ' + c['gprMax_version'])
print(file)
################
# Display data #
################
if v4:
# Discretisation
dl = c['dx_dy_dz']
# Number of voxels
nl = c['nx_ny_nz']
# Store materials
try:
mats = c['Materials']
for i, material in enumerate(mats):
materials[material] = i
if i > material_ID_max:
material_ID_max = i
except KeyError:
print('No materials to load')
# Display any sources
try:
srcs = c['Sources']
display_src_rx(srcs, dl)
except KeyError:
print('No sources to load')
# Display any receivers
try:
rxs = c['Receivers']
display_src_rx(rxs, dl)
except KeyError:
print('No receivers to load')
# Display any PMLs
try:
pt = c['PMLthickness']
display_pmls(pt, dl, nl)
except KeyError:
print('No PMLs to load')
else:
# Display any sources and PMLs
srcs_pmls = dict(srcs)
srcs_pmls.update(pmls)
if srcs_pmls:
for k, v in srcs_pmls.items():
threshold = threshold_filt(model, v, v, 'Sources_PML')
RenameSource(k, threshold)
# Show data in view
thresholddisplay = Show(threshold, renderview)
thresholddisplay.ColorArrayName = 'Sources_PML'
if v == 1:
thresholddisplay.Opacity = 0.5
threshold.UpdatePipeline()
# Display any receivers
if rxs:
for k, v in rxs.items():
threshold = threshold_filt(model, v, v, 'Receivers')
RenameSource(k, threshold)
# Show data in view
thresholddisplay = Show(threshold, renderview)
thresholddisplay.ColorArrayName = 'Receivers'
threshold.UpdatePipeline()
# Display materials
material_range = range(0, material_ID_max + 1)
for k, v in sorted(materials.items(), key=lambda x: x[1]):
if v in material_range:
threshold = threshold_filt(model, v, v, ['CELLS', 'Material'])
RenameSource(k, threshold)
# Show data in view, except for free_space
if v != 1:
thresholddisplay = Show(threshold, renderview)
thresholddisplay.ColorArrayName = ['CELLS', 'Material']
threshold.UpdatePipeline()
RenderAllViews()
# Reset view to fit data
renderview.ResetCamera()
# Show color bar/color legend
# thresholdDisplay.SetScalarBarVisibility(renderview, False)

查看文件

@@ -0,0 +1,129 @@
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import argparse
import logging
import os
import h5py
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import numpy as np
logger = logging.getLogger(__name__)
class Cursor(object):
"""Get RGB(A) value of pixel at x,y coordinate of button press and store in a list."""
def __init__(self, im, materials):
"""
Args:
im (ndarray): Pixels of the image.
materials (list): To store selected RGB(A) values of selected pixels.
"""
self.im = im
self.materials = materials
plt.connect('button_press_event', self)
def __call__(self, event):
"""
Args:
event (MouseEvent): matplotlib mouse event.
"""
if not event.dblclick:
x, y = event.xdata, event.ydata
if x is not None and y is not None:
pixel = self.im[int(y), int(x)]
pixel = np.floor(pixel * 255).astype(np.int16) # Convert pixel values from float (0-1) to integer (0-255)
match = pixel_match(materials, pixel)
if match is False:
logger.info('x, y: {} {} px; RGB: {}; material ID: {}'.format(int(x), int(y), pixel[:-1], len(self.materials)))
materials.append(pixel)
def pixel_match(pixellist, pixeltest):
"""Checks if the RGB(A) value of a pixel already exists in a list of pixel values.
Args:
pixellist (list): List of numpy arrays of pixels to test against.
pixeltest (ndarray): RGB(A) value of test pixel.
Returns:
match (boolean): True if pixel is matched in pixel list or False if not found.
"""
match = False
for pixel in pixellist:
if np.all(pixel == pixeltest):
match = True
break
return match
if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(description='Convert a PNG image to a HDF5 file that can be used to import geometry (#geometry_objects_read) into a 2D gprMax model. Colours from the image are selected which correspond to a list of materials that should be supplied in a separate text file.', usage='python convert_png2h5.py imagefile dx dy dz')
parser.add_argument('imagefile', help='name of image file including path')
parser.add_argument('dxdydz', type=float, action='append', nargs=3, help='spatial resolution of model, e.g. dx dy dz')
parser.add_argument('-zcells', default=1, type=int, help='number of cells for domain in z-direction (infinite direction)')
args = parser.parse_args()
# Open image file
im = mpimg.imread(args.imagefile)
# Store image data to use for creating geometry
imdata = np.rot90(im, k=3) # Rotate 90CW
imdata = np.floor(imdata * 255).astype(np.int16) # Convert pixel values from float (0-1) to integer (0-255)
logger.info('Reading PNG image file: {}'.format(os.path.split(args.imagefile)[1]))
logger.info(' 1. Select discrete material colours by clicking on parts of the image.\n 2. When all materials have been selected close the image.')
# List to hold selected RGB values from image
materials = []
# Plot image and record rgb values from mouse clicks
fig = plt.figure(num=os.path.split(args.imagefile)[1], facecolor='w', edgecolor='w')
im = np.flipud(im) # Flip image for viewing with origin in lower left
plt.imshow(im, interpolation='nearest', aspect='equal', origin='lower')
Cursor(im, materials)
plt.show()
# Format spatial resolution into tuple
dx_dy_dz = (args.dxdydz[0][0], args.dxdydz[0][1], args.dxdydz[0][2])
# Filename for geometry (HDF5) file
hdf5file = os.path.splitext(args.imagefile)[0] + '.h5'
# Array to store geometry data (initialised as background, i.e. -1)
data = np.ones((imdata.shape[0], imdata.shape[1], args.zcells), dtype=np.int16) * -1
# Write geometry (HDF5) file
with h5py.File(hdf5file, 'w') as fout:
# Add attribute with name 'dx_dy_dz' for spatial resolution
fout.attrs['dx_dy_dz'] = dx_dy_dz
# Use a boolean mask to match selected pixel values with position in image
for i, material in enumerate(materials):
mask = np.all(imdata == material, axis=-1)
data[mask,:] = i
# Write data to file
fout.create_dataset('data', data=data)
logger.info('Written HDF5 file: {}'.format(os.path.split(hdf5file)[1]))

查看文件

@@ -0,0 +1,50 @@
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from gprMax.utilities.host_info import (detect_cuda_gpus, detect_opencl,
get_host_info, print_cuda_info,
print_opencl_info)
from gprMax.utilities.utilities import get_terminal_width, human_size
# Host machine info.
hostinfo = get_host_info()
hyperthreadingstr = f", {hostinfo['logicalcores']} cores with Hyper-Threading" if hostinfo['hyperthreading'] else ''
hostname = (f"\n=== {hostinfo['hostname']}")
print(f"{hostname} {'=' * (get_terminal_width() - len(hostname) - 1)}")
print(f"\n{'Mfr/model:':<12} {hostinfo['machineID']}")
print(f"{'CPU:':<12} {hostinfo['sockets']} x {hostinfo['cpuID']} ({hostinfo['physicalcores']} cores{hyperthreadingstr})")
print(f"{'RAM:':<12} {human_size(hostinfo['ram'], a_kilobyte_is_1024_bytes=True)}")
print(f"{'OS/Version:':<12} {hostinfo['osversion']}")
# OpenMP
print("\n\n=== OpenMP capabilities (gprMax will not use Hyper-Threading with OpenMP as there is no performance advantage)\n")
print(f"{'OpenMP threads: '} {hostinfo['physicalcores']}")
# CUDA
print("\n\n=== CUDA capabilities\n")
gpus = detect_cuda_gpus()
if gpus:
print_cuda_info(gpus)
# OpenCL
print("\n\n=== OpenCL capabilities\n")
devs = detect_opencl()
if devs:
print_opencl_info(devs)
print(f"\n{'=' * (get_terminal_width() - 1)}\n")

查看文件

@@ -0,0 +1,429 @@
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import argparse
import logging
logger = logging.getLogger(__name__)
"""Converts old to new style input files."""
# Parse command line arguments
parser = argparse.ArgumentParser(description='Converts old style input file to new style input file.', usage='cd gprMax; python -m tools.inputfile_new2old inputfile')
parser.add_argument('inputfile', help='name of input file including path')
args = parser.parse_args()
inputfile = args.inputfile
with open(inputfile, 'r') as f:
# Strip out any newline characters and comments that must begin with double hashes
inputlines = [line.rstrip() for line in f]
# New file name base
try:
newfile = inputfile.split('.')[0]
except:
newfile = inputfile
newfile += '_v3syntax'
logger.info("Attempting to convert inputfile '{}' to use new syntax...\n".format(inputfile))
model2D = False
txs = []
badwaveforms = ['gaussiandot', 'gaussiandotdot']
linesources = []
voltagesources = []
hertziandipoles = []
transmissionlines = []
lindex = 0
while(lindex < len(inputlines)):
if inputlines[lindex].startswith('#') and not inputlines[lindex].startswith('##'):
cmd = inputlines[lindex].split(':')
cmdname = cmd[0].lower()
params = cmd[1].split()
if cmdname == '#dx_dy':
model2D = True
# Syntax of old command: #dx_dy: x y
replacement = '#dx_dy_dz: {:g} {:g} {:g}'.format(float(params[0]), float(params[1]), float(params[1]))
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
dx_dy_dz = (float(params[0]), float(params[1]), float(params[1]))
elif cmdname == '#dx_dy_dz':
dx_dy_dz = (float(params[0]), float(params[1]), float(params[2]))
lindex += 1
else:
lindex += 1
else:
lindex += 1
lindex = 0
while(lindex < len(inputlines)):
if inputlines[lindex].startswith('#') and not inputlines[lindex].startswith('##'):
cmd = inputlines[lindex].split(':')
cmdname = cmd[0].lower()
params = cmd[1].split()
if cmdname == '#domain':
if model2D:
# Syntax of old command: #domain: x y
replacement = '#domain: {:g} {:g} {:g}'.format(float(params[0]), float(params[1]), dx_dy_dz[2])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
domain = (float(params[0]), float(params[1]), dx_dy_dz[2])
else:
domain = (float(params[0]), float(params[1]), float(params[2]))
lindex += 1
elif cmdname == '#time_window':
params = params[0].lower()
if '.' in params or 'e' in params:
timewindow = float(params)
else:
timewindow = int(params)
lindex += 1
elif cmdname == '#num_of_procs':
# Syntax of old command: #num_of_procs: nthreads
replacement = '#num_threads: {}'.format(params[0])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#tx':
txs.append(inputlines[lindex])
lindex += 1
elif cmdname == '#line_source':
linesources.append(inputlines[lindex])
lindex += 1
elif cmdname == '#voltage_source':
voltagesources.append(inputlines[lindex])
lindex += 1
elif cmdname == '#hertzian_dipole':
hertziandipoles.append(inputlines[lindex])
lindex += 1
elif cmdname == '#transmission_line':
transmissionlines.append(inputlines[lindex])
lindex += 1
elif cmdname == '#rx':
if model2D:
# Syntax of old command: #rx: x1 y1
replacement = '#rx: {} {} {}'.format(params[0], params[1], 0)
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#rx_box':
if model2D:
# Syntax of old command: #rx_box: x1 y1 x2 y2 dx dy
replacement = '#rx_array: {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], 0, params[2], params[3], dx_dy_dz[2], params[4], params[5], dx_dy_dz[2])
else:
# Syntax of old command: #rx_box: x1 y1 z1 x2 y2 z2 dx dy dz
replacement = '#rx_array: {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#tx_steps':
if model2D:
# Syntax of old command: #tx_steps: dx dy
replacement = '#src_steps: {} {} {}'.format(params[0], params[1], 0)
else:
# Syntax of old command: #tx_steps: dx dy dz
replacement = '#src_steps: {} {} {}'.format(params[0], params[1], params[2])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#rx_steps':
if model2D:
# Syntax of old command: #rx_steps: dx dy
replacement = '#rx_steps: {} {} {}'.format(params[0], params[1], 0)
else:
# Syntax of old command: #rx_steps: dx dy dz
replacement = '#rx_steps: {} {} {}'.format(params[0], params[1], params[2])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#medium':
# Syntax of old command: #medium: e_rs e_inf tau sig_e mu_r sig_m ID
replacement = '#material: {} {} {} {} {}'.format(params[0], params[3], params[4], params[5], params[6])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
if float(params[1]) > 0:
replacement = '#add_dispersion_debye: 1 {} {} {}'.format(float(params[0]) - float(params[1]), params[2], params[6])
logger.info("Command '{}' added.".format(replacement))
inputlines.insert(lindex + 1, replacement)
lindex += 1
elif cmdname == '#box':
if model2D:
# Syntax of old command: #box: x1 y1 x2 y2 ID
replacement = '#box: {} {} {} {} {} {} {}'.format(params[0], params[1], 0, params[2], params[3], dx_dy_dz[2], params[4])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#triangle':
if model2D:
# Syntax of old command: #triangle: x1 y1 x2 y2 x3 y3 ID
replacement = '#triangle: {} {} {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], 0, params[2], params[3], 0, params[4], params[5], 0, dx_dy_dz[2], params[6])
else:
# Syntax of old command: #triangle: x1 y1 z1 x2 y2 z2 x3 y3 z3 ID
replacement = '#triangle: {} {} {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], 0, params[9])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#wedge':
# Syntax of old command: #wedge: x1 y1 z1 x2 y2 z2 x3 y3 z3 thickness ID
replacement = '#triangle: {} {} {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#bowtie':
logger.info("Command '{}', is no longer supported. You can create the bowtie shape using two triangle commands.".format(inputlines[lindex]))
inputlines.pop(lindex)
elif cmdname == '#cylinder':
if model2D:
# Syntax of old command: #cylinder: x y radius ID
replacement = '#cylinder: {} {} {} {} {} {} {} {}'.format(params[0], params[1], 0, params[0], params[1], dx_dy_dz[2], params[2], params[3])
else:
# Syntax of old command: #cylinder: axis axis_start axis_stop f1 f2 radius ID
if params[0] == 'x':
replacement = '#cylinder: {} {} {} {} {} {} {} {}'.format(params[1], params[3], params[4], params[2], params[3], params[4], params[5], params[6])
elif params[0] == 'y':
replacement = '#cylinder: {} {} {} {} {} {} {} {}'.format(params[3], params[1], params[4], params[3], params[2], params[4], params[5], params[6])
elif params[0] == 'z':
replacement = '#cylinder: {} {} {} {} {} {} {} {}'.format(params[3], params[4], params[1], params[3], params[4], params[2], params[5], params[6])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#cylinder_new':
# Syntax of old command: #cylinder_new: x1 y1 z1 x2 y2 z2 radius ID
replacement = '#cylinder: {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#cylindrical_segment':
logger.info("Command '{}' has been removed as it is no longer supported. You can create a cylindrical segment by using a #box to cut through a #cylinder.".format(inputlines[lindex]))
inputlines.pop(lindex)
elif cmdname in ['#x_segment', '#y_segment']:
logger.info("Command '{}' has been removed. A circular segment can be created by using the #cylinder command and cutting it with a #box. Alternatively the #cylindrical_sector command maybe useful.".format(inputlines[lindex]))
inputlines.pop(lindex)
elif cmdname == '#media_file':
logger.info("Command '{}' has is no longer supported. Please include your materials using the #material command directly in the input file.".format(inputlines[lindex]))
inputlines.pop(lindex)
elif cmdname == '#pml_layers':
# Syntax of old command: #pml_layers: num_layers
replacement = '#pml_cells: {}'.format(params[0])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname in ['#abc_order', '#abc_type', 'abc_optimisation_angles', '#abc_mixing_parameters', '#abc_stability_factors']:
logger.info("Command '{}' has been removed as Higdon Absorbing Boundary Conditions (ABC) are no longer supported. The default ABC is the (better performing) Perfectly Matched Layer (PML).".format(inputlines[lindex]))
inputlines.pop(lindex)
elif cmdname == '#analysis':
# Syntax of old command: #analysis: num_model_runs outputfile outputfiletype
if int(params[0]) > 1:
extra = " To run a model multiple times use the command line option -n, e.g. gprMax {} -n {}".format(inputfile, int(params[0]))
else:
extra = ''
logger.info("Command '{}' has been removed as it is no longer required.{}".format(inputlines[lindex], extra))
inputlines.pop(lindex)
elif cmdname in ['#end_analysis', '#number_of_media', '#nips_number']:
logger.info("Command '{}' has been removed as it is no longer required.".format(inputlines[lindex]))
inputlines.pop(lindex)
elif cmdname == '#snapshot':
if model2D:
# Syntax of old command: #snapshot: i1 x1 y1 x2 y2 dx dy time filename type
replacement = '#snapshot: {} {} {} {} {} {} {} {} {} {} {}'.format(params[1], params[2], 0, params[3], params[4], dx_dy_dz[2], params[5], params[6], dx_dy_dz[2], params[7], params[8])
else:
# Syntax of old command: #snapshot: i1 x1 y1 z1 x2 y2 z2 dx dy dz time filename type
replacement = '#snapshot: {} {} {} {} {} {} {} {} {} {} {}'.format(params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#geometry_file':
# Syntax of old command: #geometry_file: filename
if params[0].endswith('.geo'):
params = params[0].split('.')
replacement = '#geometry_view: 0 0 0 {} {} {} {} {} {} {} n'.format(domain[0], domain[1], domain[2], dx_dy_dz[0], dx_dy_dz[1], dx_dy_dz[2], params[0])
logger.info("Command '{}', replaced with '{}'. This is a geometry view of the entire domain, sampled at the spatial resolution of the model, using the per Yee cell option (n). You may want to consider taking a smaller geometry view or using a coarser sampling. You may also want to use the per Yee cell edge option (f) to view finer details.".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname == '#geometry_vtk':
# Syntax of old command: #geometry_vtk: x1 y1 z1 x2 y2 z2 dx dy dz filename type
replacement = '#geometry_view: {} {} {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10])
logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement))
inputlines.pop(lindex)
inputlines.insert(lindex, replacement)
lindex += 1
elif cmdname in ['#plane_wave', '#thin_wire', '#huygens_surface']:
logger.exception("Command '{}' has not yet implemented in the new version of gprMax. For now please continue to use the old version.".format(inputlines[lindex]))
raise ValueError
else:
lindex += 1
else:
lindex += 1
# Convert separate #line_source and associated #tx to #waveform and #hertzian_dipole
for source in linesources:
params = source.split()
if params[3] is badwaveforms:
logger.exception("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms)))
raise ValueError
elif params[3] == 'ricker':
params[3] = 'gaussiandotnorm'
waveform = '#waveform: {} {} {} {}'.format(params[3], params[1], params[2], params[4])
tx = next(tx for tx in txs if tx.split()[3] == params[4])
hertziantx = tx.split()
if float(hertziantx[4]) > 0 or float(hertziantx[5]) != timewindow:
hertzian = '#hertzian_dipole: z {} {} {} {} {} {}'.format(hertziantx[1], hertziantx[2], 0, hertziantx[3], hertziantx[4], hertziantx[5])
else:
hertzian = '#hertzian_dipole: z {} {} {} {}'.format(hertziantx[1], hertziantx[2], 0, hertziantx[3])
logger.info("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, hertzian))
inputlines.remove(source)
inputlines.remove(tx)
inputlines.append(waveform)
inputlines.append(hertzian)
# Convert separate #hertzian_dipole and associated #tx to #waveform and #hertzian_dipole
for source in hertziandipoles:
params = source.split()
if params[3] is badwaveforms:
logger.exception("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms)))
raise ValueError
elif params[3] == 'ricker':
params[3] = 'gaussiandotnorm'
waveform = '#waveform: {} {} {} {}'.format(params[3], params[1], params[2], params[4])
tx = next(tx for tx in txs if tx.split()[5] == params[4])
hertziantx = tx.split()
if float(hertziantx[6]) > 0 or float(hertziantx[7]) != timewindow:
hertzian = '#hertzian_dipole: {} {} {} {} {} {} {}'.format(hertziantx[1], hertziantx[2], hertziantx[3], hertziantx[4], hertziantx[5], hertziantx[6], hertziantx[7])
else:
hertzian = '#hertzian_dipole: {} {} {} {} {}'.format(hertziantx[1], hertziantx[2], hertziantx[3], hertziantx[4], hertziantx[5])
logger.info("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, hertzian))
inputlines.remove(source)
inputlines.remove(tx)
inputlines.append(waveform)
inputlines.append(hertzian)
# Convert separate #voltage_source and associated #tx to #waveform and #voltage_source
for source in voltagesources:
params = source.split()
if params[3] is badwaveforms:
logger.exception("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms)))
raise ValueError
elif params[3] == 'ricker':
params[3] = 'gaussiandotnorm'
waveform = '#waveform: {} {} {} {}'.format(params[3], params[1], params[2], params[5])
tx = next(tx for tx in txs if tx.split()[5] == params[5])
voltagesourcetx = tx.split()
if float(voltagesourcetx[6]) > 0 or float(voltagesourcetx[7]) != timewindow:
voltagesource = '#voltage_source: {} {} {} {} {} {} {} {}'.format(voltagesourcetx[1], voltagesourcetx[2], voltagesourcetx[3], voltagesourcetx[4], params[4], voltagesourcetx[5], voltagesourcetx[6], voltagesourcetx[7])
else:
voltagesource = '#voltage_source: {} {} {} {} {} {}'.format(voltagesourcetx[1], voltagesourcetx[2], voltagesourcetx[3], voltagesourcetx[4], params[4], voltagesourcetx[5])
logger.info("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, voltagesource))
inputlines.remove(source)
inputlines.remove(tx)
inputlines.append(waveform)
inputlines.append(voltagesource)
# Convert separate #transmission_line and associated #tx to #waveform and #transmission_line
for source in transmissionlines:
params = source.split()
if params[3] is badwaveforms:
logger.exception("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms)))
raise ValueError
elif params[3] == 'ricker':
params[3] = 'gaussiandotnorm'
waveform = '#waveform: {} {} {} {}'.format(params[3], params[1], params[2], params[6])
tx = next(tx for tx in txs if tx.split()[5] == params[6])
transmissionlinetx = tx.split()
if float(transmissionlinetx[6]) != 0 or float(transmissionlinetx[7]) < timewindow:
transmissionline = '#transmission_line: {} {} {} {} {} {} {} {}'.format(transmissionlinetx[1], transmissionlinetx[2], transmissionlinetx[3], transmissionlinetx[4], params[5], transmissionlinetx[5], transmissionlinetx[6], transmissionlinetx[7])
else:
transmissionline = '#transmission_line: {} {} {} {} {} {}'.format(transmissionlinetx[1], transmissionlinetx[2], transmissionlinetx[3], transmissionlinetx[4], params[5], transmissionlinetx[5])
logger.info("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, transmissionline))
inputlines.remove(source)
inputlines.remove(tx)
inputlines.append(waveform)
inputlines.append(transmissionline)
# Write new input file
newinputfile = newfile + '.in'
with open(newinputfile, 'w') as f:
for line in inputlines:
f.write('{}\n'.format(line))
logger.info("\nWritten new input file: '{}'".format(newinputfile))

查看文件

@@ -0,0 +1,135 @@
# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import argparse
import glob
import logging
import os
from pathlib import Path
import h5py
import numpy as np
from gprMax._version import __version__
from gprMax.utilities.utilities import natural_keys
logger = logging.getLogger(__name__)
def get_output_data(filename, rxnumber, rxcomponent):
"""Gets B-scan output data from a model.
Args:
filename (string): Filename (including path) of output file.
rxnumber (int): Receiver output number.
rxcomponent (str): Receiver output field/current component.
Returns:
outputdata (array): Array of A-scans, i.e. B-scan data.
dt (float): Temporal resolution of the model.
"""
# Open output file and read some attributes
with h5py.File(filename, 'r') as f:
nrx = f.attrs['nrx']
dt = f.attrs['dt']
# Check there are any receivers
if nrx == 0:
logger.exception(f'No receivers found in {filename}')
raise ValueError
path = '/rxs/rx' + str(rxnumber) + '/'
availableoutputs = list(f[path].keys())
# Check if requested output is in file
if rxcomponent not in availableoutputs:
logger.exception(f"{rxcomponent} output requested to plot, but the available output for receiver 1 is {', '.join(availableoutputs)}")
raise ValueError
outputdata = f[path + '/' + rxcomponent]
outputdata = np.array(outputdata)
return outputdata, dt
def merge_files(outputfiles, removefiles=False):
"""Merges traces (A-scans) from multiple output files into one new file,
then optionally removes the series of output files.
Args:
outputfiles (list): List of output files to be merged.
removefiles (boolean): Flag to remove individual output files after merge.
"""
merged_outputfile = os.path.commonprefix(outputfiles) + '_merged.h5'
# Combined output file
fout = h5py.File(merged_outputfile, 'w')
for i, outputfile in enumerate(outputfiles):
fin = h5py.File(outputfile, 'r')
nrx = fin.attrs['nrx']
# Write properties for merged file on first iteration
if i == 0:
fout.attrs['gprMax'] = __version__
fout.attrs['Iterations'] = fin.attrs['Iterations']
fout.attrs['nx_ny_nz'] = fin.attrs['nx_ny_nz']
fout.attrs['dx_dy_dz'] = fin.attrs['dx_dy_dz']
fout.attrs['dt'] = fin.attrs['dt']
fout.attrs['nsrc'] = fin.attrs['nsrc']
fout.attrs['nrx'] = fin.attrs['nrx']
fout.attrs['srcsteps'] = fin.attrs['srcsteps']
fout.attrs['rxsteps'] = fin.attrs['rxsteps']
for rx in range(1, nrx + 1):
path = '/rxs/rx' + str(rx)
grp = fout.create_group(path)
availableoutputs = list(fin[path].keys())
for output in availableoutputs:
grp.create_dataset(output,
(fout.attrs['Iterations'], len(outputfiles)),
dtype=fin[path + '/' + output].dtype)
# For all receivers
for rx in range(1, nrx + 1):
path = '/rxs/rx' + str(rx) + '/'
availableoutputs = list(fin[path].keys())
# For all receiver outputs
for output in availableoutputs:
fout[path + '/' + output][:, i] = fin[path + '/' + output][:]
fin.close()
fout.close()
if removefiles:
for outputfile in outputfiles:
os.remove(outputfile)
if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(description='Merges traces (A-scans) from multiple output files into one new file, then optionally removes the series of output files.', usage='cd gprMax; python -m tools.outputfiles_merge basefilename')
parser.add_argument('basefilename', help='base name of output file series including path')
parser.add_argument('--remove-files', action='store_true', default=False, help='flag to remove individual output files after merge')
args = parser.parse_args()
files = glob.glob(args.basefilename + '*.h5')
outputfiles = [filename for filename in files if '_merged' not in filename]
outputfiles.sort(key=natural_keys)
merge_files(outputfiles, removefiles=args.remove_files)

0
toolboxes/__init__.py 普通文件
查看文件