Skip to content

WP6: ray tracing and Q-compensated depth imaging (the Fig. 3 experiment) - #7

Merged
ingehap merged 1 commit into
mainfrom
claude/blissful-gauss-3y1u5w
Oct 14, 2026
Merged

ingehap merged 1 commit into
mainfrom
claude/blissful-gauss-3y1u5w

Conversation

@ingehap

@ingehap ingehap commented Oct 14, 2026

Copy link
Copy Markdown
Owner

This PR implements work package 6 of docs/IMPLEMENTATION_PLAN.md: layered VTI ray tracing and ray-based Q-compensated depth migration (Cavalca et al., 2013). With them it reproduces the article's Fig. 3 experiment.

What is added

  • iwt.raytrace
    • Exact qP group velocity, and an analytic d²q/dp².
    • reflection_table: for every depth × offset in layered VTI it returns the two-way PP time, ray parameter, reflection angle and t* = Σt/Q. It also returns the stationary-phase spreading factor that turns a point-source amplitude into the plane-wave R(θ).
  • iwt.imaging
    • A first-order Kjartansson constant-Q operator, written in numpy's FFT convention.
    • Inverse Q with Wang's (2002) stabilization, set by the maximum gain.
    • 1D true-amplitude prestack depth migration of CMP gathers. Every output point gets its own inverse Q filter, either amplitude + phase or phase-only, with a common high-cut taper.
  • iwt.avo: event tracking across migrated gathers, and A + B sin²θ fits.
  • examples/wp6_imaging.py and docs/figures/wp6_*.png:
    • Alkhalifah–Tsvankin moveout check;
    • reflectivity Born gathers with and without Q;
    • eight migration variants scored on the Stø-top AVA;
    • a gain-limit study on noisy data.
  • Tests: 15 new.
    • Kinematics: group velocity vs Tsvankin, homogeneous VTI vs analytic, dT/dx = p, Alkhalifah–Tsvankin accuracy.
    • Q operators: comparison with the exact Kjartansson operator, the gain limit, wavelet recovery.
    • Migration: true amplitude vs exact R, Q compensation in migration, isotropic residual moveout.
    • AVO: fits and event tracking.
  • README: WP6 sections added, and all sections reordered by WP. The WP5 sections had been out of order. The plan is updated and the version is 0.6.0.

Main findings

The "observed" gather has Q; the reference is the Q-free gather migrated with the true VTI model. AVA is compared over 20–44°.

Variant AVA misfit
VTI + Q compensation 0.014 (gradient 0.96 × reference)
Isotropic (NMO velocity) + Q compensation 0.16
Isotropic (vertical Vp0) + Q compensation 0.24
VTI, no Q compensation 0.53
  • The article's ranking is reproduced. Without Q the result is far worse than the article's "OK", because the synthetic's overburden Q of 40–60 removes most of the gradient.
  • A Q model 30% too low overshoots the gradient by 58%. This is the risk of compensating with an apparent Q instead of the intrinsic one.
  • The Stø-top AVA is tuned by the stringers 11 and 24 m above it. The single-interface R has only 57% of the intercept.
  • The seabed reflection limits the reservoir AVA to about 45°. It crosses the Stø reflection at about 1150 m of offset.
  • Strong shallow events must be muted before Q compensation. Otherwise the stabilized inverse filter leaks their energy onto the target.
  • Beyond a gain limit of about 10 (with an 80 Hz high cut), noise sets the error. Higher gain no longer reduces it.

Validation

  • Python 3.13 with RuntimeWarning and DeprecationWarning as errors: 143 passed.
  • Python 3.9 with numpy 1.21 and scipy 1.7: 139 passed, 1 skipped.
  • pyflakes is clean, and the README quick start runs.

🤖 Generated with Claude Code

https://claude.ai/code/session_019vaUDUPicoFeQaUhfhnoYq


Generated by Claude Code

