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,289 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
import simplejson as json
|
|
5
|
+
|
|
6
|
+
from worker import API
|
|
7
|
+
from worker.data.json_encoder import JsonEncoder
|
|
8
|
+
from worker.data.operations import get_config_by_id, get_data_by_path, is_stream_app, nanround
|
|
9
|
+
from worker.data.serialization import serialization
|
|
10
|
+
from worker.mixins.logging import Logger
|
|
11
|
+
from worker.wellbore.model.annulus import Annulus
|
|
12
|
+
from worker.wellbore.model.drillstring import Drillstring
|
|
13
|
+
from worker.wellbore.model.drillstring_components import UnderReamer
|
|
14
|
+
from worker.wellbore.model.enums import HoleType
|
|
15
|
+
from worker.wellbore.model.hole import Hole
|
|
16
|
+
from worker.wellbore.model.hole_section import HoleSection
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@serialization
|
|
20
|
+
class Wellbore:
|
|
21
|
+
DEPTH_THRESHOLD = 50 # ft
|
|
22
|
+
|
|
23
|
+
SERIALIZED_VARIABLES = {
|
|
24
|
+
"original_drillstring": Drillstring,
|
|
25
|
+
"original_hole": Hole,
|
|
26
|
+
"bit_depth": float,
|
|
27
|
+
"hole_depth": float,
|
|
28
|
+
"last_hole_depth": float,
|
|
29
|
+
"last_non_decreasing_hole_depth": float,
|
|
30
|
+
"under_reamer_alert_timestamp": float,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def __init__(self, **kwargs):
|
|
34
|
+
super().__init__()
|
|
35
|
+
|
|
36
|
+
self.under_reamer: Union[UnderReamer, None] = None
|
|
37
|
+
self.under_reamer_alert_timestamp: float = 0.0
|
|
38
|
+
|
|
39
|
+
self.original_drillstring: Drillstring = kwargs.get("original_drillstring") or kwargs.get("drillstring")
|
|
40
|
+
self.actual_drillstring: Drillstring = kwargs.get("actual_drillstring")
|
|
41
|
+
|
|
42
|
+
# Explicitly checking if original_hole is not None, because bool of Hole with no sections evaluates to False
|
|
43
|
+
self.original_hole: Hole = (
|
|
44
|
+
kwargs.get("original_hole") if kwargs.get("original_hole") is not None else kwargs.get("hole")
|
|
45
|
+
)
|
|
46
|
+
self.actual_hole: Hole = kwargs.get("actual_hole")
|
|
47
|
+
|
|
48
|
+
self.actual_annulus: Annulus = kwargs.get("actual_annulus")
|
|
49
|
+
|
|
50
|
+
self.bit_depth: float = kwargs.get("bit_depth") or 0.0
|
|
51
|
+
self.hole_depth: float = kwargs.get("hole_depth")
|
|
52
|
+
|
|
53
|
+
self.last_hole_depth: float = kwargs.get("last_hole_depth") or self.hole_depth
|
|
54
|
+
self.last_non_decreasing_hole_depth: float = (
|
|
55
|
+
kwargs.get("last_non_decreasing_hole_depth") or self.last_hole_depth
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
mud_flow_in = kwargs.get("mud_flow_in") or 0.0
|
|
59
|
+
|
|
60
|
+
self.update(mud_flow_in=mud_flow_in)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def annulus(self):
|
|
64
|
+
return self.actual_annulus
|
|
65
|
+
|
|
66
|
+
def update(self, bit_depth: Union[float, dict] = None, hole_depth: float = None, mud_flow_in: float = None) -> None:
|
|
67
|
+
"""
|
|
68
|
+
:param bit_depth: a wits dict (that will be used for both bit and hole depths) or actual bit depth
|
|
69
|
+
:param hole_depth: actual hole depth value
|
|
70
|
+
:param mud_flow_in: mud flow rate to check under-reamer activation condition
|
|
71
|
+
:return:
|
|
72
|
+
"""
|
|
73
|
+
if mud_flow_in is None:
|
|
74
|
+
mud_flow_in = 0.0
|
|
75
|
+
|
|
76
|
+
if self.original_drillstring is None:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
if isinstance(bit_depth, dict):
|
|
80
|
+
bit_depth = get_data_by_path(bit_depth, "data.bit_depth", float)
|
|
81
|
+
hole_depth = get_data_by_path(bit_depth, "data.hole_depth", float)
|
|
82
|
+
|
|
83
|
+
self.bit_depth = bit_depth or self.bit_depth or 0.0
|
|
84
|
+
|
|
85
|
+
# a qc for the hole depth: if bit depth > hole depth override it with bit depth
|
|
86
|
+
if hole_depth is None:
|
|
87
|
+
hole_depth = self.hole_depth or self.original_hole.get_bottom_depth()
|
|
88
|
+
|
|
89
|
+
hole_depth = max(hole_depth, self.bit_depth)
|
|
90
|
+
self.hole_depth = hole_depth
|
|
91
|
+
|
|
92
|
+
self.actual_drillstring = deepcopy(self.original_drillstring)
|
|
93
|
+
self.actual_drillstring.update(self.bit_depth)
|
|
94
|
+
|
|
95
|
+
self.last_non_decreasing_hole_depth = max(self.last_non_decreasing_hole_depth, self.hole_depth)
|
|
96
|
+
|
|
97
|
+
self.actual_hole = deepcopy(self.original_hole)
|
|
98
|
+
self.actual_hole.update(self.hole_depth)
|
|
99
|
+
|
|
100
|
+
under_reamer: UnderReamer = self.actual_drillstring.get_under_reamer()
|
|
101
|
+
if under_reamer:
|
|
102
|
+
self.under_reamer = under_reamer
|
|
103
|
+
self.under_reamer.alerted_at_timestamp = self.under_reamer_alert_timestamp
|
|
104
|
+
self.add_under_reamer_sections(mud_flow_in)
|
|
105
|
+
self.under_reamer_alert_timestamp = self.under_reamer.alerted_at_timestamp
|
|
106
|
+
self.actual_hole.update(self.hole_depth)
|
|
107
|
+
|
|
108
|
+
self.actual_annulus = Annulus(drillstring=self.actual_drillstring, hole=self.actual_hole)
|
|
109
|
+
|
|
110
|
+
self.last_hole_depth = self.hole_depth
|
|
111
|
+
|
|
112
|
+
def add_under_reamer_sections(self, mud_flow_rate):
|
|
113
|
+
current_hole: HoleSection = self.actual_hole.find_section_at_measured_depth(
|
|
114
|
+
self.under_reamer.top_depth + 0.0001
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Determine a valid start of reaming depth: either casing shoe or open hole top depth.
|
|
118
|
+
if current_hole.hole_type == HoleType.CASED_HOLE:
|
|
119
|
+
start_reaming_depth = current_hole.bottom_depth
|
|
120
|
+
elif current_hole.inner_diameter < self.under_reamer.ur_opened_od:
|
|
121
|
+
start_reaming_depth = current_hole.top_depth
|
|
122
|
+
else:
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
activation_mode = self.under_reamer.activate_under_reamer(
|
|
126
|
+
mud_flow_rate, start_reaming_depth, current_hole.hole_type
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# If unactivated, do nothing
|
|
130
|
+
if activation_mode == self.under_reamer.UnderReamerActivationType.UNACTIVATED:
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
# Activated for the first time
|
|
134
|
+
if (
|
|
135
|
+
activation_mode == self.under_reamer.UnderReamerActivationType.MUD_FLOW_RATE
|
|
136
|
+
or activation_mode == self.under_reamer.UnderReamerActivationType.OPEN_HOLE
|
|
137
|
+
):
|
|
138
|
+
drillstring = self.get_and_update_drill_string()
|
|
139
|
+
|
|
140
|
+
# Update the original drillstring to match the chopped drillstring under-reamer component
|
|
141
|
+
self.original_drillstring = Drillstring(drillstring)
|
|
142
|
+
|
|
143
|
+
# Update API only if running a stream app
|
|
144
|
+
if is_stream_app():
|
|
145
|
+
drillstring = self.update_drillstring_api(drillstring)
|
|
146
|
+
# If api request failed do not enlarge
|
|
147
|
+
if not drillstring:
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
Logger.debug(f"Updated API with under-reamer activation at depth {self.under_reamer.ur_opened_depth}")
|
|
151
|
+
else:
|
|
152
|
+
Logger.info(
|
|
153
|
+
f"Under-reamer was opened at {self.under_reamer.ur_opened_depth}. "
|
|
154
|
+
f"However, not updating the ur_opened_depth on drillstring on api "
|
|
155
|
+
f"because it was not triggered from Stream app"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
reamed_length = self.under_reamer.bottom_depth - self.under_reamer.ur_opened_depth
|
|
159
|
+
# If under reamer is shallower than the opened depth do nothing
|
|
160
|
+
if reamed_length <= 0:
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
# This property is used for enabling split flow
|
|
164
|
+
self.under_reamer.set_opened(True)
|
|
165
|
+
|
|
166
|
+
ur_opened_od = self.under_reamer.ur_opened_od
|
|
167
|
+
current_hole = self.actual_hole.find_section_at_measured_depth(self.under_reamer.ur_opened_depth + 0.01)
|
|
168
|
+
|
|
169
|
+
# ENLARGEMENT
|
|
170
|
+
# Enlarge the hole from previous run
|
|
171
|
+
if current_hole.inner_diameter == ur_opened_od and current_hole.top_depth == self.under_reamer.ur_opened_depth:
|
|
172
|
+
current_hole.set_length(reamed_length)
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
# Create a new reamed hole
|
|
176
|
+
bottom_hole = deepcopy(current_hole)
|
|
177
|
+
current_hole.set_bottom_depth(self.under_reamer.ur_opened_depth)
|
|
178
|
+
current_hole.set_length()
|
|
179
|
+
bottom_hole.set_top_depth(self.under_reamer.ur_opened_depth)
|
|
180
|
+
bottom_hole.set_length()
|
|
181
|
+
|
|
182
|
+
under_reamer_hole = HoleSection(
|
|
183
|
+
inner_diameter=ur_opened_od, hole_type=HoleType.OPEN_HOLE, top_depth=0.0, bottom_depth=0, length=0
|
|
184
|
+
)
|
|
185
|
+
under_reamer_hole.set_length(reamed_length)
|
|
186
|
+
|
|
187
|
+
# If the current hole is zero no need to add a new enlarged section, enlarge the current hole
|
|
188
|
+
if current_hole.length <= 0.0:
|
|
189
|
+
current_hole.inner_diameter = ur_opened_od
|
|
190
|
+
current_hole.bottom_depth = self.under_reamer.bottom_depth
|
|
191
|
+
current_hole.set_length()
|
|
192
|
+
self.actual_hole.insert_section_after(bottom_hole, current_hole)
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
# Add enlarged hole section between opened depth and current under-reamer depth
|
|
196
|
+
self.actual_hole.insert_section_after(under_reamer_hole, current_hole)
|
|
197
|
+
self.actual_hole.insert_section_after(bottom_hole, under_reamer_hole)
|
|
198
|
+
self.actual_hole.update(self.actual_hole.get_bottom_depth())
|
|
199
|
+
|
|
200
|
+
def update_drillstring_api(self, drillstring: dict) -> Union[dict, None]:
|
|
201
|
+
try:
|
|
202
|
+
path = "/v1/data/corva/data.drillstring"
|
|
203
|
+
res = API().post(path=path, data=json.dumps(drillstring, cls=JsonEncoder, ignore_nan=True))
|
|
204
|
+
return res.data
|
|
205
|
+
except Exception as e:
|
|
206
|
+
Logger.error(e)
|
|
207
|
+
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
def get_and_update_drill_string(self):
|
|
211
|
+
mongo_id = self.original_drillstring.mongo_id
|
|
212
|
+
drillstring = get_config_by_id(mongo_id, collection="data.drillstring")
|
|
213
|
+
|
|
214
|
+
ur_opened_depth = self.under_reamer.ur_opened_depth
|
|
215
|
+
|
|
216
|
+
components = drillstring.get("data", {}).get("components", [])
|
|
217
|
+
# Modify stringJson and post to api
|
|
218
|
+
under_reamer_component = next((component for component in components if component.get("family") == "ur"), {})
|
|
219
|
+
if not under_reamer_component:
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
under_reamer_component["ur_opened_depth"] = ur_opened_depth
|
|
223
|
+
|
|
224
|
+
return drillstring
|
|
225
|
+
|
|
226
|
+
def update_casing(self, casings: Hole) -> None:
|
|
227
|
+
"""
|
|
228
|
+
If there is any update in the casings
|
|
229
|
+
:param casings:
|
|
230
|
+
:return:
|
|
231
|
+
"""
|
|
232
|
+
self.original_hole.update_casings(casings)
|
|
233
|
+
self.actual_hole.update_casings(casings)
|
|
234
|
+
self.update()
|
|
235
|
+
|
|
236
|
+
def trim_after_hole_depth_reduction(self, hole_depth: float) -> None:
|
|
237
|
+
self.last_non_decreasing_hole_depth = hole_depth
|
|
238
|
+
self.original_hole = deepcopy(self.actual_hole)
|
|
239
|
+
|
|
240
|
+
self.actual_hole.sections = [sec for sec in self.actual_hole.sections if sec.top_depth <= hole_depth]
|
|
241
|
+
if self.actual_hole:
|
|
242
|
+
self.actual_hole[-1].bottom_depth = hole_depth
|
|
243
|
+
self.actual_hole[-1].set_length()
|
|
244
|
+
|
|
245
|
+
self.update(hole_depth, hole_depth)
|
|
246
|
+
|
|
247
|
+
def compute_get_drillstring_body_volume_change(self, from_bit_depth: float, to_bit_depth: float) -> float:
|
|
248
|
+
"""
|
|
249
|
+
Compute the change in the drillstring body volume between two bit depths
|
|
250
|
+
:param from_bit_depth: start bit depth
|
|
251
|
+
:param to_bit_depth: end bit depth
|
|
252
|
+
:return: volume in FOOT^3
|
|
253
|
+
"""
|
|
254
|
+
ds_from = deepcopy(self.original_drillstring)
|
|
255
|
+
ds_from.update(from_bit_depth)
|
|
256
|
+
body_volume_from = ds_from.compute_get_body_volume()
|
|
257
|
+
|
|
258
|
+
ds_to = deepcopy(self.original_drillstring)
|
|
259
|
+
ds_to.update(to_bit_depth)
|
|
260
|
+
body_volume_to = ds_to.compute_get_body_volume()
|
|
261
|
+
|
|
262
|
+
return body_volume_to - body_volume_from
|
|
263
|
+
|
|
264
|
+
def compute_get_drillstring_outside_volume_change(self, from_bit_depth: float, to_bit_depth: float) -> float:
|
|
265
|
+
"""
|
|
266
|
+
Compute the change in the drillstring solid volume between two bit depths using OD
|
|
267
|
+
:param from_bit_depth: start bit depth
|
|
268
|
+
:param to_bit_depth: end bit depth
|
|
269
|
+
:return: volume in FOOT^3
|
|
270
|
+
"""
|
|
271
|
+
ds_from = deepcopy(self.original_drillstring)
|
|
272
|
+
ds_from.update(from_bit_depth)
|
|
273
|
+
outside_volume_from = ds_from.compute_get_outside_volume()
|
|
274
|
+
|
|
275
|
+
ds_to = deepcopy(self.original_drillstring)
|
|
276
|
+
ds_to.update(to_bit_depth)
|
|
277
|
+
outside_volume_to = ds_to.compute_get_outside_volume()
|
|
278
|
+
|
|
279
|
+
return outside_volume_to - outside_volume_from
|
|
280
|
+
|
|
281
|
+
def __repr__(self):
|
|
282
|
+
return (
|
|
283
|
+
f"Wellbore:\n"
|
|
284
|
+
f"===Bit Depth / Hole Depth = {nanround(self.bit_depth)} / {nanround(self.hole_depth)}\n"
|
|
285
|
+
f"===Drillstring:\n{self.actual_drillstring}\n"
|
|
286
|
+
f"===Hole:\n{self.actual_hole}\n"
|
|
287
|
+
f"===Annulus:\n{self.actual_annulus}\n\n"
|
|
288
|
+
f"===Given Drillstring:\n{self.actual_drillstring}\n"
|
|
289
|
+
)
|