welleng package
Subpackages
- welleng.errors package
- Submodules
- welleng.errors.extract_iscwsa_codes module
- welleng.errors.extract_owsg_codes_generic module
- welleng.errors.tool_errors module
ABIXY_TI1()ABIXY_TI2()ABIZ()ABXY_TI1()ABXY_TI2()ABZ()AMID()AMIL()ASIXY_TI1()ASIXY_TI2()ASIXY_TI3()ASIZ()ASXY_TI1()ASXY_TI2()ASXY_TI3()ASZ()AZ()CNA()CNI()DBH()DBHR()DREF()DSF()DST()MBIXY_TI1()MBIXY_TI2()MBXY_TI1()MBXY_TI2()MBZ()MDI()MFI()MSIXY_TI1()MSIXY_TI2()MSIXY_TI3()MSXY_TI1()MSXY_TI2()MSXY_TI3()MSZ()SAG()SAGE()ToolErrorXCL()XCLA()XCLH()XYM1()XYM2()XYM3()XYM3E()XYM3L()XYM4()XYM4E()XYM4L()
- Module contents
- welleng.exchange package
- Submodules
- welleng.exchange.csv module
- welleng.exchange.edm module
- welleng.exchange.wbp module
- Module contents
Submodules
welleng.architecture module
- class welleng.architecture.String(name, top, bottom, *args, method='bottom_up', **kwargs)[source]
Bases:
object- __init__(name, top, bottom, *args, method='bottom_up', **kwargs)[source]
A generic well bore architecture collection, e.g. a casing string made up a a number of different lengths of weights and grades.
- Parameters:
name (str) – The name of the collection.
top (float) – The shallowest measured depth at the top of the collection of items in meters.
bottom (float) – The deepest measured depth at the bottom of the collection of items in meters.
method (string (default: 'bottom up')) – The method in which items are added to the collection, either ‘bottom up’ starting from the deepest element and adding items above, else ‘top down’ starting from the shallowest item and adding items below.
- add_section_bottom_up(**kwargs)[source]
Sections built from the bottom up until the top of the top section is equal to the defined string top.
Default is to extend the section to the top of the String as defined in the String.top property (when length = top = None).
Parameters:
welleng.clearance module
- class welleng.clearance.Clearance(reference: Survey, offset: Survey, k: float = 3.5, sigma_pa: float = 0.5, Sm: float = 0.3, Rr: float = 0.4572, Ro: float = 0.3048, kop_depth: float = -inf)[source]
Bases:
objectInitialize a welleng.clearance.Clearance object.
- Parameters:
reference (welleng.survey.Survey object) – The current well from which other wells are referenced.
offset (welleng.survey.Survey object) – The other well.
k (float) – The dimensionless scaling factor that determines the probability of well crossing.
sigma_pa (float) – Quantifies the 1-SD uncertainty in the projection ahead of the current survey station. Its value is partially correlated with the projection distance, determined as the current survey depth to the bit plus the next survey interval. The magnitude of the actual uncertainty also depends on the planned curvature and on the actual BHA performance at the wellbore attitude in the formation being drilled. The project-ahead uncertainty is only an approximation, and although it is predominantly oriented normal to the reference well, it is mathematically convenient to define sigma_pa as being the radius of a sphere.
Sm (float) – The surface margin term increases the effective radius of the offset well. It accommodates small, unidentified errors and helps overcome one of the geometric limitations of the separation rule, described in the Separation-Rule Limitations section. It also defines the minimum acceptable slot separation during facility design and ensures that the separation rule will prohibit the activity before nominal contact between the reference and offset wells, even if the position uncertainty is zero.
Rr (float) – The openhole radius of the reference borehole (in meters).
Ro (float) – The openhole radius of the offset borehole (in meters).
kop_depth (float) – The kick-off point (measured) depth along the well bore - the default value assures that the first survey station is utilized.
References
Sawaryn, S. J., Wilson, H.. , Bang, J.. , Nyrnes, E.. , Sentance, A.. , Poedjono, B.. , Lowdon, R.. , Mitchell, I.. , Codling, J.. , Clark, P. J., and W. T. Allen. “Well-Collision-Avoidance Separation Rule.” SPE Drill & Compl 34 (2019): 01–15. doi: https://doi.org/10.2118/187073-PA
- __init__(reference: Survey, offset: Survey, k: float = 3.5, sigma_pa: float = 0.5, Sm: float = 0.3, Rr: float = 0.4572, Ro: float = 0.3048, kop_depth: float = -inf)[source]
- sf_vs_md()[source]
Separation-factor-versus-MD profiles for both methods along the reference well — a single consistent computation, inherited by every
Clearancesubclass.At each reference station, returns the minimum over the offset stations of (i) the pedal / support-function separation factor (the ISCWSA separation rule) and (ii) the exact combined-ellipsoid Mahalanobis separation factor, evaluated over the same station pairing with this clearance’s
k,Smandsigma_pa. Because only the metric differs, the exact factor is>=the pedal factor at every station (Kantorovich). Comparing two differentClearancesubclasses station-by-station is not apples-to-apples — they pair reference/offset and resolve closest approach differently, and their profiles can appear to cross.- Returns:
md ((N,) ndarray) – Reference-well measured depth at each station.
pedal_sf ((N,) ndarray) – Pedal / support-function (ISCWSA separation-rule) separation factor.
mahalanobis_sf ((N,) ndarray) – Exact combined-ellipsoid (Mahalanobis) separation factor.
- class welleng.clearance.IscwsaClearance(*clearance_args, minimize_sf=None, **clearance_kwargs)[source]
Bases:
ClearanceParameters:
- clearance_args: List
See ‘welleng.clearance.Clearance` for args.
- minimize_sf: bool
If True (default), then the closest points on the reference well are determined and added to the ref object as interpolated stations.
- clearance_kwargs: dict
See ‘welleng.clearance.Clearance` for kwargs.
Attributes:
- Roarray of floats
The radius of the offset well at each station of the off well.
- Rrarray
The radius of the reference well at each station on the ref well.
- sfarray of floats
The calculated Separation Factor to the closest point on the offset well for each station on the reference well.
- Smfloat
The surface margin term increases the effective radius of the offset well. It accommodates small, unidentified errors and helps overcome one of the geometric limitations of the separation rule, described in the Separation-Rule Limitations section. It also defines the minimum acceptable slot separation during facility design and ensures that the separation rule will prohibit the activity before nominal contact between the reference and offset wells, even if the position uncertainty is zero.
- calc_hole: array of floats
The calculated combined equivalent radius of the two well bores, i.e. the sum or their radii plus margins.
- closest:
The closest point on the off well from each station on the ref well.
- distance_cc:
The closest center to center distance for each station on the ref well to the off well.
- eou_boundary:
The sum of the ellipse of uncertainty radii of the ref and off wells.
- eou_separation:
The distance between the ellipses of uncertainty of the ref and off wells.
- hoz_bearing:
The horizontal bearing between the closest points in radians.
- hoz_bearing_deg:
The horizontal bearing between the closest points in degrees.
- idx: int
The index of the closest point on the off well for each station on the ref well.
- masd:
The Minimum Allowable Separation Distance from the ref well.
- off: Survey
The offset well Survey.
- off_pcr:
The Pedal Curve Radii for each station on the off well.
- off_cov_hla:
The covariance matrix in the HLA domain for each station of the off well.
- off_cov_nev:
The covariance matrix in the NEV domain for each station of the off well.
- off_nevs:
The NEV coordinates of the off well.
- offset: Survey
The initial offset well Survey.
- offset_nevs:
The initial NEV coordinates of the offset well.
- ref: Survey
The ref well Survey.
- ref_pcr:
The Pedal Curve Radii for each station on the ref well.
- ref_cov_hla:
The covariance matrix in the HLA domain for each station of the ref well.
- ref_cov_nev:
The covariance matrix in the NEV domain for each station of the ref well.
- ref_nevs:
The NEV coordinates of the ref well.
- reference: Survey
The initial reference well Survey.
- reference_nevs:
The initial NEV coordinates of the reference well.
- sf:
The Separation Factor between the closest point on the off well for each station on the ref well.
- toolface_bearing:
The toolface bearing in radians from each station on the ref well to the closest point on the off well.
- trav_cyl_azi_deg:
The heading in degrees from each station on teh ref well to the closest point on the off well.
- wellbore_separation:
The distance between the edge of the wellbore for each station on the ref well to the closest point on the off well.
- get_sf_mins()[source]
Method for assessing whether a minima has occurred between survey station SF values on the reference well and if so calculates the minimum SF value between stations (between the previous and next station relative to the identified station).
Modifies the sf attribute to include the interpolated minimum sf values.
- class welleng.clearance.MahalanobisClearance(*args, n_candidates=8, tol=0.001, **kwargs)[source]
Bases:
ClearanceAnti-collision using the exact Mahalanobis k-sigma boundary of the combined (relative-position) uncertainty ellipsoid, rather than the pedal-curve support-function approximation used by the ISCWSA separation rule (
IscwsaClearance).Subclasses the lightweight
Clearancebase (NOT IscwsaClearance) so it does not pay for the pedal-curve separation-factor minimisation it does not use; it needs only the reference/offset surveys, their covariances and hole radii.The separation rule measures the combined ellipsoid’s extent toward the offset with its support function
sqrt(uT.Sigma.u)(the tangent distance), which always over-states the ellipsoid’s reach in an off-axis direction and so is conservative. This class instead uses the true ellipsoid-surface distance — the Mahalanobis distance of the radii-adjusted centre-to-centre vector in the combined covariance metric:- SF = min over both curves of
sqrt(d’T (Sigma_ref + Sigma_off + sigma_pa^2 I)^-1 d’) / k
where
d'is the centre-to-centre vector shortened by the combined hole radii and surface marginSm, andsigma_pa^2 Iis the isotropic project-ahead floor (mirroring the separation rule’ssigma_paterm), which keeps the metric finite where a survey covariance is degenerate. The minimum is found over both wells by broadphase (all-pairs at the surveys’ own stations) plus a continuous narrowphase refinement, so the worst point between stations is captured without an externally imposed step.SF < 1means the offset lies within the k-sigma combined ellipsoid (collision). It is a k-sigma geometric boundary method (not a probability of collision), fast and analytic — no mesh or collision library required.This is the Mahalanobis distance of Brooks (SPE-116155, 2008; after Alfano’s satellite-conjunction work); see papers/anti-collision- conservatism.md for the derivation, validation and references. If you use this method, please cite welleng (doi:10.5281/zenodo.20968887) and that paper. Author: Jonathan Corcutt, Corcutt Beheer B.V. (ORCID 0009-0008-1953-7760).
- Parameters:
reference (welleng.survey.Survey) – The two wells (with an error model so
cov_nevis populated).offset (welleng.survey.Survey) – The two wells (with an error model so
cov_nevis populated).k (float, default 3.5) – Confidence multiple (inherited from
Clearance).Sm (float, default 0.3) – Surface margin, m (inherited).
sigma_pa (float, default 0.5) – Isotropic project-ahead floor, m (inherited).
sigma_pa > 0guarantees a positive-definite combined covariance; set0for the pure combined-ellipsoid metric (e.g. when reproducing Brooks).kop_depth (float, default -inf) – Kick-off depth below which to scan (inherited; for sidetracks).
n_candidates (int, default 8) – Number of globally-lowest broadphase stations polished by the narrowphase, IN ADDITION to every local minimum of the broadphase profile (so a sharp crossing is refined whatever its rank). For the ISCWSA standard set the result already converges with a single candidate; this is defensive headroom, not a tuned value, and remains a heuristic rather than a guarantee.
tol (float, default 1e-3) – Narrowphase convergence tolerance on the curve parameter (measured depth), m.
- sf
Separation factor at each reference station;
min(sf) < 1is a collision.- Type:
numpy.ndarray
- min_sf
The governing (minimum) separation factor over all stations.
- Type:
float
- property min_sf
The governing (minimum) separation factor;
< 1is a collision.
- class welleng.clearance.MeshClearance(*clearance_args, n_verts: int = 12, sigma: float = 2.445, return_data: bool = True, return_meshes: bool = False, polygon_fit: str = 'circumscribed', **clearance_kwargs)[source]
Bases:
ClearanceClass to calculate the clearance between two well bores using a novel mesh clearance method. This method is experimental and was developed to provide a fast method for determining if well bores are potentially colliding.
This class requires that trimesh is installed along with python-fcl.
- Parameters:
n_verts (int) – The number of points (vertices) used to generate the uncertainty ellipses which are used to generate a trimesh representation of the well bores. The default is 12 which is a good balance between accuracy and speed.
sigma (float) – The required/desired sigma value representation of the generated mesh. The default value of 2.445 represents about 98.5% confidence of the well bore being located within the volume of the generated mesh.
- welleng.clearance.combined_cov_mesh(survey, other, k=2.445, n_verts=12, Sm=0.0, polygon_fit='circumscribed')[source]
Build a trimesh of
survey’s uncertainty tube carrying the COMBINED relative-position covarianceSigma_survey + Sigma_other.A collision check between two uncertain wells should use the combined (relative-position) uncertainty
Sigma_ref + Sigma_off(the variance ofP_off - P_ref), which a single ellipsoid represents exactly. Building a separate k-sigma mesh for each well and testing surface overlap instead sums their extents linearly (k(sigma_ref + sigma_off)) rather than in quadrature (k*sqrt(sigma_ref^2 + sigma_off^2)), which over-states the required standoff by up to sqrt(2) (symmetric case) and raises false collision alarms.This builds ONE mesh —
surveyinflated by the combined covariance and the combined hole radii (survey.radius + other.radius + Sm) — intended to be tested against the centreline ofother(e.g. viamesh.contains(other.pos_nev)or atrimeshCollisionManageragainst a zero-uncertainty tube). It is the mesh/visualisation counterpart ofMahalanobisClearanceand is consistent with it (same RSS combined covariance), but not numerically identical: it omits the project-ahead floorsigma_pa, mapsother’s covariance by nearest Euclidean station, and discretises the surface inton_vertsfacets. For the exact pairwise separation factor useMahalanobisClearance; use this where a triangulated surface is needed (visualisation or a multi-well collision-manager scene).- Parameters:
survey (welleng.survey.Survey) – The two wells.
other’s covariance and hole radius are mapped ontosurveyby nearest position and folded into the returned mesh.other (welleng.survey.Survey) – The two wells.
other’s covariance and hole radius are mapped ontosurveyby nearest position and folded into the returned mesh.k (float) – The sigma multiple defining the uncertainty surface (default 2.445).
n_verts – Passed through to
welleng.mesh.WellMesh(polygon_fitdefaults to “circumscribed” so the polygon never under-counts the ellipse).polygon_fit – Passed through to
welleng.mesh.WellMesh(polygon_fitdefaults to “circumscribed” so the polygon never under-counts the ellipse).Sm – Passed through to
welleng.mesh.WellMesh(polygon_fitdefaults to “circumscribed” so the polygon never under-counts the ellipse).
- Returns:
survey’s combined-covariance uncertainty tube.- Return type:
trimesh.Trimesh
Notes
The combination is pairwise (it depends on
other), so a candidate checked against N offsets needs N combined meshes; the other well is taken as a deterministic centreline.
welleng.connector module
Wellbore trajectory connector.
Resolves a minimum-curvature connection between two stations (each a position
and/or a direction), classifying it into the appropriate type — a straight
hold, a single curve (min-curvature), a curve-hold, or a curve-hold-curve
(the circle-line-circle, CLC, point-to-target case) — and returns the arc/hold
sections, doglegs and measured depths. The curve-hold-curve case is solved in
closed form via Sawaryn (2021, SPE-204111-PA) — see welleng.sawaryn_analytical.
- class welleng.connector.Connector(node1=None, node2=None, pos1=[0.0, 0.0, 0.0], vec1=None, inc1=None, azi1=None, md1=0, dls_design=3.0, dls_design2=None, md2=None, pos2=None, vec2=None, inc2=None, azi2=None, degrees=True, unit='meters', min_error=1e-05, delta_dls=0.1, min_tangent=0.0, max_iterations=1000, force_min_curve=False, closest_approach=False, on_infeasible='raise')[source]
Bases:
objectSolves minimum-MD wellbore trajectories between two survey stations.
Automatically selects the appropriate geometric method (hold, curve-hold, min-curve, or curve-hold-curve) based on the provided start/end constraints and computes control points for the connecting path segment. The solver honours a maximum dog-leg severity (DLS) constraint where geometrically feasible.
- method
The geometric method used (‘hold’, ‘min_curve’, ‘curve_hold_curve’, ‘min_dist_to_target’, or ‘min_curve_to_target’).
- Type:
str
- pos1
Start position in NEV coordinates.
- Type:
ndarray of shape (3,)
- vec1
Unit direction vector at the start position.
- Type:
ndarray of shape (3,)
- inc1
Inclination at the start position (radians).
- Type:
float
- azi1
Azimuth at the start position (radians).
- Type:
float
- md1
Measured depth at the start position.
- Type:
float
- pos2
Position at the end of the first arc section in NEV coordinates. Equal to vec2 direction at this point.
- Type:
ndarray of shape (3,) or None
- vec2
Unit direction vector at the end of the first arc. Equals vec3 for curve-hold-curve solutions.
- Type:
ndarray of shape (3,) or None
- inc2
Inclination at the end of the first arc (radians).
- Type:
float or None
- azi2
Azimuth at the end of the first arc (radians).
- Type:
float or None
- md2
Measured depth at the end of the first arc.
- Type:
float or None
- pos3
Position at the start of the second arc (end of the hold section) in NEV coordinates. Only set for curve-hold-curve solutions.
- Type:
ndarray of shape (3,) or None
- vec3
Unit direction vector at the start of the second arc. Only set for curve-hold-curve solutions.
- Type:
ndarray of shape (3,) or None
- inc3
Inclination at the start of the second arc (radians).
- Type:
float or None
- azi3
Azimuth at the start of the second arc (radians).
- Type:
float or None
- md3
Measured depth at the start of the second arc.
- Type:
float or None
- md_target
Measured depth at the target position.
- Type:
float
- pos_target
Target position in NEV coordinates.
- Type:
ndarray of shape (3,)
- vec_target
Target unit direction vector in NEV coordinates.
- Type:
ndarray of shape (3,)
- inc_target
Target inclination (radians).
- Type:
float
- azi_target
Target azimuth (radians).
- Type:
float
- dogleg
Dogleg angle of the first arc (radians).
- Type:
float
- dogleg2
Dogleg angle of the second arc (radians). Only set for curve-hold-curve solutions.
- Type:
float or None
- dist_curve
Arc length of the first curve section.
- Type:
float
- dist_curve2
Arc length of the second curve section.
- Type:
float
- tangent_length
Length of the hold (tangent) section between the two arcs.
- Type:
float or None
- dls
Dogleg severity of the first arc (radians per unit length).
- Type:
float
- dls2
Dogleg severity of the second arc (radians per unit length).
- Type:
float
- dls_design
Design DLS constraint for the first arc (radians per unit length).
- Type:
float
- dls_design2
Design DLS constraint for the second arc (radians per unit length).
- Type:
float
- radius_design
Design turn radius derived from dls_design.
- Type:
float
- radius_design2
Design turn radius derived from dls_design2.
- Type:
float
- radius_critical
Critical (minimum geometric) radius for the first arc.
- Type:
float
- radius_critical2
Critical radius for the second arc.
- Type:
float
- __init__(node1=None, node2=None, pos1=[0.0, 0.0, 0.0], vec1=None, inc1=None, azi1=None, md1=0, dls_design=3.0, dls_design2=None, md2=None, pos2=None, vec2=None, inc2=None, azi2=None, degrees=True, unit='meters', min_error=1e-05, delta_dls=0.1, min_tangent=0.0, max_iterations=1000, force_min_curve=False, closest_approach=False, on_infeasible='raise')[source]
Initializes the Connector and solves the trajectory.
Only specific combinations of input data are permitted. For example, providing both a start vector and start inc/azi raises an error. The solver determines the appropriate method from the provided parameters and computes the connecting path immediately.
- Parameters:
node1 (Node or None) – Start Node. Overrides pos1, vec1, md1 if provided.
node2 (Node or None) – End Node. Overrides pos2, vec2, md2 if provided.
pos1 (list or ndarray) – Start position as [n, e, v] in NEV coordinates.
vec1 (list or ndarray or None) – Start unit direction vector in NEV coordinates.
inc1 (float or None) – Start inclination angle.
azi1 (float or None) – Start azimuth angle.
md1 (float) – Start measured depth.
dls_design (float) – Design DLS for the first curve section in deg/30m (meters) or deg/100ft (feet).
dls_design2 (float or None) – Design DLS for the second curve section. Defaults to dls_design if None.
md2 (float or None) – Target measured depth. Mutually exclusive with pos2.
pos2 (list or ndarray or None) – Target position in NEV coordinates.
vec2 (list or ndarray or None) – Target unit direction vector in NEV coordinates. Mutually exclusive with inc2/azi2.
inc2 (float or None) – Target inclination angle.
azi2 (float or None) – Target azimuth angle.
degrees (bool) – If True, angles are in degrees; if False, radians.
unit (str) – Distance unit, either ‘meters’ or ‘feet’.
min_error (float) – Error tolerance for iterative convergence. Must be less than 1.
delta_dls (float) – DLS tolerance (deg/30m) for balancing curve sections in curve-hold-curve solutions. Deprecated: accepted for backwards compatibility but unused by the analytic CHC path.
min_tangent (float) – Minimum tangent length to stabilize curve-hold-curve iteration. Deprecated: accepted for backwards compatibility but unused by the analytic CHC path.
max_iterations (int) – Maximum iteration count for curve-hold-curve fitting. Deprecated: accepted for backwards compatibility but unused by the analytic CHC path.
force_min_curve (bool) – If True, forces minimum-curvature method.
closest_approach (bool) – If True, finds the closest-approach trajectory when the target is inside the critical radius.
on_infeasible (str) – Behaviour when no curve-hold-curve solution exists at the design radii.
'raise'(default) raisesValueError.'max_radius'falls back to the gentlest feasible curve — the beta=0 biarc at the largest radius admitting a valid CLC (seewelleng.sawaryn_analytical.max_radius()) — and emits aUserWarningthat the design DLS is exceeded.
- Raises:
AssertionError – If input parameter combinations are invalid.
- welleng.connector.check_dogleg(dogleg)[source]
Ensures the dogleg angle is positive by wrapping negative values.
Accepts scalar or array-like; output shape matches input.
- Parameters:
dogleg (float or array_like) – Dogleg angle(s) in radians.
- Returns:
The dogleg angle(s) normalized to [0, 2*pi).
- Return type:
float or ndarray
- welleng.connector.connect_points(cartesians, vec_start=[0.0, 0.0, 1.0], dls_design=3.0, nev=True, md_start=0.0)[source]
Connects a sequence of Cartesian points with Connector sections.
- Parameters:
cartesians (list or ndarray) – Array of shape (n, 3) with positions as [n, e, tvd] (if nev=True) or [x, y, z] (if nev=False).
vec_start (list or ndarray) – Unit start direction vector in the corresponding coordinate system.
dls_design (float or list) – Design DLS in deg/30m (or deg/100ft). Can be a scalar or array of length n.
nev (bool) – If True, cartesians are in NEV coordinates; if False, XYZ.
md_start (float) – Measured depth at the first point.
- Returns:
A list of Connector objects linking consecutive points.
- Return type:
list
- welleng.connector.convert_target_input_to_booleans(*inputs)[source]
Converts target parameters to a binary string for method lookup.
- Parameters:
*inputs – Variable number of target parameters (md2, inc2, azi2, pos2, vec2). Each is mapped to ‘1’ if not None, ‘0’ otherwise.
- Returns:
A 5-character binary string encoding which parameters were provided.
- Return type:
str
- welleng.connector.drop_off(target_inc: float, dls: float, delta_md: float | None = None, node: Node | None = None, tol: float = 1e-05) list[source]
Computes trajectory sections to drop off (or build) to a target inclination.
Use
extend_to_tvdif a specific TVD target is also required.- Parameters:
target_inc (float) – Target inclination in degrees.
dls (float) – Design DLS in deg/30m.
delta_md (float or None) – Maximum section length in meters. If None, the section is unconstrained.
node (Node or None) – Starting Node. Defaults to surface pointing down.
tol (float) – Tolerance for tangent section length; sections shorter than this are omitted.
- Returns:
A list of Nodes describing the trajectory. Contains one Node (the arc endpoint) or two (arc endpoint plus tangent endpoint) if the target inclination was achieved within the section.
- Return type:
list
- welleng.connector.extend_to_tvd(target_tvd: float, node: Node | None = None, delta_md: float | None = None, target_inc: float | None = None, dls: float | None = None) list[source]
Computes Connector sections to reach a target TVD with optional inclination change.
- Parameters:
target_tvd (float) – Target true vertical depth in meters.
node (Node or None) – Starting Node. Defaults to surface pointing down.
delta_md (float or None) – Maximum section length in meters. If None, unconstrained.
target_inc (float or None) – Target inclination in degrees at the target TVD. If provided, the solver attempts to achieve this inclination and holds tangent to the target TVD.
dls (float or None) – Design DLS in deg/30m. Defaults to 2.5 if None and target_inc is provided.
- Returns:
A list of Connector objects. Contains one Connector (curve only) or two (curve plus tangent hold) if the target inclination was achieved within the section.
- Return type:
list
Examples
A well at 30 degrees inclination dropping to vertical:
>>> import welleng as we >>> node = we.node.Node(pos=[0, 0, 3000], md=4000, inc=30, azi=135) >>> connectors = we.connector.extend_to_tvd( ... target_tvd=3200, node=node, target_inc=0, dls=3 ... )
- welleng.connector.get_curve_hold_data(radius, dogleg)[source]
Computes arc length and shape factor for a curve section.
- Parameters:
radius (float) – Radius of curvature.
dogleg (float) – Dogleg angle in radians.
- Returns:
A tuple of (dist_curve, func_dogleg) where dist_curve is the arc length and func_dogleg is the minimum-curvature shape factor.
- Return type:
tuple
- welleng.connector.get_interpolate_hold(section, step=30, data=None)[source]
Interpolates a hold-method Connector section.
- Parameters:
section (Connector) – A Connector object with method ‘hold’.
step (float) – Desired delta measured depth between interpolated points.
data (list or None) – Optional list to append results to.
- Returns:
A list of interpolated survey data dictionaries.
- Return type:
list
- welleng.connector.get_interpolate_min_curve_to_target(section, step=30, data=None)[source]
Interpolates a min-curve-to-target Connector section.
- Parameters:
section (Connector) – A Connector object with method ‘min_curve_to_target’.
step (float) – Desired delta measured depth between interpolated points.
data (list or None) – Optional list to append results to.
- Returns:
A list of interpolated survey data dictionaries.
- Return type:
list
- welleng.connector.get_interpolate_min_dist_to_target(section, step=30, data=None)[source]
Interpolates a min-dist-to-target Connector section (curve + hold).
- Parameters:
section (Connector) – A Connector object with method ‘min_dist_to_target’.
step (float) – Desired delta measured depth between interpolated points.
data (list or None) – Optional list to append results to.
- Returns:
A list of interpolated survey data dictionaries.
- Return type:
list
- welleng.connector.get_interpololate_curve_hold_curve(section, step=30, data=None)[source]
Interpolates a curve-hold-curve Connector section.
- Parameters:
section (Connector) – A Connector object with method ‘curve_hold_curve’.
step (float) – Desired delta measured depth between interpolated points.
data (list or None) – Optional list to append results to.
- Returns:
A list of interpolated survey data dictionaries.
- Return type:
list
- welleng.connector.get_min_curve(section, step=30, data=None)[source]
Interpolates a minimum-curve section, dispatching by sub-method.
- Parameters:
section (Connector) – A Connector object with method ‘min_curve’.
step (float) – Desired delta measured depth between interpolated points.
data (list or None) – Optional list to append results to.
- Returns:
A list of interpolated survey data dictionaries.
- Return type:
list
- welleng.connector.get_pos(pos1, vec1, vec2, dist_curve, func_dogleg)[source]
Computes the end position of a minimum-curvature arc.
- Parameters:
pos1 (ndarray) – Start position in NEV coordinates.
vec1 (ndarray) – Start unit direction vector in NEV coordinates.
vec2 (ndarray) – End unit direction vector in NEV coordinates.
dist_curve (float) – Arc length of the curve section.
func_dogleg (float) – Shape factor (ratio factor) for the curve.
- Returns:
End position in NEV coordinates.
- Return type:
ndarray
- welleng.connector.get_radius_critical(radius, distances, min_error)[source]
Computes the critical radius for a given target geometry.
The critical radius is the minimum curvature radius needed to reach the target with a pure curve (no tangent). Below this radius, a curve-hold path is possible; above it, minimum curvature is needed.
- Parameters:
radius (float) – Design radius of curvature.
distances (tuple) – Tuple of (dist_to_target, dist_perp_to_target, dist_norm_to_target) geometric distances.
min_error (float) – Error tolerance factor applied to the result.
- Returns:
The critical radius. Returns 0 if the normal distance is zero.
- Return type:
float
- welleng.connector.get_vec_target(pos1, vec1, pos_target, tangent_length, dist_curve, func_dogleg)[source]
Derives the target unit vector from curve geometry and target position.
Solves for the direction vector at the end of a curve-hold section given the start state, curve parameters, and target position. Accepts either scalar inputs (legacy shape-(3,) positions/vectors with scalar tangent_length/dist_curve/func_dogleg) or batched inputs (leading batch dims on all arrays, positions/vectors with trailing axis 3).
- Parameters:
pos1 (ndarray, shape (..., 3)) – Start position in NEV coordinates.
vec1 (ndarray, shape (..., 3)) – Start unit direction vector in NEV coordinates.
pos_target (ndarray, shape (..., 3)) – Target position in NEV coordinates.
tangent_length (float or ndarray, shape (...)) – Length of the tangent (hold) section.
dist_curve (float or ndarray, shape (...)) – Arc length of the curve section. Where equal to zero, the input
vec1is returned unchanged (pure-hold fallback).func_dogleg (float or ndarray, shape (...)) – Shape factor (ratio factor) for the curve.
- Returns:
Target unit direction vector in NEV coordinates.
- Return type:
ndarray, shape (…, 3)
- welleng.connector.interpolate_curve(md1, pos1, vec1, vec2, dist_curve, dogleg, func_dogleg, step, endpoint=False)[source]
Interpolates survey points along a curve section at regular MD intervals.
Uses Rodrigues’ rotation formula for numerical stability, especially for near-180-degree doglegs where SLERP becomes unstable.
- Parameters:
md1 (float) – Measured depth at the start of the curve.
pos1 (ndarray) – Start position in NEV coordinates.
vec1 (ndarray) – Start unit direction vector in NEV coordinates.
vec2 (ndarray) – End unit direction vector in NEV coordinates.
dist_curve (float) – Arc length of the curve section.
dogleg (float) – Total dogleg angle in radians.
func_dogleg (float) – Shape factor (ratio factor) for the curve.
step (float) – Desired delta measured depth between interpolated points.
endpoint (bool) – If True, includes the curve endpoint in the output.
- Returns:
Dictionary with keys ‘md’, ‘vec’, ‘inc’, ‘azi’, ‘dogleg’ containing numpy arrays of interpolated survey data.
- Return type:
dict
- welleng.connector.interpolate_hold(md1, pos1, vec1, md2, step, endpoint=False)[source]
Interpolates survey points along a hold (tangent) section.
- Parameters:
md1 (float) – Measured depth at the start of the hold.
pos1 (ndarray) – Start position in NEV coordinates.
vec1 (ndarray) – Constant unit direction vector during the hold.
md2 (float) – Measured depth at the end of the hold.
step (float) – Desired delta measured depth between interpolated points.
endpoint (bool) – If True, includes the hold endpoint in the output.
- Returns:
Dictionary with keys ‘md’, ‘vec’, ‘inc’, ‘azi’, ‘dogleg’ containing numpy arrays of interpolated survey data.
- Return type:
dict
- welleng.connector.interpolate_well(sections, step=30)[source]
Constructs interpolated survey data from a list of Connector sections.
- welleng.connector.min_curve_to_target(distances)[source]
Computes minimum-curvature parameters when the design DLS is insufficient.
Used when the target cannot be reached with the design radius, so the curve section uses the minimum radius geometrically required.
- Parameters:
distances (tuple) – Tuple of (dist_to_target, dist_perp_to_target, dist_norm_to_target) geometric distances.
- Returns:
tangent_length (float) – Always 0 (pure curve, no hold).
radius_critical (float) – Minimum required radius of curvature.
dogleg (float) – Curve angle in radians.
- welleng.connector.min_dist_to_target(radius, distances)[source]
Computes tangent length and dogleg for a curve-hold section to a target.
- Parameters:
radius (float) – Radius of curvature for the curve section.
distances (tuple) – Tuple of (dist_to_target, dist_perp_to_target, dist_norm_to_target) geometric distances.
- Returns:
tangent_length (float) – Hold section length.
dogleg (float) – Curve angle in radians.
- welleng.connector.mod_vec(vec, error=1e-05)[source]
Slightly perturbs a direction vector to avoid exact antiparallel degeneracy.
- Parameters:
vec (ndarray) – Unit direction vector in NEV coordinates.
error (float) – Perturbation magnitude applied to the vertical component.
- Returns:
A tuple of (perturbed_vec, inclination, azimuth).
- Return type:
tuple
- welleng.connector.shape_factor(dogleg)[source]
Computes the minimum-curvature shape factor for a dogleg angle.
- Parameters:
dogleg (float) – Dogleg angle in radians.
- Returns:
The ratio factor (shape factor) for minimum-curvature interpolation.
- Return type:
float
- welleng.connector.solve_curve_hold_batch(pos1, vec1, pos_target, radius)[source]
Vectorised curve-hold connector: fixed start pose, fixed target pos.
Solves the minimum-MD curve-then-hold geometry from a start pose
(pos1, vec1)to a target positionpos_targetwith a given design radius. The target tangent vector is an OUTPUT of the solve — computed analytically from the geometry — not an input. Equivalent toConnector(pos1=..., vec1=..., pos2=pos_target, dls_design=...)in the'curve_hold'mode (binary code00110in_get_initial_methods), but operates element-wise on arrays so a large sweep is one numpy call rather than a Python loop overConnectorinstances.- Parameters:
pos1 (array_like, shape (..., 3)) – Start positions in NEV coordinates. Arbitrary leading batch shape.
vec1 (array_like, shape (..., 3)) – Unit direction vectors at the start. Must share
pos1’s leading shape.pos_target (array_like, shape (..., 3)) – Target positions in NEV coordinates. Must share
pos1’s leading shape.radius (float or array_like, shape (...)) – Design radius of curvature. Broadcasts against the leading shape.
- Returns:
All entries are ndarrays whose leading shape matches the inputs.
'pos2'shape (…, 3) — end of the curve / start of the hold.'vec_target'shape (…, 3) — computed unit tangent at target.'tangent_length'shape (…) — hold-section length.'dogleg'shape (…) — curve angle, radians.'dist_curve'shape (…) — arc length of the curve section.'md'shape (…) — total measured depth (curve + hold).
- Return type:
dict
Notes
When the target is exactly along
vec1(pure-hold degenerate case), the solver returnsdogleg = 0,tangent_length = dist_to_target,vec_target = vec1, andpos2 = pos1. This matches the scalarConnectorbehaviour in that regime.The underlying helpers (
min_dist_to_target,get_curve_hold_data,get_vec_target) have all been array-safe since the vectorisation patch; this function is just a thin wrapper that computes the three intermediate distance scalars and composes the helpers.
- welleng.connector.survey_to_plan(survey, tolerance=0.2, dls_design=1.0, step=30.0)[source]
Extracts a minimal well plan from a drilled survey.
Identifies the minimum number of control points (start/end of hold or build/turn sections) needed to reproduce the survey trajectory within the given tolerance.
- Parameters:
survey (Survey) – A welleng Survey object representing the drilled well.
tolerance (float) – Fit tolerance. Higher values produce fewer control points but a looser fit.
dls_design (float) – Minimum design DLS in deg/30m for the planned trajectory.
step (float) – Desired MD step interval for the output survey.
- Returns:
A list of Connector objects representing the planned sections.
- Return type:
list
- Raises:
AssertionError – If dls_design is not greater than 0.
welleng.error module
ISCWSA error models for computing wellbore positional uncertainty.
The "ISCWSA MWD Rev5" string remains a selectable error model, but is now
a deprecated alias for the Rev 5.11 compliant implementation (“ISCWSA MWD
Rev5.11”). As of welleng 0.10.0 the Rev5 YAML and weight functions were
corrected against the ISCWSA Rev 5.11 example workbooks, so users who
previously selected "ISCWSA MWD Rev5" will get slightly different (and
correct) covariance output. "ISCWSA MWD Rev4" is unchanged.
- class welleng.error.ErrorModel(survey, error_model='ISCWSA MWD Rev5.11')[source]
Bases:
objectA class to initiate the field parameters and error magnitudes for subsequent error calculations.
- error_model
Name of the error model used (e.g.
'ISCWSA MWD Rev5.11', the current default;'ISCWSA MWD Rev4'for legacy Rev 4 behaviour).- Type:
str
- survey
The input Survey object.
- Type:
- errors
ToolError object containing per-source error magnitudes and covariance data.
- survey_rad
Array of (md, inc_rad, azi_true_rad) per station, shape (n, 3).
- Type:
numpy.ndarray
- drdp
Jacobian of position with respect to survey parameters (depth, inclination, azimuth) in NEV coordinates.
- Type:
numpy.ndarray
- cov_NEVs
Summed covariance matrices in NEV coordinates per station, shape (n, 3, 3). Accessible via
errors.cov_NEVs.- Type:
numpy.ndarray
- class Error(code, propagation, e_DIA, cov_DIA, e_NEV, e_NEV_star, sigma_e_NEV, cov_NEV)[source]
Bases:
objectStandard components of a well bore survey error.
- __init__(code, propagation, e_DIA, cov_DIA, e_NEV, e_NEV_star, sigma_e_NEV, cov_NEV)[source]
Initialize an Error with computed error vectors and covariances.
- Parameters:
code (str) – The error source code identifier.
propagation (str) – Propagation type (‘systematic’, ‘random’, ‘global’, or ‘within_pad’).
e_DIA (numpy.ndarray) – Error vectors in Depth-Inclination-Azimuth coordinates.
cov_DIA (numpy.ndarray) – Covariance matrices in DIA coordinates.
e_NEV (numpy.ndarray) – Error vectors in North-East-Vertical coordinates.
e_NEV_star (numpy.ndarray) – Single-station NEV error vectors.
sigma_e_NEV (numpy.ndarray) – Cumulative NEV error vectors.
cov_NEV (numpy.ndarray) – Covariance matrices in NEV coordinates.
- __init__(survey, error_model='ISCWSA MWD Rev5.11')[source]
Initialize the error model for a given survey.
- Parameters:
survey (welleng.survey.Survey) – The survey to compute errors for.
error_model (str, optional) – Name of the error model to apply. Defaults to the Rev 5.11 compliant
"ISCWSA MWD Rev5.11". The legacy name"ISCWSA MWD Rev5"is accepted as a deprecated alias.
- drk_dAz(survey)[source]
Derivative of position with respect to azimuth at each station.
- Parameters:
survey (array_like) – Survey stations as (md, inc_rad, azi_rad) rows.
- Returns:
Shape (n, 3) array of NEV derivatives.
- Return type:
numpy.ndarray
- drk_dDepth(survey)[source]
Derivative of position with respect to measured depth at each station.
Equal to 0.5 * (unit_vec[i] + unit_vec[i+1]) in NEV coordinates – the direction-cosine part of minimum curvature without the RF or delta_md.
- Parameters:
survey (array_like) – Survey stations as (md, inc_rad, azi_rad) rows.
- Returns:
Shape (n, 3) array of NEV derivatives.
- Return type:
numpy.ndarray
- drk_dInc(survey)[source]
Derivative of position with respect to inclination at each station.
- Parameters:
survey (array_like) – Survey stations as (md, inc_rad, azi_rad) rows.
- Returns:
Shape (n, 3) array of NEV derivatives.
- Return type:
numpy.ndarray
- drkplus1_dAz(survey)[source]
Derivative of next-station position with respect to azimuth.
- Parameters:
survey (array_like) – Survey stations as (md, inc_rad, azi_rad) rows.
- Returns:
Shape (n, 3) array of NEV derivatives.
- Return type:
numpy.ndarray
- welleng.error.get_error_models(tool_index=None)[source]
Return a list of available error model short names.
- Parameters:
tool_index (dict, optional) – Pre-loaded tool index dict. If None, loads from disk.
- Returns:
Short names of all registered error models.
- Return type:
list of str
- welleng.error.get_errors(error)[source]
Extract the six unique covariance components from a 3x3 NEV matrix.
- Parameters:
error (numpy.ndarray) – A 3x3 covariance matrix in NEV coordinates.
- Returns:
[nn, ee, vv, ne, nv, ev] covariance components.
- Return type:
list
- welleng.error.get_tool_index()[source]
Load the tool error model index from the bundled YAML file.
- Returns:
Mapping of tool model names to their configuration parameters.
- Return type:
dict
- welleng.error.make_diagnostic_data(survey)[source]
Build a per-station diagnostic breakdown of all error model components.
- Parameters:
survey (welleng.survey.Survey) – A welleng Survey with an attached ErrorModel (survey.err).
- Returns:
Nested dict keyed by MD, then error code, containing the six unique covariance components and a TOTAL row summing all codes.
- Return type:
dict
welleng.fluid module
- class welleng.fluid.DensityDiesel[source]
Bases:
object
- class welleng.fluid.Fluid(fluid_density, reference_temp=32.0, reference_pressure=0.0, base_fluid_water_ratio=0.2, weighting_material='Barite')[source]
Bases:
object- __init__(fluid_density, reference_temp=32.0, reference_pressure=0.0, base_fluid_water_ratio=0.2, weighting_material='Barite')[source]
Density profile calculated from SPE 11118 Mathematical Field Model Predicts Downhold Density Changes in Static Drilling Fluids by Roland R. Sorelle et al.
This paper was written in oilfield units, so we’ll convert inputs to ppg, ft, F and psi.
- Parameters:
fluid_density (float) – The combined fluid density in ppg at reference conditions.
reference_temp (float (default 32.0)) – The reference temperature in Fahrenheit
reference_pressure (float (default 0.0)) – The reference pressure in psig.
weighting_material (str) – The material being used to weight the drilling fluid (see the WEIGHTING_MATERIAL_DENSITY dictionary).
- get_density_profile(depth, temperature, pressure_applied=0.0, density_bounds=(6.0, 25.0))[source]
Function that returns a density profile of the fluid, adjusted for temperature and compressibility and assuming that the fluid’s reference parameters are the surface parameters.
- Parameters:
depth (float or list or (n) array of floats) – The vertical depth of interest relative to surface in feet.
temperature (float or list or (n) array of floats) – The temperature corresponding to the vertical depth of interest in Fahrenheit.
pressure_applied (float (default=0.)) – Additional pressure applied to the fluid in psi.
density_bounds – Density bounds to constrain the optimization algorithm in ppg.
welleng.io module
welleng.mesh module
Wellbore mesh generation from survey data and positional uncertainty.
- class welleng.mesh.WellMesh(survey: Survey, n_verts: int = 12, sigma: float = 3.0, sigma_pa: float = 0.5, Sm: float = 0, method: str = 'ellipse', polygon_fit: str = 'circumscribed')[source]
Bases:
objectTriangular mesh representing a wellbore’s positional uncertainty envelope.
- s
The input Survey object.
- Type:
- vertices
Vertex positions array, shape (n_stations, n_verts, 3).
- Type:
numpy.ndarray
- faces
Triangle face index array, shape (n_faces, 3).
- Type:
numpy.ndarray
- mesh
Lightweight mesh container with
verticesandfacesattributes.- Type:
types.SimpleNamespace
- n_verts
Number of vertices per station cross-section.
- Type:
int
- sigma
Sigma multiplier for the uncertainty envelope.
- Type:
float
- radius
Wellbore radius at each station.
- Type:
numpy.ndarray
- nevs
Station positions in NEV coordinates, shape (n_stations, 3).
- Type:
numpy.ndarray
- __init__(survey: Survey, n_verts: int = 12, sigma: float = 3.0, sigma_pa: float = 0.5, Sm: float = 0, method: str = 'ellipse', polygon_fit: str = 'circumscribed')[source]
Create a WellMesh object from a welleng Survey object.
- Parameters:
survey (welleng.survey.Survey) – The survey from which to build the mesh.
n_verts (int, optional) – The number of vertices along the uncertainty ellipse edge from which to construct the mesh. Recommended minimum is 12 and that the number is a multiple of 4.
sigma (float, optional) – The desired standard deviation sigma value of the well bore uncertainty.
sigma_pa (float, optional) – The desired “project ahead” value. A remnant of the ISCWSA method but may be used in the future to accommodate for well bore curvature that is not captured by the mesh.
Sm (float, optional) – From the ISCWSA method, this is an additional factor applied to the well bore radius of the offset well to oversize the hole.
method (str, optional) – The method for constructing the uncertainty edge. Either “ellipse”, “pedal_curve” or “circle”.
polygon_fit (str, optional) – How the n_verts polygon approximates the uncertainty ellipse: “circumscribed” (default) scales the polygon out by 1 / cos(pi / n_verts) so its edges are tangent to and the polygon fully contains the ellipse — it never under-represents the uncertainty for the given sigma (the safety-conservative choice); “inscribed” places the vertices on the ellipse, which under-counts the uncertainty area between vertices. Only applies to the “ellipse”/”circle” methods.
- welleng.mesh.fix_mesh(mesh)[source]
Fix a non-watertight mesh by removing duplicate and degenerate faces, then repairing windings and normals.
- Parameters:
mesh (trimesh.Trimesh) – The mesh to repair.
- Returns:
A repaired mesh with correct windings and normals.
- Return type:
trimesh.Trimesh
- welleng.mesh.get_ends(n_verts, rows)[source]
Build cap faces for the first and last cross-section rings.
End-cap triangles fan from center vertices (the wellpath positions) appended after all ring vertices, rather than from circumference vertex 0.
- Parameters:
n_verts (int) – Number of vertices per cross-section ring.
rows (int) – Number of cross-section rings along the wellbore.
- Returns:
(top_faces, bottom_faces), each of shape (n_verts, 3).
- Return type:
tuple of numpy.ndarray
- welleng.mesh.get_faces(n_verts, rows)[source]
Build triangular face indices for a tubular mesh.
- Parameters:
n_verts (int) – Number of vertices per cross-section ring.
rows (int) – Number of cross-section rings along the wellbore.
- Returns:
Face index array of shape (n_faces, 3).
- Return type:
numpy.ndarray
- welleng.mesh.make_trimesh_scene(data)[source]
Construct a trimesh scene. A collision manager can’t be saved, but a scene can and a scene can be imported into a collision manager.
- Parameters:
data (list) – List of welleng.mesh.WellMesh objects.
- Returns:
A trimesh scene containing all well meshes.
- Return type:
trimesh.scene.scene.Scene
- welleng.mesh.sliced_mesh(survey, n_verts=12, sigma=3.0, sigma_pa=0.5, Sm=0, start=0, stop=-1, step=1, method='mesh_ellipse')[source]
Generates a list of mesh objects of a user defined length.
- Parameters:
survey (welleng.survey.Survey) – The survey from which to build the meshes.
n_verts (int, optional) – The number of vertices along the uncertainty ellipse edge from which to construct the mesh. Recommended minimum is 12 and that the number is a multiple of 4.
sigma (float, optional) – The desired standard deviation sigma value of the well bore uncertainty.
sigma_pa (float, optional) – The desired “project ahead” value. A remnant of the ISCWSA method but may be used in the future to accommodate for well bore curvature that is not captured by the mesh.
Sm (float, optional) – From the ISCWSA method, this is an additional factor applied to the well bore radius of the offset well to oversize the hole.
method (str, optional) – The method for constructing the uncertainty edge. Either “ellipse” or “pedal_curve”.
- Returns:
List of mesh namespace objects.
- Return type:
list
- welleng.mesh.to_trimesh(well_mesh)[source]
Convert a
WellMeshto atrimesh.Trimeshobject.This is the single point where trimesh is constructed from the stored geometry arrays. Call this explicitly wherever a true trimesh object is required (e.g. collision detection in
MeshClearance).- Parameters:
well_mesh (WellMesh)
- Return type:
trimesh.Trimesh
- welleng.mesh.transform_trimesh_scene(scene, origin=None, scale=100, redux=0.25)[source]
Transforms a scene by scaling it, reseting the origin/datum and performing a reduction in the number of triangles to reduce the file size.
- Parameters:
scene (trimesh.scene.scene.Scene) – A trimesh scene of well meshes.
origin (array_like, optional) – 3D array [x, y, z]. The origin of the scene from which the new scene will reset to [0, 0, 0].
scale (float, optional) – A scalar reduction will be performed using this float.
redux (float, optional) – The desired reduction ratio for the number of triangles in each mesh.
- Returns:
A transformed, scaled and reprocessed scene.
- Return type:
trimesh.scene.scene.Scene
welleng.node module
Wellbore survey node representing a position and direction in a well trajectory.
- class welleng.node.Node(pos: ArrayLike | None = None, vec: ArrayLike | None = None, md: float | None = None, inc: float | None = None, azi: float | None = None, unit: str = 'meters', degrees: bool = True, nev: bool = True, cov_nev: ndarray | None = None, **kwargs: Any)[source]
Bases:
objectA survey station in a wellbore trajectory.
Stores position, direction vector, measured depth, and covariance for a single point along a well path. Coordinates can be specified in either NEV (north-east-vertical) or XYZ convention.
- pos_nev
Position as [north, east, vertical].
- Type:
list
- pos_xyz
Position as [x, y, z].
- Type:
list
- vec_nev
Unit direction vector in NEV.
- Type:
list
- vec_xyz
Unit direction vector in XYZ.
- Type:
list
- inc_rad
Inclination in radians.
- Type:
float
- inc_deg
Inclination in degrees.
- Type:
float
- azi_rad
Azimuth in radians.
- Type:
float
- azi_deg
Azimuth in degrees.
- Type:
float
- md
Measured depth along the wellbore.
- Type:
float
- unit
Unit of measurement (default ‘meters’).
- Type:
str
- cov_nev
3x3 covariance matrix in NEV coordinates.
- Type:
ndarray
- __init__(pos: ArrayLike | None = None, vec: ArrayLike | None = None, md: float | None = None, inc: float | None = None, azi: float | None = None, unit: str = 'meters', degrees: bool = True, nev: bool = True, cov_nev: ndarray | None = None, **kwargs: Any) None[source]
Initialize a Node with position and direction.
- Parameters:
pos (array_like, optional) – Position as a 3-element array. Interpreted as NEV or XYZ depending on the
nevflag.vec (array_like, optional) – Unit direction vector (3-element). If provided,
incandaziare ignored.md (float, optional) – Measured depth along the wellbore.
inc (float, optional) – Inclination angle.
azi (float, optional) – Azimuth angle.
unit (str) – Length unit, default
'meters'.degrees (bool) – If True,
incandaziare in degrees.nev (bool) – If True,
posandvecare in NEV coordinates; otherwise XYZ.cov_nev (ndarray, optional) – Covariance matrix (1, 3, 3). Defaults to zeros.
**kwargs – Additional attributes set on the instance.
- azi_deg: float | None
- azi_rad: float | None
- check_angle_inputs(inc: float | None, azi: float | None, vec: ArrayLike | None, nev: bool, degrees: bool) None[source]
- cov_nev: ndarray
- inc_deg: float | None
- inc_rad: float | None
- md: float | None
- pos_nev: list | None
- pos_xyz: list | None
- properties() dict[source]
Return all instance attributes as a dictionary.
- Returns:
Mapping of attribute names to their values.
- Return type:
dict
- unit: str
- vec_nev: list | None
- vec_xyz: list | None
welleng.sawaryn_analytical module
Analytical 3D Curve-Line-Curve (CLC) point-to-target solver.
An open implementation of the closed-form point-to-target solution of Sawaryn (2021):
Sawaryn, S. J. (2021). “A Generalized Solution to the Point-to-Target Problem Using the Minimum Curvature Method.” SPE Drilling & Completion. DOI: 10.2118/204111-PA.
A CLC trajectory connects a kickoff station (position + unit tangent) to a
target station with two circular arcs (radii R1, R2) joined by a
straight tangent of length beta. The solution is parameterised by the
tangent length and the two subtended arc angles alpha1, alpha2.
The corrected Eq. 15, and a note on the printed one
The paper presents the solution two ways: the forward constraint equations
(Eqs. 11-13: eta1, eta4, eta14 as functions of alpha1, alpha2, beta) and the
eliminated implicit form (Eq. 15: a degree-10 polynomial in beta whose real
positive roots are every solution). The printed Eq. 15 is not scale-
covariant — it does not reproduce the paper’s own worked roots under length
normalisation, i.e. it carries a transcription error in the eliminated
polynomial (whose true expansion the paper notes is “~4000 terms, beyond human
capability”). The eq15 function below reproduces the printed form and is
retained only to document that trap (see test_eq15_is_trapped).
The correct degree-10 coefficients were re-derived by replicating Sawaryn’s own
surd-elimination (Appendix B: the half-angle quadratics B-15/B-16 and the
bilinear constraint B-19), eliminating the surds symbolically (_eq15_coeffs).
These power the vectorised closed-form solvers solve_clc (single pair) and
solve_clc_batch (batched, ~0.02 ms/solve via companion-matrix eigenvalues),
which return every CLC solution and reproduce Example 2 of SPE-204111-PA
exactly. Subtended angles are reported as the true dogleg in [0, 2*pi) so the
measured depth ranks solutions correctly. Planar (eta14 ~ 0) and parallel-
tangent (|mu| = 1) cases are handled by solve_clc_2d (the paper’s biquadratic
2D form). Forward-verified solvers (solve_clc_analytical scan,
solve_clc_resultant per-instance resultant) are provided as cross-checks.
This supersedes the iterative scheme of Sawaryn & Thorogood (2005, “A Compendium
of Directional Calculations Based on the Minimum Curvature Method”, SPE-84246-PA)
that welleng’s Connector inherits.
Citation
Use of this work requires citation. Cite Sawaryn (2021, SPE-204111-PA) for the underlying mathematics, and — for any use of this (welleng’s) implementation, its corrected coefficients, or its sweep/feasibility tooling — you must also cite welleng (software concept DOI 10.5281/zenodo.20968887) and the welleng analytical-CLC paper:
Corcutt, J. (2026). An Open, Vectorized Closed-Form Solver for the 3D Curve-Hold-Curve Point-to-Target Problem. Zenodo. DOI 10.5281/zenodo.21130979.
implements his last and most general solution to a problem he advanced for over four decades. —————————————————————————-
- welleng.sawaryn_analytical.eq15(beta, psi2, eta1, eta4, eta14, mu, R1, R2)[source]
Sawaryn Eq. 15 — the eliminated degree-10 polynomial. REFERENCE ONLY.
The printed form is NOT scale-covariant (it does not reproduce the paper’s own worked roots under length normalisation) — it carries a transcription/ print error in the eliminated polynomial.
solve_clc_analyticaldoes not use it; it forward-verifies via the clean Eqs. 11-13 + 18-25 instead. Kept here only to document the discrepancy.
- welleng.sawaryn_analytical.forward(alpha1, alpha2, beta, mu, R1, R2)[source]
Forward model (Eqs. 11-13): (eta1, eta4, eta14) from the path parameters.
Verified exact against SPE-204111-PA Example 2. Returns
Nonewhere the angular surd is negative (geometrically inconsistent).
- welleng.sawaryn_analytical.max_radius(p1, t1, p4, t4, ratio=1.0)[source]
Largest radius admitting a valid CLC — the gentlest feasible curve.
The point-to-target CLC is reachable with both arc doglegs
<= pionly up to a maximum radius; beyond it the target is reachable only by a> pi(loop) arc, which the minimum-curvature renderer cannot draw. That maximum is thebeta = 0(curve-curve / biarc) boundary, where the hold vanishes — equivalently the largest root of the constant coefficientc0of the corrected Eq. 15 whose biarc has both doglegs<= pi. This is the analytic form of the classical “critical radius”: the gentlest curvature that still reaches the target. A caller can fall back to it when no CLC exists at the design radii, instead of iterating the radius down.- Parameters:
p1 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
t1 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
p4 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
t4 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
ratio (float, default 1.0) –
R2 / R1.1.0is symmetric radii; otherwise the second radius scales with the first along this ratio.
- Returns:
radius(R1),radius2(R2),beta(0.0),alpha1,alpha2(biarc doglegs, radians) andtotal_md;Noneif no feasible biarc exists (the target is unreachable under thepiconstraint).- Return type:
dict or None
Notes
Closed-form condition of Sawaryn (2021, SPE-204111-PA); no iteration. Parallel tangents (
|mu| = 1) — where the general form is singular — are handled by a 2D feasibility bisection (solve_clc_2d()). Seesolve_clc()for the general (fixed-design-radius) solve.
- welleng.sawaryn_analytical.solve_clc(p1, t1, p4, t4, R1, R2=None, return_all=False)[source]
Solve the CLC point-to-target problem for a single station pair.
Main entry point. Runs the general closed-form solver first; degenerate pairs (parallel/antiparallel tangents
|mu| = 1, or planareta14 ~ 0) auto-fall back tosolve_clc_2d(), so the caller need not pre-classify.- Parameters:
p1 ((3,) array_like) – Kickoff position and unit tangent (N, E, V);
t1is a unit vector.t1 ((3,) array_like) – Kickoff position and unit tangent (N, E, V);
t1is a unit vector.p4 ((3,) array_like) – Target position and unit tangent.
t4 ((3,) array_like) – Target position and unit tangent.
R1 (float) – First / second arc radii.
R2defaults toR1(symmetric arcs). Unit-agnostic, so long as positions and radii share one length unit (e.g. all metres, or all feet).R2 (float) – First / second arc radii.
R2defaults toR1(symmetric arcs). Unit-agnostic, so long as positions and radii share one length unit (e.g. all metres, or all feet).return_all (bool, default False) – If False, return only the shortest (minimum measured-depth) solution. If True, return every valid CLC solution.
- Returns:
return_all=False: the shortest solution as a dict with keysbeta(tangent / hold length),alpha1,alpha2(arc doglegs, radians) andtotal_md(measured depth);Noneif no CLC exists.return_all=True: list of such dicts, shortest first.- Return type:
dict or list of dict or None
- welleng.sawaryn_analytical.solve_clc_2d(p1, t1, p4, t4, R1, R2=None, return_all=False)[source]
Planar / singular CLC solve (
eta14 ~ 0) — Sawaryn Eq. 34, biquadratic in beta.Covers the degenerate 2D case AND the parallel/antiparallel-tangent singularities
mu = +-1(where the general form’s1/(1-mu^2)blows up):eta14is identically 0 there, so this biquadratic subsumes Sawaryn’s Eqs 37/38. The+-is the two arc senses (Figs 10/11). Verification uses Eqs 11-12 only (the out-of-plane surd sits at 0 numerically here).return_all=False (default): the shortest solution dict, or None. return_all=True: list of all solution dicts, shortest first.
- welleng.sawaryn_analytical.solve_clc_analytical(p1, t1, p4, t4, R1, R2=None, n_scan=4000, tol=1e-06)[source]
Solve the CLC point-to-target problem (Sawaryn 2021), forward-verified.
- Parameters:
p1 ((3,) array — kickoff position and unit tangent (N, E, V);
t1is) – a unit vector, as tangents are throughout welleng (cf.Survey.vec_nev).t1 ((3,) array — kickoff position and unit tangent (N, E, V);
t1is) – a unit vector, as tangents are throughout welleng (cf.Survey.vec_nev).p4 ((3,) array — target position and unit tangent.)
t4 ((3,) array — target position and unit tangent.)
R1 (float — first/second arc radii (
R2defaults toR1, symmetric) – arcs). Unit-agnostic: any length unit, so long as positions and radii share it (e.g. all metres, or all feet). The returned lengths come back in that same unit; angles are radians.R2 (float — first/second arc radii (
R2defaults toR1, symmetric) – arcs). Unit-agnostic: any length unit, so long as positions and radii share it (e.g. all metres, or all feet). The returned lengths come back in that same unit; angles are radians.
- Returns:
list of dict, sorted by total measured depth, each with keys (driller’s
terms in brackets)
beta/line— straight tangent length (the hold section);alpha1,alpha2— subtended arc angles in radians (the dogleg of eachbuild/turn);
arc1,arc2— arc lengthsR*alpha(the buildsections);
total_md— total measured depth;residual. The build-planetoolface is not returned but is recoverable from the reconstructed tangent.
Complete (every real CLC solution is returned.)
- welleng.sawaryn_analytical.solve_clc_batch(P1, T1, P4, T4, R1, R2=None, return_all=False)[source]
Vectorised CLC point-to-target solve via the corrected Eq. 15 closed form.
Batched over N station pairs, no Python loop: invariants (einsum) -> Eq. 15 coefficients -> roots (batched companion eigvals) -> forward-verify. ~0.02 ms/solve, the only solver here that vectorises.
- Parameters:
P1 ((N, 3) — kickoff/target positions and unit tangents.)
T1 ((N, 3) — kickoff/target positions and unit tangents.)
P4 ((N, 3) — kickoff/target positions and unit tangents.)
T4 ((N, 3) — kickoff/target positions and unit tangents.)
R1 (float or (N,) — first/second arc radii (
R2defaults toR1).)R2 (float or (N,) — first/second arc radii (
R2defaults toR1).)return_all (bool, default False) – False -> the SHORTEST (min total-MD) solution per pair. True -> every valid CLC solution per pair.
- Returns:
dict of numpy arrays –
- return_all=False
beta, alpha1, alpha2, total_mdeach (N,), plus found(N,) bool (False where no CLC exists).- return_all=True
beta, alpha1, alpha2, total_mdeach (N, 10), plus valid(N, 10) bool.
- return_all=False
Assumes the general case
|mu| < 1,eta14 != 0; route degenerate(planar / parallel-tangent) pairs to
solve_clc_2d().
- welleng.sawaryn_analytical.solve_clc_landing(p1, t1, p0, t4, R1, R2=None, return_all=False)[source]
Land onto a LINE target: p4 = p0 + k*t4, solving for the scalar k.
The landing problem (Sawaryn 2021, Appendix C): the target is not a fixed point but any point on the line through
p0in directiont4; the free parameter is the along-line distancek, and the connection is a biarc (beta = 0). On that line the invariants collapse to low-order functions ofk(Eqs. C-8/C-13/C-17):eta1 = eps1 + mu*k,eta4 = eps4 + k,eta14 = eps14(constant),psi^2 = psi0^2 + 2*eps4*k + k^2, witheps* = (p0 - p1).<basis>. Substituting these into the biarc conditionc0 = 0(the constant coefficient of the corrected Eq. 15) gives a polynomial ink(Eq. 44) whose roots are the landing distances; this is solved numerically fork.- Parameters:
p1 ((3,) array_like) – Kickoff position and unit tangent (N, E, V).
t1 ((3,) array_like) – Kickoff position and unit tangent (N, E, V).
p0 ((3,) array_like) – The landing line:
p0is its anchor (thek = 0base point) andt4its unit direction. Namedp0(notp4) because the target pointp4 = p0 + k*t4is the solved output, not an input.t4 ((3,) array_like) – The landing line:
p0is its anchor (thek = 0base point) andt4its unit direction. Namedp0(notp4) because the target pointp4 = p0 + k*t4is the solved output, not an input.R1 (float) – Arc radii;
R2defaults toR1.R2 (float) – Arc radii;
R2defaults toR1.return_all (bool, default False) – False -> the shortest feasible landing (both biarc doglegs <= pi) as a dict, or
None. True -> every landing root, feasible-first then by MD.
- Returns:
Each dict:
k(along-line distance),p4(landing point),beta(0.0),alpha1,alpha2(biarc doglegs, radians),total_md.- Return type:
dict or list of dict or None
- welleng.sawaryn_analytical.solve_clc_r_grid(p1, t1, p4, t4, R1, R2=None, scale_min=0.75, scale_max=1.25, n_steps=11, r1_scales=None, r2_scales=None)[source]
Sweep
R1andR2independently — the min-MD CLC over a 2D grid.The general case of
solve_clc_r_sweep(): instead of scaling both radii together,R1andR2are swept on independent axes, giving the fullK1 x K2reachability picture (e.g. asymmetry can buy back feasibility a coupled sweep misses). No new mathematics — the closed form already solves asymmetric radii; this is one batchedsolve_clc_batch()call over the flattened grid (mu=1 rows routed to the 2D solver automatically).- Parameters:
p1 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
t1 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
p4 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
t4 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
R1 (float) – Design arc-1 radius.
R2defaults toR1.R2 (float, optional) – Design arc-2 radius.
scale_min (float, float, int) – Scale-factor range applied to BOTH axes when explicit scales are not given (default 0.75 .. 1.25, 11 steps; design 1.0 snapped in per axis).
scale_max (float, float, int) – Scale-factor range applied to BOTH axes when explicit scales are not given (default 0.75 .. 1.25, 11 steps; design 1.0 snapped in per axis).
n_steps (float, float, int) – Scale-factor range applied to BOTH axes when explicit scales are not given (default 0.75 .. 1.25, 11 steps; design 1.0 snapped in per axis).
r1_scales (array_like, optional) – Explicit per-axis scale factors (override
scale_min/max/n_steps).r2_scales (array_like, optional) – Explicit per-axis scale factors (override
scale_min/max/n_steps).
- Returns:
r1_scales(K1,),r2_scales(K2,),radius1(K1,),radius2(K2,);feasibleandbeta,alpha1,alpha2,total_mdeach (K1, K2) with axis 0 =R1and axis 1 =R2(NaN where infeasible);design_index= (i, j) of the(1.0, 1.0)cell.- Return type:
dict
- welleng.sawaryn_analytical.solve_clc_r_sweep(p1, t1, p4, t4, R1, R2=None, scale_min=0.75, scale_max=1.25, n_steps=11, scales=None, values=None)[source]
Sweep the design radius: the min-MD CLC over a range of radii, batched.
A convenience feature (not new mathematics): the fixed-radius solver of
solve_clc()evaluated over a range of radii in ONE vectorisedsolve_clc_batch()call (only the radii change). Both arc radii are scaled by a common factor, soR2/R1is preserved and the sweep is 1-D in the design radius. Per radius it returns the shortest solution whose two arc doglegs are both<= pi(the renderable one), or marks it infeasible; the feasible range is bounded above bymax_radius().The range is a set of scale factors on the design radii — as
(scale_min, scale_max, n_steps)(a linspace), or explicitscales, or explicit radiusvalues(converted to scales ofR1). The design radii (scale 1.0) are always included when the range brackets them (snap-to-1.0), so the sweep always contains the as-designed solution;design_indexlocates it. Unlike a per-instance solver there is no per-radius frame normalisation — the batch solves every radius in the world frame at once.Lets a caller trade dogleg severity against measured depth, or recover a feasible radius when the design DLS fails.
- Parameters:
p1 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
t1 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
p4 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
t4 ((3,) array_like) – Kickoff / target positions and unit tangents (N, E, V).
R1 (float) – Design arc-1 radius.
R2 (float, optional) – Design arc-2 radius; defaults to
R1.scale_min (float) – Range of scale factors applied to the design radii (default 0.75 .. 1.25, the typical +/-25% design spread).
scale_max (float) – Range of scale factors applied to the design radii (default 0.75 .. 1.25, the typical +/-25% design spread).
n_steps (int) – Number of scale steps, linspace endpoints inclusive (default 11).
scales (array_like, optional) – Explicit scale factors (overrides
scale_min/max/n_steps).values (array_like, optional) – Explicit
R1radius values (overridesscales; converted to scalesR/R1soR2tracks at the design ratio). Mutually exclusive withscales.
- Returns:
scale,radius1(R1*scale),radius2(R2*scale),design_index(int — the scale==1.0 row, or nearest),feasible(bool), andbeta,alpha1,alpha2,total_md(NaN where infeasible).R = 0collapses to the straight tangent (beta = total_md = chord). Parallel-tangent (|mu| = 1) rows — the planar vertical S — are routed tosolve_clc_2d()by the batch, so they are handled, not skipped.- Return type:
dict of ndarrays (one entry per swept radius)
- welleng.sawaryn_analytical.solve_clc_resultant(p1, t1, p4, t4, R1, R2=None)[source]
Complete CLC solve via per-instance resultant elimination.
Independent cross-check for the vectorised
solve_clc/solve_clc_batch(exercised in the test suite); not on welleng’s hot path.Eliminates the two half-angle tangents from Sawaryn’s clean forward equations (11-13) by exact-rational resultants -> a polynomial in beta whose real positive roots include EVERY CLC solution (complete by construction - unlike the scan-based
solve_clc_analytical, which can drop large-angle / min-MD roots). Spurious roots (from clearing the half-angle denominators and squaring Eq. 13) are removed by forward-verification.Fast (~90ms/solve; python-flint resultant + arbitrary-precision acb roots, scale-normalised so coefficients stay representable) and complete + deterministic. The rational inputs are truncated to ~5 digits (1e5): the forward-verification filter rejects any root that drifts, so the lower precision buys ~2.4x speed with no loss of completeness.
- welleng.sawaryn_analytical.subtended_angles(beta, psi2, eta1, eta4, eta14, mu, R1, R2)[source]
Candidate subtended arc angles (alpha1, alpha2), radians, for a
beta.Eqs. 18-25: each half-angle tangent
T = tan(alpha/2)solves a quadraticA T^2 + B T + C = 0. Returns the two branches per arc; forward- verification selects the physical one.
welleng.survey module
Well survey management, coordinate transforms, and trajectory analysis.
- class welleng.survey.SplitSurvey(survey)[source]
Bases:
objectSplit a survey into upper and lower station pairs for interval calculations.
Provides paired arrays of inclinations, azimuths, vectors, and doglegs for consecutive survey stations.
- class welleng.survey.Survey(md, inc, azi, n=None, e=None, tvd=None, x=None, y=None, z=None, vec=None, nev=True, header=None, radius=None, cov_nev=None, cov_hla=None, error_model=None, start_xyz=[0.0, 0.0, 0.0], start_nev=[0.0, 0.0, 0.0], start_cov_nev=None, deg=True, unit='meters', steering=None, **kwargs)[source]
Bases:
objectDirectional well survey with positions, vectors, errors, and trajectory properties.
Computes wellbore positions via minimum curvature, converts between azimuth reference systems (true/magnetic/grid), calculates dogleg severity, toolface, build/turn rates, and optionally propagates ISCWSA error model covariances.
- header
Survey metadata including location, datum, and reference information.
- Type:
- md
Measured depths along the wellbore.
- Type:
ndarray of shape (n,)
- inc_deg
Inclination angles in degrees.
- Type:
ndarray of shape (n,)
- inc_rad
Inclination angles in radians.
- Type:
ndarray of shape (n,)
- azi_grid_deg
Grid azimuth angles in degrees.
- Type:
ndarray of shape (n,)
- azi_grid_rad
Grid azimuth angles in radians.
- Type:
ndarray of shape (n,)
- azi_true_deg
True north azimuth angles in degrees.
- Type:
ndarray of shape (n,)
- azi_true_rad
True north azimuth angles in radians.
- Type:
ndarray of shape (n,)
- azi_mag_deg
Magnetic north azimuth angles in degrees.
- Type:
ndarray of shape (n,)
- azi_mag_rad
Magnetic north azimuth angles in radians.
- Type:
ndarray of shape (n,)
- pos_nev
Station positions in North-East-Vertical coordinates.
- Type:
ndarray of shape (n, 3)
- pos_xyz
Station positions in X-Y-Z coordinates.
- Type:
ndarray of shape (n, 3)
- vec_nev
Unit direction vectors in North-East-Vertical coordinates.
- Type:
ndarray of shape (n, 3)
- vec_xyz
Unit direction vectors in X-Y-Z coordinates.
- Type:
ndarray of shape (n, 3)
- n
Northing coordinates of each survey station.
- Type:
ndarray of shape (n,)
- e
Easting coordinates of each survey station.
- Type:
ndarray of shape (n,)
- tvd
True vertical depth of each survey station.
- Type:
ndarray of shape (n,)
- x
X coordinates of each survey station.
- Type:
ndarray of shape (n,)
- y
Y coordinates of each survey station.
- Type:
ndarray of shape (n,)
- z
Z coordinates (depth) of each survey station.
- Type:
ndarray of shape (n,)
- dogleg
Dogleg angles between successive stations in radians.
- Type:
ndarray of shape (n,)
- dls
Dogleg severity per 30 m (or 100 ft) interval.
- Type:
ndarray of shape (n,)
- delta_md
Measured depth intervals between successive stations.
- Type:
ndarray of shape (n,)
- rf
Ratio factors from minimum curvature calculation.
- Type:
ndarray of shape (n,)
- toolface
Toolface angles in radians at each station.
- Type:
ndarray of shape (n,)
- build_rate
Build rate (inclination change rate) per unit length.
- Type:
ndarray of shape (n,)
- turn_rate
Turn rate (azimuth change rate) per unit length.
- Type:
ndarray of shape (n,)
- curve_radius
Radius of curvature at each station.
- Type:
ndarray of shape (n,)
- radius
Wellbore radius at each station.
- Type:
ndarray of shape (n,)
- cov_nev
Covariance matrices in North-East-Vertical coordinates.
- Type:
ndarray of shape (n, 3, 3) or None
- cov_hla
Covariance matrices in High-Lateral-Along-hole coordinates.
- Type:
ndarray of shape (n, 3, 3) or None
- err
Error model results when an error model is applied.
- Type:
ErrorModel or None
- survey_deg
Survey data as [md, inc_deg, azi_grid_deg] columns.
- Type:
ndarray of shape (n, 3)
- survey_rad
Survey data as [md, inc_rad, azi_grid_rad] columns.
- Type:
ndarray of shape (n, 3)
- vertical_section
Vertical section lateral displacement if a VS azimuth is defined.
- Type:
ndarray of shape (n,) or None
- __init__(md, inc, azi, n=None, e=None, tvd=None, x=None, y=None, z=None, vec=None, nev=True, header=None, radius=None, cov_nev=None, cov_hla=None, error_model=None, start_xyz=[0.0, 0.0, 0.0], start_nev=[0.0, 0.0, 0.0], start_cov_nev=None, deg=True, unit='meters', steering=None, **kwargs)[source]
Initialize a welleng.Survey object. Calculations are performed in the azi_reference “grid” domain.
- Parameters:
md ((,n) list or array of floats) – List or array of well bore measured depths.
inc ((,n) list or array of floats) – List or array of well bore survey inclinations
azi ((,n) list or array of floats) – List or array of well bore survey azimuths
n ((,n) list or array of floats (default: None)) – List or array of well bore northings
e ((,n) list or array of floats (default: None)) – List or array of well bore eastings
tvd ((,n) list or array of floats (default: None)) – List or array of local well bore z coordinates, i.e. depth and usually relative to surface or mean sea level.
x ((,n) list or array of floats (default: None)) – List or array of local well bore x coordinates, which is usually aligned to the east direction.
y ((,n) list or array of floats (default: None)) – List or array of local well bore y coordinates, which is usually aligned to the north direction.
z ((,n) list or array of floats (default: None)) – List or array of well bore true vertical depths relative to the well surface datum (usually the drill floor elevation DFE, so not always identical to tvd).
vec ((n,3) list or array of (,3) floats (default: None)) – List or array of well bore unit vectors that describe the inclination and azimuth of the well relative to (x,y,z) coordinates.
header (SurveyHeader object (default: None)) – A SurveyHeader object with information about the well location and survey data. If left default then a SurveyHeader will be generated with the default properties assigned, but these may not be relevant and may result in incorrect data.
radius (float or (,n) list or array of floats (default: None)) – If a single float is specified, this value will be assigned to the entire well bore. If a list or array of floats is provided, these are the radii of the well bore. If None, a well bore radius of 12” or approximately 0.3 m is applied.
cov_nev ((n,3,3) list or array of floats (default: None)) – List or array of covariance matrices in the (n,e,v) coordinate system.
cov_hla ((n,3,3) list or array of floats (default: None)) – List or array of covariance matrices in the (h,l,a) well bore coordinate system (high side, lateral, along hole).
error_model (str (default: None)) – Name of the survey-tool error model used to compute the position covariance. Leave as None for no uncertainty calculation. The recommended/standard model is
"ISCWSA MWD Rev5.11"(the validated ISCWSA standard);"ISCWSA MWD Rev4"is the legacy model. The OWSG toolcode library ("MWD+SRGM",+SAG,+AX,+IFR, gyro stacks"GYRO-NS"/"GYRO-NS-CT"/"GYRO-MWD", …) is also selectable. List every available name withwelleng.error.get_error_models(); switch by passing a different name. Raises if the name is unrecognised.start_xyz ((,3) list or array of floats (default: [0,0,0])) – The start position of the well bore in (x,y,z) coordinates.
start_nev ((,3) list or array of floats (default: [0,0,0])) – The start position of the well bore in (n,e,v) coordinates.
start_cov_nev ((,3,3) list or array of floats (default: None)) – The covariance matrix for the start position of the well bore in (n,e,v) coordinates.
deg (boolean (default: True)) – Indicates whether the provided angles are in degrees (True), else radians (False).
unit (str (default: 'meters')) – Indicates whether the provided lengths and distances are in ‘meters’ or ‘feet’, which impacts the calculation of the dls (dog leg severity).
- Return type:
A welleng.survey.Survey object.
- curvature_rate()[source]
Rate of change of curvature
dκ/dsper station (rad per unit length²).The along-hole derivative of curvature
κ = dogleg / Δmd. With the torsion term it completes the 3D stiff-string contact force (SPE-105068-PA, Eq. 20: theEI·τ·dκ/dsbinormal term). Zero on a constant-curvature (constant-DLS) section.Discrete form (SPE-105068-PA, Eq. 18):
(κ_{j+1} − κ_j) / (s_{j+1} − s_j).- Returns:
dkappa_ds – Per-station
dκ/dsaligned toself.md; the final station (undefined forward difference) is 0.- Return type:
ndarray of shape (n,)
- directional_difficulty_index(**kwargs)[source]
Taken from IADC/SPE 59196 The Directional Difficulty Index - A New Approach to Performance Benchmarking by Alistair W. Oag et al.
- Returns:
data – The ddi for each survey station.
- Return type:
array of floats
- figure(type='scatter3d', **kwargs)[source]
Generate a plotly figure of the survey trajectory.
- Parameters:
type (str) – Plot type passed to
welleng.visual.figure.**kwargs – Additional keyword arguments forwarded to the plotting function.
- Returns:
A plotly figure object.
- Return type:
object
- get_error(error_model, return_error=False)[source]
Apply an error model and compute covariance matrices.
- Parameters:
error_model (str) – Name of the error model (e.g.
"ISCWSA_MWD").return_error (bool) – If True, return the ErrorModel object; otherwise return the Survey with updated covariances.
- Returns:
The ErrorModel object if
return_erroris True, otherwise the Survey instance with updated covariance attributes.- Return type:
ErrorModel or Survey
- Raises:
AssertionError – If
error_modelis not a recognized model name.
- get_nev_arr()[source]
Return survey positions as an (n, 3) array of [N, E, TVD].
- Returns:
Array of shape (n, 3) with northing, easting, and TVD columns.
- Return type:
ndarray
- get_vertical_section(vertical_section_azimuth, deg=True)[source]
Calculate the vertical section.
- Parameters:
vertical_section_azimuth (float) – The azimuth (relative to the reference azimuth defined in the survey header) along which to calculate the vertical section lateral displacement.
deg (boolean (default: True)) – Indicates whether the vertical section azimuth parameter is in degrees or radians (True or False respectively).
- Returns:
result
- Return type:
(n, 1) ndarray
- interpolate_md(md)[source]
Method to interpolate a position based on measured depth and return a node.
- Parameters:
md (float) – The measured depth of the point of interest.
- Returns:
node – A node with attributes describing the point at the provided measured depth.
- Return type:
we.node.Node object
Examples
>>> import welleng as we >>> survey = we.connector.interpolate_survey( ... survey=we.survey.Survey( ... md=[0, 500, 1000, 2000, 3000], ... inc=[0, 0, 30, 90, 90], ... azi=[0, 0, 45, 135, 180], ... ), ... step=30 ... ) >>> node = survey.interpolate_md(1234) >>> node.properties() { 'vec_nev': [0.07584209568113438, 0.5840332282889957, 0.8081789187902809], 'vec_xyz': [0.5840332282889957, 0.07584209568113438, 0.8081789187902809], 'inc_rad': 0.6297429542197106, 'azi_rad': 1.4416597719915565, 'inc_deg': 36.081613454889634, 'azi_deg': 82.60102042890875, 'pos_nev': [141.27728744087796, 201.41424652428694, 1175.5823295305202], 'pos_xyz': [201.41424652428694, 141.27728744087796, 1175.5823295305202], 'md': 1234.0, 'unit': 'meters', 'interpolated': True }
- interpolate_mds(md)[source]
Method to interpolate positions at an array of measured depths and return a new welleng.Survey object. This is a vectorized equivalent of looping the scalar interpolate_md, and produces a survey equivalent to interpolate_survey when passed the same station measured depths.
- Parameters:
md ((,n) list or array of floats) – The measured depths of the points of interest.
- Returns:
A welleng.survey.Survey object with an interpolated property
indicating whether each station was interpolated (True) or is an
original survey station (False).
Examples
>>> import welleng as we >>> import numpy as np >>> survey = we.survey.Survey( ... md=[0, 500, 1000, 2000, 3000], ... inc=[0, 0, 30, 90, 90], ... azi=[0, 0, 45, 135, 180], ... ) >>> survey_interp = survey.interpolate_mds(np.arange(0, 3000, 30))
- interpolate_survey(step=30, dls=1e-08)[source]
Convenience method for interpolating a Survey object’s MD.
- interpolate_survey_tvd(start=None, stop=None, step=10)[source]
Convenience method for interpolating a Survey object’s TVD.
- interpolate_tvd(tvd)[source]
Interpolate the survey at a target true vertical depth.
Reversal-robust (Sawaryn & Thorogood 2005, SPE-84246-PA): returns every crossing of
tvd, so a target hit twice by a TVD reversal yields two Nodes.- Parameters:
tvd (float) – The true vertical depth at which to interpolate.
- Returns:
Every crossing of
tvd, sorted by measured depth (normally a single element; empty iftvdis outside the well’s TVD range).- Return type:
list of Node
Notes
Breaking change (welleng 0.15.0): returns a
listof Nodes instead of a single Node. Useinterpolate_tvd(tvd)[0]on a monotonic well for the previous behaviour.
- maximum_curvature(dls_noise=1.0, steering=None)[source]
Create a well trajectory using the Maximum Curvature method.
- Parameters:
survey (welleng.survey.Survey object)
dls_noise (float) – The additional Dog Leg Severity (DLS) in deg/30m used to calculate the curvature for the initial section of the survey interval.
steering ({'slide', 'rotary'}, or (,n) array-like of those / bool) –
The slide/rotary mode of each leg - a physical drilling property that is not inferable from the survey (inclination, azimuth, measured depth); it is either a single value applied to every leg, or one entry per survey station (the mode of the leg arriving at that station):
'slide'- the leg was steered by sliding a bent motor at an oriented toolface. The extradls_noisecurvature is applied in the surveyed toolface, giving a directional deflection (the survey-interval error has a consistent sign - the well lands shallower).'rotary'- the leg was drilled rotating (RSS or rotary hold). The toolface averages out over rotation, so no directional deflection is applied (the leg keeps its minimum-curvature path); the survey-interval error there is random, not directional.
A boolean array is read as
True == slide. Defaults to'slide'- the conservative choice, since folding a directional bias into a symmetric (rotary) treatment would under-state the error; rotary is therefore opt-in and must be positively declared. Falls back toSurvey.steeringwhen the argument is left asNone.
- Returns:
survey_new – A revised survey object calculated using the Minimum Curvature method with updated survey positions and additional mid-point stations.
- Return type:
welleng.Survey.survey object
- Raises:
ValueError – If an array is given whose length does not match the number of survey stations.
- modified_tortuosity_index(rtol=1.0, dls_tol=0.001, step=1.0, dls_noise=1.0, data=False, **kwargs)[source]
Convenience method for the Modified Tortuosity Index (MTI): a native-3D, dimensionless variant of the Tortuosity Index (TI) of Ashok et al. ([IADD presentation](https://www.iadd-intl.org/media/files/files/47d68cb4/iadd-luncheon-february-22-2018-v2.pdf)) and D’Angelo et al. (SPE/IADC-194099-MS).
Compared with
tortuosity_index(), the MTI divides each curve turn’s(L_cs / L_xs - 1)term by its arc lengthL_csand usesL_c(rather than1 / L_c) as the normalizing factor, which makes the result independent of the survey’s unit of length (a survey in feet and the same survey in metres give the same MTI). See [the method post](https://jonnymaserati.github.io/2022/05/26/a-modified-tortuosity-index.html).Warning
“MTI” here means Modified Tortuosity Index. In SPE/IADC-194099-MS “MTI” denotes the unrelated Mapped Tortuosity Index (planned curve turns mapped onto the as-drilled path); do not conflate the two.
By default the survey is pre-processed with the maximum-curvature method (interpolated to
stepthendls_noisedeg/30m added) so that the MTI is robust to survey-station frequency; setdls_noise=Noneto use the raw minimum-curvature survey instead.- Parameters:
rtol (float) – Relative tolerance when testing normal-vector continuity (passed to
numpy.iscloseas bothrtolandatol).dls_tol (float or None) – If not None, additionally require dogleg-severity continuity within this tolerance when sectionizing.
step (float or None) – Step length (metres) for interpolating the survey before applying the maximum-curvature method. Ignored if
dls_noiseis None.dls_noise (float or None) – Incremental Dog Leg Severity (deg/30m) added by the maximum-curvature method. If None, no pre-processing is done and minimum curvature is assumed. When applied, every leg is treated as a slide (
steering='slide') - the maximum, worst-case tortuosity the method is defined to give; the slide/rotary distinction ofmaximum_curvature()is deliberately not exposed here, since the MTI is a conservative geometric quality metric rather than an error-propagation calculation.data (bool) – If True, return a dict of intermediate properties instead of the array.
- Returns:
mti – Per-station modified tortuosity index, or a dict of intermediate results (
starts,mds,locs,l_cs,l_xs,mti,survey…) ifdatais True.- Return type:
(n,) ndarray or dict
References
Further details on the maximum-curvature method and survey-frequency robustness are [here](https://jonnymaserati.github.io/2022/06/19/modified-tortuosity-index-survey-frequency.html).
- project_to_bit(delta_md, dls=None, toolface=None)[source]
Convenience method to project the survey ahead to the bit.
- Parameters:
delta_md (float) – The along hole distance from the surveying tool to the bit in meters.
dls (float) – The desired dog leg severity (deg / 30m) between the surveying tool and the bit. Default is to project the DLS of the last survey section.
toolface (float) – The desired toolface to project from at the last survey point. The default is to project the current toolface from the last survey station.
- Returns:
node
- Return type:
welleng.node.Node object
- project_to_target(node_target, dls_design=3.0, delta_md=None, dls=None, toolface=None, step=30)[source]
Project a wellpath from the end of this survey to a target node.
- Parameters:
node_target (Node) – The target Node to connect to.
dls_design (float) – Design dogleg severity (deg/30m) for the connection.
delta_md (float or None) – Along-hole distance from survey tool to bit. If None, projection starts at the last survey station.
dls (float or None) – DLS for the projection to the bit. Defaults to last survey DLS.
toolface (float or None) – Toolface for the projection to the bit. Defaults to last survey toolface.
step (float) – Survey interval (m) for the projected wellpath.
- Returns:
A Survey object representing the projected path to the target.
- Return type:
- save(filename)[source]
Saves a minimal (control points) survey listing as a .csv file, including the survey header information.
- Parameters:
filename (str) – The path and filename for saving the text file.
- set_vertical_section(vertical_section_azimuth, deg=True)[source]
Sets the vertical_section_azimuth property in the survey header and the vertical section data with the data calculated for the input azimuth.
- Parameters:
vertical_section_azimuth (float) – The azimuth (relative to the reference azimuth defined in the survey header) along which to calculate the vertical section lateral displacement.
deg (boolean (default: True)) – Indicates whether the vertical section azimuth parameter is in degrees or radians (True or False respectively).
- torsion()[source]
Geometric torsion
τper station (radians per unit length).The helical rate of the wellpath — the rate at which the osculating-plane normal (
self.normals, i.e. the Serret-Frenet binormal direction) rotates along the trajectory. Zero on a planar (2D) trajectory; nonzero only where the well turns out of plane. This is a geometric property of the path and is distinct from mechanical drillstring twist.Discrete form (Mitchell & Samuel 2009, SPE-105068-PA, Eq. 17):
τ_j = arccos(b_{j-1} · b_j) / Δs_j
with
bthe unit osculating-plane normal andΔs_jthe central measured-depth spacing about stationj. This is the same normal-vector continuity the (modified) tortuosity index uses — a section of constant normal is planar and has zero torsion.- Returns:
torsion – Per-station geometric torsion (rad/unit length), aligned to
self.md. The two end stations (undefined) and any straight-hold sections (undefined osculating plane) are set to 0.- Return type:
ndarray of shape (n,)
Notes
Consumers (e.g. the stiff-string T&D 3D contact terms, SPE-105068-PA Eq. 20) should prefer a smooth (spline) trajectory: a minimum-curvature survey gives a bending-moment discontinuity at stations (App. F), so torsion evaluated on raw min-curve stations is a station-spaced approximation.
- tortuosity_index(rtol=0.01, dls_tol=None, data=False, **kwargs)[source]
Convenience method for the Tortuosity Index (TI), a native-3D variant of the method presented in the [IADD presentation](https://www.iadd-intl.org/media/files/files/47d68cb4/iadd-luncheon-february-22-2018-v2.pdf) by Pradeep Ashok et al. and in SPE/IADC-194099-MS by D’Angelo et al. (itself adapted from the retinal-vessel tortuosity work of Grisan et al.).
The published method computes a tortuosity index separately in the inclination and azimuth domains and combines them as the root of the sum of squares. However, the arc-length and chord-length terms used in each domain are the full 3D quantities (only the curve-turn detection differs between the domains), so the two components are not independent and the 3D curvature is effectively double-counted. This method avoids that by sectionizing the trajectory directly in 3D: a curve turn is considered continuous while its normal vector (
vec_i x vec_j) remains constant, which inherently accounts for torsion. Seetortuosity_index()for the implementation andmodified_tortuosity_index()for the dimensionless variant.Note that TI is not dimensionless (the result scales with the unit of length, since the
1 / L_cnormalization carries length units); per SPE/IADC-194099-MS a scale factor of1e7is applied so values fall in a convenient range. ComputeL_cin feet to compare with the published reference ranges.- Parameters:
rtol (float) – Relative tolerance when testing normal-vector continuity (passed to
numpy.iscloseas bothrtolandatol).dls_tol (float or None) – If not None, additionally require dogleg-severity continuity within this tolerance when sectionizing.
data (bool) – If True, return a dict of intermediate properties instead of the array.
**kwargs –
coeff(length unit conversion, default 0.3048 -> feet) andkappa(scale factor, default 1e7) may be overridden.
- Returns:
ti – Per-station tortuosity index, or a dict of results if
data.- Return type:
ndarray or dict
- tortuosity_views(modified=True, target_md=None, **kwargs)[source]
Total, remaining and local readings of the tortuosity profile.
The tortuosity index is evaluated at every station, so it is a profile; these are the three engineering reads of it (see the MTI paper):
total: the value at the end (the whole-well KPI scalar);
remaining: the increment still to accumulate from each station to the end (or to
target_md) — what is left to drill;local: the along-hole gradient of the index — flags a single tortuous interval that the total would hide.
- Parameters:
modified (bool) – If True (default) use the dimensionless
modified_tortuosity_index(); otherwise usetortuosity_index().target_md (float or None) – Reference depth for
remaining; defaults to total depth.**kwargs – Passed through to the underlying index method.
- Returns:
{'md', 'total', 'remaining', 'local'}wheremdis the depth grid the profile is evaluated on (the maximum-curvature pre-processed grid whenmodifiedpre-processing is active).- Return type:
dict
- class welleng.survey.SurveyData(survey)[source]
Bases:
objectLightweight container for combining survey data from multiple sections.
Extracts the minimal data needed from Survey objects and provides methods to append additional sections and reconstruct a unified Survey.
- __init__(survey)[source]
A class for extracting the minimal amount of data from a Survey object, with methods for combining data from a list of surveys that describe an entire well path.
- Parameters:
survey (welleng.survey.Survey)
- class welleng.survey.SurveyHeader(name: str = None, longitude=None, latitude=None, altitude=None, survey_date=None, G=9.80665, b_total=None, earth_rate=0.26251614, dip=None, declination=None, convergence=0, azi_reference='true', vertical_inc_limit=0.0001, deg=True, depth_unit='meters', surface_unit='meters', mag_defaults={'b_total': 50000.0, 'declination': 0.0, 'dip': 70.0}, vertical_section_azimuth=0, grid_scale_factor: float = 1.0)[source]
Bases:
objectMetadata for a well survey including location, magnetic field, and reference systems.
Stores the geographic position, magnetic field parameters (total field, dip, declination), convergence, azimuth reference system, and unit conventions needed to interpret and process directional survey data.
- __init__(name: str = None, longitude=None, latitude=None, altitude=None, survey_date=None, G=9.80665, b_total=None, earth_rate=0.26251614, dip=None, declination=None, convergence=0, azi_reference='true', vertical_inc_limit=0.0001, deg=True, depth_unit='meters', surface_unit='meters', mag_defaults={'b_total': 50000.0, 'declination': 0.0, 'dip': 70.0}, vertical_section_azimuth=0, grid_scale_factor: float = 1.0)[source]
A class for storing header information about a well.
- Parameters:
name (string (default: None)) – The assigned name of the well bore.
longitude (float (default: None)) – The longitude of the surface location of the well. If left default (None) then it will be assigned to Grenwich, the undisputed center of the universe.
latitude (float (default: None)) – The latitude of the surface location of the well. If left default (None) then it will be assigned to Grenwich, the undisputed center of the universe.
altitude (float (default: None)) – The altitude of the surface location. If left defaults (None) then it will be assigned to 0.
survey_date (YYYY-mm-dd (default: None)) – The date on which the survey data was recorded. If left default then the current date is assigned.
G (float (default: 9.80665)) – The gravitational field strength in m/s^2.
b_total (float (default: None)) – The gravitation field strength in nT. If left default, then the value is calculated from the longitude, latitude, altitude and survey_data properties using the magnetic_field_calculator.
earth_rate (float (default: 0.26249751949994715)) – The rate of rotation of the earth in radians per hour.
noise_reduction_factor (float (default: 1.0)) – A fiddle factor for random gyro noise.
dip (float (default: None)) – The dip (inclination) of the magnetic field relative to the earth’s horizontal. If left default, then the value is calculated using the magnetic_field_calculator. The unit (deg of rad) is determined by the deg property.
declination (float (default: None)) – The angle between true north and magnetic north at the well location. If left default, then the value is calculated using the magnetic_field_calculator.
convergence (float (default: 0)) – The angle of convergence between the projection meridian and the line from true north through the location of the well.
azi_reference (string (default: 'true')) – The reference system for the azimuth angles in the survey data, either “true”, “magnetic” or “grid”. Note that survey calculations are performed in the “grid” reference and converted to and from the other systems.
vertical_inc_limit (float (default 0.0001)) – For survey inclination angles less than the vertical_inc_limit (in degrees), calculations are approximated to avoid singularities and errors.
deg (bool (default: True)) – Indicates whether the survey angles are measured in degrees (True) or radians (False).
depth_unit (string (default: "meters")) – The unit of depth for the survey data, either “meters” or “feet”.
surface_unit (string (default: "feet")) – The unit of distance for the survey data, either “meters” or “feet”.
vertical_section_azimuth (float (default: 0.0)) – The azimuth along which to determine the vertical section data for the well trajectory.
grid_scale_factor (float (default: 1.0)) – Scale factor applied during when determining the grid coordinates from the provided survey data.
- class welleng.survey.SurveyParameters(projection: str = 'EPSG:23031')[source]
Bases:
ProjClass for calculating survey parameters for input to a Survey Header.
This is a wrapper of pyproj that tries to simplify the process of getting convergence, declination and dip values for a survey header.
Notes
Requires
pyprojandmagnetic_field_calculatorto be installed and access to the internet.For reference, here’s some EPSG codes: {
‘UTM31_ED50’: ‘EPSG:23031’, ‘UTM31_WGS84’: ‘EPSG:32631’, ‘RD’: ‘EPSG:28992’, ‘ED50-UTM31’: ‘EPSG:23031’, ‘ED50-NEDTM’: ‘EPSG:23095’, # assume same as ED50-UTM31 ‘ETRS89-UTM31’: ‘EPSG:25831’, ‘ED50-UTM32’: ‘EPSG:23032’, ‘ED50-GEOGR’: ‘EPSG:4230’, ‘WGS84-UTM31’: ‘EPSG:32631’
}
References
For more info on transformations between maps, refer to the pyproj project [here](https://pypi.org/project/pyproj/).
- __init__(projection: str = 'EPSG:23031') None[source]
Initiates a SurveyParameters object for conversion of map coordinates to WGS84 lat/lon for calculating magnetic field properties.
- Parameters:
projection (str (default: "EPSG:23031")) – The EPSG code of the map of interest. The default represents ED50/UTM zone 31N.
References
For codes refer to [EPSG](https://epsg.io).
- get_factors_from_x_y(x: float, y: float, altitude: float = None, date: str = None) dict[source]
Calculates the survey header parameters for a given map coordinate.
- Parameters:
x (float) – The x or East/West coordinate.
y (float) – The y or North/South coordinate.
altitude (float (default: None)) – The altitude or z value coordinate. If none is provided this will default to zero (sea level).
date (str (default: None)) – The date of the survey, used when calculating the magnetic parameters. Will default to the current date.
- Returns:
- x: float
The x coordinate.
- y: float
The y coordinate.
- northing: float
The Northing (negative values are South).
- easting: float
The Easting (negative values are West).
- latitude: float
The WGS84 latitude.
- longitude: float
The WGS84 longitude.
- convergence: float
Te grid convergence for the provided coordinates.
- scale_factor: float
The scale factor for the provided coordinates.
- magnetic_field_intensity: float
The total field intensity for the provided coordinates and time.
- declination: float
The declination at the provided coordinates and time.
- dip: float
The dip angle at the provided coordinates and time.
- date:
The date used for determining the magnetic parameters.
- Return type:
dict
Examples
In the following example, the parameters for Den Haag in The Netherlands are looked up with the reference map ED50 UTM Zone 31N.
>>> import pprint >>> from welleng.survey import SurveyParameters >>> calculator = SurveyParameters('EPSG:23031') >>> survey_parameters = calculator.get_factors_from_x_y( ... x=588319.02, y=5770571.03 ... ) >>> pprint(survey_parameters) {'convergence': 1.01664403471959, 'date': '2023-12-16', 'declination': 2.213, 'dip': -67.199, 'easting': 588319.02, 'latitude': 52.077583926214494, 'longitude': 4.288694821453205, 'magnetic_field_intensity': 49381, 'northing': 5770571.03, 'scale_factor': 0.9996957469340414, 'srs': 'EPSG:23031', 'x': 588319.02, 'y': 5770571.03}
- transform_coordinates(coords: ArrayLike, to_projection: str, altitude: float = None, *args, **kwargs) ArrayLike[source]
Transforms coordinates from instance’s projection to another projection.
- Parameters:
coords (arraylike) – A list of decimal coordinates to transform from the instance projection to the specified projection system. Can be 2D or 3D in (x, y, z) format, where x is East/West and y is North/South.
to_projection (str) – The EPSG code of the desired coordinates.
- Returns:
result – An array of transformed coordinates in the desired projection.
- Return type:
ArrayLike
Examples
Convert the coordinates of Den Haag from ED50-UTM31 to WGS84-UTM31:
>>> from welleng.survey import SurveyParameters >>> calculator = SurveyParameters('EPSG:23031') >>> result = calculator.transform_coordinates( ... coords=[(588319.02, 5770571.03)], to_projection='EPSG:32631' ... ) >>> print(result) [[ 588225.93417027 5770360.56500115]]
- class welleng.survey.TurnPoint(md=None, inc=None, azi=None, build_rate=None, turn_rate=None, dls=None, toolface=None, method=None, target=None, tie_on=False, location=None)[source]
Bases:
objectA control point in a well plan, representing a hold or curve section.
Used when discretizing a survey into sections for export to planning software (e.g. Landmark COMPASS .wbp format).
- __init__(md=None, inc=None, azi=None, build_rate=None, turn_rate=None, dls=None, toolface=None, method=None, target=None, tie_on=False, location=None)[source]
Initialize a TurnPoint.
- Parameters:
md (float or None) – Measured depth.
inc (float or None) – Inclination in degrees.
azi (float or None) – Azimuth in degrees.
build_rate (float or None) – Build rate in deg per unit length.
turn_rate (float or None) – Turn rate in deg per unit length.
dls (float or None) – Dogleg severity.
toolface (float or None) – Toolface angle in degrees.
method (str or None) – Planning method code (e.g.
"920"for minimum curvature).target (object or None) – Associated target, if any.
tie_on (bool) – Whether this is the tie-on point.
location (list or None) – Position as
[x, y, z].
- welleng.survey.directional_difficulty_index(survey, **kwargs)[source]
Taken from IADC/SPE 59196 The Directional Difficulty Index - A New Approach to Performance Benchmarking by Alistair W. Oag et al. :param survey: :type survey: welleng.survey.Survey object :param data: If True, returns the ddi at each survey station. :type data: bool
- Returns:
ddi (float) – The ddi for the well at well (at TD).
data ((n) array of floats) – The ddi for each survey station.
- welleng.survey.export_csv(survey, filename, tolerance=0.1, dls_cont=False, decimals=3, **kwargs)[source]
Function to export a minimalist (only the control points - i.e. the begining and end points of hold and/or turn sections) survey to input into third party trajectory planning software.
- Parameters:
survey (welleng.survey.Survey object)
filename (str) – The path and filename for saving the text file.
tolerance (float (default: 0.1)) – How close the the final N, E, TVD position of the minimalist survey should be to the original survey point (e.g. within 1 meter)
dls_cont (bool) – Whether to explicitly check for dls continuity. May result in a larger number of control points but a trajectory that is a closer fit to the survey.
decimals (int (default: 3)) – Number of decimal places provided in the output file listing
- welleng.survey.from_connections(section_data, step=None, survey_header=None, start_nev=[0.0, 0.0, 0.0], start_xyz=[0.0, 0.0, 0.0], start_cov_nev=None, radius=10, deg=False, error_model=None, depth_unit='meters', surface_unit='meters', decimals: int | None = None)[source]
Constructs a well survey from a list of sections of control points.
- Parameters:
section_data (list of dicts with section data)
start_nev – The starting position in NEV coordinates.
radius (float (default: 10)) – The radius is passed to the welleng.survey.Survey object and represents the radius of the wellbore. It is also used when visualizing the results, so can be used to make the wellbore thicker in the plot.
decimals (int (default=6)) – Round the md decimal when checking for duplicate surveys.
- Returns:
survey – A Survey object constructed from the connections.
- Return type:
- welleng.survey.func(x0, survey, dls_cont, tolerance)[source]
Objective function for optimizing control-point tolerance in export_csv.
- Parameters:
x0 (float) – Current tolerance value being optimized.
survey (Survey) – The original Survey object.
dls_cont (bool) – Whether to check DLS continuity.
tolerance (float) – Target positional tolerance for the endpoint.
- Returns:
Absolute difference between the target tolerance and the maximum endpoint position error.
- Return type:
float
- welleng.survey.get_circle_radius(survey, **targets)[source]
Compute curvature circle centers and endpoints for each survey interval.
- Parameters:
survey (Survey) – A Survey object.
**targets – Reserved for future target data support.
- Returns:
Tuple of (starts, ends) arrays representing circle center positions and their corresponding survey station positions.
- Return type:
tuple of ndarray
- welleng.survey.get_data(tol, survey, dls_cont)[source]
Extract control-point data from a survey at a given tolerance.
- Parameters:
tol (float) – Tolerance for section boundary detection (used as rtol and atol).
survey (Survey) – A Survey object.
dls_cont (bool) – Whether to check DLS continuity between sections.
- Returns:
Array of shape (n, 10) with MD, inc, azi, N, E, TVD, DLS, toolface, build rate, and turn rate for each control point.
- Return type:
ndarray
- welleng.survey.get_node(survey, idx, interpolated=False)[source]
Extract a Node from a survey at a given index.
- welleng.survey.get_node_tvd(survey, node1, node2, tvd, node_origin)[source]
Connect two nodes and interpolate to a target TVD.
- Parameters:
- Returns:
A Node at the target TVD between the two input nodes.
- Return type:
- welleng.survey.get_sections(survey, rtol=0.1, atol=0.1, dls_cont=False, **targets)[source]
Tries to discretize a survey file into hold or curve sections. These sections can then be used to generate a WellPlan object to generate a .wbp format file for import into Landmark COMPASS, thus converting a survey file to an editable well trajectory.
Note that this is in development and only tested on output from planning software. In its current form it likely won’t be too successful on “as drilled” surveys (but optimizing the tolerances may help).
- Parameters:
survey (welleng.survey.Survey object)
rtol (float (default: 1e-1)) – The relative tolerance when comparing the normals using the numpy.isclose() function.
atol (float (default: 1e-2)) – The absolute tolerance when comparing the normals using the numpy.isclose() function.
dls_cont (bool) – Whether to explicitly check for dls continuity. May results in a larger number of control points but a trajectory that is a closer fit to the survey.
**targets (list of Target objects) – Not supported yet…
- Returns:
sections – List of TurnPoint objects representing control points.
- Return type:
list of TurnPoint
- welleng.survey.get_unit(unit)[source]
Normalize a unit string to
'meters'or'feet'.- Parameters:
unit (str) – Input unit string (e.g.
'm','meters','ft','feet').- Returns:
'meters','feet', or None if unrecognized.- Return type:
str or None
- welleng.survey.interpolate_mds(survey, md)[source]
Interpolates a survey at an array of measured depths, returning a new welleng.survey.Survey object that includes the original survey stations plus the requested (interpolated) measured depths.
This is a vectorized equivalent of looping the scalar interpolate_md. Any requested depth that coincides with an existing survey station is dropped (the station is already present in the output).
- Parameters:
survey (welleng.survey.Survey) – A survey object with at least two survey stations.
md ((,n) list or array of floats) – The measured depths of the points of interest.
- Returns:
survey_interpolated
- Return type:
welleng.survey.Survey object
- welleng.survey.interpolate_survey(survey, step=30, dls=1e-08)[source]
Interpolate a sparse survey with the desired md step.
- Parameters:
survey (welleng.survey.Survey object)
step (float (default=30)) – The desired delta md between stations.
dls (float (default=0.01)) – The design DLS used to calculate the minimum curvature. This will be the minimum DLS used to fit a curve between stations so should be set to a small value to ensure a continuous curve is fit without any tangent sections.
- Returns:
survey_interpolated – Note that a interpolated property is added indicating if the survey stations is interpolated (True) or not (False).
- Return type:
welleng.survey.Survey object
- welleng.survey.interpolate_survey_tvd(survey, start=None, stop=None, step=10)[source]
Interpolate a survey at regular TVD intervals.
Reversal-robust (welleng 0.15.0): builds regular TVD levels spanning the well’s full TVD range and inserts a station at every crossing of each level (so a level revisited by a TVD reversal is represented at each pass), interleaved with the original survey stations. Crossings are found with the closed-form, turning-point-segmented
interpolate_tvd()(Sawaryn & Thorogood 2005, SPE-84246-PA).- Parameters:
survey (Survey) – A Survey object.
start (float or None) – TVD level anchor. Levels are placed at
start + k * step. Defaults to the first survey station’s TVD.stop (float or None) – Upper TVD bound for the levels. Defaults to the well’s maximum TVD.
step (float) – TVD interval between interpolated levels.
- Returns:
A Survey object with stations at regular TVD levels plus the original survey stations, ordered by measured depth.
- Return type:
- welleng.survey.interpolate_tvd(survey, tvd, **kwargs)[source]
Interpolate a survey at a target true vertical depth.
Reversal-robust: does not assume monotonic TVD. The survey is walked segment by segment; each minimum-curvature arc is split at its TVD turning point (where the well goes horizontal) into monotonic spans, and every crossing of the target TVD is solved for in closed form. All crossings are returned, sorted by measured depth.
Method: Sawaryn & Thorogood (2005), “A Compendium of Directional Calculations Based on the Minimum Curvature Method” (SPE-84246-PA), Interpolation at a Plane (Eqs. 25-27 and Eq. 1) with the target plane horizontal, plus the Turning Point construction (Eq. 31) to segment each arc into monotonic-TVD spans. See also
_arc_tvd_crossings()and_horizontal_tangent_delta().- Parameters:
survey (Survey) – A Survey object.
tvd (float) – The target true vertical depth.
**kwargs –
- node_originNode, optional
Interpolate on the sub-arc that starts at this node (rather than a survey station), spanning to the next survey station. Used to reference the interpolation to a previously interpolated point.
- Returns:
Every crossing of
tvd, sorted by measured depth (normally a single element; an empty list iftvdis outside the well’s TVD range).- Return type:
list of Node
Notes
Breaking change (welleng 0.15.0): this returns a
listof Nodes instead of a single Node. On a monotonic well,interpolate_tvd(tvd)[0]recovers the previous single-crossing behaviour.
- welleng.survey.make_survey_header(data)[source]
Takes a dictionary of survey header data with the same keys as the SurveyHeader class properties and returns a SurveyHeader object.
- welleng.survey.modified_tortuosity_index(survey, rtol=1.0, dls_tol=0.001, data=False, **kwargs)[source]
Calculate the Modified Tortuosity Index (MTI): a native-3D, dimensionless variant of the Tortuosity Index (TI) of Ashok et al. ([IADD presentation](https://www.iadd-intl.org/media/files/files/47d68cb4/iadd-luncheon-february-22-2018-v2.pdf)) and D’Angelo et al. (SPE/IADC-194099-MS).
The trajectory is split into curve-turn / hold sections in 3D via normal-vector continuity (see
_get_ti_data()). Each section’s(L_cs / L_xs - 1)term is divided by its arc lengthL_csand the running sum is scaled byn / (n + 1)and the curve lengthL_c, making the result independent of the unit of length.L_csis the along-hole (arc) distance from the section start to each station andL_xsthe corresponding straight-line (chord) distance.Note: “MTI” here is the Modified Tortuosity Index; in SPE/IADC-194099-MS “MTI” is the unrelated Mapped Tortuosity Index.
- Parameters:
survey (welleng.survey.Survey)
rtol (float) – Relative tolerance for normal-vector continuity (also used as atol).
dls_tol (float or None) – If not None, also require dogleg-severity continuity within this tolerance.
data (bool) – If True, return a dict of intermediate properties.
**kwargs –
coeff(unit conversion, default 1.0) andkappa(scale factor, default 1) may be overridden.
- Returns:
mti
- Return type:
ndarray or dict
- welleng.survey.project_ahead(pos, vec, delta_md, dls, toolface, md=0.0)[source]
Apply a simple arc or hold from a current position and vector.
- Parameters:
pos – Current position in n, e, tvd coordinates.
vec – Current vector in n, e, tvd coordinates.
delta_md (float) – The desired along hole projection length.
dls (float) – The desired dogleg severity of the projection. Entering 0.0 will result in a hold section.
toolface (float) – The desired toolface for the projection.
md (float (optional)) – The current md if applicable.
- Returns:
node
- Return type:
welleng.node.Node object
- welleng.survey.project_to_target(survey, node_target, dls_design=3.0, delta_md=None, dls=None, toolface=None, step=30)[source]
Project a wellpath from the end of a current survey to a target, taking account of the location of the bit relative to the surveying tool if the delta_md property is not None.
- Parameters:
survey (welleng.survey.Survey obj)
node_target (welleng.node.Node obj)
dls_design (float) – The dls from which to construct the projected wellpath.
delta_md (float) – The along hole length from the surveying sensor to the bit.
dls (float) – The desired dogleg severity for the projection from the survey tool to the bit. Entering 0.0 will result in a hold section.
toolface (float) – The desired toolface for the projection from the survey tool to the bit.
step (float) – The desired survey interval for the projected wellpath to the target.
- Returns:
node
- Return type:
welleng.survey.Survey obj
- welleng.survey.slice_survey(survey: Survey, start: int, stop: int = None)[source]
Take a slice from a welleng.survey.Survey object.
- Parameters:
survey (welleng.survey.Survey object)
start (int) – The start index of the desired slice.
stop (int (default: None)) – The stop index of the desired slice, else the remainder of the well bore TD is the default.
- Returns:
s – A survey object of the desired slice is returned.
- Return type:
welleng.survey.Survey object
- welleng.survey.splice_surveys(surveys)[source]
Join together an ordered list of surveys for a well (for example, a list of surveys with a different error model for each survey).
- Parameters:
surveys (list of welleng.survey.Survey objects) – The first survey in the list is assumed to be the shallowest and the survey header data is taken from this well. Subsequent surveys are assumed to be ordered by depth, with the first md of the next survey being equal to the last md of the previous survey.
- Returns:
spliced_survey – A single survey consisting of the input surveys placed together.
- Return type:
welleng.survey.Survey object
Notes
The returned survey will include the covariance data describing the well bore uncertainty, but will not include the error models since these may be different for each well section.
- welleng.survey.survey_to_df(survey: Survey) DataFrame[source]
Convert a Survey object to a pandas DataFrame.
- Parameters:
survey (Survey) – A Survey object.
- Returns:
DataFrame with columns for MD, inclination, azimuths, positions, DLS, toolface, build rate, and turn rate.
- Return type:
pd.DataFrame
- welleng.survey.tortuosity_index(survey, rtol=0.01, dls_tol=None, data=False, **kwargs)[source]
Calculate the Tortuosity Index (TI), a native-3D variant of the method of Ashok et al. ([IADD presentation](https://www.iadd-intl.org/media/files/files/47d68cb4/iadd-luncheon-february-22-2018-v2.pdf)) and D’Angelo et al. (SPE/IADC-194099-MS).
The trajectory is split into curve-turn / hold sections in 3D via normal-vector continuity (see
_get_ti_data()); each section’s(L_cs / L_xs - 1)term is accumulated, scaled byn / (n + 1)and normalized by1 / L_c, then bykappa(1e7 per SPE/IADC-194099-MS).L_csis the along-hole (arc) distance from the section start to each station andL_xsthe corresponding straight-line (chord) distance.TI is not dimensionless: the result scales with the unit of length, so
coeffdefaults to 0.3048 to expressL_cin feet and match the published reference ranges. Seemodified_tortuosity_index()for the dimensionless variant.- Parameters:
survey (welleng.survey.Survey)
rtol (float) – Relative tolerance for normal-vector continuity (also used as atol).
dls_tol (float or None) – If not None, also require dogleg-severity continuity within this tolerance.
data (bool) – If True, return a dict of intermediate properties.
**kwargs –
coeff(unit conversion, default 0.3048 -> feet) andkappa(scale factor, default 1e7) may be overridden.
- Returns:
ti
- Return type:
ndarray or dict
- welleng.survey.tortuosity_views(profile, md, target_md=None)[source]
Derive total, remaining and local readings from a tortuosity profile.
- Parameters:
profile (array_like) – A per-station tortuosity index profile (TI or MTI), monotonic non-decreasing.
md (array_like) – Measured depth at each station, same length as
profile.target_md (float or None) – Reference depth for the
remainingcalculation; defaults to the last station (total depth).
- Returns:
total(float, the profile value attarget_md/ end),remaining(ndarray,totalminus the profile — what is left to accumulate from each station),local(ndarray, the along-hole gradientd(profile)/d(md)— the rate of tortuosity accumulation).- Return type:
dict
welleng.target module
Drilling target definitions for wellbore trajectory visualization.
- class welleng.target.Target(name, n, e, tvd, shape, locked=0, orientation=0, dip=0, color='green', alpha=0.5, **geometry)[source]
Bases:
objectA geometric target zone in 3D space for wellbore trajectory planning.
Represents a target area (circle, ellipse, rectangle, or polygon) at a given subsurface location, with optional orientation and dip. Requires vedo for visualization.
- name
Identifier for the target.
- Type:
str
- n
Northing coordinate.
- Type:
float
- e
Easting coordinate.
- Type:
float
- tvd
True vertical depth.
- Type:
float
- shape
Target geometry type.
- Type:
str
- locked
Lock state of the target.
- Type:
int
- orientation
Rotation angle about the vertical axis in degrees.
- Type:
float
- dip
Dip angle of the target plane in degrees.
- Type:
float
- color
Display color for rendering.
- Type:
str
- alpha
Opacity for rendering (0.0 to 1.0).
- Type:
float
- geometry
Shape-specific dimensional parameters.
- Type:
dict
- __init__(name, n, e, tvd, shape, locked=0, orientation=0, dip=0, color='green', alpha=0.5, **geometry)[source]
Initialize a Target.
- Parameters:
name (str) – Identifier for the target.
n (float) – Northing coordinate (meters).
e (float) – Easting coordinate (meters).
tvd (float) – True vertical depth (meters).
shape (str) – Target geometry type. One of ‘circle’, ‘ellipse’, ‘rectangle’, or ‘polygon’.
locked (int, optional) – Lock state of the target (0 = unlocked).
orientation (float, optional) – Rotation angle about the vertical axis in degrees.
dip (float, optional) – Dip angle of the target plane in degrees.
color (str, optional) – Display color for rendering.
alpha (float, optional) – Opacity for rendering (0.0 to 1.0).
**geometry (dict) – Shape-specific parameters. For ‘circle’: radius. For ‘ellipse’: radius_1, radius_2, res. For ‘rectangle’: pos1, pos2.
- Raises:
AssertionError – If vedo is not installed, shape is invalid, or geometry keys do not match the expected keys for the shape.
- plot_data()[source]
Generate a vedo mesh object for rendering the target.
Currently supports the ‘circle’ shape. The target is positioned at (n, e, tvd) and rotated according to dip and orientation.
- Returns:
A vedo geometry object representing the target, with the target name assigned to its
flagattribute.- Return type:
vedo object
welleng.torque_drag module
Wellbore torque and drag calculations based on Johancsik et al. (SPE 11380-PA).
- class welleng.torque_drag.HookLoad(survey, wellbore, string, fluid_density, step=30, name=None, ff_range=(0.1, 0.4, 0.1))[source]
Bases:
objectHookload (broomstick) plot model for running or pulling a string.
- __init__(survey, wellbore, string, fluid_density, step=30, name=None, ff_range=(0.1, 0.4, 0.1))[source]
A class for calculating the hookload or broomstick plot data for running or pulling a string in a wellbore.
- Parameters:
survey (welleng.survey.Survey instance) – The well trajectory of the scenario being modelled.
wellbore (welleng.architecture.WellBore instance) – The well bore architecture of the scenario being modelled.
string (welleng.architecture.BHA or welleng.architecture.CasingString)
instance – The string being run inside the well bore for the scenario being modelled.
fluid_density (float) – The density (in SG) of the fluid in the well bore.
step (float) – The measured depth step distance in meters to move the string.
name (str) – The name of the scenario being modeled.
ff_range – The start, stop and step for the range of friction factors to be used in the hookload calculations.
- class welleng.torque_drag.TorqueDrag(survey, wellbore, string, fluid_density, name=None, wob=None, tob=None, overpull=None)[source]
Bases:
objectTorque and drag model for a string in a wellbore.
Computes axial tension and torsion profiles along a drillstring or casing string for pickup, slackoff, rotating, and drilling scenarios using the soft-string (Johancsik) method.
- add_survey_points_from_strings(strings)[source]
Add string section boundaries as survey station points.
- get_characteristic_od(strings)[source]
Determine effective OD for each survey interval from string data.
- get_weight_buoyed_and_radius(strings)[source]
Calculate buoyed weight and bend radius per interval.
- get_coeff_friction_sliding(strings)[source]
Get sliding friction coefficients per interval from string data.
- get_forces_and_torsion(mode, friction)[source]
Calculate axial forces and torque along the wellbore.
- __init__(survey, wellbore, string, fluid_density, name=None, wob=None, tob=None, overpull=None)[source]
A class for calculating wellbore torque and drag, based on the “Torque and Drag in Directional Wells–Prediction and Measurement (SPE 11380-PA) by C.A. Johancsik et al.
- Parameters:
survey (welleng.survey.Survey instance) – The well trajectory of the scenario being modelled.
wellbore (welleng.architecture.WellBore instance) – The well bore architecture of the scenario being modelled.
string (welleng.architecture.BHA or welleng.architecture.CasingString)
instance – The string being run inside the well bore for the scenario being modelled.
fluid_density (float) – The density (in SG) of the fluid in the well bore.
name (str) – The name of the scenario being modeled.
wob (float) – The compressive force (weight on bit) applied at the bottom of the string in N.
tob (float) – The torque (torque on bit) applied at the bottom of the string in N.m.
overpull (float) – The tension applied at the bottom of the string in N.
- add_survey_points_from_strings()[source]
Check that there’s survey stations for the top and bottoms of the string sections to ensure that the torque and drag is calculated for these key locations.
- figure()[source]
Generate a plotly figure of tension and torque vs depth.
- Returns:
Figure with tension (left) and torque (right) subplots.
- Return type:
plotly.graph_objects.Figure
- get_buoyancy_factors()[source]
Determine the buoyancy factor for each string section and add it to the string sections dict.
- get_characteristic_od(section)[source]
Return the effective outer diameter for a string section.
Uses the tooljoint OD if available, otherwise the pipe body OD.
- Parameters:
section (int) – Index of the string section.
- Returns:
The characteristic outer diameter in meters.
- Return type:
float
- get_coeff_friction_sliding()[source]
Build an array of sliding friction coefficients mapped to survey stations.
- get_forces_and_torsion(wob=False, tob=False, overpull=False)[source]
Compute tension and torque profiles along the string.
Iterates from bit to surface, accumulating normal force, axial tension, and torsion at each survey station. Results are stored in
self.tensionandself.torquedicts keyed by load case.- Parameters:
wob (float, optional) – Weight on bit in Newtons. Must be provided with tob.
tob (float, optional) – Torque on bit in N*m. Must be provided with wob.
overpull (float, optional) – Additional tension at the bit in Newtons.
- welleng.torque_drag.buoyancy_factor(fluid_density, string_density=7.85)[source]
- Parameters:
fluid_density (float) – The density of the fluid in SG.
string_density (float) – The density of the string, typically made from steel.
- Returns:
result – The buoyancy factor when when multiplied against the string weight yields the bouyed string weight.
- Return type:
float
- welleng.torque_drag.figure_hookload(hl, units={'depth': 'ft', 'tension': 'lbf', 'torque': 'ft_lbf'})[source]
Create a plotly hookload (broomstick) figure.
- Parameters:
hl (HookLoad) – Completed hookload model instance.
units (dict, optional) – Unit keys for
depth,tension, andtorque.
- Returns:
Hookload plot with pickup, slackoff, and rotating traces.
- Return type:
plotly.graph_objects.Figure
- welleng.torque_drag.figure_string_tension_and_torque(td, units={'depth': 'ft', 'tension': 'lbf', 'torque': 'ft_lbf'})[source]
Create a plotly figure showing string tension and torque vs depth.
- Parameters:
td (TorqueDrag) – Completed torque-drag model instance.
units (dict, optional) – Unit keys for
depth,tension, andtorque.
- Returns:
Figure with tension (left) and torque (right) subplots.
- Return type:
plotly.graph_objects.Figure
- welleng.torque_drag.force_normal(force_tension, inc_average, inc_delta, azi_delta, weight_buoyed)[source]
Calculate the normal contact force between string and wellbore.
- Parameters:
force_tension (numpy.ndarray) – Axial tension array (pickup, slackoff, rotating) in N.
inc_average (float) – Average inclination of the interval in radians.
inc_delta (float) – Inclination change over the interval in radians.
azi_delta (float) – Azimuth change over the interval in radians.
weight_buoyed (float) – Buoyed weight of the string element in N.
- Returns:
Normal force array for each load case in N.
- Return type:
numpy.ndarray
- welleng.torque_drag.force_tension_delta(weight_buoyed, inc_average, coeff_friction_sliding, force_normal)[source]
Calculate the incremental tension change over one survey interval.
- Parameters:
weight_buoyed (float) – Buoyed weight of the string element in N.
inc_average (float) – Average inclination of the interval in radians.
coeff_friction_sliding (float) – Sliding friction coefficient for the interval.
force_normal (numpy.ndarray) – Normal contact force for each load case in N.
- Returns:
Tension increments for (pickup, slackoff, rotating) in N.
- Return type:
tuple of float
- welleng.torque_drag.torsion_delta(coeff_friction_sliding, force_normal, radius)[source]
Calculate the incremental torsion change over one survey interval.
- Parameters:
coeff_friction_sliding (float) – Sliding friction coefficient for the interval.
force_normal (float) – Normal contact force for the rotating load case in N.
radius (float) – Contact radius of the string element in meters.
- Returns:
Torsion increment in N*m.
- Return type:
float
welleng.units module
welleng.utils module
- class welleng.utils.Arc(dogleg, radius)[source]
Bases:
object- __init__(dogleg, radius)[source]
Generates a generic arc that can be transformed with a specific pos and vec via a transform method. The arc is initialized at a local origin and kicks off down and to the north (assuming an NEV coordinate system).
- Parameters:
dogleg (float) – The sweep angle of the arc in radians.
radius (float) – The radius of the arc in meters.
- Returns:
arc
- Return type:
Arc object
- transform(toolface, pos=None, vec=None, target=False)[source]
Transforms an Arc to a position and orientation.
- Parameters:
pos ((,3) array)
arc. (The desired position to transform the)
vec ((,3) array) – The orientation unit vector to transform the arc.
target (bool) – If true, returned arc vector is reversed.
- Returns:
tuple (pos_new, vec_new)
pos_new ((,3) array) – The position at the end of the arc post transform.
vec_new ((,3) array) – The unit vector at the end of the arc post transform.
- class welleng.utils.MinCurve(md, inc, azi, start_xyz=[0.0, 0.0, 0.0], unit='meters')[source]
Bases:
object- __init__(md, inc, azi, start_xyz=[0.0, 0.0, 0.0], unit='meters')[source]
Generate geometric data from a well bore survey.
- Parameters:
md (list or 1d array of floats) – Measured depth along well path from a datum.
inc (list or 1d array of floats) – Well path inclination (relative to z/tvd axis where 0 indicates down), in radians.
azi (list or 1d array of floats) – Well path azimuth (relative to y/North axis), in radians.
unit (str) – Either “meters” or “feet” to determine the unit of the dogleg severity.
- welleng.utils.NEV_to_HLA(survey: Annotated[ndarray[tuple[Any, ...], dtype[_ScalarT]], Literal['N', 3]], NEV: Annotated[ndarray[tuple[Any, ...], dtype[_ScalarT]], Literal['N', 3]], cov: bool = True) Annotated[ndarray[tuple[Any, ...], dtype[_ScalarT]], Literal['N, 3']] | Annotated[ndarray[tuple[Any, ...], dtype[_ScalarT]], Literal['N, 3, 3']][source]
Transform from NEV to HLA coordinate system.
- Parameters:
survey ((n,3) array of floats) – The [md, inc, azi] survey listing array.
NEV ((n,3) or (n,3,3) array of floats) – The NEV coordinates or covariance matrices.
cov (boolean) – If cov is True then a (n,3,3) array of covariance matrices is expected, else a (n,3) array of coordinates.
- Returns:
HLAs – Either a transformed (n,3) array of HLA coordinates or an (n,3,3) array of HLA covariance matrices.
- Return type:
NDArray
- welleng.utils.annular_volume(od: float, id: float = None, length: float = None)[source]
Calculate an annular volume.
If no
idis provided then circular volume is calculated. If nolengthis provided, then the unit volume is calculated (i.e. the area).Units are assumed consistent across input parameters, i.e. the calculation is dimensionless.
- Parameters:
od (float) – The outer diameter.
id (float | None, optional) – The inner diameter, default is 0.
length (float | None, optional) – The length of the annulus.
- Returns:
annular_volume – The (unit) volume of the annulus or cylinder.
- Return type:
float
Examples
In the following example we calculate annular volume along a 1,000 meter section length of 9 5/8” casing inside 12 1/4” hole.
>>> from welleng.utils import annular_volume >>> from welleng.units import ureg >>> av = annular_volume( ... od=ureg('12.25 inch').to('meters), ... id=ureg(f'{9+5/8} inch').to('meter'), ... length=ureg('1000 meter') ... ) >>> print(av) 29.096093526301622 meter ** 3
- welleng.utils.cov_from_vec(arr)[source]
Returns a (n, 3, 3) covariance matrix from an (n, 3) array via outer product.
- Parameters:
arr ((n, 3) array) – Array of vector components.
- Return type:
(n, 3, 3) array
- welleng.utils.decimal2dms(decimal: tuple | ndarray[tuple[Any, ...], dtype[_ScalarT]], ndigits: int = None) tuple | ndarray[tuple[Any, ...], dtype[_ScalarT]][source]
Converts a decimal lat, lon to degrees, minutes and seconds.
- Parameters:
decimal (tuple | arraylike) – A tuple of (lat, direction) or (lon, direction) or arraylike of ((lat, direction), (lon, direction)) coordinates.
ndigits (int (default is None)) – If specified, rounds the seconds decimal to the desired number of digits.
- Returns:
dms – An array of (degrees, minutes, seconds, direction).
- Return type:
arraylike
Examples
If you want to convert the lat/lon coordinates for Den Haag from decimals to degrees, minutes and seconds:
>>> LAT, LON = [(52.078663, 'N'), (4.288788, 'E')] >>> dms = decimal2dms((LAT, LON), ndigits=6) >>> print(dms) [[52 4 43.1868 'N'] [4 17 19.6368 'E']]
- welleng.utils.dms2decimal(dms: tuple | ndarray[tuple[Any, ...], dtype[_ScalarT]], ndigits: int = None) ndarray[tuple[Any, ...], dtype[_ScalarT]][source]
Converts a degrees, minutes and seconds lat, lon to decimals.
- Parameters:
dms (tuple | arraylike) – A tuple or arraylike of (degrees, minutes, seconds, direction) lat and/or lon or arraylike of lat, lon coordinates.
ndigits (int (default is None)) – If specified, rounds the decimal to the desired number of digits.
- Returns:
degrees – A tuple or array of lats and/or longs in decimals.
- Return type:
arraylike
Examples
If you want to convert the lat/lon coordinates for Den Haag from degrees, minutes and seconds to decimals:
>>> LAT, LON = (52, 4, 43.1868, 'N'), (4, 17, 19.6368, 'E') >>> decimal = dms2decimal((LAT, LON), ndigits=6) >>> print(decimal) [[52.078663 'N'] [4.288788 'E']]
- welleng.utils.dms_from_string(text)[source]
Extracts the values from a string dms x or y or northing or easting.
- welleng.utils.errors_from_cov(cov, data=False)[source]
- Parameters:
cov ((n, 3, 3) array) – The error covariance matrices.
data (bool (default: False)) – If True returns a dictionary, else returns a list.
- welleng.utils.get_angles(vec: Annotated[ndarray[tuple[Any, ...], dtype[_ScalarT]], Literal['N', 3]], nev: bool = False)[source]
Determines the inclination and azimuth from a vector.
- Parameters:
vec ((n,3) array of floats)
nev (boolean (default: False)) – Indicates if the vector is in (x,y,z) or (n,e,v) coordinates.
- Returns:
[inc, azi] – A numpy array of incs and axis in radians
- Return type:
(n,2) array of floats
- welleng.utils.get_arc(dogleg, radius, toolface, pos=None, vec=None, target=False) tuple[source]
Creates an Arc instance and transforms it to the desired position and orientation.
- Parameters:
dogleg (float) – The swept angle of the arc (arc angle) in radians.
radius (float) – The radius of the arc (in meters).
toolface (float) – The toolface angle in radians (relative to the high side) to rotate the arc at the desired position and orientation.
pos ((,3) array) – The desired position to transform the arc.
vec ((,3) array) – The orientation unit vector to transform the arc.
target (bool) – If true, returned arc vector is reversed.
- Returns:
tuple of (pos_new, vec_new, arc.delta_md)
pos_new ((,3) array) – The position at the end of the arc post transform.
vec_new ((,3) array) – The unit vector at the end of the arc post transform.
arc.delta_md (int) – The arc length of the arc.
- welleng.utils.get_dogleg(inc1, azi1, inc2, azi2)[source]
Compute the dogleg angle between two survey stations (vectorised).
Uses the numerically stable Haversine form to avoid arccos precision loss at small angles.
- Parameters:
inc1 (float or array — inclination / azimuth at station 1 (radians))
azi1 (float or array — inclination / azimuth at station 1 (radians))
inc2 (float or array — inclination / azimuth at station 2 (radians))
azi2 (float or array — inclination / azimuth at station 2 (radians))
- Returns:
dogleg
- Return type:
float or array — dogleg angle in radians
- welleng.utils.get_nev(pos, start_xyz=array([0., 0., 0.]), start_nev=array([0., 0., 0.]))[source]
Convert [x, y, z] coordinates to [n, e, tvd] coordinates.
- Parameters:
pos ((n,3) array of floats) – Array of [x, y, z] coordinates
start_xyz ((,3) array of floats) – The datum of the [x, y, z] cooardinates
start_nev ((,3) array of floats) – The datum of the [n, e, tvd] coordinates
- Return type:
An (n,3) array of [n, e, tvd] coordinates.
- welleng.utils.get_rf(dogleg)[source]
Compute the ratio factor (RF) for minimum curvature (vectorised).
Returns 1.0 where dogleg is 0 (limit of the function as dogleg → 0).
- Parameters:
dogleg (float or array — dogleg angle(s) in radians)
- Returns:
rf
- Return type:
float or array — ratio factor(s)
- welleng.utils.get_sigmas(cov, long=False)[source]
Extracts the sigma values of a covariance matrix along the principle axii.
- Parameters:
cov ((n,3,3) array of floats)
- Returns:
arr
- Return type:
(n,3) array of floats
- welleng.utils.get_toolface(pos1: ndarray[tuple[Any, ...], dtype[_ScalarT]], vec1: ndarray[tuple[Any, ...], dtype[_ScalarT]], pos2: ndarray[tuple[Any, ...], dtype[_ScalarT]]) ndarray[tuple[Any, ...], dtype[_ScalarT]][source]
Returns the toolface(s) of offset position(s) relative to reference positions and vectors. Accepts either single (3,) arrays or batches of (n, 3) arrays; all three arguments must have the same leading dimension.
- Parameters:
pos1 (ndarray, shape (3,) or (n, 3)) – The reference NEV coordinate(s), e.g. current location.
vec1 (ndarray, shape (3,) or (n, 3)) – The reference NEV unit vector(s), e.g. current direction.
pos2 (ndarray, shape (3,) or (n, 3)) – The offset NEV coordinate(s), e.g. a target position.
- Returns:
toolface – The toolface(s) in radians [0, 2π) to pos2 from pos1 along vec1. Returns a scalar float when single (3,) inputs are given.
- Return type:
float or ndarray
- welleng.utils.get_toolface_fast(pos1: ndarray[tuple[Any, ...], dtype[_ScalarT]], vec1: ndarray[tuple[Any, ...], dtype[_ScalarT]], pos2: ndarray[tuple[Any, ...], dtype[_ScalarT]]) float[source]
Returns the toolface of a single offset position using a direct closed-form expression — approximately 12× faster than
get_toolfacefor scalar inputs.Suitable when pos1, vec1 and pos2 are all individual (3,) arrays. For batch use, prefer the vectorised
get_toolface.- Parameters:
pos1 (array-like, shape (3,)) – The reference NEV coordinate, e.g. current location.
vec1 (array-like, shape (3,)) – The reference NEV unit vector, e.g. current direction.
pos2 (array-like, shape (3,)) – The offset NEV coordinate, e.g. a target position.
- Returns:
toolface – The toolface in radians [0, 2π) to pos2 from pos1 along vec1.
- Return type:
float
- welleng.utils.get_transform(survey)[source]
Determine the transform for transforming between NEV and HLA coordinate systems.
- Parameters:
survey ((n,3) array of floats) – The [md, inc, azi] survey listing array.
- Returns:
transform
- Return type:
(n,3,3) array of floats
- welleng.utils.get_vec(inc, azi, nev=False, r=1, deg=True)[source]
Convert inc and azi into a vector.
- Parameters:
inc (array of n floats) – Inclination relative to the z-axis (up)
azi (array of n floats) – Azimuth relative to the y-axis
r (float or array of n floats) – Scalar to return a scaled vector
- Returns:
vec – An (n,3) array of vectors
- Return type:
arraylike
- welleng.utils.make_clc_path(toolface1, dogleg1, distance, toolface2, dogleg2, pos0=None, vec0=None, radius=1.0)[source]
Generate a curve-hold-curve (CLC) path from arc parameters.
Builds the path in three steps: first arc, straight hold, second arc. Useful for constructing known-geometry test cases and for quickly prototyping CLC trajectories.
- Parameters:
toolface1 (float) – Toolface angle for the first curve in radians.
dogleg1 (float) – Sweep angle (dogleg) for the first curve in radians.
distance (float) – Length of the straight hold section (same units as radius).
toolface2 (float) – Toolface angle for the second curve in radians.
dogleg2 (float) – Sweep angle (dogleg) for the second curve in radians.
pos0 ((3,) array-like, optional) – Start position [N, E, V]. Defaults to [0, 0, 0].
vec0 ((3,) array-like, optional) – Start direction unit vector. Defaults to [0, 0, 1] (pointing down).
radius (float, optional) – Arc radius for both curves. Defaults to 1.0.
- Returns:
pos1, vec1 – end of first arc dist_curve1 – arc length of first curve pos2, vec2 – end of hold section / start of second arc pos3, vec3 – end of second arc dist_curve2 – arc length of second curve
- Return type:
dict with keys
- welleng.utils.make_long_cov(arr)[source]
Build a (n, 3, 3) covariance matrix from the 6 unique upper-triangle elements per station.
- Parameters:
arr ((n, 6) array — columns [aa, ab, ac, bb, bc, cc])
- Returns:
cov
- Return type:
(n, 3, 3) array
- welleng.utils.min_curve_step(delta_md, inc1, azi1, inc2, azi2, rf=None)[source]
Compute position increments using minimum curvature (vectorised).
All trigonometric values are computed once and shared across the three coordinate directions for efficiency.
- Parameters:
delta_md ((n,) array — measured-depth increments)
inc1 ((n,) arrays — start inclination / azimuth (radians))
azi1 ((n,) arrays — start inclination / azimuth (radians))
inc2 ((n,) arrays — end inclination / azimuth (radians))
azi2 ((n,) arrays — end inclination / azimuth (radians))
rf ((n,) array or None — ratio factors; computed if not supplied)
- Returns:
deltas
- Return type:
(n, 3) array — position increments in [N, E, V] order
- welleng.utils.pprint_dms(dms, symbols: bool = True, return_data: bool = False)[source]
Pretty prints a (decimal, minutes, seconds) tuple or list.
- Parameters:
dms (tuple | list) – An x or y or northing or easting (degree, minute, second).
symbols (bool (default: True)) – Whether to print symbols for (deg, min, sec).
return_data (bool (default: False)) – If True then will return the string rather than print it.
welleng.version module
welleng.visual module
Visualization utilities for wellbore trajectories using vedo/VTK and plotly.
- welleng.visual.figure(obj, type='scatter3d', **kwargs)[source]
Create a plotly figure from a survey or mesh object.
- Parameters:
- Returns:
A plotly Figure instance.
- Return type:
plotly.graph_objects.Figure
- welleng.visual.get_lines(clearance)[source]
Add lines per reference well interval between the closest points on the reference well and the offset well and color them according to the calculated Separation Factor (SF) between the two wells at these points.
- Parameters:
clearance (welleng.clearance.Clearance) – A welleng clearance object.
- Returns:
A vedo.Lines object colored by the object’s SF values.
- Return type:
vedo.Lines
- welleng.visual.plot(data, colors=None, names=None, lines=None, arrows=None, interactive=True, **kwargs)[source]
Convenience function for quick visualization of well meshes.
- Parameters:
data (WellMesh or list of WellMesh) – The well mesh(es) to plot.
colors (list of str, optional) – Per-item colors when data is a list.
names (list of str, optional) – Per-item names (currently unused, reserved for legends).
lines (vedo object, optional) – Lines to add to the scene.
arrows (vedo object, optional) – Arrows to add to the scene.
interactive (bool) – Whether to show an interactive window.