Model Output

Field(s) output

gprMax produces an output file that primarily contains time history data for electromagnetic field outputs (receivers) in the model. By default, a single run uses the input file’s basename with the .h5 extension: model.in produces model.h5. The output uses the widely-supported HDF5 format which was designed to store and organize large amounts of numerical data. There are a number of free tools available to read HDF5 files. MATLAB also has high- and low-level functions for reading HDF5 files, including h5info, h5disp, and h5read. The Utilities toolbox supplies gprmax_read_h5.m to load the complete hierarchy into a MATLAB structure and gprmax_h5_to_mat.m to create MATLAB v7.3 MAT files. These functions reconstruct complex arrays and retain gprMax’s documented multidimensional array ordering. Its modernised plot_Ascan.m and plot_Bscan.m functions use the stored physical timing and support receiver fields, subgrids, and merged terminal-voltage scans. gprMax also includes Python modules in the Plotting toolbox to view output data.

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.

File structure

The output file has the following HDF5 attributes at the root (/):

  • gprMax is the version number of gprMax used to create the output

  • Title is the title of the model

  • Iterations is the number of iterations for the time window of the model

  • nx_ny_nz is a tuple containing the number of cells in each direction of the model

  • dx_dy_dz is a tuple containing the spatial discretisation, i.e. \(\Delta x\), \(\Delta y\), \(\Delta z\)

  • dt is the time step of the model, i.e. \(\Delta t\)

  • srcsteps is the spatial increment used to move all sources between model runs.

  • rxsteps is the spatial increment used to move all receivers between model runs.

  • nsrc is the total number of sources in the model.

  • nrx is the total number of receievers in the model.

  • nports counts voltage-source and explicitly requested rational-network port outputs in ports; it does not include tls, frills or eigenmode_ports.

  • neigenmodeports is the number of eigenmode source/receiver port monitors.

  • SourceExcitationSchemaVersion identifies the schema used for exact source histories stored below local source groups.

  • ReceiverTimingSchemaVersion identifies the schema used for receiver dataset time offsets.

  • ReceiverOrderSchemaVersion=1 identifies the public receiver ordering contract. ReceiverOrder=construction means rank-independent construction order; ReceiverLayout identifies the ordered receiver declarations.

The output file contains HDF5 groups for sources (srcs), transmission lines (tls), magnetic frill sources (frills), receivers (rxs), voltage-source and rational-network ports (ports), SAR outputs (sar), radiometric absorption outputs (radiometry), and KSIR outputs (ntff) when requested. Eigenmode sources and receivers add eigenmode_ports. Within these are further groups for each named or numbered output.

Absorbed power density and specific absorption rate

A requested main-grid SAR output adds /sar/<id>; an output attached to a subgrid adds /subgrids/<subgrid ID>/sar/<id>. gprMax transforms only the unique Yee electric edges required by the selected tagged cells and only at the requested frequencies; it does not store a full time history for every cell. The serial CPU solver uses a Cython/OpenMP sparse transform. CUDA, OpenCL, and Metal accumulate the same sparse transforms in device memory and download only the completed complex edge spectra. Local SAR and any requested spatial mass averages are then evaluated on the host using the common implementation. With MPI domain decomposition, every rank transforms only the sparse electric edges needed near its selected tagged cells. After time stepping, uniquely owned edge spectra and owned-cell metadata are gathered once. The coordinator then collocates the global Yee-edge spectra, applies source or port normalisation, and performs any mass averaging on the complete tag volume. This avoids additional collectives inside the timestep loop and permits both tag regions and averaging cubes to cross rank boundaries. It also avoids depending on diagonal MPI halo values, which the field solver does not require and therefore does not exchange. The transform uses the engineering convention

\[\tilde{E}(f)=\int E(t)\exp(-j2\pi ft)\,\mathrm{d}t.\]

The three-dimensional Yee geometry is retained in both 3-D and reduced 2-D models. In 3-D, the four parallel Yee edges of each electric component are averaged as complex phasors to the cell centre. A 2-D TM model uses only the electric component parallel to the invariant axis, at invariant index zero; its four edges surround the cell in the transverse cross-section. A 2-D TE model uses the two electric components tangential to the invariant axis on the live central plane at invariant index one. Each TE component is averaged from its two genuine tangential edges. The second TE cell exists only to preserve the 3-D Yee staggering and is not a second physical SAR plane. Thus, for example,

\[\begin{split}\begin{aligned} E_{z,c}^{\mathrm{TMz}} &= \frac{1}{4} \left[E_z(i,j,0)+E_z(i+1,j,0)+E_z(i,j+1,0)+E_z(i+1,j+1,0)\right],\\ E_{x,c}^{\mathrm{TEz}} &= \frac{1}{2} \left[E_x(i,j,1)+E_x(i,j+1,1)\right],\\ E_{y,c}^{\mathrm{TEz}} &= \frac{1}{2} \left[E_y(i,j,1)+E_y(i+1,j,1)\right]. \end{aligned}\end{split}\]

The corresponding coordinate permutations apply to invariant x and y models. All inactive components remain zero. Two-dimensional TE/TM FDTD SAR is useful for rapid invariant-model dosimetry and polarisation studies [LWI2019]. With peak (not RMS) phasors, the local time-average absorbed power density and SAR are [IEEE62704-3]

\[\begin{split}\begin{aligned} p_{\mathrm{abs}}(f) &= \frac{1}{2}\sum_{a\in\{x,y,z\}} \sigma_{\mathrm{eff},a}(f)|E_a(f)|^2,\\ \mathrm{SAR}(f) &= \frac{p_{\mathrm{abs}}(f)}{\rho},\\ \sigma_{\mathrm{eff},a}(f) &= -\omega\epsilon_0 \operatorname{Im}\{\epsilon_{r,a}(f)\}. \end{aligned}\end{split}\]

Thus absorbed_power_density is in W/m3, density is in kg/m3, and sar is in W/kg. This evaluates dielectric/conductive loss directly and does not explicitly form a current-density array. The cell material supplies \(\sigma_{\mathrm{eff}}\) and \(\rho\); dielectric smoothing of Yee-edge update coefficients does not average mass density or tag membership.

For an isotropic material, all three loss spectra are identical and the expression reduces to \(\sigma_{\mathrm{eff}}|\mathbf E|^2/2\). Grid-aligned diagonal anisotropy uses the ordered x/y/z materials supplied to the volume primitive, including each direction’s dispersive poles. An arithmetic mean of directional conductivities is not used: it would generally give the wrong absorption for a polarised field. A zero field component contributes zero without renormalising the sum; zero static conductivity does not suppress loss from dispersive poles. General off-diagonal tensors are not supported. This is electric absorption only, not magnetic material heating.

Warning

SAR and radiometry do not include magnetic absorption. A runtime warning identifies magnetic materials (relative permeability different from one or nonzero magnetic conductivity) in the selected cells outside PML, including directional material constituents. Magnetic properties still affect the FDTD fields; they are not ignored by the solver. When magnetic conductivity is nonzero, the reported absorption and SAR are not total absorption or total SAR, and radiometric weights omit that magnetic loss. Lossless permeability alone adds no magnetic heating. Ideal PMC constraints are not treated as finite-conductivity absorbers for this warning.

For SAR, the three directional material definitions must all specify the same positive mass density. Missing or conflicting densities stop the calculation; they are not averaged. Radiometry has no density requirement. Spatial interpolation of each complex electric component from its Yee edges to the cell centre remains separate from this directional loss calculation.

Because a finite transient spectrum is not itself a continuous-wave excitation, waveform normalisation divides the field DFT by the DFT of the selected source waveform and multiplies it by TargetAmplitude. Exactly one active source may use that waveform. For a Hertzian electric dipole, current_moment divides by the product of waveform spectrum and source-edge length, so TargetAmplitude is \(I\ell\) in A m. For a discrete plane wave, the source waveform is the incident electric-field magnitude in V/m. It can either be used directly or converted to an incident power flux using

\[S_{\mathrm{inc}}(f)=\frac{1}{2}|E_{\mathrm{inc}}(f)|^2 \operatorname{Re}\!\left\{\frac{1}{\eta(f)}\right\}, \qquad \eta(f)=\sqrt{\frac{\mu(f)}{\epsilon(f)}}.\]

The incident_flux mode scales to TargetFlux in W/m2; the complex permittivity and permeability of the plane wave’s source-side material are used at every requested frequency. Alternatively, incident- or accepted-power normalisation multiplies the field DFT by

\[C_P(f)=\sqrt{\frac{P_{\mathrm{target}}}{P_{\mathrm{port}}(f)}}.\]

The requested port power is obtained through the same terminal/modal power adapter used for antenna gain. Non-positive, non-finite, or invalid port powers produce invalid SAR bins rather than unbounded scaling. Frequencies below SourceFloorDB are likewise marked invalid and stored as NaN. Transforms of stored source histories evaluate their phase arguments in at least double precision, even for single-precision simulations. This reduces phase-rounding errors at spectral notches; it does not make normalisation by an arbitrarily weak source spectrum reliable. The source-support cutoff and validity masks still apply, and the transform output retains the configured complex precision. Source and port normalisation are resolved across the complete model rather than only the grid containing the SAR output. Consequently, a source on the main grid can illuminate and normalise tagged tissue sampled at every fine timestep inside a subgrid. A subgrid port uses the model-wide qualified reference <subgrid ID>/<port ID>.

The datasets include frequency, cell_indices, tag_id, material_id, density, source_spectrum, source_relative_db, source_valid, incident_power, incident_flux, mesh_valid, valid, cells_per_wavelength, limiting_material, absorbed_power_density, and sar. Each tags/<name> subgroup contains cell count, integrated mass and absorbed power, mass-average SAR, and peak voxel SAR. CellIndexOrigin and CellCentreOffset map the integer cell_indices to physical cell-centre positions. CellIndexFrame is main-grid for a normal output and subgrid-local for an HSG output. MPI output uses globally sorted main-grid cell indices and the same HDF5 schema as the serial solver. The gathered sparse spectra and completed frequency-by- cell arrays reside on the coordinator during finalisation; for extremely large tag volumes this coordinator memory is the present scalability limit.

