completor 1.2.0__py3-none-any.whl → 1.4.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/constants.py CHANGED
@@ -254,6 +254,7 @@ class _Keywords:
254
254
  INFLOW_CONTROL_VALVE = "WSEGICV"
255
255
  INFLOW_CONTROL_DEVICE = "WSEGSICD"
256
256
  DENSITY = "WSEGDENSITY"
257
+ PYTHON_DEPENDENT = "PYTHON"
257
258
  DENSITY_ACTIVATED_RECOVERY = "WSEGDAR"
258
259
  INJECTION_VALVE = "WSEGINJV"
259
260
  LATERAL_TO_DEVICE = "LATERAL_TO_DEVICE"
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import getpass
6
6
  from datetime import datetime
7
7
 
8
+ import matplotlib.pyplot as plt # type: ignore
8
9
  import numpy as np
9
10
  import numpy.typing as npt
10
11
  import pandas as pd
@@ -20,13 +21,13 @@ from completor.visualize_well import visualize_well
20
21
  from completor.wells import Lateral, Well
21
22
 
22
23
 
23
- def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None) -> tuple[str, str, str, str]:
24
+ def format_output(well: Well, case: ReadCasefile, pdf: PdfPages | None = None) -> tuple[str, str, str, str]:
24
25
  """Formats the finished output string to be written to a file.
25
26
 
26
27
  Args:
27
28
  well: Well data.
28
29
  case: Case data.
29
- figure_name: The name of the figure, if None, no figure is printed. Defaults to None.
30
+ pdf: The name of the figure, if None, no figure is printed. Defaults to None.
30
31
 
31
32
  Returns:
32
33
  Properly formatted output data for completion data, well segments, completion segments, and bonus.
@@ -41,8 +42,10 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
41
42
  print_autonomous_inflow_control_device = ""
42
43
  print_inflow_control_device = ""
43
44
  print_density_driven = ""
45
+ print_density_driven_include = ""
44
46
  print_injection_valve = ""
45
47
  print_dual_rate_controlled_production = ""
48
+ print_density_driven_pyaction = ""
46
49
 
47
50
  start_segment = 2
48
51
  start_branch = 1
@@ -147,23 +150,35 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
147
150
  print_inflow_control_valve += _format_inflow_control_valve(
148
151
  well.well_name, lateral.lateral_number, df_inflow_control_valve, first
149
152
  )
150
- print_density_driven += _format_density_driven(well.well_number, df_density_driven)
151
153
  print_injection_valve += _format_injection_valve(well.well_number, df_injection_valve)
152
154
  print_dual_rate_controlled_production += _format_dual_rate_controlled_production(
153
155
  well.well_number, df_dual_rate_controlled_production
154
156
  )
157
+ # output using ACTIONX (if-else) logic is dual RCP, density driven, and injection valve
158
+ if case.python_dependent:
159
+ # print the python file out
160
+ # append all laterals for density driven, dual RCP, and injection valve
161
+ # TODO(#274): Add functionality for dual RCP
162
+ print_density_driven_pyaction = _format_density_driven_pyaction(df_density_driven)
163
+ output_directory = prepare_outputs.print_python_file(
164
+ print_density_driven_pyaction, str(case.output_file), well.well_name, lateral.lateral_number
165
+ )
166
+ print_density_driven_include += prepare_outputs.print_wsegdensity_include(
167
+ output_directory, well.well_name, lateral.lateral_number
168
+ )
169
+ else:
170
+ print_density_driven += _format_density_driven(well.well_number, df_density_driven)
155
171
 
156
- if figure_name is not None:
157
- logger.info(f"Creating figure for lateral {lateral.lateral_number}.")
158
- with PdfPages(figure_name) as figure:
159
- figure.savefig(
160
- visualize_well(
161
- well.well_name, well.df_well_all_laterals, well.df_reservoir_all_laterals, case.segment_length
162
- ),
163
- orientation="landscape",
164
- )
165
- logger.info("Creating schematics: %s.pdf", figure_name)
172
+ if pdf is not None:
173
+ logger.info(f"Creating figure for well {well.well_name}, lateral {lateral.lateral_number}.")
174
+ fig = visualize_well(
175
+ well.well_name, well.df_well_all_laterals, well.df_reservoir_all_laterals, case.segment_length
176
+ )
177
+ pdf.savefig(fig, orientation="landscape")
178
+ plt.close(fig)
179
+ logger.info("Creating schematics: %s", pdf)
166
180
  first = False
181
+
167
182
  print_completion_data = "\n".join(completion_data_list)
168
183
  if print_well_segments:
169
184
  print_well_segments = f"{print_well_segments}\n/\n\n"
@@ -185,7 +200,7 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
185
200
  if print_density_driven:
186
201
  metadata = (
187
202
  f"{'-' * 100}\n"
188
- "-- This is how we model DENSITY technology using sets of ACTIONX keywords.\n"
203
+ "-- This is how we model density driven technology using sets of ACTIONX keywords.\n"
189
204
  "-- The segment dP curves changes according to the segment water-\n"
190
205
  "-- and gas volume fractions at downhole condition.\n"
191
206
  "-- The value of Cv is adjusted according to the segment length and the number of\n"
@@ -197,7 +212,7 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
197
212
  if print_injection_valve:
198
213
  metadata = (
199
214
  f"{'-' * 100}\n"
200
- "-- This is how we model INJV technology using sets of ACTIONX keywords.\n"
215
+ "-- This is how we model autonomous injection valve technology using sets of ACTIONX keywords.\n"
201
216
  "-- The DP paramaters changes according to the trigger parameter.-\n"
202
217
  "-- The value of Cv is adjusted according to the segment length and the number of\n"
203
218
  "-- devices per joint. The constriction area will change if the parameter is triggered.\n"
@@ -207,12 +222,24 @@ def format_output(well: Well, case: ReadCasefile, figure_name: str | None = None
207
222
  if print_dual_rate_controlled_production:
208
223
  metadata = (
209
224
  f"{'-' * 100}\n"
210
- "-- This is how we model DUALRCP technology using sets of ACTIONX keyword\n"
225
+ "-- This is how we model dual RCP curves using sets of ACTIONX keyword\n"
211
226
  "-- the DP parameters change according to the segment water cut (at downhole condition )\n"
212
227
  "-- and gas volume fraction (at downhole condition)\n"
213
228
  f"{'-' * 100}\n\n\n"
214
229
  )
215
230
  bonus.append(metadata + print_dual_rate_controlled_production + "\n\n\n\n")
231
+ if print_density_driven_pyaction:
232
+ metadata = (
233
+ f"{'-' * 100}\n"
234
+ "-- This is how we model density driven technology for python dependent keyword.\n"
235
+ "-- The segment dP curves changes according to the segment water-\n"
236
+ "-- and gas volume fractions at downhole condition.\n"
237
+ "-- The value of Cv is adjusted according to the segment length and the number of\n"
238
+ "-- devices per joint. The constriction area varies according to values of\n"
239
+ "-- volume fractions.\n"
240
+ f"{'-' * 100}\n\n\n"
241
+ )
242
+ bonus.append(metadata + print_density_driven_include + "\n\n\n\n")
216
243
 
217
244
  return print_completion_data, print_well_segments, print_completion_segments, "".join(bonus)
218
245
 
@@ -463,6 +490,20 @@ def _format_density_driven(well_number: int, df_wsegdensity: pd.DataFrame) -> st
463
490
  return prepare_outputs.print_wsegdensity(df_wsegdensity, well_number + 1)
464
491
 
465
492
 
493
+ def _format_density_driven_pyaction(df_wsegdensity: pd.DataFrame) -> str:
494
+ """Formats well-segments for density driven valve.
495
+
496
+ Args:
497
+ df_wsegdensity: Data to print.
498
+
499
+ Returns:
500
+ Formatted string.
501
+ """
502
+ if df_wsegdensity.empty:
503
+ return ""
504
+ return prepare_outputs.print_wsegdensity_pyaction(df_wsegdensity)
505
+
506
+
466
507
  def _format_injection_valve(well_number: int, df_wseginjv: pd.DataFrame) -> str:
467
508
  """Formats well-segments for injection valve.
