这个提交包含在:
jasminium
2019-08-23 18:06:03 +01:00
父节点 910d2752a0
当前提交 d9c5ed34f9
共有 38 个文件被更改,包括 9310 次插入9390 次删除

查看文件

@@ -1,164 +1,164 @@
"""Class for add_grass command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..utilities import round_value
from ..materials import Material
from ..fractals import FractalSurface
from ..fractals import Grass
from tqdm import tqdm
import numpy as np
class AddGrass(UserObjectGeometry):
"""User class for Grass command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 12
self.hash = '#add_grass'
def create(self, grid, uip):
"""Add Grass to fractal box."""
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
fractal_box_id = self.kwargs['fractal_box_id']
frac_dim = self.kwargs['frac_dim']
limits = self.kwargs['limits']
n_blades = self.kwargs['n_blades']
except KeyError:
raise CmdInputError(self.__str__() + ' requires at least eleven parameters')
try:
seed = self.kwargs['seed']
except KeyError:
seed = None
# grab the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
if volumes:
volume = volumes[0]
else:
raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id))
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
if frac_dim < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension')
if limits[0] < 0 or limits[1] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the minimum and maximum heights for grass blades')
# Check for valid orientations
if xs == xf:
if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if xs != volume.xs and xs != volume.xf:
raise CmdInputError(self.__str__() + ' must specify external surfaces on a fractal box')
fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
# xminus surface
if xs == volume.xs:
raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction')
# xplus surface
elif xf == volume.xf:
if fractalrange[1] > grid.nx:
raise CmdInputError(self.__str__() + ' cannot apply grass to fractal box as it would exceed the domain size in the x direction')
requestedsurface = 'xplus'
elif ys == yf:
if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if ys != volume.ys and ys != volume.yf:
raise CmdInputError(self.__str__() + ' must specify external surfaces on a fractal box')
fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
# yminus surface
if ys == volume.ys:
raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction')
# yplus surface
elif yf == volume.yf:
if fractalrange[1] > grid.ny:
raise CmdInputError(self.__str__() + ' cannot apply grass to fractal box as it would exceed the domain size in the y direction')
requestedsurface = 'yplus'
elif zs == zf:
if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if zs != volume.zs and zs != volume.zf:
raise CmdInputError(self.__str__() + ' must specify external surfaces on a fractal box')
fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))
# zminus surface
if zs == volume.zs:
raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction')
# zplus surface
elif zf == volume.zf:
if fractalrange[1] > grid.nz:
raise CmdInputError(self.__str__() + ' cannot apply grass to fractal box as it would exceed the domain size in the z direction')
requestedsurface = 'zplus'
else:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim)
surface.ID = 'grass'
surface.surfaceID = requestedsurface
surface.seed = seed
# Set the fractal range to scale the fractal distribution between zero and one
surface.fractalrange = (0, 1)
surface.operatingonID = volume.ID
surface.generate_fractal_surface(grid)
if n_blades > surface.fractalsurface.shape[0] * surface.fractalsurface.shape[1]:
raise CmdInputError(self.__str__() + ' the specified surface is not large enough for the number of grass blades/roots specified')
# Scale the distribution so that the summation is equal to one, i.e. a probability distribution
surface.fractalsurface = surface.fractalsurface / np.sum(surface.fractalsurface)
# Set location of grass blades using probability distribution
# Create 1D vector of probability values from the 2D surface
probability1D = np.cumsum(np.ravel(surface.fractalsurface))
# Create random numbers between zero and one for the number of blades of grass
R = np.random.RandomState(surface.seed)
A = R.random_sample(n_blades)
# Locate the random numbers in the bins created by the 1D vector of probability values, and convert the 1D index back into a x, y index for the original surface.
bladesindex = np.unravel_index(np.digitize(A, probability1D), (surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]))
# Set the fractal range to minimum and maximum heights of the grass blades
surface.fractalrange = fractalrange
# Set the fractal surface using the pre-calculated spatial distribution and a random height
surface.fractalsurface = np.zeros((surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]))
for i in range(len(bladesindex[0])):
surface.fractalsurface[bladesindex[0][i], bladesindex[1][i]] = R.randint(surface.fractalrange[0], surface.fractalrange[1], size=1)
# Create grass geometry parameters
g = Grass(n_blades)
g.seed = surface.seed
surface.grass.append(g)
# Check to see if grass has been already defined as a material
if not any(x.ID == 'grass' for x in grid.materials):
m = Material(len(grid.materials), 'grass')
m.averagable = False
m.type = 'builtin, debye'
m.er = Material.grasseri
m.deltaer.append(Material.grassdeltaer)
m.tau.append(Material.grasstau)
grid.materials.append(m)
if Material.maxpoles == 0:
Material.maxpoles = 1
# Check if time step for model is suitable for using grass
grass = next((x for x in grid.materials if x.ID == 'grass'))
testgrass = next((x for x in grass.tau if x < grid.dt), None)
if testgrass:
raise CmdInputError(self.__str__() + ' requires the time step for the model to be less than the relaxation time required to model grass.')
volume.fractalsurfaces.append(surface)
if config.is_messages():
tqdm.write('{} blades of grass on surface from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with fractal dimension {:g}, fractal seeding {}, and range {:g}m to {:g}m, added to {}.'.format(n_blades, xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, surface.dimension, surface.seed, limits[0], limits[1], surface.operatingonID))
"""Class for add_grass command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..utilities import round_value
from ..materials import Material
from ..fractals import FractalSurface
from ..fractals import Grass
from tqdm import tqdm
import numpy as np
class AddGrass(UserObjectGeometry):
"""User class for Grass command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 12
self.hash = '#add_grass'
def create(self, grid, uip):
"""Add Grass to fractal box."""
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
fractal_box_id = self.kwargs['fractal_box_id']
frac_dim = self.kwargs['frac_dim']
limits = self.kwargs['limits']
n_blades = self.kwargs['n_blades']
except KeyError:
raise CmdInputError(self.__str__() + ' requires at least eleven parameters')
try:
seed = self.kwargs['seed']
except KeyError:
seed = None
# grab the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
if volumes:
volume = volumes[0]
else:
raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id))
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
if frac_dim < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension')
if limits[0] < 0 or limits[1] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the minimum and maximum heights for grass blades')
# Check for valid orientations
if xs == xf:
if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if xs != volume.xs and xs != volume.xf:
raise CmdInputError(self.__str__() + ' must specify external surfaces on a fractal box')
fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
# xminus surface
if xs == volume.xs:
raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction')
# xplus surface
elif xf == volume.xf:
if fractalrange[1] > grid.nx:
raise CmdInputError(self.__str__() + ' cannot apply grass to fractal box as it would exceed the domain size in the x direction')
requestedsurface = 'xplus'
elif ys == yf:
if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if ys != volume.ys and ys != volume.yf:
raise CmdInputError(self.__str__() + ' must specify external surfaces on a fractal box')
fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
# yminus surface
if ys == volume.ys:
raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction')
# yplus surface
elif yf == volume.yf:
if fractalrange[1] > grid.ny:
raise CmdInputError(self.__str__() + ' cannot apply grass to fractal box as it would exceed the domain size in the y direction')
requestedsurface = 'yplus'
elif zs == zf:
if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if zs != volume.zs and zs != volume.zf:
raise CmdInputError(self.__str__() + ' must specify external surfaces on a fractal box')
fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))
# zminus surface
if zs == volume.zs:
raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction')
# zplus surface
elif zf == volume.zf:
if fractalrange[1] > grid.nz:
raise CmdInputError(self.__str__() + ' cannot apply grass to fractal box as it would exceed the domain size in the z direction')
requestedsurface = 'zplus'
else:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim)
surface.ID = 'grass'
surface.surfaceID = requestedsurface
surface.seed = seed
# Set the fractal range to scale the fractal distribution between zero and one
surface.fractalrange = (0, 1)
surface.operatingonID = volume.ID
surface.generate_fractal_surface(grid)
if n_blades > surface.fractalsurface.shape[0] * surface.fractalsurface.shape[1]:
raise CmdInputError(self.__str__() + ' the specified surface is not large enough for the number of grass blades/roots specified')
# Scale the distribution so that the summation is equal to one, i.e. a probability distribution
surface.fractalsurface = surface.fractalsurface / np.sum(surface.fractalsurface)
# Set location of grass blades using probability distribution
# Create 1D vector of probability values from the 2D surface
probability1D = np.cumsum(np.ravel(surface.fractalsurface))
# Create random numbers between zero and one for the number of blades of grass
R = np.random.RandomState(surface.seed)
A = R.random_sample(n_blades)
# Locate the random numbers in the bins created by the 1D vector of probability values, and convert the 1D index back into a x, y index for the original surface.
bladesindex = np.unravel_index(np.digitize(A, probability1D), (surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]))
# Set the fractal range to minimum and maximum heights of the grass blades
surface.fractalrange = fractalrange
# Set the fractal surface using the pre-calculated spatial distribution and a random height
surface.fractalsurface = np.zeros((surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]))
for i in range(len(bladesindex[0])):
surface.fractalsurface[bladesindex[0][i], bladesindex[1][i]] = R.randint(surface.fractalrange[0], surface.fractalrange[1], size=1)
# Create grass geometry parameters
g = Grass(n_blades)
g.seed = surface.seed
surface.grass.append(g)
# Check to see if grass has been already defined as a material
if not any(x.ID == 'grass' for x in grid.materials):
m = Material(len(grid.materials), 'grass')
m.averagable = False
m.type = 'builtin, debye'
m.er = Material.grasseri
m.deltaer.append(Material.grassdeltaer)
m.tau.append(Material.grasstau)
grid.materials.append(m)
if Material.maxpoles == 0:
Material.maxpoles = 1
# Check if time step for model is suitable for using grass
grass = next((x for x in grid.materials if x.ID == 'grass'))
testgrass = next((x for x in grass.tau if x < grid.dt), None)
if testgrass:
raise CmdInputError(self.__str__() + ' requires the time step for the model to be less than the relaxation time required to model grass.')
volume.fractalsurfaces.append(surface)
if config.is_messages():
tqdm.write('{} blades of grass on surface from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with fractal dimension {:g}, fractal seeding {}, and range {:g}m to {:g}m, added to {}.'.format(n_blades, xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, surface.dimension, surface.seed, limits[0], limits[1], surface.operatingonID))

查看文件

