1 Centre for Innovative Ultrasound Solutions and Dept. of Circulation and Medical Imaging, Norwegian University of Science and Technology, Trondheim, Norway
This Jupyter Notebook is a companion to the article of the same name, published in the Proceedings of the 43rd Scandinavian Symposium on Physical Acoustics in 2020. While the article provides more background information and a tidier presentation of the results, this notebook shows the code underlying these results.
The notebook uses the dlisio library to investigate data from a sonic and an ultrasonic tool in an integrity well log file from the open Volve Data Village dataset. The file in question is WL_RAW_PROD_AC-AIMG-CCL-GR_2013-06-05_2.DLIS
, from well F-11 B. While investigating acoustic data from two specific tools is the main topic of this notebook, other types of data can be similarly investigated using dlisio.
This notebook is published under an MIT license and the newest version was developed using python 3.9.12
. All packages used in this notebook and their versions are listed in requirements.txt
in the root folder of this repository. Run pip install -r requirements.txt
if you wish to install all the correct versions. The newest version of this notebook can always be found on github.
*Privacy note:* This notebook hides well log parameters that contain people's names. This is to avoid their names being tied to the log file by search engines without their explicit consent. Users of the notebook are of course free to uncomment the code lines that hide these names.
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
import dlisio
from dlisio import dlis
import pandas as pd
import numpy as np
import scipy.signal as signal
import logging
import copy
# Ensure that we can see pandas tables of up to 500 rows
pd.set_option('display.max_rows', 500)
# Set a default DPI for all figures in this file. Lower DPI gives smaller figures, higher bigger.
mpl.rcParams['figure.dpi'] = 100
The Volve data is made publically available by Equinor through Azure BlobStore. In the next cell we download the dlis file that we intend to work with in this notebook from that BlobStore. In order to do that, we first need to head over to https://data.equinor.com/dataset/Volve to fetch a sas token (our credentials). After logging in with a Microsoft account and accepting the terms and conditions, scroll to the bottom of the page and copy the sas token from the shared access signature URI. Note that the sas token is only the last part of that URI, i.e. everything after the question mark (excluding the question mark itself). Paste it into the sas
variable in the cell below and run it.
from os import path
from azure.storage.blob import BlobClient
dst = 'WL_RAW_PROD_AC-AIMG-CCL-GR_2013-06-05_2.DLIS'
url = 'https://datavillagesa.blob.core.windows.net/volve/Well_logs_pr_WELL/15_9-F-11 B/11. INTEGRITY LOGS/RAW/WL_RAW_PROD_AC-AIMG-CCL-GR_2013-06-05_2.DLIS'
sas = 'PASTE YOUR SAS TOKEN HERE!'
if not path.isfile(dst):
client = BlobClient.from_blob_url(url, credential = sas)
stream = client.download_blob()
with open(dst, "wb") as f:
f.write(stream.readall())
By default a .DLIS
-file should only contain strings that can be decoded with 'utf-8', which is the default encoding in Python. However, a lot of files use different encodings for strings. These need special care. Sadly, there is no way for dlisio to guess which encoding to use, simply because multiple encodings might work but yield different results. However, dlisio can be told to use whatever encoding you believe is used in the file with the set_encodings function.
For the file that we are investigating in this notebook, we need to use the latin1
encoding.
dlisio.common.set_encodings(['latin1'])
Before starting to work with DLIS and dlisio, there are a couple of concepts you should be familiar with. Firstly, a DLIS-file may contain multiple logical files. In short, this means that there might be multiple files within a .DLIS
disk file. In practice, logical files are independent of each other and you can regard them as such. The dlisio documentation explains the definition of a logical file in more detail.
Because of this, the syntax for opening a DLIS file with dlisio can look a bit strange at a first glance. The syntax we use in the cell below extracts the first logical file into f
and puts the remaining logical files into f_tail
. dlisio's documentation offers more in-detail examples of loading files.
filename = 'WL_RAW_PROD_AC-AIMG-CCL-GR_2013-06-05_2.DLIS'
f, *f_tail = dlis.load(filename)
if len(f_tail): logging.warning('There are more logical files in tail')
Secondly, within a logical file there are one or more Origin objects. These describe the origin of the file, and contain information about which field and well the data is from. The fact that there might be multiple origins opens up the possiblity of the logical file containing information from multiple wells. In that case, the origin must be checked for every piece of information in the logical file to avoid misinterpretation of the information.
Luckily the more common case is that a .DLIS
-file contains one logical file, with one origin. When this is true, we can safely assume that all the information stems from the same well.
origin, *origin_tail = f.origins
if len(origin_tail): logging.warning('f contains multiple origins')
All dlisio objects have a describe()
function that returns a string containing useful overview information about that object. Describing the file gives an overview of how many frames and channels it contains, and which DLIS sets it contains.
f.describe()
------------ Logical File ------------ Description : LogicalFile(MaxWell_13Dlis) Frames : 3 Channels : 310 Known objects -- PROCESS : 15 CALIBRATION-MEASUREMENT : 7 ORIGIN : 1 FILE-HEADER : 1 CALIBRATION-COEFFICIENT : 19 FRAME : 3 TOOL : 7 PARAMETER : 473 EQUIPMENT : 27 CALIBRATION : 9 CHANNEL : 310 AXIS : 2 Unknown objects -- 440-HZVERSIONINFO : 1 440-LIS-INPU : 307 440-CALIBRATION-COEFFICIENT : 19 440-OP-TOOL : 7 440-PASS : 1 440-FILE : 1 440-CHANNEL : 307
We can also run describe()
on the origin
object for some overall information about the data:
origin.describe()
------ Origin ------ name : LAP-87FCBFDC-5AE1-4C07-9822-C5A250FDCFE9 origin : 57 copy : 0 Logical file ID : MaxWell_13Dlis File set name and number : USIT / 1056964608 File number and type : 1 / CUSTOMER Field : Volve Well (id/name) : 15/9-F-11 / 15/9-F-11 B Produced by (code/name) : 440 / Schlumberger Produced for : Statoil Order number : USIT 9_58 Run number : 1 Descent number : 0 Created : 2013-06-06 23:22:34.906000 Created by : HZ-OP:Wireline, (version: 3.1.9755.0) Other programs/services : DTC-H:Digital Telemetry Cartridge - Version H CAL-YA:Casing Anomaly Locator 3-3/8 in 31 Pin Heads DEPTH_DEVICE:Depth Measuring Device USIT-E:USIT Ultrasonic Tool SGT-N:Scintillation Gamma-Ray Tool DSLT-H:Digitizing Sonic Logging Tool - H TENSION_DEVICE:Tension Device LOGGING_CABLE:Logging Cable WAFE:WAFE type LEH-QT:Logging Equipment Head - QT, 3-3/8 inch 31 pin HPHT with Tension Sensor ACTS-B:Auxiliary Compression Tension Sub - B (Only external acquisition supported)
To explore the parameters and channels in the file, we create a helper function that iterates through a sequence of dlisio objects and compiles a pandas dataframe from selected object attributes. We will show how the function is used in the next cell.
def summarize(objs, **kwargs):
"""Create a pd.DataFrame that summarize the content of 'objs', One
object pr. row
Parameters
----------
objs : list()
list of metadata objects
**kwargs
Keyword arguments
Use kwargs to tell summarize() which fields (attributes) of the
objects you want to include in the DataFrame. The parameter name
must match an attribute on the object in 'objs', while the value
of the parameters is used as a column name. Any kwargs are excepted,
but if the object does not have the requested attribute, 'KeyError'
is used as the value.
Returns
-------
summary : pd.DataFrame
"""
summary = []
for attr, label in kwargs.items():
column = []
for obj in objs:
try:
value = getattr(obj, attr)
except AttributeError:
value = 'KeyError'
column.append(value)
summary.append(column)
summary = pd.DataFrame(summary).T
summary.columns = kwargs.values()
return summary
DLIS files contain a number of parameters, which dlisio exposes to the user as Parameter objects. These describe parameters used in the acquisition and processing of data. The parameter value(s) may be scalars or an array.
f.parameters
gives us a sequence of all the parameters in the DLIS file. We can apply the just-defined summarize
helper function to compile a pandas table out of these parameters. dlisio Parameter objects have several attributes, but the ones we will compile here are name
, long_name
, and values
. Furthermore, parameters can have units. While dlisio does not yet have a high-level interface for reading parameter units, we can use a lower-level units interface to fill in the units in the pandas table.
parameter_table = summarize(f.parameters, name='Name', long_name='Long name', values='Value(s)')
# Hide parameters containing names; see the privacy note in the Introduction. Comment out these lines to show them.
mask = ~parameter_table['Name'].isin(['R8', 'RR1', 'WITN', 'ENGI'])
parameter_table = parameter_table[mask]
# Add units through the temporary units interface
units_column = []
for i, par in enumerate(f.parameters):
if i not in parameter_table.index: continue
try:
units_column.append(par.attic['VALUES'].units)
except KeyError:
units_column.append(None)
parameter_table['Units'] = units_column
parameter_table.sort_values('Name')
Name | Long name | Value(s) | Units | |
---|---|---|---|---|
193 | A | Constant A of the Archie Formation Factor - Po... | [1.0] | |
335 | AC_TRIM | AC Main Cable Trim | [-999.25] | ohm |
285 | AFVU | Automatic Fluid Velocity Update | [Off] | |
339 | AGMN | Minimum Gain of Cartridge | [-12] | dB |
340 | AGMX | Maximum Gain of Cartridge | [40] | dB |
11 | ALT_PDAT | Name of Alternate Permanent Datum (for PDAT = ... | [] | |
289 | AMD | Azimuth of Maximum Deviation | [110.30000305175781] | deg |
217 | AMSG | Auxiliary Minimum Sliding Gate | [180.0] | us |
266 | APD | Elevation of Depth Reference (LMF) Above Perma... | [54.900001525878906] | m |
54 | APIN | API Well Number | [] | |
262 | APRE | Atmospheric Pressure | [1013.0] | mbar |
405 | ATEM | Ambient Temperature | [20.0] | degC |
247 | AU_TRIM | AC AUX Cable Trim | [-999.25] | ohm |
29 | AZ_ENABLE | Z-Axis Acceleration Channel Enabled for Real-T... | [NO] | |
59 | AZ_SELECT | Z-Axis Acceleration Channel Selection for Real... | [] | |
86 | BAP | Elevation of the cellar base, for on-shore wells | [-999.25] | m |
263 | BASI | Name of the sedimentary basin in which the wel... | [] | |
72 | BERJ | Bad Echo Rejection | [ON] | |
63 | BG | Gas Formation Volume Factor, Bg | [-999.25] | |
94 | BHAL | GRA Borehole Aluminum Weight Percent | [0.0] | kgf/kgf |
279 | BHK | Drilling Fluid Potassium Concentration | [0.0] | % |
95 | BHS | Borehole Status (Open or Cased Hole) | [OPEN] | |
124 | BHT | Bottom Hole Temperature | [100.0] | degC |
429 | BHT_RM | Bottom Hole Temperature (RM) | [100.0] | degC |
248 | BILI | Bond Index Level for Zone Isolation | [0.800000011920929] | |
147 | BLI | Bottom Log Interval | [3185.0] | m |
273 | BO | Oil Formation Volume Factor, Bo | [-999.25] | |
116 | BPP | Bubble Point Pressure | [-999.25] | psi |
299 | BPT | Bubble Point Temperature | [-999.25] | degC |
355 | BS | Bit Size | [12.25] | in |
334 | BSAL | Borehole Salinity | [0.0] | ppm |
417 | BSAL_RM | Borehole Salinity (NaCl-equivalent concentrati... | [0.0] | ppm |
356 | BSDF | Bit Size Depth From | [2573.0] | m |
357 | BSDT | Bit Size Depth To | [3205.0] | m |
135 | BSSO | Source of Borehole Salinity Data | [Measured Resistivity (GEN-9)] | |
109 | BW | Water Formation Volume Factor, Bw | [-999.25] | |
358 | CADT | Casing Depth To | [3200.0] | m |
459 | CALI_CABLE_TYP | Calibration Cable Type | [7-46 PXS] | |
460 | CALI_DATE | Calibration Date | [28-May-2013] | |
461 | CALI_DATE | Calibration Date | [04-Jun-2013] | |
462 | CALI_SERIALNUM | Calibrator Serial Number | [32] | |
463 | CALI_SERIALNUM | Calibrator Serial Number | [1115] | |
359 | CASG | Casing Grade | [P110] | |
165 | CASN | Casing String Number | [] | |
360 | CBDR | Casing Bottom of Driller | [3200.0] | m |
239 | CBLG | CBL Gate Width | [45.0] | us |
361 | CBLO | Logger Casing Shoe Depth | [3200.0] | m |
75 | CBRA | CBL LQC Reference Amplitude in Free Pipe | [53.0] | mV |
205 | CCL_MULTIPLIER | Casing Collar Locator Multiplier | [3.0] | |
331 | CDEN | Cement Density | [2.0] | g/cm3 |
362 | CDF | Casing Depth From | [600.0] | m |
363 | CDIA | Casing Outer Diameter | [9.625] | in |
87 | CDTS | Correction for Delta-T Shale, Empirical | [100.0] | us/ft |
364 | CGRD | Casing Grade | [P110] | |
267 | CLAB | County/Parish Label, used with the COUN parame... | [County:] | |
82 | CMCF | CBL Cement Type Compensation Factor | [0.678943395614624] | |
410 | CMR_FORM_H2O_SAL | CMR Formation Water Salinity | [0.0] | ppm |
150 | CMTY | Cement Type | [Regular Cement] | |
314 | CN | Company Name | [Statoil] | |
213 | CN1 | Company Name, Line 1 | [] | |
254 | CONT | Continent | [Europe] | |
320 | CONTYP | Conveyance Type | [Wireline] | |
215 | COORDSYS_NAME | Surface coordinate system name | [] | |
134 | COUN | County or Parish | [] | |
365 | CSDE | Casing Density | [0.07961677014827728] | g/cm3 |
366 | CSID | Casing Inner Diameter | [8.54683780670166] | in |
367 | CSIZ | Current Casing Size | [9.625] | in |
210 | CSLH | Flag Indicating Presence of Liner Hanger in Ca... | [No] | |
57 | CTOT | Total Compressibility of Rock and Fluid | [0.007251886650919914] | 1E-6 1/Pa |
368 | CWEI | Casing Weight | [53.499996185302734] | lbm/ft |
369 | CYST | Casing Yield Strength | [110000.0] | psi |
321 | C_ALGO | Collar detection algorithm | [CCLU] | |
296 | C_BLANK | Ignore on each side of each collar, inches | [12] | |
31 | C_DLEN | Collar detection length, inches | [24] | |
337 | C_JNO | Joint number offset | [0] | |
277 | C_MFILT | Apply 3-point median filter to statistics? | [No] | |
8 | C_MPL | Minimum pipe length, inches | [24] | |
240 | C_NUP | Number joints from bottom? | [Yes] | |
90 | C_TPC | Collar detection threshold percentage | [50] | |
176 | C_WIND | Collar detection window length, inches | [600] | |
383 | DATE | Tools Above Rotary Table Date | [05-Jun-2013] | |
178 | DATF | Date Logged From | [04-Jun-2013] | |
292 | DATT | Date Logged To | [05-Jun-2013] | |
384 | DCS | Date Circulation Stopped | [] | |
192 | DC_MODE | Depth Correction Mode | [REALTIME] | |
313 | DC_RT_ENABLE | Depth Correction Real-Time Enabled | [NO] | |
294 | DC_TRIM | DC Main Cable Trim | [-999.25] | ohm |
70 | DDEL | Digitizing Delay | [0.0] | us |
159 | DELT_LOCB | Reference Acceptance Criteria for Magnetic Flu... | [300.0] | nT |
197 | DELT_LOCG | Reference Acceptance Criteria for Gravitationa... | [0.024516625329852104] | m/s2 |
322 | DELT_MDIP | Reference Acceptance Criteria for Magnetic Dip... | [0.44999998807907104] | deg |
194 | DELT_USER_LOCB | Acceptance Criteria for Magnetic Flux Density | [300.0] | nT |
185 | DELT_USER_LOCG | Acceptance Criteria for Gravitational Field St... | [0.024516625329852104] | m/s2 |
309 | DELT_USER_MDIP | Acceptance Criteria for Magnetic Dip Angle | [0.44999998807907104] | deg |
222 | DEPREM1 | Depth Remark 1 | [Schlumberger depth control procedure is follo... | |
190 | DEPREM2 | Depth Remark 2 | [IDW is used as primary depth control device a... | |
102 | DEPREM3 | Depth Remark 3 | [] | |
121 | DEPREM4 | Depth Remark 4 | [] | |
125 | DEPREM5 | Depth Remark 5 | [] | |
180 | DEPREM6 | Depth Remark 6 | [] | |
199 | DEPTH_SYS_TYPE | Depth System Type | [Schlumberger Depth System] | |
318 | DETE | Delta-T Detection | [E1] | |
71 | DFD | Drilling Fluid Density | [1.2799999713897705] | g/cm3 |
201 | DFES | Electrical Stability | [-999.25] | V |
281 | DFL | Drilling Fluid Loss | [-999.25] | cm3 |
311 | DFPH | Drilling Fluid pH | [-999.25] | |
432 | DFT | Drilling Fluid Type | [OIL] | |
244 | DFTS | Drilling Fluid Total Solids | [-999.25] | |
144 | DFV | Drilling Fluid Viscosity | [-999.25] | s |
396 | DFVL | Default Fluid Velocity | [232.99998474121094] | us/ft |
246 | DHGS | Drilling Fluid High Gravity Solids | [-999.25] | |
304 | DIP_AZIMUTH | Azimuth of a dipping plane in the Earth subsur... | [-999.25] | deg |
385 | DLAB | Date Logger At Bottom | [05-Jun-2013] | |
187 | DMAG_CORR_ON | Enable DMAG Correction | [0] | |
211 | DMAG_CUTOFF | DMAG Cutoff Depth | [-999.25] | m |
353 | DMF | Drilling Measured From (Name of Drilling Eleva... | [DF] | |
261 | DNI_DATE | Direction and Inclination Inits Calculation Date | [04-Jun-2013] | |
323 | DOT | Diameter of Transducer Sensor | [4.874000072479248] | in |
137 | DOWR | Drilling Fluid Oil/water Ratio | [-999.25] | |
14 | DSIN | Digitizer Sample Interval | [10.0] | us |
155 | DSSN | Depth System Serial Number | [] | |
168 | DTCM | Delta-T Computation Mode | [FULL] | |
34 | DTF | Delta-T Fluid | [189.0] | us/ft |
133 | DTFS | DSLT Telemetry Frame Size | [536] | |
200 | DTM | Delta-T Matrix | [56.0] | us/ft |
253 | DTMD | Borehole Fluid Slowness | [232.99998474121094] | us/ft |
408 | DTMUD | Delta-T for Mud | [232.99998474121094] | us/ft |
403 | DTMUD_RM | [232.99998474121094] | us/ft | |
214 | DWCO | Digitizer Word Count | [250] | |
105 | EAE | Estimated Azimuth Error | [-999.25] | deg |
257 | ECF | Elevation of Casing Flange | [-999.25] | m |
303 | EDF | Elevation of Derrick Floor | [54.900001525878906] | m |
24 | EDI | Estimated Drillstring Interference | [-999.25] | nT |
170 | EGL | Elevation of Ground Level | [-91.0] | m |
55 | EKB | Elevation of Kelly Bushing | [-999.25] | m |
179 | ELZ | Elevation of Logging Zero Reference | [54.900001525878906] | m |
234 | EMXV | EMEX Voltage | [90] | V |
38 | ENABLED | Equipment or Computation Acquisition Status | [No] | |
39 | ENABLED | Equipment or Computation Acquisition Status | [No] | |
40 | ENABLED | Equipment or Computation Acquisition Status | [Yes] | |
50 | ENABLED | Equipment or Computation Acquisition Status | [Yes] | |
41 | ENABLED | Equipment or Computation Acquisition Status | [Yes] | |
42 | ENABLED | Equipment or Computation Acquisition Status | [Yes] | |
43 | ENABLED | Equipment or Computation Acquisition Status | [No] | |
49 | ENABLED | Equipment or Computation Acquisition Status | [Yes] | |
44 | ENABLED | Equipment or Computation Acquisition Status | [No] | |
48 | ENABLED | Equipment or Computation Acquisition Status | [Yes] | |
47 | ENABLED | Equipment or Computation Acquisition Status | [No] | |
45 | ENABLED | Equipment or Computation Acquisition Status | [Yes] | |
46 | ENABLED | Equipment or Computation Acquisition Status | [No] | |
280 | EPD | Elevation of Permanent Datum (PDAT) above Mean... | [0.0] | m |
464 | EP_DDEQUIP_TYP | Depth measuring device type | [IDW-JA] | |
457 | EP_LCEQUIP_TYP | Logging cable type | [7-46P-XS] | |
467 | EP_TDEQUIP_TYP | Tension device type | [CMTD-B/A] | |
198 | ETIP | Elevation of the TIP above MSL | [54.900001525878906] | m |
333 | FATT | Acoustic Attenuation due to Fluid | [0.0] | dB/m |
227 | FCD | Future Casing (Outer) Diameter | [-999.25] | in |
118 | FCF | CBL Fluid Compensation Factor | [1.0] | |
0 | FD | Fluid Density | [1.0] | g/cm3 |
404 | FEXP | Exponent "m" for Formation Factor Formula | [2.0] | |
1 | FFV | Formation Fluid Viscosity | [0.5] | mPa.s |
136 | FL | Field Location | [North Sea] | |
112 | FL1 | Field Location, Line 1 | [Norwegian Sector] | |
272 | FL2 | Field Location, Line 2 | [] | |
426 | FLDE | Fluid Density (kg/m3) | [1.2799999713897705] | g/cm3 |
67 | FLEV | Depth of Drilling Fluid Level to LMF (Log Meas... | [2.4384000301361084] | m |
183 | FN | Field Name | [Volve] | |
422 | FNUM | F-Numerator for Formation Factor Formula | [1.0] | |
433 | FPHI | Source of Porosity for Formation Factor Formula | [PHI] | |
225 | FSAL | Formation Salinity | [0.0] | ppm |
418 | FVEL | Fluid Interval Transit Time | [232.99998474121094] | us/ft |
434 | GCSE | Generalized Caliper Selection | [BS] | |
115 | GDD | Gas Downhole Density | [0.10000000149011612] | g/cm3 |
394 | GDD_SPRI | Gas Downhole Density | [0.10000000149011612] | g/cm3 |
98 | GDEV | Average Angular Deviation of Borehole from Ver... | [0.0] | deg |
252 | GEOMAG_MODEL | Geomag Model that used for FAC calculation | [BGGM] | |
167 | GGRA | Gas Gravity | [-999.25] | g/cm3 |
195 | GGRD | Geothermal Gradient | [18.22688865661621] | degC/km |
330 | GMLD | Magnetic Dip Angle | [71.67223358154297] | deg |
325 | GMLG | Gravitational Field Strength | [9.81800651550293] | m/s2 |
251 | GMLH | Magnetic Flux Density | [50490.58203125] | nT |
229 | GOBO | Good Bond | [6.259347438812256] | mV |
305 | GOR | Gas Oil Ratio | [-999.25] | m3/m3 |
401 | GOR_SPRI | Gas Oil Ratio | [-999.25] | m3/m3 |
66 | GPIT_CABLE_CONFIDENCE_FACTOR | Confidence factor on cable depth used in GPIT ... | [3.0] | |
203 | GPIT_RECOVERY_SPEED | Ratio (average speed after sticking) / (averag... | [3.0] | |
284 | GPIT_STICKING_DETECTION_THRESHOLD | Sticking detection threshold used in GPIT spee... | [0.019999999552965164] | m/s2 |
395 | GRAD | Real-Time Formation Temp Gradient | [18.22688865661621] | degC/km |
302 | GRDC | RST Grid Current Set Point | [0.0] | deg |
435 | GRSE | Generalized Mud Resistivity Selection | [GEN9] | |
52 | GR_MULTIPLIER | Gamma Ray Multiplier | [1.0] | |
436 | GTSE | Generalized Temperature Selection | [TEMP] | |
85 | GVIS | Gas Viscosity | [0.019999999552965164] | mPa.s |
126 | HDAT | Coordinate system adopted | [SAD69] | |
450 | HID1 | Header Identifier 1 | [] | |
451 | HID2 | Header Identifier 2 | [] | |
449 | HIDE | Header Identifier | [Volve] | |
74 | HIFF | Hydrogen Index of Formation Fluid | [1.0] | |
397 | HILT_GAS_DENSITY | HILT Gas Downhole Density | [0.10000000149011612] | g/cm3 |
232 | HRES | Horizontal Resolution | [5 deg] | |
231 | HTEN_CALI | Head Tension Calibration Method | [Calibration] | |
230 | HTEN_CALI | Head Tension Calibration Method | [Calibration] | |
131 | HTEN_MULTIPL | Head Tension Multiplier or Manual Gain | [-999.25] | |
130 | HTEN_MULTIPL | Head Tension Multiplier or Manual Gain | [1.0] | |
113 | HTEN_PM_REF | Head Tension Plus Reference | [1014.0399169921875] | lbf |
21 | HTEN_SHIFT | Head Tension Shift or Manual Offset | [0.0] | lbf |
22 | HTEN_SHIFT | Head Tension Shift or Manual Offset | [0.0] | lbf |
145 | HTEN_ZM_REF | Head Tension Zero Reference | [0.0] | lbf |
437 | HVCS | Integrated Hole Volume Caliper Selection | [BS] | |
175 | IBG | 1/Gas Formation Volume Factor, 1/Bg | [-999.25] | |
65 | IHVC | Integrated Hole Volume Control | [START] | |
409 | IHVS | Integrated Hole Volume Start Value | [-999.25] | m3 |
438 | ISSBAR | Barite Mud Switch | [NOBARITE] | |
188 | ITTS | Integrated Transit Time Source | [DT] | |
129 | JOBID | Job Identification | [USIT 9_58] | |
393 | KPER | [0.0] | % | |
265 | LATD | Latitude (N=+ S=-) | [58.441654205322266] | deg |
220 | LATI | Latitude | [58° 26' 29.96" N] | |
5 | LCC | Logging Company Code (API DLIS Company Code) | [440] | |
226 | LCC1 | Logging Company Name | [Schlumberger] | |
458 | LCL | Logging Cable Length | [7315.2001953125] | m |
352 | LCSN | Logging Cable Serial Number | [F711165] | |
172 | LLAB | Section/Latitude Label, used with the SECT or ... | [Section] | |
221 | LMF | Logging Measured From (Name of Logging Elevati... | [DF] | |
182 | LNAM | Identification mnemonic for the logging operation | [] | |
389 | LOC1 | Location - 1 Text | [North Sea] | |
300 | LOCG | Gravitational Field Strength | [9.81800651550293] | m/s2 |
13 | LOGSEQ | Log Sequence | [Subsequent trip] | |
329 | LOGTYP | Type of log | [] | |
107 | LOND | Longitude | [1.887463927268982] | deg |
274 | LONG | Longitude | [1° 53' 14.87" E] | |
177 | LSRV | Type of service | [] | |
104 | LUL | Logging Unit Location | [NOBO] | |
312 | LUN | Logging Unit Number | [ONCC A 3815] | |
241 | M | Exponent M of the Archie Formation Factor - Po... | [2.0] | |
163 | MAHTR | Manual High Threshold Reference for first arri... | [120] | |
237 | MANL_GRID_CONV | Flag for manually entering the grid convergenc... | [0] | |
439 | MATR | Rock Matrix for Neutron Porosity Corrections | [LIME] | |
298 | MATT | Maximum Attenuation | [23.392528533935547] | dB/m |
141 | MAX_LOG_SPEED | Toolstring Maximum Logging Speed | [1097.280029296875] | m/h |
78 | MAX_TOOL_SPEED | Maximum service speed allowed for, or attained... | [2057.39990234375] | m/h |
79 | MAX_TOOL_SPEED | Maximum service speed allowed for, or attained... | [1097.280029296875] | m/h |
80 | MAX_TOOL_SPEED | Maximum service speed allowed for, or attained... | [1371.5999755859375] | m/h |
245 | MCI | Minimum Cemented Interval for Isolation | [4.515231132507324] | m |
249 | MCSS | Mudcake Sample Source | [Pressed] | |
33 | MCST | Mudcake Sample Temperature | [20.0] | degC |
4 | MDEC | Magnetic Field Declination | [-1.1452460289001465] | deg |
3 | MDEC | Magnetic Field Declination | [-1.1452460289001465] | deg |
440 | MDEN | Matrix Density for Density Porosity | [2.7100000381469727] | g/cm3 |
288 | MFIN | Magnetic Field Intensity | [0.5049058198928833] | Oe |
89 | MFSS | Mud Filtrate Sample Source | [] | |
37 | MFST | Mud Filtrate Sample Temperature | [20.0] | degC |
53 | MHD | Maximum Hole Deviation | [64.4000015258789] | deg |
196 | MINC | Magnetic Field Inclination | [71.67223358154297] | deg |
242 | MNHTR | Minimum High Threshold Reference for first arr... | [100] | |
6 | MODE | Sonic Firing Mode (e.g. DDBHC = Depth-Derived ... | [CBL] | |
425 | MRES | Mud Resistivity | [500.0] | ohm.m |
51 | MRT | Maximum Recorded Temperature | [-999.25] | degC |
7 | MRT1 | Maximum Recorded Temperature 1 | [-999.25] | degC |
184 | MRT2 | Maximum Recorded Temperature 2 | [-999.25] | degC |
32 | MRT3 | Maximum Recorded Temperature 3 | [-999.25] | degC |
160 | MSA | Minimum Sonic Amplitude | [3.669377088546753] | mV |
157 | MSS | Mud Sample Source | [Active Tank] | |
169 | MST | Mud Sample Temperature | [20.0] | degC |
424 | MST_RM | Mud Sample Temperature (RM) | [20.0] | degC |
191 | MVIS | Mud Filtrate Viscosity | [1.0] | s |
399 | MW | Mud Weight | [1.2799999713897705] | g/cm3 |
414 | MW_RM | Mud Weight (RM) | [1.2799999713897705] | g/cm3 |
20 | N | N Exponent in SW Formula | [2.0] | |
73 | NATI | Nation | [Norway] | |
293 | NMSG | Near Minimum Sliding Gate | [344] | us |
149 | NMXG | Near Maximum Sliding Gate | [1026] | us |
117 | NORTH_REF | North Reference | [0] | |
93 | NPPW | Number of Points Per Waveform | [120] | |
315 | NUMP | Number of Detection Passes | [2] | |
171 | NWPD | Number of Waveforms per Depth | [72] | |
441 | OBM | Oil Based Mud Flag | [YES] | |
101 | ODD | Oil Downhole Density | [0.800000011920929] | g/cm3 |
415 | ODD_SPRI | Oil Downhole Density | [0.800000011920929] | g/cm3 |
207 | ODEN | Oil Density | [-999.25] | g/cm3 |
123 | OFFSHORE_BLOCK_NUMBER | Identification of the Offshore block where the... | [15/9] | |
140 | OGRA | Surface Oil Gravity | [-999.25] | dAPI |
430 | OGRA_SPRI | Gravity of Oil | [-999.25] | dAPI |
402 | OMR | Origin of Mud Resistivity | [Active Tank] | |
398 | OMRX | Origin of Mud Resistivity [CONS(def), AMS, DEP... | [Active Tank] | |
153 | OPER | Operator Code (API DLIS Company Code) | [] | None |
264 | OPLEV | USIT Remove Flagged Data Level | [OPT2] | |
452 | OS1 | Other Services Line 1 | [] | |
453 | OS2 | Other Services Line 2 | [] | |
454 | OS3 | Other Services Line 3 | [] | |
455 | OS4 | Other Services Line 4 | [] | |
456 | OS5 | Other Services Line 5 | [] | |
58 | OVERIDE_LOCB | Override Magnetic Flux Density | [0] | |
270 | OVERIDE_LOCG | Override Gravitational Field Strength | [0] | |
106 | OVERIDE_MAGDEC | Override Magnetic Declination Angle | [0] | |
96 | OVERIDE_MAGDIP | Override Magnetic Dip Angle | [0] | |
216 | OVIS | Oil Viscosity | [1.0] | mPa.s |
92 | PCIDDRL | Packer Inner Diameter - Zoned along driller de... | [-999.25] | in |
128 | PCODDRL | Packer Outer Diameter - Zoned along driller de... | [-999.25] | in |
62 | PDAT | Permanent Datum | [MSL] | |
259 | PHI | Porosity | [0.20000000298023224] | m3/m3 |
295 | PLODDRL | Plug Outer Diameter - Zoned along driller depths | [-999.25] | in |
390 | PMUD | Potassium Concentration in Mud | [0.0] | % |
142 | PROD_MOD_TRC | Software product may do modifications to the d... | [<ProductModificationTrace Version="0.1"><Oper... | |
386 | PVER | Program Version | [20C0-999] | |
132 | R1 | Remarks Line 1 | [] | |
256 | R10 | Remarks Line 10 | [Log objectives: To verify good cement behind ... | |
12 | R11 | Remarks Line 11 | [USIT Resolution for both main and repeat pass... | |
122 | R12 | Remarks Line 12 | [Tool ran as per toolsketch centralized with 5... | |
36 | R13 | Remarks Line 13 | [CBL firing was 15 Hz. Free pipe normalization... | |
324 | R14 | Remarks Line 14 | [Theoretical Z mud was used with K factor: 0.... | |
286 | R15 | Remarks Line 15 | [Well is filled with OBM Environmul 1.28 SG. W... | |
103 | R16 | Remarks Line 16 | [The images have been rotated such that the ce... | |
97 | R17 | Remarks Line 17 | [] | |
317 | R2 | Remarks Line 2 | [] | |
224 | R3 | Remarks Line 3 | [] | |
158 | R4 | Remarks Line 4 | [] | |
223 | R5 | Remarks Line 5 | [] | |
276 | R6 | Remarks Line 6 | [] | |
151 | R7 | Remarks Line 7 | [] | |
301 | R9 | Remarks Line 9 | [Correlated main pass at pup joint at 3154.53 ... | |
173 | RANG | Range | [] | |
181 | RATE | Firing Rate | [R15] | |
84 | RCOD | Reference Calibrator Outer Diameter | [7.0] | in |
91 | RCSO | Reference Calibrator Standoff | [1.3779499530792236] | in |
9 | RCTH | Reference Calibrator Thickness | [0.295199990272522] | in |
99 | REQPERFOINTRVL | Requested Perforation Intervals | [[[-999.25]]] | m |
316 | REQSMPLDEPTH | Requested Sample Depths | [-999.25] | m |
233 | REQSMPLREMARK | Requested Sample Remarks | [] | |
423 | RHOF | Density of the Formation Fluid | [1.0] | g/cm3 |
204 | RIGN | Rig Name | [Maersk Inspirer] | |
152 | RIGTYP | Rig Type | [Jack Up rig] | |
327 | RLAB | Range/Blank Label, used with the RANG paramete... | [Range] | |
238 | RLDT | Reference Log Date (dd-MMM-yyyy) | [] | |
60 | RLNM | Reference Log Name | [] | |
291 | RLRN | Reference Log Run Number | [] | |
282 | RMB | Resistivity of Mud at Bottom Hole Temperature | [170.88723754882812] | ohm.m |
307 | RMCS | Resistivity of Mud Cake Sample | [-999.25] | ohm.m |
76 | RMFB | Resistivity of Mud Filtrate At Bottom Hole Tem... | [128.16542053222656] | ohm.m |
17 | RMFS | Resistivity of Mud Filtrate Sample | [375.0] | ohm.m |
209 | RMS | Resistivity of Mud Sample | [500.0] | ohm.m |
419 | RMS_RM | Resistivity of Mud Sample (RM) | [500.0] | ohm.m |
431 | RMUD | Resistivity of Mud Sample | [500.0] | ohm.m |
421 | RMUX | Resistivity of Mud Sample (SRTX) | [500.0] | ohm.m |
83 | ROMFS | Density of Mud Filtrate Sample | [1.0] | g/cm3 |
376 | RR10 | Run Remark Line 10 | [] | |
375 | RR2 | Run Remark Line 2 | [Correlated main pass at pup joint at 3154.53 ... | |
378 | RR3 | Run Remark Line 3 | [Log objectives: To verify good cement behind ... | |
374 | RR4 | Run Remark Line 4 | [USIT Resolution for both main and repeat pass... | |
373 | RR5 | Run Remark Line 5 | [Tool ran as per toolsketch centralized with 5... | |
382 | RR6 | Run Remark Line 6 | [CBL firing was 15 Hz. Free pipe normalization... | |
377 | RR7 | Run Remark Line 7 | [Theoretical Z mud was used with K factor: 0.... | |
379 | RR8 | Run Remark Line 8 | [Well is filled with OBM Environmul 1.28 SG. W... | |
380 | RR9 | Run Remark Line 9 | [The images have been rotated such that the ce... | |
391 | RTST | Real-Time Surface Temperature | [20.0] | degC |
427 | RT_BSAL | Real-Time Mud Salinity | [0.0] | ppm |
120 | RULB | Rig Up Length at Bottom | [-999.25] | m |
25 | RULS | Rig Up Length at Surface | [-999.25] | m |
336 | RUN | Run Number | [1] | |
442 | RUPI | Pipe Rugosity | [0.00014986000314820558] | m |
16 | RW | Connate Water Resistivity | [1.0] | ohm.m |
189 | RWF | Free Water Resistivity | [1.0] | ohm.m |
228 | RWS | Water Sample Resistivity | [1.0] | ohm.m |
161 | SAG_CORR_ON | Enable Sag Correction | [0] | |
30 | SCORR | Cable Stretch Correction | [-999.25] | m |
219 | SDNV | Number of Vertical Samples used for Micro-debo... | [5] | |
81 | SDTH | Switch Down Threshold | [20000] | |
341 | SDTHOR | Acoustic Impedance STD Horizontal Threshold fo... | [0.5] | |
342 | SDTVER | Acoustic Impedance STD Vertical Threshold for ... | [0.30000001192092896] | |
354 | SEABDEPTH | Water Depth | [91.0] | m |
166 | SECT | Section | [] | |
428 | SEXP_HILT | HILT Saturation Exponent | [2.0] | |
35 | SFAF | Sonic Formation Attenuation Factor | [10.662729263305664] | dB/m |
18 | SFTY | Slowness Formation Type (Fast, Intermediate, S... | [Intermediate] | |
119 | SGAD | Sliding Gate Status | [OFF] | |
218 | SGAI | Selectable Acquisition Gain | [1X] | |
271 | SGCL | Sliding Gate Closing Delta-T | [130] | us/ft |
287 | SGCW | Sliding Gate Closing Width | [25] | us |
23 | SGDT | Sliding Gate Delta-T | [57] | us/ft |
319 | SGOR | Solution Gas Oil Ratio | [-999.25] | m3/m3 |
64 | SGW | Sliding Gate Width | [110] | us |
26 | SHT | Surface Hole Temperature | [20.0] | degC |
411 | SHT_RM | Surface Hole Temperature (RM) | [20.0] | degC |
69 | SLAB | State/Province Label, used with the STAT param... | [State:] | |
326 | SLEV | Signal Level for AGC | [5000.0] | mV |
332 | SOGR | Standoff Distance of the Gamma Ray Tool | [0.0] | in |
143 | SON | Service Order Number | [] | |
2 | SPF | Shots per Foot | [] | None |
443 | SPFS | Sonic Porosity Formula | [R_H] | |
27 | SPSO | Sonic Porosity Source | [DT] | |
235 | SPUD_DATE | Start date when the well drilling began | [] | |
108 | STAT | State or Province | [] | |
100 | STDLC | Subsequent Trip Down Log Correction | [-999.25] | m |
412 | STEM | Surface Temperature | [20.0] | degC |
343 | SUBT | USIT Sub type | [10INC] | |
310 | SUTH | Switch Up Threshold | [1000] | |
138 | TBGD | Tubing Depth | [-999.25] | m |
387 | TCS | Time Circulation Stopped | [] | |
148 | TCUB | T^3 Processing Level | [VXLP] | |
250 | TD | Total Measured Depth | [3185.0] | m |
370 | TDD | Driller Total Depth | [3205.0] | m |
371 | TDL | Logger Total Depth | [3205.0] | m |
468 | TD_CALI_GAIN | Calibration coefficient gain used for Tension ... | [0.8960000276565552] | |
469 | TD_CALI_OFFSET | Calibration coefficient offset used for Tensio... | [-104.0] | |
470 | TD_CALI_PEAK_ERR | Calibration Peak Error Used for Tension Device | [72.0] | |
471 | TD_CALI_PTS | Calibration Points Used for Tension Device | [10] | |
472 | TD_CALI_RMS | Calibration Root Mean Square Error Used for Te... | [32.0] | |
406 | TD_RM | Total Depth (RM) | [3185.0] | m |
164 | TD_TOLERANCE_LEVEL | Tests and Diagnostics Tasks Tolerance Level | [Shop] | |
420 | TET_SHOT_VT | Fluid velocity | [189.0] | us/ft |
290 | THDH | Maximum Search Thickness (percentage of nominal) | [130.0] | % |
111 | THDL | Minimum Search Thickness (percentage of nominal) | [70.0] | % |
283 | THDP | Thickness Detection Policy | [Fundamental] | |
372 | THNO | Nominal Thickness of Casing | [0.539080798625946] | in |
388 | TLAB | Time Logger At Bottom | [11:32:15] | |
243 | TLI | Top Log Interval | [2500.0] | m |
269 | TLLAB | Township or Longitude Label for Log Heading, u... | [Township] | |
444 | TMUC | Type of Mud | [OBM] | |
351 | TNDSN | Tension Device Serial Number | [1412] | |
278 | TOOLS | Tool or tool string | [] | |
445 | TOOL_MAP_1 | Tool Object Map | [<ToolMaps><ToolMap><HN>DTC-H</HN><MN>DTC-H</M... | |
446 | TOOL_MAP_2 | Tool Object Map | [olMap><ToolMap><HN>USIT-E</HN><MN>USIT</MN><I... | |
447 | TOOL_MAP_3 | Tool Object Map | [N>TENSION_DEVICE</HN><MN>NONE</MN><InsNo>0</I... | |
448 | TOOL_MAP_4 | Tool Object Map | [lMap><ToolMap><HN>LEH-QT</HN><MN>LEHQT</MN><I... | |
88 | TOTC | Total Azimuthal Correction | [-1.1452460289001465] | deg |
258 | TOWN | Township | [] | |
162 | TPOS | Tool Position: Centered or Eccentered | [ECCENTERED] | |
338 | TVD | True Vertical Depth | [0.0] | m |
127 | TWS | Connate Water Temperature | [20.0] | degC |
416 | TWS_RM | Temperature of Water Sample (RM) | [20.0] | degC |
349 | U-USIT_DDT5 | USIC Downhole Decimation for T5 only | [0_NONE] | |
344 | ULOG | Logging Objective | [MEASURE] | |
345 | UMAO | USIT Measurement Angular Offset | [18.0] | deg |
208 | UMFR | Modulation Frequency | [333333.0] | Hz |
346 | UPAT | Emission Pattern | [300K] | |
297 | USFR | Ultrasonic Sampling Frequency | [500000.0] | Hz |
347 | USTO | USIT Time Offset | [-2.0] | us |
348 | USUB | USIT Sub Identifier | [10INC] | |
146 | UWID | Unique Well Identifier | [15/9-F-11 B] | |
350 | UWKM | Working Mode | [D606005L] | |
61 | VCAS | Ultrasonic Transversal Velocity in Casing | [51.400001525878906] | us/ft |
308 | VDLG | VDL Manual Gain | [5.0] | |
275 | VHOL | Cumulative Hole Volume | [-999.25] | m3 |
156 | VILL_VDCLRAT | Volume fraction of dry clay composed of Illite | [-999.25] | |
392 | VIS_OBMF | Viscosity of Oil Based Mud Filtrate | [1.0] | mPa.s |
110 | VKAO_VDCLRAT | Volume fraction of dry clay composed of Kaolinite | [-999.25] | |
186 | VMON_VDCLRAT | Volume fraction of dry clay composed of Montmo... | [-999.25] | |
174 | VRES | Vertical Resolution | [6.0INCH] | |
154 | WDD | Water Downhole Density | [1.0] | g/cm3 |
68 | WDEP | Water Depth | [91.0] | m |
465 | WHEEL_CORR1 | Wheel Correction 1 | [-2.0] | |
466 | WHEEL_CORR2 | Wheel Correction 2 | [-4.0] | |
306 | WINB | Window Begin Time | [53.79532241821289] | us |
260 | WINE | Window End Time | [120.3934326171875] | us |
10 | WLEN | T^3 Processing Length | [32.32666778564453] | us |
15 | WMOD | Waveform Firing Mode | [FULL] | |
407 | WMUC | Weight of Mud for CET | [1.2799999713897705] | g/cm3 |
413 | WMUD | Weight of Mud | [1.2799999713897705] | g/cm3 |
212 | WN | Well Name | [15/9-F-11 B] | |
56 | WSAL | Water Salinity | [-999.25] | ppm |
400 | WSAL_SPRI | Water Salinity | [-999.25] | ppm |
114 | WVIS | Water Viscosity | [0.5] | mPa.s |
268 | ZCAS | Acoustic Impedance of Casing | [46.25] | Mrayl |
28 | ZCMT | Acoustic Impedance of Cement | [5.599999904632568] | Mrayl |
328 | ZINI | Initial Estimate of Cement Impedance | [-1.0] | Mrayl |
236 | ZMUD | Acoustic Impedance of Mud | [1.590000033378601] | Mrayl |
19 | ZRCS | Tool Zero Reference Check at Surface | [-999.25] | m |
255 | ZTCM | Acoustic Impedance Threshold for Cement | [2.5999999046325684] | Mrayl |
202 | ZTGS | Acoustic Impedance Threshold for Gas | [0.30000001192092896] | Mrayl |
Some of these parameters give a description of the the logged well and the logging run:
desc_parameters = ['CN', 'WN', 'FN', # Well ID
'NATI', 'CONT', 'FL', 'FL1', 'FL2', 'LONG', 'LATI', # Well location
'DLAB', 'TLAB', # Time and date of well logging
'CSIZ', 'THNO', 'BS'] # Casing and well parameters
# Create a table out of the parameters in desc_parameters
desc_table = parameter_table.loc[parameter_table['Name'].isin(desc_parameters)].copy()
# Sort the table in the same order as desc_parameters
categorical_sorter = pd.Categorical(desc_parameters, desc_parameters, ordered=True)
desc_table['Name'] = desc_table['Name'].astype(categorical_sorter.dtype)
desc_table.sort_values(by='Name', inplace=True)
display(desc_table)
Name | Long name | Value(s) | Units | |
---|---|---|---|---|
314 | CN | Company Name | [Statoil] | |
212 | WN | Well Name | [15/9-F-11 B] | |
183 | FN | Field Name | [Volve] | |
73 | NATI | Nation | [Norway] | |
254 | CONT | Continent | [Europe] | |
136 | FL | Field Location | [North Sea] | |
112 | FL1 | Field Location, Line 1 | [Norwegian Sector] | |
272 | FL2 | Field Location, Line 2 | [] | |
274 | LONG | Longitude | [1° 53' 14.87" E] | |
220 | LATI | Latitude | [58° 26' 29.96" N] | |
385 | DLAB | Date Logger At Bottom | [05-Jun-2013] | |
388 | TLAB | Time Logger At Bottom | [11:32:15] | |
367 | CSIZ | Current Casing Size | [9.625] | in |
372 | THNO | Nominal Thickness of Casing | [0.539080798625946] | in |
355 | BS | Bit Size | [12.25] | in |
The file also contains a series of remarks describing the logging run in the parameters R1 to R17. We can easily retrieve all of them using a regular expression to find all parameters where the name is R followed by one or two digits:
remarks = f.find('PARAMETER', '^R[0-9]{1,2}')
remarks = sorted(remarks, key=lambda x: int(x.name[1:]) )
for remark in remarks:
#if not remark.values: continue # Uncomment to skip empty remarks
if remark.name == 'R8': continue # Hide a remark containing a name; comment out the line to show the remark
print(f'{remark.name}: {" ".join(remark.values)}')
R1: R2: R3: R4: R5: R6: R7: R9: Correlated main pass at pup joint at 3154.53 mMD R10: Log objectives: To verify good cement behind 9 5/8 in casing 53.5 ppf P110. (Nominal ID: 8.547 in) R11: USIT Resolution for both main and repeat pass is 5 deg 6 in with Speed 2000 fph. R12: Tool ran as per toolsketch centralized with 5 GEMCOS (OD: 8.8 in) and 2 built in USIS Centralizers R13: CBL firing was 15 Hz. Free pipe normalization is adjusted at 2453.7 mMD with CBAF value 1.03. Top of cement observed at 2680 mMD R14: Theoretical Z mud was used with K factor: 0.95; FVEL: 233 us/ft and Z mud: 1.59 Mrayl R15: Well is filled with OBM Environmul 1.28 SG. Well is cemented with standard Class G Cement with ZCMT 5.6 MRayl R16: The images have been rotated such that the centre of each image track represents the low side of tubular R17:
The majority of a DLIS file is typically made up of measurement data taken along the well bore, or computed data based on such measurements. This data is stored in channels, which we will get to soon, and channels are organised in frames, which dlisio exposes as Frame objects. All of the channels in one frame are indexed against the same reference channel, which typically specifies the time or depth of each measurement.
for frame in f.frames:
index_channel = next(ch for ch in frame.channels if ch.name == frame.index)
print(f'Frame {frame.name}:')
print(f'Description : {frame.description}')
print(f'Indexed by : {frame.index_type}')
print(f'Interval : [{frame.index_min}, {frame.index_max}] {index_channel.units}')
print(f'Direction : {frame.direction}')
print(f'Constant spacing : {frame.spacing} {index_channel.units}')
print(f'Index channel : {index_channel}')
print(f'No. of channels : {len(frame.channels)}')
print()
Frame 60B: Description : DOMAIN_DEPTH Indexed by : BOREHOLE-DEPTH Interval : [974340.0, 1253940.0] 0.1 in Direction : DECREASING Constant spacing : -60.0 0.1 in Index channel : Channel(TDEP) No. of channels : 289 Frame 10B: Description : DOMAIN_DEPTH Indexed by : BOREHOLE-DEPTH Interval : [974790.0, 1253910.0] 0.1 in Direction : DECREASING Constant spacing : -10.0 0.1 in Index channel : Channel(TDEP) No. of channels : 7 Frame 20B: Description : DOMAIN_DEPTH Indexed by : BOREHOLE-DEPTH Interval : [976060.0, 1250960.0] 0.1 in Direction : DECREASING Constant spacing : -20.0 0.1 in Index channel : Channel(TDEP) No. of channels : 14
We see that all of these frames are indexed by borehole depth. This is the depth measured along the length of the wellbore, also known as measured depth, and should not be confused with true vertical depth. Every frame has an index channel called TDEP
, which is stored in units of 0.1 in. We will later have to convert this to metres. Every frame is stored from the bottom of the well to the top. This is because the measurements were made by log tools starting at the bottom of the well and moving up.
A channel is a sequence of measured or computed samples that are indexed against some physical quantity, e.g. depth or time, stored in the index channel of the frame that the channel belongs to. The DLIS standard supports multidimensional samples. Each sample can be a scalar or a multidimensional array. dlisio exposes channels as Channel objects.
f.channels
gives us a sequence of every channel present in the file. We can use the summarize()
helper function again to make a list of all the channels in the file. Out of the attributes of dlisio Channel objects, we will compile name
, long_name
, units
, dimension
, and frame
:
channel_table = summarize(f.channels, name='Name', long_name='Long name', units='Units',
dimension='Dimension', frame='Frame')
channel_table.sort_values('Name')
Name | Long name | Units | Dimension | Frame | |
---|---|---|---|---|---|
3 | AGMA | Maximum Allowed Electronic Gain | dB | [1] | Frame(60B) |
4 | AGMI | Minimum Allowed Electronic Gain | dB | [1] | Frame(60B) |
5 | AIAV | Acoustic Impedance Average | Mrayl | [1] | Frame(60B) |
6 | AIAV_RF | Median of Unflagged Measured Impedance | Mrayl | [1] | Frame(60B) |
7 | AIBK | Acoustic Impedance | Mrayl | [72] | Frame(60B) |
8 | AIMN | Acoustic Impedance Minimum | Mrayl | [1] | Frame(60B) |
9 | AIMN_RF | Minimum of Unflagged Acoustic Impedance | Mrayl | [1] | Frame(60B) |
10 | AIMX | Acoustic Impedance Maximum | Mrayl | [1] | Frame(60B) |
11 | AIMX_RF | Maximum of Unflagged Acoustic Impedance | Mrayl | [1] | Frame(60B) |
12 | AI_MICRO_DEBONDING_IMAGE | Acoustic Impedance With Micro-debonding Image | Mrayl | [72] | Frame(60B) |
293 | AREA | Area of Borehole | in2 | [1] | Frame(60B) |
13 | AWAV | Amplitude of Wave Average | dB | [1] | Frame(60B) |
14 | AWAV_RF | Average of Unflagged Wave Amplitude | dB | [1] | Frame(60B) |
15 | AWBK | Amplitude of Wave | dB | [72] | Frame(60B) |
16 | AWBK_RF | Unflagged Echo Amplitude minus median amplitude | dB | [72] | Frame(60B) |
17 | AWMN | Amplitude of Wave Minimum | dB | [1] | Frame(60B) |
18 | AWMN_RF | Minimum of Unflagged Wave Amplitude | dB | [1] | Frame(60B) |
19 | AWMX | Amplitude of Wave Maximum | dB | [1] | Frame(60B) |
20 | AWMX_RF | Maximum of Unflagged Wave Amplitude | dB | [1] | Frame(60B) |
21 | AZEC | Azimuth of Eccentering | deg | [1] | Frame(60B) |
22 | AZEC_EXT | Azimuth of External Eccentering | deg | [1] | Frame(60B) |
23 | AZEC_RF | Azimuth of Eccentering for Unflagged Waves | deg | [1] | Frame(60B) |
283 | AZIM | Measured Azimuth | deg | [1] | Frame(60B) |
279 | BHPR | Borehole Pressure | psi | [1] | Frame(60B) |
290 | BHRM | Borehole Mud Resistivity | ohm.m | [1] | Frame(60B) |
248 | BI | Bond Index | [1] | Frame(60B) | |
24 | BPRE | Burst Pressure | psi | [1] | Frame(60B) |
275 | BS | Bit Size | in | [1] | Frame(60B) |
250 | CBL | CBL Amplitude | mV | [1] | Frame(60B) |
249 | CBLF | CBL Amplitude (Fluid Compensated) | mV | [1] | Frame(60B) |
251 | CBSL | CBL Amplitude (Sliding Gate) | mV | [1] | Frame(60B) |
294 | CCL | Casing Collar Locator Amplitude | [1] | Frame(10B) | |
25 | CCLU | Casing Collar Locator Ultrasonic | in | [1] | Frame(60B) |
260 | CDF | Calibrated Downhole Force | lbf | [1] | Frame(60B) |
26 | CEMR | Ratio of Cement Measurements to Total | [1] | Frame(60B) | |
27 | CFVL | Memorized Fluid Acoustic Slowness | us/ft | [1] | Frame(60B) |
252 | CMCG | CBL Cement Type Compensation Gain | [1] | Frame(60B) | |
277 | CS | Cable Speed | m/h | [1] | Frame(60B) |
278 | CS | Cable Speed | m/h | [1] | Frame(10B) |
258 | CTEM | Cartridge Temperature | degC | [1] | Frame(60B) |
271 | CVEL | Cable Velocity | m/h | [1] | Frame(60B) |
272 | CVEL | Cable Velocity | m/h | [1] | Frame(10B) |
28 | CZMD | Acoustic Impedance of Mud | Mrayl | [1] | Frame(60B) |
267 | DCAL | Differential Caliper | in | [1] | Frame(60B) |
280 | DEPTH | Depth Index | m | [1] | Frame(60B) |
261 | DF | Uncalibrated Downhole Force | lbf | [1] | Frame(60B) |
266 | DLS | Dog Leg Severity | deg/30m | [1] | Frame(60B) |
30 | ECCE | Amplitude of Eccentering | in | [1] | Frame(60B) |
29 | ECCEEXT_RF | Unflagged Data External Eccentering | in | [1] | Frame(60B) |
31 | ECCE_EXT | USIT External Eccentering | in | [1] | Frame(60B) |
32 | ECCE_RF | Amplitude of Eccentering for Unflagged Waves | in | [1] | Frame(60B) |
255 | ECGR | Environmentally Corrected Gamma Ray | gAPI | [1] | Frame(60B) |
268 | ED | East Departure | m | [1] | Frame(60B) |
307 | EHGR | High Resolution Corrected Gamma Ray | gAPI | [1] | Frame(20B) |
33 | ERAV | External Radii Average | in | [1] | Frame(60B) |
34 | ERAV_EXT | Average External Radius Corrected for External... | in | [1] | Frame(60B) |
35 | ERAV_RF | Median of External Radii for Unflagged Waves | in | [1] | Frame(60B) |
36 | ERBA_EXT | External Radii Corrected for External Eccenter... | in | [72] | Frame(60B) |
38 | ERBK | External Radii | in | [72] | Frame(60B) |
37 | ERBK_EXT | External Radii Corrected for External Eccentering | in | [72] | Frame(60B) |
39 | ERBN_EXT | External Radii Corrected for External Eccenter... | in | [72] | Frame(60B) |
40 | ERMN | External Radii Minimum | in | [1] | Frame(60B) |
41 | ERMN_EXT | Minimum External Radius Corrected for External... | in | [1] | Frame(60B) |
42 | ERMN_RF | Minimum of Unflagged External Radii | in | [1] | Frame(60B) |
43 | ERMX | External Radii Maximum | in | [1] | Frame(60B) |
44 | ERMX_EXT | Maximum External Radius Corrected for External... | in | [1] | Frame(60B) |
45 | ERMX_RF | Maximum of Unflagged External Radii | in | [1] | Frame(60B) |
46 | ERNO | Nominal Casing External Radius | in | [1] | Frame(60B) |
295 | FCCL | Casing Collar Pip | [1] | Frame(10B) | |
270 | FCD | Future Casing Diameter | in | [1] | Frame(60B) |
282 | FF | Formation Factor | [1] | Frame(60B) | |
47 | GASR | Ratio of Gas Measurements to Total | [1] | Frame(60B) | |
289 | GHD | Borehole Diameter | in | [1] | Frame(60B) |
48 | GNMN | Waveform Gain Minimum | dB | [1] | Frame(60B) |
49 | GNMX | Waveform Gain Maximum | dB | [1] | Frame(60B) |
256 | GR | Gamma Ray | gAPI | [1] | Frame(60B) |
265 | GTEM | Generalized Borehole Temperature | degC | [1] | Frame(60B) |
276 | HDAR | Hole Diameter from Area | in | [1] | Frame(60B) |
308 | HGR | High Resolution Gamma Ray | gAPI | [1] | Frame(20B) |
50 | HRTT | Histogram of Raw Transit Time Index | us | [180] | Frame(60B) |
259 | HV | Head Voltage | V | [1] | Frame(60B) |
281 | ICV | Integrated Cement Volume | m3 | [1] | Frame(60B) |
51 | IDQC | Internal Diameter Quality Check | in | [1] | Frame(60B) |
262 | IDWD | Borehole depth as measured by the IDW (Integra... | m | [1] | Frame(60B) |
274 | IHV | Integrated Hole Volume | m3 | [1] | Frame(60B) |
273 | INCL | Hole inclination | deg | [1] | Frame(60B) |
52 | IRAV | Internal Radius Averaged Value | in | [1] | Frame(60B) |
53 | IRAV_EXT | Average Internal Radius Corrected for External... | in | [1] | Frame(60B) |
54 | IRAV_RF | Median Internal Radius of Casing for Unflagged... | in | [1] | Frame(60B) |
55 | IRBK | Internal Radii Normalized | in | [72] | Frame(60B) |
56 | IRBK_EXT | Internal Radii Corrected for External Eccentering | in | [72] | Frame(60B) |
57 | IRBK_RF | Unflagged External Radii of Casing | in | [72] | Frame(60B) |
58 | IRMN | Internal Radius Minimum Value | in | [1] | Frame(60B) |
59 | IRMN_EXT | Minimum Internal Radius Corrected for External... | in | [1] | Frame(60B) |
60 | IRMN_RF | Minimum of Unflagged Internal Radii Corrected ... | in | [1] | Frame(60B) |
61 | IRMX | Internal Radius Maximum Value | in | [1] | Frame(60B) |
62 | IRMX_EXT | Maximum Internal Radius Corrected for External... | in | [1] | Frame(60B) |
63 | IRMX_RF | Maximum of Unflagged Internal Radii Corrected ... | in | [1] | Frame(60B) |
64 | IRNO | Nominal Casing Internal Radius | in | [1] | Frame(60B) |
292 | MD | Measured Depth | 0.1 in | [1] | Frame(60B) |
65 | MDR | Micro-debonding Ratio | [1] | Frame(60B) | |
66 | MLOSS | Metal Loss | % | [1] | Frame(60B) |
286 | ND | North Departure | m | [1] | Frame(60B) |
67 | RB | Relative Bearing | deg | [1] | Frame(60B) |
68 | RB_USIT | USIT RB | deg | [1] | Frame(60B) |
296 | RCCL | Raw Casing Collar Locator Amplitude | [1] | Frame(10B) | |
257 | RGR | Raw Gamma Ray | gAPI | [1] | Frame(60B) |
309 | RHGR | High Resolution Raw Gamma Ray | gAPI | [1] | Frame(20B) |
264 | ROAZ | Azimuth from rotary table to target | deg | [1] | Frame(60B) |
69 | RSAV | Motor Revolution Speed | c/s | [1] | Frame(60B) |
291 | STIT | Stuck Tool Indicator, Total | m | [1] | Frame(60B) |
70 | T1BK | Internal Radius Metal Loss | in | [72] | Frame(60B) |
71 | T2AV | Thickness Average of Casing minus Nominal | in | [1] | Frame(60B) |
72 | T2BK | Thickness of Casing minus Nominal | in | [72] | Frame(60B) |
0 | TDEP | Tool Depth | 0.1 in | [1] | Frame(60B) |
2 | TDEP | Tool Depth | 0.1 in | [1] | Frame(20B) |
1 | TDEP | Tool Depth | 0.1 in | [1] | Frame(10B) |
288 | TDSP | Total Displacement (of survey measure point fr... | m | [1] | Frame(60B) |
285 | TENS | Cable Tension | lbf | [1] | Frame(10B) |
284 | TENS | Cable Tension | lbf | [1] | Frame(60B) |
73 | THAV | Thickness Average Value | in | [1] | Frame(60B) |
74 | THAV_RF | Average of Unflagged Casing Thickness | in | [1] | Frame(60B) |
75 | THBK | Casing Thickness Normalized | in | [72] | Frame(60B) |
76 | THBK_RF | Image of Unflagged Thickness of Casing | in | [72] | Frame(60B) |
77 | THMN | Thickness Minimum Value | in | [1] | Frame(60B) |
78 | THMN_RF | Minimum of Unflagged Casing Thickness | in | [1] | Frame(60B) |
79 | THMX | Thickness Maximum Value | in | [1] | Frame(60B) |
80 | THMX_RF | Maximum of Unflagged Casing Thickness | in | [1] | Frame(60B) |
81 | THNO | Nominal Casing Thickness | in | [1] | Frame(60B) |
254 | TT | Transit Time for CBL | us | [1] | Frame(60B) |
297 | TT1 | Transit Time 1 | us | [1] | Frame(20B) |
299 | TT2 | Transit Time 2 | us | [1] | Frame(20B) |
298 | TT2S | Transit Time 2 Secondary | us | [1] | Frame(20B) |
82 | TTAV | Transit time average | us | [1] | Frame(60B) |
83 | TTBK | Transit Time | us | [72] | Frame(60B) |
84 | TTMN | Transit Time Minimum Value | us | [1] | Frame(60B) |
85 | TTMX | Transit Time Maximum Value | us | [1] | Frame(60B) |
253 | TTSL | Transit Time (Sliding Gate) | us | [1] | Frame(60B) |
263 | TVD | True Vertical Depth | m | [1] | Frame(60B) |
287 | TVDSS | Sub-sea TVD: True Vertical Depth measured from... | m | [1] | Frame(60B) |
96 | U-ED1 | External Diameter in USI mode | in | [1] | Frame(60B) |
86 | U-ED10 | External Diameter in USI mode | in | [1] | Frame(60B) |
87 | U-ED11 | External Diameter in USI mode | in | [1] | Frame(60B) |
88 | U-ED12 | External Diameter in USI mode | in | [1] | Frame(60B) |
89 | U-ED13 | External Diameter in USI mode | in | [1] | Frame(60B) |
90 | U-ED14 | External Diameter in USI mode | in | [1] | Frame(60B) |
91 | U-ED15 | External Diameter in USI mode | in | [1] | Frame(60B) |
92 | U-ED16 | External Diameter in USI mode | in | [1] | Frame(60B) |
93 | U-ED17 | External Diameter in USI mode | in | [1] | Frame(60B) |
94 | U-ED18 | External Diameter in USI mode | in | [1] | Frame(60B) |
95 | U-ED19 | External Diameter in USI mode | in | [1] | Frame(60B) |
107 | U-ED2 | External Diameter in USI mode | in | [1] | Frame(60B) |
97 | U-ED20 | External Diameter in USI mode | in | [1] | Frame(60B) |
98 | U-ED21 | External Diameter in USI mode | in | [1] | Frame(60B) |
99 | U-ED22 | External Diameter in USI mode | in | [1] | Frame(60B) |
100 | U-ED23 | External Diameter in USI mode | in | [1] | Frame(60B) |
101 | U-ED24 | External Diameter in USI mode | in | [1] | Frame(60B) |
102 | U-ED25 | External Diameter in USI mode | in | [1] | Frame(60B) |
103 | U-ED26 | External Diameter in USI mode | in | [1] | Frame(60B) |
104 | U-ED27 | External Diameter in USI mode | in | [1] | Frame(60B) |
105 | U-ED28 | External Diameter in USI mode | in | [1] | Frame(60B) |
106 | U-ED29 | External Diameter in USI mode | in | [1] | Frame(60B) |
115 | U-ED3 | External Diameter in USI mode | in | [1] | Frame(60B) |
108 | U-ED30 | External Diameter in USI mode | in | [1] | Frame(60B) |
109 | U-ED31 | External Diameter in USI mode | in | [1] | Frame(60B) |
110 | U-ED32 | External Diameter in USI mode | in | [1] | Frame(60B) |
111 | U-ED33 | External Diameter in USI mode | in | [1] | Frame(60B) |
112 | U-ED34 | External Diameter in USI mode | in | [1] | Frame(60B) |
113 | U-ED35 | External Diameter in USI mode | in | [1] | Frame(60B) |
114 | U-ED36 | External Diameter in USI mode | in | [1] | Frame(60B) |
116 | U-ED4 | External Diameter in USI mode | in | [1] | Frame(60B) |
117 | U-ED5 | External Diameter in USI mode | in | [1] | Frame(60B) |
118 | U-ED6 | External Diameter in USI mode | in | [1] | Frame(60B) |
119 | U-ED7 | External Diameter in USI mode | in | [1] | Frame(60B) |
120 | U-ED8 | External Diameter in USI mode | in | [1] | Frame(60B) |
121 | U-ED9 | External Diameter in USI mode | in | [1] | Frame(60B) |
132 | U-ID1 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
122 | U-ID10 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
123 | U-ID11 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
124 | U-ID12 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
125 | U-ID13 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
126 | U-ID14 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
127 | U-ID15 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
128 | U-ID16 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
129 | U-ID17 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
130 | U-ID18 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
131 | U-ID19 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
143 | U-ID2 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
133 | U-ID20 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
134 | U-ID21 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
135 | U-ID22 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
136 | U-ID23 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
137 | U-ID24 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
138 | U-ID25 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
139 | U-ID26 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
140 | U-ID27 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
141 | U-ID28 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
142 | U-ID29 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
151 | U-ID3 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
144 | U-ID30 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
145 | U-ID31 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
146 | U-ID32 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
147 | U-ID33 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
148 | U-ID34 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
149 | U-ID35 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
150 | U-ID36 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
152 | U-ID4 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
153 | U-ID5 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
154 | U-ID6 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
155 | U-ID7 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
156 | U-ID8 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
157 | U-ID9 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
158 | U-USIT_AZECEXT_RF | Azimuth of External Eccentering for Unflagged ... | deg | [1] | Frame(60B) |
159 | U001 | Waveform for Azimuth 01 | [120] | Frame(60B) | |
160 | U002 | Waveform for Azimuth 02 | [120] | Frame(60B) | |
161 | U003 | Waveform for Azimuth 03 | [120] | Frame(60B) | |
162 | U004 | Waveform for Azimuth 04 | [120] | Frame(60B) | |
163 | U005 | Waveform for Azimuth 05 | [120] | Frame(60B) | |
164 | U006 | Waveform for Azimuth 06 | [120] | Frame(60B) | |
165 | U007 | Waveform for Azimuth 07 | [120] | Frame(60B) | |
166 | U008 | Waveform for Azimuth 08 | [120] | Frame(60B) | |
167 | U009 | Waveform for Azimuth 09 | [120] | Frame(60B) | |
168 | U010 | Waveform for Azimuth 10 | [120] | Frame(60B) | |
169 | U011 | Waveform for Azimuth 11 | [120] | Frame(60B) | |
170 | U012 | Waveform for Azimuth 12 | [120] | Frame(60B) | |
171 | U013 | Waveform for Azimuth 13 | [120] | Frame(60B) | |
172 | U014 | Waveform for Azimuth 14 | [120] | Frame(60B) | |
173 | U015 | Waveform for Azimuth 15 | [120] | Frame(60B) | |
174 | U016 | Waveform for Azimuth 16 | [120] | Frame(60B) | |
175 | U017 | Waveform for Azimuth 17 | [120] | Frame(60B) | |
176 | U018 | Waveform for Azimuth 18 | [120] | Frame(60B) | |
177 | U019 | Waveform for Azimuth 19 | [120] | Frame(60B) | |
178 | U020 | Waveform for Azimuth 20 | [120] | Frame(60B) | |
179 | U021 | Waveform for Azimuth 21 | [120] | Frame(60B) | |
180 | U022 | Waveform for Azimuth 22 | [120] | Frame(60B) | |
181 | U023 | Waveform for Azimuth 23 | [120] | Frame(60B) | |
182 | U024 | Waveform for Azimuth 24 | [120] | Frame(60B) | |
183 | U025 | Waveform for Azimuth 25 | [120] | Frame(60B) | |
184 | U026 | Waveform for Azimuth 26 | [120] | Frame(60B) | |
185 | U027 | Waveform for Azimuth 27 | [120] | Frame(60B) | |
186 | U028 | Waveform for Azimuth 28 | [120] | Frame(60B) | |
187 | U029 | Waveform for Azimuth 29 | [120] | Frame(60B) | |
188 | U030 | Waveform for Azimuth 30 | [120] | Frame(60B) | |
189 | U031 | Waveform for Azimuth 31 | [120] | Frame(60B) | |
190 | U032 | Waveform for Azimuth 32 | [120] | Frame(60B) | |
191 | U033 | Waveform for Azimuth 33 | [120] | Frame(60B) | |
192 | U034 | Waveform for Azimuth 34 | [120] | Frame(60B) | |
193 | U035 | Waveform for Azimuth 35 | [120] | Frame(60B) | |
194 | U036 | Waveform for Azimuth 36 | [120] | Frame(60B) | |
195 | U037 | Waveform for Azimuth 37 | [120] | Frame(60B) | |
196 | U038 | Waveform for Azimuth 38 | [120] | Frame(60B) | |
197 | U039 | Waveform for Azimuth 39 | [120] | Frame(60B) | |
198 | U040 | Waveform for Azimuth 40 | [120] | Frame(60B) | |
199 | U041 | Waveform for Azimuth 41 | [120] | Frame(60B) | |
200 | U042 | Waveform for Azimuth 42 | [120] | Frame(60B) | |
201 | U043 | Waveform for Azimuth 43 | [120] | Frame(60B) | |
202 | U044 | Waveform for Azimuth 44 | [120] | Frame(60B) | |
203 | U045 | Waveform for Azimuth 45 | [120] | Frame(60B) | |
204 | U046 | Waveform for Azimuth 46 | [120] | Frame(60B) | |
205 | U047 | Waveform for Azimuth 47 | [120] | Frame(60B) | |
206 | U048 | Waveform for Azimuth 48 | [120] | Frame(60B) | |
207 | U049 | Waveform for Azimuth 49 | [120] | Frame(60B) | |
208 | U050 | Waveform for Azimuth 50 | [120] | Frame(60B) | |
209 | U051 | Waveform for Azimuth 51 | [120] | Frame(60B) | |
210 | U052 | Waveform for Azimuth 52 | [120] | Frame(60B) | |
211 | U053 | Waveform for Azimuth 53 | [120] | Frame(60B) | |
212 | U054 | Waveform for Azimuth 54 | [120] | Frame(60B) | |
213 | U055 | Waveform for Azimuth 55 | [120] | Frame(60B) | |
214 | U056 | Waveform for Azimuth 56 | [120] | Frame(60B) | |
215 | U057 | Waveform for Azimuth 57 | [120] | Frame(60B) | |
216 | U058 | Waveform for Azimuth 58 | [120] | Frame(60B) | |
217 | U059 | Waveform for Azimuth 59 | [120] | Frame(60B) | |
218 | U060 | Waveform for Azimuth 60 | [120] | Frame(60B) | |
219 | U061 | Waveform for Azimuth 61 | [120] | Frame(60B) | |
220 | U062 | Waveform for Azimuth 62 | [120] | Frame(60B) | |
221 | U063 | Waveform for Azimuth 63 | [120] | Frame(60B) | |
222 | U064 | Waveform for Azimuth 64 | [120] | Frame(60B) | |
223 | U065 | Waveform for Azimuth 65 | [120] | Frame(60B) | |
224 | U066 | Waveform for Azimuth 66 | [120] | Frame(60B) | |
225 | U067 | Waveform for Azimuth 67 | [120] | Frame(60B) | |
226 | U068 | Waveform for Azimuth 68 | [120] | Frame(60B) | |
227 | U069 | Waveform for Azimuth 69 | [120] | Frame(60B) | |
228 | U070 | Waveform for Azimuth 70 | [120] | Frame(60B) | |
229 | U071 | Waveform for Azimuth 71 | [120] | Frame(60B) | |
230 | U072 | Waveform for Azimuth 72 | [120] | Frame(60B) | |
231 | UCAZ | Ultrasonic Azimuth | deg | [1] | Frame(60B) |
232 | UDEP | USIT River Depth Index | m | [1] | Frame(60B) |
233 | UFLG | USIT Processing Flags | [72] | Frame(60B) | |
234 | UFRT | Firing Rate | Hz | [1] | Frame(60B) |
235 | UGCI_RF | USIT Gaseous Index Computed on Unflagged Data | [1] | Frame(60B) | |
236 | UPGA | USIC Programmable Gain Amplitude of Waves | dB | [72] | Frame(60B) |
237 | USBI | USIT Bond Index | [1] | Frame(60B) | |
238 | USBI_RF | USIT Bond Index Computed on Unflagged Data | [1] | Frame(60B) | |
239 | USGI | USIT Gas Index | [1] | Frame(60B) | |
240 | USGI_RF | USIT Gas Index Computed on Unflagged Data | [1] | Frame(60B) | |
241 | UTDL | USIC Tachometer Delay | us | [1] | Frame(60B) |
242 | UTIM | Time of Arrival of Waves | ms | [72] | Frame(60B) |
300 | VDL | Variable Density Log | [500] | Frame(20B) | |
269 | VSEC | Vertical Section | m | [1] | Frame(60B) |
243 | WAGN | Waveform Applied Gain | dB | [72] | Frame(60B) |
301 | WDE1 | Waveform Delay 1 | us | [1] | Frame(20B) |
302 | WDE2 | Waveform Delay 2 | us | [1] | Frame(20B) |
244 | WDMN | Waveform Delay Minimum | us | [1] | Frame(60B) |
245 | WDMX | Waveform Delay Maximum | us | [1] | Frame(60B) |
304 | WF1 | Waveform 1 | [250] | Frame(20B) | |
303 | WF1N | Waveform 1 Normalization Factor | [1] | Frame(20B) | |
306 | WF2 | Waveform 2 | [250] | Frame(20B) | |
305 | WF2N | Waveform 2 Normalization Factor | [1] | Frame(20B) | |
246 | WFDL | Waveform Delay | us | [72] | Frame(60B) |
247 | WPKA | Waveform Peak Amplitude | [72] | Frame(60B) |
There are clearly a lot of parameters and channels available in our DLIS file. However, we will soon see how we can find shorter, more managable lists of parameters and channels belonging to a particular tool that we are interested in.
DLIS files contain information about which tools were present on the toolstring during the logging run(s) stored in the file. dlisio exposes this information as Tool objects.
Similarly to before, we use f.tools
to get a sequence of tools and use summarize()
to compile a table out of it:
tool_table = summarize(f.tools, name='Name', generic_name='Generic name',
trademark_name='Trademark name', description='Description')
tool_table.sort_values('Name')
Name | Generic name | Trademark name | Description | |
---|---|---|---|---|
4 | ACTS-B | ACTS | Auxiliary Compression Tension Sub - B (Only ex... | |
5 | CALY | CCL | CAL-Y | Casing Anomaly Locator 3-3/8 in 31 Pin Heads |
1 | DSLT-H | SONIC | DSLT-H | Digitizing Sonic Logging Tool - H |
3 | DTC-H | DTC-H | Digital Telemetry Cartridge - Version H | |
6 | LEHQT | Logging Equipment Head - QT, 3-3/8 inch 31 pin... | ||
2 | SGTN | GR | SGT-N | Scintillation Gamma-Ray Tool |
0 | USIT | USIT-E | USIT-E | Ultrasonic Imaging |
The sonic tool's name is "DSLT", which is short for "Digitizing Sonic Logging Tool". Section 15-4.4 of the book Well Cementing from 2006, edited by Erik B. Nelson and Domonique Guillot and published by Schlumberger, gives a good overview of the workings of such tools.
In short, an acoustic transmitter on the tool emits an acoustic pulse that travels along the wellbore. The acoustic pulse reaches two acoustic receivers on the tool, whose distances to the transmitter are 3 ft and 5 ft. The waveforms recorded by the receivers are then analysed to draw out information about the well integrity.
We will get back to the waveforms and what can be drawn from them, but let us first get an overview of the data we have from the sonic tool. Let us look at the overall description of this tool and its data:
dslt = f.object('TOOL', 'DSLT-H')
dslt.describe()
---- Tool ---- name : DSLT-H origin : 57 copy : 0 Description : Digitizing Sonic Logging Tool - H Trademark name : DSLT-H Generic name : SONIC Channels : BI TT1 TTSL WF2 VDL WF1 TT2 CBL WDE1 WF2N CBLF WDE2 CBSL WF1N CMCG TT TT2S Parameters : MODE DSIN WMOD SGDT SPSO ZCMT SFAF SGW DDEL CBRA ENABLED SDTH CMCF FCF SGAD DTFS MAX_TOOL_SPEED NMXG MSA MAHTR DTCM RATE ITTS DWCO AMSG SGAI GOBO CBLG MNHTR MCI BILI SGCL SGCW NMSG MATT VDLG SUTH NUMP DETE SLEV FATT Parts : SLS-E ECH-KH DSLC-H
Now, let us look at the sonic tool parameters in more detail:
dslt_param_table = summarize(dslt.parameters, name='Name', long_name='Long name', values='Value(s)')
dslt_param_table.sort_values('Name')
Name | Long name | Value(s) | |
---|---|---|---|
24 | AMSG | Auxiliary Minimum Sliding Gate | [180.0] |
30 | BILI | Bond Index Level for Zone Isolation | [0.800000011920929] |
27 | CBLG | CBL Gate Width | [45.0] |
9 | CBRA | CBL LQC Reference Amplitude in Free Pipe | [53.0] |
12 | CMCF | CBL Cement Type Compensation Factor | [0.678943395614624] |
8 | DDEL | Digitizing Delay | [0.0] |
38 | DETE | Delta-T Detection | [E1] |
1 | DSIN | Digitizer Sample Interval | [10.0] |
20 | DTCM | Delta-T Computation Mode | [FULL] |
15 | DTFS | DSLT Telemetry Frame Size | [536] |
23 | DWCO | Digitizer Word Count | [250] |
10 | ENABLED | Equipment or Computation Acquisition Status | [Yes] |
40 | FATT | Acoustic Attenuation due to Fluid | [0.0] |
13 | FCF | CBL Fluid Compensation Factor | [1.0] |
26 | GOBO | Good Bond | [6.259347438812256] |
22 | ITTS | Integrated Transit Time Source | [DT] |
19 | MAHTR | Manual High Threshold Reference for first arri... | [120] |
34 | MATT | Maximum Attenuation | [23.392528533935547] |
16 | MAX_TOOL_SPEED | Maximum service speed allowed for, or attained... | [1371.5999755859375] |
29 | MCI | Minimum Cemented Interval for Isolation | [4.515231132507324] |
28 | MNHTR | Minimum High Threshold Reference for first arr... | [100] |
0 | MODE | Sonic Firing Mode (e.g. DDBHC = Depth-Derived ... | [CBL] |
18 | MSA | Minimum Sonic Amplitude | [3.669377088546753] |
33 | NMSG | Near Minimum Sliding Gate | [344] |
17 | NMXG | Near Maximum Sliding Gate | [1026] |
37 | NUMP | Number of Detection Passes | [2] |
21 | RATE | Firing Rate | [R15] |
11 | SDTH | Switch Down Threshold | [20000] |
6 | SFAF | Sonic Formation Attenuation Factor | [10.662729263305664] |
14 | SGAD | Sliding Gate Status | [OFF] |
25 | SGAI | Selectable Acquisition Gain | [1X] |
31 | SGCL | Sliding Gate Closing Delta-T | [130] |
32 | SGCW | Sliding Gate Closing Width | [25] |
3 | SGDT | Sliding Gate Delta-T | [57] |
7 | SGW | Sliding Gate Width | [110] |
39 | SLEV | Signal Level for AGC | [5000.0] |
4 | SPSO | Sonic Porosity Source | [DT] |
36 | SUTH | Switch Up Threshold | [1000] |
35 | VDLG | VDL Manual Gain | [5.0] |
2 | WMOD | Waveform Firing Mode | [FULL] |
5 | ZCMT | Acoustic Impedance of Cement | [5.599999904632568] |
Finally, let us look at the sonic tool channels in more detail:
dslt_channel_table = summarize(dslt.channels, name='Name', long_name='Long name', units='Units',
dimension='Dimension', frame='Frame')
dslt_channel_table.sort_values('Name')
Name | Long name | Units | Dimension | Frame | |
---|---|---|---|---|---|
0 | BI | Bond Index | [1] | Frame(60B) | |
7 | CBL | CBL Amplitude | mV | [1] | Frame(60B) |
10 | CBLF | CBL Amplitude (Fluid Compensated) | mV | [1] | Frame(60B) |
12 | CBSL | CBL Amplitude (Sliding Gate) | mV | [1] | Frame(60B) |
14 | CMCG | CBL Cement Type Compensation Gain | [1] | Frame(60B) | |
15 | TT | Transit Time for CBL | us | [1] | Frame(60B) |
1 | TT1 | Transit Time 1 | us | [1] | Frame(20B) |
6 | TT2 | Transit Time 2 | us | [1] | Frame(20B) |
16 | TT2S | Transit Time 2 Secondary | us | [1] | Frame(20B) |
2 | TTSL | Transit Time (Sliding Gate) | us | [1] | Frame(60B) |
4 | VDL | Variable Density Log | [500] | Frame(20B) | |
8 | WDE1 | Waveform Delay 1 | us | [1] | Frame(20B) |
11 | WDE2 | Waveform Delay 2 | us | [1] | Frame(20B) |
5 | WF1 | Waveform 1 | [250] | Frame(20B) | |
13 | WF1N | Waveform 1 Normalization Factor | [1] | Frame(20B) | |
3 | WF2 | Waveform 2 | [250] | Frame(20B) | |
9 | WF2N | Waveform 2 Normalization Factor | [1] | Frame(20B) |
We can see here that we have two channels WF1
and WF2
, called "Waveform 1" and "Waveform 2" respectively. Each channel has a number of samples per depth that corresponds to the DWCO
(‘Digitizer Word Count’) parameter. We may expect that these channels contain recorded waveforms from the two receivers, but it's hard to say which channel corresponds to which receiver. Let's investigate further.
Both channels are a part of the Frame 20B
. This is convenient, as it means that they share the same index. Let us create some convenience functions to get the index and a named channel from a given frame, and use them to get the depth index, WF1
, and WF2
as Channel objects:
def index_of(frame):
"""Return the index channel of the frame"""
return next(ch for ch in frame.channels if ch.name == frame.index)
def get_channel(frame, name):
"""Get a channel with a given name from a given frame; fail if the frame does not have exactly one such channel"""
[channel] = [x for x in frame.channels if x.name == name]
return channel
# Get 20B and its index channel
frame20B = f.object('FRAME', '20B')
index20B = index_of(frame20B)
# Get the metadata related to WF1 and WF2
wf1 = get_channel(frame20B, 'WF1')
wf2 = get_channel(frame20B, 'WF2')
We can extract all the curves in the Frame as a numpy structured array curves20B
, whose field names are the names of the channels in the frame. This lets us look up the curves of any channel in the frame as e.g. curves20B['WF1']
.
curves20B = frame20B.curves()
# Convert the index to metres if needed
if index20B.units == '0.1 in':
curves20B[frame20B.index] *= 0.00254
As a simple example, we can look up the shapes of the curves of the frame's index channel, WF1
, and WF2
:
print('Shape of depth index curve array:', curves20B[frame20B.index].shape)
print('Shape of WF1 curve array: ', curves20B['WF1'].shape)
print('Shape of WF2 curve array: ', curves20B['WF2'].shape)
Shape of depth index curve array: (13746,) Shape of WF1 curve array: (13746, 250) Shape of WF2 curve array: (13746, 250)
The waveform channels' curves contain two-dimensional arrays, e.g. WF1
$(i,k)$, where $i$ indexes the depths and $k$ indexes the waveform samples. The actual depth $z_i$ can be found from the curve array of the index channel (in this case called TDEP
) as
Next, we create a helper function for plotting two-dimensional arrays as images and use it to plot WF1
and WF2
:
def paint_channel(ax, curve, y_axis, x_axis, **kwargs):
"""Plot an image channel into an axes using an index channel for the y-axis
Parameters
----------
ax : matplotlib.axes
curve : numpy array
The curve to be plotted
index : numpy array
The depth index as a Channel object (slower) or a numpy array (faster)
**kwargs : dict
Keyword arguments to be passed on to ax.imshow()
"""
# Determine the extent of the image so that the pixel centres correspond with the correct axis values
dx = np.mean(x_axis[1:] - x_axis[:-1])
dy = np.mean(y_axis[1:] - y_axis[:-1])
extent = (x_axis[0] - dx/2, x_axis[-1] + dx/2, y_axis[0] - dy/2, y_axis[-1] + dy/2)
# Determine the correct orientation of the image
if y_axis[1] < y_axis[0]: # Frame recorded from the bottom to the top of the well
origin = 'lower'
else: # Frame recorded from the top to the bottom of the well
origin = 'upper'
return ax.imshow(curve, aspect='auto', origin=origin, extent=extent, **kwargs)
# Determine the maximum absolute value of WF1 and WF2 so that we can balance the colormap around 0
wf_max = max(np.max(np.abs(curves20B['WF1'])), np.max(np.abs(curves20B['WF2'])))
wf_lim = 0.5 * wf_max
# Parameters for plotting the waveforms
wf_pltargs = {
'cmap': 'seismic',
'vmin': -wf_lim,
'vmax': wf_lim,
}
wf_samples = np.arange(wf1.dimension[0]) # x values to use in plotting
# Create figure and axes
fig, axes = plt.subplots(ncols=2, sharey=True, figsize=(9, 12), constrained_layout=True)
# Plot WF1 as an image
ax = axes[0]
im = paint_channel(ax, curves20B['WF1'], curves20B[frame20B.index], wf_samples, **wf_pltargs)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{wf1.name}: {wf1.long_name}')
ax.set_ylabel('Depth $z$ [m]')
# Plot WF2 as an image
ax = axes[1]
im = paint_channel(ax, curves20B['WF2'], curves20B[frame20B.index], wf_samples, **wf_pltargs)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{wf2.name}: {wf2.long_name}')
for ax in axes:
ax.set_xlabel('Sample $k$')
ax.grid(True)
This shows us the WF1
and WF2
waveforms recorded by the two receivers. These two channels do not have any units, so it is not yet clear how to convert them to physical units such as millivolts, except that it should involve some scaling factor.
We do not yet know which of these waveforms corresponds to the near and far receiver. Comparing them, we can make a few observations:
WF2
than in WF1
.WF2
than in WF1
.WF1
than in WF2
, indicating that WF1
has been measured further away.These observations all tell us that WF1
represents the far transducer at 5 ft, while WF2
represents the near transducer at 3 ft.
While this shows us how to find the waveforms at every depth, we don't yet know what sampling rate they were measured at. Thus, we have no time axis for the waveforms. To solve this, let's look at time-related channels and parameters.
According to section Section 15-4.4.2.5 of Well Cementing, the transit time represents the elapsed time between the transmitter firing a pulse and the first arrival of the pulse in the receiver. Among the sonic tool's channels, we have two transit time channels TT1
and TT2
in frame 20B
, which we can assume correspond to WF1
and WF2
respectively.
# Get the metadata related to TT1 and TT2
tt1 = get_channel(frame20B, 'TT1')
tt2 = get_channel(frame20B, 'TT2')
Let's assume that both waveforms start at $t=0$, when the transmitter fires the pulse. (This may also be what the DDEL
("Digitizing Delay") parameter tells us with its value of 0.) The DSIN
("Digitizer Sample Interval") parameter may be telling us that the sampling period is 10 µs, corresponding to a sampling rate of 100 kHz. To check this, we can assume these values to be true and see if we get a good fit between the transit times and the first arrivals in the waveforms.
wf_Ts = 10e-6 # Assumed waveform sampling period in seconds, i.e. time between samples
wf_Fs = 1/wf_Ts # Assumed waveform sampling rate in Hertz, i.e. frequency of sampling
wf_t = wf_Ts * np.arange(wf1.dimension[0]) # Assumed time axis for the waveforms
To check whether our assumed sampling rate is correct, we can plot the travel time over the waveforms.
# Create figure and axes
fig, axes = plt.subplots(ncols=2, sharey=True, figsize=(9, 12))
# Plot WF1 as an image
ax = axes[0]
paint_channel(ax, curves20B['WF1'], curves20B[frame20B.index], wf_t*1e6, **wf_pltargs)
ax.plot(curves20B['TT1'], curves20B[frame20B.index], color='k', alpha=0.75, linewidth=2)
ax.set_ylabel('Depth $z$ [m]')
ax.set_title(f'{wf1.name} with {tt1.long_name}')
# Plot WF2 as an image
ax = axes[1]
paint_channel(ax, curves20B['WF2'], curves20B[frame20B.index], wf_t*1e6, **wf_pltargs)
ax.plot(curves20B['TT2'], curves20B[frame20B.index], color='k', alpha=0.75, linewidth=2)
ax.set_title(f'{wf2.name} with {tt2.long_name}')
for ax in axes:
ax.set_xlabel('Time $t$ [µs]')
ax.grid(True)
fig.set_tight_layout(True)
A waveform sampling rate of 100 kHz thus gives us an excellent correspondence between the travel time channels and the first arrivals in the waveforms. We have also validated our assumption that the receivers start recording at $t=0$, i.e. the pulse emission time.
We can also compare individual waveforms from WF1
and WF2
at the same depth:
# Choose the depth index at which to plot the waveforms
i_wf = 12300
# Determine where to put the arrival times
tt2_t = curves20B['TT2'][i_wf]
tt2_y = np.interp(tt2_t, wf_t*1e6, curves20B['WF2'][i_wf, :])
tt1_t = curves20B['TT1'][i_wf]
tt1_y = np.interp(tt1_t, wf_t*1e6, curves20B['WF1'][i_wf, :])
shift = 1e4 # Vertical shift for both waveforms
fig, ax = plt.subplots(figsize=(9, 4))
[wf2_line] = ax.plot(wf_t*1e6, curves20B['WF2'][i_wf, :]+1e4, label='WF2')
ax.plot(tt2_t, tt2_y+shift, 'o', color=wf2_line.get_color(), alpha=0.5, label='WF2 arrival')
[wf1_line] = ax.plot(wf_t*1e6, curves20B['WF1'][i_wf, :]-1e4, label='WF1')
ax.plot(tt1_t, tt1_y-shift, 'o', color=wf1_line.get_color(), alpha=0.5, label='WF1 arrival')
ax.set_xlabel('Time $t$ [µs]')
ax.set_ylabel('Waveform value [arbitrary units]')
ax.set_title(f'Waveforms at depth {curves20B[frame20B.index][i_wf]:.2f} m')
ax.legend()
fig.set_tight_layout(True)
As mentioned, the waveform channels are stored without units, and there seems to be no parameter or channel available to convert the waveform values to any physical unit such as Pa or mV. While the WF1N
("Waveform 1 Normalization Factor") and WF2N
("Waveform 2 Normalization Factor") channels seem like promising candidates for this, they contain nothing but the value 1 at every depth.
We can also investigate the frequency content of these signals by performing the one-sided discrete Fourier transform and finding the spectral magnitude:
# Create a von Hann time window
n_samp = wf1.dimension[0]
hann_window = np.hanning(n_samp)
# Determine the spectral magnitude of the windowed signals
wf1_rfft = np.fft.rfft(hann_window*curves20B['WF1'][i_wf, :])
wf1_specmag = 20*np.log10(np.abs(wf1_rfft))
wf2_rfft = np.fft.rfft(hann_window*curves20B['WF2'][i_wf, :])
wf2_specmag = 20*np.log10(np.abs(wf2_rfft))
wf_f = np.fft.rfftfreq(n_samp, 10e-6)
wf_specmag_max = max(np.max(wf1_specmag), np.max(wf2_specmag))
fig, ax = plt.subplots(figsize=(9, 4))
ax.plot(wf_f/1e3, wf2_specmag, label='WF2')
ax.plot(wf_f/1e3, wf1_specmag, label='WF1')
ax.set_ylim(wf_specmag_max - 78, wf_specmag_max + 2)
ax.set_xlim(0, 50)
ax.set_xlabel('Frequency $f$ [kHz]')
ax.set_ylabel('Spectral magnitude [dB]')
ax.set_title(f'Waveform spectral magnitude at depth {curves20B[frame20B.index][i_wf]:.2f} m')
ax.legend()
ax.grid(True)
fig.set_tight_layout(True)
Based on these spectra, it seems that the recorded waveforms have most of their frequency content at roughly 6–22 kHz. The emitted pulses may have more content at higher frequencies, as the waves will typically be more attenuated at higher frequencies on their way from the transmitter to the receivers.
Waveform summary: WF1
and WF2
store acoustic waveforms recorded by the receivers at 5 ft and 3 ft, respectively. These waveforms are stored at sampling rates of 100 kHz. There seems to be no straightforward way to convert the waveforms to any physical unit. TT1
and TT2
represent the arrival times of the first-arriving waveform components in WF1
and WF2
, respectively.
From the literature (e.g. the Well Cementing book), we know that a waveform display against depth, as plotted above, is often called a variable density log, or VDL. In fact, there is also a VDL
channel in the files. This VDL
channel has twice as many samples in time as the WF1
and WF2
channels. While the VDL
channel is in the same frame as the WF1
and WF2
channels in this file, this is not true for every acoustic log file in Volve Data Village dataset. In other words, it may be sampled differently in both time and depth.
Let's investigate further, first by simply plotting the VDL
channel as an image.
vdl = get_channel(frame20B, 'VDL')
# Determine the maximum absolute value of VDL so that we can balance the colormap around 0
vdl_max = np.max(np.abs(curves20B['VDL']))
vdl_lim = 1 * vdl_max
# Parameters for matplotlib.imshow
vdl_pltargs = {
'cmap': 'seismic',
'vmin': -vdl_lim,
'vmax': vdl_lim,
}
vdl_samples = np.arange(vdl.dimension[0])
# Plot VDL
fig, ax = plt.subplots(figsize=(5,12), constrained_layout=True)
im = paint_channel(ax, curves20B['VDL'], curves20B[frame20B.index], vdl_samples, **vdl_pltargs)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{vdl.name}: {vdl.long_name}')
ax.set_ylabel('Depth $z$ [m]')
ax.set_xlabel('Sample $k$')
ax.grid(True)
From the image, the VDL
channel seems to be very similar to the WF1
channel from the 5 ft receiver, albeit with twice the sampling rate (i.e. half the sampling period) and a different contrast. Let's investigate further by plotting a VDL
waveform on top of a WF1
waveform:
# Calculate the VDL time axis
vdl_Ts = wf_Ts/2
vdl_t = vdl_Ts * np.arange(vdl.dimension[0])
# Plot WF1 and VDL
fig, ax = plt.subplots(figsize=(9, 4))
ax.plot(wf_t*1e6, curves20B['WF1'][i_wf, :], label='WF1')
ax.plot(vdl_t*1e6, curves20B['VDL'][i_wf, :], label='VDL')
ax.set_title(f'Comparing waveforms from the WF1 and VDL channels at depth {curves20B[frame20B.index][i_wf]:.2f} m')
ax.set_xlabel('Time $t$ [µs]')
ax.set_ylabel('Waveform value [arbitrary units]')
ax.legend()
fig.set_tight_layout(True)
It looks like VDL
is simply a scaled version of WF1
. Let's find a median scaling factor and compare again:
vdl_scaling = np.nanmedian(curves20B['VDL'][:, ::2] / curves20B['WF1'])
print('VDL/WF1 vdl_scaling factor:', vdl_scaling)
# Plot the comparison
fig, ax = plt.subplots(figsize=(9, 4))
ax.plot(wf_t*1e6, curves20B['WF1'][i_wf, :], label='WF1')
ax.plot(vdl_t*1e6, curves20B['VDL'][i_wf, :], label='VDL')
ax.plot(wf_t*1e6, vdl_scaling*curves20B['WF1'][i_wf, :], ':', color='0.4', label='WF1 (scaled)')
ax.set_title(f'Comparing waveforms from the WF1 and VDL channels at depth {curves20B[frame20B.index][i_wf]:.2f} m')
ax.set_xlabel('Time $t$ [µs]')
ax.set_ylabel('Waveform value [arbitrary units]')
ax.legend()
fig.set_tight_layout(True)
/var/folders/0k/6fvjhp7x2659gf4_pbq0zkj40000gp/T/ipykernel_37145/4213171325.py:1: RuntimeWarning: invalid value encountered in true_divide vdl_scaling = np.nanmedian(curves20B['VDL'][:, ::2] / curves20B['WF1'])
VDL/WF1 vdl_scaling factor: 4.55906976744186
We see that the scaled WF1
and VDL
waveforms overlap, except for the strongest peaks and troughs where VDL
is clipped and the scaled WF1
sticks out. Thus, we have confirmed that VDL
is a scaled version of WF1
, with twice the sampling rate. When VDL
is scaled up enough, some of its sample values reach the maximum and minimum value that can be stored using the data type that the VDL
channel is saved as in the DLIS file. As dlisio uses numpy datatypes equivalent to those used in the original DLIS file, we can simply interrogate the datatype to show this:
print(f'VDL value range: [{np.min(curves20B["VDL"])}, {np.max(curves20B["VDL"])}]')
print('Numpy datatype for the VDL channel:', curves20B['VDL'].dtype)
type_info = np.iinfo(curves20B['VDL'].dtype)
print(f'Range of {curves20B["VDL"].dtype}: [{type_info.min}, {type_info.max}]')
VDL value range: [-32767, 32767] Numpy datatype for the VDL channel: int16 Range of int16: [-32768, 32767]
In other words, this scaling can cause the VDL
channel data to be clipped, meaning that we are losing information about the strongest peaks and troughs. On the other hand, this clipping means that there is less difference between the strongest values in the VDL
channel and the rest. This is the reason that the VDL
looks like it has higher contrast than WF1
when both are plotted as images.
VDL summary: VDL
contains a scaled version of the same waveforms as the WF1
channel, but at twice the sampling rate (200 kHz) and sometimes with a different sampling in depth. It is not clear why these sampling differences exist, but there are two options: Either, the waveforms are originally recorded at a higher sampling rate and WF1
and WF2
are downsampled, or VDL
is upsampled from WF1
.
As the literature (e.g. the Well Cementing book) explains, the first-arriving component of the waveforms represents the fastest travel path from source to receiver. Typically, this path is where the emitted pulse travels through the casing fluid to the casing, where it excites a wave travelling along the casing, which in turn emits a wavefront back into the casing fluid that impinges on the receivers.
As the casing wave travels, it is attenuated due to energy loss into the casing fluid and the material outside the casing. With solids such as cement outside the casing, this attenuation is strong, which means that the first-arriving component will be much weaker than in cases with liquids such as water or mud outside the casing. Thus, the amplitude of the first-arriving component gives us an idea of the material outside the casing: High amplitudes correspond to fluids, and low amplitudes correspond to solids. (Middling amplitudes may correspond to solids partially covering the outside of the casing.)
This amplitude is quantified as the cement bond log (CBL), which is the amplitude $E_1$ in mV of the earliest detectable peak in the waveform signal recorded at the 3 ft receiver. In our files, there is a CBL
channel with units of mV, which we can compare against the WF2
channel:
# The CBL channel is in frame 60B
frame60B = f.object('FRAME', '60B')
index60B = index_of(frame60B)
curves60B = frame60B.curves()
# Convert the index to metres if needed
if index60B.units == '0.1 in':
curves60B[index60B.name] *= 0.00254
# Get CBL channel metadata
cbl = get_channel(frame60B, 'CBL')
fig, axes = plt.subplots(ncols=2, figsize=(9, 12), sharey=True)
# Plot CBL
ax = axes[0]
ax.plot(curves60B['CBL'], curves60B[frame60B.index], color='k', linewidth=1)
ax.set_xlim(0, 60)
ax.set_xlabel('CBL [mV]')
ax.set_ylabel('Depth $z$ [m]')
ax.set_title(f'{cbl.name}: {cbl.long_name}')
ax.grid(True)
# Plot WF2 around the waveform arrival
ax = axes[1]
paint_channel(ax, curves20B['WF2'], curves20B[frame20B.index], wf_t*1e6,
vmin=-0.5*wf_max, vmax=0.5*wf_max, cmap='seismic')
ax.set_xlabel('Time $t$ [µs]')
ax.set_title(f'{wf2.name}: {wf2.long_name}')
ax.grid(True)
ax.set_xlim(300, 600)
ax.axvline(365, linestyle=':', color='k')
fig.set_tight_layout(True)
We thus see that the value of the CBL
channel follows the amplitude of the first-arriving component in the WF2
channel, roughly where the dotted line is:
WF2
is strong, CBL
is high.WF2
is faint, CBL
is low.The CBL
channel also shows some sharp spikes around every 12th metre. This is due to the joints between casing sections, and we can see corresponding disturbances in the WF2
channel.
CBL is typically roughly interpreted by thresholding. Very high values (depending on casing size and normalisation, but typically 50–60 mV) indicate free pipe, i.e. only fluids on the outside of the casing. CBL values below around 10 mV indicate solids fully covering the casing.
Because there does not seem to be any parameter that lets us convert the waveforms to units of mV, we cannot find the CBL directly from the waveforms. However, we can find the values of the $E_1$ peak in arbitrary units for every waveform to show that its overall shape matches the CBL curve. To do this, we:
WF2
waveform using a Fourier method (which should be nearly exact, as we have seen that the signal is band-limited). One drawback of Fourier upsampling is that it assumes the signal to be periodic and can cause ringing if it is not, and we therefore enforce periodicity by applying a Tukey window to force the waveform to zero at its beginning and end.To be able to directly compare the $E_1$ values with the values in the CBL
channel, we need to find a conversion factor $a$. Here, we simply find $a$ from a linear fit, with no intercept, between $E_1$ and CBL
:
def waveform_E1(wfs, k_window):
"""Returns the first peak amplitudes E1 from an array of waveforms, searching only through samples in k_window"""
n_wf = wfs.shape[0]
wf_E1s = np.zeros(n_wf)
for i in range(n_wf):
k_max = k_window[0] + np.argmax(wfs[i][k_window])
fit_ks = k_max + np.array([-1,0,1])
fit = np.polyfit(fit_ks, wfs[i, fit_ks], 2)
k_max = - fit[1] / (2 * fit[0])
wf_E1s[i] = np.polyval(fit, k_max)
return wf_E1s
# Determine E1 from the upsampled windowed waveforms
window = signal.tukey(250, 20/250)
upsampled_wfs, upsampled_wfs_t = signal.resample(window*curves20B['WF2'], 2500, wf_t, axis=1)
k_window = 365 + np.arange(-20, 21)
wf2_E1 = waveform_E1(upsampled_wfs, k_window)
# Interpolate waveform peaks to the CBL frame depths
wf2_E1 = np.interp(curves60B[frame60B.index], curves20B[frame20B.index][::-1], wf2_E1[::-1])
# Find a linear scaling between wf2_El and CBL as an intercept-free linear fit
mask = np.logical_and(curves60B['CBL'] > 0, wf2_E1 > 0) # Omit missing data and invalid data
fit = np.linalg.lstsq(wf2_E1[mask][:,np.newaxis], curves60B['CBL'][mask], rcond=None)
a = fit[0][0]
# Plot comparison
fig, ax = plt.subplots(figsize=(5, 12))
ax.plot(wf2_E1*a, curves60B[frame60B.index], linewidth=0.5, label='$a E_1$')
ax.plot(curves60B['CBL'], curves60B[frame60B.index], 'k', linewidth=0.5, label='Reference CBL')
ax.set_xlim(0, 60)
ax.set_ylim(np.max(curves60B[frame60B.index]), np.min(curves60B[frame60B.index]))
ax.legend()
ax.set_xlabel('CBL [mV]')
ax.set_ylabel('Depth $z$ [m]');
fig.set_tight_layout(True)
Ignoring the spikes due to casing joints, we see that the $a E_1$ have the same shape as the reference CBL
channel, but deviates for particularly large or small CBL values. We can check the match further by looking at how the scaling matches the joint distribution of $E_1$ and CBL
values, and how the absolute difference $|aE_1 - \mathrm{CBL}|$ is distributed:
fig, axes = plt.subplots(ncols=2, figsize=(9,4))
cmap = copy.copy(mpl.cm.get_cmap('viridis'))
cmap.set_bad(cmap.colors[0])
# Plot a 2D histogram of E_1 values against the values from the CBL channel, and show the scaling
ax = axes[0]
plot_max = (max(wf2_E1), max(curves60B['CBL']))
hist = ax.hist2d(wf2_E1, curves60B['CBL'], norm=mpl.colors.LogNorm(), cmap=cmap,
bins=(np.linspace(0, plot_max[0], 100), np.linspace(0, plot_max[1], 100)))
ax.plot((0, plot_max[0]), (0, a*plot_max[0]), '--', label='$a E_1$')
fig.colorbar(hist[3], ax=ax)
ax.set_xlim(0, plot_max[0])
ax.set_ylim(0, plot_max[1])
ax.set_title('2D histogram against scaling')
ax.set_xlabel('$E_1$ from WF2 [arbitrary units]')
ax.set_ylabel('CBL [mV]')
ax.legend()
# Plot a histogram showing the match
ax = axes[1]
x = np.abs(wf2_E1[mask]*a - curves60B['CBL'][mask])
ax.hist(x, bins=np.arange(0,5.25,0.25), label='$a E_1$')
ax.axvline(np.mean(x), color='k', label='Mean')
ax.legend()
ax.set_title('Fit closeness to reference')
ax.set_xlabel('Absolute difference to reference [mV]')
ax.set_ylabel('Bin frequency')
fig.set_tight_layout(True)
The 2D histogram shows a slight S-shape in the relation between $E_1$ and CBL
, so that a conversion factor is not sufficient to connect the two. This implies that there is some kind of correction applied on the way from the waveform to the CBL
channel. Among the sonic tool's channels, we find CMCG
('CBL Cement Type Compensation Gain'). Let's see what that looks like:
fig, ax = plt.subplots(figsize=(5,9))
ax.plot(curves60B['CMCG'], curves60B[frame60B.index])
ax.set_xlim(0.6, 1.02)
ax.set_ylim(np.max(curves60B[frame60B.index]), np.min(curves60B[frame60B.index]))
ax.set_ylabel('Depth $z$ [m]')
ax.set_xlabel('CMCG')
fig.set_tight_layout(True)
Let's try using the CMCG
channel to compensate our $E_1$ values before determining a conversion factor $a'$.
fit = np.linalg.lstsq((curves60B['CMCG']*wf2_E1)[mask][:,np.newaxis], curves60B['CBL'][mask], rcond=None)
aprime = fit[0][0]
fig, ax = plt.subplots(figsize=(5,10))
ax.plot(curves60B['CBL'], curves60B['TDEP'], 'k', linewidth=0.5, label='Reference CBL')
ax.plot(wf2_E1*a, curves60B['TDEP'], linewidth=0.5, alpha=0.75, label='$a E_1$')
ax.plot(curves60B['CMCG']*wf2_E1*aprime, curves60B['TDEP'], linewidth=0.5, alpha=0.75, label="$a' (\mathrm{CMCG} \cdot E_1)$")
ax.set_ylabel('Depth $z$ [m]')
ax.set_xlabel('CBL [mV]')
ax.set_xlim(0, 60)
ax.set_ylim(np.max(curves60B[frame60B.index]), np.min(curves60B[frame60B.index]))
ax.legend()
fig.set_tight_layout(True)
This gives us a much better fit. Let's have a closer look through the same kind of histograms as we used earlier:
fig, axes = plt.subplots(ncols=2, figsize=(9,4))
# Plot a 2D histogram of corrected E_1 values against the values from the CBL channel, and show the scaling
ax = axes[0]
plot_max = (max(wf2_E1), max(curves60B['CBL']))
hist = ax.hist2d(curves60B['CMCG']*wf2_E1, curves60B['CBL'], norm=mpl.colors.LogNorm(), cmap=cmap,
bins=(np.linspace(0, plot_max[0], 100), np.linspace(0, plot_max[1], 100)))
ax.plot((0, plot_max[0]), (0, aprime*plot_max[0]), '--', color='C1', label="$a'(\mathrm{CMCG} \cdot E_1)$")
fig.colorbar(hist[3], ax=ax)
ax.set_xlim(0, plot_max[0])
ax.set_ylim(0, plot_max[1])
ax.set_title('2D histogram against scaling')
ax.set_xlabel('$\mathrm{CMCG} \cdot E_1$ from WF2 [arbitrary units]')
ax.set_ylabel('CBL [mV]')
ax.legend()
# Plot a histogram showing the match
ax = axes[1]
x = [np.abs(wf2_E1[mask]*a - curves60B['CBL'][mask]),
np.abs((curves60B['CMCG']*wf2_E1*aprime)[mask] - curves60B['CBL'][mask])]
ax.hist(x, bins=np.arange(0,4.5,0.25), label=['$a E_1$', "$a' (\mathrm{CMCG} \cdot E_1)$"])
ax.axvline(np.mean(x[0]), color='C0', label='Mean')
ax.axvline(np.mean(x[1]), color='C1', label='Mean')
ax.legend()
ax.set_title('Fit closeness to reference')
ax.set_xlabel('Absolute difference to reference [mV]')
ax.set_ylabel('Bin frequency')
fig.set_tight_layout(True)
CBL summary: It is possible to determine CBL values from the first peak amplitudes $E_1$ from the WF2
waveforms, although there are some issues with this. The main issue is that the waveforms in the WF2
and WF1
channels are stored in arbitrary units instead of mV, and the files contain no obvious way to convert the units. Second, a straightforward implementation of this would not match the CBL
channel in the file that well, as the CBL
values have undergone some correction, related to the CMCG
channel, that we at this point do not know how to calculate from scratch.
While CBL tells us of the attenuation accumulated by the casing wave as it travels along the casing, we can also estimate this attenuation directly when we have two or more receivers. From the Well Cementing book, we find that attenuation can be estimated from $E_1$ in the near and far receivers as
$$\alpha = \frac{20}{L_\mathrm{RR}} \log_{10}\left( \frac{E_{1,\mathrm{near}}}{E_{1,\mathrm{far}}} \right) ,$$where $L_\mathrm{RR}$ is the distance between receivers (2 feet in our case). If only one receiver is availabel at a distance $L_\mathrm{TR}$ from the transducer, the attenuation can still be approximated as
$$\alpha' = \frac{20}{L_\mathrm{TR}} \log_{10}\left( \frac{E_{1,\mathrm{free}}}{E_1} \right) ,$$where $E_{1,\mathrm{free}}$ is the amplitude for free-pipe intervals in the same receiver as $E_1$ is measured. For the near receiver, a reference amplitude is given in the file in the CBRA
(‘CBL LQC Reference Amplitude in Free Pipe’) parameter.
A common way of representing these attenuations is through the bond index BI, which scales and shifts the attenuation to lie between 0 and 1 as
$$\mathrm{BI} = \frac{\alpha - \alpha_\mathrm{free}}{\alpha_\mathrm{full} - \alpha_\mathrm{free}} ,$$where $\alpha_\mathrm{free}$ is the attenuation in free-pipe intervals and $\alpha_\mathrm{full}$ is the attenuation in intervals where the casing is fully covered by bonded solids. Thus, $\mathrm{BI} = 0$ corresponds to no coverage and $\mathrm{BI} = 1$ corresponds to full coverage. The idea is that $\mathrm{BI}$ should correspond to the proportion of casing covered by bonded solids.
If only one casing is available, we can calculate an approximate bond index as
$$\mathrm{BI}' = \frac{\alpha' - \alpha_\mathrm{free}}{\alpha_\mathrm{full} - \alpha_\mathrm{free}} .$$Additionally, we can calculate the alternative bond percentage index as
$$\mathrm{BPI} = \frac{E_{1,\mathrm{free}} - E_1}{E_{1,\mathrm{free}} - E_{1,\mathrm{full}}} ,$$where $E_{1,\mathrm{full}}$ is the amplitude for full-coverage intervals in the same receiver as $E_1$ is measured. We find this from the parameter MSA
(‘Minimum Sonic Amplitude’).
Before we do this, we must calculate $E_1$ for the far receiver, i.e. from the waveforms in the WF1
channel. We do this in the same way as from WF2
:
# Calculate the first peak values E_1 from the WF1 waveforms
upsampled_wfs, upsampled_wfs_t = signal.resample(window*curves20B['WF1'], 2500, wf_t, axis=1)
k_window = np.arange(465, 489)
wf1_E1 = waveform_E1(upsampled_wfs, k_window)
# Interpolate waveform peaks to the CBL frame depths
wf1_E1 = np.interp(curves60B[frame60B.index], curves20B[frame20B.index][::-1], wf1_E1[::-1])
Now we can calculate the attenuations and bond indices, and compare them against each other and the file's channel BI
(‘Bond Index’) by plotting:
# Calculate corrected E1 values for the near and far receivers
E1_near = aprime*curves60B['CMCG']*wf2_E1
E1_far = aprime*curves60B['CMCG']*wf1_E1
# Calculate near receiver reference values
E1_free = f.object('PARAMETER', 'CBRA').values[0] # Free pipe
E1_full = f.object('PARAMETER', 'MSA').values[0] # Full solid coverage
# Calculate distances between transducers
L_RR = (5-3)*0.3048 # Near and far receivers
L_TR = 3*0.3048 # Transmitter and near receiver
# Calculate full attenuation and approximate attenuation based only on the near receiver
attenuation = 20/L_RR * np.log10(E1_near/E1_far)
attenuation_approx = 20/L_TR * np.log10(E1_free/E1_near)
# Calculate reference attenuation values for free pipe and full coverage of solids
attenuation_free = 0
attenuation_full = 20/L_TR * np.log10(E1_free/E1_full)
# Calculate bond index, approximate bond index based on the near receiver, and bond percentage index
BI = (attenuation - attenuation_free) / (attenuation_full - attenuation_free)
BI_approx = (attenuation_approx - attenuation_free) / (attenuation_full - attenuation_free)
BPI = (E1_free - E1_near) / (E1_free - E1_full)
fig, axes = plt.subplots(figsize=(9,10), ncols=3, sharey=True)
# Plot E1
ax = axes[0]
ax.plot(E1_near, curves60B[frame60B.index], linewidth=0.5, label='$E_{1,\mathrm{near}}$')
ax.plot(E1_far, curves60B[frame60B.index], linewidth=0.5, label='$E_{1,\mathrm{far}}$')
ax.set_ylabel('Depth $z$ [m]')
ax.set_xlabel('First peak amplitudes [mV]')
ax.legend(loc='upper left')
ax.set_xlim(0, 60)
# Plot attenuation
ax = axes[1]
ax.plot(attenuation, curves60B[frame60B.index], linewidth=0.5, label='$\\alpha$')
ax.plot(attenuation_approx, curves60B[frame60B.index], linewidth=0.5, label="$\\alpha'$")
ax.set_xlabel('Attenuation [dB/m]')
ax.set_xlim(-1, 26)
ax.legend()
# Plot bond indices
ax = axes[2]
ax.plot(curves60B['BI'], curves60B[frame60B.index], 'k', linewidth=1, label='Reference BI')
ax.plot(BI, curves60B[frame60B.index], linewidth=0.5, label='BI')
ax.plot(BI_approx, curves60B[frame60B.index], linewidth=0.5, label="BI'")
ax.plot(BPI, curves60B[frame60B.index], linewidth=0.5, label='BPI')
ax.set_xlabel('Bond indices')
ax.set_xlim(-0.1, 1.1)
ax.legend()
# Set the y limits from the bottom to the top of the logged interval
ax.set_ylim(max(curves60B[frame60B.index]), min(curves60B[frame60B.index]))
for ax in axes:
ax.grid(True)
fig.set_tight_layout(True)
/var/folders/0k/6fvjhp7x2659gf4_pbq0zkj40000gp/T/ipykernel_37145/2125932976.py:14: RuntimeWarning: invalid value encountered in log10 attenuation = 20/L_RR * np.log10(E1_near/E1_far) /var/folders/0k/6fvjhp7x2659gf4_pbq0zkj40000gp/T/ipykernel_37145/2125932976.py:15: RuntimeWarning: invalid value encountered in log10 attenuation_approx = 20/L_TR * np.log10(E1_free/E1_near)
The approximate attenuation $\alpha'$ is higher than the attenuation $\alpha$ calculated from both receivers. This is reflected in the approximate bond index $\mathrm{BI}'$ being higher than the bond index $\mathrm{BI}$ calculated from both receivers. The reference from the BI
channel matches $\mathrm{BI}'$ well, with the minor deviations originating from us calculating $\mathrm{BI'}$ based on our own $E_1$ processing instead of from the CBL
channel. $\mathrm{BPI}$ predicts a higher coverage than the other bond metrics.
Attenuation summary: From the waveforms in the WF1
and WF2
channels, we can compute various attenuation and bond metrics that are not provided in the file.
The ultrasonic tool's name is "USIT", which is short for "Ultrasonic Imaging Tool". It was first described in a 1991 article by Hayman et al., and some further details are described in Section 15-4.5 of the aforementioned Well Cementing book.
The basic mechanism of the USIT (and similar pulse-echo tools) is that an ultrasonic transducer emits a pulse onto the casing at normal incidence. A small part of the pulse is transmitted into the casing, where it resonates. The reflected pulse is then recorded by the transducer. The recorded pulse consists of the strong first reflection from the fluid-casing interface, known as the first-interface echo (FIE), followed by a decaying resonance. Based on a single pulse, the USIT can estimate the distance from the transducer to the casing, the thickness $d$ of the casing, and the acoustic impedance $Z = \rho c$ of the material on the far side of the casing, where $\rho$ and $c$ are the material's density and pressure wave speed, respectively. Furthermore, if the transducer position is known, the transducer-casing distance allows calculating the inner radius $r_\mathrm{i}$ of the casing, which can be combined with the thickness $d$ to give the outer radius $r_\mathrm{o} = r_\mathrm{i} + d$.
The USIT head rotates very quickly as the tool moves through the well, so that the transducer can emit its pulses at different azimuthal angles. This means that unlike the sonic tool, which can only sample the well in depth, the USIT can sample the well in both depth and azimuthal angle. The USIT's angular resolution can be set to either 5° (360°/5° = 72 measurements per depth) or 10° (360°/10° = 36 measurements per depth).
The processing algorithm giving the casing thickness and the acoustic impedance behind the casing is called Traitement Très Tôt ("very early processing" in French), or simply T3. This algorithm is described in the two sources mentioned above. Although the data in the file would let us recreate this processing, we will not go into that in this notebook.
First, let us look at what data the USIT tool gives us:
usit = f.object('TOOL', 'USIT')
usit.describe()
---- Tool ---- name : USIT origin : 57 copy : 0 Description : Ultrasonic Imaging Trademark name : USIT-E Generic name : USIT-E Channels : AWAV_RF MDR U044 U016 U023 U036 U008 AIMN_RF RB U022 AZEC_EXT IDQC RSAV AWAV U020 U024 U051 IRAV_EXT T2BK IRMN_EXT AWMN_RF U063 U027 U009 U014 U035 WPKA AZEC U056 THBK AI_MICRO_DEBONDING_IMAGE GASR IRBK CZMD IRMN ERMN USGI U042 U011 U001 U055 U043 UPGA U050 ECCE ECCEEXT_RF TTBK U066 UCAZ AIBK CCLU WFDL U064 ERMN_EXT AGMI AIMX CFVL U006 U058 U002 U018 BPRE U045 U046 U049 U015 U019 U040 U033 USBI U068 WDMN U038 ERMX_EXT U069 U029 U025 U053 U021 IRAV U071 U007 IRNO ERBN_EXT USGI_RF ERMX U067 U004 AGMA AIMN AIMX_RF U030 UDEP U012 WDMX U013 ECCE_EXT U010 AIAV_RF UTDL U017 T1BK U032 U065 ERNO IRMX AWMX ERBA_EXT T2AV U037 ECCE_RF U041 AIAV UTIM U052 U026 THNO U005 UGCI_RF WAGN AWMX_RF U057 U054 U072 IRMX_EXT USBI_RF U028 ERAV_EXT ERAV U031 U047 U034 AWBK AWMN ERBK_EXT U039 U003 CEMR U048 U061 U062 AZEC_RF ERBK U060 IRBK_EXT U070 U059 AWBK_RF ERAV_RF ERMN_RF ERMX_RF GNMN GNMX HRTT IRAV_RF IRBK_RF IRMN_RF IRMX_RF MLOSS RB_USIT THAV THAV_RF THBK_RF THMN THMN_RF THMX THMX_RF TTAV TTMN TTMX U-ED1 U-ED10 U-ED11 U-ED12 U-ED13 U-ED14 U-ED15 U-ED16 U-ED17 U-ED18 U-ED19 U-ED2 U-ED20 U-ED21 U-ED22 U-ED23 U-ED24 U-ED25 U-ED26 U-ED27 U-ED28 U-ED29 U-ED3 U-ED30 U-ED31 U-ED32 U-ED33 U-ED34 U-ED35 U-ED36 U-ED4 U-ED5 U-ED6 U-ED7 U-ED8 U-ED9 UFLG UFRT U-ID1 U-ID10 U-ID11 U-ID12 U-ID13 U-ID14 U-ID15 U-ID16 U-ID17 U-ID18 U-ID19 U-ID2 U-ID20 U-ID21 U-ID22 U-ID23 U-ID24 U-ID25 U-ID26 U-ID27 U-ID28 U-ID29 U-ID3 U-ID30 U-ID31 U-ID32 U-ID33 U-ID34 U-ID35 U-ID36 U-ID4 U-ID5 U-ID6 U-ID7 U-ID8 U-ID9 U-USIT_AZECEXT_RF Parameters : C_MPL RCTH WLEN C_DLEN ENABLED ENABLED VCAS BERJ ENABLED MAX_TOOL_SPEED RCOD C_TPC RCSO NPPW THDL ENABLED TCUB CMTY NWPD VRES C_WIND ENABLED ZTGS ENABLED UMFR SDNV ENABLED HRES EMXV C_NUP ZTCM WINE OPLEV ZCAS C_MFILT THDP AFVU THDH C_BLANK USFR WINB C_ALGO DOT ZINI C_JNO TVD AGMN AGMX SDTHOR SDTVER SUBT ULOG UMAO UPAT USTO USUB U-USIT_DDT5 UWKM Parts : USRS-C USI-SENSOR USIS-A USSC-B ECH-MFA USAC-A
Now, let's look at the USIT parameters:
usit_param_table = summarize(usit.parameters, name='Name', long_name='Long name', values='Value(s)')
usit_param_table.sort_values('Name')
Name | Long name | Value(s) | |
---|---|---|---|
36 | AFVU | Automatic Fluid Velocity Update | [Off] |
46 | AGMN | Minimum Gain of Cartridge | [-12] |
47 | AGMX | Maximum Gain of Cartridge | [40] |
7 | BERJ | Bad Echo Rejection | [ON] |
17 | CMTY | Cement Type | [Regular Cement] |
41 | C_ALGO | Collar detection algorithm | [CCLU] |
38 | C_BLANK | Ignore on each side of each collar, inches | [12] |
3 | C_DLEN | Collar detection length, inches | [24] |
44 | C_JNO | Joint number offset | [0] |
34 | C_MFILT | Apply 3-point median filter to statistics? | [No] |
0 | C_MPL | Minimum pipe length, inches | [24] |
29 | C_NUP | Number joints from bottom? | [Yes] |
11 | C_TPC | Collar detection threshold percentage | [50] |
20 | C_WIND | Collar detection window length, inches | [600] |
42 | DOT | Diameter of Transducer Sensor | [4.874000072479248] |
28 | EMXV | EMEX Voltage | [90] |
8 | ENABLED | Equipment or Computation Acquisition Status | [Yes] |
21 | ENABLED | Equipment or Computation Acquisition Status | [No] |
5 | ENABLED | Equipment or Computation Acquisition Status | [No] |
23 | ENABLED | Equipment or Computation Acquisition Status | [No] |
4 | ENABLED | Equipment or Computation Acquisition Status | [No] |
15 | ENABLED | Equipment or Computation Acquisition Status | [No] |
26 | ENABLED | Equipment or Computation Acquisition Status | [No] |
27 | HRES | Horizontal Resolution | [5 deg] |
9 | MAX_TOOL_SPEED | Maximum service speed allowed for, or attained... | [2057.39990234375] |
13 | NPPW | Number of Points Per Waveform | [120] |
18 | NWPD | Number of Waveforms per Depth | [72] |
32 | OPLEV | USIT Remove Flagged Data Level | [OPT2] |
10 | RCOD | Reference Calibrator Outer Diameter | [7.0] |
12 | RCSO | Reference Calibrator Standoff | [1.3779499530792236] |
1 | RCTH | Reference Calibrator Thickness | [0.295199990272522] |
25 | SDNV | Number of Vertical Samples used for Micro-debo... | [5] |
48 | SDTHOR | Acoustic Impedance STD Horizontal Threshold fo... | [0.5] |
49 | SDTVER | Acoustic Impedance STD Vertical Threshold for ... | [0.30000001192092896] |
50 | SUBT | USIT Sub type | [10INC] |
16 | TCUB | T^3 Processing Level | [VXLP] |
37 | THDH | Maximum Search Thickness (percentage of nominal) | [130.0] |
14 | THDL | Minimum Search Thickness (percentage of nominal) | [70.0] |
35 | THDP | Thickness Detection Policy | [Fundamental] |
45 | TVD | True Vertical Depth | [0.0] |
56 | U-USIT_DDT5 | USIC Downhole Decimation for T5 only | [0_NONE] |
51 | ULOG | Logging Objective | [MEASURE] |
52 | UMAO | USIT Measurement Angular Offset | [18.0] |
24 | UMFR | Modulation Frequency | [333333.0] |
53 | UPAT | Emission Pattern | [300K] |
39 | USFR | Ultrasonic Sampling Frequency | [500000.0] |
54 | USTO | USIT Time Offset | [-2.0] |
55 | USUB | USIT Sub Identifier | [10INC] |
57 | UWKM | Working Mode | [D606005L] |
6 | VCAS | Ultrasonic Transversal Velocity in Casing | [51.400001525878906] |
19 | VRES | Vertical Resolution | [6.0INCH] |
40 | WINB | Window Begin Time | [53.79532241821289] |
31 | WINE | Window End Time | [120.3934326171875] |
2 | WLEN | T^3 Processing Length | [32.32666778564453] |
33 | ZCAS | Acoustic Impedance of Casing | [46.25] |
43 | ZINI | Initial Estimate of Cement Impedance | [-1.0] |
30 | ZTCM | Acoustic Impedance Threshold for Cement | [2.5999999046325684] |
22 | ZTGS | Acoustic Impedance Threshold for Gas | [0.30000001192092896] |
Finally, here are the USIT channels:
usit_channel_table = summarize(usit.channels, name='Name', long_name='Long name', units='Units',
dimension='Dimension', frame='Frame')
usit_channel_table.sort_values('Name')
Name | Long name | Units | Dimension | Frame | |
---|---|---|---|---|---|
88 | AGMA | Maximum Allowed Electronic Gain | dB | [1] | Frame(60B) |
54 | AGMI | Minimum Allowed Electronic Gain | dB | [1] | Frame(60B) |
112 | AIAV | Acoustic Impedance Average | Mrayl | [1] | Frame(60B) |
98 | AIAV_RF | Median of Unflagged Measured Impedance | Mrayl | [1] | Frame(60B) |
49 | AIBK | Acoustic Impedance | Mrayl | [72] | Frame(60B) |
89 | AIMN | Acoustic Impedance Minimum | Mrayl | [1] | Frame(60B) |
7 | AIMN_RF | Minimum of Unflagged Acoustic Impedance | Mrayl | [1] | Frame(60B) |
55 | AIMX | Acoustic Impedance Maximum | Mrayl | [1] | Frame(60B) |
90 | AIMX_RF | Maximum of Unflagged Acoustic Impedance | Mrayl | [1] | Frame(60B) |
30 | AI_MICRO_DEBONDING_IMAGE | Acoustic Impedance With Micro-debonding Image | Mrayl | [72] | Frame(60B) |
13 | AWAV | Amplitude of Wave Average | dB | [1] | Frame(60B) |
0 | AWAV_RF | Average of Unflagged Wave Amplitude | dB | [1] | Frame(60B) |
132 | AWBK | Amplitude of Wave | dB | [72] | Frame(60B) |
147 | AWBK_RF | Unflagged Echo Amplitude minus median amplitude | dB | [72] | Frame(60B) |
133 | AWMN | Amplitude of Wave Minimum | dB | [1] | Frame(60B) |
20 | AWMN_RF | Minimum of Unflagged Wave Amplitude | dB | [1] | Frame(60B) |
106 | AWMX | Amplitude of Wave Maximum | dB | [1] | Frame(60B) |
120 | AWMX_RF | Maximum of Unflagged Wave Amplitude | dB | [1] | Frame(60B) |
27 | AZEC | Azimuth of Eccentering | deg | [1] | Frame(60B) |
10 | AZEC_EXT | Azimuth of External Eccentering | deg | [1] | Frame(60B) |
141 | AZEC_RF | Azimuth of Eccentering for Unflagged Waves | deg | [1] | Frame(60B) |
61 | BPRE | Burst Pressure | psi | [1] | Frame(60B) |
50 | CCLU | Casing Collar Locator Ultrasonic | in | [1] | Frame(60B) |
137 | CEMR | Ratio of Cement Measurements to Total | [1] | Frame(60B) | |
56 | CFVL | Memorized Fluid Acoustic Slowness | us/ft | [1] | Frame(60B) |
33 | CZMD | Acoustic Impedance of Mud | Mrayl | [1] | Frame(60B) |
44 | ECCE | Amplitude of Eccentering | in | [1] | Frame(60B) |
45 | ECCEEXT_RF | Unflagged Data External Eccentering | in | [1] | Frame(60B) |
96 | ECCE_EXT | USIT External Eccentering | in | [1] | Frame(60B) |
110 | ECCE_RF | Amplitude of Eccentering for Unflagged Waves | in | [1] | Frame(60B) |
128 | ERAV | External Radii Average | in | [1] | Frame(60B) |
127 | ERAV_EXT | Average External Radius Corrected for External... | in | [1] | Frame(60B) |
148 | ERAV_RF | Median of External Radii for Unflagged Waves | in | [1] | Frame(60B) |
107 | ERBA_EXT | External Radii Corrected for External Eccenter... | in | [72] | Frame(60B) |
142 | ERBK | External Radii | in | [72] | Frame(60B) |
134 | ERBK_EXT | External Radii Corrected for External Eccentering | in | [72] | Frame(60B) |
83 | ERBN_EXT | External Radii Corrected for External Eccenter... | in | [72] | Frame(60B) |
35 | ERMN | External Radii Minimum | in | [1] | Frame(60B) |
53 | ERMN_EXT | Minimum External Radius Corrected for External... | in | [1] | Frame(60B) |
149 | ERMN_RF | Minimum of Unflagged External Radii | in | [1] | Frame(60B) |
85 | ERMX | External Radii Maximum | in | [1] | Frame(60B) |
73 | ERMX_EXT | Maximum External Radius Corrected for External... | in | [1] | Frame(60B) |
150 | ERMX_RF | Maximum of Unflagged External Radii | in | [1] | Frame(60B) |
104 | ERNO | Nominal Casing External Radius | in | [1] | Frame(60B) |
31 | GASR | Ratio of Gas Measurements to Total | [1] | Frame(60B) | |
151 | GNMN | Waveform Gain Minimum | dB | [1] | Frame(60B) |
152 | GNMX | Waveform Gain Maximum | dB | [1] | Frame(60B) |
153 | HRTT | Histogram of Raw Transit Time Index | us | [180] | Frame(60B) |
11 | IDQC | Internal Diameter Quality Check | in | [1] | Frame(60B) |
79 | IRAV | Internal Radius Averaged Value | in | [1] | Frame(60B) |
17 | IRAV_EXT | Average Internal Radius Corrected for External... | in | [1] | Frame(60B) |
154 | IRAV_RF | Median Internal Radius of Casing for Unflagged... | in | [1] | Frame(60B) |
32 | IRBK | Internal Radii Normalized | in | [72] | Frame(60B) |
144 | IRBK_EXT | Internal Radii Corrected for External Eccentering | in | [72] | Frame(60B) |
155 | IRBK_RF | Unflagged External Radii of Casing | in | [72] | Frame(60B) |
34 | IRMN | Internal Radius Minimum Value | in | [1] | Frame(60B) |
19 | IRMN_EXT | Minimum Internal Radius Corrected for External... | in | [1] | Frame(60B) |
156 | IRMN_RF | Minimum of Unflagged Internal Radii Corrected ... | in | [1] | Frame(60B) |
105 | IRMX | Internal Radius Maximum Value | in | [1] | Frame(60B) |
124 | IRMX_EXT | Maximum Internal Radius Corrected for External... | in | [1] | Frame(60B) |
157 | IRMX_RF | Maximum of Unflagged Internal Radii Corrected ... | in | [1] | Frame(60B) |
82 | IRNO | Nominal Casing Internal Radius | in | [1] | Frame(60B) |
1 | MDR | Micro-debonding Ratio | [1] | Frame(60B) | |
158 | MLOSS | Metal Loss | % | [1] | Frame(60B) |
8 | RB | Relative Bearing | deg | [1] | Frame(60B) |
159 | RB_USIT | USIT RB | deg | [1] | Frame(60B) |
12 | RSAV | Motor Revolution Speed | c/s | [1] | Frame(60B) |
101 | T1BK | Internal Radius Metal Loss | in | [72] | Frame(60B) |
108 | T2AV | Thickness Average of Casing minus Nominal | in | [1] | Frame(60B) |
18 | T2BK | Thickness of Casing minus Nominal | in | [72] | Frame(60B) |
160 | THAV | Thickness Average Value | in | [1] | Frame(60B) |
161 | THAV_RF | Average of Unflagged Casing Thickness | in | [1] | Frame(60B) |
29 | THBK | Casing Thickness Normalized | in | [72] | Frame(60B) |
162 | THBK_RF | Image of Unflagged Thickness of Casing | in | [72] | Frame(60B) |
163 | THMN | Thickness Minimum Value | in | [1] | Frame(60B) |
164 | THMN_RF | Minimum of Unflagged Casing Thickness | in | [1] | Frame(60B) |
165 | THMX | Thickness Maximum Value | in | [1] | Frame(60B) |
166 | THMX_RF | Maximum of Unflagged Casing Thickness | in | [1] | Frame(60B) |
116 | THNO | Nominal Casing Thickness | in | [1] | Frame(60B) |
167 | TTAV | Transit time average | us | [1] | Frame(60B) |
46 | TTBK | Transit Time | us | [72] | Frame(60B) |
168 | TTMN | Transit Time Minimum Value | us | [1] | Frame(60B) |
169 | TTMX | Transit Time Maximum Value | us | [1] | Frame(60B) |
170 | U-ED1 | External Diameter in USI mode | in | [1] | Frame(60B) |
171 | U-ED10 | External Diameter in USI mode | in | [1] | Frame(60B) |
172 | U-ED11 | External Diameter in USI mode | in | [1] | Frame(60B) |
173 | U-ED12 | External Diameter in USI mode | in | [1] | Frame(60B) |
174 | U-ED13 | External Diameter in USI mode | in | [1] | Frame(60B) |
175 | U-ED14 | External Diameter in USI mode | in | [1] | Frame(60B) |
176 | U-ED15 | External Diameter in USI mode | in | [1] | Frame(60B) |
177 | U-ED16 | External Diameter in USI mode | in | [1] | Frame(60B) |
178 | U-ED17 | External Diameter in USI mode | in | [1] | Frame(60B) |
179 | U-ED18 | External Diameter in USI mode | in | [1] | Frame(60B) |
180 | U-ED19 | External Diameter in USI mode | in | [1] | Frame(60B) |
181 | U-ED2 | External Diameter in USI mode | in | [1] | Frame(60B) |
182 | U-ED20 | External Diameter in USI mode | in | [1] | Frame(60B) |
183 | U-ED21 | External Diameter in USI mode | in | [1] | Frame(60B) |
184 | U-ED22 | External Diameter in USI mode | in | [1] | Frame(60B) |
185 | U-ED23 | External Diameter in USI mode | in | [1] | Frame(60B) |
186 | U-ED24 | External Diameter in USI mode | in | [1] | Frame(60B) |
187 | U-ED25 | External Diameter in USI mode | in | [1] | Frame(60B) |
188 | U-ED26 | External Diameter in USI mode | in | [1] | Frame(60B) |
189 | U-ED27 | External Diameter in USI mode | in | [1] | Frame(60B) |
190 | U-ED28 | External Diameter in USI mode | in | [1] | Frame(60B) |
191 | U-ED29 | External Diameter in USI mode | in | [1] | Frame(60B) |
192 | U-ED3 | External Diameter in USI mode | in | [1] | Frame(60B) |
193 | U-ED30 | External Diameter in USI mode | in | [1] | Frame(60B) |
194 | U-ED31 | External Diameter in USI mode | in | [1] | Frame(60B) |
195 | U-ED32 | External Diameter in USI mode | in | [1] | Frame(60B) |
196 | U-ED33 | External Diameter in USI mode | in | [1] | Frame(60B) |
197 | U-ED34 | External Diameter in USI mode | in | [1] | Frame(60B) |
198 | U-ED35 | External Diameter in USI mode | in | [1] | Frame(60B) |
199 | U-ED36 | External Diameter in USI mode | in | [1] | Frame(60B) |
200 | U-ED4 | External Diameter in USI mode | in | [1] | Frame(60B) |
201 | U-ED5 | External Diameter in USI mode | in | [1] | Frame(60B) |
202 | U-ED6 | External Diameter in USI mode | in | [1] | Frame(60B) |
203 | U-ED7 | External Diameter in USI mode | in | [1] | Frame(60B) |
204 | U-ED8 | External Diameter in USI mode | in | [1] | Frame(60B) |
205 | U-ED9 | External Diameter in USI mode | in | [1] | Frame(60B) |
208 | U-ID1 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
209 | U-ID10 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
210 | U-ID11 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
211 | U-ID12 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
212 | U-ID13 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
213 | U-ID14 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
214 | U-ID15 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
215 | U-ID16 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
216 | U-ID17 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
217 | U-ID18 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
218 | U-ID19 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
219 | U-ID2 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
220 | U-ID20 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
221 | U-ID21 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
222 | U-ID22 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
223 | U-ID23 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
224 | U-ID24 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
225 | U-ID25 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
226 | U-ID26 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
227 | U-ID27 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
228 | U-ID28 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
229 | U-ID29 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
230 | U-ID3 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
231 | U-ID30 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
232 | U-ID31 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
233 | U-ID32 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
234 | U-ID33 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
235 | U-ID34 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
236 | U-ID35 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
237 | U-ID36 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
238 | U-ID4 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
239 | U-ID5 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
240 | U-ID6 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
241 | U-ID7 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
242 | U-ID8 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
243 | U-ID9 | Internal Diameter in USI mode | in | [1] | Frame(60B) |
244 | U-USIT_AZECEXT_RF | Azimuth of External Eccentering for Unflagged ... | deg | [1] | Frame(60B) |
39 | U001 | Waveform for Azimuth 01 | [120] | Frame(60B) | |
59 | U002 | Waveform for Azimuth 02 | [120] | Frame(60B) | |
136 | U003 | Waveform for Azimuth 03 | [120] | Frame(60B) | |
87 | U004 | Waveform for Azimuth 04 | [120] | Frame(60B) | |
117 | U005 | Waveform for Azimuth 05 | [120] | Frame(60B) | |
57 | U006 | Waveform for Azimuth 06 | [120] | Frame(60B) | |
81 | U007 | Waveform for Azimuth 07 | [120] | Frame(60B) | |
6 | U008 | Waveform for Azimuth 08 | [120] | Frame(60B) | |
23 | U009 | Waveform for Azimuth 09 | [120] | Frame(60B) | |
97 | U010 | Waveform for Azimuth 10 | [120] | Frame(60B) | |
38 | U011 | Waveform for Azimuth 11 | [120] | Frame(60B) | |
93 | U012 | Waveform for Azimuth 12 | [120] | Frame(60B) | |
95 | U013 | Waveform for Azimuth 13 | [120] | Frame(60B) | |
24 | U014 | Waveform for Azimuth 14 | [120] | Frame(60B) | |
65 | U015 | Waveform for Azimuth 15 | [120] | Frame(60B) | |
3 | U016 | Waveform for Azimuth 16 | [120] | Frame(60B) | |
100 | U017 | Waveform for Azimuth 17 | [120] | Frame(60B) | |
60 | U018 | Waveform for Azimuth 18 | [120] | Frame(60B) | |
66 | U019 | Waveform for Azimuth 19 | [120] | Frame(60B) | |
14 | U020 | Waveform for Azimuth 20 | [120] | Frame(60B) | |
78 | U021 | Waveform for Azimuth 21 | [120] | Frame(60B) | |
9 | U022 | Waveform for Azimuth 22 | [120] | Frame(60B) | |
4 | U023 | Waveform for Azimuth 23 | [120] | Frame(60B) | |
15 | U024 | Waveform for Azimuth 24 | [120] | Frame(60B) | |
76 | U025 | Waveform for Azimuth 25 | [120] | Frame(60B) | |
115 | U026 | Waveform for Azimuth 26 | [120] | Frame(60B) | |
22 | U027 | Waveform for Azimuth 27 | [120] | Frame(60B) | |
126 | U028 | Waveform for Azimuth 28 | [120] | Frame(60B) | |
75 | U029 | Waveform for Azimuth 29 | [120] | Frame(60B) | |
91 | U030 | Waveform for Azimuth 30 | [120] | Frame(60B) | |
129 | U031 | Waveform for Azimuth 31 | [120] | Frame(60B) | |
102 | U032 | Waveform for Azimuth 32 | [120] | Frame(60B) | |
68 | U033 | Waveform for Azimuth 33 | [120] | Frame(60B) | |
131 | U034 | Waveform for Azimuth 34 | [120] | Frame(60B) | |
25 | U035 | Waveform for Azimuth 35 | [120] | Frame(60B) | |
5 | U036 | Waveform for Azimuth 36 | [120] | Frame(60B) | |
109 | U037 | Waveform for Azimuth 37 | [120] | Frame(60B) | |
72 | U038 | Waveform for Azimuth 38 | [120] | Frame(60B) | |
135 | U039 | Waveform for Azimuth 39 | [120] | Frame(60B) | |
67 | U040 | Waveform for Azimuth 40 | [120] | Frame(60B) | |
111 | U041 | Waveform for Azimuth 41 | [120] | Frame(60B) | |
37 | U042 | Waveform for Azimuth 42 | [120] | Frame(60B) | |
41 | U043 | Waveform for Azimuth 43 | [120] | Frame(60B) | |
2 | U044 | Waveform for Azimuth 44 | [120] | Frame(60B) | |
62 | U045 | Waveform for Azimuth 45 | [120] | Frame(60B) | |
63 | U046 | Waveform for Azimuth 46 | [120] | Frame(60B) | |
130 | U047 | Waveform for Azimuth 47 | [120] | Frame(60B) | |
138 | U048 | Waveform for Azimuth 48 | [120] | Frame(60B) | |
64 | U049 | Waveform for Azimuth 49 | [120] | Frame(60B) | |
43 | U050 | Waveform for Azimuth 50 | [120] | Frame(60B) | |
16 | U051 | Waveform for Azimuth 51 | [120] | Frame(60B) | |
114 | U052 | Waveform for Azimuth 52 | [120] | Frame(60B) | |
77 | U053 | Waveform for Azimuth 53 | [120] | Frame(60B) | |
122 | U054 | Waveform for Azimuth 54 | [120] | Frame(60B) | |
40 | U055 | Waveform for Azimuth 55 | [120] | Frame(60B) | |
28 | U056 | Waveform for Azimuth 56 | [120] | Frame(60B) | |
121 | U057 | Waveform for Azimuth 57 | [120] | Frame(60B) | |
58 | U058 | Waveform for Azimuth 58 | [120] | Frame(60B) | |
146 | U059 | Waveform for Azimuth 59 | [120] | Frame(60B) | |
143 | U060 | Waveform for Azimuth 60 | [120] | Frame(60B) | |
139 | U061 | Waveform for Azimuth 61 | [120] | Frame(60B) | |
140 | U062 | Waveform for Azimuth 62 | [120] | Frame(60B) | |
21 | U063 | Waveform for Azimuth 63 | [120] | Frame(60B) | |
52 | U064 | Waveform for Azimuth 64 | [120] | Frame(60B) | |
103 | U065 | Waveform for Azimuth 65 | [120] | Frame(60B) | |
47 | U066 | Waveform for Azimuth 66 | [120] | Frame(60B) | |
86 | U067 | Waveform for Azimuth 67 | [120] | Frame(60B) | |
70 | U068 | Waveform for Azimuth 68 | [120] | Frame(60B) | |
74 | U069 | Waveform for Azimuth 69 | [120] | Frame(60B) | |
145 | U070 | Waveform for Azimuth 70 | [120] | Frame(60B) | |
80 | U071 | Waveform for Azimuth 71 | [120] | Frame(60B) | |
123 | U072 | Waveform for Azimuth 72 | [120] | Frame(60B) | |
48 | UCAZ | Ultrasonic Azimuth | deg | [1] | Frame(60B) |
92 | UDEP | USIT River Depth Index | m | [1] | Frame(60B) |
206 | UFLG | USIT Processing Flags | [72] | Frame(60B) | |
207 | UFRT | Firing Rate | Hz | [1] | Frame(60B) |
118 | UGCI_RF | USIT Gaseous Index Computed on Unflagged Data | [1] | Frame(60B) | |
42 | UPGA | USIC Programmable Gain Amplitude of Waves | dB | [72] | Frame(60B) |
69 | USBI | USIT Bond Index | [1] | Frame(60B) | |
125 | USBI_RF | USIT Bond Index Computed on Unflagged Data | [1] | Frame(60B) | |
36 | USGI | USIT Gas Index | [1] | Frame(60B) | |
84 | USGI_RF | USIT Gas Index Computed on Unflagged Data | [1] | Frame(60B) | |
99 | UTDL | USIC Tachometer Delay | us | [1] | Frame(60B) |
113 | UTIM | Time of Arrival of Waves | ms | [72] | Frame(60B) |
119 | WAGN | Waveform Applied Gain | dB | [72] | Frame(60B) |
71 | WDMN | Waveform Delay Minimum | us | [1] | Frame(60B) |
94 | WDMX | Waveform Delay Maximum | us | [1] | Frame(60B) |
51 | WFDL | Waveform Delay | us | [72] | Frame(60B) |
26 | WPKA | Waveform Peak Amplitude | [72] | Frame(60B) |
From the much larger number of parameters and channels, it is clear that there is a lot more information available from the USIT than the DSLT.
To start making sense of this information, we can see that many of the USIT channels have a dimension corresponding to the value of the NWPD
(‘Number of Waveforms per Depth’) parameter. (NWPD
has values of 72 or 36 in the Volve integrity log files). These channels represent the results of processing the measured waveforms, with each of the 72 or 36 values per depth representing a measurement at a different azimuthal angle. Let's show all of them:
# Get the value of the NWPD parameter
nwpd = f.object('PARAMETER', 'NWPD').values[0]
# Show all azimuthal channels
usit_channel_table[usit_channel_table['Dimension'] == nwpd].sort_values('Name')
Name | Long name | Units | Dimension | Frame | |
---|---|---|---|---|---|
49 | AIBK | Acoustic Impedance | Mrayl | [72] | Frame(60B) |
30 | AI_MICRO_DEBONDING_IMAGE | Acoustic Impedance With Micro-debonding Image | Mrayl | [72] | Frame(60B) |
132 | AWBK | Amplitude of Wave | dB | [72] | Frame(60B) |
147 | AWBK_RF | Unflagged Echo Amplitude minus median amplitude | dB | [72] | Frame(60B) |
107 | ERBA_EXT | External Radii Corrected for External Eccenter... | in | [72] | Frame(60B) |
142 | ERBK | External Radii | in | [72] | Frame(60B) |
134 | ERBK_EXT | External Radii Corrected for External Eccentering | in | [72] | Frame(60B) |
83 | ERBN_EXT | External Radii Corrected for External Eccenter... | in | [72] | Frame(60B) |
32 | IRBK | Internal Radii Normalized | in | [72] | Frame(60B) |
144 | IRBK_EXT | Internal Radii Corrected for External Eccentering | in | [72] | Frame(60B) |
155 | IRBK_RF | Unflagged External Radii of Casing | in | [72] | Frame(60B) |
101 | T1BK | Internal Radius Metal Loss | in | [72] | Frame(60B) |
18 | T2BK | Thickness of Casing minus Nominal | in | [72] | Frame(60B) |
29 | THBK | Casing Thickness Normalized | in | [72] | Frame(60B) |
162 | THBK_RF | Image of Unflagged Thickness of Casing | in | [72] | Frame(60B) |
46 | TTBK | Transit Time | us | [72] | Frame(60B) |
206 | UFLG | USIT Processing Flags | [72] | Frame(60B) | |
42 | UPGA | USIC Programmable Gain Amplitude of Waves | dB | [72] | Frame(60B) |
113 | UTIM | Time of Arrival of Waves | ms | [72] | Frame(60B) |
119 | WAGN | Waveform Applied Gain | dB | [72] | Frame(60B) |
51 | WFDL | Waveform Delay | us | [72] | Frame(60B) |
26 | WPKA | Waveform Peak Amplitude | [72] | Frame(60B) |
There are a lot of channels here that sound similar (for example several channels to do with internal and external radii of the casing), but there are some we can pick out immediately based on what information we know should be present:
AIBK
(‘Acoustic Impedance’) should represent the estimated impedance of the material behind the casing.IRBK
(‘Internal Radii Normalized’) should represent the internal radius, with some kind of normalisation.T2BK
(‘Thickness of Casing minus Nominal’) should represent the difference between the actual casing thickness and the nominal casing thickness, which is stored in the parameter THNO
.ERBK
(‘External Radii’) should represent the external radius of the casing.Let's load these channels to investigate them further:
# All the Channels we want are in Frame(60B)
frame60B = f.object('FRAME', '60B')
index60B = index_of(frame60B)
curves60B = frame60B.curves()
# Convert the index to metres if needed
if index60B.units == '0.1 in':
curves60B[index60B.name] *= 0.00254
# Get the metadata of each channel
aibk = get_channel(frame60B, 'AIBK')
t2bk = get_channel(frame60B, 'T2BK')
erbk = get_channel(frame60B, 'ERBK')
irbk = get_channel(frame60B, 'IRBK')
Each array corresponding to the channel curves, e.g. $\mathtt{AIBK}(i,j)$, has two dimensions. The first dimension, which we represent by the index $i$, is depth. The second, which we represent by the index $j$, is the azimuthal angle. The actual azimuthal angle is given from the NWPD
parameter as $\varphi_j = j \Delta \varphi$, where $\Delta\varphi = 360/\mathrm{NWPD}$:
dang = 360//nwpd
ang = dang * np.arange(nwpd)
Now we can plot the data from these four channels:
fig, axes = plt.subplots(ncols=4, figsize=(9,12), sharey=True, constrained_layout=True)
ax = axes[0]
im = paint_channel(ax, curves60B['AIBK'], curves60B[frame60B.index], ang, cmap='YlOrBr', vmin=0, vmax=7.5)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'AIBK [{aibk.units}]')
ax.set_ylabel('Depth $z$ [m]')
ax = axes[1]
im = paint_channel(ax, curves60B['IRBK'], curves60B[frame60B.index], ang, cmap='seismic', vmin=-0.045, vmax=0.045)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'IRBK [{irbk.units}]')
ax = axes[2]
im = paint_channel(ax, curves60B['T2BK'], curves60B[frame60B.index], ang, cmap='seismic', vmin=-0.035, vmax=0.035)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'T2BK [{t2bk.units}]')
ax = axes[3]
im = paint_channel(ax, curves60B['ERBK'], curves60B[frame60B.index], ang, vmin=4.83, vmax=4.91)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'ERBK [{erbk.units}]')
for ax in axes:
ax.set_xticks([0, 90, 180, 270, 360])
ax.grid(True)
ax.set_xlabel('Azimuthal angle $\\varphi$ [°]')
The three latter channels provide information about the geometry of the casing. T2BK
, together with the nominal thickness parameter THNO
give us the casing thickness as
while the ERBK
channel directly gives us the casing outer radius as
However, the long name of the IRBK
channel, "Internal Radii Normalized", refers to some as of yet unknown normalisation. Using Schlumberger's Curve Mnemonic Dictionary to look up IRBK, we find that the channel has the property Differential_Radius, which means that it represents the difference between the actual radius of an object and its average radius. Looking at the list of channels for something giving an average radius, we can find the IRAV
("Internal Radius Averaged Value") curve channel. We may then guess that we can find the actual internal radius as
To check this guess, we can compare two methods of finding the internal radius and check if
$$r_\mathrm{i}(z_i,\varphi_j) = r_\mathrm{o}(z_i,\varphi_j) - d(z_i,\varphi_j) :$$# Casing thickness
thno = f.object('PARAMETER', 'THNO').values[0]
d = curves60B['T2BK'] + thno
# Outer casing radius
r_o = curves60B['ERBK']
# Inner casing radius?
r_i = curves60B['IRAV'][:, np.newaxis] + curves60B['IRBK'][:, :]
print('Maximum absolute difference between the two internal radius approaches:', np.max(np.abs(r_i - (r_o - d))))
print(f'Machine epsilon for {curves60B["IRBK"].dtype} values: {np.finfo(curves60B["IRBK"].dtype).eps}')
Maximum absolute difference between the two internal radius approaches: 4.7683716e-07 Machine epsilon for float32 values: 1.1920928955078125e-07
As the difference between the two methods is on the order of machine epsilon for the float32 variables used in the data channels, we have confirmed that both these methods agree on the internal radius, with the only differences caused by roundoff error.
We can use this geometry data to plot the casing geometry at a particular depth:
# Depth index to plot geometry for
i_geom = 4150
# Find closed curves for the inner and outer radii, as well as for the azimuthal angles in radians
r_i_closed = np.concatenate((r_i[i_geom, :], r_i[i_geom, 0][np.newaxis]))
r_o_closed = np.concatenate((r_o[i_geom, :], r_o[i_geom, 0][np.newaxis]))
ang_closed = np.concatenate((ang, ang[0][np.newaxis])) * 2*np.pi/360
fig, ax = plt.subplots(figsize=(5,5), subplot_kw=dict(polar=True), facecolor='white')
ax.fill_between(ang_closed, r_i_closed, r_o_closed, color='0.75', label='Casing')
ax.plot(ang_closed, r_i_closed, label='$r_\mathrm{i}$')
ax.plot(ang_closed, r_o_closed, label='$r_\mathrm{o}$')
ax.set_ylim(0, 5.5)
ax.legend()
ax.set_title(f'Casing radii [in] at depth {curves60B[frame60B.index][i_geom]:.2f} m');
Azimuthal channel summary: At every depth, a number of channels store one value per azimuthal angle measured by the ultrasonic tool. For example, the IRBK
, T2BK
, and ERBK
channels store information that, together with the IRAV
channel that only stores one value per depth, can be used to find the inner radius, thickness, and outer radius of the casing at every depth and angle.
The file also contains channels of USIT waveforms, named U001
, U002
, and so forth. The number of such channels equals the NWPD
("Number of Waveforms per Depth") parameter:
usit_channel_table[usit_channel_table['Name'].str.contains('U0')].sort_values('Name')
Name | Long name | Units | Dimension | Frame | |
---|---|---|---|---|---|
39 | U001 | Waveform for Azimuth 01 | [120] | Frame(60B) | |
59 | U002 | Waveform for Azimuth 02 | [120] | Frame(60B) | |
136 | U003 | Waveform for Azimuth 03 | [120] | Frame(60B) | |
87 | U004 | Waveform for Azimuth 04 | [120] | Frame(60B) | |
117 | U005 | Waveform for Azimuth 05 | [120] | Frame(60B) | |
57 | U006 | Waveform for Azimuth 06 | [120] | Frame(60B) | |
81 | U007 | Waveform for Azimuth 07 | [120] | Frame(60B) | |
6 | U008 | Waveform for Azimuth 08 | [120] | Frame(60B) | |
23 | U009 | Waveform for Azimuth 09 | [120] | Frame(60B) | |
97 | U010 | Waveform for Azimuth 10 | [120] | Frame(60B) | |
38 | U011 | Waveform for Azimuth 11 | [120] | Frame(60B) | |
93 | U012 | Waveform for Azimuth 12 | [120] | Frame(60B) | |
95 | U013 | Waveform for Azimuth 13 | [120] | Frame(60B) | |
24 | U014 | Waveform for Azimuth 14 | [120] | Frame(60B) | |
65 | U015 | Waveform for Azimuth 15 | [120] | Frame(60B) | |
3 | U016 | Waveform for Azimuth 16 | [120] | Frame(60B) | |
100 | U017 | Waveform for Azimuth 17 | [120] | Frame(60B) | |
60 | U018 | Waveform for Azimuth 18 | [120] | Frame(60B) | |
66 | U019 | Waveform for Azimuth 19 | [120] | Frame(60B) | |
14 | U020 | Waveform for Azimuth 20 | [120] | Frame(60B) | |
78 | U021 | Waveform for Azimuth 21 | [120] | Frame(60B) | |
9 | U022 | Waveform for Azimuth 22 | [120] | Frame(60B) | |
4 | U023 | Waveform for Azimuth 23 | [120] | Frame(60B) | |
15 | U024 | Waveform for Azimuth 24 | [120] | Frame(60B) | |
76 | U025 | Waveform for Azimuth 25 | [120] | Frame(60B) | |
115 | U026 | Waveform for Azimuth 26 | [120] | Frame(60B) | |
22 | U027 | Waveform for Azimuth 27 | [120] | Frame(60B) | |
126 | U028 | Waveform for Azimuth 28 | [120] | Frame(60B) | |
75 | U029 | Waveform for Azimuth 29 | [120] | Frame(60B) | |
91 | U030 | Waveform for Azimuth 30 | [120] | Frame(60B) | |
129 | U031 | Waveform for Azimuth 31 | [120] | Frame(60B) | |
102 | U032 | Waveform for Azimuth 32 | [120] | Frame(60B) | |
68 | U033 | Waveform for Azimuth 33 | [120] | Frame(60B) | |
131 | U034 | Waveform for Azimuth 34 | [120] | Frame(60B) | |
25 | U035 | Waveform for Azimuth 35 | [120] | Frame(60B) | |
5 | U036 | Waveform for Azimuth 36 | [120] | Frame(60B) | |
109 | U037 | Waveform for Azimuth 37 | [120] | Frame(60B) | |
72 | U038 | Waveform for Azimuth 38 | [120] | Frame(60B) | |
135 | U039 | Waveform for Azimuth 39 | [120] | Frame(60B) | |
67 | U040 | Waveform for Azimuth 40 | [120] | Frame(60B) | |
111 | U041 | Waveform for Azimuth 41 | [120] | Frame(60B) | |
37 | U042 | Waveform for Azimuth 42 | [120] | Frame(60B) | |
41 | U043 | Waveform for Azimuth 43 | [120] | Frame(60B) | |
2 | U044 | Waveform for Azimuth 44 | [120] | Frame(60B) | |
62 | U045 | Waveform for Azimuth 45 | [120] | Frame(60B) | |
63 | U046 | Waveform for Azimuth 46 | [120] | Frame(60B) | |
130 | U047 | Waveform for Azimuth 47 | [120] | Frame(60B) | |
138 | U048 | Waveform for Azimuth 48 | [120] | Frame(60B) | |
64 | U049 | Waveform for Azimuth 49 | [120] | Frame(60B) | |
43 | U050 | Waveform for Azimuth 50 | [120] | Frame(60B) | |
16 | U051 | Waveform for Azimuth 51 | [120] | Frame(60B) | |
114 | U052 | Waveform for Azimuth 52 | [120] | Frame(60B) | |
77 | U053 | Waveform for Azimuth 53 | [120] | Frame(60B) | |
122 | U054 | Waveform for Azimuth 54 | [120] | Frame(60B) | |
40 | U055 | Waveform for Azimuth 55 | [120] | Frame(60B) | |
28 | U056 | Waveform for Azimuth 56 | [120] | Frame(60B) | |
121 | U057 | Waveform for Azimuth 57 | [120] | Frame(60B) | |
58 | U058 | Waveform for Azimuth 58 | [120] | Frame(60B) | |
146 | U059 | Waveform for Azimuth 59 | [120] | Frame(60B) | |
143 | U060 | Waveform for Azimuth 60 | [120] | Frame(60B) | |
139 | U061 | Waveform for Azimuth 61 | [120] | Frame(60B) | |
140 | U062 | Waveform for Azimuth 62 | [120] | Frame(60B) | |
21 | U063 | Waveform for Azimuth 63 | [120] | Frame(60B) | |
52 | U064 | Waveform for Azimuth 64 | [120] | Frame(60B) | |
103 | U065 | Waveform for Azimuth 65 | [120] | Frame(60B) | |
47 | U066 | Waveform for Azimuth 66 | [120] | Frame(60B) | |
86 | U067 | Waveform for Azimuth 67 | [120] | Frame(60B) | |
70 | U068 | Waveform for Azimuth 68 | [120] | Frame(60B) | |
74 | U069 | Waveform for Azimuth 69 | [120] | Frame(60B) | |
145 | U070 | Waveform for Azimuth 70 | [120] | Frame(60B) | |
80 | U071 | Waveform for Azimuth 71 | [120] | Frame(60B) | |
123 | U072 | Waveform for Azimuth 72 | [120] | Frame(60B) |
Each of these contains 120 values per depth, which also corresponds with the parameter NPPW
("Number of Points Per Waveform"). We can expect that these 120 values represent samples of a time signal, just like the sonic waveforms. Let's plot one of the ultrasonic waveform channels to have a look:
u001 = get_channel(frame60B, 'U001')
u001_max = np.max(np.abs(curves60B['U001']))
u001_lim = 0.5*u001_max
us_pltargs = {
'cmap': 'seismic',
'vmin': -u001_lim,
'vmax': u001_lim
}
us_samples = np.arange(u001.dimension[0])
fig, ax = plt.subplots(figsize=(5, 12), constrained_layout=True)
im = paint_channel(ax, curves60B['U001'], curves60B[frame60B.index], us_samples, **us_pltargs)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{u001.name}: {u001.long_name}')
ax.set_ylabel('Depth $z$ [m]')
ax.set_xlabel('Sample $k$');
This channel shows ultrasonic pulse-echo waveforms similar to those shown in the literature. The strong pulse at the beginning is the reflection from the interface between the casing fluid and the casing, also known as the first-interface echo (FIE). After the FIE, we find a resonance corresponding to the pulse bouncing back and forth inside the casing. From the literature, we know that:
The U001
channel gives us the waveforms at every depth for one single azimuth. To have all the waveforms at every azimuth, we want to load all of the waveform channels and assemble them into one large three-dimensional numpy array us_data(i,j,k)
, where the first dimension corresponds to depth, the second to azimuth, and the third to sample number.
# Create a numpy array to hold every waveform
nppw = f.object('PARAMETER', 'NPPW').values[0]
us_data = np.zeros((len(curves60B[frame60B.index]), nwpd, nppw))
def azim_to_name(j):
"""Return the name of the waveform channel corresponding to azimuth index j"""
return f'U{j + 1:03}'
# Read all the waveform data into us_data
for j in range(nwpd):
us_data[:, j, :] = curves60B[azim_to_name(j)]
As a test, let's plot the waveform data from every azimuth at the same depth:
i_us = 4150
fig, ax = plt.subplots(figsize=(5,5))
im = ax.imshow(us_data[i_us, :, :], aspect='auto', **us_pltargs)
cbar = fig.colorbar(im, ax=ax)
ax.set_ylabel('Angular index $j$')
ax.set_xlabel('Sample $k$')
ax.set_title(f'USIT waveforms at depth {curves60B[frame60B.index][i_us]:.2f} m')
fig.set_tight_layout(True)
We can see that some of the waveforms are weaker overall than others, which may indicate that the waveforms are scaled differently before stored. Looking through the azimuthal image channels for a channel that might specify the scaling applied to each waveform, we find two likely candidates:
UPGA
("USIC Programmable Gain Amplitude of Waves") andWAGN
("Waveform Applied Gain") channels.Both have units of dB. Let's see what they look like:
upga = get_channel(frame60B, 'UPGA')
wagn = get_channel(frame60B, 'WAGN')
fig, axes = plt.subplots(ncols=2, figsize=(9, 12), sharey=True, constrained_layout=True)
ax = axes[0]
im = paint_channel(ax, curves60B['UPGA'], curves60B[frame60B.index], ang)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{upga.name}: {upga.long_name} [{upga.units}]')
ax.set_ylabel('Depth $z$ [m]')
ax = axes[1]
im = paint_channel(ax, curves60B['WAGN'], curves60B[frame60B.index], ang)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{wagn.name}: {wagn.long_name} [{wagn.units}]')
for ax in axes:
ax.set_xlabel('Azimuthal angle $\\varphi$ [°]')
ax.set_xticks(np.linspace(0, 360, 9))
These two look pretty much identical, and show a band of lower gain through the middle, similar to the band of weaker waveforms in the middle of the waveform-against-azimuth image above. We may be able to use these to reequalise the amplitudes of the waveforms.
But first, let's check how similar the two channels are by looking at their maximum absolute difference:
gain_diff = curves60B['UPGA'] - curves60B['WAGN']
print('Maximum absolute difference between UPGA and WAGN:', np.max(np.abs(gain_diff)), upga.units)
Maximum absolute difference between UPGA and WAGN: 0.0 dB
So, the two channels are, in fact, identical in this file. Let's use WAGN
in the following, as "Waveform Applied Gain" sounds exactly like what we are looking for. To reequalise the waveforms' amplitudes, we need to convert this decibel gain into a scaling factor $10^{-\mathtt{WAGN}/20}$ that we multiply every waveform with:
us_waveform_scaling = 10**(-curves60B['WAGN']/20)
us_data_reeq = us_data.copy() * us_waveform_scaling[:, :, np.newaxis]
Finally, let's verify that this has reequalised the waveforms by replotting the same waveform-against-angle image as above.
us_max = np.max(np.abs(us_data_reeq))
us_lim = 0.5*us_max
us_reeq_pltargs = {
'cmap': 'seismic',
'vmin': -us_lim,
'vmax': us_lim
}
fig, ax = plt.subplots(figsize=(5,5))
im = ax.imshow(us_data_reeq[i_us, :, :], aspect='auto', **us_reeq_pltargs)
cbar = fig.colorbar(im, ax=ax)
ax.set_ylabel('Azimuthal index $j$')
ax.set_xlabel('Sample $k$')
ax.set_title(f'USIT waveforms at depth {curves60B[frame60B.index][i_us]:.2f} m')
fig.set_tight_layout(True)
Success! After correcting the waveform amplitudes with WAGN
, the waveforms' amplitudes do not have the same sudden transitions any longer.
While there is no difference between the WAGN
and UPGA
channels in this file, or indeed most of the other integrity logs in the Volve dataset, the logs from well F-12 do show such a difference. In these logs, the WAGN
channels give a better correction than the UPGA
channels do.
Even though we now have all of the ultrasonic waveform data in the DLIS file, we are in a similar situation as we were in for the sonic waveforms: We do not know what the time axis of each waveform is.
The USFR
("Ultrasonic Sampling Frequency") parameter seems like a promising first step, but its values throughout the DLIS files in the Volve Data Village dataset (500 kHz, 666 kHz, and even 0 kHz) are much too low to be accurate when we know from the literature that the ultrasonic pulses can carry significant energy from 200–700 kHz. For that reason, we'll have to discount this parameter.
Instead, we can look more closely at the three depth-by-azimuth channels with time units, namely WFDL
("Waveform Delay"), TTBK
("Transit Time"), and UTIM
("Time of Arrival of Waves").
wfdl = get_channel(frame60B, 'WFDL')
ttbk = get_channel(frame60B, 'TTBK')
utim = get_channel(frame60B, 'UTIM')
fig, axes = plt.subplots(ncols=3, figsize=(9,12), sharey=True, constrained_layout=True)
ax = axes[0]
im = paint_channel(ax, curves60B['WFDL'], curves60B[frame60B.index], ang)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{wfdl.name}: {wfdl.long_name} [{wfdl.units}]')
ax.set_ylabel('Depth $z$ [m]')
ax = axes[1]
im = paint_channel(ax, curves60B['TTBK'], curves60B[frame60B.index], ang)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{ttbk.name}: {ttbk.long_name} [{ttbk.units}]')
ax = axes[2]
im = paint_channel(ax, curves60B['UTIM'], curves60B[frame60B.index], ang)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{utim.name}: {utim.long_name} [{utim.units}]')
for ax in axes:
ax.set_xticks([0, 90, 180, 270, 360])
ax.grid(True)
ax.set_xlabel('Azimuthal angle $\\varphi$ [°]')
First, let's look closer at UTIM
. As it increases from a low value at the bottom of the well (where the logging tool starts) to a high value at the top of the well, it might represent the times, relative to some initial reference time, at which each waveform was measured. If that is the case, we can expect that the time between the first and last values of UTIM
corresponds to other time measures in the file. While there is no time index channel in the file, we can at least find an average tool speed from the CS
("Cable speed") channel that we can compare with.
utim_bottom = np.min(curves60B['UTIM'][0,:]) / 1000 # Converted to from ms to s
utim_top = np.min(curves60B['UTIM'][-1,:]) / 1000 # Converted to from ms to s
print(f'UTIM at bottom: {utim_bottom/60:.2f} min')
print(f'UTIM at top: {utim_top/60:.2f} min')
utim_diff = (utim_top-utim_bottom)
print(f'UTIM difference: {utim_diff/60:.2f} min')
print()
cs_mean = np.mean(curves60B['CS']) / 3600 # Converted from m/h to m/s
print(f'Mean cable speed: {cs_mean*60:.2f} m/min')
depths = curves60B[index60B.name]
depth_range = max(depths) - min(depths)
print(f'Depth range: {depth_range:.2f} m')
cable_time = depth_range/cs_mean
print(f'Expected bottom-to-top time: {cable_time/60:.2f} min')
print()
print(f'UTIM-cable time difference: {utim_diff - cable_time} s')
UTIM at bottom: 14.10 min UTIM at top: 56.35 min UTIM difference: 42.25 min Mean cable speed: 16.83 m/min Depth range: 710.18 m Expected bottom-to-top time: 42.20 min UTIM-cable time difference: 3.008782484166204 s
The estimated time of the log tool moving from the bottom to the top of the well, as found from the mean cable speed, corresponds very well with the difference in UTIM
values at the top and the bottom of the well. Thus, UTIM
does seem to represent the time at which each waveform was measured.
We can look more closely at UTIM
to see when the different measurements at each depth were made:
# Choose the depth indexes to plot at
n_depths = 6
i_min = 4148
i_max = i_min + n_depths
fig, ax = plt.subplots()
# Plot the UTIM values for each depth index i against azimuthal angle
colors = mpl.cm.coolwarm(np.linspace(0, 1, n_depths))
for j in range(n_depths):
ax.plot(ang, curves60B['UTIM'][i_min+j, :]/1000, 'o', color=colors[j])
ax.set_ylabel('UTIM [s]')
ax.set_xlabel('Azimuthal angle $\\varphi$ [°]')
ax.set_xlim(0, 360)
ax.set_xticks((0, 90, 180, 270, 360))
ax.grid(True)
# Create space at the top for the legend
ylim = list(ax.get_ylim())
ylim[1] += 0.5
ax.set_ylim(ylim)
legend = [f'{curves60B[frame60B.index][i]:.2f} m' for i in range(i_min, i_max)]
ax.legend(legend, loc='upper center', ncol=3)
fig.set_tight_layout(True)
This indicates that the tool measures at every second azimuth during one rotation, and fills in the remaining azimuthal measurements during a later rotation.
Next up are WFDL
and TTBK
. The latter is called "Transit Time", which we may assume corresponds to the "Travel time" defined in the USIT article by Hayman et al. (1991) as the time of arrival of an estimate of the FIE envelope maximum. (While the Hayman et al. article describes a fast approximate method to find the envelope maximum, the USIT patent by Wright (1993) describes a more accurate approach that we will follow in the following.) Thus, it represents the time the pulse takes from the transducer to the casing and back.
This implies that the differences between high and low TTBK
values at the same depth come from the tool being eccentered; the travel time is shortest when the transducer is pointing in the direction of eccentering, and longest when it is pointing against the direction of eccentering. This also implies that the waveforms stored in the U001
, U002
, ...
channels have been shifted to align their peaks.
As the two channels do seem to have the same overall shape, but with an offset, then maybe WFDL
represents the time of the first sample? Let's plot histograms of the two channels and their difference to have a closer look:
time_diff = curves60B['TTBK'] - curves60B['WFDL']
fig, axes = plt.subplots(figsize=(9,6), nrows=3)
ax = axes[0]
xlim = (60.5, 87.5)
ax.hist(curves60B['TTBK'].flatten(), bins=np.linspace(xlim[0], xlim[1], 217))
ax.set_xlim(xlim)
ax.set_title('TTBK histogram')
ax = axes[1]
xlim = (53, 80)
ax.hist(curves60B['WFDL'].flatten(), bins=np.linspace(xlim[0], xlim[1], 217))
ax.set_xlim(xlim)
ax.set_title('WFDL histogram')
ax = axes[2]
xlim = (6.55, 7.9)
hist = ax.hist(time_diff.flatten(), bins=np.linspace(xlim[0], xlim[1], 217))
ax.set_xlim(xlim)
ax.set_title('TTBK – WFDL histogram')
for ax in axes:
ax.set_xlabel('Time [µs]')
ax.set_ylabel('Bin frequency')
fig.set_tight_layout(True)
TTBK
and WFDL
do indeed have the same overall shape, and their difference is almost always between 6.7 µs and 7.7 µs. But the most interesting result here is the WFDL
histogram, which indicates that the values of WFDL
are quantised, with a constant step separating them.
If the waveforms are all 120 samples and aligned to each other, this implies that a longer waveform was originally recorded and that the waveforms present in the files are shorter extracts of these original waveforms, selected so that the peaks of all waveforms are aligned. If that is the case, the step between the WFDL
values should represent the sampling period of the waveforms. Let's find out what that step is by looking at the differences between adjacent values of WFDL
:
from collections import Counter
wfdl_flat = curves60B['WFDL'].flatten()
diff_counter = Counter(np.round( np.abs(wfdl_flat[1:] - wfdl_flat[0:-1]), decimals=4))
print(diff_counter)
Counter({0.5: 154695, 0.0: 89262, 1.0: 73334, 1.5: 14725, 2.0: 1997, 2.5: 736, 3.0: 432, 3.5: 221, 4.0: 96, 4.5: 42, 5.0: 14, 6.0: 9, 6.5: 9, 5.5: 8, 7.0: 7, 7.5: 4})
Thus, we have found that the step is 0.5 µs, which seems to be the waveform sampling period. If so, the sampling rate of the ultrasonic waveforms is 2 MHz.
We thus assume that WFDL
specifies the time of the first waveform sample, TTBK
the arrival time of the pulse, and that the waveforms are sampled at 2 MHz. We can test all of this to check that it fits, by comparing the difference TTBK
– WFDL
with the time between the first sample and the pulse arrival. We find the arrival of the pulse through a quadratic fit around the peak of its envelope. The envelope is found as the magnitude of the analytic signal found through the Hilbert transform.
# Calculate TTBK - WFDL
ttbk_wfdl_diff = curves60B['TTBK'] - curves60B['WFDL']
# Compute the envelope of every waveform in the file
us_env = np.abs(signal.hilbert(us_data_reeq, axis=2))
# Compute the relative time (time since the first sample) of every sample
us_t_rel = 0.5 * np.arange(120)
We can first look at a single waveform and its envelope, and the peak found from the envelope
i_us = 4150 # Depth index of waveform to look at
j_us = 0 # Azimuthal index of waveform to look at
def find_peak(envelope, t_rel):
"""Find the time and amplitude of an envelope peak accurately using a quadratic polynomial fit"""
i_max = np.argmax(envelope)
i_range = i_max + np.array([-1, 0, 1])
fit = np.polyfit(t_rel[i_range], envelope[i_range], 2)
t_max = - fit[1] / (2 * fit[0])
max_peak = np.polyval(fit, t_max)
return t_max, max_peak
t_peak, max_peak = find_peak(us_env[i_us, j_us, :], us_t_rel)
print(f'TTBK - WFDL for waveform: {ttbk_wfdl_diff[i_us, j_us]:.2f} us')
print(f'Envelope peaks at {t_peak:.2f} µs after first sample')
fig, ax = plt.subplots()
ax.plot(us_t_rel, us_env[i_us, j_us, :], color='0.6', label='Envelope')
ax.plot(us_t_rel, -us_env[i_us, j_us, :], color='0.6')
ax.plot(us_t_rel, us_data_reeq[i_us, j_us, :], label='Waveform')
ax.plot(t_peak, max_peak, 'o', alpha=0.5, label='Envelope peak')
ax.set_ylabel('Waveform [arbitrary units]')
ax.set_xlabel("Relative time $t'$ [µs]")
ax.set_xlim(0, 60)
ax.legend()
ax.grid(True)
fig.set_tight_layout(True)
TTBK - WFDL for waveform: 7.48 us Envelope peaks at 9.42 µs after first sample
This was around 2 µs off from what we expected. What do we find if we investigate the statistics of this difference for every waveform?
t_peaks = np.zeros(ttbk_wfdl_diff.shape)
for i in range(t_peaks.shape[0]):
for j in range(t_peaks.shape[1]):
t_peaks[i, j], _ = find_peak(us_env[i, j, :], us_t_rel)
fig, ax = plt.subplots()
ax.hist((t_peaks - ttbk_wfdl_diff).flatten(), bins=np.linspace(1.85,2.2,100))
ax.set_xlabel('Time difference [µs]')
ax.set_ylabel('Bin frequency')
ax.set_title('Difference between relative peak arrival time and (TTBK – WFDL)')
fig.set_tight_layout(True)
Unless our assumptions about the meanings of the WFDL
and/or TTBK
channels are incorrect, this means that WFDL
and TTBK
use different reference times. In that case, the values from one would need to be offset to be directly comparable to the values form the other.
Another look at the list of USIT parameters shows us that there is a parameter called USTO
(‘USIT Time Offset’), with the value of –2 µs. If we assume that WFDL
must first be corrected as WFDL
+USTO
, the aforementioned difference becomes close to zero:
# Calculate TTBK - (WFDL + USTO)
usto = f.object('PARAMETER', 'USTO').values[0]
ttbk_wfdl_diff2 = curves60B['TTBK'] - (curves60B['WFDL'] + usto)
fig, ax = plt.subplots()
ax.hist((t_peaks - ttbk_wfdl_diff2).flatten(), bins=np.linspace(-0.15,0.2,100))
ax.set_xlabel('Time difference [µs]')
ax.set_ylabel('Bin frequency')
ax.set_title('Difference between relative peak arrival time and (TTBK – [WFDL + USTO])')
fig.set_tight_layout(True)
The remaining differences that we now find is much smaller than the sampling period of 0.5 µs. While we don't know exactly where they come from, they might simply stem from the USIT processing having a slightly different method to estimate the time of the envelope peak than the method we have used here.
We can then find the time axis of every waveform by using WFDL
+USTO
as the time of the first sample. We'll put it in an array with the same shape as the waveforms, i.e., depth by angle by sample.
us_t0 = curves60B['WFDL'] + usto
us_t = us_t0[:, :, np.newaxis] + us_t_rel[np.newaxis, np.newaxis, :]
Now we have a timestamp for every waveform sample. While the waveforms in the U001
, U002
, ...
channels were stored aligned to each other, we can use this time information to plot the waveforms with the same shift that they were originally recorded with. As an example, we can plot every waveform from the same depth into a polar plot.
def waveform_cross_section(ax, i=0, envelope=False):
"""Plot waveform data (or its logaritmic envelope) as a polar plot at a given depth index"""
# Get the base data to plot
times = us_t[i,:,:]
if envelope:
data = 20*np.log10(us_env[i,:,:])
else:
data = us_data_reeq[i,:,:]
ang_mesh = np.repeat(ang[:, np.newaxis], data.shape[1], axis=1)
n_ang = data.shape[0]
# Make some transformations of the data to ensure that the pcolormesh plot function plots each
# sample as an isosceles trapezoid centred at the sample's angle and time.
# Repeat every element in the base data twice along the angle axis
ang_mesh = np.repeat(ang_mesh, 2, axis=0)
times = np.repeat(times, 2, axis=0)
data = np.repeat(data, 2, axis=0)
data = data[:-1, :-1] # Used for trapezoid centres; need one fewer value than for trapezoid corners
# Shift the coordinates so that each trapezoid is centered at the sample's angle and time
ang_mesh = ang_mesh - (360 / n_ang) / 2
times = times - 0.25
# Roll the azimuthal mesh
ang_mesh = np.roll(ang_mesh, shift=-1, axis=0)
ang_mesh[-1, :] += 360 # Ensure that trapezoid corner coordinates are strictly increasing
if envelope:
vmax = np.max(data)
return ax.pcolormesh(np.radians(ang_mesh), times, data, vmin=vmax-50, vmax=vmax, cmap='CMRmap')
else:
vmax = np.max(np.abs(data)) / 5
return ax.pcolormesh(np.radians(ang_mesh), times, data, vmin=-vmax, vmax=vmax, cmap='seismic')
i_us = 4150
fig, axes = plt.subplots(ncols=2, subplot_kw=dict(polar=True), figsize=(9,5))
ax = axes[0]
pc = waveform_cross_section(ax, i_us, envelope=False)
ax.set_title('Waveforms')
ax = axes[1]
waveform_cross_section(ax, i_us, envelope=True)
ax.set_title('Envelopes [dB]')
for ax in axes:
ax.grid(True)
fig.set_tight_layout(True)
/var/folders/0k/6fvjhp7x2659gf4_pbq0zkj40000gp/T/ipykernel_37145/155844224.py:35: MatplotlibDeprecationWarning: Auto-removal of grids by pcolor() and pcolormesh() is deprecated since 3.5 and will be removed two minor releases later; please call grid(False) first. return ax.pcolormesh(np.radians(ang_mesh), times, data, vmin=-vmax, vmax=vmax, cmap='seismic') /var/folders/0k/6fvjhp7x2659gf4_pbq0zkj40000gp/T/ipykernel_37145/155844224.py:32: MatplotlibDeprecationWarning: Auto-removal of grids by pcolor() and pcolormesh() is deprecated since 3.5 and will be removed two minor releases later; please call grid(False) first. return ax.pcolormesh(np.radians(ang_mesh), times, data, vmin=vmax-50, vmax=vmax, cmap='CMRmap')
We can see that the FIE is received at different times at different angles. This reflects the eccentering of the USIT; where the tool is closer to the wall, the FIE will be received earlier, and where the tool is further away, the FIE will be received later.
Waveform summary: The waveforms recorded by the USIT are organised into channels U001
, U002
, ...
according to the azimuthal angle at which they were recorded. Different waveforms may have had different gains to them, and these gains can be compensated for using the WAGN
channel. The recorded waveforms are stored in such a way that the first interface echoes (FIEs) are aligned with each other. The time axis of each waveform can be found through two steps:
WFDL(i,j) + USTO
.Now that we understand the ultrasonic waveforms better, we can look at a simple example of what we can do with them. We can investigate a simple way to draw information about the well status from them and compare with the reference information present in the log file.
First of all, let's look at a waveform and its envelope.
us_env_dB = 20*np.log10(us_env)
fig, axes = plt.subplots(nrows=2, figsize=(4,6))
ax = axes[0]
ax.plot(us_t_rel, us_env[i_us, j_us, :], color='0.6', label='Envelope')
ax.plot(us_t_rel, -us_env[i_us, j_us, :], color='0.6')
ax.plot(us_t_rel, us_data_reeq[i_us, j_us, :], label='Waveform')
ax.set_xlim(0, 60)
ax.legend()
ax.grid(True)
ax.set_ylabel('Waveform [arbitrary units]')
ax.set_xlabel('Time [µs]')
ax.set_title('Linear representation')
ax = axes[1]
ax.plot(us_t_rel, us_env_dB[i_us, j_us, :], color='0.6', label='Envelope')
ax.set_xlim(0, 60)
ax.legend()
ax.grid(True)
ax.set_ylabel('Waveform envelope [dB]')
ax.set_xlabel('Time [µs]')
ax.set_title('Logarithmic representation')
fig.set_tight_layout(True)
The waveform contains a strong initial reflection from the fluid-casing interface, followed by a decaying resonance of the pulse component that was transmitted into the casing. From the literature, we know that this decay should be at least approximately exponential, and that the decay is faster for higher-impedance materials behind the casing. And if the decay is exponential, it should be approximately linear on a logarithmic plot like the one shown here.
The impedance behind the casing, which is available in the AIBK
channel, has been found through the aforementioned T3 algorithm, which is relatively complex. We will not try to reproduce it here; instead, we will try to get similar information through a linear fit of the logarithmic envelope, which has also been used by Sirevaag et al. to investigate the impedance behind the casing. This gives us a decay rate $L_1$ in dB/µs, which we can then compare against the impedance in the AIBK
channel.
We first show this for a single waveform envelope as an example:
i_us = 4150
def fit_decay(env_dB, env_t, fit_slice):
"""Find a linear fit over the fit_slice indices in env_dB and env_t and return it"""
return np.polyfit(env_t[fit_slice], env_dB[fit_slice], 1)
# Specify the range of the fit
t_fit = [20, 40] # Time range to carry out the fit in
i_fit = [np.argmin(np.abs(us_t_rel - t_fit[0])), # First sample index of the range
np.argmin(np.abs(us_t_rel - t_fit[1]))] # Last sample index of the range
fit_slice = slice(i_fit[0], i_fit[1] + 1) # The range expressed as a slice object
# Get the linear fit and evaluate it at the edges of the time range
fit = fit_decay(us_env_dB[i_us, j_us, :], us_t_rel, fit_slice)
val = np.polyval(fit, t_fit)
# Plot the envelope and the fit
fig, ax = plt.subplots()
ax.plot(us_t_rel, us_env_dB[i_us, j_us, :], label='Envelope')
ax.plot(t_fit, val, linewidth=3, label='Fit')
ax.set_xlim(0, 60)
ax.legend()
ax.grid(True)
ax.set_ylabel('Waveform envelope [dB]')
ax.set_xlabel('Time [µs]')
print(f'L_1 value: {-fit[0]:.3f} dB/µs')
L_1 value: 0.158 dB/µs
We then find the decay rate $L_1$ for every waveform in the file:
us_L1 = np.zeros(curves60B['AIBK'].shape)
for i in range(us_L1.shape[0]):
for j in range(us_L1.shape[1]):
us_L1[i, j] = -fit_decay(us_env_dB[i, j, :], us_t_rel, fit_slice)[0]
We can then plot $L_1$ together with AIBK
to compare them:
fig, axes = plt.subplots(figsize=(9,12), ncols=2, sharey=True, constrained_layout=True)
ax = axes[0]
min_quantile = np.mean(curves60B['AIBK'] <= 0) # The quantile at which we find the value 0
max_quantile = np.mean(curves60B['AIBK'] <= 7.5) # The quantile at which we find the value 7.5
im = paint_channel(ax, curves60B['AIBK'], curves60B[frame60B.index], ang, cmap='YlOrBr', vmin=0, vmax=7.5)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'{aibk.long_name} [{aibk.units}]')
ax.set_ylabel('Depth $z$ [m]')
ax = axes[1]
vmin = np.quantile(us_L1, min_quantile) # The value we find at quantile min_quantile in us_L1
vmax = np.quantile(us_L1, max_quantile) # The value we find at quantile max_quantile in us_L1
im = paint_channel(ax, us_L1, curves60B[frame60B.index], ang, cmap='YlOrBr', vmin=vmin, vmax=vmax)
cbar = fig.colorbar(im, ax=ax, location='top')
cbar.set_label(f'Decay rate $L_1$ [dB/µs]')
for ax in axes:
ax.set_xticks([0, 90, 180, 270, 360])
ax.grid(True, color='0.5', alpha=0.5)
ax.set_xlabel('Azimuthal angle $\\varphi$ [°]')
Comparing the two, we see the same overall patterns, especially near the top of the log. But how well correlated are their numerical values with each other? To look into that, we can calculate some correlation measures and plot a 2D histogram showing the joint distribution of the $L_1$ and impedance values:
import scipy.stats as stats
x = curves60B['AIBK'].flatten()
y = us_L1.flatten()
print(f'Pearson correlation: {stats.pearsonr(x, y)[0]:.3f}')
print(f'Spearman correlation: {stats.spearmanr(x, y)[0]:.3f}')
Pearson correlation: 0.457 Spearman correlation: 0.634
# Get a colormap that ensures that histogram bins with zero occurrences still have a colour
cmap = copy.copy(mpl.cm.get_cmap('viridis'))
cmap.set_bad(cmap.colors[0])
# Plot the 2D histogram
fig, ax = plt.subplots()
hist = ax.hist2d(x, y, norm=mpl.colors.LogNorm(), cmap=cmap,
bins=[np.linspace(-2, 12, 100), np.linspace(-0.1, 1, 100)]);
cbar = fig.colorbar(hist[3], ax=ax)
cbar.set_label('Bin frequency')
ax.set_title('2D histogram of $L_1$ and $Z$')
ax.set_xlabel('Reference impedance $Z$ [MRayl]')
ax.set_ylabel('Decay rate $L_1$ [dB/µs]')
fig.set_tight_layout(True)
The correlation metrics and the histogram all show that there is a clear positive correlation between $L_1$ and the reference impedance, but it's far from a one-to-one-relationship between the two.
Reprocessing summary: While this is just a simple example of what we can do with the ultrasonic waveforms, it does show that the data present in these files lets us try out various processing algorithms on real-world data. One challenge with this approach is that we do not have access to a ground truth that we can use to objectively evaluate the quality of processing methods. All we have is the measured waveforms and one processing method's estimates of what the waveforms imply.