completor 1.1.3__py3-none-any.whl → 1.2.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.
completor/completion.py CHANGED
@@ -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`, `DENSITY`, `VALVE`, `DUALRCP`, `ICV`.
554
+ device_type: Device type. `AICD`, `ICD`, `DENSITY`, `VALVE`, `DUALRCP`, `ICV`, `INJV`.
555
555
 
556
556
  Returns:
557
557
  Updated well information with device characteristics.
@@ -581,6 +581,10 @@ def get_device(df_well: pd.DataFrame, df_device: pd.DataFrame, device_type: str)
581
581
  # rescale the Cv
582
582
  # because no scaling factor in WELL_SEGMENTS_VALVE
583
583
  df_well[Headers.FLOW_COEFFICIENT] = -df_well[Headers.FLOW_COEFFICIENT] / df_well[Headers.SCALE_FACTOR]
584
+ elif device_type == Content.INJECTION_VALVE:
585
+ # rescale the Cv
586
+ # because no scaling factor in WELL_SEGMENTS_VALVE
587
+ df_well[Headers.FLOW_COEFFICIENT] = -df_well[Headers.FLOW_COEFFICIENT] / df_well[Headers.SCALE_FACTOR]
584
588
  return df_well
585
589
 
586
590
 
completor/constants.py CHANGED
@@ -78,7 +78,7 @@ class _Headers:
78
78
  DR = "DR"
79
79
  FLAG = "FLAG" # This is actually a header, but OPEN, SHUT, and AUTO are its possible values, see manual on COMPLETION_DATA.
80
80
  SHUT = "SHUT"
81
- # CROSS = "CROSS"
81
+ CROSS = "CROSS"
82
82
  PRESSURE_TABLE = "PRESSURETABLE"
83
83
  DENSITY_CALCULATION_TYPE = "DENSCAL" # Type of density calculation for the wellbore hydrostatic head.
84
84
  REGION = "REGION"
@@ -126,6 +126,7 @@ class _Headers:
126
126
  # This stops making sense from here on out?
127
127
  X = "X"
128
128
  Y = "Y"
129
+ Z = "Z"
129
130
  # FLAG
130
131
  A = "A"
131
132
  B = "B"
@@ -165,6 +166,15 @@ class _Headers:
165
166
  GAS_HOLDUP_FRACTION_LOW_CUTOFF = "GAS_HOLDUP_FRACTION_LOW_CUTOFF"
166
167
  GAS_HOLDUP_FRACTION_HIGH_CUTOFF = "GAS_HOLDUP_FRACTION_HIGH_CUTOFF"
167
168
 
169
+ # Injection Valve Well Segments (WSEGINJV)
170
+ # DEVICE_NUMBER
171
+ TRIGGER_PARAMETER = "TRIGGER_PARAMETER"
172
+ TRIGGER_VALUE = "TRIGGER_VALUE"
173
+ # FLOW_COEFFICIENT / Cv
174
+ # FLOW_CROSS_SECTIONAL_AREA
175
+ PRIMARY_FLOW_CROSS_SECTIONAL_AREA = "PRIMARY_FLOW_CROSS_SECTIONAL_AREA"
176
+ SECONDARY_FLOW_CROSS_SECTIONAL_AREA = "SECONDARY_FLOW_CROSS_SECTIONAL_AREA"
177
+
168
178
  # Miscellaneous
169
179
  DEFAULTS = "DEFAULTS"
170
180
  MEASURED_DEPTH = "MEASURED_DEPTH"
@@ -245,6 +255,7 @@ class _Keywords:
245
255
  INFLOW_CONTROL_DEVICE = "WSEGSICD"
246
256
  DENSITY = "WSEGDENSITY"
247
257
  DENSITY_ACTIVATED_RECOVERY = "WSEGDAR"
258
+ INJECTION_VALVE = "WSEGINJV"
248
259
  LATERAL_TO_DEVICE = "LATERAL_TO_DEVICE"
249
260
  JOINT_LENGTH = "JOINTLENGTH"
250
261
  SEGMENT_LENGTH = "SEGMENTLENGTH"
@@ -290,6 +301,7 @@ class _Content:
290
301
  AUTONOMOUS_INFLOW_CONTROL_DEVICE = "AICD"
291
302
  DENSITY = "DENSITY"
292
303
  DENSITY_ACTIVATED_RECOVERY = "DAR"
304
+ INJECTION_VALVE = "INJV"
293
305
  VALVE = "VALVE"
294
306
  DEVICE_TYPES = [
295
307
  AUTONOMOUS_INFLOW_CONTROL_DEVICE,
@@ -297,6 +309,7 @@ class _Content:
297
309
  AUTONOMOUS_INFLOW_CONTROL_VALVE,
298
310
  DENSITY,
299
311
  DENSITY_ACTIVATED_RECOVERY,
312
+ INJECTION_VALVE,
300
313
  INFLOW_CONTROL_DEVICE,
301
314
  VALVE,
302
315
  INFLOW_CONTROL_VALVE,
@@ -41,6 +41,7 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
41
41
  print_autonomous_inflow_control_device = ""
42
42
  print_inflow_control_device = ""
43
43
  print_density_driven = ""
44
+ print_injection_valve = ""
44
45
  print_dual_rate_controlled_production = ""
45
46
 
46
47
  start_segment = 2
@@ -111,6 +112,7 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
111
112
  well.well_name, lateral.df_well, lateral.df_device
112
113
  )
113
114
  df_density_driven = prepare_outputs.prepare_density_driven(well.well_name, lateral.df_well, lateral.df_device)
115
+ df_injection_valve = prepare_outputs.prepare_injection_valve(well.well_name, lateral.df_well, lateral.df_device)
114
116
  df_dual_rate_controlled_production = prepare_outputs.prepare_dual_rate_controlled_production(
115
117
  well.well_name, lateral.df_well, lateral.df_device
116
118
  )
@@ -146,6 +148,7 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
146
148
  well.well_name, lateral.lateral_number, df_inflow_control_valve, first
147
149
  )
148
150
  print_density_driven += _format_density_driven(well.well_number, df_density_driven)
151
+ print_injection_valve += _format_injection_valve(well.well_number, df_injection_valve)
149
152
  print_dual_rate_controlled_production += _format_dual_rate_controlled_production(
150
153
  well.well_number, df_dual_rate_controlled_production
151
154
  )
@@ -191,6 +194,16 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
191
194
  f"{'-' * 100}\n\n\n"
192
195
  )
193
196
  bonus.append(metadata + print_density_driven + "\n\n\n\n")
197
+ if print_injection_valve:
198
+ metadata = (
199
+ f"{'-' * 100}\n"
200
+ "-- This is how we model INJV technology using sets of ACTIONX keywords.\n"
201
+ "-- The DP paramaters changes according to the trigger parameter.-\n"
202
+ "-- The value of Cv is adjusted according to the segment length and the number of\n"
203
+ "-- devices per joint. The constriction area will change if the parameter is triggered.\n"
204
+ f"{'-' * 100}\n\n\n"
205
+ )
206
+ bonus.append(metadata + print_injection_valve + "\n\n\n\n")
194
207
  if print_dual_rate_controlled_production:
195
208
  metadata = (
196
209
  f"{'-' * 100}\n"
@@ -450,6 +463,21 @@ def _format_density_driven(well_number: int, df_wsegdensity: pd.DataFrame) -> st
450
463
  return prepare_outputs.print_wsegdensity(df_wsegdensity, well_number + 1)
451
464
 
452
465
 
466
+ def _format_injection_valve(well_number: int, df_wseginjv: pd.DataFrame) -> str:
467
+ """Formats well-segments for injection valve.
468
+
469
+ Args:
470
+ well_number: The well's number
471
+ df_wsegdinjv: Data to print.
472
+
473
+ Returns:
474
+ Formatted string.
475
+ """
476
+ if df_wseginjv.empty:
477
+ return ""
478
+ return prepare_outputs.print_wseginjv(df_wseginjv, well_number + 1)
479
+
480
+
453
481
  def _format_dual_rate_controlled_production(well_number: int, df_wsegdualrcp: pd.DataFrame) -> str:
454
482
  """Formats the DUALRCP section.
455
483
 
@@ -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, DENSITY, DUALRCP, and ICV."
176
+ "Valid types are PERF, AICD, ICD, VALVE, DENSITY, INJV, DUALRCP, and ICV."
177
177
  )
178
178
  if df_comp[Headers.ANNULUS].iloc[idx] not in Content.ANNULUS_TYPES:
179
179
  raise CompletorError(
@@ -290,6 +290,25 @@ def set_format_wsegdensity(df_temp: pd.DataFrame) -> pd.DataFrame:
290
290
  return df_temp
291
291
 
292
292
 
293
+ def set_format_wseginjv(df_temp: pd.DataFrame) -> pd.DataFrame:
294
+ """Format the well segments Injection Valve (INJV) data.
295
+
296
+ Args:
297
+ df_temp: Well segments INJV device data.
298
+
299
+ Returns:
300
+ Updated data.
301
+ """
302
+ df_temp[Headers.DEVICE_NUMBER] = df_temp[Headers.DEVICE_NUMBER].astype(np.int64)
303
+ # left out devicenumber and trigger parameter because devicenumber has been formatted as integer
304
+ # trigger parameter is a string
305
+ columns = df_temp.columns.to_numpy()[2:]
306
+ df_temp[columns] = df_temp[columns].astype(np.float64)
307
+ # Create ID device column
308
+ df_temp.insert(0, Headers.DEVICE_TYPE, np.full(df_temp.shape[0], Content.INJECTION_VALVE))
309
+ return df_temp
310
+
311
+
293
312
  def set_format_wsegdualrcp(df_temp: pd.DataFrame) -> pd.DataFrame:
294
313
  """Format the well segments Dual RCP (DUALRCP) table.
295
314
 
@@ -120,6 +120,7 @@ def dataframe_tostring(
120
120
  "END_MD": "{:.3f}".format,
121
121
  Headers.FLOW_COEFFICIENT: "{:.10g}".format,
122
122
  "CV": "{:.10g}".format,
123
+ Headers.CROSS: "{:.3e}".format,
123
124
  Headers.FLOW_CROSS_SECTIONAL_AREA: "{:.3e}".format,
124
125
  "FLOW_CROSS_SECTIONAL_AREA": "{:.3e}".format,
125
126
  Headers.OIL_FLOW_CROSS_SECTIONAL_AREA: "{:.3e}".format,
@@ -135,6 +136,13 @@ def dataframe_tostring(
135
136
  Headers.ALPHA_PILOT: "{:.10g}".format,
136
137
  }
137
138
 
139
+ # Cast floats to str befor headers are messed up (pandas formatter does not work reliably with MultiIndex headers).
140
+ for column, formatter in formatters.items():
141
+ try:
142
+ df_temp[column] = df_temp[column].map(formatter)
143
+ except (KeyError, ValueError):
144
+ pass
145
+
138
146
  if header:
139
147
  # Modify headers to reduce width.
140
148
  column_splits = [tuple(column.split("_")) for column in df_temp.columns]
@@ -173,9 +181,7 @@ def dataframe_tostring(
173
181
  df_temp.columns = pd.MultiIndex.from_frame(new_cols)
174
182
 
175
183
  try:
176
- output_string = df_temp.to_string(
177
- index=False, justify="justify", formatters=formatters, header=header, sparsify=False
178
- )
184
+ output_string = df_temp.to_string(index=False, justify="justify", header=header, sparsify=False)
179
185
  except ValueError:
180
186
  if df_temp.isnull().values.any():
181
187
  raise CompletorError("Got NaN values in table, please report if encountered!")
@@ -184,18 +190,14 @@ def dataframe_tostring(
184
190
  df_temp = df_temp.replace("1*", np.nan, inplace=False)
185
191
  # Probably find columns where this is the case and cast to numeric after replacing with nan?
186
192
  df_temp[columns_with_1_star] = df_temp[columns_with_1_star].astype(np.float64, errors="ignore")
187
- output_string = df_temp.to_string(
188
- index=False, justify="justify", formatters=formatters, header=header, sparsify=False, na_rep="1*"
189
- )
193
+ output_string = df_temp.to_string(index=False, justify="justify", header=header, sparsify=False, na_rep="1*")
190
194
 
191
195
  if output_string is None:
192
196
  return ""
193
197
 
194
198
  too_long_lines = check_width_lines(output_string, limit)
195
199
  if too_long_lines:
196
- output_string = df_temp.to_string(
197
- index=False, justify="left", formatters=formatters, header=header, sparsify=False
198
- )
200
+ output_string = df_temp.to_string(index=False, justify="left", header=header, sparsify=False)
199
201
  if output_string is None:
200
202
  return ""
201
203
  too_long_lines2 = check_width_lines(output_string, limit)
@@ -454,7 +456,11 @@ def prepare_device_layer(df_well: pd.DataFrame, df_tubing: pd.DataFrame, device_
454
456
  df_well[Headers.DEVICE_TYPE] == Content.DUAL_RATE_CONTROLLED_PRODUCTION,
455
457
  "/ -- DUALRCP types",
456
458
  np.where(
457
- df_well[Headers.DEVICE_TYPE] == Content.INFLOW_CONTROL_VALVE, "/ -- ICV types", ""
459
+ df_well[Headers.DEVICE_TYPE] == Content.INJECTION_VALVE,
460
+ "/ -- INJV types",
461
+ np.where(
462
+ df_well[Headers.DEVICE_TYPE] == Content.INFLOW_CONTROL_VALVE, "/ -- ICV types", ""
463
+ ),
458
464
  ),
459
465
  ),
460
466
  ),
@@ -1068,6 +1074,8 @@ def prepare_autonomous_inflow_control_device(
1068
1074
  wsegaicd[Headers.D] = df_merge[Headers.D].to_numpy()
1069
1075
  wsegaicd[Headers.E] = df_merge[Headers.E].to_numpy()
1070
1076
  wsegaicd[Headers.F] = df_merge[Headers.F].to_numpy()
1077
+ if Headers.Z in df_merge.columns:
1078
+ wsegaicd[Headers.Z] = df_merge[Headers.Z].to_numpy()
1071
1079
  wsegaicd[Headers.EMPTY] = "/"
1072
1080
  return wsegaicd
1073
1081
 
@@ -1303,6 +1311,48 @@ def prepare_density_driven(well_name: str, df_well: pd.DataFrame, df_device: pd.
1303
1311
  return wsegdensity
1304
1312
 
1305
1313
 
1314
+ def prepare_injection_valve(well_name: str, df_well: pd.DataFrame, df_device: pd.DataFrame) -> pd.DataFrame:
1315
+ """Prepare data frame for INJECTION VALVE.
1316
+
1317
+ Args:
1318
+ well_name: Well name.
1319
+ df_well: Well data.
1320
+ df_device: Device data for this well and lateral.
1321
+
1322
+ Returns:
1323
+ DataFrame for INJECTION VALVE.
1324
+ """
1325
+ df_well = df_well[(df_well[Headers.DEVICE_TYPE] == Content.PERFORATED) | (df_well[Headers.NUMBER_OF_DEVICES] > 0)]
1326
+ if df_well.shape[0] == 0:
1327
+ return pd.DataFrame()
1328
+ df_merge = pd.merge_asof(
1329
+ left=df_device,
1330
+ right=df_well,
1331
+ left_on=[Headers.MEASURED_DEPTH],
1332
+ right_on=[Headers.TUBING_MEASURED_DEPTH],
1333
+ direction="nearest",
1334
+ )
1335
+ df_merge = df_merge[df_merge[Headers.DEVICE_TYPE] == Content.INJECTION_VALVE]
1336
+ wseginjv = pd.DataFrame()
1337
+ if df_merge.shape[0] > 0:
1338
+ wseginjv[Headers.WELL] = [well_name] * df_merge.shape[0]
1339
+ wseginjv[Headers.START_SEGMENT_NUMBER] = df_merge[Headers.START_SEGMENT_NUMBER].to_numpy()
1340
+ # the Cv is already corrected by the scaling factor
1341
+ wseginjv[Headers.FLOW_COEFFICIENT] = df_merge[Headers.FLOW_COEFFICIENT].to_numpy()
1342
+ wseginjv[Headers.PRIMARY_FLOW_CROSS_SECTIONAL_AREA] = df_merge[
1343
+ Headers.PRIMARY_FLOW_CROSS_SECTIONAL_AREA
1344
+ ].to_numpy()
1345
+ wseginjv[Headers.SECONDARY_FLOW_CROSS_SECTIONAL_AREA] = df_merge[
1346
+ Headers.SECONDARY_FLOW_CROSS_SECTIONAL_AREA
1347
+ ].to_numpy()
1348
+ wseginjv[Headers.TRIGGER_PARAMETER] = df_merge[Headers.TRIGGER_PARAMETER].to_numpy()
1349
+ wseginjv[Headers.TRIGGER_VALUE] = df_merge[Headers.TRIGGER_VALUE].to_numpy()
1350
+ wseginjv[Headers.DEFAULTS] = "5*"
1351
+ wseginjv[Headers.MAX_FLOW_CROSS_SECTIONAL_AREA] = wseginjv[Headers.PRIMARY_FLOW_CROSS_SECTIONAL_AREA].to_numpy()
1352
+ wseginjv[Headers.EMPTY] = "/"
1353
+ return wseginjv
1354
+
1355
+
1306
1356
  def prepare_dual_rate_controlled_production(
1307
1357
  well_name: str, df_well: pd.DataFrame, df_device: pd.DataFrame
1308
1358
  ) -> pd.DataFrame:
@@ -1513,6 +1563,168 @@ def print_wsegdensity(df_wsegdensity: pd.DataFrame, well_number: int) -> str:
1513
1563
  return action
1514
1564
 
1515
1565
 
1566
+ def print_wseginjv(df_wseginjv: pd.DataFrame, well_number: int) -> str:
1567
+ """Print INJECTION VALVE devices.
1568
+
1569
+ Args:
1570
+ df_wseginjv: Output from function prepare_wseginjv.
1571
+ well_number: Well number.
1572
+
1573
+ Returns:
1574
+ Formatted actions to be included in the output file.
1575
+
1576
+ Raises:
1577
+ CompletorError: If there are to many wells and/or segments with INJECTION VALVE.
1578
+ """
1579
+ header = [
1580
+ [
1581
+ Headers.WELL,
1582
+ Headers.START_SEGMENT_NUMBER,
1583
+ Headers.FLOW_COEFFICIENT,
1584
+ Headers.SECONDARY_FLOW_CROSS_SECTIONAL_AREA,
1585
+ Headers.DEFAULTS,
1586
+ Headers.MAX_FLOW_CROSS_SECTIONAL_AREA,
1587
+ ],
1588
+ [
1589
+ Headers.WELL,
1590
+ Headers.START_SEGMENT_NUMBER,
1591
+ Headers.FLOW_COEFFICIENT,
1592
+ Headers.PRIMARY_FLOW_CROSS_SECTIONAL_AREA,
1593
+ Headers.DEFAULTS,
1594
+ Headers.MAX_FLOW_CROSS_SECTIONAL_AREA,
1595
+ ],
1596
+ ]
1597
+
1598
+ sign = ["<", ">="]
1599
+ suvtrig = ["0", "1"]
1600
+ action = "UDQ\n"
1601
+ for idx in range(df_wseginjv.shape[0]):
1602
+ segment_number = df_wseginjv[Headers.START_SEGMENT_NUMBER].iloc[idx]
1603
+ well_name = df_wseginjv[Headers.WELL].iloc[idx]
1604
+ action += f" ASSIGN SUVTRIG {well_name} {segment_number} 0 /\n"
1605
+ action += "/\n\n"
1606
+ iaction = 1
1607
+ action += Keywords.WELL_SEGMENTS_VALVE + "\n"
1608
+ header_string = "--"
1609
+ for itm in header[iaction]:
1610
+ header_string += " " + itm
1611
+ action += header_string.rstrip() + "\n"
1612
+ for idx in range(df_wseginjv.shape[0]):
1613
+ segment_number = df_wseginjv[Headers.START_SEGMENT_NUMBER].iloc[idx]
1614
+ print_df = df_wseginjv[df_wseginjv[Headers.START_SEGMENT_NUMBER] == segment_number]
1615
+ print_df = print_df[header[iaction]]
1616
+ print_df = dataframe_tostring(print_df, True, False, False) + "\n"
1617
+ action += print_df
1618
+ action += "/\n\n"
1619
+ for idx in range(df_wseginjv.shape[0]):
1620
+ segment_number = df_wseginjv[Headers.START_SEGMENT_NUMBER].iloc[idx]
1621
+ well_name = df_wseginjv[Headers.WELL].iloc[idx]
1622
+ # Trigger paramater is segment water rate
1623
+ if df_wseginjv[Headers.TRIGGER_PARAMETER].iloc[idx] == "SWFR":
1624
+ water_segment_rate_cutoff = -1 * df_wseginjv[Headers.TRIGGER_VALUE].iloc[idx]
1625
+ iaction = 0
1626
+ act_number = iaction + 1
1627
+ act_name = f"INJVOP{well_number:03d}{segment_number:03d}{act_number:1d}"
1628
+ if len(act_name) > 13:
1629
+ raise CompletorError("Too many wells and/or too many segments with Injection Valve")
1630
+ action += (
1631
+ f"ACTIONX\n{act_name} 1000000 /\n"
1632
+ f"SWFR '{well_name}' {segment_number} "
1633
+ f"{sign[iaction]} {water_segment_rate_cutoff} AND /\n"
1634
+ f"SUVTRIG '{well_name}' {segment_number} "
1635
+ f"= {suvtrig[iaction]} /\n/\n\n"
1636
+ )
1637
+ print_df = df_wseginjv[df_wseginjv[Headers.START_SEGMENT_NUMBER] == segment_number]
1638
+ print_df = print_df[header[iaction]] # type: ignore
1639
+ header_string = Keywords.WELL_SEGMENTS_VALVE + "\n--"
1640
+
1641
+ for item in header[iaction]:
1642
+ header_string += " " + item
1643
+ header_string = header_string.rstrip() + "\n"
1644
+ print_df = header_string + dataframe_tostring(print_df, True, False, False) # type: ignore
1645
+ print_df += "\n/\n"
1646
+ print_df += f"\nUDQ\n ASSIGN SUVTRIG {well_name} {segment_number} 1 /\n/\n"
1647
+ action += print_df + "\nENDACTIO\n\n"
1648
+
1649
+ iaction = 1
1650
+ act_number = iaction + 1
1651
+ act_name = f"INJVCL{well_number:03d}{segment_number:03d}{act_number:1d}"
1652
+ if len(act_name) > 13:
1653
+ raise CompletorError("Too many wells and/or too many segments with Injection Valve")
1654
+ action += (
1655
+ f"ACTIONX\n{act_name} 1000000 /\n"
1656
+ f"SWFR '{well_name}' {segment_number} "
1657
+ f"{sign[iaction]} {water_segment_rate_cutoff} AND /\n"
1658
+ f"SUVTRIG '{well_name}' {segment_number} "
1659
+ f"= {suvtrig[iaction]} /\n/\n\n"
1660
+ )
1661
+ print_df = df_wseginjv[df_wseginjv[Headers.START_SEGMENT_NUMBER] == segment_number]
1662
+ print_df = print_df[header[iaction]] # type: ignore
1663
+ header_string = Keywords.WELL_SEGMENTS_VALVE + "\n--"
1664
+
1665
+ for item in header[iaction]:
1666
+ header_string += " " + item
1667
+ header_string = header_string.rstrip() + "\n"
1668
+ print_df = header_string + dataframe_tostring(print_df, True, False, False) # type: ignore
1669
+ print_df += "\n/\n"
1670
+ print_df += f"\nUDQ\n ASSIGN SUVTRIG {well_name} {segment_number} 0 /\n/\n"
1671
+ action += print_df + "\nENDACTIO\n\n"
1672
+
1673
+ # Trigger parameter is segment pressure drop
1674
+ elif df_wseginjv[Headers.TRIGGER_PARAMETER].iloc[idx] == "SPRD":
1675
+ pressure_drop_cutoff = -1 * df_wseginjv[Headers.TRIGGER_VALUE].iloc[idx]
1676
+ iaction = 0
1677
+ act_number = iaction + 1
1678
+ act_name = f"INJVOP{well_number:03d}{segment_number:03d}{act_number:1d}"
1679
+ if len(act_name) > 13:
1680
+ raise CompletorError("Too many wells and/or too many segments with Injection Valve")
1681
+ action += (
1682
+ f"ACTIONX\n{act_name} 1000000 /\n"
1683
+ f"SPRD '{well_name}' {segment_number} "
1684
+ f"{sign[iaction]} {pressure_drop_cutoff} AND /\n"
1685
+ f"SUVTRIG '{well_name}' {segment_number} "
1686
+ f"= {suvtrig[iaction]} /\n/\n\n"
1687
+ )
1688
+ print_df = df_wseginjv[df_wseginjv[Headers.START_SEGMENT_NUMBER] == segment_number]
1689
+ print_df = print_df[header[iaction]] # type: ignore
1690
+ header_string = Keywords.WELL_SEGMENTS_VALVE + "\n--"
1691
+
1692
+ for item in header[iaction]:
1693
+ header_string += " " + item
1694
+ header_string = header_string.rstrip() + "\n"
1695
+ print_df = header_string + dataframe_tostring(print_df, True, False, False) # type: ignore
1696
+ print_df += "\n/\n"
1697
+ print_df += f"\nUDQ\n ASSIGN SUVTRIG {well_name} {segment_number} 1 /\n/\n"
1698
+ action += print_df + "\nENDACTIO\n\n"
1699
+
1700
+ iaction = 1
1701
+ act_number = iaction + 1
1702
+ act_name = f"INJVCL{well_number:03d}{segment_number:03d}{act_number:1d}"
1703
+ if len(act_name) > 13:
1704
+ raise CompletorError("Too many wells and/or too many segments with Injection Valve")
1705
+ action += (
1706
+ f"ACTIONX\n{act_name} 1000000 /\n"
1707
+ f"SPRD '{well_name}' {segment_number} "
1708
+ f"{sign[iaction]} {pressure_drop_cutoff} AND /\n"
1709
+ f"SUVTRIG '{well_name}' {segment_number} "
1710
+ f"= {suvtrig[iaction]} /\n/\n\n"
1711
+ )
1712
+ print_df = df_wseginjv[df_wseginjv[Headers.START_SEGMENT_NUMBER] == segment_number]
1713
+ print_df = print_df[header[iaction]] # type: ignore
1714
+ header_string = Keywords.WELL_SEGMENTS_VALVE + "\n--"
1715
+
1716
+ for item in header[iaction]:
1717
+ header_string += " " + item
1718
+ header_string = header_string.rstrip() + "\n"
1719
+ print_df = header_string + dataframe_tostring(print_df, True, False, False) # type: ignore
1720
+ print_df += "\n/\n"
1721
+ print_df += f"\nUDQ\n ASSIGN SUVTRIG {well_name} {segment_number} 0 /\n/\n"
1722
+ action += print_df + "\nENDACTIO\n\n"
1723
+ else:
1724
+ raise CompletorError("Trigger paramater given is not supported")
1725
+ return action
1726
+
1727
+
1516
1728
  def print_wsegdualrcp(df_wsegdualrcp: pd.DataFrame, well_number: int) -> str:
1517
1729
  """Print for DUALRCP devices.
1518
1730
 
@@ -48,7 +48,7 @@ class ReadCasefile:
48
48
  This class reads the case/input file of the Completor program.
49
49
  It reads the following keywords:
50
50
  COMPLETION, SEGMENTLENGTH, JOINTLENGTH, AUTONOMOUS_INFLOW_CONTROL_DEVICE, WELL_SEGMENTS_VALVE,
51
- INFLOW_CONTROL_DEVICE, DENSITY_DRIVEN, DUAL_RATE_CONTROLLED_PRODUCTION, INFLOW_CONTROL_VALVE.
51
+ INFLOW_CONTROL_DEVICE, DENSITY_DRIVEN, INJECTION_VALVE, 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:
@@ -64,6 +64,7 @@ class ReadCasefile:
64
64
  wsegvalv_table (pd.DataFrame): WELL_SEGMENTS_VALVE.
65
65
  wsegicv_table (pd.DataFrame): INFLOW_CONTROL_VALVE.
66
66
  wsegdensity_table (pd.DataFrame): DENSITY_DRIVEN.
67
+ wseginjv_table (pd.DataFrame): INJECTION_VALVE.
67
68
  wsegdualrcp_table (pd.DataFrame): DUAL_RATE_CONTROLLED_PRODUCTION.
68
69
  strict (bool): USE_STRICT. If TRUE it will exit if any lateral is not defined in the case-file. Default to TRUE.
69
70
  lat2device (pd.DataFrame): LATERAL_TO_DEVICE.
@@ -99,6 +100,7 @@ class ReadCasefile:
99
100
  self.wsegsicd_table = pd.DataFrame()
100
101
  self.wsegvalv_table = pd.DataFrame()
101
102
  self.wsegdensity_table = pd.DataFrame()
103
+ self.wseginjv_table = pd.DataFrame()
102
104
  self.wsegdualrcp_table = pd.DataFrame()
103
105
  self.wsegicv_table = pd.DataFrame()
104
106
  self.lat2device = pd.DataFrame()
@@ -117,6 +119,7 @@ class ReadCasefile:
117
119
  self.read_wsegvalv()
118
120
  self.read_wsegsicd()
119
121
  self.read_wsegdensity()
122
+ self.read_wseginjv()
120
123
  self.read_wsegdualrcp()
121
124
  self.read_wsegicv()
122
125
  self.read_lat2device()
@@ -449,10 +452,14 @@ class ReadCasefile:
449
452
  Headers.F,
450
453
  Headers.AICD_CALIBRATION_FLUID_DENSITY,
451
454
  Headers.AICD_FLUID_VISCOSITY,
455
+ Headers.Z,
452
456
  ]
453
- self.wsegaicd_table = input_validation.set_format_wsegaicd(
454
- self._create_dataframe_with_columns(header, start_index, end_index)
455
- )
457
+ try:
458
+ df_temp = self._create_dataframe_with_columns(header, start_index, end_index)
459
+ except CaseReaderFormatError:
460
+ header.remove(Headers.Z)
461
+ df_temp = self._create_dataframe_with_columns(header, start_index, end_index)
462
+ self.wsegaicd_table = input_validation.set_format_wsegaicd(df_temp)
456
463
  device_checks = self.completion_table[
457
464
  self.completion_table[Headers.DEVICE_TYPE] == Content.AUTONOMOUS_INFLOW_CONTROL_DEVICE
458
465
  ][Headers.DEVICE_NUMBER].to_numpy()
@@ -514,6 +521,40 @@ class ReadCasefile:
514
521
  if not check_contents(device_checks, self.wsegdensity_table[Headers.DEVICE_NUMBER].to_numpy()):
515
522
  raise CompletorError(f"Not all device in COMPLETION is specified in {key}")
516
523
 
524
+ def read_wseginjv(self) -> None:
525
+ """Read the INJECTION_VALVE keyword in the case file.
526
+
527
+ Raises:
528
+ CompletorError: If INJECTION_VALVE is not defined and INJV is used in COMPLETION,
529
+ or if the device number is not found.
530
+ If not all devices in COMPLETION are specified in INJECTION_VALVE.
531
+ """
532
+ start_index, end_index = parse.locate_keyword(self.content, Keywords.INJECTION_VALVE)
533
+ if start_index == end_index:
534
+ if Content.INJECTION_VALVE in self.completion_table[Headers.DEVICE_TYPE]:
535
+ raise CompletorError(
536
+ f"{Keywords.INJECTION_VALVE} keyword must be defined, if INJV is used in the completion."
537
+ )
538
+ else:
539
+ # Table headers
540
+ header = [
541
+ Headers.DEVICE_NUMBER,
542
+ Headers.TRIGGER_PARAMETER,
543
+ Headers.TRIGGER_VALUE,
544
+ Headers.FLOW_COEFFICIENT,
545
+ Headers.PRIMARY_FLOW_CROSS_SECTIONAL_AREA,
546
+ Headers.SECONDARY_FLOW_CROSS_SECTIONAL_AREA,
547
+ ]
548
+ self.wseginjv_table = input_validation.set_format_wseginjv(
549
+ self._create_dataframe_with_columns(header, start_index, end_index)
550
+ )
551
+ # Check if the device in COMPLETION is exist in INJECTION_VALVE
552
+ device_checks = self.completion_table[
553
+ self.completion_table[Headers.DEVICE_TYPE] == Content.INJECTION_VALVE
554
+ ][Headers.DEVICE_NUMBER].to_numpy()
555
+ if not check_contents(device_checks, self.wseginjv_table[Headers.DEVICE_NUMBER].to_numpy()):
556
+ raise CompletorError(f"Not all device in COMPLETION is specified in {Keywords.INJECTION_VALVE}")
557
+
517
558
  def read_wsegdualrcp(self) -> None:
518
559
  """Read the DUALRCP keyword in the case file.
519
560
 
completor/utils.py CHANGED
@@ -193,6 +193,7 @@ def get_active_wells(completion_table: pd.DataFrame, gp_perf_devicelayer: bool)
193
193
  Content.AUTONOMOUS_INFLOW_CONTROL_DEVICE,
194
194
  Content.DUAL_RATE_CONTROLLED_PRODUCTION,
195
195
  Content.DENSITY,
196
+ Content.INJECTION_VALVE,
196
197
  Content.INFLOW_CONTROL_DEVICE,
197
198
  Content.VALVE,
198
199
  Content.INFLOW_CONTROL_VALVE,
@@ -48,6 +48,8 @@ def visualize_device(axs: Axes, df_well: pd.DataFrame) -> Axes:
48
48
  axs.plot(xpar, ypar, "rv-", markevery=[1])
49
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.INJECTION_VALVE:
52
+ axs.plot(xpar, ypar, "rP-", markevery=[1])
51
53
  elif df_device[Headers.DEVICE_TYPE].iloc[idx] == Content.DUAL_RATE_CONTROLLED_PRODUCTION:
52
54
  axs.plot(xpar, ypar, "r*-", markevery=[1])
53
55
  return axs
completor/wells.py CHANGED
@@ -220,6 +220,8 @@ class Lateral:
220
220
  df_well = completion.get_device(df_well, case.wsegaicd_table, Content.AUTONOMOUS_INFLOW_CONTROL_DEVICE)
221
221
  if Content.DENSITY in active_devices:
222
222
  df_well = completion.get_device(df_well, case.wsegdensity_table, Content.DENSITY)
223
+ if Content.INJECTION_VALVE in active_devices:
224
+ df_well = completion.get_device(df_well, case.wseginjv_table, Content.INJECTION_VALVE)
223
225
  if Content.DUAL_RATE_CONTROLLED_PRODUCTION in active_devices:
224
226
  df_well = completion.get_device(df_well, case.wsegdualrcp_table, Content.DUAL_RATE_CONTROLLED_PRODUCTION)
225
227
  if Content.INFLOW_CONTROL_VALVE in active_devices:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: completor
3
- Version: 1.1.3
3
+ Version: 1.2.0
4
4
  Summary: Advanced multi-segmented well completion tool.
5
5
  Home-page: https://github.com/equinor/completor
6
6
  License: LGPL-3.0-only
@@ -1,27 +1,27 @@
1
1
  completor/__init__.py,sha256=k6amf7jhp7KkBIlaw93-NZITxyZjPSzA5McFAZh8yv8,76
2
- completor/completion.py,sha256=--c2tN1ItE8r7YjxxOrTeZ71tz0KCVStxzWkjU-ERy4,30925
2
+ completor/completion.py,sha256=quhQOdQREv8MsHW5amT0sG-84Y12KVdT0eylbKCul3g,31177
3
3
  completor/config_jobs/run_completor,sha256=XePKj2xocfGF0XFRqr7sqfpZGrjgWcfaZLHIhVvGFCQ,600
4
- completor/constants.py,sha256=N1u1SrsZap8hSvd4Cs9LCsNZBBIJy_3P2wd9djdTrgg,13028
5
- completor/create_output.py,sha256=T9W_uNdPzPkW1IyTAXWV38hF8ZvZs5ZrmrxvNHuS-mM,22909
4
+ completor/constants.py,sha256=eZPe6cbP8see9f_sJdUkdy6J6HM2eXulgqRCzTCBAqY,13489
5
+ completor/create_output.py,sha256=T0hPnCOns86q2lvRMNuOz8Nz-8zlliYBtxNiiEPMRWs,24101
6
6
  completor/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  completor/exceptions/clean_exceptions.py,sha256=uQ2jgGfvUDAoS56JBWh-8ZCPIMB4hvYxLF-snA-_-pg,373
8
8
  completor/exceptions/exceptions.py,sha256=eS00mVzmC4gQ7MUyGSknvKotmo2XzNGyp007mhjLMy0,5081
9
9
  completor/get_version.py,sha256=LMzybu7m-O203lUSZvyu86J4bMobbjE9A23fWLDlxaM,168
10
10
  completor/hook_implementations/jobs.py,sha256=8Ao8UadfJzEY7jWzj6C_OXm18fHmfXrbCB2_jS7KHiU,2180
11
- completor/input_validation.py,sha256=jNqYeAZFxrGJy5-xwK-n1Y0t9EKvJ12aA_z6yzN3N5E,14315
11
+ completor/input_validation.py,sha256=F17AWi-qR6cX5_1lEFgtYOMOO0Xo3gTtL2ETM5P_uGE,15022
12
12
  completor/launch_args_parser.py,sha256=gb3FcyufZlRnKS3BZkFmgVH1VoSxMD0MbCLsHZKmz4c,1413
13
13
  completor/logger.py,sha256=gYDbPL8ca5qT_MqYlDKEMKcSfIXW_59QklX8Gss5b2U,4784
14
14
  completor/main.py,sha256=ipARMDXXqPYRtBxYQnlj5MohepwkgrQkX3s2IJEaEcI,9151
15
15
  completor/parse.py,sha256=EGlt9CgkrXPqa7woyWQ5t_nh6OWsFrc2SJr8aFr_KsQ,20133
16
- completor/prepare_outputs.py,sha256=GAQsRFrz7SwilExCq0w6f1tZE-4TmpHIeMS6_CwdbCM,72547
17
- completor/read_casefile.py,sha256=ToE7wGh0bKlS8hNFlRN21sPAufPuhRAvadkZGwB2ky4,35452
16
+ completor/prepare_outputs.py,sha256=YS6p_kfTw_zXi0jKPwxrx45_54wrUaD0ryuZ2qe_S5Q,82582
17
+ completor/read_casefile.py,sha256=qs39CkpwyNXV4cm0tpMi9NOyehBQKOBWmsCjSrWEauM,37578
18
18
  completor/read_schedule.py,sha256=IYyCubOggFGg664h1flTl7MUJhJWyibr6JsptnURjUA,18101
19
- completor/utils.py,sha256=ieqgLXfjbjDXHHSW283bliznj04Qep5KRUYGi_BXQRI,13557
19
+ completor/utils.py,sha256=9Qayt7xMsimpoVDo5sL86gVaQBbIOezMBRyMXd3haXw,13598
20
20
  completor/visualization.py,sha256=ObxThqIyW3fsvVIupxVsgySFI_54n_Di-HTzFtDMSgo,4580
21
- completor/visualize_well.py,sha256=fkrjlEtSLsRrmSHoPYtCKIrCeEvXg4LsYyEEPryAB0I,8653
22
- completor/wells.py,sha256=GgZyfRa5wpSMIJUl5CPi5F1zdmU0XK6QjSduTzuh2NI,12093
23
- completor-1.1.3.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
24
- completor-1.1.3.dist-info/METADATA,sha256=TAE2gJSk-z2H0sRan100EzToaDlrisCAdlo_jVOVFzA,7979
25
- completor-1.1.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
- completor-1.1.3.dist-info/entry_points.txt,sha256=co5L2_CC2QQWVdEALeMp-NIC4mx4nRpcLcvpVXMYdeI,106
27
- completor-1.1.3.dist-info/RECORD,,
21
+ completor/visualize_well.py,sha256=HLglSN2ce-u9nklJelqwhSxwwaj7n-aU6o8lUScbiI0,8790
22
+ completor/wells.py,sha256=Q2detK0FxIBgusYkCRmomRIfcScBNvNTYX43GFyAHGw,12246
23
+ completor-1.2.0.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
24
+ completor-1.2.0.dist-info/METADATA,sha256=4C5L62K_4i2YRU-SuQxVlcStnd03iTd0bZzVscKJWTg,7979
25
+ completor-1.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
+ completor-1.2.0.dist-info/entry_points.txt,sha256=co5L2_CC2QQWVdEALeMp-NIC4mx4nRpcLcvpVXMYdeI,106
27
+ completor-1.2.0.dist-info/RECORD,,