MPI material_id values refer to the output-local materials/id and materials/name catalogue. Generated directional materials can have different numeric IDs on different ranks; their definitions are gathered once after time stepping, without changing the reusable grid’s material IDs.

In a 2-D model, local absorbed_power_density and sar retain their ordinary W/m3 and W/kg units. Integrated quantities describe the invariant geometry per unit length:

\[P'_{\mathrm{abs}}(f) = \int_A p_{\mathrm{abs}}(f)\,\mathrm{d}A, \qquad m' = \int_A \rho\,\mathrm{d}A, \qquad \overline{\mathrm{SAR}} = \frac{P'_{\mathrm{abs}}}{m'}.\]

Consequently, a 2-D tag subgroup contains MassPerLength in kg/m and absorbed_power_per_length in W/m. Its mass_average_sar remains in W/kg. Dimensionality, ModelMode, Polarisation, InvariantAxis, LiveInvariantIndex, and ActiveElectricComponents make the convention explicit. Port powers used for 2-D normalisation are likewise interpreted in W/m rather than W.

The TMz and TEz cell collocation, local SAR, and absorbed power per unit length are compared with exact lossy-cylinder series in Two-dimensional SAR in lossy dielectric cylinders.

Spatial averaging is not performed by default. This keeps the application- neutral local quantities available without imposing a bioelectromagnetic 1 g/10 g convention or its potentially substantial post-processing cost. When one or more target masses are explicitly requested, the output also writes spatial_average/<mass>g. The standard 1 g and 10 g outputs are requested with averaging_masses=(0.001, 0.01). They use the two-step cubical mass-averaging procedure of IEC/IEEE 62704-1 [IEEE62704-1]: first a cube is expanded symmetrically about each tissue voxel; remaining boundary voxels are then evaluated with six face-centred cubes. During centred-cube growth every complete voxel shell is checked to ensure that each of its six faces touches or intersects tissue. A cell is marked as covered when more than 99.9% of its volume lies in a valid centred cube. Fractional boundary-voxel volumes are used to reach the target mass. Density is piecewise constant within each tagged FDTD cell: a fractional contribution has mass

\[\Delta m_i = q_i\,\rho_i\,\Delta x\Delta y\Delta z,\]

where \(0\leq q_i\leq1\) is only the geometrical fraction inside the averaging cube. Density is not interpolated or Yee-edge averaged. The selected tags collectively define tissue; cells outside them are background. Each mass group contains sar, status, averaging_mass, averaging_volume, orientation, peak_sar, and peak_cell. Status values are 0 invalid/background, 1 unused boundary, 2 covered by a valid centred cube, and 3 a valid centred-cube location. Orientation 7 is a centred cube and 1–6 identify the face-centred directions.

The density-, tag-, and grid-dependent averaging-cube geometry is constructed once for each requested mass and reused for every frequency. The compiled OpenMP implementation therefore avoids repeating the expensive target-mass search for a frequency sweep.

Spatial 1 g/10 g averaging is currently restricted to 3-D models. A reduced 2-D field solution is invariant, so such an average would require a deliberate virtual extrusion of the density, tags, and fields along the invariant axis; the one-cell TM and two-cell TE storage thicknesses must not be treated as a finite anatomical volume. Until that invariant-extrusion procedure is added and separately validated, a 2-D SAR request must leave averaging_masses=().

Cells inside either a domain-boundary PML or an internal PML slab are always excluded, even when their geometry tag is selected. PML attenuation is a numerical coordinate stretching and must not be counted as physical material absorption, tissue mass, or SAR. The group attributes PMLCellPolicy and ExcludedPMLCellCount record this operation.

The requested frequencies must be below temporal Nyquist. By default they must also have at least ten cells per shortest wavelength in every model material; a lambda/8 criterion may be selected. In a 2-D TM or TE model this check uses only the two propagation directions, excluding the invariant-axis thickness. In 3-D it uses all three spatial steps. The same rule applies to port-output wavelength validation. The nyquist research mode retains frequencies outside that spatial criterion and records the mesh validity metadata, but does not make those results physically reliable.

For lossy or dispersive media this output guard uses \(c/(f|\sqrt{\varepsilon_r^*\mu_r^*}|)\) as a conservative spatial scale, including both electric and magnetic loss. It is not the physical phase wavelength. The separate pre-simulation spatial-resolution diagnostic reports phase wavelength and attenuation length separately.

Radiometric absorption weighting

A Radiometry request writes /radiometry/<id> on the main grid, or /subgrids/<subgrid ID>/radiometry/<id> on a subgrid. It uses the same sparse electric-field transforms, cell-centre collocation, \(\sigma_{\mathrm{eff}}\), source/port normalisation, PML exclusion, and mesh-validity checks as SAR. It deliberately does not use mass density and does not calculate SAR or a 1 g/10 g average. This makes it suitable for lossy geological and planetary materials for which density is unavailable or irrelevant to the electromagnetic absorption calculation.

The electric-only absorption limitation and its runtime warning apply to all radiometry outputs and normalisations.

The principal local quantities are absorbed_power_density and normalised_absorption_density. The latter has a definition appropriate to the selected excitation:

\[\begin{split}w(\mathbf r,f)= \begin{cases} p_{\mathrm{abs}}(\mathbf r,f)/P_{\mathrm{ref}}, & \text{incident or accepted port power},\\ p_{\mathrm{abs}}(\mathbf r,f)/S_{\mathrm{inc}}, & \text{incident plane-wave flux},\\ p_{\mathrm{abs}}(\mathbf r,f)/|q_{\mathrm{src}}|^2, & \text{portless source amplitude}. \end{cases}\end{split}\]

This is the active-mode reciprocity construction used for microwave radiometry. At one frequency the cell weight for an incident-power reference is

\[C_n(f)=\frac{P_{\mathrm{abs},n}(f)}{P_{\mathrm{inc}}(f)},\]

and Wu and Nieh’s finite-band weighting factor is

\[\overline{C}_n=\frac{1}{B} \int_{f_0-B/2}^{f_0+B/2} \frac{P_{\mathrm{abs},n}(f)}{P_{\mathrm{inc}}(f)}\,\mathrm{d}f.\]

The normalised_absorption values are the per-frequency integrand; a receiver-specific quadrature over frequency remains post-processing because its bandshape is not an FDTD property. The equivalent continuous weighting function commonly used in radiometric forward models is

\[W(\mathbf r,f)=\frac{p_{\mathrm{abs}}(\mathbf r,f)} {\int_V p_{\mathrm{abs}}(\mathbf r,f)\,\mathrm{d}V}, \qquad p_{\mathrm{abs}}=\frac{1}{2}\sum_{a\in\{x,y,z\}} \sigma_{\mathrm{eff},a}|E_a|^2,\]

which integrates to unity over the chosen receiving volume [WU1995] and [ROD2013]. It can be formed from normalised_absorption_density without rerunning the model. Normalising to accepted rather than incident power removes mismatch loss from the spatial weight but does not otherwise change its shape.

For a 3-D power-normalised port, \(w\) has units m-3 and its volume integral is the fraction of the reference power absorbed in a tag. For plane-wave flux, it has units m-1 and its volume integral is an absorption cross section in m2. A 2-D invariant model instead integrates over area and reports, respectively, a dimensionless absorbed fraction or an absorption cross section per unit invariant length in metres. For a portless source the integral is absorbed power per squared native source amplitude; examples include V/m for a plane-wave waveform, A m for Hertzian current moment, and V for a voltage source.

Each tags/<name> subgroup stores the integrated physical absorbed power and normalised_absorption. These tag-resolved weighting quantities are the electromagnetic part needed by radiometer forward models. gprMax does not yet combine them with physical temperature, receiver bandwidth, receiver noise, or a radiative-transfer model to produce brightness or antenna temperature; those are separate measurement-model inputs rather than FDTD field quantities.

Study outputs

Reusable parameter studies add a root study group. Its attributes are Type, CaseID, CaseIndex (one based), CaseCount, GeometryReused, and, for CSV studies, SourcePath. The source dataset preserves the complete CSV text. definition stores the normalised full study as JSON, while resolved_case contains the post-validation values actually applied to every study-managed object, including baseline receivers and implicitly disabled sources. Study-managed source and receiver groups also carry a StudyID attribute, including the tls and frills groups used by a source study. This provides an unambiguous link back to the case table.

For a plane-wave study, resolved_case records the requested case together with the actual rationalised propagation angles and integer DPW mapping. Each case remains an ordinary numbered model output; no aggregate matrix is needed because RCS patterns from different illuminations are independent results.

For a source study, resolved_case records every stateful terminal, including sources omitted from the sparse case table. active=false and scale=0 identify a passive physical termination rather than an object removed from the model. Transmission-line, magnetic-frill, rational-network, receiver, port, and declarative NTFF datasets in each numbered file contain only that case’s reset state.

Every case in a finite-resistance voltage-port study additionally contains study/port_response. DrivenSourceID and DrivenPortID identify its input port; S_source_column contains the power-normalised raw source-plane response for every output port. gap_correction_c stores the dimensionless parallel-gap admittance \(Z_{0,p}Y_{\mathrm{gap},p}\) used by the matrix correction.

After all requested cases finish, gprMax writes <output>_study.h5. Its principal datasets are:

  • frequency: common frequency axis;

  • port_ids and source_ids: output-port and deterministic source order;

  • case_ids: case contributing each input-port column;

  • reference_impedance: positive real wave-reference impedance per port;

  • gap_correction_c: numerical gap shunt correction for every frequency and port;

  • S_source: uncorrected source-plane S matrix;

  • S: matrix after removing all numerical gap admittances;

  • valid_S_source and valid_S: element validity masks.

