Started implementing new config structure.

这个提交包含在:
Craig Warren
2019-10-11 17:35:14 +01:00
父节点 c2042af1ad
当前提交 0359cef4d6
共有 28 个文件被更改,包括 315 次插入360 次删除

查看文件

@@ -194,8 +194,4 @@ class AddGrass(UserObjectGeometry):
volume.fractalsurfaces.append(surface)
log.info(f'{n_blades} blades of grass on surface from {xs * grid.dx:g}m, \
{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, \
{yf * grid.dy:g}m, {zf * grid.dz:g}m with fractal dimension \
{surface.dimension:g}, fractal seeding {surface.seed}, and range \
{limits[0]:g}m to {limits[1]:g}m, added to {surface.operatingonID}.')
log.info(f'{n_blades} blades of grass on surface from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m with fractal dimension {surface.dimension:g}, fractal seeding {surface.seed}, and range {limits[0]:g}m to {limits[1]:g}m, added to {surface.operatingonID}.')

查看文件

@@ -159,9 +159,4 @@ class AddSurfaceRoughness(UserObjectGeometry):
surface.generate_fractal_surface(grid)
volume.fractalsurfaces.append(surface)
log.info(f'Fractal surface from {xs * grid.dx:g}m, {ys * grid.dy:g}m, \
{zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, \
{zf * grid.dz:g}m with fractal dimension {surface.dimension:g}, \
fractal weightings {surface.weighting[0]:g}, {surface.weighting[1]:g}, \
fractal seeding {surface.seed}, and range {limits[0]:g}m to \
{limits[1]:g}m, added to {surface.operatingonID}.')
log.info(f'Fractal surface from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m with fractal dimension {surface.dimension:g}, fractal weightings {surface.weighting[0]:g}, {surface.weighting[1]:g}, fractal seeding {surface.seed}, and range {limits[0]:g}m to {limits[1]:g}m, added to {surface.operatingonID}.')

查看文件

@@ -143,7 +143,4 @@ class AddSurfaceWater(UserObjectGeometry):
if testwater:
raise CmdInputError(self.__str__() + ' requires the time step for the model to be less than the relaxation time required to model water.')
log.info(f'Water on surface from {xs * grid.dx:g}m, {ys * grid.dy:g}m, \
{zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, \
{zf * grid.dz:g}m with depth {filldepth:g}m, added to \
{surface.operatingonID}.')
log.info(f'Water on surface from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m with depth {filldepth:g}m, added to {surface.operatingonID}.')

查看文件

@@ -117,7 +117,4 @@ class Box(UserObjectGeometry):
build_box(xs, xf, ys, yf, zs, zf, numID, numIDx, numIDy, numIDz, averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID)
dielectricsmoothing = 'on' if averaging else 'off'
log.info(f"Box from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, \
to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m of \
material(s) {', '.join(materialsrequested)} created, dielectric \
smoothing is {dielectricsmoothing}.")
log.info(f"Box from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m of material(s) {', '.join(materialsrequested)} created, dielectric smoothing is {dielectricsmoothing}.")

查看文件

@@ -122,6 +122,4 @@ class Cylinder(UserObjectGeometry):
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)
dielectricsmoothing = 'on' if averaging else 'off'
log.info(f"Cylinder with face centres {x1:g}m, {y1:g}m, {z1:g}m and {x2:g}m, \
{y2:g}m, {z2:g}m, with radius {r:g}m, of material(s) {', '.join(materialsrequested)} \
created, dielectric smoothing is {dielectricsmoothing}.")
log.info(f"Cylinder with face centres {x1:g}m, {y1:g}m, {z1:g}m and {x2:g}m, {y2:g}m, {z2:g}m, with radius {r:g}m, of material(s) {', '.join(materialsrequested)} created, dielectric smoothing is {dielectricsmoothing}.")

查看文件

@@ -172,14 +172,6 @@ class CylindricalSector(UserObjectGeometry):
if thickness > 0:
dielectricsmoothing = 'on' if averaging else 'off'
log.info(f"Cylindrical sector with centre {ctr1:g}m, {ctr2:g}m, \
radius {r:g}m, starting angle {(sectorstartangle / (2 * np.pi)) * 360:.1f} \
degrees, sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} \
degrees, thickness {thickness:g}m, of material(s) \
{', '.join(materialsrequested)} created, dielectric smoothing \
is {dielectricsmoothing}.")
log.info(f"Cylindrical sector with centre {ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle {(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} degrees, thickness {thickness:g}m, of material(s) {', '.join(materialsrequested)} created, dielectric smoothing is {dielectricsmoothing}.")
else:
log.info(f"Cylindrical sector with centre {ctr1:g}m, {ctr2:g}m, \
radius {r:g}m, starting angle {(sectorstartangle / (2 * np.pi)) * 360:.1f} \
degrees, sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} \
degrees, of material(s) {', '.join(materialsrequested)} created.")
log.info(f"Cylindrical sector with centre {ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle {(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} degrees, of material(s) {', '.join(materialsrequested)} created.")

查看文件

@@ -87,6 +87,4 @@ class Edge(UserObjectGeometry):
for k in range(zs, zf):
build_edge_z(xs, ys, k, material.numID, grid.rigidE, grid.rigidH, grid.ID)
log.info(f'Edge from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, \
to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m of \
material {material_id} created.')
log.info(f'Edge from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m of material {material_id} created.')

查看文件

@@ -122,11 +122,6 @@ class FractalBox(UserObjectGeometry):
volume.mixingmodel = mixingmodel
dielectricsmoothing = 'on' if volume.averaging else 'off'
log.info(f'Fractal box {volume.ID} from {xs * grid.dx:g}m, {ys * grid.dy:g}m, \
{zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m \
with {volume.operatingonID}, fractal dimension {volume.dimension:g}, fractal \
weightings {volume.weighting[0]:g}, {volume.weighting[1]:g}, {volume.weighting[2]:g}, \
fractal seeding {volume.seed}, with {volume.nbins} material(s) created, \
dielectric smoothing is {dielectricsmoothing}.')
log.info(f'Fractal box {volume.ID} from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m with {volume.operatingonID}, fractal dimension {volume.dimension:g}, fractal weightings {volume.weighting[0]:g}, {volume.weighting[1]:g}, {volume.weighting[2]:g}, fractal seeding {volume.seed}, with {volume.nbins} material(s) created, dielectric smoothing is {dielectricsmoothing}.')
grid.fractalvolumes.append(volume)

查看文件

