# 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 h5py import matplotlib.pyplot as plt import numpy as np import gprMax.config as config 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(f"Centre frequency: {f / 1000000000.0} GHz") if epsr: logger.info(f"Critical angle for Er {epsr} is {thetac} degrees") logger.info(f"Wavelength: {wavelength:.3f} m") 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(f"Written Numpy file: {os.path.splitext(outputfile)[0]}.npy")