welleng.exchange package
Submodules
welleng.exchange.csv module
- welleng.exchange.csv.export_csv(survey, filename, tolerance=0.1, dls_cont=False, decimals=3, **kwargs)[source]
Wrapper for survey.export_csv
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.exchange.edm module
- class welleng.exchange.edm.EDM(filename)[source]
Bases:
object- __init__(filename)[source]
Initiate an instance of an EDM object.
Deprecated since version ``EDM``: loads the entire EDM XML into a DOM, which is impractical for large exports (the Volve file is ~211 MB). Prefer the streaming
welleng.exchange.edm_stream.EDMReader(also reachable viaEDM.open()), which indexes the file with bounded memory and returns typed surveys with per-station covariance and resolved survey tools.EDMis retained for the torque & drag / case workflows the streaming reader does not (yet) cover.- Parameters:
filename (str) – The path and filename of the EDM file to be imported.
- get_attributes(tags=None, attributes={}, logic='AND')[source]
Get the attributes for the given tags in an EDM instance.
- Parameters:
tags (str or list of str (default: None)) – The tag or list of tags you wish to return. The default will return all the tags that satisfy the given attributes.
attributes (dict) – A dictionary of attribute keys and values to satisfy in the search of tags.
logic (str (default: 'AND')) – Indicates whether the attributes should be all be satisfied (‘AND’) or if only one needs to be satisfied (‘OR’).
- Returns:
data – A dictionary of a list of dictionaries of tags and their attributes.
- Return type:
dict
- classmethod open(filename, source_units='feet')[source]
Open an EDM file with the streaming reader.
Returns a
welleng.exchange.edm_stream.EDMReader– the memory-bounded replacement for the DOM-basedEDM. This does not build anEDMinstance and does not emit the deprecation warning.- Parameters:
filename (str) – Path to the EDM XML file.
source_units (str, {"feet", "meters"}) – Units of the stored depth/offset values (default
"feet").
- Return type:
welleng.exchange.edm_stream.EDMReader
welleng.exchange.ipm module
Reader for IPM (Instrument Performance Model) survey-tool error-model files.
The .IPM format is the de-facto industry exchange format for directional-survey
error models (Landmark Engineer’s Desktop / COMPASS, Schlumberger Drilling Office and
others export it). Each file describes one survey tool as a set of ISCWSA-style
weight-function terms: an error source, the axis it perturbs, its propagation
(correlation) mode, a 1-sigma magnitude and the weight-function formula.
File structure:
#Tool Name : MWD+SAG
#ShortName : MWD+SAG
#Description: ...
#Remarks : ...
#Name<TAB>Vector<TAB>Tie-On<TAB>Unit<TAB>Value<TAB>Formula
abx<TAB>i<TAB>s<TAB>-<TAB>0.004<TAB>(-cos(inc)*sin(tfo))/gtot
...
Columns
Name — error-source code (
abx,mbz,sag,decg…).Vector — axis the term perturbs:
iinclination,aazimuth,llateral (andd/e/fdepth-family in some dialects).Tie-On — propagation/correlation mode:
ssystematic,rrandom,gglobal,wwell-by-well.Unit — magnitude unit (
-dimensionless,nt,deg…).Value — the 1-sigma error magnitude.
Formula — the weight function (an expression in
inc,azm,tfo,dip,gtot,mtot,tmd,tvd…). Stored verbatim; this reader does not evaluate it.
This module only parses the file into IPMModel / IPMTerm;
mapping the
terms onto a propagation engine is left to the caller.
- class welleng.exchange.ipm.IPMModel(name: str = '', short_name: str = '', description: str = '', remarks: str = '', header: ~typing.Dict[str, str] = <factory>, terms: ~typing.List[~welleng.exchange.ipm.IPMTerm] = <factory>)[source]
Bases:
objectA parsed IPM survey-tool error model.
- __init__(name: str = '', short_name: str = '', description: str = '', remarks: str = '', header: ~typing.Dict[str, str] = <factory>, terms: ~typing.List[~welleng.exchange.ipm.IPMTerm] = <factory>) None
- by_tie_on(tie_on: str) List[IPMTerm][source]
All terms with the given propagation mode (
's'/'r'/'g'/'w').
- description: str = ''
- header: Dict[str, str]
- name: str = ''
- remarks: str = ''
- short_name: str = ''
- class welleng.exchange.ipm.IPMTerm(name: str, vector: str, tie_on: str, unit: str, value: float, formula: str)[source]
Bases:
objectA single error-model term (one row of an IPM file).
- __init__(name: str, vector: str, tie_on: str, unit: str, value: float, formula: str) None
- formula: str
- name: str
- tie_on: str
- unit: str
- value: float
- vector: str
- welleng.exchange.ipm.loads_ipm(text: str) IPMModel[source]
Parse an IPM model from an in-memory string (see
read_ipm()).
- welleng.exchange.ipm.read_ipm(path, encoding: str = 'latin-1') IPMModel[source]
Parse an
.IPMfile frompathinto anIPMModel.- Parameters:
path (str or path-like) – Path to the
.IPMfile.encoding (str, default
'latin-1') – Text encoding (IPM exports are typically latin-1; degree signs etc.).
- Return type:
Examples
>>> model = read_ipm("MWD+SAG.IPM") >>> model.name, len(model.terms) ('MWD+SAG', 30)
welleng.exchange.wbp module
- class welleng.exchange.wbp.SurveyPoint(md=None, inc=None, azi=None, cov_xx=None, cov_xy=None, cov_xz=None, cov_yy=None, cov_yz=None, cov_zz=None, x_bias=None, y_bias=None, z_bias=None, tool=None, location=None)[source]
Bases:
object
- class welleng.exchange.wbp.Target(name, location=None, geometry={'azimuth': None, 'category': None, 'color': {'application': None, 'color': None, 'feature': None, 'interpreter': None}, 'dip': None, 'locked': None, 'offset': None, 'orientation': None, 'radius_1': None, 'radius_2': None, 'thickness_down': None, 'thickness_up': None, 'type': None, 'vertices': []})[source]
Bases:
object- __init__(name, location=None, geometry={'azimuth': None, 'category': None, 'color': {'application': None, 'color': None, 'feature': None, 'interpreter': None}, 'dip': None, 'locked': None, 'offset': None, 'orientation': None, 'radius_1': None, 'radius_2': None, 'thickness_down': None, 'thickness_up': None, 'type': None, 'vertices': []})[source]
- class welleng.exchange.wbp.WellPlan(depth_unit='meters', surface_unit='meters', survey=None, plan_name=None, parent_name=None, location_type=None, plan_method='curve_only', dirty_flag=None, sidetrack_id=None, dls=3.0, extension=0, wbp_data=None, targets=[], line=None, parent_wbp_file=None)[source]
Bases:
object- __init__(depth_unit='meters', surface_unit='meters', survey=None, plan_name=None, parent_name=None, location_type=None, plan_method='curve_only', dirty_flag=None, sidetrack_id=None, dls=3.0, extension=0, wbp_data=None, targets=[], line=None, parent_wbp_file=None)[source]
An object for storing data extracted from or for writing to a .wbp file. As such, the following parameters are driven by those required by Landmark’s .wbp format.
- Parameters:
depth_unit (string (default: 'meters')) – The units used for expressing depth (z axis or tvd) in either ‘meters’ or ‘feet’.
surface_unit (string (default: 'meters')) – The units used for expressing lateral distances (x, y, N, E) in either ‘meters’ or ‘feet’.
survey (welleng.survey.Survey object (default: None))
plan_name (string (default: None)) – The name of the well bore plan.
parent_name (string (default: None)) – The name of the parent well bore plan (in the event that the planned well is a sidetrack or lateral).
location_type (string (default: None)) – Best to review the wbp.yaml file for options.
plan_method (string (default: 'curve_only')) – The method used for joining the plan points in the .wbp file. Options can be reviewed in the wbp.yaml file but won’t currently effect how the code runs, so just leave default.
dirt_flag (string (default: None)) – Again, review the wbp.yaml file for options, but this is not currently used in this code.
sidetrack_id (string (default: None)) – Leave default, not used.
dls (float (default: 0)) – Suggests that this sets the design dls for planning, but doesn’t appear to matter so leave as default.
extension (float (default: 0)) – Not really sure what this does.
wbp_data (list of strings (default: None)) – A list of strings with each string representing a line from of text loaded from a .wbp file. Used for importing .wbp data.
targets (list of welleng.exchange.wbp.Target objects (default: [])) – A list of target objects, but more of a future function.
line (int (default: None)) – Used for processing .wbp files that contain multiple well bores.
- Return type:
A welleng.exchange.wbp.WellPlan object representing a well bore.
- welleng.exchange.wbp.export(data, filename=None, comments=None)[source]
Export a WellPlan object to .wbp format.
- Parameters:
data (welleng.exchange.wbp.WellPlan object or a list of objects)
filename (string (default: None)) – The filename to save the .wbp file to. If None then the output is returned as data.
comments (list of strings (default: None)) – A list of comments to be printed in the header of the .wbp file.
- Returns:
doc
- Return type:
list of strings
- welleng.exchange.wbp.load(filename)[source]
Loads data line by line from a .wbp file, initiates a WellPlan object and populates it with data.
- Parameters:
filename (string) – The location and filename of the .wbp file to load.
- Return type:
A welleng.exchange.wbp.WellPlan object
- welleng.exchange.wbp.strip_duplicates(survey)[source]
Function to strip out identical successive survey stations from a Survey object.
- Parameters:
survey (welleng.survey.Survey object)
- Returns:
survey_stripped – A survey object with repeating survey stations removed.
- Return type:
welleng.survey.Survey object
- welleng.exchange.wbp.wbp_to_survey(data, step=None, radius=10, azi_reference='true', convergence=0.0, utm_zone=31, utm_north=True)[source]
Converts a WellPlan object created from a .wbp file into a Survey object.
- Parameters:
data (wellend.exchange.wbp.WellPlan object)
step (float) – The desired step interval used to create the Survey object. e.g. step=30 would create a survey station every 30 meters.
radius (float (default: 10)) – The radius of the well bore generated in the survey. The default is used assuming that the well will be rendered with welleng.visual.plot.
- Returns:
survey
- Return type:
welleng.survey.Survey object
Client for the NLOG (Dutch subsurface data portal) borehole REST API.
NLOG’s map viewer at nlog.nl/nlog-mapviewer is a single-page app
backed by an undocumented but stable REST API. This module wraps it so
that Dutch well data — trajectories, document indexes, log inventories
— can be pulled programmatically instead of clicked through, and so
that unmeasured (assumed / back-filled) azimuth columns can be
detected automatically before an error model is attached to them.
Endpoint shape, determined by reading the app bundle
(main-*.js, URL_PREFIX = "/nlog-mapviewer"):
POST /nlog-mapviewer/rest/brh/<resource> body: the bare integer borehole id (NOT JSON-wrapped) resources: details, dirsurveys, documents, logdocuments,
measurements, coreruns, photos
GET /brh-web/rest/brh/document/<bfileDbk> (scanned reports) GET /brh-web/rest/brh/logdocument/<bfileDbk> (LIS/LAS/DLIS/TXT)
POST /nlog-mapviewer/rest/brh/boreholes body: a JSON filter object ({} = the whole catalogue) -> the datacenter overview list, one row per borehole keyed by
boreholeDbk, with status/purpose/result/dates. This is the discovery endpoint: select wells by status (e.g. ‘Plugged and abandoned’) here, then feed boreholeDbk to the per-well resources.
The borehole id is the number in a map-viewer URL, e.g.
nlog.nl/nlog-mapviewer/brh/106523583 -> 106523583.
Bulk alternative: NLOG publishes a monthly zip of all directional
surveys (thematische_data_boringen.zip); prefer it for
whole-database work and this API for per-well detail and documents.
No authentication. Be polite — the portal is a public service.
Licence note: NLOG data is published by TNO on behalf of the Dutch state under its own terms; check them before redistributing.
- class welleng.exchange.nlog.DirSurvey(borehole_name: str, md: list[float], inc: list[float], azi: list[float], tvd: list[float], dx: list[float], dy: list[float], north_ref: str | None, coord_system: str | None, proc_method: str | None, convergence: float | None, declination: float | None, proc_date_ms: int | None, remark: str | None)[source]
Bases:
objectOne directional survey for a borehole, in SI units (m, degrees).
- __init__(borehole_name: str, md: list[float], inc: list[float], azi: list[float], tvd: list[float], dx: list[float], dy: list[float], north_ref: str | None, coord_system: str | None, proc_method: str | None, convergence: float | None, declination: float | None, proc_date_ms: int | None, remark: str | None) None
- azi: list[float]
- azimuth_provenance(inc_threshold: float = 0.5) Literal['measured', 'all_zero_vertical', 'all_zero_deviated', 'constant_assumed', 'single_bearing', 'no_survey'][source]
Classify whether the azimuth column is a measurement.
The distinction matters because an ISCWSA tool model applied to a fabricated azimuth reports a confident position that was never surveyed.
- borehole_name: str
- convergence: float | None
- coord_system: str | None
- declination: float | None
- dx: list[float]
- dy: list[float]
- inc: list[float]
- lateral_displacement() float[source]
Magnitude of lateral displacement implied by the inclination record — the radius of the annulus when azimuth is unknown.
- property max_inclination: float
- md: list[float]
- property n_stations: int
- north_ref: str | None
- proc_date_ms: int | None
- proc_method: str | None
- remark: str | None
- to_welleng(error_model: str | None = None, **header_kwargs)[source]
Build a
welleng.survey.Survey. Raises if the azimuth is not a measurement and an error model was requested, because the result would be a confident position derived from a placeholder — passerror_model=Noneto build geometry only, or setforce=Trueinheader_kwargsto override.
- tvd: list[float]
- class welleng.exchange.nlog.NLOGClient(timeout: float = 30.0, user_agent: str = 'welleng-nlog/1.0')[source]
Bases:
objectMinimal client for the NLOG borehole API.
- boreholes(filters: dict | None = None) list[dict][source]
The full borehole catalogue (the datacenter overview list).
POST /brh/boreholes with a JSON filter object;
{}(the default) returns every borehole. Each row carriesboreholeName,boreholeDbk,statusDescription(e.g. ‘Plugged and abandoned’),resultCode,purposeCd,onOffshoreandstartDate/endDate/confidentialityDate(epoch ms).This is the discovery endpoint that
suggest(name lookup) cannot replace: filter by status/purpose/date here, then feed theboreholeDbktodetails/documents/log_documents.
- details(borehole_id: int) dict[source]
Borehole metadata. NOTE the API quirk: unlike the other resources this one takes a JSON array of string ids, not a bare integer.
- fetch_document(bfile_dbk: int | str, *, log: bool = False) bytes[source]
Scanned report (
log=False) or log file (log=True).
- id_for_name(wellbore_name: str) int | None[source]
Resolve a bulk-dump WELLBORE name to a borehole id.
Match on title, preferring an exact hit. NLOG titles some bores
"NAME (ALIAS)"(the alias is a second registry number, e.g."P11-B-01 (P11-05)"); an exact match would silently miss those, so if no exact hit is found the alias is stripped and an alias-tolerant match is tried — but only accepted when it is UNAMBIGUOUS, so a query can never silently resolve to one of several sidetracks. Returns None if the portal does not know the name or the match is ambiguous.
- save_document(bfile_dbk: int | str, path, *, log: bool = False) int[source]
Download a document/log file to
path; return bytes written.
- stratigraphy(borehole_id: int, preferred_only: bool = True) list[StratColumn][source]
Lithostratigraphic interpretation(s) for a borehole.
A borehole can carry more than one interpretation;
preferred_only(default) returns only the one NLOG flags preferred (preferredBln == 'J'), otherwise all are returned. Depths in the returned intervals are MD along hole — NLOG serves no TVD for this resource. The depth datum is attached to each column. Unit codes are the raw RGD lithostratigraphic codes; NLOG serves no code-to-name lookup on this endpoint.
- class welleng.exchange.nlog.StratColumn(borehole_name: str, datum_description: str | None, datum_code: str | None, datum_height_m: float | None, end_md: float | None, source: str | None, model: str | None, preferred: bool, interpretation_date_ms: int | None, intervals: list[StratInterval])[source]
Bases:
objectA borehole’s lithostratigraphic interpretation (one of possibly several).
Depths are MD along hole — NLOG serves no TVD here, so derive it from the directional survey if needed and label it as derived. The depth datum is carried alongside the intervals because the payload is self-describing.
- __init__(borehole_name: str, datum_description: str | None, datum_code: str | None, datum_height_m: float | None, end_md: float | None, source: str | None, model: str | None, preferred: bool, interpretation_date_ms: int | None, intervals: list[StratInterval]) None
- borehole_name: str
- datum_code: str | None
- datum_description: str | None
- datum_height_m: float | None
- end_md: float | None
- interpretation_date_ms: int | None
- intervals: list[StratInterval]
- model: str | None
- preferred: bool
- source: str | None
- class welleng.exchange.nlog.StratInterval(top_md: float, bottom_md: float, unit_id: str | None, quality: str | None, anomaly: str | None, remark: str | None)[source]
Bases:
objectOne lithostratigraphic interval. Depths are MD along hole (NLOG serves no TVD for this resource);
unit_idis the raw RGD code (e.g.'NU').- __init__(top_md: float, bottom_md: float, unit_id: str | None, quality: str | None, anomaly: str | None, remark: str | None) None
- anomaly: str | None
- bottom_md: float
- quality: str | None
- remark: str | None
- top_md: float
- unit_id: str | None
- welleng.exchange.nlog.audit_borehole(borehole_id: int, client: NLOGClient | None = None) dict[source]
One-shot provenance report for a borehole.
Returns the survey classification plus whether any archived document looks like it might contain the missing azimuth.
Survey-data-quality audit for the NLOG bulk deviation dump.
NLOG (the Netherlands oil & gas portal) publishes a monthly bulk CSV of every
Dutch well’s deviation survey (nlog_dirstelsel_*.csv inside
thematische_data_boringen.zip). This module audits that dump for the defects
that bite a downstream consumer, using only the geometry in the file (no external
truth needed):
Surface coordinates — the per-well surface position is given in three datums (RD / ED50-UTM31 / WGS84-UTM31); cross-transform them and flag any that disagree (a wrong header coordinate).
Grid-azimuth alignment — each well declares its azimuth reference in
COORD_SYSTEM_CD(RD grid / ED50-UTM31 grid / true north / …). Compare the stored azimuths against the direction of the reported ED50-UTM31 position steps (a purely geometric check, no magnetics) and classify the actual reference from the grid convergences. A well is flagged when the declared and actual references disagree, split by failure mode:mis_grid— azimuth is in a different grid than declared (a fixed rotation by the convergence difference, up to ~3.4 deg across NL);wrong_sign— the grid convergence was applied with the wrong sign (a2*gammaerror — the classic loader bug);reflected— the azimuth is mirrored (360 - azi);unexplained— matches no known reference (likely corrupt).
Azimuth provenance — classify each well
vertical/deviated(azimuth recorded and varying) /single_bearing(near-constant azimuth) /azi_unrecorded(deviated but azimuth missing). The last is the “hollow annulus” case: the true horizontal position error is a ring at radiusR = MD*sin(inc), which a per-station covariance cannot represent.Dogleg severity — flag physically impossible spikes (> 30 deg/30 m).
Torsion — out-of-plane twist of the osculating plane between consecutive curved legs. Curvature (DLS) is in-plane; a near-180 deg plane flip is out-of-plane and betrays an azimuth flip / +-180 ambiguity that DLS cannot see.
Structural — non-monotonic / duplicate measured depths, and TVD drift (min-curvature TVD vs the reported
TV_DEPTH_NAP).
The checks are geometric and self-contained, so the report is reproducible by
anyone who downloads the same public dump. Requires pandas and pyproj
(both welleng dependencies).
- class welleng.exchange.nlog_audit.DumpAudit(n_wellbores: int, surface_p95_m: dict = <factory>, n_surface_defects: int = 0, projection: dict = <factory>, provenance: dict = <factory>, n_dls_wells: int = 0, n_torsion_wells: int = 0, wellbores: list = <factory>)[source]
Bases:
objectWhole-dump audit summary + the per-wellbore rows.
- __init__(n_wellbores: int, surface_p95_m: dict = <factory>, n_surface_defects: int = 0, projection: dict = <factory>, provenance: dict = <factory>, n_dls_wells: int = 0, n_torsion_wells: int = 0, wellbores: list = <factory>) None
- n_dls_wells: int = 0
- n_surface_defects: int = 0
- n_torsion_wells: int = 0
- n_wellbores: int
- projection: dict
- provenance: dict
- surface_p95_m: dict
- wellbores: list
- class welleng.exchange.nlog_audit.WellboreAudit(wellbore: str, n_stations: int, max_inc: float | None = None, provenance: str = 'too_short', annulus_radius_m: float | None = None, grid_declared: str | None = None, grid_actual: str | None = None, grid_status: str | None = None, grid_rotation_deg: float | None = None, max_dls_deg30m: float | None = None, n_dls_spikes: int = 0, max_torsion_deg: float | None = None, n_plane_inversions: int = 0, nonmonotonic_md: int = 0, duplicate_md: int = 0, tvd_worst_step_m: float | None = None)[source]
Bases:
objectPer-wellbore audit result (all angles in degrees, lengths in metres).
- __init__(wellbore: str, n_stations: int, max_inc: float | None = None, provenance: str = 'too_short', annulus_radius_m: float | None = None, grid_declared: str | None = None, grid_actual: str | None = None, grid_status: str | None = None, grid_rotation_deg: float | None = None, max_dls_deg30m: float | None = None, n_dls_spikes: int = 0, max_torsion_deg: float | None = None, n_plane_inversions: int = 0, nonmonotonic_md: int = 0, duplicate_md: int = 0, tvd_worst_step_m: float | None = None) None
- annulus_radius_m: float | None = None
- duplicate_md: int = 0
- grid_actual: str | None = None
- grid_declared: str | None = None
- grid_rotation_deg: float | None = None
- grid_status: str | None = None
- max_dls_deg30m: float | None = None
- max_inc: float | None = None
- max_torsion_deg: float | None = None
- n_dls_spikes: int = 0
- n_plane_inversions: int = 0
- n_stations: int
- nonmonotonic_md: int = 0
- provenance: str = 'too_short'
- tvd_worst_step_m: float | None = None
- wellbore: str
- welleng.exchange.nlog_audit.audit_dump(path, surface_defect_m=50.0)[source]
Audit an NLOG bulk deviation dump. Returns a
DumpAudit.- Parameters:
path (str) – Path to an
nlog_dirstelsel_*.csv(unzipped from the monthlythematische_data_boringen.zip).surface_defect_m (float) – Cross-datum surface residual (m) above which a well is a header defect.
Module contents
welleng/exchange
Contains the importers and exporters for various survey formats.