orchid-python-api 5.25.3__py3-none-any.whl → 5.25.4__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 (57) hide show
  1. orchid_python_api/examples/search_data_frames.py +1 -1
  2. {orchid_python_api-5.25.3.dist-info → orchid_python_api-5.25.4.dist-info}/METADATA +4 -3
  3. orchid_python_api-5.25.4.dist-info/RECORD +38 -0
  4. {orchid_python_api-5.25.3.dist-info → orchid_python_api-5.25.4.dist-info}/WHEEL +1 -1
  5. ReleaseNotes.md +0 -730
  6. copy_orchid_examples.py +0 -88
  7. copy_orchid_low_level_examples.py +0 -93
  8. copy_orchid_manual_examples.py +0 -93
  9. copy_orchid_tutorials.py +0 -88
  10. orchid/VERSION +0 -1
  11. orchid/__init__.py +0 -42
  12. orchid/__version__.py +0 -18
  13. orchid/base.py +0 -31
  14. orchid/base_time_series_adapter.py +0 -91
  15. orchid/configuration.py +0 -162
  16. orchid/convert.py +0 -44
  17. orchid/core.py +0 -149
  18. orchid/dom_project_object.py +0 -28
  19. orchid/dot_net.py +0 -68
  20. orchid/dot_net_disposable.py +0 -64
  21. orchid/dot_net_dom_access.py +0 -241
  22. orchid/measurement.py +0 -35
  23. orchid/native_data_frame_adapter.py +0 -247
  24. orchid/native_fiber_data.py +0 -73
  25. orchid/native_fiber_data_set_info.py +0 -28
  26. orchid/native_monitor_adapter.py +0 -67
  27. orchid/native_project_user_data_adapter.py +0 -137
  28. orchid/native_stage_adapter.py +0 -631
  29. orchid/native_stage_part_adapter.py +0 -50
  30. orchid/native_subsurface_point.py +0 -70
  31. orchid/native_time_series_adapter.py +0 -54
  32. orchid/native_trajectory_adapter.py +0 -246
  33. orchid/native_treatment_calculations.py +0 -158
  34. orchid/native_treatment_curve_adapter.py +0 -60
  35. orchid/native_well_adapter.py +0 -134
  36. orchid/net_date_time.py +0 -328
  37. orchid/net_enumerable.py +0 -72
  38. orchid/net_fracture_diagnostics_factory.py +0 -55
  39. orchid/net_quantity.py +0 -620
  40. orchid/net_stage_qc.py +0 -62
  41. orchid/physical_quantity.py +0 -37
  42. orchid/project.py +0 -182
  43. orchid/project_store.py +0 -269
  44. orchid/reference_origins.py +0 -34
  45. orchid/script_adapter_context.py +0 -81
  46. orchid/searchable_data_frames.py +0 -44
  47. orchid/searchable_project_objects.py +0 -190
  48. orchid/searchable_stage_parts.py +0 -73
  49. orchid/searchable_stages.py +0 -29
  50. orchid/unit_system.py +0 -173
  51. orchid/utils.py +0 -14
  52. orchid/validation.py +0 -52
  53. orchid/version.py +0 -37
  54. orchid_python_api-5.25.3.dist-info/LICENSE +0 -176
  55. orchid_python_api-5.25.3.dist-info/RECORD +0 -88
  56. {orchid_python_api-5.25.3.dist-info → orchid_python_api-5.25.4.dist-info}/entry_points.txt +0 -0
  57. /LICENSE → /orchid_python_api-5.25.4.dist-info/licenses/LICENSE +0 -0
