你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-06 12:36:51 +08:00
fixed merge
这个提交包含在:
10
README.rst
10
README.rst
@@ -125,11 +125,13 @@ Optional command line arguments
|
||||
|
||||
There are optional command line arguments for gprMax:
|
||||
|
||||
* ``--geometry-only`` will build a model and produce any geometry views but will not run the simulation. This option is useful for checking the geometry of the model is correct.
|
||||
* ``-n`` is used along with a integer number to specify the number of times to run the input file. This option can be used to run a series of models, e.g. to create a B-scan that uses an antenna model.
|
||||
* ``-mpi`` is a flag to turn on Message Passing Interface (MPI) task farm functionality. This option is most usefully combined with ``-n`` to allow individual models to be farmed out using MPI. For further details see the Parallel performance section (http://docs.gprmax.com/en/latest/openmp_mpi.html).
|
||||
* ``-n`` is used along with a integer number to specify the number of times to run the input file. This option can be used to run a series of models, e.g. to create a B-scan.
|
||||
* ``-mpi`` is a flag to turn on Message Passing Interface (MPI) task farm functionality. This option is most usefully combined with ``-n`` to allow individual models to be farmed out using MPI. For further details see the Parallel performance section (http://docs.gprmax.com/en/latest/openmp_mpi.html)
|
||||
* ``-benchmark`` is a flag to turn on benchmarking mode. This can be used to benchmark the threading (parallel) performance of gprMax on different hardware. For further details see the benchmarking section (http://docs.gprmax.com/en/latest/benchmarking.html)
|
||||
* ``--write-processed`` will write an input file after any Python code and include commands in the original input file have been processed
|
||||
* ``--geometry-only`` will build a model and produce any geometry views but will not run the simulation. This option is useful for checking the geometry of the model is correct.
|
||||
* ``--geometry-fixed`` can be used when running a series of models where the geometry does not change between runs, e.g. a B-scan where only sources and receivers, moved using ``#src_steps`` and ``#rx_steps``, change from run to run.
|
||||
* ``--opt-taguchi`` will run a series of simulations using a optimisation process based on Taguchi's method. For further details see the user libraries section (http://docs.gprmax.com/en/latest/user_libs_opt_taguchi.html)
|
||||
* ``--write-processed`` will write an input file after any Python code and include commands in the original input file have been processed.
|
||||
* ``-h`` or ``--help`` can be used to get help on command line options.
|
||||
|
||||
For example, to check the geometry of a model:
|
||||
|
@@ -6,12 +6,13 @@ dependencies:
|
||||
- python>=3.4
|
||||
- cython
|
||||
- h5py
|
||||
- jupyter
|
||||
- matplotlib
|
||||
- mpi4py
|
||||
- numpy
|
||||
- scipy
|
||||
- lxml
|
||||
|
||||
- pip:
|
||||
- pyfiglet
|
||||
- psutil
|
||||
|
||||
- pyfiglet
|
@@ -1,202 +0,0 @@
|
||||
***********************
|
||||
Overview of source code
|
||||
***********************
|
||||
|
||||
This section provides an overview of the source code modules and describes each of the classes and methods used in the gprMax package. The following licensing information applies to all source files unless otherwise stated::
|
||||
|
||||
Copyright (C) 2015, The University of Edinburgh.
|
||||
|
||||
Authors: Craig Warren and Antonis Giannopoulos
|
||||
|
||||
gprMax is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gprMax is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
constants.py
|
||||
============
|
||||
|
||||
Defines constants:
|
||||
|
||||
* Speed of light in vacuum :math:`c=2.9979245 \times 10^8` m/s
|
||||
* Permittivity of free space :math:`\epsilon_0=8.854187 \times 10^{-12}` F/m
|
||||
* Permeability of free space :math:`\mu_0=1.256637 \times 10^{-6}` H/m
|
||||
* Impedance of free space :math:`z_0=376.7303134` Ohms
|
||||
|
||||
Defines data types:
|
||||
|
||||
* Solid and ID arrays use 32-bit integers (0 to 4294967295)
|
||||
* Rigid arrays use 8-bit integers (the smallest available numpy type to store booleans - true/false)
|
||||
* Fractal and dispersive coefficient arrays use complex numbers (:code:`complextype`) which are represented as two :code:`floattype`
|
||||
* Main field arrays use floats (:code:`floattype`) and complex numbers (:code:`complextype`)
|
||||
* :code:`floattype` and :code:`complextype` are set to use 32-bit floats but can be changed to use 64-bit double precision if required.
|
||||
|
||||
.. automodule:: gprMax.constants
|
||||
|
||||
|
||||
exceptions.py
|
||||
=============
|
||||
|
||||
.. automodule:: gprMax.exceptions
|
||||
|
||||
|
||||
fields_update.pyx
|
||||
=================
|
||||
|
||||
.. automodule:: gprMax.fields_update
|
||||
|
||||
|
||||
fractals.py
|
||||
===========
|
||||
|
||||
.. automodule:: gprMax.fractals
|
||||
|
||||
|
||||
geometry_primitives.pyx
|
||||
=======================
|
||||
|
||||
.. automodule:: gprMax.geometry_primitives
|
||||
|
||||
|
||||
geometry_views.py
|
||||
=================
|
||||
|
||||
.. automodule:: gprMax.geometry_views
|
||||
|
||||
|
||||
gprMax.py
|
||||
===========
|
||||
|
||||
.. automodule:: gprMax.gprMax
|
||||
|
||||
grid.py
|
||||
=======
|
||||
|
||||
.. automodule:: gprMax.grid
|
||||
|
||||
|
||||
input_cmds_file.py
|
||||
==================
|
||||
|
||||
.. automodule:: gprMax.input_cmds_file
|
||||
|
||||
|
||||
input_cmds_geometry.py
|
||||
======================
|
||||
|
||||
.. automodule:: gprMax.input_cmds_geometry
|
||||
|
||||
|
||||
input_cmds_multiuse.py
|
||||
======================
|
||||
|
||||
.. automodule:: gprMax.input_cmds_multiuse
|
||||
|
||||
|
||||
input_cmds_singleuse.py
|
||||
=======================
|
||||
|
||||
.. automodule:: gprMax.input_cmds_singleuse
|
||||
|
||||
|
||||
materials.py
|
||||
============
|
||||
|
||||
.. automodule:: gprMax.materials
|
||||
|
||||
|
||||
output.py
|
||||
=========
|
||||
|
||||
.. automodule:: gprMax.output
|
||||
|
||||
|
||||
pml_1order_update.pyx
|
||||
=====================
|
||||
|
||||
.. automodule:: gprMax.pml_1order_update
|
||||
|
||||
|
||||
pml_2order_update.pyx
|
||||
=====================
|
||||
|
||||
.. automodule:: gprMax.pml_2order_update
|
||||
|
||||
|
||||
pml_call_updates.py
|
||||
===================
|
||||
|
||||
.. automodule:: gprMax.pml_call_updates
|
||||
|
||||
|
||||
pml.py
|
||||
======
|
||||
|
||||
.. automodule:: gprMax.pml
|
||||
|
||||
|
||||
receivers.py
|
||||
============
|
||||
|
||||
.. automodule:: gprMax.receivers
|
||||
|
||||
|
||||
snapshots.py
|
||||
============
|
||||
|
||||
.. automodule:: gprMax.snapshots
|
||||
|
||||
|
||||
sources.py
|
||||
==========
|
||||
|
||||
.. automodule:: gprMax.sources
|
||||
|
||||
|
||||
user_libs.antennas.py
|
||||
=====================
|
||||
|
||||
This module is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License::
|
||||
|
||||
Copyright (C) 2015, Craig Warren
|
||||
|
||||
This module is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
|
||||
|
||||
Please use the attribution at http://dx.doi.org/10.1190/1.3548506
|
||||
|
||||
.. automodule:: user_libs.antennas
|
||||
|
||||
|
||||
utilities.py
|
||||
============
|
||||
|
||||
.. automodule:: gprMax.utilities
|
||||
|
||||
|
||||
waveforms.py
|
||||
============
|
||||
|
||||
.. automodule:: gprMax.waveforms
|
||||
|
||||
|
||||
yee_cell_build.pyx
|
||||
==================
|
||||
|
||||
.. automodule:: gprMax.yee_cell_build
|
||||
|
||||
|
||||
yee_cell_setget_rigid.pyx
|
||||
=========================
|
||||
|
||||
.. automodule:: gprMax.yee_cell_setget_rigid
|
||||
|
@@ -161,7 +161,7 @@ You can now view an image of the B-scan using the command:
|
||||
|
||||
python -m tools.plot_Bscan user_models/cylinder_Bscan_2D_merged.out Ez
|
||||
|
||||
:numref:`cylinder_Bscan_results` shows the B-scan (of the :math:`E_z` field component). Again, the initial part of the signal (~0.5-1.5 ns) represents the direct wave from transmitter to receiver. Then comes a hyperbolic response from the metal cylinder.
|
||||
:numref:`cylinder_Bscan_results` shows the B-scan (of the :math:`E_z` field component). Again, the initial part of the signal (~0.5-1.5 ns) represents the direct wave from transmitter to receiver. Then comes the refelected wave (~2-3 ns) from the metal cylinder which creates the hyperbolic shape.
|
||||
|
||||
.. _cylinder_Bscan_results:
|
||||
|
||||
|
@@ -131,7 +131,7 @@ This latter option is often referred to as dielectric smoothing and has been sho
|
||||
Perfectly Matched Layer (PML) boundary conditions
|
||||
-------------------------------------------------
|
||||
|
||||
With increased research into quantitative information from GPR, it has become necessary for models to be able to have more efficient and better-performing Perfectly Matched Layer (PML) absorbing boundary conditions. Since 2005 gprMax has featured PML absorbing boundary conditions based on the uniaxial PML (UPML) [GED1998]_ formulation. A PML based on a recursive integration approach to the complex frequency shifted (CFS) PML [GIA2012]_ has been adopted in the new version of gprMax. A general formulation of this RIPML, which can be used to develop any order of PML, has been used to implement first and second order CFS stretching functions. One of the attractions of the RIPML is that it is easily applied as a correction to the field quantities after the complete FDTD grid has been updated using the standard FDTD update equations. gprMax now offers the ability (for advanced users) to customise the parameters of the PML which allows its performance to be better optimised for specific applications. Additionally, since the RIPML is media agnostic it can be used without change to problems involving dispersive and anisotropic materials. For further details see the :ref:`PML commands section <pml>`.
|
||||
With increased research into quantitative information from GPR, it has become necessary for models to be able to have more efficient and better-performing Perfectly Matched Layer (PML) absorbing boundary conditions. Since 2005 gprMax has featured PML absorbing boundary conditions based on the uniaxial PML (UPML) [GED1998]_ formulation. A PML based on a recursive integration approach to the complex frequency shifted (CFS) PML [GIA2012]_ has been adopted in the new version of gprMax. A general formulation of this RIPML, which can be used to develop any order of PML, has been used to implement first and second order CFS stretching functions. One of the attractions of the RIPML is that it is easily applied as a correction to the field quantities after the complete FDTD grid has been updated using the standard FDTD update equations. gprMax now offers the ability (for advanced users) to customise the parameters of the PML which allows its performance to be better optimised for specific applications. Additionally, since the RIPML is media agnostic it can be used without change to problems involving dispersive and anisotropic materials. For further details see the :ref:`PML commands section <pml-commands>`.
|
||||
|
||||
Open source, robust, file formats
|
||||
---------------------------------
|
||||
|
@@ -107,6 +107,6 @@ The absorbing boundary conditions (ABCs) employed in gprMax will, in general, pe
|
||||
|
||||
The cells of the RIPML, which have a user adjustable thickness, very efficiently absorb most waves that propagate in them. Although, source and output points can be specified inside these cells **it is wrong to do so** from the point of view of correct modelling. The fields inside these cells are not of interest to GPR modelling. Placing sources inside these cells could have effects that have not been studied and will certainly provide erroneous results from a GPR modeller's point of view. The requirement to keep sources and targets at least 15 cells away for the PML has to be taken into account when deciding the size of the model domain. Additionally, free space (i.e. air) should be always included above a source for at least 15-20 cells in GPR models. Obviously, the more cells there are between observation points, sources, targets and the absorbing boundaries, the better the results will be.
|
||||
|
||||
gprMax now offers the ability (for advanced users) to customise the parameters of the PML which allows its performance to be better optimised for specific applications. For further details see the :ref:`PML commands section <pml>`.
|
||||
gprMax now offers the ability (for advanced users) to customise the parameters of the PML which allows its performance to be better optimised for specific applications. For further details see the :ref:`PML commands section <pml-commands>`.
|
||||
|
||||
This user guide, cannot serve as an in-depth tutorial and review of the FDTD method. However some useful hints and tips are given in order to cover the most fundamental aspects of using a FDTD based solver and to avoid the most common errors.
|
||||
|
二进制文件未显示。
之后 宽度: | 高度: | 大小: 443 KiB |
二进制文件未显示。
之后 宽度: | 高度: | 大小: 1.1 MiB |
二进制文件未显示。
之后 宽度: | 高度: | 大小: 45 KiB |
二进制文件未显示。
之后 宽度: | 高度: | 大小: 45 KiB |
二进制文件未显示。
之后 宽度: | 高度: | 大小: 39 KiB |
二进制文件未显示。
之后 宽度: | 高度: | 大小: 92 KiB |
@@ -16,7 +16,6 @@ gprMax User Guide
|
||||
:caption: Using gprMax
|
||||
|
||||
input
|
||||
geometry_snapshots
|
||||
output
|
||||
|
||||
.. toctree::
|
||||
@@ -37,7 +36,9 @@ gprMax User Guide
|
||||
:maxdepth: 2
|
||||
:caption: User libraries
|
||||
|
||||
user_libraries
|
||||
user_libs_antennas
|
||||
user_libs_austinman
|
||||
user_libs_opt_taguchi
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
@@ -108,16 +108,16 @@ General commands
|
||||
|
||||
Allows you to write blocks of Python code between ``#python`` and ``#end_python`` in the input file. The code is executed when the input file is read by gprMax. For further details see the :ref:`Python section <python-scripting>`.
|
||||
|
||||
#include:
|
||||
---------
|
||||
#include_file:
|
||||
--------------
|
||||
|
||||
Allows you to include commands from a file. It will insert the commands from the specified file at the location where the ``#include`` command is placed. The syntax of the command is:
|
||||
Allows you to include commands from a file. It will insert the commands from the specified file at the location where the ``#include_file`` command is placed. The syntax of the command is:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
#include: str1
|
||||
#include_file: file
|
||||
|
||||
``str1`` can be the name of the file containing the commands in the same directory as the input file, or ``str1`` can be the full path to the file containing the commands (allowing you to specify any location).
|
||||
``file`` can be the name of the file containing the commands in the same directory as the input file, or ``file`` can be the full path to the file containing the commands (allowing you to specify any location).
|
||||
|
||||
|
||||
#time_step_stability_factor:
|
||||
@@ -364,6 +364,7 @@ At the boundaries between different materials in the model there is the question
|
||||
|
||||
.. note::
|
||||
|
||||
* If a material has dispersive properties then dielectric smoothing is automatically turned off for that material.
|
||||
* If an object is anistropic then dielectric smoothing is automatically turned off for that object.
|
||||
* Non-volumetric object building commands, ``#edge`` and ``#plate`` cannot have dielectric smoothing.
|
||||
|
||||
@@ -377,12 +378,12 @@ Allows you output to file(s) information about the geometry of model. The file(s
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
#geometry_view: f1 f2 f3 f4 f5 f6 f7 f8 f9 file1 c1
|
||||
#geometry_view: f1 f2 f3 f4 f5 f6 f7 f8 f9 file c1
|
||||
|
||||
* ``f1 f2 f3`` are the lower left (x,y,z) coordinates of the volume of the geometry view in metres.
|
||||
* ``f4 f5 f6`` are the upper right (x,y,z) coordinates of the volume of the geometry view in metres.
|
||||
* ``f7 f8 f9`` are the spatial discretisation of the geometry view in metres. Typically these will be the same as the spatial discretisation of the model but they can be courser if desired.
|
||||
* ``file1`` is the filename of the file where the geometry view will be stored.
|
||||
* ``file`` is the filename of the file where the geometry view will be stored in the same directory as the input file.
|
||||
* ``c1`` can be either n (normal) or f (fine) which specifies whether to output the geometry information on a per-cell basis (n) or a per-cell-edge basis (f). The fine mode should be reserved for viewing detailed parts of the geometry that occupy small volumes, as using this mode can generate geometry files with large file sizes.
|
||||
|
||||
.. tip::
|
||||
@@ -586,12 +587,35 @@ Allows you to add grass with roots to a ``#fractal_box`` in the model. The blade
|
||||
* ``str1`` is an identifier for the ``#fractal_box`` that the grass should be applied to.
|
||||
* ``i2`` is an optional parameter which controls the seeding of the random number generator used to create the fractals. By default (if you don't specify this parameter) the random number generator will be seeded by trying to read data from ``/dev/urandom`` (or the Windows analogue) if available or from the clock otherwise.
|
||||
|
||||
For example, to apply 100 blades of grass that vary in height between 100 and 150 mm to the entire surface in the positive z direction of a ``#fractal_box`` that had been specified using ``#fractal_box: 0 0 0 0.1 0.1 0.1 1.5 1 1 50 my_soil my_fractal_box``, use: ``#add_grass: 0 0 0.1 0.1 0.1 0.1 1.5 0.2 0.25 100 my_fractal_box``.
|
||||
For example, to apply 100 blades of grass that vary in height between 100 and 150 mm to the entire surface in the positive z direction of a ``#fractal_box`` that had been specified using ``#fractal_box: 0 0 0 0.1 0.1 0.1 1.5 1 1 50 my_soil my_fractal_box``, use ``#add_grass: 0 0 0.1 0.1 0.1 0.1 1.5 0.2 0.25 100 my_fractal_box``.
|
||||
|
||||
.. note::
|
||||
|
||||
* The grass is modelled using a single-pole Debye formulation with properties :math:`\epsilon_{rs} = 18.5087`, :math:`\epsilon_{\infty} = 12.7174`, and a relaxation time of :math:`\tau = 1.0793 \times 10^{-11}` seconds (http://dx.doi.org/10.1007/BF00902994). If you prefer, gprMax will use your own definition for grass if you use a material named ``grass``. The geometry of the blades of grass are defined by the parametric equations: :math:`x = x_c +s_x {\left( \frac{t}{b_x} \right)}^2`, :math:`y = y_c +s_y {\left( \frac{t}{b_y} \right)}^2`, and :math:`z=t`, where :math:`s_x` and :math:`s_y` can be -1 or 1 which are randomly chosen, and where the constants :math:`b_x` and :math:`b_y` are random numbers based on a Gaussian distribution.
|
||||
|
||||
#geometry_objects_file:
|
||||
-----------------------
|
||||
|
||||
Allows you to insert pre-defined geometry into a model. The geometry is specified using a 3D array of integer numbers stored in a HDF5 file. The integer numbers must correspond to the order of a list of ``#material`` commands specified in a text file. The syntax of the command is:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
#geometry_objects_file: f1 f2 f3 file1 file2
|
||||
|
||||
* ``f1 f2 f3`` are the lower left (x,y,z) coordinates in the domain where the lower left corner of the geometry array should be placed.
|
||||
* ``file1`` is the path to and filename of the HDF5 file that contains an integer array which defines the geometry.
|
||||
* ``file2`` is the path to and filename of the text file that contains ``#material`` commands.
|
||||
|
||||
.. note::
|
||||
|
||||
* The integer numbers in the HDF5 file must be stored as a NumPy array at the root named ``data`` with type ``np.uint16``.
|
||||
* The integer numbers in the HDF5 file correspond to the order of material commands in the materials text file, i.e. if ``#sand: 3 0 1 0`` is the first material in the materials file, it will be associated with any integers that are zero in the HDF5 file.
|
||||
* The spatial resolution of the geometry objects must match the spatial resolution defined in the model.
|
||||
* The spatial resolution of must be specified as a root attribute of the HDF5 file with the name ``dx, dy, dz`` equal to a tuple of floats, e.g. (0.002, 0.002, 0.002)
|
||||
|
||||
For example, to insert a 2x2x2mm^3 AustinMan model with the lower left corner 40mm from the origin of the domain, and using disperive material properties use ``#geometry_objects_file: 0.04 0.04 0.04 ../user_libs/AustinManWoman/AustinMan_v2.3_2x2x2.h5 ../user_libs/AustinManWoman/AustinManWoman_gprMax_materials.txt``
|
||||
|
||||
|
||||
Source and output commands
|
||||
==========================
|
||||
|
||||
@@ -633,9 +657,9 @@ If there are less amplitude values than the number of iterations that are going
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
#excitation_file: str1
|
||||
#excitation_file: file
|
||||
|
||||
``str1`` can be the name of the file containing the specified waveform in the same directory as the input file, or ``str1`` can be the full path to the file containing the specified waveform (allowing you to specify any location).
|
||||
``file`` can be the name of the file containing the specified waveform in the same directory as the input file, or ``file`` can be the full path to the file containing the specified waveform (allowing you to specify any location).
|
||||
|
||||
For example, to specify the file ``my_waves.txt``, which contains two custom waveform shapes, use: ``#excitation_file: my_waves.txt``. The contents of the file ``my_waves.txt`` would take the form:
|
||||
|
||||
@@ -762,14 +786,19 @@ Provides a simple method of defining multiple output points in the model. The sy
|
||||
#src_steps: and #rx_steps:
|
||||
--------------------------
|
||||
|
||||
Provide a simple method to allow you to move the location of all sources (``#src_steps``) or all receivers (``#rx_steps``) between runs of a model. The syntax of the commands is:
|
||||
Provides a simple method to allow you to move the location of all simple sources (``#src_steps``) or all receivers (``#rx_steps``) between runs of a model. The syntax of the commands is:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
#src_steps: f1 f2 f3
|
||||
#rx_steps: f1 f2 f3
|
||||
|
||||
``f1 f2 f3`` are increments (x,y,z) to move all sources (``#hertzian_dipole``, ``#magnetic_dipole``, or ``#voltage_source``) or all receivers (created using either ``#rx`` or ``#rx_box`` commands).
|
||||
``f1 f2 f3`` are increments (x,y,z) to move all simple sources (``#hertzian_dipole`` or ``#magnetic_dipole``) or all receivers (created using either ``#rx`` or ``#rx_box`` commands).
|
||||
|
||||
.. note::
|
||||
|
||||
* ``#src_steps`` and ``#rx_steps`` are not suitable for moving sources which have associated geometry, e.g. antenna models.
|
||||
* Values for ``#src_steps`` and ``#rx_steps`` should not be changed between model runs using Python scripting.
|
||||
|
||||
#snapshot:
|
||||
----------
|
||||
@@ -778,19 +807,19 @@ Allows you to obtain information about the electromagnetic fields within a volum
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
#snapshot: f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 file1
|
||||
#snapshot: f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 file
|
||||
|
||||
or
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
#snapshot: f1 f2 f3 f4 f5 f6 f7 f8 f9 i1 file1
|
||||
#snapshot: f1 f2 f3 f4 f5 f6 f7 f8 f9 i1 file
|
||||
|
||||
* ``f1 f2 f3`` are the lower left (x,y,z) coordinates of the volume of the snapshot in metres.
|
||||
* ``f4 f5 f6`` are the upper right (x,y,z) coordinates of the volume of the snapshot in metres.
|
||||
* ``f7 f8 f9`` are the spatial discretisation of the snapshot in metres.
|
||||
* ``f10`` or ``i1`` are the time in seconds (float) or the iteration number (integer) which denote the point in time at which the snapshot will be taken.
|
||||
* ``file1`` is the filename of the file where the snapshot will be stored.
|
||||
* ``file`` is the name of the file where the snapshot will be stored. Snapshot files are automatically stored in a directory with the name of the input file appended with '_snaps'. For multiple model runs each model run will have its own directory, i.e. '_snaps1', 'snaps2' etc...
|
||||
|
||||
For example to save a snapshot of the electromagnetic fields in the model at a simulated time of 3 nanoseconds use: ``#snapshot: 0 0 0 1 1 1 0.1 0.1 0.1 3e-9 snap1``
|
||||
|
||||
@@ -805,7 +834,7 @@ For example to save a snapshot of the electromagnetic fields in the model at a s
|
||||
#end_python:
|
||||
|
||||
|
||||
.. _pml:
|
||||
.. _pml-commands:
|
||||
|
||||
PML commands
|
||||
============
|
||||
|
@@ -14,6 +14,7 @@ File structure
|
||||
|
||||
The output file has the following HDF5 attributes at the root (``/``):
|
||||
|
||||
* ``gprMax`` is the version number of gprMax used to create the output
|
||||
* ``Title`` is the title of the model
|
||||
* ``Iterations`` is the number of iterations for the time window of the model
|
||||
* ``nx, ny, nz`` is a tuple containing the number of cells in each direction of the model
|
||||
@@ -60,7 +61,7 @@ The output file contains HDF5 groups for sources (``srcs``), transmission lines
|
||||
Iinc
|
||||
Vtotal
|
||||
Itotal
|
||||
tl22/
|
||||
tl2/
|
||||
...
|
||||
|
||||
Within each individual ``rx`` group are the following attributes:
|
||||
|
@@ -20,7 +20,7 @@ You can access the following built-in variables from your Python code:
|
||||
|
||||
* ``current_model_run`` is the current run number of the model that is been executed.
|
||||
* ``number_model_runs`` is the total number of runs specified when the model was initially executed, i.e. from ``python -m gprMax my_input_file -n number_of_model_runs``
|
||||
* ``inputdirectory`` is the path to the directory where your input file is located.
|
||||
* ``input_directory`` is the path to the directory where your input file is located.
|
||||
|
||||
|
||||
Functions for input commands
|
||||
|
@@ -25,6 +25,8 @@ References
|
||||
.. [TUR1997] Turcotte, D. L. (1997). Fractals and chaos in geology and geophysics. Cambridge university press. (http://dx.doi.org/10.1017/cbo9781139174695)
|
||||
.. [VIA2005] Vial, A., Grimault, A. S., Macías, D., Barchiesi, D., & de La Chapelle, M. L. (2005). Improved analytical fit of gold dispersion: Application to the modeling of extinction spectra with a finite-difference time-domain method. Physical Review B, 71(8), 085416. (http://dx.doi.org/10.1103/physrevb.71.085416)
|
||||
.. [WAR2011] Warren, C., & Giannopoulos, A. (2011). Creating finite-difference time-domain models of commercial ground-penetrating radar antennas using Taguchi’s optimization method. Geophysics, 76(2), G37-G47. (http://dx.doi.org/10.1190/1.3548506)
|
||||
.. [WEN2007a] Weng, W. C., Yang, F., & Elsherbeni, A. (2007). Electromagnetics and antenna optimization using Taguchi’s method. Synthesis Lectures on Computational Electromagnetics, 2(1), 1-94.
|
||||
.. [WEN2007b] Weng, W. C., Yang, F., & Elsherbeni, A. Z. (2007). Linear antenna array synthesis using Taguchi's method: A novel optimization technique in electromagnetics. Antennas and Propagation, IEEE Transactions on, 55(3), 723-730.
|
||||
.. [WHI2009] Whittow, W. G., & Edwards, R. M. (2009). Effects of averaging procedures for electrical properties at the interface of dissimilar tissues in the human head with finite-difference time-domain modelling. Science, Measurement & Technology, IET, 3(1), 51-58.
|
||||
.. [YEE1966] Yee, K. S. (1966). Numerical solution of initial boundary value problems involving Maxwell’s equations in isotropic media. IEEE Trans. Antennas Propag, 14(3), 302-307. (http://dx.doi.org/10.1109/TAP.1966.1138693)
|
||||
|
||||
|
@@ -1,14 +1,16 @@
|
||||
.. _user-libs:
|
||||
|
||||
**************
|
||||
User libraries
|
||||
**************
|
||||
|
||||
User libraries is a sub-package where useful Python modules contributed by users are stored.
|
||||
|
||||
antennas.py
|
||||
********
|
||||
Antennas
|
||||
********
|
||||
|
||||
Information
|
||||
===========
|
||||
|
||||
**Author/Contact**: Craig Warren (Craig.Warren@ed.ac.uk), University of Edinburgh
|
||||
|
||||
**License**: Creative Commons Attribution-ShareAlike 4.0 International License (http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Copyright (C) 2015-2016, Craig Warren
|
||||
@@ -25,7 +27,21 @@ The module currently features models of antennas similar to commercial antennas:
|
||||
|
||||
A description of how the models were created can be found at http://dx.doi.org/10.1190/1.3548506.
|
||||
|
||||
The antenna models can be accessed from within a block of Python code in your simulation. The models must be used with cubic spatial resolutions of either 1mm (default) or 2mm. For example, to use Python to include an antenna model similar to a GSSI 1.5 GHz antenna at a location 0.125m, 0.094m, 0.100m (x,y,z):
|
||||
Module overview
|
||||
===============
|
||||
|
||||
* ``antennas.py`` is a module containing the descriptions of the antennas.
|
||||
|
||||
|
||||
How to use the module
|
||||
=====================
|
||||
|
||||
The antenna models can be accessed from within a block of Python code in an input file. The models must be used with cubic spatial resolutions of either 1mm (default) or 2mm.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
To include an antenna model similar to a GSSI 1.5 GHz antenna at a location 0.125m, 0.094m, 0.100m (x,y,z):
|
||||
|
||||
.. code-block:: none
|
||||
|
@@ -0,0 +1,78 @@
|
||||
User libraries is a sub-package where useful Python modules contributed by users are stored.
|
||||
|
||||
*********************
|
||||
AustinMan/AustinWoman
|
||||
*********************
|
||||
|
||||
Information
|
||||
===========
|
||||
|
||||
**Authors**: Jackson W. Massey, Cemil S. Geyik, Jungwook Choi, Hyun-Jae Lee, Natcha Techachainiran, Che-Lun Hsu, Robin Q. Nguyen, Trevor Latson, Madison Ball, and Ali E. Yılmaz
|
||||
|
||||
**Contact**: Ali E. Yılmaz (ayilmaz@mail.utexas.edu), The University of Texas at Austin
|
||||
|
||||
**License**: Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License (http://creativecommons.org/licenses/by-nc-nd/3.0/)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# This module is licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
|
||||
# To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/3.0/.
|
||||
#
|
||||
# Please use the attribution at http://web.corral.tacc.utexas.edu/AustinManEMVoxels/AustinMan/citing_the_model/index.html
|
||||
|
||||
AustinMan and AustinWoman (http://bit.ly/AustinMan) are open source electromagnetic voxel models of the human body, which are developed by the Computational Electromagnetics Group (http://www.ece.utexas.edu/research/areas/electromagnetics-acoustics) at The University of Texas at Austin (http://www.utexas.edu). The models are based on data from the National Library of Medicine’s Visible Human Project (https://www.nlm.nih.gov/research/visible/visible_human.html).
|
||||
|
||||
.. figure:: images/user_libs/AustinMan_head.png
|
||||
:width: 600 px
|
||||
|
||||
FDTD geometry mesh showing the head of the AustinMan model (2x2x2mm :math:`^3`).
|
||||
|
||||
The AustinMan and AustinWoman models are not currently included in the user libraries sub-package, however they can be downloaded from http://bit.ly/AustinMan.
|
||||
|
||||
The following whole body models are available.
|
||||
|
||||
=========== ========================== ==================
|
||||
Model Resolution (mm :math:`^3`) Dimensions (cells)
|
||||
=========== ========================== ==================
|
||||
AustinMan 8x8x8 86 x 47 x 235
|
||||
AustinMan 4x4x4 171 x 94 x 470
|
||||
AustinMan 2x2x2 342 x 187 x 939
|
||||
AustinMan 1x1x1 683 x 374 x 1877
|
||||
AustinWoman 8x8x8 86 x 47 x 217
|
||||
AustinWoman 4x4x4 171 x 94 x 433
|
||||
AustinWoman 2x2x2 342 x 187 x 865
|
||||
AustinWoman 1x1x1 683 x 374 x 1730
|
||||
=========== ========================== ==================
|
||||
|
||||
How to use the models
|
||||
=====================
|
||||
|
||||
From http://bit.ly/AustinMan:
|
||||
|
||||
* Download a HDF5 file (.h5) of AustinMan or AustinWoman at the resolution you wish to use.
|
||||
* Download a text file of material descriptions for gprMax, either
|
||||
|
||||
* ``AustinManWoman_gprMax_materials.txt`` for non-dispersive material properties at 900 MHz (http://niremf.ifac.cnr.it/tissprop/).
|
||||
* ``AustinManWoman_gprMax_materials_dispersive.txt`` for dispersive material properties that feature a 3-pole Debye model (http://dx.doi.org/10.1109/LMWC.2011.2180371). Not all materials have a dispersive description.
|
||||
|
||||
To insert either AustinMan or AustinWoman models into a simulation use the ``#geometry_objects_file``.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
To insert a 2x2x2mm :math:`^3` AustinMan with the lower left corner 40mm from the origin of the domain, and using disperive material properties, use the command:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
#geometry_objects_file: 0.04 0.04 0.04 ../user_libs/AustinManWoman/AustinMan_v2.3_2x2x2.h5 ../user_libs/AustinManWoman/AustinManWoman_gprMax_materials_dispersive.txt
|
||||
|
||||
For further information on the `#geometry_objects_file` see the section on object contruction commands in the :ref:`Input commands section <commands>`.
|
||||
|
||||
.. figure:: images/user_libs/AustinMan.png
|
||||
:width: 300 px
|
||||
|
||||
FDTD geometry mesh showing the AustinMan body model (2x2x2mm :math:`^3`).
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,156 @@
|
||||
User libraries is a sub-package where useful Python modules contributed by users are stored.
|
||||
|
||||
*******************************
|
||||
Optimisation - Taguchi's method
|
||||
*******************************
|
||||
|
||||
Information
|
||||
===========
|
||||
|
||||
**Author/Contact**: Craig Warren (Craig.Warren@ed.ac.uk), University of Edinburgh
|
||||
|
||||
**License**: Creative Commons Attribution-ShareAlike 4.0 International License (http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Copyright (C) 2015-2016, Craig Warren
|
||||
#
|
||||
# This module is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
|
||||
#
|
||||
# Please use the attribution at http://dx.doi.org/10.1190/1.3548506
|
||||
|
||||
The package features an optimisation technique based on Taguchi's method. It allows users to define parameters in an input file and optimise their values based on a fitness function, for example it can be used to optimise material properties or geometry in a simulation.
|
||||
|
||||
.. warning::
|
||||
|
||||
This package combines a number of advanced features and should not be used without knowledge and familiarity of the underlying techniques. It requires:
|
||||
|
||||
* Knowledge of Python to contruct an input file to use with the optimisation
|
||||
* Familiarity of optimisation techniques, and in particular Taguchi's method
|
||||
* Careful sanity checks to be made throughout the process
|
||||
|
||||
|
||||
Taguchi's method
|
||||
----------------
|
||||
|
||||
Taguchi's method is based on the concept of the Orthogonal Array (OA) and has the following advantages:
|
||||
|
||||
* Simple to implement
|
||||
* Effective in reduction of experiments
|
||||
* Fast convergence speed
|
||||
* Global optimum results
|
||||
* Independence from initial values of optimisation parameters
|
||||
|
||||
Details of Taguchi's method in the context of electromagnetics can be found in [WEN2007a]_ and [WEN2007b]_.
|
||||
|
||||
|
||||
Package overview
|
||||
================
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
antenna_bowtie_opt.in
|
||||
fitness_functions.py
|
||||
OA_9_4_3_2.npy
|
||||
OA_18_7_3_2.npy
|
||||
plot_results.py
|
||||
|
||||
* ``antenna_bowtie_opt.in`` is a example model of a bowtie antenna where values of loading resistors are optimised.
|
||||
* ``fitness_functions.py`` is a module containing fitness functions. There are some pre-built ones but users should add their own here.
|
||||
* ``OA_9_4_3_2.npy`` and ``OA_18_7_3_2.npy`` are NumPy archives containing pre-built OAs from http://neilsloane.com/oadir/
|
||||
* ``plot_results.py`` is a module for plotting the results, such as parameter values and convergence history, from an optimisation process when it has completed.
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
The process by which Taguchi's method optimises parameters is illustrated in the following figure:
|
||||
|
||||
.. figure:: images/user_libs/taguchi_process.png
|
||||
:width: 300 px
|
||||
|
||||
Process associated with Taguchi's method.
|
||||
|
||||
In stage 1a, one of the 2 pre-built OAs will automatically be chosen depending on the number of parameters to optimise. Currently, up to 7 independent parameters can be optimised, although a method to construct OAs of any size is under testing.
|
||||
|
||||
In stage 1b, a fitness function is required to set a goal against which to compare results from the optimisation process. A number of pre-built fitness functions can be found in the ``fitness_functions.py`` module, e.g. ``minvalue``, ``maxvalue`` and ``xcorr``. Users can also easily add their own fitness functions to this module. All fitness functions must take two arguments:
|
||||
|
||||
* ``filename`` a string containing the full path and filename of the output file
|
||||
* ``args`` a dictionary which can contain any number of additional arguments for the function, e.g. names of outputs (rxs) in the model
|
||||
|
||||
Additionally, all fitness functions must return a single fitness value which the optimsation process will aim to maximise.
|
||||
|
||||
Stages 2-6 are iterated through by the optimisation process.
|
||||
|
||||
Parameters and settings for the optimisation process are specified within a special Python block defined by ``#taguchi`` and ``#end_taguchi`` in the input file. The parameters to optimise must be defined in a dictionary named ``optparams`` and their initial ranges specified as lists with lower and upper values. The fitness function, it's parameters, and a stopping value are defined in dictionary named ``fitness`` which has keys for:
|
||||
|
||||
* ``name`` a string that is the name of the fitness function to be used
|
||||
* ``args`` a dictionary containing arguments to be passed to the fitness function. Within ``args`` there must be a key called ``outputs`` which contains a string or list of the names of one or more outputs (rxs) in the model
|
||||
* ``stop`` a value from the fitness function which when exceeded the optimisation should stop
|
||||
|
||||
Optionally a variable called ``maxiterations`` maybe specified which will set a maximum number of iterations after which the optimisation process will terminate irrespective of any other criteria. If it is not specified it defaults to a maximum of 20 iterations.
|
||||
|
||||
There is also a builtin criterion to terminate the optimisation process is successive fitness values are within 0.1% of one another.
|
||||
|
||||
|
||||
How to use the package
|
||||
======================
|
||||
|
||||
The package requires ``#python`` and ``#end_python`` to be used in the input file, as well as ``#taguchi`` and ``#end_taguchi`` for specifying parameters and setting for the optimisation process. A Taguchi optimisation is run using the command line option ``--opt-taguchi``.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
The following example demonstrates using the Taguchi optimisation process to optimise values of loading resistors used in a bowtie antenna. The example is slighty contrived as the goal is simply to find values for the resistors that produces a maximum absolute amplitude in the response from the antenna. We already know this should occur when the values of the resistors are at a minimum. Nevertheless, it is useful to illustrate the optimisation process and how to use it.
|
||||
|
||||
.. figure:: images/user_libs/antenna_bowtie_opt.png
|
||||
:width: 600 px
|
||||
|
||||
FDTD geometry mesh showing bowtie antenna with slots and loading resistors.
|
||||
|
||||
The bowtie design features three vertical slots (y-direction) in each arm of the bowtie. Each slot has different loading resistors, but within each slot there are four resistors of the same value. A resistor is modelled as two parallel edges of a cell. The bowtie is placed on a lossless substrate of relative permittivity 4.8. The antenna is modelled in free space, and an output point of the electric field (named ``Ex60mm``) is specified at a distance of 60mm from the feed of the bowtie (red coloured cell).
|
||||
|
||||
.. literalinclude:: ../../user_libs/optimisation_taguchi/antenna_bowtie_opt.in
|
||||
:language: none
|
||||
:linenos:
|
||||
|
||||
The first part of the input file (lines 1-7) contains the parameters to optimise, their initial ranges, and fitness function information for the optimisation process. Three parameters representing the resistor values are defined with ranges between 0.1 :math:`\Omega` and 1 :math:`k\Omega`. A pre-built fitness function called ``maxabsvalue`` is specified with a stopping criterion of 10V/m. The output point in the model that will be used in the optimisation is specified as having the name ``Ex60mm``.
|
||||
|
||||
The next part of the input file (lines 9-93) contains the model. For the most part there is nothing special about the way the model is defined - a mixture of Python, NumPy and functional forms of the input commands (available by importing the module ``input_cmd_funcs``) are used. However, it is worth pointing out how the values of the parameters to optimise are accessed. On line 29 a NumPy array of the values of the resistors is created. The values are accessed using their names as keys to the ``optparams`` dictionary. On line 30 the values of the resistors are converted to conductivities, which are used to create new materials (line 34-35). The resistors are then built by applying the materials to cell edges (e.g. lines 55-62). The output point in the model in specifed with the name ``Ex60mm`` and as having only an ``Ex`` field output (line 42).
|
||||
|
||||
The optimisation process is run on the model using the ``--opt-taguchi`` command line flag.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
python -m gprMax user_libs/optimisation_taguchi/antenna_bowtie_opt.in --opt-taguchi
|
||||
|
||||
Results
|
||||
^^^^^^^
|
||||
|
||||
When the optimisation has completed a summary will be printed showing histories of the parameter values and the fitness metric. These values are also saved (pickled) to file and can be plotted using the ``plot_results.py`` module, for example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
python -m user_libs.optimisation_taguchi.plot_results user_libs/optimisation_taguchi/antenna_bowtie_opt_hist.pickle
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Optimisations summary for: antenna_bowtie_opt_hist.pickle
|
||||
Number of iterations: 4
|
||||
History of fitness values: [4.2720928, 5.68856, 5.7023263, 5.7023263]
|
||||
History of parameter values:
|
||||
resinner [250.07498, 0.87031555, 0.1, 0.1]
|
||||
resmiddle [250.07498, 0.87031555, 0.1, 0.1]
|
||||
resouter [250.07498, 0.87031555, 0.1, 0.1]
|
||||
|
||||
.. figure:: images/user_libs/taguchi_fitness_hist.png
|
||||
:width: 600 px
|
||||
|
||||
History of fitness metric (``maxabsvalue``) value.
|
||||
|
||||
.. figure:: images/user_libs/taguchi_parameter_hist.png
|
||||
:width: 600 px
|
||||
|
||||
History of values of parameters, ``resinner``, ``resmiddle``, and ``resouter``.
|
||||
|
||||
The optimisation process terminated after 4 iterations because succcessive fitness values were within 0.1% of one another. A maximum absolute amplitude value of 5.7 V/m was achieved when the three resistors had values of 0.1 :math:`\Omega`.
|
@@ -1,4 +1,4 @@
|
||||
# This is where the version number is set and read by setup.py and conf.py (for the docs)
|
||||
|
||||
__version__ = '3.0.0b26'
|
||||
__version__ = '3.0.0b29'
|
||||
|
||||
|
@@ -23,7 +23,7 @@ from gprMax.constants import floattype, complextype
|
||||
from gprMax.utilities import round_value
|
||||
|
||||
|
||||
class FractalSurface:
|
||||
class FractalSurface(object):
|
||||
"""Fractal surfaces."""
|
||||
|
||||
surfaceIDs = ['xminus', 'xplus', 'yminus', 'yplus', 'zminus', 'zplus']
|
||||
@@ -106,7 +106,7 @@ class FractalSurface:
|
||||
self.fractalsurface = self.fractalsurface * ((self.fractalrange[1] - self.fractalrange[0])/fractalrange) + self.fractalrange[0] - ((self.fractalrange[1] - self.fractalrange[0])/fractalrange) * fractalmin
|
||||
|
||||
|
||||
class FractalVolume:
|
||||
class FractalVolume(object):
|
||||
"""Fractal volumes."""
|
||||
|
||||
def __init__(self, xs, xf, ys, yf, zs, zf, dimension):
|
||||
@@ -189,7 +189,7 @@ class FractalVolume:
|
||||
self.mask[maskxs:maskxf, maskys:maskyf, maskzs:maskzf] = 1
|
||||
|
||||
|
||||
class Grass:
|
||||
class Grass(object):
|
||||
"""Geometry information for blades of grass."""
|
||||
|
||||
def __init__(self, numblades):
|
||||
|
@@ -668,15 +668,33 @@ cpdef void build_voxels_from_array(int xs, int ys, int zs, np.uint16_t[:, :, ::1
|
||||
"""
|
||||
|
||||
cdef Py_ssize_t i, j, k
|
||||
cdef int nx, ny, nz, numID
|
||||
cdef int xf, yf, zf, numID
|
||||
|
||||
nx = data.shape[0]
|
||||
ny = data.shape[1]
|
||||
nz = data.shape[2]
|
||||
# Set bounds to domain if they outside
|
||||
if xs < 0:
|
||||
xs = 0
|
||||
if xs + data.shape[0] >= solid.shape[0]:
|
||||
xf = solid.shape[0] - 1
|
||||
else:
|
||||
xf = xs + data.shape[0]
|
||||
|
||||
for i in range(nx):
|
||||
for j in range(ny):
|
||||
for k in range(nz):
|
||||
numID = data[i, j, k]
|
||||
build_voxel(i + xs, j + ys, k + zs, numID, numID, numID, numID, False, solid, rigidE, rigidH, ID)
|
||||
if ys < 0:
|
||||
ys = 0
|
||||
if ys + data.shape[1] >= solid.shape[1]:
|
||||
yf = solid.shape[1] - 1
|
||||
else:
|
||||
yf = ys + data.shape[1]
|
||||
|
||||
if zs < 0:
|
||||
zs = 0
|
||||
if zs + data.shape[2] >= solid.shape[2]:
|
||||
zf = solid.shape[2] - 1
|
||||
else:
|
||||
zf = zs + data.shape[2]
|
||||
|
||||
for i in range(xs, xf):
|
||||
for j in range(ys, yf):
|
||||
for k in range(zs, zf):
|
||||
numID = data[i - xs, j - ys, k - zs]
|
||||
build_voxel(i, j, k, numID, numID, numID, numID, False, solid, rigidE, rigidH, ID)
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import os, sys
|
||||
import numpy as np
|
||||
from struct import pack
|
||||
from gprMax.xdmf import write_output_file
|
||||
@@ -24,7 +24,7 @@ from gprMax.xdmf import write_output_file
|
||||
from gprMax.utilities import round_value
|
||||
|
||||
|
||||
class GeometryView:
|
||||
class GeometryView(object):
|
||||
"""Views of the geometry of the model."""
|
||||
|
||||
if sys.byteorder == 'little':
|
||||
@@ -53,7 +53,7 @@ class GeometryView:
|
||||
self.dx = dx
|
||||
self.dy = dy
|
||||
self.dz = dz
|
||||
self.filename = filename
|
||||
self.basefilename = filename
|
||||
self.type = type
|
||||
|
||||
def write_xdmf(self, modelrun, numbermodelruns, G):
|
||||
@@ -73,9 +73,9 @@ class GeometryView:
|
||||
|
||||
# Construct filename from user-supplied name and model run number
|
||||
if numbermodelruns == 1:
|
||||
self.filename = G.inputdirectory + self.filename
|
||||
self.filename = os.path.abspath(os.path.join(G.inputdirectory, self.basefilename))
|
||||
else:
|
||||
self.filename = G.inputdirectory + self.filename + str(modelrun)
|
||||
self.filename = os.path.abspath(os.path.join(G.inputdirectory, self.basefilename + str(modelrun)))
|
||||
|
||||
if self.type == 'n':
|
||||
self.filename += '.vti'
|
||||
@@ -91,14 +91,18 @@ class GeometryView:
|
||||
self.vtk_nycells = self.vtk_yfcells - self.vtk_yscells
|
||||
self.vtk_nzcells = self.vtk_zfcells - self.vtk_zscells
|
||||
|
||||
# Create an array and add numeric IDs for PML, sources and receivers
|
||||
self.srcs_rxs_pml = np.zeros((G.nx + 1, G.ny + 1, G.nz + 1), dtype=np.int8)
|
||||
# Create arrays and add numeric IDs for PML, sources and receivers (0 is not set, 1 is PML, srcs and rxs numbered thereafter)
|
||||
self.srcs_pml = np.zeros((G.nx + 1, G.ny + 1, G.nz + 1), dtype=np.int8)
|
||||
self.rxs = np.zeros((G.nx + 1, G.ny + 1, G.nz + 1), dtype=np.int8)
|
||||
for pml in G.pmls:
|
||||
self.srcs_rxs_pml[pml.xs:pml.xf, pml.ys:pml.yf, pml.zs:pml.zf] = 1
|
||||
for index, srcrx in enumerate(G.rxs + G.hertziandipoles + G.magneticdipoles + G.voltagesources + G.transmissionlines):
|
||||
self.srcs_rxs_pml[srcrx.xcoord, srcrx.ycoord, srcrx.zcoord] = index + 2
|
||||
self.srcs_pml[pml.xs:pml.xf, pml.ys:pml.yf, pml.zs:pml.zf] = 1
|
||||
for index, src in enumerate(G.hertziandipoles + G.magneticdipoles + G.voltagesources + G.transmissionlines):
|
||||
self.srcs_pml[src.xcoord, src.ycoord, src.zcoord] = index + 2
|
||||
for index, rx in enumerate(G.rxs):
|
||||
self.rxs[rx.xcoord, rx.ycoord, rx.zcoord] = index + 1
|
||||
|
||||
vtk_srcs_rxs_pml_offset = round_value((np.dtype(np.uint32).itemsize * self.vtk_nxcells * self.vtk_nycells * self.vtk_nzcells) + np.dtype(np.uint32).itemsize)
|
||||
vtk_srcs_pml_offset = round_value((np.dtype(np.uint32).itemsize * self.vtk_nxcells * self.vtk_nycells * self.vtk_nzcells) + np.dtype(np.uint32).itemsize)
|
||||
vtk_rxs_offset = round_value((np.dtype(np.uint32).itemsize * self.vtk_nxcells * self.vtk_nycells * self.vtk_nzcells) + np.dtype(np.uint32).itemsize + (np.dtype(np.int8).itemsize * self.vtk_nxcells * self.vtk_nycells * self.vtk_nzcells) + np.dtype(np.uint32).itemsize)
|
||||
|
||||
with open(self.filename, 'wb') as f:
|
||||
f.write('<?xml version="1.0"?>\n'.encode('utf-8'))
|
||||
@@ -107,7 +111,8 @@ class GeometryView:
|
||||
f.write('<Piece Extent="{} {} {} {} {} {}">\n'.format(self.vtk_xscells, self.vtk_xfcells, self.vtk_yscells, self.vtk_yfcells, self.vtk_zscells, self.vtk_zfcells).encode('utf-8'))
|
||||
f.write('<CellData Scalars="Material">\n'.encode('utf-8'))
|
||||
f.write('<DataArray type="UInt32" Name="Material" format="appended" offset="0" />\n'.encode('utf-8'))
|
||||
f.write('<DataArray type="Int8" Name="Sources_Receivers_PML" format="appended" offset="{}" />\n'.format(vtk_srcs_rxs_pml_offset).encode('utf-8'))
|
||||
f.write('<DataArray type="Int8" Name="Sources_PML" format="appended" offset="{}" />\n'.format(vtk_srcs_pml_offset).encode('utf-8'))
|
||||
f.write('<DataArray type="Int8" Name="Receivers" format="appended" offset="{}" />\n'.format(vtk_rxs_offset).encode('utf-8'))
|
||||
f.write('</CellData>\n'.encode('utf-8'))
|
||||
f.write('</Piece>\n</ImageData>\n<AppendedData encoding="raw">\n_'.encode('utf-8'))
|
||||
|
||||
@@ -120,13 +125,21 @@ class GeometryView:
|
||||
for i in range(self.xs, self.xf, self.dx):
|
||||
f.write(pack('I', G.solid[i, j, k]))
|
||||
|
||||
# Write source/receiver IDs
|
||||
# Write source/PML IDs
|
||||
datasize = int(np.dtype(np.int8).itemsize * self.vtk_nxcells * self.vtk_nycells * self.vtk_nzcells)
|
||||
f.write(pack('I', datasize))
|
||||
for k in range(self.zs, self.zf, self.dz):
|
||||
for j in range(self.ys, self.yf, self.dy):
|
||||
for i in range(self.xs, self.xf, self.dx):
|
||||
f.write(pack('b', self.srcs_rxs_pml[i, j, k]))
|
||||
f.write(pack('b', self.srcs_pml[i, j, k]))
|
||||
|
||||
# Write receiver IDs
|
||||
datasize = int(np.dtype(np.int8).itemsize * self.vtk_nxcells * self.vtk_nycells * self.vtk_nzcells)
|
||||
f.write(pack('I', datasize))
|
||||
for k in range(self.zs, self.zf, self.dz):
|
||||
for j in range(self.ys, self.yf, self.dy):
|
||||
for i in range(self.xs, self.xf, self.dx):
|
||||
f.write(pack('b', self.rxs[i, j, k]))
|
||||
|
||||
f.write('\n</AppendedData>\n</VTKFile>'.encode('utf-8'))
|
||||
|
||||
@@ -244,8 +257,8 @@ class GeometryView:
|
||||
f.write('<Material name="{}">{}</Material>\n'.format(material.ID, material.numID).encode('utf-8'))
|
||||
if not materialsonly:
|
||||
f.write('<PML name="PML boundary region">1</PML>\n'.encode('utf-8'))
|
||||
for index, srcrx in enumerate(G.rxs + G.hertziandipoles + G.magneticdipoles + G.voltagesources + G.transmissionlines):
|
||||
f.write('<Sources_Receivers name="{}">{}</Sources_Receivers>\n'.format(srcrx.ID, index + 2).encode('utf-8'))
|
||||
for index, src in enumerate(G.hertziandipoles + G.magneticdipoles + G.voltagesources + G.transmissionlines):
|
||||
f.write('<Sources name="{}">{}</Sources>\n'.format(src.ID, index + 2).encode('utf-8'))
|
||||
for index, rx in enumerate(G.rxs):
|
||||
f.write('<Receivers name="{}">{}</Receivers>\n'.format(rx.ID, index + 1).encode('utf-8'))
|
||||
f.write('</gprMax>\n'.encode('utf-8'))
|
||||
|
||||
|
||||
|
227
gprMax/gprMax.py
227
gprMax/gprMax.py
@@ -53,15 +53,16 @@ def main():
|
||||
parser.add_argument('-mpi', action='store_true', default=False, help='switch on MPI task farm')
|
||||
parser.add_argument('-benchmark', action='store_true', default=False, help='switch on benchmarking mode')
|
||||
parser.add_argument('--geometry-only', action='store_true', default=False, help='only build model and produce geometry file(s)')
|
||||
parser.add_argument('--geometry-fixed', action='store_true', default=False, help='do not reprocess model geometry for multiple model runs')
|
||||
parser.add_argument('--write-processed', action='store_true', default=False, help='write an input file after any Python code and include commands in the original input file have been processed')
|
||||
parser.add_argument('--opt-taguchi', action='store_true', default=False, help='optimise parameters using the Taguchi optimisation method')
|
||||
args = parser.parse_args()
|
||||
numbermodelruns = args.n
|
||||
inputdirectory = os.path.dirname(os.path.abspath(args.inputfile)) + os.sep
|
||||
inputfile = inputdirectory + os.path.basename(args.inputfile)
|
||||
inputdirectory = os.path.dirname(os.path.abspath(args.inputfile))
|
||||
inputfile = os.path.abspath(os.path.join(inputdirectory, os.path.basename(args.inputfile)))
|
||||
|
||||
# Create a separate namespace that users can access in any Python code blocks in the input file
|
||||
usernamespace = {'c': c, 'e0': e0, 'm0': m0, 'z0': z0, 'number_model_runs': numbermodelruns, 'inputdirectory': inputdirectory}
|
||||
usernamespace = {'c': c, 'e0': e0, 'm0': m0, 'z0': z0, 'number_model_runs': numbermodelruns, 'input_directory': inputdirectory}
|
||||
|
||||
# Process for Taguchi optimisation
|
||||
if args.opt_taguchi:
|
||||
@@ -239,107 +240,143 @@ def run_model(args, modelrun, numbermodelruns, inputfile, usernamespace):
|
||||
# Monitor memory usage
|
||||
p = psutil.Process()
|
||||
|
||||
print('\n{}\n\nModel input file: {}\n'.format(68*'*', inputfile))
|
||||
# Declare variable to hold FDTDGrid class
|
||||
global G
|
||||
|
||||
# Add the current model run to namespace that can be accessed by user in any Python code blocks in input file
|
||||
usernamespace['current_model_run'] = modelrun
|
||||
print('Constants/variables available for Python scripting: {}\n'.format(usernamespace))
|
||||
# Normal model reading/building process; bypassed if geometry information to be reused
|
||||
if not 'G' in globals():
|
||||
print('\n{}\n\nModel input file: {}\n'.format(68*'*', inputfile))
|
||||
|
||||
# Process any user input Python commands
|
||||
processedlines = process_python_include_code(inputfile, usernamespace)
|
||||
# Add the current model run to namespace that can be accessed by user in any Python code blocks in input file
|
||||
usernamespace['current_model_run'] = modelrun
|
||||
print('Constants/variables available for Python scripting: {}\n'.format(usernamespace))
|
||||
|
||||
# Write a file containing the input commands after Python blocks have been processed
|
||||
if args.write_processed:
|
||||
write_processed_file(inputfile, modelrun, numbermodelruns, processedlines)
|
||||
# Read input file and process any Python or include commands
|
||||
processedlines = process_python_include_code(inputfile, usernamespace)
|
||||
|
||||
# Check validity of command names & that essential commands are present
|
||||
singlecmds, multicmds, geometry = check_cmd_names(processedlines)
|
||||
# Write a file containing the input commands after Python or include commands have been processed
|
||||
if args.write_processed:
|
||||
write_processed_file(inputfile, modelrun, numbermodelruns, processedlines)
|
||||
|
||||
# Initialise an instance of the FDTDGrid class
|
||||
G = FDTDGrid()
|
||||
G.inputdirectory = usernamespace['inputdirectory']
|
||||
# Check validity of command names and that essential commands are present
|
||||
singlecmds, multicmds, geometry = check_cmd_names(processedlines)
|
||||
|
||||
# Create built-in materials
|
||||
m = Material(0, 'pec', G)
|
||||
m.average = False
|
||||
G.materials.append(m)
|
||||
m = Material(1, 'free_space', G)
|
||||
G.materials.append(m)
|
||||
# Initialise an instance of the FDTDGrid class
|
||||
G = FDTDGrid()
|
||||
G.inputfilename = os.path.split(inputfile)[1]
|
||||
G.inputdirectory = os.path.dirname(os.path.abspath(inputfile))
|
||||
|
||||
# Process parameters for commands that can only occur once in the model
|
||||
process_singlecmds(singlecmds, G)
|
||||
# Create built-in materials
|
||||
m = Material(0, 'pec', G)
|
||||
m.average = False
|
||||
G.materials.append(m)
|
||||
m = Material(1, 'free_space', G)
|
||||
G.materials.append(m)
|
||||
|
||||
# Process parameters for commands that can occur multiple times in the model
|
||||
process_multicmds(multicmds, G)
|
||||
# Process parameters for commands that can only occur once in the model
|
||||
process_singlecmds(singlecmds, G)
|
||||
|
||||
# Initialise an array for volumetric material IDs (solid), boolean arrays for specifying materials not to be averaged (rigid),
|
||||
# an array for cell edge IDs (ID), and arrays for the field components.
|
||||
G.initialise_std_arrays()
|
||||
# Process parameters for commands that can occur multiple times in the model
|
||||
process_multicmds(multicmds, G)
|
||||
|
||||
# Process the geometry commands in the order they were given
|
||||
tinputprocstart = perf_counter()
|
||||
process_geometrycmds(geometry, G)
|
||||
tinputprocend = perf_counter()
|
||||
print('\nInput file processed in [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tinputprocend - tinputprocstart))))
|
||||
# Initialise an array for volumetric material IDs (solid), boolean arrays for specifying materials not to be averaged (rigid),
|
||||
# an array for cell edge IDs (ID)
|
||||
G.initialise_geometry_arrays()
|
||||
|
||||
# Build the PML and calculate initial coefficients
|
||||
build_pmls(G)
|
||||
# Initialise arrays for the field components
|
||||
G.initialise_field_arrays()
|
||||
|
||||
# Build the model, i.e. set the material properties (ID) for every edge of every Yee cell
|
||||
tbuildstart = perf_counter()
|
||||
build_electric_components(G.solid, G.rigidE, G.ID, G)
|
||||
build_magnetic_components(G.solid, G.rigidH, G.ID, G)
|
||||
tbuildend = perf_counter()
|
||||
print('\nModel built in [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tbuildend - tbuildstart))))
|
||||
# Process geometry commands in the order they were given
|
||||
tinputprocstart = perf_counter()
|
||||
process_geometrycmds(geometry, G)
|
||||
tinputprocend = perf_counter()
|
||||
print('\nInput file processed in [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tinputprocend - tinputprocstart))))
|
||||
|
||||
# Process any voltage sources (that have resistance) to create a new material at the source location
|
||||
for voltagesource in G.voltagesources:
|
||||
voltagesource.create_material(G)
|
||||
# Build the PML and calculate initial coefficients
|
||||
build_pmls(G)
|
||||
|
||||
# Initialise arrays of update coefficients to pass to update functions
|
||||
G.initialise_std_updatecoeff_arrays()
|
||||
# Build the model, i.e. set the material properties (ID) for every edge of every Yee cell
|
||||
tbuildstart = perf_counter()
|
||||
build_electric_components(G.solid, G.rigidE, G.ID, G)
|
||||
build_magnetic_components(G.solid, G.rigidH, G.ID, G)
|
||||
tbuildend = perf_counter()
|
||||
print('\nModel built in [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tbuildend - tbuildstart))))
|
||||
|
||||
# Initialise arrays of update coefficients and temporary values if there are any dispersive materials
|
||||
if Material.maxpoles != 0:
|
||||
G.initialise_dispersive_arrays()
|
||||
# Process any voltage sources (that have resistance) to create a new material at the source location
|
||||
for voltagesource in G.voltagesources:
|
||||
voltagesource.create_material(G)
|
||||
|
||||
# Calculate update coefficients, store in arrays, and list materials in model
|
||||
if G.messages:
|
||||
print('\nMaterials:\n')
|
||||
print('ID\tName\t\tProperties')
|
||||
print('{}'.format('-'*50))
|
||||
for material in G.materials:
|
||||
# Initialise arrays of update coefficients to pass to update functions
|
||||
G.initialise_std_update_coeff_arrays()
|
||||
|
||||
# Calculate update coefficients for material
|
||||
material.calculate_update_coeffsE(G)
|
||||
material.calculate_update_coeffsH(G)
|
||||
|
||||
# Store all update coefficients together
|
||||
G.updatecoeffsE[material.numID, :] = material.CA, material.CBx, material.CBy, material.CBz, material.srce
|
||||
G.updatecoeffsH[material.numID, :] = material.DA, material.DBx, material.DBy, material.DBz, material.srcm
|
||||
|
||||
# Store coefficients for any dispersive materials
|
||||
# Initialise arrays of update coefficients and temporary values if there are any dispersive materials
|
||||
if Material.maxpoles != 0:
|
||||
z = 0
|
||||
for pole in range(Material.maxpoles):
|
||||
G.updatecoeffsdispersive[material.numID, z:z+3] = e0 * material.eqt2[pole], material.eqt[pole], material.zt[pole]
|
||||
z += 3
|
||||
G.initialise_dispersive_arrays()
|
||||
|
||||
# Calculate update coefficients, store in arrays, and list materials in model
|
||||
if G.messages:
|
||||
if material.deltaer and material.tau:
|
||||
tmp = 'delta_epsr={}, tau={} secs; '.format(', '.join('{:g}'.format(deltaer) for deltaer in material.deltaer), ', '.join('{:g}'.format(tau) for tau in material.tau))
|
||||
else:
|
||||
tmp = ''
|
||||
if material.average:
|
||||
dielectricsmoothing = 'dielectric smoothing permitted.'
|
||||
else:
|
||||
dielectricsmoothing = 'dielectric smoothing not permitted.'
|
||||
print('{:3}\t{:12}\tepsr={:g}, sig={:g} S/m; mur={:g}, sig*={:g} S/m; '.format(material.numID, material.ID, material.er, material.se, material.mr, material.sm) + tmp + dielectricsmoothing)
|
||||
print('\nMaterials:\n')
|
||||
print('ID\tName\t\tProperties')
|
||||
print('{}'.format('-'*50))
|
||||
for material in G.materials:
|
||||
|
||||
# Check to see if numerical dispersion might be a problem
|
||||
resolution = dispersion_check(G)
|
||||
if resolution != 0 and max((G.dx, G.dy, G.dz)) > resolution:
|
||||
print('\nWARNING: Potential numerical dispersion in the simulation. Check the spatial discretisation against the smallest wavelength present. Suggested resolution should be less than {:g}m'.format(resolution))
|
||||
# Calculate update coefficients for material
|
||||
material.calculate_update_coeffsE(G)
|
||||
material.calculate_update_coeffsH(G)
|
||||
|
||||
# Store all update coefficients together
|
||||
G.updatecoeffsE[material.numID, :] = material.CA, material.CBx, material.CBy, material.CBz, material.srce
|
||||
G.updatecoeffsH[material.numID, :] = material.DA, material.DBx, material.DBy, material.DBz, material.srcm
|
||||
|
||||
# Store coefficients for any dispersive materials
|
||||
if Material.maxpoles != 0:
|
||||
z = 0
|
||||
for pole in range(Material.maxpoles):
|
||||
G.updatecoeffsdispersive[material.numID, z:z+3] = e0 * material.eqt2[pole], material.eqt[pole], material.zt[pole]
|
||||
z += 3
|
||||
|
||||
if G.messages:
|
||||
if material.deltaer and material.tau:
|
||||
tmp = 'delta_epsr={}, tau={} secs; '.format(', '.join('{:g}'.format(deltaer) for deltaer in material.deltaer), ', '.join('{:g}'.format(tau) for tau in material.tau))
|
||||
else:
|
||||
tmp = ''
|
||||
if material.average:
|
||||
dielectricsmoothing = 'dielectric smoothing permitted.'
|
||||
else:
|
||||
dielectricsmoothing = 'dielectric smoothing not permitted.'
|
||||
print('{:3}\t{:12}\tepsr={:g}, sig={:g} S/m; mur={:g}, sig*={:g} S/m; '.format(material.numID, material.ID, material.er, material.se, material.mr, material.sm) + tmp + dielectricsmoothing)
|
||||
|
||||
# Check to see if numerical dispersion might be a problem
|
||||
resolution = dispersion_check(G)
|
||||
if resolution and max((G.dx, G.dy, G.dz)) > resolution:
|
||||
print('\nWARNING: Potential numerical dispersion in the simulation. Check the spatial discretisation against the smallest wavelength present. Suggested resolution should be less than {:g}m'.format(resolution))
|
||||
|
||||
# If geometry information to be reused between model runs
|
||||
else:
|
||||
# Clear arrays for field components
|
||||
G.initialise_field_arrays()
|
||||
|
||||
# Clear arrays for fields in PML
|
||||
for pml in G.pmls:
|
||||
pml.initialise_field_arrays()
|
||||
|
||||
# Adjust position of simple sources and receivers if required
|
||||
if G.srcstepx > 0 or G.srcstepy > 0 or G.srcstepz > 0:
|
||||
for source in itertools.chain(G.hertziandipoles, G.magneticdipoles):
|
||||
if modelrun == 1:
|
||||
if source.xcoord + G.srcstepx * (numbermodelruns - 1) > G.nx or source.ycoord + G.srcstepy * (numbermodelruns - 1) > G.ny or source.zcoord + G.srcstepz * (numbermodelruns - 1) > G.nz:
|
||||
raise GeneralError('Source(s) will be stepped to a position outside the domain.')
|
||||
source.xcoord = source.xcoordbase + (modelrun - 1) * G.srcstepx
|
||||
source.ycoord = source.ycoordbase + (modelrun - 1) * G.srcstepy
|
||||
source.zcoord = source.zcoordbase + (modelrun - 1) * G.srcstepz
|
||||
if G.rxstepx > 0 or G.rxstepy > 0 or G.rxstepz > 0:
|
||||
for receiver in G.rxs:
|
||||
if modelrun == 1:
|
||||
if receiver.xcoord + G.rxstepx * (numbermodelruns - 1) > G.nx or receiver.ycoord + G.rxstepy * (numbermodelruns - 1) > G.ny or receiver.zcoord + G.rxstepz * (numbermodelruns - 1) > G.nz:
|
||||
raise GeneralError('Receiver(s) will be stepped to a position outside the domain.')
|
||||
receiver.xcoord = receiver.xcoordbase + (modelrun - 1) * G.rxstepx
|
||||
receiver.ycoord = receiver.ycoordbase + (modelrun - 1) * G.rxstepy
|
||||
receiver.zcoord = receiver.zcoordbase + (modelrun - 1) * G.rxstepz
|
||||
|
||||
# Write files for any geometry views
|
||||
if not G.geometryviews and args.geometry_only:
|
||||
@@ -352,31 +389,13 @@ def run_model(args, modelrun, numbermodelruns, inputfile, usernamespace):
|
||||
tgeoend = perf_counter()
|
||||
print('\nGeometry file(s) written in [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tgeoend - tgeostart))))
|
||||
|
||||
# Run simulation if not doing only geometry
|
||||
# Run simulation (if not doing geometry only)
|
||||
if not args.geometry_only:
|
||||
|
||||
# Prepare any snapshot files
|
||||
for snapshot in G.snapshots:
|
||||
snapshot.prepare_vtk_imagedata(modelrun, numbermodelruns, G)
|
||||
|
||||
# Adjust position of sources and receivers if required
|
||||
if G.srcstepx > 0 or G.srcstepy > 0 or G.srcstepz > 0:
|
||||
for source in itertools.chain(G.hertziandipoles, G.magneticdipoles, G.voltagesources, G.transmissionlines):
|
||||
if modelrun == 1:
|
||||
if source.xcoord + G.srcstepx * (numbermodelruns - 1) > G.nx or source.ycoord + G.srcstepy * (numbermodelruns - 1) > G.ny or source.zcoord + G.srcstepz * (numbermodelruns - 1) > G.nz:
|
||||
raise GeneralError('Source(s) will be stepped to a position outside the domain.')
|
||||
source.xcoord += (modelrun - 1) * G.srcstepx
|
||||
source.ycoord += (modelrun - 1) * G.srcstepy
|
||||
source.zcoord += (modelrun - 1) * G.srcstepz
|
||||
if G.rxstepx > 0 or G.rxstepy > 0 or G.rxstepz > 0:
|
||||
for receiver in G.rxs:
|
||||
if modelrun == 1:
|
||||
if receiver.xcoord + G.rxstepx * (numbermodelruns - 1) > G.nx or receiver.ycoord + G.rxstepy * (numbermodelruns - 1) > G.ny or receiver.zcoord + G.rxstepz * (numbermodelruns - 1) > G.nz:
|
||||
raise GeneralError('Receiver(s) will be stepped to a position outside the domain.')
|
||||
receiver.xcoord += (modelrun - 1) * G.rxstepx
|
||||
receiver.ycoord += (modelrun - 1) * G.rxstepy
|
||||
receiver.zcoord += (modelrun - 1) * G.rxstepz
|
||||
|
||||
# Prepare output file
|
||||
inputfileparts = os.path.splitext(inputfile)
|
||||
if numbermodelruns == 1:
|
||||
@@ -466,6 +485,10 @@ def run_model(args, modelrun, numbermodelruns, inputfile, usernamespace):
|
||||
print('\n\nSolving took [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tsolveend - tsolvestart))))
|
||||
print('Peak memory (approx) used: {}'.format(human_size(p.memory_info().rss)))
|
||||
|
||||
# If geometry information to be reused between model runs then FDTDGrid class instance must be global so that it persists
|
||||
if not args.geometry_fixed:
|
||||
del G
|
||||
|
||||
return int(tsolveend - tsolvestart)
|
||||
|
||||
|
||||
|
@@ -22,6 +22,7 @@ import matplotlib.pyplot as plt
|
||||
from gprMax.constants import c, floattype, complextype
|
||||
from gprMax.materials import Material
|
||||
|
||||
|
||||
class Grid():
|
||||
|
||||
def __init__(self, grid):
|
||||
@@ -46,10 +47,12 @@ class Grid():
|
||||
def get(self, i, j, k):
|
||||
return self.grid[i, j, k]
|
||||
|
||||
|
||||
class FDTDGrid(Grid):
|
||||
"""Holds attributes associated with the entire grid. A convenient way for accessing regularly used parameters."""
|
||||
|
||||
def __init__(self):
|
||||
self.inputfilename = ''
|
||||
self.inputdirectory = ''
|
||||
self.title = ''
|
||||
self.messages = True
|
||||
@@ -76,25 +79,27 @@ class FDTDGrid(Grid):
|
||||
self.hertziandipoles = []
|
||||
self.magneticdipoles = []
|
||||
self.transmissionlines = []
|
||||
self.rxs = []
|
||||
self.srcstepx = 0
|
||||
self.srcstepy = 0
|
||||
self.srcstepz = 0
|
||||
self.rxstepx = 0
|
||||
self.rxstepy = 0
|
||||
self.rxstepz = 0
|
||||
self.rxs = []
|
||||
self.snapshots = []
|
||||
|
||||
def initialise_std_arrays(self):
|
||||
def initialise_geometry_arrays(self):
|
||||
"""Initialise an array for volumetric material IDs (solid); boolean arrays for specifying whether materials can have dielectric smoothing (rigid);
|
||||
an array for cell edge IDs (ID); and arrays for the electric and magnetic field components. Solid and ID arrays are initialised to free_space (one); rigid arrays
|
||||
to allow dielectric smoothing (zero).
|
||||
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).
|
||||
"""
|
||||
self.solid = np.ones((self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.uint32)
|
||||
self.rigidE = np.zeros((12, self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.int8)
|
||||
self.rigidH = np.zeros((6, self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.int8)
|
||||
self.IDlookup = {'Ex': 0, 'Ey': 1, 'Ez': 2, 'Hx': 3, 'Hy': 4, 'Hz': 5}
|
||||
self.ID = np.ones((6, self.nx + 1, self.ny + 1, self.nz + 1), dtype=np.uint32)
|
||||
|
||||
def initialise_field_arrays(self):
|
||||
"""Initialise arrays for the electric and magnetic field components."""
|
||||
self.Ex = np.zeros((self.nx, self.ny + 1, self.nz + 1), dtype=floattype)
|
||||
self.Ey = np.zeros((self.nx + 1, self.ny, self.nz + 1), dtype=floattype)
|
||||
self.Ez = np.zeros((self.nx + 1, self.ny + 1, self.nz), dtype=floattype)
|
||||
@@ -102,7 +107,7 @@ class FDTDGrid(Grid):
|
||||
self.Hy = np.zeros((self.nx, self.ny + 1, self.nz), dtype=floattype)
|
||||
self.Hz = np.zeros((self.nx, self.ny, self.nz + 1), dtype=floattype)
|
||||
|
||||
def initialise_std_updatecoeff_arrays(self):
|
||||
def initialise_std_update_coeff_arrays(self):
|
||||
"""Initialise arrays for storing update coefficients."""
|
||||
self.updatecoeffsE = np.zeros((len(self.materials), 5), dtype=floattype)
|
||||
self.updatecoeffsH = np.zeros((len(self.materials), 5), dtype=floattype)
|
||||
@@ -132,31 +137,38 @@ def dispersion_check(G):
|
||||
maxfreqs = []
|
||||
for waveform in G.waveforms:
|
||||
|
||||
# User-defined waveform
|
||||
if waveform.uservalues is not None:
|
||||
waveformvalues = waveform.uservalues
|
||||
if waveform.type == 'sine' or waveform.type == 'contsine':
|
||||
maxfreqs.append(4 * waveform.freq)
|
||||
|
||||
elif waveform.type =='impulse':
|
||||
pass
|
||||
|
||||
# Built-in waveform
|
||||
else:
|
||||
time = np.linspace(0, 1, G.iterations)
|
||||
time *= (G.iterations * G.dt)
|
||||
waveformvalues = np.zeros(len(time))
|
||||
timeiter = np.nditer(time, flags=['c_index'])
|
||||
# User-defined waveform
|
||||
if waveform.type == 'user':
|
||||
waveformvalues = waveform.uservalues
|
||||
|
||||
while not timeiter.finished:
|
||||
waveformvalues[timeiter.index] = waveform.calculate_value(timeiter[0], G.dt)
|
||||
timeiter.iternext()
|
||||
# Built-in waveform
|
||||
else:
|
||||
time = np.linspace(0, 1, G.iterations)
|
||||
time *= (G.iterations * G.dt)
|
||||
waveformvalues = np.zeros(len(time))
|
||||
timeiter = np.nditer(time, flags=['c_index'])
|
||||
|
||||
# Calculate magnitude of frequency spectra of waveform
|
||||
power = 20 * np.log10(np.abs(np.fft.fft(waveformvalues))**2)
|
||||
freqs = np.fft.fftfreq(power.size, d=G.dt)
|
||||
while not timeiter.finished:
|
||||
waveformvalues[timeiter.index] = waveform.calculate_value(timeiter[0], G.dt)
|
||||
timeiter.iternext()
|
||||
|
||||
# Shift powers so that frequency with maximum power is at zero decibels
|
||||
power -= np.amax(power)
|
||||
# Calculate magnitude of frequency spectra of waveform
|
||||
power = 20 * np.log10(np.abs(np.fft.fft(waveformvalues))**2)
|
||||
freqs = np.fft.fftfreq(power.size, d=G.dt)
|
||||
|
||||
# Set maximum frequency to -60dB from maximum power
|
||||
freq = np.where((np.amax(power[1::]) - power[1::]) > 60)[0][0] + 1
|
||||
maxfreqs.append(freqs[freq])
|
||||
# Shift powers so that frequency with maximum power is at zero decibels
|
||||
power -= np.amax(power)
|
||||
|
||||
# Set maximum frequency to -60dB from maximum power
|
||||
freq = np.where((np.amax(power[1::]) - power[1::]) > 60)[0][0] + 1
|
||||
maxfreqs.append(freqs[freq])
|
||||
|
||||
if maxfreqs:
|
||||
maxfreq = max(maxfreqs)
|
||||
@@ -175,7 +187,7 @@ def dispersion_check(G):
|
||||
resolution = minwavelength / resolvedsteps
|
||||
|
||||
else:
|
||||
resolution = 0
|
||||
resolution = False
|
||||
|
||||
return resolution
|
||||
|
||||
|
@@ -33,6 +33,8 @@ def process_python_include_code(inputfile, usernamespace):
|
||||
processedlines (list): Input commands after Python processing.
|
||||
"""
|
||||
|
||||
userpython = False
|
||||
|
||||
with open(inputfile, 'r') as f:
|
||||
# Strip out any newline characters and comments that must begin with double hashes
|
||||
inputlines = [line.rstrip() for line in f if(not line.startswith('##') and line.rstrip('\n'))]
|
||||
@@ -45,6 +47,9 @@ def process_python_include_code(inputfile, usernamespace):
|
||||
|
||||
# Process any Python code
|
||||
if(inputlines[x].startswith('#python:')):
|
||||
# Save stdout location to restore later
|
||||
stdout = sys.stdout
|
||||
|
||||
# String to hold Python code to be executed
|
||||
pythoncode = ''
|
||||
x += 1
|
||||
@@ -67,12 +72,15 @@ def process_python_include_code(inputfile, usernamespace):
|
||||
# Add processed Python code to list
|
||||
processedlines.extend(codeproc)
|
||||
|
||||
# Reset stdio
|
||||
sys.stdout = stdout
|
||||
|
||||
# Process any include commands
|
||||
elif(inputlines[x].startswith('#include:')):
|
||||
elif(inputlines[x].startswith('#include_file:')):
|
||||
includefile = inputlines[x].split()
|
||||
|
||||
if len(includefile) != 2:
|
||||
raise CmdInputError('#include requires exactly one parameter')
|
||||
raise CmdInputError('#include_file requires exactly one parameter')
|
||||
|
||||
includefile = includefile[1]
|
||||
|
||||
@@ -95,8 +103,6 @@ def process_python_include_code(inputfile, usernamespace):
|
||||
|
||||
x += 1
|
||||
|
||||
sys.stdout = sys.__stdout__ # Reset stdio
|
||||
|
||||
return processedlines
|
||||
|
||||
|
||||
|
@@ -53,7 +53,7 @@ def process_geometrycmds(geometry, G):
|
||||
|
||||
# See if material file exists at specified path and if not try input file directory
|
||||
if not os.path.isfile(matfile):
|
||||
matfile = os.path.join(G.inputdirectory, matfile)
|
||||
matfile = os.path.abspath(os.path.join(G.inputdirectory, matfile))
|
||||
|
||||
# Read materials from file
|
||||
with open(matfile, 'r') as f:
|
||||
@@ -70,25 +70,21 @@ def process_geometrycmds(geometry, G):
|
||||
|
||||
# See if geometry object file exists at specified path and if not try input file directory
|
||||
if not os.path.isfile(geofile):
|
||||
geofile = os.path.join(G.inputdirectory, geofile)
|
||||
geofile = os.path.abspath(os.path.join(G.inputdirectory, geofile))
|
||||
|
||||
# Open geometry object file and read spatial resolution attribute
|
||||
# Open geometry object file and read/check spatial resolution attribute
|
||||
f = h5py.File(geofile, 'r')
|
||||
dx_dy_dz = f.attrs['dx, dy, dz']
|
||||
if dx_dy_dz[0] != G.dx or dx_dy_dz[1] != G.dy or dx_dy_dz[2] != G.dz:
|
||||
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires the spatial resolution of the objects file to match the spatial resolution of the model')
|
||||
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' requires the spatial resolution of the geometry objects file to match the spatial resolution of the model')
|
||||
|
||||
data = f['/data'][:]
|
||||
nx = data.shape[0]
|
||||
ny = data.shape[1]
|
||||
nz = data.shape[2]
|
||||
|
||||
if (xs + nx) > G.nx or (ys + ny) > G.ny or (zs + nz) > G.nz:
|
||||
raise CmdInputError("'" + ' '.join(tmp) + "'" + ' the requested geometry objects do not fit within the model domain')
|
||||
|
||||
data += numexistmaterials
|
||||
build_voxels_from_array(xs, ys, zs, data, G.solid, G.rigidE, G.rigidH, G.ID)
|
||||
|
||||
if G.messages:
|
||||
print('Geometry objects from file {} inserted at {:g}m, {:g}m, {:g}m, with corresponding materials file {}.'.format(geofile, xs * G.dx, ys * G.dy, zs * G.dz, matfile))
|
||||
|
||||
|
||||
elif tmp[0] == '#edge:':
|
||||
if len(tmp) != 8:
|
||||
|
@@ -160,6 +160,9 @@ def process_multicmds(multicmds, G):
|
||||
h.xcoord = xcoord
|
||||
h.ycoord = ycoord
|
||||
h.zcoord = zcoord
|
||||
h.xcoordbase = xcoord
|
||||
h.ycoordbase = ycoord
|
||||
h.zcoordbase = zcoord
|
||||
h.ID = 'HertzianDipole(' + str(h.xcoord) + ',' + str(h.ycoord) + ',' + str(h.zcoord) + ')'
|
||||
h.waveformID = tmp[4]
|
||||
|
||||
@@ -222,6 +225,9 @@ def process_multicmds(multicmds, G):
|
||||
m.xcoord = xcoord
|
||||
m.ycoord = ycoord
|
||||
m.zcoord = zcoord
|
||||
m.xcoordbase = xcoord
|
||||
m.ycoordbase = ycoord
|
||||
m.zcoordbase = zcoord
|
||||
m.ID = 'MagneticDipole(' + str(m.xcoord) + ',' + str(m.ycoord) + ',' + str(m.zcoord) + ')'
|
||||
m.waveformID = tmp[4]
|
||||
|
||||
@@ -340,7 +346,13 @@ def process_multicmds(multicmds, G):
|
||||
if xcoord < G.pmlthickness[0] or xcoord > G.nx - G.pmlthickness[3] or ycoord < G.pmlthickness[1] or ycoord > G.ny - G.pmlthickness[4] or zcoord < G.pmlthickness[2] or zcoord > G.nz - G.pmlthickness[5]:
|
||||
print("WARNING: '" + cmdname + ': ' + ' '.join(tmp) + "'" + ' sources and receivers should not normally be positioned within the PML.\n')
|
||||
|
||||
r = Rx(xcoord=xcoord, ycoord=ycoord, zcoord=zcoord)
|
||||
r = Rx()
|
||||
r.xcoord = xcoord
|
||||
r.ycoord = ycoord
|
||||
r.zcoord = zcoord
|
||||
r.xcoordbase = xcoord
|
||||
r.ycoordbase = ycoord
|
||||
r.zcoordbase = zcoord
|
||||
|
||||
# If no ID or outputs are specified, use default i.e Ex, Ey, Ez, Hx, Hy, Hz, Ix, Iy, Iz
|
||||
if len(tmp) == 3:
|
||||
@@ -403,7 +415,13 @@ def process_multicmds(multicmds, G):
|
||||
for x in range(xs, xf, dx):
|
||||
for y in range(ys, yf, dy):
|
||||
for z in range(zs, zf, dz):
|
||||
r = Rx(xcoord=x, ycoord=y, zcoord=z)
|
||||
r = Rx()
|
||||
r.xcoord = x
|
||||
r.ycoord = y
|
||||
r.zcoord = z
|
||||
r.xcoordbase = x
|
||||
r.ycoordbase = y
|
||||
r.zcoordbase = z
|
||||
r.ID = 'Rx(' + str(x) + ',' + str(y) + ',' + str(z) + ')'
|
||||
G.rxs.append(r)
|
||||
|
||||
@@ -429,15 +447,16 @@ def process_multicmds(multicmds, G):
|
||||
dy = round_value(float(tmp[7])/G.dy)
|
||||
dz = round_value(float(tmp[8])/G.dz)
|
||||
|
||||
# If number of iterations given
|
||||
try:
|
||||
time = int(tmp[9])
|
||||
# If real floating point value given
|
||||
if '.' in tmp[9] or 'e' in tmp[9]:
|
||||
if float(tmp[9]) > 0:
|
||||
time = round_value((float(tmp[9]) / G.dt)) + 1
|
||||
except:
|
||||
time = float(tmp[9])
|
||||
if time > 0:
|
||||
time = round_value((time / G.dt)) + 1
|
||||
else:
|
||||
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' time value must be greater than zero')
|
||||
# If number of iterations given
|
||||
else:
|
||||
time = int(tmp[9])
|
||||
|
||||
if xs < 0 or xs > G.nx:
|
||||
raise CmdInputError("'" + cmdname + ': ' + ' '.join(tmp) + "'" + ' the lower x-coordinate {:g}m is not within the model domain'.format(xs * G.dx))
|
||||
@@ -463,7 +482,7 @@ def process_multicmds(multicmds, G):
|
||||
s = Snapshot(xs, ys, zs, xf, yf, zf, dx, dy, dz, time, tmp[10])
|
||||
|
||||
if G.messages:
|
||||
print('Snapshot from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, discretisation {:g}m, {:g}m, {:g}m, at {:g} secs with filename {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dx * G.dy, dx * G.dz, s.time * G.dt, s.filename))
|
||||
print('Snapshot from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, discretisation {:g}m, {:g}m, {:g}m, at {:g} secs with filename {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dx * G.dy, dx * G.dz, s.time * G.dt, s.basefilename))
|
||||
|
||||
G.snapshots.append(s)
|
||||
|
||||
@@ -533,7 +552,7 @@ def process_multicmds(multicmds, G):
|
||||
Material.maxpoles = material.poles
|
||||
|
||||
if G.messages:
|
||||
print('Debye-type disperion added to {} with delta_epsr={}, and tau={} secs created.'.format(material.ID, ','.join('%4.2f' % deltaer for deltaer in material.deltaer), ','.join('%4.3e' % tau for tau in material.tau)))
|
||||
print('Debye disperion added to {} with delta_epsr={}, and tau={} secs created.'.format(material.ID, ', '.join('%4.2f' % deltaer for deltaer in material.deltaer), ', '.join('%4.3e' % tau for tau in material.tau)))
|
||||
|
||||
cmdname = '#add_dispersion_lorentz'
|
||||
if multicmds[cmdname] != 'None':
|
||||
@@ -569,7 +588,7 @@ def process_multicmds(multicmds, G):
|
||||
Material.maxpoles = material.poles
|
||||
|
||||
if G.messages:
|
||||
print('Lorentz-type disperion added to {} with delta_epsr={}, omega={} secs, and gamma={} created.'.format(material.ID, ','.join('%4.2f' % deltaer for deltaer in material.deltaer), ','.join('%4.3e' % tau for tau in material.tau), ','.join('%4.3e' % alpha for alpha in material.alpha)))
|
||||
print('Lorentz disperion added to {} with delta_epsr={}, omega={} secs, and gamma={} created.'.format(material.ID, ', '.join('%4.2f' % deltaer for deltaer in material.deltaer), ', '.join('%4.3e' % tau for tau in material.tau), ', '.join('%4.3e' % alpha for alpha in material.alpha)))
|
||||
|
||||
|
||||
cmdname = '#add_dispersion_drude'
|
||||
@@ -605,7 +624,7 @@ def process_multicmds(multicmds, G):
|
||||
Material.maxpoles = material.poles
|
||||
|
||||
if G.messages:
|
||||
print('Drude-type disperion added to {} with omega={} secs, and gamma={} secs created.'.format(material.ID, ','.join('%4.3e' % tau for tau in material.tau), ','.join('%4.3e' % alpha for alpha in material.alpha)))
|
||||
print('Drude disperion added to {} with omega={} secs, and gamma={} secs created.'.format(material.ID, ', '.join('%4.3e' % tau for tau in material.tau), ', '.join('%4.3e' % alpha for alpha in material.alpha)))
|
||||
|
||||
|
||||
cmdname = '#soil_peplinski'
|
||||
@@ -685,7 +704,7 @@ def process_multicmds(multicmds, G):
|
||||
g = GeometryView(xs, ys, zs, xf, yf, zf, dx, dy, dz, tmp[9], tmp[10].lower())
|
||||
|
||||
if G.messages:
|
||||
print('Geometry view from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, discretisation {:g}m, {:g}m, {:g}m, filename {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dy * G.dy, dz * G.dz, g.filename))
|
||||
print('Geometry view from {:g}m, {:g}m, {:g}m, to {:g}m, {:g}m, {:g}m, discretisation {:g}m, {:g}m, {:g}m, filename {} created.'.format(xs * G.dx, ys * G.dy, zs * G.dz, xf * G.dx, yf * G.dy, zf * G.dz, dx * G.dx, dy * G.dy, dz * G.dz, g.basefilename))
|
||||
|
||||
# Append the new GeometryView object to the geometry views list
|
||||
G.geometryviews.append(g)
|
||||
|
@@ -164,17 +164,20 @@ def process_singlecmds(singlecmds, G):
|
||||
if len(tmp) != 1:
|
||||
raise CmdInputError(cmd + ' requires exactly one parameter to specify the time window. Either in seconds or number of iterations.')
|
||||
tmp = tmp[0].lower()
|
||||
|
||||
# If number of iterations given
|
||||
try:
|
||||
tmp = int(tmp)
|
||||
G.timewindow = (tmp - 1) * G.dt
|
||||
G.iterations = tmp
|
||||
# If real floating point value given
|
||||
if '.' in tmp or 'e' in tmp:
|
||||
if float(tmp) > 0:
|
||||
G.timewindow = float(tmp)
|
||||
G.iterations = round_value((float(tmp) / G.dt)) + 1
|
||||
except:
|
||||
tmp = float(tmp)
|
||||
if tmp > 0:
|
||||
G.timewindow = tmp
|
||||
G.iterations = round_value((tmp / G.dt)) + 1
|
||||
else:
|
||||
raise CmdInputError(cmd + ' must have a value greater than zero')
|
||||
# If number of iterations given
|
||||
else:
|
||||
G.timewindow = (int(tmp) - 1) * G.dt
|
||||
G.iterations = int(tmp)
|
||||
if G.messages:
|
||||
print('Time window: {:g} secs ({} iterations)'.format(G.timewindow, G.iterations))
|
||||
|
||||
@@ -203,7 +206,7 @@ def process_singlecmds(singlecmds, G):
|
||||
G.srcstepy = round_value(float(tmp[1])/G.dy)
|
||||
G.srcstepz = round_value(float(tmp[2])/G.dz)
|
||||
if G.messages:
|
||||
print('All sources will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.srcstepx * G.dx, G.srcstepy * G.dy, G.srcstepz * G.dz))
|
||||
print('Simple sources will step {:g}m, {:g}m, {:g}m for each model run.'.format(G.srcstepx * G.dx, G.srcstepy * G.dy, G.srcstepz * G.dz))
|
||||
|
||||
|
||||
# rx_steps
|
||||
@@ -229,7 +232,7 @@ def process_singlecmds(singlecmds, G):
|
||||
|
||||
# See if file exists at specified path and if not try input file directory
|
||||
if not os.path.isfile(excitationfile):
|
||||
excitationfile = os.path.join(G.inputdirectory, excitationfile)
|
||||
excitationfile = os.path.abspath(os.path.join(G.inputdirectory, excitationfile))
|
||||
|
||||
# Get waveform names
|
||||
with open(excitationfile, 'r') as f:
|
||||
|
@@ -21,7 +21,7 @@ import numpy as np
|
||||
from gprMax.constants import e0, m0, floattype, complextype
|
||||
|
||||
|
||||
class Material():
|
||||
class Material(object):
|
||||
"""Materials, their properties and update coefficients."""
|
||||
|
||||
# Maximum number of dispersive material poles in a model
|
||||
@@ -144,7 +144,7 @@ class Material():
|
||||
self.srce = 1 / EA
|
||||
|
||||
|
||||
class PeplinskiSoil:
|
||||
class PeplinskiSoil(object):
|
||||
"""Soil objects that are characterised according to a mixing model by Peplinski (http://dx.doi.org/10.1109/36.387598)."""
|
||||
|
||||
def __init__(self, ID, sandfraction, clayfraction, bulkdensity, sandpartdensity, watervolfraction):
|
||||
|
@@ -62,11 +62,17 @@ def run_opt_sim(args, numbermodelruns, inputfile, usernamespace):
|
||||
optparamshist = OrderedDict((key, list()) for key in optparams)
|
||||
|
||||
# Import specified fitness function
|
||||
fitness_metric = getattr(importlib.import_module('user_libs.optimisation_taguchi.optimisation_taguchi_fitness'), fitness['name'])
|
||||
fitness_metric = getattr(importlib.import_module('user_libs.optimisation_taguchi.fitness_functions'), fitness['name'])
|
||||
|
||||
# Select OA
|
||||
OA, N, cols, k, s, t = construct_OA(optparams)
|
||||
print('\n{}\n\nTaguchi optimisation: orthogonal array with {} experiments, {} parameters ({} used), {} levels, and strength {} will be used.'.format(68*'*', N, cols, k, s, t))
|
||||
print('\n{}\nTaguchi optimisation...\n'.format(68*'*'))
|
||||
print('\tOrthogonal array: {:g} experiments per iteration, {:g} parameters ({:g} will be used), {:g} levels, and strength {:g}'.format(N, cols, k, s, t))
|
||||
tmp = [(k, v) for k, v in optparams.items()]
|
||||
print('\tParameters to optimise with ranges: {}'.format(str(tmp).strip('[]')))
|
||||
print('\tOutput name(s) from model: {}'.format(fitness['args']['outputs']))
|
||||
print('\tFitness function {} with stopping criterion {:g}'.format(fitness['name'], fitness['stop']))
|
||||
print('\tMaximum iterations: {:g}'.format(maxiterations))
|
||||
|
||||
# Initialise arrays and lists to store parameters required throughout optimisation
|
||||
# Lower, central, and upper values for each parameter
|
||||
@@ -132,12 +138,12 @@ def run_opt_sim(args, numbermodelruns, inputfile, usernamespace):
|
||||
break
|
||||
|
||||
# Stop optimisation if successive fitness values are within a percentage threshold
|
||||
# if iteration > 2:
|
||||
# fitnessvaluesclose = (np.abs(fitnessvalueshist[iteration - 2] - fitnessvalueshist[iteration - 1]) / fitnessvalueshist[iteration - 1]) * 100
|
||||
# fitnessvaluesthres = 0.1
|
||||
# if fitnessvaluesclose < fitnessvaluesthres:
|
||||
# print('\nTaguchi optimisation stopped as successive fitness values within {}%'.format(fitnessvaluesthres))
|
||||
# break
|
||||
if iteration > 2:
|
||||
fitnessvaluesclose = (np.abs(fitnessvalueshist[iteration - 2] - fitnessvalueshist[iteration - 1]) / fitnessvalueshist[iteration - 1]) * 100
|
||||
fitnessvaluesthres = 0.1
|
||||
if fitnessvaluesclose < fitnessvaluesthres:
|
||||
print('\nTaguchi optimisation stopped as successive fitness values within {}%'.format(fitnessvaluesthres))
|
||||
break
|
||||
|
||||
# Save optimisation parameters history and fitness values history to file
|
||||
opthistfile = inputfileparts[0] + '_hist.pickle'
|
||||
@@ -164,6 +170,9 @@ def taguchi_code_blocks(inputfile, taguchinamespace):
|
||||
# Strip out any newline characters and comments that must begin with double hashes
|
||||
inputlines = [line.rstrip() for line in f if(not line.startswith('##') and line.rstrip('\n'))]
|
||||
|
||||
# Store length of dict
|
||||
taglength = len(taguchinamespace)
|
||||
|
||||
x = 0
|
||||
while(x < len(inputlines)):
|
||||
if(inputlines[x].startswith('#taguchi:')):
|
||||
@@ -185,6 +194,10 @@ def taguchi_code_blocks(inputfile, taguchinamespace):
|
||||
|
||||
x += 1
|
||||
|
||||
# Check if any Taguchi code blocks were found
|
||||
if len(taguchinamespace) == taglength:
|
||||
raise CmdInputError('No #taguchi and #end_taguchi code blocks found.')
|
||||
|
||||
return taguchinamespace
|
||||
|
||||
|
||||
@@ -203,6 +216,9 @@ def construct_OA(optparams):
|
||||
t (int): Strength of OA
|
||||
"""
|
||||
|
||||
oadirectory = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, 'user_libs', 'optimisation_taguchi')
|
||||
oadirectory = os.path.abspath(oadirectory)
|
||||
|
||||
# Properties of the orthogonal array (OA)
|
||||
# Strength
|
||||
t = 2
|
||||
@@ -215,7 +231,7 @@ def construct_OA(optparams):
|
||||
|
||||
# Load the appropriate OA
|
||||
if k <= 4:
|
||||
OA = np.load(os.path.join('user_libs', 'OA_9_4_3_2.npy'))
|
||||
OA = np.load(os.path.join(oadirectory, 'OA_9_4_3_2.npy'))
|
||||
|
||||
# Number of experiments
|
||||
N = OA.shape[0]
|
||||
@@ -227,7 +243,7 @@ def construct_OA(optparams):
|
||||
OA = OA[:, 0:k]
|
||||
|
||||
elif k <= 7:
|
||||
OA = np.load(os.path.join('user_libs', 'OA_18_7_3_2.npy'))
|
||||
OA = np.load(os.path.join(oadirectory, 'OA_18_7_3_2.npy'))
|
||||
|
||||
# Number of experiments
|
||||
N = OA.shape[0]
|
||||
@@ -238,8 +254,10 @@ def construct_OA(optparams):
|
||||
# Cut down OA columns to number of parameters to optimise
|
||||
OA = OA[:, 0:k]
|
||||
|
||||
# THIS CASE NEEDS FURTHER TESTING
|
||||
else:
|
||||
# THIS CASE NEEDS FURTHER TESTING
|
||||
print('\nTaguchi optimisation, WARNING: Optimising more than 7 parameters is currently an experimental feature!')
|
||||
|
||||
p = int(np.ceil(np.log(k * (s - 1) + 1) / np.log(s)))
|
||||
|
||||
# Number of experiments
|
||||
@@ -380,8 +398,8 @@ def calculate_optimal_levels(optparams, levels, levelsopt, fitnessvalues, OA, N,
|
||||
# Calculate optimal level from table of responses
|
||||
optlevel = np.where(responses == np.amax(responses))[0]
|
||||
|
||||
# If 2 experiments produce the same fitness value (this shouldn't happen if the fitness function is designed correctly)
|
||||
if len(optlevel):
|
||||
# If 2 experiments produce the same fitness value (this shouldn't happen if the fitness function is designed correctly) pick first level
|
||||
if len(optlevel) > 1:
|
||||
optlevel = optlevel[0]
|
||||
|
||||
levelsopt[p] = optlevel
|
||||
|
@@ -23,7 +23,7 @@ from gprMax.pml_1order_update import *
|
||||
from gprMax.pml_2order_update import *
|
||||
|
||||
|
||||
class CFSParameter:
|
||||
class CFSParameter(object):
|
||||
"""Individual CFS parameter (e.g. alpha, kappa, or sigma)."""
|
||||
|
||||
# Allowable scaling profiles and directions
|
||||
@@ -49,7 +49,7 @@ class CFSParameter:
|
||||
self.max = max
|
||||
|
||||
|
||||
class CFS:
|
||||
class CFS(object):
|
||||
"""CFS term for PML."""
|
||||
|
||||
def __init__(self):
|
||||
@@ -140,7 +140,7 @@ class CFS:
|
||||
return Evalues, Hvalues
|
||||
|
||||
|
||||
class PML:
|
||||
class PML(object):
|
||||
"""PML - the implementation comes from the derivation in: http://dx.doi.org/10.1109/TAP.2011.2180344"""
|
||||
|
||||
directions = {0: 'xminus', 1: 'yminus', 2: 'zminus', 3: 'xplus', 4: 'yplus', 5: 'zplus'}
|
||||
@@ -165,6 +165,10 @@ class PML:
|
||||
self.CFS = G.cfs
|
||||
if not self.CFS:
|
||||
self.CFS = [CFS()]
|
||||
self.initialise_field_arrays()
|
||||
|
||||
def initialise_field_arrays(self):
|
||||
"""Initialise arrays to store fields in PML."""
|
||||
|
||||
# Subscript notation, e.g. 'EPhiyxz' means the electric field Phi vector, of which the
|
||||
# component being corrected is y, the stretching direction is x, and field derivative
|
||||
@@ -188,15 +192,6 @@ class PML:
|
||||
self.HPhixzy = np.zeros((len(self.CFS), self.nx + 1, self.ny, self.nz), dtype=floattype)
|
||||
self.HPhiyzx = np.zeros((len(self.CFS), self.nx, self.ny + 1, self.nz), dtype=floattype)
|
||||
|
||||
self.ERA = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.ERB = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.ERE = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.ERF = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.HRA = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.HRB = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.HRE = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.HRF = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
|
||||
def calculate_update_coeffs(self, er, mr, G):
|
||||
"""Calculates electric and magnetic update coefficients for the PML.
|
||||
|
||||
@@ -206,6 +201,15 @@ class PML:
|
||||
G (class): Grid class instance - holds essential parameters describing the model.
|
||||
"""
|
||||
|
||||
self.ERA = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.ERB = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.ERE = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.ERF = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.HRA = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.HRB = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.HRE = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
self.HRF = np.zeros((len(self.CFS), self.thickness + 1), dtype=floattype)
|
||||
|
||||
for x, cfs in enumerate(self.CFS):
|
||||
if not cfs.sigma.max:
|
||||
cfs.calculate_sigmamax(self.direction, er, mr, G)
|
||||
|
@@ -16,21 +16,18 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
class Rx:
|
||||
class Rx(object):
|
||||
"""Receiever output points."""
|
||||
|
||||
availableoutputs = ['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz', 'Ix', 'Iy', 'Iz']
|
||||
|
||||
def __init__(self, xcoord=None, ycoord=None, zcoord=None):
|
||||
"""
|
||||
Args:
|
||||
xcoord (float): x-coordinate of location in model.
|
||||
ycoord (float): y-coordinate of location in model.
|
||||
zcoord (float): z-coordinate of location in model.
|
||||
"""
|
||||
def __init__(self):
|
||||
|
||||
self.ID = None
|
||||
self.outputs = []
|
||||
self.xcoord = xcoord
|
||||
self.ycoord = ycoord
|
||||
self.zcoord = zcoord
|
||||
self.xcoord = None
|
||||
self.ycoord = None
|
||||
self.zcoord = None
|
||||
self.xcoordbase = None
|
||||
self.ycoordbase = None
|
||||
self.zcoordbase = None
|
@@ -16,7 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import os, sys
|
||||
import numpy as np
|
||||
from struct import pack
|
||||
|
||||
@@ -25,7 +25,7 @@ from gprMax.grid import Ix, Iy, Iz
|
||||
from gprMax.utilities import round_value
|
||||
|
||||
|
||||
class Snapshot:
|
||||
class Snapshot(object):
|
||||
"""Snapshots of the electric and magnetic field values."""
|
||||
|
||||
# Set string for byte order
|
||||
@@ -61,7 +61,7 @@ class Snapshot:
|
||||
self.dy = dy
|
||||
self.dz = dz
|
||||
self.time = time
|
||||
self.filename = filename
|
||||
self.basefilename = filename
|
||||
|
||||
def prepare_vtk_imagedata(self, modelrun, numbermodelruns, G):
|
||||
"""Prepares a VTK ImageData (.vti) file for a snapshot.
|
||||
@@ -77,11 +77,15 @@ class Snapshot:
|
||||
self.vtk_ny = self.yf - self.ys
|
||||
self.vtk_nz = self.zf - self.zs
|
||||
|
||||
# Construct filename from user-supplied name and model run number
|
||||
# Create directory and construct filename from user-supplied name and model run number
|
||||
if numbermodelruns == 1:
|
||||
self.filename = G.inputdirectory + self.filename + '.vti'
|
||||
snapshotdir = os.path.join(G.inputdirectory, os.path.splitext(G.inputfilename)[0] + '_snaps')
|
||||
else:
|
||||
self.filename = G.inputdirectory + self.filename + '_' + str(modelrun) + '.vti'
|
||||
snapshotdir = os.path.join(G.inputdirectory, os.path.splitext(G.inputfilename)[0] + '_snaps' + str(modelrun))
|
||||
|
||||
if not os.path.exists(snapshotdir):
|
||||
os.mkdir(snapshotdir)
|
||||
self.filename = os.path.abspath(os.path.join(snapshotdir, self.basefilename + '.vti'))
|
||||
|
||||
# Calculate number of cells according to requested sampling
|
||||
self.vtk_xscells = round_value(self.xs / self.dx)
|
||||
|
@@ -24,8 +24,8 @@ from gprMax.grid import Ix, Iy, Iz
|
||||
from gprMax.utilities import round_value
|
||||
|
||||
|
||||
class VoltageSource:
|
||||
"""The 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."""
|
||||
class Source(object):
|
||||
"""Super-class which describes a generic source."""
|
||||
|
||||
def __init__(self):
|
||||
self.ID = None
|
||||
@@ -33,11 +33,21 @@ class VoltageSource:
|
||||
self.xcoord = None
|
||||
self.ycoord = None
|
||||
self.zcoord = None
|
||||
self.xcoordbase = None
|
||||
self.ycoordbase = None
|
||||
self.zcoordbase = None
|
||||
self.start = None
|
||||
self.stop = None
|
||||
self.resistance = None
|
||||
self.waveformID = None
|
||||
|
||||
|
||||
class VoltageSource(Source):
|
||||
"""The 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."""
|
||||
|
||||
def __init__(self):
|
||||
super(Source, self).__init__()
|
||||
self.resistance = None
|
||||
|
||||
def update_electric(self, abstime, updatecoeffsE, ID, Ex, Ey, Ez, G):
|
||||
"""Updates electric field values for a voltage source.
|
||||
|
||||
@@ -59,19 +69,22 @@ class VoltageSource:
|
||||
|
||||
if self.polarisation is 'x':
|
||||
if self.resistance != 0:
|
||||
Ex[i, j, k] -= updatecoeffsE[ID[0, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (self.resistance * G.dy * G.dz))
|
||||
componentID = 'E' + self.polarisation
|
||||
Ex[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (self.resistance * G.dy * G.dz))
|
||||
else:
|
||||
Ex[i, j, k] = -1 * waveform.amp * waveform.calculate_value(time, G.dt) / G.dx
|
||||
|
||||
elif self.polarisation is 'y':
|
||||
if self.resistance != 0:
|
||||
Ey[i, j, k] -= updatecoeffsE[ID[1, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (self.resistance * G.dx * G.dz))
|
||||
componentID = 'E' + self.polarisation
|
||||
Ey[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (self.resistance * G.dx * G.dz))
|
||||
else:
|
||||
Ey[i, j, k] = -1 * waveform.amp * waveform.calculate_value(time, G.dt) / G.dy
|
||||
|
||||
elif self.polarisation is 'z':
|
||||
if self.resistance != 0:
|
||||
Ez[i, j, k] -= updatecoeffsE[ID[2, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (self.resistance * G.dx * G.dy))
|
||||
componentID = 'E' + self.polarisation
|
||||
Ez[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (self.resistance * G.dx * G.dy))
|
||||
else:
|
||||
Ez[i, j, k] = -1 * waveform.amp * waveform.calculate_value(time, G.dt) / G.dz
|
||||
|
||||
@@ -83,8 +96,12 @@ class VoltageSource:
|
||||
"""
|
||||
|
||||
if self.resistance != 0:
|
||||
ID = 'E' + self.polarisation
|
||||
requirednumID = G.ID[G.IDlookup[ID], self.xcoord, self.ycoord, self.zcoord]
|
||||
i = self.xcoord
|
||||
j = self.ycoord
|
||||
k = self.zcoord
|
||||
|
||||
componentID = '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 = material.ID + '+VoltageSource_' + str(self.resistance)
|
||||
@@ -99,22 +116,15 @@ class VoltageSource:
|
||||
elif self.polarisation == 'z':
|
||||
newmaterial.se += G.dz / (self.resistance * G.dx * G.dy)
|
||||
|
||||
G.ID[G.IDlookup[ID], self.xcoord, self.ycoord, self.zcoord] = newmaterial.numID
|
||||
G.ID[G.IDlookup[componentID], i, j, k] = newmaterial.numID
|
||||
G.materials.append(newmaterial)
|
||||
|
||||
|
||||
class HertzianDipole:
|
||||
class HertzianDipole(Source):
|
||||
"""The Hertzian dipole is an additive source (electric current density)."""
|
||||
|
||||
def __init__(self):
|
||||
self.ID = None
|
||||
self.polarisation = None
|
||||
self.xcoord = None
|
||||
self.ycoord = None
|
||||
self.zcoord = None
|
||||
self.start = None
|
||||
self.stop = None
|
||||
self.waveformID = None
|
||||
super(Source, self).__init__()
|
||||
|
||||
def update_electric(self, abstime, updatecoeffsE, ID, Ex, Ey, Ez, G):
|
||||
"""Updates electric field values for a Hertzian dipole.
|
||||
@@ -136,27 +146,23 @@ class HertzianDipole:
|
||||
waveform = next(x for x in G.waveforms if x.ID == self.waveformID)
|
||||
|
||||
if self.polarisation is 'x':
|
||||
Ex[i, j, k] -= updatecoeffsE[ID[0, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (G.dy * G.dz))
|
||||
componentID = 'E' + self.polarisation
|
||||
Ex[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (G.dy * G.dz))
|
||||
|
||||
elif self.polarisation is 'y':
|
||||
Ey[i, j, k] -= updatecoeffsE[ID[1, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (G.dx * G.dz))
|
||||
componentID = 'E' + self.polarisation
|
||||
Ey[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (G.dx * G.dz))
|
||||
|
||||
elif self.polarisation is 'z':
|
||||
Ez[i, j, k] -= updatecoeffsE[ID[2, i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (G.dx * G.dy))
|
||||
componentID = 'E' + self.polarisation
|
||||
Ez[i, j, k] -= updatecoeffsE[ID[G.IDlookup[componentID], i, j, k], 4] * waveform.amp * waveform.calculate_value(time, G.dt) * (1 / (G.dx * G.dy))
|
||||
|
||||
|
||||
class MagneticDipole:
|
||||
class MagneticDipole(Source):
|
||||
"""The magnetic dipole is an additive source (magnetic current density)."""
|
||||
|
||||
def __init__(self):
|
||||
self.ID = None
|
||||
self.polarisation = None
|
||||
self.xcoord = None
|
||||
self.ycoord = None
|
||||
self.zcoord = None
|
||||
self.start = None
|
||||
self.stop = None
|
||||
self.waveformID = None
|
||||
super(Source, self).__init__()
|
||||
|
||||
def update_magnetic(self, abstime, updatecoeffsH, ID, Hx, Hy, Hz, G):
|
||||
"""Updates magnetic field values for a magnetic dipole.
|
||||
@@ -187,7 +193,7 @@ class MagneticDipole:
|
||||
Hz[i, j, k] -= waveform.amp * waveform.calculate_value(time, G.dt) * (G.dt / (G.dx * G.dy * G.dz))
|
||||
|
||||
|
||||
class TransmissionLine:
|
||||
class TransmissionLine(Source):
|
||||
"""The transmission line source is a one-dimensional transmission line which is attached virtually to a grid cell."""
|
||||
|
||||
def __init__(self, G):
|
||||
@@ -196,15 +202,8 @@ class TransmissionLine:
|
||||
G (class): Grid class instance - holds essential parameters describing the model.
|
||||
"""
|
||||
|
||||
self.ID = None
|
||||
self.polarisation = None
|
||||
self.xcoord = None
|
||||
self.ycoord = None
|
||||
self.zcoord = None
|
||||
self.start = None
|
||||
self.stop = None
|
||||
super(Source, self).__init__()
|
||||
self.resistance = None
|
||||
self.waveformID = None
|
||||
|
||||
# Coefficients for ABC termination of end of the transmission line
|
||||
self.abcv0 = 0
|
||||
|
@@ -21,7 +21,7 @@ import decimal as d
|
||||
from pyfiglet import Figlet
|
||||
|
||||
|
||||
class ListStream:
|
||||
class ListStream(object):
|
||||
"""A list can be streamed into. Required when temporarily redirecting stdio to capture output from users Python code blocks."""
|
||||
|
||||
def __init__(self):
|
||||
|
@@ -21,7 +21,7 @@ import numpy as np
|
||||
from gprMax.utilities import round_value
|
||||
|
||||
|
||||
class Waveform:
|
||||
class Waveform(object):
|
||||
"""Definitions of waveform shapes that can be used with sources."""
|
||||
|
||||
types = ['gaussian', 'gaussiandot', 'gaussiandotnorm', 'gaussiandotdot', 'gaussiandotdotnorm', 'ricker', 'sine', 'contsine', 'impulse', 'user']
|
||||
@@ -89,7 +89,7 @@ class Waveform:
|
||||
waveform = ramp * np.sin(2 * np.pi * self.freq * time)
|
||||
|
||||
elif self.type == 'impulse':
|
||||
# time < G.dt condition required to do impulsive magnetic dipole
|
||||
# time < dt condition required to do impulsive magnetic dipole
|
||||
if time == 0 or time < dt:
|
||||
waveform = 1
|
||||
elif time >= dt:
|
||||
|
@@ -19,6 +19,7 @@
|
||||
import h5py
|
||||
import numpy as np
|
||||
|
||||
import gprMax
|
||||
from gprMax.constants import floattype
|
||||
from gprMax.grid import Ix, Iy, Iz
|
||||
|
||||
@@ -35,6 +36,7 @@ def prepare_hdf5(outputfile, G):
|
||||
"""
|
||||
|
||||
f = h5py.File(outputfile, 'w')
|
||||
f.attrs['gprMax'] = gprMax.__version__
|
||||
f.attrs['Title'] = G.title
|
||||
f.attrs['Iterations'] = G.iterations
|
||||
f.attrs['nx, ny, nz'] = (G.nx, G.ny, G.nz)
|
||||
|
@@ -108,10 +108,10 @@ cpdef void build_electric_components(np.uint32_t[:, :, ::1] solid, np.int8_t[:,
|
||||
"""
|
||||
|
||||
cdef Py_ssize_t i, j, k
|
||||
cdef int numID1, numID2, numID3, numID4
|
||||
cdef int numID1, numID2, numID3, numID4, componentID
|
||||
|
||||
# Ex component
|
||||
componentID = 0
|
||||
componentID = G.IDlookup['Ex']
|
||||
for i in range(0, G.nx):
|
||||
for j in range(1, G.ny):
|
||||
for k in range(1, G.nz):
|
||||
@@ -133,7 +133,7 @@ cpdef void build_electric_components(np.uint32_t[:, :, ::1] solid, np.int8_t[:,
|
||||
create_electric_average(i, j, k, numID1, numID2, numID3, numID4, componentID, G)
|
||||
|
||||
# Ey component
|
||||
componentID = 1
|
||||
componentID = G.IDlookup['Ey']
|
||||
for i in range(1, G.nx):
|
||||
for j in range(0, G.ny):
|
||||
for k in range(1, G.nz):
|
||||
@@ -155,7 +155,7 @@ cpdef void build_electric_components(np.uint32_t[:, :, ::1] solid, np.int8_t[:,
|
||||
create_electric_average(i, j, k, numID1, numID2, numID3, numID4, componentID, G)
|
||||
|
||||
# Ez component
|
||||
componentID = 2
|
||||
componentID = G.IDlookup['Ez']
|
||||
for i in range(1, G.nx):
|
||||
for j in range(1, G.ny):
|
||||
for k in range(0, G.nz):
|
||||
@@ -185,10 +185,10 @@ cpdef void build_magnetic_components(np.uint32_t[:, :, ::1] solid, np.int8_t[:,
|
||||
"""
|
||||
|
||||
cdef Py_ssize_t i, j, k
|
||||
cdef int numID1, numID2
|
||||
cdef int numID1, numID2, componentID
|
||||
|
||||
# Hx component
|
||||
componentID = 3
|
||||
componentID = G.IDlookup['Hx']
|
||||
for i in range(1, G.nx):
|
||||
for j in range(0, G.ny):
|
||||
for k in range(0, G.nz):
|
||||
@@ -208,7 +208,7 @@ cpdef void build_magnetic_components(np.uint32_t[:, :, ::1] solid, np.int8_t[:,
|
||||
create_magnetic_average(i, j, k, numID1, numID2, componentID, G)
|
||||
|
||||
# Hy component
|
||||
componentID = 4
|
||||
componentID = G.IDlookup['Hy']
|
||||
for i in range(0, G.nx):
|
||||
for j in range(1, G.ny):
|
||||
for k in range(0, G.nz):
|
||||
@@ -228,7 +228,7 @@ cpdef void build_magnetic_components(np.uint32_t[:, :, ::1] solid, np.int8_t[:,
|
||||
create_magnetic_average(i, j, k, numID1, numID2, componentID, G)
|
||||
|
||||
# Hz component
|
||||
componentID = 5
|
||||
componentID = G.IDlookup['Hz']
|
||||
for i in range(0, G.nx):
|
||||
for j in range(0, G.ny):
|
||||
for k in range(1, G.nz):
|
||||
|
7
setup.py
7
setup.py
@@ -109,11 +109,12 @@ if sys.platform == 'win32':
|
||||
extra_objects = []
|
||||
# Mac OS X - needs gcc (usually via HomeBrew) because the default compiler LLVM (clang) does not support OpenMP
|
||||
elif sys.platform == 'darwin':
|
||||
gccpath = glob.glob('/usr/local/bin/gcc-[4-5]*')
|
||||
gccpath = glob.glob('/usr/local/bin/gcc-[4-5-6]*')
|
||||
if gccpath:
|
||||
os.environ['CC'] = gccpath[0].split(os.sep)[-1]
|
||||
# Use newest gcc found
|
||||
os.environ['CC'] = gccpath[-1].split(os.sep)[-1]
|
||||
else:
|
||||
raise('Cannot find gcc 4.x or gcc 5.x 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 must be installed')
|
||||
raise('Cannot find gcc 4.x, 5.x or 6.x 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, i.e. --without-multilib, must be installed')
|
||||
compile_args = ['-O3', '-w', '-fstrict-aliasing', '-fno-common', '-fopenmp']
|
||||
linker_args = ['-fopenmp']
|
||||
extra_objects = []
|
||||
|
@@ -21,9 +21,6 @@ triangle(tx_pos[0] + dxdydz[0], tx_pos[1], tx_pos[2], tx_pos[0] + bowtie_dims[0]
|
||||
# Bowtie - lower x half
|
||||
triangle(tx_pos[0] + dxdydz[0], tx_pos[1], tx_pos[2], tx_pos[0] - bowtie_dims[0], tx_pos[1] - bowtie_dims[1]/2, tx_pos[2], tx_pos[0] - bowtie_dims[0], tx_pos[1] + bowtie_dims[1]/2, tx_pos[2], 0, 'pec')
|
||||
|
||||
# Uncomment to temporarily check location of source
|
||||
#edge(tx_pos[0], tx_pos[1], tx_pos[2], tx_pos[0] + dxdydz[0], tx_pos[1], tx_pos[2], 'pec')
|
||||
|
||||
# Detailed geometry view around bowtie and feed position
|
||||
geometry_view(tx_pos[0] - bowtie_dims[0] - 2*dxdydz[0], tx_pos[1] - bowtie_dims[1]/2 - 2*dxdydz[1], tx_pos[2] - 2*dxdydz[2], tx_pos[0] + bowtie_dims[0] + 2*dxdydz[0], tx_pos[1] + bowtie_dims[1]/2 + 2*dxdydz[1], tx_pos[2] + 2*dxdydz[2], dxdydz[0], dxdydz[1], dxdydz[2], title + '_pcb', type='f')
|
||||
|
||||
|
@@ -36,7 +36,8 @@ renderview.ResetCamera()
|
||||
|
||||
# Lists to hold material and sources/receiver identifiers written in VTK file in tags <gprMax3D> <Material> and <gprMax3D> <Sources/Receivers>
|
||||
materials = []
|
||||
srcs_rxs_pml = []
|
||||
srcs_pml = []
|
||||
rxs = []
|
||||
with open(model.FileName[0], 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('<Material'):
|
||||
@@ -46,14 +47,18 @@ with open(model.FileName[0], 'r') as f:
|
||||
elif line.startswith('<Sources') or line.startswith('<PML'):
|
||||
line.rstrip('\n')
|
||||
tmp = (int(ET.fromstring(line).text), ET.fromstring(line).attrib.get('name'))
|
||||
srcs_rxs_pml.append(tmp)
|
||||
srcs_pml.append(tmp)
|
||||
elif line.startswith('<Receivers'):
|
||||
line.rstrip('\n')
|
||||
tmp = (int(ET.fromstring(line).text), ET.fromstring(line).attrib.get('name'))
|
||||
rxs.append(tmp)
|
||||
|
||||
if materials:
|
||||
# Get range of data
|
||||
matdatarange = model.CellData.GetArray(0).GetRange()
|
||||
mat_datarange = model.CellData.GetArray('Material').GetRange()
|
||||
|
||||
# Create threshold for materials (name and numeric value)
|
||||
for x in range(0, int(matdatarange[1]) + 1):
|
||||
for x in range(0, int(mat_datarange[1]) + 1):
|
||||
for y in range(len(materials)):
|
||||
if materials[y][0] == x:
|
||||
threshold = Threshold(Input=model)
|
||||
@@ -67,27 +72,45 @@ if materials:
|
||||
thresholddisplay = Show(threshold, renderview)
|
||||
thresholddisplay.ColorArrayName = 'Material'
|
||||
|
||||
if srcs_rxs_pml:
|
||||
if srcs_pml:
|
||||
# Get ranges of data
|
||||
srcs_rxs_pml_datarange = model.CellData.GetArray(1).GetRange()
|
||||
srcs_pml_datarange = model.CellData.GetArray('Sources_PML').GetRange()
|
||||
|
||||
# Create threshold for sources/receivers (name and numeric value)
|
||||
for x in range(0, int(srcs_rxs_pml_datarange[1]) + 1):
|
||||
for y in range(len(srcs_rxs_pml)):
|
||||
if srcs_rxs_pml[y][0] == x:
|
||||
# Create threshold for sources/pml (name and numeric value)
|
||||
for x in range(1, int(srcs_pml_datarange[1]) + 1):
|
||||
for y in range(len(srcs_pml)):
|
||||
if srcs_pml[y][0] == x:
|
||||
threshold = Threshold(Input=model)
|
||||
threshold.Scalars = 'Sources_Receivers_PML'
|
||||
threshold.ThresholdRange = [srcs_rxs_pml[y][0], srcs_rxs_pml[y][0]]
|
||||
threshold.Scalars = 'Sources_PML'
|
||||
threshold.ThresholdRange = [srcs_pml[y][0], srcs_pml[y][0]]
|
||||
|
||||
RenameSource(srcs_rxs_pml[y][1], threshold)
|
||||
RenameSource(srcs_pml[y][1], threshold)
|
||||
|
||||
# Show data in view
|
||||
thresholddisplay = Show(threshold, renderview)
|
||||
thresholddisplay.ColorArrayName = 'Sources_Receivers_PML'
|
||||
thresholddisplay.ColorArrayName = 'Sources_PML'
|
||||
|
||||
if srcs_rxs_pml[y][0] == 1:
|
||||
if srcs_pml[y][0] == 1:
|
||||
thresholddisplay.Opacity = 0.5
|
||||
|
||||
if rxs:
|
||||
# Get ranges of data
|
||||
rxs_datarange = model.CellData.GetArray('Receivers').GetRange()
|
||||
|
||||
# Create threshold for sources/pml (name and numeric value)
|
||||
for x in range(1, int(rxs_datarange[1]) + 1):
|
||||
for y in range(len(rxs)):
|
||||
if rxs[y][0] == x:
|
||||
threshold = Threshold(Input=model)
|
||||
threshold.Scalars = 'Receivers'
|
||||
threshold.ThresholdRange = [rxs[y][0], rxs[y][0]]
|
||||
|
||||
RenameSource(rxs[y][1], threshold)
|
||||
|
||||
# Show data in view
|
||||
thresholddisplay = Show(threshold, renderview)
|
||||
thresholddisplay.ColorArrayName = 'Receivers'
|
||||
|
||||
#renderview.CameraParallelProjection = 1
|
||||
RenderAllViews()
|
||||
# Show color bar/color legend
|
||||
|
@@ -25,188 +25,201 @@ import matplotlib.gridspec as gridspec
|
||||
from gprMax.exceptions import CmdInputError
|
||||
from gprMax.receivers import Rx
|
||||
|
||||
"""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."""
|
||||
|
||||
# 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 tools.plot_Ascan outputfile')
|
||||
parser.add_argument('outputfile', help='name of output file including path')
|
||||
parser.add_argument('--outputs', help='outputs to be plotted (Ex, Ey, Ez, Hx, Hy, Hz, Ix, Iy, Iz)', default=Rx.availableoutputs, nargs='+')
|
||||
parser.add_argument('-fft', action='store_true', default=False, help='plot FFT (single output must be specified)')
|
||||
args = parser.parse_args()
|
||||
def make_plot(filename, outputs=Rx.availableoutputs, fft=False):
|
||||
"""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.
|
||||
|
||||
# Open output file and read some attributes
|
||||
f = h5py.File(args.outputfile, 'r')
|
||||
nrx = f.attrs['nrx']
|
||||
dt = f.attrs['dt']
|
||||
iterations = f.attrs['Iterations']
|
||||
time = np.linspace(0, 1, iterations)
|
||||
time *= (iterations * dt)
|
||||
Args:
|
||||
filename (string): Filename (including path) of output file.
|
||||
outputs (list): List of field/current components to plot.
|
||||
fft (boolean): Plot FFT switch.
|
||||
"""
|
||||
|
||||
# Check there are any receivers
|
||||
if nrx == 0:
|
||||
raise CmdInputError('No receivers found in {}'.format(args.outputfile))
|
||||
# Open output file and read some attributes
|
||||
f = h5py.File(filename, 'r')
|
||||
nrx = f.attrs['nrx']
|
||||
dt = f.attrs['dt']
|
||||
iterations = f.attrs['Iterations']
|
||||
time = np.linspace(0, 1, iterations)
|
||||
time *= (iterations * dt)
|
||||
|
||||
# Check for single output component when doing a FFT
|
||||
if args.fft:
|
||||
if not len(args.outputs) == 1:
|
||||
raise CmdInputError('A single output must be specified when using the -fft option')
|
||||
# Check there are any receivers
|
||||
if nrx == 0:
|
||||
raise CmdInputError('No receivers found in {}'.format(filename))
|
||||
|
||||
# New plot for each receiver
|
||||
for rx in range(1, nrx + 1):
|
||||
path = '/rxs/rx' + str(rx) + '/'
|
||||
availableoutputs = list(f[path].keys())
|
||||
# Check for single output component when doing a FFT
|
||||
if fft:
|
||||
if not len(outputs) == 1:
|
||||
raise CmdInputError('A single output must be specified when using the -fft option')
|
||||
|
||||
# If only a single output is required, create one subplot
|
||||
if len(args.outputs) == 1:
|
||||
# New plot for each receiver
|
||||
for rx in range(1, nrx + 1):
|
||||
path = '/rxs/rx' + str(rx) + '/'
|
||||
availableoutputs = list(f[path].keys())
|
||||
|
||||
# Check for polarity of output and if requested output is in file
|
||||
if args.outputs[0][0] == 'm':
|
||||
polarity = -1
|
||||
outputtext = '-' + args.outputs[0][1:]
|
||||
output = args.outputs[0][1:]
|
||||
else:
|
||||
polarity = 1
|
||||
outputtext = args.outputs[0]
|
||||
output = args.outputs[0]
|
||||
|
||||
if output not in availableoutputs:
|
||||
raise CmdInputError('{} output requested to plot, but the available output for receiver 1 is {}'.format(output, ', '.join(availableoutputs)))
|
||||
|
||||
outputdata = f[path + output][:] * polarity
|
||||
|
||||
# Plotting if FFT required
|
||||
if args.fft:
|
||||
# Calculate magnitude of frequency spectra of waveform
|
||||
power = 10 * np.log10(np.abs(np.fft.fft(outputdata))**2)
|
||||
freqs = np.fft.fftfreq(power.size, d=dt)
|
||||
|
||||
# Shift powers so that frequency with maximum power is at zero decibels
|
||||
power -= np.amax(power)
|
||||
|
||||
# Set plotting range to -60dB from maximum power
|
||||
pltrange = np.where((np.amax(power[1::]) - power[1::]) > 60)[0][0] + 1
|
||||
# To a maximum frequency
|
||||
#pltrange = np.where(freqs > 2e9)[0][0]
|
||||
pltrange = np.s_[0:pltrange]
|
||||
|
||||
# Plot time history of output component
|
||||
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, num='rx' + str(rx), 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()
|
||||
|
||||
# Plot frequency spectra
|
||||
markerline, stemlines, baseline = ax2.stem(freqs[pltrange], power[pltrange], '-.')
|
||||
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()
|
||||
|
||||
# Change colours and labels for magnetic field components or currents
|
||||
if 'H' in args.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 args.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='rx' + str(rx), 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()
|
||||
|
||||
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 populate only the specified ones
|
||||
else:
|
||||
fig, ax = plt.subplots(subplot_kw=dict(xlabel='Time [s]'), num='rx' + str(rx), figsize=(20, 10), facecolor='w', edgecolor='w')
|
||||
gs = gridspec.GridSpec(3, 3, hspace=0.3, wspace=0.3)
|
||||
for output in args.outputs:
|
||||
# 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 output[0] == 'm':
|
||||
if outputs[0][-1] == '-':
|
||||
polarity = -1
|
||||
outputtext = '-' + output[1:]
|
||||
output = output[1:]
|
||||
outputtext = '-' + outputs[0][0:-1]
|
||||
output = outputs[0][0:-1]
|
||||
else:
|
||||
polarity = 1
|
||||
outputtext = output
|
||||
outputtext = outputs[0]
|
||||
output = outputs[0]
|
||||
|
||||
# Check if requested output is in file
|
||||
if output not in availableoutputs:
|
||||
raise CmdInputError('Output(s) requested to plot: {}, but available output(s) for receiver {} in the file: {}'.format(', '.join(args.outputs), rx, ', '.join(availableoutputs)))
|
||||
raise CmdInputError('{} output requested to plot, but the available output for receiver 1 is {}'.format(output, ', '.join(availableoutputs)))
|
||||
|
||||
outputdata = f[path + output][:] * polarity
|
||||
|
||||
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.set_ylim([-15, 20])
|
||||
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.set_ylim([-15, 20])
|
||||
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.set_ylim([-15, 20])
|
||||
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.set_ylim([-0.03, 0.03])
|
||||
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.set_ylim([-0.03, 0.03])
|
||||
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.set_ylim([-0.03, 0.03])
|
||||
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 = plt.subplot(gs[1, 2])
|
||||
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]')
|
||||
for ax in fig.axes:
|
||||
ax.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
# Plotting if FFT required
|
||||
if fft:
|
||||
# Calculate magnitude of frequency spectra of waveform
|
||||
power = 10 * np.log10(np.abs(np.fft.fft(outputdata))**2)
|
||||
freqs = np.fft.fftfreq(power.size, d=dt)
|
||||
|
||||
# Save a PDF/PNG of the figure
|
||||
#fig.savefig(os.path.splitext(os.path.abspath(file))[0] + '_rx' + str(rx) + '.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig.savefig(os.path.splitext(os.path.abspath(file))[0] + '_rx' + str(rx) + '.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
# Shift powers so that frequency with maximum power is at zero decibels
|
||||
power -= np.amax(power)
|
||||
|
||||
plt.show()
|
||||
# Set plotting range to -60dB from maximum power
|
||||
pltrange = np.where((np.amax(power[1::]) - power[1::]) > 60)[0][0] + 1
|
||||
# To a maximum frequency
|
||||
#pltrange = np.where(freqs > 2e9)[0][0]
|
||||
pltrange = np.s_[0:pltrange]
|
||||
|
||||
# Plot time history of output component
|
||||
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, num='rx' + str(rx), 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()
|
||||
|
||||
# Plot frequency spectra
|
||||
markerline, stemlines, baseline = ax2.stem(freqs[pltrange], power[pltrange], '-.')
|
||||
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()
|
||||
|
||||
# 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')
|
||||
|
||||
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='rx' + str(rx), 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()
|
||||
|
||||
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 populate only the specified ones
|
||||
else:
|
||||
fig, ax = plt.subplots(subplot_kw=dict(xlabel='Time [s]'), num='rx' + str(rx), figsize=(20, 10), facecolor='w', edgecolor='w')
|
||||
gs = gridspec.GridSpec(3, 3, hspace=0.3, wspace=0.3)
|
||||
for output in outputs:
|
||||
|
||||
# Check for polarity of output and if requested output is in file
|
||||
if output[-1] == 'm':
|
||||
polarity = -1
|
||||
outputtext = '-' + output[0:-1]
|
||||
output = output[0:-1]
|
||||
else:
|
||||
polarity = 1
|
||||
outputtext = output
|
||||
|
||||
# Check if requested output is in file
|
||||
if output not in availableoutputs:
|
||||
raise CmdInputError('Output(s) requested to plot: {}, but available output(s) for receiver {} in the file: {}'.format(', '.join(outputs), rx, ', '.join(availableoutputs)))
|
||||
|
||||
outputdata = f[path + output][:] * polarity
|
||||
|
||||
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.set_ylim([-15, 20])
|
||||
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.set_ylim([-15, 20])
|
||||
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.set_ylim([-15, 20])
|
||||
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.set_ylim([-0.03, 0.03])
|
||||
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.set_ylim([-0.03, 0.03])
|
||||
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.set_ylim([-0.03, 0.03])
|
||||
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 = plt.subplot(gs[1, 2])
|
||||
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]')
|
||||
for ax in fig.axes:
|
||||
ax.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
|
||||
# Save a PDF/PNG of the figure
|
||||
#fig.savefig(os.path.splitext(os.path.abspath(file))[0] + '_rx' + str(rx) + '.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig.savefig(os.path.splitext(os.path.abspath(file))[0] + '_rx' + str(rx) + '.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
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 tools.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.availableoutputs, 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', help='plot FFT (single output must be specified)', default=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
make_plot(args.outputfile, args.outputs, fft=args.fft)
|
||||
|
@@ -23,52 +23,64 @@ import matplotlib.pyplot as plt
|
||||
|
||||
from gprMax.exceptions import CmdInputError
|
||||
|
||||
"""Plots a B-scan image."""
|
||||
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(description='Plots a B-scan image.', usage='cd gprMax; python -m tools.plot_Bscan outputfile output')
|
||||
parser.add_argument('outputfile', help='name of output file including path')
|
||||
parser.add_argument('output', help='name of output component to be plotted (Ex, Ey, Ez, Hx, Hy, Hz, Ix, Iy or Iz)')
|
||||
args = parser.parse_args()
|
||||
def make_plot(filename, output):
|
||||
"""Plots a B-scan image.
|
||||
|
||||
# Open output file and read some attributes
|
||||
f = h5py.File(args.outputfile, 'r')
|
||||
nrx = f.attrs['nrx']
|
||||
Args:
|
||||
filename (string): Filename (including path) of output file.
|
||||
output (string): Field/current component to plot.
|
||||
"""
|
||||
|
||||
# Check there are any receivers
|
||||
if nrx == 0:
|
||||
raise CmdInputError('No receivers found in {}'.format(args.outputfile))
|
||||
# Open output file and read some attributes
|
||||
f = h5py.File(filename, 'r')
|
||||
nrx = f.attrs['nrx']
|
||||
|
||||
for rx in range(1, nrx + 1):
|
||||
path = '/rxs/rx' + str(rx) + '/'
|
||||
availableoutputs = list(f[path].keys())
|
||||
# Check there are any receivers
|
||||
if nrx == 0:
|
||||
raise CmdInputError('No receivers found in {}'.format(filename))
|
||||
|
||||
# Check if requested output is in file
|
||||
if args.output not in availableoutputs:
|
||||
raise CmdInputError('{} output requested to plot, but the available output for receiver 1 is {}'.format(args.output, ', '.join(availableoutputs)))
|
||||
for rx in range(1, nrx + 1):
|
||||
path = '/rxs/rx' + str(rx) + '/'
|
||||
availableoutputs = list(f[path].keys())
|
||||
|
||||
outputdata = f[path + '/' + args.output]
|
||||
# Check if requested output is in file
|
||||
if output not in availableoutputs:
|
||||
raise CmdInputError('{} output requested to plot, but the available output for receiver 1 is {}'.format(output, ', '.join(availableoutputs)))
|
||||
|
||||
# Check that there is more than one A-scan present
|
||||
if outputdata.shape[1] == 1:
|
||||
raise CmdInputError('{} contains only a single A-scan.'.format(args.outputfile))
|
||||
outputdata = f[path + '/' + output]
|
||||
|
||||
# Plot B-scan image
|
||||
fig = plt.figure(num='rx' + str(rx), figsize=(20, 10), facecolor='w', edgecolor='w')
|
||||
plt.imshow(outputdata, extent=[0, outputdata.shape[1], outputdata.shape[0]*f.attrs['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]')
|
||||
plt.grid()
|
||||
cb = plt.colorbar()
|
||||
if 'E' in args.output:
|
||||
cb.set_label('Field strength [V/m]')
|
||||
elif 'H' in args.output:
|
||||
cb.set_label('Field strength [A/m]')
|
||||
elif 'I' in args.output:
|
||||
cb.set_label('Current [A]')
|
||||
# Check that there is more than one A-scan present
|
||||
if outputdata.shape[1] == 1:
|
||||
raise CmdInputError('{} contains only a single A-scan.'.format(filename))
|
||||
|
||||
# Save a PDF/PNG of the figure
|
||||
#fig.savefig(os.path.splitext(os.path.abspath(args.outputfile))[0] + '.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig.savefig(os.path.splitext(os.path.abspath(args.outputfile))[0] + '.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
# Plot B-scan image
|
||||
fig = plt.figure(num='rx' + str(rx), figsize=(20, 10), facecolor='w', edgecolor='w')
|
||||
plt.imshow(outputdata, extent=[0, outputdata.shape[1], outputdata.shape[0]*f.attrs['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]')
|
||||
plt.grid()
|
||||
cb = plt.colorbar()
|
||||
if 'E' in args.output:
|
||||
cb.set_label('Field strength [V/m]')
|
||||
elif 'H' in args.output:
|
||||
cb.set_label('Field strength [A/m]')
|
||||
elif 'I' in args.output:
|
||||
cb.set_label('Current [A]')
|
||||
|
||||
plt.show()
|
||||
# Save a PDF/PNG of the figure
|
||||
#fig.savefig(os.path.splitext(os.path.abspath(args.outputfile))[0] + '.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig.savefig(os.path.splitext(os.path.abspath(args.outputfile))[0] + '.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(description='Plots a B-scan image.', usage='cd gprMax; python -m tools.plot_Bscan outputfile output')
|
||||
parser.add_argument('outputfile', help='name of output file including path')
|
||||
parser.add_argument('output', help='name of output component to be plotted', choices=['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz', 'Ix', 'Iy', 'Iz'])
|
||||
args = parser.parse_args()
|
||||
|
||||
make_plot(args.outputfile, args.output)
|
@@ -16,335 +16,357 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os, argparse
|
||||
import argparse, os
|
||||
import h5py
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.gridspec as gridspec
|
||||
#import scipy.io as sio
|
||||
|
||||
moduledirectory = os.path.dirname(os.path.abspath(__file__))
|
||||
from gprMax.exceptions import CmdInputError
|
||||
|
||||
"""Plots antenna parameters (s11 parameter and input impedance and admittance) from an output file containing a transmission line source."""
|
||||
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(description='Plots antenna parameters (s11 parameter and input impedance and admittance) from an output file containing a transmission line source.', usage='cd gprMax; python -m tools.plot_antenna_params outputfile')
|
||||
parser.add_argument('outputfile', help='name of output file including path')
|
||||
parser.add_argument('-tln', default=1, type=int, help='transmission line number')
|
||||
args = parser.parse_args()
|
||||
def plot_antenna_params(filename, tln=1, rxn=None, rx=None):
|
||||
"""Calculates and plots antenna parameters - s11, (s21) and input impedance.
|
||||
|
||||
print("Antenna parameter analysis from file '{}'...".format(args.outputfile))
|
||||
Args:
|
||||
filename (string): Filename (including path) of output file.
|
||||
tln (int): Transmitting antenna - transmission line number
|
||||
rxn (int): Receiver antenna - output number
|
||||
rx (str): Receiver antenna - output electric field component
|
||||
"""
|
||||
|
||||
# Open output file and read some attributes
|
||||
file = args.outputfile
|
||||
f = h5py.File(file, 'r')
|
||||
dt = f.attrs['dt']
|
||||
iterations = f.attrs['Iterations']
|
||||
# Open output file and read some attributes
|
||||
f = h5py.File(filename, 'r')
|
||||
dxdydz = f.attrs['dx, dy, dz']
|
||||
dt = f.attrs['dt']
|
||||
iterations = f.attrs['Iterations']
|
||||
|
||||
# Choose a specific frequency bin spacing
|
||||
#df = 1.5e6
|
||||
#iterations = int((1 / df) / dt)
|
||||
# Calculate time array and frequency bin spacing
|
||||
time = np.linspace(0, 1, iterations)
|
||||
time *= (iterations * dt)
|
||||
df = 1 / np.amax(time)
|
||||
|
||||
# Calculate time array and frequency bin spacing
|
||||
time = np.linspace(0, 1, iterations)
|
||||
time *= (iterations * dt)
|
||||
df = 1 / np.amax(time)
|
||||
print('Time window: {:g} s ({} iterations)'.format(np.amax(time), iterations))
|
||||
print('Time step: {:g} s'.format(dt))
|
||||
print('Frequency bin spacing: {:g} Hz'.format(df))
|
||||
|
||||
print('Time window: {:g} s ({} iterations)'.format(np.amax(time), iterations))
|
||||
print('Time step: {:g} s'.format(dt))
|
||||
print('Frequency bin spacing: {:g} Hz'.format(df))
|
||||
# Read/calculate voltages and currents
|
||||
tlpath = '/tls/tl' + str(tln) + '/'
|
||||
|
||||
# Read/calculate voltages and currents
|
||||
path = '/tls/tl' + str(args.tln) + '/'
|
||||
Vinc = f[path + 'Vinc'][:]
|
||||
Iinc = f[path + 'Iinc'][:]
|
||||
Vtotal = f[path +'Vtotal'][:]
|
||||
Itotal = f[path +'Itotal'][:]
|
||||
f.close()
|
||||
Vref = Vtotal - Vinc
|
||||
Iref = Itotal - Iinc
|
||||
# Incident voltages/currents
|
||||
Vinc = f[tlpath + 'Vinc'][:]
|
||||
Iinc = f[tlpath + 'Iinc'][:]
|
||||
|
||||
# Frequency bins
|
||||
freqs = np.fft.fftfreq(Vinc.size, d=dt)
|
||||
# Total (incident + reflected) voltages/currents
|
||||
Vtotal = f[tlpath +'Vtotal'][:]
|
||||
Itotal = f[tlpath +'Itotal'][:]
|
||||
|
||||
# Delay correction - current lags voltage, so delay voltage to match current timestep
|
||||
delaycorrection = np.exp(-1j * 2 * np.pi * freqs * (dt / 2))
|
||||
# Reflected voltages/currents
|
||||
Vref = Vtotal - Vinc
|
||||
Iref = Itotal - Iinc
|
||||
|
||||
# Calculate s11
|
||||
s11 = np.abs(np.fft.fft(Vref) * delaycorrection) / np.abs(np.fft.fft(Vinc) * delaycorrection)
|
||||
# If a receiver number for a receiever antenna is given can get received voltage for s21
|
||||
if rxn:
|
||||
if rx not in ['Ex', 'Ey', 'Ez']:
|
||||
raise CmdInputError('The field component for the receiver antenna output must be Ex, Ey, or Ez')
|
||||
|
||||
# Calculate input impedance
|
||||
zin = (np.fft.fft(Vtotal) * delaycorrection) / np.fft.fft(Itotal)
|
||||
rxpath = '/rxs/rx' + str(rxn) + '/'
|
||||
availableoutputs = list(f[rxpath].keys())
|
||||
|
||||
# Load MoM zin from MATLAB antenna toolbox
|
||||
#MoM = {}
|
||||
#sio.loadmat(moduledirectory + '/../tests/numerical/vs_MoM_MATLAB/antenna_bowtie_fs/antenna_bowtie_fs_MoM.mat', MoM)
|
||||
if rx not in availableoutputs:
|
||||
raise CmdInputError('{} output requested, but the available output for receiver {} is {}'.format(rx, rxn, ', '.join(availableoutputs)))
|
||||
|
||||
# Calculate input admittance
|
||||
yin = np.fft.fft(Itotal) / (np.fft.fft(Vtotal) * delaycorrection)
|
||||
rxpath += rx
|
||||
|
||||
# Convert to decibels
|
||||
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)))
|
||||
Irefp = 20 * np.log10(np.abs(np.fft.fft(Iref)))
|
||||
Vtotalp = 20 * np.log10(np.abs((np.fft.fft(Vtotal) * delaycorrection)))
|
||||
Itotalp = 20 * np.log10(np.abs(np.fft.fft(Itotal)))
|
||||
s11 = 20 * np.log10(s11)
|
||||
# Received voltage
|
||||
if rx == 'Ex':
|
||||
Vrec = f[rxpath][:] * -1 * dxdydz[0]
|
||||
elif rx == 'Ey':
|
||||
Vrec = f[rxpath][:] * -1 * dxdydz[1]
|
||||
elif rx == 'Ez':
|
||||
Vrec = f[rxpath][:] * -1 * dxdydz[2]
|
||||
f.close()
|
||||
|
||||
# Set plotting range
|
||||
pltrangemin = 1
|
||||
# To a certain drop from maximum power
|
||||
pltrangemax = np.where((np.amax(Vincp[1::]) - Vincp[1::]) > 60)[0][0] + 1
|
||||
# To a maximum frequency
|
||||
#pltrangemax = np.where(freqs > 6e9)[0][0]
|
||||
pltrange = np.s_[pltrangemin:pltrangemax]
|
||||
# Frequency bins
|
||||
freqs = np.fft.fftfreq(Vinc.size, d=dt)
|
||||
|
||||
# Print some useful values from s11, input impedance and admittance
|
||||
s11minfreq = np.where(s11[pltrange] == np.amin(s11[pltrange]))[0][0]
|
||||
print('s11 minimum: {:g} dB at {:g} Hz'.format(np.amin(s11[pltrange]), freqs[s11minfreq + pltrangemin]))
|
||||
print('At {:g} Hz...'.format(freqs[s11minfreq + pltrangemin]))
|
||||
print('Input impedance: {:.1f}{:+.1f}j Ohms'.format(np.abs(zin[s11minfreq + pltrangemin]), zin[s11minfreq + pltrangemin].imag))
|
||||
print('Input admittance (mag): {:g} S'.format(np.abs(yin[s11minfreq + pltrangemin])))
|
||||
print('Input admittance (phase): {:.1f} deg'.format(np.angle(yin[s11minfreq + pltrangemin], deg=True)))
|
||||
# Delay correction - current lags voltage, so delay voltage to match current timestep
|
||||
delaycorrection = np.exp(-1j * 2 * np.pi * freqs * (dt / 2))
|
||||
|
||||
# Figure 1
|
||||
# Plot incident voltage
|
||||
fig1, ax = plt.subplots(num='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.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
# Calculate s11
|
||||
s11 = np.abs(np.fft.fft(Vref) * delaycorrection) / np.abs(np.fft.fft(Vinc) * delaycorrection)
|
||||
if rxn:
|
||||
s21 = np.abs(np.fft.fft(Vrec)) / np.abs(np.fft.fft(Vinc) * delaycorrection)
|
||||
|
||||
# Plot frequency spectra of incident voltage
|
||||
ax = plt.subplot(gs1[0, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vincp[pltrange], '-.')
|
||||
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()
|
||||
# Calculate input impedance
|
||||
zin = (np.fft.fft(Vtotal) * delaycorrection) / np.fft.fft(Itotal)
|
||||
|
||||
# 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.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
# Calculate input admittance
|
||||
yin = np.fft.fft(Itotal) / (np.fft.fft(Vtotal) * delaycorrection)
|
||||
|
||||
# Plot frequency spectra of incident current
|
||||
ax = plt.subplot(gs1[1, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Iincp[pltrange], '-.')
|
||||
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()
|
||||
# Convert to decibels
|
||||
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)))
|
||||
Irefp = 20 * np.log10(np.abs(np.fft.fft(Iref)))
|
||||
Vtotalp = 20 * np.log10(np.abs((np.fft.fft(Vtotal) * delaycorrection)))
|
||||
Itotalp = 20 * np.log10(np.abs(np.fft.fft(Itotal)))
|
||||
s11 = 20 * np.log10(s11)
|
||||
if rxn:
|
||||
s21 = 20 * np.log10(s21)
|
||||
|
||||
# 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.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
# Set plotting range
|
||||
pltrangemin = 1
|
||||
# To a certain drop from maximum power
|
||||
pltrangemax = np.where((np.amax(Vincp[1::]) - Vincp[1::]) > 60)[0][0] + 1
|
||||
# To a maximum frequency
|
||||
#pltrangemax = np.where(freqs > 6e9)[0][0]
|
||||
pltrange = np.s_[pltrangemin:pltrangemax]
|
||||
|
||||
# Plot frequency spectra of total voltage
|
||||
ax = plt.subplot(gs1[2, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vtotalp[pltrange], '-.')
|
||||
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()
|
||||
# Print some useful values from s11, and input impedance
|
||||
s11minfreq = np.where(s11[pltrange] == np.amin(s11[pltrange]))[0][0]
|
||||
print('s11 minimum: {:g} dB at {:g} Hz'.format(np.amin(s11[pltrange]), freqs[s11minfreq + pltrangemin]))
|
||||
print('At {:g} Hz...'.format(freqs[s11minfreq + pltrangemin]))
|
||||
print('Input impedance: {:.1f}{:+.1f}j Ohms'.format(np.abs(zin[s11minfreq + pltrangemin]), zin[s11minfreq + pltrangemin].imag))
|
||||
#print('Input admittance (mag): {:g} S'.format(np.abs(yin[s11minfreq + pltrangemin])))
|
||||
#print('Input admittance (phase): {:.1f} deg'.format(np.angle(yin[s11minfreq + pltrangemin], deg=True)))
|
||||
|
||||
# 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.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
# Figure 1
|
||||
# Plot incident voltage
|
||||
fig1, ax = plt.subplots(num='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.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
|
||||
# Plot frequency spectra of reflected current
|
||||
ax = plt.subplot(gs1[3, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Itotalp[pltrange], '-.')
|
||||
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()
|
||||
# Plot frequency spectra of incident voltage
|
||||
ax = plt.subplot(gs1[0, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vincp[pltrange], '-.')
|
||||
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()
|
||||
|
||||
## Plot reflected (reflected) voltage
|
||||
#ax = plt.subplot(gs1[4, 0])
|
||||
#ax.plot(time, Vref, 'r', lw=2, label='Vref')
|
||||
#ax.set_title('Reflected voltage')
|
||||
#ax.set_xlabel('Time [s]')
|
||||
#ax.set_ylabel('Voltage [V]')
|
||||
#ax.set_xlim([0, np.amax(time)])
|
||||
#ax.grid()
|
||||
#
|
||||
## Plot frequency spectra of reflected voltage
|
||||
#ax = plt.subplot(gs1[4, 1])
|
||||
#markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vrefp[pltrange], '-.')
|
||||
#plt.setp(baseline, 'linewidth', 0)
|
||||
#plt.setp(stemlines, 'color', 'r')
|
||||
#plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r')
|
||||
#ax.plot(freqs[pltrange], Vrefp[pltrange], 'r', lw=2)
|
||||
#ax.set_title('Reflected voltage')
|
||||
#ax.set_xlabel('Frequency [Hz]')
|
||||
#ax.set_ylabel('Power [dB]')
|
||||
#ax.grid()
|
||||
#
|
||||
## Plot reflected (reflected) current
|
||||
#ax = plt.subplot(gs1[5, 0])
|
||||
#ax.plot(time, Iref, 'b', lw=2, label='Iref')
|
||||
#ax.set_title('Reflected current')
|
||||
#ax.set_xlabel('Time [s]')
|
||||
#ax.set_ylabel('Current [A]')
|
||||
#ax.set_xlim([0, np.amax(time)])
|
||||
#ax.grid()
|
||||
#
|
||||
## Plot frequency spectra of reflected current
|
||||
#ax = plt.subplot(gs1[5, 1])
|
||||
#markerline, stemlines, baseline = ax.stem(freqs[pltrange], Irefp[pltrange], '-.')
|
||||
#plt.setp(baseline, 'linewidth', 0)
|
||||
#plt.setp(stemlines, 'color', 'b')
|
||||
#plt.setp(markerline, 'markerfacecolor', 'b', 'markeredgecolor', 'b')
|
||||
#ax.plot(freqs[pltrange], Irefp[pltrange], 'b', lw=2)
|
||||
#ax.set_title('Reflected current')
|
||||
#ax.set_xlabel('Frequency [Hz]')
|
||||
#ax.set_ylabel('Power [dB]')
|
||||
#ax.grid()
|
||||
# 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.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
|
||||
# Figure 2
|
||||
# Plot frequency spectra of s11
|
||||
fig2, ax = plt.subplots(num='Antenna parameters', figsize=(20, 12), facecolor='w', edgecolor='w')
|
||||
gs2 = gridspec.GridSpec(3, 2, hspace=0.5)
|
||||
ax = plt.subplot(gs2[0, 0])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], s11[pltrange], '-.')
|
||||
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.88e9, 1.02e9])
|
||||
#ax.set_ylim([-20, 0])
|
||||
ax.grid()
|
||||
# Plot frequency spectra of incident current
|
||||
ax = plt.subplot(gs1[1, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Iincp[pltrange], '-.')
|
||||
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()
|
||||
|
||||
# Plot input resistance (real part of impedance)
|
||||
ax = plt.subplot(gs2[1, 0])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].real, '-.')
|
||||
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, 350])
|
||||
ax.grid()
|
||||
# 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.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
|
||||
# Plot input reactance (imaginery part of impedance)
|
||||
ax = plt.subplot(gs2[1, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].imag, '-.')
|
||||
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([-1400, 200])
|
||||
ax.grid()
|
||||
# Plot frequency spectra of total voltage
|
||||
ax = plt.subplot(gs1[2, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vtotalp[pltrange], '-.')
|
||||
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()
|
||||
|
||||
# Plot input admittance (magnitude)
|
||||
ax = plt.subplot(gs2[2, 0])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], np.abs(yin[pltrange]), '-.')
|
||||
plt.setp(baseline, 'linewidth', 0)
|
||||
plt.setp(stemlines, 'color', 'g')
|
||||
plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g')
|
||||
ax.plot(freqs[pltrange], np.abs(yin[pltrange]), 'g', lw=2)
|
||||
ax.set_title('Input admittance (magnitude)')
|
||||
ax.set_xlabel('Frequency [Hz]')
|
||||
ax.set_ylabel('Admittance [Siemens]')
|
||||
#ax.set_xlim([0.88e9, 1.02e9])
|
||||
#ax.set_ylim([0, 0.035])
|
||||
ax.grid()
|
||||
# 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.set_xlim([0, np.amax(time)])
|
||||
ax.grid()
|
||||
|
||||
# Plot input admittance (phase)
|
||||
ax = plt.subplot(gs2[2, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], np.angle(yin[pltrange], deg=True), '-.')
|
||||
plt.setp(baseline, 'linewidth', 0)
|
||||
plt.setp(stemlines, 'color', 'g')
|
||||
plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g')
|
||||
ax.plot(freqs[pltrange], np.angle(yin[pltrange], deg=True), 'g', lw=2)
|
||||
ax.set_title('Input admittance (phase)')
|
||||
ax.set_xlabel('Frequency [Hz]')
|
||||
ax.set_ylabel('Phase [degrees]')
|
||||
#ax.set_xlim([0.88e9, 1.02e9])
|
||||
#ax.set_ylim([-40, 100])
|
||||
ax.grid()
|
||||
# Plot frequency spectra of reflected current
|
||||
ax = plt.subplot(gs1[3, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], Itotalp[pltrange], '-.')
|
||||
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()
|
||||
|
||||
# Figure 3 - Comparison of numerical modelling techniques
|
||||
#fig3, ax = plt.subplots(num='FDTD vs MoM', figsize=(20, 5), facecolor='w', edgecolor='w')
|
||||
#gs3 = gridspec.GridSpec(1, 2, hspace=0.5)
|
||||
#
|
||||
## Plot input resistance (real part of impedance)
|
||||
#ax = plt.subplot(gs3[0, 0])
|
||||
#ax.plot(freqs[pltrange], zin[pltrange].real, 'g', lw=2, label='FDTD')
|
||||
#ax.plot(MoM['freqs'], MoM['zin'].real, 'r', lw=2, ls='--', label='MoM')
|
||||
#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, 350])
|
||||
#ax.grid()
|
||||
#ax.legend()
|
||||
#
|
||||
## Plot input reactance (imaginery part of impedance)
|
||||
#ax = plt.subplot(gs3[0, 1])
|
||||
#ax.plot(freqs[pltrange], zin[pltrange].imag, 'g', lw=2, label='FDTD')
|
||||
#ax.plot(MoM['freqs'], -MoM['zin'].imag, 'r', lw=2, ls='--', label='MoM')
|
||||
#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([-350, 350])
|
||||
#ax.grid()
|
||||
#ax.legend()
|
||||
## Plot reflected (reflected) voltage
|
||||
#ax = plt.subplot(gs1[4, 0])
|
||||
#ax.plot(time, Vref, 'r', lw=2, label='Vref')
|
||||
#ax.set_title('Reflected voltage')
|
||||
#ax.set_xlabel('Time [s]')
|
||||
#ax.set_ylabel('Voltage [V]')
|
||||
#ax.set_xlim([0, np.amax(time)])
|
||||
#ax.grid()
|
||||
#
|
||||
## Plot frequency spectra of reflected voltage
|
||||
#ax = plt.subplot(gs1[4, 1])
|
||||
#markerline, stemlines, baseline = ax.stem(freqs[pltrange], Vrefp[pltrange], '-.')
|
||||
#plt.setp(baseline, 'linewidth', 0)
|
||||
#plt.setp(stemlines, 'color', 'r')
|
||||
#plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r')
|
||||
#ax.plot(freqs[pltrange], Vrefp[pltrange], 'r', lw=2)
|
||||
#ax.set_title('Reflected voltage')
|
||||
#ax.set_xlabel('Frequency [Hz]')
|
||||
#ax.set_ylabel('Power [dB]')
|
||||
#ax.grid()
|
||||
#
|
||||
## Plot reflected (reflected) current
|
||||
#ax = plt.subplot(gs1[5, 0])
|
||||
#ax.plot(time, Iref, 'b', lw=2, label='Iref')
|
||||
#ax.set_title('Reflected current')
|
||||
#ax.set_xlabel('Time [s]')
|
||||
#ax.set_ylabel('Current [A]')
|
||||
#ax.set_xlim([0, np.amax(time)])
|
||||
#ax.grid()
|
||||
#
|
||||
## Plot frequency spectra of reflected current
|
||||
#ax = plt.subplot(gs1[5, 1])
|
||||
#markerline, stemlines, baseline = ax.stem(freqs[pltrange], Irefp[pltrange], '-.')
|
||||
#plt.setp(baseline, 'linewidth', 0)
|
||||
#plt.setp(stemlines, 'color', 'b')
|
||||
#plt.setp(markerline, 'markerfacecolor', 'b', 'markeredgecolor', 'b')
|
||||
#ax.plot(freqs[pltrange], Irefp[pltrange], 'b', lw=2)
|
||||
#ax.set_title('Reflected current')
|
||||
#ax.set_xlabel('Frequency [Hz]')
|
||||
#ax.set_ylabel('Power [dB]')
|
||||
#ax.grid()
|
||||
|
||||
# Save a PDF/PNG of the figure
|
||||
#fig1.savefig(os.path.splitext(os.path.abspath(file))[0] + '_tl_params.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig2.savefig(os.path.splitext(os.path.abspath(file))[0] + '_ant_params.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig3.savefig(os.path.splitext(os.path.abspath(file))[0] + '_ant_params.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig1.savefig(os.path.splitext(os.path.abspath(file))[0] + '_tl_params.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig2.savefig(os.path.splitext(os.path.abspath(file))[0] + '_ant_params.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
# Figure 2
|
||||
# Plot frequency spectra of s11
|
||||
fig2, ax = plt.subplots(num='Antenna parameters', figsize=(20, 12), facecolor='w', edgecolor='w')
|
||||
gs2 = gridspec.GridSpec(2, 2, hspace=0.5)
|
||||
ax = plt.subplot(gs2[0, 0])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], s11[pltrange], '-.')
|
||||
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.88e9, 1.02e9])
|
||||
#ax.set_ylim([-20, 0])
|
||||
ax.grid()
|
||||
|
||||
# Plot frequency spectra of s21
|
||||
if rxn:
|
||||
ax = plt.subplot(gs2[0, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], s21[pltrange], '-.')
|
||||
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()
|
||||
|
||||
# Plot input resistance (real part of impedance)
|
||||
ax = plt.subplot(gs2[1, 0])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].real, '-.')
|
||||
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()
|
||||
|
||||
# Plot input reactance (imaginery part of impedance)
|
||||
ax = plt.subplot(gs2[1, 1])
|
||||
markerline, stemlines, baseline = ax.stem(freqs[pltrange], zin[pltrange].imag, '-.')
|
||||
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([-200, 100])
|
||||
ax.grid()
|
||||
|
||||
## Plot input admittance (magnitude)
|
||||
#ax = plt.subplot(gs2[2, 0])
|
||||
#markerline, stemlines, baseline = ax.stem(freqs[pltrange], np.abs(yin[pltrange]), '-.')
|
||||
#plt.setp(baseline, 'linewidth', 0)
|
||||
#plt.setp(stemlines, 'color', 'g')
|
||||
#plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g')
|
||||
#ax.plot(freqs[pltrange], np.abs(yin[pltrange]), 'g', lw=2)
|
||||
#ax.set_title('Input admittance (magnitude)')
|
||||
#ax.set_xlabel('Frequency [Hz]')
|
||||
#ax.set_ylabel('Admittance [Siemens]')
|
||||
##ax.set_xlim([0.88e9, 1.02e9])
|
||||
##ax.set_ylim([0, 0.035])
|
||||
#ax.grid()
|
||||
#
|
||||
## Plot input admittance (phase)
|
||||
#ax = plt.subplot(gs2[2, 1])
|
||||
#markerline, stemlines, baseline = ax.stem(freqs[pltrange], np.angle(yin[pltrange], deg=True), '-.')
|
||||
#plt.setp(baseline, 'linewidth', 0)
|
||||
#plt.setp(stemlines, 'color', 'g')
|
||||
#plt.setp(markerline, 'markerfacecolor', 'g', 'markeredgecolor', 'g')
|
||||
#ax.plot(freqs[pltrange], np.angle(yin[pltrange], deg=True), 'g', lw=2)
|
||||
#ax.set_title('Input admittance (phase)')
|
||||
#ax.set_xlabel('Frequency [Hz]')
|
||||
#ax.set_ylabel('Phase [degrees]')
|
||||
##ax.set_xlim([0.88e9, 1.02e9])
|
||||
##ax.set_ylim([-40, 100])
|
||||
#ax.grid()
|
||||
|
||||
# Save a PDF/PNG of the figure
|
||||
#fig1.savefig(os.path.splitext(os.path.abspath(filename))[0] + '_tl_params.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig2.savefig(os.path.splitext(os.path.abspath(filename))[0] + '_ant_params.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig1.savefig(os.path.splitext(os.path.abspath(filename))[0] + '_tl_params.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig2.savefig(os.path.splitext(os.path.abspath(filename))[0] + '_ant_params.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(description='Plots antenna parameters (s11, s21 parameters and input impedance) from an output file containing a transmission line source.', usage='cd gprMax; python -m tools.plot_antenna_params outputfile')
|
||||
parser.add_argument('outputfile', help='name of output file including path')
|
||||
parser.add_argument('-tln', default=1, type=int, help='transmitting antenna - transmission line number')
|
||||
parser.add_argument('-rxn', type=int, help='receiver antenna - output number')
|
||||
parser.add_argument('-rx', type=str, help='receiver antenna - output electric field component')
|
||||
args = parser.parse_args()
|
||||
|
||||
plot_antenna_params(args.outputfile, args.tln, args.rxn, args.rx)
|
||||
|
||||
plt.show()
|
||||
|
@@ -26,108 +26,144 @@ from gprMax.utilities import round_value
|
||||
from gprMax.waveforms import Waveform
|
||||
|
||||
|
||||
"""Plot built-in waveforms that can be used for sources."""
|
||||
def check_timewindow(timewindow, dt):
|
||||
"""Checks and sets time window and number of iterations.
|
||||
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(description='Plot built-in waveforms that can be used for sources.', usage='cd gprMax; python -m tools.plot_builtin_wave type amp freq timewindow dt')
|
||||
parser.add_argument('type', help='type of waveform, e.g. gaussian, ricker etc...')
|
||||
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')
|
||||
args = parser.parse_args()
|
||||
Args:
|
||||
timewindow (float): Time window.
|
||||
dt (float): Time discretisation.
|
||||
|
||||
# Check waveform parameters
|
||||
if args.type.lower() not in Waveform.types:
|
||||
raise CmdInputError('The waveform must have one of the following types {}'.format(', '.join(Waveform.types)))
|
||||
if args.freq <= 0:
|
||||
raise CmdInputError('The waveform requires an excitation frequency value of greater than zero')
|
||||
Returns:
|
||||
timewindow (float): Time window.
|
||||
iterations (int): Number of interations.
|
||||
"""
|
||||
|
||||
w = Waveform()
|
||||
w.type = args.type
|
||||
w.amp = args.amp
|
||||
w.freq = args.freq
|
||||
dt = args.dt
|
||||
# Time window could be a string, float or int, so convert to string then check
|
||||
timewindow = str(timewindow)
|
||||
|
||||
try:
|
||||
timewindow = int(timewindow)
|
||||
iterations = timewindow
|
||||
timewindow = (timewindow - 1) * dt
|
||||
|
||||
except:
|
||||
timewindow = float(timewindow)
|
||||
if timewindow > 0:
|
||||
iterations = round_value((timewindow / dt)) + 1
|
||||
else:
|
||||
raise CmdInputError('Time window must have a value greater than zero')
|
||||
|
||||
return timewindow, iterations
|
||||
|
||||
|
||||
def make_plot(w, timewindow, dt, iterations, fft=False):
|
||||
"""Plots waveform and prints useful information about its properties.
|
||||
|
||||
Args:
|
||||
w (class): Waveform class instance.
|
||||
timewindow (float): Time window.
|
||||
dt (float): Time discretisation.
|
||||
iterations (int): Number of iterations.
|
||||
fft (boolean): Plot FFT switch.
|
||||
"""
|
||||
|
||||
time = np.linspace(0, 1, iterations)
|
||||
time *= (iterations * dt)
|
||||
waveform = np.zeros(len(time))
|
||||
timeiter = np.nditer(time, flags=['c_index'])
|
||||
|
||||
while not timeiter.finished:
|
||||
waveform[timeiter.index] = w.calculate_value(timeiter[0], dt)
|
||||
timeiter.iternext()
|
||||
|
||||
print('Waveform characteristics...')
|
||||
print('Type: {}'.format(w.type))
|
||||
print('Amplitude: {:g}'.format(w.amp))
|
||||
print('Centre frequency: {:g} Hz'.format(w.freq))
|
||||
print('Time to centre of pulse: {:g} s'.format(1 / w.freq))
|
||||
|
||||
# Calculate pulse width for gaussian
|
||||
if w.type == 'gaussian':
|
||||
powerdrop = -3 #dB
|
||||
start = np.where((10 * np.log10(waveform / np.amax(waveform))) > powerdrop)[0][0]
|
||||
stop = np.where((10 * np.log10(waveform[start:] / np.amax(waveform))) < powerdrop)[0][0] + start
|
||||
print('Pulse width at {:d}dB, i.e. FWHM: {:g} s'.format(powerdrop, time[stop] - time[start]))
|
||||
|
||||
print('Time window: {:g} s ({} iterations)'.format(timewindow, iterations))
|
||||
print('Time step: {:g} s'.format(dt))
|
||||
|
||||
if fft:
|
||||
# Calculate magnitude of frequency spectra of waveform
|
||||
power = 10 * np.log10(np.abs(np.fft.fft(waveform))**2)
|
||||
freqs = np.fft.fftfreq(power.size, d=dt)
|
||||
|
||||
# Shift powers so that frequency with maximum power is at zero decibels
|
||||
power -= np.amax(power)
|
||||
|
||||
# Set plotting range to 4 times centre frequency of waveform
|
||||
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')
|
||||
|
||||
# Plot waveform
|
||||
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], '-.')
|
||||
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]')
|
||||
|
||||
# Check time window
|
||||
if '.' in args.timewindow or 'e' in args.timewindow:
|
||||
if float(args.timewindow) > 0:
|
||||
timewindow = float(args.timewindow)
|
||||
iterations = round_value((float(args.timewindow) / dt)) + 1
|
||||
else:
|
||||
raise CmdInputError('Time window must have a value greater than zero')
|
||||
# If number of iterations given
|
||||
else:
|
||||
timewindow = (int(args.timewindow) - 1) * dt
|
||||
iterations = int(args.timewindow)
|
||||
fig, ax1 = plt.subplots(num=w.type, figsize=(20, 10), facecolor='w', edgecolor='w')
|
||||
|
||||
time = np.linspace(0, 1, iterations)
|
||||
time *= (iterations * dt)
|
||||
waveform = np.zeros(len(time))
|
||||
timeiter = np.nditer(time, flags=['c_index'])
|
||||
# Plot waveform
|
||||
ax1.plot(time, waveform, 'r', lw=2)
|
||||
ax1.set_xlabel('Time [s]')
|
||||
ax1.set_ylabel('Amplitude')
|
||||
|
||||
while not timeiter.finished:
|
||||
waveform[timeiter.index] = w.calculate_value(timeiter[0], dt)
|
||||
timeiter.iternext()
|
||||
[ax.grid() for ax in fig.axes] # Turn on grid
|
||||
|
||||
print('Waveform characteristics...')
|
||||
print('Type: {}'.format(w.type))
|
||||
print('Amplitude: {:g}'.format(w.amp))
|
||||
print('Centre frequency: {:g} Hz'.format(w.freq))
|
||||
print('Time to centre of pulse: {:g} s'.format(1 / w.freq))
|
||||
# Save a PDF/PNG of the figure
|
||||
#fig.savefig(os.path.dirname(os.path.abspath(__file__)) + os.sep + w.type + '.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig.savefig(os.path.dirname(os.path.abspath(__file__)) + os.sep + w.type + '.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
|
||||
# Calculate pulse width for gaussian
|
||||
if w.type == 'gaussian':
|
||||
powerdrop = -3 #dB
|
||||
start = np.where((10 * np.log10(waveform / np.amax(waveform))) > powerdrop)[0][0]
|
||||
stop = np.where((10 * np.log10(waveform[start:] / np.amax(waveform))) < powerdrop)[0][0] + start
|
||||
print('Pulse width at {:d}dB, i.e. FWHM: {:g} s'.format(powerdrop, time[stop] - time[start]))
|
||||
plt.show()
|
||||
|
||||
print('Time window: {:g} s ({} iterations)'.format(timewindow, iterations))
|
||||
print('Time step: {:g} s'.format(dt))
|
||||
|
||||
if args.fft:
|
||||
# Calculate magnitude of frequency spectra of waveform
|
||||
power = 10 * np.log10(np.abs(np.fft.fft(waveform))**2)
|
||||
freqs = np.fft.fftfreq(power.size, d=dt)
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Shift powers so that frequency with maximum power is at zero decibels
|
||||
power -= np.amax(power)
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(description='Plot built-in waveforms that can be used for sources.', usage='cd gprMax; python -m tools.plot_builtin_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', help='plot FFT of waveform', default=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Set plotting range to 4 times centre frequency of waveform
|
||||
pltrange = np.where(freqs > 4 * w.freq)[0][0]
|
||||
pltrange = np.s_[0:pltrange]
|
||||
# Check waveform parameters
|
||||
if args.type.lower() not in Waveform.types:
|
||||
raise CmdInputError('The waveform must have one of the following types {}'.format(', '.join(Waveform.types)))
|
||||
if args.freq <= 0:
|
||||
raise CmdInputError('The waveform requires an excitation frequency value of greater than zero')
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, num=w.type, figsize=(20, 10), facecolor='w', edgecolor='w')
|
||||
# Create waveform instance
|
||||
w = Waveform()
|
||||
w.type = args.type
|
||||
w.amp = args.amp
|
||||
w.freq = args.freq
|
||||
|
||||
# Plot waveform
|
||||
ax1.plot(time, waveform, 'r', lw=2)
|
||||
ax1.set_xlabel('Time [s]')
|
||||
ax1.set_ylabel('Amplitude')
|
||||
timewindow, iterations = check_timewindow(args.timewindow, args.dt)
|
||||
make_plot(w, timewindow, args.dt, iterations, args.fft)
|
||||
|
||||
# Plot frequency spectra
|
||||
markerline, stemlines, baseline = ax2.stem(freqs[pltrange], power[pltrange], '-.')
|
||||
plt.setp(baseline, 'linewidth', 0)
|
||||
plt.setp(stemlines, 'color', 'r')
|
||||
plt.setp(markerline, 'markerfacecolor', 'r', 'markeredgecolor', 'r')
|
||||
ax2.plot(freqs[pltrange]/1e9, power[pltrange], 'r', lw=2)
|
||||
ax2.set_xlabel('Frequency [Hz]')
|
||||
ax2.set_ylabel('Power [dB]')
|
||||
|
||||
else:
|
||||
fig, ax1 = plt.subplots(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')
|
||||
|
||||
[ax.grid() for ax in fig.axes] # Turn on grid
|
||||
|
||||
# Save a PDF/PNG of the figure
|
||||
#fig.savefig(os.path.dirname(os.path.abspath(__file__)) + os.sep + w.type + '.pdf', dpi=None, format='pdf', bbox_inches='tight', pad_inches=0.1)
|
||||
#fig.savefig(os.path.dirname(os.path.abspath(__file__)) + os.sep + w.type + '.png', dpi=150, format='png', bbox_inches='tight', pad_inches=0.1)
|
||||
|
||||
plt.show()
|
||||
|
||||
|
@@ -0,0 +1,92 @@
|
||||
#taguchi:
|
||||
optparams['resinner'] = [0.1, 1000]
|
||||
optparams['resmiddle'] = [0.1, 1000]
|
||||
optparams['resouter'] = [0.1, 1000]
|
||||
fitness = {'name': 'maxabsvalue', 'stop': 10, 'args': {'outputs': 'Ex60mm'}}
|
||||
#end_taguchi:
|
||||
|
||||
#python:
|
||||
|
||||
import numpy as np
|
||||
from gprMax.input_cmd_funcs import *
|
||||
|
||||
title = 'antenna_bowtie_opt'
|
||||
print('#title: {}'.format(title))
|
||||
|
||||
domain = domain(0.180, 0.120, 0.160)
|
||||
dxdydz = dx_dy_dz(0.001, 0.001, 0.001)
|
||||
timewindow = time_window(5e-9)
|
||||
fr4_dims = (0.120, 0.060, 0.002)
|
||||
bowtie_dims = (0.050, 0.040) # Length, height
|
||||
flare_angle = np.arctan((bowtie_dims[1]/2) / bowtie_dims[0])
|
||||
tx_pos = (domain[0]/2, domain[1]/2, 0.050)
|
||||
|
||||
# Vertical slot positions, relative to feed position, i.e. txpos[0]
|
||||
vcut_pos = (0.014, 0.027, 0.038)
|
||||
|
||||
# Loading resistor values
|
||||
res = np.array([optparams['resinner'], optparams['resmiddle'], optparams['resouter']])
|
||||
rescond = ((1 / res) * (dxdydz[1] / (dxdydz[0] * dxdydz[2]))) / 2 # Divide by number of parallel edges per resistor
|
||||
|
||||
# Materials
|
||||
material(4.8, 0, 1, 0, 'fr4')
|
||||
for i in range(len(res)):
|
||||
material(1, rescond[i], 1, 0, 'res' + str(i + 1))
|
||||
|
||||
# Source excitation and type
|
||||
print('#waveform: gaussian 1 2e9 mypulse')
|
||||
print('#transmission_line: x {:g} {:g} {:g} 50 mypulse'.format(tx_pos[0], tx_pos[1], tx_pos[2]))
|
||||
|
||||
# Output point - distance from tx_pos in z direction
|
||||
print('#rx: {:g} {:g} {:g} Ex60mm Ex'.format(tx_pos[0], tx_pos[1], tx_pos[2] + 0.060))
|
||||
|
||||
# Bowtie - upper x half
|
||||
triangle(tx_pos[0], tx_pos[1], tx_pos[2], tx_pos[0] + bowtie_dims[0], tx_pos[1] - bowtie_dims[1]/2, tx_pos[2], tx_pos[0] + bowtie_dims[0], tx_pos[1] + bowtie_dims[1]/2, tx_pos[2], 0, 'pec')
|
||||
|
||||
# Bowtie - upper x half - vertical cuts
|
||||
for i in range(len(vcut_pos)):
|
||||
for j in range(int(bowtie_dims[1] / dxdydz[2])):
|
||||
edge(tx_pos[0] + vcut_pos[i], tx_pos[1] - bowtie_dims[1]/2 + j * dxdydz[1], tx_pos[2], tx_pos[0] + vcut_pos[i] + dxdydz[0], tx_pos[1] - bowtie_dims[1]/2 + j * dxdydz[1], tx_pos[2], 'free_space')
|
||||
|
||||
# Bowtie - upper x half - vertical cuts - loading
|
||||
for i in range(len(vcut_pos)):
|
||||
gap = ((vcut_pos[i] * np.tan(flare_angle) * 2) - 4*dxdydz[1]) / 5
|
||||
edge(tx_pos[0] + vcut_pos[i], tx_pos[1] - (1.5 * gap) - dxdydz[1], tx_pos[2], tx_pos[0] + vcut_pos[i] + dxdydz[0], tx_pos[1] - (1.5 * gap) - dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] + vcut_pos[i], tx_pos[1] - (1.5 * gap) - 2*dxdydz[1], tx_pos[2], tx_pos[0] + vcut_pos[i] + dxdydz[0], tx_pos[1] - (1.5 * gap) - 2*dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] + vcut_pos[i], tx_pos[1] - (0.5 * gap), tx_pos[2], tx_pos[0] + vcut_pos[i] + dxdydz[0], tx_pos[1] - (0.5 * gap), tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] + vcut_pos[i], tx_pos[1] - (0.5 * gap) - dxdydz[1], tx_pos[2], tx_pos[0] + vcut_pos[i] + dxdydz[0], tx_pos[1] - (0.5 * gap) - dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] + vcut_pos[i], tx_pos[1] + (0.5 * gap), tx_pos[2], tx_pos[0] + vcut_pos[i] + dxdydz[0], tx_pos[1] + (0.5 * gap), tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] + vcut_pos[i], tx_pos[1] + (0.5 * gap) + dxdydz[1], tx_pos[2], tx_pos[0] + vcut_pos[i] + dxdydz[0], tx_pos[1] + (0.5 * gap) + dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] + vcut_pos[i], tx_pos[1] + (1.5 * gap) + dxdydz[1], tx_pos[2], tx_pos[0] + vcut_pos[i] + dxdydz[0], tx_pos[1] + (1.5 * gap) + dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] + vcut_pos[i], tx_pos[1] + (1.5 * gap) + 2*dxdydz[1], tx_pos[2], tx_pos[0] + vcut_pos[i] + dxdydz[0], tx_pos[1] + (1.5 * gap) + 2*dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
|
||||
# Bowtie - lower x half
|
||||
triangle(tx_pos[0] + dxdydz[0], tx_pos[1], tx_pos[2], tx_pos[0] - bowtie_dims[0], tx_pos[1] - bowtie_dims[1]/2, tx_pos[2], tx_pos[0] - bowtie_dims[0], tx_pos[1] + bowtie_dims[1]/2, tx_pos[2], 0, 'pec')
|
||||
|
||||
# Bowtie - lower x half - cuts for loading
|
||||
for i in range(len(vcut_pos)):
|
||||
for j in range(int(bowtie_dims[1] / dxdydz[2])):
|
||||
edge(tx_pos[0] - vcut_pos[i] - dxdydz[0], tx_pos[1] - bowtie_dims[1]/2 + j * dxdydz[1], tx_pos[2], tx_pos[0] - vcut_pos[i], tx_pos[1] - bowtie_dims[1]/2 + j * dxdydz[1], tx_pos[2], 'free_space')
|
||||
|
||||
# Bowtie - lower x half - vertical cuts - loading
|
||||
for i in range(len(vcut_pos)):
|
||||
gap = ((vcut_pos[i] * np.tan(flare_angle) * 2) - 4*dxdydz[1]) / 5
|
||||
edge(tx_pos[0] - vcut_pos[i] - dxdydz[0], tx_pos[1] - (1.5 * gap) - dxdydz[1], tx_pos[2], tx_pos[0] - vcut_pos[i], tx_pos[1] - (1.5 * gap) - dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] - vcut_pos[i] - dxdydz[0], tx_pos[1] - (1.5 * gap) - 2*dxdydz[1], tx_pos[2], tx_pos[0] - vcut_pos[i], tx_pos[1] - (1.5 * gap) - 2*dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] - vcut_pos[i] - dxdydz[0], tx_pos[1] - (0.5 * gap), tx_pos[2], tx_pos[0] - vcut_pos[i], tx_pos[1] - (0.5 * gap), tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] - vcut_pos[i] - dxdydz[0], tx_pos[1] - (0.5 * gap) - dxdydz[1], tx_pos[2], tx_pos[0] - vcut_pos[i], tx_pos[1] - (0.5 * gap) - dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] - vcut_pos[i] - dxdydz[0], tx_pos[1] + (0.5 * gap), tx_pos[2], tx_pos[0] - vcut_pos[i], tx_pos[1] + (0.5 * gap), tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] - vcut_pos[i] - dxdydz[0], tx_pos[1] + (0.5 * gap) + dxdydz[1], tx_pos[2], tx_pos[0] - vcut_pos[i], tx_pos[1] + (0.5 * gap) + dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] - vcut_pos[i] - dxdydz[0], tx_pos[1] + (1.5 * gap) + dxdydz[1], tx_pos[2], tx_pos[0] - vcut_pos[i], tx_pos[1] + (1.5 * gap) + dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
edge(tx_pos[0] - vcut_pos[i] - dxdydz[0], tx_pos[1] + (1.5 * gap) + 2*dxdydz[1], tx_pos[2], tx_pos[0] - vcut_pos[i], tx_pos[1] + (1.5 * gap) + 2*dxdydz[1], tx_pos[2], 'res' + str(i + 1))
|
||||
|
||||
# PCB
|
||||
box(tx_pos[0] - fr4_dims[0]/2, tx_pos[1] - fr4_dims[1]/2, tx_pos[2] - fr4_dims[2], tx_pos[0] + fr4_dims[0]/2, tx_pos[1] + fr4_dims[1]/2, tx_pos[2], 'fr4')
|
||||
|
||||
# Detailed geometry view of PCB and bowtie
|
||||
#geometry_view(tx_pos[0] - fr4_dims[0]/2, tx_pos[1] - fr4_dims[1]/2, tx_pos[2] - fr4_dims[2], tx_pos[0] + fr4_dims[0]/2, tx_pos[1] + fr4_dims[1]/2, tx_pos[2] + dxdydz[2], dxdydz[0], dxdydz[1], dxdydz[2], title + '_tx', type='f')
|
||||
|
||||
# Geometry view of entire domain
|
||||
#geometry_view(0, 0, 0, domain[0], domain[1], domain[2], dxdydz[0], dxdydz[1], dxdydz[2], title)
|
||||
|
||||
#end_python:
|
@@ -16,7 +16,7 @@ import matplotlib.pyplot as plt
|
||||
|
||||
All fitness functions must take two arguments and return a single fitness value.
|
||||
The first argument should be the name of the output file
|
||||
The second argument is a list which can contain any number of additional arguments, e.g. names (IDs) of outputs (rxs) from input file
|
||||
The second argument is a dictionary which can contain any number of additional arguments, e.g. names (IDs) of outputs (rxs) from input file
|
||||
"""
|
||||
|
||||
def minvalue(filename, args):
|
||||
@@ -64,6 +64,28 @@ def maxvalue(filename, args):
|
||||
|
||||
return maxvalue
|
||||
|
||||
def maxabsvalue(filename, args):
|
||||
"""Maximum absolute value from a response.
|
||||
|
||||
Args:
|
||||
filename (str): Name of output file
|
||||
args (dict): 'outputs' key with a list of names (IDs) of outputs (rxs) from input file
|
||||
|
||||
Returns:
|
||||
maxabsvalue (float): Maximum absolute value from specific outputs
|
||||
"""
|
||||
|
||||
f = h5py.File(filename, 'r')
|
||||
nrx = f.attrs['nrx']
|
||||
|
||||
for rx in range(1, nrx + 1):
|
||||
output = f['/rxs/rx' + str(rx) + '/']
|
||||
if output.attrs['Name'] in args['outputs']:
|
||||
outputname = list(output.keys())[0]
|
||||
maxabsvalue = np.amax(np.abs(output[outputname]))
|
||||
|
||||
return maxabsvalue
|
||||
|
||||
|
||||
def xcorr(filename, args):
|
||||
"""Maximum value of a cross-correlation between a response and a reference response.
|
@@ -12,7 +12,7 @@ from gprMax.optimisation_taguchi import plot_optimisation_history
|
||||
"""Plots the results (pickled to file) from a Taguchi optimisation process."""
|
||||
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(description='Plots the results (pickled to file) from a Taguchi optimisation process.', usage='cd gprMax; python -m user_libs.optimisation_taguchi_plot picklefile')
|
||||
parser = argparse.ArgumentParser(description='Plots the results (pickled to file) from a Taguchi optimisation process.', usage='cd gprMax; python -m user_libs.optimisation_taguchi.plot_results picklefile')
|
||||
parser.add_argument('picklefile', help='name of file including path')
|
||||
args = parser.parse_args()
|
||||
|
在新工单中引用
屏蔽一个用户