Both matrices use axis order S[frequency, output_port, input_port]. The Complete attribute states whether every input-port column is present. A restarted study loads compatible columns from an existing aggregate file and replaces the newly evaluated columns; an incompatible existing file is rejected rather than mixed with new results.

Every eigenmode-study case instead contains study/eigenmode_response. InputPort and InputMode identify the nominal incident modal channel. incident and outgoing contain the complete measured modal vectors for that run, ordered by channel_ports and channel_modes; power_wave_valid and coefficient_valid_wave are their validity masks. S_column is the single-source-normalized per-run diagnostic retained alongside those raw waves. It is not the authoritative column when passive ports have residual incident waves.

The eigenmode aggregate <output>_study.h5 contains frequency, channel_ports, channel_modes, case_ids, incident_matrix, outgoing_matrix, power_wave_valid_matrix, coefficient_valid_wave_matrix, deembedding_condition_number, deembedding_valid, S, power_wave_valid_S, and coefficient_valid_S. Only these descriptive validity names are supported. Its convention is S[frequency, output_channel, input_channel]. Modal channels can represent different mode counts on different ports; the two channel-index datasets are therefore authoritative and should be used instead of assuming one mode per port. The aggregate \(S\) satisfies \(B=SA\) for the stored measured incident matrix \(A\) and outgoing matrix \(B\); it is calculated by a conditioned solve rather than independent scalar division. A restarted study retains the raw case columns and repeats this complete solve. Both run matrices use [frequency, modal_channel, excitation_case] axis order.

When an array codebook is attached, the same aggregate may also contain:

/array_codebook/
    definition [canonical JSON, always present]
    source [original JSON text, when loaded from disk]
/embedded_far_fields/<transform_id>/<output_id>/
    frequency
    theta
    phi
    Etheta                         [frequency, direction, channel]
    Ephi                           [frequency, direction, channel]
    valid                          [frequency, channel]
    sphere/
        theta
        phi
        weights
        Etheta                     [frequency, sphere_direction, channel]
        Ephi                       [frequency, sphere_direction, channel]
    raw_runs/
        Etheta                     [frequency, direction, excitation_case]
        Ephi                       [frequency, direction, excitation_case]
        valid                      [frequency, excitation_case]
        sphere/{Etheta,Ephi}
/array_states/<state_id>/
    drives/{port,mode,power_w,phase_deg,delay_s}
    incident
    outgoing
    active_reflection
    incident_power
    reflected_power
    accepted_power
    tarc
    valid
    far_fields/<transform_id>/<output_id>/
        Etheta
        Ephi
        radiation_intensity
        radiated_power
        directivity
        directivity_dbi
        gain
        gain_dbi
        realized_gain
        realized_gain_dbi
        radiation_efficiency
        total_efficiency
        maximum_directivity
        maximum_directivity_dbi
        maximum_theta
        maximum_phi
        valid

The embedded fields have units of range-normalized field per square root watt of incident modal power. The requested-direction and full-sphere arrays retain complex fields, rather than per-channel directivity or power, so coherent cross terms are included when a state is synthesized. The sphere weights integrate radiation intensity over solid angle. valid is false wherever an active input does not have a physical propagating power-wave S coefficient; such bins are stored as NaN for power-derived state results. definition is a complete portable representation even when the codebook was constructed with the Python API; source preserves the user’s original text when it came from a JSON file.

/
    study/ [optional]
        source [CSV studies]
        definition
        resolved_case
        port_response/ [port studies]
            port_ids
            source_ids
            frequency
            reference_impedance
            gap_correction_c
            S_source_column
            valid_S_source_column
        eigenmode_response/ [eigenmode studies]
            channel_ports
            channel_modes
            frequency
            S_column
            power_wave_valid_S_column
            coefficient_valid_S_column
            incident
            outgoing
            power_wave_valid
            coefficient_valid_wave
    rxs/
        rx1/
            Name
            Position
            Ex
            Ey
            Ez
            Hx
            Hy
            Hz
            Ix [optional]
            Iy [optional]
            Iz [optional]
        rx2/
            ...
    srcs/
        src1/
            Type
            Position
            ID
            GridPosition
            Polarisation
            excitation/
                samples
        src2/
            ...

    tls/
        tl1/
            Position
            Resistance
            dl
            Vinc
            Iinc
            Vtotal
            Itotal
            frequency
            S11
            Zin
            Yin
            S11_current
            Zin_current
            ...
        tl2/
            ...
    frills/ [optional]
        frill1/
            Position
            Polarisation
            Z0
            Mirror1Face
            Mirror2Face
            Mirror1
            Mirror2
            Vinc
            Vtotal
            Itot
            frequency
            S11
            Zin
            Yin
            ...
        frill2/
            ...
    ntff/ [optional]
        <monitor name>/
            ...
    ports/ [optional]
        <port ID>/
            frequency
            S11
            Zin
            Yin
            ...
    eigenmode_ports/ [optional]
        port1/
            frequency
            incident
            outgoing
            electric_cross_power_matrix
            power_matrix
            anchor_mode_valid
            anchor_mode_reference_valid
            anchor_mode_propagating
            anchor_balanced_power
            reference_basis_valid
            coefficient_valid
            power_basis_valid
            power_matrix_valid
            power_wave_valid
            condition_number
            S
            coefficient_valid_S
            power_wave_valid_S
            active_S
            active_S_driven
            coefficient_valid_active_S
            power_wave_valid_active_S

Within each individual rx group are the following attributes:

  • Name is the name of the receiver if specified. Otherwise ‘Rx(x,y,z)’, where x,y,z is the position of the receiver, is used.

  • Position is the x, y, z position (in metres) of the receiver in the model.

  • GridPosition is the integer x, y, z position of the receiver on its owning grid.

  • BuildIndex is the zero-based public construction ordinal within this grid. For ordinary solver output, rxN has BuildIndex=N-1.

  • NameKind distinguishes a user label (user) from an automatically generated coordinate label (generated).

Receiver numbering and cross-version processing

Public receivers are numbered in construction order, independently of their names and the MPI partition. Receiver arrays follow their x/y/z traversal order (z varies fastest). Private port samplers do not consume public numbers. Each subgrid has its own numbering namespace. The solver’s runtime receiver list and accelerator buffer pages are not reordered when writing a file.

Construction order follows command build priorities: individual Rx commands are built before RxArray commands. It is not a promise of literal input-line order across interleaved command types. The ordinal survives MPI migration and geometry reuse. ReceiverLayout is a SHA-256 digest of the ordered declarations (explicit label or null, sorted requested components, study ID). Coordinates are intentionally excluded so a declared acquisition can move between runs. This digest describes an acquisition layout, not a model UUID or proof that two arbitrary simulations are physically equivalent. Do not reorder anonymous declarations between runs and assume their ordinal identities remain the same. Prefer unique names for independently built models.

Earlier v4 development outputs sorted receiver Names lexicographically; v3 used insertion order. Neither has the new ordering metadata. Consequently, the same /rxs/rx1 path can refer to different receivers across versions. Existing files are not rewritten. Low-level serial writers given receiver objects without build ordinals report ReceiverOrder=runtime and do not claim stable construction identities. Distributed output requires ordinals.

The current Python merge, SEG-Y/SEG-2/DT1 collection and Marimo stacking tools anchor the selected receiver in the first file, then match its identity in later files. Unique StudyID takes precedence, followed by unique Name; matching construction layout/index also supports generated or duplicate names. For generated names in the same layout, the ordinal takes precedence over coordinates embedded in the name, which may change during a scan. Ambiguous matches fail (Marimo skips the trace with a warning). Legacy single-receiver files without labels remain supported; unlabeled or duplicate-labeled legacy multi-receiver files require explicit, verified per-file selections rather than an inferred mapping. Positions may move and are kept with their samples.

Merging retains the first file’s paths and identity metadata. Each receiver’s trace_metadata/rxs/rxN/InputReceiverPath and Name datasets retain its per-input mapping alongside the existing per-trace positions. Derived ImpulseResponse files retain selected receivers’ original paths/indices; these subsets can contain gaps, so readers should enumerate actual groups and sort numeric suffixes rather than construct paths from 1..nrx.

Use name-based selections when migrating saved scripts:

from gprMax.toolboxes.SFCW.processing import load_receiver
trace = load_receiver("run.h5", "name:surface", "Ez")

from gprMax.toolboxes.Utilities.outputfiles_merge import get_output_data
data, dt = get_output_data("merged.h5", 1, "Ez", receiver_name="surface")

SFCW/FMCW --receiver and ImpulseResponse receiver selections also accept name:surface or study:identifier. ImpulseResponse CLI syntax appends the component, for example --receiver name:surface:Ez. Export utilities and Python B-scan plotting accept --trace-group name:surface. Export --receiver N selects N in the first input file. The shared Python receiver_identity utility also offers per-grid build:N selection.

FMCW automatically identity-matches an omitted background/incident receiver. An explicitly supplied background/incident receiver is an intentional per-file override, allowing reference measurements at a different point. AntennaPatterns still uses its own zero-padded radius/angle Names to order bins; do not replace that consumer-side sort with public group order. Native ports and NTFF retain their separate ID-based namespaces.

MATLAB plot_Ascan and plot_Bscan accept ReceiverName (and Grid for a subgrid). gprmax_receiver_path(file, name, grid) resolves a unique Name for lower-level readers/converters. Numeric/path selections remain file-local. The interactive legacy converter accepts only a single receiver; use an explicitly selected modern reader/exporter for multi-receiver data.

Upgrade processing tools together with the solver. Old installed tools and external scripts cannot infer this mapping from numeric paths alone. Review stored numeric selections against Name/StudyID and positions. Regression comparisons now match receiver identities, but strict whole-group/file HDF5 comparisons will still report the new metadata: verify the signals and then deliberately update reference files, rather than regenerating them blindly.

