diff --git a/doc/Petrophysics_Handbook.pdf b/doc/Petrophysics_Handbook.pdf
new file mode 100644
index 0000000..f54443d
Binary files /dev/null and b/doc/Petrophysics_Handbook.pdf differ
diff --git a/tools/gen_petrolib_handbook.py b/tools/gen_petrolib_handbook.py
index 44dae79..61a7026 100644
--- a/tools/gen_petrolib_handbook.py
+++ b/tools/gen_petrolib_handbook.py
@@ -7,16 +7,19 @@
"Code for Petrophysics Jan/Feb 2014 - Jun/Jul 2026", and
"https://github.com/ingehap/Petrophysics_Code";
* one blank page;
+* a table of acronyms (CBL, NMR, ...) and a table of non-SI units
+ (MRayl, mD, ...) used in the manual;
* 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.
+* one page per function: general call format, 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:
+returns); the acronym and unit tables live in tools/handbook_glossary.json.
+Keep both in sync when the petrolib API changes — missing entries are
+reported on stderr. Requires reportlab:
pip install reportlab
python tools/gen_petrolib_handbook.py
@@ -34,6 +37,7 @@
ROOT = pathlib.Path(__file__).resolve().parents[1]
OUT = ROOT / "doc" / "Petrophysics_Handbook.pdf"
DESCRIPTIONS = pathlib.Path(__file__).with_name("handbook_descriptions.json")
+GLOSSARY = pathlib.Path(__file__).with_name("handbook_glossary.json")
RETURN_ALIASES = {
"_Float": "numpy.ndarray of float64",
@@ -79,6 +83,20 @@ class Entry:
returns_meaning: str = ""
sources: list[str] = field(default_factory=list)
+ @property
+ def signature(self) -> str:
+ """General call format, e.g. ``f(x, y, *, clip=None)``."""
+ bits: list[str] = []
+ star_done = False
+ for p in self.params:
+ if p.kw_only and not star_done:
+ 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"{self.name}({', '.join(bits)})"
+
def parse_references(module_doc: str) -> dict[str, str]:
"""Map srcYYYY_MM tags to full citations from a References section."""
@@ -278,8 +296,8 @@ def build_pdf(entries: list[Entry]) -> None:
func_heading = ParagraphStyle(
"FuncHeading",
fontName="DejaVuSansMono",
- fontSize=13,
- leading=16,
+ fontSize=11.5,
+ leading=14.5,
spaceAfter=0,
textColor=navy,
)
@@ -353,19 +371,46 @@ def on_page(canvas: object, _doc: object) -> None:
story.append(NextPageTemplate("normal"))
story.append(PageBreak())
- # ---- table of contents
- story.append(
- Paragraph(
- "Table of Contents",
- ParagraphStyle(
- "TocTitle",
- fontName="DejaVuSans-Bold",
- fontSize=18,
- textColor=navy,
- spaceAfter=10,
- ),
- )
+ section_title = ParagraphStyle(
+ "SectionTitle",
+ fontName="DejaVuSans-Bold",
+ fontSize=18,
+ textColor=navy,
+ spaceAfter=10,
)
+ glossary_style = 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), 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), 2),
+ ("BOTTOMPADDING", (0, 0), (-1, -1), 2),
+ ("LEFTPADDING", (0, 0), (-1, -1), 2),
+ ]
+ )
+ term = ParagraphStyle("Term", parent=body, fontName="DejaVuSans-Bold", fontSize=8.8)
+
+ def glossary_table(title: str, col0: str, items: dict[str, str]) -> None:
+ story.append(Paragraph(title, section_title))
+ rows: list[list[object]] = [[col0, "Meaning"]]
+ for k in sorted(items, key=str.lower):
+ rows.append([Paragraph(esc(k), term), Paragraph(rich(items[k]), body)])
+ t = Table(rows, colWidths=[38 * mm, 128 * mm], repeatRows=1)
+ t.setStyle(glossary_style)
+ story.append(t)
+ story.append(PageBreak())
+
+ # ---- acronyms, then non-SI units
+ glossary = json.loads(GLOSSARY.read_text())
+ glossary_table("Acronyms", "Acronym", glossary["acronyms"])
+ glossary_table("Non-SI Units", "Unit", glossary["units"])
+
+ # ---- table of contents
+ story.append(Paragraph("Table of Contents", section_title))
toc = TableOfContents()
toc.levelStyles = [
ParagraphStyle(
@@ -382,15 +427,23 @@ def on_page(canvas: object, _doc: object) -> None:
# ---- one page per function
blank_line = Spacer(1, 12.4) # one body-height line with no text
+ mono_line = ParagraphStyle(
+ "MonoLine", parent=body, fontName="DejaVuSansMono", fontSize=9.4, leading=12.4
+ )
for i, e in enumerate(entries):
key = f"e{i}"
path = f"{e.module}.{e.name}"
- head = Paragraph(f'{esc(path)}', func_heading)
+ story.append(Paragraph(f'Call format', label))
+ head = Paragraph(esc(e.signature), func_heading)
head._toc_key = key
head._toc_text = f"{e.name} — {e.module}"
story.append(head)
block: list[object] = [blank_line]
+ block.append(Paragraph("Python path", label))
+ block.append(Paragraph(esc(path), mono_line))
+
+ block.append(blank_line)
block.append(Paragraph("Purpose", label))
if e.purpose:
for para in e.purpose.split("\n\n"):
@@ -455,7 +508,8 @@ def on_page(canvas: object, _doc: object) -> None:
block.append(Paragraph("• " + rich(s), src_style))
# shrink-to-fit so every function occupies exactly one page
- story.append(KeepInFrame(frame_w, frame_h - 14 * mm, block, mode="shrink", hAlign="LEFT"))
+ # (28 mm reserved for the call-format heading, which can wrap)
+ story.append(KeepInFrame(frame_w, frame_h - 28 * mm, block, mode="shrink", hAlign="LEFT"))
story.append(PageBreak())
doc.multiBuild(story)
diff --git a/tools/handbook_glossary.json b/tools/handbook_glossary.json
new file mode 100644
index 0000000..791f03a
--- /dev/null
+++ b/tools/handbook_glossary.json
@@ -0,0 +1,124 @@
+{
+ "acronyms": {
+ "AAPE": "Average absolute percentage error, a regression fit-quality metric.",
+ "ANNIE": "The Schoenberg-Muir-Sayers (1996) simplified three-parameter transversely isotropic velocity model for shales, behind the annie_c11/annie_c13 stiffness approximations.",
+ "API": "American Petroleum Institute; API units calibrate gamma-ray logs (see the non-SI units table) and API gravity grades crude oils.",
+ "BET": "Brunauer-Emmett-Teller multilayer gas-adsorption isotherm, the basis of specific-surface-area measurement.",
+ "BI": "Bond index: cement-bond quality normalized to [0, 1] between the free-pipe (0) and well-bonded (1) anchors.",
+ "BPP": "Bloembergen-Purcell-Pound theory of NMR relaxation by molecular motion.",
+ "BVI": "Bulk volume irreducible: the NMR porosity fraction below the T2 cutoff, held as capillary-bound water.",
+ "BVW": "Bulk volume water: porosity multiplied by water saturation.",
+ "C/O": "Carbon-oxygen ratio from inelastic neutron spectroscopy, an oil-saturation indicator independent of water salinity.",
+ "C1-C5": "Light-hydrocarbon components in mud-gas analysis: C1 methane, C2 ethane, C3 propane, C4 butanes, C5 pentanes.",
+ "CBL": "Cement bond log: the cased-hole sonic amplitude/attenuation measurement of casing-to-cement bond quality.",
+ "CBW": "Clay-bound water: the shortest-T2 fraction of the NMR porosity spectrum.",
+ "CEC": "Cation exchange capacity of clay (typically meq/g), the source of excess conductivity in shaly-sand models.",
+ "CFL": "Courant-Friedrichs-Lewy stability number limiting the time step of explicit finite-difference schemes.",
+ "CGR": "Computed (uranium-free) gamma ray built from the potassium and thorium contributions only.",
+ "Cij": "Elastic stiffness-tensor components in Voigt notation (C11, C33, C44, C66, C13, C12).",
+ "CODATA": "Committee on Data for Science and Technology; source of the recommended values of the physical constants used in petrolib.constants.",
+ "CPMG": "Carr-Purcell-Meiboom-Gill multi-echo pulse sequence used to measure NMR T2.",
+ "CRIM": "Complex refractive index method, a volumetric mixing law for dielectric permittivity.",
+ "CT": "(X-ray) computed tomography of core; its attenuation contrast maps to porosity and saturation.",
+ "DC": "Direct current, i.e. the zero-frequency limit (e.g. the DC resistivity rho0 of Cole-Cole dispersion).",
+ "DLIS": "Digital Log Interchange Standard wireline data format.",
+ "DOI": "Digital object identifier of a journal article.",
+ "DT": "Sonic transit-time (slowness) log mnemonic.",
+ "DTW": "Dynamic time warping, the elastic depth-alignment algorithm for log curves.",
+ "ECD": "Equivalent circulating density: effective mud density including annular friction losses while circulating.",
+ "EM": "Electromagnetic.",
+ "FFI": "Free fluid index: the NMR porosity fraction above the T2 cutoff, i.e. producible fluid.",
+ "FHZ": "Flory-Huggins-Zuo equation of state for asphaltene concentration gradients.",
+ "FZI": "Flow zone indicator: RQI/phi_z, the Amaefule-Tiab hydraulic rock-typing variable.",
+ "GLCM": "Gray-level co-occurrence matrix, a texture descriptor for borehole images.",
+ "GOC": "Gas-oil contact.",
+ "GOR": "Gas-oil ratio.",
+ "GR": "(Natural) gamma-ray log.",
+ "HC": "Hydrocarbon.",
+ "HCPV": "Hydrocarbon pore volume.",
+ "HI": "Hydrogen index: hydrogen density relative to fresh water.",
+ "IEEE": "Institute of Electrical and Electronics Engineers; its standard 754 defines the binary floating-point arithmetic assumed by the regression tests.",
+ "IGR": "Gamma-ray index (GR - GR_clean)/(GR_shale - GR_clean), the linear input to shale-volume transforms.",
+ "IQR": "Interquartile range (Q3 - Q1), a robust spread measure.",
+ "ISTA": "Iterative shrinkage-thresholding algorithm for L1-regularized (sparse) inversion.",
+ "L1 / L2": "Sum-of-absolute-values and Euclidean (sum-of-squares) vector norms used in misfits and regularization.",
+ "LAS": "Log ASCII Standard wireline data format.",
+ "LET": "Lomeland-Ebeltoft-Thomas three-parameter relative-permeability correlation.",
+ "LM": "Levenberg-Marquardt damped least-squares algorithm for nonlinear fitting.",
+ "LOM": "Level of organic metamorphism: the thermal-maturity scale in Passey's Delta-log-R TOC method.",
+ "MAD": "Median absolute deviation, a robust scale estimate used for despiking.",
+ "MALA": "Metropolis-adjusted Langevin algorithm, a gradient-informed MCMC sampler.",
+ "MAP": "Maximum a posteriori: the mode of a Bayesian posterior distribution.",
+ "MCMC": "Markov chain Monte Carlo posterior sampling.",
+ "MICP": "Mercury injection capillary pressure measurement.",
+ "ML": "Machine learning.",
+ "MW": "Mud weight (in the drilling gas-normalization formulas).",
+ "N_A": "Avogadro constant, 6.022e23 per mole.",
+ "NCS": "Net confining stress on a core sample.",
+ "ND": "Neutron-density shale/clay indicator built from the neutron-porosity and density logs.",
+ "NMR": "Nuclear magnetic resonance.",
+ "NPHI": "Neutron-porosity log mnemonic.",
+ "OB": "Overburden (vertical total) stress or pressure, as in the Eaton pore-pressure relation.",
+ "OD": "Optical density (asphaltene gradients); also outer diameter (casing condition).",
+ "ODE": "Ordinary differential equation.",
+ "OM": "Organic matter.",
+ "OSI": "Oil saturation index: Rock-Eval S1 divided by TOC, in mg hydrocarbon per g TOC.",
+ "OWC": "Oil-water contact.",
+ "P-wave": "Compressional (primary) elastic wave.",
+ "PEF": "Photoelectric factor log.",
+ "PR": "Peng-Robinson cubic equation of state.",
+ "RD": "Deep-resistivity log mnemonic.",
+ "RDDTW": "Regularized derivative dynamic time warping.",
+ "RF": "Ratio factor of the minimum-curvature well-trajectory method.",
+ "RFN": "Rock fabric number in Lucia's carbonate permeability classification.",
+ "RHOB": "Bulk-density log mnemonic.",
+ "RI": "Resistivity index Rt/R0 in Archie's second law.",
+ "RMS": "Root mean square.",
+ "ROP": "Rate of penetration while drilling.",
+ "RQI": "Rock quality index 0.0314*sqrt(k/phi).",
+ "S-wave": "Shear (secondary) elastic wave.",
+ "S/V": "Pore surface-to-volume ratio, which controls NMR surface relaxation.",
+ "SCAL": "Special core analysis.",
+ "SDR": "Schlumberger-Doll Research NMR permeability transform k = C*phi^4*T2LM^2.",
+ "SEM": "Scanning electron microscopy.",
+ "SG": "Specific gravity: density relative to fresh water (liquids) or to air (gases).",
+ "SGR": "Spectral (total) gamma ray reconstructed from the K, U, and Th concentrations.",
+ "SI": "The International System of Units; petrolib's default unit convention.",
+ "SNR": "Signal-to-noise ratio.",
+ "SPWLA": "Society of Petrophysicists and Well Log Analysts, publisher of the journal Petrophysics.",
+ "SSA": "Specific surface area.",
+ "STP": "Standard temperature and pressure.",
+ "SVD": "Singular value decomposition.",
+ "T1": "Longitudinal (spin-lattice) NMR relaxation time.",
+ "T2": "Transverse (spin-spin) NMR relaxation time; T2LM is its amplitude-weighted logarithmic mean.",
+ "TE": "Inter-echo spacing of the CPMG pulse sequence.",
+ "TMALI": "Thermal Maturity-Adjusted Log Interpretation, the Craddock et al. (2019) organic-shale workflow.",
+ "TOC": "Total organic carbon.",
+ "TP / FP / TN / FN": "True/false positive/negative counts of a binary classification.",
+ "TVD": "True vertical depth.",
+ "ULP": "Unit in the last place: the spacing between adjacent floating-point numbers.",
+ "USBM": "U.S. Bureau of Mines wettability index.",
+ "VMD": "Variational mode decomposition, a signal-denoising method.",
+ "VTI": "Vertical transverse isotropy: a layered medium with a vertical axis of rotational symmetry."
+ },
+ "units": {
+ "API unit (gAPI)": "Gamma-ray calibration unit of the American Petroleum Institute, defined so the contrast of the API test pit in Houston reads 200 units; gamma-ray logs are recorded in gAPI.",
+ "bar": "Pressure unit: 1 bar = 100 kPa = 14.504 psi.",
+ "c.u.": "Capture unit of the thermal-neutron capture cross-section Sigma: 1 c.u. = 1e-3 per cm.",
+ "darcy (D), millidarcy (mD)": "Permeability: 1 darcy = 9.869233e-13 m2 (about 1 um2); 1 mD = 1e-3 D.",
+ "degF": "Degree Fahrenheit: T[degF] = 1.8*T[degC] + 32.",
+ "ft": "Foot: 1 ft = 0.3048 m exactly (also ft/s velocities, ft/hr penetration rates, and psi/ft gradients).",
+ "g/cc": "Gram per cubic centimetre (CGS density): 1 g/cc = 1000 kg/m3.",
+ "in.": "Inch: 1 in. = 25.4 mm exactly (bit and casing diameters).",
+ "M (molar)": "Mole per litre of solution: 1 M = 1000 mol/m3.",
+ "meq/g, meq/ml": "Milliequivalent (1e-3 mol of unit charge) per gram or per millilitre; the units of CEC and of Qv in shaly-sand conductivity models.",
+ "MRayl": "Megarayl, specific acoustic impedance: 1 MRayl = 1e6 rayl, where 1 rayl = 1 Pa*s/m = 1 kg/(m2*s).",
+ "p.u.": "Porosity unit: one percentage point of porosity (0.01 v/v).",
+ "ppg": "Pound per US gallon (mud weight): 1 ppg = 119.83 kg/m3, equivalent to a hydrostatic gradient of 0.052 psi/ft.",
+ "ppm": "Part per million (fraction 1e-6), e.g. NaCl salinity or trace-element concentration.",
+ "psi": "Pound-force per square inch: 1 psi = 6894.757 Pa; the gradient 1 psi/ft = 22.62 kPa/m.",
+ "us/ft": "Microsecond per foot, the sonic slowness convention: 1 us/ft = 3.2808 us/m; velocity in ft/s is 1e6 divided by the slowness.",
+ "v/v": "Volume fraction (dimensionless); the porosity and saturation convention.",
+ "wt%": "Weight (mass) percent."
+ }
+}