First commit

这个提交包含在:
Craig Warren
2015-09-30 14:26:59 +01:00
当前提交 843ec686de
共有 122 个文件被更改,包括 16784 次插入0 次删除

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

15
gprMax/__main__.py 普通文件
查看文件

@@ -0,0 +1,15 @@
"""gprMax.__main__: executed when gprMax directory is called as script."""
from .gprMax import main
main()
# Code profiling
# Time profiling
#import cProfile, pstats
#cProfile.run('main()','stats')
#p = pstats.Stats('stats')
#p.sort_stats('time').print_stats(50)
# Memory profiling - use in gprMax.py
# from memory profiler import profile
# add @profile before function to profile

29
gprMax/constants.pxd 普通文件
查看文件

@@ -0,0 +1,29 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
# 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) which are represented as two floats
# Main field arrays use floats (floattype) and complex numbers (complextype)
ctypedef np.float32_t floattype_t
ctypedef np.complex64_t complextype_t

43
gprMax/constants.py 普通文件
查看文件

@@ -0,0 +1,43 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import sys
import decimal as d
import numpy as np
from pyfiglet import Figlet
# 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) which are represented as two floats
# Main field arrays use floats (floattype) and complex numbers (complextype)
floattype = np.float32
complextype = np.complex64
# Speed of light in vacuum (m/s)
c = 2.9979245e8
# Permittivity of free space (F/m)
e0 = 8.854187e-12
# Permeability of free space (H/m)
m0 = 1.256637e-6
# Impedance of free space (Ohms)
z0 = 376.7303134

20
gprMax/exceptions.py 普通文件
查看文件

@@ -0,0 +1,20 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
class CmdInputError(ValueError):
"""Handles errors in user specified commands. Subclasses the ValueError class."""

112
gprMax/fields_output.py 普通文件
查看文件

@@ -0,0 +1,112 @@
import numpy as np
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import h5py
from .constants import floattype
def prepare_output_file(outputfile, G):
"""Prepares an output file in HDF5 format for writing.
Args:
outputfile (str): Name of the output file.
G (class): Grid class instance - holds essential parameters describing the model.
Returns:
f (file object): File object for the file to be written to.
"""
f = h5py.File(outputfile, 'w')
f.attrs['Title'] = G.title
f.attrs['Iterations'] = G.iterations
f.attrs['dx, dy, dz'] = (G.dx, G.dy, G.dz)
f.attrs['dt'] = G.dt
f.attrs['txsteps'] = (G.txstepx, G.txstepy, G.txstepz)
f.attrs['rxsteps'] = (G.rxstepx, G.rxstepy, G.rxstepz)
f.attrs['ntx'] = len(G.voltagesources) + len(G.hertziandipoles) + len(G.magneticdipoles)
f.attrs['nrx'] = len(G.rxs)
# Create groups for txs, rxs
txs = f.create_group('/txs')
rxs = f.create_group('/rxs')
# Add positional data for txs
if G.txs: # G.txs will be populated only if this is being used for converting old style output file to HDF5 format
txlist = G.txs
else:
txlist = G.voltagesources + G.hertziandipoles + G.magneticdipoles
for txindex, tx in enumerate(txlist):
tmp = f.create_group('/txs/tx' + str(txindex + 1))
tmp['Position'] = (tx.positionx * G.dx, tx.positiony * G.dy, tx.positionz * G.dz)
# Add positional data for rxs
for rxindex, rx in enumerate(G.rxs):
tmp = f.create_group('/rxs/rx' + str(rxindex + 1))
tmp['Position'] = (rx.positionx * G.dx, rx.positiony * G.dy, rx.positionz * G.dz)
tmp['Ex'] = np.zeros(G.iterations, dtype=floattype)
tmp['Ey'] = np.zeros(G.iterations, dtype=floattype)
tmp['Ez'] = np.zeros(G.iterations, dtype=floattype)
tmp['Hx'] = np.zeros(G.iterations, dtype=floattype)
tmp['Hy'] = np.zeros(G.iterations, dtype=floattype)
tmp['Hz'] = np.zeros(G.iterations, dtype=floattype)
return f
def write_output(f, timestep, Ex, Ey, Ez, Hx, Hy, Hz, G):
"""Writes field component values to an output file in HDF5 format.
Args:
f (file object): File object for the file to be written to.
timestep (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.
"""
# Normal field writing from main
if type(timestep) is not slice:
# For each rx, write field component values at current timestep
for rxindex, rx in enumerate(G.rxs):
f['/rxs/rx' + str(rxindex + 1) + '/Ex'][timestep] = Ex[rx.positionx, rx.positiony, rx.positionz]
f['/rxs/rx' + str(rxindex + 1) + '/Ey'][timestep] = Ey[rx.positionx, rx.positiony, rx.positionz]
f['/rxs/rx' + str(rxindex + 1) + '/Ez'][timestep] = Ez[rx.positionx, rx.positiony, rx.positionz]
f['/rxs/rx' + str(rxindex + 1) + '/Hx'][timestep] = Hx[rx.positionx, rx.positiony, rx.positionz]
f['/rxs/rx' + str(rxindex + 1) + '/Hy'][timestep] = Hy[rx.positionx, rx.positiony, rx.positionz]
f['/rxs/rx' + str(rxindex + 1) + '/Hz'][timestep] = Hz[rx.positionx, rx.positiony, rx.positionz]
# Field writing when converting old style output file to HDF5 format
else:
if len(G.rxs) == 1:
f['/rxs/rx1/Ex'][timestep] = Ex
f['/rxs/rx1/Ey'][timestep] = Ey
f['/rxs/rx1/Ez'][timestep] = Ez
f['/rxs/rx1/Hx'][timestep] = Hx
f['/rxs/rx1/Hy'][timestep] = Hy
f['/rxs/rx1/Hz'][timestep] = Hz
else:
for rxindex, rx in enumerate(G.rxs):
f['/rxs/rx' + str(rxindex + 1) + '/Ex'][timestep] = Ex[:, rxindex]
f['/rxs/rx' + str(rxindex + 1) + '/Ey'][timestep] = Ey[:, rxindex]
f['/rxs/rx' + str(rxindex + 1) + '/Ez'][timestep] = Ez[:, rxindex]
f['/rxs/rx' + str(rxindex + 1) + '/Hx'][timestep] = Hx[:, rxindex]
f['/rxs/rx' + str(rxindex + 1) + '/Hy'][timestep] = Hy[:, rxindex]
f['/rxs/rx' + str(rxindex + 1) + '/Hz'][timestep] = Hz[:, rxindex]

397
gprMax/fields_update.pyx 普通文件
查看文件

