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.
Files changed (63) hide show
  1. corva_worker_python-2.0.0.dist-info/METADATA +30 -0
  2. corva_worker_python-2.0.0.dist-info/RECORD +63 -0
  3. corva_worker_python-2.0.0.dist-info/WHEEL +5 -0
  4. corva_worker_python-2.0.0.dist-info/top_level.txt +1 -0
  5. worker/__init__.py +5 -0
  6. worker/app/__init__.py +291 -0
  7. worker/app/modules/__init__.py +265 -0
  8. worker/app/modules/activity_module.py +141 -0
  9. worker/app/modules/connection_module.py +21 -0
  10. worker/app/modules/depth_activity_module.py +21 -0
  11. worker/app/modules/scheduler.py +44 -0
  12. worker/app/modules/time_activity_module.py +21 -0
  13. worker/app/modules/trigger.py +43 -0
  14. worker/constants.py +51 -0
  15. worker/data/__init__.py +0 -0
  16. worker/data/activity/__init__.py +132 -0
  17. worker/data/activity/activity_grouping.py +242 -0
  18. worker/data/alert.py +89 -0
  19. worker/data/api.py +155 -0
  20. worker/data/enums.py +141 -0
  21. worker/data/json_encoder.py +18 -0
  22. worker/data/math.py +104 -0
  23. worker/data/operations.py +477 -0
  24. worker/data/serialization.py +110 -0
  25. worker/data/task_handler.py +82 -0
  26. worker/data/two_way_dict.py +17 -0
  27. worker/data/unit_conversions.py +5 -0
  28. worker/data/wits.py +323 -0
  29. worker/event/__init__.py +53 -0
  30. worker/event/event_handler.py +90 -0
  31. worker/event/scheduled.py +64 -0
  32. worker/event/stream.py +48 -0
  33. worker/exceptions.py +26 -0
  34. worker/mixins/__init__.py +0 -0
  35. worker/mixins/logging.py +119 -0
  36. worker/mixins/rollbar.py +87 -0
  37. worker/partial_rerun_merge/__init__.py +0 -0
  38. worker/partial_rerun_merge/merge.py +500 -0
  39. worker/partial_rerun_merge/models.py +91 -0
  40. worker/partial_rerun_merge/progress.py +241 -0
  41. worker/state/__init__.py +96 -0
  42. worker/state/mixins.py +111 -0
  43. worker/state/state.py +46 -0
  44. worker/test/__init__.py +3 -0
  45. worker/test/lambda_function_test_run.py +196 -0
  46. worker/test/local_testing/__init__.py +0 -0
  47. worker/test/local_testing/to_local_transfer.py +360 -0
  48. worker/test/utils.py +51 -0
  49. worker/wellbore/__init__.py +0 -0
  50. worker/wellbore/factory.py +496 -0
  51. worker/wellbore/measured_depth_finder.py +12 -0
  52. worker/wellbore/model/__init__.py +0 -0
  53. worker/wellbore/model/ann.py +103 -0
  54. worker/wellbore/model/annulus.py +113 -0
  55. worker/wellbore/model/drillstring.py +196 -0
  56. worker/wellbore/model/drillstring_components.py +439 -0
  57. worker/wellbore/model/element.py +102 -0
  58. worker/wellbore/model/enums.py +92 -0
  59. worker/wellbore/model/hole.py +297 -0
  60. worker/wellbore/model/hole_section.py +51 -0
  61. worker/wellbore/model/riser.py +22 -0
  62. worker/wellbore/sections_mixin.py +64 -0
  63. 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
+ )