Within each individual rx group can be the following datasets:

  • Ex is an array containing the time history (for the model time window) of the values of the x component of the electric field at that receiver position.

  • Ey is an array containing the time history (for the model time window) of the values of the y component of the electric field at that receiver position.

  • Ez is an array containing the time history (for the model time window) of the values of the z component of the electric field at that receiver position.

  • Hx is an array containing the time history (for the model time window) of the values of the x component of the magnetic field at that receiver position.

  • Hy is an array containing the time history (for the model time window) of the values of the y component of the magnetic field at that receiver position.

  • Hz is an array containing the time history (for the model time window) of the values of the z component of the magnetic field at that receiver position.

  • Ix is an optional array containing the time history (for the model time window) of the values of the x component of current (calculated around a single cell loop) at that receiver position.

  • Iy is an optional array containing the time history (for the model time window) of the values of the y component of current (calculated around a single cell loop) at that receiver position.

  • Iz is an optional array containing the time history (for the model time window) of the values of the z component of current (calculated around a single cell loop) at that receiver position.

Every receiver component dataset has SampleInterval, TimeSampleOffset, and Quantity attributes. Electric fields have TimeSampleOffset=0 and represent \(E^n\). Magnetic fields and magnetic-loop currents have TimeSampleOffset=-\Delta t/2 and represent \(H^{n-1/2}\) at stored sample index \(n\). These explicit physical times are important when combining electric and magnetic quantities or deconvolving a source. Receivers are observed before each field update. Sample zero is the initial state: magnetic fields are zero, but an active zero-resistance voltage source can prescribe a nonzero electric field at its edge before this observation.

Within each individual src group are the following attributes:

  • Type is the type of source, e.g. Hertzian dipole, voltage source etc…

  • Position is the x, y, z position (in metres) of the source in the model.

  • ID, GridPosition, and Polarisation identify the source and its electric or magnetic Yee component.

Each supported local source also contains an excitation group. Its samples dataset is the exact scalar history consumed by the solver, with the update look-ahead sample removed. SampleInterval and TimeSampleOffset define the physical time of sample \(n\) as \(t_n=n\Delta t+t_0\); DrivingQuantity, Units, SpatialScale, UpdateLattice, and waveform attributes describe the excitation. A WaveformEvaluationTimeOffset attribute separately records the time used to evaluate the waveform function for source sample \(n\). A resistive voltage source and Hertzian electric dipole, for example, use \(t_0=\Delta t/2\). Hard voltage sources initialise \(E^0\) and then prescribe \(E^{n+1}\) at \((n+1)\Delta t\); their two offsets are zero. Their history contains samples 0 through \(N-1\), including the initial prescription, but not the terminal \(E^N\) update. Older hard-source files have physical offset \(\Delta t\) and evaluation offset zero; readers must honour the stored metadata rather than assuming either convention. Transmission-line sources store their two staggered histories as samples_whole and samples_half; samples is a non-duplicating HDF5 link to the whole-step generator-voltage reference.

This timing-aware source schema is used by the impulse-response waveform-synthesis toolbox, the SFCW toolbox, and the FMCW toolbox, and is also available for other file-based deconvolution and post-processing.

Within each individual tl group are the following attributes:

  • Position is the x, y, z position (in metres) of the source in the model.

  • Resistance is the resistance of the transmission line.

  • dl is the spatial discretisation of the transmission line.

  • ReferenceImpedance is the real reference impedance used for S11 and is equal to Resistance.

  • SpectrumLimitMode, MinimumWavelengthCells, MeshFrequencyLimit, NyquistFrequency, and LimitingMaterial describe the automatically selected frequency band.

  • ZinPrimaryMethod identifies the voltage-wave S11 result as the primary impedance calculation. CurrentCheckMethod identifies the independent discrete-line current-wave calculation.

Within each individual tl group are the following datasets:

  • Vinc is an array containing the time history (for the model time window) of the values of the incident voltage in the transmission line.

  • Iinc is an array containing the time history (for the model time window) of the values of the incident current in the transmission line.

  • Vtotal is an array containing the time history (for the model time window) of the values of the total (field) voltage in the transmission line.

  • Itotal is an array containing the time history (for the model time window) of the values of the total (field) current in the transmission line.

  • frequency is the authoritative non-negative frequency axis in Hz.

  • Vincident_spectrum, Vreflected_spectrum, and Vtotal_spectrum are the complex voltage spectra.

  • Iincident_spectrum and Itotal_spectrum are the complex current spectra after accounting for the current’s half-time-step staggering.

  • S11 is the voltage-wave reflection coefficient \((V_\mathrm{total}-V_\mathrm{inc})/V_\mathrm{inc}\).

  • Zin and Yin are the input impedance and admittance derived from S11 and the line resistance.

  • S11_current and Zin_current are independent checks calculated from the line current. The current is displaced from the voltage node by both half a time step and half a line cell; gprMax removes both offsets using the discrete 1D transmission-line dispersion relation. A simple phase shift of the total current alone is not sufficient because incident and reflected current waves require opposite spatial corrections.

  • valid_S11, valid_Zin, valid_Yin, valid_S11_current, and valid_Zin_current are per-bin integer masks. source_valid, mesh_valid, line_propagation_valid, incident_relative_dB, and cells_per_minimum_wavelength provide the corresponding diagnostics.

Within each individual frill group are the following attributes:

  • Position is the x, y, z position (in metres) of the feed point in the model.

  • Polarisation is the antenna axis the source drives current along (x, y, or z).

  • Z0 is the user-supplied characteristic impedance (Zcoax), and is also the reference impedance used for S11.

  • InnerConductorRadius is the radius \(a\) obtained from the mandatory co-located #thin_wire.

  • CurrentTimeApproximation is average for Hyun’s recommended average of the adjacent magnetic half-step currents.

  • FeedSelfAdmittance is the precomputed Cartesian feed-cell coefficient \(G_f\) (Siemens) used to solve the current feedback in closed form.

  • TimeOffset is zero: Vinc, Vtotal, and the averaged Itot are all centred at integer electric-field times.

  • Mirror1Face and Mirror2Face name the two domain faces transverse to Polarisation (for example x0/y0 for a z-polarised source). Mirror1 and Mirror2 record whether the feed point actually sits on a symmetry-plane corner declared with #symmetry_boundary at that face.

  • SpectrumLimitMode, MinimumWavelengthCells, MeshFrequencyLimit, NyquistFrequency, and LimitingMaterial describe the automatically selected frequency band, as for tl groups.

  • ZinPrimaryMethod identifies the voltage-wave S11 result as the primary impedance calculation - there is no separate current-based cross-check here, unlike a transmission line, since the frill’s voltage and current histories are solved together at the same instant, not staggered.

Within each individual frill group are the following datasets:

  • Vinc is an array containing the time history of the incident voltage (half the generator waveform).

  • Vtotal is an array containing the time history of the total terminal voltage \(V_\mathrm{ab}\).

  • Itot is an array containing the time history of the total current at the feed point, averaged from the adjacent magnetic half steps and generalised for a symmetry-mirrored feed point.

  • frequency, S11, Zin, Yin, and their associated valid_S11/valid_Zin/valid_Yin/source_valid/mesh_valid/ incident_relative_dB/cells_per_minimum_wavelength diagnostics have the same meaning as the corresponding tl datasets.

Transmission-line S11 and impedance output

S11, input impedance, and input admittance are generated automatically for every transmission-line source; no additional receiver is required. With the real line resistance \(Z_0\) as the reference impedance, the primary results are

\[S_{11}=\frac{V_\mathrm{total}-V_\mathrm{inc}}{V_\mathrm{inc}}, \qquad Z_\mathrm{in}=Z_0\frac{1+S_{11}}{1-S_{11}}, \qquad Y_\mathrm{in}=\frac{1-S_{11}}{Z_0(1+S_{11})}.\]

This S11-based Zin is the primary result because direct division by the terminal current becomes ill-conditioned at frequencies where that current approaches zero. The independently de-embedded current result remains in Zin_current so that the line coupling and the voltage-wave result can be checked. Algebraically undefined open- or short-circuit quantities are stored as complex NaNs and identified by their validity masks.

The stored spectrum uses the native resolution \(\Delta f=1/T\) and is capped automatically at the lambda/10 mesh limit of the most restrictive material in the model. The original Vinc, Iinc, Vtotal, and Itotal histories remain available for users who need to reprocess a wider research band.

For example, the valid S11 and impedance bins can be read directly:

import h5py
import numpy as np

with h5py.File('model.h5', 'r') as output:
    line = output['tls/tl1']
    frequency = line['frequency'][...]
    s11 = line['S11'][...]
    zin = line['Zin'][...]
    valid = (
        line['valid_S11'][...].astype(bool)
        & line['valid_Zin'][...].astype(bool)
    )

s11_db = 20 * np.log10(np.abs(s11[valid]))
resistance = zin.real[valid]
reactance = zin.imag[valid]

Magnetic-frill-source S11 and impedance output

S11, input impedance, and input admittance are generated automatically for every #magnetic_frill_source, exactly as for a transmission line; no additional receiver is required (the electric field component along the polarisation axis is identically zero at the feed point by construction, so no field sample is possible there in the first place). Main-grid results are stored at /frills/frillN. A Python API frill inside a subgrid is stored at /subgrids/<subgrid ID>/frills/frillN; its Position is reported in global physical coordinates, while its histories, frequency axis, and validity limits use the owning subgrid’s finer dt and dl. With the frill’s user-supplied characteristic impedance \(Z_0\) as the reference impedance,

\[S_{11}=\frac{V_\mathrm{ab}-V_\mathrm{inc}}{V_\mathrm{inc}}, \qquad Z_\mathrm{in}=Z_0\frac{1+S_{11}}{1-S_{11}}, \qquad Y_\mathrm{in}=\frac{1-S_{11}}{Z_0(1+S_{11})}.\]