@@ -0,0 +1,397 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
from cython.parallel import prange
from .constants cimport floattype_t, complextype_t
#########################################
# Electric field updates - Ex component #
#########################################
cpdef update_ex(int nx, int ny, int nz, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Hz):
"""This function updates the Ex 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 int i, j, k, listIndex
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
listIndex = ID[0, i, j, k]
Ex[i, j, k] = updatecoeffsE[listIndex, 0] * Ex[i, j, k] + updatecoeffsE[listIndex, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[listIndex, 3] * (Hy[i, j, k] - Hy[i, j, k - 1])
cpdef update_ex_dispersive_multipole_A(int nx, int ny, int nz, int nthreads, int maxpoles, floattype_t[:, :] updatecoeffsE, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Tx, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Hz):
"""This function updates the Ex 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 int i, j, k, listIndex, p
cdef float phi = 0.0
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
listIndex = ID[0, i, j, k]
phi = 0.0
for p in range(0, maxpoles):
phi = phi + updatecoeffsdispersive[listIndex, p * 3].real * Tx[p, i, j, k].real
Tx[p, i, j, k] = updatecoeffsdispersive[listIndex, 1 + (p * 3)] * Tx[p, i, j, k] + updatecoeffsdispersive[listIndex, 2 + (p * 3)] * Ex[i, j, k]
Ex[i, j, k] = updatecoeffsE[listIndex, 0] * Ex[i, j, k] + updatecoeffsE[listIndex, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[listIndex, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[listIndex, 4] * phi
cpdef update_ex_dispersive_multipole_B(int nx, int ny, int nz, int nthreads, int maxpoles, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Tx, floattype_t[:, :, :] Ex):
"""This function updates the Ex 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 (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef int i, j, k, listIndex, p
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
listIndex = ID[0, i, j, k]
for p in range(0, maxpoles):
Tx[p, i, j, k] = Tx[p, i, j, k] - updatecoeffsdispersive[listIndex, 2 + (p * 3)] * Ex[i, j, k]
cpdef update_ex_dispersive_1pole_A(int nx, int ny, int nz, int nthreads, floattype_t[:, :] updatecoeffsE, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Tx, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Hz):
"""This function updates the Ex 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 int i, j, k, listIndex
cdef float phi = 0.0
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
listIndex = ID[0, i, j, k]
phi = updatecoeffsdispersive[listIndex, 0].real * Tx[0, i, j, k].real
Tx[0, i, j, k] = updatecoeffsdispersive[listIndex, 1] * Tx[0, i, j, k] + updatecoeffsdispersive[listIndex, 2] * Ex[i, j, k]
Ex[i, j, k] = updatecoeffsE[listIndex, 0] * Ex[i, j, k] + updatecoeffsE[listIndex, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[listIndex, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[listIndex, 4] * phi
cpdef update_ex_dispersive_1pole_B(int nx, int ny, int nz, int nthreads, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Tx, floattype_t[:, :, :] Ex):
"""This function updates the Ex 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 (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef int i, j, k, listIndex
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
listIndex = ID[0, i, j, k]
Tx[0, i, j, k] = Tx[0, i, j, k] - updatecoeffsdispersive[listIndex, 2] * Ex[i, j, k]
#########################################
# Electric field updates - Ey component #
#########################################
cpdef update_ey(int nx, int ny, int nz, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Hz):
"""This function updates the Ey 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 int i, j, k, listIndex
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
listIndex = ID[1, i, j, k]
Ey[i, j, k] = updatecoeffsE[listIndex, 0] * Ey[i, j, k] + updatecoeffsE[listIndex, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[listIndex, 1] * (Hz[i, j, k] - Hz[i - 1, j, k])
cpdef update_ey_dispersive_multipole_A(int nx, int ny, int nz, int nthreads, int maxpoles, floattype_t[:, :] updatecoeffsE, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Ty, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Hz):
"""This function updates the Ey 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 int i, j, k, listIndex, p
cdef float phi = 0.0
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
listIndex = ID[1, i, j, k]
phi = 0.0
for p in range(0, maxpoles):
phi = phi + updatecoeffsdispersive[listIndex, p * 3].real * Ty[p, i, j, k].real
Ty[p, i, j, k] = updatecoeffsdispersive[listIndex, 1 + (p * 3)] * Ty[p, i, j, k] + updatecoeffsdispersive[listIndex, 2 + (p * 3)] * Ey[i, j, k]
Ey[i, j, k] = updatecoeffsE[listIndex, 0] * Ey[i, j, k] + updatecoeffsE[listIndex, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[listIndex, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[listIndex, 4] * phi
cpdef update_ey_dispersive_multipole_B(int nx, int ny, int nz, int nthreads, int maxpoles, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Ty, floattype_t[:, :, :] Ey):
"""This function updates the Ey 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 int i, j, k, listIndex, p
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
listIndex = ID[1, i, j, k]
for p in range(0, maxpoles):
Ty[p, i, j, k] = Ty[p, i, j, k] - updatecoeffsdispersive[listIndex, 2 + (p * 3)] * Ey[i, j, k]
cpdef update_ey_dispersive_1pole_A(int nx, int ny, int nz, int nthreads, floattype_t[:, :] updatecoeffsE, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Ty, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Hz):
"""This function updates the Ey 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 int i, j, k, listIndex
cdef float phi = 0.0
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
listIndex = ID[1, i, j, k]
phi = updatecoeffsdispersive[listIndex, 0].real * Ty[0, i, j, k].real
Ty[0, i, j, k] = updatecoeffsdispersive[listIndex, 1] * Ty[0, i, j, k] + updatecoeffsdispersive[listIndex, 2] * Ey[i, j, k]
Ey[i, j, k] = updatecoeffsE[listIndex, 0] * Ey[i, j, k] + updatecoeffsE[listIndex, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[listIndex, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[listIndex, 4] * phi
cpdef update_ey_dispersive_1pole_B(int nx, int ny, int nz, int nthreads, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Ty, floattype_t[:, :, :] Ey):
"""This function updates the Ey 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 (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef int i, j, k, listIndex
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
listIndex = ID[1, i, j, k]
Ty[0, i, j, k] = Ty[0, i, j, k] - updatecoeffsdispersive[listIndex, 2] * Ey[i, j, k]
#########################################
# Electric field updates - Ez component #
#########################################
cpdef update_ez(int nx, int ny, int nz, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Hy):
"""This function updates the Ez 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 int i, j, k, listIndex
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
listIndex = ID[2, i, j, k]
Ez[i, j, k] = updatecoeffsE[listIndex, 0] * Ez[i, j, k] + updatecoeffsE[listIndex, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[listIndex, 2] * (Hx[i, j, k] - Hx[i, j - 1, k])
cpdef update_ez_dispersive_multipole_A(int nx, int ny, int nz, int nthreads, int maxpoles, floattype_t[:, :] updatecoeffsE, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Tz, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Hy):
"""This function updates the Ez 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 int i, j, k, listIndex, p
cdef float phi = 0.0
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
listIndex = ID[2, i, j, k]
phi = 0.0
for p in range(0, maxpoles):
phi = phi + updatecoeffsdispersive[listIndex, p * 3].real * Tz[p, i, j, k].real
Tz[p, i, j, k] = updatecoeffsdispersive[listIndex, 1 + (p * 3)] * Tz[p, i, j, k] + updatecoeffsdispersive[listIndex, 2 + (p * 3)] * Ez[i, j, k]
Ez[i, j, k] = updatecoeffsE[listIndex, 0] * Ez[i, j, k] + updatecoeffsE[listIndex, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[listIndex, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[listIndex, 4] * phi
cpdef update_ez_dispersive_multipole_B(int nx, int ny, int nz, int nthreads, int maxpoles, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Tz, floattype_t[:, :, :] Ez):
"""This function updates the Ez 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 (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef int i, j, k, listIndex, p
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
listIndex = ID[2, i, j, k]
for p in range(0, maxpoles):
Tz[p, i, j, k] = Tz[p, i, j, k] - updatecoeffsdispersive[listIndex, 2 + (p * 3)] * Ez[i, j, k]
cpdef update_ez_dispersive_1pole_A(int nx, int ny, int nz, int nthreads, floattype_t[:, :] updatecoeffsE, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Tz, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Hy):
"""This function updates the Ez 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 int i, j, k, listIndex
cdef float phi = 0.0
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
listIndex = ID[2, i, j, k]
phi = updatecoeffsdispersive[listIndex, 0].real * Tz[0, i, j, k].real
Tz[0, i, j, k] = updatecoeffsdispersive[listIndex, 1] * Tz[0, i, j, k] + updatecoeffsdispersive[listIndex, 2] * Ez[i, j, k]
Ez[i, j, k] = updatecoeffsE[listIndex, 0] * Ez[i, j, k] + updatecoeffsE[listIndex, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[listIndex, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[listIndex, 4] * phi
cpdef update_ez_dispersive_1pole_B(int nx, int ny, int nz, int nthreads, complextype_t[:, :] updatecoeffsdispersive, np.uint32_t[:, :, :, :] ID, complextype_t[:, :, :, :] Tz, floattype_t[:, :, :] Ez):
"""This function updates the Ez 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 (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef int i, j, k, listIndex
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
listIndex = ID[2, i, j, k]
Tz[0, i, j, k] = Tz[0, i, j, k] - updatecoeffsdispersive[listIndex, 2] * Ez[i, j, k]
#########################################
# Magnetic field updates - Hx component #
#########################################
cpdef update_hx(int nx, int ny, int nz, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Ez):
"""This function updates the Hx 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 int i, j, k, listIndex
for i in prange(1, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[3, i, j, k]
Hx[i, j, k] = updatecoeffsH[listIndex, 0] * Hx[i, j, k] - updatecoeffsH[listIndex, 2] * (Ez[i, j + 1, k] - Ez[i, j, k]) + updatecoeffsH[listIndex, 3] * (Ey[i, j, k + 1] - Ey[i, j, k])
#########################################
# Magnetic field updates - Hy component #
#########################################
cpdef update_hy(int nx, int ny, int nz, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Ez):
"""This function updates the Hy 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 int i, j, k, listIndex
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
listIndex = ID[4, i, j, k]
Hy[i, j, k] = updatecoeffsH[listIndex, 0] * Hy[i, j, k] - updatecoeffsH[listIndex, 3] * (Ex[i, j, k + 1] - Ex[i, j, k]) + updatecoeffsH[listIndex, 1] * (Ez[i + 1, j, k] - Ez[i, j, k])
#########################################
# Magnetic field updates - Hz component #
#########################################
cpdef update_hz(int nx, int ny, int nz, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hz, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Ey):
"""This function updates the Hz 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 int i, j, k, listIndex
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
listIndex = ID[5, i, j, k]
Hz[i, j, k] = updatecoeffsH[listIndex, 0] * Hz[i, j, k] - updatecoeffsH[listIndex, 1] * (Ey[i + 1, j, k] - Ey[i, j, k]) + updatecoeffsH[listIndex, 2] * (Ex[i, j + 1, k] - Ex[i, j, k])

265
gprMax/fractals.py 普通文件
查看文件

@@ -0,0 +1,265 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
np.seterr(divide='raise')
from .constants import floattype, complextype
from .utilities import rvalue
class FractalSurface():
"""Fractal surfaces."""
surfaceIDs = ['xminus', 'xplus', 'yminus', 'yplus', 'zminus', 'zplus']
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.
"""
self.ID = None
self.surfaceID = None
self.xs = xs
self.xf = xf
self.ys = ys
self.yf = yf
self.zs = zs
self.zf = zf
self.nx = xf - xs
self.ny = yf - ys
self.nz = zf - zs
self.seed = None
self.dimension = dimension
# Constant related to fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695
self.b = -(2 * self.dimension - 7) / 2
self.weighting = (1, 1)
self.fractalrange = (0, 0)
self.filldepth = 0
self.grass = []
def generate_fractal_surface(self, G):
"""Generate a 2D array with a fractal distribution.
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
if self.xs == self.xf:
surfacedims = (self.ny + 1, self.nz + 1)
d = G.dx
elif self.ys == self.yf:
surfacedims = (self.nx + 1, self.nz + 1)
d = G.dy
elif self.zs == self.zf:
surfacedims = (self.nx + 1, self.ny + 1)
d = G.dz
self.fractalsurface = np.zeros(surfacedims, dtype=complextype)
# Positional vector at centre of array, scaled by weighting
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)
A = R.randn(surfacedims[0], surfacedims[1])
# 2D FFT
A = np.fft.fftn(A)
for i in range(surfacedims[0]):
for j in range(surfacedims[1]):
# Positional vector for current position
v2 = np.array([self.weighting[0]*i, self.weighting[1]*j])
rr = np.linalg.norm(v2 - v1)
try:
self.fractalsurface[i, j] = A[i, j] * 1/(rr**self.b)
except FloatingPointError:
rr = 0.9
self.fractalsurface[i, j] = A[i, j] * 1/(rr**self.b)
# Shift the zero frequency component to the centre of the spectrum
self.fractalsurface = np.fft.ifftshift(self.fractalsurface)
# Take the real part (numerical errors can give rise to an imaginary part) of the IFFT
self.fractalsurface = np.real(np.fft.ifftn(self.fractalsurface))
# Scale the fractal volume according to requested range
fractalmin = np.amin(self.fractalsurface)
fractalmax = np.amax(self.fractalsurface)
fractalrange = fractalmax - fractalmin
self.fractalsurface = self.fractalsurface * ((self.fractalrange[1] - self.fractalrange[0])/fractalrange) + self.fractalrange[0] - ((self.fractalrange[1] - self.fractalrange[0])/fractalrange) * fractalmin
class FractalVolume():
"""Fractal volumes."""
def __init__(self, xs, xf, ys, yf, zs, zf, dimension):
"""
Args:
xs, xf, ys, yf, zs, zf (float): Extent of the fractal volume.
dimension (float): Fractal dimension that controls the fractal distribution.
"""
self.ID = None
self.operatingonID= None
self.xs = xs
self.xf = xf
self.ys = ys
self.yf = yf
self.zs = zs
self.zf = zf
self.nx = xf - xs
self.ny = yf - ys
self.nz = zf - zs
self.seed = None
self.dimension = dimension
# Constant related to fractal dimension from: http://dx.doi.org/10.1017/CBO9781139174695
self.b = -(2 * self.dimension - 7) / 2
self.weighting = (1, 1, 1)
self.nbins = 0
self.fractalsurfaces = []
def generate_fractal_volume(self, G):
"""Generate a 3D volume with a fractal distribution.
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
self.fractalvolume = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=complextype)
# Positional vector at centre of array, scaled by weighting
v1 = np.array([self.weighting[0]*(self.nx + 1)/2, self.weighting[1]*(self.ny + 1)/2, self.weighting[2]*(self.nz + 1)/2])
# 3D array of random numbers to be convolved with the fractal function
R = np.random.RandomState(self.seed)
A = R.randn(self.nx + 1, self.ny + 1, self.nz + 1)
# 3D FFT
A = np.fft.fftn(A)
for i in range(self.nx + 1):
for j in range(self.ny + 1):
for k in range(self.nz + 1):
# Positional vector for current position
v2 = np.array([self.weighting[0]*i, self.weighting[1]*j, self.weighting[2]*k])
rr = np.linalg.norm(v2 - v1)
try:
self.fractalvolume[i, j, k] = A[i, j, k] * 1/(rr**self.b)
except FloatingPointError:
rr = 0.9
self.fractalvolume[i, j, k] = A[i, j, k] * 1/(rr**self.b)
# Shift the zero frequency component to the centre of the spectrum
self.fractalvolume = np.fft.ifftshift(self.fractalvolume)
# Take the real part (numerical errors can give rise to an imaginary part) of the IFFT
self.fractalvolume = np.real(np.fft.ifftn(self.fractalvolume))
# Bin fractal values
bins = np.linspace(np.amin(self.fractalvolume), np.amax(self.fractalvolume), self.nbins + 1)
for j in range(self.ny + 1):
for k in range(self.nz + 1):
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."""
self.mask = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.int8)
maskxs = self.originalxs - self.xs
maskxf = (self.originalxf - self.originalxs) + maskxs + 1
maskys = self.originalys - self.ys
maskyf = (self.originalyf - self.originalys) + maskys + 1
maskzs = self.originalzs - self.zs
maskzf = (self.originalzf - self.originalzs) + maskzs + 1
self.mask[maskxs:maskxf, maskys:maskyf, maskzs:maskzf] = 1
class Grass():
"""Geometry information for blades of grass."""
def __init__(self, numblades):
"""
Args:
numblades (int): Number of blades of grass.
"""
self.numblades = numblades
self.geometryparams = np.zeros((self.numblades, 6), dtype=floattype)
self.seed = None
# Randomly defined parameters that will be used to calculate geometry
self.R1 = np.random.RandomState(self.seed)
self.R2 = np.random.RandomState(self.seed)
self.R3 = np.random.RandomState(self.seed)
self.R4 = np.random.RandomState(self.seed)
self.R5 = np.random.RandomState(self.seed)
self.R6 = np.random.RandomState(self.seed)
for i in range(self.numblades):
self.geometryparams[i, 0] = 10 + 20 * self.R1.random_sample()
self.geometryparams[i, 1] = 10 + 20 * self.R2.random_sample()
self.geometryparams[i, 2] = self.R3.choice([-1, 1])
self.geometryparams[i, 3] = self.R4.choice([-1, 1])
def calculate_blade_geometry(self, blade, height):
"""Calculates the x and y coordinates for a given height of grass blade.
Args:
blade (int): Numeric ID of grass blade.
height (float): Height of grass blade.
Returns:
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 = rvalue(x)
y = rvalue(y)
return x, y
def calculate_root_geometry(self, root, depth):
"""Calculates the x and y coordinates for a given depth of grass root.
Args:
root (int): Numeric ID of grass root.
depth (float): Depth of grass root.
Returns:
x, y (float): x and y coordinates of grass root.
"""
self.geometryparams[root, 4] += -1 + 2 * self.R5.random_sample()
self.geometryparams[root, 5] += -1 + 2 * self.R6.random_sample()
x = round(self.geometryparams[root, 4])
y = round(self.geometryparams[root, 5])
return x, y

查看文件

@@ -0,0 +1,600 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
np.seterr(divide='raise')
from .utilities import rvalue
from .yee_cell_setget_rigid cimport set_rigid_Ex, set_rigid_Ey, set_rigid_Ez, set_rigid_Hx, set_rigid_Hy, set_rigid_Hz, set_rigid_E, unset_rigid_E, set_rigid_H, unset_rigid_H
cpdef bint are_clockwise(float v1x, float v1y, float v2x, float v2y):
"""Find if vector 2 is clockwise relative to vector 1.
Args:
v1x, v1y, v2x, v2y (float): Coordinates of vectors.
Returns:
(boolean)
"""
return -v1x*v2y + v1y*v2x > 0
cpdef bint is_within_radius(float vx, float vy, float radius):
"""Check if the point is within a given radius of the centre of the circle.
Args:
vx, vy (float): Coordinates of vector.
radius (float): Radius.
Returns:
(boolean)
"""
return vx*vx + vy*vy <= radius*radius
cpdef bint is_inside_sector(float px, float py, float ctrx, float ctry, float sectorstartangle, float sectorangle, float radius):
"""For a point to be inside a circular sector, it has to meet the following tests:
It has to be positioned anti-clockwise from the start "arm" of the sector
It has to be positioned clockwise from the end arm of the sector
It has to be closer to the center of the circle than the sectors radius.
Assumes sector start is always clockwise from sector end,
i.e. sector defined in an anti-clockwise direction
Args:
px, py (float): Coordinates of point.
ctrx, ctry (float): Coordinates of centre of circle.
sectorstartangle (float): Angle (in radians) of start of sector.
sectorangle (float): Angle (in radians) that sector makes.
radius (float): Radius.
Returns:
(boolean)
"""
cdef float sectorstart1, sectorstart2, sectorend1, sectorend2, relpoint1, relpoint2
sectorstart1 = radius * np.cos(sectorstartangle)
sectorstart2 = radius * np.sin(sectorstartangle)
sectorend1 = radius * np.cos(sectorstartangle + sectorangle)
sectorend2 = radius * np.sin(sectorstartangle + sectorangle)
relpoint1 = px - ctrx
relpoint2 = py - ctry
return not are_clockwise(sectorstart1, sectorstart2, relpoint1, relpoint2) and are_clockwise(sectorend1, sectorend2, relpoint1, relpoint2) and is_within_radius(relpoint1, relpoint2, radius)
cpdef build_edge_x(int i, int j, int k, int numIDx, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Set x-orientated edges in the rigid and ID arrays for a Yee voxel.
Args:
i, j, k (int): Cell coordinates of edge.
numIDz (int): Numeric ID of material.
rigidE, rigidH, ID (memoryviews): Access to rigid and ID arrays.
"""
set_rigid_Ex(i, j, k, rigidE)
ID[0, i, j, k] = numIDx
cpdef build_edge_y(int i, int j, int k, int numIDy, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Set y-orientated edges in the rigid and ID arrays for a Yee voxel.
Args:
i, j, k (int): Cell coordinates of edge.
numIDz (int): Numeric ID of material.
rigidE, rigidH, ID (memoryviews): Access to rigid and ID arrays.
"""
set_rigid_Ey(i, j, k, rigidE)
ID[1, i, j, k] = numIDy
cpdef build_edge_z(int i, int j, int k, int numIDz, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Set z-orientated edges in the rigid and ID arrays for a Yee voxel.
Args:
i, j, k (int): Cell coordinates of edge.
numIDz (int): Numeric ID of material.
rigidE, rigidH, ID (memoryviews): Access to rigid and ID arrays.
"""
set_rigid_Ez(i, j, k, rigidE)
ID[2, i, j, k] = numIDz
cpdef build_face_yz(int i, int j, int k, int numIDy, int numIDz, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Set the edges of the yz-plane face of a Yell cell in the rigid and ID arrays.
Args:
i, j, k (int): Cell coordinates of the face.
numIDx, numIDy (int): Numeric ID of material.
rigidE, rigidH, ID (memoryviews): Access to rigid and ID arrays.
"""
set_rigid_Ey(i, j, k, rigidE)
set_rigid_Ez(i, j, k, rigidE)
set_rigid_Ey(i, j, k + 1, rigidE)
set_rigid_Ez(i, j + 1, k, rigidE)
set_rigid_Hy(i, j, k, rigidH)
set_rigid_Hz(i, j, k, rigidH)
set_rigid_Hy(i, j, k + 1, rigidH)
set_rigid_Hz(i, j + 1, k, rigidH)
ID[1, i, j, k] = numIDy
ID[2, i, j, k] = numIDz
ID[1, i, j, k + 1] = numIDy
ID[2, i, j + 1, k] = numIDz
ID[4, i, j, k] = numIDy
ID[5, i, j, k] = numIDz
ID[4, i, j, k + 1] = numIDy
ID[5, i, j + 1, k] = numIDz
cpdef build_face_xz(int i, int j, int k, int numIDx, int numIDz, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Set the edges of the xz-plane face of a Yell cell in the rigid and ID arrays.
Args:
i, j, k (int): Cell coordinates of the face.
numIDx, numIDy (int): Numeric ID of material.
rigidE, rigidH, ID (memoryviews): Access to rigid and ID arrays.
"""
set_rigid_Ex(i, j, k, rigidE)
set_rigid_Ez(i, j, k, rigidE)
set_rigid_Ex(i, j, k + 1, rigidE)
set_rigid_Ez(i + 1, j, k, rigidE)
set_rigid_Hx(i, j, k, rigidH)
set_rigid_Hz(i, j, k, rigidH)
set_rigid_Hx(i, j, k + 1, rigidH)
set_rigid_Hz(i + 1, j, k, rigidH)
ID[0, i, j, k] = numIDx
ID[2, i, j, k] = numIDz
ID[0, i, j, k + 1] = numIDx
ID[2, i + 1, j, k] = numIDz
ID[3, i, j, k] = numIDx
ID[5, i, j, k] = numIDz
ID[3, i, j, k + 1] = numIDx
ID[5, i + 1, j, k] = numIDz
cpdef build_face_xy(int i, int j, int k, int numIDx, int numIDy, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Set the edges of the xy-plane face of a Yell cell in the rigid and ID arrays.
Args:
i, j, k (int): Cell coordinates of the face.
numIDx, numIDy (int): Numeric ID of material.
rigidE, rigidH, ID (memoryviews): Access to rigid and ID arrays.
"""
set_rigid_Ex(i, j, k, rigidE)
set_rigid_Ey(i, j, k, rigidE)
set_rigid_Ex(i, j + 1, k, rigidE)
set_rigid_Ey(i + 1, j, k, rigidE)
set_rigid_Hx(i, j, k, rigidH)
set_rigid_Hy(i, j, k, rigidH)
set_rigid_Hx(i, j + 1, k, rigidH)
set_rigid_Hy(i + 1, j, k, rigidH)
ID[0, i, j, k] = numIDx
ID[1, i, j, k] = numIDy
ID[0, i, j + 1, k] = numIDx
ID[1, i + 1, j, k] = numIDy
ID[3, i, j, k] = numIDx
ID[4, i, j, k] = numIDy
ID[3, i, j + 1, k] = numIDx
ID[4, i + 1, j, k] = numIDy
cpdef build_voxel(int i, int j, int k, int numID, int numIDx, int numIDy, int numIDz, bint averaging, np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Set values in the solid, rigid and ID arrays for a Yee voxel.
Args:
i, j, k (int): Cell coordinates of voxel.
numID, numIDx, numIDy, numIDz (int): Numeric ID of material.
averaging (bint): Whether material property averging will occur for the object.
solid, rigidE, rigidH, ID (memoryviews): Access to solid, rigid and ID arrays.
"""
if averaging:
solid[i, j, k] = numID
unset_rigid_E(i, j, k, rigidE)
unset_rigid_H(i, j, k, rigidH)
else:
solid[i, j, k] = numID
set_rigid_E(i, j, k, rigidE)
set_rigid_H(i, j, k, rigidH)
ID[0, i, j, k] = numIDx
ID[0, i, j + 1, k + 1] = numIDx
ID[0, i, j + 1, k] = numIDx
ID[0, i, j, k + 1] = numIDx
ID[1, i, j, k] = numIDy
ID[1, i + 1, j, k + 1] = numIDy
ID[1, i + 1, j, k] = numIDy
ID[1, i, j, k + 1] = numIDy
ID[2, i, j, k] = numIDz
ID[2, i + 1, j + 1, k] = numIDz
ID[2, i + 1, j, k] = numIDz
ID[2, i, j + 1, k] = numIDz
ID[3, i, j, k] = numIDx
ID[3, i, j + 1, k + 1] = numIDx
ID[3, i, j + 1, k] = numIDx
ID[3, i, j, k + 1] = numIDx
ID[4, i, j, k] = numIDy
ID[4, i + 1, j, k + 1] = numIDy
ID[4, i + 1, j, k] = numIDy
ID[4, i, j, k + 1] = numIDy
ID[5, i, j, k] = numIDz
ID[5, i + 1, j + 1, k] = numIDz
ID[5, i + 1, j, k] = numIDz
ID[5, i, j + 1, k] = numIDz
cpdef build_triangle(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, str normal, int thickness, float dx, float dy, float dz, int numID, int numIDx, int numIDy, int numIDz, bint averaging, np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Builds #triangle and #triangular_prism commands which sets values in the solid, rigid and ID arrays for a Yee voxel.
Args:
x1, y1, z1, x2, y2, z2, x3, y3, z3 (float): Coordinates of the vertices of the triangular prism.
normal (char): Normal direction to the plane of the triangular prism.
thickness (int): Thickness of the triangular prism.
dx, dy, dz (float): Spatial discretisation.
numID, numIDx, numIDy, numIDz (int): Numeric ID of material.
averaging (bint): Whether material property averging will occur for the object.
solid, rigidE, rigidH, ID (memoryviews): Access to solid, rigid and ID arrays.
"""
cdef int i, j, k, i1, i2, j1, j2, sign, level
cdef float area, s, t
# Calculate a bounding box for the triangle
if normal == 'x':
area = 0.5 * (-z2 * y3 + z1 * (-y2 + y3) + y1 * (z2 - z3) + y2 * z3)
i1 = rvalue(np.amin([y1, y2, y3]) / dy) - 1
i2 = rvalue(np.amax([y1, y2, y3]) / dy) + 1
j1 = rvalue(np.amin([z1, z2, z3]) / dz) - 1
j2 = rvalue(np.amax([z1, z2, z3]) / dz) + 1
level = rvalue(x1 / dx)
elif normal == 'y':
area = 0.5 * (-z2 * x3 + z1 * (-x2 + x3) + x1 * (z2 - z3) + x2 * z3)
i1 = rvalue(np.amin([x1, x2, x3]) / dx) - 1
i2 = rvalue(np.amax([x1, x2, x3]) / dx) + 1
j1 = rvalue(np.amin([z1, z2, z3]) / dz) - 1
j2 = rvalue(np.amax([z1, z2, z3]) / dz) + 1
level = rvalue(y1 /dy)
elif normal == 'z':
area = 0.5 * (-y2 * x3 + y1 * (-x2 + x3) + x1 * (y2 - y3) + x2 * y3)
i1 = rvalue(np.amin([x1, x2, x3]) / dx) - 1
i2 = rvalue(np.amax([x1, x2, x3]) / dx) + 1
j1 = rvalue(np.amin([y1, y2, y3]) / dy) - 1
j2 = rvalue(np.amax([y1, y2, y3]) / dy) + 1
level = rvalue(z1 / dz)
sign = np.sign(area)
for i in range(i1, i2):
for j in range(j1, j2):
# Calculate the areas of the 3 triangles defined by the 3 vertices of the main triangle and the point under test
if normal == 'x':
ir = (i + 0.5) * dy
jr = (j + 0.5) * dz
s = sign * (z1 * y3 - y1 * z3 + (z3 - z1) * ir + (y1 - y3) * jr);
t = sign * (y1 * z2 - z1 * y2 + (z1 - z2) * ir + (y2 - y1) * jr);
elif normal == 'y':
ir = (i + 0.5) * dx
jr = (j + 0.5) * dz
s = sign * (z1 * x3 - x1 * z3 + (z3 - z1) * ir + (x1 - x3) * jr);
t = sign * (x1 * z2 - z1 * x2 + (z1 - z2) * ir + (x2 - x1) * jr);
elif normal == 'z':
ir = (i + 0.5) * dx
jr = (j + 0.5) * dy
s = sign * (y1 * x3 - x1 * y3 + (y3 - y1) * ir + (x1 - x3) * jr);
t = sign * (x1 * y2 - y1 * x2 + (y1 - y2) * ir + (x2 - x1) * jr);
# If these conditions are true then point is inside triangle
if s > 0 and t > 0 and (s + t) < 2 * area * sign:
if thickness == 0:
if normal == 'x':
build_face_yz(level, i, j, numIDy, numIDz, rigidE, rigidH, ID)
elif normal == 'y':
build_face_xz(i, level, j, numIDx, numIDz, rigidE, rigidH, ID)
elif normal == 'z':
build_face_xy(i, j, level, numIDx, numIDy, rigidE, rigidH, ID)
else:
for k in range(level, level + thickness):
if normal == 'x':
build_voxel(k, i, j, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID)
elif normal == 'y':
build_voxel(i, k, j, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID)
elif normal == 'z':
build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID)
cpdef build_cylindrical_sector(float ctr1, float ctr2, int level, float sectorstartangle, float sectorangle, float radius, str normal, int thickness, float dx, float dy, float dz, int numID, int numIDx, int numIDy, int numIDz, bint averaging, np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Builds #cylindrical_sector commands which sets values in the solid, rigid and ID arrays for a Yee voxel. It defines a sector of cylinder given by the direction of the axis of the coordinates of the cylinder face centre, depth coordinates, sector start point, sector angle, and sector radius. N.B Assumes sector start is always clockwise from sector end, i.e. sector defined in an anti-clockwise direction.
Args:
ctr1, ctr2 (float): Coordinates of centre of circle.
level (int): Third dimensional coordinate.
sectorstartangle (float): Angle (in radians) of start of sector.
sectorangle (float): Angle (in radians) that sector makes.
radius (float): Radius of the cylindrical sector.
normal (char): Normal direction to the plane of the cylindrical sector.
thickness (int): Thickness of the cylindrical sector.
dx, dy, dz (float): Spatial discretisation.
numID, numIDx, numIDy, numIDz (int): Numeric ID of material.
averaging (bint): Whether material property averging will occur for the object.
solid, rigidE, rigidH, ID (memoryviews): Access to solid, rigid and ID arrays.
"""
cdef int x1, x2, y1, y2, z1, z2, x, y, z
if normal == 'x':
# Angles are defined from zero degrees on the positive y-axis going towards positive z-axis
y1 = rvalue((ctr1 - radius)/dy)
y2 = rvalue((ctr1 + radius)/dy)
z1 = rvalue((ctr2 - radius)/dz)
z2 = rvalue((ctr2 + radius)/dz)
for y in range(y1, y2):
for z in range(z1, z2):
if is_inside_sector(y * dy + 0.5 * dy, z * dz + 0.5 * dz, ctr1, ctr2, sectorstartangle, sectorangle, radius):
if thickness == 0:
build_face_yz(level, y, z, numIDy, numIDz, rigidE, rigidH, ID)
else:
for x in range(level, level + thickness):
build_voxel(x, y, z, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID)
elif normal == 'y':
# Angles are defined from zero degrees on the positive x-axis going towards positive z-axis
x1 = rvalue((ctr1 - radius)/dx)
x2 = rvalue((ctr1 + radius)/dx)
z1 = rvalue((ctr2 - radius)/dz)
z2 = rvalue((ctr2 + radius)/dz)
for x in range(x1, x2):
for z in range(z2, z2):
if is_inside_sector(x * dx + 0.5 * dx, z * dz + 0.5 * dz, ctr1, ctr2, sectorstartangle, sectorangle, radius):
if thickness == 0:
build_face_xz(x, level, z, numIDx, numIDz, rigidE, rigidH, ID)
else:
for y in range(level, level + thickness):
build_voxel(x, y, z, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID)
elif normal == 'z':
# Angles are defined from zero degrees on the positive x-axis going towards positive y-axis
x1 = rvalue((ctr1 - radius)/dx)
x2 = rvalue((ctr1 + radius)/dx)
y1 = rvalue((ctr2 - radius)/dy)
y2 = rvalue((ctr2 + radius)/dy)
for x in range(x1, x2):
for y in range(y1, y2):
if is_inside_sector(x * dx + 0.5 * dx, y * dy + 0.5 * dy, ctr1, ctr2, sectorstartangle, sectorangle, radius):
if thickness == 0:
build_face_xy(x, y, level, numIDx, numIDy, rigidE, rigidH, ID)
else:
for z in range(level, level + thickness):
build_voxel(x, y, z, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID)
cpdef build_box(int xs, int xf, int ys, int yf, int zs, int zf, int numID, int numIDx, int numIDy, int numIDz, bint averaging, np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Builds #box commands which sets values in the solid, rigid and ID arrays.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box.
numID, numIDx, numIDy, numIDz (int): Numeric ID of material.
averaging (bint): Whether material property averging will occur for the object.
solid, rigidE, rigidH, ID (memoryviews): Access to solid, rigid and ID arrays.
"""
cdef int i, j, k
if averaging:
for i in range(xs, xf):
for j in range(ys, yf):
for k in range(zs, zf):
solid[i, j, k] = numID
unset_rigid_E(i, j, k, rigidE)
unset_rigid_H(i, j, k, rigidH)
else:
for i in range(xs, xf):
for j in range(ys, yf):
for k in range(zs, zf):
solid[i, j, k] = numID
set_rigid_E(i, j, k, rigidE)
set_rigid_H(i, j, k, rigidH)
for i in range(xs, xf):
for j in range(ys, yf + 1):
for k in range(zs, zf + 1):
ID[0, i, j, k] = numIDx
for i in range(xs, xf + 1):
for j in range(ys, yf):
for k in range(zs, zf + 1):
ID[1, i, j, k] = numIDy
for i in range(xs, xf + 1):
for j in range(ys, yf + 1):
for k in range(zs, zf):
ID[2, i, j, k] = numIDz
for i in range(xs, xf + 1):
for j in range(ys, yf):
for k in range(zs, zf):
ID[3, i, j, k] = numIDx
for i in range(xs, xf):
for j in range(ys, yf + 1):
for k in range(zs, zf):
ID[4, i, j, k] = numIDy
for i in range(xs, xf):
for j in range(ys, yf):
for k in range(zs, zf + 1):
ID[5, i, j, k] = numIDz
cpdef build_cylinder(float x1, float y1, float z1, float x2, float y2, float z2, float r, float dx, float dy, float dz, int numID, int numIDx, int numIDy, int numIDz, bint averaging, np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Builds #cylinder commands which sets values in the solid, rigid and ID arrays for a Yee voxel.
Args:
x1, y1, z1, x2, y2, z2 (float): Coordinates of the centres of cylinder faces.
r (float): Radius of the cylinder.
dx, dy, dz (float): Spatial discretisation.
numID, numIDx, numIDy, numIDz (int): Numeric ID of material.
averaging (bint): Whether material property averging will occur for the object.
solid, rigidE, rigidH, ID (memoryviews): Access to solid, rigid and ID arrays.
"""
cdef int i, j, k, xs, xf, ys, yf, zs, zf
cdef float f1f2mag, f2f1mag, f1ptmag, f2ptmag, dot1, dot2, factor1, factor2, theta1, theta2, distance1, distance2
cdef bint build
cdef np.ndarray f1f2, f2f1, f1pt, f2pt
# Calculate a bounding box for the cylinder
if x1 < x2:
xs = rvalue((x1 - r) / dx) - 1
xf = rvalue((x2 + r) / dx) + 1
else:
xs = rvalue((x2 - r) / dx) - 1
xf = rvalue((x1 + r) / dx) + 1
if y1 < y2:
ys = rvalue((y1 - r) / dy) - 1
yf = rvalue((y2 + r) / dy) + 1
else:
ys = rvalue((y2 - r) / dy) - 1
yf = rvalue((y1 + r) / dy) + 1
if z1 < z2:
zs = rvalue((z1 - r) / dz) - 1
zf = rvalue((z2 + r) / dz) + 1
else:
zs = rvalue((z2 - r) / dz) - 1
zf = rvalue((z1 + r) / dz) + 1
# Set bounds to domain if they outside
if xs < 0:
xs = 0
if xf >= solid.shape[0]:
xf = solid.shape[0] - 1
if ys < 0:
ys = 0
if yf >= solid.shape[1]:
yf = solid.shape[1] - 1
if zs < 0:
zs = 0
if zf >= solid.shape[2]:
zf = solid.shape[2] - 1
# Vectors between centres of cylinder faces
f1f2 = np.array([x2 - x1, y2 - y1, z2 - z1], dtype=np.float32)
f2f1 = np.array([x1 - x2, y1 - y2, z1 - z2], dtype=np.float32)
# Magnitudes
f1f2mag = np.sqrt((f1f2*f1f2).sum(axis=0))
f2f1mag = np.sqrt((f2f1*f2f1).sum(axis=0))
for i in range(xs, xf):
for j in range(ys, yf):
for k in range(zs, zf):
# Build flag - default false, set to True if point is in cylinder
build = 0
# Vector from centre of first cylinder face to test point
f1pt = np.array([i * dx + 0.5 * dx - x1, j * dy + 0.5 * dy - y1, k * dz + 0.5 * dz - z1], dtype=np.float32)
# Vector from centre of second cylinder face to test point
f2pt = np.array([i * dx + 0.5 * dx - x2, j * dy + 0.5 * dy - y2, k * dz + 0.5 * dz - z2], dtype=np.float32)
# Magnitudes
f1ptmag = np.sqrt((f1pt*f1pt).sum(axis=0))
f2ptmag = np.sqrt((f2pt*f2pt).sum(axis=0))
# Dot products
dot1 = np.dot(f1f2, f1pt)
dot2 = np.dot(f2f1, f2pt)
if f1ptmag == 0 or f2ptmag == 0:
build = 1
else:
factor1 = dot1 / (f1f2mag * f1ptmag)
factor2 = dot2 / (f2f1mag * f2ptmag)
# Catch cases where either factor1 or factor2 are 1
try:
theta1 = np.arccos(factor1)
except FloatingPointError:
theta1 = 0
try:
theta2 = np.arccos(factor2)
except FloatingPointError:
theta2 = 0
distance1 = f1ptmag * np.sin(theta1)
distance2 = f2ptmag * np.sin(theta2)
if (distance1 <= r or distance2 <= r) and theta1 <= np.pi/2 and theta2 <= np.pi/2:
build = 1
if build:
build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID)
cpdef build_sphere(int xc, int yc, int zc, float r, float dx, float dy, float dz, int numID, int numIDx, int numIDy, int numIDz, bint averaging, np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidE, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID):
"""Builds #sphere commands which sets values in the solid, rigid and ID arrays for a Yee voxel.
Args:
xc, yc, zc (int): Cell coordinates of the centre of the sphere.
r (float): Radius of the sphere.
dx, dy, dz (float): Spatial discretisation.
numID, numIDx, numIDy, numIDz (int): Numeric ID of material.
averaging (bint): Whether material property averging will occur for the object.
solid, rigidE, rigidH, ID (memoryviews): Access to solid, rigid and ID arrays.
"""
cdef int i, j, k, xs, xf, ys, yf, zs, zf
# Calculate a bounding box for sphere
xs = rvalue(((xc * dx) - r) / dx) - 1
xf = rvalue(((xc * dx) + r) / dx) + 1
ys = rvalue(((yc * dy) - r) / dy) - 1
yf = rvalue(((yc * dy) + r) / dy) + 1
zs = rvalue(((zc * dz) - r) / dz) - 1
zf = rvalue(((zc * dz) + r) / dz) + 1
# Set bounds to domain if they outside
if xs < 0:
xs = 0
if xf >= solid.shape[0]:
xf = solid.shape[0] - 1
if ys < 0:
ys = 0
if yf >= solid.shape[1]:
yf = solid.shape[1] - 1
if zs < 0:
zs = 0
if zf >= solid.shape[2]:
zf = solid.shape[2] - 1
for i in range(xs, xf):
for j in range(ys, yf):
for k in range(zs, zf):
if np.sqrt((i - xc)**2 * dx**2 + (j - yc)**2 * dy**2 + (k - zc)**2 * dz**2) <= r:
build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID)

205
gprMax/geometry_views.py 普通文件
查看文件

@@ -0,0 +1,205 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import sys
import numpy as np
from struct import pack
from .utilities import rvalue
class GeometryView:
"""Views of the geometry of the model."""
if sys.byteorder == 'little':
byteorder = 'LittleEndian'
else:
byteorder = 'BigEndian'
def __init__(self, xs=None, ys=None, zs=None, xf=None, yf=None, zf=None, dx=None, dy=None, dz=None, filename=None, type=None):
"""
Args:
xs, xf, ys, yf, zs, zf (float): Extent of the volume.
dx, dy, dz (float): Spatial discretisation.
filename (str): Filename to save to.
type (str): Either 'n' for a per cell geometry view, or 'f' for a per cell edge geometry view.
"""
self.xs = xs
self.ys = ys
self.zs = zs
self.xf = xf
self.yf = yf
self.zf = zf
self.dx = dx
self.dy = dy
self.dz = dz
self.filename = filename
self.type = type
def write_file(self, modelrun, numbermodelruns, G):
"""Writes the geometry information to a VTK file. Either ImageData (.vti) for a per cell geometry view, or PolygonalData (.vtp) for a per cell edge geometry view.
Args:
modelrun (int): Current model run number.
numbermodelruns (int): Total number of model runs.
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Construct filename from user-supplied name and model run number
if numbermodelruns == 1:
self.filename = G.inputdirectory + self.filename
else:
self.filename = G.inputdirectory + self.filename + '_' + str(modelrun)
# No Python 3 support for VTK at time of writing (03/2015)
self.vtk_nx = self.xf - self.xs
self.vtk_ny = self.yf - self.ys
self.vtk_nz = self.zf - self.zs
if self.type == 'n':
self.filename += '.vti'
# Calculate number of cells according to requested sampling
self.vtk_xscells = rvalue(self.xs / self.dx)
self.vtk_xfcells = rvalue(self.xf / self.dx)
self.vtk_yscells = rvalue(self.ys / self.dy)
self.vtk_yfcells = rvalue(self.yf / self.dy)
self.vtk_zscells = rvalue(self.zs / self.dz)
self.vtk_zfcells = rvalue(self.zf / self.dz)
with open(self.filename, 'wb') as f:
f.write('<?xml version="1.0"?>\n'.encode('utf-8'))
f.write('<VTKFile type="ImageData" version="1.0" byte_order="{}">\n'.format(GeometryView.byteorder).encode('utf-8'))
f.write('<ImageData WholeExtent="{} {} {} {} {} {}" Origin="0 0 0" Spacing="{:.3} {:.3} {:.3}">\n'.format(self.vtk_xscells, self.vtk_xfcells, self.vtk_yscells, self.vtk_yfcells, self.vtk_zscells, self.vtk_zfcells, self.dx * G.dx, self.dy * G.dy, self.dz * G.dz).encode('utf-8'))
f.write('<Piece Extent="{} {} {} {} {} {}">\n'.format(self.vtk_xscells, self.vtk_xfcells, self.vtk_yscells, self.vtk_yfcells, self.vtk_zscells, self.vtk_zfcells).encode('utf-8'))
f.write('<CellData Scalars="Material">\n'.encode('utf-8'))
f.write('<DataArray type="UInt32" Name="Material" format="appended" offset="0" />\n'.encode('utf-8'))
f.write('</CellData>\n</Piece>\n</ImageData>\n<AppendedData encoding="raw">\n_'.encode('utf-8'))
# Calculate number of bytes of appended data section
datasize = rvalue(np.dtype(np.uint32).itemsize * (self.vtk_nx / self.dx) * (self.vtk_ny / self.dy) * (self.vtk_nz / self.dz))
# Write number of bytes of appended data as UInt32
f.write(pack('I', datasize))
for k in range(self.zs, self.zf, self.dz):
for j in range(self.ys, self.yf, self.dy):
for i in range(self.xs, self.xf, self.dx):
f.write(pack('I', G.solid[i, j, k]))
f.write('\n</AppendedData>\n</VTKFile>'.encode('utf-8'))
# Write gprMax specific information which relates material name to material numeric identifier
f.write('\n\n<gprMax>\n'.encode('utf-8'))
for material in G.materials:
f.write('<Material name="{}">{}</Material>\n'.format(material.ID, material.numID).encode('utf-8'))
f.write('</gprMax>\n'.encode('utf-8'))
elif self.type == 'f':
self.filename += '.vtp'
vtk_numpoints = (self.vtk_nx + 1) * (self.vtk_ny + 1) * (self.vtk_nz + 1)
vtk_numpoint_components = 3
vtk_numlines = 2 * self.vtk_nx * self.vtk_ny + 2 * self.vtk_ny * self.vtk_nz + 2 * self.vtk_nx * self.vtk_nz + 3 * self.vtk_nx * self.vtk_ny * self.vtk_nz + self.vtk_nx + self.vtk_ny + self.vtk_nz
vtk_numline_components = 2;
vtk_connectivity_offset = (vtk_numpoints * vtk_numpoint_components * np.dtype(np.float32).itemsize) + np.dtype(np.uint32).itemsize
vtk_offsets_offset = vtk_connectivity_offset + (vtk_numlines * vtk_numline_components * np.dtype(np.uint32).itemsize) + np.dtype(np.uint32).itemsize
vtk_id_offset = vtk_offsets_offset + (vtk_numlines * np.dtype(np.uint32).itemsize) + np.dtype(np.uint32).itemsize
vtk_offsets_size = vtk_numlines
with open(self.filename, 'wb') as f:
f.write('<?xml version="1.0"?>\n'.encode('utf-8'))
f.write('<VTKFile type="PolyData" version="1.0" byte_order="{}">\n'.format(GeometryView.byteorder).encode('utf-8'))
f.write('<PolyData>\n<Piece NumberOfPoints="{}" NumberOfVerts="0" NumberOfLines="{}" NumberOfStrips="0" NumberOfPolys="0">\n'.format(vtk_numpoints, vtk_numlines).encode('utf-8'))
f.write('<Points>\n<DataArray type="Float32" NumberOfComponents="3" format="appended" offset="0" />\n</Points>\n'.encode('utf-8'))
f.write('<Lines>\n<DataArray type="UInt32" Name="connectivity" format="appended" offset="{}" />\n'.format(vtk_connectivity_offset).encode('utf-8'))
f.write('<DataArray type="UInt32" Name="offsets" format="appended" offset="{}" />\n</Lines>\n'.format(vtk_offsets_offset).encode('utf-8'))
f.write('<CellData Scalars="Material">\n<DataArray type="UInt32" Name="Material" format="appended" offset="{}" />\n</CellData>\n'.format(vtk_id_offset).encode('utf-8'))
f.write('</Piece>\n</PolyData>\n<AppendedData encoding="raw">\n_'.encode('utf-8'))
# Write points
datasize = np.dtype(np.float32).itemsize * vtk_numpoints * vtk_numpoint_components
f.write(pack('I', datasize))
for i in range(self.xs, self.xf + 1):
for j in range(self.ys, self.yf + 1):
for k in range(self.zs, self.zf + 1):
f.write(pack('fff', i * G.dx, j * G.dy, k * G.dz))
# Write cell type (line) connectivity for x components
datasize = np.dtype(np.uint32).itemsize * vtk_numlines * vtk_numline_components
f.write(pack('I', datasize))
vtk_x2 = (self.vtk_ny + 1) * (self.vtk_nz + 1)
for vtk_x1 in range(self.vtk_nx * (self.vtk_ny + 1) * (self.vtk_nz + 1)):
f.write(pack('II', vtk_x1, vtk_x2))
# print('x {} {}'.format(vtk_x1, vtk_x2))
vtk_x2 += 1
# Write cell type (line) connectivity for y components
vtk_ycnt1 = 1
vtk_ycnt2 = 0
for vtk_y1 in range((self.vtk_nx + 1) * (self.vtk_ny + 1) * (self.vtk_nz + 1)):
if vtk_y1 >= (vtk_ycnt1 * (self.vtk_ny + 1) * (self.vtk_nz + 1)) - (self.vtk_nz + 1) and vtk_y1 < vtk_ycnt1 * (self.vtk_ny + 1) * (self.vtk_nz + 1):
vtk_ycnt2 += 1
else:
vtk_y2 = vtk_y1 + self.vtk_nz + 1
f.write(pack('II', vtk_y1, vtk_y2))
# print('y {} {}'.format(vtk_y1, vtk_y2))
if vtk_ycnt2 == self.vtk_nz + 1:
vtk_ycnt1 += 1
vtk_ycnt2 = 0
# Write cell type (line) connectivity for z components
vtk_zcnt = self.vtk_nz
for vtk_z1 in range((self.vtk_nx + 1) * (self.vtk_ny + 1) * self.vtk_nz + (self.vtk_nx + 1) * (self.vtk_ny + 1)):
if vtk_z1 != vtk_zcnt:
vtk_z2 = vtk_z1 + 1
f.write(pack('II', vtk_z1, vtk_z2))
# print('z {} {}'.format(vtk_z1, vtk_z2))
else:
vtk_zcnt += self.vtk_nz + 1
# Write cell type (line) offsets
vtk_cell_pts = 2
datasize = np.dtype(np.uint32).itemsize * vtk_offsets_size
f.write(pack('I', datasize))
for vtk_offsets in range(vtk_cell_pts, (vtk_numline_components * vtk_numlines) + vtk_cell_pts, vtk_cell_pts):
f.write(pack('I', vtk_offsets))
# Write Ex, Ey, Ez values from ID array
datasize = np.dtype(np.uint32).itemsize * vtk_numlines
f.write(pack('I', datasize))
for i in range(self.xs, self.xf):
for j in range(self.ys, self.yf + 1):
for k in range(self.zs, self.zf + 1):
f.write(pack('I', G.ID[0, i, j, k]))
for i in range(self.xs, self.xf + 1):
for j in range(self.ys, self.yf):
for k in range(self.zs, self.zf + 1):
f.write(pack('I', G.ID[1, i, j, k]))
for i in range(self.xs, self.xf + 1):
for j in range(self.ys, self.yf + 1):
for k in range(self.zs, self.zf):
f.write(pack('I', G.ID[2, i, j, k]))
f.write('\n</AppendedData>\n</VTKFile>'.encode('utf-8'))
# Write gprMax specific information which relates material name to material numeric identifier
f.write('\n\n<gprMax>\n'.encode('utf-8'))
for material in G.materials:
f.write('<Material name="{}">{}</Material>\n'.format(material.ID, material.numID).encode('utf-8'))
f.write('</gprMax>\n'.encode('utf-8'))

396
gprMax/gprMax.py 普通文件
查看文件

@@ -0,0 +1,396 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
"""gprMax.gprMax: provides entry point main()."""
# Set the version number here
__version__ = '3.0.0b1'
versionname = ' (Bowmore)'
import sys, os, datetime, itertools, argparse
if sys.platform != 'win32':
import resource
from time import perf_counter
from copy import deepcopy
from enum import Enum
import numpy as np
from .constants import e0
from .exceptions import CmdInputError
from .fields_output import prepare_output_file, write_output
from .fields_update import *
from .grid import FDTDGrid
from .input_cmds_geometry import process_geometrycmds
from .input_cmds_file import python_code_blocks, write_python_processed, check_cmd_names
from .input_cmds_multiuse import process_multicmds
from .input_cmds_singleuse import process_singlecmds
from .materials import Material
from .pml_call_updates import update_pml_electric, update_pml_magnetic
from .pml import build_pml, calculate_initial_pml_params
from .utilities import update_progress, logo, human_size
from .yee_cell_build import build_ex_component, build_ey_component, build_ez_component, build_hx_component, build_hy_component, build_hz_component
def main():
"""This is the main function for gprMax."""
# Print gprMax logo, version, and licencing/copyright information
logo(__version__ + versionname)
# Parse command line arguments
parser = argparse.ArgumentParser(prog='gprMax', description='Electromagnetic modelling software based on the Finite-Difference Time-Domain (FDTD) method')
parser.add_argument('inputfile', help='path to and name of inputfile')
parser.add_argument('--geometry-only', action='store_true', default=False, help='only build model and produce geometry files')
parser.add_argument('-n', default=1, type=int, help='number of times to run the input file')
parser.add_argument('-mpi', action='store_true', default=False, help='switch on MPI')
parser.add_argument('--commands-python', action='store_true', default=False, help='write an input file after any Python code blocks in the original input file have been processed')
args = parser.parse_args()
numbermodelruns = args.n
inputdirectory = os.path.dirname(os.path.abspath(args.inputfile)) + os.sep
inputfile = inputdirectory + os.path.basename(args.inputfile)
print('Model input file: {}\n'.format(inputfile))
# Mixed mode MPI/OpenMP - task farm for model runs with MPI; each model parallelised with OpenMP
if args.mpi:
from mpi4py import MPI
# Define MPI message tags
tags = Enum('tags', {'READY': 0, 'DONE': 1, 'EXIT': 2, 'START': 3})
# Initializations and preliminaries
comm = MPI.COMM_WORLD # get MPI communicator object
size = comm.size # total number of processes
rank = comm.rank # rank of this process
status = MPI.Status() # get MPI status object
name = MPI.Get_processor_name() # get name of processor/host
if rank == 0:
# Master process
modelrun = 1
numworkers = size - 1
closedworkers = 0
print('Master: PID {} on {} using {} workers.'.format(os.getpid(), name, numworkers))
while closedworkers < numworkers:
data = comm.recv(source=MPI.ANY_SOURCE, tag=MPI.ANY_TAG, status=status)
source = status.Get_source()
tag = status.Get_tag()
if tag == tags.READY.value:
# Worker is ready, so send it a task
if modelrun < numbermodelruns + 1:
comm.send(modelrun, dest=source, tag=tags.START.value)
print('Master: sending model {} to worker {}.'.format(modelrun, source))
modelrun += 1
else:
comm.send(None, dest=source, tag=tags.EXIT.value)
elif tag == tags.DONE.value:
print('Worker {}: completed.'.format(source))
elif tag == tags.EXIT.value:
print('Worker {}: exited.'.format(source))
closedworkers += 1
else:
# Worker process
print('Worker {}: PID {} on {} requesting {} OpenMP threads.'.format(rank, os.getpid(), name, os.environ.get('OMP_NUM_THREADS')))
while True:
comm.send(None, dest=0, tag=tags.READY.value)
# Receive a model number to run from the master
modelrun = comm.recv(source=0, tag=MPI.ANY_TAG, status=status)
tag = status.Get_tag()
if tag == tags.START.value:
# Run a model
run_model(args, modelrun, numbermodelruns, inputfile, inputdirectory)
comm.send(None, dest=0, tag=tags.DONE.value)
elif tag == tags.EXIT.value:
break
comm.send(None, dest=0, tag=tags.EXIT.value)
# Standard behaviour - models run serially; each model parallelised with OpenMP
else:
tsimstart = perf_counter()
for modelrun in range(1, numbermodelruns + 1):
run_model(args, modelrun, numbermodelruns, inputfile, inputdirectory)
tsimend = perf_counter()
print('\nTotal simulation time [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tsimend - tsimstart))))
print('\nSimulation completed.\n{}\n'.format(65*'*'))
def run_model(args, modelrun, numbermodelruns, inputfile, inputdirectory):
"""Runs a model - processes the input file; builds the Yee cells; calculates update coefficients; runs main FDTD loop.
Args:
args (dict): Namespace with command line arguments
modelrun (int): Current model run number.
numbermodelruns (int): Total number of model runs.
inputfile (str): Name of the input file to open.
inputdirectory (str): Path to the directory containing the inputfile.
"""
# Process any user input Python commands
processedlines = python_code_blocks(inputfile, modelrun, numbermodelruns, inputdirectory)
# Write a file containing the input commands after Python blocks have been processed
if args.commands_python:
write_python_processed(inputfile, modelrun, numbermodelruns, processedlines)
# Check validity of command names & that essential commands are present
singlecmds, multicmds, geometry = check_cmd_names(processedlines)
# Initialise an instance of the FDTDGrid class
G = FDTDGrid()
G.inputdirectory = inputdirectory
# Process parameters for commands that can only occur once in the model
process_singlecmds(singlecmds, multicmds, G)
# Process parameters for commands that can occur multiple times in the model
process_multicmds(multicmds, G)
# Initialise an array for volumetric material IDs (solid), boolean arrays for specifying materials not to be averaged (rigid),
# an array for cell edge IDs (ID), and arrays for the field components.
G.initialise_std_arrays()
# Process the geometry commands in the order they were given
tinputprocstart = perf_counter()
process_geometrycmds(geometry, G)
tinputprocend = perf_counter()
print('\nInput file processed in [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tinputprocend - tinputprocstart))))
# Build the PML and calculate initial coefficients
build_pml(G)
calculate_initial_pml_params(G)
# Build the model, i.e. set the material properties (ID) for every edge of every Yee cell
tbuildstart = perf_counter()
build_ex_component(G.solid, G.rigidE, G.ID, G)
build_ey_component(G.solid, G.rigidE, G.ID, G)
build_ez_component(G.solid, G.rigidE, G.ID, G)
build_hx_component(G.solid, G.rigidH, G.ID, G)
build_hy_component(G.solid, G.rigidH, G.ID, G)
build_hz_component(G.solid, G.rigidH, G.ID, G)
tbuildend = perf_counter()
print('\nModel built in [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tbuildend - tbuildstart))))
# Process any voltage sources that have resistance to create a new material at the source location
# that adds the voltage source conductivity to the underlying parameters
if G.voltagesources:
for source in G.voltagesources:
if source.resistance != 0:
if source.polarisation == 'x':
requirednumID = G.ID[0, source.positionx, source.positiony, source.positionz]
material = next(x for x in G.materials if x.numID == requirednumID)
newmaterial = deepcopy(material)
newmaterial.ID = material.ID + '|VoltageSource_' + str(source.resistance)
newmaterial.numID = len(G.materials)
newmaterial.se += G.dx / (source.resistance * G.dy * G.dz)
newmaterial.average = False
G.ID[0, source.positionx, source.positiony, source.positionz] = newmaterial.numID
elif source.polarisation == 'y':
requirednumID = G.ID[1, source.positionx, source.positiony, source.positionz]
material = next(x for x in G.materials if x.numID == requirednumID)
newmaterial = deepcopy(material)
newmaterial.ID = material.ID + '|VoltageSource_' + str(source.resistance)
newmaterial.numID = len(G.materials)
newmaterial.se += G.dy / (source.resistance * G.dx * G.dz)
newmaterial.average = False
G.ID[1, source.positionx, source.positiony, source.positionz] = newmaterial.numID
elif source.polarisation == 'z':
requirednumID = G.ID[2, source.positionx, source.positiony, source.positionz]
material = next(x for x in G.materials if x.numID == requirednumID)
newmaterial = deepcopy(material)
newmaterial.ID = material.ID + '|VoltageSource_' + str(source.resistance)
newmaterial.numID = len(G.materials)
newmaterial.se += G.dz / (source.resistance * G.dx * G.dy)
newmaterial.average = False
G.ID[2, source.positionx, source.positiony, source.positionz] = newmaterial.numID
G.materials.append(newmaterial)
# Initialise arrays for storing temporary values if there are any dispersive materials
if Material.maxpoles != 0:
G.initialise_dispersive_arrays(len(G.materials))
# Initialise arrays of update coefficients to pass to update functions
G.initialise_std_updatecoeff_arrays(len(G.materials))
# Calculate update coefficients, store in arrays, and list materials in model
if G.messages:
print('\nMaterials:\n')
print('ID\tName\t\tProperties')
print('{}'.format('-'*50))
for x, material in enumerate(G.materials):
material.calculate_update_coeffsE(G)
material.calculate_update_coeffsH(G)
G.updatecoeffsE[x, :] = material.CA, material.CBx, material.CBy, material.CBz, material.srce
G.updatecoeffsH[x, :] = material.DA, material.DBx, material.DBy, material.DBz, material.srcm
if Material.maxpoles != 0:
z = 0
for y in range(Material.maxpoles):
G.updatecoeffsdispersive[x, z:z+3] = e0 * material.eqt2[y], material.eqt[y], material.zt[y]
z += 3
if G.messages:
if material.deltaer and material.tau:
tmp = 'delta_epsr={}, tau={} secs; '.format(','.join('%4.2f' % deltaer for deltaer in material.deltaer), ','.join('%4.3e' % tau for tau in material.tau))
else:
tmp = ''
if material.average:
dielectricsmoothing = 'dielectric smoothing permitted.'
else:
dielectricsmoothing = 'dielectric smoothing not permitted.'
print('{:3}\t{:12}\tepsr={:4.2f}, sig={:.3e} S/m; mur={:4.2f}, sig*={:.3e} S/m; '.format(material.numID, material.ID, material.er, material.se, material.mr, material.sm) + tmp + dielectricsmoothing)
# Write files for any geometry views
if G.geometryviews:
tgeostart = perf_counter()
for geometryview in G.geometryviews:
geometryview.write_file(modelrun, numbermodelruns, G)
tgeoend = perf_counter()
print('\nGeometry file(s) written in [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tgeoend - tgeostart))))
# Run simulation if not doing only geometry
if not args.geometry_only:
# Prepare any snapshot files
if G.snapshots:
for snapshot in G.snapshots:
snapshot.prepare_file(modelrun, numbermodelruns, G)
# Prepare output file
inputfileparts = inputfile.split('.')
if numbermodelruns == 1:
outputfile = inputfileparts[0] + '.out'
else:
outputfile = inputfileparts[0] + str(modelrun) + '.out'
sys.stdout.write('\nOutput to file: {}\n'.format(outputfile))
sys.stdout.flush()
f = prepare_output_file(outputfile, G)
# Adjust position of sources and receivers if required
if G.txstepx > 0 or G.txstepy > 0 or G.txstepz > 0:
for source in itertools.chain(G.hertziandipoles, G.magneticdipoles, G.voltagesources):
source.positionx += (modelrun - 1) * G.txstepx
source.positiony += (modelrun - 1) * G.txstepy
source.positionz += (modelrun - 1) * G.txstepz
if G.rxstepx > 0 or G.rxstepy > 0 or G.rxstepz > 0:
for receiver in G.rxs:
receiver.positionx += (modelrun - 1) * G.rxstepx
receiver.positiony += (modelrun - 1) * G.rxstepy
receiver.positionz += (modelrun - 1) * G.rxstepz
##################################
# Main FDTD calculation loop #
##################################
tsolvestart = perf_counter()
# Absolute time
abstime = 0
for timestep in range(G.iterations):
if timestep == 0:
tstepstart = perf_counter()
# Write field outputs to file
write_output(f, timestep, G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz, G)
# Write any snapshots to file
if G.snapshots:
for snapshot in G.snapshots:
if snapshot.time == timestep + 1:
snapshot.write_snapshot(G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz, G)
# Update electric field components
# 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.
if Material.maxpoles == 1:
update_ex_dispersive_1pole_A(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsE, G.updatecoeffsdispersive, G.ID, G.Tx, G.Ex, G.Hy, G.Hz)
update_ey_dispersive_1pole_A(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsE, G.updatecoeffsdispersive, G.ID, G.Ty, G.Ey, G.Hx, G.Hz)
update_ez_dispersive_1pole_A(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsE, G.updatecoeffsdispersive, G.ID, G.Tz, G.Ez, G.Hx, G.Hy)
elif Material.maxpoles > 1:
update_ex_dispersive_multipole_A(G.nx, G.ny, G.nz, G.nthreads, Material.maxpoles, G.updatecoeffsE, G.updatecoeffsdispersive, G.ID, G.Tx, G.Ex, G.Hy, G.Hz)
update_ey_dispersive_multipole_A(G.nx, G.ny, G.nz, G.nthreads, Material.maxpoles, G.updatecoeffsE, G.updatecoeffsdispersive, G.ID, G.Ty, G.Ey, G.Hx, G.Hz)
update_ez_dispersive_multipole_A(G.nx, G.ny, G.nz, G.nthreads, Material.maxpoles, G.updatecoeffsE, G.updatecoeffsdispersive, G.ID, G.Tz, G.Ez, G.Hx, G.Hy)
# Otherwise all materials are non-dispersive so do standard update
else:
update_ex(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsE, G.ID, G.Ex, G.Hy, G.Hz)
update_ey(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsE, G.ID, G.Ey, G.Hx, G.Hz)
update_ez(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsE, G.ID, G.Ez, G.Hx, G.Hy)
# Update electric field components with the PML correction
update_pml_electric(G)
# Update electric field components with electric sources
if G.voltagesources:
for v in G.voltagesources:
v.update_fields(abstime, timestep, G.updatecoeffsE, G.ID, G.Ex, G.Ey, G.Ez, G)
if G.hertziandipoles: # Update any Hertzian dipole sources last
for h in G.hertziandipoles:
h.update_fields(abstime, timestep, G.updatecoeffsE, G.ID, G.Ex, G.Ey, G.Ez, G)
# 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 == 1:
update_ex_dispersive_1pole_B(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsdispersive, G.ID, G.Tx, G.Ex)
update_ey_dispersive_1pole_B(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsdispersive, G.ID, G.Ty, G.Ey)
update_ez_dispersive_1pole_B(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsdispersive, G.ID, G.Tz, G.Ez)
elif Material.maxpoles > 1:
update_ex_dispersive_multipole_B(G.nx, G.ny, G.nz, G.nthreads, Material.maxpoles, G.updatecoeffsdispersive, G.ID, G.Tx, G.Ex)
update_ey_dispersive_multipole_B(G.nx, G.ny, G.nz, G.nthreads, Material.maxpoles, G.updatecoeffsdispersive, G.ID, G.Ty, G.Ey)
update_ez_dispersive_multipole_B(G.nx, G.ny, G.nz, G.nthreads, Material.maxpoles, G.updatecoeffsdispersive, G.ID, G.Tz, G.Ez)
# Increment absolute time value
abstime += 0.5 * G.dt
# Update magnetic field components
update_hx(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsH, G.ID, G.Hx, G.Ey, G.Ez)
update_hy(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsH, G.ID, G.Hy, G.Ex, G.Ez)
update_hz(G.nx, G.ny, G.nz, G.nthreads, G.updatecoeffsH, G.ID, G.Hz, G.Ex, G.Ey)
# Update magnetic field components with the PML correction
update_pml_magnetic(G)
# Update magnetic field components with magnetic sources
if G.magneticdipoles:
for m in G.magneticdipoles:
m.update_fields(abstime, timestep, G.updatecoeffsH, G.ID, G.Hx, G.Hy, G.Hz, G)
# Increment absolute time value
abstime += 0.5 * G.dt
# Calculate time for two iterations, used to estimate overall runtime
if timestep == 1:
tstepend = perf_counter()
runtime = datetime.timedelta(seconds=int((tstepend - tstepstart) / 2 * G.iterations))
sys.stdout.write('Estimated runtime [HH:MM:SS]: {}\n'.format(runtime))
sys.stdout.write('Solving for model run {} of {}...\n'.format(modelrun, numbermodelruns))
sys.stdout.flush()
elif timestep > 1:
update_progress((timestep + 1) / G.iterations)
# Close output file
f.close()
tsolveend = perf_counter()
print('\n\nSolving took [HH:MM:SS]:'.format(datetime.timedelta(seconds=int(tsolveend - tsolvestart))))
if sys.platform != 'win32':
print('Peak memory (approx) required: {}'.format(human_size(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss, False)))

102
gprMax/grid.py 普通文件
查看文件

@@ -0,0 +1,102 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
from .constants import floattype, complextype
from .materials import Material
class FDTDGrid():
"""Holds attributes associated with the entire grid. A convenient way for accessing regularly used parameters."""
def __init__(self):
self.inputdirectory = ''
self.title = ''
self.messages = True
self.nx = 0
self.ny = 0
self.nz = 0
self.dx = 0
self.dy = 0
self.dz = 0
self.dt = 0
self.iterations = 0
self.timewindow = 0
self.nthreads = 0
self.cfs = []
self.pmlthickness = (10, 10, 10, 10, 10, 10)
self.pmls = []
self.materials = []
self.mixingmodels = []
self.averagevolumeobjects = True
self.fractalvolumes = []
self.geometryviews = []
self.waveforms = []
self.voltagesources = []
self.hertziandipoles = []
self.magneticdipoles = []
self.txs = [] # Only used for converting old output files to HDF5 format
self.txstepx = 0
self.txstepy = 0
self.txstepz = 0
self.rxstepx = 0
self.rxstepy = 0
self.rxstepz = 0
self.rxs = []
self.snapshots = []
def initialise_std_arrays(self):
"""Initialise an array for volumetric material IDs (solid); boolean arrays for specifying whether materials can have dielectric smoothing (rigid);
an array for cell edge IDs (ID); and arrays for the electric and magnetic field components. Solid and ID arrays are initialised to free_space (one); rigid arrays
to allow dielectric smoothing (zero).
"""
self.solid = np.ones((self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.uint32)
self.rigidE = np.zeros((12, self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.int8)
self.rigidH = np.zeros((6, self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.int8)
self.ID = np.ones((6, self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.uint32)
self.Ex = np.zeros((self.nx, self.ny + 1, self.nz + 1), dtype=floattype)
self.Ey = np.zeros((self.nx + 1, self.ny, self.nz + 1), dtype=floattype)
self.Ez = np.zeros((self.nx + 1, self.ny + 1, self.nz), dtype=floattype)
self.Hx = np.zeros((self.nx + 1, self.ny, self.nz), dtype=floattype)
self.Hy = np.zeros((self.nx, self.ny + 1, self.nz), dtype=floattype)
self.Hz = np.zeros((self.nx, self.ny, self.nz + 1), dtype=floattype)
def initialise_std_updatecoeff_arrays(self, nummaterials):
"""Initialise arrays for storing update coefficients.
Args:
nummaterials (int): Number of materials present in the model.
"""
self.updatecoeffsE = np.zeros((nummaterials, 5), dtype=floattype)
self.updatecoeffsH = np.zeros((nummaterials, 5), dtype=floattype)
def initialise_dispersive_arrays(self, nummaterials):
"""Initialise arrays for storing coefficients when there are dispersive materials present.
Args:
nummaterials (int): Number of materials present in the model.
"""
self.Tx = np.zeros((Material.maxpoles, self.nx, self.ny + 1, self.nz + 1), dtype=complextype)
self.Ty = np.zeros((Material.maxpoles, self.nx + 1, self.ny, self.nz + 1), dtype=complextype)
self.Tz = np.zeros((Material.maxpoles, self.nx + 1, self.ny + 1, self.nz), dtype=complextype)
self.updatecoeffsdispersive = np.zeros((nummaterials, 3 * Material.maxpoles), dtype=complextype)

174
gprMax/input_cmds_file.py 普通文件
查看文件

@@ -0,0 +1,174 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import sys, os
from .constants import c, e0, m0, z0
from .exceptions import CmdInputError
from .utilities import ListStream
def python_code_blocks(inputfile, modelrun, numbermodelruns, inputdirectory):
"""Looks for and processes any Python code found in the input file. It will ignore any lines that are comments, i.e. begin with a double hash (##), and any blank lines. It will also ignore any lines that do not begin with a hash (#) after it has processed Python commands.
Args:
inputfile (str): Name of the input file to open.
modelrun (int): Current model run number.
numbermodelruns (int): Total number of model runs.
inputdirectory (str): Directory containing input file.
Returns:
processedlines (list): Input commands after Python processing.
"""
with open(inputfile, 'r') as f:
# Strip out any newline characters and comments that must begin with double hashes
inputlines = [line.rstrip() for line in f if(not line.startswith('##') and line.rstrip('\n'))]
# List to hold final processed commands
processedlines = []
# Separate namespace for users Python code blocks to use; pre-populated some standard constants and the
# current model run number and total number of model runs
usernamespace = {'c': c, 'e0': e0, 'm0': m0, 'z0': z0, 'current_model_run': modelrun, 'number_model_runs': numbermodelruns, 'inputdirectory': inputdirectory}
print('Constants/variables available for Python scripting: {}\n'.format(usernamespace))
x = 0
while(x < len(inputlines)):
if(inputlines[x].startswith('#python:')):
# String to hold Python code to be executed
pythoncode = ''
x += 1
while not inputlines[x].startswith('#end_python:'):
# Add all code in current code block to string
pythoncode += inputlines[x] + '\n'
x += 1
if x == len(inputlines):
raise CmdInputError('Cannot find the end of the Python code block, i.e. missing #end_python: command.')
# Compile code for faster execution
pythoncompiledcode = compile(pythoncode, '<string>', 'exec')
# Redirect stdio to a ListStream
sys.stdout = codeout = ListStream()
# Execute code block & make available only usernamespace
exec(pythoncompiledcode, usernamespace)
# Now strip out any lines that don't begin with a hash command
codeproc = [line + ('\n') for line in codeout.data if(line.startswith('#'))]
# Add processed Python code to list
processedlines.extend(codeproc)
x += 1
elif(inputlines[x].startswith('#')):
# Add gprMax command to list
inputlines[x] += ('\n')
processedlines.append(inputlines[x])
x += 1
else:
x += 1
sys.stdout = sys.__stdout__ # Reset stdio
return processedlines
def write_python_processed(inputfile, modelrun, numbermodelruns, processedlines):
"""Writes input commands to file after Python processing.
Args:
inputfile (str): Name of the input file to open.
modelrun (int): Current model run number.
numbermodelruns (int): Total number of model runs.
processedlines (list): Input commands after Python processing.
"""
if numbermodelruns == 1:
processedfile = os.path.splitext(inputfile)[0] + '_proc.in'
else:
processedfile = os.path.splitext(inputfile)[0] + str(modelrun) + '_proc.in'
with open(processedfile, 'w') as f:
for item in processedlines:
f.write('{}'.format(item))
print('Written input commands after Python processing to file: {}\n'.format(processedfile))
def check_cmd_names(processedlines):
"""Checks the validity of commands, i.e. are they gprMax commands, and that all essential commands are present.
Args:
processedlines (list): Input commands after Python processing.
Returns:
singlecmds (dict): Commands that can only occur once in the model.
multiplecmds (dict): Commands that can have multiple instances in the model.
geometry (list): Geometry commands in the model.
"""
# Dictionaries of available commands
# Essential commands neccessary to run a gprMax model
essentialcmds = ['#domain', '#dx_dy_dz', '#time_window']
# Commands that there should only be one instance of in a model
singlecmds = dict.fromkeys(['#domain', '#dx_dy_dz', '#time_window', '#title', '#messages', '#num_threads', '#time_step_stability_factor', '#time_step_limit_type', '#pml_cells', '#excitation_file', '#src_steps', '#rx_steps'], 'None')
# Commands that there can be multiple instances of in a model - these will be lists within the dictionary
multiplecmds = {key: [] for key in ['#geometry_view', '#material', '#soil_peplinski', '#add_dispersion_debye', '#add_dispersion_lorenz', '#add_dispersion_drude', '#waveform', '#voltage_source', '#hertzian_dipole', '#magnetic_dipole', '#rx', '#rx_box', '#snapshot', '#pml_cfs']}
# Geometry object building commands that there can be multiple instances of in a model - these will be lists within the dictionary
geometrycmds = ['#edge', '#plate', '#triangle', '#box', '#sphere', '#cylinder', '#cylindrical_sector', '#fractal_box', '#add_surface_roughness', '#add_surface_water', '#add_grass']
# List to store all geometry object commands in order from input file
geometry = []
# Check if command names are valid, if essential commands are present, and add command parameters to appropriate dictionary values or lists
countessentialcmds = 0
lindex = 0
while(lindex < len(processedlines)):
cmd = processedlines[lindex].split(':')
cmdname = cmd[0].lower()
# Check if command name is valid
if cmdname not in essentialcmds and cmdname not in singlecmds and cmdname not in multiplecmds and cmdname not in geometrycmds:
raise CmdInputError('Your input file contains the invalid command: ' + cmdname)
# Count essential commands
if cmdname in essentialcmds:
countessentialcmds += 1
# Assign command parameters as values to dictionary keys
if cmdname in singlecmds:
if singlecmds[cmdname] == 'None':
singlecmds[cmdname] = cmd[1].strip(' \t\n')
else:
raise CmdInputError('You can only have one ' + cmdname + ' commmand in your model')
elif cmdname in multiplecmds:
multiplecmds[cmdname].append(cmd[1].strip(' \t\n'))
elif cmdname in geometrycmds:
geometry.append(processedlines[lindex].strip(' \t\n'))
lindex += 1
if (countessentialcmds < len(essentialcmds)):
raise CmdInputError('Your input file is missing essential gprMax commands required to run a model. Essential commands are: ' + ', '.join(essentialcmds))
return singlecmds, multiplecmds, geometry

1438
gprMax/input_cmds_geometry.py 普通文件

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

查看文件

@@ -0,0 +1,620 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
from .exceptions import CmdInputError
from .geometry_views import GeometryView
from .materials import Material, PeplinskiSoil
from .pml import CFS
from .receivers import Rx
from .snapshots import Snapshot
from .sources import VoltageSource, HertzianDipole, MagneticDipole
from .utilities import rvalue
from .waveforms import Waveform
def process_multicmds(multicmds, G):
"""Checks the validity of command parameters and creates instances of classes of parameters.
Args:
multicmds (dict): Commands that can have multiple instances in the model.
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Waveform definitions
cmdname = '#waveform'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 4:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly four parameters')
if tmp[0].lower() not in Waveform.waveformtypes:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' must have one of the following types {}'.format(','.join(Waveform.waveformtypes)))
if float(tmp[2]) <= 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires an excitation frequency value of greater than zero')
if any(x.ID == tmp[3] for x in G.waveforms):
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' with ID {} already exists'.format(tmp[2]))
w = Waveform()
w.ID = tmp[3]
w.type = tmp[0].lower()
w.amp = float(tmp[1])
w.freq = float(tmp[2])
if G.messages:
print('Waveform {} of type {} with amplitude {}, frequency {:.3e} Hz created.'.format(w.ID, w.type, w.amp, w.freq))
G.waveforms.append(w)
# Voltage source
cmdname = '#voltage_source'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 6:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters')
# Check polarity & position parameters
if tmp[0].lower() not in ('x', 'y', 'z'):
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z')
positionx = rvalue(float(tmp[1])/G.dx)
positiony = rvalue(float(tmp[2])/G.dy)
positionz = rvalue(float(tmp[3])/G.dz)
resistance = float(tmp[4])
if positionx < 0 or positionx > G.nx:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' x-coordinate is not within the model domain')
if positiony < 0 or positiony > G.ny:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' y-coordinate is not within the model domain')
if positionz < 0 or positionz > G.nz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' z-coordinate is not within the model domain')
if resistance < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a source resistance of zero or greater')
# Check if there is a waveformID in the waveforms list
if not any(x.ID == tmp[5] for x in G.waveforms):
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' there is no waveform with the identifier {}'.format(tmp[5]))
v = VoltageSource()
v.polarisation= tmp[0]
v.positionx = positionx
v.positiony = positiony
v.positionz = positionz
v.resistance = resistance
if len(tmp) > 6:
# Check source start & source remove time parameters
start = float(tmp[6])
stop = float(tmp[7])
if start < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' delay of the initiation of the source should not be less than zero')
if stop < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time to remove the source should not be less than zero')
if stop - start <= 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' duration of the source should not be zero or less')
v.start = start
if stop > G.timewindow:
v.stop = G.timewindow
v.waveformID = tmp[8]
tmp = ' start time {:.3e} secs, finish time {:.3e} secs '.format(v.start, v.stop)
else:
v.start = 0
v.stop = G.timewindow
v.waveformID = tmp[5]
tmp = ' '
if G.messages:
print('Voltage source with polarity {} at {:.3f}m, {:.3f}m, {:.3f}m, resistance {:.1f} Ohms,'.format(v.polarisation, v.positionx * G.dx, v.positiony * G.dy, v.positionz * G.dz, v.resistance) + tmp + 'using waveform {} created.'.format(v.waveformID))
G.voltagesources.append(v)
# Hertzian dipole
cmdname = '#hertzian_dipole'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
# Check polarity & position parameters
if tmp[0].lower() not in ('x', 'y', 'z'):
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z')
positionx = rvalue(float(tmp[1])/G.dx)
positiony = rvalue(float(tmp[2])/G.dy)
positionz = rvalue(float(tmp[3])/G.dz)
if positionx < 0 or positionx > G.nx:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' x-coordinate is not within the model domain')
if positiony < 0 or positiony > G.ny:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' y-coordinate is not within the model domain')
if positionz < 0 or positionz > G.nz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' z-coordinate is not within the model domain')
# Check if there is a waveformID in the waveforms list
if not any(x.ID == tmp[4] for x in G.waveforms):
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' there is no waveform with the identifier {}'.format(tmp[4]))
h = HertzianDipole()
h.polarisation = tmp[0]
h.positionx = positionx
h.positiony = positiony
h.positionz = positionz
if len(tmp) > 6:
# Check source start & source remove time parameters
start = float(tmp[6])
stop = float(tmp[7])
if start < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' delay of the initiation of the source should not be less than zero')
if stop < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time to remove the source should not be less than zero')
if stop - start <= 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' duration of the source should not be zero or less')
h.start = start
if stop > G.timewindow:
h.stop = G.timewindow
h.waveformID = tmp[7]
tmp = ' start time {:.3e} secs, finish time {:.3e} secs '.format(h.start, h.stop)
else:
h.start = 0
h.stop = G.timewindow
h.waveformID = tmp[4]
tmp = ' '
if G.messages:
print('Hertzian dipole with polarity {} at {:.3f}m, {:.3f}m, {:.3f}m,'.format(h.polarisation, h.positionx * G.dx, h.positiony * G.dy, h.positionz * G.dz) + tmp + 'using waveform {} created.'.format(h.waveformID))
G.hertziandipoles.append(h)
# Magnetic dipole
cmdname = '#magnetic_dipole'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
# Check polarity & position parameters
if tmp[0].lower() not in ('x', 'y', 'z'):
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' polarisation must be x, y, or z')
positionx = rvalue(float(tmp[1])/G.dx)
positiony = rvalue(float(tmp[2])/G.dy)
positionz = rvalue(float(tmp[3])/G.dz)
if positionx < 0 or positionx > G.nx:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' x-coordinate is not within the model domain')
if positiony < 0 or positiony > G.ny:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' y-coordinate is not within the model domain')
if positionz < 0 or positionz > G.nz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' z-coordinate is not within the model domain')
# Check if there is a waveformID in the waveforms list
if not any(x.ID == tmp[4] for x in G.waveforms):
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' there is no waveform with the identifier {}'.format(tmp[4]))
m = MagneticDipole()
m.polarisation = tmp[0]
m.positionx = positionx
m.positiony = positiony
m.positionz = positionz
if len(tmp) > 6:
# Check source start & source remove time parameters
start = float(tmp[6])
stop = float(tmp[7])
if start < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' delay of the initiation of the source should not be less than zero')
if stop < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time to remove the source should not be less than zero')
if stop - start <= 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' duration of the source should not be zero or less')
m.start = start
if stop > G.timewindow:
m.stop = G.timewindow
m.waveformID = tmp[7]
tmp = ' start time {:.3e} secs, finish time {:.3e} secs '.format(m.start, m.stop)
else:
m.start = 0
m.stop = G.timewindow
m.waveformID = tmp[4]
tmp = ' '
if G.messages:
print('Magnetic dipole with polarity {} at {:.3f}m, {:.3f}m, {:.3f}m,'.format(m.polarisation, m.positionx * G.dx, m.positiony * G.dy, m.positionz * G.dz) + tmp + 'using waveform {} created.'.format(m.waveformID))
G.magneticdipoles.append(m)
# Receiver
cmdname = '#rx'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 3:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly three parameters')
# Check position parameters
positionx = rvalue(float(tmp[0])/G.dx)
positiony = rvalue(float(tmp[1])/G.dy)
positionz = rvalue(float(tmp[2])/G.dz)
if positionx < 0 or positionx > G.nx:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' x-coordinate is not within the model domain')
if positiony < 0 or positiony > G.ny:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' y-coordinate is not within the model domain')
if positionz < 0 or positionz > G.nz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' z-coordinate is not within the model domain')
r = Rx(positionx=positionx, positiony=positiony, positionz=positionz)
if G.messages:
print('Receiver at {:.3f}m, {:.3f}m, {:.3f}m created.'.format(r.positionx * G.dx, r.positiony * G.dy, r.positionz * G.dz))
G.rxs.append(r)
# Receiver box
cmdname = '#rx_box'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 9:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly nine parameters')
xs = rvalue(float(tmp[0])/G.dx)
xf = rvalue(float(tmp[3])/G.dx)
ys = rvalue(float(tmp[1])/G.dy)
yf = rvalue(float(tmp[4])/G.dy)
zs = rvalue(float(tmp[2])/G.dz)
zf = rvalue(float(tmp[5])/G.dz)
dx = rvalue(float(tmp[6])/G.dx)
dy = rvalue(float(tmp[7])/G.dy)
dz = rvalue(float(tmp[8])/G.dz)
if xs < 0 or xs > G.nx:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower x-coordinate {} is not within the model domain'.format(xs))
if xf < 0 or xf > G.nx:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper x-coordinate {} is not within the model domain'.format(xf))
if ys < 0 or ys > G.ny:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower y-coordinate {} is not within the model domain'.format(ys))
if yf < 0 or yf > G.ny:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper y-coordinate {} is not within the model domain'.format(yf))
if zs < 0 or zs > G.nz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower z-coordinate {} is not within the model domain'.format(zs))
if zf < 0 or zf > G.nz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper z-coordinate {} is not within the model domain'.format(zf))
if xs >= xf or ys >= yf or zs >= zf:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower coordinates should be less than the upper coordinates')
if dx < 0 or dy < 0 or dz < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than zero')
if dx < G.dx or dy < G.dy or dz < G.dz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation')
for x in range(xs, xf, dx):
for y in range(ys, yf, dy):
for z in range(zs, zf, dz):
r = Rx(positionx=x, positiony=y, positionz=z)
G.rxs.append(r)
if G.messages:
print('Receiver box {:.3f}m, {:.3f}m, {:.3f}m, to {:.3f}m, {:.3f}m, {:.3f}m with steps {:.3f}m, {:.3f}m, {:.3f} 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))
# Snapshot
cmdname = '#snapshot'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 11:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')
xs = rvalue(float(tmp[0])/G.dx)
xf = rvalue(float(tmp[3])/G.dx)
ys = rvalue(float(tmp[1])/G.dy)
yf = rvalue(float(tmp[4])/G.dy)
zs = rvalue(float(tmp[2])/G.dz)
zf = rvalue(float(tmp[5])/G.dz)
dx = rvalue(float(tmp[6])/G.dx)
dy = rvalue(float(tmp[7])/G.dy)
dz = rvalue(float(tmp[8])/G.dz)
# If real floating point value given
if '.' in tmp[9] or 'e' in tmp[9]:
if float(tmp[9]) > 0:
time = rvalue((float(tmp[9]) / G.dt)) + 1
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time value must be greater than zero')
# If number of iterations given
else:
time = int(tmp[9])
if dx < 0 or dy < 0 or dz < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than zero')
if dx < G.dx or dy < G.dy or dz < G.dz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation')
if time <= 0 or time > G.iterations:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time value is not valid')
s = Snapshot(xs, ys, zs, xf, yf, zf, dx, dy, dz, time, tmp[10])
if G.messages:
print('Snapshot from {:.3f}m, {:.3f}m, {:.3f}m, to {:.3f}m, {:.3f}m, {:.3f}m, discretisation {:.3f}m, {:.3f}m, {:.3f}m, at {:.3e} 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, dx * G.dy, dx * G.dz, s.time * G.dt, s.filename))
G.snapshots.append(s)
# Materials
# Create built-in materials
m = Material(0, 'pec', G)
m.average = False
G.materials.append(m)
m = Material(1, 'free_space', G)
m.average = True
G.materials.append(m)
cmdname = '#material'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly five parameters')
if float(tmp[0]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for static (DC) permittivity')
if float(tmp[1]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for conductivity')
if float(tmp[2]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for permeability')
if float(tmp[3]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for magnetic conductivity')
if any(x.ID == tmp[4] for x in G.materials):
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' with ID {} already exists'.format(tmp[4]))
# Create a new instance of the Material class material (start index after pec & free_space)
m = Material(len(G.materials), tmp[4], G)
m.er = float(tmp[0])
m.se = float(tmp[1])
m.mr = float(tmp[2])
m.sm = float(tmp[3])
if G.messages:
print('Material {} with epsr={:4.2f}, sig={:.3e} S/m; mur={:4.2f}, sig*={:.3e} S/m created.'.format(m.ID, m.er, m.se, m.mr, m.sm))
# Append the new material object to the materials list
G.materials.append(m)
cmdname = '#add_dispersion_debye'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 4:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least four parameters')
if int(tmp[0]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for number of poles')
poles = int(tmp[0])
materialsrequested = tmp[(2 * poles) + 1:len(tmp)]
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in G.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' material(s) {} do not exist'.format(notfound))
for material in materials:
material.type = 'debye'
material.poles = poles
material.average = False
for pole in range(1, 2 * poles, 2):
if float(tmp[pole]) > 0 and float(tmp[pole + 1]) > G.dt:
material.deltaer.append(float(tmp[pole]))
material.tau.append(float(tmp[pole + 1]))
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires positive values for the permittivity difference and relaxation times, and relaxation times that are greater than the time step for the model.')
if material.poles > Material.maxpoles:
Material.maxpoles = material.poles
if G.messages:
print('Debye-type disperion added to {} with delta_epsr={}, 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)))
cmdname = '#add_dispersion_lorenz'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
if int(tmp[0]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for number of poles')
poles = int(tmp[0])
materialsrequested = tmp[(3 * poles) + 1:len(tmp)]
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in G.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' material(s) {} do not exist'.format(notfound))
for material in materials:
material.type = 'lorenz'
material.poles = poles
material.average = False
for pole in range(1, 3 * poles, 3):
if float(tmp[pole]) > 0 and float(tmp[pole + 1]) > G.dt and float(tmp[pole + 2]) > G.dt:
material.deltaer.append(float(tmp[pole]))
material.tau.append(float(tmp[pole + 1]))
material.alpha.append(float(tmp[pole + 2]))
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires positive values for the permittivity difference and relaxation times, and relaxation times that are greater than the time step for the model.')
if material.poles > Material.maxpoles:
Material.maxpoles = material.poles
if G.messages:
print('Lorenz-type disperion added to {} with delta_epsr={}, tau={} secs, and alpha={} 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'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
if int(tmp[0]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for number of poles')
poles = int(tmp[0])
materialsrequested = tmp[(3 * poles) + 1:len(tmp)]
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in G.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' material(s) {} do not exist'.format(notfound))
for material in materials:
material.type = 'drude'
material.poles = poles
material.average = False
for pole in range(1, 3 * poles, 3):
if float(tmp[pole]) > 0 and float(tmp[pole + 1]) > G.dt and float(tmp[pole + 2]) > G.dt:
material.deltaer.append(float(tmp[pole]))
material.tau.append(float(tmp[pole + 1]))
material.alpha.append(float(tmp[pole + 2]))
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires positive values for the permittivity difference and relaxation times, and relaxation times that are greater than the time step for the model.')
if material.poles > Material.maxpoles:
Material.maxpoles = material.poles
if G.messages:
print('Drude-type disperion added to {} with delta_epsr={}, tau1={} secs, and tau2={} secs 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 = '#soil_peplinski'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 7:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at exactly seven parameters')
if float(tmp[0]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the sand fraction')
if float(tmp[1]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the clay fraction')
if float(tmp[2]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the bulk density')
if float(tmp[3]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the sand particle density')
if float(tmp[4]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the lower limit of the water volumetric fraction')
if float(tmp[5]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires a positive value for the upper limit of the water volumetric fraction')
if any(x.ID == tmp[6] for x in G.mixingmodels):
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' with ID {} already exists'.format(tmp[6]))
# 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 G.messages:
print('Mixing model (Peplinski) used to create {} with sand fraction {:.3f}, clay fraction {:.3f}, bulk density {:.3f} g/cm3, sand particle density {:.3f} g/cm3, and water volumetric fraction {} to {} 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
G.mixingmodels.append(s)
# Geometry views (creates VTK-based geometry files)
cmdname = '#geometry_view'
if multicmds[cmdname] != 'None':
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 11:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')
xs = rvalue(float(tmp[0])/G.dx)
xf = rvalue(float(tmp[3])/G.dx)
ys = rvalue(float(tmp[1])/G.dy)
yf = rvalue(float(tmp[4])/G.dy)
zs = rvalue(float(tmp[2])/G.dz)
zf = rvalue(float(tmp[5])/G.dz)
dx = rvalue(float(tmp[6])/G.dx)
dy = rvalue(float(tmp[7])/G.dy)
dz = rvalue(float(tmp[8])/G.dz)
if xs < 0 or xs > G.nx:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower x-coordinate {} is not within the model domain'.format(xs * G.dx))
if xf < 0 or xf > G.nx:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper x-coordinate {} is not within the model domain'.format(xf * G.dx))
if ys < 0 or ys > G.ny:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower y-coordinate {} is not within the model domain'.format(ys * G.dy))
if yf < 0 or yf > G.ny:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper y-coordinate {} is not within the model domain'.format(yf * G.dy))
if zs < 0 or zs > G.nz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower z-coordinate {} is not within the model domain'.format(zs * G.dz))
if zf < 0 or zf > G.nz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the upper z-coordinate {} is not within the model domain'.format(zf * G.dz))
if xs >= xf or ys >= yf or zs >= zf:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower coordinates should be less than the upper coordinates')
if dx < 0 or dy < 0 or dz < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than zero')
if dx < G.dx or dy < G.dy or dz < G.dz:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the step size should not be less than the spatial discretisation')
if tmp[10].lower() != 'n' and tmp[10].lower() != 'f':
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires type to be either n (normal) or f (fine)')
g = GeometryView(xs, ys, zs, xf, yf, zf, dx, dy, dz, tmp[9], tmp[10].lower())
if G.messages:
print('Geometry view from {:.3f}m, {:.3f}m, {:.3f}m, to {:.3f}m, {:.3f}m, {:.3f}m, discretisation {:.3f}m, {:.3f}m, {:.3f}m, 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, g.filename))
# Append the new GeometryView object to the geometry views list
G.geometryviews.append(g)
# Complex frequency shifted (CFS) PML parameter
cmdname = '#pml_cfs'
if multicmds[cmdname] != 'None':
if len(multicmds[cmdname]) > 2:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' can only be used up to two times, for up to a 2nd order PML')
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 9:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly nine parameters')
if tmp[0] not in CFS.scalingtypes or tmp[3] not in CFS.scalingtypes or tmp[6] not in CFS.scalingtypes:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' must have scaling type {}'.format(','.join(CFS.scalingtypes)))
if float(tmp[1]) < 0 or float(tmp[2]) < 0 or float(tmp[4]) < 0 or float(tmp[5]) < 0 or float(tmp[7]) < 0:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' minimum and maximum scaling values must be greater than zero')
cfs = CFS()
cfs.alphascaling = tmp[0]
cfs.alphamin = float(tmp[1])
cfs.alphamax = float(tmp[2])
cfs.kappascaling = tmp[3]
cfs.kappamin = float(tmp[4])
cfs.kappamax = float(tmp[5])
cfs.sigmascaling = tmp[6]
cfs.sigmamin = float(tmp[7])
if tmp[8] == 'None':
cfs.sigmamax = None
else:
cfs.sigmamax = float(tmp[8])
if G.messages:
print('CFS parameters: alpha scaling {}, alpha_min {:.2f}, alpha_max {:.2f}, kappa scaling {}, kappa_min {:.2f}, kappa_max {:.2f}, sigma scaling {}, sigma_min {:.2f}, sigma_max {} created.'.format(cfs.alphascaling, cfs.alphamin, cfs.alphamax, cfs.kappascaling, cfs.kappamin, cfs.kappamax, cfs.sigmascaling, cfs.sigmamin, cfs.sigmamax))
G.cfs.append(cfs)

查看文件

@@ -0,0 +1,258 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import os, sys
import numpy as np
from psutil import virtual_memory
from .constants import c, floattype
from .exceptions import CmdInputError
from .pml import PML, CFS
from .utilities import rvalue, human_size
from .waveforms import Waveform
def process_singlecmds(singlecmds, multicmds, G):
"""Checks the validity of command parameters and creates instances of classes of parameters.
Args:
singlecmds (dict): Commands that can only occur once in the model.
multicmds (dict): Commands that can have multiple instances in the model (required to pass to process_materials_file function).
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Check validity of command parameters in order needed
# messages
cmd = '#messages'
if singlecmds[cmd] != 'None':
tmp = singlecmds[cmd].split()
if len(tmp) != 1:
raise CmdInputError(cmd + ' requires exactly one parameter')
if singlecmds[cmd].lower() == 'y':
G.messages = True
elif singlecmds[cmd].lower() == 'n':
G.messages = False
else:
raise CmdInputError(cmd + ' requires input values of either y or n')
# Title
cmd = '#title'
if singlecmds[cmd] != 'None':
G.title = singlecmds[cmd]
if G.messages:
print('Model title: {}'.format(G.title))
# Number of processors to run on (OpenMP)
cmd = '#num_threads'
ompthreads = os.environ.get('OMP_NUM_THREADS')
if singlecmds[cmd] != 'None':
tmp = tuple(int(x) for x in singlecmds[cmd].split())
if len(tmp) != 1:
raise CmdInputError(cmd + ' requires exactly one parameter to specify the number of OpenMP threads to use')
if tmp[0] < 1:
raise CmdInputError(cmd + ' requires the value to be an integer not less than one')
G.nthreads = tmp[0]
elif ompthreads:
G.nthreads = int(ompthreads)
else:
# Set number of threads to number of physical CPU cores, i.e. avoid hyperthreading with OpenMP for now
if sys.platform == 'darwin':
G.nthreads = int(os.popen('sysctl hw.physicalcpu').readlines()[0].split(':')[1].strip())
elif sys.platform == 'win32':
# Consider using wmi tools to check hyperthreading on Windows
G.nthreads = os.cpu_count()
elif 'linux' in sys.platform:
lscpu = os.popen('lscpu').readlines()
cpusockets = [item for item in lscpu if item.startswith('Socket(s)')]
cpusockets = int(cpusockets[0].split(':')[1].strip())
corespersocket = [item for item in lscpu if item.startswith('Core(s) per socket')]
corespersocket = int(corespersocket[0].split(':')[1].strip())
G.nthreads = cpusockets * corespersocket
else:
G.nthreads = os.cpu_count()
if G.messages:
print('Number of threads: {}'.format(G.nthreads))
# Spatial discretisation
cmd = '#dx_dy_dz'
tmp = [float(x) for x in singlecmds[cmd].split()]
if len(tmp) != 3:
raise CmdInputError(cmd + ' requires exactly three parameters')
if tmp[0] <= 0:
raise CmdInputError(cmd + ' requires the x-direction spatial step to be greater than zero')
if tmp[1] <= 0:
raise CmdInputError(cmd + ' requires the y-direction spatial step to be greater than zero')
if tmp[2] <= 0:
raise CmdInputError(cmd + ' requires the z-direction spatial step to be greater than zero')
G.dx = tmp[0]
G.dy = tmp[1]
G.dz = tmp[2]
if G.messages:
print('Spatial discretisation: {:.3f} x {:.3f} x {:.3f} m'.format(G.dx, G.dy, G.dz))
# Domain
cmd = '#domain'
tmp = [float(x) for x in singlecmds[cmd].split()]
nx = rvalue(tmp[0]/G.dx)
ny = rvalue(tmp[1]/G.dy)
nz = rvalue(tmp[2]/G.dz)
if len(tmp) != 3:
raise CmdInputError(cmd + ' requires exactly three parameters')
G.nx = nx
G.ny = ny
G.nz = nz
if G.messages:
print('Model domain: {:.3f} x {:.3f} x {:.3f} m ({:d} x {:d} x {:d} = {:d} Mcells)'.format(tmp[0], tmp[1], tmp[2], G.nx, G.ny, G.nz, int((G.nx * G.ny * G.nz)/1e6)))
mem = (((G.nx + 1) * (G.ny + 1) * (G.nz + 1) * 13 * np.dtype(floattype).itemsize + (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * 18) * 1.1) + 30e6
print('Memory (approx) required/available: {} / {}'.format(human_size(mem), human_size(virtual_memory().total)))
# Time step CFL limit - use either 2D or 3D (default)
cmd = '#time_step_limit_type'
if singlecmds[cmd] != 'None':
tmp = singlecmds[cmd].split()
if len(tmp) != 1:
raise CmdInputError(cmd + ' requires exactly one parameter')
if singlecmds[cmd].lower() == '2d':
if G.nx == 1:
G.dt = 1 / (c * np.sqrt((1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz)))
elif G.ny == 1:
G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dz) * (1 / G.dz)))
elif G.nz == 1:
G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy)))
else:
raise CmdInputError(cmd + ' 2D CFL limit can only be used when one dimension of the domain is one cell')
elif singlecmds[cmd].lower() == '3d':
G.dt = 1 / (c * np.sqrt((1 / G.dx) * (1 / G.dx) + (1 / G.dy) * (1 / G.dy) + (1 / G.dz) * (1 / G.dz)))
else:
raise CmdInputError(cmd + ' requires input values of either 2D or 3D')
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)))
if G.messages:
print('Time step: {:.3e} secs'.format(G.dt))
# Time step stability factor
cmd = '#time_step_stability_factor'
if singlecmds[cmd] != 'None':
tmp = tuple(float(x) for x in singlecmds[cmd].split())
if len(tmp) != 1:
raise CmdInputError(cmd + ' requires exactly one parameter')
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 G.messages:
print('Time step (modified): {:.3e} secs'.format(G.dt))
# Time window
cmd = '#time_window'
tmp = singlecmds[cmd].split()
if len(tmp) != 1:
raise CmdInputError(cmd + ' requires exactly one parameter to specify the time window. Either in seconds or number of iterations.')
tmp = tmp[0].lower()
# If real floating point value given
if '.' in tmp or 'e' in tmp:
if float(tmp) > 0:
G.timewindow = float(tmp)
G.iterations = rvalue((float(tmp) / G.dt)) + 1
else:
raise CmdInputError(cmd + ' must have a value greater than zero')
# If number of iterations given
else:
G.timewindow = (int(tmp) - 1) * G.dt
G.iterations = int(tmp)
if G.messages:
print('Time window: {:.3e} secs ({} iterations)'.format(G.timewindow, G.iterations))
# PML
cmd = '#pml_cells'
if singlecmds[cmd] != 'None':
tmp = singlecmds[cmd].split()
if len(tmp) != 1 and len(tmp) != 6:
raise CmdInputError(cmd + ' requires either one or six parameters')
if len(tmp) == 1:
G.pmlthickness = (int(tmp[0]), int(tmp[0]), int(tmp[0]), int(tmp[0]), int(tmp[0]), int(tmp[0]))
else:
G.pmlthickness = (int(tmp[0]), int(tmp[1]), int(tmp[2]), int(tmp[3]), int(tmp[4]), int(tmp[5]))
if 2*G.pmlthickness[0] >= G.nx or 2*G.pmlthickness[1] >= G.ny or 2*G.pmlthickness[2] >= G.nz or 2*G.pmlthickness[3] >= G.nx or 2*G.pmlthickness[4] >= G.ny or 2*G.pmlthickness[5] >= G.nz:
raise CmdInputError(cmd + ' has too many cells for the domain size')
# src_steps
cmd = '#src_steps'
if singlecmds[cmd] != 'None':
tmp = singlecmds[cmd].split()
if len(tmp) != 3:
raise CmdInputError(cmd + ' requires exactly three parameters')
G.txstepx = rvalue(float(tmp[0])/G.dx)
G.txstepy = rvalue(float(tmp[1])/G.dy)
G.txstepz = rvalue(float(tmp[2])/G.dz)
if G.messages:
print('All sources will step {:.3f}m, {:.3f}m, {:.3f}m for each model run.'.format(G.txstepx * G.dx, G.txstepy * G.dy, G.txstepz * G.dz))
# rx_steps
cmd = '#rx_steps'
if singlecmds[cmd] != 'None':
tmp = singlecmds[cmd].split()
if len(tmp) != 3:
raise CmdInputError(cmd + ' requires exactly three parameters')
G.rxstepx = rvalue(float(tmp[0])/G.dx)
G.rxstepy = rvalue(float(tmp[1])/G.dy)
G.rxstepz = rvalue(float(tmp[2])/G.dz)
if G.messages:
print('All receivers will step {:.3f}m, {:.3f}m, {:.3f}m for each model run.'.format(G.rxstepx * G.dx, G.rxstepy * G.dy, G.rxstepz * G.dz))
# Excitation file for user-defined source waveforms
cmd = '#excitation_file'
if singlecmds[cmd] != 'None':
tmp = singlecmds[cmd].split()
if len(tmp) != 1:
raise CmdInputError(cmd + ' requires exactly one parameter')
excitationfile = tmp[0]
# Open file and get waveform names
with open(excitationfile, 'r') as f:
waveformIDs = f.readline().split()
# Read all waveform values into an array
waveformvalues = np.loadtxt(excitationfile, skiprows=1, dtype=floattype)
for waveform in range(len(waveformIDs)):
if any(x.ID == waveformIDs[waveform] for x in G.waveforms):
raise CmdInputError('Waveform with ID {} already exists'.format(waveformIDs[waveform]))
w = Waveform()
w.ID = waveformIDs[waveform]
w.type = 'user'
w.uservalues = waveformvalues[:,waveform]
if G.messages:
print('User waveform {} created.'.format(w.ID))
G.waveforms.append(w)

233
gprMax/materials.py 普通文件
查看文件

@@ -0,0 +1,233 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
from .constants import e0, m0, floattype, complextype
class Material():
"""Materials, their properties and update coefficients."""
# Maximum number of dispersive material poles in a model
maxpoles = 0
# Types of material
types = ['standard', 'debye', 'lorenz', 'drude']
# 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, G):
"""
Args:
numID (int): Numeric identifier of the material.
ID (str): Name of the material.
G (class): Grid class instance - holds essential parameters describing the model.
"""
self.numID = numID
self.ID = ID
self.type = 'standard'
# Default material averaging
self.average = True
# Default material constitutive parameters (free_space)
self.er = 1.0
self.se = 0.0
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.
"""
HA = (m0*self.mr / G.dt) + 0.5*self.sm
HB = (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
self.DBz = (1 / G.dz) * 1 / HA
self.srcm = 1 / HA
# Calculate electric update coefficients
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
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 self.type == 'debye':
self.w[x] = self.deltaer[x] / self.tau[x]
self.q[x] = -1 / self.tau[x]
elif self.type == 'lorenz':
wp2 = (2 * np.pi * (1 / self.tau[x])) * (2 * np.pi * (1 / self.tau[x]))
self.w[x] = -(wp2 * self.deltaer[x]) * j / np.sqrt(wp2 - (self.alpha[x] * self.alpha[x]))
self.q[x] = -self.alpha[x] + np.sqrt(wp2 - (self.alpha[x] * self.alpha[x])) * j
elif self.type == 'drude':
wp2 = (2 * np.pi * (1 / self.tau[x])) * (2 * np.pi * (1 / self.tau[x]))
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
if self.ID == 'pec':
self.CA = 0
self.CBx = 0
self.CBy = 0
self.CBz = 0
self.srce = 0
else:
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
class PeplinskiSoil():
"""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):
"""
Args:
ID (str): Name of the soil.
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.
"""
self.ID = ID
self.S = sandfraction
self.C = clayfraction
self.rb = bulkdensity
self.rs = sandpartdensity
self.mu = watervolfraction
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 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.
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Debye model properties of water
f = 1.3e9
w = 2 * np.pi * f
erealw = Material.watereri + ((Material.waterdeltaer) / (1 + (w * Material.watertau)**2))
eimagw = w * Material.watertau * ((Material.waterdeltaer) / (1 + (w * Material.watertau)**2))
a = 0.65 # Experimentally derived constant
es = (1.01 + 0.44 * self.rs)**2 - 0.062
b1 = 1.2748 - 0.519 * self.S - 0.152 * self.C
b2 = 1.33797 - 0.603 * self.S - 0.166 * self.C
# For frequencies in the range 0.3GHz to 1.3GHz
sigf1 = 0.0467 + 0.2204 * self.rb - 0.411 * self.S + 0.6614 * self.C
# For frequencies in the range 1.4GHz to 18GHz
sigf2 = -1.645 + 1.939 * self.rb - 2.25622 * self.S + 1.594 * self.C
# Generate a set of bins based on the given volumetric water fraction values
mubins = np.linspace(self.mu[0], self.mu[1], nbins + 1)
# Generate a range of volumetric water fraction values the mid-point of each bin to make materials from
mumaterials = mubins + (mubins[1] - mubins[0]) / 2
# Create an iterator
muiter = np.nditer(mumaterials, flags=['c_index'])
while not muiter.finished:
# Real part for frequencies in the range 1.4GHz to 18GHz
er1 = (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
er2 = 1.15 * er1 - 0.68
# Imaginary part for frequencies in the range 0.3GHz to 1.3GHz
eri = er2 - (muiter[0]**(b2/a) * Material.waterdeltaer)
# Effective conductivity
sig = muiter[0]**(b2/a) * ((sigf1 * (self.rs - self.rb)) / (self.rs * muiter[0]))
# Check to see if the material already exists before creating a new one
requiredID = '|{:.4f}|'.format(float(muiter[0]))
material = next((x for x in G.materials if x.ID == requiredID), None)
if muiter.index == 0:
if material:
self.startmaterialnum = material.numID
else:
self.startmaterialnum = len(G.materials)
if not material:
m = Material(len(G.materials), requiredID, G)
m.average = False
m.er = eri
m.se = sig
m.deltaer.append(er2 - m.er)
m.tau.append(Material.watertau)
G.materials.append(m)
muiter.iternext()

343
gprMax/pml.py 普通文件
查看文件

@@ -0,0 +1,343 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
from .constants import e0, z0, floattype
class CFS():
"""PML CFS parameters."""
# Allowable scaling types
scalingtypes = {'constant': 0, 'linear': 1, 'inverselinear': -1, 'quadratic': 2, 'cubic': 3, 'quartic': 4}
def __init__(self, alphascaling='constant', alphamin=0, alphamax=0, kappascaling='constant', kappamin=1, kappamax=1, sigmascaling='quartic', sigmamin=0, sigmamax=None):
"""
Args:
alphascaling (str): Type of scaling used for alpha parameter. Can be: 'constant', 'linear', 'inverselinear', 'quadratic', 'cubic', 'quartic'.
alphamin (float): Minimum value for alpha parameter.
alphamax (float): Maximum value for alpha parameter.
kappascaling (str): Type of scaling used for kappa parameter. Can be: 'constant', 'linear', 'inverselinear', 'quadratic', 'cubic', 'quartic'.
kappamin (float): Minimum value for kappa parameter.
kappamax (float): Maximum value for kappa parameter.
sigmascaling (str): Type of scaling used for sigma parameter. Can be: 'constant', 'linear', 'inverselinear', 'quadratic', 'cubic', 'quartic'.
sigmamin (float): Minimum value for sigma parameter.
sigmamax (float): Maximum value for sigma parameter.
"""
self.alphascaling = alphascaling
self.alphamin = alphamin
self.alphamax = alphamax
self.kappascaling = kappascaling
self.kappamin = kappamin
self.kappamax = kappamax
self.sigmascaling = sigmascaling
self.sigmamin = sigmamin
self.sigmamax = sigmamax
def calculate_sigmamax(self, direction, er, mr, G):
"""Calculates an optimum value for sigma max based on underlying material properties.
Args:
direction (str): Direction of PML slab
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.
"""
# Get general direction from first letter of PML direction
if direction[0] == 'x':
d = G.dx
elif direction[0] == 'y':
d = G.dy
elif direction[0] == 'z':
d = G.dz
# Calculation of the maximum value of sigma from http://dx.doi.org/10.1109/8.546249
m = CFS.scalingtypes[self.sigmascaling]
self.sigmamax = (0.8 * (m + 1)) / (z0 * d * np.sqrt(er * mr))
def scaling_polynomial(self, min, max, order, Evalues, Hvalues):
"""Applies the polynomial to be used for scaling for electric and magnetic PML updates based on scaling type and minimum and maximum values.
Args:
min (float): Minimum value for scaling.
max (float): Maximum value for scaling.
order (int): Order of polynomial for scaling.
Evalues (float): numpy array holding scaling value for electric PML update.
Hvalues (float): numpy array holding scaling value for magnetic PML update.
Returns:
Evalues (float): numpy array holding scaling value for electric PML update.
Hvalues (float): numpy array holding scaling value for magnetic PML update.
"""
tmp = max * ((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, min, max, scaling, Evalues, Hvalues):
"""Calculates values for electric and magnetic PML updates based on scaling type and minimum and maximum values.
Args:
min (float): Minimum value for scaling.
max (float): Maximum value for scaling.
scaling (int): Type of scaling, can be: 'constant', 'linear', 'inverselinear', 'quadratic', 'cubic', 'quartic'.
Evalues (float): numpy array holding scaling value for electric PML update.
Hvalues (float): numpy array holding scaling value for magnetic PML update.
Returns:
Evalues (float): numpy array holding scaling value for electric PML update.
Hvalues (float): numpy array holding scaling value for magnetic PML update.
"""
if scaling == 'constant':
Evalues += max
Hvalues += max
else:
Evalues, Hvalues = self.scaling_polynomial(min, max, CFS.scalingtypes[scaling], Evalues, Hvalues)
if scaling == 'inverselinear':
Evalues = Evalues[::-1]
Hvalues = Hvalues[::-1]
# print('Evalues: scaling {}, {}'.format(scaling, Evalues))
# print('Hvalues: scaling {}, {}'.format(scaling, Hvalues))
return Evalues, Hvalues
class PML():
"""PML - the implementation comes from the derivation in: http://dx.doi.org/10.1109/TAP.2011.2180344"""
def __init__(self, direction=None, xs=0, ys=0, zs=0, xf=0, yf=0, zf=0, cfs=[]):
"""
Args:
xs, xf, ys, yf, zs, zf (float): Extent of the PML volume.
cfs (list): CFS class instances associated with the PML.
"""
self.direction = direction
self.xs = xs
self.xf = xf
self.ys = ys
self.yf = yf
self.zs = zs
self.zf = zf
self.nx = xf - xs
self.ny = yf - ys
self.nz = zf - zs
self.CFS = cfs
if not self.CFS:
self.CFS = [CFS()]
# Subscript notation, e.g. 'EPhiyxz' means the electric field Phi vector, of which the
# component being corrected is y, the stretching direction is x, and field derivative
# is z direction.
if self.direction == 'xminus' or self.direction == 'xplus':
self.thickness = self.nx
self.EPhiyxz = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1), dtype=floattype)
self.EPhizxy = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz), dtype=floattype)
self.HPhiyxz = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz), dtype=floattype)
self.HPhizxy = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1), dtype=floattype)
elif self.direction == 'yminus' or self.direction == 'yplus':
self.thickness = self.ny
self.EPhixyz = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1), dtype=floattype)
self.EPhizyx = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz), dtype=floattype)
self.HPhixyz = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz), dtype=floattype)
self.HPhizyx = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1), dtype=floattype)
elif self.direction == 'zminus' or self.direction == 'zplus':
self.thickness = self.nz
self.EPhixzy = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1), dtype=floattype)
self.EPhiyzx = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1), dtype=floattype)
self.HPhixzy = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz), dtype=floattype)
self.HPhiyzx = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz), dtype=floattype)
self.ERA = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
self.ERB = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
self.ERE = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
self.ERF = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
self.HRA = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
self.HRB = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
self.HRE = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
self.HRF = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
def calculate_update_coeffs(self, er, mr, G):
"""Calculates electric and magnetic update coefficients for the PML.
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.
"""
for x, cfs in enumerate(self.CFS):
Ealpha = np.zeros(self.thickness + 1, dtype=floattype)
Halpha = np.zeros(self.thickness + 1, dtype=floattype)
Ekappa = np.zeros(self.thickness + 1, dtype=floattype)
Hkappa = np.zeros(self.thickness + 1, dtype=floattype)
Esigma = np.zeros(self.thickness + 1, dtype=floattype)
Hsigma = np.zeros(self.thickness + 1, dtype=floattype)
if not cfs.sigmamax:
cfs.calculate_sigmamax(self.direction, er, mr, G)
Ealpha, Halpha = cfs.calculate_values(cfs.alphamin, cfs.alphamax, cfs.alphascaling, Ealpha, Halpha)
Ekappa, Hkappa = cfs.calculate_values(cfs.kappamin, cfs.kappamax, cfs.kappascaling, Ekappa, Hkappa)
Esigma, Hsigma = cfs.calculate_values(cfs.sigmamin, cfs.sigmamax, cfs.sigmascaling, Esigma, Hsigma)
# print('Ealpha {}'.format(Ealpha))
# print('Halpha {}'.format(Halpha))
# print('Ekappa {}'.format(Ekappa))
# print('Hkappa {}'.format(Hkappa))
# print('Esigma {}'.format(Esigma))
# print('Hsigma {}'.format(Hsigma))
# Electric PML 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
self.ERF[x, :] = (2*Esigma*G.dt) / (Ekappa * tmp)
# Magnetic PML 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
self.HRF[x, :] = (2*Hsigma*G.dt) / (Hkappa * tmp)
# print('ERA {}'.format(self.ERA))
# print('ERB {}'.format(self.ERB))
# print('ERE {}'.format(self.ERE))
# print('ERF {}'.format(self.ERF))
# print('HRA {}'.format(self.HRA))
# print('HRB {}'.format(self.HRB))
# print('HRE {}'.format(self.HRE))
# print('HRF {}'.format(self.HRF))
def build_pml(G):
"""This function builds instances of the PML."""
if G.messages:
print('')
# Create the PML slabs
if G.pmlthickness[0] > 0:
pml = PML(direction='xminus', xf=G.pmlthickness[0], yf=G.ny, zf=G.nz, cfs=G.cfs)
if G.messages and G.pmlthickness.count(G.pmlthickness[0]) != len(G.pmlthickness):
print('PML {} slab with {} cells created.'.format(pml.direction, pml.thickness))
G.pmls.append(pml)
if G.pmlthickness[1] > 0:
pml = PML(direction='yminus', xf=G.nx, yf=G.pmlthickness[1], zf=G.nz, cfs=G.cfs)
if G.messages and G.pmlthickness.count(G.pmlthickness[0]) != len(G.pmlthickness):
print('PML {} slab with {} cells created.'.format(pml.direction, pml.thickness))
G.pmls.append(pml)
if G.pmlthickness[2] > 0:
pml = PML(direction='zminus', xf=G.nx, yf=G.ny, zf=G.pmlthickness[2], cfs=G.cfs)
if G.messages and G.pmlthickness.count(G.pmlthickness[0]) != len(G.pmlthickness):
print('PML {} slab with {} cells created.'.format(pml.direction, pml.thickness))
G.pmls.append(pml)
if G.pmlthickness[3] > 0:
pml = PML(direction='xplus', xs=G.nx-G.pmlthickness[3], xf=G.nx, yf=G.ny, zf=G.nz, cfs=G.cfs)
if G.messages and G.pmlthickness.count(G.pmlthickness[0]) != len(G.pmlthickness):
print('PML {} slab with {} cells created.'.format(pml.direction, pml.thickness))
G.pmls.append(pml)
if G.pmlthickness[4] > 0:
pml = PML(direction='yplus', xf=G.nx, ys=G.ny-G.pmlthickness[4], yf=G.ny, zf=G.nz, cfs=G.cfs)
if G.messages and G.pmlthickness.count(G.pmlthickness[0]) != len(G.pmlthickness):
print('PML {} slab with {} cells created.'.format(pml.direction, pml.thickness))
G.pmls.append(pml)
if G.pmlthickness[5] > 0:
pml = PML(direction='zplus', xf=G.nx, yf=G.ny, zs=G.nz-G.pmlthickness[5], zf=G.nz, cfs=G.cfs)
if G.messages and G.pmlthickness.count(G.pmlthickness[0]) != len(G.pmlthickness):
print('PML {} slab with {} cells created.'.format(pml.direction, pml.thickness))
G.pmls.append(pml)
if G.messages and G.pmlthickness.count(G.pmlthickness[0]) == len(G.pmlthickness):
if G.pmlthickness[0] == 0:
print('PML is switched off')
else:
print('PML: {} cells'.format(pml.thickness))
def calculate_initial_pml_params(G):
""" This function calculates the initial parameters and coefficients for PML including setting scaling
(based on underlying material er and mr from solid array).
"""
for pml in G.pmls:
sumer = 0
summr = 0
if pml.direction == 'xminus':
for j in range(G.ny):
for k in range(G.nz):
numID = G.solid[0, j, k]
material = next(x for x in G.materials if x.numID == numID)
sumer += material.er
summr += material.mr
averageer = sumer / (G.ny * G.nz)
averagemr = summr / (G.ny * G.nz)
elif pml.direction == 'xplus':
for j in range(G.ny):
for k in range(G.nz):
numID = G.solid[G.nx - pml.thickness, j, k]
material = next(x for x in G.materials if x.numID == numID)
sumer += material.er
summr += material.mr
averageer = sumer / (G.ny * G.nz)
averagemr = summr / (G.ny * G.nz)
elif pml.direction == 'yminus':
for i in range(G.nx):
for k in range(G.nz):
numID = G.solid[i, 0, k]
material = next(x for x in G.materials if x.numID == numID)
sumer += material.er
summr += material.mr
averageer = sumer / (G.nx * G.nz)
averagemr = summr / (G.nx * G.nz)
elif pml.direction == 'yplus':
for i in range(G.nx):
for k in range(G.nz):
numID = G.solid[i, G.ny - pml.thickness, k]
material = next(x for x in G.materials if x.numID == numID)
sumer += material.er
summr += material.mr
averageer = sumer / (G.nx * G.nz)
averagemr = summr / (G.nx * G.nz)
elif pml.direction == 'zminus':
for i in range(G.nx):
for j in range(G.ny):
numID = G.solid[i, j, 0]
material = next(x for x in G.materials if x.numID == numID)
sumer += material.er
summr += material.mr
averageer = sumer / (G.nx * G.ny)
averagemr = summr / (G.nx * G.ny)
elif pml.direction == 'zplus':
for i in range(G.nx):
for j in range(G.ny):
numID = G.solid[i, j, G.nz - pml.thickness]
material = next(x for x in G.materials if x.numID == numID)
sumer += material.er
summr += material.mr
averageer = sumer / (G.nx * G.ny)
averagemr = summr / (G.nx * G.ny)
pml.calculate_update_coeffs(averageer, averagemr, G)

