petroplots

Publication-quality well log plots for matplotlib.

DataFrames in, Figure out. No file parsing, no petrophysical calculations, no well data model. lasio, welly and dlisio already do those better.

import petroplots as pp

df = pp.datasets.load_example()

plot = pp.LogPlot(df, top=2000, bottom=2100)
plot.add_depth(interval=10)

gr = plot.add_track()
gr.add_curve("GR", limits=(0, 150), units="API")

nd = plot.add_track()
nd.add_curve("RHOB", limits=(1.9, 2.9),    units="g/cm3")
nd.add_curve("NPHI", limits=(0.45, -0.05), units="v/v")
nd.crossover()

plot.add_facies("FACIES", pp.LITHOLOGY_FGDC, style="patches")

fig, axes = plot.render()

The same vocabulary carries across the other plot types, so a facies colour, a curve range and a formation column mean the same thing in each of them:

df = pp.add_tops(df, "field_tops.csv", well="15/9-19 A")

pp.crossplot(df, "NPHI", "RHOB", color="GR", color_steps=6)
pp.crossplot(df, "PHIT", "PERM", by="FORMATION", color="FACIES",
             scheme=pp.LITHOLOGY)
pp.histogram(df, "PHIT", by="FORMATION")

The top level holds the entry points above, plus the schemes, swatches, curve conventions and errors shared between plot types. Anything specific to one kind of plot lives in that plot’s package.

Using petroplots

Why this exists

Plotting a well log in matplotlib takes roughly 150 lines, and they are much the same every time: invert the depth axis once on the shared axis, call tick_top() on every track, build a ListedColormap and BoundaryNorm so a facies colour means the same thing in every well, add proxy Patch handles because imshow produces no legend entries, and set gridspec_kw width ratios to size a narrow facies track beside wide curve tracks.

What it handles that hand-rolled versions get wrong

These failures produce a figure that renders without error and reports something incorrect.

A facies colour means the same thing in every well. FaciesScheme builds its norm from the declared class set, never from the data. imshow autoscales its norm to whatever array it is given, so a well that contains no limestone recolours its sandstone.

Legend entries are built for artists that have none. Neither an image nor a fill produces legend handles, so every track builds its own proxies. Patterned swatches get a custom handler, which puts the ornament in the key.

The depth axis is inverted once, on the shared axis, after all tracks have drawn. Two tracks each inverting it leaves it the right way up.

Layer edges sit at sample midpoints, which avoids both a hairline at every contact and an overlap that hides thin beds.

Crossover shading is computed in normalised space. Density and neutron are recorded on incompatible scales, so “where NPHI is left of RHOB” only has a meaning once both are mapped onto their own display limits. Shading computed without that step marks the wrong intervals.

Limits are set after drawing, never during. Matplotlib autoscales as artists are added, so a limit set mid-draw is overridden by the next artist on the same axes.

Install

pip install petroplots            # core: matplotlib, numpy, pandas
pip install petroplots[las]       # + lasio, for pp.io.from_las()
pip install petroplots[yaml]      # + PyYAML, for templates
pip install petroplots[test]      # + pytest, pytest-mpl

Python 3.10+. import petroplots pulls in nothing heavier than matplotlib, numpy and pandas. seaborn, scipy, lasio and yaml are all lazy.

Where to go next

If you want to…

Read

Get a figure in five minutes

Getting started

Build plots from a loop, a config or a UI

Building a plot

Look up an add_* method and its options

Adding tracks: every method and option

Understand track composition

Tracks

Drive petroplots from a UI

Driving a user interface

Make facies colours consistent across a field

Facies schemes

Add FGDC lithology ornament

Lithology swatches

Plot two curves against each other

Crossplots

Split a plot into one panel per formation

Small multiples

Look at the distribution of one curve

Histograms

Get a tops file onto the frame

Loading data

Apply one plot definition to 40 wells

Templates

Get a LAS file into a plottable frame

Loading data

Licence

MIT. Lithology patterns follow the FGDC Digital Cartographic Standard for Geologic Map Symbolization (USGS, public domain); the geometry is reimplemented parametrically and no artwork is redistributed. Bundled example data is synthetic. See The bundled example.

Indices