From f9dd7f24207e65a3325e376a53815b65a5a9f7c2 Mon Sep 17 00:00:00 2001 From: Sai-Suraj-27 Date: Mon, 26 Jun 2023 16:09:39 +0530 Subject: [PATCH] Added a pre-commit config file and reformatted all the files accordingly by using it. --- .gitattributes | 2 +- .github/FUNDING.yml | 2 +- .pre-commit-config.yaml | 14 + .readthedocs.yaml | 2 +- conda_env.yml | 1 - docs/source/accelerators.rst | 6 +- docs/source/comparisons_numerical.rst | 1 - docs/source/conf.py | 20 +- docs/source/examples_advanced.rst | 16 +- docs/source/features.rst | 2 +- docs/source/hpc.rst | 2 +- docs/source/inc_AntennaPatterns.rst | 2 +- docs/source/inc_AustinMan.rst | 2 +- docs/source/inc_DebyeFit.rst | 2 +- docs/source/inc_GPRAntennaModels.rst | 2 +- docs/source/inc_LandmineModels.rst | 2 +- docs/source/inc_Materials.rst | 2 +- docs/source/inc_Plotting.rst | 2 +- docs/source/inc_README.rst | 2 +- docs/source/inc_STLtoVoxel.rst | 2 +- docs/source/inc_Utilities.rst | 2 +- docs/source/input_api.rst | 10 +- docs/source/input_hash_cmds.rst | 4 +- docs/source/output.rst | 4 +- examples/antenna_like_GSSI_1500_fs.py | 32 +- examples/antenna_like_GSSI_1500_patterns.py | 51 +- examples/antenna_like_MALA_1200_fs.py | 9 +- examples/antenna_wire_dipole_fs.py | 53 +- examples/cylinder_Ascan_2D.in | 2 +- examples/cylinder_Bscan_2D.in | 2 +- examples/cylinder_Bscan_GSSI_1500.py | 26 +- examples/heterogeneous_soil.in | 2 +- examples/jupyter-notebooks/README.rst | 2 +- examples/subgrids/cylinder_fs.py | 41 +- .../gssi_400_over_fractal_subsurface.py | 79 +- gprMax.toml | 2 +- gprMax/__init__.py | 43 +- gprMax/__main__.py | 2 +- gprMax/_version.py | 4 +- gprMax/cmds_geometry/add_grass.py | 159 +- gprMax/cmds_geometry/add_surface_roughness.py | 180 +- gprMax/cmds_geometry/add_surface_water.py | 95 +- gprMax/cmds_geometry/box.py | 77 +- gprMax/cmds_geometry/build_templates.py | 61 +- gprMax/cmds_geometry/cmds_geometry.py | 78 +- gprMax/cmds_geometry/cone.py | 93 +- gprMax/cmds_geometry/cylinder.py | 80 +- gprMax/cmds_geometry/cylindrical_sector.py | 136 +- gprMax/cmds_geometry/edge.py | 41 +- gprMax/cmds_geometry/ellipsoid.py | 78 +- gprMax/cmds_geometry/fractal_box.py | 117 +- gprMax/cmds_geometry/fractal_box_builder.py | 217 ++- gprMax/cmds_geometry/geometry_objects_read.py | 105 +- gprMax/cmds_geometry/plate.py | 54 +- gprMax/cmds_geometry/sphere.py | 73 +- gprMax/cmds_geometry/triangle.py | 122 +- gprMax/cmds_multiuse.py | 1306 ++++++++------- gprMax/cmds_singleuse.py | 247 +-- gprMax/config.py | 225 +-- gprMax/contexts.py | 77 +- gprMax/cuda_opencl/knl_common_cuda.tmpl | 2 +- gprMax/cuda_opencl/knl_common_opencl.tmpl | 6 +- gprMax/cuda_opencl/knl_fields_updates.py | 212 ++- .../knl_pml_updates_electric_HORIPML.py | 387 +++-- .../knl_pml_updates_electric_MRIPML.py | 365 ++-- .../knl_pml_updates_magnetic_HORIPML.py | 392 +++-- .../knl_pml_updates_magnetic_MRIPML.py | 374 +++-- gprMax/cuda_opencl/knl_snapshots.py | 79 +- gprMax/cuda_opencl/knl_source_updates.py | 143 +- gprMax/cuda_opencl/knl_store_outputs.py | 43 +- .../fields_updates_dispersive_template.jinja | 94 +- gprMax/cython/fields_updates_normal.pyx | 64 +- gprMax/cython/fractals_generate.pyx | 46 +- gprMax/cython/geometry_outputs.pyx | 22 +- gprMax/cython/geometry_primitives.pyx | 152 +- gprMax/cython/pml_build.pyx | 6 +- .../cython/pml_updates_electric_HORIPML.pyx | 120 +- gprMax/cython/pml_updates_electric_MRIPML.pyx | 98 +- .../cython/pml_updates_magnetic_HORIPML.pyx | 120 +- gprMax/cython/pml_updates_magnetic_MRIPML.pyx | 96 +- gprMax/cython/snapshots.pyx | 20 +- gprMax/cython/yee_cell_build.pyx | 66 +- gprMax/cython/yee_cell_setget_rigid.pyx | 96 +- gprMax/fields_outputs.py | 73 +- gprMax/fractals.py | 81 +- gprMax/geometry_outputs.py | 320 ++-- gprMax/gprMax.py | 271 +-- gprMax/grid.py | 305 ++-- gprMax/hash_cmds_file.py | 207 ++- gprMax/hash_cmds_geometry.py | 329 ++-- gprMax/hash_cmds_multiuse.py | 280 ++-- gprMax/hash_cmds_singleuse.py | 71 +- gprMax/materials.py | 345 ++-- gprMax/model_build_run.py | 286 ++-- gprMax/mpi.py | 100 +- gprMax/pml.py | 663 ++++---- gprMax/receivers.py | 28 +- gprMax/scene.py | 27 +- gprMax/snapshots.py | 262 +-- gprMax/solvers.py | 39 +- gprMax/sources.py | 212 +-- gprMax/subgrids/grid.py | 35 +- gprMax/subgrids/precursor_nodes.py | 585 ++++--- gprMax/subgrids/subgrid_hsg.py | 711 ++++++-- gprMax/subgrids/updates.py | 20 +- gprMax/subgrids/user_objects.py | 85 +- gprMax/updates.py | 1490 +++++++++-------- gprMax/user_inputs.py | 81 +- gprMax/utilities/host_info.py | 348 ++-- gprMax/utilities/logging.py | 47 +- gprMax/utilities/utilities.py | 95 +- gprMax/waveforms.py | 60 +- setup.py | 312 ++-- testing/analytical_solutions.py | 48 +- testing/benchmarking/bench_simple.py | 13 +- .../antenna_GSSI_1500_fs.in | 1 - .../antenna_MALA_1200_fs.in | 1 - testing/models_pmls/plot_pml_comparison.py | 66 +- .../pml_3D_pec_plate/pml_3D_pec_plate.py | 250 +-- .../pml_3D_pec_plate/pml_3D_pec_plate_ref.py | 61 +- testing/models_pmls/pml_basic.py | 111 +- .../antenna_bowtie_fs/antenna_bowtie_fs.py | 60 +- testing/test_experimental.py | 61 +- testing/test_models.py | 232 +-- toolboxes/AntennaPatterns/initial_save.py | 106 +- toolboxes/AntennaPatterns/plot_fields.py | 73 +- toolboxes/AustinManWoman/head_only_h5.py | 22 +- toolboxes/DebyeFit/Debye_Fit.py | 548 +++--- toolboxes/DebyeFit/README.rst | 6 +- .../examples/example_BiologicalTissues.ipynb | 2 +- .../DebyeFit/examples/example_ColeCole.py | 51 +- .../examples/example_DebyeFitting.ipynb | 2 +- toolboxes/DebyeFit/optimization.py | 108 +- toolboxes/DebyeFit/optimization.rst | 2 +- toolboxes/GPRAntennaModels/GSSI.py | 1326 +++++++++------ toolboxes/GPRAntennaModels/MALA.py | 821 +++++---- toolboxes/GPRAntennaModels/README.rst | 4 +- toolboxes/Plotting/README.rst | 6 +- toolboxes/Plotting/plot_Ascan.py | 279 +-- toolboxes/Plotting/plot_Bscan.py | 76 +- toolboxes/Plotting/plot_antenna_params.py | 386 +++-- toolboxes/Plotting/plot_source_wave.py | 113 +- toolboxes/STLtoVoxel/README.rst | 4 +- toolboxes/STLtoVoxel/convert.py | 8 +- toolboxes/STLtoVoxel/examples/bunny.py | 14 +- toolboxes/STLtoVoxel/examples/materials.txt | 2 +- toolboxes/STLtoVoxel/perimeter.py | 22 +- toolboxes/STLtoVoxel/slice.py | 50 +- toolboxes/STLtoVoxel/stltovoxel.py | 42 +- toolboxes/Utilities/HPC/gprmax_omp.sh | 2 +- toolboxes/Utilities/Paraview/gprMax.py | 186 +- toolboxes/Utilities/README.rst | 2 +- toolboxes/Utilities/convert_png2h5.py | 58 +- toolboxes/Utilities/get_host_spec.py | 31 +- toolboxes/Utilities/outputfiles_merge.py | 81 +- 155 files changed, 11383 insertions(+), 8802 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.gitattributes b/.gitattributes index 64a62598..c8fd44ec 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -tools/Jupyter_notebooks/* linguist-vendored +tools/Jupyter_notebooks/* linguist-vendored diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 409e993f..c558e7b6 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -10,4 +10,4 @@ liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] \ No newline at end of file +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..43e4d52d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,14 @@ +# See https://pre-commit.com for more information +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files +- repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black + args: ["--line-length", "120"] # Adjust the max line length value as needed. diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 069ea989..378e2caa 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -18,4 +18,4 @@ sphinx: configuration: docs/source/conf.py formats: - - pdf \ No newline at end of file + - pdf diff --git a/conda_env.yml b/conda_env.yml index e42be5d2..10b9d63f 100644 --- a/conda_env.yml +++ b/conda_env.yml @@ -24,4 +24,3 @@ dependencies: - terminaltables - tqdm - git+https://github.com/craig-warren/PyEVTK.git - diff --git a/docs/source/accelerators.rst b/docs/source/accelerators.rst index dabceeb0..78c96985 100644 --- a/docs/source/accelerators.rst +++ b/docs/source/accelerators.rst @@ -8,7 +8,7 @@ The most computationally intensive parts of gprMax, which are the FDTD solver lo 1. `OpenMP `_ which supports multi-platform shared memory multiprocessing. 2. `NVIDIA CUDA `_ for NVIDIA GPUs. -3. `OpenCL `_ for a wider range of CPU and GPU hardware. +3. `OpenCL `_ for a wider range of CPU and GPU hardware. Additionally, the Message Passing Interface (MPI) can be utilised to implement a simple task farm that can be used to distribute a series of models as independent tasks. This can be useful in many GPR simulations where a B-scan (composed of multiple A-scans) is required. Each A-scan can be task-farmed as an independent model, and within each model, OpenMP or CUDA can still be used for parallelism. This creates mixed mode OpenMP/MPI or CUDA/MPI environments. @@ -74,7 +74,7 @@ Run one of the test models: .. note:: - * If you want to select a specific GPU card on your system, you can specify an integer after the ``-gpu`` flag. The integer should be the NVIDIA CUDA device ID for a specific GPU card. If it is not specified it defaults to device ID 0. + * If you want to select a specific GPU card on your system, you can specify an integer after the ``-gpu`` flag. The integer should be the NVIDIA CUDA device ID for a specific GPU card. If it is not specified it defaults to device ID 0. * You can use the ``get_host_spec.py`` module (in ``toolboxes/Utilities``) to help you understand what hardware (CPU/GPU) you have and how gprMax can use it. @@ -108,7 +108,7 @@ Run one of the test models: CUDA/MPI ======== -Message Passing Interface (MPI) has been utilised to implement a simple task farm that can be used to distribute a series of models as independent tasks. This is described in more detail in the :ref:`HPC ` section. MPI can be combined with the GPU functionality to allow a series of models to be distributed to multiple GPUs on the same machine (node). +Message Passing Interface (MPI) has been utilised to implement a simple task farm that can be used to distribute a series of models as independent tasks. This is described in more detail in the :ref:`HPC ` section. MPI can be combined with the GPU functionality to allow a series of models to be distributed to multiple GPUs on the same machine (node). Example ------- diff --git a/docs/source/comparisons_numerical.rst b/docs/source/comparisons_numerical.rst index 109f305a..96609555 100644 --- a/docs/source/comparisons_numerical.rst +++ b/docs/source/comparisons_numerical.rst @@ -50,4 +50,3 @@ Results Input impedance (resistive and reactive) of a bowtie antenna in free space using FDTD (gprMax) and MoM (MATLAB) models. The results from the FDTD and MoM modelling techniques are in very good agreement. The biggest mismatch occurs in the resistive part of the input impedance at frequencies above 3GHz. - diff --git a/docs/source/conf.py b/docs/source/conf.py index 408e4731..5fff2b52 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -9,30 +9,26 @@ import re import time -project = 'gprMax' +project = "gprMax" copyright = f'2015-{time.strftime("%Y")}, The University of Edinburgh, United Kingdom. Authors: Craig Warren, Antonis Giannopoulos, and John Hartley' -author = 'Craig Warren, Antonis Giannopoulos, and John Hartley' -with open('../../gprMax/_version.py', 'r') as fd: - version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', - fd.read(), re.MULTILINE).group(1) +author = "Craig Warren, Antonis Giannopoulos, and John Hartley" +with open("../../gprMax/_version.py", "r") as fd: + version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -extensions = ['sphinx.ext.mathjax', - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon'] +extensions = ["sphinx.ext.mathjax", "sphinx.ext.autodoc", "sphinx.ext.napoleon"] # Figure numbering numfig = True -templates_path = ['_templates'] +templates_path = ["_templates"] exclude_patterns = [] - # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'sphinx_rtd_theme' -html_static_path = ['_static'] +html_theme = "sphinx_rtd_theme" +html_static_path = ["_static"] diff --git a/docs/source/examples_advanced.rst b/docs/source/examples_advanced.rst index 73cd2c39..a33c4a46 100644 --- a/docs/source/examples_advanced.rst +++ b/docs/source/examples_advanced.rst @@ -63,9 +63,9 @@ This example is a basic demonstration of how to use subgrids. The geometry is 3D :language: python :linenos: -Much of the functionality demonstrated in this example is standard use of our :ref:`Python API `, so mainly the parts that relate to the subgrid will be described here. Lines 20-25 specify the spatial discretisation of the course main grid (5mm) and fine subgrid (1mm). Lines 56-60 specify the centres and radius of the cylinder and coordinates of a bounding box which will be used to set the domain of the subgrid. +Much of the functionality demonstrated in this example is standard use of our :ref:`Python API `, so mainly the parts that relate to the subgrid will be described here. Lines 20-25 specify the spatial discretisation of the course main grid (5mm) and fine subgrid (1mm). Lines 56-60 specify the centres and radius of the cylinder and coordinates of a bounding box which will be used to set the domain of the subgrid. -The subgrid object is created on line 63 (providing its extent, the ratio of the spatial resolution, and a string identifier) and then added to the main scene on line 64. Any objects that are to be placed within the subgrid can be added to the subgrid scene (through the variable ``subgrid``) in the same way as the main grid/scene. +The subgrid object is created on line 63 (providing its extent, the ratio of the spatial resolution, and a string identifier) and then added to the main scene on line 64. Any objects that are to be placed within the subgrid can be added to the subgrid scene (through the variable ``subgrid``) in the same way as the main grid/scene. In lines 67-71 the material used to represent water is created and added to the subgrid. The function ``calculate_water_properties()`` is used to help define the properties of water which is represented as a dispersive material using a single pole Debye model. @@ -122,14 +122,4 @@ The performance of each PML can be compared with a reference solution using the :language: python :linenos: -In lines 43-122 a dictionary with different PML formulations and parameters is created. - - - - - - - - - - +In lines 43-122 a dictionary with different PML formulations and parameters is created. diff --git a/docs/source/features.rst b/docs/source/features.rst index cdf9f991..b932e1f5 100644 --- a/docs/source/features.rst +++ b/docs/source/features.rst @@ -9,7 +9,7 @@ This section highlights some of the key features of gprMax that are useful for G Python API ========== -There is now a **Python API**, which includes all the functionality of the input file (hash) commands as well as several more advanced features. It allows users to access to gprMax functions directly from Python by importing the gprMax module. This method is recommended for those who prefer to use Python or need access to specific API-only advanced features, and is described in the :ref:`Python API ` section. There are several advantages to using the Python API: +There is now a **Python API**, which includes all the functionality of the input file (hash) commands as well as several more advanced features. It allows users to access to gprMax functions directly from Python by importing the gprMax module. This method is recommended for those who prefer to use Python or need access to specific API-only advanced features, and is described in the :ref:`Python API ` section. There are several advantages to using the Python API: 1. Users can take advantage of the Python language - for instance, the structural elements of Python can be utilised more easily. 2. gprMax objects can be used directly within functions, classes, modules and packages. In this way, collections of components can be defined, reused, and modified. For example, complex targets can be imported from a separate module and combined with an antenna from another module. diff --git a/docs/source/hpc.rst b/docs/source/hpc.rst index 2f30c020..ab3f4c20 100644 --- a/docs/source/hpc.rst +++ b/docs/source/hpc.rst @@ -52,4 +52,4 @@ Here is an example of a job script for running models, e.g. A-scans to make a B- The ``-t`` tells Grid Engine that we are using a job array followed by a range of integers which will be the IDs for each individual task (model). Task IDs must start from 1, and the total number of tasks in the range should correspond to the number of models you want to run, i.e. the integer with the ``-n`` flag passed to gprMax. The ``-i`` flag is passed to gprMax along with the specific number of the task (model) with the environment variable ``$SGE_TASK_ID``. -A job array means that exactly the same submit script is going to be run multiple times, the only difference between each run is the environment variable ``$SGE_TASK_ID``. \ No newline at end of file +A job array means that exactly the same submit script is going to be run multiple times, the only difference between each run is the environment variable ``$SGE_TASK_ID``. diff --git a/docs/source/inc_AntennaPatterns.rst b/docs/source/inc_AntennaPatterns.rst index 07453119..ef238759 100644 --- a/docs/source/inc_AntennaPatterns.rst +++ b/docs/source/inc_AntennaPatterns.rst @@ -1 +1 @@ -.. include:: ../../toolboxes/AntennaPatterns/README.rst \ No newline at end of file +.. include:: ../../toolboxes/AntennaPatterns/README.rst diff --git a/docs/source/inc_AustinMan.rst b/docs/source/inc_AustinMan.rst index 14d347aa..79cade6c 100644 --- a/docs/source/inc_AustinMan.rst +++ b/docs/source/inc_AustinMan.rst @@ -1 +1 @@ -.. include:: ../../toolboxes/AustinManWoman/README.rst \ No newline at end of file +.. include:: ../../toolboxes/AustinManWoman/README.rst diff --git a/docs/source/inc_DebyeFit.rst b/docs/source/inc_DebyeFit.rst index bf2928a4..7067c82c 100644 --- a/docs/source/inc_DebyeFit.rst +++ b/docs/source/inc_DebyeFit.rst @@ -1 +1 @@ -.. include:: ../../toolboxes/DebyeFit/README.rst \ No newline at end of file +.. include:: ../../toolboxes/DebyeFit/README.rst diff --git a/docs/source/inc_GPRAntennaModels.rst b/docs/source/inc_GPRAntennaModels.rst index 481ebba4..54ab026a 100644 --- a/docs/source/inc_GPRAntennaModels.rst +++ b/docs/source/inc_GPRAntennaModels.rst @@ -1 +1 @@ -.. include:: ../../toolboxes/GPRAntennaModels/README.rst \ No newline at end of file +.. include:: ../../toolboxes/GPRAntennaModels/README.rst diff --git a/docs/source/inc_LandmineModels.rst b/docs/source/inc_LandmineModels.rst index b8e647c8..34e7eef6 100644 --- a/docs/source/inc_LandmineModels.rst +++ b/docs/source/inc_LandmineModels.rst @@ -1 +1 @@ -.. include:: ../../toolboxes/LandmineModels/README.rst \ No newline at end of file +.. include:: ../../toolboxes/LandmineModels/README.rst diff --git a/docs/source/inc_Materials.rst b/docs/source/inc_Materials.rst index ec36d7ac..fea4eb0d 100644 --- a/docs/source/inc_Materials.rst +++ b/docs/source/inc_Materials.rst @@ -1 +1 @@ -.. include:: ../../toolboxes/Materials/README.rst \ No newline at end of file +.. include:: ../../toolboxes/Materials/README.rst diff --git a/docs/source/inc_Plotting.rst b/docs/source/inc_Plotting.rst index b2a4b5fe..9eb2c1a3 100644 --- a/docs/source/inc_Plotting.rst +++ b/docs/source/inc_Plotting.rst @@ -1 +1 @@ -.. include:: ../../toolboxes/Plotting/README.rst \ No newline at end of file +.. include:: ../../toolboxes/Plotting/README.rst diff --git a/docs/source/inc_README.rst b/docs/source/inc_README.rst index 38ba8043..a6210d3d 100644 --- a/docs/source/inc_README.rst +++ b/docs/source/inc_README.rst @@ -1 +1 @@ -.. include:: ../../README.rst \ No newline at end of file +.. include:: ../../README.rst diff --git a/docs/source/inc_STLtoVoxel.rst b/docs/source/inc_STLtoVoxel.rst index 2f8c7036..5204a0b3 100644 --- a/docs/source/inc_STLtoVoxel.rst +++ b/docs/source/inc_STLtoVoxel.rst @@ -1 +1 @@ -.. include:: ../../toolboxes/STLtoVoxel/README.rst \ No newline at end of file +.. include:: ../../toolboxes/STLtoVoxel/README.rst diff --git a/docs/source/inc_Utilities.rst b/docs/source/inc_Utilities.rst index 48606a46..b663d158 100644 --- a/docs/source/inc_Utilities.rst +++ b/docs/source/inc_Utilities.rst @@ -1 +1 @@ -.. include:: ../../toolboxes/Utilities/README.rst \ No newline at end of file +.. include:: ../../toolboxes/Utilities/README.rst diff --git a/docs/source/input_api.rst b/docs/source/input_api.rst index 6bc65ebe..b239e732 100644 --- a/docs/source/input_api.rst +++ b/docs/source/input_api.rst @@ -12,7 +12,7 @@ gprMax has a choice of two methods for building a model to simulate: 1. A **text-based (ASCII) input file**, which can be created with any text editor, and uses a series of gprMax commands which begin with the hash character (``#``). This method is recommended for beginners and those not familiar with Python, and is described in the :ref:`input-hash-cmds` section. 2. A **Python API**, which includes all the functionality of method 1 as well as several more advanced features. This method is recommended for those who prefer to use Python or need access to specific API-only advanced features, and is described in this section of the documentation. -The Python API in gprMax allows users to access to gprMax functions directly from Python through importing the gprMax module. There are several advantages to using the API: +The Python API in gprMax allows users to access to gprMax functions directly from Python through importing the gprMax module. There are several advantages to using the API: * Users can take advantage of the Python language - for instance, the structural elements of Python can be utilised more easily. * gprMax objects can be used directly within functions, classes, modules and packages. In this way collections of components can be defined, reused and modified. For example, complex targets can be imported from a separate module and combined with an antenna from another module. @@ -25,7 +25,7 @@ Example :download:`antenna_wire_dipole_fs.py <../../examples/antenna_wire_dipole_fs.py>` -This example is used to give an introduction to the gprMax Python API. +This example is used to give an introduction to the gprMax Python API. .. literalinclude:: ../../examples/antenna_wire_dipole_fs.py :language: python @@ -260,7 +260,7 @@ Allows you control of the specific parameters that are used to build each order .. autoclass:: gprMax.cmds_multiuse.PMLCFS -The CFS values (which are internally specified) used for the default standard first order PML are: +The CFS values (which are internally specified) used for the default standard first order PML are: * ``alphascalingprofile = 'constant'`` * ``alphascalingdirection = 'forward'`` * ``alphamin = 0`` @@ -275,7 +275,7 @@ The CFS values (which are internally specified) used for the default standard fi * ``sigmamax = None`` .. note:: - + * The parameters will be applied to all slabs of the PML that are switched on. - * Using ``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. + * Using ``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. * ``forward`` direction implies a minimum parameter value at the inner boundary of the PML and maximum parameter value at the edge of the computational domain, ``reverse`` is the opposite. diff --git a/docs/source/input_hash_cmds.rst b/docs/source/input_hash_cmds.rst index 8b7a84fa..abca708f 100644 --- a/docs/source/input_hash_cmds.rst +++ b/docs/source/input_hash_cmds.rst @@ -585,7 +585,7 @@ Allows you to introduce an ellipsoid into the model. The syntax of the command i #ellipsoid: f1 f2 f3 f4 f5 f6 str1 [c1] -* ``f1 f2 f3`` are the coordinates (x,y,z) of the centre of the ellipsoid. +* ``f1 f2 f3`` are the coordinates (x,y,z) of the centre of the ellipsoid. * ``f4 f5 f6`` are the coordinates (x,y,z) of the semi-axes of the ellipsoid. * ``str1`` is a material identifier that must correspond to material that has already been defined in the input file, or is one of the builtin materials. * ``c1`` is an optional parameter which can be ``y`` or ``n``, used to switch on and off dielectric smoothing. @@ -984,4 +984,4 @@ 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 \ No newline at end of file + #pml_cells: 10 10 20 10 10 20 diff --git a/docs/source/output.rst b/docs/source/output.rst index 82107a4d..29ea2fa0 100644 --- a/docs/source/output.rst +++ b/docs/source/output.rst @@ -115,8 +115,8 @@ Snapshot files contain a snapshot of the electromagnetic field values of a speci import gprMax for i in range(1, 31): - s = gprMax.Snapshot(p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), - time=(i/10) * 1e-9, + s = gprMax.Snapshot(p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), + time=(i/10) * 1e-9, filename=fn.with_suffix('').parts[-1] + '_' + str(i)) scene.add(s) diff --git a/examples/antenna_like_GSSI_1500_fs.py b/examples/antenna_like_GSSI_1500_fs.py index fdb1b0db..989f5e7e 100644 --- a/examples/antenna_like_GSSI_1500_fs.py +++ b/examples/antenna_like_GSSI_1500_fs.py @@ -1,8 +1,8 @@ """An antenna model similar to a GSSI 1.5GHz antenna in free space -This example model demonstrates how to use one of the built-in antenna models. +This example model demonstrates how to use one of the built-in antenna models. -The geometry is 3D and the domain filled with freespace (the default). The +The geometry is 3D and the domain filled with freespace (the default). The antenna model method is imported from its toolbox and the objects that build the antenna are iteratively added to the scene. The antenna can be rotated if desired, by rotating the objects that it is built from before they are added to @@ -27,7 +27,7 @@ z = 0.220 scene = gprMax.Scene() -title = gprMax.Title(name=fn.with_suffix('').name) +title = gprMax.Title(name=fn.with_suffix("").name) domain = gprMax.Domain(p1=(x, y, z)) dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) time_window = gprMax.TimeWindow(time=6e-9) @@ -39,21 +39,23 @@ scene.add(time_window) # Import antenna model and add to model ant_pos = (0.125, 0.094, 0.100) -gssi_objects = antenna_like_GSSI_1500(ant_pos[0], ant_pos[1], ant_pos[2], - resolution=dl) +gssi_objects = antenna_like_GSSI_1500(ant_pos[0], ant_pos[1], ant_pos[2], resolution=dl) for obj in gssi_objects: - #obj.rotate('z', 90, origin=(ant_pos[0], ant_pos[1], ant_pos[2])) + # obj.rotate('z', 90, origin=(ant_pos[0], ant_pos[1], ant_pos[2])) scene.add(obj) -gv1 = gprMax.GeometryView(p1=(0, 0, 0), p2=(x, y, z), - dl=(dl, dl, dl), filename='antenna_like_GSSI_1500', - output_type='n') -gv2 = gprMax.GeometryView(p1=(ant_pos[0] - 0.170/2, ant_pos[1] - 0.108/2, ant_pos[2] - 0.050), - p2=(ant_pos[0] + 0.170/2, ant_pos[1] + 0.108/2, ant_pos[2] + 0.010), - dl=(dl, dl, dl), filename='antenna_like_GSSI_1500_pcb', - output_type='f') -#scene.add(gv1) -#scene.add(gv2) +gv1 = gprMax.GeometryView( + p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), filename="antenna_like_GSSI_1500", output_type="n" +) +gv2 = gprMax.GeometryView( + p1=(ant_pos[0] - 0.170 / 2, ant_pos[1] - 0.108 / 2, ant_pos[2] - 0.050), + p2=(ant_pos[0] + 0.170 / 2, ant_pos[1] + 0.108 / 2, ant_pos[2] + 0.010), + dl=(dl, dl, dl), + filename="antenna_like_GSSI_1500_pcb", + output_type="f", +) +# scene.add(gv1) +# scene.add(gv2) # Run model gprMax.run(scenes=[scene], geometry_only=False, outputfile=fn, gpu=None) diff --git a/examples/antenna_like_GSSI_1500_patterns.py b/examples/antenna_like_GSSI_1500_patterns.py index ce6d5b2c..2644ba70 100644 --- a/examples/antenna_like_GSSI_1500_patterns.py +++ b/examples/antenna_like_GSSI_1500_patterns.py @@ -13,71 +13,66 @@ dl = 0.001 scene = gprMax.Scene() -title = gprMax.Title(name=fn.with_suffix('').name) +title = gprMax.Title(name=fn.with_suffix("").name) dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) pml = gprMax.PMLProps(thickness=14) scene.add(title) scene.add(dxdydz) scene.add(pml) -timewindow = 4.5e-9 # For 0.3m max +timewindow = 4.5e-9 # For 0.3m max radii = np.linspace(0.1, 0.3, 20) theta = np.linspace(3, 357, 60) fs = np.array([0.040, 0.040, 0.040]) -domain_size = np.array([2 * fs[0] + 0.170, - 2 * fs[1] + 2 * radii[-1], - 2 * fs[2] + 2 * radii[-1]]) +domain_size = np.array([2 * fs[0] + 0.170, 2 * fs[1] + 2 * radii[-1], 2 * fs[2] + 2 * radii[-1]]) domain = gprMax.Domain(p1=(domain_size[0], domain_size[1], domain_size[2])) time_window = gprMax.TimeWindow(time=timewindow) scene.add(domain) scene.add(time_window) -antennaposition = np.array([domain_size[0] / 2, - fs[1] + radii[-1], - fs[2] + radii[-1]]) -gssi_objects = antenna_like_GSSI_1500(antennaposition[0], - antennaposition[1], - antennaposition[2]) +antennaposition = np.array([domain_size[0] / 2, fs[1] + radii[-1], fs[2] + radii[-1]]) +gssi_objects = antenna_like_GSSI_1500(antennaposition[0], antennaposition[1], antennaposition[2]) for obj in gssi_objects: scene.add(obj) ## Can introduce soil model -# soil = gprMax.SoilPeplinski(sand_fraction=0.5, clay_fraction=0.5, +# soil = gprMax.SoilPeplinski(sand_fraction=0.5, clay_fraction=0.5, # bulk_density=2.0, sand_density=2.66, # water_fraction_lower=0.001, # water_fraction_upper=0.25, # id='mySoil') # scene.add(soil) -# fbox = gprMax.FractalBox(p1=(0, 0, 0), p2=(domain_size[0], domain_size[1], fs[2] + radii[-1]), -# frac_dim=1.5, weighting=[1, 1, 1], n_materials=50, +# fbox = gprMax.FractalBox(p1=(0, 0, 0), p2=(domain_size[0], domain_size[1], fs[2] + radii[-1]), +# frac_dim=1.5, weighting=[1, 1, 1], n_materials=50, # mixing_model_id=soil.id, id='mySoilBox') # scene.add(fbox) -mat = gprMax.Material(er=5, se=0, mr=1, sm=0, id='er5') +mat = gprMax.Material(er=5, se=0, mr=1, sm=0, id="er5") scene.add(mat) -box = gprMax.Box(p1=(0, 0, 0), p2=(domain_size[0], domain_size[1], fs[2] + radii[-1]), - material_id='er5') +box = gprMax.Box(p1=(0, 0, 0), p2=(domain_size[0], domain_size[1], fs[2] + radii[-1]), material_id="er5") scene.add(box) ## Save the position of the antenna to file for use when processing results -np.savetxt(fn.with_suffix('').name + '_rxsorigin.txt', antennaposition, fmt="%f") +np.savetxt(fn.with_suffix("").name + "_rxsorigin.txt", antennaposition, fmt="%f") ## Generate receiver points for pattern for radius in range(len(radii)): ## E-plane circle (yz plane, x=0, phi=pi/2,3pi/2) - x = radii[radius] * np.sin(theta * np.pi /180) * np.cos(90 * np.pi / 180) - y = radii[radius] * np.sin(theta * np.pi /180) * np.sin(90 * np.pi / 180) - z = radii[radius] * np.cos(theta * np.pi /180) + x = radii[radius] * np.sin(theta * np.pi / 180) * np.cos(90 * np.pi / 180) + y = radii[radius] * np.sin(theta * np.pi / 180) * np.sin(90 * np.pi / 180) + z = radii[radius] * np.cos(theta * np.pi / 180) for rxpt in range(len(theta)): - rx = gprMax.Rx(p1=(x[rxpt] + antennaposition[0], - y[rxpt] + antennaposition[1], - z[rxpt] + antennaposition[2])) + rx = gprMax.Rx(p1=(x[rxpt] + antennaposition[0], y[rxpt] + antennaposition[1], z[rxpt] + antennaposition[2])) scene.add(rx) -gv1 = gprMax.GeometryView(p1=(0, 0, 0), p2=(domain_size[0], domain_size[1], domain_size[2]), - dl=(dl, dl, dl), filename='antenna_like_GSSI_1500_patterns', - output_type='n') +gv1 = gprMax.GeometryView( + p1=(0, 0, 0), + p2=(domain_size[0], domain_size[1], domain_size[2]), + dl=(dl, dl, dl), + filename="antenna_like_GSSI_1500_patterns", + output_type="n", +) scene.add(gv1) -gprMax.run(scenes=[scene], geometry_only=True, outputfile=fn, gpu=None) \ No newline at end of file +gprMax.run(scenes=[scene], geometry_only=True, outputfile=fn, gpu=None) diff --git a/examples/antenna_like_MALA_1200_fs.py b/examples/antenna_like_MALA_1200_fs.py index a94d0b8f..d48eef05 100644 --- a/examples/antenna_like_MALA_1200_fs.py +++ b/examples/antenna_like_MALA_1200_fs.py @@ -1,8 +1,8 @@ """An antenna model similar to a MALA 1.2GHz antenna in free space -This example model demonstrates how to use one of the built-in antenna models. +This example model demonstrates how to use one of the built-in antenna models. -The geometry is 3D and the domain filled with freespace (the default). The +The geometry is 3D and the domain filled with freespace (the default). The antenna model method is imported from its toolbox and the objects that build the antenna are iteratively added to the scene. """ @@ -25,7 +25,7 @@ z = 0.220 scene = gprMax.Scene() -title = gprMax.Title(name=fn.with_suffix('').name) +title = gprMax.Title(name=fn.with_suffix("").name) domain = gprMax.Domain(p1=(x, y, z)) dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) time_window = gprMax.TimeWindow(time=6e-9) @@ -37,8 +37,7 @@ scene.add(time_window) # Import antenna model and add to model ant_pos = (0.132, 0.095, 0.100) -mala_objects = antenna_like_MALA_1200(ant_pos[0], ant_pos[1], ant_pos[2], - resolution=dl) +mala_objects = antenna_like_MALA_1200(ant_pos[0], ant_pos[1], ant_pos[2], resolution=dl) for obj in mala_objects: scene.add(obj) diff --git a/examples/antenna_wire_dipole_fs.py b/examples/antenna_wire_dipole_fs.py index d459c765..8cd3b4ab 100644 --- a/examples/antenna_wire_dipole_fs.py +++ b/examples/antenna_wire_dipole_fs.py @@ -2,7 +2,7 @@ This example model demonstrates how to a the transmission line source, which allows s-parameters and input impedances to be calculated after the simulation -has run. +has run. Both API and hash commands are given next to each other as a comparison of the two different methods that can be used to build a model. @@ -14,45 +14,42 @@ import gprMax fn = Path(__file__) -title = gprMax.Title(name=fn.with_suffix('').name) -#title: antenna_wire_dipole_fs +title = gprMax.Title(name=fn.with_suffix("").name) +# title: antenna_wire_dipole_fs domain = gprMax.Domain(p1=(0.050, 0.050, 0.200)) -#domain: 0.050 0.050 0.200 +# domain: 0.050 0.050 0.200 dxdydz = gprMax.Discretisation(p1=(0.001, 0.001, 0.001)) -#dx_dy_dz: 0.001 0.001 0.001 +# dx_dy_dz: 0.001 0.001 0.001 time_window = gprMax.TimeWindow(time=10e-9) -#time_window: 10e-9 +# time_window: 10e-9 -waveform = gprMax.Waveform(wave_type='gaussian', amp=1, freq=1e9, id='mypulse') -#waveform: gaussian 1 1e9 mypulse +waveform = gprMax.Waveform(wave_type="gaussian", amp=1, freq=1e9, id="mypulse") +# waveform: gaussian 1 1e9 mypulse -transmission_line = gprMax.TransmissionLine(polarisation='z', - p1=(0.025, 0.025, 0.100), - resistance=73, - waveform_id='mypulse') -#transmission_line: z 0.025 0.025 0.100 73 mypulse +transmission_line = gprMax.TransmissionLine( + polarisation="z", p1=(0.025, 0.025, 0.100), resistance=73, waveform_id="mypulse" +) +# transmission_line: z 0.025 0.025 0.100 73 mypulse ## 150mm length wire -e1 = gprMax.Edge(p1=(0.025, 0.025, 0.025), - p2=(0.025, 0.025, 0.175), - material_id='pec') -#edge: 0.025 0.025 0.025 0.025 0.025 0.175 pec +e1 = gprMax.Edge(p1=(0.025, 0.025, 0.025), p2=(0.025, 0.025, 0.175), material_id="pec") +# edge: 0.025 0.025 0.025 0.025 0.025 0.175 pec ## 1mm gap at centre of dipole -e2 = gprMax.Edge(p1=(0.025, 0.025, 0.100), - p2=(0.025, 0.025, 0.101), - material_id='free_space') -#edge: 0.025 0.025 0.100 0.025 0.025 0.101 free_space +e2 = gprMax.Edge(p1=(0.025, 0.025, 0.100), p2=(0.025, 0.025, 0.101), material_id="free_space") +# edge: 0.025 0.025 0.100 0.025 0.025 0.101 free_space -gv = gprMax.GeometryView(p1=(0.020, 0.020, 0.020), - p2=(0.030, 0.030, 0.180), - dl=(0.001, 0.001, 0.001), - filename=fn.with_suffix('').name, - output_type='n') -#geometry_view: 0.020 0.020 0.020 0.030 0.030 0.180 0.001 0.001 0.001 antenna_wire_dipole_fs f +gv = gprMax.GeometryView( + p1=(0.020, 0.020, 0.020), + p2=(0.030, 0.030, 0.180), + dl=(0.001, 0.001, 0.001), + filename=fn.with_suffix("").name, + output_type="n", +) +# geometry_view: 0.020 0.020 0.020 0.030 0.030 0.180 0.001 0.001 0.001 antenna_wire_dipole_fs f # Create a scene scene = gprMax.Scene() @@ -69,4 +66,4 @@ scene.add(e2) scene.add(gv) # Run the simulation -gprMax.run(scenes=[scene], n=1, outputfile=fn) \ No newline at end of file +gprMax.run(scenes=[scene], n=1, outputfile=fn) diff --git a/examples/cylinder_Ascan_2D.in b/examples/cylinder_Ascan_2D.in index edd88986..870cda28 100644 --- a/examples/cylinder_Ascan_2D.in +++ b/examples/cylinder_Ascan_2D.in @@ -12,4 +12,4 @@ #box: 0 0 0 0.240 0.170 0.002 half_space #cylinder: 0.120 0.080 0 0.120 0.080 0.002 0.010 pec -#geometry_view: 0 0 0 0.240 0.210 0.002 0.002 0.002 0.002 cylinder_half_space n \ No newline at end of file +#geometry_view: 0 0 0 0.240 0.210 0.002 0.002 0.002 0.002 cylinder_half_space n diff --git a/examples/cylinder_Bscan_2D.in b/examples/cylinder_Bscan_2D.in index aa75bacb..e41f9857 100644 --- a/examples/cylinder_Bscan_2D.in +++ b/examples/cylinder_Bscan_2D.in @@ -12,4 +12,4 @@ #rx_steps: 0.002 0 0 #box: 0 0 0 0.240 0.170 0.002 half_space -#cylinder: 0.120 0.080 0 0.120 0.080 0.002 0.010 pec \ No newline at end of file +#cylinder: 0.120 0.080 0 0.120 0.080 0.002 0.010 pec diff --git a/examples/cylinder_Bscan_GSSI_1500.py b/examples/cylinder_Bscan_GSSI_1500.py index 1b0fdf4b..e7e004ad 100644 --- a/examples/cylinder_Bscan_GSSI_1500.py +++ b/examples/cylinder_Bscan_GSSI_1500.py @@ -1,16 +1,16 @@ -"""B-scan using a antenna model. +"""B-scan using a antenna model. This example model demonstrates how to create a B-scan using an antenna model. The key part of this example is the concept of moving the antenna model in steps -within the model domain to create multiple A-scans that build up the B-scan. +within the model domain to create multiple A-scans that build up the B-scan. Each A-scan requires its own scene and a list of scenes it built up using a for loop. A different scene is required for each A-scan because the model geometry is -changing, as the antenna geometry must be moved to a new position. This is in +changing, as the antenna geometry must be moved to a new position. This is in contrast to simpler models that may use a Hertzian dipole source which has no associated 'geometry' and can be moved within a model without having to change the scene. When the all the scenes are created, the list of scenes is then passed -to gprMax to run, noting the number of times 'n' gprMax is run corresponds to the +to gprMax to run, noting the number of times 'n' gprMax is run corresponds to the number of scenes, i.e. A-scans. """ @@ -36,7 +36,7 @@ scenes = [] for i in range(1, 55): scene = gprMax.Scene() - title = gprMax.Title(name=fn.with_suffix('').name) + title = gprMax.Title(name=fn.with_suffix("").name) domain = gprMax.Domain(p1=(x, y, z)) dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) time_window = gprMax.TimeWindow(time=6e-9) @@ -46,25 +46,23 @@ for i in range(1, 55): scene.add(dxdydz) scene.add(time_window) - mat = gprMax.Material(er=6, se=0, mr=1, sm=0, id='half_space') - c1 = gprMax.Cylinder(p1=(0.240, 0, 0.080), p2=(0.240, 0.148, 0.080), r=0.010, - material_id='pec') + mat = gprMax.Material(er=6, se=0, mr=1, sm=0, id="half_space") + c1 = gprMax.Cylinder(p1=(0.240, 0, 0.080), p2=(0.240, 0.148, 0.080), r=0.010, material_id="pec") scene.add(mat) scene.add(c1) # Import antenna model and add to model ant_pos = (0.125, 0.094, 0.100) - gssi_objects = antenna_like_GSSI_1500(0.105 + i * 0.005, 0.074, 0.170, - resolution=dl) + gssi_objects = antenna_like_GSSI_1500(0.105 + i * 0.005, 0.074, 0.170, resolution=dl) for obj in gssi_objects: scene.add(obj) - gv1 = gprMax.GeometryView(p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), - filename=fn.with_suffix('').name, - output_type='n') + gv1 = gprMax.GeometryView( + p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), filename=fn.with_suffix("").name, output_type="n" + ) # scene.add(gv1) scenes.append(scene) # Run model -gprMax.run(scenes=scenes, n=len(scenes), geometry_only=False, outputfile=fn, gpu=None) \ No newline at end of file +gprMax.run(scenes=scenes, n=len(scenes), geometry_only=False, outputfile=fn, gpu=None) diff --git a/examples/heterogeneous_soil.in b/examples/heterogeneous_soil.in index 4f0a10d5..558d8ea1 100644 --- a/examples/heterogeneous_soil.in +++ b/examples/heterogeneous_soil.in @@ -11,4 +11,4 @@ #fractal_box: 0 0 0 0.15 0.15 0.070 1.5 1 1 1 50 my_soil my_soil_box #add_surface_roughness: 0 0 0.070 0.15 0.15 0.070 1.5 1 1 0.065 0.080 my_soil_box -#geometry_view: 0 0 0 0.15 0.15 0.1 0.001 0.001 0.001 heterogeneous_soil n \ No newline at end of file +#geometry_view: 0 0 0 0.15 0.15 0.1 0.001 0.001 0.001 heterogeneous_soil n diff --git a/examples/jupyter-notebooks/README.rst b/examples/jupyter-notebooks/README.rst index e4cec863..8516c5c1 100644 --- a/examples/jupyter-notebooks/README.rst +++ b/examples/jupyter-notebooks/README.rst @@ -1 +1 @@ -**Tip:** ``ipynb`` files can be viewed on GitHub. Just click them. \ No newline at end of file +**Tip:** ``ipynb`` files can be viewed on GitHub. Just click them. diff --git a/examples/subgrids/cylinder_fs.py b/examples/subgrids/cylinder_fs.py index 9a0a1a46..90928658 100644 --- a/examples/subgrids/cylinder_fs.py +++ b/examples/subgrids/cylinder_fs.py @@ -2,10 +2,10 @@ This example model demonstrates how to use subgrids at a basic level. -The geometry is 3D (required for any use of subgrids) and is of a water-filled +The geometry is 3D (required for any use of subgrids) and is of a water-filled cylindrical object in freespace. The subgrid encloses the cylinderical object -using a fine spatial discretisation (1mm), and a courser spatial discretisation -(5mm) is used in the rest of the model (main grid). A simple Hertzian dipole +using a fine spatial discretisation (1mm), and a courser spatial discretisation +(5mm) is used in the rest of the model (main grid). A simple Hertzian dipole source is used with a waveform shaped as the first derivative of a gaussian. """ @@ -39,9 +39,8 @@ dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) domain = gprMax.Domain(p1=(x, y, z)) time_window = gprMax.TimeWindow(time=tw) -wf = gprMax.Waveform(wave_type='gaussiandot', amp=1, freq=1.5e9, id='mypulse') -hd = gprMax.HertzianDipole(polarisation='z', p1=(0.205, 0.400, 0.250), - waveform_id='mypulse') +wf = gprMax.Waveform(wave_type="gaussiandot", amp=1, freq=1.5e9, id="mypulse") +hd = gprMax.HertzianDipole(polarisation="z", p1=(0.205, 0.400, 0.250), waveform_id="mypulse") rx = gprMax.Rx(p1=(0.245, 0.400, 0.250)) scene.add(title) @@ -60,36 +59,40 @@ sg1 = (c1[0] - r, c1[1] - r, c1[2]) sg2 = (c2[0] + r, c2[1] + r, c2[2]) # Create subgrid -subgrid = gprMax.SubGridHSG(p1=sg1, p2=sg2, ratio=ratio, id='sg') +subgrid = gprMax.SubGridHSG(p1=sg1, p2=sg2, ratio=ratio, id="sg") scene.add(subgrid) # Create water material eri, er, tau, sig = calculate_water_properties() -water = gprMax.Material(er=eri, se=sig, mr=1, sm=0, id='water') +water = gprMax.Material(er=eri, se=sig, mr=1, sm=0, id="water") subgrid.add(water) -water = gprMax.AddDebyeDispersion(poles=1, er_delta=[er - eri], tau=[tau], material_ids=['water']) +water = gprMax.AddDebyeDispersion(poles=1, er_delta=[er - eri], tau=[tau], material_ids=["water"]) subgrid.add(water) # Add cylinder to subgrid -cylinder = gprMax.Cylinder(p1=c1, p2=c2, r=r, material_id='water') +cylinder = gprMax.Cylinder(p1=c1, p2=c2, r=r, material_id="water") subgrid.add(cylinder) # Create some geometry views for both subgrid and main grid -gvsg = gprMax.GeometryView(p1=sg1, p2=sg2, dl=(dl_sg, dl_sg, dl_sg), - filename=fn.with_suffix('').parts[-1] + '_sg', - output_type='n') +gvsg = gprMax.GeometryView( + p1=sg1, p2=sg2, dl=(dl_sg, dl_sg, dl_sg), filename=fn.with_suffix("").parts[-1] + "_sg", output_type="n" +) subgrid.add(gvsg) -gv1 = gprMax.GeometryView(p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), - filename=fn.with_suffix('').parts[-1], - output_type='n') +gv1 = gprMax.GeometryView( + p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), filename=fn.with_suffix("").parts[-1], output_type="n" +) scene.add(gv1) # Create some snapshots of entire domain for i in range(5): - s = gprMax.Snapshot(p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl), - time=(i + 0.5) * 1e-9, - filename=fn.with_suffix('').parts[-1] + '_' + str(i + 1)) + s = gprMax.Snapshot( + p1=(0, 0, 0), + p2=(x, y, z), + dl=(dl, dl, dl), + time=(i + 0.5) * 1e-9, + filename=fn.with_suffix("").parts[-1] + "_" + str(i + 1), + ) scene.add(s) gprMax.run(scenes=[scene], n=1, geometry_only=False, outputfile=fn, subgrid=True, autotranslate=True) diff --git a/examples/subgrids/gssi_400_over_fractal_subsurface.py b/examples/subgrids/gssi_400_over_fractal_subsurface.py index 67da324b..fb8e4875 100644 --- a/examples/subgrids/gssi_400_over_fractal_subsurface.py +++ b/examples/subgrids/gssi_400_over_fractal_subsurface.py @@ -1,16 +1,16 @@ -"""GPR antenna model (like a GSSI 400MHz antenna) over layered media with a +"""GPR antenna model (like a GSSI 400MHz antenna) over layered media with a rough subsurface interface. -This example model demonstrates how to use subgrids at a more advanced level - +This example model demonstrates how to use subgrids at a more advanced level - combining use of an imported antenna model and rough subsurface interface. The geometry is 3D (required for any use of subgrids) and is of a 2 layered subsurface. The top layer in a sandy soil and the bottom layer a soil with higher permittivity (both have some simple conductive loss). There is a rough interface between the soil layers. A GPR antenna model (like a GSSI 400MHz -antenna) is imported and placed on the surface of the layered media. The antenna -is meshed using a subgrid with a fine spatial discretisation (1mm), and a -courser spatial discretisation (9mm) is used in the rest of the model (main +antenna) is imported and placed on the surface of the layered media. The antenna +is meshed using a subgrid with a fine spatial discretisation (1mm), and a +courser spatial discretisation (9mm) is used in the rest of the model (main grid). """ @@ -38,7 +38,7 @@ y = 1 z = 2 # Time window -# Estimated two way travel time over 1 metre in material with highest +# Estimated two way travel time over 1 metre in material with highest # permittivity, slowest velocity. tw = 2 / 3e8 * (np.sqrt(3.2) + np.sqrt(9)) @@ -72,21 +72,19 @@ sg_y1 = antenna_p[1] + antenna_case[1] / 2 + bounding_box sg_z1 = antenna_p[2] + antenna_case[2] + bounding_box # Create subgrid -sg = gprMax.SubGridHSG(p1=[sg_x0, sg_y0, sg_z0], - p2=[sg_x1, sg_y1, sg_z1], - ratio=ratio, id='sg') +sg = gprMax.SubGridHSG(p1=[sg_x0, sg_y0, sg_z0], p2=[sg_x1, sg_y1, sg_z1], ratio=ratio, id="sg") scene.add(sg) # Create and add a box of homogeneous material to main grid - sandy_soil -sandy_soil = gprMax.Material(er=3.2, se=0.397e-3, mr=1, sm=0, id='sandy_soil') +sandy_soil = gprMax.Material(er=3.2, se=0.397e-3, mr=1, sm=0, id="sandy_soil") scene.add(sandy_soil) -b1 = gprMax.Box(p1=(0, 0, 0), p2=(x, y, antenna_p[2]), material_id='sandy_soil') +b1 = gprMax.Box(p1=(0, 0, 0), p2=(x, y, antenna_p[2]), material_id="sandy_soil") scene.add(b1) # Position box of sandy_soil in the subgrid. # It has to be positioned manually because it traverses the main grid/subgrid -# interface. Grid traversal is when objects extend beyond the outer surface. -# Setting autotranslate to false allows you to place objects beyond the outer +# interface. Grid traversal is when objects extend beyond the outer surface. +# Setting autotranslate to false allows you to place objects beyond the outer # surface. # PML separation from the outer surface @@ -101,8 +99,7 @@ h = antenna_p[2] - sg_z0 + (ps + pc + isos) * dl_sg # Create and add a box of homogeneous material to subgrid - sandy_soil sg.add(sandy_soil) -b2 = gprMax.Box(p1=(0, 0, 0), - p2=(411 * dl_sg, 411 * dl_sg, h), material_id='sandy_soil') +b2 = gprMax.Box(p1=(0, 0, 0), p2=(411 * dl_sg, 411 * dl_sg, h), material_id="sandy_soil") # Set autotranslate for the box object to false b2.autotranslate = False sg.add(b2) @@ -113,39 +110,49 @@ for obj in gssi_objects: sg.add(obj) # Create and add a homogeneous material with a rough surface -soil = gprMax.Material(er=9, se=0.397e-3, mr=1, sm=0, id='soil') +soil = gprMax.Material(er=9, se=0.397e-3, mr=1, sm=0, id="soil") scene.add(soil) -fb = gprMax.FractalBox(p1=(0, 0, 0), p2=(3, 1, 1), frac_dim=1.5, - weighting=(1, 1, 1), n_materials=1, mixing_model_id='soil', id='fbox', seed=1) +fb = gprMax.FractalBox( + p1=(0, 0, 0), + p2=(3, 1, 1), + frac_dim=1.5, + weighting=(1, 1, 1), + n_materials=1, + mixing_model_id="soil", + id="fbox", + seed=1, +) scene.add(fb) -rough_surf = gprMax.AddSurfaceRoughness(p1=(0, 0, 1), p2=(3, 1, 1), - frac_dim=1.5, weighting=(1, 1), - limits=(0.4, 1.2), - fractal_box_id='fbox', seed=1) +rough_surf = gprMax.AddSurfaceRoughness( + p1=(0, 0, 1), p2=(3, 1, 1), frac_dim=1.5, weighting=(1, 1), limits=(0.4, 1.2), fractal_box_id="fbox", seed=1 +) scene.add(rough_surf) # Create some snapshots and geometry views for i in range(1, 51): - snap = gprMax.Snapshot(p1=(0, y / 2, 0), - p2=(x, y / 2 + dl, z), - dl=(dl, dl, dl), - filename=Path(*parts[:-1], f'{parts[-1]}_{str(i)}').name, - time=i * tw / 50,) + snap = gprMax.Snapshot( + p1=(0, y / 2, 0), + p2=(x, y / 2 + dl, z), + dl=(dl, dl, dl), + filename=Path(*parts[:-1], f"{parts[-1]}_{str(i)}").name, + time=i * tw / 50, + ) scene.add(snap) -gvsg = gprMax.GeometryView(p1=(sg_x0, sg_y0, sg_z0), p2=(sg_x1, sg_y1, sg_z1), - dl=(dl_sg, dl_sg, dl_sg), - filename=fn.with_suffix('').parts[-1] + '_sg', - output_type='n') +gvsg = gprMax.GeometryView( + p1=(sg_x0, sg_y0, sg_z0), + p2=(sg_x1, sg_y1, sg_z1), + dl=(dl_sg, dl_sg, dl_sg), + filename=fn.with_suffix("").parts[-1] + "_sg", + output_type="n", +) sg.add(gvsg) -gv1 = gprMax.GeometryView(p1=(0, 0, 0), - p2=domain.props.p1, - dl=dl, - filename=fn.with_suffix('').parts[-1], - output_type='n') +gv1 = gprMax.GeometryView( + p1=(0, 0, 0), p2=domain.props.p1, dl=dl, filename=fn.with_suffix("").parts[-1], output_type="n" +) scene.add(gv1) gprMax.run(scenes=[scene], n=1, geometry_only=True, outputfile=fn, subgrid=True, autotranslate=True) diff --git a/gprMax.toml b/gprMax.toml index 59acdb97..d792e535 100644 --- a/gprMax.toml +++ b/gprMax.toml @@ -1,4 +1,4 @@ [build-system] requires = ['setuptools', 'wheel', 'numpy>=1.19.0', 'Cython>=0.29.21', 'jinja2'] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/gprMax/__init__.py b/gprMax/__init__.py index 55463eeb..7c48639f 100644 --- a/gprMax/__init__.py +++ b/gprMax/__init__.py @@ -22,18 +22,41 @@ from .cmds_geometry.geometry_objects_read import GeometryObjectsRead from .cmds_geometry.plate import Plate from .cmds_geometry.sphere import Sphere from .cmds_geometry.triangle import Triangle -from .cmds_multiuse import (PMLCFS, AddDebyeDispersion, AddDrudeDispersion, - AddLorentzDispersion, GeometryObjectsWrite, - GeometryView, HertzianDipole, MagneticDipole, - Material, MaterialList, MaterialRange, Rx, RxArray, - Snapshot, SoilPeplinski, TransmissionLine, - VoltageSource, Waveform) -from .cmds_singleuse import (Discretisation, Domain, ExcitationFile, - OMPThreads, PMLProps, RxSteps, SrcSteps, - TimeStepStabilityFactor, TimeWindow, Title) +from .cmds_multiuse import ( + PMLCFS, + AddDebyeDispersion, + AddDrudeDispersion, + AddLorentzDispersion, + GeometryObjectsWrite, + GeometryView, + HertzianDipole, + MagneticDipole, + Material, + MaterialList, + MaterialRange, + Rx, + RxArray, + Snapshot, + SoilPeplinski, + TransmissionLine, + VoltageSource, + Waveform, +) +from .cmds_singleuse import ( + Discretisation, + Domain, + ExcitationFile, + OMPThreads, + PMLProps, + RxSteps, + SrcSteps, + TimeStepStabilityFactor, + TimeWindow, + Title, +) from .gprMax import run as run from .hash_cmds_file import user_libs_fn_to_scene_obj from .scene import Scene from .subgrids.user_objects import SubGridHSG -__name__ = 'gprMax' +__name__ = "gprMax" diff --git a/gprMax/__main__.py b/gprMax/__main__.py index 6846a5c6..6b4599eb 100644 --- a/gprMax/__main__.py +++ b/gprMax/__main__.py @@ -2,7 +2,7 @@ import gprMax.gprMax -if __name__ == '__main__': +if __name__ == "__main__": gprMax.gprMax.cli() # Code profiling diff --git a/gprMax/_version.py b/gprMax/_version.py index f2834f5c..a057c94a 100644 --- a/gprMax/_version.py +++ b/gprMax/_version.py @@ -1,4 +1,4 @@ # This is where the version number is set and read by setup.py and conf.py (for the docs) -__version__ = '4.0.0b0' -codename = 'Càrn Mòr' +__version__ = "4.0.0b0" +codename = "Càrn Mòr" diff --git a/gprMax/cmds_geometry/add_grass.py b/gprMax/cmds_geometry/add_grass.py index 43b80f25..8689c9b2 100644 --- a/gprMax/cmds_geometry/add_grass.py +++ b/gprMax/cmds_geometry/add_grass.py @@ -32,23 +32,23 @@ class AddGrass(UserObjectGeometry): """Adds grass with roots to a FractalBox class in the model. Attributes: - p1: list of the lower left (x,y,z) coordinates of a surface on a + p1: list of the lower left (x,y,z) coordinates of a surface on a FractalBox class. - p2: list of the upper right (x,y,z) coordinates of a surface on a + p2: list of the upper right (x,y,z) coordinates of a surface on a FractalBox class. - frac_dim: float for the fractal dimension which, for an orthogonal + frac_dim: float for the fractal dimension which, for an orthogonal parallelepiped, should take values between zero and three. - limits: list to define lower and upper limits for a range over which + limits: list to define lower and upper limits for a range over which the height of the blades of grass can vary. - n_blades:int for the number of blades of grass that should be + n_blades:int for the number of blades of grass that should be applied to the surface area. - fractal_box_id: string identifier for the FractalBox class that the + fractal_box_id: string identifier for the FractalBox class that the grass should be applied to. """ def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#add_grass' + self.hash = "#add_grass" def rotate(self, axis, angle, origin=None): """Set parameters for rotation.""" @@ -59,26 +59,26 @@ class AddGrass(UserObjectGeometry): def _do_rotate(self): """Perform rotation.""" - pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) + pts = np.array([self.kwargs["p1"], self.kwargs["p2"]]) rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin) - self.kwargs['p1'] = tuple(rot_pts[0, :]) - self.kwargs['p2'] = tuple(rot_pts[1, :]) + self.kwargs["p1"] = tuple(rot_pts[0, :]) + self.kwargs["p2"] = tuple(rot_pts[1, :]) def create(self, grid, uip): """Add Grass to fractal box.""" try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - fractal_box_id = self.kwargs['fractal_box_id'] - frac_dim = self.kwargs['frac_dim'] - limits = self.kwargs['limits'] - n_blades = self.kwargs['n_blades'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + fractal_box_id = self.kwargs["fractal_box_id"] + frac_dim = self.kwargs["frac_dim"] + limits = self.kwargs["limits"] + n_blades = self.kwargs["n_blades"] except KeyError: - logger.exception(f'{self.__str__()} requires at least eleven parameters') + logger.exception(f"{self.__str__()} requires at least eleven parameters") raise try: - seed = self.kwargs['seed'] + seed = self.kwargs["seed"] except KeyError: seed = None @@ -90,7 +90,7 @@ class AddGrass(UserObjectGeometry): try: volume = volumes[0] except NameError: - logger.exception(f'{self.__str__()} cannot find FractalBox {fractal_box_id}') + logger.exception(f"{self.__str__()} cannot find FractalBox {fractal_box_id}") raise p1, p2 = uip.check_box_points(p1, p2, self.__str__()) @@ -98,87 +98,96 @@ class AddGrass(UserObjectGeometry): xf, yf, zf = p2 if frac_dim < 0: - logger.exception(f'{self.__str__()} requires a positive value for ' + - 'the fractal dimension') + logger.exception(f"{self.__str__()} requires a positive value for " + "the fractal dimension") raise ValueError if limits[0] < 0 or limits[1] < 0: - logger.exception(f'{self.__str__()} requires a positive value for ' + - 'the minimum and maximum heights for grass blades') + logger.exception( + f"{self.__str__()} requires a positive value for " + "the minimum and maximum heights for grass blades" + ) raise ValueError # Check for valid orientations if xs == xf: if ys == yf or zs == zf: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if xs != volume.xs and xs != volume.xf: - logger.exception(f'{self.__str__()} must specify external surfaces on a fractal box') + logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box") raise ValueError fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx)) # xminus surface if xs == volume.xs: - logger.exception(f'{self.__str__()} grass can only be specified ' + - 'on surfaces in the positive axis direction') + logger.exception( + f"{self.__str__()} grass can only be specified " + "on surfaces in the positive axis direction" + ) raise ValueError # xplus surface elif xf == volume.xf: if fractalrange[1] > grid.nx: - logger.exception(f'{self.__str__()} cannot apply grass to ' + - 'fractal box as it would exceed the domain ' + - 'size in the x direction') + logger.exception( + f"{self.__str__()} cannot apply grass to " + + "fractal box as it would exceed the domain " + + "size in the x direction" + ) raise ValueError - requestedsurface = 'xplus' + requestedsurface = "xplus" elif ys == yf: if xs == xf or zs == zf: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if ys != volume.ys and ys != volume.yf: - logger.exception(f'{self.__str__()} must specify external surfaces on a fractal box') + logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box") raise ValueError fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy)) # yminus surface if ys == volume.ys: - logger.exception(f'{self.__str__()} grass can only be specified ' + - 'on surfaces in the positive axis direction') + logger.exception( + f"{self.__str__()} grass can only be specified " + "on surfaces in the positive axis direction" + ) raise ValueError # yplus surface elif yf == volume.yf: if fractalrange[1] > grid.ny: - logger.exception(f'{self.__str__()} cannot apply grass to ' + - 'fractal box as it would exceed the domain ' + - 'size in the y direction') + logger.exception( + f"{self.__str__()} cannot apply grass to " + + "fractal box as it would exceed the domain " + + "size in the y direction" + ) raise ValueError - requestedsurface = 'yplus' + requestedsurface = "yplus" elif zs == zf: if xs == xf or ys == yf: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if zs != volume.zs and zs != volume.zf: - logger.exception(f'{self.__str__()} must specify external surfaces on a fractal box') + logger.exception(f"{self.__str__()} must specify external surfaces on a fractal box") raise ValueError fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz)) # zminus surface if zs == volume.zs: - logger.exception(f'{self.__str__()} grass can only be specified ' + - 'on surfaces in the positive axis direction') + logger.exception( + f"{self.__str__()} grass can only be specified " + "on surfaces in the positive axis direction" + ) raise ValueError # zplus surface elif zf == volume.zf: if fractalrange[1] > grid.nz: - logger.exception(f'{self.__str__()} cannot apply grass to ' + - 'fractal box as it would exceed the domain ' + - 'size in the z direction') + logger.exception( + f"{self.__str__()} cannot apply grass to " + + "fractal box as it would exceed the domain " + + "size in the z direction" + ) raise ValueError - requestedsurface = 'zplus' + requestedsurface = "zplus" else: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim) - surface.ID = 'grass' + surface.ID = "grass" surface.surfaceID = requestedsurface surface.seed = seed @@ -187,11 +196,13 @@ class AddGrass(UserObjectGeometry): surface.operatingonID = volume.ID surface.generate_fractal_surface() if n_blades > surface.fractalsurface.shape[0] * surface.fractalsurface.shape[1]: - logger.exception(f'{self.__str__()} the specified surface is not large ' + - 'enough for the number of grass blades/roots specified') + logger.exception( + f"{self.__str__()} the specified surface is not large " + + "enough for the number of grass blades/roots specified" + ) raise ValueError - # Scale the distribution so that the summation is equal to one, + # Scale the distribution so that the summation is equal to one, # i.e. a probability distribution surface.fractalsurface = surface.fractalsurface / np.sum(surface.fractalsurface) @@ -203,23 +214,23 @@ class AddGrass(UserObjectGeometry): R = np.random.RandomState(surface.seed) A = R.random_sample(n_blades) - # Locate the random numbers in the bins created by the 1D vector of - # probability values, and convert the 1D index back into a x, y index + # Locate the random numbers in the bins created by the 1D vector of + # probability values, and convert the 1D index back into a x, y index # for the original surface. - bladesindex = np.unravel_index(np.digitize(A, probability1D), - (surface.fractalsurface.shape[0], - surface.fractalsurface.shape[1])) + bladesindex = np.unravel_index( + np.digitize(A, probability1D), (surface.fractalsurface.shape[0], surface.fractalsurface.shape[1]) + ) # Set the fractal range to minimum and maximum heights of the grass blades surface.fractalrange = fractalrange - # Set the fractal surface using the pre-calculated spatial distribution + # Set the fractal surface using the pre-calculated spatial distribution # and a random height - surface.fractalsurface = np.zeros((surface.fractalsurface.shape[0], - surface.fractalsurface.shape[1])) + surface.fractalsurface = np.zeros((surface.fractalsurface.shape[0], surface.fractalsurface.shape[1])) for i in range(len(bladesindex[0])): - surface.fractalsurface[bladesindex[0][i], bladesindex[1][i]] = R.randint(surface.fractalrange[0], - surface.fractalrange[1], size=1) + surface.fractalsurface[bladesindex[0][i], bladesindex[1][i]] = R.randint( + surface.fractalrange[0], surface.fractalrange[1], size=1 + ) # Create grass geometry parameters g = Grass(n_blades) @@ -227,22 +238,26 @@ class AddGrass(UserObjectGeometry): surface.grass.append(g) # Check to see if grass has been already defined as a material - if not any(x.ID == 'grass' for x in grid.materials): + if not any(x.ID == "grass" for x in grid.materials): create_grass(grid) # Check if time step for model is suitable for using grass - grass = next((x for x in grid.materials if x.ID == 'grass')) + grass = next((x for x in grid.materials if x.ID == "grass")) testgrass = next((x for x in grass.tau if x < grid.dt), None) if testgrass: - logger.exception(f'{self.__str__()} requires the time step for the ' + - 'model to be less than the relaxation time required to model grass.') + logger.exception( + f"{self.__str__()} requires the time step for the " + + "model to be less than the relaxation time required to model grass." + ) raise ValueError volume.fractalsurfaces.append(surface) - logger.info(f'{self.grid_name(grid)}{n_blades} blades of grass on surface from ' + - f'{xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, ' + - f'to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m ' + - f'with fractal dimension {surface.dimension:g}, fractal seeding ' + - f'{surface.seed}, and range {limits[0]:g}m to {limits[1]:g}m, ' + - f'added to {surface.operatingonID}.') + logger.info( + f"{self.grid_name(grid)}{n_blades} blades of grass on surface from " + + f"{xs * grid.dx:g}m, {ys * grid.dy:g}m, {zs * grid.dz:g}m, " + + f"to {xf * grid.dx:g}m, {yf * grid.dy:g}m, {zf * grid.dz:g}m " + + f"with fractal dimension {surface.dimension:g}, fractal seeding " + + f"{surface.seed}, and range {limits[0]:g}m to {limits[1]:g}m, " + + f"added to {surface.operatingonID}." + ) diff --git a/gprMax/cmds_geometry/add_surface_roughness.py b/gprMax/cmds_geometry/add_surface_roughness.py index df4b02da..bb6f9e36 100644 --- a/gprMax/cmds_geometry/add_surface_roughness.py +++ b/gprMax/cmds_geometry/add_surface_roughness.py @@ -31,25 +31,25 @@ class AddSurfaceRoughness(UserObjectGeometry): """Adds surface roughness to a FractalBox class in the model. Attributes: - p1: list of the lower left (x,y,z) coordinates of a surface on a + p1: list of the lower left (x,y,z) coordinates of a surface on a FractalBox class. - p2: list of the upper right (x,y,z) coordinates of a surface on a + p2: list of the upper right (x,y,z) coordinates of a surface on a FractalBox class. - frac_dim: float for the fractal dimension which, for an orthogonal + frac_dim: float for the fractal dimension which, for an orthogonal parallelepiped, should take values between zero and three. - weighting: list with weightings in the first and second direction of + weighting: list with weightings in the first and second direction of the surface. - limits: ist to define lower and upper limits for a range over which + limits: ist to define lower and upper limits for a range over which the surface roughness can vary. fractal_box_id: string identifier for the FractalBox class that the surface roughness should be applied to. - seed: (optional) float parameter which controls the seeding of the random + seed: (optional) float parameter which controls the seeding of the random number generator used to create the fractals. """ def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#add_surface_roughness' + self.hash = "#add_surface_roughness" def rotate(self, axis, angle, origin=None): """Set parameters for rotation.""" @@ -60,29 +60,31 @@ class AddSurfaceRoughness(UserObjectGeometry): def _do_rotate(self): """Perform rotation.""" - pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) + pts = np.array([self.kwargs["p1"], self.kwargs["p2"]]) rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin) - self.kwargs['p1'] = tuple(rot_pts[0, :]) - self.kwargs['p2'] = tuple(rot_pts[1, :]) - + self.kwargs["p1"] = tuple(rot_pts[0, :]) + self.kwargs["p2"] = tuple(rot_pts[1, :]) + def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - frac_dim = self.kwargs['frac_dim'] - weighting = np.array(self.kwargs['weighting'], dtype=np.float64) - limits = np.array(self.kwargs['limits']) - fractal_box_id = self.kwargs['fractal_box_id'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + frac_dim = self.kwargs["frac_dim"] + weighting = np.array(self.kwargs["weighting"], dtype=np.float64) + limits = np.array(self.kwargs["limits"]) + fractal_box_id = self.kwargs["fractal_box_id"] except KeyError: - logger.exception(f'{self.__str__()} incorrect parameters') + logger.exception(f"{self.__str__()} incorrect parameters") raise try: - seed = self.kwargs['seed'] + seed = self.kwargs["seed"] except KeyError: - logger.warning(f'{self.__str__()} no value for seed detected. This ' + - 'means you will get a different fractal distribution ' + - 'every time the model runs.') + logger.warning( + f"{self.__str__()} no value for seed detected. This " + + "means you will get a different fractal distribution " + + "every time the model runs." + ) seed = None if self.do_rotate: @@ -93,7 +95,7 @@ class AddSurfaceRoughness(UserObjectGeometry): if volumes: volume = volumes[0] else: - logger.exception(f'{self.__str__()} cannot find FractalBox {fractal_box_id}') + logger.exception(f"{self.__str__()} cannot find FractalBox {fractal_box_id}") raise ValueError p1, p2 = uip.check_box_points(p1, p2, self.__str__()) @@ -101,108 +103,117 @@ class AddSurfaceRoughness(UserObjectGeometry): xf, yf, zf = p2 if frac_dim < 0: - logger.exception(f'{self.__str__()} requires a positive value for the ' + - 'fractal dimension') + logger.exception(f"{self.__str__()} requires a positive value for the " + "fractal dimension") raise ValueError if weighting[0] < 0: - logger.exception(f'{self.__str__()} requires a positive value for the ' + - 'fractal weighting in the first direction of the surface') + logger.exception( + f"{self.__str__()} requires a positive value for the " + + "fractal weighting in the first direction of the surface" + ) raise ValueError if weighting[1] < 0: - logger.exception(f'{self.__str__()} requires a positive value for the ' + - 'fractal weighting in the second direction of the surface') + logger.exception( + f"{self.__str__()} requires a positive value for the " + + "fractal weighting in the second direction of the surface" + ) raise ValueError # Check for valid orientations if xs == xf: if ys == yf or zs == zf: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if xs != volume.xs and xs != volume.xf: - logger.exception(f'{self.__str__()} can only be used on the external ' + - 'surfaces of a fractal box') + logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box") raise ValueError - fractalrange = (round_value(limits[0] / grid.dx), - round_value(limits[1] / grid.dx)) + fractalrange = (round_value(limits[0] / grid.dx), round_value(limits[1] / grid.dx)) # xminus surface if xs == volume.xs: if fractalrange[0] < 0 or fractalrange[1] > volume.xf: - logger.exception(f'{self.__str__()} cannot apply fractal surface ' + - 'to fractal box as it would exceed either the ' + - 'upper coordinates of the fractal box or the ' + - 'domain in the x direction') + logger.exception( + f"{self.__str__()} cannot apply fractal surface " + + "to fractal box as it would exceed either the " + + "upper coordinates of the fractal box or the " + + "domain in the x direction" + ) raise ValueError - requestedsurface = 'xminus' + requestedsurface = "xminus" # xplus surface elif xf == volume.xf: if fractalrange[0] < volume.xs or fractalrange[1] > grid.nx: - logger.exception(f'{self.__str__()} cannot apply fractal surface ' + - 'to fractal box as it would exceed either the ' + - 'lower coordinates of the fractal box or the ' + - 'domain in the x direction') + logger.exception( + f"{self.__str__()} cannot apply fractal surface " + + "to fractal box as it would exceed either the " + + "lower coordinates of the fractal box or the " + + "domain in the x direction" + ) raise ValueError - requestedsurface = 'xplus' + requestedsurface = "xplus" elif ys == yf: if xs == xf or zs == zf: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if ys != volume.ys and ys != volume.yf: - logger.exception(f'{self.__str__()} can only be used on the external ' + - 'surfaces of a fractal box') + logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box") raise ValueError - fractalrange = (round_value(limits[0] / grid.dy), - round_value(limits[1] / grid.dy)) + fractalrange = (round_value(limits[0] / grid.dy), round_value(limits[1] / grid.dy)) # yminus surface if ys == volume.ys: if fractalrange[0] < 0 or fractalrange[1] > volume.yf: - logger.exception(f'{self.__str__()} cannot apply fractal surface ' + - 'to fractal box as it would exceed either the ' + - 'upper coordinates of the fractal box or the ' + - 'domain in the y direction') + logger.exception( + f"{self.__str__()} cannot apply fractal surface " + + "to fractal box as it would exceed either the " + + "upper coordinates of the fractal box or the " + + "domain in the y direction" + ) raise ValueError - requestedsurface = 'yminus' + requestedsurface = "yminus" # yplus surface elif yf == volume.yf: if fractalrange[0] < volume.ys or fractalrange[1] > grid.ny: - logger.exception(f'{self.__str__()} cannot apply fractal surface ' + - 'to fractal box as it would exceed either the ' + - 'lower coordinates of the fractal box or the ' + - 'domain in the y direction') + logger.exception( + f"{self.__str__()} cannot apply fractal surface " + + "to fractal box as it would exceed either the " + + "lower coordinates of the fractal box or the " + + "domain in the y direction" + ) raise ValueError - requestedsurface = 'yplus' + requestedsurface = "yplus" elif zs == zf: if xs == xf or ys == yf: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if zs != volume.zs and zs != volume.zf: - logger.exception(f'{self.__str__()} can only be used on the external ' + - 'surfaces of a fractal box') + logger.exception(f"{self.__str__()} can only be used on the external " + "surfaces of a fractal box") raise ValueError - fractalrange = (round_value(limits[0] / grid.dz), - round_value(limits[1] / grid.dz)) + fractalrange = (round_value(limits[0] / grid.dz), round_value(limits[1] / grid.dz)) # zminus surface if zs == volume.zs: if fractalrange[0] < 0 or fractalrange[1] > volume.zf: - logger.exception(f'{self.__str__()} cannot apply fractal surface ' + - 'to fractal box as it would exceed either the ' + - 'upper coordinates of the fractal box or the ' + - 'domain in the x direction') + logger.exception( + f"{self.__str__()} cannot apply fractal surface " + + "to fractal box as it would exceed either the " + + "upper coordinates of the fractal box or the " + + "domain in the x direction" + ) raise ValueError - requestedsurface = 'zminus' + requestedsurface = "zminus" # zplus surface elif zf == volume.zf: if fractalrange[0] < volume.zs or fractalrange[1] > grid.nz: - logger.exception(f'{self.__str__()} cannot apply fractal surface ' + - 'to fractal box as it would exceed either the ' + - 'lower coordinates of the fractal box or the ' + - 'domain in the z direction') + logger.exception( + f"{self.__str__()} cannot apply fractal surface " + + "to fractal box as it would exceed either the " + + "lower coordinates of the fractal box or the " + + "domain in the z direction" + ) raise ValueError - requestedsurface = 'zplus' + requestedsurface = "zplus" else: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError surface = FractalSurface(xs, xf, ys, yf, zs, zf, frac_dim) @@ -218,17 +229,18 @@ class AddSurfaceRoughness(UserObjectGeometry): # List of existing surfaces IDs existingsurfaceIDs = [x.surfaceID for x in volume.fractalsurfaces] if surface.surfaceID in existingsurfaceIDs: - logger.exception(f'{self.__str__()} has already been used on the ' + - f'{surface.surfaceID} surface') + logger.exception(f"{self.__str__()} has already been used on the " + f"{surface.surfaceID} surface") raise ValueError surface.generate_fractal_surface() volume.fractalsurfaces.append(surface) - logger.info(f'{self.grid_name(grid)}Fractal surface from {xs * grid.dx:g}m, ' + - f'{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, ' + - f'{yf * grid.dy:g}m, {zf * grid.dz:g}m with fractal dimension ' + - f'{surface.dimension:g}, fractal weightings {surface.weighting[0]:g}, ' + - f'{surface.weighting[1]:g}, fractal seeding {surface.seed}, ' + - f'and range {limits[0]:g}m to {limits[1]:g}m, added to ' + - f'{surface.operatingonID}.') \ No newline at end of file + logger.info( + f"{self.grid_name(grid)}Fractal surface from {xs * grid.dx:g}m, " + + f"{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, " + + f"{yf * grid.dy:g}m, {zf * grid.dz:g}m with fractal dimension " + + f"{surface.dimension:g}, fractal weightings {surface.weighting[0]:g}, " + + f"{surface.weighting[1]:g}, fractal seeding {surface.seed}, " + + f"and range {limits[0]:g}m to {limits[1]:g}m, added to " + + f"{surface.operatingonID}." + ) diff --git a/gprMax/cmds_geometry/add_surface_water.py b/gprMax/cmds_geometry/add_surface_water.py index 00d33a48..9b0c4cd9 100644 --- a/gprMax/cmds_geometry/add_surface_water.py +++ b/gprMax/cmds_geometry/add_surface_water.py @@ -31,20 +31,20 @@ class AddSurfaceWater(UserObjectGeometry): """Adds surface water to a FractalBox class in the model. Attributes: - p1: list of the lower left (x,y,z) coordinates of a surface on a + p1: list of the lower left (x,y,z) coordinates of a surface on a FractalBox class. - p2: list of the upper right (x,y,z) coordinates of a surface on a + p2: list of the upper right (x,y,z) coordinates of a surface on a FractalBox class. - depth: float that defines the depth of the water, which should be - specified relative to the dimensions of the #fractal_box that + depth: float that defines the depth of the water, which should be + specified relative to the dimensions of the #fractal_box that the surface water is being applied. - fractal_box_id: string identifier for the FractalBox class that the + fractal_box_id: string identifier for the FractalBox class that the surface water should be applied to. """ def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#add_surface_water' + self.hash = "#add_surface_water" def rotate(self, axis, angle, origin=None): """Set parameters for rotation.""" @@ -55,20 +55,20 @@ class AddSurfaceWater(UserObjectGeometry): def _do_rotate(self): """Perform rotation.""" - pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) + pts = np.array([self.kwargs["p1"], self.kwargs["p2"]]) rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin) - self.kwargs['p1'] = tuple(rot_pts[0, :]) - self.kwargs['p2'] = tuple(rot_pts[1, :]) - + self.kwargs["p1"] = tuple(rot_pts[0, :]) + self.kwargs["p2"] = tuple(rot_pts[1, :]) + def create(self, grid, uip): - """"Create surface water on fractal box.""" + """ "Create surface water on fractal box.""" try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - fractal_box_id = self.kwargs['fractal_box_id'] - depth = self.kwargs['depth'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + fractal_box_id = self.kwargs["fractal_box_id"] + depth = self.kwargs["depth"] except KeyError: - logger.exception(f'{self.__str__()} requires exactly eight parameters') + logger.exception(f"{self.__str__()} requires exactly eight parameters") raise if self.do_rotate: @@ -77,7 +77,7 @@ class AddSurfaceWater(UserObjectGeometry): if volumes := [volume for volume in grid.fractalvolumes if volume.ID == fractal_box_id]: volume = volumes[0] else: - logger.exception(f'{self.__str__()} cannot find FractalBox {fractal_box_id}') + logger.exception(f"{self.__str__()} cannot find FractalBox {fractal_box_id}") raise ValueError p1, p2 = uip.check_box_points(p1, p2, self.__str__()) @@ -85,89 +85,92 @@ class AddSurfaceWater(UserObjectGeometry): xf, yf, zf = p2 if depth <= 0: - logger.exception(f'{self.__str__()} requires a positive value for the ' + - f'depth of water') + logger.exception(f"{self.__str__()} requires a positive value for the " + f"depth of water") raise ValueError # Check for valid orientations if xs == xf: if ys == yf or zs == zf: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if xs not in [volume.xs, volume.xf]: - logger.exception(f'{self.__str__()} can only be used on the external surfaces ' - f'of a fractal box') + logger.exception(f"{self.__str__()} can only be used on the external surfaces " f"of a fractal box") raise ValueError # xminus surface if xs == volume.xs: - requestedsurface = 'xminus' + requestedsurface = "xminus" # xplus surface elif xf == volume.xf: - requestedsurface = 'xplus' + requestedsurface = "xplus" filldepthcells = round_value(depth / grid.dx) filldepth = filldepthcells * grid.dx elif ys == yf: if zs == zf: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError if ys not in [volume.ys, volume.yf]: - logger.exception(f'{self.__str__()} can only be used on the external surfaces ' + - f'of a fractal box') + logger.exception(f"{self.__str__()} can only be used on the external surfaces " + f"of a fractal box") raise ValueError # yminus surface if ys == volume.ys: - requestedsurface = 'yminus' + requestedsurface = "yminus" # yplus surface elif yf == volume.yf: - requestedsurface = 'yplus' + requestedsurface = "yplus" filldepthcells = round_value(depth / grid.dy) filldepth = filldepthcells * grid.dy elif zs == zf: if zs not in [volume.zs, volume.zf]: - logger.exception(f'{self.__str__()} can only be used on the external surfaces ' - f'of a fractal box') + logger.exception(f"{self.__str__()} can only be used on the external surfaces " f"of a fractal box") raise ValueError # zminus surface if zs == volume.zs: - requestedsurface = 'zminus' + requestedsurface = "zminus" # zplus surface elif zf == volume.zf: - requestedsurface = 'zplus' + requestedsurface = "zplus" filldepthcells = round_value(depth / grid.dz) filldepth = filldepthcells * grid.dz else: - logger.exception(f'{self.__str__()} dimensions are not specified correctly') + logger.exception(f"{self.__str__()} dimensions are not specified correctly") raise ValueError surface = next((x for x in volume.fractalsurfaces if x.surfaceID == requestedsurface), None) if not surface: - logger.exception(f'{self.__str__()} specified surface {requestedsurface} ' + - f'does not have a rough surface applied') + logger.exception( + f"{self.__str__()} specified surface {requestedsurface} " + f"does not have a rough surface applied" + ) raise ValueError surface.filldepth = filldepthcells # Check that requested fill depth falls within range of surface roughness if surface.filldepth < surface.fractalrange[0] or surface.filldepth > surface.fractalrange[1]: - logger.exception(f'{self.__str__()} requires a value for the depth of water that lies with the ' + - f'range of the requested surface roughness') + logger.exception( + f"{self.__str__()} requires a value for the depth of water that lies with the " + + f"range of the requested surface roughness" + ) raise ValueError # Check to see if water has been already defined as a material - if all(x.ID != 'water' for x in grid.materials): + if all(x.ID != "water" for x in grid.materials): create_water(grid) # Check if time step for model is suitable for using water - water = next((x for x in grid.materials if x.ID == 'water')) + water = next((x for x in grid.materials if x.ID == "water")) if testwater := next((x for x in water.tau if x < grid.dt), None): - logger.exception(f'{self.__str__()} requires the time step for the model ' - f'to be less than the relaxation time required to model water.') + logger.exception( + f"{self.__str__()} requires the time step for the model " + f"to be less than the relaxation time required to model water." + ) raise ValueError - logger.info(f'{self.grid_name(grid)}Water on surface from {xs * grid.dx:g}m, ' + - f'{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, ' + - f'{yf * grid.dy:g}m, {zf * grid.dz:g}m with depth {filldepth:g}m, ' + - f'added to {surface.operatingonID}.') + logger.info( + f"{self.grid_name(grid)}Water on surface from {xs * grid.dx:g}m, " + + f"{ys * grid.dy:g}m, {zs * grid.dz:g}m, to {xf * grid.dx:g}m, " + + f"{yf * grid.dy:g}m, {zf * grid.dz:g}m with depth {filldepth:g}m, " + + f"added to {surface.operatingonID}." + ) diff --git a/gprMax/cmds_geometry/box.py b/gprMax/cmds_geometry/box.py index de07b862..41b3cfcd 100644 --- a/gprMax/cmds_geometry/box.py +++ b/gprMax/cmds_geometry/box.py @@ -30,13 +30,13 @@ logger = logging.getLogger(__name__) class Box(UserObjectGeometry): - """Introduces an orthogonal parallelepiped with specific properties into + """Introduces an orthogonal parallelepiped with specific properties into the model. Attributes: p1: list of the lower left (x,y,z) coordinates of the parallelepiped. p2: list of the upper right (x,y,z) coordinates of the parallelepiped. - material_id: string for the material identifier that must correspond + material_id: string for the material identifier that must correspond to material that has already been defined. material_ids: list of material identifiers in the x, y, z directions. averaging: string (y or n) used to switch on and off dielectric smoothing. @@ -44,7 +44,7 @@ class Box(UserObjectGeometry): def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#box' + self.hash = "#box" def rotate(self, axis, angle, origin=None): """Set parameters for rotation.""" @@ -55,17 +55,17 @@ class Box(UserObjectGeometry): def _do_rotate(self): """Perform rotation.""" - pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) + pts = np.array([self.kwargs["p1"], self.kwargs["p2"]]) rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin) - self.kwargs['p1'] = tuple(rot_pts[0, :]) - self.kwargs['p2'] = tuple(rot_pts[1, :]) + self.kwargs["p1"] = tuple(rot_pts[0, :]) + self.kwargs["p2"] = tuple(rot_pts[1, :]) def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] except KeyError: - logger.exception(f'{self.__str__()} Please specify two points.') + logger.exception(f"{self.__str__()} Please specify two points.") raise if self.do_rotate: @@ -74,19 +74,19 @@ class Box(UserObjectGeometry): # Check materials have been specified # Isotropic case try: - materialsrequested = [self.kwargs['material_id']] + materialsrequested = [self.kwargs["material_id"]] except KeyError: # Anisotropic case try: - materialsrequested = self.kwargs['material_ids'] + materialsrequested = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.__str__()} No materials have been specified') + logger.exception(f"{self.__str__()} No materials have been specified") raise # Check averaging try: # Try user-specified averaging - averagebox = self.kwargs['averaging'] + averagebox = self.kwargs["averaging"] except KeyError: # Otherwise go with the grid default averagebox = grid.averagevolumeobjects @@ -103,7 +103,7 @@ class Box(UserObjectGeometry): if len(materials) != len(materialsrequested): notfound = [x for x in materialsrequested if x not in materials] - logger.exception(f'{self.__str__()} material(s) {notfound} do not exist') + logger.exception(f"{self.__str__()} material(s) {notfound} do not exist") raise ValueError # Isotropic case @@ -117,34 +117,47 @@ class Box(UserObjectGeometry): numIDx = materials[0].numID numIDy = materials[1].numID numIDz = materials[2].numID - requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID + requiredID = materials[0].ID + "+" + materials[1].ID + "+" + materials[2].ID averagedmaterial = [x for x in grid.materials if x.ID == requiredID] if averagedmaterial: numID = averagedmaterial.numID else: numID = len(grid.materials) m = Material(numID, requiredID) - m.type = 'dielectric-smoothed' + m.type = "dielectric-smoothed" # Create dielectric-smoothed constituents for material - m.er = np.mean((materials[0].er, materials[1].er, - materials[2].er), axis=0) - m.se = np.mean((materials[0].se, materials[1].se, - materials[2].se), axis=0) - m.mr = np.mean((materials[0].mr, materials[1].mr, - materials[2].mr), axis=0) - m.sm = np.mean((materials[0].sm, materials[1].sm, - materials[2].sm), axis=0) + m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) + m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0) + m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0) + m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0) # Append the new material object to the materials list grid.materials.append(m) - build_box(xs, xf, ys, yf, zs, zf, config.get_model_config().ompthreads, - numID, numIDx, numIDy, numIDz, averaging, grid.solid, - grid.rigidE, grid.rigidH, grid.ID) + build_box( + xs, + xf, + ys, + yf, + zs, + zf, + config.get_model_config().ompthreads, + numID, + numIDx, + numIDy, + numIDz, + averaging, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) - dielectricsmoothing = 'on' if averaging else 'off' + dielectricsmoothing = "on" if averaging else "off" - logger.info(f"{self.grid_name(grid)}Box from {p5[0]:g}m, {p5[1]:g}m, " + - f"{p5[2]:g}m, to {p6[0]:g}m, {p6[1]:g}m, {p6[2]:g}m of " + - f"material(s) {', '.join(materialsrequested)} created, " + - f"dielectric smoothing is {dielectricsmoothing}.") + logger.info( + f"{self.grid_name(grid)}Box from {p5[0]:g}m, {p5[1]:g}m, " + + f"{p5[2]:g}m, to {p6[0]:g}m, {p6[1]:g}m, {p6[2]:g}m of " + + f"material(s) {', '.join(materialsrequested)} created, " + + f"dielectric smoothing is {dielectricsmoothing}." + ) diff --git a/gprMax/cmds_geometry/build_templates.py b/gprMax/cmds_geometry/build_templates.py index 7f0cbd2b..211511cd 100644 --- a/gprMax/cmds_geometry/build_templates.py +++ b/gprMax/cmds_geometry/build_templates.py @@ -19,52 +19,53 @@ from jinja2 import Environment, PackageLoader env = Environment( - loader=PackageLoader(__name__, 'templates'), + loader=PackageLoader(__name__, "templates"), ) -template = env.get_template('fields_updates_dispersive_template') +template = env.get_template("fields_updates_dispersive_template") r = template.render( functions=[ # name, double, real { - 'name_a': 'update_electric_dispersive_multipole_A_double_real', - 'name_b': 'update_electric_dispersive_multipole_B_double_real', - 'name_a_1': 'update_electric_dispersive_1pole_A_double_real', - 'name_b_1': 'update_electric_dispersive_1pole_B_double_real', - 'field_type': 'double', - 'dispersive_type': 'double' + "name_a": "update_electric_dispersive_multipole_A_double_real", + "name_b": "update_electric_dispersive_multipole_B_double_real", + "name_a_1": "update_electric_dispersive_1pole_A_double_real", + "name_b_1": "update_electric_dispersive_1pole_B_double_real", + "field_type": "double", + "dispersive_type": "double", }, # name, float, real { - 'name_a': 'update_electric_dispersive_multipole_A_float_real', - 'name_b': 'update_electric_dispersive_multipole_B_float_real', - 'name_a_1': 'update_electric_dispersive_1pole_A_float_real', - 'name_b_1': 'update_electric_dispersive_1pole_B_float_real', - 'field_type': 'float', - 'dispersive_type': 'float' + "name_a": "update_electric_dispersive_multipole_A_float_real", + "name_b": "update_electric_dispersive_multipole_B_float_real", + "name_a_1": "update_electric_dispersive_1pole_A_float_real", + "name_b_1": "update_electric_dispersive_1pole_B_float_real", + "field_type": "float", + "dispersive_type": "float", }, # name, double, complex { - 'name_a': 'update_electric_dispersive_multipole_A_double_complex', - 'name_b': 'update_electric_dispersive_multipole_B_double_complex', - 'name_a_1': 'update_electric_dispersive_1pole_A_double_complex', - 'name_b_1': 'update_electric_dispersive_1pole_B_double_complex', - 'field_type': 'double', - 'dispersive_type': 'double complex', - 'real_part': 'creal' + "name_a": "update_electric_dispersive_multipole_A_double_complex", + "name_b": "update_electric_dispersive_multipole_B_double_complex", + "name_a_1": "update_electric_dispersive_1pole_A_double_complex", + "name_b_1": "update_electric_dispersive_1pole_B_double_complex", + "field_type": "double", + "dispersive_type": "double complex", + "real_part": "creal", }, # name, float, complex { - 'name_a': 'update_electric_dispersive_multipole_A_float_complex', - 'name_b': 'update_electric_dispersive_multipole_B_float_complex', - 'name_a_1': 'update_electric_dispersive_1pole_A_float_complex', - 'name_b_1': 'update_electric_dispersive_1pole_B_float_complex', - 'field_type': 'float', - 'dispersive_type': 'float complex', - 'real_part': 'crealf' - }] + "name_a": "update_electric_dispersive_multipole_A_float_complex", + "name_b": "update_electric_dispersive_multipole_B_float_complex", + "name_a_1": "update_electric_dispersive_1pole_A_float_complex", + "name_b_1": "update_electric_dispersive_1pole_B_float_complex", + "field_type": "float", + "dispersive_type": "float complex", + "real_part": "crealf", + }, + ] ) -with open('cython/dispersive_updates_test.pyx', 'w') as f: +with open("cython/dispersive_updates_test.pyx", "w") as f: f.write(r) diff --git a/gprMax/cmds_geometry/cmds_geometry.py b/gprMax/cmds_geometry/cmds_geometry.py index 63980390..918f25a3 100644 --- a/gprMax/cmds_geometry/cmds_geometry.py +++ b/gprMax/cmds_geometry/cmds_geometry.py @@ -30,19 +30,19 @@ class UserObjectGeometry: def __init__(self, **kwargs): self.kwargs = kwargs - self.hash = '#example' + self.hash = "#example" self.autotranslate = True self.do_rotate = False def __str__(self): """Readable string of parameters given to object.""" - s = '' + s = "" for _, v in self.kwargs.items(): if isinstance(v, (tuple, list)): - v = ' '.join([str(el) for el in v]) - s += f'{str(v)} ' + v = " ".join([str(el) for el in v]) + s += f"{str(v)} " - return f'{self.hash}: {s[:-1]}' + return f"{self.hash}: {s[:-1]}" def create(self, grid, uip): """Creates object and adds it to the grid.""" @@ -54,18 +54,18 @@ class UserObjectGeometry: def grid_name(self, grid): """Returns subgrid name for use with logging info. Returns an empty - string if the grid is the main grid. + string if the grid is the main grid. """ - if config.sim_config.general['subgrid'] and grid.name != 'main_grid': - return f'[{grid.name}] ' + if config.sim_config.general["subgrid"] and grid.name != "main_grid": + return f"[{grid.name}] " else: - return '' + return "" def rotate_point(p, axis, angle, origin=(0, 0, 0)): """Rotates a point. - + Args: p: array of coordinates of point (x, y, z). axis: string which defines the axis about which to perform rotation (x, y, or z). @@ -91,11 +91,11 @@ def rotate_point(p, axis, angle, origin=(0, 0, 0)): p += origin return p - + def rotate_2point_object(pts, axis, angle, origin=None): """Rotate a geometry object that is defined by 2 points. - + Args: pts: array ofcoordinates of points of object to be rotated. axis: string which defines the axis about which to perform rotation (x, y, or z). @@ -105,23 +105,23 @@ def rotate_2point_object(pts, axis, angle, origin=None): Returns: new_pts: array of coordinates of points of rotated object. """ - + # Use origin at centre of object if not given if not origin: - origin = pts[0,:] + (pts[1,:] - pts[0,:]) / 2 + origin = pts[0, :] + (pts[1, :] - pts[0, :]) / 2 # Check angle value is suitable angle = int(angle) if angle < 0 or angle > 360: - logger.exception('Angle of rotation must be between 0-360 degrees') + logger.exception("Angle of rotation must be between 0-360 degrees") raise ValueError if angle % 90 != 0: - logger.exception('Angle of rotation must be a multiple of 90 degrees') + logger.exception("Angle of rotation must be a multiple of 90 degrees") raise ValueError # Check axis is valid - if axis not in ['x', 'y', 'z']: - logger.exception('Axis of rotation must be x, y, or z') + if axis not in ["x", "y", "z"]: + logger.exception("Axis of rotation must be x, y, or z") raise ValueError # Save original points @@ -136,15 +136,15 @@ def rotate_2point_object(pts, axis, angle, origin=None): new_pts[0, :] = np.min(pts, axis=0) new_pts[1, :] = np.max(pts, axis=0) - # Reset coordinates of invariant direction + # Reset coordinates of invariant direction # - only needed for 2D models, has no effect on 3D models. - if axis =='x': + if axis == "x": new_pts[0, 0] = orig_pts[0, 0] new_pts[1, 0] = orig_pts[1, 0] - elif axis == 'y': + elif axis == "y": new_pts[0, 1] = orig_pts[0, 1] new_pts[1, 1] = orig_pts[1, 1] - elif axis == 'z': + elif axis == "z": new_pts[0, 2] = orig_pts[0, 2] new_pts[1, 2] = orig_pts[1, 2] @@ -153,7 +153,7 @@ def rotate_2point_object(pts, axis, angle, origin=None): def rotate_polarisation(p, polarisation, axis, angle, G): """Rotates a geometry object that is defined by a point and polarisation. - + Args: p: array of coordinates of point (x, y, z). polarisation: string defining the current polarisation (x, y, or z). @@ -166,27 +166,27 @@ def rotate_polarisation(p, polarisation, axis, angle, G): new_polarisation: string defining the new polarisation (x, y, or z). """ - if polarisation.lower() == 'x': + if polarisation.lower() == "x": new_pt = (p[0] + G.dx, p[1], p[2]) - if axis == 'y' and angle == 90 or angle == 270: - new_polarisation = 'z' - if axis == 'z' and angle == 90 or angle == 270: - new_polarisation = 'y' + if axis == "y" and angle == 90 or angle == 270: + new_polarisation = "z" + if axis == "z" and angle == 90 or angle == 270: + new_polarisation = "y" - elif polarisation.lower() == 'y': + elif polarisation.lower() == "y": new_pt = (p[0], p[1] + G.dy, p[2]) - if axis == 'x' and angle == 90 or angle == 270: - new_polarisation = 'z' - if axis == 'z' and angle == 90 or angle == 270: - new_polarisation = 'x' + if axis == "x" and angle == 90 or angle == 270: + new_polarisation = "z" + if axis == "z" and angle == 90 or angle == 270: + new_polarisation = "x" - elif polarisation.lower() == 'z': + elif polarisation.lower() == "z": new_pt = (p[0], p[1], p[2] + G.dz) - if axis == 'x' and angle == 90 or angle == 270: - new_polarisation = 'y' - if axis == 'y' and angle == 90 or angle == 270: - new_polarisation = 'x' + if axis == "x" and angle == 90 or angle == 270: + new_polarisation = "y" + if axis == "y" and angle == 90 or angle == 270: + new_polarisation = "x" pts = np.array([p, new_pt]) - return pts, new_polarisation \ No newline at end of file + return pts, new_polarisation diff --git a/gprMax/cmds_geometry/cone.py b/gprMax/cmds_geometry/cone.py index 96cbc7ef..e81040c9 100644 --- a/gprMax/cmds_geometry/cone.py +++ b/gprMax/cmds_geometry/cone.py @@ -32,13 +32,13 @@ class Cone(UserObjectGeometry): can have different radii and one of them can be zero. Attributes: - p1: list of the coordinates (x,y,z) of the centre of the first face + p1: list of the coordinates (x,y,z) of the centre of the first face of the cone. - p2: list of the coordinates (x,y,z) of the centre of the second face + p2: list of the coordinates (x,y,z) of the centre of the second face of the cone. r1: float of the radius of the first face of the cone. r2: float of the radius of the second face of the cone. - material_id: string for the material identifier that must correspond + material_id: string for the material identifier that must correspond to material that has already been defined. material_ids: list of material identifiers in the x, y, z directions. averaging: string (y or n) used to switch on and off dielectric smoothing. @@ -46,22 +46,22 @@ class Cone(UserObjectGeometry): def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#cone' + self.hash = "#cone" def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - r1 = self.kwargs['r1'] - r2 = self.kwargs['r2'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + r1 = self.kwargs["r1"] + r2 = self.kwargs["r2"] except KeyError: - logger.exception(f'{self.__str__()} please specify two points and two radii') + logger.exception(f"{self.__str__()} please specify two points and two radii") raise # Check averaging try: # Try user-specified averaging - averagecylinder = self.kwargs['averaging'] + averagecylinder = self.kwargs["averaging"] except KeyError: # Otherwise go with the grid default averagecylinder = grid.averagevolumeobjects @@ -69,13 +69,13 @@ class Cone(UserObjectGeometry): # Check materials have been specified # Isotropic case try: - materialsrequested = [self.kwargs['material_id']] + materialsrequested = [self.kwargs["material_id"]] except KeyError: # Anisotropic case try: - materialsrequested = self.kwargs['material_ids'] + materialsrequested = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.__str__()} no materials have been specified') + logger.exception(f"{self.__str__()} no materials have been specified") raise p3 = uip.round_to_grid_static_point(p1) @@ -85,17 +85,15 @@ class Cone(UserObjectGeometry): x2, y2, z2 = uip.round_to_grid(p2) if r1 < 0: - logger.exception(f'{self.__str__()} the radius of the first face ' + - f'{r1:g} should be a positive value.') + logger.exception(f"{self.__str__()} the radius of the first face " + f"{r1:g} should be a positive value.") raise ValueError - + if r2 < 0: - logger.exception(f'{self.__str__()} the radius of the second face ' + - f'{r2:g} should be a positive value.') + logger.exception(f"{self.__str__()} the radius of the second face " + f"{r2:g} should be a positive value.") raise ValueError - + if r1 == 0 and r2 == 0: - logger.exception(f'{self.__str__()} both radii cannot be zero.') + logger.exception(f"{self.__str__()} both radii cannot be zero.") raise ValueError # Look up requested materials in existing list of material instances @@ -103,7 +101,7 @@ class Cone(UserObjectGeometry): if len(materials) != len(materialsrequested): notfound = [x for x in materialsrequested if x not in materials] - logger.exception(f'{self.__str__()} material(s) {notfound} do not exist') + logger.exception(f"{self.__str__()} material(s) {notfound} do not exist") raise ValueError # Isotropic case @@ -117,33 +115,50 @@ class Cone(UserObjectGeometry): numIDx = materials[0].numID numIDy = materials[1].numID numIDz = materials[2].numID - requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID + requiredID = materials[0].ID + "+" + materials[1].ID + "+" + materials[2].ID averagedmaterial = [x for x in grid.materials if x.ID == requiredID] if averagedmaterial: numID = averagedmaterial.numID else: numID = len(grid.materials) m = Material(numID, requiredID) - m.type = 'dielectric-smoothed' + m.type = "dielectric-smoothed" # Create dielectric-smoothed constituents for material - m.er = np.mean((materials[0].er, materials[1].er, - materials[2].er), axis=0) - m.se = np.mean((materials[0].se, materials[1].se, - materials[2].se), axis=0) - m.mr = np.mean((materials[0].mr, materials[1].mr, - materials[2].mr), axis=0) - m.sm = np.mean((materials[0].sm, materials[1].sm, - materials[2].sm), axis=0) + m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) + m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0) + m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0) + m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0) # Append the new material object to the materials list grid.materials.append(m) - build_cone(x1, y1, z1, x2, y2, z2, r1, r2, grid.dx, grid.dy, grid.dz, - numID, numIDx, numIDy, numIDz, averaging, grid.solid, - grid.rigidE, grid.rigidH, grid.ID) + build_cone( + x1, + y1, + z1, + x2, + y2, + z2, + r1, + r2, + grid.dx, + grid.dy, + grid.dz, + numID, + numIDx, + numIDy, + numIDz, + averaging, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) - dielectricsmoothing = 'on' if averaging else 'off' - logger.info(f"{self.grid_name(grid)}Cone with face centres {p3[0]:g}m, " + - f"{p3[1]:g}m, {p3[2]:g}m and {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m, " + - f"with radii {r1:g}m and {r2:g}, of material(s) {', '.join(materialsrequested)} " + - f"created, dielectric smoothing is {dielectricsmoothing}.") + dielectricsmoothing = "on" if averaging else "off" + logger.info( + f"{self.grid_name(grid)}Cone with face centres {p3[0]:g}m, " + + f"{p3[1]:g}m, {p3[2]:g}m and {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m, " + + f"with radii {r1:g}m and {r2:g}, of material(s) {', '.join(materialsrequested)} " + + f"created, dielectric smoothing is {dielectricsmoothing}." + ) diff --git a/gprMax/cmds_geometry/cylinder.py b/gprMax/cmds_geometry/cylinder.py index 006b46a4..aac7b143 100644 --- a/gprMax/cmds_geometry/cylinder.py +++ b/gprMax/cmds_geometry/cylinder.py @@ -31,12 +31,12 @@ class Cylinder(UserObjectGeometry): """Introduces a circular cylinder into the model. Attributes: - p1: list of the coordinates (x,y,z) of the centre of the first face + p1: list of the coordinates (x,y,z) of the centre of the first face of the cylinder. - p2: list of the coordinates (x,y,z) of the centre of the second face + p2: list of the coordinates (x,y,z) of the centre of the second face of the cylinder. r: float of the radius of the cylinder. - material_id: string for the material identifier that must correspond + material_id: string for the material identifier that must correspond to material that has already been defined. material_ids: list of material identifiers in the x, y, z directions. averaging: string (y or n) used to switch on and off dielectric smoothing. @@ -44,21 +44,21 @@ class Cylinder(UserObjectGeometry): def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#cylinder' + self.hash = "#cylinder" def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - r = self.kwargs['r'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + r = self.kwargs["r"] except KeyError: - logger.exception(f'{self.__str__()} please specify 2 points and a radius') + logger.exception(f"{self.__str__()} please specify 2 points and a radius") raise # Check averaging try: # Try user-specified averaging - averagecylinder = self.kwargs['averaging'] + averagecylinder = self.kwargs["averaging"] except KeyError: # Otherwise go with the grid default averagecylinder = grid.averagevolumeobjects @@ -66,13 +66,13 @@ class Cylinder(UserObjectGeometry): # Check materials have been specified # Isotropic case try: - materialsrequested = [self.kwargs['material_id']] + materialsrequested = [self.kwargs["material_id"]] except KeyError: # Anisotropic case try: - materialsrequested = self.kwargs['material_ids'] + materialsrequested = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.__str__()} no materials have been specified') + logger.exception(f"{self.__str__()} no materials have been specified") raise p3 = uip.round_to_grid_static_point(p1) @@ -82,7 +82,7 @@ class Cylinder(UserObjectGeometry): x2, y2, z2 = uip.round_to_grid(p2) if r <= 0: - logger.exception(f'{self.__str__()} the radius {r:g} should be a positive value.') + logger.exception(f"{self.__str__()} the radius {r:g} should be a positive value.") raise ValueError # Look up requested materials in existing list of material instances @@ -90,7 +90,7 @@ class Cylinder(UserObjectGeometry): if len(materials) != len(materialsrequested): notfound = [x for x in materialsrequested if x not in materials] - logger.exception(f'{self.__str__()} material(s) {notfound} do not exist') + logger.exception(f"{self.__str__()} material(s) {notfound} do not exist") raise ValueError # Isotropic case @@ -104,33 +104,49 @@ class Cylinder(UserObjectGeometry): numIDx = materials[0].numID numIDy = materials[1].numID numIDz = materials[2].numID - requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID + requiredID = materials[0].ID + "+" + materials[1].ID + "+" + materials[2].ID averagedmaterial = [x for x in grid.materials if x.ID == requiredID] if averagedmaterial: numID = averagedmaterial.numID else: numID = len(grid.materials) m = Material(numID, requiredID) - m.type = 'dielectric-smoothed' + m.type = "dielectric-smoothed" # Create dielectric-smoothed constituents for material - m.er = np.mean((materials[0].er, materials[1].er, - materials[2].er), axis=0) - m.se = np.mean((materials[0].se, materials[1].se, - materials[2].se), axis=0) - m.mr = np.mean((materials[0].mr, materials[1].mr, - materials[2].mr), axis=0) - m.sm = np.mean((materials[0].sm, materials[1].sm, - materials[2].sm), axis=0) + m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) + m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0) + m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0) + m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0) # Append the new material object to the materials list grid.materials.append(m) - build_cylinder(x1, y1, z1, x2, y2, z2, r, grid.dx, grid.dy, grid.dz, - numID, numIDx, numIDy, numIDz, averaging, grid.solid, - grid.rigidE, grid.rigidH, grid.ID) + build_cylinder( + x1, + y1, + z1, + x2, + y2, + z2, + r, + grid.dx, + grid.dy, + grid.dz, + numID, + numIDx, + numIDy, + numIDz, + averaging, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) - dielectricsmoothing = 'on' if averaging else 'off' - logger.info(f"{self.grid_name(grid)}Cylinder with face centres {p3[0]:g}m, " + - f"{p3[1]:g}m, {p3[2]:g}m and {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m, " + - f"with radius {r:g}m, of material(s) {', '.join(materialsrequested)} " + - f"created, dielectric smoothing is {dielectricsmoothing}.") + dielectricsmoothing = "on" if averaging else "off" + logger.info( + f"{self.grid_name(grid)}Cylinder with face centres {p3[0]:g}m, " + + f"{p3[1]:g}m, {p3[2]:g}m and {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m, " + + f"with radius {r:g}m, of material(s) {', '.join(materialsrequested)} " + + f"created, dielectric smoothing is {dielectricsmoothing}." + ) diff --git a/gprMax/cmds_geometry/cylindrical_sector.py b/gprMax/cmds_geometry/cylindrical_sector.py index 8fa52c6c..1f9d4dde 100644 --- a/gprMax/cmds_geometry/cylindrical_sector.py +++ b/gprMax/cmds_geometry/cylindrical_sector.py @@ -31,21 +31,21 @@ class CylindricalSector(UserObjectGeometry): """Introduces a cylindrical sector (shaped like a slice of pie) into the model. Attributes: - normal: string for the direction of the axis of the cylinder from which + normal: string for the direction of the axis of the cylinder from which the sector is defined and can be x, y, or z. - ctr1: float for the first coordinate of the centre of the cylindrical + ctr1: float for the first coordinate of the centre of the cylindrical sector. - ctr2: float for the second coordinate of the centre of the cylindrical + ctr2: float for the second coordinate of the centre of the cylindrical sector. - extent1: float for the first thickness from the centre of the + extent1: float for the first thickness from the centre of the cylindrical sector. - extent2: float for the second thickness from the centre of the + extent2: float for the second thickness from the centre of the cylindrical sector. r: float for the radius of the cylindrical sector. - start: float for the starting angle (in degrees) for the cylindrical + start: float for the starting angle (in degrees) for the cylindrical sector. end: float for the angle (in degrees) swept by the cylindrical sector. - material_id: string for the material identifier that must correspond + material_id: string for the material identifier that must correspond to material that has already been defined. material_ids: list of material identifiers in the x, y, z directions. averaging: string (y or n) used to switch on and off dielectric smoothing. @@ -53,19 +53,18 @@ class CylindricalSector(UserObjectGeometry): def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#cylindrical_sector' + self.hash = "#cylindrical_sector" def create(self, grid, uip): - try: - normal = self.kwargs['normal'].lower() - ctr1 = self.kwargs['ctr1'] - ctr2 = self.kwargs['ctr2'] - extent1 = self.kwargs['extent1'] - extent2 = self.kwargs['extent2'] - start = self.kwargs['start'] - end = self.kwargs['end'] - r = self.kwargs['r'] + normal = self.kwargs["normal"].lower() + ctr1 = self.kwargs["ctr1"] + ctr2 = self.kwargs["ctr2"] + extent1 = self.kwargs["extent1"] + extent2 = self.kwargs["extent2"] + start = self.kwargs["start"] + end = self.kwargs["end"] + r = self.kwargs["r"] thickness = extent2 - extent1 except KeyError: logger.exception(self.__str__()) @@ -74,7 +73,7 @@ class CylindricalSector(UserObjectGeometry): # Check averaging try: # Try user-specified averaging - averagecylindricalsector = self.kwargs['averaging'] + averagecylindricalsector = self.kwargs["averaging"] except KeyError: # Otherwise go with the grid default averagecylindricalsector = grid.averagevolumeobjects @@ -82,31 +81,30 @@ class CylindricalSector(UserObjectGeometry): # Check materials have been specified # Isotropic case try: - materialsrequested = [self.kwargs['material_id']] + materialsrequested = [self.kwargs["material_id"]] except KeyError: # Anisotropic case try: - materialsrequested = self.kwargs['material_ids'] + materialsrequested = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.__str__()} No materials have been specified') + logger.exception(f"{self.__str__()} No materials have been specified") raise sectorstartangle = 2 * np.pi * (start / 360) sectorangle = 2 * np.pi * (end / 360) - if normal not in ['x', 'y', 'z']: - logger.exception(f'{self.__str__()} the normal direction must be either ' + - f'x, y or z.') + if normal not in ["x", "y", "z"]: + logger.exception(f"{self.__str__()} the normal direction must be either " + f"x, y or z.") raise ValueError if r <= 0: - logger.exception(f'{self.__str__()} the radius {r:g} should be a positive value.') + logger.exception(f"{self.__str__()} the radius {r:g} should be a positive value.") if sectorstartangle < 0 or sectorangle <= 0: - logger.exception(f'{self.__str__()} the starting angle and sector angle should be ' + - f'a positive values.') + logger.exception(f"{self.__str__()} the starting angle and sector angle should be " + f"a positive values.") raise ValueError if sectorstartangle >= 2 * np.pi or sectorangle >= 2 * np.pi: - logger.exception(f'{self.__str__()} the starting angle and sector angle must be ' + - f'less than 360 degrees.') + logger.exception( + f"{self.__str__()} the starting angle and sector angle must be " + f"less than 360 degrees." + ) raise ValueError # Look up requested materials in existing list of material instances @@ -114,7 +112,7 @@ class CylindricalSector(UserObjectGeometry): if len(materials) != len(materialsrequested): notfound = [x for x in materialsrequested if x not in materials] - logger.exception(f'{self.__str__()} material(s) {notfound} do not exist') + logger.exception(f"{self.__str__()} material(s) {notfound} do not exist") raise ValueError if thickness > 0: @@ -128,23 +126,19 @@ class CylindricalSector(UserObjectGeometry): numIDx = materials[0].numID numIDy = materials[1].numID numIDz = materials[2].numID - requiredID = f'{materials[0].ID}+{materials[1].ID}+{materials[2].ID}' + requiredID = f"{materials[0].ID}+{materials[1].ID}+{materials[2].ID}" averagedmaterial = [x for x in grid.materials if x.ID == requiredID] if averagedmaterial: numID = averagedmaterial.numID else: numID = len(grid.materials) m = Material(numID, requiredID) - m.type = 'dielectric-smoothed' + m.type = "dielectric-smoothed" # Create dielectric-smoothed constituents for material - m.er = np.mean((materials[0].er, materials[1].er, - materials[2].er), axis=0) - m.se = np.mean((materials[0].se, materials[1].se, - materials[2].se), axis=0) - m.mr = np.mean((materials[0].mr, materials[1].mr, - materials[2].mr), axis=0) - m.sm = np.mean((materials[0].sm, materials[1].sm, - materials[2].sm), axis=0) + m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) + m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0) + m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0) + m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0) # Append the new material object to the materials list grid.materials.append(m) @@ -163,34 +157,56 @@ class CylindricalSector(UserObjectGeometry): numIDz = materials[2].numID # yz-plane cylindrical sector - if normal == 'x': + if normal == "x": level, ctr1, ctr2 = uip.round_to_grid((extent1, ctr1, ctr2)) # xz-plane cylindrical sector - elif normal == 'y': + elif normal == "y": ctr1, level, ctr2 = uip.round_to_grid((ctr1, extent1, ctr2)) # xy-plane cylindrical sector - elif normal == 'z': + elif normal == "z": ctr1, ctr2, level = uip.round_to_grid((ctr1, ctr2, extent1)) - build_cylindrical_sector(ctr1, ctr2, level, sectorstartangle, sectorangle, - r, normal, thickness, grid.dx, grid.dy, grid.dz, - numID, numIDx, numIDy, numIDz, averaging, - grid.solid, grid.rigidE, grid.rigidH, grid.ID) + build_cylindrical_sector( + ctr1, + ctr2, + level, + sectorstartangle, + sectorangle, + r, + normal, + thickness, + grid.dx, + grid.dy, + grid.dz, + numID, + numIDx, + numIDy, + numIDz, + averaging, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) if thickness > 0: - dielectricsmoothing = 'on' if averaging else 'off' - logger.info(f"{self.grid_name(grid)}Cylindrical sector with centre " + - f"{ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle " + - f"{(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, " + - f"sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} degrees, " + - f"thickness {thickness:g}m, of material(s) {', '.join(materialsrequested)} " + - f"created, dielectric smoothing is {dielectricsmoothing}.") + dielectricsmoothing = "on" if averaging else "off" + logger.info( + f"{self.grid_name(grid)}Cylindrical sector with centre " + + f"{ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle " + + f"{(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, " + + f"sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} degrees, " + + f"thickness {thickness:g}m, of material(s) {', '.join(materialsrequested)} " + + f"created, dielectric smoothing is {dielectricsmoothing}." + ) else: - logger.info(f"{self.grid_name(grid)}Cylindrical sector with centre " + - f"{ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle " + - f"{(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, " + - f"sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} " + - f"degrees, of material(s) {', '.join(materialsrequested)} " + - f"created.") + logger.info( + f"{self.grid_name(grid)}Cylindrical sector with centre " + + f"{ctr1:g}m, {ctr2:g}m, radius {r:g}m, starting angle " + + f"{(sectorstartangle / (2 * np.pi)) * 360:.1f} degrees, " + + f"sector angle {(sectorangle / (2 * np.pi)) * 360:.1f} " + + f"degrees, of material(s) {', '.join(materialsrequested)} " + + f"created." + ) diff --git a/gprMax/cmds_geometry/edge.py b/gprMax/cmds_geometry/edge.py index e1af273e..0e1c6302 100644 --- a/gprMax/cmds_geometry/edge.py +++ b/gprMax/cmds_geometry/edge.py @@ -20,8 +20,7 @@ import logging import numpy as np -from ..cython.geometry_primitives import (build_edge_x, build_edge_y, - build_edge_z) +from ..cython.geometry_primitives import build_edge_x, build_edge_y, build_edge_z from .cmds_geometry import UserObjectGeometry, rotate_2point_object logger = logging.getLogger(__name__) @@ -33,13 +32,13 @@ class Edge(UserObjectGeometry): Attributes: p1: list of the coordinates (x,y,z) of the starting point of the edge. p2: list of the coordinates (x,y,z) of the ending point of the edge. - material_id: string for the material identifier that must correspond + material_id: string for the material identifier that must correspond to material that has already been defined. """ def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#edge' + self.hash = "#edge" def rotate(self, axis, angle, origin=None): """Set parameters for rotation.""" @@ -50,19 +49,19 @@ class Edge(UserObjectGeometry): def _do_rotate(self): """Performs rotation.""" - pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) + pts = np.array([self.kwargs["p1"], self.kwargs["p2"]]) rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin) - self.kwargs['p1'] = tuple(rot_pts[0, :]) - self.kwargs['p2'] = tuple(rot_pts[1, :]) - + self.kwargs["p1"] = tuple(rot_pts[0, :]) + self.kwargs["p2"] = tuple(rot_pts[1, :]) + def create(self, grid, uip): """Creates edge and adds it to the grid.""" try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - material_id = self.kwargs['material_id'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + material_id = self.kwargs["material_id"] except KeyError: - logger.exception(f'{self.__str__()} requires exactly 3 parameters') + logger.exception(f"{self.__str__()} requires exactly 3 parameters") raise if self.do_rotate: @@ -78,15 +77,17 @@ class Edge(UserObjectGeometry): material = next((x for x in grid.materials if x.ID == material_id), None) if not material: - logger.exception(f'Material with ID {material_id} does not exist') + logger.exception(f"Material with ID {material_id} does not exist") raise ValueError # Check for valid orientations # x-orientated edge - if ((xs != xf and (ys != yf or zs != zf)) + if ( + (xs != xf and (ys != yf or zs != zf)) or (ys != yf and (xs != xf or zs != zf)) - or (zs != zf and (xs != xf or ys != yf))): - logger.exception(f'{self.__str__()} the edge is not specified correctly') + or (zs != zf and (xs != xf or ys != yf)) + ): + logger.exception(f"{self.__str__()} the edge is not specified correctly") raise ValueError elif xs != xf: for i in range(xs, xf): @@ -100,6 +101,8 @@ class Edge(UserObjectGeometry): for k in range(zs, zf): build_edge_z(xs, ys, k, material.numID, grid.rigidE, grid.rigidH, grid.ID) - logger.info(f'{self.grid_name(grid)}Edge from {p3[0]:g}m, {p3[1]:g}m, ' + - f'{p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m of ' + - f'material {material_id} created.') + logger.info( + f"{self.grid_name(grid)}Edge from {p3[0]:g}m, {p3[1]:g}m, " + + f"{p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m of " + + f"material {material_id} created." + ) diff --git a/gprMax/cmds_geometry/ellipsoid.py b/gprMax/cmds_geometry/ellipsoid.py index 24060607..e72a904a 100644 --- a/gprMax/cmds_geometry/ellipsoid.py +++ b/gprMax/cmds_geometry/ellipsoid.py @@ -35,7 +35,7 @@ class Ellipsoid(UserObjectGeometry): xr: float for x-semiaxis of the elliposid. xy: float for y-semiaxis of the ellipsoid. xz: float for z-semiaxis of the ellipsoid. - material_id: string for the material identifier that must correspond + material_id: string for the material identifier that must correspond to material that has already been defined. material_ids: list of material identifiers in the x, y, z directions. averaging: string (y or n) used to switch on and off dielectric smoothing. @@ -43,24 +43,23 @@ class Ellipsoid(UserObjectGeometry): def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#ellipsoid' + self.hash = "#ellipsoid" def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - xr = self.kwargs['xr'] - yr = self.kwargs['yr'] - zr = self.kwargs['zr'] + p1 = self.kwargs["p1"] + xr = self.kwargs["xr"] + yr = self.kwargs["yr"] + zr = self.kwargs["zr"] except KeyError: - logger.exception(f'{self.__str__()} please specify a point and ' + - f'the three semiaxes.') + logger.exception(f"{self.__str__()} please specify a point and " + f"the three semiaxes.") raise # Check averaging try: # Try user-specified averaging - averageellipsoid = self.kwargs['averaging'] + averageellipsoid = self.kwargs["averaging"] except KeyError: # Otherwise go with the grid default averageellipsoid = grid.averagevolumeobjects @@ -68,13 +67,13 @@ class Ellipsoid(UserObjectGeometry): # Check materials have been specified # Isotropic case try: - materialsrequested = [self.kwargs['material_id']] + materialsrequested = [self.kwargs["material_id"]] except KeyError: # Anisotropic case try: - materialsrequested = self.kwargs['material_ids'] + materialsrequested = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.__str__()} no materials have been specified') + logger.exception(f"{self.__str__()} no materials have been specified") raise # Centre of ellipsoid @@ -86,7 +85,7 @@ class Ellipsoid(UserObjectGeometry): if len(materials) != len(materialsrequested): notfound = [x for x in materialsrequested if x not in materials] - logger.exception(f'{self.__str__()} material(s) {notfound} do not exist') + logger.exception(f"{self.__str__()} material(s) {notfound} do not exist") raise ValueError # Isotropic case @@ -100,34 +99,49 @@ class Ellipsoid(UserObjectGeometry): numIDx = materials[0].numID numIDy = materials[1].numID numIDz = materials[2].numID - requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID + requiredID = materials[0].ID + "+" + materials[1].ID + "+" + materials[2].ID averagedmaterial = [x for x in grid.materials if x.ID == requiredID] if averagedmaterial: numID = averagedmaterial.numID else: numID = len(grid.materials) m = Material(numID, requiredID) - m.type = 'dielectric-smoothed' + m.type = "dielectric-smoothed" # Create dielectric-smoothed constituents for material - m.er = np.mean((materials[0].er, materials[1].er, - materials[2].er), axis=0) - m.se = np.mean((materials[0].se, materials[1].se, - materials[2].se), axis=0) - m.mr = np.mean((materials[0].mr, materials[1].mr, - materials[2].mr), axis=0) - m.sm = np.mean((materials[0].sm, materials[1].sm, - materials[2].sm), axis=0) + m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) + m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0) + m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0) + m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0) # Append the new material object to the materials list grid.materials.append(m) - build_ellipsoid(xc, yc, zc, xr, yr, zr, grid.dx, grid.dy, grid.dz, numID, - numIDx, numIDy, numIDz, averaging, grid.solid, - grid.rigidE, grid.rigidH, grid.ID) + build_ellipsoid( + xc, + yc, + zc, + xr, + yr, + zr, + grid.dx, + grid.dy, + grid.dz, + numID, + numIDx, + numIDy, + numIDz, + averaging, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) - dielectricsmoothing = 'on' if averaging else 'off' - logger.info(f"{self.grid_name(grid)}Ellipsoid with centre {p2[0]:g}m, " + - f"{p2[1]:g}m, {p2[2]:g}m, x-semiaxis {xr:g}m, " + - f"y-semiaxis {yr:g}m and z-semiaxis {zr:g}m of material(s) " + - f"{', '.join(materialsrequested)} created, dielectric " + - f"smoothing is {dielectricsmoothing}.") + dielectricsmoothing = "on" if averaging else "off" + logger.info( + f"{self.grid_name(grid)}Ellipsoid with centre {p2[0]:g}m, " + + f"{p2[1]:g}m, {p2[2]:g}m, x-semiaxis {xr:g}m, " + + f"y-semiaxis {yr:g}m and z-semiaxis {zr:g}m of material(s) " + + f"{', '.join(materialsrequested)} created, dielectric " + + f"smoothing is {dielectricsmoothing}." + ) diff --git a/gprMax/cmds_geometry/fractal_box.py b/gprMax/cmds_geometry/fractal_box.py index 5e6cc598..a205415f 100644 --- a/gprMax/cmds_geometry/fractal_box.py +++ b/gprMax/cmds_geometry/fractal_box.py @@ -28,32 +28,32 @@ logger = logging.getLogger(__name__) class FractalBox(UserObjectGeometry): - """Introduces an orthogonal parallelepiped with fractal distributed - properties which are related to a mixing model or normal material into + """Introduces an orthogonal parallelepiped with fractal distributed + properties which are related to a mixing model or normal material into the model. Attributes: p1: list of the lower left (x,y,z) coordinates of the parallelepiped. p2: list of the upper right (x,y,z) coordinates of the parallelepiped. - frac_dim: float for the fractal dimension which, for an orthogonal + frac_dim: float for the fractal dimension which, for an orthogonal parallelepiped, should take values between zero and three. - weighting: list of the weightings in the x, y, z direction of the + weighting: list of the weightings in the x, y, z direction of the parallelepiped. - n_materials: int of the number of materials to use for the fractal - distribution (defined according to the associated - mixing model). This should be set to one if using a + n_materials: int of the number of materials to use for the fractal + distribution (defined according to the associated + mixing model). This should be set to one if using a normal material instead of a mixing model. - mixing_model_id: string identifier for the associated mixing model or + mixing_model_id: string identifier for the associated mixing model or material. id: string identifier for the fractal box itself. - seed: (optional) float parameter which controls the seeding of the + seed: (optional) float parameter which controls the seeding of the random number generator used to create the fractals. averaging: string (y or n) used to switch on and off dielectric smoothing. """ def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#fractal_box' + self.hash = "#fractal_box" def rotate(self, axis, angle, origin=None): """Set parameters for rotation.""" @@ -64,30 +64,32 @@ class FractalBox(UserObjectGeometry): def _do_rotate(self): """Performs rotation.""" - pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) + pts = np.array([self.kwargs["p1"], self.kwargs["p2"]]) rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin) - self.kwargs['p1'] = tuple(rot_pts[0, :]) - self.kwargs['p2'] = tuple(rot_pts[1, :]) + self.kwargs["p1"] = tuple(rot_pts[0, :]) + self.kwargs["p2"] = tuple(rot_pts[1, :]) def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - frac_dim = self.kwargs['frac_dim'] - weighting = np.array(self.kwargs['weighting']) - n_materials = self.kwargs['n_materials'] - mixing_model_id = self.kwargs['mixing_model_id'] - ID = self.kwargs['id'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + frac_dim = self.kwargs["frac_dim"] + weighting = np.array(self.kwargs["weighting"]) + n_materials = self.kwargs["n_materials"] + mixing_model_id = self.kwargs["mixing_model_id"] + ID = self.kwargs["id"] except KeyError: - logger.exception(f'{self.__str__()} Incorrect parameters') + logger.exception(f"{self.__str__()} Incorrect parameters") raise try: - seed = self.kwargs['seed'] + seed = self.kwargs["seed"] except KeyError: - logger.warning(f'{self.__str__()} no value for seed detected. This ' + - 'means you will get a different fractal distribution ' + - 'every time the model runs.') + logger.warning( + f"{self.__str__()} no value for seed detected. This " + + "means you will get a different fractal distribution " + + "every time the model runs." + ) seed = None if self.do_rotate: @@ -96,9 +98,9 @@ class FractalBox(UserObjectGeometry): # Check averaging try: # Go with user specified averaging - averagefractalbox = self.kwargs['averaging'] + averagefractalbox = self.kwargs["averaging"] except KeyError: - # If they havent specified - default is no dielectric smoothing for + # If they havent specified - default is no dielectric smoothing for # a fractal box. averagefractalbox = False @@ -110,26 +112,27 @@ class FractalBox(UserObjectGeometry): xf, yf, zf = p2 if frac_dim < 0: - logger.exception(f'{self.__str__()} requires a positive value for the ' + - 'fractal dimension') + logger.exception(f"{self.__str__()} requires a positive value for the " + "fractal dimension") raise ValueError if weighting[0] < 0: - logger.exception(f'{self.__str__()} requires a positive value for the ' + - 'fractal weighting in the x direction') + logger.exception( + f"{self.__str__()} requires a positive value for the " + "fractal weighting in the x direction" + ) raise ValueError if weighting[1] < 0: - logger.exception(f'{self.__str__()} requires a positive value for the ' + - 'fractal weighting in the y direction') + logger.exception( + f"{self.__str__()} requires a positive value for the " + "fractal weighting in the y direction" + ) raise ValueError if weighting[2] < 0: - logger.exception(f'{self.__str__()} requires a positive value for the ' + - 'fractal weighting in the z direction') + logger.exception( + f"{self.__str__()} requires a positive value for the " + "fractal weighting in the z direction" + ) if n_materials < 0: - logger.exception(f'{self.__str__()} requires a positive value for the ' + - 'number of bins') + logger.exception(f"{self.__str__()} requires a positive value for the " + "number of bins") raise ValueError - # Find materials to use to build fractal volume, either from mixing + # Find materials to use to build fractal volume, either from mixing # models or normal materials. mixingmodel = next((x for x in grid.mixingmodels if x.ID == mixing_model_id), None) material = next((x for x in grid.materials if x.ID == mixing_model_id), None) @@ -137,21 +140,23 @@ class FractalBox(UserObjectGeometry): if mixingmodel: if nbins == 1: - logger.exception(f'{self.__str__()} must be used with more than ' + - 'one material from the mixing model.') + logger.exception( + f"{self.__str__()} must be used with more than " + "one material from the mixing model." + ) raise ValueError if isinstance(mixingmodel, ListMaterial): if nbins > len(mixingmodel.mat): - logger.exception(f'{self.__str__()} too many materials/bins ' + - 'requested compared to materials in ' + - 'mixing model.') + logger.exception( + f"{self.__str__()} too many materials/bins " + + "requested compared to materials in " + + "mixing model." + ) raise ValueError - # Create materials from mixing model as number of bins now known + # Create materials from mixing model as number of bins now known # from fractal_box command. mixingmodel.calculate_properties(nbins, grid) elif not material: - logger.exception(f'{self.__str__()} mixing model or material with ' + - 'ID {mixing_model_id} does not exist') + logger.exception(f"{self.__str__()} mixing model or material with " + "ID {mixing_model_id} does not exist") raise ValueError volume = FractalVolume(xs, xf, ys, yf, zs, zf, frac_dim) @@ -166,14 +171,16 @@ class FractalBox(UserObjectGeometry): volume.averaging = averagefractalbox volume.mixingmodel = mixingmodel - dielectricsmoothing = 'on' if volume.averaging else 'off' - logger.info(f'{self.grid_name(grid)}Fractal box {volume.ID} from ' + - f'{p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, ' + - f'{p4[1]:g}m, {p4[2]:g}m with {volume.operatingonID}, ' + - f'fractal dimension {volume.dimension:g}, fractal weightings ' + - f'{volume.weighting[0]:g}, {volume.weighting[1]:g}, ' + - f'{volume.weighting[2]:g}, fractal seeding {volume.seed}, ' + - f'with {volume.nbins} material(s) created, dielectric smoothing ' + - f'is {dielectricsmoothing}.') + dielectricsmoothing = "on" if volume.averaging else "off" + logger.info( + f"{self.grid_name(grid)}Fractal box {volume.ID} from " + + f"{p3[0]:g}m, {p3[1]:g}m, {p3[2]:g}m, to {p4[0]:g}m, " + + f"{p4[1]:g}m, {p4[2]:g}m with {volume.operatingonID}, " + + f"fractal dimension {volume.dimension:g}, fractal weightings " + + f"{volume.weighting[0]:g}, {volume.weighting[1]:g}, " + + f"{volume.weighting[2]:g}, fractal seeding {volume.seed}, " + + f"with {volume.nbins} material(s) created, dielectric smoothing " + + f"is {dielectricsmoothing}." + ) grid.fractalvolumes.append(volume) diff --git a/gprMax/cmds_geometry/fractal_box_builder.py b/gprMax/cmds_geometry/fractal_box_builder.py index d8db556a..ee97fdce 100644 --- a/gprMax/cmds_geometry/fractal_box_builder.py +++ b/gprMax/cmds_geometry/fractal_box_builder.py @@ -21,8 +21,7 @@ import logging import gprMax.config as config import numpy as np -from ..cython.geometry_primitives import (build_voxels_from_array, - build_voxels_from_array_mask) +from ..cython.geometry_primitives import build_voxels_from_array, build_voxels_from_array_mask from .cmds_geometry import UserObjectGeometry logger = logging.getLogger(__name__) @@ -30,11 +29,11 @@ logger = logging.getLogger(__name__) class FractalBoxBuilder(UserObjectGeometry): """Internal class for fractal box modifications. This class should be used - internally only when surface modification have been made to a fractal box.""" + internally only when surface modification have been made to a fractal box.""" def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#fractal_box_modifications' + self.hash = "#fractal_box_modifications" def create(self, grid, uip): for volume in grid.fractalvolumes: @@ -46,39 +45,40 @@ class FractalBoxBuilder(UserObjectGeometry): volume.originalzs = volume.zs volume.originalzf = volume.zf - # Extend the volume to accomodate any rough surfaces, grass, + # Extend the volume to accomodate any rough surfaces, grass, # or roots for surface in volume.fractalsurfaces: - if surface.surfaceID == 'xminus': + if surface.surfaceID == "xminus": if surface.fractalrange[0] < volume.xs: volume.nx += volume.xs - surface.fractalrange[0] volume.xs = surface.fractalrange[0] - elif surface.surfaceID == 'xplus': + elif surface.surfaceID == "xplus": if surface.fractalrange[1] > volume.xf: volume.nx += surface.fractalrange[1] - volume.xf volume.xf = surface.fractalrange[1] - elif surface.surfaceID == 'yminus': + elif surface.surfaceID == "yminus": if surface.fractalrange[0] < volume.ys: volume.ny += volume.ys - surface.fractalrange[0] volume.ys = surface.fractalrange[0] - elif surface.surfaceID == 'yplus': + elif surface.surfaceID == "yplus": if surface.fractalrange[1] > volume.yf: volume.ny += surface.fractalrange[1] - volume.yf volume.yf = surface.fractalrange[1] - elif surface.surfaceID == 'zminus': + elif surface.surfaceID == "zminus": if surface.fractalrange[0] < volume.zs: volume.nz += volume.zs - surface.fractalrange[0] volume.zs = surface.fractalrange[0] - elif surface.surfaceID == 'zplus': + elif surface.surfaceID == "zplus": if surface.fractalrange[1] > volume.zf: volume.nz += surface.fractalrange[1] - volume.zf volume.zf = surface.fractalrange[1] - # If there is only 1 bin then a normal material is being used, + # If there is only 1 bin then a normal material is being used, # otherwise a mixing model if volume.nbins == 1: - volume.fractalvolume = np.ones((volume.nx, volume.ny, volume.nz), - dtype=config.sim_config.dtypes['float_or_double']) + volume.fractalvolume = np.ones( + (volume.nx, volume.ny, volume.nz), dtype=config.sim_config.dtypes["float_or_double"] + ) materialnumID = next(x.numID for x in grid.materials if x.ID == volume.operatingonID) volume.fractalvolume *= materialnumID else: @@ -86,16 +86,16 @@ class FractalBoxBuilder(UserObjectGeometry): for i in range(0, volume.nx): for j in range(0, volume.ny): for k in range(0, volume.nz): - numberinbin = volume.fractalvolume[i,j,k] - volume.fractalvolume[i,j,k] = volume.mixingmodel.matID[int(numberinbin)] + numberinbin = volume.fractalvolume[i, j, k] + volume.fractalvolume[i, j, k] = volume.mixingmodel.matID[int(numberinbin)] volume.generate_volume_mask() - # Apply any rough surfaces and add any surface water to the + # Apply any rough surfaces and add any surface water to the # 3D mask array # TODO: Allow extract of rough surface profile (to print/file?) for surface in volume.fractalsurfaces: - if surface.surfaceID == 'xminus': + if surface.surfaceID == "xminus": for i in range(surface.fractalrange[0], surface.fractalrange[1]): for j in range(surface.ys, surface.yf): for k in range(surface.zs, surface.zf): @@ -106,7 +106,7 @@ class FractalBoxBuilder(UserObjectGeometry): else: volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0 - elif surface.surfaceID == 'xplus': + elif surface.surfaceID == "xplus": if not surface.ID: for i in range(surface.fractalrange[0], surface.fractalrange[1]): for j in range(surface.ys, surface.yf): @@ -117,7 +117,7 @@ class FractalBoxBuilder(UserObjectGeometry): volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2 else: volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0 - elif surface.ID == 'grass': + elif surface.ID == "grass": g = surface.grass[0] # Build the blades of the grass blade = 0 @@ -126,15 +126,22 @@ class FractalBoxBuilder(UserObjectGeometry): if surface.fractalsurface[j - surface.ys, k - surface.zs] > 0: height = 0 for i in range(volume.xs, surface.fractalrange[1]): - if (i < surface.fractalsurface[j - surface.ys, k - surface.zs] and - volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1): + if ( + i < surface.fractalsurface[j - surface.ys, k - surface.zs] + and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1 + ): y, z = g.calculate_blade_geometry(blade, height) # Add y, z coordinates to existing location yy = int(j - volume.ys + y) zz = int(k - volume.zs + z) - # If these coordinates are outwith fractal volume stop building the blade, + # If these coordinates are outwith fractal volume stop building the blade, # otherwise set the mask for grass. - if yy < 0 or yy >= volume.mask.shape[1] or zz < 0 or zz >= volume.mask.shape[2]: + if ( + yy < 0 + or yy >= volume.mask.shape[1] + or zz < 0 + or zz >= volume.mask.shape[2] + ): break else: volume.mask[i - volume.xs, yy, zz] = 3 @@ -149,15 +156,27 @@ class FractalBoxBuilder(UserObjectGeometry): depth = 0 i = volume.xf - 1 while i > volume.xs: - if (i > volume.originalxf - (surface.fractalsurface[j - surface.ys, k - surface.zs] - - volume.originalxf) and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1): + if ( + i + > volume.originalxf + - ( + surface.fractalsurface[j - surface.ys, k - surface.zs] + - volume.originalxf + ) + and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1 + ): y, z = g.calculate_root_geometry(root, depth) # Add y, z coordinates to existing location yy = int(j - volume.ys + y) zz = int(k - volume.zs + z) - # If these coordinates are outwith the fractal volume stop building the root, + # If these coordinates are outwith the fractal volume stop building the root, # otherwise set the mask for grass. - if yy < 0 or yy >= volume.mask.shape[1] or zz < 0 or zz >= volume.mask.shape[2]: + if ( + yy < 0 + or yy >= volume.mask.shape[1] + or zz < 0 + or zz >= volume.mask.shape[2] + ): break else: volume.mask[i - volume.xs, yy, zz] = 3 @@ -165,7 +184,7 @@ class FractalBoxBuilder(UserObjectGeometry): i -= 1 root += 1 - elif surface.surfaceID == 'yminus': + elif surface.surfaceID == "yminus": for i in range(surface.xs, surface.xf): for j in range(surface.fractalrange[0], surface.fractalrange[1]): for k in range(surface.zs, surface.zf): @@ -176,7 +195,7 @@ class FractalBoxBuilder(UserObjectGeometry): else: volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0 - elif surface.surfaceID == 'yplus': + elif surface.surfaceID == "yplus": if not surface.ID: for i in range(surface.xs, surface.xf): for j in range(surface.fractalrange[0], surface.fractalrange[1]): @@ -187,7 +206,7 @@ class FractalBoxBuilder(UserObjectGeometry): volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2 else: volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0 - elif surface.ID == 'grass': + elif surface.ID == "grass": g = surface.grass[0] # Build the blades of the grass blade = 0 @@ -196,15 +215,22 @@ class FractalBoxBuilder(UserObjectGeometry): if surface.fractalsurface[i - surface.xs, k - surface.zs] > 0: height = 0 for j in range(volume.ys, surface.fractalrange[1]): - if (j < surface.fractalsurface[i - surface.xs, k - surface.zs] and - volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1): + if ( + j < surface.fractalsurface[i - surface.xs, k - surface.zs] + and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1 + ): x, z = g.calculate_blade_geometry(blade, height) # Add x, z coordinates to existing location xx = int(i - volume.xs + x) zz = int(k - volume.zs + z) - # If these coordinates are outwith fractal volume stop building the blade, + # If these coordinates are outwith fractal volume stop building the blade, # otherwise set the mask for grass. - if xx < 0 or xx >= volume.mask.shape[0] or zz < 0 or zz >= volume.mask.shape[2]: + if ( + xx < 0 + or xx >= volume.mask.shape[0] + or zz < 0 + or zz >= volume.mask.shape[2] + ): break else: volume.mask[xx, j - volume.ys, zz] = 3 @@ -219,15 +245,27 @@ class FractalBoxBuilder(UserObjectGeometry): depth = 0 j = volume.yf - 1 while j > volume.ys: - if (j > volume.originalyf - (surface.fractalsurface[i - surface.xs, k - surface.zs] - - volume.originalyf) and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1): + if ( + j + > volume.originalyf + - ( + surface.fractalsurface[i - surface.xs, k - surface.zs] + - volume.originalyf + ) + and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1 + ): x, z = g.calculate_root_geometry(root, depth) # Add x, z coordinates to existing location xx = int(i - volume.xs + x) zz = int(k - volume.zs + z) - # If these coordinates are outwith the fractal volume stop building the root, + # If these coordinates are outwith the fractal volume stop building the root, # otherwise set the mask for grass. - if xx < 0 or xx >= volume.mask.shape[0] or zz < 0 or zz >= volume.mask.shape[2]: + if ( + xx < 0 + or xx >= volume.mask.shape[0] + or zz < 0 + or zz >= volume.mask.shape[2] + ): break else: volume.mask[xx, j - volume.ys, zz] = 3 @@ -235,7 +273,7 @@ class FractalBoxBuilder(UserObjectGeometry): j -= 1 root += 1 - elif surface.surfaceID == 'zminus': + elif surface.surfaceID == "zminus": for i in range(surface.xs, surface.xf): for j in range(surface.ys, surface.yf): for k in range(surface.fractalrange[0], surface.fractalrange[1]): @@ -246,7 +284,7 @@ class FractalBoxBuilder(UserObjectGeometry): else: volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0 - elif surface.surfaceID == 'zplus': + elif surface.surfaceID == "zplus": if not surface.ID: for i in range(surface.xs, surface.xf): for j in range(surface.ys, surface.yf): @@ -257,7 +295,7 @@ class FractalBoxBuilder(UserObjectGeometry): volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 2 else: volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] = 0 - elif surface.ID == 'grass': + elif surface.ID == "grass": g = surface.grass[0] # Build the blades of the grass blade = 0 @@ -266,15 +304,22 @@ class FractalBoxBuilder(UserObjectGeometry): if surface.fractalsurface[i - surface.xs, j - surface.ys] > 0: height = 0 for k in range(volume.zs, surface.fractalrange[1]): - if (k < surface.fractalsurface[i - surface.xs, j - surface.ys] and - volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1): + if ( + k < surface.fractalsurface[i - surface.xs, j - surface.ys] + and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] != 1 + ): x, y = g.calculate_blade_geometry(blade, height) # Add x, y coordinates to existing location xx = int(i - volume.xs + x) yy = int(j - volume.ys + y) - # If these coordinates are outwith the fractal volume stop building the blade, + # If these coordinates are outwith the fractal volume stop building the blade, # otherwise set the mask for grass. - if xx < 0 or xx >= volume.mask.shape[0] or yy < 0 or yy >= volume.mask.shape[1]: + if ( + xx < 0 + or xx >= volume.mask.shape[0] + or yy < 0 + or yy >= volume.mask.shape[1] + ): break else: volume.mask[xx, yy, k - volume.zs] = 3 @@ -289,15 +334,27 @@ class FractalBoxBuilder(UserObjectGeometry): depth = 0 k = volume.zf - 1 while k > volume.zs: - if (k > volume.originalzf - (surface.fractalsurface[i - surface.xs, j - surface.ys] - - volume.originalzf) and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1): + if ( + k + > volume.originalzf + - ( + surface.fractalsurface[i - surface.xs, j - surface.ys] + - volume.originalzf + ) + and volume.mask[i - volume.xs, j - volume.ys, k - volume.zs] == 1 + ): x, y = g.calculate_root_geometry(root, depth) # Add x, y coordinates to existing location xx = int(i - volume.xs + x) yy = int(j - volume.ys + y) - # If these coordinates are outwith the fractal volume stop building the root, + # If these coordinates are outwith the fractal volume stop building the root, # otherwise set the mask for grass. - if xx < 0 or xx >= volume.mask.shape[0] or yy < 0 or yy >= volume.mask.shape[1]: + if ( + xx < 0 + or xx >= volume.mask.shape[0] + or yy < 0 + or yy >= volume.mask.shape[1] + ): break else: volume.mask[xx, yy, k - volume.zs] = 3 @@ -306,33 +363,53 @@ class FractalBoxBuilder(UserObjectGeometry): root += 1 # Build voxels from any true values of the 3D mask array - waternumID = next((x.numID for x in grid.materials if x.ID == 'water'), 0) - grassnumID = next((x.numID for x in grid.materials if x.ID == 'grass'), 0) - data = volume.fractalvolume.astype('int16', order='C') - mask = volume.mask.copy(order='C') - build_voxels_from_array_mask(volume.xs, volume.ys, volume.zs, - config.get_model_config().ompthreads, - waternumID, grassnumID, volume.averaging, - mask, data, grid.solid, grid.rigidE, - grid.rigidH, grid.ID) + waternumID = next((x.numID for x in grid.materials if x.ID == "water"), 0) + grassnumID = next((x.numID for x in grid.materials if x.ID == "grass"), 0) + data = volume.fractalvolume.astype("int16", order="C") + mask = volume.mask.copy(order="C") + build_voxels_from_array_mask( + volume.xs, + volume.ys, + volume.zs, + config.get_model_config().ompthreads, + waternumID, + grassnumID, + volume.averaging, + mask, + data, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) else: if volume.nbins == 1: - logger.exception(f'{self.__str__()} is being used with a ' + - 'single material and no modifications, ' + - 'therefore please use a #box command instead.') + logger.exception( + f"{self.__str__()} is being used with a " + + "single material and no modifications, " + + "therefore please use a #box command instead." + ) raise ValueError else: volume.generate_fractal_volume() for i in range(0, volume.nx): for j in range(0, volume.ny): for k in range(0, volume.nz): - numberinbin = volume.fractalvolume[i,j,k] - volume.fractalvolume[i,j,k] = volume.mixingmodel.matID[int(numberinbin)] - + numberinbin = volume.fractalvolume[i, j, k] + volume.fractalvolume[i, j, k] = volume.mixingmodel.matID[int(numberinbin)] - data = volume.fractalvolume.astype('int16', order='C') - build_voxels_from_array(volume.xs, volume.ys, volume.zs, - config.get_model_config().ompthreads, 0, - volume.averaging, data, grid.solid, - grid.rigidE, grid.rigidH, grid.ID) + data = volume.fractalvolume.astype("int16", order="C") + build_voxels_from_array( + volume.xs, + volume.ys, + volume.zs, + config.get_model_config().ompthreads, + 0, + volume.averaging, + data, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) diff --git a/gprMax/cmds_geometry/geometry_objects_read.py b/gprMax/cmds_geometry/geometry_objects_read.py index a53f84af..6f194cd6 100644 --- a/gprMax/cmds_geometry/geometry_objects_read.py +++ b/gprMax/cmds_geometry/geometry_objects_read.py @@ -31,10 +31,9 @@ logger = logging.getLogger(__name__) class GeometryObjectsRead(UserObjectGeometry): - def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#geometry_objects_read' + self.hash = "#geometry_objects_read" def rotate(self, axis, angle, origin=None): pass @@ -42,11 +41,11 @@ class GeometryObjectsRead(UserObjectGeometry): def create(self, grid, uip): """Creates the object and adds it to the grid.""" try: - p1 = self.kwargs['p1'] - geofile = self.kwargs['geofile'] - matfile = self.kwargs['matfile'] + p1 = self.kwargs["p1"] + geofile = self.kwargs["geofile"] + matfile = self.kwargs["matfile"] except KeyError: - logger.exception(f'{self.__str__()} requires exactly five parameters') + logger.exception(f"{self.__str__()} requires exactly five parameters") raise # Discretise the point using uip object. This has different behaviour @@ -61,14 +60,18 @@ class GeometryObjectsRead(UserObjectGeometry): if not matfile.exists(): matfile = Path(config.sim_config.input_file_path.parent, matfile) - matstr = matfile.with_suffix('').name + matstr = matfile.with_suffix("").name numexistmaterials = len(grid.materials) # Read materials from file - with open(matfile, 'r') as f: + with open(matfile, "r") as f: # Read any lines that begin with a hash. Strip out any newline # characters and comments that must begin with double hashes. - materials = [line.rstrip() + '{' + matstr + '}\n' for line in f if(line.startswith('#') and not line.startswith('##') and line.rstrip('\n'))] + materials = [ + line.rstrip() + "{" + matstr + "}\n" + for line in f + if (line.startswith("#") and not line.startswith("##") and line.rstrip("\n")) + ] # Build scene # API for multiple scenes / model runs @@ -84,54 +87,74 @@ class GeometryObjectsRead(UserObjectGeometry): for material in grid.materials: if material.numID >= numexistmaterials: if material.type: - material.type += ',\nimported' + material.type += ",\nimported" else: - material.type = 'imported' + material.type = "imported" - # See if geometry object file exists at specified path and if not try + # See if geometry object file exists at specified path and if not try # input file directory. geofile = Path(geofile) if not geofile.exists(): geofile = Path(config.sim_config.input_file_path.parent, geofile) # Open geometry object file and read/check spatial resolution attribute - f = h5py.File(geofile, 'r') - dx_dy_dz = f.attrs['dx_dy_dz'] - if round_value((dx_dy_dz[0] / grid.dx) != 1 or - round_value(dx_dy_dz[1] / grid.dy) != 1 or - round_value(dx_dy_dz[2] / grid.dz) != 1): - logger.exception(f'{self.__str__()} requires the spatial resolution ' + - 'of the geometry objects file to match the spatial ' + - 'resolution of the model') + f = h5py.File(geofile, "r") + dx_dy_dz = f.attrs["dx_dy_dz"] + if round_value( + (dx_dy_dz[0] / grid.dx) != 1 + or round_value(dx_dy_dz[1] / grid.dy) != 1 + or round_value(dx_dy_dz[2] / grid.dz) != 1 + ): + logger.exception( + f"{self.__str__()} requires the spatial resolution " + + "of the geometry objects file to match the spatial " + + "resolution of the model" + ) raise ValueError - data = f['/data'][:] + data = f["/data"][:] # Should be int16 to allow for -1 which indicates background, i.e. # don't build anything, but AustinMan/Woman maybe uint16 - if data.dtype != 'int16': - data = data.astype('int16') + if data.dtype != "int16": + data = data.astype("int16") # Look to see if rigid and ID arrays are present (these should be # present if the original geometry objects were written from gprMax) try: - rigidE = f['/rigidE'][:] - rigidH = f['/rigidH'][:] - ID = f['/ID'][:] - grid.solid[xs:xs + data.shape[0], ys:ys + data.shape[1], zs:zs + data.shape[2]] = data + numexistmaterials - grid.rigidE[:, xs:xs + rigidE.shape[1], ys:ys + rigidE.shape[2], zs:zs + rigidE.shape[3]] = rigidE - grid.rigidH[:, xs:xs + rigidH.shape[1], ys:ys + rigidH.shape[2], zs:zs + rigidH.shape[3]] = rigidH - grid.ID[:, xs:xs + ID.shape[1], ys:ys + ID.shape[2], zs:zs + ID.shape[3]] = ID + numexistmaterials - logger.info(f'{self.grid_name(grid)}Geometry objects from file {geofile} ' + - f'inserted at {xs * grid.dx:g}m, {ys * grid.dy:g}m, ' + - f'{zs * grid.dz:g}m, with corresponding materials file ' + - f'{matfile}.') + rigidE = f["/rigidE"][:] + rigidH = f["/rigidH"][:] + ID = f["/ID"][:] + grid.solid[xs : xs + data.shape[0], ys : ys + data.shape[1], zs : zs + data.shape[2]] = ( + data + numexistmaterials + ) + grid.rigidE[:, xs : xs + rigidE.shape[1], ys : ys + rigidE.shape[2], zs : zs + rigidE.shape[3]] = rigidE + grid.rigidH[:, xs : xs + rigidH.shape[1], ys : ys + rigidH.shape[2], zs : zs + rigidH.shape[3]] = rigidH + grid.ID[:, xs : xs + ID.shape[1], ys : ys + ID.shape[2], zs : zs + ID.shape[3]] = ID + numexistmaterials + logger.info( + f"{self.grid_name(grid)}Geometry objects from file {geofile} " + + f"inserted at {xs * grid.dx:g}m, {ys * grid.dy:g}m, " + + f"{zs * grid.dz:g}m, with corresponding materials file " + + f"{matfile}." + ) except KeyError: averaging = False - build_voxels_from_array(xs, ys, zs, config.get_model_config().ompthreads, - numexistmaterials, averaging, data, - grid.solid, grid.rigidE, grid.rigidH, grid.ID) - logger.info(f'{self.grid_name(grid)}Geometry objects from file ' + - f'(voxels only){geofile} inserted at {xs * grid.dx:g}m, ' + - f'{ys * grid.dy:g}m, {zs * grid.dz:g}m, with corresponding ' + - f'materials file {matfile}.') + build_voxels_from_array( + xs, + ys, + zs, + config.get_model_config().ompthreads, + numexistmaterials, + averaging, + data, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) + logger.info( + f"{self.grid_name(grid)}Geometry objects from file " + + f"(voxels only){geofile} inserted at {xs * grid.dx:g}m, " + + f"{ys * grid.dy:g}m, {zs * grid.dz:g}m, with corresponding " + + f"materials file {matfile}." + ) diff --git a/gprMax/cmds_geometry/plate.py b/gprMax/cmds_geometry/plate.py index bffc7b2a..005fa535 100644 --- a/gprMax/cmds_geometry/plate.py +++ b/gprMax/cmds_geometry/plate.py @@ -20,8 +20,7 @@ import logging import numpy as np -from ..cython.geometry_primitives import (build_face_xy, build_face_xz, - build_face_yz) +from ..cython.geometry_primitives import build_face_xy, build_face_xz, build_face_yz from .cmds_geometry import UserObjectGeometry, rotate_2point_object logger = logging.getLogger(__name__) @@ -33,14 +32,14 @@ class Plate(UserObjectGeometry): Attributes: p1: list of the lower left (x,y,z) coordinates of the plate. p2: list of the upper right (x,y,z) coordinates of the plate. - material_id: string for the material identifier that must correspond + material_id: string for the material identifier that must correspond to material that has already been defined. material_ids: list of material identifiers in the x, y, z directions. """ def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#plate' + self.hash = "#plate" def rotate(self, axis, angle, origin=None): """Set parameters for rotation.""" @@ -51,28 +50,28 @@ class Plate(UserObjectGeometry): def _do_rotate(self): """Performs rotation.""" - pts = np.array([self.kwargs['p1'], self.kwargs['p2']]) + pts = np.array([self.kwargs["p1"], self.kwargs["p2"]]) rot_pts = rotate_2point_object(pts, self.axis, self.angle, self.origin) - self.kwargs['p1'] = tuple(rot_pts[0, :]) - self.kwargs['p2'] = tuple(rot_pts[1, :]) - + self.kwargs["p1"] = tuple(rot_pts[0, :]) + self.kwargs["p2"] = tuple(rot_pts[1, :]) + def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] except KeyError: - logger.exception(f'{self.__str__()} 2 points must be specified') + logger.exception(f"{self.__str__()} 2 points must be specified") raise # isotropic try: - materialsrequested = [self.kwargs['material_id']] + materialsrequested = [self.kwargs["material_id"]] except KeyError: # Anisotropic case try: - materialsrequested = self.kwargs['material_ids'] + materialsrequested = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.__str__()} No materials have been specified') + logger.exception(f"{self.__str__()} No materials have been specified") raise if self.do_rotate: @@ -86,10 +85,12 @@ class Plate(UserObjectGeometry): xf, yf, zf = p2 # Check for valid orientations - if ((xs == xf and (ys == yf or zs == zf)) + if ( + (xs == xf and (ys == yf or zs == zf)) or (ys == yf and (xs == xf or zs == zf)) - or (zs == zf and (xs == xf or ys == yf))): - logger.exception(f'{self.__str__()} the plate is not specified correctly') + or (zs == zf and (xs == xf or ys == yf)) + ): + logger.exception(f"{self.__str__()} the plate is not specified correctly") raise ValueError # Look up requested materials in existing list of material instances @@ -97,7 +98,7 @@ class Plate(UserObjectGeometry): if len(materials) != len(materialsrequested): notfound = [x for x in materialsrequested if x not in materials] - logger.exception(f'{self.__str__()} material(s) {notfound} do not exist') + logger.exception(f"{self.__str__()} material(s) {notfound} do not exist") raise ValueError # yz-plane plate @@ -113,8 +114,7 @@ class Plate(UserObjectGeometry): for j in range(ys, yf): for k in range(zs, zf): - build_face_yz(xs, j, k, numIDy, numIDz, grid.rigidE, - grid.rigidH, grid.ID) + build_face_yz(xs, j, k, numIDy, numIDz, grid.rigidE, grid.rigidH, grid.ID) # xz-plane plate elif ys == yf: @@ -129,8 +129,7 @@ class Plate(UserObjectGeometry): for i in range(xs, xf): for k in range(zs, zf): - build_face_xz(i, ys, k, numIDx, numIDz, grid.rigidE, - grid.rigidH, grid.ID) + build_face_xz(i, ys, k, numIDx, numIDz, grid.rigidE, grid.rigidH, grid.ID) # xy-plane plate elif zs == zf: @@ -145,9 +144,10 @@ class Plate(UserObjectGeometry): for i in range(xs, xf): for j in range(ys, yf): - build_face_xy(i, j, zs, numIDx, numIDy, grid.rigidE, - grid.rigidH, grid.ID) + build_face_xy(i, j, zs, numIDx, numIDy, grid.rigidE, grid.rigidH, grid.ID) - logger.info(f"{self.grid_name(grid)}Plate from {p3[0]:g}m, {p3[1]:g}m, " + - f"{p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m of " + - f"material(s) {', '.join(materialsrequested)} created.") + logger.info( + f"{self.grid_name(grid)}Plate from {p3[0]:g}m, {p3[1]:g}m, " + + f"{p3[2]:g}m, to {p4[0]:g}m, {p4[1]:g}m, {p4[2]:g}m of " + + f"material(s) {', '.join(materialsrequested)} created." + ) diff --git a/gprMax/cmds_geometry/sphere.py b/gprMax/cmds_geometry/sphere.py index 2679aaaa..796340e9 100644 --- a/gprMax/cmds_geometry/sphere.py +++ b/gprMax/cmds_geometry/sphere.py @@ -33,7 +33,7 @@ class Sphere(UserObjectGeometry): Attributes: p1: list of the coordinates (x,y,z) of the centre of the sphere. r: float of radius of the sphere. - material_id: string for the material identifier that must correspond + material_id: string for the material identifier that must correspond to material that has already been defined. material_ids: list of material identifiers in the x, y, z directions. averaging: string (y or n) used to switch on and off dielectric smoothing. @@ -41,20 +41,20 @@ class Sphere(UserObjectGeometry): def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#sphere' + self.hash = "#sphere" def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - r = self.kwargs['r'] + p1 = self.kwargs["p1"] + r = self.kwargs["r"] except KeyError: - logger.exception(f'{self.__str__()} please specify a point and a radius.') + logger.exception(f"{self.__str__()} please specify a point and a radius.") raise # Check averaging try: # Try user-specified averaging - averagesphere = self.kwargs['averaging'] + averagesphere = self.kwargs["averaging"] except KeyError: # Otherwise go with the grid default averagesphere = grid.averagevolumeobjects @@ -62,25 +62,25 @@ class Sphere(UserObjectGeometry): # Check materials have been specified # Isotropic case try: - materialsrequested = [self.kwargs['material_id']] + materialsrequested = [self.kwargs["material_id"]] except KeyError: # Anisotropic case try: - materialsrequested = self.kwargs['material_ids'] + materialsrequested = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.__str__()} no materials have been specified') + logger.exception(f"{self.__str__()} no materials have been specified") raise - + # Centre of sphere p2 = uip.round_to_grid_static_point(p1) xc, yc, zc = uip.discretise_point(p1) - + # Look up requested materials in existing list of material instances materials = [y for x in materialsrequested for y in grid.materials if y.ID == x] if len(materials) != len(materialsrequested): notfound = [x for x in materialsrequested if x not in materials] - logger.exception(f'{self.__str__()} material(s) {notfound} do not exist') + logger.exception(f"{self.__str__()} material(s) {notfound} do not exist") raise ValueError # Isotropic case @@ -94,33 +94,46 @@ class Sphere(UserObjectGeometry): numIDx = materials[0].numID numIDy = materials[1].numID numIDz = materials[2].numID - requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID + requiredID = materials[0].ID + "+" + materials[1].ID + "+" + materials[2].ID averagedmaterial = [x for x in grid.materials if x.ID == requiredID] if averagedmaterial: numID = averagedmaterial.numID else: numID = len(grid.materials) m = Material(numID, requiredID) - m.type = 'dielectric-smoothed' + m.type = "dielectric-smoothed" # Create dielectric-smoothed constituents for material - m.er = np.mean((materials[0].er, materials[1].er, - materials[2].er), axis=0) - m.se = np.mean((materials[0].se, materials[1].se, - materials[2].se), axis=0) - m.mr = np.mean((materials[0].mr, materials[1].mr, - materials[2].mr), axis=0) - m.sm = np.mean((materials[0].sm, materials[1].sm, - materials[2].sm), axis=0) + m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) + m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0) + m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0) + m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0) # Append the new material object to the materials list grid.materials.append(m) - build_sphere(xc, yc, zc, r, grid.dx, grid.dy, grid.dz, numID, - numIDx, numIDy, numIDz, averaging, grid.solid, - grid.rigidE, grid.rigidH, grid.ID) + build_sphere( + xc, + yc, + zc, + r, + grid.dx, + grid.dy, + grid.dz, + numID, + numIDx, + numIDy, + numIDz, + averaging, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) - dielectricsmoothing = 'on' if averaging else 'off' - logger.info(f"{self.grid_name(grid)}Sphere with centre {p2[0]:g}m, " + - f"{p2[1]:g}m, {p2[2]:g}m, radius {r:g}m, of material(s) " + - f"{', '.join(materialsrequested)} created, dielectric " + - f"smoothing is {dielectricsmoothing}.") + dielectricsmoothing = "on" if averaging else "off" + logger.info( + f"{self.grid_name(grid)}Sphere with centre {p2[0]:g}m, " + + f"{p2[1]:g}m, {p2[2]:g}m, radius {r:g}m, of material(s) " + + f"{', '.join(materialsrequested)} created, dielectric " + + f"smoothing is {dielectricsmoothing}." + ) diff --git a/gprMax/cmds_geometry/triangle.py b/gprMax/cmds_geometry/triangle.py index 77a334db..de6235f3 100644 --- a/gprMax/cmds_geometry/triangle.py +++ b/gprMax/cmds_geometry/triangle.py @@ -28,16 +28,16 @@ logger = logging.getLogger(__name__) class Triangle(UserObjectGeometry): - """Introduces a triangular patch or a triangular prism with specific + """Introduces a triangular patch or a triangular prism with specific properties into the model. Attributes: p1: list of the coordinates (x,y,z) of the first apex of the triangle. p2: list of the coordinates (x,y,z) of the second apex of the triangle. p3: list of the coordinates (x,y,z) of the third apex of the triangle. - thickness: float for the thickness of the triangular prism. If the + thickness: float for the thickness of the triangular prism. If the thickness is zero then a triangular patch is created. - material_id: string for the material identifier that must correspond + material_id: string for the material identifier that must correspond to material that has already been defined. material_ids: list of material identifiers in the x, y, z directions. averaging: string (y or n) used to switch on and off dielectric smoothing. @@ -45,7 +45,7 @@ class Triangle(UserObjectGeometry): def __init__(self, **kwargs): super().__init__(**kwargs) - self.hash = '#triangle' + self.hash = "#triangle" def rotate(self, axis, angle, origin=None): """Sets parameters for rotation.""" @@ -56,30 +56,30 @@ class Triangle(UserObjectGeometry): def _do_rotate(self): """Performs rotation.""" - p1 = rotate_point(self.kwargs['p1'], self.axis, self.angle, self.origin) - p2 = rotate_point(self.kwargs['p2'], self.axis, self.angle, self.origin) - p3 = rotate_point(self.kwargs['p3'], self.axis, self.angle, self.origin) - self.kwargs['p1'] = tuple(p1) - self.kwargs['p2'] = tuple(p2) - self.kwargs['p3'] = tuple(p3) + p1 = rotate_point(self.kwargs["p1"], self.axis, self.angle, self.origin) + p2 = rotate_point(self.kwargs["p2"], self.axis, self.angle, self.origin) + p3 = rotate_point(self.kwargs["p3"], self.axis, self.angle, self.origin) + self.kwargs["p1"] = tuple(p1) + self.kwargs["p2"] = tuple(p2) + self.kwargs["p3"] = tuple(p3) def create(self, grid, uip): try: - up1 = self.kwargs['p1'] - up2 = self.kwargs['p2'] - up3 = self.kwargs['p3'] - thickness = self.kwargs['thickness'] + up1 = self.kwargs["p1"] + up2 = self.kwargs["p2"] + up3 = self.kwargs["p3"] + thickness = self.kwargs["thickness"] except KeyError: - logger.exception(f'{self.__str__()} specify 3 points and a thickness') + logger.exception(f"{self.__str__()} specify 3 points and a thickness") raise - + if self.do_rotate: self._do_rotate() # Check averaging try: # Try user-specified averaging - averagetriangularprism = self.kwargs['averaging'] + averagetriangularprism = self.kwargs["averaging"] except KeyError: # Otherwise go with the grid default averagetriangularprism = grid.averagevolumeobjects @@ -87,13 +87,13 @@ class Triangle(UserObjectGeometry): # Check materials have been specified # Isotropic case try: - materialsrequested = [self.kwargs['material_id']] + materialsrequested = [self.kwargs["material_id"]] except KeyError: # Anisotropic case try: - materialsrequested = self.kwargs['material_ids'] + materialsrequested = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.__str__()} no materials have been specified') + logger.exception(f"{self.__str__()} no materials have been specified") raise p4 = uip.round_to_grid_static_point(up1) @@ -106,23 +106,23 @@ class Triangle(UserObjectGeometry): x1, y1, z1 = uip.round_to_grid(up1) x2, y2, z2 = uip.round_to_grid(up2) x3, y3, z3 = uip.round_to_grid(up3) - + if thickness < 0: - logger.exception(f'{self.__str__()} requires a positive value for thickness') + logger.exception(f"{self.__str__()} requires a positive value for thickness") raise ValueError # Check for valid orientations # yz-plane triangle if x1 == x2 == x3: - normal = 'x' + normal = "x" # xz-plane triangle elif y1 == y2 == y3: - normal = 'y' + normal = "y" # xy-plane triangle elif z1 == z2 == z3: - normal = 'z' + normal = "z" else: - logger.exception(f'{self.__str__()} the triangle is not specified correctly') + logger.exception(f"{self.__str__()} the triangle is not specified correctly") raise ValueError # Look up requested materials in existing list of material instances @@ -130,7 +130,7 @@ class Triangle(UserObjectGeometry): if len(materials) != len(materialsrequested): notfound = [x for x in materialsrequested if x not in materials] - logger.exception(f'{self.__str__()} material(s) {notfound} do not exist') + logger.exception(f"{self.__str__()} material(s) {notfound} do not exist") raise ValueError if thickness > 0: @@ -145,23 +145,19 @@ class Triangle(UserObjectGeometry): numIDx = materials[0].numID numIDy = materials[1].numID numIDz = materials[2].numID - requiredID = materials[0].ID + '+' + materials[1].ID + '+' + materials[2].ID + requiredID = materials[0].ID + "+" + materials[1].ID + "+" + materials[2].ID averagedmaterial = [x for x in grid.materials if x.ID == requiredID] if averagedmaterial: numID = averagedmaterial.numID else: numID = len(grid.materials) m = Material(numID, requiredID) - m.type = 'dielectric-smoothed' + m.type = "dielectric-smoothed" # Create dielectric-smoothed constituents for material - m.er = np.mean((materials[0].er, materials[1].er, - materials[2].er), axis=0) - m.se = np.mean((materials[0].se, materials[1].se, - materials[2].se), axis=0) - m.mr = np.mean((materials[0].mr, materials[1].mr, - materials[2].mr), axis=0) - m.sm = np.mean((materials[0].sm, materials[1].sm, - materials[2].sm), axis=0) + m.er = np.mean((materials[0].er, materials[1].er, materials[2].er), axis=0) + m.se = np.mean((materials[0].se, materials[1].se, materials[2].se), axis=0) + m.mr = np.mean((materials[0].mr, materials[1].mr, materials[2].mr), axis=0) + m.sm = np.mean((materials[0].sm, materials[1].sm, materials[2].sm), axis=0) # Append the new material object to the materials list grid.materials.append(m) @@ -179,19 +175,45 @@ class Triangle(UserObjectGeometry): numIDy = materials[1].numID numIDz = materials[2].numID - build_triangle(x1, y1, z1, x2, y2, z2, x3, y3, z3, normal, thickness, - grid.dx, grid.dy, grid.dz, numID, numIDx, numIDy, numIDz, - averaging, grid.solid, grid.rigidE, grid.rigidH, grid.ID) + build_triangle( + x1, + y1, + z1, + x2, + y2, + z2, + x3, + y3, + z3, + normal, + thickness, + grid.dx, + grid.dy, + grid.dz, + numID, + numIDx, + numIDy, + numIDz, + averaging, + grid.solid, + grid.rigidE, + grid.rigidH, + grid.ID, + ) if thickness > 0: - dielectricsmoothing = 'on' if averaging else 'off' - logger.info(f"{self.grid_name(grid)}Triangle with coordinates " + - f"{p4[0]:g}m {p4[1]:g}m {p4[2]:g}m, {p5[0]:g}m {p5[1]:g}m " + - f"{p5[2]:g}m, {p6[0]:g}m {p6[1]:g}m {p6[2]:g}m and thickness " + - f"{thickness:g}m of material(s) {', '.join(materialsrequested)} " + - f"created, dielectric smoothing is {dielectricsmoothing}.") + dielectricsmoothing = "on" if averaging else "off" + logger.info( + f"{self.grid_name(grid)}Triangle with coordinates " + + f"{p4[0]:g}m {p4[1]:g}m {p4[2]:g}m, {p5[0]:g}m {p5[1]:g}m " + + f"{p5[2]:g}m, {p6[0]:g}m {p6[1]:g}m {p6[2]:g}m and thickness " + + f"{thickness:g}m of material(s) {', '.join(materialsrequested)} " + + f"created, dielectric smoothing is {dielectricsmoothing}." + ) else: - logger.info(f"{self.grid_name(grid)}Triangle with coordinates " + - f"{p4[0]:g}m {p4[1]:g}m {p4[2]:g}m, {p5[0]:g}m {p5[1]:g}m " + - f"{p5[2]:g}m, {p6[0]:g}m {p6[1]:g}m {p6[2]:g}m of material(s) " + - f"{', '.join(materialsrequested)} created.") + logger.info( + f"{self.grid_name(grid)}Triangle with coordinates " + + f"{p4[0]:g}m {p4[1]:g}m {p4[2]:g}m, {p5[0]:g}m {p5[1]:g}m " + + f"{p5[2]:g}m, {p6[0]:g}m {p6[1]:g}m {p6[2]:g}m of material(s) " + + f"{', '.join(materialsrequested)} created." + ) diff --git a/gprMax/cmds_multiuse.py b/gprMax/cmds_multiuse.py index e7f68ee3..b5127cb6 100644 --- a/gprMax/cmds_multiuse.py +++ b/gprMax/cmds_multiuse.py @@ -24,9 +24,7 @@ from scipy import interpolate import gprMax.config as config -from .cmds_geometry.cmds_geometry import (UserObjectGeometry, - rotate_2point_object, - rotate_polarisation) +from .cmds_geometry.cmds_geometry import UserObjectGeometry, rotate_2point_object, rotate_polarisation from .geometry_outputs import GeometryObjects as GeometryObjectsUser from .materials import DispersiveMaterial as DispersiveMaterialUser from .materials import ListMaterial as ListMaterialUser @@ -59,13 +57,13 @@ class UserObjectMulti: def __str__(self): """Readable user string as per hash commands.""" - s = '' + s = "" for _, v in self.kwargs.items(): if isinstance(v, (tuple, list)): - v = ' '.join([str(el) for el in v]) - s += f'{str(v)} ' + v = " ".join([str(el) for el in v]) + s += f"{str(v)} " - return f'{self.hash}: {s[:-1]}' + return f"{self.hash}: {s[:-1]}" def create(self, grid, uip): """Creates object and adds it to grid.""" @@ -77,17 +75,17 @@ class UserObjectMulti: def params_str(self): """Readable string of parameters given to object.""" - return f'{self.hash}: {str(self.kwargs)}' + return f"{self.hash}: {str(self.kwargs)}" def grid_name(self, grid): """Returns subgrid name for use with logging info. Returns an empty - string if the grid is the main grid. + string if the grid is the main grid. """ if isinstance(grid, SubGridBaseGrid): - return f'[{grid.name}] ' + return f"[{grid.name}] " else: - return '' + return "" class Waveform(UserObjectMulti): @@ -98,7 +96,7 @@ class Waveform(UserObjectMulti): 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_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 @@ -108,36 +106,37 @@ class Waveform(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 1 - self.hash = '#waveform' + self.hash = "#waveform" def create(self, grid, uip): try: - wavetype = self.kwargs['wave_type'].lower() + wavetype = self.kwargs["wave_type"].lower() except KeyError: - logger.exception(f"{self.params_str()} must have one of the " + - f"following types {','.join(WaveformUser.types)}.") + logger.exception( + f"{self.params_str()} must have one of the " + f"following types {','.join(WaveformUser.types)}." + ) raise if wavetype not in WaveformUser.types: - logger.exception(f"{self.params_str()} must have one of the " + - f"following types {','.join(WaveformUser.types)}.") + logger.exception( + f"{self.params_str()} must have one of the " + f"following types {','.join(WaveformUser.types)}." + ) raise ValueError - if wavetype != 'user': + if wavetype != "user": try: - amp = self.kwargs['amp'] - freq = self.kwargs['freq'] - ID = self.kwargs['id'] + 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.')) + 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.')) + 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" with ID {ID} already " "exists.")) raise ValueError w = WaveformUser() @@ -146,36 +145,38 @@ class Waveform(UserObjectMulti): 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.')) + 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'] + uservalues = self.kwargs["user_values"] + ID = self.kwargs["id"] fullargspec = inspect.getfullargspec(interpolate.interp1d) - kwargs = dict(zip(reversed(fullargspec.args), - reversed(fullargspec.defaults))) + kwargs = dict(zip(reversed(fullargspec.args), reversed(fullargspec.defaults))) except KeyError: - logger.exception(self.params_str() + (' a user-defined ' - 'waveform requires at least two parameters.')) + 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'] + if "user_time" in self.kwargs: + waveformtime = self.kwargs["user_time"] else: waveformtime = np.arange(0, grid.timewindow + grid.dt, grid.dt) # Set args for interpolation if given by user - if 'kind' in self.kwargs: - kwargs['kind'] = self.kwargs['kind'] - if 'fill_value' in self.kwargs: - kwargs['fill_value'] = self.kwargs['fill_value'] + if "kind" in self.kwargs: + kwargs["kind"] = self.kwargs["kind"] + if "fill_value" in self.kwargs: + kwargs["fill_value"] = self.kwargs["fill_value"] 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" with ID {ID} already " "exists.")) raise ValueError w = WaveformUser() @@ -183,8 +184,7 @@ class Waveform(UserObjectMulti): 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.')) + logger.info(self.grid_name(grid) + (f"Waveform {w.ID} that is " "user-defined created.")) grid.waveforms.append(w) @@ -195,7 +195,7 @@ class VoltageSource(UserObjectMulti): 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 + 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. @@ -205,7 +205,7 @@ class VoltageSource(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 2 - self.hash = '#voltage_source' + self.hash = "#voltage_source" def rotate(self, axis, angle, origin=None): """Sets parameters for rotation.""" @@ -216,61 +216,58 @@ class VoltageSource(UserObjectMulti): def _do_rotate(self, grid): """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) - self.kwargs['p1'] = tuple(rot_pts[0, :]) + 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) + self.kwargs["p1"] = tuple(rot_pts[0, :]) def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - polarisation = self.kwargs['polarisation'].lower() - resistance = self.kwargs['resistance'] - waveform_id = self.kwargs['waveform_id'] + p1 = self.kwargs["p1"] + polarisation = self.kwargs["polarisation"].lower() + 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.do_rotate: self._do_rotate(grid) # Check polarity & position parameters - if polarisation not in ('x', 'y', 'z'): - logger.exception(self.params_str() + (' polarisation must be ' - 'x, y, or z.')) + if polarisation not in ("x", "y", "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 in ['y','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 in [ + "y", + "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 in ['x','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 in [ + "x", + "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 in ['x','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 in [ + "x", + "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() + (' there is no waveform with ' - 'the identifier ' - f'{waveform_id}.')) + logger.exception(self.params_str() + (" there is no waveform with " "the identifier " f"{waveform_id}.")) raise ValueError v = VoltageSourceUser() @@ -278,45 +275,40 @@ 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 try: - start = self.kwargs['start'] - stop = self.kwargs['stop'] + start = self.kwargs["start"] + 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 v.stop = min(stop, grid.timewindow) - startstop = (f' start time {v.start:g} secs, finish time ' - f'{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 - startstop = ' ' + startstop = " " v.calculate_waveform_values(grid) - logger.info(f'{self.grid_name(grid)}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.') + logger.info( + f"{self.grid_name(grid)}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) @@ -337,7 +329,7 @@ class HertzianDipole(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 3 - self.hash = '#hertzian_dipole' + self.hash = "#hertzian_dipole" def rotate(self, axis, angle, origin=None): """Sets parameters for rotation.""" @@ -348,63 +340,64 @@ class HertzianDipole(UserObjectMulti): def _do_rotate(self, grid): """Performs rotation.""" - rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], - self.kwargs['polarisation'], - self.axis, - self.angle, - grid) + 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) - self.kwargs['p1'] = tuple(rot_pts[0, :]) + self.kwargs["p1"] = tuple(rot_pts[0, :]) def create(self, grid, uip): try: - polarisation = self.kwargs['polarisation'].lower() - p1 = self.kwargs['p1'] - waveform_id = self.kwargs['waveform_id'] + polarisation = self.kwargs["polarisation"].lower() + p1 = self.kwargs["p1"] + waveform_id = self.kwargs["waveform_id"] except KeyError: - logger.exception(f'{self.params_str()} requires at least 3 parameters.') + logger.exception(f"{self.params_str()} requires at least 3 parameters.") raise if self.do_rotate: self._do_rotate(grid) # Check polarity & position parameters - if polarisation not in ('x', 'y', 'z'): - logger.exception(self.params_str() + (' polarisation must be ' - 'x, y, or z.')) + if polarisation not in ("x", "y", "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 in ['y','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 in [ + "y", + "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 in ['x','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 in [ + "x", + "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 in ['x','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 in [ + "x", + "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) - # Check if there is a waveformID in the waveforms list if not any(x.ID == waveform_id for x in grid.waveforms): - logger.exception(f'{self.params_str()} there is no waveform ' + - f'with the identifier {waveform_id}.') + logger.exception(f"{self.params_str()} there is no waveform " + f"with the identifier {waveform_id}.") raise ValueError h = HertzianDipoleUser() h.polarisation = polarisation # Set length of dipole to grid size in polarisation direction - if h.polarisation == 'x': + if h.polarisation == "x": h.dl = grid.dx - elif h.polarisation == 'y': + elif h.polarisation == "y": h.dl = grid.dy - elif h.polarisation == 'z': + elif h.polarisation == "z": h.dl = grid.dz h.xcoord = xcoord @@ -413,53 +406,57 @@ class HertzianDipole(UserObjectMulti): h.xcoordorigin = xcoord h.ycoordorigin = ycoord h.zcoordorigin = zcoord - h.ID = f'{h.__class__.__name__}({str(h.xcoord)},{str(h.ycoord)},{str(h.zcoord)})' + h.ID = f"{h.__class__.__name__}({str(h.xcoord)},{str(h.ycoord)},{str(h.zcoord)})" h.waveformID = waveform_id try: # Check source start & source remove time parameters - start = self.kwargs['start'] - stop = self.kwargs['stop'] + start = self.kwargs["start"] + stop = self.kwargs["stop"] if start < 0: - logger.exception(f'{self.params_str()} delay of the initiation of the ' + - f'source should not be less than zero.') + logger.exception( + f"{self.params_str()} delay of the initiation of the " + f"source should not be less than zero." + ) raise ValueError if stop < 0: - logger.exception(f'{self.params_str()} time to remove the source ' + - f'should not be less than zero.') + logger.exception(f"{self.params_str()} time to remove the source " + f"should not be less than zero.") raise ValueError if stop - start <= 0: - logger.exception( - f'{self.params_str()} duration of the source should not be zero or less.') + logger.exception(f"{self.params_str()} duration of the source should not be zero or less.") raise ValueError h.start = start h.stop = min(stop, grid.timewindow) - startstop = (f' start time {h.start:g} secs, finish time ' - f'{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 - startstop = ' ' + startstop = " " h.calculate_waveform_values(grid) - if config.get_model_config().mode == '2D': - logger.info(f'{self.grid_name(grid)}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.') + if config.get_model_config().mode == "2D": + logger.info( + f"{self.grid_name(grid)}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(f'{self.grid_name(grid)}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.') + logger.info( + f"{self.grid_name(grid)}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): - """Simulates an infinitesimal magnetic dipole. - + """Simulates an infinitesimal magnetic dipole. + Often referred to as an additive or soft source. Attributes: @@ -473,7 +470,7 @@ class MagneticDipole(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 4 - self.hash = '#magnetic_dipole' + self.hash = "#magnetic_dipole" def rotate(self, axis, angle, origin=None): """Sets parameters for rotation.""" @@ -484,52 +481,53 @@ class MagneticDipole(UserObjectMulti): def _do_rotate(self, grid): """Performs rotation.""" - rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], - self.kwargs['polarisation'], - self.axis, - self.angle, - grid) + 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) - self.kwargs['p1'] = tuple(rot_pts[0, :]) + self.kwargs["p1"] = tuple(rot_pts[0, :]) def create(self, grid, uip): try: - polarisation = self.kwargs['polarisation'].lower() - p1 = self.kwargs['p1'] - waveform_id = self.kwargs['waveform_id'] + polarisation = self.kwargs["polarisation"].lower() + p1 = self.kwargs["p1"] + waveform_id = self.kwargs["waveform_id"] except KeyError: - logger.exception(f'{self.params_str()} requires at least five parameters.') + logger.exception(f"{self.params_str()} requires at least five parameters.") raise if self.do_rotate: self._do_rotate(grid) # Check polarity & position parameters - if polarisation not in ('x', 'y', 'z'): - logger.exception(self.params_str() + (' polarisation must be ' - 'x, y, or z.')) + if polarisation not in ("x", "y", "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 in ['y','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 in [ + "y", + "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 in ['x','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 in [ + "x", + "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 in ['x','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 in [ + "x", + "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) - # Check if there is a waveformID in the waveforms list if not any(x.ID == waveform_id for x in grid.waveforms): - logger.exception(f'{self.params_str()} there is no waveform ' + - f'with the identifier {waveform_id}.') + logger.exception(f"{self.params_str()} there is no waveform " + f"with the identifier {waveform_id}.") raise ValueError m = MagneticDipoleUser() @@ -540,55 +538,52 @@ 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: # Check source start & source remove time parameters - start = self.kwargs['start'] - stop = self.kwargs['stop'] + 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 m.stop = min(stop, grid.timewindow) - startstop = (f' start time {m.start:g} secs, ' - f'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 - startstop = ' ' + startstop = " " m.calculate_waveform_values(grid) - logger.info(f'{self.grid_name(grid)}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.') + logger.info( + f"{self.grid_name(grid)}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): - """Specifies a one-dimensional transmission line model at an electric + """Specifies a one-dimensional transmission line model at an electric field location. 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 + 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. @@ -598,7 +593,7 @@ class TransmissionLine(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 5 - self.hash = '#transmission_line' + self.hash = "#transmission_line" def rotate(self, axis, angle, origin=None): """Sets parameters for rotation.""" @@ -609,66 +604,71 @@ class TransmissionLine(UserObjectMulti): def _do_rotate(self, grid): """Performs rotation.""" - rot_pol_pts, self.kwargs['polarisation'] = rotate_polarisation(self.kwargs['p1'], - self.kwargs['polarisation'], - self.axis, - self.angle, - grid) + 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) - self.kwargs['p1'] = tuple(rot_pts[0, :]) + self.kwargs["p1"] = tuple(rot_pts[0, :]) def create(self, grid, uip): try: - polarisation = self.kwargs['polarisation'].lower() - p1 = self.kwargs['p1'] - waveform_id = self.kwargs['waveform_id'] - resistance = self.kwargs['resistance'] + polarisation = self.kwargs["polarisation"].lower() + p1 = self.kwargs["p1"] + waveform_id = self.kwargs["waveform_id"] + resistance = self.kwargs["resistance"] except KeyError: - logger.exception(f'{self.params_str()} requires at least six parameters.') + logger.exception(f"{self.params_str()} requires at least six parameters.") raise if self.do_rotate: self._do_rotate(grid) # Warn about using a transmission line on GPU - if (config.sim_config.general['solver'] in ['cuda', 'opencl']): - logger.exception(f'{self.params_str()} cannot currently be used ' + - 'with the CUDA or OpenCL-based solver. Consider ' + - 'using a #voltage_source instead.') + if config.sim_config.general["solver"] in ["cuda", "opencl"]: + logger.exception( + f"{self.params_str()} cannot currently be used " + + "with the CUDA or OpenCL-based solver. 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.')) + if polarisation not in ("x", "y", "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 in ['y','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 in [ + "y", + "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 in ['x','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 in [ + "x", + "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 in ['x','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 in [ + "x", + "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 or resistance >= config.sim_config.em_consts['z0']: - logger.exception(f'{self.params_str()} requires a resistance ' + - 'greater than zero and less than the impedance ' + - 'of free space, i.e. 376.73 Ohms.') + if resistance <= 0 or resistance >= config.sim_config.em_consts["z0"]: + logger.exception( + f"{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(f'{self.params_str()} there is no waveform ' + - f'with the identifier {waveform_id}.') + logger.exception(f"{self.params_str()} there is no waveform " + f"with the identifier {waveform_id}.") raise ValueError t = TransmissionLineUser(grid) @@ -676,54 +676,51 @@ 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 try: # Check source start & source remove time parameters - start = self.kwargs['start'] - stop = self.kwargs['stop'] + 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 t.stop = min(stop, grid.timewindow) - startstop = (f' start time {t.start:g} secs, finish time ' + - f'{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 - startstop = ' ' + startstop = " " t.calculate_waveform_values(grid) t.calculate_incident_V_I(grid) - logger.info(f'{self.grid_name(grid)}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.') + logger.info( + f"{self.grid_name(grid)}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): - """Specifies output points in the model. - - These are locations where the values of the electric and magnetic field + """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. Attributes: @@ -736,7 +733,7 @@ class Rx(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 6 - self.hash = '#rx' + self.hash = "#rx" self.constructor = RxUser def rotate(self, axis, angle, origin=None): @@ -748,25 +745,24 @@ class Rx(UserObjectMulti): def _do_rotate(self, G): """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]) + 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, :]) + self.kwargs["p1"] = tuple(rot_pts[0, :]) # If specific field components are specified, set to output all components try: - self.kwargs['id'] - self.kwargs['outputs'] + self.kwargs["id"] + self.kwargs["outputs"] rxargs = dict(self.kwargs) - del rxargs['outputs'] + del rxargs["outputs"] self.kwargs = rxargs except KeyError: pass def create(self, grid, uip): try: - p1 = self.kwargs['p1'] + p1 = self.kwargs["p1"] except KeyError: logger.exception(self.params_str()) raise @@ -782,36 +778,38 @@ class Rx(UserObjectMulti): r.xcoordorigin, r.ycoordorigin, r.zcoordorigin = p try: - r.ID = self.kwargs['id'] - outputs = [self.kwargs['outputs']] + r.ID = self.kwargs["id"] + outputs = [self.kwargs["outputs"]] except KeyError: # If no ID or outputs are specified, use default - r.ID = f'{r.__class__.__name__}({str(r.xcoord)},{str(r.ycoord)},{str(r.zcoord)})' + r.ID = f"{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 - if config.sim_config.general['solver'] in ['cuda', 'opencl']: + if config.sim_config.general["solver"] in ["cuda", "opencl"]: allowableoutputs = RxUser.allowableoutputs_dev else: allowableoutputs = RxUser.allowableoutputs # 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(f'{self.params_str()} contains an output ' - f'type that is not allowable. Allowable ' - f'outputs in current context are ' - f'{allowableoutputs}.') + logger.exception( + f"{self.params_str()} contains an output " + f"type that is not allowable. Allowable " + f"outputs in current context are " + f"{allowableoutputs}." + ) raise ValueError - logger.info(f"{self.grid_name(grid)}Receiver at {p2[0]:g}m, {p2[1]:g}m, " - f"{p2[2]:g}m with output component(s) " - f"{', '.join(r.outputs)} created.") + logger.info( + f"{self.grid_name(grid)}Receiver at {p2[0]:g}m, {p2[1]:g}m, " + f"{p2[2]:g}m with output component(s) " + f"{', '.join(r.outputs)} created." + ) grid.rxs.append(r) @@ -830,57 +828,62 @@ class RxArray(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 7 - self.hash = '#rx_array' + self.hash = "#rx_array" def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - dl = self.kwargs['dl'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + dl = self.kwargs["dl"] except KeyError: - logger.exception(f'{self.params_str()} requires exactly 9 parameters') + logger.exception(f"{self.params_str()} requires exactly 9 parameters") raise - xs, ys, zs = uip.check_src_rx_point(p1, self.params_str(), 'lower') - xf, yf, zf = uip.check_src_rx_point(p2, self.params_str(), 'upper') + xs, ys, zs = uip.check_src_rx_point(p1, self.params_str(), "lower") + xf, yf, zf = uip.check_src_rx_point(p2, self.params_str(), "upper") p3 = uip.round_to_grid_static_point(p1) p4 = uip.round_to_grid_static_point(p2) dx, dy, dz = uip.discretise_point(dl) if xs > xf or ys > yf or zs > zf: - logger.exception(f'{self.params_str()} the lower coordinates ' + - 'should be less than the upper coordinates.') + logger.exception( + f"{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(f'{self.params_str()} the step size should not ' + - 'be less than zero.') + logger.exception(f"{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(f'{self.params_str()} the step size should ' + - 'not be less than the spatial discretisation.') + logger.exception( + f"{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(f'{self.params_str()} the step size should ' + - 'not be less than the spatial discretisation.') + logger.exception( + f"{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(f'{self.params_str()} the step size should ' + - 'not be less than the spatial discretisation.') + logger.exception( + f"{self.params_str()} the step size should " + "not be less than the spatial discretisation." + ) raise ValueError - logger.info(f'{self.grid_name(grid)}Receiver array ' - f'{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 with steps ' - f'{dx * grid.dx:g}m, {dy * grid.dy:g}m, {dz * grid.dz:g}m') + logger.info( + f"{self.grid_name(grid)}Receiver array " + f"{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 with steps " + f"{dx * grid.dx:g}m, {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): @@ -896,31 +899,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 = f'{r.__class__.__name__}({str(x)},{str(y)},{str(z)})' + r.ID = f"{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, " - f"{p5[2]:g}m with output component(s) " - f"{', '.join(r.outputs)} created.") + 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, " + f"{p5[2]:g}m with output component(s) " + f"{', '.join(r.outputs)} created." + ) grid.rxs.append(r) class Snapshot(UserObjectMulti): - """Obtains information about the electromagnetic fields within a volume + """Obtains information about the electromagnetic fields within a volume of the model at a given time instant. Attributes: - p1: tuple required to specify lower left (x,y,z) coordinates of volume + 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 + 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 + 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 + 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 + fileext: optional string to indicate type for snapshot file, either '.vti' (default) or '.h5' outputs: optional list of outputs for receiver. It can be any selection from Ex, Ey, Ez, Hx, Hy, or Hz. @@ -929,19 +934,19 @@ class Snapshot(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 8 - self.hash = '#snapshot' + self.hash = "#snapshot" def create(self, grid, uip): if isinstance(grid, SubGridBaseGrid): - logger.exception(f'{self.params_str()} do not add snapshots to subgrids.') + logger.exception(f"{self.params_str()} do not add snapshots to subgrids.") raise ValueError try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - dl = self.kwargs['dl'] - filename = self.kwargs['filename'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + dl = self.kwargs["dl"] + filename = self.kwargs["filename"] except KeyError: - logger.exception(f'{self.params_str()} requires exactly 11 parameters.') + logger.exception(f"{self.params_str()} requires exactly 11 parameters.") raise try: @@ -949,7 +954,7 @@ class Snapshot(UserObjectMulti): p4 = uip.round_to_grid_static_point(p2) p1, p2 = uip.check_box_points(p1, p2, self.params_str()) except ValueError: - logger.exception(f'{self.params_str()} point is outside the domain.') + logger.exception(f"{self.params_str()} point is outside the domain.") raise xs, ys, zs = p1 xf, yf, zf = p2 @@ -958,41 +963,44 @@ class Snapshot(UserObjectMulti): # If number of iterations given try: - iterations = self.kwargs['iterations'] + iterations = self.kwargs["iterations"] # If real floating point value given except KeyError: try: - time = self.kwargs['time'] + time = self.kwargs["time"] except KeyError: - logger.exception(f'{self.params_str()} requires exactly 5 parameters.') + logger.exception(f"{self.params_str()} requires exactly 5 parameters.") raise if time > 0: iterations = round_value((time / grid.dt)) + 1 else: - logger.exception(f'{self.params_str()} time value must be ' + - 'greater than zero.') + logger.exception(f"{self.params_str()} time value must be " + "greater than zero.") raise ValueError try: - fileext = self.kwargs['fileext'] + 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: " - f"{' '.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] try: - tmp = self.kwargs['outputs'] + tmp = self.kwargs["outputs"] outputs = dict.fromkeys(SnapshotUser.allowableoutputs, False) # Check and set output names for output in tmp: if output not in SnapshotUser.allowableoutputs.keys(): - logger.exception(f"{self.params_str()} contains an output " - f"type that is not allowable. Allowable " - f"outputs in current context are " - f"{', '.join(SnapshotUser.allowableoutputs.keys())}.") + logger.exception( + f"{self.params_str()} contains an output " + f"type that is not allowable. Allowable " + f"outputs in current context are " + f"{', '.join(SnapshotUser.allowableoutputs.keys())}." + ) raise ValueError else: outputs[output] = True @@ -1001,32 +1009,33 @@ class Snapshot(UserObjectMulti): outputs = dict.fromkeys(SnapshotUser.allowableoutputs, True) if dx < 0 or dy < 0 or dz < 0: - logger.exception(f'{self.params_str()} the step size should not ' + - 'be less than zero.') + logger.exception(f"{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(f'{self.params_str()} the step size should not ' + - 'be less than the spatial discretisation.') + logger.exception( + f"{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(f'{self.params_str()} time value is not valid.') + logger.exception(f"{self.params_str()} time value is not valid.") raise ValueError - s = SnapshotUser(xs, ys, zs, xf, yf, zf, dx, dy, dz, iterations, - filename, fileext=fileext, outputs=outputs) + s = SnapshotUser(xs, ys, zs, xf, yf, zf, dx, dy, dz, iterations, filename, fileext=fileext, outputs=outputs) - 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 field outputs " - f"{', '.join([k for k, v in outputs.items() if v])} and " - f"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 field outputs " + f"{', '.join([k for k, v in outputs.items() if v])} and " + f"filename {s.filename}{s.fileext} will be created." + ) grid.snapshots.append(s) class Material(UserObjectMulti): - """Specifies a material in the model described by a set of constitutive + """Specifies a material in the model described by a set of constitutive parameters. Attributes: @@ -1040,45 +1049,44 @@ class Material(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 9 - self.hash = '#material' + self.hash = "#material" def create(self, grid, uip): try: - er = self.kwargs['er'] - se = self.kwargs['se'] - mr = self.kwargs['mr'] - sm = self.kwargs['sm'] - material_id = self.kwargs['id'] + er = self.kwargs["er"] + se = self.kwargs["se"] + mr = self.kwargs["mr"] + sm = self.kwargs["sm"] + material_id = self.kwargs["id"] except KeyError: - logger.exception(f'{self.params_str()} requires exactly five parameters.') + logger.exception(f"{self.params_str()} requires exactly five parameters.") raise if er < 1: - logger.exception(f'{self.params_str()} requires a positive value of ' + - f'one or greater for static (DC) permittivity.') + logger.exception( + f"{self.params_str()} requires a positive value of " + f"one or greater for static (DC) permittivity." + ) raise ValueError - if se != 'inf': + if se != "inf": se = float(se) if se < 0: - logger.exception(f'{self.params_str()} requires a positive ' + - f'value for electric conductivity.') + logger.exception(f"{self.params_str()} requires a positive " + f"value for electric conductivity.") raise ValueError else: - se = float('inf') + se = float("inf") if mr < 1: - logger.exception(f'{self.params_str()} requires a positive value of ' + - f'one or greater for magnetic permeability.') + logger.exception( + f"{self.params_str()} requires a positive value of " + f"one or greater for magnetic permeability." + ) raise ValueError if sm < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - f'for magnetic loss.') + logger.exception(f"{self.params_str()} requires a positive value " + f"for magnetic loss.") raise ValueError if any(x.ID == material_id for x in grid.materials): - logger.exception(f'{self.params_str()} with ID {material_id} ' + - f'already exists') + logger.exception(f"{self.params_str()} with ID {material_id} " + f"already exists") raise ValueError - # Create a new instance of the Material class material + # Create a new instance of the Material class material # (start index after pec & free_space) m = MaterialUser(len(grid.materials), material_id) m.se = se @@ -1086,13 +1094,15 @@ class Material(UserObjectMulti): m.sm = sm # Set material averaging to False if infinite conductivity, i.e. pec - if m.se == float('inf'): + if m.se == float("inf"): m.averagable = False m.er = er - logger.info(f'{self.grid_name(grid)}Material {m.ID} with eps_r={m.er:g}, ' - f'sigma={m.se:g} S/m; mu_r={m.mr:g}, sigma*={m.sm:g} Ohm/m ' - f'created.') + logger.info( + f"{self.grid_name(grid)}Material {m.ID} with eps_r={m.er:g}, " + f"sigma={m.se:g} S/m; mu_r={m.mr:g}, sigma*={m.sm:g} Ohm/m " + f"created." + ) grid.materials.append(m) @@ -1103,32 +1113,31 @@ class AddDebyeDispersion(UserObjectMulti): Attributes: 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 + 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 + material_ids: list required of material ids to apply disperive properties. """ def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 10 - self.hash = '#add_dispersion_debye' + self.hash = "#add_dispersion_debye" def create(self, grid, uip): try: - poles = self.kwargs['poles'] - er_delta = self.kwargs['er_delta'] - tau = self.kwargs['tau'] - material_ids = self.kwargs['material_ids'] + poles = self.kwargs["poles"] + er_delta = self.kwargs["er_delta"] + tau = self.kwargs["tau"] + material_ids = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.params_str()} requires at least four parameters.') + logger.exception(f"{self.params_str()} requires at least four parameters.") raise if poles < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for number of poles.') + logger.exception(f"{self.params_str()} requires a positive value " + "for number of poles.") raise ValueError # Look up requested materials in existing list of material instances @@ -1136,7 +1145,7 @@ class AddDebyeDispersion(UserObjectMulti): if len(materials) != len(material_ids): notfound = [x for x in material_ids if x not in materials] - logger.exception(f'{self.params_str()} material(s) {notfound} do not exist') + logger.exception(f"{self.params_str()} material(s) {notfound} do not exist") raise ValueError for material in materials: @@ -1145,28 +1154,30 @@ class AddDebyeDispersion(UserObjectMulti): disp_material.se = material.se disp_material.mr = material.mr disp_material.sm = material.sm - disp_material.type = 'debye' + disp_material.type = "debye" disp_material.poles = poles disp_material.averagable = False for i in range(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(f'{self.params_str()} requires positive ' + - 'values for the permittivity difference.') + logger.exception( + f"{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 + if disp_material.poles > config.get_model_config().materials["maxpoles"]: + config.get_model_config().materials["maxpoles"] = disp_material.poles # Replace original material with newly created DispersiveMaterial - grid.materials = [disp_material if mat.numID==material.numID else mat for mat in grid.materials] + grid.materials = [disp_material if mat.numID == material.numID else mat for mat in grid.materials] - logger.info(f"{self.grid_name(grid)}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.") + logger.info( + f"{self.grid_name(grid)}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): @@ -1175,34 +1186,33 @@ class AddLorentzDispersion(UserObjectMulti): Attributes: 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 + 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 + material_ids: list required of material ids to apply disperive properties. """ def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 11 - self.hash = '#add_dispersion_lorentz' + self.hash = "#add_dispersion_lorentz" def create(self, grid, uip): try: - poles = self.kwargs['poles'] - er_delta = self.kwargs['er_delta'] - omega = self.kwargs['omega'] - delta = self.kwargs['delta'] - material_ids = self.kwargs['material_ids'] + poles = self.kwargs["poles"] + er_delta = self.kwargs["er_delta"] + omega = self.kwargs["omega"] + delta = self.kwargs["delta"] + material_ids = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.params_str()} requires at least five parameters.') + logger.exception(f"{self.params_str()} requires at least five parameters.") raise if poles < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for number of poles.') + logger.exception(f"{self.params_str()} requires a positive value " + "for number of poles.") raise ValueError # Look up requested materials in existing list of material instances @@ -1210,7 +1220,7 @@ class AddLorentzDispersion(UserObjectMulti): if len(materials) != len(material_ids): notfound = [x for x in material_ids if x not in materials] - logger.exception(f'{self.params_str()} material(s) {notfound} do not exist') + logger.exception(f"{self.params_str()} material(s) {notfound} do not exist") raise ValueError for material in materials: @@ -1219,7 +1229,7 @@ class AddLorentzDispersion(UserObjectMulti): disp_material.se = material.se disp_material.mr = material.mr disp_material.sm = material.sm - disp_material.type = 'lorentz' + disp_material.type = "lorentz" disp_material.poles = poles disp_material.averagable = False for i in range(poles): @@ -1228,56 +1238,57 @@ class AddLorentzDispersion(UserObjectMulti): disp_material.tau.append(omega[i]) disp_material.alpha.append(delta[i]) else: - logger.exception(f'{self.params_str()} requires positive ' - 'values for the permittivity difference ' - 'and frequencies, and associated times ' - 'that are greater than the time step for ' - 'the model.') + logger.exception( + f"{self.params_str()} requires positive " + "values for the permittivity difference " + "and frequencies, and associated times " + "that are greater than the time step for " + "the model." + ) raise ValueError - if disp_material.poles > config.get_model_config().materials['maxpoles']: - config.get_model_config().materials['maxpoles'] = disp_material.poles + if disp_material.poles > config.get_model_config().materials["maxpoles"]: + config.get_model_config().materials["maxpoles"] = disp_material.poles # Replace original material with newly created DispersiveMaterial - grid.materials = [disp_material if mat.numID==material.numID else mat for mat in grid.materials] + grid.materials = [disp_material if mat.numID == material.numID else mat for mat in grid.materials] logger.info( - f"{self.grid_name(grid)}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' % omega for omega in disp_material.tau)} secs, " + - f"and gamma={', '.join('%4.3e' % delta for delta in disp_material.alpha)} created.") + f"{self.grid_name(grid)}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' % omega for omega in disp_material.tau)} secs, " + + f"and gamma={', '.join('%4.3e' % delta for delta in disp_material.alpha)} created." + ) class AddDrudeDispersion(UserObjectMulti): - """Adds dispersive properties to already defined Material based on a + """Adds dispersive properties to already defined Material based on a multi-pole Drude formulation. Attributes: poles: float required for number of Drude poles. - omega: tuple required for frequency (Hz) for each pole. + 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 + material_ids: list required of material ids to apply disperive properties. """ def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 12 - self.hash = '#add_dispersion_drude' + self.hash = "#add_dispersion_drude" def create(self, grid, uip): try: - poles = self.kwargs['poles'] - omega = self.kwargs['omega'] - alpha = self.kwargs['alpha'] - material_ids = self.kwargs['material_ids'] + poles = self.kwargs["poles"] + omega = self.kwargs["omega"] + alpha = self.kwargs["alpha"] + material_ids = self.kwargs["material_ids"] except KeyError: - logger.exception(f'{self.params_str()} requires at least four ' + - 'parameters.') + logger.exception(f"{self.params_str()} requires at least four " + "parameters.") raise if poles < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for number of poles.') + logger.exception(f"{self.params_str()} requires a positive value " + "for number of poles.") raise ValueError # Look up requested materials in existing list of material instances @@ -1285,7 +1296,7 @@ class AddDrudeDispersion(UserObjectMulti): if len(materials) != len(material_ids): notfound = [x for x in material_ids if x not in materials] - logger.exception(f'{self.params_str()} material(s) {notfound} do not exist.') + logger.exception(f"{self.params_str()} material(s) {notfound} do not exist.") raise ValueError for material in materials: @@ -1294,7 +1305,7 @@ class AddDrudeDispersion(UserObjectMulti): disp_material.se = material.se disp_material.mr = material.mr disp_material.sm = material.sm - disp_material.type = 'drude' + disp_material.type = "drude" disp_material.poles = poles disp_material.averagable = False for i in range(poles): @@ -1302,21 +1313,24 @@ class AddDrudeDispersion(UserObjectMulti): disp_material.tau.append(omega[i]) disp_material.alpha.append(alpha[i]) else: - logger.exception(f'{self.params_str()} requires positive ' + - 'values for the frequencies, and ' + - 'associated times that are greater than ' + - 'the time step for the model.') + logger.exception( + f"{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 + if disp_material.poles > config.get_model_config().materials["maxpoles"]: + config.get_model_config().materials["maxpoles"] = disp_material.poles # Replace original material with newly created DispersiveMaterial - grid.materials = [disp_material if mat.numID==material.numID else mat for mat in grid.materials] + grid.materials = [disp_material if mat.numID == material.numID else mat for mat in grid.materials] logger.info( f"{self.grid_name(grid)}Drude disperion added to {disp_material.ID} " f"with omega={', '.join('%4.3e' % omega for omega in disp_material.tau)} secs, " - f"and gamma={', '.join('%4.3e' % alpha for alpha in disp_material.alpha)} secs created.") + f"and gamma={', '.join('%4.3e' % alpha for alpha in disp_material.alpha)} secs created." + ) class SoilPeplinski(UserObjectMulti): @@ -1328,9 +1342,9 @@ class SoilPeplinski(UserObjectMulti): 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_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_upper: float required for upper boundary of volumetric water fraction of the soil. id: string used as identifier for soil. """ @@ -1338,153 +1352,160 @@ class SoilPeplinski(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 13 - self.hash = '#soil_peplinski' + self.hash = "#soil_peplinski" def create(self, grid, uip): try: - sand_fraction = self.kwargs['sand_fraction'] - clay_fraction = self.kwargs['clay_fraction'] - bulk_density = self.kwargs['bulk_density'] - sand_density = self.kwargs['sand_density'] - water_fraction_lower = self.kwargs['water_fraction_lower'] - water_fraction_upper = self.kwargs['water_fraction_upper'] - ID = self.kwargs['id'] + sand_fraction = self.kwargs["sand_fraction"] + clay_fraction = self.kwargs["clay_fraction"] + bulk_density = self.kwargs["bulk_density"] + sand_density = self.kwargs["sand_density"] + water_fraction_lower = self.kwargs["water_fraction_lower"] + water_fraction_upper = self.kwargs["water_fraction_upper"] + ID = self.kwargs["id"] except KeyError: - logger.exception(f'{self.params_str()} requires at exactly seven ' + - 'parameters.') + logger.exception(f"{self.params_str()} requires at exactly seven " + "parameters.") raise if sand_fraction < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for the sand fraction.') + logger.exception(f"{self.params_str()} requires a positive value " + "for the sand fraction.") raise ValueError if clay_fraction < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for the clay fraction.') + logger.exception(f"{self.params_str()} requires a positive value " + "for the clay fraction.") raise ValueError if bulk_density < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for the bulk density.') + logger.exception(f"{self.params_str()} requires a positive value " + "for the bulk density.") raise ValueError if sand_density < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for the sand particle density.') + logger.exception(f"{self.params_str()} requires a positive value " + "for the sand particle density.") raise ValueError if water_fraction_lower < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for the lower limit of the water volumetric ' - 'fraction.') + logger.exception( + f"{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(f'{self.params_str()} requires a positive value ' + - 'for the upper limit of the water volumetric ' - 'fraction.') + logger.exception( + f"{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(f'{self.params_str()} with ID {ID} already exists') + logger.exception(f"{self.params_str()} with ID {ID} already exists") raise ValueError - # Create a new instance of the Material class material + # 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)) + s = PeplinskiSoilUser( + ID, sand_fraction, clay_fraction, bulk_density, sand_density, (water_fraction_lower, water_fraction_upper) + ) - logger.info(f'{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.') + logger.info( + f"{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." + ) grid.mixingmodels.append(s) class MaterialRange(UserObjectMulti): """Creates varying material properties for stochastic models. - + Attributes: er_lower: float required for lower relative permittivity value. - er_upper: float required for upper relative permittivity value. + er_upper: float required for upper relative permittivity value. sigma_lower: float required for lower conductivity value. - sigma_upper: float required for upper conductivity value. + sigma_upper: float required for upper conductivity value. mr_lower: float required for lower relative magnetic permeability value. - mr_upper: float required for upper relative magnetic permeability value. + mr_upper: float required for upper relative magnetic permeability value. ro_lower: float required for lower magnetic loss value. - ro_upper: float required for upper magnetic loss value. + ro_upper: float required for upper magnetic loss value. id: string used as identifier for this variable material. """ def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 17 - self.hash = '#material_range' + self.hash = "#material_range" def create(self, grid, uip): try: - er_lower = self.kwargs['er_lower'] - er_upper = self.kwargs['er_upper'] - sigma_lower = self.kwargs['sigma_lower'] - sigma_upper = self.kwargs['sigma_upper'] - mr_lower = self.kwargs['mr_lower'] - mr_upper = self.kwargs['mr_upper'] - ro_lower = self.kwargs['ro_lower'] - ro_upper = self.kwargs['ro_upper'] - ID = self.kwargs['id'] + er_lower = self.kwargs["er_lower"] + er_upper = self.kwargs["er_upper"] + sigma_lower = self.kwargs["sigma_lower"] + sigma_upper = self.kwargs["sigma_upper"] + mr_lower = self.kwargs["mr_lower"] + mr_upper = self.kwargs["mr_upper"] + ro_lower = self.kwargs["ro_lower"] + ro_upper = self.kwargs["ro_upper"] + ID = self.kwargs["id"] except KeyError: - logger.exception(f'{self.params_str()} requires at exactly nine ' + - 'parameters.') + logger.exception(f"{self.params_str()} requires at exactly nine " + "parameters.") raise if er_lower < 1: - logger.exception(f'{self.params_str()} requires a value greater or equal to 1 ' + - 'for the lower range of relative permittivity.') + logger.exception( + f"{self.params_str()} requires a value greater or equal to 1 " + + "for the lower range of relative permittivity." + ) raise ValueError if mr_lower < 1: - logger.exception(f'{self.params_str()} requires a value greater or equal to 1 ' + - 'for the lower range of relative magnetic permeability.') + logger.exception( + f"{self.params_str()} requires a value greater or equal to 1 " + + "for the lower range of relative magnetic permeability." + ) raise ValueError if sigma_lower < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for the lower limit of conductivity.') + logger.exception(f"{self.params_str()} requires a positive value " + "for the lower limit of conductivity.") raise ValueError if ro_lower < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for the lower range magnetic loss.') + logger.exception(f"{self.params_str()} requires a positive value " + "for the lower range magnetic loss.") raise ValueError if er_upper < 1: - logger.exception(f'{self.params_str()} requires a value greater or equal to 1' + - 'for the upper range of relative permittivity.') + logger.exception( + f"{self.params_str()} requires a value greater or equal to 1" + + "for the upper range of relative permittivity." + ) raise ValueError if mr_upper < 1: - logger.exception(f'{self.params_str()} requires a value greater or equal to 1' + - 'for the upper range of relative magnetic permeability') + logger.exception( + f"{self.params_str()} requires a value greater or equal to 1" + + "for the upper range of relative magnetic permeability" + ) raise ValueError if sigma_upper < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for the upper range of conductivity.') + logger.exception(f"{self.params_str()} requires a positive value " + "for the upper range of conductivity.") raise ValueError if ro_upper < 0: - logger.exception(f'{self.params_str()} requires a positive value ' + - 'for the upper range of magnetic loss.') + logger.exception( + f"{self.params_str()} requires a positive value " + "for the upper range of magnetic loss." + ) if any(x.ID == ID for x in grid.mixingmodels): - logger.exception(f'{self.params_str()} with ID {ID} already exists') + logger.exception(f"{self.params_str()} with ID {ID} already exists") raise ValueError - s = RangeMaterialUser(ID, (er_lower, er_upper), (sigma_lower, sigma_upper), - (mr_lower, mr_upper), (ro_lower, ro_upper)) - - logger.info(f'{self.grid_name(grid)}Material properties used to ' + - f'create {s.ID} with range(s) {s.er[0]:g} to {s.er[1]:g}, relative permittivity ' + - f'{s.sig[0]:g} to {s.sig[1]:g}, S/m conductivity, {s.mu[0]:g} to {s.mu[1]:g} relative magnetic permeability ' + - f'{s.ro[0]:g} to {s.ro[1]:g} Ohm/m magnetic loss, created') + s = RangeMaterialUser( + ID, (er_lower, er_upper), (sigma_lower, sigma_upper), (mr_lower, mr_upper), (ro_lower, ro_upper) + ) + logger.info( + f"{self.grid_name(grid)}Material properties used to " + + f"create {s.ID} with range(s) {s.er[0]:g} to {s.er[1]:g}, relative permittivity " + + f"{s.sig[0]:g} to {s.sig[1]:g}, S/m conductivity, {s.mu[0]:g} to {s.mu[1]:g} relative magnetic permeability " + + f"{s.ro[0]:g} to {s.ro[1]:g} Ohm/m magnetic loss, created" + ) grid.mixingmodels.append(s) class MaterialList(UserObjectMulti): """Creates varying material properties for stochastic models. - + Attributes: list_of_materials: list of material IDs id: string used as identifier for this variable material. @@ -1493,26 +1514,25 @@ class MaterialList(UserObjectMulti): def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 18 - self.hash = '#material_list' + self.hash = "#material_list" def create(self, grid, uip): try: - list_of_materials = self.kwargs['list_of_materials'] - ID = self.kwargs['id'] + list_of_materials = self.kwargs["list_of_materials"] + ID = self.kwargs["id"] except KeyError: - logger.exception(f'{self.params_str()} requires at at least 2 ' + - 'parameters.') + logger.exception(f"{self.params_str()} requires at at least 2 " + "parameters.") raise if any(x.ID == ID for x in grid.mixingmodels): - logger.exception(f'{self.params_str()} with ID {ID} already exists') + logger.exception(f"{self.params_str()} with ID {ID} already exists") raise ValueError s = ListMaterialUser(ID, list_of_materials) - logger.info(f'{self.grid_name(grid)}A list of materials used to ' + - f'create {s.ID} that includes {s.mat}, created') - + logger.info( + f"{self.grid_name(grid)}A list of materials used to " + f"create {s.ID} that includes {s.mat}, created" + ) grid.mixingmodels.append(s) @@ -1523,30 +1543,29 @@ class GeometryView(UserObjectMulti): The geometry information is saved in Visual Toolkit (VTK) formats. Attributes: - p1: tuple required for lower left (x,y,z) coordinates of volume of + 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 + 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 + filename: string required for filename where geometry view will be stored in the same directory as input file. """ def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 14 - self.hash = '#geometry_view' + self.hash = "#geometry_view" def geometry_view_constructor(self, grid, output_type): - """Selects appropriate class for geometry view dependent on geometry - view type, i.e. normal or fine. + """Selects appropriate class for geometry view dependent on geometry + view type, i.e. normal or fine. """ - if output_type == 'n': - from .geometry_outputs import \ - GeometryViewVoxels as GeometryViewUser + if output_type == "n": + from .geometry_outputs import GeometryViewVoxels as GeometryViewUser else: from .geometry_outputs import GeometryViewLines as GeometryViewUser @@ -1554,14 +1573,13 @@ class GeometryView(UserObjectMulti): def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - dl = self.kwargs['dl'] - output_type = self.kwargs['output_type'].lower() - filename = self.kwargs['filename'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + dl = self.kwargs["dl"] + output_type = self.kwargs["output_type"].lower() + filename = self.kwargs["filename"] except KeyError: - logger.exception(f'{self.params_str()} requires exactly eleven ' + - 'parameters.') + logger.exception(f"{self.params_str()} requires exactly eleven " + "parameters.") raise GeometryViewUser = self.geometry_view_constructor(grid, output_type) @@ -1571,7 +1589,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(f'{self.params_str()} point is outside the domain.') + logger.exception(f"{self.params_str()} point is outside the domain.") raise xs, ys, zs = p1 xf, yf, zf = p2 @@ -1579,67 +1597,66 @@ class GeometryView(UserObjectMulti): dx, dy, dz = uip.discretise_static_point(dl) if dx < 0 or dy < 0 or dz < 0: - logger.exception(f'{self.params_str()} the step size should not be ' + - 'less than zero.') + logger.exception(f"{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(f'{self.params_str()} the step size should be ' + - 'less than the domain size.') + logger.exception(f"{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(f'{self.params_str()} the step size should not ' + - 'be less than the spatial discretisation.') + logger.exception( + f"{self.params_str()} the step size should not " + "be less than the spatial discretisation." + ) raise ValueError - if output_type not in ['n', 'f']: - logger.exception(f'{self.params_str()} requires type to be either ' + - 'n (normal) or f (fine).') + if output_type not in ["n", "f"]: + logger.exception(f"{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(f'{self.params_str()} requires the spatial ' + - 'discretisation for the geometry view to be the ' + - 'same as the model for geometry view of ' + - 'type f (fine)') + if output_type == "f" and (dx * grid.dx != grid.dx or dy * grid.dy != grid.dy or dz * grid.dz != grid.dz): + logger.exception( + f"{self.params_str()} requires the spatial " + + "discretisation for the geometry view to be the " + + "same as the model for geometry view of " + + "type f (fine)" + ) raise ValueError g = GeometryViewUser(xs, ys, zs, xf, yf, zf, dx, dy, dz, filename, grid) - logger.info(f'{self.grid_name(grid)}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.') + logger.info( + f"{self.grid_name(grid)}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." + ) grid.geometryviews.append(g) class GeometryObjectsWrite(UserObjectMulti): - """Writes geometry generated in a model to file which can be imported into + """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 + 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 + 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 + filename: string required for filename where output will be stored in the same directory as input file. """ def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 15 - self.hash = '#geometry_objects_write' + self.hash = "#geometry_objects_write" def create(self, grid, uip): try: - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] - basefilename = self.kwargs['filename'] + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] + basefilename = self.kwargs["filename"] except KeyError: - logger.exception(f'{self.params_str()} requires exactly seven ' + - 'parameters.') + logger.exception(f"{self.params_str()} requires exactly seven " + "parameters.") raise p1, p2 = uip.check_box_points(p1, p2, self.params_str()) @@ -1648,12 +1665,14 @@ 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, ' - 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}') + 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}" + ) grid.geometryobjectswrite.append(g) @@ -1663,21 +1682,21 @@ class PMLCFS(UserObjectMulti): values are set in pml.py Attributes: - alphascalingprofile: string required for type of scaling to use for + alphascalingprofile: string required for type of scaling to use for CFS alpha parameter. - alphascalingdirection: string required for direction of scaling to use + 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 + kappascalingprofile: string required for type of scaling to use for CFS kappa parameter. - kappascalingdirection: string required for direction of scaling to use + 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 + sigmascalingprofile: string required for type of scaling to use for CFS sigma parameter. - sigmascalingdirection: string required for direction of scaling to use + 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. @@ -1689,39 +1708,48 @@ class PMLCFS(UserObjectMulti): def create(self, grid, uip): try: - alphascalingprofile = self.kwargs['alphascalingprofile'] - alphascalingdirection = self.kwargs['alphascalingdirection'] - alphamin = self.kwargs['alphamin'] - alphamax = self.kwargs['alphamax'] - kappascalingprofile = self.kwargs['kappascalingprofile'] - kappascalingdirection = self.kwargs['kappascalingdirection'] - kappamin = self.kwargs['kappamin'] - kappamax = self.kwargs['kappamax'] - sigmascalingprofile = self.kwargs['sigmascalingprofile'] - sigmascalingdirection = self.kwargs['sigmascalingdirection'] - sigmamin = self.kwargs['sigmamin'] - sigmamax = self.kwargs['sigmamax'] + alphascalingprofile = self.kwargs["alphascalingprofile"] + alphascalingdirection = self.kwargs["alphascalingdirection"] + alphamin = self.kwargs["alphamin"] + alphamax = self.kwargs["alphamax"] + kappascalingprofile = self.kwargs["kappascalingprofile"] + kappascalingdirection = self.kwargs["kappascalingdirection"] + kappamin = self.kwargs["kappamin"] + kappamax = self.kwargs["kappamax"] + sigmascalingprofile = self.kwargs["sigmascalingprofile"] + sigmascalingdirection = self.kwargs["sigmascalingdirection"] + sigmamin = self.kwargs["sigmamin"] + sigmamax = self.kwargs["sigmamax"] except KeyError: - logger.exception(f'{self.params_str()} requires exactly twelve ' + - 'parameters.') + logger.exception(f"{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(f'{self.params_str()} must have scaling type ' + - f"{','.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( + f"{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(f'{self.params_str()} must have scaling type ' + - f"{','.join(CFSParameter.scalingdirections)}") + if ( + alphascalingdirection not in CFSParameter.scalingdirections + or kappascalingdirection not in CFSParameter.scalingdirections + or sigmascalingdirection not in CFSParameter.scalingdirections + ): + logger.exception( + f"{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(f'{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(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: @@ -1730,19 +1758,19 @@ class PMLCFS(UserObjectMulti): # raise ValueError cfsalpha = CFSParameter() - cfsalpha.ID = 'alpha' + cfsalpha.ID = "alpha" cfsalpha.scalingprofile = alphascalingprofile cfsalpha.scalingdirection = alphascalingdirection cfsalpha.min = float(alphamin) cfsalpha.max = float(alphamax) cfskappa = CFSParameter() - cfskappa.ID = 'kappa' + cfskappa.ID = "kappa" cfskappa.scalingprofile = kappascalingprofile cfskappa.scalingdirection = kappascalingdirection cfskappa.min = float(kappamin) cfskappa.max = float(kappamax) cfssigma = CFSParameter() - cfssigma.ID = 'sigma' + cfssigma.ID = "sigma" cfssigma.scalingprofile = sigmascalingprofile cfssigma.scalingdirection = sigmascalingdirection cfssigma.min = float(sigmamin) @@ -1753,25 +1781,27 @@ class PMLCFS(UserObjectMulti): cfs.kappa = cfskappa cfs.sigma = cfssigma - 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.pmls['cfs'].append(cfs) + 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." + ) - if len(grid.pmls['cfs']) > 2: - logger.exception(f'{self.params_str()} can only be used up to two ' + - 'times, for up to a 2nd order PML.') + grid.pmls["cfs"].append(cfs) + + if len(grid.pmls["cfs"]) > 2: + logger.exception(f"{self.params_str()} can only be used up to two " + "times, for up to a 2nd order PML.") raise ValueError - + class Subgrid(UserObjectMulti): """""" + def __init__(self, **kwargs): super().__init__(**kwargs) self.children_multiple = [] @@ -1783,5 +1813,5 @@ 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 diff --git a/gprMax/cmds_singleuse.py b/gprMax/cmds_singleuse.py index f65a68ca..f9354cde 100644 --- a/gprMax/cmds_singleuse.py +++ b/gprMax/cmds_singleuse.py @@ -70,9 +70,9 @@ class Title(UserObjectSingle): def create(self, G, uip): try: - title = self.kwargs['name'] + title = self.kwargs["name"] G.title = title - logger.info(f'Model title: {G.title}') + logger.info(f"Model title: {G.title}") except KeyError: pass @@ -90,26 +90,29 @@ class Discretisation(UserObjectSingle): def create(self, G, uip): try: - G.dl = np.array(self.kwargs['p1']) - G.dx, G.dy, G.dz = self.kwargs['p1'] + G.dl = np.array(self.kwargs["p1"]) + G.dx, G.dy, G.dz = self.kwargs["p1"] except KeyError: - logger.exception(f'{self.__str__()} discretisation requires a point') + logger.exception(f"{self.__str__()} discretisation requires a point") raise if G.dl[0] <= 0: - logger.exception(f'{self.__str__()} discretisation requires the ' - f'x-direction spatial step to be greater than zero') + logger.exception( + f"{self.__str__()} discretisation requires the " f"x-direction spatial step to be greater than zero" + ) raise ValueError if G.dl[1] <= 0: - logger.exception(f'{self.__str__()} discretisation requires the ' - f'y-direction spatial step to be greater than zero') + logger.exception( + f"{self.__str__()} discretisation requires the " f"y-direction spatial step to be greater than zero" + ) raise ValueError if G.dl[2] <= 0: - logger.exception(f'{self.__str__()} discretisation requires the ' - f'z-direction spatial step to be greater than zero') + logger.exception( + f"{self.__str__()} discretisation requires the " f"z-direction spatial step to be greater than zero" + ) raise ValueError - logger.info(f'Spatial discretisation: {G.dl[0]:g} x {G.dl[1]:g} x {G.dl[2]:g}m') + logger.info(f"Spatial discretisation: {G.dl[0]:g} x {G.dl[1]:g} x {G.dl[2]:g}m") class Domain(UserObjectSingle): @@ -125,46 +128,47 @@ class Domain(UserObjectSingle): def create(self, G, uip): try: - G.nx, G.ny, G.nz = uip.discretise_point(self.kwargs['p1']) + G.nx, G.ny, G.nz = uip.discretise_point(self.kwargs["p1"]) except KeyError: - logger.exception(f'{self.__str__()} please specify a point') + logger.exception(f"{self.__str__()} please specify a point") raise if G.nx == 0 or G.ny == 0 or G.nz == 0: - logger.exception(f'{self.__str__()} requires at least one cell in ' - f'every dimension') + logger.exception(f"{self.__str__()} requires at least one cell in " f"every dimension") raise ValueError - logger.info(f"Domain size: {self.kwargs['p1'][0]:g} x {self.kwargs['p1'][1]:g} x " + - f"{self.kwargs['p1'][2]:g}m ({G.nx:d} x {G.ny:d} x {G.nz:d} = " + - f"{(G.nx * G.ny * G.nz):g} cells)") + logger.info( + f"Domain size: {self.kwargs['p1'][0]:g} x {self.kwargs['p1'][1]:g} x " + + f"{self.kwargs['p1'][2]:g}m ({G.nx:d} x {G.ny:d} x {G.nz:d} = " + + f"{(G.nx * G.ny * G.nz):g} cells)" + ) # Calculate time step at CFL limit; switch off appropriate PMLs for 2D if G.nx == 1: - config.get_model_config().mode = '2D TMx' - G.pmls['thickness']['x0'] = 0 - G.pmls['thickness']['xmax'] = 0 + config.get_model_config().mode = "2D TMx" + G.pmls["thickness"]["x0"] = 0 + G.pmls["thickness"]["xmax"] = 0 elif G.ny == 1: - config.get_model_config().mode = '2D TMy' - G.pmls['thickness']['y0'] = 0 - G.pmls['thickness']['ymax'] = 0 + config.get_model_config().mode = "2D TMy" + G.pmls["thickness"]["y0"] = 0 + G.pmls["thickness"]["ymax"] = 0 elif G.nz == 1: - config.get_model_config().mode = '2D TMz' - G.pmls['thickness']['z0'] = 0 - G.pmls['thickness']['zmax'] = 0 + config.get_model_config().mode = "2D TMz" + G.pmls["thickness"]["z0"] = 0 + G.pmls["thickness"]["zmax"] = 0 else: - config.get_model_config().mode = '3D' + config.get_model_config().mode = "3D" G.calculate_dt() - logger.info(f'Mode: {config.get_model_config().mode}') + logger.info(f"Mode: {config.get_model_config().mode}") # Sub-grids cannot be used with 2D models. There would typically be # minimal performance benefit with sub-gridding and 2D models. - if '2D' in config.get_model_config().mode and config.sim_config.general['subgrid']: - logger.exception('Sub-gridding cannot be used with 2D models') + if "2D" in config.get_model_config().mode and config.sim_config.general["subgrid"]: + logger.exception("Sub-gridding cannot be used with 2D models") raise ValueError - logger.info(f'Time step (at CFL limit): {G.dt:g} secs') + logger.info(f"Time step (at CFL limit): {G.dt:g} secs") class TimeStepStabilityFactor(UserObjectSingle): @@ -180,20 +184,21 @@ class TimeStepStabilityFactor(UserObjectSingle): def create(self, G, uip): try: - f = self.kwargs['f'] + f = self.kwargs["f"] except KeyError: - logger.exception(f'{self.__str__()} requires exactly one parameter') + logger.exception(f"{self.__str__()} requires exactly one parameter") raise if f <= 0 or f > 1: - logger.exception(f'{self.__str__()} requires the value of the time ' - f'step stability factor to be between zero and one') + logger.exception( + f"{self.__str__()} requires the value of the time " f"step stability factor to be between zero and one" + ) raise ValueError - + G.dt_mod = f G.dt = G.dt * G.dt_mod - logger.info(f'Time step (modified): {G.dt:g} secs') + logger.info(f"Time step (modified): {G.dt:g} secs") class TimeWindow(UserObjectSingle): @@ -213,32 +218,32 @@ class TimeWindow(UserObjectSingle): # The +/- 1 used in calculating the number of iterations is to account for # the fact that the solver (iterations) loop runs from 0 to < G.iterations try: - iterations = int(self.kwargs['iterations']) + iterations = int(self.kwargs["iterations"]) G.timewindow = (iterations - 1) * G.dt G.iterations = iterations except KeyError: pass try: - tmp = float(self.kwargs['time']) + tmp = float(self.kwargs["time"]) if tmp > 0: G.timewindow = tmp G.iterations = int(np.ceil(tmp / G.dt)) + 1 else: - logger.exception(self.__str__() + ' must have a value greater than zero') + logger.exception(self.__str__() + " must have a value greater than zero") raise ValueError except KeyError: pass if not G.timewindow: - logger.exception(self.__str__() + ' specify a time or number of iterations') + logger.exception(self.__str__() + " specify a time or number of iterations") raise ValueError - logger.info(f'Time window: {G.timewindow:g} secs ({G.iterations} iterations)') + logger.info(f"Time window: {G.timewindow:g} secs ({G.iterations} iterations)") class OMPThreads(UserObjectSingle): - """Controls how many OpenMP threads (usually the number of physical CPU + """Controls how many OpenMP threads (usually the number of physical CPU cores available) are used when running the model. Attributes: @@ -251,30 +256,31 @@ class OMPThreads(UserObjectSingle): def create(self, G, uip): try: - n = self.kwargs['n'] + n = self.kwargs["n"] except KeyError: - logger.exception(f'{self.__str__()} requires exactly one parameter ' - f'to specify the number of CPU OpenMP threads to use') + logger.exception( + f"{self.__str__()} requires exactly one parameter " + f"to specify the number of CPU OpenMP threads to use" + ) raise if n < 1: - logger.exception(f'{self.__str__()} requires the value to be an ' - f'integer not less than one') + logger.exception(f"{self.__str__()} requires the value to be an " f"integer not less than one") raise ValueError config.get_model_config().ompthreads = set_omp_threads(n) class PMLProps(UserObjectSingle): - """Specifies the formulation used and thickness (number of cells) of PML - that are used on the six sides of the model domain. Current options are - to use the Higher Order RIPML (HORIPML) - https://doi.org/10.1109/TAP.2011.2180344, + """Specifies the formulation used and thickness (number of cells) of PML + that are used on the six sides of the model domain. 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. Attributes: - formulation: string specifying formulation to be used for all PMLs + formulation: string specifying formulation to be used for all PMLs either 'HORIPML' or 'MRIPML'. - thickness or x0, y0, z0, xmax, ymax, zmax: ints for thickness of PML - on all 6 sides or individual + thickness or x0, y0, z0, xmax, ymax, zmax: ints for thickness of PML + on all 6 sides or individual sides of the model domain. """ @@ -284,37 +290,40 @@ class PMLProps(UserObjectSingle): def create(self, G, uip): try: - G.pmls['formulation'] = self.kwargs['formulation'] - if G.pmls['formulation'] not in PML.formulations: - logger.exception(self.__str__() + f" requires the value to be " + - f"one of {' '.join(PML.formulations)}") + G.pmls["formulation"] = self.kwargs["formulation"] + if G.pmls["formulation"] not in PML.formulations: + logger.exception( + self.__str__() + f" requires the value to be " + f"one of {' '.join(PML.formulations)}" + ) except KeyError: pass try: - thickness = self.kwargs['thickness'] - for key in G.pmls['thickness'].keys(): - G.pmls['thickness'][key] = int(thickness) + thickness = self.kwargs["thickness"] + for key in G.pmls["thickness"].keys(): + G.pmls["thickness"][key] = int(thickness) except KeyError: try: - G.pmls['thickness']['x0'] = int(self.kwargs['x0']) - G.pmls['thickness']['y0'] = int(self.kwargs['y0']) - G.pmls['thickness']['z0'] = int(self.kwargs['z0']) - G.pmls['thickness']['xmax'] = int(self.kwargs['xmax']) - G.pmls['thickness']['ymax'] = int(self.kwargs['ymax']) - G.pmls['thickness']['zmax'] = int(self.kwargs['zmax']) + G.pmls["thickness"]["x0"] = int(self.kwargs["x0"]) + G.pmls["thickness"]["y0"] = int(self.kwargs["y0"]) + G.pmls["thickness"]["z0"] = int(self.kwargs["z0"]) + G.pmls["thickness"]["xmax"] = int(self.kwargs["xmax"]) + G.pmls["thickness"]["ymax"] = int(self.kwargs["ymax"]) + G.pmls["thickness"]["zmax"] = int(self.kwargs["zmax"]) except KeyError: - logger.exception(f'{self.__str__()} requires either one or six parameter(s)') + logger.exception(f"{self.__str__()} requires either one or six parameter(s)") raise - if (2 * G.pmls['thickness']['x0'] >= G.nx or - 2 * G.pmls['thickness']['y0'] >= G.ny or - 2 * G.pmls['thickness']['z0'] >= G.nz or - 2 * G.pmls['thickness']['xmax'] >= G.nx or - 2 * G.pmls['thickness']['ymax'] >= G.ny or - 2 * G.pmls['thickness']['zmax'] >= G.nz): - logger.exception(f'{self.__str__()} has too many cells for the domain size') + if ( + 2 * G.pmls["thickness"]["x0"] >= G.nx + or 2 * G.pmls["thickness"]["y0"] >= G.ny + or 2 * G.pmls["thickness"]["z0"] >= G.nz + or 2 * G.pmls["thickness"]["xmax"] >= G.nx + or 2 * G.pmls["thickness"]["ymax"] >= G.ny + or 2 * G.pmls["thickness"]["zmax"] >= G.nz + ): + logger.exception(f"{self.__str__()} has too many cells for the domain size") raise ValueError @@ -331,14 +340,16 @@ class SrcSteps(UserObjectSingle): def create(self, G, uip): try: - G.srcsteps = uip.discretise_point(self.kwargs['p1']) + G.srcsteps = uip.discretise_point(self.kwargs["p1"]) except KeyError: - logger.exception(f'{self.__str__()} requires exactly three parameters') + logger.exception(f"{self.__str__()} requires exactly three parameters") raise - logger.info(f'Simple sources will step {G.srcsteps[0] * G.dx:g}m, ' + - f'{G.srcsteps[1] * G.dy:g}m, {G.srcsteps[2] * G.dz:g}m ' + - 'for each model run.') + logger.info( + f"Simple sources will step {G.srcsteps[0] * G.dx:g}m, " + + f"{G.srcsteps[1] * G.dy:g}m, {G.srcsteps[2] * G.dz:g}m " + + "for each model run." + ) class RxSteps(UserObjectSingle): @@ -354,23 +365,25 @@ class RxSteps(UserObjectSingle): def create(self, G, uip): try: - G.rxsteps = uip.discretise_point(self.kwargs['p1']) + G.rxsteps = uip.discretise_point(self.kwargs["p1"]) except KeyError: - logger.exception(f'{self.__str__()} requires exactly three parameters') + logger.exception(f"{self.__str__()} requires exactly three parameters") raise - logger.info(f'All receivers will step {G.rxsteps[0] * G.dx:g}m, ' + - f'{G.rxsteps[1] * G.dy:g}m, {G.rxsteps[2] * G.dz:g}m ' + - 'for each model run.') + logger.info( + f"All receivers will step {G.rxsteps[0] * G.dx:g}m, " + + f"{G.rxsteps[1] * G.dy:g}m, {G.rxsteps[2] * G.dz:g}m " + + "for each model run." + ) class ExcitationFile(UserObjectSingle): - """An ASCII file that contains columns of amplitude values that specify + """An ASCII file that contains columns of amplitude values that specify custom waveform shapes that can be used with sources in the model. Attributes: filepath: string of excitation file path. - kind: string or int specifying interpolation kind passed to + kind: string or int specifying interpolation kind passed to scipy.interpolate.interp1d. fill_value: float or 'extrapolate' passed to scipy.interpolate.interp1d. """ @@ -382,74 +395,75 @@ class ExcitationFile(UserObjectSingle): def create(self, G, uip): try: kwargs = {} - excitationfile = self.kwargs['filepath'] - kwargs['kind'] = self.kwargs['kind'] - kwargs['fill_value'] = self.kwargs['fill_value'] + excitationfile = self.kwargs["filepath"] + kwargs["kind"] = self.kwargs["kind"] + kwargs["fill_value"] = self.kwargs["fill_value"] except KeyError: try: - excitationfile = self.kwargs['filepath'] + excitationfile = self.kwargs["filepath"] fullargspec = inspect.getfullargspec(interpolate.interp1d) - kwargs = dict(zip(reversed(fullargspec.args), - reversed(fullargspec.defaults))) + kwargs = dict(zip(reversed(fullargspec.args), reversed(fullargspec.defaults))) except KeyError: - logger.exception(f'{self.__str__()} requires either one or three parameter(s)') + logger.exception(f"{self.__str__()} requires either one or three parameter(s)") raise # See if file exists at specified path and if not try input file directory excitationfile = Path(excitationfile) # excitationfile = excitationfile.resolve() if not excitationfile.exists(): - excitationfile = Path(config.sim_config.input_file_path.parent, - excitationfile) + excitationfile = Path(config.sim_config.input_file_path.parent, excitationfile) - logger.info(f'Excitation file: {excitationfile}') + logger.info(f"Excitation file: {excitationfile}") # Get waveform names - with open(excitationfile, 'r') as f: + with open(excitationfile, "r") as f: waveformIDs = f.readline().split() # Read all waveform values into an array - waveformvalues = np.loadtxt(excitationfile, skiprows=1, - dtype=config.sim_config.dtypes['float_or_double']) + waveformvalues = np.loadtxt(excitationfile, skiprows=1, dtype=config.sim_config.dtypes["float_or_double"]) # Time array (if specified) for interpolation, otherwise use simulation time - if waveformIDs[0].lower() == 'time': + if waveformIDs[0].lower() == "time": waveformIDs = waveformIDs[1:] waveformtime = waveformvalues[:, 0] waveformvalues = waveformvalues[:, 1:] - timestr = 'user-defined time array' + timestr = "user-defined time array" else: waveformtime = np.arange(0, G.timewindow + G.dt, G.dt) - timestr = 'simulation time array' + timestr = "simulation time array" for waveform in range(len(waveformIDs)): if any(x.ID == waveformIDs[waveform] for x in G.waveforms): - logger.exception(f'Waveform with ID {waveformIDs[waveform]} already exists') + logger.exception(f"Waveform with ID {waveformIDs[waveform]} already exists") raise ValueError w = Waveform() w.ID = waveformIDs[waveform] - w.type = 'user' + w.type = "user" # Select correct column of waveform values depending on array shape singlewaveformvalues = waveformvalues[:] if len(waveformvalues.shape) == 1 else waveformvalues[:, waveform] # Truncate waveform array if it is longer than time array if len(singlewaveformvalues) > len(waveformtime): - singlewaveformvalues = singlewaveformvalues[:len(waveformtime)] + singlewaveformvalues = singlewaveformvalues[: len(waveformtime)] # Zero-pad end of waveform array if it is shorter than time array elif len(singlewaveformvalues) < len(waveformtime): - singlewaveformvalues = np.pad(singlewaveformvalues, - (0, len(waveformtime) - - len(singlewaveformvalues)), - 'constant', constant_values=0) + singlewaveformvalues = np.pad( + singlewaveformvalues, + (0, len(waveformtime) - len(singlewaveformvalues)), + "constant", + constant_values=0, + ) # Interpolate waveform values w.userfunc = interpolate.interp1d(waveformtime, singlewaveformvalues, **kwargs) - logger.info(f"User waveform {w.ID} created using {timestr} and, if " + - f"required, interpolation parameters (kind: {kwargs['kind']}, " + - f"fill value: {kwargs['fill_value']}).") + logger.info( + f"User waveform {w.ID} created using {timestr} and, if " + + f"required, interpolation parameters (kind: {kwargs['kind']}, " + + f"fill value: {kwargs['fill_value']})." + ) G.waveforms.append(w) @@ -460,9 +474,10 @@ class OutputDir(UserObjectSingle): Attributes: dir: string of file path to directory. """ + def __init__(self, **kwargs): super().__init__(**kwargs) self.order = 11 def create(self, grid, uip): - config.get_model_config().set_output_file_path(self.kwargs['dir']) \ No newline at end of file + config.get_model_config().set_output_file_path(self.kwargs["dir"]) diff --git a/gprMax/config.py b/gprMax/config.py index 162b06c8..d75844c1 100644 --- a/gprMax/config.py +++ b/gprMax/config.py @@ -48,6 +48,7 @@ model_configs = [] # ModelConfig is created model_num = 0 + def get_model_config(): """Return ModelConfig instace for specific model.""" if sim_config.args.mpi: @@ -55,27 +56,27 @@ def get_model_config(): else: return model_configs[model_num] + class ModelConfig: """Configuration parameters for a model. - N.B. Multiple models can exist within a simulation + N.B. Multiple models can exist within a simulation """ def __init__(self): - - self.mode = '3D' + self.mode = "3D" self.grids = [] self.ompthreads = None # Store information for CUDA or OpenCL solver # dev: compute device object. # snapsgpu2cpu: copy snapshot data from GPU to CPU during simulation. - # N.B. This will happen if the requested snapshots are too large to - # fit on the memory of the GPU. If True this will slow + # N.B. This will happen if the requested snapshots are too large to + # fit on the memory of the GPU. If True this will slow # performance significantly. - if sim_config.general['solver'] == 'cuda' or sim_config.general['solver'] == 'opencl': - if sim_config.general['solver'] == 'cuda': + if sim_config.general["solver"] == "cuda" or sim_config.general["solver"] == "opencl": + if sim_config.general["solver"] == "cuda": devs = sim_config.args.gpu - elif sim_config.general['solver'] == 'opencl': + elif sim_config.general["solver"] == "opencl": devs = sim_config.args.opencl # If a list of lists of deviceIDs is found, flatten it @@ -89,8 +90,7 @@ class ModelConfig: except: deviceID = 0 - self.device = {'dev': sim_config.set_model_device(deviceID), - 'snapsgpu2cpu': False} + self.device = {"dev": sim_config.set_model_device(deviceID), "snapsgpu2cpu": False} # Total memory usage for all grids in the model. Starts with 50MB overhead. self.mem_overhead = 65e6 @@ -99,26 +99,22 @@ class ModelConfig: self.reuse_geometry = False # String to print at start of each model run - s = (f'\n--- Model {model_num + 1}/{sim_config.model_end}, ' - f'input file: {sim_config.input_file_path}') - self.inputfilestr = (Fore.GREEN + f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n" + - Style.RESET_ALL) + s = f"\n--- Model {model_num + 1}/{sim_config.model_end}, " f"input file: {sim_config.input_file_path}" + self.inputfilestr = Fore.GREEN + f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n" + Style.RESET_ALL # Output file path and name for specific model - self.appendmodelnumber = '' if sim_config.args.n == 1 else str(model_num + 1) # Indexed from 1 + self.appendmodelnumber = "" if sim_config.args.n == 1 else str(model_num + 1) # Indexed from 1 self.set_output_file_path() # Numerical dispersion analysis parameters - # highestfreqthres: threshold (dB) down from maximum power (0dB) of - # main frequency used to calculate highest + # highestfreqthres: threshold (dB) down from maximum power (0dB) of + # main frequency used to calculate highest # frequency for numerical dispersion analysis. - # maxnumericaldisp: maximum allowable percentage physical + # maxnumericaldisp: maximum allowable percentage physical # phase-velocity phase error. - # mingridsampling: minimum grid sampling of smallest wavelength for + # mingridsampling: minimum grid sampling of smallest wavelength for # physical wave propagation. - self.numdispersion = {'highestfreqthres': 40, - 'maxnumericaldisp': 2, - 'mingridsampling': 3} + self.numdispersion = {"highestfreqthres": 40, "maxnumericaldisp": 2, "mingridsampling": 3} # General information to configure materials # maxpoles: Maximum number of dispersive material poles in a model. @@ -127,41 +123,45 @@ class ModelConfig: # drudelorentz: True/False model contains Drude or Lorentz materials. # cudarealfunc: String to substitute into CUDA kernels for fields # dependent on dispersive material type. - self.materials = {'maxpoles': 0, - 'dispersivedtype': None, - 'dispersiveCdtype': None, - 'drudelorentz': None, - 'cudarealfunc': ''} + self.materials = { + "maxpoles": 0, + "dispersivedtype": None, + "dispersiveCdtype": None, + "drudelorentz": None, + "cudarealfunc": "", + } def get_scene(self): if sim_config.scenes: return sim_config.scenes[model_num] - else: return None + else: + return None def get_usernamespace(self): """Namespace only used with #python blocks which are deprecated.""" - tmp = {'number_model_runs': sim_config.model_end, - 'current_model_run': model_num + 1, - 'inputfile': sim_config.input_file_path.resolve()} + tmp = { + "number_model_runs": sim_config.model_end, + "current_model_run": model_num + 1, + "inputfile": sim_config.input_file_path.resolve(), + } return dict(**sim_config.em_consts, **tmp) - def set_dispersive_material_types(self): """Sets data type for disperive materials. Complex if Drude or Lorentz - materials are present. Real if Debye materials. + materials are present. Real if Debye materials. """ - if self.materials['drudelorentz']: - self.materials['crealfunc'] = '.real()' - self.materials['dispersivedtype'] = sim_config.dtypes['complex'] - self.materials['dispersiveCdtype'] = sim_config.dtypes['C_complex'] + if self.materials["drudelorentz"]: + self.materials["crealfunc"] = ".real()" + self.materials["dispersivedtype"] = sim_config.dtypes["complex"] + self.materials["dispersiveCdtype"] = sim_config.dtypes["C_complex"] else: - self.materials['crealfunc'] = '' - self.materials['dispersivedtype'] = sim_config.dtypes['float_or_double'] - self.materials['dispersiveCdtype'] = sim_config.dtypes['C_float_or_double'] + self.materials["crealfunc"] = "" + self.materials["dispersivedtype"] = sim_config.dtypes["float_or_double"] + self.materials["dispersiveCdtype"] = sim_config.dtypes["C_float_or_double"] def set_output_file_path(self, outputdir=None): - """Sets output file path. Can be provided by the user via the API or an - input file command. If they haven't provided one use the input file + """Sets output file path. Can be provided by the user via the API or an + input file command. If they haven't provided one use the input file path instead. Args: @@ -172,33 +172,33 @@ class ModelConfig: try: self.output_file_path = Path(self.args.outputfile) except AttributeError: - self.output_file_path = sim_config.input_file_path.with_suffix('') + self.output_file_path = sim_config.input_file_path.with_suffix("") else: try: Path(outputdir).mkdir(exist_ok=True) self.output_file_path = Path(outputdir, sim_config.input_file_path.stem) except AttributeError: - self.output_file_path = sim_config.input_file_path.with_suffix('') + self.output_file_path = sim_config.input_file_path.with_suffix("") parts = self.output_file_path.parts self.output_file_path = Path(*parts[:-1], parts[-1] + self.appendmodelnumber) - self.output_file_path_ext = self.output_file_path.with_suffix('.h5') + self.output_file_path_ext = self.output_file_path.with_suffix(".h5") def set_snapshots_dir(self): """Sets directory to store any snapshots. - + Returns: snapshot_dir: Path to directory to store snapshot files in. """ - parts = self.output_file_path.with_suffix('').parts - snapshot_dir = Path(*parts[:-1], parts[-1] + '_snaps') - + parts = self.output_file_path.with_suffix("").parts + snapshot_dir = Path(*parts[:-1], parts[-1] + "_snaps") + return snapshot_dir class SimulationConfig: """Configuration parameters for a simulation. - N.B. A simulation can consist of multiple models. + N.B. A simulation can consist of multiple models. """ def __init__(self, args): @@ -210,11 +210,11 @@ class SimulationConfig: self.args = args if args.mpi and args.geometry_fixed: - logger.exception('The geometry fixed option cannot be used with MPI.') + logger.exception("The geometry fixed option cannot be used with MPI.") raise ValueError if args.gpu and args.opencl: - logger.exception('You cannot use both CUDA and OpenCl simultaneously.') + logger.exception("You cannot use both CUDA and OpenCl simultaneously.") raise ValueError # General settings for the simulation @@ -224,62 +224,62 @@ class SimulationConfig: # solver: cpu, cuda, opencl. # subgrid: whether the simulation uses sub-grids. # precision: data type for electromagnetic field output (single/double). - # progressbars: progress bars on stdoout or not - switch off - # progressbars when logging level is greater than - # info (20) + # progressbars: progress bars on stdoout or not - switch off + # progressbars when logging level is greater than + # info (20) - self.general = {'solver': 'cpu', - 'subgrid': False, - 'precision': 'single', - 'progressbars': args.log_level <= 20} + self.general = {"solver": "cpu", "subgrid": False, "precision": "single", "progressbars": args.log_level <= 20} - self.em_consts = {'c': c, # Speed of light in free space (m/s) - 'e0': e0, # Permittivity of free space (F/m) - 'm0': m0, # Permeability of free space (H/m) - 'z0': np.sqrt(m0 / e0)} # Impedance of free space (Ohms) + self.em_consts = { + "c": c, # Speed of light in free space (m/s) + "e0": e0, # Permittivity of free space (F/m) + "m0": m0, # Permeability of free space (H/m) + "z0": np.sqrt(m0 / e0), + } # Impedance of free space (Ohms) # Store information about host machine self.hostinfo = get_host_info() # CUDA if self.args.gpu is not None: - self.general['solver'] = 'cuda' + self.general["solver"] = "cuda" # Both single and double precision are possible on GPUs, but single # provides best performance. - self.general['precision'] = 'single' - self.devices = {'devs': [], # pycuda device objects - 'nvcc_opts': None} # nvcc compiler options + self.general["precision"] = "single" + self.devices = {"devs": [], "nvcc_opts": None} # pycuda device objects # nvcc compiler options # Suppress nvcc warnings on Microsoft Windows - if sys.platform == 'win32': self.devices['nvcc_opts'] = ['-w'] + if sys.platform == "win32": + self.devices["nvcc_opts"] = ["-w"] # Add pycuda available GPU(s) - self.devices['devs'] = detect_cuda_gpus() + self.devices["devs"] = detect_cuda_gpus() # OpenCL if self.args.opencl is not None: - self.general['solver'] = 'opencl' - self.general['precision'] = 'single' - self.devices = {'devs': [], # pyopencl available device(s) - 'compiler_opts': None} + self.general["solver"] = "opencl" + self.general["precision"] = "single" + self.devices = {"devs": [], "compiler_opts": None} # pyopencl available device(s) # Suppress unused variable warnings on gcc # if sys.platform != 'win32': self.devices['compiler_opts'] = ['-w'] # Add pyopencl available device(s) - self.devices['devs'] = detect_opencl() + self.devices["devs"] = detect_opencl() # Subgrid parameter may not exist if user enters via CLI try: - self.general['subgrid'] = self.args.subgrid + self.general["subgrid"] = self.args.subgrid # Double precision should be used with subgrid for best accuracy - self.general['precision'] = 'double' - if ((self.general['subgrid'] and self.general['solver'] == 'cuda') or - (self.general['subgrid'] and self.general['solver'] == 'opencl')): - logger.exception('You cannot currently use CUDA or OpenCL-based ' - 'solvers with models that contain sub-grids.') + self.general["precision"] = "double" + if (self.general["subgrid"] and self.general["solver"] == "cuda") or ( + self.general["subgrid"] and self.general["solver"] == "opencl" + ): + logger.exception( + "You cannot currently use CUDA or OpenCL-based " "solvers with models that contain sub-grids." + ) raise ValueError except AttributeError: - self.general['subgrid'] = False + self.general["subgrid"] = False # Scenes parameter may not exist if user enters via CLI try: @@ -303,49 +303,52 @@ class SimulationConfig: """ found = False - for ID, dev in self.devices['devs'].items(): + for ID, dev in self.devices["devs"].items(): if ID == deviceID: found = True return dev if not found: - logger.exception(f'Compute device with device ID {deviceID} does ' - 'not exist.') + logger.exception(f"Compute device with device ID {deviceID} does " "not exist.") raise ValueError def _set_precision(self): """Data type (precision) for electromagnetic field output. - Solid and ID arrays use 32-bit integers (0 to 4294967295). - Rigid arrays use 8-bit integers (the smallest available type to store true/false). - Fractal arrays use complex numbers. - Dispersive coefficient arrays use either float or complex numbers. - Main field arrays use floats. + Solid and ID arrays use 32-bit integers (0 to 4294967295). + Rigid arrays use 8-bit integers (the smallest available type to store true/false). + Fractal arrays use complex numbers. + Dispersive coefficient arrays use either float or complex numbers. + Main field arrays use floats. """ - if self.general['precision'] == 'single': - self.dtypes = {'float_or_double': np.float32, - 'complex': np.complex64, - 'cython_float_or_double': cython.float, - 'cython_complex': cython.floatcomplex, - 'C_float_or_double': 'float', - 'C_complex': None} - if self.general['solver'] == 'cuda': - self.dtypes['C_complex'] = 'pycuda::complex' - elif self.general['solver'] == 'opencl': - self.dtypes['C_complex'] = 'cfloat' + if self.general["precision"] == "single": + self.dtypes = { + "float_or_double": np.float32, + "complex": np.complex64, + "cython_float_or_double": cython.float, + "cython_complex": cython.floatcomplex, + "C_float_or_double": "float", + "C_complex": None, + } + if self.general["solver"] == "cuda": + self.dtypes["C_complex"] = "pycuda::complex" + elif self.general["solver"] == "opencl": + self.dtypes["C_complex"] = "cfloat" - elif self.general['precision'] == 'double': - self.dtypes = {'float_or_double': np.float64, - 'complex': np.complex128, - 'cython_float_or_double': cython.double, - 'cython_complex': cython.doublecomplex, - 'C_float_or_double': 'double', - 'C_complex': None} - if self.general['solver'] == 'cuda': - self.dtypes['C_complex'] = 'pycuda::complex' - elif self.general['solver'] == 'opencl': - self.dtypes['C_complex'] = 'cdouble' + elif self.general["precision"] == "double": + self.dtypes = { + "float_or_double": np.float64, + "complex": np.complex128, + "cython_float_or_double": cython.double, + "cython_complex": cython.doublecomplex, + "C_float_or_double": "double", + "C_complex": None, + } + if self.general["solver"] == "cuda": + self.dtypes["C_complex"] = "pycuda::complex" + elif self.general["solver"] == "opencl": + self.dtypes["C_complex"] = "cdouble" def _set_model_start_end(self): """Sets range for number of models to run (internally 0 index).""" diff --git a/gprMax/contexts.py b/gprMax/contexts.py index fbde3698..38a10856 100644 --- a/gprMax/contexts.py +++ b/gprMax/contexts.py @@ -27,8 +27,7 @@ import gprMax.config as config from ._version import __version__, codename from .model_build_run import ModelBuildRun from .solvers import create_G, create_solver -from .utilities.host_info import (print_cuda_info, print_host_info, - print_opencl_info) +from .utilities.host_info import print_cuda_info, print_host_info, print_opencl_info from .utilities.utilities import get_terminal_width, logo, timer logger = logging.getLogger(__name__) @@ -36,19 +35,18 @@ logger = logging.getLogger(__name__) class Context: """Standard context - models are run one after another and each model - can exploit parallelisation using either OpenMP (CPU), CUDA (GPU), or - OpenCL (CPU/GPU). + can exploit parallelisation using either OpenMP (CPU), CUDA (GPU), or + OpenCL (CPU/GPU). """ def __init__(self): - self.model_range = range(config.sim_config.model_start, - config.sim_config.model_end) + self.model_range = range(config.sim_config.model_start, config.sim_config.model_end) self.tsimend = None - self.tsimstart = None + self.tsimstart = None def run(self): """Run the simulation in the correct context. - + Returns: results: dict that can contain useful results/data from simulation. """ @@ -57,10 +55,10 @@ class Context: self.tsimstart = timer() self.print_logo_copyright() print_host_info(config.sim_config.hostinfo) - if config.sim_config.general['solver'] == 'cuda': - print_cuda_info(config.sim_config.devices['devs']) - elif config.sim_config.general['solver'] == 'opencl': - print_opencl_info(config.sim_config.devices['devs']) + if config.sim_config.general["solver"] == "cuda": + print_cuda_info(config.sim_config.devices["devs"]) + elif config.sim_config.general["solver"] == "opencl": + print_opencl_info(config.sim_config.devices["devs"]) # Clear list of model configs. It can be retained when gprMax is # called in a loop, and want to avoid this. @@ -80,7 +78,7 @@ class Context: model = ModelBuildRun(G) model.build() - + if not config.sim_config.args.geometry_only: solver = create_solver(G) model.solve(solver) @@ -92,20 +90,22 @@ class Context: def print_logo_copyright(self): """Prints gprMax logo, version, and copyright/licencing information.""" - logo_copyright = logo(f'{__version__} ({codename})') + logo_copyright = logo(f"{__version__} ({codename})") logger.basic(logo_copyright) def print_sim_time_taken(self): """Prints the total simulation time based on context.""" - s = (f"\n=== Simulation completed in " + - f"{humanize.precisedelta(datetime.timedelta(seconds=self.tsimend - self.tsimstart), format='%0.4f')}") + s = ( + f"\n=== Simulation completed in " + + f"{humanize.precisedelta(datetime.timedelta(seconds=self.tsimend - self.tsimstart), format='%0.4f')}" + ) logger.basic(f"{s} {'=' * (get_terminal_width() - 1 - len(s))}\n") class MPIContext(Context): """Mixed mode MPI/OpenMP/CUDA context - MPI task farm is used to distribute - models, and each model parallelised using either OpenMP (CPU), - CUDA (GPU), or OpenCL (CPU/GPU). + models, and each model parallelised using either OpenMP (CPU), + CUDA (GPU), or OpenCL (CPU/GPU). """ def __init__(self): @@ -120,32 +120,31 @@ class MPIContext(Context): def _run_model(self, **work): """Process for running a single model. - + Args: - work: dict of any additional information that is passed to MPI + work: dict of any additional information that is passed to MPI workers. By default only model number (i) is used. """ # Create configuration for model - config.model_num = work['i'] + config.model_num = work["i"] model_config = config.ModelConfig() # Set GPU deviceID according to worker rank - if config.sim_config.general['solver'] == 'cuda': - model_config.device = {'dev': config.sim_config.devices['devs'][self.rank - 1], - 'snapsgpu2cpu': False} + if config.sim_config.general["solver"] == "cuda": + model_config.device = {"dev": config.sim_config.devices["devs"][self.rank - 1], "snapsgpu2cpu": False} config.model_configs = model_config G = create_G() model = ModelBuildRun(G) model.build() - + if not config.sim_config.args.geometry_only: solver = create_solver(G) model.solve(solver) def run(self): """Specialise how the models are run. - + Returns: results: dict that can contain useful results/data from simulation. """ @@ -154,25 +153,29 @@ class MPIContext(Context): self.tsimstart = timer() self.print_logo_copyright() print_host_info(config.sim_config.hostinfo) - if config.sim_config.general['solver'] == 'cuda': - print_cuda_info(config.sim_config.devices['devs']) - elif config.sim_config.general['solver'] == 'opencl': - print_opencl_info(config.sim_config.devices['devs']) + if config.sim_config.general["solver"] == "cuda": + print_cuda_info(config.sim_config.devices["devs"]) + elif config.sim_config.general["solver"] == "opencl": + print_opencl_info(config.sim_config.devices["devs"]) sys.stdout.flush() # Contruct MPIExecutor executor = self.MPIExecutor(self._run_model, comm=self.comm) # Check GPU resources versus number of MPI tasks - if (executor.is_master() and - config.sim_config.general['solver'] == 'cuda' and - executor.size - 1 > len(config.sim_config.devices['devs'])): - logger.exception('Not enough GPU resources for number of ' - 'MPI tasks requested. Number of MPI tasks ' - 'should be equal to number of GPUs + 1.') + if ( + executor.is_master() + and config.sim_config.general["solver"] == "cuda" + and executor.size - 1 > len(config.sim_config.devices["devs"]) + ): + logger.exception( + "Not enough GPU resources for number of " + "MPI tasks requested. Number of MPI tasks " + "should be equal to number of GPUs + 1." + ) raise ValueError - jobs = [{'i': i} for i in self.model_range] + jobs = [{"i": i} for i in self.model_range] # Send the workers to their work loop executor.start() if executor.is_master(): diff --git a/gprMax/cuda_opencl/knl_common_cuda.tmpl b/gprMax/cuda_opencl/knl_common_cuda.tmpl index 7fdc89a0..e4628b01 100644 --- a/gprMax/cuda_opencl/knl_common_cuda.tmpl +++ b/gprMax/cuda_opencl/knl_common_cuda.tmpl @@ -8,4 +8,4 @@ {% block constmem %} __device__ __constant__ {{REAL}} updatecoeffsE[{{N_updatecoeffsE}}]; __device__ __constant__ {{REAL}} updatecoeffsH[{{N_updatecoeffsH}}]; -{% endblock constmem %} \ No newline at end of file +{% endblock constmem %} diff --git a/gprMax/cuda_opencl/knl_common_opencl.tmpl b/gprMax/cuda_opencl/knl_common_opencl.tmpl index 5c16bfa8..590f7d86 100644 --- a/gprMax/cuda_opencl/knl_common_opencl.tmpl +++ b/gprMax/cuda_opencl/knl_common_opencl.tmpl @@ -6,17 +6,17 @@ {% block constmem %} -__constant {{REAL}} updatecoeffsE[{{N_updatecoeffsE}}] = +__constant {{REAL}} updatecoeffsE[{{N_updatecoeffsE}}] = { {% for i in updatecoeffsE %} {{i}}, {% endfor %} }; -__constant {{REAL}} updatecoeffsH[{{N_updatecoeffsH}}] = +__constant {{REAL}} updatecoeffsH[{{N_updatecoeffsH}}] = { {% for i in updatecoeffsH %} {{i}}, {% endfor %} }; -{% endblock constmem %} \ No newline at end of file +{% endblock constmem %} diff --git a/gprMax/cuda_opencl/knl_fields_updates.py b/gprMax/cuda_opencl/knl_fields_updates.py index a90d4a89..70012904 100644 --- a/gprMax/cuda_opencl/knl_fields_updates.py +++ b/gprMax/cuda_opencl/knl_fields_updates.py @@ -19,19 +19,22 @@ from string import Template update_electric = { - 'args_cuda': Template(""" + "args_cuda": Template( + """ __global__ void update_electric(int NX, int NY, - int NZ, - const unsigned int* __restrict__ ID, - $REAL *Ex, - $REAL *Ey, - $REAL *Ez, - const $REAL* __restrict__ Hx, - const $REAL* __restrict__ Hy, + int NZ, + const unsigned int* __restrict__ ID, + $REAL *Ex, + $REAL *Ey, + $REAL *Ez, + const $REAL* __restrict__ Hx, + const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz) - """), - 'args_opencl': Template(""" + """ + ), + "args_opencl": Template( + """ int NX, int NY, int NZ, @@ -42,8 +45,10 @@ update_electric = { __global const $REAL * restrict Hx, __global const $REAL * restrict Hy, __global const $REAL * restrict Hz - """), - 'func': Template(""" + """ + ), + "func": Template( + """ // Electric field updates - normal materials. // // Args: @@ -65,42 +70,47 @@ update_electric = { // Ex component if ((NY != 1 || NZ != 1) && x >= 0 && x < NX && y > 0 && y < NY && z > 0 && z < NZ) { int materialEx = ID[IDX4D_ID(0,x_ID,y_ID,z_ID)]; - Ex[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEx,0)] * Ex[IDX3D_FIELDS(x,y,z)] + - updatecoeffsE[IDX2D_MAT(materialEx,2)] * (Hz[IDX3D_FIELDS(x,y,z)] - Hz[IDX3D_FIELDS(x,y-1,z)]) - + Ex[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEx,0)] * Ex[IDX3D_FIELDS(x,y,z)] + + updatecoeffsE[IDX2D_MAT(materialEx,2)] * (Hz[IDX3D_FIELDS(x,y,z)] - Hz[IDX3D_FIELDS(x,y-1,z)]) - updatecoeffsE[IDX2D_MAT(materialEx,3)] * (Hy[IDX3D_FIELDS(x,y,z)] - Hy[IDX3D_FIELDS(x,y,z-1)]); } // Ey component if ((NX != 1 || NZ != 1) && x > 0 && x < NX && y >= 0 && y < NY && z > 0 && z < NZ) { int materialEy = ID[IDX4D_ID(1,x_ID,y_ID,z_ID)]; - Ey[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEy,0)] * Ey[IDX3D_FIELDS(x,y,z)] + - updatecoeffsE[IDX2D_MAT(materialEy,3)] * (Hx[IDX3D_FIELDS(x,y,z)] - Hx[IDX3D_FIELDS(x,y,z-1)]) - + Ey[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEy,0)] * Ey[IDX3D_FIELDS(x,y,z)] + + updatecoeffsE[IDX2D_MAT(materialEy,3)] * (Hx[IDX3D_FIELDS(x,y,z)] - Hx[IDX3D_FIELDS(x,y,z-1)]) - updatecoeffsE[IDX2D_MAT(materialEy,1)] * (Hz[IDX3D_FIELDS(x,y,z)] - Hz[IDX3D_FIELDS(x-1,y,z)]); } // Ez component if ((NX != 1 || NY != 1) && x > 0 && x < NX && y > 0 && y < NY && z >= 0 && z < NZ) { int materialEz = ID[IDX4D_ID(2,x_ID,y_ID,z_ID)]; - Ez[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEz,0)] * Ez[IDX3D_FIELDS(x,y,z)] + - updatecoeffsE[IDX2D_MAT(materialEz,1)] * (Hy[IDX3D_FIELDS(x,y,z)] - Hy[IDX3D_FIELDS(x-1,y,z)]) - + Ez[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEz,0)] * Ez[IDX3D_FIELDS(x,y,z)] + + updatecoeffsE[IDX2D_MAT(materialEz,1)] * (Hy[IDX3D_FIELDS(x,y,z)] - Hy[IDX3D_FIELDS(x-1,y,z)]) - updatecoeffsE[IDX2D_MAT(materialEz,2)] * (Hx[IDX3D_FIELDS(x,y,z)] - Hx[IDX3D_FIELDS(x,y-1,z)]); } - """)} + """ + ), +} update_magnetic = { - 'args_cuda': Template(""" + "args_cuda": Template( + """ __global__ void update_magnetic(int NX, int NY, - int NZ, - const unsigned int* __restrict__ ID, - $REAL *Hx, - $REAL *Hy, - $REAL *Hz, - const $REAL* __restrict__ Ex, - const $REAL* __restrict__ Ey, + int NZ, + const unsigned int* __restrict__ ID, + $REAL *Hx, + $REAL *Hy, + $REAL *Hz, + const $REAL* __restrict__ Ex, + const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez) - """), - 'args_opencl': Template(""" + """ + ), + "args_opencl": Template( + """ int NX, int NY, int NZ, @@ -111,8 +121,10 @@ update_magnetic = { __global const $REAL * restrict Ex, __global const $REAL * restrict Ey, __global const $REAL * restrict Ez - """), - 'func': Template(""" + """ + ), + "func": Template( + """ // Magnetic field updates - normal materials. // // Args: @@ -120,7 +132,7 @@ update_magnetic = { // ID, E, H: Access to ID and field component arrays. $CUDA_IDX - + // Convert the linear index to subscripts for 3D field arrays int x = i / ($NY_FIELDS * $NZ_FIELDS); int y = (i % ($NY_FIELDS * $NZ_FIELDS)) / $NZ_FIELDS; @@ -134,47 +146,52 @@ update_magnetic = { // Hx component if (NX != 1 && x > 0 && x < NX && y >= 0 && y < NY && z >= 0 && z < NZ) { int materialHx = ID[IDX4D_ID(3,x_ID,y_ID,z_ID)]; - Hx[IDX3D_FIELDS(x,y,z)] = updatecoeffsH[IDX2D_MAT(materialHx,0)] * Hx[IDX3D_FIELDS(x,y,z)] - - updatecoeffsH[IDX2D_MAT(materialHx,2)] * (Ez[IDX3D_FIELDS(x,y+1,z)] - Ez[IDX3D_FIELDS(x,y,z)]) + + Hx[IDX3D_FIELDS(x,y,z)] = updatecoeffsH[IDX2D_MAT(materialHx,0)] * Hx[IDX3D_FIELDS(x,y,z)] - + updatecoeffsH[IDX2D_MAT(materialHx,2)] * (Ez[IDX3D_FIELDS(x,y+1,z)] - Ez[IDX3D_FIELDS(x,y,z)]) + updatecoeffsH[IDX2D_MAT(materialHx,3)] * (Ey[IDX3D_FIELDS(x,y,z+1)] - Ey[IDX3D_FIELDS(x,y,z)]); } // Hy component if (NY != 1 && x >= 0 && x < NX && y > 0 && y < NY && z >= 0 && z < NZ) { int materialHy = ID[IDX4D_ID(4,x_ID,y_ID,z_ID)]; - Hy[IDX3D_FIELDS(x,y,z)] = updatecoeffsH[IDX2D_MAT(materialHy,0)] * Hy[IDX3D_FIELDS(x,y,z)] - - updatecoeffsH[IDX2D_MAT(materialHy,3)] * (Ex[IDX3D_FIELDS(x,y,z+1)] - Ex[IDX3D_FIELDS(x,y,z)]) + + Hy[IDX3D_FIELDS(x,y,z)] = updatecoeffsH[IDX2D_MAT(materialHy,0)] * Hy[IDX3D_FIELDS(x,y,z)] - + updatecoeffsH[IDX2D_MAT(materialHy,3)] * (Ex[IDX3D_FIELDS(x,y,z+1)] - Ex[IDX3D_FIELDS(x,y,z)]) + updatecoeffsH[IDX2D_MAT(materialHy,1)] * (Ez[IDX3D_FIELDS(x+1,y,z)] - Ez[IDX3D_FIELDS(x,y,z)]); } // Hz component if (NZ != 1 && x >= 0 && x < NX && y >= 0 && y < NY && z > 0 && z < NZ) { int materialHz = ID[IDX4D_ID(5,x_ID,y_ID,z_ID)]; - Hz[IDX3D_FIELDS(x,y,z)] = updatecoeffsH[IDX2D_MAT(materialHz,0)] * Hz[IDX3D_FIELDS(x,y,z)] - - updatecoeffsH[IDX2D_MAT(materialHz,1)] * (Ey[IDX3D_FIELDS(x+1,y,z)] - Ey[IDX3D_FIELDS(x,y,z)]) + + Hz[IDX3D_FIELDS(x,y,z)] = updatecoeffsH[IDX2D_MAT(materialHz,0)] * Hz[IDX3D_FIELDS(x,y,z)] - + updatecoeffsH[IDX2D_MAT(materialHz,1)] * (Ey[IDX3D_FIELDS(x+1,y,z)] - Ey[IDX3D_FIELDS(x,y,z)]) + updatecoeffsH[IDX2D_MAT(materialHz,2)] * (Ex[IDX3D_FIELDS(x,y+1,z)] - Ex[IDX3D_FIELDS(x,y,z)]); } - """)} + """ + ), +} update_electric_dispersive_A = { - 'args_cuda': Template(""" + "args_cuda": Template( + """ __global__ void update_electric_dispersive_A(int NX, int NY, int NZ, - int MAXPOLES, - const $COMPLEX* __restrict__ updatecoeffsdispersive, - $COMPLEX *Tx, - $COMPLEX *Ty, - $COMPLEX *Tz, - const unsigned int* __restrict__ ID, - $REAL *Ex, - $REAL *Ey, - $REAL *Ez, - const $REAL* __restrict__ Hx, - const $REAL* __restrict__ Hy, + int MAXPOLES, + const $COMPLEX* __restrict__ updatecoeffsdispersive, + $COMPLEX *Tx, + $COMPLEX *Ty, + $COMPLEX *Tz, + const unsigned int* __restrict__ ID, + $REAL *Ex, + $REAL *Ey, + $REAL *Ez, + const $REAL* __restrict__ Hx, + const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz) - """), - 'args_opencl': Template(""" + """ + ), + "args_opencl": Template( + """ int NX, int NY, int NZ, @@ -190,17 +207,19 @@ update_electric_dispersive_A = { __global const $REAL* restrict Hx, __global const $REAL* restrict Hy, __global const $REAL* restrict Hz - """), - 'func': Template(""" + """ + ), + "func": Template( + """ // Electric field updates - dispersive materials - part A of updates to electric - // field values when dispersive materials + // field values when dispersive materials // (with multiple poles) are present. // // Args: // NX, NY, NZ: Number of cells of the model domain. // MAXPOLES: Maximum number of dispersive material poles present in model. - // updatedispersivecoeffs, T, ID, E, H: Access to update coefficients, - // dispersive, ID and field + // updatedispersivecoeffs, T, ID, E, H: Access to update coefficients, + // dispersive, ID and field // component arrays. @@ -227,12 +246,12 @@ update_electric_dispersive_A = { $REAL phi = 0; for (int pole = 0; pole < MAXPOLES; pole++) { phi = phi + updatecoeffsdispersive[IDX2D_MATDISP(materialEx,pole*3)]$REALFUNC * Tx[IDX4D_T(pole,x_T,y_T,z_T)]$REALFUNC; - Tx[IDX4D_T(pole,x_T,y_T,z_T)] = updatecoeffsdispersive[IDX2D_MATDISP(materialEx,1+(pole*3))] * Tx[IDX4D_T(pole,x_T,y_T,z_T)] + + Tx[IDX4D_T(pole,x_T,y_T,z_T)] = updatecoeffsdispersive[IDX2D_MATDISP(materialEx,1+(pole*3))] * Tx[IDX4D_T(pole,x_T,y_T,z_T)] + updatecoeffsdispersive[IDX2D_MATDISP(materialEx,2+(pole*3))] * Ex[IDX3D_FIELDS(x,y,z)]; } - Ex[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEx,0)] * Ex[IDX3D_FIELDS(x,y,z)] + - updatecoeffsE[IDX2D_MAT(materialEx,2)] * (Hz[IDX3D_FIELDS(x,y,z)] - Hz[IDX3D_FIELDS(x,y-1,z)]) - - updatecoeffsE[IDX2D_MAT(materialEx,3)] * (Hy[IDX3D_FIELDS(x,y,z)] - Hy[IDX3D_FIELDS(x,y,z-1)]) - + Ex[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEx,0)] * Ex[IDX3D_FIELDS(x,y,z)] + + updatecoeffsE[IDX2D_MAT(materialEx,2)] * (Hz[IDX3D_FIELDS(x,y,z)] - Hz[IDX3D_FIELDS(x,y-1,z)]) - + updatecoeffsE[IDX2D_MAT(materialEx,3)] * (Hy[IDX3D_FIELDS(x,y,z)] - Hy[IDX3D_FIELDS(x,y,z-1)]) - updatecoeffsE[IDX2D_MAT(materialEx,4)] * phi; } @@ -242,12 +261,12 @@ update_electric_dispersive_A = { $REAL phi = 0; for (int pole = 0; pole < MAXPOLES; pole++) { phi = phi + updatecoeffsdispersive[IDX2D_MATDISP(materialEy,pole*3)]$REALFUNC * Ty[IDX4D_T(pole,x_T,y_T,z_T)]$REALFUNC; - Ty[IDX4D_T(pole,x_T,y_T,z_T)] = updatecoeffsdispersive[IDX2D_MATDISP(materialEy,1+(pole*3))] * Ty[IDX4D_T(pole,x_T,y_T,z_T)] + + Ty[IDX4D_T(pole,x_T,y_T,z_T)] = updatecoeffsdispersive[IDX2D_MATDISP(materialEy,1+(pole*3))] * Ty[IDX4D_T(pole,x_T,y_T,z_T)] + updatecoeffsdispersive[IDX2D_MATDISP(materialEy,2+(pole*3))] * Ey[IDX3D_FIELDS(x,y,z)]; } - Ey[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEy,0)] * Ey[IDX3D_FIELDS(x,y,z)] + - updatecoeffsE[IDX2D_MAT(materialEy,3)] * (Hx[IDX3D_FIELDS(x,y,z)] - Hx[IDX3D_FIELDS(x,y,z-1)]) - - updatecoeffsE[IDX2D_MAT(materialEy,1)] * (Hz[IDX3D_FIELDS(x,y,z)] - Hz[IDX3D_FIELDS(x-1,y,z)]) - + Ey[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEy,0)] * Ey[IDX3D_FIELDS(x,y,z)] + + updatecoeffsE[IDX2D_MAT(materialEy,3)] * (Hx[IDX3D_FIELDS(x,y,z)] - Hx[IDX3D_FIELDS(x,y,z-1)]) - + updatecoeffsE[IDX2D_MAT(materialEy,1)] * (Hz[IDX3D_FIELDS(x,y,z)] - Hz[IDX3D_FIELDS(x-1,y,z)]) - updatecoeffsE[IDX2D_MAT(materialEy,4)] * phi; } @@ -257,32 +276,37 @@ update_electric_dispersive_A = { $REAL phi = 0; for (int pole = 0; pole < MAXPOLES; pole++) { phi = phi + updatecoeffsdispersive[IDX2D_MATDISP(materialEz,pole*3)]$REALFUNC * Tz[IDX4D_T(pole,x_T,y_T,z_T)]$REALFUNC; - Tz[IDX4D_T(pole,x_T,y_T,z_T)] = updatecoeffsdispersive[IDX2D_MATDISP(materialEz,1+(pole*3))] * Tz[IDX4D_T(pole,x_T,y_T,z_T)] + + Tz[IDX4D_T(pole,x_T,y_T,z_T)] = updatecoeffsdispersive[IDX2D_MATDISP(materialEz,1+(pole*3))] * Tz[IDX4D_T(pole,x_T,y_T,z_T)] + updatecoeffsdispersive[IDX2D_MATDISP(materialEz,2+(pole*3))] * Ez[IDX3D_FIELDS(x,y,z)]; } - Ez[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEz,0)] * Ez[IDX3D_FIELDS(x,y,z)] + - updatecoeffsE[IDX2D_MAT(materialEz,1)] * (Hy[IDX3D_FIELDS(x,y,z)] - Hy[IDX3D_FIELDS(x-1,y,z)]) - - updatecoeffsE[IDX2D_MAT(materialEz,2)] * (Hx[IDX3D_FIELDS(x,y,z)] - Hx[IDX3D_FIELDS(x,y-1,z)]) - + Ez[IDX3D_FIELDS(x,y,z)] = updatecoeffsE[IDX2D_MAT(materialEz,0)] * Ez[IDX3D_FIELDS(x,y,z)] + + updatecoeffsE[IDX2D_MAT(materialEz,1)] * (Hy[IDX3D_FIELDS(x,y,z)] - Hy[IDX3D_FIELDS(x-1,y,z)]) - + updatecoeffsE[IDX2D_MAT(materialEz,2)] * (Hx[IDX3D_FIELDS(x,y,z)] - Hx[IDX3D_FIELDS(x,y-1,z)]) - updatecoeffsE[IDX2D_MAT(materialEz,4)] * phi; } - """)} + """ + ), +} update_electric_dispersive_B = { - 'args_cuda': Template(""" + "args_cuda": Template( + """ __global__ void update_electric_dispersive_B(int NX, int NY, int NZ, - int MAXPOLES, - const $COMPLEX* __restrict__ updatecoeffsdispersive, - $COMPLEX *Tx, - $COMPLEX *Ty, - $COMPLEX *Tz, - const unsigned int* __restrict__ ID, - const $REAL* __restrict__ Ex, - const $REAL* __restrict__ Ey, + int MAXPOLES, + const $COMPLEX* __restrict__ updatecoeffsdispersive, + $COMPLEX *Tx, + $COMPLEX *Ty, + $COMPLEX *Tz, + const unsigned int* __restrict__ ID, + const $REAL* __restrict__ Ex, + const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez) - """), - 'args_opencl': Template(""" + """ + ), + "args_opencl": Template( + """ int NX, int NY, int NZ, @@ -295,17 +319,19 @@ update_electric_dispersive_B = { __global const $REAL* restrict Ex, __global const $REAL* restrict Ey, __global const $REAL* restrict Ez - """), - 'func': Template(""" + """ + ), + "func": Template( + """ // Electric field updates - dispersive materials - part B of updates to electric - // field values when dispersive materials + // field values when dispersive materials // (with multiple poles) are present. // // Args: // NX, NY, NZ: Number of cells of the model domain. // MAXPOLES: Maximum number of dispersive material poles present in model. - // updatedispersivecoeffs, T, ID, E, H: Access to update coefficients, - // dispersive, ID and field + // updatedispersivecoeffs, T, ID, E, H: Access to update coefficients, + // dispersive, ID and field // component arrays. @@ -325,12 +351,12 @@ update_electric_dispersive_B = { int z_T = ((i % ($NX_T * $NY_T * $NZ_T)) % ($NY_T * $NZ_T)) % $NZ_T; $CUDA_IDX - + // Ex component if ((NY != 1 || NZ != 1) && x >= 0 && x < NX && y > 0 && y < NY && z > 0 && z < NZ) { int materialEx = ID[IDX4D_ID(0,x_ID,y_ID,z_ID)]; for (int pole = 0; pole < MAXPOLES; pole++) { - Tx[IDX4D_T(pole,x_T,y_T,z_T)] = Tx[IDX4D_T(pole,x_T,y_T,z_T)] - + Tx[IDX4D_T(pole,x_T,y_T,z_T)] = Tx[IDX4D_T(pole,x_T,y_T,z_T)] - updatecoeffsdispersive[IDX2D_MATDISP(materialEx,2+(pole*3))] * Ex[IDX3D_FIELDS(x,y,z)]; } } @@ -339,7 +365,7 @@ update_electric_dispersive_B = { if ((NX != 1 || NZ != 1) && x > 0 && x < NX && y >= 0 && y < NY && z > 0 && z < NZ) { int materialEy = ID[IDX4D_ID(1,x_ID,y_ID,z_ID)]; for (int pole = 0; pole < MAXPOLES; pole++) { - Ty[IDX4D_T(pole,x_T,y_T,z_T)] = Ty[IDX4D_T(pole,x_T,y_T,z_T)] - + Ty[IDX4D_T(pole,x_T,y_T,z_T)] = Ty[IDX4D_T(pole,x_T,y_T,z_T)] - updatecoeffsdispersive[IDX2D_MATDISP(materialEy,2+(pole*3))] * Ey[IDX3D_FIELDS(x,y,z)]; } } @@ -348,8 +374,10 @@ update_electric_dispersive_B = { if ((NX != 1 || NY != 1) && x > 0 && x < NX && y > 0 && y < NY && z >= 0 && z < NZ) { int materialEz = ID[IDX4D_ID(2,x_ID,y_ID,z_ID)]; for (int pole = 0; pole < MAXPOLES; pole++) { - Tz[IDX4D_T(pole,x_T,y_T,z_T)] = Tz[IDX4D_T(pole,x_T,y_T,z_T)] - + Tz[IDX4D_T(pole,x_T,y_T,z_T)] = Tz[IDX4D_T(pole,x_T,y_T,z_T)] - updatecoeffsdispersive[IDX2D_MATDISP(materialEz,2+(pole*3))] * Ez[IDX3D_FIELDS(x,y,z)]; } } - """)} \ No newline at end of file + """ + ), +} diff --git a/gprMax/cuda_opencl/knl_pml_updates_electric_HORIPML.py b/gprMax/cuda_opencl/knl_pml_updates_electric_HORIPML.py index 1749d6a5..8974a3a6 100644 --- a/gprMax/cuda_opencl/knl_pml_updates_electric_HORIPML.py +++ b/gprMax/cuda_opencl/knl_pml_updates_electric_HORIPML.py @@ -18,28 +18,32 @@ from string import Template -x_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, - const unsigned int* __restrict__ ID, - const $REAL* __restrict__ Ex, $REAL *Ey, - $REAL *Ez, - const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, - $REAL *PHI2, +x_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, + const unsigned int* __restrict__ ID, + const $REAL* __restrict__ Ex, $REAL *Ey, + $REAL *Ez, + const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -67,32 +71,37 @@ x_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -y_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, +y_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, const unsigned int* __restrict__ ID, $REAL *Ex, - const $REAL* __restrict__ Ey, - $REAL *Ez, - const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, - $REAL *PHI2, + const $REAL* __restrict__ Ey, + $REAL *Ez, + const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -120,32 +129,37 @@ y_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -z_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, +z_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, const unsigned int* __restrict__ ID, $REAL *Ex, - $REAL *Ey, - const $REAL* __restrict__ Ez, - const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, - $REAL *PHI2, + $REAL *Ey, + const $REAL* __restrict__ Ez, + const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -173,12 +187,15 @@ z_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -order1_xminus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order1_xminus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Ey and Ez field components for the xminus slab. // // Args: @@ -224,7 +241,7 @@ order1_xminus = {'args_cuda': x_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * (RA01 * dHz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHz; } @@ -244,15 +261,19 @@ order1_xminus = {'args_cuda': x_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * (RA01 * dHy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHy; } -""")} +""" + ), +} -order2_xminus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order2_xminus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Ey and Ez field components for the xminus slab. // // Args: @@ -303,10 +324,10 @@ order2_xminus = {'args_cuda': x_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * - (RA01 * dHz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + (RA01 * dHz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dHz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHz; } @@ -331,18 +352,22 @@ order2_xminus = {'args_cuda': x_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * - (RA01 * dHy + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * + (RA01 * dHy + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dHy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHy; } -""")} +""" + ), +} -order1_xplus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order1_xplus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Ey and Ez field components for the xplus slab. // // Args: @@ -388,7 +413,7 @@ order1_xplus = {'args_cuda': x_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * (RA01 * dHz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHz; } @@ -408,15 +433,19 @@ order1_xplus = {'args_cuda': x_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * (RA01 * dHy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHy; } -""")} +""" + ), +} -order2_xplus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order2_xplus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Ey and Ez field components for the xplus slab. // // Args: @@ -467,9 +496,9 @@ order2_xplus = {'args_cuda': x_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * (RA01 * dHz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dHz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHz; } @@ -494,18 +523,22 @@ order2_xplus = {'args_cuda': x_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * - (RA01 * dHy + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * + (RA01 * dHy + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dHy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHy; } -""")} +""" + ), +} -order1_yminus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order1_yminus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ez field components for the yminus slab. // // Args: @@ -551,7 +584,7 @@ order1_yminus = {'args_cuda': y_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * (RA01 * dHz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHz; } @@ -571,15 +604,19 @@ order1_yminus = {'args_cuda': y_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * (RA01 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx; } -""")} +""" + ), +} -order2_yminus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order2_yminus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ez field components for the yminus slab. // // Args: @@ -630,10 +667,10 @@ order2_yminus = {'args_cuda': y_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * - (RA01 * dHz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * + (RA01 * dHz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dHz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHz; } @@ -658,18 +695,22 @@ order2_yminus = {'args_cuda': y_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * - (RA01 * dHx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + (RA01 * dHx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx; } -""")} +""" + ), +} -order1_yplus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order1_yplus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ez field components for the yplus slab. // // Args: @@ -715,7 +756,7 @@ order1_yplus = {'args_cuda': y_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * (RA01 * dHz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHz; } @@ -735,15 +776,19 @@ order1_yplus = {'args_cuda': y_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * (RA01 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx; } -""")} +""" + ), +} -order2_yplus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order2_yplus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ez field components for the yplus slab. // // Args: @@ -794,10 +839,10 @@ order2_yplus = {'args_cuda': y_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * - (RA01 * dHz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * + (RA01 * dHz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dHz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHz; } @@ -822,18 +867,22 @@ order2_yplus = {'args_cuda': y_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * - (RA01 * dHx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + (RA01 * dHx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx; } -""")} +""" + ), +} -order1_zminus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order1_zminus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ey field components for the zminus slab. // // Args: @@ -879,7 +928,7 @@ order1_zminus = {'args_cuda': z_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * (RA01 * dHy + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHy; } @@ -899,15 +948,19 @@ order1_zminus = {'args_cuda': z_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * (RA01 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx; } -""")} +""" + ), +} -order2_zminus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order2_zminus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ey field components for the zminus slab. // // Args: @@ -958,10 +1011,10 @@ order2_zminus = {'args_cuda': z_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * - (RA01 * dHy + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + (RA01 * dHy + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dHy + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHy; } @@ -986,18 +1039,22 @@ order2_zminus = {'args_cuda': z_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * - (RA01 * dHx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * + (RA01 * dHx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx; } -""")} +""" + ), +} -order1_zplus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order1_zplus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ey field components for the zplus slab. // // Args: @@ -1043,7 +1100,7 @@ order1_zplus = {'args_cuda': z_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * (RA01 * dHy + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHy; } @@ -1063,15 +1120,19 @@ order1_zplus = {'args_cuda': z_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * (RA01 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx; } -""")} +""" + ), +} -order2_zplus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order2_zplus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ey field components for the zplus slab. // // Args: @@ -1122,10 +1183,10 @@ order2_zplus = {'args_cuda': z_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * - (RA01 * dHy + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + (RA01 * dHy + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dHy + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dHy; } @@ -1150,11 +1211,13 @@ order2_zplus = {'args_cuda': z_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * - (RA01 * dHx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * + (RA01 * dHx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dHx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dHx; } -""")} \ No newline at end of file +""" + ), +} diff --git a/gprMax/cuda_opencl/knl_pml_updates_electric_MRIPML.py b/gprMax/cuda_opencl/knl_pml_updates_electric_MRIPML.py index 3238815d..1effe788 100644 --- a/gprMax/cuda_opencl/knl_pml_updates_electric_MRIPML.py +++ b/gprMax/cuda_opencl/knl_pml_updates_electric_MRIPML.py @@ -18,28 +18,32 @@ from string import Template -x_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, - const unsigned int* __restrict__ ID, - const $REAL* __restrict__ Ex, $REAL *Ey, - $REAL *Ez, - const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, - $REAL *PHI2, +x_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, + const unsigned int* __restrict__ ID, + const $REAL* __restrict__ Ex, $REAL *Ey, + $REAL *Ez, + const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -67,32 +71,37 @@ x_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -y_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, +y_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, const unsigned int* __restrict__ ID, $REAL *Ex, - const $REAL* __restrict__ Ey, - $REAL *Ez, - const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, - $REAL *PHI2, + const $REAL* __restrict__ Ey, + $REAL *Ez, + const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -120,32 +129,37 @@ y_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -z_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, +z_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, const unsigned int* __restrict__ ID, $REAL *Ex, - $REAL *Ey, - const $REAL* __restrict__ Ez, - const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, - $REAL *PHI2, + $REAL *Ey, + const $REAL* __restrict__ Ez, + const $REAL* __restrict__ Hx, const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -173,12 +187,15 @@ z_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -order1_xminus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order1_xminus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Ey and Ez field components for the xminus slab. // // Args: @@ -226,9 +243,9 @@ order1_xminus = {'args_cuda': x_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * (IRA1 * dHz - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHz - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHz - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -249,16 +266,20 @@ order1_xminus = {'args_cuda': x_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * (IRA1 * dHy - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHy - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHy - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_xminus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order2_xminus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Ey and Ez field components for the xminus slab. // // Args: @@ -311,7 +332,7 @@ order2_xminus = {'args_cuda': x_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * (IRA1 * dHz - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dHz - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dHz - Psi1); @@ -339,16 +360,20 @@ order2_xminus = {'args_cuda': x_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * (IRA1 * dHy - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHy - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHy - Psi2); } -""")} +""" + ), +} -order1_xplus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order1_xplus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Ey and Ez field components for the xplus slab. // // Args: @@ -396,9 +421,9 @@ order1_xplus = {'args_cuda': x_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * (IRA1 * dHz - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHz - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHz - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -419,16 +444,20 @@ order1_xplus = {'args_cuda': x_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * (IRA1 * dHy - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHy - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHy - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_xplus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order2_xplus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Ey and Ez field components for the xplus slab. // // Args: @@ -481,7 +510,7 @@ order2_xplus = {'args_cuda': x_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * (IRA1 * dHz - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dHz - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dHz - Psi1); @@ -509,16 +538,20 @@ order2_xplus = {'args_cuda': x_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii-1,jj,kk)]) / dx; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEz,4)] * (IRA1 * dHy - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHy - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHy - Psi2); } -""")} +""" + ), +} -order1_yminus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order1_yminus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ez field components for the yminus slab. // // Args: @@ -566,9 +599,9 @@ order1_yminus = {'args_cuda': y_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * (IRA1 * dHz - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHz - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHz - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -589,16 +622,20 @@ order1_yminus = {'args_cuda': y_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * (IRA1 * dHx - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_yminus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order2_yminus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ez field components for the yminus slab. // // Args: @@ -651,7 +688,7 @@ order2_yminus = {'args_cuda': y_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * (IRA1 * dHz - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dHz - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dHz - Psi1); @@ -679,16 +716,20 @@ order2_yminus = {'args_cuda': y_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * (IRA1 * dHx - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHx - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHx - Psi2); } -""")} +""" + ), +} -order1_yplus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order1_yplus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ez field components for the yplus slab. // // Args: @@ -736,9 +777,9 @@ order1_yplus = {'args_cuda': y_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * (IRA1 * dHz - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHz - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHz - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -759,16 +800,20 @@ order1_yplus = {'args_cuda': y_args['cuda'], // Ez materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * (IRA1 * dHx - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_yplus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order2_yplus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ez field components for the yplus slab. // // Args: @@ -821,7 +866,7 @@ order2_yplus = {'args_cuda': y_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHz = (Hz[IDX3D_FIELDS(ii,jj,kk)] - Hz[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEx,4)] * (IRA1 * dHz - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dHz - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dHz - Psi1); @@ -849,16 +894,20 @@ order2_yplus = {'args_cuda': y_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialEz = ID[IDX4D_ID(2,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj-1,kk)]) / dy; - Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(ii,jj,kk)] = Ez[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * (IRA1 * dHx - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHx - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHx - Psi2); } -""")} +""" + ), +} -order1_zminus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order1_zminus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ey field components for the zminus slab. // // Args: @@ -906,9 +955,9 @@ order1_zminus = {'args_cuda': z_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * (IRA1 * dHy - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHy - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHy - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -929,16 +978,20 @@ order1_zminus = {'args_cuda': z_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * (IRA1 * dHx - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_zminus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order2_zminus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ey field components for the zminus slab. // // Args: @@ -991,7 +1044,7 @@ order2_zminus = {'args_cuda': z_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * (IRA1 * dHy - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dHy - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dHy - Psi1); @@ -1019,16 +1072,20 @@ order2_zminus = {'args_cuda': z_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * (IRA1 * dHx - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHx - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHx - Psi2); } -""")} +""" + ), +} -order1_zplus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order1_zplus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ey field components for the zplus slab. // // Args: @@ -1076,9 +1133,9 @@ order1_zplus = {'args_cuda': z_args['cuda'], // Ex materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * (IRA1 * dHy - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHy - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dHy - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -1099,16 +1156,20 @@ order1_zplus = {'args_cuda': z_args['cuda'], // Ey materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * (IRA1 * dHx - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dHx - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_zplus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order2_zplus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Ex and Ey field components for the zplus slab. // // Args: @@ -1161,7 +1222,7 @@ order2_zplus = {'args_cuda': z_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialEx = ID[IDX4D_ID(0,ii,jj,kk)]; dHy = (Hy[IDX3D_FIELDS(ii,jj,kk)] - Hy[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(ii,jj,kk)] = Ex[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * (IRA1 * dHy - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dHy - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dHy - Psi1); @@ -1189,9 +1250,11 @@ order2_zplus = {'args_cuda': z_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialEy = ID[IDX4D_ID(1,ii,jj,kk)]; dHx = (Hx[IDX3D_FIELDS(ii,jj,kk)] - Hx[IDX3D_FIELDS(ii,jj,kk-1)]) / dz; - Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(ii,jj,kk)] = Ey[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsE[IDX2D_MAT(materialEy,4)] * (IRA1 * dHx - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dHx - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dHx - Psi2); } -""")} \ No newline at end of file +""" + ), +} diff --git a/gprMax/cuda_opencl/knl_pml_updates_magnetic_HORIPML.py b/gprMax/cuda_opencl/knl_pml_updates_magnetic_HORIPML.py index 1579af40..3f8bc823 100644 --- a/gprMax/cuda_opencl/knl_pml_updates_magnetic_HORIPML.py +++ b/gprMax/cuda_opencl/knl_pml_updates_magnetic_HORIPML.py @@ -18,28 +18,32 @@ from string import Template -x_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, - const unsigned int* __restrict__ ID, - const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, const $REAL* __restrict__ Hx, $REAL *Hy, - $REAL *Hz, - $REAL *PHI1, - $REAL *PHI2, +x_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, + const unsigned int* __restrict__ ID, + const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, const $REAL* __restrict__ Hx, $REAL *Hy, + $REAL *Hz, + $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -67,33 +71,38 @@ x_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -y_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, - const unsigned int* __restrict__ ID, - const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, +y_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, + const unsigned int* __restrict__ ID, + const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, $REAL *Hx, - const $REAL* __restrict__ Hy, - $REAL *Hz, - $REAL *PHI1, - $REAL *PHI2, + const $REAL* __restrict__ Hy, + $REAL *Hz, + $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -121,33 +130,38 @@ y_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -z_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, - const unsigned int* __restrict__ ID, - const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, +z_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, + const unsigned int* __restrict__ ID, + const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, $REAL *Hx, $REAL *Hy, - const $REAL* __restrict__ Hz, - $REAL *PHI1, - $REAL *PHI2, + const $REAL* __restrict__ Hz, + $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -175,12 +189,15 @@ z_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -order1_xminus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order1_xminus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Hy and Hz field components for the xminus slab. // // Args: @@ -191,7 +208,7 @@ order1_xminus = {'args_cuda': x_args['cuda'], // d: Spatial discretisation, e.g. dx, dy or dz $CUDA_IDX - + // Convert the linear index to subscripts for PML PHI1 (4D) arrays int p1 = i / (NX_PHI1 * NY_PHI1 * NZ_PHI1); int i1 = (i % (NX_PHI1 * NY_PHI1 * NZ_PHI1)) / (NY_PHI1 * NZ_PHI1); @@ -226,7 +243,7 @@ order1_xminus = {'args_cuda': x_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii+1,jj,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * (RA01 * dEz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEz; } @@ -246,15 +263,19 @@ order1_xminus = {'args_cuda': x_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii+1,jj,kk)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * (RA01 * dEy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEy; } -""")} +""" + ), +} -order2_xminus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order2_xminus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Hy and Hz field components for the xminus slab. // // Args: @@ -305,10 +326,10 @@ order2_xminus = {'args_cuda': x_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii+1,jj,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * - (RA01 * dEz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * + (RA01 * dEz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dEz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEz; } @@ -333,18 +354,22 @@ order2_xminus = {'args_cuda': x_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii+1,jj,kk)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * - (RA01 * dEy + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * + (RA01 * dEy + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dEy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEy; } -""")} +""" + ), +} -order1_xplus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order1_xplus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Hy and Hz field components for the xplus slab. // // Args: @@ -390,7 +415,7 @@ order1_xplus = {'args_cuda': x_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii+1,jj,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * (RA01 * dEz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEz; } @@ -410,15 +435,19 @@ order1_xplus = {'args_cuda': x_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii+1,jj,kk)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * (RA01 * dEy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEy; } -""")} +""" + ), +} -order2_xplus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order2_xplus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Hy and Hz field components for the xplus slab. // // Args: @@ -469,10 +498,10 @@ order2_xplus = {'args_cuda': x_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii+1,jj,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * - (RA01 * dEz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * + (RA01 * dEz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dEz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEz; } @@ -497,18 +526,22 @@ order2_xplus = {'args_cuda': x_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii+1,jj,kk)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * - (RA01 * dEy + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * + (RA01 * dEy + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dEy + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEy; } -""")} +""" + ), +} -order1_yminus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order1_yminus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hz field components for the yminus slab. // // Args: @@ -554,7 +587,7 @@ order1_yminus = {'args_cuda': y_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii,jj+1,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * (RA01 * dEz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEz; } @@ -574,15 +607,19 @@ order1_yminus = {'args_cuda': y_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj+1,kk)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * (RA01 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx; } -""")} +""" + ), +} -order2_yminus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order2_yminus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hz field components for the yminus slab. // // Args: @@ -633,10 +670,10 @@ order2_yminus = {'args_cuda': y_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii,jj+1,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * - (RA01 * dEz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * + (RA01 * dEz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dEz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEz; } @@ -661,17 +698,21 @@ order2_yminus = {'args_cuda': y_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj+1,kk)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * (RA01 * dEx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx; } -""")} +""" + ), +} -order1_yplus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order1_yplus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hz field components for the yplus slab. // // Args: @@ -717,7 +758,7 @@ order1_yplus = {'args_cuda': y_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii,jj+1,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * (RA01 * dEz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEz; } @@ -737,15 +778,19 @@ order1_yplus = {'args_cuda': y_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj+1,kk)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * (RA01 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx; } -""")} +""" + ), +} -order2_yplus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order2_yplus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hz field components for the yplus slab. // // Args: @@ -796,9 +841,9 @@ order2_yplus = {'args_cuda': y_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii,jj+1,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * (RA01 * dEz + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dEz + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEz; } @@ -823,17 +868,21 @@ order2_yplus = {'args_cuda': y_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj+1,kk)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * (RA01 * dEx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx; } -""")} +""" + ), +} -order1_zminus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order1_zminus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hy field components for the zminus slab. // // Args: @@ -879,7 +928,7 @@ order1_zminus = {'args_cuda': z_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii,jj,kk+1)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * (RA01 * dEy + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEy; } @@ -899,15 +948,19 @@ order1_zminus = {'args_cuda': z_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj,kk+1)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * (RA01 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx; } -""")} +""" + ), +} -order2_zminus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order2_zminus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hy field components for the zminus slab. // // Args: @@ -958,10 +1011,10 @@ order2_zminus = {'args_cuda': z_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii,jj,kk+1)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * - (RA01 * dEy + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * + (RA01 * dEy + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dEy + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEy; } @@ -986,18 +1039,22 @@ order2_zminus = {'args_cuda': z_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj,kk+1)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * - (RA01 * dEx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * + (RA01 * dEx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx; } -""")} +""" + ), +} -order1_zplus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order1_zplus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hy field components for the zplus slab. // // Args: @@ -1043,7 +1100,7 @@ order1_zplus = {'args_cuda': z_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii,jj,kk+1)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * (RA01 * dEy + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEy; } @@ -1063,15 +1120,19 @@ order1_zplus = {'args_cuda': z_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj,kk+1)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * (RA01 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx; } -""")} +""" + ), +} -order2_zplus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order2_zplus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hy field components for the zplus slab. // // Args: @@ -1122,10 +1183,10 @@ order2_zplus = {'args_cuda': z_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii,jj,kk+1)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * - (RA01 * dEy + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * + (RA01 * dEy + RA1 * RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]); - PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * + PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] - RF1 * (RA0 * dEy + RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] - RF0 * dEy; } @@ -1150,12 +1211,13 @@ order2_zplus = {'args_cuda': z_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj,kk+1)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * - (RA01 * dEx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * + (RA01 * dEx + RA1 * RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]); - PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * + PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] - RF1 * (RA0 * dEx + RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] - RF0 * dEx; } -""")} - +""" + ), +} diff --git a/gprMax/cuda_opencl/knl_pml_updates_magnetic_MRIPML.py b/gprMax/cuda_opencl/knl_pml_updates_magnetic_MRIPML.py index e1de2bc1..05ca8b9e 100644 --- a/gprMax/cuda_opencl/knl_pml_updates_magnetic_MRIPML.py +++ b/gprMax/cuda_opencl/knl_pml_updates_magnetic_MRIPML.py @@ -18,28 +18,32 @@ from string import Template -x_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, - const unsigned int* __restrict__ ID, - const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, const $REAL* __restrict__ Hx, $REAL *Hy, - $REAL *Hz, - $REAL *PHI1, - $REAL *PHI2, +x_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, + const unsigned int* __restrict__ ID, + const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, const $REAL* __restrict__ Hx, $REAL *Hy, + $REAL *Hz, + $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -67,33 +71,38 @@ x_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -y_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, - const unsigned int* __restrict__ ID, - const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, +y_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, + const unsigned int* __restrict__ ID, + const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, $REAL *Hx, - const $REAL* __restrict__ Hy, - $REAL *Hz, - $REAL *PHI1, - $REAL *PHI2, + const $REAL* __restrict__ Hy, + $REAL *Hz, + $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -121,33 +130,38 @@ y_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -z_args = {'cuda': Template(""" - __global__ void $FUNC(int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int NX_PHI1, - int NY_PHI1, - int NZ_PHI1, - int NX_PHI2, - int NY_PHI2, - int NZ_PHI2, - int NY_R, - const unsigned int* __restrict__ ID, - const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, +z_args = { + "cuda": Template( + """ + __global__ void $FUNC(int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int NX_PHI1, + int NY_PHI1, + int NZ_PHI1, + int NX_PHI2, + int NY_PHI2, + int NZ_PHI2, + int NY_R, + const unsigned int* __restrict__ ID, + const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, const $REAL* __restrict__ Ez, $REAL *Hx, $REAL *Hy, - const $REAL* __restrict__ Hz, - $REAL *PHI1, - $REAL *PHI2, + const $REAL* __restrict__ Hz, + $REAL *PHI1, + $REAL *PHI2, const $REAL* __restrict__ RA, const $REAL* __restrict__ RB, const $REAL* __restrict__ RE, const $REAL* __restrict__ RF, $REAL d) - """), - 'opencl': Template(""" + """ + ), + "opencl": Template( + """ int xs, int xf, int ys, @@ -175,12 +189,15 @@ z_args = {'cuda': Template(""" __global const $REAL* restrict RE, __global const $REAL* restrict RF, $REAL d - """) - } + """ + ), +} -order1_xminus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order1_xminus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Hy and Hz field components for the xminus slab. // // Args: @@ -191,7 +208,7 @@ order1_xminus = {'args_cuda': x_args['cuda'], // d: Spatial discretisation, e.g. dx, dy or dz $CUDA_IDX - + // Convert the linear index to subscripts for PML PHI1 (4D) arrays int p1 = i / (NX_PHI1 * NY_PHI1 * NZ_PHI1); int i1 = (i % (NX_PHI1 * NY_PHI1 * NZ_PHI1)) / (NY_PHI1 * NZ_PHI1); @@ -228,9 +245,9 @@ order1_xminus = {'args_cuda': x_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii+1,jj,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * (IRA1 * dEz - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEz - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEz - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -251,16 +268,20 @@ order1_xminus = {'args_cuda': x_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii+1,jj,kk)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * (IRA1 * dEy - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEy - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEy - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_xminus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order2_xminus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Hy and Hz field components for the xminus slab. // // Args: @@ -313,7 +334,7 @@ order2_xminus = {'args_cuda': x_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii+1,jj,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * (IRA1 * dEz - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dEz - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dEz - Psi1); @@ -341,16 +362,20 @@ order2_xminus = {'args_cuda': x_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii+1,jj,kk)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * (IRA1 * dEy - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEy - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEy - Psi2); } -""")} +""" + ), +} -order1_xplus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order1_xplus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Hy and Hz field components for the xplus slab. // // Args: @@ -398,9 +423,9 @@ order1_xplus = {'args_cuda': x_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii+1,jj,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * (IRA1 * dEz - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEz - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEz - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -421,16 +446,20 @@ order1_xplus = {'args_cuda': x_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii+1,jj,kk)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * (IRA1 * dEy - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEy - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEy - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_xplus = {'args_cuda': x_args['cuda'], - 'args_opencl': x_args['opencl'], - 'func': Template(""" +order2_xplus = { + "args_cuda": x_args["cuda"], + "args_opencl": x_args["opencl"], + "func": Template( + """ // This function updates the Hy and Hz field components for the xplus slab. // // Args: @@ -483,7 +512,7 @@ order2_xplus = {'args_cuda': x_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii+1,jj,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHy,4)] * (IRA1 * dEz - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dEz - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dEz - Psi1); @@ -511,16 +540,20 @@ order2_xplus = {'args_cuda': x_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii+1,jj,kk)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dx; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * (IRA1 * dEy - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEy - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEy - Psi2); } -""")} +""" + ), +} -order1_yminus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order1_yminus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hz field components for the yminus slab. // // Args: @@ -568,9 +601,9 @@ order1_yminus = {'args_cuda': y_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii,jj+1,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * (IRA1 * dEz - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEz - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEz - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -591,16 +624,20 @@ order1_yminus = {'args_cuda': y_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj+1,kk)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * (IRA1 * dEx - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_yminus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order2_yminus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hz field components for the yminus slab. // // Args: @@ -653,7 +690,7 @@ order2_yminus = {'args_cuda': y_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii,jj+1,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * (IRA1 * dEz - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dEz - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dEz - Psi1); @@ -681,16 +718,20 @@ order2_yminus = {'args_cuda': y_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj+1,kk)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * (IRA1 * dEx - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEx - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEx - Psi2); } -""")} +""" + ), +} -order1_yplus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order1_yplus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hz field components for the yplus slab. // // Args: @@ -738,9 +779,9 @@ order1_yplus = {'args_cuda': y_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii,jj+1,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * (IRA1 * dEz - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEz - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEz - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -761,16 +802,20 @@ order1_yplus = {'args_cuda': y_args['cuda'], // Hz materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj+1,kk)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * (IRA1 * dEx - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_yplus = {'args_cuda': y_args['cuda'], - 'args_opencl': y_args['opencl'], - 'func': Template(""" +order2_yplus = { + "args_cuda": y_args["cuda"], + "args_opencl": y_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hz field components for the yplus slab. // // Args: @@ -823,7 +868,7 @@ order2_yplus = {'args_cuda': y_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEz = (Ez[IDX3D_FIELDS(ii,jj+1,kk)] - Ez[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * (IRA1 * dEz - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dEz - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dEz - Psi1); @@ -851,16 +896,20 @@ order2_yplus = {'args_cuda': y_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialHz = ID[IDX4D_ID(5,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj+1,kk)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dy; - Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(ii,jj,kk)] = Hz[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHz,4)] * (IRA1 * dEx - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEx - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEx - Psi2); } -""")} +""" + ), +} -order1_zminus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order1_zminus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hy field components for the zminus slab. // // Args: @@ -908,9 +957,9 @@ order1_zminus = {'args_cuda': z_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii,jj,kk+1)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * (IRA1 * dEy - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEy - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEy - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -931,16 +980,20 @@ order1_zminus = {'args_cuda': z_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj,kk+1)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * (IRA1 * dEx - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_zminus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order2_zminus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hy field components for the zminus slab. // // Args: @@ -993,7 +1046,7 @@ order2_zminus = {'args_cuda': z_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii,jj,kk+1)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * (IRA1 * dEy - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dEy - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dEy - Psi1); @@ -1021,16 +1074,20 @@ order2_zminus = {'args_cuda': z_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj,kk+1)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * (IRA1 * dEx - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEx - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEx - Psi2); } -""")} +""" + ), +} -order1_zplus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order1_zplus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hy field components for the zplus slab. // // Args: @@ -1078,9 +1135,9 @@ order1_zplus = {'args_cuda': z_args['cuda'], // Hx materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii,jj,kk+1)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * (IRA1 * dEy - IRA * PHI1[IDX4D_PHI1(0,i1,j1,k1)]); - PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEy - + PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * dEy - RC0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)]; } @@ -1101,16 +1158,20 @@ order1_zplus = {'args_cuda': z_args['cuda'], // Hy materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj,kk+1)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * (IRA1 * dEx - IRA * PHI2[IDX4D_PHI2(0,i2,j2,k2)]); - PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx - + PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * dEx - RC0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)]; } -""")} +""" + ), +} -order2_zplus = {'args_cuda': z_args['cuda'], - 'args_opencl': z_args['opencl'], - 'func': Template(""" +order2_zplus = { + "args_cuda": z_args["cuda"], + "args_opencl": z_args["opencl"], + "func": Template( + """ // This function updates the Hx and Hy field components for the zplus slab. // // Args: @@ -1163,7 +1224,7 @@ order2_zplus = {'args_cuda': z_args['cuda'], Psi1 = RB0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RB1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)]; materialHx = ID[IDX4D_ID(3,ii,jj,kk)]; dEy = (Ey[IDX3D_FIELDS(ii,jj,kk+1)] - Ey[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(ii,jj,kk)] = Hx[IDX3D_FIELDS(ii,jj,kk)] + updatecoeffsH[IDX2D_MAT(materialHx,4)] * (IRA1 * dEy - IRA * Psi1); PHI1[IDX4D_PHI1(1,i1,j1,k1)] = RE1 * PHI1[IDX4D_PHI1(1,i1,j1,k1)] + RC1 * (dEy - Psi1); PHI1[IDX4D_PHI1(0,i1,j1,k1)] = RE0 * PHI1[IDX4D_PHI1(0,i1,j1,k1)] + RC0 * (dEy - Psi1); @@ -1191,10 +1252,11 @@ order2_zplus = {'args_cuda': z_args['cuda'], Psi2 = RB0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RB1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)]; materialHy = ID[IDX4D_ID(4,ii,jj,kk)]; dEx = (Ex[IDX3D_FIELDS(ii,jj,kk+1)] - Ex[IDX3D_FIELDS(ii,jj,kk)]) / dz; - Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(ii,jj,kk)] = Hy[IDX3D_FIELDS(ii,jj,kk)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * (IRA1 * dEx - IRA * Psi2); PHI2[IDX4D_PHI2(1,i2,j2,k2)] = RE1 * PHI2[IDX4D_PHI2(1,i2,j2,k2)] + RC1 * (dEx - Psi2); PHI2[IDX4D_PHI2(0,i2,j2,k2)] = RE0 * PHI2[IDX4D_PHI2(0,i2,j2,k2)] + RC0 * (dEx - Psi2); } -""")} - +""" + ), +} diff --git a/gprMax/cuda_opencl/knl_snapshots.py b/gprMax/cuda_opencl/knl_snapshots.py index 26863844..db0ef001 100644 --- a/gprMax/cuda_opencl/knl_snapshots.py +++ b/gprMax/cuda_opencl/knl_snapshots.py @@ -19,31 +19,35 @@ from string import Template -store_snapshot = {'args_cuda': Template(""" - __global__ void store_snapshot(int p, - int xs, - int xf, - int ys, - int yf, - int zs, - int zf, - int dx, - int dy, +store_snapshot = { + "args_cuda": Template( + """ + __global__ void store_snapshot(int p, + int xs, + int xf, + int ys, + int yf, + int zs, + int zf, + int dx, + int dy, int dz, - const $REAL* __restrict__ Ex, + const $REAL* __restrict__ Ex, const $REAL* __restrict__ Ey, - const $REAL* __restrict__ Ez, + const $REAL* __restrict__ Ez, const $REAL* __restrict__ Hx, - const $REAL* __restrict__ Hy, + const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz, - $REAL *snapEx, - $REAL *snapEy, + $REAL *snapEx, + $REAL *snapEy, $REAL *snapEz, - $REAL *snapHx, - $REAL *snapHy, + $REAL *snapHx, + $REAL *snapHy, $REAL *snapHz) - """), - 'args_opencl': Template(""" + """ + ), + "args_opencl": Template( + """ int p, int xs, int xf, @@ -53,7 +57,7 @@ store_snapshot = {'args_cuda': Template(""" int zf, int dx, int dy, - int dz, + int dz, __global const $REAL* restrict Ex, __global const $REAL* restrict Ey, __global const $REAL* restrict Ez, @@ -66,8 +70,10 @@ store_snapshot = {'args_cuda': Template(""" __global $REAL *snapHx, __global $REAL *snapHy, __global $REAL *snapHz - """), - 'func': Template(""" + """ + ), + "func": Template( + """ // Stores field values for a snapshot. // // Args: @@ -96,27 +102,28 @@ store_snapshot = {'args_cuda': Template(""" // The electric field component value at a point comes from an average of // the 4 electric field component values in that cell - snapEx[IDX4D_SNAPS(p,x,y,z)] = (Ex[IDX3D_FIELDS(xx,yy,zz)] + - Ex[IDX3D_FIELDS(xx,yy+1,zz)] + - Ex[IDX3D_FIELDS(xx,yy,zz+1)] + + snapEx[IDX4D_SNAPS(p,x,y,z)] = (Ex[IDX3D_FIELDS(xx,yy,zz)] + + Ex[IDX3D_FIELDS(xx,yy+1,zz)] + + Ex[IDX3D_FIELDS(xx,yy,zz+1)] + Ex[IDX3D_FIELDS(xx,yy+1,zz+1)]) / 4; - snapEy[IDX4D_SNAPS(p,x,y,z)] = (Ey[IDX3D_FIELDS(xx,yy,zz)] + - Ey[IDX3D_FIELDS(xx+1,yy,zz)] + - Ey[IDX3D_FIELDS(xx,yy,zz+1)] + + snapEy[IDX4D_SNAPS(p,x,y,z)] = (Ey[IDX3D_FIELDS(xx,yy,zz)] + + Ey[IDX3D_FIELDS(xx+1,yy,zz)] + + Ey[IDX3D_FIELDS(xx,yy,zz+1)] + Ey[IDX3D_FIELDS(xx+1,yy,zz+1)]) / 4; - snapEz[IDX4D_SNAPS(p,x,y,z)] = (Ez[IDX3D_FIELDS(xx,yy,zz)] + - Ez[IDX3D_FIELDS(xx+1,yy,zz)] + - Ez[IDX3D_FIELDS(xx,yy+1,zz)] + + snapEz[IDX4D_SNAPS(p,x,y,z)] = (Ez[IDX3D_FIELDS(xx,yy,zz)] + + Ez[IDX3D_FIELDS(xx+1,yy,zz)] + + Ez[IDX3D_FIELDS(xx,yy+1,zz)] + Ez[IDX3D_FIELDS(xx+1,yy+1,zz)]) / 4; // The magnetic field component value at a point comes from average of // 2 magnetic field component values in that cell and the following cell - snapHx[IDX4D_SNAPS(p,x,y,z)] = (Hx[IDX3D_FIELDS(xx,yy,zz)] + + snapHx[IDX4D_SNAPS(p,x,y,z)] = (Hx[IDX3D_FIELDS(xx,yy,zz)] + Hx[IDX3D_FIELDS(xx+1,yy,zz)]) / 2; - snapHy[IDX4D_SNAPS(p,x,y,z)] = (Hy[IDX3D_FIELDS(xx,yy,zz)] + + snapHy[IDX4D_SNAPS(p,x,y,z)] = (Hy[IDX3D_FIELDS(xx,yy,zz)] + Hy[IDX3D_FIELDS(xx,yy+1,zz)]) / 2; - snapHz[IDX4D_SNAPS(p,x,y,z)] = (Hz[IDX3D_FIELDS(xx,yy,zz)] + + snapHz[IDX4D_SNAPS(p,x,y,z)] = (Hz[IDX3D_FIELDS(xx,yy,zz)] + Hz[IDX3D_FIELDS(xx,yy,zz+1)]) / 2; } -""") -} \ No newline at end of file +""" + ), +} diff --git a/gprMax/cuda_opencl/knl_source_updates.py b/gprMax/cuda_opencl/knl_source_updates.py index 2f17e930..5e1e6372 100644 --- a/gprMax/cuda_opencl/knl_source_updates.py +++ b/gprMax/cuda_opencl/knl_source_updates.py @@ -18,21 +18,25 @@ from string import Template -update_hertzian_dipole = {'args_cuda': Template(""" - __global__ void update_hertzian_dipole(int NHERTZDIPOLE, - int iteration, - $REAL dx, - $REAL dy, - $REAL dz, - const int* __restrict__ srcinfo1, - const $REAL* __restrict__ srcinfo2, - const $REAL* __restrict__ srcwaveforms, - const unsigned int* __restrict__ ID, - $REAL *Ex, - $REAL *Ey, +update_hertzian_dipole = { + "args_cuda": Template( + """ + __global__ void update_hertzian_dipole(int NHERTZDIPOLE, + int iteration, + $REAL dx, + $REAL dy, + $REAL dz, + const int* __restrict__ srcinfo1, + const $REAL* __restrict__ srcinfo2, + const $REAL* __restrict__ srcwaveforms, + const unsigned int* __restrict__ ID, + $REAL *Ex, + $REAL *Ey, $REAL *Ez) - """), - 'args_opencl': Template(""" + """ + ), + "args_opencl": Template( + """ int NHERTZDIPOLE, int iteration, $REAL dx, @@ -45,8 +49,10 @@ update_hertzian_dipole = {'args_cuda': Template(""" __global $REAL *Ex, __global $REAL *Ey, __global $REAL *Ez - """), - 'func': Template(""" + """ + ), + "func": Template( + """ // Updates electric field values for Hertzian dipole sources. // // Args: @@ -74,42 +80,47 @@ update_hertzian_dipole = {'args_cuda': Template(""" // 'x' polarised source if (polarisation == 0) { int materialEx = ID[IDX4D_ID(0,x,y,z)]; - Ex[IDX3D_FIELDS(x,y,z)] = Ex[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(x,y,z)] = Ex[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * dl * (1 / (dx * dy * dz)); } // 'y' polarised source else if (polarisation == 1) { int materialEy = ID[IDX4D_ID(1,x,y,z)]; - Ey[IDX3D_FIELDS(x,y,z)] = Ey[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(x,y,z)] = Ey[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * dl * (1 / (dx * dy * dz)); } // 'z' polarised source else if (polarisation == 2) { int materialEz = ID[IDX4D_ID(2,x,y,z)]; - Ez[IDX3D_FIELDS(x,y,z)] = Ez[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(x,y,z)] = Ez[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * dl * (1 / (dx * dy * dz)); } } -""") +""" + ), } -update_magnetic_dipole = {'args_cuda': Template(""" - __global__ void update_magnetic_dipole(int NMAGDIPOLE, - int iteration, - $REAL dx, - $REAL dy, - $REAL dz, - const int* __restrict__ srcinfo1, - const $REAL* __restrict__ srcinfo2, - const $REAL* __restrict__ srcwaveforms, - const unsigned int* __restrict__ ID, - $REAL *Hx, - $REAL *Hy, +update_magnetic_dipole = { + "args_cuda": Template( + """ + __global__ void update_magnetic_dipole(int NMAGDIPOLE, + int iteration, + $REAL dx, + $REAL dy, + $REAL dz, + const int* __restrict__ srcinfo1, + const $REAL* __restrict__ srcinfo2, + const $REAL* __restrict__ srcwaveforms, + const unsigned int* __restrict__ ID, + $REAL *Hx, + $REAL *Hy, $REAL *Hz) - """), - 'args_opencl': Template(""" + """ + ), + "args_opencl": Template( + """ int NMAGDIPOLE, int iteration, $REAL dx, @@ -122,8 +133,10 @@ update_magnetic_dipole = {'args_cuda': Template(""" __global $REAL *Hx, __global $REAL *Hy, __global $REAL *Hz - """), - 'func': Template(""" + """ + ), + "func": Template( + """ // Updates electric field values for Hertzian dipole sources. // // Args: @@ -149,42 +162,47 @@ update_magnetic_dipole = {'args_cuda': Template(""" // 'x' polarised source if (polarisation == 0) { int materialHx = ID[IDX4D_ID(3,x,y,z)]; - Hx[IDX3D_FIELDS(x,y,z)] = Hx[IDX3D_FIELDS(x,y,z)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * + Hx[IDX3D_FIELDS(x,y,z)] = Hx[IDX3D_FIELDS(x,y,z)] - updatecoeffsH[IDX2D_MAT(materialHx,4)] * srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * (1 / (dx * dy * dz)); } // 'y' polarised source else if (polarisation == 1) { int materialHy = ID[IDX4D_ID(4,x,y,z)]; - Hy[IDX3D_FIELDS(x,y,z)] = Hy[IDX3D_FIELDS(x,y,z)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * + Hy[IDX3D_FIELDS(x,y,z)] = Hy[IDX3D_FIELDS(x,y,z)] - updatecoeffsH[IDX2D_MAT(materialHy,4)] * srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * (1 / (dx * dy * dz)); } // 'z' polarised source else if (polarisation == 2) { int materialHz = ID[IDX4D_ID(5,x,y,z)]; - Hz[IDX3D_FIELDS(x,y,z)] = Hz[IDX3D_FIELDS(x,y,z)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * + Hz[IDX3D_FIELDS(x,y,z)] = Hz[IDX3D_FIELDS(x,y,z)] - updatecoeffsH[IDX2D_MAT(materialHz,4)] * srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * (1 / (dx * dy * dz)); } } -""") +""" + ), } -update_voltage_source = {'args_cuda': Template(""" - __global__ void update_voltage_source(int NVOLTSRC, - int iteration, - $REAL dx, - $REAL dy, - $REAL dz, - const int* __restrict__ srcinfo1, - const $REAL* __restrict__ srcinfo2, - const $REAL* __restrict__ srcwaveforms, - const unsigned int* __restrict__ ID, - $REAL *Ex, - $REAL *Ey, +update_voltage_source = { + "args_cuda": Template( + """ + __global__ void update_voltage_source(int NVOLTSRC, + int iteration, + $REAL dx, + $REAL dy, + $REAL dz, + const int* __restrict__ srcinfo1, + const $REAL* __restrict__ srcinfo2, + const $REAL* __restrict__ srcwaveforms, + const unsigned int* __restrict__ ID, + $REAL *Ex, + $REAL *Ey, $REAL *Ez) - """), - 'args_opencl': Template(""" + """ + ), + "args_opencl": Template( + """ int NVOLTSRC, int iteration, $REAL dx, @@ -197,8 +215,10 @@ update_voltage_source = {'args_cuda': Template(""" __global $REAL *Ex, __global $REAL *Ey, __global $REAL *Ez - """), - 'func': Template(""" + """ + ), + "func": Template( + """ // Updates electric field values for voltage sources. // // Args: @@ -227,7 +247,7 @@ update_voltage_source = {'args_cuda': Template(""" if (polarisation == 0) { if (resistance != 0) { int materialEx = ID[IDX4D_ID(0,x,y,z)]; - Ex[IDX3D_FIELDS(x,y,z)] = Ex[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * + Ex[IDX3D_FIELDS(x,y,z)] = Ex[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEx,4)] * srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * (1 / (resistance * dy * dz)); } else { @@ -239,7 +259,7 @@ update_voltage_source = {'args_cuda': Template(""" else if (polarisation == 1) { if (resistance != 0) { int materialEy = ID[IDX4D_ID(1,x,y,z)]; - Ey[IDX3D_FIELDS(x,y,z)] = Ey[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * + Ey[IDX3D_FIELDS(x,y,z)] = Ey[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEy,4)] * srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * (1 / (resistance * dx * dz)); } else { @@ -251,7 +271,7 @@ update_voltage_source = {'args_cuda': Template(""" else if (polarisation == 2) { if (resistance != 0) { int materialEz = ID[IDX4D_ID(2,x,y,z)]; - Ez[IDX3D_FIELDS(x,y,z)] = Ez[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * + Ez[IDX3D_FIELDS(x,y,z)] = Ez[IDX3D_FIELDS(x,y,z)] - updatecoeffsE[IDX2D_MAT(materialEz,4)] * srcwaveforms[IDX2D_SRCWAVES(i,iteration)] * (1 / (resistance * dx * dy)); } else { @@ -259,5 +279,6 @@ update_voltage_source = {'args_cuda': Template(""" } } } -""") -} \ No newline at end of file +""" + ), +} diff --git a/gprMax/cuda_opencl/knl_store_outputs.py b/gprMax/cuda_opencl/knl_store_outputs.py index 21370d22..39020390 100644 --- a/gprMax/cuda_opencl/knl_store_outputs.py +++ b/gprMax/cuda_opencl/knl_store_outputs.py @@ -19,19 +19,23 @@ from string import Template -store_outputs = {'args_cuda': Template(""" - __global__ void store_outputs(int NRX, - int iteration, - const int* __restrict__ rxcoords, - $REAL *rxs, - const $REAL* __restrict__ Ex, - const $REAL* __restrict__ Ey, - const $REAL* __restrict__ Ez, - const $REAL* __restrict__ Hx, - const $REAL* __restrict__ Hy, +store_outputs = { + "args_cuda": Template( + """ + __global__ void store_outputs(int NRX, + int iteration, + const int* __restrict__ rxcoords, + $REAL *rxs, + const $REAL* __restrict__ Ex, + const $REAL* __restrict__ Ey, + const $REAL* __restrict__ Ez, + const $REAL* __restrict__ Hx, + const $REAL* __restrict__ Hy, const $REAL* __restrict__ Hz) - """), - 'args_opencl': Template(""" + """ + ), + "args_opencl": Template( + """ int NRX, int iteration, __global const int* restrict rxcoords, @@ -42,13 +46,15 @@ store_outputs = {'args_cuda': Template(""" __global const $REAL* restrict Hx, __global const $REAL* restrict Hy, __global const $REAL* restrict Hz - """), - 'func': Template(""" + """ + ), + "func": Template( + """ // Stores field component values for every receiver in the model. // - // Args: + // Args: // NRX: total number of receivers in the model. - // rxs: array to store field components for receivers - rows + // rxs: array to store field components for receivers - rows // are field components; columns are iterations; pages are receiver. $CUDA_IDX @@ -65,5 +71,6 @@ store_outputs = {'args_cuda': Template(""" rxs[IDX3D_RXS(4,iteration,i)] = Hy[IDX3D_FIELDS(x,y,z)]; rxs[IDX3D_RXS(5,iteration,i)] = Hz[IDX3D_FIELDS(x,y,z)]; } -""") -} \ No newline at end of file +""" + ), +} diff --git a/gprMax/cython/fields_updates_dispersive_template.jinja b/gprMax/cython/fields_updates_dispersive_template.jinja index 2aca26f8..74ddc4bf 100644 --- a/gprMax/cython/fields_updates_dispersive_template.jinja +++ b/gprMax/cython/fields_updates_dispersive_template.jinja @@ -57,14 +57,14 @@ cpdef void {{ item.name_a }}( {{ item.field_type }}[:, :, ::1] Hy, {{ item.field_type }}[:, :, ::1] Hz ): - """Updates the electric field components when dispersive materials + """Updates the electric field components when dispersive materials (with multiple poles) are present. Args: nx, ny, nz: int for grid size in cells. nthreads: int for number of threads to use. maxpoles: int for maximum number of poles. - updatecoeffs, T, ID, E, H: memoryviews to access to update coeffients, + updatecoeffs, T, ID, E, H: memoryviews to access to update coeffients, temporary, ID and field component arrays. """ @@ -82,20 +82,20 @@ cpdef void {{ item.name_a }}( for pole in range(maxpoles): {% if 'complex' in item.dispersive_type %} {% if item.iswin %} - phi = (phi + updatecoeffsdispersive[material, pole * 3].real + phi = (phi + updatecoeffsdispersive[material, pole * 3].real * Tx[pole, i, j, k].real) {% else %} - phi = (phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) + phi = (phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tx[pole, i, j, k])) {% endif %} {% else %} phi = phi + updatecoeffsdispersive[material, pole * 3] * Tx[pole, i, j, k] {% endif %} - Tx[pole, i, j, k] = (updatecoeffsdispersive[material, 1 + (pole * 3)] - * Tx[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] + Tx[pole, i, j, k] = (updatecoeffsdispersive[material, 1 + (pole * 3)] + * Tx[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k]) - Ex[i, j, k] = (updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] - * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] + Ex[i, j, k] = (updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] + * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi) # Ey component @@ -108,20 +108,20 @@ cpdef void {{ item.name_a }}( for pole in range(maxpoles): {% if 'complex' in item.dispersive_type %} {% if item.iswin %} - phi = (phi + updatecoeffsdispersive[material, pole * 3].real + phi = (phi + updatecoeffsdispersive[material, pole * 3].real * Ty[pole, i, j, k].real) {% else %} - phi = (phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) + phi = (phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Ty[pole, i, j, k])) {% endif %} {% else %} phi = phi + updatecoeffsdispersive[material, pole * 3] * Ty[pole, i, j, k] {% endif %} - Ty[pole, i, j, k] = (updatecoeffsdispersive[material, 1 + (pole * 3)] - * Ty[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] + Ty[pole, i, j, k] = (updatecoeffsdispersive[material, 1 + (pole * 3)] + * Ty[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k]) - Ey[i, j, k] = (updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] - * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] + Ey[i, j, k] = (updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] + * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi) # Ez component @@ -134,20 +134,20 @@ cpdef void {{ item.name_a }}( for pole in range(maxpoles): {% if 'complex' in item.dispersive_type %} {% if item.iswin %} - phi = (phi + updatecoeffsdispersive[material, pole * 3].real + phi = (phi + updatecoeffsdispersive[material, pole * 3].real * Tz[pole, i, j, k].real) {% else %} - phi = (phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) + phi = (phi + {{ item.real_part }}(updatecoeffsdispersive[material, pole * 3]) * {{ item.real_part }}(Tz[pole, i, j, k])) {% endif %} {% else %} phi = phi + updatecoeffsdispersive[material, pole * 3] * Tz[pole, i, j, k] {% endif %} - Tz[pole, i, j, k] = (updatecoeffsdispersive[material, 1 + (pole * 3)] - * Tz[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] + Tz[pole, i, j, k] = (updatecoeffsdispersive[material, 1 + (pole * 3)] + * Tz[pole, i, j, k] + updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k]) - Ez[i, j, k] = (updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] - * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] + Ez[i, j, k] = (updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] + * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi) {% endfor %} @@ -172,14 +172,14 @@ cpdef void {{ item.name_b }}( {{ item.field_type }}[:, :, ::1] Ey, {{ item.field_type }}[:, :, ::1] Ez ): - """Updates a temporary dispersive material array when disperisive materials + """Updates a temporary dispersive material array when disperisive materials (with multiple poles) are present. Args: nx, ny, nz: int for grid size in cells. nthreads: int for number of threads to use. maxpoles: int for maximum number of poles. - updatecoeffs, T, ID, E, H: memoryviews to access to update coeffients, + updatecoeffs, T, ID, E, H: memoryviews to access to update coeffients, temporary, ID and field component arrays. """ @@ -193,8 +193,8 @@ cpdef void {{ item.name_b }}( for k in range(1, nz): material = ID[0, i, j, k] for pole in range(maxpoles): - Tx[pole, i, j, k] = (Tx[pole, i, j, k] - - updatecoeffsdispersive[material, 2 + (pole * 3)] + Tx[pole, i, j, k] = (Tx[pole, i, j, k] + - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ex[i, j, k]) # Ey component @@ -204,8 +204,8 @@ cpdef void {{ item.name_b }}( for k in range(1, nz): material = ID[1, i, j, k] for pole in range(maxpoles): - Ty[pole, i, j, k] = (Ty[pole, i, j, k] - - updatecoeffsdispersive[material, 2 + (pole * 3)] + Ty[pole, i, j, k] = (Ty[pole, i, j, k] + - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ey[i, j, k]) # Ez component @@ -215,8 +215,8 @@ cpdef void {{ item.name_b }}( for k in range(0, nz): material = ID[2, i, j, k] for pole in range(maxpoles): - Tz[pole, i, j, k] = (Tz[pole, i, j, k] - - updatecoeffsdispersive[material, 2 + (pole * 3)] + Tz[pole, i, j, k] = (Tz[pole, i, j, k] + - updatecoeffsdispersive[material, 2 + (pole * 3)] * Ez[i, j, k]) {% endfor %} @@ -246,14 +246,14 @@ cpdef void {{ item.name_a_1 }}( {{ item.field_type }}[:, :, ::1] Hy, {{ item.field_type }}[:, :, ::1] Hz ): - """Updates the electric field components when dispersive materials + """Updates the electric field components when dispersive materials (with 1 pole) are present. Args: nx, ny, nz: int for grid size in cells. nthreads: int for number of threads to use. maxpoles: int for maximum number of poles. - updatecoeffs, T, ID, E, H: memoryviews to access to update coeffients, + updatecoeffs, T, ID, E, H: memoryviews to access to update coeffients, temporary, ID and field component arrays. """ @@ -269,19 +269,19 @@ cpdef void {{ item.name_a_1 }}( material = ID[0, i, j, k] {% if 'complex' in item.dispersive_type %} {% if item.iswin %} - phi = (updatecoeffsdispersive[material, 0].real + phi = (updatecoeffsdispersive[material, 0].real * Tx[0, i, j, k].real) {% else %} - phi = ({{ item.real_part }}(updatecoeffsdispersive[material, 0]) + phi = ({{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tx[0, i, j, k])) {% endif %} {% else %} phi = updatecoeffsdispersive[material, 0] * Tx[0, i, j, k] {% endif %} - Tx[0, i, j, k] = (updatecoeffsdispersive[material, 1] * Tx[0, i, j, k] + Tx[0, i, j, k] = (updatecoeffsdispersive[material, 1] * Tx[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ex[i, j, k]) - Ex[i, j, k] = (updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] - * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] + Ex[i, j, k] = (updatecoeffsE[material, 0] * Ex[i, j, k] + updatecoeffsE[material, 2] + * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[material, 3] * (Hy[i, j, k] - Hy[i, j, k - 1]) - updatecoeffsE[material, 4] * phi) # Ey component @@ -292,19 +292,19 @@ cpdef void {{ item.name_a_1 }}( material = ID[1, i, j, k] {% if 'complex' in item.dispersive_type %} {% if item.iswin %} - phi = (updatecoeffsdispersive[material, 0].real + phi = (updatecoeffsdispersive[material, 0].real * Ty[0, i, j, k].real) {% else %} - phi = ({{ item.real_part }}(updatecoeffsdispersive[material, 0]) + phi = ({{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Ty[0, i, j, k])) {% endif %} {% else %} phi = updatecoeffsdispersive[material, 0] * Ty[0, i, j, k] {% endif %} - Ty[0, i, j, k] = (updatecoeffsdispersive[material, 1] * Ty[0, i, j, k] + Ty[0, i, j, k] = (updatecoeffsdispersive[material, 1] * Ty[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ey[i, j, k]) - Ey[i, j, k] = (updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] - * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] + Ey[i, j, k] = (updatecoeffsE[material, 0] * Ey[i, j, k] + updatecoeffsE[material, 3] + * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[material, 1] * (Hz[i, j, k] - Hz[i - 1, j, k]) - updatecoeffsE[material, 4] * phi) # Ez component @@ -315,19 +315,19 @@ cpdef void {{ item.name_a_1 }}( material = ID[2, i, j, k] {% if 'complex' in item.dispersive_type %} {% if item.iswin %} - phi = (updatecoeffsdispersive[material, 0].real + phi = (updatecoeffsdispersive[material, 0].real * Tz[0, i, j, k].real) {% else %} - phi = ({{ item.real_part }}(updatecoeffsdispersive[material, 0]) + phi = ({{ item.real_part }}(updatecoeffsdispersive[material, 0]) * {{ item.real_part }}(Tz[0, i, j, k])) {% endif %} {% else %} phi = updatecoeffsdispersive[material, 0] * Tz[0, i, j, k] {% endif %} - Tz[0, i, j, k] = (updatecoeffsdispersive[material, 1] * Tz[0, i, j, k] + Tz[0, i, j, k] = (updatecoeffsdispersive[material, 1] * Tz[0, i, j, k] + updatecoeffsdispersive[material, 2] * Ez[i, j, k]) - Ez[i, j, k] = (updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] - * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] + Ez[i, j, k] = (updatecoeffsE[material, 0] * Ez[i, j, k] + updatecoeffsE[material, 1] + * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[material, 2] * (Hx[i, j, k] - Hx[i, j - 1, k]) - updatecoeffsE[material, 4] * phi) {% endfor %} @@ -353,14 +353,14 @@ cpdef void {{ item.name_b_1 }}( {{ item.field_type }}[:, :, ::1] Ey, {{ item.field_type }}[:, :, ::1] Ez ): - """Updates a temporary dispersive material array when disperisive materials + """Updates a temporary dispersive material array when disperisive materials (with 1 pole) are present. Args: nx, ny, nz: int for grid size in cells. nthreads: int for number of threads to use. maxpoles: int for maximum number of poles. - updatecoeffs, T, ID, E, H: memoryviews to access to update coeffients, + updatecoeffs, T, ID, E, H: memoryviews to access to update coeffients, temporary, ID and field component arrays. """ diff --git a/gprMax/cython/fields_updates_normal.pyx b/gprMax/cython/fields_updates_normal.pyx index dbe63e9d..43c20fbd 100644 --- a/gprMax/cython/fields_updates_normal.pyx +++ b/gprMax/cython/fields_updates_normal.pyx @@ -42,7 +42,7 @@ cpdef void update_electric( Args: nx, ny, nz: ints for grid size in cells. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays """ @@ -55,8 +55,8 @@ cpdef void update_electric( for j in range(1, ny): for k in range(1, nz): materialEx = ID[0, i, j, k] - Ex[i, j, k] = (updatecoeffsE[materialEx, 0] * Ex[i, j, k] + - updatecoeffsE[materialEx, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - + Ex[i, j, k] = (updatecoeffsE[materialEx, 0] * Ex[i, j, k] + + updatecoeffsE[materialEx, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[materialEx, 3] * (Hy[i, j, k] - Hy[i, j, k - 1])) # 2D - Ey component @@ -65,8 +65,8 @@ cpdef void update_electric( for j in range(0, ny): for k in range(1, nz): materialEy = ID[1, i, j, k] - Ey[i, j, k] = (updatecoeffsE[materialEy, 0] * Ey[i, j, k] + - updatecoeffsE[materialEy, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - + Ey[i, j, k] = (updatecoeffsE[materialEy, 0] * Ey[i, j, k] + + updatecoeffsE[materialEy, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[materialEy, 1] * (Hz[i, j, k] - Hz[i - 1, j, k])) # 2D - Ez component @@ -75,8 +75,8 @@ cpdef void update_electric( for j in range(1, ny): for k in range(0, nz): materialEz = ID[2, i, j, k] - Ez[i, j, k] = (updatecoeffsE[materialEz, 0] * Ez[i, j, k] + - updatecoeffsE[materialEz, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - + Ez[i, j, k] = (updatecoeffsE[materialEz, 0] * Ez[i, j, k] + + updatecoeffsE[materialEz, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[materialEz, 2] * (Hx[i, j, k] - Hx[i, j - 1, k])) # 3D @@ -87,38 +87,38 @@ cpdef void update_electric( materialEx = ID[0, i, j, k] materialEy = ID[1, i, j, k] materialEz = ID[2, i, j, k] - Ex[i, j, k] = (updatecoeffsE[materialEx, 0] * Ex[i, j, k] + - updatecoeffsE[materialEx, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - + Ex[i, j, k] = (updatecoeffsE[materialEx, 0] * Ex[i, j, k] + + updatecoeffsE[materialEx, 2] * (Hz[i, j, k] - Hz[i, j - 1, k]) - updatecoeffsE[materialEx, 3] * (Hy[i, j, k] - Hy[i, j, k - 1])) - Ey[i, j, k] = (updatecoeffsE[materialEy, 0] * Ey[i, j, k] + - updatecoeffsE[materialEy, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - + Ey[i, j, k] = (updatecoeffsE[materialEy, 0] * Ey[i, j, k] + + updatecoeffsE[materialEy, 3] * (Hx[i, j, k] - Hx[i, j, k - 1]) - updatecoeffsE[materialEy, 1] * (Hz[i, j, k] - Hz[i - 1, j, k])) - Ez[i, j, k] = (updatecoeffsE[materialEz, 0] * Ez[i, j, k] + - updatecoeffsE[materialEz, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - + Ez[i, j, k] = (updatecoeffsE[materialEz, 0] * Ez[i, j, k] + + updatecoeffsE[materialEz, 1] * (Hy[i, j, k] - Hy[i - 1, j, k]) - updatecoeffsE[materialEz, 2] * (Hx[i, j, k] - Hx[i, j - 1, k])) # Ex components at i = 0 for j in prange(1, ny, nogil=True, schedule='static', num_threads=nthreads): for k in range(1, nz): materialEx = ID[0, 0, j, k] - Ex[0, j, k] = (updatecoeffsE[materialEx, 0] * Ex[0, j, k] + - updatecoeffsE[materialEx, 2] * (Hz[0, j, k] - Hz[0, j - 1, k]) - + Ex[0, j, k] = (updatecoeffsE[materialEx, 0] * Ex[0, j, k] + + updatecoeffsE[materialEx, 2] * (Hz[0, j, k] - Hz[0, j - 1, k]) - updatecoeffsE[materialEx, 3] * (Hy[0, j, k] - Hy[0, j, k - 1])) # Ey components at j = 0 for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for k in range(1, nz): materialEy = ID[1, i, 0, k] - Ey[i, 0, k] = (updatecoeffsE[materialEy, 0] * Ey[i, 0, k] + - updatecoeffsE[materialEy, 3] * (Hx[i, 0, k] - Hx[i, 0, k - 1]) - + Ey[i, 0, k] = (updatecoeffsE[materialEy, 0] * Ey[i, 0, k] + + updatecoeffsE[materialEy, 3] * (Hx[i, 0, k] - Hx[i, 0, k - 1]) - updatecoeffsE[materialEy, 1] * (Hz[i, 0, k] - Hz[i - 1, 0, k])) # Ez components at k = 0 for i in prange(1, nx, nogil=True, schedule='static', num_threads=nthreads): for j in range(1, ny): materialEz = ID[2, i, j, 0] - Ez[i, j, 0] = (updatecoeffsE[materialEz, 0] * Ez[i, j, 0] + - updatecoeffsE[materialEz, 1] * (Hy[i, j, 0] - Hy[i - 1, j, 0]) - + Ez[i, j, 0] = (updatecoeffsE[materialEz, 0] * Ez[i, j, 0] + + updatecoeffsE[materialEz, 1] * (Hy[i, j, 0] - Hy[i - 1, j, 0]) - updatecoeffsE[materialEz, 2] * (Hx[i, j, 0] - Hx[i, j - 1, 0])) @@ -141,7 +141,7 @@ cpdef void update_magnetic( Args: nx, ny, nz: ints for grid size in cells. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays """ @@ -156,8 +156,8 @@ cpdef void update_magnetic( for j in range(0, ny): for k in range(0, nz): materialHx = ID[3, i, j, k] - Hx[i, j, k] = (updatecoeffsH[materialHx, 0] * Hx[i, j, k] - - updatecoeffsH[materialHx, 2] * (Ez[i, j + 1, k] - Ez[i, j, k]) + + Hx[i, j, k] = (updatecoeffsH[materialHx, 0] * Hx[i, j, k] - + updatecoeffsH[materialHx, 2] * (Ez[i, j + 1, k] - Ez[i, j, k]) + updatecoeffsH[materialHx, 3] * (Ey[i, j, k + 1] - Ey[i, j, k])) # Hy component @@ -166,8 +166,8 @@ cpdef void update_magnetic( for j in range(1, ny): for k in range(0, nz): materialHy = ID[4, i, j, k] - Hy[i, j, k] = (updatecoeffsH[materialHy, 0] * Hy[i, j, k] - - updatecoeffsH[materialHy, 3] * (Ex[i, j, k + 1] - Ex[i, j, k]) + + Hy[i, j, k] = (updatecoeffsH[materialHy, 0] * Hy[i, j, k] - + updatecoeffsH[materialHy, 3] * (Ex[i, j, k + 1] - Ex[i, j, k]) + updatecoeffsH[materialHy, 1] * (Ez[i + 1, j, k] - Ez[i, j, k])) # Hz component @@ -176,8 +176,8 @@ cpdef void update_magnetic( for j in range(0, ny): for k in range(1, nz): materialHz = ID[5, i, j, k] - Hz[i, j, k] = (updatecoeffsH[materialHz, 0] * Hz[i, j, k] - - updatecoeffsH[materialHz, 1] * (Ey[i + 1, j, k] - Ey[i, j, k]) + + Hz[i, j, k] = (updatecoeffsH[materialHz, 0] * Hz[i, j, k] - + updatecoeffsH[materialHz, 1] * (Ey[i + 1, j, k] - Ey[i, j, k]) + updatecoeffsH[materialHz, 2] * (Ex[i, j + 1, k] - Ex[i, j, k])) # 3D else: @@ -187,12 +187,12 @@ cpdef void update_magnetic( materialHx = ID[3, i + 1, j, k] materialHy = ID[4, i, j + 1, k] materialHz = ID[5, i, j, k + 1] - Hx[i + 1, j, k] = (updatecoeffsH[materialHx, 0] * Hx[i + 1, j, k] - - updatecoeffsH[materialHx, 2] * (Ez[i + 1, j + 1, k] - Ez[i + 1, j, k]) + + Hx[i + 1, j, k] = (updatecoeffsH[materialHx, 0] * Hx[i + 1, j, k] - + updatecoeffsH[materialHx, 2] * (Ez[i + 1, j + 1, k] - Ez[i + 1, j, k]) + updatecoeffsH[materialHx, 3] * (Ey[i + 1, j, k + 1] - Ey[i + 1, j, k])) - Hy[i, j + 1, k] = (updatecoeffsH[materialHy, 0] * Hy[i, j + 1, k] - - updatecoeffsH[materialHy, 3] * (Ex[i, j + 1, k + 1] - Ex[i, j + 1, k]) + + Hy[i, j + 1, k] = (updatecoeffsH[materialHy, 0] * Hy[i, j + 1, k] - + updatecoeffsH[materialHy, 3] * (Ex[i, j + 1, k + 1] - Ex[i, j + 1, k]) + updatecoeffsH[materialHy, 1] * (Ez[i + 1, j + 1, k] - Ez[i, j + 1, k])) - Hz[i, j, k + 1] = (updatecoeffsH[materialHz, 0] * Hz[i, j, k + 1] - - updatecoeffsH[materialHz, 1] * (Ey[i + 1, j, k + 1] - Ey[i, j, k + 1]) + + Hz[i, j, k + 1] = (updatecoeffsH[materialHz, 0] * Hz[i, j, k + 1] - + updatecoeffsH[materialHz, 1] * (Ey[i + 1, j, k + 1] - Ey[i, j, k + 1]) + updatecoeffsH[materialHz, 2] * (Ex[i, j + 1, k + 1] - Ex[i, j, k + 1])) diff --git a/gprMax/cython/fractals_generate.pyx b/gprMax/cython/fractals_generate.pyx index 24963d9c..cdf25c73 100644 --- a/gprMax/cython/fractals_generate.pyx +++ b/gprMax/cython/fractals_generate.pyx @@ -24,13 +24,13 @@ from gprMax.config cimport float_or_double_complex cpdef void generate_fractal2D( - int nx, - int ny, - int nthreads, - int b, - np.float64_t[:] weighting, - np.float64_t[:] v1, - np.complex128_t[:, ::1] A, + int nx, + int ny, + int nthreads, + int b, + np.float64_t[:] weighting, + np.float64_t[:] v1, + np.complex128_t[:, ::1] A, float_or_double_complex[:, ::1] fractalsurface ): """Generates a fractal surface for a 2D array. @@ -40,11 +40,11 @@ cpdef void generate_fractal2D( nthreads: int for number of threads to use b: int for constant related to fractal dimension. weighting: memoryview for access to weighting vector. - v1: memoryview for access to positional vector at centre of array, + v1: memoryview for access to positional vector at centre of array, scaled by weighting. - A: memoryview for access to array containing random numbers + A: memoryview for access to array containing random numbers (to be convolved with fractal function). - fractalsurface: memoryview for access to array containing fractal + fractalsurface: memoryview for access to array containing fractal surface data. """ @@ -67,14 +67,14 @@ cpdef void generate_fractal2D( cpdef void generate_fractal3D( - int nx, - int ny, - int nz, - int nthreads, - int b, - np.float64_t[:] weighting, - np.float64_t[:] v1, - np.complex128_t[:, :, ::1] A, + int nx, + int ny, + int nz, + int nthreads, + int b, + np.float64_t[:] weighting, + np.float64_t[:] v1, + np.complex128_t[:, :, ::1] A, float_or_double_complex[:, :, ::1] fractalvolume ): """Generates a fractal volume for a 3D array. @@ -84,11 +84,11 @@ cpdef void generate_fractal3D( nthreads: int for number of threads to use b: int for constant related to fractal dimension. weighting: memoryview for access to weighting vector. - v1: memoryview for access to positional vector at centre of array, + v1: memoryview for access to positional vector at centre of array, scaled by weighting. - A: memoryview for access to array containing random numbers + A: memoryview for access to array containing random numbers (to be convolved with fractal function). - fractalsurface: memoryview for access to array containing fractal + fractalsurface: memoryview for access to array containing fractal volume data. """ @@ -107,6 +107,6 @@ cpdef void generate_fractal3D( rr = ((v2x - v1[0])**2 + (v2y - v1[1])**2 + (v2z - v1[2])**2)**(1/2) B = rr**b if B == 0: - B = 0.9 - + B = 0.9 + fractalvolume[i, j, k] = A[i, j, k] / B diff --git a/gprMax/cython/geometry_outputs.pyx b/gprMax/cython/geometry_outputs.pyx index c95770ad..1312fec7 100644 --- a/gprMax/cython/geometry_outputs.pyx +++ b/gprMax/cython/geometry_outputs.pyx @@ -21,15 +21,15 @@ cimport numpy as np cpdef write_lines( - float xs, - float ys, - float zs, - int nx, - int ny, - int nz, - float dx, - float dy, - float dz, + float xs, + float ys, + float zs, + int nx, + int ny, + int nz, + float dx, + float dy, + float dz, np.uint32_t[:, :, :, :] ID ): """Generates arrays with to be written as lines (cell edges) to a VTK file. @@ -38,7 +38,7 @@ cpdef write_lines( xs, ys, zs: float for starting coordinates of geometry view in metres. nx, ny, nz: int for size of the volume in cells. dx, dy, dz: float for spatial discretisation of geometry view in metres. - ID: memoryview of sampled ID array according to geometry view spatial + ID: memoryview of sampled ID array according to geometry view spatial discretisation. Returns: @@ -108,4 +108,4 @@ cpdef write_lines( y += ys z += zs - return x, y, z, lines \ No newline at end of file + return x, y, z, lines diff --git a/gprMax/cython/geometry_primitives.pyx b/gprMax/cython/geometry_primitives.pyx index fa37554e..d89c12b2 100644 --- a/gprMax/cython/geometry_primitives.pyx +++ b/gprMax/cython/geometry_primitives.pyx @@ -330,7 +330,7 @@ cpdef void build_voxel( Args: i, j, k: ints for cell coordinates of voxel. numID, numIDx, numIDy, numIDz: ints for numeric ID of material. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays. """ @@ -402,7 +402,7 @@ cpdef void build_triangle( np.uint32_t[:, :, :, ::1] ID ): """ - Builds triangles and triangular prisms which sets values in the solid, + Builds triangles and triangular prisms which sets values in the solid, rigid and ID arrays for a Yee voxel. Args: @@ -412,7 +412,7 @@ cpdef void build_triangle( thickness: float for thickness of the triangular prism. dx, dy, dz: floats for spatial discretisation. numID, numIDx, numIDy, numIDz: ints for numeric ID of material. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays. """ @@ -452,7 +452,7 @@ cpdef void build_triangle( for i in range(i1, i2): for j in range(j1, j2): - # Calculate the areas of the 3 triangles defined by the 3 vertices + # Calculate the areas of the 3 triangles defined by the 3 vertices # of the main triangle and the point under test. if normal == 'x': ir = (i + 0.5) * dy @@ -474,24 +474,24 @@ cpdef void build_triangle( if s > 0 and t > 0 and (s + t) < 2 * area * sign: if thicknesscells == 0: if normal == 'x': - build_face_yz(level, i, j, numIDy, numIDz, + build_face_yz(level, i, j, numIDy, numIDz, rigidE, rigidH, ID) elif normal == 'y': - build_face_xz(i, level, j, numIDx, numIDz, + build_face_xz(i, level, j, numIDx, numIDz, rigidE, rigidH, ID) elif normal == 'z': - build_face_xy(i, j, level, numIDx, numIDy, + build_face_xy(i, j, level, numIDx, numIDy, rigidE, rigidH, ID) else: for k in range(level, level + thicknesscells): if normal == 'x': - build_voxel(k, i, j, numID, numIDx, numIDy, numIDz, + build_voxel(k, i, j, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) elif normal == 'y': - build_voxel(i, k, j, numID, numIDx, numIDy, numIDz, + build_voxel(i, k, j, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) elif normal == 'z': - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) @@ -518,11 +518,11 @@ cpdef void build_cylindrical_sector( np.uint32_t[:, :, :, ::1] ID ): """ - Builds cylindrical sectors which sets values in the solid, rigid and ID + Builds cylindrical sectors which sets values in the solid, rigid and ID arrays for a Yee voxel. It defines a sector of cylinder given by the - direction of the axis of the coordinates of the cylinder face centre, - depth coordinates, sector start point, sector angle, and sector radius. - N.B Assumes sector start is always clockwise from sector end, + direction of the axis of the coordinates of the cylinder face centre, + depth coordinates, sector start point, sector angle, and sector radius. + N.B Assumes sector start is always clockwise from sector end, i.e. sector defined in an anti-clockwise direction. Args: @@ -535,7 +535,7 @@ cpdef void build_cylindrical_sector( thickness: float for thickness of the triangular prism. dx, dy, dz: floats for spatial discretisation. numID, numIDx, numIDy, numIDz: ints for numeric ID of material. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays. """ @@ -544,7 +544,7 @@ cpdef void build_cylindrical_sector( cdef int x1, x2, y1, y2, z1, z2, thicknesscells if normal == 'x': - # Angles are defined from zero degrees on the positive y-axis going + # Angles are defined from zero degrees on the positive y-axis going # towards positive z-axis. y1 = round_value((ctr1 - radius)/dy) y2 = round_value((ctr1 + radius)/dy) @@ -564,18 +564,18 @@ cpdef void build_cylindrical_sector( for y in range(y1, y2): for z in range(z1, z2): - if is_inside_sector(y * dy + 0.5 * dy, z * dz + 0.5 * dz, ctr1, + if is_inside_sector(y * dy + 0.5 * dy, z * dz + 0.5 * dz, ctr1, ctr2, sectorstartangle, sectorangle, radius): if thicknesscells == 0: - build_face_yz(level, y, z, numIDy, numIDz, + build_face_yz(level, y, z, numIDy, numIDz, rigidE, rigidH, ID) else: for x in range(level, level + thicknesscells): - build_voxel(x, y, z, numID, numIDx, numIDy, numIDz, + build_voxel(x, y, z, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) elif normal == 'y': - # Angles are defined from zero degrees on the positive x-axis going + # Angles are defined from zero degrees on the positive x-axis going # towards positive z-axis. x1 = round_value((ctr1 - radius)/dx) x2 = round_value((ctr1 + radius)/dx) @@ -595,18 +595,18 @@ cpdef void build_cylindrical_sector( for x in range(x1, x2): for z in range(z1, z2): - if is_inside_sector(x * dx + 0.5 * dx, z * dz + 0.5 * dz, ctr1, + if is_inside_sector(x * dx + 0.5 * dx, z * dz + 0.5 * dz, ctr1, ctr2, sectorstartangle, sectorangle, radius): if thicknesscells == 0: - build_face_xz(x, level, z, numIDx, numIDz, + build_face_xz(x, level, z, numIDx, numIDz, rigidE, rigidH, ID) else: for y in range(level, level + thicknesscells): - build_voxel(x, y, z, numID, numIDx, numIDy, numIDz, + build_voxel(x, y, z, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) elif normal == 'z': - # Angles are defined from zero degrees on the positive x-axis going + # Angles are defined from zero degrees on the positive x-axis going # towards positive y-axis. x1 = round_value((ctr1 - radius)/dx) x2 = round_value((ctr1 + radius)/dx) @@ -626,14 +626,14 @@ cpdef void build_cylindrical_sector( for x in range(x1, x2): for y in range(y1, y2): - if is_inside_sector(x * dx + 0.5 * dx, y * dy + 0.5 * dy, ctr1, + if is_inside_sector(x * dx + 0.5 * dx, y * dy + 0.5 * dy, ctr1, ctr2, sectorstartangle, sectorangle, radius): if thicknesscells == 0: - build_face_xy(x, y, level, numIDx, numIDy, + build_face_xy(x, y, level, numIDx, numIDy, rigidE, rigidH, ID) else: for z in range(level, level + thicknesscells): - build_voxel(x, y, z, numID, numIDx, numIDy, numIDz, + build_voxel(x, y, z, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) @@ -661,7 +661,7 @@ cpdef void build_box( xs, xf, ys, yf, zs, zf: ints for cell coordinates of entire box. nthreads: int for number of threads to use numID, numIDx, numIDy, numIDz: ints for numeric ID of material. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays. """ @@ -741,23 +741,23 @@ cpdef void build_cylinder( np.int8_t[:, :, :, ::1] rigidH, np.uint32_t[:, :, :, ::1] ID ): - """Builds cylinders which sets values in the solid, rigid and ID arrays for + """Builds cylinders which sets values in the solid, rigid and ID arrays for a Yee voxel. Args: - x1, y1, z1, x2, y2, z2: floats for coordinates of the centres of cylinder + x1, y1, z1, x2, y2, z2: floats for coordinates of the centres of cylinder faces. r: float for radius of the cylinder. dx, dy, dz: floats for spatial discretisation. numID, numIDx, numIDy, numIDz: ints for numeric ID of material. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays. """ cdef Py_ssize_t i, j, k cdef int xs, xf, ys, yf, zs, zf, xc, yc, zc - cdef float f1f2mag, f2f1mag, f1ptmag, f2ptmag, dot1, dot2, factor1, factor2 + cdef float f1f2mag, f2f1mag, f1ptmag, f2ptmag, dot1, dot2, factor1, factor2 cdef float theta1, theta2, distance1, distance2 cdef bint build, x_align, y_align, z_align cdef np.ndarray f1f2, f2f1, f1pt, f2pt @@ -765,17 +765,17 @@ cpdef void build_cylinder( # Check if cylinder is aligned with an axis x_align = y_align = z_align = 0 # x-aligned - if (round_value(y1 / dy) == round_value(y2 / dy) and + if (round_value(y1 / dy) == round_value(y2 / dy) and round_value(z1 / dz) == round_value(z2 / dz)): x_align = 1 # y-aligned - elif (round_value(x1 / dx) == round_value(x2 / dx) and + elif (round_value(x1 / dx) == round_value(x2 / dx) and round_value(z1 / dz) == round_value(z2 / dz)): y_align = 1 # z-aligned - elif (round_value(x1 / dx) == round_value(x2 / dx) and + elif (round_value(x1 / dx) == round_value(x2 / dx) and round_value(y1 / dy) == round_value(y2 / dy)): z_align = 1 @@ -843,7 +843,7 @@ cpdef void build_cylinder( for k in range(zs, zf): if np.sqrt((j * dy + 0.5 * dy - y1)**2 + (k * dz + 0.5 * dz - z1)**2) <= r: for i in range(xs, xf): - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) # y-aligned cylinder elif y_align: @@ -851,7 +851,7 @@ cpdef void build_cylinder( for k in range(zs, zf): if np.sqrt((i * dx + 0.5 * dx - x1)**2 + (k * dz + 0.5 * dz - z1)**2) <= r: for j in range(ys, yf): - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) # z-aligned cylinder elif z_align: @@ -859,7 +859,7 @@ cpdef void build_cylinder( for j in range(ys, yf): if np.sqrt((i * dx + 0.5 * dx - x1)**2 + (j * dy + 0.5 * dy - y1)**2) <= r: for k in range(zs, zf): - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) # Not aligned with any axis @@ -878,12 +878,12 @@ cpdef void build_cylinder( # Build flag - default false, set to True if point is in cylinder build = 0 # Vector from centre of first cylinder face to test point - f1pt = np.array([i * dx + 0.5 * dx - x1, - j * dy + 0.5 * dy - y1, + f1pt = np.array([i * dx + 0.5 * dx - x1, + j * dy + 0.5 * dy - y1, k * dz + 0.5 * dz - z1], dtype=np.float32) # Vector from centre of second cylinder face to test point - f2pt = np.array([i * dx + 0.5 * dx - x2, - j * dy + 0.5 * dy - y2, + f2pt = np.array([i * dx + 0.5 * dx - x2, + j * dy + 0.5 * dy - y2, k * dz + 0.5 * dz - z2], dtype=np.float32) # Magnitudes f1ptmag = np.sqrt((f1pt*f1pt).sum(axis=0)) @@ -908,12 +908,12 @@ cpdef void build_cylinder( theta2 = 0 distance1 = f1ptmag * np.sin(theta1) distance2 = f2ptmag * np.sin(theta2) - if ((distance1 <= r or distance2 <= r) and + if ((distance1 <= r or distance2 <= r) and theta1 <= np.pi/2 and theta2 <= np.pi/2): build = 1 if build: - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) @@ -939,7 +939,7 @@ cpdef void build_cone( np.int8_t[:, :, :, ::1] rigidH, np.uint32_t[:, :, :, ::1] ID ): - """Builds cones which sets values in the solid, rigid and ID arrays for + """Builds cones which sets values in the solid, rigid and ID arrays for a Yee voxel. Args: @@ -949,14 +949,14 @@ cpdef void build_cone( r2: float for radius of the second face of the cone. dx, dy, dz: floats for spatial discretisation. numID, numIDx, numIDy, numIDz: ints for numeric ID of material. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays. """ cdef Py_ssize_t i, j, k cdef int xs, xf, ys, yf, zs, zf, xc, yc, zc - cdef float f1f2mag, f2f1mag, f1ptmag, f2ptmag, dot1, dot2, factor1, factor2 + cdef float f1f2mag, f2f1mag, f1ptmag, f2ptmag, dot1, dot2, factor1, factor2 cdef float theta1, theta2, distance1, distance2, R1, R2 cdef float height, distance_axis_1, distance_axis_2 cdef bint build, x_align, y_align, z_align @@ -968,17 +968,17 @@ cpdef void build_cone( # Check if cone is aligned with an axis x_align = y_align = z_align = 0 # x-aligned - if (round_value(y1 / dy) == round_value(y2 / dy) and + if (round_value(y1 / dy) == round_value(y2 / dy) and round_value(z1 / dz) == round_value(z2 / dz)): x_align = 1 # y-aligned - elif (round_value(x1 / dx) == round_value(x2 / dx) and + elif (round_value(x1 / dx) == round_value(x2 / dx) and round_value(z1 / dz) == round_value(z2 / dz)): y_align = 1 # z-aligned - elif (round_value(x1 / dx) == round_value(x2 / dx) and + elif (round_value(x1 / dx) == round_value(x2 / dx) and round_value(y1 / dy) == round_value(y2 / dy)): z_align = 1 @@ -1046,7 +1046,7 @@ cpdef void build_cone( for k in range(zs, zf): for i in range(xs, xf): if np.sqrt((j * dy + 0.5 * dy - y1)**2 + (k * dz + 0.5 * dz - z1)**2) <= ((i-xs)/(xf-xs))*(r2-r1) + r1: - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) # y-aligned cone elif y_align: @@ -1054,7 +1054,7 @@ cpdef void build_cone( for k in range(zs, zf): for j in range(ys, yf): if np.sqrt((i * dx + 0.5 * dx - x1)**2 + (k * dz + 0.5 * dz - z1)**2) <= ((j-ys)/(yf-ys))*(r2-r1) + r1: - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) # z-aligned cone elif z_align: @@ -1062,7 +1062,7 @@ cpdef void build_cone( for j in range(ys, yf): for k in range(zs, zf): if np.sqrt((i * dx + 0.5 * dx - x1)**2 + (j * dy + 0.5 * dy - y1)**2) <= ((k-zs)/(zf-zs))*(r2-r1) + r1: - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) # Not aligned with any axis @@ -1083,12 +1083,12 @@ cpdef void build_cone( # Build flag - default false, set to True if point is in cone build = 0 # Vector from centre of first cone face to test point - f1pt = np.array([i * dx + 0.5 * dx - x1, - j * dy + 0.5 * dy - y1, + f1pt = np.array([i * dx + 0.5 * dx - x1, + j * dy + 0.5 * dy - y1, k * dz + 0.5 * dz - z1], dtype=np.float32) # Vector from centre of second cone face to test point - f2pt = np.array([i * dx + 0.5 * dx - x2, - j * dy + 0.5 * dy - y2, + f2pt = np.array([i * dx + 0.5 * dx - x2, + j * dy + 0.5 * dy - y2, k * dz + 0.5 * dz - z2], dtype=np.float32) # Magnitudes f1ptmag = np.sqrt((f1pt*f1pt).sum(axis=0)) @@ -1118,12 +1118,12 @@ cpdef void build_cone( R1 = r1 R2 = r2 - if ((distance1 <= (distance_axis_1/height)*(R2 - R1) + R1 or distance2 <= (distance_axis_2/height)*(R1 - R2) + R2) and + if ((distance1 <= (distance_axis_1/height)*(R2 - R1) + R1 or distance2 <= (distance_axis_2/height)*(R1 - R2) + R2) and theta1 <= np.pi/2 and theta2 <= np.pi/2): build = 1 if build: - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) @@ -1145,7 +1145,7 @@ cpdef void build_sphere( np.int8_t[:, :, :, ::1] rigidH, np.uint32_t[:, :, :, ::1] ID ): - """Builds spheres which sets values in the solid, rigid and ID arrays for + """Builds spheres which sets values in the solid, rigid and ID arrays for a Yee voxel. Args: @@ -1153,7 +1153,7 @@ cpdef void build_sphere( r: float for radius of the sphere. dx, dy, dz: floats for spatial discretisation. numID, numIDx, numIDy, numIDz: ints for numeric ID of material. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays. """ @@ -1186,10 +1186,10 @@ cpdef void build_sphere( for i in range(xs, xf): for j in range(ys, yf): for k in range(zs, zf): - if (np.sqrt((i + 0.5 - xc)**2 * dx**2 + - (j + 0.5 - yc)**2 * dy**2 + + if (np.sqrt((i + 0.5 - xc)**2 * dx**2 + + (j + 0.5 - yc)**2 * dy**2 + (k + 0.5 - zc)**2 * dz**2) <= r): - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) @@ -1213,7 +1213,7 @@ cpdef void build_ellipsoid( np.int8_t[:, :, :, ::1] rigidH, np.uint32_t[:, :, :, ::1] ID ): - """Builds ellipsoids which sets values in the solid, rigid and ID arrays for + """Builds ellipsoids which sets values in the solid, rigid and ID arrays for a Yee voxel. Args: @@ -1223,7 +1223,7 @@ cpdef void build_ellipsoid( zr: float for z-semiaxis of the elliposid. dx, dy, dz: floats for spatial discretisation. numID, numIDx, numIDy, numIDz: ints for numeric ID of material. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays. """ @@ -1256,10 +1256,10 @@ cpdef void build_ellipsoid( for i in range(xs, xf): for j in range(ys, yf): for k in range(zs, zf): - if (((i + 0.5 - xc)**2 * dx**2)/xr**2 + - ((j + 0.5 - yc)**2 * dy**2)/yr**2 + + if (((i + 0.5 - xc)**2 * dx**2)/xr**2 + + ((j + 0.5 - yc)**2 * dy**2)/yr**2 + ((k + 0.5 - zc)**2 * dz**2)/zr**2 <= 1): - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) @@ -1279,12 +1279,12 @@ cpdef void build_voxels_from_array( """Builds Yee voxels by reading integers from an array. Args: - xs, ys, zs: ints for cell coordinates of position of start of array in + xs, ys, zs: ints for cell coordinates of position of start of array in domain. nthreads: int for number of threads to use - numexistmaterials: int for number of existing materials in model prior + numexistmaterials: int for number of existing materials in model prior to building voxels. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. data: memoryview to access array containing numeric IDs of voxels to create. solid, rigidE, rigidH, ID: memoryviews to access solid, rigid and ID arrays. @@ -1345,7 +1345,7 @@ cpdef void build_voxels_from_array_mask( xs, ys, zs: ints for cell coordinates of position of start of array in domain. nthreads: int for number of threads to use waternumID, grassnumID: ints for numeric ID of water and grass materials. - averaging: bint for whether material property averaging will occur for + averaging: bint for whether material property averaging will occur for the object. data: memoryview to access array containing numeric IDs of voxels to create. mask: memoryview to access to array containing a mask of voxels to create. @@ -1365,13 +1365,13 @@ cpdef void build_voxels_from_array_mask( for k in range(zs, zf): if mask[i - xs, j - ys, k - zs] == 1: numID = numIDx = numIDy = numIDz = data[i - xs, j - ys, k - zs] - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) elif mask[i - xs, j - ys, k - zs] == 2: numID = numIDx = numIDy = numIDz = waternumID - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) elif mask[i - xs, j - ys, k - zs] == 3: numID = numIDx = numIDy = numIDz = grassnumID - build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, + build_voxel(i, j, k, numID, numIDx, numIDy, numIDz, averaging, solid, rigidE, rigidH, ID) diff --git a/gprMax/cython/pml_build.pyx b/gprMax/cython/pml_build.pyx index aaa0d211..fbff8ef2 100644 --- a/gprMax/cython/pml_build.pyx +++ b/gprMax/cython/pml_build.pyx @@ -31,14 +31,14 @@ cpdef pml_average_er_mr( float_or_double[::1] ers, float_or_double[::1] mrs ): - """Calculates average permittivity and permeability in PML slab (based on + """Calculates average permittivity and permeability in PML slab (based on underlying material er and mr from solid array). Used to build PML. Args: n1, n2: ints for PML size in cells perpendicular to thickness direction. nthreads: int for number of threads to use. solid: memoryviews to access solid array. - ers, mrs: memoryviews to access arrays containing permittivity and + ers, mrs: memoryviews to access arrays containing permittivity and permeability. Returns: @@ -49,7 +49,7 @@ cpdef pml_average_er_mr( cdef Py_ssize_t m, n cdef int numID # Sum and average of relative permittivities and permeabilities in PML slab - cdef float sumer, summr, averageer, averagemr + cdef float sumer, summr, averageer, averagemr for m in prange(n1, nogil=True, schedule='static', num_threads=nthreads): for n in range(n2): diff --git a/gprMax/cython/pml_updates_electric_HORIPML.pyx b/gprMax/cython/pml_updates_electric_HORIPML.pyx index 5d58d5dd..4ea872fa 100644 --- a/gprMax/cython/pml_updates_electric_HORIPML.pyx +++ b/gprMax/cython/pml_updates_electric_HORIPML.pyx @@ -52,7 +52,7 @@ cpdef void order1_xminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -79,13 +79,13 @@ cpdef void order1_xminus( # Ey materialEy = ID[1, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii - 1, jj, kk]) / dx - Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * (RA01 * dHz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHz # Ez materialEz = ID[2, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii - 1, jj, kk]) / dx - Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * (RA01 * dHy + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHy @@ -118,7 +118,7 @@ cpdef void order2_xminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -150,19 +150,19 @@ cpdef void order2_xminus( # Ey materialEy = ID[1, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii - 1, jj, kk]) / dx - Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * - (RA01 * dHz + RA1 * RB0 * Phi1[0, i, j, k] + + Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * + (RA01 * dHz + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dHz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHz # Ez materialEz = ID[2, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii - 1, jj, kk]) / dx - Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * - (RA01 * dHy + RA1 * RB0 * Phi2[0, i, j, k] + + Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * + (RA01 * dHy + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dHy + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHy @@ -196,7 +196,7 @@ cpdef void order1_xplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -223,13 +223,13 @@ cpdef void order1_xplus( # Ey materialEy = ID[1, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii - 1, jj, kk]) / dx - Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * (RA01 * dHz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHz # Ez materialEz = ID[2, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii - 1, jj, kk]) / dx - Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * (RA01 * dHy + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHy @@ -262,7 +262,7 @@ cpdef void order2_xplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -294,19 +294,19 @@ cpdef void order2_xplus( # Ey materialEy = ID[1, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii - 1, jj, kk]) / dx - Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * - (RA01 * dHz + RA1 * RB0 * Phi1[0, i, j, k] + + Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * + (RA01 * dHz + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dHz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHz # Ez materialEz = ID[2, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii - 1, jj, kk]) / dx - Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * - (RA01 * dHy + RA1 * RB0 * Phi2[0, i, j, k] + + Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * + (RA01 * dHy + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dHy + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHy @@ -340,7 +340,7 @@ cpdef void order1_yminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -367,13 +367,13 @@ cpdef void order1_yminus( # Ex materialEx = ID[0, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii, jj - 1, kk]) / dy - Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * (RA01 * dHz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHz # Ez materialEz = ID[2, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj - 1, kk]) / dy - Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * (RA01 * dHx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHx @@ -406,7 +406,7 @@ cpdef void order2_yminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -438,19 +438,19 @@ cpdef void order2_yminus( # Ex materialEx = ID[0, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii, jj - 1, kk]) / dy - Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * - (RA01 * dHz + RA1 * RB0 * Phi1[0, i, j, k] + + Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * + (RA01 * dHz + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dHz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHz # Ez materialEz = ID[2, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj - 1, kk]) / dy - Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * - (RA01 * dHx + RA1 * RB0 * Phi2[0, i, j, k] + + Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * + (RA01 * dHx + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dHx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHx @@ -484,7 +484,7 @@ cpdef void order1_yplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -511,13 +511,13 @@ cpdef void order1_yplus( # Ex materialEx = ID[0, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii, jj - 1, kk]) / dy - Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * (RA01 * dHz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHz # Ez materialEz = ID[2, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj - 1, kk]) / dy - Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * (RA01 * dHx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHx @@ -550,7 +550,7 @@ cpdef void order2_yplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -582,19 +582,19 @@ cpdef void order2_yplus( # Ex materialEx = ID[0, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii, jj - 1, kk]) / dy - Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * - (RA01 * dHz + RA1 * RB0 * Phi1[0, i, j, k] + + Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * + (RA01 * dHz + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dHz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHz # Ez materialEz = ID[2, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj - 1, kk]) / dy - Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * - (RA01 * dHx + RA1 * RB0 * Phi2[0, i, j, k] + + Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * + (RA01 * dHx + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dHx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHx @@ -628,7 +628,7 @@ cpdef void order1_zminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -655,13 +655,13 @@ cpdef void order1_zminus( # Ex materialEx = ID[0, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii, jj, kk - 1]) / dz - Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * (RA01 * dHy + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHy # Ey materialEy = ID[1, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj, kk - 1]) / dz - Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * (RA01 * dHx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHx @@ -694,7 +694,7 @@ cpdef void order2_zminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -726,19 +726,19 @@ cpdef void order2_zminus( # Ex materialEx = ID[0, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii, jj, kk - 1]) / dz - Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * - (RA01 * dHy + RA1 * RB0 * Phi1[0, i, j, k] + + Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * + (RA01 * dHy + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dHy + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHy # Ey materialEy = ID[1, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj, kk - 1]) / dz - Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * - (RA01 * dHx + RA1 * RB0 * Phi2[0, i, j, k] + + Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * + (RA01 * dHx + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dHx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHx @@ -772,7 +772,7 @@ cpdef void order1_zplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -799,13 +799,13 @@ cpdef void order1_zplus( # Ex materialEx = ID[0, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii, jj, kk - 1]) / dz - Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * (RA01 * dHy + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHy # Ey materialEy = ID[1, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj, kk - 1]) / dz - Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * (RA01 * dHx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHx @@ -838,7 +838,7 @@ cpdef void order2_zplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -870,18 +870,18 @@ cpdef void order2_zplus( # Ex materialEx = ID[0, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii, jj, kk - 1]) / dz - Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * - (RA01 * dHy + RA1 * RB0 * Phi1[0, i, j, k] + + Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * + (RA01 * dHy + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dHy + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dHy # Ey materialEy = ID[1, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj, kk - 1]) / dz - Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * - (RA01 * dHx + RA1 * RB0 * Phi2[0, i, j, k] + + Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * + (RA01 * dHx + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dHx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dHx diff --git a/gprMax/cython/pml_updates_electric_MRIPML.pyx b/gprMax/cython/pml_updates_electric_MRIPML.pyx index c7277a24..51cc64fb 100644 --- a/gprMax/cython/pml_updates_electric_MRIPML.pyx +++ b/gprMax/cython/pml_updates_electric_MRIPML.pyx @@ -52,7 +52,7 @@ cpdef void order1_xminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -81,16 +81,16 @@ cpdef void order1_xminus( # Ey materialEy = ID[1, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii - 1, jj, kk]) / dx - Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * (IRA1 * dHz - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHz - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHz - RC0 * Phi1[0, i, j, k]) # Ez materialEz = ID[2, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii - 1, jj, kk]) / dx - Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * (IRA1 * dHy - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHy - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHy - RC0 * Phi2[0, i, j, k]) cpdef void order2_xminus( @@ -122,7 +122,7 @@ cpdef void order2_xminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -132,7 +132,7 @@ cpdef void order2_xminus( cdef int nx, ny, nz, materialEy, materialEz cdef float_or_double dx, dHy, dHz, IRA, IRA1, RB0, RC0, RE0, RF0 cdef float_or_double RB1, RC1, RE1, RF1, Psi1, Psi2 - + dx = d nx = xf - xs ny = yf - ys @@ -159,14 +159,14 @@ cpdef void order2_xminus( # Ey materialEy = ID[1, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii - 1, jj, kk]) / dx - Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * (IRA1 * dHz - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dHz - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dHz - Psi1) # Ez materialEz = ID[2, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii - 1, jj, kk]) / dx - Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * (IRA1 * dHy - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dHy - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dHy - Psi2) @@ -201,7 +201,7 @@ cpdef void order1_xplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -230,16 +230,16 @@ cpdef void order1_xplus( # Ey materialEy = ID[1, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii - 1, jj, kk]) / dx - Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * (IRA1 * dHz - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHz - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHz - RC0 * Phi1[0, i, j, k]) # Ez materialEz = ID[2, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii - 1, jj, kk]) / dx - Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * (IRA1 * dHy - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHy - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHy - RC0 * Phi2[0, i, j, k]) cpdef void order2_xplus( @@ -271,7 +271,7 @@ cpdef void order2_xplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -307,14 +307,14 @@ cpdef void order2_xplus( # Ey materialEy = ID[1, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii - 1, jj, kk]) / dx - Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] - updatecoeffsE[materialEy, 4] * (IRA1 * dHz - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dHz - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dHz - Psi1) # Ez materialEz = ID[2, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii - 1, jj, kk]) / dx - Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] + updatecoeffsE[materialEz, 4] * (IRA1 * dHy - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dHy - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dHy - Psi2) @@ -349,7 +349,7 @@ cpdef void order1_yminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -378,16 +378,16 @@ cpdef void order1_yminus( # Ex materialEx = ID[0, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii, jj - 1, kk]) / dy - Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * (IRA1 * dHz - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHz - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHz - RC0 * Phi1[0, i, j, k]) # Ez materialEz = ID[2, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj - 1, kk]) / dy - Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * (IRA1 * dHx - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHx - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHx - RC0 * Phi2[0, i, j, k]) cpdef void order2_yminus( @@ -419,7 +419,7 @@ cpdef void order2_yminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -455,14 +455,14 @@ cpdef void order2_yminus( # Ex materialEx = ID[0, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii, jj - 1, kk]) / dy - Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * (IRA1 * dHz - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dHz - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dHz - Psi1) # Ez materialEz = ID[2, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj - 1, kk]) / dy - Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * (IRA1 * dHx - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dHx - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dHx - Psi2) @@ -497,7 +497,7 @@ cpdef void order1_yplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -526,16 +526,16 @@ cpdef void order1_yplus( # Ex materialEx = ID[0, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii, jj - 1, kk]) / dy - Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * (IRA1 * dHz - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHz - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHz - RC0 * Phi1[0, i, j, k]) # Ez materialEz = ID[2, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj - 1, kk]) / dy - Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * (IRA1 * dHx - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHx - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHx - RC0 * Phi2[0, i, j, k]) cpdef void order2_yplus( @@ -567,7 +567,7 @@ cpdef void order2_yplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -603,14 +603,14 @@ cpdef void order2_yplus( # Ex materialEx = ID[0, ii, jj, kk] dHz = (Hz[ii, jj, kk] - Hz[ii, jj - 1, kk]) / dy - Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] + updatecoeffsE[materialEx, 4] * (IRA1 * dHz - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dHz - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dHz - Psi1) # Ez materialEz = ID[2, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj - 1, kk]) / dy - Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * + Ez[ii, jj, kk] = (Ez[ii, jj, kk] - updatecoeffsE[materialEz, 4] * (IRA1 * dHx - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dHx - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dHx - Psi2) @@ -645,7 +645,7 @@ cpdef void order1_zminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -674,16 +674,16 @@ cpdef void order1_zminus( # Ex materialEx = ID[0, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii, jj, kk - 1]) / dz - Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * (IRA1 * dHy - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHy - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHy - RC0 * Phi1[0, i, j, k]) # Ey materialEy = ID[1, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj, kk - 1]) / dz - Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * (IRA1 * dHx - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHx - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHx - RC0 * Phi2[0, i, j, k]) cpdef void order2_zminus( @@ -715,7 +715,7 @@ cpdef void order2_zminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -750,14 +750,14 @@ cpdef void order2_zminus( # Ex materialEx = ID[0, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii, jj, kk - 1]) / dz - Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * (IRA1 * dHy - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dHy - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dHy - Psi1) # Ey materialEy = ID[1, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj, kk - 1]) / dz - Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * (IRA1 * dHx - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dHx - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dHx - Psi2) @@ -792,7 +792,7 @@ cpdef void order1_zplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -821,16 +821,16 @@ cpdef void order1_zplus( # Ex materialEx = ID[0, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii, jj, kk - 1]) / dz - Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * (IRA1 * dHy - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHy - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dHy - RC0 * Phi1[0, i, j, k]) # Ey materialEy = ID[1, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj, kk - 1]) / dz - Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * (IRA1 * dHx - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHx - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dHx - RC0 * Phi2[0, i, j, k]) cpdef void order2_zplus( @@ -862,7 +862,7 @@ cpdef void order2_zplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -898,14 +898,14 @@ cpdef void order2_zplus( # Ex materialEx = ID[0, ii, jj, kk] dHy = (Hy[ii, jj, kk] - Hy[ii, jj, kk - 1]) / dz - Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * + Ex[ii, jj, kk] = (Ex[ii, jj, kk] - updatecoeffsE[materialEx, 4] * (IRA1 * dHy - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dHy - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dHy - Psi1) # Ey materialEy = ID[1, ii, jj, kk] dHx = (Hx[ii, jj, kk] - Hx[ii, jj, kk - 1]) / dz - Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * + Ey[ii, jj, kk] = (Ey[ii, jj, kk] + updatecoeffsE[materialEy, 4] * (IRA1 * dHx - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dHx - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dHx - Psi2) diff --git a/gprMax/cython/pml_updates_magnetic_HORIPML.pyx b/gprMax/cython/pml_updates_magnetic_HORIPML.pyx index ca458483..7088467d 100644 --- a/gprMax/cython/pml_updates_magnetic_HORIPML.pyx +++ b/gprMax/cython/pml_updates_magnetic_HORIPML.pyx @@ -52,7 +52,7 @@ cpdef void order1_xminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -79,13 +79,13 @@ cpdef void order1_xminus( # Hy materialHy = ID[4, ii, jj, kk] dEz = (Ez[ii + 1, jj, kk] - Ez[ii, jj, kk]) / dx - Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * (RA01 * dEz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEz # Hz materialHz = ID[5, ii, jj, kk] dEy = (Ey[ii + 1, jj, kk] - Ey[ii, jj, kk]) / dx - Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * (RA01 * dEy + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEy @@ -118,7 +118,7 @@ cpdef void order2_xminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -150,19 +150,19 @@ cpdef void order2_xminus( # Hy materialHy = ID[4, ii, jj, kk] dEz = (Ez[ii + 1, jj, kk] - Ez[ii, jj, kk]) / dx - Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * - (RA01 * dEz + RA1 * RB0 * Phi1[0, i, j, k] + + Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * + (RA01 * dEz + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dEz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEz # Hz materialHz = ID[5, ii, jj, kk] dEy = (Ey[ii + 1, jj, kk] - Ey[ii, jj, kk]) / dx - Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * - (RA01 * dEy + RA1 * RB0 * Phi2[0, i, j, k] + + Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * + (RA01 * dEy + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dEy + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEy @@ -196,7 +196,7 @@ cpdef void order1_xplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -223,13 +223,13 @@ cpdef void order1_xplus( # Hy materialHy = ID[4, ii, jj, kk] dEz = (Ez[ii + 1, jj, kk] - Ez[ii, jj, kk]) / dx - Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * (RA01 * dEz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEz # Hz materialHz = ID[5, ii, jj, kk] dEy = (Ey[ii + 1, jj, kk] - Ey[ii, jj, kk]) / dx - Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * (RA01 * dEy + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEy @@ -262,7 +262,7 @@ cpdef void order2_xplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -294,19 +294,19 @@ cpdef void order2_xplus( # Hy materialHy = ID[4, ii, jj, kk] dEz = (Ez[ii + 1, jj, kk] - Ez[ii, jj, kk]) / dx - Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * - (RA01 * dEz + RA1 * RB0 * Phi1[0, i, j, k] + + Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * + (RA01 * dEz + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dEz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEz # Hz materialHz = ID[5, ii, jj, kk] dEy = (Ey[ii + 1, jj, kk] - Ey[ii, jj, kk]) / dx - Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * - (RA01 * dEy + RA1 * RB0 * Phi2[0, i, j, k] + + Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * + (RA01 * dEy + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dEy + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEy @@ -340,7 +340,7 @@ cpdef void order1_yminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -367,13 +367,13 @@ cpdef void order1_yminus( # Hx materialHx = ID[3, ii, jj, kk] dEz = (Ez[ii, jj + 1, kk] - Ez[ii, jj, kk]) / dy - Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * (RA01 * dEz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEz # Hz materialHz = ID[5, ii, jj, kk] dEx = (Ex[ii, jj + 1, kk] - Ex[ii, jj, kk]) / dy - Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * (RA01 * dEx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEx @@ -406,7 +406,7 @@ cpdef void order2_yminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -438,19 +438,19 @@ cpdef void order2_yminus( # Hx materialHx = ID[3, ii, jj, kk] dEz = (Ez[ii, jj + 1, kk] - Ez[ii, jj, kk]) / dy - Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * - (RA01 * dEz + RA1 * RB0 * Phi1[0, i, j, k] + + Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * + (RA01 * dEz + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dEz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEz # Hz materialHz = ID[5, ii, jj, kk] dEx = (Ex[ii, jj + 1, kk] - Ex[ii, jj, kk]) / dy - Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * - (RA01 * dEx + RA1 * RB0 * Phi2[0, i, j, k] + + Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * + (RA01 * dEx + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dEx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEx @@ -484,7 +484,7 @@ cpdef void order1_yplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -511,13 +511,13 @@ cpdef void order1_yplus( # Hx materialHx = ID[3, ii, jj, kk] dEz = (Ez[ii, jj + 1, kk] - Ez[ii, jj, kk]) / dy - Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * (RA01 * dEz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEz # Hz materialHz = ID[5, ii, jj, kk] dEx = (Ex[ii, jj + 1, kk] - Ex[ii, jj, kk]) / dy - Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * (RA01 * dEx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEx @@ -550,7 +550,7 @@ cpdef void order2_yplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -582,19 +582,19 @@ cpdef void order2_yplus( # Hx materialHx = ID[3, ii, jj, kk] dEz = (Ez[ii, jj + 1, kk] - Ez[ii, jj, kk]) / dy - Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * - (RA01 * dEz + RA1 * RB0 * Phi1[0, i, j, k] + + Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * + (RA01 * dEz + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dEz + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEz # Hz materialHz = ID[5, ii, jj, kk] dEx = (Ex[ii, jj + 1, kk] - Ex[ii, jj, kk]) / dy - Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * - (RA01 * dEx + RA1 * RB0 * Phi2[0, i, j, k] + + Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * + (RA01 * dEx + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dEx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEx @@ -628,7 +628,7 @@ cpdef void order1_zminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -655,13 +655,13 @@ cpdef void order1_zminus( # Hx materialHx = ID[3, ii, jj, kk] dEy = (Ey[ii, jj, kk + 1] - Ey[ii, jj, kk]) / dz - Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * (RA01 * dEy + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEy # Hy materialHy = ID[4, ii, jj, kk] dEx = (Ex[ii, jj, kk + 1] - Ex[ii, jj, kk]) / dz - Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * (RA01 * dEx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEx @@ -694,7 +694,7 @@ cpdef void order2_zminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -726,19 +726,19 @@ cpdef void order2_zminus( # Hx materialHx = ID[3, ii, jj, kk] dEy = (Ey[ii, jj, kk + 1] - Ey[ii, jj, kk]) / dz - Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * - (RA01 * dEy + RA1 * RB0 * Phi1[0, i, j, k] + + Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * + (RA01 * dEy + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dEy + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEy # Hy materialHy = ID[4, ii, jj, kk] dEx = (Ex[ii, jj, kk + 1] - Ex[ii, jj, kk]) / dz - Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * - (RA01 * dEx + RA1 * RB0 * Phi2[0, i, j, k] + + Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * + (RA01 * dEx + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dEx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEx @@ -772,7 +772,7 @@ cpdef void order1_zplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -799,13 +799,13 @@ cpdef void order1_zplus( # Hx materialHx = ID[3, ii, jj, kk] dEy = (Ey[ii, jj, kk + 1] - Ey[ii, jj, kk]) / dz - Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * (RA01 * dEy + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEy # Hy materialHy = ID[4, ii, jj, kk] dEx = (Ex[ii, jj, kk + 1] - Ex[ii, jj, kk]) / dz - Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * (RA01 * dEx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEx @@ -838,7 +838,7 @@ cpdef void order2_zplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -870,18 +870,18 @@ cpdef void order2_zplus( # Hx materialHx = ID[3, ii, jj, kk] dEy = (Ey[ii, jj, kk + 1] - Ey[ii, jj, kk]) / dz - Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * - (RA01 * dEy + RA1 * RB0 * Phi1[0, i, j, k] + + Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * + (RA01 * dEy + RA1 * RB0 * Phi1[0, i, j, k] + RB1 * Phi1[1, i, j, k])) - Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * + Phi1[1, i, j, k] = (RE1 * Phi1[1, i, j, k] - RF1 * (RA0 * dEy + RB0 * Phi1[0, i, j, k])) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] - RF0 * dEy # Hy materialHy = ID[4, ii, jj, kk] dEx = (Ex[ii, jj, kk + 1] - Ex[ii, jj, kk]) / dz - Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * - (RA01 * dEx + RA1 * RB0 * Phi2[0, i, j, k] + + Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * + (RA01 * dEx + RA1 * RB0 * Phi2[0, i, j, k] + RB1 * Phi2[1, i, j, k])) - Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * + Phi2[1, i, j, k] = (RE1 * Phi2[1, i, j, k] - RF1 * (RA0 * dEx + RB0 * Phi2[0, i, j, k])) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] - RF0 * dEx diff --git a/gprMax/cython/pml_updates_magnetic_MRIPML.pyx b/gprMax/cython/pml_updates_magnetic_MRIPML.pyx index 9179e464..bd7161ee 100644 --- a/gprMax/cython/pml_updates_magnetic_MRIPML.pyx +++ b/gprMax/cython/pml_updates_magnetic_MRIPML.pyx @@ -52,7 +52,7 @@ cpdef void order1_xminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -81,16 +81,16 @@ cpdef void order1_xminus( # Hy materialHy = ID[4, ii, jj, kk] dEz = (Ez[ii + 1, jj, kk] - Ez[ii, jj, kk]) / dx - Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * (IRA1 * dEz - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEz - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEz - RC0 * Phi1[0, i, j, k]) # Hz materialHz = ID[5, ii, jj, kk] dEy = (Ey[ii + 1, jj, kk] - Ey[ii, jj, kk]) / dx - Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * (IRA1 * dEy - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEy - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEy - RC0 * Phi2[0, i, j, k]) cpdef void order2_xminus( @@ -122,7 +122,7 @@ cpdef void order2_xminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -158,14 +158,14 @@ cpdef void order2_xminus( # Hy materialHy = ID[4, ii, jj, kk] dEz = (Ez[ii + 1, jj, kk] - Ez[ii, jj, kk]) / dx - Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * (IRA1 * dEz - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dEz - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dEz - Psi1) # Hz materialHz = ID[5, ii, jj, kk] dEy = (Ey[ii + 1, jj, kk] - Ey[ii, jj, kk]) / dx - Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * (IRA1 * dEy - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dEy - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dEy - Psi2) @@ -200,7 +200,7 @@ cpdef void order1_xplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -229,16 +229,16 @@ cpdef void order1_xplus( # Hy materialHy = ID[4, ii, jj, kk] dEz = (Ez[ii + 1, jj, kk] - Ez[ii, jj, kk]) / dx - Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * (IRA1 * dEz - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEz - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEz - RC0 * Phi1[0, i, j, k]) # Hz materialHz = ID[5, ii, jj, kk] dEy = (Ey[ii + 1, jj, kk] - Ey[ii, jj, kk]) / dx - Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * (IRA1 * dEy - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEy - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEy - RC0 * Phi2[0, i, j, k]) cpdef void order2_xplus( @@ -270,7 +270,7 @@ cpdef void order2_xplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -306,14 +306,14 @@ cpdef void order2_xplus( # Hy materialHy = ID[4, ii, jj, kk] dEz = (Ez[ii + 1, jj, kk] - Ez[ii, jj, kk]) / dx - Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] + updatecoeffsH[materialHy, 4] * (IRA1 * dEz - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dEz - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dEz - Psi1) # Hz materialHz = ID[5, ii, jj, kk] dEy = (Ey[ii + 1, jj, kk] - Ey[ii, jj, kk]) / dx - Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] - updatecoeffsH[materialHz, 4] * (IRA1 * dEy - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dEy - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dEy - Psi2) @@ -348,7 +348,7 @@ cpdef void order1_yminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -377,16 +377,16 @@ cpdef void order1_yminus( # Hx materialHx = ID[3, ii, jj, kk] dEz = (Ez[ii, jj + 1, kk] - Ez[ii, jj, kk]) / dy - Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * (IRA1 * dEz - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEz - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEz - RC0 * Phi1[0, i, j, k]) # Hz materialHz = ID[5, ii, jj, kk] dEx = (Ex[ii, jj + 1, kk] - Ex[ii, jj, kk]) / dy - Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * (IRA1 * dEx - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEx - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEx - RC0 * Phi2[0, i, j, k]) cpdef void order2_yminus( @@ -418,7 +418,7 @@ cpdef void order2_yminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -454,14 +454,14 @@ cpdef void order2_yminus( # Hx materialHx = ID[3, ii, jj, kk] dEz = (Ez[ii, jj + 1, kk] - Ez[ii, jj, kk]) / dy - Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * (IRA1 * dEz - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dEz - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dEz - Psi1) # Hz materialHz = ID[5, ii, jj, kk] dEx = (Ex[ii, jj + 1, kk] - Ex[ii, jj, kk]) / dy - Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * (IRA1 * dEx - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dEx - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dEx - Psi2) @@ -496,7 +496,7 @@ cpdef void order1_yplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -525,16 +525,16 @@ cpdef void order1_yplus( # Hx materialHx = ID[3, ii, jj, kk] dEz = (Ez[ii, jj + 1, kk] - Ez[ii, jj, kk]) / dy - Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * (IRA1 * dEz - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEz - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEz - RC0 * Phi1[0, i, j, k]) # Hz materialHz = ID[5, ii, jj, kk] dEx = (Ex[ii, jj + 1, kk] - Ex[ii, jj, kk]) / dy - Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * (IRA1 * dEx - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEx - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEx - RC0 * Phi2[0, i, j, k]) cpdef void order2_yplus( @@ -566,7 +566,7 @@ cpdef void order2_yplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -602,14 +602,14 @@ cpdef void order2_yplus( # Hx materialHx = ID[3, ii, jj, kk] dEz = (Ez[ii, jj + 1, kk] - Ez[ii, jj, kk]) / dy - Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] - updatecoeffsH[materialHx, 4] * (IRA1 * dEz - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dEz - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dEz - Psi1) # Hz materialHz = ID[5, ii, jj, kk] dEx = (Ex[ii, jj + 1, kk] - Ex[ii, jj, kk]) / dy - Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * + Hz[ii, jj, kk] = (Hz[ii, jj, kk] + updatecoeffsH[materialHz, 4] * (IRA1 * dEx - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dEx - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dEx - Psi2) @@ -644,7 +644,7 @@ cpdef void order1_zminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -673,16 +673,16 @@ cpdef void order1_zminus( # Hx materialHx = ID[3, ii, jj, kk] dEy = (Ey[ii, jj, kk + 1] - Ey[ii, jj, kk]) / dz - Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * (IRA1 * dEy - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEy - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEy - RC0 * Phi1[0, i, j, k]) # Hy materialHy = ID[4, ii, jj, kk] dEx = (Ex[ii, jj, kk + 1] - Ex[ii, jj, kk]) / dz - Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * (IRA1 * dEx - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEx - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEx - RC0 * Phi2[0, i, j, k]) cpdef void order2_zminus( @@ -714,7 +714,7 @@ cpdef void order2_zminus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -750,14 +750,14 @@ cpdef void order2_zminus( # Hx materialHx = ID[3, ii, jj, kk] dEy = (Ey[ii, jj, kk + 1] - Ey[ii, jj, kk]) / dz - Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * (IRA1 * dEy - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dEy - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dEy - Psi1) # Hy materialHy = ID[4, ii, jj, kk] dEx = (Ex[ii, jj, kk + 1] - Ex[ii, jj, kk]) / dz - Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * (IRA1 * dEx - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dEx - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dEx - Psi2) @@ -792,7 +792,7 @@ cpdef void order1_zplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -821,16 +821,16 @@ cpdef void order1_zplus( # Hx materialHx = ID[3, ii, jj, kk] dEy = (Ey[ii, jj, kk + 1] - Ey[ii, jj, kk]) / dz - Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * (IRA1 * dEy - IRA * Phi1[0, i, j, k])) - Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEy - + Phi1[0, i, j, k] = (RE0 * Phi1[0, i, j, k] + RC0 * dEy - RC0 * Phi1[0, i, j, k]) # Hy materialHy = ID[4, ii, jj, kk] dEx = (Ex[ii, jj, kk + 1] - Ex[ii, jj, kk]) / dz - Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * (IRA1 * dEx - IRA * Phi2[0, i, j, k])) - Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEx - + Phi2[0, i, j, k] = (RE0 * Phi2[0, i, j, k] + RC0 * dEx - RC0 * Phi2[0, i, j, k]) cpdef void order2_zplus( @@ -862,7 +862,7 @@ cpdef void order2_zplus( Args: xs, xf, ys, yf, zs, zf: ints for cell coordinates of PML slab. nthreads: int for number of threads to use. - updatecoeffs, ID, E, H: memoryviews to access update coefficients, + updatecoeffs, ID, E, H: memoryviews to access update coefficients, ID and field component arrays. Phi, RA, RB, RE, RF: memoryviews to access PML coefficient arrays. d: float for spatial discretisation, e.g. dx, dy or dz. @@ -898,14 +898,14 @@ cpdef void order2_zplus( # Hx materialHx = ID[3, ii, jj, kk] dEy = (Ey[ii, jj, kk + 1] - Ey[ii, jj, kk]) / dz - Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * + Hx[ii, jj, kk] = (Hx[ii, jj, kk] + updatecoeffsH[materialHx, 4] * (IRA1 * dEy - IRA * Psi1)) Phi1[1, i, j, k] = RE1 * Phi1[1, i, j, k] + RC1 * (dEy - Psi1) Phi1[0, i, j, k] = RE0 * Phi1[0, i, j, k] + RC0 * (dEy - Psi1) # Hy materialHy = ID[4, ii, jj, kk] dEx = (Ex[ii, jj, kk + 1] - Ex[ii, jj, kk]) / dz - Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * + Hy[ii, jj, kk] = (Hy[ii, jj, kk] - updatecoeffsH[materialHy, 4] * (IRA1 * dEx - IRA * Psi2)) Phi2[1, i, j, k] = RE1 * Phi2[1, i, j, k] + RC1 * (dEx - Psi2) Phi2[0, i, j, k] = RE0 * Phi2[0, i, j, k] + RC0 * (dEx - Psi2) diff --git a/gprMax/cython/snapshots.pyx b/gprMax/cython/snapshots.pyx index fd7a6888..5a4fcf37 100644 --- a/gprMax/cython/snapshots.pyx +++ b/gprMax/cython/snapshots.pyx @@ -45,7 +45,7 @@ cpdef void calculate_snapshot_fields( float_or_double[:, :, ::1] Hysnap, float_or_double[:, :, ::1] Hzsnap ): - """Calculates electric and magnetic values at points from averaging values + """Calculates electric and magnetic values at points from averaging values in cells. Args: @@ -64,30 +64,30 @@ cpdef void calculate_snapshot_fields( # The electric field component value at a point comes from the # average of the 4 electric field component values in that cell. if isEx: - Exsnap[i, j, k] = (Exslice[i, j, k] + + Exsnap[i, j, k] = (Exslice[i, j, k] + Exslice[i, j + 1, k] + - Exslice[i, j, k + 1] + + Exslice[i, j, k + 1] + Exslice[i, j + 1, k + 1]) / 4 if isEy: - Eysnap[i, j, k] = (Eyslice[i, j, k] + + Eysnap[i, j, k] = (Eyslice[i, j, k] + Eyslice[i + 1, j, k] + - Eyslice[i, j, k + 1] + + Eyslice[i, j, k + 1] + Eyslice[i + 1, j, k + 1]) / 4 if isEz: - Ezsnap[i, j, k] = (Ezslice[i, j, k] + + Ezsnap[i, j, k] = (Ezslice[i, j, k] + Ezslice[i + 1, j, k] + - Ezslice[i, j + 1, k] + + Ezslice[i, j + 1, k] + Ezslice[i + 1, j + 1, k]) / 4 # The magnetic field component value at a point comes from # average of 2 magnetic field component values in that cell and # the neighbouring cell. if isHx: - Hxsnap[i, j, k] = (Hxslice[i, j, k] + + Hxsnap[i, j, k] = (Hxslice[i, j, k] + Hxslice[i + 1, j, k]) / 2 if isHy: - Hysnap[i, j, k] = (Hyslice[i, j, k] + + Hysnap[i, j, k] = (Hyslice[i, j, k] + Hyslice[i, j + 1, k]) / 2 if isHz: - Hzsnap[i, j, k] = (Hzslice[i, j, k] + + Hzsnap[i, j, k] = (Hzslice[i, j, k] + Hzslice[i, j, k + 1]) / 2 diff --git a/gprMax/cython/yee_cell_build.pyx b/gprMax/cython/yee_cell_build.pyx index 52f41791..c0016dd2 100644 --- a/gprMax/cython/yee_cell_build.pyx +++ b/gprMax/cython/yee_cell_build.pyx @@ -29,17 +29,17 @@ from gprMax.materials import Material cpdef void create_electric_average( - int i, - int j, - int k, - int numID1, - int numID2, - int numID3, - int numID4, - int componentID, + int i, + int j, + int k, + int numID1, + int numID2, + int numID3, + int numID4, + int componentID, G ): - """Creates a new material by averaging the dielectric properties of the + """Creates a new material by averaging the dielectric properties of the surrounding cells. Args: @@ -50,7 +50,7 @@ cpdef void create_electric_average( """ # Make an ID composed of the names of the four materials that will be averaged - requiredID = (G.materials[numID1].ID + '+' + G.materials[numID2].ID + '+' + + requiredID = (G.materials[numID1].ID + '+' + G.materials[numID2].ID + '+' + G.materials[numID3].ID + '+' + G.materials[numID4].ID) # Check if this material already exists @@ -69,13 +69,13 @@ cpdef void create_electric_average( m = Material(newNumID, requiredID) m.type = 'dielectric-smoothed' # Create averaged constituents for material - m.er = np.mean((G.materials[numID1].er, G.materials[numID2].er, + m.er = np.mean((G.materials[numID1].er, G.materials[numID2].er, G.materials[numID3].er, G.materials[numID4].er), axis=0) - m.se = np.mean((G.materials[numID1].se, G.materials[numID2].se, + m.se = np.mean((G.materials[numID1].se, G.materials[numID2].se, G.materials[numID3].se, G.materials[numID4].se), axis=0) - m.mr = np.mean((G.materials[numID1].mr, G.materials[numID2].mr, + m.mr = np.mean((G.materials[numID1].mr, G.materials[numID2].mr, G.materials[numID3].mr, G.materials[numID4].mr), axis=0) - m.sm = np.mean((G.materials[numID1].sm, G.materials[numID2].sm, + m.sm = np.mean((G.materials[numID1].sm, G.materials[numID2].sm, G.materials[numID3].sm, G.materials[numID4].sm), axis=0) # Append the new material object to the materials list @@ -85,15 +85,15 @@ cpdef void create_electric_average( cpdef void create_magnetic_average( - int i, - int j, - int k, - int numID1, - int numID2, - int componentID, + int i, + int j, + int k, + int numID1, + int numID2, + int componentID, G ): - """Creates a new material by averaging the dielectric properties of the + """Creates a new material by averaging the dielectric properties of the surrounding cells. Args: @@ -133,9 +133,9 @@ cpdef void create_magnetic_average( cpdef void build_electric_components( - np.uint32_t[:, :, ::1] solid, - np.int8_t[:, :, :, ::1] rigidE, - np.uint32_t[:, :, :, ::1] ID, + np.uint32_t[:, :, ::1] solid, + np.int8_t[:, :, :, ::1] rigidE, + np.uint32_t[:, :, :, ::1] ID, G ): """Builds the electric field components in the ID array. @@ -172,7 +172,7 @@ cpdef void build_electric_components( ID[IDEx, i, j, k] = numID1 else: # Averaging is required - create_electric_average(i, j, k, numID1, numID2, + create_electric_average(i, j, k, numID1, numID2, numID3, numID4, IDEx, G) # Ey component @@ -190,7 +190,7 @@ cpdef void build_electric_components( ID[IDEy, i, j, k] = numID1 else: # Averaging is required - create_electric_average(i, j, k, numID1, numID2, + create_electric_average(i, j, k, numID1, numID2, numID3, numID4, IDEy, G) # Ez component @@ -208,7 +208,7 @@ cpdef void build_electric_components( ID[IDEz, i, j, k] = numID1 else: # Averaging is required - create_electric_average(i, j, k, numID1, numID2, + create_electric_average(i, j, k, numID1, numID2, numID3, numID4, IDEz, G) # Extra loops for Ex component @@ -229,7 +229,7 @@ cpdef void build_electric_components( ID[IDEx, i, j, k] = numID1 else: # Averaging is required - create_electric_average(i, j, k, numID1, numID2, + create_electric_average(i, j, k, numID1, numID2, numID3, numID4, IDEx, G) # Extra loops for Ey component @@ -250,7 +250,7 @@ cpdef void build_electric_components( ID[IDEy, i, j, k] = numID1 else: # Averaging is required - create_electric_average(i, j, k, numID1, numID2, + create_electric_average(i, j, k, numID1, numID2, numID3, numID4, IDEy, G) # Extra loops for Ez component @@ -271,14 +271,14 @@ cpdef void build_electric_components( ID[IDEz, i, j, k] = numID1 else: # Averaging is required - create_electric_average(i, j, k, numID1, numID2, + create_electric_average(i, j, k, numID1, numID2, numID3, numID4, IDEz, G) cpdef void build_magnetic_components( - np.uint32_t[:, :, ::1] solid, - np.int8_t[:, :, :, ::1] rigidH, - np.uint32_t[:, :, :, ::1] ID, + np.uint32_t[:, :, ::1] solid, + np.int8_t[:, :, :, ::1] rigidH, + np.uint32_t[:, :, :, ::1] ID, G ): """Builds the magnetic field components in the ID array. diff --git a/gprMax/cython/yee_cell_setget_rigid.pyx b/gprMax/cython/yee_cell_setget_rigid.pyx index 8bb6dd78..6aab5ed1 100644 --- a/gprMax/cython/yee_cell_setget_rigid.pyx +++ b/gprMax/cython/yee_cell_setget_rigid.pyx @@ -24,9 +24,9 @@ cimport numpy as np # is 4D with the 1st dimension holding the 12 electric edge components of a # cell - Ex1, Ex2, Ex3, Ex4, Ey1, Ey2, Ey3, Ey4, Ez1, Ez2, Ez3, Ez4 cdef bint get_rigid_Ex( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidE ) nogil: @@ -47,9 +47,9 @@ cdef bint get_rigid_Ex( cdef bint get_rigid_Ey( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidE ) nogil: cdef bint result @@ -69,9 +69,9 @@ cdef bint get_rigid_Ey( cdef bint get_rigid_Ez( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidE ) nogil: cdef bint result @@ -91,9 +91,9 @@ cdef bint get_rigid_Ez( cdef void set_rigid_Ex( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidE ) nogil: rigidE[0, i, j, k] = True @@ -106,9 +106,9 @@ cdef void set_rigid_Ex( cdef void set_rigid_Ey( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidE ) nogil: rigidE[4, i, j, k] = True @@ -121,9 +121,9 @@ cdef void set_rigid_Ey( cdef void set_rigid_Ez( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidE ) nogil: rigidE[8, i, j, k] = True @@ -136,18 +136,18 @@ cdef void set_rigid_Ez( cdef void set_rigid_E( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidE ) nogil: rigidE[:, i, j, k] = True cdef void unset_rigid_E( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidE ) nogil: rigidE[:, i, j, k] = False @@ -156,9 +156,9 @@ cdef void unset_rigid_E( # is 4D with the 1st dimension holding the 6 magnetic edge components - Hx1, # Hx2, Hy1, Hy2, Hz1, Hz2 cdef bint get_rigid_Hx( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidH ) nogil: cdef bint result @@ -172,9 +172,9 @@ cdef bint get_rigid_Hx( cdef bint get_rigid_Hy( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidH ) nogil: cdef bint result @@ -188,9 +188,9 @@ cdef bint get_rigid_Hy( cdef bint get_rigid_Hz( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidH ) nogil: cdef bint result @@ -204,9 +204,9 @@ cdef bint get_rigid_Hz( cdef void set_rigid_Hx( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidH ) nogil: rigidH[0, i, j, k] = True @@ -215,9 +215,9 @@ cdef void set_rigid_Hx( cdef void set_rigid_Hy( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidH ) nogil: rigidH[2, i, j, k] = True @@ -226,9 +226,9 @@ cdef void set_rigid_Hy( cdef void set_rigid_Hz( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidH ) nogil: rigidH[4, i, j, k] = True @@ -237,18 +237,18 @@ cdef void set_rigid_Hz( cdef void set_rigid_H( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidH ) nogil: rigidH[:, i, j, k] = True cdef void unset_rigid_H( - int i, - int j, - int k, + int i, + int j, + int k, np.int8_t[:, :, :, ::1] rigidH ) nogil: rigidH[:, i, j, k] = False diff --git a/gprMax/fields_outputs.py b/gprMax/fields_outputs.py index d8050c9a..9a0ba03d 100644 --- a/gprMax/fields_outputs.py +++ b/gprMax/fields_outputs.py @@ -38,14 +38,13 @@ def store_outputs(G): for rx in G.rxs: for output in rx.outputs: # Store electric or magnetic field components - if 'I' not in output: + if "I" not in output: field = locals()[output] rx.outputs[output][iteration] = field[rx.xcoord, rx.ycoord, rx.zcoord] # Store current component else: func = globals()[output] - rx.outputs[output][iteration] = func(rx.xcoord, rx.ycoord, rx.zcoord, - Hx, Hy, Hz, G) + rx.outputs[output][iteration] = func(rx.xcoord, rx.ycoord, rx.zcoord, Hx, Hy, Hz, G) for tl in G.transmissionlines: tl.Vtotal[iteration] = tl.voltage[tl.antpos] @@ -65,9 +64,9 @@ def write_hdf5_outputfile(outputfile, G): # Create output file and write top-level meta data if G.rxs or sg_rxs: - f = h5py.File(outputfile, 'w') - f.attrs['gprMax'] = __version__ - f.attrs['Title'] = G.title + f = h5py.File(outputfile, "w") + f.attrs["gprMax"] = __version__ + f.attrs["Title"] = G.title # Write meta data and data for main grid if G.rxs: @@ -76,11 +75,11 @@ def write_hdf5_outputfile(outputfile, G): # Write meta data and data for any subgrids if sg_rxs: for sg in G.subgrids: - grp = f.create_group(f'/subgrids/{sg.name}') + grp = f.create_group(f"/subgrids/{sg.name}") write_hd5_data(grp, sg, is_subgrid=True) if G.rxs or sg_rxs: - logger.basic(f'Written output file: {outputfile.name}') + logger.basic(f"Written output file: {outputfile.name}") def write_hd5_data(basegrp, G, is_subgrid=False): @@ -93,55 +92,55 @@ def write_hd5_data(basegrp, G, is_subgrid=False): """ # Write meta data for grid - basegrp.attrs['Iterations'] = G.iterations - basegrp.attrs['nx_ny_nz'] = (G.nx, G.ny, G.nz) - basegrp.attrs['dx_dy_dz'] = (G.dx, G.dy, G.dz) - basegrp.attrs['dt'] = G.dt + basegrp.attrs["Iterations"] = G.iterations + basegrp.attrs["nx_ny_nz"] = (G.nx, G.ny, G.nz) + basegrp.attrs["dx_dy_dz"] = (G.dx, G.dy, G.dz) + basegrp.attrs["dt"] = G.dt nsrc = len(G.voltagesources + G.hertziandipoles + G.magneticdipoles + G.transmissionlines) - basegrp.attrs['nsrc'] = nsrc - basegrp.attrs['nrx'] = len(G.rxs) - basegrp.attrs['srcsteps'] = G.srcsteps - basegrp.attrs['rxsteps'] = G.rxsteps + basegrp.attrs["nsrc"] = nsrc + basegrp.attrs["nrx"] = len(G.rxs) + basegrp.attrs["srcsteps"] = G.srcsteps + basegrp.attrs["rxsteps"] = G.rxsteps if is_subgrid: # Write additional meta data about subgrid - basegrp.attrs['is_os_sep'] = G.is_os_sep - basegrp.attrs['pml_separation'] = G.pml_separation - basegrp.attrs['subgrid_pml_thickness'] = G.pml['thickness']['x0'] - basegrp.attrs['filter'] = G.filter - basegrp.attrs['ratio'] = G.ratio - basegrp.attrs['interpolation'] = G.interpolation + basegrp.attrs["is_os_sep"] = G.is_os_sep + basegrp.attrs["pml_separation"] = G.pml_separation + basegrp.attrs["subgrid_pml_thickness"] = G.pml["thickness"]["x0"] + basegrp.attrs["filter"] = G.filter + basegrp.attrs["ratio"] = G.ratio + basegrp.attrs["interpolation"] = G.interpolation # Create group for sources (except transmission lines); add type and positional data attributes srclist = G.voltagesources + G.hertziandipoles + G.magneticdipoles for srcindex, src in enumerate(srclist): - grp = basegrp.create_group(f'srcs/src{str(srcindex + 1)}') - grp.attrs['Type'] = type(src).__name__ - grp.attrs['Position'] = (src.xcoord * G.dx, src.ycoord * G.dy, src.zcoord * G.dz) + grp = basegrp.create_group(f"srcs/src{str(srcindex + 1)}") + grp.attrs["Type"] = type(src).__name__ + grp.attrs["Position"] = (src.xcoord * G.dx, src.ycoord * G.dy, src.zcoord * G.dz) # Create group for transmission lines; add positional data, line resistance and # line discretisation attributes; write arrays for line voltages and currents for tlindex, tl in enumerate(G.transmissionlines): - grp = basegrp.create_group('tls/tl' + str(tlindex + 1)) - grp.attrs['Position'] = (tl.xcoord * G.dx, tl.ycoord * G.dy, tl.zcoord * G.dz) - grp.attrs['Resistance'] = tl.resistance - grp.attrs['dl'] = tl.dl + grp = basegrp.create_group("tls/tl" + str(tlindex + 1)) + grp.attrs["Position"] = (tl.xcoord * G.dx, tl.ycoord * G.dy, tl.zcoord * G.dz) + grp.attrs["Resistance"] = tl.resistance + grp.attrs["dl"] = tl.dl # Save incident voltage and current - grp['Vinc'] = tl.Vinc - grp['Iinc'] = tl.Iinc + grp["Vinc"] = tl.Vinc + grp["Iinc"] = tl.Iinc # Save total voltage and current - basegrp['tls/tl' + str(tlindex + 1) + '/Vtotal'] = tl.Vtotal - basegrp['tls/tl' + str(tlindex + 1) + '/Itotal'] = tl.Itotal + basegrp["tls/tl" + str(tlindex + 1) + "/Vtotal"] = tl.Vtotal + basegrp["tls/tl" + str(tlindex + 1) + "/Itotal"] = tl.Itotal # Create group, add positional data and write field component arrays for receivers for rxindex, rx in enumerate(G.rxs): - grp = basegrp.create_group('rxs/rx' + str(rxindex + 1)) + grp = basegrp.create_group("rxs/rx" + str(rxindex + 1)) if rx.ID: - grp.attrs['Name'] = rx.ID - grp.attrs['Position'] = (rx.xcoord * G.dx, rx.ycoord * G.dy, rx.zcoord * G.dz) + grp.attrs["Name"] = rx.ID + grp.attrs["Position"] = (rx.xcoord * G.dx, rx.ycoord * G.dy, rx.zcoord * G.dz) for output in rx.outputs: - basegrp['rxs/rx' + str(rxindex + 1) + '/' + output] = rx.outputs[output] + basegrp["rxs/rx" + str(rxindex + 1) + "/" + output] = rx.outputs[output] def Ix(x, y, z, Hx, Hy, Hz, G): diff --git a/gprMax/fractals.py b/gprMax/fractals.py index 42573fcc..607e247a 100644 --- a/gprMax/fractals.py +++ b/gprMax/fractals.py @@ -24,19 +24,19 @@ import gprMax.config as config from .cython.fractals_generate import generate_fractal2D, generate_fractal3D from .utilities.utilities import round_value -np.seterr(divide='raise') +np.seterr(divide="raise") class FractalSurface: """Fractal surfaces.""" - surfaceIDs = ['xminus', 'xplus', 'yminus', 'yplus', 'zminus', 'zplus'] + surfaceIDs = ["xminus", "xplus", "yminus", "yplus", "zminus", "zplus"] def __init__(self, xs, xf, ys, yf, zs, zf, dimension): """ Args: xs, xf, ys, yf, zs, zf: floats for the extent of the fractal surface - (one pair of coordinates must be equal + (one pair of coordinates must be equal to correctly define a surface). dimension: float for the fractal dimension that controls the fractal distribution. @@ -83,8 +83,7 @@ class FractalSurface: self.fractalsurface = np.zeros(surfacedims, dtype=self.dtype) # Positional vector at centre of array, scaled by weighting - v1 = np.array([self.weighting[0] * (surfacedims[0]) / 2, self.weighting[1] - * (surfacedims[1]) / 2]) + v1 = np.array([self.weighting[0] * (surfacedims[0]) / 2, self.weighting[1] * (surfacedims[1]) / 2]) # 2D array of random numbers to be convolved with the fractal function rng = np.random.default_rng(seed=self.seed) @@ -96,8 +95,16 @@ class FractalSurface: A = fftpack.fftshift(A) # Generate fractal - generate_fractal2D(surfacedims[0], surfacedims[1], config.get_model_config().ompthreads, - self.b, self.weighting, v1, A, self.fractalsurface) + generate_fractal2D( + surfacedims[0], + surfacedims[1], + config.get_model_config().ompthreads, + self.b, + self.weighting, + v1, + A, + self.fractalsurface, + ) # Shift the zero frequency component to start of the array self.fractalsurface = fftpack.ifftshift(self.fractalsurface) # Set DC component of FFT to zero @@ -105,16 +112,18 @@ class FractalSurface: # Take the real part (numerical errors can give rise to an imaginary part) # of the IFFT, and convert type to floattype. N.B calculation of fractals # must always be carried out at double precision, i.e. float64, complex128 - self.fractalsurface = np.real(fftpack.ifftn(self.fractalsurface)).astype(config.sim_config.dtypes['float_or_double'], - copy=False) + self.fractalsurface = np.real(fftpack.ifftn(self.fractalsurface)).astype( + config.sim_config.dtypes["float_or_double"], copy=False + ) # Scale the fractal volume according to requested range fractalmin = np.amin(self.fractalsurface) fractalmax = np.amax(self.fractalsurface) fractalrange = fractalmax - fractalmin - self.fractalsurface = (self.fractalsurface * ((self.fractalrange[1] - - self.fractalrange[0]) / fractalrange) - + self.fractalrange[0] - ((self.fractalrange[1] - - self.fractalrange[0]) / fractalrange) * fractalmin) + self.fractalsurface = ( + self.fractalsurface * ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) + + self.fractalrange[0] + - ((self.fractalrange[1] - self.fractalrange[0]) / fractalrange) * fractalmin + ) class FractalVolume: @@ -177,8 +186,9 @@ class FractalVolume: self.fractalvolume = np.zeros((self.nx, self.ny, self.nz), dtype=self.dtype) # Positional vector at centre of array, scaled by weighting - v1 = np.array([self.weighting[0] * self.nx / 2, self.weighting[1] - * self.ny / 2, self.weighting[2] * self.nz / 2]) + v1 = np.array( + [self.weighting[0] * self.nx / 2, self.weighting[1] * self.ny / 2, self.weighting[2] * self.nz / 2] + ) # 3D array of random numbers to be convolved with the fractal function rng = np.random.default_rng(seed=self.seed) @@ -190,18 +200,28 @@ class FractalVolume: A = fftpack.fftshift(A) # Generate fractal - generate_fractal3D(self.nx, self.ny, self.nz, config.get_model_config().ompthreads, - self.b, self.weighting, v1, A, self.fractalvolume) + generate_fractal3D( + self.nx, + self.ny, + self.nz, + config.get_model_config().ompthreads, + self.b, + self.weighting, + v1, + A, + self.fractalvolume, + ) # Shift the zero frequency component to the start of the array self.fractalvolume = fftpack.ifftshift(self.fractalvolume) # Set DC component of FFT to zero self.fractalvolume[0, 0, 0] = 0 - # Take the real part (numerical errors can give rise to an imaginary part) + # Take the real part (numerical errors can give rise to an imaginary part) # of the IFFT, and convert type to floattype. N.B calculation of fractals # must always be carried out at double precision, i.e. float64, complex128 - self.fractalvolume = np.real(fftpack.ifftn(self.fractalvolume)).astype(config.sim_config.dtypes['float_or_double'], - copy=False) + self.fractalvolume = np.real(fftpack.ifftn(self.fractalvolume)).astype( + config.sim_config.dtypes["float_or_double"], copy=False + ) # Bin fractal values bins = np.linspace(np.amin(self.fractalvolume), np.amax(self.fractalvolume), self.nbins) @@ -211,8 +231,8 @@ class FractalVolume: def generate_volume_mask(self): """Generate a 3D volume to use as a mask for adding rough surfaces, - water and grass/roots. Zero signifies the mask is not set, one - signifies the mask is set. + water and grass/roots. Zero signifies the mask is not set, one + signifies the mask is set. """ self.mask = np.zeros((self.nx, self.ny, self.nz), dtype=np.int8) @@ -235,8 +255,7 @@ class Grass: """ self.numblades = numblades - self.geometryparams = np.zeros((self.numblades, 6), - dtype=config.sim_config.dtypes['float_or_double']) + self.geometryparams = np.zeros((self.numblades, 6), dtype=config.sim_config.dtypes["float_or_double"]) self.seed = None # Randomly defined parameters that will be used to calculate geometry @@ -264,10 +283,16 @@ class Grass: x, y: floats for the x and y coordinates of grass blade. """ - x = (self.geometryparams[blade, 2] * (height / self.geometryparams[blade, 0]) - * (height / self.geometryparams[blade, 0])) - y = (self.geometryparams[blade, 3] * (height / self.geometryparams[blade, 1]) - * (height / self.geometryparams[blade, 1])) + x = ( + self.geometryparams[blade, 2] + * (height / self.geometryparams[blade, 0]) + * (height / self.geometryparams[blade, 0]) + ) + y = ( + self.geometryparams[blade, 3] + * (height / self.geometryparams[blade, 1]) + * (height / self.geometryparams[blade, 1]) + ) x = round_value(x) y = round_value(y) diff --git a/gprMax/geometry_outputs.py b/gprMax/geometry_outputs.py index 8c2447a4..5af12603 100644 --- a/gprMax/geometry_outputs.py +++ b/gprMax/geometry_outputs.py @@ -32,37 +32,39 @@ import gprMax.config as config from ._version import __version__ from .cython.geometry_outputs import write_lines from .subgrids.grid import SubGridBaseGrid -from .utilities.utilities import (get_terminal_width, - numeric_list_to_float_list, - numeric_list_to_int_list) +from .utilities.utilities import get_terminal_width, numeric_list_to_float_list, numeric_list_to_int_list logger = logging.getLogger(__name__) def save_geometry_views(gvs): """Creates and saves geometryviews. - + Args: gvs: list of all GeometryViews. """ - - logger.info('') + + logger.info("") for i, gv in enumerate(gvs): gv.set_filename() vtk_data = gv.prep_vtk() - pbar = tqdm(total=gv.nbytes, unit='byte', unit_scale=True, - desc=f'Writing geometry view file {i + 1}/{len(gvs)}, ' - f'{gv.filename.name}{gv.vtkfiletype.ext}', - ncols=get_terminal_width() - 1, file=sys.stdout, - disable=not config.sim_config.general['progressbars']) + pbar = tqdm( + total=gv.nbytes, + unit="byte", + unit_scale=True, + desc=f"Writing geometry view file {i + 1}/{len(gvs)}, " f"{gv.filename.name}{gv.vtkfiletype.ext}", + ncols=get_terminal_width() - 1, + file=sys.stdout, + disable=not config.sim_config.general["progressbars"], + ) gv.write_vtk(vtk_data) pbar.update(gv.nbytes) pbar.close() - - logger.info('') - -class GeometryView(): + logger.info("") + + +class GeometryView: """Base class for Geometry Views.""" def __init__(self, xs, ys, zs, xf, yf, zf, dx, dy, dz, filename, grid): @@ -93,10 +95,8 @@ class GeometryView(): def set_filename(self): """Constructs filename from user-supplied name and model run number.""" parts = config.get_model_config().output_file_path.parts - self.filename = Path(*parts[:-1], - self.filename + - config.get_model_config().appendmodelnumber) - + self.filename = Path(*parts[:-1], self.filename + config.get_model_config().appendmodelnumber) + class GeometryViewLines(GeometryView): """Unstructured grid (.vtu) for a per-cell-edge geometry view.""" @@ -104,40 +104,47 @@ class GeometryViewLines(GeometryView): def __init__(self, *args): super().__init__(*args) self.vtkfiletype = VtkUnstructuredGrid - + def prep_vtk(self): """Prepares data for writing to VTK file. - + Returns: vtk_data: dict of coordinates, data, and comments for VTK file. """ - # Sample ID array according to geometry view spatial discretisation + # Sample ID array according to geometry view spatial discretisation # Only create a new array if subsampling is required - if (self.grid.ID.shape != (self.xf, self.yf, self.zf) or - (self.dx, self.dy, self.dz) != (1, 1, 1) or - (self.xs, self.ys, self.zs) != (0, 0, 0)): + if ( + self.grid.ID.shape != (self.xf, self.yf, self.zf) + or (self.dx, self.dy, self.dz) != (1, 1, 1) + or (self.xs, self.ys, self.zs) != (0, 0, 0) + ): # Require contiguous for evtk library - ID = np.ascontiguousarray(self.grid.ID[:, self.xs:self.xf:self.dx, - self.ys:self.yf:self.dy, - self.zs:self.zf:self.dz]) + ID = np.ascontiguousarray( + self.grid.ID[:, self.xs : self.xf : self.dx, self.ys : self.yf : self.dy, self.zs : self.zf : self.dz] + ) else: # This array is contiguous by design ID = self.grid.ID - x, y, z, lines = write_lines((self.xs * self.grid.dx), - (self.ys * self.grid.dy), - (self.zs * self.grid.dz), - self.nx, self.ny, self.nz, - (self.dx * self.grid.dx), - (self.dy * self.grid.dy), - (self.dz * self.grid.dz), ID) + x, y, z, lines = write_lines( + (self.xs * self.grid.dx), + (self.ys * self.grid.dy), + (self.zs * self.grid.dz), + self.nx, + self.ny, + self.nz, + (self.dx * self.grid.dx), + (self.dy * self.grid.dy), + (self.dz * self.grid.dz), + ID, + ) # Add offset to subgrid geometry to correctly locate within main grid if isinstance(self.grid, SubGridBaseGrid): - x += (self.grid.i0 * self.grid.dx * self.grid.ratio) - y += (self.grid.j0 * self.grid.dy * self.grid.ratio) - z += (self.grid.k0 * self.grid.dz * self.grid.ratio) + x += self.grid.i0 * self.grid.dx * self.grid.ratio + y += self.grid.j0 * self.grid.dy * self.grid.ratio + z += self.grid.k0 * self.grid.dz * self.grid.ratio # Write information about any PMLs, sources, receivers comments = Comments(self.grid, self) @@ -147,28 +154,31 @@ class GeometryViewLines(GeometryView): comments = json.dumps(info) # Number of bytes of data to be written to file - offsets_size = np.arange(start = 2, step = 2, stop = len(x) + 1, - dtype = 'int32').nbytes - connect_size = len(x) * np.dtype('int32').itemsize - cell_type_size = len(x) * np.dtype('uint8').itemsize - self.nbytes = (x.nbytes + y.nbytes + z.nbytes + lines.nbytes + offsets_size - + connect_size + cell_type_size) - - vtk_data = {'x': x, 'y': y, 'z': z, 'data': lines, 'comments': comments} + offsets_size = np.arange(start=2, step=2, stop=len(x) + 1, dtype="int32").nbytes + connect_size = len(x) * np.dtype("int32").itemsize + cell_type_size = len(x) * np.dtype("uint8").itemsize + self.nbytes = x.nbytes + y.nbytes + z.nbytes + lines.nbytes + offsets_size + connect_size + cell_type_size + + vtk_data = {"x": x, "y": y, "z": z, "data": lines, "comments": comments} return vtk_data def write_vtk(self, vtk_data): """Writes geometry information to a VTK file. - + Args: vtk_data: dict of coordinates, data, and comments for VTK file. """ - # Write the VTK file .vtu - linesToVTK(str(self.filename), vtk_data['x'], vtk_data['y'], - vtk_data['z'], cellData={"Material": vtk_data['data']}, - comments=[vtk_data['comments']]) + # Write the VTK file .vtu + linesToVTK( + str(self.filename), + vtk_data["x"], + vtk_data["y"], + vtk_data["z"], + cellData={"Material": vtk_data["data"]}, + comments=[vtk_data["comments"]], + ) class GeometryViewVoxels(GeometryView): @@ -177,23 +187,25 @@ class GeometryViewVoxels(GeometryView): def __init__(self, *args): super().__init__(*args) self.vtkfiletype = VtkImageData - + def prep_vtk(self): """Prepares data for writing to VTK file. - + Returns: vtk_data: dict of data and comments for VTK file. """ # Sample solid array according to geometry view spatial discretisation # Only create a new array if subsampling is required - if (self.grid.solid.shape != (self.xf, self.yf, self.zf) or - (self.dx, self.dy, self.dz) != (1, 1, 1) or - (self.xs, self.ys, self.zs) != (0, 0, 0)): + if ( + self.grid.solid.shape != (self.xf, self.yf, self.zf) + or (self.dx, self.dy, self.dz) != (1, 1, 1) + or (self.xs, self.ys, self.zs) != (0, 0, 0) + ): # Require contiguous for evtk library - solid = np.ascontiguousarray(self.grid.solid[self.xs:self.xf:self.dx, - self.ys:self.yf:self.dy, - self.zs:self.zf:self.dz]) + solid = np.ascontiguousarray( + self.grid.solid[self.xs : self.xf : self.dx, self.ys : self.yf : self.dy, self.zs : self.zf : self.dz] + ) else: # This array is contiguous by design solid = self.grid.solid @@ -205,39 +217,39 @@ class GeometryViewVoxels(GeometryView): self.nbytes = solid.nbytes - vtk_data = {'data': solid, 'comments': comments} + vtk_data = {"data": solid, "comments": comments} return vtk_data def write_vtk(self, vtk_data): """Writes geometry information to a VTK file. - + Args: vtk_data: dict of data and comments for VTK file. """ if isinstance(self.grid, SubGridBaseGrid): - origin = ((self.grid.i0 * self.grid.dx * self.grid.ratio), - (self.grid.j0 * self.grid.dy * self.grid.ratio), - (self.grid.k0 * self.grid.dz * self.grid.ratio)) + origin = ( + (self.grid.i0 * self.grid.dx * self.grid.ratio), + (self.grid.j0 * self.grid.dy * self.grid.ratio), + (self.grid.k0 * self.grid.dz * self.grid.ratio), + ) else: - origin = ((self.xs * self.grid.dx), - (self.ys * self.grid.dy), - (self.zs * self.grid.dz)) + origin = ((self.xs * self.grid.dx), (self.ys * self.grid.dy), (self.zs * self.grid.dz)) # Write the VTK file .vti - imageToVTK(str(self.filename), - origin=origin, - spacing=((self.dx * self.grid.dx), - (self.dy * self.grid.dy), - (self.dz * self.grid.dz)), - cellData={"Material": vtk_data['data']}, - comments=[vtk_data['comments']]) + imageToVTK( + str(self.filename), + origin=origin, + spacing=((self.dx * self.grid.dx), (self.dy * self.grid.dy), (self.dz * self.grid.dz)), + cellData={"Material": vtk_data["data"]}, + comments=[vtk_data["comments"]], + ) -class Comments(): +class Comments: """Comments can be strings included in the header of XML VTK file, and are - used to hold extra (gprMax) information about the VTK data. + used to hold extra (gprMax) information about the VTK data. """ def __init__(self, grid, gv): @@ -248,49 +260,54 @@ class Comments(): def get_gprmax_info(self): """Returns gprMax specific information relating material, source, - and receiver names to numeric identifiers. + and receiver names to numeric identifiers. """ # Comments for Paraview macro - comments = {'gprMax_version': __version__, - 'dx_dy_dz': self.dx_dy_dz_comment(), - 'nx_ny_nz': self.nx_ny_nz_comment(), - 'Materials': self.materials_comment()} # Write the name and numeric ID for each material + comments = { + "gprMax_version": __version__, + "dx_dy_dz": self.dx_dy_dz_comment(), + "nx_ny_nz": self.nx_ny_nz_comment(), + "Materials": self.materials_comment(), + } # Write the name and numeric ID for each material # Write information on PMLs, sources, and receivers if not self.materials_only: # Information on PML thickness - if self.grid.pmls['slabs']: - comments['PMLthickness'] = self.pml_gv_comment() - srcs = (self.grid.hertziandipoles + self.grid.magneticdipoles + - self.grid.voltagesources + self.grid.transmissionlines) + if self.grid.pmls["slabs"]: + comments["PMLthickness"] = self.pml_gv_comment() + srcs = ( + self.grid.hertziandipoles + + self.grid.magneticdipoles + + self.grid.voltagesources + + self.grid.transmissionlines + ) if srcs: - comments['Sources'] = self.srcs_rx_gv_comment(srcs) + comments["Sources"] = self.srcs_rx_gv_comment(srcs) if self.grid.rxs: - comments['Receivers'] = self.srcs_rx_gv_comment(self.grid.rxs) + comments["Receivers"] = self.srcs_rx_gv_comment(self.grid.rxs) return comments def pml_gv_comment(self): - grid = self.grid # Only render PMLs if they are in the geometry view - pmlstorender = dict.fromkeys(grid.pmls['thickness'], 0) + pmlstorender = dict.fromkeys(grid.pmls["thickness"], 0) # Casting to int required as json does not handle numpy types - if grid.pmls['thickness']['x0'] - self.gv.xs > 0: - pmlstorender['x0'] = int(grid.pmls['thickness']['x0'] - self.gv.xs) - if grid.pmls['thickness']['y0'] - self.gv.ys > 0: - pmlstorender['y0'] = int(grid.pmls['thickness']['y0'] - self.gv.ys) - if grid.pmls['thickness']['z0'] - self.gv.zs > 0: - pmlstorender['z0'] = int(grid.pmls['thickness']['z0'] - self.gv.zs) - if self.gv.xf > grid.nx - grid.pmls['thickness']['xmax']: - pmlstorender['xmax'] = int(self.gv.xf - (grid.nx - grid.pmls['thickness']['xmax'])) - if self.gv.yf > grid.ny - grid.pmls['thickness']['ymax']: - pmlstorender['ymax'] = int(self.gv.yf - (grid.ny - grid.pmls['thickness']['ymax'])) - if self.gv.zf > grid.nz - grid.pmls['thickness']['zmax']: - pmlstorender['zmax'] = int(self.gv.zf - (grid.nz - grid.pmls['thickness']['zmax'])) + if grid.pmls["thickness"]["x0"] - self.gv.xs > 0: + pmlstorender["x0"] = int(grid.pmls["thickness"]["x0"] - self.gv.xs) + if grid.pmls["thickness"]["y0"] - self.gv.ys > 0: + pmlstorender["y0"] = int(grid.pmls["thickness"]["y0"] - self.gv.ys) + if grid.pmls["thickness"]["z0"] - self.gv.zs > 0: + pmlstorender["z0"] = int(grid.pmls["thickness"]["z0"] - self.gv.zs) + if self.gv.xf > grid.nx - grid.pmls["thickness"]["xmax"]: + pmlstorender["xmax"] = int(self.gv.xf - (grid.nx - grid.pmls["thickness"]["xmax"])) + if self.gv.yf > grid.ny - grid.pmls["thickness"]["ymax"]: + pmlstorender["ymax"] = int(self.gv.yf - (grid.ny - grid.pmls["thickness"]["ymax"])) + if self.gv.zf > grid.nz - grid.pmls["thickness"]["zmax"]: + pmlstorender["zmax"] = int(self.gv.zf - (grid.nz - grid.pmls["thickness"]["zmax"])) return list(pmlstorender.values()) @@ -298,13 +315,10 @@ class Comments(): """Used to name sources and/or receivers.""" sc = [] for src in srcs: - p = (src.xcoord * self.grid.dx, - src.ycoord * self.grid.dy, - src.zcoord * self.grid.dz) + p = (src.xcoord * self.grid.dx, src.ycoord * self.grid.dy, src.zcoord * self.grid.dz) p = numeric_list_to_float_list(p) - s = {'name': src.ID, - 'position': p} + s = {"name": src.ID, "position": p} sc.append(s) return sc @@ -317,7 +331,7 @@ class Comments(): def materials_comment(self): if not self.averaged_materials: - return [m.ID for m in self.grid.materials if m.type != 'dielectric-smoothed'] + return [m.ID for m in self.grid.materials if m.type != "dielectric-smoothed"] else: return [m.ID for m in self.grid.materials] @@ -344,25 +358,16 @@ class GeometryObjects: self.basefilename = basefilename # Set filenames - parts = config.sim_config.input_file_path.with_suffix('').parts + parts = config.sim_config.input_file_path.with_suffix("").parts self.filename_hdf5 = Path(*parts[:-1], self.basefilename) - self.filename_hdf5 = self.filename_hdf5.with_suffix('.h5') - self.filename_materials = Path(*parts[:-1], f'{self.basefilename}_materials') - self.filename_materials = self.filename_materials.with_suffix('.txt') + self.filename_hdf5 = self.filename_hdf5.with_suffix(".h5") + self.filename_materials = Path(*parts[:-1], f"{self.basefilename}_materials") + self.filename_materials = self.filename_materials.with_suffix(".txt") # Sizes of arrays to write necessary to update progress bar - self.solidsize = ((self.nx + 1) * - (self.ny + 1) * - (self.nz + 1) * - np.dtype(np.uint32).itemsize) - self.rigidsize = (18 * (self.nx + 1) * - (self.ny + 1) * - (self.nz + 1) * - np.dtype(np.int8).itemsize) - self.IDsize = (6 * (self.nx + 1) * - (self.ny + 1) * - (self.nz + 1) * - np.dtype(np.uint32).itemsize) + self.solidsize = (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.uint32).itemsize + self.rigidsize = 18 * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.int8).itemsize + self.IDsize = 6 * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * np.dtype(np.uint32).itemsize self.datawritesize = self.solidsize + self.rigidsize + self.IDsize def write_hdf5(self, G, pbar): @@ -373,55 +378,50 @@ class GeometryObjects: pbar: Progress bar class instance. """ - with h5py.File(self.filename_hdf5, 'w') as fdata: - fdata.attrs['gprMax'] = __version__ - fdata.attrs['Title'] = G.title - fdata.attrs['dx_dy_dz'] = (G.dx, G.dy, G.dz) + with h5py.File(self.filename_hdf5, "w") as fdata: + fdata.attrs["gprMax"] = __version__ + fdata.attrs["Title"] = G.title + fdata.attrs["dx_dy_dz"] = (G.dx, G.dy, G.dz) # Get minimum and maximum integers of materials in geometry objects volume - minmat = np.amin(G.ID[:, self.xs:self.xf + 1, - self.ys:self.yf + 1, self.zs:self.zf + 1]) - maxmat = np.amax(G.ID[:, self.xs:self.xf + 1, - self.ys:self.yf + 1, self.zs:self.zf + 1]) - fdata['/data'] = G.solid[self.xs:self.xf + 1, self.ys:self.yf + - 1, self.zs:self.zf + 1].astype('int16') - minmat + minmat = np.amin(G.ID[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1]) + maxmat = np.amax(G.ID[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1]) + fdata["/data"] = ( + G.solid[self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1].astype("int16") - minmat + ) pbar.update(self.solidsize) - fdata['/rigidE'] = G.rigidE[:, self.xs:self.xf + - 1, self.ys:self.yf + 1, self.zs:self.zf + 1] - fdata['/rigidH'] = G.rigidH[:, self.xs:self.xf + - 1, self.ys:self.yf + 1, self.zs:self.zf + 1] + fdata["/rigidE"] = G.rigidE[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1] + fdata["/rigidH"] = G.rigidH[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1] pbar.update(self.rigidsize) - fdata['/ID'] = G.ID[:, self.xs:self.xf + 1, - self.ys:self.yf + 1, self.zs:self.zf + 1] - minmat + fdata["/ID"] = G.ID[:, self.xs : self.xf + 1, self.ys : self.yf + 1, self.zs : self.zf + 1] - minmat pbar.update(self.IDsize) # Write materials list to a text file # This includes all materials in range whether used in volume or not - with open(self.filename_materials, 'w') as fmaterials: + with open(self.filename_materials, "w") as fmaterials: for numID in range(minmat, maxmat + 1): for material in G.materials: if material.numID == numID: - fmaterials.write(f'#material: {material.er:g} {material.se:g} ' - f'{material.mr:g} {material.sm:g} {material.ID}\n') - if hasattr(material, 'poles'): - if 'debye' in material.type: - dispersionstr = ('#add_dispersion_debye: ' - f'{material.poles:g} ') + fmaterials.write( + f"#material: {material.er:g} {material.se:g} " + f"{material.mr:g} {material.sm:g} {material.ID}\n" + ) + if hasattr(material, "poles"): + if "debye" in material.type: + dispersionstr = "#add_dispersion_debye: " f"{material.poles:g} " for pole in range(material.poles): - dispersionstr += (f'{material.deltaer[pole]:g} ' - f'{material.tau[pole]:g} ') - elif 'lorenz' in material.type: - dispersionstr = (f'#add_dispersion_lorenz: ' - f'{material.poles:g} ') + dispersionstr += f"{material.deltaer[pole]:g} " f"{material.tau[pole]:g} " + elif "lorenz" in material.type: + dispersionstr = f"#add_dispersion_lorenz: " f"{material.poles:g} " for pole in range(material.poles): - dispersionstr += (f'{material.deltaer[pole]:g} ' - f'{material.tau[pole]:g} ' - f'{material.alpha[pole]:g} ') - elif 'drude' in material.type: - dispersionstr = (f'#add_dispersion_drude: ' - f'{material.poles:g} ') + dispersionstr += ( + f"{material.deltaer[pole]:g} " + f"{material.tau[pole]:g} " + f"{material.alpha[pole]:g} " + ) + elif "drude" in material.type: + dispersionstr = f"#add_dispersion_drude: " f"{material.poles:g} " for pole in range(material.poles): - dispersionstr += (f'{material.tau[pole]:g} ' - f'{material.alpha[pole]:g} ') + dispersionstr += f"{material.tau[pole]:g} " f"{material.alpha[pole]:g} " dispersionstr += material.ID - fmaterials.write(dispersionstr + '\n') + fmaterials.write(dispersionstr + "\n") diff --git a/gprMax/gprMax.py b/gprMax/gprMax.py index 91776ca8..9b96b14c 100644 --- a/gprMax/gprMax.py +++ b/gprMax/gprMax.py @@ -24,135 +24,140 @@ from .contexts import Context, MPIContext from .utilities.logging import logging_config # Arguments (used for API) and their default values (used for API and CLI) -args_defaults = {'scenes': None, - 'inputfile': None, - 'outputfile': None, - 'n': 1, - 'i': None, - 'mpi': False, - 'gpu': None, - 'opencl': None, - 'subgrid': False, - 'autotranslate': False, - 'geometry_only': False, - 'geometry_fixed': False, - 'write_processed': False, - 'log_level': 20, # Level DEBUG = 10; INFO = 20; BASIC = 25 - 'log_file': False} +args_defaults = { + "scenes": None, + "inputfile": None, + "outputfile": None, + "n": 1, + "i": None, + "mpi": False, + "gpu": None, + "opencl": None, + "subgrid": False, + "autotranslate": False, + "geometry_only": False, + "geometry_fixed": False, + "write_processed": False, + "log_level": 20, # Level DEBUG = 10; INFO = 20; BASIC = 25 + "log_file": False, +} # Argument help messages (used for CLI argparse) -help_msg = {'scenes': '(list, req): Scenes to run the model. ' - 'Multiple scene objects can given in order to run multiple ' - 'simulation runs. Each scene must contain the essential ' - 'simulation objects', - 'inputfile': '(str, opt): Input file path. Can also run simulation ' - 'by providing an input file.', - 'outputfile': '(str, req): File path to the output data file.', - 'n': '(int, req): Number of required simulation runs.', - 'i': '(int, opt): Model number to start/restart simulation ' - 'from. It would typically be used to restart a series of ' - 'models from a specific model number, with the n argument, ' - 'e.g. to restart from A-scan 45 when creating a B-scan ' - 'with 60 traces.', - 'mpi': '(bool, opt): Flag to use Message Passing Interface (MPI) ' - 'task farm. This option is most usefully combined with n to ' - 'allow individual models to be farmed out using a MPI task ' - 'farm, e.g. to create a B-scan with 60 traces and use MPI to ' - 'farm out each trace. For further details see the parallel ' - 'performance section of the User Guide.', - 'gpu': '(list/bool, opt): Flag to use NVIDIA GPU or list of NVIDIA ' - 'GPU device ID(s) for specific GPU card(s).', - 'opencl': '(list/bool, opt): Flag to use OpenCL or list of OpenCL ' - 'device ID(s) for specific compute device(s).', - 'subgrid': '(bool, opt): Flag to use sub-gridding.', - 'autotranslate': '(bool, opt): For sub-gridding - auto translate ' - 'objects with main grid coordinates to their ' - 'equivalent local grid coordinate within the ' - 'subgrid. If this option is off users must specify ' - 'sub-grid object point within the global subgrid space.', - 'geometry_only': '(bool, opt): Build a model and produce any ' - 'geometry views but do not run the simulation.', - 'geometry_fixed': '(bool, opt): Run a series of models where the ' - 'geometry does not change between models.', - 'write_processed': '(bool, opt): Writes another input file after ' - 'any Python code (#python blocks) and in the ' - 'original input file has been processed.', - 'log_level': '(int, opt): Level of logging to use.', - 'log_file': '(bool, opt): Write logging information to file.'} +help_msg = { + "scenes": "(list, req): Scenes to run the model. " + "Multiple scene objects can given in order to run multiple " + "simulation runs. Each scene must contain the essential " + "simulation objects", + "inputfile": "(str, opt): Input file path. Can also run simulation " "by providing an input file.", + "outputfile": "(str, req): File path to the output data file.", + "n": "(int, req): Number of required simulation runs.", + "i": "(int, opt): Model number to start/restart simulation " + "from. It would typically be used to restart a series of " + "models from a specific model number, with the n argument, " + "e.g. to restart from A-scan 45 when creating a B-scan " + "with 60 traces.", + "mpi": "(bool, opt): Flag to use Message Passing Interface (MPI) " + "task farm. This option is most usefully combined with n to " + "allow individual models to be farmed out using a MPI task " + "farm, e.g. to create a B-scan with 60 traces and use MPI to " + "farm out each trace. For further details see the parallel " + "performance section of the User Guide.", + "gpu": "(list/bool, opt): Flag to use NVIDIA GPU or list of NVIDIA " "GPU device ID(s) for specific GPU card(s).", + "opencl": "(list/bool, opt): Flag to use OpenCL or list of OpenCL " "device ID(s) for specific compute device(s).", + "subgrid": "(bool, opt): Flag to use sub-gridding.", + "autotranslate": "(bool, opt): For sub-gridding - auto translate " + "objects with main grid coordinates to their " + "equivalent local grid coordinate within the " + "subgrid. If this option is off users must specify " + "sub-grid object point within the global subgrid space.", + "geometry_only": "(bool, opt): Build a model and produce any " "geometry views but do not run the simulation.", + "geometry_fixed": "(bool, opt): Run a series of models where the " "geometry does not change between models.", + "write_processed": "(bool, opt): Writes another input file after " + "any Python code (#python blocks) and in the " + "original input file has been processed.", + "log_level": "(int, opt): Level of logging to use.", + "log_file": "(bool, opt): Write logging information to file.", +} -def run(scenes=args_defaults['scenes'], - inputfile=args_defaults['inputfile'], - outputfile=args_defaults['outputfile'], - n=args_defaults['n'], - i=args_defaults['i'], - mpi=args_defaults['mpi'], - gpu=args_defaults['gpu'], - opencl=args_defaults['opencl'], - subgrid=args_defaults['subgrid'], - autotranslate=args_defaults['autotranslate'], - geometry_only=args_defaults['geometry_only'], - geometry_fixed=args_defaults['geometry_fixed'], - write_processed=args_defaults['write_processed'], - log_level=args_defaults['log_level'], - log_file=args_defaults['log_file']): - """Entry point for application programming interface (API). Runs the +def run( + scenes=args_defaults["scenes"], + inputfile=args_defaults["inputfile"], + outputfile=args_defaults["outputfile"], + n=args_defaults["n"], + i=args_defaults["i"], + mpi=args_defaults["mpi"], + gpu=args_defaults["gpu"], + opencl=args_defaults["opencl"], + subgrid=args_defaults["subgrid"], + autotranslate=args_defaults["autotranslate"], + geometry_only=args_defaults["geometry_only"], + geometry_fixed=args_defaults["geometry_fixed"], + write_processed=args_defaults["write_processed"], + log_level=args_defaults["log_level"], + log_file=args_defaults["log_file"], +): + """Entry point for application programming interface (API). Runs the simulation for the given list of scenes. Args: - scenes: list of the scenes to run the model. Multiple scene objects can - be given in order to run multiple simulation runs. Each scene + scenes: list of the scenes to run the model. Multiple scene objects can + be given in order to run multiple simulation runs. Each scene must contain the essential simulation objects. - inputfile: optional string for input file path. Can also run simulation + inputfile: optional string for input file path. Can also run simulation by providing an input file. outputfile: string for file path to the output data file n: optional int for number of required simulation runs. - i: optional int for model number to start/restart simulation from. - It would typically be used to restart a series of models from a - specific model number, with the n argument, e.g. to restart from + i: optional int for model number to start/restart simulation from. + It would typically be used to restart a series of models from a + specific model number, with the n argument, e.g. to restart from A-scan 45 when creating a B-scan with 60 traces. - mpi: optional boolean flag to use Message Passing Interface (MPI) task - farm. This option is most usefully combined with n to allow - individual models to be farmed out using a MPI task farm, - e.g. to create a B-scan with 60 traces and use MPI to farm out - each trace. For further details see the performance section of + mpi: optional boolean flag to use Message Passing Interface (MPI) task + farm. This option is most usefully combined with n to allow + individual models to be farmed out using a MPI task farm, + e.g. to create a B-scan with 60 traces and use MPI to farm out + each trace. For further details see the performance section of the User Guide - gpu: optional list/boolean to use NVIDIA GPU or list of NVIDIA GPU device + gpu: optional list/boolean to use NVIDIA GPU or list of NVIDIA GPU device ID(s) for specific GPU card(s). - opencl: optional list/boolean to use OpenCL or list of OpenCL device ID(s) + opencl: optional list/boolean to use OpenCL or list of OpenCL device ID(s) for specific compute device(s). subgrid: optional boolean to use sub-gridding. - autotranslate: optional boolean for sub-gridding to auto translate - objects with main grid coordinates to their equivalent - local grid coordinate within the subgrid. If this option - is off users must specify sub-grid object point within + autotranslate: optional boolean for sub-gridding to auto translate + objects with main grid coordinates to their equivalent + local grid coordinate within the subgrid. If this option + is off users must specify sub-grid object point within the global subgrid space. - geometry_only: optional boolean to build a model and produce any + geometry_only: optional boolean to build a model and produce any geometry views but do not run the simulation. - geometry_fixed: optional boolean to run a series of models where the + geometry_fixed: optional boolean to run a series of models where the geometry does not change between models. - write_processed: optional boolean to write another input file after any - #python blocks (which are deprecated) in the + write_processed: optional boolean to write another input file after any + #python blocks (which are deprecated) in the original input file has been processed. log_level: optional int for level of logging to use. log_file: optional boolean to write logging information to file. """ - args = argparse.Namespace(**{'scenes': scenes, - 'inputfile': inputfile, - 'outputfile': outputfile, - 'n': n, - 'i': i, - 'mpi': mpi, - 'gpu': gpu, - 'opencl': opencl, - 'subgrid': subgrid, - 'autotranslate': autotranslate, - 'geometry_only': geometry_only, - 'geometry_fixed': geometry_fixed, - 'write_processed': write_processed, - 'log_level': log_level, - 'log_file': log_file}) + args = argparse.Namespace( + **{ + "scenes": scenes, + "inputfile": inputfile, + "outputfile": outputfile, + "n": n, + "i": i, + "mpi": mpi, + "gpu": gpu, + "opencl": opencl, + "subgrid": subgrid, + "autotranslate": autotranslate, + "geometry_only": geometry_only, + "geometry_fixed": geometry_fixed, + "write_processed": write_processed, + "log_level": log_level, + "log_file": log_file, + } + ) run_main(args) @@ -161,32 +166,30 @@ def cli(): """Entry point for command line interface (CLI).""" # Parse command line arguments - parser = argparse.ArgumentParser(prog='gprMax', - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('inputfile', help=help_msg['inputfile']) - parser.add_argument('-n', default=args_defaults['n'], type=int, help=help_msg['n']) - parser.add_argument('-i', type=int, help=help_msg['i']) - parser.add_argument('-mpi', action='store_true', default=args_defaults['mpi'], - help=help_msg['mpi']) - parser.add_argument('-gpu', type=int, action='append', nargs='*', - help=help_msg['gpu']) - parser.add_argument('-opencl', type=int, action='append', nargs='*', - help=help_msg['opencl']) - parser.add_argument('--geometry-only', action='store_true', - default=args_defaults['geometry_only'], - help=help_msg['geometry_only']) - parser.add_argument('--geometry-fixed', action='store_true', - default=args_defaults['geometry_fixed'], - help=help_msg['geometry_fixed']) - parser.add_argument('--write-processed', action='store_true', - default=args_defaults['write_processed'], - help=help_msg['write_processed']) - parser.add_argument('--log-level', type=int, - default=args_defaults['log_level'], - help=help_msg['log_level']) - parser.add_argument('--log-file', action='store_true', - default=args_defaults['log_file'], - help=help_msg['log_file']) + parser = argparse.ArgumentParser(prog="gprMax", formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("inputfile", help=help_msg["inputfile"]) + parser.add_argument("-n", default=args_defaults["n"], type=int, help=help_msg["n"]) + parser.add_argument("-i", type=int, help=help_msg["i"]) + parser.add_argument("-mpi", action="store_true", default=args_defaults["mpi"], help=help_msg["mpi"]) + parser.add_argument("-gpu", type=int, action="append", nargs="*", help=help_msg["gpu"]) + parser.add_argument("-opencl", type=int, action="append", nargs="*", help=help_msg["opencl"]) + parser.add_argument( + "--geometry-only", action="store_true", default=args_defaults["geometry_only"], help=help_msg["geometry_only"] + ) + parser.add_argument( + "--geometry-fixed", + action="store_true", + default=args_defaults["geometry_fixed"], + help=help_msg["geometry_fixed"], + ) + parser.add_argument( + "--write-processed", + action="store_true", + default=args_defaults["write_processed"], + help=help_msg["write_processed"], + ) + parser.add_argument("--log-level", type=int, default=args_defaults["log_level"], help=help_msg["log_level"]) + parser.add_argument("--log-file", action="store_true", default=args_defaults["log_file"], help=help_msg["log_file"]) args = parser.parse_args() results = run_main(args) @@ -201,7 +204,7 @@ def run_main(args): args: namespace with arguments from either API or CLI. Returns: - results: dict that can contain useful results/data from simulation. + results: dict that can contain useful results/data from simulation. Enables these to be propagated to calling script. """ @@ -210,7 +213,7 @@ def run_main(args): logging_config(level=args.log_level, log_file=args.log_file) config.sim_config = config.SimulationConfig(args) - + # MPI running with (OpenMP/CUDA/OpenCL) if config.sim_config.args.mpi: context = MPIContext() @@ -220,4 +223,4 @@ def run_main(args): context = Context() results = context.run() - return results \ No newline at end of file + return results diff --git a/gprMax/grid.py b/gprMax/grid.py index a4960727..4da21882 100644 --- a/gprMax/grid.py +++ b/gprMax/grid.py @@ -26,17 +26,17 @@ import gprMax.config as config from .pml import PML from .utilities.utilities import fft_power, round_value -np.seterr(invalid='raise') +np.seterr(invalid="raise") class FDTDGrid: """Holds attributes associated with entire grid. A convenient way for - accessing regularly used parameters. + accessing regularly used parameters. """ def __init__(self): - self.title = '' - self.name = 'main_grid' + self.title = "" + self.name = "main_grid" self.mem_use = 0 self.nx = 0 @@ -46,22 +46,22 @@ class FDTDGrid: self.dy = 0 self.dz = 0 self.dt = 0 - self.dt_mod = None # Time step stability factor - self.iteration = 0 # Current iteration number - self.iterations = 0 # Total number of iterations + self.dt_mod = None # Time step stability factor + self.iteration = 0 # Current iteration number + self.iterations = 0 # Total number of iterations self.timewindow = 0 # PML parameters - set some defaults to use if not user provided self.pmls = {} - self.pmls['formulation'] = 'HORIPML' - self.pmls['cfs'] = [] - self.pmls['slabs'] = [] + self.pmls["formulation"] = "HORIPML" + self.pmls["cfs"] = [] + self.pmls["slabs"] = [] # Ordered dictionary required so that PMLs are always updated in the # same order. The order itself does not matter, however, if must be the # same from model to model otherwise the numerical precision from adding # the PML corrections will be different. - self.pmls['thickness'] = OrderedDict((key, 10) for key in PML.boundaryIDs) - + self.pmls["thickness"] = OrderedDict((key, 10) for key in PML.boundaryIDs) + self.materials = [] self.mixingmodels = [] self.averagevolumeobjects = True @@ -81,11 +81,11 @@ class FDTDGrid: def within_bounds(self, p): if p[0] < 0 or p[0] > self.nx: - raise ValueError('x') + raise ValueError("x") if p[1] < 0 or p[1] > self.ny: - raise ValueError('y') + raise ValueError("y") if p[2] < 0 or p[2] > self.nz: - raise ValueError('z') + raise ValueError("z") def discretise_point(self, p): x = round_value(float(p[0]) / self.dx) @@ -95,25 +95,25 @@ class FDTDGrid: def round_to_grid(self, p): p = self.discretise_point(p) - p_r = (p[0] * self.dx, - p[1] * self.dy, - p[2] * self.dz) + p_r = (p[0] * self.dx, p[1] * self.dy, p[2] * self.dz) return p_r def within_pml(self, p): - if (p[0] < self.pmls['thickness']['x0'] or - p[0] > self.nx - self.pmls['thickness']['xmax'] or - p[1] < self.pmls['thickness']['y0'] or - p[1] > self.ny - self.pmls['thickness']['ymax'] or - p[2] < self.pmls['thickness']['z0'] or - p[2] > self.nz - self.pmls['thickness']['zmax']): + if ( + p[0] < self.pmls["thickness"]["x0"] + or p[0] > self.nx - self.pmls["thickness"]["xmax"] + or p[1] < self.pmls["thickness"]["y0"] + or p[1] > self.ny - self.pmls["thickness"]["ymax"] + or p[2] < self.pmls["thickness"]["z0"] + or p[2] > self.nz - self.pmls["thickness"]["zmax"] + ): return True else: return False def initialise_geometry_arrays(self): """Initialise an array for volumetric material IDs (solid); - boolean arrays for specifying whether materials can have dielectric + boolean arrays for specifying whether materials can have dielectric smoothing (rigid); and an array for cell edge IDs (ID). Solid and ID arrays are initialised to free_space (one); rigid arrays to allow dielectric smoothing (zero). @@ -122,59 +122,55 @@ class FDTDGrid: self.rigidE = np.zeros((12, self.nx, self.ny, self.nz), dtype=np.int8) self.rigidH = np.zeros((6, self.nx, self.ny, self.nz), dtype=np.int8) self.ID = np.ones((6, self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.uint32) - self.IDlookup = {'Ex': 0, 'Ey': 1, 'Ez': 2, 'Hx': 3, 'Hy': 4, 'Hz': 5} + self.IDlookup = {"Ex": 0, "Ey": 1, "Ez": 2, "Hx": 3, "Hy": 4, "Hz": 5} def initialise_field_arrays(self): """Initialise arrays for the electric and magnetic field components.""" - self.Ex = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - self.Ey = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - self.Ez = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - self.Hx = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - self.Hy = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - self.Hz = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) + self.Ex = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]) + self.Ey = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]) + self.Ez = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]) + self.Hx = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]) + self.Hy = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]) + self.Hz = np.zeros((self.nx + 1, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"]) def initialise_std_update_coeff_arrays(self): """Initialise arrays for storing update coefficients.""" - self.updatecoeffsE = np.zeros((len(self.materials), 5), - dtype=config.sim_config.dtypes['float_or_double']) - self.updatecoeffsH = np.zeros((len(self.materials), 5), - dtype=config.sim_config.dtypes['float_or_double']) + self.updatecoeffsE = np.zeros((len(self.materials), 5), dtype=config.sim_config.dtypes["float_or_double"]) + self.updatecoeffsH = np.zeros((len(self.materials), 5), dtype=config.sim_config.dtypes["float_or_double"]) def initialise_dispersive_arrays(self): """Initialise field arrays when there are dispersive materials present.""" - self.Tx = np.zeros((config.get_model_config().materials['maxpoles'], - self.nx + 1, self.ny + 1, self.nz + 1), - dtype=config.get_model_config().materials['dispersivedtype']) - self.Ty = np.zeros((config.get_model_config().materials['maxpoles'], - self.nx + 1, self.ny + 1, self.nz + 1), - dtype=config.get_model_config().materials['dispersivedtype']) - self.Tz = np.zeros((config.get_model_config().materials['maxpoles'], - self.nx + 1, self.ny + 1, self.nz + 1), - dtype=config.get_model_config().materials['dispersivedtype']) + self.Tx = np.zeros( + (config.get_model_config().materials["maxpoles"], self.nx + 1, self.ny + 1, self.nz + 1), + dtype=config.get_model_config().materials["dispersivedtype"], + ) + self.Ty = np.zeros( + (config.get_model_config().materials["maxpoles"], self.nx + 1, self.ny + 1, self.nz + 1), + dtype=config.get_model_config().materials["dispersivedtype"], + ) + self.Tz = np.zeros( + (config.get_model_config().materials["maxpoles"], self.nx + 1, self.ny + 1, self.nz + 1), + dtype=config.get_model_config().materials["dispersivedtype"], + ) def initialise_dispersive_update_coeff_array(self): """Initialise array for storing update coefficients when there are dispersive - materials present. + materials present. """ - self.updatecoeffsdispersive = np.zeros((len(self.materials), 3 * - config.get_model_config().materials['maxpoles']), - dtype=config.get_model_config().materials['dispersivedtype']) + self.updatecoeffsdispersive = np.zeros( + (len(self.materials), 3 * config.get_model_config().materials["maxpoles"]), + dtype=config.get_model_config().materials["dispersivedtype"], + ) def reset_fields(self): """Clear arrays for field components and PMLs.""" # Clear arrays for field components self.initialise_field_arrays() - if config.get_model_config().materials['maxpoles'] > 0: + if config.get_model_config().materials["maxpoles"] > 0: self.initialise_dispersive_arrays() # Clear arrays for fields in PML - for pml in self.pmls['slabs']: + for pml in self.pmls["slabs"]: pml.initialise_field_arrays() def mem_est_basic(self): @@ -190,28 +186,33 @@ class FDTDGrid: rigidarrays = (12 + 6) * self.nx * self.ny * self.nz * np.dtype(np.int8).itemsize # 6 x field arrays + 6 x ID arrays - fieldarrays = ((6 + 6) * (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * - np.dtype(config.sim_config.dtypes['float_or_double']).itemsize) + fieldarrays = ( + (6 + 6) + * (self.nx + 1) + * (self.ny + 1) + * (self.nz + 1) + * np.dtype(config.sim_config.dtypes["float_or_double"]).itemsize + ) # PML arrays pmlarrays = 0 - for (k, v) in self.pmls['thickness'].items(): + for k, v in self.pmls["thickness"].items(): if v > 0: - if 'x' in k: - pmlarrays += ((v + 1) * self.ny * (self.nz + 1)) - pmlarrays += ((v + 1) * (self.ny + 1) * self.nz) - pmlarrays += (v * self.ny * (self.nz + 1)) - pmlarrays += (v * (self.ny + 1) * self.nz) - elif 'y' in k: - pmlarrays += (self.nx * (v + 1) * (self.nz + 1)) - pmlarrays += ((self.nx + 1) * (v + 1) * self.nz) - pmlarrays += ((self.nx + 1) * v * self.nz) - pmlarrays += (self.nx * v * (self.nz + 1)) - elif 'z' in k: - pmlarrays += (self.nx * (self.ny + 1) * (v + 1)) - pmlarrays += ((self.nx + 1) * self.ny * (v + 1)) - pmlarrays += ((self.nx + 1) * self.ny * v) - pmlarrays += (self.nx * (self.ny + 1) * v) + if "x" in k: + pmlarrays += (v + 1) * self.ny * (self.nz + 1) + pmlarrays += (v + 1) * (self.ny + 1) * self.nz + pmlarrays += v * self.ny * (self.nz + 1) + pmlarrays += v * (self.ny + 1) * self.nz + elif "y" in k: + pmlarrays += self.nx * (v + 1) * (self.nz + 1) + pmlarrays += (self.nx + 1) * (v + 1) * self.nz + pmlarrays += (self.nx + 1) * v * self.nz + pmlarrays += self.nx * v * (self.nz + 1) + elif "z" in k: + pmlarrays += self.nx * (self.ny + 1) * (v + 1) + pmlarrays += (self.nx + 1) * self.ny * (v + 1) + pmlarrays += (self.nx + 1) * self.ny * v + pmlarrays += self.nx * (self.ny + 1) * v mem_use = int(fieldarrays + solidarray + rigidarrays + pmlarrays) @@ -224,9 +225,14 @@ class FDTDGrid: mem_use: int of memory (bytes). """ - mem_use = int(3 * config.get_model_config().materials['maxpoles'] * - (self.nx + 1) * (self.ny + 1) * (self.nz + 1) * - np.dtype(config.get_model_config().materials['dispersivedtype']).itemsize) + mem_use = int( + 3 + * config.get_model_config().materials["maxpoles"] + * (self.nx + 1) + * (self.ny + 1) + * (self.nz + 1) + * np.dtype(config.get_model_config().materials["dispersivedtype"]).itemsize + ) return mem_use def mem_est_fractals(self): @@ -236,22 +242,20 @@ class FDTDGrid: Returns: mem_use: int of memory (bytes). """ - + mem_use = 0 for vol in self.fractalvolumes: - mem_use += (vol.nx * vol.ny * vol.nz * vol.dtype.itemsize) + mem_use += vol.nx * vol.ny * vol.nz * vol.dtype.itemsize for surface in vol.fractalsurfaces: surfacedims = surface.get_surface_dims() - mem_use += (surfacedims[0] * - surfacedims[1] * - surface.dtype.itemsize) + mem_use += surfacedims[0] * surfacedims[1] * surface.dtype.itemsize return mem_use def tmx(self): """Add PEC boundaries to invariant direction in 2D TMx mode. - N.B. 2D modes are a single cell slice of 3D grid. + N.B. 2D modes are a single cell slice of 3D grid. """ # Ey & Ez components self.ID[1, 0, :, :] = 0 @@ -261,7 +265,7 @@ class FDTDGrid: def tmy(self): """Add PEC boundaries to invariant direction in 2D TMy mode. - N.B. 2D modes are a single cell slice of 3D grid. + N.B. 2D modes are a single cell slice of 3D grid. """ # Ex & Ez components self.ID[0, :, 0, :] = 0 @@ -271,7 +275,7 @@ class FDTDGrid: def tmz(self): """Add PEC boundaries to invariant direction in 2D TMz mode. - N.B. 2D modes are a single cell slice of 3D grid. + N.B. 2D modes are a single cell slice of 3D grid. """ # Ex & Ey components self.ID[0, :, :, 0] = 0 @@ -281,18 +285,16 @@ class FDTDGrid: def calculate_dt(self): """Calculate time step at the CFL limit.""" - if config.get_model_config().mode == '2D TMx': - self.dt = 1 / (config.sim_config.em_consts['c'] * - np.sqrt((1 / self.dy**2) + (1 / self.dz**2))) - elif config.get_model_config().mode == '2D TMy': - self.dt = 1 / (config.sim_config.em_consts['c'] * - np.sqrt((1 / self.dx**2) + (1 / self.dz**2))) - elif config.get_model_config().mode == '2D TMz': - self.dt = 1 / (config.sim_config.em_consts['c'] * - np.sqrt((1 / self.dx**2) + (1 / self.dy**2))) + if config.get_model_config().mode == "2D TMx": + self.dt = 1 / (config.sim_config.em_consts["c"] * np.sqrt((1 / self.dy**2) + (1 / self.dz**2))) + elif config.get_model_config().mode == "2D TMy": + self.dt = 1 / (config.sim_config.em_consts["c"] * np.sqrt((1 / self.dx**2) + (1 / self.dz**2))) + elif config.get_model_config().mode == "2D TMz": + self.dt = 1 / (config.sim_config.em_consts["c"] * np.sqrt((1 / self.dx**2) + (1 / self.dy**2))) else: - self.dt = 1 / (config.sim_config.em_consts['c'] * - np.sqrt((1 / self.dx**2) + (1 / self.dy**2) + (1 / self.dz**2))) + self.dt = 1 / ( + config.sim_config.em_consts["c"] * np.sqrt((1 / self.dx**2) + (1 / self.dy**2) + (1 / self.dz**2)) + ) # Round down time step to nearest float with precision one less than # hardware maximum. Avoids inadvertently exceeding the CFL due to @@ -313,44 +315,47 @@ class CUDAGrid(FDTDGrid): def set_blocks_per_grid(self): """Set the blocks per grid size used for updating the electric and - magnetic field arrays on a GPU. + magnetic field arrays on a GPU. """ - self.bpg = (int(np.ceil(((self.nx + 1) * (self.ny + 1) * - (self.nz + 1)) / self.tpb[0])), 1, 1) + self.bpg = (int(np.ceil(((self.nx + 1) * (self.ny + 1) * (self.nz + 1)) / self.tpb[0])), 1, 1) def htod_geometry_arrays(self, queue=None): """Initialise an array for cell edge IDs (ID) on compute device. - + Args: queue: pyopencl queue. """ - if config.sim_config.general['solver'] == 'cuda': + if config.sim_config.general["solver"] == "cuda": import pycuda.gpuarray as gpuarray + self.ID_dev = gpuarray.to_gpu(self.ID) - elif config.sim_config.general['solver'] == 'opencl': + elif config.sim_config.general["solver"] == "opencl": import pyopencl.array as clarray - self.ID_dev = clarray.to_device(queue, self.ID) + + self.ID_dev = clarray.to_device(queue, self.ID) def htod_field_arrays(self, queue=None): """Initialise field arrays on compute device. - + Args: queue: pyopencl queue. """ - if config.sim_config.general['solver'] == 'cuda': + if config.sim_config.general["solver"] == "cuda": import pycuda.gpuarray as gpuarray + self.Ex_dev = gpuarray.to_gpu(self.Ex) self.Ey_dev = gpuarray.to_gpu(self.Ey) self.Ez_dev = gpuarray.to_gpu(self.Ez) self.Hx_dev = gpuarray.to_gpu(self.Hx) self.Hy_dev = gpuarray.to_gpu(self.Hy) self.Hz_dev = gpuarray.to_gpu(self.Hz) - elif config.sim_config.general['solver'] == 'opencl': + elif config.sim_config.general["solver"] == "opencl": import pyopencl.array as clarray + self.Ex_dev = clarray.to_device(queue, self.Ex) self.Ey_dev = clarray.to_device(queue, self.Ey) self.Ez_dev = clarray.to_device(queue, self.Ez) @@ -360,19 +365,21 @@ class CUDAGrid(FDTDGrid): def htod_dispersive_arrays(self, queue=None): """Initialise dispersive material coefficient arrays on compute device. - + Args: queue: pyopencl queue. """ - if config.sim_config.general['solver'] == 'cuda': + if config.sim_config.general["solver"] == "cuda": import pycuda.gpuarray as gpuarray + self.Tx_dev = gpuarray.to_gpu(self.Tx) self.Ty_dev = gpuarray.to_gpu(self.Ty) self.Tz_dev = gpuarray.to_gpu(self.Tz) self.updatecoeffsdispersive_dev = gpuarray.to_gpu(self.updatecoeffsdispersive) - elif config.sim_config.general['solver'] == 'opencl': + elif config.sim_config.general["solver"] == "opencl": import pyopencl.array as clarray + self.Tx_dev = clarray.to_device(queue, self.Tx) self.Ty_dev = clarray.to_device(queue, self.Ty) self.Tz_dev = clarray.to_device(queue, self.Tz) @@ -405,27 +412,23 @@ def dispersion_analysis(G): # material: material with maximum permittivity # maxfreq: maximum significant frequency # error: error message - results = {'deltavp': None, - 'N': None, - 'material': None, - 'maxfreq': [], - 'error': ''} + results = {"deltavp": None, "N": None, "material": None, "maxfreq": [], "error": ""} # Find maximum significant frequency if G.waveforms: for waveform in G.waveforms: - if waveform.type == 'sine' or waveform.type == 'contsine': - results['maxfreq'].append(4 * waveform.freq) + if waveform.type == "sine" or waveform.type == "contsine": + results["maxfreq"].append(4 * waveform.freq) - elif waveform.type == 'impulse': - results['error'] = 'impulse waveform used.' + elif waveform.type == "impulse": + results["error"] = "impulse waveform used." - elif waveform.type == 'user': - results['error'] = 'user waveform detected.' + elif waveform.type == "user": + results["error"] = "user waveform detected." else: # User-defined waveform - if waveform.type == 'user': + if waveform.type == "user": iterations = G.iterations # Built-in waveform @@ -450,12 +453,19 @@ def dispersion_analysis(G): # Set maximum frequency to a threshold drop from maximum power, ignoring DC value try: - freqthres = np.where(power[freqmaxpower:] < -config.get_model_config().numdispersion['highestfreqthres'])[0][0] + freqmaxpower - results['maxfreq'].append(freqs[freqthres]) + freqthres = ( + np.where( + power[freqmaxpower:] < -config.get_model_config().numdispersion["highestfreqthres"] + )[0][0] + + freqmaxpower + ) + results["maxfreq"].append(freqs[freqthres]) except ValueError: - results['error'] = ('unable to calculate maximum power ' + - 'from waveform, most likely due to ' + - 'undersampling.') + results["error"] = ( + "unable to calculate maximum power " + + "from waveform, most likely due to " + + "undersampling." + ) # Ignore case where someone is using a waveform with zero amplitude, i.e. on a receiver elif waveform.amp == 0: @@ -463,40 +473,41 @@ def dispersion_analysis(G): # If waveform is truncated don't do any further analysis else: - results['error'] = ('waveform does not fit within specified ' + - 'time window and is therefore being truncated.') + results["error"] = ( + "waveform does not fit within specified " + "time window and is therefore being truncated." + ) else: - results['error'] = 'no waveform detected.' + results["error"] = "no waveform detected." - if results['maxfreq']: - results['maxfreq'] = max(results['maxfreq']) + if results["maxfreq"]: + results["maxfreq"] = max(results["maxfreq"]) # Find minimum wavelength (material with maximum permittivity) maxer = 0 - matmaxer = '' + matmaxer = "" for x in G.materials: - if x.se != float('inf'): + if x.se != float("inf"): er = x.er - # If there are dispersive materials calculate the complex + # If there are dispersive materials calculate the complex # relative permittivity at maximum frequency and take the real part - if x.__class__.__name__ == 'DispersiveMaterial': - er = x.calculate_er(results['maxfreq']) + if x.__class__.__name__ == "DispersiveMaterial": + er = x.calculate_er(results["maxfreq"]) er = er.real if er > maxer: maxer = er matmaxer = x.ID - results['material'] = next(x for x in G.materials if x.ID == matmaxer) + results["material"] = next(x for x in G.materials if x.ID == matmaxer) # Minimum velocity minvelocity = config.c / np.sqrt(maxer) # Minimum wavelength - minwavelength = minvelocity / results['maxfreq'] + minwavelength = minvelocity / results["maxfreq"] # Maximum spatial step - if '3D' in config.get_model_config().mode: + if "3D" in config.get_model_config().mode: delta = max(G.dx, G.dy, G.dz) - elif '2D' in config.get_model_config().mode: + elif "2D" in config.get_model_config().mode: if G.nx == 1: delta = max(G.dy, G.dz) elif G.ny == 1: @@ -508,17 +519,17 @@ def dispersion_analysis(G): S = (config.c * G.dt) / delta # Grid sampling density - results['N'] = minwavelength / delta + results["N"] = minwavelength / delta # Check grid sampling will result in physical wave propagation - if int(np.floor(results['N'])) >= config.get_model_config().numdispersion['mingridsampling']: + if int(np.floor(results["N"])) >= config.get_model_config().numdispersion["mingridsampling"]: # Numerical phase velocity - vp = np.pi / (results['N'] * np.arcsin((1 / S) * np.sin((np.pi * S) / results['N']))) + vp = np.pi / (results["N"] * np.arcsin((1 / S) * np.sin((np.pi * S) / results["N"]))) # Physical phase velocity error (percentage) - results['deltavp'] = (((vp * config.c) - config.c) / config.c) * 100 + results["deltavp"] = (((vp * config.c) - config.c) / config.c) * 100 # Store rounded down value of grid sampling density - results['N'] = int(np.floor(results['N'])) + results["N"] = int(np.floor(results["N"])) - return results \ No newline at end of file + return results diff --git a/gprMax/hash_cmds_file.py b/gprMax/hash_cmds_file.py index 845b21d5..66e26dad 100644 --- a/gprMax/hash_cmds_file.py +++ b/gprMax/hash_cmds_file.py @@ -40,7 +40,7 @@ def process_python_include_code(inputfile, usernamespace): Args: inputfile: file object for input file. - usernamespace: namespace that can be accessed by user in any Python code + usernamespace: namespace that can be accessed by user in any Python code blocks in input file. Returns: @@ -48,7 +48,7 @@ def process_python_include_code(inputfile, usernamespace): """ # Strip out any newline characters and comments that must begin with double hashes - inputlines = [line.rstrip() for line in inputfile if(not line.startswith('##') and line.rstrip('\n'))] + inputlines = [line.rstrip() for line in inputfile if (not line.startswith("##") and line.rstrip("\n"))] # Rewind input file in preparation for any subsequent reading function inputfile.seek(0) @@ -57,32 +57,34 @@ def process_python_include_code(inputfile, usernamespace): processedlines = [] x = 0 - while(x < len(inputlines)): - + while x < len(inputlines): # Process any Python code - if(inputlines[x].startswith('#python:')): - logger.warning('#python blocks are deprecated and will be removed in ' + - 'the next release of gprMax. Please convert your ' + - 'model to use our Python API instead.\n') + if inputlines[x].startswith("#python:"): + logger.warning( + "#python blocks are deprecated and will be removed in " + + "the next release of gprMax. Please convert your " + + "model to use our Python API instead.\n" + ) # String to hold Python code to be executed - pythoncode = '' + pythoncode = "" x += 1 - while not inputlines[x].startswith('#end_python:'): + while not inputlines[x].startswith("#end_python:"): # Add all code in current code block to string - pythoncode += inputlines[x] + '\n' + pythoncode += inputlines[x] + "\n" x += 1 if x == len(inputlines): - logger.exception('Cannot find the end of the Python code ' + - 'block, i.e. missing #end_python: command.') + logger.exception( + "Cannot find the end of the Python code " + "block, i.e. missing #end_python: command." + ) raise SyntaxError # Compile code for faster execution - pythoncompiledcode = compile(pythoncode, '', 'exec') + pythoncompiledcode = compile(pythoncode, "", "exec") # Redirect stdout to a text stream sys.stdout = result = StringIO() # Execute code block & make available only usernamespace exec(pythoncompiledcode, usernamespace) # String containing buffer of executed code - codeout = result.getvalue().split('\n') + codeout = result.getvalue().split("\n") result.close() # Reset stdio @@ -92,8 +94,8 @@ def process_python_include_code(inputfile, usernamespace): hashcmds = [] pythonout = [] for line in codeout: - if line.startswith('#'): - hashcmds.append(line + '\n') + if line.startswith("#"): + hashcmds.append(line + "\n") elif line: pythonout.append(line) @@ -102,12 +104,12 @@ def process_python_include_code(inputfile, usernamespace): # Print any generated output that is not commands if pythonout: - logger.info(f'Python messages (from stdout/stderr): {pythonout}\n') + logger.info(f"Python messages (from stdout/stderr): {pythonout}\n") # Add any other commands to list - elif(inputlines[x].startswith('#')): + elif inputlines[x].startswith("#"): # Add gprMax command to list - inputlines[x] += ('\n') + inputlines[x] += "\n" processedlines.append(inputlines[x]) x += 1 @@ -126,18 +128,18 @@ def process_include_files(hashcmds): hashcmds: list of input commands. Returns: - processedincludecmds: list of input commands after processing any + processedincludecmds: list of input commands after processing any include file commands. """ processedincludecmds = [] x = 0 while x < len(hashcmds): - if hashcmds[x].startswith('#include_file:'): + if hashcmds[x].startswith("#include_file:"): includefile = hashcmds[x].split() if len(includefile) != 2: - logger.exception('#include_file requires exactly one parameter') + logger.exception("#include_file requires exactly one parameter") raise ValueError includefile = includefile[1] @@ -147,9 +149,13 @@ def process_include_files(hashcmds): if not includefile.exists(): includefile = Path(config.sim_config.input_file_path.parent, includefile) - with open(includefile, 'r') as f: + with open(includefile, "r") as f: # Strip out any newline characters and comments that must begin with double hashes - includelines = [includeline.rstrip() + '\n' for includeline in f if(not includeline.startswith('##') and includeline.rstrip('\n'))] + includelines = [ + includeline.rstrip() + "\n" + for includeline in f + if (not includeline.startswith("##") and includeline.rstrip("\n")) + ] # Add lines from include file processedincludecmds.extend(includelines) @@ -172,62 +178,99 @@ def write_processed_file(processedlines): """ parts = config.get_model_config().output_file_path.parts - processedfile = (Path(*parts[:-1], parts[-1] + '_processed.in')) + processedfile = Path(*parts[:-1], parts[-1] + "_processed.in") - with open(processedfile, 'w') as f: + with open(processedfile, "w") as f: for item in processedlines: - f.write(f'{item}') + f.write(f"{item}") - logger.info(f'Written input commands, after processing any Python ' + - f'code and include commands, to file: {processedfile}\n') + logger.info( + f"Written input commands, after processing any Python " + + f"code and include commands, to file: {processedfile}\n" + ) def check_cmd_names(processedlines, checkessential=True): """Checks the validity of commands, i.e. are they gprMax commands, and that all essential commands are present. - + Args: processedlines: list of input commands after Python processing. checkessential: boolean to check for essential commands or not. Returns: singlecmds: dict of commands that can only occur once in the model. - multiplecmds: dict of commands that can have multiple instances in the + multiplecmds: dict of commands that can have multiple instances in the model. geometry: list of geometry commands in the model. """ # Dictionaries of available commands # Essential commands neccessary to run a gprMax model - essentialcmds = ['#domain', '#dx_dy_dz', '#time_window'] + essentialcmds = ["#domain", "#dx_dy_dz", "#time_window"] # Commands that there should only be one instance of in a model - singlecmds = dict.fromkeys(['#domain', '#dx_dy_dz', '#time_window', - '#title', '#omp_threads', - '#time_step_stability_factor', '#pml_cells', - '#excitation_file', '#src_steps', '#rx_steps', - '#output_dir'], None) + singlecmds = dict.fromkeys( + [ + "#domain", + "#dx_dy_dz", + "#time_window", + "#title", + "#omp_threads", + "#time_step_stability_factor", + "#pml_cells", + "#excitation_file", + "#src_steps", + "#rx_steps", + "#output_dir", + ], + None, + ) # Commands that there can be multiple instances of in a model # - these will be lists within the dictionary - multiplecmds = {key: [] for key in ['#geometry_view', - '#geometry_objects_write', '#material', - '#material_range', '#material_list', - '#soil_peplinski', - '#add_dispersion_debye', - '#add_dispersion_lorentz', - '#add_dispersion_drude', - '#waveform', '#voltage_source', - '#hertzian_dipole', '#magnetic_dipole', - '#transmission_line', '#rx', '#rx_array', - '#snapshot', '#include_file']} + multiplecmds = { + key: [] + for key in [ + "#geometry_view", + "#geometry_objects_write", + "#material", + "#material_range", + "#material_list", + "#soil_peplinski", + "#add_dispersion_debye", + "#add_dispersion_lorentz", + "#add_dispersion_drude", + "#waveform", + "#voltage_source", + "#hertzian_dipole", + "#magnetic_dipole", + "#transmission_line", + "#rx", + "#rx_array", + "#snapshot", + "#include_file", + ] + } # Geometry object building commands that there can be multiple instances # of in a model - these will be lists within the dictionary - geometrycmds = ['#geometry_objects_read', '#edge', '#plate', '#triangle', - '#box', '#sphere', '#ellipsoid', '#cone', '#cylinder', - '#cylindrical_sector', '#fractal_box', - '#add_surface_roughness', '#add_surface_water', '#add_grass'] + geometrycmds = [ + "#geometry_objects_read", + "#edge", + "#plate", + "#triangle", + "#box", + "#sphere", + "#ellipsoid", + "#cone", + "#cylinder", + "#cylindrical_sector", + "#fractal_box", + "#add_surface_roughness", + "#add_surface_water", + "#add_grass", + ] # List to store all geometry object commands in order from input file geometry = [] @@ -235,26 +278,28 @@ def check_cmd_names(processedlines, checkessential=True): # add command parameters to appropriate dictionary values or lists countessentialcmds = 0 lindex = 0 - while(lindex < len(processedlines)): - cmd = processedlines[lindex].split(':') + while lindex < len(processedlines): + cmd = processedlines[lindex].split(":") cmdname = cmd[0] cmdparams = cmd[1] # Check if there is space between command name and parameters, i.e. # check first character of parameter string. Ignore case when there # are no parameters for a command, e.g. for #taguchi: - if ' ' not in cmdparams[0] and len(cmdparams.strip('\n')) != 0: - logger.exception('There must be a space between the command name ' + - 'and parameters in ' + processedlines[lindex]) + if " " not in cmdparams[0] and len(cmdparams.strip("\n")) != 0: + logger.exception( + "There must be a space between the command name " + "and parameters in " + processedlines[lindex] + ) raise SyntaxError # Check if command name is valid - if (cmdname not in essentialcmds and - cmdname not in singlecmds and - cmdname not in multiplecmds and - cmdname not in geometrycmds): - logger.exception('Your input file contains an invalid command: ' + - cmdname) + if ( + cmdname not in essentialcmds + and cmdname not in singlecmds + and cmdname not in multiplecmds + and cmdname not in geometrycmds + ): + logger.exception("Your input file contains an invalid command: " + cmdname) raise SyntaxError # Count essential commands @@ -264,25 +309,26 @@ def check_cmd_names(processedlines, checkessential=True): # Assign command parameters as values to dictionary keys if cmdname in singlecmds: if singlecmds[cmdname] is None: - singlecmds[cmdname] = cmd[1].strip(' \t\n') + singlecmds[cmdname] = cmd[1].strip(" \t\n") else: - logger.exception('You can only have a single instance of ' + - cmdname + ' in your model') + logger.exception("You can only have a single instance of " + cmdname + " in your model") raise SyntaxError elif cmdname in multiplecmds: - multiplecmds[cmdname].append(cmd[1].strip(' \t\n')) + multiplecmds[cmdname].append(cmd[1].strip(" \t\n")) elif cmdname in geometrycmds: - geometry.append(processedlines[lindex].strip(' \t\n')) + geometry.append(processedlines[lindex].strip(" \t\n")) lindex += 1 if checkessential: - if (countessentialcmds < len(essentialcmds)): - logger.exception('Your input file is missing essential commands ' + - 'required to run a model. Essential commands are: ' + - ', '.join(essentialcmds)) + if countessentialcmds < len(essentialcmds): + logger.exception( + "Your input file is missing essential commands " + + "required to run a model. Essential commands are: " + + ", ".join(essentialcmds) + ) raise SyntaxError return singlecmds, multiplecmds, geometry @@ -328,19 +374,17 @@ def parse_hash_commands(scene): """ with open(config.sim_config.input_file_path) as inputfile: - usernamespace = config.get_model_config().get_usernamespace() # Read input file and process any Python and include file commands processedlines = process_python_include_code(inputfile, usernamespace) # Print constants/variables in user-accessable namespace - uservars = '' + uservars = "" for key, value in sorted(usernamespace.items()): - if key != '__builtins__': - uservars += f'{key}: {value}, ' - logger.info(f'Constants/variables used/available for Python scripting: ' + - f'{{{uservars[:-2]}}}\n') + if key != "__builtins__": + uservars += f"{key}: {value}, " + logger.info(f"Constants/variables used/available for Python scripting: " + f"{{{uservars[:-2]}}}\n") # Write a file containing the input commands after Python or include # file commands have been processed @@ -356,6 +400,7 @@ def parse_hash_commands(scene): class Capturing(list): """Context manager to capture standard output stream.""" + # https://stackoverflow.com/questions/16571150/how-to-capture-stdout-output-from-a-python-function-call def __enter__(self): @@ -365,13 +410,13 @@ class Capturing(list): def __exit__(self, *args): self.extend(self._stringio.getvalue().splitlines()) - del self._stringio # free up some memory + del self._stringio # free up some memory sys.stdout = self._stdout def user_libs_fn_to_scene_obj(f, *args, **kwargs): """Function to convert library functions in the toolboxes directory - into geometry objects which can be added to the scene. + into geometry objects which can be added to the scene. """ with Capturing() as str_cmds: diff --git a/gprMax/hash_cmds_geometry.py b/gprMax/hash_cmds_geometry.py index e71a1c1f..271c3885 100644 --- a/gprMax/hash_cmds_geometry.py +++ b/gprMax/hash_cmds_geometry.py @@ -41,27 +41,27 @@ logger = logging.getLogger(__name__) def check_averaging(averaging): """Check and set material averaging value. - Args: - averaging: string for input value from hash command - should be 'y' - or 'n'. + Args: + averaging: string for input value from hash command - should be 'y' + or 'n'. - Returns: - averaging: boolean for geometry object material averaging. - """ + Returns: + averaging: boolean for geometry object material averaging. + """ - if averaging == 'y': + if averaging == "y": averaging = True - elif averaging == 'n': + elif averaging == "n": averaging = False else: - logger.exception('Averaging should be either y or n') + logger.exception("Averaging should be either y or n") return averaging def process_geometrycmds(geometry): - """Checks the validity of command parameters, creates instances of classes - of parameters, and calls functions to directly set arrays solid, rigid + """Checks the validity of command parameters, creates instances of classes + of parameters, and calls functions to directly set arrays solid, rigid and ID. Args: @@ -76,13 +76,11 @@ def process_geometrycmds(geometry): for object in geometry: tmp = object.split() - if tmp[0] == '#geometry_objects_read:': - from .cmds_geometry.geometry_objects_read import \ - GeometryObjectsRead - + if tmp[0] == "#geometry_objects_read:": + from .cmds_geometry.geometry_objects_read import GeometryObjectsRead + if len(tmp) != 6: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires exactly five parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires exactly five parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -90,47 +88,49 @@ def process_geometrycmds(geometry): gor = GeometryObjectsRead(p1=p1, geofile=tmp[4], matfile=tmp[5]) scene_objects.append(gor) - elif tmp[0] == '#edge:': + elif tmp[0] == "#edge:": if len(tmp) != 8: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires exactly seven parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires exactly seven parameters") raise ValueError - edge = Edge(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), - p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])), - material_id=tmp[7]) + edge = Edge( + p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), + p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])), + material_id=tmp[7], + ) scene_objects.append(edge) - elif tmp[0] == '#plate:': + elif tmp[0] == "#plate:": if len(tmp) < 8: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least seven parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least seven parameters") raise ValueError # Isotropic case if len(tmp) == 8: - plate = Plate(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), - p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])), - material_id=tmp[7]) + plate = Plate( + p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), + p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])), + material_id=tmp[7], + ) # Anisotropic case elif len(tmp) == 9: - plate = Plate(p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), - p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])), - material_ids=tmp[7:]) + plate = Plate( + p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), + p2=(float(tmp[4]), float(tmp[5]), float(tmp[6])), + material_ids=tmp[7:], + ) else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(plate) - elif tmp[0] == '#triangle:': + elif tmp[0] == "#triangle:": if len(tmp) < 12: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least eleven parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least eleven parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -140,31 +140,26 @@ def process_geometrycmds(geometry): # Isotropic case with no user specified averaging if len(tmp) == 12: - triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, - material_id=tmp[11]) + triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11]) # Isotropic case with user specified averaging elif len(tmp) == 13: averaging = check_averaging(tmp[12].lower()) - triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, - material_id=tmp[11], averaging=averaging) + triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_id=tmp[11], averaging=averaging) # Uniaxial anisotropic case elif len(tmp) == 14: - triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, - material_ids=tmp[11:]) + triangle = Triangle(p1=p1, p2=p2, p3=p3, thickness=thickness, material_ids=tmp[11:]) else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(triangle) - elif tmp[0] == '#box:': + elif tmp[0] == "#box:": if len(tmp) < 8: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least seven parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least seven parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -184,16 +179,14 @@ def process_geometrycmds(geometry): box = Box(p1=p1, p2=p2, material_ids=tmp[7:]) else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(box) - elif tmp[0] == '#cylinder:': + elif tmp[0] == "#cylinder:": if len(tmp) < 9: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least eight parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least eight parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -207,24 +200,21 @@ def process_geometrycmds(geometry): # Isotropic case with user specified averaging elif len(tmp) == 10: averaging = check_averaging(tmp[9].lower()) - cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8], - averaging=averaging) + cylinder = Cylinder(p1=p1, p2=p2, r=r, material_id=tmp[8], averaging=averaging) # Uniaxial anisotropic case elif len(tmp) == 11: cylinder = Cylinder(p1=p1, p2=p2, r=r, material_ids=tmp[8:]) - + else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(cylinder) - elif tmp[0] == '#cone:': + elif tmp[0] == "#cone:": if len(tmp) < 10: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least nine parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least nine parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -239,24 +229,21 @@ def process_geometrycmds(geometry): # Isotropic case with user specified averaging elif len(tmp) == 11: averaging = check_averaging(tmp[10].lower()) - cone = Cone(p1=p1, p2=p2, r1=r1, r2=r2, material_id=tmp[9], - averaging=averaging) + cone = Cone(p1=p1, p2=p2, r1=r1, r2=r2, material_id=tmp[9], averaging=averaging) # Uniaxial anisotropic case elif len(tmp) == 12: cone = Cone(p1=p1, p2=p2, r1=r1, r2=r2, material_ids=tmp[9:]) - + else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(cone) - elif tmp[0] == '#cylindrical_sector:': + elif tmp[0] == "#cylindrical_sector:": if len(tmp) < 10: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least nine parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least nine parameters") raise ValueError normal = tmp[1].lower() @@ -270,41 +257,57 @@ def process_geometrycmds(geometry): # Isotropic case with no user specified averaging if len(tmp) == 10: - cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1, - ctr2=ctr2, extent1=extent1, - extent2=extent2, r=r, - start=start, end=end, - material_id=tmp[9]) + cylindrical_sector = CylindricalSector( + normal=normal, + ctr1=ctr1, + ctr2=ctr2, + extent1=extent1, + extent2=extent2, + r=r, + start=start, + end=end, + material_id=tmp[9], + ) # Isotropic case with user specified averaging elif len(tmp) == 11: averaging = check_averaging(tmp[10].lower()) - cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1, - ctr2=ctr2, extent1=extent1, - extent2=extent2, r=r, - start=start, end=end, - averaging=averaging, - material_id=tmp[9]) + cylindrical_sector = CylindricalSector( + normal=normal, + ctr1=ctr1, + ctr2=ctr2, + extent1=extent1, + extent2=extent2, + r=r, + start=start, + end=end, + averaging=averaging, + material_id=tmp[9], + ) # Uniaxial anisotropic case elif len(tmp) == 12: - cylindrical_sector = CylindricalSector(normal=normal, ctr1=ctr1, - ctr2=ctr2, extent1=extent1, - extent2=extent2, r=r, - start=start, end=end, - material_ids=tmp[9:]) + cylindrical_sector = CylindricalSector( + normal=normal, + ctr1=ctr1, + ctr2=ctr2, + extent1=extent1, + extent2=extent2, + r=r, + start=start, + end=end, + material_ids=tmp[9:], + ) else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(cylindrical_sector) - elif tmp[0] == '#sphere:': + elif tmp[0] == "#sphere:": if len(tmp) < 6: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least five parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least five parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -317,24 +320,21 @@ def process_geometrycmds(geometry): # Isotropic case with user specified averaging elif len(tmp) == 7: averaging = check_averaging(tmp[6].lower()) - sphere = Sphere(p1=p1, r=r, material_id=tmp[5], - averaging=averaging) + sphere = Sphere(p1=p1, r=r, material_id=tmp[5], averaging=averaging) # Uniaxial anisotropic case elif len(tmp) == 8: sphere = Sphere(p1=p1, r=r, material_id=tmp[5:]) else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(sphere) - elif tmp[0] == '#ellipsoid:': + elif tmp[0] == "#ellipsoid:": if len(tmp) < 8: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least seven parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least seven parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -344,33 +344,28 @@ def process_geometrycmds(geometry): # Isotropic case with no user specified averaging if len(tmp) == 8: - ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr, - material_id=tmp[7]) + ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr, material_id=tmp[7]) # Isotropic case with user specified averaging elif len(tmp) == 9: averaging = check_averaging(tmp[8].lower()) - ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr, - material_id=tmp[7], averaging=averaging) + ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr, material_id=tmp[7], averaging=averaging) # Uniaxial anisotropic case elif len(tmp) == 8: - ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr, - material_id=tmp[7:]) + ellipsoid = Ellipsoid(p1=p1, xr=xr, yr=yr, zr=zr, material_id=tmp[7:]) else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(ellipsoid) - elif tmp[0] == '#fractal_box:': + elif tmp[0] == "#fractal_box:": # Default is no dielectric smoothing for a fractal box if len(tmp) < 14: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least thirteen parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least thirteen parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -382,22 +377,40 @@ def process_geometrycmds(geometry): ID = tmp[13] if len(tmp) == 14: - fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, - weighting=weighting, n_materials=n_materials, - mixing_model_id=mixing_model_id, id=ID) + fb = FractalBox( + p1=p1, + p2=p2, + frac_dim=frac_dim, + weighting=weighting, + n_materials=n_materials, + mixing_model_id=mixing_model_id, + id=ID, + ) elif len(tmp) == 15: - fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, - weighting=weighting, n_materials=n_materials, - mixing_model_id=mixing_model_id, id=ID, - seed=tmp[14]) + fb = FractalBox( + p1=p1, + p2=p2, + frac_dim=frac_dim, + weighting=weighting, + n_materials=n_materials, + mixing_model_id=mixing_model_id, + id=ID, + seed=tmp[14], + ) elif len(tmp) == 16: - fb = FractalBox(p1=p1, p2=p2, frac_dim=frac_dim, - weighting=weighting, n_materials=n_materials, - mixing_model_id=mixing_model_id, id=ID, - seed=tmp[14], averaging=tmp[15].lower()) + fb = FractalBox( + p1=p1, + p2=p2, + frac_dim=frac_dim, + weighting=weighting, + n_materials=n_materials, + mixing_model_id=mixing_model_id, + id=ID, + seed=tmp[14], + averaging=tmp[15].lower(), + ) else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(fb) @@ -406,10 +419,9 @@ def process_geometrycmds(geometry): for object in geometry: tmp = object.split() - if tmp[0] == '#add_surface_roughness:': + if tmp[0] == "#add_surface_roughness:": if len(tmp) < 13: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least twelve parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least twelve parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -420,25 +432,33 @@ def process_geometrycmds(geometry): fractal_box_id = tmp[12] if len(tmp) == 13: - asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim, - weighting=weighting, limits=limits, - fractal_box_id=fractal_box_id) + asr = AddSurfaceRoughness( + p1=p1, + p2=p2, + frac_dim=frac_dim, + weighting=weighting, + limits=limits, + fractal_box_id=fractal_box_id, + ) elif len(tmp) == 14: - asr = AddSurfaceRoughness(p1=p1, p2=p2, frac_dim=frac_dim, - weighting=weighting, limits=limits, - fractal_box_id=fractal_box_id, - seed=int(tmp[13])) + asr = AddSurfaceRoughness( + p1=p1, + p2=p2, + frac_dim=frac_dim, + weighting=weighting, + limits=limits, + fractal_box_id=fractal_box_id, + seed=int(tmp[13]), + ) else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(asr) - if tmp[0] == '#add_surface_water:': + if tmp[0] == "#add_surface_water:": if len(tmp) != 9: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires exactly eight parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires exactly eight parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -446,14 +466,12 @@ def process_geometrycmds(geometry): depth = float(tmp[7]) fractal_box_id = tmp[8] - asf = AddSurfaceWater(p1=p1, p2=p2, depth=depth, - fractal_box_id=fractal_box_id) + asf = AddSurfaceWater(p1=p1, p2=p2, depth=depth, fractal_box_id=fractal_box_id) scene_objects.append(asf) - if tmp[0] == '#add_grass:': + if tmp[0] == "#add_grass:": if len(tmp) < 12: - logger.exception("'" + ' '.join(tmp) + "'" + - ' requires at least eleven parameters') + logger.exception("'" + " ".join(tmp) + "'" + " requires at least eleven parameters") raise ValueError p1 = (float(tmp[1]), float(tmp[2]), float(tmp[3])) @@ -464,19 +482,28 @@ def process_geometrycmds(geometry): fractal_box_id = tmp[11] if len(tmp) == 12: - grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim, - limits=limits, n_blades=n_blades, - fractal_box_id=fractal_box_id) + grass = AddGrass( + p1=p1, + p2=p2, + frac_dim=frac_dim, + limits=limits, + n_blades=n_blades, + fractal_box_id=fractal_box_id, + ) elif len(tmp) == 13: - grass = AddGrass(p1=p1, p2=p2, frac_dim=frac_dim, - limits=limits, n_blades=n_blades, - fractal_box_id=fractal_box_id, - seed=int(tmp[12])) + grass = AddGrass( + p1=p1, + p2=p2, + frac_dim=frac_dim, + limits=limits, + n_blades=n_blades, + fractal_box_id=fractal_box_id, + seed=int(tmp[12]), + ) else: - logger.exception("'" + ' '.join(tmp) + "'" + - ' too many parameters have been given') + logger.exception("'" + " ".join(tmp) + "'" + " too many parameters have been given") raise ValueError scene_objects.append(grass) - return scene_objects \ No newline at end of file + return scene_objects diff --git a/gprMax/hash_cmds_multiuse.py b/gprMax/hash_cmds_multiuse.py index 6925ae8c..18e71c06 100644 --- a/gprMax/hash_cmds_multiuse.py +++ b/gprMax/hash_cmds_multiuse.py @@ -18,12 +18,25 @@ import logging -from .cmds_multiuse import (AddDebyeDispersion, AddDrudeDispersion, - AddLorentzDispersion, GeometryObjectsWrite, - GeometryView, HertzianDipole, MagneticDipole, - Material, MaterialList, MaterialRange, Rx, RxArray, - Snapshot, SoilPeplinski, TransmissionLine, - VoltageSource, Waveform) +from .cmds_multiuse import ( + AddDebyeDispersion, + AddDrudeDispersion, + AddLorentzDispersion, + GeometryObjectsWrite, + GeometryView, + HertzianDipole, + MagneticDipole, + Material, + MaterialList, + MaterialRange, + Rx, + RxArray, + Snapshot, + SoilPeplinski, + TransmissionLine, + VoltageSource, + Waveform, +) logger = logging.getLogger(__name__) @@ -41,137 +54,143 @@ def process_multicmds(multicmds): scene_objects = [] - cmdname = '#waveform' + cmdname = "#waveform" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 4: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires exactly four parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly four parameters") raise ValueError - waveform = Waveform(wave_type=tmp[0], amp=float(tmp[1]), - freq=float(tmp[2]), id=tmp[3]) + waveform = Waveform(wave_type=tmp[0], amp=float(tmp[1]), freq=float(tmp[2]), id=tmp[3]) scene_objects.append(waveform) - cmdname = '#voltage_source' + cmdname = "#voltage_source" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) == 6: - voltage_source = VoltageSource(polarisation=tmp[0].lower(), - p1=(float(tmp[1]), float(tmp[2]), - float(tmp[3])), resistance=float(tmp[4]), - waveform_id=tmp[5]) + voltage_source = VoltageSource( + polarisation=tmp[0].lower(), + p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), + resistance=float(tmp[4]), + waveform_id=tmp[5], + ) elif len(tmp) == 8: - voltage_source = VoltageSource(polarisation=tmp[0].lower(), - p1=(float(tmp[1]), float(tmp[2]), - float(tmp[3])), resistance=float(tmp[4]), - waveform_id=tmp[5], start=float(tmp[6]), - end=float(tmp[7])) + voltage_source = VoltageSource( + polarisation=tmp[0].lower(), + p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), + resistance=float(tmp[4]), + waveform_id=tmp[5], + start=float(tmp[6]), + end=float(tmp[7]), + ) else: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires at least six parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least six parameters") raise ValueError scene_objects.append(voltage_source) - cmdname = '#hertzian_dipole' + cmdname = "#hertzian_dipole" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) < 5: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires at least five parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least five parameters") raise ValueError if len(tmp) == 5: - hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), - float(tmp[2]), float(tmp[3])), - waveform_id=tmp[4]) + hertzian_dipole = HertzianDipole( + polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4] + ) elif len(tmp) == 7: - hertzian_dipole = HertzianDipole(polarisation=tmp[0], p1=(float(tmp[1]), - float(tmp[2]), float(tmp[3])), - waveform_id=tmp[4], start=float(tmp[5]), - end=float(tmp[6])) + hertzian_dipole = HertzianDipole( + polarisation=tmp[0], + p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), + waveform_id=tmp[4], + start=float(tmp[5]), + end=float(tmp[6]), + ) else: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' too many parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " too many parameters") raise ValueError scene_objects.append(hertzian_dipole) - cmdname = '#magnetic_dipole' + cmdname = "#magnetic_dipole" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) < 5: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires at least five parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least five parameters") raise ValueError if len(tmp) == 5: - magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), - float(tmp[2]), float(tmp[3])), - waveform_id=tmp[4]) + magnetic_dipole = MagneticDipole( + polarisation=tmp[0], p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), waveform_id=tmp[4] + ) elif len(tmp) == 7: - magnetic_dipole = MagneticDipole(polarisation=tmp[0], p1=(float(tmp[1]), - float(tmp[2]), float(tmp[3])), - waveform_id=tmp[4], start=float(tmp[5]), - end=float(tmp[6])) + magnetic_dipole = MagneticDipole( + polarisation=tmp[0], + p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), + waveform_id=tmp[4], + start=float(tmp[5]), + end=float(tmp[6]), + ) else: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' too many parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " too many parameters") raise ValueError scene_objects.append(magnetic_dipole) - cmdname = '#transmission_line' + cmdname = "#transmission_line" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) < 6: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires at least six parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least six parameters") raise ValueError if len(tmp) == 6: - tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), - float(tmp[2]), float(tmp[3])), - resistance=float(tmp[4]), waveform_id=tmp[5]) + tl = TransmissionLine( + polarisation=tmp[0], + p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), + resistance=float(tmp[4]), + waveform_id=tmp[5], + ) elif len(tmp) == 8: - tl = TransmissionLine(polarisation=tmp[0], p1=(float(tmp[1]), - float(tmp[2]), float(tmp[3])), - resistance=float(tmp[4]), waveform_id=tmp[5], - start=tmp[6], end=tmp[7]) + tl = TransmissionLine( + polarisation=tmp[0], + p1=(float(tmp[1]), float(tmp[2]), float(tmp[3])), + resistance=float(tmp[4]), + waveform_id=tmp[5], + start=tmp[6], + end=tmp[7], + ) else: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' too many parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " too many parameters") raise ValueError scene_objects.append(tl) - cmdname = '#rx' + cmdname = "#rx" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 3 and len(tmp) < 5: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' has an incorrect number of parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " has an incorrect number of parameters") raise ValueError if len(tmp) == 3: rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2]))) else: - rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])), - id=tmp[3], outputs=' '.join(tmp[4:])) + rx = Rx(p1=(float(tmp[0]), float(tmp[1]), float(tmp[2])), id=tmp[3], outputs=" ".join(tmp[4:])) scene_objects.append(rx) - cmdname = '#rx_array' + cmdname = "#rx_array" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 9: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires exactly nine parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly nine parameters") raise ValueError p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2])) @@ -181,13 +200,12 @@ def process_multicmds(multicmds): rx_array = RxArray(p1=p1, p2=p2, dl=dl) scene_objects.append(rx_array) - cmdname = '#snapshot' + cmdname = "#snapshot" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 11: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires exactly eleven parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly eleven parameters") raise ValueError p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2])) @@ -197,64 +215,57 @@ def process_multicmds(multicmds): try: iterations = int(tmp[9]) - snapshot = Snapshot(p1=p1, p2=p2, dl=dl, iterations=iterations, - filename=filename) + snapshot = Snapshot(p1=p1, p2=p2, dl=dl, iterations=iterations, filename=filename) except ValueError: time = float(tmp[9]) - snapshot = Snapshot(p1=p1, p2=p2, dl=dl, time=time, - filename=filename) + snapshot = Snapshot(p1=p1, p2=p2, dl=dl, time=time, filename=filename) scene_objects.append(snapshot) - cmdname = '#material' + cmdname = "#material" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 5: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires exactly five parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly five parameters") raise ValueError - material = Material(er=float(tmp[0]), se=float(tmp[1]), - mr=float(tmp[2]), sm=float(tmp[3]), id=tmp[4]) + material = Material(er=float(tmp[0]), se=float(tmp[1]), mr=float(tmp[2]), sm=float(tmp[3]), id=tmp[4]) scene_objects.append(material) - cmdname = '#add_dispersion_debye' + cmdname = "#add_dispersion_debye" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) < 4: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires at least four parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least four parameters") raise ValueError poles = int(tmp[0]) er_delta = [] tau = [] - material_ids = tmp[(2 * poles) + 1:len(tmp)] + material_ids = tmp[(2 * poles) + 1 : len(tmp)] for pole in range(1, 2 * poles, 2): er_delta.append(float(tmp[pole])) tau.append(float(tmp[pole + 1])) - debye_dispersion = AddDebyeDispersion(poles=poles, er_delta=er_delta, - tau=tau, material_ids=material_ids) + debye_dispersion = AddDebyeDispersion(poles=poles, er_delta=er_delta, tau=tau, material_ids=material_ids) scene_objects.append(debye_dispersion) - cmdname = '#add_dispersion_lorentz' + cmdname = "#add_dispersion_lorentz" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) < 5: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires at least five parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least five parameters") raise ValueError poles = int(tmp[0]) - material_ids = tmp[(3 * poles) + 1:len(tmp)] + material_ids = tmp[(3 * poles) + 1 : len(tmp)] er_delta = [] tau = [] alpha = [] @@ -264,24 +275,22 @@ def process_multicmds(multicmds): tau.append(float(tmp[pole + 1])) alpha.append(float(tmp[pole + 2])) - lorentz_dispersion = AddLorentzDispersion(poles=poles, - material_ids=material_ids, - er_delta=er_delta, tau=tau, - alpha=alpha) + lorentz_dispersion = AddLorentzDispersion( + poles=poles, material_ids=material_ids, er_delta=er_delta, tau=tau, alpha=alpha + ) scene_objects.append(lorentz_dispersion) - cmdname = '#add_dispersion_drude' + cmdname = "#add_dispersion_drude" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) < 5: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires at least five parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at least five parameters") raise ValueError poles = int(tmp[0]) - material_ids = tmp[(3 * poles) + 1:len(tmp)] + material_ids = tmp[(3 * poles) + 1 : len(tmp)] tau = [] alpha = [] @@ -289,53 +298,49 @@ def process_multicmds(multicmds): tau.append(float(tmp[pole])) alpha.append(float(tmp[pole + 1])) - drude_dispersion = AddDrudeDispersion(poles=poles, - material_ids=material_ids, - tau=tau, alpha=alpha) + drude_dispersion = AddDrudeDispersion(poles=poles, material_ids=material_ids, tau=tau, alpha=alpha) scene_objects.append(drude_dispersion) - cmdname = '#soil_peplinski' + cmdname = "#soil_peplinski" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 7: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires at exactly seven parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at exactly seven parameters") raise ValueError - soil = SoilPeplinski(sand_fraction=float(tmp[0]), - clay_fraction=float(tmp[1]), - bulk_density=float(tmp[2]), - sand_density=float(tmp[3]), - water_fraction_lower=float(tmp[4]), - water_fraction_upper=float(tmp[5]), - id=tmp[6]) + soil = SoilPeplinski( + sand_fraction=float(tmp[0]), + clay_fraction=float(tmp[1]), + bulk_density=float(tmp[2]), + sand_density=float(tmp[3]), + water_fraction_lower=float(tmp[4]), + water_fraction_upper=float(tmp[5]), + id=tmp[6], + ) scene_objects.append(soil) - cmdname = '#geometry_view' + cmdname = "#geometry_view" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 11: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires exactly eleven parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly eleven parameters") raise ValueError p1 = float(tmp[0]), float(tmp[1]), float(tmp[2]) p2 = float(tmp[3]), float(tmp[4]), float(tmp[5]) dl = float(tmp[6]), float(tmp[7]), float(tmp[8]) - geometry_view = GeometryView(p1=p1, p2=p2, dl=dl, filename=tmp[9], - output_type=tmp[10]) + geometry_view = GeometryView(p1=p1, p2=p2, dl=dl, filename=tmp[9], output_type=tmp[10]) scene_objects.append(geometry_view) - cmdname = '#geometry_objects_write' + cmdname = "#geometry_objects_write" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 7: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires exactly seven parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires exactly seven parameters") raise ValueError p1 = float(tmp[0]), float(tmp[1]), float(tmp[2]) @@ -343,43 +348,42 @@ def process_multicmds(multicmds): gow = GeometryObjectsWrite(p1=p1, p2=p2, filename=tmp[6]) scene_objects.append(gow) - cmdname = '#material_range' + cmdname = "#material_range" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: tmp = cmdinstance.split() if len(tmp) != 9: - logger.exception("'" + cmdname + ': ' + ' '.join(tmp) + "'" + - ' requires at exactly nine parameters') + logger.exception("'" + cmdname + ": " + " ".join(tmp) + "'" + " requires at exactly nine parameters") raise ValueError - material_range = MaterialRange(er_lower=float(tmp[0]), - er_upper=float(tmp[1]), - sigma_lower=float(tmp[2]), - sigma_upper=float(tmp[3]), - mr_lower=float(tmp[4]), - mr_upper=float(tmp[5]), - ro_lower=float(tmp[6]), - ro_upper=float(tmp[7]), - id=tmp[8]) + material_range = MaterialRange( + er_lower=float(tmp[0]), + er_upper=float(tmp[1]), + sigma_lower=float(tmp[2]), + sigma_upper=float(tmp[3]), + mr_lower=float(tmp[4]), + mr_upper=float(tmp[5]), + ro_lower=float(tmp[6]), + ro_upper=float(tmp[7]), + id=tmp[8], + ) scene_objects.append(material_range) - cmdname = '#material_list' + cmdname = "#material_list" if multicmds[cmdname] is not None: for cmdinstance in multicmds[cmdname]: 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 2 parameters") raise ValueError - + tokens = len(tmp) lmats = [] - for iter in range(tokens-1): + for iter in range(tokens - 1): lmats.append(tmp[iter]) - material_list = MaterialList(list_of_materials=lmats, - id=tmp[tokens-1]) + material_list = MaterialList(list_of_materials=lmats, id=tmp[tokens - 1]) scene_objects.append(material_list) - return scene_objects \ No newline at end of file + return scene_objects diff --git a/gprMax/hash_cmds_singleuse.py b/gprMax/hash_cmds_singleuse.py index 86ac37cc..00279d60 100644 --- a/gprMax/hash_cmds_singleuse.py +++ b/gprMax/hash_cmds_singleuse.py @@ -18,10 +18,19 @@ import logging -from .cmds_singleuse import (Discretisation, Domain, ExcitationFile, - OMPThreads, OutputDir, PMLProps, RxSteps, - SrcSteps, TimeStepStabilityFactor, TimeWindow, - Title) +from .cmds_singleuse import ( + Discretisation, + Domain, + ExcitationFile, + OMPThreads, + OutputDir, + PMLProps, + RxSteps, + SrcSteps, + TimeStepStabilityFactor, + TimeWindow, + Title, +) logger = logging.getLogger(__name__) @@ -40,62 +49,65 @@ def process_singlecmds(singlecmds): scene_objects = [] # Check validity of command parameters in order needed - cmd = '#title' + cmd = "#title" if singlecmds[cmd] is not None: title = Title(name=str(singlecmds[cmd])) scene_objects.append(title) - cmd = '#output_dir' + cmd = "#output_dir" if singlecmds[cmd] is not None: output_dir = OutputDir(dir=singlecmds[cmd]) scene_objects.append(output_dir) # Number of threads for CPU-based (OpenMP) parallelised parts of code - cmd = '#omp_threads' + cmd = "#omp_threads" if singlecmds[cmd] is not None: tmp = tuple(int(x) for x in singlecmds[cmd].split()) if len(tmp) != 1: - logger.exception(f'{cmd} requires exactly one parameter to specify ' + - f'the number of CPU OpenMP threads to use') + logger.exception( + f"{cmd} requires exactly one parameter to specify " + f"the number of CPU OpenMP threads to use" + ) raise ValueError omp_threads = OMPThreads(n=tmp[0]) scene_objects.append(omp_threads) - cmd = '#dx_dy_dz' + cmd = "#dx_dy_dz" if singlecmds[cmd] is not None: tmp = [float(x) for x in singlecmds[cmd].split()] if len(tmp) != 3: - logger.exception(f'{cmd} requires exactly three parameters') + logger.exception(f"{cmd} requires exactly three parameters") raise ValueError dl = (tmp[0], tmp[1], tmp[2]) discretisation = Discretisation(p1=dl) scene_objects.append(discretisation) - cmd = '#domain' + cmd = "#domain" if singlecmds[cmd] is not None: tmp = [float(x) for x in singlecmds[cmd].split()] if len(tmp) != 3: - logger.exception(f'{cmd} requires exactly three parameters') + logger.exception(f"{cmd} requires exactly three parameters") raise ValueError p1 = (tmp[0], tmp[1], tmp[2]) domain = Domain(p1=p1) scene_objects.append(domain) - cmd = '#time_step_stability_factor' + cmd = "#time_step_stability_factor" if singlecmds[cmd] is not None: tmp = tuple(float(x) for x in singlecmds[cmd].split()) tsf = TimeStepStabilityFactor(f=tmp[0]) scene_objects.append(tsf) - cmd = '#time_window' + cmd = "#time_window" if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) != 1: - logger.exception(f'{cmd} requires exactly one parameter to specify the ' + - f'time window. Either in seconds or number of iterations.') + logger.exception( + f"{cmd} requires exactly one parameter to specify the " + + f"time window. Either in seconds or number of iterations." + ) raise ValueError tmp = tmp[0].lower() @@ -110,40 +122,37 @@ def process_singlecmds(singlecmds): scene_objects.append(tw) - cmd = '#pml_cells' + 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)') + logger.exception(f"{cmd} requires either one or six parameter(s)") raise ValueError if len(tmp) == 1: pml_cells = PMLProps(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])) + 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]) + ) scene_objects.append(pml_cells) - cmd = '#src_steps' + cmd = "#src_steps" if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) != 3: - logger.exception(f'{cmd} requires exactly three parameters') + logger.exception(f"{cmd} requires exactly three parameters") raise ValueError p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2])) src_steps = SrcSteps(p1=p1) scene_objects.append(src_steps) - cmd = '#rx_steps' + cmd = "#rx_steps" if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) != 3: - logger.exception(f'{cmd} requires exactly three parameters') + logger.exception(f"{cmd} requires exactly three parameters") raise ValueError p1 = (float(tmp[0]), float(tmp[1]), float(tmp[2])) @@ -151,11 +160,11 @@ def process_singlecmds(singlecmds): scene_objects.append(rx_steps) # Excitation file for user-defined source waveforms - cmd = '#excitation_file' + cmd = "#excitation_file" if singlecmds[cmd] is not None: tmp = singlecmds[cmd].split() if len(tmp) not in [1, 3]: - logger.exception(f'{cmd} requires either one or three parameter(s)') + logger.exception(f"{cmd} requires either one or three parameter(s)") raise ValueError if len(tmp) > 1: diff --git a/gprMax/materials.py b/gprMax/materials.py index a723efd3..ed7b1935 100644 --- a/gprMax/materials.py +++ b/gprMax/materials.py @@ -25,9 +25,10 @@ import gprMax.config as config logger = logging.getLogger(__name__) + class Material: """Super-class to describe generic, non-dispersive materials, - their properties and update coefficients. + their properties and update coefficients. """ def __init__(self, numID, ID): @@ -39,7 +40,7 @@ class Material: self.numID = numID self.ID = ID - self.type = '' + self.type = "" # Default material averaging self.averagable = True @@ -71,10 +72,10 @@ class Material: G: FDTDGrid class describing a grid in a model. """ - EA = (config.sim_config.em_consts['e0'] * self.er / G.dt) + 0.5 * self.se - EB = (config.sim_config.em_consts['e0'] * self.er / G.dt) - 0.5 * self.se + EA = (config.sim_config.em_consts["e0"] * self.er / G.dt) + 0.5 * self.se + EB = (config.sim_config.em_consts["e0"] * self.er / G.dt) - 0.5 * self.se - if self.ID == 'pec' or self.se == float('inf'): + if self.ID == "pec" or self.se == float("inf"): self.CA = 0 self.CBx = 0 self.CBy = 0 @@ -104,7 +105,7 @@ class Material: class DispersiveMaterial(Material): """Class to describe materials with frequency dependent properties, e.g. - Debye, Drude, Lorenz. + Debye, Drude, Lorenz. """ # Properties of water from: http://dx.doi.org/10.1109/TGRS.2006.873208 @@ -135,46 +136,64 @@ class DispersiveMaterial(Material): # The implementation of the dispersive material modelling comes from the # derivation in: http://dx.doi.org/10.1109/TAP.2014.2308549 - self.w = np.zeros(config.get_model_config().materials['maxpoles'], - dtype=config.get_model_config().materials['dispersivedtype']) - self.q = np.zeros(config.get_model_config().materials['maxpoles'], - dtype=config.get_model_config().materials['dispersivedtype']) - self.zt = np.zeros(config.get_model_config().materials['maxpoles'], - dtype=config.get_model_config().materials['dispersivedtype']) - self.zt2 = np.zeros(config.get_model_config().materials['maxpoles'], - dtype=config.get_model_config().materials['dispersivedtype']) - self.eqt = np.zeros(config.get_model_config().materials['maxpoles'], - dtype=config.get_model_config().materials['dispersivedtype']) - self.eqt2 = np.zeros(config.get_model_config().materials['maxpoles'], - dtype=config.get_model_config().materials['dispersivedtype']) + self.w = np.zeros( + config.get_model_config().materials["maxpoles"], + dtype=config.get_model_config().materials["dispersivedtype"], + ) + self.q = np.zeros( + config.get_model_config().materials["maxpoles"], + dtype=config.get_model_config().materials["dispersivedtype"], + ) + self.zt = np.zeros( + config.get_model_config().materials["maxpoles"], + dtype=config.get_model_config().materials["dispersivedtype"], + ) + self.zt2 = np.zeros( + config.get_model_config().materials["maxpoles"], + dtype=config.get_model_config().materials["dispersivedtype"], + ) + self.eqt = np.zeros( + config.get_model_config().materials["maxpoles"], + dtype=config.get_model_config().materials["dispersivedtype"], + ) + self.eqt2 = np.zeros( + config.get_model_config().materials["maxpoles"], + dtype=config.get_model_config().materials["dispersivedtype"], + ) for x in range(self.poles): - if 'debye' in self.type: + if "debye" in self.type: self.w[x] = self.deltaer[x] / self.tau[x] self.q[x] = -1 / self.tau[x] - elif 'lorentz' in self.type: + elif "lorentz" in self.type: # tau for Lorentz materials are pole frequencies # alpha for Lorentz materials are the damping coefficients - wp2 = (2 * np.pi * self.tau[x])**2 - self.w[x] = -1j * ((wp2 * self.deltaer[x]) / np.sqrt(wp2 - self.alpha[x]**2)) - self.q[x] = -self.alpha[x] + (1j * np.sqrt(wp2 - self.alpha[x]**2)) - elif 'drude' in self.type: + wp2 = (2 * np.pi * self.tau[x]) ** 2 + self.w[x] = -1j * ((wp2 * self.deltaer[x]) / np.sqrt(wp2 - self.alpha[x] ** 2)) + self.q[x] = -self.alpha[x] + (1j * np.sqrt(wp2 - self.alpha[x] ** 2)) + elif "drude" in self.type: # tau for Drude materials are pole frequencies # alpha for Drude materials are the inverse of relaxation times - wp2 = (2 * np.pi * self.tau[x])**2 + wp2 = (2 * np.pi * self.tau[x]) ** 2 self.se += wp2 / self.alpha[x] - self.w[x] = - (wp2 / self.alpha[x]) - self.q[x] = - self.alpha[x] + self.w[x] = -(wp2 / self.alpha[x]) + self.q[x] = -self.alpha[x] self.eqt[x] = np.exp(self.q[x] * G.dt) self.eqt2[x] = np.exp(self.q[x] * (G.dt / 2)) self.zt[x] = (self.w[x] / self.q[x]) * (1 - self.eqt[x]) / G.dt self.zt2[x] = (self.w[x] / self.q[x]) * (1 - self.eqt2[x]) - EA = ((config.sim_config.em_consts['e0'] * self.er / G.dt) + 0.5 * self.se - - (config.sim_config.em_consts['e0'] / G.dt) * np.sum(self.zt2.real)) - EB = ((config.sim_config.em_consts['e0'] * self.er / G.dt) - 0.5 * self.se - - (config.sim_config.em_consts['e0'] / G.dt) * np.sum(self.zt2.real)) + EA = ( + (config.sim_config.em_consts["e0"] * self.er / G.dt) + + 0.5 * self.se + - (config.sim_config.em_consts["e0"] / G.dt) * np.sum(self.zt2.real) + ) + EB = ( + (config.sim_config.em_consts["e0"] * self.er / G.dt) + - 0.5 * self.se + - (config.sim_config.em_consts["e0"] / G.dt) * np.sum(self.zt2.real) + ) self.CA = EB / EA self.CBx = (1 / G.dx) * 1 / EA @@ -198,30 +217,30 @@ class DispersiveMaterial(Material): er = self.er w = 2 * np.pi * freq - er += self.se / (1j * w * config.sim_config.em_consts['e0']) - if 'debye' in self.type: + er += self.se / (1j * w * config.sim_config.em_consts["e0"]) + if "debye" in self.type: for pole in range(self.poles): er += self.deltaer[pole] / (1 + 1j * w * self.tau[pole]) - elif 'lorentz' in self.type: + elif "lorentz" in self.type: for pole in range(self.poles): - er += ((self.deltaer[pole] * self.tau[pole]**2) - / (self.tau[pole]**2 + 2j * w * self.alpha[pole] - w**2)) - elif 'drude' in self.type: + er += (self.deltaer[pole] * self.tau[pole] ** 2) / ( + self.tau[pole] ** 2 + 2j * w * self.alpha[pole] - w**2 + ) + elif "drude" in self.type: ersum = 0 for pole in range(self.poles): - ersum += self.tau[pole]**2 / (w**2 - 1j * w * self.alpha[pole]) + ersum += self.tau[pole] ** 2 / (w**2 - 1j * w * self.alpha[pole]) er -= ersum return er - + class PeplinskiSoil: """Soil objects that are characterised according to a mixing model - by Peplinski (http://dx.doi.org/10.1109/36.387598). + by Peplinski (http://dx.doi.org/10.1109/36.387598). """ - def __init__(self, ID, sandfraction, clayfraction, bulkdensity, - sandpartdensity, watervolfraction): + def __init__(self, ID, sandfraction, clayfraction, bulkdensity, sandpartdensity, watervolfraction): """ Args: ID: string for name of the soil. @@ -230,8 +249,8 @@ class PeplinskiSoil: bulkdensity: float of bulk density of the soil (g/cm3). sandpartdensity: float of density of the sand particles in the soil (g/cm3). - watervolfraction: tuple of floats of two numbers that specify a - range for the volumetric water fraction of the + watervolfraction: tuple of floats of two numbers that specify a + range for the volumetric water fraction of the soil. """ @@ -242,7 +261,7 @@ class PeplinskiSoil: self.rs = sandpartdensity self.mu = watervolfraction # Store all of the material IDs which allows for more general mixing models. - self.matID = [] + self.matID = [] def calculate_properties(self, nbins, G): """Calculates the real and imaginery part of a Debye model for the soil @@ -260,10 +279,10 @@ class PeplinskiSoil: watereri, waterer, watertau, watersig = calculate_water_properties(T, S) f = 1.3e9 w = 2 * np.pi * f - erealw = watereri + ((waterer - watereri) / (1 + (w * watertau)**2)) + erealw = watereri + ((waterer - watereri) / (1 + (w * watertau) ** 2)) a = 0.65 # Experimentally derived constant - es = (1.01 + 0.44 * self.rs)**2 - 0.062 #  Relative permittivity of sand particles + es = (1.01 + 0.44 * self.rs) ** 2 - 0.062 #  Relative permittivity of sand particles b1 = 1.2748 - 0.519 * self.S - 0.152 * self.C b2 = 1.33797 - 0.603 * self.S - 0.166 * self.C @@ -272,44 +291,43 @@ class PeplinskiSoil: # For frequencies in the range 1.4GHz to 18GHz # sigf = -1.645 + 1.939 * self.rb - 2.25622 * self.S + 1.594 * self.C - # Generate a set of bins based on the given volumetric water fraction - # values. Changed to make sure mid points are contained completely within the ranges. + # Generate a set of bins based on the given volumetric water fraction + # values. Changed to make sure mid points are contained completely within the ranges. # The limiting values of the ranges are not included in this. mubins = np.linspace(self.mu[0], self.mu[1], nbins + 1) - # Generate a range of volumetric water fraction values the mid-point of + # Generate a range of volumetric water fraction values the mid-point of # each bin to make materials from - mumaterials = 0.5 * (mubins[1:nbins+1] + mubins[0:nbins]) + mumaterials = 0.5 * (mubins[1 : nbins + 1] + mubins[0:nbins]) # Create an iterator - muiter = np.nditer(mumaterials, flags=['c_index']) + muiter = np.nditer(mumaterials, flags=["c_index"]) while not muiter.finished: # Real part for frequencies in the range 1.4GHz to 18GHz - er = (1 + (self.rb / self.rs) * ((es**a) - 1) + (muiter[0]**b1 * erealw**a) - - muiter[0]) ** (1 / a) - # Real part for frequencies in the range 0.3GHz to 1.3GHz (linear + er = (1 + (self.rb / self.rs) * ((es**a) - 1) + (muiter[0] ** b1 * erealw**a) - muiter[0]) ** (1 / a) + # Real part for frequencies in the range 0.3GHz to 1.3GHz (linear # correction to 1.4-18GHz value) er = 1.15 * er - 0.68 # Permittivity at infinite frequency - eri = er - (muiter[0]**(b2 / a) * DispersiveMaterial.waterdeltaer) + eri = er - (muiter[0] ** (b2 / a) * DispersiveMaterial.waterdeltaer) # Effective conductivity - sig = muiter[0]**(b2 / a) * ((sigf * (self.rs - self.rb)) / (self.rs * muiter[0])) + sig = muiter[0] ** (b2 / a) * ((sigf * (self.rs - self.rb)) / (self.rs * muiter[0])) # Check to see if the material already exists before creating a new one - requiredID = '|{:.4f}|'.format(float(muiter[0])) + requiredID = "|{:.4f}|".format(float(muiter[0])) material = next((x for x in G.materials if x.ID == requiredID), None) if muiter.index == 0: if material: - self.matID.append(material.numID) + self.matID.append(material.numID) if not material: m = DispersiveMaterial(len(G.materials), requiredID) - m.type = 'debye' + m.type = "debye" m.averagable = False m.poles = 1 - if m.poles > config.get_model_config().materials['maxpoles']: - config.get_model_config().materials['maxpoles'] = m.poles + if m.poles > config.get_model_config().materials["maxpoles"]: + config.get_model_config().materials["maxpoles"] = m.poles m.er = eri m.se = sig m.deltaer.append(er - eri) @@ -318,35 +336,35 @@ class PeplinskiSoil: self.matID.append(m.numID) muiter.iternext() - + class RangeMaterial: - """Material objects defined by a given range of their parameters to be used - for fractal spatial distributions. + """Material objects defined by a given range of their parameters to be used + for fractal spatial distributions. """ def __init__(self, ID, er_range, se_range, mr_range, sm_range): """ Args: ID: string for name of the material range. - er_range: tuple of floats for relative permittivity range of the + er_range: tuple of floats for relative permittivity range of the materials. - se_range: tuple of floats for electric conductivity range of the + se_range: tuple of floats for electric conductivity range of the materials. - mr_range: tuple of floats for magnetic permeability of materials. - sm_range: tuple of floats for magnetic loss range of materials. + mr_range: tuple of floats for magnetic permeability of materials. + sm_range: tuple of floats for magnetic loss range of materials. """ self.ID = ID self.er = er_range self.sig = se_range - self.mu = mr_range + self.mu = mr_range self.ro = sm_range # Store all of the material IDs which allows for more general mixing models. - self.matID = [] - + self.matID = [] + def calculate_properties(self, nbins, G): - """Calculates the specific properties of each of the materials. + """Calculates the specific properties of each of the materials. Args: nbins: int for number of bins to use to create the different materials. @@ -354,53 +372,53 @@ class RangeMaterial: """ # Generate a set of relative permittivity bins based on the given range - erbins = np.linspace(self.er[0], self.er[1], nbins+1) + erbins = np.linspace(self.er[0], self.er[1], nbins + 1) - # Generate a range of relative permittivity values the mid-point of + # Generate a range of relative permittivity values the mid-point of # each bin to make materials from - ermaterials = 0.5 * (erbins[1:nbins+1] + erbins[0:nbins]) + ermaterials = 0.5 * (erbins[1 : nbins + 1] + erbins[0:nbins]) # Generate a set of conductivity bins based on the given range sigmabins = np.linspace(self.sig[0], self.sig[1], nbins + 1) - # Generate a range of conductivity values the mid-point of + # Generate a range of conductivity values the mid-point of # each bin to make materials from - sigmamaterials = 0.5 * (sigmabins[1:nbins+1] + sigmabins[0:nbins]) + sigmamaterials = 0.5 * (sigmabins[1 : nbins + 1] + sigmabins[0:nbins]) # Generate a set of magnetic permeability bins based on the given range mubins = np.linspace(self.mu[0], self.mu[1], nbins + 1) - # Generate a range of magnetic permeability values the mid-point of + # Generate a range of magnetic permeability values the mid-point of # each bin to make materials from - mumaterials = 0.5 * (mubins[1:nbins+1] + mubins[0:nbins]) - + mumaterials = 0.5 * (mubins[1 : nbins + 1] + mubins[0:nbins]) + # Generate a set of magnetic loss bins based on the given range robins = np.linspace(self.ro[0], self.ro[1], nbins + 1) - # Generate a range of magnetic loss values the mid-point of each bin to + # Generate a range of magnetic loss values the mid-point of each bin to # make materials from - romaterials = 0.5 * (robins[1:nbins+1] + robins[0:nbins]) + romaterials = 0.5 * (robins[1 : nbins + 1] + robins[0:nbins]) # Iterate over the bins for iter in np.arange(nbins): - # Relative permittivity + # Relative permittivity er = ermaterials[iter] # Effective conductivity - se = sigmamaterials[iter] + se = sigmamaterials[iter] # Magnetic permeability mr = mumaterials[iter] # Magnetic loss sm = romaterials[iter] # Check to see if the material already exists before creating a new one - requiredID = f'|{float(er):.4f}+{float(se):.4f}+{float(mr):.4f}+{float(sm):.4f}|' + requiredID = f"|{float(er):.4f}+{float(se):.4f}+{float(mr):.4f}+{float(sm):.4f}|" material = next((x for x in G.materials if x.ID == requiredID), None) if iter == 0: if material: self.matID.append(material.numID) if not material: m = Material(len(G.materials), requiredID) - m.type = '' + m.type = "" m.averagable = True m.er = er m.se = se @@ -411,23 +429,23 @@ class RangeMaterial: class ListMaterial: - """A list of predefined materials to be used for fractal spatial distributions. - This class does not create new materials but collects them to be used - in a stochastic distribution by a fractal box. + """A list of predefined materials to be used for fractal spatial distributions. + This class does not create new materials but collects them to be used + in a stochastic distribution by a fractal box. """ def __init__(self, ID, listofmaterials): """ Args: ID: string for name of the material list. - listofmaterials: list of material IDs. + listofmaterials: list of material IDs. """ self.ID = ID self.mat = listofmaterials # Store all of the material IDs which allows for more general mixing models. - self.matID = [] - + self.matID = [] + def calculate_properties(self, nbins, G): """Calculates the properties of the materials. @@ -442,12 +460,12 @@ class ListMaterial: # Check if the material already exists before creating a new one material = next((x for x in G.materials if x.ID == requiredID), None) self.matID.append(material.numID) - + if not material: - logger.exception(self.__str__() + f' material(s) {material} do not exist') + logger.exception(self.__str__() + f" material(s) {material} do not exist") raise ValueError - - + + def create_built_in_materials(G): """Creates pre-defined (built-in) materials. @@ -455,14 +473,14 @@ def create_built_in_materials(G): G: FDTDGrid class describing a grid in a model. """ - m = Material(0, 'pec') - m.se = float('inf') - m.type = 'builtin' + m = Material(0, "pec") + m.se = float("inf") + m.type = "builtin" m.averagable = False G.materials.append(m) - m = Material(1, 'free_space') - m.type = 'builtin' + m = Material(1, "free_space") + m.type = "builtin" G.materials.append(m) @@ -483,12 +501,12 @@ def calculate_water_properties(T=25, S=0): # Properties of water from: https://doi.org/10.1109/JOE.1977.1145319 eri = 4.9 er = 88.045 - 0.4147 * T + 6.295e-4 * T**2 + 1.075e-5 * T**3 - tau = (1 / (2 * np.pi)) * (1.1109e-10 - 3.824e-12 * T + 6.938e-14 * T**2 - - 5.096e-16 * T**3) + tau = (1 / (2 * np.pi)) * (1.1109e-10 - 3.824e-12 * T + 6.938e-14 * T**2 - 5.096e-16 * T**3) delta = 25 - T - beta = (2.033e-2 + 1.266e-4 * delta + 2.464e-6 * delta**2 - S * - (1.849e-5 - 2.551e-7 * delta + 2.551e-8 * delta**2)) + beta = ( + 2.033e-2 + 1.266e-4 * delta + 2.464e-6 * delta**2 - S * (1.849e-5 - 2.551e-7 * delta + 2.551e-8 * delta**2) + ) sig_25s = S * (0.182521 - 1.46192e-3 * S + 2.09324e-5 * S**2 - 1.28205e-7 * S**3) sig = sig_25s * np.exp(-delta * beta) @@ -506,18 +524,18 @@ def create_water(G, T=25, S=0): """ eri, er, tau, sig = calculate_water_properties(T, S) - - m = DispersiveMaterial(len(G.materials), 'water') + + m = DispersiveMaterial(len(G.materials), "water") m.averagable = False - m.type = 'builtin, debye' + m.type = "builtin, debye" m.poles = 1 m.er = eri m.se = sig m.deltaer.append(er - eri) m.tau.append(tau) G.materials.append(m) - if config.get_model_config().materials['maxpoles'] == 0: - config.get_model_config().materials['maxpoles'] = 1 + if config.get_model_config().materials["maxpoles"] == 0: + config.get_model_config().materials["maxpoles"] = 1 def create_grass(G): @@ -533,17 +551,17 @@ def create_grass(G): tau = 1.0793e-11 sig = 0 - m = DispersiveMaterial(len(G.materials), 'grass') + m = DispersiveMaterial(len(G.materials), "grass") m.averagable = False - m.type = 'builtin, debye' + m.type = "builtin, debye" m.poles = 1 m.er = eri m.se = sig m.deltaer.append(er - eri) m.tau.append(tau) G.materials.append(m) - if config.get_model_config().materials['maxpoles'] == 0: - config.get_model_config().materials['maxpoles'] = 1 + if config.get_model_config().materials["maxpoles"] == 0: + config.get_model_config().materials["maxpoles"] = 1 def process_materials(G): @@ -558,13 +576,37 @@ def process_materials(G): print a table. """ - if config.get_model_config().materials['maxpoles'] == 0: - materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]', - '\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']] + if config.get_model_config().materials["maxpoles"] == 0: + materialsdata = [ + [ + "\nID", + "\nName", + "\nType", + "\neps_r", + "sigma\n[S/m]", + "\nmu_r", + "sigma*\n[Ohm/m]", + "Dielectric\nsmoothable", + ] + ] else: - materialsdata = [['\nID', '\nName', '\nType', '\neps_r', 'sigma\n[S/m]', - 'Delta\neps_r', 'tau\n[s]', 'omega\n[Hz]', 'delta\n[Hz]', - 'gamma\n[Hz]', '\nmu_r', 'sigma*\n[Ohm/m]', 'Dielectric\nsmoothable']] + materialsdata = [ + [ + "\nID", + "\nName", + "\nType", + "\neps_r", + "sigma\n[S/m]", + "Delta\neps_r", + "tau\n[s]", + "omega\n[Hz]", + "delta\n[Hz]", + "gamma\n[Hz]", + "\nmu_r", + "sigma*\n[Ohm/m]", + "Dielectric\nsmoothable", + ] + ] for material in G.materials: # Calculate update coefficients for specific material @@ -576,39 +618,44 @@ def process_materials(G): G.updatecoeffsH[material.numID, :] = material.DA, material.DBx, material.DBy, material.DBz, material.srcm # Add update coefficients to overall storage for dispersive materials - if hasattr(material, 'poles'): + if hasattr(material, "poles"): z = 0 - for pole in range(config.get_model_config().materials['maxpoles']): - G.updatecoeffsdispersive[material.numID, z:z + 3] = (config.sim_config.em_consts['e0'] * - material.eqt2[pole], material.eqt[pole], material.zt[pole]) + for pole in range(config.get_model_config().materials["maxpoles"]): + G.updatecoeffsdispersive[material.numID, z : z + 3] = ( + config.sim_config.em_consts["e0"] * material.eqt2[pole], + material.eqt[pole], + material.zt[pole], + ) z += 3 # Construct information on material properties for printing table - materialtext = [str(material.numID), - material.ID[:50] if len(material.ID) > 50 else material.ID, - material.type, - f'{material.er:g}', - f'{material.se:g}'] - if config.get_model_config().materials['maxpoles'] > 0: - if 'debye' in material.type: - materialtext.append('\n'.join('{:g}'.format(deltaer) for deltaer in material.deltaer)) - materialtext.append('\n'.join('{:g}'.format(tau) for tau in material.tau)) - materialtext.extend(['', '', '']) - elif 'lorentz' in material.type: - materialtext.append(', '.join('{:g}'.format(deltaer) for deltaer in material.deltaer)) - materialtext.append('') - materialtext.append(', '.join('{:g}'.format(tau) for tau in material.tau)) - materialtext.append(', '.join('{:g}'.format(alpha) for alpha in material.alpha)) - materialtext.append('') - elif 'drude' in material.type: - materialtext.extend(['', '']) - materialtext.append(', '.join('{:g}'.format(tau) for tau in material.tau)) - materialtext.append('') - materialtext.append(', '.join('{:g}'.format(alpha) for alpha in material.alpha)) + materialtext = [ + str(material.numID), + material.ID[:50] if len(material.ID) > 50 else material.ID, + material.type, + f"{material.er:g}", + f"{material.se:g}", + ] + if config.get_model_config().materials["maxpoles"] > 0: + if "debye" in material.type: + materialtext.append("\n".join("{:g}".format(deltaer) for deltaer in material.deltaer)) + materialtext.append("\n".join("{:g}".format(tau) for tau in material.tau)) + materialtext.extend(["", "", ""]) + elif "lorentz" in material.type: + materialtext.append(", ".join("{:g}".format(deltaer) for deltaer in material.deltaer)) + materialtext.append("") + materialtext.append(", ".join("{:g}".format(tau) for tau in material.tau)) + materialtext.append(", ".join("{:g}".format(alpha) for alpha in material.alpha)) + materialtext.append("") + elif "drude" in material.type: + materialtext.extend(["", ""]) + materialtext.append(", ".join("{:g}".format(tau) for tau in material.tau)) + materialtext.append("") + materialtext.append(", ".join("{:g}".format(alpha) for alpha in material.alpha)) else: - materialtext.extend(['', '', '', '', '']) + materialtext.extend(["", "", "", "", ""]) - materialtext.extend((f'{material.mr:g}', f'{material.sm:g}', material.averagable)) + materialtext.extend((f"{material.mr:g}", f"{material.sm:g}", material.averagable)) materialsdata.append(materialtext) - return materialsdata \ No newline at end of file + return materialsdata diff --git a/gprMax/model_build_run.py b/gprMax/model_build_run.py index ce6ea112..3f621117 100644 --- a/gprMax/model_build_run.py +++ b/gprMax/model_build_run.py @@ -33,8 +33,7 @@ from tqdm import tqdm import gprMax.config as config -from .cython.yee_cell_build import (build_electric_components, - build_magnetic_components) +from .cython.yee_cell_build import build_electric_components, build_magnetic_components from .fields_outputs import write_hdf5_outputfile from .geometry_outputs import save_geometry_views from .grid import dispersion_analysis @@ -43,8 +42,7 @@ from .materials import process_materials from .pml import CFS, build_pml, print_pml_info from .scene import Scene from .snapshots import save_snapshots -from .utilities.host_info import (mem_check_build_all, mem_check_run_all, - set_omp_threads) +from .utilities.host_info import mem_check_build_all, mem_check_run_all, set_omp_threads from .utilities.utilities import get_terminal_width logger = logging.getLogger(__name__) @@ -75,19 +73,21 @@ class ModelBuildRun: # Normal model reading/building process; bypassed if geometry information to be reused self.reuse_geometry() if config.get_model_config().reuse_geometry else self.build_geometry() - logger.info(f'\nOutput directory: {config.get_model_config().output_file_path.parent.resolve()}') + logger.info(f"\nOutput directory: {config.get_model_config().output_file_path.parent.resolve()}") # Adjust position of simple sources and receivers if required if G.srcsteps[0] != 0 or G.srcsteps[1] != 0 or G.srcsteps[2] != 0: for source in itertools.chain(G.hertziandipoles, G.magneticdipoles): if config.model_num == 0: - if (source.xcoord + G.srcsteps[0] * config.sim_config.model_end < 0 or - source.xcoord + G.srcsteps[0] * config.sim_config.model_end > G.nx or - source.ycoord + G.srcsteps[1] * config.sim_config.model_end < 0 or - source.ycoord + G.srcsteps[1] * config.sim_config.model_end > G.ny or - source.zcoord + G.srcsteps[2] * config.sim_config.model_end < 0 or - source.zcoord + G.srcsteps[2] * config.sim_config.model_end > G.nz): - logger.exception('Source(s) will be stepped to a position outside the domain.') + if ( + source.xcoord + G.srcsteps[0] * config.sim_config.model_end < 0 + or source.xcoord + G.srcsteps[0] * config.sim_config.model_end > G.nx + or source.ycoord + G.srcsteps[1] * config.sim_config.model_end < 0 + or source.ycoord + G.srcsteps[1] * config.sim_config.model_end > G.ny + or source.zcoord + G.srcsteps[2] * config.sim_config.model_end < 0 + or source.zcoord + G.srcsteps[2] * config.sim_config.model_end > G.nz + ): + logger.exception("Source(s) will be stepped to a position outside the domain.") raise ValueError source.xcoord = source.xcoordorigin + config.model_num * G.srcsteps[0] source.ycoord = source.ycoordorigin + config.model_num * G.srcsteps[1] @@ -95,13 +95,15 @@ class ModelBuildRun: if G.rxsteps[0] != 0 or G.rxsteps[1] != 0 or G.rxsteps[2] != 0: for receiver in G.rxs: if config.model_num == 0: - if (receiver.xcoord + G.rxsteps[0] * config.sim_config.model_end < 0 or - receiver.xcoord + G.rxsteps[0] * config.sim_config.model_end > G.nx or - receiver.ycoord + G.rxsteps[1] * config.sim_config.model_end < 0 or - receiver.ycoord + G.rxsteps[1] * config.sim_config.model_end > G.ny or - receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end < 0 or - receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end > G.nz): - logger.exception('Receiver(s) will be stepped to a position outside the domain.') + if ( + receiver.xcoord + G.rxsteps[0] * config.sim_config.model_end < 0 + or receiver.xcoord + G.rxsteps[0] * config.sim_config.model_end > G.nx + or receiver.ycoord + G.rxsteps[1] * config.sim_config.model_end < 0 + or receiver.ycoord + G.rxsteps[1] * config.sim_config.model_end > G.ny + or receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end < 0 + or receiver.zcoord + G.rxsteps[2] * config.sim_config.model_end > G.nz + ): + logger.exception("Receiver(s) will be stepped to a position outside the domain.") raise ValueError receiver.xcoord = receiver.xcoordorigin + config.model_num * G.rxsteps[0] receiver.ycoord = receiver.ycoordorigin + config.model_num * G.rxsteps[1] @@ -109,29 +111,34 @@ class ModelBuildRun: # Write files for any geometry views and geometry object outputs gvs = G.geometryviews + [gv for sg in G.subgrids for gv in sg.geometryviews] - if (not gvs and not G.geometryobjectswrite and config.sim_config.args.geometry_only): - logger.exception('\nNo geometry views or geometry objects found.') + if not gvs and not G.geometryobjectswrite and config.sim_config.args.geometry_only: + logger.exception("\nNo geometry views or geometry objects found.") raise ValueError save_geometry_views(gvs) if G.geometryobjectswrite: - logger.info('') + logger.info("") for i, go in enumerate(G.geometryobjectswrite): - pbar = tqdm(total=go.datawritesize, unit='byte', unit_scale=True, - desc=f'Writing geometry object file {i + 1}/{len(G.geometryobjectswrite)}, ' + - f'{go.filename_hdf5.name}', - ncols=get_terminal_width() - 1, file=sys.stdout, - disable=not config.sim_config.general['progressbars']) + pbar = tqdm( + total=go.datawritesize, + unit="byte", + unit_scale=True, + desc=f"Writing geometry object file {i + 1}/{len(G.geometryobjectswrite)}, " + + f"{go.filename_hdf5.name}", + ncols=get_terminal_width() - 1, + file=sys.stdout, + disable=not config.sim_config.general["progressbars"], + ) go.write_hdf5(G, pbar) pbar.close() - logger.info('') + logger.info("") def build_geometry(self): G = self.G logger.info(config.get_model_config().inputfilestr) - # Build objects in the scene and check memory for building + # Build objects in the scene and check memory for building self.build_scene() # Print info on any subgrids @@ -143,15 +150,17 @@ class ModelBuildRun: # Check for dispersive materials (and specific type) for grid in grids: - if config.get_model_config().materials['maxpoles'] != 0: - config.get_model_config().materials['drudelorentz'] = any([m for m in grid.materials if 'drude' in m.type or 'lorentz' in m.type]) + if config.get_model_config().materials["maxpoles"] != 0: + config.get_model_config().materials["drudelorentz"] = any( + [m for m in grid.materials if "drude" in m.type or "lorentz" in m.type] + ) # Set data type if any dispersive materials (must be done before memory checks) - if config.get_model_config().materials['maxpoles'] != 0: + if config.get_model_config().materials["maxpoles"] != 0: config.get_model_config().set_dispersive_material_types() - # Check memory requirements to build model/scene (different to memory - # requirements to run model when FractalVolumes/FractalSurfaces are + # Check memory requirements to build model/scene (different to memory + # requirements to run model when FractalVolumes/FractalSurfaces are # used as these can require significant additional memory) total_mem_build, mem_strs_build = mem_check_build_all(grids) @@ -159,22 +168,26 @@ class ModelBuildRun: total_mem_run, mem_strs_run = mem_check_run_all(grids) if total_mem_build > total_mem_run: - logger.info(f'\nMemory required (estimated): {" + ".join(mem_strs_build)} + ' - f'~{humanize.naturalsize(config.get_model_config().mem_overhead)} ' - f'overhead = {humanize.naturalsize(total_mem_build)}') + logger.info( + f'\nMemory required (estimated): {" + ".join(mem_strs_build)} + ' + f"~{humanize.naturalsize(config.get_model_config().mem_overhead)} " + f"overhead = {humanize.naturalsize(total_mem_build)}" + ) else: - logger.info(f'\nMemory required (estimated): {" + ".join(mem_strs_run)} + ' - f'~{humanize.naturalsize(config.get_model_config().mem_overhead)} ' - f'overhead = {humanize.naturalsize(total_mem_run)}') + logger.info( + f'\nMemory required (estimated): {" + ".join(mem_strs_run)} + ' + f"~{humanize.naturalsize(config.get_model_config().mem_overhead)} " + f"overhead = {humanize.naturalsize(total_mem_run)}" + ) # Build grids gridbuilders = [GridBuilder(grid) for grid in grids] for gb in gridbuilders: # Set default CFS parameter for PMLs if not user provided - if not gb.grid.pmls['cfs']: - gb.grid.pmls['cfs'] = [CFS()] + if not gb.grid.pmls["cfs"]: + gb.grid.pmls["cfs"] = [CFS()] logger.info(print_pml_info(gb.grid)) - if not all(value == 0 for value in gb.grid.pmls['thickness'].values()): + if not all(value == 0 for value in gb.grid.pmls["thickness"].values()): gb.build_pmls() if gb.grid.averagevolumeobjects: gb.build_components() @@ -182,50 +195,62 @@ class ModelBuildRun: gb.update_voltage_source_materials() gb.grid.initialise_field_arrays() gb.grid.initialise_std_update_coeff_arrays() - if config.get_model_config().materials['maxpoles'] > 0: + if config.get_model_config().materials["maxpoles"] > 0: gb.grid.initialise_dispersive_arrays() gb.grid.initialise_dispersive_update_coeff_array() gb.build_materials() # Check to see if numerical dispersion might be a problem results = dispersion_analysis(gb.grid) - if results['error']: - logger.warning(f"\nNumerical dispersion analysis [{gb.grid.name}] " - f"not carried out as {results['error']}") - elif results['N'] < config.get_model_config().numdispersion['mingridsampling']: - logger.exception(f"\nNon-physical wave propagation in [{gb.grid.name}] " - f"detected. Material '{results['material'].ID}' " - f"has wavelength sampled by {results['N']} cells, " - f"less than required minimum for physical wave " - f"propagation. Maximum significant frequency " - f"estimated as {results['maxfreq']:g}Hz") + if results["error"]: + logger.warning( + f"\nNumerical dispersion analysis [{gb.grid.name}] " f"not carried out as {results['error']}" + ) + elif results["N"] < config.get_model_config().numdispersion["mingridsampling"]: + logger.exception( + f"\nNon-physical wave propagation in [{gb.grid.name}] " + f"detected. Material '{results['material'].ID}' " + f"has wavelength sampled by {results['N']} cells, " + f"less than required minimum for physical wave " + f"propagation. Maximum significant frequency " + f"estimated as {results['maxfreq']:g}Hz" + ) raise ValueError - elif (results['deltavp'] and np.abs(results['deltavp']) > - config.get_model_config().numdispersion['maxnumericaldisp']): - logger.warning(f"\n[{gb.grid.name}] has potentially significant " - f"numerical dispersion. Estimated largest physical " - f"phase-velocity error is {results['deltavp']:.2f}% " - f"in material '{results['material'].ID}' whose " - f"wavelength sampled by {results['N']} cells. " - f"Maximum significant frequency estimated as " - f"{results['maxfreq']:g}Hz") - elif results['deltavp']: - logger.info(f"\nNumerical dispersion analysis [{gb.grid.name}]: " - f"estimated largest physical phase-velocity error is " - f"{results['deltavp']:.2f}% in material '{results['material'].ID}' " - f"whose wavelength sampled by {results['N']} cells. " - f"Maximum significant frequency estimated as " - f"{results['maxfreq']:g}Hz") + elif ( + results["deltavp"] + and np.abs(results["deltavp"]) > config.get_model_config().numdispersion["maxnumericaldisp"] + ): + logger.warning( + f"\n[{gb.grid.name}] has potentially significant " + f"numerical dispersion. Estimated largest physical " + f"phase-velocity error is {results['deltavp']:.2f}% " + f"in material '{results['material'].ID}' whose " + f"wavelength sampled by {results['N']} cells. " + f"Maximum significant frequency estimated as " + f"{results['maxfreq']:g}Hz" + ) + elif results["deltavp"]: + logger.info( + f"\nNumerical dispersion analysis [{gb.grid.name}]: " + f"estimated largest physical phase-velocity error is " + f"{results['deltavp']:.2f}% in material '{results['material'].ID}' " + f"whose wavelength sampled by {results['N']} cells. " + f"Maximum significant frequency estimated as " + f"{results['maxfreq']:g}Hz" + ) def reuse_geometry(self): - s = (f'\n--- Model {config.get_model_config().appendmodelnumber}/{config.sim_config.model_end}, ' - f'input file (not re-processed, i.e. geometry fixed): ' - f'{config.sim_config.input_file_path}') - config.get_model_config().inputfilestr = (Fore.GREEN + f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n" + - Style.RESET_ALL) + s = ( + f"\n--- Model {config.get_model_config().appendmodelnumber}/{config.sim_config.model_end}, " + f"input file (not re-processed, i.e. geometry fixed): " + f"{config.sim_config.input_file_path}" + ) + config.get_model_config().inputfilestr = ( + Fore.GREEN + f"{s} {'-' * (get_terminal_width() - 1 - len(s))}\n" + Style.RESET_ALL + ) logger.basic(config.get_model_config().inputfilestr) for grid in [self.G] + self.G.subgrids: - grid.iteration = 0 # Reset current iteration number + grid.iteration = 0 # Reset current iteration number grid.reset_fields() def build_scene(self): @@ -244,8 +269,8 @@ class ModelBuildRun: return scene def write_output_data(self): - """Writes output data, i.e. field data for receivers and snapshots to - file(s). + """Writes output data, i.e. field data for receivers and snapshots to + file(s). """ write_hdf5_outputfile(config.get_model_config().output_file_path_ext, self.G) @@ -262,32 +287,42 @@ class ModelBuildRun: """ # Print information about and check OpenMP threads - if config.sim_config.general['solver'] == 'cpu': - logger.basic(f"\nModel {config.model_num + 1}/{config.sim_config.model_end} " - f"on {config.sim_config.hostinfo['hostname']} " - f"with OpenMP backend using {config.get_model_config().ompthreads} thread(s)") - if config.get_model_config().ompthreads > config.sim_config.hostinfo['physicalcores']: - logger.warning(f"You have specified more threads ({config.get_model_config().ompthreads}) " - f"than available physical CPU cores ({config.sim_config.hostinfo['physicalcores']}). " - f"This may lead to degraded performance.") - elif config.sim_config.general['solver'] in ['cuda', 'opencl']: - if config.sim_config.general['solver'] == 'opencl': - solvername = 'OpenCL' - platformname = ' on ' + ' '.join(config.get_model_config().device['dev'].platform.name.split()) - devicename = ' '.join(config.get_model_config().device['dev'].name.split()) + if config.sim_config.general["solver"] == "cpu": + logger.basic( + f"\nModel {config.model_num + 1}/{config.sim_config.model_end} " + f"on {config.sim_config.hostinfo['hostname']} " + f"with OpenMP backend using {config.get_model_config().ompthreads} thread(s)" + ) + if config.get_model_config().ompthreads > config.sim_config.hostinfo["physicalcores"]: + logger.warning( + f"You have specified more threads ({config.get_model_config().ompthreads}) " + f"than available physical CPU cores ({config.sim_config.hostinfo['physicalcores']}). " + f"This may lead to degraded performance." + ) + elif config.sim_config.general["solver"] in ["cuda", "opencl"]: + if config.sim_config.general["solver"] == "opencl": + solvername = "OpenCL" + platformname = " on " + " ".join(config.get_model_config().device["dev"].platform.name.split()) + devicename = " ".join(config.get_model_config().device["dev"].name.split()) else: - solvername = 'CUDA' - platformname = '' - devicename = ' '.join(config.get_model_config().device['dev'].name().split()) - logger.basic(f"\nModel {config.model_num + 1}/{config.sim_config.model_end} " - f"on {config.sim_config.hostinfo['hostname']} " - f"with {solvername} backend using {devicename}{platformname}") + solvername = "CUDA" + platformname = "" + devicename = " ".join(config.get_model_config().device["dev"].name().split()) + logger.basic( + f"\nModel {config.model_num + 1}/{config.sim_config.model_end} " + f"on {config.sim_config.hostinfo['hostname']} " + f"with {solvername} backend using {devicename}{platformname}" + ) # Prepare iterator - if config.sim_config.general['progressbars']: - iterator = tqdm(range(self.G.iterations), desc='|--->', - ncols=get_terminal_width() - 1, file=sys.stdout, - disable=not config.sim_config.general['progressbars']) + if config.sim_config.general["progressbars"]: + iterator = tqdm( + range(self.G.iterations), + desc="|--->", + ncols=get_terminal_width() - 1, + file=sys.stdout, + disable=not config.sim_config.general["progressbars"], + ) else: iterator = range(self.G.iterations) @@ -299,12 +334,16 @@ class ModelBuildRun: # Print information about memory usage and solving time for a model # Add a string on GPU memory usage if applicable - mem_str = f' host + ~{humanize.naturalsize(solver.memused)} GPU' if config.sim_config.general['solver'] == 'cuda' else '' + mem_str = ( + f" host + ~{humanize.naturalsize(solver.memused)} GPU" + if config.sim_config.general["solver"] == "cuda" + else "" + ) - logger.info(f'\nMemory used (estimated): ' + - f'~{humanize.naturalsize(self.p.memory_full_info().uss)}{mem_str}') - logger.info(f"Time taken: " + - f"{humanize.precisedelta(datetime.timedelta(seconds=solver.solvetime), format='%0.4f')}") + logger.info(f"\nMemory used (estimated): " + f"~{humanize.naturalsize(self.p.memory_full_info().uss)}{mem_str}") + logger.info( + f"Time taken: " + f"{humanize.precisedelta(datetime.timedelta(seconds=solver.solvetime), format='%0.4f')}" + ) class GridBuilder: @@ -312,11 +351,14 @@ class GridBuilder: self.grid = grid def build_pmls(self): - pbar = tqdm(total=sum(1 for value in self.grid.pmls['thickness'].values() if value > 0), - desc=f'Building PML boundaries [{self.grid.name}]', - ncols=get_terminal_width() - 1, file=sys.stdout, - disable=not config.sim_config.general['progressbars']) - for pml_id, thickness in self.grid.pmls['thickness'].items(): + pbar = tqdm( + total=sum(1 for value in self.grid.pmls["thickness"].values() if value > 0), + desc=f"Building PML boundaries [{self.grid.name}]", + ncols=get_terminal_width() - 1, + file=sys.stdout, + disable=not config.sim_config.general["progressbars"], + ) + for pml_id, thickness in self.grid.pmls["thickness"].items(): if thickness > 0: build_pml(self.grid, pml_id, thickness) pbar.update() @@ -325,10 +367,14 @@ class GridBuilder: def build_components(self): # Build the model, i.e. set the material properties (ID) for every edge # of every Yee cell - logger.info('') - pbar = tqdm(total=2, desc=f'Building Yee cells [{self.grid.name}]', - ncols=get_terminal_width() - 1, file=sys.stdout, - disable=not config.sim_config.general['progressbars']) + logger.info("") + pbar = tqdm( + total=2, + desc=f"Building Yee cells [{self.grid.name}]", + ncols=get_terminal_width() - 1, + file=sys.stdout, + disable=not config.sim_config.general["progressbars"], + ) build_electric_components(self.grid.solid, self.grid.rigidE, self.grid.ID, self.grid) pbar.update() build_magnetic_components(self.grid.solid, self.grid.rigidH, self.grid.ID, self.grid) @@ -336,11 +382,11 @@ class GridBuilder: pbar.close() def tm_grid_update(self): - if config.get_model_config().mode == '2D TMx': + if config.get_model_config().mode == "2D TMx": self.grid.tmx() - elif config.get_model_config().mode == '2D TMy': + elif config.get_model_config().mode == "2D TMy": self.grid.tmy() - elif config.get_model_config().mode == '2D TMz': + elif config.get_model_config().mode == "2D TMz": self.grid.tmz() def update_voltage_source_materials(self): @@ -355,7 +401,7 @@ class GridBuilder: materialsdata = process_materials(self.grid) materialstable = SingleTable(materialsdata) materialstable.outer_border = False - materialstable.justify_columns[0] = 'right' + materialstable.justify_columns[0] = "right" - logger.info(f'\nMaterials [{self.grid.name}]:') + logger.info(f"\nMaterials [{self.grid.name}]:") logger.info(materialstable.table) diff --git a/gprMax/mpi.py b/gprMax/mpi.py index 2a0dfbff..61b46ead 100644 --- a/gprMax/mpi.py +++ b/gprMax/mpi.py @@ -37,7 +37,7 @@ EXIT Send by master to worker to initiate worker shutdown and then send back to master to signal shutdown has completed. """ -Tags = IntEnum('Tags', 'READY START DONE EXIT') +Tags = IntEnum("Tags", "READY START DONE EXIT") class MPIExecutor(object): @@ -122,11 +122,11 @@ class MPIExecutor(object): def __init__(self, func, master=0, comm=None): """Initializes a new executor instance. - + Attributes: - func: callable worker function. Jobs will be passed as keyword - arguments, so `func` must support this. This is usually the - case, but can be a problem when builtin functions are used, + func: callable worker function. Jobs will be passed as keyword + arguments, so `func` must support this. This is usually the + case, but can be a problem when builtin functions are used, e.g. `abs()`. master: int of the rank of the master. Must be in `comm`. All other ranks in `comm` will be treated as workers. @@ -136,22 +136,22 @@ class MPIExecutor(object): if comm is None: self.comm = MPI.COMM_WORLD elif not comm.Is_intra(): - raise TypeError('MPI.Intracomm expected') + raise TypeError("MPI.Intracomm expected") else: self.comm = comm self.rank = self.comm.rank self.size = self.comm.size if self.size < 2: - raise RuntimeError('MPIExecutor must run with at least 2 processes') + raise RuntimeError("MPIExecutor must run with at least 2 processes") self._up = False master = int(master) if master < 0: - raise ValueError('Master rank must be non-negative') + raise ValueError("Master rank must be non-negative") elif master >= self.size: - raise ValueError('Master not in comm') + raise ValueError("Master not in comm") else: self.master = master @@ -159,17 +159,17 @@ class MPIExecutor(object): self.workers = tuple(set(range(self.size)) - {self.master}) # the worker function if not callable(func): - raise TypeError('Func must be a callable') + raise TypeError("Func must be a callable") self.func = func # holds the state of workers on the master self.busy = [False] * len(self.workers) if self.is_master(): - logger.basic(f'\n({self.comm.name}) - Master: {self.master}, Workers: {self.workers}') + logger.basic(f"\n({self.comm.name}) - Master: {self.master}, Workers: {self.workers}") def __enter__(self): - """Context manager enter. Only the master returns an executor, all other - ranks return None. + """Context manager enter. Only the master returns an executor, all other + ranks return None. """ self.start() if self.is_master(): @@ -188,10 +188,10 @@ class MPIExecutor(object): return True def is_idle(self): - """Returns a bool indicating whether the executor is idle. The executor - is considered to be not idle if *any* worker process is busy with a - job. That means, it is idle only if *all* workers are idle. - Note: This member must not be called on a worker. + """Returns a bool indicating whether the executor is idle. The executor + is considered to be not idle if *any* worker process is busy with a + job. That means, it is idle only if *all* workers are idle. + Note: This member must not be called on a worker. """ assert self.is_master() return not any(self.busy) @@ -205,16 +205,16 @@ class MPIExecutor(object): return not self.is_master() def start(self): - """Starts up workers. A check is performed on the master whether the - executor has already been terminated, in which case a RuntimeError - is raised on the master. + """Starts up workers. A check is performed on the master whether the + executor has already been terminated, in which case a RuntimeError + is raised on the master. """ if self.is_master(): if self._up: - raise RuntimeError('Start has already been called') + raise RuntimeError("Start has already been called") self._up = True - logger.debug(f'({self.comm.name}) - Starting up MPIExecutor master/workers...') + logger.debug(f"({self.comm.name}) - Starting up MPIExecutor master/workers...") if self.is_worker(): self.__wait() @@ -222,12 +222,12 @@ class MPIExecutor(object): """Joins the workers.""" if not self.is_master(): return - logger.debug(f'({self.comm.name}) - Terminating. Sending sentinel to all workers.') + logger.debug(f"({self.comm.name}) - Terminating. Sending sentinel to all workers.") # Send sentinel to all workers for worker in self.workers: self.comm.send(None, dest=worker, tag=Tags.EXIT) - logger.debug(f'({self.comm.name}) - Waiting for all workers to terminate.') + logger.debug(f"({self.comm.name}) - Waiting for all workers to terminate.") down = [False] * len(self.workers) while True: @@ -239,28 +239,28 @@ class MPIExecutor(object): break self._up = False - logger.debug(f'({self.comm.name}) - All workers terminated.') + logger.debug(f"({self.comm.name}) - All workers terminated.") def submit(self, jobs, sleep=0.0): """Submits a list of jobs to the workers and returns the results. - + Args: - jobs: list of keyword argument dicts. Each dict describes a job and + jobs: list of keyword argument dicts. Each dict describes a job and will be unpacked and supplied to the work function. - sleep: float of number of seconds the master will sleep for when - trying to find an idle worker. The default value is 0.0, + sleep: float of number of seconds the master will sleep for when + trying to find an idle worker. The default value is 0.0, which means the master will not sleep at all. - + Returns: - results: list of results, i.e. the return values of the work - function, received from the workers. The order of + results: list of results, i.e. the return values of the work + function, received from the workers. The order of results is identical to the order of `jobs`. """ if not self._up: - raise RuntimeError('Cannot run jobs without a call to start()') + raise RuntimeError("Cannot run jobs without a call to start()") - logger.basic(f'Running {len(jobs):d} jobs.') - assert self.is_master(), 'run() must not be called on a worker process' + logger.basic(f"Running {len(jobs):d} jobs.") + assert self.is_master(), "run() must not be called on a worker process" my_jobs = jobs.copy() num_jobs = len(my_jobs) @@ -269,7 +269,7 @@ class MPIExecutor(object): for i, worker in enumerate(self.workers): if self.comm.Iprobe(source=worker, tag=Tags.DONE): job_idx, result = self.comm.recv(source=worker, tag=Tags.DONE) - logger.debug(f'({self.comm.name}) - Received finished job {job_idx} from worker {worker:d}.') + logger.debug(f"({self.comm.name}) - Received finished job {job_idx} from worker {worker:d}.") results[job_idx] = result self.busy[i] = False elif self.comm.Iprobe(source=worker, tag=Tags.READY): @@ -277,49 +277,49 @@ class MPIExecutor(object): self.comm.recv(source=worker, tag=Tags.READY) self.busy[i] = True job_idx = num_jobs - len(my_jobs) - logger.debug(f'({self.comm.name}) - Sending job {job_idx} to worker {worker:d}.') + logger.debug(f"({self.comm.name}) - Sending job {job_idx} to worker {worker:d}.") self.comm.send((job_idx, my_jobs.pop(0)), dest=worker, tag=Tags.START) elif self.comm.Iprobe(source=worker, tag=Tags.EXIT): - logger.debug(f'({self.comm.name}) - Worker on rank {worker:d} has terminated.') + logger.debug(f"({self.comm.name}) - Worker on rank {worker:d} has terminated.") self.comm.recv(source=worker, tag=Tags.EXIT) self.busy[i] = False time.sleep(sleep) - logger.debug(f'({self.comm.name}) - Finished all jobs.') + logger.debug(f"({self.comm.name}) - Finished all jobs.") return results def __wait(self): - """The worker main loop. The worker will enter the loop after `start()` - has been called and stay here until it receives the sentinel, - e.g. by calling `join()` on the master. In the mean time, the worker - is accepting work. + """The worker main loop. The worker will enter the loop after `start()` + has been called and stay here until it receives the sentinel, + e.g. by calling `join()` on the master. In the mean time, the worker + is accepting work. """ assert self.is_worker() status = MPI.Status() - logger.debug(f'({self.comm.name}) - Starting up worker.') + logger.debug(f"({self.comm.name}) - Starting up worker.") while True: self.comm.send(None, dest=self.master, tag=Tags.READY) - logger.debug(f'({self.comm.name}) - Worker on rank {self.rank} waiting for job.') + logger.debug(f"({self.comm.name}) - Worker on rank {self.rank} waiting for job.") data = self.comm.recv(source=self.master, tag=MPI.ANY_TAG, status=status) tag = status.tag if tag == Tags.START: job_idx, work = data - logger.debug(f'({self.comm.name}) - Received job {job_idx} (work={work}).') + logger.debug(f"({self.comm.name}) - Received job {job_idx} (work={work}).") result = self.__guarded_work(work) - logger.debug(f'({self.comm.name}) - Finished job. Sending results to master.') + logger.debug(f"({self.comm.name}) - Finished job. Sending results to master.") self.comm.send((job_idx, result), dest=self.master, tag=Tags.DONE) elif tag == Tags.EXIT: - logger.debug(f'({self.comm.name}) - Received sentinel from master.') + logger.debug(f"({self.comm.name}) - Received sentinel from master.") break - logger.debug(f'({self.comm.name}) - Terminating worker.') + logger.debug(f"({self.comm.name}) - Terminating worker.") self.comm.send(None, dest=self.master, tag=Tags.EXIT) def __guarded_work(self, work): @@ -327,7 +327,7 @@ class MPIExecutor(object): N.B. All exceptions that occur in the work function `func` are caught and logged. The worker returns `None` to the master in that case instead of the actual result. - + Args: work: dict ofeyword arguments that are unpacked and given to the work function. diff --git a/gprMax/pml.py b/gprMax/pml.py index ffb640ca..fba68ddd 100644 --- a/gprMax/pml.py +++ b/gprMax/pml.py @@ -29,21 +29,28 @@ class CFSParameter: """Individual CFS parameter (e.g. alpha, kappa, or sigma).""" # Allowable scaling profiles and directions - scalingprofiles = {'constant': 0, 'linear': 1, 'quadratic': 2, 'cubic': 3, - 'quartic': 4, 'quintic': 5, 'sextic': 6, 'septic': 7, - 'octic': 8} - scalingdirections = ['forward', 'reverse'] + scalingprofiles = { + "constant": 0, + "linear": 1, + "quadratic": 2, + "cubic": 3, + "quartic": 4, + "quintic": 5, + "sextic": 6, + "septic": 7, + "octic": 8, + } + scalingdirections = ["forward", "reverse"] - def __init__(self, ID=None, scaling='polynomial', scalingprofile=None, - scalingdirection='forward', min=0, max=0): + def __init__(self, ID=None, scaling="polynomial", scalingprofile=None, scalingdirection="forward", min=0, max=0): """ Args: - ID: string identifier for CFS parameter, can be: 'alpha', 'kappa' or + ID: string identifier for CFS parameter, can be: 'alpha', 'kappa' or 'sigma'. scaling: string for type of scaling, can be: 'polynomial'. - scalingprofile: string for type of scaling profile from + scalingprofile: string for type of scaling profile from scalingprofiles. - scalingdirection: string for direction of scaling profile from + scalingdirection: string for direction of scaling profile from scalingdirections. min: float for minimum value for parameter. max: float for maximum value for parameter. @@ -68,9 +75,9 @@ class CFS: sigma: CFSParameter sigma parameter for CFS. """ - self.alpha = CFSParameter(ID='alpha', scalingprofile='constant') - self.kappa = CFSParameter(ID='kappa', scalingprofile='constant', min=1, max=1) - self.sigma = CFSParameter(ID='sigma', scalingprofile='quartic', min=0, max=None) + self.alpha = CFSParameter(ID="alpha", scalingprofile="constant") + self.kappa = CFSParameter(ID="kappa", scalingprofile="constant", min=1, max=1) + self.sigma = CFSParameter(ID="sigma", scalingprofile="quartic", min=0, max=None) def calculate_sigmamax(self, d, er, mr, G): """Calculates an optimum value for sigma max based on underlying @@ -85,7 +92,7 @@ class CFS: # Calculation of the maximum value of sigma from http://dx.doi.org/10.1109/8.546249 m = CFSParameter.scalingprofiles[self.sigma.scalingprofile] - self.sigma.max = (0.8 * (m + 1)) / (config.sim_config.em_consts['z0'] * d * np.sqrt(er * mr)) + self.sigma.max = (0.8 * (m + 1)) / (config.sim_config.em_consts["z0"] * d * np.sqrt(er * mr)) def scaling_polynomial(self, order, Evalues, Hvalues): """Applies the polynomial to be used for the scaling profile for @@ -105,8 +112,7 @@ class CFS: magnetic PML update. """ - tmp = (np.linspace(0, (len(Evalues) - 1) + 0.5, num=2 * len(Evalues)) - / (len(Evalues) - 1)) ** order + tmp = (np.linspace(0, (len(Evalues) - 1) + 0.5, num=2 * len(Evalues)) / (len(Evalues) - 1)) ** order Evalues = tmp[0:-1:2] Hvalues = tmp[1::2] @@ -127,37 +133,37 @@ class CFS: PML update. """ - # Extra cell of thickness added to allow correct scaling of electric and + # Extra cell of thickness added to allow correct scaling of electric and # magnetic values - Evalues = np.zeros(thickness + 1, dtype=config.sim_config.dtypes['float_or_double']) - Hvalues = np.zeros(thickness + 1, dtype=config.sim_config.dtypes['float_or_double']) + Evalues = np.zeros(thickness + 1, dtype=config.sim_config.dtypes["float_or_double"]) + Hvalues = np.zeros(thickness + 1, dtype=config.sim_config.dtypes["float_or_double"]) - if parameter.scalingprofile == 'constant': + if parameter.scalingprofile == "constant": Evalues += parameter.max Hvalues += parameter.max - elif parameter.scaling == 'polynomial': + elif parameter.scaling == "polynomial": Evalues, Hvalues = self.scaling_polynomial( - CFSParameter.scalingprofiles[parameter.scalingprofile], - Evalues, Hvalues) - if parameter.ID == 'alpha': + CFSParameter.scalingprofiles[parameter.scalingprofile], Evalues, Hvalues + ) + if parameter.ID == "alpha": Evalues = Evalues * (self.alpha.max - self.alpha.min) + self.alpha.min Hvalues = Hvalues * (self.alpha.max - self.alpha.min) + self.alpha.min - elif parameter.ID == 'kappa': + elif parameter.ID == "kappa": Evalues = Evalues * (self.kappa.max - self.kappa.min) + self.kappa.min Hvalues = Hvalues * (self.kappa.max - self.kappa.min) + self.kappa.min - elif parameter.ID == 'sigma': + elif parameter.ID == "sigma": Evalues = Evalues * (self.sigma.max - self.sigma.min) + self.sigma.min Hvalues = Hvalues * (self.sigma.max - self.sigma.min) + self.sigma.min - if parameter.scalingdirection == 'reverse': + if parameter.scalingdirection == "reverse": Evalues = Evalues[::-1] Hvalues = Hvalues[::-1] - # Magnetic values must be shifted one element to the left after + # Magnetic values must be shifted one element to the left after # reversal Hvalues = np.roll(Hvalues, -1) - # Extra cell of thickness not required and therefore removed after + # Extra cell of thickness not required and therefore removed after # scaling Evalues = Evalues[:-1] Hvalues = Hvalues[:-1] @@ -171,17 +177,17 @@ class PML: # Available PML formulations: # Higher Order RIPML (HORIPML) see: https://doi.org/10.1109/TAP.2011.2180344 # Multipole RIPML (MRIPML) see: https://doi.org/10.1109/TAP.2018.2823864 - formulations = ['HORIPML', 'MRIPML'] + formulations = ["HORIPML", "MRIPML"] # PML slabs IDs at boundaries of domain. - boundaryIDs = ['x0', 'y0', 'z0', 'xmax', 'ymax', 'zmax'] + boundaryIDs = ["x0", "y0", "z0", "xmax", "ymax", "zmax"] # Indicates direction of increasing absorption - # xminus, yminus, zminus - absorption increases in negative direction of + # xminus, yminus, zminus - absorption increases in negative direction of # x-axis, y-axis, or z-axis - # xplus, yplus, zplus - absorption increases in positive direction of + # xplus, yplus, zplus - absorption increases in positive direction of # x-axis, y-axis, or z-axis - directions = ['xminus', 'yminus', 'zminus', 'xplus', 'yplus', 'zplus'] + directions = ["xminus", "yminus", "zminus", "xplus", "yplus", "zplus"] def __init__(self, G, ID=None, direction=None, xs=0, xf=0, ys=0, yf=0, zs=0, zf=0): """ @@ -206,50 +212,62 @@ class PML: self.nz = zf - zs # Spatial discretisation and thickness - if self.direction[0] == 'x': + if self.direction[0] == "x": self.d = self.G.dx self.thickness = self.nx - elif self.direction[0] == 'y': + elif self.direction[0] == "y": self.d = self.G.dy self.thickness = self.ny - elif self.direction[0] == 'z': + elif self.direction[0] == "z": self.d = self.G.dz self.thickness = self.nz - self.CFS = self.G.pmls['cfs'] + self.CFS = self.G.pmls["cfs"] self.initialise_field_arrays() def initialise_field_arrays(self): """Initialise arrays to store fields in PML.""" - if self.direction[0] == 'x': - self.EPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz), - dtype=config.sim_config.dtypes['float_or_double']) - self.HPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz), - dtype=config.sim_config.dtypes['float_or_double']) - self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - elif self.direction[0] == 'y': - self.EPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny + 1, self.nz), - dtype=config.sim_config.dtypes['float_or_double']) - self.HPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz), - dtype=config.sim_config.dtypes['float_or_double']) - self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - elif self.direction[0] == 'z': - self.EPhi1 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - self.EPhi2 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz + 1), - dtype=config.sim_config.dtypes['float_or_double']) - self.HPhi1 = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz), - dtype=config.sim_config.dtypes['float_or_double']) - self.HPhi2 = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz), - dtype=config.sim_config.dtypes['float_or_double']) + if self.direction[0] == "x": + self.EPhi1 = np.zeros( + (len(self.CFS), self.nx + 1, self.ny, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.EPhi2 = np.zeros( + (len(self.CFS), self.nx + 1, self.ny + 1, self.nz), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.HPhi1 = np.zeros( + (len(self.CFS), self.nx, self.ny + 1, self.nz), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.HPhi2 = np.zeros( + (len(self.CFS), self.nx, self.ny, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"] + ) + elif self.direction[0] == "y": + self.EPhi1 = np.zeros( + (len(self.CFS), self.nx, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.EPhi2 = np.zeros( + (len(self.CFS), self.nx + 1, self.ny + 1, self.nz), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.HPhi1 = np.zeros( + (len(self.CFS), self.nx + 1, self.ny, self.nz), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.HPhi2 = np.zeros( + (len(self.CFS), self.nx, self.ny, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"] + ) + elif self.direction[0] == "z": + self.EPhi1 = np.zeros( + (len(self.CFS), self.nx, self.ny + 1, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.EPhi2 = np.zeros( + (len(self.CFS), self.nx + 1, self.ny, self.nz + 1), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.HPhi1 = np.zeros( + (len(self.CFS), self.nx + 1, self.ny, self.nz), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.HPhi2 = np.zeros( + (len(self.CFS), self.nx, self.ny + 1, self.nz), dtype=config.sim_config.dtypes["float_or_double"] + ) def calculate_update_coeffs(self, er, mr): """Calculates electric and magnetic update coefficients for the PML. @@ -259,22 +277,14 @@ class PML: mr: float of average permeability of underlying material """ - self.ERA = np.zeros((len(self.CFS), self.thickness), - dtype=config.sim_config.dtypes['float_or_double']) - self.ERB = np.zeros((len(self.CFS), self.thickness), - dtype=config.sim_config.dtypes['float_or_double']) - self.ERE = np.zeros((len(self.CFS), self.thickness), - dtype=config.sim_config.dtypes['float_or_double']) - self.ERF = np.zeros((len(self.CFS), self.thickness), - dtype=config.sim_config.dtypes['float_or_double']) - self.HRA = np.zeros((len(self.CFS), self.thickness), - dtype=config.sim_config.dtypes['float_or_double']) - self.HRB = np.zeros((len(self.CFS), self.thickness), - dtype=config.sim_config.dtypes['float_or_double']) - self.HRE = np.zeros((len(self.CFS), self.thickness), - dtype=config.sim_config.dtypes['float_or_double']) - self.HRF = np.zeros((len(self.CFS), self.thickness), - dtype=config.sim_config.dtypes['float_or_double']) + self.ERA = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"]) + self.ERB = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"]) + self.ERE = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"]) + self.ERF = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"]) + self.HRA = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"]) + self.HRB = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"]) + self.HRE = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"]) + self.HRF = np.zeros((len(self.CFS), self.thickness), dtype=config.sim_config.dtypes["float_or_double"]) for x, cfs in enumerate(self.CFS): if not cfs.sigma.max: @@ -284,76 +294,108 @@ class PML: Esigma, Hsigma = cfs.calculate_values(self.thickness, cfs.sigma) # Define different parameters depending on PML formulation - if self.G.pmls['formulation'] == 'HORIPML': + if self.G.pmls["formulation"] == "HORIPML": # HORIPML electric update coefficients - tmp = ((2 * config.sim_config.em_consts['e0'] * Ekappa) + - self.G.dt * (Ealpha * Ekappa + Esigma)) - self.ERA[x, :] = ((2 * config.sim_config.em_consts['e0'] + - self.G.dt * Ealpha) / tmp) - self.ERB[x, :] = ((2 * config.sim_config.em_consts['e0'] * Ekappa) - / tmp) - self.ERE[x, :] = (((2 * config.sim_config.em_consts['e0'] * Ekappa) - - self.G.dt * (Ealpha * Ekappa + Esigma)) / tmp) + tmp = (2 * config.sim_config.em_consts["e0"] * Ekappa) + self.G.dt * (Ealpha * Ekappa + Esigma) + self.ERA[x, :] = (2 * config.sim_config.em_consts["e0"] + self.G.dt * Ealpha) / tmp + self.ERB[x, :] = (2 * config.sim_config.em_consts["e0"] * Ekappa) / tmp + self.ERE[x, :] = ( + (2 * config.sim_config.em_consts["e0"] * Ekappa) - self.G.dt * (Ealpha * Ekappa + Esigma) + ) / tmp self.ERF[x, :] = (2 * Esigma * self.G.dt) / (Ekappa * tmp) # HORIPML magnetic update coefficients - tmp = ((2 * config.sim_config.em_consts['e0'] * Hkappa) + - self.G.dt * (Halpha * Hkappa + Hsigma)) - self.HRA[x, :] = ((2 * config.sim_config.em_consts['e0'] + - self.G.dt * Halpha) / tmp) - self.HRB[x, :] = ((2 * config.sim_config.em_consts['e0'] * Hkappa) - / tmp) - self.HRE[x, :] = (((2 * config.sim_config.em_consts['e0'] * Hkappa) - - self.G.dt * (Halpha * Hkappa + Hsigma)) / tmp) + tmp = (2 * config.sim_config.em_consts["e0"] * Hkappa) + self.G.dt * (Halpha * Hkappa + Hsigma) + self.HRA[x, :] = (2 * config.sim_config.em_consts["e0"] + self.G.dt * Halpha) / tmp + self.HRB[x, :] = (2 * config.sim_config.em_consts["e0"] * Hkappa) / tmp + self.HRE[x, :] = ( + (2 * config.sim_config.em_consts["e0"] * Hkappa) - self.G.dt * (Halpha * Hkappa + Hsigma) + ) / tmp self.HRF[x, :] = (2 * Hsigma * self.G.dt) / (Hkappa * tmp) - elif self.G.pmls['formulation'] == 'MRIPML': + elif self.G.pmls["formulation"] == "MRIPML": # MRIPML electric update coefficients - tmp = 2 * config.sim_config.em_consts['e0'] + self.G.dt * Ealpha + tmp = 2 * config.sim_config.em_consts["e0"] + self.G.dt * Ealpha self.ERA[x, :] = Ekappa + (self.G.dt * Esigma) / tmp - self.ERB[x, :] = (2 * config.sim_config.em_consts['e0']) / tmp - self.ERE[x, :] = (((2 * config.sim_config.em_consts['e0']) - - self.G.dt * Ealpha) / tmp) + self.ERB[x, :] = (2 * config.sim_config.em_consts["e0"]) / tmp + self.ERE[x, :] = ((2 * config.sim_config.em_consts["e0"]) - self.G.dt * Ealpha) / tmp self.ERF[x, :] = (2 * Esigma * self.G.dt) / tmp # MRIPML magnetic update coefficients - tmp = 2 * config.sim_config.em_consts['e0'] + self.G.dt * Halpha + tmp = 2 * config.sim_config.em_consts["e0"] + self.G.dt * Halpha self.HRA[x, :] = Hkappa + (self.G.dt * Hsigma) / tmp - self.HRB[x, :] = (2 * config.sim_config.em_consts['e0']) / tmp - self.HRE[x, :] = (((2 * config.sim_config.em_consts['e0']) - - self.G.dt * Halpha) / tmp) + self.HRB[x, :] = (2 * config.sim_config.em_consts["e0"]) / tmp + self.HRE[x, :] = ((2 * config.sim_config.em_consts["e0"]) - self.G.dt * Halpha) / tmp self.HRF[x, :] = (2 * Hsigma * self.G.dt) / tmp def update_electric(self): - """This functions updates electric field components with the PML - correction. + """This functions updates electric field components with the PML + correction. """ - pmlmodule = 'gprMax.cython.pml_updates_electric_' + self.G.pmls['formulation'] - func = getattr(import_module(pmlmodule), - 'order' + str(len(self.CFS)) + '_' + self.direction) - func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf, - config.get_model_config().ompthreads, self.G.updatecoeffsE, self.G.ID, - self.G.Ex, self.G.Ey, self.G.Ez, self.G.Hx, self.G.Hy, self.G.Hz, - self.EPhi1, self.EPhi2, self.ERA, self.ERB, self.ERE, self.ERF, self.d) + pmlmodule = "gprMax.cython.pml_updates_electric_" + self.G.pmls["formulation"] + func = getattr(import_module(pmlmodule), "order" + str(len(self.CFS)) + "_" + self.direction) + func( + self.xs, + self.xf, + self.ys, + self.yf, + self.zs, + self.zf, + config.get_model_config().ompthreads, + self.G.updatecoeffsE, + self.G.ID, + self.G.Ex, + self.G.Ey, + self.G.Ez, + self.G.Hx, + self.G.Hy, + self.G.Hz, + self.EPhi1, + self.EPhi2, + self.ERA, + self.ERB, + self.ERE, + self.ERF, + self.d, + ) def update_magnetic(self): - """This functions updates magnetic field components with the PML - correction. + """This functions updates magnetic field components with the PML + correction. """ - pmlmodule = 'gprMax.cython.pml_updates_magnetic_' + self.G.pmls['formulation'] - func = getattr(import_module(pmlmodule), - 'order' + str(len(self.CFS)) + '_' + self.direction) - func(self.xs, self.xf, self.ys, self.yf, self.zs, self.zf, - config.get_model_config().ompthreads, self.G.updatecoeffsH, self.G.ID, - self.G.Ex, self.G.Ey, self.G.Ez, self.G.Hx, self.G.Hy, self.G.Hz, - self.HPhi1, self.HPhi2, self.HRA, self.HRB, self.HRE, self.HRF, self.d) + pmlmodule = "gprMax.cython.pml_updates_magnetic_" + self.G.pmls["formulation"] + func = getattr(import_module(pmlmodule), "order" + str(len(self.CFS)) + "_" + self.direction) + func( + self.xs, + self.xf, + self.ys, + self.yf, + self.zs, + self.zf, + config.get_model_config().ompthreads, + self.G.updatecoeffsH, + self.G.ID, + self.G.Ex, + self.G.Ey, + self.G.Ez, + self.G.Hx, + self.G.Hy, + self.G.Hz, + self.HPhi1, + self.HPhi2, + self.HRA, + self.HRB, + self.HRE, + self.HRF, + self.d, + ) class CUDAPML(PML): """Perfectly Matched Layer (PML) Absorbing Boundary Conditions (ABC) for - solving on GPU using CUDA. + solving on GPU using CUDA. """ def __init__(self, *args, **kwargs): @@ -379,10 +421,17 @@ class CUDAPML(PML): def set_blocks_per_grid(self): """Sets the blocks per grid size used for updating the PML field arrays - on a GPU.""" - self.bpg = (int(np.ceil(((self.EPhi1_dev.shape[1] + 1) * - (self.EPhi1_dev.shape[2] + 1) * - (self.EPhi1_dev.shape[3] + 1)) / self.G.tpb[0])), 1, 1) + on a GPU.""" + self.bpg = ( + int( + np.ceil( + ((self.EPhi1_dev.shape[1] + 1) * (self.EPhi1_dev.shape[2] + 1) * (self.EPhi1_dev.shape[3] + 1)) + / self.G.tpb[0] + ) + ), + 1, + 1, + ) def get_update_funcs(self, kernelselectric, kernelsmagnetic): """Gets update functions from PML kernels. @@ -394,79 +443,81 @@ class CUDAPML(PML): magnetic updates. """ - self.update_electric_dev = kernelselectric.get_function('order' + - str(len(self.CFS)) + - '_' + self.direction) - self.update_magnetic_dev = kernelsmagnetic.get_function('order' + - str(len(self.CFS)) + - '_' + self.direction) + self.update_electric_dev = kernelselectric.get_function("order" + str(len(self.CFS)) + "_" + self.direction) + self.update_magnetic_dev = kernelsmagnetic.get_function("order" + str(len(self.CFS)) + "_" + self.direction) def update_electric(self): """Updates electric field components with the PML correction on the GPU.""" - self.update_electric_dev(np.int32(self.xs), - np.int32(self.xf), - np.int32(self.ys), - np.int32(self.yf), - np.int32(self.zs), - np.int32(self.zf), - np.int32(self.EPhi1_dev.shape[1]), - np.int32(self.EPhi1_dev.shape[2]), - np.int32(self.EPhi1_dev.shape[3]), - np.int32(self.EPhi2_dev.shape[1]), - np.int32(self.EPhi2_dev.shape[2]), - np.int32(self.EPhi2_dev.shape[3]), - np.int32(self.thickness), - self.G.ID_dev.gpudata, - self.G.Ex_dev.gpudata, - self.G.Ey_dev.gpudata, - self.G.Ez_dev.gpudata, - self.G.Hx_dev.gpudata, - self.G.Hy_dev.gpudata, - self.G.Hz_dev.gpudata, - self.EPhi1_dev.gpudata, - self.EPhi2_dev.gpudata, - self.ERA_dev.gpudata, - self.ERB_dev.gpudata, - self.ERE_dev.gpudata, - self.ERF_dev.gpudata, - config.sim_config.dtypes['float_or_double'](self.d), - block=self.G.tpb, grid=self.bpg) + self.update_electric_dev( + np.int32(self.xs), + np.int32(self.xf), + np.int32(self.ys), + np.int32(self.yf), + np.int32(self.zs), + np.int32(self.zf), + np.int32(self.EPhi1_dev.shape[1]), + np.int32(self.EPhi1_dev.shape[2]), + np.int32(self.EPhi1_dev.shape[3]), + np.int32(self.EPhi2_dev.shape[1]), + np.int32(self.EPhi2_dev.shape[2]), + np.int32(self.EPhi2_dev.shape[3]), + np.int32(self.thickness), + self.G.ID_dev.gpudata, + self.G.Ex_dev.gpudata, + self.G.Ey_dev.gpudata, + self.G.Ez_dev.gpudata, + self.G.Hx_dev.gpudata, + self.G.Hy_dev.gpudata, + self.G.Hz_dev.gpudata, + self.EPhi1_dev.gpudata, + self.EPhi2_dev.gpudata, + self.ERA_dev.gpudata, + self.ERB_dev.gpudata, + self.ERE_dev.gpudata, + self.ERF_dev.gpudata, + config.sim_config.dtypes["float_or_double"](self.d), + block=self.G.tpb, + grid=self.bpg, + ) def update_magnetic(self): """Updates magnetic field components with the PML correction on the GPU.""" - self.update_magnetic_dev(np.int32(self.xs), - np.int32(self.xf), - np.int32(self.ys), - np.int32(self.yf), - np.int32(self.zs), - np.int32(self.zf), - np.int32(self.HPhi1_dev.shape[1]), - np.int32(self.HPhi1_dev.shape[2]), - np.int32(self.HPhi1_dev.shape[3]), - np.int32(self.HPhi2_dev.shape[1]), - np.int32(self.HPhi2_dev.shape[2]), - np.int32(self.HPhi2_dev.shape[3]), - np.int32(self.thickness), - self.G.ID_dev.gpudata, - self.G.Ex_dev.gpudata, - self.G.Ey_dev.gpudata, - self.G.Ez_dev.gpudata, - self.G.Hx_dev.gpudata, - self.G.Hy_dev.gpudata, - self.G.Hz_dev.gpudata, - self.HPhi1_dev.gpudata, - self.HPhi2_dev.gpudata, - self.HRA_dev.gpudata, - self.HRB_dev.gpudata, - self.HRE_dev.gpudata, - self.HRF_dev.gpudata, - config.sim_config.dtypes['float_or_double'](self.d), - block=self.G.tpb, grid=self.bpg) + self.update_magnetic_dev( + np.int32(self.xs), + np.int32(self.xf), + np.int32(self.ys), + np.int32(self.yf), + np.int32(self.zs), + np.int32(self.zf), + np.int32(self.HPhi1_dev.shape[1]), + np.int32(self.HPhi1_dev.shape[2]), + np.int32(self.HPhi1_dev.shape[3]), + np.int32(self.HPhi2_dev.shape[1]), + np.int32(self.HPhi2_dev.shape[2]), + np.int32(self.HPhi2_dev.shape[3]), + np.int32(self.thickness), + self.G.ID_dev.gpudata, + self.G.Ex_dev.gpudata, + self.G.Ey_dev.gpudata, + self.G.Ez_dev.gpudata, + self.G.Hx_dev.gpudata, + self.G.Hy_dev.gpudata, + self.G.Hz_dev.gpudata, + self.HPhi1_dev.gpudata, + self.HPhi2_dev.gpudata, + self.HRA_dev.gpudata, + self.HRB_dev.gpudata, + self.HRE_dev.gpudata, + self.HRF_dev.gpudata, + config.sim_config.dtypes["float_or_double"](self.d), + block=self.G.tpb, + grid=self.bpg, + ) class OpenCLPML(PML): """Perfectly Matched Layer (PML) Absorbing Boundary Conditions (ABC) for - solving on compute device using OpenCL. + solving on compute device using OpenCL. """ def __init__(self, *args, **kwargs): @@ -474,7 +525,7 @@ class OpenCLPML(PML): def set_queue(self, queue): """Passes in pyopencl queue. - + Args: queue: pyopencl queue. """ @@ -505,72 +556,76 @@ class OpenCLPML(PML): pass def update_electric(self): - """Updates electric field components with the PML correction on the - compute device. + """Updates electric field components with the PML correction on the + compute device. """ - event = self.update_electric_dev(np.int32(self.xs), - np.int32(self.xf), - np.int32(self.ys), - np.int32(self.yf), - np.int32(self.zs), - np.int32(self.zf), - np.int32(self.EPhi1_dev.shape[1]), - np.int32(self.EPhi1_dev.shape[2]), - np.int32(self.EPhi1_dev.shape[3]), - np.int32(self.EPhi2_dev.shape[1]), - np.int32(self.EPhi2_dev.shape[2]), - np.int32(self.EPhi2_dev.shape[3]), - np.int32(self.thickness), - self.G.ID_dev, - self.G.Ex_dev, - self.G.Ey_dev, - self.G.Ez_dev, - self.G.Hx_dev, - self.G.Hy_dev, - self.G.Hz_dev, - self.EPhi1_dev, - self.EPhi2_dev, - self.ERA_dev, - self.ERB_dev, - self.ERE_dev, - self.ERF_dev, - config.sim_config.dtypes['float_or_double'](self.d)) + event = self.update_electric_dev( + np.int32(self.xs), + np.int32(self.xf), + np.int32(self.ys), + np.int32(self.yf), + np.int32(self.zs), + np.int32(self.zf), + np.int32(self.EPhi1_dev.shape[1]), + np.int32(self.EPhi1_dev.shape[2]), + np.int32(self.EPhi1_dev.shape[3]), + np.int32(self.EPhi2_dev.shape[1]), + np.int32(self.EPhi2_dev.shape[2]), + np.int32(self.EPhi2_dev.shape[3]), + np.int32(self.thickness), + self.G.ID_dev, + self.G.Ex_dev, + self.G.Ey_dev, + self.G.Ez_dev, + self.G.Hx_dev, + self.G.Hy_dev, + self.G.Hz_dev, + self.EPhi1_dev, + self.EPhi2_dev, + self.ERA_dev, + self.ERB_dev, + self.ERE_dev, + self.ERF_dev, + config.sim_config.dtypes["float_or_double"](self.d), + ) event.wait() def update_magnetic(self): - """Updates magnetic field components with the PML correction on the - compute device. + """Updates magnetic field components with the PML correction on the + compute device. """ - event = self.update_magnetic_dev(np.int32(self.xs), - np.int32(self.xf), - np.int32(self.ys), - np.int32(self.yf), - np.int32(self.zs), - np.int32(self.zf), - np.int32(self.HPhi1_dev.shape[1]), - np.int32(self.HPhi1_dev.shape[2]), - np.int32(self.HPhi1_dev.shape[3]), - np.int32(self.HPhi2_dev.shape[1]), - np.int32(self.HPhi2_dev.shape[2]), - np.int32(self.HPhi2_dev.shape[3]), - np.int32(self.thickness), - self.G.ID_dev, - self.G.Ex_dev, - self.G.Ey_dev, - self.G.Ez_dev, - self.G.Hx_dev, - self.G.Hy_dev, - self.G.Hz_dev, - self.HPhi1_dev, - self.HPhi2_dev, - self.HRA_dev, - self.HRB_dev, - self.HRE_dev, - self.HRF_dev, - config.sim_config.dtypes['float_or_double'](self.d)) + event = self.update_magnetic_dev( + np.int32(self.xs), + np.int32(self.xf), + np.int32(self.ys), + np.int32(self.yf), + np.int32(self.zs), + np.int32(self.zf), + np.int32(self.HPhi1_dev.shape[1]), + np.int32(self.HPhi1_dev.shape[2]), + np.int32(self.HPhi1_dev.shape[3]), + np.int32(self.HPhi2_dev.shape[1]), + np.int32(self.HPhi2_dev.shape[2]), + np.int32(self.HPhi2_dev.shape[3]), + np.int32(self.thickness), + self.G.ID_dev, + self.G.Ex_dev, + self.G.Ey_dev, + self.G.Ez_dev, + self.G.Hx_dev, + self.G.Hy_dev, + self.G.Hz_dev, + self.HPhi1_dev, + self.HPhi2_dev, + self.HRA_dev, + self.HRB_dev, + self.HRE_dev, + self.HRF_dev, + config.sim_config.dtypes["float_or_double"](self.d), + ) event.wait() - + def print_pml_info(G): """Prints information about PMLs. @@ -578,24 +633,26 @@ def print_pml_info(G): G: FDTDGrid class describing a grid in a model. """ # No PML - if all(value == 0 for value in G.pmls['thickness'].values()): - return f'\nPML boundaries [{G.name}]: switched off' + if all(value == 0 for value in G.pmls["thickness"].values()): + return f"\nPML boundaries [{G.name}]: switched off" - if all(value == G.pmls['thickness']['x0'] for value in G.pmls['thickness'].values()): - pmlinfo = str(G.pmls['thickness']['x0']) + if all(value == G.pmls["thickness"]["x0"] for value in G.pmls["thickness"].values()): + pmlinfo = str(G.pmls["thickness"]["x0"]) else: - pmlinfo = '' - for key, value in G.pmls['thickness'].items(): - pmlinfo += f'{key}: {value}, ' + pmlinfo = "" + for key, value in G.pmls["thickness"].items(): + pmlinfo += f"{key}: {value}, " pmlinfo = pmlinfo[:-2] - return (f"\nPML boundaries [{G.name}]: {{formulation: {G.pmls['formulation']}, " - f"order: {len(G.pmls['cfs'])}, thickness (cells): {pmlinfo}}}") + return ( + f"\nPML boundaries [{G.name}]: {{formulation: {G.pmls['formulation']}, " + f"order: {len(G.pmls['cfs'])}, thickness (cells): {pmlinfo}}}" + ) def build_pml(G, pml_ID, thickness): - """Builds instances of the PML and calculates the initial parameters and - coefficients including setting profile (based on underlying material + """Builds instances of the PML and calculates the initial parameters and + coefficients including setting profile (based on underlying material er and mr from solid array). Args: @@ -604,7 +661,7 @@ def build_pml(G, pml_ID, thickness): thickness: int with thickness of PML slab in cells. """ - # Arrays to hold values of permittivity and permeability (avoids accessing + # Arrays to hold values of permittivity and permeability (avoids accessing # Material class in Cython.) ers = np.zeros(len(G.materials)) mrs = np.zeros(len(G.materials)) @@ -613,57 +670,39 @@ def build_pml(G, pml_ID, thickness): ers[i] = m.er mrs[i] = m.mr - if config.sim_config.general['solver'] == 'cpu': + if config.sim_config.general["solver"] == "cpu": pml_type = PML - elif config.sim_config.general['solver'] == 'cuda': + elif config.sim_config.general["solver"] == "cuda": pml_type = CUDAPML - elif config.sim_config.general['solver'] == 'opencl': + elif config.sim_config.general["solver"] == "opencl": pml_type = OpenCLPML - if pml_ID == 'x0': - pml = pml_type(G, ID=pml_ID, direction='xminus', - xs=0, xf=thickness, - ys=0, yf=G.ny, - zs=0, zf=G.nz) - elif pml_ID == 'xmax': - pml = pml_type(G, ID=pml_ID, direction='xplus', - xs=G.nx-thickness, xf=G.nx, - ys=0, yf=G.ny, - zs=0, zf=G.nz) - elif pml_ID == 'y0': - pml = pml_type(G, ID=pml_ID, direction='yminus', - xs=0, xf=G.nx, - ys=0, yf=thickness, - zs=0, zf=G.nz) - elif pml_ID == 'ymax': - pml = pml_type(G, ID=pml_ID, direction='yplus', - xs=0, xf=G.nx, - ys=G.ny-thickness, yf=G.ny, - zs=0, zf=G.nz) - elif pml_ID == 'z0': - pml = pml_type(G, ID=pml_ID, direction='zminus', - xs=0, xf=G.nx, - ys=0, yf=G.ny, - zs=0, zf=thickness) - elif pml_ID == 'zmax': - pml = pml_type(G, ID=pml_ID, direction='zplus', - xs=0, xf=G.nx, - ys=0, yf=G.ny, - zs=G.nz-thickness, zf=G.nz) + if pml_ID == "x0": + pml = pml_type(G, ID=pml_ID, direction="xminus", xs=0, xf=thickness, ys=0, yf=G.ny, zs=0, zf=G.nz) + elif pml_ID == "xmax": + pml = pml_type(G, ID=pml_ID, direction="xplus", xs=G.nx - thickness, xf=G.nx, ys=0, yf=G.ny, zs=0, zf=G.nz) + elif pml_ID == "y0": + pml = pml_type(G, ID=pml_ID, direction="yminus", xs=0, xf=G.nx, ys=0, yf=thickness, zs=0, zf=G.nz) + elif pml_ID == "ymax": + pml = pml_type(G, ID=pml_ID, direction="yplus", xs=0, xf=G.nx, ys=G.ny - thickness, yf=G.ny, zs=0, zf=G.nz) + elif pml_ID == "z0": + pml = pml_type(G, ID=pml_ID, direction="zminus", xs=0, xf=G.nx, ys=0, yf=G.ny, zs=0, zf=thickness) + elif pml_ID == "zmax": + pml = pml_type(G, ID=pml_ID, direction="zplus", xs=0, xf=G.nx, ys=0, yf=G.ny, zs=G.nz - thickness, zf=G.nz) - if pml_ID[0] == 'x': - averageer, averagemr = pml_average_er_mr(G.ny, G.nz, - config.get_model_config().ompthreads, - G.solid[pml.xs, :, :], ers, mrs) - elif pml_ID[0] == 'y': - averageer, averagemr = pml_average_er_mr(G.nx, G.nz, - config.get_model_config().ompthreads, - G.solid[:, pml.ys, :], ers, mrs) - elif pml_ID[0] == 'z': - averageer, averagemr = pml_average_er_mr(G.nx, G.ny, - config.get_model_config().ompthreads, - G.solid[:, :, pml.zs], ers, mrs) - - pml.CFS = G.pmls['cfs'] + if pml_ID[0] == "x": + averageer, averagemr = pml_average_er_mr( + G.ny, G.nz, config.get_model_config().ompthreads, G.solid[pml.xs, :, :], ers, mrs + ) + elif pml_ID[0] == "y": + averageer, averagemr = pml_average_er_mr( + G.nx, G.nz, config.get_model_config().ompthreads, G.solid[:, pml.ys, :], ers, mrs + ) + elif pml_ID[0] == "z": + averageer, averagemr = pml_average_er_mr( + G.nx, G.ny, config.get_model_config().ompthreads, G.solid[:, :, pml.zs], ers, mrs + ) + + pml.CFS = G.pmls["cfs"] pml.calculate_update_coeffs(averageer, averagemr) - G.pmls['slabs'].append(pml) + G.pmls["slabs"].append(pml) diff --git a/gprMax/receivers.py b/gprMax/receivers.py index 1ca01b7e..50781a9f 100644 --- a/gprMax/receivers.py +++ b/gprMax/receivers.py @@ -23,14 +23,13 @@ import numpy as np class Rx: """Receiver output points.""" - allowableoutputs = ['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz', 'Ix', 'Iy', 'Iz'] + allowableoutputs = ["Ex", "Ey", "Ez", "Hx", "Hy", "Hz", "Ix", "Iy", "Iz"] defaultoutputs = allowableoutputs[:-3] allowableoutputs_dev = allowableoutputs[:-3] maxnumoutputs_dev = 0 def __init__(self): - self.ID = None self.outputs = {} self.xcoord = None @@ -65,19 +64,22 @@ def htod_rx_arrays(G, queue=None): if len(rx.outputs) > Rx.maxnumoutputs_dev: Rx.maxnumoutputs_dev = len(rx.outputs) - # Array to store field components for receivers on compute device - + # Array to store field components for receivers on compute device - # rows are field components; columns are iterations; pages are receivers - rxs = np.zeros((len(Rx.allowableoutputs_dev), G.iterations, len(G.rxs)), - dtype=config.sim_config.dtypes['float_or_double']) + rxs = np.zeros( + (len(Rx.allowableoutputs_dev), G.iterations, len(G.rxs)), dtype=config.sim_config.dtypes["float_or_double"] + ) # Copy arrays to compute device - if config.sim_config.general['solver'] == 'cuda': + if config.sim_config.general["solver"] == "cuda": import pycuda.gpuarray as gpuarray + rxcoords_dev = gpuarray.to_gpu(rxcoords) rxs_dev = gpuarray.to_gpu(rxs) - elif config.sim_config.general['solver'] == 'opencl': + elif config.sim_config.general["solver"] == "opencl": import pyopencl.array as clarray + rxcoords_dev = clarray.to_device(queue, rxcoords) rxs_dev = clarray.to_device(queue, rxs) @@ -85,7 +87,7 @@ def htod_rx_arrays(G, queue=None): def dtoh_rx_array(rxs_dev, rxcoords_dev, G): - """Copy output from receivers array used on compute device back to receiver + """Copy output from receivers array used on compute device back to receiver objects. Args: @@ -93,13 +95,15 @@ def dtoh_rx_array(rxs_dev, rxcoords_dev, G): rxs_dev: float array of receiver data on compute device - rows are field components; columns are iterations; pages are receivers. G: FDTDGrid class describing a grid in a model. - + """ for rx in G.rxs: for rxd in range(len(G.rxs)): - if (rx.xcoord == rxcoords_dev[rxd, 0] and - rx.ycoord == rxcoords_dev[rxd, 1] and - rx.zcoord == rxcoords_dev[rxd, 2]): + if ( + rx.xcoord == rxcoords_dev[rxd, 0] + and rx.ycoord == rxcoords_dev[rxd, 1] + and rx.zcoord == rxcoords_dev[rxd, 2] + ): for output in rx.outputs.keys(): rx.outputs[output] = rxs_dev[Rx.allowableoutputs_dev.index(output), :, rxd] diff --git a/gprMax/scene.py b/gprMax/scene.py index 363cad5e..b69d420b 100644 --- a/gprMax/scene.py +++ b/gprMax/scene.py @@ -21,8 +21,7 @@ import logging from .cmds_geometry.cmds_geometry import UserObjectGeometry from .cmds_geometry.fractal_box_builder import FractalBoxBuilder from .cmds_multiuse import UserObjectMulti -from .cmds_singleuse import (Discretisation, Domain, TimeWindow, - UserObjectSingle) +from .cmds_singleuse import Discretisation, Domain, TimeWindow, UserObjectSingle from .materials import create_built_in_materials from .subgrids.user_objects import SubGridBase as SubGridUserBase from .user_inputs import create_user_input_points @@ -43,7 +42,7 @@ class Scene: """Add the user object to the scene. Args: - user_object: user object to add to the scene. For example, + user_object: user object to add to the scene. For example, :class:`gprMax.cmds_single_use.Domain` """ if isinstance(user_object, UserObjectMulti): @@ -53,7 +52,7 @@ class Scene: elif isinstance(user_object, UserObjectSingle): self.single_cmds.append(user_object) else: - logger.exception('This object is unknown to gprMax') + logger.exception("This object is unknown to gprMax") raise ValueError def process_subgrid_commands(self): @@ -91,7 +90,7 @@ class Scene: try: obj.create(grid, uip) except ValueError: - logger.exception('Error creating user input object') + logger.exception("Error creating user input object") raise return self @@ -100,24 +99,26 @@ class Scene: # Check for duplicate commands and warn user if they exist cmds_unique = list(set(self.single_cmds)) if len(cmds_unique) != len(self.single_cmds): - logger.exception('Duplicate single-use commands exist in the input.') + logger.exception("Duplicate single-use commands exist in the input.") raise ValueError # Check essential commands and warn user if missing for cmd_type in self.essential_cmds: d = any(isinstance(cmd, cmd_type) for cmd in cmds_unique) if not d: - logger.exception('Your input file is missing essential commands ' + - 'required to run a model. Essential commands ' + - 'are: Domain, Discretisation, Time Window') + logger.exception( + "Your input file is missing essential commands " + + "required to run a model. Essential commands " + + "are: Domain, Discretisation, Time Window" + ) raise ValueError self.process_cmds(cmds_unique, G) def create_internal_objects(self, G): - """Calls the UserObject.create() function in the correct way - API - presents the user with UserObjects in order to build the internal - Rx(), Cylinder() etc... objects. + """Calls the UserObject.create() function in the correct way - API + presents the user with UserObjects in order to build the internal + Rx(), Cylinder() etc... objects. """ # Fractal box commands have an additional nonuser object which @@ -142,4 +143,4 @@ class Scene: self.process_cmds(self.geometry_cmds, G, sort=False) # Process all the commands for subgrids - self.process_subgrid_commands() \ No newline at end of file + self.process_subgrid_commands() diff --git a/gprMax/snapshots.py b/gprMax/snapshots.py index 08f0daab..80dd2614 100644 --- a/gprMax/snapshots.py +++ b/gprMax/snapshots.py @@ -36,7 +36,7 @@ logger = logging.getLogger(__name__) def save_snapshots(grid): """Saves snapshots to file(s). - + Args: grid: FDTDGrid class describing a grid in a model. """ @@ -44,31 +44,35 @@ def save_snapshots(grid): # Create directory for snapshots snapshotdir = config.get_model_config().set_snapshots_dir() snapshotdir.mkdir(exist_ok=True) - logger.info('') - logger.info(f'Snapshot directory: {snapshotdir.resolve()}') + logger.info("") + logger.info(f"Snapshot directory: {snapshotdir.resolve()}") for i, snap in enumerate(grid.snapshots): fn = snapshotdir / Path(snap.filename) snap.filename = fn.with_suffix(snap.fileext) - pbar = tqdm(total=snap.nbytes, leave=True, unit='byte', - unit_scale=True, desc=f'Writing snapshot file {i + 1} ' - f'of {len(grid.snapshots)}, ' - f'{snap.filename.name}', - ncols=get_terminal_width() - 1, file=sys.stdout, - disable=not config.sim_config.general['progressbars']) + pbar = tqdm( + total=snap.nbytes, + leave=True, + unit="byte", + unit_scale=True, + desc=f"Writing snapshot file {i + 1} " f"of {len(grid.snapshots)}, " f"{snap.filename.name}", + ncols=get_terminal_width() - 1, + file=sys.stdout, + disable=not config.sim_config.general["progressbars"], + ) snap.write_file(pbar, grid) pbar.close() - logger.info('') + logger.info("") + class Snapshot: """Snapshots of the electric and magnetic field values.""" - allowableoutputs = {'Ex': None, 'Ey': None, 'Ez': None, - 'Hx': None, 'Hy': None, 'Hz': None} + allowableoutputs = {"Ex": None, "Ey": None, "Ez": None, "Hx": None, "Hy": None, "Hz": None} - # Snapshots can be output as VTK ImageData (.vti) format or + # Snapshots can be output as VTK ImageData (.vti) format or # HDF5 format (.h5) files - fileexts = ['.vti', '.h5'] + fileexts = [".vti", ".h5"] # Dimensions of largest requested snapshot nx_max = 0 @@ -80,9 +84,22 @@ class Snapshot: # GPU - blocks per grid - set according to largest requested snapshot bpg = None - def __init__(self, xs=None, ys=None, zs=None, xf=None, yf=None, zf=None, - dx=None, dy=None, dz=None, time=None, filename=None, - fileext=None, outputs=None): + def __init__( + self, + xs=None, + ys=None, + zs=None, + xf=None, + yf=None, + zf=None, + dx=None, + dy=None, + dz=None, + time=None, + filename=None, + fileext=None, + outputs=None, + ): """ Args: xs, xf, ys, yf, zs, zf: ints for the extent of the volume in cells. @@ -118,14 +135,14 @@ class Snapshot: self.snapfields = {} for k, v in self.outputs.items(): if v: - self.snapfields[k] = np.zeros((self.nx, self.ny, self.nz), - dtype=config.sim_config.dtypes['float_or_double']) - self.nbytes += (self.snapfields[k].nbytes) + self.snapfields[k] = np.zeros( + (self.nx, self.ny, self.nz), dtype=config.sim_config.dtypes["float_or_double"] + ) + self.nbytes += self.snapfields[k].nbytes else: - # If output is not required for snapshot just use a mimimal + # If output is not required for snapshot just use a mimimal # size of array - still required to pass to Cython function - self.snapfields[k] = np.zeros((1, 1, 1), - dtype=config.sim_config.dtypes['float_or_double']) + self.snapfields[k] = np.zeros((1, 1, 1), dtype=config.sim_config.dtypes["float_or_double"]) def store(self, G): """Store (in memory) electric and magnetic field values for snapshot. @@ -142,35 +159,35 @@ class Snapshot: Hyslice = np.ascontiguousarray(G.Hy[self.sx, self.sy, self.sz]) Hzslice = np.ascontiguousarray(G.Hz[self.sx, self.sy, self.sz]) - # Calculate field values at points (comes from averaging field + # Calculate field values at points (comes from averaging field # components in cells) calculate_snapshot_fields( self.nx, self.ny, self.nz, config.get_model_config().ompthreads, - self.outputs['Ex'], - self.outputs['Ey'], - self.outputs['Ez'], - self.outputs['Hx'], - self.outputs['Hy'], - self.outputs['Hz'], + self.outputs["Ex"], + self.outputs["Ey"], + self.outputs["Ez"], + self.outputs["Hx"], + self.outputs["Hy"], + self.outputs["Hz"], Exslice, Eyslice, Ezslice, Hxslice, Hyslice, Hzslice, - self.snapfields['Ex'], - self.snapfields['Ey'], - self.snapfields['Ez'], - self.snapfields['Hx'], - self.snapfields['Hy'], - self.snapfields['Hz'] + self.snapfields["Ex"], + self.snapfields["Ey"], + self.snapfields["Ez"], + self.snapfields["Hx"], + self.snapfields["Hy"], + self.snapfields["Hz"], ) def write_file(self, pbar, G): - """Writes snapshot file either as VTK ImageData (.vti) format + """Writes snapshot file either as VTK ImageData (.vti) format or HDF5 format (.h5) files Args: @@ -178,9 +195,9 @@ class Snapshot: G: FDTDGrid class describing a grid in a model. """ - if self.fileext == '.vti': + if self.fileext == ".vti": self.write_vtk(pbar, G) - elif self.fileext == '.h5': + elif self.fileext == ".h5": self.write_hdf5(pbar, G) def write_vtk(self, pbar, G): @@ -195,31 +212,33 @@ class Snapshot: for k, v in self.outputs.items(): if v: - if k == 'Ex': - celldata[k] = self.snapfields['Ex'] - if k == 'Ey': - celldata[k] = self.snapfields['Ey'] - if k == 'Ez': - celldata[k] = self.snapfields['Ez'] - if k == 'Hx': - celldata[k] = self.snapfields['Hx'] - if k == 'Hy': - celldata[k] = self.snapfields['Hy'] - if k == 'Hz': - celldata[k] = self.snapfields['Hz'] + if k == "Ex": + celldata[k] = self.snapfields["Ex"] + if k == "Ey": + celldata[k] = self.snapfields["Ey"] + if k == "Ez": + celldata[k] = self.snapfields["Ez"] + if k == "Hx": + celldata[k] = self.snapfields["Hx"] + if k == "Hy": + celldata[k] = self.snapfields["Hy"] + if k == "Hz": + celldata[k] = self.snapfields["Hz"] - imageToVTK(str(self.filename.with_suffix('')), - origin=((self.xs * self.dx * G.dx), - (self.ys * self.dy * G.dy), - (self.zs * self.dz * G.dz)), - spacing=((self.dx * G.dx), - (self.dy * G.dy), - (self.dz * G.dz)), - cellData=celldata) - - pbar.update(n=len(celldata) * self.nx * self.ny * self.nz * - np.dtype(config.sim_config.dtypes['float_or_double']).itemsize) + imageToVTK( + str(self.filename.with_suffix("")), + origin=((self.xs * self.dx * G.dx), (self.ys * self.dy * G.dy), (self.zs * self.dz * G.dz)), + spacing=((self.dx * G.dx), (self.dy * G.dy), (self.dz * G.dz)), + cellData=celldata, + ) + pbar.update( + n=len(celldata) + * self.nx + * self.ny + * self.nz + * np.dtype(config.sim_config.dtypes["float_or_double"]).itemsize + ) def write_hdf5(self, pbar, G): """Writes snapshot file in HDF5 (.h5) format. @@ -229,31 +248,31 @@ class Snapshot: G: FDTDGrid class describing a grid in a model. """ - f = h5py.File(self.filename, 'w') - f.attrs['gprMax'] = __version__ - f.attrs['Title'] = G.title - f.attrs['nx_ny_nz'] = (self.nx, self.ny, self.nz) - f.attrs['dx_dy_dz'] = (self.dx * G.dx, self.dy * G.dy, self.dz * G.dz) - f.attrs['time'] = self.time * G.dt + f = h5py.File(self.filename, "w") + f.attrs["gprMax"] = __version__ + f.attrs["Title"] = G.title + f.attrs["nx_ny_nz"] = (self.nx, self.ny, self.nz) + f.attrs["dx_dy_dz"] = (self.dx * G.dx, self.dy * G.dy, self.dz * G.dz) + f.attrs["time"] = self.time * G.dt - if self.outputs['Ex']: - f['Ex'] = self.snapfields['Ex'] - pbar.update(n=self.snapfields['Ex'].nbytes) - if self.outputs['Ey']: - f['Ey'] = self.snapfields['Ey'] - pbar.update(n=self.snapfields['Ey'].nbytes) - if self.outputs['Ez']: - f['Ez'] = self.snapfields['Ez'] - pbar.update(n=self.snapfields['Ez'].nbytes) - if self.outputs['Hx']: - f['Hx'] = self.snapfields['Hx'] - pbar.update(n=self.snapfields['Hx'].nbytes) - if self.outputs['Hy']: - f['Hy'] = self.snapfields['Hy'] - pbar.update(n=self.snapfields['Hy'].nbytes) - if self.outputs['Hz']: - f['Hz'] = self.snapfields['Hz'] - pbar.update(n=self.snapfields['Hz'].nbytes) + if self.outputs["Ex"]: + f["Ex"] = self.snapfields["Ex"] + pbar.update(n=self.snapfields["Ex"].nbytes) + if self.outputs["Ey"]: + f["Ey"] = self.snapfields["Ey"] + pbar.update(n=self.snapfields["Ey"].nbytes) + if self.outputs["Ez"]: + f["Ez"] = self.snapfields["Ez"] + pbar.update(n=self.snapfields["Ez"].nbytes) + if self.outputs["Hx"]: + f["Hx"] = self.snapfields["Hx"] + pbar.update(n=self.snapfields["Hx"].nbytes) + if self.outputs["Hy"]: + f["Hy"] = self.snapfields["Hy"] + pbar.update(n=self.snapfields["Hy"].nbytes) + if self.outputs["Hz"]: + f["Hz"] = self.snapfields["Hz"] + pbar.update(n=self.snapfields["Hz"].nbytes) f.close() @@ -278,37 +297,44 @@ def htod_snapshot_array(G, queue=None): if snap.nz > Snapshot.nz_max: Snapshot.nz_max = snap.nz - if config.sim_config.general['solver'] == 'cuda': + if config.sim_config.general["solver"] == "cuda": # Blocks per grid - according to largest requested snapshot - Snapshot.bpg = (int(np.ceil(((Snapshot.nx_max) * - (Snapshot.ny_max) * - (Snapshot.nz_max)) / Snapshot.tpb[0])), 1, 1) - elif config.sim_config.general['solver'] == 'opencl': + Snapshot.bpg = ( + int(np.ceil(((Snapshot.nx_max) * (Snapshot.ny_max) * (Snapshot.nz_max)) / Snapshot.tpb[0])), + 1, + 1, + ) + elif config.sim_config.general["solver"] == "opencl": # Workgroup size - according to largest requested snapshot - Snapshot.wgs = (int(np.ceil(((Snapshot.nx_max) * - (Snapshot.ny_max) * - (Snapshot.nz_max)))), 1, 1) + Snapshot.wgs = (int(np.ceil(((Snapshot.nx_max) * (Snapshot.ny_max) * (Snapshot.nz_max)))), 1, 1) # 4D arrays to store snapshots on GPU, e.g. snapEx(time, x, y, z); # if snapshots are not being stored on the GPU during the simulation then # they are copied back to the host after each iteration, hence numsnaps = 1 - numsnaps = 1 if config.get_model_config().device['snapsgpu2cpu'] else len(G.snapshots) - snapEx = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), - dtype=config.sim_config.dtypes['float_or_double']) - snapEy = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), - dtype=config.sim_config.dtypes['float_or_double']) - snapEz = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), - dtype=config.sim_config.dtypes['float_or_double']) - snapHx = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), - dtype=config.sim_config.dtypes['float_or_double']) - snapHy = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), - dtype=config.sim_config.dtypes['float_or_double']) - snapHz = np.zeros((numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), - dtype=config.sim_config.dtypes['float_or_double']) + numsnaps = 1 if config.get_model_config().device["snapsgpu2cpu"] else len(G.snapshots) + snapEx = np.zeros( + (numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"] + ) + snapEy = np.zeros( + (numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"] + ) + snapEz = np.zeros( + (numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"] + ) + snapHx = np.zeros( + (numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"] + ) + snapHy = np.zeros( + (numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"] + ) + snapHz = np.zeros( + (numsnaps, Snapshot.nx_max, Snapshot.ny_max, Snapshot.nz_max), dtype=config.sim_config.dtypes["float_or_double"] + ) # Copy arrays to compute device - if config.sim_config.general['solver'] == 'cuda': + if config.sim_config.general["solver"] == "cuda": import pycuda.gpuarray as gpuarray + snapEx_dev = gpuarray.to_gpu(snapEx) snapEy_dev = gpuarray.to_gpu(snapEy) snapEz_dev = gpuarray.to_gpu(snapEz) @@ -316,8 +342,9 @@ def htod_snapshot_array(G, queue=None): snapHy_dev = gpuarray.to_gpu(snapHy) snapHz_dev = gpuarray.to_gpu(snapHz) - elif config.sim_config.general['solver'] == 'opencl': + elif config.sim_config.general["solver"] == "opencl": import pyopencl.array as clarray + snapEx_dev = clarray.to_device(queue, snapEx) snapEy_dev = clarray.to_device(queue, snapEy) snapEz_dev = clarray.to_device(queue, snapEz) @@ -328,9 +355,8 @@ def htod_snapshot_array(G, queue=None): return snapEx_dev, snapEy_dev, snapEz_dev, snapHx_dev, snapHy_dev, snapHz_dev -def dtoh_snapshot_array(snapEx_dev, snapEy_dev, snapEz_dev, - snapHx_dev, snapHy_dev, snapHz_dev, i, snap): - """Copies snapshot array used on compute device back to snapshot objects and +def dtoh_snapshot_array(snapEx_dev, snapEy_dev, snapEz_dev, snapHx_dev, snapHy_dev, snapHz_dev, i, snap): + """Copies snapshot array used on compute device back to snapshot objects and store in format for Paraview. Args: @@ -339,9 +365,9 @@ def dtoh_snapshot_array(snapEx_dev, snapEy_dev, snapEz_dev, snap: Snapshot class instance """ - snap.snapfields['Ex'] = snapEx_dev[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf] - snap.snapfields['Ey'] = snapEy_dev[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf] - snap.snapfields['Ez'] = snapEz_dev[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf] - snap.snapfields['Hx'] = snapHx_dev[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf] - snap.snapfields['Hy'] = snapHy_dev[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf] - snap.snapfields['Hz'] = snapHz_dev[i, snap.xs:snap.xf, snap.ys:snap.yf, snap.zs:snap.zf] + snap.snapfields["Ex"] = snapEx_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf] + snap.snapfields["Ey"] = snapEy_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf] + snap.snapfields["Ez"] = snapEz_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf] + snap.snapfields["Hx"] = snapHx_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf] + snap.snapfields["Hy"] = snapHy_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf] + snap.snapfields["Hz"] = snapHz_dev[i, snap.xs : snap.xf, snap.ys : snap.yf, snap.zs : snap.zf] diff --git a/gprMax/solvers.py b/gprMax/solvers.py index 3813dbe8..ac510956 100644 --- a/gprMax/solvers.py +++ b/gprMax/solvers.py @@ -30,11 +30,11 @@ def create_G(): G: FDTDGrid class describing a grid in a model. """ - if config.sim_config.general['solver'] == 'cpu': + if config.sim_config.general["solver"] == "cpu": G = FDTDGrid() - elif config.sim_config.general['solver'] == 'cuda': + elif config.sim_config.general["solver"] == "cuda": G = CUDAGrid() - elif config.sim_config.general['solver'] == 'opencl': + elif config.sim_config.general["solver"] == "opencl": G = OpenCLGrid() return G @@ -45,10 +45,10 @@ def create_solver(G): N.B. A large range of different functions exist to advance the time step for dispersive materials. The correct function is set by the - set_dispersive_updates method, based on the required numerical - precision and dispersive material type. - This is done for solvers running on CPU, i.e. where Cython is used. - CUDA and OpenCL dispersive material functions are handled through + set_dispersive_updates method, based on the required numerical + precision and dispersive material type. + This is done for solvers running on CPU, i.e. where Cython is used. + CUDA and OpenCL dispersive material functions are handled through templating and substitution at runtime. Args: @@ -58,23 +58,24 @@ def create_solver(G): solver: Solver object. """ - if config.sim_config.general['subgrid']: + if config.sim_config.general["subgrid"]: updates = create_subgrid_updates(G) - if config.get_model_config().materials['maxpoles'] != 0: - # Set dispersive update functions for both SubgridUpdates and + if config.get_model_config().materials["maxpoles"] != 0: + # Set dispersive update functions for both SubgridUpdates and # SubgridUpdaters subclasses updates.set_dispersive_updates() - for u in updates.updaters: u.set_dispersive_updates() - solver = Solver(updates, hsg=True) - elif config.sim_config.general['solver'] == 'cpu': + for u in updates.updaters: + u.set_dispersive_updates() + solver = Solver(updates, hsg=True) + elif config.sim_config.general["solver"] == "cpu": updates = CPUUpdates(G) - if config.get_model_config().materials['maxpoles'] != 0: + if config.get_model_config().materials["maxpoles"] != 0: updates.set_dispersive_updates() solver = Solver(updates) - elif config.sim_config.general['solver'] == 'cuda': + elif config.sim_config.general["solver"] == "cuda": updates = CUDAUpdates(G) solver = Solver(updates) - elif config.sim_config.general['solver'] == 'opencl': + elif config.sim_config.general["solver"] == "opencl": updates = OpenCLUpdates(G) solver = Solver(updates) @@ -119,9 +120,9 @@ class Solver: if self.hsg: self.updates.hsg_1() self.updates.update_electric_b() - if config.sim_config.general['solver'] == 'cuda': - self.memused = self.updates.calculate_memory_used(iteration) + if config.sim_config.general["solver"] == "cuda": + self.memused = self.updates.calculate_memory_used(iteration) self.updates.finalise() self.solvetime = self.updates.calculate_solve_time() - self.updates.cleanup() \ No newline at end of file + self.updates.cleanup() diff --git a/gprMax/sources.py b/gprMax/sources.py index 88d3ac6b..b78d8be7 100644 --- a/gprMax/sources.py +++ b/gprMax/sources.py @@ -48,12 +48,10 @@ class Source: G: FDTDGrid class describing a grid in a model. """ # Waveform values for sources that need to be calculated on whole timesteps - self.waveformvalues_wholedt = np.zeros((G.iterations), - dtype=config.sim_config.dtypes['float_or_double']) + self.waveformvalues_wholedt = np.zeros((G.iterations), dtype=config.sim_config.dtypes["float_or_double"]) # Waveform values for sources that need to be calculated on half timesteps - self.waveformvalues_halfdt = np.zeros((G.iterations), - dtype=config.sim_config.dtypes['float_or_double']) + self.waveformvalues_halfdt = np.zeros((G.iterations), dtype=config.sim_config.dtypes["float_or_double"]) waveform = next(x for x in G.waveforms if x.ID == self.waveformID) @@ -65,14 +63,13 @@ class Source: time -= self.start self.waveformvalues_wholedt[iteration] = waveform.calculate_value(time, G.dt) self.waveformvalues_halfdt[iteration] = waveform.calculate_value(time + 0.5 * G.dt, G.dt) - class VoltageSource(Source): """A voltage source can be a hard source if it's resistance is zero, - i.e. the time variation of the specified electric field component - is prescribed. If it's resistance is non-zero it behaves as a resistive - voltage source. + i.e. the time variation of the specified electric field component + is prescribed. If it's resistance is non-zero it behaves as a resistive + voltage source. """ def __init__(self): @@ -86,7 +83,7 @@ class VoltageSource(Source): iteration: int of current iteration (timestep). updatecoeffsE: memory view of array of electric field update coefficients. - ID: memory view of array of numeric IDs corresponding to materials + ID: memory view of array of numeric IDs corresponding to materials in the model. Ex, Ey, Ez: memory view of array of electric field values. G: FDTDGrid class describing a grid in a model. @@ -96,29 +93,35 @@ class VoltageSource(Source): i = self.xcoord j = self.ycoord k = self.zcoord - componentID = f'E{self.polarisation}' + componentID = f"E{self.polarisation}" - if self.polarisation == 'x': + if self.polarisation == "x": if self.resistance != 0: - Ex[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * - self.waveformvalues_halfdt[iteration] * - (1 / (self.resistance * G.dy * G.dz))) + Ex[i, j, k] -= ( + updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] + * self.waveformvalues_halfdt[iteration] + * (1 / (self.resistance * G.dy * G.dz)) + ) else: Ex[i, j, k] = -1 * self.waveformvalues_wholedt[iteration] / G.dx - elif self.polarisation == 'y': + elif self.polarisation == "y": if self.resistance != 0: - Ey[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * - self.waveformvalues_halfdt[iteration] * - (1 / (self.resistance * G.dx * G.dz))) + Ey[i, j, k] -= ( + updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] + * self.waveformvalues_halfdt[iteration] + * (1 / (self.resistance * G.dx * G.dz)) + ) else: Ey[i, j, k] = -1 * self.waveformvalues_wholedt[iteration] / G.dy - elif self.polarisation == 'z': + elif self.polarisation == "z": if self.resistance != 0: - Ez[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * - self.waveformvalues_halfdt[iteration] * - (1 / (self.resistance * G.dx * G.dy))) + Ez[i, j, k] -= ( + updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] + * self.waveformvalues_halfdt[iteration] + * (1 / (self.resistance * G.dx * G.dy)) + ) else: Ez[i, j, k] = -1 * self.waveformvalues_wholedt[iteration] / G.dz @@ -136,21 +139,21 @@ class VoltageSource(Source): j = self.ycoord k = self.zcoord - componentID = f'E{self.polarisation}' + componentID = f"E{self.polarisation}" requirednumID = G.ID[G.IDlookup[componentID], i, j, k] material = next(x for x in G.materials if x.numID == requirednumID) newmaterial = deepcopy(material) - newmaterial.ID = f'{material.ID}+{self.ID}' + newmaterial.ID = f"{material.ID}+{self.ID}" newmaterial.numID = len(G.materials) newmaterial.averagable = False - newmaterial.type += ',\nvoltage-source' if newmaterial.type else 'voltage-source' + newmaterial.type += ",\nvoltage-source" if newmaterial.type else "voltage-source" # Add conductivity of voltage source to underlying conductivity - if self.polarisation == 'x': + if self.polarisation == "x": newmaterial.se += G.dx / (self.resistance * G.dy * G.dz) - elif self.polarisation == 'y': + elif self.polarisation == "y": newmaterial.se += G.dy / (self.resistance * G.dx * G.dz) - elif self.polarisation == 'z': + elif self.polarisation == "z": newmaterial.se += G.dz / (self.resistance * G.dx * G.dy) G.ID[G.IDlookup[componentID], i, j, k] = newmaterial.numID @@ -171,7 +174,7 @@ class HertzianDipole(Source): iteration: int of current iteration (timestep). updatecoeffsE: memory view of array of electric field update coefficients. - ID: memory view of array of numeric IDs corresponding to materials + ID: memory view of array of numeric IDs corresponding to materials in the model. Ex, Ey, Ez: memory view of array of electric field values. G: FDTDGrid class describing a grid in a model. @@ -181,21 +184,30 @@ class HertzianDipole(Source): i = self.xcoord j = self.ycoord k = self.zcoord - componentID = f'E{self.polarisation}' - if self.polarisation == 'x': - Ex[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * - self.waveformvalues_halfdt[iteration] * self.dl * - (1 / (G.dx * G.dy * G.dz))) + componentID = f"E{self.polarisation}" + if self.polarisation == "x": + Ex[i, j, k] -= ( + updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] + * self.waveformvalues_halfdt[iteration] + * self.dl + * (1 / (G.dx * G.dy * G.dz)) + ) - elif self.polarisation == 'y': - Ey[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * - self.waveformvalues_halfdt[iteration] * self.dl * - (1 / (G.dx * G.dy * G.dz))) + elif self.polarisation == "y": + Ey[i, j, k] -= ( + updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] + * self.waveformvalues_halfdt[iteration] + * self.dl + * (1 / (G.dx * G.dy * G.dz)) + ) - elif self.polarisation == 'z': - Ez[i, j, k] -= (updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * - self.waveformvalues_halfdt[iteration] * self.dl * - (1 / (G.dx * G.dy * G.dz))) + elif self.polarisation == "z": + Ez[i, j, k] -= ( + updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] + * self.waveformvalues_halfdt[iteration] + * self.dl + * (1 / (G.dx * G.dy * G.dz)) + ) class MagneticDipole(Source): @@ -208,7 +220,7 @@ class MagneticDipole(Source): iteration: int of current iteration (timestep). updatecoeffsH: memory view of array of magnetic field update coefficients. - ID: memory view of array of numeric IDs corresponding to materials + ID: memory view of array of numeric IDs corresponding to materials in the model. Hx, Hy, Hz: memory view of array of magnetic field values. G: FDTDGrid class describing a grid in a model. @@ -218,26 +230,32 @@ class MagneticDipole(Source): i = self.xcoord j = self.ycoord k = self.zcoord - componentID = f'H{self.polarisation}' + componentID = f"H{self.polarisation}" - if self.polarisation == 'x': - Hx[i, j, k] -= (updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] * - self.waveformvalues_wholedt[iteration] * - (1 / (G.dx * G.dy * G.dz))) + if self.polarisation == "x": + Hx[i, j, k] -= ( + updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] + * self.waveformvalues_wholedt[iteration] + * (1 / (G.dx * G.dy * G.dz)) + ) - elif self.polarisation == 'y': - Hy[i, j, k] -= (updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] * - self.waveformvalues_wholedt[iteration] * - (1 / (G.dx * G.dy * G.dz))) + elif self.polarisation == "y": + Hy[i, j, k] -= ( + updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] + * self.waveformvalues_wholedt[iteration] + * (1 / (G.dx * G.dy * G.dz)) + ) - elif self.polarisation == 'z': - Hz[i, j, k] -= (updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] * - self.waveformvalues_wholedt[iteration] * - (1 / (G.dx * G.dy * G.dz))) + elif self.polarisation == "z": + Hz[i, j, k] -= ( + updatecoeffsH[ID[G.IDlookup[componentID], i, j, k], 4] + * self.waveformvalues_wholedt[iteration] + * (1 / (G.dx * G.dy * G.dz)) + ) def htod_src_arrays(sources, G, queue=None): - """Initialise arrays on compute device for source coordinates/polarisation, + """Initialise arrays on compute device for source coordinates/polarisation, other source information, and source waveform values. Args: @@ -246,32 +264,32 @@ def htod_src_arrays(sources, G, queue=None): queue: pyopencl queue. Returns: - srcinfo1_dev: int array of source cell coordinates and polarisation + srcinfo1_dev: int array of source cell coordinates and polarisation information. - srcinfo2_dev: float array of other source information, e.g. length, + srcinfo2_dev: float array of other source information, e.g. length, resistance etc... srcwaves_dev: float array of source waveform values. """ srcinfo1 = np.zeros((len(sources), 4), dtype=np.int32) - srcinfo2 = np.zeros((len(sources)), dtype=config.sim_config.dtypes['float_or_double']) - srcwaves = np.zeros((len(sources), G.iterations), dtype=config.sim_config.dtypes['float_or_double']) + srcinfo2 = np.zeros((len(sources)), dtype=config.sim_config.dtypes["float_or_double"]) + srcwaves = np.zeros((len(sources), G.iterations), dtype=config.sim_config.dtypes["float_or_double"]) for i, src in enumerate(sources): srcinfo1[i, 0] = src.xcoord srcinfo1[i, 1] = src.ycoord srcinfo1[i, 2] = src.zcoord - if src.polarisation == 'x': + if src.polarisation == "x": srcinfo1[i, 3] = 0 - elif src.polarisation == 'y': + elif src.polarisation == "y": srcinfo1[i, 3] = 1 - elif src.polarisation == 'z': + elif src.polarisation == "z": srcinfo1[i, 3] = 2 - if src.__class__.__name__ == 'HertzianDipole': + if src.__class__.__name__ == "HertzianDipole": srcinfo2[i] = src.dl srcwaves[i, :] = src.waveformvalues_halfdt - elif src.__class__.__name__ == 'VoltageSource': + elif src.__class__.__name__ == "VoltageSource": if src.resistance: srcinfo2[i] = src.resistance srcwaves[i, :] = src.waveformvalues_halfdt @@ -280,17 +298,19 @@ def htod_src_arrays(sources, G, queue=None): srcwaves[i, :] = src.waveformvalues_wholedt srcinfo2[i] = src.resistance srcwaves[i, :] = src.waveformvalues_halfdt - elif src.__class__.__name__ == 'MagneticDipole': + elif src.__class__.__name__ == "MagneticDipole": srcwaves[i, :] = src.waveformvalues_wholedt # Copy arrays to compute device - if config.sim_config.general['solver'] == 'cuda': + if config.sim_config.general["solver"] == "cuda": import pycuda.gpuarray as gpuarray + srcinfo1_dev = gpuarray.to_gpu(srcinfo1) srcinfo2_dev = gpuarray.to_gpu(srcinfo2) srcwaves_dev = gpuarray.to_gpu(srcwaves) - elif config.sim_config.general['solver'] == 'opencl': + elif config.sim_config.general["solver"] == "opencl": import pyopencl.array as clarray + srcinfo1_dev = clarray.to_device(queue, srcinfo1) srcinfo2_dev = clarray.to_device(queue, srcinfo2) srcwaves_dev = clarray.to_device(queue, srcwaves) @@ -300,7 +320,7 @@ def htod_src_arrays(sources, G, queue=None): class TransmissionLine(Source): """A transmission line source is a one-dimensional transmission line - which is attached virtually to a grid cell. + which is attached virtually to a grid cell. """ def __init__(self, G): @@ -330,12 +350,12 @@ class TransmissionLine(Source): # Cell position of where line connects to antenna/main grid self.antpos = 10 - self.voltage = np.zeros(self.nl, dtype=config.sim_config.dtypes['float_or_double']) - self.current = np.zeros(self.nl, dtype=config.sim_config.dtypes['float_or_double']) - self.Vinc = np.zeros(G.iterations, dtype=config.sim_config.dtypes['float_or_double']) - self.Iinc = np.zeros(G.iterations, dtype=config.sim_config.dtypes['float_or_double']) - self.Vtotal = np.zeros(G.iterations, dtype=config.sim_config.dtypes['float_or_double']) - self.Itotal = np.zeros(G.iterations, dtype=config.sim_config.dtypes['float_or_double']) + self.voltage = np.zeros(self.nl, dtype=config.sim_config.dtypes["float_or_double"]) + self.current = np.zeros(self.nl, dtype=config.sim_config.dtypes["float_or_double"]) + self.Vinc = np.zeros(G.iterations, dtype=config.sim_config.dtypes["float_or_double"]) + self.Iinc = np.zeros(G.iterations, dtype=config.sim_config.dtypes["float_or_double"]) + self.Vtotal = np.zeros(G.iterations, dtype=config.sim_config.dtypes["float_or_double"]) + self.Itotal = np.zeros(G.iterations, dtype=config.sim_config.dtypes["float_or_double"]) def calculate_incident_V_I(self, G): """Calculates the incident voltage and current with a long length @@ -377,8 +397,9 @@ class TransmissionLine(Source): """ # Update all the voltage values along the line - self.voltage[1:self.nl] -= (self.resistance * (config.c * G.dt / self.dl) * - (self.current[1:self.nl] - self.current[0:self.nl - 1])) + self.voltage[1 : self.nl] -= ( + self.resistance * (config.c * G.dt / self.dl) * (self.current[1 : self.nl] - self.current[0 : self.nl - 1]) + ) # Update the voltage at the position of the one-way injector excitation self.voltage[self.srcpos] += (config.c * G.dt / self.dl) * self.waveformvalues_halfdt[iteration] @@ -395,13 +416,16 @@ class TransmissionLine(Source): """ # Update all the current values along the line - self.current[0:self.nl - 1] -= ((1 / self.resistance) * (config.c * G.dt / self.dl) * - (self.voltage[1:self.nl] - self.voltage[0:self.nl - 1])) + self.current[0 : self.nl - 1] -= ( + (1 / self.resistance) + * (config.c * G.dt / self.dl) + * (self.voltage[1 : self.nl] - self.voltage[0 : self.nl - 1]) + ) # Update the current one cell before the position of the one-way injector excitation - self.current[self.srcpos - 1] += ((1 / self.resistance) * - (config.c * G.dt / self.dl) * - self.waveformvalues_wholedt[iteration]) + self.current[self.srcpos - 1] += ( + (1 / self.resistance) * (config.c * G.dt / self.dl) * self.waveformvalues_wholedt[iteration] + ) def update_electric(self, iteration, updatecoeffsE, ID, Ex, Ey, Ez, G): """Updates electric field value in the main grid from voltage value in @@ -411,7 +435,7 @@ class TransmissionLine(Source): iteration: int of current iteration (timestep). updatecoeffsE: memory view of array of electric field update coefficients. - ID: memory view of array of numeric IDs corresponding to materials + ID: memory view of array of numeric IDs corresponding to materials in the model. Ex, Ey, Ez: memory view of array of electric field values. G: FDTDGrid class describing a grid in a model. @@ -424,14 +448,14 @@ class TransmissionLine(Source): self.update_voltage(iteration, G) - if self.polarisation == 'x': - Ex[i, j, k] = - self.voltage[self.antpos] / G.dx + if self.polarisation == "x": + Ex[i, j, k] = -self.voltage[self.antpos] / G.dx - elif self.polarisation == 'y': - Ey[i, j, k] = - self.voltage[self.antpos] / G.dy + elif self.polarisation == "y": + Ey[i, j, k] = -self.voltage[self.antpos] / G.dy - elif self.polarisation == 'z': - Ez[i, j, k] = - self.voltage[self.antpos] / G.dz + elif self.polarisation == "z": + Ez[i, j, k] = -self.voltage[self.antpos] / G.dz def update_magnetic(self, iteration, updatecoeffsH, ID, Hx, Hy, Hz, G): """Updates current value in transmission line from magnetic field values @@ -441,7 +465,7 @@ class TransmissionLine(Source): iteration: int of current iteration (timestep). updatecoeffsH: memory view of array of magnetic field update coefficients. - ID: memory view of array of numeric IDs corresponding to materials + ID: memory view of array of numeric IDs corresponding to materials in the model. Hx, Hy, Hz: memory view of array of magnetic field values. G: FDTDGrid class describing a grid in a model. @@ -452,13 +476,13 @@ class TransmissionLine(Source): j = self.ycoord k = self.zcoord - if self.polarisation == 'x': + if self.polarisation == "x": self.current[self.antpos] = Ix(i, j, k, G.Hx, G.Hy, G.Hz, G) - elif self.polarisation == 'y': + elif self.polarisation == "y": self.current[self.antpos] = Iy(i, j, k, G.Hx, G.Hy, G.Hz, G) - elif self.polarisation == 'z': + elif self.polarisation == "z": self.current[self.antpos] = Iz(i, j, k, G.Hx, G.Hy, G.Hz, G) self.update_current(iteration, G) diff --git a/gprMax/subgrids/grid.py b/gprMax/subgrids/grid.py index e91fdb5d..5ab51817 100644 --- a/gprMax/subgrids/grid.py +++ b/gprMax/subgrids/grid.py @@ -24,42 +24,41 @@ logger = logging.getLogger(__name__) class SubGridBaseGrid(FDTDGrid): - def __init__(self, *args, **kwargs): super().__init__() - self.ratio = kwargs['ratio'] + self.ratio = kwargs["ratio"] if self.ratio % 2 == 0: - logger.exception('Subgrid Error: Only odd ratios are supported') + logger.exception("Subgrid Error: Only odd ratios are supported") raise ValueError # Name of the grid - self.name = kwargs['id'] + self.name = kwargs["id"] - self.filter = kwargs['filter'] + self.filter = kwargs["filter"] # Number of main grid cells between the IS and OS - self.is_os_sep = kwargs['is_os_sep'] + self.is_os_sep = kwargs["is_os_sep"] # Number of subgrid grid cells between the IS and OS self.s_is_os_sep = self.is_os_sep * self.ratio # Distance from OS to PML or the edge of the grid when PML is off - self.pml_separation = kwargs['pml_separation'] + self.pml_separation = kwargs["pml_separation"] - self.pmls['thickness']['x0'] = kwargs['subgrid_pml_thickness'] - self.pmls['thickness']['y0'] = kwargs['subgrid_pml_thickness'] - self.pmls['thickness']['z0'] = kwargs['subgrid_pml_thickness'] - self.pmls['thickness']['xmax'] = kwargs['subgrid_pml_thickness'] - self.pmls['thickness']['ymax'] = kwargs['subgrid_pml_thickness'] - self.pmls['thickness']['zmax'] = kwargs['subgrid_pml_thickness'] + self.pmls["thickness"]["x0"] = kwargs["subgrid_pml_thickness"] + self.pmls["thickness"]["y0"] = kwargs["subgrid_pml_thickness"] + self.pmls["thickness"]["z0"] = kwargs["subgrid_pml_thickness"] + self.pmls["thickness"]["xmax"] = kwargs["subgrid_pml_thickness"] + self.pmls["thickness"]["ymax"] = kwargs["subgrid_pml_thickness"] + self.pmls["thickness"]["zmax"] = kwargs["subgrid_pml_thickness"] # Number of sub cells to extend the sub grid beyond the IS boundary d_to_pml = self.s_is_os_sep + self.pml_separation # Index of the IS - self.n_boundary_cells = d_to_pml + self.pmls['thickness']['x0'] - self.n_boundary_cells_x = d_to_pml + self.pmls['thickness']['x0'] - self.n_boundary_cells_y = d_to_pml + self.pmls['thickness']['y0'] - self.n_boundary_cells_z = d_to_pml + self.pmls['thickness']['z0'] + self.n_boundary_cells = d_to_pml + self.pmls["thickness"]["x0"] + self.n_boundary_cells_x = d_to_pml + self.pmls["thickness"]["x0"] + self.n_boundary_cells_y = d_to_pml + self.pmls["thickness"]["y0"] + self.n_boundary_cells_z = d_to_pml + self.pmls["thickness"]["z0"] - self.interpolation = kwargs['interpolation'] \ No newline at end of file + self.interpolation = kwargs["interpolation"] diff --git a/gprMax/subgrids/precursor_nodes.py b/gprMax/subgrids/precursor_nodes.py index f5e85ece..fb95c841 100644 --- a/gprMax/subgrids/precursor_nodes.py +++ b/gprMax/subgrids/precursor_nodes.py @@ -30,7 +30,6 @@ def calculate_weighting_coefficients(x1, x): class PrecursorNodesBase: - def __init__(self, fdtd_grid, sub_grid): self.G = fdtd_grid self.ratio = sub_grid.ratio @@ -68,7 +67,6 @@ class PrecursorNodesBase: self.initialize_electric_slices_array() def _initialize_fields(self): - # Initialise the precursor arrays # The precursors are divided up into the 6. Each represent 1 @@ -144,23 +142,34 @@ class PrecursorNodesBase: self.hy_bottom_0 = np.copy(self.hy_top_1) def _initialize_field_names(self): - self.fn_m = [ - 'hx_front', 'hz_front', - 'hx_back', 'hz_back', - 'hy_left', 'hz_left', - 'hy_right', 'hz_right', - 'hx_top', 'hy_top', - 'hx_bottom', 'hy_bottom' + "hx_front", + "hz_front", + "hx_back", + "hz_back", + "hy_left", + "hz_left", + "hy_right", + "hz_right", + "hx_top", + "hy_top", + "hx_bottom", + "hy_bottom", ] self.fn_e = [ - 'ex_front', 'ez_front', - 'ex_back', 'ez_back', - 'ey_left', 'ez_left', - 'ey_right', 'ez_right', - 'ex_top', 'ey_top', - 'ex_bottom', 'ey_bottom' + "ex_front", + "ez_front", + "ex_back", + "ez_back", + "ey_left", + "ez_left", + "ey_right", + "ez_right", + "ex_top", + "ey_top", + "ex_bottom", + "ey_bottom", ] def interpolate_magnetic_in_time(self, m): @@ -174,17 +183,17 @@ class PrecursorNodesBase: for f in field_names: try: - val = c1 * getattr(self, f'{f}_0') + c2 * getattr(self, f'{f}_1') + val = c1 * getattr(self, f"{f}_0") + c2 * getattr(self, f"{f}_1") except ValueError: raise setattr(self, f, val) def calc_exact_field(self, field_names): - """Sets the fields used in update calculations to the values at the + """Sets the fields used in update calculations to the values at the current main time step, i.e. ey_left = copy.ey_left_1 """ for f in field_names: - val = np.copy(getattr(self, f'{f}_1')) + val = np.copy(getattr(self, f"{f}_1")) setattr(self, f, val) def calc_exact_magnetic_in_time(self): @@ -194,7 +203,6 @@ class PrecursorNodesBase: self.calc_exact_field(self.fn_e) def create_interpolated_coords(self, mid, field): - n_x = field.shape[0] n_y = field.shape[1] @@ -218,9 +226,9 @@ class PrecursorNodesBase: def update_previous_timestep_fields(self, field_names): for fn in field_names: - val = getattr(self, f'{fn}_1') + val = getattr(self, f"{fn}_1") val_c = np.copy(val) - setattr(self, f'{fn}_0', val_c) + setattr(self, f"{fn}_0", val_c) def interpolate_to_sub_grid(self, field, coords): x, z, x_sg, z_sg = coords @@ -229,31 +237,26 @@ class PrecursorNodesBase: return f_i def update_electric(self): - self.update_previous_timestep_fields(self.fn_e) for obj in self.electric_slices: f_m = self.get_transverse_e(obj) f_i = self.interpolate_to_sub_grid(f_m, obj[1]) f = f_i - #f = f_i[self.ratio:-self.ratio, self.ratio:-self.ratio] + # f = f_i[self.ratio:-self.ratio, self.ratio:-self.ratio] setattr(self, obj[0], f) def update_magnetic(self): - # Copy previous time step magnetic field values to the previous # time step variables self.update_previous_timestep_fields(self.fn_m) for obj in self.magnetic_slices: - # Grab the main grid fields used to interpolate across the IS # f = self.Hi[slice] f_1, f_2 = self.get_transverse_h(obj) - if ('left' in obj[0] or - 'bottom' in obj[0] or - 'front' in obj[0]): + if "left" in obj[0] or "bottom" in obj[0] or "front" in obj[0]: w = self.l_weight else: w = self.r_weight @@ -268,30 +271,28 @@ class PrecursorNodesBase: raise ValueError # discard the outer nodes only required for interpolation - #f = f_i[self.ratio:-self.ratio, self.ratio:-self.ratio] + # f = f_i[self.ratio:-self.ratio, self.ratio:-self.ratio] f = f_i setattr(self, obj[0], f) class PrecursorNodes(PrecursorNodesBase): - def __init__(self, fdtd_grid, sub_grid): super().__init__(fdtd_grid, sub_grid) def initialize_magnetic_slices_array(self): - # Array contains the indices at which the main grid should be sliced # to obtain the 2 2d array of H nodes required for interpolation # across the IS boundary for each h field on each face of the subgrid # Extend the surface so that the outer fields can be interpolated # more accurately - #i0 = self.i0 - 1 - #j0 = self.j0 - 1 - #k0 = self.k0 - 1 - #i1 = self.i1 + 1 - #j1 = self.j1 + 1 - #k1 = self.k1 + 1 + # i0 = self.i0 - 1 + # j0 = self.j0 - 1 + # k0 = self.k0 - 1 + # i1 = self.i1 + 1 + # j1 = self.j1 + 1 + # k1 = self.k1 + 1 # not extended i0 = self.i0 @@ -302,43 +303,91 @@ class PrecursorNodes(PrecursorNodesBase): k1 = self.k1 slices = [ - ['hy_left_1', False, - (self.i0 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Hy], - ['hy_right_1', False, - (self.i1 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Hy], - ['hz_left_1', True, - (self.i0 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Hz], - ['hz_right_1', True, - (self.i1 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Hz], - ['hx_front_1', False, - (slice(i0, i1 + 1, 1), self.j0 - 1, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), self.Hx], - ['hx_back_1', False, - (slice(i0, i1 + 1, 1), self.j1 - 1, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), self.Hx], - ['hz_front_1', True, - (slice(i0, i1, 1), self.j0 - 1, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), self.Hz], - ['hz_back_1', True, - (slice(i0, i1, 1), self.j1 - 1, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), self.Hz], - ['hx_bottom_1', False, - # check these indexes - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 1), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), self.Hx], - ['hx_top_1', False, - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 1), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), self.Hx], - ['hy_bottom_1', True, - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 1), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), self.Hy], - ['hy_top_1', True, - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 1), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), self.Hy] + [ + "hy_left_1", + False, + (self.i0 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + self.Hy, + ], + [ + "hy_right_1", + False, + (self.i1 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + self.Hy, + ], + [ + "hz_left_1", + True, + (self.i0 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + self.Hz, + ], + [ + "hz_right_1", + True, + (self.i1 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + self.Hz, + ], + [ + "hx_front_1", + False, + (slice(i0, i1 + 1, 1), self.j0 - 1, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), + self.Hx, + ], + [ + "hx_back_1", + False, + (slice(i0, i1 + 1, 1), self.j1 - 1, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), + self.Hx, + ], + [ + "hz_front_1", + True, + (slice(i0, i1, 1), self.j0 - 1, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), + self.Hz, + ], + [ + "hz_back_1", + True, + (slice(i0, i1, 1), self.j1 - 1, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), + self.Hz, + ], + [ + "hx_bottom_1", + False, + # check these indexes + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 1), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), + self.Hx, + ], + [ + "hx_top_1", + False, + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 1), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), + self.Hx, + ], + [ + "hy_bottom_1", + True, + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 1), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), + self.Hy, + ], + [ + "hy_top_1", + True, + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 1), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), + self.Hy, + ], ] for obj in slices: @@ -350,12 +399,12 @@ class PrecursorNodes(PrecursorNodesBase): def initialize_electric_slices_array(self): # Extend the region sliced from the main grid by 1 cell. # this allows more accurate interpolation for the outernodes - #i0 = self.i0 - 1 - #j0 = self.j0 - 1 - #k0 = self.k0 - 1 - #i1 = self.i1 + 1 - #j1 = self.j1 + 1 - #k1 = self.k1 + 1 + # i0 = self.i0 - 1 + # j0 = self.j0 - 1 + # k0 = self.k0 - 1 + # i1 = self.i1 + 1 + # j1 = self.j1 + 1 + # k1 = self.k1 + 1 # Not extended i0 = self.i0 @@ -367,30 +416,18 @@ class PrecursorNodes(PrecursorNodesBase): # Spatially interpolate nodes slices = [ - ['ex_front_1', True, - (slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), self.Ex], - ['ex_back_1', True, - (slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), self.Ex], - ['ez_front_1', False, - (slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), self.Ez], - ['ez_back_1', False, - (slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), self.Ez], - ['ey_left_1', True, - (self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey], - ['ey_right_1', True, - (self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey], - ['ez_left_1', False, - (self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez], - ['ez_right_1', False, - (self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez], - ['ex_bottom_1', True, - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), self.Ex], - ['ex_top_1', True, - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), self.Ex], - ['ey_bottom_1', False, - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), self.Ey], - ['ey_top_1', False, - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), self.Ey] + ["ex_front_1", True, (slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), self.Ex], + ["ex_back_1", True, (slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), self.Ex], + ["ez_front_1", False, (slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), self.Ez], + ["ez_back_1", False, (slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), self.Ez], + ["ey_left_1", True, (self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey], + ["ey_right_1", True, (self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey], + ["ez_left_1", False, (self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez], + ["ez_right_1", False, (self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez], + ["ex_bottom_1", True, (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), self.Ex], + ["ex_top_1", True, (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), self.Ex], + ["ey_bottom_1", False, (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), self.Ey], + ["ey_top_1", False, (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), self.Ey], ] for obj in slices: @@ -410,24 +447,22 @@ class PrecursorNodes(PrecursorNodesBase): class PrecursorNodesFiltered(PrecursorNodesBase): - def __init__(self, fdtd_grid, sub_grid): super().__init__(fdtd_grid, sub_grid) def initialize_magnetic_slices_array(self): - # Array contains the indices at which the main grid should be sliced # to obtain the 2 2d array of H nodes required for interpolation # across the IS boundary for each h field on each face of the subgrid # Extend the surface so that the outer fields can be interpolated # more accurately - #i0 = self.i0 - 1 - #j0 = self.j0 - 1 - #k0 = self.k0 - 1 - #i1 = self.i1 + 1 - #j1 = self.j1 + 1 - #k1 = self.k1 + 1 + # i0 = self.i0 - 1 + # j0 = self.j0 - 1 + # k0 = self.k0 - 1 + # i1 = self.i1 + 1 + # j1 = self.j1 + 1 + # k1 = self.k1 + 1 # Not extended i0 = self.i0 @@ -438,67 +473,115 @@ class PrecursorNodesFiltered(PrecursorNodesBase): k1 = self.k1 slices = [ - ['hy_left_1', False, - (self.i0 - 2, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i0 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i0 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Hy], - ['hy_right_1', False, - (self.i1 - 2, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i1 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i1 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Hy], - ['hz_left_1', True, - (self.i0 - 2, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i0 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i0 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Hz], - ['hz_right_1', True, - (self.i1 - 2, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i1 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i1 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Hz], - ['hx_front_1', False, - (slice(i0, i1 + 1, 1), self.j0 - 2, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j0 - 1, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j0 + 1, slice(k0, k1, 1)), self.Hx], - ['hx_back_1', False, - (slice(i0, i1 + 1, 1), self.j1 - 2, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j1 - 1, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j1 + 1, slice(k0, k1, 1)), self.Hx], - ['hz_front_1', True, - (slice(i0, i1, 1), self.j0 - 2, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j0 - 1, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j0 + 1, slice(k0, k1 + 1, 1)), self.Hz], - ['hz_back_1', True, - (slice(i0, i1, 1), self.j1 - 2, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j1 - 1, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j1 + 1, slice(k0, k1 + 1, 1)), self.Hz], - ['hx_bottom_1', False, - # Check these indexes - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 2), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 1), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 + 1), self.Hx], - ['hx_top_1', False, - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 2), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 1), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 + 1), self.Hx], - ['hy_bottom_1', True, - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 2), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 1), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 + 1), self.Hy], - ['hy_top_1', True, - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 2), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 1), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 + 1), self.Hy] + [ + "hy_left_1", + False, + (self.i0 - 2, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i0 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i0 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + self.Hy, + ], + [ + "hy_right_1", + False, + (self.i1 - 2, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i1 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i1 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + self.Hy, + ], + [ + "hz_left_1", + True, + (self.i0 - 2, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i0 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i0 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + self.Hz, + ], + [ + "hz_right_1", + True, + (self.i1 - 2, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i1 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i1 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + self.Hz, + ], + [ + "hx_front_1", + False, + (slice(i0, i1 + 1, 1), self.j0 - 2, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j0 - 1, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j0 + 1, slice(k0, k1, 1)), + self.Hx, + ], + [ + "hx_back_1", + False, + (slice(i0, i1 + 1, 1), self.j1 - 2, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j1 - 1, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j1 + 1, slice(k0, k1, 1)), + self.Hx, + ], + [ + "hz_front_1", + True, + (slice(i0, i1, 1), self.j0 - 2, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j0 - 1, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j0 + 1, slice(k0, k1 + 1, 1)), + self.Hz, + ], + [ + "hz_back_1", + True, + (slice(i0, i1, 1), self.j1 - 2, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j1 - 1, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j1 + 1, slice(k0, k1 + 1, 1)), + self.Hz, + ], + [ + "hx_bottom_1", + False, + # Check these indexes + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 2), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 1), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 + 1), + self.Hx, + ], + [ + "hx_top_1", + False, + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 2), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 1), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 + 1), + self.Hx, + ], + [ + "hy_bottom_1", + True, + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 2), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 1), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 + 1), + self.Hy, + ], + [ + "hy_top_1", + True, + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 2), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 1), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 + 1), + self.Hy, + ], ] for obj in slices: @@ -510,12 +593,12 @@ class PrecursorNodesFiltered(PrecursorNodesBase): def initialize_electric_slices_array(self): # Extend the region sliced from the main grid by 1 cell. # this allows more accurate interpolation for the outernodes - #i0 = self.i0 - 1 - #j0 = self.j0 - 1 - #k0 = self.k0 - 1 - #i1 = self.i1 + 1 - #j1 = self.j1 + 1 - #k1 = self.k1 + 1 + # i0 = self.i0 - 1 + # j0 = self.j0 - 1 + # k0 = self.k0 - 1 + # i1 = self.i1 + 1 + # j1 = self.j1 + 1 + # k1 = self.k1 + 1 # Not extended i0 = self.i0 @@ -527,54 +610,102 @@ class PrecursorNodesFiltered(PrecursorNodesBase): # Spatially interpolate nodes slices = [ - ['ex_front_1', True, - (slice(i0, i1, 1), self.j0 - 1, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j0 + 1, slice(k0, k1 + 1, 1)), self.Ex], - ['ex_back_1', True, - (slice(i0, i1, 1), self.j1 - 1, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), - (slice(i0, i1, 1), self.j1 + 1, slice(k0, k1 + 1, 1)), self.Ex], - ['ez_front_1', False, - (slice(i0, i1 + 1, 1), self.j0 - 1, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j0 + 1, slice(k0, k1, 1)), self.Ez], - ['ez_back_1', False, - (slice(i0, i1 + 1, 1), self.j1 - 1, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), - (slice(i0, i1 + 1, 1), self.j1 + 1, slice(k0, k1, 1)), self.Ez], - ['ey_left_1', True, - (self.i0 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i0 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey], - ['ey_right_1', True, - (self.i1 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), - (self.i1 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), self.Ey], - ['ez_left_1', False, - (self.i0 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i0 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez], - ['ez_right_1', False, - (self.i1 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), - (self.i1 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), self.Ez], - ['ex_bottom_1', True, - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 1), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 + 1), self.Ex], - ['ex_top_1', True, - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 1), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), - (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 + 1), self.Ex], - ['ey_bottom_1', False, - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 1), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 + 1), self.Ey], - ['ey_top_1', False, - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 1), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), - (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 + 1), self.Ey] + [ + "ex_front_1", + True, + (slice(i0, i1, 1), self.j0 - 1, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j0, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j0 + 1, slice(k0, k1 + 1, 1)), + self.Ex, + ], + [ + "ex_back_1", + True, + (slice(i0, i1, 1), self.j1 - 1, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j1, slice(k0, k1 + 1, 1)), + (slice(i0, i1, 1), self.j1 + 1, slice(k0, k1 + 1, 1)), + self.Ex, + ], + [ + "ez_front_1", + False, + (slice(i0, i1 + 1, 1), self.j0 - 1, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j0, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j0 + 1, slice(k0, k1, 1)), + self.Ez, + ], + [ + "ez_back_1", + False, + (slice(i0, i1 + 1, 1), self.j1 - 1, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j1, slice(k0, k1, 1)), + (slice(i0, i1 + 1, 1), self.j1 + 1, slice(k0, k1, 1)), + self.Ez, + ], + [ + "ey_left_1", + True, + (self.i0 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i0, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i0 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + self.Ey, + ], + [ + "ey_right_1", + True, + (self.i1 - 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + (self.i1 + 1, slice(j0, j1, 1), slice(k0, k1 + 1, 1)), + self.Ey, + ], + [ + "ez_left_1", + False, + (self.i0 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i0, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i0 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + self.Ez, + ], + [ + "ez_right_1", + False, + (self.i1 - 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + (self.i1 + 1, slice(j0, j1 + 1, 1), slice(k0, k1, 1)), + self.Ez, + ], + [ + "ex_bottom_1", + True, + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 - 1), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k0 + 1), + self.Ex, + ], + [ + "ex_top_1", + True, + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 - 1), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1), + (slice(i0, i1, 1), slice(j0, j1 + 1, 1), self.k1 + 1), + self.Ex, + ], + [ + "ey_bottom_1", + False, + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 - 1), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k0 + 1), + self.Ey, + ], + [ + "ey_top_1", + False, + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 - 1), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1), + (slice(i0, i1 + 1, 1), slice(j0, j1, 1), self.k1 + 1), + self.Ey, + ], ] for obj in slices: diff --git a/gprMax/subgrids/subgrid_hsg.py b/gprMax/subgrids/subgrid_hsg.py index 60e04db7..f74863ce 100644 --- a/gprMax/subgrids/subgrid_hsg.py +++ b/gprMax/subgrids/subgrid_hsg.py @@ -20,15 +20,13 @@ import logging import gprMax.config as config -from ..cython.fields_updates_hsg import (update_electric_os, update_is, - update_magnetic_os) +from ..cython.fields_updates_hsg import update_electric_os, update_is, update_magnetic_os from .grid import SubGridBaseGrid logger = logging.getLogger(__name__) class SubGridHSG(SubGridBaseGrid): - def __init__(self, **kwargs): super().__init__(**kwargs) @@ -37,7 +35,7 @@ class SubGridHSG(SubGridBaseGrid): from the main grid. Args: - precursors: + precursors: """ # Form of FDTD update equations for H @@ -46,43 +44,139 @@ class SubGridHSG(SubGridBaseGrid): # Hx = c0Hx - c2Ez + c3Ey # Bottom and top - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, - self.n_boundary_cells, -1, self.nwx, self.nwy + 1, self.nwz, 1, - self.Hy, precursors.ex_bottom, precursors.ex_top, - self.IDlookup['Hy'], 1, -1, 3, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsH, + self.ID, + self.n_boundary_cells, + -1, + self.nwx, + self.nwy + 1, + self.nwz, + 1, + self.Hy, + precursors.ex_bottom, + precursors.ex_top, + self.IDlookup["Hy"], + 1, + -1, + 3, + config.get_model_config().ompthreads, + ) - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, - self.n_boundary_cells, -1, self.nwx + 1, self.nwy, self.nwz, 1, - self.Hx, precursors.ey_bottom, precursors.ey_top, - self.IDlookup['Hx'], -1, 1, 3, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsH, + self.ID, + self.n_boundary_cells, + -1, + self.nwx + 1, + self.nwy, + self.nwz, + 1, + self.Hx, + precursors.ey_bottom, + precursors.ey_top, + self.IDlookup["Hx"], + -1, + 1, + 3, + config.get_model_config().ompthreads, + ) # Left and right - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, - self.n_boundary_cells, -1, self.nwy, self.nwz + 1, self.nwx, 2, - self.Hz, precursors.ey_left, precursors.ey_right, - self.IDlookup['Hz'], 1, -1, 1, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsH, + self.ID, + self.n_boundary_cells, + -1, + self.nwy, + self.nwz + 1, + self.nwx, + 2, + self.Hz, + precursors.ey_left, + precursors.ey_right, + self.IDlookup["Hz"], + 1, + -1, + 1, + config.get_model_config().ompthreads, + ) - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, - self.n_boundary_cells, -1, self.nwy + 1, self.nwz, self.nwx, 2, - self.Hy, precursors.ez_left, precursors.ez_right, - self.IDlookup['Hy'], -1, 1, 1, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsH, + self.ID, + self.n_boundary_cells, + -1, + self.nwy + 1, + self.nwz, + self.nwx, + 2, + self.Hy, + precursors.ez_left, + precursors.ez_right, + self.IDlookup["Hy"], + -1, + 1, + 1, + config.get_model_config().ompthreads, + ) # Front and back - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, - self.n_boundary_cells, -1, self.nwx, self.nwz + 1, self.nwy, 3, - self.Hz, precursors.ex_front, precursors.ex_back, - self.IDlookup['Hz'], -1, 1, 2, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsH, + self.ID, + self.n_boundary_cells, + -1, + self.nwx, + self.nwz + 1, + self.nwy, + 3, + self.Hz, + precursors.ex_front, + precursors.ex_back, + self.IDlookup["Hz"], + -1, + 1, + 2, + config.get_model_config().ompthreads, + ) - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsH, self.ID, - self.n_boundary_cells, -1, self.nwx + 1, self.nwz, self.nwy, 3, - self.Hx, precursors.ez_front, precursors.ez_back, - self.IDlookup['Hx'], 1, -1, 2, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsH, + self.ID, + self.n_boundary_cells, + -1, + self.nwx + 1, + self.nwz, + self.nwy, + 3, + self.Hx, + precursors.ez_front, + precursors.ez_back, + self.IDlookup["Hx"], + 1, + -1, + 2, + config.get_model_config().ompthreads, + ) def update_electric_is(self, precursors): """Updates the subgrid nodes at the IS with the currents derived @@ -98,43 +192,139 @@ class SubGridHSG(SubGridBaseGrid): # Ez = c0(Ez) + c1(dHy) - c2(dHx) # Bottom and top - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, - self.n_boundary_cells, 0, self.nwx, self.nwy + 1, self.nwz, 1, - self.Ex, precursors.hy_bottom, precursors.hy_top, - self.IDlookup['Ex'], 1, -1, 3, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsE, + self.ID, + self.n_boundary_cells, + 0, + self.nwx, + self.nwy + 1, + self.nwz, + 1, + self.Ex, + precursors.hy_bottom, + precursors.hy_top, + self.IDlookup["Ex"], + 1, + -1, + 3, + config.get_model_config().ompthreads, + ) - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, - self.n_boundary_cells, 0, self.nwx + 1, self.nwy, self.nwz, 1, - self.Ey, precursors.hx_bottom, precursors.hx_top, - self.IDlookup['Ey'], -1, 1, 3, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsE, + self.ID, + self.n_boundary_cells, + 0, + self.nwx + 1, + self.nwy, + self.nwz, + 1, + self.Ey, + precursors.hx_bottom, + precursors.hx_top, + self.IDlookup["Ey"], + -1, + 1, + 3, + config.get_model_config().ompthreads, + ) # Left and right - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, - self.n_boundary_cells, 0, self.nwy, self.nwz + 1, self.nwx, 2, - self.Ey, precursors.hz_left, precursors.hz_right, - self.IDlookup['Ey'], 1, -1, 1, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsE, + self.ID, + self.n_boundary_cells, + 0, + self.nwy, + self.nwz + 1, + self.nwx, + 2, + self.Ey, + precursors.hz_left, + precursors.hz_right, + self.IDlookup["Ey"], + 1, + -1, + 1, + config.get_model_config().ompthreads, + ) - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, - self.n_boundary_cells, 0, self.nwy + 1, self.nwz, self.nwx, 2, - self.Ez, precursors.hy_left, precursors.hy_right, - self.IDlookup['Ez'], -1, 1, 1, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsE, + self.ID, + self.n_boundary_cells, + 0, + self.nwy + 1, + self.nwz, + self.nwx, + 2, + self.Ez, + precursors.hy_left, + precursors.hy_right, + self.IDlookup["Ez"], + -1, + 1, + 1, + config.get_model_config().ompthreads, + ) # Front and back - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, - self.n_boundary_cells, 0, self.nwx, self.nwz + 1, self.nwy, 3, - self.Ex, precursors.hz_front, precursors.hz_back, - self.IDlookup['Ex'], -1, 1, 2, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsE, + self.ID, + self.n_boundary_cells, + 0, + self.nwx, + self.nwz + 1, + self.nwy, + 3, + self.Ex, + precursors.hz_front, + precursors.hz_back, + self.IDlookup["Ex"], + -1, + 1, + 2, + config.get_model_config().ompthreads, + ) - update_is(self.nwx, self.nwy, self.nwz, self.updatecoeffsE, self.ID, - self.n_boundary_cells, 0, self.nwx + 1, self.nwz, self.nwy, 3, - self.Ez, precursors.hx_front, precursors.hx_back, - self.IDlookup['Ez'], 1, -1, 2, - config.get_model_config().ompthreads) + update_is( + self.nwx, + self.nwy, + self.nwz, + self.updatecoeffsE, + self.ID, + self.n_boundary_cells, + 0, + self.nwx + 1, + self.nwz, + self.nwy, + 3, + self.Ez, + precursors.hx_front, + precursors.hx_back, + self.IDlookup["Ez"], + 1, + -1, + 2, + config.get_model_config().ompthreads, + ) def update_electric_os(self, main_grid): """ @@ -155,49 +345,151 @@ class SubGridHSG(SubGridBaseGrid): # Ez = c0(Ez) + c1(dHy) - c2(dHx) # Front and back - update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 3, - i_l, i_u, k_l, k_u + 1, j_l, j_u, self.nwy, - main_grid.IDlookup['Ex'], main_grid.Ex, self.Hz, - 2, 1, -1, 1, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_electric_os( + main_grid.updatecoeffsE, + main_grid.ID, + 3, + i_l, + i_u, + k_l, + k_u + 1, + j_l, + j_u, + self.nwy, + main_grid.IDlookup["Ex"], + main_grid.Ex, + self.Hz, + 2, + 1, + -1, + 1, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) - update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 3, - i_l, i_u + 1, k_l, k_u, j_l, j_u, self.nwy, - main_grid.IDlookup['Ez'], main_grid.Ez, self.Hx, - 2, -1, 1, 0, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_electric_os( + main_grid.updatecoeffsE, + main_grid.ID, + 3, + i_l, + i_u + 1, + k_l, + k_u, + j_l, + j_u, + self.nwy, + main_grid.IDlookup["Ez"], + main_grid.Ez, + self.Hx, + 2, + -1, + 1, + 0, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) # Left and right - update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 2, - j_l, j_u, k_l, k_u + 1, i_l, i_u, self.nwx, - main_grid.IDlookup['Ey'], main_grid.Ey, self.Hz, - 1, -1, 1, 1, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_electric_os( + main_grid.updatecoeffsE, + main_grid.ID, + 2, + j_l, + j_u, + k_l, + k_u + 1, + i_l, + i_u, + self.nwx, + main_grid.IDlookup["Ey"], + main_grid.Ey, + self.Hz, + 1, + -1, + 1, + 1, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) - update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 2, - j_l, j_u + 1, k_l, k_u, i_l, i_u, self.nwx, - main_grid.IDlookup['Ez'], main_grid.Ez, self.Hy, - 1, 1, -1, 0, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_electric_os( + main_grid.updatecoeffsE, + main_grid.ID, + 2, + j_l, + j_u + 1, + k_l, + k_u, + i_l, + i_u, + self.nwx, + main_grid.IDlookup["Ez"], + main_grid.Ez, + self.Hy, + 1, + 1, + -1, + 0, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) # Bottom and top - update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 1, - i_l, i_u, j_l, j_u + 1, k_l, k_u, self.nwz, - main_grid.IDlookup['Ex'], main_grid.Ex, self.Hy, - 3, -1, 1, 1, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_electric_os( + main_grid.updatecoeffsE, + main_grid.ID, + 1, + i_l, + i_u, + j_l, + j_u + 1, + k_l, + k_u, + self.nwz, + main_grid.IDlookup["Ex"], + main_grid.Ex, + self.Hy, + 3, + -1, + 1, + 1, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) - update_electric_os(main_grid.updatecoeffsE, main_grid.ID, 1, - i_l, i_u + 1, j_l, j_u, k_l, k_u, self.nwz, - main_grid.IDlookup['Ey'], main_grid.Ey, self.Hx, - 3, 1, -1, 0, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_electric_os( + main_grid.updatecoeffsE, + main_grid.ID, + 1, + i_l, + i_u + 1, + j_l, + j_u, + k_l, + k_u, + self.nwz, + main_grid.IDlookup["Ey"], + main_grid.Ey, + self.Hx, + 3, + 1, + -1, + 0, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) def update_magnetic_os(self, main_grid): """ @@ -218,80 +510,185 @@ class SubGridHSG(SubGridBaseGrid): # Hx = c0Hx - c2Ez + c3Ey # Front and back - update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 3, - i_l, i_u, k_l, k_u + 1, j_l - 1, j_u, self.nwy, - main_grid.IDlookup['Hz'], main_grid.Hz, self.Ex, - 2, 1, -1, 1, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_magnetic_os( + main_grid.updatecoeffsH, + main_grid.ID, + 3, + i_l, + i_u, + k_l, + k_u + 1, + j_l - 1, + j_u, + self.nwy, + main_grid.IDlookup["Hz"], + main_grid.Hz, + self.Ex, + 2, + 1, + -1, + 1, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) - update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 3, - i_l, i_u + 1, k_l, k_u, j_l - 1, j_u, self.nwy, - main_grid.IDlookup['Hx'], main_grid.Hx, self.Ez, - 2, -1, 1, 0, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_magnetic_os( + main_grid.updatecoeffsH, + main_grid.ID, + 3, + i_l, + i_u + 1, + k_l, + k_u, + j_l - 1, + j_u, + self.nwy, + main_grid.IDlookup["Hx"], + main_grid.Hx, + self.Ez, + 2, + -1, + 1, + 0, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) # Left and right - update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 2, - j_l, j_u, k_l, k_u + 1, i_l - 1, i_u, self.nwx, - main_grid.IDlookup['Hz'], main_grid.Hz, self.Ey, - 1, -1, 1, 1, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_magnetic_os( + main_grid.updatecoeffsH, + main_grid.ID, + 2, + j_l, + j_u, + k_l, + k_u + 1, + i_l - 1, + i_u, + self.nwx, + main_grid.IDlookup["Hz"], + main_grid.Hz, + self.Ey, + 1, + -1, + 1, + 1, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) - update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 2, - j_l, j_u + 1, k_l, k_u, i_l - 1, i_u, self.nwx, - main_grid.IDlookup['Hy'], main_grid.Hy, self.Ez, - 1, 1, -1, 0, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_magnetic_os( + main_grid.updatecoeffsH, + main_grid.ID, + 2, + j_l, + j_u + 1, + k_l, + k_u, + i_l - 1, + i_u, + self.nwx, + main_grid.IDlookup["Hy"], + main_grid.Hy, + self.Ez, + 1, + 1, + -1, + 0, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) # Bottom and top - update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 1, - i_l, i_u, j_l, j_u + 1, k_l - 1, k_u, self.nwz, - main_grid.IDlookup['Hy'], main_grid.Hy, self.Ex, - 3, -1, 1, 1, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_magnetic_os( + main_grid.updatecoeffsH, + main_grid.ID, + 1, + i_l, + i_u, + j_l, + j_u + 1, + k_l - 1, + k_u, + self.nwz, + main_grid.IDlookup["Hy"], + main_grid.Hy, + self.Ex, + 3, + -1, + 1, + 1, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) - update_magnetic_os(main_grid.updatecoeffsH, main_grid.ID, 1, - i_l, i_u + 1, j_l, j_u, k_l - 1, k_u, self.nwz, - main_grid.IDlookup['Hx'], main_grid.Hx, self.Ey, - 3, 1, -1, 0, self.ratio, self.is_os_sep, - self.n_boundary_cells, - config.get_model_config().ompthreads) + update_magnetic_os( + main_grid.updatecoeffsH, + main_grid.ID, + 1, + i_l, + i_u + 1, + j_l, + j_u, + k_l - 1, + k_u, + self.nwz, + main_grid.IDlookup["Hx"], + main_grid.Hx, + self.Ey, + 3, + 1, + -1, + 0, + self.ratio, + self.is_os_sep, + self.n_boundary_cells, + config.get_model_config().ompthreads, + ) def print_info(self): """Prints information about the subgrid. Useful info: - Total region = working region + + Total region = working region + 2 * (is_os_sep * pml_separation * pml_thickness) - is_os_sep: number of main grid cells between the Inner Surface and - the Outer Surface. Defaults to 3. Multiply by ratio to + is_os_sep: number of main grid cells between the Inner Surface and + the Outer Surface. Defaults to 3. Multiply by ratio to get subgrid cells. - pml_separation: number of subgrid cells between the Outer Surface + pml_separation: number of subgrid cells between the Outer Surface and the PML. Defaults to ratio // 2 + 2. - pml_thickness: number of PML cells on each of the 6 sides of the + pml_thickness: number of PML cells on each of the 6 sides of the subgrid. Defaults to 6. """ # Working region - xs, ys, zs = self.round_to_grid((self.i0 * self.dx * self.ratio, - self.j0 * self.dy * self.ratio, - self.k0 * self.dz * self.ratio)) - xf, yf, zf = self.round_to_grid((self.i1 * self.dx * self.ratio, - self.j1 * self.dy * self.ratio, - self.k1 * self.dz * self.ratio)) + xs, ys, zs = self.round_to_grid( + (self.i0 * self.dx * self.ratio, self.j0 * self.dy * self.ratio, self.k0 * self.dz * self.ratio) + ) + xf, yf, zf = self.round_to_grid( + (self.i1 * self.dx * self.ratio, self.j1 * self.dy * self.ratio, self.k1 * self.dz * self.ratio) + ) - logger.info('') - logger.debug(f'[{self.name}] Type: {self.__class__.__name__}') - logger.info(f'[{self.name}] Ratio: 1:{self.ratio}') - logger.info(f'[{self.name}] Spatial discretisation: {self.dx:g} x ' + - f'{self.dy:g} x {self.dz:g}m') - logger.info(f'[{self.name}] Extent (working region): {xs}m, {ys}m, {zs}m to {xf}m, {yf}m, {zf}m ' + - f'(({self.nwx} x {self.nwy} x {self.nwz} = {self.nwx * self.nwy * self.nwz} cells)') - logger.debug(f'[{self.name}] Total region: {self.nx:d} x {self.ny:d} x {self.nz:d} = ' + - f'{(self.nx * self.ny * self.nz):g} cells') - logger.info(f'[{self.name}] Time step: {self.dt:g} secs') \ No newline at end of file + logger.info("") + logger.debug(f"[{self.name}] Type: {self.__class__.__name__}") + logger.info(f"[{self.name}] Ratio: 1:{self.ratio}") + logger.info(f"[{self.name}] Spatial discretisation: {self.dx:g} x " + f"{self.dy:g} x {self.dz:g}m") + logger.info( + f"[{self.name}] Extent (working region): {xs}m, {ys}m, {zs}m to {xf}m, {yf}m, {zf}m " + + f"(({self.nwx} x {self.nwy} x {self.nwz} = {self.nwx * self.nwy * self.nwz} cells)" + ) + logger.debug( + f"[{self.name}] Total region: {self.nx:d} x {self.ny:d} x {self.nz:d} = " + + f"{(self.nx * self.ny * self.nz):g} cells" + ) + logger.info(f"[{self.name}] Time step: {self.dt:g} secs") diff --git a/gprMax/subgrids/updates.py b/gprMax/subgrids/updates.py index fc7f5d69..9e75a013 100644 --- a/gprMax/subgrids/updates.py +++ b/gprMax/subgrids/updates.py @@ -36,7 +36,7 @@ def create_updates(G): elif sg_type == SubGridHSG: precursors = PrecursorNodes(G, sg) else: - logger.exception(f'{str(sg)} is not a subgrid type') + logger.exception(f"{str(sg)} is not a subgrid type") raise ValueError sgu = SubgridUpdater(sg, precursors, G) @@ -65,17 +65,17 @@ class SubgridUpdates(CPUUpdates): class SubgridUpdater(CPUUpdates): - """Handles updating the electric and magnetic fields of an HSG subgrid. - The IS, OS, subgrid region and the electric/magnetic sources are updated - using the precursor regions. + """Handles updating the electric and magnetic fields of an HSG subgrid. + The IS, OS, subgrid region and the electric/magnetic sources are updated + using the precursor regions. """ def __init__(self, subgrid, precursors, G): """ Args: subgrid: SubGrid3d instance to be updated. - precursors (PrecursorNodes): PrecursorNodes instance nodes associated - with the subgrid - contain interpolated + precursors (PrecursorNodes): PrecursorNodes instance nodes associated + with the subgrid - contain interpolated fields. G: FDTDGrid class describing a grid in a model. """ @@ -85,8 +85,8 @@ class SubgridUpdater(CPUUpdates): self.source_iteration = 0 def hsg_1(self): - """First half of the subgrid update. Takes the time step up to the main - grid magnetic update. + """First half of the subgrid update. Takes the time step up to the main + grid magnetic update. """ G = self.G @@ -122,8 +122,8 @@ class SubgridUpdater(CPUUpdates): subgrid.update_electric_os(G) def hsg_2(self): - """Second half of the subgrid update. Takes the time step up to the main - grid electric update. + """Second half of the subgrid update. Takes the time step up to the main + grid electric update. """ G = self.G diff --git a/gprMax/subgrids/user_objects.py b/gprMax/subgrids/user_objects.py index ef6b38e1..00a77fc9 100644 --- a/gprMax/subgrids/user_objects.py +++ b/gprMax/subgrids/user_objects.py @@ -29,8 +29,8 @@ logger = logging.getLogger(__name__) class SubGridBase(UserObjectMulti): - """Allows UserObjectMulti and UserObjectGeometry to be nested in SubGrid - type user objects. + """Allows UserObjectMulti and UserObjectGeometry to be nested in SubGrid + type user objects. """ def __init__(self, **kwargs): @@ -45,7 +45,7 @@ class SubGridBase(UserObjectMulti): elif isinstance(node, UserObjectGeometry): self.children_geometry.append(node) else: - logger.exception(f'{str(node)} this Object can not be added to a sub grid') + logger.exception(f"{str(node)} this Object can not be added to a sub grid") raise ValueError def set_discretisation(self, sg, grid): @@ -64,7 +64,7 @@ class SubGridBase(UserObjectMulti): sg.x2, sg.y2, sg.z2 = uip.round_to_grid(p2) def set_name(self, sg): - sg.name = self.kwargs['id'] + sg.name = self.kwargs["id"] def set_working_region_cells(self, sg): """Number of cells in each dimension for the working region.""" @@ -83,21 +83,20 @@ class SubGridBase(UserObjectMulti): sg.iterations = main.iterations * sg.ratio def setup(self, sg, grid, uip): - """"Common setup to both all subgrid types.""" - p1 = self.kwargs['p1'] - p2 = self.kwargs['p2'] + """ "Common setup to both all subgrid types.""" + p1 = self.kwargs["p1"] + p2 = self.kwargs["p2"] p1, p2 = uip.check_box_points(p1, p2, self.__str__()) self.set_discretisation(sg, grid) - # Set temporal discretisation including any inherited time step + # Set temporal discretisation including any inherited time step # stability factor from the main grid sg.calculate_dt() if grid.dt_mod: sg.dt = sg.dt * grid.dt_mod - # Set the indices related to the subgrids main grid placement self.set_main_grid_indices(sg, uip, p1, p2) @@ -122,17 +121,17 @@ class SubGridBase(UserObjectMulti): sg.timewindow = grid.timewindow - # Copy a subgrid reference to self so that children.create(grid, uip) + # Copy a subgrid reference to self so that children.create(grid, uip) # can access the correct grid. self.subgrid = sg # Copy over built in materials - sg.materials = [copy(m) for m in grid.materials if m.type == 'builtin'] + sg.materials = [copy(m) for m in grid.materials if m.type == "builtin"] # Don't mix and match different subgrid types for sg_made in grid.subgrids: if type(sg) != type(sg_made): - logger.exception(f'{self.__str__()} please only use one type of subgrid') + logger.exception(f"{self.__str__()} please only use one type of subgrid") raise ValueError # Reference the subgrid under the main grid to which it belongs @@ -143,53 +142,55 @@ class SubGridHSG(SubGridBase): """Huygens Surface subgridding (HSG) user object. Attributes: - p1: list of the position of the lower left corner of the Inner Surface + p1: list of the position of the lower left corner of the Inner Surface (x, y, z) in the main grid. - p2: list of the position of the upper right corner of the Inner Surface + p2: list of the position of the upper right corner of the Inner Surface (x, y, z) in the main grid. - ratio: int of the ratio of the main grid spatial step to the sub-grid + ratio: int of the ratio of the main grid spatial step to the sub-grid spatial step. Must be an odd integer. id: string identifier for the sub-grid. - is_os_sep: int for the number of main grid cells between the Inner + is_os_sep: int for the number of main grid cells between the Inner Surface and the Outer Surface. Defaults to 3. - pml_separation: int for the number of sub-grid cells between the Outer + pml_separation: int for the number of sub-grid cells between the Outer Surface and the PML. Defaults to ratio // 2 + 2 - subgrid_pml_thickness: int for the thickness of the PML on each of the + subgrid_pml_thickness: int for the thickness of the PML on each of the 6 sides of the sub-grid. Defaults to 6. - interpolation: string for the degree of the interpolation scheme used - for spatial interpolation of the fields at the Inner + interpolation: string for the degree of the interpolation scheme used + for spatial interpolation of the fields at the Inner Surface. Defaults to Linear. - filter: boolean to turn on the 3-pole filter. Increases numerical + filter: boolean to turn on the 3-pole filter. Increases numerical stability. Defaults to True. """ - def __init__(self, - p1=None, - p2=None, - ratio=3, - id='', - is_os_sep=3, - pml_separation=4, - subgrid_pml_thickness=6, - interpolation=1, - filter=True, - **kwargs): + def __init__( + self, + p1=None, + p2=None, + ratio=3, + id="", + is_os_sep=3, + pml_separation=4, + subgrid_pml_thickness=6, + interpolation=1, + filter=True, + **kwargs, + ): pml_separation = ratio // 2 + 2 # Copy over the optional parameters - kwargs['p1'] = p1 - kwargs['p2'] = p2 - kwargs['ratio'] = ratio - kwargs['id'] = id - kwargs['is_os_sep'] = is_os_sep - kwargs['pml_separation'] = pml_separation - kwargs['subgrid_pml_thickness'] = subgrid_pml_thickness - kwargs['interpolation'] = interpolation - kwargs['filter'] = filter + kwargs["p1"] = p1 + kwargs["p2"] = p2 + kwargs["ratio"] = ratio + kwargs["id"] = id + kwargs["is_os_sep"] = is_os_sep + kwargs["pml_separation"] = pml_separation + kwargs["subgrid_pml_thickness"] = subgrid_pml_thickness + kwargs["interpolation"] = interpolation + kwargs["filter"] = filter super().__init__(**kwargs) self.order = 18 - self.hash = '#subgrid_hsg' + self.hash = "#subgrid_hsg" def create(self, grid, uip): sg = SubGridHSGUser(**self.kwargs) diff --git a/gprMax/updates.py b/gprMax/updates.py index 8c05f560..34973420 100644 --- a/gprMax/updates.py +++ b/gprMax/updates.py @@ -24,12 +24,9 @@ from jinja2 import Environment, PackageLoader import gprMax.config as config -from .cuda_opencl import (knl_fields_updates, knl_snapshots, - knl_source_updates, knl_store_outputs) -from .cython.fields_updates_normal import \ - update_electric as update_electric_cpu -from .cython.fields_updates_normal import \ - update_magnetic as update_magnetic_cpu +from .cuda_opencl import knl_fields_updates, knl_snapshots, knl_source_updates, knl_store_outputs +from .cython.fields_updates_normal import update_electric as update_electric_cpu +from .cython.fields_updates_normal import update_magnetic as update_magnetic_cpu from .fields_outputs import store_outputs as store_outputs_cpu from .receivers import dtoh_rx_array, htod_rx_arrays from .snapshots import Snapshot, dtoh_snapshot_array, htod_snapshot_array @@ -67,127 +64,143 @@ class CPUUpdates: def update_magnetic(self): """Updates magnetic field components.""" - update_magnetic_cpu(self.grid.nx, - self.grid.ny, - self.grid.nz, - config.get_model_config().ompthreads, - self.grid.updatecoeffsH, - self.grid.ID, - self.grid.Ex, - self.grid.Ey, - self.grid.Ez, - self.grid.Hx, - self.grid.Hy, - self.grid.Hz) + update_magnetic_cpu( + self.grid.nx, + self.grid.ny, + self.grid.nz, + config.get_model_config().ompthreads, + self.grid.updatecoeffsH, + self.grid.ID, + self.grid.Ex, + self.grid.Ey, + self.grid.Ez, + self.grid.Hx, + self.grid.Hy, + self.grid.Hz, + ) def update_magnetic_pml(self): """Updates magnetic field components with the PML correction.""" - for pml in self.grid.pmls['slabs']: + for pml in self.grid.pmls["slabs"]: pml.update_magnetic() def update_magnetic_sources(self): """Updates magnetic field components from sources.""" for source in self.grid.transmissionlines + self.grid.magneticdipoles: - source.update_magnetic(self.grid.iteration, - self.grid.updatecoeffsH, - self.grid.ID, - self.grid.Hx, - self.grid.Hy, - self.grid.Hz, - self.grid) + source.update_magnetic( + self.grid.iteration, + self.grid.updatecoeffsH, + self.grid.ID, + self.grid.Hx, + self.grid.Hy, + self.grid.Hz, + self.grid, + ) def update_electric_a(self): """Updates electric field components.""" # All materials are non-dispersive so do standard update. - if config.get_model_config().materials['maxpoles'] == 0: - update_electric_cpu(self.grid.nx, - self.grid.ny, - self.grid.nz, - config.get_model_config().ompthreads, - self.grid.updatecoeffsE, - self.grid.ID, - self.grid.Ex, - self.grid.Ey, - self.grid.Ez, - self.grid.Hx, - self.grid.Hy, - self.grid.Hz) + if config.get_model_config().materials["maxpoles"] == 0: + update_electric_cpu( + self.grid.nx, + self.grid.ny, + self.grid.nz, + config.get_model_config().ompthreads, + self.grid.updatecoeffsE, + self.grid.ID, + self.grid.Ex, + self.grid.Ey, + self.grid.Ez, + self.grid.Hx, + self.grid.Hy, + self.grid.Hz, + ) # If there are any dispersive materials do 1st part of dispersive update # (it is split into two parts as it requires present and updated electric field values). else: - self.dispersive_update_a(self.grid.nx, - self.grid.ny, - self.grid.nz, - config.get_model_config().ompthreads, - config.get_model_config().materials['maxpoles'], - self.grid.updatecoeffsE, - self.grid.updatecoeffsdispersive, - self.grid.ID, - self.grid.Tx, - self.grid.Ty, - self.grid.Tz, - self.grid.Ex, - self.grid.Ey, - self.grid.Ez, - self.grid.Hx, - self.grid.Hy, - self.grid.Hz) + self.dispersive_update_a( + self.grid.nx, + self.grid.ny, + self.grid.nz, + config.get_model_config().ompthreads, + config.get_model_config().materials["maxpoles"], + self.grid.updatecoeffsE, + self.grid.updatecoeffsdispersive, + self.grid.ID, + self.grid.Tx, + self.grid.Ty, + self.grid.Tz, + self.grid.Ex, + self.grid.Ey, + self.grid.Ez, + self.grid.Hx, + self.grid.Hy, + self.grid.Hz, + ) def update_electric_pml(self): """Updates electric field components with the PML correction.""" - for pml in self.grid.pmls['slabs']: + for pml in self.grid.pmls["slabs"]: pml.update_electric() def update_electric_sources(self): """Updates electric field components from sources - - update any Hertzian dipole sources last. + update any Hertzian dipole sources last. """ for source in self.grid.voltagesources + self.grid.transmissionlines + self.grid.hertziandipoles: - source.update_electric(self.grid.iteration, - self.grid.updatecoeffsE, - self.grid.ID, - self.grid.Ex, - self.grid.Ey, - self.grid.Ez, - self.grid) + source.update_electric( + self.grid.iteration, + self.grid.updatecoeffsE, + self.grid.ID, + self.grid.Ex, + self.grid.Ey, + self.grid.Ez, + self.grid, + ) self.grid.iteration += 1 def update_electric_b(self): """If there are any dispersive materials do 2nd part of dispersive - update - it is split into two parts as it requires present and - updated electric field values. Therefore it can only be completely - updated after the electric field has been updated by the PML and - source updates. + update - it is split into two parts as it requires present and + updated electric field values. Therefore it can only be completely + updated after the electric field has been updated by the PML and + source updates. """ - if config.get_model_config().materials['maxpoles'] != 0: - self.dispersive_update_b(self.grid.nx, - self.grid.ny, - self.grid.nz, - config.get_model_config().ompthreads, - config.get_model_config().materials['maxpoles'], - self.grid.updatecoeffsdispersive, - self.grid.ID, - self.grid.Tx, - self.grid.Ty, - self.grid.Tz, - self.grid.Ex, - self.grid.Ey, - self.grid.Ez) + if config.get_model_config().materials["maxpoles"] != 0: + self.dispersive_update_b( + self.grid.nx, + self.grid.ny, + self.grid.nz, + config.get_model_config().ompthreads, + config.get_model_config().materials["maxpoles"], + self.grid.updatecoeffsdispersive, + self.grid.ID, + self.grid.Tx, + self.grid.Ty, + self.grid.Tz, + self.grid.Ex, + self.grid.Ey, + self.grid.Ez, + ) def set_dispersive_updates(self): """Sets dispersive update functions.""" - poles = 'multi' if config.get_model_config().materials['maxpoles'] > 1 else '1' - precision = 'float' if config.sim_config.general['precision'] == 'single' else 'double' - dispersion = 'complex' if config.get_model_config().materials['dispersivedtype'] == config.sim_config.dtypes['complex'] else 'real' + poles = "multi" if config.get_model_config().materials["maxpoles"] > 1 else "1" + precision = "float" if config.sim_config.general["precision"] == "single" else "double" + dispersion = ( + "complex" + if config.get_model_config().materials["dispersivedtype"] == config.sim_config.dtypes["complex"] + else "real" + ) - update_f = 'update_electric_dispersive_{}pole_{}_{}_{}' - disp_a = update_f.format(poles, 'A', precision, dispersion) - disp_b = update_f.format(poles, 'B', precision, dispersion) - - disp_a_f = getattr(import_module('gprMax.cython.fields_updates_dispersive'), disp_a) - disp_b_f = getattr(import_module('gprMax.cython.fields_updates_dispersive'), disp_b) + update_f = "update_electric_dispersive_{}pole_{}_{}_{}" + disp_a = update_f.format(poles, "A", precision, dispersion) + disp_b = update_f.format(poles, "B", precision, dispersion) + + disp_a_f = getattr(import_module("gprMax.cython.fields_updates_dispersive"), disp_a) + disp_b_f = getattr(import_module("gprMax.cython.fields_updates_dispersive"), disp_b) self.dispersive_update_a = disp_a_f self.dispersive_update_b = disp_b_f @@ -221,35 +234,39 @@ class CUDAUpdates: self.dispersive_update_b = None # Import PyCUDA modules - self.drv = import_module('pycuda.driver') - self.source_module = getattr(import_module('pycuda.compiler'), 'SourceModule') + self.drv = import_module("pycuda.driver") + self.source_module = getattr(import_module("pycuda.compiler"), "SourceModule") self.drv.init() # Create device handle and context on specific GPU device (and make it current context) - self.dev = config.get_model_config().device['dev'] + self.dev = config.get_model_config().device["dev"] self.ctx = self.dev.make_context() # Set common substitutions for use in kernels # Substitutions in function arguments - self.subs_name_args = {'REAL': config.sim_config.dtypes['C_float_or_double'], - 'COMPLEX': config.get_model_config().materials['dispersiveCdtype']} + self.subs_name_args = { + "REAL": config.sim_config.dtypes["C_float_or_double"], + "COMPLEX": config.get_model_config().materials["dispersiveCdtype"], + } # Substitutions in function bodies - self.subs_func = {'REAL': config.sim_config.dtypes['C_float_or_double'], - 'CUDA_IDX': 'int i = blockIdx.x * blockDim.x + threadIdx.x;', - 'NX_FIELDS': self.grid.nx + 1, - 'NY_FIELDS': self.grid.ny + 1, - 'NZ_FIELDS': self.grid.nz + 1, - 'NX_ID': self.grid.ID.shape[1], - 'NY_ID': self.grid.ID.shape[2], - 'NZ_ID': self.grid.ID.shape[3]} + self.subs_func = { + "REAL": config.sim_config.dtypes["C_float_or_double"], + "CUDA_IDX": "int i = blockIdx.x * blockDim.x + threadIdx.x;", + "NX_FIELDS": self.grid.nx + 1, + "NY_FIELDS": self.grid.ny + 1, + "NZ_FIELDS": self.grid.nz + 1, + "NX_ID": self.grid.ID.shape[1], + "NY_ID": self.grid.ID.shape[2], + "NZ_ID": self.grid.ID.shape[3], + } # Enviroment for templating kernels - self.env = Environment(loader=PackageLoader('gprMax', 'cuda_opencl')) + self.env = Environment(loader=PackageLoader("gprMax", "cuda_opencl")) # Initialise arrays on GPU, prepare kernels, and get kernel functions self._set_macros() self._set_field_knls() - if self.grid.pmls['slabs']: + if self.grid.pmls["slabs"]: self._set_pml_knls() if self.grid.rxs: self._set_rx_knl() @@ -260,12 +277,12 @@ class CUDAUpdates: def _build_knl(self, knl_func, subs_name_args, subs_func): """Builds a CUDA kernel from templates: 1) function name and args; - and 2) function (kernel) body. + and 2) function (kernel) body. Args: knl_func: dict containing templates for function name and args, and function body. - subs_name_args: dict containing substitutions to be used with + subs_name_args: dict containing substitutions to be used with function name and args. subs_func: dict containing substitutions to be used with function (kernel) body. @@ -274,184 +291,198 @@ class CUDAUpdates: knl: string with complete kernel """ - name_plus_args = knl_func['args_cuda'].substitute(subs_name_args) - func_body = knl_func['func'].substitute(subs_func) - knl = self.knl_common + '\n' + name_plus_args + '{' + func_body + '}' + name_plus_args = knl_func["args_cuda"].substitute(subs_name_args) + func_body = knl_func["func"].substitute(subs_func) + knl = self.knl_common + "\n" + name_plus_args + "{" + func_body + "}" return knl def _set_macros(self): """Common macros to be used in kernels.""" - # Set specific values for any dispersive materials - if config.get_model_config().materials['maxpoles'] > 0: + # Set specific values for any dispersive materials + if config.get_model_config().materials["maxpoles"] > 0: NY_MATDISPCOEFFS = self.grid.updatecoeffsdispersive.shape[1] NX_T = self.grid.Tx.shape[1] NY_T = self.grid.Tx.shape[2] NZ_T = self.grid.Tx.shape[3] - else: # Set to one any substitutions for dispersive materials. + else: # Set to one any substitutions for dispersive materials. NY_MATDISPCOEFFS = 1 NX_T = 1 NY_T = 1 NZ_T = 1 - self.knl_common = self.env.get_template('knl_common_cuda.tmpl').render( - REAL=config.sim_config.dtypes['C_float_or_double'], - N_updatecoeffsE=self.grid.updatecoeffsE.size, - N_updatecoeffsH=self.grid.updatecoeffsH.size, - NY_MATCOEFFS=self.grid.updatecoeffsE.shape[1], - NY_MATDISPCOEFFS=NY_MATDISPCOEFFS, - NX_FIELDS=self.grid.nx + 1, - NY_FIELDS=self.grid.ny + 1, - NZ_FIELDS=self.grid.nz + 1, - NX_ID=self.grid.ID.shape[1], - NY_ID=self.grid.ID.shape[2], - NZ_ID=self.grid.ID.shape[3], - NX_T=NX_T, - NY_T=NY_T, - NZ_T=NZ_T, - NY_RXCOORDS=3, - NX_RXS=6, - NY_RXS=self.grid.iterations, - NZ_RXS=len(self.grid.rxs), - NY_SRCINFO=4, - NY_SRCWAVES=self.grid.iterations, - NX_SNAPS=Snapshot.nx_max, - NY_SNAPS=Snapshot.ny_max, - NZ_SNAPS=Snapshot.nz_max) + self.knl_common = self.env.get_template("knl_common_cuda.tmpl").render( + REAL=config.sim_config.dtypes["C_float_or_double"], + N_updatecoeffsE=self.grid.updatecoeffsE.size, + N_updatecoeffsH=self.grid.updatecoeffsH.size, + NY_MATCOEFFS=self.grid.updatecoeffsE.shape[1], + NY_MATDISPCOEFFS=NY_MATDISPCOEFFS, + NX_FIELDS=self.grid.nx + 1, + NY_FIELDS=self.grid.ny + 1, + NZ_FIELDS=self.grid.nz + 1, + NX_ID=self.grid.ID.shape[1], + NY_ID=self.grid.ID.shape[2], + NZ_ID=self.grid.ID.shape[3], + NX_T=NX_T, + NY_T=NY_T, + NZ_T=NZ_T, + NY_RXCOORDS=3, + NX_RXS=6, + NY_RXS=self.grid.iterations, + NZ_RXS=len(self.grid.rxs), + NY_SRCINFO=4, + NY_SRCWAVES=self.grid.iterations, + NX_SNAPS=Snapshot.nx_max, + NY_SNAPS=Snapshot.ny_max, + NZ_SNAPS=Snapshot.nz_max, + ) def _set_field_knls(self): """Electric and magnetic field updates - prepares kernels, and - gets kernel functions. + gets kernel functions. """ - - bld = self._build_knl(knl_fields_updates.update_electric, - self.subs_name_args, self.subs_func) - knlE = self.source_module(bld, - options=config.sim_config.devices['nvcc_opts']) + + bld = self._build_knl(knl_fields_updates.update_electric, self.subs_name_args, self.subs_func) + knlE = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) self.update_electric_dev = knlE.get_function("update_electric") - bld = self._build_knl(knl_fields_updates.update_magnetic, - self.subs_name_args, self.subs_func) - knlH = self.source_module(bld, - options=config.sim_config.devices['nvcc_opts']) + bld = self._build_knl(knl_fields_updates.update_magnetic, self.subs_name_args, self.subs_func) + knlH = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) self.update_magnetic_dev = knlH.get_function("update_magnetic") self._copy_mat_coeffs(knlE, knlH) # If there are any dispersive materials (updates are split into two # parts as they require present and updated electric field values). - if config.get_model_config().materials['maxpoles'] > 0: - self.subs_func.update({'REAL': config.sim_config.dtypes['C_float_or_double'], - 'REALFUNC': config.get_model_config().materials['cudarealfunc'], - 'NX_T': self.grid.Tx.shape[1], - 'NY_T': self.grid.Tx.shape[2], - 'NZ_T': self.grid.Tx.shape[3]}) + if config.get_model_config().materials["maxpoles"] > 0: + self.subs_func.update( + { + "REAL": config.sim_config.dtypes["C_float_or_double"], + "REALFUNC": config.get_model_config().materials["cudarealfunc"], + "NX_T": self.grid.Tx.shape[1], + "NY_T": self.grid.Tx.shape[2], + "NZ_T": self.grid.Tx.shape[3], + } + ) - bld = self._build_knl(knl_fields_updates.update_electric_dispersive_A, - self.subs_name_args, self.subs_func) - knl = self.source_module(bld, - options=config.sim_config.devices['nvcc_opts']) + bld = self._build_knl(knl_fields_updates.update_electric_dispersive_A, self.subs_name_args, self.subs_func) + knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) self.dispersive_update_a = knl.get_function("update_electric_dispersive_A") - bld = self._build_knl(knl_fields_updates.update_electric_dispersive_B, - self.subs_name_args, self.subs_func) - knl = self.source_module(bld, - options=config.sim_config.devices['nvcc_opts']) + bld = self._build_knl(knl_fields_updates.update_electric_dispersive_B, self.subs_name_args, self.subs_func) + knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) self.dispersive_update_b = knl.get_function("update_electric_dispersive_B") # Set blocks per grid and initialise field arrays on GPU self.grid.set_blocks_per_grid() self.grid.htod_geometry_arrays() self.grid.htod_field_arrays() - if config.get_model_config().materials['maxpoles'] > 0: + if config.get_model_config().materials["maxpoles"] > 0: self.grid.htod_dispersive_arrays() def _set_pml_knls(self): """PMLS - prepares kernels and gets kernel functions.""" - knl_pml_updates_electric = import_module('gprMax.cuda_opencl.knl_pml_updates_electric_' + - self.grid.pmls['formulation']) - knl_pml_updates_magnetic = import_module('gprMax.cuda_opencl.knl_pml_updates_magnetic_' + - self.grid.pmls['formulation']) + knl_pml_updates_electric = import_module( + "gprMax.cuda_opencl.knl_pml_updates_electric_" + self.grid.pmls["formulation"] + ) + knl_pml_updates_magnetic = import_module( + "gprMax.cuda_opencl.knl_pml_updates_magnetic_" + self.grid.pmls["formulation"] + ) # Initialise arrays on GPU, set block per grid, and get kernel functions - for pml in self.grid.pmls['slabs']: + for pml in self.grid.pmls["slabs"]: pml.htod_field_arrays() pml.set_blocks_per_grid() - knl_name = f'order{len(pml.CFS)}_{pml.direction}' - self.subs_name_args['FUNC'] = knl_name + knl_name = f"order{len(pml.CFS)}_{pml.direction}" + self.subs_name_args["FUNC"] = knl_name knl_electric = getattr(knl_pml_updates_electric, knl_name) bld = self._build_knl(knl_electric, self.subs_name_args, self.subs_func) - knlE = self.source_module(bld, options=config.sim_config.devices['nvcc_opts']) + knlE = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) pml.update_electric_dev = knlE.get_function(knl_name) knl_magnetic = getattr(knl_pml_updates_magnetic, knl_name) bld = self._build_knl(knl_magnetic, self.subs_name_args, self.subs_func) - knlH = self.source_module(bld, options=config.sim_config.devices['nvcc_opts']) + knlH = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) pml.update_magnetic_dev = knlH.get_function(knl_name) self._copy_mat_coeffs(knlE, knlH) def _set_rx_knl(self): """Receivers - initialises arrays on GPU, prepares kernel and gets kernel - function. + function. """ self.rxcoords_dev, self.rxs_dev = htod_rx_arrays(self.grid) - self.subs_func.update({'REAL': config.sim_config.dtypes['C_float_or_double'], - 'NY_RXCOORDS': 3, - 'NX_RXS': 6, - 'NY_RXS': self.grid.iterations, - 'NZ_RXS': len(self.grid.rxs)}) + self.subs_func.update( + { + "REAL": config.sim_config.dtypes["C_float_or_double"], + "NY_RXCOORDS": 3, + "NX_RXS": 6, + "NY_RXS": self.grid.iterations, + "NZ_RXS": len(self.grid.rxs), + } + ) - bld = self._build_knl(knl_store_outputs.store_outputs, - self.subs_name_args, self.subs_func) - knl = self.source_module(bld, options=config.sim_config.devices['nvcc_opts']) + bld = self._build_knl(knl_store_outputs.store_outputs, self.subs_name_args, self.subs_func) + knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) self.store_outputs_dev = knl.get_function("store_outputs") def _set_src_knls(self): """Sources - initialises arrays on GPU, prepares kernel and gets kernel - function. + function. """ - self.subs_func.update({'NY_SRCINFO': 4, - 'NY_SRCWAVES': self.grid.iteration}) - + self.subs_func.update({"NY_SRCINFO": 4, "NY_SRCWAVES": self.grid.iteration}) + if self.grid.hertziandipoles: - self.srcinfo1_hertzian_dev, self.srcinfo2_hertzian_dev, self.srcwaves_hertzian_dev = htod_src_arrays(self.grid.hertziandipoles, self.grid) - bld = self._build_knl(knl_source_updates.update_hertzian_dipole, - self.subs_name_args, self.subs_func) - knl = self.source_module(bld, options=config.sim_config.devices['nvcc_opts']) + self.srcinfo1_hertzian_dev, self.srcinfo2_hertzian_dev, self.srcwaves_hertzian_dev = htod_src_arrays( + self.grid.hertziandipoles, self.grid + ) + bld = self._build_knl(knl_source_updates.update_hertzian_dipole, self.subs_name_args, self.subs_func) + knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) self.update_hertzian_dipole_dev = knl.get_function("update_hertzian_dipole") if self.grid.magneticdipoles: - self.srcinfo1_magnetic_dev, self.srcinfo2_magnetic_dev, self.srcwaves_magnetic_dev = htod_src_arrays(self.grid.magneticdipoles, self.grid) - bld = self._build_knl(knl_source_updates.update_magnetic_dipole, - self.subs_name_args, self.subs_func) - knl = self.source_module(bld, options=config.sim_config.devices['nvcc_opts']) + self.srcinfo1_magnetic_dev, self.srcinfo2_magnetic_dev, self.srcwaves_magnetic_dev = htod_src_arrays( + self.grid.magneticdipoles, self.grid + ) + bld = self._build_knl(knl_source_updates.update_magnetic_dipole, self.subs_name_args, self.subs_func) + knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) self.update_magnetic_dipole_dev = knl.get_function("update_magnetic_dipole") if self.grid.voltagesources: - self.srcinfo1_voltage_dev, self.srcinfo2_voltage_dev, self.srcwaves_voltage_dev = htod_src_arrays(self.grid.voltagesources, self.grid) - bld = self._build_knl(knl_source_updates.update_voltage_source, - self.subs_name_args, self.subs_func) - knl = self.source_module(bld, options=config.sim_config.devices['nvcc_opts']) + self.srcinfo1_voltage_dev, self.srcinfo2_voltage_dev, self.srcwaves_voltage_dev = htod_src_arrays( + self.grid.voltagesources, self.grid + ) + bld = self._build_knl(knl_source_updates.update_voltage_source, self.subs_name_args, self.subs_func) + knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) self.update_voltage_source_dev = knl.get_function("update_voltage_source") self._copy_mat_coeffs(knl, knl) def _set_snapshot_knl(self): """Snapshots - initialises arrays on GPU, prepares kernel and gets kernel - function. + function. """ - self.snapEx_dev, self.snapEy_dev, self.snapEz_dev, self.snapHx_dev, self.snapHy_dev, self.snapHz_dev = htod_snapshot_array(self.grid) + ( + self.snapEx_dev, + self.snapEy_dev, + self.snapEz_dev, + self.snapHx_dev, + self.snapHy_dev, + self.snapHz_dev, + ) = htod_snapshot_array(self.grid) - self.subs_func.update({'REAL': config.sim_config.dtypes['C_float_or_double'], - 'NX_SNAPS': Snapshot.nx_max, - 'NY_SNAPS': Snapshot.ny_max, - 'NZ_SNAPS': Snapshot.nz_max}) + self.subs_func.update( + { + "REAL": config.sim_config.dtypes["C_float_or_double"], + "NX_SNAPS": Snapshot.nx_max, + "NY_SNAPS": Snapshot.ny_max, + "NZ_SNAPS": Snapshot.nz_max, + } + ) - bld = self._build_knl(knl_snapshots.store_snapshot, - self.subs_name_args, self.subs_func) - knl = self.source_module(bld, options=config.sim_config.devices['nvcc_opts']) + bld = self._build_knl(knl_snapshots.store_snapshot, self.subs_name_args, self.subs_func) + knl = self.source_module(bld, options=config.sim_config.devices["nvcc_opts"]) self.store_snapshot_dev = knl.get_function("store_snapshot") def _copy_mat_coeffs(self, knlE, knlH): @@ -464,34 +495,40 @@ class CUDAUpdates: """ # Check if coefficient arrays will fit on constant memory of GPU - if (self.grid.updatecoeffsE.nbytes + self.grid.updatecoeffsH.nbytes - > config.get_model_config().device['dev'].total_constant_memory): - device = config.get_model_config().device['dev'] - logger.exception(f"Too many materials in the model to fit onto " + - f"constant memory of size {humanize.naturalsize(device.total_constant_memory)} " + - f"on {device.deviceID}: {' '.join(device.name().split())}") + if ( + self.grid.updatecoeffsE.nbytes + self.grid.updatecoeffsH.nbytes + > config.get_model_config().device["dev"].total_constant_memory + ): + device = config.get_model_config().device["dev"] + logger.exception( + f"Too many materials in the model to fit onto " + + f"constant memory of size {humanize.naturalsize(device.total_constant_memory)} " + + f"on {device.deviceID}: {' '.join(device.name().split())}" + ) raise ValueError - updatecoeffsE = knlE.get_global('updatecoeffsE')[0] - updatecoeffsH = knlH.get_global('updatecoeffsH')[0] + updatecoeffsE = knlE.get_global("updatecoeffsE")[0] + updatecoeffsH = knlH.get_global("updatecoeffsH")[0] self.drv.memcpy_htod(updatecoeffsE, self.grid.updatecoeffsE) self.drv.memcpy_htod(updatecoeffsH, self.grid.updatecoeffsH) def store_outputs(self): """Stores field component values for every receiver.""" if self.grid.rxs: - self.store_outputs_dev(np.int32(len(self.grid.rxs)), - np.int32(self.grid.iteration), - self.rxcoords_dev.gpudata, - self.rxs_dev.gpudata, - self.grid.Ex_dev.gpudata, - self.grid.Ey_dev.gpudata, - self.grid.Ez_dev.gpudata, - self.grid.Hx_dev.gpudata, - self.grid.Hy_dev.gpudata, - self.grid.Hz_dev.gpudata, - block=(1, 1, 1), - grid=(round32(len(self.grid.rxs)), 1, 1)) + self.store_outputs_dev( + np.int32(len(self.grid.rxs)), + np.int32(self.grid.iteration), + self.rxcoords_dev.gpudata, + self.rxs_dev.gpudata, + self.grid.Ex_dev.gpudata, + self.grid.Ey_dev.gpudata, + self.grid.Ez_dev.gpudata, + self.grid.Hx_dev.gpudata, + self.grid.Hy_dev.gpudata, + self.grid.Hz_dev.gpudata, + block=(1, 1, 1), + grid=(round32(len(self.grid.rxs)), 1, 1), + ) def store_snapshots(self, iteration): """Stores any snapshots. @@ -502,181 +539,200 @@ class CUDAUpdates: for i, snap in enumerate(self.grid.snapshots): if snap.time == iteration + 1: - snapno = 0 if config.get_model_config().device['snapsgpu2cpu'] else i - self.store_snapshot_dev(np.int32(snapno), - np.int32(snap.xs), - np.int32(snap.xf), - np.int32(snap.ys), - np.int32(snap.yf), - np.int32(snap.zs), - np.int32(snap.zf), - np.int32(snap.dx), - np.int32(snap.dy), - np.int32(snap.dz), - self.grid.Ex_dev.gpudata, - self.grid.Ey_dev.gpudata, - self.grid.Ez_dev.gpudata, - self.grid.Hx_dev.gpudata, - self.grid.Hy_dev.gpudata, - self.grid.Hz_dev.gpudata, - self.snapEx_dev.gpudata, - self.snapEy_dev.gpudata, - self.snapEz_dev.gpudata, - self.snapHx_dev.gpudata, - self.snapHy_dev.gpudata, - self.snapHz_dev.gpudata, - block=Snapshot.tpb, - grid=Snapshot.bpg) - if config.get_model_config().device['snapsgpu2cpu']: - dtoh_snapshot_array(self.snapEx_dev.get(), - self.snapEy_dev.get(), - self.snapEz_dev.get(), - self.snapHx_dev.get(), - self.snapHy_dev.get(), - self.snapHz_dev.get(), - 0, snap) + snapno = 0 if config.get_model_config().device["snapsgpu2cpu"] else i + self.store_snapshot_dev( + np.int32(snapno), + np.int32(snap.xs), + np.int32(snap.xf), + np.int32(snap.ys), + np.int32(snap.yf), + np.int32(snap.zs), + np.int32(snap.zf), + np.int32(snap.dx), + np.int32(snap.dy), + np.int32(snap.dz), + self.grid.Ex_dev.gpudata, + self.grid.Ey_dev.gpudata, + self.grid.Ez_dev.gpudata, + self.grid.Hx_dev.gpudata, + self.grid.Hy_dev.gpudata, + self.grid.Hz_dev.gpudata, + self.snapEx_dev.gpudata, + self.snapEy_dev.gpudata, + self.snapEz_dev.gpudata, + self.snapHx_dev.gpudata, + self.snapHy_dev.gpudata, + self.snapHz_dev.gpudata, + block=Snapshot.tpb, + grid=Snapshot.bpg, + ) + if config.get_model_config().device["snapsgpu2cpu"]: + dtoh_snapshot_array( + self.snapEx_dev.get(), + self.snapEy_dev.get(), + self.snapEz_dev.get(), + self.snapHx_dev.get(), + self.snapHy_dev.get(), + self.snapHz_dev.get(), + 0, + snap, + ) def update_magnetic(self): """Updates magnetic field components.""" - self.update_magnetic_dev(np.int32(self.grid.nx), - np.int32(self.grid.ny), - np.int32(self.grid.nz), - self.grid.ID_dev.gpudata, - self.grid.Hx_dev.gpudata, - self.grid.Hy_dev.gpudata, - self.grid.Hz_dev.gpudata, - self.grid.Ex_dev.gpudata, - self.grid.Ey_dev.gpudata, - self.grid.Ez_dev.gpudata, - block=self.grid.tpb, - grid=self.grid.bpg) + self.update_magnetic_dev( + np.int32(self.grid.nx), + np.int32(self.grid.ny), + np.int32(self.grid.nz), + self.grid.ID_dev.gpudata, + self.grid.Hx_dev.gpudata, + self.grid.Hy_dev.gpudata, + self.grid.Hz_dev.gpudata, + self.grid.Ex_dev.gpudata, + self.grid.Ey_dev.gpudata, + self.grid.Ez_dev.gpudata, + block=self.grid.tpb, + grid=self.grid.bpg, + ) def update_magnetic_pml(self): """Updates magnetic field components with the PML correction.""" - for pml in self.grid.pmls['slabs']: + for pml in self.grid.pmls["slabs"]: pml.update_magnetic() def update_magnetic_sources(self): """Updates magnetic field components from sources.""" if self.grid.magneticdipoles: - self.update_magnetic_dipole_dev(np.int32(len(self.grid.magneticdipoles)), - np.int32(self.grid.iteration), - config.sim_config.dtypes['float_or_double'](self.grid.dx), - config.sim_config.dtypes['float_or_double'](self.grid.dy), - config.sim_config.dtypes['float_or_double'](self.grid.dz), - self.srcinfo1_magnetic_dev.gpudata, - self.srcinfo2_magnetic_dev.gpudata, - self.srcwaves_magnetic_dev.gpudata, - self.grid.ID_dev.gpudata, - self.grid.Hx_dev.gpudata, - self.grid.Hy_dev.gpudata, - self.grid.Hz_dev.gpudata, - block=(1, 1, 1), - grid=(round32(len(self.grid.magneticdipoles)), 1, 1)) + self.update_magnetic_dipole_dev( + np.int32(len(self.grid.magneticdipoles)), + np.int32(self.grid.iteration), + config.sim_config.dtypes["float_or_double"](self.grid.dx), + config.sim_config.dtypes["float_or_double"](self.grid.dy), + config.sim_config.dtypes["float_or_double"](self.grid.dz), + self.srcinfo1_magnetic_dev.gpudata, + self.srcinfo2_magnetic_dev.gpudata, + self.srcwaves_magnetic_dev.gpudata, + self.grid.ID_dev.gpudata, + self.grid.Hx_dev.gpudata, + self.grid.Hy_dev.gpudata, + self.grid.Hz_dev.gpudata, + block=(1, 1, 1), + grid=(round32(len(self.grid.magneticdipoles)), 1, 1), + ) def update_electric_a(self): """Updates electric field components.""" # All materials are non-dispersive so do standard update. - if config.get_model_config().materials['maxpoles'] == 0: - self.update_electric_dev(np.int32(self.grid.nx), - np.int32(self.grid.ny), - np.int32(self.grid.nz), - self.grid.ID_dev.gpudata, - self.grid.Ex_dev.gpudata, - self.grid.Ey_dev.gpudata, - self.grid.Ez_dev.gpudata, - self.grid.Hx_dev.gpudata, - self.grid.Hy_dev.gpudata, - self.grid.Hz_dev.gpudata, - block=self.grid.tpb, - grid=self.grid.bpg) + if config.get_model_config().materials["maxpoles"] == 0: + self.update_electric_dev( + np.int32(self.grid.nx), + np.int32(self.grid.ny), + np.int32(self.grid.nz), + self.grid.ID_dev.gpudata, + self.grid.Ex_dev.gpudata, + self.grid.Ey_dev.gpudata, + self.grid.Ez_dev.gpudata, + self.grid.Hx_dev.gpudata, + self.grid.Hy_dev.gpudata, + self.grid.Hz_dev.gpudata, + block=self.grid.tpb, + grid=self.grid.bpg, + ) # If there are any dispersive materials do 1st part of dispersive update # (it is split into two parts as it requires present and updated electric field values). else: - self.dispersive_update_a(np.int32(self.grid.nx), - np.int32(self.grid.ny), - np.int32(self.grid.nz), - np.int32(config.get_model_config().materials['maxpoles']), - self.grid.updatecoeffsdispersive_dev.gpudata, - self.grid.Tx_dev.gpudata, - self.grid.Ty_dev.gpudata, - self.grid.Tz_dev.gpudata, - self.grid.ID_dev.gpudata, - self.grid.Ex_dev.gpudata, - self.grid.Ey_dev.gpudata, - self.grid.Ez_dev.gpudata, - self.grid.Hx_dev.gpudata, - self.grid.Hy_dev.gpudata, - self.grid.Hz_dev.gpudata, - block=self.grid.tpb, - grid=self.grid.bpg) + self.dispersive_update_a( + np.int32(self.grid.nx), + np.int32(self.grid.ny), + np.int32(self.grid.nz), + np.int32(config.get_model_config().materials["maxpoles"]), + self.grid.updatecoeffsdispersive_dev.gpudata, + self.grid.Tx_dev.gpudata, + self.grid.Ty_dev.gpudata, + self.grid.Tz_dev.gpudata, + self.grid.ID_dev.gpudata, + self.grid.Ex_dev.gpudata, + self.grid.Ey_dev.gpudata, + self.grid.Ez_dev.gpudata, + self.grid.Hx_dev.gpudata, + self.grid.Hy_dev.gpudata, + self.grid.Hz_dev.gpudata, + block=self.grid.tpb, + grid=self.grid.bpg, + ) def update_electric_pml(self): """Updates electric field components with the PML correction.""" - for pml in self.grid.pmls['slabs']: + for pml in self.grid.pmls["slabs"]: pml.update_electric() def update_electric_sources(self): """Updates electric field components from sources - - update any Hertzian dipole sources last. + update any Hertzian dipole sources last. """ if self.grid.voltagesources: - self.update_voltage_source_dev(np.int32(len(self.grid.voltagesources)), - np.int32(self.grid.iteration), - config.sim_config.dtypes['float_or_double'](self.grid.dx), - config.sim_config.dtypes['float_or_double'](self.grid.dy), - config.sim_config.dtypes['float_or_double'](self.grid.dz), - self.srcinfo1_voltage_dev.gpudata, - self.srcinfo2_voltage_dev.gpudata, - self.srcwaves_voltage_dev.gpudata, - self.grid.ID_dev.gpudata, - self.grid.Ex_dev.gpudata, - self.grid.Ey_dev.gpudata, - self.grid.Ez_dev.gpudata, - block=(1, 1, 1), - grid=(round32(len(self.grid.voltagesources)), 1, 1)) + self.update_voltage_source_dev( + np.int32(len(self.grid.voltagesources)), + np.int32(self.grid.iteration), + config.sim_config.dtypes["float_or_double"](self.grid.dx), + config.sim_config.dtypes["float_or_double"](self.grid.dy), + config.sim_config.dtypes["float_or_double"](self.grid.dz), + self.srcinfo1_voltage_dev.gpudata, + self.srcinfo2_voltage_dev.gpudata, + self.srcwaves_voltage_dev.gpudata, + self.grid.ID_dev.gpudata, + self.grid.Ex_dev.gpudata, + self.grid.Ey_dev.gpudata, + self.grid.Ez_dev.gpudata, + block=(1, 1, 1), + grid=(round32(len(self.grid.voltagesources)), 1, 1), + ) if self.grid.hertziandipoles: - self.update_hertzian_dipole_dev(np.int32(len(self.grid.hertziandipoles)), - np.int32(self.grid.iteration), - config.sim_config.dtypes['float_or_double'](self.grid.dx), - config.sim_config.dtypes['float_or_double'](self.grid.dy), - config.sim_config.dtypes['float_or_double'](self.grid.dz), - self.srcinfo1_hertzian_dev.gpudata, - self.srcinfo2_hertzian_dev.gpudata, - self.srcwaves_hertzian_dev.gpudata, - self.grid.ID_dev.gpudata, - self.grid.Ex_dev.gpudata, - self.grid.Ey_dev.gpudata, - self.grid.Ez_dev.gpudata, - block=(1, 1, 1), - grid=(round32(len(self.grid.hertziandipoles)), 1, 1)) + self.update_hertzian_dipole_dev( + np.int32(len(self.grid.hertziandipoles)), + np.int32(self.grid.iteration), + config.sim_config.dtypes["float_or_double"](self.grid.dx), + config.sim_config.dtypes["float_or_double"](self.grid.dy), + config.sim_config.dtypes["float_or_double"](self.grid.dz), + self.srcinfo1_hertzian_dev.gpudata, + self.srcinfo2_hertzian_dev.gpudata, + self.srcwaves_hertzian_dev.gpudata, + self.grid.ID_dev.gpudata, + self.grid.Ex_dev.gpudata, + self.grid.Ey_dev.gpudata, + self.grid.Ez_dev.gpudata, + block=(1, 1, 1), + grid=(round32(len(self.grid.hertziandipoles)), 1, 1), + ) self.grid.iteration += 1 def update_electric_b(self): """If there are any dispersive materials do 2nd part of dispersive - update - it is split into two parts as it requires present and - updated electric field values. Therefore it can only be completely - updated after the electric field has been updated by the PML and - source updates. + update - it is split into two parts as it requires present and + updated electric field values. Therefore it can only be completely + updated after the electric field has been updated by the PML and + source updates. """ - if config.get_model_config().materials['maxpoles'] > 0: - self.dispersive_update_b(np.int32(self.grid.nx), - np.int32(self.grid.ny), - np.int32(self.grid.nz), - np.int32(config.get_model_config().materials['maxpoles']), - self.grid.updatecoeffsdispersive_dev.gpudata, - self.grid.Tx_dev.gpudata, - self.grid.Ty_dev.gpudata, - self.grid.Tz_dev.gpudata, - self.grid.ID_dev.gpudata, - self.grid.Ex_dev.gpudata, - self.grid.Ey_dev.gpudata, - self.grid.Ez_dev.gpudata, - block=self.grid.tpb, - grid=self.grid.bpg) + if config.get_model_config().materials["maxpoles"] > 0: + self.dispersive_update_b( + np.int32(self.grid.nx), + np.int32(self.grid.ny), + np.int32(self.grid.nz), + np.int32(config.get_model_config().materials["maxpoles"]), + self.grid.updatecoeffsdispersive_dev.gpudata, + self.grid.Tx_dev.gpudata, + self.grid.Ty_dev.gpudata, + self.grid.Tz_dev.gpudata, + self.grid.ID_dev.gpudata, + self.grid.Ex_dev.gpudata, + self.grid.Ey_dev.gpudata, + self.grid.Ez_dev.gpudata, + block=self.grid.tpb, + grid=self.grid.bpg, + ) def time_start(self): """Starts event timers used to calculate solving time for model.""" @@ -708,20 +764,21 @@ class CUDAUpdates: """Copies data from GPU back to CPU to save to file(s).""" # Copy output from receivers array back to correct receiver objects if self.grid.rxs: - dtoh_rx_array(self.rxs_dev.get(), - self.rxcoords_dev.get(), - self.grid) + dtoh_rx_array(self.rxs_dev.get(), self.rxcoords_dev.get(), self.grid) # Copy data from any snapshots back to correct snapshot objects - if self.grid.snapshots and not config.get_model_config().device['snapsgpu2cpu']: + if self.grid.snapshots and not config.get_model_config().device["snapsgpu2cpu"]: for i, snap in enumerate(self.grid.snapshots): - dtoh_snapshot_array(self.snapEx_dev.get(), - self.snapEy_dev.get(), - self.snapEz_dev.get(), - self.snapHx_dev.get(), - self.snapHy_dev.get(), - self.snapHz_dev.get(), - i, snap) + dtoh_snapshot_array( + self.snapEx_dev.get(), + self.snapEy_dev.get(), + self.snapEz_dev.get(), + self.snapHx_dev.get(), + self.snapHy_dev.get(), + self.snapHz_dev.get(), + i, + snap, + ) def cleanup(self): """Cleanup GPU context.""" @@ -744,223 +801,309 @@ class OpenCLUpdates: self.dispersive_update_b = None # Import pyopencl module - self.cl = import_module('pyopencl') - self.elwise = getattr(import_module('pyopencl.elementwise'), 'ElementwiseKernel') + self.cl = import_module("pyopencl") + self.elwise = getattr(import_module("pyopencl.elementwise"), "ElementwiseKernel") # Select device, create context and command queue - self.dev = config.get_model_config().device['dev'] + self.dev = config.get_model_config().device["dev"] self.ctx = self.cl.Context(devices=[self.dev]) - self.queue = self.cl.CommandQueue(self.ctx, - properties=self.cl.command_queue_properties.PROFILING_ENABLE) + self.queue = self.cl.CommandQueue(self.ctx, properties=self.cl.command_queue_properties.PROFILING_ENABLE) # Enviroment for templating kernels - self.env = Environment(loader=PackageLoader('gprMax', 'cuda_opencl')) + self.env = Environment(loader=PackageLoader("gprMax", "cuda_opencl")) # Initialise arrays on device, prepare kernels, and get kernel functions self._set_field_knls() - if self.grid.pmls['slabs']: + if self.grid.pmls["slabs"]: self._set_pml_knls() if self.grid.rxs: self._set_rx_knl() if self.grid.voltagesources + self.grid.hertziandipoles + self.grid.magneticdipoles: self._set_src_knls() if self.grid.snapshots: - self._set_snapshot_knl() + self._set_snapshot_knl() def _set_field_knls(self): """Electric and magnetic field updates - prepares kernels, and - gets kernel functions. + gets kernel functions. """ - if config.get_model_config().materials['maxpoles'] > 0: + if config.get_model_config().materials["maxpoles"] > 0: NY_MATDISPCOEFFS = self.grid.updatecoeffsdispersive.shape[1] NX_T = self.grid.Tx.shape[1] NY_T = self.grid.Tx.shape[2] NZ_T = self.grid.Tx.shape[3] - else: # Set to one any substitutions for dispersive materials. + else: # Set to one any substitutions for dispersive materials. NY_MATDISPCOEFFS = 1 NX_T = 1 NY_T = 1 NZ_T = 1 - self.knl_common = self.env.get_template('knl_common_opencl.tmpl').render( - updatecoeffsE = self.grid.updatecoeffsE.ravel(), - updatecoeffsH = self.grid.updatecoeffsH.ravel(), - REAL=config.sim_config.dtypes['C_float_or_double'], - N_updatecoeffsE=self.grid.updatecoeffsE.size, - N_updatecoeffsH=self.grid.updatecoeffsH.size, - NY_MATCOEFFS=self.grid.updatecoeffsE.shape[1], - NY_MATDISPCOEFFS=NY_MATDISPCOEFFS, - NX_FIELDS=self.grid.nx + 1, - NY_FIELDS=self.grid.ny + 1, - NZ_FIELDS=self.grid.nz + 1, - NX_ID=self.grid.ID.shape[1], - NY_ID=self.grid.ID.shape[2], - NZ_ID=self.grid.ID.shape[3], - NX_T=NX_T, - NY_T=NY_T, - NZ_T=NZ_T, - NY_RXCOORDS=3, - NX_RXS=6, - NY_RXS=self.grid.iterations, - NZ_RXS=len(self.grid.rxs), - NY_SRCINFO=4, - NY_SRCWAVES=self.grid.iterations, - NX_SNAPS=Snapshot.nx_max, - NY_SNAPS=Snapshot.ny_max, - NZ_SNAPS=Snapshot.nz_max) + self.knl_common = self.env.get_template("knl_common_opencl.tmpl").render( + updatecoeffsE=self.grid.updatecoeffsE.ravel(), + updatecoeffsH=self.grid.updatecoeffsH.ravel(), + REAL=config.sim_config.dtypes["C_float_or_double"], + N_updatecoeffsE=self.grid.updatecoeffsE.size, + N_updatecoeffsH=self.grid.updatecoeffsH.size, + NY_MATCOEFFS=self.grid.updatecoeffsE.shape[1], + NY_MATDISPCOEFFS=NY_MATDISPCOEFFS, + NX_FIELDS=self.grid.nx + 1, + NY_FIELDS=self.grid.ny + 1, + NZ_FIELDS=self.grid.nz + 1, + NX_ID=self.grid.ID.shape[1], + NY_ID=self.grid.ID.shape[2], + NZ_ID=self.grid.ID.shape[3], + NX_T=NX_T, + NY_T=NY_T, + NZ_T=NZ_T, + NY_RXCOORDS=3, + NX_RXS=6, + NY_RXS=self.grid.iterations, + NZ_RXS=len(self.grid.rxs), + NY_SRCINFO=4, + NY_SRCWAVES=self.grid.iterations, + NX_SNAPS=Snapshot.nx_max, + NY_SNAPS=Snapshot.ny_max, + NZ_SNAPS=Snapshot.nz_max, + ) - subs = {'CUDA_IDX': '', - 'NX_FIELDS': self.grid.nx + 1, - 'NY_FIELDS': self.grid.ny + 1, - 'NZ_FIELDS': self.grid.nz + 1, - 'NX_ID': self.grid.ID.shape[1], - 'NY_ID': self.grid.ID.shape[2], - 'NZ_ID': self.grid.ID.shape[3]} + subs = { + "CUDA_IDX": "", + "NX_FIELDS": self.grid.nx + 1, + "NY_FIELDS": self.grid.ny + 1, + "NZ_FIELDS": self.grid.nz + 1, + "NX_ID": self.grid.ID.shape[1], + "NY_ID": self.grid.ID.shape[2], + "NZ_ID": self.grid.ID.shape[3], + } - self.update_electric_dev = self.elwise(self.ctx, - knl_fields_updates.update_electric['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double']}), - knl_fields_updates.update_electric['func'].substitute(subs), - 'update_electric', preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) + self.update_electric_dev = self.elwise( + self.ctx, + knl_fields_updates.update_electric["args_opencl"].substitute( + {"REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + knl_fields_updates.update_electric["func"].substitute(subs), + "update_electric", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) - self.update_magnetic_dev = self.elwise(self.ctx, - knl_fields_updates.update_magnetic['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double']}), - knl_fields_updates.update_magnetic['func'].substitute(subs), - 'update_magnetic', preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) + self.update_magnetic_dev = self.elwise( + self.ctx, + knl_fields_updates.update_magnetic["args_opencl"].substitute( + {"REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + knl_fields_updates.update_magnetic["func"].substitute(subs), + "update_magnetic", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) # If there are any dispersive materials (updates are split into two # parts as they require present and updated electric field values). - if config.get_model_config().materials['maxpoles'] > 0: - subs = {'CUDA_IDX': '', - 'REAL': config.sim_config.dtypes['C_float_or_double'], - 'REALFUNC': config.get_model_config().materials['crealfunc'], - 'NX_FIELDS': self.grid.nx + 1, - 'NY_FIELDS': self.grid.ny + 1, - 'NZ_FIELDS': self.grid.nz + 1, - 'NX_ID': self.grid.ID.shape[1], - 'NY_ID': self.grid.ID.shape[2], - 'NZ_ID': self.grid.ID.shape[3], - 'NX_T': NX_T, - 'NY_T': NY_T, - 'NZ_T': NZ_T} - self.dispersive_update_a = self.elwise(self.ctx, - knl_fields_updates.update_electric_dispersive_A['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double'], 'COMPLEX': config.get_model_config().materials['dispersiveCdtype']}), - knl_fields_updates.update_electric_dispersive_A['func'].substitute(subs), - 'update_electric_dispersive_A', preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) - self.dispersive_update_b = self.elwise(self.ctx, - knl_fields_updates.update_electric_dispersive_B['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double'] ,'COMPLEX': config.get_model_config().materials['dispersiveCdtype']}), - knl_fields_updates.update_electric_dispersive_B['func'].substitute(subs), - 'update_electric_dispersive_B', preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) - + if config.get_model_config().materials["maxpoles"] > 0: + subs = { + "CUDA_IDX": "", + "REAL": config.sim_config.dtypes["C_float_or_double"], + "REALFUNC": config.get_model_config().materials["crealfunc"], + "NX_FIELDS": self.grid.nx + 1, + "NY_FIELDS": self.grid.ny + 1, + "NZ_FIELDS": self.grid.nz + 1, + "NX_ID": self.grid.ID.shape[1], + "NY_ID": self.grid.ID.shape[2], + "NZ_ID": self.grid.ID.shape[3], + "NX_T": NX_T, + "NY_T": NY_T, + "NZ_T": NZ_T, + } + self.dispersive_update_a = self.elwise( + self.ctx, + knl_fields_updates.update_electric_dispersive_A["args_opencl"].substitute( + { + "REAL": config.sim_config.dtypes["C_float_or_double"], + "COMPLEX": config.get_model_config().materials["dispersiveCdtype"], + } + ), + knl_fields_updates.update_electric_dispersive_A["func"].substitute(subs), + "update_electric_dispersive_A", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) + self.dispersive_update_b = self.elwise( + self.ctx, + knl_fields_updates.update_electric_dispersive_B["args_opencl"].substitute( + { + "REAL": config.sim_config.dtypes["C_float_or_double"], + "COMPLEX": config.get_model_config().materials["dispersiveCdtype"], + } + ), + knl_fields_updates.update_electric_dispersive_B["func"].substitute(subs), + "update_electric_dispersive_B", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) + # Initialise field arrays on compute device self.grid.htod_geometry_arrays(self.queue) self.grid.htod_field_arrays(self.queue) - if config.get_model_config().materials['maxpoles'] > 0: + if config.get_model_config().materials["maxpoles"] > 0: self.grid.htod_dispersive_arrays(self.queue) def _set_pml_knls(self): """PMLS - prepares kernels and gets kernel functions.""" - knl_pml_updates_electric = import_module('gprMax.cuda_opencl.knl_pml_updates_electric_' + self.grid.pmls['formulation']) - knl_pml_updates_magnetic = import_module('gprMax.cuda_opencl.knl_pml_updates_magnetic_' + self.grid.pmls['formulation']) + knl_pml_updates_electric = import_module( + "gprMax.cuda_opencl.knl_pml_updates_electric_" + self.grid.pmls["formulation"] + ) + knl_pml_updates_magnetic = import_module( + "gprMax.cuda_opencl.knl_pml_updates_magnetic_" + self.grid.pmls["formulation"] + ) - subs = {'CUDA_IDX': '', - 'REAL': config.sim_config.dtypes['C_float_or_double'], - 'NX_FIELDS': self.grid.nx + 1, - 'NY_FIELDS': self.grid.ny + 1, - 'NZ_FIELDS': self.grid.nz + 1, - 'NX_ID': self.grid.ID.shape[1], - 'NY_ID': self.grid.ID.shape[2], - 'NZ_ID': self.grid.ID.shape[3]} + subs = { + "CUDA_IDX": "", + "REAL": config.sim_config.dtypes["C_float_or_double"], + "NX_FIELDS": self.grid.nx + 1, + "NY_FIELDS": self.grid.ny + 1, + "NZ_FIELDS": self.grid.nz + 1, + "NX_ID": self.grid.ID.shape[1], + "NY_ID": self.grid.ID.shape[2], + "NZ_ID": self.grid.ID.shape[3], + } - # Set workgroup size, initialise arrays on compute device, and get + # Set workgroup size, initialise arrays on compute device, and get # kernel functions - for pml in self.grid.pmls['slabs']: + for pml in self.grid.pmls["slabs"]: pml.set_queue(self.queue) pml.htod_field_arrays() - knl_name = f'order{len(pml.CFS)}_{pml.direction}' + knl_name = f"order{len(pml.CFS)}_{pml.direction}" knl_electric_name = getattr(knl_pml_updates_electric, knl_name) - knl_magnetic_name = getattr(knl_pml_updates_magnetic, knl_name) + knl_magnetic_name = getattr(knl_pml_updates_magnetic, knl_name) - pml.update_electric_dev = self.elwise(self.ctx, - knl_electric_name['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double']}), - knl_electric_name['func'].substitute(subs), - f'pml_updates_electric_{knl_name}', - preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts'],) + pml.update_electric_dev = self.elwise( + self.ctx, + knl_electric_name["args_opencl"].substitute({"REAL": config.sim_config.dtypes["C_float_or_double"]}), + knl_electric_name["func"].substitute(subs), + f"pml_updates_electric_{knl_name}", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) - pml.update_magnetic_dev = self.elwise(self.ctx, - knl_magnetic_name['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double']}), - knl_magnetic_name['func'].substitute(subs), - f'pml_updates_magnetic_{knl_name}', - preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) + pml.update_magnetic_dev = self.elwise( + self.ctx, + knl_magnetic_name["args_opencl"].substitute({"REAL": config.sim_config.dtypes["C_float_or_double"]}), + knl_magnetic_name["func"].substitute(subs), + f"pml_updates_magnetic_{knl_name}", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) def _set_rx_knl(self): - """Receivers - initialises arrays on compute device, prepares kernel and - gets kernel function. + """Receivers - initialises arrays on compute device, prepares kernel and + gets kernel function. """ self.rxcoords_dev, self.rxs_dev = htod_rx_arrays(self.grid, self.queue) - self.store_outputs_dev = self.elwise(self.ctx, - knl_store_outputs.store_outputs['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double']}), - knl_store_outputs.store_outputs['func'].substitute({'CUDA_IDX': ''}), - 'store_outputs', preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) + self.store_outputs_dev = self.elwise( + self.ctx, + knl_store_outputs.store_outputs["args_opencl"].substitute( + {"REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + knl_store_outputs.store_outputs["func"].substitute({"CUDA_IDX": ""}), + "store_outputs", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) def _set_src_knls(self): - """Sources - initialises arrays on compute device, prepares kernel and - gets kernel function. + """Sources - initialises arrays on compute device, prepares kernel and + gets kernel function. """ if self.grid.hertziandipoles: - self.srcinfo1_hertzian_dev, self.srcinfo2_hertzian_dev, self.srcwaves_hertzian_dev = htod_src_arrays(self.grid.hertziandipoles, self.grid, self.queue) - self.update_hertzian_dipole_dev = self.elwise(self.ctx, - knl_source_updates.update_hertzian_dipole['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double']}), - knl_source_updates.update_hertzian_dipole['func'].substitute({'CUDA_IDX': '', 'REAL': config.sim_config.dtypes['C_float_or_double']}), - 'update_hertzian_dipole', preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) + self.srcinfo1_hertzian_dev, self.srcinfo2_hertzian_dev, self.srcwaves_hertzian_dev = htod_src_arrays( + self.grid.hertziandipoles, self.grid, self.queue + ) + self.update_hertzian_dipole_dev = self.elwise( + self.ctx, + knl_source_updates.update_hertzian_dipole["args_opencl"].substitute( + {"REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + knl_source_updates.update_hertzian_dipole["func"].substitute( + {"CUDA_IDX": "", "REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + "update_hertzian_dipole", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) if self.grid.magneticdipoles: - self.srcinfo1_magnetic_dev, self.srcinfo2_magnetic_dev, self.srcwaves_magnetic_dev = htod_src_arrays(self.grid.magneticdipoles, self.grid, self.queue) - self.update_magnetic_dipole_dev = self.elwise(self.ctx, - knl_source_updates.update_magnetic_dipole['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double']}), - knl_source_updates.update_magnetic_dipole['func'].substitute({'CUDA_IDX': '', 'REAL': config.sim_config.dtypes['C_float_or_double']}), - 'update_magnetic_dipole', preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) + self.srcinfo1_magnetic_dev, self.srcinfo2_magnetic_dev, self.srcwaves_magnetic_dev = htod_src_arrays( + self.grid.magneticdipoles, self.grid, self.queue + ) + self.update_magnetic_dipole_dev = self.elwise( + self.ctx, + knl_source_updates.update_magnetic_dipole["args_opencl"].substitute( + {"REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + knl_source_updates.update_magnetic_dipole["func"].substitute( + {"CUDA_IDX": "", "REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + "update_magnetic_dipole", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) if self.grid.voltagesources: - self.srcinfo1_voltage_dev, self.srcinfo2_voltage_dev,self.srcwaves_voltage_dev = htod_src_arrays(self.grid.voltagesources, self.grid, self.queue) - self.update_voltage_source_dev = self.elwise(self.ctx, - knl_source_updates.update_voltage_source['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double']}), - knl_source_updates.update_voltage_source['func'].substitute({'CUDA_IDX': '', 'REAL': config.sim_config.dtypes['C_float_or_double']}), 'update_voltage_source', preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) + self.srcinfo1_voltage_dev, self.srcinfo2_voltage_dev, self.srcwaves_voltage_dev = htod_src_arrays( + self.grid.voltagesources, self.grid, self.queue + ) + self.update_voltage_source_dev = self.elwise( + self.ctx, + knl_source_updates.update_voltage_source["args_opencl"].substitute( + {"REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + knl_source_updates.update_voltage_source["func"].substitute( + {"CUDA_IDX": "", "REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + "update_voltage_source", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) def _set_snapshot_knl(self): - """Snapshots - initialises arrays on compute device, prepares kernel and - gets kernel function. + """Snapshots - initialises arrays on compute device, prepares kernel and + gets kernel function. """ - self.snapEx_dev, self.snapEy_dev, self.snapEz_dev, self.snapHx_dev, self.snapHy_dev, self.snapHz_dev = htod_snapshot_array(self.grid, self.queue) - self.store_snapshot_dev = self.elwise(self.ctx, - knl_snapshots.store_snapshot['args_opencl'].substitute({'REAL': config.sim_config.dtypes['C_float_or_double']}), - knl_snapshots.store_snapshot['func'].substitute( {'CUDA_IDX': '', - 'NX_SNAPS': Snapshot.nx_max, - 'NY_SNAPS': Snapshot.ny_max, - 'NZ_SNAPS': Snapshot.nz_max}), - 'store_snapshot', preamble=self.knl_common, - options=config.sim_config.devices['compiler_opts']) + ( + self.snapEx_dev, + self.snapEy_dev, + self.snapEz_dev, + self.snapHx_dev, + self.snapHy_dev, + self.snapHz_dev, + ) = htod_snapshot_array(self.grid, self.queue) + self.store_snapshot_dev = self.elwise( + self.ctx, + knl_snapshots.store_snapshot["args_opencl"].substitute( + {"REAL": config.sim_config.dtypes["C_float_or_double"]} + ), + knl_snapshots.store_snapshot["func"].substitute( + {"CUDA_IDX": "", "NX_SNAPS": Snapshot.nx_max, "NY_SNAPS": Snapshot.ny_max, "NZ_SNAPS": Snapshot.nz_max} + ), + "store_snapshot", + preamble=self.knl_common, + options=config.sim_config.devices["compiler_opts"], + ) def store_outputs(self): """Stores field component values for every receiver.""" if self.grid.rxs: - event = self.store_outputs_dev(np.int32(len(self.grid.rxs)), - np.int32(self.grid.iteration), - self.rxcoords_dev, - self.rxs_dev, - self.grid.Ex_dev, - self.grid.Ey_dev, - self.grid.Ez_dev, - self.grid.Hx_dev, - self.grid.Hy_dev, - self.grid.Hz_dev) + event = self.store_outputs_dev( + np.int32(len(self.grid.rxs)), + np.int32(self.grid.iteration), + self.rxcoords_dev, + self.rxs_dev, + self.grid.Ex_dev, + self.grid.Ey_dev, + self.grid.Ez_dev, + self.grid.Hx_dev, + self.grid.Hy_dev, + self.grid.Hz_dev, + ) event.wait() def store_snapshots(self, iteration): @@ -972,173 +1115,191 @@ class OpenCLUpdates: for i, snap in enumerate(self.grid.snapshots): if snap.time == iteration + 1: - snapno = 0 if config.get_model_config().device['snapsgpu2cpu'] else i - event = self.store_snapshot_dev(np.int32(snapno), - np.int32(snap.xs), - np.int32(snap.xf), - np.int32(snap.ys), - np.int32(snap.yf), - np.int32(snap.zs), - np.int32(snap.zf), - np.int32(snap.dx), - np.int32(snap.dy), - np.int32(snap.dz), - self.grid.Ex_dev, - self.grid.Ey_dev, - self.grid.Ez_dev, - self.grid.Hx_dev, - self.grid.Hy_dev, - self.grid.Hz_dev, - self.snapEx_dev, - self.snapEy_dev, - self.snapEz_dev, - self.snapHx_dev, - self.snapHy_dev, - self.snapHz_dev) + snapno = 0 if config.get_model_config().device["snapsgpu2cpu"] else i + event = self.store_snapshot_dev( + np.int32(snapno), + np.int32(snap.xs), + np.int32(snap.xf), + np.int32(snap.ys), + np.int32(snap.yf), + np.int32(snap.zs), + np.int32(snap.zf), + np.int32(snap.dx), + np.int32(snap.dy), + np.int32(snap.dz), + self.grid.Ex_dev, + self.grid.Ey_dev, + self.grid.Ez_dev, + self.grid.Hx_dev, + self.grid.Hy_dev, + self.grid.Hz_dev, + self.snapEx_dev, + self.snapEy_dev, + self.snapEz_dev, + self.snapHx_dev, + self.snapHy_dev, + self.snapHz_dev, + ) event.wait() - if config.get_model_config().device['snapsgpu2cpu']: - dtoh_snapshot_array(self.snapEx_dev.get(), - self.snapEy_dev.get(), - self.snapEz_dev.get(), - self.snapHx_dev.get(), - self.snapHy_dev.get(), - self.snapHz_dev.get(), - 0, - snap) + if config.get_model_config().device["snapsgpu2cpu"]: + dtoh_snapshot_array( + self.snapEx_dev.get(), + self.snapEy_dev.get(), + self.snapEz_dev.get(), + self.snapHx_dev.get(), + self.snapHy_dev.get(), + self.snapHz_dev.get(), + 0, + snap, + ) def update_magnetic(self): """Updates magnetic field components.""" - event = self.update_magnetic_dev(np.int32(self.grid.nx), - np.int32(self.grid.ny), - np.int32(self.grid.nz), - self.grid.ID_dev, - self.grid.Hx_dev, - self.grid.Hy_dev, - self.grid.Hz_dev, - self.grid.Ex_dev, - self.grid.Ey_dev, - self.grid.Ez_dev) + event = self.update_magnetic_dev( + np.int32(self.grid.nx), + np.int32(self.grid.ny), + np.int32(self.grid.nz), + self.grid.ID_dev, + self.grid.Hx_dev, + self.grid.Hy_dev, + self.grid.Hz_dev, + self.grid.Ex_dev, + self.grid.Ey_dev, + self.grid.Ez_dev, + ) event.wait() def update_magnetic_pml(self): """Updates magnetic field components with the PML correction.""" - for pml in self.grid.pmls['slabs']: + for pml in self.grid.pmls["slabs"]: pml.update_magnetic() def update_magnetic_sources(self): """Updates magnetic field components from sources.""" if self.grid.magneticdipoles: - event = self.update_magnetic_dipole_dev(np.int32(len(self.grid.magneticdipoles)), - np.int32(self.grid.iteration), - config.sim_config.dtypes['float_or_double'](self.grid.dx), - config.sim_config.dtypes['float_or_double'](self.grid.dy), - config.sim_config.dtypes['float_or_double'](self.grid.dz), - self.srcinfo1_magnetic_dev, - self.srcinfo2_magnetic_dev, - self.srcwaves_magnetic_dev, - self.grid.ID_dev, - self.grid.Hx_dev, - self.grid.Hy_dev, - self.grid.Hz_dev) + event = self.update_magnetic_dipole_dev( + np.int32(len(self.grid.magneticdipoles)), + np.int32(self.grid.iteration), + config.sim_config.dtypes["float_or_double"](self.grid.dx), + config.sim_config.dtypes["float_or_double"](self.grid.dy), + config.sim_config.dtypes["float_or_double"](self.grid.dz), + self.srcinfo1_magnetic_dev, + self.srcinfo2_magnetic_dev, + self.srcwaves_magnetic_dev, + self.grid.ID_dev, + self.grid.Hx_dev, + self.grid.Hy_dev, + self.grid.Hz_dev, + ) event.wait() def update_electric_a(self): """Updates electric field components.""" # All materials are non-dispersive so do standard update. - if config.get_model_config().materials['maxpoles'] == 0: - event = self.update_electric_dev(np.int32(self.grid.nx), - np.int32(self.grid.ny), - np.int32(self.grid.nz), - self.grid.ID_dev, - self.grid.Ex_dev, - self.grid.Ey_dev, - self.grid.Ez_dev, - self.grid.Hx_dev, - self.grid.Hy_dev, - self.grid.Hz_dev) + if config.get_model_config().materials["maxpoles"] == 0: + event = self.update_electric_dev( + np.int32(self.grid.nx), + np.int32(self.grid.ny), + np.int32(self.grid.nz), + self.grid.ID_dev, + self.grid.Ex_dev, + self.grid.Ey_dev, + self.grid.Ez_dev, + self.grid.Hx_dev, + self.grid.Hy_dev, + self.grid.Hz_dev, + ) event.wait() # If there are any dispersive materials do 1st part of dispersive update # (it is split into two parts as it requires present and updated electric field values). else: - event = self.dispersive_update_a(np.int32(self.grid.nx), - np.int32(self.grid.ny), - np.int32(self.grid.nz), - np.int32(config.get_model_config().materials['maxpoles']), - self.grid.updatecoeffsdispersive_dev, - self.grid.Tx_dev, - self.grid.Ty_dev, - self.grid.Tz_dev, - self.grid.ID_dev, - self.grid.Ex_dev, - self.grid.Ey_dev, - self.grid.Ez_dev, - self.grid.Hx_dev, - self.grid.Hy_dev, - self.grid.Hz_dev) + event = self.dispersive_update_a( + np.int32(self.grid.nx), + np.int32(self.grid.ny), + np.int32(self.grid.nz), + np.int32(config.get_model_config().materials["maxpoles"]), + self.grid.updatecoeffsdispersive_dev, + self.grid.Tx_dev, + self.grid.Ty_dev, + self.grid.Tz_dev, + self.grid.ID_dev, + self.grid.Ex_dev, + self.grid.Ey_dev, + self.grid.Ez_dev, + self.grid.Hx_dev, + self.grid.Hy_dev, + self.grid.Hz_dev, + ) event.wait() def update_electric_pml(self): """Updates electric field components with the PML correction.""" - for pml in self.grid.pmls['slabs']: + for pml in self.grid.pmls["slabs"]: pml.update_electric() def update_electric_sources(self): """Updates electric field components from sources - - update any Hertzian dipole sources last. + update any Hertzian dipole sources last. """ if self.grid.voltagesources: - event = self.update_voltage_source_dev(np.int32(len(self.grid.voltagesources)), - np.int32(self.grid.iteration), - config.sim_config.dtypes['float_or_double'](self.grid.dx), - config.sim_config.dtypes['float_or_double'](self.grid.dy), - config.sim_config.dtypes['float_or_double'](self.grid.dz), - self.srcinfo1_voltage_dev, - self.srcinfo2_voltage_dev, - self.srcwaves_voltage_dev, - self.grid.ID_dev, - self.grid.Ex_dev, - self.grid.Ey_dev, - self.grid.Ez_dev) + event = self.update_voltage_source_dev( + np.int32(len(self.grid.voltagesources)), + np.int32(self.grid.iteration), + config.sim_config.dtypes["float_or_double"](self.grid.dx), + config.sim_config.dtypes["float_or_double"](self.grid.dy), + config.sim_config.dtypes["float_or_double"](self.grid.dz), + self.srcinfo1_voltage_dev, + self.srcinfo2_voltage_dev, + self.srcwaves_voltage_dev, + self.grid.ID_dev, + self.grid.Ex_dev, + self.grid.Ey_dev, + self.grid.Ez_dev, + ) event.wait() if self.grid.hertziandipoles: - event = self.update_hertzian_dipole_dev(np.int32(len(self.grid.hertziandipoles)), - np.int32(self.grid.iteration), - config.sim_config.dtypes['float_or_double'](self.grid.dx), - config.sim_config.dtypes['float_or_double'](self.grid.dy), - config.sim_config.dtypes['float_or_double'](self.grid.dz), - self.srcinfo1_hertzian_dev, - self.srcinfo2_hertzian_dev, - self.srcwaves_hertzian_dev, - self.grid.ID_dev, - self.grid.Ex_dev, - self.grid.Ey_dev, - self.grid.Ez_dev) + event = self.update_hertzian_dipole_dev( + np.int32(len(self.grid.hertziandipoles)), + np.int32(self.grid.iteration), + config.sim_config.dtypes["float_or_double"](self.grid.dx), + config.sim_config.dtypes["float_or_double"](self.grid.dy), + config.sim_config.dtypes["float_or_double"](self.grid.dz), + self.srcinfo1_hertzian_dev, + self.srcinfo2_hertzian_dev, + self.srcwaves_hertzian_dev, + self.grid.ID_dev, + self.grid.Ex_dev, + self.grid.Ey_dev, + self.grid.Ez_dev, + ) event.wait() self.grid.iteration += 1 def update_electric_b(self): """If there are any dispersive materials do 2nd part of dispersive - update - it is split into two parts as it requires present and - updated electric field values. Therefore it can only be completely - updated after the electric field has been updated by the PML and - source updates. + update - it is split into two parts as it requires present and + updated electric field values. Therefore it can only be completely + updated after the electric field has been updated by the PML and + source updates. """ - if config.get_model_config().materials['maxpoles'] > 0: - event = self.dispersive_update_b(np.int32(self.grid.nx), - np.int32(self.grid.ny), - np.int32(self.grid.nz), - np.int32(config.get_model_config().materials['maxpoles']), - self.grid.updatecoeffsdispersive_dev, - self.grid.Tx_dev, - self.grid.Ty_dev, - self.grid.Tz_dev, - self.grid.ID_dev, - self.grid.Ex_dev, - self.grid.Ey_dev, - self.grid.Ez_dev) + if config.get_model_config().materials["maxpoles"] > 0: + event = self.dispersive_update_b( + np.int32(self.grid.nx), + np.int32(self.grid.ny), + np.int32(self.grid.nz), + np.int32(config.get_model_config().materials["maxpoles"]), + self.grid.updatecoeffsdispersive_dev, + self.grid.Tx_dev, + self.grid.Ty_dev, + self.grid.Tz_dev, + self.grid.ID_dev, + self.grid.Ex_dev, + self.grid.Ey_dev, + self.grid.Ez_dev, + ) event.wait() def time_start(self): @@ -1157,14 +1318,14 @@ class OpenCLUpdates: """ # if iteration == self.grid.iterations - 1: # return self.drv.mem_get_info()[1] - self.drv.mem_get_info()[0] - logger.debug('Look at memory estimate for pyopencl') + logger.debug("Look at memory estimate for pyopencl") pass def calculate_solve_time(self): """Calculates solving time for model.""" event_marker2 = self.cl.enqueue_marker(self.queue) event_marker2.wait() - return (event_marker2.profile.end - self.event_marker1.profile.start)*1e-9 + return (event_marker2.profile.end - self.event_marker1.profile.start) * 1e-9 def finalise(self): """Copies data from compute device back to CPU to save to file(s).""" @@ -1173,15 +1334,18 @@ class OpenCLUpdates: dtoh_rx_array(self.rxs_dev.get(), self.rxcoords_dev.get(), self.grid) # Copy data from any snapshots back to correct snapshot objects - if self.grid.snapshots and not config.get_model_config().device['snapsgpu2cpu']: + if self.grid.snapshots and not config.get_model_config().device["snapsgpu2cpu"]: for i, snap in enumerate(self.grid.snapshots): - dtoh_snapshot_array(self.snapEx_dev.get(), - self.snapEy_dev.get(), - self.snapEz_dev.get(), - self.snapHx_dev.get(), - self.snapHy_dev.get(), - self.snapHz_dev.get(), - i, snap) + dtoh_snapshot_array( + self.snapEx_dev.get(), + self.snapEy_dev.get(), + self.snapEz_dev.get(), + self.snapHx_dev.get(), + self.snapHy_dev.get(), + self.snapHz_dev.get(), + i, + snap, + ) def cleanup(self): pass diff --git a/gprMax/user_inputs.py b/gprMax/user_inputs.py index c920f5e7..87698d95 100644 --- a/gprMax/user_inputs.py +++ b/gprMax/user_inputs.py @@ -63,17 +63,15 @@ class UserInput: try: self.grid.within_bounds(p) except ValueError as err: - v = ['x', 'y', 'z'] + v = ["x", "y", "z"] # Discretisation - dl = getattr(self.grid, f'd{err.args[0]}') + dl = getattr(self.grid, f"d{err.args[0]}") # Incorrect index i = p[v.index(err.args[0])] if name: - s = (f"\n'{cmd_str}' {err.args[0]} {name}-coordinate {i * dl:g} " + - "is not within the model domain") + s = f"\n'{cmd_str}' {err.args[0]} {name}-coordinate {i * dl:g} " + "is not within the model domain" else: - s = (f"\n'{cmd_str}' {err.args[0]}-coordinate {i * dl:g} is not " + - "within the model domain") + s = f"\n'{cmd_str}' {err.args[0]}-coordinate {i * dl:g} is not " + "within the model domain" logger.exception(s) raise @@ -84,10 +82,10 @@ class UserInput: def round_to_grid(self, p): """Gets the nearest continuous point on the grid from a continuous point - in space. + in space. """ return self.discretise_point(p) * self.grid.dl - + def descretised_to_continuous(self, p): """Returns a point given as indices to a continuous point in the real space.""" return p * self.grid.dl @@ -99,68 +97,63 @@ class MainGridUserInput(UserInput): def __init__(self, grid): super().__init__(grid) - def check_point(self, p, cmd_str, name=''): + def check_point(self, p, cmd_str, name=""): """Discretises point and check its within the domain""" p = self.discretise_point(p) self.point_within_bounds(p, cmd_str, name) return p - def check_src_rx_point(self, p, cmd_str, name=''): + def check_src_rx_point(self, p, cmd_str, name=""): p = self.check_point(p, cmd_str, name) if self.grid.within_pml(p): - logger.warning(f"'{cmd_str}' sources and receivers should not " + - "normally be positioned within the PML.") + logger.warning(f"'{cmd_str}' sources and receivers should not " + "normally be positioned within the PML.") return p def check_box_points(self, p1, p2, cmd_str): - p1 = self.check_point(p1, cmd_str, name='lower') - p2 = self.check_point(p2, cmd_str, name='upper') + p1 = self.check_point(p1, cmd_str, name="lower") + p2 = self.check_point(p2, cmd_str, name="upper") if np.greater(p1, p2).any(): - logger.exception(f"'{cmd_str}' the lower coordinates should be less " + - "than the upper coordinates.") + logger.exception(f"'{cmd_str}' the lower coordinates should be less " + "than the upper coordinates.") raise ValueError return p1, p2 def check_tri_points(self, p1, p2, p3, cmd_str): - p1 = self.check_point(p1, cmd_str, name='vertex_1') - p2 = self.check_point(p2, cmd_str, name='vertex_2') - p3 = self.check_point(p3, cmd_str, name='vertex_3') + p1 = self.check_point(p1, cmd_str, name="vertex_1") + p2 = self.check_point(p2, cmd_str, name="vertex_2") + p3 = self.check_point(p3, cmd_str, name="vertex_3") return p1, p2, p3 def discretise_static_point(self, p): - """Gets the index of a continuous point regardless of the point of - origin of the grid. + """Gets the index of a continuous point regardless of the point of + origin of the grid. """ return super().discretise_point(p) def round_to_grid_static_point(self, p): - """Gets the index of a continuous point regardless of the point of - origin of the grid. + """Gets the index of a continuous point regardless of the point of + origin of the grid. """ return super().discretise_point(p) * self.grid.dl class SubgridUserInput(MainGridUserInput): """Handles (x, y, z) points supplied by the user in the subgrid. - This class autotranslates points from main grid to subgrid equivalent - (within IS). Useful if material traverse is not required. + This class autotranslates points from main grid to subgrid equivalent + (within IS). Useful if material traverse is not required. """ def __init__(self, grid): super().__init__(grid) # Defines the region exposed to the user - self.inner_bound = np.array([grid.n_boundary_cells_x, - grid.n_boundary_cells_y, - grid.n_boundary_cells_z]) + self.inner_bound = np.array([grid.n_boundary_cells_x, grid.n_boundary_cells_y, grid.n_boundary_cells_z]) - self.outer_bound = np.subtract([grid.nx, grid.ny, grid.nz], - self.inner_bound) + self.outer_bound = np.subtract([grid.nx, grid.ny, grid.nz], self.inner_bound) def translate_to_gap(self, p): """Translates the user input point to the real point in the subgrid.""" @@ -172,9 +165,9 @@ class SubgridUserInput(MainGridUserInput): return np.array([p1, p2, p3]) def discretise_point(self, p): - """Discretises a point. Does not provide any checks. The user enters - coordinates relative to self.inner_bound. This function translate - the user point to the correct index for building objects. + """Discretises a point. Does not provide any checks. The user enters + coordinates relative to self.inner_bound. This function translate + the user point to the correct index for building objects. """ p = super().discretise_point(p) @@ -186,25 +179,21 @@ class SubgridUserInput(MainGridUserInput): p_m = p_t * self.grid.dl return p_m - def check_point(self, p, cmd_str, name=''): + def check_point(self, p, cmd_str, name=""): p_t = super().check_point(p, cmd_str, name) # Provide user within a warning if they have placed objects within # the OS non-working region. - if (np.less(p_t, self.inner_bound).any() or - np.greater(p_t, self.outer_bound).any()): - logger.warning(f"'{cmd_str}' this object traverses the Outer " + - "Surface. This is an advanced feature.") + if np.less(p_t, self.inner_bound).any() or np.greater(p_t, self.outer_bound).any(): + logger.warning(f"'{cmd_str}' this object traverses the Outer " + "Surface. This is an advanced feature.") return p_t - + def discretise_static_point(self, p): - """Gets the index of a continuous point regardless of the point of - origin of the grid.""" + """Gets the index of a continuous point regardless of the point of + origin of the grid.""" return super().discretise_point(p) - + def round_to_grid_static_point(self, p): - """Gets the index of a continuous point regardless of the point of - origin of the grid.""" + """Gets the index of a continuous point regardless of the point of + origin of the grid.""" return super().discretise_point(p) * self.grid.dl - - diff --git a/gprMax/utilities/host_info.py b/gprMax/utilities/host_info.py index ebaca218..02ad8866 100644 --- a/gprMax/utilities/host_info.py +++ b/gprMax/utilities/host_info.py @@ -35,49 +35,57 @@ def get_host_info(): """Gets information about the machine, CPU, RAM, and OS. Returns: - hostinfo: dict containing manufacturer and model of machine; + hostinfo: dict containing manufacturer and model of machine; description of CPU type, speed, cores; RAM; name and version of operating system. """ # Default to 'unknown' if any of the detection fails - manufacturer = model = cpuID = sockets = threadspercore = 'unknown' + manufacturer = model = cpuID = sockets = threadspercore = "unknown" # Windows - if sys.platform == 'win32': + if sys.platform == "win32": # Manufacturer/model try: - manufacturer = subprocess.check_output(["wmic", "csproduct", "get", "vendor"], - shell=False, - stderr=subprocess.STDOUT).decode('utf-8').strip() - manufacturer = manufacturer.split('\n') + manufacturer = ( + subprocess.check_output(["wmic", "csproduct", "get", "vendor"], shell=False, stderr=subprocess.STDOUT) + .decode("utf-8") + .strip() + ) + manufacturer = manufacturer.split("\n") if len(manufacturer) > 1: manufacturer = manufacturer[1] else: manufacturer = manufacturer[0] - model = subprocess.check_output(["wmic", "computersystem", "get", "model"], - shell=False, - stderr=subprocess.STDOUT).decode('utf-8').strip() - model = model.split('\n') + model = ( + subprocess.check_output( + ["wmic", "computersystem", "get", "model"], shell=False, stderr=subprocess.STDOUT + ) + .decode("utf-8") + .strip() + ) + model = model.split("\n") if len(model) > 1: model = model[1] else: model = model[0] except subprocess.CalledProcessError: pass - machineID = ' '.join(manufacturer.split()) + ' ' + ' '.join(model.split()) + machineID = " ".join(manufacturer.split()) + " " + " ".join(model.split()) # CPU information try: - allcpuinfo = subprocess.check_output(["wmic", "cpu", "get", "Name"], - shell=False, - stderr=subprocess.STDOUT).decode('utf-8').strip() - allcpuinfo = allcpuinfo.split('\n') + allcpuinfo = ( + subprocess.check_output(["wmic", "cpu", "get", "Name"], shell=False, stderr=subprocess.STDOUT) + .decode("utf-8") + .strip() + ) + allcpuinfo = allcpuinfo.split("\n") sockets = 0 for line in allcpuinfo: - if 'CPU' in line: + if "CPU" in line: cpuID = line.strip() - cpuID = ' '.join(cpuID.split()) + cpuID = " ".join(cpuID.split()) sockets += 1 except subprocess.CalledProcessError: pass @@ -89,33 +97,42 @@ def get_host_info(): hyperthreading = False # OS version - if platform.machine().endswith('64'): - osbit = ' (64-bit)' + if platform.machine().endswith("64"): + osbit = " (64-bit)" else: - osbit = ' (32-bit)' - osversion = 'Windows ' + platform.release() + osbit + osbit = " (32-bit)" + osversion = "Windows " + platform.release() + osbit # Mac OS X/macOS - elif sys.platform == 'darwin': + elif sys.platform == "darwin": # Manufacturer/model - manufacturer = 'Apple' + manufacturer = "Apple" try: - model = subprocess.check_output(["sysctl", "-n", "hw.model"], shell=False, - stderr=subprocess.STDOUT).decode('utf-8').strip() + model = ( + subprocess.check_output(["sysctl", "-n", "hw.model"], shell=False, stderr=subprocess.STDOUT) + .decode("utf-8") + .strip() + ) except subprocess.CalledProcessError: pass - machineID = ' '.join(manufacturer.split()) + ' ' + ' '.join(model.split()) + machineID = " ".join(manufacturer.split()) + " " + " ".join(model.split()) # CPU information try: - sockets = subprocess.check_output(["sysctl", "-n", "hw.packages"], - shell=False, - stderr=subprocess.STDOUT).decode('utf-8').strip() + sockets = ( + subprocess.check_output(["sysctl", "-n", "hw.packages"], shell=False, stderr=subprocess.STDOUT) + .decode("utf-8") + .strip() + ) sockets = int(sockets) - cpuID = subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"], - shell=False, - stderr=subprocess.STDOUT).decode('utf-8').strip() - cpuID = ' '.join(cpuID.split()) + cpuID = ( + subprocess.check_output( + ["sysctl", "-n", "machdep.cpu.brand_string"], shell=False, stderr=subprocess.STDOUT + ) + .decode("utf-8") + .strip() + ) + cpuID = " ".join(cpuID.split()) except subprocess.CalledProcessError: pass @@ -126,40 +143,50 @@ def get_host_info(): hyperthreading = False # OS version - osversion = 'macOS (' + platform.mac_ver()[0] + ')' + osversion = "macOS (" + platform.mac_ver()[0] + ")" # Linux - elif sys.platform == 'linux': + elif sys.platform == "linux": # Manufacturer/model try: - manufacturer = subprocess.check_output(["cat", "/sys/class/dmi/id/sys_vendor"], - shell=False, - stderr=subprocess.STDOUT).decode('utf-8').strip() - model = subprocess.check_output(["cat", "/sys/class/dmi/id/product_name"], - shell=False, - stderr=subprocess.STDOUT).decode('utf-8').strip() + manufacturer = ( + subprocess.check_output(["cat", "/sys/class/dmi/id/sys_vendor"], shell=False, stderr=subprocess.STDOUT) + .decode("utf-8") + .strip() + ) + model = ( + subprocess.check_output( + ["cat", "/sys/class/dmi/id/product_name"], shell=False, stderr=subprocess.STDOUT + ) + .decode("utf-8") + .strip() + ) except subprocess.CalledProcessError: pass - machineID = ' '.join(manufacturer.split()) + ' ' + ' '.join(model.split()) + machineID = " ".join(manufacturer.split()) + " " + " ".join(model.split()) # CPU information try: # Locale to ensure English - myenv = {**os.environ, 'LANG': 'en_US.utf8'} - cpuIDinfo = subprocess.check_output(["cat", "/proc/cpuinfo"], shell=False, - stderr=subprocess.STDOUT, - env=myenv).decode('utf-8').strip() - for line in cpuIDinfo.split('\n'): - if re.search('model name', line): - cpuID = re.sub('.*model name.*:', '', line, 1).strip() - cpuID = ' '.join(cpuID.split()) - allcpuinfo = subprocess.check_output(["lscpu"], shell=False, - stderr=subprocess.STDOUT, - env=myenv).decode('utf-8').strip() - for line in allcpuinfo.split('\n'): - if 'Socket(s)' in line: + myenv = {**os.environ, "LANG": "en_US.utf8"} + cpuIDinfo = ( + subprocess.check_output(["cat", "/proc/cpuinfo"], shell=False, stderr=subprocess.STDOUT, env=myenv) + .decode("utf-8") + .strip() + ) + for line in cpuIDinfo.split("\n"): + if re.search("model name", line): + cpuID = re.sub(".*model name.*:", "", line, 1).strip() + cpuID = " ".join(cpuID.split()) + allcpuinfo = ( + subprocess.check_output(["lscpu"], shell=False, stderr=subprocess.STDOUT, env=myenv) + .decode("utf-8") + .strip() + ) + for line in allcpuinfo.split("\n"): + if "Socket(s)" in line: sockets = int(line.strip()[-1]) - if 'Thread(s) per core' in line: + if "Thread(s) per core" in line: threadspercore = int(line.strip()[-1]) except subprocess.CalledProcessError: pass @@ -172,25 +199,25 @@ def get_host_info(): # Dictionary of host information hostinfo = {} - hostinfo['hostname'] = platform.node() - hostinfo['machineID'] = machineID.strip() - hostinfo['sockets'] = sockets - hostinfo['cpuID'] = cpuID - hostinfo['osversion'] = osversion - hostinfo['hyperthreading'] = hyperthreading - hostinfo['logicalcores'] = psutil.cpu_count() + hostinfo["hostname"] = platform.node() + hostinfo["machineID"] = machineID.strip() + hostinfo["sockets"] = sockets + hostinfo["cpuID"] = cpuID + hostinfo["osversion"] = osversion + hostinfo["hyperthreading"] = hyperthreading + hostinfo["logicalcores"] = psutil.cpu_count() try: # Get number of physical CPU cores, i.e. avoid hyperthreading with OpenMP - hostinfo['physicalcores'] = psutil.cpu_count(logical=False) + hostinfo["physicalcores"] = psutil.cpu_count(logical=False) except ValueError: - hostinfo['physicalcores'] = hostinfo['logicalcores'] + hostinfo["physicalcores"] = hostinfo["logicalcores"] # Handle case where cpu_count returns None on some machines - if not hostinfo['physicalcores']: - hostinfo['physicalcores'] = hostinfo['logicalcores'] + if not hostinfo["physicalcores"]: + hostinfo["physicalcores"] = hostinfo["logicalcores"] - hostinfo['ram'] = psutil.virtual_memory().total + hostinfo["ram"] = psutil.virtual_memory().total return hostinfo @@ -199,19 +226,24 @@ def print_host_info(hostinfo): """Prints information about the machine, CPU, RAM, and OS. Args: - hostinfo: dict containing manufacturer and model of machine; + hostinfo: dict containing manufacturer and model of machine; description of CPU type, speed, cores; RAM; name and version of operating system. """ - hyperthreadingstr = (f", {config.sim_config.hostinfo['logicalcores']} " - f"cores with Hyper-Threading" if config.sim_config.hostinfo['hyperthreading'] else '') - logger.basic(f"\n{config.sim_config.hostinfo['hostname']} | " - f"{config.sim_config.hostinfo['machineID']} | " - f"{hostinfo['sockets']} x {hostinfo['cpuID']} " - f"({hostinfo['physicalcores']} cores{hyperthreadingstr}) | " - f"{humanize.naturalsize(hostinfo['ram'], True)} | " - f"{hostinfo['osversion']}") + hyperthreadingstr = ( + f", {config.sim_config.hostinfo['logicalcores']} " f"cores with Hyper-Threading" + if config.sim_config.hostinfo["hyperthreading"] + else "" + ) + logger.basic( + f"\n{config.sim_config.hostinfo['hostname']} | " + f"{config.sim_config.hostinfo['machineID']} | " + f"{hostinfo['sockets']} x {hostinfo['cpuID']} " + f"({hostinfo['physicalcores']} cores{hyperthreadingstr}) | " + f"{humanize.naturalsize(hostinfo['ram'], True)} | " + f"{hostinfo['osversion']}" + ) logger.basic(f"|--->OpenMP: {hostinfo['physicalcores']} threads") @@ -222,42 +254,42 @@ def set_omp_threads(nthreads=None): nthreads: int for number of OpenMP threads. """ - if sys.platform == 'darwin': + if sys.platform == "darwin": # Should waiting threads consume CPU power (can drastically effect # performance) - if 'Apple' in config.sim_config.hostinfo['cpuID']: + if "Apple" in config.sim_config.hostinfo["cpuID"]: # https://developer.apple.com/documentation/apple-silicon/tuning-your-code-s-performance-for-apple-silicon - os.environ['OMP_WAIT_POLICY'] = 'PASSIVE' + os.environ["OMP_WAIT_POLICY"] = "PASSIVE" else: - os.environ['OMP_WAIT_POLICY'] = 'ACTIVE' + os.environ["OMP_WAIT_POLICY"] = "ACTIVE" # Number of threads may be adjusted by the run time environment to best # utilize system resources - os.environ['OMP_DYNAMIC'] = 'FALSE' + os.environ["OMP_DYNAMIC"] = "FALSE" # Each place corresponds to a single core (having one or more hardware threads) - os.environ['OMP_PLACES'] = 'cores' + os.environ["OMP_PLACES"] = "cores" # Bind threads to physical cores - os.environ['OMP_PROC_BIND'] = 'TRUE' + os.environ["OMP_PROC_BIND"] = "TRUE" # Prints OMP version and environment variables (useful for debug) # os.environ['OMP_DISPLAY_ENV'] = 'TRUE' # Catch bug with Windows Subsystem for Linux (https://github.com/Microsoft/BashOnWindows/issues/785) - if 'Microsoft' in config.sim_config.hostinfo['osversion']: - os.environ['KMP_AFFINITY'] = 'disabled' - del os.environ['OMP_PLACES'] - del os.environ['OMP_PROC_BIND'] + if "Microsoft" in config.sim_config.hostinfo["osversion"]: + os.environ["KMP_AFFINITY"] = "disabled" + del os.environ["OMP_PLACES"] + del os.environ["OMP_PROC_BIND"] if nthreads: - os.environ['OMP_NUM_THREADS'] = str(nthreads) - elif os.environ.get('OMP_NUM_THREADS'): - nthreads = int(os.environ.get('OMP_NUM_THREADS')) + os.environ["OMP_NUM_THREADS"] = str(nthreads) + elif os.environ.get("OMP_NUM_THREADS"): + nthreads = int(os.environ.get("OMP_NUM_THREADS")) else: # Set number of threads to number of physical CPU cores - nthreads = config.sim_config.hostinfo['physicalcores'] - os.environ['OMP_NUM_THREADS'] = str(nthreads) + nthreads = config.sim_config.hostinfo["physicalcores"] + os.environ["OMP_NUM_THREADS"] = str(nthreads) return nthreads @@ -268,10 +300,12 @@ def mem_check_host(mem): Args: mem: int for memory required (bytes). """ - if mem > config.sim_config.hostinfo['ram']: - logger.exception(f"Memory (RAM) required ~{humanize.naturalsize(mem)} exceeds " - f"{humanize.naturalsize(config.sim_config.hostinfo['ram'], True)} " - "detected!\n") + if mem > config.sim_config.hostinfo["ram"]: + logger.exception( + f"Memory (RAM) required ~{humanize.naturalsize(mem)} exceeds " + f"{humanize.naturalsize(config.sim_config.hostinfo['ram'], True)} " + "detected!\n" + ) raise ValueError @@ -284,26 +318,28 @@ def mem_check_device_snaps(total_mem, snaps_mem): snaps_mem: int for memory required for all snapshots (bytes). """ - if config.sim_config.general['solver'] == 'cuda': - device_mem = config.get_model_config().device['dev'].total_memory() - elif config.sim_config.general['solver'] == 'opencl': - device_mem = config.get_model_config().device['dev'].global_mem_size - + if config.sim_config.general["solver"] == "cuda": + device_mem = config.get_model_config().device["dev"].total_memory() + elif config.sim_config.general["solver"] == "opencl": + device_mem = config.get_model_config().device["dev"].global_mem_size + if total_mem - snaps_mem > device_mem: - logger.exception(f"Memory (RAM) required ~{humanize.naturalsize(total_mem)} exceeds " - f"{humanize.naturalsize(device_mem, True)} " - f"detected on specified {' '.join(config.get_model_config().device['dev'].name.split())} device!\n") + logger.exception( + f"Memory (RAM) required ~{humanize.naturalsize(total_mem)} exceeds " + f"{humanize.naturalsize(device_mem, True)} " + f"detected on specified {' '.join(config.get_model_config().device['dev'].name.split())} device!\n" + ) raise ValueError # If the required memory without the snapshots will fit on the GPU then # transfer and store snaphots on host if snaps_mem != 0 and total_mem - snaps_mem < device_mem: - config.get_model_config().device['snapsgpu2cpu'] = True + config.get_model_config().device["snapsgpu2cpu"] = True def mem_check_run_all(grids): - """Checks memory required to run model for all grids, including for any - dispersive materials, snapshots, and if solver with GPU, whether + """Checks memory required to run model for all grids, including for any + dispersive materials, snapshots, and if solver with GPU, whether snapshots will fit on GPU memory. Args: @@ -320,7 +356,7 @@ def mem_check_run_all(grids): for grid in grids: # Keep track of total memory for each model in - # config.get_model_config().mem_use, which can contain multiple grids, + # config.get_model_config().mem_use, which can contain multiple grids, # and also total memory per grid in grid.mem_use # Memory required for main grid arrays @@ -328,7 +364,7 @@ def mem_check_run_all(grids): grid.mem_use += grid.mem_est_basic() # Additional memory required if there are any dispersive materials. - if config.get_model_config().materials['maxpoles'] != 0: + if config.get_model_config().materials["maxpoles"] != 0: config.get_model_config().mem_use += grid.mem_est_dispersive() grid.mem_use += grid.mem_est_dispersive() @@ -339,7 +375,7 @@ def mem_check_run_all(grids): grid.mem_use += snap.nbytes total_mem_snaps += snap.nbytes - mem_strs.append(f'~{humanize.naturalsize(grid.mem_use)} [{grid.name}]') + mem_strs.append(f"~{humanize.naturalsize(grid.mem_use)} [{grid.name}]") total_mem_model = config.get_model_config().mem_use @@ -347,8 +383,11 @@ def mem_check_run_all(grids): mem_check_host(total_mem_model) # Check if there is sufficient memory for any snapshots on GPU - if (total_mem_snaps > 0 and config.sim_config.general['solver'] == 'cuda' or - config.sim_config.general['solver'] == 'opencl'): + if ( + total_mem_snaps > 0 + and config.sim_config.general["solver"] == "cuda" + or config.sim_config.general["solver"] == "opencl" + ): mem_check_device_snaps(total_mem_model, total_mem_snaps) return total_mem_model, mem_strs @@ -357,7 +396,7 @@ def mem_check_run_all(grids): def mem_check_build_all(grids): """Checks memory required to build all grids - primarily memory required to initialise grid arrays and to build any FractalVolume or FractalSurface - objects which can require significant amounts of memory. + objects which can require significant amounts of memory. Args: grids: list of FDTDGrid objects. @@ -381,7 +420,7 @@ def mem_check_build_all(grids): if grid.fractalvolumes: grid_mem += grid.mem_est_fractals() - mem_strs.append(f'~{humanize.naturalsize(grid_mem)} [{grid.name}]') + mem_strs.append(f"~{humanize.naturalsize(grid_mem)} [{grid.name}]") total_mem_model += grid_mem # Check if there is sufficient memory on host @@ -389,6 +428,7 @@ def mem_check_build_all(grids): return total_mem_model, mem_strs + def has_pycuda(): """Checks if pycuda module is installed.""" pycuda = True @@ -419,22 +459,25 @@ def detect_cuda_gpus(): gpus = {} - cuda_reqs = ('To use gprMax with CUDA you must:' - '\n 1) install pycuda' - '\n 2) install NVIDIA CUDA Toolkit (https://developer.nvidia.com/cuda-toolkit)' - '\n 3) have an NVIDIA CUDA-Enabled GPU (https://developer.nvidia.com/cuda-gpus)') + cuda_reqs = ( + "To use gprMax with CUDA you must:" + "\n 1) install pycuda" + "\n 2) install NVIDIA CUDA Toolkit (https://developer.nvidia.com/cuda-toolkit)" + "\n 3) have an NVIDIA CUDA-Enabled GPU (https://developer.nvidia.com/cuda-gpus)" + ) if has_pycuda(): import pycuda.driver as drv + drv.init() # Check and list any CUDA-Enabled GPUs deviceIDsavail = [] if drv.Device.count() == 0: - logger.warning('No NVIDIA CUDA-Enabled GPUs detected!\n' + cuda_reqs) - elif 'CUDA_VISIBLE_DEVICES' in os.environ: - deviceIDsavail = os.environ.get('CUDA_VISIBLE_DEVICES') - deviceIDsavail = [int(s) for s in deviceIDsavail.split(',')] + logger.warning("No NVIDIA CUDA-Enabled GPUs detected!\n" + cuda_reqs) + elif "CUDA_VISIBLE_DEVICES" in os.environ: + deviceIDsavail = os.environ.get("CUDA_VISIBLE_DEVICES") + deviceIDsavail = [int(s) for s in deviceIDsavail.split(",")] else: deviceIDsavail = range(drv.Device.count()) @@ -443,27 +486,29 @@ def detect_cuda_gpus(): gpus[ID] = drv.Device(ID) else: - logger.warning('pycuda not detected!\n' + cuda_reqs) + logger.warning("pycuda not detected!\n" + cuda_reqs) return gpus def print_cuda_info(devs): """"Prints info about detected CUDA-capable GPU(s). - + Args: devs: dict of detected pycuda device object(s) where where device ID(s) are keys. - """"" + """ "" import pycuda - logger.basic('|--->CUDA:') - logger.debug(f'PyCUDA: {pycuda.VERSION_TEXT}') + logger.basic("|--->CUDA:") + logger.debug(f"PyCUDA: {pycuda.VERSION_TEXT}") for ID, gpu in devs.items(): - logger.basic(f" |--->Device {ID}: {' '.join(gpu.name().split())} | " - f"{humanize.naturalsize(gpu.total_memory(), True)}") + logger.basic( + f" |--->Device {ID}: {' '.join(gpu.name().split())} | " + f"{humanize.naturalsize(gpu.total_memory(), True)}" + ) def detect_opencl(): @@ -476,13 +521,16 @@ def detect_opencl(): devs = {} - ocl_reqs = ('To use gprMax with OpenCL you must:' - '\n 1) install pyopencl' - '\n 2) install appropriate OpenCL device driver(s)' - '\n 3) have at least one OpenCL-capable platform.') + ocl_reqs = ( + "To use gprMax with OpenCL you must:" + "\n 1) install pyopencl" + "\n 2) install appropriate OpenCL device driver(s)" + "\n 3) have at least one OpenCL-capable platform." + ) if has_pyopencl(): import pyopencl as cl + try: i = 0 for platform in cl.get_platforms(): @@ -490,37 +538,39 @@ def detect_opencl(): devs[i] = device i += 1 except: - logger.warning('No OpenCL-capable platforms detected!\n' + ocl_reqs) + logger.warning("No OpenCL-capable platforms detected!\n" + ocl_reqs) else: - logger.warning('pyopencl not detected!\n' + ocl_reqs) + logger.warning("pyopencl not detected!\n" + ocl_reqs) return devs def print_opencl_info(devs): """"Prints info about detected OpenCL-capable device(s). - + Args: devs: dict of detected pyopencl device object(s) where where device ID(s) are keys. - """"" + """ "" import pyopencl as cl - logger.basic('|--->OpenCL:') - logger.debug(f'PyOpenCL: {cl.VERSION_TEXT}') + logger.basic("|--->OpenCL:") + logger.debug(f"PyOpenCL: {cl.VERSION_TEXT}") for i, (ID, dev) in enumerate(devs.items()): if i == 0: platform = dev.platform.name - logger.basic(f' |--->Platform: {platform}') + logger.basic(f" |--->Platform: {platform}") if platform != dev.platform.name: - logger.basic(f' |--->Platform: {dev.platform.name}') + logger.basic(f" |--->Platform: {dev.platform.name}") types = cl.device_type.to_string(dev.type) - if 'CPU' in types: - type = 'CPU' - if 'GPU' in types: - type = 'GPU' - logger.basic(f" |--->Device {ID}: {type} | {' '.join(dev.name.split())} | " - f"{humanize.naturalsize(dev.global_mem_size, True)}") + if "CPU" in types: + type = "CPU" + if "GPU" in types: + type = "GPU" + logger.basic( + f" |--->Device {ID}: {type} | {' '.join(dev.name.split())} | " + f"{humanize.naturalsize(dev.global_mem_size, True)}" + ) diff --git a/gprMax/utilities/logging.py b/gprMax/utilities/logging.py index d7f4d5c4..eb30d720 100644 --- a/gprMax/utilities/logging.py +++ b/gprMax/utilities/logging.py @@ -28,28 +28,32 @@ logger = logging.getLogger(__name__) BASIC_NUM = 25 logging.addLevelName(BASIC_NUM, "BASIC") logging.BASIC = BASIC_NUM + + def basic(self, message, *args, **kws): if self.isEnabledFor(BASIC_NUM): self._log(BASIC_NUM, message, args, **kws) + + logging.Logger.basic = basic # Colour mapping for different log levels MAPPING = { - 'DEBUG' : 37, # white - 'BASIC' : 37, # white - 'INFO' : 37, # white - 'WARNING' : 33, # yellow - 'ERROR' : 31, # red - 'CRITICAL': 41, # white on red bg + "DEBUG": 37, # white + "BASIC": 37, # white + "INFO": 37, # white + "WARNING": 33, # yellow + "ERROR": 31, # red + "CRITICAL": 41, # white on red bg } -PREFIX = '\033[' -SUFFIX = '\033[0m' - +PREFIX = "\033[" +SUFFIX = "\033[0m" + class CustomFormatter(logging.Formatter): - """Logging Formatter to add colors and count warning / errors - (https://stackoverflow.com/a/46482050).""" + """Logging Formatter to add colors and count warning / errors + (https://stackoverflow.com/a/46482050).""" def __init__(self, pattern): logging.Formatter.__init__(self, pattern) @@ -57,14 +61,14 @@ class CustomFormatter(logging.Formatter): def format(self, record): colored_record = copy(record) levelname = colored_record.levelname - seq = MAPPING.get(levelname, 37) # default white - colored_levelname = f'{PREFIX}{seq}m{levelname}{SUFFIX}' + seq = MAPPING.get(levelname, 37) # default white + colored_levelname = f"{PREFIX}{seq}m{levelname}{SUFFIX}" colored_record.levelname = colored_levelname - colored_record.msg = f'{PREFIX}{seq}m{colored_record.getMessage()}{SUFFIX}' + colored_record.msg = f"{PREFIX}{seq}m{colored_record.getMessage()}{SUFFIX}" return logging.Formatter.format(self, colored_record) -def logging_config(name='gprMax', level=logging.INFO, format_style='std', log_file=False): +def logging_config(name="gprMax", level=logging.INFO, format_style="std", log_file=False): """Setup and configure logging. Args: @@ -78,9 +82,9 @@ def logging_config(name='gprMax', level=logging.INFO, format_style='std', log_fi format_full = "%(asctime)s:%(levelname)s:%(name)s:%(lineno)d: %(message)s" # Set format style - if format_style == 'full' or level == logging.DEBUG: + if format_style == "full" or level == logging.DEBUG: format = format_full - elif format_style == 'std': + elif format_style == "std": format = format_std # Create main top-level logger @@ -92,16 +96,15 @@ def logging_config(name='gprMax', level=logging.INFO, format_style='std', log_fi handler = logging.StreamHandler(sys.stdout) handler.setLevel(level) handler.setFormatter(CustomFormatter(format)) - if (logger.hasHandlers()): + if logger.hasHandlers(): logger.handlers.clear() logger.addHandler(handler) # Config for logging to file if required if log_file: - filename = (name + '-log-' + - datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + '.txt') - handler = logging.FileHandler(filename, mode='w') + filename = name + "-log-" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + ".txt" + handler = logging.FileHandler(filename, mode="w") formatter = logging.Formatter(format_full) handler.setLevel(logging.DEBUG) handler.setFormatter(formatter) - logger.addHandler(handler) \ No newline at end of file + logger.addHandler(handler) diff --git a/gprMax/utilities/utilities.py b/gprMax/utilities/utilities.py index b5cbe682..2983e50e 100644 --- a/gprMax/utilities/utilities.py +++ b/gprMax/utilities/utilities.py @@ -34,9 +34,12 @@ try: from time import thread_time as timer_fn except ImportError: from time import perf_counter as timer_fn - logger.debug('"thread_time" not currently available in macOS and bug' - ' (https://bugs.python.org/issue36205) with "process_time", ' - 'so use "perf_counter".') + + logger.debug( + '"thread_time" not currently available in macOS and bug' + ' (https://bugs.python.org/issue36205) with "process_time", ' + 'so use "perf_counter".' + ) def get_terminal_width(): @@ -63,47 +66,53 @@ def logo(version): str: string containing logo, version, and licencing/copyright info. """ - description = ('\n=== Electromagnetic modelling software based on the ' - 'Finite-Difference Time-Domain (FDTD) method') + description = "\n=== Electromagnetic modelling software based on the " "Finite-Difference Time-Domain (FDTD) method" current_year = datetime.datetime.now().year - copyright = (f'Copyright (C) 2015-{current_year}: The University of ' - 'Edinburgh, United Kingdom') - authors = 'Authors: Craig Warren, Antonis Giannopoulos, and John Hartley' - licenseinfo1 = ('gprMax is free software: you can redistribute it and/or ' - 'modify it under the terms of the GNU General Public ' - 'License as published by the Free Software Foundation, ' - 'either version 3 of the License, or (at your option) any ' - 'later version.\n') - licenseinfo2 = ('gprMax is distributed in the hope that it will be useful, ' - 'but WITHOUT ANY WARRANTY; without even the implied ' - 'warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ' - 'PURPOSE. See the GNU General Public License for more ' - 'details.') - licenseinfo3 = ('You should have received a copy of the GNU General Public ' - 'License along with gprMax. If not, ' - 'see www.gnu.org/licenses.') + copyright = f"Copyright (C) 2015-{current_year}: The University of " "Edinburgh, United Kingdom" + authors = "Authors: Craig Warren, Antonis Giannopoulos, and John Hartley" + licenseinfo1 = ( + "gprMax is free software: you can redistribute it and/or " + "modify it under the terms of the GNU General Public " + "License as published by the Free Software Foundation, " + "either version 3 of the License, or (at your option) any " + "later version.\n" + ) + licenseinfo2 = ( + "gprMax is distributed in the hope that it will be useful, " + "but WITHOUT ANY WARRANTY; without even the implied " + "warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR " + "PURPOSE. See the GNU General Public License for more " + "details." + ) + licenseinfo3 = ( + "You should have received a copy of the GNU General Public " + "License along with gprMax. If not, " + "see www.gnu.org/licenses." + ) - logo = """ www.gprmax.com __ __ + logo = ( + """ www.gprmax.com __ __ __ _ _ __ _ __| \/ | __ ___ __ / _` | '_ \| '__| |\/| |/ _` \ \/ / | (_| | |_) | | | | | | (_| |> < \__, | .__/|_| |_| |_|\__,_/_/\_\\ |___/|_| - v""" + version + '\n\n' + v""" + + version + + "\n\n" + ) str = f"{description} {'=' * (get_terminal_width() - len(description) - 1)}\n\n" - str += f'{Fore.CYAN}{logo}' - str += Style.RESET_ALL + textwrap.fill(copyright, - width=get_terminal_width() - 1, - initial_indent=' ') + '\n' - str += textwrap.fill(authors, width=get_terminal_width() - 1, - initial_indent=' ') + '\n\n' - str += textwrap.fill(licenseinfo1, width=get_terminal_width() - 1, - initial_indent=' ', subsequent_indent=' ') + '\n' - str += textwrap.fill(licenseinfo2, width=get_terminal_width() - 1, - initial_indent=' ', subsequent_indent=' ') + '\n' - str += textwrap.fill(licenseinfo3, width=get_terminal_width() - 1, - initial_indent=' ', subsequent_indent=' ') + str += f"{Fore.CYAN}{logo}" + str += Style.RESET_ALL + textwrap.fill(copyright, width=get_terminal_width() - 1, initial_indent=" ") + "\n" + str += textwrap.fill(authors, width=get_terminal_width() - 1, initial_indent=" ") + "\n\n" + str += ( + textwrap.fill(licenseinfo1, width=get_terminal_width() - 1, initial_indent=" ", subsequent_indent=" ") + "\n" + ) + str += ( + textwrap.fill(licenseinfo2, width=get_terminal_width() - 1, initial_indent=" ", subsequent_indent=" ") + "\n" + ) + str += textwrap.fill(licenseinfo3, width=get_terminal_width() - 1, initial_indent=" ", subsequent_indent=" ") return str @@ -122,14 +131,12 @@ def round_value(value, decimalplaces=0): # Rounds to nearest integer (half values are rounded downwards) if decimalplaces == 0: - rounded = int(d.Decimal(value).quantize(d.Decimal('1'), - rounding=d.ROUND_HALF_DOWN)) + rounded = int(d.Decimal(value).quantize(d.Decimal("1"), rounding=d.ROUND_HALF_DOWN)) # Rounds down to nearest float represented by number of decimal places else: precision = f"1.{'0' * decimalplaces}" - rounded = float(d.Decimal(value).quantize(d.Decimal(precision), - rounding=d.ROUND_FLOOR)) + rounded = float(d.Decimal(value).quantize(d.Decimal(precision), rounding=d.ROUND_FLOOR)) return rounded @@ -152,10 +159,10 @@ def fft_power(waveform, dt): power: array containing power spectra. """ - # Calculate magnitude of frequency spectra of waveform (ignore warning from + # Calculate magnitude of frequency spectra of waveform (ignore warning from # taking a log of any zero values) - with np.errstate(divide='ignore'): - power = 10 * np.log10(np.abs(np.fft.fft(waveform))**2) + with np.errstate(divide="ignore"): + power = 10 * np.log10(np.abs(np.fft.fft(waveform)) ** 2) # Replace any NaNs or Infs from zero division power[np.invert(np.isfinite(power))] = 0 @@ -181,7 +188,7 @@ def atoi(text): def natural_keys(text): """Human sorting of a string.""" - return [atoi(c) for c in re.split(r'(\d+)', text)] + return [atoi(c) for c in re.split(r"(\d+)", text)] def numeric_list_to_int_list(l): @@ -191,4 +198,4 @@ def numeric_list_to_int_list(l): def numeric_list_to_float_list(l): """List of float from a numerical list.""" - return list(map(float, l)) \ No newline at end of file + return list(map(float, l)) diff --git a/gprMax/waveforms.py b/gprMax/waveforms.py index 65e52d40..9137e4f7 100644 --- a/gprMax/waveforms.py +++ b/gprMax/waveforms.py @@ -25,9 +25,20 @@ logger = logging.getLogger(__name__) class Waveform: """Definitions of waveform shapes that can be used with sources.""" - types = ['gaussian', 'gaussiandot', 'gaussiandotnorm', 'gaussiandotdot', - 'gaussiandotdotnorm', 'gaussianprime', 'gaussiandoubleprime', - 'ricker', 'sine', 'contsine', 'impulse', 'user'] + types = [ + "gaussian", + "gaussiandot", + "gaussiandotnorm", + "gaussiandotdot", + "gaussiandotdotnorm", + "gaussianprime", + "gaussiandoubleprime", + "ricker", + "sine", + "contsine", + "impulse", + "user", + ] # Information about specific waveforms: # @@ -51,17 +62,13 @@ class Waveform: def calculate_coefficients(self): """Calculates coefficients (used to calculate values) for specific - waveforms. + waveforms. """ - if self.type in ['gaussian', - 'gaussiandot', - 'gaussiandotnorm', - 'gaussianprime', - 'gaussiandoubleprime']: + if self.type in ["gaussian", "gaussiandot", "gaussiandotnorm", "gaussianprime", "gaussiandoubleprime"]: self.chi = 1 / self.freq self.zeta = 2 * np.pi**2 * self.freq**2 - elif self.type in ['gaussiandotdot', 'gaussiandotdotnorm', 'ricker']: + elif self.type in ["gaussiandotdot", "gaussiandotdotnorm", "ricker"]: self.chi = np.sqrt(2) / self.freq self.zeta = np.pi**2 * self.freq**2 @@ -79,56 +86,55 @@ class Waveform: self.calculate_coefficients() # Waveforms - if self.type == 'gaussian': + if self.type == "gaussian": delay = time - self.chi ampvalue = np.exp(-self.zeta * delay**2) - elif self.type in ['gaussiandot', 'gaussianprime']: + elif self.type in ["gaussiandot", "gaussianprime"]: delay = time - self.chi ampvalue = -2 * self.zeta * delay * np.exp(-self.zeta * delay**2) - elif self.type == 'gaussiandotnorm': + elif self.type == "gaussiandotnorm": delay = time - self.chi normalise = np.sqrt(np.exp(1) / (2 * self.zeta)) ampvalue = -2 * self.zeta * delay * np.exp(-self.zeta * delay**2) * normalise - elif self.type in ['gaussiandotdot', 'gaussiandoubleprime']: + elif self.type in ["gaussiandotdot", "gaussiandoubleprime"]: delay = time - self.chi - ampvalue = (2 * self.zeta * (2 * self.zeta * delay**2 - 1) * - np.exp(-self.zeta * delay**2)) + ampvalue = 2 * self.zeta * (2 * self.zeta * delay**2 - 1) * np.exp(-self.zeta * delay**2) - elif self.type == 'gaussiandotdotnorm': + elif self.type == "gaussiandotdotnorm": delay = time - self.chi normalise = 1 / (2 * self.zeta) - ampvalue = (2 * self.zeta * (2 * self.zeta * delay**2 - 1) * - np.exp(-self.zeta * delay**2) * normalise) + ampvalue = 2 * self.zeta * (2 * self.zeta * delay**2 - 1) * np.exp(-self.zeta * delay**2) * normalise - elif self.type == 'ricker': + elif self.type == "ricker": delay = time - self.chi normalise = 1 / (2 * self.zeta) - ampvalue = - ((2 * self.zeta * (2 * self.zeta * delay**2 - 1) * - np.exp(-self.zeta * delay**2)) * normalise) + ampvalue = -( + (2 * self.zeta * (2 * self.zeta * delay**2 - 1) * np.exp(-self.zeta * delay**2)) * normalise + ) - elif self.type == 'sine': + elif self.type == "sine": ampvalue = np.sin(2 * np.pi * self.freq * time) if time * self.freq > 1: ampvalue = 0 - elif self.type == 'contsine': + elif self.type == "contsine": rampamp = 0.25 ramp = rampamp * time * self.freq ramp = min(ramp, 1) ampvalue = ramp * np.sin(2 * np.pi * self.freq * time) - elif self.type == 'impulse': + elif self.type == "impulse": # time < dt condition required to do impulsive magnetic dipole if time == 0 or time < dt: ampvalue = 1 elif time >= dt: ampvalue = 0 - - elif self.type == 'user': + + elif self.type == "user": ampvalue = self.userfunc(time) ampvalue *= self.amp diff --git a/setup.py b/setup.py index 8f3acf4f..8f23782a 100644 --- a/setup.py +++ b/setup.py @@ -33,25 +33,26 @@ from setuptools import Extension, find_packages, setup # Check Python version MIN_PYTHON_VERSION = (3, 7) if sys.version_info[:2] < MIN_PYTHON_VERSION: - sys.exit('\nExited: Requires Python {MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]} or newer!\n') + sys.exit("\nExited: Requires Python {MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]} or newer!\n") # Importing gprMax _version__.py before building can cause issues. -with open('gprMax/_version.py', 'r') as fd: - version = re.search( - r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE - )[1] +with open("gprMax/_version.py", "r") as fd: + version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE)[1] + def build_dispersive_material_templates(): """Function to generate Cython .pyx file for dispersive media update. - Jinja2 templates are used to render the various dispersive update - functions. + Jinja2 templates are used to render the various dispersive update + functions. """ - iswin = (sys.platform == 'win32') + iswin = sys.platform == "win32" - env = Environment(loader = FileSystemLoader(os.path.join('gprMax', 'cython')), ) + env = Environment( + loader=FileSystemLoader(os.path.join("gprMax", "cython")), + ) - template = env.get_template('fields_updates_dispersive_template.jinja') + template = env.get_template("fields_updates_dispersive_template.jinja") # Render dispersive template for different types r = template.render( @@ -59,234 +60,247 @@ def build_dispersive_material_templates(): # templates for Double precision and dispersive materials with # real susceptibility functions { - 'name_a': 'update_electric_dispersive_multipole_A_double_real', - 'name_b': 'update_electric_dispersive_multipole_B_double_real', - 'name_a_1': 'update_electric_dispersive_1pole_A_double_real', - 'name_b_1': 'update_electric_dispersive_1pole_B_double_real', - 'field_type': 'double', - 'dispersive_type': 'double', - 'iswin': iswin + "name_a": "update_electric_dispersive_multipole_A_double_real", + "name_b": "update_electric_dispersive_multipole_B_double_real", + "name_a_1": "update_electric_dispersive_1pole_A_double_real", + "name_b_1": "update_electric_dispersive_1pole_B_double_real", + "field_type": "double", + "dispersive_type": "double", + "iswin": iswin, }, # templates for Float precision and dispersive materials with # real susceptibility functions { - 'name_a': 'update_electric_dispersive_multipole_A_float_real', - 'name_b': 'update_electric_dispersive_multipole_B_float_real', - 'name_a_1': 'update_electric_dispersive_1pole_A_float_real', - 'name_b_1': 'update_electric_dispersive_1pole_B_float_real', - 'field_type': 'float', - 'dispersive_type': 'float', - 'iswin': iswin + "name_a": "update_electric_dispersive_multipole_A_float_real", + "name_b": "update_electric_dispersive_multipole_B_float_real", + "name_a_1": "update_electric_dispersive_1pole_A_float_real", + "name_b_1": "update_electric_dispersive_1pole_B_float_real", + "field_type": "float", + "dispersive_type": "float", + "iswin": iswin, }, # templates for Double precision and dispersive materials with # complex susceptibility functions { - 'name_a': 'update_electric_dispersive_multipole_A_double_complex', - 'name_b': 'update_electric_dispersive_multipole_B_double_complex', - 'name_a_1': 'update_electric_dispersive_1pole_A_double_complex', - 'name_b_1': 'update_electric_dispersive_1pole_B_double_complex', - 'field_type': 'double', - 'dispersive_type': 'double complex', + "name_a": "update_electric_dispersive_multipole_A_double_complex", + "name_b": "update_electric_dispersive_multipole_B_double_complex", + "name_a_1": "update_electric_dispersive_1pole_A_double_complex", + "name_b_1": "update_electric_dispersive_1pole_B_double_complex", + "field_type": "double", + "dispersive_type": "double complex", # c function to take real part of complex double type - 'real_part': 'creal', - 'iswin': iswin + "real_part": "creal", + "iswin": iswin, }, # templates for Float precision and dispersive materials with # complex susceptibility functions { - 'name_a': 'update_electric_dispersive_multipole_A_float_complex', - 'name_b': 'update_electric_dispersive_multipole_B_float_complex', - 'name_a_1': 'update_electric_dispersive_1pole_A_float_complex', - 'name_b_1': 'update_electric_dispersive_1pole_B_float_complex', - 'field_type': 'float', - 'dispersive_type': 'float complex', + "name_a": "update_electric_dispersive_multipole_A_float_complex", + "name_b": "update_electric_dispersive_multipole_B_float_complex", + "name_a_1": "update_electric_dispersive_1pole_A_float_complex", + "name_b_1": "update_electric_dispersive_1pole_B_float_complex", + "field_type": "float", + "dispersive_type": "float complex", # c function to take real part of complex double type - 'real_part': 'crealf', - 'iswin': iswin - }] + "real_part": "crealf", + "iswin": iswin, + }, + ] ) - with open(os.path.join('gprMax', 'cython', 'fields_updates_dispersive.pyx'), 'w') as f: + with open(os.path.join("gprMax", "cython", "fields_updates_dispersive.pyx"), "w") as f: f.write(r) + # Generate Cython file for dispersive materials update functions -cython_disp_file = os.path.join('gprMax', 'cython', 'fields_updates_dispersive.pyx') +cython_disp_file = os.path.join("gprMax", "cython", "fields_updates_dispersive.pyx") if not os.path.isfile(cython_disp_file): build_dispersive_material_templates() # Process 'build' command line argument -if 'build' in sys.argv: +if "build" in sys.argv: print("Running 'build_ext --inplace'") - sys.argv.remove('build') - sys.argv.append('build_ext') - sys.argv.append('--inplace') + sys.argv.remove("build") + sys.argv.append("build_ext") + sys.argv.append("--inplace") -# Build a list of all the files that need to be Cythonized looking in gprMax +# Build a list of all the files that need to be Cythonized looking in gprMax # directory cythonfiles = [] -for root, dirs, files in os.walk(os.path.join(os.getcwd(), 'gprMax'), topdown=True): +for root, dirs, files in os.walk(os.path.join(os.getcwd(), "gprMax"), topdown=True): for file in files: - if file.endswith('.pyx'): + if file.endswith(".pyx"): cythonfiles.append(os.path.relpath(os.path.join(root, file))) # Process 'cleanall' command line argument -if 'cleanall' in sys.argv: +if "cleanall" in sys.argv: for file in cythonfiles: filebase = os.path.splitext(file)[0] # Remove Cython C files - if os.path.isfile(f'{filebase}.c'): + if os.path.isfile(f"{filebase}.c"): try: - os.remove(f'{filebase}.c') - print(f'Removed: {filebase}.c') + os.remove(f"{filebase}.c") + print(f"Removed: {filebase}.c") except OSError: - print(f'Could not remove: {filebase}.c') + print(f"Could not remove: {filebase}.c") # Remove compiled Cython modules - libfile = (glob.glob(os.path.join(os.getcwd(), - os.path.splitext(file)[0]) + '*.pyd') + - glob.glob(os.path.join(os.getcwd(), - os.path.splitext(file)[0]) + '*.so')) + libfile = glob.glob(os.path.join(os.getcwd(), os.path.splitext(file)[0]) + "*.pyd") + glob.glob( + os.path.join(os.getcwd(), os.path.splitext(file)[0]) + "*.so" + ) if libfile: libfile = libfile[0] try: os.remove(libfile) - print(f'Removed: {os.path.abspath(libfile)}') + print(f"Removed: {os.path.abspath(libfile)}") except OSError: - print(f'Could not remove: {os.path.abspath(libfile)}') + print(f"Could not remove: {os.path.abspath(libfile)}") # Remove build, dist, egg and __pycache__ directories - shutil.rmtree(Path.cwd().joinpath('build'), ignore_errors=True) - shutil.rmtree(Path.cwd().joinpath('dist'), ignore_errors=True) - shutil.rmtree(Path.cwd().joinpath('gprMax.egg-info'), ignore_errors=True) - for p in Path.cwd().rglob('__pycache__'): + shutil.rmtree(Path.cwd().joinpath("build"), ignore_errors=True) + shutil.rmtree(Path.cwd().joinpath("dist"), ignore_errors=True) + shutil.rmtree(Path.cwd().joinpath("gprMax.egg-info"), ignore_errors=True) + for p in Path.cwd().rglob("__pycache__"): shutil.rmtree(p, ignore_errors=True) - print(f'Removed: {p}') + print(f"Removed: {p}") # Remove 'gprMax/cython/fields_updates_dispersive.jinja' if its there if os.path.isfile(cython_disp_file): os.remove(cython_disp_file) # Now do a normal clean - sys.argv[1] = 'clean' # this is what distutils understands + sys.argv[1] = "clean" # this is what distutils understands else: # Compiler options - Windows - if sys.platform == 'win32': - # No static linking as no static version of OpenMP library; + if sys.platform == "win32": + # No static linking as no static version of OpenMP library; # /w disables warnings - compile_args = ['/O2', '/openmp', '/w'] + compile_args = ["/O2", "/openmp", "/w"] linker_args = [] libraries = [] - elif sys.platform == 'darwin': + elif sys.platform == "darwin": # Check for Intel or Apple M series CPU - cpuID = subprocess.check_output("sysctl -n machdep.cpu.brand_string", - shell=True, - stderr=subprocess.STDOUT).decode('utf-8').strip() - cpuID = ' '.join(cpuID.split()) - if 'Apple' in cpuID: - gccpath = glob.glob('/opt/homebrew/bin/gcc-[4-9]*') - gccpath += glob.glob('/opt/homebrew/bin/gcc-[10-11]*') + cpuID = ( + subprocess.check_output("sysctl -n machdep.cpu.brand_string", shell=True, stderr=subprocess.STDOUT) + .decode("utf-8") + .strip() + ) + cpuID = " ".join(cpuID.split()) + if "Apple" in cpuID: + gccpath = glob.glob("/opt/homebrew/bin/gcc-[4-9]*") + gccpath += glob.glob("/opt/homebrew/bin/gcc-[10-11]*") if gccpath: # Use newest gcc found - os.environ['CC'] = gccpath[-1].split(os.sep)[-1] - rpath = '/opt/homebrew/opt/gcc/lib/gcc/' + gccpath[-1].split(os.sep)[-1][-1] + '/' + os.environ["CC"] = gccpath[-1].split(os.sep)[-1] + rpath = "/opt/homebrew/opt/gcc/lib/gcc/" + gccpath[-1].split(os.sep)[-1][-1] + "/" else: - raise('Cannot find gcc in /opt/homebrew/bin. gprMax requires gcc ' + - 'to be installed - easily done through the Homebrew package ' + - 'manager (http://brew.sh). Note: gcc with OpenMP support ' + - 'is required.') + raise ( + "Cannot find gcc in /opt/homebrew/bin. gprMax requires gcc " + + "to be installed - easily done through the Homebrew package " + + "manager (http://brew.sh). Note: gcc with OpenMP support " + + "is required." + ) else: - gccpath = glob.glob('/usr/local/bin/gcc-[4-9]*') - gccpath += glob.glob('/usr/local/bin/gcc-[10-11]*') + gccpath = glob.glob("/usr/local/bin/gcc-[4-9]*") + gccpath += glob.glob("/usr/local/bin/gcc-[10-11]*") if gccpath: # Use newest gcc found - os.environ['CC'] = gccpath[-1].split(os.sep)[-1] + os.environ["CC"] = gccpath[-1].split(os.sep)[-1] else: - raise('Cannot find gcc in /usr/local/bin. gprMax requires gcc ' + - 'to be installed - easily done through the Homebrew package ' + - 'manager (http://brew.sh). Note: gcc with OpenMP support ' + - 'is required.') + raise ( + "Cannot find gcc in /usr/local/bin. gprMax requires gcc " + + "to be installed - easily done through the Homebrew package " + + "manager (http://brew.sh). Note: gcc with OpenMP support " + + "is required." + ) # Minimum supported macOS deployment target - MIN_MACOS_VERSION = '10.13' + MIN_MACOS_VERSION = "10.13" try: - os.environ['MACOSX_DEPLOYMENT_TARGET'] - del os.environ['MACOSX_DEPLOYMENT_TARGET'] + os.environ["MACOSX_DEPLOYMENT_TARGET"] + del os.environ["MACOSX_DEPLOYMENT_TARGET"] except: pass - os.environ['MIN_SUPPORTED_MACOSX_DEPLOYMENT_TARGET'] = MIN_MACOS_VERSION + os.environ["MIN_SUPPORTED_MACOSX_DEPLOYMENT_TARGET"] = MIN_MACOS_VERSION # Sometimes worth testing with '-fstrict-aliasing', '-fno-common' - compile_args = ['-O3', - '-w', - '-fopenmp', - '-march=native', - f'-mmacosx-version-min={MIN_MACOS_VERSION}'] - linker_args = ['-fopenmp', f'-mmacosx-version-min={MIN_MACOS_VERSION}'] - libraries=['gomp'] + compile_args = ["-O3", "-w", "-fopenmp", "-march=native", f"-mmacosx-version-min={MIN_MACOS_VERSION}"] + linker_args = ["-fopenmp", f"-mmacosx-version-min={MIN_MACOS_VERSION}"] + libraries = ["gomp"] - elif sys.platform == 'linux': - compile_args = ['-O3', '-w', '-fopenmp', '-march=native'] - linker_args = ['-fopenmp'] + elif sys.platform == "linux": + compile_args = ["-O3", "-w", "-fopenmp", "-march=native"] + linker_args = ["-fopenmp"] libraries = [] # Build list of all the extensions - Cython source files extensions = [] for file in cythonfiles: tmp = os.path.splitext(file) - extension = Extension(tmp[0].replace(os.sep, '.'), - [tmp[0] + tmp[1]], - language='c', - include_dirs=[np.get_include()], - extra_compile_args=compile_args, - extra_link_args=linker_args, - libraries=libraries) + extension = Extension( + tmp[0].replace(os.sep, "."), + [tmp[0] + tmp[1]], + language="c", + include_dirs=[np.get_include()], + extra_compile_args=compile_args, + extra_link_args=linker_args, + libraries=libraries, + ) extensions.append(extension) # Cythonize - build .c files - extensions = cythonize(extensions, - compiler_directives={'boundscheck': False, - 'wraparound': False, - 'initializedcheck': False, - 'embedsignature': True, - 'language_level': 3}, - nthreads=None, - annotate=False) + extensions = cythonize( + extensions, + compiler_directives={ + "boundscheck": False, + "wraparound": False, + "initializedcheck": False, + "embedsignature": True, + "language_level": 3, + }, + nthreads=None, + annotate=False, + ) # Parse long_description from README.rst file. - with open('README.rst','r') as fd: + with open("README.rst", "r") as fd: long_description = fd.read() - setup(name='gprMax', - version=version, - author='Craig Warren, Antonis Giannopoulos, and John Hartley', - url='http://www.gprmax.com', - description='Electromagnetic Modelling Software based on the ' - + 'Finite-Difference Time-Domain (FDTD) method', - long_description=long_description, - long_description_content_type="text/x-rst", - license='GPLv3+', - python_requires=f'>{str(MIN_PYTHON_VERSION[0])}.{str(MIN_PYTHON_VERSION[1])}', - install_requires=['colorama', - 'cython', - 'h5py', - 'jinja2', - 'matplotlib', - 'numpy', - 'psutil', - 'scipy', - 'terminaltables', - 'tqdm'], + setup( + name="gprMax", + version=version, + author="Craig Warren, Antonis Giannopoulos, and John Hartley", + url="http://www.gprmax.com", + description="Electromagnetic Modelling Software based on the " + "Finite-Difference Time-Domain (FDTD) method", + long_description=long_description, + long_description_content_type="text/x-rst", + license="GPLv3+", + python_requires=f">{str(MIN_PYTHON_VERSION[0])}.{str(MIN_PYTHON_VERSION[1])}", + install_requires=[ + "colorama", + "cython", + "h5py", + "jinja2", + "matplotlib", + "numpy", + "psutil", + "scipy", + "terminaltables", + "tqdm", + ], ext_modules=extensions, packages=find_packages(), include_package_data=True, include_dirs=[np.get_include()], zip_safe=False, - classifiers=['Environment :: Console', - 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', - 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Cython', - 'Programming Language :: Python :: 3', - 'Topic :: Scientific/Engineering'], + classifiers=[ + "Environment :: Console", + "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Cython", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering", + ], ) diff --git a/testing/analytical_solutions.py b/testing/analytical_solutions.py index 90becf41..4fa4a512 100644 --- a/testing/analytical_solutions.py +++ b/testing/analytical_solutions.py @@ -23,14 +23,14 @@ from gprMax.waveforms import Waveform def hertzian_dipole_fs(iterations, dt, dxdydz, rx): - """Analytical solution of a z-directed Hertzian dipole in free space with a + """Analytical solution of a z-directed Hertzian dipole in free space with a Gaussian current waveform (http://dx.doi.org/10.1016/0021-9991(83)90103-1). Args: iterations: int for number of time steps. dt: float for time step (seconds). dxdydz: tuple of floats for spatial resolution (metres). - rx: tuple of floats for coordinates of receiver position relative to + rx: tuple of floats for coordinates of receiver position relative to transmitter position (metres). Returns: @@ -39,25 +39,25 @@ def hertzian_dipole_fs(iterations, dt, dxdydz, rx): # Waveform w = Waveform() - w.type = 'gaussianprime' + w.type = "gaussianprime" w.amp = 1 w.freq = 1e9 # Waveform integral wint = Waveform() - wint.type = 'gaussian' + wint.type = "gaussian" wint.amp = w.amp wint.freq = w.freq # Waveform first derivative wdot = Waveform() - wdot.type = 'gaussiandoubleprime' + wdot.type = "gaussiandoubleprime" wdot.amp = w.amp wdot.freq = w.freq # Time time = np.linspace(0, 1, iterations) - time *= (iterations * dt) + time *= iterations * dt # Spatial resolution dx = dxdydz[0] @@ -77,43 +77,42 @@ def hertzian_dipole_fs(iterations, dt, dxdydz, rx): Ex_y = y Ex_z = z - 0.5 * dz Er_x = np.sqrt((Ex_x**2 + Ex_y**2 + Ex_z**2)) - tau_Ex = Er_x / config.sim_config.em_consts['c'] + tau_Ex = Er_x / config.sim_config.em_consts["c"] # Coordinates of Rx for Ey FDTD component Ey_x = x Ey_y = y + 0.5 * dy Ey_z = z - 0.5 * dz Er_y = np.sqrt((Ey_x**2 + Ey_y**2 + Ey_z**2)) - tau_Ey = Er_y / config.sim_config.em_consts['c'] + tau_Ey = Er_y / config.sim_config.em_consts["c"] # Coordinates of Rx for Ez FDTD component Ez_x = x Ez_y = y Ez_z = z Er_z = np.sqrt((Ez_x**2 + Ez_y**2 + Ez_z**2)) - tau_Ez = Er_z / config.sim_config.em_consts['c'] + tau_Ez = Er_z / config.sim_config.em_consts["c"] # Coordinates of Rx for Hx FDTD component Hx_x = x Hx_y = y + 0.5 * dy Hx_z = z Hr_x = np.sqrt((Hx_x**2 + Hx_y**2 + Hx_z**2)) - tau_Hx = Hr_x / config.sim_config.em_consts['c'] + tau_Hx = Hr_x / config.sim_config.em_consts["c"] # Coordinates of Rx for Hy FDTD component Hy_x = x + 0.5 * dx Hy_y = y Hy_z = z Hr_y = np.sqrt((Hy_x**2 + Hy_y**2 + Hy_z**2)) - tau_Hy = Hr_y / config.sim_config.em_consts['c'] + tau_Hy = Hr_y / config.sim_config.em_consts["c"] # Initialise fields fields = np.zeros((iterations, 6)) # Calculate fields for timestep in range(iterations): - - # Calculate values for waveform, I * dl (current multiplied by dipole + # Calculate values for waveform, I * dl (current multiplied by dipole # length) to match gprMax behaviour fint_Ex = wint.calculate_value((timestep * dt) - tau_Ex, dt) * dl f_Ex = w.calculate_value((timestep * dt) - tau_Ex, dt) * dl @@ -134,31 +133,36 @@ def hertzian_dipole_fs(iterations, dt, dxdydz, rx): fdot_Hy = wdot.calculate_value((timestep * dt) - tau_Hy, dt) * dl # Ex - fields[timestep, 0] = (((Ex_x * Ex_z) / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_x**5)) * - (3 * (fint_Ex + (tau_Ex * f_Ex)) + (tau_Ex**2 * fdot_Ex))) + fields[timestep, 0] = ((Ex_x * Ex_z) / (4 * np.pi * config.sim_config.em_consts["e0"] * Er_x**5)) * ( + 3 * (fint_Ex + (tau_Ex * f_Ex)) + (tau_Ex**2 * fdot_Ex) + ) # Ey try: tmp = Ey_y / Ey_x except ZeroDivisionError: tmp = 0 - fields[timestep, 1] = (tmp * ((Ey_x * Ey_z) / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_y**5)) * - (3 * (fint_Ey + (tau_Ey * f_Ey)) + (tau_Ey**2 * fdot_Ey))) + fields[timestep, 1] = ( + tmp + * ((Ey_x * Ey_z) / (4 * np.pi * config.sim_config.em_consts["e0"] * Er_y**5)) + * (3 * (fint_Ey + (tau_Ey * f_Ey)) + (tau_Ey**2 * fdot_Ey)) + ) # Ez - fields[timestep, 2] = ((1 / (4 * np.pi * config.sim_config.em_consts['e0'] * Er_z**5)) * - ((2 * Ez_z**2 - (Ez_x**2 + Ez_y**2)) * (fint_Ez + (tau_Ez * f_Ez)) - - (Ez_x**2 + Ez_y**2) * tau_Ez**2 * fdot_Ez)) + fields[timestep, 2] = (1 / (4 * np.pi * config.sim_config.em_consts["e0"] * Er_z**5)) * ( + (2 * Ez_z**2 - (Ez_x**2 + Ez_y**2)) * (fint_Ez + (tau_Ez * f_Ez)) + - (Ez_x**2 + Ez_y**2) * tau_Ez**2 * fdot_Ez + ) # Hx - fields[timestep, 3] = - (Hx_y / (4 * np.pi * Hr_x**3)) * (f_Hx + (tau_Hx * fdot_Hx)) + fields[timestep, 3] = -(Hx_y / (4 * np.pi * Hr_x**3)) * (f_Hx + (tau_Hx * fdot_Hx)) # Hy try: tmp = Hy_x / Hy_y except ZeroDivisionError: tmp = 0 - fields[timestep, 4] = - tmp * (- (Hy_y / (4 * np.pi * Hr_y**3)) * (f_Hy + (tau_Hy * fdot_Hy))) + fields[timestep, 4] = -tmp * (-(Hy_y / (4 * np.pi * Hr_y**3)) * (f_Hy + (tau_Hy * fdot_Hy))) # Hz fields[timestep, 5] = 0 diff --git a/testing/benchmarking/bench_simple.py b/testing/benchmarking/bench_simple.py index 9688ca56..7fec7406 100644 --- a/testing/benchmarking/bench_simple.py +++ b/testing/benchmarking/bench_simple.py @@ -1,5 +1,5 @@ """A series of models with different domain sizes used for benchmarking. - The domain is free space with a simple source (Hertzian Dipole) and + The domain is free space with a simple source (Hertzian Dipole) and receiver at the centre. """ @@ -19,7 +19,6 @@ scenes = [] for d in domains: for threads in ompthreads: - # Discretisation dl = 0.001 @@ -30,14 +29,12 @@ for d in domains: scene = gprMax.Scene() - title = gprMax.Title(name=fn.with_suffix('').name) + title = gprMax.Title(name=fn.with_suffix("").name) domain = gprMax.Domain(p1=(x, y, z)) dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) time_window = gprMax.TimeWindow(time=3e-9) - wv = gprMax.Waveform(wave_type='gaussiandotnorm', amp=1, freq=900e6, - id='MySource') - src = gprMax.HertzianDipole(p1=(x/2, y/2, z/2), polarisation='x', - waveform_id='MySource') + wv = gprMax.Waveform(wave_type="gaussiandotnorm", amp=1, freq=900e6, id="MySource") + src = gprMax.HertzianDipole(p1=(x / 2, y / 2, z / 2), polarisation="x", waveform_id="MySource") omp = gprMax.OMPThreads(n=threads) @@ -51,4 +48,4 @@ for d in domains: scenes.append(scene) # Run model -gprMax.run(scenes=scenes, n=len(scenes), geometry_only=False, outputfile=fn, gpu=None) \ No newline at end of file +gprMax.run(scenes=scenes, n=len(scenes), geometry_only=False, outputfile=fn, gpu=None) diff --git a/testing/experimental/antenna_GSSI_1500_fs/antenna_GSSI_1500_fs.in b/testing/experimental/antenna_GSSI_1500_fs/antenna_GSSI_1500_fs.in index 2b0a21d2..155ffb1c 100755 --- a/testing/experimental/antenna_GSSI_1500_fs/antenna_GSSI_1500_fs.in +++ b/testing/experimental/antenna_GSSI_1500_fs/antenna_GSSI_1500_fs.in @@ -7,4 +7,3 @@ from user_libs.GPRAntennaModels import antenna_like_GSSI_1500 antenna_like_GSSI_1500(0.125, 0.094, 0.100) #end_python: - diff --git a/testing/experimental/antenna_MALA_1200_fs/antenna_MALA_1200_fs.in b/testing/experimental/antenna_MALA_1200_fs/antenna_MALA_1200_fs.in index cdc84b40..c0b89a01 100755 --- a/testing/experimental/antenna_MALA_1200_fs/antenna_MALA_1200_fs.in +++ b/testing/experimental/antenna_MALA_1200_fs/antenna_MALA_1200_fs.in @@ -7,4 +7,3 @@ from user_libs.GPRAntennaModels import antenna_like_MALA_1200 antenna_like_MALA_1200(0.132, 0.095, 0.100) #end_python: - diff --git a/testing/models_pmls/plot_pml_comparison.py b/testing/models_pmls/plot_pml_comparison.py index a713ebf4..95c5a79f 100644 --- a/testing/models_pmls/plot_pml_comparison.py +++ b/testing/models_pmls/plot_pml_comparison.py @@ -28,41 +28,45 @@ import numpy as np logger = logging.getLogger(__name__) # Create/setup plot figure -#colors = ['#E60D30', '#5CB7C6', '#A21797', '#A3B347'] # Plot colours from http://tools.medialab.sciences-po.fr/iwanthue/index.php -#colorIDs = ["#62a85b", "#9967c7", "#b3943f", "#6095cd", "#cb5c42", "#c95889"] +# colors = ['#E60D30', '#5CB7C6', '#A21797', '#A3B347'] # Plot colours from http://tools.medialab.sciences-po.fr/iwanthue/index.php +# colorIDs = ["#62a85b", "#9967c7", "#b3943f", "#6095cd", "#cb5c42", "#c95889"] colorIDs = ["#79c72e", "#5774ff", "#ff7c2c", "#4b4e80", "#d7004e", "#007545", "#ff83ec"] -#colorIDs = ["#ba0044", "#b2d334", "#470055", "#185300", "#ff96b1", "#3e2700", "#0162a9", "#fdb786"] +# colorIDs = ["#ba0044", "#b2d334", "#470055", "#185300", "#ff96b1", "#3e2700", "#0162a9", "#fdb786"] colors = itertools.cycle(colorIDs) # for i in range(2): # next(colors) -lines = itertools.cycle(('--', ':', '-.', '-')) -markers = ['o', 'd', '^', 's', '*'] +lines = itertools.cycle(("--", ":", "-.", "-")) +markers = ["o", "d", "^", "s", "*"] parts = Path(__file__).parts -path = 'rxs/rx1/' -basename = 'pml_3D_pec_plate' -PMLIDs = ['CFS-PML', 'HORIPML-1', 'HORIPML-2', 'MRIPML-1', 'MRIPML-2'] +path = "rxs/rx1/" +basename = "pml_3D_pec_plate" +PMLIDs = ["CFS-PML", "HORIPML-1", "HORIPML-2", "MRIPML-1", "MRIPML-2"] maxerrors = [] -testmodels = ['pml_3D_pec_plate_' + s for s in PMLIDs] +testmodels = ["pml_3D_pec_plate_" + s for s in PMLIDs] -fig, ax = plt.subplots(subplot_kw=dict(xlabel='Iterations', ylabel='Error [dB]'), figsize=(20, 10), facecolor='w', edgecolor='w') +fig, ax = plt.subplots( + subplot_kw=dict(xlabel="Iterations", ylabel="Error [dB]"), figsize=(20, 10), facecolor="w", edgecolor="w" +) for x, model in enumerate(testmodels): # Open output file and read iterations - fileref = h5py.File(Path(*parts[:-1], basename, basename + '_ref.h5'), 'r') - filetest = h5py.File(Path(*parts[:-1], basename, basename + str(x + 1) + '.h5'), 'r') + fileref = h5py.File(Path(*parts[:-1], basename, basename + "_ref.h5"), "r") + filetest = h5py.File(Path(*parts[:-1], basename, basename + str(x + 1) + ".h5"), "r") # Get available field output component names outputsref = list(fileref[path].keys()) outputstest = list(filetest[path].keys()) if outputsref != outputstest: - logger.exception('Field output components do not match reference solution') + logger.exception("Field output components do not match reference solution") raise ValueError # Check that type of float used to store fields matches if filetest[path + outputstest[0]].dtype != fileref[path + outputsref[0]].dtype: - logger.warning(f'Type of floating point number in test model ({filetest[path + outputstest[0]].dtype}) ' - f'does not match type in reference solution ({fileref[path + outputsref[0]].dtype})\n') + logger.warning( + f"Type of floating point number in test model ({filetest[path + outputstest[0]].dtype}) " + f"does not match type in reference solution ({fileref[path + outputsref[0]].dtype})\n" + ) floattyperef = fileref[path + outputsref[0]].dtype floattypetest = filetest[path + outputstest[0]].dtype # logger.info(f'Data type: {floattypetest}') @@ -72,19 +76,19 @@ for x, model in enumerate(testmodels): # timeref = np.linspace(0, (fileref.attrs['Iterations'] - 1) * fileref.attrs['dt'], num=fileref.attrs['Iterations']) / 1e-9 # timetest = np.zeros((filetest.attrs['Iterations']), dtype=floattypetest) # timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'], num=filetest.attrs['Iterations']) / 1e-9 - timeref = np.zeros((fileref.attrs['Iterations']), dtype=floattyperef) - timeref = np.linspace(0, (fileref.attrs['Iterations'] - 1), num=fileref.attrs['Iterations']) - timetest = np.zeros((filetest.attrs['Iterations']), dtype=floattypetest) - timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1), num=filetest.attrs['Iterations']) + timeref = np.zeros((fileref.attrs["Iterations"]), dtype=floattyperef) + timeref = np.linspace(0, (fileref.attrs["Iterations"] - 1), num=fileref.attrs["Iterations"]) + timetest = np.zeros((filetest.attrs["Iterations"]), dtype=floattypetest) + timetest = np.linspace(0, (filetest.attrs["Iterations"] - 1), num=filetest.attrs["Iterations"]) # Arrays for storing field data - dataref = np.zeros((fileref.attrs['Iterations'], len(outputsref)), dtype=floattyperef) - datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), dtype=floattypetest) + dataref = np.zeros((fileref.attrs["Iterations"], len(outputsref)), dtype=floattyperef) + datatest = np.zeros((filetest.attrs["Iterations"], len(outputstest)), dtype=floattypetest) for ID, name in enumerate(outputsref): dataref[:, ID] = fileref[path + str(name)][:] datatest[:, ID] = filetest[path + str(name)][:] if np.any(np.isnan(datatest[:, ID])): - logger.exception('Test data contains NaNs') + logger.exception("Test data contains NaNs") raise ValueError fileref.close() @@ -94,18 +98,20 @@ for x, model in enumerate(testmodels): datadiffs = np.zeros(datatest.shape, dtype=np.float64) for i in range(len(outputstest)): maxi = np.amax(np.abs(dataref[:, i])) - datadiffs[:, i] = np.divide(np.abs(datatest[:, i] - dataref[:, i]), maxi, out=np.zeros_like(dataref[:, i]), where=maxi != 0) # Replace any division by zero with zero + datadiffs[:, i] = np.divide( + np.abs(datatest[:, i] - dataref[:, i]), maxi, out=np.zeros_like(dataref[:, i]), where=maxi != 0 + ) # Replace any division by zero with zero # Calculate power (ignore warning from taking a log of any zero values) - with np.errstate(divide='ignore'): + with np.errstate(divide="ignore"): datadiffs[:, i] = 20 * np.log10(datadiffs[:, i]) # Replace any NaNs or Infs from zero division datadiffs[:, i][np.invert(np.isfinite(datadiffs[:, i]))] = 0 # Print maximum error value start = 210 - maxerrors.append(f': {np.amax(datadiffs[start::, 1]):.1f} [dB]') - logger.info(f'{model}: Max. error {maxerrors[x]}') + maxerrors.append(f": {np.amax(datadiffs[start::, 1]):.1f} [dB]") + logger.info(f"{model}: Max. error {maxerrors[x]}") # Plot diffs (select column to choose field component, 0-Ex, 1-Ey etc..) ax.plot(timeref[start::], datadiffs[start::, 1], color=next(colors), lw=2, ls=next(lines), label=model) @@ -114,16 +120,16 @@ for x, model in enumerate(testmodels): ax.set_yticks(np.arange(-160, 0, step=20)) ax.set_ylim([-160, -20]) ax.set_axisbelow(True) - ax.grid(color=(0.75,0.75,0.75), linestyle='dashed') + ax.grid(color=(0.75, 0.75, 0.75), linestyle="dashed") mylegend = list(map(add, PMLIDs, maxerrors)) legend = ax.legend(mylegend, loc=1, fontsize=14) frame = legend.get_frame() -frame.set_edgecolor('white') +frame.set_edgecolor("white") frame.set_alpha(0) plt.show() # Save a PDF/PNG of the figure -#fig.savefig(basepath + '.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1) -#fig.savefig(savename + '.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1) +# fig.savefig(basepath + '.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1) +# fig.savefig(savename + '.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1) diff --git a/testing/models_pmls/pml_3D_pec_plate/pml_3D_pec_plate.py b/testing/models_pmls/pml_3D_pec_plate/pml_3D_pec_plate.py index e87751e5..b3e1b94b 100755 --- a/testing/models_pmls/pml_3D_pec_plate/pml_3D_pec_plate.py +++ b/testing/models_pmls/pml_3D_pec_plate/pml_3D_pec_plate.py @@ -20,112 +20,162 @@ dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) time_window = gprMax.TimeWindow(iterations=2100) tssf = gprMax.TimeStepStabilityFactor(f=0.99) -waveform = gprMax.Waveform(wave_type='gaussiandotnorm', amp=1, freq=9.42e9, id='mypulse') -hertzian_dipole = gprMax.HertzianDipole(polarisation='z', - p1=(0.013, 0.013, 0.014), - waveform_id='mypulse') +waveform = gprMax.Waveform(wave_type="gaussiandotnorm", amp=1, freq=9.42e9, id="mypulse") +hertzian_dipole = gprMax.HertzianDipole(polarisation="z", p1=(0.013, 0.013, 0.014), waveform_id="mypulse") rx = gprMax.Rx(p1=(0.038, 0.114, 0.013)) -plate = gprMax.Plate(p1=(0.013, 0.013, 0.013), - p2=(0.038, 0.113, 0.013), material_id='pec') +plate = gprMax.Plate(p1=(0.013, 0.013, 0.013), p2=(0.038, 0.113, 0.013), material_id="pec") -gv1 = gprMax.GeometryView(p1=(0, 0, 0), - p2=(x, y, z), - dl=(dl, dl, dl), - filename=Path(*parts[:-1], f'{parts[-1]}_n'), - output_type='n',) -gv2 = gprMax.GeometryView(p1=(0, 0, 0), - p2=(x, y, z), - dl=(dl, dl, dl), - filename=Path(*parts[:-1], f'{parts[-1]}_f'), - output_type='f',) +gv1 = gprMax.GeometryView( + p1=(0, 0, 0), + p2=(x, y, z), + dl=(dl, dl, dl), + filename=Path(*parts[:-1], f"{parts[-1]}_n"), + output_type="n", +) +gv2 = gprMax.GeometryView( + p1=(0, 0, 0), + p2=(x, y, z), + dl=(dl, dl, dl), + filename=Path(*parts[:-1], f"{parts[-1]}_f"), + output_type="f", +) -pmls = {'CFS-PML': {'pml': gprMax.PMLProps(formulation='HORIPML', thickness=10), - # Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864 - 'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='constant', - alphascalingdirection='forward', - alphamin=0.05, alphamax=0.05, - kappascalingprofile='quartic', - kappascalingdirection='forward', - kappamin=1, kappamax=8, - sigmascalingprofile='quartic', - sigmascalingdirection='forward', - sigmamin=0, - sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)))]}, - 'HORIPML-1': {'pml': gprMax.PMLProps(formulation='HORIPML', thickness=10), - # Parameters from http://dx.doi.org/10.1109/TAP.2011.2180344 - 'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='constant', - alphascalingdirection='forward', - alphamin=0, alphamax=0, - kappascalingprofile='quartic', - kappascalingdirection='forward', - kappamin=1, kappamax=12, - sigmascalingprofile='quartic', - sigmascalingdirection='forward', - sigmamin=0, - sigmamax=0.7 * ((4 + 1) / (150 * np.pi * dl)))]}, - 'HORIPML-2': {'pml': gprMax.PMLProps(formulation='HORIPML', thickness=10), - # Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864 - 'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='constant', - alphascalingdirection='forward', - alphamin=0, alphamax=0, - kappascalingprofile='constant', - kappascalingdirection='forward', - kappamin=1, kappamax=1, - sigmascalingprofile='sextic', - sigmascalingdirection='forward', - sigmamin=0, - sigmamax=0.275 / (150 * np.pi * dl)), - gprMax.PMLCFS(alphascalingprofile='sextic', - alphascalingdirection='forward', - alphamin=0.07, alphamax=0.07 + (0.275 / (150 * np.pi * dl)), - kappascalingprofile='cubic', - kappascalingdirection='forward', - kappamin=1, kappamax=8, - sigmascalingprofile='quadratic', - sigmascalingdirection='forward', - sigmamin=0, - sigmamax=2.75 / (150 * np.pi * dl))]}, - 'MRIPML-1': {'pml': gprMax.PMLProps(formulation='MRIPML', thickness=10), - # Parameters from Antonis' MATLAB script (M3Dparams.m) - 'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='constant', - alphascalingdirection='forward', - alphamin=0.05, alphamax=0.05, - kappascalingprofile='quartic', - kappascalingdirection='forward', - kappamin=1, kappamax=8, - sigmascalingprofile='quartic', - sigmascalingdirection='forward', - sigmamin=0, - sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)))]}, - 'MRIPML-2': {'pml': gprMax.PMLProps(formulation='MRIPML', thickness=10), - # Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864 - 'pml_cfs': [gprMax.PMLCFS(alphascalingprofile='quadratic', - alphascalingdirection='reverse', - alphamin=0, alphamax=0.15, - kappascalingprofile='quartic', - kappascalingdirection='forward', - kappamin=1, kappamax=12, - sigmascalingprofile='quartic', - sigmascalingdirection='forward', - sigmamin=0, - sigmamax=0.65 * ((4 + 1) / (150 * np.pi * dl))), - gprMax.PMLCFS(alphascalingprofile='linear', - alphascalingdirection='reverse', - alphamin=0.07, alphamax=0.8, - kappascalingprofile='constant', - kappascalingdirection='forward', - kappamin=0, kappamax=0, - sigmascalingprofile='quadratic', - sigmascalingdirection='forward', - sigmamin=0, - sigmamax=0.65 * ((2 + 1) / (150 * np.pi * dl)))]} - } +pmls = { + "CFS-PML": { + "pml": gprMax.PMLProps(formulation="HORIPML", thickness=10), + # Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864 + "pml_cfs": [ + gprMax.PMLCFS( + alphascalingprofile="constant", + alphascalingdirection="forward", + alphamin=0.05, + alphamax=0.05, + kappascalingprofile="quartic", + kappascalingdirection="forward", + kappamin=1, + kappamax=8, + sigmascalingprofile="quartic", + sigmascalingdirection="forward", + sigmamin=0, + sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)), + ) + ], + }, + "HORIPML-1": { + "pml": gprMax.PMLProps(formulation="HORIPML", thickness=10), + # Parameters from http://dx.doi.org/10.1109/TAP.2011.2180344 + "pml_cfs": [ + gprMax.PMLCFS( + alphascalingprofile="constant", + alphascalingdirection="forward", + alphamin=0, + alphamax=0, + kappascalingprofile="quartic", + kappascalingdirection="forward", + kappamin=1, + kappamax=12, + sigmascalingprofile="quartic", + sigmascalingdirection="forward", + sigmamin=0, + sigmamax=0.7 * ((4 + 1) / (150 * np.pi * dl)), + ) + ], + }, + "HORIPML-2": { + "pml": gprMax.PMLProps(formulation="HORIPML", thickness=10), + # Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864 + "pml_cfs": [ + gprMax.PMLCFS( + alphascalingprofile="constant", + alphascalingdirection="forward", + alphamin=0, + alphamax=0, + kappascalingprofile="constant", + kappascalingdirection="forward", + kappamin=1, + kappamax=1, + sigmascalingprofile="sextic", + sigmascalingdirection="forward", + sigmamin=0, + sigmamax=0.275 / (150 * np.pi * dl), + ), + gprMax.PMLCFS( + alphascalingprofile="sextic", + alphascalingdirection="forward", + alphamin=0.07, + alphamax=0.07 + (0.275 / (150 * np.pi * dl)), + kappascalingprofile="cubic", + kappascalingdirection="forward", + kappamin=1, + kappamax=8, + sigmascalingprofile="quadratic", + sigmascalingdirection="forward", + sigmamin=0, + sigmamax=2.75 / (150 * np.pi * dl), + ), + ], + }, + "MRIPML-1": { + "pml": gprMax.PMLProps(formulation="MRIPML", thickness=10), + # Parameters from Antonis' MATLAB script (M3Dparams.m) + "pml_cfs": [ + gprMax.PMLCFS( + alphascalingprofile="constant", + alphascalingdirection="forward", + alphamin=0.05, + alphamax=0.05, + kappascalingprofile="quartic", + kappascalingdirection="forward", + kappamin=1, + kappamax=8, + sigmascalingprofile="quartic", + sigmascalingdirection="forward", + sigmamin=0, + sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)), + ) + ], + }, + "MRIPML-2": { + "pml": gprMax.PMLProps(formulation="MRIPML", thickness=10), + # Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864 + "pml_cfs": [ + gprMax.PMLCFS( + alphascalingprofile="quadratic", + alphascalingdirection="reverse", + alphamin=0, + alphamax=0.15, + kappascalingprofile="quartic", + kappascalingdirection="forward", + kappamin=1, + kappamax=12, + sigmascalingprofile="quartic", + sigmascalingdirection="forward", + sigmamin=0, + sigmamax=0.65 * ((4 + 1) / (150 * np.pi * dl)), + ), + gprMax.PMLCFS( + alphascalingprofile="linear", + alphascalingdirection="reverse", + alphamin=0.07, + alphamax=0.8, + kappascalingprofile="constant", + kappascalingdirection="forward", + kappamin=0, + kappamax=0, + sigmascalingprofile="quadratic", + sigmascalingdirection="forward", + sigmamin=0, + sigmamax=0.65 * ((2 + 1) / (150 * np.pi * dl)), + ), + ], + }, +} scenes = [] for k, v in pmls.items(): scene = gprMax.Scene() - title = gprMax.Title(name=fn.with_suffix('').name + '_' + k) + title = gprMax.Title(name=fn.with_suffix("").name + "_" + k) scene.add(title) scene.add(domain) scene.add(dxdydz) @@ -137,8 +187,8 @@ for k, v in pmls.items(): # scene.add(gv1) # scene.add(gv2) - scene.add(v['pml']) - for pml_cfs in v['pml_cfs']: + scene.add(v["pml"]) + for pml_cfs in v["pml_cfs"]: scene.add(pml_cfs) scenes.append(scene) diff --git a/testing/models_pmls/pml_3D_pec_plate/pml_3D_pec_plate_ref.py b/testing/models_pmls/pml_3D_pec_plate/pml_3D_pec_plate_ref.py index f9f42c9f..f441c5d8 100755 --- a/testing/models_pmls/pml_3D_pec_plate/pml_3D_pec_plate_ref.py +++ b/testing/models_pmls/pml_3D_pec_plate/pml_3D_pec_plate_ref.py @@ -20,42 +20,47 @@ dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) time_window = gprMax.TimeWindow(iterations=2100) tssf = gprMax.TimeStepStabilityFactor(f=0.99) -waveform = gprMax.Waveform(wave_type='gaussiandotnorm', amp=1, freq=9.42e9, id='mypulse') -hertzian_dipole = gprMax.HertzianDipole(polarisation='z', - p1=(0.088, 0.088, 0.089), - waveform_id='mypulse') +waveform = gprMax.Waveform(wave_type="gaussiandotnorm", amp=1, freq=9.42e9, id="mypulse") +hertzian_dipole = gprMax.HertzianDipole(polarisation="z", p1=(0.088, 0.088, 0.089), waveform_id="mypulse") rx = gprMax.Rx(p1=(0.113, 0.189, 0.088)) -plate = gprMax.Plate(p1=(0.088, 0.088, 0.088), - p2=(0.113, 0.188, 0.088), material_id='pec') +plate = gprMax.Plate(p1=(0.088, 0.088, 0.088), p2=(0.113, 0.188, 0.088), material_id="pec") -gv1 = gprMax.GeometryView(p1=(0, 0, 0), - p2=(x, y, z), - dl=(dl, dl, dl), - filename=Path(*parts[:-1], f'{parts[-1]}_n'), - output_type='n',) -gv2 = gprMax.GeometryView(p1=(0, 0, 0), - p2=(x, y, z), - dl=(dl, dl, dl), - filename=Path(*parts[:-1], f'{parts[-1]}_f'), - output_type='f',) +gv1 = gprMax.GeometryView( + p1=(0, 0, 0), + p2=(x, y, z), + dl=(dl, dl, dl), + filename=Path(*parts[:-1], f"{parts[-1]}_n"), + output_type="n", +) +gv2 = gprMax.GeometryView( + p1=(0, 0, 0), + p2=(x, y, z), + dl=(dl, dl, dl), + filename=Path(*parts[:-1], f"{parts[-1]}_f"), + output_type="f", +) -pml = gprMax.PMLProps(formulation='HORIPML', thickness=10) +pml = gprMax.PMLProps(formulation="HORIPML", thickness=10) # Parameters from http://dx.doi.org/10.1109/TAP.2018.2823864 -pml_cfs = gprMax.PMLCFS(alphascalingprofile='constant', - alphascalingdirection='forward', - alphamin=0.05, alphamax=0.05, - kappascalingprofile='quartic', - kappascalingdirection='forward', - kappamin=1, kappamax=8, - sigmascalingprofile='quartic', - sigmascalingdirection='forward', - sigmamin=0, - sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl))) +pml_cfs = gprMax.PMLCFS( + alphascalingprofile="constant", + alphascalingdirection="forward", + alphamin=0.05, + alphamax=0.05, + kappascalingprofile="quartic", + kappascalingdirection="forward", + kappamin=1, + kappamax=8, + sigmascalingprofile="quartic", + sigmascalingdirection="forward", + sigmamin=0, + sigmamax=1.1 * ((4 + 1) / (150 * np.pi * dl)), +) scene = gprMax.Scene() -title = gprMax.Title(name=fn.with_suffix('').name + '_ref') +title = gprMax.Title(name=fn.with_suffix("").name + "_ref") scene.add(title) scene.add(domain) scene.add(dxdydz) diff --git a/testing/models_pmls/pml_basic.py b/testing/models_pmls/pml_basic.py index 3a71eb85..0c29b4f1 100755 --- a/testing/models_pmls/pml_basic.py +++ b/testing/models_pmls/pml_basic.py @@ -17,83 +17,88 @@ domain = gprMax.Domain(p1=(x, y, z)) dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) time_window = gprMax.TimeWindow(time=3e-9) -waveform = gprMax.Waveform(wave_type='gaussian', amp=1, freq=1e9, id='mypulse') -hertzian_dipole = gprMax.HertzianDipole(polarisation='z', - p1=(0.050, 0.050, 0.050), - waveform_id='mypulse') +waveform = gprMax.Waveform(wave_type="gaussian", amp=1, freq=1e9, id="mypulse") +hertzian_dipole = gprMax.HertzianDipole(polarisation="z", p1=(0.050, 0.050, 0.050), waveform_id="mypulse") rx = gprMax.Rx(p1=(0.070, 0.070, 0.070)) # PML cases -thick = 10 # thickness -cases = {'off': {'x0': 0, 'y0': 0, 'z0': 0, 'xmax': 0, 'ymax': 0, 'zmax':0}, - 'x0': {'x0': thick, 'y0': 0, 'z0': 0, 'xmax': 0, 'ymax': 0, 'zmax':0}, - 'y0': {'x0': 0, 'y0': thick, 'z0': 0, 'xmax': 0, 'ymax': 0, 'zmax':0}, - 'z0': {'x0': 0, 'y0': 0, 'z0': thick, 'xmax': 0, 'ymax': 0, 'zmax':0}, - 'xmax': {'x0': 0, 'y0': 0, 'z0': 0, 'xmax': thick, 'ymax': 0, 'zmax':0}, - 'ymax': {'x0': 0, 'y0': 0, 'z0': 0, 'xmax': 0, 'ymax': thick, 'zmax':0}, - 'zmax': {'x0': 0, 'y0': 0, 'z0': 0, 'xmax': 0, 'ymax': 0, 'zmax': thick}} +thick = 10 # thickness +cases = { + "off": {"x0": 0, "y0": 0, "z0": 0, "xmax": 0, "ymax": 0, "zmax": 0}, + "x0": {"x0": thick, "y0": 0, "z0": 0, "xmax": 0, "ymax": 0, "zmax": 0}, + "y0": {"x0": 0, "y0": thick, "z0": 0, "xmax": 0, "ymax": 0, "zmax": 0}, + "z0": {"x0": 0, "y0": 0, "z0": thick, "xmax": 0, "ymax": 0, "zmax": 0}, + "xmax": {"x0": 0, "y0": 0, "z0": 0, "xmax": thick, "ymax": 0, "zmax": 0}, + "ymax": {"x0": 0, "y0": 0, "z0": 0, "xmax": 0, "ymax": thick, "zmax": 0}, + "zmax": {"x0": 0, "y0": 0, "z0": 0, "xmax": 0, "ymax": 0, "zmax": thick}, +} # PML formulation -pml_type = gprMax.PMLProps(formulation='HORIPML') +pml_type = gprMax.PMLProps(formulation="HORIPML") ## Built-in 1st order PML -pml_cfs = gprMax.PMLCFS(alphascalingprofile='constant', - alphascalingdirection='forward', - alphamin=0, alphamax=0, - kappascalingprofile='constant', - kappascalingdirection='forward', - kappamin=1, kappamax=1, - sigmascalingprofile='quartic', - sigmascalingdirection='forward', - sigmamin=0, sigmamax=None) +pml_cfs = gprMax.PMLCFS( + alphascalingprofile="constant", + alphascalingdirection="forward", + alphamin=0, + alphamax=0, + kappascalingprofile="constant", + kappascalingdirection="forward", + kappamin=1, + kappamax=1, + sigmascalingprofile="quartic", + sigmascalingdirection="forward", + sigmamin=0, + sigmamax=None, +) ## PMLs from http://dx.doi.org/10.1109/TAP.2011.2180344 ## Standard PML -# pml_cfs = gprMax.PMLCFS(alphascalingprofile='constant', -# alphascalingdirection='forward', +# pml_cfs = gprMax.PMLCFS(alphascalingprofile='constant', +# alphascalingdirection='forward', # alphamin=0, alphamax=0, -# kappascalingprofile='quartic', -# kappascalingdirection='forward', -# kappamin=1, kappamax=11, -# sigmascalingprofile='quartic', -# sigmascalingdirection='forward', +# kappascalingprofile='quartic', +# kappascalingdirection='forward', +# kappamin=1, kappamax=11, +# sigmascalingprofile='quartic', +# sigmascalingdirection='forward', # sigmamin=0, sigmamax=7.427) ## CFS PML -# pml_cfs = gprMax.PMLCFS(alphascalingprofile='constant', -# alphascalingdirection='forward', +# pml_cfs = gprMax.PMLCFS(alphascalingprofile='constant', +# alphascalingdirection='forward', # alphamin=0.05, alphamax=0.05, -# kappascalingprofile='quartic', -# kappascalingdirection='forward', -# kappamin=1, kappamax=7, -# sigmascalingprofile='quartic', -# sigmascalingdirection='forward', +# kappascalingprofile='quartic', +# kappascalingdirection='forward', +# kappamin=1, kappamax=7, +# sigmascalingprofile='quartic', +# sigmascalingdirection='forward', # sigmamin=0, sigmamax=11.671) ## 2nd order RIPML -# pml_cfs1 = gprMax.PMLCFS(alphascalingprofile='constant', -# alphascalingdirection='forward', +# pml_cfs1 = gprMax.PMLCFS(alphascalingprofile='constant', +# alphascalingdirection='forward', # alphamin=0, alphamax=0, -# kappascalingprofile='constant', -# kappascalingdirection='forward', -# kappamin=1, kappamax=1, -# sigmascalingprofile='sextic', -# sigmascalingdirection='forward', +# kappascalingprofile='constant', +# kappascalingdirection='forward', +# kappamin=1, kappamax=1, +# sigmascalingprofile='sextic', +# sigmascalingdirection='forward', # sigmamin=0, sigmamax=0.5836) -# pml_cfs2 = gprMax.PMLCFS(alphascalingprofile='constant', -# alphascalingdirection='forward', +# pml_cfs2 = gprMax.PMLCFS(alphascalingprofile='constant', +# alphascalingdirection='forward', # alphamin=0.05, alphamax=0.05, -# kappascalingprofile='cubic', -# kappascalingdirection='forward', -# kappamin=1, kappamax=8, -# sigmascalingprofile='quadratic', -# sigmascalingdirection='forward', +# kappascalingprofile='cubic', +# kappascalingdirection='forward', +# kappamin=1, kappamax=8, +# sigmascalingprofile='quadratic', +# sigmascalingdirection='forward', # sigmamin=0, sigmamax=5.8357) scenes = [] for k, v in cases.items(): scene = gprMax.Scene() - title = gprMax.Title(name=fn.with_suffix('').name + '_' + k) + title = gprMax.Title(name=fn.with_suffix("").name + "_" + k) scene.add(title) scene.add(domain) scene.add(dxdydz) @@ -102,11 +107,11 @@ for k, v in cases.items(): scene.add(hertzian_dipole) scene.add(rx) - pml = gprMax.PMLProps(formulation='HORIPML', **v) + pml = gprMax.PMLProps(formulation="HORIPML", **v) scene.add(pml) scene.add(pml_cfs) scenes.append(scene) # Run model -gprMax.run(scenes=scenes, n=len(cases), geometry_only=False, outputfile=fn) \ No newline at end of file +gprMax.run(scenes=scenes, n=len(cases), geometry_only=False, outputfile=fn) diff --git a/testing/other_codes/vs_MoM_MATLAB/antenna_bowtie_fs/antenna_bowtie_fs.py b/testing/other_codes/vs_MoM_MATLAB/antenna_bowtie_fs/antenna_bowtie_fs.py index 6b31a564..fd2cdbfc 100644 --- a/testing/other_codes/vs_MoM_MATLAB/antenna_bowtie_fs/antenna_bowtie_fs.py +++ b/testing/other_codes/vs_MoM_MATLAB/antenna_bowtie_fs/antenna_bowtie_fs.py @@ -15,7 +15,7 @@ z = 0.100 scene = gprMax.Scene() -title = gprMax.Title(name=fn.with_suffix('').name) +title = gprMax.Title(name=fn.with_suffix("").name) domain = gprMax.Domain(p1=(x, y, z)) dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) time_window = gprMax.TimeWindow(time=30e-9) @@ -25,49 +25,47 @@ scene.add(domain) scene.add(dxdydz) scene.add(time_window) -bowtie_dims = (0.050, 0.100) # Length, height -tx_pos = (x/2, y/2, z/2) +bowtie_dims = (0.050, 0.100) # Length, height +tx_pos = (x / 2, y / 2, z / 2) # Source excitation and type -wave = gprMax.Waveform(wave_type='gaussian', amp=1, freq=1.5e9, id='mypulse') -tl = gprMax.TransmissionLine(p1=(tx_pos[0], tx_pos[1], tx_pos[2]), - polarisation='x', resistance=50, waveform_id='mypulse') +wave = gprMax.Waveform(wave_type="gaussian", amp=1, freq=1.5e9, id="mypulse") +tl = gprMax.TransmissionLine( + p1=(tx_pos[0], tx_pos[1], tx_pos[2]), polarisation="x", resistance=50, waveform_id="mypulse" +) scene.add(wave) scene.add(tl) # Bowtie - upper x half -t1 = gprMax.Triangle(p1=(tx_pos[0], tx_pos[1], tx_pos[2]), - p2=(tx_pos[0] + bowtie_dims[0] + 2 * dl, - tx_pos[1] - bowtie_dims[1]/2, - tx_pos[2]), - p3=(tx_pos[0] + bowtie_dims[0] + 2 * dl, - tx_pos[1] + bowtie_dims[1]/2, - tx_pos[2]), - thickness=0, material_id='pec') +t1 = gprMax.Triangle( + p1=(tx_pos[0], tx_pos[1], tx_pos[2]), + p2=(tx_pos[0] + bowtie_dims[0] + 2 * dl, tx_pos[1] - bowtie_dims[1] / 2, tx_pos[2]), + p3=(tx_pos[0] + bowtie_dims[0] + 2 * dl, tx_pos[1] + bowtie_dims[1] / 2, tx_pos[2]), + thickness=0, + material_id="pec", +) # Bowtie - lower x half -t2 = gprMax.Triangle(p1=(tx_pos[0] + dl, tx_pos[1], tx_pos[2]), - p2=(tx_pos[0] - bowtie_dims[0], - tx_pos[1] - bowtie_dims[1]/2, - tx_pos[2]), - p3=(tx_pos[0] - bowtie_dims[0], - tx_pos[1] + bowtie_dims[1]/2, - tx_pos[2]), - thickness=0, material_id='pec') +t2 = gprMax.Triangle( + p1=(tx_pos[0] + dl, tx_pos[1], tx_pos[2]), + p2=(tx_pos[0] - bowtie_dims[0], tx_pos[1] - bowtie_dims[1] / 2, tx_pos[2]), + p3=(tx_pos[0] - bowtie_dims[0], tx_pos[1] + bowtie_dims[1] / 2, tx_pos[2]), + thickness=0, + material_id="pec", +) scene.add(t1) scene.add(t2) # Detailed geometry view around bowtie and feed position -gv1 = gprMax.GeometryView(p1=(tx_pos[0] - bowtie_dims[0] - 2*dl, - tx_pos[1] - bowtie_dims[1]/2 - 2*dl, - tx_pos[2] - 2*dl), - p2=(tx_pos[0] + bowtie_dims[0] + 2*dl, - tx_pos[1] + bowtie_dims[1]/2 + 2*dl, - tx_pos[2] + 2*dl), - dl=(dl, dl, dl), filename='antenna_bowtie_fs_pcb', - output_type='f') +gv1 = gprMax.GeometryView( + p1=(tx_pos[0] - bowtie_dims[0] - 2 * dl, tx_pos[1] - bowtie_dims[1] / 2 - 2 * dl, tx_pos[2] - 2 * dl), + p2=(tx_pos[0] + bowtie_dims[0] + 2 * dl, tx_pos[1] + bowtie_dims[1] / 2 + 2 * dl, tx_pos[2] + 2 * dl), + dl=(dl, dl, dl), + filename="antenna_bowtie_fs_pcb", + output_type="f", +) scene.add(gv1) # Run model -gprMax.run(scenes=[scene], geometry_only=False, outputfile=fn, gpu=None) \ No newline at end of file +gprMax.run(scenes=[scene], geometry_only=False, outputfile=fn, gpu=None) diff --git a/testing/test_experimental.py b/testing/test_experimental.py index 1146f935..b9e8f366 100644 --- a/testing/test_experimental.py +++ b/testing/test_experimental.py @@ -26,80 +26,85 @@ import numpy as np logger = logging.getLogger(__name__) -"""Plots a comparison of fields between given simulation output and experimental +"""Plots a comparison of fields between given simulation output and experimental data files. """ # Parse command line arguments -parser = argparse.ArgumentParser(description='Plots a comparison of fields between ' + - 'given simulation output and experimental data files.', - usage='cd gprMax; python -m testing.test_experimental modelfile realfile output') -parser.add_argument('modelfile', help='name of model output file including path') -parser.add_argument('realfile', help='name of file containing experimental data including path') -parser.add_argument('output', help='output to be plotted, i.e. Ex Ey Ez', nargs='+') +parser = argparse.ArgumentParser( + description="Plots a comparison of fields between " + "given simulation output and experimental data files.", + usage="cd gprMax; python -m testing.test_experimental modelfile realfile output", +) +parser.add_argument("modelfile", help="name of model output file including path") +parser.add_argument("realfile", help="name of file containing experimental data including path") +parser.add_argument("output", help="output to be plotted, i.e. Ex Ey Ez", nargs="+") args = parser.parse_args() modelfile = Path(args.modelfile) realfile = Path(args.realfile) # Model results -f = h5py.File(Path(modelfile), 'r') -path = '/rxs/rx1/' +f = h5py.File(Path(modelfile), "r") +path = "/rxs/rx1/" availablecomponents = list(f[path].keys()) # Check for polarity of output and if requested output is in file -if args.output[0][0] == 'm': +if args.output[0][0] == "m": polarity = -1 args.outputs[0] = args.output[0][1:] else: polarity = 1 if args.output[0] not in availablecomponents: - logger.exception(f"{args.output[0]} output requested to plot, but the " + - f"available output for receiver 1 is {', '.join(availablecomponents)}") + logger.exception( + f"{args.output[0]} output requested to plot, but the " + + f"available output for receiver 1 is {', '.join(availablecomponents)}" + ) raise ValueError floattype = f[path + args.output[0]].dtype -iterations = f.attrs['Iterations'] -dt = f.attrs['dt'] +iterations = f.attrs["Iterations"] +dt = f.attrs["dt"] model = np.zeros(iterations, dtype=floattype) model = f[path + args.output[0]][:] * polarity model /= np.amax(np.abs(model)) timemodel = np.linspace(0, 1, iterations) -timemodel *= (iterations * dt) +timemodel *= iterations * dt f.close() # Find location of maximum value from model modelmax = np.where(np.abs(model) == 1)[0][0] # Real results -with open(realfile, 'r') as f: +with open(realfile, "r") as f: real = np.loadtxt(f) real[:, 1] = real[:, 1] / np.amax(np.abs(real[:, 1])) realmax = np.where(np.abs(real[:, 1]) == 1)[0][0] -difftime = - (timemodel[modelmax] - real[realmax, 0]) +difftime = -(timemodel[modelmax] - real[realmax, 0]) # Plot modelled and real data -fig, ax = plt.subplots(num=f'{modelfile.stem}_vs_{realfile.stem}', - figsize=(20, 10), - facecolor='w', - edgecolor='w',) -ax.plot(timemodel + difftime, model, 'r', lw=2, label='Model') -ax.plot(real[:, 0], real[:, 1], 'r', ls='--', lw=2, label='Experiment') -ax.set_xlabel('Time [s]') -ax.set_ylabel('Amplitude') +fig, ax = plt.subplots( + num=f"{modelfile.stem}_vs_{realfile.stem}", + figsize=(20, 10), + facecolor="w", + edgecolor="w", +) +ax.plot(timemodel + difftime, model, "r", lw=2, label="Model") +ax.plot(real[:, 0], real[:, 1], "r", ls="--", lw=2, label="Experiment") +ax.set_xlabel("Time [s]") +ax.set_ylabel("Amplitude") ax.set_xlim([0, timemodel[-1]]) # ax.set_ylim([-1, 1]) ax.legend() ax.grid() # Save a PDF/PNG of the figure -savename = f'{modelfile.stem}_vs_{realfile.stem}' +savename = f"{modelfile.stem}_vs_{realfile.stem}" savename = modelfile.parent / savename -# fig.savefig(savename.with_suffix('.pdf'), dpi=None, format='pdf', +# fig.savefig(savename.with_suffix('.pdf'), dpi=None, format='pdf', # bbox_inches='tight', pad_inches=0.1) -# fig.savefig(savename.with_suffix('.png'), dpi=150, format='png', +# fig.savefig(savename.with_suffix('.png'), dpi=150, format='png', # bbox_inches='tight', pad_inches=0.1) plt.show() diff --git a/testing/test_models.py b/testing/test_models.py index f21bc144..c1e849c6 100644 --- a/testing/test_models.py +++ b/testing/test_models.py @@ -29,8 +29,8 @@ from testing.analytical_solutions import hertzian_dipole_fs logger = logging.getLogger(__name__) -if sys.platform == 'linux': - plt.switch_backend('agg') +if sys.platform == "linux": + plt.switch_backend("agg") """Compare field outputs @@ -41,7 +41,7 @@ if sys.platform == 'linux': """ # Specify directoty with set of models to test -modelset = 'models_basic' +modelset = "models_basic" # modelset += 'models_advanced' # modelset += 'models_pmls' @@ -49,9 +49,17 @@ basepath = Path(__file__).parents[0] / modelset # List of available basic test models -testmodels = ['hertzian_dipole_fs_analytical', '2D_ExHyHz', '2D_EyHxHz', '2D_EzHxHy', - 'cylinder_Ascan_2D', 'hertzian_dipole_fs', 'hertzian_dipole_hs', - 'hertzian_dipole_dispersive', 'magnetic_dipole_fs'] +testmodels = [ + "hertzian_dipole_fs_analytical", + "2D_ExHyHz", + "2D_EyHxHz", + "2D_EzHxHy", + "cylinder_Ascan_2D", + "hertzian_dipole_fs", + "hertzian_dipole_hs", + "hertzian_dipole_dispersive", + "magnetic_dipole_fs", +] # List of available advanced test models # testmodels = ['antenna_GSSI_1500_fs', 'antenna_MALA_1200_fs'] @@ -63,97 +71,99 @@ testmodels = ['hertzian_dipole_fs_analytical', '2D_ExHyHz', '2D_EyHxHz', '2D_EzH # testmodels = testmodels[:-1] # testmodels = [testmodels[6]] testresults = dict.fromkeys(testmodels) -path = '/rxs/rx1/' +path = "/rxs/rx1/" # Minimum value of difference to plot (dB) plotmin = -160 for i, model in enumerate(testmodels): - testresults[model] = {} # Run model file = basepath / model / model - gprMax.run(inputfile=file.with_suffix('.in'), gpu=None) + gprMax.run(inputfile=file.with_suffix(".in"), gpu=None) # Special case for analytical comparison - if model == 'hertzian_dipole_fs_analytical': + if model == "hertzian_dipole_fs_analytical": # Get output for model file - filetest = h5py.File(file.with_suffix('.h5'), 'r') - testresults[model]['Test version'] = filetest.attrs['gprMax'] + filetest = h5py.File(file.with_suffix(".h5"), "r") + testresults[model]["Test version"] = filetest.attrs["gprMax"] # Get available field output component names outputstest = list(filetest[path].keys()) # Arrays for storing time float_or_double = filetest[path + outputstest[0]].dtype - timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'], - num=filetest.attrs['Iterations']) / 1e-9 + timetest = ( + np.linspace(0, (filetest.attrs["Iterations"] - 1) * filetest.attrs["dt"], num=filetest.attrs["Iterations"]) + / 1e-9 + ) timeref = timetest # Arrays for storing field data - datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), - dtype=float_or_double) + datatest = np.zeros((filetest.attrs["Iterations"], len(outputstest)), dtype=float_or_double) for ID, name in enumerate(outputstest): datatest[:, ID] = filetest[path + str(name)][:] if np.any(np.isnan(datatest[:, ID])): - logger.exception('Test data contains NaNs') + logger.exception("Test data contains NaNs") raise ValueError # Tx/Rx position to feed to analytical solution - rxpos = filetest[path].attrs['Position'] - txpos = filetest['/srcs/src1/'].attrs['Position'] - rxposrelative = ((rxpos[0] - txpos[0]), - (rxpos[1] - txpos[1]), - (rxpos[2] - txpos[2])) + rxpos = filetest[path].attrs["Position"] + txpos = filetest["/srcs/src1/"].attrs["Position"] + rxposrelative = ((rxpos[0] - txpos[0]), (rxpos[1] - txpos[1]), (rxpos[2] - txpos[2])) # Analytical solution of a dipole in free space - dataref = hertzian_dipole_fs(filetest.attrs['Iterations'], - filetest.attrs['dt'], - filetest.attrs['dx_dy_dz'], rxposrelative) + dataref = hertzian_dipole_fs( + filetest.attrs["Iterations"], filetest.attrs["dt"], filetest.attrs["dx_dy_dz"], rxposrelative + ) else: # Get output for model and reference files - fileref = f'{file.stem}_ref' + fileref = f"{file.stem}_ref" fileref = file.parent / Path(fileref) - fileref = h5py.File(fileref.with_suffix('.h5'), 'r') - filetest = h5py.File(file.with_suffix('.h5'), 'r') - testresults[model]['Ref version'] = fileref.attrs['gprMax'] - testresults[model]['Test version'] = filetest.attrs['gprMax'] + fileref = h5py.File(fileref.with_suffix(".h5"), "r") + filetest = h5py.File(file.with_suffix(".h5"), "r") + testresults[model]["Ref version"] = fileref.attrs["gprMax"] + testresults[model]["Test version"] = filetest.attrs["gprMax"] # Get available field output component names outputsref = list(fileref[path].keys()) outputstest = list(filetest[path].keys()) if outputsref != outputstest: - logger.exception('Field output components do not match reference solution') + logger.exception("Field output components do not match reference solution") raise ValueError # Check that type of float used to store fields matches if filetest[path + outputstest[0]].dtype != fileref[path + outputsref[0]].dtype: - logger.warning(f'Type of floating point number in test model ' + - f'({filetest[path + outputstest[0]].dtype}) does not ' + - f'match type in reference solution ({fileref[path + outputsref[0]].dtype})\n') + logger.warning( + f"Type of floating point number in test model " + + f"({filetest[path + outputstest[0]].dtype}) does not " + + f"match type in reference solution ({fileref[path + outputsref[0]].dtype})\n" + ) float_or_doubleref = fileref[path + outputsref[0]].dtype float_or_doubletest = filetest[path + outputstest[0]].dtype # Arrays for storing time - timeref = np.zeros((fileref.attrs['Iterations']), dtype=float_or_doubleref) - timeref = np.linspace(0, (fileref.attrs['Iterations'] - 1) * fileref.attrs['dt'], - num=fileref.attrs['Iterations']) / 1e-9 - timetest = np.zeros((filetest.attrs['Iterations']), dtype=float_or_doubletest) - timetest = np.linspace(0, (filetest.attrs['Iterations'] - 1) * filetest.attrs['dt'], - num=filetest.attrs['Iterations']) / 1e-9 + timeref = np.zeros((fileref.attrs["Iterations"]), dtype=float_or_doubleref) + timeref = ( + np.linspace(0, (fileref.attrs["Iterations"] - 1) * fileref.attrs["dt"], num=fileref.attrs["Iterations"]) + / 1e-9 + ) + timetest = np.zeros((filetest.attrs["Iterations"]), dtype=float_or_doubletest) + timetest = ( + np.linspace(0, (filetest.attrs["Iterations"] - 1) * filetest.attrs["dt"], num=filetest.attrs["Iterations"]) + / 1e-9 + ) # Arrays for storing field data - dataref = np.zeros((fileref.attrs['Iterations'], len(outputsref)), - dtype=float_or_doubleref) - datatest = np.zeros((filetest.attrs['Iterations'], len(outputstest)), - dtype=float_or_doubletest) + dataref = np.zeros((fileref.attrs["Iterations"], len(outputsref)), dtype=float_or_doubleref) + datatest = np.zeros((filetest.attrs["Iterations"], len(outputstest)), dtype=float_or_doubletest) for ID, name in enumerate(outputsref): dataref[:, ID] = fileref[path + str(name)][:] datatest[:, ID] = filetest[path + str(name)][:] if np.any(np.isnan(datatest[:, ID])): - logger.exception('Test data contains NaNs') + logger.exception("Test data contains NaNs") raise ValueError fileref.close() @@ -163,43 +173,52 @@ for i, model in enumerate(testmodels): datadiffs = np.zeros(datatest.shape, dtype=np.float64) for i in range(len(outputstest)): maxi = np.amax(np.abs(dataref[:, i])) - datadiffs[:, i] = np.divide(np.abs(dataref[:, i] - datatest[:, i]), maxi, - out=np.zeros_like(dataref[:, i]), - where=maxi != 0) # Replace any division by zero with zero + datadiffs[:, i] = np.divide( + np.abs(dataref[:, i] - datatest[:, i]), maxi, out=np.zeros_like(dataref[:, i]), where=maxi != 0 + ) # Replace any division by zero with zero # Calculate power (ignore warning from taking a log of any zero values) - with np.errstate(divide='ignore'): + with np.errstate(divide="ignore"): datadiffs[:, i] = 20 * np.log10(datadiffs[:, i]) # Replace any NaNs or Infs from zero division datadiffs[:, i][np.invert(np.isfinite(datadiffs[:, i]))] = 0 # Store max difference maxdiff = np.amax(np.amax(datadiffs)) - testresults[model]['Max diff'] = maxdiff + testresults[model]["Max diff"] = maxdiff # Plot datasets - fig1, ((ex1, hx1), (ey1, hy1), (ez1, hz1)) = plt.subplots(nrows=3, ncols=2, - sharex=False, sharey='col', - subplot_kw=dict(xlabel='Time [ns]'), - num=model + '.in', - figsize=(20, 10), - facecolor='w', - edgecolor='w') - ex1.plot(timetest, datatest[:, 0], 'r', lw=2, label=model) - ex1.plot(timeref, dataref[:, 0], 'g', lw=2, ls='--', label=f'{model}(Ref)') - ey1.plot(timetest, datatest[:, 1], 'r', lw=2, label=model) - ey1.plot(timeref, dataref[:, 1], 'g', lw=2, ls='--', label=f'{model}(Ref)') - ez1.plot(timetest, datatest[:, 2], 'r', lw=2, label=model) - ez1.plot(timeref, dataref[:, 2], 'g', lw=2, ls='--', label=f'{model}(Ref)') - hx1.plot(timetest, datatest[:, 3], 'r', lw=2, label=model) - hx1.plot(timeref, dataref[:, 3], 'g', lw=2, ls='--', label=f'{model}(Ref)') - hy1.plot(timetest, datatest[:, 4], 'r', lw=2, label=model) - hy1.plot(timeref, dataref[:, 4], 'g', lw=2, ls='--', label=f'{model}(Ref)') - hz1.plot(timetest, datatest[:, 5], 'r', lw=2, label=model) - hz1.plot(timeref, dataref[:, 5], 'g', lw=2, ls='--', label=f'{model}(Ref)') - ylabels = ['$E_x$, field strength [V/m]', '$H_x$, field strength [A/m]', - '$E_y$, field strength [V/m]', '$H_y$, field strength [A/m]', - '$E_z$, field strength [V/m]', '$H_z$, field strength [A/m]'] + fig1, ((ex1, hx1), (ey1, hy1), (ez1, hz1)) = plt.subplots( + nrows=3, + ncols=2, + sharex=False, + sharey="col", + subplot_kw=dict(xlabel="Time [ns]"), + num=model + ".in", + figsize=(20, 10), + facecolor="w", + edgecolor="w", + ) + ex1.plot(timetest, datatest[:, 0], "r", lw=2, label=model) + ex1.plot(timeref, dataref[:, 0], "g", lw=2, ls="--", label=f"{model}(Ref)") + ey1.plot(timetest, datatest[:, 1], "r", lw=2, label=model) + ey1.plot(timeref, dataref[:, 1], "g", lw=2, ls="--", label=f"{model}(Ref)") + ez1.plot(timetest, datatest[:, 2], "r", lw=2, label=model) + ez1.plot(timeref, dataref[:, 2], "g", lw=2, ls="--", label=f"{model}(Ref)") + hx1.plot(timetest, datatest[:, 3], "r", lw=2, label=model) + hx1.plot(timeref, dataref[:, 3], "g", lw=2, ls="--", label=f"{model}(Ref)") + hy1.plot(timetest, datatest[:, 4], "r", lw=2, label=model) + hy1.plot(timeref, dataref[:, 4], "g", lw=2, ls="--", label=f"{model}(Ref)") + hz1.plot(timetest, datatest[:, 5], "r", lw=2, label=model) + hz1.plot(timeref, dataref[:, 5], "g", lw=2, ls="--", label=f"{model}(Ref)") + ylabels = [ + "$E_x$, field strength [V/m]", + "$H_x$, field strength [A/m]", + "$E_y$, field strength [V/m]", + "$H_y$, field strength [A/m]", + "$E_z$, field strength [V/m]", + "$H_z$, field strength [A/m]", + ] for i, ax in enumerate(fig1.axes): ax.set_ylabel(ylabels[i]) ax.set_xlim(0, np.amax(timetest)) @@ -207,22 +226,31 @@ for i, model in enumerate(testmodels): ax.legend() # Plot diffs - fig2, ((ex2, hx2), (ey2, hy2), (ez2, hz2)) = plt.subplots(nrows=3, ncols=2, - sharex=False, sharey='col', - subplot_kw=dict(xlabel='Time [ns]'), - num='Diffs: ' + model + '.in', - figsize=(20, 10), - facecolor='w', - edgecolor='w') - ex2.plot(timeref, datadiffs[:, 0], 'r', lw=2, label='Ex') - ey2.plot(timeref, datadiffs[:, 1], 'r', lw=2, label='Ey') - ez2.plot(timeref, datadiffs[:, 2], 'r', lw=2, label='Ez') - hx2.plot(timeref, datadiffs[:, 3], 'r', lw=2, label='Hx') - hy2.plot(timeref, datadiffs[:, 4], 'r', lw=2, label='Hy') - hz2.plot(timeref, datadiffs[:, 5], 'r', lw=2, label='Hz') - ylabels = ['$E_x$, difference [dB]', '$H_x$, difference [dB]', - '$E_y$, difference [dB]', '$H_y$, difference [dB]', - '$E_z$, difference [dB]', '$H_z$, difference [dB]'] + fig2, ((ex2, hx2), (ey2, hy2), (ez2, hz2)) = plt.subplots( + nrows=3, + ncols=2, + sharex=False, + sharey="col", + subplot_kw=dict(xlabel="Time [ns]"), + num="Diffs: " + model + ".in", + figsize=(20, 10), + facecolor="w", + edgecolor="w", + ) + ex2.plot(timeref, datadiffs[:, 0], "r", lw=2, label="Ex") + ey2.plot(timeref, datadiffs[:, 1], "r", lw=2, label="Ey") + ez2.plot(timeref, datadiffs[:, 2], "r", lw=2, label="Ez") + hx2.plot(timeref, datadiffs[:, 3], "r", lw=2, label="Hx") + hy2.plot(timeref, datadiffs[:, 4], "r", lw=2, label="Hy") + hz2.plot(timeref, datadiffs[:, 5], "r", lw=2, label="Hz") + ylabels = [ + "$E_x$, difference [dB]", + "$H_x$, difference [dB]", + "$E_y$, difference [dB]", + "$H_y$, difference [dB]", + "$E_z$, difference [dB]", + "$H_z$, difference [dB]", + ] for i, ax in enumerate(fig2.axes): ax.set_ylabel(ylabels[i]) ax.set_xlim(0, np.amax(timetest)) @@ -230,23 +258,25 @@ for i, model in enumerate(testmodels): ax.grid() # Save a PDF/PNG of the figure - filediffs = f'{file.stem}_diffs' + filediffs = f"{file.stem}_diffs" filediffs = file.parent / Path(filediffs) - # fig1.savefig(file.with_suffix('.pdf'), dpi=None, format='pdf', + # fig1.savefig(file.with_suffix('.pdf'), dpi=None, format='pdf', # bbox_inches='tight', pad_inches=0.1) - # fig2.savefig(savediffs.with_suffix('.pdf'), dpi=None, format='pdf', + # fig2.savefig(savediffs.with_suffix('.pdf'), dpi=None, format='pdf', # bbox_inches='tight', pad_inches=0.1) - fig1.savefig(file.with_suffix('.png'), dpi=150, format='png', - bbox_inches='tight', pad_inches=0.1) - fig2.savefig(filediffs.with_suffix('.png'), dpi=150, format='png', - bbox_inches='tight', pad_inches=0.1) + fig1.savefig(file.with_suffix(".png"), dpi=150, format="png", bbox_inches="tight", pad_inches=0.1) + fig2.savefig(filediffs.with_suffix(".png"), dpi=150, format="png", bbox_inches="tight", pad_inches=0.1) # Summary of results for name, data in sorted(testresults.items()): - if 'analytical' in name: - logger.info(f"Test '{name}.in' using v.{data['Test version']} compared " + - f"to analytical solution. Max difference {data['Max diff']:.2f}dB.") + if "analytical" in name: + logger.info( + f"Test '{name}.in' using v.{data['Test version']} compared " + + f"to analytical solution. Max difference {data['Max diff']:.2f}dB." + ) else: - logger.info(f"Test '{name}.in' using v.{data['Test version']} compared to " + - f"reference solution using v.{data['Ref version']}. Max difference " + - f"{data['Max diff']:.2f}dB.") + logger.info( + f"Test '{name}.in' using v.{data['Test version']} compared to " + + f"reference solution using v.{data['Ref version']}. Max difference " + + f"{data['Max diff']:.2f}dB." + ) diff --git a/toolboxes/AntennaPatterns/initial_save.py b/toolboxes/AntennaPatterns/initial_save.py index 8c1b04f1..27df19fb 100644 --- a/toolboxes/AntennaPatterns/initial_save.py +++ b/toolboxes/AntennaPatterns/initial_save.py @@ -18,8 +18,11 @@ logger = logging.getLogger(__name__) # Parse command line arguments -parser = argparse.ArgumentParser(description='Calculate and store (in a Numpy file) field patterns from a simulation with receivers positioned in circles around an antenna.', usage='cd gprMax; python -m user_libs.AntennaPatterns.initial_save outputfile') -parser.add_argument('outputfile', help='name of gprMax output file including path') +parser = argparse.ArgumentParser( + description="Calculate and store (in a Numpy file) field patterns from a simulation with receivers positioned in circles around an antenna.", + usage="cd gprMax; python -m user_libs.AntennaPatterns.initial_save outputfile", +) +parser.add_argument("outputfile", help="name of gprMax output file including path") args = parser.parse_args() outputfile = args.outputfile @@ -27,7 +30,7 @@ outputfile = args.outputfile # User configurable parameters # Pattern type (E or H) -type = 'H' +type = "H" # Antenna (true if using full antenna model; false for a theoretical Hertzian dipole antenna = True @@ -55,35 +58,47 @@ traceno = np.s_[:] # All traces # Critical angle and velocity if epsr: mr = 1 - z1 = np.sqrt(mr / epsr) * config.sim_config.em_consts['z0'] - v1 = config.sim_config.em_consts['c'] / np.sqrt(epsr) - thetac = np.round(np.arcsin(v1 / config.sim_config.em_consts['c']) * (180 / np.pi)) + z1 = np.sqrt(mr / epsr) * config.sim_config.em_consts["z0"] + v1 = config.sim_config.em_consts["c"] / np.sqrt(epsr) + thetac = np.round(np.arcsin(v1 / config.sim_config.em_consts["c"]) * (180 / np.pi)) wavelength = v1 / f # Print some useful information -logger.info('Centre frequency: {} GHz'.format(f / 1e9)) +logger.info("Centre frequency: {} GHz".format(f / 1e9)) if epsr: - logger.info('Critical angle for Er {} is {} degrees'.format(epsr, thetac)) - logger.info('Wavelength: {:.3f} m'.format(wavelength)) - logger.info('Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)'.format(radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength)) - logger.info('Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m'.format(0.62 * np.sqrt((D**3) / wavelength))) - logger.info('Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m'.format((2 * D**2) / wavelength)) + logger.info("Critical angle for Er {} is {} degrees".format(epsr, thetac)) + logger.info("Wavelength: {:.3f} m".format(wavelength)) + logger.info( + "Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)".format( + radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength + ) + ) + logger.info( + "Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m".format( + 0.62 * np.sqrt((D**3) / wavelength) + ) + ) + logger.info( + "Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m".format( + (2 * D**2) / wavelength + ) + ) # Load text file with coordinates of pattern origin -origin = np.loadtxt(os.path.splitext(outputfile)[0] + '_rxsorigin.txt') +origin = np.loadtxt(os.path.splitext(outputfile)[0] + "_rxsorigin.txt") # Load output file and read some header information -f = h5py.File(outputfile, 'r') -iterations = f.attrs['Iterations'] -dt = f.attrs['dt'] -nrx = f.attrs['nrx'] +f = h5py.File(outputfile, "r") +iterations = f.attrs["Iterations"] +dt = f.attrs["dt"] +nrx = f.attrs["nrx"] if antenna: nrx = nrx - 1 # Ignore first receiver point with full antenna model start = 2 else: start = 1 time = np.arange(0, dt * iterations, dt) -time = time / 1E-9 +time = time / 1e-9 fs = 1 / dt # Sampling frequency # Initialise arrays to store fields @@ -105,15 +120,15 @@ Hthetasum = np.zeros(len(theta), dtype=np.float32) patternsave = np.zeros((len(radii), len(theta)), dtype=np.float32) for rx in range(0, nrx): - path = '/rxs/rx' + str(rx + start) + '/' - position = f[path].attrs['Position'][:] + path = "/rxs/rx" + str(rx + start) + "/" + position = f[path].attrs["Position"][:] coords[rx, :] = position - origin - Ex[:, rx] = f[path + 'Ex'][:] - Ey[:, rx] = f[path + 'Ey'][:] - Ez[:, rx] = f[path + 'Ez'][:] - Hx[:, rx] = f[path + 'Hx'][:] - Hy[:, rx] = f[path + 'Hy'][:] - Hz[:, rx] = f[path + 'Hz'][:] + Ex[:, rx] = f[path + "Ex"][:] + Ey[:, rx] = f[path + "Ey"][:] + Ez[:, rx] = f[path + "Ez"][:] + Hx[:, rx] = f[path + "Hx"][:] + Hy[:, rx] = f[path + "Hy"][:] + Hz[:, rx] = f[path + "Hz"][:] f.close() # Plot traces for sanity checking @@ -141,14 +156,23 @@ for radius in range(0, len(radii)): # Observation points for pt in range(rxstart, rxstart + len(theta)): # Cartesian to spherical coordinate transform coefficients from (Kraus,1991,Electromagnetics,p.34) - r1 = coords[pt, 0] / np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2 + coords[pt, 2]**2) - r2 = coords[pt, 1] / np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2 + coords[pt, 2]**2) - r3 = coords[pt, 2] / np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2 + coords[pt, 2]**2) - theta1 = (coords[pt, 0] * coords[pt, 2]) / (np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2) * np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2 + coords[pt, 2]**2)) - theta2 = (coords[pt, 1] * coords[pt, 2]) / (np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2) * np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2 + coords[pt, 2]**2)) - theta3 = -(np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2) / np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2 + coords[pt, 2]**2)) - phi1 = -(coords[pt, 1] / np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2)) - phi2 = coords[pt, 0] / np.sqrt(coords[pt, 0]**2 + coords[pt, 1]**2) + r1 = coords[pt, 0] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2) + r2 = coords[pt, 1] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2) + r3 = coords[pt, 2] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2) + theta1 = (coords[pt, 0] * coords[pt, 2]) / ( + np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2) + * np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2) + ) + theta2 = (coords[pt, 1] * coords[pt, 2]) / ( + np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2) + * np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2) + ) + theta3 = -( + np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2) + / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2 + coords[pt, 2] ** 2) + ) + phi1 = -(coords[pt, 1] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2)) + phi2 = coords[pt, 0] / np.sqrt(coords[pt, 0] ** 2 + coords[pt, 1] ** 2) phi3 = 0 # Fields in spherical coordinates @@ -161,22 +185,22 @@ for radius in range(0, len(radii)): # Calculate metric for time-domain field pattern values if impscaling and coords[pt, 2] < 0: - Ethetasum[index] = np.sum(Etheta[:, index]**2) / z1 - Hthetasum[index] = np.sum(Htheta[:, index]**2) / z1 + Ethetasum[index] = np.sum(Etheta[:, index] ** 2) / z1 + Hthetasum[index] = np.sum(Htheta[:, index] ** 2) / z1 else: - Ethetasum[index] = np.sum(Etheta[:, index]**2) / config.sim_config.em_consts['z0'] - Hthetasum[index] = np.sum(Htheta[:, index]**2) / config.sim_config.em_consts['z0'] + Ethetasum[index] = np.sum(Etheta[:, index] ** 2) / config.sim_config.em_consts["z0"] + Hthetasum[index] = np.sum(Htheta[:, index] ** 2) / config.sim_config.em_consts["z0"] index += 1 - if type == 'H': + if type == "H": # Flip H-plane patterns as rx points are written CCW but always plotted CW patternsave[radius, :] = Hthetasum[::-1] - elif type == 'E': + elif type == "E": patternsave[radius, :] = Ethetasum rxstart += len(theta) # Save pattern to numpy file np.save(os.path.splitext(outputfile)[0], patternsave) -logger.info('Written Numpy file: {}.npy'.format(os.path.splitext(outputfile)[0])) +logger.info("Written Numpy file: {}.npy".format(os.path.splitext(outputfile)[0])) diff --git a/toolboxes/AntennaPatterns/plot_fields.py b/toolboxes/AntennaPatterns/plot_fields.py index f9e49a75..1d14b6de 100644 --- a/toolboxes/AntennaPatterns/plot_fields.py +++ b/toolboxes/AntennaPatterns/plot_fields.py @@ -17,8 +17,11 @@ logger = logging.getLogger(__name__) # Parse command line arguments -parser = argparse.ArgumentParser(description='Plot field patterns from a simulation with receivers positioned in circles around an antenna. This module should be used after the field pattern data has been processed and stored using the initial_save.py module.', usage='cd gprMax; python -m user_libs.AntennaPatterns.plot_fields numpyfile') -parser.add_argument('numpyfile', help='name of numpy file including path') +parser = argparse.ArgumentParser( + description="Plot field patterns from a simulation with receivers positioned in circles around an antenna. This module should be used after the field pattern data has been processed and stored using the initial_save.py module.", + usage="cd gprMax; python -m user_libs.AntennaPatterns.plot_fields numpyfile", +) +parser.add_argument("numpyfile", help="name of numpy file including path") # parser.add_argument('hertzian', help='name of numpy file including path') args = parser.parse_args() patterns = np.load(args.numpyfile) @@ -28,7 +31,7 @@ patterns = np.load(args.numpyfile) # User configurable parameters # Pattern type (E or H) -type = 'H' +type = "H" # Relative permittivity of half-space for homogeneous materials (set to None for inhomogeneous) epsr = 5 @@ -52,33 +55,45 @@ step = 12 # Critical angle and velocity if epsr: mr = 1 - z1 = np.sqrt(mr / epsr) * config.sim_config.em_consts['z0'] - v1 = config.sim_config.em_consts['c'] / np.sqrt(epsr) - thetac = np.round(np.rad2deg(np.arcsin(v1 / config.sim_config.em_consts['c']))) + z1 = np.sqrt(mr / epsr) * config.sim_config.em_consts["z0"] + v1 = config.sim_config.em_consts["c"] / np.sqrt(epsr) + thetac = np.round(np.rad2deg(np.arcsin(v1 / config.sim_config.em_consts["c"]))) wavelength = v1 / f # Print some useful information -logger.info('Centre frequency: {} GHz'.format(f / 1e9)) +logger.info("Centre frequency: {} GHz".format(f / 1e9)) if epsr: - logger.info('Critical angle for Er {} is {} degrees'.format(epsr, thetac)) - logger.info('Wavelength: {:.3f} m'.format(wavelength)) - logger.info('Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)'.format(radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength)) - logger.info('Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m'.format(0.62 * np.sqrt((D**3) / wavelength))) - logger.info('Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m'.format((2 * D**2) / wavelength)) + logger.info("Critical angle for Er {} is {} degrees".format(epsr, thetac)) + logger.info("Wavelength: {:.3f} m".format(wavelength)) + logger.info( + "Observation distance(s) from {:.3f} m ({:.1f} wavelengths) to {:.3f} m ({:.1f} wavelengths)".format( + radii[0], radii[0] / wavelength, radii[-1], radii[-1] / wavelength + ) + ) + logger.info( + "Theoretical boundary between reactive & radiating near-field (0.62*sqrt((D^3/wavelength): {:.3f} m".format( + 0.62 * np.sqrt((D**3) / wavelength) + ) + ) + logger.info( + "Theoretical boundary between radiating near-field & far-field (2*D^2/wavelength): {:.3f} m".format( + (2 * D**2) / wavelength + ) + ) # Setup figure -fig = plt.figure(num=args.numpyfile, figsize=(8, 8), facecolor='w', edgecolor='w') +fig = plt.figure(num=args.numpyfile, figsize=(8, 8), facecolor="w", edgecolor="w") ax = plt.subplot(111, polar=True) -cmap = plt.cm.get_cmap('rainbow') -ax.set_prop_cycle('color', [cmap(i) for i in np.linspace(0, 1, len(radii))]) +cmap = plt.cm.get_cmap("rainbow") +ax.set_prop_cycle("color", [cmap(i) for i in np.linspace(0, 1, len(radii))]) # Critical angle window and air/subsurface interface lines if epsr: - ax.plot([0, np.deg2rad(180 - thetac)], [min, 0], color='0.7', lw=2) - ax.plot([0, np.deg2rad(180 + thetac)], [min, 0], color='0.7', lw=2) -ax.plot([np.deg2rad(270), np.deg2rad(90)], [0, 0], color='0.7', lw=2) -ax.annotate('Air', xy=(np.deg2rad(270), 0), xytext=(8, 8), textcoords='offset points') -ax.annotate('Ground', xy=(np.deg2rad(270), 0), xytext=(8, -15), textcoords='offset points') + ax.plot([0, np.deg2rad(180 - thetac)], [min, 0], color="0.7", lw=2) + ax.plot([0, np.deg2rad(180 + thetac)], [min, 0], color="0.7", lw=2) +ax.plot([np.deg2rad(270), np.deg2rad(90)], [0, 0], color="0.7", lw=2) +ax.annotate("Air", xy=(np.deg2rad(270), 0), xytext=(8, 8), textcoords="offset points") +ax.annotate("Ground", xy=(np.deg2rad(270), 0), xytext=(8, -15), textcoords="offset points") # Plot patterns for patt in range(0, len(radii)): @@ -86,12 +101,12 @@ for patt in range(0, len(radii)): pattplot = pattplot / np.max(np.max(patterns)) # Normalise, based on set of patterns # Calculate power (ignore warning from taking a log of any zero values) - with np.errstate(divide='ignore'): + with np.errstate(divide="ignore"): power = 10 * np.log10(pattplot) # Replace any NaNs or Infs from zero division power[np.invert(np.isfinite(power))] = 0 - ax.plot(theta, power, label='{:.2f}m'.format(radii[patt]), marker='.', ms=6, lw=1.5) + ax.plot(theta, power, label="{:.2f}m".format(radii[patt]), marker=".", ms=6, lw=1.5) # Add Hertzian dipole plot # hertzplot1 = np.append(hertzian[0, :], hertzian[0, 0]) # Append start value to close circle @@ -102,8 +117,8 @@ for patt in range(0, len(radii)): # ax.plot(theta, 10 * np.log10(hertzplot2), label='Inf. dipole, 0.58m', color='black', ls='--', lw=3) # Theta axis options -ax.set_theta_zero_location('N') -ax.set_theta_direction('clockwise') +ax.set_theta_zero_location("N") +ax.set_theta_direction("clockwise") ax.set_thetagrids(np.arange(0, 360, 30)) # Radial axis options @@ -111,19 +126,21 @@ ax.set_rmax(0) ax.set_rlabel_position(45) ax.set_yticks(np.arange(min, step, step)) yticks = ax.get_yticks().tolist() -yticks[-1] = '0 dB' +yticks[-1] = "0 dB" ax.set_yticklabels(yticks) # Grid and legend ax.grid(True) handles, existlabels = ax.get_legend_handles_labels() -leg = ax.legend([handles[0], handles[-1]], [existlabels[0], existlabels[-1]], ncol=2, loc=(0.27, -0.12), frameon=False) # Plot just first and last legend entries +leg = ax.legend( + [handles[0], handles[-1]], [existlabels[0], existlabels[-1]], ncol=2, loc=(0.27, -0.12), frameon=False +) # Plot just first and last legend entries # leg = ax.legend([handles[0], handles[-3], handles[-2], handles[-1]], [existlabels[0], existlabels[-3], existlabels[-2], existlabels[-1]], ncol=4, loc=(-0.13,-0.12), frameon=False) [legobj.set_linewidth(2) for legobj in leg.legendHandles] # Save a pdf of the plot -savename = os.path.splitext(args.numpyfile)[0] + '.pdf' -fig.savefig(savename, dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1) +savename = os.path.splitext(args.numpyfile)[0] + ".pdf" +fig.savefig(savename, dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1) # savename = os.path.splitext(args.numpyfile)[0] + '.png' # fig.savefig(savename, dpi=150, format='png', bbox_inches='tight', pad_inches=0.1) diff --git a/toolboxes/AustinManWoman/head_only_h5.py b/toolboxes/AustinManWoman/head_only_h5.py index 3eac0e03..ff4de7fb 100644 --- a/toolboxes/AustinManWoman/head_only_h5.py +++ b/toolboxes/AustinManWoman/head_only_h5.py @@ -8,22 +8,24 @@ logger = logging.getLogger(__name__) # Parse command line arguments -parser = argparse.ArgumentParser(description='Writes a HDF5 file of AustinMan or AustinWoman head only.', usage='python head_only_hdf5 filename') -parser.add_argument('filename', help='name and path to (HDF5) file containing AustinMan or AustinWoman model') +parser = argparse.ArgumentParser( + description="Writes a HDF5 file of AustinMan or AustinWoman head only.", usage="python head_only_hdf5 filename" +) +parser.add_argument("filename", help="name and path to (HDF5) file containing AustinMan or AustinWoman model") args = parser.parse_args() # Read full body HDF5 file -f = h5py.File(args.filename, 'r') -dx_dy_dz = f.attrs['dx_dy_dz'] -data = f['/data'][:, :, :] +f = h5py.File(args.filename, "r") +dx_dy_dz = f.attrs["dx_dy_dz"] +data = f["/data"][:, :, :] # Define head as last 1/8 of total body height nzhead = 7 * int(data.shape[2] / 8) -logger.info(f'Dimensions of head model: {data.shape[0]:g} x {data.shape[1]:g} x {data.shape[2] - nzhead:g} cells') +logger.info(f"Dimensions of head model: {data.shape[0]:g} x {data.shape[1]:g} x {data.shape[2] - nzhead:g} cells") # Write HDF5 file -headfile = os.path.splitext(args.filename)[0] + '_head.h5' -f = h5py.File(headfile, 'w') -f.attrs['dx_dy_dz'] = dx_dy_dz -f['/data'] = data[:, :, nzhead:data.shape[2]] +headfile = os.path.splitext(args.filename)[0] + "_head.h5" +f = h5py.File(headfile, "w") +f.attrs["dx_dy_dz"] = dx_dy_dz +f["/data"] = data[:, :, nzhead : data.shape[2]] diff --git a/toolboxes/DebyeFit/Debye_Fit.py b/toolboxes/DebyeFit/Debye_Fit.py index 362b8950..28faf219 100644 --- a/toolboxes/DebyeFit/Debye_Fit.py +++ b/toolboxes/DebyeFit/Debye_Fit.py @@ -19,7 +19,7 @@ from optimization import DA_DLS, DE_DLS, PSO_DLS class Relaxation(object): - """ Create Relaxation function object for complex material. + """Create Relaxation function object for complex material. :param sigma: The conductivity (Siemens/metre). :type sigma: float, non-optional @@ -52,13 +52,20 @@ class Relaxation(object): :type optimizer_options: dict, optional, default: empty dict """ - def __init__(self, sigma, mu, mu_sigma, - material_name, f_n=50, - number_of_debye_poles=-1, - plot=True, save=False, - optimizer=PSO_DLS, - optimizer_options={}): - self.name = 'Relaxation function' + def __init__( + self, + sigma, + mu, + mu_sigma, + material_name, + f_n=50, + number_of_debye_poles=-1, + plot=True, + save=False, + optimizer=PSO_DLS, + optimizer_options={}, + ): + self.name = "Relaxation function" self.params = {} self.number_of_debye_poles = number_of_debye_poles self.f_n = f_n @@ -71,7 +78,7 @@ class Relaxation(object): self.optimizer = optimizer(**optimizer_options) def set_freq(self, f_min, f_max, f_n=50): - """ Interpolate frequency vector using n equally logarithmicaly spaced frequencies. + """Interpolate frequency vector using n equally logarithmicaly spaced frequencies. Args: f_min (float): First bound of the frequency range @@ -84,19 +91,17 @@ class Relaxation(object): f_min and f_max must satisfied f_min < f_max """ if abs(f_min - f_max) > 1e12: - warnings.warn(f'The chosen range is realy big. ' - f'Consider setting greater number of points ' - f'on the frequency grid!') - self.freq = np.logspace(np.log10(f_min), - np.log10(f_max), - int(f_n)) + warnings.warn( + f"The chosen range is realy big. " + f"Consider setting greater number of points " + f"on the frequency grid!" + ) + self.freq = np.logspace(np.log10(f_min), np.log10(f_max), int(f_n)) def check_inputs(self): - """ Check the validity of the inputs. """ + """Check the validity of the inputs.""" try: - d = [float(i) for i in - [self.number_of_debye_poles, - self.sigma, self.mu, self.mu_sigma]] + d = [float(i) for i in [self.number_of_debye_poles, self.sigma, self.mu, self.mu_sigma]] except ValueError: sys.exit("The inputs should be numeric.") if not isinstance(self.number_of_debye_poles, int): @@ -105,7 +110,7 @@ class Relaxation(object): sys.exit("The inputs should be positive.") def calculation(self): - """ Approximate the given relaxation function + """Approximate the given relaxation function (Havriliak-Negami function, Crim, Jonscher) or based on raw data. """ raise NotImplementedError() @@ -116,17 +121,16 @@ class Relaxation(object): Returns: s (str): Info about chosen function and its parameters. """ - print(f"Approximating {self.name}" - f" using {self.number_of_debye_poles} Debye poles") + print(f"Approximating {self.name}" f" using {self.number_of_debye_poles} Debye poles") print(f"{self.name} parameters: ") - s = '' + s = "" for k, v in self.params.items(): s += f"{k:10s} = {v}\n" print(s) - return f'{self.name}:\n{s}' + return f"{self.name}:\n{s}" def optimize(self): - """ Calling the main optimisation module with defined lower and upper boundaries of search. + """Calling the main optimisation module with defined lower and upper boundaries of search. Returns: tau (ndarray): The optimised relaxation times. @@ -138,21 +142,19 @@ class Relaxation(object): for given frequency points. """ # Define the lower and upper boundaries of search - lb = np.full(self.number_of_debye_poles, - -np.log10(np.max(self.freq)) - 3) - ub = np.full(self.number_of_debye_poles, - -np.log10(np.min(self.freq)) + 3) + lb = np.full(self.number_of_debye_poles, -np.log10(np.max(self.freq)) - 3) + ub = np.full(self.number_of_debye_poles, -np.log10(np.min(self.freq)) + 3) # Call optimizer to minimize the cost function - tau, weights, ee, rl, im = self.optimizer.fit(func=self.optimizer.cost_function, - lb=lb, ub=ub, - funckwargs={'rl': self.rl, - 'im': self.im, - 'freq': self.freq} - ) + tau, weights, ee, rl, im = self.optimizer.fit( + func=self.optimizer.cost_function, + lb=lb, + ub=ub, + funckwargs={"rl": self.rl, "im": self.im, "freq": self.freq}, + ) return tau, weights, ee, rl, im def run(self): - """ Solve the problem described by the given relaxation function + """Solve the problem described by the given relaxation function (Havriliak-Negami function, Crim, Jonscher) or data given from a text file. @@ -173,9 +175,7 @@ class Relaxation(object): self.rl, self.im = q.real, q.imag if self.number_of_debye_poles == -1: - print("\n#########", - "Try to automaticaly fit number of Debye poles, up to 20!", - "##########\n", sep="") + print("\n#########", "Try to automaticaly fit number of Debye poles, up to 20!", "##########\n", sep="") error = np.infty # artificial best error starting value self.number_of_debye_poles = 1 iteration = 1 @@ -198,9 +198,7 @@ class Relaxation(object): # Print the results in gprMax format style properties = self.print_output(tau, weights, ee) - print(f'The average fractional error for:\n' - f'- real part: {err_real}\n' - f'- imaginary part: {err_imag}\n') + print(f"The average fractional error for:\n" f"- real part: {err_real}\n" f"- imaginary part: {err_imag}\n") if self.save: self.save_result(properties) # Plot the actual and the approximate dielectric properties @@ -209,7 +207,7 @@ class Relaxation(object): return err_real + err_imag, properties def print_output(self, tau, weights, ee): - """ Print out the resulting Debye parameters in a gprMax format. + """Print out the resulting Debye parameters in a gprMax format. Args: tau (ndarray): The best known position form optimization module @@ -225,28 +223,25 @@ class Relaxation(object): print(f" |{'e_inf':^14s}|{'De':^14s}|{'log(tau_0)':^25s}|") print("_" * 65) for i in range(0, len(tau)): - print("Debye {0:}|{1:^14.5f}|{2:^14.5f}|{3:^25.5f}|" - .format(i + 1, ee/len(tau), weights[i], - tau[i])) + print("Debye {0:}|{1:^14.5f}|{2:^14.5f}|{3:^25.5f}|".format(i + 1, ee / len(tau), weights[i], tau[i])) print("_" * 65) # Print the Debye expnasion in a gprMax format material_prop = [] - material_prop.append("#material: {} {} {} {} {}\n".format(ee, self.sigma, - self.mu, - self.mu_sigma, - self.material_name)) + material_prop.append( + "#material: {} {} {} {} {}\n".format(ee, self.sigma, self.mu, self.mu_sigma, self.material_name) + ) print(material_prop[0], end="") dispersion_prop = "#add_dispersion_debye: {}".format(len(tau)) for i in range(len(tau)): - dispersion_prop += " {} {}".format(weights[i], 10**tau[i]) + dispersion_prop += " {} {}".format(weights[i], 10 ** tau[i]) dispersion_prop += " {}".format(self.material_name) print(dispersion_prop) - material_prop.append(dispersion_prop + '\n') + material_prop.append(dispersion_prop + "\n") return material_prop def plot_result(self, rl_exp, im_exp): - """ Plot the actual and the approximated electric permittivity, + """Plot the actual and the approximated electric permittivity, along with relative error for real and imaginary parts using a semilogarithm X axes. @@ -261,14 +256,10 @@ class Relaxation(object): gs = gridspec.GridSpec(2, 1) ax = fig.add_subplot(gs[0]) ax.grid(b=True, which="major", linewidth=0.2, linestyle="--") - ax.semilogx(self.freq * 1e-6, rl_exp, "b-", linewidth=2.0, - label="Debye Expansion: Real part") - ax.semilogx(self.freq * 1e-6, -im_exp, "k-", linewidth=2.0, - label="Debye Expansion: Imaginary part") - ax.semilogx(self.freq * 1e-6, self.rl, "r.", - linewidth=2.0, label=f"{self.name}: Real part") - ax.semilogx(self.freq * 1e-6, -self.im, "g.", linewidth=2.0, - label=f"{self.name}: Imaginary part") + ax.semilogx(self.freq * 1e-6, rl_exp, "b-", linewidth=2.0, label="Debye Expansion: Real part") + ax.semilogx(self.freq * 1e-6, -im_exp, "k-", linewidth=2.0, label="Debye Expansion: Imaginary part") + ax.semilogx(self.freq * 1e-6, self.rl, "r.", linewidth=2.0, label=f"{self.name}: Real part") + ax.semilogx(self.freq * 1e-6, -self.im, "g.", linewidth=2.0, label=f"{self.name}: Imaginary part") ax.set_ylim([-1, np.max(np.concatenate([self.rl, -self.im])) + 1]) ax.legend() ax.set_xlabel("Frequency (MHz)") @@ -276,17 +267,15 @@ class Relaxation(object): ax = fig.add_subplot(gs[1]) ax.grid(b=True, which="major", linewidth=0.2, linestyle="--") - ax.semilogx(self.freq * 1e-6, (rl_exp - self.rl)/(self.rl + 1), "b-", linewidth=2.0, - label="Real part") - ax.semilogx(self.freq * 1e-6, (-im_exp + self.im)/(self.im + 1), "k-", linewidth=2.0, - label="Imaginary part") + ax.semilogx(self.freq * 1e-6, (rl_exp - self.rl) / (self.rl + 1), "b-", linewidth=2.0, label="Real part") + ax.semilogx(self.freq * 1e-6, (-im_exp + self.im) / (self.im + 1), "k-", linewidth=2.0, label="Imaginary part") ax.legend() ax.set_xlabel("Frequency (MHz)") ax.set_ylabel("Relative approximation error") plt.show() def error(self, rl_exp, im_exp): - """ Calculate the average fractional error separately for + """Calculate the average fractional error separately for relative permittivity (real part) and conductivity (imaginary part) Args: @@ -300,13 +289,13 @@ class Relaxation(object): avg_err_imag (float): average fractional error for conductivity (imaginary part) """ - avg_err_real = np.sum(np.abs((rl_exp - self.rl)/(self.rl + 1)) * 100)/len(rl_exp) - avg_err_imag = np.sum(np.abs((-im_exp + self.im)/(self.im + 1)) * 100)/len(im_exp) + avg_err_real = np.sum(np.abs((rl_exp - self.rl) / (self.rl + 1)) * 100) / len(rl_exp) + avg_err_imag = np.sum(np.abs((-im_exp + self.im) / (self.im + 1)) * 100) / len(im_exp) return avg_err_real, avg_err_imag @staticmethod def save_result(output, fdir="../materials"): - """ Save the resulting Debye parameters in a gprMax format. + """Save the resulting Debye parameters in a gprMax format. Args: output (list(str)): Material and resulting Debye parameters @@ -316,17 +305,13 @@ class Relaxation(object): if fdir != "../materials" and os.path.isdir(fdir): file_path = os.path.join(fdir, "my_materials.txt") elif os.path.isdir("../materials"): - file_path = os.path.join("../materials", - "my_materials.txt") + file_path = os.path.join("../materials", "my_materials.txt") elif os.path.isdir("materials"): - file_path = os.path.join("materials", - "my_materials.txt") + file_path = os.path.join("materials", "my_materials.txt") elif os.path.isdir("user_libs/materials"): - file_path = os.path.join("user_libs", "materials", - "my_materials.txt") + file_path = os.path.join("user_libs", "materials", "my_materials.txt") else: - sys.exit("Cannot save material properties " - f"in {os.path.join(fdir, 'my_materials.txt')}!") + sys.exit("Cannot save material properties " f"in {os.path.join(fdir, 'my_materials.txt')}!") fileH = open(file_path, "a") fileH.write(f"## {output[0].split(' ')[-1]}") fileH.writelines(output) @@ -336,7 +321,7 @@ class Relaxation(object): class HavriliakNegami(Relaxation): - """ Approximate a given Havriliak-Negami function + """Approximate a given Havriliak-Negami function Havriliak-Negami function = ε_∞ + Δ‎ε / (1 + (2πfjτ)**α)**β, where f is the frequency in Hz. @@ -362,20 +347,40 @@ class HavriliakNegami(Relaxation): :param tau_0: Real positive float number, tau_0 is the relaxation time. :type tau_0: float """ - def __init__(self, f_min, f_max, - alpha, beta, e_inf, de, tau_0, - sigma, mu, mu_sigma, material_name, - number_of_debye_poles=-1, f_n=50, - plot=False, save=False, - optimizer=PSO_DLS, - optimizer_options={}): - super(HavriliakNegami, self).__init__(sigma=sigma, mu=mu, mu_sigma=mu_sigma, - material_name=material_name, f_n=f_n, - number_of_debye_poles=number_of_debye_poles, - plot=plot, save=save, - optimizer=optimizer, - optimizer_options=optimizer_options) - self.name = 'Havriliak-Negami function' + + def __init__( + self, + f_min, + f_max, + alpha, + beta, + e_inf, + de, + tau_0, + sigma, + mu, + mu_sigma, + material_name, + number_of_debye_poles=-1, + f_n=50, + plot=False, + save=False, + optimizer=PSO_DLS, + optimizer_options={}, + ): + super(HavriliakNegami, self).__init__( + sigma=sigma, + mu=mu, + mu_sigma=mu_sigma, + material_name=material_name, + f_n=f_n, + number_of_debye_poles=number_of_debye_poles, + plot=plot, + save=save, + optimizer=optimizer, + optimizer_options=optimizer_options, + ) + self.name = "Havriliak-Negami function" # Place the lower frequency bound at f_min and the upper frequency bound at f_max if f_min > f_max: self.f_min, self.f_max = f_max, f_min @@ -384,12 +389,18 @@ class HavriliakNegami(Relaxation): # Choosing n frequencies logarithmicaly equally spaced between the bounds given self.set_freq(self.f_min, self.f_max, self.f_n) self.e_inf, self.alpha, self.beta, self.de, self.tau_0 = e_inf, alpha, beta, de, tau_0 - self.params = {'f_min': self.f_min, 'f_max': self.f_max, - 'eps_inf': self.e_inf, 'Delta_eps': self.de, 'tau_0': self.tau_0, - 'alpha': self.alpha, 'beta': self.beta} + self.params = { + "f_min": self.f_min, + "f_max": self.f_max, + "eps_inf": self.e_inf, + "Delta_eps": self.de, + "tau_0": self.tau_0, + "alpha": self.alpha, + "beta": self.beta, + } def check_inputs(self): - """ Check the validity of the Havriliak Negami model's inputs. """ + """Check the validity of the Havriliak Negami model's inputs.""" super(HavriliakNegami, self).check_inputs() try: d = [float(i) for i in self.params.values()] @@ -406,15 +417,12 @@ class HavriliakNegami(Relaxation): def calculation(self): """Calculates the Havriliak-Negami function for - the given parameters.""" - return self.e_inf + self.de / ( - 1 + (1j * 2 * np.pi * - self.freq * self.tau_0)**self.alpha - )**self.beta + the given parameters.""" + return self.e_inf + self.de / (1 + (1j * 2 * np.pi * self.freq * self.tau_0) ** self.alpha) ** self.beta class Jonscher(Relaxation): - """ Approximate a given Jonsher function + """Approximate a given Jonsher function Jonscher function = ε_∞ - ap * (-1j * 2πf / omegap)**n_p, where f is the frequency in Hz @@ -433,20 +441,39 @@ class Jonscher(Relaxation): :params n_p: Jonscher parameter, 0 < n_p < 1. :type n_p: float, non-optional """ - def __init__(self, f_min, f_max, - e_inf, a_p, omega_p, n_p, - sigma, mu, mu_sigma, - material_name, number_of_debye_poles=-1, - f_n=50, plot=False, save=False, - optimizer=PSO_DLS, - optimizer_options={}): - super(Jonscher, self).__init__(sigma=sigma, mu=mu, mu_sigma=mu_sigma, - material_name=material_name, f_n=f_n, - number_of_debye_poles=number_of_debye_poles, - plot=plot, save=save, - optimizer=optimizer, - optimizer_options=optimizer_options) - self.name = 'Jonsher function' + + def __init__( + self, + f_min, + f_max, + e_inf, + a_p, + omega_p, + n_p, + sigma, + mu, + mu_sigma, + material_name, + number_of_debye_poles=-1, + f_n=50, + plot=False, + save=False, + optimizer=PSO_DLS, + optimizer_options={}, + ): + super(Jonscher, self).__init__( + sigma=sigma, + mu=mu, + mu_sigma=mu_sigma, + material_name=material_name, + f_n=f_n, + number_of_debye_poles=number_of_debye_poles, + plot=plot, + save=save, + optimizer=optimizer, + optimizer_options=optimizer_options, + ) + self.name = "Jonsher function" # Place the lower frequency bound at f_min and the upper frequency bound at f_max if f_min > f_max: self.f_min, self.f_max = f_max, f_min @@ -455,12 +482,17 @@ class Jonscher(Relaxation): # Choosing n frequencies logarithmicaly equally spaced between the bounds given self.set_freq(self.f_min, self.f_max, self.f_n) self.e_inf, self.a_p, self.omega_p, self.n_p = e_inf, a_p, omega_p, n_p - self.params = {'f_min': self.f_min, 'f_max': self.f_max, - 'eps_inf': self.e_inf, 'n_p': self.n_p, - 'omega_p': self.omega_p, 'a_p': self.a_p} + self.params = { + "f_min": self.f_min, + "f_max": self.f_max, + "eps_inf": self.e_inf, + "n_p": self.n_p, + "omega_p": self.omega_p, + "a_p": self.a_p, + } def check_inputs(self): - """ Check the validity of the inputs. """ + """Check the validity of the inputs.""" super(Jonscher, self).check_inputs() try: d = [float(i) for i in self.params.values()] @@ -475,13 +507,13 @@ class Jonscher(Relaxation): def calculation(self): """Calculates the Q function for the given parameters""" - return self.e_inf + (self.a_p * (2 * np.pi * - self.freq / self.omega_p)**(self.n_p-1)) * ( - 1 - 1j / np.tan(self.n_p * np.pi/2)) + return self.e_inf + (self.a_p * (2 * np.pi * self.freq / self.omega_p) ** (self.n_p - 1)) * ( + 1 - 1j / np.tan(self.n_p * np.pi / 2) + ) class Crim(Relaxation): - """ Approximate a given CRIM function + """Approximate a given CRIM function CRIM = (Σ frac_i * (‎ε_∞_i + Δ‎ε_i/(1 + 2πfj*τ_i))^a)^(1/a) :param f_min: First bound of the frequency range @@ -498,20 +530,37 @@ class Crim(Relaxation): :type materials: ndarray, non-optional """ - def __init__(self, f_min, f_max, a, volumetric_fractions, - materials, sigma, mu, mu_sigma, material_name, - number_of_debye_poles=-1, f_n=50, - plot=False, save=False, - optimizer=PSO_DLS, - optimizer_options={}): - - super(Crim, self).__init__(sigma=sigma, mu=mu, mu_sigma=mu_sigma, - material_name=material_name, f_n=f_n, - number_of_debye_poles=number_of_debye_poles, - plot=plot, save=save, - optimizer=optimizer, - optimizer_options=optimizer_options) - self.name = 'CRIM function' + def __init__( + self, + f_min, + f_max, + a, + volumetric_fractions, + materials, + sigma, + mu, + mu_sigma, + material_name, + number_of_debye_poles=-1, + f_n=50, + plot=False, + save=False, + optimizer=PSO_DLS, + optimizer_options={}, + ): + super(Crim, self).__init__( + sigma=sigma, + mu=mu, + mu_sigma=mu_sigma, + material_name=material_name, + f_n=f_n, + number_of_debye_poles=number_of_debye_poles, + plot=plot, + save=save, + optimizer=optimizer, + optimizer_options=optimizer_options, + ) + self.name = "CRIM function" # Place the lower frequency bound at f_min and the upper frequency bound at f_max if f_min > f_max: self.f_min, self.f_max = f_max, f_min @@ -522,16 +571,19 @@ class Crim(Relaxation): self.a = a self.volumetric_fractions = np.array(volumetric_fractions) self.materials = np.array(materials) - self.params = {'f_min': self.f_min, 'f_max': self.f_max, - 'a': self.a, 'volumetric_fractions': self.volumetric_fractions, - 'materials': self.materials} + self.params = { + "f_min": self.f_min, + "f_max": self.f_max, + "a": self.a, + "volumetric_fractions": self.volumetric_fractions, + "materials": self.materials, + } def check_inputs(self): - """ Check the validity of the inputs. """ + """Check the validity of the inputs.""" super(Crim, self).check_inputs() try: - d = [float(i) for i in - [self.f_min, self.f_max, self.a]] + d = [float(i) for i in [self.f_min, self.f_max, self.a]] except ValueError: sys.exit("The inputs should be numeric.") if (np.array(d) < 0).sum() != 0: @@ -557,12 +609,11 @@ class Crim(Relaxation): sys.exit("Error: The summation of volumetric volumes should be equal to 1") def print_info(self): - """ Print information about chosen approximation settings """ - print(f"Approximating Complex Refractive Index Model (CRIM)" - f" using {self.number_of_debye_poles} Debye poles") + """Print information about chosen approximation settings""" + print(f"Approximating Complex Refractive Index Model (CRIM)" f" using {self.number_of_debye_poles} Debye poles") print("CRIM parameters: ") for i in range(len(self.volumetric_fractions)): - print("Material {}.:".format(i+1)) + print("Material {}.:".format(i + 1)) print("---------------------------------") print(f"{'Vol. fraction':>27s} = {self.volumetric_fractions[i]}") print(f"{'e_inf':>27s} = {self.materials[i][0]}") @@ -571,16 +622,27 @@ class Crim(Relaxation): def calculation(self): """Calculates the Crim function for the given parameters""" - return np.sum(np.repeat(self.volumetric_fractions, len(self.freq) - ).reshape((-1, len(self.materials)))*( - self.materials[:, 0] + self.materials[:, 1] / ( - 1 + 1j * 2 * np.pi * np.repeat(self.freq, len(self.materials) - ).reshape((-1, len(self.materials))) * self.materials[:, 2]))**self.a, - axis=1)**(1 / self.a) + return np.sum( + np.repeat(self.volumetric_fractions, len(self.freq)).reshape((-1, len(self.materials))) + * ( + self.materials[:, 0] + + self.materials[:, 1] + / ( + 1 + + 1j + * 2 + * np.pi + * np.repeat(self.freq, len(self.materials)).reshape((-1, len(self.materials))) + * self.materials[:, 2] + ) + ) + ** self.a, + axis=1, + ) ** (1 / self.a) class Rawdata(Relaxation): - """ Interpolate data given from a text file. + """Interpolate data given from a text file. :param filename: text file which contains three columns: frequency (Hz),Real,Imaginary (separated by comma). @@ -588,113 +650,147 @@ class Rawdata(Relaxation): :param delimiter: separator for three data columns :type delimiter: str, optional (Deafult: ',') """ - def __init__(self, filename, - sigma, mu, mu_sigma, - material_name, number_of_debye_poles=-1, - f_n=50, delimiter=',', - plot=False, save=False, - optimizer=PSO_DLS, - optimizer_options={}): - super(Rawdata, self).__init__(sigma=sigma, mu=mu, mu_sigma=mu_sigma, - material_name=material_name, f_n=f_n, - number_of_debye_poles=number_of_debye_poles, - plot=plot, save=save, - optimizer=optimizer, - optimizer_options=optimizer_options) + def __init__( + self, + filename, + sigma, + mu, + mu_sigma, + material_name, + number_of_debye_poles=-1, + f_n=50, + delimiter=",", + plot=False, + save=False, + optimizer=PSO_DLS, + optimizer_options={}, + ): + super(Rawdata, self).__init__( + sigma=sigma, + mu=mu, + mu_sigma=mu_sigma, + material_name=material_name, + f_n=f_n, + number_of_debye_poles=number_of_debye_poles, + plot=plot, + save=save, + optimizer=optimizer, + optimizer_options=optimizer_options, + ) self.delimiter = delimiter self.filename = Path(filename).absolute() - self.params = {'filename': self.filename} + self.params = {"filename": self.filename} def check_inputs(self): - """ Check the validity of the inputs. """ + """Check the validity of the inputs.""" super(Rawdata, self).check_inputs() if not os.path.isfile(self.filename): sys.exit("File doesn't exists!") def calculation(self): - """ Interpolate real and imaginary part from data. + """Interpolate real and imaginary part from data. Column framework of the input file three columns comma-separated Frequency(Hz),Real,Imaginary """ # Read the file with open(self.filename) as f: try: - array = np.array( - [[float(x) for x in line.split(self.delimiter)] for line in f] - ) + array = np.array([[float(x) for x in line.split(self.delimiter)] for line in f]) except ValueError: sys.exit("Error: The inputs should be numeric") self.set_freq(min(array[:, 0]), max(array[:, 0]), self.f_n) - rl_interp = scipy.interpolate.interp1d(array[:, 0], array[:, 1], - fill_value="extrapolate") - im_interp = scipy.interpolate.interp1d(array[:, 0], array[:, 2], - fill_value="extrapolate") + rl_interp = scipy.interpolate.interp1d(array[:, 0], array[:, 1], fill_value="extrapolate") + im_interp = scipy.interpolate.interp1d(array[:, 0], array[:, 2], fill_value="extrapolate") return rl_interp(self.freq) - 1j * im_interp(self.freq) if __name__ == "__main__": # Kelley et al. parameters - setup = HavriliakNegami(f_min=1e7, f_max=1e11, - alpha=0.91, beta=0.45, - e_inf=2.7, de=8.6-2.7, tau_0=9.4e-10, - sigma=0, mu=0, mu_sigma=0, - material_name="Kelley", f_n=100, - number_of_debye_poles=6, - plot=True, save=False, - optimizer_options={'swarmsize': 30, - 'maxiter': 100, - 'omega': 0.5, - 'phip': 1.4, - 'phig': 1.4, - 'minstep': 1e-8, - 'minfun': 1e-8, - 'seed': 111, - 'pflag': True}) + setup = HavriliakNegami( + f_min=1e7, + f_max=1e11, + alpha=0.91, + beta=0.45, + e_inf=2.7, + de=8.6 - 2.7, + tau_0=9.4e-10, + sigma=0, + mu=0, + mu_sigma=0, + material_name="Kelley", + f_n=100, + number_of_debye_poles=6, + plot=True, + save=False, + optimizer_options={ + "swarmsize": 30, + "maxiter": 100, + "omega": 0.5, + "phip": 1.4, + "phig": 1.4, + "minstep": 1e-8, + "minfun": 1e-8, + "seed": 111, + "pflag": True, + }, + ) setup.run() - setup = HavriliakNegami(f_min=1e7, f_max=1e11, - alpha=0.91, beta=0.45, - e_inf=2.7, de=8.6-2.7, tau_0=9.4e-10, - sigma=0, mu=0, mu_sigma=0, - material_name="Kelley", f_n=100, - number_of_debye_poles=6, - plot=True, save=False, - optimizer=DA_DLS, - optimizer_options={'seed': 111}) + setup = HavriliakNegami( + f_min=1e7, + f_max=1e11, + alpha=0.91, + beta=0.45, + e_inf=2.7, + de=8.6 - 2.7, + tau_0=9.4e-10, + sigma=0, + mu=0, + mu_sigma=0, + material_name="Kelley", + f_n=100, + number_of_debye_poles=6, + plot=True, + save=False, + optimizer=DA_DLS, + optimizer_options={"seed": 111}, + ) setup.run() - setup = HavriliakNegami(f_min=1e7, f_max=1e11, - alpha=0.91, beta=0.45, - e_inf=2.7, de=8.6-2.7, tau_0=9.4e-10, - sigma=0, mu=0, mu_sigma=0, - material_name="Kelley", f_n=100, - number_of_debye_poles=6, - plot=True, save=False, - optimizer=DE_DLS, - optimizer_options={'seed': 111}) + setup = HavriliakNegami( + f_min=1e7, + f_max=1e11, + alpha=0.91, + beta=0.45, + e_inf=2.7, + de=8.6 - 2.7, + tau_0=9.4e-10, + sigma=0, + mu=0, + mu_sigma=0, + material_name="Kelley", + f_n=100, + number_of_debye_poles=6, + plot=True, + save=False, + optimizer=DE_DLS, + optimizer_options={"seed": 111}, + ) setup.run() # Testing setup - setup = Rawdata("examples/Test.txt", 0.1, 1, 0.1, "M1", - number_of_debye_poles=3, plot=True, - optimizer_options={'seed': 111}) + setup = Rawdata( + "examples/Test.txt", 0.1, 1, 0.1, "M1", number_of_debye_poles=3, plot=True, optimizer_options={"seed": 111} + ) setup.run() np.random.seed(111) - setup = HavriliakNegami(1e12, 1e-3, 0.5, 1, 10, 5, - 1e-6, 0.1, 1, 0, "M2", - number_of_debye_poles=6, - plot=True) + setup = HavriliakNegami(1e12, 1e-3, 0.5, 1, 10, 5, 1e-6, 0.1, 1, 0, "M2", number_of_debye_poles=6, plot=True) setup.run() - setup = Jonscher(1e6, 1e-5, 50, 1, 1e5, 0.7, - 0.1, 1, 0.1, "M3", - number_of_debye_poles=4, - plot=True) + setup = Jonscher(1e6, 1e-5, 50, 1, 1e5, 0.7, 0.1, 1, 0.1, "M3", number_of_debye_poles=4, plot=True) setup.run() f = np.array([0.5, 0.5]) material1 = [3, 25, 1e6] material2 = [3, 0, 1e3] materials = np.array([material1, material2]) - setup = Crim(1*1e-1, 1e-9, 0.5, f, materials, 0.1, - 1, 0, "M4", number_of_debye_poles=2, - plot=True) + setup = Crim(1 * 1e-1, 1e-9, 0.5, f, materials, 0.1, 1, 0, "M4", number_of_debye_poles=2, plot=True) setup.run() diff --git a/toolboxes/DebyeFit/README.rst b/toolboxes/DebyeFit/README.rst index f92c220e..638ad447 100644 --- a/toolboxes/DebyeFit/README.rst +++ b/toolboxes/DebyeFit/README.rst @@ -23,7 +23,7 @@ The generic form of dispersive media is \epsilon(\omega) = \epsilon^{'}(\omega) - j\epsilon^{''}(\omega), -where :math:`\omega` is the angular frequency, :math:`\epsilon^{'}` and :math:`\epsilon^{''}` are the real and imaginary parts of the permittivity respectively. +where :math:`\omega` is the angular frequency, :math:`\epsilon^{'}` and :math:`\epsilon^{''}` are the real and imaginary parts of the permittivity respectively. This package provides scripts and tools which can be used to fit a multi-Debye expansion to dielectric data, defined as @@ -144,7 +144,7 @@ The ``CRIM`` class has the following structure: .. code-block:: none CRIM(f_min, f_max, a, volumetric_fractions, - materials, sigma, mu, mu_sigma, material_name, + materials, sigma, mu, mu_sigma, material_name, number_of_debye_poles=-1, f_n=50, plot=False, save=True, optimizer=PSO_DLS, @@ -245,4 +245,4 @@ The following code shows a basic example of how to use the Havriliak-Negami func 'seed': 111, 'pflag': True}) # run optimization - setup.run() \ No newline at end of file + setup.run() diff --git a/toolboxes/DebyeFit/examples/example_BiologicalTissues.ipynb b/toolboxes/DebyeFit/examples/example_BiologicalTissues.ipynb index abc387b5..8360778a 100644 --- a/toolboxes/DebyeFit/examples/example_BiologicalTissues.ipynb +++ b/toolboxes/DebyeFit/examples/example_BiologicalTissues.ipynb @@ -468,4 +468,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/toolboxes/DebyeFit/examples/example_ColeCole.py b/toolboxes/DebyeFit/examples/example_ColeCole.py index 2bcb1655..39898060 100644 --- a/toolboxes/DebyeFit/examples/example_ColeCole.py +++ b/toolboxes/DebyeFit/examples/example_ColeCole.py @@ -1,28 +1,41 @@ # I. Giannakis, A. Giannopoulos and N. Davidson, # "Incorporating dispersive electrical properties in FDTD GPR models -# using a general Cole-Cole dispersion function," +# using a general Cole-Cole dispersion function," # 2012 14th International Conference on Ground Penetrating Radar (GPR), 2012, pp. 232-236 import os, sys -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) + +sys.path.append(os.path.join(os.path.dirname(__file__), "..")) from Debye_Fit import HavriliakNegami if __name__ == "__main__": # set Havrilak-Negami function with initial parameters - setup = HavriliakNegami(f_min=1e4, f_max=1e11, - alpha=0.3, beta=1, - e_inf=3.4, de=2.7, tau_0=.8e-10, - sigma=0.45e-3, mu=1, mu_sigma=0, - material_name="dry_sand", f_n=100, - plot=True, save=False, - optimizer_options={'swarmsize':30, - 'maxiter':100, - 'omega':0.5, - 'phip':1.4, - 'phig':1.4, - 'minstep':1e-8, - 'minfun':1e-8, - 'seed': 111, - 'pflag': True}) + setup = HavriliakNegami( + f_min=1e4, + f_max=1e11, + alpha=0.3, + beta=1, + e_inf=3.4, + de=2.7, + tau_0=0.8e-10, + sigma=0.45e-3, + mu=1, + mu_sigma=0, + material_name="dry_sand", + f_n=100, + plot=True, + save=False, + optimizer_options={ + "swarmsize": 30, + "maxiter": 100, + "omega": 0.5, + "phip": 1.4, + "phig": 1.4, + "minstep": 1e-8, + "minfun": 1e-8, + "seed": 111, + "pflag": True, + }, + ) ### Dry Sand in case of 3, 5 # and automatically set number of Debye poles (-1) for number_of_debye_poles in [3, 5, -1]: @@ -31,12 +44,12 @@ if __name__ == "__main__": ### Moist sand # set Havrilak-Negami function parameters - setup.material_name="moist_sand" + setup.material_name = "moist_sand" setup.alpha = 0.25 setup.beta = 1 setup.e_inf = 5.6 setup.de = 3.3 - setup.tau_0 = 1.1e-10, + setup.tau_0 = (1.1e-10,) setup.sigma = 2e-3 # calculate for different number of Debye poles for number_of_debye_poles in [3, 5, -1]: diff --git a/toolboxes/DebyeFit/examples/example_DebyeFitting.ipynb b/toolboxes/DebyeFit/examples/example_DebyeFitting.ipynb index ce92b73d..9a3f17d0 100644 --- a/toolboxes/DebyeFit/examples/example_DebyeFitting.ipynb +++ b/toolboxes/DebyeFit/examples/example_DebyeFitting.ipynb @@ -768,4 +768,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/toolboxes/DebyeFit/optimization.py b/toolboxes/DebyeFit/optimization.py index a2828e37..342d880d 100644 --- a/toolboxes/DebyeFit/optimization.py +++ b/toolboxes/DebyeFit/optimization.py @@ -22,6 +22,7 @@ class Optimizer(object): unsigned integers (Default: None). :type seed: int, NoneType, optional """ + def __init__(self, maxiter=1000, seed=None): self.maxiter = maxiter self.seed = seed @@ -58,12 +59,11 @@ class Optimizer(object): # find the weights using a calc_weights method if self.calc_weights is None: raise NotImplementedError() - _, _, weights, ee, rl_exp, im_exp = \ - self.calc_weights(tau, **funckwargs) + _, _, weights, ee, rl_exp, im_exp = self.calc_weights(tau, **funckwargs) return tau, weights, ee, rl_exp, im_exp def calc_relaxation_times(self): - """ Optimisation method that tries to find an optimal set + """Optimisation method that tries to find an optimal set of relaxation times that minimise the error between the actual and the approximated electric permittivity. """ @@ -93,7 +93,7 @@ class Optimizer(object): class PSO_DLS(Optimizer): - """ Create hybrid Particle Swarm-Damped Least Squares optimisation + """Create hybrid Particle Swarm-Damped Least Squares optimisation object with predefined parameters. :param swarmsize: The number of particles in the swarm (Default: 40). @@ -119,11 +119,10 @@ class PSO_DLS(Optimizer): value during optimization process (Default: False). :type pflag: bool, optional """ - def __init__(self, swarmsize=40, maxiter=50, - omega=0.9, phip=0.9, phig=0.9, - minstep=1e-8, minfun=1e-8, - pflag=False, seed=None): + def __init__( + self, swarmsize=40, maxiter=50, omega=0.9, phip=0.9, phig=0.9, minstep=1e-8, minfun=1e-8, pflag=False, seed=None + ): super(PSO_DLS, self).__init__(maxiter, seed) self.swarmsize = swarmsize self.omega = omega @@ -156,13 +155,11 @@ class PSO_DLS(Optimizer): """ np.random.seed(self.seed) # check input parameters - assert len(lb) == len(ub), \ - 'Lower- and upper-bounds must be the same length' - assert hasattr(func, '__call__'), 'Invalid function handle' + assert len(lb) == len(ub), "Lower- and upper-bounds must be the same length" + assert hasattr(func, "__call__"), "Invalid function handle" lb = np.array(lb) ub = np.array(ub) - assert np.all(ub > lb), \ - 'All upper-bound values must be greater than lower-bound values' + assert np.all(ub > lb), "All upper-bound values must be greater than lower-bound values" vhigh = np.abs(ub - lb) vlow = -vhigh @@ -200,14 +197,16 @@ class PSO_DLS(Optimizer): v[i, :] = vlow + np.random.rand(d) * (vhigh - vlow) # Iterate until termination criterion met - for it in tqdm(range(self.maxiter), desc='Debye fitting'): + for it in tqdm(range(self.maxiter), desc="Debye fitting"): rp = np.random.uniform(size=(self.swarmsize, d)) rg = np.random.uniform(size=(self.swarmsize, d)) for i in range(self.swarmsize): # Update the particle's velocity - v[i, :] = self.omega * v[i, :] + self.phip * rp[i, :] * \ - (p[i, :] - x[i, :]) + \ - self.phig * rg[i, :] * (g - x[i, :]) + v[i, :] = ( + self.omega * v[i, :] + + self.phip * rp[i, :] * (p[i, :] - x[i, :]) + + self.phig * rg[i, :] * (g - x[i, :]) + ) # Update the particle's position, # correcting lower and upper bound # violations, then update the objective function value @@ -227,12 +226,10 @@ class PSO_DLS(Optimizer): tmp = x[i, :].copy() stepsize = np.sqrt(np.sum((g - tmp) ** 2)) if np.abs(fg - fx) <= self.minfun: - print(f'Stopping search: Swarm best objective ' - f'change less than {self.minfun}') + print(f"Stopping search: Swarm best objective " f"change less than {self.minfun}") return tmp, fx elif stepsize <= self.minstep: - print(f'Stopping search: Swarm best position ' - f'change less than {self.minstep}') + print(f"Stopping search: Swarm best position " f"change less than {self.minstep}") return tmp, fx else: g = tmp.copy() @@ -261,11 +258,9 @@ class PSO_DLS(Optimizer): # it clears an axes plt.cla() plt.plot(x, y, "b-", linewidth=1.0) - plt.ylim(min(y) - 0.1 * min(y), - max(y) + 0.1 * max(y)) + plt.ylim(min(y) - 0.1 * min(y), max(y) + 0.1 * max(y)) plt.xlim(min(x) - 0.1, max(x) + 0.1) - plt.grid(b=True, which="major", color="k", - linewidth=0.2, linestyle="--") + plt.grid(b=True, which="major", color="k", linewidth=0.2, linestyle="--") plt.suptitle("Debye fitting process") plt.xlabel("Iteration") plt.ylabel("Average Error") @@ -273,17 +268,27 @@ class PSO_DLS(Optimizer): class DA_DLS(Optimizer): - """ Create Dual Annealing object with predefined parameters. + """Create Dual Annealing object with predefined parameters. The current class is a modified edition of the scipy.optimize package which can be found at: https://docs.scipy.org/doc/scipy/reference/generated/ scipy.optimize.dual_annealing.html#scipy.optimize.dual_annealing """ - def __init__(self, maxiter=1000, - local_search_options={}, initial_temp=5230.0, - restart_temp_ratio=2e-05, visit=2.62, accept=-5.0, - maxfun=1e7, no_local_search=False, - callback=None, x0=None, seed=None): + + def __init__( + self, + maxiter=1000, + local_search_options={}, + initial_temp=5230.0, + restart_temp_ratio=2e-05, + visit=2.62, + accept=-5.0, + maxfun=1e7, + no_local_search=False, + callback=None, + x0=None, + seed=None, + ): super(DA_DLS, self).__init__(maxiter, seed) self.local_search_options = local_search_options self.initial_temp = initial_temp @@ -330,7 +335,8 @@ class DA_DLS(Optimizer): maxfun=self.maxfun, no_local_search=self.no_local_search, callback=self.callback, - x0=self.x0) + x0=self.x0, + ) print(result.message) return result.x, result.fun @@ -344,12 +350,25 @@ class DE_DLS(Optimizer): https://docs.scipy.org/doc/scipy/reference/generated/ scipy.optimize.differential_evolution.html#scipy.optimize.differential_evolution """ - def __init__(self, maxiter=1000, - strategy='best1bin', popsize=15, tol=0.01, mutation=(0.5, 1), - recombination=0.7, callback=None, disp=False, polish=True, - init='latinhypercube', atol=0, - updating='immediate', workers=1, - constraints=(), seed=None): + + def __init__( + self, + maxiter=1000, + strategy="best1bin", + popsize=15, + tol=0.01, + mutation=(0.5, 1), + recombination=0.7, + callback=None, + disp=False, + polish=True, + init="latinhypercube", + atol=0, + updating="immediate", + workers=1, + constraints=(), + seed=None, + ): super(DE_DLS, self).__init__(maxiter, seed) self.strategy = strategy self.popsize = popsize @@ -403,7 +422,8 @@ class DE_DLS(Optimizer): atol=self.atol, updating=self.updating, workers=self.workers, - constraints=self.constraints) + constraints=self.constraints, + ) print(result.message) return result.x, result.fun @@ -446,17 +466,15 @@ def DLS(logt, rl, im, freq): # Here they are transformed back t0=10**logt tt = 10**logt # y = Ax, here the A matrix for the real and the imaginary part is builded - d = 1 / (1 + 1j * 2 * np.pi * np.repeat( - freq, len(tt)).reshape((-1, len(tt))) * tt) + d = 1 / (1 + 1j * 2 * np.pi * np.repeat(freq, len(tt)).reshape((-1, len(tt))) * tt) # Adding dumping (Levenberg–Marquardt algorithm) # Solving the overdetermined system y=Ax x = np.abs(np.linalg.lstsq(d.imag, im, rcond=None)[0]) # x - absolute damped least-squares solution - rp, ip = np.matmul(d.real, x[np.newaxis].T).T[0], np.matmul( - d.imag, x[np.newaxis].T).T[0] - cost_i = np.sum(np.abs(ip-im))/len(im) + rp, ip = np.matmul(d.real, x[np.newaxis].T).T[0], np.matmul(d.imag, x[np.newaxis].T).T[0] + cost_i = np.sum(np.abs(ip - im)) / len(im) ee = np.mean(rl - rp) if ee < 1: ee = 1 - cost_r = np.sum(np.abs(rp + ee - rl))/len(im) + cost_r = np.sum(np.abs(rp + ee - rl)) / len(im) return cost_i, cost_r, x, ee, rp, ip diff --git a/toolboxes/DebyeFit/optimization.rst b/toolboxes/DebyeFit/optimization.rst index f53b8499..920e3e28 100644 --- a/toolboxes/DebyeFit/optimization.rst +++ b/toolboxes/DebyeFit/optimization.rst @@ -12,7 +12,7 @@ Supported methods: Methods ^^^^^^^ -1. __constructor__ - is called in all child classes. +1. __constructor__ - is called in all child classes. It takes the following arguments: - `maxiter`: maximum number of iterations for the optimizer, diff --git a/toolboxes/GPRAntennaModels/GSSI.py b/toolboxes/GPRAntennaModels/GSSI.py index febd2f6e..7f396a7d 100644 --- a/toolboxes/GPRAntennaModels/GSSI.py +++ b/toolboxes/GPRAntennaModels/GSSI.py @@ -25,7 +25,7 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs): centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction. resolution (float): Spatial resolution for the antenna model. - kwargs (dict): Optional variables, e.g. can be fed from an optimisation + kwargs (dict): Optional variables, e.g. can be fed from an optimisation process. Returns: @@ -64,124 +64,171 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs): patchheight = 0.016 tx = x + 0.114, y + 0.052, z + skidthickness else: - logger.exception('This antenna module can only be used with a spatial discretisation of 1mm or 2mm') + logger.exception("This antenna module can only be used with a spatial discretisation of 1mm or 2mm") raise ValueError # If using parameters from an optimisation - try: + try: kwargs - absorber1Er = kwargs['absorber1Er'] - absorber1sig = kwargs['absorber1sig'] - absorber2Er = kwargs['absorber2Er'] - absorber2sig = kwargs['absorber2sig'] - pcbEr = kwargs['pcbEr'] - pcbsig = kwargs['pcbsig'] - hdpeEr = kwargs['hdpeEr'] - hdpesig = kwargs['hdpesig'] + absorber1Er = kwargs["absorber1Er"] + absorber1sig = kwargs["absorber1sig"] + absorber2Er = kwargs["absorber2Er"] + absorber2sig = kwargs["absorber2sig"] + pcbEr = kwargs["pcbEr"] + pcbsig = kwargs["pcbsig"] + hdpeEr = kwargs["hdpeEr"] + hdpesig = kwargs["hdpesig"] sourceresistance = 195 rxres = 50 - absorber1 = gprMax.Material(er=absorber1Er, se=absorber1sig, mr=1, sm=0, id='absorber1') - absorber2 = gprMax.Material(er=absorber2Er, se=absorber2sig, mr=1, sm=0, id='absorber2') - pcb = gprMax.Material(er=pcbEr, se=pcbsig, mr=1, sm=0, id='pcb') - hdpe = gprMax.Material(er=hdpeEr, se=hdpesig, mr=1, sm=0, id='hdpe') + absorber1 = gprMax.Material(er=absorber1Er, se=absorber1sig, mr=1, sm=0, id="absorber1") + absorber2 = gprMax.Material(er=absorber2Er, se=absorber2sig, mr=1, sm=0, id="absorber2") + pcb = gprMax.Material(er=pcbEr, se=pcbsig, mr=1, sm=0, id="pcb") + hdpe = gprMax.Material(er=hdpeEr, se=hdpesig, mr=1, sm=0, id="hdpe") scene_objects.extend((absorber1, absorber2, pcb, hdpe)) # Otherwise choose parameters for different optimisation models except: # Specify optimisation model - optstate = ['WarrenThesis', 'DebyeAbsorber', 'GiannakisPaper'] + optstate = ["WarrenThesis", "DebyeAbsorber", "GiannakisPaper"] optstate = optstate[0] - if optstate == 'WarrenThesis': + if optstate == "WarrenThesis": # Original optimised values from http://hdl.handle.net/1842/4074 excitationfreq = 1.71e9 sourceresistance = 230 # Correction for old (< 123) GprMax3D bug (optimised to 4) rxres = 925 # Resistance at Rx bowtie - absorber1 = gprMax.Material(er=1.58, se=0.428, mr=1, sm=0, id='absorber1') - absorber2 = gprMax.Material(er=3, se=0, mr=1, sm=0, id='absorber2') # Foam modelled as PCB material - pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id='pcb') - hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id='hdpe') - rxres = gprMax.Material(er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id='rxres') + absorber1 = gprMax.Material(er=1.58, se=0.428, mr=1, sm=0, id="absorber1") + absorber2 = gprMax.Material(er=3, se=0, mr=1, sm=0, id="absorber2") # Foam modelled as PCB material + pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id="pcb") + hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id="hdpe") + rxres = gprMax.Material(er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id="rxres") scene_objects.extend((absorber1, absorber2, pcb, hdpe, rxres)) - elif optstate == 'DebyeAbsorber': - # Same values as WarrenThesis but uses dispersive absorber properties for Eccosorb LS22 + elif optstate == "DebyeAbsorber": + # Same values as WarrenThesis but uses dispersive absorber properties for Eccosorb LS22 excitationfreq = 1.71e9 sourceresistance = 230 # Correction for old (< 123) GprMax3D bug (optimised to 4) rxres = 925 # Resistance at Rx bowtie - absorber1 = gprMax.Material(er=1, se=0, mr=1, sm=0, id='absorber1') + absorber1 = gprMax.Material(er=1, se=0, mr=1, sm=0, id="absorber1") # Eccosorb LS22 3-pole Debye model (https://bitbucket.org/uoyaeg/aegboxts/wiki/Home) - absorber1_disp = gprMax.AddDebyeDispersion(poles=3, er_delta=[3.7733, 3.14418, 20.2441], - tau=[1.00723e-11, 1.55686e-10, 3.44129e-10], - material_ids=['absorber1']) - absorber2 = gprMax.Material(er=3, se=0, mr=1, sm=0, id='absorber2') # Foam modelled as PCB material - pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id='pcb') - hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id='hdpe') - rxres = gprMax.Material(er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id='rxres') + absorber1_disp = gprMax.AddDebyeDispersion( + poles=3, + er_delta=[3.7733, 3.14418, 20.2441], + tau=[1.00723e-11, 1.55686e-10, 3.44129e-10], + material_ids=["absorber1"], + ) + absorber2 = gprMax.Material(er=3, se=0, mr=1, sm=0, id="absorber2") # Foam modelled as PCB material + pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id="pcb") + hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id="hdpe") + rxres = gprMax.Material(er=3, se=(1 / rxres) * (dy / (dx * dz)), mr=1, sm=0, id="rxres") scene_objects.extend((absorber1, absorber1_disp, absorber2, pcb, hdpe, rxres)) - elif optstate == 'GiannakisPaper': + elif optstate == "GiannakisPaper": # Further optimised values from https://doi.org/10.1109/TGRS.2018.2869027 sourceresistance = 195 - absorber1 = gprMax.Material(er=3.96, se=0.31, mr=1, sm=0, id='absorber1') - absorber2 = gprMax.Material(er=1.05, se=1.01, mr=1, sm=0, id='absorber2') - pcb = gprMax.Material(er=1.37, se=0.0002, mr=1, sm=0, id='pcb') - hdpe = gprMax.Material(er=1.99, se=0.013, mr=1, sm=0, id='hdpe') + absorber1 = gprMax.Material(er=3.96, se=0.31, mr=1, sm=0, id="absorber1") + absorber2 = gprMax.Material(er=1.05, se=1.01, mr=1, sm=0, id="absorber2") + pcb = gprMax.Material(er=1.37, se=0.0002, mr=1, sm=0, id="pcb") + hdpe = gprMax.Material(er=1.99, se=0.013, mr=1, sm=0, id="hdpe") scene_objects.extend((absorber1, absorber2, pcb, hdpe)) # Antenna geometry # Plastic case - b1 = gprMax.Box(p1=(x, y, z + skidthickness), - p2=(x + casesize[0], y + casesize[1], z + skidthickness + casesize[2]), - material_id='hdpe') - b2 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness), - p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, - z + skidthickness + casesize[2] - casethickness), - material_id='free_space') + b1 = gprMax.Box( + p1=(x, y, z + skidthickness), + p2=(x + casesize[0], y + casesize[1], z + skidthickness + casesize[2]), + material_id="hdpe", + ) + b2 = gprMax.Box( + p1=(x + casethickness, y + casethickness, z + skidthickness), + p2=( + x + casesize[0] - casethickness, + y + casesize[1] - casethickness, + z + skidthickness + casesize[2] - casethickness, + ), + material_id="free_space", + ) # Metallic enclosure - b3 = gprMax.Box(p1=(x + 0.025, y + casethickness, z + skidthickness), - p2=(x + casesize[0] - 0.025, y + casesize[1] - casethickness, - z + skidthickness + 0.027), material_id='pec') + b3 = gprMax.Box( + p1=(x + 0.025, y + casethickness, z + skidthickness), + p2=(x + casesize[0] - 0.025, y + casesize[1] - casethickness, z + skidthickness + 0.027), + material_id="pec", + ) # Absorber material (absorber1) and foam (absorber2) around edge of absorber - b4 = gprMax.Box(p1=(x + 0.025 + shieldthickness, y + casethickness + shieldthickness, - z + skidthickness), p2=(x + 0.025 + shieldthickness + 0.057, - y + casesize[1] - casethickness - shieldthickness, - z + skidthickness + 0.027 - shieldthickness - 0.001), - material_id='absorber2') - b5 = gprMax.Box(p1=(x + 0.025 + shieldthickness + foamsurroundthickness, - y + casethickness + shieldthickness + foamsurroundthickness, - z + skidthickness), - p2=(x + 0.025 + shieldthickness + 0.057 - foamsurroundthickness, - y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, - z + skidthickness + 0.027 - shieldthickness), - material_id='absorber1') - b6 = gprMax.Box(p1=(x + 0.086, y + casethickness + shieldthickness, z + skidthickness), - p2=(x + 0.086 + 0.057, y + casesize[1] - casethickness - shieldthickness, - z + skidthickness + 0.027 - shieldthickness - 0.001), - material_id='absorber2') - b7 = gprMax.Box(p1=(x + 0.086 + foamsurroundthickness, - y + casethickness + shieldthickness + foamsurroundthickness, - z + skidthickness), - p2=(x + 0.086 + 0.057 - foamsurroundthickness, - y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, - z + skidthickness + 0.027 - shieldthickness), - material_id='absorber1') + b4 = gprMax.Box( + p1=(x + 0.025 + shieldthickness, y + casethickness + shieldthickness, z + skidthickness), + p2=( + x + 0.025 + shieldthickness + 0.057, + y + casesize[1] - casethickness - shieldthickness, + z + skidthickness + 0.027 - shieldthickness - 0.001, + ), + material_id="absorber2", + ) + b5 = gprMax.Box( + p1=( + x + 0.025 + shieldthickness + foamsurroundthickness, + y + casethickness + shieldthickness + foamsurroundthickness, + z + skidthickness, + ), + p2=( + x + 0.025 + shieldthickness + 0.057 - foamsurroundthickness, + y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, + z + skidthickness + 0.027 - shieldthickness, + ), + material_id="absorber1", + ) + b6 = gprMax.Box( + p1=(x + 0.086, y + casethickness + shieldthickness, z + skidthickness), + p2=( + x + 0.086 + 0.057, + y + casesize[1] - casethickness - shieldthickness, + z + skidthickness + 0.027 - shieldthickness - 0.001, + ), + material_id="absorber2", + ) + b7 = gprMax.Box( + p1=( + x + 0.086 + foamsurroundthickness, + y + casethickness + shieldthickness + foamsurroundthickness, + z + skidthickness, + ), + p2=( + x + 0.086 + 0.057 - foamsurroundthickness, + y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, + z + skidthickness + 0.027 - shieldthickness, + ), + material_id="absorber1", + ) # PCB - b8 = gprMax.Box(p1=(x + 0.025 + shieldthickness + foamsurroundthickness, - y + casethickness + shieldthickness + foamsurroundthickness, - z + skidthickness), - p2=(x + 0.086 - shieldthickness - foamsurroundthickness, - y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, - z + skidthickness + pcbthickness), material_id='pcb') - b9 = gprMax.Box(p1=(x + 0.086 + foamsurroundthickness, - y + casethickness + shieldthickness + foamsurroundthickness, - z + skidthickness), - p2=(x + 0.086 + 0.057 - foamsurroundthickness, - y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, - z + skidthickness + pcbthickness), material_id='pcb') + b8 = gprMax.Box( + p1=( + x + 0.025 + shieldthickness + foamsurroundthickness, + y + casethickness + shieldthickness + foamsurroundthickness, + z + skidthickness, + ), + p2=( + x + 0.086 - shieldthickness - foamsurroundthickness, + y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) + b9 = gprMax.Box( + p1=( + x + 0.086 + foamsurroundthickness, + y + casethickness + shieldthickness + foamsurroundthickness, + z + skidthickness, + ), + p2=( + x + 0.086 + 0.057 - foamsurroundthickness, + y + casesize[1] - casethickness - shieldthickness - foamsurroundthickness, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) scene_objects.extend((b1, b2, b3, b4, b5, b6, b7, b8, b9)) @@ -191,143 +238,183 @@ def antenna_like_GSSI_1500(x, y, z, resolution=0.001, **kwargs): a = 0 b = 0 while b < 13: - p1 = gprMax.Plate(p1=(x + 0.045 + a * dx, y + 0.039 + b * dx, z + skidthickness), - p2=(x + 0.065 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness), - material_id='pec') - p2 = gprMax.Plate(p1=(x + 0.045 + a * dx, y + 0.067 - b * dx, z + skidthickness), - p2=(x + 0.065 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness), - material_id='pec') - p3 = gprMax.Plate(p1=(x + 0.104 + a * dx, y + 0.039 + b * dx, z + skidthickness), - p2=(x + 0.124 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness), - material_id='pec') - p4 = gprMax.Plate(p1=(x + 0.104 + a * dx, y + 0.067 - b * dx, z + skidthickness), - p2=(x + 0.124 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness), - material_id='pec') + p1 = gprMax.Plate( + p1=(x + 0.045 + a * dx, y + 0.039 + b * dx, z + skidthickness), + p2=(x + 0.065 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness), + material_id="pec", + ) + p2 = gprMax.Plate( + p1=(x + 0.045 + a * dx, y + 0.067 - b * dx, z + skidthickness), + p2=(x + 0.065 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness), + material_id="pec", + ) + p3 = gprMax.Plate( + p1=(x + 0.104 + a * dx, y + 0.039 + b * dx, z + skidthickness), + p2=(x + 0.124 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness), + material_id="pec", + ) + p4 = gprMax.Plate( + p1=(x + 0.104 + a * dx, y + 0.067 - b * dx, z + skidthickness), + p2=(x + 0.124 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness), + material_id="pec", + ) scene_objects.extend((p1, p2, p3, p4)) b += 1 if a == 2 or a == 4 or a == 7: - p5 = gprMax.Plate(p1=(x + 0.045 + a * dx, y + 0.039 + b * dx, z + skidthickness), - p2=(x + 0.065 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness), - material_id='pec') - p6 = gprMax.Plate(p1=(x + 0.045 + a * dx, y + 0.067 - b * dx, z + skidthickness), - p2=(x + 0.065 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness), - material_id='pec') - p7 = gprMax.Plate(p1=(x + 0.104 + a * dx, y + 0.039 + b * dx, z + skidthickness), - p2=(x + 0.124 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness), - material_id='pec') - p8 = gprMax.Plate(p1=(x + 0.104 + a * dx, y + 0.067 - b * dx, z + skidthickness), - p2=(x + 0.124 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness), - material_id='pec') + p5 = gprMax.Plate( + p1=(x + 0.045 + a * dx, y + 0.039 + b * dx, z + skidthickness), + p2=(x + 0.065 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness), + material_id="pec", + ) + p6 = gprMax.Plate( + p1=(x + 0.045 + a * dx, y + 0.067 - b * dx, z + skidthickness), + p2=(x + 0.065 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness), + material_id="pec", + ) + p7 = gprMax.Plate( + p1=(x + 0.104 + a * dx, y + 0.039 + b * dx, z + skidthickness), + p2=(x + 0.124 - a * dx, y + 0.039 + b * dx + dy, z + skidthickness), + material_id="pec", + ) + p8 = gprMax.Plate( + p1=(x + 0.104 + a * dx, y + 0.067 - b * dx, z + skidthickness), + p2=(x + 0.124 - a * dx, y + 0.067 - b * dx + dy, z + skidthickness), + material_id="pec", + ) b += 1 scene_objects.extend((p5, p6, p7, p8)) a += 1 # Rx extension section (upper y) - p9 = gprMax.Plate(p1=(x + 0.044, y + 0.068, z + skidthickness), - p2=(x + 0.044 + bowtiebase, y + 0.068 + patchheight, z + skidthickness), - material_id='pec') + p9 = gprMax.Plate( + p1=(x + 0.044, y + 0.068, z + skidthickness), + p2=(x + 0.044 + bowtiebase, y + 0.068 + patchheight, z + skidthickness), + material_id="pec", + ) # Tx extension section (upper y) - p10 = gprMax.Plate(p1=(x + 0.103, y + 0.068, z + skidthickness), - p2=(x + 0.103 + bowtiebase, y + 0.068 + patchheight, z + skidthickness), - material_id='pec') + p10 = gprMax.Plate( + p1=(x + 0.103, y + 0.068, z + skidthickness), + p2=(x + 0.103 + bowtiebase, y + 0.068 + patchheight, z + skidthickness), + material_id="pec", + ) scene_objects.extend((p9, p10)) # Edges that represent wire between bowtie halves in 1mm model - e1 = gprMax.Edge(p1=(tx[0] - 0.059, tx[1] - dy, tx[2]), - p2=(tx[0] - 0.059, tx[1], tx[2]), material_id='pec') - e2 = gprMax.Edge(p1=(tx[0] - 0.059, tx[1] + dy, tx[2]), - p2=(tx[0] - 0.059, tx[1] + 0.002, tx[2]), material_id='pec') - e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), - p2=(tx[0], tx[1], tx[2]), material_id='pec') - e4 = gprMax.Edge(p1=(tx[0], tx[1] + dz, tx[2]), - p2=(tx[0], tx[1] + 0.002, tx[2]), material_id='pec') + e1 = gprMax.Edge(p1=(tx[0] - 0.059, tx[1] - dy, tx[2]), p2=(tx[0] - 0.059, tx[1], tx[2]), material_id="pec") + e2 = gprMax.Edge( + p1=(tx[0] - 0.059, tx[1] + dy, tx[2]), p2=(tx[0] - 0.059, tx[1] + 0.002, tx[2]), material_id="pec" + ) + e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec") + e4 = gprMax.Edge(p1=(tx[0], tx[1] + dz, tx[2]), p2=(tx[0], tx[1] + 0.002, tx[2]), material_id="pec") scene_objects.extend((e1, e2, e3, e4)) elif resolution == 0.002: # Rx & Tx bowties for a in range(0, 6): - p1 = gprMax.Plate(p1=(x + 0.044 + a * dx, y + 0.040 + a * dx, z + skidthickness), - p2=(x + 0.066 - a * dx, y + 0.040 + a * dx + dy, z + skidthickness), - material_id='pec') - p2 = gprMax.Plate(p1=(x + 0.044 + a * dx, y + 0.064 - a * dx, z + skidthickness), - p2=(x + 0.066 - a * dx, y + 0.064 - a * dx + dy, z + skidthickness), - material_id='pec') - p3 = gprMax.Plate(p1=(x + 0.103 + a * dx, y + 0.040 + a * dx, z + skidthickness), - p2=(x + 0.125 - a * dx, y + 0.040 + a * dx + dy, z + skidthickness), - material_id='pec') - p4 = gprMax.Plate(p1=(x + 0.103 + a * dx, y + 0.064 - a * dx, z + skidthickness), - p2=(x + 0.125 - a * dx, y + 0.064 - a * dx + dy, z + skidthickness), - material_id='pec') + p1 = gprMax.Plate( + p1=(x + 0.044 + a * dx, y + 0.040 + a * dx, z + skidthickness), + p2=(x + 0.066 - a * dx, y + 0.040 + a * dx + dy, z + skidthickness), + material_id="pec", + ) + p2 = gprMax.Plate( + p1=(x + 0.044 + a * dx, y + 0.064 - a * dx, z + skidthickness), + p2=(x + 0.066 - a * dx, y + 0.064 - a * dx + dy, z + skidthickness), + material_id="pec", + ) + p3 = gprMax.Plate( + p1=(x + 0.103 + a * dx, y + 0.040 + a * dx, z + skidthickness), + p2=(x + 0.125 - a * dx, y + 0.040 + a * dx + dy, z + skidthickness), + material_id="pec", + ) + p4 = gprMax.Plate( + p1=(x + 0.103 + a * dx, y + 0.064 - a * dx, z + skidthickness), + p2=(x + 0.125 - a * dx, y + 0.064 - a * dx + dy, z + skidthickness), + material_id="pec", + ) # Rx extension section (upper y) - p5 = gprMax.Plate(p1=(x + 0.044, y + 0.066, z + skidthickness), - p2=(x + 0.044 + bowtiebase, y + 0.066 + patchheight, z + skidthickness), - material_id='pec') + p5 = gprMax.Plate( + p1=(x + 0.044, y + 0.066, z + skidthickness), + p2=(x + 0.044 + bowtiebase, y + 0.066 + patchheight, z + skidthickness), + material_id="pec", + ) # Tx extension section (upper y) - p6 = gprMax.Plate(p1=(x + 0.103, y + 0.066, z + skidthickness), - p2=(x + 0.103 + bowtiebase, y + 0.066 + patchheight, z + skidthickness), - material_id='pec') + p6 = gprMax.Plate( + p1=(x + 0.103, y + 0.066, z + skidthickness), + p2=(x + 0.103 + bowtiebase, y + 0.066 + patchheight, z + skidthickness), + material_id="pec", + ) scene_objects.extend((p1, p2, p3, p4, p5, p6)) # Rx extension section (lower y) - p11 = gprMax.Plate(p1=(x + 0.044, y + 0.024, z + skidthickness), - p2=(x + 0.044 + bowtiebase, y + 0.024 + patchheight, z + skidthickness), - material_id='pec') + p11 = gprMax.Plate( + p1=(x + 0.044, y + 0.024, z + skidthickness), + p2=(x + 0.044 + bowtiebase, y + 0.024 + patchheight, z + skidthickness), + material_id="pec", + ) # Tx extension section (lower y) - p12 = gprMax.Plate(p1=(x + 0.103, y + 0.024, z + skidthickness), - p2=(x + 0.103 + bowtiebase, y + 0.024 + patchheight, z + skidthickness), - material_id='pec') + p12 = gprMax.Plate( + p1=(x + 0.103, y + 0.024, z + skidthickness), + p2=(x + 0.103 + bowtiebase, y + 0.024 + patchheight, z + skidthickness), + material_id="pec", + ) scene_objects.extend((p11, p12)) # Skid - b10 = gprMax.Box(p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + skidthickness), - material_id='hdpe') + b10 = gprMax.Box(p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + skidthickness), material_id="hdpe") scene_objects.append(b10) # Geometry views - gv1 = gprMax.GeometryView(p1=(x - dx, y - dy, z - dz), p2=(x + casesize[0] + dx, - y + casesize[1] + dy, z + skidthickness + casesize[2] + dz), - dl=(dx, dy, dz), filename='antenna_like_GSSI_1500', - output_type='n') - gv2 = gprMax.GeometryView(p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + 0.010), - dl=(dx, dy, dz), filename='antenna_like_GSSI_1500_pcb', - output_type='f') + gv1 = gprMax.GeometryView( + p1=(x - dx, y - dy, z - dz), + p2=(x + casesize[0] + dx, y + casesize[1] + dy, z + skidthickness + casesize[2] + dz), + dl=(dx, dy, dz), + filename="antenna_like_GSSI_1500", + output_type="n", + ) + gv2 = gprMax.GeometryView( + p1=(x, y, z), + p2=(x + casesize[0], y + casesize[1], z + 0.010), + dl=(dx, dy, dz), + filename="antenna_like_GSSI_1500_pcb", + output_type="f", + ) # scene_objects.extend((gv1, gv2)) # Excitation - if optstate == 'WarrenThesis' or optstate == 'DebyeAbsorber': + if optstate == "WarrenThesis" or optstate == "DebyeAbsorber": # Gaussian pulse - w1 = gprMax.Waveform(wave_type='gaussian', amp=1, - freq=excitationfreq, id='my_gaussian') - vs1 = gprMax.VoltageSource(polarisation='y', p1=(tx[0], tx[1], tx[2]), - resistance=sourceresistance, waveform_id='my_gaussian') + w1 = gprMax.Waveform(wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian") + vs1 = gprMax.VoltageSource( + polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_gaussian" + ) scene_objects.extend((w1, vs1)) - elif optstate == 'GiannakisPaper': + elif optstate == "GiannakisPaper": # Optimised custom pulse - exc1 = gprMax.ExcitationFile(filepath='user_libs/GPRAntennaModels/GSSI1p5optpulse.txt', - kind='linear', fill_value='extrapolate') - vs1 = gprMax.VoltageSource(polarisation='y', p1=(tx[0], tx[1], tx[2]), - resistance=sourceresistance, waveform_id='GSSI1p5optpulse') + exc1 = gprMax.ExcitationFile( + filepath="user_libs/GPRAntennaModels/GSSI1p5optpulse.txt", kind="linear", fill_value="extrapolate" + ) + vs1 = gprMax.VoltageSource( + polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="GSSI1p5optpulse" + ) scene_objects.extend((exc1, vs1)) # Output point - receiver bowtie if resolution == 0.001: - if optstate == 'WarrenThesis' or optstate == 'DebyeAbsorber': - e1 = gprMax.Edge(p1=(tx[0] - 0.059, tx[1], tx[2]), - p2=(tx[0] - 0.059, tx[1] + dy, tx[2]), - material_id='rxres') + if optstate == "WarrenThesis" or optstate == "DebyeAbsorber": + e1 = gprMax.Edge( + p1=(tx[0] - 0.059, tx[1], tx[2]), p2=(tx[0] - 0.059, tx[1] + dy, tx[2]), material_id="rxres" + ) scene_objects.append(e1) - r1 = gprMax.Rx(p1=(tx[0] - 0.059, tx[1], tx[2]), id='rxbowtie', - outputs='Ey') + r1 = gprMax.Rx(p1=(tx[0] - 0.059, tx[1], tx[2]), id="rxbowtie", outputs="Ey") scene_objects.append(r1) elif resolution == 0.002: - if optstate == 'WarrenThesis' or optstate == 'DebyeAbsorber': - e1 = gprMax.Edge(p1=(tx[0] - 0.060, tx[1], tx[2]), - p2=(tx[0] - 0.060, tx[1] + dy, tx[2]), - material_id='rxres') + if optstate == "WarrenThesis" or optstate == "DebyeAbsorber": + e1 = gprMax.Edge( + p1=(tx[0] - 0.060, tx[1], tx[2]), p2=(tx[0] - 0.060, tx[1] + dy, tx[2]), material_id="rxres" + ) scene_objects.append(e1) - r1 = gprMax.Rx(p1=(tx[0] - 0.060, tx[1], tx[2]), id='rxbowtie', - outputs='Ey') + r1 = gprMax.Rx(p1=(tx[0] - 0.060, tx[1], tx[2]), id="rxbowtie", outputs="Ey") scene_objects.append(r1) return scene_objects @@ -347,7 +434,7 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.001, **kwargs): centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction. resolution (float): Spatial resolution for the antenna model. - kwargs (dict): Optional variables, e.g. can be fed from an optimisation + kwargs (dict): Optional variables, e.g. can be fed from an optimisation process. Returns: @@ -358,19 +445,19 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.001, **kwargs): scene_objects = [] # Antenna geometry properties - casesize = (0.3, 0.3, 0.178) # original + casesize = (0.3, 0.3, 0.178) # original casethickness = 0.002 shieldthickness = 0.002 foamsurroundthickness = 0.003 pcbthickness = 0.002 bowtiebase = 0.06 - bowtieheight = 0.06 # original 0.056 - patchheight = 0.06 # original 0.056 + bowtieheight = 0.06 # original 0.056 + patchheight = 0.06 # original 0.056 metalboxheight = 0.089 metalmiddleplateheight = 0.11 - smooth_dec = 'yes' # choose to use dielectric smoothing or not - src_type = 'voltage_source' # source type. "voltage_source" or "transmission_line" + smooth_dec = "yes" # choose to use dielectric smoothing or not + src_type = "voltage_source" # source type. "voltage_source" or "transmission_line" pcber = 2.35 hdper = 2.35 skidthickness = 0.01 @@ -378,20 +465,20 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.001, **kwargs): # If using parameters from an optimisation try: kwargs - excitationfreq = kwargs['excitationfreq'] - sourceresistance = kwargs['sourceresistance'] + excitationfreq = kwargs["excitationfreq"] + sourceresistance = kwargs["sourceresistance"] receiverresistance = sourceresistance - absorberEr = kwargs['absorberEr'] - absorbersig = kwargs['absorbersig'] + absorberEr = kwargs["absorberEr"] + absorbersig = kwargs["absorbersig"] # Otherwise choose pre-set optimised parameters except: - excitationfreq = 0.39239891e9 # GHz - sourceresistance = 111.59927 # Ohms - receiverresistance = sourceresistance # Ohms + excitationfreq = 0.39239891e9 # GHz + sourceresistance = 111.59927 # Ohms + receiverresistance = sourceresistance # Ohms absorberEr = 1.1 - absorbersig = 0.062034689 # S/m - + absorbersig = 0.062034689 # S/m + x = x - (casesize[0] / 2) y = y - (casesize[1] / 2) @@ -415,156 +502,250 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.001, **kwargs): metalboxheight = 0.088 tx = x + 0.01 + 0.004 + 0.056, y + casethickness + 0.005 + 0.143 - 0.002, z + skidthickness else: - logger.exception('This antenna module can only be used with a spatial discretisation of 0.5mm, 1mm, 2mm') + logger.exception("This antenna module can only be used with a spatial discretisation of 0.5mm, 1mm, 2mm") raise ValueError # Material definitions - absorber = gprMax.Material(er=absorberEr, se=absorbersig, mr=1, sm=0, id='absorber') - pcb = gprMax.Material(er=pcber, se=0, mr=1, sm=0, id='pcb') - hdpe = gprMax.Material(er=hdper, se=0, mr=1, sm=0, id='hdpe') + absorber = gprMax.Material(er=absorberEr, se=absorbersig, mr=1, sm=0, id="absorber") + pcb = gprMax.Material(er=pcber, se=0, mr=1, sm=0, id="pcb") + hdpe = gprMax.Material(er=hdper, se=0, mr=1, sm=0, id="hdpe") scene_objects.extend((absorber, pcb, hdpe)) # Antenna geometry - if smooth_dec == 'yes': + if smooth_dec == "yes": # Plastic case - b1 = gprMax.Box(p1=(x, y, z + skidthickness - 0.002), - p2=(x + casesize[0], y + casesize[1], z + casesize[2]), - material_id='hdpe') - b2 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness - 0.002), - p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, - z + casesize[2] - casethickness), material_id='free_space') + b1 = gprMax.Box( + p1=(x, y, z + skidthickness - 0.002), + p2=(x + casesize[0], y + casesize[1], z + casesize[2]), + material_id="hdpe", + ) + b2 = gprMax.Box( + p1=(x + casethickness, y + casethickness, z + skidthickness - 0.002), + p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + casesize[2] - casethickness), + material_id="free_space", + ) # Metallic enclosure - b3 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness + - (metalmiddleplateheight - metalboxheight)), - p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, - z + skidthickness + (metalmiddleplateheight - metalboxheight) + - metalboxheight), material_id='pec') + b3 = gprMax.Box( + p1=(x + casethickness, y + casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight)), + p2=( + x + casesize[0] - casethickness, + y + casesize[1] - casethickness, + z + skidthickness + (metalmiddleplateheight - metalboxheight) + metalboxheight, + ), + material_id="pec", + ) # Absorber, and foam (modelled as PCB material) around edge of absorber - b4 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness), - p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, - z + skidthickness + (metalmiddleplateheight - metalboxheight)), - material_id='absorber') - b5 = gprMax.Box(p1=(x + casethickness + shieldthickness, y + casethickness + - shieldthickness, z + skidthickness + (metalmiddleplateheight - metalboxheight)), - p2=(x + casesize[0] - casethickness - shieldthickness, - y + casesize[1] - casethickness - shieldthickness, - z + skidthickness - shieldthickness + metalmiddleplateheight), - material_id='absorber') + b4 = gprMax.Box( + p1=(x + casethickness, y + casethickness, z + skidthickness), + p2=( + x + casesize[0] - casethickness, + y + casesize[1] - casethickness, + z + skidthickness + (metalmiddleplateheight - metalboxheight), + ), + material_id="absorber", + ) + b5 = gprMax.Box( + p1=( + x + casethickness + shieldthickness, + y + casethickness + shieldthickness, + z + skidthickness + (metalmiddleplateheight - metalboxheight), + ), + p2=( + x + casesize[0] - casethickness - shieldthickness, + y + casesize[1] - casethickness - shieldthickness, + z + skidthickness - shieldthickness + metalmiddleplateheight, + ), + material_id="absorber", + ) scene_objects.extend((b1, b2, b3, b4, b5)) # PCB if resolution == 0.0005: - b6 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.018, y + casethickness + - 0.005 + 0.0235, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.034 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb') - b7 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.178, y + casethickness + - 0.005 + 0.0235, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.194 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb') + b6 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.0235, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.034 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) + b7 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.0235, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.194 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) scene_objects.extend((b6, b7)) elif resolution == 0.001: - b6 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.023, - z + skidthickness), p2=(x + 0.01 + 0.005 + 0.034 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb') - b7 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.023, - z + skidthickness), p2=(x + 0.01 + 0.005 + 0.194 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb') + b6 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.023, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.034 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) + b7 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.023, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.194 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) scene_objects.extend((b6, b7)) elif resolution == 0.002: - b6 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.017, y + casethickness + 0.005 + 0.021, - z + skidthickness), p2=(x + 0.01 + 0.005 + 0.033 + bowtiebase, - y + casethickness + 0.006 + 0.202 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb') - b7 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.179, y + casethickness + 0.005 + 0.021, - z + skidthickness), p2=(x + 0.01 + 0.005 + 0.195 + bowtiebase, - y + casethickness + 0.006 + 0.202 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb') + b6 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.017, y + casethickness + 0.005 + 0.021, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.033 + bowtiebase, + y + casethickness + 0.006 + 0.202 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) + b7 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.179, y + casethickness + 0.005 + 0.021, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.195 + bowtiebase, + y + casethickness + 0.006 + 0.202 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) scene_objects.extend((b6, b7)) - elif smooth_dec == 'no': + elif smooth_dec == "no": # Plastic case - b8 = gprMax.Box(p1=(x, y, z + skidthickness - 0.002), - p2=(x + casesize[0], y + casesize[1], z + casesize[2]), - material_id='hdpe', averaging='n') - b9 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness - 0.002), - p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, - z + casesize[2] - casethickness), material_id='free_space', - averaging='n') + b8 = gprMax.Box( + p1=(x, y, z + skidthickness - 0.002), + p2=(x + casesize[0], y + casesize[1], z + casesize[2]), + material_id="hdpe", + averaging="n", + ) + b9 = gprMax.Box( + p1=(x + casethickness, y + casethickness, z + skidthickness - 0.002), + p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + casesize[2] - casethickness), + material_id="free_space", + averaging="n", + ) # Metallic enclosure - b10 = gprMax.Box(p1=(x + casethickness, y + casethickness, - z + skidthickness + (metalmiddleplateheight - metalboxheight)), - p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, - z + skidthickness + (metalmiddleplateheight - metalboxheight) + - metalboxheight), material_id='pec') + b10 = gprMax.Box( + p1=(x + casethickness, y + casethickness, z + skidthickness + (metalmiddleplateheight - metalboxheight)), + p2=( + x + casesize[0] - casethickness, + y + casesize[1] - casethickness, + z + skidthickness + (metalmiddleplateheight - metalboxheight) + metalboxheight, + ), + material_id="pec", + ) # Absorber, and foam (modelled as PCB material) around edge of absorber - b11 = gprMax.Box(p1=(x + casethickness, y + casethickness, z + skidthickness), - p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, - z + skidthickness + (metalmiddleplateheight - metalboxheight)), - material_id='absorber', averaging='n') - b12 = gprMax.Box(p1=(x + casethickness + shieldthickness, - y + casethickness + shieldthickness, - z + skidthickness + (metalmiddleplateheight - metalboxheight)), - p2=(x + casesize[0] - casethickness - shieldthickness, - y + casesize[1] - casethickness - shieldthickness, - z + skidthickness - shieldthickness + metalmiddleplateheight), - material_id='absorber', averaging='n') + b11 = gprMax.Box( + p1=(x + casethickness, y + casethickness, z + skidthickness), + p2=( + x + casesize[0] - casethickness, + y + casesize[1] - casethickness, + z + skidthickness + (metalmiddleplateheight - metalboxheight), + ), + material_id="absorber", + averaging="n", + ) + b12 = gprMax.Box( + p1=( + x + casethickness + shieldthickness, + y + casethickness + shieldthickness, + z + skidthickness + (metalmiddleplateheight - metalboxheight), + ), + p2=( + x + casesize[0] - casethickness - shieldthickness, + y + casesize[1] - casethickness - shieldthickness, + z + skidthickness - shieldthickness + metalmiddleplateheight, + ), + material_id="absorber", + averaging="n", + ) scene_objects.extend((b8, b9, b10, b11, b12)) # PCB if resolution == 0.0005: - b13 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.018, - y + casethickness + 0.005 + 0.0235, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.034 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb', - averaging='n') - b14 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.178, - y + casethickness + 0.005 + 0.0235, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.194 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb', - averaging='n') + b13 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.0235, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.034 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + averaging="n", + ) + b14 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.0235, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.194 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + averaging="n", + ) scene_objects.extend((b13, b14)) elif resolution == 0.001: - b13 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.018, - y + casethickness + 0.005 + 0.023, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.034 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb', - averaging='n') - b14 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.178, - y + casethickness + 0.005 + 0.023, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.194 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb', - averaging='n') + b13 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.018, y + casethickness + 0.005 + 0.023, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.034 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + averaging="n", + ) + b14 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.178, y + casethickness + 0.005 + 0.023, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.194 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + averaging="n", + ) scene_objects.extend((b13, b14)) elif resolution == 0.002: - b13 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.017, - y + casethickness + 0.005 + 0.021, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.033 + bowtiebase, - y + casethickness + 0.006 + 0.202 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb', - averaging='n') - b14 = gprMax.Box(p1=(x + 0.01 + 0.005 + 0.179, - y + casethickness + 0.005 + 0.021, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.195 + bowtiebase, - y + casethickness + 0.006 + 0.202 + patchheight, - z + skidthickness + pcbthickness), material_id='pcb', - averaging='n') + b13 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.017, y + casethickness + 0.005 + 0.021, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.033 + bowtiebase, + y + casethickness + 0.006 + 0.202 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + averaging="n", + ) + b14 = gprMax.Box( + p1=(x + 0.01 + 0.005 + 0.179, y + casethickness + 0.005 + 0.021, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.195 + bowtiebase, + y + casethickness + 0.006 + 0.202 + patchheight, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + averaging="n", + ) scene_objects.extend((b13, b14)) # PCB components @@ -572,265 +753,380 @@ def antenna_like_GSSI_400(x, y, z, resolution=0.001, **kwargs): if resolution == 0.0005: # "left" side # extension plates - p1 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.026, - y + casethickness + 0.005 + 0.0235, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, - y + casethickness + 0.005 + 0.0235 + patchheight, - z + skidthickness), material_id='pec') - p2 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.026, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness), material_id='pec') + p1 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.0235, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.026 + bowtiebase, + y + casethickness + 0.005 + 0.0235 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) + p2 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.026 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) # triangles - t1 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.026, - y + casethickness + 0.005 + 0.0835, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, - y + casethickness + 0.005 + 0.0835, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), - y + casethickness + 0.005 + 0.0835 + bowtieheight, - z + skidthickness), thickness=0, material_id='pec') - t2 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.026, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), - y + casethickness + 0.005 + 0.204 - bowtieheight, - z + skidthickness), thickness=0, material_id='pec') + t1 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.0835, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.0835, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.026 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.0835 + bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) + t2 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.026 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.204 - bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) # "right" side - p3 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.186, - y + casethickness + 0.005 + 0.0235, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, - y + casethickness + 0.005 + 0.0235 + patchheight, - z + skidthickness), material_id='pec') - p4 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.186, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness), material_id='pec') + p3 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.0235, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.186 + bowtiebase, + y + casethickness + 0.005 + 0.0235 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) + p4 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.186 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) # triangles - t3 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.186, - y + casethickness + 0.005 + 0.0835, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, - y + casethickness + 0.005 + 0.0835, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), - y + casethickness + 0.005 + 0.0835 + bowtieheight, - z + skidthickness), thickness=0, material_ID='pec') - t4 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.186, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), - y + casethickness + 0.005 + 0.204 - bowtieheight, - z + skidthickness), thickness=0, material_id='pec') + t3 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.0835, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.0835, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.186 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.0835 + bowtieheight, + z + skidthickness, + ), + thickness=0, + material_ID="pec", + ) + t4 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.186 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.204 - bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) # Edges that represent wire between bowtie halves in 1mm model - e1 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] - dy, tx[2]), - p2=(tx[0] + 0.16, tx[1], tx[2]), material_id='pec') - e2 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] + dy, tx[2]), - p2=(tx[0] + 0.16, tx[1] + 2*dy, tx[2]), material_id='pec') - e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), - p2=(tx[0], tx[1], tx[2]), material_id='pec') - e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]), - p2=(tx[0], tx[1] + 2*dy, tx[2]), material_id='pec') + e1 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] - dy, tx[2]), p2=(tx[0] + 0.16, tx[1], tx[2]), material_id="pec") + e2 = gprMax.Edge( + p1=(tx[0] + 0.16, tx[1] + dy, tx[2]), p2=(tx[0] + 0.16, tx[1] + 2 * dy, tx[2]), material_id="pec" + ) + e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec") + e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]), p2=(tx[0], tx[1] + 2 * dy, tx[2]), material_id="pec") scene_objects.extend((p1, p2, t1, t2, p3, p4, t3, t4, e1, e2, e3, e4)) elif resolution == 0.001: # "left" side # extension plates - p1 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.026, - y + casethickness + 0.005 + 0.023, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, - y + casethickness + 0.005 + 0.023 + patchheight, - z + skidthickness), material_id='pec') - p2 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.026, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness), material_id='pec') + p1 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.023, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.026 + bowtiebase, + y + casethickness + 0.005 + 0.023 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) + p2 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.026 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) # triangles - t1 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.026, - y + casethickness + 0.005 + 0.083, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, - y + casethickness + 0.005 + 0.083, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), - y + casethickness + 0.005 + 0.083 + bowtieheight, - z + skidthickness), thickness=0, material_id='pec') - t2 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.026, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.026 + (bowtiebase/2), - y + casethickness + 0.005 + 0.204 - bowtieheight, - z + skidthickness), thickness=0, material_id='pec') + t1 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.083, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.083, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.026 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.083 + bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) + t2 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.026, y + casethickness + 0.005 + 0.204, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.026 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.026 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.204 - bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) # "right" side - p3 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.186, - y + casethickness + 0.005 + 0.023, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, - y + casethickness + 0.005 + 0.023 + patchheight, - z + skidthickness), material_id='pec') - p4 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.186, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, - y + casethickness + 0.005 + 0.204 + patchheight, - z + skidthickness), material_id='pec') + p3 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.023, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.186 + bowtiebase, + y + casethickness + 0.005 + 0.023 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) + p4 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.186 + bowtiebase, + y + casethickness + 0.005 + 0.204 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) # triangles - t3 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.186, - y + casethickness + 0.005 + 0.083, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, - y + casethickness + 0.005 + 0.083, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), - y + casethickness + 0.005 + 0.083 + bowtieheight, - z + skidthickness), thickness=0, material_id='pec') - t4 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.186, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, - y + casethickness + 0.005 + 0.204, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.186 + (bowtiebase/2), - y + casethickness + 0.005 + 0.204 - bowtieheight, - z + skidthickness), thickness=0, material_id='pec') + t3 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.083, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.083, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.186 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.083 + bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) + t4 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.186, y + casethickness + 0.005 + 0.204, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.186 + bowtiebase, y + casethickness + 0.005 + 0.204, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.186 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.204 - bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) # Edges that represent wire between bowtie halves in 1mm model - e1 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] - dy, tx[2]), - p2=(tx[0] + 0.16, tx[1], tx[2]), material_id='pec') - e2 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] + dy, tx[2]), - p2=(tx[0] + 0.16, tx[1] + 2*dy, tx[2]), material_id='pec') - e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), - p2=(tx[0], tx[1], tx[2]), material_id='pec') - e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]), - p2=(tx[0], tx[1] + 2*dy, tx[2]), material_id='pec') + e1 = gprMax.Edge(p1=(tx[0] + 0.16, tx[1] - dy, tx[2]), p2=(tx[0] + 0.16, tx[1], tx[2]), material_id="pec") + e2 = gprMax.Edge( + p1=(tx[0] + 0.16, tx[1] + dy, tx[2]), p2=(tx[0] + 0.16, tx[1] + 2 * dy, tx[2]), material_id="pec" + ) + e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec") + e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]), p2=(tx[0], tx[1] + 2 * dy, tx[2]), material_id="pec") scene_objects.extend((p1, p2, t1, t2, p3, p4, t3, t4, e1, e2, e3, e4)) elif resolution == 0.002: - # "left" side - # extension plates - p1 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.025, - y + casethickness + 0.005 + 0.021, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase, - y + casethickness + 0.005 + 0.021 + patchheight, - z + skidthickness), material_id='pec') - p2 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.025, - y + casethickness + 0.005 + 0.203, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase, - y + casethickness + 0.005 + 0.203 + patchheight, - z + skidthickness), material_id='pec') - # triangles - t1 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.025, - y + casethickness + 0.005 + 0.081, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase, - y + casethickness + 0.005 + 0.081, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), - y + casethickness + 0.005 + 0.081 + bowtieheight, - z + skidthickness), thickness=0, material_id='pec') - t2 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.025, - y + casethickness + 0.005 + 0.203, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase, - y + casethickness + 0.005 + 0.203, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.025 + (bowtiebase/2), - y + casethickness + 0.005 + 0.203 - bowtieheight, - z + skidthickness), thickness=0, material_id='pec') - # "right" side - p3 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.187, - y + casethickness + 0.005 + 0.021, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase, - y + casethickness + 0.005 + 0.021 + patchheight, - z + skidthickness), material_id='pec') - p4 = gprMax.Plate(p1=(x + 0.01 + 0.005 + 0.187, - y + casethickness + 0.005 + 0.203, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase, - y + casethickness + 0.005 + 0.203 + patchheight, - z + skidthickness), material_id='pec') - # triangles - t3 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.187, - y + casethickness + 0.005 + 0.081, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase, - y + casethickness + 0.005 + 0.081, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), - y + casethickness + 0.005 + 0.081 + bowtieheight, - z + skidthickness), thickness=0, material_id='pec') - t4 = gprMax.Triangle(p1=(x + 0.01 + 0.005 + 0.187, - y + casethickness + 0.005 + 0.203, z + skidthickness), - p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase, - y + casethickness + 0.005 + 0.203, z + skidthickness), - p3=(x + 0.01 + 0.005 + 0.187 + (bowtiebase/2), - y + casethickness + 0.005 + 0.203 - bowtieheight, - z + skidthickness), thickness=0, material_id='pec') + # "left" side + # extension plates + p1 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.021, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.025 + bowtiebase, + y + casethickness + 0.005 + 0.021 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) + p2 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.025 + bowtiebase, + y + casethickness + 0.005 + 0.203 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) + # triangles + t1 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.081, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.025 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.081 + bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) + t2 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.025, y + casethickness + 0.005 + 0.203, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.025 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.025 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.203 - bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) + # "right" side + p3 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.021, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.187 + bowtiebase, + y + casethickness + 0.005 + 0.021 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) + p4 = gprMax.Plate( + p1=(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness), + p2=( + x + 0.01 + 0.005 + 0.187 + bowtiebase, + y + casethickness + 0.005 + 0.203 + patchheight, + z + skidthickness, + ), + material_id="pec", + ) + # triangles + t3 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.081, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.081, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.187 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.081 + bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) + t4 = gprMax.Triangle( + p1=(x + 0.01 + 0.005 + 0.187, y + casethickness + 0.005 + 0.203, z + skidthickness), + p2=(x + 0.01 + 0.005 + 0.187 + bowtiebase, y + casethickness + 0.005 + 0.203, z + skidthickness), + p3=( + x + 0.01 + 0.005 + 0.187 + (bowtiebase / 2), + y + casethickness + 0.005 + 0.203 - bowtieheight, + z + skidthickness, + ), + thickness=0, + material_id="pec", + ) - # Edges that represent wire between bowtie halves in 2mm model - e1 = gprMax.Edge(p1=(tx[0] + 0.162, tx[1] - dy, tx[2]), - p2=(tx[0] + 0.162, tx[1], tx[2]), material_id='pec') - e2 = gprMax.Edge(p1=(tx[0] + 0.162, tx[1] + dy, tx[2]), - p2=(tx[0] + 0.162, tx[1] + 2*dy, tx[2]), material_id='pec') - e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), - p2=(tx[0], tx[1], tx[2]), material_id='pec') - e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]), - p2=(tx[0], tx[1] + 2*dy, tx[2]), material_id='pec') - scene_objects.extend((p1, p2, t1, t2, p3, p4, t3, t4, e1, e2, e3, e4)) + # Edges that represent wire between bowtie halves in 2mm model + e1 = gprMax.Edge(p1=(tx[0] + 0.162, tx[1] - dy, tx[2]), p2=(tx[0] + 0.162, tx[1], tx[2]), material_id="pec") + e2 = gprMax.Edge( + p1=(tx[0] + 0.162, tx[1] + dy, tx[2]), p2=(tx[0] + 0.162, tx[1] + 2 * dy, tx[2]), material_id="pec" + ) + e3 = gprMax.Edge(p1=(tx[0], tx[1] - dy, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec") + e4 = gprMax.Edge(p1=(tx[0], tx[1] + dy, tx[2]), p2=(tx[0], tx[1] + 2 * dy, tx[2]), material_id="pec") + scene_objects.extend((p1, p2, t1, t2, p3, p4, t3, t4, e1, e2, e3, e4)) # Metallic plate extension - b15 = gprMax.Box(p1=(x + (casesize[0] / 2), y + casethickness, z + skidthickness), - p2=(x + (casesize[0] / 2) + shieldthickness, - y + casesize[1] - casethickness, z + skidthickness + metalmiddleplateheight), - material_id='pec') + b15 = gprMax.Box( + p1=(x + (casesize[0] / 2), y + casethickness, z + skidthickness), + p2=( + x + (casesize[0] / 2) + shieldthickness, + y + casesize[1] - casethickness, + z + skidthickness + metalmiddleplateheight, + ), + material_id="pec", + ) # Skid - if smooth_dec == 'yes': - b16 = gprMax.Box(p1=(x, y, z), - p2=(x + casesize[0], y + casesize[1], z + skidthickness - 0.002), - material_id='hdpe') - elif smooth_dec == 'no': - b16 = gprMax.Box(p1=(x, y, z), - p2=(x + casesize[0], y + casesize[1], z + skidthickness - 0.002), - material_id='hdpe', averaging='n') + if smooth_dec == "yes": + b16 = gprMax.Box( + p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + skidthickness - 0.002), material_id="hdpe" + ) + elif smooth_dec == "no": + b16 = gprMax.Box( + p1=(x, y, z), + p2=(x + casesize[0], y + casesize[1], z + skidthickness - 0.002), + material_id="hdpe", + averaging="n", + ) scene_objects.extend((b15, b16)) # Source # Excitation - Gaussian pulse - w1 = gprMax.Waveform(wave_type='gaussian', amp=1, freq=excitationfreq, id='my_gaussian') + w1 = gprMax.Waveform(wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian") scene_objects.append(w1) - if src_type == 'voltage_source': - vs1 = gprMax.VoltageSource(polarisation='y', p1=(tx[0], tx[1], tx[2]), - resistance=sourceresistance, waveform_id='my_gaussian') + if src_type == "voltage_source": + vs1 = gprMax.VoltageSource( + polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_gaussian" + ) scene_objects.append(vs1) - elif src_type == 'transmission_line': - tl1 = gprMax.TransmissionLine(polarisation='y', p1=(tx[0], tx[1], tx[2]), - resistance=sourceresistance, waveform_id='my_gaussian') + elif src_type == "transmission_line": + tl1 = gprMax.TransmissionLine( + polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_gaussian" + ) scene_objects.append(tl1) - # Receiver + # Receiver # Zero waveform to use with transmission line at receiver output - w2 = gprMax.Waveform(wave_type='gaussian', amp=0, freq=excitationfreq, id='my_zero_wave') + w2 = gprMax.Waveform(wave_type="gaussian", amp=0, freq=excitationfreq, id="my_zero_wave") scene_objects.append(w2) if resolution == 0.001 or resolution == 0.0005: - if src_type == 'transmission_line': - tl2 = gprMax.TransmissionLine(polarisation='y', p1=(tx[0] + 0.16, tx[1], tx[2]), - resistance=receiverresistance, waveform_id='my_zero_wave') + if src_type == "transmission_line": + tl2 = gprMax.TransmissionLine( + polarisation="y", + p1=(tx[0] + 0.16, tx[1], tx[2]), + resistance=receiverresistance, + waveform_id="my_zero_wave", + ) scene_objects.append(tl2) - elif src_type == 'voltage_source': - r1 = gprMax.Rx(p1=(tx[0] + 0.16, tx[1], tx[2]), id='rxbowtie', outputs='Ey') + elif src_type == "voltage_source": + r1 = gprMax.Rx(p1=(tx[0] + 0.16, tx[1], tx[2]), id="rxbowtie", outputs="Ey") scene_objects.append(r1) elif resolution == 0.002: - if src_type == 'transmission_line': - tl2 = gprMax.TransmissionLine(polarisation='y', p1=(tx[0] + 0.162, tx[1], tx[2]), - resistance=receiverresistance, waveform_id='my_zero_wave') + if src_type == "transmission_line": + tl2 = gprMax.TransmissionLine( + polarisation="y", + p1=(tx[0] + 0.162, tx[1], tx[2]), + resistance=receiverresistance, + waveform_id="my_zero_wave", + ) scene_objects.append(tl2) - elif src_type == 'voltage_source': - r1 = gprMax.Rx(p1=(tx[0] + 0.162, tx[1], tx[2]), id='rxbowtie', outputs='Ey') + elif src_type == "voltage_source": + r1 = gprMax.Rx(p1=(tx[0] + 0.162, tx[1], tx[2]), id="rxbowtie", outputs="Ey") scene_objects.append(r1) # Geometry views - gv1 = gprMax.GeometryView(p1=(x - dx, y - dy, z - dz), p2=(x + casesize[0] + dx, - y + casesize[1] + dy, z + skidthickness + casesize[2] + dz), - dl=(dx, dy, dz), filename='antenna_like_GSSI_400', - output_type='n') - gv2 = gprMax.GeometryView(p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + 0.010), - dl=(dx, dy, dz), filename='antenna_like_GSSI_400_pcb', - output_type='f') + gv1 = gprMax.GeometryView( + p1=(x - dx, y - dy, z - dz), + p2=(x + casesize[0] + dx, y + casesize[1] + dy, z + skidthickness + casesize[2] + dz), + dl=(dx, dy, dz), + filename="antenna_like_GSSI_400", + output_type="n", + ) + gv2 = gprMax.GeometryView( + p1=(x, y, z), + p2=(x + casesize[0], y + casesize[1], z + 0.010), + dl=(dx, dy, dz), + filename="antenna_like_GSSI_400_pcb", + output_type="f", + ) # scene_objects.extend((gv1, gv2)) return scene_objects diff --git a/toolboxes/GPRAntennaModels/MALA.py b/toolboxes/GPRAntennaModels/MALA.py index 86098848..0e2f32f3 100644 --- a/toolboxes/GPRAntennaModels/MALA.py +++ b/toolboxes/GPRAntennaModels/MALA.py @@ -26,7 +26,7 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs): centre of the antenna in the x-y plane and the bottom of the antenna skid in the z direction. resolution (float): Spatial resolution for the antenna model. - kwargs (dict): Optional variables, e.g. can be fed from an optimisation + kwargs (dict): Optional variables, e.g. can be fed from an optimisation process. Returns: @@ -50,10 +50,10 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs): # If using parameters from an optimisation try: kwargs - excitationfreq = kwargs['excitationfreq'] - sourceresistance = kwargs['sourceresistance'] - absorberEr = kwargs['absorberEr'] - absorbersig = kwargs['absorbersig'] + excitationfreq = kwargs["excitationfreq"] + sourceresistance = kwargs["sourceresistance"] + absorberEr = kwargs["absorberEr"] + absorbersig = kwargs["absorbersig"] # Otherwise choose pre-set optimised parameters except: @@ -84,7 +84,7 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs): bowtieheight = 0.024 tx = x + 0.062, y + 0.052, z + skidthickness else: - logger.exception('This antenna module can only be used with a spatial resolution of 1mm or 2mm') + logger.exception("This antenna module can only be used with a spatial resolution of 1mm or 2mm") raise ValueError # SMD resistors - 3 on each Tx & Rx bowtie arm @@ -100,382 +100,573 @@ def antenna_like_MALA_1200(x, y, z, resolution=0.001, **kwargs): rxsiglower = ((1 / rxrescelllower) * (dy / (dx * dz))) / 2 # Divide by number of parallel edges per resistor # Material definitions - absorber = gprMax.Material(er=absorberEr, se=absorbersig, mr=1, sm=0, id='absorber') - pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id='pcb') - hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id='hdpe') - polypropylene = gprMax.Material(er=2.26, se=0, mr=1, sm=0, id='polypropylene') - txreslower = gprMax.Material(er=3, se=txsiglower, mr=1, sm=0, id='txreslower') - txresupper = gprMax.Material(er=3, se=txsigupper, mr=1, sm=0, id='txresupper') - rxreslower = gprMax.Material(er=3, se=rxsiglower, mr=1, sm=0, id='rxreslower') - rxresupper = gprMax.Material(er=3, se=rxsigupper, mr=1, sm=0, id='rxresupper') - scene_objects.extend((absorber, pcb, hdpe, polypropylene, txreslower, txresupper, - rxreslower, rxresupper)) + absorber = gprMax.Material(er=absorberEr, se=absorbersig, mr=1, sm=0, id="absorber") + pcb = gprMax.Material(er=3, se=0, mr=1, sm=0, id="pcb") + hdpe = gprMax.Material(er=2.35, se=0, mr=1, sm=0, id="hdpe") + polypropylene = gprMax.Material(er=2.26, se=0, mr=1, sm=0, id="polypropylene") + txreslower = gprMax.Material(er=3, se=txsiglower, mr=1, sm=0, id="txreslower") + txresupper = gprMax.Material(er=3, se=txsigupper, mr=1, sm=0, id="txresupper") + rxreslower = gprMax.Material(er=3, se=rxsiglower, mr=1, sm=0, id="rxreslower") + rxresupper = gprMax.Material(er=3, se=rxsigupper, mr=1, sm=0, id="rxresupper") + scene_objects.extend((absorber, pcb, hdpe, polypropylene, txreslower, txresupper, rxreslower, rxresupper)) # Antenna geometry # Shield - metallic enclosure - b1 = gprMax.Box(p1=(x, y, z + skidthickness), - p2=(x + casesize[0], y + casesize[1], z + skidthickness + casesize[2]), - material_id='pec') - b2 = gprMax.Box(p1=(x + 0.020, y + casethickness, z + skidthickness), - p2=(x + 0.100, y + casesize[1] - casethickness, - z + skidthickness + casethickness), material_id='free_space') - b3 = gprMax.Box(p1=(x + 0.100, y + casethickness, z + skidthickness), - p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, - z + skidthickness + casethickness), material_id='free_space') + b1 = gprMax.Box( + p1=(x, y, z + skidthickness), + p2=(x + casesize[0], y + casesize[1], z + skidthickness + casesize[2]), + material_id="pec", + ) + b2 = gprMax.Box( + p1=(x + 0.020, y + casethickness, z + skidthickness), + p2=(x + 0.100, y + casesize[1] - casethickness, z + skidthickness + casethickness), + material_id="free_space", + ) + b3 = gprMax.Box( + p1=(x + 0.100, y + casethickness, z + skidthickness), + p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, z + skidthickness + casethickness), + material_id="free_space", + ) # Absorber material - b4 = gprMax.Box(p1=(x + 0.020, y + casethickness, z + skidthickness), - p2=(x + 0.100, y + casesize[1] - casethickness, - z + skidthickness + casesize[2] - casethickness), - material_id='absorber') - b5 = gprMax.Box(p1=(x + 0.100, y + casethickness, z + skidthickness), - p2=(x + casesize[0] - casethickness, y + casesize[1] - casethickness, - z + skidthickness + casesize[2] - casethickness), - material_id='absorber') + b4 = gprMax.Box( + p1=(x + 0.020, y + casethickness, z + skidthickness), + p2=(x + 0.100, y + casesize[1] - casethickness, z + skidthickness + casesize[2] - casethickness), + material_id="absorber", + ) + b5 = gprMax.Box( + p1=(x + 0.100, y + casethickness, z + skidthickness), + p2=( + x + casesize[0] - casethickness, + y + casesize[1] - casethickness, + z + skidthickness + casesize[2] - casethickness, + ), + material_id="absorber", + ) scene_objects.extend((b1, b2, b3, b4, b5)) # Shield - cylindrical sections - c1 = gprMax.Cylinder(p1=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness), - p2=(x + 0.055, y + casesize[1] - 0.008, - z + skidthickness + casesize[2] - casethickness), - r=0.008, material_id='pec') - c2 = gprMax.Cylinder(p1=(x + 0.055, y + 0.008, z + skidthickness), - p2=(x + 0.055, y + 0.008, - z + skidthickness + casesize[2] - casethickness), - r=0.008, material_id='pec') - c3 = gprMax.Cylinder(p1=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness), - p2=(x + 0.147, y + casesize[1] - 0.008, - z + skidthickness + casesize[2] - casethickness), - r=0.008, material_id='pec') - c4 = gprMax.Cylinder(p1=(x + 0.147, y + 0.008, z + skidthickness), - p2=(x + 0.147, y + 0.008, - z + skidthickness + casesize[2] - casethickness), - r=0.008, material_id='pec') - c5 = gprMax.Cylinder(p1=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness), - p2=(x + 0.055, y + casesize[1] - 0.008, - z + skidthickness + casesize[2] - casethickness), - r=0.007, material_id='free_space') - c6 = gprMax.Cylinder(p1=(x + 0.055, y + 0.008, z + skidthickness), - p2=(x + 0.055, y + 0.008, - z + skidthickness + casesize[2] - casethickness), - r=0.007, material_id='free_space') - c7 = gprMax.Cylinder(p1=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness), - p2=(x + 0.147, y + casesize[1] - 0.008, - z + skidthickness + casesize[2] - casethickness), - r=0.007, material_id='free_space') - c8 = gprMax.Cylinder(p1=(x + 0.147, y + 0.008, z + skidthickness), - p2=(x + 0.147, y + 0.008, - z + skidthickness + casesize[2] - casethickness), - r=0.007, material_id='free_space') - b6 = gprMax.Box(p1=(x + 0.054, y + casesize[1] - 0.016, z + skidthickness), - p2=(x + 0.056, y + casesize[1] - 0.014, - z + skidthickness + casesize[2] - casethickness), - material_id='free_space') - b7 = gprMax.Box(p1=(x + 0.054, y + 0.014, z + skidthickness), - p2=(x + 0.056, y + 0.016, - z + skidthickness + casesize[2] - casethickness), - material_id='free_space') - b8 = gprMax.Box(p1=(x + 0.146, y + casesize[1] - 0.016, z + skidthickness), - p2=(x + 0.148, y + casesize[1] - 0.014, - z + skidthickness + casesize[2] - casethickness), - material_id='free_space') - b9 = gprMax.Box(p1=(x + 0.146, y + 0.014, z + skidthickness), - p2=(x + 0.148, y + 0.016, - z + skidthickness + casesize[2] - casethickness), - material_id='free_space') + c1 = gprMax.Cylinder( + p1=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness), + p2=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness), + r=0.008, + material_id="pec", + ) + c2 = gprMax.Cylinder( + p1=(x + 0.055, y + 0.008, z + skidthickness), + p2=(x + 0.055, y + 0.008, z + skidthickness + casesize[2] - casethickness), + r=0.008, + material_id="pec", + ) + c3 = gprMax.Cylinder( + p1=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness), + p2=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness), + r=0.008, + material_id="pec", + ) + c4 = gprMax.Cylinder( + p1=(x + 0.147, y + 0.008, z + skidthickness), + p2=(x + 0.147, y + 0.008, z + skidthickness + casesize[2] - casethickness), + r=0.008, + material_id="pec", + ) + c5 = gprMax.Cylinder( + p1=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness), + p2=(x + 0.055, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness), + r=0.007, + material_id="free_space", + ) + c6 = gprMax.Cylinder( + p1=(x + 0.055, y + 0.008, z + skidthickness), + p2=(x + 0.055, y + 0.008, z + skidthickness + casesize[2] - casethickness), + r=0.007, + material_id="free_space", + ) + c7 = gprMax.Cylinder( + p1=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness), + p2=(x + 0.147, y + casesize[1] - 0.008, z + skidthickness + casesize[2] - casethickness), + r=0.007, + material_id="free_space", + ) + c8 = gprMax.Cylinder( + p1=(x + 0.147, y + 0.008, z + skidthickness), + p2=(x + 0.147, y + 0.008, z + skidthickness + casesize[2] - casethickness), + r=0.007, + material_id="free_space", + ) + b6 = gprMax.Box( + p1=(x + 0.054, y + casesize[1] - 0.016, z + skidthickness), + p2=(x + 0.056, y + casesize[1] - 0.014, z + skidthickness + casesize[2] - casethickness), + material_id="free_space", + ) + b7 = gprMax.Box( + p1=(x + 0.054, y + 0.014, z + skidthickness), + p2=(x + 0.056, y + 0.016, z + skidthickness + casesize[2] - casethickness), + material_id="free_space", + ) + b8 = gprMax.Box( + p1=(x + 0.146, y + casesize[1] - 0.016, z + skidthickness), + p2=(x + 0.148, y + casesize[1] - 0.014, z + skidthickness + casesize[2] - casethickness), + material_id="free_space", + ) + b9 = gprMax.Box( + p1=(x + 0.146, y + 0.014, z + skidthickness), + p2=(x + 0.148, y + 0.016, z + skidthickness + casesize[2] - casethickness), + material_id="free_space", + ) scene_objects.extend((c1, c2, c3, c4, c5, c6, c7, c8, b6, b7, b8, b9)) # PCB - b10 = gprMax.Box(p1=(x + 0.020, y + 0.018, z + skidthickness), - p2=(x + casesize[0] - casethickness, y + casesize[1] - 0.018, - z + skidthickness + pcbthickness), material_id='pcb') + b10 = gprMax.Box( + p1=(x + 0.020, y + 0.018, z + skidthickness), + p2=(x + casesize[0] - casethickness, y + casesize[1] - 0.018, z + skidthickness + pcbthickness), + material_id="pcb", + ) # Shield - Tx & Rx cavities - b11 = gprMax.Box(p1=(x + 0.032, y + 0.022, z + skidthickness), - p2=(x + 0.032 + cavitysize[0], y + 0.022 + cavitysize[1], - z + skidthickness + cavitysize[2]), material_id='pec') - b12 = gprMax.Box(p1=(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, - z + skidthickness), p2=(x + 0.032 + cavitysize[0] - cavitythickness, - y + 0.022 + cavitysize[1] - cavitythickness, - z + skidthickness + cavitysize[2]), material_id='absorber') - b13 = gprMax.Box(p1=(x + 0.108, y + 0.022, z + skidthickness), - p2=(x + 0.108 + cavitysize[0], y + 0.022 + cavitysize[1], - z + skidthickness + cavitysize[2]), material_id='pec') - b14 = gprMax.Box(p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, - z + skidthickness), p2=(x + 0.108 + cavitysize[0] - cavitythickness, - y + 0.022 + cavitysize[1] - cavitythickness, - z + skidthickness + cavitysize[2]), material_id='free_space') + b11 = gprMax.Box( + p1=(x + 0.032, y + 0.022, z + skidthickness), + p2=(x + 0.032 + cavitysize[0], y + 0.022 + cavitysize[1], z + skidthickness + cavitysize[2]), + material_id="pec", + ) + b12 = gprMax.Box( + p1=(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness), + p2=( + x + 0.032 + cavitysize[0] - cavitythickness, + y + 0.022 + cavitysize[1] - cavitythickness, + z + skidthickness + cavitysize[2], + ), + material_id="absorber", + ) + b13 = gprMax.Box( + p1=(x + 0.108, y + 0.022, z + skidthickness), + p2=(x + 0.108 + cavitysize[0], y + 0.022 + cavitysize[1], z + skidthickness + cavitysize[2]), + material_id="pec", + ) + b14 = gprMax.Box( + p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness), + p2=( + x + 0.108 + cavitysize[0] - cavitythickness, + y + 0.022 + cavitysize[1] - cavitythickness, + z + skidthickness + cavitysize[2], + ), + material_id="free_space", + ) # Shield - Tx & Rx cavities - joining strips - b15 = gprMax.Box(p1=(x + 0.032 + cavitysize[0], y + 0.022 + cavitysize[1] - 0.006, - z + skidthickness + cavitysize[2] - casethickness), - p2=(x + 0.108, y + 0.022 + cavitysize[1], - z + skidthickness + cavitysize[2]), material_id='pec') - b16 = gprMax.Box(p1=(x + 0.032 + cavitysize[0], y + 0.022, - z + skidthickness + cavitysize[2] - casethickness), - p2=(x + 0.108, y + 0.022 + 0.006, - z + skidthickness + cavitysize[2]), material_id='pec') + b15 = gprMax.Box( + p1=( + x + 0.032 + cavitysize[0], + y + 0.022 + cavitysize[1] - 0.006, + z + skidthickness + cavitysize[2] - casethickness, + ), + p2=(x + 0.108, y + 0.022 + cavitysize[1], z + skidthickness + cavitysize[2]), + material_id="pec", + ) + b16 = gprMax.Box( + p1=(x + 0.032 + cavitysize[0], y + 0.022, z + skidthickness + cavitysize[2] - casethickness), + p2=(x + 0.108, y + 0.022 + 0.006, z + skidthickness + cavitysize[2]), + material_id="pec", + ) # PCB - replace bits chopped by TX & Rx cavities - b17 = gprMax.Box(p1=(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, - z + skidthickness), p2=(x + 0.032 + cavitysize[0] - cavitythickness, - y + 0.022 + cavitysize[1] - cavitythickness, - z + skidthickness + pcbthickness), material_id='pcb') - b18 = gprMax.Box(p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, - z + skidthickness), p2=(x + 0.108 + cavitysize[0] - cavitythickness, - y + 0.022 + cavitysize[1] - cavitythickness, - z + skidthickness + pcbthickness), material_id='pcb') + b17 = gprMax.Box( + p1=(x + 0.032 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness), + p2=( + x + 0.032 + cavitysize[0] - cavitythickness, + y + 0.022 + cavitysize[1] - cavitythickness, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) + b18 = gprMax.Box( + p1=(x + 0.108 + cavitythickness, y + 0.022 + cavitythickness, z + skidthickness), + p2=( + x + 0.108 + cavitysize[0] - cavitythickness, + y + 0.022 + cavitysize[1] - cavitythickness, + z + skidthickness + pcbthickness, + ), + material_id="pcb", + ) scene_objects.extend((b10, b11, b12, b13, b14, b15, b16, b17, b18)) # PCB components # Tx bowtie if resolution == 0.001: - t1 = gprMax.Triangle(p1=(tx[0], tx[1] - 0.001, tx[2]), - p2=(tx[0] - 0.026, tx[1] - bowtieheight - 0.001, tx[2]), - p3=(tx[0] + 0.026, tx[1] - bowtieheight - 0.001, tx[2]), - thickness=0, material_id='pec') - e1 = gprMax.Edge(p1=(tx[0], tx[1] - 0.001, tx[2]), - p2=(tx[0], tx[1], tx[2]), material_id='pec') - t2 = gprMax.Triangle(p1=(tx[0], tx[1] + 0.002, tx[2]), - p2=(tx[0] - 0.026, tx[1] + bowtieheight + 0.002, tx[2]), - p3=(tx[0] + 0.026, tx[1] + bowtieheight + 0.002, tx[2]), - thickness=0, material_id='pec') - e2 = gprMax.Edge(p1=(tx[0], tx[1] + 0.001, tx[2]), - p2=(tx[0], tx[1] + 0.002, tx[2]), material_id='pec') + t1 = gprMax.Triangle( + p1=(tx[0], tx[1] - 0.001, tx[2]), + p2=(tx[0] - 0.026, tx[1] - bowtieheight - 0.001, tx[2]), + p3=(tx[0] + 0.026, tx[1] - bowtieheight - 0.001, tx[2]), + thickness=0, + material_id="pec", + ) + e1 = gprMax.Edge(p1=(tx[0], tx[1] - 0.001, tx[2]), p2=(tx[0], tx[1], tx[2]), material_id="pec") + t2 = gprMax.Triangle( + p1=(tx[0], tx[1] + 0.002, tx[2]), + p2=(tx[0] - 0.026, tx[1] + bowtieheight + 0.002, tx[2]), + p3=(tx[0] + 0.026, tx[1] + bowtieheight + 0.002, tx[2]), + thickness=0, + material_id="pec", + ) + e2 = gprMax.Edge(p1=(tx[0], tx[1] + 0.001, tx[2]), p2=(tx[0], tx[1] + 0.002, tx[2]), material_id="pec") scene_objects.extend((t1, t2, e1, e2)) elif resolution == 0.002: - t1 = gprMax.Triangle(p1=(tx[0], tx[1], tx[2]), - p2=(tx[0] - 0.026, tx[1] - bowtieheight, tx[2]), - p3=(tx[0] + 0.026, tx[1] - bowtieheight, tx[2]), - thickness=0, material_id='pec') - t2 = gprMax.Triangle(p1=(tx[0], tx[1] + 0.002, tx[2]), - p2=(tx[0] - 0.026, tx[1] + bowtieheight + 0.002, tx[2]), - p3=(tx[0] + 0.026, tx[1] + bowtieheight + 0.002, tx[2]), - thickness=0, material_id='pec') + t1 = gprMax.Triangle( + p1=(tx[0], tx[1], tx[2]), + p2=(tx[0] - 0.026, tx[1] - bowtieheight, tx[2]), + p3=(tx[0] + 0.026, tx[1] - bowtieheight, tx[2]), + thickness=0, + material_id="pec", + ) + t2 = gprMax.Triangle( + p1=(tx[0], tx[1] + 0.002, tx[2]), + p2=(tx[0] - 0.026, tx[1] + bowtieheight + 0.002, tx[2]), + p3=(tx[0] + 0.026, tx[1] + bowtieheight + 0.002, tx[2]), + thickness=0, + material_id="pec", + ) scene_objects.extend((t1, t2)) # Rx bowtie if resolution == 0.001: - t3 = gprMax.Triangle(p1=(tx[0] + 0.076, tx[1] - 0.001, tx[2]), - p2=(tx[0] + 0.076 - 0.026, tx[1] - bowtieheight - 0.001, tx[2]), - p3=(tx[0] + 0.076 + 0.026, tx[1] - bowtieheight - 0.001, tx[2]), - thickness=0, material_id='pec') - e3 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] - 0.001, tx[2]), - p2=(tx[0] + 0.076, tx[1], tx[2]), material_id='pec') - t4 = gprMax.Triangle(p1=(tx[0] + 0.076, tx[1] + 0.002, tx[2]), - p2=(tx[0] + 0.076 - 0.026, tx[1] + bowtieheight + 0.002, tx[2]), - p3=(tx[0] + 0.076 + 0.026, tx[1] + bowtieheight + 0.002, tx[2]), - thickness=0, material_id='pec') - e4 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] + 0.001, tx[2]), - p2=(tx[0] + 0.076, tx[1] + 0.002, tx[2]), material_id='pec') + t3 = gprMax.Triangle( + p1=(tx[0] + 0.076, tx[1] - 0.001, tx[2]), + p2=(tx[0] + 0.076 - 0.026, tx[1] - bowtieheight - 0.001, tx[2]), + p3=(tx[0] + 0.076 + 0.026, tx[1] - bowtieheight - 0.001, tx[2]), + thickness=0, + material_id="pec", + ) + e3 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] - 0.001, tx[2]), p2=(tx[0] + 0.076, tx[1], tx[2]), material_id="pec") + t4 = gprMax.Triangle( + p1=(tx[0] + 0.076, tx[1] + 0.002, tx[2]), + p2=(tx[0] + 0.076 - 0.026, tx[1] + bowtieheight + 0.002, tx[2]), + p3=(tx[0] + 0.076 + 0.026, tx[1] + bowtieheight + 0.002, tx[2]), + thickness=0, + material_id="pec", + ) + e4 = gprMax.Edge( + p1=(tx[0] + 0.076, tx[1] + 0.001, tx[2]), p2=(tx[0] + 0.076, tx[1] + 0.002, tx[2]), material_id="pec" + ) scene_objects.extend((t3, e3, t4, e4)) elif resolution == 0.002: - t3 = gprMax.Triangle(p1=(tx[0] + 0.076, tx[1], tx[2]), - p2=(tx[0] + 0.076 - 0.026, tx[1] - bowtieheight, tx[2]), - p3=(tx[0] + 0.076 + 0.026, tx[1] - bowtieheight, tx[2]), - thickness=0, material_id='pec') - t4 = gprMax.Triangle(p1=(tx[0] + 0.076, tx[1] + 0.002, tx[2]), - p2=(tx[0] + 0.076 - 0.026, tx[1] + bowtieheight + 0.002, tx[2]), - p3=(tx[0] + 0.076 + 0.026, tx[1] + bowtieheight + 0.002, tx[2]), - thickness=0, material_id='pec') + t3 = gprMax.Triangle( + p1=(tx[0] + 0.076, tx[1], tx[2]), + p2=(tx[0] + 0.076 - 0.026, tx[1] - bowtieheight, tx[2]), + p3=(tx[0] + 0.076 + 0.026, tx[1] - bowtieheight, tx[2]), + thickness=0, + material_id="pec", + ) + t4 = gprMax.Triangle( + p1=(tx[0] + 0.076, tx[1] + 0.002, tx[2]), + p2=(tx[0] + 0.076 - 0.026, tx[1] + bowtieheight + 0.002, tx[2]), + p3=(tx[0] + 0.076 + 0.026, tx[1] + bowtieheight + 0.002, tx[2]), + thickness=0, + material_id="pec", + ) scene_objects.extend((t3, t4)) # Tx surface mount resistors (lower y coordinate) if resolution == 0.001: - e5 = gprMax.Edge(p1=(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] - 0.023, tx[1] - bowtieheight - dy, tx[2]), - material_id='txreslower') - e6 = gprMax.Edge(p1=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - dy, tx[2]), - material_id='txreslower') - e7 = gprMax.Edge(p1=(tx[0], tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0], tx[1] - bowtieheight - dy, tx[2]), - material_id='txreslower') - e8 = gprMax.Edge(p1=(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + dx, tx[1] - bowtieheight - dy, tx[2]), - material_id='txreslower') - e9 = gprMax.Edge(p1=(tx[0] + 0.022, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.022, tx[1] - bowtieheight - dy, tx[2]), - material_id='txreslower') - e10 = gprMax.Edge(p1=(tx[0] + 0.022 + dx, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.022 + dx, tx[1] - bowtieheight - dy, tx[2]), - material_id='txreslower') + e5 = gprMax.Edge( + p1=(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] - 0.023, tx[1] - bowtieheight - dy, tx[2]), + material_id="txreslower", + ) + e6 = gprMax.Edge( + p1=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - dy, tx[2]), + material_id="txreslower", + ) + e7 = gprMax.Edge( + p1=(tx[0], tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0], tx[1] - bowtieheight - dy, tx[2]), + material_id="txreslower", + ) + e8 = gprMax.Edge( + p1=(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + dx, tx[1] - bowtieheight - dy, tx[2]), + material_id="txreslower", + ) + e9 = gprMax.Edge( + p1=(tx[0] + 0.022, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.022, tx[1] - bowtieheight - dy, tx[2]), + material_id="txreslower", + ) + e10 = gprMax.Edge( + p1=(tx[0] + 0.022 + dx, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.022 + dx, tx[1] - bowtieheight - dy, tx[2]), + material_id="txreslower", + ) scene_objects.extend((e5, e6, e7, e8, e9, e10)) elif resolution == 0.002: - e5 = gprMax.Edge(p1=(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] - 0.023, tx[1] - bowtieheight, tx[2]), - material_id='txreslower') - e6 = gprMax.Edge(p1=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] - 0.023 + dx, tx[1] - bowtieheight, tx[2]), - material_id='txreslower') - e7 = gprMax.Edge(p1=(tx[0], tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0], tx[1] - bowtieheight, tx[2]), - material_id='txreslower') - e8 = gprMax.Edge(p1=(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + dx, tx[1] - bowtieheight, tx[2]), - material_id='txreslower') - e9 = gprMax.Edge(p1=(tx[0] + 0.020, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.020, tx[1] - bowtieheight, tx[2]), - material_id='txreslower') - e10 = gprMax.Edge(p1=(tx[0] + 0.020 + dx, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.020 + dx, tx[1] - bowtieheight, tx[2]), - material_id='txreslower') + e5 = gprMax.Edge( + p1=(tx[0] - 0.023, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] - 0.023, tx[1] - bowtieheight, tx[2]), + material_id="txreslower", + ) + e6 = gprMax.Edge( + p1=(tx[0] - 0.023 + dx, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] - 0.023 + dx, tx[1] - bowtieheight, tx[2]), + material_id="txreslower", + ) + e7 = gprMax.Edge( + p1=(tx[0], tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0], tx[1] - bowtieheight, tx[2]), + material_id="txreslower", + ) + e8 = gprMax.Edge( + p1=(tx[0] + dx, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + dx, tx[1] - bowtieheight, tx[2]), + material_id="txreslower", + ) + e9 = gprMax.Edge( + p1=(tx[0] + 0.020, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.020, tx[1] - bowtieheight, tx[2]), + material_id="txreslower", + ) + e10 = gprMax.Edge( + p1=(tx[0] + 0.020 + dx, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.020 + dx, tx[1] - bowtieheight, tx[2]), + material_id="txreslower", + ) scene_objects.extend((e5, e6, e7, e8, e9, e10)) # Tx surface mount resistors (upper y coordinate) if resolution == 0.001: - e11 = gprMax.Edge(p1=(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] - 0.023, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e12 = gprMax.Edge(p1=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e13 = gprMax.Edge(p1=(tx[0], tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0], tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e14 = gprMax.Edge(p1=(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + dx, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e15 = gprMax.Edge(p1=(tx[0] + 0.022, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.022, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e16 = gprMax.Edge(p1=(tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') + e11 = gprMax.Edge( + p1=(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] - 0.023, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e12 = gprMax.Edge( + p1=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e13 = gprMax.Edge( + p1=(tx[0], tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0], tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e14 = gprMax.Edge( + p1=(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + dx, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e15 = gprMax.Edge( + p1=(tx[0] + 0.022, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.022, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e16 = gprMax.Edge( + p1=(tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.022 + dx, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) scene_objects.extend((e11, e12, e13, e14, e15, e16)) elif resolution == 0.002: - e11 = gprMax.Edge(p1=(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] - 0.023, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e12 = gprMax.Edge(p1=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e13 = gprMax.Edge(p1=(tx[0], tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0], tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e14 = gprMax.Edge(p1=(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + dx, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e15 = gprMax.Edge(p1=(tx[0] + 0.020, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.020, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') - e16 = gprMax.Edge(p1=(tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='txresupper') + e11 = gprMax.Edge( + p1=(tx[0] - 0.023, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] - 0.023, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e12 = gprMax.Edge( + p1=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] - 0.023 + dx, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e13 = gprMax.Edge( + p1=(tx[0], tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0], tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e14 = gprMax.Edge( + p1=(tx[0] + dx, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + dx, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e15 = gprMax.Edge( + p1=(tx[0] + 0.020, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.020, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) + e16 = gprMax.Edge( + p1=(tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.020 + dx, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="txresupper", + ) scene_objects.extend((e11, e12, e13, e14, e15, e16)) # Rx surface mount resistors (lower y coordinate) if resolution == 0.001: - e17 = gprMax.Edge(p1=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - dy, tx[2]), - material_id='rxreslower') - e18 = gprMax.Edge(p1=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]), - material_id='rxreslower') - e19 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.076, tx[1] - bowtieheight - dy, tx[2]), - material_id='rxreslower') - e20 = gprMax.Edge(p1=(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]), - material_id='rxreslower') - e21 = gprMax.Edge(p1=(tx[0] + 0.022 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.022 + 0.076, tx[1] - bowtieheight - dy, tx[2]), - material_id='rxreslower') - e22 = gprMax.Edge(p1=(tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]), - material_id='rxreslower') + e17 = gprMax.Edge( + p1=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - dy, tx[2]), + material_id="rxreslower", + ) + e18 = gprMax.Edge( + p1=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]), + material_id="rxreslower", + ) + e19 = gprMax.Edge( + p1=(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.076, tx[1] - bowtieheight - dy, tx[2]), + material_id="rxreslower", + ) + e20 = gprMax.Edge( + p1=(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]), + material_id="rxreslower", + ) + e21 = gprMax.Edge( + p1=(tx[0] + 0.022 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.022 + 0.076, tx[1] - bowtieheight - dy, tx[2]), + material_id="rxreslower", + ) + e22 = gprMax.Edge( + p1=(tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.022 + dx + 0.076, tx[1] - bowtieheight - dy, tx[2]), + material_id="rxreslower", + ) scene_objects.extend((e17, e18, e19, e20, e21, e22)) elif resolution == 0.002: - e17 = gprMax.Edge(p1=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight, tx[2]), - material_id='rxreslower') - e18 = gprMax.Edge(p1=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight, tx[2]), - material_id='rxreslower') - e19 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.076, tx[1] - bowtieheight, tx[2]), - material_id='rxreslower') - e20 = gprMax.Edge(p1=(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + dx + 0.076, tx[1] - bowtieheight, tx[2]), - material_id='rxreslower') - e21 = gprMax.Edge(p1=(tx[0] + 0.020 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.020 + 0.076, tx[1] - bowtieheight, tx[2]), - material_id='rxreslower') - e22 = gprMax.Edge(p1=(tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), - p2=(tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight, tx[2]), - material_id='rxreslower') + e17 = gprMax.Edge( + p1=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] - 0.023 + 0.076, tx[1] - bowtieheight, tx[2]), + material_id="rxreslower", + ) + e18 = gprMax.Edge( + p1=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] - 0.023 + dx + 0.076, tx[1] - bowtieheight, tx[2]), + material_id="rxreslower", + ) + e19 = gprMax.Edge( + p1=(tx[0] + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.076, tx[1] - bowtieheight, tx[2]), + material_id="rxreslower", + ) + e20 = gprMax.Edge( + p1=(tx[0] + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + dx + 0.076, tx[1] - bowtieheight, tx[2]), + material_id="rxreslower", + ) + e21 = gprMax.Edge( + p1=(tx[0] + 0.020 + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.020 + 0.076, tx[1] - bowtieheight, tx[2]), + material_id="rxreslower", + ) + e22 = gprMax.Edge( + p1=(tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight - 0.004, tx[2]), + p2=(tx[0] + 0.020 + dx + 0.076, tx[1] - bowtieheight, tx[2]), + material_id="rxreslower", + ) scene_objects.extend((e17, e18, e19, e20, e21, e22)) # Rx surface mount resistors (upper y coordinate) if resolution == 0.001: - e23 = gprMax.Edge(p1=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e24 = gprMax.Edge(p1=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e25 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e26 = gprMax.Edge(p1=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e27 = gprMax.Edge(p1=(tx[0] + 0.022 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.022 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e28 = gprMax.Edge(p1=(tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') + e23 = gprMax.Edge( + p1=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e24 = gprMax.Edge( + p1=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e25 = gprMax.Edge( + p1=(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e26 = gprMax.Edge( + p1=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e27 = gprMax.Edge( + p1=(tx[0] + 0.022 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.022 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e28 = gprMax.Edge( + p1=(tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.022 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) scene_objects.extend((e23, e24, e25, e26, e27, e28)) elif resolution == 0.002: - e23 = gprMax.Edge(p1=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e24 = gprMax.Edge(p1=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e25 = gprMax.Edge(p1=(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e26 = gprMax.Edge(p1=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e27 = gprMax.Edge(p1=(tx[0] + 0.020 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.020 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') - e28 = gprMax.Edge(p1=(tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), - p2=(tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), - material_id='rxresupper') + e23 = gprMax.Edge( + p1=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] - 0.023 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e24 = gprMax.Edge( + p1=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] - 0.023 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e25 = gprMax.Edge( + p1=(tx[0] + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e26 = gprMax.Edge( + p1=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e27 = gprMax.Edge( + p1=(tx[0] + 0.020 + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.020 + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) + e28 = gprMax.Edge( + p1=(tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.002, tx[2]), + p2=(tx[0] + 0.020 + dx + 0.076, tx[1] + bowtieheight + 0.006, tx[2]), + material_id="rxresupper", + ) scene_objects.extend((e23, e24, e25, e26, e27, e28)) # Skid - b19 = gprMax.Box(p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], - z + polypropylenethickness), material_id='polypropylene') - b20 = gprMax.Box(p1=(x, y, z + polypropylenethickness), - p2=(x + casesize[0], y + casesize[1], - z + polypropylenethickness + hdpethickness), - material_id='hdpe') + b19 = gprMax.Box( + p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + polypropylenethickness), material_id="polypropylene" + ) + b20 = gprMax.Box( + p1=(x, y, z + polypropylenethickness), + p2=(x + casesize[0], y + casesize[1], z + polypropylenethickness + hdpethickness), + material_id="hdpe", + ) scene_objects.extend((b19, b20)) # Excitation - w2 = gprMax.Waveform(wave_type='gaussian', amp=1, freq=excitationfreq, id='my_gaussian') + w2 = gprMax.Waveform(wave_type="gaussian", amp=1, freq=excitationfreq, id="my_gaussian") scene_objects.append(w2) - vs1 = gprMax.VoltageSource(polarisation='y', p1=(tx[0], tx[1], tx[2]), - resistance=sourceresistance, waveform_id='my_gaussian') + vs1 = gprMax.VoltageSource( + polarisation="y", p1=(tx[0], tx[1], tx[2]), resistance=sourceresistance, waveform_id="my_gaussian" + ) scene_objects.append(vs1) # Output point - receiver bowtie - r1 = gprMax.Rx(p1=(tx[0] + 0.076, tx[1], tx[2]), id='rxbowtie', outputs='Ey') + r1 = gprMax.Rx(p1=(tx[0] + 0.076, tx[1], tx[2]), id="rxbowtie", outputs="Ey") scene_objects.append(r1) # Geometry views - gv1 = gprMax.GeometryView(p1=(x - dx, y - dy, z - dz), p2=(x + casesize[0] + dx, - y + casesize[1] + dy, z + skidthickness + casesize[2] + dz), - dl=(dx, dy, dz), filename='antenna_like_MALA_1200', - output_type='n') - gv2 = gprMax.GeometryView(p1=(x, y, z), p2=(x + casesize[0], y + casesize[1], z + 0.010), - dl=(dx, dy, dz), filename='antenna_like_MALA_1200_pcb', - output_type='f') + gv1 = gprMax.GeometryView( + p1=(x - dx, y - dy, z - dz), + p2=(x + casesize[0] + dx, y + casesize[1] + dy, z + skidthickness + casesize[2] + dz), + dl=(dx, dy, dz), + filename="antenna_like_MALA_1200", + output_type="n", + ) + gv2 = gprMax.GeometryView( + p1=(x, y, z), + p2=(x + casesize[0], y + casesize[1], z + 0.010), + dl=(dx, dy, dz), + filename="antenna_like_MALA_1200_pcb", + output_type="f", + ) # scene_objects.extend((gv1, gv2)) return scene_objects diff --git a/toolboxes/GPRAntennaModels/README.rst b/toolboxes/GPRAntennaModels/README.rst index bf6f5291..71ccec01 100644 --- a/toolboxes/GPRAntennaModels/README.rst +++ b/toolboxes/GPRAntennaModels/README.rst @@ -53,11 +53,11 @@ To include an antenna model similar to a GSSI 1.5 GHz antenna at a location 0.12 from toolboxes.GPRAntennaModels.GSSI import antenna_like_GSSI_1500 scene = gprMax.Scene() - + # Import antenna model and add to model dl = 0.002 ant_pos = (0.125, 0.094, 0.100) - gssi_objects = antenna_like_GSSI_1500(ant_pos[0], ant_pos[1], ant_pos[2], + gssi_objects = antenna_like_GSSI_1500(ant_pos[0], ant_pos[1], ant_pos[2], resolution=dl) for obj in gssi_objects: # Rotate antenna model if desired diff --git a/toolboxes/Plotting/README.rst b/toolboxes/Plotting/README.rst index 189eed9e..db251d64 100644 --- a/toolboxes/Plotting/README.rst +++ b/toolboxes/Plotting/README.rst @@ -253,7 +253,7 @@ where :math:`dt` is the temporal resolution (timestep) of the model. Example of the ``impulse`` waveform - time domain. .. note:: - * The impulse waveform should be used with care! - * The impulse response of a model, i.e. when the source in the model is excited using the impulse waveform, is not likely to be useful when viewed in isolation. + * The impulse waveform should be used with care! + * The impulse response of a model, i.e. when the source in the model is excited using the impulse waveform, is not likely to be useful when viewed in isolation. * However, the impulse response of a model can be convolved with different inputs (waveforms) to provide valid outputs without having to run a separate model for each different input (waveform). - * The impulse response of the model can only be legitimately convolved with inputs (waveforms) that respect the limits of numerical dispersion in the original model, i.e. if a waveform contains frequencies that will not propagate correctly (due to numerical dispersion) in the original model, then the convolution of the waveform with the impulse response will not be valid. \ No newline at end of file + * The impulse response of the model can only be legitimately convolved with inputs (waveforms) that respect the limits of numerical dispersion in the original model, i.e. if a waveform contains frequencies that will not propagate correctly (due to numerical dispersion) in the original model, then the convolution of the waveform with the impulse response will not be valid. diff --git a/toolboxes/Plotting/plot_Ascan.py b/toolboxes/Plotting/plot_Ascan.py index 4087c83b..3990e0f0 100644 --- a/toolboxes/Plotting/plot_Ascan.py +++ b/toolboxes/Plotting/plot_Ascan.py @@ -48,55 +48,53 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False): file = Path(filename) # Open output file and read iterations - f = h5py.File(file, 'r') + f = h5py.File(file, "r") # Paths to grid(s) to traverse for outputs - paths = ['/'] + paths = ["/"] # Check if any subgrids and add path(s) is_subgrids = "/subgrids" in f if is_subgrids: - paths = paths + ['/subgrids/' + path + '/' for path in f['/subgrids'].keys()] + paths = paths + ["/subgrids/" + path + "/" for path in f["/subgrids"].keys()] # Get number of receivers in grid(s) nrxs = [] for path in paths: - if f[path].attrs['nrx'] > 0: - nrxs.append(f[path].attrs['nrx']) + if f[path].attrs["nrx"] > 0: + nrxs.append(f[path].attrs["nrx"]) else: paths.remove(path) # Check there are any receivers if not paths: - logger.exception(f'No receivers found in {file}') + logger.exception(f"No receivers found in {file}") raise ValueError # Loop through all grids for path in paths: - iterations = f[path].attrs['Iterations'] - nrx = f[path].attrs['nrx'] - dt = f[path].attrs['dt'] + iterations = f[path].attrs["Iterations"] + nrx = f[path].attrs["nrx"] + dt = f[path].attrs["dt"] time = np.linspace(0, (iterations - 1) * dt, num=iterations) # Check for single output component when doing a FFT if fft: if not len(outputs) == 1: - logger.exception('A single output must be specified when using ' + - 'the -fft option') + logger.exception("A single output must be specified when using " + "the -fft option") raise ValueError # New plot for each receiver for rx in range(1, nrx + 1): - rxpath = path + 'rxs/rx' + str(rx) + '/' + rxpath = path + "rxs/rx" + str(rx) + "/" availableoutputs = list(f[rxpath].keys()) # If only a single output is required, create one subplot if len(outputs) == 1: - # Check for polarity of output and if requested output is in file - if outputs[0][-1] == '-': + if outputs[0][-1] == "-": polarity = -1 - outputtext = '-' + outputs[0][0:-1] + outputtext = "-" + outputs[0][0:-1] output = outputs[0][0:-1] else: polarity = 1 @@ -104,9 +102,11 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False): output = outputs[0] if output not in availableoutputs: - logger.exception(f"{output} output requested to plot, but " + - f"the available output for receiver 1 is " + - f"{', '.join(availableoutputs)}") + logger.exception( + f"{output} output requested to plot, but " + + f"the available output for receiver 1 is " + + f"{', '.join(availableoutputs)}" + ) raise ValueError outputdata = f[rxpath + output][:] * polarity @@ -118,7 +118,7 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False): freqmaxpower = np.where(np.isclose(power, 0))[0][0] # Set plotting range to -60dB from maximum power or 4 times - # frequency at maximum power + # frequency at maximum power try: pltrange = np.where(power[freqmaxpower:] < -60)[0][0] + freqmaxpower + 1 except: @@ -127,82 +127,91 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False): pltrange = np.s_[0:pltrange] # Plot time history of output component - fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, - num=rxpath + ' - ' + f[rxpath].attrs['Name'], - figsize=(20, 10), facecolor='w', - edgecolor='w') - line1 = ax1.plot(time, outputdata, 'r', lw=2, label=outputtext) - ax1.set_xlabel('Time [s]') - ax1.set_ylabel(outputtext + ' field strength [V/m]') + fig, (ax1, ax2) = plt.subplots( + nrows=1, + ncols=2, + num=rxpath + " - " + f[rxpath].attrs["Name"], + figsize=(20, 10), + facecolor="w", + edgecolor="w", + ) + line1 = ax1.plot(time, outputdata, "r", lw=2, label=outputtext) + ax1.set_xlabel("Time [s]") + ax1.set_ylabel(outputtext + " field strength [V/m]") ax1.set_xlim([0, np.amax(time)]) - ax1.grid(which='both', axis='both', linestyle='-.') + ax1.grid(which="both", axis="both", linestyle="-.") # Plot frequency spectra - markerline, stemlines, baseline = ax2.stem(freqs[pltrange], - power[pltrange], '-.', - use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'r') - plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r') - line2 = ax2.plot(freqs[pltrange], power[pltrange], 'r', lw=2) - ax2.set_xlabel('Frequency [Hz]') - ax2.set_ylabel('Power [dB]') - ax2.grid(which='both', axis='both', linestyle='-.') + markerline, stemlines, baseline = ax2.stem( + freqs[pltrange], power[pltrange], "-.", use_line_collection=True + ) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "r") + plt.setp(markerline, "markerfacecolor", "r", "markeredgecolor", "r") + line2 = ax2.plot(freqs[pltrange], power[pltrange], "r", lw=2) + ax2.set_xlabel("Frequency [Hz]") + ax2.set_ylabel("Power [dB]") + ax2.grid(which="both", axis="both", linestyle="-.") - # Change colours and labels for magnetic field components + # Change colours and labels for magnetic field components # or currents - if 'H' in outputs[0]: - plt.setp(line1, color='g') - plt.setp(line2, color='g') - plt.setp(ax1, ylabel=outputtext + ' field strength [A/m]') - plt.setp(stemlines, 'color', 'g') - plt.setp(markerline, 'markerfacecolor', 'g', - 'markeredgecolor', 'g') - elif 'I' in outputs[0]: - plt.setp(line1, color='b') - plt.setp(line2, color='b') - plt.setp(ax1, ylabel=outputtext + ' current [A]') - plt.setp(stemlines, 'color', 'b') - plt.setp(markerline, 'markerfacecolor', 'b', - 'markeredgecolor', 'b') + if "H" in outputs[0]: + plt.setp(line1, color="g") + plt.setp(line2, color="g") + plt.setp(ax1, ylabel=outputtext + " field strength [A/m]") + plt.setp(stemlines, "color", "g") + plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g") + elif "I" in outputs[0]: + plt.setp(line1, color="b") + plt.setp(line2, color="b") + plt.setp(ax1, ylabel=outputtext + " current [A]") + plt.setp(stemlines, "color", "b") + plt.setp(markerline, "markerfacecolor", "b", "markeredgecolor", "b") plt.show() # Plotting if no FFT required else: - fig, ax = plt.subplots(subplot_kw=dict(xlabel='Time [s]', - ylabel=outputtext + ' field strength [V/m]'), - num=rxpath + ' - ' + f[rxpath].attrs['Name'], - figsize=(20, 10), facecolor='w', edgecolor='w') - line = ax.plot(time, outputdata, 'r', lw=2, label=outputtext) + fig, ax = plt.subplots( + subplot_kw=dict(xlabel="Time [s]", ylabel=outputtext + " field strength [V/m]"), + num=rxpath + " - " + f[rxpath].attrs["Name"], + figsize=(20, 10), + facecolor="w", + edgecolor="w", + ) + line = ax.plot(time, outputdata, "r", lw=2, label=outputtext) ax.set_xlim([0, np.amax(time)]) # ax.set_ylim([-15, 20]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") - if 'H' in output: - plt.setp(line, color='g') - plt.setp(ax, ylabel=outputtext + ', field strength [A/m]') - elif 'I' in output: - plt.setp(line, color='b') - plt.setp(ax, ylabel=outputtext + ', current [A]') + if "H" in output: + plt.setp(line, color="g") + plt.setp(ax, ylabel=outputtext + ", field strength [A/m]") + elif "I" in output: + plt.setp(line, color="b") + plt.setp(ax, ylabel=outputtext + ", current [A]") - # If multiple outputs required, create all nine subplots and + # If multiple outputs required, create all nine subplots and # populate only the specified ones else: - fig, ax = plt.subplots(subplot_kw=dict(xlabel='Time [s]'), - num=rxpath + ' - ' + f[rxpath].attrs['Name'], - figsize=(20, 10), facecolor='w', edgecolor='w') + fig, ax = plt.subplots( + subplot_kw=dict(xlabel="Time [s]"), + num=rxpath + " - " + f[rxpath].attrs["Name"], + figsize=(20, 10), + facecolor="w", + edgecolor="w", + ) if len(outputs) == 9: gs = gridspec.GridSpec(3, 3, hspace=0.3, wspace=0.3) else: gs = gridspec.GridSpec(3, 2, hspace=0.3, wspace=0.3) for output in outputs: - # Check for polarity of output and if requested output + # Check for polarity of output and if requested output # is in file - if output[-1] == 'm': + if output[-1] == "m": polarity = -1 - outputtext = '-' + output[0:-1] + outputtext = "-" + output[0:-1] output = output[0:-1] else: polarity = 1 @@ -210,93 +219,115 @@ def mpl_plot(filename, outputs=Rx.defaultoutputs, fft=False, save=False): # Check if requested output is in file if output not in availableoutputs: - logger.exception(f"Output(s) requested to plot: " + - f"{', '.join(outputs)}, but available output(s) " + - f"for receiver {rx} in the file: " + - f"{', '.join(availableoutputs)}") + logger.exception( + f"Output(s) requested to plot: " + + f"{', '.join(outputs)}, but available output(s) " + + f"for receiver {rx} in the file: " + + f"{', '.join(availableoutputs)}" + ) raise ValueError outputdata = f[rxpath + output][:] * polarity - if output == 'Ex': + if output == "Ex": ax = plt.subplot(gs[0, 0]) - ax.plot(time, outputdata, 'r', lw=2, label=outputtext) - ax.set_ylabel(outputtext + ', field strength [V/m]') + ax.plot(time, outputdata, "r", lw=2, label=outputtext) + ax.set_ylabel(outputtext + ", field strength [V/m]") # ax.set_ylim([-15, 20]) - elif output == 'Ey': + elif output == "Ey": ax = plt.subplot(gs[1, 0]) - ax.plot(time, outputdata, 'r', lw=2, label=outputtext) - ax.set_ylabel(outputtext + ', field strength [V/m]') + ax.plot(time, outputdata, "r", lw=2, label=outputtext) + ax.set_ylabel(outputtext + ", field strength [V/m]") # ax.set_ylim([-15, 20]) - elif output == 'Ez': + elif output == "Ez": ax = plt.subplot(gs[2, 0]) - ax.plot(time, outputdata, 'r', lw=2, label=outputtext) - ax.set_ylabel(outputtext + ', field strength [V/m]') + ax.plot(time, outputdata, "r", lw=2, label=outputtext) + ax.set_ylabel(outputtext + ", field strength [V/m]") # ax.set_ylim([-15, 20]) - elif output == 'Hx': + elif output == "Hx": ax = plt.subplot(gs[0, 1]) - ax.plot(time, outputdata, 'g', lw=2, label=outputtext) - ax.set_ylabel(outputtext + ', field strength [A/m]') + ax.plot(time, outputdata, "g", lw=2, label=outputtext) + ax.set_ylabel(outputtext + ", field strength [A/m]") # ax.set_ylim([-0.03, 0.03]) - elif output == 'Hy': + elif output == "Hy": ax = plt.subplot(gs[1, 1]) - ax.plot(time, outputdata, 'g', lw=2, label=outputtext) - ax.set_ylabel(outputtext + ', field strength [A/m]') + ax.plot(time, outputdata, "g", lw=2, label=outputtext) + ax.set_ylabel(outputtext + ", field strength [A/m]") # ax.set_ylim([-0.03, 0.03]) - elif output == 'Hz': + elif output == "Hz": ax = plt.subplot(gs[2, 1]) - ax.plot(time, outputdata, 'g', lw=2, label=outputtext) - ax.set_ylabel(outputtext + ', field strength [A/m]') + ax.plot(time, outputdata, "g", lw=2, label=outputtext) + ax.set_ylabel(outputtext + ", field strength [A/m]") # ax.set_ylim([-0.03, 0.03]) - elif output == 'Ix': + elif output == "Ix": ax = plt.subplot(gs[0, 2]) - ax.plot(time, outputdata, 'b', lw=2, label=outputtext) - ax.set_ylabel(outputtext + ', current [A]') - elif output == 'Iy': + ax.plot(time, outputdata, "b", lw=2, label=outputtext) + ax.set_ylabel(outputtext + ", current [A]") + elif output == "Iy": ax = plt.subplot(gs[1, 2]) - ax.plot(time, outputdata, 'b', lw=2, label=outputtext) - ax.set_ylabel(outputtext + ', current [A]') - elif output == 'Iz': + ax.plot(time, outputdata, "b", lw=2, label=outputtext) + ax.set_ylabel(outputtext + ", current [A]") + elif output == "Iz": ax = plt.subplot(gs[2, 2]) - ax.plot(time, outputdata, 'b', lw=2, label=outputtext) - ax.set_ylabel(outputtext + ', current [A]') + ax.plot(time, outputdata, "b", lw=2, label=outputtext) + ax.set_ylabel(outputtext + ", current [A]") for ax in fig.axes: ax.set_xlim([0, np.amax(time)]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") f.close() if save: # Save a PDF of the figure - fig.savefig(filename[:-3] + '.pdf', dpi=None, format='pdf', - bbox_inches='tight', pad_inches=0.1) + fig.savefig(filename[:-3] + ".pdf", dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1) # Save a PNG of the figure - # fig.savefig(filename[:-3] + '.png', dpi=150, format='png', + # fig.savefig(filename[:-3] + '.png', dpi=150, format='png', # bbox_inches='tight', pad_inches=0.1) return plt if __name__ == "__main__": - # Parse command line arguments - parser = argparse.ArgumentParser(description='Plots electric and magnetic fields and ' + - 'currents from all receiver points in the given output file. ' + - 'Each receiver point is plotted in a new figure window.', - usage='cd gprMax; python -m toolboxes.Plotting.plot_Ascan outputfile') - parser.add_argument('outputfile', help='name of output file including path') - parser.add_argument('--outputs', help='outputs to be plotted', - default=Rx.defaultoutputs, - choices=['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz', - 'Ix', 'Iy', 'Iz', 'Ex-', 'Ey-', 'Ez-', - 'Hx-', 'Hy-', 'Hz-', 'Ix-', 'Iy-', 'Iz-'], - nargs='+') - parser.add_argument('-fft', action='store_true', default=False, - help='plot FFT (single output must be specified)') - parser.add_argument('-save', action='store_true', default=False, - help='save plot directly to file, i.e. do not display') + parser = argparse.ArgumentParser( + description="Plots electric and magnetic fields and " + + "currents from all receiver points in the given output file. " + + "Each receiver point is plotted in a new figure window.", + usage="cd gprMax; python -m toolboxes.Plotting.plot_Ascan outputfile", + ) + parser.add_argument("outputfile", help="name of output file including path") + parser.add_argument( + "--outputs", + help="outputs to be plotted", + default=Rx.defaultoutputs, + choices=[ + "Ex", + "Ey", + "Ez", + "Hx", + "Hy", + "Hz", + "Ix", + "Iy", + "Iz", + "Ex-", + "Ey-", + "Ez-", + "Hx-", + "Hy-", + "Hz-", + "Ix-", + "Iy-", + "Iz-", + ], + nargs="+", + ) + parser.add_argument("-fft", action="store_true", default=False, help="plot FFT (single output must be specified)") + parser.add_argument( + "-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display" + ) args = parser.parse_args() plthandle = mpl_plot(args.outputfile, args.outputs, fft=args.fft, save=args.save) - - plthandle.show() \ No newline at end of file + + plthandle.show() diff --git a/toolboxes/Plotting/plot_Bscan.py b/toolboxes/Plotting/plot_Bscan.py index 2f4ddc1d..7573db32 100644 --- a/toolboxes/Plotting/plot_Bscan.py +++ b/toolboxes/Plotting/plot_Bscan.py @@ -46,59 +46,69 @@ def mpl_plot(filename, outputdata, dt, rxnumber, rxcomponent, save=False): file = Path(filename) - fig = plt.figure(num=file.stem + ' - rx' + str(rxnumber), figsize=(20, 10), - facecolor='w', edgecolor='w') - plt.imshow(outputdata, extent=[0, outputdata.shape[1], outputdata.shape[0] * dt, 0], - interpolation='nearest', aspect='auto', cmap='seismic', - vmin=-np.amax(np.abs(outputdata)), vmax=np.amax(np.abs(outputdata))) - plt.xlabel('Trace number') - plt.ylabel('Time [s]') + fig = plt.figure(num=file.stem + " - rx" + str(rxnumber), figsize=(20, 10), facecolor="w", edgecolor="w") + plt.imshow( + outputdata, + extent=[0, outputdata.shape[1], outputdata.shape[0] * dt, 0], + interpolation="nearest", + aspect="auto", + cmap="seismic", + vmin=-np.amax(np.abs(outputdata)), + vmax=np.amax(np.abs(outputdata)), + ) + plt.xlabel("Trace number") + plt.ylabel("Time [s]") # Grid properties ax = fig.gca() - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") cb = plt.colorbar() - if 'E' in rxcomponent: - cb.set_label('Field strength [V/m]') - elif 'H' in rxcomponent: - cb.set_label('Field strength [A/m]') - elif 'I' in rxcomponent: - cb.set_label('Current [A]') + if "E" in rxcomponent: + cb.set_label("Field strength [V/m]") + elif "H" in rxcomponent: + cb.set_label("Field strength [A/m]") + elif "I" in rxcomponent: + cb.set_label("Current [A]") if save: # Save a PDF of the figure - fig.savefig(filename[:-3] + '.pdf', dpi=None, format='pdf', - bbox_inches='tight', pad_inches=0.1) + fig.savefig(filename[:-3] + ".pdf", dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1) # Save a PNG of the figure - # fig.savefig(filename[:-3] + '.png', dpi=150, format='png', + # fig.savefig(filename[:-3] + '.png', dpi=150, format='png', # bbox_inches='tight', pad_inches=0.1) return plt if __name__ == "__main__": - # Parse command line arguments - parser = argparse.ArgumentParser(description='Plots a B-scan image.', - usage='cd gprMax; python -m toolboxes.Plotting.plot_Bscan outputfile output') - parser.add_argument('outputfile', help='name of output file including path') - parser.add_argument('rx_component', help='name of output component to be plotted', - choices=['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz', 'Ix', 'Iy', 'Iz']) - parser.add_argument('-gather', action='store_true', default=False, - help='gather together all receiver outputs in file') - parser.add_argument('-save', action='store_true', default=False, - help='save plot directly to file, i.e. do not display') + parser = argparse.ArgumentParser( + description="Plots a B-scan image.", + usage="cd gprMax; python -m toolboxes.Plotting.plot_Bscan outputfile output", + ) + parser.add_argument("outputfile", help="name of output file including path") + parser.add_argument( + "rx_component", + help="name of output component to be plotted", + choices=["Ex", "Ey", "Ez", "Hx", "Hy", "Hz", "Ix", "Iy", "Iz"], + ) + parser.add_argument( + "-gather", action="store_true", default=False, help="gather together all receiver outputs in file" + ) + parser.add_argument( + "-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display" + ) args = parser.parse_args() # Open output file and read number of outputs (receivers) - f = h5py.File(args.outputfile, 'r') - nrx = f.attrs['nrx'] + f = h5py.File(args.outputfile, "r") + nrx = f.attrs["nrx"] f.close() # Check there are any receivers if nrx == 0: - logger.exception(f'No receivers found in {args.outputfile}') + logger.exception(f"No receivers found in {args.outputfile}") raise ValueError for rx in range(1, nrx + 1): @@ -108,12 +118,10 @@ if __name__ == "__main__": rxsgather = outputdata rxsgather = np.column_stack((rxsgather, outputdata)) else: - plthandle = mpl_plot(args.outputfile, outputdata, dt, rx, - args.rx_component, save=args.save) + plthandle = mpl_plot(args.outputfile, outputdata, dt, rx, args.rx_component, save=args.save) # Plot all receivers from single output file together if required if args.gather: - plthandle = mpl_plot(args.outputfile, rxsgather, dt, rx, - args.rx_component, save=args.save) + plthandle = mpl_plot(args.outputfile, rxsgather, dt, rx, args.rx_component, save=args.save) plthandle.show() diff --git a/toolboxes/Plotting/plot_antenna_params.py b/toolboxes/Plotting/plot_antenna_params.py index 387a7e2c..03ada0d9 100644 --- a/toolboxes/Plotting/plot_antenna_params.py +++ b/toolboxes/Plotting/plot_antenna_params.py @@ -45,65 +45,67 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N # Open output file and read some attributes file = Path(filename) - f = h5py.File(file, 'r') - dxdydz = f.attrs['dx_dy_dz'] - dt = f.attrs['dt'] - iterations = f.attrs['Iterations'] + f = h5py.File(file, "r") + dxdydz = f.attrs["dx_dy_dz"] + dt = f.attrs["dt"] + iterations = f.attrs["Iterations"] # Calculate time array and frequency bin spacing time = np.linspace(0, (iterations - 1) * dt, num=iterations) df = 1 / np.amax(time) - logger.info(f'Time window: {np.amax(time):g} s ({iterations} iterations)') - logger.info(f'Time step: {dt:g} s') - logger.info(f'Frequency bin spacing: {df:g} Hz') + logger.info(f"Time window: {np.amax(time):g} s ({iterations} iterations)") + logger.info(f"Time step: {dt:g} s") + logger.info(f"Frequency bin spacing: {df:g} Hz") # Read/calculate voltages and currents from transmitter antenna - tltxpath = '/tls/tl' + str(tltxnumber) + '/' + tltxpath = "/tls/tl" + str(tltxnumber) + "/" # Incident voltages/currents - Vinc = f[tltxpath + 'Vinc'][:] - Iinc = f[tltxpath + 'Iinc'][:] + Vinc = f[tltxpath + "Vinc"][:] + Iinc = f[tltxpath + "Iinc"][:] # Total (incident + reflected) voltages/currents - Vtotal = f[tltxpath + 'Vtotal'][:] - Itotal = f[tltxpath + 'Itotal'][:] + Vtotal = f[tltxpath + "Vtotal"][:] + Itotal = f[tltxpath + "Itotal"][:] # Reflected voltages/currents Vref = Vtotal - Vinc Iref = Itotal - Iinc - # If a receiver antenna is used (with a transmission line or receiver), + # If a receiver antenna is used (with a transmission line or receiver), # get received voltage for s21 if tlrxnumber: - tlrxpath = '/tls/tl' + str(tlrxnumber) + '/' - Vrec = f[tlrxpath + 'Vtotal'][:] + tlrxpath = "/tls/tl" + str(tlrxnumber) + "/" + Vrec = f[tlrxpath + "Vtotal"][:] elif rxnumber: - rxpath = '/rxs/rx' + str(rxnumber) + '/' + rxpath = "/rxs/rx" + str(rxnumber) + "/" availableoutputs = list(f[rxpath].keys()) if rxcomponent not in availableoutputs: - logger.exception(f"{rxcomponent} output requested, but the available " + - f"output for receiver {rxnumber} is " + - f"{', '.join(availableoutputs)}") + logger.exception( + f"{rxcomponent} output requested, but the available " + + f"output for receiver {rxnumber} is " + + f"{', '.join(availableoutputs)}" + ) raise ValueError rxpath += rxcomponent # Received voltage - if rxcomponent == 'Ex': + if rxcomponent == "Ex": Vrec = f[rxpath][:] * -1 * dxdydz[0] - elif rxcomponent == 'Ey': + elif rxcomponent == "Ey": Vrec = f[rxpath][:] * -1 * dxdydz[1] - elif rxcomponent == 'Ez': + elif rxcomponent == "Ez": Vrec = f[rxpath][:] * -1 * dxdydz[2] f.close() # Frequency bins freqs = np.fft.fftfreq(Vinc.size, d=dt) - # Delay correction - current lags voltage, so delay voltage to match + # Delay correction - current lags voltage, so delay voltage to match # current timestep delaycorrection = np.exp(1j * 2 * np.pi * freqs * (dt / 2)) @@ -119,7 +121,7 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N yin = np.fft.fft(Itotal) / (np.fft.fft(Vtotal) * delaycorrection) # Convert to decibels (ignore warning from taking a log of any zero values) - with np.errstate(divide='ignore'): + with np.errstate(divide="ignore"): Vincp = 20 * np.log10(np.abs((np.fft.fft(Vinc) * delaycorrection))) Iincp = 20 * np.log10(np.abs(np.fft.fft(Iinc))) Vrefp = 20 * np.log10(np.abs((np.fft.fft(Vref) * delaycorrection))) @@ -138,23 +140,56 @@ def calculate_antenna_params(filename, tltxnumber=1, tlrxnumber=None, rxnumber=N s11[np.invert(np.isfinite(s11))] = 0 # Create dictionary of antenna parameters - antennaparams = {'time': time, 'freqs': freqs, 'Vinc': Vinc, 'Vincp': Vincp, - 'Iinc': Iinc, 'Iincp': Iincp, 'Vref': Vref, 'Vrefp': Vrefp, - 'Iref': Iref, 'Irefp': Irefp, 'Vtotal': Vtotal, - 'Vtotalp': Vtotalp, 'Itotal': Itotal, 'Itotalp': Itotalp, - 's11': s11, 'zin': zin, 'yin': yin} + antennaparams = { + "time": time, + "freqs": freqs, + "Vinc": Vinc, + "Vincp": Vincp, + "Iinc": Iinc, + "Iincp": Iincp, + "Vref": Vref, + "Vrefp": Vrefp, + "Iref": Iref, + "Irefp": Irefp, + "Vtotal": Vtotal, + "Vtotalp": Vtotalp, + "Itotal": Itotal, + "Itotalp": Itotalp, + "s11": s11, + "zin": zin, + "yin": yin, + } if tlrxnumber or rxnumber: - with np.errstate(divide='ignore'): # Ignore warning from taking a log of any zero values + with np.errstate(divide="ignore"): # Ignore warning from taking a log of any zero values s21 = 20 * np.log10(s21) s21[np.invert(np.isfinite(s21))] = 0 - antennaparams['s21'] = s21 + antennaparams["s21"] = s21 return antennaparams -def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp, - Iref, Irefp, Vtotal, Vtotalp, Itotal, Itotalp, s11, zin, yin, - s21=None, save=False): +def mpl_plot( + filename, + time, + freqs, + Vinc, + Vincp, + Iinc, + Iincp, + Vref, + Vrefp, + Iref, + Irefp, + Vtotal, + Vtotalp, + Itotal, + Itotalp, + s11, + zin, + yin, + s21=None, + save=False, +): """Plots antenna parameters - incident, reflected and total voltages and currents; s11, (s21) and input impedance. @@ -162,14 +197,14 @@ def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp, filename: string of filename (including path) of output file. time: array of simulation time. freq: array of frequencies for FFTs. - Vinc, Vincp, Iinc, Iincp: arrays of time and frequency domain - representations of incident voltage and + Vinc, Vincp, Iinc, Iincp: arrays of time and frequency domain + representations of incident voltage and current. - Vref, Vrefp, Iref, Irefp: arrays of time and frequency domain - representations of reflected voltage and + Vref, Vrefp, Iref, Irefp: arrays of time and frequency domain + representations of reflected voltage and current. - Vtotal, Vtotalp, Itotal, Itotalp: arrays of time and frequency domain - representations of total voltage and + Vtotal, Vtotalp, Itotal, Itotalp: arrays of time and frequency domain + representations of total voltage and current. s11, s21: array(s) of s11 and, optionally, s21 parameters. zin, yin: arrays of input impedance and input admittance parameters. @@ -189,105 +224,103 @@ def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp, # Print some useful values from s11, and input impedance s11minfreq = np.where(s11[pltrange] == np.amin(s11[pltrange]))[0][0] - logger.info(f's11 minimum: {np.amin(s11[pltrange]):g} dB at ' + - f'{freqs[s11minfreq + pltrangemin]:g} Hz') - logger.info(f'At {freqs[s11minfreq + pltrangemin]:g} Hz...') - logger.info(f'Input impedance: {np.abs(zin[s11minfreq + pltrangemin]):.1f}' + - f'{zin[s11minfreq + pltrangemin].imag:+.1f}j Ohms') + logger.info(f"s11 minimum: {np.amin(s11[pltrange]):g} dB at " + f"{freqs[s11minfreq + pltrangemin]:g} Hz") + logger.info(f"At {freqs[s11minfreq + pltrangemin]:g} Hz...") + logger.info( + f"Input impedance: {np.abs(zin[s11minfreq + pltrangemin]):.1f}" + + f"{zin[s11minfreq + pltrangemin].imag:+.1f}j Ohms" + ) # logger.info(f'Input admittance (mag): {np.abs(yin[s11minfreq + pltrangemin]):g} S') # logger.info(f'Input admittance (phase): {np.angle(yin[s11minfreq + pltrangemin], deg=True):.1f} deg') # Figure 1 # Plot incident voltage - fig1, ax = plt.subplots(num='Transmitter transmission line parameters', - figsize=(20, 12), facecolor='w', edgecolor='w') + fig1, ax = plt.subplots( + num="Transmitter transmission line parameters", figsize=(20, 12), facecolor="w", edgecolor="w" + ) gs1 = gridspec.GridSpec(4, 2, hspace=0.7) ax = plt.subplot(gs1[0, 0]) - ax.plot(time, Vinc, 'r', lw=2, label='Vinc') - ax.set_title('Incident voltage') - ax.set_xlabel('Time [s]') - ax.set_ylabel('Voltage [V]') + ax.plot(time, Vinc, "r", lw=2, label="Vinc") + ax.set_title("Incident voltage") + ax.set_xlabel("Time [s]") + ax.set_ylabel("Voltage [V]") ax.set_xlim([0, np.amax(time)]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") # Plot frequency spectra of incident voltage ax = plt.subplot(gs1[0, 1]) - markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vincp[pltrange], - '-.', use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'r') - plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r') - ax.plot(freqs[pltrange], Vincp[pltrange], 'r', lw=2) - ax.set_title('Incident voltage') - ax.set_xlabel('Frequency [Hz]') - ax.set_ylabel('Power [dB]') - ax.grid(which='both', axis='both', linestyle='-.') + markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vincp[pltrange], "-.", use_line_collection=True) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "r") + plt.setp(markerline, "markerfacecolor", "r", "markeredgecolor", "r") + ax.plot(freqs[pltrange], Vincp[pltrange], "r", lw=2) + ax.set_title("Incident voltage") + ax.set_xlabel("Frequency [Hz]") + ax.set_ylabel("Power [dB]") + ax.grid(which="both", axis="both", linestyle="-.") # Plot incident current ax = plt.subplot(gs1[1, 0]) - ax.plot(time, Iinc, 'b', lw=2, label='Vinc') - ax.set_title('Incident current') - ax.set_xlabel('Time [s]') - ax.set_ylabel('Current [A]') + ax.plot(time, Iinc, "b", lw=2, label="Vinc") + ax.set_title("Incident current") + ax.set_xlabel("Time [s]") + ax.set_ylabel("Current [A]") ax.set_xlim([0, np.amax(time)]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") # Plot frequency spectra of incident current ax = plt.subplot(gs1[1, 1]) - markerline, stemlines, baseline = ax.stem(freqs[pltrange], Iincp[pltrange], - '-.', use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'b') - plt.setp(markerline, 'markerfacecolor', 'b', 'markeredgecolor', 'b') - ax.plot(freqs[pltrange], Iincp[pltrange], 'b', lw=2) - ax.set_title('Incident current') - ax.set_xlabel('Frequency [Hz]') - ax.set_ylabel('Power [dB]') - ax.grid(which='both', axis='both', linestyle='-.') + markerline, stemlines, baseline = ax.stem(freqs[pltrange], Iincp[pltrange], "-.", use_line_collection=True) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "b") + plt.setp(markerline, "markerfacecolor", "b", "markeredgecolor", "b") + ax.plot(freqs[pltrange], Iincp[pltrange], "b", lw=2) + ax.set_title("Incident current") + ax.set_xlabel("Frequency [Hz]") + ax.set_ylabel("Power [dB]") + ax.grid(which="both", axis="both", linestyle="-.") # Plot total voltage ax = plt.subplot(gs1[2, 0]) - ax.plot(time, Vtotal, 'r', lw=2, label='Vinc') - ax.set_title('Total (incident + reflected) voltage') - ax.set_xlabel('Time [s]') - ax.set_ylabel('Voltage [V]') + ax.plot(time, Vtotal, "r", lw=2, label="Vinc") + ax.set_title("Total (incident + reflected) voltage") + ax.set_xlabel("Time [s]") + ax.set_ylabel("Voltage [V]") ax.set_xlim([0, np.amax(time)]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") # Plot frequency spectra of total voltage ax = plt.subplot(gs1[2, 1]) - markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vtotalp[pltrange], - '-.', use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'r') - plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r') - ax.plot(freqs[pltrange], Vtotalp[pltrange], 'r', lw=2) - ax.set_title('Total (incident + reflected) voltage') - ax.set_xlabel('Frequency [Hz]') - ax.set_ylabel('Power [dB]') - ax.grid(which='both', axis='both', linestyle='-.') + markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vtotalp[pltrange], "-.", use_line_collection=True) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "r") + plt.setp(markerline, "markerfacecolor", "r", "markeredgecolor", "r") + ax.plot(freqs[pltrange], Vtotalp[pltrange], "r", lw=2) + ax.set_title("Total (incident + reflected) voltage") + ax.set_xlabel("Frequency [Hz]") + ax.set_ylabel("Power [dB]") + ax.grid(which="both", axis="both", linestyle="-.") # Plot total current ax = plt.subplot(gs1[3, 0]) - ax.plot(time, Itotal, 'b', lw=2, label='Vinc') - ax.set_title('Total (incident + reflected) current') - ax.set_xlabel('Time [s]') - ax.set_ylabel('Current [A]') + ax.plot(time, Itotal, "b", lw=2, label="Vinc") + ax.set_title("Total (incident + reflected) current") + ax.set_xlabel("Time [s]") + ax.set_ylabel("Current [A]") ax.set_xlim([0, np.amax(time)]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") # Plot frequency spectra of total current ax = plt.subplot(gs1[3, 1]) - markerline, stemlines, baseline = ax.stem(freqs[pltrange], Itotalp[pltrange], - '-.', use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'b') - plt.setp(markerline, 'markerfacecolor', 'b', 'markeredgecolor', 'b') - ax.plot(freqs[pltrange], Itotalp[pltrange], 'b', lw=2) - ax.set_title('Total (incident + reflected) current') - ax.set_xlabel('Frequency [Hz]') - ax.set_ylabel('Power [dB]') - ax.grid(which='both', axis='both', linestyle='-.') + markerline, stemlines, baseline = ax.stem(freqs[pltrange], Itotalp[pltrange], "-.", use_line_collection=True) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "b") + plt.setp(markerline, "markerfacecolor", "b", "markeredgecolor", "b") + ax.plot(freqs[pltrange], Itotalp[pltrange], "b", lw=2) + ax.set_title("Total (incident + reflected) current") + ax.set_xlabel("Frequency [Hz]") + ax.set_ylabel("Power [dB]") + ax.grid(which="both", axis="both", linestyle="-.") # Plot reflected (reflected) voltage # ax = plt.subplot(gs1[4, 0]) @@ -335,69 +368,64 @@ def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp, # Figure 2 # Plot frequency spectra of s11 - fig2, ax = plt.subplots(num='Antenna parameters', figsize=(20, 12), - facecolor='w', edgecolor='w') + fig2, ax = plt.subplots(num="Antenna parameters", figsize=(20, 12), facecolor="w", edgecolor="w") gs2 = gridspec.GridSpec(2, 2, hspace=0.3) ax = plt.subplot(gs2[0, 0]) - markerline, stemlines, baseline = ax.stem(freqs[pltrange], s11[pltrange], - '-.', use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'g') - plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g') - ax.plot(freqs[pltrange], s11[pltrange], 'g', lw=2) - ax.set_title('s11') - ax.set_xlabel('Frequency [Hz]') - ax.set_ylabel('Power [dB]') + markerline, stemlines, baseline = ax.stem(freqs[pltrange], s11[pltrange], "-.", use_line_collection=True) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "g") + plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g") + ax.plot(freqs[pltrange], s11[pltrange], "g", lw=2) + ax.set_title("s11") + ax.set_xlabel("Frequency [Hz]") + ax.set_ylabel("Power [dB]") # ax.set_xlim([0, 5e9]) # ax.set_ylim([-25, 0]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") # Plot frequency spectra of s21 if s21 is not None: ax = plt.subplot(gs2[0, 1]) - markerline, stemlines, baseline = ax.stem(freqs[pltrange], s21[pltrange], - '-.', use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'g') - plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g') - ax.plot(freqs[pltrange], s21[pltrange], 'g', lw=2) - ax.set_title('s21') - ax.set_xlabel('Frequency [Hz]') - ax.set_ylabel('Power [dB]') + markerline, stemlines, baseline = ax.stem(freqs[pltrange], s21[pltrange], "-.", use_line_collection=True) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "g") + plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g") + ax.plot(freqs[pltrange], s21[pltrange], "g", lw=2) + ax.set_title("s21") + ax.set_xlabel("Frequency [Hz]") + ax.set_ylabel("Power [dB]") # ax.set_xlim([0.88e9, 1.02e9]) # ax.set_ylim([-25, 50]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") # Plot input resistance (real part of impedance) ax = plt.subplot(gs2[1, 0]) - markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].real, - '-.', use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'g') - plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g') - ax.plot(freqs[pltrange], zin[pltrange].real, 'g', lw=2) - ax.set_title('Input impedance (resistive)') - ax.set_xlabel('Frequency [Hz]') - ax.set_ylabel('Resistance [Ohms]') + markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].real, "-.", use_line_collection=True) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "g") + plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g") + ax.plot(freqs[pltrange], zin[pltrange].real, "g", lw=2) + ax.set_title("Input impedance (resistive)") + ax.set_xlabel("Frequency [Hz]") + ax.set_ylabel("Resistance [Ohms]") # ax.set_xlim([0.88e9, 1.02e9]) ax.set_ylim(bottom=0) # ax.set_ylim([0, 300]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") # Plot input reactance (imaginery part of impedance) ax = plt.subplot(gs2[1, 1]) - markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].imag, - '-.', use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'g') - plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g') - ax.plot(freqs[pltrange], zin[pltrange].imag, 'g', lw=2) - ax.set_title('Input impedance (reactive)') - ax.set_xlabel('Frequency [Hz]') - ax.set_ylabel('Reactance [Ohms]') + markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].imag, "-.", use_line_collection=True) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "g") + plt.setp(markerline, "markerfacecolor", "g", "markeredgecolor", "g") + ax.plot(freqs[pltrange], zin[pltrange].imag, "g", lw=2) + ax.set_title("Input impedance (reactive)") + ax.set_xlabel("Frequency [Hz]") + ax.set_ylabel("Reactance [Ohms]") # ax.set_xlim([0.88e9, 1.02e9]) # ax.set_ylim([-300, 300]) - ax.grid(which='both', axis='both', linestyle='-.') + ax.grid(which="both", axis="both", linestyle="-.") # Plot input admittance (magnitude) # ax = plt.subplot(gs2[2, 0]) @@ -430,49 +458,49 @@ def mpl_plot(filename, time, freqs, Vinc, Vincp, Iinc, Iincp, Vref, Vrefp, # ax.grid(which='both', axis='both', linestyle='-.') if save: - savename1 = filename.stem + '_tl_params' + savename1 = filename.stem + "_tl_params" savename1 = filename.parent / savename1 - savename2 = filename.stem + '_ant_params' + savename2 = filename.stem + "_ant_params" savename2 = filename.parent / savename2 # Save a PDF of the figure - fig1.savefig(savename1.with_suffix('.pdf'), dpi=None, format='pdf', - bbox_inches='tight', pad_inches=0.1) - fig2.savefig(savename2.with_suffix('.pdf'), dpi=None, format='pdf', - bbox_inches='tight', pad_inches=0.1) + fig1.savefig(savename1.with_suffix(".pdf"), dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1) + fig2.savefig(savename2.with_suffix(".pdf"), dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1) # Save a PNG of the figure - # fig1.savefig(savename1.with_suffix('.png'), dpi=150, format='png', + # fig1.savefig(savename1.with_suffix('.png'), dpi=150, format='png', # bbox_inches='tight', pad_inches=0.1) - # fig2.savefig(savename2.with_suffix('.png'), dpi=150, format='png', + # fig2.savefig(savename2.with_suffix('.png'), dpi=150, format='png', # bbox_inches='tight', pad_inches=0.1) return plt if __name__ == "__main__": - # Parse command line arguments - parser = argparse.ArgumentParser(description='Plots antenna parameters - ' + - 'incident, reflected and total voltages ' + - 'and currents; s11, (s21) and input impedance ' + - 'from an output file containing a transmission ' + - 'line source.', - usage='cd gprMax; python -m toolboxes.Plotting.plot_antenna_params outputfile') - parser.add_argument('outputfile', help='name of output file including path') - parser.add_argument('--tltx-num', default=1, type=int, - help='transmitter antenna - transmission line number') - parser.add_argument('--tlrx-num', type=int, - help='receiver antenna - transmission line number') - parser.add_argument('--rx-num', type=int, - help='receiver antenna - output number') - parser.add_argument('--rx-component', type=str, - help='receiver antenna - output electric field component', - choices=['Ex', 'Ey', 'Ez']) - parser.add_argument('-save', action='store_true', default=False, - help='save plot directly to file, i.e. do not display') + parser = argparse.ArgumentParser( + description="Plots antenna parameters - " + + "incident, reflected and total voltages " + + "and currents; s11, (s21) and input impedance " + + "from an output file containing a transmission " + + "line source.", + usage="cd gprMax; python -m toolboxes.Plotting.plot_antenna_params outputfile", + ) + parser.add_argument("outputfile", help="name of output file including path") + parser.add_argument("--tltx-num", default=1, type=int, help="transmitter antenna - transmission line number") + parser.add_argument("--tlrx-num", type=int, help="receiver antenna - transmission line number") + parser.add_argument("--rx-num", type=int, help="receiver antenna - output number") + parser.add_argument( + "--rx-component", + type=str, + help="receiver antenna - output electric field component", + choices=["Ex", "Ey", "Ez"], + ) + parser.add_argument( + "-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display" + ) args = parser.parse_args() - antennaparams = calculate_antenna_params(args.outputfile, args.tltx_num, - args.tlrx_num, args.rx_num, - args.rx_component) + antennaparams = calculate_antenna_params( + args.outputfile, args.tltx_num, args.tlrx_num, args.rx_num, args.rx_component + ) plthandle = mpl_plot(args.outputfile, **antennaparams, save=args.save) plthandle.show() diff --git a/toolboxes/Plotting/plot_source_wave.py b/toolboxes/Plotting/plot_source_wave.py index ba4cfaf5..cd838143 100644 --- a/toolboxes/Plotting/plot_source_wave.py +++ b/toolboxes/Plotting/plot_source_wave.py @@ -25,7 +25,7 @@ import numpy as np from gprMax.utilities.utilities import fft_power, round_value from gprMax.waveforms import Waveform -logging.basicConfig(format='%(message)s', level=logging.INFO) +logging.basicConfig(format="%(message)s", level=logging.INFO) def check_timewindow(timewindow, dt): @@ -53,7 +53,7 @@ def check_timewindow(timewindow, dt): if timewindow > 0: iterations = round_value((timewindow / dt)) + 1 else: - logging.exception('Time window must have a value greater than zero') + logging.exception("Time window must have a value greater than zero") raise ValueError return timewindow, iterations @@ -76,36 +76,41 @@ def mpl_plot(w, timewindow, dt, iterations, fft=False, save=False): time = np.linspace(0, (iterations - 1) * dt, num=iterations) waveform = np.zeros(len(time)) - timeiter = np.nditer(time, flags=['c_index']) + timeiter = np.nditer(time, flags=["c_index"]) while not timeiter.finished: waveform[timeiter.index] = w.calculate_value(timeiter[0], dt) timeiter.iternext() - logging.info('Waveform characteristics...') - logging.info(f'Type: {w.type}') - logging.info(f'Maximum (absolute) amplitude: {np.max(np.abs(waveform)):g}') + logging.info("Waveform characteristics...") + logging.info(f"Type: {w.type}") + logging.info(f"Maximum (absolute) amplitude: {np.max(np.abs(waveform)):g}") - if w.freq and not w.type == 'gaussian' and not w.type == 'impulse': - logging.info(f'Centre frequency: {w.freq:g} Hz') + if w.freq and not w.type == "gaussian" and not w.type == "impulse": + logging.info(f"Centre frequency: {w.freq:g} Hz") - if (w.type == 'gaussian' or w.type == 'gaussiandot' or w.type == 'gaussiandotnorm' - or w.type == 'gaussianprime' or w.type == 'gaussiandoubleprime'): + if ( + w.type == "gaussian" + or w.type == "gaussiandot" + or w.type == "gaussiandotnorm" + or w.type == "gaussianprime" + or w.type == "gaussiandoubleprime" + ): delay = 1 / w.freq - logging.info(f'Time to centre of pulse: {delay:g} s') - elif w.type == 'gaussiandotdot' or w.type == 'gaussiandotdotnorm' or w.type == 'ricker': + logging.info(f"Time to centre of pulse: {delay:g} s") + elif w.type == "gaussiandotdot" or w.type == "gaussiandotdotnorm" or w.type == "ricker": delay = np.sqrt(2) / w.freq - logging.info(f'Time to centre of pulse: {delay:g} s') + logging.info(f"Time to centre of pulse: {delay:g} s") - logging.info(f'Time window: {timewindow:g} s ({iterations} iterations)') - logging.info(f'Time step: {dt:g} s') + logging.info(f"Time window: {timewindow:g} s ({iterations} iterations)") + logging.info(f"Time step: {dt:g} s") if fft: # FFT freqs, power = fft_power(waveform, dt) # Set plotting range to 4 times frequency at max power of waveform or - # 4 times the centre frequency + # 4 times the centre frequency freqmaxpower = np.where(np.isclose(power, 0))[0][0] if freqs[freqmaxpower] > w.freq: pltrange = np.where(freqs > 4 * freqs[freqmaxpower])[0][0] @@ -113,73 +118,66 @@ def mpl_plot(w, timewindow, dt, iterations, fft=False, save=False): pltrange = np.where(freqs > 4 * w.freq)[0][0] pltrange = np.s_[0:pltrange] - fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, num=w.type, - figsize=(20, 10), facecolor='w', - edgecolor='w') + fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, num=w.type, figsize=(20, 10), facecolor="w", edgecolor="w") # Plot waveform - ax1.plot(time, waveform, 'r', lw=2) - ax1.set_xlabel('Time [s]') - ax1.set_ylabel('Amplitude') + ax1.plot(time, waveform, "r", lw=2) + ax1.set_xlabel("Time [s]") + ax1.set_ylabel("Amplitude") # Plot frequency spectra - markerline, stemlines, baseline = ax2.stem(freqs[pltrange], power[pltrange], - '-.', use_line_collection=True) - plt.setp(baseline, 'linewidth', 0) - plt.setp(stemlines, 'color', 'r') - plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r') - ax2.plot(freqs[pltrange], power[pltrange], 'r', lw=2) - ax2.set_xlabel('Frequency [Hz]') - ax2.set_ylabel('Power [dB]') + markerline, stemlines, baseline = ax2.stem(freqs[pltrange], power[pltrange], "-.", use_line_collection=True) + plt.setp(baseline, "linewidth", 0) + plt.setp(stemlines, "color", "r") + plt.setp(markerline, "markerfacecolor", "r", "markeredgecolor", "r") + ax2.plot(freqs[pltrange], power[pltrange], "r", lw=2) + ax2.set_xlabel("Frequency [Hz]") + ax2.set_ylabel("Power [dB]") else: - fig, ax1 = plt.subplots(num=w.type, figsize=(10, 10), facecolor='w', - edgecolor='w') + fig, ax1 = plt.subplots(num=w.type, figsize=(10, 10), facecolor="w", edgecolor="w") # Plot waveform - ax1.plot(time, waveform, 'r', lw=2) - ax1.set_xlabel('Time [s]') - ax1.set_ylabel('Amplitude') + ax1.plot(time, waveform, "r", lw=2) + ax1.set_xlabel("Time [s]") + ax1.set_ylabel("Amplitude") # Turn on grid - [ax.grid(which='both', axis='both', linestyle='-.') for ax in fig.axes] + [ax.grid(which="both", axis="both", linestyle="-.") for ax in fig.axes] if save: savefile = Path(__file__).parent / w.type # Save a PDF of the figure - fig.savefig(savefile.with_suffix('.pdf'), dpi=None, format='pdf', - bbox_inches='tight', pad_inches=0.1) + fig.savefig(savefile.with_suffix(".pdf"), dpi=None, format="pdf", bbox_inches="tight", pad_inches=0.1) # Save a PNG of the figure - fig.savefig(savefile.with_suffix('.png'), dpi=150, format='png', - bbox_inches='tight', pad_inches=0.1) + fig.savefig(savefile.with_suffix(".png"), dpi=150, format="png", bbox_inches="tight", pad_inches=0.1) return plt if __name__ == "__main__": - # Parse command line arguments - parser = argparse.ArgumentParser(description='Plot built-in waveforms that can be used for sources.', - usage='cd gprMax; python -m toolboxes.Plotting.plot_source_wave type amp freq timewindow dt') - parser.add_argument('type', help='type of waveform', choices=Waveform.types) - parser.add_argument('amp', type=float, help='amplitude of waveform') - parser.add_argument('freq', type=float, help='centre frequency of waveform') - parser.add_argument('timewindow', help='time window to view waveform') - parser.add_argument('dt', type=float, help='time step to view waveform') - parser.add_argument('-fft', action='store_true', default=False, - help='plot FFT of waveform') - parser.add_argument('-save', action='store_true', default=False, - help='save plot directly to file, i.e. do not display') + parser = argparse.ArgumentParser( + description="Plot built-in waveforms that can be used for sources.", + usage="cd gprMax; python -m toolboxes.Plotting.plot_source_wave type amp freq timewindow dt", + ) + parser.add_argument("type", help="type of waveform", choices=Waveform.types) + parser.add_argument("amp", type=float, help="amplitude of waveform") + parser.add_argument("freq", type=float, help="centre frequency of waveform") + parser.add_argument("timewindow", help="time window to view waveform") + parser.add_argument("dt", type=float, help="time step to view waveform") + parser.add_argument("-fft", action="store_true", default=False, help="plot FFT of waveform") + parser.add_argument( + "-save", action="store_true", default=False, help="save plot directly to file, i.e. do not display" + ) args = parser.parse_args() # Check waveform parameters if args.type.lower() not in Waveform.types: - logging.exception(f"The waveform must have one of the following types " + - f"{', '.join(Waveform.types)}") + logging.exception(f"The waveform must have one of the following types " + f"{', '.join(Waveform.types)}") raise ValueError if args.freq <= 0: - logging.exception('The waveform requires an excitation frequency value of ' + - 'greater than zero') + logging.exception("The waveform requires an excitation frequency value of " + "greater than zero") raise ValueError # Create waveform instance @@ -189,6 +187,5 @@ if __name__ == "__main__": w.freq = args.freq timewindow, iterations = check_timewindow(args.timewindow, args.dt) - plthandle = mpl_plot(w, timewindow, args.dt, iterations, fft=args.fft, - save=args.save) + plthandle = mpl_plot(w, timewindow, args.dt, iterations, fft=args.fft, save=args.save) plthandle.show() diff --git a/toolboxes/STLtoVoxel/README.rst b/toolboxes/STLtoVoxel/README.rst index b075597f..05322f52 100644 --- a/toolboxes/STLtoVoxel/README.rst +++ b/toolboxes/STLtoVoxel/README.rst @@ -9,7 +9,7 @@ Information **Author/Contact**: Kartik Bansal (kartikbn21000@gmail.com) -This package provides the ability to directly model real objects without having to build their geometries manually using geometry primitives such as the ``#edge``, ``#plate``, ``#box`` etc.. commands. It specifically provides a tool to convert a `STL file `_, which can be produced by many CAD software packages, to a voxelised mesh (FDTD Yee cells) which is saved as a geometry file in HDF5 format suitable for directly importing into gprMax. +This package provides the ability to directly model real objects without having to build their geometries manually using geometry primitives such as the ``#edge``, ``#plate``, ``#box`` etc.. commands. It specifically provides a tool to convert a `STL file `_, which can be produced by many CAD software packages, to a voxelised mesh (FDTD Yee cells) which is saved as a geometry file in HDF5 format suitable for directly importing into gprMax. This package was created as part of the `Google Summer of Code `_ programme 2021 which gprMax participated. The package uses the `stl-to-voxel `_ Python library by Christian Pederkoff. @@ -67,4 +67,4 @@ The following Python script (using our Python API) can be used to import the gen .. figure:: ../../images_shared/stanford_bunny.png :width: 600 px - FDTD geometry mesh showing the Stanford bunny \ No newline at end of file + FDTD geometry mesh showing the Stanford bunny diff --git a/toolboxes/STLtoVoxel/convert.py b/toolboxes/STLtoVoxel/convert.py index da157532..a861ed90 100644 --- a/toolboxes/STLtoVoxel/convert.py +++ b/toolboxes/STLtoVoxel/convert.py @@ -7,12 +7,12 @@ from . import slice def convert_meshes(meshes, discretization, parallel=True): scale, shift, shape = slice.calculate_scale_shift(meshes, discretization) vol = np.zeros(shape[::-1], dtype=np.int32) - + for mesh_ind, org_mesh in enumerate(meshes): slice.scale_and_shift_mesh(org_mesh, scale, shift) cur_vol = slice.mesh_to_plane(org_mesh, shape, parallel) vol[cur_vol] = mesh_ind + 1 - + return vol, scale, shift @@ -22,12 +22,12 @@ def convert_file(input_file_path, discretization, pad=1, parallel=False): def convert_files(input_file_paths, discretization, colors=[(0, 0, 0)], pad=1, parallel=False): meshes = [] - + for input_file_path in input_file_paths: mesh_obj = mesh.Mesh.from_file(input_file_path) org_mesh = np.hstack((mesh_obj.v0[:, np.newaxis], mesh_obj.v1[:, np.newaxis], mesh_obj.v2[:, np.newaxis])) meshes.append(org_mesh) vol, scale, shift = convert_meshes(meshes, discretization, parallel) vol = np.transpose(vol) - + return vol diff --git a/toolboxes/STLtoVoxel/examples/bunny.py b/toolboxes/STLtoVoxel/examples/bunny.py index 19ca8e68..74dc8a2a 100644 --- a/toolboxes/STLtoVoxel/examples/bunny.py +++ b/toolboxes/STLtoVoxel/examples/bunny.py @@ -15,7 +15,7 @@ z = 0.148 scene = gprMax.Scene() -title = gprMax.Title(name=fn.with_suffix('').name) +title = gprMax.Title(name=fn.with_suffix("").name) domain = gprMax.Domain(p1=(x, y, z)) dxdydz = gprMax.Discretisation(p1=(dl, dl, dl)) time_window = gprMax.TimeWindow(time=10e-9) @@ -25,13 +25,13 @@ scene.add(domain) scene.add(dxdydz) scene.add(time_window) -go = gprMax.GeometryObjectsRead(p1=(0.020, 0.020, 0.020), geofile='stl/Stanford_Bunny.h5', matfile= 'materials.txt') +go = gprMax.GeometryObjectsRead(p1=(0.020, 0.020, 0.020), geofile="stl/Stanford_Bunny.h5", matfile="materials.txt") + +gv = gprMax.GeometryView( + p1=(0, 0, 0), p2=domain.props.p1, dl=(dl, dl, dl), filename=fn.with_suffix("").name, output_type="n" +) -gv = gprMax.GeometryView(p1=(0, 0, 0), p2=domain.props.p1, - dl=(dl, dl, dl), filename=fn.with_suffix('').name, - output_type='n') - scene.add(go) scene.add(gv) -gprMax.run(scenes=[scene], geometry_only=True, outputfile=fn) \ No newline at end of file +gprMax.run(scenes=[scene], geometry_only=True, outputfile=fn) diff --git a/toolboxes/STLtoVoxel/examples/materials.txt b/toolboxes/STLtoVoxel/examples/materials.txt index 0a6fd99c..cf67b1d5 100644 --- a/toolboxes/STLtoVoxel/examples/materials.txt +++ b/toolboxes/STLtoVoxel/examples/materials.txt @@ -1,4 +1,4 @@ #material: 3 0 1 0 sand #material: 5 0.001 1 0 concrete #material: 2.35 0 1 0 hdpe -#material: 3 0 1 0 pcb \ No newline at end of file +#material: 3 0 1 0 pcb diff --git a/toolboxes/STLtoVoxel/perimeter.py b/toolboxes/STLtoVoxel/perimeter.py index e583626d..ea28a047 100644 --- a/toolboxes/STLtoVoxel/perimeter.py +++ b/toolboxes/STLtoVoxel/perimeter.py @@ -4,16 +4,16 @@ from functools import reduce def lines_to_voxels(line_list, pixels): current_line_indices = set() x = 0 - for (event_x, status, line_ind) in generate_line_events(line_list): + for event_x, status, line_ind in generate_line_events(line_list): while event_x - x >= 0: lines = reduce(lambda acc, cur: acc + [line_list[cur]], current_line_indices, []) paint_y_axis(lines, pixels, x) x += 1 - if status == 'start': + if status == "start": assert line_ind not in current_line_indices current_line_indices.add(line_ind) - elif status == 'end': + elif status == "end": assert line_ind in current_line_indices current_line_indices.remove(line_ind) @@ -23,14 +23,14 @@ def slope_intercept(p1, p2): x2, y2 = p2[:2] slope = (y2 - y1) / (x2 - x1) intercept = y1 - slope * x1 - + return slope, intercept def generate_y(p1, p2, x): slope, intercept = slope_intercept(p1, p2) y = slope * x + intercept - + return y @@ -41,7 +41,7 @@ def paint_y_axis(lines, pixels, x): if len(target_ys) % 2: distances = [] for i in range(len(target_ys) - 1): - distances.append(target_ys[i+1] - target_ys[i]) + distances.append(target_ys[i + 1] - target_ys[i]) # https://stackoverflow.com/a/17952763 min_idx = -min((x, -i) for i, x in enumerate(distances))[1] del target_ys[min_idx] @@ -54,15 +54,15 @@ def paint_y_axis(lines, pixels, x): pixels[target_y][x] = True is_black = not is_black yi = target_y - assert is_black is False, 'an error has occured at x%s' % x + assert is_black is False, "an error has occured at x%s" % x def generate_line_events(line_list): events = [] - + for i, line in enumerate(line_list): first, second = sorted(line, key=lambda pt: pt[0]) - events.append((first[0], 'start', i)) - events.append((second[0], 'end', i)) - + events.append((first[0], "start", i)) + events.append((second[0], "end", i)) + return sorted(events, key=lambda tup: tup[0]) diff --git a/toolboxes/STLtoVoxel/slice.py b/toolboxes/STLtoVoxel/slice.py index 8b609ca9..7767f826 100644 --- a/toolboxes/STLtoVoxel/slice.py +++ b/toolboxes/STLtoVoxel/slice.py @@ -27,13 +27,13 @@ def mesh_to_plane(mesh, bounding_box, parallel): else: pbar.update(1) _, pixels = paint_z_plane(mesh_subset, z, bounding_box[1::-1]) - vol[z]=pixels + vol[z] = pixels z += 1 - if status == 'start': + if status == "start": assert tri_ind not in current_mesh_indices current_mesh_indices.add(tri_ind) - elif status == 'end': + elif status == "end": assert tri_ind in current_mesh_indices current_mesh_indices.remove(tri_ind) @@ -61,17 +61,17 @@ def paint_z_plane(mesh, height, plane_shape): def linear_interpolation(p1, p2, distance): - ''' + """ :param p1: Point 1 :param p2: Point 2 :param distance: Between 0 and 1, Lower numbers return points closer to p1. :return: A point on the line between p1 and p2 - ''' - return p1 * (1-distance) + p2 * distance + """ + return p1 * (1 - distance) + p2 * distance def triangle_to_intersecting_lines(triangle, height, pixels, lines): - assert (len(triangle) == 3) + assert len(triangle) == 3 above = list(filter(lambda pt: pt[2] > height, triangle)) below = list(filter(lambda pt: pt[2] < height, triangle)) same = list(filter(lambda pt: pt[2] == height, triangle)) @@ -100,36 +100,36 @@ def triangle_to_intersecting_lines(triangle, height, pixels, lines): def where_line_crosses_z(p1, p2, z): - if (p1[2] > p2[2]): + if p1[2] > p2[2]: p1, p2 = p2, p1 # now p1 is below p2 in z if p2[2] == p1[2]: distance = 0 else: distance = (z - p1[2]) / (p2[2] - p1[2]) - + return linear_interpolation(p1, p2, distance) def calculate_scale_shift(meshes, discretization): mesh_min = meshes[0].min(axis=(0, 1)) mesh_max = meshes[0].max(axis=(0, 1)) - + for mesh in meshes[1:]: mesh_min = np.minimum(mesh_min, mesh.min(axis=(0, 1))) mesh_max = np.maximum(mesh_max, mesh.max(axis=(0, 1))) - amplitude = mesh_max - mesh_min - #Standard Unit of STL is mm - vx=discretization[0]*1000 - vy=discretization[1]*1000 - vz=discretization[2]*1000 - - bx=int(amplitude[0]/vx) - by=int(amplitude[1]/vy) - bz=int(amplitude[2]/vz) - bounding_box = [bx+1, by+1, bz+1] - - return max(1/vx,1/vy,1/vz), mesh_min, bounding_box + amplitude = mesh_max - mesh_min + # Standard Unit of STL is mm + vx = discretization[0] * 1000 + vy = discretization[1] * 1000 + vz = discretization[2] * 1000 + + bx = int(amplitude[0] / vx) + by = int(amplitude[1] / vy) + bz = int(amplitude[2] / vz) + bounding_box = [bx + 1, by + 1, bz + 1] + + return max(1 / vx, 1 / vy, 1 / vz), mesh_min, bounding_box def scale_and_shift_mesh(mesh, scale, shift): @@ -142,7 +142,7 @@ def generate_tri_events(mesh): events = [] for i, tri in enumerate(mesh): bottom, middle, top = sorted(tri, key=lambda pt: pt[2]) - events.append((bottom[2], 'start', i)) - events.append((top[2], 'end', i)) - + events.append((bottom[2], "start", i)) + events.append((top[2], "end", i)) + return sorted(events, key=lambda tup: tup[0]) diff --git a/toolboxes/STLtoVoxel/stltovoxel.py b/toolboxes/STLtoVoxel/stltovoxel.py index 62311a27..7146c3a6 100644 --- a/toolboxes/STLtoVoxel/stltovoxel.py +++ b/toolboxes/STLtoVoxel/stltovoxel.py @@ -7,33 +7,35 @@ import h5py from .convert import convert_file logger = logging.getLogger(__name__) -logging.basicConfig(format='%(message)s', level=logging.INFO) +logging.basicConfig(format="%(message)s", level=logging.INFO) if __name__ == "__main__": - # Parse command line arguments - parser = argparse.ArgumentParser(description='Allows the user to convert a STL files to voxelized mesh.', usage='cd gprMax; python -m user_libs.STLtoVoxel.stltovoxel stlfilename matindex dx_dy_dz') - parser.add_argument('stlfilename', help='name of STL file to convert including path') - parser.add_argument('-matindex', type=int, required=True, - help='index of material to extract from STL file') - parser.add_argument('-dxdydz', nargs='+', type=float, required=True, - help='discretisation to use in voxelisation process') + parser = argparse.ArgumentParser( + description="Allows the user to convert a STL files to voxelized mesh.", + usage="cd gprMax; python -m user_libs.STLtoVoxel.stltovoxel stlfilename matindex dx_dy_dz", + ) + parser.add_argument("stlfilename", help="name of STL file to convert including path") + parser.add_argument("-matindex", type=int, required=True, help="index of material to extract from STL file") + parser.add_argument( + "-dxdydz", nargs="+", type=float, required=True, help="discretisation to use in voxelisation process" + ) args = parser.parse_args() filename_stl = Path(args.stlfilename) dxdydz = tuple(args.dxdydz) - logger.info(f'\nConverting STL file: {filename_stl.name}') + logger.info(f"\nConverting STL file: {filename_stl.name}") model_array = convert_file(filename_stl, dxdydz) - model_array[model_array==0] = -1 - model_array[model_array==1] = args.matindex - logger.info(f'Number of voxels: {model_array.shape[0]} x {model_array.shape[1]} x {model_array.shape[2]}') - logger.info(f'Spatial discretisation: {dxdydz[0]} x {dxdydz[1]} x {dxdydz[2]}m') - - # Write HDF5 file for gprMax using voxels - filename_hdf5 = filename_stl.with_suffix('.h5') - with h5py.File(filename_hdf5, 'w') as f: - f.create_dataset('data', data=model_array) - f.attrs['dx_dy_dz'] = (dxdydz[0], dxdydz[1], dxdydz[2]) + model_array[model_array == 0] = -1 + model_array[model_array == 1] = args.matindex + logger.info(f"Number of voxels: {model_array.shape[0]} x {model_array.shape[1]} x {model_array.shape[2]}") + logger.info(f"Spatial discretisation: {dxdydz[0]} x {dxdydz[1]} x {dxdydz[2]}m") - logger.info(f'Written geometry object file: {filename_hdf5.name}') \ No newline at end of file + # Write HDF5 file for gprMax using voxels + filename_hdf5 = filename_stl.with_suffix(".h5") + with h5py.File(filename_hdf5, "w") as f: + f.create_dataset("data", data=model_array) + f.attrs["dx_dy_dz"] = (dxdydz[0], dxdydz[1], dxdydz[2]) + + logger.info(f"Written geometry object file: {filename_hdf5.name}") diff --git a/toolboxes/Utilities/HPC/gprmax_omp.sh b/toolboxes/Utilities/HPC/gprmax_omp.sh index 2329dfa9..80dddd06 100644 --- a/toolboxes/Utilities/HPC/gprmax_omp.sh +++ b/toolboxes/Utilities/HPC/gprmax_omp.sh @@ -28,4 +28,4 @@ export OMP_NUM_THREADS=16 ### Run gprMax with input file cd $HOME/gprMax -python -m gprMax mymodel.in -n 10 \ No newline at end of file +python -m gprMax mymodel.in -n 10 diff --git a/toolboxes/Utilities/Paraview/gprMax.py b/toolboxes/Utilities/Paraview/gprMax.py index a72017d4..52530cf9 100644 --- a/toolboxes/Utilities/Paraview/gprMax.py +++ b/toolboxes/Utilities/Paraview/gprMax.py @@ -19,15 +19,25 @@ import json import os -from paraview.simple import (AppendDatasets, Box, GetActiveSource, - GetActiveView, GetParaViewVersion, Hide, - OpenDataFile, RenameSource, RenderAllViews, - SetActiveSource, Show, Threshold) +from paraview.simple import ( + AppendDatasets, + Box, + GetActiveSource, + GetActiveView, + GetParaViewVersion, + Hide, + OpenDataFile, + RenameSource, + RenderAllViews, + SetActiveSource, + Show, + Threshold, +) def threshold_filt(input, lt, ut, scalars): """Create threshold filter according to Paraview version. - + Args: input (array): input data to threshold filter lt, ut (int): lower and upper bounds of thresholding operation @@ -42,7 +52,7 @@ def threshold_filt(input, lt, ut, scalars): threshold = Threshold(Input=input) threshold.Scalars = scalars - + if pvv.major == 5 and pvv.minor < 10: threshold.ThresholdRange = [lt, ut] else: @@ -62,76 +72,88 @@ def display_pmls(pmlthick, dx_dy_dz, nx_ny_nz): nx_ny_dz (tuple): Domain size (cells) """ - pml_names = ['x0', 'y0', 'z0', 'xmax', 'ymax', 'zmax'] + pml_names = ["x0", "y0", "z0", "xmax", "ymax", "zmax"] pmls = dict.fromkeys(pml_names, None) SetActiveSource(pv_src) if pmlthick[0] != 0: - x0 = Box(Center=[pmlthick[0] * dx_dy_dz[0] / 2, - nx_ny_nz[1] * dx_dy_dz[1] / 2, - nx_ny_nz[2] * dx_dy_dz[2] / 2], - XLength=pmlthick[0] * dx_dy_dz[0], - YLength=nx_ny_nz[1] * dx_dy_dz[1], - ZLength=nx_ny_nz[2] * dx_dy_dz[2]) - pmls['x0'] = x0 + x0 = Box( + Center=[pmlthick[0] * dx_dy_dz[0] / 2, nx_ny_nz[1] * dx_dy_dz[1] / 2, nx_ny_nz[2] * dx_dy_dz[2] / 2], + XLength=pmlthick[0] * dx_dy_dz[0], + YLength=nx_ny_nz[1] * dx_dy_dz[1], + ZLength=nx_ny_nz[2] * dx_dy_dz[2], + ) + pmls["x0"] = x0 if pmlthick[3] != 0: - xmax = Box(Center=[dx_dy_dz[0] * (nx_ny_nz[0] - pmlthick[3] / 2), - nx_ny_nz[1] * dx_dy_dz[1] / 2, - nx_ny_nz[2] * dx_dy_dz[2] / 2], - XLength=pmlthick[3] * dx_dy_dz[0], - YLength=nx_ny_nz[1] * dx_dy_dz[1], - ZLength=nx_ny_nz[2] * dx_dy_dz[2]) - pmls['xmax'] = xmax + xmax = Box( + Center=[ + dx_dy_dz[0] * (nx_ny_nz[0] - pmlthick[3] / 2), + nx_ny_nz[1] * dx_dy_dz[1] / 2, + nx_ny_nz[2] * dx_dy_dz[2] / 2, + ], + XLength=pmlthick[3] * dx_dy_dz[0], + YLength=nx_ny_nz[1] * dx_dy_dz[1], + ZLength=nx_ny_nz[2] * dx_dy_dz[2], + ) + pmls["xmax"] = xmax if pmlthick[1] != 0: - y0 = Box(Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2, - pmlthick[1] * dx_dy_dz[1] / 2, - nx_ny_nz[2] * dx_dy_dz[2] / 2], - XLength=nx_ny_nz[0] * dx_dy_dz[0], - YLength=pmlthick[1] * dx_dy_dz[1], - ZLength=nx_ny_nz[2] * dx_dy_dz[2]) - pmls['y0'] = y0 + y0 = Box( + Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2, pmlthick[1] * dx_dy_dz[1] / 2, nx_ny_nz[2] * dx_dy_dz[2] / 2], + XLength=nx_ny_nz[0] * dx_dy_dz[0], + YLength=pmlthick[1] * dx_dy_dz[1], + ZLength=nx_ny_nz[2] * dx_dy_dz[2], + ) + pmls["y0"] = y0 if pmlthick[4] != 0: - ymax = Box(Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2, - dx_dy_dz[1] * (nx_ny_nz[1] - pmlthick[4] / 2), - nx_ny_nz[2] * dx_dy_dz[2] / 2], - XLength=nx_ny_nz[0] * dx_dy_dz[0], - YLength=pmlthick[4] * dx_dy_dz[1], - ZLength=nx_ny_nz[2] * dx_dy_dz[2]) - pmls['ymax'] = ymax + ymax = Box( + Center=[ + nx_ny_nz[0] * dx_dy_dz[0] / 2, + dx_dy_dz[1] * (nx_ny_nz[1] - pmlthick[4] / 2), + nx_ny_nz[2] * dx_dy_dz[2] / 2, + ], + XLength=nx_ny_nz[0] * dx_dy_dz[0], + YLength=pmlthick[4] * dx_dy_dz[1], + ZLength=nx_ny_nz[2] * dx_dy_dz[2], + ) + pmls["ymax"] = ymax if pmlthick[2] != 0: - z0 = Box(Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2, - nx_ny_nz[1] * dx_dy_dz[1] / 2, - pmlthick[2] * dx_dy_dz[2] / 2], - XLength=nx_ny_nz[0] * dx_dy_dz[0], - YLength=nx_ny_nz[1] * dx_dy_dz[1], - ZLength=pmlthick[2] * dx_dy_dz[2]) - pmls['z0'] = z0 + z0 = Box( + Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2, nx_ny_nz[1] * dx_dy_dz[1] / 2, pmlthick[2] * dx_dy_dz[2] / 2], + XLength=nx_ny_nz[0] * dx_dy_dz[0], + YLength=nx_ny_nz[1] * dx_dy_dz[1], + ZLength=pmlthick[2] * dx_dy_dz[2], + ) + pmls["z0"] = z0 if pmlthick[5] != 0: - zmax = Box(Center=[nx_ny_nz[0] * dx_dy_dz[0] / 2, - nx_ny_nz[1] * dx_dy_dz[1] / 2, - dx_dy_dz[2] * (nx_ny_nz[2] - pmlthick[5] / 2)], - XLength=nx_ny_nz[0] * dx_dy_dz[0], - YLength=nx_ny_nz[1] * dx_dy_dz[1], - ZLength=pmlthick[5] * dx_dy_dz[2]) - pmls['zmax'] = zmax + zmax = Box( + Center=[ + nx_ny_nz[0] * dx_dy_dz[0] / 2, + nx_ny_nz[1] * dx_dy_dz[1] / 2, + dx_dy_dz[2] * (nx_ny_nz[2] - pmlthick[5] / 2), + ], + XLength=nx_ny_nz[0] * dx_dy_dz[0], + YLength=nx_ny_nz[1] * dx_dy_dz[1], + ZLength=pmlthick[5] * dx_dy_dz[2], + ) + pmls["zmax"] = zmax # Name PML sources and set opacity tmp = [] for pml in pmls: if pmls[pml]: - RenameSource('PML - ' + pml, pmls[pml]) + RenameSource("PML - " + pml, pmls[pml]) Hide(pmls[pml], pv_view) tmp.append(pmls[pml]) # Create a group of PMLs to switch on/off easily if tmp: pml_gp = AppendDatasets(Input=tmp) - RenameSource('PML - All', pml_gp) + RenameSource("PML - All", pml_gp) pml_view = Show(pml_gp) pml_view.Opacity = 0.5 @@ -148,7 +170,7 @@ file = data.FileName dirname = os.path.dirname(file[0]) # Read and display data from file, i.e. materials, sources, receivers, and PMLs -with open(file, 'rb') as f: +with open(file, "rb") as f: # Comments () embedded in line 3 of file f.readline() f.readline() @@ -157,14 +179,14 @@ with open(file, 'rb') as f: c = c[5:-5] # Model information c = json.loads(c) - print('\ngprMax version: ' + c['gprMax_version']) - print(file) + print("\ngprMax version: " + c["gprMax_version"]) + print(file) ################ # Display data # ################ pv_view = GetActiveView() -pv_view.AxesGrid.Visibility = 1 # Show Data Axes Grid +pv_view.AxesGrid.Visibility = 1 # Show Data Axes Grid pv_data = OpenDataFile(file) pv_disp = Show(pv_data, pv_view) pv_src = GetActiveSource() @@ -173,60 +195,64 @@ src_name = os.path.split(file) RenameSource(src_name[1]) # Discretisation -dl = c['dx_dy_dz'] +dl = c["dx_dy_dz"] # Number of voxels -nl = c['nx_ny_nz'] +nl = c["nx_ny_nz"] # Materials try: - for i, mat in enumerate(c['Materials']): - threshold = threshold_filt(pv_src, i, i, ['CELLS', 'Material']) + for i, mat in enumerate(c["Materials"]): + threshold = threshold_filt(pv_src, i, i, ["CELLS", "Material"]) RenameSource(mat, threshold) # Show data in view, except for free_space if i != 1: thresholddisplay = Show(threshold, pv_view) - thresholddisplay.ColorArrayName = ['CELLS', 'Material'] + thresholddisplay.ColorArrayName = ["CELLS", "Material"] threshold.UpdatePipeline() except KeyError: - print('No materials to load') + print("No materials to load") # Display any sources try: - for item in c['Sources']: - pos = item['position'] - name = item['name'] - src = Box(Center=[pos[0] + dl[0]/2, - pos[1] + dl[1]/2, - pos[2] + dl[2]/2], - XLength=dl[0], YLength=dl[1], ZLength=dl[2]) + for item in c["Sources"]: + pos = item["position"] + name = item["name"] + src = Box( + Center=[pos[0] + dl[0] / 2, pos[1] + dl[1] / 2, pos[2] + dl[2] / 2], + XLength=dl[0], + YLength=dl[1], + ZLength=dl[2], + ) RenameSource(name, src) Show(src) except KeyError: - print('No sources to load') + print("No sources to load") # Display any receivers try: - for item in c['Receivers']: - pos = item['position'] - name = item['name'] - rx = Box(Center=[pos[0] + dl[0]/2, - pos[1] + dl[1]/2, - pos[2] + dl[2]/2], - XLength=dl[0], YLength=dl[1], ZLength=dl[2]) + for item in c["Receivers"]: + pos = item["position"] + name = item["name"] + rx = Box( + Center=[pos[0] + dl[0] / 2, pos[1] + dl[1] / 2, pos[2] + dl[2] / 2], + XLength=dl[0], + YLength=dl[1], + ZLength=dl[2], + ) RenameSource(name, rx) Show(rx) except KeyError: - print('No receivers to load') + print("No receivers to load") # Display any PMLs try: - pt = c['PMLthickness'] + pt = c["PMLthickness"] display_pmls(pt, dl, nl) except KeyError: - print('No PMLs to load') + print("No PMLs to load") + - RenderAllViews() # Reset view to fit data diff --git a/toolboxes/Utilities/README.rst b/toolboxes/Utilities/README.rst index 0417737c..c1c395b1 100644 --- a/toolboxes/Utilities/README.rst +++ b/toolboxes/Utilities/README.rst @@ -74,4 +74,4 @@ gprMax produces a separate output file for each trace (A-scan) in a B-scan. This where: * ``basefilename`` is the base name file of the output file series, e.g. for ``myoutput1.h5``, ``myoutput2.h5`` the base file name would be ``myoutput`` -* ``remove-files`` is an optional argument (flag) that when given will remove the separate output files after the merge. \ No newline at end of file +* ``remove-files`` is an optional argument (flag) that when given will remove the separate output files after the merge. diff --git a/toolboxes/Utilities/convert_png2h5.py b/toolboxes/Utilities/convert_png2h5.py index 516b9ae4..8de71dc9 100644 --- a/toolboxes/Utilities/convert_png2h5.py +++ b/toolboxes/Utilities/convert_png2h5.py @@ -39,7 +39,7 @@ class Cursor(object): """ self.im = im self.materials = materials - plt.connect('button_press_event', self) + plt.connect("button_press_event", self) def __call__(self, event): """ @@ -50,12 +50,19 @@ class Cursor(object): x, y = event.xdata, event.ydata if x is not None and y is not None: pixel = self.im[int(y), int(x)] - pixel = np.floor(pixel * 255).astype(np.int16) # Convert pixel values from float (0-1) to integer (0-255) + pixel = np.floor(pixel * 255).astype( + np.int16 + ) # Convert pixel values from float (0-1) to integer (0-255) match = pixel_match(materials, pixel) if match is False: - logger.info('x, y: {} {} px; RGB: {}; material ID: {}'.format(int(x), int(y), pixel[:-1], len(self.materials))) + logger.info( + "x, y: {} {} px; RGB: {}; material ID: {}".format( + int(x), int(y), pixel[:-1], len(self.materials) + ) + ) materials.append(pixel) + def pixel_match(pixellist, pixeltest): """Checks if the RGB(A) value of a pixel already exists in a list of pixel values. @@ -75,31 +82,39 @@ def pixel_match(pixellist, pixeltest): if __name__ == "__main__": - # Parse command line arguments - parser = argparse.ArgumentParser(description='Convert a PNG image to a HDF5 file that can be used to import geometry (#geometry_objects_read) into a 2D gprMax model. Colours from the image are selected which correspond to a list of materials that should be supplied in a separate text file.', usage='python convert_png2h5.py imagefile dx dy dz') - parser.add_argument('imagefile', help='name of image file including path') - parser.add_argument('dxdydz', type=float, action='append', nargs=3, help='spatial resolution of model, e.g. dx dy dz') - parser.add_argument('-zcells', default=1, type=int, help='number of cells for domain in z-direction (infinite direction)') + parser = argparse.ArgumentParser( + description="Convert a PNG image to a HDF5 file that can be used to import geometry (#geometry_objects_read) into a 2D gprMax model. Colours from the image are selected which correspond to a list of materials that should be supplied in a separate text file.", + usage="python convert_png2h5.py imagefile dx dy dz", + ) + parser.add_argument("imagefile", help="name of image file including path") + parser.add_argument( + "dxdydz", type=float, action="append", nargs=3, help="spatial resolution of model, e.g. dx dy dz" + ) + parser.add_argument( + "-zcells", default=1, type=int, help="number of cells for domain in z-direction (infinite direction)" + ) args = parser.parse_args() # Open image file im = mpimg.imread(args.imagefile) # Store image data to use for creating geometry - imdata = np.rot90(im, k=3) # Rotate 90CW - imdata = np.floor(imdata * 255).astype(np.int16) # Convert pixel values from float (0-1) to integer (0-255) + imdata = np.rot90(im, k=3) # Rotate 90CW + imdata = np.floor(imdata * 255).astype(np.int16) # Convert pixel values from float (0-1) to integer (0-255) - logger.info('Reading PNG image file: {}'.format(os.path.split(args.imagefile)[1])) - logger.info(' 1. Select discrete material colours by clicking on parts of the image.\n 2. When all materials have been selected close the image.') + logger.info("Reading PNG image file: {}".format(os.path.split(args.imagefile)[1])) + logger.info( + " 1. Select discrete material colours by clicking on parts of the image.\n 2. When all materials have been selected close the image." + ) # List to hold selected RGB values from image materials = [] # Plot image and record rgb values from mouse clicks - fig = plt.figure(num=os.path.split(args.imagefile)[1], facecolor='w', edgecolor='w') - im = np.flipud(im) # Flip image for viewing with origin in lower left - plt.imshow(im, interpolation='nearest', aspect='equal', origin='lower') + fig = plt.figure(num=os.path.split(args.imagefile)[1], facecolor="w", edgecolor="w") + im = np.flipud(im) # Flip image for viewing with origin in lower left + plt.imshow(im, interpolation="nearest", aspect="equal", origin="lower") Cursor(im, materials) plt.show() @@ -107,23 +122,22 @@ if __name__ == "__main__": dx_dy_dz = (args.dxdydz[0][0], args.dxdydz[0][1], args.dxdydz[0][2]) # Filename for geometry (HDF5) file - hdf5file = os.path.splitext(args.imagefile)[0] + '.h5' + hdf5file = os.path.splitext(args.imagefile)[0] + ".h5" # Array to store geometry data (initialised as background, i.e. -1) data = np.ones((imdata.shape[0], imdata.shape[1], args.zcells), dtype=np.int16) * -1 # Write geometry (HDF5) file - with h5py.File(hdf5file, 'w') as fout: - + with h5py.File(hdf5file, "w") as fout: # Add attribute with name 'dx_dy_dz' for spatial resolution - fout.attrs['dx_dy_dz'] = dx_dy_dz + fout.attrs["dx_dy_dz"] = dx_dy_dz # Use a boolean mask to match selected pixel values with position in image for i, material in enumerate(materials): mask = np.all(imdata == material, axis=-1) - data[mask,:] = i + data[mask, :] = i # Write data to file - fout.create_dataset('data', data=data) + fout.create_dataset("data", data=data) - logger.info('Written HDF5 file: {}'.format(os.path.split(hdf5file)[1])) + logger.info("Written HDF5 file: {}".format(os.path.split(hdf5file)[1])) diff --git a/toolboxes/Utilities/get_host_spec.py b/toolboxes/Utilities/get_host_spec.py index 97ba465a..aa7c1412 100644 --- a/toolboxes/Utilities/get_host_spec.py +++ b/toolboxes/Utilities/get_host_spec.py @@ -20,30 +20,37 @@ import logging import humanize -from gprMax.utilities.host_info import (detect_cuda_gpus, detect_opencl, - get_host_info, print_cuda_info, - print_opencl_info) +from gprMax.utilities.host_info import ( + detect_cuda_gpus, + detect_opencl, + get_host_info, + print_cuda_info, + print_opencl_info, +) from gprMax.utilities.utilities import get_terminal_width -logging.basicConfig(format='%(message)s', level=logging.INFO) - +logging.basicConfig(format="%(message)s", level=logging.INFO) + # Host machine info. hostinfo = get_host_info() -hyperthreadingstr = f", {hostinfo['logicalcores']} cores with Hyper-Threading" if hostinfo['hyperthreading'] else '' -hostname = (f"\n=== {hostinfo['hostname']}") +hyperthreadingstr = f", {hostinfo['logicalcores']} cores with Hyper-Threading" if hostinfo["hyperthreading"] else "" +hostname = f"\n=== {hostinfo['hostname']}" logging.info(f"{hostname} {'=' * (get_terminal_width() - len(hostname) - 1)}") logging.info(f"\n{'Mfr/model:':<12} {hostinfo['machineID']}") -logging.info(f"{'CPU:':<12} {hostinfo['sockets']} x {hostinfo['cpuID']} " + - f"({hostinfo['physicalcores']} cores{hyperthreadingstr})") +logging.info( + f"{'CPU:':<12} {hostinfo['sockets']} x {hostinfo['cpuID']} " + + f"({hostinfo['physicalcores']} cores{hyperthreadingstr})" +) logging.info(f"{'RAM:':<12} {humanize.naturalsize(hostinfo['ram'], True)}") logging.info(f"{'OS/Version:':<12} {hostinfo['osversion']}") # OpenMP -logging.info("\n\n=== OpenMP capabilities (gprMax will not use Hyper-Threading " + - "as there is no performance advantage)\n") +logging.info( + "\n\n=== OpenMP capabilities (gprMax will not use Hyper-Threading " + "as there is no performance advantage)\n" +) logging.info(f"{'OpenMP threads: '} {hostinfo['physicalcores']}") -# CUDA +# CUDA logging.info("\n\n=== CUDA capabilities\n") gpus = detect_cuda_gpus() if gpus: diff --git a/toolboxes/Utilities/outputfiles_merge.py b/toolboxes/Utilities/outputfiles_merge.py index 43ed574c..bd3597d7 100644 --- a/toolboxes/Utilities/outputfiles_merge.py +++ b/toolboxes/Utilities/outputfiles_merge.py @@ -44,26 +44,28 @@ def get_output_data(filename, rxnumber, rxcomponent): """ # Open output file and read some attributes - with h5py.File(filename, 'r') as f: - nrx = f.attrs['nrx'] - dt = f.attrs['dt'] + with h5py.File(filename, "r") as f: + nrx = f.attrs["nrx"] + dt = f.attrs["dt"] # Check there are any receivers if nrx == 0: - logger.exception(f'No receivers found in {filename}') + logger.exception(f"No receivers found in {filename}") raise ValueError - path = '/rxs/rx' + str(rxnumber) + '/' + path = "/rxs/rx" + str(rxnumber) + "/" availableoutputs = list(f[path].keys()) # Check if requested output is in file if rxcomponent not in availableoutputs: - logger.exception(f"{rxcomponent} output requested to plot, but the " + - f"available output for receiver 1 is " + - f"{', '.join(availableoutputs)}") + logger.exception( + f"{rxcomponent} output requested to plot, but the " + + f"available output for receiver 1 is " + + f"{', '.join(availableoutputs)}" + ) raise ValueError - outputdata = f[path + '/' + rxcomponent] + outputdata = f[path + "/" + rxcomponent] outputdata = np.array(outputdata) return outputdata, dt @@ -78,43 +80,43 @@ def merge_files(outputfiles, removefiles=False): removefiles: boolean flag to remove individual output files after merge. """ - merged_outputfile = os.path.commonprefix(outputfiles) + '_merged.h5' + merged_outputfile = os.path.commonprefix(outputfiles) + "_merged.h5" # Combined output file - fout = h5py.File(merged_outputfile, 'w') + fout = h5py.File(merged_outputfile, "w") for i, outputfile in enumerate(outputfiles): - fin = h5py.File(outputfile, 'r') - nrx = fin.attrs['nrx'] + fin = h5py.File(outputfile, "r") + nrx = fin.attrs["nrx"] # Write properties for merged file on first iteration if i == 0: - fout.attrs['gprMax'] = __version__ - fout.attrs['Iterations'] = fin.attrs['Iterations'] - fout.attrs['nx_ny_nz'] = fin.attrs['nx_ny_nz'] - fout.attrs['dx_dy_dz'] = fin.attrs['dx_dy_dz'] - fout.attrs['dt'] = fin.attrs['dt'] - fout.attrs['nsrc'] = fin.attrs['nsrc'] - fout.attrs['nrx'] = fin.attrs['nrx'] - fout.attrs['srcsteps'] = fin.attrs['srcsteps'] - fout.attrs['rxsteps'] = fin.attrs['rxsteps'] + fout.attrs["gprMax"] = __version__ + fout.attrs["Iterations"] = fin.attrs["Iterations"] + fout.attrs["nx_ny_nz"] = fin.attrs["nx_ny_nz"] + fout.attrs["dx_dy_dz"] = fin.attrs["dx_dy_dz"] + fout.attrs["dt"] = fin.attrs["dt"] + fout.attrs["nsrc"] = fin.attrs["nsrc"] + fout.attrs["nrx"] = fin.attrs["nrx"] + fout.attrs["srcsteps"] = fin.attrs["srcsteps"] + fout.attrs["rxsteps"] = fin.attrs["rxsteps"] for rx in range(1, nrx + 1): - path = '/rxs/rx' + str(rx) + path = "/rxs/rx" + str(rx) grp = fout.create_group(path) availableoutputs = list(fin[path].keys()) for output in availableoutputs: - grp.create_dataset(output, - (fout.attrs['Iterations'], len(outputfiles)), - dtype=fin[path + '/' + output].dtype) + grp.create_dataset( + output, (fout.attrs["Iterations"], len(outputfiles)), dtype=fin[path + "/" + output].dtype + ) # For all receivers for rx in range(1, nrx + 1): - path = '/rxs/rx' + str(rx) + '/' + path = "/rxs/rx" + str(rx) + "/" availableoutputs = list(fin[path].keys()) # For all receiver outputs for output in availableoutputs: - fout[path + '/' + output][:, i] = fin[path + '/' + output][:] + fout[path + "/" + output][:, i] = fin[path + "/" + output][:] fin.close() fout.close() @@ -123,19 +125,22 @@ def merge_files(outputfiles, removefiles=False): for outputfile in outputfiles: os.remove(outputfile) -if __name__ == "__main__": +if __name__ == "__main__": # Parse command line arguments - parser = argparse.ArgumentParser(description='Merges traces (A-scans) from multiple ' + - 'output files into one new file, then ' + - 'optionally removes the series of output files.', - usage='cd gprMax; python -m tools.outputfiles_merge basefilename') - parser.add_argument('basefilename', help='base name of output file series including path') - parser.add_argument('--remove-files', action='store_true', default=False, - help='flag to remove individual output files after merge') + parser = argparse.ArgumentParser( + description="Merges traces (A-scans) from multiple " + + "output files into one new file, then " + + "optionally removes the series of output files.", + usage="cd gprMax; python -m tools.outputfiles_merge basefilename", + ) + parser.add_argument("basefilename", help="base name of output file series including path") + parser.add_argument( + "--remove-files", action="store_true", default=False, help="flag to remove individual output files after merge" + ) args = parser.parse_args() - files = glob.glob(args.basefilename + '*.h5') - outputfiles = [filename for filename in files if '_merged' not in filename] + files = glob.glob(args.basefilename + "*.h5") + outputfiles = [filename for filename in files if "_merged" not in filename] outputfiles.sort(key=natural_keys) merge_files(outputfiles, removefiles=args.remove_files)