665
gprMax/pml_1order_update.pyx 普通文件
查看文件

@@ -0,0 +1,665 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
from cython.parallel import prange
from .constants cimport floattype_t, complextype_t
#############################################
# Electric field PML updates - Ex component #
#############################################
cpdef update_pml_1order_ex_yplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hz, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Ex field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[0, i + xs, j + ys, k + zs]
dHz = (Hz[i + xs, j + ys, k + zs] - Hz[i + xs, j - 1 + ys, k + zs]) / dy
Ex[i + xs, j + ys, k + zs] = Ex[i + xs, j + ys, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, j] - 1) * dHz + RB[0, j] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, j] * EPhi[0, i, j, k] - RF[0, j] * dHz
cpdef update_pml_1order_ex_yminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hz, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Ex field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[0, i + xs, yf - j, k + zs]
dHz = (Hz[i + xs, yf - j, k + zs] - Hz[i + xs, yf - j - 1, k + zs]) / dy
Ex[i + xs, yf - j, k + zs] = Ex[i + xs, yf - j, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, j] - 1) * dHz + RB[0, j] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, j] * EPhi[0, i, j, k] - RF[0, j] * dHz
cpdef update_pml_1order_ex_zplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hy, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Ex field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[0, i + xs, j + ys, k + zs]
dHy = (Hy[i + xs, j + ys, k + zs] - Hy[i + xs, j + ys, k - 1 + zs]) / dz
Ex[i + xs, j + ys, k + zs] = Ex[i + xs, j + ys, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, k] - 1) * dHy + RB[0, k] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, k] * EPhi[0, i, j, k] - RF[0, k] * dHy
cpdef update_pml_1order_ex_zminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hy, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Ex field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[0, i + xs, j + ys, zf - k]
dHy = (Hy[i + xs, j + ys, zf - k] - Hy[i + xs, j + ys, zf - k - 1]) / dz
Ex[i + xs, j + ys, zf - k] = Ex[i + xs, j + ys, zf - k] - updatecoeffsE[listIndex, 4] * ((RA[0, k] - 1) * dHy + RB[0, k] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, k] * EPhi[0, i, j, k] - RF[0, k] * dHy
#############################################
# Electric field PML updates - Ey component #
#############################################
cpdef update_pml_1order_ey_xplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hz, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Ey field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[1, i + xs, j + ys, k + zs]
dHz = (Hz[i + xs, j + ys, k + zs] - Hz[i - 1 + xs, j + ys, k + zs]) / dx
Ey[i + xs, j + ys, k + zs] = Ey[i + xs, j + ys, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, i] - 1) * dHz + RB[0, i] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, i] * EPhi[0, i, j, k] - RF[0, i] * dHz
cpdef update_pml_1order_ey_xminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hz, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Ey field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[1, xf - i, j + ys, k + zs]
dHz = (Hz[xf - i, j + ys, k + zs] - Hz[xf - i - 1, j + ys, k + zs]) / dx
Ey[xf - i, j + ys, k + zs] = Ey[xf - i, j + ys, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, i] - 1) * dHz + RB[0, i] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, i] * EPhi[0, i, j, k] - RF[0, i] * dHz
cpdef update_pml_1order_ey_zplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hx, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Ey field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[1, i + xs, j + ys, k + zs]
dHx = (Hx[i + xs, j + ys, k + zs] - Hx[i + xs, j + ys, k - 1 + zs]) / dz
Ey[i + xs, j + ys, k + zs] = Ey[i + xs, j + ys, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, k] - 1) * dHx + RB[0, k] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, k] * EPhi[0, i, j, k] - RF[0, k] * dHx
cpdef update_pml_1order_ey_zminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hx, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Ey field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[1, i + xs, j + ys, zf - k]
dHx = (Hx[i + xs, j + ys, zf - k] - Hx[i + xs, j + ys, zf - k - 1]) / dz
Ey[i + xs, j + ys, zf - k] = Ey[i + xs, j + ys, zf - k] + updatecoeffsE[listIndex, 4] * ((RA[0, k] - 1) * dHx + RB[0, k] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, k] * EPhi[0, i, j, k] - RF[0, k] * dHx
#############################################
# Electric field PML updates - Ez component #
#############################################
cpdef update_pml_1order_ez_xplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hy, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Ey field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[2, i + xs, j + ys, k + zs]
dHy = (Hy[i + xs, j + ys, k + zs] - Hy[i - 1 + xs, j + ys, k + zs]) / dx
Ez[i + xs, j + ys, k + zs] = Ez[i + xs, j + ys, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, i] - 1) * dHy + RB[0, i] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, i] * EPhi[0, i, j, k] - RF[0, i] * dHy
cpdef update_pml_1order_ez_xminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hy, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Ez field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[2, xf - i, j + ys, k + zs]
dHy = (Hy[xf - i, j + ys, k + zs] - Hy[xf - i - 1, j + ys, k + zs]) / dx
Ez[xf - i, j + ys, k + zs] = Ez[xf - i, j + ys, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, i] - 1) * dHy + RB[0, i] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, i] * EPhi[0, i, j, k] - RF[0, i] * dHy
cpdef update_pml_1order_ez_yplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hx, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Ez field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[2, i + xs, j + ys, k + zs]
dHx = (Hx[i + xs, j + ys, k + zs] - Hx[i + xs, j - 1 + ys, k + zs]) / dy
Ez[i + xs, j + ys, k + zs] = Ez[i + xs, j + ys, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, j] - 1) * dHx + RB[0, j] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, j] * EPhi[0, i, j, k] - RF[0, j] * dHx
cpdef update_pml_1order_ez_yminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hx, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Ez field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[2, i + xs, yf - j, k + zs]
dHx = (Hx[i + xs, yf - j, k + zs] - Hx[i + xs, yf - j - 1, k + zs]) / dy
Ez[i + xs, yf - j, k + zs] = Ez[i + xs, yf - j, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, j] - 1) * dHx + RB[0, j] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, j] * EPhi[0, i, j, k] - RF[0, j] * dHx
#############################################
# Magnetic field PML updates - Hx component #
#############################################
cpdef update_pml_1order_hx_yplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Ez, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Hx field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[3, i + xs, j + ys, k + zs]
dEz = (Ez[i + xs, j + 1 + ys, k + zs] - Ez[i + xs, j + ys, k + zs]) / dy
Hx[i + xs, j + ys, k + zs] = Hx[i + xs, j + ys, k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, j] - 1) * dEz + RB[0, j] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, j] * HPhi[0, i, j, k] - RF[0, j] * dEz
cpdef update_pml_1order_hx_yminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Ez, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Hx field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[3, i + xs, yf - (j + 1), k + zs]
dEz = (Ez[i + xs, yf - j, k + zs] - Ez[i + xs, yf - (j + 1), k + zs]) / dy
Hx[i + xs, yf - (j + 1), k + zs] = Hx[i + xs, yf - (j + 1), k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, j] - 1) * dEz + RB[0, j] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, j] * HPhi[0, i, j, k] - RF[0, j] * dEz
cpdef update_pml_1order_hx_zplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Ey, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Hx field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[3, i + xs, j + ys, k + zs]
dEy = (Ey[i + xs, j + ys, k + 1 + zs] - Ey[i + xs, j + ys, k + zs]) / dz
Hx[i + xs, j + ys, k + zs] = Hx[i + xs, j + ys, k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, k] - 1) * dEy + RB[0, k] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, k] * HPhi[0, i, j, k] - RF[0, k] * dEy
cpdef update_pml_1order_hx_zminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Ey, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Hx field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[3, i + xs, j + ys, zf - (k + 1)]
dEy = (Ey[i + xs, j + ys, zf - k] - Ey[i + xs, j + ys, zf - (k + 1)]) / dz
Hx[i + xs, j + ys, zf - (k + 1)] = Hx[i + xs, j + ys, zf - (k + 1)] + updatecoeffsH[listIndex, 4] * ((RA[0, k] - 1) * dEy + RB[0, k] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, k] * HPhi[0, i, j, k] - RF[0, k] * dEy
#############################################
# Magnetic field PML updates - Hy component #
#############################################
cpdef update_pml_1order_hy_xplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Ez, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Hy field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[4, i + xs, j + ys, k + zs]
dEz = (Ez[i + 1 + xs, j + ys, k + zs] - Ez[i + xs, j + ys, k + zs]) / dx
Hy[i + xs, j + ys, k + zs] = Hy[i + xs, j + ys, k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, i] - 1) * dEz + RB[0, i] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, i] * HPhi[0, i, j, k] - RF[0, i] * dEz
cpdef update_pml_1order_hy_xminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Ez, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Hy field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[4, xf - (i + 1), j + ys, k + zs]
dEz = (Ez[xf - i, j + ys, k + zs] - Ez[xf - (i + 1), j + ys, k + zs]) / dx
Hy[xf - (i + 1), j + ys, k + zs] = Hy[xf - (i + 1), j + ys, k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, i] - 1) * dEz + RB[0, i] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, i] * HPhi[0, i, j, k] - RF[0, i] * dEz
cpdef update_pml_1order_hy_zplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Ex, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Hy field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[4, i + xs, j + ys, k + zs]
dEx = (Ex[i + xs, j + ys, k + 1 + zs] - Ex[i + xs, j + ys, k + zs]) / dz
Hy[i + xs, j + ys, k + zs] = Hy[i + xs, j + ys, k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, k] - 1) * dEx + RB[0, k] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, k] * HPhi[0, i, j, k] - RF[0, k] * dEx
cpdef update_pml_1order_hy_zminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Ex, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Hy field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[4, i + xs, j + ys, zf - (k + 1)]
dEx = (Ex[i + xs, j + ys, zf - k] - Ex[i + xs, j + ys, zf - (k + 1)]) / dz
Hy[i + xs, j + ys, zf - (k + 1)] = Hy[i + xs, j + ys, zf - (k + 1)] - updatecoeffsH[listIndex, 4] * ((RA[0, k] - 1) * dEx + RB[0, k] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, k] * HPhi[0, i, j, k] - RF[0, k] * dEx
#############################################
# Magnetic field PML updates - Hz component #
#############################################
cpdef update_pml_1order_hz_xplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hz, floattype_t[:, :, :] Ey, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Hz field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[5, i + xs, j + ys, k + zs]
dEy = (Ey[i + 1 + xs, j + ys, k + zs] - Ey[i + xs, j + ys, k + zs]) / dx
Hz[i + xs, j + ys, k + zs] = Hz[i + xs, j + ys, k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, i] - 1) * dEy + RB[0, i] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, i] * HPhi[0, i, j, k] - RF[0, i] * dEy
cpdef update_pml_1order_hz_xminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hz, floattype_t[:, :, :] Ey, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Hz field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[5, xf - (i + 1), j + ys, k + zs]
dEy = (Ey[xf - i, j + ys, k + zs] - Ey[xf - (i + 1), j + ys, k + zs]) / dx
Hz[xf - (i + 1), j + ys, k + zs] = Hz[xf - (i + 1), j + ys, k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, i] - 1) * dEy + RB[0, i] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, i] * HPhi[0, i, j, k] - RF[0, i] * dEy
cpdef update_pml_1order_hz_yplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hz, floattype_t[:, :, :] Ex, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Hz field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[5, i + xs, j + ys, k + zs]
dEx = (Ex[i + xs, j + 1 + ys, k + zs] - Ex[i + xs, j + ys, k + zs]) / dy
Hz[i + xs, j + ys, k + zs] = Hz[i + xs, j + ys, k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, j] - 1) * dEx + RB[0, j] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, j] * HPhi[0, i, j, k] - RF[0, j] * dEx
cpdef update_pml_1order_hz_yminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hz, floattype_t[:, :, :] Ex, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Hz field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[5, i + xs, yf - (j + 1), k + zs]
dEx = (Ex[i + xs, yf - j, k + zs] - Ex[i + xs, yf - (j + 1), k + zs]) / dy
Hz[i + xs, yf - (j + 1), k + zs] = Hz[i + xs, yf - (j + 1), k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, j] - 1) * dEx + RB[0, j] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, j] * HPhi[0, i, j, k] - RF[0, j] * dEx