468
509
 
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from .run_completor import RunCompletor
4
+
5
+ __all__ = ["RunCompletor"]
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ import ert # type: ignore
4
+
5
+ from completor.hook_implementations import RunCompletor
6
+ from completor.logger import logger
7
+
8
+ PLUGIN_NAME = "completor"
9
+
10
+ try:
11
+ from ert.plugins.plugin_manager import ErtPluginManager # type: ignore # noqa: F401
12
+ except ModuleNotFoundError:
13
+
14
+ def ert_plugin(name: str = ""):
15
+ """Dummy decorator"""
16
+
17
+ def decorator(func):
18
+ return func
19
+
20
+ return decorator
21
+
22
+ logger.warning("Cannot import ERT, did you install Completor with ert option enabled?")
23
+
24
+
25
+ @ert.plugin(name=PLUGIN_NAME)
26
+ def installable_workflow_jobs() -> dict[str, str]:
27
+ return {}
28
+
29
+
30
+ @ert.plugin(name=PLUGIN_NAME)
31
+ def installable_forward_model_steps() -> list[ert.ForwardModelStepPlugin]:
32
+ return [RunCompletor]
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ from ert import ForwardModelStepDocumentation, ForwardModelStepJSON, ForwardModelStepPlugin # type: ignore
4
+
5
+ desc = """Completor is a script for modelling
6
+ wells with advanced completion.
7
+ It generates a well schedule to be included in reservoir simulator,
8
+ by combining the multi-segment tubing definition (from pre-processor reservoir modelling tools)
9
+ with a user defined file specifying the completion design.
10
+ The resulting well schedule comprises all keywords and parameters required by
11
+ reservoir simulator. See the Completor documentation for details.
12
+
13
+ Required:
14
+ ---------
15
+ -i : followed by name of file specifying completion design (e.g. completion.case).
16
+ -s : followed by name of schedule file with multi-segment tubing definition,
17
+ including COMPDAT, COMPSEGS and WELSEGS (required if not specified in case file).
18
+
19
+ Optional:
20
+ ---------
21
+ --help : how to run completor.
22
+ --about : about completor.
23
+ -o : followed by name of completor output file.
24
+ --figure : generates a pdf file with a schematics of the well segment structure.
25
+
26
+ """
27
+
28
+
29
+ class RunCompletor(ForwardModelStepPlugin):
30
+ def __init__(self) -> None:
31
+ super().__init__(
32
+ name="run_completor",
33
+ command=[
34
+ "completor",
35
+ "-i",
36
+ "<CASE>",
37
+ "-s",
38
+ "<INPUT_SCH>",
39
+ "-o",
40
+ "<OUTPUT_SCH>",
41
+ ],
42
+ )
43
+
44
+ def validate_pre_realization_run(self, fm_step_json: ForwardModelStepJSON) -> ForwardModelStepJSON:
45
+ return fm_step_json
46
+
47
+ def validate_pre_experiment(self, fm_step_json: ForwardModelStepJSON) -> None:
48
+ pass
49
+
50
+ @staticmethod
51
+ def documentation() -> ForwardModelStepDocumentation | None:
52
+ return ForwardModelStepDocumentation(
53
+ category="modelling.reservoir",
54
+ source_package="completor",
55
+ source_function_name="RunCompletor",
56
+ description=desc,
57
+ examples="""
58
+ .. code-block:: console
59
+ FORWARD_MODEL run_completor(<CASE>=case_completor.case, <INPUT_SCH>=drogon_pred.sch, <OUTPUT_SCH>=drogon_pred_ict.sch)
60
+ """,
61
+ )
completor/main.py CHANGED
@@ -5,8 +5,10 @@ from __future__ import annotations
5
5
  import logging