@@ -1,127 +1,127 @@
"""Class for surface roughness command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..fractals import FractalSurface
from ..utilities import round_value
import gprMax.config as config
from tqdm import tqdm
import numpy as np
class AddSurfaceRoughness(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 10
self.hash = '#add_surface_roughness'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
frac_dim = self.kwargs['frac_dim']
weighting = np.array(self.kwargs['weighting'], dtype=np.float64)
limits = np.array(self.kwargs['limits'])
fractal_box_id = self.kwargs['fractal_box_id']
except KeyError:
raise CmdInputError(self.__str__() + ' Incorrect parameters')
try:
seed = self.kwargs['seed']
except KeyError:
seed = None
# grab the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
if volumes:
volume = volumes[0]
else:
raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id))
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
if frac_dim < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension')
if weighting[0] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the first direction of the surface')
if weighting[1] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the second direction of the surface')
# Check for valid orientations
if xs == xf:
if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if xs != volume.xs and xs != volume.xf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
# xminus surface
if xs == volume.xs:
if fractalrange[0] < 0 or fractalrange[1] > volume.xf:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the upper coordinates of the fractal box or the domain in the x direction')
requestedsurface = 'xminus'
# xplus surface
elif xf == volume.xf:
if fractalrange[0] < volume.xs or fractalrange[1] > grid.nx:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the lower coordinates of the fractal box or the domain in the x direction')
requestedsurface = 'xplus'
elif ys == yf:
if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if ys != volume.ys and ys != volume.yf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
# yminus surface
if ys == volume.ys:
if fractalrange[0] < 0 or fractalrange[1] > volume.yf:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the upper coordinates of the fractal box or the domain in the y direction')
requestedsurface = 'yminus'
# yplus surface
elif yf == volume.yf:
if fractalrange[0] < volume.ys or fractalrange[1] > grid.ny:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the lower coordinates of the fractal box or the domain in the y direction')
requestedsurface = 'yplus'
elif zs == zf:
if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if zs != volume.zs and zs != volume.zf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))
# zminus surface
if zs == volume.zs:
if fractalrange[0] < 0 or fractalrange[1] > volume.zf:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the upper coordinates of the fractal box or the domain in the x direction')
requestedsurface = 'zminus'
# zplus surface
elif zf == volume.zf:
if fractalrange[0] < volume.zs or fractalrange[1] > grid.nz:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the lower coordinates of the fractal box or the domain in the z direction')
requestedsurface = 'zplus'
else:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim)
surface.surfaceID = requestedsurface
surface.fractalrange = fractalrange
surface.operatingonID = volume.ID
surface.seed = seed
surface.weighting = weighting
# List of existing surfaces IDs
existingsurfaceIDs = [x.surfaceID for x in volume.fractalsurfaces]
if surface.surfaceID in existingsurfaceIDs:
raise CmdInputError(self.__str__() + ' has already been used on the {} surface'.format(surface.surfaceID))
surface.generate_fractal_surface(grid)
volume.fractalsurfaces.append(surface)
if config.is_messages():
tqdm.write('Fractal surface from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with fractal dimension {:g}, fractal weightings {:g}, {:g}, fractal seeding {}, and range {:g}m to {:g}m, added to {}.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, surface.dimension, surface.weighting[0], surface.weighting[1], surface.seed, limits[0], limits[1], surface.operatingonID))
"""Class for surface roughness command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..fractals import FractalSurface
from ..utilities import round_value
import gprMax.config as config
from tqdm import tqdm
import numpy as np
class AddSurfaceRoughness(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 10
self.hash = '#add_surface_roughness'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
frac_dim = self.kwargs['frac_dim']
weighting = np.array(self.kwargs['weighting'], dtype=np.float64)
limits = np.array(self.kwargs['limits'])
fractal_box_id = self.kwargs['fractal_box_id']
except KeyError:
raise CmdInputError(self.__str__() + ' Incorrect parameters')
try:
seed = self.kwargs['seed']
except KeyError:
seed = None
# grab the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
if volumes:
volume = volumes[0]
else:
raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id))
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
if frac_dim < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension')
if weighting[0] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the first direction of the surface')
if weighting[1] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the second direction of the surface')
# Check for valid orientations
if xs == xf:
if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if xs != volume.xs and xs != volume.xf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
# xminus surface
if xs == volume.xs:
if fractalrange[0] < 0 or fractalrange[1] > volume.xf:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the upper coordinates of the fractal box or the domain in the x direction')
requestedsurface = 'xminus'
# xplus surface
elif xf == volume.xf:
if fractalrange[0] < volume.xs or fractalrange[1] > grid.nx:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the lower coordinates of the fractal box or the domain in the x direction')
requestedsurface = 'xplus'
elif ys == yf:
if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if ys != volume.ys and ys != volume.yf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
# yminus surface
if ys == volume.ys:
if fractalrange[0] < 0 or fractalrange[1] > volume.yf:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the upper coordinates of the fractal box or the domain in the y direction')
requestedsurface = 'yminus'
# yplus surface
elif yf == volume.yf:
if fractalrange[0] < volume.ys or fractalrange[1] > grid.ny:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the lower coordinates of the fractal box or the domain in the y direction')
requestedsurface = 'yplus'
elif zs == zf:
if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if zs != volume.zs and zs != volume.zf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))
# zminus surface
if zs == volume.zs:
if fractalrange[0] < 0 or fractalrange[1] > volume.zf:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the upper coordinates of the fractal box or the domain in the x direction')
requestedsurface = 'zminus'
# zplus surface
elif zf == volume.zf:
if fractalrange[0] < volume.zs or fractalrange[1] > grid.nz:
raise CmdInputError(self.__str__() + ' cannot apply fractal surface to fractal box as it would exceed either the lower coordinates of the fractal box or the domain in the z direction')
requestedsurface = 'zplus'
else:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim)
surface.surfaceID = requestedsurface
surface.fractalrange = fractalrange
surface.operatingonID = volume.ID
surface.seed = seed
surface.weighting = weighting
# List of existing surfaces IDs
existingsurfaceIDs = [x.surfaceID for x in volume.fractalsurfaces]
if surface.surfaceID in existingsurfaceIDs:
raise CmdInputError(self.__str__() + ' has already been used on the {} surface'.format(surface.surfaceID))
surface.generate_fractal_surface(grid)
volume.fractalsurfaces.append(surface)
if config.is_messages():
tqdm.write('Fractal surface from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with fractal dimension {:g}, fractal weightings {:g}, {:g}, fractal seeding {}, and range {:g}m to {:g}m, added to {}.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, surface.dimension, surface.weighting[0], surface.weighting[1], surface.seed, limits[0], limits[1], surface.operatingonID))

查看文件

@@ -1,118 +1,118 @@
"""Class for surface water command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..utilities import round_value
from ..materials import Material
from tqdm import tqdm
class AddSurfaceWater(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 11
self.hash = '#add_surface_water'
def create(self, grid, uip):
""""Create surface water on fractal box."""
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
fractal_box_id = self.kwargs['fractal_box_id']
depth = self.kwargs['depth']
except KeyError:
raise CmdInputError(self.__str__() + ' requires exactly eight parameters')
# grab the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
if volumes:
volume = volumes[0]
else:
raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id))
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
if depth <= 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the depth of water')
# Check for valid orientations
if xs == xf:
if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if xs != volume.xs and xs != volume.xf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
# xminus surface
if xs == volume.xs:
requestedsurface = 'xminus'
# xplus surface
elif xf == volume.xf:
requestedsurface = 'xplus'
filldepthcells = round_value(depth / grid.dx)
filldepth = filldepthcells * grid.dx
elif ys == yf:
if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if ys != volume.ys and ys != volume.yf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
# yminus surface
if ys == volume.ys:
requestedsurface = 'yminus'
# yplus surface
elif yf == volume.yf:
requestedsurface = 'yplus'
filldepthcells = round_value(depth / grid.dy)
filldepth = filldepthcells * grid.dy
elif zs == zf:
if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if zs != volume.zs and zs != volume.zf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
# zminus surface
if zs == volume.zs:
requestedsurface = 'zminus'
# zplus surface
elif zf == volume.zf:
requestedsurface = 'zplus'
filldepthcells = round_value(depth / grid.dz)
filldepth = filldepthcells * grid.dz
else:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
surface = next((x for x in volume.fractalsurfaces if x.surfaceID == requestedsurface), None)
if not surface:
raise CmdInputError(self.__str__() + ' specified surface {} does not have a rough surface applied'.format(requestedsurface))
surface.filldepth = filldepthcells
# Check that requested fill depth falls within range of surface roughness
if surface.filldepth < surface.fractalrange[0] or surface.filldepth > surface.fractalrange[1]:
raise CmdInputError(self.__str__() + ' requires a value for the depth of water that lies with the range of the requested surface roughness')
# Check to see if water has been already defined as a material
if not any(x.ID == 'water' for x in grid.materials):
m = Material(len(grid.materials), 'water')
m.averagable = False
m.type = 'builtin, debye'
m.er = Material.watereri
m.deltaer.append(Material.waterdeltaer)
m.tau.append(Material.watertau)
grid.materials.append(m)
if Material.maxpoles == 0:
Material.maxpoles = 1
# Check if time step for model is suitable for using water
water = next((x for x in grid.materials if x.ID == 'water'))
testwater = next((x for x in water.tau if x < grid.dt), None)
if testwater:
raise CmdInputError(self.__str__() + ' requires the time step for the model to be less than the relaxation time required to model water.')
if config.is_messages():
tqdm.write('Water on surface from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with depth {:g}m, added to {}.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, filldepth, surface.operatingonID))
"""Class for surface water command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..utilities import round_value
from ..materials import Material
from tqdm import tqdm
class AddSurfaceWater(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 11
self.hash = '#add_surface_water'
def create(self, grid, uip):
""""Create surface water on fractal box."""
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
fractal_box_id = self.kwargs['fractal_box_id']
depth = self.kwargs['depth']
except KeyError:
raise CmdInputError(self.__str__() + ' requires exactly eight parameters')
# grab the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
if volumes:
volume = volumes[0]
else:
raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id))
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
if depth <= 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the depth of water')
# Check for valid orientations
if xs == xf:
if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if xs != volume.xs and xs != volume.xf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
# xminus surface
if xs == volume.xs:
requestedsurface = 'xminus'
# xplus surface
elif xf == volume.xf:
requestedsurface = 'xplus'
filldepthcells = round_value(depth / grid.dx)
filldepth = filldepthcells * grid.dx
elif ys == yf:
if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if ys != volume.ys and ys != volume.yf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
# yminus surface
if ys == volume.ys:
requestedsurface = 'yminus'
# yplus surface
elif yf == volume.yf:
requestedsurface = 'yplus'
filldepthcells = round_value(depth / grid.dy)
filldepth = filldepthcells * grid.dy
elif zs == zf:
if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if zs != volume.zs and zs != volume.zf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
# zminus surface
if zs == volume.zs:
requestedsurface = 'zminus'
# zplus surface
elif zf == volume.zf:
requestedsurface = 'zplus'
filldepthcells = round_value(depth / grid.dz)
filldepth = filldepthcells * grid.dz
else:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
surface = next((x for x in volume.fractalsurfaces if x.surfaceID == requestedsurface), None)
if not surface:
raise CmdInputError(self.__str__() + ' specified surface {} does not have a rough surface applied'.format(requestedsurface))
surface.filldepth = filldepthcells
# Check that requested fill depth falls within range of surface roughness
if surface.filldepth < surface.fractalrange[0] or surface.filldepth > surface.fractalrange[1]:
raise CmdInputError(self.__str__() + ' requires a value for the depth of water that lies with the range of the requested surface roughness')
# Check to see if water has been already defined as a material
if not any(x.ID == 'water' for x in grid.materials):
m = Material(len(grid.materials), 'water')
m.averagable = False
m.type = 'builtin, debye'
m.er = Material.watereri
m.deltaer.append(Material.waterdeltaer)
m.tau.append(Material.watertau)
grid.materials.append(m)
if Material.maxpoles == 0:
Material.maxpoles = 1
# Check if time step for model is suitable for using water
water = next((x for x in grid.materials if x.ID == 'water'))
testwater = next((x for x in water.tau if x < grid.dt), None)
if testwater:
raise CmdInputError(self.__str__() + ' requires the time step for the model to be less than the relaxation time required to model water.')
if config.is_messages():
tqdm.write('Water on surface from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with depth {:g}m, added to {}.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, filldepth, surface.operatingonID))

查看文件

@@ -1,93 +1,93 @@
"""Class for triangle command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_box
import gprMax.config as config
from tqdm import tqdm
import numpy as np
class Box(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 5
self.hash = '#box'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
except KeyError:
raise CmdInputError(self.__str__() + ' Please specify two points.')
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
# check averaging
try:
# go with user specified averaging
averagebox = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagebox = grid.averagevolumeobjects
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagebox
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
build_box(xs, xf, ys, yf, zs, zf, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Box from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m of material(s) {} created, dielectric smoothing is {}.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, ', '.join(materialsrequested), dielectricsmoothing))
"""Class for triangle command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_box
import gprMax.config as config
from tqdm import tqdm
import numpy as np
class Box(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 5
self.hash = '#box'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
except KeyError:
raise CmdInputError(self.__str__() + ' Please specify two points.')
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
# check averaging
try:
# go with user specified averaging
averagebox = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagebox = grid.averagevolumeobjects
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagebox
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
build_box(xs, xf, ys, yf, zs, zf, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Box from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m of material(s) {} created, dielectric smoothing is {}.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, ', '.join(materialsrequested), dielectricsmoothing))

查看文件

@@ -1,52 +1,52 @@
from jinja2 import Environment, PackageLoader, select_autoescape
env = Environment(
loader=PackageLoader(__name__, 'templates'),
)
template = env.get_template('fields_updates_dispersive_template')
r = template.render(
functions=[
# name, double, real
{
'name_a': 'update_electric_dispersive_multipole_A_double_real',
'name_b': 'update_electric_dispersive_multipole_B_double_real',
'name_a_1': 'update_electric_dispersive_1pole_A_double_real',
'name_b_1': 'update_electric_dispersive_1pole_B_double_real',
'field_type': 'double',
'dispersive_type': 'double'
},
# name, float, real
{
'name_a': 'update_electric_dispersive_multipole_A_float_real',
'name_b': 'update_electric_dispersive_multipole_B_float_real',
'name_a_1': 'update_electric_dispersive_1pole_A_float_real',
'name_b_1': 'update_electric_dispersive_1pole_B_float_real',
'field_type': 'float',
'dispersive_type': 'float'
},
# name, double, complex
{
'name_a': 'update_electric_dispersive_multipole_A_double_complex',
'name_b': 'update_electric_dispersive_multipole_B_double_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_double_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_double_complex',
'field_type': 'double',
'dispersive_type': 'double complex',
'real_part': 'creal'
},
# name, float, complex
{
'name_a': 'update_electric_dispersive_multipole_A_float_complex',
'name_b': 'update_electric_dispersive_multipole_B_float_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_float_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_float_complex',
'field_type': 'float',
'dispersive_type': 'float complex',
'real_part': 'crealf'
}]
)
f = open('cython/dispersive_updates_test.pyx', 'w')
f.write(r)
f.close()
from jinja2 import Environment, PackageLoader, select_autoescape
env = Environment(
loader=PackageLoader(__name__, 'templates'),
)
template = env.get_template('fields_updates_dispersive_template')
r = template.render(
functions=[
# name, double, real
{
'name_a': 'update_electric_dispersive_multipole_A_double_real',
'name_b': 'update_electric_dispersive_multipole_B_double_real',
'name_a_1': 'update_electric_dispersive_1pole_A_double_real',
'name_b_1': 'update_electric_dispersive_1pole_B_double_real',
'field_type': 'double',
'dispersive_type': 'double'
},
# name, float, real
{
'name_a': 'update_electric_dispersive_multipole_A_float_real',
'name_b': 'update_electric_dispersive_multipole_B_float_real',
'name_a_1': 'update_electric_dispersive_1pole_A_float_real',
'name_b_1': 'update_electric_dispersive_1pole_B_float_real',
'field_type': 'float',
'dispersive_type': 'float'
},
# name, double, complex
{
'name_a': 'update_electric_dispersive_multipole_A_double_complex',
'name_b': 'update_electric_dispersive_multipole_B_double_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_double_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_double_complex',
'field_type': 'double',
'dispersive_type': 'double complex',
'real_part': 'creal'
},
# name, float, complex
{
'name_a': 'update_electric_dispersive_multipole_A_float_complex',
'name_b': 'update_electric_dispersive_multipole_B_float_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_float_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_float_complex',
'field_type': 'float',
'dispersive_type': 'float complex',
'real_part': 'crealf'
}]
)
f = open('cython/dispersive_updates_test.pyx', 'w')
f.write(r)
f.close()

查看文件

@@ -1,98 +1,98 @@
"""Class for cylinder command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_cylinder
from tqdm import tqdm
import numpy as np
import gprMax.config as config
class Cylinder(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 6
self.hash = '#cylinder'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
r = self.kwargs['r']
except KeyError:
raise CmdInputError(self.__str__() + ' Please specify 2 points and a radius')
# check averaging
try:
# go with user specified averaging
averagecylinder = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagecylinder = grid.averagevolumeobjects
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
x1, y1, z1 = uip.round_to_grid(p1)
x2, y2, z2 = uip.round_to_grid(p2)
if r <= 0:
raise CmdInputError(self.__str__() + ' the radius {:g} should be a positive value.'.format(r))
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagecylinder
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
build_cylinder(x1, y1, z1, x2, y2, z2, r, grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Cylinder with face centres {:g}m, {:g}m, {:g}m and {:g}m, {:g}m, {:g}m, with radius {:g}m, of material(s) {} created, dielectric smoothing is {}.'.format(x1, y1, z1, x2, y2, z2, r, ', '.join(materialsrequested), dielectricsmoothing))
"""Class for cylinder command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_cylinder
from tqdm import tqdm
import numpy as np
import gprMax.config as config
class Cylinder(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 6
self.hash = '#cylinder'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
r = self.kwargs['r']
except KeyError:
raise CmdInputError(self.__str__() + ' Please specify 2 points and a radius')
# check averaging
try:
# go with user specified averaging
averagecylinder = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagecylinder = grid.averagevolumeobjects
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
x1, y1, z1 = uip.round_to_grid(p1)
x2, y2, z2 = uip.round_to_grid(p2)
if r <= 0:
raise CmdInputError(self.__str__() + ' the radius {:g} should be a positive value.'.format(r))
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagecylinder
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
build_cylinder(x1, y1, z1, x2, y2, z2, r, grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Cylinder with face centres {:g}m, {:g}m, {:g}m and {:g}m, {:g}m, {:g}m, with radius {:g}m, of material(s) {} created, dielectric smoothing is {}.'.format(x1, y1, z1, x2, y2, z2, r, ', '.join(materialsrequested), dielectricsmoothing))

查看文件

@@ -1,137 +1,137 @@
"""Class for cylinder command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_cylindrical_sector
from tqdm import tqdm
import numpy as np
class CylindricalSector(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 7
self.hash = '#cylindrical_sector'
def create(self, grid, uip):
try:
normal = self.kwargs['normal'].lower()
ctr1 = self.kwargs['ctr1']
ctr2 = self.kwargs['ctr2']
extent1 = self.kwargs['extent1']
extent2 = self.kwargs['extent2']
start = self.kwargs['start']
end = self.kwargs['end']
r = self.kwargs['r']
thickness = extent2 - extent1
except KeyError:
raise CmdInputError(self.__str__())
# check averaging
try:
# go with user specified averaging
averagecylindricalsector = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagecylindricalsector = grid.averagevolumeobjects
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
sectorstartangle = 2 * np.pi * (start / 360)
sectorangle = 2 * np.pi * (end / 360)
if normal != 'x' and normal != 'y' and normal != 'z':
raise CmdInputError(self.__str__() + ' the normal direction must be either x, y or z.')
if r <= 0:
raise CmdInputError(self.__str__() + ' the radius {:g} should be a positive value.'.format(r))
if sectorstartangle < 0 or sectorangle <= 0:
raise CmdInputError(self.__str__() + ' the starting angle and sector angle should be a positive values.')
if sectorstartangle >= 2 * np.pi or sectorangle >= 2 * np.pi:
raise CmdInputError(self.__str__() + ' the starting angle and sector angle must be less than 360 degrees.')
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
if thickness > 0:
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagecylindricalsector
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
else:
averaging = False
# Isotropic case
if len(materials) == 1:
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
# numID requires a value but it will not be used
numID = None
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
# yz-plane cylindrical sector
if normal == 'x':
level, ctr1, ctr2 = uip.round_to_grid((extent1, ctr1, ctr2))
# xz-plane cylindrical sector
elif normal == 'y':
ctr1, level, ctr2 = uip.round_to_grid((ctr1, extent1, ctr2))
# xy-plane cylindrical sector
elif normal == 'z':
ctr1, ctr2, level = uip.round_to_grid((ctr1, ctr2, extent1))
build_cylindrical_sector(ctr1, ctr2, level, sectorstartangle, sectorangle, r, normal, thickness, grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if thickness > 0:
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Cylindrical sector with centre {:g}m, {:g}m, radius {:g}m, starting angle {:.1f} degrees, sector angle {:.1f} degrees, thickness {:g}m, of material(s) {} created, dielectric smoothing is {}.'.format(ctr1, ctr2, r, (sectorstartangle / (2 * np.pi)) * 360, (sectorangle / (2 * np.pi)) * 360, thickness, ', '.join(materialsrequested), dielectricsmoothing))
else:
tqdm.write('Cylindrical sector with centre {:g}m, {:g}m, radius {:g}m, starting angle {:.1f} degrees, sector angle {:.1f} degrees, of material(s) {} created.'.format(ctr1, ctr2, r, (sectorstartangle / (2 * np.pi)) * 360, (sectorangle / (2 * np.pi)) * 360, ', '.join(materialsrequested)))
"""Class for cylinder command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_cylindrical_sector
from tqdm import tqdm
import numpy as np
class CylindricalSector(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 7
self.hash = '#cylindrical_sector'
def create(self, grid, uip):
try:
normal = self.kwargs['normal'].lower()
ctr1 = self.kwargs['ctr1']
ctr2 = self.kwargs['ctr2']
extent1 = self.kwargs['extent1']
extent2 = self.kwargs['extent2']
start = self.kwargs['start']
end = self.kwargs['end']
r = self.kwargs['r']
thickness = extent2 - extent1
except KeyError:
raise CmdInputError(self.__str__())
# check averaging
try:
# go with user specified averaging
averagecylindricalsector = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagecylindricalsector = grid.averagevolumeobjects
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
sectorstartangle = 2 * np.pi * (start / 360)
sectorangle = 2 * np.pi * (end / 360)
if normal != 'x' and normal != 'y' and normal != 'z':
raise CmdInputError(self.__str__() + ' the normal direction must be either x, y or z.')
if r <= 0:
raise CmdInputError(self.__str__() + ' the radius {:g} should be a positive value.'.format(r))
if sectorstartangle < 0 or sectorangle <= 0:
raise CmdInputError(self.__str__() + ' the starting angle and sector angle should be a positive values.')
if sectorstartangle >= 2 * np.pi or sectorangle >= 2 * np.pi:
raise CmdInputError(self.__str__() + ' the starting angle and sector angle must be less than 360 degrees.')
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
if thickness > 0:
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagecylindricalsector
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
else:
averaging = False
# Isotropic case
if len(materials) == 1:
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
# numID requires a value but it will not be used
numID = None
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
# yz-plane cylindrical sector
if normal == 'x':
level, ctr1, ctr2 = uip.round_to_grid((extent1, ctr1, ctr2))
# xz-plane cylindrical sector
elif normal == 'y':
ctr1, level, ctr2 = uip.round_to_grid((ctr1, extent1, ctr2))
# xy-plane cylindrical sector
elif normal == 'z':
ctr1, ctr2, level = uip.round_to_grid((ctr1, ctr2, extent1))
build_cylindrical_sector(ctr1, ctr2, level, sectorstartangle, sectorangle, r, normal, thickness, grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if thickness > 0:
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Cylindrical sector with centre {:g}m, {:g}m, radius {:g}m, starting angle {:.1f} degrees, sector angle {:.1f} degrees, thickness {:g}m, of material(s) {} created, dielectric smoothing is {}.'.format(ctr1, ctr2, r, (sectorstartangle / (2 * np.pi)) * 360, (sectorangle / (2 * np.pi)) * 360, thickness, ', '.join(materialsrequested), dielectricsmoothing))
else:
tqdm.write('Cylindrical sector with centre {:g}m, {:g}m, radius {:g}m, starting angle {:.1f} degrees, sector angle {:.1f} degrees, of material(s) {} created.'.format(ctr1, ctr2, r, (sectorstartangle / (2 * np.pi)) * 360, (sectorangle / (2 * np.pi)) * 360, ', '.join(materialsrequested)))

查看文件

@@ -1,64 +1,64 @@
"""Class for edge command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..cython.geometry_primitives import build_edge_x
from ..cython.geometry_primitives import build_edge_y
from ..cython.geometry_primitives import build_edge_z
from tqdm import tqdm
class Edge(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 2
self.hash = '#edge'
def create(self, grid, uip):
"""Create edge and add it to the grid."""
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
material_id = self.kwargs['material_id']
except KeyError:
raise CmdInputError(self.__str__() + ' requires exactly 3 parameters')
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
material = next((x for x in grid.materials if x.ID == material_id), None)
if not material:
raise CmdInputError('Material with ID {} does not exist'.format(material_id))
# Check for valid orientations
# x-orientated wire
if xs != xf:
if ys != yf or zs != zf:
raise CmdInputError(self.__str__() + ' the edge is not specified correctly')
else:
for i in range(xs, xf):
build_edge_x(i, ys, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID)
# y-orientated wire
elif ys != yf:
if xs != xf or zs != zf:
raise CmdInputError(self.__str__() + ' the edge is not specified correctly')
else:
for j in range(ys, yf):
build_edge_y(xs, j, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID)
# z-orientated wire
elif zs != zf:
if xs != xf or ys != yf:
raise CmdInputError(self.__str__() + ' the edge is not specified correctly')
else:
for k in range(zs, zf):
build_edge_z(xs, ys, k, material.numID, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
tqdm.write('Edge from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m of material {} created.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, material_id))
"""Class for edge command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..cython.geometry_primitives import build_edge_x
from ..cython.geometry_primitives import build_edge_y
from ..cython.geometry_primitives import build_edge_z
from tqdm import tqdm
class Edge(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 2
self.hash = '#edge'
def create(self, grid, uip):
"""Create edge and add it to the grid."""
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
material_id = self.kwargs['material_id']
except KeyError:
raise CmdInputError(self.__str__() + ' requires exactly 3 parameters')
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
material = next((x for x in grid.materials if x.ID == material_id), None)
if not material:
raise CmdInputError('Material with ID {} does not exist'.format(material_id))
# Check for valid orientations
# x-orientated wire
if xs != xf:
if ys != yf or zs != zf:
raise CmdInputError(self.__str__() + ' the edge is not specified correctly')
else:
for i in range(xs, xf):
build_edge_x(i, ys, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID)
# y-orientated wire
elif ys != yf:
if xs != xf or zs != zf:
raise CmdInputError(self.__str__() + ' the edge is not specified correctly')
else:
for j in range(ys, yf):
build_edge_y(xs, j, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID)
# z-orientated wire
elif zs != zf:
if xs != xf or ys != yf:
raise CmdInputError(self.__str__() + ' the edge is not specified correctly')
else:
for k in range(zs, zf):
build_edge_z(xs, ys, k, material.numID, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
tqdm.write('Edge from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m of material {} created.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, material_id))

查看文件

@@ -1,93 +1,93 @@
"""Class for surface roughness command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..fractals import FractalVolume
import gprMax.config as config
from tqdm import tqdm
import numpy as np
class FractalBox(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 9
self.hash = '#fractal_box'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
frac_dim = self.kwargs['frac_dim']
weighting = np.array(self.kwargs['weighting'])
n_materials = self.kwargs['n_materials']
mixing_model_id = self.kwargs['mixing_model_id']
ID = self.kwargs['id']
except KeyError:
raise CmdInputError(self.__str__() + ' Incorrect parameters')
try:
seed = self.kwargs['seed']
except KeyError:
seed = None
# Default is no dielectric smoothing for a fractal box
averagefractalbox = False
# check averaging
try:
# go with user specified averaging
averagefractalbox = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagefractalbox = False
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
if frac_dim < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension')
if weighting[0] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the x direction')
if weighting[1] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the y direction')
if weighting[2] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the z direction')
if n_materials < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the number of bins')
# Find materials to use to build fractal volume, either from mixing models or normal materials
mixingmodel = next((x for x in grid.mixingmodels if x.ID == mixing_model_id), None)
material = next((x for x in grid.materials if x.ID == mixing_model_id), None)
nbins = n_materials
if mixingmodel:
if nbins == 1:
raise CmdInputError(self.__str__() + ' must be used with more than one material from the mixing model.')
# Create materials from mixing model as number of bins now known from fractal_box command
mixingmodel.calculate_debye_properties(nbins, grid)
elif not material:
raise CmdInputError(self.__str__() + ' mixing model or material with ID {} does not exist'.format(mixing_model_id))
volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim)
volume.ID = ID
volume.operatingonID = mixing_model_id
volume.nbins = nbins
volume.seed = seed
volume.weighting = weighting
volume.averaging = averagefractalbox
volume.mixingmodel = mixingmodel
if config.is_messages():
if volume.averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Fractal box {} from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with {}, fractal dimension {:g}, fractal weightings {:g}, {:g}, {:g}, fractal seeding {}, with {} material(s) created, dielectric smoothing is {}.'.format(volume.ID, xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, volume.operatingonID, volume.dimension, volume.weighting[0], volume.weighting[1], volume.weighting[2], volume.seed, volume.nbins, dielectricsmoothing))
grid.fractalvolumes.append(volume)
"""Class for surface roughness command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..fractals import FractalVolume
import gprMax.config as config
from tqdm import tqdm
import numpy as np
class FractalBox(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 9
self.hash = '#fractal_box'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
frac_dim = self.kwargs['frac_dim']
weighting = np.array(self.kwargs['weighting'])
n_materials = self.kwargs['n_materials']
mixing_model_id = self.kwargs['mixing_model_id']
ID = self.kwargs['id']
except KeyError:
raise CmdInputError(self.__str__() + ' Incorrect parameters')
try:
seed = self.kwargs['seed']
except KeyError:
seed = None
# Default is no dielectric smoothing for a fractal box
averagefractalbox = False
# check averaging
try:
# go with user specified averaging
averagefractalbox = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagefractalbox = False
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
if frac_dim < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension')
if weighting[0] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the x direction')
if weighting[1] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the y direction')
if weighting[2] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the z direction')
if n_materials < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the number of bins')
# Find materials to use to build fractal volume, either from mixing models or normal materials
mixingmodel = next((x for x in grid.mixingmodels if x.ID == mixing_model_id), None)
material = next((x for x in grid.materials if x.ID == mixing_model_id), None)
nbins = n_materials
if mixingmodel:
if nbins == 1:
raise CmdInputError(self.__str__() + ' must be used with more than one material from the mixing model.')
# Create materials from mixing model as number of bins now known from fractal_box command
mixingmodel.calculate_debye_properties(nbins, grid)
elif not material:
raise CmdInputError(self.__str__() + ' mixing model or material with ID {} does not exist'.format(mixing_model_id))
volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim)
volume.ID = ID
volume.operatingonID = mixing_model_id
volume.nbins = nbins
volume.seed = seed
volume.weighting = weighting
volume.averaging = averagefractalbox
volume.mixingmodel = mixingmodel
if config.is_messages():
if volume.averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Fractal box {} from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m with {}, fractal dimension {:g}, fractal weightings {:g}, {:g}, {:g}, fractal seeding {}, with {} material(s) created, dielectric smoothing is {}.'.format(volume.ID, xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, volume.operatingonID, volume.dimension, volume.weighting[0], volume.weighting[1], volume.weighting[2], volume.seed, volume.nbins, dielectricsmoothing))
grid.fractalvolumes.append(volume)

查看文件

@@ -1,112 +1,112 @@
"""Class for edge command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..cython.geometry_primitives import build_face_yz
from ..cython.geometry_primitives import build_face_xz
from ..cython.geometry_primitives import build_face_xy
from tqdm import tqdm
class Plate(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 3
self.hash = '#plate'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
except KeyError:
raise CmdInputError(self.__str__() + ' 2 points must be specified')
# isotropic
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
# Check for valid orientations
if xs == xf:
if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
elif ys == yf:
if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
elif zs == zf:
if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
else:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# yz-plane plate
if xs == xf:
# Isotropic case
if len(materials) == 1:
numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 2:
numIDy = materials[0].numID
numIDz = materials[1].numID
for j in range(ys, yf):
for k in range(zs, zf):
build_face_yz(xs, j, k, numIDy, numIDz, grid.rigidE, grid.rigidH, grid.ID)
# xz-plane plate
elif ys == yf:
# Isotropic case
if len(materials) == 1:
numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 2:
numIDx = materials[0].numID
numIDz = materials[1].numID
for i in range(xs, xf):
for k in range(zs, zf):
build_face_xz(i, ys, k, numIDx, numIDz, grid.rigidE, grid.rigidH, grid.ID)
# xy-plane plate
elif zs == zf:
# Isotropic case
if len(materials) == 1:
numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 2:
numIDx = materials[0].numID
numIDy = materials[1].numID
for i in range(xs, xf):
for j in range(ys, yf):
build_face_xy(i, j, zs, numIDx, numIDy, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
tqdm.write('Plate from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m of material(s) {} created.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, ', '.join(materialsrequested)))
"""Class for edge command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..cython.geometry_primitives import build_face_yz
from ..cython.geometry_primitives import build_face_xz
from ..cython.geometry_primitives import build_face_xy
from tqdm import tqdm
class Plate(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 3
self.hash = '#plate'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
except KeyError:
raise CmdInputError(self.__str__() + ' 2 points must be specified')
# isotropic
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1
xf, yf, zf = p2
# Check for valid orientations
if xs == xf:
if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
elif ys == yf:
if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
elif zs == zf:
if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
else:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# yz-plane plate
if xs == xf:
# Isotropic case
if len(materials) == 1:
numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 2:
numIDy = materials[0].numID
numIDz = materials[1].numID
for j in range(ys, yf):
for k in range(zs, zf):
build_face_yz(xs, j, k, numIDy, numIDz, grid.rigidE, grid.rigidH, grid.ID)
# xz-plane plate
elif ys == yf:
# Isotropic case
if len(materials) == 1:
numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 2:
numIDx = materials[0].numID
numIDz = materials[1].numID
for i in range(xs, xf):
for k in range(zs, zf):
build_face_xz(i, ys, k, numIDx, numIDz, grid.rigidE, grid.rigidH, grid.ID)
# xy-plane plate
elif zs == zf:
# Isotropic case
if len(materials) == 1:
numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 2:
numIDx = materials[0].numID
numIDy = materials[1].numID
for i in range(xs, xf):
for j in range(ys, yf):
build_face_xy(i, j, zs, numIDx, numIDy, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
tqdm.write('Plate from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m of material(s) {} created.'.format(xs * grid.dx, ys * grid.dy, zs * grid.dz, xf * grid.dx, yf * grid.dy, zf * grid.dz, ', '.join(materialsrequested)))

查看文件

@@ -1,91 +1,91 @@
"""Class for sphere command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_sphere
from tqdm import tqdm
import numpy as np
class Sphere(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 8
self.hash = '#sphere'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
r = self.kwargs['r']
except KeyError:
raise CmdInputError(self.__str__() + ' Please specify a point and a radius.')
# check averaging
try:
# go with user specified averaging
averagesphere = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagesphere = grid.averagevolumeobjects
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
# Centre of sphere
xc, yc, zc = uip.round_to_grid(p1)
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagesphere
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
build_sphere(xc, yc, zc, r, grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Sphere with centre {:g}m, {:g}m, {:g}m, radius {:g}m, of material(s) {} created, dielectric smoothing is {}.'.format(xc * grid.dx, yc * grid.dy, zc * grid.dz, r, ', '.join(materialsrequested), dielectricsmoothing))
"""Class for sphere command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_sphere
from tqdm import tqdm
import numpy as np
class Sphere(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 8
self.hash = '#sphere'
def create(self, grid, uip):
try:
p1 = self.kwargs['p1']
r = self.kwargs['r']
except KeyError:
raise CmdInputError(self.__str__() + ' Please specify a point and a radius.')
# check averaging
try:
# go with user specified averaging
averagesphere = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagesphere = grid.averagevolumeobjects
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
# Centre of sphere
xc, yc, zc = uip.round_to_grid(p1)
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagesphere
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
build_sphere(xc, yc, zc, r, grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Sphere with centre {:g}m, {:g}m, {:g}m, radius {:g}m, of material(s) {} created, dielectric smoothing is {}.'.format(xc * grid.dx, yc * grid.dy, zc * grid.dz, r, ', '.join(materialsrequested), dielectricsmoothing))

查看文件

@@ -1,132 +1,132 @@
"""Class for triangle command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_triangle
from tqdm import tqdm
import numpy as np
class Triangle(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 4
self.hash = '#triangle'
def create(self, grid, uip):
try:
up1 = self.kwargs['p1']
up2 = self.kwargs['p2']
up3 = self.kwargs['p3']
thickness = self.kwargs['thickness']
except KeyError:
raise CmdInputError(self.params_str() + ' Specify 3 points and a thickness')
# check averaging
try:
# go with user specified averaging
averagetriangularprism = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagetriangularprism = grid.averagevolumeobjects
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
# Check whether points are valid against grid
uip.check_tri_points(up1, up2, up3, object)
# Convert points to metres
x1, y1, z1 = uip.round_to_grid(up1)
x2, y2, z2 = uip.round_to_grid(up2)
x3, y3, z3 = uip.round_to_grid(up3)
if thickness < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for thickness')
# Check for valid orientations
# yz-plane triangle
if x1 == x2 and x2 == x3:
normal = 'x'
# xz-plane triangle
elif y1 == y2 and y2 == y3:
normal = 'y'
# xy-plane triangle
elif z1 == z2 and z2 == z3:
normal = 'z'
else:
raise CmdInputError(self.__str__() + ' the triangle is not specified correctly')
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
if thickness > 0:
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagetriangularprism
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
else:
averaging = False
# Isotropic case
if len(materials) == 1:
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
# numID requires a value but it will not be used
numID = None
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
build_triangle(x1, y1, z1, x2, y2, z2, x3, y3, z3, normal, thickness, grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if thickness > 0:
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Triangle with coordinates {:g}m {:g}m {:g}m, {:g}m {:g}m {:g}m, {:g}m {:g}m {:g}m and thickness {:g}m of material(s) {} created, dielectric smoothing is {}.'.format(x1, y1, z1, x2, y2, z2, x3, y3, z3, thickness, ', '.join(materialsrequested), dielectricsmoothing))
else:
tqdm.write('Triangle with coordinates {:g}m {:g}m {:g}m, {:g}m {:g}m {:g}m, {:g}m {:g}m {:g}m of material(s) {} created.'.format(x1, y1, z1, x2, y2, z2, x3, y3, z3, ', '.join(materialsrequested)))
"""Class for triangle command."""
from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError
from ..materials import Material
from ..cython.geometry_primitives import build_triangle
from tqdm import tqdm
import numpy as np
class Triangle(UserObjectGeometry):
"""User class for edge command."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.order = 4
self.hash = '#triangle'
def create(self, grid, uip):
try:
up1 = self.kwargs['p1']
up2 = self.kwargs['p2']
up3 = self.kwargs['p3']
thickness = self.kwargs['thickness']
except KeyError:
raise CmdInputError(self.params_str() + ' Specify 3 points and a thickness')
# check averaging
try:
# go with user specified averaging
averagetriangularprism = self.kwargs['averaging']
except KeyError:
# if they havent specfied - go with the grid default
averagetriangularprism = grid.averagevolumeobjects
# check materials have been specified
# isotropic case
try:
materialsrequested = [self.kwargs['material_id']]
except KeyError:
# Anisotropic case
try:
materialsrequested = self.kwargs['material_ids']
except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified')
# Check whether points are valid against grid
uip.check_tri_points(up1, up2, up3, object)
# Convert points to metres
x1, y1, z1 = uip.round_to_grid(up1)
x2, y2, z2 = uip.round_to_grid(up2)
x3, y3, z3 = uip.round_to_grid(up3)
if thickness < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for thickness')
# Check for valid orientations
# yz-plane triangle
if x1 == x2 and x2 == x3:
normal = 'x'
# xz-plane triangle
elif y1 == y2 and y2 == y3:
normal = 'y'
# xy-plane triangle
elif z1 == z2 and z2 == z3:
normal = 'z'
else:
raise CmdInputError(self.__str__() + ' the triangle is not specified correctly')
# Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
if thickness > 0:
# Isotropic case
if len(materials) == 1:
averaging = materials[0].averagable and averagetriangularprism
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
averaging = False
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial:
numID = averagedmaterial.numID
else:
numID = len(grid.materials)
m = Material(numID, requiredID)
m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0)
m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0)
m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list
grid.materials.append(m)
else:
averaging = False
# Isotropic case
if len(materials) == 1:
numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case
elif len(materials) == 3:
# numID requires a value but it will not be used
numID = None
numIDx = materials[0].numID
numIDy = materials[1].numID
numIDz = materials[2].numID
build_triangle(x1, y1, z1, x2, y2, z2, x3, y3, z3, normal, thickness, grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages():
if thickness > 0:
if averaging:
dielectricsmoothing = 'on'
else:
dielectricsmoothing = 'off'
tqdm.write('Triangle with coordinates {:g}m {:g}m {:g}m, {:g}m {:g}m {:g}m, {:g}m {:g}m {:g}m and thickness {:g}m of material(s) {} created, dielectric smoothing is {}.'.format(x1, y1, z1, x2, y2, z2, x3, y3, z3, thickness, ', '.join(materialsrequested), dielectricsmoothing))
else:
tqdm.write('Triangle with coordinates {:g}m {:g}m {:g}m, {:g}m {:g}m {:g}m, {:g}m {:g}m {:g}m of material(s) {} created.'.format(x1, y1, z1, x2, y2, z2, x3, y3, z3, ', '.join(materialsrequested)))

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

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

查看文件

@@ -1,267 +1,267 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import os
import cython
import numpy as np
from scipy.constants import c
from scipy.constants import mu_0 as m0
from scipy.constants import epsilon_0 as e0
import sys
from .utilities import get_terminal_width
from .utilities import get_host_info
from pathlib import Path
from colorama import init
from colorama import Fore
from colorama import Style
init()
# Impedance of free space (Ohms)
z0 = np.sqrt(m0 / e0)
# General setting for the simulation
# inputfilepath: path to inputfile location
# outputfilepath: path to outputfile location
# messages: whether to print all messages as output to stdout or not
# progressbars: whether to show progress bars on stdoout or not
# mode: 2D TMx, 2D TMy, 2D TMz, or 3D
# cpu, cuda, opencl: solver type
# autotranslate: auto translate objects with main grid coordinates
# to their equivalent local grid coordinate within the subgrid. If this option is off
# users must specify sub-grid object point within the global subgrid space.
general = {'inputfilepath': 'gprMax', 'outputfilepath': 'gprMax', 'messages': True,
'progressbars': True, 'mode': '3D', 'cpu': True, 'cuda': False, 'opencl': False, 'autotranslate': False}
def is_messages():
"""Function to return messages."""
return general['messages']
# Store information about host machine
hostinfo = get_host_info()
# Store information for CUDA solver type
# gpus: information about any GPUs as a list of GPU objects
# snapsgpu2cpu: copy snapshot data from GPU to CPU during simulation
# N.B. This will happen if the requested snapshots are too large to fit
# on the memory of the GPU. If True this will slow performance significantly
cuda = {'gpus': None, 'snapsgpu2cpu': False}
# Numerical dispersion analysis parameters
# highestfreqthres: threshold (dB) down from maximum power (0dB) of main frequency used
# to calculate highest frequency for numerical dispersion analysis
# maxnumericaldisp: maximum allowable percentage physical phase-velocity phase error
# mingridsampling: minimum grid sampling of smallest wavelength for physical wave propagation
numdispersion = {'highestfreqthres': 40, 'maxnumericaldisp': 2, 'mingridsampling': 3}
# Materials
# maxpoles: Maximum number of dispersive material poles in a model
materials = {'maxpoles': 0, 'dispersivedtype': None, 'dispersiveCdtype': None}
# Data types
# Solid and ID arrays use 32-bit integers (0 to 4294967295)
# Rigid arrays use 8-bit integers (the smallest available type to store true/false)
# Fractal and dispersive coefficient arrays use complex numbers (complex)
# which are represented as two floats
# Main field arrays use floats (float_or_double) and complex numbers (complex)
# Precision of float_or_double and complex: single or double for numpy and C (CUDA) arrays
precision = 'double'
if precision == 'single':
dtypes = {'float_or_double': np.float32, 'complex': np.complex64,
'cython_float_or_double': cython.float, 'cython_complex': cython.floatcomplex,
'C_float_or_double': 'float', 'C_complex': 'pycuda::complex<float>'}
elif precision == 'double':
dtypes = {'float_or_double': np.float64, 'complex': np.complex128,
'cython_float_or_double': cython.double, 'cython_complex': cython.doublecomplex,
'C_float_or_double': 'double', 'C_complex': 'pycuda::complex<double>'}
class ModelConfig():
def __init__(self, sim_config, i):
self.sim_config = sim_config
self.reuse_geometry = False
# current model number (indexed from 0)
self.i = i
parts = self.sim_config.output_file_path.parts
if not sim_config.single_model:
# 1 indexed
self.appendmodelnumber = str(self.i + 1)
else:
self.appendmodelnumber = ''
# outputfilepath for specific model
self.output_file_path = Path(*parts[:-2], parts[-1] + self.appendmodelnumber)
self.output_file_path_ext = self.output_file_path.with_suffix('.out')
# make a snapshot directory
stem = parts[-1] + '_snaps' + self.appendmodelnumber
self.snapshot_dir = Path(*parts[:-2], stem)
inputfilestr_f = '\n--- Model {}/{}, input file: {}'
self.inputfilestr = inputfilestr_f.format(self.i + 1, self.sim_config.model_end, self.sim_config.input_file_path)
# string to print at start of each model run
self.next_model = Fore.GREEN + '{} {}\n'.format(self.inputfilestr, '-' * (get_terminal_width() - 1 - len(self.inputfilestr))) + Style.RESET_ALL
# Add the current model run to namespace that can be accessed by
# user in any Python code blocks in input file
#self.usernamespace['current_model_run'] = self.i + 1
def get_scene(self):
if self.sim_config.scenes:
return self.sim_config.scenes[self.i]
else: return None
def get_usernamespace(self):
return {'c': c,
'e0': e0,
'm0': m0,
'z0': z0,
'number_model_runs': self.sim_config.model_end + 1,
'current_model_run': self.i + 1,
'inputfile': self.sim_config.input_file_path.resolve()}
class SimulationConfig:
def __init__(self, args):
"""Adapter for args into Simulation level configuration"""
# adapt the arg properties to link smoothly with MPIRunner(), CPURunner() etc..
# args.inputfile
# args.n
# args.task
# args.restart
# args.mpi
# args.mpi_no_spawn
# args.mpicomm
# args.gpu
# args.benchmark
# args.geometry_only
# args.geometry_fixed
# args.write_processed
self.args = args
self.n_models = args.n
self.inputfile = args.inputfile
self.gpu = args.gpu
self.mpi = args.mpi
self.mpi_no_spawn = args.mpi_no_spawn
self.general = {}
self.general['messages'] = general['messages']
self.geometry_fixed = args.geometry_fixed
self.geometry_only = args.geometry_only
self.write_processed = args.write_processed
# subgrid parameter may not exist if user uses CLI api
try:
self.subgrid = args.subgrid
except AttributeError:
# this must be CLI user. No subgrids are available
self.subgrid = False
# scenes parameter may not exist if user uses CLI api
try:
self.scenes = args.scenes
except AttributeError:
self.scenes = []
# set more complex parameters
self.set_input_file_path()
self.set_output_file_path()
self.set_model_start_end()
self.set_single_model()
def set_single_model(self):
if self.model_start == 0 and self.model_end == 1:
self.single_model = True
else:
self.single_model = False
# for example
def set_model_start_end(self):
# set range for number of models to run (internally 0 index)
if self.args.task:
# Job array feeds args.n number of single tasks
modelstart = self.args.task - 1
modelend = self.args.task
elif self.args.restart:
modelstart = self.args.restart - 1
modelend = modelstart + self.args.n - 1
else:
modelstart = 0
modelend = modelstart + self.args.n
self.model_start = modelstart
self.model_end = modelend
def set_precision(self):
pass
def set_input_file_path(self):
"""Function to set to inputfile path"""
# if the API is in use an id for the simulation must be provided.
if self.args.inputfile is None:
self.input_file_path = Path(self.args.outputfile)
else:
self.input_file_path = Path(self.args.inputfile)
def set_output_file_path(self):
# output file can be provided by the user. if they havent provided None
# use the inputfilefile path instead
try:
self.output_file_path = Path(self.args.outputfile)
except AttributeError:
self.output_file_path = Path(self.args.inputfile)
class SimulationConfigMPI(SimulationConfig):
def __init__(self, args):
super().__init__(args)
def set_model_start_end(self):
# Set range for number of models to run
self.model_start = self.args.restart if self.args.restart else 1
self.model_end = self.modelstart + self.args.n
def create_simulation_config(args):
if not args.mpi and not args.mpi_no_spawn:
sc = SimulationConfig(args)
elif args.mpi:
sc = SimulationConfigMPI(args)
return sc
def create_model_config(sim_config, i):
mc = ModelConfig(sim_config, i)
return mc
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import os
import cython
import numpy as np
from scipy.constants import c
from scipy.constants import mu_0 as m0
from scipy.constants import epsilon_0 as e0
import sys
from .utilities import get_terminal_width
from .utilities import get_host_info
from pathlib import Path
from colorama import init
from colorama import Fore
from colorama import Style
init()
# Impedance of free space (Ohms)
z0 = np.sqrt(m0 / e0)
# General setting for the simulation
# inputfilepath: path to inputfile location
# outputfilepath: path to outputfile location
# messages: whether to print all messages as output to stdout or not
# progressbars: whether to show progress bars on stdoout or not
# mode: 2D TMx, 2D TMy, 2D TMz, or 3D
# cpu, cuda, opencl: solver type
# autotranslate: auto translate objects with main grid coordinates
# to their equivalent local grid coordinate within the subgrid. If this option is off
# users must specify sub-grid object point within the global subgrid space.
general = {'inputfilepath': 'gprMax', 'outputfilepath': 'gprMax', 'messages': True,
'progressbars': True, 'mode': '3D', 'cpu': True, 'cuda': False, 'opencl': False, 'autotranslate': False}
def is_messages():
"""Function to return messages."""
return general['messages']
# Store information about host machine
hostinfo = get_host_info()
# Store information for CUDA solver type
# gpus: information about any GPUs as a list of GPU objects
# snapsgpu2cpu: copy snapshot data from GPU to CPU during simulation
# N.B. This will happen if the requested snapshots are too large to fit
# on the memory of the GPU. If True this will slow performance significantly
cuda = {'gpus': None, 'snapsgpu2cpu': False}
# Numerical dispersion analysis parameters
# highestfreqthres: threshold (dB) down from maximum power (0dB) of main frequency used
# to calculate highest frequency for numerical dispersion analysis
# maxnumericaldisp: maximum allowable percentage physical phase-velocity phase error
# mingridsampling: minimum grid sampling of smallest wavelength for physical wave propagation
numdispersion = {'highestfreqthres': 40, 'maxnumericaldisp': 2, 'mingridsampling': 3}
# Materials
# maxpoles: Maximum number of dispersive material poles in a model
materials = {'maxpoles': 0, 'dispersivedtype': None, 'dispersiveCdtype': None}
# Data types
# Solid and ID arrays use 32-bit integers (0 to 4294967295)
# Rigid arrays use 8-bit integers (the smallest available type to store true/false)
# Fractal and dispersive coefficient arrays use complex numbers (complex)
# which are represented as two floats
# Main field arrays use floats (float_or_double) and complex numbers (complex)
# Precision of float_or_double and complex: single or double for numpy and C (CUDA) arrays
precision = 'double'
if precision == 'single':
dtypes = {'float_or_double': np.float32, 'complex': np.complex64,
'cython_float_or_double': cython.float, 'cython_complex': cython.floatcomplex,
'C_float_or_double': 'float', 'C_complex': 'pycuda::complex<float>'}
elif precision == 'double':
dtypes = {'float_or_double': np.float64, 'complex': np.complex128,
'cython_float_or_double': cython.double, 'cython_complex': cython.doublecomplex,
'C_float_or_double': 'double', 'C_complex': 'pycuda::complex<double>'}
class ModelConfig():
def __init__(self, sim_config, i):
self.sim_config = sim_config
self.reuse_geometry = False
# current model number (indexed from 0)
self.i = i
parts = self.sim_config.output_file_path.parts
if not sim_config.single_model:
# 1 indexed
self.appendmodelnumber = str(self.i + 1)
else:
self.appendmodelnumber = ''
# outputfilepath for specific model
self.output_file_path = Path(*parts[:-2], parts[-1] + self.appendmodelnumber)
self.output_file_path_ext = self.output_file_path.with_suffix('.out')
# make a snapshot directory
stem = parts[-1] + '_snaps' + self.appendmodelnumber
self.snapshot_dir = Path(*parts[:-2], stem)
inputfilestr_f = '\n--- Model {}/{}, input file: {}'
self.inputfilestr = inputfilestr_f.format(self.i + 1, self.sim_config.model_end, self.sim_config.input_file_path)
# string to print at start of each model run
self.next_model = Fore.GREEN + '{} {}\n'.format(self.inputfilestr, '-' * (get_terminal_width() - 1 - len(self.inputfilestr))) + Style.RESET_ALL
# Add the current model run to namespace that can be accessed by
# user in any Python code blocks in input file
#self.usernamespace['current_model_run'] = self.i + 1
def get_scene(self):
if self.sim_config.scenes:
return self.sim_config.scenes[self.i]
else: return None
def get_usernamespace(self):
return {'c': c,
'e0': e0,
'm0': m0,
'z0': z0,
'number_model_runs': self.sim_config.model_end + 1,
'current_model_run': self.i + 1,
'inputfile': self.sim_config.input_file_path.resolve()}
class SimulationConfig:
def __init__(self, args):
"""Adapter for args into Simulation level configuration"""
# adapt the arg properties to link smoothly with MPIRunner(), CPURunner() etc..
# args.inputfile
# args.n
# args.task
# args.restart
# args.mpi
# args.mpi_no_spawn
# args.mpicomm
# args.gpu
# args.benchmark
# args.geometry_only
# args.geometry_fixed
# args.write_processed
self.args = args
self.n_models = args.n
self.inputfile = args.inputfile
self.gpu = args.gpu
self.mpi = args.mpi
self.mpi_no_spawn = args.mpi_no_spawn
self.general = {}
self.general['messages'] = general['messages']
self.geometry_fixed = args.geometry_fixed
self.geometry_only = args.geometry_only
self.write_processed = args.write_processed
# subgrid parameter may not exist if user uses CLI api
try:
self.subgrid = args.subgrid
except AttributeError:
# this must be CLI user. No subgrids are available
self.subgrid = False
# scenes parameter may not exist if user uses CLI api
try:
self.scenes = args.scenes
except AttributeError:
self.scenes = []
# set more complex parameters
self.set_input_file_path()
self.set_output_file_path()
self.set_model_start_end()
self.set_single_model()
def set_single_model(self):
if self.model_start == 0 and self.model_end == 1:
self.single_model = True
else:
self.single_model = False
# for example
def set_model_start_end(self):
# set range for number of models to run (internally 0 index)
if self.args.task:
# Job array feeds args.n number of single tasks
modelstart = self.args.task - 1
modelend = self.args.task
elif self.args.restart:
modelstart = self.args.restart - 1
modelend = modelstart + self.args.n - 1
else:
modelstart = 0
modelend = modelstart + self.args.n
self.model_start = modelstart
self.model_end = modelend
def set_precision(self):
pass
def set_input_file_path(self):
"""Function to set to inputfile path"""
# if the API is in use an id for the simulation must be provided.
if self.args.inputfile is None:
self.input_file_path = Path(self.args.outputfile)
else:
self.input_file_path = Path(self.args.inputfile)
def set_output_file_path(self):
# output file can be provided by the user. if they havent provided None
# use the inputfilefile path instead
try:
self.output_file_path = Path(self.args.outputfile)
except AttributeError:
self.output_file_path = Path(self.args.inputfile)
class SimulationConfigMPI(SimulationConfig):
def __init__(self, args):
super().__init__(args)
def set_model_start_end(self):
# Set range for number of models to run
self.model_start = self.args.restart if self.args.restart else 1
self.model_end = self.modelstart + self.args.n
def create_simulation_config(args):
if not args.mpi and not args.mpi_no_spawn:
sc = SimulationConfig(args)
elif args.mpi:
sc = SimulationConfigMPI(args)
return sc
def create_model_config(sim_config, i):
mc = ModelConfig(sim_config, i)
return mc

查看文件

@@ -1,119 +1,119 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from .utilities import get_terminal_width
from .utilities import timer
from .model_build_run import ModelBuildRun
import datetime
from .config import create_model_config
from .solvers import create_solver
from .solvers import create_G
class Context():
def __init__(self, sim_config):
"""Context for the model to run in. Sub-class this with contexts
i.e. an MPI context.
Args:
sim_config (SimConfig): Simulation level configuration object.
solver (Solver): FDTD general solver object.
"""
self.sim_config = sim_config
self.model_range = range(sim_config.model_start,
sim_config.model_end)
self.tsimend = 0
self.tsimstart = 1
def run(self):
"""Function to run the simulation in the correct context."""
self.tsimstart = timer()
self._run()
self.tsimend = timer()
def print_time_report(self):
"""Function to print the total simulation time based on context."""
s = self.make_time_report(sim_time)
print(s)
def make_time_report(self):
"""Function to generate a string for the total simulation time bas"""
pass
class NoMPIContext(Context):
def _run(self):
"""Specialise how the models are farmed out."""
for i in self.model_range:
model_config = create_model_config(self.sim_config, i)
# always create a solver for the first model
# the next model to run only gets a new solver if the
# geometry is not re used.
if i != 0 and self.sim_config.geometry_fixed:
model_config.reuse_geometry = True
else:
G = create_G(self.sim_config)
model = ModelBuildRun(G, self.sim_config, model_config)
model.build()
solver = create_solver(G, self.sim_config)
if not self.sim_config.geometry_only:
model.run_model(solver)
def make_time_report(self):
"""Function to specialise the time reporting for the standard Simulation
context."""
sim_time = datetime.timedelta(seconds=self.tsimend - self.tsimstart)
s = '\n=== Simulation on {} completed in [HH:MM:SS]: {}'
s = s.format(self.simconfig.hostinfo['hostname'], sim_time)
return '{} {}\n'.format(s, '=' * (get_terminal_width() - 1 - len(s)))
class MPIContext(Context):
def _run(self):
pass
def make_time_report(self):
pass
class MPINoSpawnContext(Context):
def _run(self):
pass
def make_time_report(self):
pass
def create_context(sim_config):
"""Create a context in which to run the simulation. i.e MPI."""
if sim_config.mpi_no_spawn:
context = MPIContext(sim_config)
elif sim_config.mpi:
context = MPINoSpawnContext(sim_config)
else:
context = NoMPIContext(sim_config)
return context
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from .utilities import get_terminal_width
from .utilities import timer
from .model_build_run import ModelBuildRun
import datetime
from .config import create_model_config
from .solvers import create_solver
from .solvers import create_G
class Context():
def __init__(self, sim_config):
"""Context for the model to run in. Sub-class this with contexts
i.e. an MPI context.
Args:
sim_config (SimConfig): Simulation level configuration object.
solver (Solver): FDTD general solver object.
"""
self.sim_config = sim_config
self.model_range = range(sim_config.model_start,
sim_config.model_end)
self.tsimend = 0
self.tsimstart = 1
def run(self):
"""Function to run the simulation in the correct context."""
self.tsimstart = timer()
self._run()
self.tsimend = timer()
def print_time_report(self):
"""Function to print the total simulation time based on context."""
s = self.make_time_report(sim_time)
print(s)
def make_time_report(self):
"""Function to generate a string for the total simulation time bas"""
pass
class NoMPIContext(Context):
def _run(self):
"""Specialise how the models are farmed out."""
for i in self.model_range:
model_config = create_model_config(self.sim_config, i)
# always create a solver for the first model
# the next model to run only gets a new solver if the
# geometry is not re used.
if i != 0 and self.sim_config.geometry_fixed:
model_config.reuse_geometry = True
else:
G = create_G(self.sim_config)
model = ModelBuildRun(G, self.sim_config, model_config)
model.build()
solver = create_solver(G, self.sim_config)
if not self.sim_config.geometry_only:
model.run_model(solver)
def make_time_report(self):
"""Function to specialise the time reporting for the standard Simulation
context."""
sim_time = datetime.timedelta(seconds=self.tsimend - self.tsimstart)
s = '\n=== Simulation on {} completed in [HH:MM:SS]: {}'
s = s.format(self.simconfig.hostinfo['hostname'], sim_time)
return '{} {}\n'.format(s, '=' * (get_terminal_width() - 1 - len(s)))
class MPIContext(Context):
def _run(self):
pass
def make_time_report(self):
pass
class MPINoSpawnContext(Context):
def _run(self):
pass
def make_time_report(self):
pass
def create_context(sim_config):
"""Create a context in which to run the simulation. i.e MPI."""
if sim_config.mpi_no_spawn:
context = MPIContext(sim_config)
elif sim_config.mpi:
context = MPINoSpawnContext(sim_config)
else:
context = NoMPIContext(sim_config)
return context

查看文件

@@ -1,281 +1,281 @@
# Copyright (C) 2015-2017: 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
cpdef void cython_update_electric_os(
np.float64_t[:, :] updatecoeffsE,
np.uint32_t[:, :, :, :] ID,
int face,
int l_l,
int l_u,
int m_l,
int m_u,
size_t n_l,
size_t n_u,
int nwn,
size_t lookup_id,
np.float64_t[:, :, :] field,
np.float64_t[:, :, :] inc_field,
size_t co,
int sign_n,
int sign_f,
int mid,
int r,
int s,
int nb,
int nthreads
):
"""
Args:
subgrid: (Subgrid)
n: (String) the normal to the face to update
nwn: (Int) number of working cell in the normal direction
to the face
lookup_id: (Int) id of the H component we wish to update at
each node
field: (Numpy array) main grid field to be updated
inc_field: (Numpy array) incident sub_grid field
co: (Int) Coefficient used by gprMax update equations which
is specific to the field component being updated.
sign_n: (Int) 1 or -1 sign of the incident field on the near face.
sign_f: (Int) 1 or -1 sign of the incident field on the far face.
mid: (Bool) is the H node midway along the lower edge?
r = self.ratio
s = self.is_os_sep
nb = self.n_boundary_cells
"""
# Comments here as as per left and right face
cdef Py_ssize_t l, m, l_s, m_s, n_s_l, n_s_r, material_e_l, material_e_r, i0, j0, k0, i1, j1, k1, i2, j2, k2, i3, j3, k3
cdef int os
cdef double inc_n, inc_f
# surface normal index for the subgrid near face h nodes (left i index)
n_s_l = nb - s * r - r + r // 2
# surface normal index for the subgrid far face h nodes (right i index)
n_s_r = nb + nwn + s * r + r // 2
# OS at the left face
os = nb - r * s
# Iterate over a slice of the main grid using dummy indices
for l in prange(l_l, l_u, nogil=True, schedule='static', num_threads=nthreads):
# Calculate the subgrid j component of the H nodes
# i.e. Hz node of the left or right face
if mid == 1:
l_s = os + (l - l_l) * r + r // 2
# i.e. the Hy node of the left or right face
else:
l_s = os + (l - l_l) * r
for m in range(m_l, m_u):
# Calculate the subgrid k component of the H nodes
if mid == 1:
m_s = os + (m - m_l) * r
else:
m_s = os + (m - m_l) * r + r // 2
# left and right
if face == 2:
# main grid index
i0, j0, k0 = n_l, l, m
# equivalent subgrid index
i1, j1, k1 = n_s_l, l_s, m_s
i2, j2, k2 = n_u, l, m
i3, j3, k3 = n_s_r, l_s, m_s
# front and back
if face == 3:
i0, j0, k0 = l, n_l, m
i1, j1, k1 = l_s, n_s_l, m_s
i2, j2, k2 = l, n_u, m
i3, j3, k3 = l_s, n_s_r, m_s
# top bottom
if face == 1:
i0, j0, k0 = l, m, n_l
i1, j1, k1 = l_s, m_s, n_s_l
i2, j2, k2 = l, m, n_u
i3, j3, k3 = l_s, m_s, n_s_r
# Update the left face
# Get the material at main grid index
material_e_l = ID[lookup_id, i0, j0, k0]
# Get the associated indident field from the subgrid
inc_n = inc_field[i1, j1, k1] * sign_n
# Update the main grid E field with the corrected H field
field[i0, j0, k0] += updatecoeffsE[material_e_l, co] * inc_n
# Update the right face
material_e_r = ID[lookup_id, i2, j2, k2]
inc_f = inc_field[i3, j3, k3] * sign_f
field[i2, j2, k2] += updatecoeffsE[material_e_r, co] * inc_f
cpdef void cython_update_magnetic_os(
np.float64_t[:, :] updatecoeffsH,
np.uint32_t[:, :, :, :] ID,
int face,
int l_l,
int l_u,
int m_l,
int m_u,
size_t n_l,
size_t n_u,
int nwn,
size_t lookup_id,
np.float64_t[:, :, :] field,
np.float64_t[:, :, :] inc_field,
size_t co,
int sign_n,
int sign_f,
int mid,
int r,
int s,
int nb,
int nthreads
):
"""
int r ratio,
int s is_os_sep,
int nb n_boundary_cells
"""
cdef Py_ssize_t l, m, l_s, m_s, n_s_l, n_s_r, material_e_l, material_e_r, i0, j0, k0, i1, j1, k1, i2, j2, k2, i3, j3, k3
cdef int os
cdef double inc_n, inc_f
# i index (normal to os) for the subgrid near face e node
n_s_l = nb - r * s
# Normal index for the subgrid far face e node
n_s_r = nb + nwn + s * r
# os inner index for the sub grid
os = nb - r * s
for l in prange(l_l, l_u, nogil=True, schedule='static', num_threads=nthreads):
# y coord of the Ex field component
if mid == 1:
l_s = os + (l - l_l) * r + r // 2
# y coord of the Ez field component
else:
l_s = os + (l - l_l) * r
for m in range(m_l, m_u):
# z coordinate of the Ex node in the subgrid
if mid == 1:
m_s = os + (m - m_l) * r
else:
m_s = os + (m - m_l) * r + r // 2
# associate the given indices with their i, j, k values
# left and right
if face == 2:
# main grid index
i0, j0, k0 = n_l, l, m
# equivalent subgrid index
i1, j1, k1 = n_s_l, l_s, m_s
i2, j2, k2 = n_u, l, m
i3, j3, k3 = n_s_r, l_s, m_s
# front and back
if face == 3:
i0, j0, k0 = l, n_l, m
i1, j1, k1 = l_s, n_s_l, m_s
i2, j2, k2 = l, n_u, m
i3, j3, k3 = l_s, n_s_r, m_s
# top bottom
if face == 1:
i0, j0, k0 = l, m, n_l
i1, j1, k1 = l_s, m_s, n_s_l
i2, j2, k2 = l, m, n_u
i3, j3, k3 = l_s, m_s, n_s_r
material_e_l = ID[lookup_id, i0, j0, k0]
inc_n = inc_field[i1, j1, k1] * sign_n
# make sure these are the correct grid
field[i0, j0, k0] += updatecoeffsH[material_e_l, co] * inc_n
# Far face
material_e_r = ID[lookup_id, i2, j2, k2]
inc_f = inc_field[i3, j3, k3] * sign_f
field[i2, j2, k2] += updatecoeffsH[material_e_r, co] * inc_f
cpdef void cython_update_is(
int nwx,
int nwy,
int nwz,
np.float64_t[:, :] updatecoeffsE,
np.uint32_t[:, :, :, :] ID,
int n,
int offset,
int nwl,
int nwm,
int nwn,
int face,
np.float64_t[:, :, :] field,
np.float64_t[:, :] inc_field_l,
np.float64_t[:, :] inc_field_u,
Py_ssize_t lookup_id,
int sign_l,
int sign_u,
Py_ssize_t co,
int nthreads
):
cdef Py_ssize_t l, m, i1, j1, k1, i2, j2, k2, field_material_l, field_material_u, inc_i, inc_j
cdef double inc_l, inc_u, f_l, f_u
# for inner faces H nodes are 1 cell before n boundary cells
cdef int n_o = n + offset
for l in prange(n, nwl + n, nogil=True, schedule='static', num_threads=nthreads):
for m in range(n, nwm + n):
# bottom and top
if face == 1:
i1, j1, k1 = l, m, n_o
i2, j2, k2 = l, m, n + nwz
# left and right
if face == 2:
i1, j1, k1 = n_o, l, m
i2, j2, k2 = n + nwx, l, m
# front and back
if face == 3:
i1, j1, k1 = l, n_o, m
i2, j2, k2 = l, n + nwy, m
inc_i = l - n
inc_j = m - n
field_material_l = ID[lookup_id, i1, j1, k1]
inc_l = inc_field_l[inc_i, inc_j]
# Additional field at i, j, k
f_l = updatecoeffsE[field_material_l, co] * inc_l * sign_l
# Set the new value
field[i1, j1, k1] += f_l
field_material_u = ID[lookup_id, i2, j2, k2]
inc_u = inc_field_u[inc_i, inc_j]
# Additional field at i, j, k
f_u = updatecoeffsE[field_material_u, co] * inc_u * sign_u
# Set the new value
field[i2, j2, k2] += f_u
# Copyright (C) 2015-2017: 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
cpdef void cython_update_electric_os(
np.float64_t[:, :] updatecoeffsE,
np.uint32_t[:, :, :, :] ID,
int face,
int l_l,
int l_u,
int m_l,
int m_u,
size_t n_l,
size_t n_u,
int nwn,
size_t lookup_id,
np.float64_t[:, :, :] field,
np.float64_t[:, :, :] inc_field,
size_t co,
int sign_n,
int sign_f,
int mid,
int r,
int s,
int nb,
int nthreads
):
"""
Args:
subgrid: (Subgrid)
n: (String) the normal to the face to update
nwn: (Int) number of working cell in the normal direction
to the face
lookup_id: (Int) id of the H component we wish to update at
each node
field: (Numpy array) main grid field to be updated
inc_field: (Numpy array) incident sub_grid field
co: (Int) Coefficient used by gprMax update equations which
is specific to the field component being updated.
sign_n: (Int) 1 or -1 sign of the incident field on the near face.
sign_f: (Int) 1 or -1 sign of the incident field on the far face.
mid: (Bool) is the H node midway along the lower edge?
r = self.ratio
s = self.is_os_sep
nb = self.n_boundary_cells
"""
# Comments here as as per left and right face
cdef Py_ssize_t l, m, l_s, m_s, n_s_l, n_s_r, material_e_l, material_e_r, i0, j0, k0, i1, j1, k1, i2, j2, k2, i3, j3, k3
cdef int os
cdef double inc_n, inc_f
# surface normal index for the subgrid near face h nodes (left i index)
n_s_l = nb - s * r - r + r // 2
# surface normal index for the subgrid far face h nodes (right i index)
n_s_r = nb + nwn + s * r + r // 2
# OS at the left face
os = nb - r * s
# Iterate over a slice of the main grid using dummy indices
for l in prange(l_l, l_u, nogil=True, schedule='static', num_threads=nthreads):
# Calculate the subgrid j component of the H nodes
# i.e. Hz node of the left or right face
if mid == 1:
l_s = os + (l - l_l) * r + r // 2
# i.e. the Hy node of the left or right face
else:
l_s = os + (l - l_l) * r
for m in range(m_l, m_u):
# Calculate the subgrid k component of the H nodes
if mid == 1:
m_s = os + (m - m_l) * r
else:
m_s = os + (m - m_l) * r + r // 2
# left and right
if face == 2:
# main grid index
i0, j0, k0 = n_l, l, m
# equivalent subgrid index
i1, j1, k1 = n_s_l, l_s, m_s
i2, j2, k2 = n_u, l, m
i3, j3, k3 = n_s_r, l_s, m_s
# front and back
if face == 3:
i0, j0, k0 = l, n_l, m
i1, j1, k1 = l_s, n_s_l, m_s
i2, j2, k2 = l, n_u, m
i3, j3, k3 = l_s, n_s_r, m_s
# top bottom
if face == 1:
i0, j0, k0 = l, m, n_l
i1, j1, k1 = l_s, m_s, n_s_l
i2, j2, k2 = l, m, n_u
i3, j3, k3 = l_s, m_s, n_s_r
# Update the left face
# Get the material at main grid index
material_e_l = ID[lookup_id, i0, j0, k0]
# Get the associated indident field from the subgrid
inc_n = inc_field[i1, j1, k1] * sign_n
# Update the main grid E field with the corrected H field
field[i0, j0, k0] += updatecoeffsE[material_e_l, co] * inc_n
# Update the right face
material_e_r = ID[lookup_id, i2, j2, k2]
inc_f = inc_field[i3, j3, k3] * sign_f
field[i2, j2, k2] += updatecoeffsE[material_e_r, co] * inc_f
cpdef void cython_update_magnetic_os(
np.float64_t[:, :] updatecoeffsH,
np.uint32_t[:, :, :, :] ID,
int face,
int l_l,
int l_u,
int m_l,
int m_u,
size_t n_l,
size_t n_u,
int nwn,
size_t lookup_id,
np.float64_t[:, :, :] field,
np.float64_t[:, :, :] inc_field,
size_t co,
int sign_n,
int sign_f,
int mid,
int r,
int s,
int nb,
int nthreads
):
"""
int r ratio,
int s is_os_sep,
int nb n_boundary_cells
"""
cdef Py_ssize_t l, m, l_s, m_s, n_s_l, n_s_r, material_e_l, material_e_r, i0, j0, k0, i1, j1, k1, i2, j2, k2, i3, j3, k3
cdef int os
cdef double inc_n, inc_f
# i index (normal to os) for the subgrid near face e node
n_s_l = nb - r * s
# Normal index for the subgrid far face e node
n_s_r = nb + nwn + s * r
# os inner index for the sub grid
os = nb - r * s
for l in prange(l_l, l_u, nogil=True, schedule='static', num_threads=nthreads):
# y coord of the Ex field component
if mid == 1:
l_s = os + (l - l_l) * r + r // 2
# y coord of the Ez field component
else:
l_s = os + (l - l_l) * r
for m in range(m_l, m_u):
# z coordinate of the Ex node in the subgrid
if mid == 1:
m_s = os + (m - m_l) * r
else:
m_s = os + (m - m_l) * r + r // 2
# associate the given indices with their i, j, k values
# left and right
if face == 2:
# main grid index
i0, j0, k0 = n_l, l, m
# equivalent subgrid index
i1, j1, k1 = n_s_l, l_s, m_s
i2, j2, k2 = n_u, l, m
i3, j3, k3 = n_s_r, l_s, m_s
# front and back
if face == 3:
i0, j0, k0 = l, n_l, m
i1, j1, k1 = l_s, n_s_l, m_s
i2, j2, k2 = l, n_u, m
i3, j3, k3 = l_s, n_s_r, m_s
# top bottom
if face == 1:
i0, j0, k0 = l, m, n_l
i1, j1, k1 = l_s, m_s, n_s_l
i2, j2, k2 = l, m, n_u
i3, j3, k3 = l_s, m_s, n_s_r
material_e_l = ID[lookup_id, i0, j0, k0]
inc_n = inc_field[i1, j1, k1] * sign_n
# make sure these are the correct grid
field[i0, j0, k0] += updatecoeffsH[material_e_l, co] * inc_n
# Far face
material_e_r = ID[lookup_id, i2, j2, k2]
inc_f = inc_field[i3, j3, k3] * sign_f
field[i2, j2, k2] += updatecoeffsH[material_e_r, co] * inc_f
cpdef void cython_update_is(
int nwx,
int nwy,
int nwz,
np.float64_t[:, :] updatecoeffsE,
np.uint32_t[:, :, :, :] ID,
int n,
int offset,
int nwl,
int nwm,
int nwn,
int face,
np.float64_t[:, :, :] field,
np.float64_t[:, :] inc_field_l,
np.float64_t[:, :] inc_field_u,
Py_ssize_t lookup_id,
int sign_l,
int sign_u,
Py_ssize_t co,
int nthreads
):
cdef Py_ssize_t l, m, i1, j1, k1, i2, j2, k2, field_material_l, field_material_u, inc_i, inc_j
cdef double inc_l, inc_u, f_l, f_u
# for inner faces H nodes are 1 cell before n boundary cells
cdef int n_o = n + offset
for l in prange(n, nwl + n, nogil=True, schedule='static', num_threads=nthreads):
for m in range(n, nwm + n):
# bottom and top
if face == 1:
i1, j1, k1 = l, m, n_o
i2, j2, k2 = l, m, n + nwz
# left and right
if face == 2:
i1, j1, k1 = n_o, l, m
i2, j2, k2 = n + nwx, l, m
# front and back
if face == 3:
i1, j1, k1 = l, n_o, m
i2, j2, k2 = l, n + nwy, m
inc_i = l - n
inc_j = m - n
field_material_l = ID[lookup_id, i1, j1, k1]
inc_l = inc_field_l[inc_i, inc_j]
# Additional field at i, j, k
f_l = updatecoeffsE[field_material_l, co] * inc_l * sign_l
# Set the new value
field[i1, j1, k1] += f_l
field_material_u = ID[lookup_id, i2, j2, k2]
inc_u = inc_field_u[inc_i, inc_j]
# Additional field at i, j, k
f_u = updatecoeffsE[field_material_u, co] * inc_u * sign_u
# Set the new value
field[i2, j2, k2] += f_u

查看文件

@@ -1,48 +1,48 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import sys
from colorama import init
from colorama import Fore
init()
sys.tracebacklimit = None
class GeneralError(ValueError):
"""Handles general errors. Subclasses the ValueError class."""
def __init__(self, message, *args):
self.message = message
super(GeneralError, self).__init__(message, *args)
print(Fore.RED)
class CmdInputError(ValueError):
"""Handles errors in user specified commands. Subclasses the ValueError
class.
"""
def __init__(self, message, *args):
self.message = message
super(CmdInputError, self).__init__(message, *args)
print(Fore.RED)
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import sys
from colorama import init
from colorama import Fore
init()
sys.tracebacklimit = None
class GeneralError(ValueError):
"""Handles general errors. Subclasses the ValueError class."""
def __init__(self, message, *args):
self.message = message
super(GeneralError, self).__init__(message, *args)
print(Fore.RED)
class CmdInputError(ValueError):
"""Handles errors in user specified commands. Subclasses the ValueError
class.
"""
def __init__(self, message, *args):
self.message = message
super(CmdInputError, self).__init__(message, *args)
print(Fore.RED)

查看文件

@@ -1,202 +1,202 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from string import Template
from pathlib import Path
import h5py
from ._version import __version__
def store_outputs(G):
"""Stores field component values for every receiver and transmission line.
Args:
iteration (int): Current iteration number.
Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic
field values.
G (class): Grid class instance - holds essential parameters describing
the model.
"""
iteration = G.iteration
#import pdb; pdb.set_trace()
Ex, Ey, Ez, Hx, Hy, Hz = G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz
for rx in G.rxs:
for output in rx.outputs:
# Store electric or magnetic field components
if 'I' not in output:
field = locals()[output]
rx.outputs[output][iteration] = field[rx.xcoord, rx.ycoord, rx.zcoord]
# Store current component
else:
func = globals()[output]
rx.outputs[output][iteration] = func(rx.xcoord, rx.ycoord, rx.zcoord,
Hx, Hy, Hz, G)
for tl in G.transmissionlines:
tl.Vtotal[iteration] = tl.voltage[tl.antpos]
tl.Itotal[iteration] = tl.current[tl.antpos]
kernel_template_store_outputs = Template("""
// Macros for converting subscripts to linear index:
#define INDEX2D_RXCOORDS(m, n) (m)*($NY_RXCOORDS)+(n)
#define INDEX3D_RXS(i, j, k) (i)*($NY_RXS)*($NZ_RXS)+(j)*($NZ_RXS)+(k)
#define INDEX3D_FIELDS(i, j, k) (i)*($NY_FIELDS)*($NZ_FIELDS)+(j)*($NZ_FIELDS)+(k)
//////////////////////////////////////////////////////
// Stores field component values for every receiver //
//////////////////////////////////////////////////////
__global__ void store_outputs(int NRX, int iteration, const int* __restrict__ rxcoords, $REAL *rxs, const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz) {
// This function stores field component values for every receiver in the model.
//
// Args:
// NRX: Total number of receivers in the model
// rxs: Array to store field components for receivers - rows are field components; columns are iterations; pages are receivers
// E, H: Access to field component arrays
// Obtain the linear index corresponding to the current thread and use for each receiver
int rx = blockIdx.x * blockDim.x + threadIdx.x;
int i, j, k;
if (rx < NRX) {
i = rxcoords[INDEX2D_RXCOORDS(rx,0)];
j = rxcoords[INDEX2D_RXCOORDS(rx,1)];
k = rxcoords[INDEX2D_RXCOORDS(rx,2)];
rxs[INDEX3D_RXS(0,iteration,rx)] = Ex[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(1,iteration,rx)] = Ey[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(2,iteration,rx)] = Ez[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(3,iteration,rx)] = Hx[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(4,iteration,rx)] = Hy[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(5,iteration,rx)] = Hz[INDEX3D_FIELDS(i,j,k)];
}
}
""")
def write_hdf5_outputfile(outputfile, G):
write_hdf5_main_grid_outputfile(outputfile, G)
write_hdf5_sub_grid_outputfile(outputfile, G)
def write_hdf5_main_grid_outputfile(outputfile, G):
"""Write an output file in HDF5 format.
Args:
outputfile (str): Name of the output file.
Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic field values.
G (class): Grid class instance - holds essential parameters describing the model.
"""
write_data(outputfile, G)
def write_hdf5_sub_grid_outputfile(outputfile, G):
"""Write an output file in HDF5 format.
Args:
outputfile (str): Name of the output file.
Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic field values.
G (class): Grid class instance - holds essential parameters describing the model.
"""
stem = outputfile.stem
suffix = outputfile.suffix
parent = outputfile.parent
for sg in G.subgrids:
# create an outputfile for each subgrid
fp = stem + '_' + sg.name + suffix
fp = parent / Path(fp)
f = write_data(fp, sg)
# write some additional meta data about the subgrid
f.attrs['is_os_sep'] = sg.is_os_sep
f.attrs['pml_separation'] = sg.pml_separation
f.attrs['subgrid_pml_thickness'] = sg.pmlthickness['x0']
f.attrs['filter'] = sg.filter
f.attrs['ratio'] = sg.ratio
f.attrs['interpolation'] = sg.interpolation
def write_data(outputfile, G):
"""Write an output file in HDF5 format.
Args:
outputfile (str): Name of the output file.
Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic
field values.
G (class): Grid class instance - holds essential parameters describing
the model.
"""
f = h5py.File(outputfile, 'w')
f.attrs['gprMax'] = __version__
f.attrs['Title'] = G.title
f.attrs['Iterations'] = G.iterations
f.attrs['nx_ny_nz'] = (G.nx, G.ny, G.nz)
f.attrs['dx_dy_dz'] = (G.dx, G.dy, G.dz)
f.attrs['dt'] = G.dt
nsrc = len(G.voltagesources + G.hertziandipoles + G.magneticdipoles + G.transmissionlines)
f.attrs['nsrc'] = nsrc
f.attrs['nrx'] = len(G.rxs)
f.attrs['srcsteps'] = G.srcsteps
f.attrs['rxsteps'] = G.rxsteps
# Create group for sources (except transmission lines); add type and positional data attributes
srclist = G.voltagesources + G.hertziandipoles + G.magneticdipoles
for srcindex, src in enumerate(srclist):
grp = f.create_group('/srcs/src' + str(srcindex + 1))
grp.attrs['Type'] = type(src).__name__
grp.attrs['Position'] = (src.xcoord * G.dx, src.ycoord * G.dy, src.zcoord * G.dz)
# Create group for transmission lines; add positional data, line resistance and
# line discretisation attributes; write arrays for line voltages and currents
for tlindex, tl in enumerate(G.transmissionlines):
grp = f.create_group('/tls/tl' + str(tlindex + 1))
grp.attrs['Position'] = (tl.xcoord * G.dx, tl.ycoord * G.dy, tl.zcoord * G.dz)
grp.attrs['Resistance'] = tl.resistance
grp.attrs['dl'] = tl.dl
# Save incident voltage and current
grp['Vinc'] = tl.Vinc
grp['Iinc'] = tl.Iinc
# Save total voltage and current
f['/tls/tl' + str(tlindex + 1) + '/Vtotal'] = tl.Vtotal
f['/tls/tl' + str(tlindex + 1) + '/Itotal'] = tl.Itotal
# Create group, add positional data and write field component arrays for receivers
for rxindex, rx in enumerate(G.rxs):
grp = f.create_group('/rxs/rx' + str(rxindex + 1))
if rx.ID:
grp.attrs['Name'] = rx.ID
grp.attrs['Position'] = (rx.xcoord * G.dx, rx.ycoord * G.dy, rx.zcoord * G.dz)
for output in rx.outputs:
f['/rxs/rx' + str(rxindex + 1) + '/' + output] = rx.outputs[output]
return f
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from string import Template
from pathlib import Path
import h5py
from ._version import __version__
def store_outputs(G):
"""Stores field component values for every receiver and transmission line.
Args:
iteration (int): Current iteration number.
Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic
field values.
G (class): Grid class instance - holds essential parameters describing
the model.
"""
iteration = G.iteration
#import pdb; pdb.set_trace()
Ex, Ey, Ez, Hx, Hy, Hz = G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz
for rx in G.rxs:
for output in rx.outputs:
# Store electric or magnetic field components
if 'I' not in output:
field = locals()[output]
rx.outputs[output][iteration] = field[rx.xcoord, rx.ycoord, rx.zcoord]
# Store current component
else:
func = globals()[output]
rx.outputs[output][iteration] = func(rx.xcoord, rx.ycoord, rx.zcoord,
Hx, Hy, Hz, G)
for tl in G.transmissionlines:
tl.Vtotal[iteration] = tl.voltage[tl.antpos]
tl.Itotal[iteration] = tl.current[tl.antpos]
kernel_template_store_outputs = Template("""
// Macros for converting subscripts to linear index:
#define INDEX2D_RXCOORDS(m, n) (m)*($NY_RXCOORDS)+(n)
#define INDEX3D_RXS(i, j, k) (i)*($NY_RXS)*($NZ_RXS)+(j)*($NZ_RXS)+(k)
#define INDEX3D_FIELDS(i, j, k) (i)*($NY_FIELDS)*($NZ_FIELDS)+(j)*($NZ_FIELDS)+(k)
//////////////////////////////////////////////////////
// Stores field component values for every receiver //
//////////////////////////////////////////////////////
__global__ void store_outputs(int NRX, int iteration, const int* __restrict__ rxcoords, $REAL *rxs, const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz) {
// This function stores field component values for every receiver in the model.
//
// Args:
// NRX: Total number of receivers in the model
// rxs: Array to store field components for receivers - rows are field components; columns are iterations; pages are receivers
// E, H: Access to field component arrays
// Obtain the linear index corresponding to the current thread and use for each receiver
int rx = blockIdx.x * blockDim.x + threadIdx.x;
int i, j, k;
if (rx < NRX) {
i = rxcoords[INDEX2D_RXCOORDS(rx,0)];
j = rxcoords[INDEX2D_RXCOORDS(rx,1)];
k = rxcoords[INDEX2D_RXCOORDS(rx,2)];
rxs[INDEX3D_RXS(0,iteration,rx)] = Ex[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(1,iteration,rx)] = Ey[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(2,iteration,rx)] = Ez[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(3,iteration,rx)] = Hx[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(4,iteration,rx)] = Hy[INDEX3D_FIELDS(i,j,k)];
rxs[INDEX3D_RXS(5,iteration,rx)] = Hz[INDEX3D_FIELDS(i,j,k)];
}
}
""")
def write_hdf5_outputfile(outputfile, G):
write_hdf5_main_grid_outputfile(outputfile, G)
write_hdf5_sub_grid_outputfile(outputfile, G)
def write_hdf5_main_grid_outputfile(outputfile, G):
"""Write an output file in HDF5 format.
Args:
outputfile (str): Name of the output file.
Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic field values.
G (class): Grid class instance - holds essential parameters describing the model.
"""
write_data(outputfile, G)
def write_hdf5_sub_grid_outputfile(outputfile, G):
"""Write an output file in HDF5 format.
Args:
outputfile (str): Name of the output file.
Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic field values.
G (class): Grid class instance - holds essential parameters describing the model.
"""
stem = outputfile.stem
suffix = outputfile.suffix
parent = outputfile.parent
for sg in G.subgrids:
# create an outputfile for each subgrid
fp = stem + '_' + sg.name + suffix
fp = parent / Path(fp)
f = write_data(fp, sg)
# write some additional meta data about the subgrid
f.attrs['is_os_sep'] = sg.is_os_sep
f.attrs['pml_separation'] = sg.pml_separation
f.attrs['subgrid_pml_thickness'] = sg.pmlthickness['x0']
f.attrs['filter'] = sg.filter
f.attrs['ratio'] = sg.ratio
f.attrs['interpolation'] = sg.interpolation
def write_data(outputfile, G):
"""Write an output file in HDF5 format.
Args:
outputfile (str): Name of the output file.
Ex, Ey, Ez, Hx, Hy, Hz (memory view): Current electric and magnetic
field values.
G (class): Grid class instance - holds essential parameters describing
the model.
"""
f = h5py.File(outputfile, 'w')
f.attrs['gprMax'] = __version__
f.attrs['Title'] = G.title
f.attrs['Iterations'] = G.iterations
f.attrs['nx_ny_nz'] = (G.nx, G.ny, G.nz)
f.attrs['dx_dy_dz'] = (G.dx, G.dy, G.dz)
f.attrs['dt'] = G.dt
nsrc = len(G.voltagesources + G.hertziandipoles + G.magneticdipoles + G.transmissionlines)
f.attrs['nsrc'] = nsrc
f.attrs['nrx'] = len(G.rxs)
f.attrs['srcsteps'] = G.srcsteps
f.attrs['rxsteps'] = G.rxsteps
# Create group for sources (except transmission lines); add type and positional data attributes
srclist = G.voltagesources + G.hertziandipoles + G.magneticdipoles
for srcindex, src in enumerate(srclist):
grp = f.create_group('/srcs/src' + str(srcindex + 1))
grp.attrs['Type'] = type(src).__name__
grp.attrs['Position'] = (src.xcoord * G.dx, src.ycoord * G.dy, src.zcoord * G.dz)
# Create group for transmission lines; add positional data, line resistance and
# line discretisation attributes; write arrays for line voltages and currents
for tlindex, tl in enumerate(G.transmissionlines):
grp = f.create_group('/tls/tl' + str(tlindex + 1))
grp.attrs['Position'] = (tl.xcoord * G.dx, tl.ycoord * G.dy, tl.zcoord * G.dz)
grp.attrs['Resistance'] = tl.resistance
grp.attrs['dl'] = tl.dl
# Save incident voltage and current
grp['Vinc'] = tl.Vinc
grp['Iinc'] = tl.Iinc
# Save total voltage and current
f['/tls/tl' + str(tlindex + 1) + '/Vtotal'] = tl.Vtotal
f['/tls/tl' + str(tlindex + 1) + '/Itotal'] = tl.Itotal
# Create group, add positional data and write field component arrays for receivers
for rxindex, rx in enumerate(G.rxs):
grp = f.create_group('/rxs/rx' + str(rxindex + 1))
if rx.ID:
grp.attrs['Name'] = rx.ID
grp.attrs['Position'] = (rx.xcoord * G.dx, rx.ycoord * G.dy, rx.zcoord * G.dz)
for output in rx.outputs:
f['/rxs/rx' + str(rxindex + 1) + '/' + output] = rx.outputs[output]
return f

查看文件

@@ -1,94 +1,94 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from .config import create_simulation_config
from .contexts import create_context
from .solvers import create_solver
import argparse
def api(
scenes=None,
id=None,
inputfile=None,
outputfile=None,
n=1,
task=None,
restart=None,
mpi=False,
mpi_no_spawn=False,
mpicomm=None,
gpu=None,
subgrid=None,
benchmark=False,
geometry_only=False,
geometry_fixed=False,
write_processed=False,
):
"""If installed as a module this is the entry point."""
class ImportArguments:
pass
args = ImportArguments()
args.scenes = scenes
args.inputfile = inputfile
args.outputfile = outputfile
args.n = n
args.task = task
args.restart = restart
args.mpi = mpi
args.mpi_no_spawn = mpi_no_spawn
args.mpicomm = mpicomm
args.gpu = gpu
args.subgrid=subgrid
args.benchmark = benchmark
args.geometry_only = geometry_only
args.geometry_fixed = geometry_fixed
args.write_processed = write_processed
run_main(args)
def main():
"""This is the main function for gprMax."""
# Parse command line arguments
parser = argparse.ArgumentParser(prog='gprMax', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('inputfile', help='path to, and name of inputfile or file object')
parser.add_argument('-n', default=1, type=int, help='number of times to run the input file, e.g. to create a B-scan')
parser.add_argument('-task', type=int, help='task identifier (model number) for job array on Open Grid Scheduler/Grid Engine (http://gridscheduler.sourceforge.net/index.html)')
parser.add_argument('-restart', type=int, help='model number to restart from, e.g. when creating B-scan')
parser.add_argument('-mpi', type=int, help='number of MPI tasks, i.e. master + workers')
parser.add_argument('--mpi-no-spawn', action='store_true', default=False, help='flag to use MPI without spawn mechanism')
parser.add_argument('--mpi-worker', action='store_true', default=False, help=argparse.SUPPRESS)
parser.add_argument('-gpu', type=int, action='append', nargs='*', help='flag to use Nvidia GPU or option to give list of device ID(s)')
parser.add_argument('-benchmark', action='store_true', default=False, help='flag to switch on benchmarking mode')
parser.add_argument('--geometry-only', action='store_true', default=False, help='flag to only build model and produce geometry file(s)')
parser.add_argument('--geometry-fixed', action='store_true', default=False, help='flag to not reprocess model geometry, e.g. for B-scans where the geometry is fixed')
parser.add_argument('--write-processed', action='store_true', default=False, help='flag to write an input file after any Python code and include commands in the original input file have been processed')
args = parser.parse_args()
run_main(args)
def run_main(args):
sim_config = create_simulation_config(args)
context = create_context(sim_config)
context.run()
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from .config import create_simulation_config
from .contexts import create_context
from .solvers import create_solver
import argparse
def api(
scenes=None,
id=None,
inputfile=None,
outputfile=None,
n=1,
task=None,
restart=None,
mpi=False,
mpi_no_spawn=False,
mpicomm=None,
gpu=None,
subgrid=None,
benchmark=False,
geometry_only=False,
geometry_fixed=False,
write_processed=False,
):
"""If installed as a module this is the entry point."""
class ImportArguments:
pass
args = ImportArguments()
args.scenes = scenes
args.inputfile = inputfile
args.outputfile = outputfile
args.n = n
args.task = task
args.restart = restart
args.mpi = mpi
args.mpi_no_spawn = mpi_no_spawn
args.mpicomm = mpicomm
args.gpu = gpu
args.subgrid=subgrid
args.benchmark = benchmark
args.geometry_only = geometry_only
args.geometry_fixed = geometry_fixed
args.write_processed = write_processed
run_main(args)
def main():
"""This is the main function for gprMax."""
# Parse command line arguments
parser = argparse.ArgumentParser(prog='gprMax', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('inputfile', help='path to, and name of inputfile or file object')
parser.add_argument('-n', default=1, type=int, help='number of times to run the input file, e.g. to create a B-scan')
parser.add_argument('-task', type=int, help='task identifier (model number) for job array on Open Grid Scheduler/Grid Engine (http://gridscheduler.sourceforge.net/index.html)')
parser.add_argument('-restart', type=int, help='model number to restart from, e.g. when creating B-scan')
parser.add_argument('-mpi', type=int, help='number of MPI tasks, i.e. master + workers')
parser.add_argument('--mpi-no-spawn', action='store_true', default=False, help='flag to use MPI without spawn mechanism')
parser.add_argument('--mpi-worker', action='store_true', default=False, help=argparse.SUPPRESS)
parser.add_argument('-gpu', type=int, action='append', nargs='*', help='flag to use Nvidia GPU or option to give list of device ID(s)')
parser.add_argument('-benchmark', action='store_true', default=False, help='flag to switch on benchmarking mode')
parser.add_argument('--geometry-only', action='store_true', default=False, help='flag to only build model and produce geometry file(s)')
parser.add_argument('--geometry-fixed', action='store_true', default=False, help='flag to not reprocess model geometry, e.g. for B-scans where the geometry is fixed')
parser.add_argument('--write-processed', action='store_true', default=False, help='flag to write an input file after any Python code and include commands in the original input file have been processed')
args = parser.parse_args()
run_main(args)
def run_main(args):
sim_config = create_simulation_config(args)
context = create_context(sim_config)
context.run()

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

查看文件

@@ -1,318 +1,318 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import sys
from .utilities import get_terminal_width
from .cmds_geometry.geometry_objects_read import GeometryObjectsRead
from .cmds_geometry.edge import Edge
from .cmds_geometry.plate import Plate
from .cmds_geometry.triangle import Triangle
from .cmds_geometry.box import Box
from .cmds_geometry.cylinder import Cylinder
from .cmds_geometry.cylindrical_sector import CylindricalSector
from .cmds_geometry.fractal_box import FractalBox
from .cmds_geometry.sphere import Sphere
from .cmds_geometry.add_surface_roughness import AddSurfaceRoughness
from .cmds_geometry.add_surface_water import AddSurfaceWater
from .cmds_geometry.add_grass import AddGrass
from .utilities import round_value
from tqdm import tqdm
import numpy as np
def process_geometrycmds(geometry):
"""
This function checks the validity of command parameters, creates instances
of classes of parameters, and calls functions to directly set arrays
solid, rigid and ID.
Args:
geometry (list): Geometry commands in the model
"""
scene_objects = []
# Disable progress bar if on Windows as it does not update properly
# when messages are printed
#if sys.platform == 'win32':
# tqdmdisable = True
#else:
# tqdmdisable = G.tqdmdisable
tqdmdisable = False
for object in tqdm(geometry, desc='Processing geometry related cmds', unit='cmds', ncols=get_terminal_width() - 1, file=sys.stdout, disable=tqdmdisable):
tmp = object.split()
if tmp[0] == '#geometry_objects_read:':
if len(tmp) != 6:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires exactly five parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
gor = GeometryObjectsRead(p1=p1, geofile=tmp[4], matfile=tmp[5])
scene_objects.append(gor)
elif tmp[0] == '#edge:':
if len(tmp) != 8:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires exactly seven parameters')
edge = Edge(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])),
material_id=tmp[7])
scene_objects.append(edge)
elif tmp[0] == '#plate:':
if len(tmp) < 8:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters')
# Isotropic case
if len(tmp) == 8:
plate = Plate(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])),
material_id=tmp[7])
# Anisotropic case
elif len(tmp) == 9:
plate = Plate(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])),
material_ids=tmp[7:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(plate)
elif tmp[0] == '#triangle:':
if len(tmp) < 12:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
p3 = (float(tmp[7]), float(tmp[8]), float(tmp[9]))
thickness = float(tmp[10])
# Isotropic case with no user specified averaging
if len(tmp) == 12:
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11])
# Isotropic case with user specified averaging
elif len(tmp) == 13:
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11], averaging=tmp[12].lower())
# Uniaxial anisotropic case
elif len(tmp) == 14:
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_ids=tmp[11:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(triangle)
elif tmp[0] == '#box:':
if len(tmp) < 8:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
# Isotropic case with no user specified averaging
if len(tmp) == 8:
box = Box(p1=p1, p2=p2, material_id=tmp[7])
# Isotropic case with user specified averaging
elif len(tmp) == 9:
box = Box(p1=p1, p2=p2, material_id=tmp[7], averaging=tmp[8])
# Uniaxial anisotropic case
elif len(tmp) == 10:
box = Box(p1=p1, p2=p2, material_ids=tmp[7:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(box)
elif tmp[0] == '#cylinder:':
if len(tmp) < 9:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least eight parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
r = float(tmp[7])
# Isotropic case with no user specified averaging
if len(tmp) == 9:
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8])
# Isotropic case with user specified averaging
elif len(tmp) == 10:
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8], averaging=tmp[9])
# Uniaxial anisotropic case
elif len(tmp) == 11:
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_ids=tmp[8:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(cylinder)
elif tmp[0] == '#cylindrical_sector:':
if len(tmp) < 10:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least nine parameters')
normal = tmp[1].lower()
ctr1 = float(tmp[2])
ctr2 = float(tmp[3])
extent1 = float(tmp[4])
extent2 = float(tmp[5])
r = float(tmp[6])
start = float(tmp[7])
end = float(tmp[8])
# Isotropic case with no user specified averaging
if len(tmp) == 10:
CylindricalSector(normal=normal, ctl1=ctl1, ctl2=ctl2, extent1=extent1, extent2=extent2, r=r, start=start, end=end, msterial_id=tmp[9])
# Isotropic case with user specified averaging
elif len(tmp) == 11:
CylindricalSector(normal=normal, ctl1=ctl1, ctl2=ctl2, extent1=extent1, extent2=extent2, r=r, start=start, end=end, averaging=tmp[10], material_id=tmp[9])
# Uniaxial anisotropic case
elif len(tmp) == 12:
CylindricalSector(normal=normal, ctl1=ctl1, ctl2=ctl2, extent1=extent1, extent2=extent2, r=r, start=start, end=end, material_ids=tmp[9:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(cylindrical_sector)
elif tmp[0] == '#sphere:':
if len(tmp) < 6:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least five parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
r = float(tmp[4])
# Isotropic case with no user specified averaging
if len(tmp) == 6:
sphere = Sphere(p1=p1, r=r, material_id=tmp[5])
# Isotropic case with user specified averaging
elif len(tmp) == 7:
sphere = Sphere(p1=p1, r=r, material_id=tmp[5], averaging=tmp[6])
# Uniaxial anisotropic case
elif len(tmp) == 8:
sphere = Sphere(p1=p1, r=r, material_id=tmp[5:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(sphere)
elif tmp[0] == '#fractal_box:':
# Default is no dielectric smoothing for a fractal box
if len(tmp) < 14:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least thirteen parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
frac_dim = float(tmp[7])
weighting = np.array([float(tmp[8]), float(tmp[9]), float(tmp[10])])
n_materials = round_value(tmp[11])
mixing_model_id = tmp[12]
ID = tmp[13]
# without seed
if len(tmp) == 14:
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials)
# with seed
elif len(tmp) == 15:
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials, seed=tmp[14])
# user specified averaging
elif len(tmp) == 16:
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials, seed=tmp[14], averaging=tmp[15].lower())
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(fb)
# Search and process any modifiers for the fractal box
for object in geometry:
tmp = object.split()
if tmp[0] == '#add_surface_roughness:':
if len(tmp) < 13:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least twelve parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
frac_dim = float(tmp[7])
weighting = np.array([float(tmp[8]), float(tmp[9])])
limits = [float(tmp[10]), float(tmp[11])]
fractal_box_id = tmp[12]
# No seed
if len(tmp) == 13:
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, limits=limits, fractal_box_id=fractal_box_id)
elif len(tmp) == 14:
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, limits=limits, fractal_box_id=fractal_box_id, seed=int(tmp[13]))
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(asr)
if tmp[0] == '#add_surface_water:':
if len(tmp) != 9:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires exactly eight parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
depth = float(tmp[7])
fractal_box_id = tmp[8]
asf = AddSurfaceWater(p1=p1, p2=p2, depth=depth, fractal_box_id=fractal_box_id)
scene_objects.append(asf)
if tmp[0] == '#add_grass:':
if len(tmp) < 12:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
frac_dim = float(tmp[7])
limits = [float(tmp[8]), float(tmp[9])]
n_blades = int(tmp[10])
fractal_box_id = tmp[11]
# no seed
if len(tmp) == 12:
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim, limits=limits, n_blades=n_blades, fractal_box_id=fractal_box_id)
elif len(tmp) == 13:
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim, limits=limits, n_blades=n_blades, fractal_box_id=fractal_box_id, seed=int(tmp[12]))
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(grass)
return scene_objects
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import sys
from .utilities import get_terminal_width
from .cmds_geometry.geometry_objects_read import GeometryObjectsRead
from .cmds_geometry.edge import Edge
from .cmds_geometry.plate import Plate
from .cmds_geometry.triangle import Triangle
from .cmds_geometry.box import Box
from .cmds_geometry.cylinder import Cylinder
from .cmds_geometry.cylindrical_sector import CylindricalSector
from .cmds_geometry.fractal_box import FractalBox
from .cmds_geometry.sphere import Sphere
from .cmds_geometry.add_surface_roughness import AddSurfaceRoughness
from .cmds_geometry.add_surface_water import AddSurfaceWater
from .cmds_geometry.add_grass import AddGrass
from .utilities import round_value
from tqdm import tqdm
import numpy as np
def process_geometrycmds(geometry):
"""
This function checks the validity of command parameters, creates instances
of classes of parameters, and calls functions to directly set arrays
solid, rigid and ID.
Args:
geometry (list): Geometry commands in the model
"""
scene_objects = []
# Disable progress bar if on Windows as it does not update properly
# when messages are printed
#if sys.platform == 'win32':
# tqdmdisable = True
#else:
# tqdmdisable = G.tqdmdisable
tqdmdisable = False
for object in tqdm(geometry, desc='Processing geometry related cmds', unit='cmds', ncols=get_terminal_width() - 1, file=sys.stdout, disable=tqdmdisable):
tmp = object.split()
if tmp[0] == '#geometry_objects_read:':
if len(tmp) != 6:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires exactly five parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
gor = GeometryObjectsRead(p1=p1, geofile=tmp[4], matfile=tmp[5])
scene_objects.append(gor)
elif tmp[0] == '#edge:':
if len(tmp) != 8:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires exactly seven parameters')
edge = Edge(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])),
material_id=tmp[7])
scene_objects.append(edge)
elif tmp[0] == '#plate:':
if len(tmp) < 8:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters')
# Isotropic case
if len(tmp) == 8:
plate = Plate(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])),
material_id=tmp[7])
# Anisotropic case
elif len(tmp) == 9:
plate = Plate(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])),
p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])),
material_ids=tmp[7:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(plate)
elif tmp[0] == '#triangle:':
if len(tmp) < 12:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
p3 = (float(tmp[7]), float(tmp[8]), float(tmp[9]))
thickness = float(tmp[10])
# Isotropic case with no user specified averaging
if len(tmp) == 12:
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11])
# Isotropic case with user specified averaging
elif len(tmp) == 13:
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11], averaging=tmp[12].lower())
# Uniaxial anisotropic case
elif len(tmp) == 14:
triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_ids=tmp[11:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(triangle)
elif tmp[0] == '#box:':
if len(tmp) < 8:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
# Isotropic case with no user specified averaging
if len(tmp) == 8:
box = Box(p1=p1, p2=p2, material_id=tmp[7])
# Isotropic case with user specified averaging
elif len(tmp) == 9:
box = Box(p1=p1, p2=p2, material_id=tmp[7], averaging=tmp[8])
# Uniaxial anisotropic case
elif len(tmp) == 10:
box = Box(p1=p1, p2=p2, material_ids=tmp[7:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(box)
elif tmp[0] == '#cylinder:':
if len(tmp) < 9:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least eight parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
r = float(tmp[7])
# Isotropic case with no user specified averaging
if len(tmp) == 9:
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8])
# Isotropic case with user specified averaging
elif len(tmp) == 10:
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8], averaging=tmp[9])
# Uniaxial anisotropic case
elif len(tmp) == 11:
cylinder = Cylinder(p1=p1, p2=p2, r=r, material_ids=tmp[8:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(cylinder)
elif tmp[0] == '#cylindrical_sector:':
if len(tmp) < 10:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least nine parameters')
normal = tmp[1].lower()
ctr1 = float(tmp[2])
ctr2 = float(tmp[3])
extent1 = float(tmp[4])
extent2 = float(tmp[5])
r = float(tmp[6])
start = float(tmp[7])
end = float(tmp[8])
# Isotropic case with no user specified averaging
if len(tmp) == 10:
CylindricalSector(normal=normal, ctl1=ctl1, ctl2=ctl2, extent1=extent1, extent2=extent2, r=r, start=start, end=end, msterial_id=tmp[9])
# Isotropic case with user specified averaging
elif len(tmp) == 11:
CylindricalSector(normal=normal, ctl1=ctl1, ctl2=ctl2, extent1=extent1, extent2=extent2, r=r, start=start, end=end, averaging=tmp[10], material_id=tmp[9])
# Uniaxial anisotropic case
elif len(tmp) == 12:
CylindricalSector(normal=normal, ctl1=ctl1, ctl2=ctl2, extent1=extent1, extent2=extent2, r=r, start=start, end=end, material_ids=tmp[9:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(cylindrical_sector)
elif tmp[0] == '#sphere:':
if len(tmp) < 6:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least five parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
r = float(tmp[4])
# Isotropic case with no user specified averaging
if len(tmp) == 6:
sphere = Sphere(p1=p1, r=r, material_id=tmp[5])
# Isotropic case with user specified averaging
elif len(tmp) == 7:
sphere = Sphere(p1=p1, r=r, material_id=tmp[5], averaging=tmp[6])
# Uniaxial anisotropic case
elif len(tmp) == 8:
sphere = Sphere(p1=p1, r=r, material_id=tmp[5:])
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(sphere)
elif tmp[0] == '#fractal_box:':
# Default is no dielectric smoothing for a fractal box
if len(tmp) < 14:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least thirteen parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
frac_dim = float(tmp[7])
weighting = np.array([float(tmp[8]), float(tmp[9]), float(tmp[10])])
n_materials = round_value(tmp[11])
mixing_model_id = tmp[12]
ID = tmp[13]
# without seed
if len(tmp) == 14:
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials)
# with seed
elif len(tmp) == 15:
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials, seed=tmp[14])
# user specified averaging
elif len(tmp) == 16:
fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials, seed=tmp[14], averaging=tmp[15].lower())
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(fb)
# Search and process any modifiers for the fractal box
for object in geometry:
tmp = object.split()
if tmp[0] == '#add_surface_roughness:':
if len(tmp) < 13:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least twelve parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
frac_dim = float(tmp[7])
weighting = np.array([float(tmp[8]), float(tmp[9])])
limits = [float(tmp[10]), float(tmp[11])]
fractal_box_id = tmp[12]
# No seed
if len(tmp) == 13:
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, limits=limits, fractal_box_id=fractal_box_id)
elif len(tmp) == 14:
asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, limits=limits, fractal_box_id=fractal_box_id, seed=int(tmp[13]))
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(asr)
if tmp[0] == '#add_surface_water:':
if len(tmp) != 9:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires exactly eight parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
depth = float(tmp[7])
fractal_box_id = tmp[8]
asf = AddSurfaceWater(p1=p1, p2=p2, depth=depth, fractal_box_id=fractal_box_id)
scene_objects.append(asf)
if tmp[0] == '#add_grass:':
if len(tmp) < 12:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters')
p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3]))
p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6]))
frac_dim = float(tmp[7])
limits = [float(tmp[8]), float(tmp[9])]
n_blades = int(tmp[10])
fractal_box_id = tmp[11]
# no seed
if len(tmp) == 12:
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim, limits=limits, n_blades=n_blades, fractal_box_id=fractal_box_id)
elif len(tmp) == 13:
grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim, limits=limits, n_blades=n_blades, fractal_box_id=fractal_box_id, seed=int(tmp[12]))
else:
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given')
scene_objects.append(grass)
return scene_objects