689
gprMax/pml_2order_update.pyx 普通文件
查看文件

@@ -0,0 +1,689 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
from cython.parallel import prange
from .constants cimport floattype_t, complextype_t
#############################################
# Electric field PML updates - Ex component #
#############################################
cpdef update_pml_2order_ex_yplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hz, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Ex field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[0, i + xs, j + ys, k + zs]
dHz = (Hz[i + xs, j + ys, k + zs] - Hz[i + xs, j - 1 + ys, k + zs]) / dy
Ex[i + xs, j + ys, k + zs] = Ex[i + xs, j + ys, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, j] * RA[1, j] - 1) * dHz + RA[1, j] * RB[0, j] * EPhi[0, i, j, k] + RB[1, j] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, j] * EPhi[1, i, j, k] - RF[1, j] * (RA[0, j] * dHz + RB[0, j] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, j] * EPhi[0, i, j, k] - RF[0, j] * dHz
cpdef update_pml_2order_ex_yminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hz, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Ex field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[0, i + xs, yf - j, k + zs]
dHz = (Hz[i + xs, yf - j, k + zs] - Hz[i + xs, yf - j - 1, k + zs]) / dy
Ex[i + xs, yf - j, k + zs] = Ex[i + xs, yf - j, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, j] * RA[1, j] - 1) * dHz + RA[1, j] * RB[0, j] * EPhi[0, i, j, k] + RB[1, j] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, j] * EPhi[1, i, j, k] - RF[1, j] * (RA[0, j] * dHz + RB[0, j] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, j] * EPhi[0, i, j, k] - RF[0, j] * dHz
cpdef update_pml_2order_ex_zplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hy, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Ex field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[0, i + xs, j + ys, k + zs]
dHy = (Hy[i + xs, j + ys, k + zs] - Hy[i + xs, j + ys, k - 1 + zs]) / dz
Ex[i + xs, j + ys, k + zs] = Ex[i + xs, j + ys, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, k] * RA[1, k] - 1) * dHy + RA[1, k] * RB[0, k] * EPhi[0, i, j, k] + RB[1, k] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, k] * EPhi[1, i, j, k] - RF[1, k] * (RA[0, k] * dHy + RB[0, k] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, k] * EPhi[0, i, j, k] - RF[0, k] * dHy
cpdef update_pml_2order_ex_zminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ex, floattype_t[:, :, :] Hy, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Ex field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[0, i + xs, j + ys, zf - k]
dHy = (Hy[i + xs, j + ys, zf - k] - Hy[i + xs, j + ys, zf - k - 1]) / dz
Ex[i + xs, j + ys, zf - k] = Ex[i + xs, j + ys, zf - k] - updatecoeffsE[listIndex, 4] * ((RA[0, k] * RA[1, k] - 1) * dHy + RA[1, k] * RB[0, k] * EPhi[0, i, j, k] + RB[1, k] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, k] * EPhi[1, i, j, k] - RF[1, k] * (RA[0, k] * dHy + RB[0, k] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, k] * EPhi[0, i, j, k] - RF[0, k] * dHy
#############################################
# Electric field PML updates - Ey component #
#############################################
cpdef update_pml_2order_ey_xplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hz, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Ey field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[1, i + xs, j + ys, k + zs]
dHz = (Hz[i + xs, j + ys, k + zs] - Hz[i - 1 + xs, j + ys, k + zs]) / dx
Ey[i + xs, j + ys, k + zs] = Ey[i + xs, j + ys, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, i] * RA[1, i] - 1) * dHz + RA[1, i] * RB[0, i] * EPhi[0, i, j, k] + RB[1, i] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, i] * EPhi[1, i, j, k] - RF[1, i] * (RA[0, i] * dHz + RB[0, i] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, i] * EPhi[0, i, j, k] - RF[0, i] * dHz
cpdef update_pml_2order_ey_xminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hz, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Ey field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[1, xf - i, j + ys, k + zs]
dHz = (Hz[xf - i, j + ys, k + zs] - Hz[xf - i - 1, j + ys, k + zs]) / dx
Ey[xf - i, j + ys, k + zs] = Ey[xf - i, j + ys, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, i] * RA[1, i] - 1) * dHz + RA[1, i] * RB[0, i] * EPhi[0, i, j, k] + RB[1, i] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, i] * EPhi[1, i, j, k] - RF[1, i] * (RA[0, i] * dHz + RB[0, i] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, i] * EPhi[0, i, j, k] - RF[0, i] * dHz
cpdef update_pml_2order_ey_zplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hx, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Ey field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[1, i + xs, j + ys, k + zs]
dHx = (Hx[i + xs, j + ys, k + zs] - Hx[i + xs, j + ys, k - 1 + zs]) / dz
Ey[i + xs, j + ys, k + zs] = Ey[i + xs, j + ys, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, k] * RA[1, k] - 1) * dHx + RA[1, k] * RB[0, k] * EPhi[0, i, j, k] + RB[1, k] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, k] * EPhi[1, i, j, k] - RF[1, k] * (RA[0, k] * dHx + RB[0, k] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, k] * EPhi[0, i, j, k] - RF[0, k] * dHx
cpdef update_pml_2order_ey_zminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ey, floattype_t[:, :, :] Hx, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Ey field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[1, i + xs, j + ys, zf - k]
dHx = (Hx[i + xs, j + ys, zf - k] - Hx[i + xs, j + ys, zf - k - 1]) / dz
Ey[i + xs, j + ys, zf - k] = Ey[i + xs, j + ys, zf - k] + updatecoeffsE[listIndex, 4] * ((RA[0, k] * RA[1, k] - 1) * dHx + RA[1, k] * RB[0, k] * EPhi[0, i, j, k] + RB[1, k] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, k] * EPhi[1, i, j, k] - RF[1, k] * (RA[0, k] * dHx + RB[0, k] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, k] * EPhi[0, i, j, k] - RF[0, k] * dHx
#############################################
# Electric field PML updates - Ez component #
#############################################
cpdef update_pml_2order_ez_xplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hy, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Ey field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[2, i + xs, j + ys, k + zs]
dHy = (Hy[i + xs, j + ys, k + zs] - Hy[i - 1 + xs, j + ys, k + zs]) / dx
Ez[i + xs, j + ys, k + zs] = Ez[i + xs, j + ys, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, i] * RA[1, i] - 1) * dHy + RA[1, i] * RB[0, i] * EPhi[0, i, j, k] + RB[1, i] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, i] * EPhi[1, i, j, k] - RF[1, i] * (RA[0, i] * dHy + RB[0, i] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, i] * EPhi[0, i, j, k] - RF[0, i] * dHy
cpdef update_pml_2order_ez_xminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hy, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Ez field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[2, xf - i, j + ys, k + zs]
dHy = (Hy[xf - i, j + ys, k + zs] - Hy[xf - i - 1, j + ys, k + zs]) / dx
Ez[xf - i, j + ys, k + zs] = Ez[xf - i, j + ys, k + zs] + updatecoeffsE[listIndex, 4] * ((RA[0, i] * RA[1, i] - 1) * dHy + RA[1, i] * RB[0, i] * EPhi[0, i, j, k] + RB[1, i] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, i] * EPhi[1, i, j, k] - RF[1, i] * (RA[0, i] * dHy + RB[0, i] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, i] * EPhi[0, i, j, k] - RF[0, i] * dHy
cpdef update_pml_2order_ez_yplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hx, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Ez field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[2, i + xs, j + ys, k + zs]
dHx = (Hx[i + xs, j + ys, k + zs] - Hx[i + xs, j - 1 + ys, k + zs]) / dy
Ez[i + xs, j + ys, k + zs] = Ez[i + xs, j + ys, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, j] * RA[1, j] - 1) * dHx + RA[1, j] * RB[0, j] * EPhi[0, i, j, k] + RB[1, j] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, j] * EPhi[1, i, j, k] - RF[1, j] * (RA[0, j] * dHx + RB[0, j] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, j] * EPhi[0, i, j, k] - RF[0, j] * dHx
cpdef update_pml_2order_ez_yminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsE, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Ez, floattype_t[:, :, :] Hx, floattype_t[:, :, :, :] EPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Ez field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dHx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[2, i + xs, yf - j, k + zs]
dHx = (Hx[i + xs, yf - j, k + zs] - Hx[i + xs, yf - j - 1, k + zs]) / dy
Ez[i + xs, yf - j, k + zs] = Ez[i + xs, yf - j, k + zs] - updatecoeffsE[listIndex, 4] * ((RA[0, j] * RA[1, j] - 1) * dHx + RA[1, j] * RB[0, j] * EPhi[0, i, j, k] + RB[1, j] * EPhi[1, i, j, k])
EPhi[1, i, j, k] = RE[1, j] * EPhi[1, i, j, k] - RF[1, j] * (RA[0, j] * dHx + RB[0, j] * EPhi[0, i, j, k])
EPhi[0, i, j, k] = RE[0, j] * EPhi[0, i, j, k] - RF[0, j] * dHx
#############################################
# Magnetic field PML updates - Hx component #
#############################################
cpdef update_pml_2order_hx_yplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Ez, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Hx field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[3, i + xs, j + ys, k + zs]
dEz = (Ez[i + xs, j + 1 + ys, k + zs] - Ez[i + xs, j + ys, k + zs]) / dy
Hx[i + xs, j + ys, k + zs] = Hx[i + xs, j + ys, k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, j] * RA[1, j] - 1) * dEz + RA[1, j] * RB[0, j] * HPhi[0, i, j, k] + RB[1, j] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, j] * HPhi[1, i, j, k] - RF[1, j] * (RA[0, j] * dEz + RB[0, j] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, j] * HPhi[0, i, j, k] - RF[0, j] * dEz
cpdef update_pml_2order_hx_yminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Ez, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Hx field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[3, i + xs, yf - (j + 1), k + zs]
dEz = (Ez[i + xs, yf - j, k + zs] - Ez[i + xs, yf - (j + 1), k + zs]) / dy
Hx[i + xs, yf - (j + 1), k + zs] = Hx[i + xs, yf - (j + 1), k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, j] * RA[1, j] - 1) * dEz + RA[1, j] * RB[0, j] * HPhi[0, i, j, k] + RB[1, j] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, j] * HPhi[1, i, j, k] - RF[1, j] * (RA[0, j] * dEz + RB[0, j] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, j] * HPhi[0, i, j, k] - RF[0, j] * dEz
cpdef update_pml_2order_hx_zplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Ey, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Hx field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[3, i + xs, j + ys, k + zs]
dEy = (Ey[i + xs, j + ys, k + 1 + zs] - Ey[i + xs, j + ys, k + zs]) / dz
Hx[i + xs, j + ys, k + zs] = Hx[i + xs, j + ys, k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, k] * RA[1, k] - 1) * dEy + RA[1, k] * RB[0, k] * HPhi[0, i, j, k] + RB[1, k] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, k] * HPhi[1, i, j, k] - RF[1, k] * (RA[0, k] * dEy + RB[0, k] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, k] * HPhi[0, i, j, k] - RF[0, k] * dEy
cpdef update_pml_2order_hx_zminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hx, floattype_t[:, :, :] Ey, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Hx field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[3, i + xs, j + ys, zf - (k + 1)]
dEy = (Ey[i + xs, j + ys, zf - k] - Ey[i + xs, j + ys, zf - (k + 1)]) / dz
Hx[i + xs, j + ys, zf - (k + 1)] = Hx[i + xs, j + ys, zf - (k + 1)] + updatecoeffsH[listIndex, 4] * ((RA[0, k] * RA[1, k] - 1) * dEy + RA[1, k] * RB[0, k] * HPhi[0, i, j, k] + RB[1, k] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, k] * HPhi[1, i, j, k] - RF[1, k] * (RA[0, k] * dEy + RB[0, k] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, k] * HPhi[0, i, j, k] - RF[0, k] * dEy
#############################################
# Magnetic field PML updates - Hy component #
#############################################
cpdef update_pml_2order_hy_xplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Ez, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Hy field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[4, i + xs, j + ys, k + zs]
dEz = (Ez[i + 1 + xs, j + ys, k + zs] - Ez[i + xs, j + ys, k + zs]) / dx
Hy[i + xs, j + ys, k + zs] = Hy[i + xs, j + ys, k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, i] * RA[1, i] - 1) * dEz + RA[1, i] * RB[0, i] * HPhi[0, i, j, k] + RB[1, i] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, i] * HPhi[1, i, j, k] - RF[1, i] * (RA[0, i] * dEz + RB[0, i] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, i] * HPhi[0, i, j, k] - RF[0, i] * dEz
cpdef update_pml_2order_hy_xminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Ez, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Hy field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEz
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[4, xf - (i + 1), j + ys, k + zs]
dEz = (Ez[xf - i, j + ys, k + zs] - Ez[xf - (i + 1), j + ys, k + zs]) / dx
Hy[xf - (i + 1), j + ys, k + zs] = Hy[xf - (i + 1), j + ys, k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, i] * RA[1, i] - 1) * dEz + RA[1, i] * RB[0, i] * HPhi[0, i, j, k] + RB[1, i] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, i] * HPhi[1, i, j, k] - RF[1, i] * (RA[0, i] * dEz + RB[0, i] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, i] * HPhi[0, i, j, k] - RF[0, i] * dEz
cpdef update_pml_2order_hy_zplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Ex, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Hy field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[4, i + xs, j + ys, k + zs]
dEx = (Ex[i + xs, j + ys, k + 1 + zs] - Ex[i + xs, j + ys, k + zs]) / dz
Hy[i + xs, j + ys, k + zs] = Hy[i + xs, j + ys, k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, k] * RA[1, k] - 1) * dEx + RA[1, k] * RB[0, k] * HPhi[0, i, j, k] + RB[1, k] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, k] * HPhi[1, i, j, k] - RF[1, k] * (RA[0, k] * dEx + RB[0, k] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, k] * HPhi[0, i, j, k] - RF[0, k] * dEx
cpdef update_pml_2order_hy_zminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hy, floattype_t[:, :, :] Ex, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dz):
"""This function updates the Hy field components in the z stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[4, i + xs, j + ys, zf - (k + 1)]
dEx = (Ex[i + xs, j + ys, zf - k] - Ex[i + xs, j + ys, zf - (k + 1)]) / dz
Hy[i + xs, j + ys, zf - (k + 1)] = Hy[i + xs, j + ys, zf - (k + 1)] - updatecoeffsH[listIndex, 4] * ((RA[0, k] * RA[1, k] - 1) * dEx + RA[1, k] * RB[0, k] * HPhi[0, i, j, k] + RB[1, k] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, k] * HPhi[1, i, j, k] - RF[1, k] * (RA[0, k] * dEx + RB[0, k] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, k] * HPhi[0, i, j, k] - RF[0, k] * dEx
#############################################
# Magnetic field PML updates - Hz component #
#############################################
cpdef update_pml_2order_hz_xplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hz, floattype_t[:, :, :] Ey, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Hz field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[5, i + xs, j + ys, k + zs]
dEy = (Ey[i + 1 + xs, j + ys, k + zs] - Ey[i + xs, j + ys, k + zs]) / dx
Hz[i + xs, j + ys, k + zs] = Hz[i + xs, j + ys, k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, i] * RA[1, i] - 1) * dEy + RA[1, i] * RB[0, i] * HPhi[0, i, j, k] + RB[1, i] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, i] * HPhi[1, i, j, k] - RF[1, i] * (RA[0, i] * dEy + RB[0, i] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, i] * HPhi[0, i, j, k] - RF[0, i] * dEy
cpdef update_pml_2order_hz_xminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hz, floattype_t[:, :, :] Ey, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dx):
"""This function updates the Hz field components in the x stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEy
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[5, xf - (i + 1), j + ys, k + zs]
dEy = (Ey[xf - i, j + ys, k + zs] - Ey[xf - (i + 1), j + ys, k + zs]) / dx
Hz[xf - (i + 1), j + ys, k + zs] = Hz[xf - (i + 1), j + ys, k + zs] - updatecoeffsH[listIndex, 4] * ((RA[0, i] * RA[1, i] - 1) * dEy + RA[1, i] * RB[0, i] * HPhi[0, i, j, k] + RB[1, i] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, i] * HPhi[1, i, j, k] - RF[1, i] * (RA[0, i] * dEy + RB[0, i] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, i] * HPhi[0, i, j, k] - RF[0, i] * dEy
cpdef update_pml_2order_hz_yplus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hz, floattype_t[:, :, :] Ex, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Hz field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[5, i + xs, j + ys, k + zs]
dEx = (Ex[i + xs, j + 1 + ys, k + zs] - Ex[i + xs, j + ys, k + zs]) / dy
Hz[i + xs, j + ys, k + zs] = Hz[i + xs, j + ys, k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, j] * RA[1, j] - 1) * dEx + RA[1, j] * RB[0, j] * HPhi[0, i, j, k] + RB[1, j] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, j] * HPhi[1, i, j, k] - RF[1, j] * (RA[0, j] * dEx + RB[0, j] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, j] * HPhi[0, i, j, k] - RF[0, j] * dEx
cpdef update_pml_2order_hz_yminus(int xs, int xf, int ys, int yf, int zs, int zf, int nthreads, floattype_t[:, :] updatecoeffsH, np.uint32_t[:, :, :, :] ID, floattype_t[:, :, :] Hz, floattype_t[:, :, :] Ex, floattype_t[:, :, :, :] HPhi, floattype_t[:, :] RA, floattype_t[:, :] RB, floattype_t[:, :] RE, floattype_t[:, :] RF, float dy):
"""This function updates the Hz field components in the y stretching direction.
Args:
xs, xf, ys, yf, zs, zf (int): Cell coordinates of entire box
nthreads (int): Number of threads to use
updatecoeffs, ID, E, H (memoryviews): Access to update coeffients, ID and field component arrays
EPhi, HPhi, RA, RB, RE, RF (memoryviews): Access to PML coefficient arrays
dx, dy, dz (float): Spatial discretisation
"""
cdef int i, j, k, nx, ny, nz, listIndex
cdef float dEx
nx = xf - xs
ny = yf - ys
nz = zf - zs
for i in prange(0, nx, nogil=True, schedule='static', chunksize=1, num_threads=nthreads):
for j in range(0, ny):
for k in range(0, nz):
listIndex = ID[5, i + xs, yf - (j + 1), k + zs]
dEx = (Ex[i + xs, yf - j, k + zs] - Ex[i + xs, yf - (j + 1), k + zs]) / dy
Hz[i + xs, yf - (j + 1), k + zs] = Hz[i + xs, yf - (j + 1), k + zs] + updatecoeffsH[listIndex, 4] * ((RA[0, j] * RA[1, j] - 1) * dEx + RA[1, j] * RB[0, j] * HPhi[0, i, j, k] + RB[1, j] * HPhi[1, i, j, k])
HPhi[1, i, j, k] = RE[1, j] * HPhi[1, i, j, k] - RF[1, j] * (RA[0, j] * dEx + RB[0, j] * HPhi[0, i, j, k])
HPhi[0, i, j, k] = RE[0, j] * HPhi[0, i, j, k] - RF[0, j] * dEx

