tidal.measurement package#
Measurement and analysis tools for coupled-field simulations.
Provides energy computation, conversion probability, mixing length extraction, spectral decomposition, and energy conservation diagnostics for simulation output from the TIDAL Lagrangian-to-PDE pipeline.
Typical usage:
from tidal.measurement import SimulationData, compute_conversion_probability
from tidal.measurement import compute_mixing_length, compute_mixing_spectrum
data = SimulationData.from_storage(storage, spec, grid, params)
# Or from a snapshot directory (memory-mapped, O(1) RAM):
# data = SimulationData.load("output_dir", spec)
result = compute_conversion_probability(data, "phi_0", "chi_0")
mixing = compute_mixing_length(result)
print(f"L_mix = {mixing.mixing_length:.4f} +/- {mixing.mixing_length_uncertainty:.4f}")
- class tidal.measurement.AsymptoticConversionResult(P_final, P_reflected, P_transmitted, E_source_initial, E_target_final, source_field, target_field, source_wavevector)[source]#
Bases:
objectResult of asymptotic conversion measurement.
- Parameters:
- source_wavevector#
Spectral centroid wavevector of the source at
t=0(defines the forward direction).
- class tidal.measurement.ConversionResult(times, probability, source_energy, target_energy, total_energy, relative_energy_error, source_field, target_field)[source]#
Bases:
objectResult of a conversion probability measurement.
- Parameters:
- times#
Snapshot times.
- Type:
ndarray, shape
(n_snapshots,)
- probability#
P(t) = E_target(t) / E_source(0).- Type:
ndarray, shape
(n_snapshots,)
- source_energy#
Source field energy over time.
- Type:
ndarray, shape
(n_snapshots,)
- target_energy#
Target field energy over time.
- Type:
ndarray, shape
(n_snapshots,)
- total_energy#
Total system energy (source + target) over time.
- Type:
ndarray, shape
(n_snapshots,)
- relative_energy_error#
(E_total(t) - E_total(0)) / E_total(0).- Type:
ndarray, shape
(n_snapshots,)
- times: NDArray[np.float64]#
- probability: NDArray[np.float64]#
- source_energy: NDArray[np.float64]#
- target_energy: NDArray[np.float64]#
- total_energy: NDArray[np.float64]#
- relative_energy_error: NDArray[np.float64]#
- class tidal.measurement.CriticalFieldResult(rows, field_param, threshold, metric, outer_params)[source]#
Bases:
objectResult of critical field extraction.
- Parameters:
- rows#
One dict per outer-param combination. Keys include outer param values plus
B_min,inv_B_min, error columns, and quality.
- class tidal.measurement.DispersionResult(wavenumbers, frequencies, power, peak_frequencies, peak_powers, field_name, rayleigh_resolution)[source]#
Bases:
objectDispersion relation extracted from simulation output.
- Parameters:
- wavenumbers#
Radially binned wavenumber magnitudes
|k|.- Type:
ndarray, shape
(n_modes,)
- frequencies#
Angular frequencies
omega(rad/time), excluding DC.- Type:
ndarray, shape
(n_freq,)
- power#
Spectral power
S(k, omega) = sum_i |A_hat_i(k, omega)|^2summed over all fields in the group.- Type:
ndarray, shape
(n_modes, n_freq)
- peak_frequencies#
Dominant angular frequency at each k-bin (0.0 for inactive modes).
- Type:
ndarray, shape
(n_modes,)
- peak_powers#
Spectral power at the dominant frequency per k-bin.
- Type:
ndarray, shape
(n_modes,)
- wavenumbers: NDArray[np.float64]#
- frequencies: NDArray[np.float64]#
- power: NDArray[np.float64]#
- peak_frequencies: NDArray[np.float64]#
- peak_powers: NDArray[np.float64]#
- class tidal.measurement.EffectiveMassResult(m2_eff, m2_eff_std, m2_eff_values, wavenumbers, frequencies, n_active_modes, field_name)[source]#
Bases:
objectResult of effective mass extraction.
- Parameters:
- m2_eff_values#
Per-mode m² = ω² - k² for all active modes.
- Type:
ndarray
- frequencies#
Dominant ω(k) for each active mode.
- Type:
ndarray
- m2_eff_values: NDArray[np.float64]#
- wavenumbers: NDArray[np.float64]#
- frequencies: NDArray[np.float64]#
- class tidal.measurement.EnergyDiagnostics(times, total_energy, relative_error, max_relative_error, is_conserved)[source]#
Bases:
objectEnergy conservation diagnostic result.
- Parameters:
- times#
- Type:
ndarray, shape
(n_snapshots,)
- total_energy#
Spatially-averaged energy density ⟨ε⟩ at each snapshot.
- Type:
ndarray, shape
(n_snapshots,)
- relative_error#
(⟨ε⟩(t) - ⟨ε⟩(0)) / ⟨ε⟩(0).- Type:
ndarray, shape
(n_snapshots,)
- times: NDArray[np.float64]#
- total_energy: NDArray[np.float64]#
- relative_error: NDArray[np.float64]#
- class tidal.measurement.FieldEnergy(kinetic, gradient, mass, total)[source]#
Bases:
objectEnergy density decomposition for a single field at one snapshot.
All values are spatially-averaged energy densities ⟨ε⟩ = E / V_domain.
- gradient#
0.5 * ⟨|∇_self φ|²⟩— gradient energy density over self-laplacian axes only. For scalar fields (fulllaplacian), this equals0.5 * ⟨|∇φ|²⟩. For vector components with directional laplacians (e.g.laplacian_y), only the relevant axes.- Type:
- class tidal.measurement.MixingResult(mixing_length, mixing_length_uncertainty, dominant_frequency, frequency_fwhm, max_conversion, peaks)[source]#
Bases:
objectResult of spectral mixing length extraction.
The mixing length is derived from the dominant peak of the temporal FFT of
P(t), rather than from time-domain peak detection. This correctly identifies the physically meaningful oscillation timescale even in multi-scale systems where rapid noise oscillations sit on top of a slower mixing envelope.The uncertainty comes from the half-width at half-maximum (HWHM) of the spectral peak:
dL = (pi/omega**2) * HWHMwhereHWHM = FWHM / 2. A sharp spectral peak (small FWHM) gives a precise mixing length; a broad peak indicates the oscillation frequency is less well-defined.- Parameters:
- peaks#
All detected spectral peaks, sorted by power descending.
peaks[0]is the dominant peak.- Type:
- peaks: tuple[SpectralPeak, ...]#
- class tidal.measurement.MixingSpectrum(frequencies, power, dominant_frequency, dominant_mixing_length, rayleigh_resolution)[source]#
Bases:
objectFrequency decomposition of the conversion probability timeseries.
Reports angular frequencies at which
P(t)oscillates — no theory-specific conversion. For each frequencyomega, the half-periodpi/omegagives the mixing timescale at that frequency.The frequency resolution is
rayleigh_resolution = 2*pi/TwhereTis the observation window duration.- Parameters:
- frequencies#
Angular frequencies
omega(rad/time), excluding DC.- Type:
ndarray
- power#
|P_hat(omega)|**2at each frequency.- Type:
ndarray
- dominant_mixing_length#
pi / dominant_frequency— half-period at the dominant frequency.- Type:
- rayleigh_resolution#
2*pi/T— fundamental frequency resolution from the observation window duration. This is the minimum resolvable frequency difference.- Type:
- frequencies: NDArray[np.float64]#
- power: NDArray[np.float64]#
- class tidal.measurement.ResonanceResult(wavenumbers, omega_source, omega_target, resonance_mismatch, resonant_modes, n_resonant_modes, conversion_bandwidth, peak_conversion_k, source_field, target_field)[source]#
Bases:
objectResonance analysis for coupled fields.
- Parameters:
- wavenumbers#
Shared wavenumber bins where both fields have active modes.
- Type:
ndarray, shape
(n_shared,)
- omega_source#
Source field frequencies at shared k-bins.
- Type:
ndarray, shape
(n_shared,)
- omega_target#
Target field frequencies at shared k-bins.
- Type:
ndarray, shape
(n_shared,)
- resonance_mismatch#
|omega_s - omega_t| / omega_avgper shared mode.- Type:
ndarray, shape
(n_shared,)
- resonant_modes#
Boolean mask: which modes are near-resonant.
- Type:
ndarray, shape
(n_shared,)
- conversion_bandwidth#
FWHM of the resonance mismatch curve in k-space (0.0 if < 2 resonant).
- Type:
- wavenumbers: NDArray[np.float64]#
- omega_source: NDArray[np.float64]#
- omega_target: NDArray[np.float64]#
- resonance_mismatch: NDArray[np.float64]#
- resonant_modes: NDArray[np.bool_]#
- class tidal.measurement.SimulationData(times, fields, velocities, grid_spacing, grid_bounds, periodic, spec, parameters, bc_types=None, dt=None)[source]#
Bases:
objectFull time-history of a simulation, ready for measurement.
Both fields and velocities store arrays of shape
(n_snapshots, *grid_shape)— one spatial snapshot per recorded time. Constraint fields (time_derivative_order == 0) have no velocity entry.- Parameters:
times (NDArray[np.float64])
spec (EquationSystem)
dt (float | None)
- times#
Snapshot times.
- Type:
ndarray, shape (n_snapshots,)
- velocities#
Mapping
field_name → (n_snapshots, *grid_shape)velocity arrays (v = dq/dt). Only present for 2nd-order (wave) fields.
- spec#
The equation specification (fields, equations, matrices).
- Type:
- bc_types#
Per-axis boundary condition type (e.g.
("periodic", "neumann")).Nonefor legacy data where BC info was not recorded. Used by the energy module for BC-aware gradient computation.
- property dynamical_fields: tuple[str, ...]#
Field names with
time_derivative_order >= 2(have velocities).
- classmethod from_directory(path, spec)[source]#
Load from a snapshot directory with memory-mapped arrays (O(1) RAM).
The directory must contain
metadata.jsonand per-field.npyfiles written bySnapshotWriter. Arrays are opened as read-only memory maps — only the pages actually accessed by measurement functions are loaded into RAM.- Parameters:
path (Path or str) – Path to the snapshot directory.
spec (EquationSystem) – JSON-derived equation specification.
- Raises:
FileNotFoundError – If path does not exist or is not a directory.
ValueError – If
metadata.jsonis missing or corrupt.
- Return type:
- classmethod from_result(result, spec, grid_info, parameters=None, dt=None)[source]#
Build from a solver result dict (IDA/leapfrog output).
This is the native-path constructor — no py-pde types involved. Directly slices the flat state vector using
StateLayout.- Parameters:
result (dict) – Solver output with keys
"t"(1D times array) and"y"(2D array of shape(n_snapshots, total_flat_size)).spec (EquationSystem) – Equation specification.
grid_info (GridInfo) – Spatial grid descriptor.
parameters (dict, optional) – Resolved parameter values.
dt (float, optional) – Time-step size used by the solver (for conservation diagnostics).
- Raises:
ValueError – If result has no snapshots or flat vector size doesn’t match the layout.
- Return type:
- classmethod load(path, spec)[source]#
Load from a snapshot directory (memory-mapped, O(1) RAM).
- Parameters:
path (Path or str) – Path to snapshot directory.
spec (EquationSystem) – JSON-derived equation specification.
- Raises:
ValueError – If path is not a directory.
- Return type:
- save(path)[source]#
Save to a snapshot directory (metadata.json + .npy files).
This is the inverse of
load()/from_directory(). Overwrites any existing files in the directory.- Parameters:
path (Path or str) – Directory to write into (created if it does not exist).
- Returns:
The directory that was written.
- Return type:
Path
- times: NDArray[np.float64]#
- spec: EquationSystem#
- class tidal.measurement.SnapshotWriter(output_dir, field_names, velocity_names, grid_shape, n_snapshots, grid_spacing, grid_bounds, periodic, parameters=None, spec_path=None, flush_interval=10, bc_types=None, dt=None)[source]#
Bases:
objectStream simulation snapshots to disk with O(1) memory.
Pre-allocates one
.npyfile per field/velocity (plustimes.npy) usingnumpy.memmap, then writes each snapshot in-place. The exact number of snapshots must be known at construction time — compute it asint(t_end / snapshot_interval) + 1.Use as a context manager for automatic
close():with SnapshotWriter(output_dir, ...) as writer: for t, fields, velocities in simulation: writer.append(t, fields, velocities)
- Parameters:
output_dir (Path) – Directory to create. Must not already exist.
field_names (list[str]) – Names of field arrays to store (e.g.
["phi_0", "chi_0"]).velocity_names (list[str]) – Names of velocity arrays to store (e.g.
["phi_0", "chi_0"]). Stored asv_{name}.npy.grid_shape (tuple[int, ...]) – Spatial grid shape (e.g.
(96, 96)).n_snapshots (int) – Exact number of snapshots to write.
grid_spacing (tuple[float, ...]) – Cell size per spatial axis.
grid_bounds (tuple[tuple[float, float], ...]) – Domain bounds per spatial axis.
periodic (tuple[bool, ...]) – Whether each spatial axis is periodic.
parameters (dict[str, float] or None) – Resolved parameter values.
spec_path (Path or None) – Path to the JSON spec file (for auto-discovery by
tidal measure).flush_interval (int)
dt (float | None)
- append(t, fields, velocities)[source]#
Write one snapshot at the next time index.
- Parameters:
- Raises:
ValueError – If the writer is closed, the snapshot count is exceeded, the time is non-finite or non-monotonic, or a field/velocity array has the wrong shape.
- Return type:
None
- class tidal.measurement.SpectralPeak(frequency, power, mixing_length, fwhm, mixing_length_uncertainty)[source]#
Bases:
objectA detected peak in the mixing power spectrum.
Each peak represents a frequency at which
P(t)oscillates. The mixing lengthpi/omegagives the half-period of energy exchange at that frequency. FWHM measures how sharply defined the oscillation is — a narrow peak means a coherent, well-defined oscillation; a broad peak means the frequency is less certain.- Parameters:
- mixing_length_uncertainty#
Propagated from half-width:
(pi/omega**2) * HWHMwhereHWHM = FWHM / 2.- Type:
- class tidal.measurement.SpectralSnapshot(wavenumbers, power_spectrum)[source]#
Bases:
objectSpectral decomposition of a field at one time.
- Parameters:
wavenumbers (NDArray[np.float64])
power_spectrum (NDArray[np.float64])
- wavenumbers#
Radially binned wavenumber magnitudes
|k|.- Type:
ndarray
- power_spectrum#
|φ̂(k)|²averaged over shells of constant|k|.- Type:
ndarray
- wavenumbers: NDArray[np.float64]#
- power_spectrum: NDArray[np.float64]#
- class tidal.measurement.SystemEnergy(per_field, interaction, total)[source]#
Bases:
objectEnergy density for the full coupled system at one snapshot.
All values are spatially-averaged energy densities ⟨ε⟩ = E / V_domain.
- Parameters:
per_field (dict[str, FieldEnergy])
interaction (float)
total (float)
- per_field#
Energy density breakdown per field (operator-aware gradient).
- Type:
- interaction#
Cross-field coupling energy density: total potential density minus per-field self-potential densities. Uses operator-aware gradient axes, so this is zero when fields are uncoupled and only one field is excited.
- Type:
- per_field: dict[str, FieldEnergy]#
- class tidal.measurement.VelocityMismatchResult(source_velocity, target_velocity, mismatch, shared_wavenumbers, max_mismatch, mean_mismatch)[source]#
Bases:
objectVelocity mismatch between two field groups.
- Parameters:
source_velocity (VelocityResult)
target_velocity (VelocityResult)
mismatch (NDArray[np.float64])
shared_wavenumbers (NDArray[np.float64])
max_mismatch (float)
mean_mismatch (float)
- source_velocity#
Velocity analysis for the source field.
- Type:
- target_velocity#
Velocity analysis for the target field.
- Type:
- mismatch#
|v_g_source(k) - v_g_target(k)|at shared wavenumber bins.- Type:
ndarray
Wavenumbers at which both fields have active modes.
- Type:
ndarray
- source_velocity: VelocityResult#
- target_velocity: VelocityResult#
- mismatch: NDArray[np.float64]#
- shared_wavenumbers: NDArray[np.float64]#
- class tidal.measurement.VelocityResult(wavenumbers, group_velocity, phase_velocity, group_velocity_mean, phase_velocity_mean, n_active_modes, field_name)[source]#
Bases:
objectVelocity analysis from dispersion relation.
- Parameters:
- group_velocity#
Group velocity
d omega / dkper active mode.- Type:
ndarray, shape
(n_active,)
- phase_velocity#
Phase velocity
omega / kper active mode.- Type:
ndarray, shape
(n_active,)
- wavenumbers: NDArray[np.float64]#
- group_velocity: NDArray[np.float64]#
- phase_velocity: NDArray[np.float64]#
- tidal.measurement.check_energy_conservation(data, threshold=0.001)[source]#
Check whether energy density is conserved over the simulation.
For symplectic (leapfrog) solvers, the physical Hamiltonian oscillates by O(dt²) around the shadow Hamiltonian. When
data.dtis available, the threshold is automatically raised tomax(threshold, 10 * dt²)so that the expected shadow-Hamiltonian offset does not cause false FAIL results.- Parameters:
data (SimulationData)
threshold (float) – Maximum allowed
|ΔE/E₀|. Default1e-3(0.1%).
- Return type:
- Raises:
ValueError – If threshold is not positive.
- tidal.measurement.compute_asymptotic_conversion(data, source_fields, target_fields=None)[source]#
Compute asymptotic scattering observables.
The forward/backward directional split is defined by the source field’s initial propagation direction (spectral centroid wavevector at
t=0). See module docstring for details on frame independence.- Parameters:
data (SimulationData) – Simulation output.
source_fields (str or sequence of str) – Source field name(s).
target_fields (str, sequence of str, or None) – Target field name(s).
None→ all dynamical fields not in source.
- Return type:
- Raises:
ValueError – If source/target fields are invalid or overlap, or if the source has zero initial energy.
- tidal.measurement.compute_conversion_probability(data, source_field, target_field)[source]#
Compute wave conversion probability
P(t) = E_target(t) / E_source(0).This is the primary measurement for the Gertsenshtein effect. The source field is excited with some initial energy; the target field starts at zero. Coupling terms transfer energy between them over time.
Energy is computed via the canonical Hamiltonian (kinetic + gradient + mass), including the spatial volume element
sqrt|g_spatial|for curved coordinates. This gives physically correct results for both flat and curved spacetimes.- Parameters:
data (SimulationData) – Full simulation output.
source_field (str) – Name of the source field (e.g.
"phi_0").target_field (str) – Name of the target field (e.g.
"chi_0").
- Return type:
- Raises:
ValueError – If source_field or target_field is not a valid field name, if they are the same field, or if the source has zero initial energy.
- tidal.measurement.compute_critical_field(results, field_param, metric='P_final', threshold=0.99, *, interpolate=True)[source]#
Find minimum field strength for a metric to cross a threshold.
For each unique combination of the “outer” swept parameters (everything except field_param), the rows are sorted by field_param and scanned upward. The first crossing of
metric >= thresholddefinesB_min.- Parameters:
results (SweepResults) – Sweep data with field_param as one of the swept parameters.
field_param (str) – The field-strength parameter to threshold on (e.g.
"B0").metric (str) – Metric column to compare against threshold (default
"P_final").threshold (float) – Target value for metric (default 0.99 — full conversion).
interpolate (bool) – If True, linearly interpolate between bracketing grid points for sub-grid accuracy.
- Return type:
- Raises:
ValueError – If field_param is not a swept parameter or metric is not found.
- tidal.measurement.compute_dispersion(data, field_names, *, min_amplitude=1e-12)[source]#
Extract dispersion relation omega(k) from simulation output.
Algorithm#
For each field in field_names, compute spatial
rfftnper snapshot to get complex Fourier coefficientsphi_hat_i(k, t).For each spatial mode
k, temporal FFT of the complex coefficient givesS_i(k, omega).Sum spectral power across all fields:
S_group(k, omega) = sum_i S_i(k, omega).Radially bin
S_group(k, omega)into|k|shells.Peak detection:
argmax(S_group)per k-bin extractsomega(k).Modes with combined max amplitude below threshold are inactive.
Using complex coefficients (not
|phi_hat|) avoids frequency doubling artifacts from taking the absolute value before FFT. Summing power over a field group makes the measurement rotationally covariant within the group (no dependence on which single component is selected).- param data:
Simulation output with time-resolved field snapshots.
- type data:
SimulationData
- param field_names:
Field or group of fields to extract the dispersion relation for. All fields must be dynamical (
time_derivative_order >= 2); constraint fields raiseValueError.- type field_names:
str or sequence of str
- param min_amplitude:
Modes with combined max
|phi_hat(k, t)|below this threshold are treated as inactive. Default1e-12.- type min_amplitude:
float, optional
- rtype:
DispersionResult
- raises ValueError:
If any field is unknown, is a constraint field (
time_derivative_order < 2), fewer than 3 snapshots, the timestep is non-uniform, or any equation term is position-dependent (uniform medium required).
- Parameters:
data (SimulationData)
min_amplitude (float)
- Return type:
- tidal.measurement.compute_effective_mass(data, field_names, *, min_amplitude=1e-12)[source]#
Extract effective mass from the dispersion relation.
- Parameters:
data (SimulationData) – Simulation output.
field_names (str or sequence of str) – Field name(s) to analyze. Multiple fields are summed (rotationally covariant within the group).
min_amplitude (float) – Minimum Fourier amplitude for a mode to be considered active.
- Returns:
Effective mass and per-mode breakdown.
- Return type:
- Raises:
ValueError – If no active modes are found or dispersion cannot be computed.
- tidal.measurement.compute_energy_timeseries(data, fields=None)[source]#
Compute energy density for every snapshot in the simulation.
- Parameters:
data (SimulationData)
fields (set[str] or None) – If given, only evaluate Hamiltonian terms involving these base field names. Passed through to
compute_system_energy.
- Returns:
times (ndarray, shape
(n_snapshots,))per_field (dict[str, ndarray]) – Each value is shape
(n_snapshots,)— energy density of that field.interaction (ndarray, shape
(n_snapshots,))total (ndarray, shape
(n_snapshots,))
- Return type:
tuple[NDArray[np.float64], dict[str, NDArray[np.float64]], NDArray[np.float64], NDArray[np.float64]]
- tidal.measurement.compute_group_conversion(data, source_fields, target_fields=None)[source]#
Measure energy conversion between field groups.
Computes
P(t) = E_targets(t) / E_sources(0)where each energy is the sum of per-field canonical Hamiltonian energies across the group. This is the natural measurement for the Gertsenshtein effect where source and target are multi-component tensor/vector field groups.Energy is computed via
compute_energy_timeserieswhich correctly handles volume weights, operator-aware gradient axes, and position- dependent masses for curved spacetimes.- Parameters:
data (SimulationData) – Full simulation output.
source_fields (str or sequence of str) – Source field name(s). A single string is treated as a one-element group.
target_fields (str, sequence of str, or None) – Target field name(s).
Nonemeans all other dynamical fields (time_derivative_order >= 2) not in source_fields.
- Returns:
source_field/target_fieldare comma-joined group names.- Return type:
- Raises:
ValueError – If any field name is invalid, if source and target overlap, if the target group is empty, or if the source group has zero initial energy.
- tidal.measurement.compute_mixing_length(conversion, *, min_prominence=0.01)[source]#
Extract the characteristic mixing length from the power spectrum of
P(t).Computes the temporal FFT of the conversion probability
P(t), finds spectral peaks, and derives the mixing length from the dominant peak. This spectral approach correctly identifies the physically meaningful oscillation timescale even in multi-scale systems where rapid oscillations sit on top of a slower mixing envelope.The uncertainty comes from the half-width at half-maximum (HWHM) of the spectral peak. Error propagation:
L = pi/omega,dL = (pi/omega**2) * HWHMwhereHWHM = FWHM/2.- Parameters:
conversion (ConversionResult) – Output of
compute_conversion_probability()orcompute_group_conversion().min_prominence (float, optional) – Minimum spectral peak power as a fraction of the maximum power. Peaks below this threshold are treated as noise. Default
0.01(1% of dominant peak power).
- Return type:
- Raises:
ValueError – If fewer than 3 time points, if timestep is non-uniform, if
min_prominenceis not in(0, 1), or if no spectral peaks are found above the prominence threshold.
- tidal.measurement.compute_mixing_spectrum(conversion)[source]#
Compute the frequency decomposition of
P(t).Returns the power spectrum of the conversion probability timeseries, showing which oscillation frequencies participate in the energy exchange. This is the temporal analog of spatial spectral decomposition — answering “at what timescales does energy transfer occur?”
Angular frequencies are reported directly (consistent with
compute_spectrum()which reports spatial wavenumbers as angular frequencies). No theory-specific conversion is applied. For each frequencyomega, the half-periodpi/omegagives the corresponding mixing timescale.- Parameters:
conversion (ConversionResult) – Output of
compute_conversion_probability()orcompute_group_conversion().- Return type:
- Raises:
ValueError – If the timestep is non-uniform or if fewer than 3 time points.
- tidal.measurement.compute_mode_amplitudes(data, field_name)[source]#
Track mode amplitudes
|φ̂(k)|over time.- Parameters:
data (SimulationData)
field_name (str) – Which field to decompose.
- Returns:
times (ndarray, shape
(n_snapshots,))wavenumbers (ndarray, shape
(n_modes,))amplitudes (ndarray, shape
(n_snapshots, n_modes)) –|FFT coefficient|at each(time, |k|)bin.
- Raises:
ValueError – If field_name is not in the spec.
- Return type:
tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]
- tidal.measurement.compute_reference_threshold(formula, reference_b, fixed_params, sim_settings)[source]#
Compute E-M reference conversion probability from an analytical formula.
- Parameters:
formula (str) –
"boccaletti"(localized Gaussian B-field) or"uniform"(uniform B, periodic domain).reference_b (float) – Reference magnetic field strength.
fixed_params (dict[str, Any]) – Fixed sweep parameters (must include
"kappa";"R"for Boccaletti).sim_settings (dict[str, Any]) – Simulation settings (
"t_end"for uniform formula).
- Returns:
Reference conversion probability P_EM.
- Return type:
- Raises:
ValueError – If required parameters are missing or formula is unknown.
- tidal.measurement.compute_resonance_analysis(data, source_field, target_field, *, resonance_threshold=0.1, min_amplitude=1e-12)[source]#
Identify resonant modes and compute conversion bandwidth.
A mode k is resonant when
|omega_source(k) - omega_target(k)| / omega_avg(k) < threshold.- Parameters:
data (SimulationData) – Simulation output.
source_field (str or sequence of str) – Source field name(s).
target_field (str or sequence of str) – Target field name(s).
resonance_threshold (float, optional) – Relative frequency mismatch threshold for resonance.
min_amplitude (float, optional) – Minimum Fourier amplitude for active modes.
- Return type:
- tidal.measurement.compute_snapshot_count(t_end, snapshot_interval)[source]#
Compute the exact number of snapshots for a simulation.
- Parameters:
- Returns:
Exact snapshot count (includes the initial state at t=0).
- Return type:
- Raises:
ValueError – If t_end or snapshot_interval are non-positive.
- tidal.measurement.compute_spectral_energy(field_data, velocity_data, mass_squared, grid_spacing, _periodic)[source]#
Compute per-mode energy density
ε(k) = 0.5 * [|π̂_k|² + (k²+m²)|φ̂_k|²] / N².Returns radially-averaged spectral energy density consistent with the spatial-average convention:
Σ_k ε(k) = ⟨ε⟩(Parseval).- Parameters:
- Returns:
wavenumbers (ndarray) – Radially binned
|k|values.spectral_energy (ndarray) – Energy per wavenumber bin.
- Raises:
TypeError – If mass_squared is an ndarray (position-dependent mass breaks the Fourier-diagonal structure).
- Return type:
tuple[NDArray[np.float64], NDArray[np.float64]]
- tidal.measurement.compute_spectrum(field_data, grid_spacing, periodic)[source]#
Compute the radially-averaged power spectrum of a field snapshot.
- Parameters:
- Return type:
- tidal.measurement.compute_system_energy(data, t_idx, _ctx=None, fields=None)[source]#
Compute Hamiltonian energy density at snapshot t_idx.
Uses the Legendre-transform Hamiltonian from canonical structure, decomposed into per-field self-energy and cross-field interaction. This is the only physically correct energy computation — the Hamiltonian coefficients include all metric/kinetic prefactors from the covariant Lagrangian, making it coordinate-invariant.
- Parameters:
data (SimulationData)
t_idx (int) – Snapshot index.
fields (set[str] or None) – If given, only evaluate Hamiltonian terms involving these base field names. Passed through to
_compute_hamiltonian_per_field._ctx (_HamiltonianContext | None)
- Raises:
ValueError – If t_idx is out of range, or if the spec lacks hamiltonian_terms (re-derive with
tidal deriveto populate them).- Return type:
- tidal.measurement.compute_velocities(data, field_names, *, min_amplitude=1e-12)[source]#
Extract group and phase velocities from the dispersion relation.
- Parameters:
data (SimulationData) – Simulation output with time-resolved field snapshots.
field_names (str or sequence of str) – Field or group of fields to analyze.
min_amplitude (float, optional) – Minimum Fourier amplitude for a mode to be considered active.
- Return type:
- Raises:
ValueError – If no active modes found or dispersion cannot be computed.
- tidal.measurement.compute_velocity_mismatch(data, source_field, target_field, *, min_amplitude=1e-12)[source]#
Compute group velocity mismatch between two field groups.
- Parameters:
- Return type:
- Raises:
ValueError – If no shared active modes between source and target.
- tidal.measurement.create_snapshot_callback(output_dir, spec, grid, t_end, snapshot_interval, parameters=None, spec_path=None)[source]#
Create a SnapshotWriter + callback for streaming snapshots to disk.
The returned callback accepts
(state, time)where state is apde.FieldCollection. Wrap it inCallbackTracker(callback, ...)and pass topde.solve(). Callwriter.close()after the solve.- Parameters:
output_dir (Path or str) – Directory to write snapshot files into.
spec (EquationSystem) – Equation system (provides field/velocity names and state layout).
grid (CartesianGrid) – py-pde grid (provides shape, spacing, bounds, periodicity).
t_end (float) – Total simulation time.
snapshot_interval (float) – Time between snapshots.
parameters (dict or None) – Resolved parameter values to store in metadata.
spec_path (Path or None) – Path to the JSON spec file (for auto-discovery by
tidal measure).
- Returns:
writer (SnapshotWriter) – Call
writer.close()after the solve finishes.callback (callable) – Pass to
CallbackTracker(callback, interrupts=snapshot_interval).
- Return type:
tuple[SnapshotWriter, Callable[[Any, float], None]]
- tidal.measurement.critical_field_to_sweep_results(result, original)[source]#
Convert a
CriticalFieldResultto a standardSweepResults.The field-strength parameter is removed from
swept_params, andB_min/inv_B_minbecome metric columns. The resulting object can be serialized and plotted with existing sweep infrastructure.- Parameters:
result (CriticalFieldResult) – Output of
compute_critical_field().original (SweepResults) – The source sweep data (used for fixed params, sim settings, etc.).
- Return type:
SweepResults
- tidal.measurement.summarize(data)[source]#
Compute a measurement summary of the simulation.
- Returns:
per_field_energy:dict[str, list[float]]time seriesinteraction_energy:list[float]total_energy:list[float]energy_conservation:EnergyDiagnosticsfield_peaks:dict[str, tuple[float, float]](initial, final peak amplitude)
- Return type:
dict with keys
- Parameters:
data (SimulationData)