Advanced Model Building (Python API)

Introduction

Practical workflows are in Sources and ports and Reusable studies. The entries here describe the model objects and include their output contracts; complete HDF5 schemas are in Model Output.

gprMax has a choice of two methods for building a model to simulate:

  1. A text-based (ASCII) input file, which can be created with any text editor, and uses a series of gprMax commands which begin with the hash character (#). This method is recommended for beginners and those not familiar with Python, and is described in the Model Building section.

  2. A Python API, which includes all the functionality of method 1 as well as several more advanced features. This method is recommended for those who prefer to use Python or need access to specific API-only advanced features, and is described in this section of the documentation.

The Python API in gprMax allows users to access gprMax functions directly from Python by importing the gprMax module. There are several advantages to using the API:

  • Users can take advantage of the Python language - for instance, the structural elements of Python can be utilised more easily.

  • gprMax objects can be used directly within functions, classes, modules and packages. In this way collections of components can be defined, reused and modified. For example, complex targets can be imported from a separate module and combined with an antenna from another module.

  • The API can interface with other Python libraries. For example, the API could be used to create a parametric antenna and the external library Scipy could then be used to optimise its parameters.

The API syntax is generally more verbose than the hash-command syntax. However, for commands with a variable number of parameters, such as those that add dispersive properties, the API may be more manageable.

Keyword names are checked when a model object is constructed. Unknown keywords raise TypeError, including when their value is None, False, zero, or an empty string. For example, averagin=False is an error; use averaging=False. This also applies to optional optimisation parameters in the GPR antenna toolbox. No spelling is corrected automatically. Use the parameters documented for the particular command or antenna.

Source/receiver positions and output bounds containing inf are resolved against each grid when it is built. The declaration retains its symbolic coordinates, so reusing it with a different grid spacing or domain does not freeze the first build’s resolved position. This refers to independent full model builds; geometry_fixed and study reuse do not change an existing grid’s discretisation.

str(user_object) is a readable, hash-command-style diagnostic, not a general API-to-input-file exporter. In particular, omitted optional fields do not always round-trip through the positional hash-command syntax. Use the documented command syntax when writing an input file.

Note

In prior versions of gprMax (<4) the input file could be scripted using Python inserted between two commands (#python: and #end_python:). This feature is now deprecated and will be removed entirely in later versions. Users are encouraged to move to the new Python API. Antenna models can still be inserted between #python: and #end_python: commands but will need to make a small change to their input file. An example of this is provided in examples/gpr/antennas/gssi_1500/antenna_like_GSSI_1500_fs.in. Alternatively a switch to the Python API can be made using the adjacent examples/gpr/antennas/gssi_1500/antenna_like_GSSI_1500_fs.py example.

Example

antenna_wire_dipole_fs.py

The equivalent hash-command model is antenna_wire_dipole_fs.in.

This example is used to give an introduction to the gprMax Python API.

 1"""Half-wavelength wire dipole excited by a one-cell voltage-source port.
 2
 3The source-owned port calculates and stores frequency, corrected complex
 4S11, input impedance, and input admittance under ``/ports/feed`` in the model
 5HDF5 output. No post-processing of source voltage and current is required.
 6"""
 7
 8from pathlib import Path
 9
10import gprMax
11
12fn = Path(__file__)
13
14scene = gprMax.Scene()
15scene.add(gprMax.Title(name=fn.stem))
16scene.add(gprMax.Domain(p1=(0.050, 0.050, 0.200)))
17scene.add(gprMax.Discretisation(p1=(0.001, 0.001, 0.001)))
18scene.add(gprMax.TimeWindow(time=60e-9))
19scene.add(gprMax.Waveform(wave_type="gaussian", amp=1, freq=1e9, id="mypulse"))
20scene.add(
21    gprMax.Edge(
22        p1=(0.025, 0.025, 0.025),
23        p2=(0.025, 0.025, 0.175),
24        material_id="pec",
25    )
26)
27scene.add(
28    gprMax.Edge(
29        p1=(0.025, 0.025, 0.100),
30        p2=(0.025, 0.025, 0.101),
31        material_id="free_space",
32    )
33)
34scene.add(
35    gprMax.VoltageSource(
36        p1=(0.025, 0.025, 0.100),
37        polarisation="z",
38        resistance=50,
39        waveform_id="mypulse",
40        id="feed",
41    )
42)
43scene.add(
44    gprMax.GeometryView(
45        p1=(0.020, 0.020, 0.020),
46        p2=(0.030, 0.030, 0.180),
47        dl=(0.001, 0.001, 0.001),
48        filename=fn.stem,
49        output_type="f",
50    )
51)
52
53gprMax.run(scenes=[scene], n=1, outputfile=fn)
  1. Import the gprMax module.

  2. Objects for the model are created from the gprMax module by passing object parameters as key=value arguments. The adjacent .in model shows their equivalent positional hash commands.

  3. Create a gprMax.scene.Scene object. The scene is a container for all the objects required in a simulation. Simulations with multiple models, e.g. A-scans, should have a separate scene for each model (A-scan). Each scene must contain the essential functions and objects required for that particular model.

  4. Add objects are to the scene.

  5. Run the simulation.

Unless otherwise specified, the SI system of units is used throughout gprMax:

  • All parameters associated with simulated space (i.e. size of model, spatial increments, etc…) should be specified in metres.

  • All parameters associated with time (i.e. total simulation time, time instants, etc…) should be specified in seconds.

  • All parameters denoting frequency should be specified in Hertz.

  • All parameters associated with spatial coordinates in the model should be specified in metres. The origin of the coordinate system (0,0) is at the lower left corner of the model.

It is important to note that gprMax converts spatial and temporal parameters given in metres and seconds to integer values corresponding to FDTD cell coordinates and iteration number respectively. Therefore, rounding to the nearest integer number of the user defined values is performed.

The fundamental spatial and temporal discretization steps are denoted as \(\Delta x\) , \(\Delta y\), \(\Delta z\) and \(\Delta t\) respectively.

The functions have been grouped into six categories:

  • Essential - required to run any model, such as the domain size and spatial discretization

  • General - provide further control over the model

  • Material - used to introduce different materials into the model

  • Object construction - used to build geometric shapes with different constitutive parameters

  • Source and output - used to place source and output points in the model

  • PML - provide advanced customisation and optimisation of the absorbing boundary conditions

Essential functions

Most of the functions are optional but there are some essential functions which are necessary in order to construct any model. For example, none of the media and object functions are necessary to run a model. However, without specifying any objects in the model gprMax will simulate free space (air), which on its own, is not particularly useful for GPR modelling. If you have not specified a functions which is essential in order to run a model, for example the size of the model, gprMax will terminate execution and issue an appropriate error message.

The essential functions are:

Running model(s)

gprMax.run(scenes=None, inputfile=None, outputfile=None, n=1, i=None, taskfarm=False, mpi=None, gpu=None, opencl=None, metal=None, cpu_precision='single', gpu_precision='single', subgrid=False, autotranslate=False, geometry_only=False, geometry_fixed=False, study=None, write_processed=False, show_progress_bars=False, hide_progress_bars=False, log_level=20, log_file=False, log_all_ranks=False, allow_underresolved=False)

Entry point for application programming interface (API).

Runs the simulation for the given list of scenes.

Parameters
  • scenes – list of the scenes to run the model. Multiple scene objects can be given in order to run multiple simulation runs. Each scene must contain the essential simulation objects.

  • inputfile – optional string for input file path. Can also run simulation by providing an input file.

  • outputfile – string for file path to the output data file

  • n – optional int for number of required simulation runs.

  • i – optional int for model number to start/restart simulation from. For ordinary runs, n is the number of models to execute, not the final model number: i=45, n=16 runs A-scans 45 through 60. Study-managed case counts follow the study’s restart rules.

  • taskfarm – optional boolean flag to use Message Passing Interface (MPI) task farm. This option is most usefully combined with n to allow individual models to be farmed out using a MPI task farm, e.g. to create a B-scan with 60 traces and use MPI to farm out each trace. For further details see the performance section of the User Guide.

  • mpi – optional flag to use Message Passing Interface (MPI) to divide the model between MPI ranks. Three integers should be provided to define the number of MPI processes (min 1) in the x, y, and z dimensions.

  • gpu – optional list/boolean to use NVIDIA GPU or list of NVIDIA GPU device ID(s) for specific GPU card(s).

  • opencl – optional list/boolean to use OpenCL or list of OpenCL device ID(s) for specific compute device(s).

  • metal – optional list/boolean to use Apple Metal or list of Apple Metal GPU device ID(s) for specific GPU card(s).

  • cpu_precision – optional string, “single” or “double”, precision for the CPU solver. Defaults to “single” to preserve memory - ignored if a GPU solver is used. Refining subgrids force double; ratio-one equal-resolution regions inherit this setting.

  • gpu_precision – optional string, “single” or “double”, precision for the CUDA/OpenCL/Metal solvers. Defaults to “single”. Ignored if the CPU solver is used. Subgridding is currently CPU-only.

  • subgrid – optional boolean to use sub-gridding.

  • autotranslate – optional boolean for sub-gridding to auto translate objects with main grid coordinates to their equivalent local grid coordinate within the subgrid. If this option is off users must specify sub-grid object point within the global subgrid space.

  • geometry_only – optional boolean to build a model and produce any geometry views but do not run the simulation.

  • geometry_fixed – optional boolean to run a series of models where the geometry does not change between models.

  • study – optional Study instance defining validated per-run object states while reusing one geometry.

  • write_processed – optional boolean to write another input file after any #python blocks (which are deprecated) in the original input file has been processed.

  • show_progress_bars – optional boolean to force progress bars to be displayed - by default, progress bars are displayed when the log level is info (20) or less.

  • hide_progress_bars – optional boolean to force progress bars to be hidden - by default, progress bars are hidden when the log level is greater than info (20).

  • log_level – optional int for level of logging to use.

  • log_file – optional boolean to write logging information to file.

  • log_all_ranks – optional boolean to write logging information from all MPI ranks. Default behaviour only provides log output from rank 0. When used with –log-file, each ran will write to an individual file.

  • allow_underresolved – optional boolean, default False. Warn rather than stop when the pre-solve wavelength-sampling check finds insufficient spatial resolution. This research override does not change the mesh, timestep, material stability checks or frequency-domain output-validity limits.

For deliberate mesh-resolution experiments, allow_underresolved=True changes the pre-solve wavelength-sampling rejection into a warning. The default is False; material stability and output-validity checks remain enabled. See spatial-resolution diagnostic for the scope of this override and the exclusion of zero-amplitude passive-source waveforms.

Creating a model scene

class gprMax.Scene

Scene stores all of the user created objects.

add(user_object: UserObject)

Add the user object to the scene.

Parameters

user_object – user object to add to the scene. For example, gprMax.Domain

Domain

class gprMax.user_objects.cmds_singleuse.Domain(p1: Tuple[float, float, float])

Size of the model.

domain_size

Extent of the model domain (x, y, z).

Type

tuple

Domain Mode

class gprMax.user_objects.cmds_singleuse.DomainMode(mode: str)

Declares the domain mode for the model: 2D TM, 2D TE, or 3D.

Optional. Only required when the model uses inf on the invariant axis of #domain (and consequently on that axis in position-taking commands) to build a 2D model without the user having to compute the axis extent by hand. “3D” is accepted explicitly, even though it is also the default with no #domain_mode command present, so that a model can be switched between 2D and 3D by changing this command’s argument alone, without having to remove the command.

requested_mode

Requested domain mode, “TM”, “TE” or “3D”.

Type

str

For an explicit 2D model, set one component of Domain.p1 to float('inf') and add DomainMode('TM') or DomainMode('TE') before the domain is built. The infinite value identifies the invariant axis; it is resolved internally to the one-cell TM or two-cell TE Yee-grid thickness.

scene.add(gprMax.Discretisation(p1=(0.002, 0.002, 0.002)))
scene.add(gprMax.DomainMode(mode='TE'))
scene.add(gprMax.Domain(p1=(0.24, 0.21, float('inf'))))

Discretisation

class gprMax.user_objects.cmds_singleuse.Discretisation(p1: Tuple[float, float, float])

Spatial discretisation of the model in the x, y, and z dimensions.

discretisation

Spatial discretisation of the model (x, y, z)

Type

tuple

Time Window

class gprMax.user_objects.cmds_singleuse.TimeWindow(time: Optional[float] = None, iterations: Optional[int] = None)

Specifies the total required simulated time.

Either time or iterations must be specified. If both are specified, time takes precedence.

time

Simulated time in seconds.

Type

float | None

iterations

Number of iterations.

Type

int | None

A minimal three-dimensional scene contains the three essential model objects:

scene = gprMax.Scene()
scene.add(gprMax.Domain(p1=(0.30, 0.20, 0.15)))
scene.add(gprMax.Discretisation(p1=(0.002, 0.002, 0.002)))
scene.add(gprMax.TimeWindow(time=12e-9))

gprMax.run(scenes=[scene], n=1, outputfile='minimal_model')

General functions

Title

class gprMax.user_objects.cmds_singleuse.Title(name: str)

Title of the model.

title

Model title.

Type

str

Number of OpenMP threads

class gprMax.user_objects.cmds_singleuse.OMPThreads(n: int)

Set the number of OpenMP threads to use when running the model.

Usually this should match the number of physical CPU cores available.

omp_threads

Number of OpenMP threads.

Type

int

Time Step Stability Factor

class gprMax.user_objects.cmds_singleuse.TimeStepStabilityFactor(f: float)

Factor by which to reduce the time step from the CFL limit.

A Scene declaring SurfaceImpedance caps the effective factor at 0.99 before time-dependent objects are built, preserving smaller user values.

stability_factor

Factor to multiply time step by.

Type

float

Declaring SurfaceImpedance automatically caps the effective factor at 0.99, preserves any smaller user factor, and reports an automatic reduction in the build log. See Automatic time-step margin.

Dispersive materials also undergo the Dispersive-material timestep check before time stepping. If it fails, the run stops without changing the chosen timestep or material parameters. Use TimeStepStabilityFactor(f=...) to explicitly request a smaller timestep; the diagnostic gives a checked candidate when one is found.

Output Directory

class gprMax.user_objects.cmds_singleuse.OutputDir(dir: str)

Set the directory where output file(s) will be stored.

output_dir

File path to directory. Relative Python API paths are resolved against the working directory at build time. Hash commands resolve them against the top-level input file instead.

Type

str

Relative OutputDir(dir=...) paths are resolved against the working directory when the Scene is built. This differs from paths in the #output_dir hash command, which are relative to the top-level input file. The resolved directory is retained during geometry_fixed=True repetition, with distinct numbered model and snapshot paths.

Magnetic Averaging

class gprMax.user_objects.cmds_singleuse.MagneticAveraging(mode: str = 'harmonic')

Sets the mixing rule used to combine the relative magnetic permeability (mu_r) and magnetic loss (sigma*) of two different materials when a magnetic (H) field component’s Yee-cell smoothing needs to average across a material boundary.

Optional. Defaults to ‘harmonic’. Each H component (Hx, Hy, Hz) is averaged from the two neighbouring cells stacked along the component’s own axis, i.e. normal to any interface between them. The normal component of B is continuous across a material interface, so the harmonic mean of mu_r (and, for consistency, sigma*) is the physically correct mixing rule for this direction - unlike the tangential 4-cell average used for E-field smoothing, where the arithmetic mean already used remains correct and is unaffected by this command.

Versions of gprMax prior to the introduction of this command always used a simple arithmetic mean here instead. Set this command to ‘arithmetic’ to reproduce that older behaviour exactly, e.g. to reproduce results generated with an older version of gprMax.

mode

‘harmonic’ (default) or ‘arithmetic’.

Type

str

Dispersive Averaging

class gprMax.user_objects.cmds_singleuse.DispersiveAveraging(enabled: bool = True)

Enable or disable arithmetic interface averaging for dispersive media.

When enabled, Debye, Lorentz, and Drude materials participate in the four-cell electric-edge averaging used for nondispersive dielectric materials. The exact effective material retains every distinct inclusive pole and scales each residue by its cell weight. Ordinary dielectric smoothing remains controlled by the averaging argument of each volumetric object.

This option is disabled by default because the compound material can have more poles than its constituents, increasing model-wide dispersive memory.

enabled

Whether dispersive interface averaging is enabled.

Type

bool

Reusable parameter studies

See Reusable studies for the task-selection guide, case semantics, restrictions, CSV/Python examples, result types and restart behaviour. gprMax.Study, gprMax.StudyCase and gprMax.ObjectState define the common scheduling interface.

class gprMax.studies.Study(type: str, cases: Sequence[StudyCase], *, source_path: Optional[Union[Path, str]] = None, source_text: Optional[str] = None)

A sequence of cases evaluated while reusing one built geometry.

classmethod from_csv(type: str, path: Union[str, Path]) → Study

Read a sparse, wide study table from CSV.

class gprMax.studies.GPRStudy(cases: Sequence[StudyCase])

Convenience API for a gpr study.

Fixed-topology terminal-source studies

SourceStudy schedules fixed transmission-line, frill and rational-network generators without removing their passive loads.

class gprMax.studies.SourceStudy(cases: Sequence[StudyCase], *, source_path: Optional[Union[Path, str]] = None, source_text: Optional[str] = None)

Reusable study for fixed-topology stateful terminal sources.

Transmission-line resistance, magnetic-frill geometry/Z0, and rational network topology remain fixed. Cases may select the active generators and change only their waveform, time window, and scale. An omitted or inactive source retains its physical terminal with a zero generator drive.

Finite-resistance voltage-port studies

PortStudy computes a complete S matrix from finite-resistance voltage ports. Hard sources are not matched terminations.

class gprMax.studies.PortStudy(cases: Sequence[StudyCase], *, source_path: Optional[Union[Path, str]] = None, source_text: Optional[str] = None)

One-active-port-per-case finite-resistance voltage-source study.

class gprMax.studies.PortStudyResult(frequency: NDArray[floating], port_ids: tuple[str, ...], source_ids: tuple[str, ...], case_ids: tuple[str, ...], reference_impedance: NDArray[floating], gap_correction: NDArray[complexfloating], s_source: NDArray[complexfloating], s: NDArray[complexfloating], valid_s_source: NDArray[bool], valid_s: NDArray[bool], output_file: Path)

Assembled source-plane and gap-corrected multiport S parameters.

frequency

frequency axis in Hz with shape (F,).

Type

NDArray[numpy.floating]

port_ids

output-port ordering used by both S-matrix port axes.

Type

tuple[str, …]

source_ids

voltage-source identifiers corresponding to port_ids.

Type

tuple[str, …]

case_ids

study-case ordering used for the driven input columns.

Type

tuple[str, …]

reference_impedance

real port impedances in ohms with shape (P,).

Type

NDArray[numpy.floating]

gap_correction

dimensionless parallel gap admittance with shape (F, P).

Type

NDArray[numpy.complexfloating]

s_source

uncorrected source-plane S matrix with shape (F, P, P).

Type

NDArray[numpy.complexfloating]

s

gap-corrected S matrix with shape (F, P, P); its axes are (frequency, output_port, input_port).

Type

NDArray[numpy.complexfloating]

valid_s_source

validity mask for s_source.

Type

NDArray[numpy.bool]

valid_s

validity mask for s.

Type

NDArray[numpy.bool]

output_file

aggregate study HDF5 path.

Type

pathlib.Path

Eigenmode-port studies and array synthesis

EigenmodeStudy provides modal S matrices, gprMax.ArrayCodebook states and embedded far-field synthesis.

class gprMax.studies.EigenmodeStudy(cases: Sequence[StudyCase], *, codebook: Optional[ArrayCodebook] = None, source_path: Optional[Union[Path, str]] = None, source_text: Optional[str] = None)

One-active-modal-channel-per-case reusable eigenmode-port study.

All transverse modal anchor problems are solved during the first geometry build. Later cases only synthesize an injection from those cached bases, clear modal/virtual-guide state, and select a new active (port, mode).

class gprMax.studies.EigenmodeStudyResult(frequency: NDArray[~numpy.floating], channel_ports: NDArray[~numpy.integer], channel_modes: NDArray[~numpy.integer], case_ids: tuple[str, ...], s: NDArray[~numpy.complexfloating], power_wave_valid_s: NDArray[~numpy.bool], coefficient_valid_s: NDArray[~numpy.bool], output_file: ~pathlib.Path, embedded_far_fields: ~typing.Mapping[str, ~gprMax.studies.EmbeddedFarFieldBank] = <factory>, incident_matrix: ~typing.Optional[NDArray[~numpy.complexfloating]] = None, outgoing_matrix: ~typing.Optional[NDArray[~numpy.complexfloating]] = None, power_wave_valid_matrix: ~typing.Optional[NDArray[~numpy.bool]] = None, coefficient_valid_wave_matrix: ~typing.Optional[NDArray[~numpy.bool]] = None, deembedding_condition_number: ~typing.Optional[NDArray[~numpy.floating]] = None, deembedding_valid: ~typing.Optional[NDArray[~numpy.bool]] = None)

Assembled modal S matrix from one active port/mode per run.

frequency

frequency axis in Hz with shape (F,).

Type

NDArray[numpy.floating]

channel_ports

one-based port number for each modal channel.

Type

NDArray[numpy.integer]

channel_modes

one-based mode number for each modal channel.

Type

NDArray[numpy.integer]

case_ids

identifiers of the independent excitation cases.

Type

tuple[str, …]

s

generalized modal S matrix with shape (F, C, C) and axes (frequency, output_channel, input_channel).

Type

NDArray[numpy.complexfloating]

power_wave_valid_s

physical propagating-power validity mask for s.

Type

NDArray[numpy.bool]

coefficient_valid_s

generalized-coefficient validity mask for s.

Type

NDArray[numpy.bool]

output_file

aggregate study HDF5 path.

Type

pathlib.Path

embedded_far_fields

mapping from transform_id/output_id to de-embedded EmbeddedFarFieldBank objects.

Type

Mapping[str, gprMax.studies.EmbeddedFarFieldBank]

incident_matrix

measured incident modal matrix for all cases, when retained.

Type

Optional[NDArray[numpy.complexfloating]]

outgoing_matrix

measured outgoing modal matrix for all cases, when retained.

Type

Optional[NDArray[numpy.complexfloating]]

power_wave_valid_matrix

physical power-wave validity for the measured modal matrices.

Type

Optional[NDArray[numpy.bool]]

coefficient_valid_wave_matrix

generalized-coefficient validity for the measured modal matrices.

Type

Optional[NDArray[numpy.bool]]

deembedding_condition_number

condition number of the incident-wave solve at each frequency.

Type

Optional[NDArray[numpy.floating]]

deembedding_valid

validity of that solve at each frequency.

Type

Optional[NDArray[numpy.bool]]

evaluate_array_state(state: ArrayState) → ArrayStateResult

Evaluate one post-processed array state from the embedded basis.

evaluate_codebook(codebook: ArrayCodebook) → tuple[gprMax.studies.ArrayStateResult, ...]

Evaluate every named state in a validated array codebook.

excitation_weights(excitations: Iterable[ModalWeight])

Return frequency-dependent incident power-wave weights.

classmethod from_hdf5(filename: Union[str, Path]) → EigenmodeStudyResult

Load an aggregate eigenmode study for offline array synthesis.

outgoing(excitations: Iterable[ModalWeight])

Apply the assembled S matrix to a weighted incident-wave vector.

Zero-weight channels are omitted rather than multiplied by their stored NaNs. An output is NaN when any active input lacks a valid generalized coefficient to that output. Use power_wave_valid_s separately when the result must also represent propagating real-power waves.

class gprMax.studies.ModalWeight(port: int, mode: int, power: float = 1.0, phase_deg: float = 0.0, delay_s: float = 0.0)

One modal array weight using power, phase, and/or true time delay.

class gprMax.studies.ArrayState(id: str, drives: Iterable[ModalWeight], *, description: str = '')

A named set of modal power, phase-shifter, and true-time-delay weights.

class gprMax.studies.ArrayCodebook(states: Iterable[ArrayState], *, embedded_far_fields: Iterable[EmbeddedFarFieldSpec] = (), description: str = '', source_path: Optional[Union[Path, str]] = None, source_text: Optional[str] = None)

Versioned modal-array states and requested embedded far-field bases.

class gprMax.studies.EmbeddedFarFieldSpec(transform_id: str, output_id: str)

One frequency-domain far-field request retained as an embedded basis.

class gprMax.studies.EmbeddedFarFieldBank(transform_id: str, output_id: str, frequency: NDArray[floating], theta: NDArray[floating], phi: NDArray[floating], etheta: NDArray[complexfloating], ephi: NDArray[complexfloating], sphere_theta: NDArray[floating], sphere_phi: NDArray[floating], sphere_weights: NDArray[floating], sphere_etheta: NDArray[complexfloating], sphere_ephi: NDArray[complexfloating], valid: NDArray[bool], impedance: float, theta_order: int, phi_order: int, enclosure_radius: float)

Per-unit-incident-power complex field basis ordered by modal channel.

Requested-direction fields have shape (F, D, C) and full-sphere fields have shape (F, Q, C), where F is frequency, D is the number of requested directions, Q is the internal quadrature size, and C is the modal-channel count. Complex fields are range-normalized field per square-root watt of incident modal power.

transform_id

source frequency-transform identifier.

Type

str

output_id

source far-field request identifier.

Type

str

frequency

frequency axis in Hz.

Type

NDArray[numpy.floating]

theta

requested polar angles in degrees.

Type

NDArray[numpy.floating]

phi

requested azimuthal angles in degrees.

Type

NDArray[numpy.floating]

etheta

embedded requested-direction \(E_\theta\) fields.

Type

NDArray[numpy.complexfloating]

ephi

embedded requested-direction \(E_\phi\) fields.

Type

NDArray[numpy.complexfloating]

sphere_theta

internal full-sphere polar angles in degrees.

Type

NDArray[numpy.floating]

sphere_phi

internal full-sphere azimuthal angles in degrees.

Type

NDArray[numpy.floating]

sphere_weights

full-sphere solid-angle quadrature weights.

Type

NDArray[numpy.floating]

sphere_etheta

embedded full-sphere \(E_\theta\) fields.

Type

NDArray[numpy.complexfloating]

sphere_ephi

embedded full-sphere \(E_\phi\) fields.

Type

NDArray[numpy.complexfloating]

valid

physical modal-channel validity mask with shape (F, C).

Type

NDArray[numpy.bool]

impedance

lossless exterior wave impedance in ohms.

Type

float

theta_order

full-sphere polar quadrature order.

Type

int

phi_order

full-sphere azimuthal quadrature order.

Type

int

enclosure_radius

radius in metres enclosing the transformed source.

Type

float

class gprMax.studies.ArrayStateResult(id: str, frequency: NDArray[floating], incident: NDArray[complexfloating], outgoing: NDArray[complexfloating], active_reflection: NDArray[complexfloating], incident_power: NDArray[floating], reflected_power: NDArray[floating], accepted_power: NDArray[floating], tarc: NDArray[floating], valid: NDArray[bool], far_fields: Mapping[str, ArrayFarFieldResult])

Network and optional radiation result for one named array state.

id

array-state identifier.

Type

str

frequency

frequency axis in Hz with shape (F,).

Type

NDArray[numpy.floating]

incident

incident modal power waves with shape (F, C).

Type

NDArray[numpy.complexfloating]

outgoing

outgoing modal power waves with shape (F, C).

Type

NDArray[numpy.complexfloating]

active_reflection

active reflection coefficient for each driven modal channel, with shape (F, C).

Type

NDArray[numpy.complexfloating]

incident_power

total incident power in W.

Type

NDArray[numpy.floating]

reflected_power

total reflected power in W.

Type

NDArray[numpy.floating]

accepted_power

net accepted power in W.

Type

NDArray[numpy.floating]

tarc

total active reflection coefficient.

Type

NDArray[numpy.floating]

valid

frequency-bin network validity mask.

Type

NDArray[numpy.bool]

far_fields

mapping from transform_id/output_id to synthesized ArrayFarFieldResult objects.

Type

Mapping[str, gprMax.studies.ArrayFarFieldResult]

class gprMax.studies.ArrayFarFieldResult(transform_id: str, output_id: str, theta: NDArray[floating], phi: NDArray[floating], etheta: NDArray[complexfloating], ephi: NDArray[complexfloating], radiation_intensity: NDArray[floating], radiated_power: NDArray[floating], directivity: NDArray[floating], directivity_dbi: NDArray[floating], gain: NDArray[floating], gain_dbi: NDArray[floating], realized_gain: NDArray[floating], realized_gain_dbi: NDArray[floating], radiation_efficiency: NDArray[floating], total_efficiency: NDArray[floating], maximum_directivity: NDArray[floating], maximum_directivity_dbi: NDArray[floating], maximum_theta: NDArray[floating], maximum_phi: NDArray[floating], valid: NDArray[bool])

Coherently synthesized fields and radiation metrics for one array state.

Direction-dependent arrays have shape (F, D); integrated, maximum, efficiency, and validity arrays have shape (F,).

transform_id

source frequency-transform identifier.

Type

str

output_id

source far-field request identifier.

Type

str

theta

requested polar angles in degrees.

Type

NDArray[numpy.floating]

phi

requested azimuthal angles in degrees.

Type

NDArray[numpy.floating]

etheta

coherently combined range-normalized \(E_\theta\).

Type

NDArray[numpy.complexfloating]

ephi

coherently combined range-normalized \(E_\phi\).

Type

NDArray[numpy.complexfloating]

radiation_intensity

radiation intensity in W/sr.

Type

NDArray[numpy.floating]

radiated_power

full-sphere integrated radiated power in W.

Type

NDArray[numpy.floating]

directivity

linear directivity.

Type

NDArray[numpy.floating]

directivity_dbi

directivity in dBi.

Type

NDArray[numpy.floating]

gain

linear accepted-power gain.

Type

NDArray[numpy.floating]

gain_dbi

accepted-power gain in dBi.

Type

NDArray[numpy.floating]

realized_gain

linear incident-power gain.

Type

NDArray[numpy.floating]

realized_gain_dbi

incident-power gain in dBi.

Type

NDArray[numpy.floating]

radiation_efficiency

radiated power divided by accepted power.

Type

NDArray[numpy.floating]

total_efficiency

radiated power divided by incident power.

Type

NDArray[numpy.floating]

maximum_directivity

maximum linear directivity.

Type

NDArray[numpy.floating]

maximum_directivity_dbi

maximum directivity in dBi.

Type

NDArray[numpy.floating]

maximum_theta

polar angle of maximum directivity in degrees.

Type

NDArray[numpy.floating]

maximum_phi

azimuthal angle of maximum directivity in degrees.

Type

NDArray[numpy.floating]

valid

frequency-bin validity mask.

Type

NDArray[numpy.bool]

gprMax.studies.modal_array_weights(frequency, channel_ports, channel_modes, excitations: Iterable[ModalWeight])

Build incident modal power waves using the engineering FFT convention.

A constant phase shifter contributes exp(+j phase). A true time delay contributes exp(-j 2 pi f delay). The coefficient magnitude is the square root of the requested incident power in watts.

gprMax.studies.combine_embedded_modal_responses(responses, weights, *, channel_axis=-1)

Linearly combine complex embedded responses with modal weights.

responses must have frequency as its first axis and one axis ordered like the study’s modal channels. All intervening axes (observation point, angle, field component, and so on) are retained.

Plane-wave and RCS studies

PlaneWaveStudy schedules incident directions and polarisation. GPRStudy schedules point-source acquisitions.

class gprMax.studies.PlaneWaveStudy(cases: Sequence[StudyCase], *, source_path: Optional[Union[Path, str]] = None, source_text: Optional[str] = None)

Reusable-geometry study with one rebuilt discrete plane wave per case.

The main Yee geometry is retained, but the auxiliary one-dimensional DPW grid is deliberately reconstructed because its integer direction mapping, length, projections, material profile, and PML state depend on the case. Declarative NTFF monitors are reconstructed with it so their accumulated DFT and incident-wave normalisation state cannot leak between cases.

class gprMax.studies.StudyCase(id: str, states: Iterable[ObjectState])

A named set of object overrides.

class gprMax.studies.ObjectState(object: Any, **parameters: Any)

Overrides for one study object in one case.

object may be a deterministic object ID such as "hertzian_dipole_1"/"rx_1" or the corresponding Python API user-object instance.

Other general settings

Typical general settings are added directly to the scene:

scene.add(gprMax.Title(name='buried_target'))
scene.add(gprMax.OMPThreads(n=8))
scene.add(gprMax.TimeStepStabilityFactor(f=0.95))
scene.add(gprMax.OutputDir(dir='results'))
scene.add(gprMax.MagneticAveraging(mode='harmonic'))
scene.add(gprMax.DispersiveAveraging(enabled=True))

Material functions

Material

class gprMax.user_objects.cmds_multiuse.Material(**kwargs)
Specifies a material in the model described by a set of constitutive

parameters.

er

float required for the relative electric permittivity.

se

float required for the electric conductivity (Siemens/metre).

mr

float required for the relative magnetic permeability.

sm

float required for the magnetic loss.

id

string used as identifier for material. ‘+’ is reserved for internally generated material IDs; use ‘_’ in user-defined IDs.

User-defined material IDs must not contain +. It is reserved for averaged and other internally generated materials; use _ instead. This also applies to the optional id of gprMax.MaterialFromDatabase. Generated names stored by gprMax.GeometryObjectsWrite remain valid when restored with gprMax.GeometryObjectsRead.

Surface impedance

class gprMax.user_objects.cmds_multiuse.SurfaceImpedance(id: str, resistance: Optional[float] = None, *, preset: Optional[str] = None, conductivity: Optional[float] = None, fit_frequency_range: Optional[Tuple[float, float]] = None, fit_order: Union[str, int] = 'auto', fit_tolerance: float = 0.002, plot_fit: bool = False)

Define a reusable scalar passive surface impedance.

Assign its id directly as the material_id of a closed, cell-occupying geometry object. Sheet, edge, zero-thickness, and directional material_ids assignments are unsupported.

Select exactly one of a constant resistance, a named bulk-metal preset, or a user-supplied bulk-metal conductivity. Fitted models require fit_frequency_range=(fmin, fmax) and are converted internally to a passive Foster realization. fit_order='auto' tests increasing actual runtime pole counts and chooses the first deterministic local fit independently certified to reach fit_tolerance. An integer asks for exactly that many Foster poles. The constant-resistance form is an idealised broadband boundary rather than a complete physical material model and emits a warning when built. resistance=float('inf') selects the exact voxel-face PMC limit with zero tangential surface admittance. Passive impedance walls can continue uniformly along a PML absorption direction. Domain and CPU virtual-guide PML support heterogeneous isotropic retained hosts, including loss and electric dispersion, with each constituent uniformly extruded through the slab. Virtual apertures advance independent bulk polarization and surface-current histories with the coupled electric field.

Table 4 SurfaceImpedance arguments

Argument

Default

Meaning and constraints

id

Required

Unique material identifier used by geometry. Shares the bulk-material namespace; do not use + or the reserved __impedance_ prefix.

preset

None

Named thick-metal model. Choose exactly one of preset, conductivity, or resistance.

conductivity

None

Positive finite bulk conductivity in S/m, fitted to a good-conductor surface response. This is not a resistance in ohms.

resistance

None

Positive surface resistance in ohms, or positive infinity for exact voxel-face PMC. Zero, negative values, NaN, and negative infinity are invalid; use PEC for a zero-impedance wall.

fit_frequency_range

Required for a fit

Positive finite increasing (fmin, fmax) in Hz. Required with preset or conductivity; unavailable with resistance. Cover the excitation and modal solve range, including the discrete-frequency check explained in the guide.

fit_order

"auto"

Select the smallest runtime pole count from 1 through 64 meeting the tolerance. An integer from 1 through 64 requests that exact count; if the tolerance is not met, gprMax issues a warning rather than changing the count.

fit_tolerance

2e-3

Positive finite relative impedance-fit tolerance (0.2% by default). Automatic fitting must meet it; explicit-order fitting issues a warning if it is not met.

plot_fit

False

Write a fit PNG in geometry-only runs. True also writes it during full runs. Neither setting opens a window. Only available for fitted models.

Assign the ID to a closed, cell-occupying geometry object. For model choices, fit-band selection, geometry, and supported configurations, see Surface impedance and impedance volumes. The boundary equations are in Surface impedance: theory and validation; stored metadata is in Surface-impedance reproducibility metadata.

Material from database

class gprMax.user_objects.cmds_multiuse.MaterialFromDatabase(**kwargs)

Create a material from a versioned JSON material database.

Official databases are installed with gprMax. A user database is a JSON file named <database>.json beside the input file, or in the current working directory for a direct Python API model.

database

official or local database name.

material

material entry key in the database.

id

optional local material ID; defaults to material. This can provide a model-specific name, avoid a collision, or match the material name expected by imported geometry. ‘+’ is reserved for internally generated material IDs.

See Material databases for database lookup, schema, provenance, and geometry-file migration.

Material mass density

class gprMax.user_objects.cmds_multiuse.MaterialDensity(*, density, material_ids)

Assign a physical mass density to one or more existing materials.

Density is cell-centred metadata used by derived quantities such as SAR; it does not alter electromagnetic update coefficients or material averaging.

density

Finite mass density greater than zero in kg/m3.

material_ids

Material identifiers to which the density is assigned.

Mass density is optional cell-centred physical metadata in SI units (\(\mathrm{kg\,m^{-3}}\)). It does not alter electromagnetic update coefficients or dielectric smoothing. Derived dosimetry outputs require a finite, positive density for every selected material.

Material range

class gprMax.user_objects.cmds_multiuse.MaterialRange(**kwargs)

Creates varying material properties for stochastic models.

er_lower

float required for lower relative permittivity value.

er_upper

float required for upper relative permittivity value.

sigma_lower

float required for lower conductivity value.

sigma_upper

float required for upper conductivity value.

mr_lower

float required for lower relative magnetic permeability value.

mr_upper

float required for upper relative magnetic permeability value.

ro_lower

float required for lower magnetic loss value.

ro_upper

float required for upper magnetic loss value.

id

string used as identifier for this variable material.

MaterialRange defines bounded stochastic constitutive properties for a gprMax.FractalBox; it does not create a homogeneous material. See #material_range for how the sampled values are used.

Material list

class gprMax.user_objects.cmds_multiuse.MaterialList(**kwargs)

Creates varying material properties for stochastic models.

list_of_materials

list of material IDs

id

string used as identifier for this variable material.

MaterialList groups existing material IDs for stochastic assignment by a gprMax.FractalBox. See #material_list for the corresponding input-file command.

Debye Dispersion

class gprMax.user_objects.cmds_multiuse.AddDebyeDispersion(**kwargs)

Adds dispersive properties to already defined Material based on a multi-pole Debye formulation.

Perfect electric and magnetic conductors cannot be assigned electric dispersion, including custom materials with infinite electric or magnetic conductivity.

poles

float required for number of Debye poles.

er_delta

tuple required for difference between zero-frequency relative permittivity and relative permittivity at infinite frequency for each pole.

tau

tuple required for relaxation time (secs) for each pole.

material_ids

list required of material ids to apply dispersive properties.

Lorentz Dispersion

class gprMax.user_objects.cmds_multiuse.AddLorentzDispersion(**kwargs)

Adds dispersive properties to already defined Material based on a multi-pole Lorentz formulation.

Perfect electric and magnetic conductors cannot be assigned electric dispersion, including custom materials with infinite electric or magnetic conductivity.

poles

float required for number of Lorentz poles.

er_delta

tuple required for difference between zero-frequency relative permittivity and relative permittivity at infinite frequency for each pole.

omega

tuple required for resonance frequency (Hz) for each pole.

delta

tuple required for damping coefficient (per second) for each pole.

material_ids

list required of material ids to apply dispersive properties.

Drude Dispersion

class gprMax.user_objects.cmds_multiuse.AddDrudeDispersion(**kwargs)

Adds dispersive properties to already defined Material based on a multi-pole Drude formulation.

Perfect electric and magnetic conductors cannot be assigned electric dispersion, including custom materials with infinite electric or magnetic conductivity.

poles

float required for number of Drude poles.

omega

tuple required for plasma frequency (Hz) for each pole.

alpha

tuple required for inverse of relaxation time (per second) for each pole.

material_ids

list required of material ids to apply dispersive properties.

Material CRIM

class gprMax.user_objects.cmds_multiuse.MaterialCrim(**kwargs)

Mixing model based on the Complex Refractive Index Model (CRIM), combining a fixed-fraction non-dispersive matrix material with a single-pole Debye dispersive material (e.g. water or brine); the remaining volume fraction is assumed to be air.

matrix_id

string for ID of an existing non-dispersive material used for the fixed-fraction matrix (solid) phase.

matrix_fraction

float required for fixed volumetric fraction of the matrix phase.

dispersive_id

string for ID of an existing single-pole Debye material used for the dispersive phase.

fraction_lower

float required for lower boundary of the volumetric fraction of the dispersive phase.

fraction_upper

float required for upper boundary of the volumetric fraction of the dispersive phase.

f_min

float required for lower bound of the frequency range (Hz) used to fit the CRIM mixing curve.

f_max

float required for upper bound of the frequency range (Hz) used to fit the CRIM mixing curve.

a

float required for the CRIM shape factor.

id

string used as identifier for this CRIM mixing model.

See #material_crim for the CRIM mixing formula and how it is specialised to the matrix/dispersive-phase/air case used here.

The CRIM object, like the Peplinski object below, is a mixing model for a FractalBox, rather than a homogeneous material. It combines a fixed-fraction non-dispersive matrix material with a single-pole Debye dispersive material (assumed water or brine); the remaining volume fraction is assumed to be air. Both materials must already exist in the scene, and the dispersive material must have exactly one Debye pole. Conductivity is mixed separately by volume fraction as described for the hash command:

scene.add(gprMax.Material(er=5, se=0, mr=1, sm=0, id='sand'))

scene.add(gprMax.Material(er=4.9, se=0, mr=1, sm=0, id='water'))
scene.add(gprMax.AddDebyeDispersion(
    poles=1, er_delta=(73.3389,), tau=(8.0994e-12,),
    material_ids=('water',),
))

scene.add(gprMax.MaterialCrim(
    matrix_id='sand', matrix_fraction=0.6,
    dispersive_id='water', fraction_lower=0.02, fraction_upper=0.35,
    f_min=1e6, f_max=3e9, a=0.5,
    id='wetsand',
))

scene.add(gprMax.FractalBox(
    p1=(0, 0, 0), p2=(0.1, 0.1, 0.08),
    frac_dim=1.5, weighting=(1, 1, 1),
    n_materials=10, mixing_model_id='wetsand',
    id='my_fractal_box',
))

Soil Peplinski

class gprMax.user_objects.cmds_multiuse.SoilPeplinski(**kwargs)
Mixing model for soils proposed by Peplinski et al.

(http://dx.doi.org/10.1109/36.387598)

sand_fraction

float required for sand fraction of soil.

clay_fraction

float required for clay of soil.

bulk_density

float required for bulk density of soil (gm/cm^3).

sand_density

float required for density of sand particles in soil (gm/cm^3).

water_fraction_lower

float required for lower boundary of volumetric water fraction of the soil.

water_fraction_upper

float required for upper boundary of volumetric water fraction of the soil.

id

string used as identifier for soil.

The dispersion objects modify materials that have already been added to the same scene. Each material ID below is therefore unique. These objects describe electric dispersion and cannot target PEC or PMC materials, including custom materials with infinite electric or magnetic conductivity:

scene.add(gprMax.Material(
    er=6, se=0.01, mr=1, sm=0, id='half_space'
))

scene.add(gprMax.Material(
    er=4, se=0, mr=1, sm=0, id='debye_medium'
))
scene.add(gprMax.AddDebyeDispersion(
    poles=1, er_delta=(2.0,), tau=(1e-10,),
    material_ids=('debye_medium',),
))

scene.add(gprMax.Material(
    er=3, se=0, mr=1, sm=0, id='lorentz_medium'
))
scene.add(gprMax.AddLorentzDispersion(
    poles=1, er_delta=(2.0,), omega=(2e10,), delta=(5e9,),
    material_ids=('lorentz_medium',),
))

scene.add(gprMax.Material(
    er=1, se=0, mr=1, sm=0, id='drude_medium'
))
scene.add(gprMax.AddDrudeDispersion(
    poles=1, omega=(2e10,), alpha=(5e9,),
    material_ids=('drude_medium',),
))

The Peplinski object is a mixing model for a FractalBox, rather than a homogeneous material:

scene.add(gprMax.SoilPeplinski(
    sand_fraction=0.5, clay_fraction=0.2,
    bulk_density=2.0, sand_density=2.66,
    water_fraction_lower=0.05, water_fraction_upper=0.25,
    id='soil_mix',
))

Object construction functions

Object construction commands are processed in the order they appear in the scene. Therefore space in the model allocated to a specific material using for example the gprMax.user_objects.cmds_geometry.box.Box command can be reallocated to another material using the same or any other object construction command. Space in the model can be regarded as a canvas in which objects are introduced and one can be overlaid on top of the other overwriting its properties in order to produce the desired geometry. The object construction commands can therefore be used to create complex shapes and configurations.

Cell-centred geometry tags

The volumetric commands Box, Sphere, Cylinder, Cone, CylindricalSector, Ellipsoid, a Triangle with non-zero thickness, and FractalBox accept the optional keyword tag. A tag is semantic metadata independent of the electromagnetic material, for example tag='cranial_bone'. Reusing the same string on several primitives makes them one semantic region without retaining a list of the individual primitives.

Tags follow the same ordered overwrite semantics as geometry. A tagged primitive writes its tag to every cell it occupies; an untagged primitive writes tag ID zero and therefore clears an older tag in its cells. This makes constructive geometry work naturally. For example, an untagged free-space cylinder drawn inside a tagged material cylinder leaves a tagged shell and an untagged hollow interior. A free-space volume may itself be tagged when that region is intentionally significant. Dielectric smoothing does not alter tag membership.

scene.add(gprMax.Cylinder(
    p1=(0.10, 0.10, 0.05), p2=(0.10, 0.10, 0.15),
    r=0.04, material_id='plastic', tag='container',
))
scene.add(gprMax.Cylinder(
    p1=(0.10, 0.10, 0.05), p2=(0.10, 0.10, 0.15),
    r=0.03, material_id='free_space',
))

Tag ID zero is permanently reserved for untagged. The model stores one compact integer per cell only when at least one tag is present; tag arrays are not part of the FDTD field update and are not transferred to accelerators. Tags are flat rather than hierarchical. Larger groups such as head can be formed later by selecting several leaf tags such as brain, eyes, and cranial_bone.

Closed impedance volumes

Assign a gprMax.SurfaceImpedance ID directly as the material_id of a supported ordinary geometry object. No separate conversion object is needed. SurfaceImpedance defines the boundary response; assigning its ID to geometry creates an opaque impedance volume. Interior fields are excluded, not solved using a bulk conductivity. Even a one-cell-thick volume is opaque, not a transmissive sheet.

scene.add(gprMax.SurfaceImpedance(
    id='metal',
    preset='copper',
    fit_frequency_range=(8e9, 12e9),
))
scene.add(gprMax.Sphere(
    p1=(0.15, 0.10, 0.08), r=0.025,
    material_id='metal', averaging='n',
))

Use a scalar material_id and finite-volume geometry that occupies mesh cells. Sheets, lines, directional assignments, and dielectric smoothing are unavailable for this one-sided opaque boundary. Geometry follows declaration order: later objects overwrite earlier ones. The final voxel topology is checked after all cutouts. Tags remain optional metadata.

For supported primitives, PEC/PMC contacts, symmetry, 2D extrusion, clearance, and import/export limitations, see Surface impedance and impedance volumes. Uniform propagation through PML and virtual-guide apertures has additional host-material and coverage requirements in Waveguides and absorbers. Infinite surface resistance gives exact voxel-face PMC; built-in PMC volume geometry retains its legacy reflection-plane limitation.

The compiled boundary uses the same discrete surface response in FDTD and modal solves. The bulk modal operator uses Yee temporal and longitudinal difference symbols, while bulk dispersive poles still use their analytic physical-frequency response. See Surface impedance: theory and validation for the boundary reduction and Eigenmode ports: theory and numerical methods for modal operators.

Box

class gprMax.user_objects.cmds_geometry.box.Box(**kwargs)
Introduces an orthogonal parallelepiped with specific properties into

the model.

p1

list of the lower left (x,y,z) coordinates of the parallelepiped.

p2

list of the upper right (x,y,z) coordinates of the parallelepiped.

material_id

string for the material identifier that must correspond to material that has already been defined.

material_ids

list of material identifiers in the x, y, z directions.

averaging

string (y or n) used to switch on and off dielectric smoothing.

tag

optional semantic geometry-tag string written to occupied cells.

Cone

class gprMax.user_objects.cmds_geometry.cone.Cone(**kwargs)
Introduces a circular cone into the model. The difference with the cylinder is that the faces of the cone

can have different radii and one of them can be zero.

p1

list of the coordinates (x,y,z) of the centre of the first face of the cone.

p2

list of the coordinates (x,y,z) of the centre of the second face of the cone.

r1

float of the radius of the first face of the cone.

r2

float of the radius of the second face of the cone.

material_id

string for the material identifier that must correspond to material that has already been defined.

material_ids

list of material identifiers in the x, y, z directions.

averaging

string (y or n) used to switch on and off dielectric smoothing.

tag

optional semantic geometry-tag string written to occupied cells.

Cylinder

class gprMax.user_objects.cmds_geometry.cylinder.Cylinder(**kwargs)

Introduces a circular cylinder into the model.

p1

list of the coordinates (x,y,z) of the centre of the first face of the cylinder.

p2

list of the coordinates (x,y,z) of the centre of the second face of the cylinder.

r

float of the radius of the cylinder.

material_id

string for the material identifier that must correspond to material that has already been defined.

material_ids

list of material identifiers in the x, y, z directions.

averaging

string (y or n) used to switch on and off dielectric smoothing.

tag

optional semantic geometry-tag string written to occupied cells.

Cylindrical Sector

class gprMax.user_objects.cmds_geometry.cylindrical_sector.CylindricalSector(**kwargs)

Introduces a cylindrical sector (shaped like a slice of pie) into the model.

normal

string for the direction of the axis of the cylinder from which the sector is defined and can be x, y, or z.

ctr1

float for the first coordinate of the centre of the cylindrical sector.

ctr2

float for the second coordinate of the centre of the cylindrical sector.

extent1

float for the first thickness from the centre of the cylindrical sector.

extent2

float for the second thickness from the centre of the cylindrical sector.

r

float for the radius of the cylindrical sector.

start

float for the starting angle (in degrees) for the cylindrical sector.

end

float for the angle (in degrees) swept by the cylindrical sector.

material_id

string for the material identifier that must correspond to material that has already been defined.

material_ids

list of material identifiers in the x, y, z directions.

averaging

string (y or n) used to switch on and off dielectric smoothing.

tag

optional semantic geometry-tag string written to occupied cells.

Edge

class gprMax.user_objects.cmds_geometry.edge.Edge(**kwargs)

Introduces a wire with specific properties into the model.

p1

list of the coordinates (x,y,z) of the starting point of the edge.

p2

list of the coordinates (x,y,z) of the ending point of the edge.

material_id

string for the material identifier that must correspond to material that has already been defined.

Thin Wire

class gprMax.user_objects.cmds_geometry.thin_wire.ThinWire(**kwargs)

Introduces an axis-aligned PEC thin wire with physical radius a.

The logarithmic radius factor follows Umashankar, Taflove, and Beker (IEEE TAP, 1987, doi:10.1109/TAP.1987.1144000). The surrounding magnetic updates use the improved projected-field factors of Mäkinen, Juntunen, and Kivikoski (IEEE T-MTT, 2002). The charge-based open-end correction from the latter paper is not implemented.

Parameters
  • p1 – Coordinates of the start of the wire.

  • p2 – Coordinates of the end of the wire.

  • radius – Physical wire radius in metres.

scene.add(gprMax.ThinWire(
    p1=(0.10, 0.10, 0.02),
    p2=(0.10, 0.10, 0.12),
    radius=0.0001,
))

Magnetic Edge

class gprMax.user_objects.cmds_geometry.magnetic_edge.MagneticEdge(**kwargs)
Introduces a single magnetic (H) edge with specific properties into

the model - the magnetic dual of #edge.

p1

list of the coordinates (x,y,z) of the starting point of the edge.

p2

list of the coordinates (x,y,z) of the ending point of the edge.

material_id

string for the material identifier that must correspond to material that has already been defined.

scene.add(gprMax.MagneticEdge(
    p1=(0.5, 0.5, 0.5),
    p2=(0.7, 0.5, 0.5),
    material_id='pmc',
))

Ellipsoid

class gprMax.user_objects.cmds_geometry.ellipsoid.Ellipsoid(**kwargs)

Introduces an ellipsoidal object with specific parameters into the model.

p1

list of the coordinates (x,y,z) of the centre of the ellipsoid.

xr

float for x-semiaxis of the ellipsoid.

yr

float for y-semiaxis of the ellipsoid.

zr

float for z-semiaxis of the ellipsoid.

material_id

string for the material identifier that must correspond to material that has already been defined.

material_ids

list of material identifiers in the x, y, z directions.

averaging

string (y or n) used to switch on and off dielectric smoothing.

tag

optional semantic geometry-tag string written to occupied cells.

Plate

class gprMax.user_objects.cmds_geometry.plate.Plate(**kwargs)

Introduces a plate with specific properties into the model.

p1

list of the lower left (x,y,z) coordinates of the plate.

p2

list of the upper right (x,y,z) coordinates of the plate.

material_id

string for the material identifier that must correspond to material that has already been defined.

material_ids

two material identifiers for the plate’s tangential directions: y/z for a yz-plane plate, x/z for an xz-plane plate, or x/y for an xy-plane plate.

Sphere

class gprMax.user_objects.cmds_geometry.sphere.Sphere(**kwargs)

Introduces a spherical object with specific parameters into the model.

p1

list of the coordinates (x,y,z) of the centre of the sphere.

r

float of radius of the sphere.

material_id

string for the material identifier that must correspond to material that has already been defined.

material_ids

list of material identifiers in the x, y, z directions.

averaging

string (y or n) used to switch on and off dielectric smoothing.

tag

optional semantic geometry-tag string written to occupied cells.

Triangle

class gprMax.user_objects.cmds_geometry.triangle.Triangle(**kwargs)
Introduces a triangular patch or a triangular prism with specific

properties into the model.

p1

list of the coordinates (x,y,z) of the first apex of the triangle.

p2

list of the coordinates (x,y,z) of the second apex of the triangle.

p3

list of the coordinates (x,y,z) of the third apex of the triangle.

thickness

float for the thickness of the triangular prism. If the thickness is zero then a triangular patch is created.

material_id

string for the material identifier that must correspond to material that has already been defined.

material_ids

list of material identifiers in the x, y, z directions.

averaging

string (y or n) used to switch on and off dielectric smoothing.

tag

optional semantic geometry-tag string for a volumetric triangular prism. It is not accepted when thickness is zero.

The following compact examples show the required geometry keywords. The referenced material IDs must already exist; pec and pmc are built in.

scene.add(gprMax.Box(
    p1=(0, 0, 0), p2=(0.30, 0.20, 0.08), material_id='half_space'
))
scene.add(gprMax.Cone(
    p1=(0.05, 0.05, 0.08), p2=(0.05, 0.05, 0.13),
    r1=0.02, r2=0, material_id='pec',
))
scene.add(gprMax.Cylinder(
    p1=(0.15, 0.02, 0.05), p2=(0.15, 0.18, 0.05),
    r=0.01, material_id='pec',
))
scene.add(gprMax.CylindricalSector(
    normal='z', ctr1=0.15, ctr2=0.10,
    extent1=0.04, extent2=0.08, r=0.04,
    start=0, end=90, material_id='half_space',
))
scene.add(gprMax.Edge(
    p1=(0.10, 0.10, 0.02), p2=(0.10, 0.10, 0.12),
    material_id='pec',
))
scene.add(gprMax.ThinWire(
    p1=(0.12, 0.10, 0.02), p2=(0.12, 0.10, 0.12),
    radius=0.0001,
))
scene.add(gprMax.Ellipsoid(
    p1=(0.20, 0.10, 0.08), xr=0.03, yr=0.02, zr=0.01,
    material_id='half_space',
))
scene.add(gprMax.Plate(
    p1=(0.04, 0.04, 0.06), p2=(0.10, 0.10, 0.06),
    material_id='pec',
))
scene.add(gprMax.Sphere(
    p1=(0.24, 0.10, 0.08), r=0.015, material_id='pec'
))
scene.add(gprMax.Triangle(
    p1=(0.04, 0.04, 0.04), p2=(0.10, 0.04, 0.04),
    p3=(0.04, 0.10, 0.04), thickness=0.01,
    material_id='half_space',
))

Fractal Box

class gprMax.user_objects.cmds_geometry.fractal_box.FractalBox(**kwargs)
Introduces an orthogonal parallelepiped with fractal distributed

properties which are related to a mixing model or normal material into the model.

p1

list of the lower left (x,y,z) coordinates of the parallelepiped.

p2

list of the upper right (x,y,z) coordinates of the parallelepiped.

frac_dim

float for the fractal dimension which, for an orthogonal parallelepiped, should take values between zero and three.

weighting

list of the weightings in the x, y, z direction of the parallelepiped.

n_materials

int of the number of materials to use for the fractal distribution (defined according to the associated mixing model). This should be set to one if using a normal material instead of a mixing model.

mixing_model_id

string identifier for the associated mixing model or material. Several boxes may share a mixing model; each box uses its own n_materials bin mapping.

id

string identifier for the fractal box itself.

seed

(optional) float parameter which controls the seeding of the random number generator used to create the fractals.

averaging

string (y or n) used to switch on and off dielectric smoothing.

tag

optional semantic geometry-tag string written to the final cells occupied by the fractal volume.

Fractal definitions can be reused for a geometry preview followed by a solve, or in multiple rebuilt Scenes. Each fresh grid gets its own fractal volume and material-bin mapping. Supply a seed for reproducible geometry. With geometry_fixed=True, the already-built geometry is retained instead.

Note

  • We are not aware of a formulation of Perfectly Matched Layer (PML) absorbing boundary that can specifically handle distributions of material properties (such as those created by fractals) throughout the thickness of the PML, i.e. this is a required area of research. Our PML formulations can work to an extent depending on your modelling scenario and requirements. You may need to increase the thickness of the PML and/or consider tuning the parameters of the PML (PML functions) to improve performance for your specific model.

Add Grass

class gprMax.user_objects.cmds_geometry.add_grass.AddGrass(**kwargs)

Adds grass with roots to a FractalBox class in the model.

p1

list of the lower left (x,y,z) coordinates of a surface on a FractalBox class.

p2

list of the upper right (x,y,z) coordinates of a surface on a FractalBox class.

frac_dim

float for the fractal dimension which, for an orthogonal parallelepiped, should take values between zero and three.

limits

list to define lower and upper limits for a range over which the height of the blades of grass can vary.

n_blades

int for the number of blades of grass that should be applied to the surface area.

fractal_box_id

string identifier for the FractalBox class that the grass should be applied to.

seed

optional integer used to seed the random number generator. If omitted, a different distribution is generated on each run.

Add Surface Roughness

class gprMax.user_objects.cmds_geometry.add_surface_roughness.AddSurfaceRoughness(**kwargs)

Adds surface roughness to a FractalBox class in the model.

p1

list of the lower left (x,y,z) coordinates of a surface on a FractalBox class.

p2

list of the upper right (x,y,z) coordinates of a surface on a FractalBox class.

frac_dim

float for the fractal dimension which, for an orthogonal parallelepiped, should take values between zero and three.

weighting

list with weightings in the first and second direction of the surface.

limits

list to define lower and upper limits for a range over which the surface roughness can vary.

fractal_box_id

string identifier for the FractalBox class that the surface roughness should be applied to.

seed

(optional) float parameter which controls the seeding of the random number generator used to create the fractals.

Add Surface Water

class gprMax.user_objects.cmds_geometry.add_surface_water.AddSurfaceWater(**kwargs)

Adds surface water to a FractalBox class in the model.

p1

list of the lower left (x,y,z) coordinates of a surface on a FractalBox class.

p2

list of the upper right (x,y,z) coordinates of a surface on a FractalBox class.

depth

float that defines the depth of the water, which should be specified relative to the dimensions of the #fractal_box that the surface water is being applied.

fractal_box_id

string identifier for the FractalBox class that the surface water should be applied to.

A fractal volume can use either a normal material or a mixing model such as soil_mix from the material example above. Surface modifiers refer to the fractal box by its ID and must be added after it:

scene.add(gprMax.FractalBox(
    p1=(0, 0, 0), p2=(0.30, 0.20, 0.10),
    frac_dim=1.5, weighting=(1, 1, 1), n_materials=20,
    mixing_model_id='soil_mix', id='ground', seed=1, tag='soil_layer_1',
))
scene.add(gprMax.AddSurfaceRoughness(
    p1=(0, 0, 0.10), p2=(0.30, 0.20, 0.10),
    frac_dim=1.5, weighting=(1, 1), limits=(0.08, 0.12),
    fractal_box_id='ground', seed=1,
))
scene.add(gprMax.AddSurfaceWater(
    p1=(0, 0, 0.10), p2=(0.30, 0.20, 0.10),
    depth=0.105, fractal_box_id='ground',
))
scene.add(gprMax.AddGrass(
    p1=(0, 0, 0.10), p2=(0.30, 0.20, 0.10),
    frac_dim=1.5, limits=(0.01, 0.03), n_blades=100,
    fractal_box_id='ground', seed=1,
))

Geometry View

class gprMax.user_objects.cmds_output.GeometryView(p1: Tuple[float, float, float], p2: Tuple[float, float, float], dl: Tuple[float, float, float], output_type: str, filename: str)

Outputs to file(s) information about the geometry (mesh) of model.

The geometry information is saved in Visual Toolkit (VTK) formats.

p1

tuple required for lower left (x,y,z) coordinates of volume of geometry view in metres.

p2

tuple required for upper right (x,y,z) coordinates of volume of geometry view in metres.

dl

tuple required for spatial discretisation of geometry view in metres.

output_type

string required for per-cell ‘n’ (normal) or per-cell-edge ‘f’ (fine) geometry views.

filename

string required for filename where geometry view will be stored in the same directory as input file.

Geometry Objects Read

class gprMax.user_objects.cmds_geometry.geometry_objects_read.GeometryObjectsRead(**kwargs)

Allows you to insert pre-defined geometry into a model.

The geometry is specified using integer arrays in an HDF5 file. These files contain /material_keys which map their compact integer indices to a versioned JSON material database. Convert legacy HDF5/text pairs first with python -m gprMax.toolboxes.MaterialDatabase convert-geometry.

p1

list of lower left (x,y,z) coordinates in the domain where the lower left corner of the geometry array should be placed.

geofile

string path to and filename of the HDF5 file that contains an integer array which defines the geometry.

material_database

database name, without .json, for a keyed geometry file. The JSON file must be beside the HDF5 file.

averaging

optional "y"/"n" flag controlling interface averaging when a voxel-only file is reconstructed. The default is "n". Files containing complete /ID, /rigidE, and /rigidH arrays are authoritative and ignore this option.

The matfile argument is no longer supported. To reuse an old HDF5 geometry with text material commands, follow Migrating legacy geometry and text materials, then use geofile="geometry_converted.h5", material_database="geometry_materials". The insertion coordinates and averaging option remain unchanged.

Warning

Geometry files and simulation results both use .h5. For a single run, model.in produces model.h5 by default. If the geometry input is that same file, saving the simulation results overwrites it without confirmation. Importing the geometry does not protect the original file from later output writes.

Use distinct paths for geometry and results: for example, name the geometry file model_geometry.h5, choose a separate output directory, or set a different result basename with -o on the command line or outputfile in the Python API. The same precaution applies when choosing a geometry-export filename.

Geometry Objects Write

class gprMax.user_objects.cmds_output.GeometryObjectsWrite(p1: Tuple[float, float, float], p2: Tuple[float, float, float], filename: str)
Writes geometry generated in a model to file which can be imported into

other models.

p1

tuple required for lower left (x,y,z) coordinates of volume of output in metres.

p2

tuple required for upper right (x,y,z) coordinates of volume of output in metres.

filename

string required for filename where output will be stored in the same directory as input file.

Geometry views are visualisations. Geometry-object files instead preserve material-index geometry for insertion into another model. If semantic tags exist, both outputs also preserve their cell IDs and the ID-to-name table. This permits a costly tagged anatomy or geological model to be written once and inserted into later simulations without rebuilding it:

scene.add(gprMax.GeometryView(
    p1=(0, 0, 0), p2=(0.30, 0.20, 0.15),
    dl=(0.002, 0.002, 0.002),
    filename='model_geometry', output_type='n',
))
scene.add(gprMax.GeometryObjectsWrite(
    p1=(0, 0, 0), p2=(0.30, 0.20, 0.15),
    filename='reusable_geometry',
))

A geometry view can be added to an HSG subgrid. Its points and VTK origin are written in the global model coordinate system, even though its material data are sampled from the fine local grid. GeometryObjectsWrite remains a main-grid operation.

In a different scene, the saved geometry can be inserted at a chosen origin:

scene.add(gprMax.GeometryObjectsRead(
    p1=(0.05, 0.05, 0),
    geofile='reusable_geometry.h5',
    material_database='reusable_geometry_materials',
    averaging='y',
))

averaging='y' reconstructs a voxel-only file with dielectric interface averaging; its backward-compatible default is 'n'. If the file contains complete /ID, /rigidE, and /rigidH arrays, those Yee-component arrays are authoritative and the option is ignored. Dispersive tissue interfaces additionally require gprMax.DispersiveAveraging(enabled=True). Material density and cell-centred geometry tags remain discrete per cell.

Source and output functions

Point-source positions and boundary components

After coordinates are rounded to grid indices, a Hertzian dipole, voltage source, or transmission line must lie on a physical electric component; a magnetic dipole must lie on a physical magnetic component. For a grid with nx, ny, and nz cells, the inclusive index ranges are:

Table 5 Physical component indices

Component

x index

y index

z index

Ex

0 .. nx-1

0 .. ny

0 .. nz

Ey

0 .. nx

0 .. ny-1

0 .. nz

Ez

0 .. nx

0 .. ny

0 .. nz-1

Hx

0 .. nx

0 .. ny-1

0 .. nz-1

Hy

0 .. nx-1

0 .. ny

0 .. nz-1

Hz

0 .. nx-1

0 .. ny-1

0 .. nz

For example, Ex[i,j,k] is located at ((i+0.5)*dx, j*dy, k*dz). Its last physical x index is nx-1, although the field array also contains a padded entry at nx. By contrast, its transverse terminal indices j=ny and k=nz remain valid. The boundary condition still determines which components can be excited there; the bounds check does not change PEC or PMC enforcement.

The checks also apply to dipole positions reached through SrcSteps or a study. In 2D, sources must remain on the active invariant layer (index 0 for TM, index 1 for TE). MPI checks use the global physical grid, not the edge of an individual rank. Subgrid checks use the translated local fine-grid indices. Receiver and extended-source bounds are separate.

Sources sharing a field component

Hertzian dipoles or magnetic dipoles assigned to the same discretised Yee component add their contributions in source-list order. This also applies when different physical coordinates round to the same component. Different polarisations address different component arrays.

CPU and accelerator solvers use the same conventional-source ordering: voltage sources, then transmission lines, then Hertzian dipoles for the electric update; magnetic dipoles precede magnetic frills for the magnetic update. Within each family, input order is preserved. The accelerator kernels process ordinary source lists and transmission-line electric updates sequentially, so coincident writes do not race.

Not every source adds to the field. An active hard voltage source or transmission line assigns its electric component; a later assignment to that component takes precedence. This reproduces the CPU update rules, not a combined circuit model of several feeds sharing one terminal. Existing restrictions on overlapping magnetic-frill stencils and duplicate network-terminal edges still apply.

Waveform

class gprMax.user_objects.cmds_multiuse.Waveform(**kwargs)

Create waveform to use with sources in the model.

wave_type

Waveform type. One of ‘gaussian’, ‘gauspulse’, ‘gaussiandot’, ‘gaussiandotnorm’, ‘gaussiandotdot’, ‘gaussiandotdotnorm’, ‘ricker’, ‘gaussianprime’, ‘gaussiandoubleprime’, ‘sine’, ‘contsine’, ‘impulse’, or ‘user’. The continuous sine ramps over four cycles. An impulse is one nonzero sample on the source’s update lattice, not a unit-area continuous-time delta; its required positive frequency is unused. The Gaussian-modulated cosine ‘gauspulse’ uses fractional bandwidth 0.5 at -6 dB, matching MATLAB gauspuls defaults, and is delayed to start at the -60 dB envelope level. See gauspulse (modulated Gaussian).

Type

str

amp

Factor to scale the maximum amplitude of the waveform by. (For a #hertzian_dipole the units will be Amps, for a #voltage_source or #transmission_line the units will be Volts).

Type

float

freq

Centre frequency (Hz) of the waveform. In the case of the Gaussian waveform it is related to the pulse width.

id

Identifier of the waveform.

Type

str

user_values

Optional 1D array of amplitude values to use with user waveform.

user_time

Optional 1D array of time values to use with user waveform.

kind

Optional string or int, see scipy.interpolate.interp1d - https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html#scipy-interpolate-interp1d

Type

int | str | None

fill_value

Optional array or ‘extrapolate’, see scipy.interpolate.interp1d - https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html#scipy-interpolate-interp1d

The constructor has three forms. Arguments should be supplied by keyword:

# Built-in analytic waveform
gprMax.Waveform(wave_type='ricker', amp=1, freq=1e9, id='pulse')

# MATLAB-default Gaussian-modulated cosine (50% bandwidth at -6 dB)
gprMax.Waveform(wave_type='gauspulse', amp=1, freq=1e9, id='rf_pulse')

# User-defined Python function
gprMax.Waveform(wave_type='user', user_func=function, id='pulse')

# User-defined sample arrays
gprMax.Waveform(
    wave_type='user', user_values=values, user_time=times,
    kind='linear', fill_value=0, id='pulse'
)

The callable keyword is user_func (not usr_func). It provides a bespoke waveform shape as an alternative to the user_values/user_time sample arrays and is available only through the Python API because a function object cannot be represented in a text input file. The callable must accept one scalar time in seconds and return the complete numeric amplitude at that time; amp and freq are therefore not required for wave_type='user'.

For example, the following creates a callable waveform and assigns it to a Hertzian dipole:

import numpy as np
import gprMax

def my_waveform(time):
    return np.sin(2 * np.pi * 1e9 * time) * np.exp(-time / 1e-9)

scene.add(gprMax.Waveform(wave_type='user', user_func=my_waveform, id='mywave'))
scene.add(gprMax.HertzianDipole(
    p1=(0.05, 0.05, 0.05), polarisation='z', waveform_id='mywave'
))

gprMax first calls the function at \(t=0\) to check its signature and return type. It is then sampled at the whole and/or half time steps required by each source while the source arrays are prepared. It is never called from the FDTD time-stepping loop, although a computationally expensive function can increase model setup time. Imports used by the function must be available in the scope where the function is defined, following normal Python name resolution.

The callable can also be a closure, which is a convenient way to generate a family of related waveforms without duplicating code:

def make_waveform(freq, decay):
    def waveform(time):
        return np.sin(2 * np.pi * freq * time) * np.exp(-time / decay)
    return waveform

for i, freq in enumerate([0.5e9, 1e9, 2e9, 4e9]):
    scene.add(gprMax.Waveform(
        wave_type='user', user_func=make_waveform(freq, decay=1e-9), id=f'wave_{i}'
    ))

Exactly one of user_func and user_values must be supplied. When user_values is used without user_time, gprMax associates the samples with exactly iterations times, arange(iterations) * dt. The number of values must match; supply an explicit, strictly increasing user_time for another sampling grid. Sampled waveforms default to linear interpolation inside the supplied time axis and zero outside it. kind and fill_value override those defaults (including explicit 'extrapolate') and apply only to sampled waveforms. Numeric fill values are honoured outside both ends of the time axis. Callables retain their own boundary behaviour. User-defined waveforms can drive local Hertzian or magnetic dipoles, voltage sources, transmission lines, and magnetic-frill sources. The discrete-plane-wave formulation currently requires a built-in analytic waveform.

Hard voltage sources evaluate only whole-step samples, including the initial electric field at time zero. Resistive voltage sources and Hertzian dipoles use their existing half-step current samples; a final half-step outside a sampled waveform’s time axis uses the selected fill value. Zero waveform amplitude does not remove an active hard-source clamp: its start/stop window still controls whether the electric edge is prescribed.

For a Gaussian envelope with a cosine carrier, see the modulated Gaussian waveform example. It includes the built-in gauspulse definition, time trace, power spectrum, intrinsic delay, and a custom-bandwidth callable. The built-in pulse uses MATLAB’s default fractional bandwidth of 0.5 at -6 dB and starts at the -60 dB envelope level; its peak occurs about 2.781 / freq seconds after the source start time.

Eigenmode band, ports, excitation, and virtual guides

class gprMax.user_objects.cmds_multiuse.EigenmodeBand(**kwargs)

Define the frequency band and optional extra DFT bins shared by all ports.

points selects equally spaced output frequencies from fmin to fmax, including both endpoints. frequencies adds specific values within that range. The combined list is sorted from low to high, with repeated values included only once.

id

unique identifier for the shared eigenmode band.

fmin

lower direct-DFT frequency in Hz.

fmax

upper direct-DFT frequency in Hz.

points

number of equally spaced output frequencies, including both endpoints.

frequencies

optional additional direct-DFT frequencies in Hz. Values must lie between fmin and fmax inclusive and are added to the equally spaced frequencies selected by points.

transition

positive transition width in Hz for an automatically generated bandpass waveform, or "auto" (default).

spectral_threshold

relative spectral threshold used to determine significant excitation support. The default is 1e-3.

Argument

Default

Meaning and constraints

id

Required

Non-empty identifier without whitespace. Exactly one band is allowed per grid.

fmin, fmax

Required

Positive, finite frequency limits with fmax >= fmin.

points

Required

Number of equally spaced output frequencies from fmin to fmax, including both endpoints. Use one point when the limits are equal and at least two when they differ.

frequencies

No extra frequencies

Extra output frequencies in Hz, between fmin and fmax inclusive. Supply one number or a sequence. These are added to the frequencies selected by points, then sorted from low to high; repeated values appear only once.

transition

"auto"

Automatic pulse transition width, or a positive finite width in Hz.

spectral_threshold

1e-3

Relative amplitude threshold for significant waveform spectral support; strictly between zero and one.

class gprMax.user_objects.cmds_multiuse.EigenmodePort(**kwargs)

Define a modal port plane with its own anchor-frequency policy.

port

positive, one-based port number.

p1

first physical (x, y, z) corner of the port plane.

p2

opposite physical (x, y, z) corner of the port plane.

direction

launch/monitor direction normal to the plane, + or -.

modes

positive integer mode count or an increasing sequence of one-based mode indices.

anchors

"auto" (default), one frequency, or an increasing sequence of modal-solve anchor frequencies in Hz.

plot_fields

optionally force or suppress modal-field plots. None retains the geometry-only default.

degenerate

optional legacy mode pair or disjoint groups tracked as subspaces; ignored when tracking="auto".

mode_polarizations

optional global transverse E axis or real three-vector shared by the first member of every resolved doublet; the second uses positive port normal cross that direction. Automatic tracking resolves subgroups before polarization; legacy tracking uses declared pairs. Shared two-direction sequences in both trackers and mode/group mappings in legacy tracking emit a deprecation warning and will be removed in gprMax 5.0. Explicit pairs retain both signs and valid nonorthogonal directions. None preserves the current assignment. Physical selection requires a 3D cross-section and independent transverse directions.

tracking

"legacy" (default) or experimental "auto" branch tracking, including automatic symmetry detection and selection for eligible degenerate spans.

verification

"full" or "fast" quality checks for automatic tracking.

tracking_config

optional gprMax.EigenmodeTrackingConfig or mapping.

Port and outputs

EigenmodePort / #eigenmode_port defines both the modal basis and monitor; EigenmodeExcitation selects what is driven. Each numbered port writes /eigenmode_ports/portN (under /subgrids/<subgrid ID> for an owning HSG grid). frequency, incident and outgoing are modal spectra, not lumped voltage/current histories. Follow ModeIndices and the stored power matrices; do not assign an arbitrary 50 Ohm circuit reference.

With one active channel, per-port S arrays describe responses to that channel. Multiple simultaneous drives produce active reflection data, not a complete S matrix; fully passive virtual ports produce raw spectra without S. Use coefficient_valid_S for coefficient inspection and power_wave_valid_S for propagating-power interpretation. Below-cutoff coefficients must not be interpreted as transmitted power.

Use the modal plots and post-processing examples in Eigenmode ports and S-parameter analysis, not the conventional plot_port tool. See Eigenmode-port and S-parameter output for shapes and files and Eigenmode-port studies and array synthesis for complete matrices and array synthesis. Ports must satisfy the owning grid/backend restrictions in the eigenmode guide.

Ports using automatic tracking also write a versioned mode_tracking group. It contains original candidate mappings, overlaps, residuals, requested and adaptive frequencies, and per-anchor confinement/artifact diagnostics. These quality records do not alter the established anchor_mode_valid or power-wave masks; they explain warnings about anchors that remain in use.

Argument

Default

Meaning and constraints

port

Required

Unique positive, one-based port number within the grid.

p1, p2

Required

Opposite physical corners of an axis-aligned aperture. Exactly one finite coordinate must match, defining its normal; the invariant 2D axis is ignored in that test.

direction

Required

"+" or "-" along that normal. Point both port directions into the device for the usual S convention.

modes

Required

An increasing sequence of unique positive mode indices, or a positive integer mode count. modes=2 means modes 1 and 2; modes=(2,) means only mode 2.

anchors

"auto"

One modal solve frequency, an increasing sequence of positive finite frequencies, or automatic candidate selection.

plot_fields

None

True forces modal-field PNGs, False suppresses them. None enables them only in geometry-only runs.

tracking

"legacy"

Experimental "auto" enables branch assignment, automatic degeneracy detection, symmetry-based basis selection, and mode-quality diagnostics. "legacy" preserves ordering, solve counts, and manual grouping and skips symmetry selection. See Eigenmode tracking guide for tracking setup and examples.

verification

"full"

Applies only with automatic tracking. "full" requests mesh and open-window checks; "fast" uses residual and artificial-edge diagnostics without verification solves.

tracking_config

None

Optional gprMax.EigenmodeTrackingConfig or mapping of thresholds and solve limits. The complete parameter table is in Eigenmode tracking guide.

degenerate

None

Legacy tracking only: a group such as (1, 2), or disjoint groups such as ((1, 2), (3, 4)). All members must be in modes. With tracking="auto", this argument is ignored and groups are detected from the solved spectrum.

mode_polarizations

None

Optional single transverse axis ("x", "y", "z") or finite nonzero real three-vector. The orthogonal partner is inferred from the positive port normal. Automatic tracking resolves subgroups before applying the direction. Shared direction pairs in both trackers and mode/group mappings in legacy tracking remain available with a deprecation warning until their removal in gprMax 5.0. Explicit pairs preserve both directions and signs, including valid nonorthogonal directions. See Eigenmode tracking guide for migration examples.

class gprMax.user_objects.cmds_multiuse.EigenmodeExcitation(**kwargs)

Attach one active modal drive to a defined port.

Several excitations may share the same base waveform and drive different port/mode channels. Each physical port is solved and monitored only once; its additional drives reuse the cached modal anchor bank.

Parameters
  • port – One-based EigenmodePort number.

  • mode – One of the modes monitored by that port.

  • waveform – Shared waveform ID, or 'auto' for the bandpass pulse.

  • amplitude – Non-zero modal amplitude scale. Mutually exclusive with power.

  • power – Optional relative incident-power scale; applies amplitude sqrt(power).

  • phase_deg – Constant spectral phase in degrees.

  • delay_s – True time delay in seconds, applied as exp(-1j * 2*pi*f*delay_s).

  • plot_waveform – Write or suppress this drive’s waveform/DFT plot. The default writes it only for geometry-only runs.

Port and outputs

EigenmodePort / #eigenmode_port defines both the modal basis and monitor; EigenmodeExcitation selects what is driven. Each numbered port writes /eigenmode_ports/portN (under /subgrids/<subgrid ID> for an owning HSG grid). frequency, incident and outgoing are modal spectra, not lumped voltage/current histories. Follow ModeIndices and the stored power matrices; do not assign an arbitrary 50 Ohm circuit reference.

With one active channel, per-port S arrays describe responses to that channel. Multiple simultaneous drives produce active reflection data, not a complete S matrix; fully passive virtual ports produce raw spectra without S. Use coefficient_valid_S for coefficient inspection and power_wave_valid_S for propagating-power interpretation. Below-cutoff coefficients must not be interpreted as transmitted power.

Use the modal plots and post-processing examples in Eigenmode ports and S-parameter analysis, not the conventional plot_port tool. See Eigenmode-port and S-parameter output for shapes and files and Eigenmode-port studies and array synthesis for complete matrices and array synthesis. Ports must satisfy the owning grid/backend restrictions in the eigenmode guide.

Ports using automatic tracking also write a versioned mode_tracking group. It contains original candidate mappings, overlaps, residuals, requested and adaptive frequencies, and per-anchor confinement/artifact diagnostics. These quality records do not alter the established anchor_mode_valid or power-wave masks; they explain warnings about anchors that remain in use.

Argument

Default

Meaning and constraints

port, mode

Required

Existing port and one of its monitored modes. Each driven channel must be unique.

waveform

"auto"

Band-adapted finite pulse, or the ID of an explicitly added gprMax.Waveform. Simultaneous drives must share one base waveform.

amplitude

1.0

Finite nonzero modal-amplitude scale. Omit the excitation to make a channel passive.

power

Not set

Positive finite relative incident-power scale, applying amplitude sqrt(power). Mutually exclusive with amplitude; it scales the base pulse rather than specifying a constant time-domain power.

phase_deg

0.0

Finite constant spectral phase in degrees.

delay_s

0.0

Finite true time delay in seconds, applied as exp(-1j*2*pi*f*delay_s). Ensure the shifted pulse fits the time window.

plot_waveform

None

Force or suppress the waveform/spectrum PNG with True or False; None enables it only for geometry-only runs.

class gprMax.user_objects.cmds_multiuse.VirtualWaveguide(port, length_cells=30, pml_cells=12, source_clearance_cells=6, pml_profile=None)

Terminate an eigenmode port with a bidirectionally coupled FDTD guide.

The guide repeats the material cross-section at the referenced EigenmodePort, couples both E and H fields at its aperture, and terminates its remote end with a PML. It can contain the active modal source or act as a passive matched receiver.

Parameters
  • port – One-based eigenmode-port number.

  • length_cells – Total virtual-guide length in cells. Default 30.

  • pml_cells – PML thickness at the remote end. Default 12.

  • source_clearance_cells – Cells between an active internal source plane and the PML. Default 6.

  • pml_profile – Optional reusable PML profile ID.

Argument

Default

Meaning and constraints

port

Required

Positive, one-based number of an existing EigenmodePort in the same grid. Attach at most one virtual guide per port.

length_cells

30

Total auxiliary-guide length, including its PML and source clearance. A positive integer, at least pml_cells + source_clearance_cells + 3.

pml_cells

12

Thickness of the auxiliary guide’s absorbing layer at the end away from the port. An integer of at least 2 cells.

source_clearance_cells

6

Distance between the internal source plane and the start of that PML. A positive integer number of cells, at least 1.

pml_profile

None

Name of an existing reusable PML profile. None uses the global PML formulation and absorption settings (CFS terms).

class gprMax.user_objects.cmds_output.EigenmodeFieldOutput(filename='port_modes', ports=())

Export tracked modal E/H bases during a serial 3D model build.

filename is a basename in the run output directory; ports defaults to all prepared physical ports. No field time stepping is required.

Use scene.add(gprMax.EigenmodeFieldOutput(filename="bank", ports=(1, 2))) to export prepared tracked modal profiles to bank.modes.h5 in the run output directory, including during geometry-only runs. Omit ports to export all prepared physical ports; filename defaults to port_modes. Export requires a serial 3D main grid. The equivalent hash command and output restrictions are described under #eigenmode_field_output:.

A band selects output frequencies; a port defines a reference plane and monitored modes; an excitation drives a channel; a virtual guide provides a separate matched continuation. The full workflows and equivalent hash commands are in Eigenmode ports and S-parameter analysis. See Model Building for the complete command syntax, Model Output for stored arrays, and Eigenmode ports: theory and numerical methods for numerical methods.

A direct eigenmode model may also be placed wholly inside one HSG subgrid. Add its band, ports, waveform (when one is used), and excitation to that same subgrid object. With autotranslate=True, the plane coordinates remain global physical coordinates:

fine_grid.add(gprMax.Waveform(
    wave_type='contsine', amp=1, freq=22e9, id='fine_wave',
))
fine_grid.add(gprMax.EigenmodeBand(
    id='fine_band', fmin=22e9, fmax=22e9, points=1,
))
fine_grid.add(gprMax.EigenmodePort(
    port=1,
    p1=(0.045, 0.039, 0.039),
    p2=(0.045, 0.051, 0.049),
    direction='+', modes=(1,), anchors=(22e9,),
))
fine_grid.add(gprMax.EigenmodeExcitation(
    port=1, mode=1, waveform='fine_wave',
))

The FDFD solver samples the final component-resolved material slice of the fine Yee grid and uses its two transverse spatial steps. Injection and modal observation then run at every fine-grid time step. The complete plane and its adjacent staggered Yee stencil must lie strictly inside the subgrid working region; a plane touching the HSG coupling surface or entering its auxiliary or PML region is rejected. Ports cannot be divided between the main grid and a subgrid, or between different subgrids. VirtualWaveguide can be added to the same subgrid and referenced to one of its ports. The auxiliary guide then inherits the fine grid’s spatial and temporal steps, material cross-section, update coefficients, and iteration count; it is not resampled from the coarse main grid.

Voltage Source

class gprMax.user_objects.cmds_multiuse.VoltageSource(p1: Tuple[float, float, float], polarisation: str, resistance: float, waveform_id: str, start: Optional[float] = None, stop: Optional[float] = None, id: Optional[str] = None, spectrum_limit=10.0, reference_impedance: Optional[float] = None)

Specifies a voltage source at an electric field location.

In a 3-D model the source also owns an automatic terminal monitor. A hard source on a domain-minimum transverse boundary remains a valid source, but cannot provide that port output because its complete Ampere loop lies partly outside the grid.

polarisation

string required for polarisation of the source x, y, z.

p1

tuple required for position of source x, y, z.

resistance

float required for internal resistance (Ohms) of voltage source.

waveform_id

string required for identifier of waveform used with source.

start

float optional to delay start time (secs) of source.

stop

float optional to time (secs) to remove source.

id

optional HDF5 port identifier. If omitted, portN is assigned.

spectrum_limit

minimum cells per shortest material wavelength (default 10), or "nyquist" for the full research spectrum.

reference_impedance

float optional wave-reference impedance (Ohms) for a hard source. The default is 50 Ohms. This final argument is not accepted for a finite-resistance source, whose physical resistance is also its wave-reference impedance.

property result

Return the automatic port result after the model has solved.

Port and outputs

In a supported 3-D model this source automatically writes /ports/<id> (default ID portN); no extra Rx is needed. A CPU or CUDA HSG port is below /subgrids/<subgrid ID>/ports/<id>. A hard source on a domain-minimum transverse boundary remains an excitation but has no automatic port because its full magnetic-current loop is unavailable. Reduced 2-D sources do not provide this port output.

Vtotal is gap voltage in volts, with sign opposite to the electric-field component along the source edge. Use its stored time axis. Finite-resistance ports use half-step-aligned voltage histories; hard ports retain voltage at time zero and a separately staggered Iloop history on time_current. frequency, S11, Zin and Yin are the corrected terminal spectra, with separate valid_S11, valid_Zin and valid_Yin masks.

The Python source’s result provides time, total_voltage, frequency, s11, zin, yin and their lowercase validity masks after solving. The finite source’s resistance is its reference impedance; a hard source’s independent reference impedance defaults to 50 Ohms. A zero-amplitude finite-resistance source remains a passive load and reports received voltage, but its own source-normalised S11/impedance is not a valid input characterisation. Zero amplitude with zero resistance is a clamp.

python -m gprMax.toolboxes.Plotting.plot_port model.h5 --port feed --validity --save

Replace feed with the actual ID. See Voltage-port equations and timing for correction, timing and support details, Voltage-source S11 and impedance output for the schema, and Finite-resistance voltage-port studies for a complete multiport matrix.

A finite-resistance voltage source is a one-cell Thévenin gap source. It also acts as a port: after a supported 3-D simulation, source.result provides the sampled gap voltage together with the frequency-domain port quantities. Its physical resistance is the wave-reference impedance. A zero resistance creates a hard source with an independent wave-reference impedance: reference_impedance defaults to 50 Ohms and does not add a physical resistance to the gap.

feed = gprMax.VoltageSource(
    p1=(0.05, 0.05, 0.02),
    polarisation='z',
    resistance=50,
    waveform_id='pulse',
    id='feed',
)
scene.add(feed)

The waveform amplitude is the generator voltage in volts. See #voltage_source for the one-cell source equation and the definition of the automatic port spectra.

A hard source also prescribes the initial electric field before sample zero is stored. Subsequent prescriptions use the new electric time level \((n+1)\Delta t\). start and stop are inclusive at these physical times. A zero waveform still clamps the edge while active; start=0 and \(0<\mathtt{stop}<\Delta t\) apply only the initial impulse and then release the edge. In a subgrid, use its local \(\Delta t\).

Voltage-source S11 and input impedance

Every 3-D single-Yee-edge gprMax.VoltageSource normally owns the necessary hidden field monitor and calculates corrected complex S11, Zin, and Yin after the solve. The exception is a zero-resistance hard source on a domain-minimum transverse boundary: the source remains valid, but gprMax warns and omits its automatic port because the complete Ampere current loop lies partly outside the grid. A finite-resistance source uses its physical resistance as the reference impedance:

port = gprMax.VoltageSource(
    p1=(0.050, 0.050, 0.020),
    polarisation='z',
    resistance=50,
    waveform_id='source_wave',
    id='feed',
    spectrum_limit=10,
)
scene.add(port)

The default spectrum_limit=10 retains frequencies having at least ten cells per shortest material wavelength. A research run can explicitly request all native non-negative FFT bins while retaining the normal validity metadata:

scene.add(gprMax.VoltageSource(
    p1=(0.050, 0.050, 0.020),
    polarisation='z', resistance=50,
    waveform_id='source_wave',
    id='feed_full',
    spectrum_limit='nyquist',
))

A hard voltage source can instead emulate an ideal MoM delta-gap excitation. Set the source resistance to zero. Its travelling-wave reference impedance defaults to 50 Ohms and can be changed on the source:

scene.add(gprMax.VoltageSource(
    p1=(0.050, 0.050, 0.020),
    polarisation='z',
    resistance=0,
    waveform_id='source_wave',
    id='ideal_feed',
    reference_impedance=50,
))

After gprMax.run completes, port.result provides the same numerical arrays that are stored under /ports/feed in the model HDF5 file. For a hard source, gprMax obtains terminal current from the surrounding magnetic- field loop and accounts explicitly for the half-step phase difference from the integer-time voltage during transformation.

A finite-resistance source on a dispersive edge includes the background material’s complete complex permittivity in the Yee-gap correction. The same correction is used for terminal current and accepted power when calculating antenna parameters or normalising SAR. A hard source on a dispersive edge is not yet supported because its sampled Ampere-loop current requires the discrete polarisation-current contribution to be separated explicitly.

The automatic voltage-source port also supports domain-decomposed MPI CPU models. The owning rank stores the voltage history; hard-source current loops may cross internal rank faces because magnetic halos are synchronised before sampling. Histories are gathered and transformed once on the coordinator rank, while port.result is rebound to that final result for Python API use.

The source may also be placed inside a SubGridHSG. Add the waveform and voltage source to the subgrid object; the port is then sampled using that subgrid’s finer spatial and temporal discretisation. With autotranslate=True, the port and source use the same global physical coordinate:

subgrid.add(gprMax.Waveform(
    wave_type='ricker', amp=1, freq=5e9, id='feed_wave'
))
subgrid.add(gprMax.VoltageSource(
    p1=(0.090, 0.070, 0.060),
    polarisation='z', resistance=50,
    waveform_id='feed_wave',
    id='fine_feed',
))

The result is stored at /subgrids/<subgrid ID>/ports/<port ID>. The source belongs to the owning grid object, so its discretised coordinate, material edge, dl, and dt are unambiguous.

Hertzian Dipole Source

class gprMax.user_objects.cmds_multiuse.HertzianDipole(p1: Tuple[float, float, float], polarisation: str, waveform_id: str, start: Optional[float] = None, stop: Optional[float] = None)

Specifies a current density term at an electric field location.

The simplest excitation, often referred to as an additive or soft source.

polarisation

string required for polarisation of the source x, y, z.

p1

tuple required for position of source x, y, z.

waveform_id

string required for identifier of waveform used with source.

start

float optional to delay start time (secs) of source.

stop

float optional to time (secs) to remove source.

Outputs (no circuit port)

This ideal impressed-current source does not create a circuit port, S11 or input impedance. Add Rx / #rx for field or Ampere-loop observations, or an NTFF request for transformed fields. Raw field/source ratios are not power-wave S-parameters. See Sources and ports for source selection and GPR acquisitions for point-source acquisition schedules.

This additive electric source uses the waveform amplitude as current \(I\) in amperes and an effective dipole length equal to the cell step in the selected direction. It therefore impresses

\[J_s = \frac{I\,\Delta l}{\Delta x\,\Delta y\,\Delta z}.\]

For example:

scene.add(gprMax.HertzianDipole(
    p1=(0.05, 0.05, 0.05),
    polarisation='z',
    waveform_id='pulse',
))

It is an ideal field excitation, not a circuit port, and consequently does not produce S-parameters or an input impedance.

Magnetic Dipole Source

class gprMax.user_objects.cmds_multiuse.MagneticDipole(p1: Tuple[float, float, float], polarisation: str, waveform_id: str, start: Optional[float] = None, stop: Optional[float] = None)

Simulates an infinitesimal magnetic dipole.

Often referred to as an additive or soft source.

polarisation

string required for polarisation of the source x, y, z.

p1

tuple required for position of source x, y, z.

waveform_id

string required for identifier of waveform used with source.

start

float optional to delay start time (secs) of source.

stop

float optional to time (secs) to remove source.

Outputs (no circuit port)

This ideal impressed-current source does not create a circuit port, S11 or input impedance. Add Rx / #rx for field or Ampere-loop observations, or an NTFF request for transformed fields. Raw field/source ratios are not power-wave S-parameters. See Sources and ports for source selection and GPR acquisitions for point-source acquisition schedules.

This is the magnetic-current dual of the additive Hertzian source. It is placed at a magnetic Yee-field location and does not represent a circuit port. For example:

scene.add(gprMax.MagneticDipole(
    p1=(0.05, 0.05, 0.05),
    polarisation='y',
    waveform_id='pulse',
))

The permitted source component and invariant-axis index in a 2-D model follow the surviving TM or TE magnetic-field components and are validated when the scene is built.

Rational lumped-network terminal

class gprMax.user_objects.cmds_multiuse.RationalNetwork(id: str, conductance: float = 0.0, capacitance: float = 0.0, poles=(), residues=(), allow_active: bool = False)

Define a reusable rational one-port admittance.

Parameters
  • id – Unique network-model identifier.

  • conductance – Direct conductance \(G\) in siemens.

  • capacitance – Direct capacitance \(C\) in farads.

  • poles – Sequence of poles \(p_m\) in radians per second.

  • residues – Sequence of residues \(r_m\) paired with poles.

  • allow_active – Permit coefficients which do not satisfy the normal passive-network checks. The default is False.

class gprMax.user_objects.cmds_multiuse.NetworkTerminal(p1: Tuple[float, float, float], polarisation: str, network_id: str, id: str)

Connect a rational network model to one electric Yee edge.

Parameters
  • p1 – Physical (x, y, z) position of the electric edge.

  • polarisation – Electric-edge direction, x, y, or z.

  • network_id – Identifier of a preceding RationalNetwork.

  • id – Unique terminal and HDF5 port identifier.

class gprMax.user_objects.cmds_multiuse.NetworkExcitation(terminal_id: str, waveform_id: str, start: Optional[float] = None, stop: Optional[float] = None)

Apply a Thevenin open-circuit waveform to a network terminal.

Parameters
  • terminal_id – Identifier of a preceding NetworkTerminal.

  • waveform_id – Identifier of the driving waveform.

  • start – Optional source start time in seconds.

  • stop – Optional source stop time in seconds.

Port and outputs

Output is explicit: add #network_port: feed or scene.add(gprMax.NetworkPort(terminal_id='feed')) for terminal feed. The network and its excitation alone do not request port results. Output is /ports/feed, or /subgrids/<subgrid ID>/ports/feed for a CPU or CUDA HSG port.

time, Vgenerator, Vtotal and Inetwork describe the generator, gap voltage (V) and network current (A). Terminal-current spectra remove the background Yee-gap admittance and are distinct from the external-network current. frequency, S11, Zin and Yin have independent validity masks. Reference impedance defaults to 50 Ohms; it does not replace the physical network admittance.

Keep the NetworkPort object to read port.result.total_voltage, port.result.s11, port.result.zin and the other result arrays after solving. Omitting NetworkExcitation leaves a passive measurable load: its received voltage/current remain useful, but source-normalised S11 is undefined. Impedance/admittance are available where their own masks permit.

Use python -m gprMax.toolboxes.Plotting.plot_port model.h5 --port feed --save. See Rational-network port output for the schema, Fixed-topology terminal-source studies for fixed-network drive studies and Ports, antenna power and NTFF for NTFF use.

RationalNetwork defines the reusable driving-point admittance

\[Y(s)=G+sC+\sum_m\frac{r_m}{s-p_m}.\]

NetworkTerminal places it on one electric Yee edge. It is passive unless a NetworkExcitation supplies a Thévenin open-circuit waveform. For example, a 50 Ohm driven port is

scene.add(gprMax.RationalNetwork(
    id='source50', conductance=1 / 50, capacitance=0,
))
scene.add(gprMax.NetworkTerminal(
    p1=(0.05, 0.05, 0.02), polarisation='z',
    network_id='source50', id='feed',
))
scene.add(gprMax.NetworkExcitation(
    terminal_id='feed', waveform_id='pulse',
))
network_port = gprMax.NetworkPort(terminal_id='feed')
scene.add(network_port)

An inductor is represented by poles=(0,) and residues=(1/L,); a series \(RL\) branch uses poles=(-R/L,) and residues=(1/L,). Complex terms must be supplied as conjugate pairs. The circuit-to-edge formulation follows the arbitrary linear lumped-network FDTD approaches of [PER1999] and [CHE2007]. Their underlying classic PLRC time discretisation is improved here using the exponential recursive- convolution treatment of Giannakis and Giannopoulos [GIA2014]: every pole current is evaluated analytically at the electric half-step for a linearly varying voltage, rather than estimated by averaging its two integer-time values. State is stored only for placed terminals. Independent one-port rational networks are supported in 3-D on the CPU, CUDA, OpenCL, and Metal solvers, including domain-decomposed MPI CPU models; terminals inside HSG subgrids use the CPU or CUDA solver. An MPI terminal is advanced only on the rank that owns its electric edge, and its histories are gathered for port post-processing. Device runs keep the network recurrence and field correction on the compute device and copy the completed histories back after the solve. Coupled multiport admittance matrices are reserved for a later extension.

For \(Y=1/R\), NetworkExcitation and a conventional finite-resistance VoltageSource are the same discrete Thévenin source when their position, resistance, and waveform are identical. This equivalence does not apply to a zero-resistance hard voltage source.

Transmission Line

class gprMax.user_objects.cmds_multiuse.TransmissionLine(p1: Tuple[float, float, float], polarisation: str, resistance: float, waveform_id: str, start: Optional[float] = None, stop: Optional[float] = None)
Specifies a one-dimensional transmission line model at an electric

field location. The source is supported by the CPU and CUDA solvers.

polarisation

string required for polarisation of the source x, y, z.

p1

tuple required for position of source x, y, z.

resistance

float required for internal resistance (Ohms) of voltage source.

waveform_id

string required for identifier of waveform used with source.

start

float optional to delay start time (secs) of source.

stop

float optional to time (secs) to remove source.

Port and outputs

The source automatically writes /tls/tlN in creation order, or /subgrids/<subgrid ID>/tls/tlN for a CPU or CUDA HSG source. Vinc and Vtotal are incident and total line voltages (V); Iinc and Itotal are line currents (A). These are terminal quantities, not a point receiver’s local electric/magnetic fields. The line resistance is the reference impedance.

frequency, S11, Zin, Yin and their validity masks are already calculated. Zin_current is an independent current-wave de-embedding check; do not reconstruct it by simply dividing raw staggered histories. Use python -m gprMax.toolboxes.Plotting.plot_port model.h5 --port tls/tl1 --save to inspect the stored result and its timing. See Transmission-line S11 and impedance output for the line’s spatial/temporal reference plane and masks, and Fixed-topology terminal-source studies for changing generator drives while retaining the passive line termination.

Every transmission-line source automatically writes its incident and terminal voltage/current histories together with frequency, S11, Zin, and Yin beneath /tls/tlN in the model HDF5 output. Zin is derived from the voltage-wave S11 result; Zin_current is an independent, stagger-aware current-wave check. Voltage and transmission-line sources both own their terminal outputs, so no separate receiver-port object is required. See Simulation Output for the equations and validity masks.

Magnetic Frill Source

class gprMax.user_objects.cmds_multiuse.MagneticFrillSource(p1: Tuple[float, float, float], polarisation: str, zcoax: float, waveform_id: str, start: Optional[float] = None, stop: Optional[float] = None, spectrum_limit=10.0)
Specifies a magnetic-frill (equivalent-feed) source at an electric

field location, for an antenna fed through a PEC ground plane by a coaxial line. Complements #transmission_line: a different, well-established formulation (not a variant of the two-wire line - no 1D line, no ABC, no magic timestep). The corrected Hyun feed-cell formulation is supported by the CPU, CUDA, OpenCL, and Metal solvers, and by the CPU and CUDA subgrid updaters.

polarisation

string required for polarisation of the source - x, y, or z (the antenna axis the source drives current along, following the same electrical sign convention as gprMax’s Ix/Iy/Iz current output).

p1

tuple required for position of source x, y, z.

zcoax

float required for the coax’s characteristic impedance (Ohms), calculated from its physical radii and filler. The inner radius used by the discrete feed cell is inferred from a co-located ThinWire with the same polarisation.

waveform_id

string required for identifier of waveform used with source.

start

float optional delay before the incident waveform starts (secs).

stop

float optional time at which the incident waveform stops (secs). The coaxial terminal relation remains active afterwards.

spectrum_limit

minimum cells per shortest material wavelength (default 10), or "nyquist" for the full research spectrum.

Port and outputs

The source automatically writes /frills/frillN in creation order, or /subgrids/<subgrid ID>/frills/frillN for a CPU or CUDA HSG source. Vinc is half the generator waveform (V), Vtotal is the solved terminal voltage (V), and Itot is the feed current (A), centred using adjacent magnetic half steps. Zcoax is the wave-reference impedance. This continuous-wire, through-ground-plane feed is not an electric gap source.

frequency, S11, Zin, Yin and their per-quantity validity masks are stored directly. Use python -m gprMax.toolboxes.Plotting.plot_port model.h5 --port frills/frill1 --save; see Magnetic-frill-source S11 and impedance output for the sampling convention and full schema. A zero generator leaves the frill termination present; Fixed-topology terminal-source studies can vary the drive without rebuilding its physical definition.

A magnetic frill represents a sub-cell coaxial aperture through a PEC ground plane. It must share an axial Yee edge with a ThinWire; the source uses that object’s physical radius \(a\) in Hyun’s feed-cell equation. The zcoax argument is the characteristic impedance of the physical coax. For a lossless TEM coax with outer-conductor inner radius \(b\) and filler properties \(\varepsilon_{r,c}\) and \(\mu_{r,c}\),

\[Z_\mathrm{coax} = \frac{\eta_0}{2\pi} \sqrt{\frac{\mu_{r,c}}{\varepsilon_{r,c}}} \ln\!\left(\frac{b}{a}\right).\]

For the usual nonmagnetic filler,

\[Z_\mathrm{coax} \simeq \frac{60}{\sqrt{\varepsilon_{r,c}}} \ln\!\left(\frac{b}{a}\right)\ \Omega, \qquad b = a\exp\!\left( \frac{Z_\mathrm{coax}\sqrt{\varepsilon_{r,c}}}{60} \right).\]

The filler permittivity is the value inside the coax, which need not equal the antenna-side material above the ground plane. gprMax obtains \(a\) from ThinWire; \(b\) is not an input and the user must confirm that the resulting aperture remains sub-cell. For example:

scene.add(gprMax.Waveform(
    wave_type='ricker', amp=1, freq=1e9, id='pulse'
))
scene.add(gprMax.Plate(
    p1=(0, 0, 0.02), p2=(0.10, 0.10, 0.02), material_id='pec'
))
scene.add(gprMax.ThinWire(
    p1=(0.05, 0.05, 0.02), p2=(0.05, 0.05, 0.08), radius=0.0001
))
scene.add(gprMax.MagneticFrillSource(
    p1=(0.05, 0.05, 0.02), polarisation='z', zcoax=50,
    waveform_id='pulse',
))

The corrected formulation is supported by the CPU, CUDA, OpenCL, and Metal solvers and by domain-decomposed MPI CPU models. Its four magnetic feed edges may cross internal MPI rank boundaries, and PMC image completion is supported at minimum-face symmetry corners. It is also supported inside a CPU or CUDA SubGridHSG. Add the waveform, PEC ground plane, thin wire, and magnetic frill to the same subgrid object, using the same global-coordinate convention as other subgrid sources when autotranslate=True:

subgrid.add(gprMax.Waveform(
    wave_type='ricker', amp=1, freq=5e9, id='fine_feed_wave'
))
subgrid.add(gprMax.Plate(
    p1=(0.08, 0.06, 0.05), p2=(0.10, 0.08, 0.05),
    material_id='pec',
))
subgrid.add(gprMax.ThinWire(
    p1=(0.09, 0.07, 0.05), p2=(0.09, 0.07, 0.06),
    radius=0.0001,
))
subgrid.add(gprMax.MagneticFrillSource(
    p1=(0.09, 0.07, 0.05), polarisation='z', zcoax=50,
    waveform_id='fine_feed_wave',
))

The complete frill stencil and attached wire should remain within the subgrid’s working region; objects traversing its outer surface produce the usual advanced-use warning, while thin-wire or frill placement in its PML is rejected. Symmetry boundaries are not supported on a subgrid, so subgrid frills cannot use symmetry-plane completion. The source writes its time-domain terminal histories and derived S11, Zin, and Yin automatically beneath /frills/frillN on the main grid or /subgrids/<subgrid ID>/frills/frillN on a subgrid. See Simulation Output.

All local sources refer to the ID of a waveform that has already been added to the scene. The following illustrates their required arguments; a model would normally contain only the source or sources that it needs:

scene.add(gprMax.Waveform(
    wave_type='ricker', amp=1, freq=1e9, id='pulse'
))
scene.add(gprMax.VoltageSource(
    p1=(0.04, 0.05, 0.05), polarisation='z', resistance=50,
    waveform_id='pulse',
))
scene.add(gprMax.HertzianDipole(
    p1=(0.05, 0.05, 0.05), polarisation='z', waveform_id='pulse'
))
scene.add(gprMax.MagneticDipole(
    p1=(0.06, 0.05, 0.05), polarisation='y', waveform_id='pulse'
))
scene.add(gprMax.TransmissionLine(
    p1=(0.07, 0.05, 0.05), polarisation='z', resistance=50,
    waveform_id='pulse',
))

Plane Wave Angles

class gprMax.user_objects.cmds_multiuse.DiscretePlaneWaveAngles(**kwargs)

Specifies a plane wave implemented using the discrete plane wave formulation. If the background material is not specified it will default to free space. The wave will propagate in the direction given by the angles theta and phi with a polarisation given by the angle psi.

theta

float required for propagation angle (degrees) of wave.

phi

float required for propagation angle (degrees) of wave.

psi

float required for polarisation of wave.

max_angle_diff

float optional for tolerance of maximum acceptable angular difference between the desired direction of the wavevector and the estimated direction of it (degrees). Default is 3 arc minutes (0.05 degrees).

p1

tuple required for the lower left position (x, y, z) of the total field, scattered field (TFSF) box.

p2

tuple required for the upper right position (x, y, z) of the total field, scattered field (TFSF) box.

waveform_id

string required for identifier of waveform used with source.

material_id

string optional of material identifier to use as the background material in the TFSF box.

start

float optional to delay start time (secs) of source.

stop

float optional to time (secs) to remove source.

precompute

boolean optional. If True (default), precompute the auxiliary plane-wave source history before time stepping. False is reserved for a future on-the-fly implementation and is currently rejected.

Outputs (no circuit port)

A discrete plane wave does not create voltage/current ports or S-parameters. Use receivers inside/outside the TFSF box for total/scattered fields and NTFF outputs for scattering/RCS. The incident-wave normalisation is not antenna-port accepted-power normalisation. See NTFF field-transformation output and Plane-wave and RCS studies for angle/polarisation studies; every observation direction must be requested in the fixed output definition.

Plane Wave Vector

class gprMax.user_objects.cmds_multiuse.DiscretePlaneWaveVector(**kwargs)

Specifies a plane wave implemented using the discrete plane wave formulation. If the background material is not specified it will default to free space. The wave will propagate in the direction given by the vector m_vec with a polarisation given by the angle psi. The vector m_vec should be of integer values and the direction of propagation will be in the direction of that vector.

m_vec

tuple required of the three integer componets specifying the direction of the the plane wave.

psi

float required for the polarisation of wave.

p1

tuple required for the lower left position (x, y, z) of the total field, scattered field (TFSF) box.

p2

tuple required for the upper right position (x, y, z) of the total field, scattered field (TFSF) box.

waveform_id

string required for identifier of waveform used with source.

material_id

string optional of material identifier to use as the background material in the TFSF box.

start

float optional to delay start time (secs) of source.

stop

float optional to time (secs) to remove source.

precompute

boolean optional. If True (default), precompute the auxiliary plane-wave source history before time stepping. False is reserved for a future on-the-fly implementation and is currently rejected.

Outputs (no circuit port)

A discrete plane wave does not create voltage/current ports or S-parameters. Use receivers inside/outside the TFSF box for total/scattered fields and NTFF outputs for scattering/RCS. The incident-wave normalisation is not antenna-port accepted-power normalisation. See NTFF field-transformation output and Plane-wave and RCS studies for angle/polarisation studies; every observation direction must be requested in the fixed output definition.

Plane Wave Axial

class gprMax.user_objects.cmds_multiuse.DiscretePlaneWaveAxial(**kwargs)

Specifies an axial plane wave implemented using the discrete plane wave formulation. No background material is specified as materials are copied directly by the existing grid values along the axial direction of propagation. The wave will propagate in one of the directions of the Cartesian axes of the grid (x, y or z) with a polarisation given by the angle psi.

axis

a single character string required for defining the propagation axis of the wave x, y or z.

psi

float required for polarisation of wave.

p1

tuple required for the lower left position (x, y, z) of the total field, scattered field (TFSF) box.

p2

tuple required for the upper right position (x, y, z) of the total field, scattered field (TFSF) box.

waveform_id

string required for identifier of waveform used with source.

start

float optional to delay start time (secs) of source.

stop

float optional to time (secs) to remove source.

precompute

boolean optional. If True (default), precompute the auxiliary plane-wave source history before time stepping. False is reserved for a future on-the-fly implementation and is currently rejected.

Outputs (no circuit port)

A discrete plane wave does not create voltage/current ports or S-parameters. Use receivers inside/outside the TFSF box for total/scattered fields and NTFF outputs for scattering/RCS. The incident-wave normalisation is not antenna-port accepted-power normalisation. See NTFF field-transformation output and Plane-wave and RCS studies for angle/polarisation studies; every observation direction must be requested in the fixed output definition.

The angle, propagation-vector, and axial classes are alternative ways of describing a total-field/scattered-field plane wave. The two points define the total-field box. For example, choose one of:

scene.add(gprMax.DiscretePlaneWaveAngles(
    theta=90, phi=0, psi=90,
    p1=(0.02, 0.02, 0.02), p2=(0.08, 0.08, 0.08),
    waveform_id='pulse',
))

scene.add(gprMax.DiscretePlaneWaveVector(
    m_vec=(1, 0, 0), psi=90,
    p1=(0.02, 0.02, 0.02), p2=(0.08, 0.08, 0.08),
    waveform_id='pulse',
))

scene.add(gprMax.DiscretePlaneWaveAxial(
    axis='x', psi=90,
    p1=(0.02, 0.02, 0.02), p2=(0.08, 0.08, 0.08),
    waveform_id='pulse',
))

Here pulse must identify a built-in analytic waveform. Discrete plane waves use the CPU, CUDA, OpenCL, and Apple Metal solvers. Homogeneous angle/vector plane waves and layered axial plane waves support non-dispersive materials and multi-pole Debye, Lorentz, and Drude materials. Their auxiliary dispersive state uses the same real or complex precision selected for the main grid. A discrete plane wave must be added to the main scene, not to a subgrid. Its TFSF box may contain a complete subgrid; where the two regions overlap, the box must strictly enclose the subgrid’s HSG outer coupling surface so that the TFSF correction stencil remains on the main grid. MPI domain decomposition is supported. The auxiliary one-dimensional wave is replicated on every rank, and each rank applies only the TFSF corrections for the Yee components that it owns. For an axial plane wave, the layered material profile is assembled once from the distributed grid’s actual update coefficients, including multi-pole dispersive coefficients; no additional plane-wave communication occurs during timestepping.

Excitation File

class gprMax.user_objects.cmds_multiuse.ExcitationFile(filepath: Union[str, PathLike], kind: Optional[Union[int, str]] = None, fill_value: Optional[Union[float, str]] = None)

Specify file containing amplitude values of custom waveforms.

The file should be an ASCII file, and the custom waveform shapes can be used with sources in the model.

filepath

Excitation file path.

Type

str | PathLike

kind

Optional interpolation kind passed to scipy.interpolate.interp1d.

Type

int | str | None

fill_value

Optional float value or ‘extrapolate’ passed to scipy.interpolate.interp1d.

Type

float | str | None

scene.add(gprMax.ExcitationFile(
    filepath='measured_waveforms.txt', kind='linear', fill_value=0
))

Receiver

class gprMax.user_objects.cmds_multiuse.Rx(p1: Tuple[float, float, float], id: Optional[str] = None, outputs: Optional[List[str]] = None)

Specifies output points in the model.

These are locations where the values of the electric and magnetic field components over the numberof iterations of the model will be saved to file.

p1

tuple required for position of receiver x, y, z.

id

optional string used as identifier for receiver.

outputs

optional list of outputs for receiver. It can be any selection from Ex, Ey, Ez, Hx, Hy, Hz, Ix, Iy, or Iz.

Receiver Array

class gprMax.user_objects.cmds_multiuse.RxArray(p1: Tuple[float, float, float], p2: Tuple[float, float, float], dl: Tuple[float, float, float])

Defines multiple output points in the model.

p1

tuple required for position of first receiver x, y, z.

p2

tuple required for position of last receiver x, y, z.

dl

tuple required for receiver spacing dx, dy, dz.

Specific absorption rate (SAR)

class gprMax.user_objects.cmds_output.SAR(frequencies, waveform_id: str | None = None, tags=None, id: str = 'sar1', target_amplitude: float = 1.0, spectrum_limit=10.0, source_floor_db: float = -40.0, window: str = 'rectangular', averaging_masses=(), normalisation: str = 'waveform', port_id: str | None = None, target_power: float | None = None, target_flux: float | None = None)

Request frequency-domain SAR over one or more geometry tags.

The electric fields are transformed on the fly at the requested frequencies. Results are normalised to target_amplitude using the spectrum of exactly one active source associated with waveform_id, or to incident/accepted power from port_id. Port-power normalisation does not require a waveform ID because the physical port result supplies its own spectral support and validity.

Only electric absorption contributes to SAR. Magnetic properties still affect the FDTD fields, but magnetic absorption is not included. Selected magnetic materials outside PML, excluding ideal PMC constraints, trigger a warning. With nonzero magnetic loss, the result is not total SAR. Lossless permeability alone adds no magnetic heating.

Parameters
  • frequencies – Strictly increasing positive frequencies in Hz.

  • waveform_id – Waveform attached to a source-normalised excitation. Required for "waveform" and "current_moment"; optional and unused for port-power normalisation.

  • tags – Geometry tag string or iterable of tag strings.

  • id – Unique HDF5 output identifier.

  • target_amplitude – Peak phasor amplitude represented by the normalised source spectrum.

  • spectrum_limit – Minimum cells per shortest material wavelength (default 10), or "nyquist" for explicit research output.

  • source_floor_db – Frequencies below this relative source-spectrum level are stored as invalid/NaN.

  • window – "rectangular" or "hann".

  • averaging_masses – Optional spatial averaging masses in kg. The default empty tuple writes local cell SAR only. For example, (0.001, 0.01) requests the standard 1 g and 10 g results.

  • normalisation – "waveform" (default), "current_moment" for a 3-D Hertzian dipole, "incident_flux" for a discrete plane wave, "incident_power", or "accepted_power".

  • port_id – Required for either power normalisation.

  • target_power – Required positive power for either power normalisation; watts in 3-D and watts per metre in 2-D.

  • target_flux – Required positive incident plane-wave power flux in W/m2 for "incident_flux" normalisation.

See the electric-only absorption limitation for the magnetic-material warning and its scope, including directional materials and ideal PMC constraints.

Both isotropic and grid-aligned diagonal anisotropic volume materials are supported. For a primitive with material_ids=(mx, my, mz), absorption is the sum of the x-, y-, and z-component contributions using each material’s frequency-dependent electric loss, not the mean of the three conductivities. The three constituent materials must have the same positive mass density for SAR. Missing or conflicting densities are rejected; Radiometry uses the same electric absorption calculation without density. See Model Output for the equations and the supported tensor convention.

SAR selects the final voxelised cells belonging to one or more semantic geometry tags. Every selected material must first have a mass density in kg/m3, assigned with MaterialDensity or supplied by a material database. For example:

scene.add(gprMax.MaterialDensity(density=1040, material_ids=('brain',)))
scene.add(gprMax.SAR(
    frequencies=np.linspace(0.8e9, 1.2e9, 41),
    waveform_id='pulse',
    tags=('brain_region',),
    id='brain_sar',
    target_amplitude=1.0,
    spectrum_limit=10,
    averaging_masses=(0.001, 0.01),
))

target_amplitude uses the source’s native excitation units. In particular, it is incident electric-field amplitude in V/m for a discrete plane wave and generator voltage in V for a voltage source. The resulting SAR is therefore tied to that explicitly stated source normalisation. For a 3-D Hertzian dipole, use normalisation='current_moment' and give target_amplitude in A m. For a discrete plane wave, use normalisation='incident_flux' and target_flux in W/m2 when power flux rather than electric-field amplitude is the required physical normalisation. Spatial mass averaging is opt-in. With the default averaging_masses=(), gprMax writes local cell SAR, absorbed-power density, and per-tag summaries without running the potentially expensive mass-averaging stage. Supply (0.001, 0.01) to request the standard 1 g and 10 g results, or any other positive masses in kg for research applications. Density is constant within each final tagged cell; only the included volume fraction of a cell changes at an averaging-cube boundary.

For a one-watt accepted-power result from a named port, use:

scene.add(gprMax.SAR(
    frequencies=np.linspace(0.8e9, 1.2e9, 41),
    tags=('brain_region',), id='brain_sar_1W',
    normalisation='accepted_power', port_id='feed', target_power=1.0,
))

incident_power is also available. target_power is in W for 3-D models and W/m for invariant 2-D models. Power normalisation currently requires a rectangular transform window and a physical, valid port-power result at each requested frequency.

Selected tagged cells that lie in boundary or internal PML regions are excluded automatically because PML loss is not physical material absorption.

SAR may be added to a SubGridHSG in the same way as other subgrid outputs. Fields are transformed using the fine-grid dt and the result is written below /subgrids/<subgrid ID>/sar/<output ID>. The normalising source may be on the main grid or the subgrid; its waveform DFT uses the timestep of the grid that owns the source. This is useful when a main-grid plane wave or antenna illuminates finely resolved tagged tissue. Mass averaging is local to the selected subgrid tag volume, so the complete tissue region required by an averaging cube should be contained inside the subgrid working region.

Reduced 2-D TM and TE main-grid models are supported. SAR is evaluated only on the genuine field plane (invariant index zero for TM and one for TE), and only the active electric components are transformed. Tag-integrated absorbed power and mass are written per unit invariant length. Spatial mass averaging is not yet available in 2-D, so averaging_masses must remain empty for these models.

See Two-dimensional SAR in lossy dielectric cylinders for analytical TMz and TEz validation against homogeneous lossy-cylinder series over fat-, skin-, and muscle-like material properties.

The default permits output only while the shortest wavelength in any model material is sampled by at least ten cells. Use spectrum_limit=8 for a lambda/8 criterion. spectrum_limit='nyquist' is an explicit research override: it retains the requested frequencies but does not imply spatial accuracy. Three- and two-dimensional CPU, CUDA, OpenCL, and Metal models are supported on the main grid. MPI domain-decomposed CPU models and three-dimensional CPU and CUDA HSG subgrids are also supported. Under MPI, source and port normalisation and any requested spatial mass averaging are completed globally on the coordinator, so tag volumes and averaging cubes may cross rank boundaries. See Absorbed power density and specific absorption rate for the formulation and HDF5 schema.

Radiometric absorption weighting

class gprMax.user_objects.cmds_output.Radiometry(frequencies, tags=None, waveform_id: str | None = None, id: str = 'radiometry1', target_amplitude: float = 1.0, spectrum_limit=10.0, source_floor_db: float = -40.0, window: str = 'rectangular', normalisation: str = 'waveform', port_id: str | None = None, target_power: float | None = None, target_flux: float | None = None)

Request absorbed-power and radiometric weighting over geometry tags.

The field collection, source normalisation, material-loss formulation, PML exclusion, and mesh-validity policy are identical to SAR. Density is not required and no mass-based quantity is calculated.

normalisation may be "waveform", "current_moment" for a 3-D Hertzian dipole, "incident_flux" for a discrete plane wave, or "incident_power"/"accepted_power" for a physical port. The output includes absorbed-power density and its normalised weighting: absorbed-power fraction density for port power, absorption cross-section density for plane-wave flux, or absorbed power per squared native source amplitude for a portless local source.

Only electric absorption contributes to these outputs. Magnetic properties still affect the FDTD fields, but magnetic absorption is not included. Selected magnetic materials outside PML, excluding ideal PMC constraints, trigger a warning; radiometric weights omit any magnetic loss. Lossless permeability alone adds no magnetic heating.

Parameters
  • frequencies – Strictly increasing positive frequencies in Hz.

  • tags – Geometry tag string or iterable of tag strings.

  • waveform_id – Required for waveform, current-moment, and incident-flux normalisation; unused for port-power normalisation.

  • id – Unique HDF5 output identifier.

  • target_amplitude – Positive native source amplitude for waveform or current-moment normalisation.

  • spectrum_limit – Minimum cells per shortest material wavelength, or "nyquist" for explicit research output.

  • source_floor_db – Source-support validity threshold in dB.

  • window – "rectangular" or "hann".

  • normalisation – One of the modes described above.

  • port_id – Required for incident- or accepted-power normalisation.

  • target_power – Required positive W (3-D) or W/m (2-D) for port power.

  • target_flux – Required positive W/m2 for plane-wave incident flux.

The electric-only absorption limitation and its magnetic-material warning apply to every radiometry normalisation.

Radiometry is the density-independent counterpart of SAR. It uses the same tagged-cell field transforms and loss calculation, but writes absorbed power and a source-normalised absorption weighting without requiring MaterialDensity. A plane-wave absorption cross section is requested with:

scene.add(gprMax.Radiometry(
    frequencies=np.linspace(0.5e9, 2e9, 61),
    waveform_id='incident', tags=('subsurface_layer',),
    id='layer_absorption', normalisation='incident_flux',
    target_flux=1.0,
))

For an antenna or local probe with a physical port, omit waveform_id and normalise to port power:

scene.add(gprMax.Radiometry(
    frequencies=np.linspace(0.5e9, 2e9, 61),
    tags=('subsurface_layer',), id='probe_weighting',
    normalisation='accepted_power', port_id='feed', target_power=1.0,
))

For a portless Hertzian source, current_moment gives an absorption kernel per squared A m. waveform remains available for every source and retains that source’s native excitation units. Outputs and their dimensional meaning are described in Radiometric absorption weighting.

Rational-network S11 and input impedance

class gprMax.user_objects.cmds_output.NetworkPort(terminal_id: str, reference_impedance: float = 50.0, spectrum_limit=10.0)

Request port quantities for a rational-network terminal.

Port and outputs

Output is explicit: add #network_port: feed or scene.add(gprMax.NetworkPort(terminal_id='feed')) for terminal feed. The network and its excitation alone do not request port results. Output is /ports/feed, or /subgrids/<subgrid ID>/ports/feed for a CPU or CUDA HSG port.

time, Vgenerator, Vtotal and Inetwork describe the generator, gap voltage (V) and network current (A). Terminal-current spectra remove the background Yee-gap admittance and are distinct from the external-network current. frequency, S11, Zin and Yin have independent validity masks. Reference impedance defaults to 50 Ohms; it does not replace the physical network admittance.

Keep the NetworkPort object to read port.result.total_voltage, port.result.s11, port.result.zin and the other result arrays after solving. Omitting NetworkExcitation leaves a passive measurable load: its received voltage/current remain useful, but source-normalised S11 is undefined. Impedance/admittance are available where their own masks permit.

Use python -m gprMax.toolboxes.Plotting.plot_port model.h5 --port feed --save. See Rational-network port output for the schema, Fixed-topology terminal-source studies for fixed-network drive studies and Ports, antenna power and NTFF for NTFF use.

NetworkPort requests the output for an existing NetworkTerminal. Its terminal ID becomes the HDF5 port ID:

network_port = gprMax.NetworkPort(
    terminal_id='feed', reference_impedance=50, spectrum_limit=10,
)
scene.add(network_port)

After the solve, network_port.result contains aligned voltage/current histories and S11, Zin, and Yin spectra. The background Yee-gap capacitance and conductance are removed from terminal current. Omitting NetworkExcitation leaves a passive measurable port; it has no meaningful source-normalised S11 but can still report impedance/admittance where the response is numerically defined. A port inside a Python API subgrid is written beneath /subgrids/<subgrid ID>/ports/<terminal ID> and uses that subgrid’s fine dl and dt.

Source Steps

class gprMax.user_objects.cmds_singleuse.SrcSteps(p1: Tuple[float, float, float])

Move the location of all simple sources.

step_size

Increment (x, y, z) to move all simple sources by for each step.

Type

Tuple[float]

Receiver Steps

class gprMax.user_objects.cmds_singleuse.RxSteps(p1: Tuple[float, float, float])

Move the location of all receivers.

step_size

Increment (x, y, z) to move all receivers by for each step.

Type

Tuple[float]

Snapshot

class gprMax.user_objects.cmds_output.Snapshot(p1: Tuple[float, float, float], p2: Tuple[float, float, float], dl: Tuple[float, float, float], filename: str, fileext: Optional[str] = None, iterations: Optional[int] = None, time: Optional[float] = None, outputs: Optional[List[str]] = None)
Obtains information about the electromagnetic fields within a volume

of the model at a given time instant.

p1

tuple required to specify lower left (x,y,z) coordinates of volume of snapshot in metres.

p2

tuple required to specify upper right (x,y,z) coordinates of volume of snapshot in metres.

dl

tuple require to specify spatial discretisation of the snapshot in metres.

filename

string required for name of the file to store snapshot.

time/iterations

either a float for time or an int for iterations must be specified for the electric-field time level at which the snapshot will be taken. A time is mapped to the nearest full time step. Magnetic fields retain their native half-step staggering.

fileext

optional string to indicate type for snapshot file, either ‘.vtkhdf’ (default) or ‘.h5’

outputs

optional list of outputs for receiver. It can be any selection from Ex, Ey, Ez, Hx, Hy, or Hz.

A snapshot added to an HSG subgrid uses that grid’s spatial and temporal discretisation. Its output origin is expressed in global model coordinates.

Receivers can request selected field components, while an array produces regularly spaced receivers over a line, plane, or volume. Source and receiver steps are applied between repeated model runs:

scene.add(gprMax.Rx(
    p1=(0.08, 0.05, 0.05), id='surface_rx', outputs=['Ez', 'Hy']
))
scene.add(gprMax.RxArray(
    p1=(0.02, 0.10, 0.05), p2=(0.12, 0.10, 0.05),
    dl=(0.01, 0, 0),
))
scene.add(gprMax.SrcSteps(p1=(0.002, 0, 0)))
scene.add(gprMax.RxSteps(p1=(0.002, 0, 0)))
scene.add(gprMax.Snapshot(
    p1=(0, 0, 0), p2=(0.15, 0.12, 0.10),
    dl=(0.002, 0.002, 0.002), time=2e-9,
    filename='fields_2ns', fileext='.h5', outputs=['Ez', 'Hy'],
))

A snapshot can also be added to an HSG subgrid. It is sampled on every fine subgrid time step, uses the subgrid’s spatial discretisation, and records its origin in the global model coordinate system. The requested time or iteration is interpreted against the owning subgrid’s dt and iteration count. In a reduced 2-D model, any requested invariant-axis extent is collapsed to the single genuine field plane: index zero for TM or index one for TE.

Snapshot values are evaluated at the regular output-cell centres written to the file. Coarse dl uses linear interpolation of native Yee samples, not volume averaging; native-cell spacing preserves the original arithmetic. Non-dividing interior ROIs keep ceil((p2-p1)/dl) output cells. Construction rejects a final regular cell whose centre or interpolation stencil requires samples outside the physical Yee grid. Unlike older versions, this does not silently clip or enlarge p2 or alter live-axis dl. Choose a smaller extent or spacing instead. MPI exchanges only the required native samples at snapshot iterations; ranks with no output cells still participate.

The snapshot time is rounded to the nearest full electric-field time level \(n\Delta t\) (halfway values round to the earlier step), while iterations=n selects the same zero-based level directly. Snapshot electric fields are at \(n\Delta t\) and magnetic fields retain their native Yee stagger at \((n-1/2)\Delta t\); there is no temporal averaging. Valid indices are 0 <= n < the owning grid’s number of iterations. This temporal definition is separate from the existing spatial collocation of field components in a snapshot cell.

Reusable NTFF integration surface

class gprMax.user_objects.cmds_output.NTFFSurface(p1, p2, id: str, origin=None, omit_faces=())

Define a reusable Yee-aligned field-transformation surface.

The reusable Python interface has a one-to-one mapping to the supported NTFF hash commands:

Table 6 Reusable NTFF interfaces

Python class

Hash command

NTFFSurface

#ntff_surface

KSIRFrequencyTransform

#ksir_frequency

NTFFFrequencyTransform

#ntff_frequency

NTFFLayeredBackground

#ntff_layered_background

NTFFLayeredFrequencyTransform

#ntff_layered_frequency

NTFFLayeredTimeTransform

#ntff_layered_time

KSIRAntennaPorts

#ksir_antenna_ports

NTFFAntennaPorts

#ntff_antenna_ports

KSIRTimeRx

#ksir_time_rx

KSIRTimeRxSpherical

#ksir_time_rx_spherical

KSIRTimeRxArray

#ksir_time_rx_array

KSIRFrequencyRx

#ksir_frequency_rx

KSIRFrequencyRxSpherical

#ksir_frequency_rx_spherical

KSIRFrequencyRxArray

#ksir_frequency_rx_array

KSIRFarField

#ksir_far_field

KSIRFarFieldArray

#ksir_far_field_array

NTFFFarField

#ntff_far_field

NTFFFarFieldArray

#ntff_far_field_array

NTFFTimeFarField

#ntff_time_far_field

NTFFTimeFarFieldArray

#ntff_time_far_field_array

NTFFLayeredTimeFarField

#ntff_layered_time_far_field

NTFFLayeredTimeFarFieldArray

#ntff_layered_time_far_field_array

The mapping covers the reusable operations and their normal options. The Python API also exposes advanced keyword arguments that cannot be entered positionally in a hash command: NTFFSurface.origin, and save_surface_dft and plane_wave_index on either frequency-transform class. Hash commands use the default surface centre, save the surface DFT, and associate an enclosed plane wave automatically.

The KSIR* classes require a closed NTFFSurface; this rule is independent of whether an eigenmode source is present. Traditionally a real guide extends through the domain PML and the crossed Huygens face is omitted, which makes that open surface unsuitable for KSIR. A VirtualWaveguide moves the matched continuation outside the main domain, allowing the eigenmode-fed antenna to be enclosed by either a closed KSIR or closed equivalent-current surface.

NTFFSurface(omit_faces=('x0', 'xmax')) creates an open frequency-domain Huygens surface. omit_faces accepts one to five distinct Cartesian face names; at least one of the six faces must remain active. A feed crossing an opening continues uniformly into its PML, with the impressed source plane outside the Huygens volume. KSIR and ordinary transient equivalent-current outputs reject omitted physical faces. The exception is a direct layered-time transform whose one omitted face coincides exactly with its declared terminal PEC plane. This is the grounded-slab construction described in Near-to-far-field transformations.

A PEC-backed substrate can be declared through the same background class; pec is the terminal entry and the adjacent interface is its physical coordinate:

scene.add(gprMax.NTFFLayeredBackground(
    id='grounded', axis='z',
    materials=('free_space', 'substrate', 'pec'),
    interfaces=(0.0, -0.002),
))

This declaration does not build the corresponding substrate or PEC geometry. Only directions in the open hemisphere may be requested.

NTFF definitions are main-grid objects, but their notional closed integration surface may enclose complete HSG subgrids. A surface must not touch or cut an HSG outer coupling surface: overlapping regions require the NTFF surface to strictly enclose that outer surface. Sources and scatterers inside the subgrid then contribute through the normal HSG field exchange. A disjoint subgrid is permitted. NTFF surfaces cannot be defined inside a subgrid.

The same interface is available with MPI domain decomposition. Surface patches are distributed between ranks and use the existing field halos, while the completed results retain the same HDF5 schema as a serial run. MPI NTFF does not introduce a surface collective during each FDTD iteration; accumulated time histories or frequency-domain phasors are combined once at finalisation. Geometry-fixed reuse remains unsupported.

The following example reuses one surface for an exact time-domain point and a frequency-domain radiation pattern. Python keyword arguments replace the positional optional parameters used by the equivalent hash commands.

scene.add(gprMax.NTFFSurface(
    p1=(0.03, 0.03, 0.03),
    p2=(0.07, 0.07, 0.07),
    id='radiation_surface',
))
scene.add(gprMax.KSIRTimeRx(
    position=(0.12, 0.05, 0.05),
    surface_id='radiation_surface',
    id='transient',
    outputs=('Ez',),
    time_origin='first_arrival',
))
scene.add(gprMax.KSIRFrequencyTransform(
    surface_id='radiation_surface',
    id='antenna_band',
    frequencies=(0.8e9, 1.0e9, 1.2e9),
    window='hann',
))
scene.add(gprMax.KSIRFarFieldArray(
    theta_start=0,
    theta_stop=180,
    theta_step=5,
    phi_start=0,
    phi_stop=360,
    phi_step=5,
    transform_id='antenna_band',
    id='pattern',
    outputs=('Etheta', 'Ephi', 'radiation_intensity'),
))

For a completed time receiver, result.point_times(q) and result.point_field(output, q) return only the interval supported by every surface patch. point_raw_times and point_raw_field deliberately expose the additional partial retarded tail for research use. Check result.terminal_decay_ok[q]; a false value means that the FDTD time window should be increased.

KSIR frequency transform

class gprMax.user_objects.cmds_output.KSIRFrequencyTransform(surface_id: str, id: str, frequencies, window: str = 'rectangular', save_surface_dft: bool = True, plane_wave_index: Optional[int] = None)

Declare frequencies and a window for one reusable NTFF surface.

KSIR antenna-port association

class gprMax.user_objects.cmds_output.KSIRAntennaPorts(transform_id, port_ids)

Associate all physical antenna ports with one KSIR transform.

Parameters
  • transform_id – ID of a rectangular-window KSIRFrequencyTransform.

  • port_ids – IDs of every physical antenna port. Voltage-source IDs come directly from the source (explicit or automatic). Transmission-line and magnetic-frill sources use their automatic tlN and frillN IDs. Rational-network ports use their terminal IDs and must be explicitly requested with NetworkPort. A port on a subgrid is referenced as <subgrid ID>/<local port ID>. Active eigenmode sources cannot use this conventional terminal gain-normalisation path; use NTFFAntennaPorts instead. This restriction does not prohibit raw KSIR field reconstruction on a compatible closed surface around a virtual-guide-fed antenna.

The complete set is required for an unambiguous coherent accepted-power balance. A source with zero waveform amplitude is still a terminated port and must be included.

The association is needed only for gain and efficiency. It must name every physical conventional port, including a zero-amplitude source that acts as a termination and explicitly monitored rational-network terminals. Main-grid port IDs are used directly. A subgrid port is qualified by its subgrid ID, for example fine_grid/feed, fine_grid/tl1, or fine_grid/frill1. Its voltage and current spectra are transformed with the owning subgrid’s finer time step. For voltage sources, use the source’s id (or its automatic portN ID); automatic transmission-line and magnetic-frill IDs are tl1, … and frill1, … respectively. Rational-network ports use their terminal IDs. For active modal sources use an equivalent-current transform with NTFFAntennaPorts, not this conventional KSIR gain-normalisation path. Raw KSIR fields on a compatible closed surface remain a separate capability. See Ports, antenna power and NTFF. For a conventional two-terminal antenna:

scene.add(gprMax.KSIRFrequencyTransform(
    surface_id='radiation_surface',
    id='antenna_band',
    frequencies=(0.8e9, 1.0e9, 1.2e9),
    window='rectangular',
))
scene.add(gprMax.KSIRAntennaPorts(
    transform_id='antenna_band',
    port_ids=('element1', 'element2'),
))
scene.add(gprMax.KSIRFarFieldArray(
    theta_start=0,
    theta_stop=180,
    theta_step=2,
    phi_start=0,
    phi_stop=360,
    phi_step=2,
    transform_id='antenna_band',
    id='array_pattern',
    outputs=(
        'directivity_dbi',
        'gain_dbi',
        'realized_gain_dbi',
        'radiation_efficiency',
        'total_efficiency',
    ),
))

The same surface may independently supply conventional equivalent-current far fields. For example:

scene.add(gprMax.NTFFFrequencyTransform(
    surface_id='radiation_surface',
    id='current_band',
    frequencies=(0.8e9, 1.0e9, 1.2e9),
    window='hann',
))
scene.add(gprMax.NTFFFarFieldArray(
    theta_start=0,
    theta_stop=180,
    theta_step=5,
    phi_start=0,
    phi_stop=360,
    phi_step=5,
    transform_id='current_band',
    id='current_pattern',
    outputs=('Etheta', 'Ephi', 'directivity_dbi'),
))
scene.add(gprMax.NTFFTimeFarField(
    theta=90,
    phi=0,
    surface_id='radiation_surface',
    id='current_transient',
    outputs=('Etheta', 'Ephi'),
))

The full sphere needed to normalise directivity and efficiency is generated internally. The directions stored for array_pattern remain exactly those requested above. Gain uses the coherent net accepted power of the complete port set, so amplitudes and delays applied to the source waveforms can model array steering. For broadband pulses, a time delay represents true-time-delay steering; a fixed phase shift is frequency-specific.

KSIR exact time-domain receivers

class gprMax.user_objects.cmds_output.KSIRTimeRx(position, surface_id: str, id: Optional[str] = None, outputs=None, time_origin: str = 'simulation')

Request exact advanced-time KSIR fields at Cartesian point(s).

class gprMax.user_objects.cmds_output.KSIRTimeRxSpherical(r, theta, phi, surface_id, id=None, outputs=None, time_origin='simulation')

Request exact advanced-time KSIR fields at spherical coordinate(s).

class gprMax.user_objects.cmds_output.KSIRTimeRxArray(p1, p2, dl, surface_id, id=None, outputs=None, time_origin='simulation')

Request exact time-domain fields on a Cartesian point array.

KSIR exact frequency-domain receivers

class gprMax.user_objects.cmds_output.KSIRFrequencyRx(position, transform_id, id=None, outputs=None)

Request exact finite-distance frequency-domain Cartesian fields.

class gprMax.user_objects.cmds_output.KSIRFrequencyRxSpherical(r, theta, phi, transform_id, id=None, outputs=None)

Request exact finite-distance frequency-domain spherical fields.

class gprMax.user_objects.cmds_output.KSIRFrequencyRxArray(p1, p2, dl, transform_id, id=None, outputs=None)

Request exact frequency-domain fields on a Cartesian point array.

KSIR range-normalized far fields

class gprMax.user_objects.cmds_output.KSIRFarField(theta, phi, transform_id, id=None, outputs=None)

Request range-normalized far fields in paired spherical directions.

In addition to Cartesian and spherical field components, outputs may contain radiation intensity, directivity, gain, realized gain, radiation or total efficiency, and RCS. Directivity and efficiency use an internal full-sphere quadrature even when only a cut is requested. Gain and efficiency require a KSIRAntennaPorts association.

class gprMax.user_objects.cmds_output.KSIRFarFieldArray(theta_start, theta_stop, theta_step, phi_start, phi_stop, phi_step, transform_id, id=None, outputs=None)

Request a theta/phi product grid of range-normalized far fields.

The spherical receiver radius is explicit and produces an exact physical finite-distance field. KSIRFarField deliberately has no radius and returns r * exp(+j*k*r) * field. All spherical angles use theta from +z and phi from +x towards +y. A surface face that coincides with a declared PEC or PMC symmetry boundary is completed automatically by image theory.

Equivalent-current far fields

class gprMax.user_objects.cmds_output.NTFFFrequencyTransform(surface_id: str, id: str, frequencies, window: str = 'rectangular', save_surface_dft: bool = True, plane_wave_index: Optional[int] = None)

Declare a conventional equivalent-current frequency transform.

class gprMax.user_objects.cmds_output.NTFFLayeredBackground(id: str, axis: str, materials, interfaces=())

Declare a planar material stack for layered equivalent-current NTFF.

Materials are ordered from the positive-axis side to the negative-axis side. The built-in pec identifier may occur once, as the first or last entry, to declare a perfect short-circuit termination at the adjacent interface. It is analytical propagation metadata and does not build the corresponding PEC FDTD geometry.

class gprMax.user_objects.cmds_output.NTFFLayeredFrequencyTransform(surface_id: str, id: str, background_id: str, frequencies, window: str = 'rectangular', save_surface_dft: bool = True, plane_wave_index: Optional[int] = None)

Declare an equivalent-current transform for a planar layered background.

class gprMax.user_objects.cmds_output.NTFFLayeredTimeTransform(surface_id: str, id: str, background_id: str, impulse_tolerance: float = 1e-10, max_impulses: int = 100000)

Declare a direct time-domain transform for a lossless planar stack.

The transform uses the delayed TE/TM impulse-train formulation developed by Capoglu. Conductive, dispersive, evanescent, and exact-grazing cases must use NTFFLayeredFrequencyTransform.

class gprMax.user_objects.cmds_output.NTFFFarField(theta, phi, transform_id, id=None, outputs=None)

Request equivalent-current range-normalized far fields.

A planar-layered transform additionally accepts the grouped outputs "exterior_power", "exterior_maximum", and "exterior_efficiency". They store positive- and negative-stack-axis radiation summaries without retaining the temporary full-sphere fields. Exterior efficiency requires an NTFFAntennaPorts association; exterior power and conventional directivity do not.

class gprMax.user_objects.cmds_output.NTFFFarFieldArray(theta_start, theta_stop, theta_step, phi_start, phi_stop, phi_step, transform_id, id=None, outputs=None)

Request an equivalent-current theta/phi far-field product grid.

class gprMax.user_objects.cmds_output.NTFFAntennaPorts(transform_id, port_ids)

Associate all physical antenna ports with an equivalent-current transform.

In addition to conventional terminal ports, every EigenmodePort uses portN for its explicit port index, whether driven or passive. Every transform frequency must be present in each associated modal port’s direct-DFT bins; the modal DFT grid may contain additional bins.

These classes provide the conventional Love-current frequency transform and share the KSIR far-field output and antenna-metric definitions. They do not provide finite-distance receivers. See Near-to-far-field transformations for the surface-current equations and engineering phasor convention.

Advanced NTFF utilities

The following low-level API utilities support specialised post-processing and research workflows. They are not scene objects and should not be passed to Scene.add.

class gprMax.SymmetryCompletion(faces: Optional[Tuple[str, ...]] = None)

Request exact completion from resolved gprMax PEC/PMC boundaries.

If faces is omitted, every declared symmetry boundary touched by the logical KSIR box is used.

Parameters

faces – optional tuple containing any of x0, xmax, y0, ymax, z0, and zmax. If omitted, use all compatible symmetry faces touched by the logical integration box.

class gprMax.ExperimentalMask(omit_faces: Tuple[str, ...])

Deliberately omit complete physical faces from the KSIR integral.

This is a research diagnostic, not a closed-surface transformation. Omitting a face generally violates the exact KSIR representation and can produce non-physical fields unless the user supplies an independently justified closure.

Parameters

omit_faces – tuple containing one to five distinct faces from x0, xmax, y0, ymax, z0, and zmax.

gprMax.evaluate_saved_surface_dft(filename: pathlib.Path | str, monitor_name: str, theta: ArrayLike, phi: ArrayLike, *, origin: Optional[ArrayLike] = None) → KSIRSavedFarField

Evaluate new directions from a saved production KSIR surface DFT.

Parameters
  • filename – model HDF5 output containing a saved KSIR surface DFT.

  • monitor_name – frequency-transform identifier below the /ntff group.

  • theta – one-dimensional polar-angle values in degrees.

  • phi – one-dimensional azimuthal-angle values in degrees.

  • origin – optional Cartesian phase-reference origin in metres. If omitted, use the origin stored with the surface.

Returns

A KSIRSavedFarField evaluated on the Cartesian product of theta and phi without rerunning FDTD.

gprMax.spherical_observation_points(origin: ArrayLike, radius: ArrayLike, theta: ArrayLike, phi: ArrayLike, *, degrees: bool = False) → NDArray[floating]

Create Cartesian KSIR observation points from spherical coordinates.

theta is measured from +z and phi from +x toward +y. Radius and angles use NumPy broadcasting; the broadcast result is flattened to the (npoints, 3) array accepted by the exact-point KSIR evaluator. For an angular product grid, pass (for example) theta[:, None] and phi[None, :].

Parameters
  • origin – Cartesian sphere origin (x, y, z) in metres.

  • radius – positive radius value or broadcastable array in metres.

  • theta – polar angle value or broadcastable array.

  • phi – azimuthal angle value or broadcastable array.

  • degrees – interpret angles in degrees rather than radians.

Returns

Contiguous Cartesian point array with shape (npoints, 3).

SymmetryCompletion requests an exact image-theory completion at declared PEC or PMC symmetry boundaries. ExperimentalMask deliberately makes a KSIR surface mathematically open; it is provided for controlled research experiments and generally does not produce a physical field without a separately justified closure. evaluate_saved_surface_dft evaluates a compatible saved surface spectrum after a run, while spherical_observation_points constructs Cartesian exact-field receiver locations from spherical coordinates.

A layered transform reuses the same far-field request classes. For example, air above a finite dielectric layer and a lower dielectric half-space can be declared independently of the geometry objects which build those layers:

scene.add(gprMax.NTFFLayeredBackground(
    id='ground',
    axis='z',
    materials=('free_space', 'dry_soil', 'wet_soil'),
    interfaces=(0.0, -0.1),
))
scene.add(gprMax.NTFFLayeredFrequencyTransform(
    surface_id='radiation_surface',
    id='ground_band',
    background_id='ground',
    frequencies=(0.8e9, 1.0e9, 1.2e9),
    window='rectangular',
))
scene.add(gprMax.NTFFFarField(
    theta=30,
    phi=0,
    transform_id='ground_band',
    id='upper_pattern',
    outputs=(
        'Etheta',
        'Ephi',
        'directivity_dbi',
        'exterior_power',
        'exterior_maximum',
    ),
))

The two exterior materials must be lossless and an explicitly requested direction must not be exactly grazing to the layer normal. Internal layers may be conductive or electrically dispersive. See Near-to-far-field transformations for the TE/TM transmission-line formulation. Far-field and antenna metrics support different lossless exterior materials; coherent array-codebook synthesis currently requires equal, frequency-independent exterior impedances.

The grouped exterior_power output stores the radiated power and fraction in each positive- and negative-axis exterior. exterior_maximum stores the maximum radiation intensity, conventionally full-sphere-normalised directivity, and its direction in each exterior. These two requests do not require a port. exterior_efficiency additionally stores each exterior’s accepted- and incident-power-normalised coupling efficiency and maximum gain; it therefore requires NTFFAntennaPorts, every physical port, and a rectangular transform window. For example:

scene.add(gprMax.NTFFAntennaPorts(
    transform_id='ground_band',
    port_ids=('feed',),
))
pattern = gprMax.NTFFFarFieldArray(
    theta_start=1,
    theta_stop=179,
    theta_step=2,
    phi_start=0,
    phi_stop=358,
    phi_step=2,
    transform_id='ground_band',
    id='ground_pattern',
    outputs=(
        'Etheta',
        'Ephi',
        'gain_dbi',
        'realized_gain_dbi',
        'exterior_power',
        'exterior_efficiency',
        'exterior_maximum',
    ),
)
scene.add(pattern)

Here the layered transform must use window='rectangular' because the second request is normalised by the port spectra. The exact grazing direction is omitted from this example; it is singular in the layered far-zone representation. After the run, the same summaries are available without reopening the HDF5 file through pattern.result.radiation_metrics.exterior. Its arrays use region order (positive_axis, negative_axis) and then frequency; the HDF5 writer presents those two rows as named groups.

Time-domain equivalent-current far fields

class gprMax.user_objects.cmds_output.NTFFTimeFarField(theta, phi, surface_id, id=None, outputs=None)

Request 1997 equivalent-current time-domain far fields.

class gprMax.user_objects.cmds_output.NTFFTimeFarFieldArray(theta_start, theta_stop, theta_step, phi_start, phi_stop, phi_step, surface_id, id=None, outputs=None)

Request a theta/phi grid of 1997 time-domain far fields.

class gprMax.user_objects.cmds_output.NTFFLayeredTimeFarField(theta, phi, transform_id, id=None, outputs=None)

Request direct time-domain far fields from a layered time transform.

class gprMax.user_objects.cmds_output.NTFFLayeredTimeFarFieldArray(theta_start, theta_stop, theta_step, phi_start, phi_stop, phi_step, transform_id, id=None, outputs=None)

Request a theta/phi product grid from a layered time transform.

NTFFTimeFarField and NTFFTimeFarFieldArray compute homogeneous time-domain equivalent-current far fields on the CPU, CUDA, OpenCL, and Metal solvers. The calculation includes the time-staggering modification of Giannopoulos et al. [GIAFF1997], described in Time-domain far field. Their times are reduced times for range-normalized far fields, and only samples supported by every surface patch are returned.

NTFFLayeredTimeTransform and its request classes replace homogeneous propagation by the direct TE/TM impulse responses of a lossless planar stack [CAP2007]. They reuse NTFFLayeredBackground. Every layer must be positive, lossless, and nondispersive, and each requested direction must be propagating in every layer and non-grazing. CPU and MPI execution use a Cython/OpenMP accumulation kernel; CUDA, OpenCL, and Metal use device-resident gather and sparse-deposition kernels. Use NTFFLayeredFrequencyTransform for conductive, dispersive, or evanescent cases.

For example:

scene.add(gprMax.NTFFLayeredTimeTransform(
    surface_id='radiation_surface',
    id='ground_transient',
    background_id='ground',
))
scene.add(gprMax.NTFFLayeredTimeFarFieldArray(
    theta_start=5,
    theta_stop=175,
    theta_step=5,
    phi_start=0,
    phi_stop=355,
    phi_step=5,
    transform_id='ground_transient',
    id='transient_pattern',
    outputs=('Etheta', 'Ephi'),
))

Subgrid

class gprMax.SubGridHSG(p1=None, p2=None, ratio=3, id='', is_os_sep=3, pml_separation=4, subgrid_pml_thickness=6, interpolation=1, filter=True)

Huygens Surface subgridding (HSG) user object.

p1

list of the position of the lower left corner of the Inner Surface (x, y, z) in the main grid.

p2

list of the position of the upper right corner of the Inner Surface (x, y, z) in the main grid.

ratio

int of the ratio of the main grid spatial step to the sub-grid spatial step. Must be an odd integer. ratio=1 selects an equal-resolution embedded region: field values are transferred directly, without temporal or spatial interpolation, filtering, or a subgrid-boundary PML. It inherits the precision of the main CPU or CUDA grid.

id

required nonempty string identifier, unique within the Scene. Path separators, NUL, ‘.’ and ‘..’ are not allowed.

is_os_sep

int for the number of main grid cells between the Inner Surface and the Outer Surface. Defaults to 3.

pml_separation

retained for API compatibility, but the HSG formulation fixes the number of sub-grid cells between the Outer Surface and the PML at ratio // 2 + 2. Any supplied value is therefore ignored. Using a different value is experimental and requires changing the formulation.

subgrid_pml_thickness

int for the thickness of the PML on each of the 6 sides of the sub-grid. Defaults to 6. Ignored when ratio=1.

interpolation

int for the degree of the interpolation scheme used for spatial interpolation of the fields at the Inner Surface on CPU and CUDA. FITPACK spline degrees 1–5 are supported when each coarse precursor axis has more samples than the degree. Defaults to linear (1), the standard HSG choice. Higher degrees are experimental; matching the CPU does not guarantee stability. Ignored when ratio=1, where transfer is direct.

filter

boolean to turn on the 3-pole filter. Increases numerical stability. Defaults to True. Ignored when ratio=1.

A subgrid is added to the main scene, but its materials and geometry are added to the subgrid object. With autotranslate=True these objects can use main grid coordinates. Refining subgrids support the double-precision CPU and CUDA solvers. OpenCL, Metal, and distributed MPI subgrid execution are not supported.

Pass subgrid=True even for a geometry-only preview; a Scene containing subgrids without that flag is rejected before model construction. Every subgrid must have a nonempty id unique within its Scene. IDs cannot contain path separators or NUL, or be . or .., because they identify HDF5 groups.

ratio=1 selects an equal-resolution embedded region. This is exposed through SubGridHSG to avoid a second, overlapping object API, but it does not refine space or time: values are transferred directly on the shared Yee lattice, the subgrid-boundary PML and filter are disabled, and no spatial or temporal interpolation is performed. It inherits cpu_precision or gpu_precision from the main CPU or CUDA grid respectively. This mode is useful, for example, for confining dispersive material storage and updates to a local part of a larger model.

Only the auxiliary PML around the embedded region is disabled at ratio=1. Explicitly added PMLSlab absorbers remain active at each local time step.

For refining grids, interpolation=1 (linear) and filter=True are the standard defaults. The API also accepts FITPACK spline degrees 2–5 on both CPU and CUDA, provided each coarse precursor axis contains more samples than the chosen degree. CUDA uses the same spline boundary behaviour as CPU and keeps field interpolation on the device. Higher degrees are experimental: numerical parity does not establish stability or improve accuracy for every model. Both options are ignored for ratio=1.

Note

The HSG formulation fixes pml_separation at ratio // 2 + 2. The constructor retains the argument for API compatibility, but any supplied value is intentionally ignored. Changing this separation is experimental and requires modifying the formulation in the source code.

subgrid = gprMax.SubGridHSG(
    p1=(0.06, 0.04, 0.03), p2=(0.12, 0.10, 0.09),
    ratio=3, id='fine_grid',
)
scene.add(subgrid)

subgrid.add(gprMax.Material(
    er=4, se=0, mr=1, sm=0, id='subgrid_material'
))
subgrid.add(gprMax.Sphere(
    p1=(0.09, 0.07, 0.06), r=0.01,
    material_id='subgrid_material',
))

gprMax.run(
    scenes=[scene], n=1, outputfile='subgrid_model',
    subgrid=True, autotranslate=True,
)

PML functions

The default behaviour for the absorbing boundary conditions (ABC) is first order Complex Frequency Shifted (CFS) Perfectly Matched Layers (PML), with thicknesses of 10 cells on each of the six sides of the model domain. The PML can be customised using the following commands:

PML Formulation

class gprMax.user_objects.cmds_singleuse.PMLFormulation(formulation: str, id: Optional[str] = None)

Set the formulation of the PMLs.

Current options are to use the Higher Order RIPML (HORIPML) - https://doi.org/10.1109/TAP.2011.2180344, or Multipole RIPML (MRIPML) - https://doi.org/10.1109/TAP.2018.2823864.

formulation

Either ‘HORIPML’ or ‘MRIPML’.

Type

str

id

Reusable profile identifier. If omitted, set the global formulation used by domain PMLs and unqualified slabs.

Type

str, optional

PML Thickness

class gprMax.user_objects.cmds_singleuse.PMLThickness(thickness: Union[int, Tuple[int, int, int, int, int, int]])

Set the thickness of the PMLs.

The thickness can be set globally, or individually for each of the six sides of the model domain. Either thickness must be set, or all of x0, y0, z0, xmax, ymax, zmax.

thickness

Thickness of the PML on all 6 sides or individual sides of the model domain.

Type

int | Tuple[int]

For example, select the multipole formulation and set the thickness of each domain face independently, in the order x0, y0, z0, xmax, ymax, zmax:

scene.add(gprMax.PMLFormulation(formulation='MRIPML'))
scene.add(gprMax.PMLThickness(thickness=(12, 12, 10, 12, 12, 10)))

Symmetry Boundary

class gprMax.user_objects.cmds_multiuse.SymmetryBoundary(**kwargs)

Sets a PEC or PMC symmetry boundary condition on a model-domain face.

The selected boundary replaces the PML on that face. A PEC boundary forces the tangential electric-field component IDs to the built-in PEC material during grid construction. A PMC boundary uses an image-theory ghost-node update for the on-wall tangential electric fields.

PEC and PMC boundaries, including PMC boundaries in dispersive models, are supported by the CPU, CUDA, OpenCL, Metal, and domain-decomposed MPI CPU solvers. Symmetry boundaries are not supported in 2D mode or on a subgrid, although they may be used on the main grid of a model that contains subgrids.

face

One of x0, y0, z0, xmax, ymax, or zmax.

type

Either pec or pmc.

scene.add(gprMax.SymmetryBoundary(face='x0', type='pmc'))

Domain-decomposed MPI CPU models support the same PEC and PMC boundaries. Only ranks touching a selected global face construct and update it, and domain-edge corrections are not applied at internal MPI seams.

PML Properties

class gprMax.user_objects.cmds_singleuse.PMLProps(formulation: Optional[str] = None, thickness: Optional[int] = None, x0: Optional[int] = None, y0: Optional[int] = None, z0: Optional[int] = None, xmax: Optional[int] = None, ymax: Optional[int] = None, zmax: Optional[int] = None)

Specify the formulation and thickness of the PMLs.

A PML can be set on each of the six sides of the model domain. Current options are to use the Higher Order RIPML (HORIPML) - https://doi.org/10.1109/TAP.2011.2180344, or Multipole RIPML (MRIPML) - https://doi.org/10.1109/TAP.2018.2823864.

Deprecated: PMLProps is deprecated and may be removed in future releases of gprMax. Use the new PMLFormulation and PMLThickness user objects instead.

pml_formulation

User object to set the PML formulation.

Type

PMLFormulation

pml_thickness

User object to set the PML thickness.

Type

PMLThickness

Warning

PMLProps is retained for compatibility with older Python models. New models should use PMLFormulation, PMLThickness, and, when detailed coefficient control is needed, PMLCFS.

PML CFS

Allows you control of the specific parameters that are used to build each order of the PML. Up to a second order PML can currently be specified, i.e. by using two PMLCFS commands.

class gprMax.user_objects.cmds_multiuse.PMLCFS(**kwargs)
Controls parameters that are used to build each order of PML. Default

values are set in pml.py

alphascalingprofile

string required for type of scaling to use for CFS alpha parameter.

alphascalingdirection

string required for direction of scaling to use for CFS alpha parameter.

alphamin

float required for minimum value for the CFS alpha parameter.

alphamax

float required for maximum value for the CFS alpha parameter.

kappascalingprofile

string required for type of scaling to use for CFS kappa parameter.

kappascalingdirection

string required for direction of scaling to use for CFS kappa parameter.

kappamin

float required for minimum value for the CFS kappa parameter.

kappamax

float required for maximum value for the CFS kappa parameter.

sigmascalingprofile

string required for type of scaling to use for CFS sigma parameter.

sigmascalingdirection

string required for direction of scaling to use for CFS sigma parameter.

sigmamin

float required for minimum value for the CFS sigma parameter.

sigmamax

float required for maximum value for the CFS sigma parameter.

profile_id

optional reusable PML-profile identifier. If omitted, the parameters modify the global domain-PML configuration.

For example, the following explicitly requests the coefficient profiles used by the default first-order PML:

scene.add(gprMax.PMLCFS(
    alphascalingprofile='constant',
    alphascalingdirection='forward',
    alphamin=0, alphamax=0,
    kappascalingprofile='constant',
    kappascalingdirection='forward',
    kappamin=1, kappamax=1,
    sigmascalingprofile='quartic',
    sigmascalingdirection='forward',
    sigmamin=0, sigmamax=None,
))

The CFS values (which are internally specified) used for the default standard first order PML are:

  • alphascalingprofile = 'constant'

  • alphascalingdirection = 'forward'

  • alphamin = 0

  • alphamax = 0

  • kappascalingprofile = 'constant'

  • kappascalingdirection = 'forward'

  • kappamin = 1

  • kappamax = 1

  • sigmascalingprofile = 'quartic'

  • sigmascalingdirection = 'forward'

  • sigmamin = 0

  • sigmamax = None

Note

  • The parameters will be applied to all slabs of the PML that are switched on.

  • Using None for the maximum value of \(\sigma\) forces gprMax to calculate it internally based on the relative permittivity and permeability of the underlying materials in the model.

  • forward direction implies a minimum parameter value at the inner boundary of the PML and maximum parameter value at the edge of the computational domain, reverse is the opposite.

Selecting a stable second-order HORIPML profile

Duplicating two unshifted first-order HORIPML factors can create an unstable absorber. Coefficient construction rejects the demonstrated negative-real product-stretch profiles and identifies the slab/profile, electric or magnetic sample, and parameters. Reducing the timestep does not cure that profile instability.

Use one CFS factor, or repair the investigated unit-kappa configuration with an unshifted first factor by matching the second alpha grading to 1.1 * sigma1. Match both polynomial order and direction at every electric and magnetic sample. Alpha and sigma use the same units in these commands. This recipe is specific to that configuration, not arbitrary custom profiles. The full analytical condition and the tested 20,000-step cases are in Second-order HORIPML profile guard.

The check covers boundary, internal, and virtual-guide PMLs, including terminal samples and the global profile before partitioning. Explicit sigmamax=0 remains zero; only None requests automatic conductivity. Passing the check excludes the demonstrated failure mechanism, not every material, geometry, or mesh instability. The product argument is specific to HORIPML and must not be applied to MRIPML.

Reusable profiles and internal PML slabs

An optional id on PMLFormulation defines a reusable local profile instead of changing the global PML formulation. One or two PMLCFS objects may be associated with it through profile_id. A named formulation without named CFS terms uses the default first-order CFS parameters.

class gprMax.user_objects.cmds_multiuse.PMLSlab(**kwargs)

Place an experimental one-axis RIPML slab inside a grid.

p1 and p2 bound a rectangular region. maximum_face selects the local face at which complex stretching is greatest; the profile reduces to zero towards the opposite face. For example, x0 has its maximum at p1.x and opens towards increasing x. Unlike a domain-boundary PML, the slab is a correction applied to the existing geometry. By default, gprMax creates PEC plates on its four transverse faces and maximum-stretch face; faces coincident with the model boundary are omitted. The zero-stretch entrance remains open. Set build_pec=False to provide a custom or deliberately open enclosure. Exposed faces are then reported as warnings; incomplete enclosures have no stability guarantee. A slab added to an HSG subgrid must lie wholly within its working region. Domain-decomposed MPI CPU models may partition a slab normally or transversely; every rank stores only its local PML history while retaining the complete global CFS profile.

p1

lower physical (x, y, z) corner of the slab.

p2

upper physical (x, y, z) corner of the slab.

maximum_face

face at maximum stretching: x0, xmax, y0, ymax, z0, or zmax.

profile_id

optional reusable PML profile. If omitted, the global PML formulation and CFS parameters are used.

build_pec

create the five enclosing PEC plates. The default is True.

id

optional unique slab identifier. If omitted, gprMax generates one.

For example, a local MRIPML load can be placed inside a PEC rectangular guide:

scene.add(gprMax.PMLFormulation(formulation='MRIPML', id='port_load'))
scene.add(gprMax.PMLSlab(
    p1=(0.005, 0.010, 0.010),
    p2=(0.015, 0.020, 0.020),
    maximum_face='x0',
    profile_id='port_load',
    id='feed_absorber',       # optional API-only label
    build_pec=True,           # default: generate the PEC enclosure
))

The automatically generated PEC enclosure is applied after user geometry but before component averaging and PML coefficient generation. Four transverse walls and the maximum-stretch backing plate are generated unless a face is already on a model boundary; the opposite, zero-stretch face is the open entrance. Set build_pec=False for manually constructed or deliberately open experiments. gprMax then warns about exposed faces rather than rejecting the model. Such incomplete enclosures have no stability guarantee and require case-specific long-duration testing. The material cross-section must be invariant through the slab.

When id is omitted, gprMax assigns internal_pml_1, internal_pml_2, and so on. Internal slabs support the CPU, CUDA, OpenCL, and Metal solvers on the main 3D grid. A slab may also be added to an HSG subgrid, where it uses the CPU or CUDA solver and the local-grid update cycle, including equal-resolution regions with ratio=1. A subgrid-owned slab must lie wholly within the working region: overlap with its HSG coupling or auxiliary-PML regions is rejected.

Domain-decomposed MPI CPU models are supported. The user declaration remains in global coordinates and may cross normal or transverse rank boundaries. Each participating rank allocates only its local PML history arrays, but its coefficient slice is taken from the complete global CFS profile so a partition does not restart the grading. The ordinary field-halo exchanges join these local corrections; no additional slab-specific communication is required per timestep. Automatic PEC enclosure, collective material-extrusion checks, custom profiles, and use as a replacement for a disabled boundary PML retain their serial behaviour.