118
gprMax/pml_call_updates.py 普通文件
查看文件

@@ -0,0 +1,118 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
from .pml_1order_update import *
from .pml_2order_update import *
def update_pml_electric(G):
"""This functions updates electric field components with the PML correction."""
for pml in G.pmls:
if pml.direction == 'xminus':
if len(pml.CFS) == 1:
update_pml_1order_ey_xminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ey, G.Hz, pml.EPhiyxz, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dx)
update_pml_1order_ez_xminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ez, G.Hy, pml.EPhizxy, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dx)
elif len(pml.CFS) == 2:
update_pml_2order_ey_xminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ey, G.Hz, pml.EPhiyxz, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dx)
update_pml_2order_ez_xminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ez, G.Hy, pml.EPhizxy, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dx)
elif pml.direction == 'xplus':
if len(pml.CFS) == 1:
update_pml_1order_ey_xplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ey, G.Hz, pml.EPhiyxz, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dx)
update_pml_1order_ez_xplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ez, G.Hy, pml.EPhizxy, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dx)
elif len(pml.CFS) == 2:
update_pml_2order_ey_xplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ey, G.Hz, pml.EPhiyxz, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dx)
update_pml_2order_ez_xplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ez, G.Hy, pml.EPhizxy, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dx)
elif pml.direction == 'yminus':
if len(pml.CFS) == 1:
update_pml_1order_ex_yminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ex, G.Hz, pml.EPhixyz, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dy)
update_pml_1order_ez_yminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ez, G.Hx, pml.EPhizyx, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dy)
elif len(pml.CFS) == 2:
update_pml_2order_ex_yminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ex, G.Hz, pml.EPhixyz, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dy)
update_pml_2order_ez_yminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ez, G.Hx, pml.EPhizyx, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dy)
elif pml.direction == 'yplus':
if len(pml.CFS) == 1:
update_pml_1order_ex_yplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ex, G.Hz, pml.EPhixyz, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dy)
update_pml_1order_ez_yplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ez, G.Hx, pml.EPhizyx, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dy)
elif len(pml.CFS) == 2:
update_pml_2order_ex_yplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ex, G.Hz, pml.EPhixyz, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dy)
update_pml_2order_ez_yplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ez, G.Hx, pml.EPhizyx, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dy)
elif pml.direction == 'zminus':
if len(pml.CFS) == 1:
update_pml_1order_ex_zminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ex, G.Hy, pml.EPhixzy, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dz)
update_pml_1order_ey_zminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ey, G.Hx, pml.EPhiyzx, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dz)
elif len(pml.CFS) == 2:
update_pml_2order_ex_zminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ex, G.Hy, pml.EPhixzy, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dz)
update_pml_2order_ey_zminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ey, G.Hx, pml.EPhiyzx, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dz)
elif pml.direction == 'zplus':
if len(pml.CFS) == 1:
update_pml_1order_ex_zplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ex, G.Hy, pml.EPhixzy, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dz)
update_pml_1order_ey_zplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ey, G.Hx, pml.EPhiyzx, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dz)
elif len(pml.CFS) == 2:
update_pml_2order_ex_zplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ex, G.Hy, pml.EPhixzy, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dz)
update_pml_2order_ey_zplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsE, G.ID, G.Ey, G.Hx, pml.EPhiyzx, pml.ERA, pml.ERB, pml.ERE, pml.ERF, G.dz)
def update_pml_magnetic(G):
"""This functions updates magnetic field components with the PML correction."""
for pml in G.pmls:
if pml.direction == 'xminus':
if len(pml.CFS) == 1:
update_pml_1order_hy_xminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hy, G.Ez, pml.HPhiyxz, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dx)
update_pml_1order_hz_xminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hz, G.Ey, pml.HPhizxy, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dx)
elif len(pml.CFS) == 2:
update_pml_2order_hy_xminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hy, G.Ez, pml.HPhiyxz, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dx)
update_pml_2order_hz_xminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hz, G.Ey, pml.HPhizxy, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dx)
elif pml.direction == 'xplus':
if len(pml.CFS) == 1:
update_pml_1order_hy_xplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hy, G.Ez, pml.HPhiyxz, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dx)
update_pml_1order_hz_xplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hz, G.Ey, pml.HPhizxy, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dx)
elif len(pml.CFS) == 2:
update_pml_2order_hy_xplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hy, G.Ez, pml.HPhiyxz, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dx)
update_pml_2order_hz_xplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hz, G.Ey, pml.HPhizxy, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dx)
elif pml.direction == 'yminus':
if len(pml.CFS) == 1:
update_pml_1order_hx_yminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hx, G.Ez, pml.HPhixyz, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dy)
update_pml_1order_hz_yminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hz, G.Ex, pml.HPhizyx, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dy)
elif len(pml.CFS) == 2:
update_pml_2order_hx_yminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hx, G.Ez, pml.HPhixyz, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dy)
update_pml_2order_hz_yminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hz, G.Ex, pml.HPhizyx, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dy)
elif pml.direction == 'yplus':
if len(pml.CFS) == 1:
update_pml_1order_hx_yplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hx, G.Ez, pml.HPhixyz, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dy)
update_pml_1order_hz_yplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hz, G.Ex, pml.HPhizyx, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dy)
elif len(pml.CFS) == 2:
update_pml_2order_hx_yplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hx, G.Ez, pml.HPhixyz, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dy)
update_pml_2order_hz_yplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hz, G.Ex, pml.HPhizyx, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dy)
elif pml.direction == 'zminus':
if len(pml.CFS) == 1:
update_pml_1order_hx_zminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hx, G.Ey, pml.HPhixzy, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dz)
update_pml_1order_hy_zminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hy, G.Ex, pml.HPhiyzx, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dz)
elif len(pml.CFS) == 2:
update_pml_2order_hx_zminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hx, G.Ey, pml.HPhixzy, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dz)
update_pml_2order_hy_zminus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hy, G.Ex, pml.HPhiyzx, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dz)
elif pml.direction == 'zplus':
if len(pml.CFS) == 1:
update_pml_1order_hx_zplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hx, G.Ey, pml.HPhixzy, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dz)
update_pml_1order_hy_zplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hy, G.Ex, pml.HPhiyzx, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dz)
elif len(pml.CFS) == 2:
update_pml_2order_hx_zplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hx, G.Ey, pml.HPhixzy, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dz)
update_pml_2order_hy_zplus(pml.xs, pml.xf, pml.ys, pml.yf, pml.zs, pml.zf, G.nthreads, G.updatecoeffsH, G.ID, G.Hy, G.Ex, pml.HPhiyzx, pml.HRA, pml.HRB, pml.HRE, pml.HRF, G.dz)

