grid
The grid module will extract static and dynamic cell properties from a grid (from the output files of reservoir simulators). Each row in a returned dataframe represents one cell.
Typical usage
from res2df import grid, ResdataFiles
resdatafiles = ResdataFiles('MYDATADECK.DATA')
dframe = grid.df(resdatafiles, rstdates='last')
where the API is documented at res2df.grid.df()
.
I |
J |
K |
X |
Y |
Z |
VOLUME |
ZONE |
DX |
DY |
DZ |
PERMX |
PERMY |
PERMZ |
MULTX |
MULTY |
MULTZ |
PORO |
NTG |
TOPS |
DEPTH |
TRANX |
TRANY |
TRANZ |
MINPVV |
MULTPV |
MULTX- |
MULTY- |
MULTZ- |
PVTNUM |
SATNUM |
EQLNUM |
FIPNUM |
SWCR |
SGCR |
SWL |
SWU |
SGU |
SWATINIT |
PCW |
ENDNUM |
PORV |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
33 |
23 |
12 |
462878.51 |
5935238.80 |
1696.67 |
55693.32 |
LowerReek |
155.67 |
157.75 |
2.34 |
872.85 |
762.13 |
12.04 |
1.00 |
1.00 |
1.00 |
0.17 |
1.00 |
1695.50 |
1696.67 |
2.75 |
20.80 |
44.68 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
5.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
0.52 |
0.29 |
1.00 |
9441.72 |
28 |
42 |
7 |
463686.47 |
5932157.60 |
1665.32 |
85390.39 |
MidReek |
164.68 |
145.10 |
3.79 |
543.31 |
500.66 |
150.98 |
1.00 |
1.00 |
1.00 |
0.21 |
1.00 |
1663.43 |
1665.32 |
16.83 |
24.57 |
936.74 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
3.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
0.17 |
1.38 |
1.00 |
17970.08 |
3 |
46 |
10 |
460477.69 |
5929629.55 |
1715.63 |
51612.48 |
MidReek |
162.53 |
158.84 |
2.00 |
11.17 |
10.67 |
2.56 |
1.00 |
1.00 |
1.00 |
0.10 |
1.00 |
1714.64 |
1715.63 |
0.22 |
0.23 |
143.98 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
2.00 |
4.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
1.00 |
1.00 |
5316.95 |
|
40 |
58 |
1 |
466630.07 |
5930981.02 |
1746.57 |
87201.74 |
UpperReek |
162.04 |
158.67 |
3.39 |
18.57 |
18.20 |
5.20 |
1.00 |
1.00 |
1.00 |
0.16 |
1.00 |
1744.87 |
1746.57 |
0.00 |
0.41 |
209.54 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
1.00 |
1.00 |
13769.95 |
|
1 |
18 |
12 |
457973.31 |
5933320.14 |
1707.47 |
3083.59 |
LowerReek |
162.87 |
158.88 |
0.12 |
1080.20 |
1092.24 |
587.32 |
1.00 |
1.00 |
1.00 |
0.19 |
1.00 |
1707.41 |
1707.47 |
0.04 |
0.07 |
188620.52 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
2.00 |
6.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
1.00 |
1.00 |
588.97 |
|
34 |
18 |
3 |
462588.36 |
5935985.66 |
1743.73 |
86129.97 |
UpperReek |
157.82 |
159.33 |
3.77 |
824.81 |
416.02 |
7.49 |
1.00 |
1.00 |
1.00 |
0.15 |
1.00 |
1741.85 |
1743.73 |
34.85 |
0.60 |
72.32 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
1.00 |
1.00 |
13159.47 |
|
16 |
33 |
8 |
461273.47 |
5932470.66 |
1695.74 |
98164.92 |
MidReek |
159.48 |
160.87 |
3.90 |
3546.26 |
3705.67 |
1031.11 |
1.00 |
1.00 |
1.00 |
0.28 |
1.00 |
1693.79 |
1695.74 |
84.65 |
0.69 |
4601.49 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
3.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
0.37 |
0.28 |
1.00 |
27372.72 |
7 |
31 |
1 |
459824.59 |
5932016.79 |
1691.00 |
90932.57 |
UpperReek |
157.02 |
161.91 |
3.45 |
8.57 |
8.05 |
2.40 |
1.00 |
1.00 |
1.00 |
0.08 |
1.00 |
1689.27 |
1691.00 |
0.32 |
0.42 |
27.87 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
2.00 |
2.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
1.00 |
1.00 |
7239.36 |
|
5 |
42 |
8 |
460441.80 |
5930342.11 |
1706.57 |
33530.51 |
MidReek |
161.63 |
158.73 |
1.30 |
8.26 |
8.77 |
2.22 |
1.00 |
1.00 |
1.00 |
0.11 |
1.00 |
1705.92 |
1706.56 |
0.17 |
0.19 |
278.38 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
2.00 |
4.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
1.00 |
1.00 |
3739.00 |
|
8 |
23 |
4 |
459334.81 |
5933199.93 |
1697.49 |
78722.27 |
UpperReek |
175.77 |
156.61 |
3.35 |
8.80 |
9.02 |
2.29 |
1.00 |
1.00 |
1.00 |
0.10 |
1.00 |
1695.82 |
1697.49 |
0.28 |
0.38 |
36.92 |
0.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
1.00 |
2.00 |
2.00 |
0.10 |
0.10 |
0.10 |
1.00 |
0.90 |
1.00 |
1.00 |
7595.92 |
Alternatively, the same data can be produced as a CSV file using the command line
res2csv grid --help # Will display some help text
res2csv grid MYDATADECK.DATA --rstdates last --verbose --output grid.csv
Select which vectors to include (INIT and/or restart vectors) with the
vectors
argument, as in the example:
res2csv grid --verbose MYDATADECK.DATA --vectors PRESSURE PERMX
Example computations on a grid dataframe
Some grid statistic operations are very neatly expressed using Python and
Pandas. Some examples (provided that the dframe
object is initialized as in
the topmost example):
Summation of floating point numbers is difficult for computers, as summing each
number in a sequence can lead to accumulation of roundoff errors. This is
proven to have an impact on volumetrics computations on a grid. For this, the
Python function math.fsum()
should always be used.
# Average non-weighted porosity:
dframe["PORO"].mean()
# Bulk volume in Gm3:
math.fsum(dframe["VOLUME"]) / 1e9
# Total pore volume:
math.fsum(dframe["PORV"])
# Average (weighted) porosity:
math.fsum(dframe["PORV"]) / math.fsum(dframe["VOLUME"])
# Apex reservoir (cell centre):
dframe["Z"].min()
# Apex reservoir (topmost cell corner):
dframe["Z_MIN"].min()
Pandas has powerful aggregation operators, and any thinkable statistical measure can be applied to the data. The Pandas groupby() operation can be used to get statistical measures pr. regions. All of the above examples can be rephrased to compute values for every SATNUM, EQLNUM or similar. Example:
# Apex reservoir pr equilibriation zone
In [3]: dframe.groupby(["EQLNUM"])["Z"].min()
Out[3]:
EQLNUM
1.0 1568.876251
2.0 1619.720749
Name: Z, dtype: float64
Zone information
As mentioned in Zone names, if the text file called zones.lyr is found alongside, zone information will automatically be merged into each row based on the K column. This can be used for statistics pr. zone,
# Permeability (arithmetic average) pr. zone
In [4]: dframe.groupby("ZONE")["PERMX"].mean()
Out[4]:
ZONE
LowerReek 979.605462
MidReek 833.304757
UpperReek 545.180473
If you have the layer information in a different file, you need to tell the code the whereabouts of the file:
from res2df import grid, ResdataFiles, common
resdatafiles = ResdataFiles("'MYDATADECK.DATA")
dframe = grid.df(resdatafiles)
# The filename with layers is relative to .DATA file location
# or an absolute path.
subzonemap = res2df.common.parse_zonemapfile("subzones.lyr")
dframe_with_subzones = common.merge_zones(
dframe, subzonemap, zoneheader="SUBZONE", kname="K"
)
For more control over merging of zones, check the documentation for
the function res2df.common.merge_zones()
and
res2df.common.parse_zonemapfile()
Dynamic data
By adding a restart date, dynamic data for one particular restart date can be
added, through the API option rstdates
or the command line option
--rstdates
.
You can write dates in ISO-8601 format, or you can specify first, last or
all. If you select all dates, you can choose to have a set of columns for
every date, or have the date encoded in a column called DATE
, this is
controlled via the --stackdates
option.
See also the pillars module for an application of the grid data. Calculating volumes of dynamic data (pr. some region parameter) can be obtained from that module as a by-product of the pillar computations.
Generating include files from grid data
If you have loaded grid data into a Pandas frame, some operations are easily performed,
scaling porosity, permeability etc. Or remapping some region parameters. Using the
res2df.grid.df2res()
function these manipulated vectors can be written back as
include files.
Say you want to change the FIPNUM, and that FIPNUM 6 should be removed, and set it to FIPNUM 5. This can be accomplished using
from res2df import grid, ResdataFiles, common
resdatafiles = ResdataFiles("'MYDATADECK.DATA")
dframe = grid.df(resdatafiles)
# Change FIPNUM 6 to FIPNUM 5:
rows_to_touch = dframe["FIPNUM"] == 6
dframe.loc[rows_to_touch, "FIPNUM"] = 5
# Write back to new include file, ensure datatype is integer.
grid.df2res(dframe, "FIPNUM", dtype=int, filename="fipnum.inc", resdatafiles=resdatafiles)
This will produce the file fipnum.inc with the contents:
-- Output file printed by res2df.grid 0.17.2
-- at 2023-11-16 9:31:23.318941
FIPNUM
21*2 19*1 20*2 20*1 20*2 20*1 19*2 21*1 19*2 21*1 18*2
22*1 18*2 22*1 18*2 22*1 17*2 23*1 16*2 24*1 16*2 24*1
15*2 25*1 15*2 25*1 15*2 25*1 15*2 25*1 15*2 25*1 14*2
[..snip...]
39*5 0 94*5 0 148*5 0 2235*5 0 39*5 0 39*5 0 94*5 0
148*5 0 104*5 0 2130*5 0 39*5 0 39*5 0 94*5 0 148*5
0 2235*5 0 39*5 0 39*5 0 94*5 0 148*5 0 1195*5
/ -- FIPNUM: 35817 active cells, 35840 total cell count
It is recommended to supply the resdatafiles
object to df2res
, if not, correct grid
size can not be ensured.