Quick start

This page walks through the entire pipeline on a synthetic DST so it can be reproduced without any external file.

1. Load gauge data

For a real test you would use from_file():

from welltest_pta import WellTest

wt = WellTest.from_file("DST_WELL-6.txt", cross_validate=True)

For this tutorial we generate a multi-rate synthetic test:

from welltest_pta import WellTest
from welltest_pta.utils.synthetic import generate_synthetic_dst

df = generate_synthetic_dst(
    n_samples=20_000,
    sample_period_s=4.0,
    sequence=[
        ("DD", 0.5, 3300.0),    # initial flow
        ("BU", 1.0, 4490.0),    # short BU
        ("DD", 1.0, 3000.0),    # main DD
        ("BU", 8.0, 4495.0),    # extended BU
    ],
)
wt = WellTest.from_dataframe(df)

2. Inspect the catalogue

wt.print_summary()
════════════════════════════════════════════════════════════════════════
  WELL TEST SUMMARY
════════════════════════════════════════════════════════════════════════
  Samples:        20 000
  P_reservoir:    4 495.10 psi
  Noise floor:    0.97 psi
  Events:         4  (2 DD, 2 BU)
════════════════════════════════════════════════════════════════════════
 event_id      type  duration_hr  p_initial  p_final  delta_p  rate_psi_hr  n_points
     DD-1  drawdown         0.50    4495.06  3300.40 -1194.66     -2389.32       450
     BU-1   buildup         1.00    3300.51  4488.10  1187.59     +1187.59       900
     DD-2  drawdown         1.00    4486.43  3010.55 -1475.88     -1475.88       900
     BU-2   buildup         8.00    3010.55  4495.30  1484.75      +185.59      7200

3. Per-event analysis

Every event is a first-class Event with PTA-specific methods:

bu = wt.events["BU-2"]                          # by event_id
bu.print()

# Bourdet log–log diagnostic
bu.plot_loglog()

# Horner extrapolation
h = bu.horner()
print(f"P*    = {h['p_star']:.1f} psi")
print(f"slope = {h['slope_m']:.2f} psi/cycle")
print(f"R²    = {h['r2']:.4f}")

# Reservoir parameters (oilfield units)
params = bu.reservoir_params(
    q=850, mu=0.45, B=1.18,                     # rate, viscosity, FVF
    h=18, phi=0.12, ct=1.2e-5, rw=0.108,        # net pay, φ, c_t, r_w
    method="horner",
)
print(f"k    = {params['k']:.3f} mD")
print(f"kh   = {params['kh']:.1f} mD·ft")
print(f"skin = {params['skin']:+.3f}")

4. Manual splitting (when CV score is marginal)

If cross_validate_detector() returns a confidence below 60/100, override the auto-detector with explicit timestamps:

wt.split_manual([
    ("DD", "2025-01-15 10:00", "2025-01-15 12:30"),
    ("BU", "2025-01-15 12:30", "2025-01-15 18:00"),
    ("DD", "2025-01-15 18:00", "2025-01-15 20:00"),
    ("BU", "2025-01-15 20:00", "2025-01-16 04:00"),
])

The EventCollection is rebuilt in place; all per-event methods continue to work unchanged.

5. Multi-event deconvolution

Merge all DDs and BUs into one equivalent unit-rate response:

from welltest_pta import deconvolve

recon = deconvolve(
    wt.events,
    default_q=850,                  # STB/D for any DD without ev.rate
    nu=1e-2,                        # regularisation
    n_response_nodes=60,
)

print(f"converged = {recon.converged}, ||r|| = {recon.residual_norm:.2f} psi")
recon.plot()                        # log–log of merged response
recon.export("decon.csv")

See Multi-event deconvolution for algorithmic details.

6. Composite report + bulk export

wt.plot_composite(out_path="report.pdf")

wt.export_all(
    out_dir="./output",
    prefix="DST_WELL-6",
    per_event=True,                 # one CSV per event
)

Next steps