Unlike a transmission line, there is no independent current-based cross-check: \(V_\mathrm{inc}\), \(V_\mathrm{ab}\), and \(I_\mathrm{tot}\) are centred at the same integer time. The current is Hyun’s average of the preceding and following magnetic half-step values; the following value depends on the voltage applied in that update, so gprMax solves the resulting feed-cell relation analytically. There is therefore no remaining leapfrog phase shift to de-embed. \(I_\mathrm{tot}\) is formed directly from the mandatory thin wire’s stored Mäkinen-projected H edges, consistent with the \(F k_H\) source deposit. At every sample the stored histories satisfy

\[V_\mathrm{ab}=2V_\mathrm{inc}-Z_0 I_\mathrm{tot}.\]

Set spectrum_limit directly on the magnetic-frill source when a limit other than the default lambda/10 band is required.

Rational-network port output

The #network_port command and NetworkPort Python object write a rational terminal beneath /ports/<terminal ID>. The external-network current is defined positive from the FDTD gap into the network,

\[I_N(s)=Y(s)\left[V(s)-V_g(s)\right], \qquad Y(s)=G+sC+\sum_m\frac{r_m}{s-p_m}.\]

Here \(V_g=0\) for a passive terminal. For antenna and accepted-power calculations, the current entering the electromagnetic structure removes the numerical parallel Yee-gap admittance,

\[I_\mathrm{terminal}=-I_N-Y_\mathrm{gap}V, \qquad V^\pm=\frac{V\pm Z_0I_\mathrm{terminal}}{2}.\]

For each rational term, \(\dot{x}_m=p_mx_m+r_mu\), where \(u=V-V_g\). Its value at any fraction \(\theta\) of a time step is calculated without a finite-difference approximation to \(x_m\),

\[x_m^{n+\theta}=e^{\theta p_m\Delta t}x_m^n +a_{m,\theta}u^{n+1}+b_{m,\theta}u^n,\]

with the coefficients obtained from the exact convolution of an exponential pole with a linearly varying voltage. The implementation uses \(\theta=1/2\) directly for the locally implicit Ampere update and \(\theta=1\) to advance stored state. This is the relevant Giannakis–Giannopoulos improvement [GIA2014] to the classic PLRC lumped-network treatment of [PER1999] and [CHE2007]: the half-step pole current is obtained directly rather than by averaging adjacent integer-time values.

The reported quantities are \(S_{11}=V^-/V^+\), \(Z_\mathrm{in}=V/I_\mathrm{terminal}\), and \(Y_\mathrm{in}=I_\mathrm{terminal}/V\). Their validity is recorded separately, so a passive receiving port may retain valid impedance while its source-normalised S11 is invalid.

The group stores poles and residues together with Conductance and Capacitance; time histories Vgenerator, Vtotal, and Inetwork; frequency-domain Vgenerator_spectrum, Vtotal_spectrum, Inetwork_spectrum, Iterminal_spectrum, Vincident_spectrum, and Vreflected_spectrum; and the usual S11, Zin, Yin, mesh, source, gap, and cells-per-wavelength validity datasets. The transform uses the engineering convention and all three histories are sampled at the electric half-step. NetworkModelID identifies the reusable model and PortMode is rational_network.

The complete production update is compared with the exact reflection from series-RC and series-RLC sheets in a parallel-plate guide in Rational lumped networks in a parallel-plate guide.

A terminal owned by a subgrid is stored beneath /subgrids/<subgrid ID>/ports/<terminal ID> using the subgrid’s fine time and space increments.

Voltage-source S11 and impedance output

Every 3-D #voltage_source or VoltageSource Python object normally writes one source-owned group per main-grid port at /ports/<port ID>. A zero-resistance hard source at a domain-minimum transverse boundary remains a valid excitation but has no automatic port group because its complete Ampere loop is outside the model. A source added to a Python API subgrid is stored at /subgrids/<subgrid ID>/ports/<port ID> and uses the owning subgrid’s spatial step, time step, iteration count, material edge, and field histories. For a finite-resistance source, its resistance is the reference impedance \(Z_0\); the source-plane reflection coefficient is calculated from the known generator voltage and sampled total gap voltage. In a domain-decomposed MPI model the same schema is written after source, receiver, and port histories have been gathered on the coordinator. All Position and GridPosition metadata use the global main-grid frame. For a zero-resistance hard source, \(Z_0\) is supplied by the voltage source (50 Ohms by default) and the terminal quantities are calculated from the prescribed voltage and Ampere-loop current with its explicit half-step time offset. Both paths remove the effective Yee-edge background capacitance and conductance before reporting S11, Zin, and Yin:

\[Z_\mathrm{in}=Z_0\frac{1+S_{11}}{1-S_{11}}, \qquad Y_\mathrm{in}=\frac{1-S_{11}}{Z_0(1+S_{11})}.\]

For a finite-resistance source in a dispersive background, the shunt admittance uses the material’s complete complex relative permittivity, including every Debye, Lorentz, Drude, or inclusive pole,

\[\widetilde{\omega}=\frac{2}{\Delta t} \tan\!\left(\frac{\omega\Delta t}{2}\right), \qquad Y_\mathrm{gap}=j\widetilde{\omega}\varepsilon_0 \varepsilon_r(\widetilde{\omega})\frac{A}{\Delta l}.\]

This reduces to \(G_\mathrm{bg}+j\widetilde{\omega}C_\mathrm{gap}\) for a nondispersive conductive material. Hard voltage ports use their sampled Ampere-loop current and are not yet supported on dispersive source edges.

Hard-source port histories retain all \(N\) receiver samples, including the initial voltage. TimeSampleOffset=0 and CurrentTimeSampleOffset=-\Delta t/2 locate the voltage and raw loop-current histories respectively; their Fourier transforms apply these separate offsets. Native frequencies are based on \(N\Delta t\). Older hard-port outputs dropped the first sample and used \(N-1\) samples with offsets \(\Delta t\) and \(\Delta t/2\). Finite-resistance ports are unchanged: they still use \(N-1\) half-step-aligned samples. Always use the stored time, time_current, and frequency datasets, not an assumed length or a frequency axis borrowed from a different port.

Important attributes include:

  • PortMode, ReferenceImpedance, ReferenceImpedanceSource, Polarisation, Position, and GridPosition;

  • BackgroundMaterial, GapCapacitance, and BackgroundConductance;

  • SpectrumLimitMode, MinimumWavelengthCells, MeshFrequencyLimit, NyquistFrequency, and LimitingMaterial;

  • FrequencyRange (the first and last stored bins), ValidFrequencyRange (a convenience summary), and IndependentFrequencyResolution;

  • phasor_time_sign=exp(+j*omega*t) and forward_transform_sign=exp(-j*omega*t).

Position is always expressed in the global physical coordinate frame, including for a subgrid port, and therefore matches the associated source’s Position attribute. GridPosition is the integer index in the owning grid and can include the subgrid’s internal boundary padding.

The principal datasets are:

  • frequency: the authoritative plotting axis in Hz;

  • S11, Zin, and Yin: corrected complex terminal quantities;

  • S11_source and Zin_source: uncorrected source-plane quantities;

  • Vincident_spectrum, Vreflected_source_spectrum, and Vtotal_spectrum: complex voltage spectra;

  • time, Vgenerator, and Vtotal: aligned voltage audit histories;

  • time_current, Iloop, Iloop_spectrum, and Iterminal_spectrum: additional hard-source current data. Iloop is sampled at magnetic half-step times; its spectrum includes the Yee-gap admittance, while the terminal spectrum has that admittance removed;

  • valid_S11, valid_Zin, valid_Yin, source_valid, mesh_valid, and gap_correction_valid: per-bin integer masks;

  • incident_relative_dB and cells_per_minimum_wavelength: diagnostics behind the masks.

The normal output is capped by the requested cells-per-wavelength criterion. With spectrum_limit='nyquist', all native non-negative bins are retained for research, including finite values outside the recommended band, while the validity masks and lambda/10 advisory ceiling remain present. Algebraically undefined values are stored as complex NaNs; finite invalid values are not silently clipped.

The frequency dataset and validity mask can be plotted directly:

import h5py
import matplotlib.pyplot as plt
import numpy as np

with h5py.File('model.h5', 'r') as output:
    port = output['ports/feed']
    frequency = port['frequency'][...]
    s11 = port['S11'][...]
    valid = port['valid_S11'][...].astype(bool)

plt.plot(frequency[valid], 20 * np.log10(np.abs(s11[valid])))
plt.xlabel('Frequency [Hz]')
plt.ylabel(r'$|S_{11}|$ [dB]')

For routine inspection, the port plotter reads these stored values and their diagnostics directly, and can save PNG, PDF, or SVG figures without repeating the terminal calculation:

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

The same command supports rational-network, transmission-line, and magnetic-frill port groups, including ports owned by subgrids. Multiple --port options may be supplied, or omitted to plot every discovered port.

Eigenmode-port and S-parameter output

Each explicitly numbered main-grid eigenmode source or receiver is stored below /eigenmode_ports/portN. A direct port owned by an HSG subgrid is stored below /subgrids/<subgrid ID>/eigenmode_ports/portN and uses that grid’s fine dx_dy_dz, dt, and iteration count. frequency contains the direct-DFT bins. incident and outgoing have shape (nmodes, nfrequencies), with rows ordered by the one-based ModeIndices attribute. With exactly one active channel, S is the outgoing coefficient divided by that source-mode incident coefficient, so the source group contains modal S11 and other groups contain S21, S31, and modal-conversion terms.

Each mode has two audited anchor banks. anchor_mode_valid selects the propagating, one-watt profiles used by TF/SF source synthesis and real-power normalization. anchor_mode_reference_valid selects the successfully tracked profiles used only by modal monitoring and can include evanescent anchors. For generalized-only bins, the applicable contiguous evanescent reference run is converted to a common E/H-balanced scale before its E, H, and effective index are interpolated with matching branch-local weights. Propagating and evanescent anchors are never mixed across cutoff, and disconnected evanescent runs are not bridged. anchor_mode_propagating records the raw forward-power classification, and anchor_balanced_power records each raw profile’s positive balanced E/H power; its inverse square root supplies the reference scale.

