schedule-reader 0.7.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.
- schedule_reader/__init__.py +81 -0
- schedule_reader/compdat.py +153 -0
- schedule_reader/counter.py +66 -0
- schedule_reader/data_reader.py +796 -0
- schedule_reader/dates.py +37 -0
- schedule_reader/extract_keyword.py +39 -0
- schedule_reader/helpers.py +44 -0
- schedule_reader/property_keywords.py +81 -0
- schedule_reader/renamer.py +17 -0
- schedule_reader/schedule_keywords.py +75 -0
- schedule_reader/wcon.py +132 -0
- schedule_reader/welspec.py +119 -0
- schedule_reader/wlist.py +37 -0
- schedule_reader-0.7.0.dist-info/METADATA +59 -0
- schedule_reader-0.7.0.dist-info/RECORD +18 -0
- schedule_reader-0.7.0.dist-info/WHEEL +5 -0
- schedule_reader-0.7.0.dist-info/licenses/LICENSE +21 -0
- schedule_reader-0.7.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A set of functions to read and process the schedule data from a .DATA file, including functions to extract the WELSPECS, COMPDAT, WCONPROD, WCONINJE, WCONHIST, WCONINJH, and WLIST keywords from the schedule dictionary and return a DataFrame of the corresponding data by DATES. Also includes functions to read and process the property keywords in the .DATA file, such as DIMENS, PORO, PERMX, etc.
|
|
3
|
+
|
|
4
|
+
developed by: Martin Araya
|
|
5
|
+
email: martinaraya@gmail.com
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from .data_reader import read_data
|
|
10
|
+
from .welspec import extract_welspecs, extract_welspecl, extract_wellspec, extract_welspec2
|
|
11
|
+
from .compdat import extract_compdat, extract_compdatl, extract_compdat2
|
|
12
|
+
from .wcon import extract_wconprod, extract_wconinje, extract_wconhist, extract_wconinjh
|
|
13
|
+
from .wlist import extract_wlist
|
|
14
|
+
from .property_keywords import read_keyword_from_include, expand_keyword, ijk_index, get_dimens
|
|
15
|
+
from .schedule_keywords import extract_keyword
|
|
16
|
+
from .counter import start_counter
|
|
17
|
+
|
|
18
|
+
__all__ = ['compdat2df', 'welspecs2df', 'property2df', 'start_counter']
|
|
19
|
+
__version__ = '0.7.0'
|
|
20
|
+
__release__ = 20260228
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def compdat2df(path, encoding='cp1252', verbose=False):
|
|
24
|
+
if type(path) is dict:
|
|
25
|
+
return extract_compdat2(path)
|
|
26
|
+
return extract_compdat2(read_data(path, encoding=encoding, verbose=verbose))
|
|
27
|
+
|
|
28
|
+
def welspecs2df(path, encoding='cp1252', verbose=False):
|
|
29
|
+
if type(path) is dict:
|
|
30
|
+
return extract_welspec2(path)
|
|
31
|
+
return extract_welspec2(read_data(path, encoding=encoding, verbose=verbose))
|
|
32
|
+
|
|
33
|
+
def wconprod2df(path, encoding='cp1252', verbose=False):
|
|
34
|
+
if type(path) is dict:
|
|
35
|
+
return extract_wconprod(path)
|
|
36
|
+
return extract_wconprod(read_data(path, encoding=encoding, verbose=verbose))
|
|
37
|
+
|
|
38
|
+
def wconinje2df(path, encoding='cp1252', verbose=False):
|
|
39
|
+
if type(path) is dict:
|
|
40
|
+
return extract_wconinje(path)
|
|
41
|
+
return extract_wconinje(read_data(path, encoding=encoding, verbose=verbose))
|
|
42
|
+
|
|
43
|
+
def wconhist2df(path, encoding='cp1252', verbose=False):
|
|
44
|
+
if type(path) is dict:
|
|
45
|
+
return extract_wconhist(path)
|
|
46
|
+
return extract_wconhist(read_data(path, encoding=encoding, verbose=verbose))
|
|
47
|
+
|
|
48
|
+
def wconinjh2df(path, encoding='cp1252', verbose=False):
|
|
49
|
+
if type(path) is dict:
|
|
50
|
+
return extract_wconinjh(path)
|
|
51
|
+
return extract_wconinjh(read_data(path, encoding=encoding, verbose=verbose))
|
|
52
|
+
|
|
53
|
+
def wlist2df(path, encoding='cp1252', verbose=False):
|
|
54
|
+
if type(path) is dict:
|
|
55
|
+
return extract_wlist(path)
|
|
56
|
+
return extract_wlist(read_data(path, encoding=encoding, verbose=verbose))
|
|
57
|
+
|
|
58
|
+
def keyword2df(path, keyword, record_names=[], encoding='cp1252', verbose=False):
|
|
59
|
+
record_names = None if len(record_names) == 0 else record_names
|
|
60
|
+
if type(path) is dict:
|
|
61
|
+
return extract_keyword(path, keyword=keyword, record_names=record_names)
|
|
62
|
+
return extract_keyword(
|
|
63
|
+
read_data(path, encoding=encoding, verbose=verbose),
|
|
64
|
+
keyword=keyword, record_names=record_names)
|
|
65
|
+
|
|
66
|
+
def property2df(path, keyword, dimens=(None, None, None), encoding='cp1252', verbose=False, parse_to=None):
|
|
67
|
+
keyword_data = expand_keyword(read_keyword_from_include(path, keyword, encoding=encoding))
|
|
68
|
+
if dimens[0] is not None and dimens[1] is not None and dimens[2] is not None:
|
|
69
|
+
cells = ijk_index(dimens[0], dimens[1], dimens[2])
|
|
70
|
+
output = pd.Series(
|
|
71
|
+
keyword_data.split(),
|
|
72
|
+
index=pd.MultiIndex.from_tuples(cells),
|
|
73
|
+
name=keyword).reset_index()
|
|
74
|
+
output.columns = ['I', 'J', 'K', keyword]
|
|
75
|
+
output[['I', 'J', 'K']] = output[['I', 'J', 'K']].astype(int)
|
|
76
|
+
if parse_to is None:
|
|
77
|
+
parse_to = int if keyword.endswith('NUM') else float
|
|
78
|
+
output[keyword] = output[keyword].astype(parse_to)
|
|
79
|
+
return output
|
|
80
|
+
else:
|
|
81
|
+
return keyword_data
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains functions to extract and process the COMPDAT keyword from the schedule dictionary.
|
|
3
|
+
The COMPDAT keyword defines the completion data for wells in the reservoir simulation model. It includes information such as the well name, location, status, and other parameters related to the well completion.
|
|
4
|
+
The functions in this module will extract the COMPDAT data from the schedule dictionary, process it, and return it as a pandas DataFrame for further analysis and use in reservoir simulation models.
|
|
5
|
+
|
|
6
|
+
The main functions in this module are:
|
|
7
|
+
- `extract_compdat`: Extracts the COMPDAT keyword from the schedule dictionary and returns a DataFrame of COMPDAT data by DATES.
|
|
8
|
+
- `extract_compdatl`: Extracts the COMPDATL keyword from the schedule dictionary and returns a DataFrame of COMPDATL data by DATES.
|
|
9
|
+
- `extract_compdatm`: Alias for `extract_compdatl`.
|
|
10
|
+
|
|
11
|
+
developed by: Martin Araya
|
|
12
|
+
email: martinaraya@gmail.com
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import pandas as pd
|
|
16
|
+
from .dates import parse_dates
|
|
17
|
+
from .schedule_keywords import extract_keyword
|
|
18
|
+
from .welspec import extract_welspecs, extract_welspecl
|
|
19
|
+
|
|
20
|
+
__version__ = '0.7.0'
|
|
21
|
+
__release__ = 20260228
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _defaultIJ(well, date, IJ, welspec_table):
|
|
25
|
+
"""
|
|
26
|
+
Helper function to find the I and J coordinates for a well, from its WELSPECS definition.
|
|
27
|
+
Used when the I or J coordinates are defaulted in the COMPDAT keyword.
|
|
28
|
+
|
|
29
|
+
Params:
|
|
30
|
+
well: str
|
|
31
|
+
date: str or datetime
|
|
32
|
+
IJ: str 'I' 'J'
|
|
33
|
+
the coordinate to be extracted
|
|
34
|
+
welspecs_table: pandas.DataFrame
|
|
35
|
+
A DataFrame containing at least the 'date', 'well', 'I' and 'J' data from the WELSPEC keyword.
|
|
36
|
+
This DataFrame is prepared by the function `extract_welspecs`. It is automatically called by the function `extract_compdat` if required.
|
|
37
|
+
"""
|
|
38
|
+
return welspec_table[(welspec_table['well'] == well) & (welspec_table['date'] <= date), IJ].iloc[-1]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def extract_compdat(schedule_dict:dict) -> pd.DataFrame:
|
|
42
|
+
"""
|
|
43
|
+
Shortcut for `extract_keyword` for the COMPDAT keyword.
|
|
44
|
+
Extract the COMPDAT keyword from the schedule dictionary and return a DataFrame of COMPDAT data by DATES.
|
|
45
|
+
|
|
46
|
+
Params:
|
|
47
|
+
schedule_dict: dict
|
|
48
|
+
shedule dictionary prepared by the .data_reader.read_data function
|
|
49
|
+
|
|
50
|
+
Return:
|
|
51
|
+
pandas.DataFrame
|
|
52
|
+
"""
|
|
53
|
+
compdat_columns = ['date', 'well', 'I', 'J', 'K_up', 'K_low', 'status', 'saturation table',
|
|
54
|
+
'transimissibility factor', 'well bore diameter', 'Kh', 'skin', 'D-factor', 'direction',
|
|
55
|
+
'pressure equivalent radius']
|
|
56
|
+
compdat_table = extract_keyword(schedule_dict, 'COMPDAT', compdat_columns) # {}
|
|
57
|
+
if len(compdat_table) > 0:
|
|
58
|
+
compdat_table['date'] = parse_dates(
|
|
59
|
+
compdat_table['date'].to_list()) # to parse dates exactly as stated by DATES eclipse format
|
|
60
|
+
compdat_table['well'] = [well.strip("'") for well in compdat_table['well']]
|
|
61
|
+
compdat_table['well'] = compdat_table['well'].astype('string').astype('category', errors='ignore')
|
|
62
|
+
compdat_table = compdat_table.replace('1*', None).replace("'1*'", None)
|
|
63
|
+
to_int = ['I', 'J', 'K_up', 'K_low']
|
|
64
|
+
compdat_table[to_int] = compdat_table[to_int].astype('string').fillna('0').astype(int, errors='ignore')
|
|
65
|
+
if 0 in compdat_table['I'].values or 0 in compdat_table['J'].values:
|
|
66
|
+
welspecs_table = extract_welspecs(schedule_dict)
|
|
67
|
+
if 0 in compdat_table['I'].values:
|
|
68
|
+
compdat_table['I'] = [compdat_table['I'].iloc[r] if compdat_table['I'].iloc[r] > 0 else _defaultIJ(
|
|
69
|
+
compdat_table['date'].iloc[r], compdat_table['date'].iloc[r], 'I', welspecs_table) for r in
|
|
70
|
+
len(compdat_table)]
|
|
71
|
+
if 0 in compdat_table['J'].values:
|
|
72
|
+
compdat_table['J'] = [compdat_table['I'].iloc[r] if compdat_table['I'].iloc[r] > 0 else _defaultIJ(
|
|
73
|
+
compdat_table['date'].iloc[r], compdat_table['date'].iloc[r], 'J', welspecs_table) for r in
|
|
74
|
+
len(compdat_table)]
|
|
75
|
+
to_float = ['transimissibility factor', 'well bore diameter', 'Kh', 'skin', 'D-factor',
|
|
76
|
+
'pressure equivalent radius']
|
|
77
|
+
compdat_table[to_float] = compdat_table[to_float].astype(float, errors='ignore')
|
|
78
|
+
compdat_table['status'] = compdat_table['status'].astype('string').fillna('OPEN').astype('category', errors='ignore').cat.set_categories(['AUTO', 'OPEN', 'SHUT', 'HEAT'], rename=True)
|
|
79
|
+
compdat_table['skin'] = compdat_table['skin'].fillna(0.0)
|
|
80
|
+
compdat_table['direction'] = compdat_table['direction'].astype('string').fillna('Z').astype('category', errors='ignore').cat.set_categories(['X', 'Y', 'Z', 'FX', 'FY'], rename=True)
|
|
81
|
+
return compdat_table
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def extract_compdatl(schedule_dict:dict) -> pd.DataFrame:
|
|
85
|
+
"""
|
|
86
|
+
Shortcut for `extract_keyword` for the COMPDAT keyword.
|
|
87
|
+
Extract the COMPDAT keyword from the schedule dictionary and return a DataFrame of COMPDATL data by DATES.
|
|
88
|
+
|
|
89
|
+
Params:
|
|
90
|
+
schedule_dict: dict
|
|
91
|
+
schedule dictionary prepared by the .data_reader.read_data function
|
|
92
|
+
|
|
93
|
+
Return:
|
|
94
|
+
pandas.DataFrame
|
|
95
|
+
"""
|
|
96
|
+
compdatl_columns = ['date', 'well', 'local grid', 'I', 'J', 'K_up', 'K_low', 'status', 'saturation table',
|
|
97
|
+
'transimissibility factor', 'well bore diameter', 'Kh', 'skin', 'D-factor', 'direction',
|
|
98
|
+
'pressure equivalent radius']
|
|
99
|
+
compdatl_table = extract_keyword(schedule_dict, 'COMPDATL', compdatl_columns) # {}
|
|
100
|
+
if len(compdatl_table) > 0:
|
|
101
|
+
compdatl_table['date'] = parse_dates(
|
|
102
|
+
compdatl_table['date'].to_list()) # to parse dates exactly as stated by DATES eclipse format
|
|
103
|
+
compdatl_table['well'] = [well.strip("'") for well in compdatl_table['well']]
|
|
104
|
+
compdatl_table['well'] = compdatl_table['well'].astype('category', errors='ignore')
|
|
105
|
+
compdat_table = compdat_table.replace('1*', None).replace("'1*'", None)
|
|
106
|
+
to_int = ['I', 'J', 'K_up', 'K_low']
|
|
107
|
+
compdatl_table[to_int] = compdatl_table[to_int].astype('string').fillna('0').astype(int, errors='ignore')
|
|
108
|
+
if 0 in compdatl_table['I'].values or 0 in compdatl_table['J'].values:
|
|
109
|
+
welspecl_table = extract_welspecl(schedule_dict)
|
|
110
|
+
if 0 in compdatl_table['I'].values:
|
|
111
|
+
compdatl_table['I'] = [compdatl_table['I'].iloc[r] if compdatl_table['I'].iloc[r] > 0 else _defaultIJ(
|
|
112
|
+
compdatl_table['date'].iloc[r], compdatl_table['date'].iloc[r], 'I', welspecl_table) for r in
|
|
113
|
+
len(compdatl_table)]
|
|
114
|
+
if 0 in compdatl_table['J'].values:
|
|
115
|
+
compdatl_table['J'] = [compdatl_table['I'].iloc[r] if compdatl_table['I'].iloc[r] > 0 else _defaultIJ(
|
|
116
|
+
compdatl_table['date'].iloc[r], compdatl_table['date'].iloc[r], 'J', welspecl_table) for r in
|
|
117
|
+
len(compdatl_table)]
|
|
118
|
+
to_float = ['transimissibility factor', 'well bore diameter', 'Kh', 'skin', 'D-factor',
|
|
119
|
+
'pressure equivalent radius']
|
|
120
|
+
compdatl_table[to_float] = compdatl_table[to_float].astype(float, errors='ignore')
|
|
121
|
+
compdatl_table['status'] = compdatl_table['status'].astype('string').fillna('OPEN').astype('category', errors='ignore').cat.set_categories(['AUTO', 'OPEN', 'SHUT', 'HEAT'], rename=True)
|
|
122
|
+
compdatl_table['skin'] = compdatl_table['skin'].fillna(0.0)
|
|
123
|
+
compdatl_table['direction'] = compdatl_table['direction'].astype('string').fillna('Z').astype('category', errors='ignore').cat.set_categories(['X', 'Y', 'Z', 'FX', 'FY'], rename=True)
|
|
124
|
+
return compdatl_table
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def extract_compdatm(schedule_dict:dict) -> pd.DataFrame:
|
|
128
|
+
"""
|
|
129
|
+
alias for extract_compdatl
|
|
130
|
+
"""
|
|
131
|
+
return extract_compdatl(schedule_dict)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def extract_compdat2(schedule_dict:dict) -> pd.DataFrame:
|
|
135
|
+
"""
|
|
136
|
+
Extract COMPDAT, COMPDATL and COMPDATM from the schedule dictionary and return a DataFrame of COMPDAT(s) data by DATES.
|
|
137
|
+
|
|
138
|
+
Params:
|
|
139
|
+
schedule_dict: dict
|
|
140
|
+
schedule dictionary prepared by the .data_reader.read_data function
|
|
141
|
+
|
|
142
|
+
Return:
|
|
143
|
+
pandas.DataFrame
|
|
144
|
+
"""
|
|
145
|
+
compdat = extract_compdat(schedule_dict)
|
|
146
|
+
compdatl = extract_compdatl(schedule_dict)
|
|
147
|
+
|
|
148
|
+
if len(compdat) > 0 and len(compdatl) > 0:
|
|
149
|
+
return pd.concat([compdat, compdatl], axis=0, ignore_index=False)
|
|
150
|
+
elif len(compdatl) > 0:
|
|
151
|
+
return compdatl
|
|
152
|
+
else:
|
|
153
|
+
return compdat
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A simple counter class that can be used to count the number of iterations in a loop. It can be initialized with a starting value and a step size, and it has methods to increment, decrement, and get the current value of the counter. It also has a __call__ method that can be used to get the next value of the counter or the current value without incrementing it. The class also has __add__, __sub__, and __mult__ methods to perform arithmetic operations on the counter. The start_counter function is a helper function to create a new Counter instance with a given starting value.
|
|
3
|
+
|
|
4
|
+
developed by: Martin Araya
|
|
5
|
+
email: martinaraya@gmail.com
|
|
6
|
+
"""
|
|
7
|
+
__version__ = '0.7.0'
|
|
8
|
+
__release__ = 20260228
|
|
9
|
+
|
|
10
|
+
class Counter(object):
|
|
11
|
+
"""A simple counter that can be used to count the number of iterations in a loop."""
|
|
12
|
+
def __init__(self, start: int = 0, step: int = 1):
|
|
13
|
+
"""Initialize the counter with a starting value and a step size."""
|
|
14
|
+
if step == 0:
|
|
15
|
+
raise ValueError(f"The `step` can't be zero!")
|
|
16
|
+
self.start = start
|
|
17
|
+
self.step = step
|
|
18
|
+
self.current = start - step
|
|
19
|
+
|
|
20
|
+
def next(self):
|
|
21
|
+
"""Increment the counter by the step size and return the current value."""
|
|
22
|
+
self.current += self.step
|
|
23
|
+
return self.current
|
|
24
|
+
|
|
25
|
+
def curr(self):
|
|
26
|
+
"""Return the current value of the counter without incrementing it."""
|
|
27
|
+
return self.current if self.current >= self.start else self.start
|
|
28
|
+
|
|
29
|
+
def prev(self):
|
|
30
|
+
"""Decrement the counter by the step size and return the current value."""
|
|
31
|
+
self.current -= self.step
|
|
32
|
+
|
|
33
|
+
def __call__(self, count=True):
|
|
34
|
+
"""Return the next value of the counter if `count` is True, otherwise return the current value."""
|
|
35
|
+
if count is None:
|
|
36
|
+
return None
|
|
37
|
+
elif count:
|
|
38
|
+
return self.next()
|
|
39
|
+
elif not count:
|
|
40
|
+
return self.curr()
|
|
41
|
+
else:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def __add__(self, other: int):
|
|
45
|
+
"""Add a value to the current count and return the new count."""
|
|
46
|
+
self.current = self.curr + other
|
|
47
|
+
return self.curr()
|
|
48
|
+
|
|
49
|
+
def __sub__(self, other: int):
|
|
50
|
+
"""Subtract a value from the current count and return the new count."""
|
|
51
|
+
self.current = self.curr - other
|
|
52
|
+
return self.curr()
|
|
53
|
+
|
|
54
|
+
def __mult__(self, other: int):
|
|
55
|
+
"""Multiply the current count by a value and return the new count."""
|
|
56
|
+
self.current = self.curr * other
|
|
57
|
+
return self.curr()
|
|
58
|
+
|
|
59
|
+
def __repr__(self):
|
|
60
|
+
"""Return a string representation of the counter."""
|
|
61
|
+
return f"{self.curr()} counted"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def start_counter(start: int):
|
|
65
|
+
"""Start a counter with a given starting value."""
|
|
66
|
+
return Counter(start)
|