import striplog 

striplog.__version__
'unknown'

Block logs#

We’d like to make blocky, upscaled versions of logs.

Let’s load a well from an LAS File using welly:

from welly import Well

w = Well.from_las('P-129_out.LAS')

w
Kennetcook #2
Long = 63* 45'24.460 W
crsCRS({})
locationLat = 45* 12' 34.237" N
countryCA
provinceNova Scotia
latitude
longitude
datum
section45.20 Deg N
rangePD 176
township63.75 Deg W
tdNone
dataCALI, DPHI_DOL, DPHI_LIM, DPHI_SAN, DRHO, DT, DTS, GR, HCAL, NPHI_DOL, NPHI_LIM, NPHI_SAN, PEF, RHOB, RLA1, RLA2, RLA3, RLA4, RLA5, RM_HRLT, RT_HRLT, RXOZ, RXO_HRLT, SP
gr = w.data['GR']

gr
GR [gAPI]
1.0668 : 1939.1376 : 0.1524
index_unitsm
codeNone
descriptionGamma-Ray
log_typeNone
apiNone
date10-Oct-2007
null-999.25
runNone
service_companySchlumberger
Stats
samples (NaNs)12718 (0)
min mean max3.89 78.986 267.94
DepthValue
1.066846.6987
1.219246.6987
1.371646.6987
1938.832892.2462
1938.985292.2462
1939.137692.2462

We can block this log based on some cutoffs:

gr_blocky = gr.block(cutoffs=[40, 100])

gr_blocky.plot()
<AxesSubplot:title={'center':'GR'}, xlabel='gAPI'>
../_images/16_Block_logs_5_1.png

But now we’re not really dealing with regularly-sampled data anymore, we’re dealing with ‘intervals’. Striplog is sometimes a better option for representing this sort of data. So let’s try using that instead:

gr_blocky = gr.block(cutoffs=[40])
gr_blocky.plot()
<AxesSubplot:title={'center':'GR'}, xlabel='gAPI'>
../_images/16_Block_logs_7_1.png

We could find the thickest bed in this result using NumPy (i.e. the longest run of 1’s):

from itertools import groupby

samples = max(sum(run) for val, run in groupby(gr_blocky.values) if val)
samples * 0.1524
197.0532