31
gprMax/receivers.py 普通文件
查看文件

@@ -0,0 +1,31 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
class Rx:
"""Receiever output points."""
def __init__(self, positionx=None, positiony=None, positionz=None):
"""
Args:
positionx (float): x-coordinate of location in model.
positiony (float): y-coordinate of location in model.
positionz (float): z-coordinate of location in model.
"""
self.positionx = positionx
self.positiony = positiony
self.positionz = positionz

144
gprMax/snapshots.py 普通文件
查看文件

@@ -0,0 +1,144 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import sys
import numpy as np
from struct import pack
from .constants import floattype
from .utilities import rvalue
class Snapshot:
"""Snapshots of the electric and magnetic field values."""
# Set string for byte order
if sys.byteorder == 'little':
byteorder = 'LittleEndian'
else:
byteorder = 'BigEndian'
# Set format text and string depending on float type
if np.dtype(floattype).name == 'float32':
floatname = 'Float32'
floatstring = 'f'
elif np.dtype(floattype).name == '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):
"""
Args:
xs, xf, ys, yf, zs, zf (float): Extent of the volume.
dx, dy, dz (float): Spatial discretisation.
time (int): Iteration number to take the snapshot on.
filename (str): Filename to save to.
"""
self.xs = xs
self.ys = ys
self.zs = zs
self.xf = xf
self.yf = yf
self.zf = zf
self.dx = dx
self.dy = dy
self.dz = dz
self.time = time
self.filename = filename
def prepare_file(self, modelrun, numbermodelruns, G):
"""Prepares a VTK ImageData (.vti) file for a snapshot.
Args:
modelrun (int): Current model run number.
numbermodelruns (int): Total number of model runs.
G (class): Grid class instance - holds essential parameters describing the model.
"""
# No Python 3 support for VTK at time of writing (03/2015)
self.vtk_nx = self.xf - self.xs
self.vtk_ny = self.yf - self.ys
self.vtk_nz = self.zf - self.zs
# Construct filename from user-supplied name and model run number
if numbermodelruns == 1:
self.filename = G.inputdirectory + self.filename + '.vti'
else:
self.filename = G.inputdirectory + self.filename + '_' + str(modelrun) + '.vti'
# Calculate number of cells according to requested sampling
self.vtk_xscells = rvalue(self.xs / self.dx)
self.vtk_xfcells = rvalue(self.xf / self.dx)
self.vtk_yscells = rvalue(self.ys / self.dy)
self.vtk_yfcells = rvalue(self.yf / self.dz)
self.vtk_zscells = rvalue(self.zs / self.dz)
self.vtk_zfcells = rvalue(self.zf / self.dz)
vtk_hfield_offset = 3 * np.dtype(floattype).itemsize * (self.vtk_xfcells - self.vtk_xscells) * (self.vtk_yfcells - self.vtk_yscells) * (self.vtk_zfcells - self.vtk_zscells) + np.dtype(np.uint32).itemsize
# vtk_current_offset = 2 * vtk_hfield_offset
self.filehandle = open(self.filename, 'wb')
self.filehandle.write('<?xml version="1.0"?>\n'.encode('utf-8'))
self.filehandle.write('<VTKFile type="ImageData" version="1.0" byte_order="{}">\n'.format(Snapshot.byteorder).encode('utf-8'))
self.filehandle.write('<ImageData WholeExtent="{} {} {} {} {} {}" Origin="0 0 0" Spacing="{:.3} {:.3} {:.3}">\n'.format(self.vtk_xscells, self.vtk_xfcells, self.vtk_yscells, self.vtk_yfcells, self.vtk_zscells, self.vtk_zfcells, self.dx * G.dx, self.dy * G.dy, self.dz * G.dz).encode('utf-8'))
self.filehandle.write('<Piece Extent="{} {} {} {} {} {}">\n'.format(self.vtk_xscells, self.vtk_xfcells, self.vtk_yscells, self.vtk_yfcells, self.vtk_zscells, self.vtk_zfcells).encode('utf-8'))
self.filehandle.write('<CellData Vectors="E-field H-field">\n'.encode('utf-8'))
# self.filehandle.write('<CellData Vectors="E-field H-field Current">\n'.encode('utf-8'))
self.filehandle.write('<DataArray type="{}" Name="E-field" NumberOfComponents="3" format="appended" offset="0" />\n'.format(Snapshot.floatname).encode('utf-8'))
self.filehandle.write('<DataArray type="{}" Name="H-field" NumberOfComponents="3" format="appended" offset="{}" />\n'.format(Snapshot.floatname, vtk_hfield_offset).encode('utf-8'))
# self.filehandle.write('<DataArray type="{}" Name="Current" NumberOfComponents="3" format="appended" offset="{}" />\n'.format(Snapshot.floatname, vtk_current_offset).encode('utf-8'))
self.filehandle.write('</CellData>\n</Piece>\n</ImageData>\n<AppendedData encoding="raw">\n_'.encode('utf-8'))
def write_snapshot(self, Ex, Ey, Ez, Hx, Hy, Hz, G):
"""Writes electric and magnetic field values to VTK ImageData (.vti) file.
Args:
Ex, Ey, Ez, Hx, Hy, Hz (memory view): Electric and magnetic field values.
G (class): Grid class instance - holds essential parameters describing the model.
"""
datasize = 3 * np.dtype(floattype).itemsize * (self.vtk_xfcells - self.vtk_xscells) * (self.vtk_yfcells - self.vtk_yscells) * (self.vtk_zfcells - self.vtk_zscells)
# Write number of bytes of appended data as UInt32
self.filehandle.write(pack('I', datasize))
for k in range(self.zs, self.zf, self.dz):
for j in range(self.ys, self.yf, self.dy):
for i in range(self.xs, self.xf, self.dx):
# The electric field component value at a point comes from average of the 4 electric field component values in that cell
self.filehandle.write(pack(Snapshot.floatstring, (Ex[i, j, k] + Ex[i, j + 1, k] + Ex[i, j, k + 1] + Ex[i, j + 1, k + 1]) / 4))
self.filehandle.write(pack(Snapshot.floatstring, (Ey[i, j, k] + Ey[i + 1, j, k] + Ey[i, j, k + 1] + Ey[i + 1, j, k + 1]) / 4))
self.filehandle.write(pack(Snapshot.floatstring, (Ez[i, j, k] + Ez[i + 1, j, k] + Ez[i, j + 1, k] + Ez[i + 1, j + 1, k]) / 4))
self.filehandle.write(pack('I', datasize))
for k in range(self.zs, self.zf, self.dz):
for j in range(self.ys, self.yf, self.dy):
for i in range(self.xs, self.xf, self.dx):
# The magnetic field component value at a point comes from average of 2 magnetic field component values in that cell and the following cell
self.filehandle.write(pack(Snapshot.floatstring, (Hx[i, j, k] + Hx[i + 1, j, k]) / 2))
self.filehandle.write(pack(Snapshot.floatstring, (Hy[i, j, k] + Hy[i, j + 1, k]) / 2))
self.filehandle.write(pack(Snapshot.floatstring, (Hz[i, j, k] + Hz[i, j, k + 1]) / 2))
# self.filehandle.write(pack('I', datasize))
# for k in range(self.zs, self.zf, self.dz):
# for j in range(self.ys, self.yf, self.dy):
# for i in range(self.xs, self.xf, self.dx):
# self.filehandle.write(pack(Snapshot.floatstring, Ix[i, j, k]))
# self.filehandle.write(pack(Snapshot.floatstring, Iy[i, j, k]))
# self.filehandle.write(pack(Snapshot.floatstring, Iz[i, j, k]))
self.filehandle.write('\n</AppendedData>\n</VTKFile>'.encode('utf-8'))
self.filehandle.close()