The AnchorFrequencies attribute remains the union of the retained propagating source/power anchors, while ReferenceAnchorFrequencies is the union of retained monitor-reference anchors. CandidateAnchorFrequencies lists every solved candidate; use it together with anchor_mode_reference_valid to recover the monitor-reference frequencies for each mode rather than only their union. Non-propagating reference anchors never drive the source and never become power waves.

For a declared degenerate group, source and reference eligibility is decided for the complete group. Non-propagating group anchors are excluded from its physical references, and failed groups do not fall back member by member. The aligned fields are authoritative for excitation, monitoring, studies, field exports, and standard modal plots.

The port’s degenerate_groups subgroup stores numbered groups with ModeIndices, PhysicalPolarization, ReferenceAnchorIndex, and TransverseAxes attributes. subspace_overlaps records continuity; physically labelled groups additionally store requested_directions. Each anchorK stores frequency, active status, eigenvalues, and eigenvalue spread. Retained anchors additionally contain transform, residual, and power_gram; physical alignment also records electric_moments and its conditioning diagnostics. Rejected or inactive anchors need not contain those aligned quantities. The recorded transformation convention is aligned fields = raw fields @ transform. See Degenerate subspaces and physical alignment for their definitions and thresholds.

The complex electric_cross_power_matrix and Hermitian power_matrix both have shape (nfrequencies, nmodes, nmodes). For a modal coefficient vector \(c\), its spectral power is

\[P(c)=\operatorname{Re}\{c^\mathrm{H}Wc\}.\]

This full quadratic form is required when finite-grid modal profiles are not exactly orthogonal. The incident and outgoing arrays are generalized modal travelling-wave coefficients; an individual coefficient magnitude squared is not an additive power when power_matrix is non-diagonal. The electric cross-power matrix is retained to reconstruct total-field flux in lossy ports. reference_basis_valid has shape (mode, frequency) and records tracked reference eligibility before the Gram solve. power_basis_valid has the same shape and records forward real-power support. coefficient_valid and power_wave_valid have shape (mode, frequency): the first marks conditioned incident/outgoing coefficients, and the second also requires physical power-wave support and a usable power matrix.

Single-source scattering output includes S, coefficient_valid_S, and power_wave_valid_S, all with shape (mode, frequency). Ratio masks additionally require a usable incident denominator above the -60 dB spectrum floor. Below-cutoff coefficients may pass coefficient_valid_S while failing power_wave_valid_S; their squared magnitudes are not power fractions.

A run with one eigenmode source also writes <output>_sparameters.csv with one row per frequency, destination port, and destination mode. For a source in a subgrid the filename is <output>_<subgrid ID>_sparameters.csv. Ports in different owning grids are finalised independently and do not form one cross-grid S matrix.

When every eigenmode port has a passive virtual waveguide and no EigenmodeExcitation is defined, the same HDF5 groups contain the raw incident and outgoing modal spectra. No S datasets or <output>_sparameters.csv file are written because there is no active incident spectrum by which to normalize them.

When two or more modal channels are excited simultaneously, the groups also contain the raw incident and outgoing spectra and have ResponseType=driven. Source groups record ExcitationModes, DriveAmplitudes, DrivePowers, DrivePhasesDegrees, DriveDelays, and DriveWaveformIDs. Ordinary S datasets are not written because the response is not an independently excited S-matrix column. Driven rows instead store active_S=b_i/a_i, coefficient_valid_active_S, and power_wave_valid_active_S; <output>_active_sparameters.csv contains the same driven-state reflection data and drive weights. Use an EigenmodeStudy for the complete state-independent matrix.

NTFF field-transformation output

Reusable KSIR and equivalent-current outputs are stored in the normal model file under their surface and transform IDs:

MPI domain-decomposition runs use this same schema and write the completed datasets from the coordinator rank. A collection_backend value beginning with mpi_ records that the integration surface was partitioned between ranks; it does not change field normalisation or array ordering.

/ntff/<surface_id>/
    time/<rx_id>/
        points
        times
        time_origins
        valid_lengths
        fully_supported_lengths
        terminal_field_ratios
        terminal_decay_ok
        spherical_coordinates [spherical commands only]
        fields/<output>
    time_far_field/<output_id>/
        times
        theta
        phi
        directions
        terminal_field_ratios
        terminal_decay_ok
        layered_background/ [direct layered-time transforms only]
            interfaces
            material_ids
            relative_permittivity
            relative_permeability
            termination/ [PEC-terminated stacks only]
        observation_material_index [direct layered-time transforms only]
        observation_impedance [direct layered-time transforms only]
        observation_wave_speed [direct layered-time transforms only]
        impulse_counts [direct layered-time transforms only]
        discarded_path_amplitude_sums [direct layered-time transforms only]
        fields/<output>
    frequency/<transform_id>/
        frequencies
        layered_background/ [planar-layered transforms only]
            interfaces
            material_ids
            relative_permittivity
            relative_permeability
            termination/ [PEC-terminated stacks only]
        surface_dft/<component>/
            field
            normal_derivative
            patch_positions
            patch_normals
            area_weights
        receivers/<rx_id>/
            points
            spherical_coordinates [spherical commands only]
            fields/<output>
        far_field/<output_id>/
            theta
            phi
            directions
            radiated_power [directivity/efficiency/exterior requests]
            maximum_directivity
            maximum_directivity_dbi
            maximum_directivity_theta
            maximum_directivity_phi
            exterior_regions/ [requested planar-layered summaries]
                positive_to_negative_power_ratio [exterior_power]
                positive_to_negative_power_ratio_db [exterior_power]
                positive_axis/
                    relative_permittivity
                    relative_permeability
                    impedance
                    radiated_power [exterior_power]
                    radiated_fraction [exterior_power]
                    maximum_radiation_intensity [exterior_maximum]
                    maximum_directivity [exterior_maximum]
                    maximum_directivity_dbi [exterior_maximum]
                    maximum_theta [exterior_maximum]
                    maximum_phi [exterior_maximum]
                    accepted_coupling_efficiency [exterior_efficiency]
                    realized_coupling_efficiency [exterior_efficiency]
                    accepted_coupling_valid [exterior_efficiency]
                    realized_coupling_valid [exterior_efficiency]
                    maximum_gain [exterior_efficiency]
                    maximum_gain_dbi [exterior_efficiency]
                    maximum_realized_gain [exterior_efficiency]
                    maximum_realized_gain_dbi [exterior_efficiency]
                negative_axis/ [same datasets]
            port_power/ [gain/efficiency requests]
                port_ids
                source_types
                representations
                reference_impedances
                incident_voltage_per_port
                terminal_voltage_per_port
                terminal_current_per_port
                incident_power_per_port
                accepted_power_per_port
                incident_power
                accepted_power
                reflected_power
                incident_relative_db
                mesh_valid
                terminal_valid
                gain_valid
                realized_gain_valid
                modal_ports/ [eigenmode ports only]
                    port1/
                        mode_indices
                        incident
                        outgoing
                        electric_cross_power_matrix
                        power_matrix
                        valid
            fields/<output>

The surface group records logical bounds, physical reference origin, closure status, omitted faces, boundary types/coordinates, and image count. closure=huygens_open with one to five values in omitted_faces identifies an open frequency-domain Huygens surface; closure=symmetry identifies faces restored by PEC/PMC image completion. The frequency transform group records its ksir, equivalent_current, or planar_layered_equivalent_current formulation, window, configured precision and collection backend, plus the engineering convention: exp(+j*omega*t) phasors, exp(-j*omega*t) forward transform, and exp(-j*k*R) outgoing Green function.

For a planar-layered transform, layered_background records the stack axis, positive-to-negative-axis material ordering, physical interface coordinates, and the complex relative permittivity and permeability evaluated at every transform frequency. The transform-level scalar wave_speed and impedance are collection metadata for the positive-axis exterior; the far-field group uses the appropriate observation half-space for every direction. Its normalization attribute is therefore r * exp(+j*k_observation_halfspace*r) * field.

For a PEC-backed stack, termination records kind=pec, its positive or negative side, and physical position. The PEC is not included in material_ids or the constitutive arrays. Only the open observation side exists, so two-exterior regional datasets are absent.

Exact frequency receiver groups have range_normalized=False. They contain physical finite-distance phasors with every 1/R and 1/R**2 term. Far-field groups have range_normalized=True and a normalization attribute specifying r * exp(+j*k*r) * field. Their radius is intentionally absent. Complex datasets use the complex type paired with the configured gprMax real precision.

The time_far_field group is produced by #ntff_time_far_field or its array form, or by the corresponding direct layered-time commands. The homogeneous form records formulation=equivalent_current_1997 and the normalization r * field at reduced time t - r/c. A direct layered result records formulation=planar_layered_equivalent_current_time, its transform and background IDs, path tolerance and safety limit, resolved lossless stack, observation-half-space properties, retained impulse counts, and the summed absolute amplitudes of paths discarded at the configured threshold. Its normalization is r * field at reduced time t - r/c_observation. The last axis of both impulse datasets is labelled by its response_order attribute as Vi_e, Vv_e, Vi_h, Vv_h.

Both forms record linear fractional-delay interpolation and their collection backend. Their real field arrays have shape (ndirections, ntimes). Er and Hr are identically zero in the far-zone model; magnetic components are derived from \(\mathbf H=(\hat{\mathbf r}\times\mathbf E)/\eta_o\), using the direction-selected exterior impedance for a layered stack. The complete definitions and the distinction between the retained Yee-time staggering and fractional-delay interpolation are given in Near-to-far-field transformations.

Far-field derived antenna quantities

