From 0359cef4d6a9e49073f9d195e2f5186fbadd51be Mon Sep 17 00:00:00 2001 From: Craig Warren Date: Fri, 11 Oct 2019 17:35:14 +0100 Subject: [PATCH] Started implementing new config structure. --- gprMax/cmds_geometry/add_grass.py | 6 +- gprMax/cmds_geometry/add_surface_roughness.py | 7 +- gprMax/cmds_geometry/add_surface_water.py | 5 +- gprMax/cmds_geometry/box.py | 5 +- gprMax/cmds_geometry/cylinder.py | 4 +- gprMax/cmds_geometry/cylindrical_sector.py | 12 +- gprMax/cmds_geometry/edge.py | 4 +- gprMax/cmds_geometry/fractal_box.py | 7 +- gprMax/cmds_geometry/fractal_box_builder.py | 6 +- gprMax/cmds_geometry/geometry_objects_read.py | 4 +- gprMax/cmds_geometry/plate.py | 4 +- gprMax/cmds_geometry/sphere.py | 4 +- gprMax/cmds_geometry/triangle.py | 9 +- gprMax/cmds_multiple.py | 5 +- gprMax/cmds_single_use.py | 45 ++--- gprMax/config.py | 185 ++++++++---------- gprMax/config_parser.py | 47 +++++ gprMax/contexts.py | 54 ++--- gprMax/gprMax.py | 6 +- gprMax/grid.py | 23 ++- gprMax/hash_cmds_file.py | 6 +- gprMax/materials.py | 20 +- gprMax/model_build_run.py | 71 +++---- gprMax/pml.py | 78 ++++---- gprMax/snapshots.py | 29 ++- gprMax/solvers.py | 16 +- gprMax/sources.py | 4 +- gprMax/utilities.py | 9 +- 28 files changed, 315 insertions(+), 360 deletions(-) create mode 100755 gprMax/config_parser.py diff --git a/gprMax/cmds_geometry/add_grass.py b/gprMax/cmds_geometry/add_grass.py index c59d521a..46d4843b 100644 --- a/gprMax/cmds_geometry/add_grass.py +++ b/gprMax/cmds_geometry/add_grass.py @@ -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}.') diff --git a/gprMax/cmds_geometry/add_surface_roughness.py b/gprMax/cmds_geometry/add_surface_roughness.py index 87edff26..70c66370 100644 --- a/gprMax/cmds_geometry/add_surface_roughness.py +++ b/gprMax/cmds_geometry/add_surface_roughness.py @@ -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}.') diff --git a/gprMax/cmds_geometry/add_surface_water.py b/gprMax/cmds_geometry/add_surface_water.py index 8a10acba..f07efd97 100644 --- a/gprMax/cmds_geometry/add_surface_water.py +++ b/gprMax/cmds_geometry/add_surface_water.py @@ -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}.') diff --git a/gprMax/cmds_geometry/box.py b/gprMax/cmds_geometry/box.py index 5059f4cf..8bd56cec 100644 --- a/gprMax/cmds_geometry/box.py +++ b/gprMax/cmds_geometry/box.py @@ -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}.") diff --git a/gprMax/cmds_geometry/cylinder.py b/gprMax/cmds_geometry/cylinder.py index 66d9253f..710de8e2 100644 --- a/gprMax/cmds_geometry/cylinder.py +++ b/gprMax/cmds_geometry/cylinder.py @@ -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}.") diff --git a/gprMax/cmds_geometry/cylindrical_sector.py b/gprMax/cmds_geometry/cylindrical_sector.py index 8e7d59a0..9992b7b0 100644 --- a/gprMax/cmds_geometry/cylindrical_sector.py +++ b/gprMax/cmds_geometry/cylindrical_sector.py @@ -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.") diff --git a/gprMax/cmds_geometry/edge.py b/gprMax/cmds_geometry/edge.py index 74a92ef3..9df66d1b 100644 --- a/gprMax/cmds_geometry/edge.py +++ b/gprMax/cmds_geometry/edge.py @@ -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.') diff --git a/gprMax/cmds_geometry/fractal_box.py b/gprMax/cmds_geometry/fractal_box.py index 9bf6fb7d..ac853fc0 100644 --- a/gprMax/cmds_geometry/fractal_box.py +++ b/gprMax/cmds_geometry/fractal_box.py @@ -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) diff --git a/gprMax/cmds_geometry/fractal_box_builder.py b/gprMax/cmds_geometry/fractal_box_builder.py index 5c240ae1..ce72cc78 100644 --- a/gprMax/cmds_geometry/fractal_box_builder.py +++ b/gprMax/cmds_geometry/fractal_box_builder.py @@ -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: diff --git a/gprMax/cmds_geometry/geometry_objects_read.py b/gprMax/cmds_geometry/geometry_objects_read.py index 15313642..d56615d6 100644 --- a/gprMax/cmds_geometry/geometry_objects_read.py +++ b/gprMax/cmds_geometry/geometry_objects_read.py @@ -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}.') diff --git a/gprMax/cmds_geometry/plate.py b/gprMax/cmds_geometry/plate.py index 5f29031a..24274151 100644 --- a/gprMax/cmds_geometry/plate.py +++ b/gprMax/cmds_geometry/plate.py @@ -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.") diff --git a/gprMax/cmds_geometry/sphere.py b/gprMax/cmds_geometry/sphere.py index 518cdf05..8255a678 100644 --- a/gprMax/cmds_geometry/sphere.py +++ b/gprMax/cmds_geometry/sphere.py @@ -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}.") diff --git a/gprMax/cmds_geometry/triangle.py b/gprMax/cmds_geometry/triangle.py index 742f582d..94dd2aca 100644 --- a/gprMax/cmds_geometry/triangle.py +++ b/gprMax/cmds_geometry/triangle.py @@ -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.") diff --git a/gprMax/cmds_multiple.py b/gprMax/cmds_multiple.py index adecadf4..819d5fc1 100644 --- a/gprMax/cmds_multiple.py +++ b/gprMax/cmds_multiple.py @@ -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?') diff --git a/gprMax/cmds_single_use.py b/gprMax/cmds_single_use.py index e6ee764b..2319ecdf 100644 --- a/gprMax/cmds_single_use.py +++ b/gprMax/cmds_single_use.py @@ -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) diff --git a/gprMax/config.py b/gprMax/config.py index 6783a992..8a2c151a 100644 --- a/gprMax/config.py +++ b/gprMax/config.py @@ -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'} -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'} - +# 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'} + 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'} + 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 diff --git a/gprMax/config_parser.py b/gprMax/config_parser.py new file mode 100755 index 00000000..a14ceaec --- /dev/null +++ b/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 . + +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) diff --git a/gprMax/contexts.py b/gprMax/contexts.py index 6a902689..72263d83 100644 --- a/gprMax/contexts.py +++ b/gprMax/contexts.py @@ -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 diff --git a/gprMax/gprMax.py b/gprMax/gprMax.py index 2bae5c48..19e6923e 100644 --- a/gprMax/gprMax.py +++ b/gprMax/gprMax.py @@ -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() diff --git a/gprMax/grid.py b/gprMax/grid.py index 1274af48..522c9c3f 100644 --- a/gprMax/grid.py +++ b/gprMax/grid.py @@ -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)))) diff --git a/gprMax/hash_cmds_file.py b/gprMax/hash_cmds_file.py index 5a20f273..8e317002 100644 --- a/gprMax/hash_cmds_file.py +++ b/gprMax/hash_cmds_file.py @@ -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) diff --git a/gprMax/materials.py b/gprMax/materials.py index 70e14f52..8b5f9cac 100644 --- a/gprMax/materials.py +++ b/gprMax/materials.py @@ -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 diff --git a/gprMax/model_build_run.py b/gprMax/model_build_run.py index f713801e..f38b0e27 100644 --- a/gprMax/model_build_run.py +++ b/gprMax/model_build_run.py @@ -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): diff --git a/gprMax/pml.py b/gprMax/pml.py index 3fc63ab7..e4716ca5 100644 --- a/gprMax/pml.py +++ b/gprMax/pml.py @@ -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) diff --git a/gprMax/snapshots.py b/gprMax/snapshots.py index f27677e0..1b57fec6 100644 --- a/gprMax/snapshots.py +++ b/gprMax/snapshots.py @@ -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('\n'.encode('utf-8')) - self.filehandle.write(f'\n'.encode('utf-8')) - self.filehandle.write(f'\n'.encode('utf-8')) + self.filehandle.write(f'\n'.encode('utf-8')) + self.filehandle.write(f'\n'.encode('utf-8')) elif self.fieldoutputs['electric']: self.filehandle.write('\n'.encode('utf-8')) - self.filehandle.write(f'\n'.encode('utf-8')) + self.filehandle.write(f'\n'.encode('utf-8')) elif self.fieldoutputs['magnetic']: self.filehandle.write('\n'.encode('utf-8')) - self.filehandle.write(f'\n'.encode('utf-8')) + self.filehandle.write(f'\n'.encode('utf-8')) self.filehandle.write('\n\n\n\n_'.encode('utf-8')) diff --git a/gprMax/solvers.py b/gprMax/solvers.py index fed2038a..9746f688 100644 --- a/gprMax/solvers.py +++ b/gprMax/solvers.py @@ -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 diff --git a/gprMax/sources.py b/gprMax/sources.py index 7c2c13f3..22725b50 100644 --- a/gprMax/sources.py +++ b/gprMax/sources.py @@ -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) diff --git a/gprMax/utilities.py b/gprMax/utilities.py index cc06de60..dc1b1b16 100644 --- a/gprMax/utilities.py +++ b/gprMax/utilities.py @@ -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