@@ -1,631 +0,0 @@
1
- # Copyright (c) 2017-2025 KAPPA
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- #
15
- # This file is part of Orchid and related technologies.
16
- #
17
-
18
-
19
- import dataclasses as dc
20
- import enum
21
- import math
22
- from typing import Optional, Tuple, Union
23
-
24
- import deal
25
- import option
26
- import pendulum as pdt
27
- import toolz.curried as toolz
28
-
29
- import orchid.base
30
- from orchid import (
31
- dot_net_disposable as dnd,
32
- dot_net_dom_access as dna,
33
- dom_project_object as dpo,
34
- measurement as om,
35
- native_stage_part_adapter as spa,
36
- native_subsurface_point as nsp,
37
- native_treatment_curve_adapter as ntc,
38
- net_date_time as ndt,
39
- net_fracture_diagnostics_factory as fdf,
40
- net_quantity as onq,
41
- reference_origins as origins,
42
- searchable_stage_parts as ssp,
43
- unit_system as units,
44
- validation
45
- )
46
-
47
- # noinspection PyUnresolvedReferences,PyPackageRequirements
48
- from Orchid.FractureDiagnostics import FormationConnectionType, IStagePart
49
- # noinspection PyUnresolvedReferences,PyPackageRequirements
50
- from Orchid.FractureDiagnostics.Factories import Calculations
51
- # noinspection PyUnresolvedReferences
52
- from Orchid.FractureDiagnostics.SDKFacade import ScriptAdapter
53
- # noinspection PyUnresolvedReferences
54
- import UnitsNet
55
- # noinspection PyUnresolvedReferences
56
- from System.Collections.Generic import List
57
- # noinspection PyUnresolvedReferences
58
- import System
59
-
60
-
61
- VALID_LENGTH_UNIT_MESSAGE = 'The parameter, `in_length_unit`, must be a unit system length.'
62
-
63
-
64
- _object_factory = fdf.create()
65
-
66
-
67
- # TODO: Consider adding base with methods like `toNetEnum` and `fromNetEnum`
68
- class ConnectionType(enum.Enum):
69
- PLUG_AND_PERF = FormationConnectionType.PlugAndPerf
70
- SLIDING_SLEEVE = FormationConnectionType.SlidingSleeve
71
- SINGLE_POINT_ENTRY = FormationConnectionType.SinglePointEntry
72
- OPEN_HOLE = FormationConnectionType.OpenHole
73
-
74
-
75
- def as_connection_type(type_value):
76
- @toolz.curry
77
- def has_value(value, enum_type):
78
- return value == enum_type.value
79
-
80
- return toolz.pipe(iter(ConnectionType),
81
- toolz.filter(has_value(type_value)),
82
- toolz.nth(0))
83
-
84
-
85
- # This pre-condition applies to the public methods:
86
- # - bottom_location
87
- # - center_location
88
- # - cluster_location
89
- # - top_location
90
- # but is implemented here to ensure common behavior among all these public methods.
91
- @deal.pre(lambda _depth_datum, _reference_frame, in_length_unit, _net_subsurface_point_func:
92
- validation.is_unit_system_length(in_length_unit),
93
- message=VALID_LENGTH_UNIT_MESSAGE)
94
- def subsurface_point_in_length_unit(depth_datum: origins.DepthDatum,
95
- xy_reference_frame: origins.WellReferenceFrameXy,
96
- in_length_unit: Union[units.UsOilfield, units.Metric],
97
- net_subsurface_point_func) -> nsp.SubsurfacePoint:
98
- """
99
- Calculate the subsurface point `in_length_unit` whose value is calculated by the
100
- callable, `net_subsurface_point_func`.
101
-
102
- Although this method is public, the author intends it to be "private." The author has made it public
103
- **only** to support unit testing. No other usage is supported.
104
-
105
- Args:
106
- depth_datum: The datum from which we measure depths.
107
- xy_reference_frame: The reference frame for easting-northing coordinates.
108
- in_length_unit: The unit of length available from the returned value.
109
- net_subsurface_point_func: The callable to calculate the subsurface point in .NET.
110
-
111
- Returns:
112
- The subsurface point in the requested unit of length.
113
- """
114
- net_subsurface_point = net_subsurface_point_func(xy_reference_frame.value, depth_datum.value)
115
- result = nsp.SubsurfacePoint(net_subsurface_point, in_length_unit)
116
- return result
117
-
118
-
119
- class NativeStageAdapter(dpo.DomProjectObject):
120
- """Adapts a .NET IStage to be more Pythonic."""
121
-
122
- def __init__(self, adaptee, calculations_factory=None):
123
- super().__init__(adaptee, orchid.base.constantly(adaptee.Well.Project))
124
- self.calculations_factory = Calculations.FractureDiagnosticsCalculationsFactory() \
125
- if not calculations_factory else calculations_factory
126
-
127
- cluster_count = dna.dom_property('number_of_clusters', 'The number of clusters for this stage')
128
- display_name_with_well = dna.dom_property('display_name_with_well',
129
- 'The display stage number including the well name')
130
- display_name_without_well = dna.dom_property('display_name_without_well',
131
- 'The display stage number excluding the well name')
132
- display_stage_number = dna.dom_property('display_stage_number', 'The display stage number for the stage')
133
- global_stage_sequence_number = dna.dom_property('global_stage_sequence_number',
134
- 'The global sequence number of this stage')
135
- order_of_completion_on_well = dna.dom_property('order_of_completion_on_well',
136
- 'The order in which this stage was completed on its well')
137
- stage_type = dna.transformed_dom_property('stage_type', 'The formation connection type of this stage',
138
- as_connection_type)
139
- start_time = dna.transformed_dom_property('start_time', 'The start time of the stage treatment',
140
- ndt.as_date_time)
141
- stop_time = dna.transformed_dom_property('stop_time', 'The stop time of the stage treatment',
142
- ndt.as_date_time)
143
-
144
- def _get_time_range(self) -> pdt.Interval:
145
- return pdt.Interval(self.start_time, self.stop_time)
146
-
147
- def _set_time_range(self, to_time_range: pdt.Interval):
148
- to_start_net_time = ndt.as_net_date_time(to_time_range.start)
149
- to_stop_net_time = ndt.as_net_date_time(to_time_range.end)
150
- if len(self.stage_parts()) == 1:
151
- single_stage_part = toolz.first(self.stage_parts())
152
- with dnd.disposable(single_stage_part.dom_object.ToMutable()) as mutable_first_stage_part:
153
- mutable_first_stage_part.SetStartStopTimes(to_start_net_time,
154
- to_stop_net_time)
155
- elif len(self.stage_parts()) > 1:
156
- single_stage_part = toolz.first(self.stage_parts())
157
- with dnd.disposable(single_stage_part.dom_object.ToMutable()) as mutable_first_stage_part:
158
- mutable_first_stage_part.SetStartStopTimes(to_start_net_time,
159
- single_stage_part.dom_object.StopTime)
160
- last_stage_part = toolz.last(self.stage_parts())
161
- with dnd.disposable(last_stage_part.dom_object.ToMutable()) as mutable_last_stage_part:
162
- mutable_last_stage_part.SetStartStopTimes(last_stage_part.dom_object.StartTime,
163
- to_stop_net_time)
164
- else: # No stage parts
165
- factory = fdf.create()
166
- stage_part_to_add = factory.CreateStagePart(self.dom_object, to_start_net_time,
167
- to_stop_net_time, None)
168
- with dnd.disposable(self.dom_object.ToMutable()) as mutable_stage:
169
- mutable_stage.Parts.Add(stage_part_to_add)
170
-
171
- time_range = property(fget=_get_time_range, fset=_set_time_range,
172
- doc='The time range (start and end) of this stage')
173
-
174
- @property
175
- def isip(self) -> om.Quantity:
176
- """
177
- Return the instantaneous shut in pressure of this stage in project units.
178
- """
179
- return onq.as_measurement_from_option(self.expect_project_units.PRESSURE, self.dom_object.Isip)
180
-
181
- @property
182
- def pnet(self) -> om.Quantity:
183
- """
184
- Return the net pressure of this stage in project units.
185
-
186
- The net pressure of a stage is calculated by the formula:
187
- pnet = isip + fluid-density * tvd - shmin (where tvd is the true vertical depth)
188
- """
189
- return onq.as_measurement_from_option(self.expect_project_units.PRESSURE, self.dom_object.Pnet)
190
-
191
- @property
192
- def shmin(self) -> om.Quantity:
193
- """
194
- Return the minimum horizontal stress of this stage in project units.
195
- """
196
-
197
- return onq.as_measurement_from_option(self.expect_project_units.PRESSURE, self.dom_object.Shmin)
198
-
199
- @staticmethod
200
- def _sampled_quantity_name_curve_map(sampled_quantity_name):
201
- candidates = toolz.pipe(ntc.TreatmentCurveTypes,
202
- toolz.filter(lambda e: e.value == sampled_quantity_name),
203
- list)
204
- if len(candidates) == 0:
205
- raise KeyError(f'Unknown sampled quantity name: "{sampled_quantity_name}"')
206
-
207
- assert len(candidates) == 1, f'Sampled quantity name "{sampled_quantity_name}"' \
208
- f' selects many curve types: {candidates}'
209
-
210
- return candidates[0]
211
-
212
- def _center_location_depth(self, in_length_unit: Union[units.UsOilfield, units.Metric],
213
- depth_datum: origins.DepthDatum) -> om.Quantity:
214
- """
215
- Return the depth of the stage center relative to the specified `depth_datum.`
216
-
217
- Args:
218
- in_length_unit: The unit of length for the returned Measurement.
219
- depth_datum: The reference datum for the depth.
220
- """
221
- subsurface_point = self.center_location(in_length_unit, origins.WellReferenceFrameXy.ABSOLUTE_STATE_PLANE,
222
- depth_datum)
223
- return subsurface_point.depth
224
-
225
- def bottom_location(self, in_length_unit: Union[units.UsOilfield, units.Metric],
226
- xy_reference_frame: origins.WellReferenceFrameXy,
227
- depth_datum: origins.DepthDatum) -> nsp.SubsurfacePoint:
228
- """
229
- Return the location of the bottom of this stage in the `xy_well_reference_frame` using the
230
- `depth_datum` in the specified unit.
231
-
232
- Args:
233
- in_length_unit: The unit of length available from the returned value.
234
- xy_reference_frame: The reference frame for easting-northing coordinates.
235
- depth_datum: The datum from which we measure depths.
236
-
237
- Returns:
238
- The `SubsurfacePoint` of the stage bottom.
239
- """
240
-
241
- return subsurface_point_in_length_unit(depth_datum, xy_reference_frame, in_length_unit,
242
- self.dom_object.GetStageLocationBottom)
243
-
244
- def center_location(self, in_length_unit: Union[units.UsOilfield, units.Metric],
245
- xy_reference_frame: origins.WellReferenceFrameXy,
246
- depth_datum: origins.DepthDatum) -> nsp.SubsurfacePoint:
247
- """
248
- Return the location of the center of this stage in the `xy_well_reference_frame` using the `depth_datum`
249
- in the specified unit.
250
-
251
- Args:
252
- in_length_unit: The unit of length available from the returned value.
253
- xy_reference_frame: The reference frame for easting-northing coordinates.
254
- depth_datum: The datum from which we measure depths.
255
-
256
- Returns:
257
- The `SubsurfacePoint` of the stage center.
258
- """
259
- return subsurface_point_in_length_unit(depth_datum, xy_reference_frame, in_length_unit,
260
- self.dom_object.GetStageLocationCenter)
261
-
262
- def center_location_easting(self, in_length_unit: Union[units.UsOilfield, units.Metric],
263
- xy_well_reference_frame: origins.WellReferenceFrameXy) -> om.Quantity:
264
- """
265
- Return the easting location of the stage center relative to the specified reference frame in the
266
- specified unit.
267
-
268
- Args:
269
- in_length_unit: An unit of the unit of length for the returned Measurement.
270
- xy_well_reference_frame: The reference frame defining the origin.
271
-
272
- Returns:
273
- A measurement.
274
- """
275
- result = self.center_location(in_length_unit, xy_well_reference_frame, origins.DepthDatum.KELLY_BUSHING).x
276
- return result
277
-
278
- def center_location_northing(self, in_length_unit: Union[units.UsOilfield, units.Metric],
279
- xy_well_reference_frame: origins.WellReferenceFrameXy) -> om.Quantity:
280
- """
281
- Return the northing location of the stage center in the `xy_well_reference_frame` in the specified unit.
282
-
283
- Args:
284
- in_length_unit: The requested resultant length unit.
285
- xy_well_reference_frame: The reference frame defining the origin.
286
-
287
- Returns:
288
- A measurement.
289
- """
290
- subsurface_point = self.center_location(in_length_unit, xy_well_reference_frame,
291
- origins.DepthDatum.KELLY_BUSHING)
292
- return subsurface_point.y
293
-
294
- def center_location_mdkb(self, in_length_unit: Union[units.UsOilfield, units.Metric]) -> om.Quantity:
295
- """
296
- Return the measured depth of the stage center in project units.
297
-
298
- Args:
299
- in_length_unit: The unit of length for the returned Measurement.
300
- """
301
- return (self.md_top(in_length_unit) + self.md_bottom(in_length_unit)) / 2
302
-
303
- def center_location_tvdgl(self, in_length_unit: Union[units.UsOilfield, units.Metric]) -> om.Quantity:
304
- """
305
- Returns the total vertical depth from ground level of the stage center in project units.
306
-
307
- Args:
308
- in_length_unit: The unit of length for the returned Measurement.
309
- """
310
- return self._center_location_depth(in_length_unit, origins.DepthDatum.GROUND_LEVEL)
311
-
312
- def center_location_tvdss(self, in_length_unit: Union[units.UsOilfield, units.Metric]) -> om.Quantity:
313
- """
314
- Returns the total vertical depth from sea level of the stage center in project units.
315
-
316
- Args:
317
- in_length_unit: The unit of length for the returned Measurement.
318
- """
319
- return self._center_location_depth(in_length_unit, origins.DepthDatum.SEA_LEVEL)
320
-
321
- def center_location_xy(self, in_length_unit: Union[units.UsOilfield, units.Metric],
322
- xy_well_reference_frame: origins.WellReferenceFrameXy) -> Tuple[om.Quantity,
323
- om.Quantity]:
324
- """
325
- Return the easting-northing location of the stage center in the `xy_well_reference_frame` in project units.
326
-
327
- Args:
328
- in_length_unit: The unit of length for the returned Measurement.
329
- xy_well_reference_frame: The reference frame defining the origin.
330
-
331
- Returns:
332
- A tuple
333
- """
334
- subsurface_point = self.center_location(in_length_unit, xy_well_reference_frame,
335
- origins.DepthDatum.KELLY_BUSHING)
336
- return subsurface_point.x, subsurface_point.y
337
-
338
- @deal.pre(lambda _self, _in_length_unit, cluster_no, _xy_reference_frame, _depth_datum: cluster_no >= 0)
339
- def cluster_location(self, in_length_unit: Union[units.UsOilfield, units.Metric], cluster_no: int,
340
- xy_reference_frame: origins.WellReferenceFrameXy,
341
- depth_datum: origins.DepthDatum) -> nsp.SubsurfacePoint:
342
- """
343
- Return the location of the bottom of this stage in the `xy_well_reference_frame` using the
344
- `depth_datum` in the specified unit.
345
-
346
- Args:
347
- in_length_unit: The unit of length available from the returned value.
348
- cluster_no: The number identifying the cluster whose location is sought.
349
- xy_reference_frame: The reference frame for easting-northing coordinates.
350
- depth_datum: The datum from which we measure depths.
351
-
352
- Returns:
353
- The `SubsurfacePoint` of the stage cluster identified by `cluster_no`.
354
- """
355
- stage_location_cluster_func = toolz.curry(self.dom_object.GetStageLocationCluster, cluster_no)
356
- return subsurface_point_in_length_unit(depth_datum, xy_reference_frame, in_length_unit,
357
- stage_location_cluster_func)
358
-
359
- @deal.pre(validation.arg_is_acceptable_pressure_unit)
360
- def isip_in_pressure_unit(self, target_unit: Union[units.UsOilfield, units.Metric]) -> om.Quantity:
361
- return onq.as_measurement_from_option(target_unit, self.dom_object.Isip)
362
-
363
- def md_bottom(self, in_length_unit: Union[units.UsOilfield, units.Metric]):
364
- """
365
- Return the measured depth of the bottom of this stage (farthest from the wellhead / closest to the toe)
366
- in the specified unit.
367
-
368
- Args:
369
- in_length_unit: An unit of the unit of length for the returned Measurement.
370
-
371
- Returns:
372
- The measured depth of the stage bottom in the specified unit.
373
- """
374
- return onq.as_measurement(in_length_unit, option.maybe(self.dom_object.MdBottom))
375
-
376
- def md_top(self, in_length_unit: Union[units.UsOilfield, units.Metric]) -> om.Quantity:
377
- """
378
- Return the measured depth of the top of this stage (closest to the wellhead / farthest from the toe)
379
- in the specified unit.
380
-
381
- Args:
382
- in_length_unit: An unit of the requested resultant length unit.
383
-
384
- Returns;
385
- The measured depth of the stage top in the specified unit.
386
- """
387
- return onq.as_measurement(in_length_unit, option.maybe(self.dom_object.MdTop))
388
-
389
- @deal.pre(validation.arg_is_acceptable_pressure_unit)
390
- def pnet_in_pressure_unit(self, target_unit: Union[units.UsOilfield, units.Metric]) -> om.Quantity:
391
- return onq.as_measurement_from_option(target_unit, self.dom_object.Pnet)
392
-
393
- @deal.pre(validation.arg_is_acceptable_pressure_unit)
394
- def shmin_in_pressure_unit(self, target_unit: Union[units.UsOilfield, units.Metric]) -> om.Quantity:
395
- return onq.as_measurement_from_option(target_unit, self.dom_object.Shmin)
396
-
397
- def stage_length(self, in_length_unit: Union[units.UsOilfield, units.Metric]) -> om.Quantity:
398
- """
399
- Return the stage length in the specified unit.
400
-
401
- Args:
402
- in_length_unit: An unit of the unit of length for the returned Measurement.
403
-
404
- Returns:
405
- The Measurement of the length of this stage.
406
- """
407
- return self.md_bottom(in_length_unit) - self.md_top(in_length_unit)
408
-
409
- def stage_parts(self) -> ssp.SearchableStageParts:
410
- """
411
- Return a `ssp.SearchableStageParts` for all the stage parts for this stage.
412
-
413
- Returns:
414
- An `ssp.SearchableStageParts` for all the stage parts for this stage.
415
- """
416
- return ssp.SearchableStageParts(spa.NativeStagePartAdapter, self.dom_object.Parts)
417
-
418
- def top_location(self, in_length_unit: Union[units.UsOilfield, units.Metric],
419
- xy_reference_frame: origins.WellReferenceFrameXy,
420
- depth_datum: origins.DepthDatum) -> nsp.SubsurfacePoint:
421
- """
422
- Return the location of the top of this stage in the `xy_well_reference_frame` using the `depth_datum`
423
- in the specified unit.
424
-
425
- Args:
426
- in_length_unit: The unit of length available from the returned value.
427
- xy_reference_frame: The reference frame for easting-northing coordinates.
428
- depth_datum: The datum from which we measure depths.
429
-
430
- Returns:
431
- The `SubsurfacePoint` of the stage top.
432
- """
433
- return subsurface_point_in_length_unit(depth_datum, xy_reference_frame, in_length_unit,
434
- self.dom_object.GetStageLocationTop)
435
-
436
- def treatment_curves(self):
437
- """
438
- Returns the dictionary of treatment curves for this treatment_stage.
439
-
440
- Request a specific curve from the dictionary using the constants defined in `orchid`:
441
-
442
- - `PROPPANT_CONCENTRATION`
443
- - `SLURRY_RATE`
444
- - `TREATING_PRESSURE`
445
-
446
- Returns:
447
- The dictionary containing the available treatment curves.
448
- """
449
- if not self.dom_object.TreatmentCurves.Items:
450
- return {}
451
-
452
- def add_curve(so_far, treatment_curve):
453
- curve_name = self._sampled_quantity_name_curve_map(treatment_curve.sampled_quantity_name)
454
- treatment_curve_map = {curve_name: treatment_curve}
455
- return toolz.merge(treatment_curve_map, so_far)
456
-
457
- result = toolz.pipe(self.dom_object.TreatmentCurves.Items, # start with .NET treatment curves
458
- toolz.map(ntc.NativeTreatmentCurveAdapter), # wrap them in a facade
459
- # Transform the map to a dictionary keyed by the sampled quantity name
460
- lambda cs: toolz.reduce(add_curve, cs, {}))
461
- return result
462
-
463
-
464
- @dc.dataclass
465
- class CreateStageDto:
466
- """
467
- A data-transfer object (DTO) containing the required and optional data used to create new stages.
468
-
469
- This class enforces a number of constraints on the data needed to create a stage. Consider viewing the source
470
- code of the class to understand those constraints. (The class will throw exceptions if the constraints are not met
471
- at run-time.)
472
-
473
- Additionally, the comments of this class contain a number of "warnings" for situations where data is not required,
474
- but the consequences for **not** supplying the data may not be desirable or may not be expected.
475
- """
476
- stage_no: int # Must be positive
477
- connection_type: ConnectionType
478
- md_top: om.Quantity # Must be length
479
- md_bottom: om.Quantity # Must be length
480
-
481
- cluster_count: int = 0 # Must be non-negative
482
- maybe_shmin: Optional[om.Quantity] = None # If not `None`, must be pressure
483
- # WARNING: one need supply neither a start time nor a stop time; however, not supplying this data can
484
- # produce unexpected behavior for the `global_stage_sequence_number` property. For example, one can
485
- # generate duplicate values for the `global_stage_sequence_number`. This unexpected behavior is a known
486
- # issue with Orchid.
487
- #
488
- # Note supplying no value (an implicit `None`) results in the largest possible .NET time range.
489
- maybe_time_range: Optional[pdt.Interval] = None
490
- # WARNING: one must currently supply an ISIP for each stage; otherwise, Orchid fails to correctly load
491
- # the project saved with the added stages.
492
- maybe_isip: Optional[om.Quantity] = None # If not `None`, must be a pressure
493
-
494
- order_of_completion_on_well = property(fget=lambda self: self.stage_no - 1)
495
-
496
- def __post_init__(self):
497
- # See the
498
- # [StackOverflow post](https://stackoverflow.com/questions/54488765/validating-input-when-mutating-a-dataclass)
499
- if self.stage_no <= 0:
500
- raise ValueError(f'Expected stage_no to be positive. Found {self.stage_no}')
501
- if not self.md_top.check('[length]'):
502
- raise ValueError(f'Expected md_top to be a length. Found {self.md_top:~P}')
503
- if not self.md_bottom.check('[length]'):
504
- raise ValueError(f'Expected md_bottom to be a length. Found {self.md_bottom:~P}')
505
- if self.cluster_count < 0:
506
- raise ValueError(f'Expected cluster_count to be non-negative. Found {self.cluster_count}')
507
- if self.maybe_isip is not None:
508
- if not self.maybe_isip.check('[pressure]'):
509
- raise ValueError(f'Expected maybe_isip to be a pressure if not None. Found {self.maybe_isip:~P}')
510
- if self.maybe_shmin is not None:
511
- if not self.maybe_shmin.check('[pressure]'):
512
- raise ValueError(f'Expected maybe_shmin to be a pressure if not None. Found {self.maybe_shmin:~P}')
513
-
514
- def create_stage(self, well) -> NativeStageAdapter:
515
- """
516
- Creates a stage from this DTO.
517
-
518
- Args:
519
- well (NativeWellAdapter): The well of the created `NativeStageAdapter`
520
-
521
- Returns:
522
- The `NativeStageAdapter` wrapping the created .NET `IStage` instance.
523
-
524
- """
525
- project_unit_system = units.as_unit_system(well.dom_object.Project.ProjectUnits)
526
- native_md_top = onq.as_net_quantity(project_unit_system.LENGTH, self.md_top)
527
- native_md_bottom = onq.as_net_quantity(project_unit_system.LENGTH, self.md_bottom)
528
- if self.maybe_shmin is None:
529
- native_shmin = ScriptAdapter.MakeOptionNone[UnitsNet.Pressure]()
530
- elif math.isnan(self.maybe_shmin.magnitude):
531
- native_shmin = ScriptAdapter.MakeOptionNone[UnitsNet.Pressure]()
532
- else:
533
- native_shmin = ScriptAdapter.MakeOptionSome(
534
- onq.as_net_quantity(project_unit_system.PRESSURE, self.maybe_shmin))
535
- completion_order_on_well = System.UInt32(self.order_of_completion_on_well)
536
- connection_type = self.connection_type.value
537
- cluster_count = System.UInt32(self.cluster_count)
538
- no_time_range_native_stage = self.create_net_stage(well.dom_object, completion_order_on_well,
539
- connection_type, native_md_top,
540
- native_md_bottom, native_shmin,
541
- cluster_count)
542
-
543
- with dnd.disposable(no_time_range_native_stage.ToMutable()) as mutable_stage:
544
- native_start_time = (ndt.as_net_date_time(self.maybe_time_range.start)
545
- if self.maybe_time_range is not None
546
- else pdt.DateTime.max)
547
- native_stop_time = (ndt.as_net_date_time(self.maybe_time_range.end)
548
- if self.maybe_time_range is not None
549
- else pdt.DateTime.max)
550
- if self.maybe_isip is None:
551
- native_isip = None
552
- elif math.isnan(self.maybe_isip.magnitude):
553
- native_isip = None
554
- else:
555
- native_isip = onq.as_net_quantity(project_unit_system.PRESSURE, self.maybe_isip)
556
-
557
- stage_part = self.create_net_stage_part(no_time_range_native_stage, native_start_time, native_stop_time,
558
- native_isip)
559
- self.add_stage_part_to_stage(mutable_stage, stage_part)
560
-
561
- # Alias to better communicate intent
562
- created_native_stage = no_time_range_native_stage
563
- return NativeStageAdapter(created_native_stage)
564
-
565
- @staticmethod
566
- def create_net_stage(native_well, completion_order_on_well, connection_type, native_md_top, native_md_bottom,
567
- native_shmin, cluster_count):
568
- """
569
- Create a .NET `IStage`.
570
-
571
- This method primarily exists so that I can mock the call in unit tests.
572
-
573
- Args:
574
- native_well: The .NET `IWell` instance to which the created `IStage` refers.
575
- completion_order_on_well: The order of completion of this stage of the referenced `IWell`.
576
- connection_type: The .NET `FormationConnectionType` of the created .NET stage.
577
- native_md_top: The measured depth of the top (toward the heel) of the created .NET stage.
578
- native_md_bottom: The measured depth of the bottom (toward the toe) of the created .NET stage.
579
- native_shmin: The minimum horizontal stress of the created .NET stage.
580
- cluster_count: The cluster count of the created .NET stage.
581
-
582
- Returns:
583
- The newly created .NET `IStage` instance.
584
- """
585
- no_time_range_native_stage = _object_factory.CreateStage(
586
- System.UInt32(completion_order_on_well),
587
- native_well,
588
- connection_type,
589
- native_md_top,
590
- native_md_bottom,
591
- native_shmin,
592
- System.UInt32(cluster_count)
593
- )
594
- return no_time_range_native_stage
595
-
596
- @staticmethod
597
- def create_net_stage_part(native_stage, native_start_time, native_stop_time, native_isip):
598
- """
599
- Create . .NET `IStagePart` instance.
600
-
601
- This method primarily exists so that I can mock the call in unit tests.
602
-
603
- Args:
604
- native_stage: The .NET `IStage` to which the created `IStagePart` refers.
605
- native_start_time: The start time of the created `IStagePart`.
606
- native_stop_time: The stop time of the created `IStagePart`.
607
- native_isip: The ISIP of the created `IStagePart`.
608
-
609
- Returns:
610
- The newly created `IStagePart` with the specified details.
611
- """
612
- stage_part = _object_factory.CreateStagePart(native_stage,
613
- native_start_time,
614
- native_stop_time,
615
- native_isip)
616
- return stage_part
617
-
618
- @staticmethod
619
- def add_stage_part_to_stage(mutable_stage, stage_part):
620
- """
621
- Add a newly created `stage_part` to a newly created `mutable_stage`.
622
-
623
- This method exists primarily so that I can mock it for unit tests.
624
-
625
- Args:
626
- mutable_stage: The newly created stage supporting mutability.
627
- stage_part: The newly created stage part.
628
- """
629
- stage_parts = List[IStagePart]()
630
- stage_parts.Add(stage_part)
631
- mutable_stage.Parts = stage_parts
@@ -1,50 +0,0 @@
1
- # Copyright 2017-2025 KAPPA
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- #
15
- # This file is part of Orchid and related technologies.
16
- #
17
-
18
-
19
- import option
20
-
21
- from orchid import (
22
- base,
23
- dot_net_dom_access as dna,
24
- dom_project_object as dpo,
25
- measurement as om,
26
- net_date_time as ndt,
27
- net_quantity as onq,
28
- )
29
-
30
-
31
- class NativeStagePartAdapter(dpo.DomProjectObject):
32
- def __init__(self, adaptee):
33
- super().__init__(adaptee, base.constantly(adaptee.Project))
34
-
35
- display_name_with_well = dna.dom_property('display_name_with_well',
36
- 'The display stage number including the well name')
37
- display_name_without_well = dna.dom_property('display_name_without_well',
38
- 'The display stage number excluding the well name')
39
- part_no = dna.dom_property('part_number', 'The part number for this stage part')
40
- start_time = dna.transformed_dom_property('start_time', 'The start time of this stage part',
41
- ndt.as_date_time)
42
- stop_time = dna.transformed_dom_property('stop_time', 'The stop time of this stage part',
43
- ndt.as_date_time)
44
-
45
- @property
46
- def isip(self) -> om.Quantity:
47
- """
48
- Return the instantaneous shut-in pressure of this stage part in project units.
49
- """
50
- return onq.as_measurement(self.expect_project_units.PRESSURE, option.maybe(self.dom_object.Isip))