6
6
  import os
7
7
  import re
8
+ import sys
8
9
  import time
9
10
 
11
+ from matplotlib.backends.backend_pdf import PdfPages # type: ignore
10
12
  from tqdm import tqdm
11
13
 
12
14
  from completor import create_output, parse, read_schedule, utils
@@ -89,6 +91,7 @@ def create(
89
91
  """
90
92
  case = ReadCasefile(case_file=case_file, schedule_file=schedule, output_file=new_file)
91
93
  active_wells = utils.get_active_wells(case.completion_table, case.gp_perf_devicelayer)
94
+ pdf = None
92
95
  figure_name = None
93
96
  if show_fig:
94
97
  figure_no = 1
@@ -96,6 +99,7 @@ def create(
96
99
  while os.path.isfile(figure_name):
97
100
  figure_no += 1
98
101
  figure_name = f"Well_schematic_{figure_no:03d}.pdf"
102
+ pdf = PdfPages(figure_name)
99
103
 
100
104
  err: Exception | None = None
101
105
  well = None
@@ -122,13 +126,13 @@ def create(
122
126
  for chunk in find_keyword_data(Keywords.COMPLETION_SEGMENTS, schedule):
123
127
  clean_data = clean_raw_data(chunk, Keywords.COMPLETION_SEGMENTS)
124
128
  meaningful_data = read_schedule.set_compsegs(meaningful_data, clean_data)
125
- for i, well_name in tqdm(enumerate(active_wells.tolist()), total=len(active_wells)):
129
+ for i, well_name in tqdm(enumerate(active_wells.tolist()), total=len(active_wells), file=sys.stdout):
126
130
  try:
127
131
  well = Well(well_name, i, case, meaningful_data[well_name])
128
132
  except KeyError:
129
133
  logger.warning(f"Well '{well_name}' is written in case file but does not exist in schedule file.")
130
134
  continue
131
- compdat, welsegs, compsegs, bonus = create_output.format_output(well, case, figure_name)
135
+ compdat, welsegs, compsegs, bonus = create_output.format_output(well, case, pdf)
132
136
  for keyword in [Keywords.COMPLETION_SEGMENTS, Keywords.WELL_SEGMENTS, Keywords.COMPLETION_DATA]:
133
137
  old_data = find_well_keyword_data(well_name, keyword, schedule)
134
138
  if not old_data:
@@ -156,6 +160,8 @@ def create(
156
160
  schedule = replace_preprocessing_names(schedule, case.mapper)
157
161
  with open(new_file, "w", encoding="utf-8") as file:
158
162
  file.write(schedule)
163
+ if pdf is not None:
164
+ pdf.close()
159
165
 
160
166
  if err is not None:
161
167
  raise err
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import math
4
4
  from collections.abc import MutableMapping
5
+ from pathlib import Path
5
6
  from typing import Any
6
7
 
7
8
  import numpy as np
@@ -1828,3 +1829,143 @@ def print_wsegdualrcp(df_wsegdualrcp: pd.DataFrame, well_number: int) -> str:
1828
1829
  print_df = Keywords.AUTONOMOUS_INFLOW_CONTROL_DEVICE + "\n" + dataframe_tostring(print_df, True)
1829
1830
  action += f"{print_df}\n/\nENDACTIO\n\n"
1830
1831
  return action
1832
+
1833
+
1834
+ def print_wsegdensity_pyaction(df_wsegdensity: pd.DataFrame) -> str:
1835
+ """Create PYACTION code.
1836
+
1837
+ Args:
1838
+ df_wsegdensity: Output from function prepare_wsegdensity.
1839
+
1840
+ Returns:
1841
+ Final code output formatted in PYACTION.
1842
+ """
1843
+ data_dict = df_wsegdensity.to_dict(orient="list")
1844
+ final_code = f"""
1845
+ import opm_embedded
1846
+
1847
+ ecl_state = opm_embedded.current_ecl_state
1848
+ schedule = opm_embedded.current_schedule
1849
+ report_step = opm_embedded.current_report_step
1850
+ summary_state = opm_embedded.current_summary_state
1851
+
1852
+ if 'setup_done' not in locals():
1853
+ execution_counter = dict()
1854
+ executed = False
1855
+ setup_done = True
1856
+
1857
+ data={data_dict}
1858
+
1859
+ for i in range(len(data["WELL"])):
1860
+ well_name = data["WELL"][i]
1861
+ segment_number = data["START_SEGMENT_NUMBER"][i]
1862
+ flow_coefficient = data["FLOW_COEFFICIENT"][i]
1863
+ oil_flow_area = data["OIL_FLOW_CROSS_SECTIONAL_AREA"][i]
1864
+ gas_flow_area = data["GAS_FLOW_CROSS_SECTIONAL_AREA"][i]
1865
+ water_flow_area = data["WATER_FLOW_CROSS_SECTIONAL_AREA"][i]
1866
+ max_flow_area = data["MAX_FLOW_CROSS_SECTIONAL_AREA"][i]
1867
+ water_low = data["WATER_HOLDUP_FRACTION_LOW_CUTOFF"][i]
1868
+ water_high = data["WATER_HOLDUP_FRACTION_HIGH_CUTOFF"][i]
1869
+ gas_low = data["GAS_HOLDUP_FRACTION_LOW_CUTOFF"][i]
1870
+ gas_high = data["GAS_HOLDUP_FRACTION_HIGH_CUTOFF"][i]
1871
+ defaults = data["DEFAULTS"][i]
1872
+
1873
+ swhf = summary_state[f"SWHF:{{well_name}}:{{segment_number}}"]
1874
+ sghf = summary_state[f"SGHF:{{well_name}}:{{segment_number}}"]
1875
+ suvtrig = summary_state[f"SUVTRIG:{{well_name}}:{{segment_number}}"]
1876
+
1877
+ keyword_oil = (
1878
+ f"WSEGVALV\\n"
1879
+ f" '{{well_name}}' {{segment_number}} {{flow_coefficient}} {{oil_flow_area}} {{defaults}} {{max_flow_area}} /\\n/"
1880
+ )
1881
+ keyword_water = (
1882
+ f"WSEGVALV\\n"
1883
+ f" '{{well_name}}' {{segment_number}} {{flow_coefficient}} {{water_flow_area}} {{defaults}} {{max_flow_area}} /\\n/"
1884
+ )
1885
+ keyword_gas = (
1886
+ f"WSEGVALV\\n"
1887
+ f" '{{well_name}}' {{segment_number}} {{flow_coefficient}} {{gas_flow_area}} {{defaults}} {{max_flow_area}} /\\n/"
1888
+ )
1889
+
1890
+ key = (well_name, segment_number)
1891
+ execution_counter.setdefault(key, 0)
1892
+
1893
+ if execution_counter[key] == 0:
1894
+ schedule.insert_keywords(keyword_oil, report_step)
1895
+ summary_state[f"SUVTRIG:{{well_name}}:{{segment_number}}"] = 0
1896
+
1897
+ if execution_counter[key] < 1000000:
1898
+ if swhf is not None and sghf is not None:
1899
+ if swhf <= water_high and sghf > gas_high and suvtrig == 0:
1900
+ schedule.insert_keywords(keyword_gas, report_step)
1901
+ summary_state[f"SUVTRIG:{{well_name}}:{{segment_number}}"] = 1
1902
+ execution_counter[key] += 1
1903
+
1904
+ elif swhf > water_high and sghf <= gas_high and suvtrig == 0:
1905
+ schedule.insert_keywords(keyword_water, report_step)
1906
+ summary_state[f"SUVTRIG:{{well_name}}:{{segment_number}}"] = 2
1907
+ execution_counter[key] += 1
1908
+
1909
+ elif sghf < gas_low and suvtrig == 1:
1910
+ schedule.insert_keywords(keyword_oil, report_step)
1911
+ summary_state[f"SUVTRIG:{{well_name}}:{{segment_number}}"] = 0
1912
+ execution_counter[key] += 1
1913
+
1914
+ elif swhf < water_low and suvtrig == 2:
1915
+ schedule.insert_keywords(keyword_oil, report_step)
1916
+ summary_state[f"SUVTRIG:{{well_name}}:{{segment_number}}"] = 0
1917
+ execution_counter[key] += 1
1918
+ """
1919
+ return final_code
1920
+
1921
+
1922
+ def print_python_file(code: str, dir: str, well_name: str, lateral_number: int) -> str:
1923
+ """Print Python PYACTION file.
1924
+
1925
+ Args:
1926
+ code: Final code output formatted in PYACTION.
1927
+ dir: Output path.
1928
+ well_name: Well name.
1929
+ lateral_number: Lateral number.
1930
+
1931
+ Returns:
1932
+ Python file with PYACTION format, output directory with FMU format.
1933
+ """
1934
+ base_dir = Path.cwd() if Path(dir).parent == Path(".") else Path(dir).parent
1935
+ fmu_path = Path("eclipse/include/")
1936
+ if str(fmu_path) in str(base_dir):
1937
+ base_include_path = Path("../include/schedule")
1938
+ else:
1939
+ base_include_path = Path("")
1940
+ output_directory = f"{base_include_path}/wsegdensity_{well_name}_{lateral_number}.py"
1941
+ python_file = base_dir / f"wsegdensity_{well_name}_{lateral_number}.py"
1942
+ with open(python_file, "w") as file:
1943
+ file.writelines(code)
1944
+ return output_directory
1945
+
1946
+
1947
+ def print_wsegdensity_include(output_directory: str, well_name: str, lateral_number: int) -> str:
1948
+ """Formatted PYACTION include in the output file.
1949
+
1950
+ Args:
1951
+ output_directory: Include file path in FMU relative format.
1952
+ well_name: Well name.
1953
+ lateral_number: Lateral number.
1954
+
1955
+ Returns:
1956
+ Include file output for the output file.
1957
+ """
1958
+ action = f"""
1959
+ -------------------------------------
1960
+ -- START OF PYACTION SECTION
1961
+
1962
+ PYACTION
1963
+ WSEGDENSITY_{well_name}_{lateral_number} UNLIMITED /
1964
+
1965
+ '{output_directory}' /
1966
+
1967
+ -- END OF PYACTION SECTION
1968
+ -------------------------------------
1969
+ """
1970
+
1971
+ return action
@@ -91,6 +91,7 @@ class ReadCasefile:
91
91
  self.minimum_segment_length: float = 0.0
92
92
  self.strict = True
93
93
  self.gp_perf_devicelayer = False
94
+ self.python_dependent = False
94
95
  self.schedule_file = schedule_file
95
96
  self.output_file = output_file
96
97
  self.completion_table = pd.DataFrame()
@@ -119,6 +120,7 @@ class ReadCasefile:
119
120
  self.read_wsegvalv()
120
121
  self.read_wsegsicd()
121
122
  self.read_wsegdensity()
123
+ self.read_python_dependent()
122
124
  self.read_wseginjv()
123
125
  self.read_wsegdualrcp()
124
126
  self.read_wsegicv()
@@ -555,6 +557,18 @@ class ReadCasefile:
555
557
  if not check_contents(device_checks, self.wseginjv_table[Headers.DEVICE_NUMBER].to_numpy()):
556
558
  raise CompletorError(f"Not all device in COMPLETION is specified in {Keywords.INJECTION_VALVE}")
557
559
 
560
+ def read_python_dependent(self) -> None:
561
+ """Read PYTHON keyword. Accepts TRUE or just '/' as True."""
562
+ start_index, end_index = parse.locate_keyword(self.content, Keywords.PYTHON_DEPENDENT)
563
+
564
+ if end_index == start_index + 1:
565
+ # Keyword followed directly by '/', no value = True
566
+ self.python_dependent = True
567
+ elif end_index == start_index + 2:
568
+ val = self.content[start_index + 1]
569
+ if val.upper() == "TRUE":
570
+ self.python_dependent = True
571
+
558
572
  def read_wsegdualrcp(self) -> None:
559
573
  """Read the DUALRCP keyword in the case file.
560
574
 
@@ -1,29 +1,29 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: completor
3
- Version: 1.2.0
3
+ Version: 1.4.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
7
7
  Author: Equinor ASA
8
8
  Author-email: opensource@equinor.com
9
- Requires-Python: >=3.11,<4.0
9
+ Requires-Python: >=3.11,<3.14
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
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
16
17
  Provides-Extra: ert
17
18
  Provides-Extra: test
18
- Requires-Dist: ert (>=12,<13) ; extra == "ert"
19
+ Requires-Dist: ert (>=14,<15) ; extra == "ert"
19
20
  Requires-Dist: matplotlib (>=3.9,<4.0)
20
- Requires-Dist: numpy (>=1.26,<3.0)
21
+ Requires-Dist: numpy (>=2.0,<3.0)
21
22
  Requires-Dist: pandas (>=2.2,<3.0)
22
23
  Requires-Dist: pytest (>=8.3,<9.0) ; extra == "test"
23
24
  Requires-Dist: pytest-env (>=1,<2) ; extra == "test"
24
25
  Requires-Dist: pytest-xdist (>=3.6,<4.0) ; extra == "test"
25
26
  Requires-Dist: rstcheck-core (>=1.2,<2.0) ; extra == "test"
26
- Requires-Dist: scipy (>=1.14,<2.0)
27
27
  Requires-Dist: tqdm (>=4.66,<5.0)
28
28
  Project-URL: Bug Tracker, https://github.com/equinor/completor/issues
29
29
  Project-URL: Documentation, https://equinor.github.io/completor
@@ -44,7 +44,7 @@ Detailed documentation for usage of completor can be found at https://equinor.gi
44
44
  ## Getting started as a user
45
45
 
46
46
  ### Prerequisites
47
- * [Python](https://www.python.org/), version 3.11
47
+ * [Python](https://www.python.org/), version 3.11 or 3.13.
48
48
  * [ERT](https://github.com/equinor/ert) (optional, and only available on Linux.)
49
49
 
50
50
  ### Installation
@@ -85,7 +85,7 @@ and making a pull request.
85
85
  See [Contribution Document](documentation/docs/contribution_guide.mdx) on how to contribute.
86
86
 
87
87
  ### Install completor as dev
88
- In order to run Completor® you need to have versions of [Python 3.11](https://www.python.org/downloads/) installed.
88
+ In order to run Completor® you need to have versions of [Python 3.11, 3.12 or 3.13](https://www.python.org/downloads/) installed.
89
89
  #### Source code
90
90
  Clone the [Completor® repository](https://github.com/equinor/completor) to get the source code.
91
91
  ```bash
@@ -1,27 +1,29 @@
1
1
  completor/__init__.py,sha256=k6amf7jhp7KkBIlaw93-NZITxyZjPSzA5McFAZh8yv8,76
2
2
  completor/completion.py,sha256=quhQOdQREv8MsHW5amT0sG-84Y12KVdT0eylbKCul3g,31177
3
3
  completor/config_jobs/run_completor,sha256=XePKj2xocfGF0XFRqr7sqfpZGrjgWcfaZLHIhVvGFCQ,600
4
- completor/constants.py,sha256=eZPe6cbP8see9f_sJdUkdy6J6HM2eXulgqRCzTCBAqY,13489
5
- completor/create_output.py,sha256=T0hPnCOns86q2lvRMNuOz8Nz-8zlliYBtxNiiEPMRWs,24101
4
+ completor/constants.py,sha256=2e1o5Env5lHyhopkrs_C__9DksS5xml1-Ea_X3cLl0E,13521
5
+ completor/create_output.py,sha256=6ygglcCUYYII5sWr_ey4cvUCCsXb0UGqXY5GQAzX8fQ,25969
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
- completor/hook_implementations/jobs.py,sha256=8Ao8UadfJzEY7jWzj6C_OXm18fHmfXrbCB2_jS7KHiU,2180
10
+ completor/hook_implementations/__init__.py,sha256=4tdNpVj3MNfzTMIGkp2MjP1_z3grIlwJikiA-f1a24g,104
11
+ completor/hook_implementations/forward_model_steps.py,sha256=48tKeQUE8Kx1jhxs1MCGFR_ndgciuOxe27T74qE4GQM,776
12
+ completor/hook_implementations/run_completor.py,sha256=WZhry4KmzxIaufXduYa6EO8BQHY3wNJ_gDXRRnPma6Y,2164
11
13
  completor/input_validation.py,sha256=F17AWi-qR6cX5_1lEFgtYOMOO0Xo3gTtL2ETM5P_uGE,15022
12
14
  completor/launch_args_parser.py,sha256=gb3FcyufZlRnKS3BZkFmgVH1VoSxMD0MbCLsHZKmz4c,1413
13
15
  completor/logger.py,sha256=gYDbPL8ca5qT_MqYlDKEMKcSfIXW_59QklX8Gss5b2U,4784
14
- completor/main.py,sha256=ipARMDXXqPYRtBxYQnlj5MohepwkgrQkX3s2IJEaEcI,9151
16
+ completor/main.py,sha256=HlQzv5gpBYdDbYTBLSkAnK7o6d2QfTQnjrNizq3Ym6s,9343
15
17
  completor/parse.py,sha256=EGlt9CgkrXPqa7woyWQ5t_nh6OWsFrc2SJr8aFr_KsQ,20133
16
- completor/prepare_outputs.py,sha256=YS6p_kfTw_zXi0jKPwxrx45_54wrUaD0ryuZ2qe_S5Q,82582
17
- completor/read_casefile.py,sha256=qs39CkpwyNXV4cm0tpMi9NOyehBQKOBWmsCjSrWEauM,37578
18
+ completor/prepare_outputs.py,sha256=o-7SLD0F5jiw5_lnq5kewB1tYOiLa0u2TxGz6kRIO-8,87613
19
+ completor/read_casefile.py,sha256=NebZRrO35aqrZIJ7eHcS6buBbAaDWw8wUtc4kBHsV2U,38184
18
20
  completor/read_schedule.py,sha256=IYyCubOggFGg664h1flTl7MUJhJWyibr6JsptnURjUA,18101
19
21
  completor/utils.py,sha256=9Qayt7xMsimpoVDo5sL86gVaQBbIOezMBRyMXd3haXw,13598
20
22
  completor/visualization.py,sha256=ObxThqIyW3fsvVIupxVsgySFI_54n_Di-HTzFtDMSgo,4580
21
23
  completor/visualize_well.py,sha256=HLglSN2ce-u9nklJelqwhSxwwaj7n-aU6o8lUScbiI0,8790
22
24
  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,,
25
+ completor-1.4.0.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
26
+ completor-1.4.0.dist-info/METADATA,sha256=CahCvh1VKD17-UNNyhV6DhsmMLkg1pmES92__OETFDc,8018
27
+ completor-1.4.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
28
+ completor-1.4.0.dist-info/entry_points.txt,sha256=rhRKVhqh_ItG-tD5YrCfMvsGnfv-h0TXP0jX_fB6Kbc,121
29
+ completor-1.4.0.dist-info/RECORD,,
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ completor=completor.main:main
3
+
4
+ [ert]
5
+ run_completor=completor.hook_implementations.forward_model_steps
6
+
@@ -1,71 +0,0 @@
1
- import sys
2
- from pathlib import Path
3
-
4
- from completor.logger import logger
5
-
6
- try:
7
- from ert import plugin as ert_plugin # type: ignore
8
- except ModuleNotFoundError:
9
-
10
- def ert_plugin(name: str = ""):
11
- """Dummy decorator"""
12
-
13
- def decorator(func):
14
- return func
15
-
16
- return decorator
17
-
18
- logger.warning("Cannot import ERT, did you install Completor with ert option enabled?")
19
-
20
-
21
- def _get_jobs_from_directory(directory):
22
- resources = Path(sys.modules["completor"].__file__).parent / directory
23
-
24
- all_files = [resources / filename for filename in resources.glob("*") if (resources / filename).exists()]
25
- return {path.name: str(path) for path in all_files}
26
-
27
-
28
- @ert_plugin(name="completor")
29
- def installable_jobs():
30
- return _get_jobs_from_directory("config_jobs")
31
-
32
-
33
- @ert_plugin(name="completor")
34
- def job_documentation(job_name):
35
- if job_name != "run_completor":
36
- return None
37
-
38
- description = """Completor is a script for modelling
39
- wells with advanced completion.
40
- It generates a well schedule to be included in reservoir simulator,
41
- by combining the multi-segment tubing definition (from pre-processor reservoir modelling tools)
42
- with a user defined file specifying the completion design.
43
- The resulting well schedule comprises all keywords and parameters required by
44
- reservoir simulator. See the Completor documentation for details.
45
-
46
- Required:
47
- ---------
48
- -i : followed by name of file specifying completion design (e.g. completion.case).
49
- -s : followed by name of schedule file with multi-segment tubing definition,
50
- including COMPDAT, COMPSEGS and WELSEGS (required if not specified in case file).
51
-
52
- Optional:
53
- ---------
54
- --help : how to run completor.
55
- --about : about completor.
56
- -o : followed by name of completor output file.
57
- --figure : generates a pdf file with a schematics of the well segment structure.
58
-
59
- """
60
-
61
- examples = """.. code-block:: console
62
- FORWARD_MODEL run_completor(
63
- <CASE>=path/to/completion.case,
64
- <INPUT_SCH>=path/to/input.sch,
65
- <OUTPUT_SCH>path/to/output.sch
66
- )
67
- """
68
-
69
- category = "modelling.reservoir"
70
-
71
- return {"description": description, "examples": examples, "category": category}
@@ -1,6 +0,0 @@
1
- [console_scripts]
2
- completor=completor.main:main
3
-
4
- [ert]
5
- run_completor=completor.hook_implementations.jobs
6
-