completor 1.1.1__tar.gz → 1.1.3__tar.gz
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.
- {completor-1.1.1 → completor-1.1.3}/PKG-INFO +15 -13
- {completor-1.1.1 → completor-1.1.3}/completor/completion.py +10 -3
- {completor-1.1.1 → completor-1.1.3}/completor/constants.py +11 -5
- {completor-1.1.1 → completor-1.1.3}/completor/create_output.py +23 -27
- {completor-1.1.1 → completor-1.1.3}/completor/input_validation.py +39 -9
- {completor-1.1.1 → completor-1.1.3}/completor/prepare_outputs.py +103 -94
- {completor-1.1.1 → completor-1.1.3}/completor/read_casefile.py +82 -51
- {completor-1.1.1 → completor-1.1.3}/completor/utils.py +2 -2
- {completor-1.1.1 → completor-1.1.3}/completor/visualize_well.py +2 -2
- {completor-1.1.1 → completor-1.1.3}/completor/wells.py +4 -4
- {completor-1.1.1 → completor-1.1.3}/pyproject.toml +22 -19
- {completor-1.1.1 → completor-1.1.3}/LICENSE +0 -0
- {completor-1.1.1 → completor-1.1.3}/README.md +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/__init__.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/config_jobs/run_completor +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/exceptions/__init__.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/exceptions/clean_exceptions.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/exceptions/exceptions.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/get_version.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/hook_implementations/jobs.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/launch_args_parser.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/logger.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/main.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/parse.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/read_schedule.py +0 -0
- {completor-1.1.1 → completor-1.1.3}/completor/visualization.py +0 -0
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: completor
|
|
3
|
-
Version: 1.1.
|
|
4
|
-
Summary: Advanced
|
|
3
|
+
Version: 1.1.3
|
|
4
|
+
Summary: Advanced multi-segmented well completion tool.
|
|
5
5
|
Home-page: https://github.com/equinor/completor
|
|
6
6
|
License: LGPL-3.0-only
|
|
7
7
|
Author: Equinor ASA
|
|
8
8
|
Author-email: opensource@equinor.com
|
|
9
|
-
Requires-Python: >=3.11,<
|
|
9
|
+
Requires-Python: >=3.11,<4.0
|
|
10
10
|
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
|
11
11
|
Classifier: Natural Language :: English
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
16
|
Provides-Extra: ert
|
|
16
|
-
|
|
17
|
-
Requires-Dist: ert (
|
|
18
|
-
Requires-Dist: matplotlib (>=3.
|
|
19
|
-
Requires-Dist: numpy (
|
|
20
|
-
Requires-Dist: pandas (>=2.
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist: pytest-env (>=1,<2)
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: ert (>=12,<13) ; extra == "ert"
|
|
19
|
+
Requires-Dist: matplotlib (>=3.9,<4.0)
|
|
20
|
+
Requires-Dist: numpy (>=1.26,<3.0)
|
|
21
|
+
Requires-Dist: pandas (>=2.2,<3.0)
|
|
22
|
+
Requires-Dist: pytest (>=8.3,<9.0) ; extra == "test"
|
|
23
|
+
Requires-Dist: pytest-env (>=1,<2) ; extra == "test"
|
|
24
|
+
Requires-Dist: pytest-xdist (>=3.6,<4.0) ; extra == "test"
|
|
25
|
+
Requires-Dist: rstcheck-core (>=1.2,<2.0) ; extra == "test"
|
|
26
|
+
Requires-Dist: scipy (>=1.14,<2.0)
|
|
27
|
+
Requires-Dist: tqdm (>=4.66,<5.0)
|
|
26
28
|
Project-URL: Bug Tracker, https://github.com/equinor/completor/issues
|
|
27
29
|
Project-URL: Documentation, https://equinor.github.io/completor
|
|
28
30
|
Project-URL: Repository, https://github.com/equinor/completor
|
|
@@ -551,7 +551,7 @@ def get_device(df_well: pd.DataFrame, df_device: pd.DataFrame, device_type: str)
|
|
|
551
551
|
Args:
|
|
552
552
|
df_well: Must contain device type, device number, and the scaling factor.
|
|
553
553
|
df_device: Device table.
|
|
554
|
-
device_type: Device type. `AICD`, `ICD`, `
|
|
554
|
+
device_type: Device type. `AICD`, `ICD`, `DENSITY`, `VALVE`, `DUALRCP`, `ICV`.
|
|
555
555
|
|
|
556
556
|
Returns:
|
|
557
557
|
Updated well information with device characteristics.
|
|
@@ -561,7 +561,14 @@ def get_device(df_well: pd.DataFrame, df_device: pd.DataFrame, device_type: str)
|
|
|
561
561
|
"""
|
|
562
562
|
columns = [Headers.DEVICE_TYPE, Headers.DEVICE_NUMBER]
|
|
563
563
|
try:
|
|
564
|
-
df_well = pd.merge(df_well, df_device, how="left", on=columns)
|
|
564
|
+
df_well = pd.merge(df_well, df_device, how="left", on=columns, suffixes=("", "_drop"))
|
|
565
|
+
# check for duplicates if merging two WSEGVALV-es
|
|
566
|
+
for col in df_well.columns:
|
|
567
|
+
if col.endswith("_drop"):
|
|
568
|
+
base_col = col.replace("_drop", "")
|
|
569
|
+
if base_col in df_well.columns:
|
|
570
|
+
df_well[base_col] = df_well[base_col].fillna(df_well[col]) # Fill NaN values
|
|
571
|
+
df_well = df_well.drop(columns=[col for col in df_well.columns if col.endswith("_drop")])
|
|
565
572
|
except KeyError as err:
|
|
566
573
|
if f"'{Headers.DEVICE_TYPE}'" in str(err):
|
|
567
574
|
raise ValueError(f"Missing keyword 'DEVICETYPE {device_type}' in input files.") from err
|
|
@@ -570,7 +577,7 @@ def get_device(df_well: pd.DataFrame, df_device: pd.DataFrame, device_type: str)
|
|
|
570
577
|
# rescale the Cv
|
|
571
578
|
# because no scaling factor in WELL_SEGMENTS_VALVE
|
|
572
579
|
df_well[Headers.FLOW_COEFFICIENT] = -df_well[Headers.FLOW_COEFFICIENT] / df_well[Headers.SCALE_FACTOR]
|
|
573
|
-
elif device_type == Content.
|
|
580
|
+
elif device_type == Content.DENSITY:
|
|
574
581
|
# rescale the Cv
|
|
575
582
|
# because no scaling factor in WELL_SEGMENTS_VALVE
|
|
576
583
|
df_well[Headers.FLOW_COEFFICIENT] = -df_well[Headers.FLOW_COEFFICIENT] / df_well[Headers.SCALE_FACTOR]
|
|
@@ -153,7 +153,7 @@ class _Headers:
|
|
|
153
153
|
# 11. The length of the valve, lVAL (Scale factor).
|
|
154
154
|
# 12. An integer which determines how the flow scaling factor is calculated.
|
|
155
155
|
|
|
156
|
-
# Density
|
|
156
|
+
# Density Driven Well Segments (WSEGDENSITY)
|
|
157
157
|
# DEVICE_NUMBER
|
|
158
158
|
# FLOW_COEFFICIENT / Cv
|
|
159
159
|
# FLOW_CROSS_SECTIONAL_AREA
|
|
@@ -186,10 +186,10 @@ class _Headers:
|
|
|
186
186
|
LATERAL = "LATERAL"
|
|
187
187
|
NUMBER_OF_DEVICES = "NUMBER_OF_DEVICES"
|
|
188
188
|
SEGMENT_DESC = "SEGMENT_DESC"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
DUALRCP_WATER_CUT = "DUALRCP_WATER_CUT"
|
|
190
|
+
DUALRCP_GAS_HOLDUP_FRACTION = "DUALRCP_GAS_HOLDUP_FRACTION"
|
|
191
|
+
DUALRCP_CALIBRATION_FLUID_DENSITY = "DUALRCP_CALIBRATION_FLUID_DENSITY"
|
|
192
|
+
DUALRCP_FLUID_VISCOSITY = "DUALRCP_FLUID_VISCOSITY"
|
|
193
193
|
AICD_CALIBRATION_FLUID_DENSITY = "AICD_CALIBRATION_FLUID_DENSITY"
|
|
194
194
|
AICD_FLUID_VISCOSITY = "AICD_FLUID_VISCOSITY"
|
|
195
195
|
ALPHA_MAIN = "ALPHA_MAIN"
|
|
@@ -239,9 +239,11 @@ class _Keywords:
|
|
|
239
239
|
WELL_SEGMENTS_LINK = "WSEGLINK"
|
|
240
240
|
WELL_SEGMENTS_VALVE = "WSEGVALV"
|
|
241
241
|
AUTONOMOUS_INFLOW_CONTROL_DEVICE = "WSEGAICD"
|
|
242
|
+
DUAL_RATE_CONTROLLED_PRODUCTION = "WSEGDUALRCP"
|
|
242
243
|
AUTONOMOUS_INFLOW_CONTROL_VALVE = "WSEGAICV"
|
|
243
244
|
INFLOW_CONTROL_VALVE = "WSEGICV"
|
|
244
245
|
INFLOW_CONTROL_DEVICE = "WSEGSICD"
|
|
246
|
+
DENSITY = "WSEGDENSITY"
|
|
245
247
|
DENSITY_ACTIVATED_RECOVERY = "WSEGDAR"
|
|
246
248
|
LATERAL_TO_DEVICE = "LATERAL_TO_DEVICE"
|
|
247
249
|
JOINT_LENGTH = "JOINTLENGTH"
|
|
@@ -282,14 +284,18 @@ class _Content:
|
|
|
282
284
|
|
|
283
285
|
PERFORATED = "PERF"
|
|
284
286
|
INFLOW_CONTROL_VALVE = "ICV"
|
|
287
|
+
DUAL_RATE_CONTROLLED_PRODUCTION = "DUALRCP"
|
|
285
288
|
AUTONOMOUS_INFLOW_CONTROL_VALVE = "AICV"
|
|
286
289
|
INFLOW_CONTROL_DEVICE = "ICD"
|
|
287
290
|
AUTONOMOUS_INFLOW_CONTROL_DEVICE = "AICD"
|
|
291
|
+
DENSITY = "DENSITY"
|
|
288
292
|
DENSITY_ACTIVATED_RECOVERY = "DAR"
|
|
289
293
|
VALVE = "VALVE"
|
|
290
294
|
DEVICE_TYPES = [
|
|
291
295
|
AUTONOMOUS_INFLOW_CONTROL_DEVICE,
|
|
296
|
+
DUAL_RATE_CONTROLLED_PRODUCTION,
|
|
292
297
|
AUTONOMOUS_INFLOW_CONTROL_VALVE,
|
|
298
|
+
DENSITY,
|
|
293
299
|
DENSITY_ACTIVATED_RECOVERY,
|
|
294
300
|
INFLOW_CONTROL_DEVICE,
|
|
295
301
|
VALVE,
|
|
@@ -40,8 +40,8 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
|
|
|
40
40
|
print_inflow_control_valve = ""
|
|
41
41
|
print_autonomous_inflow_control_device = ""
|
|
42
42
|
print_inflow_control_device = ""
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
print_density_driven = ""
|
|
44
|
+
print_dual_rate_controlled_production = ""
|
|
45
45
|
|
|
46
46
|
start_segment = 2
|
|
47
47
|
start_branch = 1
|
|
@@ -110,10 +110,8 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
|
|
|
110
110
|
df_autonomous_inflow_control_device = prepare_outputs.prepare_autonomous_inflow_control_device(
|
|
111
111
|
well.well_name, lateral.df_well, lateral.df_device
|
|
112
112
|
)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
116
|
-
df_autonomous_inflow_control_valve = prepare_outputs.prepare_autonomous_inflow_control_valve(
|
|
113
|
+
df_density_driven = prepare_outputs.prepare_density_driven(well.well_name, lateral.df_well, lateral.df_device)
|
|
114
|
+
df_dual_rate_controlled_production = prepare_outputs.prepare_dual_rate_controlled_production(
|
|
117
115
|
well.well_name, lateral.df_well, lateral.df_device
|
|
118
116
|
)
|
|
119
117
|
df_inflow_control_valve = prepare_outputs.prepare_inflow_control_valve(
|
|
@@ -147,11 +145,9 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
|
|
|
147
145
|
print_inflow_control_valve += _format_inflow_control_valve(
|
|
148
146
|
well.well_name, lateral.lateral_number, df_inflow_control_valve, first
|
|
149
147
|
)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
print_autonomous_inflow_control_valve += _format_autonomous_inflow_control_valve(
|
|
154
|
-
well.well_number, df_autonomous_inflow_control_valve
|
|
148
|
+
print_density_driven += _format_density_driven(well.well_number, df_density_driven)
|
|
149
|
+
print_dual_rate_controlled_production += _format_dual_rate_controlled_production(
|
|
150
|
+
well.well_number, df_dual_rate_controlled_production
|
|
155
151
|
)
|
|
156
152
|
|
|
157
153
|
if figure_name is not None:
|
|
@@ -183,10 +179,10 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
|
|
|
183
179
|
bonus.append(f"{Keywords.AUTONOMOUS_INFLOW_CONTROL_DEVICE}{print_autonomous_inflow_control_device}\n/\n\n\n")
|
|
184
180
|
if print_inflow_control_valve:
|
|
185
181
|
bonus.append(f"{Keywords.WELL_SEGMENTS_VALVE}{print_inflow_control_valve}\n/\n\n\n")
|
|
186
|
-
if
|
|
182
|
+
if print_density_driven:
|
|
187
183
|
metadata = (
|
|
188
184
|
f"{'-' * 100}\n"
|
|
189
|
-
"-- This is how we model
|
|
185
|
+
"-- This is how we model DENSITY technology using sets of ACTIONX keywords.\n"
|
|
190
186
|
"-- The segment dP curves changes according to the segment water-\n"
|
|
191
187
|
"-- and gas volume fractions at downhole condition.\n"
|
|
192
188
|
"-- The value of Cv is adjusted according to the segment length and the number of\n"
|
|
@@ -194,16 +190,16 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
|
|
|
194
190
|
"-- volume fractions.\n"
|
|
195
191
|
f"{'-' * 100}\n\n\n"
|
|
196
192
|
)
|
|
197
|
-
bonus.append(metadata +
|
|
198
|
-
if
|
|
193
|
+
bonus.append(metadata + print_density_driven + "\n\n\n\n")
|
|
194
|
+
if print_dual_rate_controlled_production:
|
|
199
195
|
metadata = (
|
|
200
196
|
f"{'-' * 100}\n"
|
|
201
|
-
"-- This is how we model
|
|
197
|
+
"-- This is how we model DUALRCP technology using sets of ACTIONX keyword\n"
|
|
202
198
|
"-- the DP parameters change according to the segment water cut (at downhole condition )\n"
|
|
203
199
|
"-- and gas volume fraction (at downhole condition)\n"
|
|
204
200
|
f"{'-' * 100}\n\n\n"
|
|
205
201
|
)
|
|
206
|
-
bonus.append(metadata +
|
|
202
|
+
bonus.append(metadata + print_dual_rate_controlled_production + "\n\n\n\n")
|
|
207
203
|
|
|
208
204
|
return print_completion_data, print_well_segments, print_completion_segments, "".join(bonus)
|
|
209
205
|
|
|
@@ -439,34 +435,34 @@ def _format_inflow_control_valve(well_name: str, lateral_number: int, df_wsegicv
|
|
|
439
435
|
)
|
|
440
436
|
|
|
441
437
|
|
|
442
|
-
def
|
|
443
|
-
"""Formats well-segments for density
|
|
438
|
+
def _format_density_driven(well_number: int, df_wsegdensity: pd.DataFrame) -> str:
|
|
439
|
+
"""Formats well-segments for density driven valve.
|
|
444
440
|
|
|
445
441
|
Args:
|
|
446
442
|
well_number: The well's number
|
|
447
|
-
|
|
443
|
+
df_wsegdensity: Data to print.
|
|
448
444
|
|
|
449
445
|
Returns:
|
|
450
446
|
Formatted string.
|
|
451
447
|
"""
|
|
452
|
-
if
|
|
448
|
+
if df_wsegdensity.empty:
|
|
453
449
|
return ""
|
|
454
|
-
return prepare_outputs.
|
|
450
|
+
return prepare_outputs.print_wsegdensity(df_wsegdensity, well_number + 1)
|
|
455
451
|
|
|
456
452
|
|
|
457
|
-
def
|
|
458
|
-
"""Formats the
|
|
453
|
+
def _format_dual_rate_controlled_production(well_number: int, df_wsegdualrcp: pd.DataFrame) -> str:
|
|
454
|
+
"""Formats the DUALRCP section.
|
|
459
455
|
|
|
460
456
|
Args:
|
|
461
457
|
well_number: The well's number
|
|
462
|
-
|
|
458
|
+
df_wsegdualrcp: Data to print.
|
|
463
459
|
|
|
464
460
|
Returns:
|
|
465
461
|
Formatted string.
|
|
466
462
|
"""
|
|
467
|
-
if
|
|
463
|
+
if df_wsegdualrcp.empty:
|
|
468
464
|
return ""
|
|
469
|
-
return prepare_outputs.
|
|
465
|
+
return prepare_outputs.print_wsegdualrcp(df_wsegdualrcp, well_number + 1)
|
|
470
466
|
|
|
471
467
|
|
|
472
468
|
def _branch_revision(
|
|
@@ -173,7 +173,7 @@ def _check_for_errors(df_comp: pd.DataFrame, well_name: str, idx: int) -> None:
|
|
|
173
173
|
if df_comp[Headers.DEVICE_TYPE].iloc[idx] not in Content.DEVICE_TYPES:
|
|
174
174
|
raise CompletorError(
|
|
175
175
|
f"{df_comp[Headers.DEVICE_TYPE].iloc[idx]} is not a valid device type. "
|
|
176
|
-
"Valid types are PERF, AICD, ICD, VALVE,
|
|
176
|
+
"Valid types are PERF, AICD, ICD, VALVE, DENSITY, DUALRCP, and ICV."
|
|
177
177
|
)
|
|
178
178
|
if df_comp[Headers.ANNULUS].iloc[idx] not in Content.ANNULUS_TYPES:
|
|
179
179
|
raise CompletorError(
|
|
@@ -181,6 +181,36 @@ def _check_for_errors(df_comp: pd.DataFrame, well_name: str, idx: int) -> None:
|
|
|
181
181
|
)
|
|
182
182
|
|
|
183
183
|
|
|
184
|
+
def set_density_based(df_comp: pd.DataFrame) -> pd.DataFrame:
|
|
185
|
+
"""Set the column data format.
|
|
186
|
+
Args:
|
|
187
|
+
df_comp: Completion data.
|
|
188
|
+
Returns:
|
|
189
|
+
Updated device type to all density based.
|
|
190
|
+
"""
|
|
191
|
+
df_comp[Headers.DEVICE_TYPE] = np.where(
|
|
192
|
+
df_comp[Headers.DEVICE_TYPE] == Content.DENSITY_ACTIVATED_RECOVERY,
|
|
193
|
+
Content.DENSITY,
|
|
194
|
+
df_comp[Headers.DEVICE_TYPE],
|
|
195
|
+
)
|
|
196
|
+
return df_comp
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def set_dualrcp(df_comp: pd.DataFrame) -> pd.DataFrame:
|
|
200
|
+
"""Set the column data format.
|
|
201
|
+
Args:
|
|
202
|
+
df_comp: Completion data.
|
|
203
|
+
Returns:
|
|
204
|
+
Updated device type to all dual RCP based.
|
|
205
|
+
"""
|
|
206
|
+
df_comp[Headers.DEVICE_TYPE] = np.where(
|
|
207
|
+
df_comp[Headers.DEVICE_TYPE] == Content.AUTONOMOUS_INFLOW_CONTROL_VALVE,
|
|
208
|
+
Content.DUAL_RATE_CONTROLLED_PRODUCTION,
|
|
209
|
+
df_comp[Headers.DEVICE_TYPE],
|
|
210
|
+
)
|
|
211
|
+
return df_comp
|
|
212
|
+
|
|
213
|
+
|
|
184
214
|
def set_format_wsegvalv(df_temp: pd.DataFrame) -> pd.DataFrame:
|
|
185
215
|
"""Format the Well Segments Valve (WELSEGS) table.
|
|
186
216
|
|
|
@@ -242,11 +272,11 @@ def set_format_wsegaicd(df_temp: pd.DataFrame) -> pd.DataFrame:
|
|
|
242
272
|
return df_temp
|
|
243
273
|
|
|
244
274
|
|
|
245
|
-
def
|
|
246
|
-
"""Format the well segments Density
|
|
275
|
+
def set_format_wsegdensity(df_temp: pd.DataFrame) -> pd.DataFrame:
|
|
276
|
+
"""Format the well segments Density Driven (DENSITY) data.
|
|
247
277
|
|
|
248
278
|
Args:
|
|
249
|
-
df_temp: Well segments
|
|
279
|
+
df_temp: Well segments DENSITY device data.
|
|
250
280
|
|
|
251
281
|
Returns:
|
|
252
282
|
Updated data.
|
|
@@ -256,15 +286,15 @@ def set_format_wsegdar(df_temp: pd.DataFrame) -> pd.DataFrame:
|
|
|
256
286
|
columns = df_temp.columns.to_numpy()[1:]
|
|
257
287
|
df_temp[columns] = df_temp[columns].astype(np.float64)
|
|
258
288
|
# Create ID device column
|
|
259
|
-
df_temp.insert(0, Headers.DEVICE_TYPE, np.full(df_temp.shape[0], Content.
|
|
289
|
+
df_temp.insert(0, Headers.DEVICE_TYPE, np.full(df_temp.shape[0], Content.DENSITY))
|
|
260
290
|
return df_temp
|
|
261
291
|
|
|
262
292
|
|
|
263
|
-
def
|
|
264
|
-
"""Format the well segments
|
|
293
|
+
def set_format_wsegdualrcp(df_temp: pd.DataFrame) -> pd.DataFrame:
|
|
294
|
+
"""Format the well segments Dual RCP (DUALRCP) table.
|
|
265
295
|
|
|
266
296
|
Args:
|
|
267
|
-
df_temp: Well segments
|
|
297
|
+
df_temp: Well segments dual RCP table.
|
|
268
298
|
|
|
269
299
|
Returns:
|
|
270
300
|
Updated data.
|
|
@@ -274,7 +304,7 @@ def set_format_wsegaicv(df_temp: pd.DataFrame) -> pd.DataFrame:
|
|
|
274
304
|
columns = df_temp.columns.to_numpy()[1:]
|
|
275
305
|
df_temp[columns] = df_temp[columns].astype(np.float64)
|
|
276
306
|
# Create ID device column
|
|
277
|
-
df_temp.insert(0, Headers.DEVICE_TYPE, np.full(df_temp.shape[0], Content.
|
|
307
|
+
df_temp.insert(0, Headers.DEVICE_TYPE, np.full(df_temp.shape[0], Content.DUAL_RATE_CONTROLLED_PRODUCTION))
|
|
278
308
|
return df_temp
|
|
279
309
|
|
|
280
310
|
|
|
@@ -448,11 +448,11 @@ def prepare_device_layer(df_well: pd.DataFrame, df_tubing: pd.DataFrame, device_
|
|
|
448
448
|
df_well[Headers.DEVICE_TYPE] == Content.VALVE,
|
|
449
449
|
"/ -- Valve types",
|
|
450
450
|
np.where(
|
|
451
|
-
df_well[Headers.DEVICE_TYPE] == Content.
|
|
452
|
-
"/ --
|
|
451
|
+
df_well[Headers.DEVICE_TYPE] == Content.DENSITY,
|
|
452
|
+
"/ -- DENSITY types",
|
|
453
453
|
np.where(
|
|
454
|
-
df_well[Headers.DEVICE_TYPE] == Content.
|
|
455
|
-
"/ --
|
|
454
|
+
df_well[Headers.DEVICE_TYPE] == Content.DUAL_RATE_CONTROLLED_PRODUCTION,
|
|
455
|
+
"/ -- DUALRCP types",
|
|
456
456
|
np.where(
|
|
457
457
|
df_well[Headers.DEVICE_TYPE] == Content.INFLOW_CONTROL_VALVE, "/ -- ICV types", ""
|
|
458
458
|
),
|
|
@@ -852,6 +852,7 @@ def prepare_completion_segments(
|
|
|
852
852
|
}
|
|
853
853
|
)
|
|
854
854
|
compseg[Headers.EMPTY] = "/"
|
|
855
|
+
compseg.sort_values(Headers.START_MEASURED_DEPTH, inplace=True)
|
|
855
856
|
return compseg
|
|
856
857
|
|
|
857
858
|
|
|
@@ -1249,8 +1250,8 @@ def prepare_inflow_control_valve(
|
|
|
1249
1250
|
return wsegicv
|
|
1250
1251
|
|
|
1251
1252
|
|
|
1252
|
-
def
|
|
1253
|
-
"""Prepare data frame for
|
|
1253
|
+
def prepare_density_driven(well_name: str, df_well: pd.DataFrame, df_device: pd.DataFrame) -> pd.DataFrame:
|
|
1254
|
+
"""Prepare data frame for DENSITY.
|
|
1254
1255
|
|
|
1255
1256
|
Args:
|
|
1256
1257
|
well_name: Well name.
|
|
@@ -1258,7 +1259,7 @@ def prepare_density_activated_recovery(well_name: str, df_well: pd.DataFrame, df
|
|
|
1258
1259
|
df_device: Device data for this well and lateral.
|
|
1259
1260
|
|
|
1260
1261
|
Returns:
|
|
1261
|
-
DataFrame for
|
|
1262
|
+
DataFrame for DENSITY.
|
|
1262
1263
|
"""
|
|
1263
1264
|
df_well = df_well[(df_well[Headers.DEVICE_TYPE] == Content.PERFORATED) | (df_well[Headers.NUMBER_OF_DEVICES] > 0)]
|
|
1264
1265
|
if df_well.shape[0] == 0:
|
|
@@ -1270,34 +1271,42 @@ def prepare_density_activated_recovery(well_name: str, df_well: pd.DataFrame, df
|
|
|
1270
1271
|
right_on=[Headers.TUBING_MEASURED_DEPTH],
|
|
1271
1272
|
direction="nearest",
|
|
1272
1273
|
)
|
|
1273
|
-
df_merge = df_merge[df_merge[Headers.DEVICE_TYPE] == Content.
|
|
1274
|
-
|
|
1274
|
+
df_merge = df_merge[df_merge[Headers.DEVICE_TYPE] == Content.DENSITY]
|
|
1275
|
+
wsegdensity = pd.DataFrame()
|
|
1275
1276
|
if df_merge.shape[0] > 0:
|
|
1276
|
-
|
|
1277
|
-
|
|
1277
|
+
wsegdensity[Headers.WELL] = [well_name] * df_merge.shape[0]
|
|
1278
|
+
wsegdensity[Headers.START_SEGMENT_NUMBER] = df_merge[Headers.START_SEGMENT_NUMBER].to_numpy()
|
|
1278
1279
|
# the Cv is already corrected by the scaling factor
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1280
|
+
wsegdensity[Headers.FLOW_COEFFICIENT] = df_merge[Headers.FLOW_COEFFICIENT].to_numpy()
|
|
1281
|
+
wsegdensity[Headers.OIL_FLOW_CROSS_SECTIONAL_AREA] = df_merge[Headers.OIL_FLOW_CROSS_SECTIONAL_AREA].to_numpy()
|
|
1282
|
+
wsegdensity[Headers.GAS_FLOW_CROSS_SECTIONAL_AREA] = df_merge[Headers.GAS_FLOW_CROSS_SECTIONAL_AREA].to_numpy()
|
|
1283
|
+
wsegdensity[Headers.WATER_FLOW_CROSS_SECTIONAL_AREA] = df_merge[
|
|
1284
|
+
Headers.WATER_FLOW_CROSS_SECTIONAL_AREA
|
|
1285
|
+
].to_numpy()
|
|
1286
|
+
wsegdensity[Headers.WATER_HOLDUP_FRACTION_LOW_CUTOFF] = df_merge[
|
|
1284
1287
|
Headers.WATER_HOLDUP_FRACTION_LOW_CUTOFF
|
|
1285
1288
|
].to_numpy()
|
|
1286
|
-
|
|
1289
|
+
wsegdensity[Headers.WATER_HOLDUP_FRACTION_HIGH_CUTOFF] = df_merge[
|
|
1287
1290
|
Headers.WATER_HOLDUP_FRACTION_HIGH_CUTOFF
|
|
1288
1291
|
].to_numpy()
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1292
|
+
wsegdensity[Headers.GAS_HOLDUP_FRACTION_LOW_CUTOFF] = df_merge[
|
|
1293
|
+
Headers.GAS_HOLDUP_FRACTION_LOW_CUTOFF
|
|
1294
|
+
].to_numpy()
|
|
1295
|
+
wsegdensity[Headers.GAS_HOLDUP_FRACTION_HIGH_CUTOFF] = df_merge[
|
|
1296
|
+
Headers.GAS_HOLDUP_FRACTION_HIGH_CUTOFF
|
|
1297
|
+
].to_numpy()
|
|
1298
|
+
wsegdensity[Headers.DEFAULTS] = "5*"
|
|
1299
|
+
wsegdensity[Headers.MAX_FLOW_CROSS_SECTIONAL_AREA] = wsegdensity[
|
|
1300
|
+
Headers.OIL_FLOW_CROSS_SECTIONAL_AREA
|
|
1301
|
+
].to_numpy()
|
|
1302
|
+
wsegdensity[Headers.EMPTY] = "/"
|
|
1303
|
+
return wsegdensity
|
|
1295
1304
|
|
|
1296
1305
|
|
|
1297
|
-
def
|
|
1306
|
+
def prepare_dual_rate_controlled_production(
|
|
1298
1307
|
well_name: str, df_well: pd.DataFrame, df_device: pd.DataFrame
|
|
1299
1308
|
) -> pd.DataFrame:
|
|
1300
|
-
"""Prepare data frame for
|
|
1309
|
+
"""Prepare data frame for DUALRCP.
|
|
1301
1310
|
|
|
1302
1311
|
Args:
|
|
1303
1312
|
well_name: Well name.
|
|
@@ -1305,7 +1314,7 @@ def prepare_autonomous_inflow_control_valve(
|
|
|
1305
1314
|
df_device: Device data for this well and lateral.
|
|
1306
1315
|
|
|
1307
1316
|
Returns:
|
|
1308
|
-
DataFrame for
|
|
1317
|
+
DataFrame for DUALRCP.
|
|
1309
1318
|
"""
|
|
1310
1319
|
df_well = df_well[(df_well[Headers.DEVICE_TYPE] == Content.PERFORATED) | (df_well[Headers.NUMBER_OF_DEVICES] > 0)]
|
|
1311
1320
|
if df_well.shape[0] == 0:
|
|
@@ -1317,53 +1326,53 @@ def prepare_autonomous_inflow_control_valve(
|
|
|
1317
1326
|
right_on=[Headers.TUBING_MEASURED_DEPTH],
|
|
1318
1327
|
direction="nearest",
|
|
1319
1328
|
)
|
|
1320
|
-
df_merge = df_merge[df_merge[Headers.DEVICE_TYPE] == Content.
|
|
1321
|
-
|
|
1329
|
+
df_merge = df_merge[df_merge[Headers.DEVICE_TYPE] == Content.DUAL_RATE_CONTROLLED_PRODUCTION]
|
|
1330
|
+
wsegdualrcp = pd.DataFrame()
|
|
1322
1331
|
if df_merge.shape[0] > 0:
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
return
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
def
|
|
1356
|
-
"""Print
|
|
1332
|
+
wsegdualrcp[Headers.WELL] = [well_name] * df_merge.shape[0]
|
|
1333
|
+
wsegdualrcp[Headers.START_SEGMENT_NUMBER] = df_merge[Headers.START_SEGMENT_NUMBER].to_numpy()
|
|
1334
|
+
wsegdualrcp[Headers.END_SEGMENT_NUMBER] = df_merge[Headers.START_SEGMENT_NUMBER].to_numpy()
|
|
1335
|
+
wsegdualrcp[Headers.ALPHA_MAIN] = df_merge[Headers.ALPHA_MAIN].to_numpy()
|
|
1336
|
+
wsegdualrcp[Headers.SCALE_FACTOR] = df_merge[Headers.SCALE_FACTOR].to_numpy()
|
|
1337
|
+
wsegdualrcp[Headers.CALIBRATION_FLUID_DENSITY] = df_merge[Headers.DUALRCP_CALIBRATION_FLUID_DENSITY].to_numpy()
|
|
1338
|
+
wsegdualrcp[Headers.CALIBRATION_FLUID_VISCOSITY] = df_merge[Headers.DUALRCP_FLUID_VISCOSITY].to_numpy()
|
|
1339
|
+
wsegdualrcp[Headers.DEF] = ["5*"] * df_merge.shape[0]
|
|
1340
|
+
wsegdualrcp[Headers.X_MAIN] = df_merge[Headers.X_MAIN].to_numpy()
|
|
1341
|
+
wsegdualrcp[Headers.Y_MAIN] = df_merge[Headers.Y_MAIN].to_numpy()
|
|
1342
|
+
wsegdualrcp[Headers.FLAG] = [Headers.OPEN] * df_merge.shape[0]
|
|
1343
|
+
wsegdualrcp[Headers.A_MAIN] = df_merge[Headers.A_MAIN].to_numpy()
|
|
1344
|
+
wsegdualrcp[Headers.B_MAIN] = df_merge[Headers.B_MAIN].to_numpy()
|
|
1345
|
+
wsegdualrcp[Headers.C_MAIN] = df_merge[Headers.C_MAIN].to_numpy()
|
|
1346
|
+
wsegdualrcp[Headers.D_MAIN] = df_merge[Headers.D_MAIN].to_numpy()
|
|
1347
|
+
wsegdualrcp[Headers.E_MAIN] = df_merge[Headers.E_MAIN].to_numpy()
|
|
1348
|
+
wsegdualrcp[Headers.F_MAIN] = df_merge[Headers.F_MAIN].to_numpy()
|
|
1349
|
+
wsegdualrcp[Headers.ALPHA_PILOT] = df_merge[Headers.ALPHA_PILOT].to_numpy()
|
|
1350
|
+
wsegdualrcp[Headers.X_PILOT] = df_merge[Headers.X_PILOT].to_numpy()
|
|
1351
|
+
wsegdualrcp[Headers.Y_PILOT] = df_merge[Headers.Y_PILOT].to_numpy()
|
|
1352
|
+
wsegdualrcp[Headers.A_PILOT] = df_merge[Headers.A_PILOT].to_numpy()
|
|
1353
|
+
wsegdualrcp[Headers.B_PILOT] = df_merge[Headers.B_PILOT].to_numpy()
|
|
1354
|
+
wsegdualrcp[Headers.C_PILOT] = df_merge[Headers.C_PILOT].to_numpy()
|
|
1355
|
+
wsegdualrcp[Headers.D_PILOT] = df_merge[Headers.D_PILOT].to_numpy()
|
|
1356
|
+
wsegdualrcp[Headers.E_PILOT] = df_merge[Headers.E_PILOT].to_numpy()
|
|
1357
|
+
wsegdualrcp[Headers.F_PILOT] = df_merge[Headers.F_PILOT].to_numpy()
|
|
1358
|
+
wsegdualrcp[Headers.DUALRCP_WATER_CUT] = df_merge[Headers.DUALRCP_WATER_CUT].to_numpy()
|
|
1359
|
+
wsegdualrcp[Headers.DUALRCP_GAS_HOLDUP_FRACTION] = df_merge[Headers.DUALRCP_GAS_HOLDUP_FRACTION].to_numpy()
|
|
1360
|
+
wsegdualrcp[Headers.EMPTY] = "/"
|
|
1361
|
+
return wsegdualrcp
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
def print_wsegdensity(df_wsegdensity: pd.DataFrame, well_number: int) -> str:
|
|
1365
|
+
"""Print DENSITY devices.
|
|
1357
1366
|
|
|
1358
1367
|
Args:
|
|
1359
|
-
|
|
1368
|
+
df_wsegdensity: Output from function prepare_wsegdensity.
|
|
1360
1369
|
well_number: Well number.
|
|
1361
1370
|
|
|
1362
1371
|
Returns:
|
|
1363
1372
|
Formatted actions to be included in the output file.
|
|
1364
1373
|
|
|
1365
1374
|
Raises:
|
|
1366
|
-
CompletorError: If there are to many wells and/or segments with
|
|
1375
|
+
CompletorError: If there are to many wells and/or segments with DENSITY.
|
|
1367
1376
|
"""
|
|
1368
1377
|
header = [
|
|
1369
1378
|
[
|
|
@@ -1403,9 +1412,9 @@ def print_wsegdar(df_wsegdar: pd.DataFrame, well_number: int) -> str:
|
|
|
1403
1412
|
sign_gas = [">", "<=", "<", ""]
|
|
1404
1413
|
suvtrig = ["0", "0", "1", "2"]
|
|
1405
1414
|
action = "UDQ\n"
|
|
1406
|
-
for idx in range(
|
|
1407
|
-
segment_number =
|
|
1408
|
-
well_name =
|
|
1415
|
+
for idx in range(df_wsegdensity.shape[0]):
|
|
1416
|
+
segment_number = df_wsegdensity[Headers.START_SEGMENT_NUMBER].iloc[idx]
|
|
1417
|
+
well_name = df_wsegdensity[Headers.WELL].iloc[idx]
|
|
1409
1418
|
action += f" ASSIGN SUVTRIG {well_name} {segment_number} 0 /\n"
|
|
1410
1419
|
action += "/\n\n"
|
|
1411
1420
|
iaction = 3
|
|
@@ -1414,25 +1423,25 @@ def print_wsegdar(df_wsegdar: pd.DataFrame, well_number: int) -> str:
|
|
|
1414
1423
|
for itm in header[iaction]:
|
|
1415
1424
|
header_string += " " + itm
|
|
1416
1425
|
action += header_string.rstrip() + "\n"
|
|
1417
|
-
for idx in range(
|
|
1418
|
-
segment_number =
|
|
1419
|
-
print_df =
|
|
1426
|
+
for idx in range(df_wsegdensity.shape[0]):
|
|
1427
|
+
segment_number = df_wsegdensity[Headers.START_SEGMENT_NUMBER].iloc[idx]
|
|
1428
|
+
print_df = df_wsegdensity[df_wsegdensity[Headers.START_SEGMENT_NUMBER] == segment_number]
|
|
1420
1429
|
print_df = print_df[header[iaction]]
|
|
1421
1430
|
print_df = dataframe_tostring(print_df, True, False, False) + "\n"
|
|
1422
1431
|
action += print_df
|
|
1423
1432
|
action += "/\n\n"
|
|
1424
|
-
for idx in range(
|
|
1425
|
-
segment_number =
|
|
1426
|
-
well_name =
|
|
1427
|
-
water_holdup_fraction_low_cutoff =
|
|
1428
|
-
water_holdup_fraction_high_cutoff =
|
|
1429
|
-
gas_holdup_fraction_low_cutoff =
|
|
1430
|
-
gas_holdup_fraction_high_cutoff =
|
|
1433
|
+
for idx in range(df_wsegdensity.shape[0]):
|
|
1434
|
+
segment_number = df_wsegdensity[Headers.START_SEGMENT_NUMBER].iloc[idx]
|
|
1435
|
+
well_name = df_wsegdensity[Headers.WELL].iloc[idx]
|
|
1436
|
+
water_holdup_fraction_low_cutoff = df_wsegdensity[Headers.WATER_HOLDUP_FRACTION_LOW_CUTOFF].iloc[idx]
|
|
1437
|
+
water_holdup_fraction_high_cutoff = df_wsegdensity[Headers.WATER_HOLDUP_FRACTION_HIGH_CUTOFF].iloc[idx]
|
|
1438
|
+
gas_holdup_fraction_low_cutoff = df_wsegdensity[Headers.GAS_HOLDUP_FRACTION_LOW_CUTOFF].iloc[idx]
|
|
1439
|
+
gas_holdup_fraction_high_cutoff = df_wsegdensity[Headers.GAS_HOLDUP_FRACTION_HIGH_CUTOFF].iloc[idx]
|
|
1431
1440
|
for iaction in range(2):
|
|
1432
1441
|
act_number = iaction + 1
|
|
1433
1442
|
act_name = f"D{well_number:03d}{segment_number:03d}{act_number:1d}"
|
|
1434
1443
|
if len(act_name) > 8:
|
|
1435
|
-
raise CompletorError("Too many wells and/or too many segments with
|
|
1444
|
+
raise CompletorError("Too many wells and/or too many segments with DENSITY")
|
|
1436
1445
|
action += (
|
|
1437
1446
|
f"ACTIONX\n{act_name} 1000000 /\n"
|
|
1438
1447
|
f"SWHF '{well_name}' {segment_number} "
|
|
@@ -1442,7 +1451,7 @@ def print_wsegdar(df_wsegdar: pd.DataFrame, well_number: int) -> str:
|
|
|
1442
1451
|
f"SUVTRIG '{well_name}' {segment_number} "
|
|
1443
1452
|
f"= {suvtrig[iaction]} /\n/\n\n"
|
|
1444
1453
|
)
|
|
1445
|
-
print_df =
|
|
1454
|
+
print_df = df_wsegdensity[df_wsegdensity[Headers.START_SEGMENT_NUMBER] == segment_number]
|
|
1446
1455
|
print_df = print_df[header[iaction]] # type: ignore
|
|
1447
1456
|
header_string = Keywords.WELL_SEGMENTS_VALVE + "\n--"
|
|
1448
1457
|
for item in header[iaction]:
|
|
@@ -1460,7 +1469,7 @@ def print_wsegdar(df_wsegdar: pd.DataFrame, well_number: int) -> str:
|
|
|
1460
1469
|
act_number = iaction + 1
|
|
1461
1470
|
act_name = f"D{well_number:03d}{segment_number:03d}{act_number:1d}"
|
|
1462
1471
|
if len(act_name) > 8:
|
|
1463
|
-
raise CompletorError("Too many wells and/or too many segments with
|
|
1472
|
+
raise CompletorError("Too many wells and/or too many segments with DENSITY")
|
|
1464
1473
|
action += (
|
|
1465
1474
|
f"ACTIONX\n{act_name} 1000000 /\n"
|
|
1466
1475
|
f"SGHF '{well_name}' {segment_number} "
|
|
@@ -1468,7 +1477,7 @@ def print_wsegdar(df_wsegdar: pd.DataFrame, well_number: int) -> str:
|
|
|
1468
1477
|
f"SUVTRIG '{well_name}' {segment_number} "
|
|
1469
1478
|
f"= {suvtrig[iaction]} /\n/\n\n"
|
|
1470
1479
|
)
|
|
1471
|
-
print_df =
|
|
1480
|
+
print_df = df_wsegdensity[df_wsegdensity[Headers.START_SEGMENT_NUMBER] == segment_number]
|
|
1472
1481
|
print_df = print_df[header[iaction]] # type: ignore
|
|
1473
1482
|
header_string = Keywords.WELL_SEGMENTS_VALVE + "\n--"
|
|
1474
1483
|
for item in header[iaction]:
|
|
@@ -1483,7 +1492,7 @@ def print_wsegdar(df_wsegdar: pd.DataFrame, well_number: int) -> str:
|
|
|
1483
1492
|
act_number = iaction + 1
|
|
1484
1493
|
act_name = f"D{well_number:03d}{segment_number:03d}{act_number:1d}"
|
|
1485
1494
|
if len(act_name) > 8:
|
|
1486
|
-
raise CompletorError("Too many wells and/or too many segments with
|
|
1495
|
+
raise CompletorError("Too many wells and/or too many segments with DENSITY")
|
|
1487
1496
|
action += (
|
|
1488
1497
|
f"ACTIONX\n{act_name} 1000000 /\n"
|
|
1489
1498
|
f"SWHF '{well_name}' {segment_number} "
|
|
@@ -1491,7 +1500,7 @@ def print_wsegdar(df_wsegdar: pd.DataFrame, well_number: int) -> str:
|
|
|
1491
1500
|
f"SUVTRIG '{well_name}' {segment_number} "
|
|
1492
1501
|
f"= {suvtrig[iaction]} /\n/\n\n"
|
|
1493
1502
|
)
|
|
1494
|
-
print_df =
|
|
1503
|
+
print_df = df_wsegdensity[df_wsegdensity[Headers.START_SEGMENT_NUMBER] == segment_number]
|
|
1495
1504
|
print_df = print_df[header[iaction]] # type: ignore
|
|
1496
1505
|
header_string = Keywords.WELL_SEGMENTS_VALVE + "\n--"
|
|
1497
1506
|
for item in header[iaction]:
|
|
@@ -1504,18 +1513,18 @@ def print_wsegdar(df_wsegdar: pd.DataFrame, well_number: int) -> str:
|
|
|
1504
1513
|
return action
|
|
1505
1514
|
|
|
1506
1515
|
|
|
1507
|
-
def
|
|
1508
|
-
"""Print for
|
|
1516
|
+
def print_wsegdualrcp(df_wsegdualrcp: pd.DataFrame, well_number: int) -> str:
|
|
1517
|
+
"""Print for DUALRCP devices.
|
|
1509
1518
|
|
|
1510
1519
|
Args:
|
|
1511
|
-
|
|
1520
|
+
df_wsegdualrcp: Output from function prepare_wsegdualrcp.
|
|
1512
1521
|
well_number: Well number.
|
|
1513
1522
|
|
|
1514
1523
|
Returns:
|
|
1515
1524
|
Formatted actions to be included in the output file.
|
|
1516
1525
|
|
|
1517
1526
|
Raises:
|
|
1518
|
-
CompletorError: If there are too many wells and/or segments with
|
|
1527
|
+
CompletorError: If there are too many wells and/or segments with DUALRCP.
|
|
1519
1528
|
"""
|
|
1520
1529
|
header = [
|
|
1521
1530
|
[
|
|
@@ -1583,17 +1592,17 @@ def print_wsegaicv(df_wsegaicv: pd.DataFrame, well_number: int) -> str:
|
|
|
1583
1592
|
sign_gas = ["<", ">="]
|
|
1584
1593
|
operator = ["AND", "OR"]
|
|
1585
1594
|
action = ""
|
|
1586
|
-
for idx in range(
|
|
1587
|
-
segment_number =
|
|
1588
|
-
well_name =
|
|
1589
|
-
wct =
|
|
1590
|
-
ghf =
|
|
1595
|
+
for idx in range(df_wsegdualrcp.shape[0]):
|
|
1596
|
+
segment_number = df_wsegdualrcp[Headers.START_SEGMENT_NUMBER].iloc[idx]
|
|
1597
|
+
well_name = df_wsegdualrcp[Headers.WELL].iloc[idx]
|
|
1598
|
+
wct = df_wsegdualrcp[Headers.DUALRCP_WATER_CUT].iloc[idx]
|
|
1599
|
+
ghf = df_wsegdualrcp[Headers.DUALRCP_GAS_HOLDUP_FRACTION].iloc[idx]
|
|
1591
1600
|
# LOWWCT_LOWGHF
|
|
1592
1601
|
for iaction in range(2):
|
|
1593
1602
|
act_number = iaction + 1
|
|
1594
1603
|
act_name = f"V{well_number:03d}{segment_number:03d}{act_number:1d}"
|
|
1595
1604
|
if len(act_name) > 8:
|
|
1596
|
-
raise CompletorError("Too many wells and/or too many segments with
|
|
1605
|
+
raise CompletorError("Too many wells and/or too many segments with DUALRCP")
|
|
1597
1606
|
action += (
|
|
1598
1607
|
f"ACTIONX\n{act_name} 1000000 /\n"
|
|
1599
1608
|
f"SUWCT '{well_name}' {segment_number} {sign_water[iaction]} "
|
|
@@ -1601,7 +1610,7 @@ def print_wsegaicv(df_wsegaicv: pd.DataFrame, well_number: int) -> str:
|
|
|
1601
1610
|
f"SGHF '{well_name}' {segment_number} {sign_gas[iaction]} {ghf} /\n/\n"
|
|
1602
1611
|
)
|
|
1603
1612
|
|
|
1604
|
-
print_df =
|
|
1613
|
+
print_df = df_wsegdualrcp[df_wsegdualrcp[Headers.START_SEGMENT_NUMBER] == segment_number]
|
|
1605
1614
|
print_df = print_df[header[iaction]]
|
|
1606
1615
|
print_df.columns = new_column
|
|
1607
1616
|
print_df = Keywords.AUTONOMOUS_INFLOW_CONTROL_DEVICE + "\n" + dataframe_tostring(print_df, True)
|
|
@@ -47,8 +47,8 @@ class ReadCasefile:
|
|
|
47
47
|
|
|
48
48
|
This class reads the case/input file of the Completor program.
|
|
49
49
|
It reads the following keywords:
|
|
50
|
-
|
|
51
|
-
INFLOW_CONTROL_DEVICE,
|
|
50
|
+
COMPLETION, SEGMENTLENGTH, JOINTLENGTH, AUTONOMOUS_INFLOW_CONTROL_DEVICE, WELL_SEGMENTS_VALVE,
|
|
51
|
+
INFLOW_CONTROL_DEVICE, DENSITY_DRIVEN, DUAL_RATE_CONTROLLED_PRODUCTION, INFLOW_CONTROL_VALVE.
|
|
52
52
|
In the absence of some keywords, the program uses the default values.
|
|
53
53
|
|
|
54
54
|
Attributes:
|
|
@@ -63,8 +63,8 @@ class ReadCasefile:
|
|
|
63
63
|
wsegsicd_table (pd.DataFrame): INFLOW_CONTROL_DEVICE.
|
|
64
64
|
wsegvalv_table (pd.DataFrame): WELL_SEGMENTS_VALVE.
|
|
65
65
|
wsegicv_table (pd.DataFrame): INFLOW_CONTROL_VALVE.
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
wsegdensity_table (pd.DataFrame): DENSITY_DRIVEN.
|
|
67
|
+
wsegdualrcp_table (pd.DataFrame): DUAL_RATE_CONTROLLED_PRODUCTION.
|
|
68
68
|
strict (bool): USE_STRICT. If TRUE it will exit if any lateral is not defined in the case-file. Default to TRUE.
|
|
69
69
|
lat2device (pd.DataFrame): LATERAL_TO_DEVICE.
|
|
70
70
|
gp_perf_devicelayer (bool): GRAVEL_PACKED_PERFORATED_DEVICELAYER. If TRUE all wells with
|
|
@@ -98,8 +98,8 @@ class ReadCasefile:
|
|
|
98
98
|
self.wsegaicd_table = pd.DataFrame()
|
|
99
99
|
self.wsegsicd_table = pd.DataFrame()
|
|
100
100
|
self.wsegvalv_table = pd.DataFrame()
|
|
101
|
-
self.
|
|
102
|
-
self.
|
|
101
|
+
self.wsegdensity_table = pd.DataFrame()
|
|
102
|
+
self.wsegdualrcp_table = pd.DataFrame()
|
|
103
103
|
self.wsegicv_table = pd.DataFrame()
|
|
104
104
|
self.lat2device = pd.DataFrame()
|
|
105
105
|
self.mapfile: pd.DataFrame | str | None = None
|
|
@@ -116,8 +116,8 @@ class ReadCasefile:
|
|
|
116
116
|
self.read_wsegaicd()
|
|
117
117
|
self.read_wsegvalv()
|
|
118
118
|
self.read_wsegsicd()
|
|
119
|
-
self.
|
|
120
|
-
self.
|
|
119
|
+
self.read_wsegdensity()
|
|
120
|
+
self.read_wsegdualrcp()
|
|
121
121
|
self.read_wsegicv()
|
|
122
122
|
self.read_lat2device()
|
|
123
123
|
self.read_minimum_segment_length()
|
|
@@ -155,6 +155,10 @@ class ReadCasefile:
|
|
|
155
155
|
df_temp = input_validation.check_default_non_packer(df_temp)
|
|
156
156
|
# Fix the data types format
|
|
157
157
|
df_temp = input_validation.set_format_completion(df_temp)
|
|
158
|
+
# Fix the Density based
|
|
159
|
+
df_temp = input_validation.set_density_based(df_temp)
|
|
160
|
+
# Fix the Dual RCP
|
|
161
|
+
df_temp = input_validation.set_dualrcp(df_temp)
|
|
158
162
|
# Check overall user inputs on completion
|
|
159
163
|
input_validation.assess_completion(df_temp)
|
|
160
164
|
df_temp = self.read_icv_tubing(df_temp)
|
|
@@ -457,19 +461,25 @@ class ReadCasefile:
|
|
|
457
461
|
f"Not all device in COMPLETION is specified in {Keywords.AUTONOMOUS_INFLOW_CONTROL_DEVICE}"
|
|
458
462
|
)
|
|
459
463
|
|
|
460
|
-
def
|
|
461
|
-
"""Read the
|
|
464
|
+
def read_wsegdensity(self) -> None:
|
|
465
|
+
"""Read the DENSITY keyword in the case file.
|
|
462
466
|
|
|
463
467
|
Raises:
|
|
464
|
-
ValueError: If there are invalid entries in
|
|
465
|
-
CompletorError: If not all device in COMPLETION is specified in
|
|
466
|
-
If
|
|
468
|
+
ValueError: If there are invalid entries in DENSITY.
|
|
469
|
+
CompletorError: If not all device in COMPLETION is specified in DENSITY.
|
|
470
|
+
If DENSITY keyword not defined, when DENSITY is used in the completion.
|
|
467
471
|
"""
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
472
|
+
density_index_start, density_index_end = parse.locate_keyword(self.content, Keywords.DENSITY)
|
|
473
|
+
dar_index_start, dar_index_end = parse.locate_keyword(self.content, Keywords.DENSITY_ACTIVATED_RECOVERY)
|
|
474
|
+
|
|
475
|
+
# Determine which keyword is present
|
|
476
|
+
if density_index_start == density_index_end and dar_index_start == dar_index_end:
|
|
477
|
+
if (
|
|
478
|
+
Content.DENSITY in self.completion_table[Headers.DEVICE_TYPE]
|
|
479
|
+
or Content.DENSITY_ACTIVATED_RECOVERY in self.completion_table[Headers.DEVICE_TYPE]
|
|
480
|
+
):
|
|
471
481
|
raise CompletorError(
|
|
472
|
-
f"{Keywords.
|
|
482
|
+
f"{Keywords.DENSITY} keyword must be defined, if DENSITY is used in the completion."
|
|
473
483
|
)
|
|
474
484
|
else:
|
|
475
485
|
# Table headers
|
|
@@ -485,42 +495,56 @@ class ReadCasefile:
|
|
|
485
495
|
Headers.GAS_HOLDUP_FRACTION_HIGH_CUTOFF,
|
|
486
496
|
]
|
|
487
497
|
|
|
498
|
+
# Get start and end index from correct keyword
|
|
499
|
+
if not density_index_start == density_index_end:
|
|
500
|
+
start_index, end_index = density_index_start, density_index_end
|
|
501
|
+
key = Keywords.DENSITY
|
|
502
|
+
content = Content.DENSITY
|
|
503
|
+
else:
|
|
504
|
+
start_index, end_index = dar_index_start, dar_index_end
|
|
505
|
+
key = Keywords.DENSITY_ACTIVATED_RECOVERY
|
|
506
|
+
content = Content.DENSITY_ACTIVATED_RECOVERY
|
|
488
507
|
# Fix table format
|
|
489
|
-
|
|
490
|
-
self.
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
raise CompletorError(
|
|
498
|
-
f"Not all device in COMPLETION is specified in {Keywords.DENSITY_ACTIVATED_RECOVERY}"
|
|
499
|
-
)
|
|
508
|
+
self.wsegdensity_table = input_validation.set_format_wsegdensity(
|
|
509
|
+
self._create_dataframe_with_columns(header, start_index, end_index)
|
|
510
|
+
)
|
|
511
|
+
device_checks = self.completion_table[self.completion_table[Headers.DEVICE_TYPE] == content][
|
|
512
|
+
Headers.DEVICE_NUMBER
|
|
513
|
+
].to_numpy()
|
|
514
|
+
if not check_contents(device_checks, self.wsegdensity_table[Headers.DEVICE_NUMBER].to_numpy()):
|
|
515
|
+
raise CompletorError(f"Not all device in COMPLETION is specified in {key}")
|
|
500
516
|
|
|
501
|
-
def
|
|
502
|
-
"""Read the
|
|
517
|
+
def read_wsegdualrcp(self) -> None:
|
|
518
|
+
"""Read the DUALRCP keyword in the case file.
|
|
503
519
|
|
|
504
520
|
Raises:
|
|
505
|
-
ValueError: If invalid entries in
|
|
506
|
-
CompletorError:
|
|
507
|
-
If all devices in COMPLETION are not specified in
|
|
521
|
+
ValueError: If invalid entries in DUALRCP.
|
|
522
|
+
CompletorError: DUALRCP keyword not defined when DUALRCP is used in completion.
|
|
523
|
+
If all devices in COMPLETION are not specified in DUALRCP.
|
|
508
524
|
"""
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
525
|
+
dualrcp_index_start, dualrcp_index_end = parse.locate_keyword(
|
|
526
|
+
self.content, Keywords.DUAL_RATE_CONTROLLED_PRODUCTION
|
|
527
|
+
)
|
|
528
|
+
aicv_index_start, aicv_index_end = parse.locate_keyword(self.content, Keywords.AUTONOMOUS_INFLOW_CONTROL_VALVE)
|
|
529
|
+
|
|
530
|
+
# Determine which keyword is present
|
|
531
|
+
if dualrcp_index_start == dualrcp_index_end and aicv_index_start == aicv_index_end:
|
|
532
|
+
if (
|
|
533
|
+
Content.DUAL_RATE_CONTROLLED_PRODUCTION in self.completion_table[Headers.DEVICE_TYPE]
|
|
534
|
+
or Content.AUTONOMOUS_INFLOW_CONTROL_VALVE in self.completion_table[Headers.DEVICE_TYPE]
|
|
535
|
+
):
|
|
512
536
|
raise CompletorError(
|
|
513
|
-
f"{Keywords.
|
|
514
|
-
"if
|
|
537
|
+
f"{Keywords.DUAL_RATE_CONTROLLED_PRODUCTION} keyword must be defined, "
|
|
538
|
+
"if DUALRCP is used in the completion."
|
|
515
539
|
)
|
|
516
540
|
else:
|
|
517
541
|
# Table headers
|
|
518
542
|
header = [
|
|
519
543
|
Headers.DEVICE_NUMBER,
|
|
520
|
-
Headers.
|
|
521
|
-
Headers.
|
|
522
|
-
Headers.
|
|
523
|
-
Headers.
|
|
544
|
+
Headers.DUALRCP_WATER_CUT,
|
|
545
|
+
Headers.DUALRCP_GAS_HOLDUP_FRACTION,
|
|
546
|
+
Headers.DUALRCP_CALIBRATION_FLUID_DENSITY,
|
|
547
|
+
Headers.DUALRCP_FLUID_VISCOSITY,
|
|
524
548
|
Headers.ALPHA_MAIN,
|
|
525
549
|
Headers.X_MAIN,
|
|
526
550
|
Headers.Y_MAIN,
|
|
@@ -540,18 +564,25 @@ class ReadCasefile:
|
|
|
540
564
|
Headers.E_PILOT,
|
|
541
565
|
Headers.F_PILOT,
|
|
542
566
|
]
|
|
567
|
+
# Get start and end index from correct keyword
|
|
568
|
+
if not dualrcp_index_start == dualrcp_index_end:
|
|
569
|
+
start_index, end_index = dualrcp_index_start, dualrcp_index_end
|
|
570
|
+
key = Keywords.DUAL_RATE_CONTROLLED_PRODUCTION
|
|
571
|
+
content = Content.DUAL_RATE_CONTROLLED_PRODUCTION
|
|
572
|
+
else:
|
|
573
|
+
start_index, end_index = aicv_index_start, aicv_index_end
|
|
574
|
+
key = Keywords.AUTONOMOUS_INFLOW_CONTROL_VALVE
|
|
575
|
+
content = Content.AUTONOMOUS_INFLOW_CONTROL_VALVE
|
|
543
576
|
# Fix table format
|
|
544
|
-
self.
|
|
577
|
+
self.wsegdualrcp_table = input_validation.set_format_wsegdualrcp(
|
|
545
578
|
self._create_dataframe_with_columns(header, start_index, end_index)
|
|
546
579
|
)
|
|
547
|
-
# Check if the device in COMPLETION is exist in
|
|
548
|
-
device_checks = self.completion_table[
|
|
549
|
-
|
|
550
|
-
]
|
|
551
|
-
if not check_contents(device_checks, self.
|
|
552
|
-
raise CompletorError(
|
|
553
|
-
f"Not all devices in COMPLETION are specified in {Keywords.AUTONOMOUS_INFLOW_CONTROL_VALVE}"
|
|
554
|
-
)
|
|
580
|
+
# Check if the device in COMPLETION is exist in DUALRCP
|
|
581
|
+
device_checks = self.completion_table[self.completion_table[Headers.DEVICE_TYPE] == content][
|
|
582
|
+
Headers.DEVICE_NUMBER
|
|
583
|
+
].to_numpy()
|
|
584
|
+
if not check_contents(device_checks, self.wsegdualrcp_table[Headers.DEVICE_NUMBER].to_numpy()):
|
|
585
|
+
raise CompletorError(f"Not all devices in COMPLETION are specified in {key}")
|
|
555
586
|
|
|
556
587
|
def read_wsegicv(self) -> None:
|
|
557
588
|
"""Read INFLOW_CONTROL_VALVE keyword in the case file.
|
|
@@ -191,8 +191,8 @@ def get_active_wells(completion_table: pd.DataFrame, gp_perf_devicelayer: bool)
|
|
|
191
191
|
perf_check = completion_table[Headers.DEVICE_TYPE].isin(
|
|
192
192
|
[
|
|
193
193
|
Content.AUTONOMOUS_INFLOW_CONTROL_DEVICE,
|
|
194
|
-
Content.
|
|
195
|
-
Content.
|
|
194
|
+
Content.DUAL_RATE_CONTROLLED_PRODUCTION,
|
|
195
|
+
Content.DENSITY,
|
|
196
196
|
Content.INFLOW_CONTROL_DEVICE,
|
|
197
197
|
Content.VALVE,
|
|
198
198
|
Content.INFLOW_CONTROL_VALVE,
|
|
@@ -46,9 +46,9 @@ def visualize_device(axs: Axes, df_well: pd.DataFrame) -> Axes:
|
|
|
46
46
|
axs.plot(xpar, ypar, "rs-", markevery=[1])
|
|
47
47
|
elif df_device[Headers.DEVICE_TYPE].iloc[idx] == Content.VALVE:
|
|
48
48
|
axs.plot(xpar, ypar, "rv-", markevery=[1])
|
|
49
|
-
elif df_device[Headers.DEVICE_TYPE].iloc[idx] == Content.
|
|
49
|
+
elif df_device[Headers.DEVICE_TYPE].iloc[idx] == Content.DENSITY:
|
|
50
50
|
axs.plot(xpar, ypar, "rP-", markevery=[1])
|
|
51
|
-
elif df_device[Headers.DEVICE_TYPE].iloc[idx] == Content.
|
|
51
|
+
elif df_device[Headers.DEVICE_TYPE].iloc[idx] == Content.DUAL_RATE_CONTROLLED_PRODUCTION:
|
|
52
52
|
axs.plot(xpar, ypar, "r*-", markevery=[1])
|
|
53
53
|
return axs
|
|
54
54
|
|
|
@@ -218,10 +218,10 @@ class Lateral:
|
|
|
218
218
|
df_well = completion.get_device(df_well, case.wsegsicd_table, Content.INFLOW_CONTROL_DEVICE)
|
|
219
219
|
if Content.AUTONOMOUS_INFLOW_CONTROL_DEVICE in active_devices:
|
|
220
220
|
df_well = completion.get_device(df_well, case.wsegaicd_table, Content.AUTONOMOUS_INFLOW_CONTROL_DEVICE)
|
|
221
|
-
if Content.
|
|
222
|
-
df_well = completion.get_device(df_well, case.
|
|
223
|
-
if Content.
|
|
224
|
-
df_well = completion.get_device(df_well, case.
|
|
221
|
+
if Content.DENSITY in active_devices:
|
|
222
|
+
df_well = completion.get_device(df_well, case.wsegdensity_table, Content.DENSITY)
|
|
223
|
+
if Content.DUAL_RATE_CONTROLLED_PRODUCTION in active_devices:
|
|
224
|
+
df_well = completion.get_device(df_well, case.wsegdualrcp_table, Content.DUAL_RATE_CONTROLLED_PRODUCTION)
|
|
225
225
|
if Content.INFLOW_CONTROL_VALVE in active_devices:
|
|
226
226
|
df_well = completion.get_device(df_well, case.wsegicv_table, Content.INFLOW_CONTROL_VALVE)
|
|
227
227
|
return df_well
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "completor"
|
|
3
|
-
version = "1.1.
|
|
4
|
-
description = "Advanced
|
|
3
|
+
version = "1.1.3"
|
|
4
|
+
description = "Advanced multi-segmented well completion tool."
|
|
5
5
|
authors = ["Equinor ASA <opensource@equinor.com>"]
|
|
6
6
|
repository = "https://github.com/equinor/completor"
|
|
7
7
|
documentation = "https://equinor.github.io/completor"
|
|
@@ -19,28 +19,31 @@ classifiers = [
|
|
|
19
19
|
"Bug Tracker" = "https://github.com/equinor/completor/issues"
|
|
20
20
|
|
|
21
21
|
[tool.poetry.dependencies]
|
|
22
|
-
python = "
|
|
23
|
-
matplotlib = "^3.
|
|
24
|
-
numpy = "<
|
|
25
|
-
pandas = "^2.
|
|
26
|
-
scipy = "^1.
|
|
27
|
-
tqdm = "^4.
|
|
28
|
-
pytest-env = "^1"
|
|
22
|
+
python = "^3.11"
|
|
23
|
+
matplotlib = "^3.9"
|
|
24
|
+
numpy = ">=1.26, <3.0"
|
|
25
|
+
pandas = "^2.2"
|
|
26
|
+
scipy = "^1.14"
|
|
27
|
+
tqdm = "^4.66"
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
rstcheck-core = {version="^1.2", optional=true}
|
|
30
|
+
ert = {version="^12", optional=true}
|
|
31
|
+
pytest = {version="^8.3", optional=true}
|
|
32
|
+
pytest-xdist = {version="^3.6", optional=true}
|
|
33
|
+
pytest-env = {version="^1", optional=true}
|
|
34
34
|
|
|
35
35
|
[tool.poetry.group.dev.dependencies]
|
|
36
|
-
black = "^24.4
|
|
37
|
-
flake8 = "^7.0
|
|
38
|
-
pre-commit = "^4.0
|
|
39
|
-
pytest = "^8.3
|
|
40
|
-
pytest-xdist = "^3.6
|
|
36
|
+
black = "^24.4"
|
|
37
|
+
flake8 = "^7.0"
|
|
38
|
+
pre-commit = "^4.0"
|
|
39
|
+
pytest = "^8.3"
|
|
40
|
+
pytest-xdist = "^3.6"
|
|
41
|
+
pytest-env = "^1.1"
|
|
42
|
+
rstcheck-core = "^1.2"
|
|
41
43
|
|
|
42
44
|
[tool.poetry.extras]
|
|
43
|
-
ert = ["
|
|
45
|
+
ert = ["ert"]
|
|
46
|
+
test = ["pytest", "pytest-xdist", "pytest-env", "rstcheck-core"]
|
|
44
47
|
|
|
45
48
|
[tool.poetry.scripts]
|
|
46
49
|
completor = "completor.main:main"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|