pyrestoolbox 3.7.5__tar.gz → 3.7.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyrestoolbox-3.7.7/.github/complexity-baseline.json +34 -0
- pyrestoolbox-3.7.7/.github/scripts/check_complexity.py +84 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/.github/workflows/tests.yml +34 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/.gitignore +2 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/Cargo.lock +1 -1
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/Cargo.toml +1 -1
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/PKG-INFO +1 -1
- pyrestoolbox-3.7.7/clippy.toml +7 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/mcp/pyrestoolbox_mcp/server.py +16 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyproject.toml +1 -1
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/brine.py +56 -27
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/pr_vphi.py +14 -12
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/vphi_route.py +4 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/brine.rst +13 -6
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/changelist.rst +22 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/oil.rst +3 -1
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/gas/gas.py +31 -31
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/matbal/matbal.py +115 -88
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/nodal/nodal.py +146 -138
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/__init__.py +1 -1
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_compressibility.py +7 -1
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_constants.py +5 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_correlations.py +49 -10
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_density.py +14 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/simtools/_decks.py +178 -158
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/simtools/_relperm.py +30 -25
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/simtools/_vfp.py +288 -258
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/bessel.rs +5 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/critical_properties/mod.rs +2 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/dca/hyperbolic.rs +6 -6
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/dca/ransac.rs +10 -10
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/gwr.rs +2 -3
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/matbal/objective.rs +4 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/oil/density.rs +20 -13
- pyrestoolbox-3.7.7/src/pseudopressure.rs +371 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/spycher_pruess/mod.rs +3 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/spycher_pruess/solubility.rs +25 -16
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vle/alpha.rs +6 -6
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vle/bip.rs +6 -4
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vle/components.rs +4 -4
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vle/eos.rs +6 -6
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vle/flash.rs +8 -8
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vle/fugacity.rs +5 -5
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vle/k_init.rs +6 -6
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vle/mod.rs +7 -7
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vle/rachford_rice.rs +8 -8
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/constants.rs +3 -3
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/friction.rs +2 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/holdup_bb.rs +4 -4
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/holdup_gray.rs +2 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/holdup_wg.rs +2 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/ift.rs +2 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/march.rs +4 -4
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/mod.rs +11 -5
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/pvt_helpers.rs +9 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/vlp/static_column.rs +3 -2
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/zfactor/mod.rs +4 -4
- pyrestoolbox-3.7.5/src/pseudopressure.rs +0 -432
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/.github/workflows/build-wheels.yml +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/LICENSE +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/MANIFEST.in +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/README.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/ResToolbox/privacy_policy.md +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/build_pure_python.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/mcp/DEPLOYMENT.md +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/mcp/Dockerfile +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/mcp/README.md +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/mcp/pyproject.toml +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/mcp/pyrestoolbox_mcp/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/mcp/pyrestoolbox_mcp/serialize.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/mcp/tests/test_server.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/_accelerator.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/_lib_dielectric.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/_lib_maoduan_viscosity.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/_lib_pitzer_params.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/_lib_salting_library.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/_lib_vle_engine.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/appelo_volumes.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/iapws_viscosity.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/jones_dole_viscosity.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/kestin_nacl_viscosity.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/brine/viscosity_route.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/classes/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/classes/classes.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/constants/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/constants/constants.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/dca/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/dca/dca.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/dca.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/examples.ipynb +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/gas.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/img/bot.png +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/img/bot_PVTO.png +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/img/bot_img.png +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/img/dry_gas.png +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/img/grid_sat_df.png +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/img/influence.png +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/img/properties_df.png +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/img/sgof.png +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/img/swof.png +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/index.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/layer.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/library.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/matbal.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/nodal.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/nodal_examples.ipynb +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/nodal_hydrate_demo.ipynb +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/plyasunov.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/recommend.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/sensitivity.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/docs/simtools.rst +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/gas/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/gas/_hydrate.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/layer/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/layer/layer.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/library/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/library/component_library.xlsx +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/library/library.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/matbal/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/nodal/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_harmonize.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_pvt_class.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_rate.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_separator.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_tables.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/oil/_utils.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/plyasunov/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/plyasunov/iapws_if97.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/plyasunov/plyasunov_model.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/plyasunov/water_properties.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/recommend/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/recommend/recommend.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/sensitivity/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/sensitivity/sensitivity.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/shared_fns/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/shared_fns/shared_fns.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/simtools/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/simtools/_aquifer.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/simtools/_pvt_tables.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/simtools/simtools.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/validate/__init__.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/pyrestoolbox/validate/validate.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/setup.cfg +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/setup.py +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/dca/mod.rs +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/gas_viscosity/mod.rs +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/lib.rs +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/matbal/mod.rs +0 -0
- {pyrestoolbox-3.7.5 → pyrestoolbox-3.7.7}/src/oil/mod.rs +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pyrestoolbox/brine/_lib_vle_engine.py::SWBinaryVLE.calc_gas_solubility": 16,
|
|
3
|
+
"pyrestoolbox/brine/_lib_vle_engine.py::SWMultiComponentFlash.build_kij_matrix": 18,
|
|
4
|
+
"pyrestoolbox/brine/_lib_vle_engine.py::SWMultiComponentFlash.calc_equilibrium": 24,
|
|
5
|
+
"pyrestoolbox/brine/_lib_vle_engine.py::calc_gas_brine_equilibrium": 29,
|
|
6
|
+
"pyrestoolbox/brine/brine.py::CO2_Brine_Mixture.__init__": 18,
|
|
7
|
+
"pyrestoolbox/brine/brine.py::SoreideWhitson.__init__": 28,
|
|
8
|
+
"pyrestoolbox/brine/brine.py::SoreideWhitson._calc_properties": 19,
|
|
9
|
+
"pyrestoolbox/brine/brine.py::brine_props": 19,
|
|
10
|
+
"pyrestoolbox/dca/dca.py::fit_decline": 17,
|
|
11
|
+
"pyrestoolbox/dca/dca.py::fit_decline_cum": 22,
|
|
12
|
+
"pyrestoolbox/dca/dca.py::forecast": 16,
|
|
13
|
+
"pyrestoolbox/dca/dca.py::hyp2_from_eur": 18,
|
|
14
|
+
"pyrestoolbox/gas/_hydrate.py::gas_hydrate": 26,
|
|
15
|
+
"pyrestoolbox/gas/gas.py::gas_ponz2p": 16,
|
|
16
|
+
"pyrestoolbox/gas/gas.py::gas_tc_pc": 16,
|
|
17
|
+
"pyrestoolbox/gas/gas.py::gas_ug": 16,
|
|
18
|
+
"pyrestoolbox/matbal/matbal.py::gas_matbal": 26,
|
|
19
|
+
"pyrestoolbox/matbal/matbal.py::oil_matbal": 35,
|
|
20
|
+
"pyrestoolbox/nodal/nodal.py::Completion.__init__": 21,
|
|
21
|
+
"pyrestoolbox/nodal/nodal.py::_bb_flow_pattern": 18,
|
|
22
|
+
"pyrestoolbox/nodal/nodal.py::_prepare_flow_inputs": 16,
|
|
23
|
+
"pyrestoolbox/nodal/nodal.py::ipr_curve": 27,
|
|
24
|
+
"pyrestoolbox/nodal/nodal.py::operating_point": 18,
|
|
25
|
+
"pyrestoolbox/oil/_compressibility.py::oil_co": 16,
|
|
26
|
+
"pyrestoolbox/oil/_correlations.py::oil_pbub": 16,
|
|
27
|
+
"pyrestoolbox/oil/_harmonize.py::oil_harmonize": 16,
|
|
28
|
+
"pyrestoolbox/shared_fns/shared_fns.py::validate_pe_inputs": 43,
|
|
29
|
+
"pyrestoolbox/simtools/_decks.py::_crawl_deck_includes": 16,
|
|
30
|
+
"pyrestoolbox/simtools/_decks.py::ix_extract_problem_cells": 41,
|
|
31
|
+
"pyrestoolbox/simtools/_vfp.py::_format_vfpprod": 19,
|
|
32
|
+
"pyrestoolbox/simtools/_vfp.py::_vfpprod_axes_to_oilfield": 24,
|
|
33
|
+
"pyrestoolbox/simtools/_vfp.py::make_vfpinj": 20
|
|
34
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Cyclomatic-complexity ratchet for pyrestoolbox.
|
|
3
|
+
|
|
4
|
+
Fails when a function's McCabe complexity exceeds MAX_COMPLEXITY unless that
|
|
5
|
+
function is already recorded in the baseline at its recorded value or lower.
|
|
6
|
+
The point is to stop new branching accreting, not to force a rewrite of the
|
|
7
|
+
functions that are already large - those are listed in the baseline and can only
|
|
8
|
+
get simpler.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
python3 .github/scripts/check_complexity.py # check
|
|
12
|
+
python3 .github/scripts/check_complexity.py --update # rewrite the baseline
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
MAX_COMPLEXITY = 15
|
|
21
|
+
PACKAGE = "pyrestoolbox"
|
|
22
|
+
BASELINE = Path(__file__).resolve().parent.parent / "complexity-baseline.json"
|
|
23
|
+
SKIP_DIRS = ("/tests/", "\\tests\\")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def measure():
|
|
27
|
+
"""Return {"path::function": complexity} for every function in the package."""
|
|
28
|
+
out = subprocess.run(
|
|
29
|
+
[sys.executable, "-m", "radon", "cc", "-j", PACKAGE],
|
|
30
|
+
capture_output=True, text=True, check=True,
|
|
31
|
+
).stdout
|
|
32
|
+
scores = {}
|
|
33
|
+
for path, blocks in json.loads(out).items():
|
|
34
|
+
if any(d in "/" + path.replace("\\", "/") + "/" for d in ("/tests/",)):
|
|
35
|
+
continue
|
|
36
|
+
if isinstance(blocks, dict): # radon reports parse errors as a dict
|
|
37
|
+
continue
|
|
38
|
+
for b in blocks:
|
|
39
|
+
name = f"{b['classname']}.{b['name']}" if b.get("classname") else b["name"]
|
|
40
|
+
key = f"{path.replace(chr(92), '/')}::{name}"
|
|
41
|
+
scores[key] = max(scores.get(key, 0), b["complexity"])
|
|
42
|
+
return scores
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def main():
|
|
46
|
+
scores = measure()
|
|
47
|
+
over = {k: v for k, v in scores.items() if v > MAX_COMPLEXITY}
|
|
48
|
+
|
|
49
|
+
if "--update" in sys.argv:
|
|
50
|
+
BASELINE.write_text(json.dumps(dict(sorted(over.items())), indent=2) + "\n")
|
|
51
|
+
print(f"Wrote {len(over)} entries to {BASELINE.name}")
|
|
52
|
+
return 0
|
|
53
|
+
|
|
54
|
+
baseline = json.loads(BASELINE.read_text()) if BASELINE.exists() else {}
|
|
55
|
+
|
|
56
|
+
new = {k: v for k, v in over.items() if k not in baseline}
|
|
57
|
+
worse = {k: (baseline[k], v) for k, v in over.items() if k in baseline and v > baseline[k]}
|
|
58
|
+
better = {k: (baseline[k], scores.get(k, 0)) for k in baseline
|
|
59
|
+
if scores.get(k, 0) < baseline[k]}
|
|
60
|
+
|
|
61
|
+
for key, (was, now) in sorted(better.items()):
|
|
62
|
+
print(f"improved {key}: {was} -> {now}")
|
|
63
|
+
|
|
64
|
+
if not new and not worse:
|
|
65
|
+
if better:
|
|
66
|
+
print("\nRun with --update to lock in the improvements above.")
|
|
67
|
+
print(f"\nOK - no function above {MAX_COMPLEXITY} outside the baseline.")
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
print(f"\nCyclomatic complexity above {MAX_COMPLEXITY}:")
|
|
71
|
+
for key, val in sorted(new.items()):
|
|
72
|
+
print(f" NEW {key}: {val}")
|
|
73
|
+
for key, (was, now) in sorted(worse.items()):
|
|
74
|
+
print(f" WORSE {key}: {was} -> {now}")
|
|
75
|
+
print(
|
|
76
|
+
"\nSplit the function, or - if the branching is irreducible - run\n"
|
|
77
|
+
" python3 .github/scripts/check_complexity.py --update\n"
|
|
78
|
+
"and say why in the commit message."
|
|
79
|
+
)
|
|
80
|
+
return 1
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
sys.exit(main())
|
|
@@ -62,3 +62,37 @@ jobs:
|
|
|
62
62
|
|
|
63
63
|
- name: Run full test suite (Rust + parity)
|
|
64
64
|
run: python3 -m pytest pyrestoolbox/tests/ -q
|
|
65
|
+
|
|
66
|
+
# ── Complexity ratchet (new code only; existing offenders baselined) ─
|
|
67
|
+
complexity:
|
|
68
|
+
name: Complexity ratchet
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v5
|
|
72
|
+
|
|
73
|
+
- uses: actions/setup-python@v6
|
|
74
|
+
with:
|
|
75
|
+
python-version: "3.12"
|
|
76
|
+
|
|
77
|
+
- name: Install radon
|
|
78
|
+
run: pip install radon
|
|
79
|
+
|
|
80
|
+
- name: Check cyclomatic complexity against baseline
|
|
81
|
+
run: python3 .github/scripts/check_complexity.py
|
|
82
|
+
|
|
83
|
+
# ── Rust lint ratchet (clippy clean; any new warning fails) ─────────
|
|
84
|
+
rust-lint:
|
|
85
|
+
name: Clippy
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/checkout@v5
|
|
89
|
+
|
|
90
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
91
|
+
with:
|
|
92
|
+
components: clippy
|
|
93
|
+
|
|
94
|
+
- name: Install m4 (needed by gmp-mpfr-sys for the rug crate)
|
|
95
|
+
run: sudo apt-get update -qq && sudo apt-get install -y -qq m4
|
|
96
|
+
|
|
97
|
+
- name: Clippy
|
|
98
|
+
run: cargo clippy --all-targets -- -D warnings
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Reservoir-engineering kernels legitimately carry ~10 physical quantities
|
|
2
|
+
# (composition, criticals, temperature, geometry), and grouping unrelated
|
|
3
|
+
# scalars into a struct just to satisfy a count makes them harder to read, not
|
|
4
|
+
# easier. 12 is the line: past that you are passing a record and should say so,
|
|
5
|
+
# which is why pseudopressure.rs has GasState. Clippy's default of 7 is too low
|
|
6
|
+
# for this crate.
|
|
7
|
+
too-many-arguments-threshold = 12
|
|
@@ -46,6 +46,13 @@ mcp = FastMCP(
|
|
|
46
46
|
"recommend_methods(...) if unsure which correlation method suits a "
|
|
47
47
|
"fluid. All inputs default to oilfield units (psia, deg F, ft, mD, "
|
|
48
48
|
"cP); pass metric=True for Eclipse METRIC units where supported. "
|
|
49
|
+
"IMPORTANT: parameter NAMES keep their oilfield spelling under "
|
|
50
|
+
"metric=True. `degf` then takes deg C, `p`/`pr`/`pwf` take barsa, and "
|
|
51
|
+
"lengths take m; the docstring unit notation `(deg F | deg C)` gives "
|
|
52
|
+
"the field value first and the metric value second. Passing a "
|
|
53
|
+
"Fahrenheit value to `degf` with metric=True converts it twice and "
|
|
54
|
+
"returns a wrong answer silently. Standard volumes are always at "
|
|
55
|
+
"60 deg F and 14.696 psia, so metric sm3 is on that basis. "
|
|
49
56
|
"Full module documentation is available as docs:// resources."
|
|
50
57
|
),
|
|
51
58
|
)
|
|
@@ -104,6 +111,11 @@ def describe(name: str) -> str:
|
|
|
104
111
|
|
|
105
112
|
name is 'module.function', e.g. 'gas.gas_z' or 'dca.fit_decline'.
|
|
106
113
|
Also see the docs://<module> resources for worked examples.
|
|
114
|
+
|
|
115
|
+
Units live in the docstring, not the signature, which is why annotations
|
|
116
|
+
are stripped below. A parameter documented `(deg F | deg C)` takes the
|
|
117
|
+
first under the default and the second when metric=True; the parameter
|
|
118
|
+
name itself never changes.
|
|
107
119
|
"""
|
|
108
120
|
_, obj = _resolve(name)
|
|
109
121
|
target = obj.__init__ if inspect.isclass(obj) else obj
|
|
@@ -128,6 +140,10 @@ def call(name: str, arguments: dict = {}) -> dict:
|
|
|
128
140
|
|
|
129
141
|
name is 'module.function', e.g. 'gas.gas_z'. arguments is a JSON object
|
|
130
142
|
of keyword arguments, e.g. {"p": 2000, "sg": 0.75, "degf": 200}.
|
|
143
|
+
That example is oilfield units. With {"metric": true} the same keys take
|
|
144
|
+
metric values, so `degf` would be deg C and `p` barsa; the names do not
|
|
145
|
+
change. Call describe(name) first and read its unit notation: `(deg F |
|
|
146
|
+
deg C)` means field first, metric second.
|
|
131
147
|
Correlation methods are strings (e.g. "zmethod": "DAK"); an invalid
|
|
132
148
|
method string returns an error listing the valid options. Gas and brine
|
|
133
149
|
functions accept lists for pressure-like inputs; oil functions are
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyrestoolbox"
|
|
7
|
-
version = "3.7.
|
|
7
|
+
version = "3.7.7"
|
|
8
8
|
description = "pyResToolbox - A collection of Reservoir Engineering Utilities"
|
|
9
9
|
license = {text = "GPL-3.0-or-later"}
|
|
10
10
|
authors = [{name = "Mark W. Burgoyne", email = "mark.w.burgoyne@gmail.com"}]
|
|
@@ -119,6 +119,13 @@ _SPIVEY_SC_MPA = 0.1013
|
|
|
119
119
|
# Methane specific gravity
|
|
120
120
|
_SG_METHANE = 0.5537
|
|
121
121
|
|
|
122
|
+
# Maximum passes over the K-value/mixing-rule/cubic block in co2BrineSolubility.
|
|
123
|
+
# The CO2-rich root class (gas or liquid CO2) selects the K_CO2 coefficients but
|
|
124
|
+
# is only known after the cubic, so the block repeats until the assumption is
|
|
125
|
+
# self-consistent. Near the root-selection boundary the two classes can
|
|
126
|
+
# alternate, hence the bound.
|
|
127
|
+
_CO2_SAT_MAX_PASSES = 5
|
|
128
|
+
|
|
122
129
|
# Spycher-Pruess K-value coefficients (low-temperature, non-saturated CO2)
|
|
123
130
|
_SP_K_CO2_LT = [1.189, 1.304e-2, -5.446e-5]
|
|
124
131
|
_SP_K_H2O_LT = [-2.209, 3.097e-2, -1.098e-4, 2.048e-7]
|
|
@@ -906,6 +913,51 @@ class CO2_Brine_Mixture():
|
|
|
906
913
|
# DOI 10.1007/s11242-009-9425-y
|
|
907
914
|
#============================================================================
|
|
908
915
|
|
|
916
|
+
def solve_root_class(self):
|
|
917
|
+
#=======================================================================
|
|
918
|
+
# K-values, compositions, mixing rules and cubic under one root class
|
|
919
|
+
#=======================================================================
|
|
920
|
+
"""Solve the CO2-rich phase until its root class is self-consistent.
|
|
921
|
+
|
|
922
|
+
K_CO2 needs to know whether that phase is gas or liquid CO2, but that is
|
|
923
|
+
only settled by the cubic, which raises self.repeat when the root class
|
|
924
|
+
changes. Repeat until the assumption the K-values were evaluated under is
|
|
925
|
+
the one the cubic returns. The flag is reset first: left set by an earlier
|
|
926
|
+
solve on this object (the cw_sat pre-pass half a bar above the target
|
|
927
|
+
pressure) it would otherwise skip the block and leave a stale molar volume
|
|
928
|
+
in the fugacity coefficients.
|
|
929
|
+
"""
|
|
930
|
+
self.repeat = False
|
|
931
|
+
for _pass in range(_CO2_SAT_MAX_PASSES):
|
|
932
|
+
#==k-Parameters (y/x partitioning factors) Equations 5 and 6 =========
|
|
933
|
+
self.K_CO2()
|
|
934
|
+
|
|
935
|
+
#== First estimates of yCO2 and xCO2 =================================
|
|
936
|
+
if not self.low_temp:
|
|
937
|
+
yCO2 = 1 - self.est_yH2O() # Paper suggests using Psat/P, but this empirical fit appears more accurate
|
|
938
|
+
xCO2 = yCO2 / self.K[0]
|
|
939
|
+
else:
|
|
940
|
+
yCO2 = 1.0
|
|
941
|
+
xCO2 = 0.0
|
|
942
|
+
|
|
943
|
+
self.y = np.array([yCO2, 1.0-yCO2])
|
|
944
|
+
self.x = np.array([xCO2, 1.0-xCO2])
|
|
945
|
+
|
|
946
|
+
# Trigger mixing rules
|
|
947
|
+
self.aMix_RK()
|
|
948
|
+
self.bMix_RK()
|
|
949
|
+
|
|
950
|
+
#--Solve the Cubic Equation A-2 -------------------------------------
|
|
951
|
+
self.MolarVolume()
|
|
952
|
+
|
|
953
|
+
if not self.repeat: # Root class matched the assumption - converged
|
|
954
|
+
return
|
|
955
|
+
warnings.warn(
|
|
956
|
+
f"CO2_Brine_Mixture: CO2-rich root class did not settle in "
|
|
957
|
+
f"{_CO2_SAT_MAX_PASSES} passes at {self.pBar:.2f} bar, "
|
|
958
|
+
f"{self.degC:.2f} degC; using the last solution"
|
|
959
|
+
)
|
|
960
|
+
|
|
909
961
|
def co2BrineSolubility(self):
|
|
910
962
|
""" Calculates CO2-brine mutual solubilities at self.pBar, self.degC
|
|
911
963
|
and self.ppm (Spycher & Pruess 2010). Takes no arguments and
|
|
@@ -918,10 +970,11 @@ class CO2_Brine_Mixture():
|
|
|
918
970
|
"""
|
|
919
971
|
if _RUST_AVAILABLE:
|
|
920
972
|
try:
|
|
921
|
-
xco2, yco2, yh2o, rhogas, gasz, conv = _rust.co2_brine_solubility_rust(
|
|
973
|
+
xco2, yco2, yh2o, rhogas, gasz, conv, co2_sat = _rust.co2_brine_solubility_rust(
|
|
922
974
|
self.pBar, self.degC, self.ppm
|
|
923
975
|
)
|
|
924
976
|
self.converged = conv
|
|
977
|
+
self.CO2_sat = co2_sat # Root class the solve settled on
|
|
925
978
|
self.x = np.array([xco2, 1.0 - xco2 - (self.xSalt if self.xSalt else 0.0)])
|
|
926
979
|
self.y = np.array([yco2, yh2o])
|
|
927
980
|
self.rhoGas = rhogas
|
|
@@ -986,33 +1039,9 @@ class CO2_Brine_Mixture():
|
|
|
986
1039
|
|
|
987
1040
|
#==k-Parameters (y/x partitioning factors) Equations 5 and 6 ==========================
|
|
988
1041
|
self.K_H2O()
|
|
989
|
-
i =0
|
|
990
|
-
while not self.repeat:
|
|
991
|
-
i+=1
|
|
992
|
-
if i > 1:
|
|
993
|
-
break
|
|
994
|
-
|
|
995
|
-
#==k-Parameters (y/x partitioning factors) Equations 5 and 6 ==========================
|
|
996
|
-
self.K_CO2()
|
|
997
|
-
|
|
998
|
-
#== First estimates of yCO2 and xCO2 ================================================
|
|
999
|
-
if not self.low_temp:
|
|
1000
|
-
yCO2 = 1 - self.est_yH2O() # Paper suggests using Psat/P, but this empirical fit appears more accurate
|
|
1001
|
-
xCO2 = yCO2 / self.K[0]
|
|
1002
|
-
else:
|
|
1003
|
-
yCO2 = 1.0
|
|
1004
|
-
xCO2 = 0.0
|
|
1005
|
-
|
|
1006
|
-
self.y = np.array([yCO2, 1.0-yCO2])
|
|
1007
|
-
self.x = np.array([xCO2, 1.0-xCO2])
|
|
1008
|
-
|
|
1009
|
-
# Trigger mixing rules
|
|
1010
|
-
self.aMix_RK()
|
|
1011
|
-
self.bMix_RK()
|
|
1012
1042
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1043
|
+
self.solve_root_class()
|
|
1044
|
+
|
|
1016
1045
|
#--Calculate Fugacity Coefficients*Pressure---------------------------------------
|
|
1017
1046
|
self.fugP()
|
|
1018
1047
|
|
|
@@ -107,12 +107,12 @@ _SW_NAME = {'NC4H10': 'nC4H10'}
|
|
|
107
107
|
# measurements only by `fit_pr_vshift.py`. Regenerate with that script if any
|
|
108
108
|
# calibration point changes; the values below are pinned in validation.py.
|
|
109
109
|
VSHIFT = {
|
|
110
|
-
'CH4': -0.
|
|
111
|
-
'CO2': -0.
|
|
112
|
-
'H2S': -0.
|
|
113
|
-
'N2': -0.
|
|
114
|
-
'H2': -0.
|
|
115
|
-
'C2H6': -0.
|
|
110
|
+
'CH4': -0.111430,
|
|
111
|
+
'CO2': -0.038965,
|
|
112
|
+
'H2S': -0.079740,
|
|
113
|
+
'N2': -0.176768,
|
|
114
|
+
'H2': -0.178503,
|
|
115
|
+
'C2H6': -0.073843,
|
|
116
116
|
# C3H8 added 2026-07-25. NOT fitted to a densimetric data set, because none
|
|
117
117
|
# exists for propane in water. Set from the only two direct 298 K
|
|
118
118
|
# determinations available, Moore (1982) 70.7 and Zhou & Battino (2001)
|
|
@@ -120,7 +120,7 @@ VSHIFT = {
|
|
|
120
120
|
# is their mean, giving V_phi = 72.85 at 298 K. Those two disagree by 6.1%,
|
|
121
121
|
# which is the honest uncertainty on this gas and is far wider than for any
|
|
122
122
|
# other. It replaces a fallback that sat BELOW both of them (66.99).
|
|
123
|
-
'C3H8': -0.
|
|
123
|
+
'C3H8': -0.113326,
|
|
124
124
|
# NC4H10 added 2026-07-25 from Moore (1982) Table I, 76.6 +/- 0.1 cm3/mol
|
|
125
125
|
# (2 runs; his stated overall imprecision is +/-1.5). Two oddities that
|
|
126
126
|
# should not be smoothed over: the shift is POSITIVE, alone among the
|
|
@@ -128,7 +128,7 @@ VSHIFT = {
|
|
|
128
128
|
# run +18.4, +17.8, then +5.9). Adopted because it is the only direct
|
|
129
129
|
# measurement of this quantity, but it is a single lab and two runs, and
|
|
130
130
|
# C4 sits outside the five-gas validated scope.
|
|
131
|
-
'NC4H10': +0.
|
|
131
|
+
'NC4H10': +0.110920,
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
# Gases whose shift rests on 298 K data alone, so their temperature behaviour is
|
|
@@ -213,10 +213,12 @@ def V2_inf_raw(gas, T, P, m_nacl=0.0):
|
|
|
213
213
|
# Switched from finite differences 2026-07-31; the difference evaluation
|
|
214
214
|
# agrees to 4e-5 cm3/mol, its truncation error.
|
|
215
215
|
v = v_water_liquid(T, P, m_nacl)
|
|
216
|
-
# Pinned to the '
|
|
217
|
-
#
|
|
218
|
-
#
|
|
219
|
-
kij
|
|
216
|
+
# Pinned to the 'default' kij_AQ set (the published S&W-refresh
|
|
217
|
+
# recommendation), which is what the VSHIFT values below are fitted
|
|
218
|
+
# against since 2026-09-01. The pin stays explicit even while it matches
|
|
219
|
+
# the library default: the shift and the kij are one calibration and must
|
|
220
|
+
# not silently follow a future default move.
|
|
221
|
+
kij = _SW.get_kij_aq(_SW_NAME.get(gas, gas), T, m_nacl, framework='default')
|
|
220
222
|
a1, b1 = _ab('H2O', T, m_nacl)
|
|
221
223
|
a2, b2 = _ab(gas, T)
|
|
222
224
|
a12 = np.sqrt(a1 * a2) * (1.0 - kij)
|
|
@@ -48,7 +48,9 @@ DEFAULT_ROUTE = "auto"
|
|
|
48
48
|
# brine-density data: the seven Tiepel & Gubbins (1972) KCl dilatometry points,
|
|
49
49
|
# fitted as (generated by brine_props/code/relative_salt_shift.py)
|
|
50
50
|
# V_eff = V_phi(T,P) * (1 + g(m)), g(m) = -c*m/(1 + d*m)
|
|
51
|
-
# giving -1.
|
|
51
|
+
# giving -1.52% at 1 molal and -5.27% at 5 molal (Tiepel's molar KCl
|
|
52
|
+
# concentrations converted to molality at 25 degC since 2026-09-05; the
|
|
53
|
+
# molar-as-molal fit gave -1.56% / -5.85%). Enns and O'Sullivan & Smith
|
|
52
54
|
# agree on sign and size; the magnitude is known to about a factor of two
|
|
53
55
|
# (O'Sullivan NaCl shallower, Tiepel KCl at this value).
|
|
54
56
|
#
|
|
@@ -56,7 +58,7 @@ DEFAULT_ROUTE = "auto"
|
|
|
56
58
|
# -0.03 to -0.07 cm3/mol per molal, an order of magnitude too small and linear
|
|
57
59
|
# where the measurements saturate. It is therefore not used, and this term
|
|
58
60
|
# carries the whole effect, so there is no double counting.
|
|
59
|
-
SALT_C, SALT_D = 0.
|
|
61
|
+
SALT_C, SALT_D = 0.017061208, 0.12370899 # 8 sig digits, generated - do not retype
|
|
60
62
|
|
|
61
63
|
|
|
62
64
|
def salt_fraction(m_nacl: float) -> float:
|
|
@@ -180,6 +180,8 @@ pyrestoolbox.brine.CO2_Brine_Mixture
|
|
|
180
180
|
* - CO2 Corrected Brine Viscosity
|
|
181
181
|
- Calabrese et al. (2019) Eq. 25, a temperature-dependent increment fitted to 415 measured brine points. **Changed in 3.7.2**, superseding Islam-Carlson (2012), which carries no temperature dependence and overstates the effect by 3.5x at 366 K and 9.4x at 423 K.
|
|
182
182
|
|
|
183
|
+
**Known limit near the CO2 critical point**: the CO2-rich phase water content (``.y[1]``) and density (``.rhoGas``) are unreliable between about 31 and 38 degC at 73-100 bar, where they can step sharply with pressure. The low-temperature Redlich-Kwong parameterisation has its own pseudo-critical temperature of 311 K, 7 K above CO2's real 304.13 K, so a spurious two-root region survives above the true critical point and the published root-selection criterion steps between branches inside it. The step is intrinsic to using this cubic there (gating the root selection on the real CO2 phase boundary only relocates it), so it is documented rather than patched. Brine density, brine viscosity and CO2 solubility (``.x``, ``.Rs_sat``) are continuous through it and unaffected.
|
|
184
|
+
|
|
183
185
|
.. list-table:: Inputs
|
|
184
186
|
:widths: 10 15 40
|
|
185
187
|
:header-rows: 1
|
|
@@ -707,9 +709,12 @@ exactly. The PR
|
|
|
707
709
|
route needs one fitted number per gas against 35 coefficients per gas for the
|
|
708
710
|
correlation, matches or beats it on five of six gases against the calibration
|
|
709
711
|
densimetry, and reproduces an H2S temperature trend the correlation misses.
|
|
710
|
-
The delivered shifts are CH4 -0.
|
|
711
|
-
N2 -0.
|
|
712
|
-
nC4H10 +0.
|
|
712
|
+
The delivered shifts are CH4 -0.111430, CO2 -0.038965, H2S -0.079740,
|
|
713
|
+
N2 -0.176768, H2 -0.178503, C2H6 -0.073843, C3H8 -0.113326 and
|
|
714
|
+
nC4H10 +0.110920. They are fitted against the ``'default'`` kij_AQ set and the
|
|
715
|
+
route pins that framework internally (refitted in 3.7.7; the values before it
|
|
716
|
+
went with the ``'mc3'`` set the route pinned until then). A shift and the kij
|
|
717
|
+
set it was fitted against are one calibration, so the two are never mixed.
|
|
713
718
|
|
|
714
719
|
Three ceilings should not be conflated: the arithmetic stops at 623.15 K, the
|
|
715
720
|
volume shift stops being fitted at 473.15 K, and **accuracy is claimed only to
|
|
@@ -721,9 +726,11 @@ the equation of state unaided.
|
|
|
721
726
|
**Salinity shift on V_phi** (new in 3.7.3; relative form from 3.7.4). A
|
|
722
727
|
gas-generic dimensionless fraction is applied,
|
|
723
728
|
``V_eff = V_phi(T,P) * (1 + g(m))`` with
|
|
724
|
-
``g(m) = -1.
|
|
725
|
-
-5.
|
|
726
|
-
is fixed entirely from Tiepel and Gubbins (1972) KCl dilatometry
|
|
729
|
+
``g(m) = -1.7061 m/(1 + 0.12371 m)`` percent, giving -1.52% at 1 mol/kg and
|
|
730
|
+
-5.27% at 5 mol/kg (for CO2, -0.5 to -2.4 cm3/mol over the working range). It
|
|
731
|
+
is fixed entirely from Tiepel and Gubbins (1972) KCl dilatometry, their molar
|
|
732
|
+
concentrations converted to molality at 25 degC (from 3.7.7; the 3.7.4 fit
|
|
733
|
+
treated them as molal: -1.56% / -5.85%), with no
|
|
727
734
|
parameter fitted to any brine-density data; the magnitude is known to about a
|
|
728
735
|
factor of two. Freshwater results are unchanged exactly; gas-saturated brine
|
|
729
736
|
densities move by up to a few hundredths of a percent. Pass
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
Changelist in 3.7.7:
|
|
2
|
+
|
|
3
|
+
- **BRINE DENSITY: the dissolved-gas volume shifts refitted under the published ``'default'`` framework** (``brine.pr_vphi.VSHIFT``). A volume shift and the S&W kij_AQ set it was fitted against are one calibration and cannot be mixed, so the module pinned ``framework='mc3'``. The reference calibration has moved to the published recommendation, the ``'default'`` embedded delta-kij framework, and the shifts move with it: CH4 -0.111430 (was -0.109632), CO2 -0.038965 (-0.037913), H2S -0.079740 (-0.078975), N2 -0.176768 (-0.176510), H2 -0.178503 (-0.177625) and C2H6 -0.073843 (-0.073142), with the two literature-anchored gases re-derived on the same basis at C3H8 -0.113326 and nC4H10 +0.110920. The pin stays explicit at ``'default'`` so that a later move of the library default cannot silently cross the shifts with a kij set they were not fitted against. V_phi moves by up to 0.14% (CH4 at 350 K and 20 MPa, 39.4072 to 39.4624 cm3/mol), gas-saturated brine density by under 0.002%, and Cf_sat, being a difference of two densities, by 0.025%.
|
|
4
|
+
|
|
5
|
+
- **BRINE DENSITY: the V_phi salinity fraction refitted on molality** (``brine.vphi_route.salt_fraction``). Tiepel and Gubbins' KCl concentrations are molar; the 3.7.4 fit treated them as molal. Converted with the KCl solution density at 25 degC (1, 2, 4 mol/L = 1.033, 2.133, 4.587 mol/kg) and refitted, ``g(m) = -1.7061 m/(1 + 0.12371 m)`` percent replaces ``-1.7009 m/(1 + 0.090684 m)``: -1.52% at 1 mol/kg (was -1.56%) and -5.27% at 5 (was -5.85%). Gas-saturated brine density moves by under 0.03% at 5 mol/kg; freshwater results are unchanged exactly.
|
|
6
|
+
|
|
7
|
+
- **GAS docstrings carry both unit systems, and the MCP server states the convention.** Every ``gas`` parameter that the code converts under ``metric=True`` now shows both halves on its own parameter line in the ``(field | metric)`` form, 31 lines across nine functions. Parameter *names* never change, so ``degf`` takes deg C under ``metric=True`` and a reader who stops at the name converts twice and is silently wrong. ``pyrestoolbox-mcp``'s instructions, ``call`` and ``describe`` now say so, and also that standard volumes stay on the 60 degF, 14.696 psia basis in both unit systems. Documentation only, no numerical change.
|
|
8
|
+
|
|
9
|
+
Changelist in 3.7.6:
|
|
10
|
+
|
|
11
|
+
- **OIL Rs: the Velarde, Blasingame & McCain correlation now refuses fluids it cannot represent, instead of returning negative Rs.** For rich separator gas with a light oil at a high bubble point, the correlation's leading coefficient a0 exceeds 1 and the published form ``rsr = a0*pr^a1 + (1-a0)*pr^a2`` turns negative over most of the pressure range: at 45 API, 200 degF, sg_sp 1.0 and pb 6000 psia it returned -421 scf/stb at 1000 psia, recovering only near Pb where the form is pinned to rsb by construction, and downstream ``oil_viso`` failed on a complex intermediate. The coefficients match the 1997 paper, so this is a range limit rather than a transcription error; a 72,900-case sweep found ``a0 > 1`` an exact test, with every such case non-physical and no ``a0 <= 1`` case affected. The boundary at 200 degF sits near Pb 5000 psia at sg_sp 1.0 with 45 API, or 8100 psia at sg_sp 0.85 with 35 API, falling roughly 10% per 100 degF of heating. ``oil_rs(rsmethod='VELAR')`` now raises ``ValueError`` naming the inputs, the offending coefficient and ``rsmethod='STAN'`` as the working alternative (Valko-McCain is not offered because its own Pb ceiling refuses most of the same corner). The same fluids are refused once at the nodal VLP entry points, where the segment march carries its own copy of the formula and the Rust path would otherwise return NaN silently.
|
|
12
|
+
|
|
13
|
+
- **OIL DENSITY, Bo AND Co at low GOR: the McCain cofb polynomial is floored at Rsb = 1 scf/stb** (``_COFB_RSB_MIN``, Python and Rust both). cofb takes ln(Rsb) and is unbounded as Rsb falls: 2.6e-05 1/psi at 500 scf/stb, 3.3e-05 at 0.1, 8.9e-04 at 0.01, infinite at zero, and zero GOR is an ordinary state for a dead oil. Before the fix a 35 API dead-oil case returned ``oil_deno`` of infinity at rs = 1e-4 and 2985 kg/m3 (denser than water) at 1e-3, ``oil_bo`` of 0.0 and 0.285, and black-oil tables carried Co = inf in every row on the Rust path; the two paths also disagreed, Python raising ``ZeroDivisionError`` where Rust substituted ln(rsb) = 0 at exactly zero and still diverged either side of it. The polynomial is well behaved from 1 scf/stb upward, so nothing at or above the floor moves; the dead-oil case now returns 803.24 kg/m3 flat from rs = 0 to 0.1, identical on both paths. The correlation's validity floor of 14.696 psia is now stated in the ``oil_deno`` docs; at that floor the SWMH temperature term anchors a dead oil to within 0.6% of the thermally expanded stock-tank density on its own, so no atmospheric pin is applied.
|
|
14
|
+
|
|
15
|
+
- **OIL Co at p == pb == 14.696 psia returns the undersaturated compressibility instead of zero.** The explicit method clamps its difference stencil to standard pressure below and Pb above; with all three equal the stencil collapsed to a point and the fallback divided a zero numerator, returning -0.0 in every dead-oil table row that reached standard pressure. The stencil now steps forward into the undersaturated branch, where Bo is continuous, giving 2.512e-05 1/psi at 35 API and 200 degF.
|
|
16
|
+
|
|
17
|
+
- **CO2-BRINE: the liquid-CO2 K-value set is now reachable, and a ``cw_sat=True`` state leak is fixed.** Spycher & Pruess (2010) publish a second log(K0) coefficient set for liquid CO2, selected by the root class of a cubic that only runs after the K-values are used; the loop meant to repeat the block on a root-class change could never take a second pass, so the liquid set was dead code and below 31 degC the K-values came from the gas coefficients while the molar volume came from the liquid root. The block now repeats until the root class is self-consistent (bounded at 5 passes), in Python and Rust both. Effects confine to the liquid-CO2 region: 50 bar at 12 degC now settles on the liquid root, and at 72.2 bar and 32.35 degC the gas water content moves +0.007%, the two coefficient sets being close. Separately, the ``cw_sat=True`` pre-pass could leave the repeat flag set, making the main solve skip its cubic and reuse a stale molar volume (74.65 instead of 167.73 cm3/gmol at 72 bar and 32.35 degC, an 88% error in the CO2-rich phase water content, Python path only), and the Rust path never returned the root class at all, so ``CO2_sat`` read False whenever the extension was loaded. Both fixed; results at the frozen 200 bar / 80 degC and 5000 psia / 275 degF baselines are untouched.
|
|
18
|
+
|
|
19
|
+
- **CO2-BRINE known limit documented**: the CO2-rich phase water content and density are unreliable between about 31 and 38 degC at 73-100 bar, where they can step sharply with pressure (+85% in one 0.2 bar step at 32.35 degC). The low-temperature RK parameterisation has its own pseudo-critical temperature of 311 K, 7 K above CO2's real 304.13 K, so a spurious two-root region survives above the true critical point. A root-selection gate on the real CO2 phase boundary was implemented, measured and reverted, because it only relocates the step: the model is 42-112% dense against Span-Wagner through that wedge whichever root is taken. Brine density, brine viscosity and CO2 solubility are continuous through it and unaffected, which is why the limit went unnoticed. Stated in ``brine.rst``.
|
|
20
|
+
|
|
21
|
+
- **Internal: maintainability pass.** Duplicated flow-input handling in nodal and a doubly-written Havlena-Odeh denominator in matbal collapsed into single copies; the three most complex simtools functions decomposed, with nine new deck round-trip tests; per-function cyclomatic-complexity ratchets added to CI for Python (McCabe over 15 fails unless baselined) and Rust (clippy -D warnings, threshold 12); the crate brought clippy-clean. No numerical change.
|
|
22
|
+
|
|
1
23
|
Changelist in 3.7.5:
|
|
2
24
|
|
|
3
25
|
- **NEW: gas caloric properties.** ``gas.gas_thermal(p, sg, degf, co2, h2s, n2, h2, tc, pc, metric)`` returns enthalpy, Cp, Cv and the Joule-Thomson coefficient from the BNS 5-component Peng Robinson model (Burgoyne, Nielsen & Stanko 2025, SPE-229932-MS), vectorised over pressure, field or metric units. The library previously had no enthalpy or heat-capacity code at all. BNS only: DAK and HY are Z-factor correlations and carry no caloric content. Two things differ from the reference implementation at github.com/mwburgoyne/5_Component_PengRobinson_Z-Factor: the Peneloux volume shift is carried into the caloric path (leaving Cp and Cv invariant but shifting H by ``-c_mix*p`` and JT by ``+c_mix/Cp``, which takes the JT bias against a reference EOS from +4.2% to +0.7% for pure CO2 and from +12.0% to +5.9% for pure methane), and Gibbs-criterion root selection is shared with ``gas_z`` so Z and the caloric properties can never resolve to different roots. Known limits, stated in the docs: Cp errs by about -24% close to the CO2 critical point, where a cubic cannot represent the heat-capacity ridge, and departure enthalpy for lean hydrocarbon gas carries about a -11% systematic bias. Use a reference EOS for dense-phase CO2.
|
|
@@ -44,7 +44,7 @@ pyResToolBox uses class objects to track calculation options through the functio
|
|
|
44
44
|
- Method for calculating solution gas-oil ratio. Defaults to 'VELAR'
|
|
45
45
|
Options are:
|
|
46
46
|
|
|
47
|
-
+ 'VELAR': Velarde, Blasingame & McCain (1999)
|
|
47
|
+
+ 'VELAR': Velarde, Blasingame & McCain (1999). Raises ValueError for high-Pb rich-separator-gas fluids (see ``oil_rs``)
|
|
48
48
|
+ 'STAN': Standing Correlation (1947)
|
|
49
49
|
+ 'VALMC': Valko-McCain Correlation (2003) - Only for oil_rs_bub (Rs at Pb)
|
|
50
50
|
* - comethod
|
|
@@ -484,6 +484,8 @@ pyrestoolbox.oil.oil_rs
|
|
|
484
484
|
|
|
485
485
|
Returns solution gas oil ratio (scf/stb, or sm3/sm3 if metric=True) calculated with different correlations. Either pb, rsb or both need to be specified. If one is missing, the other will be calculated from correlation
|
|
486
486
|
|
|
487
|
+
With ``rsmethod='VELAR'`` this raises ``ValueError`` when the fluid falls outside the Velarde correlation's range. The correlation's leading coefficient grows with Pb, separator gas gravity and API, and once it passes 1 the correlation returns negative Rs over the lower part of the pressure range. The affected corner is rich separator gas with a light oil at a high bubble point, and the boundary falls as temperature rises: Pb above about 5900 psia at 100 degF or 4600 psia at 300 degF for sg_sp 1.0 with 45 API, and above about 9500 psia at 100 degF or 7300 psia at 300 degF for sg_sp 0.85 with 35 API. Use ``rsmethod='STAN'`` for those fluids - ``'VALMC'`` usually refuses the same corner, because its bubble point inversion has a ceiling that overlaps it.
|
|
488
|
+
|
|
487
489
|
.. list-table:: Inputs
|
|
488
490
|
:widths: 10 15 40
|
|
489
491
|
:header-rows: 1
|