Getting Started
Installation
Section titled “Installation”From PyPI
Section titled “From PyPI”uv pip install tide-GPROr with pip:
pip install tide-GPRFrom source
Section titled “From source”git clone https://github.com/vcholerae1/tide.gitcd tideuv buildRequirements
Section titled “Requirements”| Dependency | Version |
|---|---|
| Python | ≥ 3.12 |
| PyTorch | ≥ 2.12 |
| CUDA Toolkit | optional, for GPU support |
| CMake | ≥ 3.28, optional, for building from source |
First Success Criteria
Section titled “First Success Criteria”Minimal 2D Forward Run
Section titled “Minimal 2D Forward Run”import torchimport tide
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")dtype = torch.float32
ny, nx = 96, 96epsilon = torch.full((ny, nx), 4.0, device=device, dtype=dtype)sigma = torch.zeros_like(epsilon)mu = torch.ones_like(epsilon)
nt = 300dt = 4e-11src = tide.ricker(freq=8e8, length=nt, dt=dt, device=device, dtype=dtype).view(1, 1, nt)src_loc = torch.tensor([[[20, 48]]], device=device, dtype=torch.long)rec_loc = torch.tensor([[[20, 60]]], device=device, dtype=torch.long)
model = tide.EMModel(epsilon, sigma, mu)operator = tide.MaxwellTM( tide.Discretization(0.02, dt, boundary=tide.CPML(10)), tide.Experiment(tide.Acquisition(src_loc, rec_loc), src), execution=tide.ExecutionOptions(fallback=tide.FallbackPolicy.REFERENCE),)receivers = operator(model).receiver_data
print(receivers.shape) # [nt, n_shots, n_receivers]Optional 3D Preview
Section titled “Optional 3D Preview”import torchimport tide
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")dtype = torch.float32
nz, ny, nx = 32, 32, 32epsilon = torch.full((nz, ny, nx), 4.0, device=device, dtype=dtype)sigma = torch.zeros_like(epsilon)mu = torch.ones_like(epsilon)
nt = 200dt = 4e-11src = tide.ricker(freq=1e8, length=nt, dt=dt, device=device, dtype=dtype).view(1, 1, nt)src_loc = torch.tensor([[[16, 16, 16]]], device=device, dtype=torch.long)rec_loc = torch.tensor([[[16, 16, 20]]], device=device, dtype=torch.long)
model = tide.EMModel(epsilon, sigma, mu)operator = tide.Maxwell3D( tide.Discretization( [0.03, 0.03, 0.03], dt, boundary=tide.CPML(6), ), tide.Experiment(tide.Acquisition(src_loc, rec_loc), src), execution=tide.ExecutionOptions( backend=tide.BackendPreference.NATIVE, fallback=tide.FallbackPolicy.REFERENCE, ),)rec = operator(model).receiver_data
print(rec.shape)Verify Backend Availability
Section titled “Verify Backend Availability”from tide import backend_utils
print("backend available:", backend_utils.is_backend_available())print("library path: ", backend_utils.get_library_path())What To Read Next
Section titled “What To Read Next”- API orientation: understand models, experiments, operators, and derivative sessions.
- Modeling guide: configure forward simulations.
- Inversion workflow: connect receiver objectives and model updates.
- API reference: inspect the supported public contracts.
Common Startup Issues
Section titled “Common Startup Issues”Shape mismatch
Section titled “Shape mismatch”source_amplitudemust be[n_shots, n_sources, nt].source_locationandreceiver_locationmust be[n_shots, n_points, ndim].
Out-of-bounds indices
Section titled “Out-of-bounds indices”Coordinates must satisfy 0 <= index < model_size for each spatial dimension.
Instability warning
Section titled “Instability warning”TIDE adjusts the internal time step using CFL and resamples time signals.
Consider reducing dt or coarsening grid spacing.
Read the first result
Section titled “Read the first result”The minimal example has one shot, one source, and one receiver. Its result shape
is therefore [300, 1, 1]. The first axis is physical time sampled every
4e-11 seconds. The other axes identify shot and receiver.
The returned tensor is differentiable. Confirm this before building an inversion:
epsilon = epsilon.clone().requires_grad_(True)result = operator(tide.EMModel(epsilon, sigma, mu))objective = result.receiver_data.square().mean()objective.backward()
print("loss:", float(objective.detach()))print("gradient shape:", epsilon.grad.shape)print("finite gradient:", bool(torch.isfinite(epsilon.grad).all()))This is a computational check, not a physical validation. A finite gradient can still be wrong because of units, geometry, insufficient resolution, or a misinterpreted component.
Make one controlled change
Section titled “Make one controlled change”Change a compact region of relative permittivity and compare traces:
epsilon_perturbed = epsilon.detach().clone()epsilon_perturbed[45:55, 50:60] = 7.0
baseline = operator(tide.EMModel(epsilon.detach(), sigma, mu)).receiver_dataperturbed = operator( tide.EMModel(epsilon_perturbed, sigma, mu)).receiver_data
difference = perturbed - baselineprint("maximum trace change:", float(difference.abs().max()))This exercise establishes the core forward-modeling contract: the operator and experiment stay fixed, the material model changes, and receiver data records the effect.
Understand internal sub-stepping
Section titled “Understand internal sub-stepping”The source and returned receiver tensors use the dt supplied to
Discretization. TIDE may choose a smaller internal time step to satisfy the
CFL condition:
from tide.cfl import cfl_condition
inner_dt, step_ratio = cfl_condition( grid_spacing=0.02, dt=dt, max_vel=299_792_458.0,)print(inner_dt, step_ratio)When step_ratio > 1, propagation performs several internal updates per user
sample. TIDE upsamples the source and downsamples receiver data automatically.
The output still contains nt samples, but runtime increases with the ratio.
First physical checks
Section titled “First physical checks”Before replacing the homogeneous model with survey data:
- Estimate the direct-arrival time from source-receiver distance and expected material velocity.
- Confirm the trace becomes active near that time.
- Move the receiver farther away and confirm the arrival is later.
- Increase CPML width and confirm the direct arrival stays fixed while late boundary energy changes.
- Repeat with a finer grid or higher stencil order and compare phase.
These checks reveal coordinate, unit, boundary, and dispersion errors earlier than an inversion loop will.