158
gprMax/sources.py 普通文件
查看文件

@@ -0,0 +1,158 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
from .utilities import rvalue
class VoltageSource:
"""Voltage sources."""
def __init__(self):
self.polarisation = None
self.positionx = None
self.positiony = None
self.positionz = None
self.start = None
self.stop = None
self.resistance = None
self.waveformID = None
def update_fields(self, abstime, timestep, updatecoeffsE, ID, Ex, Ey, Ez, G):
"""Updates electric field values for a voltage source.
Args:
abstime (float): Absolute time.
timestep (int): Iteration number.
updatecoeffsE (memory view): numpy array of electric field update coefficients.
ID (memory view): numpy array of numeric IDs corresponding to materials in the model.
Ex, Ey, Ez (memory view): numpy array of electric field values.
G (class): Grid class instance - holds essential parameters describing the model.
"""
if abstime >= self.start and abstime <= self.stop:
# Set the time of the waveform evaluation to account for any delay in the start
time = abstime - self.start
i = self.positionx
j = self.positiony
k = self.positionz
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
if self.polarisation is 'x':
if self.resistance != 0:
Ex[i, j, k] -= updatecoeffsE[ID[0, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (self.resistance * G.dy * G.dz))
else:
Ex[i, j, k] = -1 * waveform.amp * waveform.calculate_value(time, G.dt) / G.dx
elif self.polarisation is 'y':
if self.resistance != 0:
Ey[i, j, k] -= updatecoeffsE[ID[1, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (self.resistance * G.dx * G.dz))
else:
Ey[i, j, k] = -1 * waveform.amp * waveform.calculate_value(time, G.dt) / G.dy
elif self.polarisation is 'z':
if self.resistance != 0:
Ez[i, j, k] -= updatecoeffsE[ID[2, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (self.resistance * G.dx * G.dy))
else:
Ez[i, j, k] = -1 * waveform.amp * waveform.calculate_value(time, G.dt) / G.dz
class HertzianDipole:
"""Hertzian dipoles, i.e. normal additive source (current density)."""
def __init__(self):
self.polarisation = None
self.positionx = None
self.positiony = None
self.positionz = None
self.start = None
self.stop = None
self.waveformID = None
def update_fields(self, abstime, timestep, updatecoeffsE, ID, Ex, Ey, Ez, G):
"""Updates electric field values for a Hertzian dipole.
Args:
abstime (float): Absolute time.
timestep (int): Iteration number.
updatecoeffsE (memory view): numpy array of electric field update coefficients.
ID (memory view): numpy array of numeric IDs corresponding to materials in the model.
Ex, Ey, Ez (memory view): numpy array of electric field values.
G (class): Grid class instance - holds essential parameters describing the model.
"""
if abstime >= self.start and abstime <= self.stop:
# Set the time of the waveform evaluation to account for any delay in the start
time = abstime - self.start
i = self.positionx
j = self.positiony
k = self.positionz
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
if self.polarisation is 'x':
Ex[i, j, k] -= updatecoeffsE[ID[0, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (G.dy * G.dz))
elif self.polarisation is 'y':
Ey[i, j, k] -= updatecoeffsE[ID[1, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (G.dx * G.dz))
elif self.polarisation is 'z':
Ez[i, j, k] -= updatecoeffsE[ID[2, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (G.dx * G.dy))
class MagneticDipole:
"""Magnetic dipoles, i.e. current on a small loop."""
def __init__(self):
self.polarisation = None
self.positionx = None
self.positiony = None
self.positionz = None
self.start = None
self.stop = None
self.waveformID = None
def update_fields(self, abstime, timestep, updatecoeffsH, ID, Hx, Hy, Hz, G):
"""Updates electric field values for a magnetic dipole.
Args:
abstime (float): Absolute time.
timestep (int): Iteration number.
updatecoeffsH (memory view): numpy array of magnetic field update coefficients.
ID (memory view): numpy array of numeric IDs corresponding to materials in the model.
Hx, Hy, Hz (memory view): numpy array of magnetic field values.
G (class): Grid class instance - holds essential parameters describing the model.
"""
if abstime >= self.start and abstime <= self.stop:
# Set the time of the waveform evaluation to account for any delay in the start
time = abstime - self.start
i = self.positionx
j = self.positiony
k = self.positionz
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
if self.polarisation is 'x':
Hx[i, j, k] -= waveform.amp * waveform.calculate_value(time, G.dt) * (G.dt / (G.dx * G.dy * G.dz))
elif self.polarisation is 'y':
Hy[i, j, k] -= waveform.amp * waveform.calculate_value(time, G.dt) * (G.dt / (G.dx * G.dy * G.dz))
elif self.polarisation is 'z':
Hz[i, j, k] -= waveform.amp * waveform.calculate_value(time, G.dt) * (G.dt / (G.dx * G.dy * G.dz))

119
gprMax/utilities.py 普通文件
查看文件

@@ -0,0 +1,119 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import sys
import decimal as d
from pyfiglet import Figlet
class ListStream:
"""A list can be streamed into. Required when temporarily redirecting stdio to capture output from users Python code blocks."""
def __init__(self):
self.data = []
def write(self, s):
self.data.append(s)
def logo(version):
"""Print gprMax logo, version, and licencing/copyright information.
Args:
version (str): Version number.
"""
licenseinfo = """
Copyright (C) 2015: The University of Edinburgh
Authors: Craig Warren and Antonis Giannopoulos
gprMax is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
gprMax is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gprMax. If not, see <http://www.gnu.org/licenses/>."""
width = 65
url = 'www.gprmax.com'
print('\n{} {} {}'.format('*'*round((width - len(url))/2), url, '*'*round((width - len(url))/2)))
gprMaxlogo = Figlet(font='standard', width=width, justify='center')
print('{}'.format(gprMaxlogo.renderText('gprMax')))
print('{} v{} {}'.format('*'*round((width - len(version))/2), (version), '*'*round((width - len(version))/2)))
print(licenseinfo)
print('\n{}\n'.format('*'*(width+3)))
def update_progress(progress):
"""Displays or updates a console progress bar.
Args:
progress (float): Number between zero and one to signify progress.
"""
# Modify this to change the length of the progress bar
barLength = 50
block = rvalue(barLength * progress)
text = '\r|{}| {:2.1f}%'.format( '#' * block + '-' * (barLength - block), progress * 100)
sys.stdout.write(text)
sys.stdout.flush()
def rvalue(value):
"""Rounds half values downward.
Args:
value (float): Number to round.
Returns:
Rounded value (float).
"""
return int(d.Decimal(value).quantize(d.Decimal('1'),rounding=d.ROUND_HALF_DOWN))
def human_size(size, a_kilobyte_is_1024_bytes=True):
"""Convert a file size to human-readable form.
Args:
size (int): file size in bytes
a_kilobyte_is_1024_bytes (boolean) - true for multiples of 1024, false for multiples of 1000
Returns:
Human-readable (string).
"""
suffixes = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
if size < 0:
raise ValueError('Number must be non-negative.')
multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
for suffix in suffixes[multiple]:
size /= multiple
if size < multiple:
return '{0:.1f} {1}'.format(size, suffix)
raise ValueError('Number is too large.')

102
gprMax/waveforms.py 普通文件
查看文件

@@ -0,0 +1,102 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
from .utilities import rvalue
class Waveform:
"""Definitions of waveform shapes that can be used with sources."""
waveformtypes = ['gaussian', 'gaussiandot', 'gaussiandotdot', 'ricker', 'sine', 'contsine', 'impulse', 'user']
def __init__(self):
self.ID = None
self.type = None
self.amp = 1
self.freq = 0
self.uservalues = None
def calculate_value(self, time, dt):
"""Calculates value of the waveform at a specific time.
Args:
time (float): Absolute time.
dt (float): Absolute time discretisation.
Returns:
waveform (float): Calculated value for waveform.
"""
chi = 1 / self.freq
zeta = 2 * np.pi * np.pi * self.freq * self.freq
delay = time - chi
if self.type == 'gaussian':
waveform = np.exp(-zeta * delay * delay)
elif self.type == 'gaussiandot':
waveform = -2 * zeta * delay * np.exp(-zeta * delay * delay)
elif self.type == 'gaussiandotnorm':
normalise = np.sqrt(np.exp(1) / (2 * zeta))
waveform = -2 * zeta * delay * np.exp(-zeta * delay * delay) * normalise
elif self.type == 'gaussiandotdot':
waveform = 2 * zeta * (2 * zeta * delay * delay - 1) * np.exp(-zeta * delay * delay)
elif self.type == 'gaussiandotdotnorm':
normalise = 1 / (2 * zeta)
waveform = 2 * zeta * (2 * zeta * delay * delay - 1) * np.exp(-zeta * delay * delay) * normalise
elif self.type == 'gaussiandotdotdot':
waveform = zeta * zeta * (3 * delay - 2 * zeta * delay * delay * delay) * np.exp(-zeta * delay * delay)
elif self.type == 'ricker':
normalise = 1 / (2 * zeta)
waveform = - (2 * zeta * (2 * zeta * delay * delay - 1) * np.exp(-zeta * delay * delay)) * normalise
elif self.type == 'sine':
waveform = np.sin(2 * np.pi * self.freq * time)
if time * self.freq > 1:
waveform = 0
elif self.type == 'contsine':
rampamp = 0.25
ramp = rampamp * time * self.freq
if ramp > 1:
ramp = 1
waveform = ramp * np.sin(2 * np.pi * self.freq * time)
elif self.type == 'impulse':
# time < G.dt condition required to do impulsive magnetic dipole
if time == 0 or time < dt:
waveform = 1
elif time >= dt:
waveform = 0
elif self.type == 'user':
index = rvalue(time / dt)
# Check to see if there are still user specified values and if not use zero
if index > len(self.uservalues) - 1:
waveform = 0
else:
waveform = self.uservalues[index]
return waveform

332
gprMax/yee_cell_build.pyx 普通文件
查看文件

@@ -0,0 +1,332 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
from .materials import Material
from .yee_cell_setget_rigid cimport get_rigid_Ex, get_rigid_Ey, get_rigid_Ez, get_rigid_Hx, get_rigid_Hy, get_rigid_Hz
cpdef build_ex_component(np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidE, np.uint32_t[:, :, :, :] ID, G):
"""This function builds the Ex components in the ID array.
Args:
solid, rigid, ID (memoryviews): Access to solid, rigid and ID arrays
"""
cdef int i, j, k, numID1, numID2, numID3, numID4
for i in range(0, G.nx):
for j in range(1, G.ny):
for k in range(1, G.nz):
# If rigid is True do not average
if get_rigid_Ex(i, j, k, rigidE):
pass
else:
numID1 = solid[i, j, k]
numID2 = solid[i, j - 1, k]
numID3 = solid[i, j - 1, k - 1]
numID4 = solid[i, j, k - 1]
# If all values are the same no need to average
if numID1 == numID2 and numID1 == numID3 and numID1 == numID4:
ID[0, i, j, k] = numID1
else:
# Averaging is required
# Make an ID composed of the names of the four materials that will be averaged
requiredID = G.materials[numID1].ID + '|' + G.materials[numID2].ID + '|' + G.materials[numID3].ID + '|' + G.materials[numID4].ID
# Check if this material already exists
tmp = requiredID.split('|')
material = [x for x in G.materials if
x.ID.count(tmp[0]) == requiredID.count(tmp[0]) and
x.ID.count(tmp[1]) == requiredID.count(tmp[1]) and
x.ID.count(tmp[2]) == requiredID.count(tmp[2]) and
x.ID.count(tmp[3]) == requiredID.count(tmp[3])]
if material:
ID[0, i, j, k] = material[0].numID
else:
# Create new material
newNumID = len(G.materials)
m = Material(newNumID, requiredID, G)
# Create averaged constituents for material
m.er = np.mean((G.materials[numID1].er, G.materials[numID2].er, G.materials[numID3].er, G.materials[numID4].er), axis=0)
m.se = np.mean((G.materials[numID1].se, G.materials[numID2].se, G.materials[numID3].se, G.materials[numID4].se), axis=0)
m.mr = np.mean((G.materials[numID1].mr, G.materials[numID2].mr, G.materials[numID3].mr, G.materials[numID4].mr), axis=0)
m.sm = np.mean((G.materials[numID1].sm, G.materials[numID2].sm, G.materials[numID3].sm, G.materials[numID4].sm), axis=0)
# Append the new material object to the materials list
G.materials.append(m)
ID[0, i, j, k] = newNumID
cpdef build_ey_component(np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidE, np.uint32_t[:, :, :, :] ID, G):
"""This function builds the Ey components in the ID array.
Args:
solid, rigid, ID (memoryviews): Access to solid, rigid and ID arrays
"""
cdef int i, j, k, numID1, numID2, numID3, numID4
for i in range(1, G.nx):
for j in range(0, G.ny):
for k in range(1, G.nz):
# If rigid is True do not average
if get_rigid_Ey(i, j, k, rigidE):
pass
else:
numID1 = solid[i, j, k]
numID2 = solid[i - 1, j, k]
numID3 = solid[i - 1, j, k - 1]
numID4 = solid[i, j, k - 1]
# If all values are the same no need to average
if numID1 == numID2 and numID1 == numID3 and numID1 == numID4:
ID[1, i, j, k] = numID1
else:
# Averaging is required
# Make an ID composed of the names of the four materials that will be averaged
requiredID = G.materials[numID1].ID + '|' + G.materials[numID2].ID + '|' + G.materials[numID3].ID + '|' + G.materials[numID4].ID
# Check if this material already exists
tmp = requiredID.split('|')
material = [x for x in G.materials if
x.ID.count(tmp[0]) == requiredID.count(tmp[0]) and
x.ID.count(tmp[1]) == requiredID.count(tmp[1]) and
x.ID.count(tmp[2]) == requiredID.count(tmp[2]) and
x.ID.count(tmp[3]) == requiredID.count(tmp[3])]
if material:
ID[1, i, j, k] = material[0].numID
else:
# Create new material
newNumID = len(G.materials)
m = Material(newNumID, requiredID, G)
# Create averaged constituents for material
m.er = np.mean((G.materials[numID1].er, G.materials[numID2].er, G.materials[numID3].er, G.materials[numID4].er), axis=0)
m.se = np.mean((G.materials[numID1].se, G.materials[numID2].se, G.materials[numID3].se, G.materials[numID4].se), axis=0)
m.mr = np.mean((G.materials[numID1].mr, G.materials[numID2].mr, G.materials[numID3].mr, G.materials[numID4].mr), axis=0)
m.sm = np.mean((G.materials[numID1].sm, G.materials[numID2].sm, G.materials[numID3].sm, G.materials[numID4].sm), axis=0)
# Append the new material object to the materials list
G.materials.append(m)
ID[1, i, j, k] = newNumID
cpdef build_ez_component(np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidE, np.uint32_t[:, :, :, :] ID, G):
"""This function builds the Ez components in the ID array.
Args:
solid, rigid, ID (memoryviews): Access to solid, rigid and ID arrays
"""
cdef int i, j, k, numID1, numID2, numID3, numID4
for i in range(1, G.nx):
for j in range(1, G.ny):
for k in range(0, G.nz):
# If rigid is True do not average
if get_rigid_Ez(i, j, k, rigidE):
pass
else:
numID1 = solid[i, j, k]
numID2 = solid[i - 1, j, k]
numID3 = solid[i - 1, j - 1, k]
numID4 = solid[i, j - 1, k]
# If all values are the same no need to average
if numID1 == numID2 and numID1 == numID3 and numID1 == numID4:
ID[2, i, j, k] = numID1
else:
# Averaging is required
# Make an ID composed of the names of the four materials that will be averaged
requiredID = G.materials[numID1].ID + '|' + G.materials[numID2].ID + '|' + G.materials[numID3].ID + '|' + G.materials[numID4].ID
# Check if this material already exists
tmp = requiredID.split('|')
material = [x for x in G.materials if
x.ID.count(tmp[0]) == requiredID.count(tmp[0]) and
x.ID.count(tmp[1]) == requiredID.count(tmp[1]) and
x.ID.count(tmp[2]) == requiredID.count(tmp[2]) and
x.ID.count(tmp[3]) == requiredID.count(tmp[3])]
if material:
ID[2, i, j, k] = material[0].numID
else:
# Create new material
newNumID = len(G.materials)
m = Material(newNumID, requiredID, G)
# Create averaged constituents for material
m.er = np.mean((G.materials[numID1].er, G.materials[numID2].er, G.materials[numID3].er, G.materials[numID4].er), axis=0)
m.se = np.mean((G.materials[numID1].se, G.materials[numID2].se, G.materials[numID3].se, G.materials[numID4].se), axis=0)
m.mr = np.mean((G.materials[numID1].mr, G.materials[numID2].mr, G.materials[numID3].mr, G.materials[numID4].mr), axis=0)
m.sm = np.mean((G.materials[numID1].sm, G.materials[numID2].sm, G.materials[numID3].sm, G.materials[numID4].sm), axis=0)
# Append the new material object to the materials list
G.materials.append(m)
ID[2, i, j, k] = newNumID
cpdef build_hx_component(np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID, G):
"""This function builds the Hx components in the ID array.
Args:
solid, rigid, ID (memoryviews): Access to solid, rigid and ID arrays
"""
cdef int i, j, k, numID1, numID2
for i in range(1, G.nx):
for j in range(0, G.ny):
for k in range(0, G.nz):
# If rigid is True do not average
if get_rigid_Hx(i, j, k, rigidH):
pass
else:
numID1 = solid[i, j, k]
numID2 = solid[i - 1, j, k]
# If all values are the same no need to average
if numID1 == numID2:
ID[3, i, j, k] = numID1
else:
# Averaging is required
# Make an ID composed of the names of the four materials that will be averaged
requiredID = G.materials[numID1].ID + '|' + G.materials[numID2].ID
# Check if this material already exists
tmp = requiredID.split('|')
material = [x for x in G.materials if
(x.ID.count(tmp[0]) == requiredID.count(tmp[0]) and
x.ID.count(tmp[1]) == requiredID.count(tmp[1])) or
(x.ID.count(tmp[0]) % 2 == 0 and x.ID.count(tmp[1]) % 2 == 0)]
if material:
ID[3, i, j, k] = material[0].numID
else:
# Create new material
newNumID = len(G.materials)
m = Material(newNumID, requiredID, G)
# Create averaged constituents for material
m.er = np.mean((G.materials[numID1].er, G.materials[numID2].er), axis=0)
m.se = np.mean((G.materials[numID1].se, G.materials[numID2].se), axis=0)
m.mr = np.mean((G.materials[numID1].mr, G.materials[numID2].mr), axis=0)
m.sm = np.mean((G.materials[numID1].sm, G.materials[numID2].sm), axis=0)
# Append the new material object to the materials list
G.materials.append(m)
ID[3, i, j, k] = newNumID
cpdef build_hy_component(np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID, G):
"""This function builds the Hy components in the ID array.
Args:
solid, rigid, ID (memoryviews): Access to solid, rigid and ID arrays
"""
cdef int i, j, k, numID1, numID2
for i in range(0, G.nx):
for j in range(1, G.ny):
for k in range(0, G.nz):
# If rigid is True do not average
if get_rigid_Hy(i, j, k, rigidH):
pass
else:
numID1 = solid[i, j, k]
numID2 = solid[i, j - 1, k]
# If all values are the same no need to average
if numID1 == numID2:
ID[4, i, j, k] = numID1
else:
# Averaging is required
# Make an ID composed of the names of the four materials that will be averaged
requiredID = G.materials[numID1].ID + '|' + G.materials[numID2].ID
# Check if this material already exists
tmp = requiredID.split('|')
material = [x for x in G.materials if
(x.ID.count(tmp[0]) == requiredID.count(tmp[0]) and
x.ID.count(tmp[1]) == requiredID.count(tmp[1])) or
(x.ID.count(tmp[0]) % 2 == 0 and x.ID.count(tmp[1]) % 2 == 0)]
if material:
ID[4, i, j, k] = material[0].numID
else:
# Create new material
newNumID = len(G.materials)
m = Material(newNumID, requiredID, G)
# Create averaged constituents for material
m.er = np.mean((G.materials[numID1].er, G.materials[numID2].er), axis=0)
m.se = np.mean((G.materials[numID1].se, G.materials[numID2].se), axis=0)
m.mr = np.mean((G.materials[numID1].mr, G.materials[numID2].mr), axis=0)
m.sm = np.mean((G.materials[numID1].sm, G.materials[numID2].sm), axis=0)
# Append the new material object to the materials list
G.materials.append(m)
ID[4, i, j, k] = newNumID
cpdef build_hz_component(np.uint32_t[:, :, :] solid, np.int8_t[:, :, :, :] rigidH, np.uint32_t[:, :, :, :] ID, G):
"""This function builds the Hz components in the ID array.
Args:
solid, rigid, ID (memoryviews): Access to solid, rigid and ID arrays
"""
cdef int i, j, k, numID1, numID2
for i in range(0, G.nx):
for j in range(0, G.ny):
for k in range(1, G.nz):
# If rigid is True do not average
if get_rigid_Hz(i, j, k, rigidH):
pass
else:
numID1 = solid[i, j, k]
numID2 = solid[i, j, k - 1]
# If all values are the same no need to average
if numID1 == numID2:
ID[5, i, j, k] = numID1
else:
# Averaging is required
# Make an ID composed of the names of the four materials that will be averaged
requiredID = G.materials[numID1].ID + '|' + G.materials[numID2].ID
# Check if this material already exists
tmp = requiredID.split('|')
material = [x for x in G.materials if
(x.ID.count(tmp[0]) == requiredID.count(tmp[0]) and
x.ID.count(tmp[1]) == requiredID.count(tmp[1])) or
(x.ID.count(tmp[0]) % 2 == 0 and x.ID.count(tmp[1]) % 2 == 0)]
if material:
ID[5, i, j, k] = material[0].numID
else:
# Create new material
newNumID = len(G.materials)
m = Material(newNumID, requiredID, G)
# Create averaged constituents for material
m.er = np.mean((G.materials[numID1].er, G.materials[numID2].er), axis=0)
m.se = np.mean((G.materials[numID1].se, G.materials[numID2].se), axis=0)
m.mr = np.mean((G.materials[numID1].mr, G.materials[numID2].mr), axis=0)
m.sm = np.mean((G.materials[numID1].sm, G.materials[numID2].sm), axis=0)
# Append the new material object to the materials list
G.materials.append(m)
ID[5, i, j, k] = newNumID

查看文件

@@ -0,0 +1,44 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
# Get and set functions for the rigid electric component array. The rigid array is 4D with the 1st dimension holding
# the 12 electric edge components of a cell - Ex1, Ex2, Ex3, Ex4, Ey1, Ey2, Ey3, Ey4, Ez1, Ez2, Ez3, Ez4
cdef bint get_rigid_Ex(int i, int j, int k, np.int8_t[:, :, :, :] rigidE)
cdef bint get_rigid_Ey(int i, int j, int k, np.int8_t[:, :, :, :] rigidE)
cdef bint get_rigid_Ez(int i, int j, int k, np.int8_t[:, :, :, :] rigidE)
cdef void set_rigid_Ex(int i, int j, int k, np.int8_t[:, :, :, :] rigidE)
cdef void set_rigid_Ey(int i, int j, int k, np.int8_t[:, :, :, :] rigidE)
cdef void set_rigid_Ez(int i, int j, int k, np.int8_t[:, :, :, :] rigidE)
cdef void set_rigid_E(int i, int j, int k, np.int8_t[:, :, :, :] rigidE)
cdef void unset_rigid_E(int i, int j, int k, np.int8_t[:, :, :, :] rigidE)
# Get and set functions for the rigid magnetic component array. The rigid array is 4D with the 1st dimension holding
# the 6 magnetic edge components - Hx1, Hx2, Hy1, Hy2, Hz1, Hz2
cdef bint get_rigid_Hx(int i, int j, int k, np.int8_t[:, :, :, :] rigidH)
cdef bint get_rigid_Hy(int i, int j, int k, np.int8_t[:, :, :, :] rigidH)
cdef bint get_rigid_Hz(int i, int j, int k, np.int8_t[:, :, :, :] rigidH)
cdef void set_rigid_Hx(int i, int j, int k, np.int8_t[:, :, :, :] rigidH)
cdef void set_rigid_Hy(int i, int j, int k, np.int8_t[:, :, :, :] rigidH)
cdef void set_rigid_Hz(int i, int j, int k, np.int8_t[:, :, :, :] rigidH)
cdef void set_rigid_H(int i, int j, int k, np.int8_t[:, :, :, :] rigidH)
cdef void unset_rigid_H(int i, int j, int k, np.int8_t[:, :, :, :] rigidH)

查看文件

@@ -0,0 +1,157 @@
# Copyright (C) 2015: 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 <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
# Get and set functions for the rigid electric component array. The rigid array is 4D with the 1st dimension holding
# the 12 electric edge components of a cell - Ex1, Ex2, Ex3, Ex4, Ey1, Ey2, Ey3, Ey4, Ez1, Ez2, Ez3, Ez4
cdef bint get_rigid_Ex(int i, int j, int k, np.int8_t[:, :, :, :] rigidE):
cdef bint result
result = False
if rigidE[0, i, j, k]:
result = True
if j != 0:
if rigidE[1, i, j - 1, k]:
result = True
if k != 0:
if rigidE[3, i, j, k - 1]:
result = True
if j != 0 and k != 0:
if rigidE[2, i, j - 1, k - 1]:
result = True
return result
cdef bint get_rigid_Ey(int i, int j, int k, np.int8_t[:, :, :, :] rigidE):
cdef bint result
result = False
if rigidE[4, i, j, k]:
result = True
if i != 0:
if rigidE[7, i - 1, j, k]:
result = True
if k != 0:
if rigidE[5, i, j, k - 1]:
result = True
if i != 0 and k != 0:
if rigidE[6, i - 1, j, k - 1]:
result = True
return result
cdef bint get_rigid_Ez(int i, int j, int k, np.int8_t[:, :, :, :] rigidE):
cdef bint result
result = False
if rigidE[8, i, j, k]:
result = True
if i != 0:
if rigidE[9, i - 1, j, k]:
result = True
if j != 0:
if rigidE[11, i, j - 1, k]:
result = True
if i != 0 and j != 0:
if rigidE[10, i - 1, j - 1, k]:
result = True
return result
cdef void set_rigid_Ex(int i, int j, int k, np.int8_t[:, :, :, :] rigidE):
rigidE[0, i, j, k] = True
if j != 0:
rigidE[1, i, j - 1, k] = True
if k != 0:
rigidE[3, i, j, k - 1] = True
if j != 0 and k != 0:
rigidE[2, i, j - 1, k - 1] = True
cdef void set_rigid_Ey(int i, int j, int k, np.int8_t[:, :, :, :] rigidE):
rigidE[4, i, j, k] = True
if i != 0:
rigidE[7, i - 1, j, k] = True
if k != 0:
rigidE[5, i, j, k - 1] = True
if i != 0 and k != 0:
rigidE[6, i - 1, j, k - 1] = True
cdef void set_rigid_Ez(int i, int j, int k, np.int8_t[:, :, :, :] rigidE):
rigidE[8, i, j, k] = True
if i != 0:
rigidE[9, i - 1, j, k] = True
if j != 0:
rigidE[11, i, j - 1, k] = True
if i != 0 and j != 0:
rigidE[10, i - 1, j - 1, k] = True
cdef void set_rigid_E(int i, int j, int k, np.int8_t[:, :, :, :] rigidE):
rigidE[:, i, j, k] = True
cdef void unset_rigid_E(int i, int j, int k, np.int8_t[:, :, :, :] rigidE):
rigidE[:, i, j, k] = False
# Get and set functions for the rigid magnetic component array. The rigid array is 4D with the 1st dimension holding
# the 6 magnetic edge components - Hx1, Hx2, Hy1, Hy2, Hz1, Hz2
cdef bint get_rigid_Hx(int i, int j, int k, np.int8_t[:, :, :, :] rigidH):
cdef bint result
result = False
if rigidH[0, i, j, k]:
result = True
if i != 0:
if rigidH[1, i - 1, j, k]:
result = True
return result
cdef bint get_rigid_Hy(int i, int j, int k, np.int8_t[:, :, :, :] rigidH):
cdef bint result
result = False
if rigidH[2, i, j, k]:
result = True
if j != 0:
if rigidH[3, i, j - 1, k]:
result = True
return result
cdef bint get_rigid_Hz(int i, int j, int k, np.int8_t[:, :, :, :] rigidH):
cdef bint result
result = False
if rigidH[4, i, j, k]:
result = True
if k != 0:
if rigidH[5, i, j, k - 1]:
result = True
return result
cdef void set_rigid_Hx(int i, int j, int k, np.int8_t[:, :, :, :] rigidH):
rigidH[0, i, j, k] = True
if i != 0:
rigidH[1, i - 1, j, k] = True
cdef void set_rigid_Hy(int i, int j, int k, np.int8_t[:, :, :, :] rigidH):
rigidH[2, i, j, k] = True
if j != 0:
rigidH[3, i, j - 1, k] = True
cdef void set_rigid_Hz(int i, int j, int k, np.int8_t[:, :, :, :] rigidH):
rigidH[4, i, j, k] = True
if k != 0:
rigidH[5, i, j, k - 1] = True
cdef void set_rigid_H(int i, int j, int k, np.int8_t[:, :, :, :] rigidH):
rigidH[:, i, j, k] = True
cdef void unset_rigid_H(int i, int j, int k, np.int8_t[:, :, :, :] rigidH):
rigidH[:, i, j, k] = False