corva-worker-python 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- corva_worker_python-2.0.0.dist-info/METADATA +30 -0
- corva_worker_python-2.0.0.dist-info/RECORD +63 -0
- corva_worker_python-2.0.0.dist-info/WHEEL +5 -0
- corva_worker_python-2.0.0.dist-info/top_level.txt +1 -0
- worker/__init__.py +5 -0
- worker/app/__init__.py +291 -0
- worker/app/modules/__init__.py +265 -0
- worker/app/modules/activity_module.py +141 -0
- worker/app/modules/connection_module.py +21 -0
- worker/app/modules/depth_activity_module.py +21 -0
- worker/app/modules/scheduler.py +44 -0
- worker/app/modules/time_activity_module.py +21 -0
- worker/app/modules/trigger.py +43 -0
- worker/constants.py +51 -0
- worker/data/__init__.py +0 -0
- worker/data/activity/__init__.py +132 -0
- worker/data/activity/activity_grouping.py +242 -0
- worker/data/alert.py +89 -0
- worker/data/api.py +155 -0
- worker/data/enums.py +141 -0
- worker/data/json_encoder.py +18 -0
- worker/data/math.py +104 -0
- worker/data/operations.py +477 -0
- worker/data/serialization.py +110 -0
- worker/data/task_handler.py +82 -0
- worker/data/two_way_dict.py +17 -0
- worker/data/unit_conversions.py +5 -0
- worker/data/wits.py +323 -0
- worker/event/__init__.py +53 -0
- worker/event/event_handler.py +90 -0
- worker/event/scheduled.py +64 -0
- worker/event/stream.py +48 -0
- worker/exceptions.py +26 -0
- worker/mixins/__init__.py +0 -0
- worker/mixins/logging.py +119 -0
- worker/mixins/rollbar.py +87 -0
- worker/partial_rerun_merge/__init__.py +0 -0
- worker/partial_rerun_merge/merge.py +500 -0
- worker/partial_rerun_merge/models.py +91 -0
- worker/partial_rerun_merge/progress.py +241 -0
- worker/state/__init__.py +96 -0
- worker/state/mixins.py +111 -0
- worker/state/state.py +46 -0
- worker/test/__init__.py +3 -0
- worker/test/lambda_function_test_run.py +196 -0
- worker/test/local_testing/__init__.py +0 -0
- worker/test/local_testing/to_local_transfer.py +360 -0
- worker/test/utils.py +51 -0
- worker/wellbore/__init__.py +0 -0
- worker/wellbore/factory.py +496 -0
- worker/wellbore/measured_depth_finder.py +12 -0
- worker/wellbore/model/__init__.py +0 -0
- worker/wellbore/model/ann.py +103 -0
- worker/wellbore/model/annulus.py +113 -0
- worker/wellbore/model/drillstring.py +196 -0
- worker/wellbore/model/drillstring_components.py +439 -0
- worker/wellbore/model/element.py +102 -0
- worker/wellbore/model/enums.py +92 -0
- worker/wellbore/model/hole.py +297 -0
- worker/wellbore/model/hole_section.py +51 -0
- worker/wellbore/model/riser.py +22 -0
- worker/wellbore/sections_mixin.py +64 -0
- worker/wellbore/wellbore.py +289 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from worker.data.serialization import serialization
|
|
4
|
+
from worker.wellbore.measured_depth_finder import get_unique_measured_depths
|
|
5
|
+
from worker.wellbore.model.ann import Ann
|
|
6
|
+
from worker.wellbore.model.drillstring import Drillstring
|
|
7
|
+
from worker.wellbore.model.enums import PipeType
|
|
8
|
+
from worker.wellbore.model.hole import Hole
|
|
9
|
+
from worker.wellbore.sections_mixin import SectionsMixin
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@serialization
|
|
13
|
+
class Annulus(SectionsMixin):
|
|
14
|
+
SERIALIZED_VARIABLES = {"sections": list}
|
|
15
|
+
|
|
16
|
+
def __init__(self, *args, **kwargs):
|
|
17
|
+
super().__init__(*args, **kwargs)
|
|
18
|
+
|
|
19
|
+
self.sections: List[Ann] = kwargs.get("sections") or []
|
|
20
|
+
|
|
21
|
+
drillstring: Drillstring = kwargs.get("drillstring")
|
|
22
|
+
hole: Hole = kwargs.get("hole")
|
|
23
|
+
if drillstring and hole:
|
|
24
|
+
self.create_annulus(drillstring, hole)
|
|
25
|
+
|
|
26
|
+
def add_section(self, section: Ann):
|
|
27
|
+
"""
|
|
28
|
+
Add a new ann section to the end of the sections.
|
|
29
|
+
:param section:
|
|
30
|
+
:return:
|
|
31
|
+
"""
|
|
32
|
+
if self:
|
|
33
|
+
section.top_depth = self[-1].bottom_depth
|
|
34
|
+
|
|
35
|
+
section.set_bottom_depth()
|
|
36
|
+
|
|
37
|
+
self.sections.append(section)
|
|
38
|
+
|
|
39
|
+
def insert_section(self, section: Ann, index: int):
|
|
40
|
+
"""
|
|
41
|
+
Insert a new pipe element in the given index.
|
|
42
|
+
:param section:
|
|
43
|
+
:param index:
|
|
44
|
+
:return:
|
|
45
|
+
"""
|
|
46
|
+
if index is not None:
|
|
47
|
+
if self:
|
|
48
|
+
section.top_depth = self[index - 1].bottom_depth
|
|
49
|
+
section.set_bottom_depth()
|
|
50
|
+
|
|
51
|
+
self.sections.insert(index, section)
|
|
52
|
+
|
|
53
|
+
for i in range(index + 1, len(self)):
|
|
54
|
+
self[i].top_depth = self[i - 1].bottom_depth
|
|
55
|
+
self[i].set_bottom_depth()
|
|
56
|
+
|
|
57
|
+
def check_for_concatenation(self):
|
|
58
|
+
"""
|
|
59
|
+
Concatenate the adjacent section with the same ID and OD and hole type
|
|
60
|
+
"""
|
|
61
|
+
for i in range(len(self) - 2, -1, -1):
|
|
62
|
+
if self[i].eq_without_length(self[i + 1]):
|
|
63
|
+
self[i].set_bottom_depth(self[i + 1].bottom_depth)
|
|
64
|
+
self[i].set_length()
|
|
65
|
+
del self[i + 1]
|
|
66
|
+
|
|
67
|
+
def create_annulus(self, drillstring: Drillstring, hole: Hole):
|
|
68
|
+
"""
|
|
69
|
+
Create annulus for a given drillstring and hole configuration
|
|
70
|
+
:return:
|
|
71
|
+
"""
|
|
72
|
+
self.sections = []
|
|
73
|
+
|
|
74
|
+
md_list = get_unique_measured_depths(drillstring, hole)
|
|
75
|
+
md_list = [md for md in md_list if md <= drillstring[-1].bottom_depth]
|
|
76
|
+
epsilon = 0.0001
|
|
77
|
+
for i in range(len(md_list) - 1):
|
|
78
|
+
hole_section = hole.find_section_at_measured_depth(md_list[i] + epsilon, True)
|
|
79
|
+
if not hole_section:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
drillstring_section = drillstring.find_section_at_measured_depth(md_list[i] + epsilon, False)
|
|
83
|
+
annulus_section = Ann(**{"top_depth": md_list[i], "bottom_depth": md_list[i + 1]})
|
|
84
|
+
|
|
85
|
+
# If there is no drillstring_section when the bit is at surface (0ft) or once we reach the bit in the loop
|
|
86
|
+
# stop looping
|
|
87
|
+
if not drillstring_section or drillstring_section.pipe_type == PipeType.BIT:
|
|
88
|
+
break
|
|
89
|
+
|
|
90
|
+
annulus_section.set_properties_from_pipe_section(drillstring_section)
|
|
91
|
+
annulus_section.set_properties_from_hole(hole_section)
|
|
92
|
+
|
|
93
|
+
self.add_section(annulus_section)
|
|
94
|
+
|
|
95
|
+
if not self:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# extend the last segment to be equal to the bit depth since the bit has been removed
|
|
99
|
+
bit_depth = drillstring.bit_depth
|
|
100
|
+
self[-1].set_bottom_depth(bit_depth)
|
|
101
|
+
self[-1].set_length()
|
|
102
|
+
|
|
103
|
+
self.check_for_concatenation()
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def volume(self):
|
|
107
|
+
return sum(sec.volume for sec in self)
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def bottom_depth(self):
|
|
111
|
+
if not self:
|
|
112
|
+
return 0
|
|
113
|
+
return self[-1].bottom_depth
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from typing import List, Union
|
|
3
|
+
|
|
4
|
+
from worker.data.operations import get_data_by_path
|
|
5
|
+
from worker.data.serialization import serialization
|
|
6
|
+
from worker.wellbore.model.drillstring_components import MWD, PDM, RSS, Agitator, Bit, Pipe, UnderReamer
|
|
7
|
+
from worker.wellbore.model.enums import PipeType
|
|
8
|
+
from worker.wellbore.model.hole import Hole
|
|
9
|
+
from worker.wellbore.sections_mixin import SectionsMixin
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@serialization
|
|
13
|
+
class Drillstring(SectionsMixin):
|
|
14
|
+
SERIALIZED_VARIABLES = {"sections": list, "mongo_id": str}
|
|
15
|
+
|
|
16
|
+
def __init__(self, *args, **kwargs):
|
|
17
|
+
super().__init__(*args, **kwargs)
|
|
18
|
+
|
|
19
|
+
self.mongo_id = kwargs.get("mongo_id")
|
|
20
|
+
self.sections: List[Union[Pipe, Bit]] = kwargs.get("sections") or []
|
|
21
|
+
if args:
|
|
22
|
+
self.set_drillstring(args[0])
|
|
23
|
+
|
|
24
|
+
def add_section(self, section: Union[Pipe, Bit]):
|
|
25
|
+
"""
|
|
26
|
+
Add a new pipe element to the end of the drillstring.
|
|
27
|
+
:param section:
|
|
28
|
+
:return:
|
|
29
|
+
"""
|
|
30
|
+
section.top_depth = 0 if not self else self[-1].bottom_depth
|
|
31
|
+
# length is the source of truth, so we are updating the bottom depth
|
|
32
|
+
section.set_bottom_depth()
|
|
33
|
+
|
|
34
|
+
self.sections.append(section)
|
|
35
|
+
|
|
36
|
+
def insert_section(self, section: Union[Pipe, Bit], index: int):
|
|
37
|
+
"""
|
|
38
|
+
Insert a new pipe element in the given index.
|
|
39
|
+
:param section:
|
|
40
|
+
:param index:
|
|
41
|
+
:return:
|
|
42
|
+
"""
|
|
43
|
+
if index is not None:
|
|
44
|
+
if self:
|
|
45
|
+
section.top_depth = self[index - 1].bottom_depth
|
|
46
|
+
section.set_bottom_depth()
|
|
47
|
+
|
|
48
|
+
self.sections.insert(index, section)
|
|
49
|
+
|
|
50
|
+
for i in range(index + 1, len(self)):
|
|
51
|
+
self[i].top_depth = self[i - 1].bottom_depth
|
|
52
|
+
self[i].set_bottom_depth()
|
|
53
|
+
|
|
54
|
+
def update(self, bit_depth: Union[dict, float]):
|
|
55
|
+
"""
|
|
56
|
+
This method gets the bit depth and chops the current drill string to fit the criteria in the wellbore.
|
|
57
|
+
If the drill string length is more than the bit measured depth it chops the extra length from
|
|
58
|
+
surface. And if it is short in length then it extends the top section to the surface.
|
|
59
|
+
In addition, the indices of the special tools are updated accordingly, when the drillstring is chopped.
|
|
60
|
+
:param bit_depth: a wits dict or actual bit depth
|
|
61
|
+
:return:
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
if isinstance(bit_depth, dict):
|
|
65
|
+
bit_depth = get_data_by_path(bit_depth, "data.bit_depth", float)
|
|
66
|
+
|
|
67
|
+
self[-1].set_bottom_depth(bit_depth)
|
|
68
|
+
self[-1].set_top_depth()
|
|
69
|
+
|
|
70
|
+
for i in range(len(self) - 2, -1, -1):
|
|
71
|
+
self[i].set_bottom_depth(self[i + 1].top_depth)
|
|
72
|
+
self[i].set_top_depth()
|
|
73
|
+
|
|
74
|
+
if self[i].bottom_depth < 0:
|
|
75
|
+
del self.sections[i]
|
|
76
|
+
|
|
77
|
+
self[0].top_depth = 0
|
|
78
|
+
self[0].set_length()
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def bit_depth(self):
|
|
82
|
+
if not self:
|
|
83
|
+
return None
|
|
84
|
+
return self[-1].bottom_depth
|
|
85
|
+
|
|
86
|
+
def set_drillstring(self, drillstring: dict):
|
|
87
|
+
"""
|
|
88
|
+
This gets the whole drillstring json and sets it
|
|
89
|
+
:return:
|
|
90
|
+
"""
|
|
91
|
+
if not drillstring:
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
self.mongo_id = drillstring.get("_id")
|
|
95
|
+
|
|
96
|
+
self.sections = []
|
|
97
|
+
|
|
98
|
+
# family to class mapping
|
|
99
|
+
mapping = {"bit": Bit, "agitator": Agitator, "pdm": PDM, "mwd": MWD, "rss": RSS, "ur": UnderReamer}
|
|
100
|
+
|
|
101
|
+
drillstring_components = drillstring.get("data", {}).get("components", [])
|
|
102
|
+
for component in drillstring_components:
|
|
103
|
+
family = component.get("family", None)
|
|
104
|
+
if family in mapping:
|
|
105
|
+
component = mapping[family](**component)
|
|
106
|
+
else:
|
|
107
|
+
component = Pipe(**component)
|
|
108
|
+
|
|
109
|
+
self.add_section(component)
|
|
110
|
+
|
|
111
|
+
def perform_qc(self, hole: Hole):
|
|
112
|
+
"""
|
|
113
|
+
Removes the items that the pipe OD is equal to or greater than the bit/hole size or OD is equal to 0.
|
|
114
|
+
If the pipe id is zero then set it equal to 0.5 of the OD
|
|
115
|
+
:return:
|
|
116
|
+
"""
|
|
117
|
+
bit_diameter = self.get_bit().size
|
|
118
|
+
|
|
119
|
+
self.sections = [sec for sec in self.sections if 0 < sec.outer_diameter < bit_diameter]
|
|
120
|
+
|
|
121
|
+
for component in self.sections:
|
|
122
|
+
if component.inner_diameter == 0:
|
|
123
|
+
component.inner_diameter = component.outer_diameter / 2
|
|
124
|
+
|
|
125
|
+
def compute_get_inside_volume(self):
|
|
126
|
+
"""
|
|
127
|
+
Compute and get the inside volume of the pipes of the drill string only,
|
|
128
|
+
not the body and outside of it.
|
|
129
|
+
:return: volume in FOOT^3
|
|
130
|
+
"""
|
|
131
|
+
return sum(sec.compute_get_volume() for sec in self)
|
|
132
|
+
|
|
133
|
+
def compute_get_outside_volume(self):
|
|
134
|
+
"""
|
|
135
|
+
Compute and get the outside volume of the pipes of the drill string only,
|
|
136
|
+
using outer diameter
|
|
137
|
+
:return: volume in FOOT^3
|
|
138
|
+
"""
|
|
139
|
+
ds = self.get_a_copy_without_bit()
|
|
140
|
+
return sum(sec.compute_outer_volume_tj_adjusted() for sec in ds)
|
|
141
|
+
|
|
142
|
+
def compute_get_body_volume(self):
|
|
143
|
+
"""
|
|
144
|
+
Compute and get the body volume of the pipes of the drill string only,
|
|
145
|
+
not inside and outside of it.
|
|
146
|
+
:return: volume in FOOT^3
|
|
147
|
+
"""
|
|
148
|
+
ds = self.get_a_copy_without_bit()
|
|
149
|
+
return sum(sec.compute_body_volume_tj_adjusted() for sec in ds)
|
|
150
|
+
|
|
151
|
+
def get_a_copy_without_bit(self):
|
|
152
|
+
"""
|
|
153
|
+
To get a deep copy of the current drillstring without the bit
|
|
154
|
+
:return:
|
|
155
|
+
"""
|
|
156
|
+
ds = deepcopy(self)
|
|
157
|
+
bit = ds.get_bit()
|
|
158
|
+
if bit:
|
|
159
|
+
ds.sections.remove(bit)
|
|
160
|
+
return ds
|
|
161
|
+
|
|
162
|
+
def get_bit(self):
|
|
163
|
+
"""
|
|
164
|
+
Get the bit element from the drillstring
|
|
165
|
+
:return:
|
|
166
|
+
"""
|
|
167
|
+
if not self:
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
bit = self[-1]
|
|
171
|
+
if isinstance(bit, Bit):
|
|
172
|
+
return bit
|
|
173
|
+
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
def get_under_reamer(self):
|
|
177
|
+
"""
|
|
178
|
+
Get the under-reamer element from the drillstring
|
|
179
|
+
:return:
|
|
180
|
+
"""
|
|
181
|
+
if not self:
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
return next((pipe for pipe in self if pipe.pipe_type == PipeType.UNDERREAMER), None)
|
|
185
|
+
|
|
186
|
+
def get_bha_length(self) -> float:
|
|
187
|
+
"""
|
|
188
|
+
To get the length of the BHA
|
|
189
|
+
:return:
|
|
190
|
+
"""
|
|
191
|
+
dp_indices = [index for index, pipe in enumerate(self) if pipe.pipe_type == PipeType.DP]
|
|
192
|
+
last_dp_index = -1
|
|
193
|
+
if dp_indices:
|
|
194
|
+
last_dp_index = max(dp_indices)
|
|
195
|
+
length = sum(el.length for el in self[last_dp_index + 1 :])
|
|
196
|
+
return length
|