schedule-reader 0.7.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Martín Carlos Araya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.4
2
+ Name: schedule_reader
3
+ Version: 0.7.0
4
+ Summary: a schedule parser for standard style input data files.
5
+ Author-email: Martin Carlos Araya <martinaraya@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Martín Carlos Araya
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/ayaranitram/schedule_reader
29
+ Project-URL: Bug Tracker, https://github.com/ayaranitram/schedule_reader/issues
30
+ Classifier: Programming Language :: Python :: 3.7
31
+ Classifier: Programming Language :: Python :: 3.8
32
+ Classifier: Programming Language :: Python :: 3.9
33
+ Classifier: Programming Language :: Python :: 3.10
34
+ Classifier: Programming Language :: Python :: 3.11
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Requires-Python: >=3.7
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Dynamic: license-file
41
+
42
+ # schedule_reader
43
+ A set of functions to read eclipse format DATA file and include files and extract well data from SCHEDULE section; like WELSPECS, COMPDAT, WCONHIST, WCONPROD or WLIST keywords.
44
+
45
+ Using the following functions, the corresponding keywords are recognized and the corresponding parameters names are set as columns labels:
46
+ - WELSPECS with `welspec2df`
47
+ - COMPDAT with `compdat2df`
48
+ - WCONHIST with `wconhist2df`
49
+ - WCONPROD with `wconprod2df`
50
+ - WCONINJH with `wconinjh2df`
51
+ - WCONINJE with `wconinje2df`
52
+ - WLIST with `wlist2df`
53
+
54
+ These functions must be provided with the path to .DATA file or include file as argument.
55
+ Any other SCHEDULE section keyword can be extracted, but the columns will no be labelled, using the function keyword2df providing the path to the file and requested keyword as arguments.
56
+
57
+ Returns a DataFrame with the extracted data, associated to its respective date from DATES keyword.
58
+
59
+
@@ -0,0 +1,18 @@
1
+ # schedule_reader
2
+ A set of functions to read eclipse format DATA file and include files and extract well data from SCHEDULE section; like WELSPECS, COMPDAT, WCONHIST, WCONPROD or WLIST keywords.
3
+
4
+ Using the following functions, the corresponding keywords are recognized and the corresponding parameters names are set as columns labels:
5
+ - WELSPECS with `welspec2df`
6
+ - COMPDAT with `compdat2df`
7
+ - WCONHIST with `wconhist2df`
8
+ - WCONPROD with `wconprod2df`
9
+ - WCONINJH with `wconinjh2df`
10
+ - WCONINJE with `wconinje2df`
11
+ - WLIST with `wlist2df`
12
+
13
+ These functions must be provided with the path to .DATA file or include file as argument.
14
+ Any other SCHEDULE section keyword can be extracted, but the columns will no be labelled, using the function keyword2df providing the path to the file and requested keyword as arguments.
15
+
16
+ Returns a DataFrame with the extracted data, associated to its respective date from DATES keyword.
17
+
18
+
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools.packages.find]
6
+ where = ["src"]
7
+
8
+ [project]
9
+ name = "schedule_reader"
10
+ version = "0.7.0"
11
+ authors = [
12
+ { name="Martin Carlos Araya", email="martinaraya@gmail.com" },
13
+ ]
14
+ description = "a schedule parser for standard style input data files."
15
+ readme = "README.md"
16
+ license = { file="LICENSE" }
17
+ requires-python = ">=3.7"
18
+ classifiers = [
19
+ "Programming Language :: Python :: 3.7",
20
+ "Programming Language :: Python :: 3.8",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "License :: OSI Approved :: MIT License",
25
+ "Operating System :: OS Independent",
26
+ ]
27
+ dependencies = [
28
+ ]
29
+ [project.urls]
30
+ "Homepage" = "https://github.com/ayaranitram/schedule_reader"
31
+ "Bug Tracker" = "https://github.com/ayaranitram/schedule_reader/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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)