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

查看文件

@@ -1,164 +1,164 @@
"""Class for add_grass command.""" """Class for add_grass command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..utilities import round_value from ..utilities import round_value
from ..materials import Material from ..materials import Material
from ..fractals import FractalSurface from ..fractals import FractalSurface
from ..fractals import Grass from ..fractals import Grass
from tqdm import tqdm from tqdm import tqdm
import numpy as np import numpy as np
class AddGrass(UserObjectGeometry): class AddGrass(UserObjectGeometry):
"""User class for Grass command.""" """User class for Grass command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 12 self.order = 12
self.hash = '#add_grass' self.hash = '#add_grass'
def create(self, grid, uip): def create(self, grid, uip):
"""Add Grass to fractal box.""" """Add Grass to fractal box."""
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
p2 = self.kwargs['p2'] p2 = self.kwargs['p2']
fractal_box_id = self.kwargs['fractal_box_id'] fractal_box_id = self.kwargs['fractal_box_id']
frac_dim = self.kwargs['frac_dim'] frac_dim = self.kwargs['frac_dim']
limits = self.kwargs['limits'] limits = self.kwargs['limits']
n_blades = self.kwargs['n_blades'] n_blades = self.kwargs['n_blades']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' requires at least eleven parameters') raise CmdInputError(self.__str__() + ' requires at least eleven parameters')
try: try:
seed = self.kwargs['seed'] seed = self.kwargs['seed']
except KeyError: except KeyError:
seed = None seed = None
# grab the correct fractal volume # grab the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id] volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
if volumes: if volumes:
volume = volumes[0] volume = volumes[0]
else: else:
raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id)) raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id))
p1, p2 = uip.check_box_points(p1, p2, self.__str__()) p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1 xs, ys, zs = p1
xf, yf, zf = p2 xf, yf, zf = p2
if frac_dim < 0: if frac_dim < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension') raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension')
if limits[0] < 0 or limits[1] < 0: 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') raise CmdInputError(self.__str__() + ' requires a positive value for the minimum and maximum heights for grass blades')
# Check for valid orientations # Check for valid orientations
if xs == xf: if xs == xf:
if ys == yf or zs == zf: if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if xs != volume.xs and xs != volume.xf: if xs != volume.xs and xs != volume.xf:
raise CmdInputError(self.__str__() + ' must specify external surfaces on a fractal box') 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)) fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
# xminus surface # xminus surface
if xs == volume.xs: if xs == volume.xs:
raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction') raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction')
# xplus surface # xplus surface
elif xf == volume.xf: elif xf == volume.xf:
if fractalrange[1] > grid.nx: 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') raise CmdInputError(self.__str__() + ' cannot apply grass to fractal box as it would exceed the domain size in the x direction')
requestedsurface = 'xplus' requestedsurface = 'xplus'
elif ys == yf: elif ys == yf:
if xs == xf or zs == zf: if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if ys != volume.ys and ys != volume.yf: if ys != volume.ys and ys != volume.yf:
raise CmdInputError(self.__str__() + ' must specify external surfaces on a fractal box') 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)) fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
# yminus surface # yminus surface
if ys == volume.ys: if ys == volume.ys:
raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction') raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction')
# yplus surface # yplus surface
elif yf == volume.yf: elif yf == volume.yf:
if fractalrange[1] > grid.ny: 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') raise CmdInputError(self.__str__() + ' cannot apply grass to fractal box as it would exceed the domain size in the y direction')
requestedsurface = 'yplus' requestedsurface = 'yplus'
elif zs == zf: elif zs == zf:
if xs == xf or ys == yf: if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if zs != volume.zs and zs != volume.zf: if zs != volume.zs and zs != volume.zf:
raise CmdInputError(self.__str__() + ' must specify external surfaces on a fractal box') 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)) fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))
# zminus surface # zminus surface
if zs == volume.zs: if zs == volume.zs:
raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction') raise CmdInputError(self.__str__() + ' grass can only be specified on surfaces in the positive axis direction')
# zplus surface # zplus surface
elif zf == volume.zf: elif zf == volume.zf:
if fractalrange[1] > grid.nz: 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') raise CmdInputError(self.__str__() + ' cannot apply grass to fractal box as it would exceed the domain size in the z direction')
requestedsurface = 'zplus' requestedsurface = 'zplus'
else: else:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim) surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim)
surface.ID = 'grass' surface.ID = 'grass'
surface.surfaceID = requestedsurface surface.surfaceID = requestedsurface
surface.seed = seed surface.seed = seed
# Set the fractal range to scale the fractal distribution between zero and one # Set the fractal range to scale the fractal distribution between zero and one
surface.fractalrange = (0, 1) surface.fractalrange = (0, 1)
surface.operatingonID = volume.ID surface.operatingonID = volume.ID
surface.generate_fractal_surface(grid) surface.generate_fractal_surface(grid)
if n_blades > surface.fractalsurface.shape[0] * surface.fractalsurface.shape[1]: 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') 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 # Scale the distribution so that the summation is equal to one, i.e. a probability distribution
surface.fractalsurface = surface.fractalsurface / np.sum(surface.fractalsurface) surface.fractalsurface = surface.fractalsurface / np.sum(surface.fractalsurface)
# Set location of grass blades using probability distribution # Set location of grass blades using probability distribution
# Create 1D vector of probability values from the 2D surface # Create 1D vector of probability values from the 2D surface
probability1D = np.cumsum(np.ravel(surface.fractalsurface)) probability1D = np.cumsum(np.ravel(surface.fractalsurface))
# Create random numbers between zero and one for the number of blades of grass # Create random numbers between zero and one for the number of blades of grass
R = np.random.RandomState(surface.seed) R = np.random.RandomState(surface.seed)
A = R.random_sample(n_blades) 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. # 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])) 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 # Set the fractal range to minimum and maximum heights of the grass blades
surface.fractalrange = fractalrange surface.fractalrange = fractalrange
# Set the fractal surface using the pre-calculated spatial distribution and a random height # 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])) surface.fractalsurface = np.zeros((surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]))
for i in range(len(bladesindex[0])): 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) surface.fractalsurface[bladesindex[0][i], bladesindex[1][i]] = R.randint(surface.fractalrange[0], surface.fractalrange[1], size=1)
# Create grass geometry parameters # Create grass geometry parameters
g = Grass(n_blades) g = Grass(n_blades)
g.seed = surface.seed g.seed = surface.seed
surface.grass.append(g) surface.grass.append(g)
# Check to see if grass has been already defined as a material # Check to see if grass has been already defined as a material
if not any(x.ID == 'grass' for x in grid.materials): if not any(x.ID == 'grass' for x in grid.materials):
m = Material(len(grid.materials), 'grass') m = Material(len(grid.materials), 'grass')
m.averagable = False m.averagable = False
m.type = 'builtin, debye' m.type = 'builtin, debye'
m.er = Material.grasseri m.er = Material.grasseri
m.deltaer.append(Material.grassdeltaer) m.deltaer.append(Material.grassdeltaer)
m.tau.append(Material.grasstau) m.tau.append(Material.grasstau)
grid.materials.append(m) grid.materials.append(m)
if Material.maxpoles == 0: if Material.maxpoles == 0:
Material.maxpoles = 1 Material.maxpoles = 1
# Check if time step for model is suitable for using grass # Check if time step for model is suitable for using grass
grass = next((x for x in grid.materials if x.ID == '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) testgrass = next((x for x in grass.tau if x < grid.dt), None)
if testgrass: if testgrass:
raise CmdInputError(self.__str__() + ' requires the time step for the model to be less than the relaxation time required to model grass.') 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) volume.fractalsurfaces.append(surface)
if config.is_messages(): 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)) 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.""" """Class for surface roughness command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..fractals import FractalSurface from ..fractals import FractalSurface
from ..utilities import round_value from ..utilities import round_value
import gprMax.config as config import gprMax.config as config
from tqdm import tqdm from tqdm import tqdm
import numpy as np import numpy as np
class AddSurfaceRoughness(UserObjectGeometry): class AddSurfaceRoughness(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 10 self.order = 10
self.hash = '#add_surface_roughness' self.hash = '#add_surface_roughness'
def create(self, grid, uip): def create(self, grid, uip):
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
p2 = self.kwargs['p2'] p2 = self.kwargs['p2']
frac_dim = self.kwargs['frac_dim'] frac_dim = self.kwargs['frac_dim']
weighting = np.array(self.kwargs['weighting'], dtype=np.float64) weighting = np.array(self.kwargs['weighting'], dtype=np.float64)
limits = np.array(self.kwargs['limits']) limits = np.array(self.kwargs['limits'])
fractal_box_id = self.kwargs['fractal_box_id'] fractal_box_id = self.kwargs['fractal_box_id']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' Incorrect parameters') raise CmdInputError(self.__str__() + ' Incorrect parameters')
try: try:
seed = self.kwargs['seed'] seed = self.kwargs['seed']
except KeyError: except KeyError:
seed = None seed = None
# grab the correct fractal volume # grab the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id] volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
if volumes: if volumes:
volume = volumes[0] volume = volumes[0]
else: else:
raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id)) raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id))
p1, p2 = uip.check_box_points(p1, p2, self.__str__()) p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1 xs, ys, zs = p1
xf, yf, zf = p2 xf, yf, zf = p2
if frac_dim < 0: if frac_dim < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension') raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension')
if weighting[0] < 0: if weighting[0] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the first direction of the surface') raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the first direction of the surface')
if weighting[1] < 0: if weighting[1] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the second direction of the surface') raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the second direction of the surface')
# Check for valid orientations # Check for valid orientations
if xs == xf: if xs == xf:
if ys == yf or zs == zf: if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if xs != volume.xs and xs != volume.xf: if xs != volume.xs and xs != volume.xf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box') 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)) fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx))
# xminus surface # xminus surface
if xs == volume.xs: if xs == volume.xs:
if fractalrange[0] < 0 or fractalrange[1] > volume.xf: 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') 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' requestedsurface = 'xminus'
# xplus surface # xplus surface
elif xf == volume.xf: elif xf == volume.xf:
if fractalrange[0] < volume.xs or fractalrange[1] > grid.nx: 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') 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' requestedsurface = 'xplus'
elif ys == yf: elif ys == yf:
if xs == xf or zs == zf: if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if ys != volume.ys and ys != volume.yf: if ys != volume.ys and ys != volume.yf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box') 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)) fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy))
# yminus surface # yminus surface
if ys == volume.ys: if ys == volume.ys:
if fractalrange[0] < 0 or fractalrange[1] > volume.yf: 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') 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' requestedsurface = 'yminus'
# yplus surface # yplus surface
elif yf == volume.yf: elif yf == volume.yf:
if fractalrange[0] < volume.ys or fractalrange[1] > grid.ny: 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') 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' requestedsurface = 'yplus'
elif zs == zf: elif zs == zf:
if xs == xf or ys == yf: if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if zs != volume.zs and zs != volume.zf: if zs != volume.zs and zs != volume.zf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box') 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)) fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz))
# zminus surface # zminus surface
if zs == volume.zs: if zs == volume.zs:
if fractalrange[0] < 0 or fractalrange[1] > volume.zf: 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') 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' requestedsurface = 'zminus'
# zplus surface # zplus surface
elif zf == volume.zf: elif zf == volume.zf:
if fractalrange[0] < volume.zs or fractalrange[1] > grid.nz: 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') 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' requestedsurface = 'zplus'
else: else:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim) surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim)
surface.surfaceID = requestedsurface surface.surfaceID = requestedsurface
surface.fractalrange = fractalrange surface.fractalrange = fractalrange
surface.operatingonID = volume.ID surface.operatingonID = volume.ID
surface.seed = seed surface.seed = seed
surface.weighting = weighting surface.weighting = weighting
# List of existing surfaces IDs # List of existing surfaces IDs
existingsurfaceIDs = [x.surfaceID for x in volume.fractalsurfaces] existingsurfaceIDs = [x.surfaceID for x in volume.fractalsurfaces]
if surface.surfaceID in existingsurfaceIDs: if surface.surfaceID in existingsurfaceIDs:
raise CmdInputError(self.__str__() + ' has already been used on the {} surface'.format(surface.surfaceID)) raise CmdInputError(self.__str__() + ' has already been used on the {} surface'.format(surface.surfaceID))
surface.generate_fractal_surface(grid) surface.generate_fractal_surface(grid)
volume.fractalsurfaces.append(surface) volume.fractalsurfaces.append(surface)
if config.is_messages(): 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)) 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.""" """Class for surface water command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..utilities import round_value from ..utilities import round_value
from ..materials import Material from ..materials import Material
from tqdm import tqdm from tqdm import tqdm
class AddSurfaceWater(UserObjectGeometry): class AddSurfaceWater(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 11 self.order = 11
self.hash = '#add_surface_water' self.hash = '#add_surface_water'
def create(self, grid, uip): def create(self, grid, uip):
""""Create surface water on fractal box.""" """"Create surface water on fractal box."""
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
p2 = self.kwargs['p2'] p2 = self.kwargs['p2']
fractal_box_id = self.kwargs['fractal_box_id'] fractal_box_id = self.kwargs['fractal_box_id']
depth = self.kwargs['depth'] depth = self.kwargs['depth']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' requires exactly eight parameters') raise CmdInputError(self.__str__() + ' requires exactly eight parameters')
# grab the correct fractal volume # grab the correct fractal volume
volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id] volumes = [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]
if volumes: if volumes:
volume = volumes[0] volume = volumes[0]
else: else:
raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id)) raise CmdInputError(self.__str__() + ' Cant find FractalBox {}'.format(fractal_box_id))
p1, p2 = uip.check_box_points(p1, p2, self.__str__()) p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1 xs, ys, zs = p1
xf, yf, zf = p2 xf, yf, zf = p2
if depth <= 0: if depth <= 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the depth of water') raise CmdInputError(self.__str__() + ' requires a positive value for the depth of water')
# Check for valid orientations # Check for valid orientations
if xs == xf: if xs == xf:
if ys == yf or zs == zf: if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if xs != volume.xs and xs != volume.xf: if xs != volume.xs and xs != volume.xf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box') raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
# xminus surface # xminus surface
if xs == volume.xs: if xs == volume.xs:
requestedsurface = 'xminus' requestedsurface = 'xminus'
# xplus surface # xplus surface
elif xf == volume.xf: elif xf == volume.xf:
requestedsurface = 'xplus' requestedsurface = 'xplus'
filldepthcells = round_value(depth / grid.dx) filldepthcells = round_value(depth / grid.dx)
filldepth = filldepthcells * grid.dx filldepth = filldepthcells * grid.dx
elif ys == yf: elif ys == yf:
if xs == xf or zs == zf: if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if ys != volume.ys and ys != volume.yf: if ys != volume.ys and ys != volume.yf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box') raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
# yminus surface # yminus surface
if ys == volume.ys: if ys == volume.ys:
requestedsurface = 'yminus' requestedsurface = 'yminus'
# yplus surface # yplus surface
elif yf == volume.yf: elif yf == volume.yf:
requestedsurface = 'yplus' requestedsurface = 'yplus'
filldepthcells = round_value(depth / grid.dy) filldepthcells = round_value(depth / grid.dy)
filldepth = filldepthcells * grid.dy filldepth = filldepthcells * grid.dy
elif zs == zf: elif zs == zf:
if xs == xf or ys == yf: if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
if zs != volume.zs and zs != volume.zf: if zs != volume.zs and zs != volume.zf:
raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box') raise CmdInputError(self.__str__() + ' can only be used on the external surfaces of a fractal box')
# zminus surface # zminus surface
if zs == volume.zs: if zs == volume.zs:
requestedsurface = 'zminus' requestedsurface = 'zminus'
# zplus surface # zplus surface
elif zf == volume.zf: elif zf == volume.zf:
requestedsurface = 'zplus' requestedsurface = 'zplus'
filldepthcells = round_value(depth / grid.dz) filldepthcells = round_value(depth / grid.dz)
filldepth = filldepthcells * grid.dz filldepth = filldepthcells * grid.dz
else: else:
raise CmdInputError(self.__str__() + ' dimensions are not specified correctly') raise CmdInputError(self.__str__() + ' dimensions are not specified correctly')
surface = next((x for x in volume.fractalsurfaces if x.surfaceID == requestedsurface), None) surface = next((x for x in volume.fractalsurfaces if x.surfaceID == requestedsurface), None)
if not surface: if not surface:
raise CmdInputError(self.__str__() + ' specified surface {} does not have a rough surface applied'.format(requestedsurface)) raise CmdInputError(self.__str__() + ' specified surface {} does not have a rough surface applied'.format(requestedsurface))
surface.filldepth = filldepthcells surface.filldepth = filldepthcells
# Check that requested fill depth falls within range of surface roughness # Check that requested fill depth falls within range of surface roughness
if surface.filldepth < surface.fractalrange[0] or surface.filldepth > surface.fractalrange[1]: 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') 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 # Check to see if water has been already defined as a material
if not any(x.ID == 'water' for x in grid.materials): if not any(x.ID == 'water' for x in grid.materials):
m = Material(len(grid.materials), 'water') m = Material(len(grid.materials), 'water')
m.averagable = False m.averagable = False
m.type = 'builtin, debye' m.type = 'builtin, debye'
m.er = Material.watereri m.er = Material.watereri
m.deltaer.append(Material.waterdeltaer) m.deltaer.append(Material.waterdeltaer)
m.tau.append(Material.watertau) m.tau.append(Material.watertau)
grid.materials.append(m) grid.materials.append(m)
if Material.maxpoles == 0: if Material.maxpoles == 0:
Material.maxpoles = 1 Material.maxpoles = 1
# Check if time step for model is suitable for using water # Check if time step for model is suitable for using water
water = next((x for x in grid.materials if x.ID == '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) testwater = next((x for x in water.tau if x < grid.dt), None)
if testwater: if testwater:
raise CmdInputError(self.__str__() + ' requires the time step for the model to be less than the relaxation time required to model water.') 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(): 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)) 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.""" """Class for triangle command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..materials import Material from ..materials import Material
from ..cython.geometry_primitives import build_box from ..cython.geometry_primitives import build_box
import gprMax.config as config import gprMax.config as config
from tqdm import tqdm from tqdm import tqdm
import numpy as np import numpy as np
class Box(UserObjectGeometry): class Box(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 5 self.order = 5
self.hash = '#box' self.hash = '#box'
def create(self, grid, uip): def create(self, grid, uip):
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
p2 = self.kwargs['p2'] p2 = self.kwargs['p2']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' Please specify two points.') raise CmdInputError(self.__str__() + ' Please specify two points.')
# check materials have been specified # check materials have been specified
# isotropic case # isotropic case
try: try:
materialsrequested = [self.kwargs['material_id']] materialsrequested = [self.kwargs['material_id']]
except KeyError: except KeyError:
# Anisotropic case # Anisotropic case
try: try:
materialsrequested = self.kwargs['material_ids'] materialsrequested = self.kwargs['material_ids']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified') raise CmdInputError(self.__str__() + ' No materials have been specified')
# check averaging # check averaging
try: try:
# go with user specified averaging # go with user specified averaging
averagebox = self.kwargs['averaging'] averagebox = self.kwargs['averaging']
except KeyError: except KeyError:
# if they havent specfied - go with the grid default # if they havent specfied - go with the grid default
averagebox = grid.averagevolumeobjects averagebox = grid.averagevolumeobjects
p1, p2 = uip.check_box_points(p1, p2, self.__str__()) p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1 xs, ys, zs = p1
xf, yf, zf = p2 xf, yf, zf = p2
# Look up requested materials in existing list of material instances # 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] materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested): if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials] notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound)) raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
averaging = materials[0].averagable and averagebox averaging = materials[0].averagable and averagebox
numID = numIDx = numIDy = numIDz = materials[0].numID numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 3: elif len(materials) == 3:
averaging = False averaging = False
numIDx = materials[0].numID numIDx = materials[0].numID
numIDy = materials[1].numID numIDy = materials[1].numID
numIDz = materials[2].numID numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID] averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial: if averagedmaterial:
numID = averagedmaterial.numID numID = averagedmaterial.numID
else: else:
numID = len(grid.materials) numID = len(grid.materials)
m = Material(numID, requiredID) m = Material(numID, requiredID)
m.type = 'dielectric-smoothed' m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material # Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) 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.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.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) m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list # Append the new material object to the materials list
grid.materials.append(m) 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) 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 config.is_messages():
if averaging: if averaging:
dielectricsmoothing = 'on' dielectricsmoothing = 'on'
else: else:
dielectricsmoothing = 'off' 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)) 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 from jinja2 import Environment, PackageLoader, select_autoescape
env = Environment( env = Environment(
loader=PackageLoader(__name__, 'templates'), loader=PackageLoader(__name__, 'templates'),
) )
template = env.get_template('fields_updates_dispersive_template') template = env.get_template('fields_updates_dispersive_template')
r = template.render( r = template.render(
functions=[ functions=[
# name, double, real # name, double, real
{ {
'name_a': 'update_electric_dispersive_multipole_A_double_real', 'name_a': 'update_electric_dispersive_multipole_A_double_real',
'name_b': 'update_electric_dispersive_multipole_B_double_real', 'name_b': 'update_electric_dispersive_multipole_B_double_real',
'name_a_1': 'update_electric_dispersive_1pole_A_double_real', 'name_a_1': 'update_electric_dispersive_1pole_A_double_real',
'name_b_1': 'update_electric_dispersive_1pole_B_double_real', 'name_b_1': 'update_electric_dispersive_1pole_B_double_real',
'field_type': 'double', 'field_type': 'double',
'dispersive_type': 'double' 'dispersive_type': 'double'
}, },
# name, float, real # name, float, real
{ {
'name_a': 'update_electric_dispersive_multipole_A_float_real', 'name_a': 'update_electric_dispersive_multipole_A_float_real',
'name_b': 'update_electric_dispersive_multipole_B_float_real', 'name_b': 'update_electric_dispersive_multipole_B_float_real',
'name_a_1': 'update_electric_dispersive_1pole_A_float_real', 'name_a_1': 'update_electric_dispersive_1pole_A_float_real',
'name_b_1': 'update_electric_dispersive_1pole_B_float_real', 'name_b_1': 'update_electric_dispersive_1pole_B_float_real',
'field_type': 'float', 'field_type': 'float',
'dispersive_type': 'float' 'dispersive_type': 'float'
}, },
# name, double, complex # name, double, complex
{ {
'name_a': 'update_electric_dispersive_multipole_A_double_complex', 'name_a': 'update_electric_dispersive_multipole_A_double_complex',
'name_b': 'update_electric_dispersive_multipole_B_double_complex', 'name_b': 'update_electric_dispersive_multipole_B_double_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_double_complex', 'name_a_1': 'update_electric_dispersive_1pole_A_double_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_double_complex', 'name_b_1': 'update_electric_dispersive_1pole_B_double_complex',
'field_type': 'double', 'field_type': 'double',
'dispersive_type': 'double complex', 'dispersive_type': 'double complex',
'real_part': 'creal' 'real_part': 'creal'
}, },
# name, float, complex # name, float, complex
{ {
'name_a': 'update_electric_dispersive_multipole_A_float_complex', 'name_a': 'update_electric_dispersive_multipole_A_float_complex',
'name_b': 'update_electric_dispersive_multipole_B_float_complex', 'name_b': 'update_electric_dispersive_multipole_B_float_complex',
'name_a_1': 'update_electric_dispersive_1pole_A_float_complex', 'name_a_1': 'update_electric_dispersive_1pole_A_float_complex',
'name_b_1': 'update_electric_dispersive_1pole_B_float_complex', 'name_b_1': 'update_electric_dispersive_1pole_B_float_complex',
'field_type': 'float', 'field_type': 'float',
'dispersive_type': 'float complex', 'dispersive_type': 'float complex',
'real_part': 'crealf' 'real_part': 'crealf'
}] }]
) )
f = open('cython/dispersive_updates_test.pyx', 'w') f = open('cython/dispersive_updates_test.pyx', 'w')
f.write(r) f.write(r)
f.close() f.close()