@@ -18,14 +18,12 @@
import numpy as np
import gprMax.config as config
from .cmds_geometry import UserObjectGeometry
from ..config import dtypes
from ..cython.geometry_primitives import build_voxels_from_array
from ..cython.geometry_primitives import build_voxels_from_array_mask
from ..exceptions import CmdInputError
floattype = dtypes['float_or_double']
class FractalBoxBuilder(UserObjectGeometry):
"""Internal class for fractal box modifications. This class should be used
@@ -75,7 +73,7 @@ class FractalBoxBuilder(UserObjectGeometry):
# If there is only 1 bin then a normal material is being used, otherwise a mixing model
if volume.nbins == 1:
volume.fractalvolume = np.ones((volume.nx, volume.ny, volume.nz), dtype=floattype)
volume.fractalvolume = np.ones((volume.nx, volume.ny, volume.nz), dtype=config.sim_config.dtype['float_or_double'])
materialnumID = next(x.numID for x in grid.materials if x.ID == volume.operatingonID)
volume.fractalvolume *= materialnumID
else:

查看文件

@@ -107,6 +107,4 @@ class GeometryObjectsRead(UserObjectGeometry):
except KeyError:
averaging = False
build_voxels_from_array(xs, ys, zs, numexistmaterials, averaging, data, G.solid, G.rigidE, G.rigidH, G.ID)
log.info(f'Geometry objects from file (voxels only){ geofile} \
inserted at {xs * G.dx:g}m, {ys * G.dy:g}m, {zs * G.dz:g}m, \
with corresponding materials file {matfile}.')
log.info(f'Geometry objects from file (voxels only){geofile} inserted at {xs * G.dx:g}m, {ys * G.dy:g}m, {zs * G.dz:g}m, with corresponding materials file {matfile}.')

查看文件

@@ -135,6 +135,4 @@ class Plate(UserObjectGeometry):
for j in range(ys, yf):
build_face_xy(i, j, zs, numIDx, numIDy, grid.rigidE, grid.rigidH, grid.ID)
log.info(f"Plate from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, \
to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m of \
material(s) {', '.join(materialsrequested)} created.")
log.info(f"Plate from {xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m of material(s) {', '.join(materialsrequested)} created.")

查看文件

@@ -116,6 +116,4 @@ class Sphere(UserObjectGeometry):
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)
dielectricsmoothing = 'on' if averaging else 'off'
log.info(f"Sphere with centre {xc * grid.dx:g}m, {yc * grid.dy:g}m, {zc * grid.dz:g}m, \
radius {r:g}m, of material(s) {', '.join(materialsrequested)} \
created, dielectric smoothing is {dielectricsmoothing}.")
log.info(f"Sphere with centre {xc * grid.dx:g}m, {yc * grid.dy:g}m, {zc * grid.dz:g}m, radius {r:g}m, of material(s) {', '.join(materialsrequested)} created, dielectric smoothing is {dielectricsmoothing}.")

查看文件

@@ -157,11 +157,6 @@ class Triangle(UserObjectGeometry):
if thickness > 0:
dielectricsmoothing = 'on' if averaging else 'off'
log.info(f"Triangle with coordinates {x1:g}m {y1:g}m {z1:g}m, \
{x2:g}m {y2:g}m {z2:g}m, {x3:g}m {y3:g}m {z3:g}m and thickness \
{thickness:g}m of material(s) {', '.join(materialsrequested)} \
created, dielectric smoothing is {dielectricsmoothing}.")
log.info(f"Triangle with coordinates {x1:g}m {y1:g}m {z1:g}m, {x2:g}m {y2:g}m {z2:g}m, {x3:g}m {y3:g}m {z3:g}m and thickness {thickness:g}m of material(s) {', '.join(materialsrequested)} created, dielectric smoothing is {dielectricsmoothing}.")
else:
log.info(f"Triangle with coordinates {x1:g}m {y1:g}m {z1:g}m, \
{x2:g}m {y2:g}m {z2:g}m, {x3:g}m {y3:g}m {z3:g}m of \
material(s) {', '.join(materialsrequested)} created.")
log.info(f"Triangle with coordinates {x1:g}m {y1:g}m {z1:g}m, {x2:g}m {y2:g}m {z2:g}m, {x3:g}m {y3:g}m {z3:g}m of material(s) {', '.join(materialsrequested)} created.")

查看文件

@@ -196,7 +196,7 @@ class VoltageSource(UserObjectMulti):
v.calculate_waveform_values(grid)
log.info('Voltage source with polarity {} at {:g}m, {:g}m, {:g}m, resistance {:.1f} Ohms,'.format(v.polarisation, v.xcoord * grid.dx, v.ycoord * grid.dy, v.zcoord * grid.dz, v.resistance) + startstop + 'using waveform {} created.'.format(v.waveformID))
log.info(f'Voltage source with polarity {v.polarisation} at {v.xcoord * grid.dx:g}m, {v.ycoord * grid.dy:g}m, {v.zcoord * grid.dz:g}m, resistance {v.resistance:.1f} Ohms,' + startstop + f'using waveform {v.waveformID} created.')
grid.voltagesources.append(v)
@@ -524,7 +524,7 @@ class Rx(UserObjectMulti):
except KeyError:
r.ID = r.__class__.__name__ + '(' + str(r.xcoord) + ',' + str(r.ycoord) + ',' + str(r.zcoord) + ')'
for key in RxUser.defaultoutputs:
r.outputs[key] = np.zeros(grid.iterations, dtype=config.dtypes['float_or_double'])
r.outputs[key] = np.zeros(grid.iterations, dtype=config.sim_config.dtypes['float_or_double'])
log.info(f"Receiver at {r.xcoord * grid.dx:g}m, {r.ycoord * grid.dy:g}m, {r.zcoord * grid.dz:g}m with output component(s) {', '.join(r.outputs)} created.")
@@ -1189,3 +1189,4 @@ class SubgridHSG(UserObjectMulti):
def __init__(self, **kwargs):
super().__init__(**kwargs)
log.debug('Is this required?')

查看文件

@@ -122,7 +122,7 @@ class Domain(UserObjectSingle):
G.nthreads = set_openmp_threads()
log.info(f'Number of CPU (OpenMP) threads: {G.nthreads}')
if G.nthreads > config.hostinfo['physicalcores']:
if G.nthreads > config.sim_config.hostinfo['physicalcores']:
log.warning(Fore.RED + f"You have specified more threads ({G.nthreads}) \
than available physical CPU cores ({config.hostinfo['physicalcores']}). \
This may lead to degraded performance." + Style.RESET_ALL)
@@ -231,7 +231,7 @@ class Messages(UserObjectSingle):
try:
s = '#messages: {}'.format(self.kwargs['yn'])
except KeyError:
log.info('messages problem')
log.exception('messages problem')
def create(self, G, uip):
try:
@@ -266,8 +266,7 @@ class Title(UserObjectSingle):
except KeyError:
pass
if config.is_messages():
log.info('Model title: {}'.format(G.title))
log.info(f'Model title: {G.title}')
class NumThreads(UserObjectSingle):
@@ -302,15 +301,16 @@ class NumThreads(UserObjectSingle):
G.nthreads = n
os.environ['OMP_NUM_THREADS'] = str(G.nthreads)
if config.is_messages():
log.info('Number of CPU (OpenMP) threads: {}'.format(G.nthreads))
log.info(f'Number of CPU (OpenMP) threads: {G.nthreads}')
if G.nthreads > config.hostinfo['physicalcores']:
log.info(Fore.RED + 'WARNING: You have specified more threads ({}) than available physical CPU cores ({}). This may lead to degraded performance.'.format(G.nthreads, config.hostinfo['physicalcores']) + Style.RESET_ALL)
log.warning(Fore.RED + f"You have specified more threads ({G.nthreads}) \
than available physical CPU cores (\
{config.sim_config.hostinfo['physicalcores']}). \
This may lead to degraded performance." + Style.RESET_ALL)
# Print information about any GPU in use
if config.is_messages():
if G.gpu is not None:
log.info('GPU solving using: {} - {}'.format(G.gpu.deviceID, G.gpu.name))
if G.gpu is not None:
log.info(f'GPU solving using: {G.gpu.deviceID} - {G.gpu.name}')
class TimeStepStabilityFactor(UserObjectSingle):
@@ -326,7 +326,7 @@ class TimeStepStabilityFactor(UserObjectSingle):
def __str__(self):
try:
return '#time_step_stability_factor: {}'.format(self.kwargs['f'])
return f"#time_step_stability_factor: {self.kwargs['f']}"
except KeyError:
return '#time_step_stability_factor:'
@@ -339,8 +339,7 @@ class TimeStepStabilityFactor(UserObjectSingle):
if f <= 0 or f > 1:
raise CmdInputError(self.__str__() + ' requires the value of the time step stability factor to be between zero and one')
G.dt = G.dt * f
if config.is_messages():
log.info('Time step (modified): {:g} secs'.format(G.dt))
log.info(f'Time step (modified): {G.dt:g} secs')
class PMLCells(UserObjectSingle):
@@ -412,8 +411,9 @@ class SrcSteps(UserObjectSingle):
except KeyError:
raise CmdInputError('#src_steps: requires exactly three parameters')
if config.is_messages():
log.info('Simple sources will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.srcsteps[0] * G.dx, G.srcsteps[1] * G.dy, G.srcsteps[2] * G.dz))
log.info(f'Simple sources will step {G.srcsteps[0] * G.dx:g}m, \
{G.srcsteps[1] * G.dy:g}m, {G.srcsteps[2] * G.dz:g}m \
for each model run.')
class RxSteps(UserObjectSingle):
@@ -433,8 +433,9 @@ class RxSteps(UserObjectSingle):
except KeyError:
raise CmdInputError('#rx_steps: requires exactly three parameters')
if config.is_messages():
log.info('All receivers will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.rxsteps[0] * G.dx, G.rxsteps[1] * G.dy, G.rxsteps[2] * G.dz))
log.info(f'All receivers will step {G.rxsteps[0] * G.dx:g}m, \
{G.rxsteps[1] * G.dy:g}m, {G.rxsteps[2] * G.dz:g}m \
for each model run.')
class ExcitationFile(UserObjectSingle):
@@ -468,8 +469,7 @@ class ExcitationFile(UserObjectSingle):
if not os.path.isfile(excitationfile):
excitationfile = os.path.abspath(os.path.join(G.inputdirectory, excitationfile))
if config.is_messages():
log.info('\nExcitation file: {}'.format(excitationfile))
log.info(f'\nExcitation file: {excitationfile}')
# Get waveform names
with open(excitationfile, 'r') as f:
@@ -490,7 +490,7 @@ class ExcitationFile(UserObjectSingle):
for waveform in range(len(waveformIDs)):
if any(x.ID == waveformIDs[waveform] for x in G.waveforms):
raise CmdInputError('Waveform with ID {} already exists'.format(waveformIDs[waveform]))
raise CmdInputError(f'Waveform with ID {waveformIDs[waveform]} already exists')
w = Waveform()
w.ID = waveformIDs[waveform]
w.type = 'user'
@@ -508,8 +508,9 @@ class ExcitationFile(UserObjectSingle):
# Interpolate waveform values
w.userfunc = interpolate.interp1d(waveformtime, singlewaveformvalues, **kwargs)
if config.is_messages():
log.info('User waveform {} created using {} and, if required, interpolation parameters (kind: {}, fill value: {}).'.format(w.ID, timestr, kwargs['kind'], kwargs['fill_value']))
log.info(f"User waveform {w.ID} created using {timestr} and, if \
required, interpolation parameters (kind: {kwargs['kind']}, \
fill value: {kwargs['fill_value']}).")
G.waveforms.append(w)

查看文件

@@ -31,79 +31,23 @@ from scipy.constants import mu_0 as m0
from .utilities import get_host_info
from .utilities import get_terminal_width
# Single instance of SimConfig to hold simulation configuration parameters.
sim_config = None
# General settings for the simulation
# inputfilepath: path to inputfile location
# outputfilepath: path to outputfile location
# messages: whether to print all messages as output to stdout or not
# progressbars: whether to show progress bars on stdoout or not
# mode: 2D TMx, 2D TMy, 2D TMz, or 3D
# cpu, cuda, opencl: solver type
# precision: data type for electromagnetic field output (single/double)
# autotranslate: auto translate objects with main grid coordinates
# to their equivalent local grid coordinate within the subgrid. If this option is off
# users must specify sub-grid object point within the global subgrid space.
# z0: impedance of free space (Ohms)
general = {'messages': True,
'progressbars': True,
'mode': '3D',
'cpu': True,
'cuda': False,
'opencl': False,
'precision': 'single',
'autotranslate': False,
'z0': np.sqrt(m0 / e0)}
def is_messages():
"""Return messages."""
return general['messages']
# Store information about host machine
hostinfo = get_host_info()
# Store information for CUDA solver type
# gpus: information about any GPUs as a list of GPU objects
# snapsgpu2cpu: copy snapshot data from GPU to CPU during simulation
# N.B. This will happen if the requested snapshots are too large to fit
# on the memory of the GPU. If True this will slow performance significantly
cuda = {'gpus': None, 'snapsgpu2cpu': False}
# Data type (precision) for electromagnetic field output
# Solid and ID arrays use 32-bit integers (0 to 4294967295)
# Rigid arrays use 8-bit integers (the smallest available type to store true/false)
# Fractal arrays use complex numbers
# Dispersive coefficient arrays use either float or complex numbers
# Main field arrays use floats
if general['precision'] == 'single':
dtypes = {'float_or_double': np.float32,
'complex': np.complex64,
'cython_float_or_double': cython.float,
'cython_complex': cython.floatcomplex,
'C_float_or_double': 'float',
'C_complex': 'pycuda::complex<float>'}
elif general['precision'] == 'double':
dtypes = {'float_or_double': np.float64,
'complex': np.complex128,
'cython_float_or_double': cython.double,
'cython_complex': cython.doublecomplex,
'C_float_or_double': 'double',
'C_complex': 'pycuda::complex<double>'}
# Instance of ModelConfig that hold model configuration parameters.
model_configs = []
class ModelConfig:
"""Configuration parameters for a model.
N.B. Multiple models can exist within a simulation
"""
def __init__(self, sim_config, i):
def __init__(self, i):
"""
Args:
sim_config (SimConfig): Simulation level configuration object.
i (int): Current model number.
"""
self.sim_config = sim_config
self.i = i # Indexed from 0
self.reuse_geometry = False
@@ -113,7 +57,7 @@ class ModelConfig:
self.appendmodelnumber = ''
# Output file path for specific model
parts = self.sim_config.output_file_path.with_suffix('').parts
parts = sim_config.output_file_path.with_suffix('').parts
self.output_file_path = Path(*parts[:-1], parts[-1] + self.appendmodelnumber)
self.output_file_path_ext = self.output_file_path.with_suffix('.out')
@@ -122,7 +66,7 @@ class ModelConfig:
# String to print at start of each model run
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, sim_config.model_end, sim_config.input_file_path)
self.next_model = Fore.GREEN + '{} {}\n'.format(self.inputfilestr, '-' * (get_terminal_width() - 1 - len(self.inputfilestr))) + Style.RESET_ALL
# Numerical dispersion analysis parameters
@@ -143,18 +87,18 @@ class ModelConfig:
'dispersiveCdtype': None}
def get_scene(self):
if self.sim_config.scenes:
return self.sim_config.scenes[self.i]
if sim_config.scenes:
return sim_config.scenes[self.i]
else: return None
def get_usernamespace(self):
return {'c': c, # Speed of light in free space (m/s)
'e0': e0, # Permittivity of free space (F/m)
'm0': m0, # Permeability of free space (H/m)
'z0': general['z0'], # Impedance of free space (Ohms)
'number_model_runs': self.sim_config.model_end + 1,
'z0': np.sqrt(m0 / e0), # Impedance of free space (Ohms)
'number_model_runs': sim_config.model_end + 1,
'current_model_run': self.i + 1,
'inputfile': self.sim_config.input_file_path.resolve()}
'inputfile': sim_config.input_file_path.resolve()}
class SimulationConfig:
@@ -169,10 +113,45 @@ class SimulationConfig:
"""
self.args = args
self.general = general
self.hostinfo = hostinfo
self.cuda = cuda
self.dtypes = dtypes
# General settings for the simulation
# inputfilepath: path to inputfile location
# outputfilepath: path to outputfile location
# messages: whether to print all messages as output to stdout or not
# progressbars: whether to show progress bars on stdoout or not
# mode: 2D TMx, 2D TMy, 2D TMz, or 3D
# cpu, cuda, opencl: solver type
# precision: data type for electromagnetic field output (single/double)
# autotranslate: auto translate objects with main grid coordinates
# to their equivalent local grid coordinate within the subgrid.
# If this option is off users must specify sub-grid object point
# within the global subgrid space.
self.general = {'messages': True,
'progressbars': True,
'mode': '3D',
'cpu': True,
'cuda': False,
'opencl': False,
'precision': 'single',
'autotranslate': False}
self.em_consts = {'c': c, # Speed of light in free space (m/s)
'e0': e0, # Permittivity of free space (F/m)
'm0': m0, # Permeability of free space (H/m)
'z0': np.sqrt(m0 / e0)} # Impedance of free space (Ohms)
# Store information about host machine
self.hostinfo = get_host_info()
# Store information for CUDA solver type
# gpus: information about any GPUs as a list of GPU objects
# snapsgpu2cpu: copy snapshot data from GPU to CPU during simulation
# N.B. This will happen if the requested snapshots are too large to fit
# on the memory of the GPU. If True this will slow performance significantly
self.cuda = {'gpus': None, 'snapsgpu2cpu': False}
# Data type (precision) for electromagnetic field output
self.dtypes = None
# Subgrid parameter may not exist if user enters via CLI
try:
@@ -187,11 +166,40 @@ class SimulationConfig:
self.scenes = []
# Set more complex parameters
self.set_precision()
self.set_input_file_path()
self.set_output_file_path()
self.set_model_start_end()
self.set_single_model()
def is_messages(self):
return self.general['messages']
def set_precision(self):
"""Data type (precision) for electromagnetic field output.
Solid and ID arrays use 32-bit integers (0 to 4294967295)
Rigid arrays use 8-bit integers (the smallest available type to store true/false)
Fractal arrays use complex numbers
Dispersive coefficient arrays use either float or complex numbers
Main field arrays use floats
"""
if self.general['precision'] == 'single':
self.dtypes = {'float_or_double': np.float32,
'complex': np.complex64,
'cython_float_or_double': cython.float,
'cython_complex': cython.floatcomplex,
'C_float_or_double': 'float',
'C_complex': 'pycuda::complex<float>'}
elif self.general['precision'] == 'double':
self.dtypes = {'float_or_double': np.float64,
'complex': np.complex128,
'cython_float_or_double': cython.double,
'cython_complex': cython.doublecomplex,
'C_float_or_double': 'double',
'C_complex': 'pycuda::complex<double>'}
def set_single_model(self):
if self.model_start == 0 and self.model_end == 1:
self.single_model = True
@@ -242,36 +250,3 @@ class SimulationConfigMPI(SimulationConfig):
# Set range for number of models to run
self.model_start = self.args.restart if self.args.restart else 1
self.model_end = self.modelstart + self.args.n
def create_simulation_config(args):
"""Create simulation level configuration object to hold simulation
level parameters.
Args:
args (Namespace): Arguments from either API or CLI.
Returns:
sc (SimulationConfig): Simulation configuration object.
"""
if not args.mpi and not args.mpi_no_spawn:
sc = SimulationConfig(args)
elif args.mpi:
sc = SimulationConfigMPI(args)
return sc
def create_model_config(sim_config, i):
"""Create model level configuration object to hold model level
parameters.
Args:
sim_config (SimConfig): Simulation level configuration object.
i (int): Current model number.
"""
mc = ModelConfig(sim_config, i)
return mc

