diff --git a/gprMax/cmds_multiuse.py b/gprMax/cmds_multiuse.py index cd39fd06..30a7aae0 100644 --- a/gprMax/cmds_multiuse.py +++ b/gprMax/cmds_multiuse.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2022: The University of Edinburgh +# Copyright (C) 2015-2022: The University of Edinburgh, United Kingdom # Authors: Craig Warren, Antonis Giannopoulos, and John Hartley # # This file is part of gprMax. @@ -16,9 +16,11 @@ # You should have received a copy of the GNU General Public License # along with gprMax. If not, see . +import inspect import logging import numpy as np +from scipy import interpolate import gprMax.config as config @@ -64,11 +66,11 @@ class UserObjectMulti: return f'{self.hash}: {s[:-1]}' def create(self, grid, uip): - """Create object and add it to the grid.""" + """Creates object and adds it to grid.""" pass def rotate(self, axis, angle, origin=None): - """Rotate object - specialised for each object.""" + """Rotates object (specialised for each object).""" pass def params_str(self): @@ -76,7 +78,7 @@ class UserObjectMulti: return self.hash + ': ' + str(self.kwargs) def grid_name(self, grid): - """Return subgrid name for use with logging info. Return an empty + """Returns subgrid name for use with logging info. Returns an empty string if the grid is the main grid. """ @@ -87,14 +89,18 @@ class UserObjectMulti: class Waveform(UserObjectMulti): - """Allows you to specify waveforms to use with sources in the model. + """Specifies waveforms to use with sources in the model. - :param wave_type: wave type (see main documentation) - :type wave_type: str, non-optional - :param amp: The scaling of the maximum amplitude of the waveform - :type amp: float, non-optional - :param freq: The centre frequency of the waveform (Hertz) - :type freq: float, non-optional + Attributes: + wave_type: string required to specify waveform type. + amp: float to scale maximum amplitude of waveform. + freq: float to specify centre frequency (Hz) of waveform. + id: string required for identifier of waveform. + user_values: optional 1D array of amplitude values to use with + user waveform. + user_time: optional 1D array of time values to use with user waveform. + kind: optional string or int, see scipy.interpolate.interp1d - https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html#scipy-interpolate-interp1d + fill_value: optional array or 'extrapolate', see scipy.interpolate.interp1d - https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html#scipy-interpolate-interp1d """ def __init__(self, **kwargs): @@ -105,49 +111,86 @@ class Waveform(UserObjectMulti): def create(self, grid, uip): try: wavetype = self.kwargs['wave_type'].lower() - amp = self.kwargs['amp'] - freq = self.kwargs['freq'] - ID = self.kwargs['id'] except KeyError: - logger.exception(self.params_str() + ' requires exactly four parameters') + logger.exception(self.params_str() + (f" must have one of the " + f"following types {','.join(WaveformUser.types)}.")) raise - if wavetype not in WaveformUser.types: - logger.exception(self.params_str() + f" must have one of the following types {','.join(WaveformUser.types)}") - raise ValueError - if freq <= 0: - 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): - logger.exception(self.params_str() + f' with ID {ID} already exists') + logger.exception(self.params_str() + (f" must have one of the " + f"following types {','.join(WaveformUser.types)}.")) raise ValueError - w = WaveformUser() - w.ID = ID - w.type = wavetype - w.amp = amp - w.freq = freq + if wavetype != 'user': + try: + amp = self.kwargs['amp'] + freq = self.kwargs['freq'] + ID = self.kwargs['id'] + except KeyError: + logger.exception(self.params_str() + (' builtin waveforms ' + 'require exactly four parameters.')) + raise + if freq <= 0: + 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): + logger.exception(self.params_str() + (f' with ID {ID} already ' + 'exists.')) + raise ValueError - logger.info(self.grid_name(grid) + f'Waveform {w.ID} of type {w.type} with maximum amplitude scaling {w.amp:g}, frequency {w.freq:g}Hz created.') + w = WaveformUser() + w.ID = ID + w.type = wavetype + w.amp = amp + w.freq = freq + + logger.info(self.grid_name(grid) + (f'Waveform {w.ID} of type ' + f'{w.type} with maximum amplitude scaling {w.amp:g}, ' + f'frequency {w.freq:g}Hz created.')) + + else: + try: + uservalues = self.kwargs['user_values'] + ID = self.kwargs['id'] + args, varargs, keywords, defaults = inspect.getargspec(interpolate.interp1d) + kwargs = dict(zip(reversed(args), reversed(defaults))) + except KeyError: + logger.exception(self.params_str() + (' a user-defined ' + 'waveform requires at least two parameters.')) + raise + + if 'user_time' in self.kwargs: + waveformtime = self.kwargs['user_time'] + else: + waveformtime = np.arange(0, grid.timewindow + grid.dt, grid.dt) + + if any(x.ID == ID for x in grid.waveforms): + logger.exception(self.params_str() + (f' with ID {ID} already ' + 'exists.')) + raise ValueError + + w = WaveformUser() + w.ID = ID + w.type = wavetype + w.userfunc = interpolate.interp1d(waveformtime, uservalues, **kwargs) + + logger.info(self.grid_name(grid) + (f'Waveform {w.ID} that is' + 'user-defined created.')) grid.waveforms.append(w) class VoltageSource(UserObjectMulti): - """Allows you to introduce a voltage source at an electric field location. + """Specifies a voltage source at an electric field location. - :param polarisation: Polarisation of the source x, y, z - :type polarisation: str, non-optional - :param p1: Position of the source x, y, z - :type p1: list, non-optional - :param resistance: Is the internal resistance of the voltage source in Ohms - :type resistance: float, non-optional - :param waveform_id: The identifier of the waveform that should be used with the source. - :type waveform_id: str, non-optional - :param start: Time to delay to start the source - :type start: float, optional - :param stop: Time to remove the source - :type stop: float, optional + Attributes: + polarisation: string required for polarisation of the source x, y, z. + p1: tuple required for position of source x, y, z. + resistance: float required for internal resistance (Ohms) of + voltage source. + waveform_id: string required for identifier of waveform used with source. + start: float optional to delay start time (secs) of source. + stop: float optional to time (secs) to remove source. """ def __init__(self, **kwargs): @@ -156,20 +199,21 @@ class VoltageSource(UserObjectMulti): self.hash = '#voltage_source' def rotate(self, axis, angle, origin=None): - """Set parameters for rotation.""" + """Sets parameters for rotation.""" self.axis = axis self.angle = angle self.origin = origin self.dorotate = True def __dorotate(self, grid): - """Perform rotation.""" + """Performs rotation.""" rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], self.kwargs['polarisation'], self.axis, self.angle, grid) - rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, self.origin) + rot_pts = rotate_2point_object(rot_pol_pts, self.axis, self.angle, + self.origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) def create(self, grid, uip): @@ -179,7 +223,8 @@ class VoltageSource(UserObjectMulti): resistance = self.kwargs['resistance'] waveform_id = self.kwargs['waveform_id'] except KeyError: - logger.exception(self.params_str() + ' requires at least six parameters') + logger.exception(self.params_str() + (' requires at least six ' + 'parameters.')) raise if self.dorotate: @@ -187,28 +232,39 @@ class VoltageSource(UserObjectMulti): # Check polarity & position parameters if polarisation not in ('x', 'y', 'z'): - logger.exception(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'): - logger.exception(self.params_str() + ' polarisation must be x in 2D TMx mode') + 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') + 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') + 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()) p2 = uip.round_to_grid_static_point(p1) if resistance < 0: - logger.exception(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): - logger.exception(self.params_str() + f' there is no waveform with the identifier {waveform_id}') + logger.exception(self.params_str() + (' there is no waveform with ' + 'the identifier ' + f'{waveform_id}.')) raise ValueError v = VoltageSourceUser() @@ -216,7 +272,8 @@ class VoltageSource(UserObjectMulti): v.xcoord = xcoord v.ycoord = ycoord v.zcoord = zcoord - v.ID = v.__class__.__name__ + '(' + str(v.xcoord) + ',' + str(v.ycoord) + ',' + str(v.zcoord) + ')' + v.ID = (v.__class__.__name__ + '(' + str(v.xcoord) + ',' + + str(v.ycoord) + ',' + str(v.zcoord) + ')') v.resistance = resistance v.waveformID = waveform_id @@ -225,20 +282,27 @@ class VoltageSource(UserObjectMulti): stop = self.kwargs['stop'] # Check source start & source remove time parameters if start < 0: - logger.exception(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: - logger.exception(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: - logger.exception(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 else: v.stop = stop - startstop = f' start time {v.start:g} secs, finish time {v.stop:g} secs ' + startstop = (f' start time {v.start:g} secs, finish time ' + f'{v.stop:g} secs ') except KeyError: v.start = 0 v.stop = grid.timewindow @@ -246,25 +310,25 @@ class VoltageSource(UserObjectMulti): v.calculate_waveform_values(grid) - logger.info(self.grid_name(grid) + f'Voltage source with polarity {v.polarisation} at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m, resistance {v.resistance:.1f} Ohms,' + startstop + f'using waveform {v.waveformID} created.') + logger.info(self.grid_name(grid) + f'Voltage source with polarity ' + f'{v.polarisation} at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m, ' + f'resistance {v.resistance:.1f} Ohms,' + startstop + + f'using waveform {v.waveformID} created.') grid.voltagesources.append(v) class HertzianDipole(UserObjectMulti): - """Allows you to specify a current density term at an electric field location - - the simplest excitation, often referred to as an additive or soft source. + """Specifies a current density term at an electric field location. - :param polarisation: Polarisation of the source x, y, z - :type polarisation: str, non-optional - :param p1: Position of the source x, y, z - :type p1: list, non-optional - :param waveform_id: The identifier of the waveform that should be used with the source. - :type waveform_id: str, non-optional - :param start: Time to delay to start the source - :type start: float, optional - :param stop: Time to remove the source - :type stop: float, optional + The simplest excitation, often referred to as an additive or soft source. + + Attributes: + polarisation: string required for polarisation of the source x, y, z. + p1: tuple required for position of source x, y, z. + waveform_id: string required for identifier of waveform used with source. + start: float optional to delay start time (secs) of source. + stop: float optional to time (secs) to remove source. """ def __init__(self, **kwargs): @@ -273,14 +337,14 @@ class HertzianDipole(UserObjectMulti): self.hash = '#hertzian_dipole' def rotate(self, axis, angle, origin=None): - """Set parameters for rotation.""" + """Sets parameters for rotation.""" self.axis = axis self.angle = angle self.origin = origin self.dorotate = True def __dorotate(self, grid): - """Perform rotation.""" + """Performs rotation.""" rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], self.kwargs['polarisation'], self.axis, @@ -295,7 +359,8 @@ class HertzianDipole(UserObjectMulti): p1 = self.kwargs['p1'] waveform_id = self.kwargs['waveform_id'] except KeyError: - logger.exception(self.params_str() + ' requires at least 3 parameters') + logger.exception(self.params_str() + ' requires at least 3 ' + + 'parameters.') raise if self.dorotate: @@ -303,16 +368,23 @@ class HertzianDipole(UserObjectMulti): # Check polarity & position parameters if polarisation not in ('x', 'y', 'z'): - logger.exception(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'): - logger.exception(self.params_str() + ' polarisation must be x in 2D TMx mode') + 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') + 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') + 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()) @@ -321,7 +393,8 @@ class HertzianDipole(UserObjectMulti): # Check if there is a waveformID in the waveforms list if not any(x.ID == waveform_id for x in grid.waveforms): - logger.exception(self.params_str() + f' there is no waveform with the identifier {waveform_id}') + logger.exception(self.params_str() + ' there is no waveform ' + + f'with the identifier {waveform_id}.') raise ValueError h = HertzianDipoleUser() @@ -341,7 +414,8 @@ class HertzianDipole(UserObjectMulti): h.xcoordorigin = xcoord h.ycoordorigin = ycoord h.zcoordorigin = zcoord - h.ID = h.__class__.__name__ + '(' + str(h.xcoord) + ',' + str(h.ycoord) + ',' + str(h.zcoord) + ')' + h.ID = (h.__class__.__name__ + '(' + str(h.xcoord) + ',' + + str(h.ycoord) + ',' + str(h.zcoord) + ')') h.waveformID = waveform_id try: @@ -349,20 +423,27 @@ class HertzianDipole(UserObjectMulti): start = self.kwargs['start'] stop = self.kwargs['stop'] if start < 0: - logger.exception(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: - logger.exception(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: - logger.exception(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 else: h.stop = stop - startstop = f' start time {h.start:g} secs, finish time {h.stop:g} secs ' + startstop = (f' start time {h.start:g} secs, finish time ' + f'{h.stop:g} secs ') except KeyError: h.start = 0 h.stop = grid.timewindow @@ -371,27 +452,30 @@ class HertzianDipole(UserObjectMulti): h.calculate_waveform_values(grid) if config.get_model_config().mode == '2D': - logger.info(self.grid_name(grid) + f'Hertzian dipole is a line source in 2D with polarity {h.polarisation} at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m,' + startstop + f'using waveform {h.waveformID} created.') + logger.info(self.grid_name(grid) + f'Hertzian dipole is a line ' + + f'source in 2D with polarity {h.polarisation} at ' + + f'{p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m,' + startstop + + f'using waveform {h.waveformID} created.') else: - logger.info(self.grid_name(grid) + f'Hertzian dipole with polarity {h.polarisation} at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m,' + startstop + f'using waveform {h.waveformID} created.') + logger.info(self.grid_name(grid) + f'Hertzian dipole with ' + + f'polarity {h.polarisation} at {p2[0]:g}m, ' + + f'{p2[1]:g}m, {p2[2]:g}m,' + startstop + + f'using waveform {h.waveformID} created.') grid.hertziandipoles.append(h) class MagneticDipole(UserObjectMulti): - """This will simulate an infinitesimal magnetic dipole. This is often referred - to as an additive or soft source. + """Simulates an infinitesimal magnetic dipole. + + Often referred to as an additive or soft source. - :param polarisation: Polarisation of the source x, y, z - :type polarisation: str, non-optional - :param p1: Position of the source x, y, z - :type p1: list, non-optional - :param waveform_id: The identifier of the waveform that should be used with the source. - :type waveform_id: str, non-optional - :param start: Time to delay to start the source - :type start: float, optional - :param stop: Time to remove the source - :type stop: float, optional + Attributes: + polarisation: string required for polarisation of the source x, y, z. + p1: tuple required for position of source x, y, z. + waveform_id: string required for identifier of waveform used with source. + start: float optional to delay start time (secs) of source. + stop: float optional to time (secs) to remove source. """ def __init__(self, **kwargs): @@ -400,14 +484,14 @@ class MagneticDipole(UserObjectMulti): self.hash = '#magnetic_dipole' def rotate(self, axis, angle, origin=None): - """Set parameters for rotation.""" + """Sets parameters for rotation.""" self.axis = axis self.angle = angle self.origin = origin self.dorotate = True def __dorotate(self, grid): - """Perform rotation.""" + """Performs rotation.""" rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], self.kwargs['polarisation'], self.axis, @@ -422,7 +506,8 @@ class MagneticDipole(UserObjectMulti): p1 = self.kwargs['p1'] waveform_id = self.kwargs['waveform_id'] except KeyError: - logger.exception(self.params_str() + ' requires at least five parameters') + logger.exception(self.params_str() + ' requires at least five ' + 'parameters.') raise if self.dorotate: @@ -430,16 +515,23 @@ class MagneticDipole(UserObjectMulti): # Check polarity & position parameters if polarisation not in ('x', 'y', 'z'): - logger.exception(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'): - logger.exception(self.params_str() + ' polarisation must be x in 2D TMx mode') + 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') + 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') + 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()) @@ -448,7 +540,8 @@ class MagneticDipole(UserObjectMulti): # Check if there is a waveformID in the waveforms list if not any(x.ID == waveform_id for x in grid.waveforms): - logger.exception(self.params_str() + f' there is no waveform with the identifier {waveform_id}') + logger.exception(self.params_str() + ' there is no waveform ' + + f'with the identifier {waveform_id}.') raise ValueError m = MagneticDipoleUser() @@ -459,7 +552,8 @@ class MagneticDipole(UserObjectMulti): m.xcoordorigin = xcoord m.ycoordorigin = ycoord m.zcoordorigin = zcoord - m.ID = m.__class__.__name__ + '(' + str(m.xcoord) + ',' + str(m.ycoord) + ',' + str(m.zcoord) + ')' + m.ID = (m.__class__.__name__ + '(' + str(m.xcoord) + ',' + + str(m.ycoord) + ',' + str(m.zcoord) + ')') m.waveformID = waveform_id try: @@ -467,20 +561,27 @@ class MagneticDipole(UserObjectMulti): start = self.kwargs['start'] stop = self.kwargs['stop'] if start < 0: - logger.exception(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: - logger.exception(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: - logger.exception(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 else: m.stop = stop - startstop = f' start time {m.start:g} secs, finish time {m.stop:g} secs ' + startstop = (f' start time {m.start:g} secs, ' + f'finish time {m.stop:g} secs ') except KeyError: m.start = 0 m.stop = grid.timewindow @@ -488,27 +589,25 @@ class MagneticDipole(UserObjectMulti): m.calculate_waveform_values(grid) - logger.info(self.grid_name(grid) + f'Magnetic dipole with polarity {m.polarisation} at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m,' + startstop + f'using waveform {m.waveformID} created.') + logger.info(self.grid_name(grid) + f'Magnetic dipole with polarity ' + + f'{m.polarisation} at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m,' + + startstop + f'using waveform {m.waveformID} created.') grid.magneticdipoles.append(m) class TransmissionLine(UserObjectMulti): - """Allows you to introduce a one-dimensional transmission line model - at an electric field location + """Specifies a one-dimensional transmission line model at an electric + field location. - :param polarisation: Polarisation of the source x, y, z - :type polarisation: str, non-optional - :param p1: Position of the source x, y, z - :type p1: list, non-optional - :param resistance: Is the internal resistance of the voltage source in Ohms - :type resistance: float, non-optional - :param waveform_id: The identifier of the waveform that should be used with the source. - :type waveform_id: str, non-optional - :param start: Time to delay to start the source - :type start: float, optional - :param stop: Time to remove the source - :type stop: float, optional + Attributes: + polarisation: string required for polarisation of the source x, y, z. + p1: tuple required for position of source x, y, z. + resistance: float required for internal resistance (Ohms) of + voltage source. + waveform_id: string required for identifier of waveform used with source. + start: float optional to delay start time (secs) of source. + stop: float optional to time (secs) to remove source. """ def __init__(self, **kwargs): @@ -517,14 +616,14 @@ class TransmissionLine(UserObjectMulti): self.hash = '#transmission_line' def rotate(self, axis, angle, origin=None): - """Set parameters for rotation.""" + """Sets parameters for rotation.""" self.axis = axis self.angle = angle self.origin = origin self.dorotate = True def __dorotate(self, grid): - """Perform rotation.""" + """Performs rotation.""" rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], self.kwargs['polarisation'], self.axis, @@ -540,7 +639,8 @@ class TransmissionLine(UserObjectMulti): waveform_id = self.kwargs['waveform_id'] resistance = self.kwargs['resistance'] except KeyError: - logger.exception(self.params_str() + ' requires at least six parameters') + logger.exception(self.params_str() + ' requires at least six ' + 'parameters.') raise if self.dorotate: @@ -548,21 +648,30 @@ class TransmissionLine(UserObjectMulti): # Warn about using a transmission line on GPU if config.sim_config.general['cuda']: - logger.exception(self.params_str() + ' A #transmission_line cannot currently be used with GPU solving. Consider using a #voltage_source instead.') + logger.exception(self.params_str() + ' 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'): - logger.exception(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'): - logger.exception(self.params_str() + ' polarisation must be x in 2D TMx mode') + 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') + 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') + 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()) @@ -570,12 +679,15 @@ class TransmissionLine(UserObjectMulti): if resistance <= 0 or resistance >= config.sim_config.em_consts['z0']: - logger.exception(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): - logger.exception(self.params_str() + f' there is no waveform with the identifier {waveform_id}') + logger.exception(self.params_str() + ' there is no waveform ' + + f'with the identifier {waveform_id}.') raise ValueError t = TransmissionLineUser(grid) @@ -583,7 +695,8 @@ class TransmissionLine(UserObjectMulti): t.xcoord = xcoord t.ycoord = ycoord t.zcoord = zcoord - t.ID = t.__class__.__name__ + '(' + str(t.xcoord) + ',' + str(t.ycoord) + ',' + str(t.zcoord) + ')' + t.ID = (t.__class__.__name__ + '(' + str(t.xcoord) + ',' + + str(t.ycoord) + ',' + str(t.zcoord) + ')') t.resistance = resistance t.waveformID = waveform_id @@ -592,20 +705,27 @@ class TransmissionLine(UserObjectMulti): start = self.kwargs['start'] stop = self.kwargs['stop'] if start < 0: - logger.exception(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: - logger.exception(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: - logger.exception(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 else: t.stop = stop - startstop = f' start time {t.start:g} secs, finish time {t.stop:g} secs ' + startstop = (f' start time {t.start:g} secs, finish time ' + f'{t.stop:g} secs ') except KeyError: t.start = 0 t.stop = grid.timewindow @@ -614,23 +734,25 @@ class TransmissionLine(UserObjectMulti): t.calculate_waveform_values(grid) t.calculate_incident_V_I(grid) - logger.info(self.grid_name(grid) + f'Transmission line with polarity {t.polarisation} at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m, resistance {t.resistance:.1f} Ohms,' + startstop + f'using waveform {t.waveformID} created.') + logger.info(self.grid_name(grid) + f'Transmission line with polarity ' + + f'{t.polarisation} at {p2[0]:g}m, {p2[1]:g}m, ' + + f'{p2[2]:g}m, resistance {t.resistance:.1f} Ohms,' + + startstop + f'using waveform {t.waveformID} created.') grid.transmissionlines.append(t) class Rx(UserObjectMulti): - """Allows you to introduce output points into the model. These are locations - where the values of the electric and magnetic field components over the number - of iterations of the model will be saved to file. . + """Specifies output points in the model. + + These are locations where the values of the electric and magnetic field + components over the numberof iterations of the model will be saved to file. - :param p1: Position of the receiver x, y, z - :type p1: list, non-optional - :param id: Identifier for the receiver - :type id: str, optional - :param outputs: is a list of outputs with this receiver. It can be any - selection from Ex, Ey, Ez, Hx, Hy, Hz, Ix, Iy, or Iz. - :type outputs: list, optional + Attributes: + p1: tuple required for position of receiver x, y, z. + id: optional string used as identifier for receiver. + outputs: optional list of outputs for receiver. It can be any + selection from Ex, Ey, Ez, Hx, Hy, Hz, Ix, Iy, or Iz. """ def __init__(self, **kwargs): @@ -640,15 +762,16 @@ class Rx(UserObjectMulti): self.constructor = RxUser def rotate(self, axis, angle, origin=None): - """Set parameters for rotation.""" + """Sets parameters for rotation.""" self.axis = axis self.angle = angle self.origin = origin self.dorotate = True def __dorotate(self, G): - """Perform rotation.""" - new_pt = (self.kwargs['p1'][0] + G.dx, self.kwargs['p1'][1] + G.dy, self.kwargs['p1'][2] + G.dz) + """Performs rotation.""" + new_pt = (self.kwargs['p1'][0] + G.dx, self.kwargs['p1'][1] + G.dy, + self.kwargs['p1'][2] + G.dz) pts = np.array([self.kwargs['p1'], new_pt]) rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin) self.kwargs['p1'] = tuple(rot_pts[0, :]) @@ -685,9 +808,11 @@ class Rx(UserObjectMulti): outputs = [self.kwargs['outputs']] except KeyError: # If no ID or outputs are specified, use default - r.ID = r.__class__.__name__ + '(' + str(r.xcoord) + ',' + str(r.ycoord) + ',' + str(r.zcoord) + ')' + r.ID = (r.__class__.__name__ + '(' + str(r.xcoord) + ',' + + str(r.ycoord) + ',' + str(r.zcoord) + ')') for key in RxUser.defaultoutputs: - r.outputs[key] = np.zeros(grid.iterations, dtype=config.sim_config.dtypes['float_or_double']) + r.outputs[key] = np.zeros(grid.iterations, + dtype=config.sim_config.dtypes['float_or_double']) else: outputs.sort() # Get allowable outputs @@ -695,12 +820,18 @@ class Rx(UserObjectMulti): # Check and add field output names for field in outputs: if field in allowableoutputs: - r.outputs[field] = np.zeros(grid.iterations, dtype=config.sim_config.dtypes['float_or_double']) + r.outputs[field] = np.zeros(grid.iterations, + dtype=config.sim_config.dtypes['float_or_double']) else: - logger.exception(self.params_str() + f' contains an output type that is not allowable. Allowable outputs in current context are {allowableoutputs}') + logger.exception(self.params_str() + ' contains an ' + 'output type that is not allowable. ' + 'Allowable outputs in current context are ' + f'{allowableoutputs}.') raise ValueError - logger.info(self.grid_name(grid) + f"Receiver at {p2[0]:g}m, {p2[1]:g}m, {p2[2]:g}m with output component(s) {', '.join(r.outputs)} created.") + logger.info(self.grid_name(grid) + f"Receiver at {p2[0]:g}m, " + f"{p2[1]:g}m, {p2[2]:g}m with output component(s) " + f"{', '.join(r.outputs)} created.") grid.rxs.append(r) @@ -708,14 +839,12 @@ class Rx(UserObjectMulti): class RxArray(UserObjectMulti): - """Provides a simple method of defining multiple output points in the model. + """Defines multiple output points in the model. - :param p1: Position of first receiver x, y, z - :type p1: list, non-optional - :param p2: Position of last receiver x, y, z - :type p2: list, non-optional - :param dl: Receiver spacing dx, dy, dz - :type dl: list, non-optional + Attributes: + p1: tuple required for position of first receiver x, y, z. + p2: tuple required for position of last receiver x, y, z. + dl: tuple required for receiver spacing dx, dy, dz. """ def __init__(self, **kwargs): @@ -729,7 +858,8 @@ class RxArray(UserObjectMulti): p2 = self.kwargs['p2'] dl = self.kwargs['dl'] except KeyError: - logger.exception(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') @@ -739,31 +869,39 @@ class RxArray(UserObjectMulti): dx, dy, dz = uip.discretise_point(dl) if xs > xf or ys > yf or zs > zf: - logger.exception(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: - logger.exception(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: - logger.exception(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: - logger.exception(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: - logger.exception(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(self.grid_name(grid) + f'Receiver array {p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m with steps {dx * grid.dx:g}m, {dy * grid.dy:g}m, {dz * grid.dz:g}m') + logger.info(self.grid_name(grid) + f'Receiver array {p3[0]:g}m, ' + f'{p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, ' + f'{p4[2]:g}m with steps {dx * grid.dx:g}m, ' + f'{dy * grid.dy:g}m, {dz * grid.dz:g}m') for x in range(xs, xf + 1, dx): for y in range(ys, yf + 1, dy): @@ -779,30 +917,33 @@ class RxArray(UserObjectMulti): p5 = np.array([x, y, z]) p5 = uip.descretised_to_continuous(p5) p5 = uip.round_to_grid_static_point(p5) - r.ID = r.__class__.__name__ + '(' + str(x) + ',' + str(y) + ',' + str(z) + ')' + r.ID = (r.__class__.__name__ + '(' + str(x) + ',' + + str(y) + ',' + str(z) + ')') for key in RxUser.defaultoutputs: r.outputs[key] = np.zeros(grid.iterations, dtype=config.sim_config.dtypes['float_or_double']) - logger.info(f" Receiver at {p5[0]:g}m, {p5[1]:g}m, {p5[2]:g}m with output component(s) {', '.join(r.outputs)} created.") + logger.info(f" Receiver at {p5[0]:g}m, {p5[1]:g}m, " + f"{p5[2]:g}m with output component(s) " + f"{', '.join(r.outputs)} created.") grid.rxs.append(r) class Snapshot(UserObjectMulti): - """Provides a simple method of defining multiple output points in the model. + """Obtains information about the electromagnetic fields within a volume + of the model at a given time instant. - :param p1: The lower left (x,y,z) coordinates of the volume of the snapshot in metres. - :type p1: list, non-optional - :param p2: the upper right (x,y,z) coordinates of the volume of the snapshot in metres. - :type p2: list, non-optional - :param dl: are the spatial discretisation of the snapshot in metres. - :type dl: list, non-optional - :param filename: is the name of the file where the snapshot will be stored - :type filename: str, non-optional - :param time: are the time in seconds (float) or the iteration number (integer) which denote the point in time at which the snapshot will be taken. - :type time: float, non-optional - :param iterations: are the iteration number (integer) which denote the point in time at which the snapshot will be taken. - :type iterations: int, non-optional - :param fileext: indicates file type for snapshot file, either 'vti' (default) or 'h5' - :type fileext: str, optional + Attributes: + p1: tuple required to specify lower left (x,y,z) coordinates of volume + of snapshot in metres. + p2: tuple required to specify upper right (x,y,z) coordinates of volume + of snapshot in metres. + dl: tuple require to specify spatial discretisation of the snapshot + in metres. + filename: string required for name of the file to store snapshot. + time/iterations: either a float for time or an int for iterations + must be specified for point in time at which the + snapshot will be taken. + fileext: optional string to indicate type for snapshot file, either + 'vti' (default) or 'h5' """ def __init__(self, **kwargs): @@ -812,7 +953,8 @@ class Snapshot(UserObjectMulti): def create(self, grid, uip): if isinstance(grid, SubGridBase): - logger.exception(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'] @@ -820,7 +962,8 @@ class Snapshot(UserObjectMulti): dl = self.kwargs['dl'] filename = self.kwargs['filename'] except KeyError: - logger.exception(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()) @@ -839,57 +982,59 @@ class Snapshot(UserObjectMulti): try: time = self.kwargs['time'] except KeyError: - logger.exception(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: - logger.exception(self.params_str() + ' time value must be greater than zero') + logger.exception(self.params_str() + ' time value must be ' + 'greater than zero.') raise ValueError try: fileext = self.kwargs['fileext'] if fileext not in SnapshotUser.fileexts: - logger.exception(f"'{fileext}' is not a valid format for a snapshot file. Valid options are: {' '.join(SnapshotUser.fileexts)}") + logger.exception(f"'{fileext}' is not a valid format for a " + "snapshot file. Valid options are: " + f"{' '.join(SnapshotUser.fileexts)}.") raise ValueError except KeyError: fileext = SnapshotUser.fileexts[0] if dx < 0 or dy < 0 or dz < 0: - logger.exception(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: - logger.exception(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: - logger.exception(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, fileext=fileext) - s = SnapshotUser(xs, ys, zs, xf, yf, zf, dx, dy, dz, iterations, filename, fileext=fileext) - - logger.info(f'Snapshot from {p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]: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}{s.fileext} will be created.') + logger.info(f'Snapshot from {p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to ' + f'{p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m, discretisation ' + f'{dx * grid.dx:g}m, {dy * grid.dy:g}m, {dz * grid.dz:g}m, ' + f'at {s.time * grid.dt:g} secs with filename ' + f'{s.filename}{s.fileext} will be created.') grid.snapshots.append(s) class Material(UserObjectMulti): - """Allows you to introduce a material into the model described by a set of constitutive parameters. + """Specifies a material in the model described by a set of constitutive + parameters. - :param er: The relative permittivity. - :type er: float, non-optional - :param se: The conductivity (Siemens/metre). - :type se: float, non-optional - :param mr: The relative permeability. - :type mr: float, non-optional - :param sm: The magnetic loss. - :type sm: float, non-optional + Attributes: + er: float required for the relative electric permittivity. + se: float required for the electric conductivity (Siemens/metre). + mr: float required for the relative magnetic permeability. + sm: float required for the magnetic loss. """ def __init__(self, **kwargs): @@ -905,30 +1050,37 @@ class Material(UserObjectMulti): sm = self.kwargs['sm'] material_id = self.kwargs['id'] except KeyError: - logger.exception(self.params_str() + ' requires exactly five parameters') + logger.exception(self.params_str() + ' requires exactly five ' + 'parameters.') raise if er < 1: - logger.exception(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: - logger.exception(self.params_str() + ' requires a positive value for conductivity') + logger.exception(self.params_str() + ' requires a positive ' + 'value for electric conductivity.') raise ValueError else: se = float('inf') if mr < 1: - logger.exception(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 magnetic permeability.') raise ValueError if sm < 0: - logger.exception(self.params_str() + ' requires a positive value for magnetic conductivity') + logger.exception(self.params_str() + ' requires a positive value ' + 'for magnetic loss.') raise ValueError if any(x.ID == material_id for x in grid.materials): - logger.exception(self.params_str() + f' 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) + # Create a new instance of the Material class material + # (start index after pec & free_space) m = MaterialUser(len(grid.materials), material_id) m.er = er m.se = se @@ -939,23 +1091,25 @@ class Material(UserObjectMulti): if m.se == float('inf'): m.averagable = False - logger.info(self.grid_name(grid) + f'Material {m.ID} with eps_r={m.er:g}, sigma={m.se:g} S/m; mu_r={m.mr:g}, sigma*={m.sm:g} Ohm/m created.') + logger.info(self.grid_name(grid) + f'Material {m.ID} with ' + f'eps_r={m.er:g}, sigma={m.se:g} S/m; mu_r={m.mr:g}, ' + f'sigma*={m.sm:g} Ohm/m created.') - # Append the new material object to the materials list grid.materials.append(m) class AddDebyeDispersion(UserObjectMulti): - """Allows you to add dispersive properties to an already defined :class:`Material` based on a multiple pole Debye formulation + """Adds dispersive properties to already defined :class:`Material` based + on multi-pole Debye formulation. - :param n_poles: The number of Debye poles. - :type n_poles: float, non-optional - :param er_delta: The difference between the zero-frequency relative permittivity and the relative permittivity at infinite frequency for each pole - :type er_delta: list, non-optional - :param tau: The relaxation time for each pole. - :type tau: list, non-optional - :param material_ids: Material ids to apply disperive properties. - :type material_ids: list, non-optional + Attributes: + n_poles: float required for number of Debye poles. + er_delta: tuple required for difference between zero-frequency relative + permittivity and relative permittivity at infinite frequency + for each pole. + tau: tuple required for relaxation time (secs) for each pole. + material_ids: list required of material ids to apply disperive + properties. """ def __init__(self, **kwargs): @@ -970,11 +1124,13 @@ class AddDebyeDispersion(UserObjectMulti): tau = self.kwargs['tau'] material_ids = self.kwargs['material_ids'] except KeyError: - logger.exception(self.params_str() + ' requires at least four parameters') + logger.exception(self.params_str() + ' requires at least four ' + 'parameters.') raise if poles < 0: - logger.exception(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 @@ -982,7 +1138,8 @@ class AddDebyeDispersion(UserObjectMulti): if len(materials) != len(material_ids): notfound = [x for x in material_ids if x not in materials] - logger.exception(self.params_str() + f' material(s) {notfound} do not exist') + logger.exception(self.params_str() + f' material(s) {notfound} do ' + 'not exist') raise ValueError for material in materials: @@ -996,11 +1153,13 @@ class AddDebyeDispersion(UserObjectMulti): disp_material.averagable = False for i in range(0, poles): if tau[i] > 0: - logger.debug('Not checking if relaxation times are greater than time-step') + logger.debug('Not checking if relaxation times are ' + 'greater than time-step.') disp_material.deltaer.append(er_delta[i]) disp_material.tau.append(tau[i]) else: - logger.exception(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 @@ -1008,22 +1167,24 @@ class AddDebyeDispersion(UserObjectMulti): # Replace original material with newly created DispersiveMaterial grid.materials = [disp_material if mat.numID==material.numID else mat for mat in grid.materials] - logger.info(self.grid_name(grid) + f"Debye disperion added to {disp_material.ID} with delta_eps_r={', '.join('%4.2f' % deltaer for deltaer in disp_material.deltaer)}, and tau={', '.join('%4.3e' % tau for tau in disp_material.tau)} secs created.") + logger.info(self.grid_name(grid) + f"Debye disperion added to {disp_material.ID} " + f"with delta_eps_r={', '.join('%4.2f' % deltaer for deltaer in disp_material.deltaer)}, " + f"and tau={', '.join('%4.3e' % tau for tau in disp_material.tau)} secs created.") class AddLorentzDispersion(UserObjectMulti): - """Allows you to add dispersive properties to an already defined :class:`Material` based on a multiple pole Lorentz formulation + """Add dispersive properties to already defined :class:`Material` based + on multi-pole Lorentz formulation. - :param n_poles: The number of Lorentz poles. - :type n_poles: float, non-optional - :param er_delta: The difference between the zero-frequency relative permittivity and the relative permittivity at infinite frequency for each pole - :type er_delta: list, non-optional - :param omega: The frequency for each pole. - :type omega: list, non-optional - :param delta: The damping coefficient (Hertz) for each pole. - :type delta: list, non-optional - :param material_ids: Material ids to apply disperive properties. - :type material_ids: list, non-optional + Attributes: + n_poles: float required for number of Lorentz poles. + er_delta: tuple required for difference between zero-frequency relative + permittivity and relative permittivity at infinite frequency + for each pole. + omega: tuple required for frequency (Hz) for each pole. + delta: tuple required for damping coefficient (Hz) for each pole. + material_ids: list required of material ids to apply disperive + properties. """ def __init__(self, **kwargs): @@ -1039,11 +1200,13 @@ class AddLorentzDispersion(UserObjectMulti): alpha = self.kwargs['delta'] material_ids = self.kwargs['material_ids'] except KeyError: - logger.exception(self.params_str() + ' requires at least five parameters') + logger.exception(self.params_str() + ' requires at least five ' + 'parameters.') raise if poles < 0: - logger.exception(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 @@ -1051,7 +1214,8 @@ class AddLorentzDispersion(UserObjectMulti): if len(materials) != len(material_ids): notfound = [x for x in material_ids if x not in materials] - logger.exception(self.params_str() + f' material(s) {notfound} do not exist') + logger.exception(self.params_str() + f' material(s) {notfound} do ' + 'not exist') raise ValueError for material in materials: @@ -1069,7 +1233,11 @@ class AddLorentzDispersion(UserObjectMulti): disp_material.tau.append(tau[i]) disp_material.alpha.append(alpha[i]) else: - 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.') + 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 @@ -1077,20 +1245,22 @@ class AddLorentzDispersion(UserObjectMulti): # Replace original material with newly created DispersiveMaterial grid.materials = [disp_material if mat.numID==material.numID else mat for mat in grid.materials] - logger.info(self.grid_name(grid) + f"Lorentz disperion added to {disp_material.ID} with delta_eps_r={', '.join('%4.2f' % deltaer for deltaer in disp_material.deltaer)}, omega={', '.join('%4.3e' % tau for tau in disp_material.tau)} secs, and gamma={', '.join('%4.3e' % alpha for alpha in disp_material.alpha)} created.") + logger.info(self.grid_name(grid) + f"Lorentz disperion added to {disp_material.ID} " + f"with delta_eps_r={', '.join('%4.2f' % deltaer for deltaer in disp_material.deltaer)}, " + f"omega={', '.join('%4.3e' % tau for tau in disp_material.tau)} secs, " + f"and gamma={', '.join('%4.3e' % alpha for alpha in disp_material.alpha)} created.") class AddDrudeDispersion(UserObjectMulti): - """Allows you to add dispersive properties to an already defined :class:`Material` based on a multiple pole Drude formulation + """Adds dispersive properties to already defined :class:`Material` based + on multi-pole Drude formulation. - :param n_poles: The number of Drude poles. - :type n_poles: float, non-optional - :param tau: The inverse of the relaxation time for each pole. - :type tau: list, non-optional - :param omega: The frequency for each pole. - :type omega: list, non-optional - :param alpha: The inverse of the relaxation time. - :type alpha: list, non-optional + Attributes: + n_poles: float required for number of Drude poles. + omega: tuple required for frequency (Hz) for each pole. + alpha: tuple required for inverse of relaxation time (secs) for each pole. + material_ids: list required of material ids to apply disperive + properties. """ def __init__(self, **kwargs): @@ -1105,11 +1275,13 @@ class AddDrudeDispersion(UserObjectMulti): alpha = self.kwargs['alpha'] material_ids = self.kwargs['material_ids'] except KeyError: - logger.exception(self.params_str() + ' requires at least four parameters') + logger.exception(self.params_str() + ' requires at least four ' + 'parameters.') raise if poles < 0: - logger.exception(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 @@ -1117,7 +1289,8 @@ class AddDrudeDispersion(UserObjectMulti): if len(materials) != len(material_ids): notfound = [x for x in material_ids if x not in materials] - logger.exception(self.params_str() + f' material(s) {notfound} do not exist') + logger.exception(self.params_str() + f' material(s) {notfound} do ' + 'not exist.') raise ValueError for material in materials: @@ -1134,7 +1307,10 @@ class AddDrudeDispersion(UserObjectMulti): disp_material.tau.append(tau[i]) disp_material.alpha.append(alpha[i]) else: - logger.exception(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 @@ -1142,24 +1318,24 @@ class AddDrudeDispersion(UserObjectMulti): # Replace original material with newly created DispersiveMaterial grid.materials = [disp_material if mat.numID==material.numID else mat for mat in grid.materials] - logger.info(self.grid_name(grid) + f"Drude disperion added to {disp_material.ID} with omega={', '.join('%4.3e' % tau for tau in disp_material.tau)} secs, and gamma={', '.join('%4.3e' % alpha for alpha in disp_material.alpha)} secs created.") + logger.info(self.grid_name(grid) + f"Drude disperion added to {disp_material.ID} " + f"with omega={', '.join('%4.3e' % tau for tau in disp_material.tau)} secs, " + f"and gamma={', '.join('%4.3e' % alpha for alpha in disp_material.alpha)} secs created.") class SoilPeplinski(UserObjectMulti): - """Allows you to use a mixing model for soils proposed by Peplinski (http://dx.doi.org/10.1109/36.387598 + """Mixing model for soils proposed by Peplinski et al. + (http://dx.doi.org/10.1109/36.387598) - :param sand_fraction: The sand fraction of the soil. - :type sand_fraction: float, non-optional - :param clay_fraction: The clay from of the soil. - :type clay_fraction: float, non-optional - :param bulk_density: The bulk density of the soil in grams per centimetre cubed. - :type bulk_density: float, non-optional - :param sand_density: The density of the sand particles in the soil in grams per centimetre cubed. - :type sand_density: float, non-optional - :param water_fraction_lower: Lower boundary volumetric water fraction of the soil. - :type water_fraction_lower: float, non-optional - :param water_fraction_upper: Upper boundary volumetric water fraction of the soil. - :type water_fraction_upper: float, non-optional + Attributes: + sand_fraction: float required for sand fraction of soil. + clay_fraction: float required for clay of soil. + bulk_density: float required for bulk density of soil (gm/cm^3). + sand_density: float required for density of sand particles in soil (gm/cm^3). + water_fraction_lower: float required for lower boundary of volumetric + water fraction of the soil. + water_fraction_upper: float required for upper boundary of volumetric + water fraction of the soil. """ def __init__(self, **kwargs): @@ -1177,54 +1353,69 @@ class SoilPeplinski(UserObjectMulti): water_fraction_upper = self.kwargs['water_fraction_upper'] ID = self.kwargs['id'] except KeyError: - logger.exception(self.params_str() + ' requires at exactly seven parameters') + logger.exception(self.params_str() + ' requires at exactly seven ' + 'parameters.') raise if sand_fraction < 0: - logger.exception(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: - logger.exception(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: - logger.exception(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: - logger.exception(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: - logger.exception(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: - logger.exception(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): 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)) + # 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)) - logger.info(self.grid_name(grid) + f'Mixing model (Peplinski) used to create {s.ID} with sand fraction {s.S:g}, clay fraction {s.C:g}, bulk density {s.rb:g}g/cm3, sand particle density {s.rs:g}g/cm3, and water volumetric fraction {s.mu[0]:g} to {s.mu[1]:g} created.') + logger.info(self.grid_name(grid) + 'Mixing model (Peplinski) used to ' + f'create {s.ID} with sand fraction {s.S:g}, clay fraction ' + f'{s.C:g}, bulk density {s.rb:g}g/cm3, sand particle ' + f'density {s.rs:g}g/cm3, and water volumetric fraction ' + f'{s.mu[0]:g} to {s.mu[1]:g} created.') - # Append the new material object to the materials list grid.mixingmodels.append(s) class GeometryView(UserObjectMulti): - """Allows you output to file(s) information about the geometry of model. + """Outputs to file(s) information about the geometry (mesh) of model. - :param p1: The lower left (x,y,z) coordinates of the volume of the geometry view in metres. - :type p1: list, non-optional - :param p2: The upper right (x,y,z) coordinates of the volume of the geometry view in metres. - :type p2: list, non-optional - :param dl: The spatial discretisation of the geometry view in metres. - :type dl: list, non-optional - :param output_type: can be either n (normal) or f (fine). - :type output_type: str, non-optional - :param filename: the filename of the file where the geometry view will be stored in the same directory as the input file. - :type filename: str, non-optional + The geometry information is saved in Visual Toolkit (VTK) formats. + Attributes: + p1: tuple required for lower left (x,y,z) coordinates of volume of + geometry view in metres. + p2: tuple required for upper right (x,y,z) coordinates of volume of + geometry view in metres. + dl: tuple required for spatial discretisation of geometry view in metres. + output_tuple: string required for per-cell 'n' (normal) or per-cell-edge + 'f' (fine) geometry views. + filename: string required for filename where geometry view will be + stored in the same directory as input file. """ def __init__(self, **kwargs): @@ -1233,8 +1424,8 @@ class GeometryView(UserObjectMulti): self.hash = '#geometry_view' def geometry_view_constructor(self, grid, output_type): - """Select appropriate class for geometry view dependent on grid type and - geometry view type, i.e. normal or fine. + """Selects appropriate class for geometry view dependent on grid type + and geometry view type, i.e. normal or fine. """ if output_type == 'n': @@ -1255,7 +1446,8 @@ class GeometryView(UserObjectMulti): output_type = self.kwargs['output_type'].lower() filename = self.kwargs['filename'] except KeyError: - logger.exception(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) try: @@ -1263,7 +1455,7 @@ class GeometryView(UserObjectMulti): p4 = uip.round_to_grid_static_point(p2) p1, p2 = uip.check_box_points(p1, p2, self.params_str()) except ValueError: - logger.exception(self.params_str() + ' point is outside the domain') + logger.exception(self.params_str() + ' point is outside the domain.') raise xs, ys, zs = p1 xf, yf, zf = p2 @@ -1272,39 +1464,52 @@ class GeometryView(UserObjectMulti): if dx < 0 or dy < 0 or dz < 0: - logger.exception(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: - logger.exception(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: - logger.exception(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': - logger.exception(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): - 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)') + if output_type == 'f' and (dx * grid.dx != grid.dx or + dy * grid.dy != grid.dy or + dz * grid.dz != grid.dz): + 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 g = GeometryViewUser(xs, ys, zs, xf, yf, zf, dx, dy, dz, filename, grid) - logger.info(self.grid_name(grid) + f'Geometry view from {p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m, discretisation {dx * grid.dx:g}m, {dy * grid.dy:g}m, {dz * grid.dz:g}m, with filename base {g.filename} created.') + logger.info(self.grid_name(grid) + f'Geometry view from {p3[0]:g}m, ' + f'{p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, ' + f'{p4[2]:g}m, discretisation {dx * grid.dx:g}m, ' + f'{dy * grid.dy:g}m, {dz * grid.dz:g}m, with filename ' + f'base {g.filename} created.') - # Append the new GeometryView object to the geometry views list grid.geometryviews.append(g) class GeometryObjectsWrite(UserObjectMulti): - """Allows you to write geometry generated in a model to file. - - :param p1: The lower left (x,y,z) coordinates of the volume of the output in metres. - :type p1: list, non-optional - :param p2: The upper right (x,y,z) coordinates of the volume of the output in metres. - :type p2: list, non-optional - :param filename: the filename of the file where the output will be stored in the same directory as the input file. - :type filename: str, non-optional + """Writes geometry generated in a model to file which can be imported into + other models. + Attributes: + p1: tuple required for lower left (x,y,z) coordinates of volume of + output in metres. + p2: tuple required for upper right (x,y,z) coordinates of volume of + output in metres. + filename: string required for filename where output will be stored in + the same directory as input file. """ def __init__(self, **kwargs): @@ -1318,7 +1523,8 @@ class GeometryObjectsWrite(UserObjectMulti): p2 = self.kwargs['p2'] basefilename = self.kwargs['filename'] except KeyError: - logger.exception(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()) @@ -1327,39 +1533,38 @@ class GeometryObjectsWrite(UserObjectMulti): g = GeometryObjectsUser(x0, y0, z0, x1, y1, z1, basefilename) - logger.info(f'Geometry objects in the volume from {p1[0] * grid.dx:g}m, {p1[1] * grid.dy:g}m, {p1[2] * grid.dz:g}m, to {p2[0] * grid.dx:g}m, {p2[1] * grid.dy:g}m, {p2[2] * grid.dz:g}m, will be written to {g.filename_hdf5}, with materials written to {g.filename_materials}') + logger.info(f'Geometry objects in the volume from {p1[0] * grid.dx:g}m, ' + f'{p1[1] * grid.dy:g}m, {p1[2] * grid.dz:g}m, to ' + f'{p2[0] * grid.dx:g}m, {p2[1] * grid.dy:g}m, ' + f'{p2[2] * grid.dz:g}m, will be written to ' + f'{g.filename_hdf5}, with materials written to ' + f'{g.filename_materials}') - # Append the new GeometryView object to the geometry objects to write list grid.geometryobjectswrite.append(g) class PMLCFS(UserObjectMulti): - """Allows you to write geometry generated in a model to file. + """Controls parameters that are used to build each order of PML. - :param alphascalingprofile: The type of scaling to use for the CFS α parameter - :type alphascalingprofile: float, non-optional - :param alphascalingdirection: The direction of the scaling to use for the CFS α parameter. - :type alphascalingdirection: float, non-optional - :param alphamin: Minimum value for the CFS α parameter. - :type alphamin: str, non-optional - :param alphamax: Minimum value for the CFS α parameter. - :type alphamax: str, non-optional - :param kappascalingprofile: The type of scaling to use for the CFS κ parameter - :type kappascalingprofile: float, non-optional - :param kappascalingdirection: The direction of the scaling to use for the CFS κ parameter. - :type kappascalingdirection: float, non-optional - :param kappamin: Minimum value for the CFS κ parameter. - :type kappamin: str, non-optional - :param kappamax: Minimum value for the CFS κ parameter. - :type kappamax: str, non-optional - :param sigmascalingprofile: The type of scaling to use for the CFS σ parameter - :type sigmascalingprofile: float, non-optional - :param sigmascalingdirection: The direction of the scaling to use for the CFS σ parameter. - :type sigmascalingdirection: float, non-optional - :param sigmamin: Minimum value for the CFS σ parameter. - :type sigmamin: str, non-optional - :param sigmamax: Minimum value for the CFS σ parameter. - :type sigmamax: str, non-optional + Attributes: + alphascalingprofile: string required for type of scaling to use for + CFS alpha parameter. + alphascalingdirection: string required for direction of scaling to use + for CFS alpha parameter. + alphamin: float required for minimum value for the CFS alpha parameter. + alphamax: float required for maximum value for the CFS alpha parameter. + kappascalingprofile: string required for type of scaling to use for + CFS kappa parameter. + kappascalingdirection: string required for direction of scaling to use + for CFS kappa parameter. + kappamin: float required for minimum value for the CFS kappa parameter. + kappamax: float required for maximum value for the CFS kappa parameter. + sigmascalingprofile: string required for type of scaling to use for + CFS sigma parameter. + sigmascalingdirection: string required for direction of scaling to use + for CFS sigma parameter. + sigmamin: float required for minimum value for the CFS sigma parameter. + sigmamax: float required for maximum value for the CFS sigma parameter. """ count = 0 @@ -1370,7 +1575,8 @@ class PMLCFS(UserObjectMulti): self.hash = '#pml_cfs' PMLCFS.count += 1 if PMLCFS.count == 2: - logger.exception(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): @@ -1388,20 +1594,30 @@ class PMLCFS(UserObjectMulti): sigmamin = self.kwargs['sigmamin'] sigmamax = self.kwargs['sigmamax'] except KeyError: - logger.exception(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(): - logger.exception(self.params_str() + f" must have scaling type {','.join(CFSParameter.scalingprofiles.keys())}") + if (alphascalingprofile not in CFSParameter.scalingprofiles.keys() or + kappascalingprofile not in CFSParameter.scalingprofiles.keys() or + sigmascalingprofile not in CFSParameter.scalingprofiles.keys()): + logger.exception(self.params_str() + ' must have scaling type ' + f"{','.join(CFSParameter.scalingprofiles.keys())}") raise ValueError - if alphascalingdirection not in CFSParameter.scalingdirections or kappascalingdirection not in CFSParameter.scalingdirections or sigmascalingdirection not in CFSParameter.scalingdirections: - logger.exception(self.params_str() + f" must have scaling type {','.join(CFSParameter.scalingdirections)}") + if (alphascalingdirection not in CFSParameter.scalingdirections or + kappascalingdirection not in CFSParameter.scalingdirections or + sigmascalingdirection not in CFSParameter.scalingdirections): + logger.exception(self.params_str() + ' must have scaling type ' + f"{','.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: - logger.exception(self.params_str() + ' minimum and maximum scaling values must be greater than zero') + if (float(alphamin) < 0 or float(alphamax) < 0 or + float(kappamin) < 0 or float(kappamax) < 0 or float(sigmamin) < 0): + logger.exception(self.params_str() + ' minimum and maximum scaling ' + 'values must be greater than zero.') raise ValueError if float(kappamin) < 1: - logger.exception(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() @@ -1430,7 +1646,14 @@ class PMLCFS(UserObjectMulti): cfs.kappa = cfskappa cfs.sigma = cfssigma - logger.info(f'PML CFS parameters: alpha (scaling: {cfsalpha.scalingprofile}, scaling direction: {cfsalpha.scalingdirection}, min: {cfsalpha.min:g}, max: {cfsalpha.max:g}), kappa (scaling: {cfskappa.scalingprofile}, scaling direction: {cfskappa.scalingdirection}, min: {cfskappa.min:g}, max: {cfskappa.max:g}), sigma (scaling: {cfssigma.scalingprofile}, scaling direction: {cfssigma.scalingdirection}, min: {cfssigma.min:g}, max: {cfssigma.max:g}) created.') + logger.info(f'PML CFS parameters: alpha (scaling: {cfsalpha.scalingprofile}, ' + f'scaling direction: {cfsalpha.scalingdirection}, min: ' + f'{cfsalpha.min:g}, max: {cfsalpha.max:g}), kappa (scaling: ' + f'{cfskappa.scalingprofile}, scaling direction: ' + f'{cfskappa.scalingdirection}, min: {cfskappa.min:g}, max: ' + f'{cfskappa.max:g}), sigma (scaling: {cfssigma.scalingprofile}, ' + f'scaling direction: {cfssigma.scalingdirection}, min: ' + f'{cfssigma.min:g}, max: {cfssigma.max:g}) created.') grid.cfs.append(cfs) @@ -1448,12 +1671,6 @@ class Subgrid(UserObjectMulti): elif isinstance(node, UserObjectGeometry): self.children_geometry.append(node) else: - logger.exception('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?') + \ No newline at end of file