查看文件

@@ -1,98 +1,98 @@
"""Class for cylinder command.""" """Class for cylinder command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..materials import Material from ..materials import Material
from ..cython.geometry_primitives import build_cylinder from ..cython.geometry_primitives import build_cylinder
from tqdm import tqdm from tqdm import tqdm
import numpy as np import numpy as np
import gprMax.config as config import gprMax.config as config
class Cylinder(UserObjectGeometry): class Cylinder(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 6 self.order = 6
self.hash = '#cylinder' self.hash = '#cylinder'
def create(self, grid, uip): def create(self, grid, uip):
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
p2 = self.kwargs['p2'] p2 = self.kwargs['p2']
r = self.kwargs['r'] r = self.kwargs['r']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' Please specify 2 points and a radius') raise CmdInputError(self.__str__() + ' Please specify 2 points and a radius')
# check averaging # check averaging
try: try:
# go with user specified averaging # go with user specified averaging
averagecylinder = self.kwargs['averaging'] averagecylinder = self.kwargs['averaging']
except KeyError: except KeyError:
# if they havent specfied - go with the grid default # if they havent specfied - go with the grid default
averagecylinder = grid.averagevolumeobjects averagecylinder = grid.averagevolumeobjects
# check materials have been specified # check materials have been specified
# isotropic case # isotropic case
try: try:
materialsrequested = [self.kwargs['material_id']] materialsrequested = [self.kwargs['material_id']]
except KeyError: except KeyError:
# Anisotropic case # Anisotropic case
try: try:
materialsrequested = self.kwargs['material_ids'] materialsrequested = self.kwargs['material_ids']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified') raise CmdInputError(self.__str__() + ' No materials have been specified')
x1, y1, z1 = uip.round_to_grid(p1) x1, y1, z1 = uip.round_to_grid(p1)
x2, y2, z2 = uip.round_to_grid(p2) x2, y2, z2 = uip.round_to_grid(p2)
if r <= 0: if r <= 0:
raise CmdInputError(self.__str__() + ' the radius {:g} should be a positive value.'.format(r)) raise CmdInputError(self.__str__() + ' the radius {:g} should be a positive value.'.format(r))
# Look up requested materials in existing list of material instances # 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] materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested): if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials] notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound)) raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
averaging = materials[0].averagable and averagecylinder averaging = materials[0].averagable and averagecylinder
numID = numIDx = numIDy = numIDz = materials[0].numID numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 3: elif len(materials) == 3:
averaging = False averaging = False
numIDx = materials[0].numID numIDx = materials[0].numID
numIDy = materials[1].numID numIDy = materials[1].numID
numIDz = materials[2].numID numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID] averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial: if averagedmaterial:
numID = averagedmaterial.numID numID = averagedmaterial.numID
else: else:
numID = len(grid.materials) numID = len(grid.materials)
m = Material(numID, requiredID) m = Material(numID, requiredID)
m.type = 'dielectric-smoothed' m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material # Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) 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.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.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) m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list # Append the new material object to the materials list
grid.materials.append(m) 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) 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 config.is_messages():
if averaging: if averaging:
dielectricsmoothing = 'on' dielectricsmoothing = 'on'
else: else:
dielectricsmoothing = 'off' 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)) 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.""" """Class for cylinder command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..materials import Material from ..materials import Material
from ..cython.geometry_primitives import build_cylindrical_sector from ..cython.geometry_primitives import build_cylindrical_sector
from tqdm import tqdm from tqdm import tqdm
import numpy as np import numpy as np
class CylindricalSector(UserObjectGeometry): class CylindricalSector(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 7 self.order = 7
self.hash = '#cylindrical_sector' self.hash = '#cylindrical_sector'
def create(self, grid, uip): def create(self, grid, uip):
try: try:
normal = self.kwargs['normal'].lower() normal = self.kwargs['normal'].lower()
ctr1 = self.kwargs['ctr1'] ctr1 = self.kwargs['ctr1']
ctr2 = self.kwargs['ctr2'] ctr2 = self.kwargs['ctr2']
extent1 = self.kwargs['extent1'] extent1 = self.kwargs['extent1']
extent2 = self.kwargs['extent2'] extent2 = self.kwargs['extent2']
start = self.kwargs['start'] start = self.kwargs['start']
end = self.kwargs['end'] end = self.kwargs['end']
r = self.kwargs['r'] r = self.kwargs['r']
thickness = extent2 - extent1 thickness = extent2 - extent1
except KeyError: except KeyError:
raise CmdInputError(self.__str__()) raise CmdInputError(self.__str__())
# check averaging # check averaging
try: try:
# go with user specified averaging # go with user specified averaging
averagecylindricalsector = self.kwargs['averaging'] averagecylindricalsector = self.kwargs['averaging']
except KeyError: except KeyError:
# if they havent specfied - go with the grid default # if they havent specfied - go with the grid default
averagecylindricalsector = grid.averagevolumeobjects averagecylindricalsector = grid.averagevolumeobjects
# check materials have been specified # check materials have been specified
# isotropic case # isotropic case
try: try:
materialsrequested = [self.kwargs['material_id']] materialsrequested = [self.kwargs['material_id']]
except KeyError: except KeyError:
# Anisotropic case # Anisotropic case
try: try:
materialsrequested = self.kwargs['material_ids'] materialsrequested = self.kwargs['material_ids']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified') raise CmdInputError(self.__str__() + ' No materials have been specified')
sectorstartangle = 2 * np.pi * (start / 360) sectorstartangle = 2 * np.pi * (start / 360)
sectorangle = 2 * np.pi * (end / 360) sectorangle = 2 * np.pi * (end / 360)
if normal != 'x' and normal != 'y' and normal != 'z': if normal != 'x' and normal != 'y' and normal != 'z':
raise CmdInputError(self.__str__() + ' the normal direction must be either x, y or z.') raise CmdInputError(self.__str__() + ' the normal direction must be either x, y or z.')
if r <= 0: if r <= 0:
raise CmdInputError(self.__str__() + ' the radius {:g} should be a positive value.'.format(r)) raise CmdInputError(self.__str__() + ' the radius {:g} should be a positive value.'.format(r))
if sectorstartangle < 0 or sectorangle <= 0: if sectorstartangle < 0 or sectorangle <= 0:
raise CmdInputError(self.__str__() + ' the starting angle and sector angle should be a positive values.') 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: 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.') 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 # 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] materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested): if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials] notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound)) raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
if thickness > 0: if thickness > 0:
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
averaging = materials[0].averagable and averagecylindricalsector averaging = materials[0].averagable and averagecylindricalsector
numID = numIDx = numIDy = numIDz = materials[0].numID numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 3: elif len(materials) == 3:
averaging = False averaging = False
numIDx = materials[0].numID numIDx = materials[0].numID
numIDy = materials[1].numID numIDy = materials[1].numID
numIDz = materials[2].numID numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID] averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial: if averagedmaterial:
numID = averagedmaterial.numID numID = averagedmaterial.numID
else: else:
numID = len(grid.materials) numID = len(grid.materials)
m = Material(numID, requiredID) m = Material(numID, requiredID)
m.type = 'dielectric-smoothed' m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material # Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) 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.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.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) m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list # Append the new material object to the materials list
grid.materials.append(m) grid.materials.append(m)
else: else:
averaging = False averaging = False
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
numID = numIDx = numIDy = numIDz = materials[0].numID numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 3: elif len(materials) == 3:
# numID requires a value but it will not be used # numID requires a value but it will not be used
numID = None numID = None
numIDx = materials[0].numID numIDx = materials[0].numID
numIDy = materials[1].numID numIDy = materials[1].numID
numIDz = materials[2].numID numIDz = materials[2].numID
# yz-plane cylindrical sector # yz-plane cylindrical sector
if normal == 'x': if normal == 'x':
level, ctr1, ctr2 = uip.round_to_grid((extent1, ctr1, ctr2)) level, ctr1, ctr2 = uip.round_to_grid((extent1, ctr1, ctr2))
# xz-plane cylindrical sector # xz-plane cylindrical sector
elif normal == 'y': elif normal == 'y':
ctr1, level, ctr2 = uip.round_to_grid((ctr1, extent1, ctr2)) ctr1, level, ctr2 = uip.round_to_grid((ctr1, extent1, ctr2))
# xy-plane cylindrical sector # xy-plane cylindrical sector
elif normal == 'z': elif normal == 'z':
ctr1, ctr2, level = uip.round_to_grid((ctr1, ctr2, extent1)) 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) 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 config.is_messages():
if thickness > 0: if thickness > 0:
if averaging: if averaging:
dielectricsmoothing = 'on' dielectricsmoothing = 'on'
else: else:
dielectricsmoothing = 'off' 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)) 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: 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))) 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.""" """Class for edge command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..cython.geometry_primitives import build_edge_x from ..cython.geometry_primitives import build_edge_x
from ..cython.geometry_primitives import build_edge_y from ..cython.geometry_primitives import build_edge_y
from ..cython.geometry_primitives import build_edge_z from ..cython.geometry_primitives import build_edge_z
from tqdm import tqdm from tqdm import tqdm
class Edge(UserObjectGeometry): class Edge(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 2 self.order = 2
self.hash = '#edge' self.hash = '#edge'
def create(self, grid, uip): def create(self, grid, uip):
"""Create edge and add it to the grid.""" """Create edge and add it to the grid."""
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
p2 = self.kwargs['p2'] p2 = self.kwargs['p2']
material_id = self.kwargs['material_id'] material_id = self.kwargs['material_id']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' requires exactly 3 parameters') raise CmdInputError(self.__str__() + ' requires exactly 3 parameters')
p1, p2 = uip.check_box_points(p1, p2, self.__str__()) p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1 xs, ys, zs = p1
xf, yf, zf = p2 xf, yf, zf = p2
material = next((x for x in grid.materials if x.ID == material_id), None) material = next((x for x in grid.materials if x.ID == material_id), None)
if not material: if not material:
raise CmdInputError('Material with ID {} does not exist'.format(material_id)) raise CmdInputError('Material with ID {} does not exist'.format(material_id))
# Check for valid orientations # Check for valid orientations
# x-orientated wire # x-orientated wire
if xs != xf: if xs != xf:
if ys != yf or zs != zf: if ys != yf or zs != zf:
raise CmdInputError(self.__str__() + ' the edge is not specified correctly') raise CmdInputError(self.__str__() + ' the edge is not specified correctly')
else: else:
for i in range(xs, xf): for i in range(xs, xf):
build_edge_x(i, ys, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID) build_edge_x(i, ys, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID)
# y-orientated wire # y-orientated wire
elif ys != yf: elif ys != yf:
if xs != xf or zs != zf: if xs != xf or zs != zf:
raise CmdInputError(self.__str__() + ' the edge is not specified correctly') raise CmdInputError(self.__str__() + ' the edge is not specified correctly')
else: else:
for j in range(ys, yf): for j in range(ys, yf):
build_edge_y(xs, j, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID) build_edge_y(xs, j, zs, material.numID, grid.rigidE, grid.rigidH, grid.ID)
# z-orientated wire # z-orientated wire
elif zs != zf: elif zs != zf:
if xs != xf or ys != yf: if xs != xf or ys != yf:
raise CmdInputError(self.__str__() + ' the edge is not specified correctly') raise CmdInputError(self.__str__() + ' the edge is not specified correctly')
else: else:
for k in range(zs, zf): for k in range(zs, zf):
build_edge_z(xs, ys, k, material.numID, grid.rigidE, grid.rigidH, grid.ID) build_edge_z(xs, ys, k, material.numID, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages(): 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)) 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.""" """Class for surface roughness command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..fractals import FractalVolume from ..fractals import FractalVolume
import gprMax.config as config import gprMax.config as config
from tqdm import tqdm from tqdm import tqdm
import numpy as np import numpy as np
class FractalBox(UserObjectGeometry): class FractalBox(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 9 self.order = 9
self.hash = '#fractal_box' self.hash = '#fractal_box'
def create(self, grid, uip): def create(self, grid, uip):
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
p2 = self.kwargs['p2'] p2 = self.kwargs['p2']
frac_dim = self.kwargs['frac_dim'] frac_dim = self.kwargs['frac_dim']
weighting = np.array(self.kwargs['weighting']) weighting = np.array(self.kwargs['weighting'])
n_materials = self.kwargs['n_materials'] n_materials = self.kwargs['n_materials']
mixing_model_id = self.kwargs['mixing_model_id'] mixing_model_id = self.kwargs['mixing_model_id']
ID = self.kwargs['id'] ID = self.kwargs['id']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' Incorrect parameters') raise CmdInputError(self.__str__() + ' Incorrect parameters')
try: try:
seed = self.kwargs['seed'] seed = self.kwargs['seed']
except KeyError: except KeyError:
seed = None seed = None
# Default is no dielectric smoothing for a fractal box # Default is no dielectric smoothing for a fractal box
averagefractalbox = False averagefractalbox = False
# check averaging # check averaging
try: try:
# go with user specified averaging # go with user specified averaging
averagefractalbox = self.kwargs['averaging'] averagefractalbox = self.kwargs['averaging']
except KeyError: except KeyError:
# if they havent specfied - go with the grid default # if they havent specfied - go with the grid default
averagefractalbox = False averagefractalbox = False
p1, p2 = uip.check_box_points(p1, p2, self.__str__()) p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1 xs, ys, zs = p1
xf, yf, zf = p2 xf, yf, zf = p2
if frac_dim < 0: if frac_dim < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension') raise CmdInputError(self.__str__() + ' requires a positive value for the fractal dimension')
if weighting[0] < 0: if weighting[0] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the x direction') raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the x direction')
if weighting[1] < 0: if weighting[1] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the y direction') raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the y direction')
if weighting[2] < 0: if weighting[2] < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the z direction') raise CmdInputError(self.__str__() + ' requires a positive value for the fractal weighting in the z direction')
if n_materials < 0: if n_materials < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for the number of bins') 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 # 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) 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) material = next((x for x in grid.materials if x.ID == mixing_model_id), None)
nbins = n_materials nbins = n_materials
if mixingmodel: if mixingmodel:
if nbins == 1: if nbins == 1:
raise CmdInputError(self.__str__() + ' must be used with more than one material from the mixing model.') 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 # Create materials from mixing model as number of bins now known from fractal_box command
mixingmodel.calculate_debye_properties(nbins, grid) mixingmodel.calculate_debye_properties(nbins, grid)
elif not material: elif not material:
raise CmdInputError(self.__str__() + ' mixing model or material with ID {} does not exist'.format(mixing_model_id)) 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 = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim)
volume.ID = ID volume.ID = ID
volume.operatingonID = mixing_model_id volume.operatingonID = mixing_model_id
volume.nbins = nbins volume.nbins = nbins
volume.seed = seed volume.seed = seed
volume.weighting = weighting volume.weighting = weighting
volume.averaging = averagefractalbox volume.averaging = averagefractalbox
volume.mixingmodel = mixingmodel volume.mixingmodel = mixingmodel
if config.is_messages(): if config.is_messages():
if volume.averaging: if volume.averaging:
dielectricsmoothing = 'on' dielectricsmoothing = 'on'
else: else:
dielectricsmoothing = 'off' 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)) 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) grid.fractalvolumes.append(volume)

查看文件

@@ -1,112 +1,112 @@
"""Class for edge command.""" """Class for edge command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..cython.geometry_primitives import build_face_yz from ..cython.geometry_primitives import build_face_yz
from ..cython.geometry_primitives import build_face_xz from ..cython.geometry_primitives import build_face_xz
from ..cython.geometry_primitives import build_face_xy from ..cython.geometry_primitives import build_face_xy
from tqdm import tqdm from tqdm import tqdm
class Plate(UserObjectGeometry): class Plate(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 3 self.order = 3
self.hash = '#plate' self.hash = '#plate'
def create(self, grid, uip): def create(self, grid, uip):
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
p2 = self.kwargs['p2'] p2 = self.kwargs['p2']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' 2 points must be specified') raise CmdInputError(self.__str__() + ' 2 points must be specified')
# isotropic # isotropic
try: try:
materialsrequested = [self.kwargs['material_id']] materialsrequested = [self.kwargs['material_id']]
except KeyError: except KeyError:
# Anisotropic case # Anisotropic case
try: try:
materialsrequested = self.kwargs['material_ids'] materialsrequested = self.kwargs['material_ids']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified') raise CmdInputError(self.__str__() + ' No materials have been specified')
p1, p2 = uip.check_box_points(p1, p2, self.__str__()) p1, p2 = uip.check_box_points(p1, p2, self.__str__())
xs, ys, zs = p1 xs, ys, zs = p1
xf, yf, zf = p2 xf, yf, zf = p2
# Check for valid orientations # Check for valid orientations
if xs == xf: if xs == xf:
if ys == yf or zs == zf: if ys == yf or zs == zf:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly') raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
elif ys == yf: elif ys == yf:
if xs == xf or zs == zf: if xs == xf or zs == zf:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly') raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
elif zs == zf: elif zs == zf:
if xs == xf or ys == yf: if xs == xf or ys == yf:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly') raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
else: else:
raise CmdInputError(self.__str__() + ' the plate is not specified correctly') raise CmdInputError(self.__str__() + ' the plate is not specified correctly')
# Look up requested materials in existing list of material instances # 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] materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested): if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials] notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound)) raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# yz-plane plate # yz-plane plate
if xs == xf: if xs == xf:
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
numIDx = numIDy = numIDz = materials[0].numID numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 2: elif len(materials) == 2:
numIDy = materials[0].numID numIDy = materials[0].numID
numIDz = materials[1].numID numIDz = materials[1].numID
for j in range(ys, yf): for j in range(ys, yf):
for k in range(zs, zf): for k in range(zs, zf):
build_face_yz(xs, j, k, numIDy, numIDz, grid.rigidE, grid.rigidH, grid.ID) build_face_yz(xs, j, k, numIDy, numIDz, grid.rigidE, grid.rigidH, grid.ID)
# xz-plane plate # xz-plane plate
elif ys == yf: elif ys == yf:
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
numIDx = numIDy = numIDz = materials[0].numID numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 2: elif len(materials) == 2:
numIDx = materials[0].numID numIDx = materials[0].numID
numIDz = materials[1].numID numIDz = materials[1].numID
for i in range(xs, xf): for i in range(xs, xf):
for k in range(zs, zf): for k in range(zs, zf):
build_face_xz(i, ys, k, numIDx, numIDz, grid.rigidE, grid.rigidH, grid.ID) build_face_xz(i, ys, k, numIDx, numIDz, grid.rigidE, grid.rigidH, grid.ID)
# xy-plane plate # xy-plane plate
elif zs == zf: elif zs == zf:
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
numIDx = numIDy = numIDz = materials[0].numID numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 2: elif len(materials) == 2:
numIDx = materials[0].numID numIDx = materials[0].numID
numIDy = materials[1].numID numIDy = materials[1].numID
for i in range(xs, xf): for i in range(xs, xf):
for j in range(ys, yf): for j in range(ys, yf):
build_face_xy(i, j, zs, numIDx, numIDy, grid.rigidE, grid.rigidH, grid.ID) build_face_xy(i, j, zs, numIDx, numIDy, grid.rigidE, grid.rigidH, grid.ID)
if config.is_messages(): 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))) 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.""" """Class for sphere command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..materials import Material from ..materials import Material
from ..cython.geometry_primitives import build_sphere from ..cython.geometry_primitives import build_sphere
from tqdm import tqdm from tqdm import tqdm
import numpy as np import numpy as np
class Sphere(UserObjectGeometry): class Sphere(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 8 self.order = 8
self.hash = '#sphere' self.hash = '#sphere'
def create(self, grid, uip): def create(self, grid, uip):
try: try:
p1 = self.kwargs['p1'] p1 = self.kwargs['p1']
r = self.kwargs['r'] r = self.kwargs['r']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' Please specify a point and a radius.') raise CmdInputError(self.__str__() + ' Please specify a point and a radius.')
# check averaging # check averaging
try: try:
# go with user specified averaging # go with user specified averaging
averagesphere = self.kwargs['averaging'] averagesphere = self.kwargs['averaging']
except KeyError: except KeyError:
# if they havent specfied - go with the grid default # if they havent specfied - go with the grid default
averagesphere = grid.averagevolumeobjects averagesphere = grid.averagevolumeobjects
# check materials have been specified # check materials have been specified
# isotropic case # isotropic case
try: try:
materialsrequested = [self.kwargs['material_id']] materialsrequested = [self.kwargs['material_id']]
except KeyError: except KeyError:
# Anisotropic case # Anisotropic case
try: try:
materialsrequested = self.kwargs['material_ids'] materialsrequested = self.kwargs['material_ids']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified') raise CmdInputError(self.__str__() + ' No materials have been specified')
# Centre of sphere # Centre of sphere
xc, yc, zc = uip.round_to_grid(p1) xc, yc, zc = uip.round_to_grid(p1)
# Look up requested materials in existing list of material instances # 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] materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested): if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials] notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound)) raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
averaging = materials[0].averagable and averagesphere averaging = materials[0].averagable and averagesphere
numID = numIDx = numIDy = numIDz = materials[0].numID numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 3: elif len(materials) == 3:
averaging = False averaging = False
numIDx = materials[0].numID numIDx = materials[0].numID
numIDy = materials[1].numID numIDy = materials[1].numID
numIDz = materials[2].numID numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID] averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial: if averagedmaterial:
numID = averagedmaterial.numID numID = averagedmaterial.numID
else: else:
numID = len(grid.materials) numID = len(grid.materials)
m = Material(numID, requiredID) m = Material(numID, requiredID)
m.type = 'dielectric-smoothed' m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material # Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) 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.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.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) m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list # Append the new material object to the materials list
grid.materials.append(m) 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) 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 config.is_messages():
if averaging: if averaging:
dielectricsmoothing = 'on' dielectricsmoothing = 'on'
else: else:
dielectricsmoothing = 'off' 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)) 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.""" """Class for triangle command."""
from .cmds_geometry import UserObjectGeometry from .cmds_geometry import UserObjectGeometry
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..materials import Material from ..materials import Material
from ..cython.geometry_primitives import build_triangle from ..cython.geometry_primitives import build_triangle
from tqdm import tqdm from tqdm import tqdm
import numpy as np import numpy as np
class Triangle(UserObjectGeometry): class Triangle(UserObjectGeometry):
"""User class for edge command.""" """User class for edge command."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.order = 4 self.order = 4
self.hash = '#triangle' self.hash = '#triangle'
def create(self, grid, uip): def create(self, grid, uip):
try: try:
up1 = self.kwargs['p1'] up1 = self.kwargs['p1']
up2 = self.kwargs['p2'] up2 = self.kwargs['p2']
up3 = self.kwargs['p3'] up3 = self.kwargs['p3']
thickness = self.kwargs['thickness'] thickness = self.kwargs['thickness']
except KeyError: except KeyError:
raise CmdInputError(self.params_str() + ' Specify 3 points and a thickness') raise CmdInputError(self.params_str() + ' Specify 3 points and a thickness')
# check averaging # check averaging
try: try:
# go with user specified averaging # go with user specified averaging
averagetriangularprism = self.kwargs['averaging'] averagetriangularprism = self.kwargs['averaging']
except KeyError: except KeyError:
# if they havent specfied - go with the grid default # if they havent specfied - go with the grid default
averagetriangularprism = grid.averagevolumeobjects averagetriangularprism = grid.averagevolumeobjects
# check materials have been specified # check materials have been specified
# isotropic case # isotropic case
try: try:
materialsrequested = [self.kwargs['material_id']] materialsrequested = [self.kwargs['material_id']]
except KeyError: except KeyError:
# Anisotropic case # Anisotropic case
try: try:
materialsrequested = self.kwargs['material_ids'] materialsrequested = self.kwargs['material_ids']
except KeyError: except KeyError:
raise CmdInputError(self.__str__() + ' No materials have been specified') raise CmdInputError(self.__str__() + ' No materials have been specified')
# Check whether points are valid against grid # Check whether points are valid against grid
uip.check_tri_points(up1, up2, up3, object) uip.check_tri_points(up1, up2, up3, object)
# Convert points to metres # Convert points to metres
x1, y1, z1 = uip.round_to_grid(up1) x1, y1, z1 = uip.round_to_grid(up1)
x2, y2, z2 = uip.round_to_grid(up2) x2, y2, z2 = uip.round_to_grid(up2)
x3, y3, z3 = uip.round_to_grid(up3) x3, y3, z3 = uip.round_to_grid(up3)
if thickness < 0: if thickness < 0:
raise CmdInputError(self.__str__() + ' requires a positive value for thickness') raise CmdInputError(self.__str__() + ' requires a positive value for thickness')
# Check for valid orientations # Check for valid orientations
# yz-plane triangle # yz-plane triangle
if x1 == x2 and x2 == x3: if x1 == x2 and x2 == x3:
normal = 'x' normal = 'x'
# xz-plane triangle # xz-plane triangle
elif y1 == y2 and y2 == y3: elif y1 == y2 and y2 == y3:
normal = 'y' normal = 'y'
# xy-plane triangle # xy-plane triangle
elif z1 == z2 and z2 == z3: elif z1 == z2 and z2 == z3:
normal = 'z' normal = 'z'
else: else:
raise CmdInputError(self.__str__() + ' the triangle is not specified correctly') raise CmdInputError(self.__str__() + ' the triangle is not specified correctly')
# Look up requested materials in existing list of material instances # 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] materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]
if len(materials) != len(materialsrequested): if len(materials) != len(materialsrequested):
notfound = [x for x in materialsrequested if x not in materials] notfound = [x for x in materialsrequested if x not in materials]
raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound)) raise CmdInputError(self.__str__() + ' material(s) {} do not exist'.format(notfound))
if thickness > 0: if thickness > 0:
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
averaging = materials[0].averagable and averagetriangularprism averaging = materials[0].averagable and averagetriangularprism
numID = numIDx = numIDy = numIDz = materials[0].numID numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 3: elif len(materials) == 3:
averaging = False averaging = False
numIDx = materials[0].numID numIDx = materials[0].numID
numIDy = materials[1].numID numIDy = materials[1].numID
numIDz = materials[2].numID numIDz = materials[2].numID
requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID
averagedmaterial = [x for x in grid.materials if x.ID == requiredID] averagedmaterial = [x for x in grid.materials if x.ID == requiredID]
if averagedmaterial: if averagedmaterial:
numID = averagedmaterial.numID numID = averagedmaterial.numID
else: else:
numID = len(grid.materials) numID = len(grid.materials)
m = Material(numID, requiredID) m = Material(numID, requiredID)
m.type = 'dielectric-smoothed' m.type = 'dielectric-smoothed'
# Create dielectric-smoothed constituents for material # Create dielectric-smoothed constituents for material
m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) 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.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.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) m.sm = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0)
# Append the new material object to the materials list # Append the new material object to the materials list
grid.materials.append(m) grid.materials.append(m)
else: else:
averaging = False averaging = False
# Isotropic case # Isotropic case
if len(materials) == 1: if len(materials) == 1:
numID = numIDx = numIDy = numIDz = materials[0].numID numID = numIDx = numIDy = numIDz = materials[0].numID
# Uniaxial anisotropic case # Uniaxial anisotropic case
elif len(materials) == 3: elif len(materials) == 3:
# numID requires a value but it will not be used # numID requires a value but it will not be used
numID = None numID = None
numIDx = materials[0].numID numIDx = materials[0].numID
numIDy = materials[1].numID numIDy = materials[1].numID
numIDz = materials[2].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) 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 config.is_messages():
if thickness > 0: if thickness > 0:
if averaging: if averaging:
dielectricsmoothing = 'on' dielectricsmoothing = 'on'
else: else:
dielectricsmoothing = 'off' 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)) 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: 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))) 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 # Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos # Authors: Craig Warren and Antonis Giannopoulos
# #
# This file is part of gprMax. # This file is part of gprMax.
# #
# gprMax is free software: you can redistribute it and/or modify # gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# gprMax is distributed in the hope that it will be useful, # gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>. # along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import os import os
import cython import cython
import numpy as np import numpy as np
from scipy.constants import c from scipy.constants import c
from scipy.constants import mu_0 as m0 from scipy.constants import mu_0 as m0
from scipy.constants import epsilon_0 as e0 from scipy.constants import epsilon_0 as e0
import sys import sys
from .utilities import get_terminal_width from .utilities import get_terminal_width
from .utilities import get_host_info from .utilities import get_host_info
from pathlib import Path from pathlib import Path
from colorama import init from colorama import init
from colorama import Fore from colorama import Fore
from colorama import Style from colorama import Style
init() init()
# Impedance of free space (Ohms) # Impedance of free space (Ohms)
z0 = np.sqrt(m0 / e0) z0 = np.sqrt(m0 / e0)
# General setting for the simulation # General setting for the simulation
# inputfilepath: path to inputfile location # inputfilepath: path to inputfile location
# outputfilepath: path to outputfile location # outputfilepath: path to outputfile location
# messages: whether to print all messages as output to stdout or not # messages: whether to print all messages as output to stdout or not
# progressbars: whether to show progress bars on stdoout or not # progressbars: whether to show progress bars on stdoout or not
# mode: 2D TMx, 2D TMy, 2D TMz, or 3D # mode: 2D TMx, 2D TMy, 2D TMz, or 3D
# cpu, cuda, opencl: solver type # cpu, cuda, opencl: solver type
# autotranslate: auto translate objects with main grid coordinates # autotranslate: auto translate objects with main grid coordinates
# to their equivalent local grid coordinate within the subgrid. If this option is off # 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. # users must specify sub-grid object point within the global subgrid space.
general = {'inputfilepath': 'gprMax', 'outputfilepath': 'gprMax', 'messages': True, general = {'inputfilepath': 'gprMax', 'outputfilepath': 'gprMax', 'messages': True,
'progressbars': True, 'mode': '3D', 'cpu': True, 'cuda': False, 'opencl': False, 'autotranslate': False} 'progressbars': True, 'mode': '3D', 'cpu': True, 'cuda': False, 'opencl': False, 'autotranslate': False}
def is_messages(): def is_messages():
"""Function to return messages.""" """Function to return messages."""
return general['messages'] return general['messages']
# Store information about host machine # Store information about host machine
hostinfo = get_host_info() hostinfo = get_host_info()
# Store information for CUDA solver type # Store information for CUDA solver type
# gpus: information about any GPUs as a list of GPU objects # gpus: information about any GPUs as a list of GPU objects
# snapsgpu2cpu: copy snapshot data from GPU to CPU during simulation # snapsgpu2cpu: copy snapshot data from GPU to CPU during simulation
# N.B. This will happen if the requested snapshots are too large to fit # 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 # on the memory of the GPU. If True this will slow performance significantly
cuda = {'gpus': None, 'snapsgpu2cpu': False} cuda = {'gpus': None, 'snapsgpu2cpu': False}
# Numerical dispersion analysis parameters # Numerical dispersion analysis parameters
# highestfreqthres: threshold (dB) down from maximum power (0dB) of main frequency used # highestfreqthres: threshold (dB) down from maximum power (0dB) of main frequency used
# to calculate highest frequency for numerical dispersion analysis # to calculate highest frequency for numerical dispersion analysis
# maxnumericaldisp: maximum allowable percentage physical phase-velocity phase error # maxnumericaldisp: maximum allowable percentage physical phase-velocity phase error
# mingridsampling: minimum grid sampling of smallest wavelength for physical wave propagation # mingridsampling: minimum grid sampling of smallest wavelength for physical wave propagation
numdispersion = {'highestfreqthres': 40, 'maxnumericaldisp': 2, 'mingridsampling': 3} numdispersion = {'highestfreqthres': 40, 'maxnumericaldisp': 2, 'mingridsampling': 3}
# Materials # Materials
# maxpoles: Maximum number of dispersive material poles in a model # maxpoles: Maximum number of dispersive material poles in a model
materials = {'maxpoles': 0, 'dispersivedtype': None, 'dispersiveCdtype': None} materials = {'maxpoles': 0, 'dispersivedtype': None, 'dispersiveCdtype': None}
# Data types # Data types
# Solid and ID arrays use 32-bit integers (0 to 4294967295) # 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) # Rigid arrays use 8-bit integers (the smallest available type to store true/false)
# Fractal and dispersive coefficient arrays use complex numbers (complex) # Fractal and dispersive coefficient arrays use complex numbers (complex)
# which are represented as two floats # which are represented as two floats
# Main field arrays use floats (float_or_double) and complex numbers (complex) # 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 of float_or_double and complex: single or double for numpy and C (CUDA) arrays
precision = 'double' precision = 'double'
if precision == 'single': if precision == 'single':
dtypes = {'float_or_double': np.float32, 'complex': np.complex64, dtypes = {'float_or_double': np.float32, 'complex': np.complex64,
'cython_float_or_double': cython.float, 'cython_complex': cython.floatcomplex, 'cython_float_or_double': cython.float, 'cython_complex': cython.floatcomplex,
'C_float_or_double': 'float', 'C_complex': 'pycuda::complex<float>'} 'C_float_or_double': 'float', 'C_complex': 'pycuda::complex<float>'}
elif precision == 'double': elif precision == 'double':
dtypes = {'float_or_double': np.float64, 'complex': np.complex128, dtypes = {'float_or_double': np.float64, 'complex': np.complex128,
'cython_float_or_double': cython.double, 'cython_complex': cython.doublecomplex, 'cython_float_or_double': cython.double, 'cython_complex': cython.doublecomplex,
'C_float_or_double': 'double', 'C_complex': 'pycuda::complex<double>'} 'C_float_or_double': 'double', 'C_complex': 'pycuda::complex<double>'}
class ModelConfig(): class ModelConfig():
def __init__(self, sim_config, i): def __init__(self, sim_config, i):
self.sim_config = sim_config self.sim_config = sim_config
self.reuse_geometry = False self.reuse_geometry = False
# current model number (indexed from 0) # current model number (indexed from 0)
self.i = i self.i = i
parts = self.sim_config.output_file_path.parts parts = self.sim_config.output_file_path.parts
if not sim_config.single_model: if not sim_config.single_model:
# 1 indexed # 1 indexed
self.appendmodelnumber = str(self.i + 1) self.appendmodelnumber = str(self.i + 1)
else: else:
self.appendmodelnumber = '' self.appendmodelnumber = ''
# outputfilepath for specific model # outputfilepath for specific model
self.output_file_path = Path(*parts[:-2], parts[-1] + self.appendmodelnumber) self.output_file_path = Path(*parts[:-2], parts[-1] + self.appendmodelnumber)
self.output_file_path_ext = self.output_file_path.with_suffix('.out') self.output_file_path_ext = self.output_file_path.with_suffix('.out')
# make a snapshot directory # make a snapshot directory
stem = parts[-1] + '_snaps' + self.appendmodelnumber stem = parts[-1] + '_snaps' + self.appendmodelnumber
self.snapshot_dir = Path(*parts[:-2], stem) self.snapshot_dir = Path(*parts[:-2], stem)
inputfilestr_f = '\n--- Model {}/{}, input file: {}' 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) 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 # 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 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 # Add the current model run to namespace that can be accessed by
# user in any Python code blocks in input file # user in any Python code blocks in input file
#self.usernamespace['current_model_run'] = self.i + 1 #self.usernamespace['current_model_run'] = self.i + 1
def get_scene(self): def get_scene(self):
if self.sim_config.scenes: if self.sim_config.scenes:
return self.sim_config.scenes[self.i] return self.sim_config.scenes[self.i]
else: return None else: return None
def get_usernamespace(self): def get_usernamespace(self):
return {'c': c, return {'c': c,
'e0': e0, 'e0': e0,
'm0': m0, 'm0': m0,
'z0': z0, 'z0': z0,
'number_model_runs': self.sim_config.model_end + 1, 'number_model_runs': self.sim_config.model_end + 1,
'current_model_run': self.i + 1, 'current_model_run': self.i + 1,
'inputfile': self.sim_config.input_file_path.resolve()} 'inputfile': self.sim_config.input_file_path.resolve()}
class SimulationConfig: class SimulationConfig:
def __init__(self, args): def __init__(self, args):
"""Adapter for args into Simulation level configuration""" """Adapter for args into Simulation level configuration"""
# adapt the arg properties to link smoothly with MPIRunner(), CPURunner() etc.. # adapt the arg properties to link smoothly with MPIRunner(), CPURunner() etc..
# args.inputfile # args.inputfile
# args.n # args.n
# args.task # args.task
# args.restart # args.restart
# args.mpi # args.mpi
# args.mpi_no_spawn # args.mpi_no_spawn
# args.mpicomm # args.mpicomm
# args.gpu # args.gpu
# args.benchmark # args.benchmark
# args.geometry_only # args.geometry_only
# args.geometry_fixed # args.geometry_fixed
# args.write_processed # args.write_processed
self.args = args self.args = args
self.n_models = args.n self.n_models = args.n
self.inputfile = args.inputfile self.inputfile = args.inputfile
self.gpu = args.gpu self.gpu = args.gpu
self.mpi = args.mpi self.mpi = args.mpi
self.mpi_no_spawn = args.mpi_no_spawn self.mpi_no_spawn = args.mpi_no_spawn
self.general = {} self.general = {}
self.general['messages'] = general['messages'] self.general['messages'] = general['messages']
self.geometry_fixed = args.geometry_fixed self.geometry_fixed = args.geometry_fixed
self.geometry_only = args.geometry_only self.geometry_only = args.geometry_only
self.write_processed = args.write_processed self.write_processed = args.write_processed
# subgrid parameter may not exist if user uses CLI api # subgrid parameter may not exist if user uses CLI api
try: try:
self.subgrid = args.subgrid self.subgrid = args.subgrid
except AttributeError: except AttributeError:
# this must be CLI user. No subgrids are available # this must be CLI user. No subgrids are available
self.subgrid = False self.subgrid = False
# scenes parameter may not exist if user uses CLI api # scenes parameter may not exist if user uses CLI api
try: try:
self.scenes = args.scenes self.scenes = args.scenes
except AttributeError: except AttributeError:
self.scenes = [] self.scenes = []
# set more complex parameters # set more complex parameters
self.set_input_file_path() self.set_input_file_path()
self.set_output_file_path() self.set_output_file_path()
self.set_model_start_end() self.set_model_start_end()
self.set_single_model() self.set_single_model()
def set_single_model(self): def set_single_model(self):
if self.model_start == 0 and self.model_end == 1: if self.model_start == 0 and self.model_end == 1:
self.single_model = True self.single_model = True
else: else:
self.single_model = False self.single_model = False
# for example # for example
def set_model_start_end(self): def set_model_start_end(self):
# set range for number of models to run (internally 0 index) # set range for number of models to run (internally 0 index)
if self.args.task: if self.args.task:
# Job array feeds args.n number of single tasks # Job array feeds args.n number of single tasks
modelstart = self.args.task - 1 modelstart = self.args.task - 1
modelend = self.args.task modelend = self.args.task
elif self.args.restart: elif self.args.restart:
modelstart = self.args.restart - 1 modelstart = self.args.restart - 1
modelend = modelstart + self.args.n - 1 modelend = modelstart + self.args.n - 1
else: else:
modelstart = 0 modelstart = 0
modelend = modelstart + self.args.n modelend = modelstart + self.args.n
self.model_start = modelstart self.model_start = modelstart
self.model_end = modelend self.model_end = modelend
def set_precision(self): def set_precision(self):
pass pass
def set_input_file_path(self): def set_input_file_path(self):
"""Function to set to inputfile path""" """Function to set to inputfile path"""
# if the API is in use an id for the simulation must be provided. # if the API is in use an id for the simulation must be provided.
if self.args.inputfile is None: if self.args.inputfile is None:
self.input_file_path = Path(self.args.outputfile) self.input_file_path = Path(self.args.outputfile)
else: else:
self.input_file_path = Path(self.args.inputfile) self.input_file_path = Path(self.args.inputfile)
def set_output_file_path(self): def set_output_file_path(self):
# output file can be provided by the user. if they havent provided None # output file can be provided by the user. if they havent provided None
# use the inputfilefile path instead # use the inputfilefile path instead
try: try:
self.output_file_path = Path(self.args.outputfile) self.output_file_path = Path(self.args.outputfile)
except AttributeError: except AttributeError:
self.output_file_path = Path(self.args.inputfile) self.output_file_path = Path(self.args.inputfile)
class SimulationConfigMPI(SimulationConfig): class SimulationConfigMPI(SimulationConfig):
def __init__(self, args): def __init__(self, args):
super().__init__(args) super().__init__(args)
def set_model_start_end(self): def set_model_start_end(self):
# Set range for number of models to run # Set range for number of models to run
self.model_start = self.args.restart if self.args.restart else 1 self.model_start = self.args.restart if self.args.restart else 1
self.model_end = self.modelstart + self.args.n self.model_end = self.modelstart + self.args.n
def create_simulation_config(args): def create_simulation_config(args):
if not args.mpi and not args.mpi_no_spawn: if not args.mpi and not args.mpi_no_spawn:
sc = SimulationConfig(args) sc = SimulationConfig(args)
elif args.mpi: elif args.mpi:
sc = SimulationConfigMPI(args) sc = SimulationConfigMPI(args)
return sc return sc
def create_model_config(sim_config, i): def create_model_config(sim_config, i):
mc = ModelConfig(sim_config, i) mc = ModelConfig(sim_config, i)
return mc return mc