47
gprMax/config_parser.py 可执行文件
查看文件

@@ -0,0 +1,47 @@
# Copyright (C) 2015-2019: The University of Edinburgh
# Authors: Craig Warren and Antonis Giannopoulos
#
# This file is part of gprMax.
#
# gprMax is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gprMax is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
import gprMax.config as config
def write_simulation_config(args):
"""Write simulation level configuration parameters to config module. As
there can only be one instance of the config module objects are always
found via 'import gprMax.config'
Args:
args (Namespace): Arguments from either API or CLI.
"""
if args.mpi or args.mpi_no_spawn:
config.sim_config = config.SimulationConfigMPI(args)
else:
config.sim_config = config.SimulationConfig(args)
def write_model_config(i):
"""Write model level configuration parameters to config module. As there can
only be one instance of the config module objects are always found via
'import gprMax.config'
Args:
i (int): Model number.
"""
model_config = config.ModelConfig(i)
config.model_configs.append(model_config)

查看文件

@@ -18,8 +18,9 @@
import datetime
import gprMax.config as config
from ._version import __version__, codename
from .config import create_model_config
from .config_parser import write_model_config
from .model_build_run import ModelBuildRun
from .solvers import create_solver
from .solvers import create_G
@@ -33,14 +34,8 @@ class Context:
e.g. an MPI context.
"""
def __init__(self, sim_config):
"""
Args:
sim_config (SimConfig): Simulation level configuration object.
"""
self.sim_config = sim_config
self.model_range = range(sim_config.model_start, sim_config.model_end)
def __init__(self):
self.model_range = range(config.sim_config.model_start, config.sim_config.model_end)
self.tsimend = 0
self.tsimstart = 1
@@ -70,29 +65,25 @@ class NoMPIContext(Context):
is parallelised using either OpenMP (CPU) or CUDA (GPU).
"""
def __init__(self, sim_config):
super().__init__(sim_config)
def _run(self):
"""Specialise how the models are farmed out."""
for i in self.model_range:
model_config = create_model_config(self.sim_config, i)
write_model_config(i)
# Always create a solver for the first model.
# The next model to run only gets a new solver if the
# geometry is not re-used.
if i != 0 and self.sim_config.geometry_fixed:
model_config.reuse_geometry = True
if i != 0 and config.sim_config.args.geometry_fixed:
config.model_config[i].reuse_geometry = True
else:
G = create_G(self.sim_config)
G = create_G()
model = ModelBuildRun(G, self.sim_config, model_config)
model = ModelBuildRun(i, G)
model.build()
solver = create_solver(G)
solver = create_solver(G, self.sim_config)
if not self.sim_config.geometry_only:
if not config.sim_config.args.geometry_only:
model.solve(solver)
def make_time_report(self):
@@ -111,8 +102,8 @@ class MPIContext(Context):
or CUDA (GPU).
"""
def __init__(self, sim_config):
super().__init__(sim_config)
def __init__(self):
super().__init__()
from mpi4py import MPI
def _run(self):
@@ -124,8 +115,8 @@ class MPIContext(Context):
class MPINoSpawnContext(Context):
def __init__(self, sim_config):
super().__init__(sim_config)
def __init__(self):
super().__init__()
from mpi4py import MPI
def _run(self):
@@ -135,21 +126,18 @@ class MPINoSpawnContext(Context):
pass
def create_context(sim_config):
def create_context():
"""Create a context in which to run the simulation. i.e MPI.
Args:
sim_config (SimConfig): Simulation level configuration object.
Returns:
context (Context): Context for the model to run in.
"""
if sim_config.args.mpi:
context = MPIContext(sim_config)
elif sim_config.args.mpi_no_spawn:
context = MPINoSpawnContext(sim_config)
if config.sim_config.args.mpi:
context = MPIContext()
elif config.sim_config.args.mpi_no_spawn:
context = MPINoSpawnContext()
else:
context = NoMPIContext(sim_config)
context = NoMPIContext()
return context

查看文件

@@ -19,7 +19,7 @@
import argparse
import logging
from .config import create_simulation_config
from .config_parser import write_simulation_config
from .contexts import create_context
logging.basicConfig(level=logging.INFO, format='%(message)s')
@@ -137,7 +137,7 @@ def run_main(args):
args (Namespace): arguments from either API or CLI.
"""
sim_config = create_simulation_config(args)
context = create_context(sim_config)
write_simulation_config(args)
context = create_context()
context.run()
context.print_time_report()

