diff --git a/README.md b/README.md index de03be6..81dc9ca 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ name, signature, and — bit for bit — its published numeric output. - **Docs:** [docs/petrolib.md](docs/petrolib.md) (guide), [docs/petrolib-api.md](docs/petrolib-api.md) (generated API reference), and the - [Petrophysics Handbook](docs/Petrophysics_Handbook.pdf) (PDF user manual — every function + [Petrophysics Handbook](doc/Petrophysics_Handbook.pdf) (PDF user manual — every function with purpose, parameters, and full journal sources); [CONVENTIONS.md](CONVENTIONS.md) has the API rules. - Running article scripts needs **no install step** — every directory keeps working from diff --git a/docs/petrolib.md b/docs/petrolib.md index 06686a2..5052f93 100644 --- a/docs/petrolib.md +++ b/docs/petrolib.md @@ -97,7 +97,7 @@ ends with a *References* section resolving those tags to complete SPWLA `help(petrolib.)` therefore shows the full bibliography of a domain. For a print-ready reference, the **Petrophysics Handbook** -([Petrophysics_Handbook.pdf](Petrophysics_Handbook.pdf)) documents every +([Petrophysics_Handbook.pdf](../doc/Petrophysics_Handbook.pdf)) documents every public function alphabetically — purpose, input parameters, output, and full journal sources — and is regenerated with `python tools/gen_petrolib_handbook.py` (needs `pip install reportlab`, or diff --git a/tools/gen_petrolib_handbook.py b/tools/gen_petrolib_handbook.py index 18ada38..44dae79 100644 --- a/tools/gen_petrolib_handbook.py +++ b/tools/gen_petrolib_handbook.py @@ -1,10 +1,22 @@ #!/usr/bin/env python3 -"""Generate docs/Petrophysics_Handbook.pdf — the petrolib user manual. +"""Generate doc/Petrophysics_Handbook.pdf — the petrolib user manual. -Title page, an alphabetical table of contents of every public petrolib -function, and one section per function giving its purpose, input parameters, -output, and the full SPWLA *Petrophysics* journal citations resolved from the -module References sections. Requires reportlab: +Layout (fixed by request): + +* title page carrying exactly three lines — "Petrophysics Handbook", + "Code for Petrophysics Jan/Feb 2014 - Jun/Jul 2026", and + "https://github.com/ingehap/Petrophysics_Code"; +* one blank page; +* an alphabetical table of contents of every public petrolib function; +* one page per function: Python path, purpose, input parameters (each with + its meaning), output parameters, and the sources as full SPWLA + *Petrophysics* journal citations resolved from the module References + sections. + +Parameter meanings and output descriptions live in +tools/handbook_descriptions.json (keyed module -> function -> params / +returns); keep it in sync when the petrolib API changes — missing entries +are reported on stderr. Requires reportlab: pip install reportlab python tools/gen_petrolib_handbook.py @@ -13,12 +25,15 @@ from __future__ import annotations import ast +import json import pathlib import re +import sys from dataclasses import dataclass, field ROOT = pathlib.Path(__file__).resolve().parents[1] -OUT = ROOT / "docs" / "Petrophysics_Handbook.pdf" +OUT = ROOT / "doc" / "Petrophysics_Handbook.pdf" +DESCRIPTIONS = pathlib.Path(__file__).with_name("handbook_descriptions.json") RETURN_ALIASES = { "_Float": "numpy.ndarray of float64", @@ -51,19 +66,18 @@ class Param: annotation: str default: str | None kw_only: bool + meaning: str = "" @dataclass class Entry: name: str module: str - kind: str # "function" | "class" - signature: str purpose: str params: list[Param] = field(default_factory=list) returns: str = "" + returns_meaning: str = "" sources: list[str] = field(default_factory=list) - methods: list[tuple[str, str]] = field(default_factory=list) def parse_references(module_doc: str) -> dict[str, str]: @@ -88,18 +102,34 @@ def parse_references(module_doc: str) -> dict[str, str]: def split_purpose_sources(doc: str) -> tuple[str, list[str]]: - """Return (purpose prose, src tags) from a function docstring.""" + """Return (purpose prose, src tags) from a function docstring. + + Tags are taken from the ``Sources:`` clause only, so incidental + src-tag mentions in the prose (e.g. "the src2018_10/src2018_02 + convention") do not leak into the citation list. + """ # rejoin tags wrapped across lines ("src2024_10/\n ml_permeability") joined = re.sub(r"(src20\d\d_\d\d/)\s*\n\s*", r"\1", doc) tags: list[str] = [] - for tok in re.findall(r"src20\d\d_\d\d(?:/[A-Za-z0-9_]+)?", joined): - if tok not in tags: - tags.append(tok) + for m in re.finditer(r"Sources?:(.*?)(?=\n\n|\Z)", joined, flags=re.S): + for tok in re.findall(r"src20\d\d_\d\d(?:/[A-Za-z0-9_]+)?", m.group(1)): + if tok not in tags: + tags.append(tok) purpose = re.sub(r"Sources?:.*?(?=\n\n|\Z)", "", joined, flags=re.S) paragraphs = [" ".join(seg.split()) for seg in purpose.split("\n\n") if seg.strip()] return "\n\n".join(paragraphs), tags +def resolve_source(tag: str, refs: dict[str, str]) -> str | None: + """Full citation for a src tag; tolerate a missing /article suffix.""" + if tag in refs: + return refs[tag] + hits = [k for k in refs if k.startswith(tag + "/")] + if len(hits) == 1: + return refs[hits[0]] + return None + + def params_of(node: ast.FunctionDef | ast.AsyncFunctionDef) -> list[Param]: out: list[Param] = [] a = node.args @@ -130,22 +160,10 @@ def params_of(node: ast.FunctionDef | ast.AsyncFunctionDef) -> list[Param]: return out -def signature_of(node: ast.FunctionDef | ast.AsyncFunctionDef, params: list[Param]) -> str: - bits: list[str] = [] - star_done = False - for p in params: - if p.kw_only and not star_done and not p.name.startswith("*"): - bits.append("*") - star_done = True - if p.name.startswith("*"): - star_done = True - bits.append(p.name + (f"={p.default}" if p.default is not None else "")) - return f"{node.name}({', '.join(bits)})" - - -def collect_entries() -> tuple[list[Entry], dict[str, dict[str, str]]]: +def collect_entries() -> list[Entry]: + descriptions = json.loads(DESCRIPTIONS.read_text()) if DESCRIPTIONS.exists() else {} + missing: list[str] = [] entries: list[Entry] = [] - all_refs: dict[str, dict[str, str]] = {} files = sorted( p for p in ROOT.glob("petrolib/**/*.py") @@ -156,62 +174,45 @@ def collect_entries() -> tuple[list[Entry], dict[str, dict[str, str]]]: modname = "petrolib." + str(rel.with_suffix("")).removeprefix("petrolib/").replace("/", ".") tree = ast.parse(pfile.read_text()) refs = parse_references(ast.get_docstring(tree) or "") - all_refs[modname] = refs + mod_desc = descriptions.get(modname, {}) for node in tree.body: - if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): - if node.name.startswith("_"): - continue - doc = ast.get_docstring(node) or "" - purpose, tags = split_purpose_sources(doc) - params = params_of(node) - ret = ast.unparse(node.returns) if node.returns else "" - entries.append( - Entry( - name=node.name, - module=modname, - kind="function", - signature=signature_of(node, params), - purpose=purpose, - params=params, - returns=pretty_annotation(ret), - sources=[refs.get(t, t) for t in tags], - ) - ) - elif isinstance(node, ast.ClassDef) and not node.name.startswith("_"): - doc = ast.get_docstring(node) or "" - purpose, tags = split_purpose_sources(doc) - init = next( - ( - n - for n in node.body - if isinstance(n, ast.FunctionDef) and n.name == "__init__" - ), - None, - ) - params = params_of(init) if init else [] - methods = [] - for m in node.body: - if isinstance(m, (ast.FunctionDef, ast.AsyncFunctionDef)): - if m.name.startswith("_"): - continue - mdoc = (ast.get_docstring(m) or "").split("\n\n")[0] - methods.append((signature_of(m, params_of(m)), " ".join(mdoc.split()))) - sig = f"{node.name}({', '.join(p.name for p in params)})" - entries.append( - Entry( - name=node.name, - module=modname, - kind="class", - signature=sig, - purpose=purpose, - params=params, - returns=f"{node.name} instance", - sources=[refs.get(t, t) for t in tags], - methods=methods, - ) + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + if node.name.startswith("_"): + continue + doc = ast.get_docstring(node) or "" + purpose, tags = split_purpose_sources(doc) + params = params_of(node) + fdesc = mod_desc.get(node.name, {}) + for p in params: + p.meaning = fdesc.get("params", {}).get(p.name, "") + if not p.meaning: + missing.append(f"{modname}.{node.name}({p.name})") + ret = ast.unparse(node.returns) if node.returns else "" + sources = [] + for t in tags: + cite = resolve_source(t, refs) + if cite is None: + missing.append(f"{modname}.{node.name} citation {t}") + cite = t + sources.append(cite) + entries.append( + Entry( + name=node.name, + module=modname, + purpose=purpose, + params=params, + returns=pretty_annotation(ret), + returns_meaning=fdesc.get("returns", ""), + sources=sources, ) - entries.sort(key=lambda e: (e.name.lower(), e.module)) - return entries, all_refs + ) + if missing: + print(f"WARNING: {len(missing)} unresolved descriptions/citations:", file=sys.stderr) + for m in missing: + print(" " + m, file=sys.stderr) + entries.sort(key=lambda e: (e.name.lower(), e.name, e.module)) + return entries # ------------------------------------------------------------- rendering @@ -239,6 +240,7 @@ def build_pdf(entries: list[Entry]) -> None: from reportlab.platypus import ( BaseDocTemplate, Frame, + KeepInFrame, NextPageTemplate, PageBreak, PageTemplate, @@ -261,44 +263,28 @@ def build_pdf(entries: list[Entry]) -> None: ) ) + navy = colors.Color(0.10, 0.20, 0.36) + grey = colors.Color(0.35, 0.35, 0.40) body = ParagraphStyle("Body", fontName="DejaVuSans", fontSize=9.2, leading=12.4, spaceAfter=4) label = ParagraphStyle( "Label", parent=body, fontName="DejaVuSans-Bold", - fontSize=9.2, - spaceBefore=5, - spaceAfter=2, - ) - mono = ParagraphStyle( - "Mono", - parent=body, - fontName="DejaVuSansMono", - fontSize=8.4, - leading=10.8, - backColor=colors.Color(0.955, 0.955, 0.965), - borderPadding=3, - spaceAfter=5, + fontSize=9.6, + textColor=navy, + spaceBefore=0, + spaceAfter=3, ) func_heading = ParagraphStyle( "FuncHeading", - fontName="DejaVuSans-Bold", - fontSize=12.5, - leading=15, - spaceBefore=14, - spaceAfter=1, - textColor=colors.Color(0.10, 0.20, 0.36), - ) - mod_line = ParagraphStyle( - "ModLine", - parent=body, - fontName="DejaVuSans-Oblique", - fontSize=8.4, - textColor=colors.Color(0.35, 0.35, 0.40), - spaceAfter=4, + fontName="DejaVuSansMono", + fontSize=13, + leading=16, + spaceAfter=0, + textColor=navy, ) src_style = ParagraphStyle( - "Src", parent=body, fontSize=8.6, leading=11.4, leftIndent=10, spaceAfter=2 + "Src", parent=body, fontSize=8.6, leading=11.4, leftIndent=10, spaceAfter=3 ) class Handbook(BaseDocTemplate): @@ -315,12 +301,14 @@ def afterFlowable(self, flowable: object) -> None: author="Petrophysics_Code / petrolib", subject="User manual for the petrolib common petrophysics library", ) - frame = Frame(22 * mm, 20 * mm, A4[0] - 44 * mm, A4[1] - 40 * mm, id="main") + frame_h = A4[1] - 40 * mm + frame_w = A4[0] - 44 * mm + frame = Frame(22 * mm, 20 * mm, frame_w, frame_h, id="main") def on_page(canvas: object, _doc: object) -> None: canvas.saveState() canvas.setFont("DejaVuSans", 8) - canvas.setFillColor(colors.Color(0.35, 0.35, 0.40)) + canvas.setFillColor(grey) canvas.drawString(22 * mm, A4[1] - 13 * mm, "Petrophysics Handbook") canvas.drawRightString(A4[0] - 22 * mm, A4[1] - 13 * mm, "petrolib reference") canvas.drawCentredString(A4[0] / 2, 11 * mm, str(canvas.getPageNumber())) @@ -328,15 +316,16 @@ def on_page(canvas: object, _doc: object) -> None: doc.addPageTemplates( [ - PageTemplate(id="title", frames=[frame]), + # 'front' pages (title, blank) carry no header/footer text at all + PageTemplate(id="front", frames=[frame]), PageTemplate(id="normal", frames=[frame], onPage=on_page), ] ) story: list[object] = [] - # ---- title page - story.append(Spacer(1, 70 * mm)) + # ---- title page: exactly three lines of text, nothing else + story.append(Spacer(1, 80 * mm)) story.append( Paragraph( "Petrophysics Handbook", @@ -349,35 +338,18 @@ def on_page(canvas: object, _doc: object) -> None: ), ) ) - story.append(Spacer(1, 10 * mm)) - story.append( - Paragraph( - "User manual for petrolib — the common petrophysics library of the " - "Petrophysics_Code repository", - ParagraphStyle("Sub", fontName="DejaVuSans", fontSize=13, leading=18, alignment=1), - ) - ) - story.append(Spacer(1, 55 * mm)) - n_funcs = sum(1 for e in entries if e.kind == "function") - n_classes = len(entries) - n_funcs - story.append( - Paragraph( - f"{n_funcs} functions and {n_classes} classes across " - f"{len({e.module for e in entries})} modules.
" - "Generated from the petrolib docstrings by " - "tools/gen_petrolib_handbook.py.
" - "Source articles are published in Petrophysics, the journal of the " - "Society of Petrophysicists and Well Log Analysts (SPWLA).", - ParagraphStyle( - "Col", - fontName="DejaVuSans", - fontSize=9, - leading=13, - alignment=1, - textColor=colors.Color(0.35, 0.35, 0.40), - ), - ) + story.append(Spacer(1, 14 * mm)) + subtitle = ParagraphStyle( + "Sub", fontName="DejaVuSans", fontSize=14, leading=19, alignment=1, textColor=grey ) + story.append(Paragraph("Code for Petrophysics Jan/Feb 2014 - Jun/Jul 2026", subtitle)) + story.append(Spacer(1, 6 * mm)) + story.append(Paragraph("https://github.com/ingehap/Petrophysics_Code", subtitle)) + + # ---- blank page + story.append(NextPageTemplate("front")) + story.append(PageBreak()) + story.append(Spacer(1, 1)) story.append(NextPageTemplate("normal")) story.append(PageBreak()) @@ -389,6 +361,7 @@ def on_page(canvas: object, _doc: object) -> None: "TocTitle", fontName="DejaVuSans-Bold", fontSize=18, + textColor=navy, spaceAfter=10, ), ) @@ -407,46 +380,53 @@ def on_page(canvas: object, _doc: object) -> None: story.append(toc) story.append(PageBreak()) - # ---- one section per entry + # ---- one page per function + blank_line = Spacer(1, 12.4) # one body-height line with no text for i, e in enumerate(entries): key = f"e{i}" - suffix = " (class)" if e.kind == "class" else "" - head = Paragraph(f'{esc(e.name)}{suffix}', func_heading) + path = f"{e.module}.{e.name}" + head = Paragraph(f'{esc(path)}', func_heading) head._toc_key = key - head._toc_text = f"{e.name}{suffix} ({e.module})" - block: list[object] = [head, Paragraph(esc(e.module), mod_line)] - block.append(Paragraph(esc(e.signature), mono)) + head._toc_text = f"{e.name} — {e.module}" + story.append(head) + + block: list[object] = [blank_line] + block.append(Paragraph("Purpose", label)) if e.purpose: - block.append(Paragraph("Purpose", label)) for para in e.purpose.split("\n\n"): block.append(Paragraph(rich(para), body)) + else: + block.append(Paragraph("(No docstring.)", body)) + + block.append(blank_line) + block.append(Paragraph("Input parameters", label)) if e.params: - block.append(Paragraph("Input parameters", label)) - rows = [["Parameter", "Type", "Default"]] + rows = [["Parameter", "Type", "Default", "Meaning"]] for p in e.params: rows.append( [ Paragraph( - f'{esc(p.name)}' - + (" (kw-only)" if p.kw_only else ""), + f'{esc(p.name)}' + + (" (kw)" if p.kw_only else ""), body, ), - Paragraph(esc(p.annotation) or "—", body), + Paragraph(esc(pretty_annotation(p.annotation)) or "—", body), Paragraph( esc(p.default) if p.default is not None else "required", body, ), + Paragraph(rich(p.meaning) or "—", body), ] ) - t = Table(rows, colWidths=[52 * mm, 72 * mm, 42 * mm], repeatRows=1) + t = Table(rows, colWidths=[30 * mm, 32 * mm, 22 * mm, 82 * mm], repeatRows=1) t.setStyle( TableStyle( [ ("FONTNAME", (0, 0), (-1, 0), "DejaVuSans-Bold"), - ("FONTSIZE", (0, 0), (-1, 0), 8.6), - ("FONTSIZE", (0, 1), (-1, -1), 8.6), - ("TEXTCOLOR", (0, 0), (-1, 0), colors.Color(0.10, 0.20, 0.36)), - ("LINEBELOW", (0, 0), (-1, 0), 0.6, colors.Color(0.10, 0.20, 0.36)), + ("FONTSIZE", (0, 0), (-1, 0), 8.4), + ("FONTSIZE", (0, 1), (-1, -1), 8.4), + ("TEXTCOLOR", (0, 0), (-1, 0), navy), + ("LINEBELOW", (0, 0), (-1, 0), 0.6, navy), ("LINEBELOW", (0, 1), (-1, -2), 0.25, colors.Color(0.85, 0.85, 0.88)), ("VALIGN", (0, 0), (-1, -1), "TOP"), ("TOPPADDING", (0, 0), (-1, -1), 1.5), @@ -457,33 +437,34 @@ def on_page(canvas: object, _doc: object) -> None: ) block.append(t) else: - block.append(Paragraph("Input parameters", label)) block.append(Paragraph("None.", body)) - block.append(Paragraph("Output", label)) - block.append(Paragraph(esc(e.returns) or "None (procedure).", body)) - if e.methods: - block.append(Paragraph("Methods", label)) - for msig, msum in e.methods: - block.append( - Paragraph( - f'{esc(msig)}' - + (f" — {rich(msum)}" if msum else ""), - body, - ) - ) + + block.append(blank_line) + block.append(Paragraph("Output parameters", label)) + out_bits = [] + if e.returns: + out_bits.append(f'{esc(e.returns)}') + if e.returns_meaning: + out_bits.append(rich(e.returns_meaning)) + block.append(Paragraph(" — ".join(out_bits) if out_bits else "None.", body)) + if e.sources: + block.append(blank_line) block.append(Paragraph("Sources", label)) for s in e.sources: block.append(Paragraph("• " + rich(s), src_style)) - story.extend(block) + + # shrink-to-fit so every function occupies exactly one page + story.append(KeepInFrame(frame_w, frame_h - 14 * mm, block, mode="shrink", hAlign="LEFT")) + story.append(PageBreak()) doc.multiBuild(story) - print(f"wrote {OUT.relative_to(ROOT)}: {len(entries)} entries") + print(f"wrote {OUT.relative_to(ROOT)}: {len(entries)} functions") def main() -> None: - entries, _ = collect_entries() - build_pdf(entries) + OUT.parent.mkdir(exist_ok=True) + build_pdf(collect_entries()) if __name__ == "__main__": diff --git a/tools/handbook_descriptions.json b/tools/handbook_descriptions.json new file mode 100644 index 0000000..fac2aff --- /dev/null +++ b/tools/handbook_descriptions.json @@ -0,0 +1,4428 @@ +{ + "petrolib.acoustic_geomech": { + "acoustic_impedance": { + "params": { + "out": "Output unit selector: rayl (default) returns SI Rayl (kg/m2/s), mrayl returns MRayl (Z/1e6); other values raise ValueError.", + "rho": "Density in kg/m3.", + "v": "Wave velocity in m/s." + }, + "returns": "Acoustic impedance Z = rho*v as a numpy float64 array, in Rayl by default or MRayl when out is mrayl." + }, + "annie_c11": { + "params": { + "c33": "VTI stiffness constant C33, Pa under the module convention.", + "c44": "VTI stiffness constant C44, same units as c33.", + "c66": "VTI stiffness constant C66, same units as c33." + }, + "returns": "ANNIE estimate of the stiffness constant C11 = 2*(C66 - C44) + C33 as a numpy float64 array, in the units of the inputs." + }, + "annie_c13": { + "params": { + "c11": "VTI stiffness constant C11, Pa under the module convention.", + "c66": "VTI stiffness constant C66, same units as c11." + }, + "returns": "ANNIE estimate of the stiffness constant C13 = C11 - 2*C66 (assuming C13 = C12) as a numpy float64 array, in the units of the inputs." + }, + "attenuation_coefficient": { + "params": { + "a0": "Initial signal amplitude at zero propagation distance.", + "ax": "Signal amplitude after propagating distance x, same units as a0.", + "x": "Propagation distance between the two amplitude measurements, in any length unit." + }, + "returns": "Exponential attenuation coefficient alpha = ln(A0/Ax)/x in the decay law A = A0*exp(-alpha*x), as a numpy float64 array in reciprocal units of x (1/length)." + }, + "attenuation_db": { + "params": { + "a1": "First (reference or input) signal amplitude, any consistent amplitude unit.", + "a2": "Second (attenuated or output) signal amplitude, same units as a1." + }, + "returns": "Attenuation 20*log10(A1/A2) in decibels as a numpy float64 array; positive when A2 is smaller than A1." + }, + "biot_coefficient": { + "params": { + "k_dry": "Dry-frame bulk modulus in Pa.", + "k_mineral": "Mineral (solid grain) bulk modulus, same units as k_dry." + }, + "returns": "Dimensionless Biot-Willis effective-stress coefficient alpha = 1 - K_dry/K_mineral as a numpy float64 array." + }, + "bowers_pore_pressure": { + "params": { + "a": "Bowers loading-curve coefficient A in sigma = ((V - V0)/A)**(1/B); default 10.0.", + "b": "Bowers loading-curve exponent B; default 0.7.", + "overburden": "Overburden (vertical) stress in psi.", + "sigma_max": "Maximum past effective stress in psi for the unloading branch; default 5000.0, used only when unloading=True.", + "u": "Bowers unloading exponent U; default 3.0, used only when unloading=True.", + "unloading": "If True, apply the Bowers unloading (hysteresis) branch to the effective stress; default False uses the loading curve only.", + "v0": "Mudline (unconsolidated-sediment) velocity in ft/s; default 5000.0.", + "velocity": "Sonic (compressional) velocity in ft/s, per the field-unit corpus convention." + }, + "returns": "Bowers (1995) pore pressure Pp = overburden - effective stress as a numpy float64 array, in psi (field units)." + }, + "breakdown_pressure": { + "params": { + "pp": "Pore pressure, same pressure unit as sh.", + "sH": "Maximum horizontal stress, same pressure unit as sh.", + "sh": "Minimum horizontal stress in the working pressure unit.", + "tensile_strength": "Rock tensile strength T0, same pressure unit as the stresses; default 0.0." + }, + "returns": "Hubbert-Willis fracture breakdown pressure 3*sh - sH - Pp + T0 as a numpy float64 array, in the input pressure unit." + }, + "brie_fluid_modulus": { + "params": { + "e": "Brie mixing exponent, a dimensionless float defaulting to 3.0.", + "k_gas": "Bulk modulus of the gas phase, same units as k_liquid.", + "k_liquid": "Bulk modulus of the liquid phase, in Pa with SI inputs.", + "sw": "Liquid (water/brine) saturation as a fraction between 0 and 1." + }, + "returns": "Brie et al. (1995) mixed-fluid bulk modulus (K_liq - K_gas)*Sw**e + K_gas as a numpy float64 array, in the units of the input moduli." + }, + "brittleness_rickman": { + "params": { + "e": "Young's modulus in the same units as e_range (GPa with the default range).", + "e_range": "Tuple (Emin, Emax) of min-max normalisation endpoints for Young's modulus; default (10.0, 80.0) GPa (Rickman's brackets).", + "nu": "Dimensionless Poisson's ratio.", + "nu_range": "Tuple (nu_min, nu_max) of normalisation endpoints for Poisson's ratio; default (0.15, 0.40), dimensionless.", + "percent": "If True, scale the index to 0-100 percent; default False returns the 0-1 fraction." + }, + "returns": "Rickman (2008) brittleness index, the average of the min-max normalised Young's modulus and (inverted) Poisson's ratio terms, as a numpy float64 array; nominally 0-1 (or 0-100 when percent=True), not clipped for out-of-range inputs." + }, + "eaton_pore_pressure": { + "params": { + "exponent": "Eaton exponent applied to the log ratio; default 3.0 is the sonic value (resistivity typically uses about 1.2).", + "hydrostatic": "Normal (hydrostatic) pore pressure at the same depth, same pressure unit as overburden.", + "log_type": "Log-type selector: sonic (default) uses ratio = normal/observed, resistivity uses ratio = observed/normal; other values raise ValueError.", + "normal": "Expected normal-compaction-trend log value at the same depth, same units as observed.", + "observed": "Measured log value at depth: sonic slowness or velocity, or resistivity, per log_type.", + "overburden": "Overburden (total vertical) stress at the depth of interest, in the working pressure unit." + }, + "returns": "Eaton pore pressure Pp = S_ov - (S_ov - P_hydro)*ratio**exponent as a numpy float64 array, in the same pressure unit as overburden and hydrostatic." + }, + "effective_stress": { + "params": { + "biot": "Dimensionless Biot effective-stress coefficient; default 1.0 gives the Terzaghi form.", + "pp": "Pore pressure, same pressure unit as sigma_total.", + "sigma_total": "Total stress in Pa (or any consistent pressure unit shared with pp)." + }, + "returns": "Terzaghi/Biot effective stress sigma_total - biot*Pp as a numpy float64 array, in the input pressure unit." + }, + "gassmann_kdry": { + "params": { + "k_fluid": "Bulk modulus of the pore fluid in Pa.", + "k_mineral": "Bulk modulus of the solid mineral grains in Pa.", + "k_sat": "Saturated-rock bulk modulus in Pa; keyword-only to avoid the k_dry/k_sat argument collision.", + "phi": "Porosity as a volume fraction between 0 and 1." + }, + "returns": "Gassmann dry-frame bulk modulus K_dry (inverse fluid substitution) as a numpy float64 array, in the same units as the input moduli." + }, + "gassmann_ksat": { + "params": { + "k_dry": "Dry-frame (drained rock skeleton) bulk modulus in Pa; keyword-only to avoid the k_dry/k_sat argument collision.", + "k_fluid": "Bulk modulus of the pore fluid in Pa.", + "k_mineral": "Bulk modulus of the solid mineral grains in Pa.", + "phi": "Porosity as a volume fraction between 0 and 1." + }, + "returns": "Gassmann saturated-rock bulk modulus K_sat as a numpy float64 array, in the same units as the input moduli (Pa with SI inputs)." + }, + "hydrostatic_pressure": { + "params": { + "depth": "Vertical depth below surface in m.", + "g": "Gravitational acceleration in m/s2; defaults to standard gravity 9.80665.", + "rho": "Pore-fluid density in kg/m3; defaults to 1000.0 (fresh water)." + }, + "returns": "Hydrostatic pressure P = rho*g*depth as a numpy float64 array, in Pa with SI inputs." + }, + "kirsch_hoop_stress": { + "params": { + "pw": "Wellbore (mud) pressure acting on the borehole wall, same pressure unit as sH.", + "sH": "Maximum horizontal stress in the working pressure unit.", + "sh": "Minimum horizontal stress, same pressure unit as sH.", + "theta_deg": "Angular position around the borehole wall, in degrees measured from the sH azimuth." + }, + "returns": "Kirsch tangential (hoop) stress at the borehole wall, (sH + sh) - 2*(sH - sh)*cos(2*theta) - Pw, as a numpy float64 array in the input pressure unit." + }, + "lame_from_velocity": { + "params": { + "rho": "Bulk density in kg/m3.", + "vp": "Compressional (P-wave) velocity in m/s.", + "vs": "Shear (S-wave) velocity in m/s." + }, + "returns": "Tuple (lambda, mu) of numpy float64 arrays: first Lame parameter lambda = rho*Vp**2 - 2*mu and shear modulus mu = rho*Vs**2, in Pa with SI inputs." + }, + "mannie2_c66": { + "params": { + "c11": "VTI stiffness constant C11, Pa under the module convention.", + "c33": "VTI stiffness constant C33, same units as c11.", + "c44": "VTI stiffness constant C44, same units as c11.", + "k": "Dimensionless gamma/epsilon anisotropy ratio in the closure relation; corpus default 0.93." + }, + "returns": "Modified-ANNIE-2 estimate of the shear stiffness C66 = C44*(1 + k*(C11 - C33)/C33) as a numpy float64 array, in the units of the inputs." + }, + "mannie_c11": { + "params": { + "c33": "VTI stiffness constant C33, Pa under the module convention.", + "c44": "VTI stiffness constant C44, same units as c33.", + "c66": "VTI stiffness constant C66, same units as c33.", + "kp": "Dimensionless modified-ANNIE scaling factor applied to 2*(C66 - C44); default 1.0 recovers plain ANNIE." + }, + "returns": "Modified-ANNIE estimate of the stiffness constant C11 = kp*2*(C66 - C44) + C33 as a numpy float64 array, in the units of the inputs." + }, + "mannie_c13": { + "params": { + "c11": "VTI stiffness constant C11, Pa under the module convention.", + "c66": "VTI stiffness constant C66, same units as c11.", + "k": "Dimensionless modified-ANNIE scaling factor applied to (C11 - 2*C66); default 1.0 recovers plain ANNIE." + }, + "returns": "Modified-ANNIE estimate of the stiffness constant C13 = k*(C11 - 2*C66) as a numpy float64 array, in the units of the inputs." + }, + "min_horizontal_stress": { + "params": { + "biot": "Dimensionless Biot effective-stress coefficient; default 1.0.", + "e": "Young's modulus in the same pressure unit as the stresses; default 0.0 disables the poroelastic strain terms.", + "eps_H": "Dimensionless tectonic strain in the maximum-horizontal-stress direction; default 0.0.", + "eps_h": "Dimensionless tectonic strain in the minimum-horizontal-stress direction; default 0.0.", + "nu": "Dimensionless Poisson's ratio of the formation.", + "pp": "Pore pressure, same pressure unit as sv.", + "sv": "Overburden (vertical) stress in the working pressure unit.", + "tectonic": "Constant additive tectonic stress term, same pressure unit as the stresses; default 0.0." + }, + "returns": "Minimum horizontal stress sh = nu/(1-nu)*(Sv - biot*Pp) + biot*Pp + E/(1-nu**2)*(eps_h + nu*eps_H) + tectonic as a numpy float64 array, in the input pressure unit." + }, + "moduli_from_velocity": { + "params": { + "rho": "Bulk density of the rock in kg/m3.", + "vp": "Compressional (P-wave) velocity in m/s.", + "vs": "Shear (S-wave) velocity in m/s." + }, + "returns": "Tuple (K, G) of numpy float64 arrays: bulk modulus K = rho*Vp**2 - 4/3*G and shear modulus G = rho*Vs**2, in Pa with SI inputs. The order is fixed as (K, G)." + }, + "overburden_stress": { + "params": { + "depth": "Vertical depth below surface in m.", + "g": "Gravitational acceleration in m/s2; defaults to standard gravity 9.80665.", + "rho_bulk": "Average or per-sample overburden bulk density in kg/m3." + }, + "returns": "Overburden (vertical) stress sigma_v = rho_bulk*g*depth as a numpy float64 array, in Pa with SI inputs." + }, + "poisson_from_kg": { + "params": { + "g": "Shear modulus in Pa (same units as k; only the ratio matters).", + "k": "Bulk modulus in Pa." + }, + "returns": "Dimensionless Poisson's ratio nu = (3K - 2G)/(2*(3K + G)) as a numpy float64 array." + }, + "poisson_from_velocity": { + "params": { + "vp": "Compressional (P-wave) velocity in m/s.", + "vs": "Shear (S-wave) velocity in m/s (same units as vp; only the ratio matters)." + }, + "returns": "Dimensionless Poisson's ratio (Vp**2 - 2*Vs**2)/(2*(Vp**2 - Vs**2)) as a numpy float64 array." + }, + "pwave_modulus": { + "params": { + "g": "Shear modulus in Pa.", + "k": "Bulk modulus in Pa." + }, + "returns": "P-wave (constrained) modulus M = K + 4/3*G as a numpy float64 array, in the same units as the inputs (Pa under the module convention)." + }, + "reflection_coefficient": { + "params": { + "z1": "Acoustic impedance of the incident-side medium, any consistent impedance unit.", + "z2": "Acoustic impedance of the transmitted-side medium, same units as z1." + }, + "returns": "Dimensionless normal-incidence amplitude reflection coefficient (Z2 - Z1)/(Z2 + Z1) as a numpy float64 array, between -1 and 1." + }, + "reopening_pressure": { + "params": { + "pp": "Pore pressure, same pressure unit as sh.", + "sH": "Maximum horizontal stress, same pressure unit as sh.", + "sh": "Minimum horizontal stress in the working pressure unit." + }, + "returns": "Fracture reopening pressure 3*sh - sH - Pp (breakdown pressure with zero tensile strength) as a numpy float64 array, in the input pressure unit." + }, + "reuss": { + "params": { + "axis": "Integer array axis along which the components are mixed; defaults to -1, the last axis.", + "f": "Volume fractions of the mixture components arranged along the mixing axis, expected to sum to one.", + "m": "Component moduli (or other property values) arranged along the mixing axis, all in the same units." + }, + "returns": "Reuss (harmonic) lower-bound modulus 1/sum(f_i/m_i) as a numpy float64 array with the mixing axis reduced, in the units of m." + }, + "shear_wave_splitting": { + "params": { + "v_fast": "Velocity of the fast shear wave in m/s.", + "v_slow": "Velocity of the slow shear wave, same units as v_fast." + }, + "returns": "Dimensionless shear-wave splitting (anisotropy) fraction (V_fast - V_slow)/V_fast as a numpy float64 array." + }, + "shmax_from_reopening": { + "params": { + "pp": "Pore pressure, same pressure unit as pr.", + "pr": "Measured fracture reopening pressure in the working pressure unit.", + "sh": "Minimum horizontal stress, same pressure unit as pr." + }, + "returns": "Maximum horizontal stress sH = 3*sh - Pr - Pp inverted from the reopening relation, as a numpy float64 array in the input pressure unit." + }, + "slowness_from_velocity": { + "params": { + "dt_unit": "Output slowness unit: us/ft (default) returns 0.3048*1e6/v, us/m returns 1e6/v; any other value raises ValueError.", + "v": "Velocity in m/s." + }, + "returns": "Acoustic slowness as a numpy float64 array, in us/ft or us/m according to dt_unit." + }, + "snell_angle": { + "params": { + "degrees": "If True (default) the angle is returned in degrees; if False, in radians.", + "v1": "Wave velocity in the first (incident) medium, in m/s; when v1 is the slower medium the result is the critical angle.", + "v2": "Wave velocity in the second medium, same units as v1." + }, + "returns": "Snell critical/refraction angle arcsin(V1/V2) as a numpy float64 array, in degrees by default or radians when degrees=False." + }, + "stiffness_from_velocity": { + "params": { + "rho": "Bulk density in kg/m3.", + "v": "Wave velocity in m/s; using Vp yields the P-wave modulus M, using Vs the shear modulus." + }, + "returns": "Stiffness coefficient C = rho*V**2 as a numpy float64 array, in Pa with SI inputs." + }, + "thomsen_delta": { + "params": { + "c13": "VTI stiffness constant C13, Pa under the module convention.", + "c33": "VTI stiffness constant C33 (vertical P-wave stiffness), same units as c13.", + "c44": "VTI stiffness constant C44 (vertical shear stiffness), same units as c13." + }, + "returns": "Dimensionless Thomsen delta parameter ((C13 + C44)**2 - (C33 - C44)**2)/(2*C33*(C33 - C44)) as a numpy float64 array." + }, + "thomsen_epsilon": { + "params": { + "c11": "VTI stiffness constant C11 (horizontal P-wave stiffness), Pa under the module convention.", + "c33": "VTI stiffness constant C33 (vertical P-wave stiffness), same units as c11." + }, + "returns": "Dimensionless Thomsen P-wave anisotropy parameter epsilon = (C11 - C33)/(2*C33) as a numpy float64 array." + }, + "thomsen_gamma": { + "params": { + "c44": "VTI stiffness constant C44 (vertical shear stiffness), same units as c66.", + "c66": "VTI stiffness constant C66 (horizontally polarized shear stiffness), Pa under the module convention." + }, + "returns": "Dimensionless Thomsen S-wave anisotropy parameter gamma = (C66 - C44)/(2*C44) as a numpy float64 array." + }, + "transmission_energy": { + "params": { + "z1": "Acoustic impedance of the incident-side medium, any consistent impedance unit.", + "z2": "Acoustic impedance of the transmitted-side medium, same units as z1." + }, + "returns": "Dimensionless fraction of incident energy transmitted across the interface, 1 - R**2 with R the normal-incidence reflection coefficient, as a numpy float64 array." + }, + "velocity_from_moduli": { + "params": { + "g": "Shear modulus in Pa.", + "k": "Bulk modulus in Pa.", + "rho": "Bulk density of the rock in kg/m3." + }, + "returns": "Tuple (Vp, Vs) of numpy float64 arrays: compressional velocity sqrt((K + 4/3*G)/rho) and shear velocity sqrt(G/rho), in m/s with SI inputs." + }, + "velocity_from_slowness": { + "params": { + "dt": "Acoustic slowness (interval transit time) in the unit named by dt_unit, us/ft or us/m.", + "dt_unit": "Slowness unit selector: us/ft (default) applies 0.3048*1e6/dt, us/m applies 1e6/dt; any other value raises ValueError." + }, + "returns": "Velocity in m/s as a numpy float64 array." + }, + "voigt": { + "params": { + "axis": "Integer array axis along which the components are mixed and summed; defaults to -1, the last axis.", + "f": "Volume fractions of the mixture components arranged along the mixing axis, expected to sum to one.", + "m": "Component moduli (or other property values) arranged along the mixing axis, all in the same units." + }, + "returns": "Voigt (arithmetic) upper-bound modulus sum(f_i*m_i) as a numpy float64 array with the mixing axis reduced, in the units of m." + }, + "voigt_reuss_hill": { + "params": { + "axis": "Integer array axis along which the components are mixed; defaults to -1, the last axis.", + "f": "Volume fractions of the mixture components arranged along the mixing axis, expected to sum to one.", + "m": "Component moduli arranged along the mixing axis, all in the same units." + }, + "returns": "Voigt-Reuss-Hill average modulus 0.5*(Voigt + Reuss) as a numpy float64 array with the mixing axis reduced, in the units of m." + }, + "vs_from_vp": { + "params": { + "method": "Empirical correlation selector: castagna_ls (default, Castagna limestone quadratic), pickett_ls (Vp/1.9), pickett_dol (Vp/1.8), carroll (power law), or brocher (Brocher 2005 fourth-order polynomial); other values raise ValueError.", + "vp": "Compressional (P-wave) velocity in km/s." + }, + "returns": "Estimated shear (S-wave) velocity Vs in km/s as a numpy float64 array." + }, + "vti_engineering_moduli": { + "params": { + "c11": "VTI stiffness constant C11, Pa under the module convention.", + "c12": "Optional VTI stiffness constant C12, same units as c11; when None (default) it is computed as C11 - 2*C66.", + "c13": "VTI stiffness constant C13, same units as c11.", + "c33": "VTI stiffness constant C33, same units as c11.", + "c44": "VTI stiffness constant C44, same units as c11.", + "c66": "VTI stiffness constant C66, same units as c11." + }, + "returns": "Dict of numpy float64 arrays with keys Ev, Eh, nu_v, nu_h, Gvh, Ghh: vertical and horizontal Young's moduli, vertical and horizontal Poisson's ratios (dimensionless), and the shear moduli Gvh = C44 and Ghh = C66, moduli in the units of the stiffness inputs." + }, + "wood_fluid_modulus": { + "params": { + "axis": "Integer array axis along which the fluid phases are mixed; defaults to -1, the last axis.", + "moduli": "Bulk moduli of the individual fluid phases arranged along the mixing axis, in Pa with SI inputs.", + "saturations": "Fluid-phase saturations (fractions summing to one) arranged along the mixing axis." + }, + "returns": "Wood's law effective fluid bulk modulus (Reuss harmonic average, 1/K_fl = sum(S_i/K_i)) as a numpy float64 array in the units of moduli." + }, + "youngs_from_kg": { + "params": { + "g": "Shear modulus in Pa.", + "k": "Bulk modulus in Pa." + }, + "returns": "Young's modulus E = 9*K*G/(3*K + G) as a numpy float64 array, in the same units as the input moduli (Pa under the module convention)." + }, + "youngs_poisson_dynamic": { + "params": { + "rho": "Bulk density in kg/m3.", + "vp": "Compressional (P-wave) velocity in m/s.", + "vs": "Shear (S-wave) velocity in m/s." + }, + "returns": "Tuple (E, nu) of numpy float64 arrays: dynamic Young's modulus E in Pa with SI inputs, and dimensionless dynamic Poisson's ratio nu." + } + }, + "petrolib.borehole_image": { + "apparent_dip": { + "params": { + "section_azimuth_deg": "Angle beta in degrees between the cross-section direction and the true-dip direction.", + "true_dip_deg": "True dip of the plane in degrees." + }, + "returns": "Apparent dip seen in the section, from tan(apparent) = tan(true)*cos(beta), in degrees as a scalar float." + }, + "bed_sinusoid": { + "params": { + "azimuth_deg": "Borehole-wall azimuth values phi in degrees at which the bed depth trace is evaluated.", + "dip_azimuth_deg": "Dip azimuth in degrees, the azimuth at which the sinusoid reaches its deepest point.", + "dip_deg": "True dip of the planar bed in degrees; the sinusoid amplitude is radius*tan(dip).", + "radius": "Borehole radius, in the same length units as z0; scales the sinusoid amplitude.", + "z0": "Sinusoid offset, the bed depth at its mean level, in the same length units as radius." + }, + "returns": "Depth trace z(phi) = z0 - radius*tan(dip)*cos(phi - dip_azimuth) on the unrolled borehole wall, as a numpy float64 array matching azimuth_deg, in the length units of z0 and radius." + }, + "class_fractions": { + "params": { + "image": "Grey-level image of any shape (flattened internally) to partition into intensity classes.", + "thresholds": "Ascending threshold values [t0, t1, ...] in the image's intensity units defining the class boundaries." + }, + "returns": "Numpy float64 array of len(thresholds)+1 volume fractions: mean(img < t0), the fractions between consecutive thresholds, and mean(img >= t_last), summing to one; e.g. pore/clay/grain from two thresholds (dark = pore convention)." + }, + "dip_from_amplitude": { + "params": { + "amplitude": "Fitted sinusoid amplitude, e.g. from fit_sinusoid; may be in depth samples if sample_spacing converts it to length.", + "radius": "Borehole radius, in the same length units as amplitude*sample_spacing.", + "sample_spacing": "Depth increment per sample (default 1.0) converting an amplitude expressed in samples to length units." + }, + "returns": "True dip angle arctan(amplitude*sample_spacing/radius) in degrees, as a scalar float." + }, + "fit_plane": { + "params": { + "points_enz": "Array of shape (n, 3) of (Easting, Northing, TVD) coordinates of points lying on the plane to fit." + }, + "returns": "Tuple of two floats (dip_deg, dip_azimuth_deg): the plane dip arccos(|n_z|) in degrees, and the dip azimuth atan2(n_E, n_N) mod 360 in degrees clockwise from North." + }, + "fit_sinusoid": { + "params": { + "azimuth_deg": "Azimuth samples in degrees corresponding to the picked depths z.", + "mask": "Optional boolean array selecting the valid samples to include in the least-squares fit.", + "z": "Picked bed-trace depth values along the azimuth axis, in any consistent length or sample units." + }, + "returns": "Tuple of three floats (z0, amplitude, phase_deg): the fitted sinusoid offset, the amplitude hypot(c1, c2) in the units of z, and the phase atan2(c2, c1) in degrees." + }, + "glcm": { + "params": { + "image": "2-D grey-level image indexed [depth, azimuth] (rows = depth, columns = azimuth), quantised internally to the requested grey levels by min-max scaling.", + "levels": "Number of quantisation grey levels and hence the size of the co-occurrence matrix (default 16).", + "offset": "Pixel displacement (drow, dcol) between co-occurring pairs (default (0, 1), horizontal neighbours).", + "symmetric": "If True (default), each pixel pair is counted in both directions, making the matrix symmetric." + }, + "returns": "Normalised grey-level co-occurrence matrix of shape (levels, levels) as a numpy float64 array whose entries sum to one (left all-zero if no pairs were counted)." + }, + "glcm_features": { + "params": { + "p": "Normalised (sum-to-one) square grey-level co-occurrence matrix, e.g. the output of glcm." + }, + "returns": "Dict of scalar floats with keys 'contrast', 'energy', and 'correlation', the Haralick texture features of the GLCM; correlation is 0.0 when either marginal variance is zero." + }, + "otsu_threshold": { + "params": { + "bins": "Number of histogram bins spanning [min, max] of the image (default 256).", + "image": "Grey-level image of any shape (flattened internally), thresholded on its native min-max data range." + }, + "returns": "Otsu threshold as a scalar float in the image's native intensity units, the centre of the histogram bin that maximises the between-class variance." + }, + "phase_saturation": { + "params": { + "phase": "Boolean (or truthy) mask flagging the voxels/pixels occupied by the fluid phase of interest.", + "pore": "Boolean (or truthy) mask flagging the pore voxels/pixels." + }, + "returns": "Saturation of the phase within the pore space, count(phase AND pore)/count(pore), as a dimensionless scalar float fraction." + }, + "porosity_from_mask": { + "params": { + "pore": "Boolean (or truthy) segmentation mask flagging pore voxels/pixels." + }, + "returns": "Porosity as the fraction of voxels flagged pore, pore.sum()/pore.size, a dimensionless scalar float in [0, 1]." + }, + "sobel_gradient": { + "params": { + "image": "2-D grey-level image indexed [depth, azimuth] (rows = depth, columns = azimuth)." + }, + "returns": "Tuple of three numpy float64 arrays (gx, gy, magnitude), each with the image's shape: the horizontal (column) derivative, the vertical (row) derivative, and sqrt(gx^2 + gy^2); the one-pixel border of gx and gy is zero." + } + }, + "petrolib.capillary_pressure": { + "brooks_corey_pc": { + "params": { + "lam": "Brooks-Corey pore-size-distribution index lambda (reciprocal-convention sources pass lam=1.0/N).", + "pc_entry": "Entry (displacement) pressure Pe; sets the pressure unit of the result.", + "snr": "Residual nonwetting-phase saturation closing the normalization window (Swn denominator 1-Swirr-Snr); default 0.0, the drainage convention.", + "sw": "Water saturation (v/v fraction).", + "swirr": "Irreducible water saturation (v/v fraction)." + }, + "returns": "Brooks-Corey capillary pressure Pc = Pe * Swn**(-1/lam), where Swn = (Sw-Swirr)/(1-Swirr-Snr), in the unit of pc_entry. Numpy float64 array broadcast over the inputs." + }, + "brooks_corey_sw": { + "params": { + "clip": "Optional (lo, hi) tuple clipping the resulting Sw; default None (unclipped).", + "lam": "Brooks-Corey pore-size-distribution index lambda; sources using the reciprocal 1/N convention must pass lam=1.0/N (the two are not interchangeable).", + "pc": "Capillary pressure, in the same pressure unit as pc_entry.", + "pc_entry": "Entry (displacement) pressure Pe, in the same unit as pc.", + "swirr": "Irreducible water saturation (v/v fraction)." + }, + "returns": "Brooks-Corey drainage water saturation Sw = Swirr + (1-Swirr)*(Pe/Pc)**lam (v/v fraction), set to 1.0 at and below the entry pressure. Numpy float64 array broadcast over the inputs." + }, + "buoyancy_pc": { + "params": { + "delta_rho": "Water-hydrocarbon density difference in kg/m3.", + "g": "Gravitational acceleration in m/s2; defaults to standard gravity 9.80665.", + "height": "Height above the free-water level in m." + }, + "returns": "Buoyancy capillary pressure Pc = delta_rho * g * h in Pa. Numpy float64 array broadcast over the inputs." + }, + "buoyancy_pc_gradient": { + "params": { + "gradient_psi_per_ft": "Freshwater pressure gradient per unit specific gravity, in psi/ft; default 0.433, the conventional rounded field constant.", + "height_ft": "Height above the free-water level in feet.", + "sg_hc": "Specific gravity of the hydrocarbon phase (dimensionless).", + "sg_water": "Specific gravity of the formation water (dimensionless, relative to fresh water)." + }, + "returns": "Oilfield buoyancy capillary pressure Pc = gradient*(SGw - SGhc)*h in psi (the module's one deliberate non-SI exception). Numpy float64 array broadcast over the inputs." + }, + "capillary_rise_height": { + "params": { + "delta_rho": "Density difference between the two fluids in kg/m3.", + "g": "Gravitational acceleration in m/s2; defaults to standard gravity 9.80665.", + "radius": "Capillary tube radius in m.", + "sigma": "Interfacial (surface) tension in N/m.", + "theta_deg": "Contact angle in degrees; default 0.0. The signed cosine is always used (no absolute option)." + }, + "returns": "Equilibrium capillary rise h = 2*sigma*cos(theta)/(delta_rho*g*r) in m, signed by physics: positive (rise) for a wetting fluid (theta < 90 deg), negative (depression) for a non-wetting fluid such as mercury. Numpy float64 array broadcast over the inputs." + }, + "centrifuge_pc": { + "params": { + "delta_rho": "Density difference between the two fluids in kg/m3.", + "omega": "Centrifuge angular speed in rad/s (convert rpm as omega = rpm*2*pi/60).", + "r1": "Distance from the rotation axis to the inner (inlet) face of the core in m.", + "r2": "Distance from the rotation axis to the outer (outlet) face of the core in m." + }, + "returns": "Hassler-Brunner capillary pressure at the inlet face, Pc = 0.5*delta_rho*omega**2*(r2**2 - r1**2), in Pa. Numpy float64 array broadcast over the inputs." + }, + "height_above_fwl": { + "params": { + "delta_rho": "Water-hydrocarbon density difference in kg/m3.", + "g": "Gravitational acceleration in m/s2; defaults to standard gravity 9.80665.", + "pc": "Capillary pressure in Pa." + }, + "returns": "Height above the free-water level h = Pc/(delta_rho*g) in m; the inverse of buoyancy_pc. Numpy float64 array broadcast over the inputs." + }, + "leverett_j": { + "params": { + "absolute": "If True (default), uses |cos(theta)| in the denominator; if False, uses the signed cosine.", + "k": "Permeability expressed as an area (e.g. m2), so that sqrt(k/phi) is a length.", + "pc": "Capillary pressure, in a pressure unit consistent with sigma (Pa with SI inputs).", + "phi": "Porosity (v/v fraction).", + "sigma": "Interfacial tension, in a force-per-length unit consistent with pc (N/m in SI).", + "theta_deg": "Contact angle in degrees; default 0.0 (sources that omit the cosine effectively pass 0)." + }, + "returns": "Dimensionless Leverett J-function J = Pc/(sigma*cos(theta)) * sqrt(k/phi). Numpy float64 array broadcast over the inputs." + }, + "lucas_washburn_length": { + "params": { + "mu": "Dynamic viscosity of the imbibing fluid in Pa*s.", + "radius": "Capillary (pore) radius in m.", + "sigma": "Interfacial tension in N/m.", + "t": "Elapsed imbibition time in s.", + "theta_deg": "Contact angle in degrees; default 0.0. The signed cosine is used, so cos(theta) < 0 (oil-wet) makes the square-root argument negative." + }, + "returns": "Lucas-Washburn imbibition front position L(t) = sqrt(sigma*r*cos(theta)*t/(2*mu)) in m; NaN where cos(theta) < 0 (no clip-to-zero, deliberately). Numpy float64 array broadcast over the inputs." + }, + "pc_convert_system": { + "params": { + "absolute": "If True (default), uses |cos| for both systems so mercury conversions stay positive; if False, uses signed cosines.", + "pc": "Capillary pressure measured in the source (e.g. laboratory air-mercury) fluid system; the output keeps its pressure unit.", + "sigma_from": "Interfacial tension of the source fluid system in N/m.", + "sigma_to": "Interfacial tension of the target (e.g. reservoir gas-brine) system in N/m.", + "theta_from_deg": "Contact angle of the source fluid system in degrees.", + "theta_to_deg": "Contact angle of the target system in degrees." + }, + "returns": "Capillary pressure converted to the target system by the sigma*cos(theta) ratio, Pc * (sigma_to*cos(theta_to))/(sigma_from*cos(theta_from)), in the same pressure unit as pc. Numpy float64 array broadcast over the inputs." + }, + "pc_from_leverett_j": { + "params": { + "absolute": "If True (default), uses |cos(theta)|; if False, uses the signed cosine.", + "j": "Dimensionless Leverett J-function values.", + "k": "Permeability expressed as an area (e.g. m2).", + "phi": "Porosity (v/v fraction).", + "sigma": "Interfacial tension of the target fluid system in N/m (sets the output pressure unit).", + "theta_deg": "Contact angle of the target system in degrees; default 0.0." + }, + "returns": "Capillary pressure Pc = J*sigma*cos(theta)/sqrt(k/phi), in Pa with SI inputs; the inverse of leverett_j. Numpy float64 array broadcast over the inputs." + }, + "thomeer_shg": { + "params": { + "bv_inf": "Thomeer asymptote Bv_inf: the bulk volume occupied by mercury at infinite pressure, setting the unit/scale of the result.", + "g": "Thomeer pore geometrical factor G; its numerical value depends on log_base (log10 and natural-log G values differ by ln(10) = 2.303 and are not interchangeable).", + "log_base": "Base of the logarithm in the hyperbola exponent; default 10.0 (log10 convention), pass e for the natural-log convention.", + "pc": "Capillary pressure, in the same pressure unit as pd.", + "pd": "Displacement (entry) pressure Pd, in the same unit as pc." + }, + "returns": "Thomeer mercury bulk-volume saturation Shg = Bv_inf * exp(-G / log_base(Pc/Pd)), in the same unit as bv_inf, and 0.0 at and below Pd. Numpy float64 array broadcast over the inputs." + }, + "washburn_diameter": { + "params": { + "absolute": "If True (default), uses |cos(theta)|, folding the mercury contact angle's negative sign into a positive result; if False, uses the signed cosine.", + "pc": "Capillary pressure in Pa.", + "sigma": "Interfacial tension in N/m.", + "theta_deg": "Contact angle in degrees; default 0.0 (mercury sources use 140 or 180)." + }, + "returns": "Pore-throat diameter d = 4*sigma*cos(theta)/Pc in m (with SI inputs); the diameter dialect of washburn_radius. Numpy float64 array broadcast over the inputs." + }, + "washburn_radius": { + "params": { + "absolute": "If True (default), uses |cos(theta)| (MICP convention); if False, uses the signed cosine.", + "pc": "Capillary pressure in Pa.", + "sigma": "Interfacial tension in N/m.", + "theta_deg": "Contact angle in degrees; default 0.0." + }, + "returns": "Pore-throat radius r = 2*sigma*cos(theta)/Pc in m (with SI inputs); the inverse of young_laplace_pc. Numpy float64 array broadcast over the inputs." + }, + "young_laplace_pc": { + "params": { + "absolute": "If True (default), uses |cos(theta)| (the MICP convention, keeping Pc positive); if False, uses the signed cosine for physical sign conventions.", + "radius": "Capillary tube (pore-throat) radius in m.", + "sigma": "Interfacial tension in N/m.", + "theta_deg": "Contact angle in degrees; default 0.0 gives the pure spherical-meniscus jump 2*sigma/r." + }, + "returns": "Capillary entry pressure Pc = 2*sigma*cos(theta)/r in Pa (with SI inputs). Numpy float64 array broadcast over the inputs." + } + }, + "petrolib.data_qc_io.clean": { + "closure_residual": { + "params": { + "*fracs": "One or more fraction arrays (e.g. saturations Sw, So, Sg) that are summed elementwise; at least one is required.", + "target": "Value the fractions should sum to (default 1.0)." + }, + "returns": "Numpy float array of the closure residual sum(fracs) - target; zero where the fractions close exactly." + }, + "despike": { + "params": { + "replace": "Replacement rule for flagged spikes: 'interp' (default) linear interpolation from surviving samples, 'median' the global median of the input, 'nan' NaN.", + "side": "'both' (default), 'upper', or 'lower', restricting which tail of the distribution counts as spikes.", + "threshold": "MAD multiplier of the spike criterion: samples further than threshold*MAD*1.4826 from the median are spikes (default 3.0).", + "x": "1-D curve to be despiked." + }, + "returns": "Numpy float copy of x with the flagged spikes replaced according to the replace rule; an unchanged copy if no spikes are found." + }, + "impute_gaps": { + "params": { + "index": "Optional sample coordinates (e.g. depths) used as the interpolation abscissa; defaults to arange(len(x)).", + "x": "1-D curve containing NaN gaps to be filled." + }, + "returns": "Numpy float copy of x with NaN runs filled by linear interpolation between surrounding valid samples; leading and trailing NaNs are held flat at the nearest valid value. Raises ValueError if x has no finite values." + }, + "outlier_mask": { + "params": { + "k": "Whisker multiplier for 'iqr': outliers fall outside (Q1 - k*IQR, Q3 + k*IQR); default 1.5, ignored by the other methods.", + "method": "Detection method: 'zscore' (default) population z-score, 'iqr' Tukey interquartile whiskers, 'mad' robust median-absolute-deviation criterion.", + "side": "'both' (default) flags both tails, 'upper' only high exceedances, 'lower' only low ones.", + "threshold": "Cutoff for 'zscore' (|z| > threshold) and 'mad' (further than threshold*MAD*1.4826 from the median); default 3.0, ignored by 'iqr'.", + "x": "Input samples screened for outliers." + }, + "returns": "Boolean numpy array the same shape as x, True where a sample is flagged as an outlier by the chosen criterion (strict comparisons)." + }, + "relative_discrepancy": { + "params": { + "a": "First set of measurements being compared.", + "b": "Second set of measurements being compared." + }, + "returns": "Numpy float array of the dimensionless relative discrepancy 2*|a - b| / (a + b), the absolute difference divided by the mean of the two values." + }, + "renormalize": { + "params": { + "axis": "Axis along which the fractions are summed and normalized (default -1, the last axis).", + "fracs": "Array of component fractions (e.g. mineral volumes or oxides) to be rescaled." + }, + "returns": "Numpy float array of the fractions divided by their sum along axis so they sum to one; a zero sum yields NaN/inf rather than a guessed composition." + }, + "sentinels_to_nan": { + "params": { + "atol": "Absolute tolerance of the np.isclose sentinel match (default 1e-8).", + "rtol": "Relative tolerance of the np.isclose sentinel match (default 1e-5), so float round-trips of a sentinel still match.", + "sentinels": "Sequence of sentinel values to replace; defaults to the common LAS/DLIS set (-999.0, -999.25, -9999.0).", + "x": "Input log/curve values that may contain no-data sentinel values." + }, + "returns": "Numpy float copy of x with every value matching a sentinel (within the isclose tolerances) replaced by NaN." + } + }, + "petrolib.data_qc_io.filt": { + "detect_bed_boundaries": { + "params": { + "curve": "1-D log curve scanned for bed boundaries.", + "threshold": "Detection cutoff on the metric |gradient|*sqrt(moving variance); None (default) uses the adaptive value mean + 2*std of the metric.", + "window": "Centered window length in samples of the moving variance in the detection metric; edge samples are left 0 (default 11)." + }, + "returns": "Integer numpy array of sample indices where the metric is a local maximum above the threshold, interpreted as bed-boundary positions." + }, + "median_filter2d": { + "params": { + "img": "2-D image array to be filtered.", + "size": "Odd side length of the square median window (default 3)." + }, + "returns": "Numpy float array of the median-filtered image, same shape as img, computed over the size-by-size neighborhood with edge-replicated padding." + }, + "moving_stat": { + "params": { + "center": "True (default) uses the symmetric window centered on each sample; False uses the causal trailing window ending at the sample.", + "stat": "Statistic to compute: 'mean' (default), 'std', 'var' (population, ddof=0), 'mad' mean absolute deviation about the window mean, or 'cv' std/mean (0 where the mean is 0).", + "window": "Window length in samples; windows shrink at the edges rather than padding.", + "x": "1-D curve over which the moving statistic is computed." + }, + "returns": "Numpy float array, same length as x, holding the moving-window statistic evaluated at every sample." + }, + "smooth": { + "params": { + "kind": "Filter type: 'boxcar' (default) centered moving average, 'gaussian' normalized Gaussian kernel, 'median' running median with edge-replicated padding.", + "sigma": "Gaussian kernel standard deviation in samples; required when kind='gaussian' and ignored otherwise.", + "window": "Filter length in samples: the boxcar length (window <= 1 returns a copy) or the odd running-median window; unused for kind='gaussian'. Default 5.", + "x": "1-D curve to be smoothed." + }, + "returns": "Numpy float array of the smoothed curve, same length as x; boxcar and Gaussian smoothing use zero-padded np.convolve(mode='same') edges, the median filter uses edge-replicated padding." + }, + "tool_response": { + "params": { + "dz": "Grid (sample) spacing of x, in the same length units as fwhm.", + "fwhm": "Full width at half maximum of the Gaussian tool vertical response (the tool aperture), in the same length units as dz.", + "x": "Fine-scale curve (e.g. core-measured property versus depth) to be blurred to logging-tool resolution." + }, + "returns": "Numpy float array of the curve convolved with the unit-normalized Gaussian tool response (sigma = fwhm/(2*sqrt(2*ln 2)), np.convolve mode='same' with zero-padded edges), same length as x." + }, + "window_features": { + "params": { + "window": "Odd window length in samples, centered on each depth sample (default 11).", + "x": "1-D curve of length n, or 2-D (n, d) matrix of d curves, to be expanded into sliding windows." + }, + "returns": "Numpy float feature matrix: shape (n, window) for a 1-D input or (n, d*window) for a 2-D input, where row i is the edge-padded window centered on sample i." + } + }, + "petrolib.data_qc_io.scale": { + "match_moments": { + "params": { + "target_mean": "Desired mean of the output, in the curve's units.", + "target_std": "Desired population standard deviation (ddof=0) of the output, in the curve's units.", + "x": "Curve values to transform." + }, + "returns": "Float64 array of the affine transform (x - mean(x))/std(x)*target_std + target_mean, having exactly the target mean and population std; a constant input divides by zero (producing inf/NaN) rather than guessing a scale." + }, + "normalize_to_reference": { + "params": { + "in_hi": "Input-well high endpoint; if None (default) it is derived from x using pct (or the maximum when pct is None).", + "in_lo": "Input-well low endpoint; if None (default) it is derived from x using pct (or the minimum when pct is None).", + "pct": "Pair of (low, high) percentiles used to derive omitted endpoints from x; default (5.0, 95.0), the Shier convention; None selects min/max instead (image-histogram convention).", + "ref_hi": "Reference-well high endpoint, in curve units, that the input-well high endpoint is mapped to.", + "ref_lo": "Reference-well low endpoint, in curve units, that the input-well low endpoint is mapped to.", + "x": "Input-well curve values to normalize (e.g. a gamma-ray log or image histogram)." + }, + "returns": "Float64 array of x affinely mapped onto the reference endpoints: out = ref_lo + (ref_hi - ref_lo)*(x - in_lo)/(in_hi - in_lo). No clipping is applied, so values outside the input endpoints extrapolate beyond the reference range." + } + }, + "petrolib.data_qc_io.signal": { + "add_gaussian_noise": { + "params": { + "rng": "Numpy random Generator or integer seed controlling the draw; None (default) uses the source article's deterministic default_rng(0).", + "sigma_fraction": "Noise standard deviation expressed as a fraction of the curve's mean absolute level: sigma = sigma_fraction * mean(|x|).", + "x": "Clean curve to which the synthetic noise is added." + }, + "returns": "Float64 array of the same shape as x with zero-mean Gaussian noise of a single scalar standard deviation added to every sample." + }, + "block_stack": { + "params": { + "axis": "Axis along which consecutive groups of n samples are averaged; default -1 (the last axis).", + "n": "Number of consecutive samples averaged into each block; must be at least 1.", + "x": "Array of repeat measurements to be stacked." + }, + "returns": "Float64 array with the stacking axis reduced to size // n block means; a trailing partial block shorter than n is dropped. Other axes are unchanged." + }, + "snr_db": { + "params": { + "guard": "Small constant added to the measured noise power (e.g. 1e-30, the VMD-paper convention) to guard against a zero denominator; default 0.0.", + "noise": "Noise record or residual whose power is mean(noise**2) + guard; give exactly one of noise or noise_std.", + "noise_std": "Known noise standard deviation; the noise power is noise_std**2; give exactly one of noise or noise_std.", + "signal": "Signal record whose power is computed as mean(signal**2)." + }, + "returns": "Scalar float: the signal-to-noise ratio in dB, 10*log10(signal power / noise power); returns inf when the noise power is exactly zero." + }, + "stack_gain": { + "params": { + "n": "Number of repeat measurements stacked (scalar or array)." + }, + "returns": "Amplitude SNR improvement factor sqrt(n) from stacking, dimensionless, as a float64 numpy array." + } + }, + "petrolib.data_qc_io.synth": { + "blocky_log": { + "params": { + "level_range": "(low, high) range from which each bed level is drawn uniformly (default (20.0, 150.0)).", + "n": "Number of samples in the generated log.", + "n_beds": "Number of constant-level beds (default 8); must be at least 1 and small enough for the interior margin.", + "noise": "Standard deviation of the additive Gaussian noise on the curve (default 2.0).", + "rng": "Random source: a numpy Generator, an integer seed, or None for the deterministic default_rng(0)." + }, + "returns": "Tuple (curve, bounds): the length-n noisy blocky log as a numpy float array, and a sorted integer array of the n_beds - 1 interior boundary indices drawn without replacement away from the ends." + }, + "disk_image": { + "params": { + "n_features": "Number of dark disks (radii 2 to 7 pixels) stamped at uniform random centers.", + "rng": "Random source: a numpy Generator, an integer seed, or None for the deterministic default_rng(0).", + "size": "Side length in pixels of the square output image." + }, + "returns": "Tuple (image, mask): a (size, size) numpy float grey image with a bright noisy background (about 0.6) and dark disks (about 0.15), clipped to [0, 1], and the boolean (size, size) mask that is True on disk (pore) pixels." + }, + "gaussian_mixture_spectrum": { + "params": { + "amps": "Peak amplitude of each Gaussian component.", + "axis": "Spectrum abscissa values (e.g. NMR T2 times or pore sizes) at which the spectrum is evaluated.", + "centres": "Center position of each Gaussian component, in axis units (converted to log10 when log_axis is True).", + "log_axis": "If True (default), components are Gaussian in log10 of the axis (widths in decades); if False, in the raw axis values.", + "noise": "Standard deviation of Gaussian noise added when greater than 0; the noisy spectrum is then clipped at zero (default 0.0).", + "rng": "Random source used only when noise > 0: a numpy Generator, an integer seed, or None for the deterministic default_rng(0).", + "widths": "Standard deviation of each component: in decades when log_axis is True, otherwise in raw axis units." + }, + "returns": "Numpy float array, same length as axis, of the summed Gaussian components at each axis sample; non-negative when noise is added because the result is clipped at zero." + }, + "log_suite": { + "params": { + "n": "Number of depth samples in the synthetic well (default 600).", + "n_facies": "Number of facies blocks, from 1 to 4, indexing the rows of LOG_SUITE_FACIES_PROPS (default 4).", + "rng": "Random source: a numpy Generator, an integer seed, or None for the deterministic default_rng(0)." + }, + "returns": "Tuple (logs, boundaries, facies_labels): a dict mapping the curve names GR, RD, RHOB, DT, NPHI to length-n numpy float arrays, an integer array of interior breakpoint indices, and a length-n integer array of the per-sample facies index." + }, + "shifted_pair": { + "params": { + "n": "Length of each generated curve in samples.", + "noise": "Standard deviation of independent Gaussian noise added to each curve (default 0.0, noise-free).", + "rng": "Random source used only when noise > 0: a numpy Generator, an integer seed, or None for the deterministic default_rng(0).", + "shift": "Integer lag in samples between the two curves (other[i] = ref[i + shift] noise-free); may be negative." + }, + "returns": "Tuple (ref, other) of length-n numpy float arrays: a multi-harmonic reference curve and its copy lagged by shift samples, so a depth-alignment method should recover the shift." + }, + "sphere_pack_volume": { + "params": { + "max_grains": "Safety cap on the number of spheres stamped before stopping (default 100000).", + "r_range": "(low, high) inclusive range of integer sphere radii in voxels, drawn uniformly per grain.", + "rng": "Random source: a numpy Generator, an integer seed, or None for the deterministic default_rng(0).", + "shape": "(nx, ny, nz) voxel dimensions of the output grid.", + "target_phi": "Target pore (void) fraction; sphere stamping stops once the pore fraction drops to this value." + }, + "returns": "Numpy uint8 array of the given shape with 0 marking pore voxels and 1 marking grain voxels: a random sphere pack whose porosity is approximately target_phi." + } + }, + "petrolib.depth_correction": { + "cable_tension": { + "params": { + "cable_weight_per_length": "Buoyant weight of the cable per unit length, in newtons per metre.", + "depth": "Depth(s) z along the cable at which the tension is evaluated, in metres.", + "tool_weight": "Weight of the tool hanging at the bottom of the cable, in newtons.", + "total_depth": "Total cable length L (the tool depth), in metres." + }, + "returns": "Cable tension profile T(z) = W_tool + w*(L - z) as a numpy float64 array; newtons with SI inputs. Greatest at surface and equal to tool_weight at the tool." + }, + "corrected_depth": { + "params": { + "convention": "Sign convention: \"tally\" (default) adds the corrections because the surface tally reads short; \"payout\" subtracts them because paid-out length overstates the tool depth; other values raise ValueError.", + "measured": "Measured depth to be corrected (surface tally or paid-out cable length), in metres.", + "stretch": "Elastic-stretch correction to apply, in metres; default 0.0.", + "thermal": "Thermal-elongation correction to apply, in metres; default 0.0." + }, + "returns": "Corrected depth as a numpy float64 array in the units of measured: the measured depth plus (tally) or minus (payout) the sum of the stretch and thermal corrections." + }, + "distributed_stretch": { + "params": { + "buoyancy": "Dimensionless buoyancy factor scaling the string's own-weight (quadratic) term; default 1.0.", + "ea": "Lumped axial stiffness E*A of the string (Young's modulus times cross-section), in newtons with SI inputs.", + "end_load": "Point load F carried at the bottom of the string, in newtons; default 0.0.", + "length": "Suspended length L of the hanging string, in metres.", + "weight_per_length": "Buoyant weight per unit length w of the string, in newtons per metre." + }, + "returns": "Total hanging-string stretch dL = (F*L + 0.5*b*w*L**2)/(E*A), combining the end-load and self-weight contributions, as a numpy float64 array; metres with SI inputs." + }, + "elastic_stretch": { + "params": { + "E": "Young's modulus of the member material, in pascals; defaults to the steel value E_STEEL = 2.0e11 Pa.", + "area": "Metal cross-sectional area of the member, in square metres.", + "force": "Axial load applied to the cable or pipe, in newtons.", + "length": "Suspended length of the member subject to the load, in metres." + }, + "returns": "Point-load Hookean stretch dL = F*L/(E*A) as a numpy float64 array; metres with SI inputs." + }, + "thermal_elongation": { + "params": { + "alpha": "Linear thermal-expansion coefficient, in 1/K; defaults to the steel value ALPHA_STEEL = 1.2e-5.", + "dT": "Temperature rise above the calibration temperature, in kelvin (or degrees celsius difference).", + "length": "Length of the member being heated, in metres." + }, + "returns": "Thermal elongation dL = alpha*L*dT as a numpy float64 array; metres with SI inputs." + } + }, + "petrolib.depth_matching": { + "apply_depth_shift": { + "params": { + "depth": "Depth coordinates of the samples, in any consistent depth unit.", + "shift": "Depth shift in the same units as depth; either a scalar bulk shift or a per-sample array.", + "values": "Curve values sampled on the depth grid." + }, + "returns": "Float64 array of the curve resampled by linear interpolation from depth + shift back onto the original depth grid, with values beyond the shifted range clamped to the first/last input values." + }, + "apply_integer_shift": { + "params": { + "fill": "Value written into the samples vacated at the exposed end; default NaN.", + "lag": "Integer shift in samples; a positive lag moves samples to greater depth (higher index).", + "log": "1-D curve to be shifted." + }, + "returns": "Float64 array of the same length as the input with all samples moved by lag without wrapping; the vacated end is filled with fill." + }, + "cow": { + "params": { + "n_segments": "Number of piecewise segments; the n_segments - 1 interior boundaries are optimised greedily; default 10.", + "ref": "Reference curve defining the output grid and segment layout.", + "slack": "Maximum boundary displacement in samples (each boundary is tried within +/-slack); default 8.", + "target": "Target curve whose interior segment boundaries are adjusted." + }, + "returns": "Tuple of two float64 arrays (aligned, warp): aligned is the target piecewise-linearly remapped onto the reference grid (same length as ref); warp is the fractional target-index coordinate assigned to each reference sample." + }, + "dtw": { + "params": { + "band": "Sakoe-Chiba band half-width in samples restricting how far the path may stray from the diagonal; None (default) allows the full matrix.", + "cost": "Local cost function: 'sqdiff' (default) uses the squared sample difference, 'absdiff' the absolute difference.", + "ref": "Reference 1-D curve onto which the target is warped.", + "root": "If True, the returned distance is the square root of the accumulated total cost; default False.", + "target": "Target 1-D curve to be aligned to the reference." + }, + "returns": "DtwResult named tuple: distance (float total accumulated cost, square-rooted if root=True), path (list of 0-based (ref_index, target_index) pairs from backtracking), and cost (the (n+1, m+1) accumulated cost matrix as a float64 array)." + }, + "local_shifts": { + "params": { + "max_lag": "Clip limit: each window's estimated lag is clipped to [-max_lag, max_lag] samples; default 20.", + "ref": "Reference 1-D curve.", + "step": "Stride between successive window start positions, in samples; default 30.", + "target": "Target 1-D curve whose local lags relative to the reference are estimated.", + "window": "Sliding-window length in samples over which each local lag is estimated; default 60." + }, + "returns": "Float64 array with one entry per window position: the integer lag (in samples, stored as float) that maximises the mean-removed full cross-correlation of the target window against the reference window." + }, + "path_depth_shifts": { + "params": { + "depth_ref": "Depth coordinates of the reference samples, in any consistent depth unit.", + "depth_target": "Depth coordinates of the target samples, in the same unit as depth_ref.", + "path": "DTW warping path as a list of (ref_index, target_index) pairs." + }, + "returns": "Tuple of two float64 arrays, one entry per path pair: the reference depths depth_ref[i] and the per-depth shifts depth_target[j] - depth_ref[i], in the supplied depth units." + }, + "rddtw": { + "params": { + "band": "Sakoe-Chiba band half-width in samples; None (default) allows the full matrix.", + "lam": "Weight of the off-diagonal penalty lam*|i/n - j/m| that discourages excessive warping; default 0.5.", + "ref": "Reference 1-D curve onto which the target is warped.", + "target": "Target 1-D curve to be aligned to the reference.", + "tau": "Weight on the Keogh-Pazzani derivative (trend) absolute-difference term in the local cost; default 4.0." + }, + "returns": "DtwResult named tuple: distance (float total accumulated cost), path (list of 0-based (ref_index, target_index) pairs), and cost (the accumulated cost matrix as a float64 array)." + }, + "warp_to_reference": { + "params": { + "n_ref": "Length of the reference grid, i.e. the number of output samples.", + "path": "DTW warping path as a list of (ref_index, target_index) pairs, e.g. DtwResult.path.", + "reduce": "How multiply-matched target samples are combined per reference index; only 'mean' is accepted.", + "target": "Target curve whose samples are collapsed onto the reference grid." + }, + "returns": "Float64 array of length n_ref: the target curve aligned to the reference grid, with reference indices absent from the path filled from the nearest populated index." + }, + "xcorr_shift": { + "params": { + "edge": "Edge handling: 'trim' (default) correlates only the overlapping samples at each lag; 'wrap' uses a circular np.roll.", + "max_lag": "Maximum integer lag magnitude scanned, in samples; lags run over [-max_lag, max_lag]; default 50.", + "ref": "Reference 1-D curve.", + "return_curve": "If True, the full correlation-versus-lag curve is included in the result; otherwise the curve field is None.", + "target": "Target 1-D curve to be shifted onto the reference." + }, + "returns": "ShiftResult named tuple: lag (int best lag in samples; a positive lag moves the target to greater depth/index), corr (float Pearson correlation at that lag), and curve (float64 correlation-versus-lag array if return_curve=True, else None)." + }, + "xcorr_shift_depth": { + "params": { + "a": "Curve sampled on depth_a (e.g. core data) that is slid against b.", + "b": "Log curve on depth_b; a single 1-D curve or an (n, k) multi-curve array whose columns are each tried.", + "depth_a": "Depth coordinates of curve a, in physical depth units (e.g. m or ft).", + "depth_b": "Depth coordinates of the log curve(s) b, in the same depth units.", + "max_shift": "Maximum shift magnitude scanned, in the same depth units as the coordinates; default 2.0.", + "step": "Shift increment of the scan, in depth units; default 0.125.", + "use_abs_corr": "If True, the absolute value of the Pearson correlation is maximised (allows anticorrelated curves); default False." + }, + "returns": "Tuple of two floats (best_shift, best_corr): the depth shift (added to depth_a) that maximises the correlation, in depth units, and the signed Pearson correlation achieved at that shift." + } + }, + "petrolib.em_dielectric": { + "anisotropy_coefficient": { + "params": { + "rh": "Horizontal (bedding-parallel) resistivity in ohm.m.", + "rv": "Vertical (bedding-normal) resistivity in ohm.m." + }, + "returns": "Dimensionless resistivity anisotropy coefficient lambda = sqrt(Rv/Rh) (the canonical definition), as a float64 numpy array." + }, + "apparent_conductivity_two_zone": { + "params": { + "r_invasion": "Invasion radius in meters, setting the geometric factor of the invaded zone.", + "sigma_t": "Conductivity of the virgin (uninvaded) formation, in S/m.", + "sigma_xo": "Conductivity of the invaded (flushed) zone, in S/m.", + "spacing_m": "Coil spacing L in meters." + }, + "returns": "Doll two-zone apparent conductivity sigma_a = G_xo*sigma_xo + (1 - G_xo)*sigma_t in S/m, where G_xo is the radial geometric factor at the invasion radius, as a float64 numpy array." + }, + "apparent_resistivity_dip": { + "params": { + "dip_deg": "Relative dip angle theta between the tool axis and the bedding normal, in degrees.", + "rh": "Horizontal resistivity in ohm.m.", + "rv": "Vertical resistivity in ohm.m." + }, + "returns": "Apparent resistivity Rh*sqrt(cos(theta)**2 + (Rv/Rh)*sin(theta)**2) in ohm.m seen by an induction tool at relative dip, as a float64 numpy array." + }, + "attenuation_db": { + "params": { + "freq_hz": "Frequency in Hz.", + "rho": "Formation resistivity in ohm.m.", + "spacing_m": "Receiver spacing L in meters over which the amplitude decays." + }, + "returns": "Induction-limit amplitude attenuation 8.686*L/delta in decibels (nepers converted to dB), as a float64 numpy array." + }, + "attenuation_phase_from_voltages": { + "params": { + "v_far": "Complex voltage measured at the far receiver.", + "v_near": "Complex voltage measured at the near receiver." + }, + "returns": "Tuple of two float64 arrays: the attenuation 20*log10(abs(v_near/v_far)) in dB, and the phase shift of the voltage ratio in degrees." + }, + "bruggeman_symmetric": { + "params": { + "eps_components": "Permittivities of the N phases (real or complex), indexed along the leading axis 0.", + "fractions": "Volume fractions of the N phases, indexed along the leading axis 0.", + "iterations": "Number of Newton iterations used to solve the implicit effective-medium equation; default 60." + }, + "returns": "Symmetric Bruggeman (spherical-grain) effective permittivity solving sum_i f_i*(eps_i - eps_eff)/(eps_i + 2*eps_eff) = 0, as a numpy array; float64 for real inputs, complex128 for complex." + }, + "bvw_from_permittivity": { + "params": { + "alpha": "Power-law mixing exponent; default 0.5 (CRIM).", + "eps_hc": "Real relative permittivity of the hydrocarbon phase (required keyword).", + "eps_matrix": "Real relative permittivity of the rock matrix (required keyword).", + "eps_meas": "Measured rock permittivity (real part), dimensionless.", + "eps_w": "Real relative permittivity of the pore water (required keyword).", + "phi": "Total porosity as a volume fraction." + }, + "returns": "Bulk volume water phi*Sw (fraction of bulk rock volume) from the CRIM inversion, unclipped, as a float64 numpy array." + }, + "cole_cole": { + "params": { + "alpha": "Cole-Cole distribution (broadening) exponent, dimensionless; 0 (the default) recovers the Debye model.", + "eps_inf": "High-frequency permittivity limit, dimensionless.", + "eps_s": "Static (zero-frequency) permittivity, dimensionless.", + "freq_hz": "Frequency (or frequency array) in Hz at which the model is evaluated.", + "tau": "Relaxation time constant in seconds." + }, + "returns": "Complex Cole-Cole permittivity eps_inf + (eps_s - eps_inf)/(1 + (j*omega*tau)**(1-alpha)), as a complex128 numpy array." + }, + "cole_cole_resistivity": { + "params": { + "c": "Frequency exponent of the Cole-Cole dispersion, dimensionless.", + "chargeability": "Pelton chargeability m, dimensionless (0 to 1).", + "freq_hz": "Frequency (or frequency array) in Hz at which the model is evaluated.", + "rho0": "DC (zero-frequency) resistivity rho0 in ohm.m.", + "tau": "Induced-polarization time constant in seconds." + }, + "returns": "Complex resistivity rho(omega) = rho0*(1 - m*(1 - 1/(1 + (j*omega*tau)**c))) in ohm.m, the Pelton induced-polarization form, as a complex128 numpy array." + }, + "complex_permittivity": { + "params": { + "eps_imag": "Dielectric loss eps'' already expressed in relative-permittivity units, added to the imaginary part; default 0.", + "eps_real": "Real part eps' of the relative permittivity (dimensionless).", + "freq_hz": "Frequency in Hz used to form omega = 2*pi*f; required only when sigma is nonzero, otherwise may be None.", + "sigma": "Electrical conductivity in S/m whose ohmic loss sigma/(omega*eps0) is added to the imaginary part; default 0." + }, + "returns": "Complex relative permittivity eps* = eps' - j*(eps'' + sigma/(omega*eps0)) in the -j engineering convention, as a complex128 numpy array (positive conductivity gives a negative imaginary part)." + }, + "complex_wavenumber": { + "params": { + "eps_r": "Real relative permittivity of the medium.", + "freq_hz": "Frequency in Hz.", + "mu_r": "Relative magnetic permeability; default 1.0.", + "sigma": "Electrical conductivity in S/m." + }, + "returns": "Tuple of two float64 arrays (k_r, k_i): the phase constant beta and attenuation constant alpha of the exact lossy-medium wavenumber k = beta - j*alpha, both in 1/m (rad/m and Np/m)." + }, + "crim": { + "params": { + "alpha": "Power-law mixing exponent; 0.5 (default) is the classic CRIM square-root mixing.", + "eps_hc": "Real relative permittivity of the hydrocarbon phase; default 2.2.", + "eps_matrix": "Real relative permittivity of the rock matrix; default 5.0.", + "eps_w": "Real relative permittivity of the pore water; default 78.0.", + "phi": "Total porosity as a volume fraction (v/v).", + "sw": "Water saturation of the pore space, fraction; default 1.0 (fully water-filled)." + }, + "returns": "Effective rock permittivity from the three-component (water/hydrocarbon/matrix) power-law forward model, dimensionless, as a float64 numpy array." + }, + "debye": { + "params": { + "eps_inf": "High-frequency (optical) permittivity limit, dimensionless.", + "eps_s": "Static (zero-frequency) permittivity, dimensionless.", + "freq_hz": "Frequency (or frequency array) in Hz at which the model is evaluated.", + "tau": "Relaxation time constant in seconds." + }, + "returns": "Complex permittivity of the single-pole Debye model, eps_inf + (eps_s - eps_inf)/(1 + j*omega*tau), as a complex128 numpy array over the input frequencies." + }, + "depolarization_spheroid": { + "params": { + "aspect": "Spheroid aspect ratio c/a (polar over equatorial semi-axis): 1 is a sphere, less than 1 oblate (disk), greater than 1 prolate (needle)." + }, + "returns": "Tuple of three float64 arrays (Lx, Ly, Lz): the Osborn depolarization factors, dimensionless and summing to 1, with Lz along the polar symmetry axis and Lx = Ly = (1 - Lz)/2." + }, + "doll_radial_geometric_factor": { + "params": { + "r": "Radial distance from the tool axis to which the geometric factor is integrated, in meters (same units as spacing_m).", + "spacing_m": "Coil (transmitter-receiver) spacing L in meters." + }, + "returns": "Dimensionless integrated Doll radial geometric factor G = r**2/(r**2 + (L/2)**2), between 0 and 1, as a float64 numpy array." + }, + "hanai_bruggeman": { + "params": { + "eps_host": "Permittivity of the continuous host phase; real or complex.", + "eps_incl": "Permittivity of the inclusion phase; real or complex.", + "f_incl": "Volume fraction of the inclusions (0 to 1).", + "iterations": "Number of Newton iterations used to solve the implicit Hanai equation; default 80." + }, + "returns": "Asymmetric (Hanai) Bruggeman effective permittivity solving ((eps_eff - eps_i)/(eps_h - eps_i))*(eps_h/eps_eff)**(1/3) = 1 - f, as a numpy array; float64 for real inputs, complex128 for complex." + }, + "havriliak_negami": { + "params": { + "alpha": "Symmetric broadening exponent applied to (j*omega*tau); default 1.0.", + "beta": "Asymmetric broadening exponent applied to the whole denominator; alpha = beta = 1 recovers the Debye model.", + "eps_inf": "High-frequency permittivity limit, dimensionless.", + "eps_s": "Static (zero-frequency) permittivity, dimensionless.", + "freq_hz": "Frequency (or frequency array) in Hz at which the model is evaluated.", + "tau": "Relaxation time constant in seconds." + }, + "returns": "Complex Havriliak-Negami permittivity eps_inf + (eps_s - eps_inf)/(1 + (j*omega*tau)**alpha)**beta, as a complex128 numpy array." + }, + "imag_permittivity_from_sigma": { + "params": { + "freq_hz": "Frequency in Hz.", + "sigma": "Electrical conductivity in S/m." + }, + "returns": "Loss (imaginary) relative permittivity eps'' = sigma/(2*pi*f*eps0), dimensionless, as a float64 numpy array." + }, + "impedivity": { + "params": { + "eps_r": "Real relative permittivity of the medium (dimensionless).", + "freq_hz": "Frequency in Hz.", + "sigma": "Electrical conductivity in S/m." + }, + "returns": "Complex impedivity 1/(j*omega*eps0*eps_r + sigma), the inverse of the complex admittivity, in ohm.m, as a complex128 numpy array." + }, + "induction_number": { + "params": { + "freq_hz": "Frequency in Hz.", + "rho": "Formation resistivity in ohm.m.", + "spacing_m": "Transmitter-receiver spacing L in meters." + }, + "returns": "Dimensionless induction number L/delta (spacing over skin depth), the near-field versus far-field discriminator, as a float64 numpy array." + }, + "loss_tangent": { + "params": { + "eps_star": "Complex permittivity in the -j loss convention (e.g. the output of complex_permittivity)." + }, + "returns": "Loss tangent tan(delta) = -Im(eps*)/Re(eps*), dimensionless, as a float64 numpy array; positive for lossy media in the -j convention." + }, + "maxwell_garnett": { + "params": { + "depol": "Depolarization factor N of the inclusion shape; default 1/3 (spheres).", + "eps_host": "Permittivity of the continuous host (background) phase; real or complex.", + "eps_incl": "Permittivity of the dispersed inclusions; real or complex.", + "f_incl": "Volume fraction of the inclusions (0 to 1)." + }, + "returns": "Maxwell-Garnett effective permittivity eps_h*(1 + f*ds/(eps_h + N*(1-f)*ds)) with ds = eps_incl - eps_host, as a numpy array; float64 for real inputs, complex128 for complex; the same formula also mixes conductivities." + }, + "mix_power_law": { + "params": { + "alpha": "Lichtenecker-Rother power-law exponent; 0.5 (the default) is the CRIM square-root (time-average) mixing law.", + "eps_components": "Component permittivities (real, or complex to carry loss), indexed along the leading axis 0.", + "fractions": "Volume fractions of the mixture components, indexed along the leading axis 0 and broadcastable with eps_components." + }, + "returns": "Effective mixture permittivity (sum_i f_i*eps_i**alpha)**(1/alpha), summed over axis 0, as a numpy array; float64 for real inputs, complex128 when the components are complex." + }, + "phase_shift_deg": { + "params": { + "freq_hz": "Frequency in Hz.", + "rho": "Formation resistivity in ohm.m.", + "spacing_m": "Receiver spacing L in meters over which the phase shift accumulates." + }, + "returns": "Induction-limit phase shift over the spacing, degrees(L/delta), in degrees, as a float64 numpy array." + }, + "resistivity_from_phase": { + "params": { + "freq_hz": "Frequency in Hz.", + "phase_deg": "Measured phase shift between receivers, in degrees.", + "spacing_m": "Receiver spacing L in meters." + }, + "returns": "Apparent resistivity rho = L**2*omega*mu0/(2*phase_rad**2) in ohm.m, the induction-limit inverse of phase_shift_deg, as a float64 numpy array." + }, + "sigma_eps_from_wavenumber": { + "params": { + "freq_hz": "Frequency in Hz.", + "k_i": "Attenuation constant alpha of the wavenumber, in 1/m.", + "k_r": "Phase constant beta of the wavenumber, in 1/m.", + "mu_r": "Relative magnetic permeability; default 1.0." + }, + "returns": "Tuple of two float64 arrays (sigma, eps_r): the conductivity in S/m and the real relative permittivity recovered in closed form from k**2 = omega**2*mu*eps*." + }, + "sigma_from_imag_permittivity": { + "params": { + "eps_imag": "Loss (imaginary) relative permittivity eps'', dimensionless.", + "freq_hz": "Frequency in Hz." + }, + "returns": "Equivalent electrical conductivity sigma = eps''*2*pi*f*eps0 in S/m, as a float64 numpy array." + }, + "skin_depth": { + "params": { + "freq_hz": "Frequency in Hz.", + "mu_r": "Relative magnetic permeability of the medium; default 1.0.", + "rho": "Formation resistivity in ohm.m." + }, + "returns": "Electromagnetic skin depth delta = sqrt(2*rho/(omega*mu0*mu_r)) in meters, as a float64 numpy array." + }, + "sw_from_permittivity": { + "params": { + "alpha": "Power-law mixing exponent; default 0.5 (CRIM).", + "clip": "If True (default) the computed saturation is clipped to the physical interval [0, 1]; False returns the raw value.", + "eps_hc": "Real relative permittivity of the hydrocarbon phase (required keyword).", + "eps_matrix": "Real relative permittivity of the rock matrix (required keyword).", + "eps_meas": "Measured rock permittivity (real part), dimensionless.", + "eps_w": "Real relative permittivity of the pore water (required keyword).", + "phi": "Total porosity as a volume fraction." + }, + "returns": "Water saturation Sw (fraction of pore volume) from the closed-form CRIM inversion, as a float64 numpy array; bounded to [0, 1] when clip=True." + }, + "water_filled_porosity": { + "params": { + "alpha": "Power-law mixing exponent; default 0.5 (CRIM).", + "clip": "If True (default) the computed porosity is clipped to [0, 1]; False returns the raw value.", + "eps_matrix": "Real relative permittivity of the rock matrix (required keyword).", + "eps_meas": "Measured rock permittivity (real part), dimensionless.", + "eps_w": "Real relative permittivity of the pore water (required keyword)." + }, + "returns": "Water-filled porosity phi_w (fraction of bulk volume) from the two-component water/matrix CRIM inversion, as a float64 numpy array; bounded to [0, 1] when clip=True." + }, + "water_permittivity": { + "params": { + "eps_dl": "Extra dielectric-loss (double-layer) term added to the imaginary part, in relative-permittivity units; default 15.0.", + "eps_real": "Real relative permittivity of the water, dimensionless; default 55.0.", + "freq_hz": "Frequency in Hz (GHz-band dielectric logging frequencies).", + "rw": "Brine (formation-water) resistivity in ohm.m; the brine conductivity is taken as sigma_w = 1/rw." + }, + "returns": "Complex relative permittivity of the brine, eps_real - j*(eps_dl + sigma_w/(omega*eps0)), in the -j convention, as a complex128 numpy array." + } + }, + "petrolib.flow_transport": { + "advect_disperse_1d": { + "params": { + "D": "Dispersion coefficient (SI: m2/s), applied with a central-difference scheme.", + "c_in": "Fixed inflow concentration held at the upstream boundary x = 0; also the upper clip limit for the solution.", + "k_rxn": "First-order reaction rate constant removing solute (1/s); default 0.0 for no reaction.", + "length": "Length of the one-dimensional domain (SI: m).", + "n_cells": "Number of finite-difference grid cells discretizing the domain.", + "n_steps": "Optional explicit number of time steps; None (default) uses the CFL/diffusion-stable step dt = 0.4*min(dx/v, dx**2/(2*D)).", + "t_total": "Total simulated time at which the profile is returned (SI: s).", + "v": "Advection (pore) velocity of the fluid (SI: m/s), applied with an upwind scheme." + }, + "returns": "Tuple (x, C) of numpy float64 arrays, each of length n_cells: the cell-centre coordinates on [0, length] and the concentration profile at t_total, clipped to the range [0, c_in]." + }, + "classify_hfu": { + "params": { + "fzi_values": "Array of per-sample Flow Zone Indicator values; values are floored at 1e-6 before taking log10.", + "n_units": "Number of hydraulic flow units to define via log-spaced FZI thresholds; default 4." + }, + "returns": "Tuple (labels, thresholds): an integer numpy array of unit labels in [0, n_units-1] for each sample, and a numpy float64 array of the n_units+1 FZI threshold values, log-spaced between the minimum and maximum FZI." + }, + "darcy_gas_permeability": { + "params": { + "area": "Cross-sectional flow area of the sample (SI: m2).", + "length": "Length of the sample along the flow direction (SI: m).", + "mu": "Gas dynamic viscosity (SI: Pa.s).", + "p_down": "Downstream (outlet) absolute pressure (SI: Pa).", + "p_ref": "Reference absolute pressure at which the rate q is measured, e.g. outlet or atmospheric (SI: Pa).", + "p_up": "Upstream (inlet) absolute pressure (SI: Pa).", + "q": "Volumetric gas flow rate measured at the reference pressure p_ref (SI: m3/s)." + }, + "returns": "Compressible gas permeability from the pressure-squared form k = 2*q*mu*L*P_ref/(A*(P_up**2 - P_down**2)), a numpy float64 array; m2 with SI inputs." + }, + "darcy_permeability": { + "params": { + "area": "Cross-sectional flow area of the sample (SI: m2).", + "dp": "Pressure drop across the sample (SI: Pa).", + "length": "Length of the sample along the flow direction (SI: m).", + "mu": "Dynamic viscosity of the flowing liquid (SI: Pa.s).", + "q": "Volumetric flow rate of the liquid through the core plug, in any consistent unit set (SI: m3/s)." + }, + "returns": "Darcy permeability k = q*mu*L/(A*dP) as a numpy float64 array; m2 with SI inputs, or whatever the chosen consistent unit set implies." + }, + "darcy_pressure_drop": { + "params": { + "area": "Cross-sectional flow area of the sample (SI: m2).", + "k": "Absolute permeability of the sample (SI: m2).", + "kr": "Dimensionless relative permeability of the flowing phase multiplying k; 1.0 (default) for single-phase flow.", + "length": "Length of the sample along the flow direction (SI: m).", + "mu": "Dynamic viscosity of the flowing phase (SI: Pa.s).", + "q": "Volumetric flow rate through the sample (SI: m3/s)." + }, + "returns": "Pressure drop dP = q*mu*L/(k*kr*A) across the sample as a numpy float64 array; Pa with SI inputs." + }, + "darcy_rate": { + "params": { + "area": "Cross-sectional flow area of the sample (SI: m2).", + "dp": "Pressure drop across the sample (SI: Pa).", + "k": "Permeability of the sample (SI: m2).", + "length": "Length of the sample along the flow direction (SI: m).", + "mu": "Dynamic viscosity of the flowing fluid (SI: Pa.s)." + }, + "returns": "Volumetric Darcy flow rate q = k*A*dP/(mu*L) as a numpy float64 array; m3/s with SI inputs. The inverse of darcy_permeability." + }, + "diffusion_length": { + "params": { + "D": "Molecular diffusion coefficient (SI: m2/s).", + "geometry_factor": "Dimensionless factor f in L = sqrt(f*D*t); 1.0 (default) in most articles, 2 for the sqrt(2*D*t) displacement form.", + "t": "Elapsed diffusion time (SI: s)." + }, + "returns": "Characteristic diffusion length L = sqrt(f*D*t) as a numpy float64 array; metres with SI inputs." + }, + "diffusion_time": { + "params": { + "D": "Molecular diffusion coefficient (SI: m2/s).", + "L": "Diffusion distance or characteristic length (SI: m).", + "geometry_factor": "Dimensionless factor f in t = L**2/(f*D); default 1.0, 2 for the displacement form, pi**2 for a sealed-column mixing time." + }, + "returns": "Diffusion or equilibration time t = L**2/(f*D) as a numpy float64 array; seconds with SI inputs. The inverse of diffusion_length." + }, + "early_time_uptake": { + "params": { + "D": "Diffusion coefficient (SI: m2/s).", + "half_length": "Half-length L of the slab into which the species diffuses (SI: m).", + "t": "Elapsed time(s) since exposure began (SI: s)." + }, + "returns": "Dimensionless early-time fractional uptake Mt/Minf = (2/L)*sqrt(D*t/pi) as a numpy float64 array, valid up to roughly 0.6." + }, + "erfc_profile": { + "params": { + "D": "Diffusion coefficient (SI: m2/s).", + "c0": "Fixed boundary concentration held at x = 0; sets the units of the result.", + "t": "Elapsed time since the boundary concentration was imposed (SI: s).", + "x": "Distance(s) from the boundary into the semi-infinite medium (SI: m)." + }, + "returns": "Semi-infinite diffusion concentration profile C(x,t) = C0*erfc(x/(2*sqrt(D*t))) as a numpy float64 array, in the units of c0 (far-field value 0). Uses scipy.special.erfc, imported lazily." + }, + "fick_flux": { + "params": { + "D": "Diffusion coefficient (SI: m2/s).", + "dc": "Concentration difference across the distance dx, in the caller's concentration units.", + "dx": "Distance over which the concentration difference applies (SI: m)." + }, + "returns": "Steady diffusive flux J = -D*dc/dx from Fick's first law, as a numpy float64 array in the concentration unit times m/s with SI inputs; the sign opposes the concentration gradient." + }, + "fit_klinkenberg": { + "params": { + "k_app": "Array of measured apparent gas permeabilities, one per mean pressure.", + "p_mean": "Array of mean gas pressures at which the apparent permeabilities were measured." + }, + "returns": "Tuple (k_inf, b) of Python floats from a straight-line fit of k_app versus 1/P: the intercept is the intrinsic permeability k_inf (units of k_app) and b = slope/intercept is the slip factor (units of p_mean)." + }, + "fit_poroperm": { + "params": { + "k_md": "Array of permeabilities in millidarcy paired with phi.", + "phi": "Array of porosity fractions of the calibration samples." + }, + "returns": "Tuple (a, b) of Python floats, the intercept and slope of the least-squares fit of log10(k) on log10(phi), directly usable in poroperm_powerlaw." + }, + "fit_stress_permeability": { + "params": { + "k": "Array of measured permeabilities, one per stress point.", + "ncs": "Array of net confining stresses at which permeability was measured." + }, + "returns": "Tuple (k0, gamma) of Python floats from a line fit in (NCS, ln k) space: k0 = exp(intercept) in the units of k, gamma = -slope in 1/stress units." + }, + "flow_regime": { + "params": { + "cuts": "Three ascending Knudsen-number cutoffs separating continuum/slip, slip/transition, and transition/free-molecular regimes; default (1e-3, 0.1, 10.0).", + "kn": "Knudsen number of the gas flow, a scalar float." + }, + "returns": "Regime name as a string: \"continuum\" for Kn below cuts[0], \"slip\" below cuts[1], \"transition\" below cuts[2], otherwise \"free-molecular\"." + }, + "fzi": { + "params": { + "c": "RQI unit-conversion constant passed through to rqi; default 0.0314.", + "k_md": "Permeability in millidarcy.", + "phi": "Porosity as a fraction." + }, + "returns": "Amaefule Flow Zone Indicator FZI = RQI/phi_z in micrometres, as a numpy float64 array." + }, + "k_from_fzi": { + "params": { + "c": "RQI unit constant, which must match the value used when computing FZI; default 0.0314.", + "fzi_val": "Flow Zone Indicator value(s) in micrometres.", + "phi": "Porosity as a fraction." + }, + "returns": "Permeability in millidarcy k = fzi**2*phi**3/(c**2*(1-phi)**2), as a numpy float64 array; the inverse of fzi for a shared c." + }, + "klinkenberg_apparent": { + "params": { + "b": "Klinkenberg gas-slip factor, in the same pressure units as p_mean.", + "c2": "Optional second-order slip coefficient (units of pressure squared); default 0.0 gives the first-order Klinkenberg law.", + "k_inf": "Intrinsic (infinite-pressure, liquid-equivalent) permeability; sets the units of the result.", + "p_mean": "Mean gas pressure of the measurement, sharing units with b." + }, + "returns": "Apparent gas permeability k_inf*(1 + b/P + c2/P**2) as a numpy float64 array, in the units of k_inf." + }, + "klinkenberg_corrected": { + "params": { + "b": "Klinkenberg gas-slip factor, in the same pressure units as p_mean.", + "k_app": "Measured apparent gas permeability to be corrected back to the intrinsic value.", + "p_mean": "Mean gas pressure of the measurement, sharing units with b." + }, + "returns": "Liquid-equivalent intrinsic permeability k_inf = k_app/(1 + b/P) as a numpy float64 array, in the units of k_app. The explicit inverse of klinkenberg_apparent, with no second-order term." + }, + "knudsen_number": { + "params": { + "mfp": "Gas mean free path, in the same length units as pore_size.", + "pore_size": "Characteristic pore length; pore radius in some source articles and diameter in others, a factor-2 choice owned by the caller." + }, + "returns": "Dimensionless Knudsen number Kn = lambda/L_pore as a numpy float64 array." + }, + "kozeny_carman": { + "params": { + "c": "Kozeny constant, typically about 2 to 5; default 5.0.", + "grain_term": "If True (default) the denominator includes the grain-volume factor (1-phi)**2 (classic form); False omits it.", + "phi": "Porosity as a fraction.", + "specific_surface": "Specific surface area S per unit volume, in 1/m.", + "tau": "Tortuosity, entering the denominator as tau**2; default 1.0." + }, + "returns": "Kozeny-Carman permeability k = phi**3/(c*tau**2*S**2) with an optional (1-phi)**2 grain factor, as a numpy float64 array in m2." + }, + "kozeny_carman_ratio": { + "params": { + "grain_term": "If True (default) applies the grain-volume factor ((1-phi0)/(1-phi))**2; False gives the simplified cubic ratio k0*(phi/phi0)**3.", + "k0": "Reference permeability measured at the reference porosity phi0; sets the units of the result.", + "phi": "Updated porosity fraction(s) at which to evaluate the permeability.", + "phi0": "Reference porosity fraction at which k0 was measured." + }, + "returns": "Updated permeability k = k0*(phi/phi0)**3, multiplied by the grain factor when enabled, as a numpy float64 array in the units of k0." + }, + "lucia_permeability": { + "params": { + "phi_g": "Interparticle porosity as a fraction.", + "rfn": "Lucia rock-fabric number of the sample." + }, + "returns": "Permeability in millidarcy from the Lucia (2007) rock-fabric-number transform, as a numpy float64 array." + }, + "lucia_rfn_from_swi": { + "params": { + "phi_g": "Interparticle porosity as a fraction.", + "swi": "Irreducible water saturation as a fraction." + }, + "returns": "Lucia rock-fabric number from the inverted log10 relation, as a numpy float64 array (dimensionless)." + }, + "mean_free_path": { + "params": { + "d_collision": "Molecular collision diameter (m); supplying it selects the kinetic-theory form kB*T/(sqrt(2)*pi*d**2*P), otherwise leave None.", + "molar_mass": "Gas molar mass in kg/mol; supply together with mu for the viscosity form, otherwise leave None.", + "mu": "Gas dynamic viscosity (Pa.s); supply together with molar_mass to select the viscosity form, otherwise leave None.", + "pressure": "Gas absolute pressure (SI: Pa).", + "temperature": "Absolute gas temperature (SI: K)." + }, + "returns": "Gas mean free path as a numpy float64 array, in metres with SI inputs. Raises ValueError if neither (mu, molar_mass) nor d_collision is supplied." + }, + "micp_apex": { + "params": { + "pc": "Array of injection capillary pressures paired sample-by-sample with shg.", + "shg": "Array of bulk mercury saturations along the MICP injection curve." + }, + "returns": "Tuple (apex_ratio, index): the maximum Shg/Pc ratio as a numpy float64 array scalar and the integer index at which it occurs. The apex value feeds swanson_permeability." + }, + "millington_quirk": { + "params": { + "D0": "Free (bulk-fluid) diffusion coefficient (SI: m2/s); sets the units of the result.", + "phi": "Porosity as a fraction.", + "sw": "Saturation of the diffusing phase as a fraction; default 1.0 for a fully saturated pore space." + }, + "returns": "Millington-Quirk effective diffusivity D_eff = (phi*Sw)**(10/3)/phi**2 * D0 (equivalent to D0*phi**(4/3)*Sw**(10/3)) as a numpy float64 array, in the units of D0." + }, + "net_confining_stress": { + "params": { + "biot": "Biot effective-stress coefficient multiplying the pore pressure; default 1.0 gives the Terzaghi form.", + "pore_pressure": "Pore pressure inside the sample, in the same units as total_stress.", + "total_stress": "Total confining stress applied to the sample." + }, + "returns": "Net (effective) confining stress sigma_eff = sigma_total - biot*Pp as a numpy float64 array, in the stress units of the inputs." + }, + "permeability_average": { + "params": { + "k": "Array of permeability values to average.", + "method": "Averaging rule: \"arithmetic\" (parallel-flow bound), \"harmonic\" (series flow), or \"geometric\" (default); other values raise ValueError.", + "weights": "Optional thickness or volume weights for a weighted mean; None (default) gives the unweighted average." + }, + "returns": "The averaged permeability as a scalar Python float, in the units of k." + }, + "phi_z": { + "params": { + "phi": "Porosity as a fraction." + }, + "returns": "Normalized porosity index phi_z = phi/(1 - phi), a dimensionless numpy float64 array." + }, + "pore_velocity": { + "params": { + "phi": "Porosity as a fraction.", + "sw": "Saturation of the flowing phase as a fraction; default 1.0.", + "u_darcy": "Darcy (superficial) velocity or flux (SI: m/s); sets the units of the result." + }, + "returns": "Interstitial (pore) velocity v = u_darcy/(phi*Sw) as a numpy float64 array, in the units of u_darcy." + }, + "poroperm_powerlaw": { + "params": { + "a": "Intercept of the log10(k) versus log10(phi) trend.", + "b": "Slope of the log10(k) versus log10(phi) trend.", + "phi": "Porosity as a fraction." + }, + "returns": "Permeability k = 10**(a + b*log10(phi)) in millidarcy, as a numpy float64 array." + }, + "rqi": { + "params": { + "c": "Unit-conversion constant; default 0.0314 = sqrt(1/1014) converts millidarcy to micrometres.", + "k_md": "Permeability in millidarcy.", + "phi": "Porosity as a fraction." + }, + "returns": "Amaefule Reservoir Quality Index RQI = c*sqrt(k/phi) in micrometres, as a numpy float64 array." + }, + "stokes_einstein": { + "params": { + "T_K": "Absolute temperature in kelvin.", + "mu": "Dynamic viscosity of the surrounding fluid (SI: Pa.s).", + "radius": "Hydrodynamic radius of the diffusing sphere (SI: m)." + }, + "returns": "Stokes-Einstein diffusivity D = kB*T/(6*pi*mu*r) as a numpy float64 array; m2/s with SI inputs." + }, + "stress_permeability": { + "params": { + "gamma": "Exponential stress-sensitivity decline coefficient, in reciprocal units of the stress (1/stress).", + "k0": "Permeability at the reference net confining stress ncs0; sets the units of the result.", + "ncs": "Net confining stress at which the permeability is evaluated, same units as ncs0.", + "ncs0": "Reference net confining stress at which k0 is defined; default 0.0 gives the plain k0*exp(-gamma*NCS) form." + }, + "returns": "Stress-declined permeability k = k0*exp(-gamma*(NCS - NCS0)) as a numpy float64 array, in the units of k0." + }, + "swanson_permeability": { + "params": { + "c": "Swanson correlation prefactor; default 399.0 (Wolfcamp copy), pass 339 for the stress-MICP copy.", + "d": "Swanson correlation exponent; default 1.691.", + "sb_pc_apex": "Maximum ratio of bulk mercury saturation to injection capillary pressure at the MICP apex." + }, + "returns": "Swanson (1981) permeability k = c*(Sb/Pc)_apex**d in millidarcy, as a numpy float64 array." + }, + "wiener_bounds": { + "params": { + "k": "Array of permeability values of the heterogeneous medium." + }, + "returns": "Tuple (k_harmonic, k_arithmetic) of Python floats: the Wiener series (harmonic) lower bound and parallel (arithmetic) upper bound on the effective permeability, in the units of k." + }, + "winland_permeability": { + "params": { + "phi": "Porosity as a fraction; converted to percent internally by the correlation.", + "r35": "Winland r35 pore-throat radius in micrometres." + }, + "returns": "Permeability in millidarcy from the inverted Winland correlation, as a numpy float64 array." + }, + "winland_r35": { + "params": { + "k_md": "Permeability in millidarcy.", + "phi": "Porosity as a fraction; multiplied by 100 internally because the correlation is defined in percent." + }, + "returns": "Winland-Kolodzie r35 pore-throat radius at 35 percent mercury saturation, in micrometres, as a numpy float64 array." + } + }, + "petrolib.geochem_fluids.adsorption": { + "bet_fit": { + "params": { + "cross_nm2": "Molecular cross-sectional area of the adsorbate in nm2; default 0.162 is the N2 value.", + "v_ads": "Array of measured adsorbed volumes at each relative pressure, in cm3(STP)/g.", + "x_rel": "Array of relative pressures x = P/P0 for the measured isotherm points." + }, + "returns": "Tuple of three scalar floats (Vm, C, SSA): the monolayer volume Vm in cm3(STP)/g, the dimensionless BET C constant, and the specific surface area in m2/g, from a least-squares fit of the linearized BET equation." + }, + "bet_isotherm": { + "params": { + "c": "BET C constant (dimensionless), governing the affinity of the first adsorbed layer.", + "vm": "Monolayer adsorbed volume Vm, in cm3(STP)/g.", + "x_rel": "Relative pressure x = P/P0 (dimensionless, 0 to 1)." + }, + "returns": "Adsorbed gas volume V = Vm*C*x/((1-x)*(1+(C-1)*x)) in the same unit as vm (cm3(STP)/g), as a numpy float64 array." + }, + "free_gas": { + "params": { + "bg": "Gas formation-volume factor Bg (reservoir volume per standard volume).", + "phi": "Porosity as a fraction of bulk volume.", + "sw": "Water saturation as a fraction of pore volume." + }, + "returns": "Free-gas volume at standard conditions per unit bulk volume, phi*(1-Sw)/Bg, as a numpy float64 array (dimensionless volume ratio)." + }, + "gas_in_place": { + "params": { + "area_m2": "Reservoir drainage area in m2.", + "bg": "Gas formation-volume factor Bg (reservoir volume per standard volume).", + "h_m": "Net reservoir thickness in m.", + "phi": "Porosity as a fraction of bulk volume.", + "sg": "Gas saturation as a fraction of pore volume." + }, + "returns": "Volumetric free gas-in-place A*h*phi*Sg/Bg as a numpy float64 array; with the SI inputs this is a standard-condition gas volume in m3." + }, + "gibbs_excess": { + "params": { + "gc": "Absolute adsorbed gas content Gc to be corrected, e.g. a Langmuir gas content (unit carried through unchanged).", + "rho_ads": "Density of the adsorbed gas phase, in the same unit as rho_free.", + "rho_free": "Density of the free (bulk) gas phase, in the same unit as rho_ads." + }, + "returns": "Gibbs excess adsorbed content Gc*(1 - rho_free/rho_adsorbed), in the same unit as gc, as a numpy float64 array." + }, + "langmuir": { + "params": { + "p": "Gas pressure, in the same unit as the Langmuir pressure p_l.", + "p_l": "Langmuir pressure P_L, the pressure at which half the Langmuir capacity is adsorbed, same unit as p.", + "rho_b": "Optional rock bulk density; when given the isotherm is multiplied by it so the result is sorbed gas content per bulk volume instead of per unit mass.", + "v_l": "Langmuir volume V_L, the maximum adsorbed gas capacity, in cm3(STP)/g." + }, + "returns": "Adsorbed gas volume V_L*P/(P+P_L) in cm3(STP)/g as a numpy float64 array, or the bulk-density-scaled sorbed gas content when rho_b is supplied." + } + }, + "petrolib.geochem_fluids.asphaltene": { + "diameter_from_molar_volume": { + "params": { + "va_m3mol": "Asphaltene molar volume Va in m3/mol." + }, + "returns": "Equivalent sphere diameter (6*(Va/NA)/pi)**(1/3) in m as a numpy float64 array; inverse of molar_volume_from_diameter." + }, + "fhz_invert_molar_volume": { + "params": { + "delta_rho": "Density contrast rho_asphaltene minus rho_oil in kg/m3.", + "od1": "Optical density measured at depth z1.", + "od2": "Optical density measured at depth z2.", + "temp_k": "Reservoir temperature in K.", + "z1": "Depth of the first measurement in m (positive downward).", + "z2": "Depth of the second measurement in m (positive downward)." + }, + "returns": "Asphaltene molar volume Va = R*T*ln(OD2/OD1)/(g*delta_rho*(z2-z1)) in m3/mol, as a numpy float64 array; inverts the gravity-only FHZ ratio." + }, + "fhz_profile": { + "params": { + "delta_rho": "Density contrast rho_asphaltene minus rho_oil in kg/m3.", + "depth_m": "Depths at which to evaluate the optical-density profile, in m (positive downward).", + "depth_ref": "Reference depth in m at which od_ref is measured.", + "od_ref": "Optical density (asphaltene concentration proxy) at the reference depth.", + "temp_k": "Reservoir temperature in K.", + "va_m3mol": "Asphaltene molar volume Va in m3/mol." + }, + "returns": "Gravity-only FHZ optical-density profile OD(z) = OD_ref * fhz_ratio(z - z_ref), in the same unit as od_ref, as a numpy float64 array." + }, + "fhz_ratio": { + "params": { + "delta_rho": "Density contrast rho_asphaltene minus rho_oil in kg/m3.", + "dsol2_pa": "Optional solubility-parameter-squared difference between the two depths in Pa, entering as -Va*dsol2/(R*T); defaults to 0.", + "dz_m": "Depth separation between the two points in m, positive downward, so a positive dz gives a ratio greater than 1.", + "entropy": "Optional dimensionless entropy term added inside the exponent; defaults to 0 (gravity-only form).", + "temp_k": "Reservoir temperature in K.", + "va_m3mol": "Asphaltene molar volume Va in m3/mol." + }, + "returns": "Flory-Huggins-Zuo concentration (optical-density) ratio OD(z+dz)/OD(z), dimensionless, as a numpy float64 array." + }, + "molar_volume_from_diameter": { + "params": { + "d_m": "Asphaltene particle (sphere) diameter in m." + }, + "returns": "Molar volume of the spherical particle, (pi/6)*d**3 times Avogadro's number, in m3/mol as a numpy float64 array." + }, + "nearest_yen_mullins": { + "params": { + "d_m": "Particle diameter in m (scalar) to classify against the Yen-Mullins hierarchy.", + "rtol": "Relative tolerance (default 0.25) within which the diameter is considered to agree with the nearest class." + }, + "returns": "Tuple (name, ref_d, agrees): the nearest Yen-Mullins class name (molecule, nanoaggregate, or cluster), its nominal reference diameter in m as a float, and a bool that is True when the input is within rtol of that reference." + } + }, + "petrolib.geochem_fluids.brine": { + "arps_correct": { + "params": { + "r1": "Known resistivity at temperature t1, in ohm-m.", + "t1": "Temperature at which r1 is known, in the unit selected by unit.", + "t2": "Target temperature to correct the resistivity to, same unit as t1.", + "unit": "Temperature unit, 'F' (Arps constant 6.77) or 'C' (constant 21.5); any other value raises ValueError." + }, + "returns": "Resistivity at t2, R2 = R1*(T1+c)/(T2+c), in ohm-m as a numpy float64 array." + }, + "brine_density_bw92": { + "params": { + "nacl_ppm": "NaCl salinity in ppm, converted internally to a weight fraction S = ppm/1e6.", + "press_mpa": "Pressure in MPa (default 0.1, atmospheric); contributes a small linear term of 0.045*(P-0.1) g/cm3 equivalent.", + "temp_c": "Temperature in degrees Celsius." + }, + "returns": "Simplified Batzle-Wang (1992) brine density in kg/m3 as a numpy float64 array: pure-water density plus an NaCl salinity term plus a small linear pressure term." + }, + "nacl_meq_per_liter": { + "params": { + "nacl_ppm": "NaCl-equivalent salinity in ppm." + }, + "returns": "NaCl concentration in milliequivalents per liter, ppm/58.44 (assumes a monovalent salt and brine density near 1 g/cm3), as a numpy float64 array." + }, + "rw75_from_salinity": { + "params": { + "nacl_ppm": "NaCl-equivalent salinity in ppm." + }, + "returns": "Bateman-Konen water resistivity at 75 degF, R75 = 0.0123 + 3647.5/C**0.955, in ohm-m as a numpy float64 array." + }, + "rw_from_salinity": { + "params": { + "nacl_ppm": "NaCl-equivalent salinity in ppm.", + "temp": "Formation temperature at which the resistivity is wanted, in the unit selected by unit.", + "unit": "Temperature unit of temp, 'F' (default) or 'C'; Celsius input is converted to Fahrenheit before the Arps correction from the 75 degF reference." + }, + "returns": "Water resistivity at the given temperature in ohm-m as a numpy float64 array (Bateman-Konen R75 followed by the Arps temperature correction)." + }, + "salinity_from_rw": { + "params": { + "rw": "Water resistivity at temperature temp, in ohm-m.", + "temp": "Temperature at which rw is measured, in the unit selected by unit.", + "unit": "Temperature unit of temp, 'F' (default) or 'C'; Celsius input is converted to Fahrenheit first." + }, + "returns": "NaCl-equivalent salinity in ppm as a numpy float64 array; inverse of rw_from_salinity (Arps-corrects rw to 75 degF, then inverts Bateman-Konen)." + }, + "salinity_from_rw75": { + "params": { + "rw75_ohmm": "Water resistivity at 75 degF in ohm-m." + }, + "returns": "NaCl-equivalent salinity in ppm as a numpy float64 array; exact algebraic inverse of rw75_from_salinity." + }, + "sigma_w_from_salinity": { + "params": { + "nacl_ppm": "NaCl-equivalent salinity in ppm, converted internally to a weight fraction w = ppm/1e6.", + "temp_c": "Brine temperature in degrees Celsius (default 24.0), entering the linear correction factor (1 - 8e-4*(T - 75))." + }, + "returns": "Thermal-neutron capture cross-section of the brine, Sigma_w = (22 + 750*w)*(1 - 8e-4*(T-75)), in capture units (c.u.) as a numpy float64 array; 22 c.u. is the fresh-water baseline." + } + }, + "petrolib.geochem_fluids.contamination": { + "cleanup_powerlaw": { + "params": { + "eta0": "Initial contamination fraction at zero pumped volume.", + "exponent": "Power-law decay exponent; the default 5/12 is the wireline-cleanup value, 2/3 applies to radial flow.", + "v": "Cumulative pumped volume, in the same (arbitrary) unit as v_star.", + "v_star": "Characteristic cleanup volume setting the scale of the power-law decline, same unit as v." + }, + "returns": "Contamination fraction after pumping, eta(V) = eta0*(1 + V/V_star)**(-exponent), dimensionless, as a numpy float64 array." + }, + "contamination_fraction": { + "params": { + "p": "Measured property of the sampled (mixed) fluid, same unit as the endpoints.", + "p_f": "Property value of the pure filtrate endpoint.", + "p_v": "Property value of the virgin formation fluid endpoint." + }, + "returns": "Contamination fraction eta = (P - P_v)/(P_f - P_v), dimensionless, as a numpy float64 array; inverse of the linear mixing rule." + }, + "mix_linear": { + "params": { + "eta": "Filtrate (contamination) volume fraction of the mixture, 0 to 1.", + "p_f": "Property value of the pure mud filtrate, same unit as p_v.", + "p_v": "Property value of the virgin (uncontaminated) formation fluid, any unit." + }, + "returns": "Linearly mixed property P = (1-eta)*P_virgin + eta*P_filtrate, in the same unit as the endpoints, as a numpy float64 array." + }, + "volume_to_target": { + "params": { + "eta0": "Initial contamination fraction at zero pumped volume.", + "eta_t": "Target contamination fraction to be reached.", + "exponent": "Power-law decay exponent of the cleanup model; default 5/12 (wireline), 2/3 for radial.", + "v_star": "Characteristic cleanup volume of the power-law model; sets the unit of the returned volume." + }, + "returns": "Pumped volume required to reach the target contamination, V = V_star*((eta0/eta_t)**(1/exponent) - 1), in the unit of v_star, as a numpy float64 array." + } + }, + "petrolib.geochem_fluids.core_geochem": { + "dean_stark": { + "params": { + "v_bulk": "Optional bulk volume of the core sample; when omitted the returned porosity is NaN.", + "v_hc": "Volume of hydrocarbon recovered, same unit as v_water.", + "v_pore": "Optional independently measured pore volume; when omitted the pore volume defaults to v_water + v_hc.", + "v_water": "Volume of water recovered from the core by Dean-Stark extraction (any consistent volume unit)." + }, + "returns": "Tuple of three numpy float64 arrays (phi, Sw, S_hc): porosity pore/v_bulk (NaN where v_bulk is not given), water saturation v_water/pore, and hydrocarbon saturation v_hc/pore, all as fractions." + }, + "osi": { + "params": { + "s1_mg_g": "Rock-Eval S1 free-hydrocarbon yield in mg HC per g rock.", + "toc_wt_pct": "Total organic carbon in weight percent." + }, + "returns": "Jarvie oil saturation index OSI = 100*S1/TOC in mg HC per g TOC, as a numpy float64 array; values above ~100 indicate producible oil." + }, + "oxide_closure": { + "params": { + "axis": "Array axis along which the suite is summed and normalized (default -1, the last axis).", + "oxides": "Oxide (or elemental) weight fractions of the suite, arranged with the components along axis." + }, + "returns": "Tuple (closed, factor) of numpy float64 arrays: the suite rescaled to sum to one along axis, and the closure factor 1/sum(oxides) with the summed axis removed." + } + }, + "petrolib.geochem_fluids.gradients": { + "density_from_gradient": { + "params": { + "dpdz": "Pressure gradient value(s), expressed in the unit named by p_unit; no clipping is applied, so a negative gradient yields a negative density.", + "p_unit": "Gradient unit switch: 'Pa' (Pa/m, default), 'psi/ft', or 'bar/m'; any other value raises ValueError." + }, + "returns": "Fluid density rho = gradient_SI / g (standard gravity 9.80665 m/s2) in kg/m3 as a numpy float64 array." + }, + "fit_pressure_gradient": { + "params": { + "depth_m": "Measurement depths in m.", + "p": "Formation pressures at each depth, in Pa (SI module convention)." + }, + "returns": "Tuple of two scalar floats (slope, intercept): the least-squares pressure gradient dP/dz in pressure unit per m, and the zero-depth intercept P0 in the pressure unit." + }, + "fluid_contact": { + "params": { + "depth_a": "Depths of the pressure points in the first fluid column, in m.", + "depth_b": "Depths of the pressure points in the second fluid column, in m.", + "p_a": "Pressures of the first fluid column at depth_a, in Pa.", + "p_b": "Pressures of the second fluid column at depth_b, in Pa." + }, + "returns": "Scalar float contact depth in m where the two least-squares pressure-depth lines intersect (e.g. a GOC or OWC)." + } + }, + "petrolib.geochem_fluids.mudgas": { + "apply_eec": { + "params": { + "alphas": "Per-component extraction-efficiency coefficients; each component is divided by its alpha.", + "comps": "Raw measured component readings to be corrected (any consistent unit)." + }, + "returns": "Extraction-efficiency-corrected composition comps/alphas as a numpy float64 array, in the same unit as comps." + }, + "balance_ratio": { + "params": { + "c1": "Methane (C1) reading, any unit consistent with the other components.", + "c2": "Ethane (C2) reading.", + "c3": "Propane (C3) reading.", + "c4": "Butane (C4) reading.", + "c5": "Pentane (C5) reading." + }, + "returns": "Haworth balance ratio Bh = (C1+...+C5)/(C3+C4+C5), dimensionless, as a numpy float64 array; inf where the C3+C4+C5 denominator is zero." + }, + "bernard_ratio": { + "params": { + "c1": "Methane (C1) reading, any unit consistent with the other components.", + "c2": "Ethane (C2) reading.", + "c3": "Propane (C3) reading." + }, + "returns": "Bernard ratio C1/(C2+C3), a biogenic-versus-thermogenic gas indicator, as a numpy float64 array; inf where the C2+C3 denominator is zero." + }, + "character_ratio": { + "params": { + "c3": "Propane (C3) reading, any unit consistent with the other components.", + "c4": "Butane (C4) reading.", + "c5": "Pentane (C5) reading." + }, + "returns": "Haworth character ratio Ch = (C4+C5)/C3, dimensionless, as a numpy float64 array; 0 where C3 is zero." + }, + "classify_fluid_gor": { + "params": { + "gor_sm3": "Gas-oil ratio in Sm3/Sm3 (scalar).", + "thresholds": "Ascending GOR boundaries separating the five fluid classes; default (180, 360, 640, 5000, 15000) Sm3/Sm3." + }, + "returns": "Fluid-type label string: 'black oil', 'volatile oil', 'gas condensate', 'wet gas', or 'dry gas', chosen as the first class whose upper threshold exceeds the GOR ('dry gas' above all thresholds)." + }, + "classify_fluid_wetness": { + "params": { + "bh": "Haworth balance ratio (scalar); in the 17.5-40 percent wetness band, Bh > 100 selects 'gas condensate' over 'oil'.", + "wh_pct": "Haworth wetness ratio in percent (0-100), scalar; the thresholds assume the percent convention." + }, + "returns": "Fluid-type label string: 'dry gas' (Wh < 0.5), 'gas' (Wh < 17.5), 'gas condensate' or 'oil' (17.5 <= Wh < 40, split on Bh > 100), or 'residual oil' (Wh >= 40)." + }, + "normalize_composition": { + "params": { + "axis": "Array axis along which the components are summed and normalized (default -1).", + "comps": "Component readings of the composition (any consistent unit), arranged with the components along axis." + }, + "returns": "Composition rescaled to sum to one along axis as a numpy float64 array; groups whose sum is zero return zeros instead of dividing by zero." + }, + "pixler_ratios": { + "params": { + "c1": "Methane (C1) reading, any unit consistent with the other components.", + "c2": "Ethane (C2) reading.", + "c3": "Propane (C3) reading.", + "c4": "Butane (C4) reading.", + "c5": "Pentane (C5) reading." + }, + "returns": "Dict with keys 'C1/C2', 'C1/C3', 'C1/C4', and 'C1/C5', each a numpy float64 array of the corresponding dimensionless Pixler ratio; inf where a denominator is zero." + }, + "wetness_ratio": { + "params": { + "c1": "Methane (C1) reading, any unit consistent with the other components.", + "c2": "Ethane (C2) reading.", + "c3": "Propane (C3) reading.", + "c4": "Butane (C4) reading.", + "c5": "Pentane (C5) reading.", + "percent": "If True (default) the ratio is returned as a percent (0-100); if False, as a fraction (0-1)." + }, + "returns": "Haworth wetness ratio Wh = (C2+C3+C4+C5)/(C1+...+C5) as a numpy float64 array, in percent by default or as a fraction with percent=False." + } + }, + "petrolib.geochem_fluids.pvt": { + "gas_density": { + "params": { + "m_kg_mol": "Gas molar mass in kg/mol; defaults to methane (0.01604 kg/mol).", + "p_pa": "Absolute pressure in Pa.", + "t_k": "Absolute temperature in K.", + "z": "Gas compressibility factor (dimensionless); defaults to 1.0 (ideal gas)." + }, + "returns": "Real-gas density rho = P*M/(z*R*T) in kg/m3 as a numpy float64 array." + }, + "gas_gravity": { + "params": { + "mw_kg_mol": "Gas molecular weight in kg/mol." + }, + "returns": "Gas specific gravity MW_gas/MW_air (air = 0.028964 kg/mol), dimensionless, as a numpy float64 array." + }, + "mixture_mw": { + "params": { + "axis": "Array axis along which the mole-fraction-weighted sum is taken (default -1).", + "mw": "Component molecular weights, in kg/mol under the module's SI convention (any consistent unit carries through).", + "y": "Component mole fractions of the mixture, arranged along axis." + }, + "returns": "Mixture molecular weight sum(y_i*MW_i) in the unit of mw, as a numpy float64 array with the summed axis removed." + }, + "pressure_from_gas_density": { + "params": { + "m_kg_mol": "Gas molar mass in kg/mol; defaults to methane (0.01604 kg/mol).", + "rho": "Gas density in kg/m3.", + "t_k": "Absolute temperature in K.", + "z": "Gas compressibility factor (dimensionless); defaults to 1.0." + }, + "returns": "Pressure P = rho*z*R*T/M in Pa as a numpy float64 array; inverse of gas_density." + }, + "pseudo_reduced": { + "params": { + "p": "Absolute pressure, in the same unit as ppc (Pa in the module's strict-SI convention).", + "ppc": "Pseudocritical pressure of the gas mixture, same unit as p.", + "t": "Absolute temperature, in the same unit as tpc (K).", + "tpc": "Pseudocritical temperature of the gas mixture, same unit as t." + }, + "returns": "Tuple of two numpy float64 arrays (Ppr, Tpr): the dimensionless pseudo-reduced pressure P/Ppc and pseudo-reduced temperature T/Tpc." + }, + "rachford_rice": { + "params": { + "k": "Equilibrium K-values of the components (e.g. from wilson_k).", + "max_iter": "Maximum number of bisection iterations (default 200).", + "tol": "Convergence tolerance on the absolute value of the Rachford-Rice objective (default 1e-12).", + "z": "Overall (feed) mole fractions of the components." + }, + "returns": "Scalar float vapor mole fraction beta in [0, 1] solving sum(z*(k-1)/(1+beta*(k-1))) = 0 by bisection; when the objective does not change sign over [0, 1], the bracket endpoint with the smaller residual (0.0 or 1.0) is returned." + }, + "wilson_k": { + "params": { + "omega": "Component acentric factor (dimensionless).", + "p_pa": "System pressure in Pa.", + "pc_pa": "Component critical pressure in Pa.", + "t_k": "System temperature in K.", + "tc_k": "Component critical temperature in K." + }, + "returns": "Wilson (1969) equilibrium K-value estimate (Pc/P)*exp(5.373*(1+omega)*(1 - Tc/T)), dimensionless, as a numpy float64 array." + }, + "z_beggs_brill": { + "params": { + "ppr": "Pseudo-reduced pressure P/Ppc (dimensionless).", + "tpr": "Pseudo-reduced temperature T/Tpc (dimensionless)." + }, + "returns": "Beggs-Brill (1973) gas compressibility factor z (dimensionless) as a numpy float64 array." + }, + "z_peng_robinson": { + "params": { + "omega": "Acentric factor of the component (dimensionless), used in the PR alpha function.", + "p_pa": "System pressure in Pa (scalar).", + "pc_pa": "Component critical pressure in Pa.", + "phase": "Root selector: 'vapor' (default) returns the largest positive real root of the PR cubic, 'liquid' the smallest positive real root; other values raise ValueError.", + "t_k": "System temperature in K (scalar).", + "tc_k": "Component critical temperature in K." + }, + "returns": "Scalar float Peng-Robinson compressibility factor z for the selected phase; raises ValueError when the cubic has no positive real root." + } + }, + "petrolib.geochem_fluids.solubility": { + "co2_solubility_brine": { + "params": { + "m_ch4": "Dissolved methane molality in mol/kg (default 0.0); accounts for methane competition via the factor 1/(1 + 0.6*m_CH4).", + "m_nacl": "NaCl molality of the brine in mol/kg (default 0.0, fresh water); reduces solubility via the Setschenow factor.", + "p_mpa": "CO2 pressure in MPa.", + "t_k": "Temperature in K." + }, + "returns": "CO2 solubility in brine, P/(H*gamma) reduced by the methane-competition factor, in mol/kg as a numpy float64 array." + }, + "henry_constant_co2": { + "params": { + "t_k": "Temperature in K." + }, + "returns": "Duan-Sun-style CO2 Henry constant H = 10*exp(-9.4234 + 4.0087e-2*T - 4.5e-5*T**2) in MPa per unit molality, as a numpy float64 array." + }, + "henry_solubility_ln": { + "params": { + "a": "Fitted constant (intercept) of the Krichevsky-Kasarnovsky correlation.", + "b": "Fitted coefficient multiplying ln(P) in the correlation.", + "dh_j_mol": "Enthalpy of solution dH in J/mol, entering as -dH/(R*T).", + "p_mpa": "Gas pressure in MPa.", + "t_k": "Temperature in K." + }, + "returns": "Natural logarithm of the dissolved-gas mole fraction in oil, ln(x) = a + b*ln(P) - dH/(R*T), as a numpy float64 array (exponentiate to get the mole fraction)." + }, + "setschenow_factor": { + "params": { + "ks25": "Setschenow salting-out coefficient at 298 K (25 degC); default 0.11.", + "m_nacl": "NaCl molality of the brine in mol/kg.", + "t_k": "Temperature in K; the Setschenow coefficient is adjusted linearly as ks = ks25 + 1e-4*(T - 298)." + }, + "returns": "Setschenow salting-out activity factor gamma = exp(2*ks*m), dimensionless, as a numpy float64 array; equals 1 for fresh water and grows with salinity." + } + }, + "petrolib.integrity_drilling": { + "acoustic_impedance": { + "params": { + "rho": "Bulk density of the medium, in kg/m3 when rho_unit is 'kg/m3' or in g/cm3 when rho_unit is 'g/cc'.", + "rho_unit": "Density unit switch: 'kg/m3' (default, SI) or 'g/cc' for g/cm3, the CBL articles' convention.", + "v": "Acoustic (compressional) velocity of the medium in m/s." + }, + "returns": "Acoustic impedance Z = rho*v expressed in MRayl, as a numpy float64 array broadcast from the inputs." + }, + "attenuation_coefficient": { + "params": { + "a0": "Initial (source-side) amplitude A0 in the exponential decay A = A0*exp(-alpha*x), any amplitude units.", + "ax": "Amplitude measured after propagating the distance x_m, in the same units as a0.", + "x_m": "Propagation distance in metres over which the amplitude decayed from a0 to ax." + }, + "returns": "Exponential attenuation coefficient alpha = ln(A0/Ax)/x in 1/m, as a numpy float64 array broadcast from the inputs." + }, + "attenuation_db": { + "params": { + "a_far": "Signal amplitude at the far receiver, in the same units as a_near.", + "a_near": "Signal amplitude at the near receiver, in any consistent amplitude units.", + "spacing_m": "Optional receiver spacing in metres; when given, the decibel value is divided by it to yield dB/m." + }, + "returns": "Amplitude attenuation 20*log10(A_near/A_far) as a numpy float64 array, in dB, or in dB/m when spacing_m is provided." + }, + "bond_index": { + "params": { + "clip": "Two-element (low, high) bounds to which the computed bond index is clipped, default (0.0, 1.0).", + "free_pipe": "Anchor value of the measurement in fully unbonded (free) pipe, corresponding to bond index 0.", + "input_kind": "Measurement type: 'amplitude' (falls as bond improves) or 'attenuation' (rises as bond improves; linear method only).", + "measured": "The CBL measurement to convert, either an amplitude or an attenuation value depending on input_kind.", + "method": "Conversion convention: 'linear' interpolates between the two anchors; 'log' uses the logarithmic CBL formula log(free_pipe/A)/log(free_pipe/well_bonded) with 0.01 amplitude floors, amplitude input only.", + "well_bonded": "Anchor value of the measurement in fully bonded pipe, corresponding to bond index 1." + }, + "returns": "Cement bond index as a numpy float64 array clipped to the clip bounds, equal to 0 at the free-pipe anchor and 1 at the well-bonded anchor." + }, + "bond_index_combined": { + "params": { + "bi_a": "First bond indicator (e.g. a bond index from one measurement), scaled by weights[0].", + "bi_b": "Second bond indicator, scaled by weights[1].", + "corrector": "Optional callable applied to the raw weighted combination before clipping, e.g. an eccentricity-correction model.", + "weights": "Two-element weights applied to bi_a and bi_b in the linear combination, default (0.6, 0.4)." + }, + "returns": "Combined bond index as a numpy float64 array, clipped to [0, 1]." + }, + "bowers_pore_pressure": { + "params": { + "A": "Bowers loading-curve amplitude coefficient in V = V0 + A*sigma^B (default 10.0).", + "B": "Bowers loading-curve exponent (default 0.7).", + "U": "Bowers unloading exponent (default 3.0), used only when unloading=True.", + "overburden": "Overburden stress in psi.", + "sigma_max": "Maximum past effective stress in psi anchoring the unloading branch (default 5000.0).", + "unloading": "If True, uses the flatter unloading branch anchored at sigma_max with exponent U; default False uses the virgin loading curve.", + "v0": "Mudline (zero effective stress) velocity V0 in ft/s (default 5000.0).", + "velocity": "Sonic (compressional) velocity in ft/s." + }, + "returns": "Bowers pore pressure in psi as a numpy float64 array: the overburden minus the effective stress inverted from the velocity; on the loading branch the term (V - V0)/A is floored at 0.01 before raising to 1/B." + }, + "casing_condition": { + "params": { + "bands": "Three ascending band edges in percent (default (10.0, 25.0, 42.5)) separating good/fair, fair/poor, and poor/critical; comparison is strict less-than at each edge.", + "loss_pct": "Casing metal loss in percent of nominal wall thickness." + }, + "returns": "Numpy array of string condition labels 'good', 'fair', 'poor', or 'critical' matching the shape of loss_pct." + }, + "casing_resonance_frequency": { + "params": { + "correction": "Multiplicative mode-correction factor (default 1.0; e.g. 0.95 for the S1 negative-group-velocity minimum).", + "n": "Harmonic number of the thickness resonance (default 1, the fundamental).", + "thickness_m": "Casing wall thickness in metres.", + "v": "Compressional velocity of the casing material in m/s (default 5900.0, casing steel)." + }, + "returns": "Casing plate thickness-resonance frequency f = correction*n*v/(2*d) in Hz, as a numpy float64 array." + }, + "casing_thickness_from_resonance": { + "params": { + "correction": "Multiplicative mode-correction factor (default 1.0), the same factor used in the forward resonance formula.", + "freq_hz": "Measured thickness-resonance frequency in Hz.", + "n": "Harmonic number of the observed resonance (default 1, the fundamental).", + "v": "Compressional velocity of the casing material in m/s (default 5900.0, casing steel)." + }, + "returns": "Casing wall thickness correction*n*v/(2*f) in metres, as a numpy float64 array." + }, + "cement_quality_score": { + "params": { + "cement_type": "Cement type key selecting the default (good, fair) thresholds: 'Portland' (4.0, 2.5), 'CO2_resistant' (3.5, 2.0), or 'epoxy_resin' (2.5, 1.5) MRayl.", + "thresholds": "Optional explicit (good, fair) impedance thresholds in MRayl overriding the cement_type defaults.", + "z_mrayl": "Annulus acoustic impedance in MRayl." + }, + "returns": "Continuous cement-quality score as a numpy float64 array clipped to [0, 1]: 1 at or above the good threshold, ramping 0.5 to 1 between fair and good, and 0.25*Z/fair below fair." + }, + "classify_annulus": { + "params": { + "cement_min": "Impedance in MRayl at or above which the fill is classified as cement (default 3.0); pass cement_min equal to liquid_max to remove the transition band.", + "gas_max": "Impedance in MRayl below which the fill is classified as gas (default 0.5).", + "liquid_max": "Impedance in MRayl below which (and at or above gas_max) the fill is classified as liquid (default 2.6).", + "z_mrayl": "Acoustic impedance of the annulus fill in MRayl (scalar)." + }, + "returns": "Class label string: 'gas', 'liquid', 'cement', or 'transition' for impedances between liquid_max and cement_min." + }, + "classify_cement_from_cbl": { + "params": { + "good_max": "Relative amplitude at or below which the cement is classed 'Good' (default 0.15).", + "medium_max": "Relative amplitude at or below which (above good_max) the cement is classed 'Medium' (default 0.30).", + "relative_amp": "CBL amplitude relative to the free-pipe amplitude (dimensionless fraction); lower values indicate better cement bond." + }, + "returns": "Numpy array of string class labels 'Good', 'Medium', or 'Poor' matching the shape of relative_amp." + }, + "classify_fluid_haworth": { + "params": { + "bh": "Haworth balance ratio (C1+C2)/(C3+C4+C5), dimensionless.", + "ch": "Haworth character ratio (C4+C5)/C3, dimensionless; optional and used only by the 8-class scheme's gas-condensate test.", + "n_classes": "Classification scheme selector: 4 for the classic Haworth bands, 8 for the extended 2026 scheme (which requires ch).", + "wh": "Haworth wetness ratio in percent; NaN is allowed in the 8-class scheme and returns 'no gas'." + }, + "returns": "Fluid class label string. With n_classes=4 one of 'dry gas', 'gas', 'gas-condensate', 'oil', 'residual oil'; with n_classes=8 one of 'no gas', 'very dry gas', 'dry gas', 'wet gas', 'gas condensate', 'volatile oil', 'light oil', 'medium/black oil', 'heavy/residual oil'." + }, + "corrosion_front_depth": { + "params": { + "K": "Diffusion rate constant (default 2.5) setting the front depth per square-root of time; the output length unit follows K.", + "t": "Exposure time, in the time units matching K; negative values are floored to zero before the square root." + }, + "returns": "Diffusion-limited corrosion front depth x = K*sqrt(t) as a numpy float64 array, in the length units implied by K." + }, + "cubic_law_conductivity": { + "params": { + "aperture_m": "Parallel-plate crack aperture w in metres.", + "g": "Gravitational acceleration in m/s^2 (default 9.81).", + "mu": "Fluid dynamic viscosity in Pa*s (default 1e-3, water).", + "rho": "Fluid density in kg/m3 (default 1000.0)." + }, + "returns": "Parallel-plate crack hydraulic conductivity K = rho*g*w^3/(12*mu), stated in the docstring as m/s, returned as a numpy float64 array." + }, + "drilling_window_margin": { + "params": { + "frac": "Fracture pressure (or gradient), the upper drilling-window bound, in the same units as pore.", + "pore": "Pore pressure (or pore-pressure gradient), the lower drilling-window bound, in any pressure or gradient units." + }, + "returns": "Drilling-window width frac - pore as a numpy float64 array, in the same units as the inputs; positive where a window exists." + }, + "eaton_pore_pressure": { + "params": { + "clip_ratio": "Optional (low, high) bounds to which the log ratio is clipped before exponentiation, e.g. (0.01, 100.0); None applies no clipping.", + "exponent": "Eaton exponent applied to the log ratio (default 3.0, the sonic convention).", + "hydrostatic": "Normal (hydrostatic) pore pressure Pn, in the same units as overburden.", + "log_type": "Log-ratio convention: 'sonic' uses ratio = normal/observed (slowness), 'resistivity' uses ratio = observed/normal.", + "normal": "Normal-compaction-trend value of the same log at the same depth, in the same units as observed.", + "observed": "Measured log value at depth: sonic slowness for log_type='sonic', resistivity for log_type='resistivity'.", + "overburden": "Overburden (vertical) stress at the depth of interest, in pressure or gradient units; the output follows these units." + }, + "returns": "Eaton pore pressure Pp = OB - (OB - Pn)*ratio^exponent as a numpy float64 array, in the same pressure or gradient units as the overburden and hydrostatic inputs." + }, + "haworth_ratios": { + "params": { + "c1": "Methane (C1) mud-gas reading, in any units consistent with the other components.", + "c2": "Ethane (C2) mud-gas reading, same units as the other components.", + "c3": "Propane (C3) mud-gas reading, same units as the other components.", + "c4": "Butane (C4) mud-gas reading, same units as the other components.", + "c5": "Pentane (C5) mud-gas reading, same units as the other components.", + "percent": "If True (default), the wetness ratio Wh is scaled by 100 to percent; otherwise it is a fraction." + }, + "returns": "Tuple of three floats (Wh, Bh, Ch): wetness (C2+...+C5)/total (percent if percent=True), balance (C1+C2)/(C3+C4+C5), and character (C4+C5)/C3. Wh is NaN when the total gas is zero; Bh and Ch are +inf on zero denominators." + }, + "hydrostatic_pressure": { + "params": { + "rho": "Fluid density in kg/m3 (default 1030.0, seawater).", + "tvd_m": "True vertical depth of the fluid column in metres." + }, + "returns": "Hydrostatic pressure rho*g*TVD in Pa, with g = 9.80665 m/s^2, as a numpy float64 array." + }, + "hydrostatic_pressure_bar": { + "params": { + "sg": "Fluid specific gravity relative to fresh water (default 1.0).", + "tvd_m": "True vertical depth of the fluid column in metres." + }, + "returns": "Hydrostatic pressure 0.0980665*SG*TVD in bar, as a numpy float64 array." + }, + "hydrostatic_pressure_psi": { + "params": { + "sg": "Fluid specific gravity relative to fresh water (default 1.0).", + "tvd_ft": "True vertical depth of the fluid column in feet." + }, + "returns": "Hydrostatic pressure 0.433*SG*TVD in psi, as a numpy float64 array." + }, + "leak_rate_gas": { + "params": { + "aperture_m": "Radial aperture of the microannulus in metres; a non-positive aperture gives zero flow.", + "length_m": "Length of the microannulus leak path in metres.", + "mu_pa_s": "Dynamic viscosity of the gas in Pa*s.", + "p_in_pa": "Absolute inlet (upstream) pressure P1 in Pa.", + "p_out_pa": "Absolute outlet (downstream) pressure P2 in Pa; must be positive.", + "r_casing_m": "Outer casing radius (inner boundary of the gap) in metres." + }, + "returns": "Gas volumetric leak rate in m^3/s referenced to outlet conditions, from the isothermal compressible-flow formula Q = (pi/(16 mu L P2))*Omega*(P1^2 - P2^2), as a scalar float floored at 0.0; gravity is neglected." + }, + "leak_rate_liquid": { + "params": { + "aperture_m": "Radial aperture of the microannulus in metres; a non-positive aperture gives zero flow.", + "dp_pa": "Applied pressure differential driving the leak, in Pa.", + "inclination_deg": "Well inclination from horizontal in degrees (default 90.0 = vertical), setting the gravity component along the leak path.", + "length_m": "Length of the microannulus leak path in metres.", + "mu_pa_s": "Dynamic viscosity of the leaking liquid in Pa*s.", + "r_casing_m": "Outer casing radius (inner boundary of the gap) in metres.", + "rho": "Liquid density in kg/m3 (default 1000.0), used for the gravity head correction." + }, + "returns": "Liquid volumetric leak rate Q in m^3/s from gravity-corrected Hagen-Poiseuille flow, as a scalar float; 0.0 when the gap is closed, the length is non-positive, or the applied pressure cannot overcome gravity." + }, + "metal_loss_pct": { + "params": { + "measured": "Measured remaining casing wall thickness, in any units consistent with nominal.", + "nominal": "Nominal (as-new) casing wall thickness, in the same units as measured." + }, + "returns": "Casing metal loss (1 - measured/nominal)*100 in percent, as a numpy float64 array clipped to [0, 100]." + }, + "microannulus_omega": { + "params": { + "aperture_m": "Radial aperture (gap width) of the microannulus in metres; a non-positive aperture is treated as closed.", + "r_casing_m": "Outer casing radius R1 in metres, the inner boundary of the microannulus gap." + }, + "returns": "Annular-gap fourth moment Omega = R2^4 - R1^4 - (R2^2 - R1^2)^2/ln(R2/R1) in m^4, as a scalar float; 0.0 for a closed gap." + }, + "mudcake_thickness": { + "params": { + "dp_pa": "Differential filtration pressure across the cake in Pa.", + "k_mc_m2": "Mudcake permeability in m^2 (for model='chin_ode' the initial cake permeability, which decays with time).", + "model": "Growth convention: 'dewan' closed-form h = sqrt(2 k dP t/(mu S)); 'sqrt_k' empirical h = rate_const*sqrt(t); 'chin_ode' stepwise integration of the Chin thickness ODE with decaying cake permeability.", + "mu_pa_s": "Mud-filtrate dynamic viscosity in Pa*s (default 1e-3).", + "rate_const": "Empirical growth-rate constant multiplying sqrt(t) (metres per square-root second), used only by model='sqrt_k'.", + "solids_ratio": "Cake solids term S in the Dewan formula, or the mud solid fraction fs for model='chin_ode' (default 0.6); unused by 'sqrt_k'.", + "t_s": "Elapsed filtration time(s) in seconds; for model='chin_ode' this must be an increasing time array over which the ODE is stepped.", + "v": "Decay exponent of the cake permeability K(t) = k_mc*((t + 1e-3)/1e-3)^-v in the chin_ode model (default 0.5)." + }, + "returns": "Mudcake thickness in metres as a numpy float64 array matching t_s; the chin_ode model starts from a 1e-5 m (10-micron) seed cake and integrates forward in time." + }, + "normalize_gas": { + "params": { + "bit_diameter": "Bit diameter in inches, used for the borehole cross-sectional area in the metric formula; unused when units='field'.", + "flow": "Mud flow rate: L/min for units='metric', gpm for units='field'.", + "mud_weight": "Mud weight in ppg, required for units='field' (enters the correction as the squared ratio to the reference); ignored for 'metric'.", + "reference": "Reference (ROP0, Q0, MW0) drilling conditions for the field normalization, default (30.0 ft/hr, 500.0 gpm, 10.0 ppg).", + "rop": "Rate of penetration: m/hr for units='metric' (floored at 1e-6), ft/hr for units='field'.", + "total_gas": "Total mud-gas reading to normalize, in any consistent gas units.", + "units": "Normalization convention: 'metric' returns the drilled-rock gas index G*Q/(ROP*A_bit); 'field' divides the reading by the factors (ROP/ROP0)*(Q/Q0)*(MW/MW0)^2 to bring it to reference conditions." + }, + "returns": "Normalized gas as a numpy float64 array: the drilled-rock gas index for units='metric', or the reading corrected to the reference drilling conditions (same units as total_gas) for units='field'." + }, + "overburden_pressure": { + "params": { + "rho_bulk": "Constant bulk density of the sediment column in kg/m3.", + "rho_sw": "Seawater density in kg/m3 (default 1030.0).", + "tvd_m": "True vertical depth below the mudline (sediment column thickness) in metres.", + "water_depth_m": "Seawater column thickness in metres (default 0.0, no water column)." + }, + "returns": "Overburden pressure g*(rho_sw*water_depth + rho_bulk*tvd) in Pa, with g = 9.80665 m/s^2, as a numpy float64 array." + }, + "overburden_pressure_psi": { + "params": { + "sediment_depth_ft": "Sediment column thickness (depth below mudline) in feet.", + "sediment_sg": "Average sediment bulk specific gravity (default 2.3).", + "sw_sg": "Seawater specific gravity (default 1.025).", + "water_depth_ft": "Seawater column thickness in feet." + }, + "returns": "Deepwater overburden pressure 0.433*(SG_sw*water_depth + SG_sed*sediment_depth) in psi, as a numpy float64 array." + }, + "pixler_ratios": { + "params": { + "c1": "Methane (C1) mud-gas reading, the numerator of every Pixler ratio; any consistent gas units.", + "c2": "Ethane (C2) mud-gas reading, same units as the other components.", + "c3": "Propane (C3) mud-gas reading, same units as the other components.", + "c4": "Butane (C4) mud-gas reading, same units as the other components.", + "c5": "Pentane (C5) mud-gas reading, same units as the other components." + }, + "returns": "Dict of scalar floats with keys 'C1/C2', 'C1/C3', 'C1/C4', 'C1/C5' (Pixler component ratios) and 'bernard' (the Bernard ratio C1/(C2+C3)); zero denominators give +inf." + }, + "reflection_coefficient": { + "params": { + "z1": "Acoustic impedance of the incident-side medium, in the same units as z2 (e.g. MRayl).", + "z2": "Acoustic impedance of the transmitted-side medium, in the same units as z1." + }, + "returns": "Dimensionless normal-incidence reflection coefficient R = (Z2 - Z1)/(Z2 + Z1), a numpy float64 array broadcast from the inputs, ranging between -1 and 1." + }, + "remaining_life_years": { + "params": { + "min_acceptable_mm": "Minimum acceptable wall thickness in millimetres, below which the casing is no longer serviceable.", + "rate_mm_per_yr": "Corrosion (wall-loss) rate in millimetres per year; a non-positive value is treated as no wear.", + "thickness_mm": "Current casing wall thickness in millimetres." + }, + "returns": "Remaining casing life (thickness - minimum)/rate in years as a scalar float, floored at 0; a non-positive corrosion rate returns the 999.0 sentinel." + }, + "transmission_energy": { + "params": { + "z1": "Acoustic impedance of the incident-side medium, in the same units as z2 (e.g. MRayl).", + "z2": "Acoustic impedance of the transmitted-side medium, in the same units as z1." + }, + "returns": "Fraction of incident acoustic energy transmitted across the interface, 1 - R^2, as a dimensionless numpy float64 array in [0, 1]." + }, + "within_drilling_window": { + "params": { + "ecd": "Equivalent circulating density or downhole pressure, in the same units as pore and frac.", + "frac": "Fracture pressure, the upper window bound, same units as ecd.", + "pore": "Pore pressure, the lower window bound, same units as ecd." + }, + "returns": "Boolean scalar: True when pore < ecd < frac, i.e. the ECD sits strictly inside the drilling window, otherwise False." + } + }, + "petrolib.inversion_numerics.costs": { + "misfit": { + "params": { + "kind": "Misfit measure: l2 weighted sum of squares, rms root-mean-square, rel_data per-datum relative squared misfit, rel_norm relative 2-norm, chi2 weighted sum of squares with weights 1/sigma^2.", + "log_space": "If True, compares log10 of the simulated and observed values instead of the raw values.", + "obs": "Observed (measured) data values the simulation is compared against.", + "sim": "Simulated (forward-modelled) data values, as an array matching the observations.", + "weights": "Optional per-datum weights w applied to the squared residuals; defaults to ones, and for kind chi2 should be 1/sigma^2." + }, + "returns": "Scalar float data misfit between simulation and observations, computed by the selected kind; rel_data and rel_norm are dimensionless, the others carry the (squared) units of the data." + }, + "reg_lambda_brd": { + "params": { + "a": "Forward or design matrix A of the linear system A x = b being regularized.", + "b": "Observed data vector b of the linear system.", + "bracket": "Tuple (low, high) bounding the lam search; bisection proceeds geometrically on log(lam) inside it (default (1e-6, 1e3)).", + "chi2_target": "Target data misfit ||A x_lam - b||^2 that the discrepancy principle should reach, e.g. n*sigma^2.", + "max_iter": "Number of geometric bisection iterations performed (default 50)." + }, + "returns": "Scalar float Tikhonov regularization weight lam whose data misfit matches chi2_target, found by geometric bisection within the bracket." + }, + "reg_lambda_multiplicative": { + "params": { + "alpha": "Multiplicative coefficient of the schedule lam = alpha * misfit^beta.", + "beta": "Exponent applied to the misfit value in the cooling schedule.", + "lam_max": "Upper cap on the returned regularization weight; defaults to infinity (no cap).", + "misfit_value": "Current data-misfit value driving the cooling schedule." + }, + "returns": "Scalar float regularization weight lam = min(alpha * misfit_value**beta, lam_max), the Habashy-Abubakar multiplicative cooling value." + } + }, + "petrolib.inversion_numerics.fitting": { + "fit_cosine": { + "params": { + "az": "Azimuth angles of the samples, in degrees.", + "y": "Measured values at each azimuth to be fit by the cosine harmonic." + }, + "returns": "Tuple (mean, amplitude, phase_deg) of floats for the fit y = mean + amplitude*cos(az - phase): mean level and amplitude in the units of y, phase in degrees." + }, + "fit_exponential_approach": { + "params": { + "t": "Time (independent-variable) samples of the relaxation record.", + "three_point": "If True, uses the closed-form estimate from the first, middle, and last samples (assumes equal spacing); if False (default), fits with scipy curve_fit.", + "y": "Observed values approaching an asymptote as y = asymptote + (y0 - asymptote)*exp(-t/tau)." + }, + "returns": "Tuple (asymptote, tau) of floats: the long-time asymptote in the units of y and the relaxation time constant tau in the units of t." + }, + "fit_line": { + "params": { + "x": "Independent-variable samples of the regression.", + "xform": "Optional transform applied to x before fitting: None, log10, log, inv, sqrt, or square.", + "y": "Dependent-variable samples, same length as x.", + "yform": "Optional transform applied to y before fitting; same accepted names as xform." + }, + "returns": "LineFit named tuple (slope, intercept, r2) of floats from the degree-1 least-squares fit, all reported in the transformed space; r2 is the coefficient of determination (0 when y is constant)." + }, + "fit_powerlaw_decay": { + "params": { + "exponent": "Optional known decay exponent b; when given only the amplitude a is recovered by linear projection, otherwise both a and b are fit with scipy curve_fit.", + "x": "Independent-variable samples of the decay record.", + "y": "Observed decaying values to be fit by y = a * x**(-b)." + }, + "returns": "Tuple (a, b) of floats for the model y = a * x**(-b): the amplitude a in the units of y and the decay exponent b (dimensionless)." + } + }, + "petrolib.inversion_numerics.linear": { + "condition_number": { + "params": { + "a": "Matrix whose numerical conditioning is assessed." + }, + "returns": "Scalar float 2-norm condition number of the matrix, the ratio of its largest to smallest singular value." + }, + "convolution_matrix": { + "params": { + "kernel": "1-D convolution (blurring) kernel, treated as centered on each output sample.", + "n": "Signal length, i.e. the side of the square output matrix." + }, + "returns": "Numpy float array of shape (n, n): the convolution matrix G whose rows apply the centered kernel, each row renormalized to sum to one so edge truncation is compensated." + }, + "deconvolve": { + "params": { + "d": "Observed (blurred) data vector, modelled as d = G x.", + "g": "Convolution/blurring matrix G, e.g. built with convolution_matrix.", + "rank": "Optional SVD truncation rank for the solve; None (default) uses the full Moore-Penrose pseudo-inverse." + }, + "returns": "Numpy float array x, the deconvolved signal estimate from the (rank-limited) pseudo-inverse of G applied to d." + }, + "difference_operator": { + "params": { + "n": "Number of model parameters, i.e. the number of columns of the operator.", + "order": "Difference order: 0 returns the (n, n) identity, 1 (default) the first-difference operator, 2 the second difference, and so on." + }, + "returns": "Numpy float array L of shape (n - order, n) (or the (n, n) identity for order 0) that applies the finite-difference roughness operator to a length-n model vector." + }, + "ista_l1": { + "params": { + "a": "Coefficient matrix A of the sparse inversion.", + "b": "Data vector b to be fit.", + "eta": "L1 sparsity weight in the objective 0.5*||Ax - b||^2 + eta*||x||_1 (default 0.1).", + "max_iter": "Number of ISTA soft-thresholding iterations performed (default 500).", + "nonneg": "If True (default), each iterate is additionally projected onto x >= 0." + }, + "returns": "Numpy float array x, the sparse solution after max_iter iterative soft-thresholding steps, with length equal to the number of columns of A." + }, + "map_estimate": { + "params": { + "d": "Observed data vector.", + "ell": "Optional prior regularization operator L; the identity when omitted.", + "g": "Linear forward operator (sensitivity matrix) G mapping the model vector to data.", + "m_prior": "Optional prior mean model the estimate is regularized toward; zero when omitted.", + "noise_var": "Scalar data-noise variance dividing the G^T G and G^T d terms of the posterior precision.", + "prior_strength": "Scalar weight of the prior precision term prior_strength * L^T L." + }, + "returns": "Tuple (x, posterior_cov): the Bayesian MAP model estimate as a numpy float array of length n_param, and the (n_param, n_param) posterior covariance matrix A^{-1} where A = G^T G/noise_var + prior_strength*L^T L." + }, + "nnls_solve": { + "params": { + "a": "Coefficient matrix of the least-squares system.", + "b": "Right-hand-side data vector.", + "reg": "Ridge weight; when greater than 0 the system is augmented with sqrt(reg)*I rows pulling the solution toward zero (default 0.0)." + }, + "returns": "Numpy float array x with all entries >= 0, the non-negative least-squares solution (scipy.optimize.nnls) of the optionally ridge-augmented system." + }, + "project_simplex": { + "params": { + "total": "Required sum of the projected vector (default 1.0).", + "v": "Input vector to be projected onto the simplex." + }, + "returns": "Numpy float array, the Euclidean projection of v onto the simplex {x >= 0, sum(x) = total}, same length as v." + }, + "svd_solve": { + "params": { + "a": "Coefficient matrix A of the linear system A x = b.", + "b": "Right-hand-side data vector b.", + "rank": "Optional number of singular values kept in the pseudo-inverse; None (default) keeps them all (plain least-squares solve)." + }, + "returns": "Numpy float array x, the truncated-SVD (rank-limited pseudo-inverse) least-squares solution of A x = b, with length equal to the number of columns of A." + }, + "tikhonov_solve": { + "params": { + "a": "Forward or design matrix A of the regularized least-squares problem.", + "b": "Data vector b to be fit.", + "lam": "Regularization weight multiplying the penalty ||L(x - x_ref)||^2, applied once and never squared (default 1.0).", + "nonneg": "If True, solves the lam-augmented stacked system with non-negative least squares (nnls_solve) instead of the normal equations.", + "reg_op": "Optional regularization operator L (e.g. from difference_operator); the identity when omitted.", + "sigma": "Optional per-datum standard deviations; rows of A and b are weighted by 1/sigma.", + "x_ref": "Optional reference model the solution is pulled toward by the penalty; zero when omitted." + }, + "returns": "Numpy float array x minimizing ||W(Ax - b)||^2 + lam*||L(x - x_ref)||^2, with length equal to the number of columns of A; non-negative when nonneg=True." + }, + "unmix": { + "params": { + "closure": "Sum-to-one handling: None (no closure), 'row' appends a heavily weighted unit-sum equation to the system, 'simplex' projects the final solution onto the unit simplex.", + "measured": "Measured log or spectral values to be unmixed (the data vector).", + "nonneg": "Non-negativity handling: None plain least squares, 'clip' clips negatives to zero, 'nnls' (default) solves with non-negative least squares, 'simplex' projects the least-squares solution onto the unit simplex.", + "normalize": "If True, rescales the final solution to sum to one (skipped when the sum is zero).", + "response_matrix": "End-member response matrix; each column is one component's response, so measured is modelled as response_matrix @ x.", + "sigma": "Optional per-measurement standard deviations applying 1/sigma weights to the rows of the system." + }, + "returns": "Numpy float array x of component fractions or abundances, one entry per response-matrix column, satisfying the requested non-negativity and closure options." + } + }, + "petrolib.inversion_numerics.nonlinear": { + "fd_gradient": { + "params": { + "f": "Scalar objective callable evaluated at a parameter vector.", + "x": "Parameter vector at which the gradient is taken." + }, + "returns": "Numpy float array of length len(x): the finite-difference gradient of the scalar objective, computed via fd_jacobian (extra keyword arguments such as eps or scheme are passed through)." + }, + "fd_jacobian": { + "params": { + "eps": "Finite-difference step size (default 1e-6); scaled per parameter by max(|m_i|, 1) when relative is True.", + "forward": "Forward-model callable mapping a model vector to a data vector.", + "m": "Model vector at which the Jacobian is evaluated.", + "relative": "If True (default), the perturbation per parameter is eps*max(|m_i|, 1); if False, the absolute step eps is used.", + "scheme": "'central' (default) uses central differences; 'forward' uses one-sided forward differences." + }, + "returns": "Numpy float array of shape (n_data, n_param) holding the finite-difference Jacobian d forward / d m." + }, + "feasible_set_sampling": { + "params": { + "bounds": "List of (low, high) tuples per parameter; candidates are clipped into this box.", + "data": "Observed data vector.", + "forward": "Forward-model callable mapping candidate models to predicted data.", + "m_center": "Central solution around which random candidate models are perturbed.", + "n_samples": "Number of random candidate models drawn (default 2000).", + "noise_level": "Data noise level; a candidate is accepted when its RMS data residual is within 2*noise_level.", + "seed": "Seed of the random-number generator (default 1).", + "step_frac": "Standard deviation of the perturbation as a fraction of each parameter's bound range (default 0.15)." + }, + "returns": "Dict with keys 'P5', 'P50', 'P95' holding per-parameter percentile arrays of the accepted models, and 'models' holding the (n_accepted, n_param) array of accepted models (the center alone if none were accepted)." + }, + "grid_search": { + "params": { + "data": "Observed data vector the predictions are compared to.", + "forward": "Forward-model callable evaluated at every grid combination.", + "grids": "List of 1-D candidate-value arrays, one per parameter; their Cartesian product is searched exhaustively.", + "misfit": "'log_l2' (default) sums squared differences of natural logs of prediction and data; any other value uses the plain L2 sum of squares." + }, + "returns": "Tuple (best, best_err): the minimizing parameter combination as a numpy float array and its scalar float misfit value." + }, + "levenberg_marquardt": { + "params": { + "bounds": "Optional (lo, hi) pair of arrays box-constraining the physical model; iterates are clipped into the box.", + "damping": "'diag' (default) damps with the diagonal of J^T J (Marquardt scaling); 'identity' damps with the identity matrix (Levenberg).", + "data": "Observed data vector the model is fit to.", + "forward": "Forward-model callable mapping a physical model vector to predicted data.", + "lam0": "Initial Levenberg-Marquardt damping parameter (default 1e-3).", + "lam_dn": "Factor multiplying the damping after an accepted step (default 0.5).", + "lam_up": "Factor multiplying the damping after a rejected (cost-increasing) step (default 5.0).", + "log_params": "If True, the inversion works on log(m), keeping parameters positive and making steps multiplicative.", + "m0": "Initial physical model vector.", + "max_iter": "Maximum number of LM iterations (default 50).", + "tol": "Convergence tolerance on the decrease of the squared-residual cost between accepted steps (default 1e-8)." + }, + "returns": "InvResult named tuple: model (final physical model, numpy float array), misfit (final sum of squared residuals, float), n_iter (iterations used), converged (bool), and lam (final damping value)." + }, + "multistart": { + "params": { + "aggregate": "'best' returns the lowest-misfit model; 'misfit_weighted' (default) returns the softmax(-misfit) weighted average of all recovered models.", + "bounds": "List of (low, high) tuples per parameter defining the box from which uniform random starts are drawn.", + "n_starts": "Number of random starting points (default 50).", + "seed": "Seed of the random-number generator drawing the start points (default 0).", + "solver": "Callable run from each random start; must return an InvResult or a (model, misfit) tuple." + }, + "returns": "Numpy float array, the aggregated model over all starts: either the single best model or the misfit-weighted average, length n_param." + }, + "occam": { + "params": { + "data": "Observed data vector.", + "forward": "Forward-model callable mapping the model vector to predicted data.", + "lam0": "Initial roughness weight lam (default 100.0), multiplied by 0.8 each iteration until the chi-square target is reached.", + "m0": "Starting model vector.", + "max_iter": "Maximum number of Occam iterations (default 50).", + "noise_level": "Data noise standard deviation; residuals are scaled by 1/noise_level and the chi-square target equals the number of data.", + "reg_order": "Order of the finite-difference roughness operator L penalizing ||L m||^2 (default 2, second difference)." + }, + "returns": "Numpy float array, the smoothest model found that fits the data to within the noise level (chi-square <= number of data), or the model after max_iter iterations." + } + }, + "petrolib.inversion_numerics.optimize": { + "gradient_descent": { + "params": { + "backtracking": "If True, the step is halved each iteration until an Armijo sufficient-decrease condition holds; if False (default), the fixed lr is used.", + "f": "Scalar objective callable to minimize.", + "lr": "Learning rate (step size); None uses the default 1e-2, which is also the starting step for backtracking.", + "max_iter": "Maximum number of descent iterations (default 4000).", + "tol": "Stopping tolerance on the absolute change of the objective between iterations (default 1e-10).", + "x0": "Initial parameter vector." + }, + "returns": "Numpy float array, the parameter vector at termination (after convergence to tol or max_iter finite-difference gradient steps)." + }, + "pso": { + "params": { + "bounds": "List of (low, high) tuples per dimension defining the search box; particle positions are clipped into it.", + "c1": "Cognitive acceleration coefficient pulling each particle toward its personal best (default 1.5).", + "c2": "Social acceleration coefficient pulling each particle toward the global best (default 1.5).", + "inertia_decay": "Optional factor multiplying the inertia weight after every iteration; None (default) keeps it constant.", + "n_iter": "Number of swarm update iterations (default 200).", + "n_particles": "Number of particles in the swarm (default 30).", + "objective": "Scalar objective callable to minimize, evaluated at a parameter vector.", + "omega": "Inertia weight multiplying each particle's previous velocity (default 0.7).", + "seed": "Seed of the random-number generator initializing positions and random factors (default 0)." + }, + "returns": "Tuple (x_best, f_best, history): the best parameter vector found (numpy float array), its scalar float objective value, and a numpy array of the best objective per iteration (length n_iter + 1)." + } + }, + "petrolib.inversion_numerics.pde": { + "cfl_number": { + "params": { + "alpha": "Diffusivity of the explicit scheme, in consistent length-squared-per-time units.", + "dt": "Time-step size, in the time unit consistent with alpha.", + "dx": "Grid spacing, in the length unit consistent with alpha." + }, + "returns": "Scalar float diffusion CFL number alpha*dt/dx^2 (dimensionless); explicit stability requires a value <= 0.5." + }, + "diffusion_step_1d": { + "params": { + "alpha": "Diffusivity, in consistent length-squared-per-time units.", + "bc": "Boundary condition: 'neumann' (default) applies no-flux ends, 'dirichlet' holds the two endpoint values fixed.", + "dt": "Time-step size of the explicit update.", + "dx": "Grid spacing.", + "source": "Optional per-node source term added to the field as dt*source; None (default) means no source.", + "u": "Current 1-D field values on the grid." + }, + "returns": "Numpy float array of the field after one explicit finite-difference diffusion step u + alpha*dt/dx^2 * laplacian(u) + dt*source, same length as u." + }, + "effective_conductivity_2d": { + "params": { + "n_iter": "Maximum number of Jacobi relaxation sweeps (default 5000).", + "sigma_map": "2-D array (ny, nx) of cell conductivities; current is driven vertically from the top row (V=1) to the bottom row (V=0) with no-flux sides.", + "tol": "Convergence tolerance on the maximum potential change between sweeps (default 1e-7)." + }, + "returns": "Scalar float effective vertical conductivity of the map, in the same units as sigma_map, computed from the top-layer current after solving the steady Laplace problem div(sigma grad V) = 0 with harmonic-mean face conductivities." + } + }, + "petrolib.inversion_numerics.stochastic": { + "enkf_update": { + "params": { + "ens": "Prior ensemble of state/model vectors, shape (n_ens, n_state).", + "obs": "Observation vector to assimilate.", + "obs_cov": "Observation-error covariance matrix R, used both in the Kalman gain and to perturb the observations per member.", + "obs_op": "Observation operator mapping state to observation space: either a callable state -> obs, or a matrix H applied as x @ H.T.", + "seed": "Seed of the random-number generator drawing the observation perturbations (default 0)." + }, + "returns": "Numpy float array of shape (n_ens, n_state): the analysis (updated) ensemble after the stochastic perturbed-observation ensemble-Kalman update." + }, + "gaussian_loglik": { + "params": { + "log_space": "If True, natural logs of obs and pred are taken before differencing.", + "obs": "Observed data values.", + "pred": "Predicted (modelled) data values compared to the observations.", + "sigma": "Data standard deviation(s) scaling the residuals; scalar or per-datum array.", + "weights": "Optional per-datum weights on the squared standardized residuals; defaults to ones." + }, + "returns": "Scalar float Gaussian log-likelihood -0.5*sum(w*((obs - pred)/sigma)^2); the normalization constant is omitted." + }, + "lm_enrml": { + "params": { + "forward": "Forward-model callable mapping a model vector to predicted data.", + "lam0": "Initial Levenberg-Marquardt damping lam (default 1.0).", + "lam_dn": "Factor multiplying lam after an accepted update (default 0.4).", + "lam_up": "Factor multiplying lam after a rejected (misfit-increasing) update (default 4.0).", + "n_ens": "Ensemble size (default 80).", + "n_iter": "Number of LM-EnRML update iterations (default 12).", + "obs": "Observed data vector.", + "obs_cov": "Observation-error covariance matrix R, inflated by (1 + lam) in the update and used for per-member observation perturbations.", + "prior_cov": "Prior covariance matrix, used both to sample the initial ensemble and inside the update gain.", + "prior_mean": "Mean of the Gaussian prior over the model parameters, used to sample the initial ensemble.", + "seed": "Seed of the random-number generator (default 0)." + }, + "returns": "Numpy float array of shape (n_ens, n_param): the final ensemble of model vectors after the damped Kalman-type updates with SVD-based empirical sensitivity." + }, + "mala": { + "params": { + "log_post": "Callable returning the log-posterior density of a parameter vector; its finite-difference gradient supplies the Langevin drift.", + "n_samples": "Number of MALA iterations; every state is kept (no burn-in).", + "seed": "Seed of the random-number generator (default 0).", + "step": "Langevin step size; proposals are x + 0.5*step^2*grad(log_post) + step*N(0,1).", + "x0": "Starting point of the chain." + }, + "returns": "Chain named tuple: samples is the (n_samples, n_param) numpy array of chain states and acceptance the accepted-move fraction (float)." + }, + "metropolis": { + "params": { + "burn_in": "Number of initial iterations excluded from the returned samples (default 0).", + "log_post": "Callable returning the log-posterior density of a parameter vector.", + "log_space": "If True, proposals are multiplicative x*exp(step*N(0,1)), keeping parameters positive; if False (default), additive x + step*N(0,1).", + "n_samples": "Total number of MCMC iterations run (default 4000).", + "seed": "Seed of the random-number generator (default 0).", + "step": "Proposal scale per parameter (scalar or array): the additive standard deviation, or the multiplicative log-scale when log_space is True.", + "x0": "Starting point of the Markov chain." + }, + "returns": "Chain named tuple: samples is the (n_samples - burn_in, n_param) numpy array of retained chain states, acceptance is the fraction of accepted moves over all iterations (float)." + }, + "soft_envelope_logprior": { + "params": { + "hi": "Upper envelope bound per parameter.", + "lo": "Lower envelope bound per parameter.", + "x": "Parameter vector to evaluate." + }, + "returns": "Scalar float log-prior: 0 for x inside [lo, hi] and the quadratic penalty -0.5*sum(excess^2) growing with the distance outside the envelope." + }, + "uniform_logprior": { + "params": { + "bounds": "List of (low, high) tuples, one per parameter, defining the uniform box support.", + "x": "Parameter vector to evaluate against the box." + }, + "returns": "Scalar float log-prior: 0.0 when every component of x lies inside its bounds, -inf otherwise." + } + }, + "petrolib.ml_stats": { + "accuracy": { + "params": { + "y_pred": "Predicted class labels, same shape as y_true.", + "y_true": "True class labels." + }, + "returns": "Scalar float fraction of samples whose predicted label matches the true label, in [0, 1]." + }, + "affine_rescale": { + "params": { + "dst_hi": "Destination-range upper endpoint.", + "dst_lo": "Destination-range lower endpoint.", + "src_hi": "Source-range upper endpoint that maps to dst_hi.", + "src_lo": "Source-range lower endpoint (e.g. a reference percentile) that maps to dst_lo.", + "x": "Input values to map." + }, + "returns": "Numpy float64 array with the linear map dst_lo + (x - src_lo)*(dst_hi - dst_lo)/(src_hi - src_lo) applied; values outside the source range extrapolate linearly (no clipping)." + }, + "confusion_matrix": { + "params": { + "labels": "Optional explicit class ordering for the matrix rows and columns; default None uses the sorted union of labels found in y_true and y_pred.", + "y_pred": "Predicted class labels, same length as y_true.", + "y_true": "True class labels." + }, + "returns": "Square numpy int64 matrix where C[i, j] counts samples of true class i predicted as class j; for binary 0/1 labels this is the [[TN, FP], [FN, TP]] layout." + }, + "fit_line": { + "params": { + "x": "Independent-variable sample values.", + "y": "Dependent-variable sample values, same length as x." + }, + "returns": "Tuple of two floats (slope, intercept) of the degree-1 least-squares fit y = slope*x + intercept, always in that order." + }, + "fit_powerlaw": { + "params": { + "x": "Independent-variable values; must be positive, since zeros or negatives propagate as NaN through the log10 transform.", + "y": "Dependent-variable values; must be positive for the same reason." + }, + "returns": "Tuple of two floats (coefficient, exponent) such that y is approximately coefficient * x**exponent, obtained from a log10-log10 line fit." + }, + "kmeans": { + "params": { + "k": "Number of clusters to form.", + "max_iter": "Maximum number of Lloyd iterations (default 100); iteration stops early when the labels no longer change.", + "weights": "Optional per-sample weights used in the weighted-mean centroid update; default None gives equal weights.", + "x": "Data matrix of shape (n_samples, n_features) to cluster." + }, + "returns": "Tuple (labels, centroids): a numpy int64 array of per-sample cluster indices and a (k, n_features) numpy float64 array of cluster centers. Deterministic by construction (farthest-point initialization, no RNG); cluster numbering is an artifact of the initialization, not a physical ordering." + }, + "mae": { + "params": { + "y_pred": "Model-predicted values, same shape as y_true.", + "y_true": "Observed (true) target values." + }, + "returns": "Scalar float mean absolute error mean(|y_pred - y_true|), in the units of the target." + }, + "mape": { + "params": { + "as_percent": "If True (default) the mean absolute relative error is multiplied by 100 and returned as a percentage; False returns the raw fraction.", + "y_pred": "Model-predicted values, same shape as y_true.", + "y_true": "Observed (true) target values; division by them is deliberately unguarded, so a zero observation yields inf/NaN." + }, + "returns": "Scalar float mean absolute percentage error (the articles' AAPE), as a percentage by default or a fraction with as_percent=False; inf/NaN when any observed value is zero." + }, + "minmax": { + "params": { + "axis": "None (default) uses the global min and max; an integer axis rescales independently along that axis.", + "eps": "Additive guard added to the (max - min) range denominator; the default 0.0 yields NaN for a constant input.", + "hi": "Upper endpoint of the output range (default 1.0).", + "lo": "Lower endpoint of the output range (default 0.0).", + "x": "Input array to rescale." + }, + "returns": "Numpy float64 array of the same shape, linearly rescaled so the data minimum maps to lo and the maximum to hi." + }, + "ols": { + "params": { + "X": "Design matrix of shape (n_samples, n_features); a 1-D vector is treated as a single feature column.", + "intercept": "If True (default) an intercept term is fitted via an appended ones column; if False the model is forced through the origin and 0.0 is returned as the intercept.", + "y": "Target vector of length n_samples." + }, + "returns": "Tuple (coef, intercept): a numpy float64 array of one coefficient per feature, and a scalar float intercept returned separately (0.0 when intercept=False), avoiding the beta[0]-vs-coef[-1] position trap." + }, + "pca": { + "params": { + "n_components": "Number of principal components to retain; default None keeps all components.", + "x": "Data matrix of shape (n_samples, n_features)." + }, + "returns": "Tuple of four numpy float64 arrays (scores, components, mean, explained): the projected data of shape (n_samples, n_kept), the principal axes as rows of shape (n_kept, n_features), the per-feature mean used for centering, and the fraction of total variance explained by each retained component." + }, + "pearson_r": { + "params": { + "eps": "Additive guard added to the product-of-standard-deviations denominator; the default 0.0 gives NaN when either input is constant.", + "x": "First variable's sample values.", + "y": "Second variable's sample values, same length as x." + }, + "returns": "Scalar float Pearson correlation coefficient R between x and y, in [-1, 1] (not R squared); NaN for a constant input with eps=0." + }, + "precision_recall_f1": { + "params": { + "positive": "Label value treated as the positive class (default 1).", + "y_pred": "Predicted class labels, same length as y_true.", + "y_true": "True class labels.", + "zero_division": "Value returned for any of the three metrics whose denominator is zero (default 0.0, the repository convention)." + }, + "returns": "Tuple of three floats (precision, recall, f1) for the positive class, each replaced by zero_division when its denominator is empty." + }, + "predict_linear": { + "params": { + "X": "Feature matrix of shape (n_samples, n_features); a 1-D vector is treated as a single feature column.", + "coef": "Coefficient vector, one entry per feature, e.g. from ols.", + "intercept": "Scalar intercept added to the linear combination (default 0.0)." + }, + "returns": "Numpy float64 array of predictions X @ coef + intercept, one value per sample." + }, + "r2_score": { + "params": { + "eps": "Additive guard added to the total-sum-of-squares denominator; the default 0.0 gives a non-finite result for constant y_true.", + "y_pred": "Model-predicted values, same shape as y_true.", + "y_true": "Observed (true) target values." + }, + "returns": "Scalar float coefficient of determination R2 = 1 - SS_res/SS_tot (not the correlation coefficient R); for constant y_true with eps=0 it is -inf, or NaN when the fit is also perfect." + }, + "rmse": { + "params": { + "y_pred": "Model-predicted values, same shape as y_true.", + "y_true": "Observed (true) target values." + }, + "returns": "Scalar float root-mean-square error sqrt(mean((y_pred - y_true)**2)), in the units of the target." + }, + "rss": { + "params": { + "y_pred": "Model-predicted values, same shape as y_true.", + "y_true": "Observed (true) target values." + }, + "returns": "Scalar float residual sum of squares sum((y_pred - y_true)**2), in squared target units." + }, + "silhouette_score": { + "params": { + "labels": "Integer cluster assignment for each sample.", + "x": "Data matrix of shape (n_samples, n_features)." + }, + "returns": "Scalar float mean silhouette coefficient over all samples, computed from the full O(N^2) pairwise Euclidean distance matrix; 0.0 when fewer than two clusters are present, and a singleton's own-cluster distance counts as 0." + }, + "zscore": { + "params": { + "axis": "None (default) standardizes the array as a whole; an integer axis (e.g. 0) standardizes independently along that axis, i.e. per feature column.", + "eps": "Additive guard added to the standard-deviation denominator (std + eps); the default 0.0 yields NaN for a constant input.", + "x": "Input array to standardize." + }, + "returns": "Numpy float64 array of the same shape with zero mean and unit population standard deviation (ddof=0) over the chosen axis." + } + }, + "petrolib.nmr": { + "bpp_spectral_density": { + "params": { + "omega": "Angular frequency, in units reciprocal to tau_c (rad/s for tau_c in seconds).", + "tau_c": "Molecular rotational correlation time (scalar), setting the time units of the result." + }, + "returns": "Bloembergen-Purcell-Pound spectral density J = tau_c/(1 + (omega*tau_c)**2), as a numpy float64 array in the time units of tau_c." + }, + "bpp_t1_t2": { + "params": { + "dipolar_constant": "Dipolar coupling constant C multiplying both relaxation rates (default 1.0).", + "omega0": "Larmor angular frequency (scalar), in units reciprocal to tau_c.", + "tau_c": "Molecular correlation time (scalar)." + }, + "returns": "Tuple of two numpy float64 arrays (T1, T2): the BPP longitudinal and transverse relaxation times from 1/T1 = (3/10)*C*(J(w0)+4*J(2w0)) and 1/T2 = (3/20)*C*(3*J(0)+5*J(w0)+2*J(2w0)), in the time units of tau_c." + }, + "bvi_ffi": { + "params": { + "amplitude": "Amplitude of each T2 bin.", + "cutoff_ms": "Single T2 cutoff in ms separating bound from free fluid; default 33.0 (sandstone), with 90-100 ms typical for carbonates.", + "t2_ms": "T2 bin values of the distribution in ms." + }, + "returns": "Tuple of two floats (BVI, FFI): amplitude summed where T2 is below the cutoff (bound fluid) and at or above it (free fluid)." + }, + "classify_t1t2": { + "params": { + "cutoff": "T1/T2 threshold at or above which a sample is flagged hydrocarbon/bound (default 2.0 for sandstone/shale; 4.0 used for kerogen).", + "t1t2": "T1/T2 ratio values to classify." + }, + "returns": "Boolean numpy array, True where T1/T2 >= cutoff (hydrocarbon/bound fluid) and False otherwise." + }, + "combine_relaxation_times": { + "params": { + "*times_ms": "Any number of component relaxation times in ms (bulk, surface, diffusion, or other contributions) combined as parallel rates." + }, + "returns": "Combined relaxation time from 1/T = sum(1/Ti) over the given components, as a numpy float64 array in ms (broadcast over array inputs)." + }, + "cpmg_kernel": { + "params": { + "t2_grid_ms": "T2 grid values in ms defining the kernel columns.", + "t_ms": "CPMG echo (acquisition) times in ms defining the kernel rows." + }, + "returns": "2-D numpy float64 array of shape (len(t_ms), len(t2_grid_ms)) with entries K[i,j] = exp(-t_i/T2_j), the CPMG forward-model kernel." + }, + "diffusion_relaxation_rate": { + "params": { + "D": "Fluid diffusion coefficient, in a unit set consistent with G, TE, and gamma (SI recommended by the docstring).", + "G": "Magnetic field gradient strength (module convention T/m), consistent with the other inputs.", + "TE": "CPMG echo spacing, in time units consistent with D, G, and gamma.", + "gamma": "Proton gyromagnetic ratio in rad/(s*T); defaults to the CODATA constant petrolib.constants.GAMMA_H." + }, + "returns": "Diffusion contribution to the transverse relaxation rate, 1/T2_D = (gamma*G*TE)**2 * D / 12, as a numpy float64 array in the reciprocal time units of the inputs." + }, + "fit_t1": { + "params": { + "model": "Recovery model to fit: 'saturation' (default) fits M0*(1-exp(-t/T1)), 'inversion' fits M0*(1-2*exp(-t/T1)); other values raise ValueError.", + "signal": "Measured magnetization values at each recovery time.", + "t_ms": "Recovery times in ms at which the signal was measured." + }, + "returns": "Tuple of two floats (M0, T1): the fitted equilibrium magnetization amplitude and longitudinal relaxation time in ms, from scipy curve_fit (imported lazily)." + }, + "hydrogen_index": { + "params": { + "m_w": "Molecular weight of water in the reference (default 18.02 g/mol).", + "mol_weight": "Molecular weight of the fluid in g/mol.", + "n_protons": "Number of hydrogen atoms per molecule of the fluid.", + "n_w": "Protons per water molecule in the reference (default 2.0).", + "rho": "Density of the fluid, in the same units as the water reference rho_w (g/cc for the defaults).", + "rho_w": "Reference water density (default 1.0 g/cc)." + }, + "returns": "Dimensionless hydrogen index HI = (rho*n/M)/(rho_w*n_w/M_w) relative to water (pure water gives HI = 1), as a numpy float64 array." + }, + "larmor_frequency": { + "params": { + "b0_T": "Static magnetic field strength B0 in tesla.", + "gamma": "Gyromagnetic ratio in rad/(s*T); defaults to the CODATA proton value GAMMA_H." + }, + "returns": "Larmor frequency f = gamma*B0/(2*pi) in Hz, as a numpy float64 array." + }, + "mitra_short_time": { + "params": { + "d0": "Free (bulk) diffusion coefficient of the fluid (scalar; module convention um2/ms).", + "normalized": "If True (default) return the dimensionless ratio D(t)/D0; if False return the apparent diffusivity D(t) = D0 times that ratio.", + "s_over_v": "Pore surface-to-volume ratio (scalar; module convention 1/um).", + "t": "Diffusion (observation) times, in units consistent with d0 and s_over_v." + }, + "returns": "Numpy float64 array of the Mitra short-time restricted-diffusion result: the dimensionless ratio D(t)/D0 = 1 - (4/(9*sqrt(pi)))*(S/V)*sqrt(D0*t), or the apparent diffusivity D(t) in the units of d0 when normalized=False." + }, + "multiexp_decay": { + "params": { + "amplitudes": "Component amplitudes A_i of the multi-exponential model; a scalar gives a mono-exponential decay.", + "noise": "Standard deviation of zero-mean Gaussian noise added to the signal; 0.0 (default) is noise-free, any positive value requires rng.", + "rng": "Numpy random Generator used for the noise draw; required when noise > 0 (a ValueError is raised otherwise), ignored when noise is 0.", + "t2_ms": "Component T2 relaxation times in ms, paired one-to-one with the amplitudes.", + "t_ms": "Echo times in ms at which the decay is evaluated." + }, + "returns": "Numpy float64 array of the CPMG magnetization M(t) = sum_i A_i*exp(-t/T2_i) at each echo time, with Gaussian noise added when requested." + }, + "nmr_saturation": { + "params": { + "v_fluid": "NMR volume (amplitude) attributed to the fluid phase of interest.", + "v_total": "Total NMR volume (amplitude) of the pore fluid." + }, + "returns": "Fluid saturation S = V_fluid/V_total as a fraction, returned as a numpy float64 array." + }, + "partition_t1t2_map": { + "params": { + "amplitudes": "Amplitude of each T1-T2 map bin, aligned with t1t2.", + "cutoff": "T1/T2 threshold separating hydrocarbon from water signal (default 2.0).", + "t1t2": "Per-bin T1/T2 ratio values of the 2D map." + }, + "returns": "Tuple of two floats (v_hc, v_water): the amplitude summed where T1/T2 >= cutoff (hydrocarbon volume) and the remaining amplitude (water volume)." + }, + "pore_radius_from_t2": { + "params": { + "rho": "Surface relaxivity scalar (module convention um/s).", + "shape_factor": "Geometric pore-shape factor: 3 for a sphere radius (default), 2 for a cylinder radius, 6 for a sphere diameter.", + "t2_bulk": "Bulk-fluid relaxation time in ms; the default infinity reduces the formula to r = shape_factor*rho*T2.", + "t2_ms": "Transverse relaxation time in ms." + }, + "returns": "Pore radius r = shape_factor/(S/V), as a numpy float64 array in the length units implied by rho and T2 (um under the module convention)." + }, + "porosity_hi_correction": { + "params": { + "hi": "Hydrogen index of the pore fluid (dimensionless, water = 1).", + "phi_apparent": "Apparent NMR porosity (fraction), which under-reads when the fluid hydrogen index is below 1." + }, + "returns": "Hydrogen-index-corrected porosity phi_true = phi_apparent/HI as a fraction, returned as a numpy float64 array." + }, + "relaxation_rate": { + "params": { + "D": "Fluid diffusion coefficient for the gradient-diffusion term; the default 0 disables that term.", + "G": "Magnetic field gradient strength for the diffusion term (module convention T/m).", + "TE": "CPMG echo spacing for the diffusion term, in time units consistent with the other inputs.", + "gamma": "Proton gyromagnetic ratio in rad/(s*T); defaults to the CODATA GAMMA_H constant.", + "rho": "Surface relaxivity (module convention um/s) multiplying S/V; the default 0 disables the surface term.", + "s_over_v": "Pore surface-to-volume ratio (module convention 1/um); the default 0 disables the surface term.", + "t2_bulk": "Bulk-fluid transverse relaxation time (module convention ms); the default infinity disables the bulk term." + }, + "returns": "Total Brownstein-Tarr relaxation rate 1/T2 = 1/T2_bulk + rho*(S/V) + (gamma*G*TE)**2*D/12, as a numpy float64 array in the reciprocal time units of the inputs; use a consistent unit set." + }, + "sdr": { + "params": { + "a": "Calibration-specific prefactor (default 4.0; other calibrations use e.g. 4.6).", + "m": "Porosity exponent (default 4.0).", + "n": "Exponent on T2LM (default 2.0).", + "phi": "Porosity as a fraction.", + "rho_um_s": "Optional surface relaxivity in um/s; when given, T2LM is multiplied by it (the KSDR form a*phi**m*(rho*T2LM)**n); default None uses plain T2LM.", + "t2lm_ms": "Logarithmic-mean T2 of the distribution in ms." + }, + "returns": "Schlumberger-Doll-Research NMR permeability k = a*phi**m*T2LM**n in mD, as a numpy float64 array." + }, + "surface_relaxivity_from_pore": { + "params": { + "radius_um": "Known pore radius in um from an independent measurement.", + "shape_factor": "Geometric pore-shape factor (default 3.0 for a sphere radius; 1 gives the raw r/T2).", + "t2_ms": "Transverse relaxation time in ms." + }, + "returns": "Surface relaxivity rho = radius/(shape_factor*T2), as a numpy float64 array in length-per-time units of the inputs (um per ms for the documented arguments)." + }, + "surface_to_volume": { + "params": { + "rho": "Surface relaxivity scalar (module convention um/s) dividing the surface relaxation rate.", + "t2_bulk": "Bulk-fluid relaxation time in ms; the default infinity gives the surface-only form 1/(rho*T2).", + "t2_ms": "Measured transverse relaxation time in ms." + }, + "returns": "Pore surface-to-volume ratio S/V = (1/T2 - 1/T2_bulk)/rho, as a numpy float64 array; units follow the inputs (module convention 1/um)." + }, + "t1_inversion_recovery": { + "params": { + "m0": "Equilibrium magnetization amplitude M0.", + "t1_ms": "Longitudinal relaxation time T1 in ms.", + "t_ms": "Recovery (wait) times in ms." + }, + "returns": "Numpy float64 array of the inversion-recovery magnetization M(t) = M0*(1 - 2*exp(-t/T1)) at each wait time." + }, + "t1_saturation_recovery": { + "params": { + "m0": "Equilibrium magnetization amplitude M0.", + "t1_ms": "Longitudinal relaxation time T1 in ms.", + "t_ms": "Recovery (wait) times in ms." + }, + "returns": "Numpy float64 array of the saturation-recovery magnetization M(t) = M0*(1 - exp(-t/T1)) at each wait time." + }, + "t1_t2_ratio": { + "params": { + "t1": "Longitudinal relaxation time T1 values.", + "t2": "Transverse relaxation time T2 values, in the same units as t1." + }, + "returns": "Dimensionless T1/T2 ratio used for fluid typing, as a numpy float64 array." + }, + "t1t2_kernel": { + "params": { + "mode": "Recovery form on the T1 axis: 'saturation' (default) uses 1 - exp(-tw/T1), 'inversion' uses 1 - 2*exp(-tw/T1); any other value raises ValueError.", + "t1_grid_ms": "T1 grid values in ms defining the T1 columns.", + "t2_grid_ms": "T2 grid values in ms defining the T2 columns.", + "t_echo_ms": "CPMG echo times in ms for the T2 dimension of the kernel.", + "t_wait_ms": "Wait (recovery) times in ms for the T1 dimension of the kernel." + }, + "returns": "2-D numpy float64 Kronecker-product kernel kron(K_T1, K_T2) for 2D T1-T2 inversion, of shape (len(t_wait)*len(t_echo), len(t1_grid)*len(t2_grid))." + }, + "t2_apparent": { + "params": { + "D": "Fluid diffusion coefficient for the gradient-diffusion term; the default 0 disables that term.", + "G": "Magnetic field gradient strength for the diffusion term (module convention T/m).", + "TE": "CPMG echo spacing for the diffusion term, in time units consistent with the other inputs.", + "gamma": "Proton gyromagnetic ratio in rad/(s*T); defaults to the CODATA GAMMA_H constant.", + "rho": "Surface relaxivity (module convention um/s) multiplying S/V; the default 0 disables the surface term.", + "s_over_v": "Pore surface-to-volume ratio (module convention 1/um); the default 0 disables the surface term.", + "t2_bulk": "Bulk-fluid transverse relaxation time (module convention ms); the default infinity disables the bulk term." + }, + "returns": "Apparent transverse relaxation time T2, the reciprocal of relaxation_rate with the same keywords, as a numpy float64 array in the time units of the inputs." + }, + "t2_logmean": { + "params": { + "amplitude": "Amplitude of each T2 bin, used as the geometric-mean weights.", + "t2_ms": "T2 bin values of the distribution in ms." + }, + "returns": "Scalar float: the amplitude-weighted geometric mean T2LM = exp(sum(A*ln T2)/sum(A)) in ms; NaN when the total amplitude is zero or negative." + }, + "t2_partition": { + "params": { + "amplitude": "Amplitude of each T2 bin.", + "cutoffs_ms": "Ascending T2 cutoffs in ms defining the band edges; default (33.0,), the sandstone BVI/FFI cutoff; two cutoffs (e.g. 3/33 ms) give the CBW/capillary/free split.", + "fractions": "If True, each band sum is normalized by the total amplitude (when positive); False (default) returns raw amplitude sums.", + "t2_ms": "T2 bin values of the distribution in ms." + }, + "returns": "Tuple of len(cutoffs)+1 floats: amplitude summed in each band, the first for T2 below the first cutoff, then between successive cutoffs, the last for T2 at or above the final cutoff; raw sums or fractions." + }, + "timur": { + "params": { + "a": "Regional calibration prefactor (default 4800.0; other calibrations use e.g. 8581 or 1360).", + "b": "Porosity exponent (default 4.4).", + "c": "Exponent on the irreducible water saturation (default 2.0).", + "phi": "Porosity as a fraction.", + "swirr": "Irreducible water saturation as a fraction." + }, + "returns": "Timur (1968) permeability k = a*phi**b/Swirr**c in mD, as a numpy float64 array." + }, + "timur_coates": { + "params": { + "C": "Calibration constant (default 10.0): the porosity divisor in the classic form, or the multiplicative prefactor in the prefactor form.", + "bvi": "Bulk volume irreducible, the T2 amplitude below the bound-fluid cutoff.", + "ffi": "Free-fluid index, the T2 amplitude above the bound-fluid cutoff.", + "form": "Equation family: 'classic' (default) computes (phi/C)**m*(FFI/BVI)**n, 'prefactor' computes C*phi**m*(FFI/BVI)**n; other values raise ValueError.", + "m": "Porosity exponent (default 4.0).", + "n": "Exponent on the FFI/BVI ratio (default 2.0).", + "phi": "Porosity as a fraction." + }, + "returns": "Timur-Coates NMR permeability estimate in mD, as a numpy float64 array." + }, + "tortuosity": { + "params": { + "d0": "Free bulk diffusivity of the fluid.", + "d_inf": "Long-time restricted (plateau) diffusivity, in the same units as d0." + }, + "returns": "Dimensionless diffusive tortuosity tau = D0/D_inf, as a numpy float64 array." + }, + "total_porosity": { + "params": { + "amplitude": "T2 distribution bin amplitudes in porosity units." + }, + "returns": "Scalar float: total NMR porosity as the summed T2 amplitude phi = sum(A), a fraction when the amplitudes are porosity fractions." + } + }, + "petrolib.nuclear": { + "activity": { + "params": { + "half_life": "Half-life T_half of the species, in the chosen time unit.", + "n": "Number of radioactive atoms N present." + }, + "returns": "Radioactive activity A = lambda*N with lambda = ln(2)/T_half, in decays per unit time (reciprocal of the half-life time unit); numpy float64 array." + }, + "attenuation_map": { + "params": { + "i": "Measured intensity values, e.g. an image of transmitted intensities.", + "i_ref": "Reference intensity the measurement is ratioed against, e.g. an open-beam or reference image." + }, + "returns": "Optical-density attenuation -ln(I/I_ref), dimensionless; the intensity ratio is clipped below at 1e-30 to guard against zeros. Numpy float64 array." + }, + "average_lethargy_gain": { + "params": { + "a_mass": "Atomic mass number A of the scattering nucleus; the hydrogen limit A=1 returns xi = 1." + }, + "returns": "Mean logarithmic energy decrement per collision xi = 1 + alpha/(1-alpha)*ln(alpha), dimensionless, with xi = 1 returned where alpha = 0; numpy float64 array." + }, + "beer_lambert": { + "params": { + "i0": "Incident (unattenuated) beam intensity, in arbitrary units.", + "mu": "Linear attenuation coefficient, in reciprocal units of the path length x (e.g. 1/cm).", + "x": "Path length through the attenuating material, in units consistent with mu." + }, + "returns": "Transmitted intensity I = I0*exp(-mu*x), in the same units as i0; numpy float64 array." + }, + "cgr_api": { + "params": { + "coeff": "Tuple (ck, cth) of API sensitivity coefficients for K and Th; default (16.0, 4.0).", + "k_pct": "Potassium concentration in weight percent.", + "th_ppm": "Thorium concentration in parts per million." + }, + "returns": "Computed (uranium-free) gamma-ray level CGR = ck*K + cth*Th in API units; numpy float64 array." + }, + "co_forward_3phase": { + "params": { + "c_gas": "Carbon contribution per unit gas-filled porosity, default 0.75.", + "c_mat": "Carbon contribution per unit matrix volume, default 0.1.", + "c_oil": "Carbon contribution per unit oil-filled porosity, default 0.85.", + "o_mat": "Oxygen contribution per unit matrix volume, default 0.55.", + "o_w": "Oxygen contribution per unit water-filled porosity, default 1.0.", + "phi": "Porosity as a volume fraction of the bulk rock (0-1).", + "sg": "Gas saturation as a fraction of the pore volume (0-1).", + "so": "Oil saturation as a fraction of the pore volume (0-1).", + "sw": "Water saturation as a fraction of the pore volume (0-1)." + }, + "returns": "Modelled inelastic carbon-oxygen ratio COR = C/(O + 1e-9) for the three-phase saturation state, dimensionless, with a 1e-9 guard against a zero oxygen term; numpy float64 array." + }, + "co_ratio": { + "params": { + "c_yield": "Carbon inelastic gamma-ray yield Y_C from the spectroscopy measurement.", + "o_yield": "Oxygen inelastic gamma-ray yield Y_O from the spectroscopy measurement." + }, + "returns": "Carbon-oxygen ratio COR = Y_C/Y_O, dimensionless; numpy float64 array." + }, + "collision_parameter": { + "params": { + "a_mass": "Atomic mass number A of the target nucleus, dimensionless." + }, + "returns": "Neutron collision parameter alpha = ((A-1)/(A+1))**2, the minimum post-collision energy ratio, dimensionless; numpy float64 array." + }, + "compensated_neutron_porosity": { + "params": { + "a": "Constant term of the quadratic count-ratio calibration, default 0.0.", + "b": "Linear calibration coefficient multiplying ln(near/far), default -30.0.", + "c": "Quadratic calibration coefficient multiplying ln(near/far) squared, default 45.0.", + "far": "Far-detector neutron count rate.", + "near": "Near-detector neutron count rate." + }, + "returns": "Compensated neutron porosity phi = a + b*lnR + c*lnR**2 with R = near/far, in porosity units (p.u.); numpy float64 array." + }, + "counting_precision": { + "params": { + "counts": "Number of recorded counts N, assumed Poisson-distributed." + }, + "returns": "Relative counting precision 1/sqrt(N), the dimensionless fractional statistical uncertainty; numpy float64 array." + }, + "counting_sigma": { + "params": { + "counts": "Number of counts N underlying the measurement, assumed Poisson-distributed.", + "value": "Measured quantity whose statistical uncertainty is being estimated, in any units." + }, + "returns": "Absolute one-sigma counting uncertainty value/sqrt(N), in the same units as value; numpy float64 array." + }, + "decay_constant": { + "params": { + "half_life": "Half-life T_half of the radioactive species, in any time unit." + }, + "returns": "Radioactive decay constant lambda = ln(2)/T_half, in reciprocal units of the half-life time unit; numpy float64 array." + }, + "delta_sw_timelapse": { + "params": { + "phi": "Porosity as a volume fraction of the bulk rock (0-1).", + "sigma_base": "Formation capture cross-section from the base (initial) survey in c.u.", + "sigma_hc": "Capture cross-section of the hydrocarbon phase in c.u.", + "sigma_mon": "Formation capture cross-section from the monitor (repeat) survey in c.u.", + "sigma_w_base": "Capture cross-section of the formation water at the base survey in c.u.", + "sigma_w_mon": "Capture cross-section of the water at the monitor survey in c.u., e.g. changed by injected brine.", + "sw_base": "Water saturation at the base survey as a fraction of the pore volume." + }, + "returns": "Monitor-survey water saturation Sw_mon as a fraction of the pore volume, computed from the time-lapse Sigma difference; numpy float64 array, not clipped." + }, + "density_from_count": { + "params": { + "count": "Measured gamma count rate, in the same units as the calibration n0.", + "mu_mass": "Mass attenuation coefficient in cm2/g, default 0.06.", + "n0": "Calibration count rate at zero density, default 1e6.", + "spacing_cm": "Source-to-detector spacing in cm, default 30.0." + }, + "returns": "Formation bulk density rho = ln(N0/N)/(mu_mass*spacing) in g/cc, the inverse of gamma_count; numpy float64 array." + }, + "dual_detector_density": { + "params": { + "a": "Calibration intercept of the count-ratio transform, in density units (g/cc); required keyword.", + "b": "Calibration slope multiplying ln(near/far); required keyword.", + "far": "Far-detector gamma count rate.", + "near": "Near-detector gamma count rate." + }, + "returns": "Bulk density rho = a + b*ln(near/far) from the dual-detector count-ratio calibration, in g/cc; numpy float64 array." + }, + "electron_density_index": { + "params": { + "a": "Atomic or molecular mass A in g/mol.", + "rho_m": "Actual mass density of the element or compound in g/cc.", + "z": "Atomic number Z, or the total electron count per formula unit for a compound." + }, + "returns": "Electron density rho_e = (2*Z/A)*rho_m of the single element or compound, in g/cc-equivalent electron-density units; numpy float64 array." + }, + "electron_density_mixture": { + "params": { + "a": "Atomic masses A_i of the constituents in g/mol.", + "mass_frac": "Mass fractions w_i of the constituents (0-1).", + "rho_m": "Bulk mass density of the mixture in g/cc.", + "z": "Atomic numbers Z_i of the mixture constituents." + }, + "returns": "Mixture electron density rho_e = 2*sum(w_i*Z_i/A_i)*rho_m in g/cc-equivalent units; numpy float64. The constituent sum is reduced with numpy sum, so 1-D constituent inputs give a single value per rho_m." + }, + "gamma_count": { + "params": { + "mu_mass": "Mass attenuation coefficient in cm2/g, default 0.06.", + "n0": "Calibration count rate at zero density, default 1e6.", + "rho": "Formation bulk density in g/cc.", + "spacing_cm": "Source-to-detector spacing in cm, default 30.0." + }, + "returns": "Gamma detector count rate N = N0*exp(-mu_mass*rho*spacing), in the same count units as n0; numpy float64 array." + }, + "gr_api": { + "params": { + "coeff": "Tuple (ck, cu, cth) of API sensitivity coefficients for K, U, and Th; default (16.0, 8.0, 4.0), with (16.32, 8.09, 3.93) as an alternative calibration.", + "k_pct": "Potassium concentration in weight percent.", + "th_ppm": "Thorium concentration in parts per million.", + "u_ppm": "Uranium concentration in parts per million." + }, + "returns": "Total spectral gamma-ray level SGR = ck*K + cu*U + cth*Th in API units; numpy float64 array." + }, + "hi_mix": { + "params": { + "hi_fluid": "Hydrogen index of the pore fluid, default 1.0 (water).", + "hi_matrix": "Hydrogen index of the rock matrix, default 0.0.", + "phi": "Porosity as a volume fraction of the bulk rock (0-1)." + }, + "returns": "Volumetric bulk hydrogen index HI = phi*HI_fluid + (1-phi)*HI_matrix, dimensionless; numpy float64 array." + }, + "hydrogen_index_chemical": { + "params": { + "mol_weight": "Molecular weight of the compound in g/mol.", + "mw_ref": "Molecular weight of the reference fluid in g/mol, default 18.015 (water).", + "n_protons": "Number of hydrogen atoms per molecule of the compound.", + "n_ref": "Hydrogen atoms per molecule of the reference fluid, default 2.0 (water).", + "rho": "Density of the fluid or compound, in the same units as rho_ref (g/cc with the defaults).", + "rho_ref": "Density of the reference fluid, default 1.0 (fresh water, g/cc)." + }, + "returns": "Hydrogen index HI = (rho*n_protons/M) / (rho_ref*n_ref/mw_ref), dimensionless and normalised so fresh water is 1.0; numpy float64 array." + }, + "hydrogen_index_fluid": { + "params": { + "fluid": "Pore-fluid selector: 'water' returns 1.0, 'gas' returns rho_gas, 'oil' returns hi_oil; other values raise ValueError.", + "hi_oil": "Hydrogen index value returned when fluid is 'oil', default 0.9.", + "rho_gas": "Hydrogen index value returned when fluid is 'gas', default 0.2." + }, + "returns": "Hydrogen index of the selected pore fluid, dimensionless with fresh water = 1.0 (the HI itself, not HI times porosity); a plain Python float." + }, + "macroscopic_sigma": { + "params": { + "micro_barns": "Microscopic capture cross-sections sigma_i of the constituents in barns (1 barn = 1e-24 cm2).", + "number_densities": "Atomic number densities N_i of the constituents in atoms per cm3.", + "units": "Output unit selector: 'cu' returns capture units (cm^-1 times 1e3), 'cm' returns cm^-1; other values raise ValueError." + }, + "returns": "Macroscopic capture cross-section Sigma = sum of N_i*sigma_i summed over all constituents, in c.u. or cm^-1 depending on units; a single numpy float64 value." + }, + "moderating_power": { + "params": { + "a_mass": "Atomic mass number A of the moderating nucleus, used to compute the lethargy gain xi.", + "sigma_s": "Macroscopic scattering cross-section Sigma_s of the material; the result carries its units." + }, + "returns": "Moderating (slowing-down) power xi*Sigma_s, in the same units as sigma_s; numpy float64 array." + }, + "mu_from_intensity": { + "params": { + "i": "Transmitted intensity measured after the path length x.", + "i0": "Incident intensity before attenuation.", + "x": "Path length through the material; the result is in its reciprocal units." + }, + "returns": "Linear attenuation coefficient mu = ln(I0/I)/x, in reciprocal units of x; numpy float64 array." + }, + "number_density": { + "params": { + "atomic_mass": "Atomic mass A of the element in g/mol.", + "rho_g_cc": "Bulk density of the material in g/cc.", + "wfrac": "Mass (weight) fraction of the element in the material (0-1)." + }, + "returns": "Atomic number density N = rho*NA*w/A in atoms per cm3, using Avogadro's number NA = 6.022e23; numpy float64 array." + }, + "phi_from_hi": { + "params": { + "hi_fluid": "Hydrogen index of the pore fluid, default 1.0 (water).", + "hi_log": "Measured bulk hydrogen-index log value, dimensionless.", + "hi_matrix": "Hydrogen index of the rock matrix (required keyword)." + }, + "returns": "Porosity phi = (HI - HI_matrix)/(HI_fluid - HI_matrix) as a volume fraction, the inverse of hi_mix; numpy float64 array." + }, + "phi_from_ls": { + "params": { + "e_mev": "Neutron energy in MeV.", + "e_ref": "Reference neutron energy in MeV, default 4.5.", + "ls": "Neutron slowing-down length in cm.", + "ls0": "Reference slowing-down length in cm at zero porosity and the reference energy, default 20.0." + }, + "returns": "Porosity as a volume fraction, obtained by inverting the empirical slowing-down-length relation of slowing_down_length_empirical; numpy float64 array." + }, + "phi_hi_correction": { + "params": { + "hi": "Hydrogen index of the pore fluid, dimensionless with water = 1.0.", + "phi_apparent": "Apparent (uncorrected) porosity from a neutron or NMR measurement, as a fraction or p.u." + }, + "returns": "Hydrogen-index-corrected porosity phi = phi_apparent/HI, in the same units as phi_apparent; numpy float64 array." + }, + "phi_n_from_lm": { + "params": { + "lithology": "Matrix endpoint selector: 'limestone', 'sandstone', or 'dolomite', choosing the built-in (matrix, fluid) migration-length endpoints in cm; other values raise ValueError.", + "lm_star": "Measured (apparent) migration length Lm in cm." + }, + "returns": "Neutron porosity phi = (Lm_ma - Lm)/(Lm_ma - Lm_fl) by linear interpolation between the lithology's matrix and fluid endpoints, as a dimensionless fraction; numpy float64 array." + }, + "pnc_decay": { + "params": { + "background": "Constant background count level added to the exponential decay, default 0.0.", + "n0": "Initial count level N0 at time zero, in arbitrary count units.", + "sigma_cu": "Formation capture cross-section in c.u.; converted internally to cm^-1 by multiplying by 1e-3.", + "t_us": "Time after the neutron burst in microseconds." + }, + "returns": "Pulsed-neutron capture count rate N(t) = N0*exp(-Sigma*v*t) + background at each time, using thermal-neutron velocity v = 2.2e5 cm/s, in the same count units as n0; numpy float64 array." + }, + "radioactive_decay": { + "params": { + "half_life": "Half-life T_half of the species, in the same time units as t.", + "n0": "Initial quantity N0 (e.g. number of atoms or count rate) at time zero.", + "t": "Elapsed time, in the same time units as half_life." + }, + "returns": "Remaining quantity N = N0*exp(-ln(2)*t/T_half), in the same units as n0; numpy float64 array." + }, + "rhob_from_rhoe": { + "params": { + "rho_e": "Electron density in g/cc-equivalent units." + }, + "returns": "Apparent bulk density rho_b = 1.0704*rho_e - 0.1883 in g/cc, the standard gamma-gamma tool transform; numpy float64 array." + }, + "rhoe_from_rhob": { + "params": { + "rho_b": "Bulk density in g/cc." + }, + "returns": "Electron density rho_e = (rho_b + 0.1883)/1.0704 in g/cc-equivalent units, the inverse of rhob_from_rhoe; numpy float64 array." + }, + "sigma_forward": { + "params": { + "phi": "Porosity as a volume fraction of the bulk rock (0-1).", + "sigma_hc": "Capture cross-section of the hydrocarbon phase in c.u., default 21.0.", + "sigma_ma": "Capture cross-section of the clean matrix in capture units (c.u.), default 10.0.", + "sigma_sh": "Capture cross-section of the shale fraction in c.u., default 27.0.", + "sigma_w": "Capture cross-section of the formation water in c.u., default 55.0.", + "sw": "Water saturation as a fraction of the pore volume (0-1).", + "vsh": "Shale volume fraction of the bulk rock (0-1); default 0.0 gives the clean two-phase form." + }, + "returns": "Volumetric bulk capture cross-section Sigma of the rock in capture units (c.u., 1 c.u. = 1e-3 per cm), as a numpy float64 array broadcast over the inputs." + }, + "sigma_forward_3phase": { + "params": { + "phi": "Porosity as a volume fraction of the bulk rock (0-1).", + "sg": "Gas saturation as a fraction of the pore volume (0-1).", + "sigma_gas": "Capture cross-section of the gas phase in c.u., default 8.0.", + "sigma_ma": "Capture cross-section of the matrix in c.u., default 10.0.", + "sigma_oil": "Capture cross-section of the oil phase in c.u., default 22.0.", + "sigma_w": "Capture cross-section of the formation water in c.u., default 80.0.", + "so": "Oil saturation as a fraction of the pore volume (0-1).", + "sw": "Water saturation as a fraction of the pore volume (0-1)." + }, + "returns": "Three-phase (oil, gas, water) volumetric bulk capture cross-section Sigma in c.u., as a numpy float64 array broadcast over the inputs." + }, + "sigma_from_decay_fit": { + "params": { + "counts": "Measured counts at each sample time; must be positive because the fit is on ln(counts).", + "fit_window": "Optional (start, stop) integer index pair selecting the sample slice used for the fit; None fits all samples.", + "t_us": "Sample times of the measured decay curve in microseconds." + }, + "returns": "Capture cross-section Sigma in c.u., obtained from the slope of a log-linear fit of ln(counts) versus time via Sigma = -slope/v * 1e3 with v the thermal-neutron velocity; a single numpy float64 value." + }, + "sigma_from_tau": { + "params": { + "tau_us": "Thermal-neutron decay time constant tau in microseconds." + }, + "returns": "Capture cross-section Sigma = 4550/tau in c.u.; numpy float64 array." + }, + "sigma_sensitivity": { + "params": { + "phi": "Porosity as a volume fraction of the bulk rock (0-1).", + "sigma_hc": "Capture cross-section of the hydrocarbon phase in c.u.", + "sigma_w": "Capture cross-section of the formation water in c.u." + }, + "returns": "Saturation sensitivity dSigma/dSw = phi*(Sigma_w - Sigma_hc) in c.u. per unit change in water saturation; numpy float64 array." + }, + "sigma_w_from_salinity": { + "params": { + "model": "Correlation selector: 'fitz2023' uses (22 + 750*s)*(1 - 8e-4*(T-75)) with s = ppm/1e6; 'linear450k' interpolates linearly from 22 to 220 c.u. at 450,000 ppm; other values raise ValueError.", + "ppm_nacl": "NaCl salinity of the brine in parts per million.", + "temperature_c": "Brine temperature in degrees Celsius used by the fitz2023 model, default 75.0; ignored by the linear450k model." + }, + "returns": "Brine capture cross-section Sigma_w in c.u. for the chosen correlation; numpy float64 array." + }, + "slowing_down_length_empirical": { + "params": { + "e_mev": "Source neutron energy in MeV.", + "e_ref": "Reference neutron energy in MeV, default 4.5.", + "ls0": "Reference slowing-down length in cm at zero porosity and the reference energy, default 20.0.", + "phi": "Porosity as a volume fraction of the bulk rock (0-1)." + }, + "returns": "Empirical neutron slowing-down length Ls = Ls0*(1 - 0.6*phi)*sqrt(E/E_ref) in cm; numpy float64 array." + }, + "so_from_co": { + "params": { + "clip": "Tuple (low, high) of bounds applied to the interpolated saturation with numpy clip, default (0.0, 1.0); pass None to return the unclipped value.", + "cor": "Measured carbon-oxygen ratio, dimensionless.", + "cor_oil": "C/O ratio endpoint corresponding to 100 percent oil saturation.", + "cor_water": "C/O ratio endpoint corresponding to 100 percent water saturation." + }, + "returns": "Oil saturation So = (COR - COR_w)/(COR_o - COR_w) as a fraction of the pore volume by linear endpoint interpolation, clipped to (0, 1) by default; numpy float64 array." + }, + "spine_ribs": { + "params": { + "rho_ls": "Long-spacing detector apparent density in g/cc.", + "rho_ss": "Short-spacing detector apparent density in g/cc.", + "rib_coeffs": "Tuple (c0, c1, c2) of rib-polynomial coefficients giving drho = c0 + c1*d + c2*d**2 with d = rho_ls - rho_ss; default (0.0, 1.0, 0.0)." + }, + "returns": "Tuple of two numpy float64 arrays (rho_b, drho): the compensated bulk density rho_b = rho_ls + drho in g/cc, and the density correction drho in g/cc from the rib polynomial." + }, + "sw_from_sigma": { + "params": { + "clip": "Tuple (low, high) of bounds applied to the computed saturation with numpy clip, default (0.0, 1.0); pass None to skip clipping.", + "phi": "Porosity as a volume fraction of the bulk rock (0-1).", + "sigma_hc": "Capture cross-section of the hydrocarbon phase in c.u. (required keyword).", + "sigma_ma": "Capture cross-section of the clean matrix in c.u. (required keyword).", + "sigma_sh": "Capture cross-section of the shale fraction in c.u., default 27.0.", + "sigma_t": "Measured total formation capture cross-section Sigma in c.u.", + "sigma_w": "Capture cross-section of the formation water in c.u. (required keyword).", + "vsh": "Shale volume fraction of the bulk rock (0-1); default 0.0 for the clean form." + }, + "returns": "Water saturation Sw as a fraction of the pore volume, obtained by inverting the volumetric Sigma balance and clipped to the clip bounds by default; numpy float64 array." + }, + "tau_from_sigma": { + "params": { + "sigma_cu": "Capture cross-section Sigma in c.u." + }, + "returns": "Thermal-neutron decay time tau = 4550/Sigma in microseconds; numpy float64 array." + }, + "toc_from_yield": { + "params": { + "calib": "Calibration multiplier converting the carbon yield to a TOC value.", + "y_toc": "Organic-carbon spectroscopy yield attributed to TOC." + }, + "returns": "Total organic carbon TOC = calib*Y_TOC, in the units set by the calibration factor (typically weight fraction or wt%); numpy float64 array." + }, + "transport_length_mix": { + "params": { + "lengths": "Transport or migration lengths L_i of the components, e.g. in cm.", + "vol_fracs": "Volume fractions v_i of the mixture components (0-1)." + }, + "returns": "Harmonic-mean mixture transport/migration length L = 1/sum(v_i/L_i), in the units of lengths; numpy float64. The sum reduces over all elements, so 1-D inputs yield a single value." + }, + "weights_to_yields": { + "params": { + "fy2w": "Yield-to-weight conversion factor FY2W for the element.", + "s": "Spectral sensitivity factor S for the element.", + "w": "Elemental weight fraction W, dimensionless." + }, + "returns": "Relative spectroscopy yield Y = W/(FY2W*S), the inverse of yields_to_weights; numpy float64 array." + }, + "yields_to_weights": { + "params": { + "fy2w": "Yield-to-weight conversion factor FY2W for the element.", + "s": "Spectral sensitivity factor S for the element.", + "y": "Measured relative elemental spectroscopy yield Y." + }, + "returns": "Elemental weight fraction W = FY2W*S*Y, dimensionless; numpy float64 array." + } + }, + "petrolib.porosity_lithology": { + "boyle_grain_volume": { + "params": { + "p1": "Initial gas pressure before expansion, in any pressure unit consistent with p2 (only the ratio p1/p2 is used).", + "p2": "Equilibrium gas pressure after expansion into the sample cell, in the same unit as p1.", + "v_cell": "Volume of the sample cell of the double-cell helium porosimeter, in any volume unit consistent with v_expansion.", + "v_expansion": "Volume of the expansion (reference) cell, in the same unit as v_cell." + }, + "returns": "Grain volume V_grain = V_cell - V_exp/(p1/p2 - 1), in the same volume unit as the cell volumes. Numpy float64 array broadcast over the inputs." + }, + "boyle_porosity": { + "params": { + "rho_bulk": "Bulk density of the sample, in any density unit consistent with rho_grain (g/cc in the corpus).", + "rho_grain": "Grain density of the sample, in the same unit as rho_bulk." + }, + "returns": "Boyle's-law density porosity phi = 1 - rho_bulk/rho_grain as a v/v fraction. Numpy float64 array broadcast over the inputs." + }, + "bulk_density": { + "params": { + "phi": "Porosity (v/v fraction).", + "rho_fl": "Pore-fluid density in the same unit as rho_ma; default 1.0.", + "rho_k": "Kerogen density in the same unit as the other densities; default 1.30.", + "rho_ma": "Matrix (grain) density, in g/cc or kg/m3 (units must be consistent across all densities).", + "v_k": "Kerogen volume fraction (v/v); default 0.0 reduces the model to the two-component matrix-plus-fluid form." + }, + "returns": "Forward-modelled bulk density rho_b = (1-phi-v_k)*rho_ma + v_k*rho_k + phi*rho_fl, in the common density unit. Numpy float64 array broadcast over the inputs." + }, + "bulk_volume_water": { + "params": { + "phi": "Porosity (v/v fraction).", + "sw": "Water saturation as a fraction of pore space (v/v)." + }, + "returns": "Bulk volume water BVW = phi*Sw (v/v fraction of bulk rock), constant within a Buckles zone. Numpy float64 array broadcast over the inputs." + }, + "combine_clay_indicators": { + "params": { + "*vcl": "One or more clay-volume indicator arrays (v/v fractions), all of the same shape, to be combined elementwise.", + "how": "Combination rule: 'mean' averages the indicators, 'min' takes the elementwise minimum (most optimistic), 'max' the elementwise maximum (upper bound)." + }, + "returns": "Combined clay-volume estimate (v/v fraction) with the shape of the input indicators. Numpy float64 array; raises ValueError for an unknown how." + }, + "ct_porosity": { + "params": { + "clip": "Clip range (lo, hi) applied to the porosity; default None (unclipped).", + "mu": "Measured CT attenuation of the sample or voxel, in any consistent attenuation unit (e.g. CT number).", + "mu_fluid": "CT attenuation of the pure-fluid (100 percent porosity) endpoint, same unit as mu.", + "mu_grain": "CT attenuation of the pure-grain (zero-porosity) endpoint, same unit as mu." + }, + "returns": "Porosity phi = (mu_grain-mu)/(mu_grain-mu_fluid) as a v/v fraction. Numpy float64 array broadcast over the inputs." + }, + "ct_saturation": { + "params": { + "clip": "Clip range (lo, hi) applied to the saturation; default (0.0, 1.0), pass None to disable.", + "mu": "Measured CT attenuation of the partially saturated state, in any consistent attenuation unit.", + "mu_dry": "CT attenuation of the dry (zero-saturation) state, same unit as mu.", + "mu_sat": "CT attenuation of the fully saturated state, same unit as mu." + }, + "returns": "Fluid saturation S = (mu-mu_dry)/(mu_sat-mu_dry) as a v/v fraction, clipped to [0,1] by default. Numpy float64 array broadcast over the inputs." + }, + "density_porosity": { + "params": { + "clip": "Clip range (lo, hi) applied to the porosity; default None (unclipped, clipping is opt-in).", + "rho_b": "Bulk density log values in g/cc.", + "rho_fl": "Pore-fluid density endpoint in g/cc; defaults to fresh water 1.0 (brines 1.01-1.03 are passed explicitly).", + "rho_ma": "Matrix (grain) density endpoint in g/cc; defaults to sandstone 2.65 (carbonate 2.71 and dolomite 2.85 are passed explicitly)." + }, + "returns": "Density porosity phi_D = (rho_ma-rho_b)/(rho_ma-rho_fl) as a v/v fraction. Numpy float64 array broadcast over the inputs." + }, + "effective_porosity": { + "params": { + "clip": "Clip range (lo, hi) applied to phi_e; default (0.0, None) clips to non-negative, pass None for the unclipped structural-shale-porosity form.", + "phi_sh": "Total porosity of the shale endpoint (v/v); explicit, corpus values include 0.10, 0.27, and 0.30.", + "phi_t": "Total porosity (v/v fraction).", + "vsh": "Shale volume fraction (v/v)." + }, + "returns": "Shale-corrected effective porosity phi_e = phi_t - Vsh*phi_sh (v/v fraction), non-negative by default. Numpy float64 array broadcast over the inputs." + }, + "electron_density_to_bulk": { + "params": { + "a": "Multiplicative calibration constant; default 1.0704 (the standard tool calibration).", + "b": "Additive calibration constant in g/cc; default -0.1883 (the standard tool calibration).", + "rho_e": "Electron density from the density tool, in g/cc." + }, + "returns": "Calibrated bulk density rho_b = a*rho_e + b in g/cc. Numpy float64 array broadcast over the input." + }, + "fluid_density": { + "params": { + "rhos": "Phase densities in g/cc (or any consistent density unit), aligned with saturations along the last axis.", + "saturations": "Phase saturations (fractions of pore space, v/v), with the phases along the last axis." + }, + "returns": "Saturation-weighted pore-fluid density rho_fl = sum(S_i*rho_i), reduced along the last axis, in the unit of rhos. Numpy float64 array." + }, + "fluid_summation_porosity": { + "params": { + "bv_gas": "Bulk-volume gas fraction (v/v); defaults to 0.0.", + "bv_oil": "Bulk-volume oil fraction from fluid summation (v/v).", + "bv_water": "Bulk-volume water fraction (v/v)." + }, + "returns": "Fluid-summation porosity phi = BV_oil + BV_water + BV_gas as a v/v fraction. Numpy float64 array broadcast over the inputs." + }, + "gamma_ray_index": { + "params": { + "clip": "Clip range (lo, hi) applied to the index; default (0.0, 1.0), pass None to leave it unclipped.", + "gr": "Measured gamma-ray log values, in any consistent GR unit (API or GAPI).", + "gr_clean": "Gamma-ray reading of the clean (shale-free) endpoint, in the same unit as gr.", + "gr_shale": "Gamma-ray reading of the 100 percent shale endpoint, in the same unit as gr." + }, + "returns": "Dimensionless gamma-ray shale index IGR = (GR-GR_clean)/(GR_shale-GR_clean), clipped to [0,1] by default. Numpy float64 array broadcast over the inputs." + }, + "gravimetric_porosity": { + "params": { + "m_dry": "Dry sample mass, in a mass unit consistent with rho_fluid and v_bulk (the corpus uses g with g/cc and cm3, or kg with kg/m3 and m3).", + "m_sat": "Fully fluid-saturated sample mass, in the same unit as m_dry.", + "rho_fluid": "Density of the saturating fluid (default 1.0), in units consistent with the masses and bulk volume.", + "v_bulk": "Bulk volume of the sample, in a volume unit consistent with the masses and rho_fluid." + }, + "returns": "Mass-balance porosity phi = (m_sat-m_dry)/(rho_fluid*V_bulk) as a v/v fraction. Numpy float64 array broadcast over the inputs." + }, + "hydrocarbon_pore_volume": { + "params": { + "phi": "Porosity (v/v fraction).", + "sw": "Water saturation as a fraction of pore space (v/v)." + }, + "returns": "Hydrocarbon pore volume fraction HCPV = phi*(1-Sw) (v/v fraction of bulk rock). Numpy float64 array broadcast over the inputs." + }, + "interval_thickness": { + "params": { + "depth": "Depth values of the samples, in any length unit; irregular sampling is handled via the midpoint gradient.", + "flag": "Boolean mask selecting the samples whose thickness contributions are summed." + }, + "returns": "Scalar float: summed thickness sum(|dz|) over flagged samples, in the length unit of depth, where dz is the absolute midpoint gradient of depth (a single-sample input uses dz=1)." + }, + "kerogen_mass_fraction": { + "params": { + "k": "Kerogen-to-carbon conversion factor (the kerogen carbon fraction is 1/k); default 1.2, with 1.25 used by some sources.", + "toc": "Total organic carbon content; the output carries the same unit (mass fraction or wt%)." + }, + "returns": "Organic-matter (kerogen) mass fraction OM = k*TOC, in the same unit as toc. Numpy float64 array broadcast over the input." + }, + "kerogen_volume_from_toc": { + "params": { + "carbon_frac": "Carbon mass fraction of kerogen; default 0.80.", + "rho_k": "Kerogen density in g/cc; default 1.30.", + "rho_ref": "Reference density in g/cc: the bulk density in most source models, or the matrix density in the TMALI form.", + "toc": "Total organic carbon as a mass fraction (divide wt% by 100 before passing)." + }, + "returns": "Kerogen volume fraction V_k = (TOC/carbon_frac)*rho_ref/rho_k (v/v fraction). Numpy float64 array broadcast over the inputs." + }, + "log_response": { + "params": { + "endpoints": "Endpoint response values of each component, as an (n_logs, n_minerals) matrix or a length-n_minerals vector, matrix-multiplied with volumes.", + "volumes": "Mineral/component volume fractions vector of length n_minerals (v/v)." + }, + "returns": "Synthesized tool response(s) M = endpoints @ volumes, in the units of the endpoint values (e.g. API for matrix GR, barns/e for PEF). Numpy float64 array (scalar-like for a single log)." + }, + "matrix_density_from_masses": { + "params": { + "rho": "Grain densities of the components in g/cc, aligned with w along the last axis.", + "rho_kerogen": "Kerogen density in g/cc (default 1.43).", + "w": "Mineral weight (mass) fractions, with the components along the last axis.", + "w_kerogen": "Optional kerogen mass fraction added as an extra harmonic term (default 0.0)." + }, + "returns": "Mass-weighted (harmonic) matrix density 1/(sum(w_i/rho_i) + w_kerogen/rho_kerogen) in g/cc, reduced along the last axis. Numpy float64 array." + }, + "matrix_density_from_volumes": { + "params": { + "rho": "Grain densities of the components in g/cc, aligned with v along the last axis.", + "v": "Mineral volume fractions (v/v), with the components along the last axis." + }, + "returns": "Volume-weighted (arithmetic) grain density sum(v_i*rho_i) in g/cc, reduced along the last axis. Numpy float64 array." + }, + "multimineral_solve": { + "params": { + "closure": "If True (default), appends a unity-sum row weighted by closure_weight so the volumes obey sum(v)=1; not used by the simplex method, which enforces the constraint by projection.", + "closure_weight": "Weight of the appended unity-sum closure row; default 1e3 (some corpus copies use 100).", + "endpoints": "Tool response matrix of shape (n_logs, n_minerals): each column holds one mineral's endpoint response on every log.", + "measured": "Vector of measured log responses, one value per tool (length n_logs), in each tool's own unit.", + "method": "Solver: 'lstsq' (unconstrained least squares, default), 'nnls' (non-negative least squares, lazily imports scipy), or 'simplex' (projected-gradient descent onto v>=0 with sum(v)=1).", + "nonneg": "If True, clips a 'lstsq' solution to be non-negative; it has no effect for the 'nnls' or 'simplex' methods.", + "sigma": "Optional per-log uncertainties (standard deviations); each equation is scaled by 1/sigma, i.e. inverse-variance weighting. None disables weighting." + }, + "returns": "Mineral volume fractions v (length n_minerals, v/v) solving endpoints @ v = measured under the chosen constraints. Numpy float64 array; raises ValueError for an unknown method." + }, + "net_to_gross": { + "params": { + "depth": "Depth values of the samples, in any length unit.", + "gross_flag": "Optional boolean mask defining the gross interval (e.g. a reservoir flag); None (default) uses the whole logged interval as gross.", + "net_flag": "Boolean mask flagging the net (e.g. pay) samples." + }, + "returns": "Scalar float: dimensionless net-to-gross ratio, net thickness divided by gross thickness (both computed with interval_thickness)." + }, + "neutron_density_porosity": { + "params": { + "method": "Combination rule: 'rms' gives the gas-corrected root-mean-square sqrt((phi_n**2+phi_d**2)/2) (default); 'mean' the arithmetic average (phi_n+phi_d)/2.", + "phi_d": "Density porosity (v/v fraction).", + "phi_n": "Neutron porosity (v/v fraction)." + }, + "returns": "Combined neutron-density porosity (v/v fraction). Numpy float64 array broadcast over the inputs; raises ValueError for an unknown method." + }, + "pay_flag": { + "params": { + "phi": "Porosity curve (v/v fraction), tested against phi_cut.", + "phi_cut": "Minimum porosity cutoff (v/v); a sample must have phi >= phi_cut. Default 0.08.", + "sw": "Optional water saturation curve (v/v); when given, the condition sw <= sw_cut is added, when None it is skipped.", + "sw_cut": "Maximum water saturation cutoff (v/v), applied only if sw is given. Default 0.60.", + "vsh": "Optional shale volume curve (v/v); when given, the condition vsh <= vsh_cut is added, when None it is skipped.", + "vsh_cut": "Maximum shale volume cutoff (v/v), applied only if vsh is given. Default 0.40." + }, + "returns": "Boolean numpy array, True where the sample passes all supplied cutoffs (net pay), False otherwise." + }, + "porosity_from_volumes": { + "params": { + "v_bulk": "Sample bulk volume, in any volume unit consistent with v_grain; note this is the first argument (fixed order).", + "v_grain": "Sample grain (solid) volume, in the same unit as v_bulk." + }, + "returns": "Core porosity phi = (V_bulk - V_grain)/V_bulk as a v/v fraction. Numpy float64 array broadcast over the inputs." + }, + "porosity_from_voxel_count": { + "params": { + "pore_voxels": "Number of voxels segmented as pore (counted under the caller's own segmentation convention).", + "total_voxels": "Total number of voxels in the image volume." + }, + "returns": "Digital-rock porosity phi = pore_voxels/total_voxels as a v/v fraction. Numpy float64 array broadcast over the inputs." + }, + "thomas_stieber_phit": { + "params": { + "phi_sand": "Porosity of the clean sand end member (v/v).", + "phi_sh": "Porosity of the shale end member (v/v).", + "v_disp": "Dispersed (pore-filling) shale volume fraction subtracted from the porosity; default 0.0 (purely laminated trend).", + "v_lam": "Laminar shale volume fraction (v/v)." + }, + "returns": "Thomas-Stieber total porosity phi_t = phi_sand*(1-V_lam) + phi_sh*V_lam - V_disp (v/v fraction). Numpy float64 array broadcast over the inputs." + }, + "thomas_stieber_sand_porosity": { + "params": { + "phi_sh": "Shale porosity endpoint (v/v).", + "phi_t": "Total porosity (v/v fraction).", + "v_lam": "Laminar shale volume fraction (v/v)." + }, + "returns": "Sand (net) porosity phi_sand = (phi_t - V_lam*phi_sh)/(1 - V_lam) (v/v fraction). Numpy float64 array broadcast over the inputs." + }, + "thomas_stieber_vlam": { + "params": { + "clip": "Clip range (lo, hi) applied to V_lam; default None (unclipped).", + "phi_sand": "Clean-sand porosity endpoint (v/v).", + "phi_sh": "Shale porosity endpoint (v/v).", + "phi_t": "Total porosity (v/v fraction)." + }, + "returns": "Laminar shale volume V_lam = (phi_sand-phi_t)/(phi_sand-phi_sh) (v/v fraction); the net-to-grain fraction is 1 - V_lam. Numpy float64 array broadcast over the inputs." + }, + "toc_passey_dlogr": { + "params": { + "k_overlay": "Overlay coefficient matching the curve type: +0.02 for sonic (default), -2.50 for density, +4.0 for neutron, +0.01 for GR.", + "lom": "Level of organic metamorphism (thermal maturity) used in the Passey maturity term; default 10.0.", + "overlay": "Overlay porosity-log curve: sonic (us/ft), density (g/cc), neutron (v/v), or GR (API), matching k_overlay.", + "overlay_base": "Baseline value of the overlay curve in the lean interval, in the same unit as overlay.", + "rt": "True (deep) resistivity in ohm-m.", + "rt_base": "Baseline resistivity in the non-source (lean) interval, in ohm-m." + }, + "returns": "Passey Delta-log-R total organic carbon, TOC = dlogR * 10**(2.297 - 0.1688*LOM), conventionally in weight percent. Numpy float64 array broadcast over the inputs." + }, + "toc_schmoker": { + "params": { + "a": "Numerator constant of the Schmoker correlation; default 154.497 (Schmoker-Hester), other basins pass their own.", + "b": "Offset constant of the Schmoker correlation; default 57.261 (Schmoker-Hester).", + "rho_b": "Bulk density in g/cc." + }, + "returns": "Total organic carbon in weight percent, TOC = a/rho_b - b. Numpy float64 array broadcast over the input." + }, + "volume_to_weight_fractions": { + "params": { + "rho": "Component densities (g/cc, or any consistent unit), aligned with v along the last axis.", + "v": "Volume fractions of the components (v/v), along the last axis." + }, + "returns": "Weight fractions w_i = v_i*rho_i/sum(v*rho), normalised to sum to one along the last axis. Numpy float64 array with the input shape." + }, + "vshale_from_gr": { + "params": { + "clip": "Clip range (lo, hi) applied to the final Vsh; default None (unclipped, the corpus Larionov behaviour); the internal IGR is always clipped to [0,1].", + "gcur": "Exponent of the generalized 'stieber_gcur' transform (2**(gcur*IGR)-1)/(2**gcur-1); default 2.0, ignored by the other methods.", + "gr": "Measured gamma-ray log values, in any consistent GR unit (API or GAPI); pass an already-computed IGR with gr_clean=0 and gr_shale=1.", + "gr_clean": "Gamma-ray reading of the clean (shale-free) endpoint, in the same unit as gr.", + "gr_shale": "Gamma-ray reading of the 100 percent shale endpoint, in the same unit as gr.", + "method": "Vsh transform: 'linear' (Vsh=IGR), 'larionov_tertiary', 'larionov_older', 'clavier', 'steiber', or 'stieber_gcur' (the normalised generalized-Stieber form using gcur)." + }, + "returns": "Shale volume fraction Vsh (v/v, dimensionless) from the selected gamma-ray-index transform. Numpy float64 array broadcast over the inputs; raises ValueError for an unknown method." + }, + "vshale_neutron_density": { + "params": { + "clip": "Clip range (lo, hi) applied to the resulting Vsh; default None (unclipped).", + "phi_d": "Density porosity value (v/v fraction).", + "phi_d_sh": "Density porosity at the shale point (v/v fraction).", + "phi_n": "Neutron porosity log value (v/v fraction).", + "phi_n_sh": "Neutron porosity at the shale point (v/v fraction)." + }, + "returns": "Shale volume fraction (v/v): the local neutron-density separation divided by the shale-point separation. Numpy float64 array broadcast over the inputs." + }, + "weight_to_volume_fractions": { + "params": { + "rho": "Component densities (g/cc, or any consistent unit), aligned with w along the last axis.", + "w": "Weight (mass) fractions of the components, along the last axis." + }, + "returns": "Volume fractions v_i = (w_i/rho_i)/sum(w/rho), normalised to sum to one along the last axis. Numpy float64 array with the input shape." + } + }, + "petrolib.relperm_wettability": { + "amott_indices": { + "params": { + "vo_forced": "Volume of oil displaced by forced drainage (or the equivalent saturation change), same units.", + "vo_spont": "Volume of oil displaced spontaneously (or the equivalent saturation change), same units.", + "vw_forced": "Volume of water imbibed under forced displacement (or the equivalent saturation change), same units.", + "vw_spont": "Volume of water imbibed spontaneously (or the equivalent saturation change), any consistent volume unit." + }, + "returns": "Tuple of three floats (Iw, Io, Iah): the water index Vw_spont/(Vw_spont+Vw_forced), the oil index likewise, and the Amott-Harvey index Iah = Iw - Io; each ratio is zero-guarded (0.0 when its total is not positive)." + }, + "bond_number": { + "params": { + "drho": "Density difference between the two fluids, kg/m3 with SI inputs.", + "g": "Gravitational acceleration, m/s2, default 9.81.", + "k": "Characteristic area: permeability in m2 for the Darcy form, or a squared pore radius (pass k = R**2) for the pore-radius form.", + "sigma": "Interfacial tension between the fluids, N/m with SI inputs." + }, + "returns": "Bond number Nb = drho*g*k/sigma, the gravity-to-capillary force ratio, dimensionless, as a numpy float64 array." + }, + "brooks_corey_burdine_kr": { + "params": { + "clip": "Tuple (lo, hi) that clips the normalized saturation Se, default (0.0, 1.0); None disables clipping.", + "lam": "Brooks-Corey pore-size-distribution index lambda, dimensionless, which fixes the Burdine exponents (2+3*lam)/lam and (2+lam)/lam.", + "snwr": "Residual non-wetting-phase saturation, fraction, subtracted in the Se denominator, default 0.", + "sw": "Wetting-phase (water) saturation, as a fraction of pore volume.", + "swr": "Residual wetting-phase saturation, fraction, used in the Se normalization." + }, + "returns": "Tuple (krw, krnw) of numpy float64 arrays: wetting-phase kr = Se**((2+3*lam)/lam) and non-wetting kr = (1-Se)**2 * (1 - Se**((2+lam)/lam)), both dimensionless with endpoint 1 (no kr_max factor)." + }, + "capillary_desaturation": { + "params": { + "exponent": "Steepness exponent of the transition, default 1.0; articles writing it as 1/width pass exponent = 1.0/width.", + "n": "Trapping (or capillary) number at which to evaluate the residual saturation, dimensionless.", + "n_crit": "Critical trapping number at the midpoint of the desaturation transition, dimensionless.", + "sor_max": "Plateau residual oil saturation at low trapping numbers, fraction of pore volume.", + "sor_min": "Floor residual saturation approached at high trapping numbers, fraction of pore volume." + }, + "returns": "Residual oil saturation Sor(N) = Sor_min + (Sor_max - Sor_min)/(1 + (N/N_crit)**exponent), a fraction falling from sor_max toward sor_min, as a numpy float64 array." + }, + "capillary_number": { + "params": { + "mu": "Viscosity of the displacing fluid, in Pa*s (SI for a dimensionless result).", + "sigma": "Interfacial tension between the fluids, in units consistent with mu*v (N/m with SI inputs).", + "v": "Darcy velocity, m/s with SI inputs; for the volumetric-rate form Q*mu/(A*sigma) pass v = Q/A." + }, + "returns": "Capillary number Nca = mu*v/sigma, the viscous-to-capillary force ratio, dimensionless, as a numpy float64 array." + }, + "classify_wettability_angle": { + "params": { + "cuts": "Two boundary angles in degrees separating the classes, default (75.0, 105.0); pass a different tuple to shift the boundaries.", + "theta_deg": "Contact angle to classify, in degrees, as a scalar value." + }, + "returns": "Classification string: 'water-wet' below the first cut, 'intermediate' from the first cut up to and including the second, 'oil-wet' above it." + }, + "classify_wettability_index": { + "params": { + "i": "Amott-Harvey or USBM-style wettability index in [-1, 1], as a scalar value.", + "scheme": "Classification scheme: '3class' gives water-wet / mixed-wet / oil-wet with cuts at +-0.3; '5class' adds weakly water-wet and weakly oil-wet bands at +-0.1." + }, + "returns": "Classification label string from the selected scheme; raises ValueError for an unknown scheme name." + }, + "corey_kr": { + "params": { + "clip": "Tuple (lo, hi) that clips the shared normalized saturation Se, default (0.0, 1.0); None disables clipping.", + "kro_max": "Endpoint oil relative permeability at Se = 0, dimensionless fraction, default 1.0.", + "krw_max": "Endpoint water relative permeability at Se = 1, dimensionless fraction, default 1.0.", + "no": "Corey oil exponent shaping the kro curve, dimensionless, default 2.0.", + "nw": "Corey water exponent shaping the krw curve, dimensionless, default 2.0.", + "sor": "Residual oil saturation, fraction, subtracted in the Se denominator; default 0 gives the drainage convention.", + "sw": "Water saturation, as a fraction of pore volume.", + "swr": "Residual (irreducible) water saturation, fraction, used in the shared Se normalization." + }, + "returns": "Tuple (krw, kro) of numpy float64 arrays: the water and oil relative permeabilities evaluated on a single shared normalized saturation, both dimensionless fractions." + }, + "corey_krg": { + "params": { + "clip": "Tuple (lo, hi) that clips the normalized gas saturation Sg*, default (0.0, 1.0); None disables clipping.", + "krg_max": "Endpoint gas relative permeability at Sg* = 1, dimensionless fraction, default 1.0.", + "ng": "Corey gas exponent controlling the curvature of the krg curve, dimensionless, default 2.0.", + "sg": "Gas saturation, as a fraction of pore volume; for Sw-framework curves pass sg = 1 - sw.", + "sgc": "Critical gas saturation, fraction, below which gas is immobile; subtracted in the Sg* numerator, default 0.", + "sorg": "Residual oil saturation to gas, fraction, subtracted in the Sg* denominator, default 0.", + "swc": "Connate water saturation, fraction, subtracted in the 4-endpoint Sg* denominator, default 0." + }, + "returns": "Corey gas relative permeability krg = krg_max * Sg***ng with the four-endpoint normalization Sg* = (Sg - Sgc)/(1 - Swc - Sgc - Sorg), as a numpy float64 array, dimensionless." + }, + "corey_kro": { + "params": { + "clip": "Tuple (lo, hi) that clips the normalized saturation Se before exponentiation, default (0.0, 1.0); None disables clipping.", + "kro_max": "Endpoint oil relative permeability reached at Se = 0, dimensionless fraction, default 1.0.", + "no": "Corey oil exponent controlling the curvature of the kro curve, dimensionless, default 2.0.", + "sor": "Residual oil saturation, fraction, subtracted in the Se denominator; default 0 gives the drainage convention.", + "sw": "Water saturation, as a fraction of pore volume.", + "swr": "Residual (irreducible) water saturation, fraction, used in the water-normalized Se." + }, + "returns": "Corey oil relative permeability kro = kro_max * (1 - Se)**no on the water-normalized Se, as a numpy float64 array, dimensionless." + }, + "corey_krw": { + "params": { + "clip": "Tuple (lo, hi) that clips the normalized saturation Se before exponentiation, default (0.0, 1.0); None disables clipping.", + "krw_max": "Endpoint water relative permeability reached at Se = 1, dimensionless fraction, default 1.0.", + "nw": "Corey water exponent controlling the curvature of the krw curve, dimensionless, default 2.0.", + "sor": "Residual oil saturation, fraction, subtracted in the Se denominator; default 0 gives the drainage convention.", + "sw": "Water saturation, as a fraction of pore volume.", + "swr": "Residual (irreducible) water saturation, fraction, used in the shared Se normalization." + }, + "returns": "Corey water relative permeability krw = krw_max * Se**nw as a numpy float64 array, a dimensionless fraction." + }, + "displacement_efficiency": { + "params": { + "soi": "Initial oil saturation before displacement, fraction of pore volume.", + "sor": "Residual oil saturation remaining after displacement, fraction of pore volume." + }, + "returns": "Microscopic displacement efficiency Ed = (Soi - Sor)/Soi as a numpy float64 array, a dimensionless fraction (also written recovery_factor in several articles)." + }, + "endpoint_mobility_ratio": { + "params": { + "kro_max": "Endpoint oil relative permeability at irreducible water, dimensionless fraction.", + "krw_max": "Endpoint water relative permeability at residual oil, dimensionless fraction.", + "mu_o": "Oil viscosity, in the same viscosity units as mu_w.", + "mu_w": "Water viscosity, in Pa*s (any consistent unit)." + }, + "returns": "Scalar float endpoint mobility ratio M = (krw_max/mu_w)/(kro_max/mu_o), dimensionless; M < 1 indicates a favorable, piston-like waterflood." + }, + "fit_corey": { + "params": { + "krnw_obs": "Observed non-wetting-phase relative permeabilities at each sw, dimensionless fractions.", + "krw_obs": "Observed wetting-phase (water) relative permeabilities at each sw, dimensionless fractions.", + "sor": "Residual oil saturation, fraction, fixed (not fitted) in the Se denominator, default 0.", + "sw": "Observed water saturations of the SCAL data points, fractions of pore volume.", + "swr": "Residual water saturation, fraction, fixed (not fitted) and used in the Se normalization." + }, + "returns": "Dict of floats with keys swr, sor, krw_max, nw, krnw_max, nnw: the passed-through endpoints plus the least-squares Corey endpoints and exponents for both phases. Requires scipy, imported lazily." + }, + "fractional_flow": { + "params": { + "kr_nw": "Relative permeability value(s) of the non-wetting phase, dimensionless fraction.", + "kr_w": "Relative permeability value(s) of the wetting phase, dimensionless fraction.", + "mu_nw": "Viscosity of the non-wetting phase, in the same viscosity units as mu_w.", + "mu_w": "Viscosity of the wetting phase, in Pa*s (any consistent unit)." + }, + "returns": "Buckley-Leverett wetting-phase fractional flow fw = (kr_w/mu_w)/(kr_w/mu_w + kr_nw/mu_nw) as a numpy float64 array, dimensionless in [0, 1]; set to 0 where the total mobility is zero." + }, + "fractional_flow_curve": { + "params": { + "kro_max": "Endpoint oil relative permeability for the Corey pair, dimensionless fraction, default 1.0.", + "krw_max": "Endpoint water relative permeability for the Corey pair, dimensionless fraction, default 1.0.", + "mu_nw": "Viscosity of the non-wetting phase, in the same viscosity units as mu_w.", + "mu_w": "Viscosity of the wetting (water) phase, in Pa*s (any consistent unit).", + "n": "Number of saturation grid points in the linspace over the mobile window, default 2000.", + "no": "Corey oil exponent of the underlying kro curve, dimensionless, default 2.0.", + "nw": "Corey water exponent of the underlying krw curve, dimensionless, default 2.0.", + "sor": "Residual oil saturation, fraction; the sw grid ends at 1 - sor, default 0.", + "swr": "Residual water saturation, fraction; both the lower end of the sw grid and the Corey normalization endpoint." + }, + "returns": "Tuple (sw, fw) of numpy float64 arrays of length n: water saturations on linspace(swr, 1-sor, n) and the corresponding wetting-phase fractional flow, both dimensionless." + }, + "land_c": { + "params": { + "s_i_max": "Maximum initial (turning-point) non-wetting-phase saturation, fraction; pass 1.0 for the single-endpoint 1/Sgt_max - 1 form.", + "s_r_max": "Maximum residual (trapped) saturation reached from s_i_max, fraction of pore volume." + }, + "returns": "Scalar float Land trapping coefficient C = 1/Sr_max - 1/Si_max, dimensionless." + }, + "land_trapped": { + "params": { + "C": "Land trapping coefficient, dimensionless; pass it directly, or leave None to derive it from s_r_max.", + "s_i": "Initial non-wetting-phase saturation before imbibition, fraction of pore volume.", + "s_r_max": "Maximum residual saturation, fraction, used as C = 1/s_r_max - 1 (the Si_max = 1 reduced form) when C is None." + }, + "returns": "Land trapped saturation Sr = Si/(1 + C*Si) as a numpy float64 array, a fraction of pore volume; raises ValueError if neither C nor s_r_max is given." + }, + "let_kr": { + "params": { + "E": "LET E parameter scaling the competing-phase term in the denominator, dimensionless.", + "L": "LET L exponent applied to the primary saturation term of the selected phase, dimensionless.", + "T": "LET T exponent applied to the competing-phase saturation term in the denominator, dimensionless.", + "clip": "Tuple (lo, hi) that clips the normalized saturation Se, default (0.0, 1.0); None disables clipping.", + "kr_max": "Endpoint relative permeability multiplying the LET ratio, dimensionless fraction, default 1.0.", + "phase": "Curve selector: 'wetting' evaluates kr_max*Se**L/(Se**L + E*(1-Se)**T); 'nonwetting' swaps Se and 1-Se; other values raise ValueError.", + "sw": "Wetting-phase (water) saturation, as a fraction of pore volume.", + "swr": "Residual water saturation, fraction, used in the drainage-denominator normalization Se = (Sw - Swr)/(1 - Swr)." + }, + "returns": "LET relative permeability of the selected phase as a numpy float64 array, a dimensionless fraction; the denominator is guarded against exact zero." + }, + "nmr_wettability_index": { + "params": { + "o_signal": "Matched oil-phase NMR quantity of the same kind and units as w_signal.", + "w_signal": "Water-phase NMR quantity: signal amplitude, surface relaxation rate, or T2 peak area." + }, + "returns": "NMR wettability index Iw = (w - o)/(w + o) as a numpy float64 array, dimensionless in [-1, 1]; positive values indicate stronger water-surface interaction." + }, + "normalized_saturation": { + "params": { + "clip": "Tuple (lo, hi) that bounds the normalized saturation Se, default (0.0, 1.0) for the mobile window; None disables clipping.", + "s": "Phase saturation to normalize, expressed as a fraction of pore volume.", + "snr": "Residual saturation of the other phase, fraction; the default 0 gives the (1-Sr) drainage denominator convention.", + "sr": "Residual (irreducible) saturation of the same phase, fraction, subtracted from s in the numerator." + }, + "returns": "Normalized (effective) saturation Se = (S - Sr)/(1 - Sr - Snr) as a numpy float64 array, a dimensionless fraction, clipped to the given bounds unless clip is None." + }, + "phase_mobility": { + "params": { + "kr": "Relative permeability of the phase, a dimensionless fraction (array-like).", + "mu": "Viscosity of the phase, in Pa*s (any consistent unit, since mobilities are used in ratios)." + }, + "returns": "Phase mobility lambda = kr/mu as a numpy float64 array, in reciprocal viscosity units (1/(Pa*s) with SI viscosity)." + }, + "total_mobility": { + "params": { + "kr_nw": "Relative permeability of the non-wetting phase, dimensionless fraction.", + "kr_w": "Relative permeability of the wetting phase, dimensionless fraction.", + "mu_nw": "Viscosity of the non-wetting phase, in the same viscosity units as mu_w.", + "mu_w": "Viscosity of the wetting phase, in Pa*s (any consistent unit)." + }, + "returns": "Total two-phase mobility lambda_t = kr_w/mu_w + kr_nw/mu_nw as a numpy float64 array, in reciprocal viscosity units." + }, + "trapping_number": { + "params": { + "nb": "Bond number value(s), dimensionless (e.g. from bond_number).", + "nca": "Capillary number value(s), dimensionless (e.g. from capillary_number)." + }, + "returns": "Total trapping number Nt = Nca + Nb (the additive form used repo-wide), dimensionless, as a numpy float64 array." + }, + "usbm_index": { + "params": { + "area_drainage": "Area under the drainage capillary-pressure curve, in any units consistent with area_imbibition.", + "area_imbibition": "Area under the imbibition capillary-pressure curve, in the same units as area_drainage." + }, + "returns": "Scalar float USBM wettability index W = log10(A_drainage/A_imbibition), dimensionless; positive values indicate a water-wet rock." + }, + "welge_shock": { + "params": { + "fw": "Fractional-flow values corresponding to each entry of sw, dimensionless in [0, 1].", + "sw": "Array of water saturations along the fractional-flow curve, fractions of pore volume.", + "swc": "Connate water saturation, fraction; the tangent is drawn from the point (Swc, 0)." + }, + "returns": "Tuple of three floats (swf, fwf, sw_avg): the shock-front water saturation, the fractional flow at the front, and the average saturation behind the front at breakthrough, all dimensionless fractions." + }, + "wenzel_angle": { + "params": { + "roughness": "Wenzel roughness factor r >= 1 (true over projected surface area), dimensionless, amplifying the intrinsic wettability.", + "theta_young_deg": "Intrinsic Young contact angle on the ideal smooth surface, in degrees." + }, + "returns": "Wenzel apparent contact angle on the rough surface, in degrees, as a numpy float64 array; the cosine r*cos(theta) is clipped to [-1, 1] before arccos." + }, + "work_of_adhesion": { + "params": { + "contact_angle_deg": "Contact angle of the fluid pair on the solid, in degrees.", + "sigma_wo": "Water-oil interfacial tension, force per length (e.g. N/m); sets the units of the result." + }, + "returns": "Young-Dupre work of adhesion W = sigma_wo*(1 + cos(theta)) as a numpy float64 array, energy per unit area in the units of sigma_wo." + }, + "young_contact_angle": { + "params": { + "sigma_so": "Solid-oil interfacial tension, force per length in any unit consistent with the other tensions.", + "sigma_sw": "Solid-water interfacial tension, in the same units as sigma_so.", + "sigma_wo": "Water-oil interfacial tension, in the same units as sigma_so." + }, + "returns": "Young's-law contact angle in degrees as a numpy float64 array; the cosine (sigma_so - sigma_sw)/sigma_wo is clipped to [-1, 1] before arccos." + } + }, + "petrolib.saturation_resistivity": { + "apparent_water_resistivity": { + "params": { + "a": "Archie tortuosity coefficient, dimensionless, default 1.0.", + "m": "Archie cementation exponent, dimensionless, default 2.0.", + "phi": "Porosity, as a fraction of bulk volume.", + "rt": "True formation resistivity, in ohm-m." + }, + "returns": "Apparent water resistivity Rwa = Rt*phi**m/a as a numpy float64 array, in ohm-m; it equals Rw in a clean fully water-bearing zone (the quick-look Rw picker)." + }, + "archie_conductivity": { + "params": { + "cw": "Formation water conductivity, in S/m.", + "m": "Archie cementation exponent, dimensionless, default 2.0.", + "n": "Archie saturation exponent, dimensionless, default 2.0.", + "phi": "Porosity, as a fraction of bulk volume.", + "sw": "Water saturation, as a fraction of pore volume." + }, + "returns": "Total rock conductivity Ct = Cw*phi**m*Sw**n as a numpy float64 array, in S/m (the conductivity-domain Archie forward model)." + }, + "archie_rt": { + "params": { + "a": "Archie tortuosity coefficient, dimensionless, default 1.0.", + "m": "Archie cementation exponent, dimensionless, default 2.0.", + "n": "Archie saturation exponent, dimensionless, default 2.0.", + "phi": "Porosity, as a fraction of bulk volume.", + "rw": "Formation water resistivity, in ohm-m.", + "sw": "Water saturation, as a fraction of pore volume." + }, + "returns": "Forward-modeled true formation resistivity Rt = a*Rw/(phi**m*Sw**n) as a numpy float64 array, in ohm-m; the exact inverse of archie_sw." + }, + "archie_sw": { + "params": { + "a": "Archie tortuosity coefficient, dimensionless, default 1.0.", + "clip": "Optional (lo, hi) tuple clipping the computed Sw to that range; default None applies no clipping, so values above 1 are returned as-is.", + "m": "Archie cementation exponent, dimensionless, default 2.0.", + "n": "Archie saturation exponent, dimensionless, default 2.0.", + "phi": "Porosity, as a fraction of bulk volume.", + "rt": "True (total) formation resistivity, in ohm-m.", + "rw": "Formation water resistivity, in ohm-m (or any unit consistent with rt)." + }, + "returns": "Archie water saturation Sw = (a*Rw/(phi**m*Rt))**(1/n) as a numpy float64 array, a fraction of pore volume, unclipped unless clip is given." + }, + "archie_sw_from_conductivity": { + "params": { + "clip": "Optional (lo, hi) tuple clipping the computed Sw to that range; default None applies no clipping.", + "ct": "Measured total rock conductivity, in S/m.", + "cw": "Formation water conductivity, in S/m.", + "m": "Archie cementation exponent, dimensionless, default 2.0.", + "n": "Archie saturation exponent, dimensionless, default 2.0.", + "phi": "Porosity, as a fraction of bulk volume." + }, + "returns": "Water saturation Sw = (Ct/(Cw*phi**m))**(1/n) as a numpy float64 array, a fraction of pore volume; the closed-form inverse of archie_conductivity." + }, + "bulk_volume_water": { + "params": { + "phi": "Porosity, as a fraction of bulk volume.", + "sw": "Water saturation, as a fraction of pore volume." + }, + "returns": "Bulk volume water BVW = phi*Sw as a numpy float64 array, the water volume as a fraction of bulk rock volume." + }, + "cec_from_qv": { + "params": { + "phi": "Porosity, as a fraction of bulk volume.", + "qv": "Cation exchange capacity per unit pore volume, in meq/ml.", + "rho_grain": "Grain (matrix) density, in g/cm3." + }, + "returns": "Cation exchange capacity per unit grain mass CEC = Qv*phi/(rho_grain*(1 - phi)) as a numpy float64 array, in meq/g; the exact inverse of qv_from_cec." + }, + "cementation_exponent_at_point": { + "params": { + "a": "Assumed Archie tortuosity coefficient, dimensionless, default 1.0.", + "ff": "Formation resistivity factor at the same point(s), dimensionless.", + "phi": "Porosity at the sample point(s), as a fraction of bulk volume." + }, + "returns": "Cementation exponent m = log(F/a)/log(1/phi) as a numpy float64 array, dimensionless, computed per sample (the single-point alternative to the regression fit)." + }, + "dual_water_conductivity": { + "params": { + "cb": "Clay bound-water conductivity, in S/m.", + "cw": "Free (far) water conductivity, in S/m.", + "m": "Cementation exponent, dimensionless, default 2.0.", + "n": "Saturation exponent, dimensionless, default 2.0.", + "phi": "Total porosity, as a fraction of bulk volume.", + "sw": "Total water saturation, as a fraction of pore volume.", + "swb": "Bound-water saturation, as a fraction of pore volume." + }, + "returns": "Dual-water (bound-water form, Clavier et al.) total conductivity Ct = Sw**n*phi**m*Cw + Sw**(n-1)*Swb*phi**m*(Cb - Cw) as a numpy float64 array, in S/m." + }, + "dual_water_conductivity_qv": { + "params": { + "alpha_vqh": "Product of the salinity-expansion factor alpha and the clay-water volume factor vQH (ml/meq); reduces the effective water conductivity via (1 - alpha_vqh*Qv).", + "beta": "Counterion equivalent conductance multiplying Qv in the clay term (conductivity contribution per unit Qv).", + "cw": "Free water conductivity, in S/m.", + "m0": "Cementation exponent of the Qv-based dual-water form, dimensionless, default 2.0.", + "n": "Saturation exponent, dimensionless, default 2.0.", + "phi": "Total porosity, as a fraction of bulk volume.", + "qv": "Cation exchange capacity per unit pore volume, in meq/ml.", + "sw": "Total water saturation, as a fraction of pore volume." + }, + "returns": "Qv-based dual-water total conductivity Ct = phi**m0*(Sw**n*(1 - alpha_vqh*Qv)*Cw + Sw**(n-1)*beta*Qv) as a numpy float64 array, in S/m; parameters do not map onto the bound-water form." + }, + "dual_water_sw": { + "params": { + "cb": "Clay bound-water conductivity, in S/m.", + "clip": "Optional (lo, hi) tuple clipping the final Sw values to that range; default None applies no clipping.", + "ct": "Measured total rock conductivity, in S/m, the target of the inversion.", + "cw": "Free (far) water conductivity, in S/m.", + "hi": "Upper bracket of the bisection search for Sw, fraction, default 1.0.", + "lo": "Lower bracket of the bisection search for Sw, fraction, default 1e-6.", + "m": "Cementation exponent, dimensionless, default 2.0.", + "n": "Saturation exponent, dimensionless, default 2.0.", + "n_iter": "Maximum number of bisection iterations, default 100.", + "phi": "Total porosity, as a fraction of bulk volume.", + "swb": "Bound-water saturation, as a fraction of pore volume.", + "tol": "Convergence tolerance on the bracket width; iteration stops early once all brackets are narrower, default 1e-12." + }, + "returns": "Total water saturation solving the bound-water dual-water conductivity equation for the measured Ct, as a numpy float64 array of fractions, found by the same vectorized bisection as waxman_smits_sw." + }, + "dual_water_sw_qv": { + "params": { + "alpha_vqh": "Product of the salinity-expansion factor and the clay-water volume factor vQH (ml/meq) in the effective-Cw term.", + "beta": "Counterion equivalent conductance multiplying Qv in the clay term.", + "clip": "Optional (lo, hi) tuple clipping the final Sw values to that range; default None applies no clipping.", + "ct": "Measured total rock conductivity, in S/m, the target of the inversion.", + "cw": "Free water conductivity, in S/m.", + "hi": "Upper bracket of the bisection search for Sw, fraction, default 1.0.", + "lo": "Lower bracket of the bisection search for Sw, fraction, default 1e-6.", + "m0": "Cementation exponent of the Qv-based form, dimensionless, default 2.0.", + "n": "Saturation exponent, dimensionless, default 2.0.", + "n_iter": "Maximum number of bisection iterations, default 100.", + "phi": "Total porosity, as a fraction of bulk volume.", + "qv": "Cation exchange capacity per unit pore volume, in meq/ml.", + "tol": "Convergence tolerance on the bracket width; iteration stops early once all brackets are narrower, default 1e-12." + }, + "returns": "Total water saturation solving the Qv-form dual-water conductivity equation for the measured Ct, as a numpy float64 array of fractions, found by the same vectorized bisection as waxman_smits_sw." + }, + "fit_cementation_exponent": { + "params": { + "ff": "Measured formation resistivity factors corresponding to each porosity sample, dimensionless.", + "phi": "Porosity samples of the core dataset, as fractions of bulk volume." + }, + "returns": "Tuple of two floats (m, a), in that order: the cementation exponent (negative slope) and tortuosity coefficient (10**intercept) from the log10(F) versus log10(phi) linear regression." + }, + "fit_saturation_exponent": { + "params": { + "ri": "Measured resistivity indices corresponding to each saturation sample, dimensionless.", + "sw": "Water saturation samples from the resistivity-index experiment, as fractions of pore volume." + }, + "returns": "Scalar float saturation exponent n, the negative slope of the log10(RI) versus log10(Sw) linear regression." + }, + "formation_factor": { + "params": { + "a": "Archie tortuosity coefficient, dimensionless, default 1.0 (the textbook convention).", + "m": "Archie cementation exponent, dimensionless, default 2.0; not the Waxman-Smits m*, which is a different calibration.", + "phi": "Porosity of the rock, as a fraction of bulk volume." + }, + "returns": "Archie formation resistivity factor F = a/phi**m as a numpy float64 array, dimensionless." + }, + "indonesia_sw": { + "params": { + "a": "Archie tortuosity coefficient of the sand term, dimensionless, default 1.0.", + "clip": "Optional (lo, hi) tuple clipping the computed Sw to that range; default None applies no clipping.", + "m": "Cementation exponent of the sand term, dimensionless, default 2.0.", + "n": "Saturation exponent, dimensionless, default 2.0.", + "phi": "Porosity, as a fraction of bulk volume.", + "rcl": "Clay resistivity, in ohm-m.", + "rt": "True formation resistivity, in ohm-m.", + "rw": "Formation water resistivity, in ohm-m.", + "vcl": "Clay volume, as a fraction of bulk volume, entering the clay term as Vcl**(1 - Vcl/2)." + }, + "returns": "Indonesia (Poupon-Leveaux) water saturation as a numpy float64 array of fractions, from the closed-form solution Sw = (1/(sqrt(Rt)*(clay_term + sand_term)))**(2/n)." + }, + "qv_from_cec": { + "params": { + "cec": "Cation exchange capacity per unit grain mass, in meq/g.", + "phi": "Porosity, as a fraction of bulk volume.", + "rho_grain": "Grain (matrix) density, in g/cm3." + }, + "returns": "Grain-based cation exchange capacity per unit pore volume Qv = CEC*rho_grain*(1 - phi)/phi as a numpy float64 array, in meq/ml." + }, + "qv_juhasz": { + "params": { + "cec_clay": "Cation exchange capacity of the dry clay, in meq/g.", + "phit": "Total porosity, as a fraction of bulk volume.", + "rho_clay": "Dry-clay density, in g/cm3.", + "v_clay_dry": "Dry-clay volume, as a fraction of bulk rock volume." + }, + "returns": "Juhasz clay-based normalization Qv = Vcl_dry*rho_clay*CEC_clay/phit as a numpy float64 array, in meq/ml; physically distinct from the grain-based qv_from_cec." + }, + "resistivity_index": { + "params": { + "ro": "Resistivity of the same rock fully saturated with water, in ohm-m.", + "rt": "Resistivity of the rock at partial water saturation, in ohm-m." + }, + "returns": "Resistivity index RI = Rt/Ro as a numpy float64 array, dimensionless." + }, + "resistivity_index_from_sw": { + "params": { + "b": "Prefactor of the power law, dimensionless, default 1.0 (the pure Archie case).", + "n": "Saturation exponent of the power law, dimensionless, default 2.0.", + "sw": "Water saturation, as a fraction of pore volume." + }, + "returns": "Resistivity index RI = b*Sw**(-n) as a numpy float64 array, dimensionless." + }, + "simandoux_sw": { + "params": { + "a": "Archie tortuosity coefficient of the sand term, dimensionless, default 1.0.", + "clip": "Optional (lo, hi) tuple clipping the computed Sw to that range; default None applies no clipping.", + "m": "Cementation exponent of the sand term, dimensionless, default 2.0.", + "phi": "Porosity, as a fraction of bulk volume.", + "rsh": "Shale resistivity, in ohm-m.", + "rt": "True formation resistivity, in ohm-m.", + "rw": "Formation water resistivity, in ohm-m.", + "vsh": "Shale volume, as a fraction of bulk volume." + }, + "returns": "Simandoux water saturation as a numpy float64 array of fractions: the positive root of the quadratic 1/Rt = Sw**2*phi**m/(a*Rw) + Vsh*Sw/Rsh; the saturation exponent is fixed at n = 2, so no n parameter exists." + }, + "sw_from_resistivity_index": { + "params": { + "b": "Prefactor of the power law, dimensionless, default 1.0 (the pure Archie case).", + "n": "Saturation exponent of the power law, dimensionless, default 2.0.", + "ri": "Resistivity index Rt/Ro, dimensionless." + }, + "returns": "Water saturation Sw = (RI/b)**(-1/n) as a numpy float64 array, a fraction of pore volume; the inverse of resistivity_index_from_sw." + }, + "waxman_smits_conductivity": { + "params": { + "b": "Equivalent counterion (cation) conductance B, in S/m per meq/ml; no default is provided because articles use fixed constants, temperature laws, or correlations.", + "cw": "Formation water (electrolyte) conductivity, in S/m.", + "m_star": "Waxman-Smits cementation exponent m* calibrated on shaly rock, dimensionless, default 2.0; not interchangeable with Archie's m.", + "n_star": "Waxman-Smits saturation exponent n* calibrated on shaly rock, dimensionless, default 2.0; not interchangeable with Archie's n.", + "phi": "Porosity, as a fraction of bulk volume.", + "qv": "Cation exchange capacity per unit pore volume, in meq/ml.", + "sw": "Water saturation, as a fraction of pore volume." + }, + "returns": "Waxman-Smits total conductivity Ct = Sw**n* * Cw * phi**m* + B*Qv*phi**m* * Sw**(n*-1) as a numpy float64 array, in S/m (Archie term plus cation-exchange term)." + }, + "waxman_smits_sw": { + "params": { + "b": "Equivalent counterion conductance B, in S/m per meq/ml (explicit, no safe default).", + "clip": "Optional (lo, hi) tuple clipping the final Sw values to that range; default None applies no clipping.", + "ct": "Measured total rock conductivity, in S/m, the target of the inversion.", + "cw": "Formation water conductivity, in S/m.", + "hi": "Upper bracket of the bisection search for Sw, fraction, default 1.0.", + "lo": "Lower bracket of the bisection search for Sw, fraction, default 1e-6.", + "m_star": "Waxman-Smits cementation exponent m*, dimensionless, default 2.0.", + "n_iter": "Maximum number of bisection iterations, default 100.", + "n_star": "Waxman-Smits saturation exponent n*, dimensionless, default 2.0.", + "phi": "Porosity, as a fraction of bulk volume.", + "qv": "Cation exchange capacity per unit pore volume, in meq/ml.", + "tol": "Convergence tolerance on the bracket width; iteration stops early once all brackets are narrower, default 1e-12." + }, + "returns": "Water saturation solving the Waxman-Smits conductivity equation for the measured Ct, as a numpy float64 array of fractions, broadcast over the inputs and found by vectorized bisection." + } + }, + "petrolib.testing": { + "assert_matches_original": { + "params": { + "atol": "Absolute tolerance passed to np.testing.assert_allclose; default 0.0.", + "cases": "Iterable of call cases; each case is a sequence of positional arguments or a (positional args, keyword args mapping) pair.", + "original": "Callable whose outputs define the reference behavior (the article-local function being replaced).", + "replacement": "Candidate replacement callable (the petrolib delegation), invoked with exactly the same arguments as original.", + "rtol": "Relative tolerance passed to np.testing.assert_allclose; default 1e-12 (the documented fallback 1e-9 is reserved for vectorization reassociation)." + }, + "returns": "Integer count of cases checked. Raises AssertionError if any case's results disagree (comparison recurses through tuples, lists and dicts; strings, bools and None must be exactly equal) or if the case iterable is empty." + } + }, + "petrolib.units": { + "convert": { + "params": { + "from_unit": "Source unit name, case-insensitive and spelling-tolerant (e.g. psi, MPa, degF, g/cc); an unknown unit raises ValueError.", + "to_unit": "Target unit name in the same quantity family as from_unit; a cross-family conversion (e.g. psi to metres) raises ValueError.", + "values": "Scalar or array of numeric values to convert, expressed in from_unit." + }, + "returns": "Converted values as a numpy float64 array with the input's shape (a 0-d array for scalar input), expressed in to_unit. Supported families are pressure, length, time, permeability, density, velocity, and affine temperature." + }, + "slowness_to_velocity": { + "params": { + "slowness": "Sonic slowness (interval transit time) values in the unit named by unit.", + "unit": "Slowness unit selector: us/ft (default) or us/m; any other value raises ValueError." + }, + "returns": "Velocity in m/s as a numpy float64 array." + }, + "velocity_to_slowness": { + "params": { + "unit": "Output slowness unit selector: us/ft (default) or us/m; any other value raises ValueError.", + "velocity": "Velocity values in m/s." + }, + "returns": "Sonic slowness as a numpy float64 array, in us/ft or us/m according to unit." + } + }, + "petrolib.wellbore_geometry": { + "dogleg_angle": { + "params": { + "azi1": "Azimuth at the first survey station, in degrees.", + "azi2": "Azimuth at the second survey station, in degrees.", + "inc1": "Inclination at the first survey station, in degrees.", + "inc2": "Inclination at the second survey station, in degrees." + }, + "returns": "Dogleg (total curvature) angle between the two stations as a scalar Python float in radians, from the spherical-law-of-cosines rearrangement with the cosine clipped to [-1, 1] before arccos." + }, + "md_to_tvd": { + "params": { + "inc_deg": "Array of inclinations in degrees, paired with md.", + "md": "Array of measured depths at the survey stations.", + "method": "Conversion convention: \"min_curvature\" (default) uses the azimuth-independent balanced minimum-curvature vertical increments; \"tangential\" uses the legacy single-inclination form summing dMD*cos(inc) with each segment's lower-station inclination; other values raise ValueError." + }, + "returns": "Cumulative true vertical depth as a numpy float64 array of the same length as md, in the length units of md with TVD positive downward." + }, + "minimum_curvature_step": { + "params": { + "azi1": "Azimuth at the first station, in degrees.", + "azi2": "Azimuth at the second station, in degrees.", + "inc1": "Inclination at the first station, in degrees.", + "inc2": "Inclination at the second station, in degrees.", + "md1": "Measured depth at the first survey station; its length unit sets the unit of the returned increments.", + "md2": "Measured depth at the second station, in the same length units as md1." + }, + "returns": "Tuple (dTVD, dNorth, dEast) of Python floats: the balanced minimum-curvature displacement increments between the two stations, in the length units of md, with TVD positive downward." + }, + "ratio_factor": { + "params": { + "dogleg_rad": "Dogleg angle between the two survey stations, in radians." + }, + "returns": "Minimum-curvature ratio (radius) factor RF = (2/DL)*tan(DL/2) as a scalar Python float; exactly 1.0 for a near-straight step (DL below 1e-9), the balanced-tangential limit." + }, + "survey_to_path": { + "params": { + "azi": "Array of azimuths at the stations, in degrees, parallel to md.", + "inc": "Array of inclinations at the stations, in degrees, parallel to md.", + "md": "Array of measured depths at consecutive survey stations; its length unit sets the trajectory units." + }, + "returns": "Numpy float64 array of shape (n, 3): the cumulative minimum-curvature trajectory integrated from the origin (0, 0, 0), with column order (TVD, North, East) in the length units of md and TVD positive downward." + } + } +}