Wellpathpy tutorial

This document aims to provide a sample workthrough using wellpathpy showing:

Abbreviations

m metres
ft feet
md measured depth
inc inclination
azi azimuth
tvd true vertical depth
east_offset horizontal distance away from wellhead towards the east
north_offset horizontal distance away from wellhead towards the north
tvdss true vertical depth subsea
mE horizontal distance in meters away from surface location towards the east
mN horizontal distance in meters away from surface location towards the north

Imports

In this tutorial, we will alias wellpathpy as wp:

import wellpathpy as wp

Loading a deviation

Wellpathpy provides a fairly simple loading function for reading a deviation survey from CSV.

Loading a deviation from CSV

A valid input file must be a CSV file containing the columns: md, inc, azi in that order, as shown in this example:

md,inc,azi
0,0,244
10,11,220
50,43,254
150,78.5,254
252.5,90,359.9
  • column headers are generally expected but will be skipped when the file is read
  • if no headers are provided, the skiprows argument can be set to 0
  • md must increase monotonically
  • as inc and azi cannot be distinguished numerically it is the user’s responsibility to ensure the data are passed in this order
  • inc must be in range 0 <= inc < 180 (to allow for horizontal wells to climb)
  • azi must be in range 0 <= azi < 360

You can then load them into wellpathpy using:

md, inc, azi = wp.read_csv(fname)

wp.read_csv simply calls np.loadtxt with delimiter=',' and skiprows=1. These can be changed if required; for example the delimiter and skiprows can be changed with:

md, inc, azi = wp.read_csv(fname, delimiter='\t', skiprows=0)

Additional kwargs accepted by np.loadtxt can also be passed in, for example:

md, inc, azi = wp.read_csv(fname, comments='$')

Notes:

Some simple sanity checks are performed to reject bad CSVs. wp.read_csv supports all options np.loadtxt supports. Only those columns named md, inc, azi will be read.

If the deviation survey is not in CSV, is generated in a different place in your program, or is from some other source, wellpathpy is still useful. If you provide three np.ndarray md, inc, and azi, the rest of wellpathpy works fine.

Observe that the same basic requirements still apply:

  • md, inc and azi have the same shape
  • md increases monotonically
  • inc is in range 0-180
  • azi is in range 0-360

Once md, inc and azi have been returned from wp.read_csv(), an instance of the wp.deviation() class is created with:

dev = wp.deviation(
    md = md,
    inc = inc,
    azi = azi
)
With this, it is then possible to resample the depths using the minimum_curvature() method
and go back to a deviation survey in md, inc and azi:
step = 30
depths = list(range(0, int(dev.md[-1]) + 1, step))
pos = dev.minimum_curvature().resample(depths = depths)
dev2 = pos.deviation()

Notes:

With increasing step size, float uncertainty can introduce some noise as shown in the figures below. First we see an overview of the well in 3D, followed by plots of inclination and azimuth versus depth.

Example well positional log\n(no header information) Deviation_resampling_example_step5 Deviation_resampling_example_step30

Loading the well header

To make sense of the deviation position, wellpathpy supports reading a survey header from json file. The header requires the following keys:

{
"datum": "kb",
"elevation_units": "m",
"elevation": 100.0,
"surface_coordinates_units": "m",
"surface_easting": 1000.0,
"surface_northing": 2000.0
}
header = wp.read_header_json(fname)

Notes:

This function is provided for convenience - wellpathpy does not care about the source of this data. It will simply use json.load() to read the JSON file and save it as a python dict.

Converting deviation surveys to positional logs

All these methods can be accessed from the deviation object created with:

dev = wp.deviation(
    md = md,
    inc = inc,
    azi = azi,
    )

Standard method

The standard method for converting a deviation surveys [md, inc, azi] into a positional logs [tvd, northing, easting] is the minimum curvature method. This method is provided by wellpathpy and is recommended for most use cases.

  • minimum curvature method : dev.minimum_curvature()

    This method uses angles from upper and lower end of survey interval to calculate a curve that passes through both survey points. This curve is smoothed by use of the ratio factor defined by the tortuosity or dogleg of the wellpath. This method returns a dogleg severity calculated for a given course_length.

