From c43104e28632ab5bb6a408dbdc5a5f6557081946 Mon Sep 17 00:00:00 2001 From: John Hartley Date: Fri, 23 Aug 2019 21:58:29 +0100 Subject: [PATCH] comments --- gprMax/cmds_multiple.py | 99 ++++++++++++++++++++++++++++++++---- gprMax/cmds_single_use.py | 103 ++++++++++++++++++++++++++++++-------- gprMax/scene.py | 4 +- 3 files changed, 173 insertions(+), 33 deletions(-) diff --git a/gprMax/cmds_multiple.py b/gprMax/cmds_multiple.py index 15572223..e64ef392 100644 --- a/gprMax/cmds_multiple.py +++ b/gprMax/cmds_multiple.py @@ -74,16 +74,21 @@ class UserObjectMulti: class Waveform(UserObjectMulti): - """Create Waveform different excitation types.""" + """Allows you to specify 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 + """ def __init__(self, **kwargs): - """Constructor.""" super().__init__(**kwargs) self.order = 0 self.hash = '#waveform' def create(self, grid, uip): - """Create the waveform and add it to the grid.""" try: wavetype = self.kwargs['wave_type'].lower() amp = self.kwargs['amp'] @@ -113,10 +118,22 @@ class Waveform(UserObjectMulti): class VoltageSource(UserObjectMulti): - """User Object for a voltage source.""" + """Allows you to introduce 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 + """ def __init__(self, **kwargs): - """Constructor.""" super().__init__(**kwargs) self.order = 1 self.hash = '#voltage_source' @@ -124,8 +141,8 @@ class VoltageSource(UserObjectMulti): def create(self, grid, uip): """Create voltage source and add it to the grid.""" try: - polarisation = self.kwargs['polarisation'].lower() p1 = self.kwargs['p1'] + polarisation = self.kwargs['polarisation'].lower() resistance = self.kwargs['resistance'] waveform_id = self.kwargs['waveform_id'] @@ -190,7 +207,20 @@ class VoltageSource(UserObjectMulti): class HertzianDipole(UserObjectMulti): - """User Object for HertzianDipole.""" + """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. + + :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 + """ def __init__(self, **kwargs): """Constructor.""" @@ -278,7 +308,20 @@ class HertzianDipole(UserObjectMulti): class MagneticDipole(UserObjectMulti): - """Magnetic Dipole User Object.""" + """This will simulate an infinitesimal magnetic dipole. This is 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 + """ def __init__(self, **kwargs): """Constructor.""" @@ -352,8 +395,22 @@ class MagneticDipole(UserObjectMulti): class TransmissionLine(UserObjectMulti): - """Magnetic Dipole User Object.""" + """Allows you to introduce 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 + """ def __init__(self, **kwargs): """Constructor.""" super().__init__(**kwargs) @@ -434,7 +491,19 @@ class TransmissionLine(UserObjectMulti): class Rx(UserObjectMulti): - """Magnetic Dipole User Object.""" + """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. . + + :param p1: Position of the receiver x, y, z + :type p1: list, non-optional + :param id: Identifier for the receiver + :type id: str, non-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, non-optional + + """ def __init__(self, **kwargs): """Constructor.""" @@ -483,7 +552,15 @@ class Rx(UserObjectMulti): return r class RxArray(UserObjectMulti): - """Receiver Array User Object.""" + """Provides a simple method of defining 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 + """ def __init__(self, **kwargs): """Constructor.""" diff --git a/gprMax/cmds_single_use.py b/gprMax/cmds_single_use.py index 634f01a6..057779d5 100644 --- a/gprMax/cmds_single_use.py +++ b/gprMax/cmds_single_use.py @@ -40,10 +40,6 @@ floattype = dtypes['float_or_double'] class UserObjectSingle: - """ - Specific geometry object - """ - def __init__(self, **kwargs): # each single command has an order to specify the order in which # the commands are constructed. IE. discretisation must be @@ -58,7 +54,12 @@ class UserObjectSingle: pass -class DomainSingle(UserObjectSingle): +class Domain(UserObjectSingle): + """Allows you to specify the size of the model. + + :param p1: point specifying total extend in x, y, z + :type p1: list of floats, non-optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors @@ -145,23 +146,12 @@ class DomainSingle(UserObjectSingle): print(Fore.RED + 'WARNING: You have specified more threads ({}) than available physical CPU cores ({}). This may lead to degraded performance.'.format(G.nthreads, hostinfo['physicalcores']) + Style.RESET_ALL) - -class Domain: - - """Restrict user object so there can only be one instance - https://python-3-patterns-idioms-test.readthedocs.io/en/latest/index.html - """ - - instance = None - - def __new__(cls, **kwargs): # __new__ always a classmethod - if not Domain.instance: - Domain.instance = DomainSingle(**kwargs) - return Domain.instance - - class Discretisation(UserObjectSingle): + """Allows you to specify the discretization of space in the x , y and z directions respectively + :param p1: Specify discretisation in x, y, z direction + :type p1: list of floats, non-optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors # when they occur @@ -199,7 +189,13 @@ class Discretisation(UserObjectSingle): class TimeWindow(UserObjectSingle): + """Allows you to specify the total required simulated time + :param time: Required simulated time in seconds + :type time: float, optional + :param iterations: Required number of iterations + :type iterations: int, optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors # when they occur @@ -248,6 +244,11 @@ class TimeWindow(UserObjectSingle): class Messages(UserObjectSingle): + """Allows you to control the amount of information displayed on screen when gprMax is run + + :param yn: Whether information should be displayed. + :type yn: bool, optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors @@ -276,6 +277,11 @@ class Messages(UserObjectSingle): class Title(UserObjectSingle): + """Allows you to include a title for your model. + + :param name: Simulation title. + :type name: str, optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors @@ -295,6 +301,12 @@ class Title(UserObjectSingle): print('Model title: {}'.format(G.title)) class NumThreads(UserObjectSingle): + """Allows you to control how many OpenMP threads (usually the number of + physical CPU cores available) are used when running the model. + + :param n: Number of threads. + :type n: int, optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors # when they occur @@ -334,7 +346,11 @@ class NumThreads(UserObjectSingle): # Time step stability factor class TimeStepStabilityFactor(UserObjectSingle): + """Factor by which to reduce the time step from the CFL limit. + :param f: Factor to multiple time step. + :type f: float, optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors # when they occur @@ -363,7 +379,25 @@ class TimeStepStabilityFactor(UserObjectSingle): class PMLCells(UserObjectSingle): + """Allows you to control the number of cells (thickness) of PML that are used + on the six sides of the model domain. Specify either single thickness or + thickness on each side. + :param thickness: Thickness of PML on all 6 sides. + :type thickness: int, optional + :param x0: Thickness of PML on left side. + :type x0: int, optional + :param y0: Thickness of PML on the front side. + :type y0: int, optional + :param z0: Thickness of PML on bottom side. + :type z0: int, optional + :param xmax: Thickness of PML on right side. + :type xmax: int, optional + :param ymax: Thickness of PML on the back side. + :type ymax: int, optional + :param zmax: Thickness of PML on top side. + :type zmax: int, optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors # when they occur @@ -399,6 +433,11 @@ class PMLCells(UserObjectSingle): class SrcSteps(UserObjectSingle): + """Provides a simple method to allow you to move the location of all simple sources + + :param p1: increments (x,y,z) to move all simple sources + :type p1: list, non-optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors @@ -417,6 +456,11 @@ class SrcSteps(UserObjectSingle): class RxSteps(UserObjectSingle): + """Provides a simple method to allow you to move the location of all simple receivers + + :param p1: increments (x,y,z) to move all simple receivers + :type p1: list, non-optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors @@ -434,6 +478,16 @@ class RxSteps(UserObjectSingle): class ExcitationFile(UserObjectSingle): + """Allows you to specify an ASCII file that contains columns of amplitude + values that specify custom waveform shapes that can be used with sources in the model. + + :param filepath: Excitation file path. + :type filepath: str, non-optional + :param kind: passed to the interpolation function (scipy.interpolate.interp1d). + :type kind: float, optional + :param fill_value: passed to the interpolation function (scipy.interpolate.interp1d). + :type fill_value: float, optional + """ def create(self, G, uip): # Excitation file for user-defined source waveforms @@ -502,7 +556,11 @@ class ExcitationFile(UserObjectSingle): class OutputDir(UserObjectSingle): + """Allows you to control the directory where output file(s) will be stored. + :param dir: File path to directory. + :type dir: str, non-optional + """ def __init__(self, **kwargs): # dont need to define parameters in advance. Just catch errors # when they occur @@ -514,7 +572,12 @@ class OutputDir(UserObjectSingle): class NumberOfModelRuns(UserObjectSingle): + """Number of times to run the simulation. This required when using multiple + class:Scene instances. + :param n: File path to directory. + :type n: str, non-optional + """ def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 12 diff --git a/gprMax/scene.py b/gprMax/scene.py index 20ef2125..2efe3723 100644 --- a/gprMax/scene.py +++ b/gprMax/scene.py @@ -1,7 +1,7 @@ from .user_inputs import create_user_input_points from .materials import create_built_in_materials from .cmds_single_use import UserObjectSingle -from .cmds_single_use import DomainSingle +from .cmds_single_use import Domain from .cmds_single_use import Discretisation from .cmds_single_use import TimeWindow from .cmds_multiple import UserObjectMulti @@ -21,7 +21,7 @@ class Scene: self.multiple_cmds = [] self.single_cmds = [] self.geometry_cmds = [] - self.essential_cmds = [DomainSingle, TimeWindow, Discretisation] + self.essential_cmds = [Domain, TimeWindow, Discretisation] # fractal box commands have an additional nonuser object which # process modifications