fixed merge

这个提交包含在:
jasminium
2016-05-11 16:30:08 +01:00
当前提交 88c41c5833
共有 50 个文件被更改,包括 1574 次插入1158 次删除

查看文件

@@ -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'))

查看文件

@@ -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):

查看文件

@@ -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()