From 6858107079c025f0ed29f8c7f6aa850f0cd036f6 Mon Sep 17 00:00:00 2001 From: jonnymaserati Date: Tue, 22 Dec 2020 11:18:40 +0100 Subject: [PATCH 01/10] Added quick fix for fitting min_curve with connector module --- welleng/exchange/wbp.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/welleng/exchange/wbp.py b/welleng/exchange/wbp.py index ef274c3..bbc51ec 100644 --- a/welleng/exchange/wbp.py +++ b/welleng/exchange/wbp.py @@ -617,10 +617,14 @@ def wbp_to_survey(data, step=None, radius=10): # need to set dls_design relatively small to trigger adaption to # the dls used in the imported design, or to set it to the actual # dls used in the design. + # TODO: update the connector code so that None can be passed for + # dls_design, which will set np.inf for radius_design and force + # the dls to be set by radius_critical. if isinstance(s, welleng.exchange.wbp.TurnPoint): - dls_design = s.dls if s.dls > 0 else 0.1 + dls_design = s.dls if s.dls > 0 else 1e-5 else: - dls_design = data.dls if data.dls > 0 else None + # dls_design = data.dls if data.dls > 0 else None + dls_design = 1e-5 c = welleng.connector.Connector( pos1=pos[-1], From 82c28cea751fd154c964f02e2ab7fba779318b99 Mon Sep 17 00:00:00 2001 From: jonnymaserati Date: Tue, 22 Dec 2020 12:35:14 +0100 Subject: [PATCH 02/10] Initiate testing workflows and start fixing errors --- tests/test_connector.py | 104 ++++++++++++++++++++++++++++++++++++++++ welleng/io.py | 32 ++++++++----- 2 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 tests/test_connector.py diff --git a/tests/test_connector.py b/tests/test_connector.py new file mode 100644 index 0000000..448510f --- /dev/null +++ b/tests/test_connector.py @@ -0,0 +1,104 @@ +import welleng as we +import numpy as np + +# test hold with only md provided +c1 = we.connector.Connector( + vec1=[0,0,1], + md2=500, +) +assert ( + c1.inc_target == c1.inc1 + and c1.azi_target == c1.azi1 + and c1.pos_target[2] == c1.md_target +), "Failed c1" +assert c1.method == 'hold' + +c1_survey = c1.survey() + +# # test with md2 and vec2 provided (minimum curvature) +c2 = we.connector.Connector( + vec1=[0,0,1], + md2=1000, + vec2=[0,1,0] +) + +# c2_survey = c2.survey() + +# test with pos2 provided (minimum distance) +c3 = we.connector.Connector( + vec1=[0,0,1], + pos2=[100,100,1000], +) +assert c3.md_target > c3.pos1[2], "Failed c3" + +c3_survey = c3.survey() + +# test with pos2 needing more aggressive dls (minimum curvature) +c4 = we.connector.Connector( + vec1=[0,0,1], + pos2=[200,400,200] +) +assert c4.method == 'min_curve_to_target' + +# test with pos2 and vec2 provided () +vec1 = [-1,-1,1] +vec2 = [1,-1,0] +c5 = we.connector.Connector( + pos1=[0.,0.,0], + vec1=vec1 / np.linalg.norm(vec1), + pos2=[0.,1000.,500.], + vec2=vec2 / np.linalg.norm(vec2), +) +assert c5.method == 'curve_hold_curve' + +# test if interpolator and survey functions are working +c5_survey = c5.survey() + +# test that a mesh can be generated from the survey data +c5_mesh = we.mesh.WellMesh(c5_survey, method='circle') + +# test with pos2, inc1 and azi1 provided () +c6 = we.connector.Connector( + pos1=[0.,0.,0], + inc1=0., + azi1=90, + pos2=[1000.,1000.,1000.], + vec2=[0.,0.,1.], +) + +# test with different dls for second curve section +c7 = we.connector.Connector( + pos1=[0.,0.,0], + vec1=[0.,0.,1.], + pos2=[0.,100.,1000.], + vec2=[0.,0.,1.], + dls_design2=5 +) + +# test with dls_critical requirement (actual dls < dls_design) +c8 = we.connector.Connector( + pos1=[0.,0.,0], + vec1=[0.,0.,1.], + pos2=[0.,100.,100.], + vec2=[0.,0.,1.], +) +assert c8.radius_critical < c8.radius_design + +# test min_curve (inc2 provided) +c9 = we.connector.Connector( + pos1=[0.,0.,0], + vec1=[0.,0.,1.], + inc2=30, +) +assert c9.method == 'min_curve' + +# test min_curve with md less than required radius +c10 = we.connector.Connector( + pos1=[0.,0.,0], + inc1=0, + azi1=0, + md2=500, + inc2=90, + azi2=0, +) +assert c10.radius_critical < c10.radius_design \ No newline at end of file diff --git a/welleng/io.py b/welleng/io.py index 7a9f611..2bc0705 100644 --- a/welleng/io.py +++ b/welleng/io.py @@ -1,7 +1,10 @@ import numbers +import numpy as np +from .survey import Survey from openpyxl import load_workbook + def get_standard_data(filename): # import data from Excel workbook = load_workbook(filename, data_only=True) @@ -26,17 +29,18 @@ def get_standard_data(filename): return data + def get_data(well, sheet, data): temp = dict( - MD = [], - IncDeg = [], - AziDeg = [], - TVD = [], - N = [], - E = [], - sigmaH = [], - sigmaL = [], - sigmaA = [], + MD=[], + IncDeg=[], + AziDeg=[], + TVD=[], + N=[], + E=[], + sigmaH=[], + sigmaL=[], + sigmaA=[], ) for row in sheet.iter_rows( @@ -60,6 +64,7 @@ def get_data(well, sheet, data): return data + def acr_setup(sheet, data): data["acr"]["Sm"] = sheet["I4"].value data["acr"]["sigmapa"] = sheet["I5"].value @@ -69,8 +74,9 @@ def acr_setup(sheet, data): return data + def make_survey(data, well): - start_nev = data["wells"][offset]["mesh"].NEVs[0] + start_nev = data["wells"]["offset"]["mesh"].NEVs[0] y, x, z = start_nev start_xyz = np.array([x, y, z]) return Survey( @@ -81,11 +87,13 @@ def make_survey(data, well): start_xyz=start_xyz ) + def import_iscwsa_collision_data(filename): data = get_standard_data(filename) return data + if __name__ == "__main__": - filename = filename=f"reference/standard-set-of-wellpaths-for-evaluating-clearance-scenarios-r4-17-may-2017.xlsm" - data = import_iscwsa_collision_data(filename) \ No newline at end of file + filename = "reference/standard-set-of-wellpaths-for-evaluating-clearance-scenarios-r4-17-may-2017.xlsm" + data = import_iscwsa_collision_data(filename) From bdc688f75dad1a3abab50f1e14c6ce9fd80e1dd9 Mon Sep 17 00:00:00 2001 From: jonnymaserati Date: Tue, 22 Dec 2020 12:45:44 +0100 Subject: [PATCH 03/10] Update io.py --- welleng/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/welleng/io.py b/welleng/io.py index 2bc0705..65212ae 100644 --- a/welleng/io.py +++ b/welleng/io.py @@ -96,4 +96,4 @@ def import_iscwsa_collision_data(filename): if __name__ == "__main__": filename = "reference/standard-set-of-wellpaths-for-evaluating-clearance-scenarios-r4-17-may-2017.xlsm" - data = import_iscwsa_collision_data(filename) + data = import_iscwsa_collision_data(filename) \ No newline at end of file From e99a0fff5880088a5f4cc897250085af5824a4d7 Mon Sep 17 00:00:00 2001 From: jonnymaserati Date: Tue, 22 Dec 2020 13:11:17 +0100 Subject: [PATCH 04/10] flake8'd clearance.py --- welleng/clearance.py | 99 +++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/welleng/clearance.py b/welleng/clearance.py index bc00c44..3cf406a 100644 --- a/welleng/clearance.py +++ b/welleng/clearance.py @@ -6,10 +6,11 @@ from scipy.spatial import KDTree from scipy.spatial.distance import cdist -from .survey import Survey, interpolate_survey, slice_survey, make_cov -from .utils import NEV_to_HLA, HLA_to_NEV +from .survey import Survey, interpolate_survey, slice_survey +from .utils import NEV_to_HLA from .mesh import WellMesh + class Clearance: def __init__( self, @@ -41,7 +42,7 @@ def __init__( self.sigma_pa = sigma_pa self.Sm = Sm # self.N_verts = N_verts - + self._get_kop_index(kop_depth) self._get_ref() self._get_radii(Rr, Ro) @@ -51,7 +52,9 @@ def __init__( def _get_kop_index(self, kop_depth): self.kop_depth = kop_depth - self.kop_index = np.searchsorted(self.reference.md, self.kop_depth, side="left") + self.kop_index = np.searchsorted( + self.reference.md, self.kop_depth, side="left" + ) def _get_nevs(self, survey): return np.array([ @@ -104,6 +107,7 @@ def _get_ref(self): unit=self.reference.unit ) + def get_ref_sigma(sigma1, sigma2, sigma3, kop_index): sigma = np.array([sigma1, sigma2, sigma3]).T sigma_diff = np.diff(sigma, axis=0) @@ -111,10 +115,11 @@ def get_ref_sigma(sigma1, sigma2, sigma3, kop_index): sigma_above = np.cumsum(sigma_diff[:kop_index][::-1], axis=0)[::-1] sigma_below = np.cumsum(sigma_diff[kop_index:], axis=0) - sigma_new = np.vstack((sigma_above, np.array([0,0,0]), sigma_below)) + sigma_new = np.vstack((sigma_above, np.array([0, 0, 0]), sigma_below)) return sigma_new + class ISCWSA: def __init__( self, @@ -126,7 +131,7 @@ def __init__( """ Clearance.__init__ self.c = clearance - # get closest survey station in offset well for each survey + # get closest survey station in offset well for each survey # station in the reference well self.idx = np.argmin( cdist( @@ -188,11 +193,15 @@ def get_lines(self): [(start_points.append(p[0]), end_points.append(p[1])) for p in points] - return np.array([np.vstack(start_points).tolist(), np.vstack(end_points).tolist()]) + return np.array([ + np.vstack(start_points).tolist(), np.vstack(end_points).tolist() + ]) def _get_closest_points(self): closest = [] - for j, (i, station) in enumerate(zip(self.idx, self.c.ref_nevs.tolist())): + for j, (i, station) in enumerate(zip( + self.idx, self.c.ref_nevs.tolist() + )): if i > 0: bnds = [(0, self.c.offset.md[i] - self.c.offset.md[i - 1])] res_1 = optimize.minimize( @@ -204,7 +213,8 @@ def _get_closest_points(self): ) mult = res_1.x[0] / (bnds[0][1] - bnds[0][0]) sigma_new_1 = self._interpolate_covs(i, mult) - else: res_1 = False + else: + res_1 = False if i < len(self.c.offset_nevs) - 1: bnds = [(0, self.c.offset.md[i + 1] - self.c.offset.md[i])] @@ -217,12 +227,22 @@ def _get_closest_points(self): ) mult = res_2.x[0] / (bnds[0][1] - bnds[0][0]) sigma_new_2 = self._interpolate_covs(i + 1, mult) - else: res_2 = False + else: + res_2 = False if res_1 and res_2 and res_1.fun < res_2.fun or not res_2: - closest.append((station, interpolate_survey(self.c.offset, res_1.x[0], i - 1), res_1, sigma_new_1)) + closest.append(( + station, + interpolate_survey(self.c.offset, res_1.x[0], i - 1), + res_1, sigma_new_1 + )) else: - closest.append((station, interpolate_survey(self.c.offset, res_2.x[0], i), res_2, sigma_new_2)) + closest.append(( + station, + interpolate_survey(self.c.offset, res_2.x[0], i), + res_2, + sigma_new_2 + )) self.closest = closest md, inc, azi, n, e, tvd, x, y, z, = np.array([ @@ -265,8 +285,8 @@ def _get_closest_points(self): error_model=None, cov_hla=cov_hla, cov_nev=cov_nev, - start_xyz=[x[0],y[0],z[0]], - start_nev=[n[0],e[0],tvd[0]], + start_xyz=[x[0], y[0], z[0]], + start_nev=[n[0], e[0], tvd[0]], deg=False, unit=self.c.offset.unit ) @@ -293,7 +313,7 @@ def _fun(self, x, survey, index, station): def _get_delta_nev_vectors(self): temp = self.off_nevs - self.c.ref_nevs - self.dist_CC_Clr = norm(temp, axis=-1).reshape(-1,1) + self.dist_CC_Clr = norm(temp, axis=-1).reshape(-1, 1) with np.errstate(divide='ignore', invalid='ignore'): self.ref_delta_nevs = np.nan_to_num( temp / self.dist_CC_Clr, @@ -310,7 +330,7 @@ def _get_delta_nev_vectors(self): ) self.hoz_bearing = np.arctan2( - self.ref_delta_nevs[:,1], self.ref_delta_nevs[:,0] + self.ref_delta_nevs[:, 1], self.ref_delta_nevs[:, 0] ) self.hoz_bearing_deg = (np.degrees(self.hoz_bearing) + 360) % 360 @@ -333,11 +353,9 @@ def _get_delta_hla_vectors(self): def _get_covs(self): self.ref_cov_hla = self.c.ref.cov_hla self.ref_cov_nev = self.c.ref.cov_nev - self.off_cov_hla = self.off.cov_hla self.off_cov_nev = self.off.cov_nev - def _get_PCRs(self): self.ref_PCR = np.hstack([ np.sqrt(np.dot(np.dot(vec, cov), vec.T)) @@ -351,6 +369,7 @@ def _get_PCRs(self): def _get_calc_hole(self): self.calc_hole = self.c.Rr + self.c.Ro[self.idx] + class MeshClearance: def __init__( self, @@ -418,8 +437,11 @@ def get_lines(self): [(start_points.append(p[0]), end_points.append(p[1])) for p in points] - return np.array([np.vstack(start_points).tolist(), np.vstack(end_points).tolist()]) - + return np.array([ + np.vstack(start_points).tolist(), + np.vstack(end_points).tolist() + ]) + def _get_mesh(self, survey, offset=False): """ Generates a mesh object from the survey object. @@ -449,7 +471,6 @@ def _process_well(self): off_nevs = self.c.offset_nevs for i in range(len(ref.md) - 1): - # slice a well section and create section survey s = slice_survey(ref, i) @@ -462,7 +483,9 @@ def _process_well(self): ) if self.return_data: - distance = self.cm.min_distance_single(m, return_name=True, return_data=True) + distance = self.cm.min_distance_single( + m, return_name=True, return_data=True + ) closest_point_reference = distance[2].point("__external") name_offset_absolute = distance[1] closest_point_offset = distance[2].point(name_offset_absolute) @@ -470,8 +493,8 @@ def _process_well(self): ref_nev = self._get_closest_nev(s, closest_point_reference) ref_md = ref.md[i-1] + ref_nev[1].x[0] - # find the closest point on the well trajectory to the closest points - # on the mesh surface + # find the closest point on the well trajectory to the closest + # points on the mesh surface off_index = KDTree(off_nevs).query(closest_point_offset)[1] if off_index < len(off.md) - 1: s = slice_survey(off, off_index) @@ -501,7 +524,9 @@ def _process_well(self): vec = off_nev[0] - ref_nev[0] distance_CC = norm(vec) - hoz_bearing_deg = (np.degrees(np.arctan2(vec[1], vec[0])) + 360) % 360 + hoz_bearing_deg = ( + np.degrees(np.arctan2(vec[1], vec[0])) + 360 + ) % 360 if collision[0] is True: depth = norm( @@ -523,8 +548,16 @@ def _process_well(self): self.SF.append(round(SF, 2)) self.nev.append((ref_nev, off_nev)) self.hoz_bearing_deg.append(hoz_bearing_deg) - self.ref_PCR.append((ref_nev[1].fun - self.c.sigma_pa / 2 - self.Rr[i]) / self.sigma) - self.off_PCR.append((off_nev[1].fun - self.c.sigma_pa / 2 - self.Ro[off_index] - self.c.Sm) / self.sigma) + self.ref_PCR.append( + (ref_nev[1].fun - self.c.sigma_pa / 2 - self.Rr[i]) + / self.sigma + ) + self.off_PCR.append( + ( + off_nev[1].fun - self.c.sigma_pa / 2 + - self.Ro[off_index] - self.c.Sm + ) / self.sigma + ) self.calc_hole.append(ref.radius[i] + off.radius[off_index]) self.ref_md.append(ref_md) self.off_md.append(off_md) @@ -546,7 +579,7 @@ def _fun(self, x, survey, pos): dist = norm(new_pos - pos, axis=-1) return dist - + def _get_closest_nev(self, survey, pos): """ Using an optimization function to determine the closest @@ -566,13 +599,3 @@ def _get_closest_nev(self, survey, pos): nev = np.array([s.n, s.e, s.tvd]).T[-1] return (nev, res) - - - - - - - - - - From 3d15fc8dbb7d7d2a2c2f1bedf04ad7e5aae7b62b Mon Sep 17 00:00:00 2001 From: Matt Hall Date: Tue, 22 Dec 2020 09:41:39 -0400 Subject: [PATCH 05/10] licence correction and python tags in code blocks --- README.md | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7640612..454fd24 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # welleng + [![Open Source Love svg2](https://badges.frapsoft.com/os/v2/open-source.svg?v=103)](https://github.com/pro-well-plan/pwptemp/blob/master/LICENSE.md) [![PyPI version](https://badge.fury.io/py/welleng.svg)](https://badge.fury.io/py/welleng) [![Downloads](https://static.pepy.tech/personalized-badge/welleng?period=total&units=international_system&left_color=grey&right_color=orange&left_text=Downloads)](https://pepy.tech/project/welleng) @@ -11,9 +12,11 @@ - Calculate well bore clearance and Separation Factors (SF) - standard [ISCWSA] method - new mesh based method using the [Flexible Collision Library] + ## New Features! + - **Import from Landmark .wbp files:** using the `exchange.wbp` module it's now possible to import .wbp files exported from Landmark's COMPASS or DecisionSpace software. - ``` + ```python import welleng as we wp = we.exchange.wbp.load(demo.wbp) # import file @@ -21,42 +24,53 @@ survey = we.exchange.wbp.wbp_to_survey(wp, step=30) # convert to survey mesh = we.mesh.WellMesh(survey, method='circle') # convert to mesh we.visual.plot(m.mesh) # plot the mesh ``` + - **Export to .wbp files *(experiemental)*:** using the `exchange.wbp` module, it's possible to convert a planned survey file into a list of turn points that can be exported to a .wbp file. - ``` + ```python import welleng as we wp = we.exchange.wbp.WellPlan(survey) # convert Survey to WellPlan object doc = we.exchange.wbp.export(wp) # create a .wbp document we.exchange.wbp.save_to_file(doc, f"demo.wbp") # save the document to file ``` + - **Well Path Creation:** the addition of the `connector` module enables drilling well paths to be created simply by providing start and end locations (with some vector data like inclination and azimuth). No need to indicate *how* to connect the points, the module will figure that out itself. - **Fast visualization of well trajectory meshes:** addition of the `visual` module for quick and simple viewing and QAQC of well meshes. - **Mesh Based Collision Detection:** the current method for determining the Separation Factor between wells is constrained by the frequency and location of survey stations or necessitates interpolation of survey stations in order to determine if Anti-Collision Rules have been violated. Meshing the well bore interpolates between survey stations and as such is a more reliable method for identifying potential well bore collisions, especially wth more sparse data sets. - More coming soon! + ## Tech + [welleng] uses a number of open source projects to work properly: * [trimesh] - awesome library for loading and using triangular meshes * [numpy] - the fundamental package for scientific computing with Python * [scipy] - a Python-based ecosystem of open-source software for mathematics, science, and engineering * [vedo] - a python module for scientific visualization, analysis of 3D objects and point clouds based on VTK. + ## Installation + [welleng] requires [trimesh], [numpy] and [scipy] to run. Other libraries are optional depending on usage and to get [python-fcl] running on which [trimesh] is built may require some additional installations. Other than that, it should be an easy pip install to get up and running with welleng and the minimum dependencies. -``` +```python pip install welleng ``` + For developers, the repository can be cloned and locally installed in your GitHub directory via your preferred Python env. -``` + +```python git clone https://github.com/jonnymaserati/welleng.git cd welleng pip install -e . ``` + Make sure you include that `.` in the final line (it's not a typo) as this ensures that any changes to your development version are immediately implemented on save. + ## Quick Start + Here's an example using `welleng` to construct a couple of simple well trajectories with the `connector` module, creating survey listings for the wells with well bore uncertainty data, using these surveys to create well bore meshes and finally printing the results and plotting the meshes with the closest lines and SF data. -``` +```python import welleng as we import numpy as np from tabulate import tabulate @@ -143,6 +157,7 @@ we.visual.plot( print("Done!") ``` + This results in a quick, interactive visualization of the well meshes that's great for QAQC. What's interesting about these results is that the ISCWSA method does not explicitly detect a collision in this scenario wheras the mesh method does. ![image](https://user-images.githubusercontent.com/41046859/102106351-c0dd1a00-3e30-11eb-82f0-a0454dfce1c6.png) @@ -154,6 +169,7 @@ For more examples, including how to build a well trajectory by joining up a seri Well trajectory generated by [build_a_well_from_sections.py] ## Todos + - Add a Target class to see what you're aiming for - **in progress** - Export to Landmark's .wbp format so survey listings can be modified in COMPASS - **in progress** - Documentation @@ -172,10 +188,9 @@ Equinor's Volve Wells The ISCWSA standard set of well paths for evaluating clearance scenarios and Equinor's [volve] wells have been rendered in [blender] above. See the [examples] for the code used to generate the [volve] scene, extracting the data from the [volve] EDM.xml file. -License ----- +## License -LGPL v3 +[Apache 2.0](LICENSE) Please note the terms of the license. Although this software endeavors to be accurate, it should not be used as is for real wells. If you want a production version or wish to develop this software for a particular application, then please get in touch with [jonnycorcutt], but the intent of this library is to assist development. From d596188b9ddec11916bd8cc7139b5bfd86611887 Mon Sep 17 00:00:00 2001 From: Matt Hall Date: Tue, 22 Dec 2020 09:45:23 -0400 Subject: [PATCH 06/10] Update `license` field in setup --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 06429fb..e7c69de 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ ], author='Jonathan Corcutt', author_email='jonnycorcutt@gmail.com', - license='LGPL v3', + license='Apache 2.0', packages=find_packages(exclude=["tests"]), install_requires=[ 'python-fcl', From 24ac82de9a3c74cc9e7c7aa9e3838a6855fdc754 Mon Sep 17 00:00:00 2001 From: jonnymaserati Date: Tue, 22 Dec 2020 14:51:53 +0100 Subject: [PATCH 07/10] Add and reformat. --- tests/__init__.py | 0 tests/test_connector.py | 77 ++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 40 deletions(-) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_connector.py b/tests/test_connector.py index 448510f..51d39fa 100644 --- a/tests/test_connector.py +++ b/tests/test_connector.py @@ -1,9 +1,9 @@ -import welleng as we +from welleng.connector import Connector import numpy as np # test hold with only md provided -c1 = we.connector.Connector( - vec1=[0,0,1], +c1 = Connector( + vec1=[0, 0, 1], md2=500, ) assert ( @@ -16,37 +16,37 @@ c1_survey = c1.survey() # # test with md2 and vec2 provided (minimum curvature) -c2 = we.connector.Connector( - vec1=[0,0,1], +c2 = Connector( + vec1=[0, 0, 1], md2=1000, - vec2=[0,1,0] + vec2=[0, 1, 0] ) # c2_survey = c2.survey() # test with pos2 provided (minimum distance) -c3 = we.connector.Connector( - vec1=[0,0,1], - pos2=[100,100,1000], +c3 = Connector( + vec1=[0, 0, 1], + pos2=[100, 100, 1000], ) assert c3.md_target > c3.pos1[2], "Failed c3" c3_survey = c3.survey() # test with pos2 needing more aggressive dls (minimum curvature) -c4 = we.connector.Connector( - vec1=[0,0,1], - pos2=[200,400,200] +c4 = Connector( + vec1=[0, 0, 1], + pos2=[200, 400, 200] ) assert c4.method == 'min_curve_to_target' # test with pos2 and vec2 provided () -vec1 = [-1,-1,1] -vec2 = [1,-1,0] -c5 = we.connector.Connector( - pos1=[0.,0.,0], +vec1 = [-1, -1, 1] +vec2 = [1, -1, 0] +c5 = Connector( + pos1=[0., 0., 0], vec1=vec1 / np.linalg.norm(vec1), - pos2=[0.,1000.,500.], + pos2=[0., 1000., 500.], vec2=vec2 / np.linalg.norm(vec2), ) assert c5.method == 'curve_hold_curve' @@ -54,51 +54,48 @@ # test if interpolator and survey functions are working c5_survey = c5.survey() -# test that a mesh can be generated from the survey data -c5_mesh = we.mesh.WellMesh(c5_survey, method='circle') - # test with pos2, inc1 and azi1 provided () -c6 = we.connector.Connector( - pos1=[0.,0.,0], +c6 = Connector( + pos1=[0., 0., 0], inc1=0., azi1=90, - pos2=[1000.,1000.,1000.], - vec2=[0.,0.,1.], + pos2=[1000., 1000., 1000.], + vec2=[0., 0., 1.], ) # test with different dls for second curve section -c7 = we.connector.Connector( - pos1=[0.,0.,0], - vec1=[0.,0.,1.], - pos2=[0.,100.,1000.], - vec2=[0.,0.,1.], +c7 = Connector( + pos1=[0., 0., 0], + vec1=[0., 0., 1.], + pos2=[0., 100., 1000.], + vec2=[0., 0., 1.], dls_design2=5 ) # test with dls_critical requirement (actual dls < dls_design) -c8 = we.connector.Connector( - pos1=[0.,0.,0], - vec1=[0.,0.,1.], - pos2=[0.,100.,100.], - vec2=[0.,0.,1.], +c8 = Connector( + pos1=[0., 0., 0], + vec1=[0., 0., 1.], + pos2=[0., 100., 100.], + vec2=[0., 0., 1.], ) assert c8.radius_critical < c8.radius_design # test min_curve (inc2 provided) -c9 = we.connector.Connector( - pos1=[0.,0.,0], - vec1=[0.,0.,1.], +c9 = Connector( + pos1=[0., 0., 0], + vec1=[0., 0., 1.], inc2=30, ) assert c9.method == 'min_curve' # test min_curve with md less than required radius -c10 = we.connector.Connector( - pos1=[0.,0.,0], +c10 = Connector( + pos1=[0., 0., 0], inc1=0, azi1=0, md2=500, inc2=90, azi2=0, ) -assert c10.radius_critical < c10.radius_design \ No newline at end of file +assert c10.radius_critical < c10.radius_design From 34286b791ee4d56d4e4ae514d4bb8b0f9de2794a Mon Sep 17 00:00:00 2001 From: jonnymaserati Date: Tue, 22 Dec 2020 14:56:54 +0100 Subject: [PATCH 08/10] Update requirements.txt with PyYAML and bump version --- requirements.txt | 3 ++- welleng/version.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4d765cb..e243c37 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,4 +17,5 @@ six==1.15.0 tabulate==0.8.7 trimesh==3.8.18 vedo==2020.4.2 -vtk==9.0.1 \ No newline at end of file +vtk==9.0.1 +PyYAML==5.3.1 \ No newline at end of file diff --git a/welleng/version.py b/welleng/version.py index 47e46d9..13b7089 100644 --- a/welleng/version.py +++ b/welleng/version.py @@ -1 +1 @@ -__version__ = '0.1.10' \ No newline at end of file +__version__ = '0.1.11' From 876599f84f55b265b9e99abc31c8f5a9640569f3 Mon Sep 17 00:00:00 2001 From: jonnymaserati <41046859+jonnymaserati@users.noreply.github.com> Date: Tue, 22 Dec 2020 15:27:55 +0100 Subject: [PATCH 09/10] Update python-package.yml --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5b6afab..41d51ca 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -41,4 +41,4 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with pytest run: | - pytest + pytest test From 17180d361f6547a9cc70ca19ad54298389daaf57 Mon Sep 17 00:00:00 2001 From: jonnymaserati Date: Tue, 22 Dec 2020 15:48:39 +0100 Subject: [PATCH 10/10] Update test_connector.py --- {tests => test}/__init__.py | 0 test/test_connector.py | 121 ++++++++++++++++++++++++++++++++++++ tests/test_connector.py | 101 ------------------------------ welleng/connector.py | 2 +- 4 files changed, 122 insertions(+), 102 deletions(-) rename {tests => test}/__init__.py (100%) create mode 100644 test/test_connector.py delete mode 100644 tests/test_connector.py diff --git a/tests/__init__.py b/test/__init__.py similarity index 100% rename from tests/__init__.py rename to test/__init__.py diff --git a/test/test_connector.py b/test/test_connector.py new file mode 100644 index 0000000..d5026b8 --- /dev/null +++ b/test/test_connector.py @@ -0,0 +1,121 @@ +from welleng.connector import Connector +from welleng.survey import Survey +import numpy as np + + +def test_md_hold(): + # test hold with only md provided + c = Connector( + vec1=[0, 0, 1], + md2=500, + ) + assert ( + c.inc_target == c.inc1 + and c.azi_target == c.azi1 + and c.pos_target[2] == c.md_target + ), "Failed c1" + assert c.method == 'hold', "Unexpected method" + + c.survey() + + +def test_md_and_vec(): + # test with md2 and vec2 provided (minimum curvature) + c = Connector( + vec1=[0, 0, 1], + md2=1000, + vec2=[0, 1, 0] + ) + assert c.method == 'min_curve' + + +def test_pos(): + # test with pos2 provided (minimum distance) + c = Connector( + vec1=[0, 0, 1], + pos2=[100, 100, 1000], + ) + assert c.md_target > c.pos1[2], "Failed c3" + + +def test_pos_and_dls(): + # test with pos2 needing more aggressive dls (minimum curvature) + c = Connector( + vec1=[0, 0, 1], + pos2=[200, 400, 200] + ) + assert c.method == 'min_curve_to_target' + + +def test_pos_and_vec(): + # test with pos2 and vec2 provided + vec1 = [-1, -1, 1] + vec2 = [1, -1, 0] + c = Connector( + pos1=[0., 0., 0], + vec1=vec1 / np.linalg.norm(vec1), + pos2=[0., 1000., 500.], + vec2=vec2 / np.linalg.norm(vec2), + ) + assert c.method == 'curve_hold_curve' + + # test if interpolator and survey functions are working + assert isinstance(c.survey(), Survey) + + +def test_pos_inc_azi(): + # test with pos2, inc1 and azi1 provided + c = Connector( + pos1=[0., 0., 0], + inc1=0., + azi1=90, + pos2=[1000., 1000., 1000.], + vec2=[0., 0., 1.], + ) + assert c.method == 'curve_hold_curve' + + +def test_dls2(): + # test with different dls for second curve section + c = Connector( + pos1=[0., 0., 0], + vec1=[0., 0., 1.], + pos2=[0., 100., 1000.], + vec2=[0., 0., 1.], + dls_design2=5 + ) + assert c.radius_design2 < c.radius_design + + +def test_radius_critical(): + # test with dls_critical requirement (actual dls < dls_design) + c = Connector( + pos1=[0., 0., 0], + vec1=[0., 0., 1.], + pos2=[0., 100., 100.], + vec2=[0., 0., 1.], + ) + assert c.radius_critical < c.radius_design + + +def test_min_curve(): + # test min_curve (inc2 provided) + c = Connector( + pos1=[0., 0., 0], + vec1=[0., 0., 1.], + inc2=30, + ) + assert c.method == 'min_curve' + + +def test_radius_critical_with_min_curve(): + # test min_curve with md less than required radius + c = Connector( + pos1=[0., 0., 0], + inc1=0, + azi1=0, + md2=500, + inc2=90, + azi2=0, + ) + assert c.radius_critical < c.radius_design diff --git a/tests/test_connector.py b/tests/test_connector.py deleted file mode 100644 index 51d39fa..0000000 --- a/tests/test_connector.py +++ /dev/null @@ -1,101 +0,0 @@ -from welleng.connector import Connector -import numpy as np - -# test hold with only md provided -c1 = Connector( - vec1=[0, 0, 1], - md2=500, -) -assert ( - c1.inc_target == c1.inc1 - and c1.azi_target == c1.azi1 - and c1.pos_target[2] == c1.md_target -), "Failed c1" -assert c1.method == 'hold' - -c1_survey = c1.survey() - -# # test with md2 and vec2 provided (minimum curvature) -c2 = Connector( - vec1=[0, 0, 1], - md2=1000, - vec2=[0, 1, 0] -) - -# c2_survey = c2.survey() - -# test with pos2 provided (minimum distance) -c3 = Connector( - vec1=[0, 0, 1], - pos2=[100, 100, 1000], -) -assert c3.md_target > c3.pos1[2], "Failed c3" - -c3_survey = c3.survey() - -# test with pos2 needing more aggressive dls (minimum curvature) -c4 = Connector( - vec1=[0, 0, 1], - pos2=[200, 400, 200] -) -assert c4.method == 'min_curve_to_target' - -# test with pos2 and vec2 provided () -vec1 = [-1, -1, 1] -vec2 = [1, -1, 0] -c5 = Connector( - pos1=[0., 0., 0], - vec1=vec1 / np.linalg.norm(vec1), - pos2=[0., 1000., 500.], - vec2=vec2 / np.linalg.norm(vec2), -) -assert c5.method == 'curve_hold_curve' - -# test if interpolator and survey functions are working -c5_survey = c5.survey() - -# test with pos2, inc1 and azi1 provided () -c6 = Connector( - pos1=[0., 0., 0], - inc1=0., - azi1=90, - pos2=[1000., 1000., 1000.], - vec2=[0., 0., 1.], -) - -# test with different dls for second curve section -c7 = Connector( - pos1=[0., 0., 0], - vec1=[0., 0., 1.], - pos2=[0., 100., 1000.], - vec2=[0., 0., 1.], - dls_design2=5 -) - -# test with dls_critical requirement (actual dls < dls_design) -c8 = Connector( - pos1=[0., 0., 0], - vec1=[0., 0., 1.], - pos2=[0., 100., 100.], - vec2=[0., 0., 1.], -) -assert c8.radius_critical < c8.radius_design - -# test min_curve (inc2 provided) -c9 = Connector( - pos1=[0., 0., 0], - vec1=[0., 0., 1.], - inc2=30, -) -assert c9.method == 'min_curve' - -# test min_curve with md less than required radius -c10 = Connector( - pos1=[0., 0., 0], - inc1=0, - azi1=0, - md2=500, - inc2=90, - azi2=0, -) -assert c10.radius_critical < c10.radius_design diff --git a/welleng/connector.py b/welleng/connector.py index a72f994..e87364b 100644 --- a/welleng/connector.py +++ b/welleng/connector.py @@ -456,7 +456,7 @@ def _min_curve(self): self.pos_target = self.pos2 + ( self.vec2 * (self.md_target - self.md2) ) - else: + else: self.dist_curve, self.func_dogleg = get_curve_hold_data( self.radius_critical, self.dogleg )