Comparison methods

Other methods are provided should the need arise to compare mininum curvature to older surveys that may have been calculated with one of these methods. In general these other methods are not recommended.

  • radius of curvature method : dev.radius_curvature()

    Calculate TVD using radius or curvature method. Caution: this will yield unreliable results when data are closely spaced or when the borehole is straight but deviated. This method uses angles from upper and lower end of survey interval to calculate a curve that passes through both survey points.

  • average tan method : dev.tan_method()

    Calculate TVD using average tangential method. This method averages the inclination and azimuth at the top and bottom of the survey interval before taking their sine and cosine, this average angle is used to estimate tvd.

  • balanced tan method : dev.tan_method(choice='bal')

    Calculate TVD using balanced tangential method. This method takes the sines and cosines of the inclination and azimuth at the top and bottom of the survey interval before averaging them, this average angle is used to estimate tvd. This will provide a smoother curve than the average tan method but requires closely spaced survey stations to avoid errors.

  • high tan method : dev.tan_method(choice='high')

    Calculate TVD using high tangential method. This method takes the sines and cosines of the inclination and azimuth at the bottom of the survey interval to estimate tvd. This method is not recommended as it can make gross tvd and offset errors in typical deviated wells.

  • low tan method : dev.tan_method(choice='low')

    Calculate TVD using low tangential method. This method takes the sines and cosines of the inclination and azimuth at the top of the survey interval to estimate tvd. This method is not recommended as it can make gross tvd and offset errors in typical deviated wells.

Usage

In order to use these functions, you first need a deviation object as described in Loading a deviation. You can then run the following methods once you’ve imported your deviation and header and done any unit conversion required as described above.

# The recommended method for most use-cases
pos = dev.mininum_curvature(course_length=30)
tvd = pos.depth
northing = pos.northing
easting = pos.easting

# Comparison methods to contrast with older deviation surveys
tvd, northing, easting      = dev.radius_curvature()
tvd, northing, easting      = dev.tan_method() # for the default 'avg' method
tvd, northing, easting      = dev.tan_method(choice='bal')
tvd, northing, easting      = dev.tan_method(choice='high')
tvd, northing, easting      = dev.tan_method(choice='low')

We can compare the outputs of all these methods in the figure below:

Dev_methods_comparison

Well location and tvdss

The methods above are not aware of surface location or datum elevation. If you want to move the positional log to a given surface location, to 0,0 coordinates, or shift the tvd to tvdss, you can use the following functions which return a copy of the positional log by default (inplace=False).

  • to shift a positional log to a wellhead location
pos_wellhead = pos.to_wellhead(surface_northing=surface_northing,
                               surface_easting=surface_easting)
pos_to_wellhead
  • to shift a positional log to a 0,0 coordinate location
pos_zero = pos_wellhead.to_zero(surface_northing=surface_northing,
                                surface_easting=surface_easting)
loc_to_zero
  • to shift a positional log to tvdss
pos_tvdss = pos.to_tvdss(datum_elevation=header['elevation'])
loc_to_tvdss

If you have a header loaded as shown in the Loading the well header section, you can use that object to access the required properties with:

surface_northing = header['surface_northing']
surface_easting  = header['surface_easting']
datum_elevation  = header['datum_elevation']

Notes:

Wellpathpy is oblivious to units, and assumes the input units are consistent.

Exporting results

The two main wellpathpy objects; deviation and position logs can be written to CSV via object methods as shown below. These both call np.savetxt with fmt='%.3f' and delimiter=','. The deviation also has header='md,inc,azi' and the postition has header='easting,northing,depth'. Other kwargs accepted by np.savetxt are also accepted.

  • for a deviation survey:
dev.to_csv('./deviation.csv')
  • for a positional log:
pos.to_csv('./position.csv')

Additional kwargs can be passed like:

dev.to_csv('./deviation.csv', fmt='%.2e')
pos.to_csv('./position.csv', header='X,Y,Z', comments='$')

This is a pretty straight-forward function convenient CSV writing. If you need more control, or more sophisticated output, you must implement your own writer.