From 1e64d493a2a4ee6e16f13da5148fc0086bda1f8c Mon Sep 17 00:00:00 2001 From: craig-warren Date: Thu, 9 Apr 2020 14:23:50 +0100 Subject: [PATCH] More updates to logging and handling exceptions. --- gprMax/cmds_single_use.py | 58 ++++++++++------ gprMax/contexts.py | 4 +- gprMax/grid.py | 1 - gprMax/hash_cmds_file.py | 19 +++-- gprMax/hash_cmds_geometry.py | 67 ++++++++++++------ gprMax/hash_cmds_multiuse.py | 59 +++++++++++----- gprMax/hash_cmds_singleuse.py | 29 +++++--- gprMax/model_build_run.py | 10 +-- gprMax/scene.py | 13 ++-- gprMax/updates.py | 4 +- gprMax/user_inputs.py | 6 +- gprMax/utilities.py | 6 +- tests/analytical_solutions.py | 18 +++++ tests/test_experimental.py | 4 +- tests/test_models.py | 13 ++-- tools/inputfile_old2new.py | 81 ++++++++++++---------- tools/outputfiles_merge.py | 10 ++- tools/plot_Ascan.py | 16 +++-- tools/plot_Bscan.py | 7 +- tools/plot_antenna_params.py | 7 +- tools/plot_antenna_params_UtEl.py | 7 +- tools/plot_source_wave.py | 13 ++-- user_libs/AustinManWoman/head_only_h5.py | 6 +- user_libs/antenna_patterns/initial_save.py | 18 +++-- user_libs/antenna_patterns/plot_fields.py | 16 +++-- user_libs/antennas/GSSI.py | 11 ++- user_libs/antennas/MALA.py | 8 ++- 27 files changed, 334 insertions(+), 177 deletions(-) diff --git a/gprMax/cmds_single_use.py b/gprMax/cmds_single_use.py index 10142ad2..a88d2373 100644 --- a/gprMax/cmds_single_use.py +++ b/gprMax/cmds_single_use.py @@ -27,7 +27,6 @@ init() from scipy import interpolate from scipy.constants import c -from .exceptions import CmdInputError from .utilities import round_value, set_omp_threads from .waveforms import Waveform @@ -92,10 +91,12 @@ class Domain(UserObjectSingle): try: G.nx, G.ny, G.nz = uip.discretise_point(self.kwargs['p1']) except KeyError: - raise CmdInputError(self.__str__() + ' please specify a point') + logger.exception(self.__str__() + ' please specify a point') + raise if G.nx == 0 or G.ny == 0 or G.nz == 0: - raise CmdInputError(self.__str__() + ' requires at least one cell in every dimension') + logger.exception(self.__str__() + ' requires at least one cell in every dimension') + raise ValueError logger.info(f"Domain size: {self.kwargs['p1'][0]:g} x {self.kwargs['p1'][1]:g} x {self.kwargs['p1'][2]:g}m ({G.nx:d} x {G.ny:d} x {G.nz:d} = {(G.nx * G.ny * G.nz):g} cells)") @@ -137,14 +138,18 @@ class Discretisation(UserObjectSingle): G.dl = np.array(self.kwargs['p1']) G.dx, G.dy, G.dz = self.kwargs['p1'] except KeyError: - raise CmdInputError(self.__str__() + ' discretisation requires a point') + logger.exception(self.__str__() + ' discretisation requires a point') + raise if G.dl[0] <= 0: - raise CmdInputError(self.__str__() + ' discretisation requires the x - direction spatial step to be greater than zero') + logger.exception(self.__str__() + ' discretisation requires the x-direction spatial step to be greater than zero') + raise ValueError if G.dl[1] <= 0: - raise CmdInputError(self.__str__() + ' discretisation requires the y - direction spatial step to be greater than zero') + logger.exception(self.__str__() + ' discretisation requires the y-direction spatial step to be greater than zero') + raise ValueError if G.dl[2] <= 0: - raise CmdInputError(self.__str__() + ' discretisation requires the z - direction spatial step to be greater than zero') + logger.exception(self.__str__() + ' discretisation requires the z-direction spatial step to be greater than zero') + raise ValueError logger.info(f'Spatial discretisation: {G.dl[0]:g} x {G.dl[1]:g} x {G.dl[2]:g}m') @@ -179,12 +184,14 @@ class TimeWindow(UserObjectSingle): G.timewindow = tmp G.iterations = int(np.ceil(tmp / G.dt)) + 1 else: - raise CmdInputError(self.__str__() + ' must have a value greater than zero') + logger.exception(self.__str__() + ' must have a value greater than zero') + raise ValueError except KeyError: pass if not G.timewindow: - raise CmdInputError(self.__str__() + ' specify a time or number of iterations') + logger.exception(self.__str__() + ' specify a time or number of iterations') + raise ValueError logger.info(f'Time window: {G.timewindow:g} secs ({G.iterations} iterations)') @@ -205,9 +212,11 @@ class NumThreads(UserObjectSingle): try: n = self.kwargs['n'] except KeyError: - raise CmdInputError(self.__str__() + ' requires exactly one parameter to specify the number of threads to use') + logger.exception(self.__str__() + ' requires exactly one parameter to specify the number of threads to use') + raise if n < 1: - raise CmdInputError(self.__str__() + ' requires the value to be an integer not less than one') + logger.exception(self.__str__() + ' requires the value to be an integer not less than one') + raise ValueError config.get_model_config().ompthreads = set_omp_threads(n) @@ -227,10 +236,12 @@ class TimeStepStabilityFactor(UserObjectSingle): try: f = self.kwargs['f'] except KeyError: - raise CmdInputError(self.__str__() + ' requires exactly one parameter') + logger.exception(self.__str__() + ' requires exactly one parameter') + raise 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') + logger.exception(self.__str__() + ' requires the value of the time step stability factor to be between zero and one') + raise ValueError G.dt = G.dt * f logger.info(f'Time step (modified): {G.dt:g} secs') @@ -276,7 +287,8 @@ class PMLCells(UserObjectSingle): G.pmlthickness['ymax'] = int(self.kwargs['ymax']) G.pmlthickness['zmax'] = int(self.kwargs['zmax']) except KeyError: - raise CmdInputError('#pml_cells: requires either one or six parameter(s)') + logger.exception(self.__str__() + ' requires either one or six parameter(s)') + raise if (2 * G.pmlthickness['x0'] >= G.nx or 2 * G.pmlthickness['y0'] >= G.ny or @@ -284,7 +296,8 @@ class PMLCells(UserObjectSingle): 2 * G.pmlthickness['xmax'] >= G.nx or 2 * G.pmlthickness['ymax'] >= G.ny or 2 * G.pmlthickness['zmax'] >= G.nz): - raise CmdInputError('#pml_thickness: has too many cells for the domain size') + logger.exception(self.__str__() + ' has too many cells for the domain size') + raise ValueError class SrcSteps(UserObjectSingle): @@ -303,7 +316,8 @@ class SrcSteps(UserObjectSingle): try: G.srcsteps = uip.discretise_point(self.kwargs['p1']) except KeyError: - raise CmdInputError('#src_steps: requires exactly three parameters') + logger.exception(self.__str__() + ' requires exactly three parameters') + raise logger.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.') @@ -324,7 +338,8 @@ class RxSteps(UserObjectSingle): try: G.rxsteps = uip.discretise_point(self.kwargs['p1']) except KeyError: - raise CmdInputError('#rx_steps: requires exactly three parameters') + logger.exception(self.__str__() + ' requires exactly three parameters') + raise logger.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.') @@ -359,7 +374,8 @@ class ExcitationFile(UserObjectSingle): args, varargs, keywords, defaults = inspect.getargspec(interpolate.interp1d) kwargs = dict(zip(reversed(args), reversed(defaults))) except KeyError: - raise CmdInputError('#excitation_file: requires either one or three parameter(s)') + logger.exception(self.__str__() + ' requires either one or three parameter(s)') + raise # See if file exists at specified path and if not try input file directory excitationfile = Path(excitationfile) @@ -387,7 +403,8 @@ class ExcitationFile(UserObjectSingle): for waveform in range(len(waveformIDs)): if any(x.ID == waveformIDs[waveform] for x in G.waveforms): - raise CmdInputError(f'Waveform with ID {waveformIDs[waveform]} already exists') + logger.exception(f'Waveform with ID {waveformIDs[waveform]} already exists') + raise ValueError w = Waveform() w.ID = waveformIDs[waveform] w.type = 'user' @@ -442,4 +459,5 @@ class NumberOfModelRuns(UserObjectSingle): try: grid.numberofmodelruns = self.kwargs['n'] except KeyError: - raise CmdInputError('#numberofmodelruns: requires exactly one parameter') + logger.exception(self.__str__() + ' requires exactly one parameter') + raise diff --git a/gprMax/contexts.py b/gprMax/contexts.py index d51bf13f..4410d8d3 100644 --- a/gprMax/contexts.py +++ b/gprMax/contexts.py @@ -23,7 +23,6 @@ import sys import gprMax.config as config from ._version import __version__, codename -from .exceptions import GeneralError from .model_build_run import ModelBuildRun from .solvers import create_G, create_solver from .utilities import get_terminal_width, human_size, logo, timer @@ -150,7 +149,8 @@ class MPIContext(Context): if executor.is_master(): if config.sim_config.general['cuda']: if executor.size - 1 > len(config.sim_config.cuda['gpus']): - raise GeneralError(f'Not enough GPU resources for number of MPI tasks requested. Number of MPI tasks should be equal to number of GPUs + 1.') + logger.exception('Not enough GPU resources for number of MPI tasks requested. Number of MPI tasks should be equal to number of GPUs + 1.') + raise ValueError # Create job list jobs = [] diff --git a/gprMax/grid.py b/gprMax/grid.py index 7d41bc8c..5583d59a 100644 --- a/gprMax/grid.py +++ b/gprMax/grid.py @@ -23,7 +23,6 @@ import gprMax.config as config import numpy as np from colorama import Fore, Style, init init() -from .exceptions import GeneralError from .pml import CFS, PML from .utilities import fft_power, human_size, round_value diff --git a/gprMax/hash_cmds_file.py b/gprMax/hash_cmds_file.py index b00843f2..396edb03 100644 --- a/gprMax/hash_cmds_file.py +++ b/gprMax/hash_cmds_file.py @@ -24,7 +24,6 @@ from pathlib import Path import gprMax.config as config -from .exceptions import CmdInputError from .hash_cmds_geometry import process_geometrycmds from .hash_cmds_multiuse import process_multicmds from .hash_cmds_singleuse import process_singlecmds @@ -72,7 +71,8 @@ def process_python_include_code(inputfile, usernamespace): pythoncode += inputlines[x] + '\n' x += 1 if x == len(inputlines): - raise CmdInputError('Cannot find the end of the Python code block, i.e. missing #end_python: command.') + logger.exception('Cannot find the end of the Python code block, i.e. missing #end_python: command.') + raise SyntaxError # Compile code for faster execution pythoncompiledcode = compile(pythoncode, '', 'exec') # Redirect stdout to a text stream @@ -136,7 +136,8 @@ def process_include_files(hashcmds, inputfile): includefile = hashcmds[x].split() if len(includefile) != 2: - raise CmdInputError('#include_file requires exactly one parameter') + logger.exception('#include_file requires exactly one parameter') + raise ValueError includefile = includefile[1] @@ -223,11 +224,13 @@ def check_cmd_names(processedlines, checkessential=True): # check first character of parameter string. Ignore case when there # are no parameters for a command, e.g. for #taguchi: if ' ' not in cmdparams[0] and len(cmdparams.strip('\n')) != 0: - raise CmdInputError('There must be a space between the command name and parameters in ' + processedlines[lindex]) + logger.exception('There must be a space between the command name and parameters in ' + processedlines[lindex]) + raise SyntaxError # Check if command name is valid if cmdname not in essentialcmds and cmdname not in singlecmds and cmdname not in multiplecmds and cmdname not in geometrycmds: - raise CmdInputError('Your input file contains an invalid command: ' + cmdname) + logger.exception('Your input file contains an invalid command: ' + cmdname) + raise SyntaxError # Count essential commands if cmdname in essentialcmds: @@ -238,7 +241,8 @@ def check_cmd_names(processedlines, checkessential=True): if singlecmds[cmdname] is None: singlecmds[cmdname] = cmd[1].strip(' \t\n') else: - raise CmdInputError('You can only have a single instance of ' + cmdname + ' in your model') + logger.exception('You can only have a single instance of ' + cmdname + ' in your model') + raise SyntaxError elif cmdname in multiplecmds: multiplecmds[cmdname].append(cmd[1].strip(' \t\n')) @@ -250,7 +254,8 @@ def check_cmd_names(processedlines, checkessential=True): if checkessential: if (countessentialcmds < len(essentialcmds)): - raise CmdInputError('Your input file is missing essential commands required to run a model. Essential commands are: ' + ', '.join(essentialcmds)) + logger.exception('Your input file is missing essential commands required to run a model. Essential commands are: ' + ', '.join(essentialcmds)) + raise SyntaxError return singlecmds, multiplecmds, geometry diff --git a/gprMax/hash_cmds_geometry.py b/gprMax/hash_cmds_geometry.py index 87128678..c650d6b0 100644 --- a/gprMax/hash_cmds_geometry.py +++ b/gprMax/hash_cmds_geometry.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with gprMax. If not, see . +import logging import numpy as np from .cmds_geometry.add_grass import AddGrass @@ -29,9 +30,10 @@ from .cmds_geometry.fractal_box import FractalBox from .cmds_geometry.plate import Plate from .cmds_geometry.sphere import Sphere from .cmds_geometry.triangle import Triangle -from .exceptions import CmdInputError from .utilities import round_value +logger = logging.getLogger(__name__) + def process_geometrycmds(geometry): """ @@ -55,7 +57,8 @@ def process_geometrycmds(geometry): from .cmds_geometry.geometry_objects_read import GeometryObjectsRead if len(tmp) != 6: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires exactly five parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires exactly five parameters') + raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -64,7 +67,8 @@ def process_geometrycmds(geometry): elif tmp[0] == '#edge:': if len(tmp) != 8: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires exactly seven parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires exactly seven parameters') + raise ValueError edge = Edge(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])), @@ -74,7 +78,8 @@ def process_geometrycmds(geometry): elif tmp[0] == '#plate:': if len(tmp) < 8: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters') + raise ValueError # Isotropic case if len(tmp) == 8: @@ -89,13 +94,15 @@ def process_geometrycmds(geometry): material_ids=tmp[7:]) else: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + raise ValueError scene_objects.append(plate) elif tmp[0] == '#triangle:': if len(tmp) < 12: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters') + raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6])) @@ -115,13 +122,15 @@ def process_geometrycmds(geometry): triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_ids=tmp[11:]) else: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + raise ValueError scene_objects.append(triangle) elif tmp[0] == '#box:': if len(tmp) < 8: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least seven parameters') + raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6])) @@ -139,13 +148,15 @@ def process_geometrycmds(geometry): box = Box(p1=p1, p2=p2, material_ids=tmp[7:]) else: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + raise ValueError scene_objects.append(box) elif tmp[0] == '#cylinder:': if len(tmp) < 9: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least eight parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least eight parameters') + raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6])) @@ -164,13 +175,15 @@ def process_geometrycmds(geometry): cylinder = Cylinder(p1=p1, p2=p2, r=r, material_ids=tmp[8:]) else: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + raise ValueError scene_objects.append(cylinder) elif tmp[0] == '#cylindrical_sector:': if len(tmp) < 10: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least nine parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least nine parameters') + raise ValueError normal = tmp[1].lower() ctr1 = float(tmp[2]) @@ -197,13 +210,15 @@ def process_geometrycmds(geometry): extent2=extent2, r=r, start=start, end=end, material_ids=tmp[9:]) else: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + raise ValueError scene_objects.append(cylindrical_sector) elif tmp[0] == '#sphere:': if len(tmp) < 6: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least five parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least five parameters') + raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) r = float(tmp[4]) @@ -221,7 +236,8 @@ def process_geometrycmds(geometry): sphere = Sphere(p1=p1, r=r, material_id=tmp[5:]) else: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + raise ValueError scene_objects.append(sphere) @@ -229,7 +245,8 @@ def process_geometrycmds(geometry): # Default is no dielectric smoothing for a fractal box if len(tmp) < 14: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least thirteen parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least thirteen parameters') + raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6])) @@ -246,7 +263,8 @@ def process_geometrycmds(geometry): elif len(tmp) == 16: fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, mixing_model_id=mixing_model_id, id=ID, n_materials=n_materials, seed=tmp[14], averaging=tmp[15].lower()) else: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + raise ValueError scene_objects.append(fb) @@ -256,7 +274,8 @@ def process_geometrycmds(geometry): if tmp[0] == '#add_surface_roughness:': if len(tmp) < 13: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least twelve parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least twelve parameters') + raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6])) @@ -270,13 +289,15 @@ def process_geometrycmds(geometry): elif len(tmp) == 14: asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim, weighting=weighting, limits=limits, fractal_box_id=fractal_box_id, seed=int(tmp[13])) else: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + raise ValueError scene_objects.append(asr) if tmp[0] == '#add_surface_water:': if len(tmp) != 9: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires exactly eight parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires exactly eight parameters') + raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6])) @@ -288,7 +309,8 @@ def process_geometrycmds(geometry): if tmp[0] == '#add_grass:': if len(tmp) < 12: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters') + logger.exception("'" + ' '.join(tmp) + "'" + ' requires at least eleven parameters') + raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) p2 = (float(tmp[4]), float(tmp[5]), float(tmp[6])) @@ -302,7 +324,8 @@ def process_geometrycmds(geometry): elif len(tmp) == 13: grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim, limits=limits, n_blades=n_blades, fractal_box_id=fractal_box_id, seed=int(tmp[12])) else: - raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + logger.exception("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') + raise ValueError scene_objects.append(grass) diff --git a/gprMax/hash_cmds_multiuse.py b/gprMax/hash_cmds_multiuse.py index d4ccd501..f30713dd 100644 --- a/gprMax/hash_cmds_multiuse.py +++ b/gprMax/hash_cmds_multiuse.py @@ -16,12 +16,15 @@ # You should have received a copy of the GNU General Public License # along with gprMax. If not, see . +import logging + from .cmds_multiple import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion, AddLorentzDispersion, GeometryObjectsWrite, GeometryView, HertzianDipole, MagneticDipole, Material, Rx, RxArray, Snapshot, SoilPeplinski, TransmissionLine, VoltageSource, Waveform) -from .exceptions import CmdInputError + +logger = logging.getLogger(__name__) def process_multicmds(multicmds): @@ -43,7 +46,8 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 4: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly four parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly four parameters') + raise ValueError waveform = Waveform(wave_type=tmp[0], amp=float(tmp[1]), freq=float(tmp[2]), id=tmp[3]) scene_objects.append(waveform) @@ -57,7 +61,8 @@ def process_multicmds(multicmds): elif len(tmp) == 8: voltage_source = VoltageSource(polarisation=tmp[0].lower(), p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5], start=float(tmp[6]), end=float(tmp[7])) else: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters') + raise ValueError scene_objects.append(voltage_source) @@ -66,13 +71,15 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) < 5: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters') + raise ValueError if len(tmp) == 5: hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4]) elif len(tmp) == 7: hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4], start=float(tmp[5]), end=float(tmp[6])) else: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters') + raise ValueError scene_objects.append(hertzian_dipole) @@ -81,13 +88,15 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) < 5: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters') + raise ValueError if len(tmp) == 5: magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4]) elif len(tmp) == 7: magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4], start=float(tmp[5]), end=float(tmp[6])) else: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters') + raise ValueError scene_objects.append(magnetic_dipole) @@ -96,14 +105,16 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) < 6: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least six parameters') + raise ValueError if len(tmp) == 6: tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5]) elif len(tmp) == 8: tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), resistance=float(tmp[4]), waveform_id=tmp[5], start=tmp[6], end=tmp[7]) else: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' too many parameters') + raise ValueError scene_objects.append(tl) @@ -112,7 +123,8 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 3 and len(tmp) < 5: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' has an incorrect number of parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' has an incorrect number of parameters') + raise ValueError if len(tmp) == 3: rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2]))) else: @@ -125,7 +137,8 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 9: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly nine parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly nine parameters') + raise ValueError p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2])) p2 = (float(tmp[3]), float(tmp[4]), float(tmp[5])) @@ -139,7 +152,8 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 11: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters') + raise ValueError p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2])) p2 = (float(tmp[3]), float(tmp[4]), float(tmp[5])) @@ -161,7 +175,8 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 5: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly five parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly five parameters') + raise ValueError material = Material(er=float(tmp[0]), se=float(tmp[1]), mr=float(tmp[2]), sm=float(tmp[3]), id=tmp[4]) scene_objects.append(material) @@ -172,7 +187,8 @@ def process_multicmds(multicmds): tmp = cmdinstance.split() if len(tmp) < 4: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least four parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least four parameters') + raise ValueError poles = int(tmp[0]) er_delta = [] @@ -192,7 +208,8 @@ def process_multicmds(multicmds): tmp = cmdinstance.split() if len(tmp) < 5: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters') + raise ValueError poles = int(tmp[0]) material_ids = tmp[(3 * poles) + 1:len(tmp)] @@ -214,7 +231,8 @@ def process_multicmds(multicmds): tmp = cmdinstance.split() if len(tmp) < 5: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at least five parameters') + raise ValueError poles = int(tmp[0]) material_ids = tmp[(3 * poles) + 1:len(tmp)] @@ -234,7 +252,8 @@ def process_multicmds(multicmds): tmp = cmdinstance.split() if len(tmp) != 7: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at exactly seven parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires at exactly seven parameters') + raise ValueError soil = SoilPeplinski(sand_fraction=float(tmp[0]), clay_fraction=float(tmp[1]), bulk_density=float(tmp[2]), @@ -249,7 +268,8 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 11: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly eleven parameters') + raise ValueError p1 = float(tmp[0]), float(tmp[1]), float(tmp[2]) p2 = float(tmp[3]), float(tmp[4]), float(tmp[5]) @@ -263,7 +283,8 @@ def process_multicmds(multicmds): for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 7: - raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly seven parameters') + logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' requires exactly seven parameters') + raise ValueError p1 = float(tmp[0]), float(tmp[1]), float(tmp[2]) p2 = float(tmp[3]), float(tmp[4]), float(tmp[5]) diff --git a/gprMax/hash_cmds_singleuse.py b/gprMax/hash_cmds_singleuse.py index 5d1727bd..282c95ff 100644 --- a/gprMax/hash_cmds_singleuse.py +++ b/gprMax/hash_cmds_singleuse.py @@ -16,11 +16,14 @@ # You should have received a copy of the GNU General Public License # along with gprMax. If not, see . +import logging + from .cmds_single_use import (Discretisation, Domain, ExcitationFile, NumThreads, OutputDir, PMLCells, RxSteps, SrcSteps, TimeStepStabilityFactor, TimeWindow, Title) -from .exceptions import CmdInputError + +logger = logging.getLogger(__name__) def process_singlecmds(singlecmds): @@ -53,7 +56,8 @@ def process_singlecmds(singlecmds): if singlecmds[cmd] is not None: tmp = tuple(int(x) for x in singlecmds[cmd].split()) if len(tmp) != 1: - raise CmdInputError(cmd + ' requires exactly one parameter to specify the number of threads to use') + logger.exception(cmd + ' requires exactly one parameter to specify the number of threads to use') + raise ValueError num_thread = NumThreads(n=tmp[0]) scene_objects.append(num_thread) @@ -62,7 +66,8 @@ def process_singlecmds(singlecmds): if singlecmds[cmd] is not None: tmp = [float(x) for x in singlecmds[cmd].split()] if len(tmp) != 3: - raise CmdInputError(cmd + ' requires exactly three parameters') + logger.exception(cmd + ' requires exactly three parameters') + raise ValueError dl = (tmp[0], tmp[1], tmp[2]) discretisation = Discretisation(p1=dl) @@ -72,7 +77,8 @@ def process_singlecmds(singlecmds): if singlecmds[cmd] is not None: tmp = [float(x) for x in singlecmds[cmd].split()] if len(tmp) != 3: - raise CmdInputError(cmd + ' requires exactly three parameters') + logger.exception(cmd + ' requires exactly three parameters') + raise ValueError p1 = (tmp[0], tmp[1], tmp[2]) domain = Domain(p1=p1) @@ -88,7 +94,8 @@ def process_singlecmds(singlecmds): if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) != 1: - raise CmdInputError(cmd + ' requires exactly one parameter to specify the time window. Either in seconds or number of iterations.') + logger.exception(cmd + ' requires exactly one parameter to specify the time window. Either in seconds or number of iterations.') + raise ValueError tmp = tmp[0].lower() # If number of iterations given @@ -106,7 +113,8 @@ def process_singlecmds(singlecmds): if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) != 1 and len(tmp) != 6: - raise CmdInputError(cmd + ' requires either one or six parameter(s)') + logger.exception(cmd + ' requires either one or six parameter(s)') + raise ValueError if len(tmp) == 1: pml_cells = PMLCells(thickness=int(tmp[0])) else: @@ -123,7 +131,8 @@ def process_singlecmds(singlecmds): if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) != 3: - raise CmdInputError(cmd + ' requires exactly three parameters') + logger.exception(cmd + ' requires exactly three parameters') + raise ValueError p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2])) src_steps = SrcSteps(p1=p1) @@ -133,7 +142,8 @@ def process_singlecmds(singlecmds): if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) != 3: - raise CmdInputError(cmd + ' requires exactly three parameters') + logger.exception(cmd + ' requires exactly three parameters') + raise ValueError p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2])) rx_steps = RxSteps(p1=p1) @@ -144,7 +154,8 @@ def process_singlecmds(singlecmds): if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) != 1 and len(tmp) != 3: - raise CmdInputError(cmd + ' requires either one or three parameter(s)') + logger.exception(cmd + ' requires either one or three parameter(s)') + raise ValueError if len(tmp) > 1: ex_file = ExcitationFile(filepath=tmp[0], kind=tmp[1], fill_value=tmp[2]) diff --git a/gprMax/model_build_run.py b/gprMax/model_build_run.py index 9f3d3a09..9984fd88 100644 --- a/gprMax/model_build_run.py +++ b/gprMax/model_build_run.py @@ -33,7 +33,6 @@ from tqdm import tqdm from .cython.yee_cell_build import (build_electric_components, build_magnetic_components) -from .exceptions import GeneralError from .fields_outputs import write_hdf5_outputfile from .grid import dispersion_analysis from .hash_cmds_file import parse_hash_commands @@ -85,7 +84,8 @@ class ModelBuildRun: source.ycoord + G.srcsteps[1] * config.sim_config.model_end > G.ny or source.zcoord + G.srcsteps[2] * config.sim_config.model_end < 0 or source.zcoord + G.srcsteps[2] * config.sim_config.model_end > G.nz): - raise GeneralError('Source(s) will be stepped to a position outside the domain.') + logger.exception('Source(s) will be stepped to a position outside the domain.') + raise ValueError source.xcoord = source.xcoordorigin + config.model_num * G.srcsteps[0] source.ycoord = source.ycoordorigin + config.model_num * G.srcsteps[1] source.zcoord = source.zcoordorigin + config.model_num * G.srcsteps[2] @@ -98,7 +98,8 @@ class ModelBuildRun: receiver.ycoord + G.rxsteps[1] * config.sim_config.model_end > G.ny or receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end < 0 or receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end > G.nz): - raise GeneralError('Receiver(s) will be stepped to a position outside the domain.') + logger.exception('Receiver(s) will be stepped to a position outside the domain.') + raise ValueError receiver.xcoord = receiver.xcoordorigin + config.model_num * G.rxsteps[0] receiver.ycoord = receiver.ycoordorigin + config.model_num * G.rxsteps[1] receiver.zcoord = receiver.zcoordorigin + config.model_num * G.rxsteps[2] @@ -172,7 +173,8 @@ class ModelBuildRun: if results['error']: logger.warning(Fore.RED + f"\nNumerical dispersion analysis [{gb.grid.name}] not carried out as {results['error']}" + Style.RESET_ALL) elif results['N'] < config.get_model_config().numdispersion['mingridsampling']: - raise GeneralError(f"\nNon-physical wave propagation in [{gb.grid.name}] detected. Material '{results['material'].ID}' has wavelength sampled by {results['N']} cells, less than required minimum for physical wave propagation. Maximum significant frequency estimated as {results['maxfreq']:g}Hz") + logger.exception(f"\nNon-physical wave propagation in [{gb.grid.name}] detected. Material '{results['material'].ID}' has wavelength sampled by {results['N']} cells, less than required minimum for physical wave propagation. Maximum significant frequency estimated as {results['maxfreq']:g}Hz") + raise ValueError elif (results['deltavp'] and np.abs(results['deltavp']) > config.get_model_config().numdispersion['maxnumericaldisp']): logger.warning(Fore.RED + f"\n[{gb.grid.name}] has potentially significant numerical dispersion. Estimated largest physical phase-velocity error is {results['deltavp']:.2f}% in material '{results['material'].ID}' whose wavelength sampled by {results['N']} cells. Maximum significant frequency estimated as {results['maxfreq']:g}Hz" + Style.RESET_ALL) diff --git a/gprMax/scene.py b/gprMax/scene.py index 9fa1aa03..a4bc3353 100644 --- a/gprMax/scene.py +++ b/gprMax/scene.py @@ -23,7 +23,6 @@ from .cmds_geometry.fractal_box_builder import FractalBoxBuilder from .cmds_multiple import UserObjectMulti from .cmds_single_use import (Discretisation, Domain, TimeWindow, UserObjectSingle) -from .exceptions import CmdInputError, GeneralError from .materials import create_built_in_materials from .subgrids.user_objects import SubGridBase as SubGridUserBase from .user_inputs import create_user_input_points @@ -54,7 +53,8 @@ class Scene: elif isinstance(user_object, UserObjectSingle): self.single_cmds.append(user_object) else: - raise GeneralError('This object is unknown to gprMax') + logger.exception('This object is unknown to gprMax') + raise ValueError def process_subgrid_commands(self, subgrids): # Check for subgrid user objects @@ -90,8 +90,9 @@ class Scene: # points belong to. try: obj.create(grid, uip) - except CmdInputError: + except ValueError: logger.exception('Error creating user input object') + raise return self @@ -99,13 +100,15 @@ class Scene: # Check for duplicate commands and warn user if they exist cmds_unique = list(set(self.single_cmds)) if len(cmds_unique) != len(self.single_cmds): - raise CmdInputError('Duplicate single-use commands exist in the input.') + logger.exception('Duplicate single-use commands exist in the input.') + raise ValueError # Check essential commands and warn user if missing for cmd_type in self.essential_cmds: d = any([isinstance(cmd, cmd_type) for cmd in cmds_unique]) if not d: - raise CmdInputError('Your input file is missing essential commands required to run a model. Essential commands are: Domain, Discretisation, Time Window') + logger.exception('Your input file is missing essential commands required to run a model. Essential commands are: Domain, Discretisation, Time Window') + raise ValueError self.process_cmds(cmds_unique, G) diff --git a/gprMax/updates.py b/gprMax/updates.py index fcb42451..f7bad615 100644 --- a/gprMax/updates.py +++ b/gprMax/updates.py @@ -29,7 +29,6 @@ from .cython.fields_updates_normal import \ update_electric as update_electric_cpu from .cython.fields_updates_normal import \ update_magnetic as update_magnetic_cpu -from .exceptions import GeneralError from .fields_outputs import kernel_template_store_outputs from .fields_outputs import store_outputs as store_outputs_cpu from .receivers import dtoh_rx_array, htod_rx_arrays @@ -445,7 +444,8 @@ class CUDAUpdates: # Check if coefficient arrays will fit on constant memory of GPU if (self.grid.updatecoeffsE.nbytes + self.grid.updatecoeffsH.nbytes > config.get_model_config().cuda['gpu'].constmem): - raise GeneralError(f"Too many materials in the model to fit onto constant memory of size {human_size(config.get_model_config().cuda['gpu'].constmem)} on {config.get_model_config().cuda['gpu'].deviceID} - {config.get_model_config().cuda['gpu'].name} GPU") + logger.exception(f"Too many materials in the model to fit onto constant memory of size {human_size(config.get_model_config().cuda['gpu'].constmem)} on {config.get_model_config().cuda['gpu'].deviceID} - {config.get_model_config().cuda['gpu'].name} GPU") + raise ValueError updatecoeffsE = kernelE.get_global('updatecoeffsE')[0] updatecoeffsH = kernelH.get_global('updatecoeffsH')[0] diff --git a/gprMax/user_inputs.py b/gprMax/user_inputs.py index 27a3f676..d9d00b32 100644 --- a/gprMax/user_inputs.py +++ b/gprMax/user_inputs.py @@ -22,7 +22,6 @@ import gprMax.config as config import numpy as np from colorama import Fore, Style, init init() -from .exceptions import CmdInputError from .subgrids.base import SubGridBase from .utilities import round_value @@ -103,7 +102,7 @@ class MainGridUserInput(UserInput): p = self.check_point(p, cmd_str, name) if self.grid.within_pml(p): - logger.warning(Fore.RED + f"'{cmd_str}' sources and receivers should not normally be positioned within the PML." + Style.RESET_ALL) + logger.warning(f"'{cmd_str}' sources and receivers should not normally be positioned within the PML.") return p @@ -112,7 +111,8 @@ class MainGridUserInput(UserInput): p2 = self.check_point(p2, cmd_str, name='upper') if np.greater(p1, p2).any(): - raise CmdInputError(logger.exception(f"'{cmd_str}' the lower coordinates should be less than the upper coordinates.")) + logger.exception(f"'{cmd_str}' the lower coordinates should be less than the upper coordinates.") + raise ValueError return p1, p2 diff --git a/gprMax/utilities.py b/gprMax/utilities.py index db0d4f57..9a91119c 100644 --- a/gprMax/utilities.py +++ b/gprMax/utilities.py @@ -432,7 +432,7 @@ def mem_check_host(mem): """ if mem > config.sim_config.hostinfo['ram']: logger.exception(f"Memory (RAM) required ~{human_size(mem)} exceeds {human_size(config.sim_config.hostinfo['ram'], a_kilobyte_is_1024_bytes=True)} detected!\n") - raise + raise ValueError def mem_check_gpu_snaps(total_mem, snaps_mem): @@ -445,7 +445,7 @@ def mem_check_gpu_snaps(total_mem, snaps_mem): """ if total_mem - snaps_mem > config.get_model_config().cuda['gpu'].totalmem: logger.exception(f"Memory (RAM) required ~{human_size(total_mem)} exceeds {human_size(config.get_model_config().cuda['gpu'].totalmem, a_kilobyte_is_1024_bytes=True)} detected on specified {config.get_model_config().cuda['gpu'].deviceID} - {config.get_model_config().cuda['gpu'].name} GPU!\n") - raise + raise ValueError # If the required memory without the snapshots will fit on the GPU then # transfer and store snaphots on host @@ -548,7 +548,7 @@ def detect_gpus(): # Check and list any CUDA-Enabled GPUs if drv.Device.count() == 0: logger.exception('No NVIDIA CUDA-Enabled GPUs detected (https://developer.nvidia.com/cuda-gpus)') - raise + raise ValueError elif 'CUDA_VISIBLE_DEVICES' in os.environ: deviceIDsavail = os.environ.get('CUDA_VISIBLE_DEVICES') deviceIDsavail = [int(s) for s in deviceIDsavail.split(',')] diff --git a/tests/analytical_solutions.py b/tests/analytical_solutions.py index 4ce8610a..1222daeb 100644 --- a/tests/analytical_solutions.py +++ b/tests/analytical_solutions.py @@ -1,3 +1,21 @@ +# Copyright (C) 2015-2020: 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 numpy as np import gprMax.config as config diff --git a/tests/test_experimental.py b/tests/test_experimental.py index acc96189..c7b0b3e5 100644 --- a/tests/test_experimental.py +++ b/tests/test_experimental.py @@ -23,7 +23,6 @@ from pathlib import Path import h5py import matplotlib.pyplot as plt import numpy as np -from gprMax.exceptions import CmdInputError """Plots a comparison of fields between given simulation output and experimental data files.""" @@ -50,7 +49,8 @@ else: polarity = 1 if args.output[0] not in availablecomponents: - raise CmdInputError(f"{args.output[0]} output requested to plot, but the available output for receiver 1 is {', '.join(availablecomponents)}") + logger.exception(f"{args.output[0]} output requested to plot, but the available output for receiver 1 is {', '.join(availablecomponents)}") + raise ValueError floattype = f[path + args.output[0]].dtype iterations = f.attrs['Iterations'] diff --git a/tests/test_models.py b/tests/test_models.py index eb580372..56ddd4a5 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with gprMax. If not, see . +import logging import sys from pathlib import Path @@ -25,9 +26,10 @@ import matplotlib.pyplot as plt import numpy as np from colorama import Fore, Style, init init() -from gprMax.exceptions import GeneralError from tests.analytical_solutions import hertzian_dipole_fs +logger = logging.getLogger(__name__) + if sys.platform == 'linux': plt.switch_backend('agg') @@ -92,7 +94,8 @@ for i, model in enumerate(testmodels): for ID, name in enumerate(outputstest): datatest[:, ID] = filetest[path + str(name)][:] if np.any(np.isnan(datatest[:, ID])): - raise GeneralError('Test data contains NaNs') + logger.exception('Test data contains NaNs') + raise ValueError # Tx/Rx position to feed to analytical solution rxpos = filetest[path].attrs['Position'] @@ -117,7 +120,8 @@ for i, model in enumerate(testmodels): outputsref = list(fileref[path].keys()) outputstest = list(filetest[path].keys()) if outputsref != outputstest: - raise GeneralError('Field output components do not match reference solution') + logger.exception('Field output components do not match reference solution') + raise ValueError # Check that type of float used to store fields matches if filetest[path + outputstest[0]].dtype != fileref[path + outputsref[0]].dtype: @@ -138,7 +142,8 @@ for i, model in enumerate(testmodels): dataref[:, ID] = fileref[path + str(name)][:] datatest[:, ID] = filetest[path + str(name)][:] if np.any(np.isnan(datatest[:, ID])): - raise GeneralError('Test data contains NaNs') + logger.exception('Test data contains NaNs') + raise ValueError fileref.close() filetest.close() diff --git a/tools/inputfile_old2new.py b/tools/inputfile_old2new.py index fd02a9f3..0cefc0d6 100644 --- a/tools/inputfile_old2new.py +++ b/tools/inputfile_old2new.py @@ -17,8 +17,10 @@ # along with gprMax. If not, see . import argparse +import logging + +logger = logging.getLogger(__name__) -from gprMax.exceptions import CmdInputError """Converts old to new style input files.""" @@ -40,7 +42,7 @@ except: newfile = inputfile newfile += '_v3syntax' -print("Attempting to convert inputfile '{}' to use new syntax...\n".format(inputfile)) +logger.info("Attempting to convert inputfile '{}' to use new syntax...\n".format(inputfile)) model2D = False txs = [] @@ -62,7 +64,7 @@ while(lindex < len(inputlines)): model2D = True # Syntax of old command: #dx_dy: x y replacement = '#dx_dy_dz: {:g} {:g} {:g}'.format(float(params[0]), float(params[1]), float(params[1])) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -90,7 +92,7 @@ while(lindex < len(inputlines)): if model2D: # Syntax of old command: #domain: x y replacement = '#domain: {:g} {:g} {:g}'.format(float(params[0]), float(params[1]), dx_dy_dz[2]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) domain = (float(params[0]), float(params[1]), dx_dy_dz[2]) @@ -110,7 +112,7 @@ while(lindex < len(inputlines)): elif cmdname == '#num_of_procs': # Syntax of old command: #num_of_procs: nthreads replacement = '#num_threads: {}'.format(params[0]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -139,7 +141,7 @@ while(lindex < len(inputlines)): if model2D: # Syntax of old command: #rx: x1 y1 replacement = '#rx: {} {} {}'.format(params[0], params[1], 0) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -151,7 +153,7 @@ while(lindex < len(inputlines)): else: # Syntax of old command: #rx_box: x1 y1 z1 x2 y2 z2 dx dy dz replacement = '#rx_array: {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -163,7 +165,7 @@ while(lindex < len(inputlines)): else: # Syntax of old command: #tx_steps: dx dy dz replacement = '#src_steps: {} {} {}'.format(params[0], params[1], params[2]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -175,7 +177,7 @@ while(lindex < len(inputlines)): else: # Syntax of old command: #rx_steps: dx dy dz replacement = '#rx_steps: {} {} {}'.format(params[0], params[1], params[2]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -183,12 +185,12 @@ while(lindex < len(inputlines)): elif cmdname == '#medium': # Syntax of old command: #medium: e_rs e_inf tau sig_e mu_r sig_m ID replacement = '#material: {} {} {} {} {}'.format(params[0], params[3], params[4], params[5], params[6]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) if float(params[1]) > 0: replacement = '#add_dispersion_debye: 1 {} {} {}'.format(float(params[0]) - float(params[1]), params[2], params[6]) - print("Command '{}' added.".format(replacement)) + logger.info("Command '{}' added.".format(replacement)) inputlines.insert(lindex + 1, replacement) lindex += 1 @@ -196,7 +198,7 @@ while(lindex < len(inputlines)): if model2D: # Syntax of old command: #box: x1 y1 x2 y2 ID replacement = '#box: {} {} {} {} {} {} {}'.format(params[0], params[1], 0, params[2], params[3], dx_dy_dz[2], params[4]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -209,7 +211,7 @@ while(lindex < len(inputlines)): # Syntax of old command: #triangle: x1 y1 z1 x2 y2 z2 x3 y3 z3 ID replacement = '#triangle: {} {} {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], 0, params[9]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -217,13 +219,13 @@ while(lindex < len(inputlines)): elif cmdname == '#wedge': # Syntax of old command: #wedge: x1 y1 z1 x2 y2 z2 x3 y3 z3 thickness ID replacement = '#triangle: {} {} {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 elif cmdname == '#bowtie': - print("Command '{}', is no longer supported. You can create the bowtie shape using two triangle commands.".format(inputlines[lindex])) + logger.info("Command '{}', is no longer supported. You can create the bowtie shape using two triangle commands.".format(inputlines[lindex])) inputlines.pop(lindex) elif cmdname == '#cylinder': @@ -239,7 +241,7 @@ while(lindex < len(inputlines)): elif params[0] == 'z': replacement = '#cylinder: {} {} {} {} {} {} {} {}'.format(params[3], params[4], params[1], params[3], params[4], params[2], params[5], params[6]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -247,33 +249,33 @@ while(lindex < len(inputlines)): elif cmdname == '#cylinder_new': # Syntax of old command: #cylinder_new: x1 y1 z1 x2 y2 z2 radius ID replacement = '#cylinder: {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 elif cmdname == '#cylindrical_segment': - print("Command '{}' has been removed as it is no longer supported. You can create a cylindrical segment by using a #box to cut through a #cylinder.".format(inputlines[lindex])) + logger.info("Command '{}' has been removed as it is no longer supported. You can create a cylindrical segment by using a #box to cut through a #cylinder.".format(inputlines[lindex])) inputlines.pop(lindex) elif cmdname in ['#x_segment', '#y_segment']: - print("Command '{}' has been removed. A circular segment can be created by using the #cylinder command and cutting it with a #box. Alternatively the #cylindrical_sector command maybe useful.".format(inputlines[lindex])) + logger.info("Command '{}' has been removed. A circular segment can be created by using the #cylinder command and cutting it with a #box. Alternatively the #cylindrical_sector command maybe useful.".format(inputlines[lindex])) inputlines.pop(lindex) elif cmdname == '#media_file': - print("Command '{}' has is no longer supported. Please include your materials using the #material command directly in the input file.".format(inputlines[lindex])) + logger.info("Command '{}' has is no longer supported. Please include your materials using the #material command directly in the input file.".format(inputlines[lindex])) inputlines.pop(lindex) elif cmdname == '#pml_layers': # Syntax of old command: #pml_layers: num_layers replacement = '#pml_cells: {}'.format(params[0]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 elif cmdname in ['#abc_order', '#abc_type', 'abc_optimisation_angles', '#abc_mixing_parameters', '#abc_stability_factors']: - print("Command '{}' has been removed as Higdon Absorbing Boundary Conditions (ABC) are no longer supported. The default ABC is the (better performing) Perfectly Matched Layer (PML).".format(inputlines[lindex])) + logger.info("Command '{}' has been removed as Higdon Absorbing Boundary Conditions (ABC) are no longer supported. The default ABC is the (better performing) Perfectly Matched Layer (PML).".format(inputlines[lindex])) inputlines.pop(lindex) elif cmdname == '#analysis': @@ -282,11 +284,11 @@ while(lindex < len(inputlines)): extra = " To run a model multiple times use the command line option -n, e.g. gprMax {} -n {}".format(inputfile, int(params[0])) else: extra = '' - print("Command '{}' has been removed as it is no longer required.{}".format(inputlines[lindex], extra)) + logger.info("Command '{}' has been removed as it is no longer required.{}".format(inputlines[lindex], extra)) inputlines.pop(lindex) elif cmdname in ['#end_analysis', '#number_of_media', '#nips_number']: - print("Command '{}' has been removed as it is no longer required.".format(inputlines[lindex])) + logger.info("Command '{}' has been removed as it is no longer required.".format(inputlines[lindex])) inputlines.pop(lindex) elif cmdname == '#snapshot': @@ -296,7 +298,7 @@ while(lindex < len(inputlines)): else: # Syntax of old command: #snapshot: i1 x1 y1 z1 x2 y2 z2 dx dy dz time filename type replacement = '#snapshot: {} {} {} {} {} {} {} {} {} {} {}'.format(params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -306,7 +308,7 @@ while(lindex < len(inputlines)): if params[0].endswith('.geo'): params = params[0].split('.') replacement = '#geometry_view: 0 0 0 {} {} {} {} {} {} {} n'.format(domain[0], domain[1], domain[2], dx_dy_dz[0], dx_dy_dz[1], dx_dy_dz[2], params[0]) - print("Command '{}', replaced with '{}'. This is a geometry view of the entire domain, sampled at the spatial resolution of the model, using the per Yee cell option (n). You may want to consider taking a smaller geometry view or using a coarser sampling. You may also want to use the per Yee cell edge option (f) to view finer details.".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'. This is a geometry view of the entire domain, sampled at the spatial resolution of the model, using the per Yee cell option (n). You may want to consider taking a smaller geometry view or using a coarser sampling. You may also want to use the per Yee cell edge option (f) to view finer details.".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 @@ -314,13 +316,14 @@ while(lindex < len(inputlines)): elif cmdname == '#geometry_vtk': # Syntax of old command: #geometry_vtk: x1 y1 z1 x2 y2 z2 dx dy dz filename type replacement = '#geometry_view: {} {} {} {} {} {} {} {} {} {} {}'.format(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10]) - print("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) + logger.info("Command '{}', replaced with '{}'".format(inputlines[lindex], replacement)) inputlines.pop(lindex) inputlines.insert(lindex, replacement) lindex += 1 elif cmdname in ['#plane_wave', '#thin_wire', '#huygens_surface']: - raise CmdInputError("Command '{}' has not yet implemented in the new version of gprMax. For now please continue to use the old version.".format(inputlines[lindex])) + logger.exception("Command '{}' has not yet implemented in the new version of gprMax. For now please continue to use the old version.".format(inputlines[lindex])) + raise ValueError else: lindex += 1 @@ -332,7 +335,8 @@ while(lindex < len(inputlines)): for source in linesources: params = source.split() if params[3] is badwaveforms: - raise CmdInputError("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms))) + logger.exception("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms))) + raise ValueError elif params[3] == 'ricker': params[3] = 'gaussiandotnorm' waveform = '#waveform: {} {} {} {}'.format(params[3], params[1], params[2], params[4]) @@ -343,7 +347,7 @@ for source in linesources: else: hertzian = '#hertzian_dipole: z {} {} {} {}'.format(hertziantx[1], hertziantx[2], 0, hertziantx[3]) - print("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, hertzian)) + logger.info("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, hertzian)) inputlines.remove(source) inputlines.remove(tx) inputlines.append(waveform) @@ -353,7 +357,8 @@ for source in linesources: for source in hertziandipoles: params = source.split() if params[3] is badwaveforms: - raise CmdInputError("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms))) + logger.exception("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms))) + raise ValueError elif params[3] == 'ricker': params[3] = 'gaussiandotnorm' waveform = '#waveform: {} {} {} {}'.format(params[3], params[1], params[2], params[4]) @@ -364,7 +369,7 @@ for source in hertziandipoles: else: hertzian = '#hertzian_dipole: {} {} {} {} {}'.format(hertziantx[1], hertziantx[2], hertziantx[3], hertziantx[4], hertziantx[5]) - print("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, hertzian)) + logger.info("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, hertzian)) inputlines.remove(source) inputlines.remove(tx) inputlines.append(waveform) @@ -374,7 +379,8 @@ for source in hertziandipoles: for source in voltagesources: params = source.split() if params[3] is badwaveforms: - raise CmdInputError("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms))) + logger.exception("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms))) + raise ValueError elif params[3] == 'ricker': params[3] = 'gaussiandotnorm' waveform = '#waveform: {} {} {} {}'.format(params[3], params[1], params[2], params[5]) @@ -385,7 +391,7 @@ for source in voltagesources: else: voltagesource = '#voltage_source: {} {} {} {} {} {}'.format(voltagesourcetx[1], voltagesourcetx[2], voltagesourcetx[3], voltagesourcetx[4], params[4], voltagesourcetx[5]) - print("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, voltagesource)) + logger.info("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, voltagesource)) inputlines.remove(source) inputlines.remove(tx) inputlines.append(waveform) @@ -395,7 +401,8 @@ for source in voltagesources: for source in transmissionlines: params = source.split() if params[3] is badwaveforms: - raise CmdInputError("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms))) + logger.exception("Waveform types {} are not compatible between new and old versions of gprMax.".format(''.join(badwaveforms))) + raise ValueError elif params[3] == 'ricker': params[3] = 'gaussiandotnorm' waveform = '#waveform: {} {} {} {}'.format(params[3], params[1], params[2], params[6]) @@ -406,7 +413,7 @@ for source in transmissionlines: else: transmissionline = '#transmission_line: {} {} {} {} {} {}'.format(transmissionlinetx[1], transmissionlinetx[2], transmissionlinetx[3], transmissionlinetx[4], params[5], transmissionlinetx[5]) - print("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, transmissionline)) + logger.info("Commands '{}' and '{}', replaced with '{}' and '{}'".format(source, tx, waveform, transmissionline)) inputlines.remove(source) inputlines.remove(tx) inputlines.append(waveform) @@ -419,4 +426,4 @@ with open(newinputfile, 'w') as f: for line in inputlines: f.write('{}\n'.format(line)) -print("\nWritten new input file: '{}'".format(newinputfile)) +logger.info("\nWritten new input file: '{}'".format(newinputfile)) diff --git a/tools/outputfiles_merge.py b/tools/outputfiles_merge.py index bfdaacde..e7317040 100644 --- a/tools/outputfiles_merge.py +++ b/tools/outputfiles_merge.py @@ -18,13 +18,15 @@ import argparse import glob +import logging import os from pathlib import Path import h5py import numpy as np from gprMax._version import __version__ -from gprMax.exceptions import CmdInputError + +logger = logging.getLogger(__name__) def get_output_data(filename, rxnumber, rxcomponent): @@ -47,14 +49,16 @@ def get_output_data(filename, rxnumber, rxcomponent): # Check there are any receivers if nrx == 0: - raise CmdInputError(f'No receivers found in {filename}') + logger.exception(f'No receivers found in {filename}') + raise ValueError path = '/rxs/rx' + str(rxnumber) + '/' availableoutputs = list(f[path].keys()) # Check if requested output is in file if rxcomponent not in availableoutputs: - raise CmdInputError(f"{rxcomponent} output requested to plot, but the available output for receiver 1 is {', '.join(availableoutputs)}") + logger.exception(f"{rxcomponent} output requested to plot, but the available output for receiver 1 is {', '.join(availableoutputs)}") + raise ValueError outputdata = f[path + '/' + rxcomponent] outputdata = np.array(outputdata) diff --git a/tools/plot_Ascan.py b/tools/plot_Ascan.py index 9a6a4520..ea230e51 100644 --- a/tools/plot_Ascan.py +++ b/tools/plot_Ascan.py @@ -17,16 +17,18 @@ # along with gprMax. If not, see . import argparse +import logging from pathlib import Path import h5py import matplotlib.gridspec as gridspec import matplotlib.pyplot as plt import numpy as np -from gprMax.exceptions import CmdInputError from gprMax.receivers import Rx from gprMax.utilities import fft_power +logger = logging.getLogger(__name__) + def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False): """Plots electric and magnetic fields and currents from all receiver points @@ -65,7 +67,8 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False): # Check there are any receivers if not paths: - raise CmdInputError(f'No receivers found in {file}') + logger.exception(f'No receivers found in {file}') + raise ValueError # Loop through all grids for path in paths: @@ -77,7 +80,8 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False): # Check for single output component when doing a FFT if fft: if not len(outputs) == 1: - raise CmdInputError('A single output must be specified when using the -fft option') + logger.exception('A single output must be specified when using the -fft option') + raise ValueError # New plot for each receiver for rx in range(1, nrx + 1): @@ -98,7 +102,8 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False): output = outputs[0] if output not in availableoutputs: - raise CmdInputError(f"{output} output requested to plot, but the available output for receiver 1 is {', '.join(availableoutputs)}") + logger.exception(f"{output} output requested to plot, but the available output for receiver 1 is {', '.join(availableoutputs)}") + raise ValueError outputdata = f[rxpath + output][:] * polarity @@ -196,7 +201,8 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False): # Check if requested output is in file if output not in availableoutputs: - raise CmdInputError(f"Output(s) requested to plot: {', '.join(outputs)}, but available output(s) for receiver {rx} in the file: {', '.join(availableoutputs)}") + logger.exception(f"Output(s) requested to plot: {', '.join(outputs)}, but available output(s) for receiver {rx} in the file: {', '.join(availableoutputs)}") + raise ValueError outputdata = f[rxpath + output][:] * polarity diff --git a/tools/plot_Bscan.py b/tools/plot_Bscan.py index 336cc0d0..4ca6086b 100644 --- a/tools/plot_Bscan.py +++ b/tools/plot_Bscan.py @@ -17,15 +17,17 @@ # along with gprMax. If not, see . import argparse +import logging from pathlib import Path import h5py import matplotlib.pyplot as plt import numpy as np -from gprMax.exceptions import CmdInputError from .outputfiles_merge import get_output_data +logger = logging.getLogger(__name__) + def mpl_plot(filename, outputdata, dt, rxnumber, rxcomponent): """Creates a plot (with matplotlib) of the B-scan. @@ -89,7 +91,8 @@ if __name__ == "__main__": # Check there are any receivers if nrx == 0: - raise CmdInputError(f'No receivers found in {args.outputfile}') + logger.exception(f'No receivers found in {args.outputfile}') + raise ValueError for rx in range(1, nrx + 1): outputdata, dt = get_output_data(args.outputfile, rx, args.rx_component) diff --git a/tools/plot_antenna_params.py b/tools/plot_antenna_params.py index 83f93a4d..064a2b97 100644 --- a/tools/plot_antenna_params.py +++ b/tools/plot_antenna_params.py @@ -17,13 +17,15 @@ # along with gprMax. If not, see . import argparse +import logging from pathlib import Path import h5py import matplotlib.gridspec as gridspec import matplotlib.pyplot as plt import numpy as np -from gprMax.exceptions import CmdInputError + +logger = logging.getLogger(__name__) def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=None, rxcomponent=None): @@ -81,7 +83,8 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N availableoutputs = list(f[rxpath].keys()) if rxcomponent not in availableoutputs: - raise CmdInputError(f"{rxcomponent} output requested, but the available output for receiver {rxnumber} is {', '.join(availableoutputs)}") + logger.exception(f"{rxcomponent} output requested, but the available output for receiver {rxnumber} is {', '.join(availableoutputs)}") + raise ValueError rxpath += rxcomponent diff --git a/tools/plot_antenna_params_UtEl.py b/tools/plot_antenna_params_UtEl.py index 5f24e7cb..ec28c660 100644 --- a/tools/plot_antenna_params_UtEl.py +++ b/tools/plot_antenna_params_UtEl.py @@ -17,15 +17,17 @@ # along with gprMax. If not, see . import argparse +import logging from pathlib import Path import h5py import matplotlib.gridspec as gridspec import matplotlib.pyplot as plt import numpy as np -from gprMax.exceptions import CmdInputError from scipy.io import loadmat +logger = logging.getLogger(__name__) + def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=None, rxcomponent=None): """Calculates antenna parameters - incident, reflected and total volatges and currents; s11, (s21) and input impedance. @@ -81,7 +83,8 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N availableoutputs = list(f[rxpath].keys()) if rxcomponent not in availableoutputs: - raise CmdInputError(f"{rxcomponent} output requested, but the available output for receiver {rxnumber} is {', '.join(availableoutputs)}") + logger.exception(f"{rxcomponent} output requested, but the available output for receiver {rxnumber} is {', '.join(availableoutputs)}") + raise ValueError rxpath += rxcomponent diff --git a/tools/plot_source_wave.py b/tools/plot_source_wave.py index 7d7299cd..08516bbf 100644 --- a/tools/plot_source_wave.py +++ b/tools/plot_source_wave.py @@ -17,14 +17,16 @@ # along with gprMax. If not, see . import argparse +import logging from pathlib import Path import matplotlib.pyplot as plt import numpy as np -from gprMax.exceptions import CmdInputError from gprMax.utilities import fft_power, round_value from gprMax.waveforms import Waveform +logger = logging.getLogger(__name__) + def check_timewindow(timewindow, dt): """Checks and sets time window and number of iterations. @@ -51,7 +53,8 @@ def check_timewindow(timewindow, dt): if timewindow > 0: iterations = round_value((timewindow / dt)) + 1 else: - raise CmdInputError('Time window must have a value greater than zero') + logger.exception('Time window must have a value greater than zero') + raise ValueError return timewindow, iterations @@ -164,9 +167,11 @@ if __name__ == "__main__": # Check waveform parameters if args.type.lower() not in Waveform.types: - raise CmdInputError(f"The waveform must have one of the following types {', '.join(Waveform.types)}") + logger.exception(f"The waveform must have one of the following types {', '.join(Waveform.types)}") + raise ValueError if args.freq <= 0: - raise CmdInputError('The waveform requires an excitation frequency value of greater than zero') + logger.exception('The waveform requires an excitation frequency value of greater than zero') + raise ValueError # Create waveform instance w = Waveform() diff --git a/user_libs/AustinManWoman/head_only_h5.py b/user_libs/AustinManWoman/head_only_h5.py index f57ac520..3eac0e03 100644 --- a/user_libs/AustinManWoman/head_only_h5.py +++ b/user_libs/AustinManWoman/head_only_h5.py @@ -1,8 +1,12 @@ import argparse +import logging import os import h5py +logger = logging.getLogger(__name__) + + # Parse command line arguments parser = argparse.ArgumentParser(description='Writes a HDF5 file of AustinMan or AustinWoman head only.', usage='python head_only_hdf5 filename') parser.add_argument('filename', help='name and path to (HDF5) file containing AustinMan or AustinWoman model') @@ -16,7 +20,7 @@ data = f['/data'][:, :, :] # Define head as last 1/8 of total body height nzhead = 7 * int(data.shape[2] / 8) -print('Dimensions of head model: {:g} x {:g} x {:g} cells'.format(data.shape[0], data.shape[1], data.shape[2] - nzhead)) +logger.info(f'Dimensions of head model: {data.shape[0]:g} x {data.shape[1]:g} x {data.shape[2] - nzhead:g} cells') # Write HDF5 file headfile = os.path.splitext(args.filename)[0] + '_head.h5' diff --git a/user_libs/antenna_patterns/initial_save.py b/user_libs/antenna_patterns/initial_save.py index b0fc312d..9d5762cf 100644 --- a/user_libs/antenna_patterns/initial_save.py +++ b/user_libs/antenna_patterns/initial_save.py @@ -6,6 +6,7 @@ # Please use the attribution at http://dx.doi.org/10.1016/j.sigpro.2016.04.010 import argparse +import logging import os import sys @@ -14,6 +15,9 @@ import h5py import matplotlib.pyplot as plt import numpy as np +logger = logging.getLogger(__name__) + + # Parse command line arguments parser = argparse.ArgumentParser(description='Calculate and store (in a Numpy file) field patterns from a simulation with receivers positioned in circles around an antenna.', usage='cd gprMax; python -m user_libs.antenna_patterns.initial_save outputfile') parser.add_argument('outputfile', help='name of gprMax output file including path') @@ -58,13 +62,13 @@ if epsr: wavelength = v1 / f # Print some useful information -print('Centre frequency: {} GHz'.format(f / 1e9)) +logger.info('Centre frequency: {} GHz'.format(f / 1e9)) if epsr: - print('Critical angle for Er {} is {} degrees'.format(epsr, thetac)) - print('Wavelength: {:.3f} m'.format(wavelength)) - print('Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)'.format(radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength)) - print('Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m'.format(0.62 * np.sqrt((D**3) / wavelength))) - print('Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m'.format((2 * D**2) / wavelength)) + logger.info('Critical angle for Er {} is {} degrees'.format(epsr, thetac)) + logger.info('Wavelength: {:.3f} m'.format(wavelength)) + logger.info('Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)'.format(radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength)) + logger.info('Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m'.format(0.62 * np.sqrt((D**3) / wavelength))) + logger.info('Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m'.format((2 * D**2) / wavelength)) # Load text file with coordinates of pattern origin origin = np.loadtxt(os.path.splitext(outputfile)[0] + '_rxsorigin.txt') @@ -176,4 +180,4 @@ for radius in range(0, len(radii)): # Save pattern to numpy file np.save(os.path.splitext(outputfile)[0], patternsave) -print('Written Numpy file: {}.npy'.format(os.path.splitext(outputfile)[0])) +logger.info('Written Numpy file: {}.npy'.format(os.path.splitext(outputfile)[0])) diff --git a/user_libs/antenna_patterns/plot_fields.py b/user_libs/antenna_patterns/plot_fields.py index e3ff3351..0e2a2e10 100644 --- a/user_libs/antenna_patterns/plot_fields.py +++ b/user_libs/antenna_patterns/plot_fields.py @@ -6,6 +6,7 @@ # Please use the attribution at http://dx.doi.org/10.1016/j.sigpro.2016.04.010 import argparse +import logging import os import sys @@ -13,6 +14,9 @@ import gprMax.config as config import matplotlib.pyplot as plt import numpy as np +logger = logging.getLogger(__name__) + + # Parse command line arguments parser = argparse.ArgumentParser(description='Plot field patterns from a simulation with receivers positioned in circles around an antenna. This module should be used after the field pattern data has been processed and stored using the initial_save.py module.', usage='cd gprMax; python -m user_libs.antenna_patterns.plot_fields numpyfile') parser.add_argument('numpyfile', help='name of numpy file including path') @@ -55,13 +59,13 @@ if epsr: wavelength = v1 / f # Print some useful information -print('Centre frequency: {} GHz'.format(f / 1e9)) +logger.info('Centre frequency: {} GHz'.format(f / 1e9)) if epsr: - print('Critical angle for Er {} is {} degrees'.format(epsr, thetac)) - print('Wavelength: {:.3f} m'.format(wavelength)) - print('Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)'.format(radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength)) - print('Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m'.format(0.62 * np.sqrt((D**3) / wavelength))) - print('Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m'.format((2 * D**2) / wavelength)) + logger.info('Critical angle for Er {} is {} degrees'.format(epsr, thetac)) + logger.info('Wavelength: {:.3f} m'.format(wavelength)) + logger.info('Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)'.format(radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength)) + logger.info('Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m'.format(0.62 * np.sqrt((D**3) / wavelength))) + logger.info('Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m'.format((2 * D**2) / wavelength)) # Setup figure fig = plt.figure(num=args.numpyfile, figsize=(8, 8), facecolor='w', edgecolor='w') diff --git a/user_libs/antennas/GSSI.py b/user_libs/antennas/GSSI.py index 4ffcdabc..7cc1192c 100644 --- a/user_libs/antennas/GSSI.py +++ b/user_libs/antennas/GSSI.py @@ -5,8 +5,11 @@ # # Please use the attribution at http://dx.doi.org/10.1190/1.3548506 +import logging + import gprMax -from gprMax.exceptions import CmdInputError + +logger = logging.getLogger(__name__) def antenna_like_GSSI_1500(x, y, z, resolution=0.001): @@ -59,7 +62,8 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001): patchheight = 0.016 tx = x + 0.114, y + 0.052, z + skidthickness else: - raise CmdInputError('This antenna module can only be used with a spatial discretisation of 1mm or 2mm') + logger.exception('This antenna module can only be used with a spatial discretisation of 1mm or 2mm') + raise ValueError # Specify optimisation state of antenna model optstate = ['WarrenThesis', 'DebyeAbsorber', 'GiannakisPaper'] @@ -367,7 +371,8 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.001): metalboxheight = 0.088 tx = x + 0.01 + 0.004 + 0.056, y + casethickness + 0.005 + 0.143 - 0.002, z + skidthickness else: - raise CmdInputError('This antenna module can only be used with a spatial discretisation of 0.5mm, 1mm, 2mm') + logger.exception('This antenna module can only be used with a spatial discretisation of 0.5mm, 1mm, 2mm') + raise ValueError # Material definitions absorber = gprMax.Material(er=absorberEr, se=absorbersig, mr=1, sm=0, id='absorber') diff --git a/user_libs/antennas/MALA.py b/user_libs/antennas/MALA.py index d168af95..f2068341 100644 --- a/user_libs/antennas/MALA.py +++ b/user_libs/antennas/MALA.py @@ -5,8 +5,11 @@ # # Please use the attribution at http://dx.doi.org/10.1190/1.3548506 +import logging + import gprMax -from gprMax.exceptions import CmdInputError + +logger = logging.getLogger(__name__) def antenna_like_MALA_1200(x, y, z, resolution=0.001): @@ -66,7 +69,8 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001): bowtieheight = 0.024 tx = x + 0.062, y + 0.052, z + skidthickness else: - raise CmdInputError('This antenna module can only be used with a spatial resolution of 1mm or 2mm') + logger.exception('This antenna module can only be used with a spatial resolution of 1mm or 2mm') + raise ValueError # SMD resistors - 3 on each Tx & Rx bowtie arm txres = 470 # Ohms