radiation_intensity has shape (nfrequencies, ndirections). If directivity, an efficiency, or an exterior summary is requested, the five full-sphere summary datasets shown above have shape (nfrequencies,). The far-field group also records the Gauss–Legendre/periodic quadrature orders and the completed surface’s bounding radius in metres. These summaries come from an internal full-sphere calculation even if the stored user directions form only a cut; the internal directional fields are not written to the output file. The maximum is refined using the directions in that particular output whenever they sample a larger value than the internal quadrature.

The linear definitions are

\[U=\frac{|F_\theta|^2+|F_\phi|^2}{2\eta_o(\hat{\mathbf r})},\qquad D=\frac{4\pi U}{P_\mathrm{rad}},\qquad P_\mathrm{rad}=\int_{4\pi}U\,\mathrm{d}\Omega.\]

For homogeneous transforms \(\eta_o\) is constant. For a planar stack it is the real impedance of the positive- or negative-axis lossless exterior, selected by the observation direction.

directivity_dbi, gain_dbi, and realized_gain_dbi are 10*log10 of their corresponding linear datasets. The linear gain and efficiency definitions are

\[G=\frac{4\pi U}{P_\mathrm{acc}},\qquad G_\mathrm{realized}=\frac{4\pi U}{P_\mathrm{inc}},\qquad \eta_\mathrm{rad}=\frac{P_\mathrm{rad}}{P_\mathrm{acc}},\qquad \eta_\mathrm{total}=\frac{P_\mathrm{rad}}{P_\mathrm{inc}}.\]

The two efficiencies are frequency-only quantities and therefore have shape (nfrequencies,) even though they are stored in fields with the other requested outputs.

Planar-layered exterior-region antenna quantities

The grouped exterior_power, exterior_maximum, and exterior_efficiency requests are available only for a planar_layered_equivalent_current transform. They divide the temporary full-sphere quadrature according to the sign of the direction cosine along the declared stack axis. The names positive_axis and negative_axis are used deliberately: gprMax does not assume that the stack is aligned with z or that either exterior represents air or ground.

For exterior region \(r\in\{+,-\}\),

\[P_r=\int_{\Omega_r}U(\theta,\phi)\,\mathrm d\Omega, \qquad P_\mathrm{rad}=P_+ + P_-, \qquad f_r=\frac{P_r}{P_\mathrm{rad}}.\]

exterior_power stores \(P_r\), \(f_r\), and \(P_+/P_-\) in linear and dB forms. A zero denominator consequently produces the mathematically corresponding non-finite ratio. The constituent material ID is an attribute of each region; its frequency-dependent relative permittivity, relative permeability, and real wave impedance are datasets.

exterior_maximum stores the maximum radiation intensity found in each region and its angles. Its directivity retains the conventional total-power, full-sphere definition

\[D_{\max,r}=\frac{4\pi\max_{\Omega_r}U}{P_\mathrm{rad}}.\]

It is not the non-standard hemisphere-normalised quantity \(2\pi U/P_r\). As for the global maximum, the estimate combines the internal quadrature with all explicitly requested directions.

With an #ntff_antenna_ports association, exterior_efficiency stores

\[\eta_{\mathrm{acc},r}=\frac{P_r}{P_\mathrm{acc}}, \qquad \eta_{\mathrm{realized},r}=\frac{P_r}{P_\mathrm{inc}},\]

and the maximum gain and realized gain in each exterior:

\[G_{\max,r}=\frac{4\pi\max_{\Omega_r}U}{P_\mathrm{acc}}, \qquad G_{\mathrm{realized},\max,r} =\frac{4\pi\max_{\Omega_r}U}{P_\mathrm{inc}}.\]

The coupling-efficiency names distinguish these regional quantities from the total radiation and total efficiencies. The corresponding validity masks use the same mesh, terminal, incident-spectrum, and positive-power checks as the ordinary gain outputs. Summing the two accepted coupling efficiencies gives radiation_efficiency; summing the two realized coupling efficiencies gives total_efficiency wherever those quantities are valid.

For a #ksir_antenna_ports or #ntff_antenna_ports association, per-port powers have shape (nports, nfrequencies). Total powers and all validity masks have shape (nfrequencies,). Eigenmode ports can normalize either a closed KSIR or equivalent-current transform. KSIR rejects an open surface, regardless of source type. Here nfrequencies is the transform frequency count: when the eigenmode DFT grid is denser, the per-port modal arrays in the far-field port_power group contain only the matching transform subset. The complete modal grid remains in the top-level eigenmode-port group. Conventional terminal ports use

\[P_{\mathrm{acc},p}=\frac{1}{2}\Re\{V_p I_p^*\},\qquad P_{\mathrm{inc},p}=\frac{|V_p^+|^2}{2Z_{0p}}.\]

For a modal port with incident and outgoing vectors \(a_p\) and \(b_p\), define \(x_p=a_p+b_p\) and \(y_p=a_p-b_p\). With electric cross-power matrix \(G^E_p\), the co-located total-field flux is

\[P_{\mathrm{acc},p} = \Re\{y_p^\mathrm{H}G^E_px_p\}.\]

The incident power of an eigenmode source is the quadratic power of its externally excited mode only. A passive eigenmode receiver has zero generator incident power. Totals are

\[P_\mathrm{acc}=\sum_p P_{\mathrm{acc},p},\qquad P_\mathrm{inc}=\sum_p P_{\mathrm{inc},p},\qquad P_\mathrm{refl}=P_\mathrm{inc}-P_\mathrm{acc}.\]

These are spectral power-normalisation quantities: the voltage and field Fourier transforms carry a common time scale, which cancels in gain and efficiency. Their HDF5 attributes consequently give voltage spectra in V s, current spectra in A s, and spectral powers in W s**2. The exact complex terminal and incident spectra are retained so that every derived power can be checked independently. For modal ports those terminal arrays and reference impedance are NaN because no artificial voltage/current equivalent is introduced; representations identifies them as modal_power_waves (the retained schema identifier), and modal_ports retains the generalized modal amplitudes, electric cross-power matrix, and Hermitian forward-wave power matrix. Where the real-power masks are true, modal amplitudes have units sqrt(W) s and their dimensionless quadratic matrices produce W s**2 spectral power. A finite coefficient outside that mask is a generalized reference-basis amplitude only; its magnitude squared is not power. A zero-amplitude conventional source remains a terminated port with zero incident voltage; its terminal voltage/current and signed accepted power can still be non-zero through mutual coupling. The port_ids dataset stores main-grid IDs unchanged and qualifies subgrid ports as <subgrid ID>/<local port ID>. Although the grouped antenna result is written below the main-grid NTFF group, each port spectrum is calculated with the spatial step, time step, and trace length of the grid that owns it.

terminal_valid is also the combined modal-power validity mask when a modal port is present. The validity datasets should always be applied before plotting. The default gain bandwidth includes frequencies whose total incident spectrum is within 40 dB of its peak and for which the mesh and port reconstruction are valid. Invalid derived results are stored as NaN rather than a plausible but ill-conditioned value.

When requested, far_field/<output_id>/fields/rcs contains real, linear bistatic radar cross section in square metres. It is not stored in dBsm. For the range-normalized scattered electric field \(F_\mathrm{s}=r\exp(+jkr)E_\mathrm{s}\), gprMax calculates

\[\sigma(\theta,\phi,f) = 4\pi \frac{|F_{\mathrm{s},\theta}|^2+|F_{\mathrm{s},\phi}|^2} {|E_{\mathrm{inc},x}|^2+|E_{\mathrm{inc},y}|^2 +|E_{\mathrm{inc},z}|^2}.\]

The denominator is obtained from the actual field history in the associated discrete-plane-wave grid, accumulated at the transform frequencies with the same time window as the surface data. It therefore includes the numerical plane-wave amplitude and its configured start and stop times. A zero incident spectrum produces NaN RCS. Very small incident values are mathematically non-zero but can give unreliable results, so the requested frequencies should remain within the useful excitation bandwidth.

When a frequency transform is associated with a plane wave, its plane_wave subgroup stores the source index, TFSF corners, waveform and material IDs, actual angles, polarisation, rational integer mapping, start and stop times, and the incident-field reference positions. This metadata is the authoritative description of the illumination used for that transform.

For plotting in dBsm, convert the stored values explicitly:

import h5py
import numpy as np

with h5py.File('model.h5', 'r') as output:
    far = output['ntff/surface/frequency/transform/far_field/backscatter']
    rcs = far['fields/rcs'][...]  # linear RCS in m**2
    rcs_dbsm = 10 * np.log10(rcs)

Here theta and phi in the same group define the observation direction. Monostatic RCS is the value in the direction opposite to plane-wave propagation; all other directions are bistatic RCS. Use separate simulations for different incident plane waves because selecting an association does not separate simultaneously accumulated scattered fields. A gprMax.PlaneWaveStudy automates those separate illuminations while reusing the unchanged Yee geometry.

Exact finite-distance KSIR time-domain fields retain the complete raw retarded buffer and have shape (npoints, max(valid_lengths)). Its final bins may contain only part of the closed-surface history because different surface patches have different propagation delays. For normal use, point q must therefore be sliced with fully_supported_lengths:

length = fully_supported_lengths[q]
physical_time = time_origins[q] + times[:length]
trace = fields[output][q, :length]

valid_lengths remains available for research access to every stored bin. Those additional bins are not a complete field reconstruction unless the surface fields had already decayed to zero. terminal_field_ratios gives, for each point, the largest ratio between the final 32 fully supported samples and the trace peak, evaluated independently for each underlying Cartesian component. terminal_decay_ok is true when this is no greater than terminal_decay_threshold (stored as a group attribute). gprMax emits a warning when the test fails; increasing the model time window is the correct remedy.

With time_origin=simulation every origin is zero. With time_origin=first_arrival each origin retains its absolute propagation time without storing the potentially large guaranteed leading-zero prefix. For this origin policy, fully_supported_lengths normally equals the number of FDTD iterations.