查看文件

@@ -1,338 +1,338 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
# Copyright (C) 2015-2018: 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 .cmds_multiple import Waveform
from .cmds_multiple import VoltageSource
from .cmds_multiple import HertzianDipole
from .cmds_multiple import MagneticDipole
from .cmds_multiple import TransmissionLine
from .cmds_multiple import Material
from .cmds_multiple import Snapshot
from .cmds_multiple import AddDebyeDispersion
from .cmds_multiple import AddLorentzDispersion
from .cmds_multiple import AddDrudeDispersion
from .cmds_multiple import SoilPeplinski
from .cmds_multiple import GeometryView
from .cmds_multiple import GeometryObjectsWrite
from .cmds_multiple import PMLCFS
from .cmds_multiple import Rx
def process_multicmds(multicmds):
"""
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.
"""
scene_objects = []
# Waveform definitions
cmdname = '#waveform'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 4:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly four parameters')
waveform = Waveform(wave_type=tmp[0], amp=float(tmp[1]), freq=float(tmp[2]), id=tmp[3])
scene_objects.append(waveform)
# Voltage source
cmdname = '#voltage_source'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) == 6:
voltage_source = VoltageSource(polarisation=tmp[0].lower(), p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5])
elif len(tmp) == 8:
voltage_source = VoltageSource(polarisation=tmp[0].lower(), p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5], start=float(tmp[6]), end=float(tmp[7]))
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters')
scene_objects.append(voltage_source)
# Hertzian dipole
cmdname = '#hertzian_dipole'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
if len(tmp) == 5:
hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4])
elif len(tmp) == 7:
hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4], start=float(tmp[5]), end=float(tmp[6]))
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters')
scene_objects.append(hertzian_dipole)
# Magnetic dipole
cmdname = '#magnetic_dipole'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
if len(tmp) == 5:
magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4])
elif len(tmp) == 7:
magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4], start=float(tmp[5]), end=float(tmp[6]))
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters')
scene_objects.append(magnetic_dipole)
# Transmission line
cmdname = '#transmission_line'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 6:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters')
if len(tmp) == 6:
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5])
elif len(tmp) == 8:
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5], start=tmp[6], end=tmp[7])
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters')
scene_objects.append(tl)
# Receiver
cmdname = '#rx'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 3 and len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' has an incorrect number of parameters')
if len(tmp) == 3:
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])))
else:
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])), id=tmp[3], outputs=tmp[4:])
scene_objects.append(rx)
# Receiver array
cmdname = '#rx_array'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 9:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly nine parameters')
p1 = (float(tmp[0], float(tmp[1]), float[tmp[2]]))
p2 = (float(tmp[3], float(tmp[4]), float[tmp[5]]))
dl = (float(tmp[6], float(tmp[7]), float[tmp[8]]))
rx_array = RxArray(p1=p1, p2=p2, dl=dl)
scene_objects.append(rx_array)
# Snapshot
cmdname = '#snapshot'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 11:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')
p1 = (float(tmp[0], float(tmp[1]), float[tmp[2]]))
p2 = (float(tmp[3], float(tmp[4]), float[tmp[5]]))
dl = (float(tmp[6], float(tmp[7]), float[tmp[8]]))
filename = tmp[10]
try:
iterations = int(tmp[9])
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, iterations=iterations, filename=filename)
except ValueError:
time = float(tmp[9])
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, time=time, filename=filename)
scene_objects.append(snapshot)
# Materials
cmdname = '#material'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly five parameters')
material = Material(er=float(tmp[0]), se=float(tmp[1]), mr=float(tmp[2]), sm=float(tmp[3]), id=tmp[4])
scene_objects.append(material)
cmdname = '#add_dispersion_debye'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 4:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least four parameters')
poles = int(tmp[0])
er_delta = []
tau = []
material_ids = tmp[(2 * poles) + 1:len(tmp)]
for pole in range(1, 2 * poles, 2):
er_delta.append(float(tmp[pole]))
tau.append(float(tmp[pole + 1]))
debye_dispersion = AddDebyeDispersion(pole=poles, er_delta=er_delta, tau=tau, material_ids=material_ids)
scene_objects.append(debye_dispersion)
cmdname = '#add_dispersion_lorentz'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
poles = int(tmp[0])
material_ids = tmp[(3 * poles) + 1:len(tmp)]
er_delta = []
tau = []
alpha = []
for pole in range(1, 3 * poles, 3):
er_delta.append(float(tmp[pole]))
tau.append(float(tmp[pole + 1]))
alpha.append(float(tmp[pole + 2]))
lorentz_dispersion = AddLorentzDispersion(poles=poles, material_ids=material_ids, er_delta=er_delta, tau=tau, alpha=alpha)
scene_objects.append(lorentz_dispersion)
cmdname = '#add_dispersion_drude'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
poles = int(tmp[0])
material_ids = tmp[(3 * poles) + 1:len(tmp)]
tau = []
alpha = []
for pole in range(1, 2 * poles, 2):
tau.append(float(tmp[pole]))
alpha.append(float(tmp[pole + 1]))
drude_dispersion = AddDrudeDispersion(poles=poles, material_ids=material_ids, tau=tau, alpha=alpha)
scene_objects.append(drude_dispersion)
cmdname = '#soil_peplinski'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 7:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at exactly seven parameters')
soil = SoilPeplinski(sand_fraction=float(tmp[0]),
clay_fraction=float(tmp[1]),
bulk_density=float(tmp[2]),
sand_density=float(tmp[3]),
water_fraction_lower=float(tmp[4]),
water_fraction_upper=float(tmp[5]),
id=tmp[6])
scene_objects.append(soil)
# Geometry views (creates VTK-based geometry files)
cmdname = '#geometry_view'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 11:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
p2 = float(tmp[3]), float(tmp[4]), float(tmp[5])
dl = float(tmp[6]), float(tmp[7]), float(tmp[8])
geometry_view = GeometryView(p1=p1, p2=p2, dl=dl, filename=tmp[9], output_type=tmp[10])
scene_objects.append(geometry_view)
# Geometry object(s) output
cmdname = '#geometry_objects_write'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 7:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly seven parameters')
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
p2 = float(tmp[3]), float(tmp[4]), float(tmp[5])
gow = GeometryObjectsWrite(p1=p1, p2=p2, filename=tmp[6])
scene_objects.append(gow)
# Complex frequency shifted (CFS) PML parameter
cmdname = '#pml_cfs'
if multicmds[cmdname] is not 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) != 12:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly twelve parameters')
pml_cfs = PMLCFS(alphascalingprofile=tmp[0],
alphascalingdirection=tmp[1],
alphamin=tmp[2],
alphamax=tmp[3],
kappascalingprofile=tmp[4],
kappascalingdirection=tmp[5],
kappamin=tmp[6],
kappamax=tmp[7],
sigmascalingprofile=tmp[8],
sigmascalingdirection=tmp[9],
sigmamin=tmp[10],
sigmamax=tmp[11])
scene_objects.append(pml_cfs)
return scene_objects
def process_subgrid_hsg(cmdinstance):
pass
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
# Copyright (C) 2015-2018: 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 .cmds_multiple import Waveform
from .cmds_multiple import VoltageSource
from .cmds_multiple import HertzianDipole
from .cmds_multiple import MagneticDipole
from .cmds_multiple import TransmissionLine
from .cmds_multiple import Material
from .cmds_multiple import Snapshot
from .cmds_multiple import AddDebyeDispersion
from .cmds_multiple import AddLorentzDispersion
from .cmds_multiple import AddDrudeDispersion
from .cmds_multiple import SoilPeplinski
from .cmds_multiple import GeometryView
from .cmds_multiple import GeometryObjectsWrite
from .cmds_multiple import PMLCFS
from .cmds_multiple import Rx
def process_multicmds(multicmds):
"""
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.
"""
scene_objects = []
# Waveform definitions
cmdname = '#waveform'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 4:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly four parameters')
waveform = Waveform(wave_type=tmp[0], amp=float(tmp[1]), freq=float(tmp[2]), id=tmp[3])
scene_objects.append(waveform)
# Voltage source
cmdname = '#voltage_source'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) == 6:
voltage_source = VoltageSource(polarisation=tmp[0].lower(), p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5])
elif len(tmp) == 8:
voltage_source = VoltageSource(polarisation=tmp[0].lower(), p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5], start=float(tmp[6]), end=float(tmp[7]))
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters')
scene_objects.append(voltage_source)
# Hertzian dipole
cmdname = '#hertzian_dipole'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
if len(tmp) == 5:
hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4])
elif len(tmp) == 7:
hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4], start=float(tmp[5]), end=float(tmp[6]))
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters')
scene_objects.append(hertzian_dipole)
# Magnetic dipole
cmdname = '#magnetic_dipole'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
if len(tmp) == 5:
magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4])
elif len(tmp) == 7:
magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4], start=float(tmp[5]), end=float(tmp[6]))
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters')
scene_objects.append(magnetic_dipole)
# Transmission line
cmdname = '#transmission_line'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 6:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters')
if len(tmp) == 6:
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5])
elif len(tmp) == 8:
tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5], start=tmp[6], end=tmp[7])
else:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters')
scene_objects.append(tl)
# Receiver
cmdname = '#rx'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 3 and len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' has an incorrect number of parameters')
if len(tmp) == 3:
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])))
else:
rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])), id=tmp[3], outputs=tmp[4:])
scene_objects.append(rx)
# Receiver array
cmdname = '#rx_array'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 9:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly nine parameters')
p1 = (float(tmp[0], float(tmp[1]), float[tmp[2]]))
p2 = (float(tmp[3], float(tmp[4]), float[tmp[5]]))
dl = (float(tmp[6], float(tmp[7]), float[tmp[8]]))
rx_array = RxArray(p1=p1, p2=p2, dl=dl)
scene_objects.append(rx_array)
# Snapshot
cmdname = '#snapshot'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 11:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')
p1 = (float(tmp[0], float(tmp[1]), float[tmp[2]]))
p2 = (float(tmp[3], float(tmp[4]), float[tmp[5]]))
dl = (float(tmp[6], float(tmp[7]), float[tmp[8]]))
filename = tmp[10]
try:
iterations = int(tmp[9])
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, iterations=iterations, filename=filename)
except ValueError:
time = float(tmp[9])
snapshot = Snapshot(p1=p1, p2=p2, dl=dl, time=time, filename=filename)
scene_objects.append(snapshot)
# Materials
cmdname = '#material'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly five parameters')
material = Material(er=float(tmp[0]), se=float(tmp[1]), mr=float(tmp[2]), sm=float(tmp[3]), id=tmp[4])
scene_objects.append(material)
cmdname = '#add_dispersion_debye'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 4:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least four parameters')
poles = int(tmp[0])
er_delta = []
tau = []
material_ids = tmp[(2 * poles) + 1:len(tmp)]
for pole in range(1, 2 * poles, 2):
er_delta.append(float(tmp[pole]))
tau.append(float(tmp[pole + 1]))
debye_dispersion = AddDebyeDispersion(pole=poles, er_delta=er_delta, tau=tau, material_ids=material_ids)
scene_objects.append(debye_dispersion)
cmdname = '#add_dispersion_lorentz'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
poles = int(tmp[0])
material_ids = tmp[(3 * poles) + 1:len(tmp)]
er_delta = []
tau = []
alpha = []
for pole in range(1, 3 * poles, 3):
er_delta.append(float(tmp[pole]))
tau.append(float(tmp[pole + 1]))
alpha.append(float(tmp[pole + 2]))
lorentz_dispersion = AddLorentzDispersion(poles=poles, material_ids=material_ids, er_delta=er_delta, tau=tau, alpha=alpha)
scene_objects.append(lorentz_dispersion)
cmdname = '#add_dispersion_drude'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) < 5:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters')
poles = int(tmp[0])
material_ids = tmp[(3 * poles) + 1:len(tmp)]
tau = []
alpha = []
for pole in range(1, 2 * poles, 2):
tau.append(float(tmp[pole]))
alpha.append(float(tmp[pole + 1]))
drude_dispersion = AddDrudeDispersion(poles=poles, material_ids=material_ids, tau=tau, alpha=alpha)
scene_objects.append(drude_dispersion)
cmdname = '#soil_peplinski'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 7:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at exactly seven parameters')
soil = SoilPeplinski(sand_fraction=float(tmp[0]),
clay_fraction=float(tmp[1]),
bulk_density=float(tmp[2]),
sand_density=float(tmp[3]),
water_fraction_lower=float(tmp[4]),
water_fraction_upper=float(tmp[5]),
id=tmp[6])
scene_objects.append(soil)
# Geometry views (creates VTK-based geometry files)
cmdname = '#geometry_view'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 11:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters')
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
p2 = float(tmp[3]), float(tmp[4]), float(tmp[5])
dl = float(tmp[6]), float(tmp[7]), float(tmp[8])
geometry_view = GeometryView(p1=p1, p2=p2, dl=dl, filename=tmp[9], output_type=tmp[10])
scene_objects.append(geometry_view)
# Geometry object(s) output
cmdname = '#geometry_objects_write'
if multicmds[cmdname] is not None:
for cmdinstance in multicmds[cmdname]:
tmp = cmdinstance.split()
if len(tmp) != 7:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly seven parameters')
p1 = float(tmp[0]), float(tmp[1]), float(tmp[2])
p2 = float(tmp[3]), float(tmp[4]), float(tmp[5])
gow = GeometryObjectsWrite(p1=p1, p2=p2, filename=tmp[6])
scene_objects.append(gow)
# Complex frequency shifted (CFS) PML parameter
cmdname = '#pml_cfs'
if multicmds[cmdname] is not 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) != 12:
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly twelve parameters')
pml_cfs = PMLCFS(alphascalingprofile=tmp[0],
alphascalingdirection=tmp[1],
alphamin=tmp[2],
alphamax=tmp[3],
kappascalingprofile=tmp[4],
kappascalingdirection=tmp[5],
kappamin=tmp[6],
kappamax=tmp[7],
sigmascalingprofile=tmp[8],
sigmascalingdirection=tmp[9],
sigmamin=tmp[10],
sigmamax=tmp[11])
scene_objects.append(pml_cfs)
return scene_objects
def process_subgrid_hsg(cmdinstance):
pass

