Skip to content

3.5.0

Choose a tag to compare

@mwburgoyne mwburgoyne released this 31 May 04:03
· 5 commits to main since this release
  • Gas non-Darcy & partial-penetration pseudoskins - three new public functions in pyrestoolbox.gas plus two matching GasPVT convenience methods. Oilfield + metric unit support throughout.

    • gas.gas_hvf_beta(k, method='FK' | 'JONES' | 'TCK', phi=0, metric=False) - Forchheimer high-velocity-flow coefficient β. Three correlations: 'FK' (default, log-log fit of Firoozabadi & Katz 1979 JPT Feb, consolidated-rock β(k) chart), 'JONES' (Jones 1987 SPE-16949), 'TCK' (Tek, Coats, Katz 1962 JPT Jul, requires phi). Returns β in 1/ft (or 1/m if metric=True).
    • gas.gas_non_darcy_skin(qg, k, h_perf, rw, mug, sg, krg=1.0, beta_method='FK', phi=0, metric=False) - returns {'beta', 'D', 'S_hvf'} where D uses the standard 2.222e-15 · β · γg · k / (μg · h · rw) form (Jones 1987; Odeh, Moreland & Schueler 1975 JPT Dec). Output D is in day/MSCF (or day/sm3 if metric) and can be passed directly to gas_rate_radial(D=...). krg < 1 evaluates β at the damaged-zone permeability k' = k·krg for a pessimistic Shvf.
    • gas.gas_partial_penetration_skin(htot, htop, hbot, rw, kh_kv=10) - partial-penetration pseudoskin by direct evaluation of the Streltsova-Adams (1979) SPE-7486 analytical series. Bessel K0 from scipy.special.k0; vectorised summation to 20,000 terms (sub-millisecond). Warns when the series tail suggests incomplete convergence for very thin wellbores. Unit-agnostic - only ratios enter the formula, so inputs share any consistent length unit.
    • GasPVT.non_darcy_skin(qg, p, degf, k, h_perf, rw, krg=1, beta_method='FK', phi=0) and GasPVT.partial_penetration_skin(htot, htop, hbot, rw, kh_kv=10) - the former auto-computes μg from stored PVT state; both honour the GasPVT.metric flag.

    18 new unit tests in test_gas.py (all three β correlations, metric round-trip, damaged-zone, series convergence, geometry validation, GasPVT delegation). 12 new doc-example tests in test_doc_examples.py pin every RST example value. Reference Sp values verified against a 50,000-term direct evaluation of the Streltsova-Adams series.

  • PVTO export format fix (simtools.make_bot_og(pvto=True, export=True)). The exported PVTO.INC placed Eclipse's / stem-terminator on every saturated row, prematurely closing each stem before its undersaturated continuation rows. A strict parser would then read each continuation row as an unset stem and either error or drop the data. The saturated row now omits / when undersaturated rows follow; the terminator moves to the LAST undersaturated row of the stem, matching the reference example in the Eclipse manual (PVTO keyword). Stems with no undersaturated extension still carry / on the saturated row, and the final null-record / on its own line still terminates the table. Underlying usat data returned in the results dict was already correct - only the export layout changed.

  • Removed dead module pyrestoolbox/oil/oil.py. Pre-April-2026 monolith superseded by the _correlations.py / _density.py / _tables.py / etc. sub-modules. Nothing in the package or tests imported from it. No public-API change - from pyrestoolbox import oil and oil.<func> keep working via oil/__init__.py re-exports.

  • Brine framework correctness (Rust path). SoreideWhitson(framework='dropin') and framework='sw_original' were silently computed as 'proposed' whenever the Rust accelerator was installed: the Rust flash hardcodes the proposed-framework MC-3 water alpha and kijAQ, with no framework parameter. The Rust dispatch in brine._lib_vle_engine (flash_tp and calc_equilibrium) is now gated on framework == 'proposed'; the other two frameworks take the framework-aware Python path. Pure-Python results were always correct. Adds a regression test exercising both non-proposed frameworks.

  • Brine convergence flag (Rust path). flash_tp_rust and co2_brine_solubility_rust now return the convergence bool they compute, so SoreideWhitson/CO2_Brine_Mixture .converged (and the converged_aq/converged_na flash flags) report the real outcome instead of being hardcoded True when Rust is active.

  • Oil oil_harmonize default pbmethod unified to VALMC (was VELAR), matching oil_pbub / oil_rs / oil_co / OilPVT. Previously oil_harmonize and the other oil functions returned mutually inconsistent Pb/Rsb on identical inputs. The deprecated OilPVT.from_harmonize default was aligned too. Behaviour change: default oil_harmonize output shifts for callers that relied on the old default (e.g. the rsb_frac returned when both pb and rsb are supplied). Pass pbmethod='VELAR' explicitly to retain the old behaviour.

  • Nodal HB VLP Rust/Python parity. Hampson-Brill superficial-gas-velocity and gas-density constants in the Rust accelerator (35.3741 molar-volume factor, 10.73 vs 10.732, 29.0 vs 28.97 air MW) are aligned with the Python path, making all four VLP methods bit-exact between Rust and Python. The HB-oil/HB-gas parity tests are tightened from rtol=1e-3 to 1e-4. Affected HB doc-example BHPs shifted by ~0.05-0.25% to the now-identical Rust/Python value (e.g. fbhp oil 1771.47 → 1770.58 psi); RST examples updated to match.

  • simtools.rr_solver single-phase guard. Single-phase feeds (all-liquid or all-vapor) previously fell through to the two-phase Nielsen-Lia solver and returned inf/nan with only a divide-by-zero warning. They are now detected up front and return the trivial split (V=0 or V=1); non-positive K-values raise ValueError.

  • nodal.fthp input validation. fthp now validates well_type / vlpmethod / bhp at entry like the other public nodal functions, instead of only erroring transitively through the inner fbhp solve.

  • validate_pe_inputs array safety. The sg and inert-fraction checks are now array-safe (previously sg <= 0 raised an ambiguous-truth-value error on array inputs).

  • Brine undersaturated-compressibility density now uses the same algebraically reformulated, non-singular Garcia Eq. 18 as the saturated-density step (removes the xCO2 → 1 singularity from the P+1 density used in Cf_usat). Mathematically identical for xCO2 < 1.

  • Removed dead Rust exports oil_bo_mccain_rust and calc_equilibrium_rust (and their now-orphaned internals), which had no Python callers. New Rust-vs-Python parity tests added for gas_ponz2p and simtools.influence_tables - the two live accelerated paths that previously had no parity coverage.

  • oil_matbal aquifer water influx. New optional We parameter (cumulative aquifer influx in reservoir volume, rb | rm3) brings oil material balance to parity with gas_matbal. The influx is subtracted from underground withdrawal before estimating OOIP (Havlena-Odeh F - We = N·[Eo + m·Eg + (1+m)·Efw]) - feeding both the Python and Rust regression objectives - and a Water Drive Index ('WDI') is added to drive_indices so the indices sum to 1 under water drive. Fully backward-compatible: with We omitted, WDI is all-zero and OOIP/DDI/SDI/CDI are unchanged.

  • Internal: hydrate code split out of gas.py. gas_hydrate, HydrateResult, the HFT/HFP/Østergaard helpers and their constants now live in pyrestoolbox/gas/_hydrate.py (≈480 lines carved off the gas.py monolith). Public API is unchanged - gas.gas_hydrate and gas.HydrateResult are re-exported. _hydrate imports gas_water_content lazily, so there is no circular import.

  • 838 validation tests (up from 812 in 3.4.0).