你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-07 23:14:03 +08:00
Re-structuring package layout
这个提交包含在:
@@ -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]]
|
700
toolboxes/DebyeFit/Debye_Fit.py
普通文件
700
toolboxes/DebyeFit/Debye_Fit.py
普通文件
@@ -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()
|
248
toolboxes/DebyeFit/README.rst
普通文件
248
toolboxes/DebyeFit/README.rst
普通文件
@@ -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()
|
文件差异因一行或多行过长而隐藏
462
toolboxes/DebyeFit/optimization.py
普通文件
462
toolboxes/DebyeFit/optimization.py
普通文件
@@ -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.
|
836
toolboxes/GPRAntennaModels/GSSI.py
普通文件
836
toolboxes/GPRAntennaModels/GSSI.py
普通文件
@@ -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
|
文件差异内容过多而无法显示
加载差异
481
toolboxes/GPRAntennaModels/MALA.py
普通文件
481
toolboxes/GPRAntennaModels/MALA.py
普通文件
@@ -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).
|
二进制文件未显示。
二进制文件未显示。
@@ -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
|
二进制文件未显示。
二进制文件未显示。
@@ -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
|
二进制文件未显示。
二进制文件未显示。
@@ -0,0 +1,2 @@
|
||||
## can landmine material properties
|
||||
#material: 1 inf 1 0 pec
|
70
toolboxes/Materials/README.rst
普通文件
70
toolboxes/Materials/README.rst
普通文件
@@ -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)
|
34
toolboxes/Materials/eccosorb.txt
普通文件
34
toolboxes/Materials/eccosorb.txt
普通文件
@@ -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
|
282
toolboxes/Plotting/plot_Ascan.py
普通文件
282
toolboxes/Plotting/plot_Ascan.py
普通文件
@@ -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()
|
101
toolboxes/Plotting/plot_Bscan.py
普通文件
101
toolboxes/Plotting/plot_Bscan.py
普通文件
@@ -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()
|
70
toolboxes/STLtoVoxel/README.rst
普通文件
70
toolboxes/STLtoVoxel/README.rst
普通文件
@@ -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
|
33
toolboxes/STLtoVoxel/convert.py
普通文件
33
toolboxes/STLtoVoxel/convert.py
普通文件
@@ -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
|
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
21
toolboxes/STLtoVoxel/license.md
普通文件
21
toolboxes/STLtoVoxel/license.md
普通文件
@@ -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])
|
148
toolboxes/STLtoVoxel/slice.py
普通文件
148
toolboxes/STLtoVoxel/slice.py
普通文件
@@ -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
普通文件
0
toolboxes/__init__.py
普通文件
在新工单中引用
屏蔽一个用户