From f52e1d63cffa697b03bc5fbc13c5b610d53c077b Mon Sep 17 00:00:00 2001 From: Craig Warren Date: Mon, 4 Sep 2023 10:49:58 +0100 Subject: [PATCH] Reinstate PML CFS and formulation via hash cmds --- docs/source/input_hash_cmds.rst | 46 ++++++++++++++++++++++++++++++++- gprMax/cmds_multiuse.py | 7 +---- gprMax/hash_cmds_file.py | 2 ++ gprMax/hash_cmds_multiuse.py | 27 ++++++++++++++++++- gprMax/hash_cmds_singleuse.py | 19 ++++++++++++-- 5 files changed, 91 insertions(+), 10 deletions(-) diff --git a/docs/source/input_hash_cmds.rst b/docs/source/input_hash_cmds.rst index c4710f32..e6ebf26a 100644 --- a/docs/source/input_hash_cmds.rst +++ b/docs/source/input_hash_cmds.rst @@ -965,7 +965,7 @@ For example to save a snapshot of the electromagnetic fields in the model at a s PML commands ============ -The default behaviour for the absorbing boundary conditions (ABC) is first order Complex Frequency Shifted (CFS) Perfectly Matched Layers (PML), with thicknesses of 10 cells on each of the six sides of the model domain. The thickness of the PML can be altered by using the following command (further customisation of the PML is possible using our Python API): +The default behaviour for the absorbing boundary conditions (ABC) is first order Complex Frequency Shifted (CFS) Perfectly Matched Layers (PML), with thicknesses of 10 cells on each of the six sides of the model domain. #pml_cells: ------------ @@ -989,3 +989,47 @@ For example to use a PML with 20 cells (thicker than the default 10 cells) on on .. code-block:: none #pml_cells: 10 10 20 10 10 20 + +#pml_formulation: +----------------- + +Allows you to alter the formulation used for the PML. The current options are to use the Higher Order RIPML (HORIPML) - https://doi.org/10.1109/TAP.2011.2180344, or Multipole RIPML (MRIPML) - https://doi.org/10.1109/TAP.2018.2823864. The syntax of the command is: + +.. code-block:: none + + #pml_formulation: str + +* ``str`` can be either 'HORIPML' or 'MRIPML' + +For example to use the Multipole RIPML: + +.. code-block:: none + + #pml_formulation: MRIPML + +#pml_cfs: +--------- + +Allows you (advanced) control of the parameters that are used to build each order of the PML. Up to a second order PML can currently be specified, i.e. by using two ``#pml_cfs`` commands. The syntax of the command is: + +.. code-block:: none + + #pml_cfs: str1 str2 f1 f2 str3 str4 f3 f4 str5 str6 f5 f6 + +* ``str1`` is the type of scaling to use for the CFS :math:`\alpha` parameter. It can be ``constant``, ``linear``, ``quadratic``, ``cubic``, ``quartic``, ``quintic`` and ``sextic``. +* ``str2`` is the direction of the scaling to use for the CFS :math:`\alpha` parameter. It can be ``forward`` or ``reverse``. +* ``f1 f2`` are the minimum and maximum values for the CFS :math:`\alpha` parameter. +* ``str3`` is the type of scaling to use for the CFS :math:`\kappa` parameter. It can be ``constant``, ``linear``, ``quadratic``, ``cubic``, ``quartic``, ``quintic`` and ``sextic``. +* ``str4`` is the direction of the scaling to use for the CFS :math:`\kappa` parameter. It can be ``forward`` or ``reverse``. +* ``f3 f4`` are the minimum and maximum values for the CFS :math:`\kappa` parameter. The minimum value for the CFS :math:`\kappa` parameter is one. +* ``str5`` is the type of scaling to use for the CFS :math:`\sigma` parameter. It can be ``constant``, ``linear``, ``quadratic``, ``cubic``, ``quartic``, ``quintic`` and ``sextic``. +* ``str6`` is the direction of the scaling to use for the CFS :math:`\sigma` parameter. It can be ``forward`` or ``reverse``. +* ``f5 f6`` are the minimum and maximum values for the CFS :math:`\sigma` parameter. + +The CFS values (which are internally specified) used for the default standard first order PML are: ``#pml_cfs: constant forward 0 0 constant forward 1 1 quartic forward 0 None``. Specifying 'None' for the maximum value of :math:`\sigma` forces gprMax to calculate it internally based on the relative permittivity and permeability of the underlying materials in the model. + +The parameters will be applied to all slabs of the PML that are switched on. + +.. tip:: + + ``forward`` direction implies minimum parameter value at the inner boundary of the PML and maximum parameter value at the edge of computational domain, ``reverse`` is the opposite. diff --git a/gprMax/cmds_multiuse.py b/gprMax/cmds_multiuse.py index 7d3a4868..50cee93c 100644 --- a/gprMax/cmds_multiuse.py +++ b/gprMax/cmds_multiuse.py @@ -1737,11 +1737,6 @@ class PMLCFS(UserObjectMulti): ): logger.exception(f"{self.params_str()} minimum and maximum scaling values must be greater than zero.") raise ValueError - # TODO: Fix handling of kappa for 2nd order PMLs - # if float(kappamin) < 1: - # logger.exception(f'{self.params_str()} minimum scaling value for ' - # 'kappa must be greater than or equal to one.') - # raise ValueError cfsalpha = CFSParameter() cfsalpha.ID = "alpha" @@ -1800,4 +1795,4 @@ class Subgrid(UserObjectMulti): self.children_geometry.append(node) else: logger.exception("This object is unknown to gprMax.") - raise ValueError + raise ValueError diff --git a/gprMax/hash_cmds_file.py b/gprMax/hash_cmds_file.py index b5ba0bcb..90539a92 100644 --- a/gprMax/hash_cmds_file.py +++ b/gprMax/hash_cmds_file.py @@ -217,6 +217,7 @@ def check_cmd_names(processedlines, checkessential=True): "#title", "#omp_threads", "#time_step_stability_factor", + "#pml_formulation", "#pml_cells", "#excitation_file", "#src_steps", @@ -248,6 +249,7 @@ def check_cmd_names(processedlines, checkessential=True): "#rx", "#rx_array", "#snapshot", + "#pml_cfs", "#include_file", ] } diff --git a/gprMax/hash_cmds_multiuse.py b/gprMax/hash_cmds_multiuse.py index 18e71c06..f3e1f5ff 100644 --- a/gprMax/hash_cmds_multiuse.py +++ b/gprMax/hash_cmds_multiuse.py @@ -29,6 +29,7 @@ from .cmds_multiuse import ( Material, MaterialList, MaterialRange, + PMLCFS, Rx, RxArray, Snapshot, @@ -375,7 +376,7 @@ def process_multicmds(multicmds): tmp = cmdinstance.split() if len(tmp) < 2: - logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least 2 parameters") + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least two parameters") raise ValueError tokens = len(tmp) @@ -386,4 +387,28 @@ def process_multicmds(multicmds): material_list = MaterialList(list_of_materials=lmats, id=tmp[tokens - 1]) scene_objects.append(material_list) + cmdname = '#pml_cfs' + if multicmds[cmdname] is not None: + for cmdinstance in multicmds[cmdname]: + tmp = cmdinstance.split() + + if len(tmp) != 12: + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly twelve parameters") + raise ValueError + + pml_cfs = PMLCFS(alphascalingprofile=tmp[0], + alphascalingdirection=tmp[1], + alphamin=tmp[2], + alphamax=tmp[3], + kappascalingprofile=tmp[4], + kappascalingdirection=tmp[5], + kappamin=tmp[6], + kappamax=tmp[7], + sigmascalingprofile=tmp[8], + sigmascalingdirection=tmp[9], + sigmamin=tmp[10], + sigmamax=tmp[11]) + + scene_objects.append(pml_cfs) + return scene_objects diff --git a/gprMax/hash_cmds_singleuse.py b/gprMax/hash_cmds_singleuse.py index 023e78a1..6dd3765b 100644 --- a/gprMax/hash_cmds_singleuse.py +++ b/gprMax/hash_cmds_singleuse.py @@ -120,17 +120,32 @@ def process_singlecmds(singlecmds): scene_objects.append(tw) + cmd = "#pml_formulation" + if singlecmds[cmd] is not None: + tmp = singlecmds[cmd].split() + if len(tmp) != 1: + logger.exception(f"{cmd} requires one parameter") + raise ValueError + else: + pml_formulation = tmp[0] + cmd = "#pml_cells" if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) not in [1, 6]: logger.exception(f"{cmd} requires either one or six parameter(s)") raise ValueError + if pml_formulation: + formulation = pml_formulation + else: + formulation = None if len(tmp) == 1: - pml_cells = PMLProps(thickness=int(tmp[0])) + pml_cells = PMLProps(formulation=formulation, thickness=int(tmp[0])) else: pml_cells = PMLProps( - x0=int(tmp[0]), y0=int(tmp[1]), z0=int(tmp[2]), xmax=int(tmp[3]), ymax=int(tmp[4]), zmax=int(tmp[5]) + formulation=formulation, + x0=int(tmp[0]), y0=int(tmp[1]), z0=int(tmp[2]), + xmax=int(tmp[3]), ymax=int(tmp[4]), zmax=int(tmp[5]) ) scene_objects.append(pml_cells)