3.5.0
-
Gas non-Darcy & partial-penetration pseudoskins - three new public functions in
pyrestoolbox.gasplus two matchingGasPVTconvenience 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, requiresphi). Returns β in 1/ft (or 1/m ifmetric=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 standard2.222e-15 · β · γg · k / (μg · h · rw)form (Jones 1987; Odeh, Moreland & Schueler 1975 JPT Dec). OutputDis in day/MSCF (or day/sm3 if metric) and can be passed directly togas_rate_radial(D=...).krg < 1evaluates β at the damaged-zone permeabilityk' = k·krgfor 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 fromscipy.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)andGasPVT.partial_penetration_skin(htot, htop, hbot, rw, kh_kv=10)- the former auto-computes μg from stored PVT state; both honour theGasPVT.metricflag.
18 new unit tests in
test_gas.py(all three β correlations, metric round-trip, damaged-zone, series convergence, geometry validation,GasPVTdelegation). 12 new doc-example tests intest_doc_examples.pypin 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 exportedPVTO.INCplaced 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 (PVTOkeyword). Stems with no undersaturated extension still carry/on the saturated row, and the final null-record/on its own line still terminates the table. Underlyingusatdata 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 oilandoil.<func>keep working viaoil/__init__.pyre-exports. -
Brine framework correctness (Rust path).
SoreideWhitson(framework='dropin')andframework='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 inbrine._lib_vle_engine(flash_tpandcalc_equilibrium) is now gated onframework == '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_rustandco2_brine_solubility_rustnow return the convergence bool they compute, soSoreideWhitson/CO2_Brine_Mixture.converged(and theconverged_aq/converged_naflash flags) report the real outcome instead of being hardcodedTruewhen Rust is active. -
Oil
oil_harmonizedefaultpbmethodunified to VALMC (wasVELAR), matchingoil_pbub/oil_rs/oil_co/OilPVT. Previouslyoil_harmonizeand the other oil functions returned mutually inconsistent Pb/Rsb on identical inputs. The deprecatedOilPVT.from_harmonizedefault was aligned too. Behaviour change: defaultoil_harmonizeoutput shifts for callers that relied on the old default (e.g. thersb_fracreturned when bothpbandrsbare supplied). Passpbmethod='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.3741molar-volume factor,10.73vs10.732,29.0vs28.97air 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 fromrtol=1e-3to1e-4. Affected HB doc-example BHPs shifted by ~0.05-0.25% to the now-identical Rust/Python value (e.g.fbhpoil 1771.47 → 1770.58 psi); RST examples updated to match. -
simtools.rr_solversingle-phase guard. Single-phase feeds (all-liquid or all-vapor) previously fell through to the two-phase Nielsen-Lia solver and returnedinf/nanwith 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 raiseValueError. -
nodal.fthpinput validation.fthpnow validateswell_type/vlpmethod/bhpat entry like the other public nodal functions, instead of only erroring transitively through the innerfbhpsolve. -
validate_pe_inputsarray safety. Thesgand inert-fraction checks are now array-safe (previouslysg <= 0raised 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 → 1singularity from the P+1 density used inCf_usat). Mathematically identical forxCO2 < 1. -
Removed dead Rust exports
oil_bo_mccain_rustandcalc_equilibrium_rust(and their now-orphaned internals), which had no Python callers. New Rust-vs-Python parity tests added forgas_ponz2pandsimtools.influence_tables- the two live accelerated paths that previously had no parity coverage. -
oil_matbalaquifer water influx. New optionalWeparameter (cumulative aquifer influx in reservoir volume, rb | rm3) brings oil material balance to parity withgas_matbal. The influx is subtracted from underground withdrawal before estimating OOIP (Havlena-OdehF - We = N·[Eo + m·Eg + (1+m)·Efw]) - feeding both the Python and Rust regression objectives - and a Water Drive Index ('WDI') is added todrive_indicesso the indices sum to 1 under water drive. Fully backward-compatible: withWeomitted,WDIis 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 inpyrestoolbox/gas/_hydrate.py(≈480 lines carved off thegas.pymonolith). Public API is unchanged -gas.gas_hydrateandgas.HydrateResultare re-exported._hydrateimportsgas_water_contentlazily, so there is no circular import. -
838 validation tests (up from 812 in 3.4.0).