Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added doc/Petrophysics_Handbook.pdf
Binary file not shown.
98 changes: 76 additions & 22 deletions tools/gen_petrolib_handbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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",
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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,
)
Expand Down Expand Up @@ -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(
Expand All @@ -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'<a name="{key}"/>{esc(path)}', func_heading)
story.append(Paragraph(f'<a name="{key}"/>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"):
Expand Down Expand Up @@ -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)
Expand Down
124 changes: 124 additions & 0 deletions tools/handbook_glossary.json
Original file line number Diff line number Diff line change
@@ -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."
}
}
Loading