查看文件

@@ -1,364 +1,364 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import datetime
from importlib import import_module
import itertools
import os
import psutil
import sys
from colorama import init
from colorama import Fore
from colorama import Style
init()
import cython
import numpy as np
from terminaltables import SingleTable
from tqdm import tqdm
from pathlib import Path
import gprMax.config as config
from .cuda.fields_updates import kernel_template_fields
from .cuda.snapshots import kernel_template_store_snapshot
from .cuda.source_updates import kernel_template_sources
from .cython.yee_cell_build import build_electric_components
from .cython.yee_cell_build import build_magnetic_components
from .exceptions import GeneralError
from .fields_outputs import kernel_template_store_outputs
from .fields_outputs import write_hdf5_outputfile
from .grid import FDTDGrid
from .grid import dispersion_analysis
from .input_cmds_geometry import process_geometrycmds
from .input_cmds_file import process_python_include_code
from .input_cmds_file import write_processed_file
from .input_cmds_file import check_cmd_names
from .input_cmds_file import parse_hash_commands
from .input_cmds_singleuse import process_singlecmds
from .input_cmds_multiuse import process_multicmds
from .materials import Material
from .materials import process_materials
from .pml import CFS
from .pml import PML
from .pml import build_pml
from .pml import pml_information
from .receivers import gpu_initialise_rx_arrays
from .receivers import gpu_get_rx_array
from .receivers import Rx
from .snapshots import Snapshot
from .snapshots import gpu_initialise_snapshot_array
from .snapshots import gpu_get_snapshot_array
from .sources import gpu_initialise_src_arrays
from .utilities import get_terminal_width
from .utilities import human_size
from .utilities import open_path_file
from .utilities import round32
from .utilities import timer
from .utilities import Printer
from .scene import Scene
from .solvers import create_solver
class ModelBuildRun:
def __init__(self, G, sim_config, model_config):
self.G = G
self.sim_config = sim_config
self.model_config = model_config
self.printer = Printer(config)
# Monitor memory usage
self.p = None
def build(self):
"""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
currentmodelrun (int): Current model run number.
modelend (int): Number of last model to run.
numbermodelruns (int): Total number of model runs.
inputfile (object): File object for the input file.
usernamespace (dict): Namespace that can be accessed by user
in any Python code blocks in input file.
Returns:
tsolve (int): Length of time (seconds) of main FDTD calculations
"""
# Monitor memory usage
self.p = psutil.Process()
# Normal model reading/building process; bypassed if geometry information to be reused
if self.model_config.reuse_geometry:
self.reuse_geometry()
else:
self.build_geometry()
G = self.G
# Adjust position of simple sources and receivers if required
if G.srcsteps[0] != 0 or G.srcsteps[1] != 0 or G.srcsteps[2] != 0:
for source in itertools.chain(G.hertziandipoles, G.magneticdipoles):
if currentmodelrun == 1:
if source.xcoord + G.srcsteps[0] * modelend < 0 or source.xcoord + G.srcsteps[0] * modelend > G.nx or source.ycoord + G.srcsteps[1] * modelend < 0 or source.ycoord + G.srcsteps[1] * modelend > G.ny or source.zcoord + G.srcsteps[2] * modelend < 0 or source.zcoord + G.srcsteps[2] * modelend > G.nz:
raise GeneralError('Source(s) will be stepped to a position outside the domain.')
source.xcoord = source.xcoordorigin + (currentmodelrun - 1) * G.srcsteps[0]
source.ycoord = source.ycoordorigin + (currentmodelrun - 1) * G.srcsteps[1]
source.zcoord = source.zcoordorigin + (currentmodelrun - 1) * G.srcsteps[2]
if G.rxsteps[0] != 0 or G.rxsteps[1] != 0 or G.rxsteps[2] != 0:
for receiver in G.rxs:
if currentmodelrun == 1:
if receiver.xcoord + G.rxsteps[0] * modelend < 0 or receiver.xcoord + G.rxsteps[0] * modelend > G.nx or receiver.ycoord + G.rxsteps[1] * modelend < 0 or receiver.ycoord + G.rxsteps[1] * modelend > G.ny or receiver.zcoord + G.rxsteps[2] * modelend < 0 or receiver.zcoord + G.rxsteps[2] * modelend > G.nz:
raise GeneralError('Receiver(s) will be stepped to a position outside the domain.')
receiver.xcoord = receiver.xcoordorigin + (currentmodelrun - 1) * G.rxsteps[0]
receiver.ycoord = receiver.ycoordorigin + (currentmodelrun - 1) * G.rxsteps[1]
receiver.zcoord = receiver.zcoordorigin + (currentmodelrun - 1) * G.rxsteps[2]
# Write files for any geometry views and geometry object outputs
if not (G.geometryviews or G.geometryobjectswrite) and self.sim_config.geometry_only and config.is_messages():
print(Fore.RED + '\nWARNING: No geometry views or geometry objects to output found.' + Style.RESET_ALL)
if config.is_messages(): print()
for i, geometryview in enumerate(G.geometryviews):
geometryview.set_filename(self.model_config.appendmodelnumber)
pbar = tqdm(total=geometryview.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry view file {}/{}, {}'.format(i + 1, len(G.geometryviews), os.path.split(geometryview.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
geometryview.write_vtk(G, pbar)
pbar.close()
for i, geometryobject in enumerate(G.geometryobjectswrite):
pbar = tqdm(total=geometryobject.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry object file {}/{}, {}'.format(i + 1, len(G.geometryobjectswrite), os.path.split(geometryobject.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
geometryobject.write_hdf5(G, pbar)
pbar.close()
# If only writing geometry information
if self.sim_config.geometry_only:
tsolve = 0
def build_geometry(self):
model_config = self.model_config
sim_config = self.sim_config
G = self.G
printer = Printer(config)
printer.print(model_config.next_model)
scene = self.build_scene()
# combine available grids
grids = [G] + G.subgrids
gridbuilders = [GridBuilder(grid, self.printer) for grid in grids]
for gb in gridbuilders:
gb.printer.print(pml_information(gb.grid))
gb.build_pmls()
gb.build_components()
gb.tm_grid_update()
gb.update_voltage_source_materials()
gb.grid.initialise_std_update_coeff_arrays()
# Set datatype for dispersive arrays if there are any dispersive materials.
if config.materials['maxpoles'] != 0:
drudelorentz = any([m for m in G.materials if 'drude' in m.type or 'lorentz' in m.type])
if drudelorentz:
config.materials['dispersivedtype'] = config.dtypes['complex']
config.materials['dispersiveCdtype'] = config.dtypes['C_complex']
else:
config.materials['dispersivedtype'] = config.dtypes['float_or_double']
config.materials['dispersiveCdtype'] = config.dtypes['C_float_or_double']
# Update estimated memory (RAM) usage
G.memoryusage += int(3 * config.materials['maxpoles'] * (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * np.dtype(config.materials['dispersivedtype']).itemsize)
G.memory_check()
printer.print('\nMemory (RAM) required - updated (dispersive): ~{}\n'.format(human_size(G.memoryusage)))
for gb in gridbuilders:
gb.grid.initialise_dispersive_arrays(config.materials['dispersivedtype'])
# Check there is sufficient memory to store any snapshots
if G.snapshots:
snapsmemsize = 0
for snap in G.snapshots:
# 2 x required to account for electric and magnetic fields
snapsmemsize += (2 * snap.datasizefield)
G.memoryusage += int(snapsmemsize)
G.memory_check(snapsmemsize=int(snapsmemsize))
printer.print('\nMemory (RAM) required - updated (snapshots): ~{}\n'.format(human_size(G.memoryusage)))
for gb in gridbuilders:
gb.build_materials()
# Check to see if numerical dispersion might be a problem
results = dispersion_analysis(G)
if results['error']:
printer.print(Fore.RED + "\nWARNING: Numerical dispersion analysis not carried out as {}".format(results['error']) + Style.RESET_ALL)
elif results['N'] < config.numdispersion['mingridsampling']:
raise GeneralError("Non-physical wave propagation: Material '{}' has wavelength sampled by {} cells, less than required minimum for physical wave propagation. Maximum significant frequency estimated as {:g}Hz".format(results['material'].ID, results['N'], results['maxfreq']))
elif results['deltavp'] and np.abs(results['deltavp']) > config.numdispersion['maxnumericaldisp']:
printer.print(Fore.RED + "\nWARNING: Potentially significant numerical dispersion. Estimated largest physical phase-velocity error is {:.2f}% in material '{}' whose wavelength sampled by {} cells. Maximum significant frequency estimated as {:g}Hz".format(results['deltavp'], results['material'].ID, results['N'], results['maxfreq']) + Style.RESET_ALL)
elif results['deltavp']:
printer.print("\nNumerical dispersion analysis: estimated largest physical phase-velocity error is {:.2f}% in material '{}' whose wavelength sampled by {} cells. Maximum significant frequency estimated as {:g}Hz".format(results['deltavp'], results['material'].ID, results['N'], results['maxfreq']))
def reuse_geometry(self):
G = self.G
inputfilestr = '\n--- Model {}/{}, input file (not re-processed, i.e. geometry fixed): {}'.format(self.model_config.appendmodelnumber, self.sim_config.model_end, self.sim_config.input_file_path)
self.printer.print(Fore.GREEN + '{} {}\n'.format(self.model_config.inputfilestr, '-' * (get_terminal_width() - 1 - len(inputfilestr))) + Style.RESET_ALL)
for grid in [G] + G.subgrids:
grid.reset_fields()
def build_scene(self):
G = self.G
# api for multiple scenes / model runs
scene = self.model_config.get_scene()
# if there is no scene - process the hash commands instead
if not scene:
scene = Scene()
# parse the input file into user objects and add them to the scene
scene = parse_hash_commands(self.model_config, G, scene)
# Creates the internal simulation objects.
scene.create_internal_objects(G)
return scene
def create_output_directory(self):
if self.G.outputdirectory:
# Check and set output directory and filename
try:
os.mkdir(self.G.outputdirectory)
self.printer.print('\nCreated output directory: {}'.format(self.G.outputdirectory))
except FileExistsError:
pass
# modify the output path (hack)
self.model_config.output_file_path_ext = Path(self.G.outputdirectory, self.model_config.output_file_path_ext)
def run_model(self, solver):
G = self.G
self.create_output_directory()
self.printer.print('\nOutput file: {}\n'.format(self.model_config.output_file_path_ext))
tsolve = self.solve(solver)
# Write an output file in HDF5 format
write_hdf5_outputfile(self.model_config.output_file_path_ext, G)
# Write any snapshots to file
if G.snapshots:
# Create directory and construct filename from user-supplied name and model run number
snapshotdir = self.model_config.snapshot_dir
if not os.path.exists(snapshotdir):
os.mkdir(snapshotdir)
self.printer.print()
for i, snap in enumerate(G.snapshots):
snap.filename = snapshotdir + snap.basefilename + '.vti'
pbar = tqdm(total=snap.vtkdatawritesize, leave=True, unit='byte', unit_scale=True, desc='Writing snapshot file {} of {}, {}'.format(i + 1, len(G.snapshots), os.path.split(snap.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
snap.write_vtk_imagedata(pbar, G)
pbar.close()
self.printer.print()
memGPU = ''
if config.cuda['gpus']:
memGPU = ' host + ~{} GPU'.format(human_size(self.solver.get_memsolve()))
self.printer.print('\nMemory (RAM) used: ~{}{}'.format(human_size(self.p.memory_full_info().uss), memGPU))
self.printer.print('Solving time [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=tsolve)))
return tsolve
def solve(self, solver):
"""
Solving using FDTD method on CPU. Parallelised using Cython (OpenMP) for
electric and magnetic field updates, and PML updates.
Args:
currentmodelrun (int): Current model run number.
modelend (int): Number of last model to run.
G (class): Grid class instance - holds essential parameters describing the model.
Returns:
tsolve (float): Time taken to execute solving (seconds)
"""
G = self.G
if config.is_messages():
iterator = tqdm(range(G.iterations), desc='Running simulation, model ' + str(self.model_config
.i + 1) + '/' + str(self.sim_config.model_end), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
else:
iterator = range(0, G.iterations)
tsolve = solver.solve(iterator)
return tsolve
class GridBuilder:
def __init__(self, grid, printer):
self.grid = grid
self.printer = printer
def build_pmls(self):
grid = self.grid
# build the PMLS
pbar = tqdm(total=sum(1 for value in grid.pmlthickness.values() if value > 0), desc='Building {} Grid PML boundaries'.format(self.grid.name), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
for pml_id, thickness in grid.pmlthickness.items():
build_pml(grid, pml_id, thickness)
pbar.update()
pbar.close()
def build_components(self):
# Build the model, i.e. set the material properties (ID) for every edge
# of every Yee cell
self.printer.print('')
pbar = tqdm(total=2, desc='Building {} grid'.format(self.grid.name), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
build_electric_components(self.grid.solid, self.grid.rigidE, self.grid.ID, self.grid)
pbar.update()
build_magnetic_components(self.grid.solid, self.grid.rigidH, self.grid.ID, self.grid)
pbar.update()
pbar.close()
def tm_grid_update(self):
if '2D TMx' == config.general['mode']:
self.grid.tmx()
elif '2D TMy' == config.general['mode']:
self.grid.tmy()
elif '2D TMz' == config.general['mode']:
self.grid.tmz()
def update_voltage_source_materials(self):
# Process any voltage sources (that have resistance) to create a new
# material at the source location
for voltagesource in self.grid.voltagesources:
voltagesource.create_material(self.grid)
def build_materials(self):
# Process complete list of materials - calculate update coefficients,
# store in arrays, and build text list of materials/properties
materialsdata = process_materials(self.grid)
materialstable = SingleTable(materialsdata)
materialstable.outer_border = False
materialstable.justify_columns[0] = 'right'
self.printer.print('\n{} Grid Materials:'.format(self.grid.name))
self.printer.print(materialstable.table)
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import datetime
from importlib import import_module
import itertools
import os
import psutil
import sys
from colorama import init
from colorama import Fore
from colorama import Style
init()
import cython
import numpy as np
from terminaltables import SingleTable
from tqdm import tqdm
from pathlib import Path
import gprMax.config as config
from .cuda.fields_updates import kernel_template_fields
from .cuda.snapshots import kernel_template_store_snapshot
from .cuda.source_updates import kernel_template_sources
from .cython.yee_cell_build import build_electric_components
from .cython.yee_cell_build import build_magnetic_components
from .exceptions import GeneralError
from .fields_outputs import kernel_template_store_outputs
from .fields_outputs import write_hdf5_outputfile
from .grid import FDTDGrid
from .grid import dispersion_analysis
from .input_cmds_geometry import process_geometrycmds
from .input_cmds_file import process_python_include_code
from .input_cmds_file import write_processed_file
from .input_cmds_file import check_cmd_names
from .input_cmds_file import parse_hash_commands
from .input_cmds_singleuse import process_singlecmds
from .input_cmds_multiuse import process_multicmds
from .materials import Material
from .materials import process_materials
from .pml import CFS
from .pml import PML
from .pml import build_pml
from .pml import pml_information
from .receivers import gpu_initialise_rx_arrays
from .receivers import gpu_get_rx_array
from .receivers import Rx
from .snapshots import Snapshot
from .snapshots import gpu_initialise_snapshot_array
from .snapshots import gpu_get_snapshot_array
from .sources import gpu_initialise_src_arrays
from .utilities import get_terminal_width
from .utilities import human_size
from .utilities import open_path_file
from .utilities import round32
from .utilities import timer
from .utilities import Printer
from .scene import Scene
from .solvers import create_solver
class ModelBuildRun:
def __init__(self, G, sim_config, model_config):
self.G = G
self.sim_config = sim_config
self.model_config = model_config
self.printer = Printer(config)
# Monitor memory usage
self.p = None
def build(self):
"""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
currentmodelrun (int): Current model run number.
modelend (int): Number of last model to run.
numbermodelruns (int): Total number of model runs.
inputfile (object): File object for the input file.
usernamespace (dict): Namespace that can be accessed by user
in any Python code blocks in input file.
Returns:
tsolve (int): Length of time (seconds) of main FDTD calculations
"""
# Monitor memory usage
self.p = psutil.Process()
# Normal model reading/building process; bypassed if geometry information to be reused
if self.model_config.reuse_geometry:
self.reuse_geometry()
else:
self.build_geometry()
G = self.G
# Adjust position of simple sources and receivers if required
if G.srcsteps[0] != 0 or G.srcsteps[1] != 0 or G.srcsteps[2] != 0:
for source in itertools.chain(G.hertziandipoles, G.magneticdipoles):
if currentmodelrun == 1:
if source.xcoord + G.srcsteps[0] * modelend < 0 or source.xcoord + G.srcsteps[0] * modelend > G.nx or source.ycoord + G.srcsteps[1] * modelend < 0 or source.ycoord + G.srcsteps[1] * modelend > G.ny or source.zcoord + G.srcsteps[2] * modelend < 0 or source.zcoord + G.srcsteps[2] * modelend > G.nz:
raise GeneralError('Source(s) will be stepped to a position outside the domain.')
source.xcoord = source.xcoordorigin + (currentmodelrun - 1) * G.srcsteps[0]
source.ycoord = source.ycoordorigin + (currentmodelrun - 1) * G.srcsteps[1]
source.zcoord = source.zcoordorigin + (currentmodelrun - 1) * G.srcsteps[2]
if G.rxsteps[0] != 0 or G.rxsteps[1] != 0 or G.rxsteps[2] != 0:
for receiver in G.rxs:
if currentmodelrun == 1:
if receiver.xcoord + G.rxsteps[0] * modelend < 0 or receiver.xcoord + G.rxsteps[0] * modelend > G.nx or receiver.ycoord + G.rxsteps[1] * modelend < 0 or receiver.ycoord + G.rxsteps[1] * modelend > G.ny or receiver.zcoord + G.rxsteps[2] * modelend < 0 or receiver.zcoord + G.rxsteps[2] * modelend > G.nz:
raise GeneralError('Receiver(s) will be stepped to a position outside the domain.')
receiver.xcoord = receiver.xcoordorigin + (currentmodelrun - 1) * G.rxsteps[0]
receiver.ycoord = receiver.ycoordorigin + (currentmodelrun - 1) * G.rxsteps[1]
receiver.zcoord = receiver.zcoordorigin + (currentmodelrun - 1) * G.rxsteps[2]
# Write files for any geometry views and geometry object outputs
if not (G.geometryviews or G.geometryobjectswrite) and self.sim_config.geometry_only and config.is_messages():
print(Fore.RED + '\nWARNING: No geometry views or geometry objects to output found.' + Style.RESET_ALL)
if config.is_messages(): print()
for i, geometryview in enumerate(G.geometryviews):
geometryview.set_filename(self.model_config.appendmodelnumber)
pbar = tqdm(total=geometryview.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry view file {}/{}, {}'.format(i + 1, len(G.geometryviews), os.path.split(geometryview.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
geometryview.write_vtk(G, pbar)
pbar.close()
for i, geometryobject in enumerate(G.geometryobjectswrite):
pbar = tqdm(total=geometryobject.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry object file {}/{}, {}'.format(i + 1, len(G.geometryobjectswrite), os.path.split(geometryobject.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
geometryobject.write_hdf5(G, pbar)
pbar.close()
# If only writing geometry information
if self.sim_config.geometry_only:
tsolve = 0
def build_geometry(self):
model_config = self.model_config
sim_config = self.sim_config
G = self.G
printer = Printer(config)
printer.print(model_config.next_model)
scene = self.build_scene()
# combine available grids
grids = [G] + G.subgrids
gridbuilders = [GridBuilder(grid, self.printer) for grid in grids]
for gb in gridbuilders:
gb.printer.print(pml_information(gb.grid))
gb.build_pmls()
gb.build_components()
gb.tm_grid_update()
gb.update_voltage_source_materials()
gb.grid.initialise_std_update_coeff_arrays()
# Set datatype for dispersive arrays if there are any dispersive materials.
if config.materials['maxpoles'] != 0:
drudelorentz = any([m for m in G.materials if 'drude' in m.type or 'lorentz' in m.type])
if drudelorentz:
config.materials['dispersivedtype'] = config.dtypes['complex']
config.materials['dispersiveCdtype'] = config.dtypes['C_complex']
else:
config.materials['dispersivedtype'] = config.dtypes['float_or_double']
config.materials['dispersiveCdtype'] = config.dtypes['C_float_or_double']
# Update estimated memory (RAM) usage
G.memoryusage += int(3 * config.materials['maxpoles'] * (G.nx + 1) * (G.ny + 1) * (G.nz + 1) * np.dtype(config.materials['dispersivedtype']).itemsize)
G.memory_check()
printer.print('\nMemory (RAM) required - updated (dispersive): ~{}\n'.format(human_size(G.memoryusage)))
for gb in gridbuilders:
gb.grid.initialise_dispersive_arrays(config.materials['dispersivedtype'])
# Check there is sufficient memory to store any snapshots
if G.snapshots:
snapsmemsize = 0
for snap in G.snapshots:
# 2 x required to account for electric and magnetic fields
snapsmemsize += (2 * snap.datasizefield)
G.memoryusage += int(snapsmemsize)
G.memory_check(snapsmemsize=int(snapsmemsize))
printer.print('\nMemory (RAM) required - updated (snapshots): ~{}\n'.format(human_size(G.memoryusage)))
for gb in gridbuilders:
gb.build_materials()
# Check to see if numerical dispersion might be a problem
results = dispersion_analysis(G)
if results['error']:
printer.print(Fore.RED + "\nWARNING: Numerical dispersion analysis not carried out as {}".format(results['error']) + Style.RESET_ALL)
elif results['N'] < config.numdispersion['mingridsampling']:
raise GeneralError("Non-physical wave propagation: Material '{}' has wavelength sampled by {} cells, less than required minimum for physical wave propagation. Maximum significant frequency estimated as {:g}Hz".format(results['material'].ID, results['N'], results['maxfreq']))
elif results['deltavp'] and np.abs(results['deltavp']) > config.numdispersion['maxnumericaldisp']:
printer.print(Fore.RED + "\nWARNING: Potentially significant numerical dispersion. Estimated largest physical phase-velocity error is {:.2f}% in material '{}' whose wavelength sampled by {} cells. Maximum significant frequency estimated as {:g}Hz".format(results['deltavp'], results['material'].ID, results['N'], results['maxfreq']) + Style.RESET_ALL)
elif results['deltavp']:
printer.print("\nNumerical dispersion analysis: estimated largest physical phase-velocity error is {:.2f}% in material '{}' whose wavelength sampled by {} cells. Maximum significant frequency estimated as {:g}Hz".format(results['deltavp'], results['material'].ID, results['N'], results['maxfreq']))
def reuse_geometry(self):
G = self.G
inputfilestr = '\n--- Model {}/{}, input file (not re-processed, i.e. geometry fixed): {}'.format(self.model_config.appendmodelnumber, self.sim_config.model_end, self.sim_config.input_file_path)
self.printer.print(Fore.GREEN + '{} {}\n'.format(self.model_config.inputfilestr, '-' * (get_terminal_width() - 1 - len(inputfilestr))) + Style.RESET_ALL)
for grid in [G] + G.subgrids:
grid.reset_fields()
def build_scene(self):
G = self.G
# api for multiple scenes / model runs
scene = self.model_config.get_scene()
# if there is no scene - process the hash commands instead
if not scene:
scene = Scene()
# parse the input file into user objects and add them to the scene
scene = parse_hash_commands(self.model_config, G, scene)
# Creates the internal simulation objects.
scene.create_internal_objects(G)
return scene
def create_output_directory(self):
if self.G.outputdirectory:
# Check and set output directory and filename
try:
os.mkdir(self.G.outputdirectory)
self.printer.print('\nCreated output directory: {}'.format(self.G.outputdirectory))
except FileExistsError:
pass
# modify the output path (hack)
self.model_config.output_file_path_ext = Path(self.G.outputdirectory, self.model_config.output_file_path_ext)
def run_model(self, solver):
G = self.G
self.create_output_directory()
self.printer.print('\nOutput file: {}\n'.format(self.model_config.output_file_path_ext))
tsolve = self.solve(solver)
# Write an output file in HDF5 format
write_hdf5_outputfile(self.model_config.output_file_path_ext, G)
# Write any snapshots to file
if G.snapshots:
# Create directory and construct filename from user-supplied name and model run number
snapshotdir = self.model_config.snapshot_dir
if not os.path.exists(snapshotdir):
os.mkdir(snapshotdir)
self.printer.print()
for i, snap in enumerate(G.snapshots):
snap.filename = snapshotdir + snap.basefilename + '.vti'
pbar = tqdm(total=snap.vtkdatawritesize, leave=True, unit='byte', unit_scale=True, desc='Writing snapshot file {} of {}, {}'.format(i + 1, len(G.snapshots), os.path.split(snap.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
snap.write_vtk_imagedata(pbar, G)
pbar.close()
self.printer.print()
memGPU = ''
if config.cuda['gpus']:
memGPU = ' host + ~{} GPU'.format(human_size(self.solver.get_memsolve()))
self.printer.print('\nMemory (RAM) used: ~{}{}'.format(human_size(self.p.memory_full_info().uss), memGPU))
self.printer.print('Solving time [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=tsolve)))
return tsolve
def solve(self, solver):
"""
Solving using FDTD method on CPU. Parallelised using Cython (OpenMP) for
electric and magnetic field updates, and PML updates.
Args:
currentmodelrun (int): Current model run number.
modelend (int): Number of last model to run.
G (class): Grid class instance - holds essential parameters describing the model.
Returns:
tsolve (float): Time taken to execute solving (seconds)
"""
G = self.G
if config.is_messages():
iterator = tqdm(range(G.iterations), desc='Running simulation, model ' + str(self.model_config
.i + 1) + '/' + str(self.sim_config.model_end), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
else:
iterator = range(0, G.iterations)
tsolve = solver.solve(iterator)
return tsolve
class GridBuilder:
def __init__(self, grid, printer):
self.grid = grid
self.printer = printer
def build_pmls(self):
grid = self.grid
# build the PMLS
pbar = tqdm(total=sum(1 for value in grid.pmlthickness.values() if value > 0), desc='Building {} Grid PML boundaries'.format(self.grid.name), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
for pml_id, thickness in grid.pmlthickness.items():
build_pml(grid, pml_id, thickness)
pbar.update()
pbar.close()
def build_components(self):
# Build the model, i.e. set the material properties (ID) for every edge
# of every Yee cell
self.printer.print('')
pbar = tqdm(total=2, desc='Building {} grid'.format(self.grid.name), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
build_electric_components(self.grid.solid, self.grid.rigidE, self.grid.ID, self.grid)
pbar.update()
build_magnetic_components(self.grid.solid, self.grid.rigidH, self.grid.ID, self.grid)
pbar.update()
pbar.close()
def tm_grid_update(self):
if '2D TMx' == config.general['mode']:
self.grid.tmx()
elif '2D TMy' == config.general['mode']:
self.grid.tmy()
elif '2D TMz' == config.general['mode']:
self.grid.tmz()
def update_voltage_source_materials(self):
# Process any voltage sources (that have resistance) to create a new
# material at the source location
for voltagesource in self.grid.voltagesources:
voltagesource.create_material(self.grid)
def build_materials(self):
# Process complete list of materials - calculate update coefficients,
# store in arrays, and build text list of materials/properties
materialsdata = process_materials(self.grid)
materialstable = SingleTable(materialsdata)
materialstable.outer_border = False
materialstable.justify_columns[0] = 'right'
self.printer.print('\n{} Grid Materials:'.format(self.grid.name))
self.printer.print(materialstable.table)

查看文件

@@ -1,121 +1,121 @@
from .user_inputs import create_user_input_points
from .materials import create_built_in_materials
from .cmds_single_use import UserObjectSingle
from .cmds_single_use import DomainSingle
from .cmds_single_use import Discretisation
from .cmds_single_use import TimeWindow
from .cmds_multiple import UserObjectMulti
from .subgrids.user_objects import SubGridBase as SubGridUserBase
from .cmds_geometry.cmds_geometry import UserObjectGeometry
from .exceptions import CmdInputError
from .cmds_geometry.fractal_box_builder import FractalBoxBuilder
from .utilities import human_size
class Scene:
"""Scene stores all of the user created objects
"""
def __init__(self):
self.multiple_cmds = []
self.single_cmds = []
self.geometry_cmds = []
self.essential_cmds = [DomainSingle, TimeWindow, Discretisation]
# fractal box commands have an additional nonuser object which
# process modifications
fbb = FractalBoxBuilder()
self.add(fbb)
def add(self, node):
if isinstance(node, UserObjectMulti):
self.multiple_cmds.append(node)
elif isinstance(node, UserObjectGeometry):
self.geometry_cmds.append(node)
elif isinstance(node, UserObjectSingle):
self.single_cmds.append(node)
else:
raise Exception('This Object is Unknown to gprMax')
def process_subgrid_commands(self, subgrids):
# check for subgrid user objects
def func(obj):
if isinstance(obj, SubGridUserBase):
return True
else:
return False
# subgrid user objects
subgrid_cmds = list(filter(func, self.multiple_cmds))
# iterate through the user command objects under the subgrid user object
for sg_cmd in subgrid_cmds:
# when the subgrid is created its reference is attached to its user
# object. this reference allows the multi and geo user objects
# to build in the correct subgrid.
sg = sg_cmd.subgrid
self.process_cmds(sg_cmd.children_multiple, sg)
self.process_cmds(sg_cmd.children_geometry, sg)
def process_cmds(self, commands, grid):
cmds_sorted = sorted(commands, key=lambda cmd: cmd.order)
for obj in cmds_sorted:
# in the first level all objects belong to the main grid
uip = create_user_input_points(grid)
# Create an instance to check the geometry points provided by the
# user. The way the point are checked depends on which grid the
# points belong to.
obj.create(grid, uip)
return self
def process_singlecmds(self, G):
# check for duplicate commands and warn user if they exist
cmds_unique = list(set(self.single_cmds))
if len(cmds_unique) != len(self.single_cmds):
raise CmdInputError('Duplicate Single Commands exist in the input.')
# check essential cmds and warn user if missing
for cmd_type in self.essential_cmds:
d = any([isinstance(cmd, cmd_type) for cmd in cmds_unique])
if not d:
raise CmdInputError('Your input file is missing essential commands required to run a model. Essential commands are: Domain, Discretisation, Time Window')
self.process_cmds(cmds_unique, G)
def create_internal_objects(self, G):
# gprMax API presents the user with UserObjects in order to build
# the internal Rx(), Cylinder() etc... objects. This function
# essentially calls the UserObject.create() function in the correct
# way
# Traverse all the user objects in the correct order and create them.
create_built_in_materials(G)
# process commands that can onlyhave a single instance
self.process_singlecmds(G)
# Process main grid multiple commands
self.process_cmds(self.multiple_cmds, G)
# Estimate and check memory (RAM) usage
G.memory_check()
#snapshot_memory_check(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)
G.initialise_grids()
# Process the main grid geometry commands
self.process_cmds(self.geometry_cmds, G)
# Process all the commands for the subgrid
self.process_subgrid_commands(G.subgrids)
return self
from .user_inputs import create_user_input_points
from .materials import create_built_in_materials
from .cmds_single_use import UserObjectSingle
from .cmds_single_use import DomainSingle
from .cmds_single_use import Discretisation
from .cmds_single_use import TimeWindow
from .cmds_multiple import UserObjectMulti
from .subgrids.user_objects import SubGridBase as SubGridUserBase
from .cmds_geometry.cmds_geometry import UserObjectGeometry
from .exceptions import CmdInputError
from .cmds_geometry.fractal_box_builder import FractalBoxBuilder
from .utilities import human_size
class Scene:
"""Scene stores all of the user created objects
"""
def __init__(self):
self.multiple_cmds = []
self.single_cmds = []
self.geometry_cmds = []
self.essential_cmds = [DomainSingle, TimeWindow, Discretisation]
# fractal box commands have an additional nonuser object which
# process modifications
fbb = FractalBoxBuilder()
self.add(fbb)
def add(self, node):
if isinstance(node, UserObjectMulti):
self.multiple_cmds.append(node)
elif isinstance(node, UserObjectGeometry):
self.geometry_cmds.append(node)
elif isinstance(node, UserObjectSingle):
self.single_cmds.append(node)
else:
raise Exception('This Object is Unknown to gprMax')
def process_subgrid_commands(self, subgrids):
# check for subgrid user objects
def func(obj):
if isinstance(obj, SubGridUserBase):
return True
else:
return False
# subgrid user objects
subgrid_cmds = list(filter(func, self.multiple_cmds))
# iterate through the user command objects under the subgrid user object
for sg_cmd in subgrid_cmds:
# when the subgrid is created its reference is attached to its user
# object. this reference allows the multi and geo user objects
# to build in the correct subgrid.
sg = sg_cmd.subgrid
self.process_cmds(sg_cmd.children_multiple, sg)
self.process_cmds(sg_cmd.children_geometry, sg)
def process_cmds(self, commands, grid):
cmds_sorted = sorted(commands, key=lambda cmd: cmd.order)
for obj in cmds_sorted:
# in the first level all objects belong to the main grid
uip = create_user_input_points(grid)
# Create an instance to check the geometry points provided by the
# user. The way the point are checked depends on which grid the
# points belong to.
obj.create(grid, uip)
return self
def process_singlecmds(self, G):
# check for duplicate commands and warn user if they exist
cmds_unique = list(set(self.single_cmds))
if len(cmds_unique) != len(self.single_cmds):
raise CmdInputError('Duplicate Single Commands exist in the input.')
# check essential cmds and warn user if missing
for cmd_type in self.essential_cmds:
d = any([isinstance(cmd, cmd_type) for cmd in cmds_unique])
if not d:
raise CmdInputError('Your input file is missing essential commands required to run a model. Essential commands are: Domain, Discretisation, Time Window')
self.process_cmds(cmds_unique, G)
def create_internal_objects(self, G):
# gprMax API presents the user with UserObjects in order to build
# the internal Rx(), Cylinder() etc... objects. This function
# essentially calls the UserObject.create() function in the correct
# way
# Traverse all the user objects in the correct order and create them.
create_built_in_materials(G)
# process commands that can onlyhave a single instance
self.process_singlecmds(G)
# Process main grid multiple commands
self.process_cmds(self.multiple_cmds, G)
# Estimate and check memory (RAM) usage
G.memory_check()
#snapshot_memory_check(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)
G.initialise_grids()
# Process the main grid geometry commands
self.process_cmds(self.geometry_cmds, G)
# Process all the commands for the subgrid
self.process_subgrid_commands(G.subgrids)
return self

查看文件

@@ -1,93 +1,96 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from gprMax.updates import CPUUpdates
from gprMax.updates import GPUUpdates
from gprMax.utilities import timer
from .grid import FDTDGrid
from .grid import GPUGrid
import gprMax.config as config
from .subgrids.solver import create_updates as create_subgrid_updates
from .subgrids.solver import SubGridSolver
def create_G(sim_config):
"""Returns the configured solver."""
if sim_config.gpu:
G = GPUGrid()
elif sim_config.subgrid:
G = FDTDGrid()
else:
G = FDTDGrid()
return G
def create_solver(G, sim_config):
"""Returns the configured solver."""
if sim_config.gpu:
updates = GPUUpdates(G)
solver = Solver(updates)
elif sim_config.subgrid:
updates = create_subgrid_updates(G)
solver = SubGridSolver(G, updates)
else:
updates = CPUUpdates(G)
solver = Solver(updates)
# a large range of function exist to advance the time step for dispersive
# materials. The correct function is set here based on the
# the required numerical precision and dispersive material type.
props = updates.adapt_dispersive_config(config)
updates.set_dispersive_updates(props)
return solver
class Solver:
"""Generic solver for Update objects"""
def __init__(self, updates):
"""Context for the model to run in. Sub-class this with contexts
i.e. an MPI context.
Args:
updates (Updates): updates contains methods to run FDTD algorithm
iterator (iterator): can be range() or tqdm()
"""
self.updates = updates
def get_G(self):
return self.updates.G
def solve(self, iterator):
"""Time step the FDTD model."""
tsolvestart = timer()
for iteration in iterator:
self.updates.grid.iteration = iteration
self.updates.store_outputs()
self.updates.store_snapshots(iteration)
self.updates.update_magnetic()
self.updates.update_magnetic_pml()
self.updates.update_magnetic_sources(iteration)
self.updates.update_electric_a()
self.updates.update_electric_pml()
self.updates.update_electric_sources(iteration)
self.updates.update_electric_b()
tsolve = timer() - tsolvestart
return tsolve
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from gprMax.updates import CPUUpdates
from gprMax.updates import GPUUpdates
from .subgrids.updates import create_updates as create_subgrid_updates
from gprMax.utilities import timer
from .grid import FDTDGrid
from .grid import GPUGrid
import gprMax.config as config
def create_G(sim_config):
"""Returns the configured solver."""
if sim_config.gpu:
G = GPUGrid()
elif sim_config.subgrid:
G = FDTDGrid()
else:
G = FDTDGrid()
return G
def create_solver(G, sim_config):
"""Returns the configured solver."""
if sim_config.gpu:
updates = GPUUpdates(G)
solver = Solver(updates)
elif sim_config.subgrid:
updates = create_subgrid_updates(G)
solver = Solver(updates, hsg=True)
else:
updates = CPUUpdates(G)
solver = Solver(updates)
# a large range of function exist to advance the time step for dispersive
# materials. The correct function is set here based on the
# the required numerical precision and dispersive material type.
props = updates.adapt_dispersive_config(config)
updates.set_dispersive_updates(props)
return solver
class Solver:
"""Generic solver for Update objects"""
def __init__(self, updates, hsg=False):
"""Context for the model to run in. Sub-class this with contexts
i.e. an MPI context.
Args:
updates (Updates): updates contains methods to run FDTD algorithm
iterator (iterator): can be range() or tqdm()
"""
self.updates = updates
self.hsg = hsg
def get_G(self):
return self.updates.G
def solve(self, iterator):
"""Time step the FDTD model."""
tsolvestart = timer()
for iteration in iterator:
self.updates.store_outputs()
self.updates.store_snapshots(iteration)
self.updates.update_magnetic()
self.updates.update_magnetic_pml()
self.updates.update_magnetic_sources()
if self.hsg:
self.updates.hsg_2()
self.updates.update_electric_a()
self.updates.update_electric_pml()
self.updates.update_electric_sources()
if self.hsg:
self.updates.hsg_1()
self.updates.update_electric_b()
tsolve = timer() - tsolvestart
return tsolve

查看文件

@@ -1,413 +1,413 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from copy import deepcopy
import numpy as np
import gprMax.config as config
from .grid import Ix
from .grid import Iy
from .grid import Iz
from .utilities import round_value
class Source(object):
"""Super-class which describes a generic source."""
def __init__(self):
self.ID = None
self.polarisation = None
self.xcoord = None
self.ycoord = None
self.zcoord = None
self.xcoordorigin = None
self.ycoordorigin = None
self.zcoordorigin = None
self.start = None
self.stop = None
self.waveformID = None
def calculate_waveform_values(self, G):
"""Calculates all waveform values for source for duration of simulation.
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Waveform values for electric sources - calculated half a timestep later
self.waveformvaluesJ = np.zeros((G.iterations), dtype=config.dtypes['float_or_double'])
# Waveform values for magnetic sources
self.waveformvaluesM = np.zeros((G.iterations), dtype=config.dtypes['float_or_double'])
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
for iteration in range(G.iterations):
time = G.dt * iteration
if time >= self.start and time <= self.stop:
# Set the time of the waveform evaluation to account for any delay in the start
time -= self.start
self.waveformvaluesJ[iteration] = waveform.calculate_value(time + 0.5 * G.dt, G.dt)
self.waveformvaluesM[iteration] = waveform.calculate_value(time, G.dt)
class VoltageSource(Source):
"""
A voltage source can be a hard source if it's resistance is zero, i.e. the
time variation of the specified electric field component is prescribed.
If it's resistance is non-zero it behaves as a resistive voltage source.
"""
def __init__(self):
super().__init__()
self.resistance = None
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
"""Updates electric field values for a voltage source.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = 'E' + self.polarisation
if self.polarisation == 'x':
if self.resistance != 0:
Ex[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * (1 / (self.resistance * G.dy * G.dz))
else:
Ex[i, j, k] = -1 * self.waveformvaluesJ[iteration] / G.dx
elif self.polarisation == 'y':
if self.resistance != 0:
Ey[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * (1 / (self.resistance * G.dx * G.dz))
else:
Ey[i, j, k] = -1 * self.waveformvaluesJ[iteration] / G.dy
elif self.polarisation == 'z':
if self.resistance != 0:
Ez[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * (1 / (self.resistance * G.dx * G.dy))
else:
Ez[i, j, k] = -1 * self.waveformvaluesJ[iteration] / G.dz
def create_material(self, G):
"""Create a new material at the voltage source location that adds the
voltage source conductivity to the underlying parameters.
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
if self.resistance != 0:
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = 'E' + self.polarisation
requirednumID = G.ID[G.IDlookup[componentID], i, j, k]
material = next(x for x in G.materials if x.numID == requirednumID)
newmaterial = deepcopy(material)
newmaterial.ID = material.ID + '+' + self.ID
newmaterial.numID = len(G.materials)
newmaterial.averagable = False
newmaterial.type += ',\nvoltage-source'
# Add conductivity of voltage source to underlying conductivity
if self.polarisation == 'x':
newmaterial.se += G.dx / (self.resistance * G.dy * G.dz)
elif self.polarisation == 'y':
newmaterial.se += G.dy / (self.resistance * G.dx * G.dz)
elif self.polarisation == 'z':
newmaterial.se += G.dz / (self.resistance * G.dx * G.dy)
G.ID[G.IDlookup[componentID], i, j, k] = newmaterial.numID
G.materials.append(newmaterial)
class HertzianDipole(Source):
"""A Hertzian dipole is an additive source (electric current density)."""
def __init__(self):
super().__init__()
self.dl = None
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
"""Updates electric field values for a Hertzian dipole.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = 'E' + self.polarisation
if self.polarisation == 'x':
Ex[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * self.dl * (1 / (G.dx * G.dy * G.dz))
elif self.polarisation == 'y':
Ey[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * self.dl * (1 / (G.dx * G.dy * G.dz))
elif self.polarisation == 'z':
Ez[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * self.dl * (1 / (G.dx * G.dy * G.dz))
class MagneticDipole(Source):
"""A magnetic dipole is an additive source (magnetic current density)."""
def __init__(self):
super().__init__()
def update_magnetic(self, iteration, updatecoeffsH, ID, Hx, Hy, Hz, G):
"""Updates magnetic field values for a magnetic dipole.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = 'H' + self.polarisation
if self.polarisation == 'x':
Hx[i, j, k] -= updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesM[iteration] * (1 / (G.dx * G.dy * G.dz))
elif self.polarisation == 'y':
Hy[i, j, k] -= updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesM[iteration] * (1 / (G.dx * G.dy * G.dz))
elif self.polarisation == 'z':
Hz[i, j, k] -= updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesM[iteration] * (1 / (G.dx * G.dy * G.dz))
def gpu_initialise_src_arrays(sources, G):
"""Initialise arrays on GPU for source coordinates/polarisation, other source information, and source waveform values.
Args:
sources (list): List of sources of one class, e.g. HertzianDipoles.
G (class): Grid class instance - holds essential parameters describing the model.
Returns:
srcinfo1_gpu (int): numpy array of source cell coordinates and polarisation information.
srcinfo2_gpu (float): numpy array of other source information, e.g. length, resistance etc...
srcwaves_gpu (float): numpy array of source waveform values.
"""
import pycuda.gpuarray as gpuarray
srcinfo1 = np.zeros((len(sources), 4), dtype=np.int32)
srcinfo2 = np.zeros((len(sources)), dtype=config.dtypes['float_or_double'])
srcwaves = np.zeros((len(sources), G.iterations), dtype=config.dtypes['float_or_double'])
for i, src in enumerate(sources):
srcinfo1[i, 0] = src.xcoord
srcinfo1[i, 1] = src.ycoord
srcinfo1[i, 2] = src.zcoord
if src.polarisation == 'x':
srcinfo1[i, 3] = 0
elif src.polarisation == 'y':
srcinfo1[i, 3] = 1
elif src.polarisation == 'z':
srcinfo1[i, 3] = 2
if src.__class__.__name__ == 'HertzianDipole':
srcinfo2[i] = src.dl
srcwaves[i, :] = src.waveformvaluesJ
elif src.__class__.__name__ == 'VoltageSource':
srcinfo2[i] = src.resistance
srcwaves[i, :] = src.waveformvaluesJ
elif src.__class__.__name__ == 'MagneticDipole':
srcwaves[i, :] = src.waveformvaluesM
srcinfo1_gpu = gpuarray.to_gpu(srcinfo1)
srcinfo2_gpu = gpuarray.to_gpu(srcinfo2)
srcwaves_gpu = gpuarray.to_gpu(srcwaves)
return srcinfo1_gpu, srcinfo2_gpu, srcwaves_gpu
class TransmissionLine(Source):
"""A transmission line source is a one-dimensional transmission line
which is attached virtually to a grid cell.
"""
def __init__(self, G):
"""
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
super().__init__()
self.resistance = None
# Coefficients for ABC termination of end of the transmission line
self.abcv0 = 0
self.abcv1 = 0
# Spatial step of transmission line (N.B if the magic time step is
# used it results in instabilities for certain impedances)
self.dl = np.sqrt(3) * config.c * G.dt
# Number of cells in the transmission line (initially a long line to
# calculate incident voltage and current); consider putting ABCs/PML at end
self.nl = round_value(0.667 * G.iterations)
# Cell position of the one-way injector excitation in the transmission line
self.srcpos = 5
# Cell position of where line connects to antenna/main grid
self.antpos = 10
self.voltage = np.zeros(self.nl, dtype=config.dtypes['float_or_double'])
self.current = np.zeros(self.nl, dtype=config.dtypes['float_or_double'])
self.Vinc = np.zeros(G.iterations, dtype=config.dtypes['float_or_double'])
self.Iinc = np.zeros(G.iterations, dtype=config.dtypes['float_or_double'])
self.Vtotal = np.zeros(G.iterations, dtype=config.dtypes['float_or_double'])
self.Itotal = np.zeros(G.iterations, dtype=config.dtypes['float_or_double'])
def calculate_incident_V_I(self, G):
"""Calculates the incident voltage and current with a long length
transmission line not connected to the main grid from: http://dx.doi.org/10.1002/mop.10415
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
for iteration in range(G.iterations):
self.Iinc[iteration] = self.current[self.antpos]
self.Vinc[iteration] = self.voltage[self.antpos]
self.update_current(iteration, G)
self.update_voltage(iteration, G)
# Shorten number of cells in the transmission line before use with main grid
self.nl = self.antpos + 1
def update_abc(self, G):
"""Updates absorbing boundary condition at end of the transmission line.
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
h = (config.c * G.dt - self.dl) / (config.c * G.dt + self.dl)
self.voltage[0] = h * (self.voltage[1] - self.abcv0) + self.abcv1
self.abcv0 = self.voltage[0]
self.abcv1 = self.voltage[1]
def update_voltage(self, iteration, G):
"""Updates voltage values along the transmission line.
Args:
iteration (int): Current iteration (timestep).
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Update all the voltage values along the line
self.voltage[1:self.nl] -= self.resistance * (config.c * G.dt / self.dl) * (self.current[1:self.nl] - self.current[0:self.nl - 1])
# Update the voltage at the position of the one-way injector excitation
self.voltage[self.srcpos] += (config.c * G.dt / self.dl) * self.waveformvaluesJ[iteration]
# Update ABC before updating current
self.update_abc(G)
def update_current(self, iteration, G):
"""Updates current values along the transmission line.
Args:
iteration (int): Current iteration (timestep).
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Update all the current values along the line
self.current[0:self.nl - 1] -= (1 / self.resistance) * (config.c * G.dt / self.dl) * (self.voltage[1:self.nl] - self.voltage[0:self.nl - 1])
# Update the current one cell before the position of the one-way injector excitation
self.current[self.srcpos - 1] += (1 / self.resistance) * (config.c * G.dt / self.dl) * self.waveformvaluesM[iteration]
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
"""Updates electric field value in the main grid from voltage value in the transmission line.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
self.update_voltage(iteration, G)
if self.polarisation == 'x':
Ex[i, j, k] = - self.voltage[self.antpos] / G.dx
elif self.polarisation == 'y':
Ey[i, j, k] = - self.voltage[self.antpos] / G.dy
elif self.polarisation == 'z':
Ez[i, j, k] = - self.voltage[self.antpos] / G.dz
def update_magnetic(self, iteration, updatecoeffsH, ID, Hx, Hy, Hz, G):
"""Updates current value in transmission line from magnetic field values in the main grid.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
if self.polarisation == 'x':
self.current[self.antpos] = Ix(i, j, k, G.Hx, G.Hy, G.Hz, G)
elif self.polarisation == 'y':
self.current[self.antpos] = Iy(i, j, k, G.Hx, G.Hy, G.Hz, G)
elif self.polarisation == 'z':
self.current[self.antpos] = Iz(i, j, k, G.Hx, G.Hy, G.Hz, G)
self.update_current(iteration, G)
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from copy import deepcopy
import numpy as np
import gprMax.config as config
from .grid import Ix
from .grid import Iy
from .grid import Iz
from .utilities import round_value
class Source(object):
"""Super-class which describes a generic source."""
def __init__(self):
self.ID = None
self.polarisation = None
self.xcoord = None
self.ycoord = None
self.zcoord = None
self.xcoordorigin = None
self.ycoordorigin = None
self.zcoordorigin = None
self.start = None
self.stop = None
self.waveformID = None
def calculate_waveform_values(self, G):
"""Calculates all waveform values for source for duration of simulation.
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Waveform values for electric sources - calculated half a timestep later
self.waveformvaluesJ = np.zeros((G.iterations), dtype=config.dtypes['float_or_double'])
# Waveform values for magnetic sources
self.waveformvaluesM = np.zeros((G.iterations), dtype=config.dtypes['float_or_double'])
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
for iteration in range(G.iterations):
time = G.dt * iteration
if time >= self.start and time <= self.stop:
# Set the time of the waveform evaluation to account for any delay in the start
time -= self.start
self.waveformvaluesJ[iteration] = waveform.calculate_value(time + 0.5 * G.dt, G.dt)
self.waveformvaluesM[iteration] = waveform.calculate_value(time, G.dt)
class VoltageSource(Source):
"""
A voltage source can be a hard source if it's resistance is zero, i.e. the
time variation of the specified electric field component is prescribed.
If it's resistance is non-zero it behaves as a resistive voltage source.
"""
def __init__(self):
super().__init__()
self.resistance = None
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
"""Updates electric field values for a voltage source.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = 'E' + self.polarisation
if self.polarisation == 'x':
if self.resistance != 0:
Ex[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * (1 / (self.resistance * G.dy * G.dz))
else:
Ex[i, j, k] = -1 * self.waveformvaluesJ[iteration] / G.dx
elif self.polarisation == 'y':
if self.resistance != 0:
Ey[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * (1 / (self.resistance * G.dx * G.dz))
else:
Ey[i, j, k] = -1 * self.waveformvaluesJ[iteration] / G.dy
elif self.polarisation == 'z':
if self.resistance != 0:
Ez[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * (1 / (self.resistance * G.dx * G.dy))
else:
Ez[i, j, k] = -1 * self.waveformvaluesJ[iteration] / G.dz
def create_material(self, G):
"""Create a new material at the voltage source location that adds the
voltage source conductivity to the underlying parameters.
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
if self.resistance != 0:
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = 'E' + self.polarisation
requirednumID = G.ID[G.IDlookup[componentID], i, j, k]
material = next(x for x in G.materials if x.numID == requirednumID)
newmaterial = deepcopy(material)
newmaterial.ID = material.ID + '+' + self.ID
newmaterial.numID = len(G.materials)
newmaterial.averagable = False
newmaterial.type += ',\nvoltage-source'
# Add conductivity of voltage source to underlying conductivity
if self.polarisation == 'x':
newmaterial.se += G.dx / (self.resistance * G.dy * G.dz)
elif self.polarisation == 'y':
newmaterial.se += G.dy / (self.resistance * G.dx * G.dz)
elif self.polarisation == 'z':
newmaterial.se += G.dz / (self.resistance * G.dx * G.dy)
G.ID[G.IDlookup[componentID], i, j, k] = newmaterial.numID
G.materials.append(newmaterial)
class HertzianDipole(Source):
"""A Hertzian dipole is an additive source (electric current density)."""
def __init__(self):
super().__init__()
self.dl = None
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
"""Updates electric field values for a Hertzian dipole.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = 'E' + self.polarisation
if self.polarisation == 'x':
Ex[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * self.dl * (1 / (G.dx * G.dy * G.dz))
elif self.polarisation == 'y':
Ey[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * self.dl * (1 / (G.dx * G.dy * G.dz))
elif self.polarisation == 'z':
Ez[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesJ[iteration] * self.dl * (1 / (G.dx * G.dy * G.dz))
class MagneticDipole(Source):
"""A magnetic dipole is an additive source (magnetic current density)."""
def __init__(self):
super().__init__()
def update_magnetic(self, iteration, updatecoeffsH, ID, Hx, Hy, Hz, G):
"""Updates magnetic field values for a magnetic dipole.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
componentID = 'H' + self.polarisation
if self.polarisation == 'x':
Hx[i, j, k] -= updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesM[iteration] * (1 / (G.dx * G.dy * G.dz))
elif self.polarisation == 'y':
Hy[i, j, k] -= updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesM[iteration] * (1 / (G.dx * G.dy * G.dz))
elif self.polarisation == 'z':
Hz[i, j, k] -= updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] * self.waveformvaluesM[iteration] * (1 / (G.dx * G.dy * G.dz))
def gpu_initialise_src_arrays(sources, G):
"""Initialise arrays on GPU for source coordinates/polarisation, other source information, and source waveform values.
Args:
sources (list): List of sources of one class, e.g. HertzianDipoles.
G (class): Grid class instance - holds essential parameters describing the model.
Returns:
srcinfo1_gpu (int): numpy array of source cell coordinates and polarisation information.
srcinfo2_gpu (float): numpy array of other source information, e.g. length, resistance etc...
srcwaves_gpu (float): numpy array of source waveform values.
"""
import pycuda.gpuarray as gpuarray
srcinfo1 = np.zeros((len(sources), 4), dtype=np.int32)
srcinfo2 = np.zeros((len(sources)), dtype=config.dtypes['float_or_double'])
srcwaves = np.zeros((len(sources), G.iterations), dtype=config.dtypes['float_or_double'])
for i, src in enumerate(sources):
srcinfo1[i, 0] = src.xcoord
srcinfo1[i, 1] = src.ycoord
srcinfo1[i, 2] = src.zcoord
if src.polarisation == 'x':
srcinfo1[i, 3] = 0
elif src.polarisation == 'y':
srcinfo1[i, 3] = 1
elif src.polarisation == 'z':
srcinfo1[i, 3] = 2
if src.__class__.__name__ == 'HertzianDipole':
srcinfo2[i] = src.dl
srcwaves[i, :] = src.waveformvaluesJ
elif src.__class__.__name__ == 'VoltageSource':
srcinfo2[i] = src.resistance
srcwaves[i, :] = src.waveformvaluesJ
elif src.__class__.__name__ == 'MagneticDipole':
srcwaves[i, :] = src.waveformvaluesM
srcinfo1_gpu = gpuarray.to_gpu(srcinfo1)
srcinfo2_gpu = gpuarray.to_gpu(srcinfo2)
srcwaves_gpu = gpuarray.to_gpu(srcwaves)
return srcinfo1_gpu, srcinfo2_gpu, srcwaves_gpu
class TransmissionLine(Source):
"""A transmission line source is a one-dimensional transmission line
which is attached virtually to a grid cell.
"""
def __init__(self, G):
"""
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
super().__init__()
self.resistance = None
# Coefficients for ABC termination of end of the transmission line
self.abcv0 = 0
self.abcv1 = 0
# Spatial step of transmission line (N.B if the magic time step is
# used it results in instabilities for certain impedances)
self.dl = np.sqrt(3) * config.c * G.dt
# Number of cells in the transmission line (initially a long line to
# calculate incident voltage and current); consider putting ABCs/PML at end
self.nl = round_value(0.667 * G.iterations)
# Cell position of the one-way injector excitation in the transmission line
self.srcpos = 5
# Cell position of where line connects to antenna/main grid
self.antpos = 10
self.voltage = np.zeros(self.nl, dtype=config.dtypes['float_or_double'])
self.current = np.zeros(self.nl, dtype=config.dtypes['float_or_double'])
self.Vinc = np.zeros(G.iterations, dtype=config.dtypes['float_or_double'])
self.Iinc = np.zeros(G.iterations, dtype=config.dtypes['float_or_double'])
self.Vtotal = np.zeros(G.iterations, dtype=config.dtypes['float_or_double'])
self.Itotal = np.zeros(G.iterations, dtype=config.dtypes['float_or_double'])
def calculate_incident_V_I(self, G):
"""Calculates the incident voltage and current with a long length
transmission line not connected to the main grid from: http://dx.doi.org/10.1002/mop.10415
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
for iteration in range(G.iterations):
self.Iinc[iteration] = self.current[self.antpos]
self.Vinc[iteration] = self.voltage[self.antpos]
self.update_current(iteration, G)
self.update_voltage(iteration, G)
# Shorten number of cells in the transmission line before use with main grid
self.nl = self.antpos + 1
def update_abc(self, G):
"""Updates absorbing boundary condition at end of the transmission line.
Args:
G (class): Grid class instance - holds essential parameters describing the model.
"""
h = (config.c * G.dt - self.dl) / (config.c * G.dt + self.dl)
self.voltage[0] = h * (self.voltage[1] - self.abcv0) + self.abcv1
self.abcv0 = self.voltage[0]
self.abcv1 = self.voltage[1]
def update_voltage(self, iteration, G):
"""Updates voltage values along the transmission line.
Args:
iteration (int): Current iteration (timestep).
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Update all the voltage values along the line
self.voltage[1:self.nl] -= self.resistance * (config.c * G.dt / self.dl) * (self.current[1:self.nl] - self.current[0:self.nl - 1])
# Update the voltage at the position of the one-way injector excitation
self.voltage[self.srcpos] += (config.c * G.dt / self.dl) * self.waveformvaluesJ[iteration]
# Update ABC before updating current
self.update_abc(G)
def update_current(self, iteration, G):
"""Updates current values along the transmission line.
Args:
iteration (int): Current iteration (timestep).
G (class): Grid class instance - holds essential parameters describing the model.
"""
# Update all the current values along the line
self.current[0:self.nl - 1] -= (1 / self.resistance) * (config.c * G.dt / self.dl) * (self.voltage[1:self.nl] - self.voltage[0:self.nl - 1])
# Update the current one cell before the position of the one-way injector excitation
self.current[self.srcpos - 1] += (1 / self.resistance) * (config.c * G.dt / self.dl) * self.waveformvaluesM[iteration]
def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G):
"""Updates electric field value in the main grid from voltage value in the transmission line.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
self.update_voltage(iteration, G)
if self.polarisation == 'x':
Ex[i, j, k] = - self.voltage[self.antpos] / G.dx
elif self.polarisation == 'y':
Ey[i, j, k] = - self.voltage[self.antpos] / G.dy
elif self.polarisation == 'z':
Ez[i, j, k] = - self.voltage[self.antpos] / G.dz
def update_magnetic(self, iteration, updatecoeffsH, ID, Hx, Hy, Hz, G):
"""Updates current value in transmission line from magnetic field values in the main grid.
Args:
iteration (int): Current iteration (timestep).
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 iteration * G.dt >= self.start and iteration * G.dt <= self.stop:
i = self.xcoord
j = self.ycoord
k = self.zcoord
if self.polarisation == 'x':
self.current[self.antpos] = Ix(i, j, k, G.Hx, G.Hy, G.Hz, G)
elif self.polarisation == 'y':
self.current[self.antpos] = Iy(i, j, k, G.Hx, G.Hy, G.Hz, G)
elif self.polarisation == 'z':
self.current[self.antpos] = Iz(i, j, k, G.Hx, G.Hy, G.Hz, G)
self.update_current(iteration, G)

查看文件

@@ -1,72 +1,72 @@
from ..grid import FDTDGrid
from ..materials import Material
from scipy.constants import mu_0
from scipy.constants import epsilon_0
from scipy.constants import c
import numpy as np
from colorama import init
from colorama import Fore
from colorama import Style
init()
class SubGridBase(FDTDGrid):
def __init__(self, **kwargs):
super().__init__()
self.mode = '3D'
self.ratio = kwargs['ratio']
if self.ratio % 2 == 0:
raise ValueError('Subgrid Error: Only odd ratios are supported')
# Name of the grid
self.name = kwargs['ID']
self.filter = kwargs['filter']
# Number of main grid cells between the IS and OS
self.is_os_sep = kwargs['is_os_sep']
# Number of subgrid grid cells between the IS and OS
self.s_is_os_sep = self.is_os_sep * self.ratio
# Distance from OS to pml or the edge of the grid when pml is off
self.pml_separation = kwargs['pml_separation']
self.pmlthickness['x0'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['y0'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['z0'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['xmax'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['ymax'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['zmax'] = kwargs['subgrid_pml_thickness']
# Number of sub cells to extend the sub grid beyond the IS boundary
d_to_pml = self.s_is_os_sep + self.pml_separation
self.n_boundary_cells = d_to_pml + self.pmlthickness['x0']
self.n_boundary_cells_x = d_to_pml + self.pmlthickness['x0']
self.n_boundary_cells_y = d_to_pml + self.pmlthickness['y0']
self.n_boundary_cells_z = d_to_pml + self.pmlthickness['z0']
self.interpolation = kwargs['interpolation']
def calculate_dt(self):
self.dt = (1 / (c * np.sqrt(
(1 / self.dx) * (1 / self.dx) +
(1 / self.dy) * (1 / self.dy) +
(1 / self.dz) * (1 / self.dz))))
def main_grid_index_to_subgrid_index(self, i, j, k):
i_s = self.n_boundary_cells_x + (i - self.i0) * self.ratio
j_s = self.n_boundary_cells_y + (j - self.j0) * self.ratio
k_s = self.n_boundary_cells_z + (k - self.k0) * self.ratio
return (i_s, j_s, k_s)
def initialise_geometry_arrays(self):
super().initialise_geometry_arrays()
from ..grid import FDTDGrid
from ..materials import Material
from scipy.constants import mu_0
from scipy.constants import epsilon_0
from scipy.constants import c
import numpy as np
from colorama import init
from colorama import Fore
from colorama import Style
init()
class SubGridBase(FDTDGrid):
def __init__(self, **kwargs):
super().__init__()
self.mode = '3D'
self.ratio = kwargs['ratio']
if self.ratio % 2 == 0:
raise ValueError('Subgrid Error: Only odd ratios are supported')
# Name of the grid
self.name = kwargs['ID']
self.filter = kwargs['filter']
# Number of main grid cells between the IS and OS
self.is_os_sep = kwargs['is_os_sep']
# Number of subgrid grid cells between the IS and OS
self.s_is_os_sep = self.is_os_sep * self.ratio
# Distance from OS to pml or the edge of the grid when pml is off
self.pml_separation = kwargs['pml_separation']
self.pmlthickness['x0'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['y0'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['z0'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['xmax'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['ymax'] = kwargs['subgrid_pml_thickness']
self.pmlthickness['zmax'] = kwargs['subgrid_pml_thickness']
# Number of sub cells to extend the sub grid beyond the IS boundary
d_to_pml = self.s_is_os_sep + self.pml_separation
self.n_boundary_cells = d_to_pml + self.pmlthickness['x0']
self.n_boundary_cells_x = d_to_pml + self.pmlthickness['x0']
self.n_boundary_cells_y = d_to_pml + self.pmlthickness['y0']
self.n_boundary_cells_z = d_to_pml + self.pmlthickness['z0']
self.interpolation = kwargs['interpolation']
def calculate_dt(self):
self.dt = (1 / (c * np.sqrt(
(1 / self.dx) * (1 / self.dx) +
(1 / self.dy) * (1 / self.dy) +
(1 / self.dz) * (1 / self.dz))))
def main_grid_index_to_subgrid_index(self, i, j, k):
i_s = self.n_boundary_cells_x + (i - self.i0) * self.ratio
j_s = self.n_boundary_cells_y + (j - self.j0) * self.ratio
k_s = self.n_boundary_cells_z + (k - self.k0) * self.ratio
return (i_s, j_s, k_s)
def initialise_geometry_arrays(self):
super().initialise_geometry_arrays()

查看文件

@@ -1,146 +1,146 @@
from ..receivers import Rx
class ReferenceRx(Rx):
"""Receiver that micks a receiver in coarse grid."""
"""We often want to compare an output in a fine reference solution with a
the solution in the coarse grid of a subgridded solution. This receiver
moves the output points in the fine grid such that they are in the same
position as the coarse grid.
"""
def __init__(self):
"""Constructor."""
super().__init__()
def get_field(self, str_id, field):
"""Return the field value at the equivalent coarse yee cell.
Parameters
----------
str_id : str
'Ex' etc...
field : np array
e.g. numpy array of grid.Ez
Returns
-------
float
Field value
"""
d = {
'Ex': self.get_Ex_from_field,
'Ey': self.get_Ey_from_field,
'Ez': self.get_Ez_from_field,
'Hx': self.get_Hx_from_field,
'Hy': self.get_Hy_from_field,
'Hz': self.get_Hz_from_field
}
e = d[str_id](field)
return e
def get_Ex_from_field(self, Ex):
"""Return the Ex field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Ex
Returns
-------
float
Ex field value
"""
# offset = ratio // 2
e = Ex[self.xcoord + self.offset, self.ycoord, self.zcoord]
return e
def get_Ey_from_field(self, Ey):
"""Return the Ey field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Ex
Returns
-------
float
Ey field value
"""
e = Ey[self.xcoord, self.ycoord + self.offset, self.zcoord]
return e
def get_Ez_from_field(self, Ez):
"""Return the Ez field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Ez
Returns
-------
float
Ez field value
"""
e = Ez[self.xcoord, self.ycoord, self.zcoord + self.offset]
return e
def get_Hx_from_field(self, Hx):
"""Return the Hx field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Hx
Returns
-------
float
Hx field value
"""
e = Hx[self.xcoord, self.ycoord + self.offset, self.zcoord + self.offset]
return e
def get_Hy_from_field(self, Hy):
"""Return the Hy field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Hx
Returns
-------
float
Hy field value
"""
e = Hy[self.xcoord + self.offset, self.ycoord, self.zcoord + self.offset]
return e
def get_Hz_from_field(self, Hz):
"""Return the Hz field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Hx
Returns
-------
float
Hz field value
"""
e = Hz[self.xcoord + self.offset, self.ycoord + self.offset, self.zcoord]
return e
from ..receivers import Rx
class ReferenceRx(Rx):
"""Receiver that micks a receiver in coarse grid."""
"""We often want to compare an output in a fine reference solution with a
the solution in the coarse grid of a subgridded solution. This receiver
moves the output points in the fine grid such that they are in the same
position as the coarse grid.
"""
def __init__(self):
"""Constructor."""
super().__init__()
def get_field(self, str_id, field):
"""Return the field value at the equivalent coarse yee cell.
Parameters
----------
str_id : str
'Ex' etc...
field : np array
e.g. numpy array of grid.Ez
Returns
-------
float
Field value
"""
d = {
'Ex': self.get_Ex_from_field,
'Ey': self.get_Ey_from_field,
'Ez': self.get_Ez_from_field,
'Hx': self.get_Hx_from_field,
'Hy': self.get_Hy_from_field,
'Hz': self.get_Hz_from_field
}
e = d[str_id](field)
return e
def get_Ex_from_field(self, Ex):
"""Return the Ex field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Ex
Returns
-------
float
Ex field value
"""
# offset = ratio // 2
e = Ex[self.xcoord + self.offset, self.ycoord, self.zcoord]
return e
def get_Ey_from_field(self, Ey):
"""Return the Ey field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Ex
Returns
-------
float
Ey field value
"""
e = Ey[self.xcoord, self.ycoord + self.offset, self.zcoord]
return e
def get_Ez_from_field(self, Ez):
"""Return the Ez field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Ez
Returns
-------
float
Ez field value
"""
e = Ez[self.xcoord, self.ycoord, self.zcoord + self.offset]
return e
def get_Hx_from_field(self, Hx):
"""Return the Hx field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Hx
Returns
-------
float
Hx field value
"""
e = Hx[self.xcoord, self.ycoord + self.offset, self.zcoord + self.offset]
return e
def get_Hy_from_field(self, Hy):
"""Return the Hy field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Hx
Returns
-------
float
Hy field value
"""
e = Hy[self.xcoord + self.offset, self.ycoord, self.zcoord + self.offset]
return e
def get_Hz_from_field(self, Hz):
"""Return the Hz field value from the equivalent coarse yee cell.
Parameters
----------
Ex : 3d numpy array
e.g. grid.Hx
Returns
-------
float
Hz field value
"""
e = Hz[self.xcoord + self.offset, self.ycoord + self.offset, self.zcoord]
return e

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

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

查看文件

@@ -1,148 +1,148 @@
from .base import SubGridBase
from ..cython.fields_updates_hsg import cython_update_is
from ..cython.fields_updates_hsg import cython_update_magnetic_os
from ..cython.fields_updates_hsg import cython_update_electric_os
from ..utilities import human_size
from colorama import init, Fore, Style
init()
class SubGridHSG(SubGridBase):
gridtype = '3DSUBGRID'
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.gridtype = SubGridHSG.gridtype
def update_magnetic_is(self, precursors):
"""Update the subgrid nodes at the IS with the currents derived
from the main grid.
Args: nwl, nwm, nwn, face, field, inc_field, lookup_id, sign, mod, co
"""
# Hz = c0Hz - c1Ey + c2Ex
# Hy = c0Hy - c3Ex + c1Ez
# Hx = c0Hx - c2Ez + c3Ey
# bottom and top
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwx, self.nwy + 1, self.nwz, 1, self.Hy, precursors.ex_bottom, precursors.ex_top, self.IDlookup['Hy'], 1, -1, 3, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwx + 1, self.nwy, self.nwz, 1, self.Hx, precursors.ey_bottom, precursors.ey_top, self.IDlookup['Hx'], -1, 1, 3, self.nthreads)
# left and right
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwy, self.nwz + 1, self.nwx, 2, self.Hz, precursors.ey_left, precursors.ey_right, self.IDlookup['Hz'], 1, -1, 1, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwy + 1, self.nwz, self.nwx, 2, self.Hy, precursors.ez_left, precursors.ez_right, self.IDlookup['Hy'], -1, 1, 1, self.nthreads)
# front and back
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwx, self.nwz + 1, self.nwy, 3, self.Hz, precursors.ex_front, precursors.ex_back, self.IDlookup['Hz'], -1, 1, 2, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwx + 1, self.nwz, self.nwy, 3, self.Hx, precursors.ez_front, precursors.ez_back, self.IDlookup['Hx'], 1, -1, 2, self.nthreads)
def update_electric_is(self, precursors):
# Args: nwl, nwm, nwn, face, field, inc_field, lookup_id, sign, mod, co
# Ex = c0(Ex) + c2(dHz) - c3(dHy)
# Ey = c0(Ey) + c3(dHx) - c1(dHz)
# Ez = c0(Ez) + c1(dHy) - c2(dHx)
# bottom and top
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwx, self.nwy + 1, self.nwz, 1, self.Ex, precursors.hy_bottom, precursors.hy_top, self.IDlookup['Ex'], 1, -1, 3, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwx + 1, self.nwy, self.nwz, 1, self.Ey, precursors.hx_bottom, precursors.hx_top, self.IDlookup['Ey'], -1, 1, 3, self.nthreads)
# left and right
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwy, self.nwz + 1, self.nwx, 2, self.Ey, precursors.hz_left, precursors.hz_right, self.IDlookup['Ey'], 1, -1, 1, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwy + 1, self.nwz, self.nwx, 2, self.Ez, precursors.hy_left, precursors.hy_right, self.IDlookup['Ez'], -1, 1, 1, self.nthreads)
# front and back
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwx, self.nwz + 1, self.nwy, 3, self.Ex, precursors.hz_front, precursors.hz_back, self.IDlookup['Ex'], -1, 1, 2, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwx + 1, self.nwz, self.nwy, 3, self.Ez, precursors.hx_front, precursors.hx_back, self.IDlookup['Ez'], 1, -1, 2, self.nthreads)
def update_electric_os(self, main_grid):
i_l = self.i0 - self.is_os_sep
i_u = self.i1 + self.is_os_sep
j_l = self.j0 - self.is_os_sep
j_u = self.j1 + self.is_os_sep
k_l = self.k0 - self.is_os_sep
k_u = self.k1 + self.is_os_sep
# Args: sub_grid, normal, l_l, l_u, m_l, m_u, n_l, n_u, nwn, lookup_id, field, inc_field, co, sign_n, sign_f
# Form of FDTD update equations for E
# Ex = c0(Ex) + c2(dHz) - c3(dHy)
# Ey = c0(Ey) + c3(dHx) - c1(dHz)
# Ez = c0(Ez) + c1(dHy) - c2(dHx)
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 3, i_l, i_u, k_l, k_u + 1, j_l, j_u, self.nwy, main_grid.IDlookup['Ex'], main_grid.Ex, self.Hz, 2, 1, -1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 3, i_l, i_u + 1, k_l, k_u, j_l, j_u, self.nwy, main_grid.IDlookup['Ez'], main_grid.Ez, self.Hx, 2, -1, 1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
# Left and Right
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 2, j_l, j_u, k_l, k_u + 1, i_l, i_u, self.nwx, main_grid.IDlookup['Ey'], main_grid.Ey, self.Hz, 1, -1, 1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 2, j_l, j_u + 1, k_l, k_u, i_l, i_u, self.nwx, main_grid.IDlookup['Ez'], main_grid.Ez, self.Hy, 1, 1, -1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
# Bottom and Top
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 1, i_l, i_u, j_l, j_u + 1, k_l, k_u, self.nwz, main_grid.IDlookup['Ex'], main_grid.Ex, self.Hy, 3, -1, 1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 1, i_l, i_u + 1, j_l, j_u, k_l, k_u, self.nwz, main_grid.IDlookup['Ey'], main_grid.Ey, self.Hx, 3, 1, -1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
def update_magnetic_os(self, main_grid):
i_l = self.i0 - self.is_os_sep
i_u = self.i1 + self.is_os_sep
j_l = self.j0 - self.is_os_sep
j_u = self.j1 + self.is_os_sep
k_l = self.k0 - self.is_os_sep
k_u = self.k1 + self.is_os_sep
# Form of FDTD update equations for H
# Hz = c0Hz - c1Ey + c2Ex
# Hy = c0Hy - c3Ex + c1Ez
# Hx = c0Hx - c2Ez + c3Ey
# Args: sub_grid, normal, l_l, l_u, m_l, m_u, n_l, n_u, nwn, lookup_id, field, inc_field, co, sign_n, sign_f):
# Front and back
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 3, i_l, i_u, k_l, k_u + 1, j_l - 1, j_u, self.nwy, main_grid.IDlookup['Hz'], main_grid.Hz, self.Ex, 2, 1, -1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 3, i_l, i_u + 1, k_l, k_u, j_l - 1, j_u, self.nwy, main_grid.IDlookup['Hx'], main_grid.Hx, self.Ez, 2, -1, 1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
# Left and Right
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 2, j_l, j_u, k_l, k_u + 1, i_l - 1, i_u, self.nwx, main_grid.IDlookup['Hz'], main_grid.Hz, self.Ey, 1, -1, 1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 2, j_l, j_u + 1, k_l, k_u, i_l - 1, i_u, self.nwx, main_grid.IDlookup['Hy'], main_grid.Hy, self.Ez, 1, 1, -1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
# bottom and top
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 1, i_l, i_u, j_l, j_u + 1, k_l - 1, k_u, self.nwz, main_grid.IDlookup['Hy'], main_grid.Hy, self.Ex, 3, -1, 1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 1, i_l, i_u + 1, j_l, j_u, k_l - 1, k_u, self.nwz, main_grid.IDlookup['Hx'], main_grid.Hx, self.Ey, 3, 1, -1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
def __str__(self):
self.memory_estimate_basic()
s = '\n'
s += Fore.CYAN
s += 'Sub Grid HSG\n'
s += 'Name: {}\n'.format(self.name)
s += 'dx, dy, dz: {}m {}m {}m\n'.format(self.dx, self.dy, self.dz)
s += 'dt: {}s\n'.format(self.dt)
s += 'Memory Estimate: {}\n'.format(human_size(self.memoryusage))
s += 'Position: ({}m, {}m, {}m), ({}m, {}m, {}m)\n'.format(self.x1,
self.y1,
self.z1,
self.x2,
self.y2,
self.z2)
s += 'Main Grid Indices: lower left({}, {}, {}), upper right({}, {}, {})\n'.format(self.i0, self.j0, self.k0, self.i1, self.j1, self.k1)
s += 'Total Cells: {} {} {}\n'.format(self.nx, self.ny, self.nz)
s += 'Working Region Cells: {} {} {}\n'.format(self.nwx,
self.nwy,
self.nwz)
for h in self.hertziandipoles:
s += 'Hertizian dipole: {} {} {}\n'.format(h.xcoord,
h.ycoord,
h.zcoord)
s += str([x for x in self.waveforms
if x.ID == h.waveformID][0]) + '\n'
for r in self.rxs:
s += 'Receiver: {} {} {}\n'.format(r.xcoord, r.ycoord, r.zcoord)
for tl in self.transmissionlines:
s += 'Transmission Line: {} {} {}\n'.format(tl.xcoord, tl.ycoord, tl.zcoord)
s += str([x for x in self.waveforms
if x.ID == tl.waveformID][0]) + '\n'
s += Style.RESET_ALL
return s
from .base import SubGridBase
from ..cython.fields_updates_hsg import cython_update_is
from ..cython.fields_updates_hsg import cython_update_magnetic_os
from ..cython.fields_updates_hsg import cython_update_electric_os
from ..utilities import human_size
from colorama import init, Fore, Style
init()
class SubGridHSG(SubGridBase):
gridtype = '3DSUBGRID'
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.gridtype = SubGridHSG.gridtype
def update_magnetic_is(self, precursors):
"""Update the subgrid nodes at the IS with the currents derived
from the main grid.
Args: nwl, nwm, nwn, face, field, inc_field, lookup_id, sign, mod, co
"""
# Hz = c0Hz - c1Ey + c2Ex
# Hy = c0Hy - c3Ex + c1Ez
# Hx = c0Hx - c2Ez + c3Ey
# bottom and top
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwx, self.nwy + 1, self.nwz, 1, self.Hy, precursors.ex_bottom, precursors.ex_top, self.IDlookup['Hy'], 1, -1, 3, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwx + 1, self.nwy, self.nwz, 1, self.Hx, precursors.ey_bottom, precursors.ey_top, self.IDlookup['Hx'], -1, 1, 3, self.nthreads)
# left and right
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwy, self.nwz + 1, self.nwx, 2, self.Hz, precursors.ey_left, precursors.ey_right, self.IDlookup['Hz'], 1, -1, 1, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwy + 1, self.nwz, self.nwx, 2, self.Hy, precursors.ez_left, precursors.ez_right, self.IDlookup['Hy'], -1, 1, 1, self.nthreads)
# front and back
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwx, self.nwz + 1, self.nwy, 3, self.Hz, precursors.ex_front, precursors.ex_back, self.IDlookup['Hz'], -1, 1, 2, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, self.n_boundary_cells, -1, self.nwx + 1, self.nwz, self.nwy, 3, self.Hx, precursors.ez_front, precursors.ez_back, self.IDlookup['Hx'], 1, -1, 2, self.nthreads)
def update_electric_is(self, precursors):
# Args: nwl, nwm, nwn, face, field, inc_field, lookup_id, sign, mod, co
# Ex = c0(Ex) + c2(dHz) - c3(dHy)
# Ey = c0(Ey) + c3(dHx) - c1(dHz)
# Ez = c0(Ez) + c1(dHy) - c2(dHx)
# bottom and top
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwx, self.nwy + 1, self.nwz, 1, self.Ex, precursors.hy_bottom, precursors.hy_top, self.IDlookup['Ex'], 1, -1, 3, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwx + 1, self.nwy, self.nwz, 1, self.Ey, precursors.hx_bottom, precursors.hx_top, self.IDlookup['Ey'], -1, 1, 3, self.nthreads)
# left and right
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwy, self.nwz + 1, self.nwx, 2, self.Ey, precursors.hz_left, precursors.hz_right, self.IDlookup['Ey'], 1, -1, 1, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwy + 1, self.nwz, self.nwx, 2, self.Ez, precursors.hy_left, precursors.hy_right, self.IDlookup['Ez'], -1, 1, 1, self.nthreads)
# front and back
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwx, self.nwz + 1, self.nwy, 3, self.Ex, precursors.hz_front, precursors.hz_back, self.IDlookup['Ex'], -1, 1, 2, self.nthreads)
cython_update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, self.n_boundary_cells, 0, self.nwx + 1, self.nwz, self.nwy, 3, self.Ez, precursors.hx_front, precursors.hx_back, self.IDlookup['Ez'], 1, -1, 2, self.nthreads)
def update_electric_os(self, main_grid):
i_l = self.i0 - self.is_os_sep
i_u = self.i1 + self.is_os_sep
j_l = self.j0 - self.is_os_sep
j_u = self.j1 + self.is_os_sep
k_l = self.k0 - self.is_os_sep
k_u = self.k1 + self.is_os_sep
# Args: sub_grid, normal, l_l, l_u, m_l, m_u, n_l, n_u, nwn, lookup_id, field, inc_field, co, sign_n, sign_f
# Form of FDTD update equations for E
# Ex = c0(Ex) + c2(dHz) - c3(dHy)
# Ey = c0(Ey) + c3(dHx) - c1(dHz)
# Ez = c0(Ez) + c1(dHy) - c2(dHx)
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 3, i_l, i_u, k_l, k_u + 1, j_l, j_u, self.nwy, main_grid.IDlookup['Ex'], main_grid.Ex, self.Hz, 2, 1, -1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 3, i_l, i_u + 1, k_l, k_u, j_l, j_u, self.nwy, main_grid.IDlookup['Ez'], main_grid.Ez, self.Hx, 2, -1, 1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
# Left and Right
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 2, j_l, j_u, k_l, k_u + 1, i_l, i_u, self.nwx, main_grid.IDlookup['Ey'], main_grid.Ey, self.Hz, 1, -1, 1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 2, j_l, j_u + 1, k_l, k_u, i_l, i_u, self.nwx, main_grid.IDlookup['Ez'], main_grid.Ez, self.Hy, 1, 1, -1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
# Bottom and Top
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 1, i_l, i_u, j_l, j_u + 1, k_l, k_u, self.nwz, main_grid.IDlookup['Ex'], main_grid.Ex, self.Hy, 3, -1, 1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 1, i_l, i_u + 1, j_l, j_u, k_l, k_u, self.nwz, main_grid.IDlookup['Ey'], main_grid.Ey, self.Hx, 3, 1, -1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
def update_magnetic_os(self, main_grid):
i_l = self.i0 - self.is_os_sep
i_u = self.i1 + self.is_os_sep
j_l = self.j0 - self.is_os_sep
j_u = self.j1 + self.is_os_sep
k_l = self.k0 - self.is_os_sep
k_u = self.k1 + self.is_os_sep
# Form of FDTD update equations for H
# Hz = c0Hz - c1Ey + c2Ex
# Hy = c0Hy - c3Ex + c1Ez
# Hx = c0Hx - c2Ez + c3Ey
# Args: sub_grid, normal, l_l, l_u, m_l, m_u, n_l, n_u, nwn, lookup_id, field, inc_field, co, sign_n, sign_f):
# Front and back
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 3, i_l, i_u, k_l, k_u + 1, j_l - 1, j_u, self.nwy, main_grid.IDlookup['Hz'], main_grid.Hz, self.Ex, 2, 1, -1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 3, i_l, i_u + 1, k_l, k_u, j_l - 1, j_u, self.nwy, main_grid.IDlookup['Hx'], main_grid.Hx, self.Ez, 2, -1, 1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
# Left and Right
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 2, j_l, j_u, k_l, k_u + 1, i_l - 1, i_u, self.nwx, main_grid.IDlookup['Hz'], main_grid.Hz, self.Ey, 1, -1, 1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 2, j_l, j_u + 1, k_l, k_u, i_l - 1, i_u, self.nwx, main_grid.IDlookup['Hy'], main_grid.Hy, self.Ez, 1, 1, -1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
# bottom and top
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 1, i_l, i_u, j_l, j_u + 1, k_l - 1, k_u, self.nwz, main_grid.IDlookup['Hy'], main_grid.Hy, self.Ex, 3, -1, 1, 1, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
cython_update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 1, i_l, i_u + 1, j_l, j_u, k_l - 1, k_u, self.nwz, main_grid.IDlookup['Hx'], main_grid.Hx, self.Ey, 3, 1, -1, 0, self.ratio, self.is_os_sep, self.n_boundary_cells, main_grid.nthreads)
def __str__(self):
self.memory_estimate_basic()
s = '\n'
s += Fore.CYAN
s += 'Sub Grid HSG\n'
s += 'Name: {}\n'.format(self.name)
s += 'dx, dy, dz: {}m {}m {}m\n'.format(self.dx, self.dy, self.dz)
s += 'dt: {}s\n'.format(self.dt)
s += 'Memory Estimate: {}\n'.format(human_size(self.memoryusage))
s += 'Position: ({}m, {}m, {}m), ({}m, {}m, {}m)\n'.format(self.x1,
self.y1,
self.z1,
self.x2,
self.y2,
self.z2)
s += 'Main Grid Indices: lower left({}, {}, {}), upper right({}, {}, {})\n'.format(self.i0, self.j0, self.k0, self.i1, self.j1, self.k1)
s += 'Total Cells: {} {} {}\n'.format(self.nx, self.ny, self.nz)
s += 'Working Region Cells: {} {} {}\n'.format(self.nwx,
self.nwy,
self.nwz)
for h in self.hertziandipoles:
s += 'Hertizian dipole: {} {} {}\n'.format(h.xcoord,
h.ycoord,
h.zcoord)
s += str([x for x in self.waveforms
if x.ID == h.waveformID][0]) + '\n'
for r in self.rxs:
s += 'Receiver: {} {} {}\n'.format(r.xcoord, r.ycoord, r.zcoord)
for tl in self.transmissionlines:
s += 'Transmission Line: {} {} {}\n'.format(tl.xcoord, tl.ycoord, tl.zcoord)
s += str([x for x in self.waveforms
if x.ID == tl.waveformID][0]) + '\n'
s += Style.RESET_ALL
return s

查看文件

@@ -1,241 +1,167 @@
from ..cython.fields_updates_normal import update_electric
from ..cython.fields_updates_normal import update_magnetic
from ..fields_outputs import store_outputs
from ..utilities import get_terminal_width
from ..exceptions import GeneralError
from .subgrid_hsg import SubGridHSG
from .precursor_nodes import PrecursorNodes as PrecursorNodesHSG
from .precursor_nodes_filtered import PrecursorNodes as PrecursorNodesFilteredHSG
from tqdm import tqdm
from time import perf_counter
import os
import sys
from ..updates import CPUUpdates
def create_updates(G):
"""Return the solver for the given subgrids."""
updaters = []
for sg in G.subgrids:
print(sg)
sg_type = type(sg)
if sg_type == SubGridHSG and sg.filter:
precursors = PrecursorNodesFilteredHSG(G, sg)
elif sg_type == SubGridHSG and not sg.filter:
precursors = PrecursorNodesHSG(G, sg)
else:
raise GeneralError(str(sg) + ' is not a subgrid type')
sgu = SubgridUpdater(sg, precursors, G)
updaters.append(sgu)
updates = SubgridUpdates(G, updaters)
return updates
class SubgridUpdates(CPUUpdates):
def __init__(self, G, updaters):
super().__init__(G)
self.updaters = updaters
def hsg_1(self):
"""Method to update the subgrids over the first phase."""
for sg_updater in self.updaters:
sg_updater.hsg_1()
def hsg_2(self):
"""Method to update the subgrids over the second phase."""
for sg_updater in self.updaters:
sg_updater.hsg_2()
class SubGridSolver:
"""Solver for subgridding simulations."""
"""Class to call the various update methods required for an HSG-Subgrid simulation.
Multiple subgrids can be updated by adding more subgrid_updater objects to the subgrid_updater
array.
"""
def __init__(self, G, updates, hsg=True):
"""
Args:
G (G): Grid class instance - holds essential parameters
describing the model.
updates: (list): list of subgrid_updaters used for updating
the subgrids
hsg (bool): HSG methods for subgrids will not be called if False.
"""
self.G = G
self.updates = updates
self.hsg = hsg
def store_snapshots(self):
"""Store any snapshots."""
for snap in self.G.snapshots:
if snap.time == self.G.iteration + 1:
snap.store(self.G)
def solve(self, iterations):
"""Run timestepping."""
tsolvestart = perf_counter()
self.iterations = iterations
# for time step in range(self.G.iterations):
# The main grid FDTD loop
for iteration in self.iterations:
self.updates.grid.iteration = iteration
self.updates.store_outputs()
#self.updates.store_snapshots(iteration)
self.updates.update_magnetic()
self.updates.update_magnetic_pml()
self.updates.update_magnetic_sources(iteration)
self.updates.hsg_2()
self.updates.update_electric_a()
self.updates.update_electric_pml()
self.updates.update_electric_sources(iteration)
self.updates.hsg_1()
self.updates.update_electric_b()
# Keep track of the index. Required for saving output correctly
self.G.iteration = iteration
# Return the elapsed time
tsolve = perf_counter() - tsolvestart
return tsolve
def write_snapshots(self, iteration):
# Write any snapshots to file
for i, snap in enumerate(self.G.snapshots):
if snap.time == iteration + 1:
snapiters = 36 * (((snap.xf - snap.xs) / snap.dx) * ((snap.yf - snap.ys) / snap.dy) * ((snap.zf - snap.zs) / snap.dz))
pbar = tqdm(total=snapiters, leave=False, unit='byte', unit_scale=True, desc=' Writing snapshot file {} of {}, {}'.format(i + 1, len(self.G.snapshots), os.path.split(snap.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=self.G.tqdmdisable)
# Use this call to print out main grid and subgrids
snap.write_vtk_imagedata(self.G.Ex, self.G.Ey, self.G.Ez, self.G.Hx, self.G.Hy, self.G.Hz, self.G, pbar, sub_grids=self.G.subgrids)
# Use this call to print out the standard grid without subgrid
# snap.write_vtk_imagedata(self.G.Ex, self.G.Ey, self.G.Ez, self.G.Hx, self.G.Hy, self.G.Hz, self.G, pbar)
# Use this call to print out only the subgrid - use in combination with commented code in .multi_cmds/snapshots.py
# snap.write_vtk_imagedata_fast(self.grid)
pbar.close()
class SubgridUpdater(CPUUpdates):
"""Class to handle updating the electric and magnetic fields of an HSG
subgrid. The IS, OS, subgrid region and the electric/magnetic sources are updated
using the precursor regions.
"""
def __init__(self, subgrid, precursors, G):
"""
Args:
subgrid (SubGrid3d): Subgrid to be updated
precursors (PrecursorNodes): Precursor nodes associated with
the subgrid
G (class): Grid class instance - holds essential parameters
describing the model.
"""
super().__init__(subgrid)
self.precursors = precursors
self.G = G
self.source_iteration = 0
def hsg_1(self):
"""This is the first half of the subgrid update. Takes the time step
up to the main grid magnetic update"""
G = self.G
sub_grid = self.grid
precursors = self.precursors
precursors.update_electric()
upper_m = int(sub_grid.ratio / 2 - 0.5)
for m in range(1, upper_m + 1):
# STD update, interpolate inc. field in time, apply correction
self.store_outputs()
self.update_electric_a()
self.update_electric_pml()
precursors.interpolate_magnetic_in_time(int(m + sub_grid.ratio / 2 - 0.5))
sub_grid.update_electric_is(precursors)
self.update_electric_b()
self.update_sub_grid_electric_sources()
# STD update, interpolate inc. field in time, apply correction
self.update_magnetic()
self.update_magnetic_pml()
precursors.interpolate_electric_in_time(m)
sub_grid.update_magnetic_is(precursors)
self.update_sub_grid_magnetic_sources()
self.store_outputs()
self.update_electric_a()
self.update_electric_pml()
precursors.calc_exact_magnetic_in_time()
sub_grid.update_electric_is(precursors)
self.update_electric_b()
self.update_sub_grid_electric_sources()
sub_grid.update_electric_os(G)
def hsg_2(self):
"""This is the first half of the subgrid update. Takes the time step
up to the main grid electric update"""
G = self.G
sub_grid = self.grid
precursors = self.precursors
precursors.update_magnetic()
upper_m = int(sub_grid.ratio / 2 - 0.5)
for m in range(1, upper_m + 1):
self.update_magnetic()
self.update_magnetic_pml()
precursors.interpolate_electric_in_time(int(m + sub_grid.ratio / 2 - 0.5))
sub_grid.update_magnetic_is(precursors)
self.update_sub_grid_magnetic_sources()
self.store_outputs()
self.update_electric_a()
self.update_electric_pml()
precursors.interpolate_magnetic_in_time(m)
sub_grid.update_electric_is(precursors)
self.update_electric_b()
self.update_sub_grid_electric_sources()
self.update_magnetic()
self.update_magnetic_pml()
precursors.calc_exact_electric_in_time()
sub_grid.update_magnetic_is(precursors)
self.update_sub_grid_magnetic_sources()
sub_grid.update_magnetic_os(G)
def update_sub_grid_electric_sources(self):
"""Update any electric sources in the subgrid"""
sg = self.grid
for source in sg.voltagesources + sg.transmissionlines + sg.hertziandipoles:
source.update_electric(self.source_iteration, sg.updatecoeffsE, sg.ID,
sg.Ex, sg.Ey, sg.Ez, sg)
self.source_iteration += 1
self.grid.iteration = self.source_iteration
def update_sub_grid_magnetic_sources(self):
"""Update any magnetic sources in the subgrid"""
sg = self.grid
for source in sg.transmissionlines + sg.magneticdipoles:
source.update_magnetic(self.source_iteration, sg.updatecoeffsH, sg.ID,
sg.Hx, sg.Hy, sg.Hz, sg)
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from ..cython.fields_updates_normal import update_electric
from ..cython.fields_updates_normal import update_magnetic
from ..fields_outputs import store_outputs
from ..utilities import get_terminal_width
from ..exceptions import GeneralError
from .subgrid_hsg import SubGridHSG
from .precursor_nodes import PrecursorNodes as PrecursorNodesHSG
from .precursor_nodes_filtered import PrecursorNodes as PrecursorNodesFilteredHSG
from tqdm import tqdm
from time import perf_counter
import os
import sys
from ..updates import CPUUpdates
def create_updates(G):
"""Return the solver for the given subgrids."""
updaters = []
for sg in G.subgrids:
print(sg)
sg_type = type(sg)
if sg_type == SubGridHSG and sg.filter:
precursors = PrecursorNodesFilteredHSG(G, sg)
elif sg_type == SubGridHSG and not sg.filter:
precursors = PrecursorNodesHSG(G, sg)
else:
raise GeneralError(str(sg) + ' is not a subgrid type')
sgu = SubgridUpdater(sg, precursors, G)
updaters.append(sgu)
updates = SubgridUpdates(G, updaters)
return updates
class SubgridUpdates(CPUUpdates):
def __init__(self, G, updaters):
super().__init__(G)
self.updaters = updaters
def hsg_1(self):
"""Method to update the subgrids over the first phase."""
for sg_updater in self.updaters:
sg_updater.hsg_1()
def hsg_2(self):
"""Method to update the subgrids over the second phase."""
for sg_updater in self.updaters:
sg_updater.hsg_2()
class SubgridUpdater(CPUUpdates):
"""Class to handle updating the electric and magnetic fields of an HSG
subgrid. The IS, OS, subgrid region and the electric/magnetic sources are updated
using the precursor regions.
"""
def __init__(self, subgrid, precursors, G):
"""
Args:
subgrid (SubGrid3d): Subgrid to be updated
precursors (PrecursorNodes): Precursor nodes associated with
the subgrid - contain interpolated fields
G (class): Grid class instance - holds essential parameters
describing the model.
"""
super().__init__(subgrid)
self.precursors = precursors
self.G = G
self.source_iteration = 0
def hsg_1(self):
"""This is the first half of the subgrid update. Takes the time step
up to the main grid magnetic update"""
G = self.G
sub_grid = self.grid
precursors = self.precursors
precursors.update_electric()
upper_m = int(sub_grid.ratio / 2 - 0.5)
for m in range(1, upper_m + 1):
# STD update, interpolate inc. field in time, apply correction
self.store_outputs()
self.update_electric_a()
self.update_electric_pml()
precursors.interpolate_magnetic_in_time(int(m + sub_grid.ratio / 2 - 0.5))
sub_grid.update_electric_is(precursors)
self.update_electric_b()
self.update_electric_sources()
# STD update, interpolate inc. field in time, apply correction
self.update_magnetic()
self.update_magnetic_pml()
precursors.interpolate_electric_in_time(m)
sub_grid.update_magnetic_is(precursors)
self.update_magnetic_sources()
self.store_outputs()
self.update_electric_a()
self.update_electric_pml()
precursors.calc_exact_magnetic_in_time()
sub_grid.update_electric_is(precursors)
self.update_electric_b()
self.update_electric_sources()
sub_grid.update_electric_os(G)
def hsg_2(self):
"""This is the first half of the subgrid update. Takes the time step
up to the main grid electric update"""
G = self.G
sub_grid = self.grid
precursors = self.precursors
precursors.update_magnetic()
upper_m = int(sub_grid.ratio / 2 - 0.5)
for m in range(1, upper_m + 1):
self.update_magnetic()
self.update_magnetic_pml()
precursors.interpolate_electric_in_time(int(m + sub_grid.ratio / 2 - 0.5))
sub_grid.update_magnetic_is(precursors)
self.update_magnetic_sources()
self.store_outputs()
self.update_electric_a()
self.update_electric_pml()
precursors.interpolate_magnetic_in_time(m)
sub_grid.update_electric_is(precursors)
self.update_electric_b()
self.update_electric_sources()
self.update_magnetic()
self.update_magnetic_pml()
precursors.calc_exact_electric_in_time()
sub_grid.update_magnetic_is(precursors)
self.update_magnetic_sources()
sub_grid.update_magnetic_os(G)

查看文件

@@ -1,205 +1,195 @@
from .subgrid_hsg import SubGridHSG as SubGridHSGUser
from .multi import ReferenceRx as ReferenceRxUser
from ..exceptions import CmdInputError
from ..cmds_multiple import UserObjectMulti
from ..cmds_geometry.cmds_geometry import UserObjectGeometry
from ..cmds_multiple import Rx
from gprMax import config
from copy import copy
import numpy as np
class SubGridBase(UserObjectMulti):
"""Class to allow UserObjectMulti and UserObjectGeometry to be nested in SubGrid type user objects."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.children_multiple = []
self.children_geometry = []
def add(self, node):
"""Function to add other user objects. Geometry and multi only."""
if isinstance(node, UserObjectMulti):
self.children_multiple.append(node)
elif isinstance(node, UserObjectGeometry):
self.children_geometry.append(node)
else:
raise Exception(str(node) + ' This Object can not be added to a sub grid')
def check_filters(self, grid):
"""Check the filter of other grids - Only allow filters all on or filters all off."""
if grid.subgrids:
f = grid.subgrids[0]
if f != self.kwargs['filter']:
raise CmdInputError(self.__str__() + "Filters should be on or off. Set Filter on or off for all subgrids")
def set_discretisation(self, sg, grid):
"""Set the spatial discretisation."""
sg.dx = grid.dx / sg.ratio
sg.dy = grid.dy / sg.ratio
sg.dz = grid.dz / sg.ratio
sg.dl = np.array([sg.dx, sg.dy, sg.dz])
def set_main_grid_indices(self, sg, grid, uip, p1, p2):
"""Set subgrid indices related to main grid placement."""
# Main grid indices of the sub grid. These are dummy indices. They are
# not user internal except for printing to the user
sg.i0_u, sg.j0_u, sg.k0_u = p1
sg.i1_u, sg.j1_u, sg.k1_u = p2
# The actual sub gridded area (IS index) is 4 cells in
sg.i0, sg.j0, sg.k0 = np.add([sg.i0_u, sg.j0_u, sg.k0_u], sg.is_os_sep)
sg.i1, sg.j1, sg.k1 = np.subtract([sg.i1_u, sg.j1_u, sg.k1_u], sg.is_os_sep)
# Main grid indices of the sub grid. These are dummy indices. They are
# not user internal except for printing to the user
sg.x1_u, sg.y1_u, sg.z1_u = uip.round_to_grid(p1)
sg.x2_u, sg.y2_u, sg.z2_u = uip.round_to_grid(p2)
sg.x1, sg.y1, sg.z1 = np.add([sg.x1_u, sg.y1_u, sg.z1_u], sg.is_os_sep * sg.dx)
sg.x2, sg.y2, sg.z2 = np.subtract([sg.x2_u, sg.y2_u, sg.z2_u], sg.is_os_sep * sg.dx)
def set_name(self, sg):
sg.name = self.kwargs['id']
def set_working_region_cells(self, sg):
"""Number of cells in each dimension for the working region."""
sg.nwx = (sg.i1 - sg.i0) * sg.ratio
sg.nwy = (sg.j1 - sg.j0) * sg.ratio
sg.nwz = (sg.k1 - sg.k0) * sg.ratio
def set_total_cells(self, sg):
"""Number of cells in each dimension for the whole region."""
sg.nx = 2 * sg.n_boundary_cells_x + sg.nwx
sg.ny = 2 * sg.n_boundary_cells_y + sg.nwy
sg.nz = 2 * sg.n_boundary_cells_z + sg.nwz
def set_iterations(self, sg, main):
"""Set number of iterations that will take place in the subgrid."""
sg.iterations = main.iterations * sg.ratio
def setup(self, sg, grid, uip):
""""Common setup to both all subgrid types."""
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
self.check_filters(grid)
self.set_discretisation(sg, grid)
# Set the temporal discretisation
sg.calculate_dt()
# ensure stability
sg.round_time_step()
# set the indices related to the subgrids main grid placement
self.set_main_grid_indices(sg, grid, uip, p1, p2)
"""
try:
uip.check_box_points([sg.i0, sg.j0, sg.k0],
[sg.i1, sg.j1, sg.k1], cmd_str)
except CmdInputError:
es_f = 'The subgrid should extend at least {} cells'
es = es_f.format(sg.is_os_sep * 2)
raise CmdInputError(cmd_str, es)
"""
self.set_working_region_cells(sg)
self.set_total_cells(sg)
self.set_iterations(sg, grid)
self.set_name(sg)
# Copy a reference for the main grid to the sub grid
sg.parent_grid = grid
sg.timewindow = grid.timewindow
# Copy a subgrid reference to self so that children.create(grid, uip) can access
# the correct grid
self.subgrid = sg
# Copy over built in materials
sg.materials = [copy(m) for m in grid.materials if m.numID in range(0, grid.n_built_in_materials + 1)]
# use same number of threads
sg.nthreads = grid.nthreads
# Dont mix and match different subgrids
for sg_made in grid.subgrids:
if type(sg) != type(sg_made):
raise CmdInputError(self.__str__() + ' Please only use one type of subgrid')
# Reference the sub grid under the main grid to which it belongs.
grid.subgrids.append(sg)
class SubGridHSG(SubGridBase):
"""HSG User Object."""
def __init__(self,
p1=None,
p2=None,
ratio=3,
ID='',
is_os_sep=3,
pml_separation=4,
subgrid_pml_thickness=6,
interpolation='linear',
loss_mechanism=False,
loss_factor=False,
filter=True,
**kwargs):
"""Constructor."""
pml_separation = ratio // 2 + 2
# copy over the optional parameters
kwargs['p1'] = p1
kwargs['p2'] = p2
kwargs['ratio'] = ratio
kwargs['ID'] = ID
kwargs['is_os_sep'] = is_os_sep
kwargs['pml_separation'] = pml_separation
kwargs['subgrid_pml_thickness'] = subgrid_pml_thickness
kwargs['interpolation'] = interpolation
kwargs['filter'] = filter
kwargs['loss_mechanism'] = loss_mechanism
kwargs['loss_factor'] = loss_factor
super().__init__(**kwargs)
self.order = 18
self.hash = '#subgrid_hsg'
def create(self, grid, uip):
sg = SubGridHSGUser(**self.kwargs)
self.setup(sg, grid, uip)
if config.is_messages():
print(sg)
return sg
class ReferenceRx(Rx):
"""ReferenceRx User Object."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#rx_reference'
self.constructor = ReferenceRxUser
def create(self, grid, uip):
r = super().create(grid, uip)
try:
ratio = self.kwargs['ratio']
r.ratio = ratio
r.offset = ratio // 2
except KeyError:
raise CmdInputError("'{}' has an no ratio parameter".format(self.__str__()))
from .subgrid_hsg import SubGridHSG as SubGridHSGUser
from .multi import ReferenceRx as ReferenceRxUser
from ..exceptions import CmdInputError
from ..cmds_multiple import UserObjectMulti
from ..cmds_geometry.cmds_geometry import UserObjectGeometry
from ..cmds_multiple import Rx
from gprMax import config
from copy import copy
import numpy as np
class SubGridBase(UserObjectMulti):
"""Class to allow UserObjectMulti and UserObjectGeometry to be nested in SubGrid type user objects."""
def __init__(self, **kwargs):
"""Constructor."""
super().__init__(**kwargs)
self.children_multiple = []
self.children_geometry = []
def add(self, node):
"""Function to add other user objects. Geometry and multi only."""
if isinstance(node, UserObjectMulti):
self.children_multiple.append(node)
elif isinstance(node, UserObjectGeometry):
self.children_geometry.append(node)
else:
raise Exception(str(node) + ' This Object can not be added to a sub grid')
def check_filters(self, grid):
"""Check the filter of other grids - Only allow filters all on or filters all off."""
if grid.subgrids:
f = grid.subgrids[0]
if f != self.kwargs['filter']:
raise CmdInputError(self.__str__() + "Filters should be on or off. Set Filter on or off for all subgrids")
def set_discretisation(self, sg, grid):
"""Set the spatial discretisation."""
sg.dx = grid.dx / sg.ratio
sg.dy = grid.dy / sg.ratio
sg.dz = grid.dz / sg.ratio
sg.dl = np.array([sg.dx, sg.dy, sg.dz])
def set_main_grid_indices(self, sg, grid, uip, p1, p2):
"""Set subgrid indices related to main grid placement."""
# location of the IS
sg.i0, sg.j0, sg.k0 = p1
sg.i1, sg.j1, sg.k1 = p2
sg.x1, sg.y1, sg.z1 = uip.round_to_grid(p1)
sg.x2, sg.y2, sg.z2 = uip.round_to_grid(p2)
def set_name(self, sg):
sg.name = self.kwargs['id']
def set_working_region_cells(self, sg):
"""Number of cells in each dimension for the working region."""
sg.nwx = (sg.i1 - sg.i0) * sg.ratio
sg.nwy = (sg.j1 - sg.j0) * sg.ratio
sg.nwz = (sg.k1 - sg.k0) * sg.ratio
def set_total_cells(self, sg):
"""Number of cells in each dimension for the whole region."""
sg.nx = 2 * sg.n_boundary_cells_x + sg.nwx
sg.ny = 2 * sg.n_boundary_cells_y + sg.nwy
sg.nz = 2 * sg.n_boundary_cells_z + sg.nwz
def set_iterations(self, sg, main):
"""Set number of iterations that will take place in the subgrid."""
sg.iterations = main.iterations * sg.ratio
def setup(self, sg, grid, uip):
""""Common setup to both all subgrid types."""
p1 = self.kwargs['p1']
p2 = self.kwargs['p2']
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
self.check_filters(grid)
self.set_discretisation(sg, grid)
# Set the temporal discretisation
sg.calculate_dt()
# ensure stability
sg.round_time_step()
# set the indices related to the subgrids main grid placement
self.set_main_grid_indices(sg, grid, uip, p1, p2)
"""
try:
uip.check_box_points([sg.i0, sg.j0, sg.k0],
[sg.i1, sg.j1, sg.k1], cmd_str)
except CmdInputError:
es_f = 'The subgrid should extend at least {} cells'
es = es_f.format(sg.is_os_sep * 2)
raise CmdInputError(cmd_str, es)
"""
self.set_working_region_cells(sg)
self.set_total_cells(sg)
self.set_iterations(sg, grid)
self.set_name(sg)
# Copy a reference for the main grid to the sub grid
sg.parent_grid = grid
sg.timewindow = grid.timewindow
# Copy a subgrid reference to self so that children.create(grid, uip) can access
# the correct grid
self.subgrid = sg
# Copy over built in materials
sg.materials = [copy(m) for m in grid.materials if m.numID in range(0, grid.n_built_in_materials + 1)]
# use same number of threads
sg.nthreads = grid.nthreads
# Dont mix and match different subgrids
for sg_made in grid.subgrids:
if type(sg) != type(sg_made):
raise CmdInputError(self.__str__() + ' Please only use one type of subgrid')
# Reference the sub grid under the main grid to which it belongs.
grid.subgrids.append(sg)
class SubGridHSG(SubGridBase):
"""HSG User Object."""
def __init__(self,
p1=None,
p2=None,
ratio=3,
ID='',
is_os_sep=3,
pml_separation=4,
subgrid_pml_thickness=6,
interpolation='linear',
loss_mechanism=False,
loss_factor=False,
filter=True,
**kwargs):
"""Constructor."""
pml_separation = ratio // 2 + 2
# copy over the optional parameters
kwargs['p1'] = p1
kwargs['p2'] = p2
kwargs['ratio'] = ratio
kwargs['ID'] = ID
kwargs['is_os_sep'] = is_os_sep
kwargs['pml_separation'] = pml_separation
kwargs['subgrid_pml_thickness'] = subgrid_pml_thickness
kwargs['interpolation'] = interpolation
kwargs['filter'] = filter
kwargs['loss_mechanism'] = loss_mechanism
kwargs['loss_factor'] = loss_factor
super().__init__(**kwargs)
self.order = 18
self.hash = '#subgrid_hsg'
def create(self, grid, uip):
sg = SubGridHSGUser(**self.kwargs)
self.setup(sg, grid, uip)
if config.is_messages():
print(sg)
return sg
class ReferenceRx(Rx):
"""ReferenceRx User Object."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hash = '#rx_reference'
self.constructor = ReferenceRxUser
def create(self, grid, uip):
r = super().create(grid, uip)
try:
ratio = self.kwargs['ratio']
r.ratio = ratio
r.offset = ratio // 2
except KeyError:
raise CmdInputError("'{}' has an no ratio parameter".format(self.__str__()))

查看文件

@@ -1,314 +1,314 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
from cython.parallel import prange
cdef extern from "complex.h" nogil:
double creal(double complex z)
float crealf(float complex z)
###############################################################
# Electric field updates - dispersive materials - multipole A #
###############################################################
{% for item in functions %}
cpdef void {{ item.name_a }}(
int nx,
int ny,
int nz,
int nthreads,
int maxpoles,
{{ item.field_type }}[:, ::1] updatecoeffsE,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez,
{{ item.field_type }}[:, :, ::1] Hx,
{{ item.field_type }}[:, :, ::1] Hy,
{{ item.field_type }}[:, :, ::1] Hz
):
"""This function updates the electric field components when dispersive materials (with multiple poles) are present.
Args:
nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef Py_ssize_t i, j, k, pole
cdef int material
cdef float phi = 0
# Ex component
if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
material = ID[0, i, j, k]
phi = 0
for pole in range(maxpoles):
{% if 'complex' in item.dispersive_type %}
phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tx[pole, i, j, k])
{% else %}
phi = phi + updatecoeffsdispersive[material, pole * 3] * Tx[pole, i, j, k]
{% endif %}
Tx[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tx[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k]
Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi
# Ey component
if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
material = ID[1, i, j, k]
phi = 0
for pole in range(maxpoles):
{% if 'complex' in item.dispersive_type %}
phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Ty[pole, i, j, k])
{% else %}
phi = phi + updatecoeffsdispersive[material, pole * 3] * Ty[pole, i, j, k]
{% endif %}
Ty[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Ty[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k]
Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi
# Ez component
if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
material = ID[2, i, j, k]
phi = 0
for pole in range(maxpoles):
{% if 'complex' in item.dispersive_type %}
phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tz[pole, i, j, k])
{% else %}
phi = phi + updatecoeffsdispersive[material, pole * 3] * Tz[pole, i, j, k]
{% endif %}
Tz[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tz[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k]
Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi
{% endfor %}
###############################################################
# Electric field updates - dispersive materials - multipole B #
###############################################################
{% for item in functions %}
cpdef void {{ item.name_b }}(
int nx,
int ny,
int nz,
int nthreads,
int maxpoles,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez
):
"""This function updates a temporary dispersive material array when disperisive materials (with multiple poles) are present.
Args:
nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef Py_ssize_t i, j, k, pole
cdef int material
# Ex component
if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
material = ID[0, i, j, k]
for pole in range(maxpoles):
Tx[pole, i, j, k] = Tx[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k]
# Ey component
if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
material = ID[1, i, j, k]
for pole in range(maxpoles):
Ty[pole, i, j, k] = Ty[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k]
# Ez component
if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
material = ID[2, i, j, k]
for pole in range(maxpoles):
Tz[pole, i, j, k] = Tz[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k]
{% endfor %}
#################################################################
# Electric field updates - dispersive materials - single pole A #
#################################################################
{% for item in functions %}
cpdef void {{ item.name_a_1 }}(
int nx,
int ny,
int nz,
int nthreads,
int maxpoles,
{{ item.field_type }}[:, ::1] updatecoeffsE,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez,
{{ item.field_type }}[:, :, ::1] Hx,
{{ item.field_type }}[:, :, ::1] Hy,
{{ item.field_type }}[:, :, ::1] Hz
):
"""This function updates the electric field components when dispersive materials (with 1 pole) are present.
Args:
nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef Py_ssize_t i, j, k
cdef int material
cdef float phi = 0
# Ex component
if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
material = ID[0, i, j, k]
{% if 'complex' in item.dispersive_type %}
phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tx[0, i, j, k])
{% else %}
phi = updatecoeffsdispersive[material, 0] * Tx[0, i, j, k]
{% endif %}
Tx[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tx[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ex[i, j, k]
Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi
# Ey component
if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
material = ID[1, i, j, k]
{% if 'complex' in item.dispersive_type %}
phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Ty[0, i, j, k])
{% else %}
phi = updatecoeffsdispersive[material, 0] * Ty[0, i, j, k]
{% endif %}
Ty[0, i, j, k] = updatecoeffsdispersive[material, 1] * Ty[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ey[i, j, k]
Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi
# Ez component
if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
material = ID[2, i, j, k]
{% if 'complex' in item.dispersive_type %}
phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tz[0, i, j, k])
{% else %}
phi = updatecoeffsdispersive[material, 0] * Tz[0, i, j, k]
{% endif %}
Tz[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tz[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ez[i, j, k]
Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi
{% endfor %}
#################################################################
# Electric field updates - dispersive materials - single pole B #
#################################################################
{% for item in functions %}
cpdef void {{ item.name_b_1 }}(
int nx,
int ny,
int nz,
int nthreads,
int maxpoles,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez
):
"""This function updates a temporary dispersive material array when disperisive materials (with 1 pole) are present.
Args:
nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef Py_ssize_t i, j, k
cdef int material
# Ex component
if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
material = ID[0, i, j, k]
Tx[0, i, j, k] = Tx[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ex[i, j, k]
# Ey component
if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
material = ID[1, i, j, k]
Ty[0, i, j, k] = Ty[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ey[i, j, k]
# Ez component
if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
material = ID[2, i, j, k]
Tz[0, i, j, k] = Tz[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ez[i, j, k]
{% endfor %}
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
cimport numpy as np
from cython.parallel import prange
cdef extern from "complex.h" nogil:
double creal(double complex z)
float crealf(float complex z)
###############################################################
# Electric field updates - dispersive materials - multipole A #
###############################################################
{% for item in functions %}
cpdef void {{ item.name_a }}(
int nx,
int ny,
int nz,
int nthreads,
int maxpoles,
{{ item.field_type }}[:, ::1] updatecoeffsE,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez,
{{ item.field_type }}[:, :, ::1] Hx,
{{ item.field_type }}[:, :, ::1] Hy,
{{ item.field_type }}[:, :, ::1] Hz
):
"""This function updates the electric field components when dispersive materials (with multiple poles) are present.
Args:
nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef Py_ssize_t i, j, k, pole
cdef int material
cdef float phi = 0
# Ex component
if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
material = ID[0, i, j, k]
phi = 0
for pole in range(maxpoles):
{% if 'complex' in item.dispersive_type %}
phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tx[pole, i, j, k])
{% else %}
phi = phi + updatecoeffsdispersive[material, pole * 3] * Tx[pole, i, j, k]
{% endif %}
Tx[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tx[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k]
Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi
# Ey component
if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
material = ID[1, i, j, k]
phi = 0
for pole in range(maxpoles):
{% if 'complex' in item.dispersive_type %}
phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Ty[pole, i, j, k])
{% else %}
phi = phi + updatecoeffsdispersive[material, pole * 3] * Ty[pole, i, j, k]
{% endif %}
Ty[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Ty[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k]
Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi
# Ez component
if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
material = ID[2, i, j, k]
phi = 0
for pole in range(maxpoles):
{% if 'complex' in item.dispersive_type %}
phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tz[pole, i, j, k])
{% else %}
phi = phi + updatecoeffsdispersive[material, pole * 3] * Tz[pole, i, j, k]
{% endif %}
Tz[pole, i, j, k] = updatecoeffsdispersive[material, 1 + (pole * 3)] * Tz[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k]
Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi
{% endfor %}
###############################################################
# Electric field updates - dispersive materials - multipole B #
###############################################################
{% for item in functions %}
cpdef void {{ item.name_b }}(
int nx,
int ny,
int nz,
int nthreads,
int maxpoles,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez
):
"""This function updates a temporary dispersive material array when disperisive materials (with multiple poles) are present.
Args:
nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef Py_ssize_t i, j, k, pole
cdef int material
# Ex component
if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
material = ID[0, i, j, k]
for pole in range(maxpoles):
Tx[pole, i, j, k] = Tx[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k]
# Ey component
if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
material = ID[1, i, j, k]
for pole in range(maxpoles):
Ty[pole, i, j, k] = Ty[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k]
# Ez component
if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
material = ID[2, i, j, k]
for pole in range(maxpoles):
Tz[pole, i, j, k] = Tz[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k]
{% endfor %}
#################################################################
# Electric field updates - dispersive materials - single pole A #
#################################################################
{% for item in functions %}
cpdef void {{ item.name_a_1 }}(
int nx,
int ny,
int nz,
int nthreads,
int maxpoles,
{{ item.field_type }}[:, ::1] updatecoeffsE,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez,
{{ item.field_type }}[:, :, ::1] Hx,
{{ item.field_type }}[:, :, ::1] Hy,
{{ item.field_type }}[:, :, ::1] Hz
):
"""This function updates the electric field components when dispersive materials (with 1 pole) are present.
Args:
nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef Py_ssize_t i, j, k
cdef int material
cdef float phi = 0
# Ex component
if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
material = ID[0, i, j, k]
{% if 'complex' in item.dispersive_type %}
phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tx[0, i, j, k])
{% else %}
phi = updatecoeffsdispersive[material, 0] * Tx[0, i, j, k]
{% endif %}
Tx[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tx[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ex[i, j, k]
Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi
# Ey component
if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
material = ID[1, i, j, k]
{% if 'complex' in item.dispersive_type %}
phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Ty[0, i, j, k])
{% else %}
phi = updatecoeffsdispersive[material, 0] * Ty[0, i, j, k]
{% endif %}
Ty[0, i, j, k] = updatecoeffsdispersive[material, 1] * Ty[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ey[i, j, k]
Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi
# Ez component
if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
material = ID[2, i, j, k]
{% if 'complex' in item.dispersive_type %}
phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tz[0, i, j, k])
{% else %}
phi = updatecoeffsdispersive[material, 0] * Tz[0, i, j, k]
{% endif %}
Tz[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tz[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ez[i, j, k]
Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi
{% endfor %}
#################################################################
# Electric field updates - dispersive materials - single pole B #
#################################################################
{% for item in functions %}
cpdef void {{ item.name_b_1 }}(
int nx,
int ny,
int nz,
int nthreads,
int maxpoles,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez
):
"""This function updates a temporary dispersive material array when disperisive materials (with 1 pole) are present.
Args:
nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays
"""
cdef Py_ssize_t i, j, k
cdef int material
# Ex component
if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(1, nz):
material = ID[0, i, j, k]
Tx[0, i, j, k] = Tx[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ex[i, j, k]
# Ey component
if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny):
for k in range(1, nz):
material = ID[1, i, j, k]
Ty[0, i, j, k] = Ty[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ey[i, j, k]
# Ez component
if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny):
for k in range(0, nz):
material = ID[2, i, j, k]
Tz[0, i, j, k] = Tz[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ez[i, j, k]
{% endfor %}

查看文件

@@ -1,185 +1,186 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from importlib import import_module
from gprMax.fields_outputs import store_outputs
import gprMax.config as config
from gprMax.cython.fields_updates_normal import update_electric
from gprMax.cython.fields_updates_normal import update_magnetic
class CPUUpdates:
def __init__(self, G):
self.grid = G
self.dispersive_update_a = None
self.dispersive_update_b = None
def store_outputs(self):
# Store field component values for every receiver and transmission line
store_outputs(self.grid)
def store_snapshots(self, iteration):
# Store any snapshots
for snap in self.grid.snapshots:
if snap.time == iteration + 1:
snap.store(self.grid)
def update_magnetic(self):
# Update magnetic field components
update_magnetic(self.grid.nx,
self.grid.ny,
self.grid.nz,
config.hostinfo['ompthreads'],
self.grid.updatecoeffsH,
self.grid.ID,
self.grid.Ex,
self.grid.Ey,
self.grid.Ez,
self.grid.Hx,
self.grid.Hy,
self.grid.Hz)
def update_magnetic_pml(self):
# Update magnetic field components with the PML correction
for pml in self.grid.pmls:
pml.update_magnetic(self.grid)
def update_magnetic_sources(self, iteration):
# Update magnetic field components from sources
for source in self.grid.transmissionlines + self.grid.magneticdipoles:
source.update_magnetic(iteration,
self.grid.updatecoeffsH,
self.grid.ID,
self.grid.Hx,
self.grid.Hy,
self.grid.Hz,
self.grid)
def update_electric_a(self):
# Update electric field components
# All materials are non-dispersive so do standard update
if config.materials['maxpoles'] == 0:
update_electric(self.grid.nx,
self.grid.ny,
self.grid.nz,
config.hostinfo['ompthreads'],
self.grid.updatecoeffsE,
self.grid.ID,
self.grid.Ex,
self.grid.Ey,
self.grid.Ez,
self.grid.Hx,
self.grid.Hy,
self.grid.Hz)
# If there are any dispersive materials do 1st part of dispersive update
# (it is split into two parts as it requires present and updated electric field values).
else:
self.dispersive_update_a(self.grid.nx,
self.grid.ny,
self.grid.nz,
config.hostinfo['ompthreads'],
config.materials['maxpoles'],
self.grid.updatecoeffsE,
self.grid.updatecoeffsdispersive,
self.grid.ID,
self.grid.Tx,
self.grid.Ty,
self.grid.Tz,
self.grid.Ex,
self.grid.Ey,
self.grid.Ez,
self.grid.Hx,
self.grid.Hy,
self.grid.Hz)
def update_electric_pml(self):
# Update electric field components with the PML correction
for pml in self.grid.pmls:
pml.update_electric(self.grid)
def update_electric_sources(self, iteration):
# Update electric field components from sources (update any Hertzian dipole sources last)
for source in self.grid.voltagesources + self.grid.transmissionlines + self.grid.hertziandipoles:
source.update_electric(iteration, self.grid.updatecoeffsE, self.grid.ID, self.grid.Ex, self.grid.Ey, self.grid.Ez, self.grid)
def update_electric_b(self):
# 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 config.materials['maxpoles'] != 0:
self.dispersive_update_b(self.grid.nx,
self.grid.ny,
self.grid.nz,
config.hostinfo['ompthreads'],
config.materials['maxpoles'],
self.grid.updatecoeffsdispersive,
self.grid.ID,
self.grid.Tx,
self.grid.Ty,
self.grid.Tz,
self.grid.Ex,
self.grid.Ey,
self.grid.Ez)
def adapt_dispersive_config(self, config):
if config.materials['maxpoles'] > 1:
poles = 'multi'
else:
poles = '1'
if config.precision == 'single':
type = 'float'
else:
type = 'double'
if config.materials['dispersivedtype'] == config.dtypes['complex']:
dispersion = 'complex'
else:
dispersion = 'real'
class Props():
pass
props = Props()
props.poles = poles
props.precision = type
props.dispersion_type = dispersion
return props
def set_dispersive_updates(self, props):
"""Function to set dispersive update functions based on model."""
update_f = 'update_electric_dispersive_{}pole_{}_{}_{}'
disp_a = update_f.format(props.poles, 'A', props.precision, props.dispersion_type)
disp_b = update_f.format(props.poles, 'B', props.precision, props.dispersion_type)
disp_a_f = getattr(import_module('gprMax.cython.fields_updates_dispersive'), disp_a)
disp_b_f = getattr(import_module('gprMax.cython.fields_updates_dispersive'), disp_b)
self.dispersive_update_a = disp_a_f
self.dispersive_update_b = disp_b_f
class GPUUpdates:
pass
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from importlib import import_module
from gprMax.fields_outputs import store_outputs
import gprMax.config as config
from gprMax.cython.fields_updates_normal import update_electric
from gprMax.cython.fields_updates_normal import update_magnetic
class CPUUpdates:
def __init__(self, G):
self.grid = G
self.dispersive_update_a = None
self.dispersive_update_b = None
def store_outputs(self):
# Store field component values for every receiver and transmission line
store_outputs(self.grid)
def store_snapshots(self, iteration):
# Store any snapshots
for snap in self.grid.snapshots:
if snap.time == iteration + 1:
snap.store(self.grid)
def update_magnetic(self):
# Update magnetic field components
update_magnetic(self.grid.nx,
self.grid.ny,
self.grid.nz,
config.hostinfo['ompthreads'],
self.grid.updatecoeffsH,
self.grid.ID,
self.grid.Ex,
self.grid.Ey,
self.grid.Ez,
self.grid.Hx,
self.grid.Hy,
self.grid.Hz)
def update_magnetic_pml(self):
# Update magnetic field components with the PML correction
for pml in self.grid.pmls:
pml.update_magnetic(self.grid)
def update_magnetic_sources(self):
# Update magnetic field components from sources
for source in self.grid.transmissionlines + self.grid.magneticdipoles:
source.update_magnetic(self.grid.iteration,
self.grid.updatecoeffsH,
self.grid.ID,
self.grid.Hx,
self.grid.Hy,
self.grid.Hz,
self.grid)
def update_electric_a(self):
# Update electric field components
# All materials are non-dispersive so do standard update
if config.materials['maxpoles'] == 0:
update_electric(self.grid.nx,
self.grid.ny,
self.grid.nz,
config.hostinfo['ompthreads'],
self.grid.updatecoeffsE,
self.grid.ID,
self.grid.Ex,
self.grid.Ey,
self.grid.Ez,
self.grid.Hx,
self.grid.Hy,
self.grid.Hz)
# If there are any dispersive materials do 1st part of dispersive update
# (it is split into two parts as it requires present and updated electric field values).
else:
self.dispersive_update_a(self.grid.nx,
self.grid.ny,
self.grid.nz,
config.hostinfo['ompthreads'],
config.materials['maxpoles'],
self.grid.updatecoeffsE,
self.grid.updatecoeffsdispersive,
self.grid.ID,
self.grid.Tx,
self.grid.Ty,
self.grid.Tz,
self.grid.Ex,
self.grid.Ey,
self.grid.Ez,
self.grid.Hx,
self.grid.Hy,
self.grid.Hz)
def update_electric_pml(self):
# Update electric field components with the PML correction
for pml in self.grid.pmls:
pml.update_electric(self.grid)
def update_electric_sources(self):
# Update electric field components from sources (update any Hertzian dipole sources last)
for source in self.grid.voltagesources + self.grid.transmissionlines + self.grid.hertziandipoles:
source.update_electric(self.grid.iteration, self.grid.updatecoeffsE, self.grid.ID, self.grid.Ex, self.grid.Ey, self.grid.Ez, self.grid)
self.grid.iteration += 1
def update_electric_b(self):
# 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 config.materials['maxpoles'] != 0:
self.dispersive_update_b(self.grid.nx,
self.grid.ny,
self.grid.nz,
config.hostinfo['ompthreads'],
config.materials['maxpoles'],
self.grid.updatecoeffsdispersive,
self.grid.ID,
self.grid.Tx,
self.grid.Ty,
self.grid.Tz,
self.grid.Ex,
self.grid.Ey,
self.grid.Ez)
def adapt_dispersive_config(self, config):
if config.materials['maxpoles'] > 1:
poles = 'multi'
else:
poles = '1'
if config.precision == 'single':
type = 'float'
else:
type = 'double'
if config.materials['dispersivedtype'] == config.dtypes['complex']:
dispersion = 'complex'
else:
dispersion = 'real'
class Props():
pass
props = Props()
props.poles = poles
props.precision = type
props.dispersion_type = dispersion
return props
def set_dispersive_updates(self, props):
"""Function to set dispersive update functions based on model."""
update_f = 'update_electric_dispersive_{}pole_{}_{}_{}'
disp_a = update_f.format(props.poles, 'A', props.precision, props.dispersion_type)
disp_b = update_f.format(props.poles, 'B', props.precision, props.dispersion_type)
disp_a_f = getattr(import_module('gprMax.cython.fields_updates_dispersive'), disp_a)
disp_b_f = getattr(import_module('gprMax.cython.fields_updates_dispersive'), disp_b)
self.dispersive_update_a = disp_a_f
self.dispersive_update_b = disp_b_f
class GPUUpdates:
pass

查看文件

@@ -1,430 +1,430 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from contextlib import contextmanager
import codecs
import decimal as d
import os
import platform
import psutil
import re
import subprocess
from shutil import get_terminal_size
import sys
import textwrap
from time import perf_counter
from colorama import init
from colorama import Fore
from colorama import Style
init()
import numpy as np
from .exceptions import GeneralError
def get_terminal_width():
"""Get/set width of terminal being used.
Returns:
terminalwidth (int): Terminal width
"""
terminalwidth = get_terminal_size()[0]
if terminalwidth == 0:
terminalwidth = 100
return terminalwidth
def logo(version):
"""Print gprMax logo, version, and licencing/copyright information.
Args:
version (str): Version number.
"""
description = '\n=== Electromagnetic modelling software based on the Finite-Difference Time-Domain (FDTD) method'
copyright = 'Copyright (C) 2015-2019: The University of Edinburgh'
authors = 'Authors: Craig Warren and Antonis Giannopoulos'
licenseinfo1 = '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.\n'
licenseinfo2 = '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.'
licenseinfo3 = 'You should have received a copy of the GNU General Public License along with gprMax. If not, see www.gnu.org/licenses.'
logo = """ www.gprmax.com __ __
__ _ _ __ _ __| \/ | __ ___ __
/ _` | '_ \| '__| |\/| |/ _` \ \/ /
| (_| | |_) | | | | | | (_| |> <
\__, | .__/|_| |_| |_|\__,_/_/\_\\
|___/|_|
v""" + version
print('{} {}\n'.format(description, '=' * (get_terminal_width() - len(description) - 1)))
print(Fore.CYAN + '{}\n'.format(logo))
print(Style.RESET_ALL + textwrap.fill(copyright, width=get_terminal_width() - 1, initial_indent=' '))
print(textwrap.fill(authors, width=get_terminal_width() - 1, initial_indent=' '))
print()
print(textwrap.fill(licenseinfo1, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' '))
print(textwrap.fill(licenseinfo2, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' '))
print(textwrap.fill(licenseinfo3, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' '))
@contextmanager
def open_path_file(path_or_file):
"""Accepts either a path as a string or a file object and returns a file
object (http://stackoverflow.com/a/6783680).
Args:
path_or_file: path as a string or a file object.
Returns:
f (object): File object.
"""
if isinstance(path_or_file, str):
f = file_to_close = codecs.open(path_or_file, 'r', encoding='utf-8')
else:
f = path_or_file
file_to_close = None
try:
yield f
finally:
if file_to_close:
file_to_close.close()
def round_value(value, decimalplaces=0):
"""Rounding function.
Args:
value (float): Number to round.
decimalplaces (int): Number of decimal places of float to represent rounded value.
Returns:
rounded (int/float): Rounded value.
"""
# Rounds to nearest integer (half values are rounded downwards)
if decimalplaces == 0:
rounded = int(d.Decimal(value).quantize(d.Decimal('1'), rounding=d.ROUND_HALF_DOWN))
# Rounds down to nearest float represented by number of decimal places
else:
precision = '1.{places}'.format(places='0' * decimalplaces)
rounded = float(d.Decimal(value).quantize(d.Decimal(precision), rounding=d.ROUND_FLOOR))
return rounded
def round32(value):
"""Rounds up to nearest multiple of 32."""
return int(32 * np.ceil(float(value) / 32))
def fft_power(waveform, dt):
"""Calculate a FFT of the given waveform of amplitude values;
converted to decibels and shifted so that maximum power is 0dB
Args:
waveform (ndarray): time domain waveform
dt (float): time step
Returns:
freqs (ndarray): frequency bins
power (ndarray): power
"""
# Calculate magnitude of frequency spectra of waveform (ignore warning from taking a log of any zero values)
with np.errstate(divide='ignore'):
power = 10 * np.log10(np.abs(np.fft.fft(waveform))**2)
# Replace any NaNs or Infs from zero division
power[np.invert(np.isfinite(power))] = 0
# Frequency bins
freqs = np.fft.fftfreq(power.size, d=dt)
# Shift powers so that frequency with maximum power is at zero decibels
power -= np.amax(power)
return freqs, power
def human_size(size, a_kilobyte_is_1024_bytes=False):
"""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 '{:.3g}{}'.format(size, suffix)
raise ValueError('Number is too large.')
def get_host_info():
"""Get information about the machine, CPU, RAM, and OS.
Returns:
hostinfo (dict): Manufacturer and model of machine; description of CPU
type, speed, cores; RAM; name and version of operating system.
"""
# Default to 'unknown' if any of the detection fails
manufacturer = model = cpuID = sockets = threadspercore = 'unknown'
# Windows
if sys.platform == 'win32':
# Manufacturer/model
try:
manufacturer = subprocess.check_output("wmic csproduct get vendor", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
manufacturer = manufacturer.split('\n')
if len(manufacturer) > 1:
manufacturer = manufacturer[1]
else:
manufacturer = manufacturer[0]
model = subprocess.check_output("wmic computersystem get model", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
model = model.split('\n')
if len(model) > 1:
model = model[1]
else:
model = model[0]
except subprocess.CalledProcessError:
pass
machineID = manufacturer + ' ' + model
# CPU information
try:
allcpuinfo = subprocess.check_output("wmic cpu get Name", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
allcpuinfo = allcpuinfo.split('\n')
sockets = 0
for line in allcpuinfo:
if 'CPU' in line:
cpuID = line.strip()
sockets += 1
except subprocess.CalledProcessError:
pass
# Hyperthreading
if psutil.cpu_count(logical=False) != psutil.cpu_count(logical=True):
hyperthreading = True
else:
hyperthreading = False
# OS version
if platform.machine().endswith('64'):
osbit = ' (64-bit)'
else:
osbit = ' (32-bit)'
osversion = 'Windows ' + platform.release() + osbit
# Mac OS X/macOS
elif sys.platform == 'darwin':
# Manufacturer/model
manufacturer = 'Apple'
try:
model = subprocess.check_output("sysctl -n hw.model", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
except subprocess.CalledProcessError:
pass
machineID = manufacturer + ' ' + model
# CPU information
try:
sockets = subprocess.check_output("sysctl -n hw.packages", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
sockets = int(sockets)
cpuID = subprocess.check_output("sysctl -n machdep.cpu.brand_string", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
cpuID = ' '.join(cpuID.split())
except subprocess.CalledProcessError:
pass
# Hyperthreading
if psutil.cpu_count(logical=False) != psutil.cpu_count(logical=True):
hyperthreading = True
else:
hyperthreading = False
# OS version
if int(platform.mac_ver()[0].split('.')[1]) < 12:
osversion = 'Mac OS X (' + platform.mac_ver()[0] + ')'
else:
osversion = 'macOS (' + platform.mac_ver()[0] + ')'
# Linux
elif sys.platform == 'linux':
# Manufacturer/model
try:
manufacturer = subprocess.check_output("cat /sys/class/dmi/id/sys_vendor", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
model = subprocess.check_output("cat /sys/class/dmi/id/product_name", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
except subprocess.CalledProcessError:
pass
machineID = manufacturer + ' ' + model
# CPU information
try:
cpuIDinfo = subprocess.check_output("cat /proc/cpuinfo", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
for line in cpuIDinfo.split('\n'):
if re.search('model name', line):
cpuID = re.sub('.*model name.*:', '', line, 1).strip()
allcpuinfo = subprocess.check_output("lscpu", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
for line in allcpuinfo.split('\n'):
if 'Socket(s)' in line:
sockets = int(line.strip()[-1])
if 'Thread(s) per core' in line:
threadspercore = int(line.strip()[-1])
except subprocess.CalledProcessError:
pass
# Hyperthreading
if threadspercore == 2:
hyperthreading = True
else:
hyperthreading = False
# OS version
osrelease = subprocess.check_output("cat /proc/sys/kernel/osrelease", shell=True).decode('utf-8').strip()
osversion = 'Linux (' + osrelease + ', ' + platform.linux_distribution()[0] + ')'
# Dictionary of host information
hostinfo = {}
hostinfo['hostname'] = platform.node()
hostinfo['machineID'] = machineID.strip()
hostinfo['sockets'] = sockets
hostinfo['cpuID'] = cpuID
hostinfo['osversion'] = osversion
hostinfo['hyperthreading'] = hyperthreading
hostinfo['logicalcores'] = psutil.cpu_count()
try:
# Get number of physical CPU cores, i.e. avoid hyperthreading with OpenMP
hostinfo['physicalcores'] = psutil.cpu_count(logical=False)
except ValueError:
hostinfo['physicalcores'] = hostinfo['logicalcores']
# Handle case where cpu_count returns None on some machines
if not hostinfo['physicalcores']:
hostinfo['physicalcores'] = hostinfo['logicalcores']
hostinfo['ram'] = psutil.virtual_memory().total
hostinfo['ompthreads'] = 1
return hostinfo
class GPU(object):
"""GPU information."""
def __init__(self, deviceID):
"""
Args:
deviceID (int): Device ID for GPU.
"""
self.deviceID = deviceID
self.name = None
self.pcibusID = None
self.constmem = None
self.totalmem = None
# Threads per block for main field updates
self.tpb = (256, 1, 1)
# Blocks per grid for main field updates (set in grid.py)
self.bpg = None
def get_gpu_info(self, drv):
"""Set information about GPU.
Args:
drv (object): PyCuda driver.
"""
self.name = drv.Device(self.deviceID).name()
self.pcibusID = drv.Device(self.deviceID).pci_bus_id()
self.constmem = drv.Device(self.deviceID).total_constant_memory
self.totalmem = drv.Device(self.deviceID).total_memory()
def detect_check_gpus(deviceIDs):
"""Get information about Nvidia GPU(s).
Args:
deviceIDs (list): List of integers of device IDs.
Returns:
gpus (list): Detected GPU(s) object(s).
"""
try:
import pycuda.driver as drv
except ImportError:
raise ImportError('To use gprMax in GPU mode the pycuda package must be installed, and you must have a NVIDIA CUDA-Enabled GPU (https://developer.nvidia.com/cuda-gpus).')
drv.init()
# Check and list any CUDA-Enabled GPUs
if drv.Device.count() == 0:
raise GeneralError('No NVIDIA CUDA-Enabled GPUs detected (https://developer.nvidia.com/cuda-gpus)')
elif 'CUDA_VISIBLE_DEVICES' in os.environ:
deviceIDsavail = os.environ.get('CUDA_VISIBLE_DEVICES')
deviceIDsavail = [int(s) for s in deviceIDsavail.split(',')]
else:
deviceIDsavail = range(drv.Device.count())
# If no device ID is given use default of 0
if not deviceIDs:
deviceIDs = [0]
# Check if requested device ID(s) exist
for ID in deviceIDs:
if ID not in deviceIDsavail:
raise GeneralError('GPU with device ID {} does not exist'.format(ID))
# Gather information about selected/detected GPUs
gpus = []
allgpustext = []
for ID in deviceIDsavail:
gpu = GPU(deviceID=ID)
gpu.get_gpu_info(drv)
if ID in deviceIDs:
gpus.append(gpu)
allgpustext.append('{} - {}, {}'.format(gpu.deviceID, gpu.name, human_size(gpu.totalmem, a_kilobyte_is_1024_bytes=True)))
return gpus, allgpustext
def timer():
"""Function to return the current process wide time in fractional seconds."""
return perf_counter()
class Printer():
def __init__(self, config):
self.printing = config.is_messages()
def print(self, str):
if self.printing:
print(str)
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from contextlib import contextmanager
import codecs
import decimal as d
import os
import platform
import psutil
import re
import subprocess
from shutil import get_terminal_size
import sys
import textwrap
from time import perf_counter
from colorama import init
from colorama import Fore
from colorama import Style
init()
import numpy as np
from .exceptions import GeneralError
def get_terminal_width():
"""Get/set width of terminal being used.
Returns:
terminalwidth (int): Terminal width
"""
terminalwidth = get_terminal_size()[0]
if terminalwidth == 0:
terminalwidth = 100
return terminalwidth
def logo(version):
"""Print gprMax logo, version, and licencing/copyright information.
Args:
version (str): Version number.
"""
description = '\n=== Electromagnetic modelling software based on the Finite-Difference Time-Domain (FDTD) method'
copyright = 'Copyright (C) 2015-2019: The University of Edinburgh'
authors = 'Authors: Craig Warren and Antonis Giannopoulos'
licenseinfo1 = '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.\n'
licenseinfo2 = '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.'
licenseinfo3 = 'You should have received a copy of the GNU General Public License along with gprMax. If not, see www.gnu.org/licenses.'
logo = """ www.gprmax.com __ __
__ _ _ __ _ __| \/ | __ ___ __
/ _` | '_ \| '__| |\/| |/ _` \ \/ /
| (_| | |_) | | | | | | (_| |> <
\__, | .__/|_| |_| |_|\__,_/_/\_\\
|___/|_|
v""" + version
print('{} {}\n'.format(description, '=' * (get_terminal_width() - len(description) - 1)))
print(Fore.CYAN + '{}\n'.format(logo))
print(Style.RESET_ALL + textwrap.fill(copyright, width=get_terminal_width() - 1, initial_indent=' '))
print(textwrap.fill(authors, width=get_terminal_width() - 1, initial_indent=' '))
print()
print(textwrap.fill(licenseinfo1, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' '))
print(textwrap.fill(licenseinfo2, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' '))
print(textwrap.fill(licenseinfo3, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' '))
@contextmanager
def open_path_file(path_or_file):
"""Accepts either a path as a string or a file object and returns a file
object (http://stackoverflow.com/a/6783680).
Args:
path_or_file: path as a string or a file object.
Returns:
f (object): File object.
"""
if isinstance(path_or_file, str):
f = file_to_close = codecs.open(path_or_file, 'r', encoding='utf-8')
else:
f = path_or_file
file_to_close = None
try:
yield f
finally:
if file_to_close:
file_to_close.close()
def round_value(value, decimalplaces=0):
"""Rounding function.
Args:
value (float): Number to round.
decimalplaces (int): Number of decimal places of float to represent rounded value.
Returns:
rounded (int/float): Rounded value.
"""
# Rounds to nearest integer (half values are rounded downwards)
if decimalplaces == 0:
rounded = int(d.Decimal(value).quantize(d.Decimal('1'), rounding=d.ROUND_HALF_DOWN))
# Rounds down to nearest float represented by number of decimal places
else:
precision = '1.{places}'.format(places='0' * decimalplaces)
rounded = float(d.Decimal(value).quantize(d.Decimal(precision), rounding=d.ROUND_FLOOR))
return rounded
def round32(value):
"""Rounds up to nearest multiple of 32."""
return int(32 * np.ceil(float(value) / 32))
def fft_power(waveform, dt):
"""Calculate a FFT of the given waveform of amplitude values;
converted to decibels and shifted so that maximum power is 0dB
Args:
waveform (ndarray): time domain waveform
dt (float): time step
Returns:
freqs (ndarray): frequency bins
power (ndarray): power
"""
# Calculate magnitude of frequency spectra of waveform (ignore warning from taking a log of any zero values)
with np.errstate(divide='ignore'):
power = 10 * np.log10(np.abs(np.fft.fft(waveform))**2)
# Replace any NaNs or Infs from zero division
power[np.invert(np.isfinite(power))] = 0
# Frequency bins
freqs = np.fft.fftfreq(power.size, d=dt)
# Shift powers so that frequency with maximum power is at zero decibels
power -= np.amax(power)
return freqs, power
def human_size(size, a_kilobyte_is_1024_bytes=False):
"""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 '{:.3g}{}'.format(size, suffix)
raise ValueError('Number is too large.')
def get_host_info():
"""Get information about the machine, CPU, RAM, and OS.
Returns:
hostinfo (dict): Manufacturer and model of machine; description of CPU
type, speed, cores; RAM; name and version of operating system.
"""
# Default to 'unknown' if any of the detection fails
manufacturer = model = cpuID = sockets = threadspercore = 'unknown'
# Windows
if sys.platform == 'win32':
# Manufacturer/model
try:
manufacturer = subprocess.check_output("wmic csproduct get vendor", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
manufacturer = manufacturer.split('\n')
if len(manufacturer) > 1:
manufacturer = manufacturer[1]
else:
manufacturer = manufacturer[0]
model = subprocess.check_output("wmic computersystem get model", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
model = model.split('\n')
if len(model) > 1:
model = model[1]
else:
model = model[0]
except subprocess.CalledProcessError:
pass
machineID = manufacturer + ' ' + model
# CPU information
try:
allcpuinfo = subprocess.check_output("wmic cpu get Name", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
allcpuinfo = allcpuinfo.split('\n')
sockets = 0
for line in allcpuinfo:
if 'CPU' in line:
cpuID = line.strip()
sockets += 1
except subprocess.CalledProcessError:
pass
# Hyperthreading
if psutil.cpu_count(logical=False) != psutil.cpu_count(logical=True):
hyperthreading = True
else:
hyperthreading = False
# OS version
if platform.machine().endswith('64'):
osbit = ' (64-bit)'
else:
osbit = ' (32-bit)'
osversion = 'Windows ' + platform.release() + osbit
# Mac OS X/macOS
elif sys.platform == 'darwin':
# Manufacturer/model
manufacturer = 'Apple'
try:
model = subprocess.check_output("sysctl -n hw.model", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
except subprocess.CalledProcessError:
pass
machineID = manufacturer + ' ' + model
# CPU information
try:
sockets = subprocess.check_output("sysctl -n hw.packages", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
sockets = int(sockets)
cpuID = subprocess.check_output("sysctl -n machdep.cpu.brand_string", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
cpuID = ' '.join(cpuID.split())
except subprocess.CalledProcessError:
pass
# Hyperthreading
if psutil.cpu_count(logical=False) != psutil.cpu_count(logical=True):
hyperthreading = True
else:
hyperthreading = False
# OS version
if int(platform.mac_ver()[0].split('.')[1]) < 12:
osversion = 'Mac OS X (' + platform.mac_ver()[0] + ')'
else:
osversion = 'macOS (' + platform.mac_ver()[0] + ')'
# Linux
elif sys.platform == 'linux':
# Manufacturer/model
try:
manufacturer = subprocess.check_output("cat /sys/class/dmi/id/sys_vendor", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
model = subprocess.check_output("cat /sys/class/dmi/id/product_name", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
except subprocess.CalledProcessError:
pass
machineID = manufacturer + ' ' + model
# CPU information
try:
cpuIDinfo = subprocess.check_output("cat /proc/cpuinfo", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
for line in cpuIDinfo.split('\n'):
if re.search('model name', line):
cpuID = re.sub('.*model name.*:', '', line, 1).strip()
allcpuinfo = subprocess.check_output("lscpu", shell=True, stderr=subprocess.STDOUT).decode('utf-8').strip()
for line in allcpuinfo.split('\n'):
if 'Socket(s)' in line:
sockets = int(line.strip()[-1])
if 'Thread(s) per core' in line:
threadspercore = int(line.strip()[-1])
except subprocess.CalledProcessError:
pass
# Hyperthreading
if threadspercore == 2:
hyperthreading = True
else:
hyperthreading = False
# OS version
osrelease = subprocess.check_output("cat /proc/sys/kernel/osrelease", shell=True).decode('utf-8').strip()
osversion = 'Linux (' + osrelease + ', ' + platform.linux_distribution()[0] + ')'
# Dictionary of host information
hostinfo = {}
hostinfo['hostname'] = platform.node()
hostinfo['machineID'] = machineID.strip()
hostinfo['sockets'] = sockets
hostinfo['cpuID'] = cpuID
hostinfo['osversion'] = osversion
hostinfo['hyperthreading'] = hyperthreading
hostinfo['logicalcores'] = psutil.cpu_count()
try:
# Get number of physical CPU cores, i.e. avoid hyperthreading with OpenMP
hostinfo['physicalcores'] = psutil.cpu_count(logical=False)
except ValueError:
hostinfo['physicalcores'] = hostinfo['logicalcores']
# Handle case where cpu_count returns None on some machines
if not hostinfo['physicalcores']:
hostinfo['physicalcores'] = hostinfo['logicalcores']
hostinfo['ram'] = psutil.virtual_memory().total
hostinfo['ompthreads'] = 1
return hostinfo
class GPU(object):
"""GPU information."""
def __init__(self, deviceID):
"""
Args:
deviceID (int): Device ID for GPU.
"""
self.deviceID = deviceID
self.name = None
self.pcibusID = None
self.constmem = None
self.totalmem = None
# Threads per block for main field updates
self.tpb = (256, 1, 1)
# Blocks per grid for main field updates (set in grid.py)
self.bpg = None
def get_gpu_info(self, drv):
"""Set information about GPU.
Args:
drv (object): PyCuda driver.
"""
self.name = drv.Device(self.deviceID).name()
self.pcibusID = drv.Device(self.deviceID).pci_bus_id()
self.constmem = drv.Device(self.deviceID).total_constant_memory
self.totalmem = drv.Device(self.deviceID).total_memory()
def detect_check_gpus(deviceIDs):
"""Get information about Nvidia GPU(s).
Args:
deviceIDs (list): List of integers of device IDs.
Returns:
gpus (list): Detected GPU(s) object(s).
"""
try:
import pycuda.driver as drv
except ImportError:
raise ImportError('To use gprMax in GPU mode the pycuda package must be installed, and you must have a NVIDIA CUDA-Enabled GPU (https://developer.nvidia.com/cuda-gpus).')
drv.init()
# Check and list any CUDA-Enabled GPUs
if drv.Device.count() == 0:
raise GeneralError('No NVIDIA CUDA-Enabled GPUs detected (https://developer.nvidia.com/cuda-gpus)')
elif 'CUDA_VISIBLE_DEVICES' in os.environ:
deviceIDsavail = os.environ.get('CUDA_VISIBLE_DEVICES')
deviceIDsavail = [int(s) for s in deviceIDsavail.split(',')]
else:
deviceIDsavail = range(drv.Device.count())
# If no device ID is given use default of 0
if not deviceIDs:
deviceIDs = [0]
# Check if requested device ID(s) exist
for ID in deviceIDs:
if ID not in deviceIDsavail:
raise GeneralError('GPU with device ID {} does not exist'.format(ID))
# Gather information about selected/detected GPUs
gpus = []
allgpustext = []
for ID in deviceIDsavail:
gpu = GPU(deviceID=ID)
gpu.get_gpu_info(drv)
if ID in deviceIDs:
gpus.append(gpu)
allgpustext.append('{} - {}, {}'.format(gpu.deviceID, gpu.name, human_size(gpu.totalmem, a_kilobyte_is_1024_bytes=True)))
return gpus, allgpustext
def timer():
"""Function to return the current process wide time in fractional seconds."""
return perf_counter()
class Printer():
def __init__(self, config):
self.printing = config.is_messages()
def print(self, str):
if self.printing:
print(str)

540
setup.py
查看文件

@@ -1,270 +1,270 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
try:
from setuptools import setup, Extension
except ImportError:
from distutils.core import setup
from distutils.extension import Extension
try:
import numpy as np
except ImportError:
raise ImportError('gprMax requires the NumPy package.')
import glob
import os
import pathlib
import re
import shutil
import sys
from jinja2 import Environment, PackageLoader, select_autoescape
def build_dispersive_material_templates():
"""
Function to generate Cython .pyx files for dispersive media update.
Jinja2 templates are used to render the various dispersive update functions.
"""
env = Environment(
loader=PackageLoader(__name__, 'gprMax/templates'),
)
template = env.get_template('fields_updates_dispersive_template')
# Render dispersive template for different types
r = template.render(
functions=[
# templates for Double precision and dispersive materials with
# real susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_double_real',
'name_b': 'update_electric_dispersive_multipole_B_double_real',
'name_a_1': 'update_electric_dispersive_1pole_A_double_real',
'name_b_1': 'update_electric_dispersive_1pole_B_double_real',
'field_type': 'double',
'dispersive_type': 'double'
},
# templates for Float precision and dispersive materials with
# real susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_float_real',
'name_b': 'update_electric_dispersive_multipole_B_float_real',
'name_a_1': 'update_electric_dispersive_1pole_A_float_real',
'name_b_1': 'update_electric_dispersive_1pole_B_float_real',
'field_type': 'float',
'dispersive_type': 'float'
},
# templates for Double precision and dispersive materials with
# complex susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_double_complex',
'name_b': 'update_electric_dispersive_multipole_B_double_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_double_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_double_complex',
'field_type': 'double',
'dispersive_type': 'double complex',
# c function to take real part of complex double type
'real_part': 'creal'
},
# templates for Float precision and dispersive materials with
# complex susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_float_complex',
'name_b': 'update_electric_dispersive_multipole_B_float_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_float_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_float_complex',
'field_type': 'float',
'dispersive_type': 'float complex',
# c function to take real part of complex double type
'real_part': 'crealf'
}]
)
with open('gprMax/cython/fields_updates_dispersive.pyx', 'w') as f:
f.write(r)
# Generate Cython file for dispersive materials update functions
build_dispersive_material_templates()
# Importing _version__.py before building can cause issues.
with open('gprMax/_version.py', 'r') as fd:
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
fd.read(), re.MULTILINE).group(1)
# Parse package name from init file. Importing __init__.py / gprMax will break as gprMax depends on compiled .pyx files.
with open('gprMax/__init__.py', 'r') as fd:
packagename = re.search(r'^__name__\s*=\s*[\'"]([^\'"]*)[\'"]',
fd.read(), re.MULTILINE).group(1)
packages = [packagename, 'tests', 'tools', 'user_libs']
# Parse long_description from README.rst file.
with open('README.rst','r') as fd:
long_description = fd.read()
# Python version
if sys.version_info[:2] < (3, 6):
sys.exit('\nExited: Requires Python 3.6 or newer!\n')
# Process 'build' command line argument
if 'build' in sys.argv:
print("Running 'build_ext --inplace'")
sys.argv.remove('build')
sys.argv.append('build_ext')
sys.argv.append('--inplace')
# Process '--no-cython' command line argument - either Cythonize or just compile the .c files
if '--no-cython' in sys.argv:
USE_CYTHON = False
sys.argv.remove('--no-cython')
else:
USE_CYTHON = True
# Build a list of all the files that need to be Cythonized looking in gprMax directory
cythonfiles = []
for root, dirs, files in os.walk(os.path.join(os.getcwd(), packagename), topdown=True):
for file in files:
if file.endswith('.pyx'):
cythonfiles.append(os.path.relpath(os.path.join(root, file)))
# Process 'cleanall' command line argument - cleanup Cython files
if 'cleanall' in sys.argv:
USE_CYTHON = False
for file in cythonfiles:
filebase = os.path.splitext(file)[0]
# Remove Cython C files
if os.path.isfile(filebase + '.c'):
try:
os.remove(filebase + '.c')
print('Removed: {}'.format(filebase + '.c'))
except OSError:
print('Could not remove: {}'.format(filebase + '.c'))
# Remove compiled Cython modules
libfile = glob.glob(os.path.join(os.getcwd(), os.path.splitext(file)[0]) + '*.pyd') + glob.glob(os.path.join(os.getcwd(), os.path.splitext(file)[0]) + '*.so')
if libfile:
libfile = libfile[0]
try:
os.remove(libfile)
print('Removed: {}'.format(os.path.abspath(libfile)))
except OSError:
print('Could not remove: {}'.format(os.path.abspath(libfile)))
# Remove build, dist, egg and __pycache__ directories
shutil.rmtree(os.path.join(os.getcwd(), 'build'), ignore_errors=True)
shutil.rmtree(os.path.join(os.getcwd(), 'dist'), ignore_errors=True)
shutil.rmtree(os.path.join(os.getcwd(), 'gprMax.egg-info'), ignore_errors=True)
for p in pathlib.Path(os.getcwd()).rglob('__pycache__'):
shutil.rmtree(p, ignore_errors=True)
print('Removed: {}'.format(p))
# Now do a normal clean
sys.argv[1] = 'clean' # this is what distutils understands
# Set compiler options
# Windows
if sys.platform == 'win32':
compile_args = ['/O2', '/openmp', '/w'] # No static linking as no static version of OpenMP library; /w disables warnings
linker_args = []
extra_objects = []
# macOS - needs gcc (usually via HomeBrew) because the default compiler LLVM (clang) does not support OpenMP
# - with gcc -fopenmp option implies -pthread
elif sys.platform == 'darwin':
gccpath = glob.glob('/usr/local/bin/gcc-[4-9]*')
if gccpath:
# Use newest gcc found
os.environ['CC'] = gccpath[-1].split(os.sep)[-1]
rpath = '/usr/local/opt/gcc/lib/gcc/' + gccpath[-1].split(os.sep)[-1][-1] + '/'
else:
raise('Cannot find gcc 4-9 in /usr/local/bin. gprMax requires gcc to be installed - easily done through the Homebrew package manager (http://brew.sh). Note: gcc with OpenMP support is required.')
compile_args = ['-O3', '-w', '-fopenmp', '-march=native'] # Sometimes worth testing with '-fstrict-aliasing', '-fno-common'
linker_args = ['-fopenmp', '-Wl,-rpath,' + rpath]
extra_objects = []
# Linux
elif sys.platform == 'linux':
compile_args = ['-O3', '-w', '-fopenmp', '-march=native']
linker_args = ['-fopenmp']
extra_objects = []
# Build a list of all the extensions
extensions = []
for file in cythonfiles:
tmp = os.path.splitext(file)
if USE_CYTHON:
fileext = tmp[1]
else:
fileext = '.c'
extension = Extension(tmp[0].replace(os.sep, '.'),
[tmp[0] + fileext],
language='c',
include_dirs=[np.get_include()],
extra_compile_args=compile_args,
extra_link_args=linker_args,
extra_objects=extra_objects)
extensions.append(extension)
# Cythonize (build .c files)
if USE_CYTHON:
from Cython.Build import cythonize
extensions = cythonize(extensions,
compiler_directives={
'boundscheck': False,
'wraparound': False,
'initializedcheck': False,
'embedsignature': True,
'language_level': 3
},
annotate=False)
# SetupTools Required to make package
import setuptools
setup(name=packagename,
version=version,
author='Craig Warren and Antonis Giannopoulos',
url='http://www.gprmax.com',
description='Electromagnetic Modelling Software based on the Finite-Difference Time-Domain (FDTD) method',
long_description=long_description,
long_description_content_type="text/x-rst",
license='GPLv3+',
classifiers=[
'Environment :: Console',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Operating System :: MacOS',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX :: Linux',
'Programming Language :: Cython',
'Programming Language :: Python :: 3',
'Topic :: Scientific/Engineering'
],
#requirements
python_requires=">3.6",
install_requires=[
"colorama",
"cython",
"h5py",
"jupyter",
"matplotlib",
"numpy",
"psutil",
"scipy",
"terminaltables",
"tqdm",
],
ext_modules=extensions,
packages=packages,
include_package_data=True,
include_dirs=[np.get_include()])
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
try:
from setuptools import setup, Extension
except ImportError:
from distutils.core import setup
from distutils.extension import Extension
try:
import numpy as np
except ImportError:
raise ImportError('gprMax requires the NumPy package.')
import glob
import os
import pathlib
import re
import shutil
import sys
from jinja2 import Environment, PackageLoader, select_autoescape
def build_dispersive_material_templates():
"""
Function to generate Cython .pyx files for dispersive media update.
Jinja2 templates are used to render the various dispersive update functions.
"""
env = Environment(
loader=PackageLoader(__name__, 'gprMax/templates'),
)
template = env.get_template('fields_updates_dispersive_template')
# Render dispersive template for different types
r = template.render(
functions=[
# templates for Double precision and dispersive materials with
# real susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_double_real',
'name_b': 'update_electric_dispersive_multipole_B_double_real',
'name_a_1': 'update_electric_dispersive_1pole_A_double_real',
'name_b_1': 'update_electric_dispersive_1pole_B_double_real',
'field_type': 'double',
'dispersive_type': 'double'
},
# templates for Float precision and dispersive materials with
# real susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_float_real',
'name_b': 'update_electric_dispersive_multipole_B_float_real',
'name_a_1': 'update_electric_dispersive_1pole_A_float_real',
'name_b_1': 'update_electric_dispersive_1pole_B_float_real',
'field_type': 'float',
'dispersive_type': 'float'
},
# templates for Double precision and dispersive materials with
# complex susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_double_complex',
'name_b': 'update_electric_dispersive_multipole_B_double_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_double_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_double_complex',
'field_type': 'double',
'dispersive_type': 'double complex',
# c function to take real part of complex double type
'real_part': 'creal'
},
# templates for Float precision and dispersive materials with
# complex susceptibility functions
{
'name_a': 'update_electric_dispersive_multipole_A_float_complex',
'name_b': 'update_electric_dispersive_multipole_B_float_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_float_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_float_complex',
'field_type': 'float',
'dispersive_type': 'float complex',
# c function to take real part of complex double type
'real_part': 'crealf'
}]
)
with open('gprMax/cython/fields_updates_dispersive.pyx', 'w') as f:
f.write(r)
# Generate Cython file for dispersive materials update functions
build_dispersive_material_templates()
# Importing _version__.py before building can cause issues.
with open('gprMax/_version.py', 'r') as fd:
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
fd.read(), re.MULTILINE).group(1)
# Parse package name from init file. Importing __init__.py / gprMax will break as gprMax depends on compiled .pyx files.
with open('gprMax/__init__.py', 'r') as fd:
packagename = re.search(r'^__name__\s*=\s*[\'"]([^\'"]*)[\'"]',
fd.read(), re.MULTILINE).group(1)
packages = [packagename, 'tests', 'tools', 'user_libs']
# Parse long_description from README.rst file.
with open('README.rst','r') as fd:
long_description = fd.read()
# Python version
if sys.version_info[:2] < (3, 6):
sys.exit('\nExited: Requires Python 3.6 or newer!\n')
# Process 'build' command line argument
if 'build' in sys.argv:
print("Running 'build_ext --inplace'")
sys.argv.remove('build')
sys.argv.append('build_ext')
sys.argv.append('--inplace')
# Process '--no-cython' command line argument - either Cythonize or just compile the .c files
if '--no-cython' in sys.argv:
USE_CYTHON = False
sys.argv.remove('--no-cython')
else:
USE_CYTHON = True
# Build a list of all the files that need to be Cythonized looking in gprMax directory
cythonfiles = []
for root, dirs, files in os.walk(os.path.join(os.getcwd(), packagename), topdown=True):
for file in files:
if file.endswith('.pyx'):
cythonfiles.append(os.path.relpath(os.path.join(root, file)))
# Process 'cleanall' command line argument - cleanup Cython files
if 'cleanall' in sys.argv:
USE_CYTHON = False
for file in cythonfiles:
filebase = os.path.splitext(file)[0]
# Remove Cython C files
if os.path.isfile(filebase + '.c'):
try:
os.remove(filebase + '.c')
print('Removed: {}'.format(filebase + '.c'))
except OSError:
print('Could not remove: {}'.format(filebase + '.c'))
# Remove compiled Cython modules
libfile = glob.glob(os.path.join(os.getcwd(), os.path.splitext(file)[0]) + '*.pyd') + glob.glob(os.path.join(os.getcwd(), os.path.splitext(file)[0]) + '*.so')
if libfile:
libfile = libfile[0]
try:
os.remove(libfile)
print('Removed: {}'.format(os.path.abspath(libfile)))
except OSError:
print('Could not remove: {}'.format(os.path.abspath(libfile)))
# Remove build, dist, egg and __pycache__ directories
shutil.rmtree(os.path.join(os.getcwd(), 'build'), ignore_errors=True)
shutil.rmtree(os.path.join(os.getcwd(), 'dist'), ignore_errors=True)
shutil.rmtree(os.path.join(os.getcwd(), 'gprMax.egg-info'), ignore_errors=True)
for p in pathlib.Path(os.getcwd()).rglob('__pycache__'):
shutil.rmtree(p, ignore_errors=True)
print('Removed: {}'.format(p))
# Now do a normal clean
sys.argv[1] = 'clean' # this is what distutils understands
# Set compiler options
# Windows
if sys.platform == 'win32':
compile_args = ['/O2', '/openmp', '/w'] # No static linking as no static version of OpenMP library; /w disables warnings
linker_args = []
extra_objects = []
# macOS - needs gcc (usually via HomeBrew) because the default compiler LLVM (clang) does not support OpenMP
# - with gcc -fopenmp option implies -pthread
elif sys.platform == 'darwin':
gccpath = glob.glob('/usr/local/bin/gcc-[4-9]*')
if gccpath:
# Use newest gcc found
os.environ['CC'] = gccpath[-1].split(os.sep)[-1]
rpath = '/usr/local/opt/gcc/lib/gcc/' + gccpath[-1].split(os.sep)[-1][-1] + '/'
else:
raise('Cannot find gcc 4-9 in /usr/local/bin. gprMax requires gcc to be installed - easily done through the Homebrew package manager (http://brew.sh). Note: gcc with OpenMP support is required.')
compile_args = ['-O3', '-w', '-fopenmp', '-march=native'] # Sometimes worth testing with '-fstrict-aliasing', '-fno-common'
linker_args = ['-fopenmp', '-Wl,-rpath,' + rpath]
extra_objects = []
# Linux
elif sys.platform == 'linux':
compile_args = ['-O3', '-w', '-fopenmp', '-march=native']
linker_args = ['-fopenmp']
extra_objects = []
# Build a list of all the extensions
extensions = []
for file in cythonfiles:
tmp = os.path.splitext(file)
if USE_CYTHON:
fileext = tmp[1]
else:
fileext = '.c'
extension = Extension(tmp[0].replace(os.sep, '.'),
[tmp[0] + fileext],
language='c',
include_dirs=[np.get_include()],
extra_compile_args=compile_args,
extra_link_args=linker_args,
extra_objects=extra_objects)
extensions.append(extension)
# Cythonize (build .c files)
if USE_CYTHON:
from Cython.Build import cythonize
extensions = cythonize(extensions,
compiler_directives={
'boundscheck': False,
'wraparound': False,
'initializedcheck': False,
'embedsignature': True,
'language_level': 3
},
annotate=False)
# SetupTools Required to make package
import setuptools
setup(name=packagename,
version=version,
author='Craig Warren and Antonis Giannopoulos',
url='http://www.gprmax.com',
description='Electromagnetic Modelling Software based on the Finite-Difference Time-Domain (FDTD) method',
long_description=long_description,
long_description_content_type="text/x-rst",
license='GPLv3+',
classifiers=[
'Environment :: Console',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Operating System :: MacOS',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX :: Linux',
'Programming Language :: Cython',
'Programming Language :: Python :: 3',
'Topic :: Scientific/Engineering'
],
#requirements
python_requires=">3.6",
install_requires=[
"colorama",
"cython",
"h5py",
"jupyter",
"matplotlib",
"numpy",
"psutil",
"scipy",
"terminaltables",
"tqdm",
],
ext_modules=extensions,
packages=packages,
include_package_data=True,
include_dirs=[np.get_include()])