查看文件

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

查看文件

@@ -1,281 +1,281 @@
# Copyright (C) 2015-2017: The University of Edinburgh # Copyright (C) 2015-2017: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos # Authors: Craig Warren and Antonis Giannopoulos
# #
# This file is part of gprMax. # This file is part of gprMax.
# #
# gprMax is free software: you can redistribute it and/or modify # gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# gprMax is distributed in the hope that it will be useful, # gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>. # along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import numpy as np import numpy as np
cimport numpy as np cimport numpy as np
from cython.parallel import prange from cython.parallel import prange
cpdef void cython_update_electric_os( cpdef void cython_update_electric_os(
np.float64_t[:, :] updatecoeffsE, np.float64_t[:, :] updatecoeffsE,
np.uint32_t[:, :, :, :] ID, np.uint32_t[:, :, :, :] ID,
int face, int face,
int l_l, int l_l,
int l_u, int l_u,
int m_l, int m_l,
int m_u, int m_u,
size_t n_l, size_t n_l,
size_t n_u, size_t n_u,
int nwn, int nwn,
size_t lookup_id, size_t lookup_id,
np.float64_t[:, :, :] field, np.float64_t[:, :, :] field,
np.float64_t[:, :, :] inc_field, np.float64_t[:, :, :] inc_field,
size_t co, size_t co,
int sign_n, int sign_n,
int sign_f, int sign_f,
int mid, int mid,
int r, int r,
int s, int s,
int nb, int nb,
int nthreads int nthreads
): ):
""" """
Args: Args:
subgrid: (Subgrid) subgrid: (Subgrid)
n: (String) the normal to the face to update n: (String) the normal to the face to update
nwn: (Int) number of working cell in the normal direction nwn: (Int) number of working cell in the normal direction
to the face to the face
lookup_id: (Int) id of the H component we wish to update at lookup_id: (Int) id of the H component we wish to update at
each node each node
field: (Numpy array) main grid field to be updated field: (Numpy array) main grid field to be updated
inc_field: (Numpy array) incident sub_grid field inc_field: (Numpy array) incident sub_grid field
co: (Int) Coefficient used by gprMax update equations which co: (Int) Coefficient used by gprMax update equations which
is specific to the field component being updated. is specific to the field component being updated.
sign_n: (Int) 1 or -1 sign of the incident field on the near face. 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. 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? mid: (Bool) is the H node midway along the lower edge?
r = self.ratio r = self.ratio
s = self.is_os_sep s = self.is_os_sep
nb = self.n_boundary_cells nb = self.n_boundary_cells
""" """
# Comments here as as per left and right face # 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 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 int os
cdef double inc_n, inc_f cdef double inc_n, inc_f
# surface normal index for the subgrid near face h nodes (left i index) # surface normal index for the subgrid near face h nodes (left i index)
n_s_l = nb - s * r - r + r // 2 n_s_l = nb - s * r - r + r // 2
# surface normal index for the subgrid far face h nodes (right i index) # surface normal index for the subgrid far face h nodes (right i index)
n_s_r = nb + nwn + s * r + r // 2 n_s_r = nb + nwn + s * r + r // 2
# OS at the left face # OS at the left face
os = nb - r * s os = nb - r * s
# Iterate over a slice of the main grid using dummy indices # 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): for l in prange(l_l, l_u, nogil=True, schedule='static', num_threads=nthreads):
# Calculate the subgrid j component of the H nodes # Calculate the subgrid j component of the H nodes
# i.e. Hz node of the left or right face # i.e. Hz node of the left or right face
if mid == 1: if mid == 1:
l_s = os + (l - l_l) * r + r // 2 l_s = os + (l - l_l) * r + r // 2
# i.e. the Hy node of the left or right face # i.e. the Hy node of the left or right face
else: else:
l_s = os + (l - l_l) * r l_s = os + (l - l_l) * r
for m in range(m_l, m_u): for m in range(m_l, m_u):
# Calculate the subgrid k component of the H nodes # Calculate the subgrid k component of the H nodes
if mid == 1: if mid == 1:
m_s = os + (m - m_l) * r m_s = os + (m - m_l) * r
else: else:
m_s = os + (m - m_l) * r + r // 2 m_s = os + (m - m_l) * r + r // 2
# left and right # left and right
if face == 2: if face == 2:
# main grid index # main grid index
i0, j0, k0 = n_l, l, m i0, j0, k0 = n_l, l, m
# equivalent subgrid index # equivalent subgrid index
i1, j1, k1 = n_s_l, l_s, m_s i1, j1, k1 = n_s_l, l_s, m_s
i2, j2, k2 = n_u, l, m i2, j2, k2 = n_u, l, m
i3, j3, k3 = n_s_r, l_s, m_s i3, j3, k3 = n_s_r, l_s, m_s
# front and back # front and back
if face == 3: if face == 3:
i0, j0, k0 = l, n_l, m i0, j0, k0 = l, n_l, m
i1, j1, k1 = l_s, n_s_l, m_s i1, j1, k1 = l_s, n_s_l, m_s
i2, j2, k2 = l, n_u, m i2, j2, k2 = l, n_u, m
i3, j3, k3 = l_s, n_s_r, m_s i3, j3, k3 = l_s, n_s_r, m_s
# top bottom # top bottom
if face == 1: if face == 1:
i0, j0, k0 = l, m, n_l i0, j0, k0 = l, m, n_l
i1, j1, k1 = l_s, m_s, n_s_l i1, j1, k1 = l_s, m_s, n_s_l
i2, j2, k2 = l, m, n_u i2, j2, k2 = l, m, n_u
i3, j3, k3 = l_s, m_s, n_s_r i3, j3, k3 = l_s, m_s, n_s_r
# Update the left face # Update the left face
# Get the material at main grid index # Get the material at main grid index
material_e_l = ID[lookup_id, i0, j0, k0] material_e_l = ID[lookup_id, i0, j0, k0]
# Get the associated indident field from the subgrid # Get the associated indident field from the subgrid
inc_n = inc_field[i1, j1, k1] * sign_n inc_n = inc_field[i1, j1, k1] * sign_n
# Update the main grid E field with the corrected H field # Update the main grid E field with the corrected H field
field[i0, j0, k0] += updatecoeffsE[material_e_l, co] * inc_n field[i0, j0, k0] += updatecoeffsE[material_e_l, co] * inc_n
# Update the right face # Update the right face
material_e_r = ID[lookup_id, i2, j2, k2] material_e_r = ID[lookup_id, i2, j2, k2]
inc_f = inc_field[i3, j3, k3] * sign_f inc_f = inc_field[i3, j3, k3] * sign_f
field[i2, j2, k2] += updatecoeffsE[material_e_r, co] * inc_f field[i2, j2, k2] += updatecoeffsE[material_e_r, co] * inc_f
cpdef void cython_update_magnetic_os( cpdef void cython_update_magnetic_os(
np.float64_t[:, :] updatecoeffsH, np.float64_t[:, :] updatecoeffsH,
np.uint32_t[:, :, :, :] ID, np.uint32_t[:, :, :, :] ID,
int face, int face,
int l_l, int l_l,
int l_u, int l_u,
int m_l, int m_l,
int m_u, int m_u,
size_t n_l, size_t n_l,
size_t n_u, size_t n_u,
int nwn, int nwn,
size_t lookup_id, size_t lookup_id,
np.float64_t[:, :, :] field, np.float64_t[:, :, :] field,
np.float64_t[:, :, :] inc_field, np.float64_t[:, :, :] inc_field,
size_t co, size_t co,
int sign_n, int sign_n,
int sign_f, int sign_f,
int mid, int mid,
int r, int r,
int s, int s,
int nb, int nb,
int nthreads int nthreads
): ):
""" """
int r ratio, int r ratio,
int s is_os_sep, int s is_os_sep,
int nb n_boundary_cells 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 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 int os
cdef double inc_n, inc_f cdef double inc_n, inc_f
# i index (normal to os) for the subgrid near face e node # i index (normal to os) for the subgrid near face e node
n_s_l = nb - r * s n_s_l = nb - r * s
# Normal index for the subgrid far face e node # Normal index for the subgrid far face e node
n_s_r = nb + nwn + s * r n_s_r = nb + nwn + s * r
# os inner index for the sub grid # os inner index for the sub grid
os = nb - r * s os = nb - r * s
for l in prange(l_l, l_u, nogil=True, schedule='static', num_threads=nthreads): for l in prange(l_l, l_u, nogil=True, schedule='static', num_threads=nthreads):
# y coord of the Ex field component # y coord of the Ex field component
if mid == 1: if mid == 1:
l_s = os + (l - l_l) * r + r // 2 l_s = os + (l - l_l) * r + r // 2
# y coord of the Ez field component # y coord of the Ez field component
else: else:
l_s = os + (l - l_l) * r l_s = os + (l - l_l) * r
for m in range(m_l, m_u): for m in range(m_l, m_u):
# z coordinate of the Ex node in the subgrid # z coordinate of the Ex node in the subgrid
if mid == 1: if mid == 1:
m_s = os + (m - m_l) * r m_s = os + (m - m_l) * r
else: else:
m_s = os + (m - m_l) * r + r // 2 m_s = os + (m - m_l) * r + r // 2
# associate the given indices with their i, j, k values # associate the given indices with their i, j, k values
# left and right # left and right
if face == 2: if face == 2:
# main grid index # main grid index
i0, j0, k0 = n_l, l, m i0, j0, k0 = n_l, l, m
# equivalent subgrid index # equivalent subgrid index
i1, j1, k1 = n_s_l, l_s, m_s i1, j1, k1 = n_s_l, l_s, m_s
i2, j2, k2 = n_u, l, m i2, j2, k2 = n_u, l, m
i3, j3, k3 = n_s_r, l_s, m_s i3, j3, k3 = n_s_r, l_s, m_s
# front and back # front and back
if face == 3: if face == 3:
i0, j0, k0 = l, n_l, m i0, j0, k0 = l, n_l, m
i1, j1, k1 = l_s, n_s_l, m_s i1, j1, k1 = l_s, n_s_l, m_s
i2, j2, k2 = l, n_u, m i2, j2, k2 = l, n_u, m
i3, j3, k3 = l_s, n_s_r, m_s i3, j3, k3 = l_s, n_s_r, m_s
# top bottom # top bottom
if face == 1: if face == 1:
i0, j0, k0 = l, m, n_l i0, j0, k0 = l, m, n_l
i1, j1, k1 = l_s, m_s, n_s_l i1, j1, k1 = l_s, m_s, n_s_l
i2, j2, k2 = l, m, n_u i2, j2, k2 = l, m, n_u
i3, j3, k3 = l_s, m_s, n_s_r i3, j3, k3 = l_s, m_s, n_s_r
material_e_l = ID[lookup_id, i0, j0, k0] material_e_l = ID[lookup_id, i0, j0, k0]
inc_n = inc_field[i1, j1, k1] * sign_n inc_n = inc_field[i1, j1, k1] * sign_n
# make sure these are the correct grid # make sure these are the correct grid
field[i0, j0, k0] += updatecoeffsH[material_e_l, co] * inc_n field[i0, j0, k0] += updatecoeffsH[material_e_l, co] * inc_n
# Far face # Far face
material_e_r = ID[lookup_id, i2, j2, k2] material_e_r = ID[lookup_id, i2, j2, k2]
inc_f = inc_field[i3, j3, k3] * sign_f inc_f = inc_field[i3, j3, k3] * sign_f
field[i2, j2, k2] += updatecoeffsH[material_e_r, co] * inc_f field[i2, j2, k2] += updatecoeffsH[material_e_r, co] * inc_f
cpdef void cython_update_is( cpdef void cython_update_is(
int nwx, int nwx,
int nwy, int nwy,
int nwz, int nwz,
np.float64_t[:, :] updatecoeffsE, np.float64_t[:, :] updatecoeffsE,
np.uint32_t[:, :, :, :] ID, np.uint32_t[:, :, :, :] ID,
int n, int n,
int offset, int offset,
int nwl, int nwl,
int nwm, int nwm,
int nwn, int nwn,
int face, int face,
np.float64_t[:, :, :] field, np.float64_t[:, :, :] field,
np.float64_t[:, :] inc_field_l, np.float64_t[:, :] inc_field_l,
np.float64_t[:, :] inc_field_u, np.float64_t[:, :] inc_field_u,
Py_ssize_t lookup_id, Py_ssize_t lookup_id,
int sign_l, int sign_l,
int sign_u, int sign_u,
Py_ssize_t co, Py_ssize_t co,
int nthreads 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 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 cdef double inc_l, inc_u, f_l, f_u
# for inner faces H nodes are 1 cell before n boundary cells # for inner faces H nodes are 1 cell before n boundary cells
cdef int n_o = n + offset cdef int n_o = n + offset
for l in prange(n, nwl + n, nogil=True, schedule='static', num_threads=nthreads): for l in prange(n, nwl + n, nogil=True, schedule='static', num_threads=nthreads):
for m in range(n, nwm + n): for m in range(n, nwm + n):
# bottom and top # bottom and top
if face == 1: if face == 1:
i1, j1, k1 = l, m, n_o i1, j1, k1 = l, m, n_o
i2, j2, k2 = l, m, n + nwz i2, j2, k2 = l, m, n + nwz
# left and right # left and right
if face == 2: if face == 2:
i1, j1, k1 = n_o, l, m i1, j1, k1 = n_o, l, m
i2, j2, k2 = n + nwx, l, m i2, j2, k2 = n + nwx, l, m
# front and back # front and back
if face == 3: if face == 3:
i1, j1, k1 = l, n_o, m i1, j1, k1 = l, n_o, m
i2, j2, k2 = l, n + nwy, m i2, j2, k2 = l, n + nwy, m
inc_i = l - n inc_i = l - n
inc_j = m - n inc_j = m - n
field_material_l = ID[lookup_id, i1, j1, k1] field_material_l = ID[lookup_id, i1, j1, k1]
inc_l = inc_field_l[inc_i, inc_j] inc_l = inc_field_l[inc_i, inc_j]
# Additional field at i, j, k # Additional field at i, j, k
f_l = updatecoeffsE[field_material_l, co] * inc_l * sign_l f_l = updatecoeffsE[field_material_l, co] * inc_l * sign_l
# Set the new value # Set the new value
field[i1, j1, k1] += f_l field[i1, j1, k1] += f_l
field_material_u = ID[lookup_id, i2, j2, k2] field_material_u = ID[lookup_id, i2, j2, k2]
inc_u = inc_field_u[inc_i, inc_j] inc_u = inc_field_u[inc_i, inc_j]
# Additional field at i, j, k # Additional field at i, j, k
f_u = updatecoeffsE[field_material_u, co] * inc_u * sign_u f_u = updatecoeffsE[field_material_u, co] * inc_u * sign_u
# Set the new value # Set the new value
field[i2, j2, k2] += f_u field[i2, j2, k2] += f_u

查看文件

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

查看文件

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

查看文件

@@ -1,94 +1,94 @@
# Copyright (C) 2015-2019: The University of Edinburgh # Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos # Authors: Craig Warren and Antonis Giannopoulos
# #
# This file is part of gprMax. # This file is part of gprMax.
# #
# gprMax is free software: you can redistribute it and/or modify # gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# gprMax is distributed in the hope that it will be useful, # gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>. # along with gprMax. If not, see <http://www.gnu.org/licenses/>.
from .config import create_simulation_config from .config import create_simulation_config
from .contexts import create_context from .contexts import create_context
from .solvers import create_solver from .solvers import create_solver
import argparse import argparse
def api( def api(
scenes=None, scenes=None,
id=None, id=None,
inputfile=None, inputfile=None,
outputfile=None, outputfile=None,
n=1, n=1,
task=None, task=None,
restart=None, restart=None,
mpi=False, mpi=False,
mpi_no_spawn=False, mpi_no_spawn=False,
mpicomm=None, mpicomm=None,
gpu=None, gpu=None,
subgrid=None, subgrid=None,
benchmark=False, benchmark=False,
geometry_only=False, geometry_only=False,
geometry_fixed=False, geometry_fixed=False,
write_processed=False, write_processed=False,
): ):
"""If installed as a module this is the entry point.""" """If installed as a module this is the entry point."""
class ImportArguments: class ImportArguments:
pass pass
args = ImportArguments() args = ImportArguments()
args.scenes = scenes args.scenes = scenes
args.inputfile = inputfile args.inputfile = inputfile
args.outputfile = outputfile args.outputfile = outputfile
args.n = n args.n = n
args.task = task args.task = task
args.restart = restart args.restart = restart
args.mpi = mpi args.mpi = mpi
args.mpi_no_spawn = mpi_no_spawn args.mpi_no_spawn = mpi_no_spawn
args.mpicomm = mpicomm args.mpicomm = mpicomm
args.gpu = gpu args.gpu = gpu
args.subgrid=subgrid args.subgrid=subgrid
args.benchmark = benchmark args.benchmark = benchmark
args.geometry_only = geometry_only args.geometry_only = geometry_only
args.geometry_fixed = geometry_fixed args.geometry_fixed = geometry_fixed
args.write_processed = write_processed args.write_processed = write_processed
run_main(args) run_main(args)
def main(): def main():
"""This is the main function for gprMax.""" """This is the main function for gprMax."""
# Parse command line arguments # Parse command line arguments
parser = argparse.ArgumentParser(prog='gprMax', formatter_class=argparse.ArgumentDefaultsHelpFormatter) 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('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('-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('-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('-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', 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-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('--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('-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('-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-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('--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') 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() args = parser.parse_args()
run_main(args) run_main(args)
def run_main(args): def run_main(args):
sim_config = create_simulation_config(args) sim_config = create_simulation_config(args)
context = create_context(sim_config) context = create_context(sim_config)
context.run() context.run()

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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

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

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

查看文件

@@ -1,148 +1,148 @@
from .base import SubGridBase from .base import SubGridBase
from ..cython.fields_updates_hsg import cython_update_is 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_magnetic_os
from ..cython.fields_updates_hsg import cython_update_electric_os from ..cython.fields_updates_hsg import cython_update_electric_os
from ..utilities import human_size from ..utilities import human_size
from colorama import init, Fore, Style from colorama import init, Fore, Style
init() init()
class SubGridHSG(SubGridBase): class SubGridHSG(SubGridBase):
gridtype = '3DSUBGRID' gridtype = '3DSUBGRID'
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.gridtype = SubGridHSG.gridtype self.gridtype = SubGridHSG.gridtype
def update_magnetic_is(self, precursors): def update_magnetic_is(self, precursors):
"""Update the subgrid nodes at the IS with the currents derived """Update the subgrid nodes at the IS with the currents derived
from the main grid. from the main grid.
Args: nwl, nwm, nwn, face, field, inc_field, lookup_id, sign, mod, co Args: nwl, nwm, nwn, face, field, inc_field, lookup_id, sign, mod, co
""" """
# Hz = c0Hz - c1Ey + c2Ex # Hz = c0Hz - c1Ey + c2Ex
# Hy = c0Hy - c3Ex + c1Ez # Hy = c0Hy - c3Ex + c1Ez
# Hx = c0Hx - c2Ez + c3Ey # Hx = c0Hx - c2Ez + c3Ey
# bottom and top # 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, 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) 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 # 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, 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) 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 # 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, 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) 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): def update_electric_is(self, precursors):
# Args: nwl, nwm, nwn, face, field, inc_field, lookup_id, sign, mod, co # Args: nwl, nwm, nwn, face, field, inc_field, lookup_id, sign, mod, co
# Ex = c0(Ex) + c2(dHz) - c3(dHy) # Ex = c0(Ex) + c2(dHz) - c3(dHy)
# Ey = c0(Ey) + c3(dHx) - c1(dHz) # Ey = c0(Ey) + c3(dHx) - c1(dHz)
# Ez = c0(Ez) + c1(dHy) - c2(dHx) # Ez = c0(Ez) + c1(dHy) - c2(dHx)
# bottom and top # 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, 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) 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 # 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, 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) 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 # 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, 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) 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): def update_electric_os(self, main_grid):
i_l = self.i0 - self.is_os_sep i_l = self.i0 - self.is_os_sep
i_u = self.i1 + self.is_os_sep i_u = self.i1 + self.is_os_sep
j_l = self.j0 - self.is_os_sep j_l = self.j0 - self.is_os_sep
j_u = self.j1 + self.is_os_sep j_u = self.j1 + self.is_os_sep
k_l = self.k0 - self.is_os_sep k_l = self.k0 - self.is_os_sep
k_u = self.k1 + 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 # 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 # Form of FDTD update equations for E
# Ex = c0(Ex) + c2(dHz) - c3(dHy) # Ex = c0(Ex) + c2(dHz) - c3(dHy)
# Ey = c0(Ey) + c3(dHx) - c1(dHz) # Ey = c0(Ey) + c3(dHx) - c1(dHz)
# Ez = c0(Ez) + c1(dHy) - c2(dHx) # 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, 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) 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 # 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, 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) 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 # 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, 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) 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): def update_magnetic_os(self, main_grid):
i_l = self.i0 - self.is_os_sep i_l = self.i0 - self.is_os_sep
i_u = self.i1 + self.is_os_sep i_u = self.i1 + self.is_os_sep
j_l = self.j0 - self.is_os_sep j_l = self.j0 - self.is_os_sep
j_u = self.j1 + self.is_os_sep j_u = self.j1 + self.is_os_sep
k_l = self.k0 - self.is_os_sep k_l = self.k0 - self.is_os_sep
k_u = self.k1 + self.is_os_sep k_u = self.k1 + self.is_os_sep
# Form of FDTD update equations for H # Form of FDTD update equations for H
# Hz = c0Hz - c1Ey + c2Ex # Hz = c0Hz - c1Ey + c2Ex
# Hy = c0Hy - c3Ex + c1Ez # Hy = c0Hy - c3Ex + c1Ez
# Hx = c0Hx - c2Ez + c3Ey # 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): # 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 # 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, 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) 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 # 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, 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) 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 # 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, 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) 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): def __str__(self):
self.memory_estimate_basic() self.memory_estimate_basic()
s = '\n' s = '\n'
s += Fore.CYAN s += Fore.CYAN
s += 'Sub Grid HSG\n' s += 'Sub Grid HSG\n'
s += 'Name: {}\n'.format(self.name) s += 'Name: {}\n'.format(self.name)
s += 'dx, dy, dz: {}m {}m {}m\n'.format(self.dx, self.dy, self.dz) s += 'dx, dy, dz: {}m {}m {}m\n'.format(self.dx, self.dy, self.dz)
s += 'dt: {}s\n'.format(self.dt) s += 'dt: {}s\n'.format(self.dt)
s += 'Memory Estimate: {}\n'.format(human_size(self.memoryusage)) s += 'Memory Estimate: {}\n'.format(human_size(self.memoryusage))
s += 'Position: ({}m, {}m, {}m), ({}m, {}m, {}m)\n'.format(self.x1, s += 'Position: ({}m, {}m, {}m), ({}m, {}m, {}m)\n'.format(self.x1,
self.y1, self.y1,
self.z1, self.z1,
self.x2, self.x2,
self.y2, self.y2,
self.z2) 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 += '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 += 'Total Cells: {} {} {}\n'.format(self.nx, self.ny, self.nz)
s += 'Working Region Cells: {} {} {}\n'.format(self.nwx, s += 'Working Region Cells: {} {} {}\n'.format(self.nwx,
self.nwy, self.nwy,
self.nwz) self.nwz)
for h in self.hertziandipoles: for h in self.hertziandipoles:
s += 'Hertizian dipole: {} {} {}\n'.format(h.xcoord, s += 'Hertizian dipole: {} {} {}\n'.format(h.xcoord,
h.ycoord, h.ycoord,
h.zcoord) h.zcoord)
s += str([x for x in self.waveforms s += str([x for x in self.waveforms
if x.ID == h.waveformID][0]) + '\n' if x.ID == h.waveformID][0]) + '\n'
for r in self.rxs: for r in self.rxs:
s += 'Receiver: {} {} {}\n'.format(r.xcoord, r.ycoord, r.zcoord) s += 'Receiver: {} {} {}\n'.format(r.xcoord, r.ycoord, r.zcoord)
for tl in self.transmissionlines: for tl in self.transmissionlines:
s += 'Transmission Line: {} {} {}\n'.format(tl.xcoord, tl.ycoord, tl.zcoord) s += 'Transmission Line: {} {} {}\n'.format(tl.xcoord, tl.ycoord, tl.zcoord)
s += str([x for x in self.waveforms s += str([x for x in self.waveforms
if x.ID == tl.waveformID][0]) + '\n' if x.ID == tl.waveformID][0]) + '\n'
s += Style.RESET_ALL s += Style.RESET_ALL
return s return s

查看文件

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

查看文件

@@ -1,205 +1,195 @@
from .subgrid_hsg import SubGridHSG as SubGridHSGUser from .subgrid_hsg import SubGridHSG as SubGridHSGUser
from .multi import ReferenceRx as ReferenceRxUser from .multi import ReferenceRx as ReferenceRxUser
from ..exceptions import CmdInputError from ..exceptions import CmdInputError
from ..cmds_multiple import UserObjectMulti from ..cmds_multiple import UserObjectMulti
from ..cmds_geometry.cmds_geometry import UserObjectGeometry from ..cmds_geometry.cmds_geometry import UserObjectGeometry
from ..cmds_multiple import Rx from ..cmds_multiple import Rx
from gprMax import config from gprMax import config
from copy import copy from copy import copy
import numpy as np import numpy as np
class SubGridBase(UserObjectMulti): class SubGridBase(UserObjectMulti):
"""Class to allow UserObjectMulti and UserObjectGeometry to be nested in SubGrid type user objects.""" """Class to allow UserObjectMulti and UserObjectGeometry to be nested in SubGrid type user objects."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Constructor.""" """Constructor."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.children_multiple = [] self.children_multiple = []
self.children_geometry = [] self.children_geometry = []
def add(self, node): def add(self, node):
"""Function to add other user objects. Geometry and multi only.""" """Function to add other user objects. Geometry and multi only."""
if isinstance(node, UserObjectMulti): if isinstance(node, UserObjectMulti):
self.children_multiple.append(node) self.children_multiple.append(node)
elif isinstance(node, UserObjectGeometry): elif isinstance(node, UserObjectGeometry):
self.children_geometry.append(node) self.children_geometry.append(node)
else: else:
raise Exception(str(node) + ' This Object can not be added to a sub grid') raise Exception(str(node) + ' This Object can not be added to a sub grid')
def check_filters(self, grid): def check_filters(self, grid):
"""Check the filter of other grids - Only allow filters all on or filters all off.""" """Check the filter of other grids - Only allow filters all on or filters all off."""
if grid.subgrids: if grid.subgrids:
f = grid.subgrids[0] f = grid.subgrids[0]
if f != self.kwargs['filter']: if f != self.kwargs['filter']:
raise CmdInputError(self.__str__() + "Filters should be on or off. Set Filter on or off for all subgrids") raise CmdInputError(self.__str__() + "Filters should be on or off. Set Filter on or off for all subgrids")
def set_discretisation(self, sg, grid): def set_discretisation(self, sg, grid):
"""Set the spatial discretisation.""" """Set the spatial discretisation."""
sg.dx = grid.dx / sg.ratio sg.dx = grid.dx / sg.ratio
sg.dy = grid.dy / sg.ratio sg.dy = grid.dy / sg.ratio
sg.dz = grid.dz / sg.ratio sg.dz = grid.dz / sg.ratio
sg.dl = np.array([sg.dx, sg.dy, sg.dz]) sg.dl = np.array([sg.dx, sg.dy, sg.dz])
def set_main_grid_indices(self, sg, grid, uip, p1, p2): def set_main_grid_indices(self, sg, grid, uip, p1, p2):
"""Set subgrid indices related to main grid placement.""" """Set subgrid indices related to main grid placement."""
# Main grid indices of the sub grid. These are dummy indices. They are # location of the IS
# not user internal except for printing to the user sg.i0, sg.j0, sg.k0 = p1
sg.i0_u, sg.j0_u, sg.k0_u = p1 sg.i1, sg.j1, sg.k1 = p2
sg.i1_u, sg.j1_u, sg.k1_u = p2
sg.x1, sg.y1, sg.z1 = uip.round_to_grid(p1)
# The actual sub gridded area (IS index) is 4 cells in sg.x2, sg.y2, sg.z2 = uip.round_to_grid(p2)
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) def set_name(self, sg):
sg.name = self.kwargs['id']
# Main grid indices of the sub grid. These are dummy indices. They are
# not user internal except for printing to the user def set_working_region_cells(self, sg):
sg.x1_u, sg.y1_u, sg.z1_u = uip.round_to_grid(p1) """Number of cells in each dimension for the working region."""
sg.x2_u, sg.y2_u, sg.z2_u = uip.round_to_grid(p2) sg.nwx = (sg.i1 - sg.i0) * sg.ratio
sg.nwy = (sg.j1 - sg.j0) * sg.ratio
sg.x1, sg.y1, sg.z1 = np.add([sg.x1_u, sg.y1_u, sg.z1_u], sg.is_os_sep * sg.dx) sg.nwz = (sg.k1 - sg.k0) * sg.ratio
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_total_cells(self, sg):
def set_name(self, sg): """Number of cells in each dimension for the whole region."""
sg.name = self.kwargs['id'] sg.nx = 2 * sg.n_boundary_cells_x + sg.nwx
sg.ny = 2 * sg.n_boundary_cells_y + sg.nwy
def set_working_region_cells(self, sg): sg.nz = 2 * sg.n_boundary_cells_z + sg.nwz
"""Number of cells in each dimension for the working region."""
sg.nwx = (sg.i1 - sg.i0) * sg.ratio def set_iterations(self, sg, main):
sg.nwy = (sg.j1 - sg.j0) * sg.ratio """Set number of iterations that will take place in the subgrid."""
sg.nwz = (sg.k1 - sg.k0) * sg.ratio sg.iterations = main.iterations * sg.ratio
def set_total_cells(self, sg): def setup(self, sg, grid, uip):
"""Number of cells in each dimension for the whole region.""" """"Common setup to both all subgrid types."""
sg.nx = 2 * sg.n_boundary_cells_x + sg.nwx p1 = self.kwargs['p1']
sg.ny = 2 * sg.n_boundary_cells_y + sg.nwy p2 = self.kwargs['p2']
sg.nz = 2 * sg.n_boundary_cells_z + sg.nwz
p1, p2 = uip.check_box_points(p1, p2, self.__str__())
def set_iterations(self, sg, main):
"""Set number of iterations that will take place in the subgrid.""" self.check_filters(grid)
sg.iterations = main.iterations * sg.ratio
self.set_discretisation(sg, grid)
def setup(self, sg, grid, uip):
""""Common setup to both all subgrid types.""" # Set the temporal discretisation
p1 = self.kwargs['p1'] sg.calculate_dt()
p2 = self.kwargs['p2']
# ensure stability
p1, p2 = uip.check_box_points(p1, p2, self.__str__()) sg.round_time_step()
self.check_filters(grid) # set the indices related to the subgrids main grid placement
self.set_main_grid_indices(sg, grid, uip, p1, p2)
self.set_discretisation(sg, grid)
"""
# Set the temporal discretisation try:
sg.calculate_dt() uip.check_box_points([sg.i0, sg.j0, sg.k0],
[sg.i1, sg.j1, sg.k1], cmd_str)
# ensure stability except CmdInputError:
sg.round_time_step() es_f = 'The subgrid should extend at least {} cells'
es = es_f.format(sg.is_os_sep * 2)
# set the indices related to the subgrids main grid placement raise CmdInputError(cmd_str, es)
self.set_main_grid_indices(sg, grid, uip, p1, p2) """
""" self.set_working_region_cells(sg)
try: self.set_total_cells(sg)
uip.check_box_points([sg.i0, sg.j0, sg.k0], self.set_iterations(sg, grid)
[sg.i1, sg.j1, sg.k1], cmd_str) self.set_name(sg)
except CmdInputError:
es_f = 'The subgrid should extend at least {} cells' # Copy a reference for the main grid to the sub grid
es = es_f.format(sg.is_os_sep * 2) sg.parent_grid = grid
raise CmdInputError(cmd_str, es)
""" sg.timewindow = grid.timewindow
self.set_working_region_cells(sg) # Copy a subgrid reference to self so that children.create(grid, uip) can access
self.set_total_cells(sg) # the correct grid
self.set_iterations(sg, grid) self.subgrid = sg
self.set_name(sg)
# Copy over built in materials
# Copy a reference for the main grid to the sub grid sg.materials = [copy(m) for m in grid.materials if m.numID in range(0, grid.n_built_in_materials + 1)]
sg.parent_grid = grid # use same number of threads
sg.nthreads = grid.nthreads
sg.timewindow = grid.timewindow
# Dont mix and match different subgrids
# Copy a subgrid reference to self so that children.create(grid, uip) can access for sg_made in grid.subgrids:
# the correct grid if type(sg) != type(sg_made):
self.subgrid = sg raise CmdInputError(self.__str__() + ' Please only use one type of subgrid')
# Copy over built in materials # Reference the sub grid under the main grid to which it belongs.
sg.materials = [copy(m) for m in grid.materials if m.numID in range(0, grid.n_built_in_materials + 1)] grid.subgrids.append(sg)
# use same number of threads
sg.nthreads = grid.nthreads
class SubGridHSG(SubGridBase):
# Dont mix and match different subgrids """HSG User Object."""
for sg_made in grid.subgrids: def __init__(self,
if type(sg) != type(sg_made): p1=None,
raise CmdInputError(self.__str__() + ' Please only use one type of subgrid') p2=None,
ratio=3,
# Reference the sub grid under the main grid to which it belongs. ID='',
grid.subgrids.append(sg) is_os_sep=3,
pml_separation=4,
subgrid_pml_thickness=6,
class SubGridHSG(SubGridBase): interpolation='linear',
"""HSG User Object.""" loss_mechanism=False,
def __init__(self, loss_factor=False,
p1=None, filter=True,
p2=None, **kwargs):
ratio=3, """Constructor."""
ID='',
is_os_sep=3, pml_separation = ratio // 2 + 2
pml_separation=4,
subgrid_pml_thickness=6, # copy over the optional parameters
interpolation='linear', kwargs['p1'] = p1
loss_mechanism=False, kwargs['p2'] = p2
loss_factor=False, kwargs['ratio'] = ratio
filter=True, kwargs['ID'] = ID
**kwargs): kwargs['is_os_sep'] = is_os_sep
"""Constructor.""" kwargs['pml_separation'] = pml_separation
kwargs['subgrid_pml_thickness'] = subgrid_pml_thickness
pml_separation = ratio // 2 + 2 kwargs['interpolation'] = interpolation
kwargs['filter'] = filter
# copy over the optional parameters kwargs['loss_mechanism'] = loss_mechanism
kwargs['p1'] = p1 kwargs['loss_factor'] = loss_factor
kwargs['p2'] = p2
kwargs['ratio'] = ratio super().__init__(**kwargs)
kwargs['ID'] = ID self.order = 18
kwargs['is_os_sep'] = is_os_sep self.hash = '#subgrid_hsg'
kwargs['pml_separation'] = pml_separation
kwargs['subgrid_pml_thickness'] = subgrid_pml_thickness def create(self, grid, uip):
kwargs['interpolation'] = interpolation sg = SubGridHSGUser(**self.kwargs)
kwargs['filter'] = filter self.setup(sg, grid, uip)
kwargs['loss_mechanism'] = loss_mechanism if config.is_messages():
kwargs['loss_factor'] = loss_factor print(sg)
return sg
super().__init__(**kwargs)
self.order = 18
self.hash = '#subgrid_hsg'
class ReferenceRx(Rx):
def create(self, grid, uip): """ReferenceRx User Object."""
sg = SubGridHSGUser(**self.kwargs)
self.setup(sg, grid, uip) def __init__(self, **kwargs):
if config.is_messages(): super().__init__(**kwargs)
print(sg) self.hash = '#rx_reference'
return sg self.constructor = ReferenceRxUser
def create(self, grid, uip):
class ReferenceRx(Rx): r = super().create(grid, uip)
"""ReferenceRx User Object."""
try:
def __init__(self, **kwargs): ratio = self.kwargs['ratio']
super().__init__(**kwargs) r.ratio = ratio
self.hash = '#rx_reference' r.offset = ratio // 2
self.constructor = ReferenceRxUser
except KeyError:
def create(self, grid, uip): raise CmdInputError("'{}' has an no ratio parameter".format(self.__str__()))
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 # Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos # Authors: Craig Warren and Antonis Giannopoulos
# #
# This file is part of gprMax. # This file is part of gprMax.
# #
# gprMax is free software: you can redistribute it and/or modify # gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# gprMax is distributed in the hope that it will be useful, # gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>. # along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import numpy as np import numpy as np
cimport numpy as np cimport numpy as np
from cython.parallel import prange from cython.parallel import prange
cdef extern from "complex.h" nogil: cdef extern from "complex.h" nogil:
double creal(double complex z) double creal(double complex z)
float crealf(float complex z) float crealf(float complex z)
############################################################### ###############################################################
# Electric field updates - dispersive materials - multipole A # # Electric field updates - dispersive materials - multipole A #
############################################################### ###############################################################
{% for item in functions %} {% for item in functions %}
cpdef void {{ item.name_a }}( cpdef void {{ item.name_a }}(
int nx, int nx,
int ny, int ny,
int nz, int nz,
int nthreads, int nthreads,
int maxpoles, int maxpoles,
{{ item.field_type }}[:, ::1] updatecoeffsE, {{ item.field_type }}[:, ::1] updatecoeffsE,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive, {{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID, np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx, {{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty, {{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz, {{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex, {{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey, {{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez, {{ item.field_type }}[:, :, ::1] Ez,
{{ item.field_type }}[:, :, ::1] Hx, {{ item.field_type }}[:, :, ::1] Hx,
{{ item.field_type }}[:, :, ::1] Hy, {{ item.field_type }}[:, :, ::1] Hy,
{{ item.field_type }}[:, :, ::1] Hz {{ item.field_type }}[:, :, ::1] Hz
): ):
"""This function updates the electric field components when dispersive materials (with multiple poles) are present. """This function updates the electric field components when dispersive materials (with multiple poles) are present.
Args: Args:
nx, ny, nz (int): Grid size in cells nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays 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 Py_ssize_t i, j, k, pole
cdef int material cdef int material
cdef float phi = 0 cdef float phi = 0
# Ex component # Ex component
if ny != 1 or nz != 1: if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny): for j in range(1, ny):
for k in range(1, nz): for k in range(1, nz):
material = ID[0, i, j, k] material = ID[0, i, j, k]
phi = 0 phi = 0
for pole in range(maxpoles): for pole in range(maxpoles):
{% if 'complex' in item.dispersive_type %} {% if 'complex' in item.dispersive_type %}
phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tx[pole, i, j, k]) phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tx[pole, i, j, k])
{% else %} {% else %}
phi = phi + updatecoeffsdispersive[material, pole * 3] * Tx[pole, i, j, k] phi = phi + updatecoeffsdispersive[material, pole * 3] * Tx[pole, i, j, k]
{% endif %} {% 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] 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 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 # Ey component
if nx != 1 or nz != 1: if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny): for j in range(0, ny):
for k in range(1, nz): for k in range(1, nz):
material = ID[1, i, j, k] material = ID[1, i, j, k]
phi = 0 phi = 0
for pole in range(maxpoles): for pole in range(maxpoles):
{% if 'complex' in item.dispersive_type %} {% if 'complex' in item.dispersive_type %}
phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Ty[pole, i, j, k]) phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Ty[pole, i, j, k])
{% else %} {% else %}
phi = phi + updatecoeffsdispersive[material, pole * 3] * Ty[pole, i, j, k] phi = phi + updatecoeffsdispersive[material, pole * 3] * Ty[pole, i, j, k]
{% endif %} {% 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] 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 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 # Ez component
if nx != 1 or ny != 1: if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny): for j in range(1, ny):
for k in range(0, nz): for k in range(0, nz):
material = ID[2, i, j, k] material = ID[2, i, j, k]
phi = 0 phi = 0
for pole in range(maxpoles): for pole in range(maxpoles):
{% if 'complex' in item.dispersive_type %} {% if 'complex' in item.dispersive_type %}
phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tz[pole, i, j, k]) phi = phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tz[pole, i, j, k])
{% else %} {% else %}
phi = phi + updatecoeffsdispersive[material, pole * 3] * Tz[pole, i, j, k] phi = phi + updatecoeffsdispersive[material, pole * 3] * Tz[pole, i, j, k]
{% endif %} {% 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] 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 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 %} {% endfor %}
############################################################### ###############################################################
# Electric field updates - dispersive materials - multipole B # # Electric field updates - dispersive materials - multipole B #
############################################################### ###############################################################
{% for item in functions %} {% for item in functions %}
cpdef void {{ item.name_b }}( cpdef void {{ item.name_b }}(
int nx, int nx,
int ny, int ny,
int nz, int nz,
int nthreads, int nthreads,
int maxpoles, int maxpoles,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive, {{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID, np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx, {{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty, {{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz, {{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex, {{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey, {{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez {{ item.field_type }}[:, :, ::1] Ez
): ):
"""This function updates a temporary dispersive material array when disperisive materials (with multiple poles) are present. """This function updates a temporary dispersive material array when disperisive materials (with multiple poles) are present.
Args: Args:
nx, ny, nz (int): Grid size in cells nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays
""" """
cdef Py_ssize_t i, j, k, pole cdef Py_ssize_t i, j, k, pole
cdef int material cdef int material
# Ex component # Ex component
if ny != 1 or nz != 1: if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny): for j in range(1, ny):
for k in range(1, nz): for k in range(1, nz):
material = ID[0, i, j, k] material = ID[0, i, j, k]
for pole in range(maxpoles): for pole in range(maxpoles):
Tx[pole, i, j, k] = Tx[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k] Tx[pole, i, j, k] = Tx[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k]
# Ey component # Ey component
if nx != 1 or nz != 1: if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny): for j in range(0, ny):
for k in range(1, nz): for k in range(1, nz):
material = ID[1, i, j, k] material = ID[1, i, j, k]
for pole in range(maxpoles): for pole in range(maxpoles):
Ty[pole, i, j, k] = Ty[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k] Ty[pole, i, j, k] = Ty[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k]
# Ez component # Ez component
if nx != 1 or ny != 1: if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny): for j in range(1, ny):
for k in range(0, nz): for k in range(0, nz):
material = ID[2, i, j, k] material = ID[2, i, j, k]
for pole in range(maxpoles): for pole in range(maxpoles):
Tz[pole, i, j, k] = Tz[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k] Tz[pole, i, j, k] = Tz[pole, i, j, k] - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k]
{% endfor %} {% endfor %}
################################################################# #################################################################
# Electric field updates - dispersive materials - single pole A # # Electric field updates - dispersive materials - single pole A #
################################################################# #################################################################
{% for item in functions %} {% for item in functions %}
cpdef void {{ item.name_a_1 }}( cpdef void {{ item.name_a_1 }}(
int nx, int nx,
int ny, int ny,
int nz, int nz,
int nthreads, int nthreads,
int maxpoles, int maxpoles,
{{ item.field_type }}[:, ::1] updatecoeffsE, {{ item.field_type }}[:, ::1] updatecoeffsE,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive, {{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID, np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx, {{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty, {{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz, {{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex, {{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey, {{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez, {{ item.field_type }}[:, :, ::1] Ez,
{{ item.field_type }}[:, :, ::1] Hx, {{ item.field_type }}[:, :, ::1] Hx,
{{ item.field_type }}[:, :, ::1] Hy, {{ item.field_type }}[:, :, ::1] Hy,
{{ item.field_type }}[:, :, ::1] Hz {{ item.field_type }}[:, :, ::1] Hz
): ):
"""This function updates the electric field components when dispersive materials (with 1 pole) are present. """This function updates the electric field components when dispersive materials (with 1 pole) are present.
Args: Args:
nx, ny, nz (int): Grid size in cells nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays updatecoeffs, T, ID, E, H (memoryviews): Access to update coeffients, temporary, ID and field component arrays
""" """
cdef Py_ssize_t i, j, k cdef Py_ssize_t i, j, k
cdef int material cdef int material
cdef float phi = 0 cdef float phi = 0
# Ex component # Ex component
if ny != 1 or nz != 1: if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny): for j in range(1, ny):
for k in range(1, nz): for k in range(1, nz):
material = ID[0, i, j, k] material = ID[0, i, j, k]
{% if 'complex' in item.dispersive_type %} {% if 'complex' in item.dispersive_type %}
phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tx[0, i, j, k]) phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tx[0, i, j, k])
{% else %} {% else %}
phi = updatecoeffsdispersive[material, 0] * Tx[0, i, j, k] phi = updatecoeffsdispersive[material, 0] * Tx[0, i, j, k]
{% endif %} {% endif %}
Tx[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tx[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ex[i, j, k] Tx[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tx[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ex[i, j, k]
Ex[i, j, k] = updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi 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 # Ey component
if nx != 1 or nz != 1: if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny): for j in range(0, ny):
for k in range(1, nz): for k in range(1, nz):
material = ID[1, i, j, k] material = ID[1, i, j, k]
{% if 'complex' in item.dispersive_type %} {% if 'complex' in item.dispersive_type %}
phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Ty[0, i, j, k]) phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Ty[0, i, j, k])
{% else %} {% else %}
phi = updatecoeffsdispersive[material, 0] * Ty[0, i, j, k] phi = updatecoeffsdispersive[material, 0] * Ty[0, i, j, k]
{% endif %} {% endif %}
Ty[0, i, j, k] = updatecoeffsdispersive[material, 1] * Ty[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ey[i, j, k] Ty[0, i, j, k] = updatecoeffsdispersive[material, 1] * Ty[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ey[i, j, k]
Ey[i, j, k] = updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi 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 # Ez component
if nx != 1 or ny != 1: if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny): for j in range(1, ny):
for k in range(0, nz): for k in range(0, nz):
material = ID[2, i, j, k] material = ID[2, i, j, k]
{% if 'complex' in item.dispersive_type %} {% if 'complex' in item.dispersive_type %}
phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tz[0, i, j, k]) phi = {{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tz[0, i, j, k])
{% else %} {% else %}
phi = updatecoeffsdispersive[material, 0] * Tz[0, i, j, k] phi = updatecoeffsdispersive[material, 0] * Tz[0, i, j, k]
{% endif %} {% endif %}
Tz[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tz[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ez[i, j, k] Tz[0, i, j, k] = updatecoeffsdispersive[material, 1] * Tz[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ez[i, j, k]
Ez[i, j, k] = updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi 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 %} {% endfor %}
################################################################# #################################################################
# Electric field updates - dispersive materials - single pole B # # Electric field updates - dispersive materials - single pole B #
################################################################# #################################################################
{% for item in functions %} {% for item in functions %}
cpdef void {{ item.name_b_1 }}( cpdef void {{ item.name_b_1 }}(
int nx, int nx,
int ny, int ny,
int nz, int nz,
int nthreads, int nthreads,
int maxpoles, int maxpoles,
{{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive, {{ item.dispersive_type }}[:, ::1] updatecoeffsdispersive,
np.uint32_t[:, :, :, ::1] ID, np.uint32_t[:, :, :, ::1] ID,
{{ item.dispersive_type }}[:, :, :, ::1] Tx, {{ item.dispersive_type }}[:, :, :, ::1] Tx,
{{ item.dispersive_type }}[:, :, :, ::1] Ty, {{ item.dispersive_type }}[:, :, :, ::1] Ty,
{{ item.dispersive_type }}[:, :, :, ::1] Tz, {{ item.dispersive_type }}[:, :, :, ::1] Tz,
{{ item.field_type }}[:, :, ::1] Ex, {{ item.field_type }}[:, :, ::1] Ex,
{{ item.field_type }}[:, :, ::1] Ey, {{ item.field_type }}[:, :, ::1] Ey,
{{ item.field_type }}[:, :, ::1] Ez {{ item.field_type }}[:, :, ::1] Ez
): ):
"""This function updates a temporary dispersive material array when disperisive materials (with 1 pole) are present. """This function updates a temporary dispersive material array when disperisive materials (with 1 pole) are present.
Args: Args:
nx, ny, nz (int): Grid size in cells nx, ny, nz (int): Grid size in cells
nthreads (int): Number of threads to use nthreads (int): Number of threads to use
maxpoles (int): Maximum number of poles maxpoles (int): Maximum number of poles
updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays updatecoeffs, T, ID, E (memoryviews): Access to update coeffients, temporary, ID and field component arrays
""" """
cdef Py_ssize_t i, j, k cdef Py_ssize_t i, j, k
cdef int material cdef int material
# Ex component # Ex component
if ny != 1 or nz != 1: if ny != 1 or nz != 1:
for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(0, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny): for j in range(1, ny):
for k in range(1, nz): for k in range(1, nz):
material = ID[0, i, j, k] material = ID[0, i, j, k]
Tx[0, i, j, k] = Tx[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ex[i, j, k] Tx[0, i, j, k] = Tx[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ex[i, j, k]
# Ey component # Ey component
if nx != 1 or nz != 1: if nx != 1 or nz != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(0, ny): for j in range(0, ny):
for k in range(1, nz): for k in range(1, nz):
material = ID[1, i, j, k] material = ID[1, i, j, k]
Ty[0, i, j, k] = Ty[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ey[i, j, k] Ty[0, i, j, k] = Ty[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ey[i, j, k]
# Ez component # Ez component
if nx != 1 or ny != 1: if nx != 1 or ny != 1:
for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads):
for j in range(1, ny): for j in range(1, ny):
for k in range(0, nz): for k in range(0, nz):
material = ID[2, i, j, k] material = ID[2, i, j, k]
Tz[0, i, j, k] = Tz[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ez[i, j, k] Tz[0, i, j, k] = Tz[0, i, j, k] - updatecoeffsdispersive[material, 2] * Ez[i, j, k]
{% endfor %} {% endfor %}

查看文件

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

查看文件

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

540
setup.py
查看文件

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