From aba5e749c57db42f1563de414f52c67a61d7a66f Mon Sep 17 00:00:00 2001 From: craig-warren Date: Thu, 9 Apr 2020 11:52:20 +0100 Subject: [PATCH] Updates to exceptions. --- gprMax/cmds_multiple.py | 327 +++++++++++++++--------- gprMax/config.py | 10 +- gprMax/cython/yee_cell_setget_rigid.pxd | 1 + gprMax/exceptions.py | 43 ---- gprMax/gprMax.py | 14 +- gprMax/hash_cmds_geometry.py | 1 + gprMax/subgrids/__init__.py | 0 gprMax/subgrids/subgrid_hsg.py | 5 +- gprMax/subgrids/updates.py | 6 +- gprMax/subgrids/user_objects.py | 19 +- gprMax/user_inputs.py | 7 +- gprMax/utilities.py | 58 ++++- 12 files changed, 289 insertions(+), 202 deletions(-) delete mode 100644 gprMax/exceptions.py create mode 100644 gprMax/subgrids/__init__.py diff --git a/gprMax/cmds_multiple.py b/gprMax/cmds_multiple.py index eb14b284..e988091f 100644 --- a/gprMax/cmds_multiple.py +++ b/gprMax/cmds_multiple.py @@ -22,7 +22,6 @@ import gprMax.config as config import numpy as np from .cmds_geometry.cmds_geometry import UserObjectGeometry -from .exceptions import CmdInputError, GeneralError from .geometry_outputs import GeometryObjects as GeometryObjectsUser from .materials import DispersiveMaterial as DispersiveMaterialUser from .materials import Material as MaterialUser @@ -92,14 +91,18 @@ class Waveform(UserObjectMulti): freq = self.kwargs['freq'] ID = self.kwargs['id'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires exactly four parameters") + logger.exception(self.params_str() + ' requires exactly four parameters') + raise if wavetype not in WaveformUser.types: - raise CmdInputError(f"'{self.params_str()}' must have one of the following types {','.join(WaveformUser.types)}") + logger.exception(self.params_str() + f" must have one of the following types {','.join(WaveformUser.types)}") + raise ValueError if freq <= 0: - raise CmdInputError(f"'{self.params_str()}' requires an excitation frequency value of greater than zero") + logger.exception(self.params_str() + ' requires an excitation frequency value of greater than zero') + raise ValueError if any(x.ID == ID for x in grid.waveforms): - raise CmdInputError(f"'{self.params_str()}' with ID {ID} already exists") + logger.exception(self.params_str() + f' with ID {ID} already exists') + raise ValueError w = WaveformUser() w.ID = ID @@ -141,26 +144,33 @@ class VoltageSource(UserObjectMulti): resistance = self.kwargs['resistance'] waveform_id = self.kwargs['waveform_id'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires at least six parameters") + logger.exception(self.params_str() + ' requires at least six parameters') + raise # Check polarity & position parameters if polarisation not in ('x', 'y', 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be x, y, or z") - if '2D TMx' in grid.mode and (polarisation == 'y' or polarisation == 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be x in 2D TMx mode") - elif '2D TMy' in grid.mode and (polarisation == 'x' or polarisation == 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be y in 2D TMy mode") - elif '2D TMz' in grid.mode and (polarisation == 'x' or polarisation == 'y'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be z in 2D TMz mode") + logger.exception(self.params_str() + ' polarisation must be x, y, or z') + raise ValueError + if '2D TMx' in config.get_model_config().mode and (polarisation == 'y' or polarisation == 'z'): + logger.exception(self.params_str() + ' polarisation must be x in 2D TMx mode') + raise ValueError + elif '2D TMy' in config.get_model_config().mode and (polarisation == 'x' or polarisation == 'z'): + logger.exception(self.params_str() + ' polarisation must be y in 2D TMy mode') + raise ValueError + elif '2D TMz' in config.get_model_config().mode and (polarisation == 'x' or polarisation == 'y'): + logger.exception(self.params_str() + ' polarisation must be z in 2D TMz mode') + raise ValueError xcoord, ycoord, zcoord = uip.check_src_rx_point(p1, self.params_str()) if resistance < 0: - raise CmdInputError(f"'{self.params_str()}' requires a source resistance of zero or greater") + logger.exception(self.params_str() + ' requires a source resistance of zero or greater') + raise ValueError # Check if there is a waveformID in the waveforms list if not any(x.ID == waveform_id for x in grid.waveforms): - raise CmdInputError(f"'{self.params_str()}' there is no waveform with the identifier {waveform_id}") + logger.exception(self.params_str() + f' there is no waveform with the identifier {waveform_id}') + raise ValueError v = VoltageSourceUser() v.polarisation = polarisation @@ -176,11 +186,14 @@ class VoltageSource(UserObjectMulti): stop = self.kwargs['stop'] # Check source start & source remove time parameters if start < 0: - raise CmdInputError(f"'{self.params_str()}' delay of the initiation of the source should not be less than zero") + logger.exception(self.params_str() + ' delay of the initiation of the source should not be less than zero') + raise ValueError if stop < 0: - raise CmdInputError(f"'{self.params_str()}' time to remove the source should not be less than zero") + logger.exception(self.params_str() + ' time to remove the source should not be less than zero') + raise ValueError if stop - start <= 0: - raise CmdInputError(f"'{self.params_str()}' duration of the source should not be zero or less") + logger.exception(self.params_str() + ' duration of the source should not be zero or less') + raise ValueError v.start = start if stop > grid.timewindow: v.stop = grid.timewindow @@ -226,23 +239,29 @@ class HertzianDipole(UserObjectMulti): p1 = self.kwargs['p1'] waveform_id = self.kwargs['waveform_id'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires at least 3 parameters") + logger.exception(self.params_str() + ' requires at least 3 parameters') + raise # Check polarity & position parameters if polarisation not in ('x', 'y', 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be x, y, or z") + logger.exception(self.params_str() + ' polarisation must be x, y, or z') + raise ValueError if '2D TMx' in config.get_model_config().mode and (polarisation == 'y' or polarisation == 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be x in 2D TMx mode") + logger.exception(self.params_str() + ' polarisation must be x in 2D TMx mode') + raise ValueError elif '2D TMy' in config.get_model_config().mode and (polarisation == 'x' or polarisation == 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be y in 2D TMy mode") + logger.exception(self.params_str() + ' polarisation must be y in 2D TMy mode') + raise ValueError elif '2D TMz' in config.get_model_config().mode and (polarisation == 'x' or polarisation == 'y'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be z in 2D TMz mode") + logger.exception(self.params_str() + ' polarisation must be z in 2D TMz mode') + raise ValueError xcoord, ycoord, zcoord = uip.check_src_rx_point(p1, self.params_str()) # Check if there is a waveformID in the waveforms list if not any(x.ID == waveform_id for x in grid.waveforms): - raise CmdInputError(f"'{self.params_str()}' there is no waveform with the identifier {waveform_id}") + logger.exception(self.params_str() + f' there is no waveform with the identifier {waveform_id}') + raise ValueError h = HertzianDipoleUser() h.polarisation = polarisation @@ -269,11 +288,14 @@ class HertzianDipole(UserObjectMulti): start = self.kwargs['start'] stop = self.kwargs['stop'] if start < 0: - raise CmdInputError(f"'{self.params_str()}' delay of the initiation of the source should not be less than zero") + logger.exception(self.params_str() + ' delay of the initiation of the source should not be less than zero') + raise ValueError if stop < 0: - raise CmdInputError(f"'{self.params_str()}' time to remove the source should not be less than zero") + logger.exception(self.params_str() + ' time to remove the source should not be less than zero') + raise ValueError if stop - start <= 0: - raise CmdInputError(f"'{self.params_str()}' duration of the source should not be zero or less") + logger.exception(self.params_str() + ' duration of the source should not be zero or less') + raise ValueError h.start = start if stop > grid.timewindow: h.stop = grid.timewindow @@ -322,23 +344,29 @@ class MagneticDipole(UserObjectMulti): p1 = self.kwargs['p1'] waveform_id = self.kwargs['waveform_id'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires at least five parameters") + logger.exception(self.params_str() + ' requires at least five parameters') + raise # Check polarity & position parameters if polarisation not in ('x', 'y', 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be x, y, or z") - if '2D TMx' in grid.mode and (polarisation == 'y' or polarisation == 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be x in 2D TMx mode") - elif '2D TMy' in grid.mode and (polarisation == 'x' or polarisation == 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be y in 2D TMy mode") - elif '2D TMz' in grid.mode and (polarisation == 'x' or polarisation == 'y'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be z in 2D TMz mode") + logger.exception(self.params_str() + ' polarisation must be x, y, or z') + raise ValueError + if '2D TMx' in config.get_model_config().mode and (polarisation == 'y' or polarisation == 'z'): + logger.exception(self.params_str() + ' polarisation must be x in 2D TMx mode') + raise ValueError + elif '2D TMy' in config.get_model_config().mode and (polarisation == 'x' or polarisation == 'z'): + logger.exception(self.params_str() + ' polarisation must be y in 2D TMy mode') + raise ValueError + elif '2D TMz' in config.get_model_config().mode and (polarisation == 'x' or polarisation == 'y'): + logger.exception(self.params_str() + ' polarisation must be z in 2D TMz mode') + raise ValueError xcoord, ycoord, zcoord = uip.check_src_rx_point(p1, self.params_str()) # Check if there is a waveformID in the waveforms list if not any(x.ID == waveform_id for x in grid.waveforms): - raise CmdInputError(f"'{self.params_str()}' there is no waveform with the identifier {waveform_id}") + logger.exception(self.params_str() + f' there is no waveform with the identifier {waveform_id}') + raise ValueError m = MagneticDipoleUser() m.polarisation = polarisation @@ -356,11 +384,14 @@ class MagneticDipole(UserObjectMulti): start = self.kwargs['start'] stop = self.kwargs['stop'] if start < 0: - raise CmdInputError(f"'{self.params_str()}' delay of the initiation of the source should not be less than zero") + logger.exception(self.params_str() + ' delay of the initiation of the source should not be less than zero') + raise ValueError if stop < 0: - raise CmdInputError(f"'{self.params_str()}' time to remove the source should not be less than zero") + logger.exception(self.params_str() + ' time to remove the source should not be less than zero') + raise ValueError if stop - start <= 0: - raise CmdInputError(f"'{self.params_str()}' duration of the source should not be zero or less") + logger.exception(self.params_str() + ' duration of the source should not be zero or less') + raise ValueError m.start = start if stop > grid.timewindow: m.stop = grid.timewindow @@ -409,30 +440,38 @@ class TransmissionLine(UserObjectMulti): waveform_id = self.kwargs['waveform_id'] resistance = self.kwargs['resistance'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires at least six parameters") + logger.exception(self.params_str() + ' requires at least six parameters') + raise # Warn about using a transmission line on GPU if config.sim_config.general['cuda']: - raise CmdInputError(f"'{self.params_str()}' A #transmission_line cannot currently be used with GPU solving. Consider using a #voltage_source instead.") + logger.exception(self.params_str() + ' A #transmission_line cannot currently be used with GPU solving. Consider using a #voltage_source instead.') + raise ValueError # Check polarity & position parameters if polarisation not in ('x', 'y', 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be x, y, or z") - if '2D TMx' in grid.mode and (polarisation == 'y' or polarisation == 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be x in 2D TMx mode") - elif '2D TMy' in grid.mode and (polarisation == 'x' or polarisation == 'z'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be y in 2D TMy mode") - elif '2D TMz' in grid.mode and (polarisation == 'x' or polarisation == 'y'): - raise CmdInputError(f"'{self.params_str()}' polarisation must be z in 2D TMz mode") + logger.exception(self.params_str() + ' polarisation must be x, y, or z') + raise ValueError + if '2D TMx' in config.get_model_config().mode and (polarisation == 'y' or polarisation == 'z'): + logger.exception(self.params_str() + ' polarisation must be x in 2D TMx mode') + raise ValueError + elif '2D TMy' in config.get_model_config().mode and (polarisation == 'x' or polarisation == 'z'): + logger.exception(self.params_str() + ' polarisation must be y in 2D TMy mode') + raise ValueError + elif '2D TMz' in config.get_model_config().mode and (polarisation == 'x' or polarisation == 'y'): + logger.exception(self.params_str() + ' polarisation must be z in 2D TMz mode') + raise ValueError xcoord, ycoord, zcoord = uip.check_src_rx_point(p1, self.params_str()) if resistance <= 0 or resistance >= config.sim_config.em_consts['z0']: - raise CmdInputError(f"'{self.params_str()}' requires a resistance greater than zero and less than the impedance of free space, i.e. 376.73 Ohms") + logger.exception(self.params_str() + ' requires a resistance greater than zero and less than the impedance of free space, i.e. 376.73 Ohms') + raise ValueError # Check if there is a waveformID in the waveforms list if not any(x.ID == waveform_id for x in grid.waveforms): - raise CmdInputError(f"'{self.params_str()}' there is no waveform with the identifier {waveform_id}") + logger.exception(self.params_str() + f' there is no waveform with the identifier {waveform_id}') + raise ValueError t = TransmissionLineUser(grid) t.polarisation = polarisation @@ -448,11 +487,14 @@ class TransmissionLine(UserObjectMulti): start = self.kwargs['start'] stop = self.kwargs['stop'] if start < 0: - raise CmdInputError(f"'{self.params_str()}' delay of the initiation of the source should not be less than zero") + logger.exception(self.params_str() + ' delay of the initiation of the source should not be less than zero') + raise ValueError if stop < 0: - raise CmdInputError(f"'{self.params_str()}' time to remove the source should not be less than zero") + logger.exception(self.params_str() + ' time to remove the source should not be less than zero') + raise ValueError if stop - start <= 0: - raise CmdInputError(f"'{self.params_str()}' duration of the source should not be zero or less") + logger.exception(self.params_str() + ' duration of the source should not be zero or less') + raise ValueError t.start = start if stop > grid.timewindow: t.stop = grid.timewindow @@ -496,7 +538,8 @@ class Rx(UserObjectMulti): try: p1 = self.kwargs['p1'] except KeyError: - logger.exception(f'KeyError with {self.params_str()}') + logger.exception(self.params_str()) + raise p = uip.check_src_rx_point(p1, self.params_str()) @@ -521,7 +564,8 @@ class Rx(UserObjectMulti): if field in allowableoutputs: r.outputs[field] = np.zeros(grid.iterations, dtype=config.sim_config.dtypes['float_or_double']) else: - raise CmdInputError(f"'{self.params_str()}' contains an output type that is not allowable. Allowable outputs in current context are {allowableoutputs}") + logger.exception(self.params_str() + f' contains an output type that is not allowable. Allowable outputs in current context are {allowableoutputs}') + raise ValueError logger.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.") @@ -552,31 +596,37 @@ class RxArray(UserObjectMulti): p2 = self.kwargs['p2'] dl = self.kwargs['dl'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires exactly 9 parameters") + logger.exception(self.params_str() + ' requires exactly 9 parameters') + raise xs, ys, zs = uip.check_src_rx_point(p1, self.params_str(), 'lower') xf, yf, zf = uip.check_src_rx_point(p2, self.params_str(), 'upper') dx, dy, dz = uip.discretise_point(dl) if xs > xf or ys > yf or zs > zf: - raise CmdInputError(f"'{self.params_str()}' the lower coordinates should be less than the upper coordinates") + logger.exception(self.params_str() + ' the lower coordinates should be less than the upper coordinates') + raise ValueError if dx < 0 or dy < 0 or dz < 0: - raise CmdInputError(f"'{self.params_str()}' the step size should not be less than zero") + logger.exception(self.params_str() + ' the step size should not be less than zero') + raise ValueError if dx < 1: if dx == 0: dx = 1 else: - raise CmdInputError(f"'{self.params_str()}' the step size should not be less than the spatial discretisation") + logger.exception(self.params_str() + ' the step size should not be less than the spatial discretisation') + raise ValueError if dy < 1: if dy == 0: dy = 1 else: - raise CmdInputError(f"'{self.params_str()}' the step size should not be less than the spatial discretisation") + logger.exception(self.params_str() + ' the step size should not be less than the spatial discretisation') + raise ValueError if dz < 1: if dz == 0: dz = 1 else: - raise CmdInputError(f"'{self.params_str()}' the step size should not be less than the spatial discretisation") + logger.exception(self.params_str() + ' the step size should not be less than the spatial discretisation') + raise ValueError logger.info(f'Receiver array {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 steps {dx * grid.dx:g}m, {dy * grid.dy:g}m, {dz * grid.dz:g}m') @@ -621,14 +671,16 @@ class Snapshot(UserObjectMulti): def create(self, grid, uip): if isinstance(grid, SubGridBase): - raise CmdInputError(f"'{self.params_str()}' do not add Snapshots to Subgrids.") + logger.exception(self.params_str() + ' do not add Snapshots to Subgrids.') + raise ValueError try: p1 = self.kwargs['p1'] p2 = self.kwargs['p2'] dl = self.kwargs['dl'] filename = self.kwargs['filename'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires exactly 11 parameters") + logger.exception(self.params_str() + ' requires exactly 11 parameters') + raise p1, p2 = uip.check_box_points(p1, p2, self.params_str()) xs, ys, zs = p1 @@ -643,24 +695,31 @@ class Snapshot(UserObjectMulti): try: time = self.kwargs['time'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires exactly 5 parameters") + logger.exception(self.params_str() + ' requires exactly 5 parameters') + raise if time > 0: iterations = round_value((time / grid.dt)) + 1 else: - raise CmdInputError(f"'{self.params_str()}' time value must be greater than zero") + logger.exception(self.params_str() + ' time value must be greater than zero') + raise ValueError if dx < 0 or dy < 0 or dz < 0: - raise CmdInputError(f"'{self.params_str()}' the step size should not be less than zero") + logger.exception(self.params_str() + ' the step size should not be less than zero') + raise ValueError if dx < 1 or dy < 1 or dz < 1: - raise CmdInputError(f"'{self.params_str()}' the step size should not be less than the spatial discretisation") + logger.exception(self.params_str() + ' the step size should not be less than the spatial discretisation') + raise ValueError if iterations <= 0 or iterations > grid.iterations: - raise CmdInputError(f"'{self.params_str()}' time value is not valid") + logger.exception(self.params_str() + ' time value is not valid') + raise ValueError + logger.debug('Is this snapshop subgrid code required?') # Replace with old style snapshots if there are subgrids #if grid.subgrids: # from .snapshot_subgrid import Snapshot as SnapshotSub # s = SnapshotSub(xs, ys, zs, xf, yf, zf, dx, dy, dz, iterations, filename) #else: + s = SnapshotUser(xs, ys, zs, xf, yf, zf, dx, dy, dz, iterations, filename) logger.info(f'Snapshot 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, discretisation {dx * grid.dx:g}m, {dy * grid.dy:g}m, {dz * grid.dz:g}m, at {s.time * grid.dt:g} secs with filename {s.filename} created.') @@ -694,22 +753,28 @@ class Material(UserObjectMulti): sm = self.kwargs['sm'] material_id = self.kwargs['id'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires exactly five parameters") + logger.exception(self.params_str() + ' requires exactly five parameters') + raise if er < 1: - raise CmdInputError(f"'{self.params_str()}' requires a positive value of one or greater for static (DC) permittivity") + logger.exception(self.params_str() + ' requires a positive value of one or greater for static (DC) permittivity') + raise ValueError if se != 'inf': se = float(se) if se < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for conductivity") + logger.exception(self.params_str() + ' requires a positive value for conductivity') + raise ValueError else: se = float('inf') if mr < 1: - raise CmdInputError(f"'{self.params_str()}' requires a positive value of one or greater for permeability") + logger.exception(self.params_str() + ' requires a positive value of one or greater for permeability') + raise ValueError if sm < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for magnetic conductivity") + logger.exception(self.params_str() + ' requires a positive value for magnetic conductivity') + raise ValueError if any(x.ID == material_id for x in grid.materials): - raise CmdInputError(f"'{self.params_str()}' with ID {material_id} already exists") + logger.exception(self.params_str() + f' with ID {material_id} already exists') + raise ValueError # Create a new instance of the Material class material (start index after pec & free_space) m = MaterialUser(len(grid.materials), material_id) @@ -753,17 +818,20 @@ class AddDebyeDispersion(UserObjectMulti): tau = self.kwargs['tau'] material_ids = self.kwargs['material_ids'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires at least four parameters") + logger.exception(self.params_str() + ' requires at least four parameters') + raise if poles < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for number of poles") + logger.exception(self.params_str() + ' requires a positive value for number of poles') + raise ValueError # Look up requested materials in existing list of material instances materials = [y for x in material_ids for y in grid.materials if y.ID == x] if len(materials) != len(material_ids): notfound = [x for x in material_ids if x not in materials] - raise CmdInputError(f"'{self.params_str()}' material(s) {notfound} do not exist") + logger.exception(self.params_str() + f' material(s) {notfound} do not exist') + raise ValueError for material in materials: disp_material = DispersiveMaterialUser(material.numID, material.ID) @@ -780,7 +848,8 @@ class AddDebyeDispersion(UserObjectMulti): disp_material.deltaer.append(er_delta[i]) disp_material.tau.append(tau[i]) else: - raise CmdInputError(f"'{self.params_str()}' requires positive values for the permittivity difference.") + logger.exception(self.params_str() + ' requires positive values for the permittivity difference.') + raise ValueError if disp_material.poles > config.get_model_config().materials['maxpoles']: config.get_model_config().materials['maxpoles'] = disp_material.poles @@ -818,17 +887,20 @@ class AddLorentzDispersion(UserObjectMulti): alpha = self.kwargs['delta'] material_ids = self.kwargs['material_ids'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires at least five parameters") + logger.exception(self.params_str() + ' requires at least five parameters') + raise if poles < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for number of poles") + logger.exception(self.params_str() + ' requires a positive value for number of poles') + raise ValueError # Look up requested materials in existing list of material instances materials = [y for x in material_ids for y in grid.materials if y.ID == x] if len(materials) != len(material_ids): notfound = [x for x in material_ids if x not in materials] - raise CmdInputError(f"'{self.params_str()}' material(s) {notfound} do not exist") + logger.exception(self.params_str() + f' material(s) {notfound} do not exist') + raise ValueError for material in materials: disp_material = DispersiveMaterialUser(material.numID, material.ID) @@ -845,7 +917,8 @@ class AddLorentzDispersion(UserObjectMulti): disp_material.tau.append(tau[i]) disp_material.alpha.append(alpha[i]) else: - raise CmdInputError(f"'{self.params_str()}' requires positive values for the permittivity difference and frequencies, and associated times that are greater than the time step for the model.") + logger.exception(self.params_str() + ' requires positive values for the permittivity difference and frequencies, and associated times that are greater than the time step for the model.') + raise ValueError if disp_material.poles > config.get_model_config().materials['maxpoles']: config.get_model_config().materials['maxpoles'] = disp_material.poles @@ -880,17 +953,20 @@ class AddDrudeDispersion(UserObjectMulti): alpha = self.kwargs['alpha'] material_ids = self.kwargs['material_ids'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires at least four parameters") + logger.exception(self.params_str() + ' requires at least four parameters') + raise if poles < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for number of poles") + logger.exception(self.params_str() + ' requires a positive value for number of poles') + raise ValueError # Look up requested materials in existing list of material instances materials = [y for x in material_ids for y in grid.materials if y.ID == x] if len(materials) != len(material_ids): notfound = [x for x in material_ids if x not in materials] - raise CmdInputError(f"'{self.params_str()}' material(s) {notfound} do not exist") + logger.exception(self.params_str() + f' material(s) {notfound} do not exist') + raise ValueError for material in materials: disp_material = DispersiveMaterialUser(material.numID, material.ID) @@ -906,7 +982,8 @@ class AddDrudeDispersion(UserObjectMulti): disp_material.tau.append(tau[i]) disp_material.alpha.append(alpha[i]) else: - raise CmdInputError(f"'{self.params_str()}' requires positive values for the frequencies, and associated times that are greater than the time step for the model.") + logger.exception(self.params_str() + ' requires positive values for the frequencies, and associated times that are greater than the time step for the model.') + raise ValueError if disp_material.poles > config.get_model_config().materials['maxpoles']: config.get_model_config().materials['maxpoles'] = disp_material.poles @@ -948,22 +1025,30 @@ class SoilPeplinski(UserObjectMulti): water_fraction_upper = self.kwargs['water_fraction_upper'] ID = self.kwargs['id'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires at exactly seven parameters") + logger.exception(self.params_str() + ' requires at exactly seven parameters') + raise if sand_fraction < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for the sand fraction") + logger.exception(self.params_str() + ' requires a positive value for the sand fraction') + raise ValueError if clay_fraction < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for the clay fraction") + logger.exception(self.params_str() + ' requires a positive value for the clay fraction') + raise ValueError if bulk_density < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for the bulk density") + logger.exception(self.params_str() + ' requires a positive value for the bulk density') + raise ValueError if sand_density < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for the sand particle density") + logger.exception(self.params_str() + ' requires a positive value for the sand particle density') + raise ValueError if water_fraction_lower < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for the lower limit of the water volumetric fraction") + logger.exception(self.params_str() + ' requires a positive value for the lower limit of the water volumetric fraction') + raise ValueError if water_fraction_upper < 0: - raise CmdInputError(f"'{self.params_str()}' requires a positive value for the upper limit of the water volumetric fraction") + logger.exception(self.params_str() + ' requires a positive value for the upper limit of the water volumetric fraction') + raise ValueError if any(x.ID == ID for x in grid.mixingmodels): - raise CmdInputError(f"'{self.params_str()}' with ID {ID} already exists") + logger.exception(self.params_str() + f' with ID {ID} already exists') + raise ValueError # Create a new instance of the Material class material (start index after pec & free_space) s = PeplinskiSoilUser(ID, sand_fraction, clay_fraction, bulk_density, sand_density, (water_fraction_lower, water_fraction_upper)) @@ -1002,9 +1087,11 @@ class GeometryView(UserObjectMulti): self.kwargs['multi_grid'] # there is no voxel output for multi grid output if isinstance(grid, SubGridBase): - raise CmdInputError(f"'{self.params_str()}' do not add multi_grid output to subgrid user object. Please add to Scene") + logger.exception(self.params_str() + ' do not add multi_grid output to subgrid user object. Please add to Scene') + raise ValueError if output_type == 'n': - raise CmdInputError(f"'{self.params_str()}' voxel output type (n) is not supported for multigrid output.") + logger.exception(self.params_str() + ' voxel output type (n) is not supported for multigrid output.') + raise ValueError # Change constructor to the multi grid output from .geometry_outputs import GeometryViewFineMultiGrid as GeometryViewUser self.multi_grid = True @@ -1022,7 +1109,8 @@ class GeometryView(UserObjectMulti): output_type = self.kwargs['output_type'].lower() filename = self.kwargs['filename'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires exactly eleven parameters") + logger.exception(self.params_str() + ' requires exactly eleven parameters') + raise GeometryViewUser = self.geometry_view_constructor(grid, output_type) @@ -1033,15 +1121,20 @@ class GeometryView(UserObjectMulti): dx, dy, dz = uip.discretise_point(dl) if dx < 0 or dy < 0 or dz < 0: - raise CmdInputError(f"'{self.params_str()}' the step size should not be less than zero") + logger.exception(self.params_str() + ' the step size should not be less than zero') + raise ValueError if dx > grid.nx or dy > grid.ny or dz > grid.nz: - raise CmdInputError(f"'{self.params_str()}' the step size should be less than the domain size") + logger.exception(self.params_str() + ' the step size should be less than the domain size') + raise ValueError if dx < 1 or dy < 1 or dz < 1: - raise CmdInputError(f"'{self.params_str()}' the step size should not be less than the spatial discretisation") + logger.exception(self.params_str() + ' the step size should not be less than the spatial discretisation') + raise ValueError if output_type != 'n' and output_type != 'f': - raise CmdInputError(f"'{self.params_str()}' requires type to be either n (normal) or f (fine)") + logger.exception(self.params_str() + ' requires type to be either n (normal) or f (fine)') + raise ValueError if output_type == 'f' and (dx * grid.dx != grid.dx or dy * grid.dy != grid.dy or dz * grid.dz != grid.dz): - raise CmdInputError(f"'{self.params_str()}' requires the spatial discretisation for the geometry view to be the same as the model for geometry view of type f (fine)") + logger.exception(self.params_str() + ' requires the spatial discretisation for the geometry view to be the same as the model for geometry view of type f (fine)') + raise ValueError # Set type of geometry file if output_type == 'n': @@ -1080,7 +1173,8 @@ class GeometryObjectsWrite(UserObjectMulti): p2 = self.kwargs['p2'] basefilename = self.kwargs['filename'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires exactly seven parameters") + logger.exception(self.params_str() + ' requires exactly seven parameters') + raise p1, p2 = uip.check_box_points(p1, p2, self.params_str()) x0, y0, z0 = p1 @@ -1131,7 +1225,8 @@ class PMLCFS(UserObjectMulti): self.hash = '#pml_cfs' PMLCFS.count += 1 if PMLCFS.count == 2: - raise CmdInputError(f"'{self.params_str()}' can only be used up to two times, for up to a 2nd order PML") + logger.exception(self.params_str() + ' can only be used up to two times, for up to a 2nd order PML') + raise ValueError def create(self, grid, uip): try: @@ -1148,16 +1243,21 @@ class PMLCFS(UserObjectMulti): sigmamin = self.kwargs['sigmamin'] sigmamax = self.kwargs['sigmamax'] except KeyError: - raise CmdInputError(f"'{self.params_str()}' requires exactly twelve parameters") + logger.exception(self.params_str() + ' requires exactly twelve parameters') + raise if alphascalingprofile not in CFSParameter.scalingprofiles.keys() or kappascalingprofile not in CFSParameter.scalingprofiles.keys() or sigmascalingprofile not in CFSParameter.scalingprofiles.keys(): - raise CmdInputError(f"'{self.params_str()}' must have scaling type {','.join(CFSParameter.scalingprofiles.keys())}") + logger.exception(self.params_str() + f" must have scaling type {','.join(CFSParameter.scalingprofiles.keys())}") + raise ValueError if alphascalingdirection not in CFSParameter.scalingdirections or kappascalingdirection not in CFSParameter.scalingdirections or sigmascalingdirection not in CFSParameter.scalingdirections: - raise CmdInputError(f"'{self.params_str()}' must have scaling type {','.join(CFSParameter.scalingdirections)}") + logger.exception(self.params_str() + f" must have scaling type {','.join(CFSParameter.scalingdirections)}") + raise ValueError if float(alphamin) < 0 or float(alphamax) < 0 or float(kappamin) < 0 or float(kappamax) < 0 or float(sigmamin) < 0: - raise CmdInputError(f"'{self.params_str()}' minimum and maximum scaling values must be greater than zero") + logger.exception(self.params_str() + ' minimum and maximum scaling values must be greater than zero') + raise ValueError if float(kappamin) < 1: - raise CmdInputError(f"'{self.params_str()}' minimum scaling value for kappa must be greater than or equal to one") + logger.exception(self.params_str() + ' minimum scaling value for kappa must be greater than or equal to one') + raise ValueError cfsalpha = CFSParameter() cfsalpha.ID = 'alpha' @@ -1191,8 +1291,7 @@ class PMLCFS(UserObjectMulti): class Subgrid(UserObjectMulti): - """ - """ + """""" def __init__(self, **kwargs): super().__init__(**kwargs) self.children_multiple = [] @@ -1204,12 +1303,12 @@ class Subgrid(UserObjectMulti): elif isinstance(node, UserObjectGeometry): self.children_geometry.append(node) else: - raise GeneralError('This object is unknown to gprMax') + logger.exception('This object is unknown to gprMax') + raise ValueError class SubgridHSG(UserObjectMulti): - """ - """ + """""" def __init__(self, **kwargs): super().__init__(**kwargs) logger.debug('Is this required?') diff --git a/gprMax/config.py b/gprMax/config.py index a878bacb..a6c753a7 100644 --- a/gprMax/config.py +++ b/gprMax/config.py @@ -28,7 +28,6 @@ from scipy.constants import c from scipy.constants import epsilon_0 as e0 from scipy.constants import mu_0 as m0 -from .exceptions import GeneralError from .utilities import detect_gpus, get_host_info, get_terminal_width logger = logging.getLogger(__name__) @@ -187,7 +186,8 @@ class SimulationConfig: self.args = args if args.mpi and args.geometry_fixed: - raise GeneralError('The geometry fixed option cannot be used with MPI.') + logger.exception('The geometry fixed option cannot be used with MPI.') + raise # General settings for the simulation # inputfilepath: path to inputfile location @@ -237,7 +237,8 @@ class SimulationConfig: # Double precision should be used with subgrid for best accuracy self.general['precision'] = 'double' if self.general['cuda']: - raise GeneralError('The CUDA-based solver cannot currently be used with models that contain sub-grids.') + logger.exception('The CUDA-based solver cannot currently be used with models that contain sub-grids.') + raise except AttributeError: self.general['subgrid'] = False @@ -271,7 +272,8 @@ class SimulationConfig: return gpu if not found: - raise GeneralError(f'GPU with device ID {deviceID} does not exist') + logger.exception(f'GPU with device ID {deviceID} does not exist') + raise def _set_precision(self): """Data type (precision) for electromagnetic field output. diff --git a/gprMax/cython/yee_cell_setget_rigid.pxd b/gprMax/cython/yee_cell_setget_rigid.pxd index d65422e4..efa8da47 100644 --- a/gprMax/cython/yee_cell_setget_rigid.pxd +++ b/gprMax/cython/yee_cell_setget_rigid.pxd @@ -19,6 +19,7 @@ import numpy as np cimport numpy as np + # Get and set functions for the rigid electric component array. The rigid array # is 4D with the 1st dimension holding the 12 electric edge components of a # cell - Ex1, Ex2, Ex3, Ex4, Ey1, Ey2, Ey3, Ey4, Ez1, Ez2, Ez3, Ez4 diff --git a/gprMax/exceptions.py b/gprMax/exceptions.py deleted file mode 100644 index 28817a6c..00000000 --- a/gprMax/exceptions.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 logging - -from colorama import Fore, init -init() - -logger = logging.getLogger(__name__) - - -class GeneralError(ValueError): - """Handles general errors. Subclasses the ValueError class.""" - def __init__(self, message, *args): - self.message = message - super(GeneralError, self).__init__(message, *args) - logger.exception(Fore.RED) - - -class CmdInputError(Exception): - """Handles errors in user specified commands. Subclasses the ValueError - class. - """ - pass - # def __init__(self, message, *args): - # self.message = message - # super(CmdInputError, self).__init__(message, *args) - # logger.exception(Fore.RED) diff --git a/gprMax/gprMax.py b/gprMax/gprMax.py index 54b4f642..7182c66a 100644 --- a/gprMax/gprMax.py +++ b/gprMax/gprMax.py @@ -25,7 +25,9 @@ from .contexts import Context, MPIContext from .utilities import setup_logging logger = logging.getLogger(__name__) -setup_logging(level=20) +level = 20 +setup_logging(level=level) + def run( scenes=None, @@ -126,10 +128,7 @@ def run( args.geometry_fixed = geometry_fixed args.write_processed = write_processed - try: - run_main(args) - except Exception: - logger.exception('Error from main API function', exc_info=True) + run_main(args) def main(): @@ -159,10 +158,7 @@ def main(): 'in the original input file have been processed') args = parser.parse_args() - try: - run_main(args) - except Exception: - logger.exception('Error from main CLI function', exc_info=True) + run_main(args) def run_main(args): diff --git a/gprMax/hash_cmds_geometry.py b/gprMax/hash_cmds_geometry.py index 275a123f..87128678 100644 --- a/gprMax/hash_cmds_geometry.py +++ b/gprMax/hash_cmds_geometry.py @@ -162,6 +162,7 @@ def process_geometrycmds(geometry): # Uniaxial anisotropic case elif len(tmp) == 11: cylinder = Cylinder(p1=p1, p2=p2, r=r, material_ids=tmp[8:]) + else: raise CmdInputError("'" + ' '.join(tmp) + "'" + ' too many parameters have been given') diff --git a/gprMax/subgrids/__init__.py b/gprMax/subgrids/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gprMax/subgrids/subgrid_hsg.py b/gprMax/subgrids/subgrid_hsg.py index 5758cb7b..bdaed9e4 100644 --- a/gprMax/subgrids/subgrid_hsg.py +++ b/gprMax/subgrids/subgrid_hsg.py @@ -18,15 +18,12 @@ import gprMax.config as config from colorama import Fore, Style, init - +init() from ..cython.fields_updates_hsg import (cython_update_electric_os, cython_update_is, cython_update_magnetic_os) from .base import SubGridBase -init() - - class SubGridHSG(SubGridBase): diff --git a/gprMax/subgrids/updates.py b/gprMax/subgrids/updates.py index 01267032..fbb02155 100644 --- a/gprMax/subgrids/updates.py +++ b/gprMax/subgrids/updates.py @@ -18,12 +18,11 @@ import logging -from ..exceptions import GeneralError from ..updates import CPUUpdates from .precursor_nodes import PrecursorNodes, PrecursorNodesFiltered from .subgrid_hsg import SubGridHSG -log = logging.getLogger(__name__) +logger = logging.getLogger(__name__) def create_updates(G): @@ -37,7 +36,8 @@ def create_updates(G): elif sg_type == SubGridHSG and not sg.filter: precursors = PrecursorNodes(G, sg) else: - raise GeneralError(str(sg) + ' is not a subgrid type') + logger.exception(str(sg) + ' is not a subgrid type') + raise ValueError sgu = SubgridUpdater(sg, precursors, G) updaters.append(sgu) diff --git a/gprMax/subgrids/user_objects.py b/gprMax/subgrids/user_objects.py index aa75b8ae..f669c60e 100644 --- a/gprMax/subgrids/user_objects.py +++ b/gprMax/subgrids/user_objects.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 from copy import copy import numpy as np @@ -23,10 +24,11 @@ from gprMax import config from ..cmds_geometry.cmds_geometry import UserObjectGeometry from ..cmds_multiple import Rx, UserObjectMulti -from ..exceptions import CmdInputError from .multi import ReferenceRx as ReferenceRxUser from .subgrid_hsg import SubGridHSG as SubGridHSGUser +logger = logging.getLogger(__name__) + class SubGridBase(UserObjectMulti): """Class to allow UserObjectMulti and UserObjectGeometry to be nested @@ -46,7 +48,8 @@ class SubGridBase(UserObjectMulti): elif isinstance(node, UserObjectGeometry): self.children_geometry.append(node) else: - raise Exception(str(node) + ' This Object can not be added to a sub grid') + logger.exception(str(node) + ' this Object can not be added to a sub grid') + raise ValueError def set_discretisation(self, sg, grid): """Set the spatial discretisation.""" @@ -105,7 +108,8 @@ class SubGridBase(UserObjectMulti): except CmdInputError: es_f = 'The subgrid should extend at least {} cells' es = es_f.format(sg.is_os_sep * 2) - raise CmdInputError(cmd_str, es) + logger.exception(cmd_str, es) + raise ValueError """ self.set_working_region_cells(sg) @@ -128,7 +132,8 @@ class SubGridBase(UserObjectMulti): # Dont mix and match different subgrids for sg_made in grid.subgrids: if type(sg) != type(sg_made): - raise CmdInputError(self.__str__() + ' Please only use one type of subgrid') + logger.exception(self.__str__() + ' please only use one type of subgrid') + raise ValueError # Reference the sub grid under the main grid to which it belongs. grid.subgrids.append(sg) @@ -201,13 +206,11 @@ class ReferenceRx(Rx): self.constructor = ReferenceRxUser def create(self, grid, uip): - r = super().create(grid, uip) - try: ratio = self.kwargs['ratio'] r.ratio = ratio r.offset = ratio // 2 - except KeyError: - raise CmdInputError(f"'{self.__str__()}' has an no ratio parameter") + logger.exception(self.__str__() + ' has an no ratio parameter') + raise diff --git a/gprMax/user_inputs.py b/gprMax/user_inputs.py index facf6df6..27a3f676 100644 --- a/gprMax/user_inputs.py +++ b/gprMax/user_inputs.py @@ -71,10 +71,11 @@ class UserInput: # Incorrect index i = p[v.index(err.args[0])] if name: - s = f"'{cmd_str}' the {err.args[0]} {name}-coordinate {i * dl:g} is not within the model domain" + s = f"\n'{cmd_str}' {err.args[0]} {name}-coordinate {i * dl:g} is not within the model domain" else: - s = f"'{cmd_str}' {err.args[0]}-coordinate {i * dl:g} is not within the model domain" - raise CmdInputError(logger.exception(s)) + s = f"\n'{cmd_str}' {err.args[0]}-coordinate {i * dl:g} is not within the model domain" + logger.exception(s) + raise def discretise_point(self, p): """Function to get the index of a continuous point with the grid.""" diff --git a/gprMax/utilities.py b/gprMax/utilities.py index 68341386..db0d4f57 100644 --- a/gprMax/utilities.py +++ b/gprMax/utilities.py @@ -26,6 +26,7 @@ import subprocess import sys import textwrap from contextlib import contextmanager +from copy import copy from shutil import get_terminal_size import gprMax.config as config @@ -33,7 +34,6 @@ import numpy as np import psutil from colorama import Fore, Style, init init() -from .exceptions import GeneralError try: from time import thread_time as timer_fn @@ -43,6 +43,32 @@ except ImportError: logger = logging.getLogger(__name__) +class CustomFormatter(logging.Formatter): + """Logging Formatter to add colors and count warning / errors + (https://stackoverflow.com/a/56944256).""" + + grey = "\x1b[38;21m" + yellow = "\x1b[33;21m" + red = "\x1b[31;21m" + bold_red = "\x1b[31;1m" + reset = "\x1b[0m" + # format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)" + format = "%(message)s" + + FORMATS = { + logging.DEBUG: grey + format + reset, + logging.INFO: grey + format + reset, + logging.WARNING: yellow + format + reset, + logging.ERROR: red + format + reset, + logging.CRITICAL: bold_red + format + reset + } + + def format(self, record): + log_fmt = self.FORMATS.get(record.levelno) + formatter = logging.Formatter(log_fmt) + return formatter.format(record) + + def setup_logging(level=logging.INFO, logfile=False): """Setup and configure logging. @@ -64,19 +90,19 @@ def setup_logging(level=logging.INFO, logfile=False): logger.setLevel(level) # Logging to console - mh = logging.StreamHandler(sys.stdout) - formatter = logging.Formatter('%(message)s') - mh.setLevel(level) - mh.setFormatter(formatter) - logger.addHandler(mh) + handler = logging.StreamHandler(sys.stdout) + formatter = CustomFormatter() + handler.setLevel(level) + handler.setFormatter(formatter) + logger.addHandler(handler) # Logging to file if logfile: - mh = logging.FileHandler("log_gprMax.txt", mode='w') + handler = logging.FileHandler("log_gprMax.txt", mode='w') formatter = logging.Formatter('%(asctime)s:%(name)s:%(levelname)s: %(message)s') - mh.setLevel(logging.DEBUG) - mh.setFormatter(formatter) - logger.addHandler(mh) + handler.setLevel(logging.DEBUG) + handler.setFormatter(formatter) + logger.addHandler(handler) def get_terminal_width(): @@ -405,7 +431,8 @@ def mem_check_host(mem): mem (int): Memory required (bytes). """ if mem > config.sim_config.hostinfo['ram']: - raise GeneralError(f"Memory (RAM) required ~{human_size(mem)} exceeds {human_size(config.sim_config.hostinfo['ram'], a_kilobyte_is_1024_bytes=True)} detected!\n") + 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 def mem_check_gpu_snaps(total_mem, snaps_mem): @@ -417,7 +444,8 @@ def mem_check_gpu_snaps(total_mem, snaps_mem): snaps_mem (int): Memory required for all snapshots (bytes). """ if total_mem - snaps_mem > config.get_model_config().cuda['gpu'].totalmem: - raise GeneralError(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") + 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 # If the required memory without the snapshots will fit on the GPU then # transfer and store snaphots on host @@ -513,12 +541,14 @@ def detect_gpus(): try: import pycuda.driver as drv except ImportError: - raise ImportError('To use gprMax in GPU mode the pycuda package must be installed, and you must have a NVIDIA CUDA-Enabled GPU (https://developer.nvidia.com/cuda-gpus).') + logger.exception('To use gprMax in GPU mode the pycuda package must be installed, and you must have a NVIDIA CUDA-Enabled GPU (https://developer.nvidia.com/cuda-gpus).') + raise drv.init() # Check and list any CUDA-Enabled GPUs if drv.Device.count() == 0: - raise GeneralError('No NVIDIA CUDA-Enabled GPUs detected (https://developer.nvidia.com/cuda-gpus)') + logger.exception('No NVIDIA CUDA-Enabled GPUs detected (https://developer.nvidia.com/cuda-gpus)') + raise elif 'CUDA_VISIBLE_DEVICES' in os.environ: deviceIDsavail = os.environ.get('CUDA_VISIBLE_DEVICES') deviceIDsavail = [int(s) for s in deviceIDsavail.split(',')]