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,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