- iwt.raytrace: exact qP group velocity; second derivative of q(p);
  reflection_table with two-way PP time, ray parameter, reflection angle,
  t* = sum t/Q and the stationary-phase (true-amplitude) spreading factor for
  every depth x offset in layered VTI, by Newton refinement on exact sums.
- iwt.imaging: first-order Kjartansson constant-Q operator in numpy's FFT
  convention; inverse Q with Wang's (2002) stabilization set by the maximum
  gain; 1D true-amplitude prestack depth migration of CMP gathers with a
  per-output-point inverse Q filter (ray-based, Cavalca et al., 2013),
  amplitude+phase or phase-only, common high cut.
- iwt.avo: event tracking on migrated gathers; A + B sin^2 fits.
- examples/wp6_imaging.py and docs/figures/wp6_*.png: Alkhalifah-Tsvankin
  moveout, reflectivity Born gathers with and without Q, eight migration
  variants scored on the Sto-top AVA, gain limit against noise.
- Tests (15 new), README (sections reordered by WP) and plan; version 0.6.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019vaUDUPicoFeQaUhfhnoYq
@ingehap
ingehap marked this pull request as ready for review October 14, 2026 20:09
Copilot AI balanced review requested due to automatic review settings October 14, 2026 20:09
@ingehap
ingehap merged commit 9377c05 into main Oct 14, 2026
4 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved correctness issues affect valid reflection rays, Q filtering and event tracking.

4 open findings
What changed in this PR

Implements WP6, extending the well-tie workflow with layered VTI reflection rays and Q-compensated depth imaging for the Fig. 3 experiment.

Changes:

  • Adds reflection tables, stabilized inverse-Q filtering, migration and AVA extraction.
  • Adds numerical validation and an imaging comparison example.
  • Documents WP6 results and updates the package version.
File Description
tests/​test_imaging.py Tests kinematics, Q operators, migration and AVA.
README.md Documents WP6 usage, results and assumptions.
pyproject.toml Updates version to 0.6.0.
iwt/​raytrace.py Adds group velocity and reflection tables.
iwt/​imaging.py Adds Q filtering and depth migration.
iwt/​avo.py Adds event tracking and AVA fitting.
iwt/​__init__.py Exports new modules and updates version.
examples/​wp6_imaging.py Demonstrates migration variants and gain sensitivity.
docs/​IMPLEMENTATION_PLAN.md Records WP6 completion and scope.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread iwt/raytrace.py
Comment on lines +298 to +302
q, dq, d2q = qp_vertical_slowness(p[:, None], *a, derivative=2)
X = -(wk * dq).sum(axis=1)
dX = -(wk * d2q).sum(axis=1)
p = np.clip(p - (X - xi) / dX, 0.0, p_cap[k - 1] * (1.0 - 1e-12))
q, dq, d2q = qp_vertical_slowness(p[:, None], *a, derivative=2)
Comment thread iwt/avo.py
order = list(range(start, n)) + list(range(start - 1, -1, -1))
for j in order:
if j == start - 1:
centre = out.depth[start]
Comment thread iwt/imaging.py
Comment on lines +69 to +73
f = np.abs(np.asarray(f, dtype=float))
tstar = np.asarray(tstar, dtype=float)
with np.errstate(divide="ignore", invalid="ignore"):
lnf = np.where(f > 0, np.log(np.where(f > 0, f, 1.0) / f_ref), 0.0)
return np.exp(-np.pi * f * tstar) * np.exp(2j * f * tstar * lnf)
Comment thread iwt/imaging.py
Comment on lines +78 to +90
from scipy.optimize import brentq

if not gain_limit > 1.0:
raise ValueError("gain_limit must exceed 1")
if not np.isfinite(gain_limit):
return 0.0

def peak(log_s):
s = np.exp(log_s)
r = np.sqrt(s * s + s) # beta* + s at the maximum
return r / (2.0 * s * (s + 1.0 - r)) - gain_limit

return float(np.exp(brentq(peak, -60.0, 10.0)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants