| title | Getting Started |
|---|---|
| description | Install TIDE, verify the backend, and run a minimal 2D Maxwell simulation. |
uv pip install tide-GPROr with pip:
pip install tide-GPRgit clone https://github.com/vcholerae1/tide.git
cd tide
uv build:::caution[GPU support] If you use CUDA, install a CUDA-enabled PyTorch build before installing TIDE. :::
| Dependency | Version |
|---|---|
| Python | ≥ 3.12 |
| PyTorch | ≥ 2.12 |
| CUDA Toolkit | optional, for GPU support |
| CMake | ≥ 3.28, optional, for building from source |
:::tip[Goals for this page] You are done with this page when you can:
-
import tidesuccessfully - verify backend availability
- run one small 2D forward simulation
- identify where to find inversion and API docs next :::
import torch
import tide
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
dtype = torch.float32
ny, nx = 96, 96
epsilon = torch.full((ny, nx), 4.0, device=device, dtype=dtype)
sigma = torch.zeros_like(epsilon)
mu = torch.ones_like(epsilon)
nt = 300
dt = 4e-11
src = 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]import torch
import tide
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
dtype = torch.float32
nz, ny, nx = 32, 32, 32
epsilon = torch.full((nz, ny, nx), 4.0, device=device, dtype=dtype)
sigma = torch.zeros_like(epsilon)
mu = torch.ones_like(epsilon)
nt = 200
dt = 4e-11
src = 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)from tide import backend_utils
print("backend available:", backend_utils.is_backend_available())
print("library path: ", backend_utils.get_library_path()):::note If the backend is unavailable, TIDE can still run on reference paths for supported configurations, but performance will be lower. :::
- 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.
source_amplitudemust be[n_shots, n_sources, nt].source_locationandreceiver_locationmust be[n_shots, n_points, ndim].
Coordinates must satisfy 0 <= index < model_size for each spatial dimension.
TIDE adjusts the internal time step using CFL and resamples time signals.
Consider reducing dt or coarsening grid spacing.
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.
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_data
perturbed = operator(
tide.EMModel(epsilon_perturbed, sigma, mu)
).receiver_data
difference = perturbed - baseline
print("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.
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.
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.