But it’s much easier to use Striplog, which can compute the resut directly from the gr log itself. (You have to pass ‘components’ to the from_log() function, but it doesn’t matter what they are.

from striplog import Striplog

s = Striplog.from_log(gr, basis=gr.basis, cutoff=[40], components=[None, None])
s.thickest().thickness
197.05320000000017

Let’s look at how to do that with actual components…

Make a striplog instead of a blocked log#

Striplog objects are potentially a bit more versatile than trying to use a log to represent intervals.

We have to do a little extra work though; for example, we have to tell Striplog what the intervals represent… the contents (lithologies or whatever) of an interval are called ‘components’.

We also have to pass the depth separately, because Striplog doesn’t know anything about welly’s Curve objects.

from striplog import Striplog, Component

comps = [
    Component(properties={'lithology': 'sandstone'}),
    Component(properties={'lithology': 'siltstone'}),
    Component(properties={'lithology': 'shale'}),
]

s = Striplog.from_log(gr, basis=gr.basis, cutoff=[40, 100], components=comps)

s
Striplog(853 Intervals, start=1.0668, stop=1939.1376)

You can plot this:

s.plot(aspect=3)
../_images/16_Block_logs_16_0.png

For a more natural visualization, it’s a good idea to make a legend to display things. For example:

from striplog import Legend

L = """comp lithology, colour, width, curve mnemonic
       sandstone, #fdf43f, 3
       siltstone, #cfbb8f, 2
       shale, #c0d0c0, 1
"""

legend = Legend.from_csv(text=L)

legend
widthcomponenthatchcolour
3.0
lithologysandstone
None#fdf43f
2.0
lithologysiltstone
None#cfbb8f
1.0
lithologyshale
None#c0d0c0

Now the plot will look more geological:

s.plot(legend=legend)
../_images/16_Block_logs_20_0.png

Let’s simplify it a bit by removing beds thinner than 3 m, then ‘annealing’ over the gaps, then merging like neighbours (otherwise you’ll likely have a lot of beds juxtaposed with similar beds):

s = s.prune(limit=3).anneal().merge_neighbours()

s.plot(legend=legend)
../_images/16_Block_logs_22_0.png

The easiest way to plot with a curve is like:

w.data['strip'] = s

tracks = ['MD', 'GR', 'strip']

C = """curve mnemonic, colour, width
       GR, #ff0000, 1
       GR-B, #ff8800, 1
"""

curve_legend = Legend.from_csv(text=C)

big_legend = legend + curve_legend

w.plot(tracks=tracks, legend=big_legend)
../_images/16_Block_logs_24_0.png

You can still make a blocky version from this Striplog:

gr_blocky, depth, comps = s.to_log(return_meta=True)

import matplotlib.pyplot as plt
plt.figure(figsize=(2, 10))
plt.plot(gr_blocky, depth)
plt.ylim(2000, 0)
(2000.0, 0.0)
../_images/16_Block_logs_26_1.png

Note that this is a NumPy array, because striplog doesn’t know about welly. But you could make a welly.Curve object:

from welly import Curve

gr_blocky_curve = Curve(gr_blocky, index=depth, mnemonic='GR-B')

gr_blocky_curve.plot()
<AxesSubplot:title={'center':'GR-B'}>
../_images/16_Block_logs_28_1.png

Let’s add this Curve to the well object w — then we can make a multi-track plot with welly:

w.data['GR-B'] = gr_blocky_curve

tracks = ['MD', 'GR', 'GR-B', 'strip']

w.plot(tracks=tracks, legend=big_legend)
../_images/16_Block_logs_30_0.png

Blocking another log using these intervals#

Let’s use the intervals we just created to block a different log from the same well.

Here’s the RHOB log. We’ll block it using the intervals from the GR.

rhob = w.data['RHOB']

rhob.plot()
<AxesSubplot:title={'center':'RHOB'}, xlabel='g/cm3'>
../_images/16_Block_logs_32_1.png

Now we will ‘extract’ the RHOB data, using some reducing function — the default is to take the mean of the interval — into the intervals of the striplog s:

⚠️ Note that since v0.8.7 this returns a copy; before that, it worked in place.

import numpy as np

s = s.extract(rhob, basis=rhob.basis, name='RHOB', function=np.median)

Now each interval contains the mean RHOB value from that interval:

s[0]
top1.0668
primary
lithologysiltstone
summary308.31 m of siltstone
description
data
RHOB2.3988199234
base309.37199999999996

We can now turn that data ‘field’ into a log, as we did before. This time, however, we can keep the data as the value of the log — i.e. instead of having ‘bins’ from the cutoff (like 1, 2, 3, etc), we want the actual value from an interval (i.e. a density in units of g/cm3). To get this, we pass bins=False.

rhob_blocky, depth, _ = s.to_log(field='RHOB', bins=False, return_meta=True)

We can plot this…

plt.plot(rhob_blocky, depth)
[<matplotlib.lines.Line2D at 0x7fa9f4b1c730>]
../_images/16_Block_logs_41_1.png

…but it’s more useful to make it into a Curve object and store it in the well object w:

rhob_blocky_curve = Curve(rhob_blocky, index=depth, mnemonic='RHOB-B', units='g/cm3')

w.data['RHOB-B'] = rhob_blocky_curve

rhob_blocky_curve.plot()
<AxesSubplot:title={'center':'RHOB-B'}, xlabel='g/cm3'>
../_images/16_Block_logs_43_1.png

Now let’s plot it next to the blocky GR to verify that the blocks are the same:

fig, axs = plt.subplots(ncols=2, figsize=(4, 10), sharey=True)
gr_blocky_curve.plot(ax=axs[0])
rhob_blocky_curve.plot(ax=axs[1])
<AxesSubplot:title={'center':'RHOB-B'}, xlabel='g/cm3'>
../_images/16_Block_logs_45_1.png

Or we can plot everything together using welly (if we first extend the legend again to accommodate the new logs):

C = """curve mnemonic, colour, width
       GR, #ff0000, 1
       GR-B, #ff8800, 1
       RHOB, #0000ff, 1
       RHOB-B, #0088ff, 1
"""

curve_legend = Legend.from_csv(text=C)

big_legend = legend + curve_legend

tracks = ['MD', 'GR', 'GR-B', 'strip', 'RHOB-B', 'RHOB', 'MD']

w.plot(tracks=tracks, legend=big_legend)
../_images/16_Block_logs_47_0.png

© 2020 Agile Scientific, licenced CC-BY.