查看文件

@@ -24,7 +24,6 @@ from colorama import Fore
from colorama import Style
init()
import numpy as np
from scipy.constants import c
import gprMax.config as config
from .exceptions import GeneralError
@@ -147,12 +146,12 @@ class FDTDGrid:
def initialise_field_arrays(self):
"""Initialise arrays for the electric and magnetic field components."""
self.Ex = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double'])
self.Ey = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double'])
self.Ez = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double'])
self.Hx = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double'])
self.Hy = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double'])
self.Hz = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.dtypes['float_or_double'])
self.Ex = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes['float_or_double'])
self.Ey = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes['float_or_double'])
self.Ez = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes['float_or_double'])
self.Hx = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes['float_or_double'])
self.Hy = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes['float_or_double'])
self.Hz = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes['float_or_double'])
def initialise_grids(self):
"""Initialise all grids."""
@@ -162,8 +161,8 @@ class FDTDGrid:
def initialise_std_update_coeff_arrays(self):
"""Initialise arrays for storing update coefficients."""
self.updatecoeffsE = np.zeros((len(self.materials), 5), dtype=config.dtypes['float_or_double'])
self.updatecoeffsH = np.zeros((len(self.materials), 5), dtype=config.dtypes['float_or_double'])
self.updatecoeffsE = np.zeros((len(self.materials), 5), dtype=config.sim_config.dtypes['float_or_double'])
self.updatecoeffsH = np.zeros((len(self.materials), 5), dtype=config.sim_config.dtypes['float_or_double'])
def initialise_dispersive_arrays(self, dtype):
"""Initialise arrays for storing coefficients when there are dispersive materials present."""
@@ -183,7 +182,7 @@ class FDTDGrid:
rigidarrays = (12 + 6) * self.nx * self.ny * self.nz * np.dtype(np.int8).itemsize
# 6 x field arrays + 6 x ID arrays
fieldarrays = (6 + 6) * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(config.dtypes['float_or_double']).itemsize
fieldarrays = (6 + 6) * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(config.sim_config.dtypes['float_or_double']).itemsize
# PML arrays
pmlarrays = 0
@@ -211,7 +210,7 @@ class FDTDGrid:
"""Check if the required amount of memory (RAM) is available to build
and/or run model on the host.
"""
if self.memoryusage > config.hostinfo['ram']:
if self.memoryusage > config.sim_config.hostinfo['ram']:
raise GeneralError(f"Memory (RAM) required ~{human_size(self.memoryusage)} \
exceeds {human_size(config.hostinfo['ram'], a_kilobyte_is_1024_bytes=True)} detected!\n")
@@ -256,7 +255,7 @@ class FDTDGrid:
def calculate_dt(self):
"""Calculate time step at the CFL limit."""
self.dt = (1 / (c * np.sqrt(
self.dt = (1 / (config.sim_config.em_consts['c'] * np.sqrt(
(1 / self.dx) * (1 / self.dx) +
(1 / self.dy) * (1 / self.dy) +
(1 / self.dz) * (1 / self.dz))))

查看文件

@@ -294,9 +294,7 @@ def parse_hash_commands(model_config, G, scene):
scene (Scene): Scene object.
"""
sim_config = model_config.sim_config
with open(sim_config.input_file_path) as inputfile:
with open(config.sim_config.input_file_path) as inputfile:
usernamespace = model_config.get_usernamespace()
@@ -312,7 +310,7 @@ def parse_hash_commands(model_config, G, scene):
# Write a file containing the input commands after Python or include
# file commands have been processed
if sim_config.args.write_processed:
if config.sim_config.args.write_processed:
write_processed_file(processedlines, model_config.appendmodelnumber, G)
user_objs = get_user_objects(processedlines, check=True)

查看文件

@@ -67,8 +67,8 @@ class Material:
G (FDTDGrid): Holds essential parameters describing a model.
"""
EA = (config.e0 * self.er / G.dt) + 0.5 * self.se
EB = (config.e0 * self.er / G.dt) - 0.5 * self.se
EA = (config.sim_config.em_consts['e0'] * self.er / G.dt) + 0.5 * self.se
EB = (config.sim_config.em_consts['e0'] * self.er / G.dt) - 0.5 * self.se
if self.ID == 'pec' or self.se == float('inf'):
self.CA = 0
@@ -100,7 +100,7 @@ class Material:
class DispersiveMaterial(Material):
"""Class to describe materials with frequency dependent properties, e.g.
Debye, Drude, Lorenz
Debye, Drude, Lorenz.
"""
# Properties of water from: http://dx.doi.org/10.1109/TGRS.2006.873208
@@ -161,8 +161,10 @@ class DispersiveMaterial(Material):
self.zt[x] = (self.w[x] / self.q[x]) * (1 - self.eqt[x]) / G.dt
self.zt2[x] = (self.w[x] / self.q[x]) * (1 - self.eqt2[x])
EA = (config.e0 * self.er / G.dt) + 0.5 * self.se - (config.e0 / G.dt) * np.sum(self.zt2.real)
EB = (config.e0 * self.er / G.dt) - 0.5 * self.se - (config.e0 / G.dt) * np.sum(self.zt2.real)
EA = ((config.sim_config.em_consts['e0'] * self.er / G.dt) + 0.5 * self.se -
(config.sim_config.em_consts['e0'] / G.dt) * np.sum(self.zt2.real))
EB = ((config.sim_config.em_consts['e0'] * self.er / G.dt) - 0.5 * self.se -
(config.sim_config.em_consts['e0'] / G.dt) * np.sum(self.zt2.real))
self.CA = EB / EA
self.CBx = (1 / G.dx) * 1 / EA
@@ -186,7 +188,7 @@ class DispersiveMaterial(Material):
er = self.er
w = 2 * np.pi * freq
er += self.se / (w * config.e0)
er += self.se / (w * config.sim_config.em_consts['e0'])
if 'debye' in self.type:
for pole in range(self.poles):
er += self.deltaer[pole] / (1 + 1j * w * self.tau[pole])
@@ -205,7 +207,7 @@ class DispersiveMaterial(Material):
def process_materials(G):
"""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
Args:
G (FDTDGrid): Holds essential parameters describing a model.
@@ -236,8 +238,8 @@ def process_materials(G):
if hasattr(material, 'poles'):
z = 0
for pole in range(config.materials['maxpoles']):
G.updatecoeffsdispersive[material.numID, z:z + 3] = (config.e0
* material.eqt2[pole], material.eqt[pole], material.zt[pole])
G.updatecoeffsdispersive[material.numID, z:z + 3] = (config.sim_config.em_consts['e0'] *
material.eqt2[pole], material.eqt[pole], material.zt[pole])
z += 3
# Construct information on material properties for printing table

查看文件

@@ -79,10 +79,9 @@ log = logging.getLogger(__name__)
class ModelBuildRun:
"""Builds and runs (solves) a model."""
def __init__(self, G, sim_config, model_config):
def __init__(self, i, G):
self.i = i
self.G = G
self.sim_config = sim_config
self.model_config = model_config
# Monitor memory usage
self.p = None
@@ -93,37 +92,34 @@ class ModelBuildRun:
self.p = psutil.Process()
# Normal model reading/building process; bypassed if geometry information to be reused
if self.model_config.reuse_geometry:
self.reuse_geometry()
else:
self.build_geometry()
self.reuse_geometry() if config.model_configs[self.i].reuse_geometry else self.build_geometry()
G = self.G
# Adjust position of simple sources and receivers if required
if G.srcsteps[0] != 0 or G.srcsteps[1] != 0 or G.srcsteps[2] != 0:
for source in itertools.chain(G.hertziandipoles, G.magneticdipoles):
if self.model_config.i == 0:
if config.model_configs[self.i].i == 0:
if source.xcoord + G.srcsteps[0] * self.sim_config.model_end < 0 or source.xcoord + G.srcsteps[0] * self.sim_config.model_end > G.nx or source.ycoord + G.srcsteps[1] * self.sim_config.model_end < 0 or source.ycoord + G.srcsteps[1] * self.sim_config.model_end > G.ny or source.zcoord + G.srcsteps[2] * self.sim_config.model_end < 0 or source.zcoord + G.srcsteps[2] * self.sim_config.model_end > G.nz:
raise GeneralError('Source(s) will be stepped to a position outside the domain.')
source.xcoord = source.xcoordorigin + (self.model_config.i) * G.srcsteps[0]
source.ycoord = source.ycoordorigin + (self.model_config.i) * G.srcsteps[1]
source.zcoord = source.zcoordorigin + (self.model_config.i) * G.srcsteps[2]
source.xcoord = source.xcoordorigin + (config.model_configs[self.i].i) * G.srcsteps[0]
source.ycoord = source.ycoordorigin + (config.model_configs[self.i].i) * G.srcsteps[1]
source.zcoord = source.zcoordorigin + (config.model_configs[self.i].i) * G.srcsteps[2]
if G.rxsteps[0] != 0 or G.rxsteps[1] != 0 or G.rxsteps[2] != 0:
for receiver in G.rxs:
if self.model_config.i == 0:
if config.model_configs[self.i].i == 0:
if receiver.xcoord + G.rxsteps[0] * self.sim_config.model_end < 0 or receiver.xcoord + G.rxsteps[0] * self.sim_config.model_end > G.nx or receiver.ycoord + G.rxsteps[1] * self.sim_config.model_end < 0 or receiver.ycoord + G.rxsteps[1] * self.sim_config.model_end > G.ny or receiver.zcoord + G.rxsteps[2] * self.sim_config.model_end < 0 or receiver.zcoord + G.rxsteps[2] * self.sim_config.model_end > G.nz:
raise GeneralError('Receiver(s) will be stepped to a position outside the domain.')
receiver.xcoord = receiver.xcoordorigin + (self.model_config.i) * G.rxsteps[0]
receiver.ycoord = receiver.ycoordorigin + (self.model_config.i) * G.rxsteps[1]
receiver.zcoord = receiver.zcoordorigin + (self.model_config.i) * G.rxsteps[2]
receiver.xcoord = receiver.xcoordorigin + (config.model_configs[self.i].i) * G.rxsteps[0]
receiver.ycoord = receiver.ycoordorigin + (config.model_configs[self.i].i) * G.rxsteps[1]
receiver.zcoord = receiver.zcoordorigin + (config.model_configs[self.i].i) * G.rxsteps[2]
# Write files for any geometry views and geometry object outputs
if not (G.geometryviews or G.geometryobjectswrite) and self.sim_config.geometry_only and config.is_messages():
log.warning(Fore.RED + f'\nNo geometry views or geometry objects to output found.' + Style.RESET_ALL)
if config.is_messages(): print()
for i, geometryview in enumerate(G.geometryviews):
geometryview.set_filename(self.model_config.appendmodelnumber)
geometryview.set_filename(config.model_configs[self.i].appendmodelnumber)
pbar = tqdm(total=geometryview.datawritesize, unit='byte', unit_scale=True, desc='Writing geometry view file {}/{}, {}'.format(i + 1, len(G.geometryviews), os.path.split(geometryview.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
geometryview.write_vtk(G, pbar)
pbar.close()
@@ -138,20 +134,18 @@ class ModelBuildRun:
# tsolve = 0
def build_geometry(self):
model_config = self.model_config
sim_config = self.sim_config
G = self.G
log.info(model_config.next_model)
log.info(config.model_configs[self.i].next_model)
scene = self.build_scene()
# Combine available grids
grids = [G] + G.subgrids
gridbuilders = [GridBuilder(grid, self.printer) for grid in grids]
gridbuilders = [GridBuilder(grid) for grid in grids]
for gb in gridbuilders:
gb.printer.print(pml_information(gb.grid))
pml_information(gb.grid)
gb.build_pmls()
gb.build_components()
gb.tm_grid_update()
@@ -159,7 +153,7 @@ class ModelBuildRun:
gb.grid.initialise_std_update_coeff_arrays()
# Set datatype for dispersive arrays if there are any dispersive materials.
if self.model_config.materials['maxpoles'] != 0:
if config.model_configs[self.i].materials['maxpoles'] != 0:
drudelorentz = any([m for m in G.materials if 'drude' in m.type or 'lorentz' in m.type])
if drudelorentz:
config.materials['dispersivedtype'] = config.dtypes['complex']
@@ -220,22 +214,22 @@ class ModelBuildRun:
G = self.G
# Reset iteration number
G.iteration = 0
self.model_config.inputfilestr = f'\n--- Model {self.model_config.appendmodelnumber}/{self.sim_config.model_end}, \
config.model_configs[self.i].inputfilestr = f'\n--- Model {config.model_configs[self.i].appendmodelnumber}/{self.sim_config.model_end}, \
input file (not re-processed, i.e. geometry fixed): {self.sim_config.input_file_path}'
log.info(Fore.GREEN + f"{self.model_config.inputfilestr} {'-' * (get_terminal_width() - 1 - len(self.model_config.inputfilestr))}" + Style.RESET_ALL)
log.info(Fore.GREEN + f"{config.model_configs[self.i].inputfilestr} {'-' * (get_terminal_width() - 1 - len(config.model_configs[self.i].inputfilestr))}" + Style.RESET_ALL)
for grid in [G] + G.subgrids:
grid.reset_fields()
def build_scene(self):
G = self.G
# API for multiple scenes / model runs
scene = self.model_config.get_scene()
scene = config.model_configs[self.i].get_scene()
# If there is no scene - process the hash commands instead
if not scene:
scene = Scene()
# Parse the input file into user objects and add them to the scene
scene = parse_hash_commands(self.model_config, G, scene)
scene = parse_hash_commands(config.model_configs[self.i], G, scene)
# Creates the internal simulation objects.
scene.create_internal_objects(G)
@@ -251,7 +245,7 @@ class ModelBuildRun:
except FileExistsError:
pass
# Modify the output path (hack)
self.model_config.output_file_path_ext = Path(self.G.outputdirectory, self.model_config.output_file_path_ext)
config.model_configs[self.i].output_file_path_ext = Path(self.G.outputdirectory, config.model_configs[self.i].output_file_path_ext)
def write_output_data(self):
"""Write output data, i.e. field data for receivers and snapshots
@@ -261,19 +255,19 @@ class ModelBuildRun:
G = self.G
# Write an output file in HDF5 format
write_hdf5_outputfile(self.model_config.output_file_path_ext, G)
write_hdf5_outputfile(config.model_configs[self.i].output_file_path_ext, G)
# Write any snapshots to file
if G.snapshots:
# Create directory and construct filename from user-supplied name
# and model run number
snapshotdir = self.model_config.snapshot_dir
snapshotdir = config.model_configs[self.i].snapshot_dir
if not os.path.exists(snapshotdir):
os.mkdir(snapshotdir)
log.info('')
for i, snap in enumerate(G.snapshots):
fn = snapshotdir / Path(self.model_config.output_file_path.stem + '_' + snap.basefilename)
fn = snapshotdir / Path(config.model_configs[self.i].output_file_path.stem + '_' + snap.basefilename)
snap.filename = fn.with_suffix('.vti')
pbar = tqdm(total=snap.vtkdatawritesize, leave=True, unit='byte', unit_scale=True, desc='Writing snapshot file {} of {}, {}'.format(i + 1, len(G.snapshots), os.path.split(snap.filename)[1]), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
snap.write_vtk_imagedata(pbar, G)
@@ -304,13 +298,13 @@ class ModelBuildRun:
G = self.G
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(config.model_configs[self.i]
.i + 1) + '/' + str(self.sim_config.model_end), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
else:
iterator = range(0, G.iterations)
self.create_output_directory()
log.info(f'Output file: {self.model_config.output_file_path_ext}\n')
log.info(f'Output file: {config.model_configs[self.i].output_file_path_ext}\n')
# Run solver
tsolve = self.solve(solver)
@@ -325,15 +319,14 @@ class ModelBuildRun:
class GridBuilder:
def __init__(self, grid, printer):
def __init__(self, grid):
self.grid = grid
self.printer = printer
def build_pmls(self):
grid = self.grid
# Build the PMLS
pbar = tqdm(total=sum(1 for value in grid.pmlthickness.values() if value > 0), desc='Building {} Grid PML boundaries'.format(self.grid.name), ncols=get_terminal_width() - 1, file=sys.stdout, disable=not config.general['progressbars'])
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.sim_config.general['progressbars'])
for pml_id, thickness in grid.pmlthickness.items():
if thickness > 0:
build_pml(grid, pml_id, thickness)
@@ -344,7 +337,7 @@ class GridBuilder:
# Build the model, i.e. set the material properties (ID) for every edge
# of every Yee cell
log.info('')
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.sim_config.general['progressbars'])
build_electric_components(self.grid.solid, self.grid.rigidE, self.grid.ID, self.grid)
pbar.update()
build_magnetic_components(self.grid.solid, self.grid.rigidH, self.grid.ID, self.grid)
@@ -352,11 +345,11 @@ class GridBuilder:
pbar.close()
def tm_grid_update(self):
if '2D TMx' == config.general['mode']:
if '2D TMx' == config.sim_config.general['mode']:
self.grid.tmx()
elif '2D TMy' == config.general['mode']:
elif '2D TMy' == config.sim_config.general['mode']:
self.grid.tmy()
elif '2D TMz' == config.general['mode']:
elif '2D TMz' == config.sim_config.general['mode']:
self.grid.tmz()
def update_voltage_source_materials(self):

查看文件

@@ -80,7 +80,7 @@ class CFS:
# Calculation of the maximum value of sigma from http://dx.doi.org/10.1109/8.546249
m = CFSParameter.scalingprofiles[self.sigma.scalingprofile]
self.sigma.max = (0.8 * (m + 1)) / (config.general['z0'] * d * np.sqrt(er * mr))
self.sigma.max = (0.8 * (m + 1)) / (config.sim_config.em_consts['z0'] * d * np.sqrt(er * mr))
def scaling_polynomial(self, order, Evalues, Hvalues):
"""Applies the polynomial to be used for the scaling profile for
@@ -123,8 +123,8 @@ class CFS:
"""
# Extra cell of thickness added to allow correct scaling of electric and magnetic values
Evalues = np.zeros(thickness + 1, dtype=config.dtypes['float_or_double'])
Hvalues = np.zeros(thickness + 1, dtype=config.dtypes['float_or_double'])
Evalues = np.zeros(thickness + 1, dtype=config.sim_config.dtypes['float_or_double'])
Hvalues = np.zeros(thickness + 1, dtype=config.sim_config.dtypes['float_or_double'])
if parameter.scalingprofile == 'constant':
Evalues += parameter.max
@@ -215,31 +215,31 @@ class PML:
if self.direction[0] == 'x':
self.EPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
elif self.direction[0] == 'y':
self.EPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
elif self.direction[0] == 'z':
self.EPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.sim_config.dtypes['float_or_double'])
def calculate_update_coeffs(self, er, mr, G):
"""Calculates electric and magnetic update coefficients for the PML.
@@ -251,21 +251,21 @@ class PML:
"""
self.ERA = np.zeros((len(self.CFS), self.thickness),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.ERB = np.zeros((len(self.CFS), self.thickness),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.ERE = np.zeros((len(self.CFS), self.thickness),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.ERF = np.zeros((len(self.CFS), self.thickness),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HRA = np.zeros((len(self.CFS), self.thickness),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HRB = np.zeros((len(self.CFS), self.thickness),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HRE = np.zeros((len(self.CFS), self.thickness),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
self.HRF = np.zeros((len(self.CFS), self.thickness),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
for x, cfs in enumerate(self.CFS):
if not cfs.sigma.max:
@@ -277,33 +277,33 @@ class PML:
# Define different parameters depending on PML formulation
if G.pmlformulation == 'HORIPML':
# HORIPML electric update coefficients
tmp = (2 * config.e0 * Ekappa) + G.dt * (Ealpha * Ekappa + Esigma)
self.ERA[x, :] = (2 * config.e0 + G.dt * Ealpha) / tmp
self.ERB[x, :] = (2 * config.e0 * Ekappa) / tmp
self.ERE[x, :] = ((2 * config.e0 * Ekappa) - G.dt
tmp = (2 * config.sim_config.em_consts['e0'] * Ekappa) + G.dt * (Ealpha * Ekappa + Esigma)
self.ERA[x, :] = (2 * config.sim_config.em_consts['e0'] + G.dt * Ealpha) / tmp
self.ERB[x, :] = (2 * config.sim_config.em_consts['e0'] * Ekappa) / tmp
self.ERE[x, :] = ((2 * config.sim_config.em_consts['e0'] * Ekappa) - G.dt
* (Ealpha * Ekappa + Esigma)) / tmp
self.ERF[x, :] = (2 * Esigma * G.dt) / (Ekappa * tmp)
# HORIPML magnetic update coefficients
tmp = (2 * config.e0 * Hkappa) + G.dt * (Halpha * Hkappa + Hsigma)
self.HRA[x, :] = (2 * config.e0 + G.dt * Halpha) / tmp
self.HRB[x, :] = (2 * config.e0 * Hkappa) / tmp
self.HRE[x, :] = ((2 * config.e0 * Hkappa) - G.dt
tmp = (2 * config.sim_config.em_consts['e0'] * Hkappa) + G.dt * (Halpha * Hkappa + Hsigma)
self.HRA[x, :] = (2 * config.sim_config.em_consts['e0'] + G.dt * Halpha) / tmp
self.HRB[x, :] = (2 * config.sim_config.em_consts['e0'] * Hkappa) / tmp
self.HRE[x, :] = ((2 * config.sim_config.em_consts['e0'] * Hkappa) - G.dt
* (Halpha * Hkappa + Hsigma)) / tmp
self.HRF[x, :] = (2 * Hsigma * G.dt) / (Hkappa * tmp)
elif G.pmlformulation == 'MRIPML':
tmp = 2 * config.e0 + G.dt * Ealpha
tmp = 2 * config.sim_config.em_consts['e0'] + G.dt * Ealpha
self.ERA[x, :] = Ekappa + (G.dt * Esigma) / tmp
self.ERB[x, :] = (2 * config.e0) / tmp
self.ERE[x, :] = ((2 * config.e0) - G.dt * Ealpha) / tmp
self.ERB[x, :] = (2 * config.sim_config.em_consts['e0']) / tmp
self.ERE[x, :] = ((2 * config.sim_config.em_consts['e0']) - G.dt * Ealpha) / tmp
self.ERF[x, :] = (2 * Esigma * G.dt) / tmp
# MRIPML magnetic update coefficients
tmp = 2 * config.e0 + G.dt * Halpha
tmp = 2 * config.sim_config.em_consts['e0'] + G.dt * Halpha
self.HRA[x, :] = Hkappa + (G.dt * Hsigma) / tmp
self.HRB[x, :] = (2 * config.e0) / tmp
self.HRE[x, :] = ((2 * config.e0) - G.dt * Halpha) / tmp
self.HRB[x, :] = (2 * config.sim_config.em_consts['e0']) / tmp
self.HRE[x, :] = ((2 * config.sim_config.sim_config.em_consts['e0']) - G.dt * Halpha) / tmp
self.HRF[x, :] = (2 * Hsigma * G.dt) / tmp
def update_electric(self, G):
@@ -316,7 +316,7 @@ class PML:
pmlmodule = 'gprMax.cython.pml_updates_electric_' + G.pmlformulation
func = getattr(import_module(pmlmodule), 'order' + str(len(self.CFS)) + '_' + self.direction)
func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf,
config.hostinfo['ompthreads'], G.updatecoeffsE, G.ID,
config.sim_config.hostinfo['ompthreads'], G.updatecoeffsE, G.ID,
G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz, self.EPhi1, self.EPhi2,
self.ERA, self.ERB, self.ERE, self.ERF, self.d)
@@ -330,7 +330,7 @@ class PML:
pmlmodule = 'gprMax.cython.pml_updates_magnetic_' + G.pmlformulation
func = getattr(import_module(pmlmodule), 'order' + str(len(self.CFS)) + '_' + self.direction)
func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf,
config.hostinfo['ompthreads'], G.updatecoeffsH, G.ID,
config.sim_config.hostinfo['ompthreads'], G.updatecoeffsH, G.ID,
G.Ex, G.Ey, G.Ez, G.Hx, G.Hy, G.Hz, self.HPhi1, self.HPhi2,
self.HRA, self.HRB, self.HRE, self.HRF, self.d)

查看文件

@@ -40,18 +40,9 @@ class Snapshot:
bpg = None
# Set string for byte order
if sys.byteorder == 'little':
byteorder = 'LittleEndian'
else:
byteorder = 'BigEndian'
byteorder = 'LittleEndian' if sys.byteorder == 'little' else 'BigEndian'
# Set format text and string depending on float type
if config.dtypes['float_or_double'] == np.float32:
floatname = 'Float32'
floatstring = 'f'
elif config.dtypes['float_or_double'] == np.float64:
floatname = 'Float64'
floatstring = 'd'
def __init__(self, xs=None, ys=None, zs=None, xf=None, yf=None, zf=None,
dx=None, dy=None, dz=None, time=None, filename=None):
@@ -63,6 +54,14 @@ class Snapshot:
filename (str): Filename to save to.
"""
# Set format text and string depending on float type
if config.sim_config.dtypes['float_or_double'] == np.float32:
self.floatname = 'Float32'
self.floatstring = 'f'
elif config.sim_config.dtypes['float_or_double'] == np.float64:
self.floatname = 'Float64'
self.floatstring = 'd'
self.fieldoutputs = {'electric': True, 'magnetic': True}
self.xs = xs
self.ys = ys
@@ -156,14 +155,14 @@ class Snapshot:
if self.fieldoutputs['electric'] and self.fieldoutputs['magnetic']:
self.filehandle.write('<CellData Vectors="E-field H-field">\n'.encode('utf-8'))
self.filehandle.write(f'<DataArray type="{Snapshot.floatname}" Name="E-field" NumberOfComponents="3" format="appended" offset="0" />\n'.encode('utf-8'))
self.filehandle.write(f'<DataArray type="{Snapshot.floatname}" Name="H-field" NumberOfComponents="3" format="appended" offset="{hfield_offset}" />\n'.encode('utf-8'))
self.filehandle.write(f'<DataArray type="{self.floatname}" Name="E-field" NumberOfComponents="3" format="appended" offset="0" />\n'.encode('utf-8'))
self.filehandle.write(f'<DataArray type="{self.floatname}" Name="H-field" NumberOfComponents="3" format="appended" offset="{hfield_offset}" />\n'.encode('utf-8'))
elif self.fieldoutputs['electric']:
self.filehandle.write('<CellData Vectors="E-field">\n'.encode('utf-8'))
self.filehandle.write(f'<DataArray type="{Snapshot.floatname}" Name="E-field" NumberOfComponents="3" format="appended" offset="0" />\n'.encode('utf-8'))
self.filehandle.write(f'<DataArray type="{self.floatname}" Name="E-field" NumberOfComponents="3" format="appended" offset="0" />\n'.encode('utf-8'))
elif self.fieldoutputs['magnetic']:
self.filehandle.write('<CellData Vectors="H-field">\n'.encode('utf-8'))
self.filehandle.write(f'<DataArray type="{Snapshot.floatname}" Name="H-field" NumberOfComponents="3" format="appended" offset="0" />\n'.encode('utf-8'))
self.filehandle.write(f'<DataArray type="{self.floatname}" Name="H-field" NumberOfComponents="3" format="appended" offset="0" />\n'.encode('utf-8'))
self.filehandle.write('</CellData>\n</Piece>\n</ImageData>\n<AppendedData encoding="raw">\n_'.encode('utf-8'))

查看文件

@@ -24,19 +24,16 @@ from .updates import CPUUpdates
from .updates import CUDAUpdates
def create_G(sim_config):
def create_G():
"""Create grid object according to solver.
Args:
sim_config (SimConfig): Simulation level configuration object.
Returns:
G (FDTDGrid): Holds essential parameters describing the model.
"""
if sim_config.general['cuda']:
if config.sim_config.general['cuda']:
G = CUDAGrid()
elif sim_config.subgrid:
elif config.sim_config.subgrid:
G = FDTDGrid()
else:
G = FDTDGrid()
@@ -44,21 +41,20 @@ def create_G(sim_config):
return G
def create_solver(G, sim_config):
def create_solver(G):
"""Create configured solver object.
Args:
G (FDTDGrid): Holds essential parameters describing the model.
sim_config (SimConfig): simulation level configuration object.
Returns:
solver (Solver): solver object.
"""
if sim_config.gpu:
if config.sim_config.gpu:
updates = CUDAUpdates(G)
solver = Solver(updates)
elif sim_config.subgrid:
elif config.sim_config.subgrid:
updates = create_subgrid_updates(G)
solver = Solver(updates, hsg=True)
# A large range of different functions exist to advance the time step for

查看文件

@@ -51,11 +51,11 @@ class Source:
"""
# Waveform values for electric sources - calculated half a timestep later
self.waveformvaluesJ = np.zeros((G.iterations),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
# Waveform values for magnetic sources
self.waveformvaluesM = np.zeros((G.iterations),
dtype=config.dtypes['float_or_double'])
dtype=config.sim_config.dtypes['float_or_double'])
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)

查看文件

@@ -36,6 +36,7 @@ from colorama import Style
init()
import numpy as np
import gprMax.config as config
from .exceptions import GeneralError
log = logging.getLogger(__name__)
@@ -90,7 +91,7 @@ def logo(version):
@contextmanager
def open_path_file(path_or_file):
"""Accepts either a path as a string or a file object and returns a file
object (http://stackoverflow.com/a/6783680).
object (http://stackoverflow.com/a/6783680).
Args:
path_or_file: path as a string or a file object.
@@ -361,7 +362,7 @@ def set_openmp_threads():
# os.environ['OMP_DISPLAY_ENV'] = 'TRUE'
# Catch bug with Windows Subsystem for Linux (https://github.com/Microsoft/BashOnWindows/issues/785)
if 'Microsoft' in config.hostinfo['osversion']:
if 'Microsoft' in config.sim_config.hostinfo['osversion']:
os.environ['KMP_AFFINITY'] = 'disabled'
del os.environ['OMP_PLACES']
del os.environ['OMP_PROC_BIND']
@@ -370,8 +371,8 @@ def set_openmp_threads():
nthreads = int(os.environ.get('OMP_NUM_THREADS'))
else:
# Set number of threads to number of physical CPU cores
nthreads = config.hostinfo['physicalcores']
os.environ['OMP_NUM_THREADS'] = str(G.nthreads)
nthreads = config.sim_config.hostinfo['physicalcores']
os.environ['OMP_NUM_THREADS'] = str(nthreads)
return nthreads