From abffb2e433504a97cc7587ceb8ed7de4233e9d65 Mon Sep 17 00:00:00 2001 From: Craig Warren Date: Fri, 5 Jul 2019 10:57:22 +0100 Subject: [PATCH] Changes to package structure and config module. Added ability to choose E or H fields in snapshots. --- gprMax/_version.py | 2 +- gprMax/config.pxd | 18 +- gprMax/config.py | 69 +-- gprMax/cuda/fields_updates.py | 106 ++-- gprMax/cuda/source_updates.py | 2 +- gprMax/cython/fields_updates.pyx | 412 -------------- gprMax/cython/fields_updates_dispersive.pyx | 514 ++++++++++++++++++ gprMax/cython/fields_updates_normal.pyx | 172 ++++++ gprMax/cython/fractals_generate.pyx | 8 +- .../cython/pml_updates_electric_HORIPML.pyx | 338 ++++++------ gprMax/cython/pml_updates_electric_MRIPML.pyx | 340 ++++++------ .../cython/pml_updates_magnetic_HORIPML.pyx | 338 ++++++------ gprMax/cython/pml_updates_magnetic_MRIPML.pyx | 338 ++++++------ gprMax/cython/snapshots.pyx | 42 +- gprMax/exceptions.py | 4 +- gprMax/fields_outputs.py | 17 +- gprMax/fractals.py | 63 ++- gprMax/geometry_outputs.py | 15 +- gprMax/gprMax.py | 53 +- gprMax/grid.py | 90 ++- gprMax/input_cmds_file.py | 7 +- gprMax/input_cmds_geometry.py | 81 ++- gprMax/input_cmds_multiuse.py | 120 ++-- gprMax/input_cmds_singleuse.py | 76 ++- gprMax/materials.py | 270 +++++---- gprMax/model_build_run.py | 335 ++++++------ gprMax/pml.py | 225 +++++--- gprMax/receivers.py | 33 +- gprMax/snapshots.py | 126 +++-- gprMax/sources.py | 149 +---- gprMax/utilities.py | 11 +- gprMax/waveforms.py | 2 - tests/test_models.py | 28 +- 33 files changed, 2369 insertions(+), 2035 deletions(-) delete mode 100644 gprMax/cython/fields_updates.pyx create mode 100644 gprMax/cython/fields_updates_dispersive.pyx create mode 100644 gprMax/cython/fields_updates_normal.pyx diff --git a/gprMax/_version.py b/gprMax/_version.py index ff74d893..e261f36e 100644 --- a/gprMax/_version.py +++ b/gprMax/_version.py @@ -1,4 +1,4 @@ # This is where the version number is set and read by setup.py and conf.py (for the docs) -__version__ = '3.1.5' +__version__ = '3.1.6b0' codename = 'Big Smoke' diff --git a/gprMax/config.pxd b/gprMax/config.pxd index eeacfbec..35f85fb5 100644 --- a/gprMax/config.pxd +++ b/gprMax/config.pxd @@ -20,10 +20,16 @@ import numpy as np cimport numpy as np -ctypedef fused floattype_t: - np.float32_t - np.float64_t +ctypedef fused float_or_double: + float + double -ctypedef fused complextype_t: - np.complex64_t - np.complex128_t +ctypedef fused float_or_double_complex: + float complex + double complex + +ctypedef fused real_or_complex: + float + double + float complex + double complex diff --git a/gprMax/config.py b/gprMax/config.py index 34ed5179..2809c3d0 100644 --- a/gprMax/config.py +++ b/gprMax/config.py @@ -16,63 +16,64 @@ # You should have received a copy of the GNU General Public License # along with gprMax. If not, see . +import os + +import cython import numpy as np from scipy.constants import c from scipy.constants import mu_0 as m0 from scipy.constants import epsilon_0 as e0 -from gprMax.utilities import get_host_info +from .utilities import get_host_info # Impedance of free space (Ohms) z0 = np.sqrt(m0 / e0) -# Setting whether messages and progress bars are printed -messages = True -progressbars = messages +# General setting for the simulation +# inputfilepath: path to inputfile location +# outputfilepath: path to outputfile location +# messages: whether to print all messages as output to stdout or not +# progressbars: whether to show progress bars on stdoout or not +# mode: 2D TMx, 2D TMy, 2D TMz, or 3D +# cpu, cuda, opencl: solver type +general = {'inputfilepath': '', 'outputfilepath': '', 'messages': True, + 'progressbars': True, 'mode': None, 'cpu': True, 'cuda': False, 'opencl': False} # Store information about host machine hostinfo = get_host_info() -# Store information about any GPUs as a list of GPU objects -gpus = None - -# Copy snapshot data from GPU to CPU during simulation -# N.B. This will happen if the requested snapshots are too large to fit -# on the memory of the GPU. If True this will slow performance significantly -snapsgpu2cpu = False +# Store information for CUDA solver type +# gpus: information about any GPUs as a list of GPU objects +# snapsgpu2cpu: copy snapshot data from GPU to CPU during simulation +# N.B. This will happen if the requested snapshots are too large to fit +# on the memory of the GPU. If True this will slow performance significantly +cuda = {'gpus': None, 'snapsgpu2cpu': False} # Numerical dispersion analysis parameters -# Threshold (dB) down from maximum power (0dB) of main frequency used -# to calculate highest frequency for numerical dispersion analysis -# Maximum allowable percentage physical phase-velocity phase error -# Minimum grid sampling of smallest wavelength for physical wave propagation +# highestfreqthres: threshold (dB) down from maximum power (0dB) of main frequency used +# to calculate highest frequency for numerical dispersion analysis +# maxnumericaldisp: maximum allowable percentage physical phase-velocity phase error +# mingridsampling: minimum grid sampling of smallest wavelength for physical wave propagation numdispersion = {'highestfreqthres': 40, 'maxnumericaldisp': 2, 'mingridsampling': 3} -# Simulation mode - 2D TMx, 2D TMy, 2D TMz, or 3D -mode = None +# Materials +# maxpoles: Maximum number of dispersive material poles in a model +materials = {'maxpoles': 0, 'dispersivedtype': None, 'dispersiveCdtype': None} # Data types # Solid and ID arrays use 32-bit integers (0 to 4294967295) # Rigid arrays use 8-bit integers (the smallest available type to store true/false) -# Fractal and dispersive coefficient arrays use complex numbers (complextype) +# Fractal and dispersive coefficient arrays use complex numbers (complex) # which are represented as two floats -# Main field arrays use floats (floattype) and complex numbers (complextype) - -# Precision of floattype and complextype: single or double +# Main field arrays use floats (float_or_double) and complex numbers (complex) +# Precision of float_or_double and complex: single or double for numpy and C (CUDA) arrays precision = 'single' if precision == 'single': - # For numpy arrays - floattype = np.float32 - complextype = np.complex64 - # For C (CUDA) arrays - cudafloattype = 'float' - cudacomplextype = 'pycuda::complex' - + dtypes = {'float_or_double': np.float32, 'complex': np.complex64, + 'cython_float_or_double': cython.float, 'cython_complex': cython.floatcomplex, + 'C_float_or_double': 'float', 'C_complex': 'pycuda::complex'} elif precision == 'double': - # For numpy arrays - floattype = np.float64 - complextype = np.complex128 - # For C (CUDA) arrays - cudafloattype = 'double' - cudacomplextype = 'pycuda::complex' + dtypes = {'float_or_double': np.float64, 'complex': np.complex128, + 'cython_float_or_double': cython.double, 'cython_complex': cython.doublecomplex, + 'C_float_or_double': 'double', 'C_complex': 'pycuda::complex'} diff --git a/gprMax/cuda/fields_updates.py b/gprMax/cuda/fields_updates.py index 6ce88155..26cf94b6 100644 --- a/gprMax/cuda/fields_updates.py +++ b/gprMax/cuda/fields_updates.py @@ -18,7 +18,7 @@ from string import Template -kernels_template_fields = Template(""" +kernel_template_fields = Template(""" #include @@ -33,11 +33,12 @@ kernels_template_fields = Template(""" __device__ __constant__ $REAL updatecoeffsE[$N_updatecoeffsE]; __device__ __constant__ $REAL updatecoeffsH[$N_updatecoeffsH]; -///////////////////////////////////////////////// -// Electric field updates - standard materials // -///////////////////////////////////////////////// -__global__ void update_e(int NX, int NY, int NZ, const unsigned int* __restrict__ ID, $REAL *Ex, $REAL *Ey, $REAL *Ez, const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz) { +/////////////////////////////////////////////// +// Electric field updates - normal materials // +/////////////////////////////////////////////// + +__global__ void update_electric(int NX, int NY, int NZ, const unsigned int* __restrict__ ID, $REAL *Ex, $REAL *Ey, $REAL *Ez, const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz) { // This function updates electric field values. // @@ -77,11 +78,57 @@ __global__ void update_e(int NX, int NY, int NZ, const unsigned int* __restrict_ } } + +//////////////////////////// +// Magnetic field updates // +//////////////////////////// + +__global__ void update_magnetic(int NX, int NY, int NZ, const unsigned int* __restrict__ ID, $REAL *Hx, $REAL *Hy, $REAL *Hz, const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez) { + + // This function updates magnetic field values. + // + // Args: + // NX, NY, NZ: Number of cells of the model domain + // ID, E, H: Access to ID and field component arrays + + // Obtain the linear index corresponding to the current thread + int idx = blockIdx.x * blockDim.x + threadIdx.x; + + // Convert the linear index to subscripts for 3D field arrays + int i = idx / ($NY_FIELDS * $NZ_FIELDS); + int j = (idx % ($NY_FIELDS * $NZ_FIELDS)) / $NZ_FIELDS; + int k = (idx % ($NY_FIELDS * $NZ_FIELDS)) % $NZ_FIELDS; + + // Convert the linear index to subscripts for 4D material ID array + int i_ID = (idx % ($NX_ID * $NY_ID * $NZ_ID)) / ($NY_ID * $NZ_ID); + int j_ID = ((idx % ($NX_ID * $NY_ID * $NZ_ID)) % ($NY_ID * $NZ_ID)) / $NZ_ID; + int k_ID = ((idx % ($NX_ID * $NY_ID * $NZ_ID)) % ($NY_ID * $NZ_ID)) % $NZ_ID; + + // Hx component + if (NX != 1 && i > 0 && i < NX && j >= 0 && j < NY && k >= 0 && k < NZ) { + int materialHx = ID[INDEX4D_ID(3,i_ID,j_ID,k_ID)]; + Hx[INDEX3D_FIELDS(i,j,k)] = updatecoeffsH[INDEX2D_MAT(materialHx,0)] * Hx[INDEX3D_FIELDS(i,j,k)] - updatecoeffsH[INDEX2D_MAT(materialHx,2)] * (Ez[INDEX3D_FIELDS(i,j+1,k)] - Ez[INDEX3D_FIELDS(i,j,k)]) + updatecoeffsH[INDEX2D_MAT(materialHx,3)] * (Ey[INDEX3D_FIELDS(i,j,k+1)] - Ey[INDEX3D_FIELDS(i,j,k)]); + } + + // Hy component + if (NY != 1 && i >= 0 && i < NX && j > 0 && j < NY && k >= 0 && k < NZ) { + int materialHy = ID[INDEX4D_ID(4,i_ID,j_ID,k_ID)]; + Hy[INDEX3D_FIELDS(i,j,k)] = updatecoeffsH[INDEX2D_MAT(materialHy,0)] * Hy[INDEX3D_FIELDS(i,j,k)] - updatecoeffsH[INDEX2D_MAT(materialHy,3)] * (Ex[INDEX3D_FIELDS(i,j,k+1)] - Ex[INDEX3D_FIELDS(i,j,k)]) + updatecoeffsH[INDEX2D_MAT(materialHy,1)] * (Ez[INDEX3D_FIELDS(i+1,j,k)] - Ez[INDEX3D_FIELDS(i,j,k)]); + } + + // Hz component + if (NZ != 1 && i >= 0 && i < NX && j >= 0 && j < NY && k > 0 && k < NZ) { + int materialHz = ID[INDEX4D_ID(5,i_ID,j_ID,k_ID)]; + Hz[INDEX3D_FIELDS(i,j,k)] = updatecoeffsH[INDEX2D_MAT(materialHz,0)] * Hz[INDEX3D_FIELDS(i,j,k)] - updatecoeffsH[INDEX2D_MAT(materialHz,1)] * (Ey[INDEX3D_FIELDS(i+1,j,k)] - Ey[INDEX3D_FIELDS(i,j,k)]) + updatecoeffsH[INDEX2D_MAT(materialHz,2)] * (Ex[INDEX3D_FIELDS(i,j+1,k)] - Ex[INDEX3D_FIELDS(i,j,k)]); + } +} + + /////////////////////////////////////////////////// // Electric field updates - dispersive materials // /////////////////////////////////////////////////// -__global__ void update_e_dispersive_A(int NX, int NY, int NZ, int MAXPOLES, const $COMPLEX* __restrict__ updatecoeffsdispersive, $COMPLEX *Tx, $COMPLEX *Ty, $COMPLEX *Tz, const unsigned int* __restrict__ ID, $REAL *Ex, $REAL *Ey, $REAL *Ez, const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz) { +__global__ void update_electric_dispersive_A(int NX, int NY, int NZ, int MAXPOLES, const $REAL_OR_COMPLEX* __restrict__ updatecoeffsdispersive, $REAL_OR_COMPLEX *Tx, $REAL_OR_COMPLEX *Ty, $REAL_OR_COMPLEX *Tz, const unsigned int* __restrict__ ID, $REAL *Ex, $REAL *Ey, $REAL *Ez, const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz) { // This function is part A of updates to electric field values when dispersive materials (with multiple poles) are present. // @@ -142,7 +189,7 @@ __global__ void update_e_dispersive_A(int NX, int NY, int NZ, int MAXPOLES, cons } } -__global__ void update_e_dispersive_B(int NX, int NY, int NZ, int MAXPOLES, const $COMPLEX* __restrict__ updatecoeffsdispersive, $COMPLEX *Tx, $COMPLEX *Ty, $COMPLEX *Tz, const unsigned int* __restrict__ ID, const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez) { +__global__ void update_electric_dispersive_B(int NX, int NY, int NZ, int MAXPOLES, const $REAL_OR_COMPLEX* __restrict__ updatecoeffsdispersive, $REAL_OR_COMPLEX *Tx, $REAL_OR_COMPLEX *Ty, $REAL_OR_COMPLEX *Tz, const unsigned int* __restrict__ ID, const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez) { // This function is part B which updates the dispersive field arrays when dispersive materials (with multiple poles) are present. // @@ -194,49 +241,4 @@ __global__ void update_e_dispersive_B(int NX, int NY, int NZ, int MAXPOLES, cons } } - -//////////////////////////// -// Magnetic field updates // -//////////////////////////// - -__global__ void update_h(int NX, int NY, int NZ, const unsigned int* __restrict__ ID, $REAL *Hx, $REAL *Hy, $REAL *Hz, const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez) { - - // This function updates magnetic field values. - // - // Args: - // NX, NY, NZ: Number of cells of the model domain - // ID, E, H: Access to ID and field component arrays - - // Obtain the linear index corresponding to the current thread - int idx = blockIdx.x * blockDim.x + threadIdx.x; - - // Convert the linear index to subscripts for 3D field arrays - int i = idx / ($NY_FIELDS * $NZ_FIELDS); - int j = (idx % ($NY_FIELDS * $NZ_FIELDS)) / $NZ_FIELDS; - int k = (idx % ($NY_FIELDS * $NZ_FIELDS)) % $NZ_FIELDS; - - // Convert the linear index to subscripts for 4D material ID array - int i_ID = (idx % ($NX_ID * $NY_ID * $NZ_ID)) / ($NY_ID * $NZ_ID); - int j_ID = ((idx % ($NX_ID * $NY_ID * $NZ_ID)) % ($NY_ID * $NZ_ID)) / $NZ_ID; - int k_ID = ((idx % ($NX_ID * $NY_ID * $NZ_ID)) % ($NY_ID * $NZ_ID)) % $NZ_ID; - - // Hx component - if (NX != 1 && i > 0 && i < NX && j >= 0 && j < NY && k >= 0 && k < NZ) { - int materialHx = ID[INDEX4D_ID(3,i_ID,j_ID,k_ID)]; - Hx[INDEX3D_FIELDS(i,j,k)] = updatecoeffsH[INDEX2D_MAT(materialHx,0)] * Hx[INDEX3D_FIELDS(i,j,k)] - updatecoeffsH[INDEX2D_MAT(materialHx,2)] * (Ez[INDEX3D_FIELDS(i,j+1,k)] - Ez[INDEX3D_FIELDS(i,j,k)]) + updatecoeffsH[INDEX2D_MAT(materialHx,3)] * (Ey[INDEX3D_FIELDS(i,j,k+1)] - Ey[INDEX3D_FIELDS(i,j,k)]); - } - - // Hy component - if (NY != 1 && i >= 0 && i < NX && j > 0 && j < NY && k >= 0 && k < NZ) { - int materialHy = ID[INDEX4D_ID(4,i_ID,j_ID,k_ID)]; - Hy[INDEX3D_FIELDS(i,j,k)] = updatecoeffsH[INDEX2D_MAT(materialHy,0)] * Hy[INDEX3D_FIELDS(i,j,k)] - updatecoeffsH[INDEX2D_MAT(materialHy,3)] * (Ex[INDEX3D_FIELDS(i,j,k+1)] - Ex[INDEX3D_FIELDS(i,j,k)]) + updatecoeffsH[INDEX2D_MAT(materialHy,1)] * (Ez[INDEX3D_FIELDS(i+1,j,k)] - Ez[INDEX3D_FIELDS(i,j,k)]); - } - - // Hz component - if (NZ != 1 && i >= 0 && i < NX && j >= 0 && j < NY && k > 0 && k < NZ) { - int materialHz = ID[INDEX4D_ID(5,i_ID,j_ID,k_ID)]; - Hz[INDEX3D_FIELDS(i,j,k)] = updatecoeffsH[INDEX2D_MAT(materialHz,0)] * Hz[INDEX3D_FIELDS(i,j,k)] - updatecoeffsH[INDEX2D_MAT(materialHz,1)] * (Ey[INDEX3D_FIELDS(i+1,j,k)] - Ey[INDEX3D_FIELDS(i,j,k)]) + updatecoeffsH[INDEX2D_MAT(materialHz,2)] * (Ex[INDEX3D_FIELDS(i,j+1,k)] - Ex[INDEX3D_FIELDS(i,j,k)]); - } -} - """) diff --git a/gprMax/cuda/source_updates.py b/gprMax/cuda/source_updates.py index a2a8448e..71b5fcc8 100644 --- a/gprMax/cuda/source_updates.py +++ b/gprMax/cuda/source_updates.py @@ -18,7 +18,7 @@ from string import Template -kernels_template_sources = Template(""" +kernel_template_sources = Template(""" // Macros for converting subscripts to linear index: #define INDEX2D_MAT(m, n) (m)*($NY_MATCOEFFS)+(n) diff --git a/gprMax/cython/fields_updates.pyx b/gprMax/cython/fields_updates.pyx deleted file mode 100644 index 99f5cd57..00000000 --- a/gprMax/cython/fields_updates.pyx +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (C) 2015-2019: The University of Edinburgh -# Authors: Craig Warren and Antonis Giannopoulos -# -# 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 . - -import numpy as np -cimport numpy as np -from cython.parallel import prange - -from gprMax.config cimport floattype_t -from gprMax.config cimport complextype_t - - -############################################### -# Electric field updates - standard materials # -############################################### -cpdef void update_electric( - int nx, - int ny, - int nz, - int nthreads, - floattype_t[:, ::1] updatecoeffsE, - np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz - ): - """This function updates the electric field components. - - Args: - nx, ny, nz (int): Grid size in cells - nthreads (int): Number of threads to use - updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays - """ - - cdef Py_ssize_t i, j, k - cdef int materialEx, materialEy, materialEz - - # 2D - Ex component - if nx == 1: - for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(1, nz): - materialEx = ID[0, i, j, k] - Ex[i, j, k] = updatecoeffsE[materialEx, 0] * Ex[i, j, k] + updatecoeffsE[materialEx, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[materialEx, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - - # 2D - Ey component - elif ny == 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(0, ny): - for k in range(1, nz): - materialEy = ID[1, i, j, k] - Ey[i, j, k] = updatecoeffsE[materialEy, 0] * Ey[i, j, k] + updatecoeffsE[materialEy, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[materialEy, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - - # 2D - Ez component - elif nz == 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(0, nz): - materialEz = ID[2, i, j, k] - Ez[i, j, k] = updatecoeffsE[materialEz, 0] * Ez[i, j, k] + updatecoeffsE[materialEz, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[materialEz, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - - # 3D - else: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(1, nz): - materialEx = ID[0, i, j, k] - materialEy = ID[1, i, j, k] - materialEz = ID[2, i, j, k] - Ex[i, j, k] = updatecoeffsE[materialEx, 0] * Ex[i, j, k] + updatecoeffsE[materialEx, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[materialEx, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - Ey[i, j, k] = updatecoeffsE[materialEy, 0] * Ey[i, j, k] + updatecoeffsE[materialEy, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[materialEy, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - Ez[i, j, k] = updatecoeffsE[materialEz, 0] * Ez[i, j, k] + updatecoeffsE[materialEz, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[materialEz, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - - # Ex components at i = 0 - for j in prange(1, ny, nogil=True, schedule='static', num_threads=nthreads): - for k in range(1, nz): - materialEx = ID[0, 0, j, k] - Ex[0, j, k] = updatecoeffsE[materialEx, 0] * Ex[0, j, k] + updatecoeffsE[materialEx, 2] * (Hz[0, j, k] - Hz[0, j - 1, k]) - updatecoeffsE[materialEx, 3] * (Hy[0, j, k] - Hy[0, j, k - 1]) - - # Ey components at j = 0 - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for k in range(1, nz): - materialEy = ID[1, i, 0, k] - Ey[i, 0, k] = updatecoeffsE[materialEy, 0] * Ey[i, 0, k] + updatecoeffsE[materialEy, 3] * (Hx[i, 0, k] - Hx[i, 0, k - 1]) - updatecoeffsE[materialEy, 1] * (Hz[i, 0, k] - Hz[i - 1, 0, k]) - - # Ez components at k = 0 - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - materialEz = ID[2, i, j, 0] - Ez[i, j, 0] = updatecoeffsE[materialEz, 0] * Ez[i, j, 0] + updatecoeffsE[materialEz, 1] * (Hy[i, j, 0] - Hy[i - 1, j, 0]) - updatecoeffsE[materialEz, 2] * (Hx[i, j, 0] - Hx[i, j - 1, 0]) - - -################################################# -# Electric field updates - dispersive materials # -################################################# -cpdef void update_electric_dispersive_multipole_A( - int nx, - int ny, - int nz, - int nthreads, - int maxpoles, - floattype_t[:, ::1] updatecoeffsE, - complextype_t[:, ::1] updatecoeffsdispersive, - np.uint32_t[:, :, :, ::1] ID, - complextype_t[:, :, :, ::1] Tx, - complextype_t[:, :, :, ::1] Ty, - complextype_t[:, :, :, ::1] Tz, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz - ): - """This function updates the electric field components when dispersive materials (with multiple poles) are present. - - Args: - nx, ny, nz (int): Grid size in cells - maxpoles (int): Maximum number of poles - nthreads (int): Number of threads to use - updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays - """ - - cdef Py_ssize_t i, j, k, pole - cdef int material - cdef float phi = 0 - - # Ex component - if ny != 1 or nz != 1: - for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(1, nz): - material = ID[0, i, j, k] - phi = 0 - for pole in range(maxpoles): - phi = phi + updatecoeffsdispersive[material, pole * 3].real * Tx[pole, i, j, k].real - Tx[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tx[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k] - Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi - - # Ey component - if nx != 1 or nz != 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(0, ny): - for k in range(1, nz): - material = ID[1, i, j, k] - phi = 0 - for pole in range(maxpoles): - phi = phi + updatecoeffsdispersive[material, pole * 3].real * Ty[pole, i, j, k].real - Ty[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Ty[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k] - Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi - - # Ez component - if nx != 1 or ny != 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(0, nz): - material = ID[2, i, j, k] - phi = 0 - for pole in range(maxpoles): - phi = phi + updatecoeffsdispersive[material, pole * 3].real * Tz[pole, i, j, k].real - Tz[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tz[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k] - Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi - - - -cpdef void update_electric_dispersive_multipole_B( - int nx, - int ny, - int nz, - int nthreads, - int maxpoles, - complextype_t[:, ::1] updatecoeffsdispersive, - np.uint32_t[:, :, :, ::1] ID, - complextype_t[:, :, :, ::1] Tx, - complextype_t[:, :, :, ::1] Ty, - complextype_t[:, :, :, ::1] Tz, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez - ): - """This function updates a temporary dispersive material array when disperisive materials (with multiple poles) are present. - - Args: - nx, ny, nz (int): Grid size in cells - maxpoles (int): Maximum number of poles - nthreads (int): Number of threads to use - updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays - """ - - cdef Py_ssize_t i, j, k, pole - cdef int material - - # Ex component - if ny != 1 or nz != 1: - for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(1, nz): - material = ID[0, i, j, k] - for pole in range(maxpoles): - Tx[pole, i, j, k] = Tx[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k] - - # Ey component - if nx != 1 or nz != 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(0, ny): - for k in range(1, nz): - material = ID[1, i, j, k] - for pole in range(maxpoles): - Ty[pole, i, j, k] = Ty[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k] - - # Ez component - if nx != 1 or ny != 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(0, nz): - material = ID[2, i, j, k] - for pole in range(maxpoles): - Tz[pole, i, j, k] = Tz[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k] - - -cpdef void update_electric_dispersive_1pole_A( - int nx, - int ny, - int nz, - int nthreads, - floattype_t[:, ::1] updatecoeffsE, - complextype_t[:, ::1] updatecoeffsdispersive, - np.uint32_t[:, :, :, ::1] ID, - complextype_t[:, :, :, ::1] Tx, - complextype_t[:, :, :, ::1] Ty, - complextype_t[:, :, :, ::1] Tz, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz - ): - """This function updates the electric field components when dispersive materials (with 1 pole) are present. - - Args: - nx, ny, nz (int): Grid size in cells - nthreads (int): Number of threads to use - updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays - """ - - cdef Py_ssize_t i, j, k - cdef int material - cdef float phi = 0 - - # Ex component - if ny != 1 or nz != 1: - for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(1, nz): - material = ID[0, i, j, k] - phi = updatecoeffsdispersive[material, 0].real * Tx[0, i, j, k].real - Tx[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tx[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ex[i, j, k] - Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi - - # Ey component - if nx != 1 or nz != 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(0, ny): - for k in range(1, nz): - material = ID[1, i, j, k] - phi = updatecoeffsdispersive[material, 0].real * Ty[0, i, j, k].real - Ty[0, i, j, k] = updatecoeffsdispersive[material, 1] * Ty[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ey[i, j, k] - Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi - - # Ez component - if nx != 1 or ny != 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(0, nz): - material = ID[2, i, j, k] - phi = updatecoeffsdispersive[material, 0].real * Tz[0, i, j, k].real - Tz[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tz[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ez[i, j, k] - Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi - - -cpdef void update_electric_dispersive_1pole_B( - int nx, - int ny, - int nz, - int nthreads, - complextype_t[:, ::1] updatecoeffsdispersive, - np.uint32_t[:, :, :, ::1] ID, - complextype_t[:, :, :, ::1] Tx, - complextype_t[:, :, :, ::1] Ty, - complextype_t[:, :, :, ::1] Tz, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez - ): - """This function updates a temporary dispersive material array when disperisive materials (with 1 pole) are present. - - Args: - nx, ny, nz (int): Grid size in cells - nthreads (int): Number of threads to use - updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays - """ - - cdef Py_ssize_t i, j, k - cdef int material - - # Ex component - if ny != 1 or nz != 1: - for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(1, nz): - material = ID[0, i, j, k] - Tx[0, i, j, k] = Tx[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ex[i, j, k] - - # Ey component - if nx != 1 or nz != 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(0, ny): - for k in range(1, nz): - material = ID[1, i, j, k] - Ty[0, i, j, k] = Ty[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ey[i, j, k] - - # Ez component - if nx != 1 or ny != 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(0, nz): - material = ID[2, i, j, k] - Tz[0, i, j, k] = Tz[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ez[i, j, k] - - -########################## -# Magnetic field updates # -########################## -cpdef void update_magnetic( - int nx, - int ny, - int nz, - int nthreads, - floattype_t[:, ::1] updatecoeffsH, - np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz - ): - """This function updates the magnetic field components. - - Args: - nx, ny, nz (int): Grid size in cells - nthreads (int): Number of threads to use - updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays - """ - - cdef Py_ssize_t i, j, k - cdef int materialHx, materialHy, materialHz - - # 2D - if nx == 1 or ny == 1 or nz == 1: - # Hx component - if ny == 1 or nz == 1: - for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(0, ny): - for k in range(0, nz): - materialHx = ID[3, i, j, k] - Hx[i, j, k] = updatecoeffsH[materialHx, 0] * Hx[i, j, k] - updatecoeffsH[materialHx, 2] * (Ez[i, j + 1, k] - Ez[i, j, k]) + updatecoeffsH[materialHx, 3] * (Ey[i, j, k + 1] - Ey[i, j, k]) - - # Hy component - if nx == 1 or nz == 1: - for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(1, ny): - for k in range(0, nz): - materialHy = ID[4, i, j, k] - Hy[i, j, k] = updatecoeffsH[materialHy, 0] * Hy[i, j, k] - updatecoeffsH[materialHy, 3] * (Ex[i, j, k + 1] - Ex[i, j, k]) + updatecoeffsH[materialHy, 1] * (Ez[i + 1, j, k] - Ez[i, j, k]) - - # Hz component - if nx == 1 or ny == 1: - for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(0, ny): - for k in range(1, nz): - materialHz = ID[5, i, j, k] - Hz[i, j, k] = updatecoeffsH[materialHz, 0] * Hz[i, j, k] - updatecoeffsH[materialHz, 1] * (Ey[i + 1, j, k] - Ey[i, j, k]) + updatecoeffsH[materialHz, 2] * (Ex[i, j + 1, k] - Ex[i, j, k]) - # 3D - else: - for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): - for j in range(0, ny): - for k in range(0, nz): - materialHx = ID[3, i + 1, j, k] - materialHy = ID[4, i, j + 1, k] - materialHz = ID[5, i, j, k + 1] - Hx[i + 1, j, k] = updatecoeffsH[materialHx, 0] * Hx[i + 1, j, k] - updatecoeffsH[materialHx, 2] * (Ez[i + 1, j + 1, k] - Ez[i + 1, j, k]) + updatecoeffsH[materialHx, 3] * (Ey[i + 1, j, k + 1] - Ey[i + 1, j, k]) - Hy[i, j + 1, k] = updatecoeffsH[materialHy, 0] * Hy[i, j + 1, k] - updatecoeffsH[materialHy, 3] * (Ex[i, j + 1, k + 1] - Ex[i, j + 1, k]) + updatecoeffsH[materialHy, 1] * (Ez[i + 1, j + 1, k] - Ez[i, j + 1, k]) - Hz[i, j, k + 1] = updatecoeffsH[materialHz, 0] * Hz[i, j, k + 1] - updatecoeffsH[materialHz, 1] * (Ey[i + 1, j, k + 1] - Ey[i, j, k + 1]) + updatecoeffsH[materialHz, 2] * (Ex[i, j + 1, k + 1] - Ex[i, j, k + 1]) diff --git a/gprMax/cython/fields_updates_dispersive.pyx b/gprMax/cython/fields_updates_dispersive.pyx new file mode 100644 index 00000000..f3da334c --- /dev/null +++ b/gprMax/cython/fields_updates_dispersive.pyx @@ -0,0 +1,514 @@ +# Copyright (C) 2015-2019: The University of Edinburgh +# Authors: Craig Warren and Antonis Giannopoulos +# +# 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 . + +import numpy as np +cimport numpy as np +from cython.parallel import prange + +from gprMax.config cimport float_or_double +from gprMax.config cimport real_or_complex + +cdef double mycreal(double complex dc) nogil: + cdef double complex* dcptr = &dc + return (dcptr)[0] + + +######################################################### +# Electric field updates - dispersive materials - Debye # +######################################################### +cpdef void update_electric_dispersive_debye_multipole_A( + int nx, + int ny, + int nz, + int nthreads, + int maxpoles, + float_or_double[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsdispersive, + np.uint32_t[:, :, :, ::1] ID, + float_or_double[:, :, :, ::1] Tx, + float_or_double[:, :, :, ::1] Ty, + float_or_double[:, :, :, ::1] Tz, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz + ): + """This function updates the electric field components when dispersive materials (with multiple poles) are present. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + maxpoles (int): Maximum number of poles + updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k, pole + cdef int material + cdef float phi = 0 + + # Ex component + if ny != 1 or nz != 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + material = ID[0, i, j, k] + phi = 0 + for pole in range(maxpoles): + phi = phi + updatecoeffsdispersive[material, pole * 3] * Tx[pole, i, j, k] + Tx[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tx[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k] + Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi + + # Ey component + if nx != 1 or nz != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + material = ID[1, i, j, k] + phi = 0 + for pole in range(maxpoles): + phi = phi + updatecoeffsdispersive[material, pole * 3] * Ty[pole, i, j, k] + Ty[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Ty[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k] + Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi + + # Ez component + if nx != 1 or ny != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + material = ID[2, i, j, k] + phi = 0 + for pole in range(maxpoles): + phi = phi + updatecoeffsdispersive[material, pole * 3] * Tz[pole, i, j, k] + Tz[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tz[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k] + Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi + + + +cpdef void update_electric_dispersive_debye_multipole_B( + int nx, + int ny, + int nz, + int nthreads, + int maxpoles, + float_or_double[:, ::1] updatecoeffsdispersive, + np.uint32_t[:, :, :, ::1] ID, + float_or_double[:, :, :, ::1] Tx, + float_or_double[:, :, :, ::1] Ty, + float_or_double[:, :, :, ::1] Tz, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez + ): + """This function updates a temporary dispersive material array when disperisive materials (with multiple poles) are present. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + maxpoles (int): Maximum number of poles + updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k, pole + cdef int material + + # Ex component + if ny != 1 or nz != 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + material = ID[0, i, j, k] + for pole in range(maxpoles): + Tx[pole, i, j, k] = Tx[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k] + + # Ey component + if nx != 1 or nz != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + material = ID[1, i, j, k] + for pole in range(maxpoles): + Ty[pole, i, j, k] = Ty[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k] + + # Ez component + if nx != 1 or ny != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + material = ID[2, i, j, k] + for pole in range(maxpoles): + Tz[pole, i, j, k] = Tz[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k] + + +cpdef void update_electric_dispersive_debye_1pole_A( + int nx, + int ny, + int nz, + int nthreads, + int maxpoles, + float_or_double[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsdispersive, + np.uint32_t[:, :, :, ::1] ID, + float_or_double[:, :, :, ::1] Tx, + float_or_double[:, :, :, ::1] Ty, + float_or_double[:, :, :, ::1] Tz, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz + ): + """This function updates the electric field components when dispersive materials (with 1 pole) are present. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + maxpoles (int): Maximum number of poles + updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k + cdef int material + cdef float phi = 0 + + # Ex component + if ny != 1 or nz != 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + material = ID[0, i, j, k] + phi = updatecoeffsdispersive[material, 0] * Tx[0, i, j, k] + Tx[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tx[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ex[i, j, k] + Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi + + # Ey component + if nx != 1 or nz != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + material = ID[1, i, j, k] + phi = updatecoeffsdispersive[material, 0] * Ty[0, i, j, k] + Ty[0, i, j, k] = updatecoeffsdispersive[material, 1] * Ty[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ey[i, j, k] + Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi + + # Ez component + if nx != 1 or ny != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + material = ID[2, i, j, k] + phi = updatecoeffsdispersive[material, 0] * Tz[0, i, j, k] + Tz[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tz[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ez[i, j, k] + Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi + + +cpdef void update_electric_dispersive_debye_1pole_B( + int nx, + int ny, + int nz, + int nthreads, + int maxpoles, + float_or_double[:, ::1] updatecoeffsdispersive, + np.uint32_t[:, :, :, ::1] ID, + float_or_double[:, :, :, ::1] Tx, + float_or_double[:, :, :, ::1] Ty, + float_or_double[:, :, :, ::1] Tz, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez + ): + """This function updates a temporary dispersive material array when disperisive materials (with 1 pole) are present. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + maxpoles (int): Maximum number of poles + updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k + cdef int material + + # Ex component + if ny != 1 or nz != 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + material = ID[0, i, j, k] + Tx[0, i, j, k] = Tx[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ex[i, j, k] + + # Ey component + if nx != 1 or nz != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + material = ID[1, i, j, k] + Ty[0, i, j, k] = Ty[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ey[i, j, k] + + # Ez component + if nx != 1 or ny != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + material = ID[2, i, j, k] + Tz[0, i, j, k] = Tz[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ez[i, j, k] + + +################################################################# +# Electric field updates - dispersive materials - Drude, Lorenz # +################################################################# +cpdef void update_electric_dispersive_multipole_A( + int nx, + int ny, + int nz, + int nthreads, + int maxpoles, + float_or_double[:, ::1] updatecoeffsE, + real_or_complex[:, ::1] updatecoeffsdispersive, + np.uint32_t[:, :, :, ::1] ID, + real_or_complex[:, :, :, ::1] Tx, + real_or_complex[:, :, :, ::1] Ty, + real_or_complex[:, :, :, ::1] Tz, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz + ): + """This function updates the electric field components when dispersive materials (with multiple poles) are present. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + maxpoles (int): Maximum number of poles + updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k, pole + cdef int material + cdef float phi + + # Ex component + if ny != 1 or nz != 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + material = ID[0, i, j, k] + phi = 0 + for pole in range(maxpoles): + phi = phi + mycreal(updatecoeffsdispersive[material, pole * 3]) * mycreal(Tx[pole, i, j, k]) + Tx[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tx[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k] + Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi + + # Ey component + if nx != 1 or nz != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + material = ID[1, i, j, k] + phi = 0 + for pole in range(maxpoles): + phi = phi + mycreal(updatecoeffsdispersive[material, pole * 3]) * mycreal(Ty[pole, i, j, k]) + Ty[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Ty[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k] + Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi + + # Ez component + if nx != 1 or ny != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + material = ID[2, i, j, k] + phi = 0 + for pole in range(maxpoles): + phi = phi + mycreal(updatecoeffsdispersive[material, pole * 3]) * mycreal(Tz[pole, i, j, k]) + Tz[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tz[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k] + Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi + + + +cpdef void update_electric_dispersive_multipole_B( + int nx, + int ny, + int nz, + int nthreads, + int maxpoles, + real_or_complex[:, ::1] updatecoeffsdispersive, + np.uint32_t[:, :, :, ::1] ID, + real_or_complex[:, :, :, ::1] Tx, + real_or_complex[:, :, :, ::1] Ty, + real_or_complex[:, :, :, ::1] Tz, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez + ): + """This function updates a temporary dispersive material array when disperisive materials (with multiple poles) are present. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + maxpoles (int): Maximum number of poles + updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k, pole + cdef int material + + # Ex component + if ny != 1 or nz != 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + material = ID[0, i, j, k] + for pole in range(maxpoles): + Tx[pole, i, j, k] = Tx[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k] + + # Ey component + if nx != 1 or nz != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + material = ID[1, i, j, k] + for pole in range(maxpoles): + Ty[pole, i, j, k] = Ty[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k] + + # Ez component + if nx != 1 or ny != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + material = ID[2, i, j, k] + for pole in range(maxpoles): + Tz[pole, i, j, k] = Tz[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k] + + +cpdef void update_electric_dispersive_1pole_A( + int nx, + int ny, + int nz, + int nthreads, + int maxpoles, + float_or_double[:, ::1] updatecoeffsE, + real_or_complex[:, ::1] updatecoeffsdispersive, + np.uint32_t[:, :, :, ::1] ID, + real_or_complex[:, :, :, ::1] Tx, + real_or_complex[:, :, :, ::1] Ty, + real_or_complex[:, :, :, ::1] Tz, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz + ): + """This function updates the electric field components when dispersive materials (with 1 pole) are present. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + maxpoles (int): Maximum number of poles + updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k + cdef int material + cdef float phi = 0 + + # Ex component + if ny != 1 or nz != 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + material = ID[0, i, j, k] + phi = mycreal(updatecoeffsdispersive[material, 0]) * mycreal(Tx[0, i, j, k]) + Tx[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tx[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ex[i, j, k] + Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi + + # Ey component + if nx != 1 or nz != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + material = ID[1, i, j, k] + phi = mycreal(updatecoeffsdispersive[material, 0]) * mycreal(Ty[0, i, j, k]) + Ty[0, i, j, k] = updatecoeffsdispersive[material, 1] * Ty[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ey[i, j, k] + Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi + + # Ez component + if nx != 1 or ny != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + material = ID[2, i, j, k] + phi = mycreal(updatecoeffsdispersive[material, 0]) * mycreal(Tz[0, i, j, k]) + Tz[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tz[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ez[i, j, k] + Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi + + +cpdef void update_electric_dispersive_1pole_B( + int nx, + int ny, + int nz, + int nthreads, + int maxpoles, + real_or_complex[:, ::1] updatecoeffsdispersive, + np.uint32_t[:, :, :, ::1] ID, + real_or_complex[:, :, :, ::1] Tx, + real_or_complex[:, :, :, ::1] Ty, + real_or_complex[:, :, :, ::1] Tz, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez + ): + """This function updates a temporary dispersive material array when disperisive materials (with 1 pole) are present. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + maxpoles (int): Maximum number of poles + updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k + cdef int material + + # Ex component + if ny != 1 or nz != 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + material = ID[0, i, j, k] + Tx[0, i, j, k] = Tx[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ex[i, j, k] + + # Ey component + if nx != 1 or nz != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + material = ID[1, i, j, k] + Ty[0, i, j, k] = Ty[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ey[i, j, k] + + # Ez component + if nx != 1 or ny != 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + material = ID[2, i, j, k] + Tz[0, i, j, k] = Tz[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ez[i, j, k] diff --git a/gprMax/cython/fields_updates_normal.pyx b/gprMax/cython/fields_updates_normal.pyx new file mode 100644 index 00000000..ccd65ee1 --- /dev/null +++ b/gprMax/cython/fields_updates_normal.pyx @@ -0,0 +1,172 @@ +cdef float phi# Copyright (C) 2015-2019: The University of Edinburgh +# Authors: Craig Warren and Antonis Giannopoulos +# +# 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 . + +import numpy as np +cimport numpy as np +from cython.parallel import prange + +from gprMax.config cimport float_or_double + + +############################################### +# Electric field updates - standard materials # +############################################### +cpdef void update_electric( + int nx, + int ny, + int nz, + int nthreads, + float_or_double[:, ::1] updatecoeffsE, + np.uint32_t[:, :, :, ::1] ID, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz + ): + """This function updates the electric field components. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k + cdef int materialEx, materialEy, materialEz + + # 2D - Ex component + if nx == 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + materialEx = ID[0, i, j, k] + Ex[i, j, k] = updatecoeffsE[materialEx, 0] * Ex[i, j, k] + updatecoeffsE[materialEx, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[materialEx, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) + + # 2D - Ey component + elif ny == 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + materialEy = ID[1, i, j, k] + Ey[i, j, k] = updatecoeffsE[materialEy, 0] * Ey[i, j, k] + updatecoeffsE[materialEy, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[materialEy, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) + + # 2D - Ez component + elif nz == 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + materialEz = ID[2, i, j, k] + Ez[i, j, k] = updatecoeffsE[materialEz, 0] * Ez[i, j, k] + updatecoeffsE[materialEz, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[materialEz, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) + + # 3D + else: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(1, nz): + materialEx = ID[0, i, j, k] + materialEy = ID[1, i, j, k] + materialEz = ID[2, i, j, k] + Ex[i, j, k] = updatecoeffsE[materialEx, 0] * Ex[i, j, k] + updatecoeffsE[materialEx, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[materialEx, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) + Ey[i, j, k] = updatecoeffsE[materialEy, 0] * Ey[i, j, k] + updatecoeffsE[materialEy, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[materialEy, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) + Ez[i, j, k] = updatecoeffsE[materialEz, 0] * Ez[i, j, k] + updatecoeffsE[materialEz, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[materialEz, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) + + # Ex components at i = 0 + for j in prange(1, ny, nogil=True, schedule='static', num_threads=nthreads): + for k in range(1, nz): + materialEx = ID[0, 0, j, k] + Ex[0, j, k] = updatecoeffsE[materialEx, 0] * Ex[0, j, k] + updatecoeffsE[materialEx, 2] * (Hz[0, j, k] - Hz[0, j - 1, k]) - updatecoeffsE[materialEx, 3] * (Hy[0, j, k] - Hy[0, j, k - 1]) + + # Ey components at j = 0 + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for k in range(1, nz): + materialEy = ID[1, i, 0, k] + Ey[i, 0, k] = updatecoeffsE[materialEy, 0] * Ey[i, 0, k] + updatecoeffsE[materialEy, 3] * (Hx[i, 0, k] - Hx[i, 0, k - 1]) - updatecoeffsE[materialEy, 1] * (Hz[i, 0, k] - Hz[i - 1, 0, k]) + + # Ez components at k = 0 + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + materialEz = ID[2, i, j, 0] + Ez[i, j, 0] = updatecoeffsE[materialEz, 0] * Ez[i, j, 0] + updatecoeffsE[materialEz, 1] * (Hy[i, j, 0] - Hy[i - 1, j, 0]) - updatecoeffsE[materialEz, 2] * (Hx[i, j, 0] - Hx[i, j - 1, 0]) + + +########################## +# Magnetic field updates # +########################## +cpdef void update_magnetic( + int nx, + int ny, + int nz, + int nthreads, + float_or_double[:, ::1] updatecoeffsH, + np.uint32_t[:, :, :, ::1] ID, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz + ): + """This function updates the magnetic field components. + + Args: + nx, ny, nz (int): Grid size in cells + nthreads (int): Number of threads to use + updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays + """ + + cdef Py_ssize_t i, j, k + cdef int materialHx, materialHy, materialHz + + # 2D + if nx == 1 or ny == 1 or nz == 1: + # Hx component + if ny == 1 or nz == 1: + for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(0, nz): + materialHx = ID[3, i, j, k] + Hx[i, j, k] = updatecoeffsH[materialHx, 0] * Hx[i, j, k] - updatecoeffsH[materialHx, 2] * (Ez[i, j + 1, k] - Ez[i, j, k]) + updatecoeffsH[materialHx, 3] * (Ey[i, j, k + 1] - Ey[i, j, k]) + + # Hy component + if nx == 1 or nz == 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(1, ny): + for k in range(0, nz): + materialHy = ID[4, i, j, k] + Hy[i, j, k] = updatecoeffsH[materialHy, 0] * Hy[i, j, k] - updatecoeffsH[materialHy, 3] * (Ex[i, j, k + 1] - Ex[i, j, k]) + updatecoeffsH[materialHy, 1] * (Ez[i + 1, j, k] - Ez[i, j, k]) + + # Hz component + if nx == 1 or ny == 1: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(1, nz): + materialHz = ID[5, i, j, k] + Hz[i, j, k] = updatecoeffsH[materialHz, 0] * Hz[i, j, k] - updatecoeffsH[materialHz, 1] * (Ey[i + 1, j, k] - Ey[i, j, k]) + updatecoeffsH[materialHz, 2] * (Ex[i, j + 1, k] - Ex[i, j, k]) + # 3D + else: + for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): + for j in range(0, ny): + for k in range(0, nz): + materialHx = ID[3, i + 1, j, k] + materialHy = ID[4, i, j + 1, k] + materialHz = ID[5, i, j, k + 1] + Hx[i + 1, j, k] = updatecoeffsH[materialHx, 0] * Hx[i + 1, j, k] - updatecoeffsH[materialHx, 2] * (Ez[i + 1, j + 1, k] - Ez[i + 1, j, k]) + updatecoeffsH[materialHx, 3] * (Ey[i + 1, j, k + 1] - Ey[i + 1, j, k]) + Hy[i, j + 1, k] = updatecoeffsH[materialHy, 0] * Hy[i, j + 1, k] - updatecoeffsH[materialHy, 3] * (Ex[i, j + 1, k + 1] - Ex[i, j + 1, k]) + updatecoeffsH[materialHy, 1] * (Ez[i + 1, j + 1, k] - Ez[i, j + 1, k]) + Hz[i, j, k + 1] = updatecoeffsH[materialHz, 0] * Hz[i, j, k + 1] - updatecoeffsH[materialHz, 1] * (Ey[i + 1, j, k + 1] - Ey[i, j, k + 1]) + updatecoeffsH[materialHz, 2] * (Ex[i, j + 1, k + 1] - Ex[i, j, k + 1]) diff --git a/gprMax/cython/fractals_generate.pyx b/gprMax/cython/fractals_generate.pyx index 2a8a0f22..9bde9b0e 100644 --- a/gprMax/cython/fractals_generate.pyx +++ b/gprMax/cython/fractals_generate.pyx @@ -20,12 +20,12 @@ import numpy as np cimport numpy as np from cython.parallel import prange -from gprMax.config cimport complextype_t +from gprMax.config cimport float_or_double_complex -cpdef void generate_fractal2D(int nx, int ny, int nthreads, int b, np.float64_t[:] weighting, np.float64_t[:] v1, np.complex128_t[:, ::1] A, complextype_t[:, ::1] fractalsurface): +cpdef void generate_fractal2D(int nx, int ny, int nthreads, int b, np.float64_t[:] weighting, np.float64_t[:] v1, np.complex128_t[:, ::1] A, float_or_double_complex[:, ::1] fractalsurface): """This function generates a fractal surface for a 2D array. - + Args: nx, ny (int): Fractal surface size in cells nthreads (int): Number of threads to use @@ -55,7 +55,7 @@ cpdef void generate_fractal2D(int nx, int ny, int nthreads, int b, np.float64_t[ fractalsurface[i, j] = A[i, j] * 1 / (rr**b) -cpdef void generate_fractal3D(int nx, int ny, int nz, int nthreads, int b, np.float64_t[:] weighting, np.float64_t[:] v1, np.complex128_t[:, :, ::1] A, complextype_t[:, :, ::1] fractalvolume): +cpdef void generate_fractal3D(int nx, int ny, int nz, int nthreads, int b, np.float64_t[:] weighting, np.float64_t[:] v1, np.complex128_t[:, :, ::1] A, float_or_double_complex[:, :, ::1] fractalvolume): """This function generates a fractal volume for a 3D array. Args: diff --git a/gprMax/cython/pml_updates_electric_HORIPML.pyx b/gprMax/cython/pml_updates_electric_HORIPML.pyx index 19798333..5bc9ea9e 100644 --- a/gprMax/cython/pml_updates_electric_HORIPML.pyx +++ b/gprMax/cython/pml_updates_electric_HORIPML.pyx @@ -20,7 +20,7 @@ import numpy as np cimport numpy as np from cython.parallel import prange -from gprMax.config cimport floattype_t +from gprMax.config cimport float_or_double cpdef void order1_xminus( @@ -31,20 +31,20 @@ cpdef void order1_xminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ey and Ez field components for the xminus slab. @@ -59,7 +59,7 @@ cpdef void order1_xminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEy, materialEz - cdef floattype_t dx, dHy, dHz, RA01, RB0, RE0, RF0 + cdef float_or_double dx, dHy, dHz, RA01, RB0, RE0, RF0 dx = d nx = xf - xs ny = yf - ys @@ -94,20 +94,20 @@ cpdef void order2_xminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ey and Ez field components for the xminus slab. @@ -122,7 +122,7 @@ cpdef void order2_xminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEy, materialEz - cdef floattype_t dx, dHy, dHz, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 + cdef float_or_double dx, dHy, dHz, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 dx = d nx = xf - xs ny = yf - ys @@ -165,20 +165,20 @@ cpdef void order1_xplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ey and Ez field components for the xplus slab. @@ -193,7 +193,7 @@ cpdef void order1_xplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEy, materialEz - cdef floattype_t dx, dHy, dHz, RA01, RB0, RE0, RF0 + cdef float_or_double dx, dHy, dHz, RA01, RB0, RE0, RF0 dx = d nx = xf - xs ny = yf - ys @@ -228,20 +228,20 @@ cpdef void order2_xplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ey and Ez field components for the xplus slab. @@ -256,7 +256,7 @@ cpdef void order2_xplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEy, materialEz - cdef floattype_t dx, dHy, dHz, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 + cdef float_or_double dx, dHy, dHz, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 dx = d nx = xf - xs ny = yf - ys @@ -299,20 +299,20 @@ cpdef void order1_yminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ez field components for the yminus slab. @@ -327,7 +327,7 @@ cpdef void order1_yminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEz - cdef floattype_t dy, dHx, dHz, RA01, RB0, RE0, RF0 + cdef float_or_double dy, dHx, dHz, RA01, RB0, RE0, RF0 dy = d nx = xf - xs ny = yf - ys @@ -362,20 +362,20 @@ cpdef void order2_yminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ez field components for the yminus slab. @@ -390,7 +390,7 @@ cpdef void order2_yminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEz - cdef floattype_t dy, dHx, dHz, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 + cdef float_or_double dy, dHx, dHz, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 dy = d nx = xf - xs ny = yf - ys @@ -433,20 +433,20 @@ cpdef void order1_yplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ez field components for the yplus slab. @@ -461,7 +461,7 @@ cpdef void order1_yplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEz - cdef floattype_t dy, dHx, dHz, RA01, RB0, RE0, RF0 + cdef float_or_double dy, dHx, dHz, RA01, RB0, RE0, RF0 dy = d nx = xf - xs ny = yf - ys @@ -496,20 +496,20 @@ cpdef void order2_yplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ez field components for the yplus slab. @@ -524,7 +524,7 @@ cpdef void order2_yplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEz - cdef floattype_t dy, dHx, dHz, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 + cdef float_or_double dy, dHx, dHz, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 dy = d nx = xf - xs ny = yf - ys @@ -567,20 +567,20 @@ cpdef void order1_zminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ey field components for the zminus slab. @@ -595,7 +595,7 @@ cpdef void order1_zminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEy - cdef floattype_t dz, dHx, dHy, RA01, RB0, RE0, RF0 + cdef float_or_double dz, dHx, dHy, RA01, RB0, RE0, RF0 dz = d nx = xf - xs ny = yf - ys @@ -630,20 +630,20 @@ cpdef void order2_zminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ey field components for the zminus slab. @@ -658,7 +658,7 @@ cpdef void order2_zminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEy - cdef floattype_t dz, dHx, dHy, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 + cdef float_or_double dz, dHx, dHy, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 dz = d nx = xf - xs ny = yf - ys @@ -701,20 +701,20 @@ cpdef void order1_zplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ey field components for the zplus slab. @@ -729,7 +729,7 @@ cpdef void order1_zplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEy - cdef floattype_t dz, dHx, dHy, RA01, RB0, RE0, RF0 + cdef float_or_double dz, dHx, dHy, RA01, RB0, RE0, RF0 dz = d nx = xf - xs ny = yf - ys @@ -764,20 +764,20 @@ cpdef void order2_zplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ey field components for the zplus slab. @@ -792,7 +792,7 @@ cpdef void order2_zplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEy - cdef floattype_t dz, dHx, dHy, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 + cdef float_or_double dz, dHx, dHy, RA0, RB0, RE0, RF0, RA1, RB1, RE1, RF1, RA01 dz = d nx = xf - xs ny = yf - ys diff --git a/gprMax/cython/pml_updates_electric_MRIPML.pyx b/gprMax/cython/pml_updates_electric_MRIPML.pyx index 8620d716..805a36ea 100644 --- a/gprMax/cython/pml_updates_electric_MRIPML.pyx +++ b/gprMax/cython/pml_updates_electric_MRIPML.pyx @@ -1,4 +1,4 @@ -cdef floattype_t# Copyright (C) 2015-2019: The University of Edinburgh +cdef float_or_double# Copyright (C) 2015-2019: The University of Edinburgh # Authors: Craig Warren and Antonis Giannopoulos # # This file is part of gprMax. @@ -20,7 +20,7 @@ import numpy as np cimport numpy as np from cython.parallel import prange -from gprMax.config cimport floattype_t +from gprMax.config cimport float_or_double cpdef void order1_xminus( @@ -31,20 +31,20 @@ cpdef void order1_xminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ey and Ez field components for the xminus slab. @@ -59,7 +59,7 @@ cpdef void order1_xminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEy, materialEz - cdef floattype_t dx, dHy, dHz, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dx, dHy, dHz, IRA, IRA1, RB0, RC0, RE0, RF0 dx = d nx = xf - xs ny = yf - ys @@ -96,20 +96,20 @@ cpdef void order2_xminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ey and Ez field components for the xminus slab. @@ -124,7 +124,7 @@ cpdef void order2_xminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEy, materialEz - cdef floattype_t dx, dHy, dHz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dx, dHy, dHz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dx = d nx = xf - xs ny = yf - ys @@ -170,20 +170,20 @@ cpdef void order1_xplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ey and Ez field components for the xplus slab. @@ -198,7 +198,7 @@ cpdef void order1_xplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEy, materialEz - cdef floattype_t dx, dHy, dHz, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dx, dHy, dHz, IRA, IRA1, RB0, RC0, RE0, RF0 dx = d nx = xf - xs ny = yf - ys @@ -235,20 +235,20 @@ cpdef void order2_xplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ey and Ez field components for the xplus slab. @@ -263,7 +263,7 @@ cpdef void order2_xplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEy, materialEz - cdef floattype_t dx, dHy, dHz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dx, dHy, dHz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dx = d nx = xf - xs ny = yf - ys @@ -309,20 +309,20 @@ cpdef void order1_yminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ez field components for the yminus slab. @@ -337,7 +337,7 @@ cpdef void order1_yminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEz - cdef floattype_t dy, dHx, dHz, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dy, dHx, dHz, IRA, IRA1, RB0, RC0, RE0, RF0 dy = d nx = xf - xs ny = yf - ys @@ -374,20 +374,20 @@ cpdef void order2_yminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ez field components for the yminus slab. @@ -402,7 +402,7 @@ cpdef void order2_yminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEz - cdef floattype_t dy, dHx, dHz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dy, dHx, dHz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dy = d nx = xf - xs ny = yf - ys @@ -448,20 +448,20 @@ cpdef void order1_yplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ez field components for the yplus slab. @@ -476,7 +476,7 @@ cpdef void order1_yplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEz - cdef floattype_t dy, dHx, dHz, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dy, dHx, dHz, IRA, IRA1, RB0, RC0, RE0, RF0 dy = d nx = xf - xs ny = yf - ys @@ -513,20 +513,20 @@ cpdef void order2_yplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ez field components for the yplus slab. @@ -541,7 +541,7 @@ cpdef void order2_yplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEz - cdef floattype_t dy, dHx, dHz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dy, dHx, dHz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dy = d nx = xf - xs ny = yf - ys @@ -587,20 +587,20 @@ cpdef void order1_zminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ey field components for the zminus slab. @@ -615,7 +615,7 @@ cpdef void order1_zminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEy - cdef floattype_t dz, dHx, dHy, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dz, dHx, dHy, IRA, IRA1, RB0, RC0, RE0, RF0 dz = d nx = xf - xs ny = yf - ys @@ -652,20 +652,20 @@ cpdef void order2_zminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ey field components for the zminus slab. @@ -680,7 +680,7 @@ cpdef void order2_zminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEy - cdef floattype_t dz, dHx, dHy, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dz, dHx, dHy, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dz = d nx = xf - xs ny = yf - ys @@ -726,20 +726,20 @@ cpdef void order1_zplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ey field components for the zplus slab. @@ -754,7 +754,7 @@ cpdef void order1_zplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEy - cdef floattype_t dz, dHx, dHy, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dz, dHx, dHy, IRA, IRA1, RB0, RC0, RE0, RF0 dz = d nx = xf - xs ny = yf - ys @@ -791,20 +791,20 @@ cpdef void order2_zplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsE, + float_or_double[:, ::1] updatecoeffsE, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Ex and Ey field components for the zplus slab. @@ -819,7 +819,7 @@ cpdef void order2_zplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialEx, materialEy - cdef floattype_t dz, dHx, dHy, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dz, dHx, dHy, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dz = d nx = xf - xs ny = yf - ys diff --git a/gprMax/cython/pml_updates_magnetic_HORIPML.pyx b/gprMax/cython/pml_updates_magnetic_HORIPML.pyx index 112b093c..6a39a5f5 100644 --- a/gprMax/cython/pml_updates_magnetic_HORIPML.pyx +++ b/gprMax/cython/pml_updates_magnetic_HORIPML.pyx @@ -20,7 +20,7 @@ import numpy as np cimport numpy as np from cython.parallel import prange -from gprMax.config cimport floattype_t +from gprMax.config cimport float_or_double cpdef void order1_xminus( @@ -31,20 +31,20 @@ cpdef void order1_xminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hy and Hz field components for the xminus slab. @@ -59,7 +59,7 @@ cpdef void order1_xminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHy, materialHz - cdef floattype_t dx, dEy, dEz, RA01, RB0, RE0, RF0 + cdef float_or_double dx, dEy, dEz, RA01, RB0, RE0, RF0 dx = d nx = xf - xs ny = yf - ys @@ -94,20 +94,20 @@ cpdef void order2_xminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hy and Hz field components for the xminus slab. @@ -122,7 +122,7 @@ cpdef void order2_xminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHy, materialHz - cdef floattype_t dx, dEy, dEz, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 + cdef float_or_double dx, dEy, dEz, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 dx = d nx = xf - xs ny = yf - ys @@ -165,20 +165,20 @@ cpdef void order1_xplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hy and Hz field components for the xplus slab. @@ -193,7 +193,7 @@ cpdef void order1_xplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHy, materialHz - cdef floattype_t dx, dEy, dEz, RA01, RB0, RE0, RF0 + cdef float_or_double dx, dEy, dEz, RA01, RB0, RE0, RF0 dx = d nx = xf - xs ny = yf - ys @@ -228,20 +228,20 @@ cpdef void order2_xplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hy and Hz field components for the xplus slab. @@ -256,7 +256,7 @@ cpdef void order2_xplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHy, materialHz - cdef floattype_t dx, dEy, dEz, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 + cdef float_or_double dx, dEy, dEz, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 dx = d nx = xf - xs ny = yf - ys @@ -299,20 +299,20 @@ cpdef void order1_yminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hz field components for the yminus slab. @@ -327,7 +327,7 @@ cpdef void order1_yminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHz - cdef floattype_t dy, dEx, dEz, RA01, RB0, RE0, RF0 + cdef float_or_double dy, dEx, dEz, RA01, RB0, RE0, RF0 dy = d nx = xf - xs ny = yf - ys @@ -362,20 +362,20 @@ cpdef void order2_yminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hz field components for the yminus slab. @@ -390,7 +390,7 @@ cpdef void order2_yminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHz - cdef floattype_t dy, dEx, dEz, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 + cdef float_or_double dy, dEx, dEz, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 dy = d nx = xf - xs ny = yf - ys @@ -433,20 +433,20 @@ cpdef void order1_yplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hz field components for the yplus slab. @@ -461,7 +461,7 @@ cpdef void order1_yplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHz - cdef floattype_t dy, dEx, dEz, RA01, RB0, RE0, RF0 + cdef float_or_double dy, dEx, dEz, RA01, RB0, RE0, RF0 dy = d nx = xf - xs ny = yf - ys @@ -496,20 +496,20 @@ cpdef void order2_yplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hz field components for the yplus slab. @@ -524,7 +524,7 @@ cpdef void order2_yplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHz - cdef floattype_t dy, dEx, dEz, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 + cdef float_or_double dy, dEx, dEz, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 dy = d nx = xf - xs ny = yf - ys @@ -567,20 +567,20 @@ cpdef void order1_zminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hy field components for the zminus slab. @@ -595,7 +595,7 @@ cpdef void order1_zminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHy - cdef floattype_t dz, dEx, dEy, RA01, RB0, RE0, RF0 + cdef float_or_double dz, dEx, dEy, RA01, RB0, RE0, RF0 dz = d nx = xf - xs ny = yf - ys @@ -630,20 +630,20 @@ cpdef void order2_zminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hy field components for the zminus slab. @@ -658,7 +658,7 @@ cpdef void order2_zminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHy - cdef floattype_t dz, dEx, dEy, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 + cdef float_or_double dz, dEx, dEy, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 dz = d nx = xf - xs ny = yf - ys @@ -701,20 +701,20 @@ cpdef void order1_zplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hy field components for the zplus slab. @@ -729,7 +729,7 @@ cpdef void order1_zplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHy - cdef floattype_t dz, dEx, dEy, RA01, RB0, RE0, RF0 + cdef float_or_double dz, dEx, dEy, RA01, RB0, RE0, RF0 dz = d nx = xf - xs ny = yf - ys @@ -764,20 +764,20 @@ cpdef void order2_zplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hy field components for the zplus slab. @@ -792,7 +792,7 @@ cpdef void order2_zplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHy - cdef floattype_t dz, dEx, dEy, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 + cdef float_or_double dz, dEx, dEy, RA0, RA01, RB0, RE0, RF0, RA1, RB1, RE1, RF1 dz = d nx = xf - xs ny = yf - ys diff --git a/gprMax/cython/pml_updates_magnetic_MRIPML.pyx b/gprMax/cython/pml_updates_magnetic_MRIPML.pyx index 2dc77842..8b899e06 100644 --- a/gprMax/cython/pml_updates_magnetic_MRIPML.pyx +++ b/gprMax/cython/pml_updates_magnetic_MRIPML.pyx @@ -20,7 +20,7 @@ import numpy as np cimport numpy as np from cython.parallel import prange -from gprMax.config cimport floattype_t +from gprMax.config cimport float_or_double cpdef void order1_xminus( @@ -31,20 +31,20 @@ cpdef void order1_xminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hy and Hz field components for the xminus slab. @@ -59,7 +59,7 @@ cpdef void order1_xminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHy, materialHz - cdef floattype_t dx, dEy, dEz, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dx, dEy, dEz, IRA, IRA1, RB0, RC0, RE0, RF0 dx = d nx = xf - xs ny = yf - ys @@ -96,20 +96,20 @@ cpdef void order2_xminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hy and Hz field components for the xminus slab. @@ -124,7 +124,7 @@ cpdef void order2_xminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHy, materialHz - cdef floattype_t dx, dEy, dEz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dx, dEy, dEz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dx = d nx = xf - xs ny = yf - ys @@ -170,20 +170,20 @@ cpdef void order1_xplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hy and Hz field components for the xplus slab. @@ -198,7 +198,7 @@ cpdef void order1_xplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHy, materialHz - cdef floattype_t dx, dEy, dEz, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dx, dEy, dEz, IRA, IRA1, RB0, RC0, RE0, RF0 dx = d nx = xf - xs ny = yf - ys @@ -235,20 +235,20 @@ cpdef void order2_xplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hy and Hz field components for the xplus slab. @@ -263,7 +263,7 @@ cpdef void order2_xplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHy, materialHz - cdef floattype_t dx, dEy, dEz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dx, dEy, dEz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dx = d nx = xf - xs ny = yf - ys @@ -309,20 +309,20 @@ cpdef void order1_yminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hz field components for the yminus slab. @@ -337,7 +337,7 @@ cpdef void order1_yminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHz - cdef floattype_t dy, dEx, dEz, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dy, dEx, dEz, IRA, IRA1, RB0, RC0, RE0, RF0 dy = d nx = xf - xs ny = yf - ys @@ -374,20 +374,20 @@ cpdef void order2_yminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hz field components for the yminus slab. @@ -402,7 +402,7 @@ cpdef void order2_yminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHz - cdef floattype_t dy, dEx, dEz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dy, dEx, dEz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dy = d nx = xf - xs ny = yf - ys @@ -448,20 +448,20 @@ cpdef void order1_yplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hz field components for the yplus slab. @@ -476,7 +476,7 @@ cpdef void order1_yplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHz - cdef floattype_t dy, dEx, dEz, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dy, dEx, dEz, IRA, IRA1, RB0, RC0, RE0, RF0 dy = d nx = xf - xs ny = yf - ys @@ -513,20 +513,20 @@ cpdef void order2_yplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hz field components for the yplus slab. @@ -541,7 +541,7 @@ cpdef void order2_yplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHz - cdef floattype_t dy, dEx, dEz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dy, dEx, dEz, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dy = d nx = xf - xs ny = yf - ys @@ -587,20 +587,20 @@ cpdef void order1_zminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hy field components for the zminus slab. @@ -615,7 +615,7 @@ cpdef void order1_zminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHy - cdef floattype_t dz, dEx, dEy, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dz, dEx, dEy, IRA, IRA1, RB0, RC0, RE0, RF0 dz = d nx = xf - xs ny = yf - ys @@ -652,20 +652,20 @@ cpdef void order2_zminus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hy field components for the zminus slab. @@ -680,7 +680,7 @@ cpdef void order2_zminus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHy - cdef floattype_t dz, dEx, dEy, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dz, dEx, dEy, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dz = d nx = xf - xs ny = yf - ys @@ -726,20 +726,20 @@ cpdef void order1_zplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hy field components for the zplus slab. @@ -754,7 +754,7 @@ cpdef void order1_zplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHy - cdef floattype_t dz, dEx, dEy, IRA, IRA1, RB0, RC0, RE0, RF0 + cdef float_or_double dz, dEx, dEy, IRA, IRA1, RB0, RC0, RE0, RF0 dz = d nx = xf - xs ny = yf - ys @@ -791,20 +791,20 @@ cpdef void order2_zplus( int zs, int zf, int nthreads, - floattype_t[:, ::1] updatecoeffsH, + float_or_double[:, ::1] updatecoeffsH, np.uint32_t[:, :, :, ::1] ID, - floattype_t[:, :, ::1] Ex, - floattype_t[:, :, ::1] Ey, - floattype_t[:, :, ::1] Ez, - floattype_t[:, :, ::1] Hx, - floattype_t[:, :, ::1] Hy, - floattype_t[:, :, ::1] Hz, - floattype_t[:, :, :, ::1] Phi1, - floattype_t[:, :, :, ::1] Phi2, - floattype_t[:, ::1] RA, - floattype_t[:, ::1] RB, - floattype_t[:, ::1] RE, - floattype_t[:, ::1] RF, + float_or_double[:, :, ::1] Ex, + float_or_double[:, :, ::1] Ey, + float_or_double[:, :, ::1] Ez, + float_or_double[:, :, ::1] Hx, + float_or_double[:, :, ::1] Hy, + float_or_double[:, :, ::1] Hz, + float_or_double[:, :, :, ::1] Phi1, + float_or_double[:, :, :, ::1] Phi2, + float_or_double[:, ::1] RA, + float_or_double[:, ::1] RB, + float_or_double[:, ::1] RE, + float_or_double[:, ::1] RF, float d ): """This function updates the Hx and Hy field components for the zplus slab. @@ -819,7 +819,7 @@ cpdef void order2_zplus( cdef Py_ssize_t i, j, k, ii, jj, kk cdef int nx, ny, nz, materialHx, materialHy - cdef floattype_t dz, dEx, dEy, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 + cdef float_or_double dz, dEx, dEy, IRA, IRA1, RB0, RC0, RE0, RF0, RB1, RC1, RE1, RF1, Psi1, Psi2 dz = d nx = xf - xs ny = yf - ys diff --git a/gprMax/cython/snapshots.pyx b/gprMax/cython/snapshots.pyx index 4efc3dad..05a9f516 100644 --- a/gprMax/cython/snapshots.pyx +++ b/gprMax/cython/snapshots.pyx @@ -18,35 +18,35 @@ cimport numpy as np -from gprMax.config cimport floattype_t +from gprMax.config cimport float_or_double cpdef void calculate_snapshot_fields( int nx, int ny, int nz, - floattype_t[:, :, ::1] sliceEx, - floattype_t[:, :, ::1] sliceEy, - floattype_t[:, :, ::1] sliceEz, - floattype_t[:, :, ::1] sliceHx, - floattype_t[:, :, ::1] sliceHy, - floattype_t[:, :, ::1] sliceHz, - floattype_t[:, :, ::1] snapEx, - floattype_t[:, :, ::1] snapEy, - floattype_t[:, :, ::1] snapEz, - floattype_t[:, :, ::1] snapHx, - floattype_t[:, :, ::1] snapHy, - floattype_t[:, :, ::1] snapHz + float_or_double[:, :, ::1] sliceEx, + float_or_double[:, :, ::1] sliceEy, + float_or_double[:, :, ::1] sliceEz, + float_or_double[:, :, ::1] sliceHx, + float_or_double[:, :, ::1] sliceHy, + float_or_double[:, :, ::1] sliceHz, + float_or_double[:, :, ::1] snapEx, + float_or_double[:, :, ::1] snapEy, + float_or_double[:, :, ::1] snapEz, + float_or_double[:, :, ::1] snapHx, + float_or_double[:, :, ::1] snapHy, + float_or_double[:, :, ::1] snapHz ): """This function calculates electric and magnetic values at points from - averaging values in cells + averaging values in cells. Args: nx, ny, nz (int): Size of snapshot array sliceEx, sliceEy, sliceEz, sliceHx, sliceHy, sliceHz (memoryview): Access to slices of field arrays - snapEx, snapEy, snapEz, snapHx, - snapHy, snapHz (memoryview): Access to snapshot arrays + snapEx, snapEy, snapEz, + snapHx, snapHy, snapHz (memoryview): Access to snapshot arrays """ cdef Py_ssize_t i, j, k @@ -54,9 +54,8 @@ cpdef void calculate_snapshot_fields( for i in range(nx): for j in range(ny): for k in range(nz): - - # The electric field component value at a point comes from - # average of the 4 electric field component values in that cell + # The electric field component value at a point comes from the + # average of the 4 electric field component values in that cell. snapEx[i, j, k] = (sliceEx[i, j, k] + sliceEx[i, j + 1, k] + sliceEx[i, j, k + 1] + sliceEx[i, j + 1, k + 1]) / 4 snapEy[i, j, k] = (sliceEy[i, j, k] + sliceEy[i + 1, j, k] + @@ -64,8 +63,9 @@ cpdef void calculate_snapshot_fields( snapEz[i, j, k] = (sliceEz[i, j, k] + sliceEz[i + 1, j, k] + sliceEz[i, j + 1, k] + sliceEz[i + 1, j + 1, k]) / 4 - # The magnetic field component value at a point comes from average - # of 2 magnetic field component values in that cell and the following cell + # The magnetic field component value at a point comes from + # average of 2 magnetic field component values in that cell and + # the neighbouring cell. snapHx[i, j, k] = (sliceHx[i, j, k] + sliceHx[i + 1, j, k]) / 2 snapHy[i, j, k] = (sliceHy[i, j, k] + sliceHy[i, j + 1, k]) / 2 snapHz[i, j, k] = (sliceHz[i, j, k] + sliceHz[i, j, k + 1]) / 2 diff --git a/gprMax/exceptions.py b/gprMax/exceptions.py index 75a34d9c..d8a96f42 100644 --- a/gprMax/exceptions.py +++ b/gprMax/exceptions.py @@ -37,7 +37,9 @@ class GeneralError(ValueError): class CmdInputError(ValueError): - """Handles errors in user specified commands. Subclasses the ValueError class.""" + """Handles errors in user specified commands. Subclasses the ValueError + class. + """ def __init__(self, message, *args): diff --git a/gprMax/fields_outputs.py b/gprMax/fields_outputs.py index c70167ba..2a89acd9 100644 --- a/gprMax/fields_outputs.py +++ b/gprMax/fields_outputs.py @@ -20,7 +20,7 @@ from string import Template import h5py -from gprMax._version import __version__ +from ._version import __version__ def store_outputs(iteration, Ex, Ey, Ez, Hx, Hy, Hz, G): @@ -28,8 +28,10 @@ def store_outputs(iteration, Ex, Ey, Ez, Hx, Hy, Hz, G): Args: iteration (int): Current iteration number. - Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic field values. - G (class): Grid class instance - holds essential parameters describing the model. + Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic + field values. + G (class): Grid class instance - holds essential parameters describing + the model. """ for rx in G.rxs: @@ -41,7 +43,8 @@ def store_outputs(iteration, Ex, Ey, Ez, Hx, Hy, Hz, G): # Store current component else: func = globals()[output] - rx.outputs[output][iteration] = func(rx.xcoord, rx.ycoord, rx.zcoord, Hx, Hy, Hz, G) + rx.outputs[output][iteration] = func(rx.xcoord, rx.ycoord, rx.zcoord, + Hx, Hy, Hz, G) for tl in G.transmissionlines: tl.Vtotal[iteration] = tl.voltage[tl.antpos] @@ -94,8 +97,10 @@ def write_hdf5_outputfile(outputfile, Ex, Ey, Ez, Hx, Hy, Hz, G): Args: outputfile (str): Name of the output file. - Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic field values. - G (class): Grid class instance - holds essential parameters describing the model. + Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic + field values. + G (class): Grid class instance - holds essential parameters describing + the model. """ f = h5py.File(outputfile, 'w') diff --git a/gprMax/fractals.py b/gprMax/fractals.py index d18e3b57..07d8f7e1 100644 --- a/gprMax/fractals.py +++ b/gprMax/fractals.py @@ -19,12 +19,10 @@ import numpy as np from scipy import fftpack -from gprMax.config import floattype -from gprMax.config import complextype -from gprMax.config import hostinfo -from gprMax.cython.fractals_generate import generate_fractal2D -from gprMax.cython.fractals_generate import generate_fractal3D -from gprMax.utilities import round_value +import gprMax.config as config +from .cython.fractals_generate import generate_fractal2D +from .cython.fractals_generate import generate_fractal3D +from .utilities import round_value np.seterr(divide='raise') @@ -37,9 +35,11 @@ class FractalSurface(object): def __init__(self, xs, xf, ys, yf, zs, zf, dimension): """ Args: - xs, xf, ys, yf, zs, zf (float): Extent of the fractal surface (one pair of - coordinates must be equal to correctly define a surface). - dimension (float): Fractal dimension that controls the fractal distribution. + xs, xf, ys, yf, zs, zf (float): Extent of the fractal surface + (one pair of coordinates must be + equal to correctly define a surface). + dimension (float): Fractal dimension that controls the fractal + distribution. """ self.ID = None @@ -66,7 +66,8 @@ class FractalSurface(object): """Generate a 2D array with a fractal distribution. Args: - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. """ if self.xs == self.xf: @@ -76,10 +77,11 @@ class FractalSurface(object): elif self.zs == self.zf: surfacedims = (self.nx, self.ny) - self.fractalsurface = np.zeros(surfacedims, dtype=complextype) + self.fractalsurface = np.zeros(surfacedims, dtype=config.dtypes['complex']) # Positional vector at centre of array, scaled by weighting - v1 = np.array([self.weighting[0] * (surfacedims[0]) / 2, self.weighting[1] * (surfacedims[1]) / 2]) + v1 = np.array([self.weighting[0] * (surfacedims[0]) / 2, self.weighting[1] + * (surfacedims[1]) / 2]) # 2D array of random numbers to be convolved with the fractal function R = np.random.RandomState(self.seed) @@ -91,7 +93,8 @@ class FractalSurface(object): A = fftpack.fftshift(A) # Generate fractal - generate_fractal2D(surfacedims[0], surfacedims[1], hostinfo['ompthreads'], self.b, self.weighting, v1, A, self.fractalsurface) + generate_fractal2D(surfacedims[0], surfacedims[1], config.hostinfo['ompthreads'], + self.b, self.weighting, v1, A, self.fractalsurface) # Shift the zero frequency component to start of the array self.fractalsurface = fftpack.ifftshift(self.fractalsurface) @@ -101,8 +104,10 @@ class FractalSurface(object): fractalmin = np.amin(self.fractalsurface) fractalmax = np.amax(self.fractalsurface) fractalrange = fractalmax - fractalmin - self.fractalsurface = self.fractalsurface * ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) \ - + self.fractalrange[0] - ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) * fractalmin + self.fractalsurface = (self.fractalsurface * ((self.fractalrange[1] + - self.fractalrange[0]) / fractalrange) + + self.fractalrange[0] - ((self.fractalrange[1] + - self.fractalrange[0]) / fractalrange) * fractalmin) class FractalVolume(object): @@ -112,7 +117,8 @@ class FractalVolume(object): """ Args: xs, xf, ys, yf, zs, zf (float): Extent of the fractal volume. - dimension (float): Fractal dimension that controls the fractal distribution. + dimension (float): Fractal dimension that controls the fractal + distribution. """ self.ID = None @@ -139,7 +145,8 @@ class FractalVolume(object): """Generate a 3D volume with a fractal distribution. Args: - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. """ # Scale filter according to size of fractal volume @@ -158,10 +165,11 @@ class FractalVolume(object): # Adjust weighting to account for filter scaling self.weighting = np.multiply(self.weighting, filterscaling) - self.fractalvolume = np.zeros((self.nx, self.ny, self.nz), dtype=complextype) + self.fractalvolume = np.zeros((self.nx, self.ny, self.nz), dtype=config.dtypes['complex']) # Positional vector at centre of array, scaled by weighting - v1 = np.array([self.weighting[0] * self.nx / 2, self.weighting[1] * self.ny / 2, self.weighting[2] * self.nz / 2]) + v1 = np.array([self.weighting[0] * self.nx / 2, self.weighting[1] + * self.ny / 2, self.weighting[2] * self.nz / 2]) # 3D array of random numbers to be convolved with the fractal function R = np.random.RandomState(self.seed) @@ -173,7 +181,8 @@ class FractalVolume(object): A = fftpack.fftshift(A) # Generate fractal - generate_fractal3D(self.nx, self.ny, self.nz, hostinfo['ompthreads'], self.b, self.weighting, v1, A, self.fractalvolume) + generate_fractal3D(self.nx, self.ny, self.nz, config.hostinfo['ompthreads'], + self.b, self.weighting, v1, A, self.fractalvolume) # Shift the zero frequency component to the start of the array self.fractalvolume = fftpack.ifftshift(self.fractalvolume) @@ -186,9 +195,10 @@ class FractalVolume(object): self.fractalvolume[:, j, k] = np.digitize(self.fractalvolume[:, j, k], bins, right=True) def generate_volume_mask(self): + """Generate a 3D volume to use as a mask for adding rough surfaces, + water and grass/roots. Zero signifies the mask is not set, one + signifies the mask is set. """ - Generate a 3D volume to use as a mask for adding rough surfaces, water and grass/roots. - Zero signifies the mask is not set, one signifies the mask is set.""" self.mask = np.zeros((self.nx, self.ny, self.nz), dtype=np.int8) maskxs = self.originalxs - self.xs @@ -210,7 +220,8 @@ class Grass(object): """ self.numblades = numblades - self.geometryparams = np.zeros((self.numblades, 6), dtype=floattype) + self.geometryparams = np.zeros((self.numblades, 6), + dtype=config.dtypes['float_or_double']) self.seed = None # Randomly defined parameters that will be used to calculate geometry @@ -238,8 +249,10 @@ class Grass(object): x, y (float): x and y coordinates of grass blade. """ - x = self.geometryparams[blade, 2] * (height / self.geometryparams[blade, 0]) * (height / self.geometryparams[blade, 0]) - y = self.geometryparams[blade, 3] * (height / self.geometryparams[blade, 1]) * (height / self.geometryparams[blade, 1]) + x = (self.geometryparams[blade, 2] * (height / self.geometryparams[blade, 0]) + * (height / self.geometryparams[blade, 0])) + y = (self.geometryparams[blade, 3] * (height / self.geometryparams[blade, 1]) + * (height / self.geometryparams[blade, 1])) x = round_value(x) y = round_value(y) diff --git a/gprMax/geometry_outputs.py b/gprMax/geometry_outputs.py index 3cefab80..fe8da4ea 100644 --- a/gprMax/geometry_outputs.py +++ b/gprMax/geometry_outputs.py @@ -23,10 +23,12 @@ import h5py import numpy as np from struct import pack -from gprMax._version import __version__ -from gprMax.cython.geometry_outputs import define_normal_geometry -from gprMax.cython.geometry_outputs import define_fine_geometry -from gprMax.utilities import round_value +import gprMax.config as config + +from ._version import __version__ +from .cython.geometry_outputs import define_normal_geometry +from .cython.geometry_outputs import define_fine_geometry +from .utilities import round_value class GeometryView(object): @@ -104,16 +106,15 @@ class GeometryView(object): + np.dtype(np.uint32).itemsize * vtk_cell_offsets + np.dtype(np.uint32).itemsize * 4) - def set_filename(self, appendmodelnumber, G): + def set_filename(self, appendmodelnumber): """ Construct filename from user-supplied name and model run number. Args: appendmodelnumber (str): Text to append to filename. - G (class): Grid class instance - holds essential parameters describing the model. """ - self.filename = os.path.abspath(os.path.join(G.inputdirectory, self.basefilename + appendmodelnumber)) + self.filename = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(config.general['inputfilepath'])), self.basefilename + appendmodelnumber)) self.filename += self.fileext def write_vtk(self, G, pbar): diff --git a/gprMax/gprMax.py b/gprMax/gprMax.py index 892fde18..c02e898b 100644 --- a/gprMax/gprMax.py +++ b/gprMax/gprMax.py @@ -28,22 +28,17 @@ from enum import Enum import h5py import numpy as np -from gprMax._version import __version__ -from gprMax._version import codename +from ._version import __version__ +from ._version import codename import gprMax.config as config -from gprMax.config import c -from gprMax.config import e0 -from gprMax.config import m0 -from gprMax.config import z0 -from gprMax.config import hostinfo -from gprMax.exceptions import GeneralError -from gprMax.model_build_run import run_model -from gprMax.utilities import detect_check_gpus -from gprMax.utilities import get_terminal_width -from gprMax.utilities import human_size -from gprMax.utilities import logo -from gprMax.utilities import open_path_file -from gprMax.utilities import timer +from .exceptions import GeneralError +from .model_build_run import run_model +from .utilities import detect_check_gpus +from .utilities import get_terminal_width +from .utilities import human_size +from .utilities import logo +from .utilities import open_path_file +from .utilities import timer def main(): """This is the main function for gprMax.""" @@ -116,24 +111,24 @@ def run_main(args): logo(__version__ + ' (' + codename + ')') # Print information about host machine - hyperthreading = ', {} cores with HT'.format(hostinfo['logicalcores']) if hostinfo['hyperthreading'] else '' - print('\nHost: {} | {} | {} x {} ({} cores{}) | {} RAM | {}'.format(hostinfo['hostname'], - hostinfo['machineID'], hostinfo['sockets'], hostinfo['cpuID'], hostinfo['physicalcores'], - hyperthreading, human_size(hostinfo['ram'], a_kilobyte_is_1024_bytes=True), hostinfo['osversion'])) + hyperthreading = ', {} cores with HT'.format(config.hostinfo['logicalcores']) if config.hostinfo['hyperthreading'] else '' + print('\nHost: {} | {} | {} x {} ({} cores{}) | {} RAM | {}'.format(config.hostinfo['hostname'], + config.hostinfo['machineID'], config.hostinfo['sockets'], config.hostinfo['cpuID'], config.hostinfo['physicalcores'], + hyperthreading, human_size(config.hostinfo['ram'], a_kilobyte_is_1024_bytes=True), config.hostinfo['osversion'])) # Get information/setup any Nvidia GPU(s) if args.gpu is not None: # Flatten a list of lists if any(isinstance(element, list) for element in args.gpu): args.gpu = [val for sublist in args.gpu for val in sublist] - config.gpus, allgpustext = detect_check_gpus(args.gpu) + config.cuda['gpus'], allgpustext = detect_check_gpus(args.gpu) print('GPU(s): {}'.format(' | '.join(allgpustext))) # Process input file with open_path_file(args.inputfile) as inputfile: # Create a separate namespace that users can access in any Python code blocks in the input file - usernamespace = {'c': c, 'e0': e0, 'm0': m0, 'z0': z0, 'number_model_runs': args.n, 'inputfile': os.path.abspath(inputfile.name)} + usernamespace = {'c': config.c, 'e0': config.e0, 'm0': config.m0, 'z0': config.z0, 'number_model_runs': args.n, 'inputfile': os.path.abspath(inputfile.name)} ####################################### # Process for benchmarking simulation # @@ -167,8 +162,8 @@ def run_main(args): else: if args.task and args.restart: raise GeneralError('Job array and restart modes cannot be used together') - if config.gpus: - config.gpus = config.gpus[0] + if config.cuda['gpus']: + config.cuda['gpus'] = config.cuda['gpus'][0] run_std_sim(args, inputfile, usernamespace) @@ -201,7 +196,7 @@ def run_std_sim(args, inputfile, usernamespace): for currentmodelrun in range(modelstart, modelend): run_model(args, currentmodelrun, modelend - 1, numbermodelruns, inputfile, usernamespace) tsimend = timer() - simcompletestr = '\n=== Simulation on {} completed in [HH:MM:SS]: {}'.format(hostinfo['hostname'], datetime.timedelta(seconds=tsimend - tsimstart)) + simcompletestr = '\n=== Simulation on {} completed in [HH:MM:SS]: {}'.format(config.hostinfo['hostname'], datetime.timedelta(seconds=tsimend - tsimstart)) print('{} {}\n'.format(simcompletestr, '=' * (get_terminal_width() - 1 - len(simcompletestr)))) @@ -219,8 +214,8 @@ def run_benchmark_sim(args, inputfile, usernamespace): """ # Store information about host machine - hyperthreading = ', {} cores with HT'.format(hostinfo['logicalcores']) if hostinfo['hyperthreading'] else '' - machineIDlong = '{}; {} x {} ({} cores{}); {} RAM; {}'.format(hostinfo['machineID'], hostinfo['sockets'], hostinfo['cpuID'], hostinfo['physicalcores'], hyperthreading, human_size(hostinfo['ram'], a_kilobyte_is_1024_bytes=True), hostinfo['osversion']) + hyperthreading = ', {} cores with HT'.format(config.hostinfo['logicalcores']) if config.hostinfo['hyperthreading'] else '' + machineIDlong = '{}; {} x {} ({} cores{}); {} RAM; {}'.format(config.hostinfo['machineID'], config.hostinfo['sockets'], config.hostinfo['cpuID'], config.hostinfo['physicalcores'], hyperthreading, human_size(config.hostinfo['ram'], a_kilobyte_is_1024_bytes=True), config.hostinfo['osversion']) # Initialise arrays to hold CPU thread info and times, and GPU info and times cputhreads = np.array([], dtype=np.int32) @@ -232,8 +227,8 @@ def run_benchmark_sim(args, inputfile, usernamespace): if args.gpu is None: # Number of CPU threads to benchmark - start from single thread and double threads until maximum number of physical cores threads = 1 - maxthreads = hostinfo['physicalcores'] - maxthreadspersocket = hostinfo['physicalcores'] / hostinfo['sockets'] + maxthreads = config.hostinfo['physicalcores'] + maxthreadspersocket = config.hostinfo['physicalcores'] / config.hostinfo['sockets'] while threads < maxthreadspersocket: cputhreads = np.append(cputhreads, int(threads)) threads *= 2 @@ -271,7 +266,7 @@ def run_benchmark_sim(args, inputfile, usernamespace): # Run GPU benchmark else: args.gpu = gpus[(currentmodelrun - 1)] - os.environ['OMP_NUM_THREADS'] = str(hostinfo['physicalcores']) + os.environ['OMP_NUM_THREADS'] = str(config.hostinfo['physicalcores']) gputimes[(currentmodelrun - 1)] = run_model(args, currentmodelrun, modelend - 1, numbermodelruns, inputfile, usernamespace) # Get model size (in cells) and number of iterations diff --git a/gprMax/grid.py b/gprMax/grid.py index 3c88aa98..2729fe99 100644 --- a/gprMax/grid.py +++ b/gprMax/grid.py @@ -26,17 +26,11 @@ import numpy as np np.seterr(invalid='raise') import gprMax.config as config -from gprMax.config import c -from gprMax.config import floattype -from gprMax.config import complextype -from gprMax.config import numdispersion -from gprMax.config import hostinfo -from gprMax.exceptions import GeneralError -from gprMax.materials import Material -from gprMax.pml import PML -from gprMax.utilities import fft_power -from gprMax.utilities import human_size -from gprMax.utilities import round_value +from .exceptions import GeneralError +from .pml import PML +from .utilities import fft_power +from .utilities import human_size +from .utilities import round_value class Grid(object): @@ -87,9 +81,6 @@ class FDTDGrid(Grid): """ def __init__(self): - self.inputfilename = '' - self.inputdirectory = '' - self.outputdirectory = '' self.title = '' self.memoryusage = 0 @@ -144,24 +135,24 @@ class FDTDGrid(Grid): def initialise_field_arrays(self): """Initialise arrays for the electric and magnetic field components.""" - self.Ex = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=floattype) - self.Ey = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=floattype) - self.Ez = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=floattype) - self.Hx = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=floattype) - self.Hy = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=floattype) - self.Hz = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=floattype) + self.Ex = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double']) + self.Ey = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double']) + self.Ez = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double']) + self.Hx = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double']) + self.Hy = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double']) + self.Hz = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double']) def initialise_std_update_coeff_arrays(self): """Initialise arrays for storing update coefficients.""" - self.updatecoeffsE = np.zeros((len(self.materials), 5), dtype=floattype) - self.updatecoeffsH = np.zeros((len(self.materials), 5), dtype=floattype) + self.updatecoeffsE = np.zeros((len(self.materials), 5), dtype=config.dtypes['float_or_double']) + self.updatecoeffsH = np.zeros((len(self.materials), 5), dtype=config.dtypes['float_or_double']) - def initialise_dispersive_arrays(self): + def initialise_dispersive_arrays(self, dtype): """Initialise arrays for storing coefficients when there are dispersive materials present.""" - self.Tx = np.zeros((Material.maxpoles, self.nx + 1, self.ny + 1, self.nz + 1), dtype=complextype) - self.Ty = np.zeros((Material.maxpoles, self.nx + 1, self.ny + 1, self.nz + 1), dtype=complextype) - self.Tz = np.zeros((Material.maxpoles, self.nx + 1, self.ny + 1, self.nz + 1), dtype=complextype) - self.updatecoeffsdispersive = np.zeros((len(self.materials), 3 * Material.maxpoles), dtype=complextype) + self.Tx = np.zeros((config.materials['maxpoles'], self.nx + 1, self.ny + 1, self.nz + 1), dtype=dtype) + self.Ty = np.zeros((config.materials['maxpoles'], self.nx + 1, self.ny + 1, self.nz + 1), dtype=dtype) + self.Tz = np.zeros((config.materials['maxpoles'], self.nx + 1, self.ny + 1, self.nz + 1), dtype=dtype) + self.updatecoeffsdispersive = np.zeros((len(self.materials), 3 * config.materials['maxpoles']), dtype=dtype) def memory_estimate_basic(self): """Estimate the amount of memory (RAM) required to run a model.""" @@ -174,7 +165,7 @@ class FDTDGrid(Grid): rigidarrays = (12 + 6) * self.nx * self.ny * self.nz * np.dtype(np.int8).itemsize # 6 x field arrays + 6 x ID arrays - fieldarrays = (6 + 6) * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(floattype).itemsize + fieldarrays = (6 + 6) * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(config.dtypes['float_or_double']).itemsize # PML arrays pmlarrays = 0 @@ -206,21 +197,21 @@ class FDTDGrid(Grid): """ # Check if model can be built and/or run on host - if self.memoryusage > hostinfo['ram']: - raise GeneralError('Memory (RAM) required ~{} exceeds {} detected!\n'.format(human_size(self.memoryusage), human_size(hostinfo['ram'], a_kilobyte_is_1024_bytes=True))) + if self.memoryusage > config.hostinfo['ram']: + raise GeneralError('Memory (RAM) required ~{} exceeds {} detected!\n'.format(human_size(self.memoryusage), human_size(config.hostinfo['ram'], a_kilobyte_is_1024_bytes=True))) # Check if model can be run on specified GPU if required - if config.gpus is not None: - if self.memoryusage - snapsmemsize > config.gpus.totalmem: - raise GeneralError('Memory (RAM) required ~{} exceeds {} detected on specified {} - {} GPU!\n'.format(human_size(self.memoryusage), human_size(config.gpus.totalmem, a_kilobyte_is_1024_bytes=True), config.gpus.deviceID, config.gpus.name)) + if config.cuda['gpus'] is not None: + if self.memoryusage - snapsmemsize > config.cuda['gpus'].totalmem: + raise GeneralError('Memory (RAM) required ~{} exceeds {} detected on specified {} - {} GPU!\n'.format(human_size(self.memoryusage), human_size(config.cuda['gpus'].totalmem, a_kilobyte_is_1024_bytes=True), config.cuda['gpus'].deviceID, config.cuda['gpus'].name)) # If the required memory without the snapshots will fit on the GPU then transfer and store snaphots on host - if snapsmemsize != 0 and self.memoryusage - snapsmemsize < config.gpus.totalmem: - config.snapsgpu2cpu = True + if snapsmemsize != 0 and self.memoryusage - snapsmemsize < config.cuda['gpus'].totalmem: + config.cuda['snapsgpu2cpu'] = True def gpu_set_blocks_per_grid(self): """Set the blocks per grid size used for updating the electric and magnetic field arrays on a GPU.""" - config.gpus.bpg = (int(np.ceil(((self.nx + 1) * (self.ny + 1) * (self.nz + 1)) / config.gpus.tpb[0])), 1, 1) + config.cuda['gpus'].bpg = (int(np.ceil(((self.nx + 1) * (self.ny + 1) * (self.nz + 1)) / config.cuda['gpus'].tpb[0])), 1, 1) def gpu_initialise_arrays(self): """Initialise standard field arrays on GPU.""" @@ -298,7 +289,7 @@ def dispersion_analysis(G): # Set maximum frequency to a threshold drop from maximum power, ignoring DC value try: - freqthres = np.where(power[freqmaxpower:] < -numdispersion['highestfreqthres'])[0][0] + freqmaxpower + freqthres = np.where(power[freqmaxpower:] < -config.numdispersion['highestfreqthres'])[0][0] + freqmaxpower results['maxfreq'].append(freqs[freqthres]) except ValueError: results['error'] = 'unable to calculate maximum power from waveform, most likely due to undersampling.' @@ -324,7 +315,7 @@ def dispersion_analysis(G): er = x.er # If there are dispersive materials calculate the complex relative permittivity # at maximum frequency and take the real part - if x.poles > 0: + if x.__class__.__name__ is 'DispersiveMaterial': er = x.calculate_er(results['maxfreq']) er = er.real if er > maxer: @@ -333,7 +324,7 @@ def dispersion_analysis(G): results['material'] = next(x for x in G.materials if x.ID == matmaxer) # Minimum velocity - minvelocity = c / np.sqrt(maxer) + minvelocity = config.c / np.sqrt(maxer) # Minimum wavelength minwavelength = minvelocity / results['maxfreq'] @@ -350,18 +341,18 @@ def dispersion_analysis(G): delta = max(G.dx, G.dy) # Courant stability factor - S = (c * G.dt) / delta + S = (config.c * G.dt) / delta # Grid sampling density results['N'] = minwavelength / delta # Check grid sampling will result in physical wave propagation - if int(np.floor(results['N'])) >= numdispersion['mingridsampling']: + if int(np.floor(results['N'])) >= config.numdispersion['mingridsampling']: # Numerical phase velocity vp = np.pi / (results['N'] * np.arcsin((1 / S) * np.sin((np.pi * S) / results['N']))) # Physical phase velocity error (percentage) - results['deltavp'] = (((vp * c) - c) / c) * 100 + results['deltavp'] = (((vp * config.c) - config.c) / config.c) * 100 # Store rounded down value of grid sampling density results['N'] = int(np.floor(results['N'])) @@ -369,21 +360,6 @@ def dispersion_analysis(G): return results -def get_other_directions(direction): - """Return the two other directions from x, y, z given a single direction - - Args: - direction (str): Component x, y or z - - Returns: - (tuple): Two directions from x, y, z - """ - - directions = {'x': ('y', 'z'), 'y': ('x', 'z'), 'z': ('x', 'y')} - - return directions[direction] - - def Ix(x, y, z, Hx, Hy, Hz, G): """Calculates the x-component of current at a grid position. diff --git a/gprMax/input_cmds_file.py b/gprMax/input_cmds_file.py index 424d4550..4c39e73d 100644 --- a/gprMax/input_cmds_file.py +++ b/gprMax/input_cmds_file.py @@ -20,7 +20,7 @@ import os import sys from io import StringIO -from gprMax.exceptions import CmdInputError +from .exceptions import CmdInputError def process_python_include_code(inputfile, usernamespace): @@ -151,7 +151,7 @@ def process_include_files(hashcmds, inputfile): return processedincludecmds -def write_processed_file(processedlines, appendmodelnumber, G): +def write_processed_file(processedlines, appendmodelnumber): """ Writes an input file after any Python code and include commands in the original input file have been processed. @@ -160,10 +160,9 @@ def write_processed_file(processedlines, appendmodelnumber, G): processedlines (list): Input commands after after processing any Python code and include commands. appendmodelnumber (str): Text to append to filename. - G (class): Grid class instance - holds essential parameters describing the model. """ - processedfile = os.path.join(G.inputdirectory, os.path.splitext(G.inputfilename)[0] + appendmodelnumber + '_processed.in') + processedfile = os.path.join(outputfilepath, os.path.splitext(inputfilepath)[0] + appendmodelnumber + '_processed.in') with open(processedfile, 'w') as f: for item in processedlines: diff --git a/gprMax/input_cmds_geometry.py b/gprMax/input_cmds_geometry.py index 9c5103cc..c7d4f93f 100644 --- a/gprMax/input_cmds_geometry.py +++ b/gprMax/input_cmds_geometry.py @@ -23,31 +23,30 @@ import h5py import numpy as np from tqdm import tqdm -from gprMax.config import floattype -from gprMax.config import messages -from gprMax.config import progressbars -from gprMax.input_cmds_file import check_cmd_names -from gprMax.input_cmds_multiuse import process_multicmds -from gprMax.exceptions import CmdInputError -from gprMax.fractals import FractalSurface -from gprMax.fractals import FractalVolume -from gprMax.fractals import Grass -from gprMax.cython.geometry_primitives import build_edge_x -from gprMax.cython.geometry_primitives import build_edge_y -from gprMax.cython.geometry_primitives import build_edge_z -from gprMax.cython.geometry_primitives import build_face_yz -from gprMax.cython.geometry_primitives import build_face_xz -from gprMax.cython.geometry_primitives import build_face_xy -from gprMax.cython.geometry_primitives import build_triangle -from gprMax.cython.geometry_primitives import build_box -from gprMax.cython.geometry_primitives import build_cylinder -from gprMax.cython.geometry_primitives import build_cylindrical_sector -from gprMax.cython.geometry_primitives import build_sphere -from gprMax.cython.geometry_primitives import build_voxels_from_array -from gprMax.cython.geometry_primitives import build_voxels_from_array_mask -from gprMax.materials import Material -from gprMax.utilities import round_value -from gprMax.utilities import get_terminal_width +import gprMax.config as config + +from .input_cmds_file import check_cmd_names +from .input_cmds_multiuse import process_multicmds +from .exceptions import CmdInputError +from .fractals import FractalSurface +from .fractals import FractalVolume +from .fractals import Grass +from .cython.geometry_primitives import build_edge_x +from .cython.geometry_primitives import build_edge_y +from .cython.geometry_primitives import build_edge_z +from .cython.geometry_primitives import build_face_yz +from .cython.geometry_primitives import build_face_xz +from .cython.geometry_primitives import build_face_xy +from .cython.geometry_primitives import build_triangle +from .cython.geometry_primitives import build_box +from .cython.geometry_primitives import build_cylinder +from .cython.geometry_primitives import build_cylindrical_sector +from .cython.geometry_primitives import build_sphere +from .cython.geometry_primitives import build_voxels_from_array +from .cython.geometry_primitives import build_voxels_from_array_mask +from .materials import Material +from .utilities import round_value +from .utilities import get_terminal_width def process_geometrycmds(geometry, G): @@ -63,9 +62,9 @@ def process_geometrycmds(geometry, G): # Disable progress bar if on Windows as it does not update properly # when messages are printed for geometry if sys.platform == 'win32': - progressbarsgeo = False + progressbarsgeo = True else: - progressbarsgeo = not progressbars + progressbarsgeo = not config.general['progressbars'] for object in tqdm(geometry, desc='Processing geometry related cmds', unit='cmds', ncols=get_terminal_width() - 1, file=sys.stdout, disable=progressbarsgeo): tmp = object.split() @@ -137,12 +136,12 @@ def process_geometrycmds(geometry, G): G.rigidE[:, xs:xs + rigidE.shape[1], ys:ys + rigidE.shape[2], zs:zs + rigidE.shape[3]] = rigidE G.rigidH[:, xs:xs + rigidH.shape[1], ys:ys + rigidH.shape[2], zs:zs + rigidH.shape[3]] = rigidH G.ID[:, xs:xs + ID.shape[1], ys:ys + ID.shape[2], zs:zs + ID.shape[3]] = ID + numexistmaterials - if messages: + if config.general['messages']: tqdm.write('Geometry objects from file {} inserted at {:g}m, {:g}m, {:g}m, with corresponding materials file {}.'.format(geofile, xs * G.dx, ys * G.dy, zs * G.dz, matfile)) except KeyError: averaging = False build_voxels_from_array(xs, ys, zs, numexistmaterials, averaging, data, G.solid, G.rigidE, G.rigidH, G.ID) - if messages: + if config.general['messages']: tqdm.write('Geometry objects from file (voxels only) {} inserted at {:g}m, {:g}m, {:g}m, with corresponding materials file {}.'.format(geofile, xs * G.dx, ys * G.dy, zs * G.dz, matfile)) elif tmp[0] == '#edge:': @@ -201,7 +200,7 @@ def process_geometrycmds(geometry, G): for k in range(zs, zf): build_edge_z(xs, ys, k, material.numID, G.rigidE, G.rigidH, G.ID) - if messages: + if config.general['messages']: tqdm.write('Edge from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m of material {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, tmp[7])) elif tmp[0] == '#plate:': @@ -309,7 +308,7 @@ def process_geometrycmds(geometry, G): for j in range(ys, yf): build_face_xy(i, j, zs, numIDx, numIDy, G.rigidE, G.rigidH, G.ID) - if messages: + if config.general['messages']: tqdm.write('Plate from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m of material(s) {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, ', '.join(materialsrequested))) elif tmp[0] == '#triangle:': @@ -422,7 +421,7 @@ def process_geometrycmds(geometry, G): build_triangle(x1, y1, z1, x2, y2, z2, x3, y3, z3, normal, thickness, G.dx, G.dy, G.dz, numID, numIDx, numIDy, numIDz, averaging, G.solid, G.rigidE, G.rigidH, G.ID) - if messages: + if config.general['messages']: if thickness > 0: if averaging: dielectricsmoothing = 'on' @@ -517,7 +516,7 @@ def process_geometrycmds(geometry, G): build_box(xs, xf, ys, yf, zs, zf, numID, numIDx, numIDy, numIDz, averaging, G.solid, G.rigidE, G.rigidH, G.ID) - if messages: + if config.general['messages']: if averaging: dielectricsmoothing = 'on' else: @@ -598,7 +597,7 @@ def process_geometrycmds(geometry, G): build_cylinder(x1, y1, z1, x2, y2, z2, r, G.dx, G.dy, G.dz, numID, numIDx, numIDy, numIDz, averaging, G.solid, G.rigidE, G.rigidH, G.ID) - if messages: + if config.general['messages']: if averaging: dielectricsmoothing = 'on' else: @@ -719,7 +718,7 @@ def process_geometrycmds(geometry, G): build_cylindrical_sector(ctr1, ctr2, level, sectorstartangle, sectorangle, r, normal, thickness, G.dx, G.dy, G.dz, numID, numIDx, numIDy, numIDz, averaging, G.solid, G.rigidE, G.rigidH, G.ID) - if messages: + if config.general['messages']: if thickness > 0: if averaging: dielectricsmoothing = 'on' @@ -798,7 +797,7 @@ def process_geometrycmds(geometry, G): build_sphere(xc, yc, zc, r, G.dx, G.dy, G.dz, numID, numIDx, numIDy, numIDz, averaging, G.solid, G.rigidE, G.rigidH, G.ID) - if messages: + if config.general['messages']: if averaging: dielectricsmoothing = 'on' else: @@ -879,7 +878,7 @@ def process_geometrycmds(geometry, G): volume.weighting = np.array([float(tmp[8]), float(tmp[9]), float(tmp[10])]) volume.averaging = averagefractalbox - if messages: + if config.general['messages']: if volume.averaging: dielectricsmoothing = 'on' else: @@ -1002,7 +1001,7 @@ def process_geometrycmds(geometry, G): surface.generate_fractal_surface(G) volume.fractalsurfaces.append(surface) - if messages: + if config.general['messages']: tqdm.write('Fractal surface from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with fractal dimension {:g}, fractal weightings {:g}, {:g}, fractal seeding {}, and range {:g}m to {:g}m, added to {}.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, surface.dimension, surface.weighting[0], surface.weighting[1], surface.seed, float(tmp[10]), float(tmp[11]), surface.operatingonID)) if tmp[0] == '#add_surface_water:': @@ -1110,7 +1109,7 @@ def process_geometrycmds(geometry, G): if testwater: raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires the time step for the model to be less than the relaxation time required to model water.') - if messages: + if config.general['messages']: tqdm.write('Water on surface from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with depth {:g}m, added to {}.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, filldepth, surface.operatingonID)) if tmp[0] == '#add_grass:': @@ -1260,7 +1259,7 @@ def process_geometrycmds(geometry, G): volume.fractalsurfaces.append(surface) - if messages: + if config.general['messages']: tqdm.write('{} blades of grass on surface from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with fractal dimension {:g}, fractal seeding {}, and range {:g}m to {:g}m, added to {}.'.format(numblades, xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, surface.dimension, surface.seed, float(tmp[8]), float(tmp[9]), surface.operatingonID)) # Process any modifications to the original fractal box then generate it @@ -1301,7 +1300,7 @@ def process_geometrycmds(geometry, G): # If there is only 1 bin then a normal material is being used, otherwise a mixing model if volume.nbins == 1: - volume.fractalvolume = np.ones((volume.nx, volume.ny, volume.nz), dtype=floattype) + volume.fractalvolume = np.ones((volume.nx, volume.ny, volume.nz), dtype=config.dtypes['float_or_double']) materialnumID = next(x.numID for x in G.materials if x.ID == volume.operatingonID) volume.fractalvolume *= materialnumID else: diff --git a/gprMax/input_cmds_multiuse.py b/gprMax/input_cmds_multiuse.py index 75193dac..2a6ad7ae 100644 --- a/gprMax/input_cmds_multiuse.py +++ b/gprMax/input_cmds_multiuse.py @@ -20,30 +20,27 @@ from colorama import init from colorama import Fore from colorama import Style init() +from copy import deepcopy import numpy as np from tqdm import tqdm import gprMax.config as config -from gprMax.config import z0 -from gprMax.config import floattype -from gprMax.config import gpus -from gprMax.config import messages - -from gprMax.exceptions import CmdInputError -from gprMax.geometry_outputs import GeometryView -from gprMax.geometry_outputs import GeometryObjects -from gprMax.materials import Material -from gprMax.materials import PeplinskiSoil -from gprMax.pml import CFSParameter -from gprMax.pml import CFS -from gprMax.receivers import Rx -from gprMax.snapshots import Snapshot -from gprMax.sources import VoltageSource -from gprMax.sources import HertzianDipole -from gprMax.sources import MagneticDipole -from gprMax.sources import TransmissionLine -from gprMax.utilities import round_value -from gprMax.waveforms import Waveform +from .exceptions import CmdInputError +from .geometry_outputs import GeometryView +from .geometry_outputs import GeometryObjects +from .materials import DispersiveMaterial +from .materials import Material +from .materials import PeplinskiSoil +from .pml import CFSParameter +from .pml import CFS +from .receivers import Rx +from .snapshots import Snapshot +from .sources import VoltageSource +from .sources import HertzianDipole +from .sources import MagneticDipole +from .sources import TransmissionLine +from .utilities import round_value +from .waveforms import Waveform def process_multicmds(multicmds, G): @@ -84,7 +81,7 @@ def process_multicmds(multicmds, G): w.amp = float(tmp[1]) w.freq = float(tmp[2]) - if messages: + if config.general['messages']: print('Waveform {} of type {} with maximum amplitude scaling {:g}, frequency {:g}Hz created.'.format(w.ID, w.type, w.amp, w.freq)) G.waveforms.append(w) @@ -155,7 +152,7 @@ def process_multicmds(multicmds, G): v.calculate_waveform_values(G) - if messages: + if config.general['messages']: print('Voltage source with polarity {} at {:g}m, {:g}m, {:g}m, resistance {:.1f} Ohms,'.format(v.polarisation, v.xcoord * G.dx, v.ycoord * G.dy, v.zcoord * G.dz, v.resistance) + startstop + 'using waveform {} created.'.format(v.waveformID)) G.voltagesources.append(v) @@ -233,7 +230,7 @@ def process_multicmds(multicmds, G): h.calculate_waveform_values(G) - if messages: + if config.general['messages']: if '2D' in config.mode: print('Hertzian dipole is a line source in 2D with polarity {} at {:g}m, {:g}m, {:g}m,'.format(h.polarisation, h.xcoord * G.dx, h.ycoord * G.dy, h.zcoord * G.dz) + startstop + 'using waveform {} created.'.format(h.waveformID)) else: @@ -305,7 +302,7 @@ def process_multicmds(multicmds, G): m.calculate_waveform_values(G) - if messages: + if config.general['messages']: print('Magnetic dipole with polarity {} at {:g}m, {:g}m, {:g}m,'.format(m.polarisation, m.xcoord * G.dx, m.ycoord * G.dy, m.zcoord * G.dz) + startstop + 'using waveform {} created.'.format(m.waveformID)) G.magneticdipoles.append(m) @@ -341,7 +338,7 @@ def process_multicmds(multicmds, G): check_coordinates(xcoord, ycoord, zcoord) if xcoord < G.pmlthickness['x0'] or xcoord > G.nx - G.pmlthickness['xmax'] or ycoord < G.pmlthickness['y0'] or ycoord > G.ny - G.pmlthickness['ymax'] or zcoord < G.pmlthickness['z0'] or zcoord > G.nz - G.pmlthickness['zmax']: print(Fore.RED + "WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.' + Style.RESET_ALL) - if resistance <= 0 or resistance >= z0: + if resistance <= 0 or resistance >= config.z0: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a resistance greater than zero and less than the impedance of free space, i.e. 376.73 Ohms') # Check if there is a waveformID in the waveforms list @@ -381,7 +378,7 @@ def process_multicmds(multicmds, G): t.calculate_waveform_values(G) t.calculate_incident_V_I(G) - if messages: + if config.general['messages']: print('Transmission line with polarity {} at {:g}m, {:g}m, {:g}m, resistance {:.1f} Ohms,'.format(t.polarisation, t.xcoord * G.dx, t.ycoord * G.dy, t.zcoord * G.dz, t.resistance) + startstop + 'using waveform {} created.'.format(t.waveformID)) G.transmissionlines.append(t) @@ -413,23 +410,30 @@ def process_multicmds(multicmds, G): # If no ID or outputs are specified, use default if len(tmp) == 3: r.ID = r.__class__.__name__ + '(' + str(r.xcoord) + ',' + str(r.ycoord) + ',' + str(r.zcoord) + ')' + for key in Rx.defaultoutputs: - r.outputs[key] = np.zeros(G.iterations, dtype=floattype) + r.outputs[key] = np.zeros(G.iterations, dtype=config.dtypes['float_or_double']) else: r.ID = tmp[3] # Get allowable outputs - if gpus is not None: + if config.cuda['gpus'] is not None: allowableoutputs = Rx.gpu_allowableoutputs else: allowableoutputs = Rx.allowableoutputs - # Check and add field output names - for field in tmp[4::]: + # Check, sort and add field output names + fieldnames = tmp[4::] + fieldnames = sorted(fieldnames, key=lambda x: allowableoutputs.index(x)) + for field in fieldnames: if field in allowableoutputs: - r.outputs[field] = np.zeros(G.iterations, dtype=floattype) + r.outputs[field] = np.zeros(G.iterations, dtype=config.dtypes['float_or_double']) else: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' contains an output type that is not allowable. Allowable outputs in current context are {}'.format(allowableoutputs)) - if messages: + # Keep track of maximum number of field outputs on a receiver + if len(r.outputs) > Rx.maxnumoutputs: + Rx.maxnumoutputs = len(r.outputs) + + if config.general['messages']: print('Receiver at {:g}m, {:g}m, {:g}m with output component(s) {} created.'.format(r.xcoord * G.dx, r.ycoord * G.dy, r.zcoord * G.dz, ', '.join(r.outputs))) G.rxs.append(r) @@ -479,7 +483,7 @@ def process_multicmds(multicmds, G): else: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation') - if messages: + if config.general['messages']: print('Receiver array {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with steps {:g}m, {:g}m, {:g}m'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dy * G.dy, dz * G.dz)) for x in range(xs, xf + 1, dx): @@ -494,8 +498,8 @@ def process_multicmds(multicmds, G): r.zcoordorigin = z r.ID = r.__class__.__name__ + '(' + str(x) + ',' + str(y) + ',' + str(z) + ')' for key in Rx.defaultoutputs: - r.outputs[key] = np.zeros(G.iterations, dtype=floattype) - if messages: + r.outputs[key] = np.zeros(G.iterations, dtype=config.dtypes['float_or_double']) + if config.general['messages']: print(' Receiver at {:g}m, {:g}m, {:g}m with output component(s) {} created.'.format(r.xcoord * G.dx, r.ycoord * G.dy, r.zcoord * G.dz, ', '.join(r.outputs))) G.rxs.append(r) @@ -544,7 +548,7 @@ def process_multicmds(multicmds, G): s = Snapshot(xs, ys, zs, xf, yf, zf, dx, dy, dz, time, tmp[10]) - if messages: + if config.general['messages']: print('Snapshot from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, discretisation {:g}m, {:g}m, {:g}m, at {:g} secs with filename {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dy * G.dy, dz * G.dz, s.time * G.dt, s.basefilename)) G.snapshots.append(s) @@ -582,7 +586,7 @@ def process_multicmds(multicmds, G): if m.se == float('inf'): m.averagable = False - if messages: + if config.general['messages']: tqdm.write('Material {} with eps_r={:g}, sigma={:g} S/m; mu_r={:g}, sigma*={:g} Ohm/m created.'.format(m.ID, m.er, m.se, m.mr, m.sm)) # Append the new material object to the materials list @@ -608,21 +612,25 @@ def process_multicmds(multicmds, G): raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' material(s) {} do not exist'.format(notfound)) for material in materials: - material.type = 'debye' - material.poles = poles - material.averagable = False + dispersivemat = DispersiveMaterial(material.numID, material.ID) + dispersivemat.type = 'debye' + dispersivemat.poles = poles + dispersivemat.averagable = False for pole in range(1, 2 * poles, 2): # N.B Not checking if relaxation times are greater than time-step if float(tmp[pole]) > 0: - material.deltaer.append(float(tmp[pole])) - material.tau.append(float(tmp[pole + 1])) + dispersivemat.deltaer.append(float(tmp[pole])) + dispersivemat.tau.append(float(tmp[pole + 1])) else: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires positive values for the permittivity difference.') - if material.poles > Material.maxpoles: - Material.maxpoles = material.poles + if dispersivemat.poles > config.materials['maxpoles']: + config.materials['maxpoles'] = dispersivemat.poles - if messages: - tqdm.write('Debye disperion added to {} with delta_eps_r={}, and tau={} secs created.'.format(material.ID, ', '.join('%4.2f' % deltaer for deltaer in material.deltaer), ', '.join('%4.3e' % tau for tau in material.tau))) + # Replace original (non-dispersive) material with new dispersive instance + G.materials[material.numID] = dispersivemat + + if config.general['messages']: + tqdm.write('Debye disperion added to {} with delta_eps_r={}, and tau={} secs created.'.format(dispersivemat.ID, ', '.join('%4.2f' % deltaer for deltaer in dispersivemat.deltaer), ', '.join('%4.3e' % tau for tau in dispersivemat.tau))) cmdname = '#add_dispersion_lorentz' if multicmds[cmdname] is not None: @@ -654,10 +662,10 @@ def process_multicmds(multicmds, G): material.alpha.append(float(tmp[pole + 2])) else: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires positive values for the permittivity difference and frequencies, and associated times that are greater than the time step for the model.') - if material.poles > Material.maxpoles: - Material.maxpoles = material.poles + if material.poles > config.materials['maxpoles']: + config.materials['maxpoles'] = material.poles - if messages: + if config.general['messages']: tqdm.write('Lorentz disperion added to {} with delta_eps_r={}, omega={} secs, and gamma={} created.'.format(material.ID, ', '.join('%4.2f' % deltaer for deltaer in material.deltaer), ', '.join('%4.3e' % tau for tau in material.tau), ', '.join('%4.3e' % alpha for alpha in material.alpha))) cmdname = '#add_dispersion_drude' @@ -689,10 +697,10 @@ def process_multicmds(multicmds, G): material.alpha.append(float(tmp[pole + 1])) else: raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires positive values for the frequencies, and associated times that are greater than the time step for the model.') - if material.poles > Material.maxpoles: - Material.maxpoles = material.poles + if material.poles > config.materials['maxpoles']: + config.materials['maxpoles'] = material.poles - if messages: + if config.general['messages']: tqdm.write('Drude disperion added to {} with omega={} secs, and gamma={} secs created.'.format(material.ID, ', '.join('%4.3e' % tau for tau in material.tau), ', '.join('%4.3e' % alpha for alpha in material.alpha))) cmdname = '#soil_peplinski' @@ -719,7 +727,7 @@ def process_multicmds(multicmds, G): # Create a new instance of the Material class material (start index after pec & free_space) s = PeplinskiSoil(tmp[6], float(tmp[0]), float(tmp[1]), float(tmp[2]), float(tmp[3]), (float(tmp[4]), float(tmp[5]))) - if messages: + if config.general['messages']: print('Mixing model (Peplinski) used to create {} with sand fraction {:g}, clay fraction {:g}, bulk density {:g}g/cm3, sand particle density {:g}g/cm3, and water volumetric fraction {:g} to {:g} created.'.format(s.ID, s.S, s.C, s.rb, s.rs, s.mu[0], s.mu[1])) # Append the new material object to the materials list @@ -769,7 +777,7 @@ def process_multicmds(multicmds, G): g = GeometryView(xs, ys, zs, xf, yf, zf, dx, dy, dz, tmp[9], fileext) - if messages: + if config.general['messages']: print('Geometry view from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, discretisation {:g}m, {:g}m, {:g}m, with filename base {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dy * G.dy, dz * G.dz, g.basefilename)) # Append the new GeometryView object to the geometry views list @@ -799,7 +807,7 @@ def process_multicmds(multicmds, G): g = GeometryObjects(xs, ys, zs, xf, yf, zf, tmp[6]) - if messages: + if config.general['messages']: print('Geometry objects in the volume from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, will be written to {}, with materials written to {}'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, g.filename, g.materialsfilename)) # Append the new GeometryView object to the geometry objects to write list @@ -849,7 +857,7 @@ def process_multicmds(multicmds, G): cfs.kappa = cfskappa cfs.sigma = cfssigma - if messages: + if config.general['messages']: print('PML CFS parameters: alpha (scaling: {}, scaling direction: {}, min: {:g}, max: {:g}), kappa (scaling: {}, scaling direction: {}, min: {:g}, max: {:g}), sigma (scaling: {}, scaling direction: {}, min: {:g}, max: {}) created.'.format(cfsalpha.scalingprofile, cfsalpha.scalingdirection, cfsalpha.min, cfsalpha.max, cfskappa.scalingprofile, cfskappa.scalingdirection, cfskappa.min, cfskappa.max, cfssigma.scalingprofile, cfssigma.scalingdirection, cfssigma.min, cfssigma.max)) G.cfs.append(cfs) diff --git a/gprMax/input_cmds_singleuse.py b/gprMax/input_cmds_singleuse.py index f6d2b228..bd9ba1d5 100644 --- a/gprMax/input_cmds_singleuse.py +++ b/gprMax/input_cmds_singleuse.py @@ -29,16 +29,12 @@ import numpy as np from scipy import interpolate import gprMax.config as config -from gprMax.config import c -from gprMax.config import floattype -from gprMax.config import gpus as gpu -from gprMax.config import hostinfo -from gprMax.exceptions import CmdInputError -from gprMax.exceptions import GeneralError -from gprMax.pml import PML -from gprMax.utilities import human_size -from gprMax.utilities import round_value -from gprMax.waveforms import Waveform +from .exceptions import CmdInputError +from .exceptions import GeneralError +from .pml import PML +from .utilities import human_size +from .utilities import round_value +from .waveforms import Waveform def process_singlecmds(singlecmds, G): @@ -57,9 +53,9 @@ def process_singlecmds(singlecmds, G): if len(tmp) != 1: raise CmdInputError(cmd + ' requires exactly one parameter') if singlecmds[cmd].lower() == 'y': - config.messages = True + config.general['messages'] = True elif singlecmds[cmd].lower() == 'n': - config.messages = False + config.general['messages'] = False else: raise CmdInputError(cmd + ' requires input values of either y or n') @@ -73,7 +69,7 @@ def process_singlecmds(singlecmds, G): # os.environ['OMP_DISPLAY_ENV'] = 'TRUE' # Prints OMP version and environment variables (useful for debug) # Catch bug with Windows Subsystem for Linux (https://github.com/Microsoft/BashOnWindows/issues/785) - if 'Microsoft' in hostinfo['osversion']: + if 'Microsoft' in config.hostinfo['osversion']: os.environ['KMP_AFFINITY'] = 'disabled' del os.environ['OMP_PLACES'] del os.environ['OMP_PROC_BIND'] @@ -90,28 +86,28 @@ def process_singlecmds(singlecmds, G): config.hostinfo['ompthreads'] = int(os.environ.get('OMP_NUM_THREADS')) else: # Set number of threads to number of physical CPU cores - config.hostinfo['ompthreads'] = hostinfo['physicalcores'] + config.hostinfo['ompthreads'] = config.hostinfo['physicalcores'] os.environ['OMP_NUM_THREADS'] = str(config.hostinfo['ompthreads']) - if config.messages: + if config.general['messages']: print('CPU (OpenMP) threads: {}'.format(config.hostinfo['ompthreads'])) - if config.hostinfo['ompthreads'] > hostinfo['physicalcores']: - print(Fore.RED + 'WARNING: You have specified more threads ({}) than available physical CPU cores ({}). This may lead to degraded performance.'.format(config.hostinfo['ompthreads'], hostinfo['physicalcores']) + Style.RESET_ALL) + if config.hostinfo['ompthreads'] > config.hostinfo['physicalcores']: + print(Fore.RED + 'WARNING: You have specified more threads ({}) than available physical CPU cores ({}). This may lead to degraded performance.'.format(config.hostinfo['ompthreads'], config.hostinfo['physicalcores']) + Style.RESET_ALL) # Print information about any GPU in use - if config.messages: - if gpu is not None: - print('GPU: {} - {}'.format(gpu.deviceID, gpu.name)) + if config.general['messages']: + if config.cuda['gpus'] is not None: + print('GPU: {} - {}'.format(config.cuda['gpus'].deviceID, config.cuda['gpus'].name)) # Print information about precision of main field values - if config.messages: - print('Output data type: {}\n'.format(np.dtype(floattype).name)) + if config.general['messages']: + print('Output data type: {}\n'.format(np.dtype(config.dtypes['float_or_double']).name)) # Title cmd = '#title' if singlecmds[cmd] is not None: G.title = singlecmds[cmd] - if config.messages: + if config.general['messages']: print('Model title: {}'.format(G.title)) # Spatial discretisation @@ -128,7 +124,7 @@ def process_singlecmds(singlecmds, G): G.dx = tmp[0] G.dy = tmp[1] G.dz = tmp[2] - if config.messages: + if config.general['messages']: print('Spatial discretisation: {:g} x {:g} x {:g}m'.format(G.dx, G.dy, G.dz)) # Domain @@ -141,34 +137,34 @@ def process_singlecmds(singlecmds, G): G.nz = round_value(tmp[2] / G.dz) if G.nx == 0 or G.ny == 0 or G.nz == 0: raise CmdInputError(cmd + ' requires at least one cell in every dimension') - if config.messages: + if config.general['messages']: print('Domain size: {:g} x {:g} x {:g}m ({:d} x {:d} x {:d} = {:g} cells)'.format(tmp[0], tmp[1], tmp[2], G.nx, G.ny, G.nz, (G.nx * G.ny * G.nz))) # Time step CFL limit (either 2D or 3D); switch off appropriate PMLs for 2D if G.nx == 1: - G.dt = 1 / (c * np.sqrt((1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz))) + G.dt = 1 / (config.c * np.sqrt((1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz))) config.mode = '2D TMx' G.pmlthickness['x0'] = 0 G.pmlthickness['xmax'] = 0 elif G.ny == 1: - G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dz) * (1 / G.dz))) + G.dt = 1 / (config.c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dz) * (1 / G.dz))) config.mode = '2D TMy' G.pmlthickness['y0'] = 0 G.pmlthickness['ymax'] = 0 elif G.nz == 1: - G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy))) + G.dt = 1 / (config.c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy))) config.mode = '2D TMz' G.pmlthickness['z0'] = 0 G.pmlthickness['zmax'] = 0 else: - G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz))) + G.dt = 1 / (config.c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz))) config.mode = '3D' # Round down time step to nearest float with precision one less than hardware maximum. # Avoids inadvertently exceeding the CFL due to binary representation of floating point number. G.dt = round_value(G.dt, decimalplaces=d.getcontext().prec - 1) - if config.messages: + if config.general['messages']: print('Mode: {}'.format(config.mode)) print('Time step (at CFL limit): {:g} secs'.format(G.dt)) @@ -181,7 +177,7 @@ def process_singlecmds(singlecmds, G): if tmp[0] <= 0 or tmp[0] > 1: raise CmdInputError(cmd + ' requires the value of the time step stability factor to be between zero and one') G.dt = G.dt * tmp[0] - if config.messages: + if config.general['messages']: print('Time step (modified): {:g} secs'.format(G.dt)) # Time window @@ -206,7 +202,7 @@ def process_singlecmds(singlecmds, G): G.iterations = int(np.ceil(tmp / G.dt)) + 1 else: raise CmdInputError(cmd + ' must have a value greater than zero') - if config.messages: + if config.general['messages']: print('Time window: {:g} secs ({} iterations)'.format(G.timewindow, G.iterations)) # PML cells @@ -248,7 +244,7 @@ def process_singlecmds(singlecmds, G): G.srcsteps[0] = round_value(float(tmp[0]) / G.dx) G.srcsteps[1] = round_value(float(tmp[1]) / G.dy) G.srcsteps[2] = round_value(float(tmp[2]) / G.dz) - if config.messages: + if config.general['messages']: print('Simple sources will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.srcsteps[0] * G.dx, G.srcsteps[1] * G.dy, G.srcsteps[2] * G.dz)) # rx_steps @@ -260,7 +256,7 @@ def process_singlecmds(singlecmds, G): G.rxsteps[0] = round_value(float(tmp[0]) / G.dx) G.rxsteps[1] = round_value(float(tmp[1]) / G.dy) G.rxsteps[2] = round_value(float(tmp[2]) / G.dz) - if config.messages: + if config.general['messages']: print('All receivers will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.rxsteps[0] * G.dx, G.rxsteps[1] * G.dy, G.rxsteps[2] * G.dz)) # Excitation file for user-defined source waveforms @@ -284,7 +280,7 @@ def process_singlecmds(singlecmds, G): if not os.path.isfile(excitationfile): excitationfile = os.path.abspath(os.path.join(G.inputdirectory, excitationfile)) - if config.messages: + if config.general['messages']: print('\nExcitation file: {}'.format(excitationfile)) # Get waveform names @@ -292,7 +288,7 @@ def process_singlecmds(singlecmds, G): waveformIDs = f.readline().split() # Read all waveform values into an array - waveformvalues = np.loadtxt(excitationfile, skiprows=1, dtype=floattype) + waveformvalues = np.loadtxt(excitationfile, skiprows=1, dtype=config.dtypes['float_or_double']) # Time array (if specified) for interpolation, otherwise use simulation time if waveformIDs[0].lower() == 'time': @@ -326,7 +322,7 @@ def process_singlecmds(singlecmds, G): # Interpolate waveform values w.userfunc = interpolate.interp1d(waveformtime, singlewaveformvalues, **kwargs) - if config.messages: + if config.general['messages']: print('User waveform {} created using {} and, if required, interpolation parameters (kind: {}, fill value: {}).'.format(w.ID, timestr, kwargs['kind'], kwargs['fill_value'])) G.waveforms.append(w) @@ -334,5 +330,7 @@ def process_singlecmds(singlecmds, G): # Set the output directory cmd = '#output_dir' if singlecmds[cmd] is not None: - outputdir = singlecmds[cmd] - G.outputdirectory = outputdir + config.outputfilepath = singlecmds[cmd] + + if config.general['messages']: + print('Output directory: {}'.format(config.outputfilepath)) diff --git a/gprMax/materials.py b/gprMax/materials.py index cbbc29dc..0a82a5bc 100644 --- a/gprMax/materials.py +++ b/gprMax/materials.py @@ -18,28 +18,13 @@ import numpy as np -from gprMax.config import e0 -from gprMax.config import m0 -from gprMax.config import complextype +import gprMax.config as config class Material(object): - """Materials, their properties and update coefficients.""" - - # Maximum number of dispersive material poles in a model - maxpoles = 0 - - # Properties of water from: http://dx.doi.org/10.1109/TGRS.2006.873208 - waterer = 80.1 - watereri = 4.9 - waterdeltaer = waterer - watereri - watertau = 9.231e-12 - - # Properties of grass from: http://dx.doi.org/10.1007/BF00902994 - grasser = 18.5087 - grasseri = 12.7174 - grassdeltaer = grasser - grasseri - grasstau = 1.0793e-11 + """Super-class to describe generic, non-dispersive materials, + their properties and update coefficients. + """ def __init__(self, numID, ID): """ @@ -60,21 +45,16 @@ class Material(object): self.mr = 1.0 self.sm = 0.0 - # Parameters for dispersive materials - self.poles = 0 - self.deltaer = [] - self.tau = [] - self.alpha = [] - def calculate_update_coeffsH(self, G): """Calculates the magnetic update coefficients of the material. Args: - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. """ - HA = (m0 * self.mr / G.dt) + 0.5 * self.sm - HB = (m0 * self.mr / G.dt) - 0.5 * self.sm + HA = (config.m0 * self.mr / G.dt) + 0.5 * self.sm + HB = (config.m0 * self.mr / G.dt) - 0.5 * self.sm self.DA = HB / HA self.DBx = (1 / G.dx) * 1 / HA self.DBy = (1 / G.dy) * 1 / HA @@ -86,48 +66,11 @@ class Material(object): Args: G (class): Grid class instance - holds essential parameters - describing the model. + describing the model. """ - # The implementation of the dispersive material modelling comes from the - # derivation in: http://dx.doi.org/10.1109/TAP.2014.2308549 - if self.maxpoles > 0: - self.w = np.zeros(self.maxpoles, dtype=complextype) - self.q = np.zeros(self.maxpoles, dtype=complextype) - self.zt = np.zeros(self.maxpoles, dtype=complextype) - self.zt2 = np.zeros(self.maxpoles, dtype=complextype) - self.eqt = np.zeros(self.maxpoles, dtype=complextype) - self.eqt2 = np.zeros(self.maxpoles, dtype=complextype) - - for x in range(self.poles): - if 'debye' in self.type: - self.w[x] = self.deltaer[x] / self.tau[x] - self.q[x] = -1 / self.tau[x] - elif 'lorentz' in self.type: - # tau for Lorentz materials are pole frequencies - # alpha for Lorentz materials are the damping coefficients - wp2 = (2 * np.pi * self.tau[x])**2 - self.w[x] = -1j * ((wp2 * self.deltaer[x]) / np.sqrt(wp2 - self.alpha[x]**2)) - self.q[x] = -self.alpha[x] + (1j * np.sqrt(wp2 - self.alpha[x]**2)) - elif 'drude' in self.type: - # tau for Drude materials are pole frequencies - # alpha for Drude materials are the inverse of relaxation times - wp2 = (2 * np.pi * self.tau[x])**2 - self.se += wp2 / self.alpha[x] - self.w[x] = - (wp2 / self.alpha[x]) - self.q[x] = - self.alpha[x] - - self.eqt[x] = np.exp(self.q[x] * G.dt) - self.eqt2[x] = np.exp(self.q[x] * (G.dt / 2)) - self.zt[x] = (self.w[x] / self.q[x]) * (1 - self.eqt[x]) / G.dt - self.zt2[x] = (self.w[x] / self.q[x]) * (1 - self.eqt2[x]) - - EA = (e0 * self.er / G.dt) + 0.5 * self.se - (e0 / G.dt) * np.sum(self.zt2.real) - EB = (e0 * self.er / G.dt) - 0.5 * self.se - (e0 / G.dt) * np.sum(self.zt2.real) - - else: - EA = (e0 * self.er / G.dt) + 0.5 * self.se - EB = (e0 * self.er / G.dt) - 0.5 * self.se + EA = (config.e0 * self.er / G.dt) + 0.5 * self.se + EB = (config.e0 * self.er / G.dt) - 0.5 * self.se if self.ID == 'pec' or self.se == float('inf'): self.CA = 0 @@ -143,11 +86,100 @@ class Material(object): self.srce = 1 / EA def calculate_er(self, freq): - """ - Calculates the complex relative permittivity of the material at a specific frequency. + """Calculates the complex relative permittivity of the material at a + specific frequency. Args: - freq (float): Frequency used to calculate complex relative permittivity. + freq (float): Frequency used to calculate complex relative + permittivity. + + Returns: + er (float): Complex relative permittivity. + """ + + return self.er + + +class DispersiveMaterial(Material): + """Class to describe materials with frequency dependent properties, e.g. + Debye, Drude, Lorenz + """ + + # Properties of water from: http://dx.doi.org/10.1109/TGRS.2006.873208 + waterer = 80.1 + watereri = 4.9 + waterdeltaer = waterer - watereri + watertau = 9.231e-12 + + # Properties of grass from: http://dx.doi.org/10.1007/BF00902994 + grasser = 18.5087 + grasseri = 12.7174 + grassdeltaer = grasser - grasseri + grasstau = 1.0793e-11 + + def __init__(self, numID, ID): + super().__init__(numID, ID) + self.poles = 0 + self.deltaer = [] + self.tau = [] + self.alpha = [] + + def calculate_update_coeffsE(self, G): + """Calculates the electric update coefficients of the material. + + Args: + G (class): Grid class instance - holds essential parameters + describing the model. + """ + + # The implementation of the dispersive material modelling comes from the + # derivation in: http://dx.doi.org/10.1109/TAP.2014.2308549 + self.w = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) + self.q = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) + self.zt = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) + self.zt2 = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) + self.eqt = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) + self.eqt2 = np.zeros(config.materials['maxpoles'], dtype=config.materials['dispersivedtype']) + + for x in range(self.poles): + if 'debye' in self.type: + self.w[x] = self.deltaer[x] / self.tau[x] + self.q[x] = -1 / self.tau[x] + elif 'lorentz' in self.type: + # tau for Lorentz materials are pole frequencies + # alpha for Lorentz materials are the damping coefficients + wp2 = (2 * np.pi * self.tau[x])**2 + self.w[x] = -1j * ((wp2 * self.deltaer[x]) / np.sqrt(wp2 - self.alpha[x]**2)) + self.q[x] = -self.alpha[x] + (1j * np.sqrt(wp2 - self.alpha[x]**2)) + elif 'drude' in self.type: + # tau for Drude materials are pole frequencies + # alpha for Drude materials are the inverse of relaxation times + wp2 = (2 * np.pi * self.tau[x])**2 + self.se += wp2 / self.alpha[x] + self.w[x] = - (wp2 / self.alpha[x]) + self.q[x] = - self.alpha[x] + + self.eqt[x] = np.exp(self.q[x] * G.dt) + self.eqt2[x] = np.exp(self.q[x] * (G.dt / 2)) + self.zt[x] = (self.w[x] / self.q[x]) * (1 - self.eqt[x]) / G.dt + self.zt2[x] = (self.w[x] / self.q[x]) * (1 - self.eqt2[x]) + + EA = (config.e0 * self.er / G.dt) + 0.5 * self.se - (config.e0 / G.dt) * np.sum(self.zt2.real) + EB = (config.e0 * self.er / G.dt) - 0.5 * self.se - (config.e0 / G.dt) * np.sum(self.zt2.real) + + self.CA = EB / EA + self.CBx = (1 / G.dx) * 1 / EA + self.CBy = (1 / G.dy) * 1 / EA + self.CBz = (1 / G.dz) * 1 / EA + self.srce = 1 / EA + + def calculate_er(self, freq): + """Calculates the complex relative permittivity of the material at a + specific frequency. + + Args: + freq (float): Frequency used to calculate complex relative + permittivity. Returns: er (float): Complex relative permittivity. @@ -156,55 +188,60 @@ class Material(object): # Permittivity at infinite frequency if the material is dispersive er = self.er - if self.poles > 0: - w = 2 * np.pi * freq - er += self.se / (w * e0) - if 'debye' in self.type: - for pole in range(self.poles): - er += self.deltaer[pole] / (1 + 1j * w * self.tau[pole]) - elif 'lorentz' in self.type: - for pole in range(self.poles): - er += (self.deltaer[pole] * self.tau[pole]**2) / (self.tau[pole]**2 + 2j * w * self.alpha[pole] - w**2) - elif 'drude' in self.type: - ersum = 0 - for pole in range(self.poles): - ersum += self.tau[pole]**2 / (w**2 - 1j * w * self.alpha[pole]) - er -= ersum + w = 2 * np.pi * freq + er += self.se / (w * config.e0) + if 'debye' in self.type: + for pole in range(self.poles): + er += self.deltaer[pole] / (1 + 1j * w * self.tau[pole]) + elif 'lorentz' in self.type: + for pole in range(self.poles): + er += ((self.deltaer[pole] * self.tau[pole]**2) + / (self.tau[pole]**2 + 2j * w * self.alpha[pole] - w**2)) + elif 'drude' in self.type: + ersum = 0 + for pole in range(self.poles): + ersum += self.tau[pole]**2 / (w**2 - 1j * w * self.alpha[pole]) + er -= ersum return er def process_materials(G): - """ - Process complete list of materials - calculate update coefficients, - store in arrays, and build text list of materials/properties + """Process complete list of materials - calculate update coefficients, + store in arrays, and build text list of materials/properties Args: - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. Returns: - materialsdata (list): List of material IDs, names, and properties to print a table. + materialsdata (list): List of material IDs, names, and properties to + print a table. """ - if Material.maxpoles == 0: - materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]', '\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']] + if config.materials['maxpoles'] == 0: + materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]', + '\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']] else: - materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]', 'Delta\neps_r', 'tau\n[s]', 'omega\n[Hz]', 'delta\n[Hz]', 'gamma\n[Hz]', '\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']] + materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]', + 'Delta\neps_r', 'tau\n[s]', 'omega\n[Hz]', 'delta\n[Hz]', + 'gamma\n[Hz]', '\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']] for material in G.materials: - # Calculate update coefficients for material + # Calculate update coefficients for specific material material.calculate_update_coeffsE(G) material.calculate_update_coeffsH(G) - # Store all update coefficients together + # Add update coefficients to overall storage for all materials G.updatecoeffsE[material.numID, :] = material.CA, material.CBx, material.CBy, material.CBz, material.srce G.updatecoeffsH[material.numID, :] = material.DA, material.DBx, material.DBy, material.DBz, material.srcm - # Store coefficients for any dispersive materials - if Material.maxpoles > 0: + # Add update coefficients to overall storage for dispersive materials + if hasattr(material, 'poles'): z = 0 - for pole in range(Material.maxpoles): - G.updatecoeffsdispersive[material.numID, z:z + 3] = e0 * material.eqt2[pole], material.eqt[pole], material.zt[pole] + for pole in range(config.materials['maxpoles']): + G.updatecoeffsdispersive[material.numID, z:z + 3] = (config.e0 + * material.eqt2[pole], material.eqt[pole], material.zt[pole]) z += 3 # Construct information on material properties for printing table @@ -214,7 +251,7 @@ def process_materials(G): materialtext.append(material.type) materialtext.append('{:g}'.format(material.er)) materialtext.append('{:g}'.format(material.se)) - if Material.maxpoles > 0: + if config.materials['maxpoles'] > 0: if 'debye' in material.type: materialtext.append('\n'.join('{:g}'.format(deltaer) for deltaer in material.deltaer)) materialtext.append('\n'.join('{:g}'.format(tau) for tau in material.tau)) @@ -242,9 +279,8 @@ def process_materials(G): class PeplinskiSoil(object): - """ - Soil objects that are characterised according to a mixing - model by Peplinski (http://dx.doi.org/10.1109/36.387598). + """Soil objects that are characterised according to a mixing model + by Peplinski (http://dx.doi.org/10.1109/36.387598). """ def __init__(self, ID, sandfraction, clayfraction, bulkdensity, sandpartdensity, watervolfraction): @@ -254,8 +290,10 @@ class PeplinskiSoil(object): sandfraction (float): Sand fraction of the soil. clayfraction (float): Clay fraction of the soil. bulkdensity (float): Bulk density of the soil (g/cm3). - sandpartdensity (float): Density of the sand particles in the soil (g/cm3). - watervolfraction (float): Two numbers that specify a range for the volumetric water fraction of the soil. + sandpartdensity (float): Density of the sand particles in the + soil (g/cm3). + watervolfraction (float): Two numbers that specify a range for the + volumetric water fraction of the soil. """ self.ID = ID @@ -267,9 +305,9 @@ class PeplinskiSoil(object): self.startmaterialnum = 0 def calculate_debye_properties(self, nbins, G): - """ - Calculates the real and imaginery part of a Debye model for the soil as - well as a conductivity. It uses an approximation to a semi-empirical model (http://dx.doi.org/10.1109/36.387598). + """Calculates the real and imaginery part of a Debye model for the soil + as well as a conductivity. It uses an approximation to a semi-empirical + model (http://dx.doi.org/10.1109/36.387598). Args: nbins (int): Number of bins to use to create the different materials. @@ -279,7 +317,8 @@ class PeplinskiSoil(object): # Debye model properties of water f = 1.3e9 w = 2 * np.pi * f - erealw = Material.watereri + ((Material.waterdeltaer) / (1 + (w * Material.watertau)**2)) + erealw = DispersiveMaterial.watereri + ((DispersiveMaterial.waterdeltaer) + / (1 + (w * DispersiveMaterial.watertau)**2)) a = 0.65 # Experimentally derived constant es = (1.01 + 0.44 * self.rs)**2 - 0.062 #  Relative permittivity of sand particles @@ -300,12 +339,13 @@ class PeplinskiSoil(object): muiter = np.nditer(mumaterials, flags=['c_index']) while not muiter.finished: # Real part for frequencies in the range 1.4GHz to 18GHz - er = (1 + (self.rb / self.rs) * ((es**a) - 1) + (muiter[0]**b1 * erealw**a) - muiter[0]) ** (1 / a) + er = (1 + (self.rb / self.rs) * ((es**a) - 1) + (muiter[0]**b1 * erealw**a) + - muiter[0]) ** (1 / a) # Real part for frequencies in the range 0.3GHz to 1.3GHz (linear correction to 1.4-18GHz value) er = 1.15 * er - 0.68 # Permittivity at infinite frequency - eri = er - (muiter[0]**(b2 / a) * Material.waterdeltaer) + eri = er - (muiter[0]**(b2 / a) * DispersiveMaterial.waterdeltaer) # Effective conductivity sig = muiter[0]**(b2 / a) * ((sigf * (self.rs - self.rb)) / (self.rs * muiter[0])) @@ -319,16 +359,16 @@ class PeplinskiSoil(object): else: self.startmaterialnum = len(G.materials) if not material: - m = Material(len(G.materials), requiredID) + m = DispersiveMaterial(len(G.materials), requiredID) m.type = 'debye' m.averagable = False m.poles = 1 - if m.poles > Material.maxpoles: - Material.maxpoles = m.poles + if m.poles > config.materials['maxpoles']: + config.materials['maxpoles'] = m.poles m.er = eri m.se = sig m.deltaer.append(er - eri) - m.tau.append(Material.watertau) + m.tau.append(DispersiveMaterial.watertau) G.materials.append(m) muiter.iternext() diff --git a/gprMax/model_build_run.py b/gprMax/model_build_run.py index 980c3054..09375e26 100644 --- a/gprMax/model_build_run.py +++ b/gprMax/model_build_run.py @@ -32,58 +32,49 @@ from terminaltables import SingleTable from tqdm import tqdm import gprMax.config as config -from gprMax.config import floattype -from gprMax.config import complextype -from gprMax.config import cudafloattype -from gprMax.config import cudacomplextype -from gprMax.config import numdispersion -from gprMax.config import hostinfo -from gprMax.config import messages -from gprMax.config import progressbars -from gprMax.config import snapsgpu2cpu -from gprMax.exceptions import GeneralError - -from gprMax.fields_outputs import store_outputs -from gprMax.fields_outputs import kernel_template_store_outputs -from gprMax.fields_outputs import write_hdf5_outputfile - -from gprMax.cython.fields_updates import update_electric -from gprMax.cython.fields_updates import update_magnetic -from gprMax.cython.fields_updates import update_electric_dispersive_multipole_A -from gprMax.cython.fields_updates import update_electric_dispersive_multipole_B -from gprMax.cython.fields_updates import update_electric_dispersive_1pole_A -from gprMax.cython.fields_updates import update_electric_dispersive_1pole_B -from gprMax.cuda.fields_updates import kernels_template_fields - -from gprMax.grid import FDTDGrid -from gprMax.grid import dispersion_analysis - -from gprMax.input_cmds_geometry import process_geometrycmds -from gprMax.input_cmds_file import process_python_include_code -from gprMax.input_cmds_file import write_processed_file -from gprMax.input_cmds_file import check_cmd_names -from gprMax.input_cmds_singleuse import process_singlecmds -from gprMax.input_cmds_multiuse import process_multicmds -from gprMax.materials import Material -from gprMax.materials import process_materials -from gprMax.pml import CFS -from gprMax.pml import PML -from gprMax.pml import build_pmls -from gprMax.receivers import gpu_initialise_rx_arrays -from gprMax.receivers import gpu_get_rx_array -from gprMax.snapshots import Snapshot -from gprMax.snapshots import gpu_initialise_snapshot_array -from gprMax.snapshots import gpu_get_snapshot_array -from gprMax.cuda.snapshots import kernel_template_store_snapshot -from gprMax.sources import gpu_initialise_src_arrays -from gprMax.cuda.source_updates import kernels_template_sources -from gprMax.utilities import get_terminal_width -from gprMax.utilities import human_size -from gprMax.utilities import open_path_file -from gprMax.utilities import round32 -from gprMax.utilities import timer -from gprMax.cython.yee_cell_build import build_electric_components -from gprMax.cython.yee_cell_build import build_magnetic_components +from .cuda.fields_updates import kernel_template_fields +from .cuda.source_updates import kernel_template_sources +from .cython.fields_updates_normal import update_electric +from .cython.fields_updates_normal import update_magnetic +from .cython.fields_updates_dispersive import update_electric_dispersive_1pole_A +from .cython.fields_updates_dispersive import update_electric_dispersive_1pole_B +from .cython.fields_updates_dispersive import update_electric_dispersive_multipole_A +from .cython.fields_updates_dispersive import update_electric_dispersive_multipole_B +from .cython.fields_updates_dispersive import update_electric_dispersive_debye_1pole_A +from .cython.fields_updates_dispersive import update_electric_dispersive_debye_1pole_B +from .cython.fields_updates_dispersive import update_electric_dispersive_debye_multipole_A +from .cython.fields_updates_dispersive import update_electric_dispersive_debye_multipole_B +from .cython.yee_cell_build import build_electric_components +from .cython.yee_cell_build import build_magnetic_components +from .exceptions import GeneralError +from .fields_outputs import store_outputs +from .fields_outputs import kernel_template_store_outputs +from .fields_outputs import write_hdf5_outputfile +from .grid import FDTDGrid +from .grid import dispersion_analysis +from .input_cmds_geometry import process_geometrycmds +from .input_cmds_file import process_python_include_code +from .input_cmds_file import write_processed_file +from .input_cmds_file import check_cmd_names +from .input_cmds_singleuse import process_singlecmds +from .input_cmds_multiuse import process_multicmds +from .materials import Material +from .materials import process_materials +from .pml import CFS +from .pml import PML +from .pml import build_pmls +from .receivers import gpu_initialise_rx_arrays +from .receivers import gpu_get_rx_array +from .receivers import Rx +from .snapshots import Snapshot +from .snapshots import gpu_initialise_snapshot_array +from .snapshots import gpu_get_snapshot_array +from .sources import gpu_initialise_src_arrays +from .utilities import get_terminal_width +from .utilities import human_size +from .utilities import open_path_file +from .utilities import round32 +from .utilities import timer def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usernamespace): @@ -117,10 +108,10 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern # Initialise an instance of the FDTDGrid class G = FDTDGrid() - G.inputfilename = os.path.split(inputfile.name)[1] - G.inputdirectory = os.path.dirname(os.path.abspath(inputfile.name)) + config.inputfilepath = os.path.realpath(inputfile.name) + config.outputfilepath = os.path.dirname(os.path.abspath(config.inputfilepath)) inputfilestr = '\n--- Model {}/{}, input file: {}'.format(currentmodelrun, modelend, inputfile.name) - if messages: + if config.general['messages']: print(Fore.GREEN + '{} {}\n'.format(inputfilestr, '-' * (get_terminal_width() - 1 - len(inputfilestr))) + Style.RESET_ALL) # Add the current model run to namespace that can be accessed by @@ -135,12 +126,12 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern for key, value in sorted(usernamespace.items()): if key != '__builtins__': uservars += '{}: {}, '.format(key, value) - if messages: + if config.general['messages']: print('Constants/variables used/available for Python scripting: {{{}}}\n'.format(uservars[:-2])) # Write a file containing the input commands after Python or include file commands have been processed if args.write_processed: - write_processed_file(processedlines, appendmodelnumber, G) + write_processed_file(processedlines, appendmodelnumber) # Check validity of command names and that essential commands are present singlecmds, multicmds, geometry = check_cmd_names(processedlines) @@ -157,18 +148,17 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern # Process parameters for commands that can only occur once in the model process_singlecmds(singlecmds, G) - from gprMax.config import mode # Process parameters for commands that can occur multiple times in the model - if messages: print() + if config.general['messages']: print() process_multicmds(multicmds, G) # Estimate and check memory (RAM) usage G.memory_estimate_basic() G.memory_check() - if messages: + if config.general['messages']: memGPU = '' - if config.gpus: + if config.cuda['gpus']: memGPU = ' host + ~{} GPU'.format(human_size(G.memoryusage)) print('\nMemory (RAM) required: ~{}{}\n'.format(human_size(G.memoryusage), memGPU)) @@ -184,16 +174,16 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern process_geometrycmds(geometry, G) # Build the PMLs and calculate initial coefficients - if messages: print() + if config.general['messages']: print() if all(value == 0 for value in G.pmlthickness.values()): - if messages: + if config.general['messages']: print('PML: switched off') pass # If all the PMLs are switched off don't need to build anything else: # Set default CFS parameters for PML if not given if not G.cfs: G.cfs = [CFS()] - if messages: + if config.general['messages']: if all(value == G.pmlthickness['x0'] for value in G.pmlthickness.values()): pmlinfo = str(G.pmlthickness['x0']) else: @@ -202,14 +192,14 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern pmlinfo += '{}: {}, '.format(key, value) pmlinfo = pmlinfo[:-2] + ' cells' print('PML: formulation: {}, order: {}, thickness: {}'.format(G.pmlformulation, len(G.cfs), pmlinfo)) - pbar = tqdm(total=sum(1 for value in G.pmlthickness.values() if value > 0), desc='Building PML boundaries', ncols=get_terminal_width() - 1, file=sys.stdout, disable=not progressbars) + pbar = tqdm(total=sum(1 for value in G.pmlthickness.values() if value > 0), desc='Building PML boundaries', ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']) build_pmls(G, pbar) pbar.close() # Build the model, i.e. set the material properties (ID) for every edge # of every Yee cell - if messages: print() - pbar = tqdm(total=2, desc='Building main grid', ncols=get_terminal_width() - 1, file=sys.stdout, disable=not progressbars) + if config.general['messages']: print() + pbar = tqdm(total=2, desc='Building main grid', ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']) build_electric_components(G.solid, G.rigidE, G.ID, G) pbar.update() build_magnetic_components(G.solid, G.rigidH, G.ID, G) @@ -218,19 +208,19 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern # Add PEC boundaries to invariant direction in 2D modes # N.B. 2D modes are a single cell slice of 3D grid - if '2D TMx' in mode: + if '2D TMx' in config.mode: # Ey & Ez components G.ID[1, 0, :, :] = 0 G.ID[1, 1, :, :] = 0 G.ID[2, 0, :, :] = 0 G.ID[2, 1, :, :] = 0 - elif '2D TMy' in mode: + elif '2D TMy' in config.mode: # Ex & Ez components G.ID[0, :, 0, :] = 0 G.ID[0, :, 1, :] = 0 G.ID[2, :, 0, :] = 0 G.ID[2, :, 1, :] = 0 - elif '2D TMz' in mode: + elif '2D TMz' in config.mode: # Ex & Ey components G.ID[0, :, :, 0] = 0 G.ID[0, :, :, 1] = 0 @@ -245,16 +235,23 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern # Initialise arrays of update coefficients to pass to update functions G.initialise_std_update_coeff_arrays() - # Initialise arrays of update coefficients and temporary values if - # there are any dispersive materials - if Material.maxpoles != 0: + # Set datatype for dispersive arrays if there are any dispersive materials. + if config.materials['maxpoles'] != 0: + drudelorentz = any([m for m in G.materials if 'drude' in m.type or 'lorentz' in m.type]) + if drudelorentz: + config.materials['dispersivedtype'] = config.dtypes['complex'] + config.materials['dispersiveCdtype'] = config.dtypes['C_complex'] + else: + config.materials['dispersivedtype'] = config.dtypes['float_or_double'] + config.materials['dispersiveCdtype'] = config.dtypes['C_float_or_double'] + # Update estimated memory (RAM) usage - G.memoryusage += int(3 * Material.maxpoles * (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * np.dtype(complextype).itemsize) + G.memoryusage += int(3 * config.materials['maxpoles'] * (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * np.dtype(config.materials['dispersivedtype']).itemsize) G.memory_check() - if messages: + if config.general['messages']: print('\nMemory (RAM) required - updated (dispersive): ~{}\n'.format(human_size(G.memoryusage))) - G.initialise_dispersive_arrays() + G.initialise_dispersive_arrays(config.materials['dispersivedtype']) # Check there is sufficient memory to store any snapshots if G.snapshots: @@ -264,13 +261,13 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern snapsmemsize += (2 * snap.datasizefield) G.memoryusage += int(snapsmemsize) G.memory_check(snapsmemsize=int(snapsmemsize)) - if messages: + if config.general['messages']: print('\nMemory (RAM) required - updated (snapshots): ~{}\n'.format(human_size(G.memoryusage))) # Process complete list of materials - calculate update coefficients, # store in arrays, and build text list of materials/properties materialsdata = process_materials(G) - if messages: + if config.general['messages']: print('\nMaterials:') materialstable = SingleTable(materialsdata) materialstable.outer_border = False @@ -279,19 +276,19 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern # Check to see if numerical dispersion might be a problem results = dispersion_analysis(G) - if results['error'] and messages: + if results['error'] and config.general['messages']: print(Fore.RED + "\nWARNING: Numerical dispersion analysis not carried out as {}".format(results['error']) + Style.RESET_ALL) - elif results['N'] < numdispersion['mingridsampling']: + elif results['N'] < config.numdispersion['mingridsampling']: raise GeneralError("Non-physical wave propagation: Material '{}' has wavelength sampled by {} cells, less than required minimum for physical wave propagation. Maximum significant frequency estimated as {:g}Hz".format(results['material'].ID, results['N'], results['maxfreq'])) - elif results['deltavp'] and np.abs(results['deltavp']) > numdispersion['maxnumericaldisp'] and messages: + elif results['deltavp'] and np.abs(results['deltavp']) > config.numdispersion['maxnumericaldisp'] and config.general['messages']: print(Fore.RED + "\nWARNING: Potentially significant numerical dispersion. Estimated largest physical phase-velocity error is {:.2f}% in material '{}' whose wavelength sampled by {} cells. Maximum significant frequency estimated as {:g}Hz".format(results['deltavp'], results['material'].ID, results['N'], results['maxfreq']) + Style.RESET_ALL) - elif results['deltavp'] and messages: + elif results['deltavp'] and config.general['messages']: print("\nNumerical dispersion analysis: estimated largest physical phase-velocity error is {:.2f}% in material '{}' whose wavelength sampled by {} cells. Maximum significant frequency estimated as {:g}Hz".format(results['deltavp'], results['material'].ID, results['N'], results['maxfreq'])) # If geometry information to be reused between model runs else: inputfilestr = '\n--- Model {}/{}, input file (not re-processed, i.e. geometry fixed): {}'.format(currentmodelrun, modelend, inputfile.name) - if messages: + if config.general['messages']: print(Fore.GREEN + '{} {}\n'.format(inputfilestr, '-' * (get_terminal_width() - 1 - len(inputfilestr))) + Style.RESET_ALL) # Clear arrays for field components @@ -320,20 +317,18 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern receiver.zcoord = receiver.zcoordorigin + (currentmodelrun - 1) * G.rxsteps[2] # Write files for any geometry views and geometry object outputs - if not (G.geometryviews or G.geometryobjectswrite) and args.geometry_only and messages: + if not (G.geometryviews or G.geometryobjectswrite) and args.geometry_only and config.general['messages']: print(Fore.RED + '\nWARNING: No geometry views or geometry objects to output found.' + Style.RESET_ALL) - if G.geometryviews: - if messages: print() - for i, geometryview in enumerate(G.geometryviews): - geometryview.set_filename(appendmodelnumber, G) - pbar = tqdm(total=geometryview.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry view file {}/{}, {}'.format(i + 1, len(G.geometryviews), os.path.split(geometryview.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not progressbars) - geometryview.write_vtk(G, pbar) - pbar.close() - if G.geometryobjectswrite: - for i, geometryobject in enumerate(G.geometryobjectswrite): - pbar = tqdm(total=geometryobject.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry object file {}/{}, {}'.format(i + 1, len(G.geometryobjectswrite), os.path.split(geometryobject.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not progressbars) - geometryobject.write_hdf5(G, pbar) - pbar.close() + if config.general['messages']: print() + for i, geometryview in enumerate(G.geometryviews): + geometryview.set_filename(appendmodelnumber) + pbar = tqdm(total=geometryview.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry view file {}/{}, {}'.format(i + 1, len(G.geometryviews), os.path.split(geometryview.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']) + geometryview.write_vtk(G, pbar) + pbar.close() + for i, geometryobject in enumerate(G.geometryobjectswrite): + pbar = tqdm(total=geometryobject.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry object file {}/{}, {}'.format(i + 1, len(G.geometryobjectswrite), os.path.split(geometryobject.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']) + geometryobject.write_hdf5(G, pbar) + pbar.close() # If only writing geometry information if args.geometry_only: @@ -341,29 +336,17 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern # Run simulation else: - # Output filename - inputdirectory, inputfilename = os.path.split(os.path.join(G.inputdirectory, G.inputfilename)) - if G.outputdirectory is None: - outputdir = inputdirectory - else: - outputdir = G.outputdirectory - # Save current directory - curdir = os.getcwd() - os.chdir(inputdirectory) - outputdir = os.path.abspath(outputdir) - if not os.path.isdir(outputdir): - os.mkdir(outputdir) - if messages: - print('\nCreated output directory: {}'.format(outputdir)) - # Restore current directory - os.chdir(curdir) - basename, ext = os.path.splitext(inputfilename) - outputfile = os.path.join(outputdir, basename + appendmodelnumber + '.out') - if messages: + # Check and set output directory and filename + if not os.path.isdir(config.outputfilepath): + os.mkdir(config.outputfilepath) + if config.general['messages']: + print('\nCreated output directory: {}'.format(config.outputfilepath)) + outputfile = os.path.join(config.outputfilepath, os.path.splitext(os.path.split(config.inputfilepath)[1])[0] + appendmodelnumber + '.out') + if config.general['messages']: print('\nOutput file: {}\n'.format(outputfile)) # Main FDTD solving functions for either CPU or GPU - if config.gpus is None: + if config.cuda['gpus'] is None: tsolve = solve_cpu(currentmodelrun, modelend, G) else: tsolve, memsolve = solve_gpu(currentmodelrun, modelend, G) @@ -374,21 +357,21 @@ def run_model(args, currentmodelrun, modelend, numbermodelruns, inputfile, usern # Write any snapshots to file if G.snapshots: # Create directory and construct filename from user-supplied name and model run number - snapshotdir = os.path.join(G.inputdirectory, os.path.splitext(G.inputfilename)[0] + '_snaps' + appendmodelnumber) + snapshotdir = os.path.splitext(config.inputfilepath)[0] + '_snaps' + appendmodelnumber if not os.path.exists(snapshotdir): os.mkdir(snapshotdir) - if messages: print() + if config.general['messages']: print() for i, snap in enumerate(G.snapshots): snap.filename = os.path.abspath(os.path.join(snapshotdir, snap.basefilename + '.vti')) - pbar = tqdm(total=snap.vtkdatawritesize, leave=True, unit='byte', unit_scale=True, desc='Writing snapshot file {} of {}, {}'.format(i + 1, len(G.snapshots), os.path.split(snap.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not progressbars) + pbar = tqdm(total=snap.vtkdatawritesize, leave=True, unit='byte', unit_scale=True, desc='Writing snapshot file {} of {}, {}'.format(i + 1, len(G.snapshots), os.path.split(snap.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']) snap.write_vtk_imagedata(pbar, G) pbar.close() - if messages: print() + if config.general['messages']: print() - if messages: + if config.general['messages']: memGPU = '' - if config.gpus: + if config.cuda['gpus']: memGPU = ' host + ~{} GPU'.format(human_size(memsolve)) print('\nMemory (RAM) used: ~{}{}'.format(human_size(p.memory_full_info().uss), memGPU)) print('Solving time [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=tsolve))) @@ -415,9 +398,26 @@ def solve_cpu(currentmodelrun, modelend, G): tsolve (float): Time taken to execute solving (seconds) """ + # Set update functions if there are dispersive materials + if config.materials['maxpoles'] != 0: + if config.materials['dispersivedtype'] == config.dtypes['complex']: + if config.materials['maxpoles'] == 1: + update_electric_dispersive_A = update_electric_dispersive_1pole_A + update_electric_dispersive_B = update_electric_dispersive_1pole_B + elif config.materials['maxpoles'] > 1: + update_electric_dispersive_A = update_electric_dispersive_multipole_A + update_electric_dispersive_B = update_electric_dispersive_multipole_B + elif config.materials['dispersivedtype'] == config.dtypes['float_or_double']: + if config.materials['maxpoles'] == 2: + update_electric_dispersive_A = update_electric_dispersive_debye_1pole_A + update_electric_dispersive_B = update_electric_dispersive_debye_1pole_B + elif config.materials['maxpoles'] == 1: + update_electric_dispersive_A = update_electric_dispersive_debye_multipole_A + update_electric_dispersive_B = update_electric_dispersive_debye_multipole_B + tsolvestart = timer() - for iteration in tqdm(range(G.iterations), desc='Running simulation, model ' + str(currentmodelrun) + '/' + str(modelend), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not progressbars): + for iteration in tqdm(range(G.iterations), desc='Running simulation, model ' + str(currentmodelrun) + '/' + str(modelend), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']): # Store field component values for every receiver and transmission line store_outputs(iteration, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz, G) @@ -427,7 +427,7 @@ def solve_cpu(currentmodelrun, modelend, G): snap.store(G) # Update magnetic field components - update_magnetic(G.nx, G.ny, G.nz, hostinfo['ompthreads'], G.updatecoeffsH, G.ID, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz) + update_magnetic(G.nx, G.ny, G.nz, config.hostinfo['ompthreads'], G.updatecoeffsH, G.ID, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz) # Update magnetic field components with the PML correction for pml in G.pmls: @@ -439,14 +439,12 @@ def solve_cpu(currentmodelrun, modelend, G): # Update electric field components # All materials are non-dispersive so do standard update - if Material.maxpoles == 0: - update_electric(G.nx, G.ny, G.nz, hostinfo['ompthreads'], G.updatecoeffsE, G.ID, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz) + if config.materials['maxpoles'] == 0: + update_electric(G.nx, G.ny, G.nz, config.hostinfo['ompthreads'], G.updatecoeffsE, G.ID, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz) # If there are any dispersive materials do 1st part of dispersive update # (it is split into two parts as it requires present and updated electric field values). - elif Material.maxpoles == 1: - update_electric_dispersive_1pole_A(G.nx, G.ny, G.nz, hostinfo['ompthreads'], G.updatecoeffsE, G.updatecoeffsdispersive, G.ID, G.Tx, G.Ty, G.Tz, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz) - elif Material.maxpoles > 1: - update_electric_dispersive_multipole_A(G.nx, G.ny, G.nz, hostinfo['ompthreads'], Material.maxpoles, G.updatecoeffsE, G.updatecoeffsdispersive, G.ID, G.Tx, G.Ty, G.Tz, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz) + else: + update_electric_dispersive_A(G.nx, G.ny, G.nz, config.hostinfo['ompthreads'], config.materials['maxpoles'], G.updatecoeffsE, G.updatecoeffsdispersive, G.ID, G.Tx, G.Ty, G.Tz, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz) # Update electric field components with the PML correction for pml in G.pmls: @@ -460,10 +458,8 @@ def solve_cpu(currentmodelrun, modelend, G): # (it is split into two parts as it requires present and updated electric # field values). Therefore it can only be completely updated after the # electric field has been updated by the PML and source updates. - if Material.maxpoles == 1: - update_electric_dispersive_1pole_B(G.nx, G.ny, G.nz, hostinfo['ompthreads'], G.updatecoeffsdispersive, G.ID, G.Tx, G.Ty, G.Tz, G.Ex, G.Ey, G.Ez) - elif Material.maxpoles > 1: - update_electric_dispersive_multipole_B(G.nx, G.ny, G.nz, hostinfo['ompthreads'], Material.maxpoles, G.updatecoeffsdispersive, G.ID, G.Tx, G.Ty, G.Tz, G.Ex, G.Ey, G.Ez) + if config.materials['maxpoles'] != 0: + update_electric_dispersive_B(G.nx, G.ny, G.nz, config.hostinfo['ompthreads'], config.materials['maxpoles'], G.updatecoeffsdispersive, G.ID, G.Tx, G.Ty, G.Tz, G.Ex, G.Ey, G.Ez) tsolve = timer() - tsolvestart @@ -494,32 +490,30 @@ def solve_gpu(currentmodelrun, modelend, G): compiler_opts = None # Create device handle and context on specifc GPU device (and make it current context) - dev = drv.Device(config.gpus.deviceID) + dev = drv.Device(config.cuda['gpus'].deviceID) ctx = dev.make_context() - # Electric and magnetic field updates - prepare kernels, and get kernel functions - if Material.maxpoles > 0: - kernels_fields = SourceModule(kernels_template_fields.substitute(REAL=cudafloattype, COMPLEX=cudacomplextype, N_updatecoeffsE=G.updatecoeffsE.size, N_updatecoeffsH=G.updatecoeffsH.size, NY_MATCOEFFS=G.updatecoeffsE.shape[1], NY_MATDISPCOEFFS=G.updatecoeffsdispersive.shape[1], NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3], NX_T=G.Tx.shape[1], NY_T=G.Tx.shape[2], NZ_T=G.Tx.shape[3]), options=compiler_opts) - else: # Set to one any substitutions for dispersive materials - kernels_fields = SourceModule(kernels_template_fields.substitute(REAL=cudafloattype, COMPLEX=cudacomplextype, N_updatecoeffsE=G.updatecoeffsE.size, N_updatecoeffsH=G.updatecoeffsH.size, NY_MATCOEFFS=G.updatecoeffsE.shape[1], NY_MATDISPCOEFFS=1, NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3], NX_T=1, NY_T=1, NZ_T=1), options=compiler_opts) + # Electric and magnetic field updates - prepare kernels, get kernel functions, and initialise arrays on GPU + if config.materials['maxpoles'] == 0: + kernels_fields = SourceModule(kernels_template_fields.substitute(REAL=config.dtypes['C_float_or_double'], REAL_OR_COMPLEX=config.dtypes['C_complex'], N_updatecoeffsE=G.updatecoeffsE.size, N_updatecoeffsH=G.updatecoeffsH.size, NY_MATCOEFFS=G.updatecoeffsE.shape[1], NY_MATDISPCOEFFS=1, NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3], NX_T=1, NY_T=1, NZ_T=1), options=compiler_opts) + + else: # # If there are any dispersive materials (updates are split into two parts as they require present and updated electric field values). + kernels_fields = SourceModule(kernels_template_fields.substitute(REAL=config.dtypes['C_float_or_double'], REAL_OR_COMPLEX=config.dtypes['C_complex'], N_updatecoeffsE=G.updatecoeffsE.size, N_updatecoeffsH=G.updatecoeffsH.size, NY_MATCOEFFS=G.updatecoeffsE.shape[1], NY_MATDISPCOEFFS=G.updatecoeffsdispersive.shape[1], NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3], NX_T=G.Tx.shape[1], NY_T=G.Tx.shape[2], NZ_T=G.Tx.shape[3]), options=compiler_opts) + update_e_dispersive_A_gpu = kernels_fields.get_function("update_e_dispersive_A") + update_e_dispersive_B_gpu = kernels_fields.get_function("update_e_dispersive_B") + G.gpu_initialise_dispersive_arrays() update_e_gpu = kernels_fields.get_function("update_e") update_h_gpu = kernels_fields.get_function("update_h") # Copy material coefficient arrays to constant memory of GPU (must be <64KB) for fields kernels updatecoeffsE = kernels_fields.get_global('updatecoeffsE')[0] updatecoeffsH = kernels_fields.get_global('updatecoeffsH')[0] - if G.updatecoeffsE.nbytes + G.updatecoeffsH.nbytes > config.gpus.constmem: - raise GeneralError('Too many materials in the model to fit onto constant memory of size {} on {} - {} GPU'.format(human_size(config.gpus.constmem), config.gpus.deviceID, config.gpus.name)) + if G.updatecoeffsE.nbytes + G.updatecoeffsH.nbytes > config.cuda['gpus'].constmem: + raise GeneralError('Too many materials in the model to fit onto constant memory of size {} on {} - {} GPU'.format(human_size(config.cuda['gpus'].constmem), config.cuda['gpus'].deviceID, config.cuda['gpus'].name)) else: drv.memcpy_htod(updatecoeffsE, G.updatecoeffsE) drv.memcpy_htod(updatecoeffsH, G.updatecoeffsH) - # Electric and magnetic field updates - dispersive materials - get kernel functions and initialise array on GPU - if Material.maxpoles > 0: # If there are any dispersive materials (updates are split into two parts as they require present and updated electric field values). - update_e_dispersive_A_gpu = kernels_fields.get_function("update_e_dispersive_A") - update_e_dispersive_B_gpu = kernels_fields.get_function("update_e_dispersive_B") - G.gpu_initialise_dispersive_arrays() - # Electric and magnetic field updates - set blocks per grid and initialise field arrays on GPU G.gpu_set_blocks_per_grid() G.gpu_initialise_arrays() @@ -527,12 +521,10 @@ def solve_gpu(currentmodelrun, modelend, G): # PML updates if G.pmls: # Prepare kernels - pmlmodulelectric = 'gprMax.pml_updates.pml_updates_electric_' + G.pmlformulation + '_gpu' - kernelelectricfunc = getattr(import_module(pmlmodulelectric), 'kernels_template_pml_electric_' + G.pmlformulation) - pmlmodulemagnetic = 'gprMax.pml_updates.pml_updates_magnetic_' + G.pmlformulation + '_gpu' - kernelmagneticfunc = getattr(import_module(pmlmodulemagnetic), 'kernels_template_pml_magnetic_' + G.pmlformulation) - kernels_pml_electric = SourceModule(kernelelectricfunc.substitute(REAL=cudafloattype, N_updatecoeffsE=G.updatecoeffsE.size, NY_MATCOEFFS=G.updatecoeffsE.shape[1], NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3]), options=compiler_opts) - kernels_pml_magnetic = SourceModule(kernelmagneticfunc.substitute(REAL=cudafloattype, N_updatecoeffsH=G.updatecoeffsH.size, NY_MATCOEFFS=G.updatecoeffsH.shape[1], NX_FIELDS=G.Hx.shape[0], NY_FIELDS=G.Hx.shape[1], NZ_FIELDS=G.Hx.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3]), options=compiler_opts) + kernelelectricfunc = getattr(import_module('gprMax.cuda.pml_updates_electric_' + G.pmlformulation), 'kernels_template_pml_electric_' + G.pmlformulation) + kernelmagneticfunc = getattr(import_module('gprMax.cuda.pml_updates_magnetic_' + G.pmlformulation), 'kernels_template_pml_magnetic_' + G.pmlformulation) + kernels_pml_electric = SourceModule(kernelelectricfunc.substitute(REAL=config.dtypes['C_float_or_double'], N_updatecoeffsE=G.updatecoeffsE.size, NY_MATCOEFFS=G.updatecoeffsE.shape[1], NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3]), options=compiler_opts) + kernels_pml_magnetic = SourceModule(kernelmagneticfunc.substitute(REAL=config.dtypes['C_float_or_double'], N_updatecoeffsH=G.updatecoeffsH.size, NY_MATCOEFFS=G.updatecoeffsH.shape[1], NX_FIELDS=G.Hx.shape[0], NY_FIELDS=G.Hx.shape[1], NZ_FIELDS=G.Hx.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3]), options=compiler_opts) # Copy material coefficient arrays to constant memory of GPU (must be <64KB) for PML kernels updatecoeffsE = kernels_pml_electric.get_global('updatecoeffsE')[0] updatecoeffsH = kernels_pml_magnetic.get_global('updatecoeffsH')[0] @@ -549,12 +541,13 @@ def solve_gpu(currentmodelrun, modelend, G): # Initialise arrays on GPU rxcoords_gpu, rxs_gpu = gpu_initialise_rx_arrays(G) # Prepare kernel and get kernel function - kernel_store_outputs = SourceModule(kernel_template_store_outputs.substitute(REAL=cudafloattype, NY_RXCOORDS=3, NX_RXS=6, NY_RXS=G.iterations, NZ_RXS=len(G.rxs), NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2]), options=compiler_opts) + + kernel_store_outputs = SourceModule(kernel_template_store_outputs.substitute(REAL=config.dtypes['C_float_or_double'], NY_RXCOORDS=3, NX_RXS=len(Rx.gpu_allowableoutputs), NY_RXS=G.iterations, NZ_RXS=len(G.rxs), NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2]), options=compiler_opts) store_outputs_gpu = kernel_store_outputs.get_function("store_outputs") # Sources - initialise arrays on GPU, prepare kernel and get kernel functions if G.voltagesources + G.hertziandipoles + G.magneticdipoles: - kernels_sources = SourceModule(kernels_template_sources.substitute(REAL=cudafloattype, N_updatecoeffsE=G.updatecoeffsE.size, N_updatecoeffsH=G.updatecoeffsH.size, NY_MATCOEFFS=G.updatecoeffsE.shape[1], NY_SRCINFO=4, NY_SRCWAVES=G.iterations, NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3]), options=compiler_opts) + kernels_sources = SourceModule(kernels_template_sources.substitute(REAL=config.dtypes['C_float_or_double'], N_updatecoeffsE=G.updatecoeffsE.size, N_updatecoeffsH=G.updatecoeffsH.size, NY_MATCOEFFS=G.updatecoeffsE.shape[1], NY_SRCINFO=4, NY_SRCWAVES=G.iterations, NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2], NX_ID=G.ID.shape[1], NY_ID=G.ID.shape[2], NZ_ID=G.ID.shape[3]), options=compiler_opts) # Copy material coefficient arrays to constant memory of GPU (must be <64KB) for source kernels updatecoeffsE = kernels_sources.get_global('updatecoeffsE')[0] updatecoeffsH = kernels_sources.get_global('updatecoeffsH')[0] @@ -575,7 +568,7 @@ def solve_gpu(currentmodelrun, modelend, G): # Initialise arrays on GPU snapEx_gpu, snapEy_gpu, snapEz_gpu, snapHx_gpu, snapHy_gpu, snapHz_gpu = gpu_initialise_snapshot_array(G) # Prepare kernel and get kernel function - kernel_store_snapshot = SourceModule(kernel_template_store_snapshot.substitute(REAL=cudafloattype, NX_SNAPS=Snapshot.nx_max, NY_SNAPS=Snapshot.ny_max, NZ_SNAPS=Snapshot.nz_max, NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2]), options=compiler_opts) + kernel_store_snapshot = SourceModule(kernel_template_store_snapshot.substitute(REAL=config.dtypes['C_float_or_double'], NX_SNAPS=Snapshot.nx_max, NY_SNAPS=Snapshot.ny_max, NZ_SNAPS=Snapshot.nz_max, NX_FIELDS=G.Ex.shape[0], NY_FIELDS=G.Ex.shape[1], NZ_FIELDS=G.Ex.shape[2]), options=compiler_opts) store_snapshot_gpu = kernel_store_snapshot.get_function("store_snapshot") # Iteration loop timer @@ -583,7 +576,7 @@ def solve_gpu(currentmodelrun, modelend, G): iterend = drv.Event() iterstart.record() - for iteration in tqdm(range(G.iterations), desc='Running simulation, model ' + str(currentmodelrun) + '/' + str(modelend), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not progressbars): + for iteration in tqdm(range(G.iterations), desc='Running simulation, model ' + str(currentmodelrun) + '/' + str(modelend), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars']): # Get GPU memory usage on final iteration if iteration == G.iterations - 1: @@ -600,7 +593,7 @@ def solve_gpu(currentmodelrun, modelend, G): # Store any snapshots for i, snap in enumerate(G.snapshots): if snap.time == iteration + 1: - if not snapsgpu2cpu: + if not config.cuda['snapsgpu2cpu']: store_snapshot_gpu(np.int32(i), np.int32(snap.xs), np.int32(snap.xf), np.int32(snap.ys), np.int32(snap.yf), np.int32(snap.zs), @@ -629,7 +622,7 @@ def solve_gpu(currentmodelrun, modelend, G): update_h_gpu(np.int32(G.nx), np.int32(G.ny), np.int32(G.nz), G.ID_gpu.gpudata, G.Hx_gpu.gpudata, G.Hy_gpu.gpudata, G.Hz_gpu.gpudata, G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, - G.Ez_gpu.gpudata, block=config.gpus.tpb, grid=config.gpus.bpg) + G.Ez_gpu.gpudata, block=config.cuda['gpus'].tpb, grid=config.cuda['gpus'].bpg) # Update magnetic field components with the PML correction for pml in G.pmls: @@ -638,7 +631,7 @@ def solve_gpu(currentmodelrun, modelend, G): # Update magnetic field components for magetic dipole sources if G.magneticdipoles: update_magnetic_dipole_gpu(np.int32(len(G.magneticdipoles)), np.int32(iteration), - floattype(G.dx), floattype(G.dy), floattype(G.dz), + config.dtypes['float_or_double'](G.dx), config.dtypes['float_or_double'](G.dy), config.dtypes['float_or_double'](G.dz), srcinfo1_magnetic_gpu.gpudata, srcinfo2_magnetic_gpu.gpudata, srcwaves_magnetic_gpu.gpudata, G.ID_gpu.gpudata, G.Hx_gpu.gpudata, G.Hy_gpu.gpudata, G.Hz_gpu.gpudata, @@ -646,20 +639,20 @@ def solve_gpu(currentmodelrun, modelend, G): # Update electric field components # If all materials are non-dispersive do standard update - if Material.maxpoles == 0: + if config.materials['maxpoles'] == 0: update_e_gpu(np.int32(G.nx), np.int32(G.ny), np.int32(G.nz), G.ID_gpu.gpudata, G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, G.Ez_gpu.gpudata, G.Hx_gpu.gpudata, G.Hy_gpu.gpudata, G.Hz_gpu.gpudata, - block=config.gpus.tpb, grid=config.gpus.bpg) + block=config.cuda['gpus'].tpb, grid=config.cuda['gpus'].bpg) # If there are any dispersive materials do 1st part of dispersive update # (it is split into two parts as it requires present and updated electric field values). else: update_e_dispersive_A_gpu(np.int32(G.nx), np.int32(G.ny), np.int32(G.nz), - np.int32(Material.maxpoles), G.updatecoeffsdispersive_gpu.gpudata, + np.int32(config.materials['maxpoles']), G.updatecoeffsdispersive_gpu.gpudata, G.Tx_gpu.gpudata, G.Ty_gpu.gpudata, G.Tz_gpu.gpudata, G.ID_gpu.gpudata, G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, G.Ez_gpu.gpudata, G.Hx_gpu.gpudata, G.Hy_gpu.gpudata, G.Hz_gpu.gpudata, - block=config.gpus.tpb, grid=config.gpus.bpg) + block=config.cuda['gpus'].tpb, grid=config.cuda['gpus'].bpg) # Update electric field components with the PML correction for pml in G.pmls: @@ -668,7 +661,7 @@ def solve_gpu(currentmodelrun, modelend, G): # Update electric field components for voltage sources if G.voltagesources: update_voltage_source_gpu(np.int32(len(G.voltagesources)), np.int32(iteration), - floattype(G.dx), floattype(G.dy), floattype(G.dz), + config.dtypes['float_or_double'](G.dx), config.dtypes['float_or_double'](G.dy), config.dtypes['float_or_double'](G.dz), srcinfo1_voltage_gpu.gpudata, srcinfo2_voltage_gpu.gpudata, srcwaves_voltage_gpu.gpudata, G.ID_gpu.gpudata, G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, G.Ez_gpu.gpudata, @@ -677,26 +670,26 @@ def solve_gpu(currentmodelrun, modelend, G): # Update electric field components for Hertzian dipole sources (update any Hertzian dipole sources last) if G.hertziandipoles: update_hertzian_dipole_gpu(np.int32(len(G.hertziandipoles)), np.int32(iteration), - floattype(G.dx), floattype(G.dy), floattype(G.dz), + config.dtypes['float_or_double'](G.dx), config.dtypes['float_or_double'](G.dy), config.dtypes['float_or_double'](G.dz), srcinfo1_hertzian_gpu.gpudata, srcinfo2_hertzian_gpu.gpudata, srcwaves_hertzian_gpu.gpudata, G.ID_gpu.gpudata, G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, G.Ez_gpu.gpudata, block=(1, 1, 1), grid=(round32(len(G.hertziandipoles)), 1, 1)) # If there are any dispersive materials do 2nd part of dispersive update (it is split into two parts as it requires present and updated electric field values). Therefore it can only be completely updated after the electric field has been updated by the PML and source updates. - if Material.maxpoles > 0: + if config.materials['maxpoles'] > 0: update_e_dispersive_B_gpu(np.int32(G.nx), np.int32(G.ny), np.int32(G.nz), - np.int32(Material.maxpoles), G.updatecoeffsdispersive_gpu.gpudata, + np.int32(config.materials['maxpoles']), G.updatecoeffsdispersive_gpu.gpudata, G.Tx_gpu.gpudata, G.Ty_gpu.gpudata, G.Tz_gpu.gpudata, G.ID_gpu.gpudata, G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, G.Ez_gpu.gpudata, - block=config.gpus.tpb, grid=config.gpus.bpg) + block=config.cuda['gpus'].tpb, grid=config.cuda['gpus'].bpg) # Copy output from receivers array back to correct receiver objects if G.rxs: gpu_get_rx_array(rxs_gpu.get(), rxcoords_gpu.get(), G) # Copy data from any snapshots back to correct snapshot objects - if G.snapshots and not snapsgpu2cpu: + if G.snapshots and not config.cuda['snapsgpu2cpu']: for i, snap in enumerate(G.snapshots): gpu_get_snapshot_array(snapEx_gpu.get(), snapEy_gpu.get(), snapEz_gpu.get(), snapHx_gpu.get(), snapHy_gpu.get(), snapHz_gpu.get(), i, snap) diff --git a/gprMax/pml.py b/gprMax/pml.py index 9c05693b..488b67a6 100644 --- a/gprMax/pml.py +++ b/gprMax/pml.py @@ -22,21 +22,18 @@ import numpy as np from tqdm import tqdm import gprMax.config as config -from gprMax.config import e0 -from gprMax.config import z0 -from gprMax.config import floattype -from gprMax.config import hostinfo -from gprMax.exceptions import GeneralError class CFSParameter(object): """Individual CFS parameter (e.g. alpha, kappa, or sigma).""" # Allowable scaling profiles and directions - scalingprofiles = {'constant': 0, 'linear': 1, 'quadratic': 2, 'cubic': 3, 'quartic': 4, 'quintic': 5, 'sextic': 6, 'septic': 7, 'octic': 8} + scalingprofiles = {'constant': 0, 'linear': 1, 'quadratic': 2, 'cubic': 3, + 'quartic': 4, 'quintic': 5, 'sextic': 6, 'septic': 7, 'octic': 8} scalingdirections = ['forward', 'reverse'] - def __init__(self, ID=None, scaling='polynomial', scalingprofile=None, scalingdirection='forward', min=0, max=0): + def __init__(self, ID=None, scaling='polynomial', scalingprofile=None, + scalingdirection='forward', min=0, max=0): """ Args: ID (str): Identifier for CFS parameter, can be: 'alpha', 'kappa' or 'sigma'. @@ -71,60 +68,73 @@ class CFS(object): self.sigma = CFSParameter(ID='sigma', scalingprofile='quartic', min=0, max=None) def calculate_sigmamax(self, d, er, mr, G): - """Calculates an optimum value for sigma max based on underlying material properties. + """Calculates an optimum value for sigma max based on underlying + material properties. Args: d (float): dx, dy, or dz in direction of PML. er (float): Average permittivity of underlying material. mr (float): Average permeability of underlying material. - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. """ # Calculation of the maximum value of sigma from http://dx.doi.org/10.1109/8.546249 m = CFSParameter.scalingprofiles[self.sigma.scalingprofile] - self.sigma.max = (0.8 * (m + 1)) / (z0 * d * np.sqrt(er * mr)) + self.sigma.max = (0.8 * (m + 1)) / (config.z0 * d * np.sqrt(er * mr)) def scaling_polynomial(self, order, Evalues, Hvalues): - """Applies the polynomial to be used for the scaling profile for electric and magnetic PML updates. + """Applies the polynomial to be used for the scaling profile for + electric and magnetic PML updates. Args: order (int): Order of polynomial for scaling profile. - Evalues (float): numpy array holding scaling profile values for electric PML update. - Hvalues (float): numpy array holding scaling profile values for magnetic PML update. + Evalues (float): numpy array holding scaling profile values for + electric PML update. + Hvalues (float): numpy array holding scaling profile values for + magnetic PML update. Returns: - Evalues (float): numpy array holding scaling profile values for electric PML update. - Hvalues (float): numpy array holding scaling profile values for magnetic PML update. + Evalues (float): numpy array holding scaling profile values for + electric PML update. + Hvalues (float): numpy array holding scaling profile values for + magnetic PML update. """ - tmp = (np.linspace(0, (len(Evalues) - 1) + 0.5, num=2 * len(Evalues)) / (len(Evalues) - 1)) ** order + tmp = (np.linspace(0, (len(Evalues) - 1) + 0.5, num=2 * len(Evalues)) + / (len(Evalues) - 1)) ** order Evalues = tmp[0:-1:2] Hvalues = tmp[1::2] return Evalues, Hvalues def calculate_values(self, thickness, parameter): - """Calculates values for electric and magnetic PML updates based on profile type and minimum and maximum values. + """Calculates values for electric and magnetic PML updates based on + profile type and minimum and maximum values. Args: thickness (int): Thickness of PML in cells. parameter (CFSParameter): Instance of CFSParameter Returns: - Evalues (float): numpy array holding profile value for electric PML update. - Hvalues (float): numpy array holding profile value for magnetic PML update. + Evalues (float): numpy array holding profile value for electric + PML update. + Hvalues (float): numpy array holding profile value for magnetic + PML update. """ # Extra cell of thickness added to allow correct scaling of electric and magnetic values - Evalues = np.zeros(thickness + 1, dtype=floattype) - Hvalues = np.zeros(thickness + 1, dtype=floattype) + Evalues = np.zeros(thickness + 1, dtype=config.dtypes['float_or_double']) + Hvalues = np.zeros(thickness + 1, dtype=config.dtypes['float_or_double']) if parameter.scalingprofile == 'constant': Evalues += parameter.max Hvalues += parameter.max elif parameter.scaling == 'polynomial': - Evalues, Hvalues = self.scaling_polynomial(CFSParameter.scalingprofiles[parameter.scalingprofile], Evalues, Hvalues) + Evalues, Hvalues = self.scaling_polynomial( + CFSParameter.scalingprofiles[parameter.scalingprofile], + Evalues, Hvalues) if parameter.ID == 'alpha': Evalues = Evalues * (self.alpha.max - self.alpha.min) + self.alpha.min Hvalues = Hvalues * (self.alpha.max - self.alpha.min) + self.alpha.min @@ -167,7 +177,8 @@ class PML(object): def __init__(self, G, ID=None, direction=None, xs=0, xf=0, ys=0, yf=0, zs=0, zf=0): """ Args: - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. ID (str): Identifier for PML slab. direction (str): Direction of increasing absorption. xs, xf, ys, yf, zs, zf (float): Extent of the PML slab. @@ -204,20 +215,32 @@ class PML(object): """Initialise arrays to store fields in PML.""" if self.direction[0] == 'x': - self.EPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1), dtype=floattype) - self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz), dtype=floattype) - self.HPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz), dtype=floattype) - self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1), dtype=floattype) + self.EPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1), + dtype=config.dtypes['float_or_double']) + self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz), + dtype=config.dtypes['float_or_double']) + self.HPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz), + dtype=config.dtypes['float_or_double']) + self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1), + dtype=config.dtypes['float_or_double']) elif self.direction[0] == 'y': - self.EPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1), dtype=floattype) - self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz), dtype=floattype) - self.HPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz), dtype=floattype) - self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1), dtype=floattype) + self.EPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1), + dtype=config.dtypes['float_or_double']) + self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz), + dtype=config.dtypes['float_or_double']) + self.HPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz), + dtype=config.dtypes['float_or_double']) + self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1), + dtype=config.dtypes['float_or_double']) elif self.direction[0] == 'z': - self.EPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1), dtype=floattype) - self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1), dtype=floattype) - self.HPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz), dtype=floattype) - self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz), dtype=floattype) + self.EPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1), + dtype=config.dtypes['float_or_double']) + self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1), + dtype=config.dtypes['float_or_double']) + self.HPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz), + dtype=config.dtypes['float_or_double']) + self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz), + dtype=config.dtypes['float_or_double']) def calculate_update_coeffs(self, er, mr, G): """Calculates electric and magnetic update coefficients for the PML. @@ -225,17 +248,26 @@ class PML(object): Args: er (float): Average permittivity of underlying material mr (float): Average permeability of underlying material - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. """ - self.ERA = np.zeros((len(self.CFS), self.thickness), dtype=floattype) - self.ERB = np.zeros((len(self.CFS), self.thickness), dtype=floattype) - self.ERE = np.zeros((len(self.CFS), self.thickness), dtype=floattype) - self.ERF = np.zeros((len(self.CFS), self.thickness), dtype=floattype) - self.HRA = np.zeros((len(self.CFS), self.thickness), dtype=floattype) - self.HRB = np.zeros((len(self.CFS), self.thickness), dtype=floattype) - self.HRE = np.zeros((len(self.CFS), self.thickness), dtype=floattype) - self.HRF = np.zeros((len(self.CFS), self.thickness), dtype=floattype) + self.ERA = np.zeros((len(self.CFS), self.thickness), + dtype=config.dtypes['float_or_double']) + self.ERB = np.zeros((len(self.CFS), self.thickness), + dtype=config.dtypes['float_or_double']) + self.ERE = np.zeros((len(self.CFS), self.thickness), + dtype=config.dtypes['float_or_double']) + self.ERF = np.zeros((len(self.CFS), self.thickness), + dtype=config.dtypes['float_or_double']) + self.HRA = np.zeros((len(self.CFS), self.thickness), + dtype=config.dtypes['float_or_double']) + self.HRB = np.zeros((len(self.CFS), self.thickness), + dtype=config.dtypes['float_or_double']) + self.HRE = np.zeros((len(self.CFS), self.thickness), + dtype=config.dtypes['float_or_double']) + self.HRF = np.zeros((len(self.CFS), self.thickness), + dtype=config.dtypes['float_or_double']) for x, cfs in enumerate(self.CFS): if not cfs.sigma.max: @@ -247,31 +279,33 @@ class PML(object): # Define different parameters depending on PML formulation if G.pmlformulation == 'HORIPML': # HORIPML electric update coefficients - tmp = (2 * e0 * Ekappa) + G.dt * (Ealpha * Ekappa + Esigma) - self.ERA[x, :] = (2 * e0 + G.dt * Ealpha) / tmp - self.ERB[x, :] = (2 * e0 * Ekappa) / tmp - self.ERE[x, :] = ((2 * e0 * Ekappa) - G.dt * (Ealpha * Ekappa + Esigma)) / tmp + tmp = (2 * config.e0 * Ekappa) + G.dt * (Ealpha * Ekappa + Esigma) + self.ERA[x, :] = (2 * config.e0 + G.dt * Ealpha) / tmp + self.ERB[x, :] = (2 * config.e0 * Ekappa) / tmp + self.ERE[x, :] = ((2 * config.e0 * Ekappa) - G.dt + * (Ealpha * Ekappa + Esigma)) / tmp self.ERF[x, :] = (2 * Esigma * G.dt) / (Ekappa * tmp) # HORIPML magnetic update coefficients - tmp = (2 * e0 * Hkappa) + G.dt * (Halpha * Hkappa + Hsigma) - self.HRA[x, :] = (2 * e0 + G.dt * Halpha) / tmp - self.HRB[x, :] = (2 * e0 * Hkappa) / tmp - self.HRE[x, :] = ((2 * e0 * Hkappa) - G.dt * (Halpha * Hkappa + Hsigma)) / tmp + tmp = (2 * config.e0 * Hkappa) + G.dt * (Halpha * Hkappa + Hsigma) + self.HRA[x, :] = (2 * config.e0 + G.dt * Halpha) / tmp + self.HRB[x, :] = (2 * config.e0 * Hkappa) / tmp + self.HRE[x, :] = ((2 * config.e0 * Hkappa) - G.dt + * (Halpha * Hkappa + Hsigma)) / tmp self.HRF[x, :] = (2 * Hsigma * G.dt) / (Hkappa * tmp) elif G.pmlformulation == 'MRIPML': - tmp = 2 * e0 + G.dt * Ealpha + tmp = 2 * config.e0 + G.dt * Ealpha self.ERA[x, :] = Ekappa + (G.dt * Esigma) / tmp - self.ERB[x, :] = (2 * e0) / tmp - self.ERE[x, :] = ((2 * e0) - G.dt * Ealpha) / tmp + self.ERB[x, :] = (2 * config.e0) / tmp + self.ERE[x, :] = ((2 * config.e0) - G.dt * Ealpha) / tmp self.ERF[x, :] = (2 * Esigma * G.dt) / tmp # MRIPML magnetic update coefficients - tmp = 2 * e0 + G.dt * Halpha + tmp = 2 * config.e0 + G.dt * Halpha self.HRA[x, :] = Hkappa + (G.dt * Hsigma) / tmp - self.HRB[x, :] = (2 * e0) / tmp - self.HRE[x, :] = ((2 * e0) - G.dt * Halpha) / tmp + self.HRB[x, :] = (2 * config.e0) / tmp + self.HRE[x, :] = ((2 * config.e0) - G.dt * Halpha) / tmp self.HRF[x, :] = (2 * Hsigma * G.dt) / tmp def update_electric(self, G): @@ -283,7 +317,10 @@ class PML(object): pmlmodule = 'gprMax.cython.pml_updates_electric_' + G.pmlformulation func = getattr(import_module(pmlmodule), 'order' + str(len(self.CFS)) + '_' + self.direction) - func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf, hostinfo['ompthreads'], G.updatecoeffsE, G.ID, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz, self.EPhi1, self.EPhi2, self.ERA, self.ERB, self.ERE, self.ERF, self.d) + func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf, + config.hostinfo['ompthreads'], G.updatecoeffsE, G.ID, + G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz, self.EPhi1, self.EPhi2, + self.ERA, self.ERB, self.ERE, self.ERF, self.d) def update_magnetic(self, G): """This functions updates magnetic field components with the PML correction. @@ -294,7 +331,10 @@ class PML(object): pmlmodule = 'gprMax.cython.pml_updates_magnetic_' + G.pmlformulation func = getattr(import_module(pmlmodule), 'order' + str(len(self.CFS)) + '_' + self.direction) - func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf, hostinfo['ompthreads'], G.updatecoeffsH, G.ID, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz, self.HPhi1, self.HPhi2, self.HRA, self.HRB, self.HRE, self.HRF, self.d) + func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf, + config.hostinfo['ompthreads'], G.updatecoeffsH, G.ID, + G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz, self.HPhi1, self.HPhi2, + self.HRA, self.HRB, self.HRE, self.HRF, self.d) def gpu_set_blocks_per_grid(self, G): """Set the blocks per grid size used for updating the PML field arrays on a GPU. @@ -303,7 +343,10 @@ class PML(object): G (class): Grid class instance - holds essential parameters describing the model. """ - config.gpus.bpg = (int(np.ceil(((self.EPhi1.shape[1] + 1) * (self.EPhi1.shape[2] + 1) * (self.EPhi1.shape[3] + 1)) / config.gpus.tpb[0])), 1, 1) + config.cuda['gpus'].bpg = (int(np.ceil(((self.EPhi1.shape[1] + 1) + * (self.EPhi1.shape[2] + 1) + * (self.EPhi1.shape[3] + 1)) + / config.cuda['gpus'].tpb[0])), 1, 1) def gpu_initialise_arrays(self): """Initialise PML field and coefficient arrays on GPU.""" @@ -327,42 +370,76 @@ class PML(object): """Get update functions from PML kernels. Args: - kernelselectric: PyCuda SourceModule containing PML kernels for electric updates. - kernelsmagnetic: PyCuda SourceModule containing PML kernels for magnetic updates. + kernelselectric: PyCuda SourceModule containing PML kernels for + electric updates. + kernelsmagnetic: PyCuda SourceModule containing PML kernels for + magnetic updates. """ from pycuda.compiler import SourceModule - self.update_electric_gpu = kernelselectric.get_function('order' + str(len(self.CFS)) + '_' + self.direction) - self.update_magnetic_gpu = kernelsmagnetic.get_function('order' + str(len(self.CFS)) + '_' + self.direction) + self.update_electric = kernelselectric.get_function('order' + str(len(self.CFS)) + + '_' + self.direction) + self.update_magnetic = kernelsmagnetic.get_function('order' + str(len(self.CFS)) + + '_' + self.direction) def gpu_update_electric(self, G): - """This functions updates electric field components with the PML correction on the GPU. + """This functions updates electric field components with the PML + correction on the GPU. Args: - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. """ - self.update_electric_gpu(np.int32(self.xs), np.int32(self.xf), np.int32(self.ys), np.int32(self.yf), np.int32(self.zs), np.int32(self.zf), np.int32(self.EPhi1.shape[1]), np.int32(self.EPhi1.shape[2]), np.int32(self.EPhi1.shape[3]), np.int32(self.EPhi2.shape[1]), np.int32(self.EPhi2.shape[2]), np.int32(self.EPhi2.shape[3]), np.int32(self.thickness), G.ID_gpu.gpudata, G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, G.Ez_gpu.gpudata, G.Hx_gpu.gpudata, G.Hy_gpu.gpudata, G.Hz_gpu.gpudata, self.EPhi1_gpu.gpudata, self.EPhi2_gpu.gpudata, self.ERA_gpu.gpudata, self.ERB_gpu.gpudata, self.ERE_gpu.gpudata, self.ERF_gpu.gpudata, floattype(self.d), block=config.gpus.tpb, grid=config.gpus.bpg) + self.update_electric(np.int32(self.xs), np.int32(self.xf), + np.int32(self.ys), np.int32(self.yf), + np.int32(self.zs), np.int32(self.zf), + np.int32(self.EPhi1.shape[1]), np.int32(self.EPhi1.shape[2]), + np.int32(self.EPhi1.shape[3]), np.int32(self.EPhi2.shape[1]), + np.int32(self.EPhi2.shape[2]), np.int32(self.EPhi2.shape[3]), + np.int32(self.thickness), G.ID_gpu.gpudata, + G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, G.Ez_gpu.gpudata, + G.Hx_gpu.gpudata, G.Hy_gpu.gpudata, G.Hz_gpu.gpudata, + self.EPhi1_gpu.gpudata, self.EPhi2_gpu.gpudata, + self.ERA_gpu.gpudata, self.ERB_gpu.gpudata, + self.ERE_gpu.gpudata, self.ERF_gpu.gpudata, + config.dtypes['float_or_double'](self.d), + block=config.cuda['gpus'].tpb, grid=config.cuda['gpus'].bpg) def gpu_update_magnetic(self, G): - """This functions updates magnetic field components with the PML correction on the GPU. + """This functions updates magnetic field components with the PML + correction on the GPU. Args: - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. """ - self.update_magnetic_gpu(np.int32(self.xs), np.int32(self.xf), np.int32(self.ys), np.int32(self.yf), np.int32(self.zs), np.int32(self.zf), np.int32(self.HPhi1.shape[1]), np.int32(self.HPhi1.shape[2]), np.int32(self.HPhi1.shape[3]), np.int32(self.HPhi2.shape[1]), np.int32(self.HPhi2.shape[2]), np.int32(self.HPhi2.shape[3]), np.int32(self.thickness), G.ID_gpu.gpudata, G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, G.Ez_gpu.gpudata, G.Hx_gpu.gpudata, G.Hy_gpu.gpudata, G.Hz_gpu.gpudata, self.HPhi1_gpu.gpudata, self.HPhi2_gpu.gpudata, self.HRA_gpu.gpudata, self.HRB_gpu.gpudata, self.HRE_gpu.gpudata, self.HRF_gpu.gpudata, floattype(self.d), block=config.gpus.tpb, grid=config.gpus.bpg) + self.update_magnetic(np.int32(self.xs), np.int32(self.xf), + np.int32(self.ys), np.int32(self.yf), + np.int32(self.zs), np.int32(self.zf), + np.int32(self.HPhi1.shape[1]), np.int32(self.HPhi1.shape[2]), + np.int32(self.HPhi1.shape[3]), np.int32(self.HPhi2.shape[1]), + np.int32(self.HPhi2.shape[2]), np.int32(self.HPhi2.shape[3]), + np.int32(self.thickness), G.ID_gpu.gpudata, + G.Ex_gpu.gpudata, G.Ey_gpu.gpudata, G.Ez_gpu.gpudata, + G.Hx_gpu.gpudata, G.Hy_gpu.gpudata, G.Hz_gpu.gpudata, + self.HPhi1_gpu.gpudata, self.HPhi2_gpu.gpudata, + self.HRA_gpu.gpudata, self.HRB_gpu.gpudata, + self.HRE_gpu.gpudata, self.HRF_gpu.gpudata, + config.dtypes['float_or_double'](self.d), + block=config.cuda['gpus'].tpb, grid=config.cuda['gpus'].bpg) def build_pmls(G, pbar): - """ - This function builds instances of the PML and calculates the initial + """This function builds instances of the PML and calculates the initial parameters and coefficients including setting profile (based on underlying material er and mr from solid array). Args: - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. pbar (class): Progress bar class instance. """ diff --git a/gprMax/receivers.py b/gprMax/receivers.py index 57be79a0..861df298 100644 --- a/gprMax/receivers.py +++ b/gprMax/receivers.py @@ -20,7 +20,7 @@ from collections import OrderedDict import numpy as np -from gprMax.config import floattype +import gprMax.config as config class Rx(object): @@ -29,6 +29,7 @@ class Rx(object): allowableoutputs = ['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz', 'Ix', 'Iy', 'Iz'] gpu_allowableoutputs = allowableoutputs[:-3] defaultoutputs = allowableoutputs[:-3] + maxnumoutputs = 0 def __init__(self): @@ -43,10 +44,12 @@ class Rx(object): def gpu_initialise_rx_arrays(G): - """Initialise arrays on GPU for receiver coordinates and to store field components for receivers. + """Initialise arrays on GPU for receiver coordinates and to store field + components for receivers. Args: - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. """ import pycuda.gpuarray as gpuarray @@ -58,8 +61,10 @@ def gpu_initialise_rx_arrays(G): rxcoords[i, 1] = rx.ycoord rxcoords[i, 2] = rx.zcoord - # Array to store field components for receivers on GPU - rows are field components; columns are iterations; pages are receivers - rxs = np.zeros((len(Rx.gpu_allowableoutputs), G.iterations, len(G.rxs)), dtype=floattype) + # Array to store field components for receivers on GPU - rows are field components; + # columns are iterations; pages are receivers + rxs = np.zeros((Rx.maxnumoutputs, G.iterations, len(G.rxs)), + dtype=config.dtypes['float_or_double']) # Copy arrays to GPU rxcoords_gpu = gpuarray.to_gpu(rxcoords) @@ -72,17 +77,17 @@ def gpu_get_rx_array(rxs_gpu, rxcoords_gpu, G): """Copy output from receivers array used on GPU back to receiver objects. Args: - rxs_gpu (float): numpy array of receiver data from GPU - rows are field components; columns are iterations; pages are receivers. + rxs_gpu (float): numpy array of receiver data from GPU - rows are field + components; columns are iterations; pages are receivers. rxcoords_gpu (float): numpy array of receiver coordinates from GPU. - G (class): Grid class instance - holds essential parameters describing the model. + G (class): Grid class instance - holds essential parameters + describing the model. """ for rx in G.rxs: for rxgpu in range(len(G.rxs)): - if rx.xcoord == rxcoords_gpu[rxgpu, 0] and rx.ycoord == rxcoords_gpu[rxgpu, 1] and rx.zcoord == rxcoords_gpu[rxgpu, 2]: - rx.outputs['Ex'] = rxs_gpu[0, :, rxgpu] - rx.outputs['Ey'] = rxs_gpu[1, :, rxgpu] - rx.outputs['Ez'] = rxs_gpu[2, :, rxgpu] - rx.outputs['Hx'] = rxs_gpu[3, :, rxgpu] - rx.outputs['Hy'] = rxs_gpu[4, :, rxgpu] - rx.outputs['Hz'] = rxs_gpu[5, :, rxgpu] + if rx.xcoord == rxcoords_gpu[rxgpu, 0] and \ + rx.ycoord == rxcoords_gpu[rxgpu, 1] and \ + rx.zcoord == rxcoords_gpu[rxgpu, 2]: + for k, v in rx.outputs.items(): + v = rxs_gpu[Rx.gpu_allowableoutputs.index(k), :, rxgpu] diff --git a/gprMax/snapshots.py b/gprMax/snapshots.py index d669eb39..9b02056f 100644 --- a/gprMax/snapshots.py +++ b/gprMax/snapshots.py @@ -22,10 +22,9 @@ from struct import pack import numpy as np -from gprMax.config import floattype -from gprMax.config import snapsgpu2cpu -from gprMax.cython.snapshots import calculate_snapshot_fields -from gprMax.utilities import round_value +import gprMax.config as config +from .cython.snapshots import calculate_snapshot_fields +from .utilities import round_value class Snapshot(object): @@ -48,14 +47,15 @@ class Snapshot(object): byteorder = 'BigEndian' # Set format text and string depending on float type - if np.dtype(floattype).name == 'float32': + if config.dtypes['float_or_double'] == np.float32: floatname = 'Float32' floatstring = 'f' - elif np.dtype(floattype).name == 'float64': + elif config.dtypes['float_or_double'] == np.float64: floatname = 'Float64' floatstring = 'd' - def __init__(self, xs=None, ys=None, zs=None, xf=None, yf=None, zf=None, dx=None, dy=None, dz=None, time=None, filename=None): + def __init__(self, xs=None, ys=None, zs=None, xf=None, yf=None, zf=None, + dx=None, dy=None, dz=None, time=None, filename=None): """ Args: xs, xf, ys, yf, zs, zf (int): Extent of the volume in cells. @@ -64,6 +64,7 @@ class Snapshot(object): filename (str): Filename to save to. """ + self.fieldoutputs = {'electric': True, 'magnetic': True} self.xs = xs self.ys = ys self.zs = zs @@ -80,8 +81,13 @@ class Snapshot(object): self.sy = slice(self.ys, self.yf + self.dy, self.dy) self.sz = slice(self.zs, self.zf + self.dz, self.dz) self.ncells = self.nx * self.ny * self.nz - self.datasizefield = 3 * np.dtype(floattype).itemsize * self.ncells - self.vtkdatawritesize = 2 * self.datasizefield + 2 * np.dtype(np.uint32).itemsize + self.datasizefield = (3 * np.dtype(config.dtypes['float_or_double']).itemsize + * self.ncells) + self.vtkdatawritesize = ((self.fieldoutputs['electric'] + + self.fieldoutputs['magnetic']) * self.datasizefield + + (self.fieldoutputs['electric'] + + self.fieldoutputs['magnetic']) + * np.dtype(np.uint32).itemsize) self.time = time self.basefilename = filename @@ -101,12 +107,12 @@ class Snapshot(object): Hzslice = np.ascontiguousarray(G.Hz[self.sx, self.sy, self.sz]) # Create arrays to hold the field data for snapshot - Exsnap = np.zeros((self.nx, self.ny, self.nz), dtype=floattype) - Eysnap = np.zeros((self.nx, self.ny, self.nz), dtype=floattype) - Ezsnap = np.zeros((self.nx, self.ny, self.nz), dtype=floattype) - Hxsnap = np.zeros((self.nx, self.ny, self.nz), dtype=floattype) - Hysnap = np.zeros((self.nx, self.ny, self.nz), dtype=floattype) - Hzsnap = np.zeros((self.nx, self.ny, self.nz), dtype=floattype) + Exsnap = np.zeros((self.nx, self.ny, self.nz), dtype=config.dtypes['float_or_double']) + Eysnap = np.zeros((self.nx, self.ny, self.nz), dtype=config.dtypes['float_or_double']) + Ezsnap = np.zeros((self.nx, self.ny, self.nz), dtype=config.dtypes['float_or_double']) + Hxsnap = np.zeros((self.nx, self.ny, self.nz), dtype=config.dtypes['float_or_double']) + Hysnap = np.zeros((self.nx, self.ny, self.nz), dtype=config.dtypes['float_or_double']) + Hzsnap = np.zeros((self.nx, self.ny, self.nz), dtype=config.dtypes['float_or_double']) # Calculate field values at points (comes from averaging field components in cells) calculate_snapshot_fields( @@ -140,29 +146,53 @@ class Snapshot(object): G (class): Grid class instance - holds essential parameters describing the model. """ - hfield_offset = 3 * np.dtype(floattype).itemsize * self.ncells + np.dtype(np.uint32).itemsize + hfield_offset = (3 * np.dtype(config.dtypes['float_or_double']).itemsize + * self.ncells + np.dtype(np.uint32).itemsize) self.filehandle = open(self.filename, 'wb') self.filehandle.write('\n'.encode('utf-8')) - self.filehandle.write('\n'.format(Snapshot.byteorder).encode('utf-8')) - self.filehandle.write('\n'.format(self.xs, round_value(self.xf / self.dx), self.ys, round_value(self.yf / self.dy), self.zs, round_value(self.zf / self.dz), self.dx * G.dx, self.dy * G.dy, self.dz * G.dz).encode('utf-8')) - self.filehandle.write('\n'.format(self.xs, round_value(self.xf / self.dx), self.ys, round_value(self.yf / self.dy), self.zs, round_value(self.zf / self.dz)).encode('utf-8')) - self.filehandle.write('\n'.encode('utf-8')) - self.filehandle.write('\n'.format(Snapshot.floatname).encode('utf-8')) - self.filehandle.write('\n'.format(Snapshot.floatname, hfield_offset).encode('utf-8')) + self.filehandle.write('\n' + .format(Snapshot.byteorder).encode('utf-8')) + self.filehandle.write('\n' + .format(self.xs, round_value(self.xf / self.dx), + self.ys, round_value(self.yf / self.dy), self.zs, + round_value(self.zf / self.dz), self.dx * G.dx, + self.dy * G.dy, self.dz * G.dz).encode('utf-8')) + self.filehandle.write('\n' + .format(self.xs, round_value(self.xf / self.dx), + self.ys, round_value(self.yf / self.dy), + self.zs, round_value(self.zf / self.dz)).encode('utf-8')) + + if self.fieldoutputs['electric'] and self.fieldoutputs['magnetic']: + self.filehandle.write('\n'.encode('utf-8')) + self.filehandle.write('\n' + .format(Snapshot.floatname).encode('utf-8')) + self.filehandle.write('\n' + .format(Snapshot.floatname, hfield_offset).encode('utf-8')) + elif self.fieldoutputs['electric']: + self.filehandle.write('\n'.encode('utf-8')) + self.filehandle.write('\n' + .format(Snapshot.floatname).encode('utf-8')) + elif self.fieldoutputs['magnetic']: + self.filehandle.write('\n'.encode('utf-8')) + self.filehandle.write('\n' + .format(Snapshot.floatname).encode('utf-8')) + self.filehandle.write('\n\n\n\n_'.encode('utf-8')) - # Write number of bytes of appended data as UInt32 - self.filehandle.write(pack('I', self.datasizefield)) - pbar.update(n=4) - self.electric.tofile(self.filehandle) - pbar.update(n=self.datasizefield) + if self.fieldoutputs['electric']: + # Write number of bytes of appended data as UInt32 + self.filehandle.write(pack('I', self.datasizefield)) + pbar.update(n=4) + self.electric.tofile(self.filehandle) + pbar.update(n=self.datasizefield) - # Write number of bytes of appended data as UInt32 - self.filehandle.write(pack('I', self.datasizefield)) - pbar.update(n=4) - self.magnetic.tofile(self.filehandle) - pbar.update(n=self.datasizefield) + if self.fieldoutputs['magnetic']: + # Write number of bytes of appended data as UInt32 + self.filehandle.write(pack('I', self.datasizefield)) + pbar.update(n=4) + self.magnetic.tofile(self.filehandle) + pbar.update(n=self.datasizefield) self.filehandle.write('\n\n'.encode('utf-8')) self.filehandle.close() @@ -192,14 +222,22 @@ def gpu_initialise_snapshot_array(G): # GPU - blocks per grid - according to largest requested snapshot Snapshot.bpg = (int(np.ceil(((Snapshot.nx_max) * (Snapshot.ny_max) * (Snapshot.nz_max)) / Snapshot.tpb[0])), 1, 1) - # 4D arrays to store snapshots on GPU, e.g. snapEx(time, x, y, z) - numsnaps = 1 if snapsgpu2cpu else len(G.snapshots) - snapEx = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=floattype) - snapEy = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=floattype) - snapEz = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=floattype) - snapHx = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=floattype) - snapHy = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=floattype) - snapHz = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=floattype) + # 4D arrays to store snapshots on GPU, e.g. snapEx(time, x, y, z); + # if snapshots are not being stored on the GPU during the simulation then + # they are copied back to the host after each iteration, hence numsnaps = 1 + numsnaps = 1 if config.cuda['snapsgpu2cpu'] else len(G.snapshots) + snapEx = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), + dtype=config.dtypes['float_or_double']) + snapEy = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), + dtype=config.dtypes['float_or_double']) + snapEz = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), + dtype=config.dtypes['float_or_double']) + snapHx = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), + dtype=config.dtypes['float_or_double']) + snapHy = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), + dtype=config.dtypes['float_or_double']) + snapHz = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), + dtype=config.dtypes['float_or_double']) # Copy arrays to GPU snapEx_gpu = gpuarray.to_gpu(snapEx) @@ -222,8 +260,8 @@ def gpu_get_snapshot_array(snapEx_gpu, snapEy_gpu, snapEz_gpu, snapHx_gpu, snapH """ snap.electric = np.stack((snapEx_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf], - snapEy_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf], - snapEz_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf])).reshape(-1, order='F') + snapEy_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf], + snapEz_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf])).reshape(-1, order='F') snap.magnetic = np.stack((snapHx_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf], - snapHy_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf], - snapHz_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf])).reshape(-1, order='F') + snapHy_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf], + snapHz_gpu[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf])).reshape(-1, order='F') diff --git a/gprMax/sources.py b/gprMax/sources.py index d84e13e5..5665ffa0 100644 --- a/gprMax/sources.py +++ b/gprMax/sources.py @@ -20,12 +20,11 @@ from copy import deepcopy import numpy as np -from gprMax.config import c -from gprMax.config import floattype -from gprMax.grid import Ix -from gprMax.grid import Iy -from gprMax.grid import Iz -from gprMax.utilities import round_value +import gprMax.config as config +from .grid import Ix +from .grid import Iy +from .grid import Iz +from .utilities import round_value class Source(object): @@ -52,10 +51,10 @@ class Source(object): """ # Waveform values for electric sources - calculated half a timestep later - self.waveformvaluesJ = np.zeros((G.iterations), dtype=floattype) + self.waveformvaluesJ = np.zeros((G.iterations), dtype=config.dtypes['float_or_double']) # Waveform values for magnetic sources - self.waveformvaluesM = np.zeros((G.iterations), dtype=floattype) + self.waveformvaluesM = np.zeros((G.iterations), dtype=config.dtypes['float_or_double']) waveform = next(x for x in G.waveforms if x.ID == self.waveformID) @@ -72,7 +71,8 @@ class VoltageSource(Source): """ A voltage source can be a hard source if it's resistance is zero, i.e. the time variation of the specified electric field component is prescribed. - If it's resistance is non-zero it behaves as a resistive voltage source.""" + If it's resistance is non-zero it behaves as a resistive voltage source. + """ def __init__(self): super().__init__() @@ -114,8 +114,7 @@ class VoltageSource(Source): Ez[i, j, k] = -1 * self.waveformvaluesJ[iteration] / G.dz def create_material(self, G): - """ - Create a new material at the voltage source location that adds the + """Create a new material at the voltage source location that adds the voltage source conductivity to the underlying parameters. Args: @@ -231,8 +230,8 @@ def gpu_initialise_src_arrays(sources, G): import pycuda.gpuarray as gpuarray srcinfo1 = np.zeros((len(sources), 4), dtype=np.int32) - srcinfo2 = np.zeros((len(sources)), dtype=floattype) - srcwaves = np.zeros((len(sources), G.iterations), dtype=floattype) + srcinfo2 = np.zeros((len(sources)), dtype=config.dtypes['float_or_double']) + srcwaves = np.zeros((len(sources), G.iterations), dtype=config.dtypes['float_or_double']) for i, src in enumerate(sources): srcinfo1[i, 0] = src.xcoord srcinfo1[i, 1] = src.ycoord @@ -262,9 +261,8 @@ def gpu_initialise_src_arrays(sources, G): class TransmissionLine(Source): - """ - A transmission line source is a one-dimensional transmission - line which is attached virtually to a grid cell. + """A transmission line source is a one-dimensional transmission line + which is attached virtually to a grid cell. """ def __init__(self, G): @@ -282,7 +280,7 @@ class TransmissionLine(Source): # Spatial step of transmission line (N.B if the magic time step is # used it results in instabilities for certain impedances) - self.dl = np.sqrt(3) * c * G.dt + self.dl = np.sqrt(3) * config.c * G.dt # Number of cells in the transmission line (initially a long line to # calculate incident voltage and current); consider putting ABCs/PML at end @@ -294,16 +292,15 @@ class TransmissionLine(Source): # Cell position of where line connects to antenna/main grid self.antpos = 10 - self.voltage = np.zeros(self.nl, dtype=floattype) - self.current = np.zeros(self.nl, dtype=floattype) - self.Vinc = np.zeros(G.iterations, dtype=floattype) - self.Iinc = np.zeros(G.iterations, dtype=floattype) - self.Vtotal = np.zeros(G.iterations, dtype=floattype) - self.Itotal = np.zeros(G.iterations, dtype=floattype) + self.voltage = np.zeros(self.nl, dtype=config.dtypes['float_or_double']) + self.current = np.zeros(self.nl, dtype=config.dtypes['float_or_double']) + self.Vinc = np.zeros(G.iterations, dtype=config.dtypes['float_or_double']) + self.Iinc = np.zeros(G.iterations, dtype=config.dtypes['float_or_double']) + self.Vtotal = np.zeros(G.iterations, dtype=config.dtypes['float_or_double']) + self.Itotal = np.zeros(G.iterations, dtype=config.dtypes['float_or_double']) def calculate_incident_V_I(self, G): - """ - Calculates the incident voltage and current with a long length + """Calculates the incident voltage and current with a long length transmission line not connected to the main grid from: http://dx.doi.org/10.1002/mop.10415 Args: @@ -326,7 +323,7 @@ class TransmissionLine(Source): G (class): Grid class instance - holds essential parameters describing the model. """ - h = (c * G.dt - self.dl) / (c * G.dt + self.dl) + h = (config.c * G.dt - self.dl) / (config.c * G.dt + self.dl) self.voltage[0] = h * (self.voltage[1] - self.abcv0) + self.abcv1 self.abcv0 = self.voltage[0] @@ -341,10 +338,10 @@ class TransmissionLine(Source): """ # Update all the voltage values along the line - self.voltage[1:self.nl] -= self.resistance * (c * G.dt / self.dl) * (self.current[1:self.nl] - self.current[0:self.nl - 1]) + self.voltage[1:self.nl] -= self.resistance * (config.c * G.dt / self.dl) * (self.current[1:self.nl] - self.current[0:self.nl - 1]) # Update the voltage at the position of the one-way injector excitation - self.voltage[self.srcpos] += (c * G.dt / self.dl) * self.waveformvaluesJ[iteration] + self.voltage[self.srcpos] += (config.c * G.dt / self.dl) * self.waveformvaluesJ[iteration] # Update ABC before updating current self.update_abc(G) @@ -358,10 +355,10 @@ class TransmissionLine(Source): """ # Update all the current values along the line - self.current[0:self.nl - 1] -= (1 / self.resistance) * (c * G.dt / self.dl) * (self.voltage[1:self.nl] - self.voltage[0:self.nl - 1]) + self.current[0:self.nl - 1] -= (1 / self.resistance) * (config.c * G.dt / self.dl) * (self.voltage[1:self.nl] - self.voltage[0:self.nl - 1]) # Update the current one cell before the position of the one-way injector excitation - self.current[self.srcpos - 1] += (1 / self.resistance) * (c * G.dt / self.dl) * self.waveformvaluesM[iteration] + self.current[self.srcpos - 1] += (1 / self.resistance) * (config.c * G.dt / self.dl) * self.waveformvaluesM[iteration] def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G): """Updates electric field value in the main grid from voltage value in the transmission line. @@ -416,95 +413,3 @@ class TransmissionLine(Source): self.current[self.antpos] = Iz(i, j, k, G.Hx, G.Hy, G.Hz, G) self.update_current(iteration, G) - - -class PlaneWave(Source): - """A plane wave source. It uses a total-field/scattered-field (TF/SF) formulation.""" - - def __init__(self, G): - """ - Args: - G (class): Grid class instance - holds essential parameters describing the model. - """ - - super(Source, self).__init__() - - # Coordinates defining Huygen's surface - self.xs = 0 - self.xf = 0 - self.ys = 0 - self.yf = 0 - self.zs = 0 - self.zf = 0 - - # Spherical coordinates defining incident unit wavevector (k) - self.theta = 0 # 0 <= theta <= 180 - self.phi = 0 # 0 <= phi <= 360 - - # Angle that incident electric field makes with k cross z - self.psi = 0 # 0 <= psi <= 360 - - def calculate_origin(self, G): - """Calculate origin of TF/SF interface with incident wavefront.""" - - if self.theta >= 0 and self.theta <= 90: - if self.phi >= 0 and self.phi <= 90: - self.xcoordorigin = 0 - self.ycoordorigin = 0 - self.zcoordorigin = 0 - - elif self.phi > 90 and self.phi <= 180: - self.xcoordorigin = G.nx - self.ycoordorigin = 0 - self.zcoordorigin = 0 - - elif self.phi > 180 and self.phi <= 270: - self.xcoordorigin = G.nx - self.ycoordorigin = G.ny - self.zcoordorigin = 0 - - elif self.phi > 270 and self.phi <= 360: - self.xcoordorigin = 0 - self.ycoordorigin = G.ny - self.zcoordorigin = 0 - - elif self.theta > 90 and self.theta <= 180: - if self.phi >= 0 and self.phi <= 90: - self.xcoordorigin = 0 - self.ycoordorigin = 0 - self.zcoordorigin = G.nz - - elif self.phi > 90 and self.phi <= 180: - self.xcoordorigin = G.nx - self.ycoordorigin = 0 - self.zcoordorigin = G.nz - - elif self.phi > 180 and self.phi <= 270: - self.xcoordorigin = G.nx - self.ycoordorigin = G.ny - self.zcoordorigin = G.nz - - elif self.phi > 270 and self.phi <= 360: - self.xcoordorigin = 0 - self.ycoordorigin = G.ny - self.zcoordorigin = G.nz - - def calculate_vector_components(self): - """Calculate components of incident fields.""" - - self.theta = np.deg2rad(self.theta) - self.phi = np.deg2rad(self.phi) - self.psi = np.deg2rad(self.psi) - - # Components of incident unit wavevector - self.kx = np.sin(self.theta) * np.cos(self.phi) - self.ky = np.sin(self.theta) * np.sin(self.phi) - self.kz = np.cos(self.theta) - - # Components of incident field vectors - self.Exinc = np.cos(self.psi) * np.sin(self.phi) - np.sin(self.psi) * np.cos(self.theta) * np.cos(self.phi) - self.Eyinc = -np.cos(self.psi) * np.cos(self.phi) - np.sin(self.psi) * np.cos(self.theta) * np.sin(self.phi) - self.Ezinc = np.sin(self.psi) * np.sin(self.theta) - self.Hxinc = np.sin(self.psi) * np.sin(self.phi) + np.cos(self.psi) * np.cos(self.theta) * np.cos(self.phi) - self.Hyinc = -np.sin(self.psi) * np.cos(self.phi) + np.cos(self.psi) * np.cos(self.theta) * np.sin(self.phi) - self.Hzinc = -np.cos(self.psi) * np.sin(self.theta) diff --git a/gprMax/utilities.py b/gprMax/utilities.py index 02f7e5e2..9ebd682d 100644 --- a/gprMax/utilities.py +++ b/gprMax/utilities.py @@ -27,15 +27,15 @@ import subprocess from shutil import get_terminal_size import sys import textwrap +from time import perf_counter from colorama import init from colorama import Fore from colorama import Style init() import numpy as np -from time import process_time -from gprMax.exceptions import GeneralError +from .exceptions import GeneralError def get_terminal_width(): @@ -86,8 +86,7 @@ def logo(version): @contextmanager def open_path_file(path_or_file): - """ - Accepts either a path as a string or a file object and returns a file + """Accepts either a path as a string or a file object and returns a file object (http://stackoverflow.com/a/6783680). Args: @@ -140,7 +139,7 @@ def round32(value): def fft_power(waveform, dt): """Calculate a FFT of the given waveform of amplitude values; - converted to decibels and shifted so that maximum power is 0dB + converted to decibels and shifted so that maximum power is 0dB Args: waveform (ndarray): time domain waveform @@ -417,4 +416,4 @@ def detect_check_gpus(deviceIDs): def timer(): """Function to return the current process wide time in fractional seconds.""" - return process_time() + return perf_counter() diff --git a/gprMax/waveforms.py b/gprMax/waveforms.py index 506d4f27..4e19e3e4 100644 --- a/gprMax/waveforms.py +++ b/gprMax/waveforms.py @@ -18,8 +18,6 @@ import numpy as np -from gprMax.utilities import round_value - class Waveform(object): """Definitions of waveform shapes that can be used with sources.""" diff --git a/tests/test_models.py b/tests/test_models.py index 542bbc19..d06926fa 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -40,22 +40,22 @@ from tests.analytical_solutions import hertzian_dipole_fs """ basepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'models_') -# basepath += 'basic' +basepath += 'basic' # basepath += 'advanced' -basepath += 'pmls' +# basepath += 'pmls' # List of available basic test models -# testmodels = ['hertzian_dipole_fs_analytical', '2D_ExHyHz', '2D_EyHxHz', '2D_EzHxHy', 'cylinder_Ascan_2D', 'hertzian_dipole_fs', 'hertzian_dipole_hs', 'hertzian_dipole_dispersive', 'magnetic_dipole_fs', 'pmls'] +testmodels = ['hertzian_dipole_fs_analytical', '2D_ExHyHz', '2D_EyHxHz', '2D_EzHxHy', 'cylinder_Ascan_2D', 'hertzian_dipole_fs', 'hertzian_dipole_hs', 'hertzian_dipole_dispersive', 'magnetic_dipole_fs'] # List of available advanced test models # testmodels = ['antenna_GSSI_1500_fs', 'antenna_MALA_1200_fs'] # List of available PML models -testmodels = ['pml_x0', 'pml_y0', 'pml_z0', 'pml_xmax', 'pml_ymax', 'pml_zmax', 'pml_3D_pec_plate'] +# testmodels = ['pml_x0', 'pml_y0', 'pml_z0', 'pml_xmax', 'pml_ymax', 'pml_zmax', 'pml_3D_pec_plate'] # Select a specific model if desired # testmodels = testmodels[:-1] -testmodels = [testmodels[6]] +# testmodels = [testmodels[6]] testresults = dict.fromkeys(testmodels) path = '/rxs/rx1/' @@ -68,7 +68,7 @@ for i, model in enumerate(testmodels): # Run model inputfile = os.path.join(basepath, model + os.path.sep + model + '.in') - api(inputfile, gpu=[None]) + api(inputfile, gpu=None) # Special case for analytical comparison if model == 'hertzian_dipole_fs_analytical': @@ -80,12 +80,12 @@ for i, model in enumerate(testmodels): outputstest = list(filetest[path].keys()) # Arrays for storing time - floattype = filetest[path + outputstest[0]].dtype + float_or_double = filetest[path + outputstest[0]].dtype timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'], num=filetest.attrs['Iterations']) / 1e-9 timeref = timetest # Arrays for storing field data - datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), dtype=floattype) + datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), dtype=float_or_double) for ID, name in enumerate(outputstest): datatest[:, ID] = filetest[path + str(name)][:] if np.any(np.isnan(datatest[:, ID])): @@ -117,18 +117,18 @@ for i, model in enumerate(testmodels): # Check that type of float used to store fields matches if filetest[path + outputstest[0]].dtype != fileref[path + outputsref[0]].dtype: print(Fore.RED + 'WARNING: Type of floating point number in test model ({}) does not match type in reference solution ({})\n'.format(filetest[path + outputstest[0]].dtype, fileref[path + outputsref[0]].dtype) + Style.RESET_ALL) - floattyperef = fileref[path + outputsref[0]].dtype - floattypetest = filetest[path + outputstest[0]].dtype + float_or_doubleref = fileref[path + outputsref[0]].dtype + float_or_doubletest = filetest[path + outputstest[0]].dtype # Arrays for storing time - timeref = np.zeros((fileref.attrs['Iterations']), dtype=floattyperef) + timeref = np.zeros((fileref.attrs['Iterations']), dtype=float_or_doubleref) timeref = np.linspace(0, (fileref.attrs['Iterations'] - 1) * fileref.attrs['dt'], num=fileref.attrs['Iterations']) / 1e-9 - timetest = np.zeros((filetest.attrs['Iterations']), dtype=floattypetest) + timetest = np.zeros((filetest.attrs['Iterations']), dtype=float_or_doubletest) timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'], num=filetest.attrs['Iterations']) / 1e-9 # Arrays for storing field data - dataref = np.zeros((fileref.attrs['Iterations'], len(outputsref)), dtype=floattyperef) - datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), dtype=floattypetest) + dataref = np.zeros((fileref.attrs['Iterations'], len(outputsref)), dtype=float_or_doubleref) + datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), dtype=float_or_doubletest) for ID, name in enumerate(outputsref): dataref[:, ID] = fileref[path + str(name)][:] datatest[:, ID] = filetest[path + str(name)][:]