Equivalent-current transient far fields use a stricter output policy. Their times dataset already contains only the common interval for which every surface patch has an available retarded-time sample; no partially supported tail is written. The time coordinate is reduced time \(\tau=t-r/c_b\), referenced to the surface origin, so increasing a hypothetical observation radius would not prepend zeros. The terminal_field_ratios and terminal_decay_ok datasets are per direction. A false value means the FDTD time window should be increased.

The surface DFT datasets are present by default and allow later angular or point evaluation without rerunning FDTD. They can be large: their leading dimensions are frequency and surface patch. An NTFF output creates the normal model output file even when there are no conventional receivers or transmission lines.

NTFF surfaces must strictly enclose every impressed source. For plane-wave scattering models, the associated total-field/scattered-field box must be strictly enclosed by the NTFF surface. The integration surface then samples the scattered-field region outside the TFSF box, while the associated numerical plane wave supplies the incident-field normalization used for RCS. Sources and scatterers may reside on an HSG subgrid enclosed by these main-grid surfaces. Neither an NTFF surface nor a TFSF correction surface may touch or cut the HSG outer coupling surface; an overlapping surface must strictly enclose the complete subgrid coupling region.

Surface-impedance reproducibility metadata

Every output file containing a surface definition has a root group /surface_impedance_models. Its attributes are:

  • SchemaVersion = 3;

  • TimeConvention = exp(+j*omega*t);

  • FourierAnalysisConvention = exp(-j*omega*t);

  • SurfaceNormalConvention = metal_to_retained_dielectric;

  • SurfaceCurrentConvention = K = n_m cross H;

  • Units = SI;

  • FDTDTimeStep.

Each definition appears as model1, model2, and so on, in definition order. A model group stores ID, ModelHashSHA256, Order, D, fit limits, source kind, conductivity, preset/provenance, requested and selected pole counts, fit method and tolerance, maximum/RMS errors, plotting policy, and whether the compiled boundary uses it. The continuous A, B, and C arrays are datasets. A preset additionally stores reference temperature and resistivity.

For every model used by the compiled boundary, fdtd_discrete stores the exact local f and q pole vectors and the TimeStep, Z0, and PassivityChecked attributes. Together with the continuous realization, these reproduce both the in-place FDTD recurrence and the equivalent FDFD transfer. A defined but unused model has no discrete subgroup. The model hash covers the continuous model and fitting provenance. The current schema does not serialize the complete sparse geometry/port map, so geometry reproducibility still depends on the input model and normal gprMax output metadata.

An eigenmode port additionally stores anchor_complex_neff below its /eigenmode_ports/portN group. Its complex array has shape (anchor frequency, mode) and is aligned with the CandidateAnchorFrequencies attribute. The associated anchor_mode_valid and anchor_mode_reference_valid datasets identify the usable rows. This is the exact FDFD propagation constant bank used for broadband interpolation and makes the FDFD attenuation comparison reproducible from the output file.

For example, inspect one used realization with:

import h5py

with h5py.File('model.h5', 'r') as output:
    models = output['surface_impedance_models']
    wall = models['model1']
    print(wall.attrs['ID'], wall.attrs['ModelHashSHA256'])
    A = wall['A'][...]
    B = wall['B'][...]
    C = wall['C'][...]
    f = wall['fdtd_discrete/f'][...]
    q = wall['fdtd_discrete/q'][...]
    Z0 = wall['fdtd_discrete'].attrs['Z0']

Snapshots

Snapshot files contain a snapshot of the electromagnetic field values of a specified volume of the model domain at a specified point in time during the simulation. By default, snapshot files use version 2.2 of the open source Visualization ToolKit (VTK) VTKHDF format. ParaView 5.12 or newer is required to view these files; the gprMax macro has been tested directly with ParaView 6.0.1. ParaView is an open-source, multi-platform data analysis and visualization application available for Linux, macOS, and Windows. You can optionally output snapshot files using the HDF5 format if desired. HDF5 snapshots include origin (global x, y, z coordinates), dx_dy_dz, nx_ny_nz, iteration, time, and magnetic_time attributes. time is the electric-field time \(n\Delta t\), and magnetic_time is the native staggered magnetic-field time \((n-1/2)\Delta t\); no temporal averaging is applied. A snapshot owned by an HSG subgrid uses its fine-grid iteration and time step and is still positioned in this global coordinate frame.

A requested real time is rounded to the nearest full electric-field step (halfway values round to the earlier step). An integer iteration selects that same zero-based level directly, so the two input forms produce the same field state. Valid indices are zero through one less than the owning grid’s number of iterations. Spatial collocation of the staggered Yee components within a snapshot cell is a separate operation and is unchanged by this timing rule.

All main-grid and subgrid snapshots for a model are written into the same snapshot directory. If two snapshots resolve to exactly the same filename, gprMax issues a warning because the later output will overwrite the earlier one. The comparison includes the file extension, so the same base name may be used safely for different formats, for example fields.h5 and fields.vtkhdf.

For a reduced 2-D model, a snapshot contains only the genuine invariant-axis field plane: index zero for TM and index one for TE. Bounds spanning the full invariant extent are collapsed automatically to that plane; bounds excluding it are rejected. Inactive components may still be requested for a consistent file schema, but contain zero. The file origin places the centre of the single output plane at the physical Yee-field plane, rather than at either forced-zero TE padding wall.

Tip

You can take advantage of our Python API to easily create a series of snapshots. For example, to create 30 snapshots starting at time 0.1ns until 3ns in intervals of 0.1ns, use the following code snippet in your input file. Replace x, y, z, dl, fn accordingly.

import gprMax

for i in range(1, 31):
    s = gprMax.Snapshot(p1=(0, 0, 0), p2=(x, y, z), dl=(dl, dl, dl),
                        time=(i/10) * 1e-9,
                        filename=fn.with_suffix('').parts[-1] + '_' + str(i))
    scene.add(s)

The following are steps to get started with viewing snapshot files in Paraview:

  1. Open the file either from the File menu or toolbar. Paraview should recognise the time series based on the file name and load in all the files.

  2. Click the Apply button in the Properties panel. You should see an outline of the snapshot volume.

  3. Use the Coloring drop down menu to select the field component you want to visual, e.g. Ex, Ey, Ez, Hx, Hy, Hz.

  4. From the Representation drop down menu select Surface.

  5. You can step through or play as an animation the time steps using the time controls in the toolbar.

Tip

  • Turn on the Animation View (View->Animation View menu) to control the speed and start/stop points of the animation.

  • Use the Color Map Editor to adjust the Color Scaling.

Geometry output

Geometry files use version 2.2 of the open source Visualization ToolKit (VTK) VTKHDF format. ParaView 5.12 or newer is required to view these files; the gprMax macro and its source/receiver representations have been tested directly with ParaView 6.0.1. ParaView is an open-source, multi-platform data analysis and visualization application available for Linux, macOS, and Windows.

The #geometry_view: command produces either ImageData for a per-cell geometry view, or UnstructuredGrid for a per-cell-edge geometry view. The following are steps to get started with viewing geometry files in Paraview:

If the model contains semantic geometry tags, a normal per-cell geometry view also contains the integer cell array TagID. VTKHDF field data stores the matching geometry_tag_ids and geometry_tag_names arrays; ID zero means untagged. The tag array records the final voxelised geometry after ordered overwrites and is not affected by dielectric smoothing. It is omitted entirely for models without tags. Per-cell-edge geometry views do not export tags because zero-thickness edge and plate objects do not yet have tag semantics.

Both normal and fine geometry views contain typed catalogues of active sources and receivers. Localised sources and receiver ports are represented by one cell-sized marker at their grid position. A discrete plane-wave source is represented by the boundary of its TFSF box (a rectangle in a reduced 2-D model), while an eigenmode port is represented by its aperture plane. Virtual waveguides use that same physical aperture and are identified as virtual- waveguide interfaces.

Port roles are determined from the resolved excitation used by that model run. A voltage-source, transmission-line, magnetic-frill, or network port whose resolved drive is identically zero is a receiver, not a source. An active eigenmode port is a source and a passive eigenmode port is a receiver. Ordinary Rx points, including points expanded from an RxArray, are receivers; the private sampling receiver belonging to a voltage-source port is not shown again at the same position. NTFF and KSIR observation locations are deliberately omitted because they describe transformations or observation points outside the FDTD geometry rather than receiver placement within the model.

The source catalogue is stored in the source_geometry_ids, source_geometry_types, source_geometry_kinds, and source_geometry_bounds field arrays. The receiver catalogue uses the corresponding receiver_geometry_* names. The gprMax ParaView macro creates the point boxes, wireframe TFSF boundaries, and port plates, and prefixes each Pipeline Browser name with Source or Receiver followed by its type and ID.

_images/paraview_toolbar.png

Fig. 19 Paraview toolbar showing gprMax macro button.

  1. Open the file either from the File menu or the toolbar.

  2. Click the Apply button in the Properties panel. You should see an outline of the volume of the geometry view.

  3. Install the gprMax.py Python script, that comes with the gprMax source code (in the gprMax/toolboxes/Utilities/Paraview directory), as a macro in Paraview. This script makes it quick and easy to view the different materials in a geometry file. To add the script as a macro in Paraview choose the file from the Macros->Add new macro menu. It will then appear as a shortcut button in the toolbar as shown in Fig. 19. You only need to do this once, the macro will be kept in Paraview for future use.

  4. Click the gprMax shortcut button. All the materials in the model should appear in the Pipeline Browser as Threshold items as shown in Fig. 20. If the geometry contains semantic tags, the macro also creates Tag - <name> Threshold items. These are hidden initially to avoid obscuring the material view and can be enabled individually with the eye icon.

_images/paraview_pipeline.png

Fig. 20 Paraview Pipeline Browser showing list of materials in an example model.

Tip

  • You can turn on and off the visibility of materials using the eye icon in the Pipeline Browser. You can select multiple materials using the Shift key, and by shift-clicking the eye icon, turn the visibility of multiple materials on and off.

  • You can set the Color and Opacity of materials from the Properties panel.