misc.grid.sector

Extract a sector from an existing cornerpoint grid.

  1"""\
  2Extract a sector from an existing cornerpoint grid.
  3"""
  4import argparse
  5import collections
  6import logging
  7import numpy
  8import re
  9import sys
 10
 11import misc.grid as pyr
 12import misc.grdecl as grdecl
 13
 14
 15# add a valid log in case we are not run through the main program which
 16# sets up one for us
 17log = logging.getLogger(__name__)  # pylint: disable=invalid-name
 18log.addHandler(logging.NullHandler())
 19
 20# three consecutive integers, separated by comma, and perhaps some spaces
 21# thrown in for readability, optionally enclosed in parenthesis
 22tuple_format = re.compile(r'\(?([0-9]+)\ *\,\ *([0-9]+)\ *\,\ *([0-9]+)\)?')
 23
 24
 25def parse_tuple(corner):
 26    """\
 27    Parse a parenthesized tuple into three constituent coordinates.
 28
 29    :param corner:  Coordinate specification on the format "(i1,j1,k1)"
 30    :type  corner:  str
 31    :returns:       The parsed tuple, converted into zero-based coordinates
 32                    and in Python-matrix order: (k, j, i)
 33    :rtype  :       (int, int, int)
 34    """
 35    # let the regular expression engine parse the string
 36    match = re.match(tuple_format, corner.strip())
 37
 38    # if the string matched, then we know that each of the sub-groups can be
 39    # parsed into strings successfully. group 0 is the entire string, so we
 40    # get natural numbering into the parenthesized expressions. subtract one
 41    # to get zero-based coordinates
 42    if match:
 43        i = int(match.group(1)) - 1
 44        j = int(match.group(2)) - 1
 45        k = int(match.group(3)) - 1
 46        return (k, j, i)
 47    # if we didn't got any valid string, then return a bottom value
 48    else:
 49        return None
 50
 51
 52def sort_tuples(corner, opposite):
 53    """\
 54    :param corner:    Coordinates of one corner
 55    :type  corner:    (int, int, int)
 56    :param opposite:  Coordinates of the opposite corner
 57    :type  opposite:  (int, int, int)
 58    :returns:         The two tuples, but with coordinates interchanged so that
 59                      one corner is always in the lower, left, back and the
 60                      other is in the upper, right, front
 61    :rtype:           ((int, int, int), (int, int, int))
 62    """
 63    # pick out the most extreme variant in either direction, into each its own
 64    # variable; this may be the same as the input or not, but at least we know
 65    # for sure when we return from this method
 66    least = (min(corner[0], opposite[0]),
 67             min(corner[1], opposite[1]),
 68             min(corner[2], opposite[2]))
 69    most = (max(corner[0], opposite[0]),
 70            max(corner[1], opposite[1]),
 71            max(corner[2], opposite[2]))
 72    return (least, most)
 73
 74
 75def extract_dimens(least, most):
 76    """\
 77    Build a new dimension tuple for a submodel
 78
 79    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
 80    :type  least:  (int, int, int)
 81    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
 82    :type  most:   (int, int, int)
 83    :returns:      Dimensions of the submodel
 84    :rtype:        numpy.ndarray((3,))
 85    """
 86    # split the corners into constituents
 87    k1, j1, i1 = least
 88    k2, j2, i2 = most
 89
 90    # make an array out of the cartesian distance of the two corners
 91    sector_dimens = numpy.array([i2-i1+1, j2-j1+1, k2-k1+1], dtype=numpy.int32)
 92    return sector_dimens
 93
 94
 95def extract_coord(coord, least, most):
 96    """\
 97    Extract the coordinate pillars for a submodel
 98
 99    :param coord:  Coordinate pillars for the entire grid
100    :type  coord:  numpy.ndarray((nj+1, ni+1, 2, 3))
101    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
102    :type  least:  (int, int, int)
103    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
104    :type  most:   (int, int, int)
105    :returns:      Coordinate pilars for the submodel
106    :rtype:        numpy.ndarray((j2-j1+2, i2-i1+2, 2, 3))
107    """
108    # split the corners into constituents
109    k1, j1, i1 = least
110    k2, j2, i2 = most
111
112    # add one to get the pillar on the other side of the element, so that
113    # we include the last element, add one more since Python indexing is
114    # end-exclusive
115    sector_coord = coord[j1:(j2+2), i1:(i2+2), :, :]
116    return sector_coord
117
118
119def extract_zcorn(zcorn, least, most):
120    """\
121    Extract the hinge depth values for a submodel
122
123    :param zcorn:  Hinge depth values for the entire grid
124    :type  zcorn:  numpy.ndarray((nk, 2, nj, 2, ni, 2))
125    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
126    :type  least:  (int, int, int)
127    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
128    :type  most:   (int, int, int)
129    :returns:      Hinge depth values for the submodel
130    :rtype:        numpy.ndarray((k2-k1+1, 2, j2-j1+1, 2, i2-i1+1))
131    """
132    # split the corners into constituents
133    k1, j1, i1 = least
134    k2, j2, i2 = most
135
136    # add one since Python-indexing is end-exclusive, and we want to include
137    # the element in the opposite corner. all eight hinges are returned for
138    # each element (we only select in every other dimension)
139    sector_zcorn = zcorn[k1:(k2+1), :, j1:(j2+1), :, i1:(i2+1), :]
140    return sector_zcorn
141
142
143def extract_cell_prop(prop, least, most):
144    """\
145    Extract the property values for a submodel
146
147    :param prop:   Property values for each cell in the the entire grid
148    :type  prop:   numpy.ndarray((nk, nj, ni))
149    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
150    :type  least:  (int, int, int)
151    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
152    :type  most:   (int, int, int)
153    :returns:      Property values for each cell in the submodel
154    :rtype:        numpy.ndarray((k2-k1+1, j2-j1+1, i2-i1+1))
155    """
156    # split the corners into constituents
157    k1, j1, i1 = least
158    k2, j2, i2 = most
159
160    # add one since Python-indexing is end-exclusive, and we want to include
161    # the element in the opposite corner.
162    sector_prop = prop[k1:(k2+1), j1:(j2+1), i1:(i2+1)]
163    return sector_prop
164
165
166def extract_grid(grid, least, most):
167    """\
168    Extract a submodel from a full grid
169
170    :param grid:   Attributes of the full grid, like COORD, ZCORN, ACTNUM
171    :type  grid:   dict
172    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
173    :type  least:  (int, int, int)
174    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
175    :type  most:   (int, int, int)
176    :returns:      Attributes of the sector model
177    :rtype:        dict
178    """
179    # create a new grid and fill extract standard properties
180    sector = collections.OrderedDict()
181    sector['DIMENS'] = extract_dimens(least, most)
182    sector['COORD'] = extract_coord(grid['COORD'], least, most)
183    sector['ZCORN'] = extract_zcorn(grid['ZCORN'], least, most)
184    sector['ACTNUM'] = extract_cell_prop(grid['ACTNUM'], least, most)
185
186    # then extract all extra cell properties, such as PORO, PERMX that
187    # may have been added
188    for prop_name in grid:
189        # ignore the standard properties; they have already been converted
190        # by specialized methods above
191        prop_upper = prop_name.upper()
192        std_prop = ((prop_upper == 'DIMENS') or (prop_upper == 'COORD') or
193                    (prop_upper == 'ZCORN') or (prop_upper == 'ACTNUM'))
194        # use the generic method to convert this property
195        if not std_prop:
196            sector[prop_name] = extract_cell_prop(grid[prop_name], least, most)
197
198    return sector
199
200
201def main(*args):
202    """Read a data file to see if it parses OK."""
203    # setup simple logging where we prefix each line with a letter code
204    logging.basicConfig(level=logging.INFO,
205                        format="%(levelname).1s: %(message).76s")
206
207    # parse command-line arguments
208    parser = argparse.ArgumentParser()
209    parser.add_argument("infile", metavar="infile.grdecl", help="Input model")
210    parser.add_argument("outfile", metavar="outfile",
211                        help="Output sector model")
212    parser.add_argument("first", metavar="i1,j1,k1", type=parse_tuple,
213                        help="A corner of the sector model (one-based)")
214    parser.add_argument("last", metavar="i2,j2,k2", type=parse_tuple,
215                        help="The opposite corner of the sector (one-based)")
216    parser.add_argument("--dialect", choices=['ecl', 'cmg'], default='ecl')
217    parser.add_argument("--verbose", action='store_true')
218    parser.add_argument("--quiet", action='store_true')
219    cmd_args = parser.parse_args(*args)
220
221    # adjust the verbosity of the program
222    if cmd_args.verbose:
223        logging.getLogger(__name__).setLevel(logging.DEBUG)
224    if cmd_args.quiet:
225        logging.getLogger(__name__).setLevel(logging.NOTSET)
226
227    # read the input file
228    log.info('Reading full grid')
229    grid = pyr.read_grid(cmd_args.infile)
230
231    # get the two opposite corners that defines the submodel
232    log.info('Determining scope of sector model')
233    least, most = sort_tuples(cmd_args.first, cmd_args.last)
234    log.info('Sector model is (%d, %d, %d)-(%d, %d, %d)',
235             least[2]+1, least[1]+1, least[0]+1,
236             most[2]+1, most[1]+1, most[0]+1)
237
238    # extract the data for the submodel into a new grid
239    log.info('Extracting sector model from full grid')
240    submodel = extract_grid(grid, least, most)
241
242    # write this grid to output
243    log.info('Writing sector model to disk')
244    grdecl.write(cmd_args.outfile, submodel, cmd_args.dialect,
245                 multi_file=True)
246
247
248# if this module is called as a standalone program, then pass all the
249# arguments, except the program name
250if __name__ == "__main__":
251    main(sys.argv[1:])
log = <Logger misc.grid.sector (WARNING)>
tuple_format = re.compile('\\(?([0-9]+)\\ *\\,\\ *([0-9]+)\\ *\\,\\ *([0-9]+)\\)?')
def parse_tuple(corner):
26def parse_tuple(corner):
27    """\
28    Parse a parenthesized tuple into three constituent coordinates.
29
30    :param corner:  Coordinate specification on the format "(i1,j1,k1)"
31    :type  corner:  str
32    :returns:       The parsed tuple, converted into zero-based coordinates
33                    and in Python-matrix order: (k, j, i)
34    :rtype  :       (int, int, int)
35    """
36    # let the regular expression engine parse the string
37    match = re.match(tuple_format, corner.strip())
38
39    # if the string matched, then we know that each of the sub-groups can be
40    # parsed into strings successfully. group 0 is the entire string, so we
41    # get natural numbering into the parenthesized expressions. subtract one
42    # to get zero-based coordinates
43    if match:
44        i = int(match.group(1)) - 1
45        j = int(match.group(2)) - 1
46        k = int(match.group(3)) - 1
47        return (k, j, i)
48    # if we didn't got any valid string, then return a bottom value
49    else:
50        return None

Parse a parenthesized tuple into three constituent coordinates.

Parameters
  • corner: Coordinate specification on the format "(i1,j1,k1)" :returns: The parsed tuple, converted into zero-based coordinates and in Python-matrix order: (k, j, i)
def sort_tuples(corner, opposite):
53def sort_tuples(corner, opposite):
54    """\
55    :param corner:    Coordinates of one corner
56    :type  corner:    (int, int, int)
57    :param opposite:  Coordinates of the opposite corner
58    :type  opposite:  (int, int, int)
59    :returns:         The two tuples, but with coordinates interchanged so that
60                      one corner is always in the lower, left, back and the
61                      other is in the upper, right, front
62    :rtype:           ((int, int, int), (int, int, int))
63    """
64    # pick out the most extreme variant in either direction, into each its own
65    # variable; this may be the same as the input or not, but at least we know
66    # for sure when we return from this method
67    least = (min(corner[0], opposite[0]),
68             min(corner[1], opposite[1]),
69             min(corner[2], opposite[2]))
70    most = (max(corner[0], opposite[0]),
71            max(corner[1], opposite[1]),
72            max(corner[2], opposite[2]))
73    return (least, most)
Parameters
  • corner: Coordinates of one corner
  • opposite: Coordinates of the opposite corner :returns: The two tuples, but with coordinates interchanged so that one corner is always in the lower, left, back and the other is in the upper, right, front
def extract_dimens(least, most):
76def extract_dimens(least, most):
77    """\
78    Build a new dimension tuple for a submodel
79
80    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
81    :type  least:  (int, int, int)
82    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
83    :type  most:   (int, int, int)
84    :returns:      Dimensions of the submodel
85    :rtype:        numpy.ndarray((3,))
86    """
87    # split the corners into constituents
88    k1, j1, i1 = least
89    k2, j2, i2 = most
90
91    # make an array out of the cartesian distance of the two corners
92    sector_dimens = numpy.array([i2-i1+1, j2-j1+1, k2-k1+1], dtype=numpy.int32)
93    return sector_dimens

Build a new dimension tuple for a submodel

Parameters
  • least: Lower, left-most, back corner of submodel, (k1, j1, i1)
  • most: Upper, right-most, front corner of submodel, (k2, j2, i2) :returns: Dimensions of the submodel
def extract_coord(coord, least, most):
 96def extract_coord(coord, least, most):
 97    """\
 98    Extract the coordinate pillars for a submodel
 99
100    :param coord:  Coordinate pillars for the entire grid
101    :type  coord:  numpy.ndarray((nj+1, ni+1, 2, 3))
102    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
103    :type  least:  (int, int, int)
104    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
105    :type  most:   (int, int, int)
106    :returns:      Coordinate pilars for the submodel
107    :rtype:        numpy.ndarray((j2-j1+2, i2-i1+2, 2, 3))
108    """
109    # split the corners into constituents
110    k1, j1, i1 = least
111    k2, j2, i2 = most
112
113    # add one to get the pillar on the other side of the element, so that
114    # we include the last element, add one more since Python indexing is
115    # end-exclusive
116    sector_coord = coord[j1:(j2+2), i1:(i2+2), :, :]
117    return sector_coord

Extract the coordinate pillars for a submodel

Parameters
  • coord: Coordinate pillars for the entire grid
  • least: Lower, left-most, back corner of submodel, (k1, j1, i1)
  • most: Upper, right-most, front corner of submodel, (k2, j2, i2) :returns: Coordinate pilars for the submodel
def extract_zcorn(zcorn, least, most):
120def extract_zcorn(zcorn, least, most):
121    """\
122    Extract the hinge depth values for a submodel
123
124    :param zcorn:  Hinge depth values for the entire grid
125    :type  zcorn:  numpy.ndarray((nk, 2, nj, 2, ni, 2))
126    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
127    :type  least:  (int, int, int)
128    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
129    :type  most:   (int, int, int)
130    :returns:      Hinge depth values for the submodel
131    :rtype:        numpy.ndarray((k2-k1+1, 2, j2-j1+1, 2, i2-i1+1))
132    """
133    # split the corners into constituents
134    k1, j1, i1 = least
135    k2, j2, i2 = most
136
137    # add one since Python-indexing is end-exclusive, and we want to include
138    # the element in the opposite corner. all eight hinges are returned for
139    # each element (we only select in every other dimension)
140    sector_zcorn = zcorn[k1:(k2+1), :, j1:(j2+1), :, i1:(i2+1), :]
141    return sector_zcorn

Extract the hinge depth values for a submodel

Parameters
  • zcorn: Hinge depth values for the entire grid
  • least: Lower, left-most, back corner of submodel, (k1, j1, i1)
  • most: Upper, right-most, front corner of submodel, (k2, j2, i2) :returns: Hinge depth values for the submodel
def extract_cell_prop(prop, least, most):
144def extract_cell_prop(prop, least, most):
145    """\
146    Extract the property values for a submodel
147
148    :param prop:   Property values for each cell in the the entire grid
149    :type  prop:   numpy.ndarray((nk, nj, ni))
150    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
151    :type  least:  (int, int, int)
152    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
153    :type  most:   (int, int, int)
154    :returns:      Property values for each cell in the submodel
155    :rtype:        numpy.ndarray((k2-k1+1, j2-j1+1, i2-i1+1))
156    """
157    # split the corners into constituents
158    k1, j1, i1 = least
159    k2, j2, i2 = most
160
161    # add one since Python-indexing is end-exclusive, and we want to include
162    # the element in the opposite corner.
163    sector_prop = prop[k1:(k2+1), j1:(j2+1), i1:(i2+1)]
164    return sector_prop

Extract the property values for a submodel

Parameters
  • prop: Property values for each cell in the the entire grid
  • least: Lower, left-most, back corner of submodel, (k1, j1, i1)
  • most: Upper, right-most, front corner of submodel, (k2, j2, i2) :returns: Property values for each cell in the submodel
def extract_grid(grid, least, most):
167def extract_grid(grid, least, most):
168    """\
169    Extract a submodel from a full grid
170
171    :param grid:   Attributes of the full grid, like COORD, ZCORN, ACTNUM
172    :type  grid:   dict
173    :param least:  Lower, left-most, back corner of submodel, (k1, j1, i1)
174    :type  least:  (int, int, int)
175    :param most:   Upper, right-most, front corner of submodel, (k2, j2, i2)
176    :type  most:   (int, int, int)
177    :returns:      Attributes of the sector model
178    :rtype:        dict
179    """
180    # create a new grid and fill extract standard properties
181    sector = collections.OrderedDict()
182    sector['DIMENS'] = extract_dimens(least, most)
183    sector['COORD'] = extract_coord(grid['COORD'], least, most)
184    sector['ZCORN'] = extract_zcorn(grid['ZCORN'], least, most)
185    sector['ACTNUM'] = extract_cell_prop(grid['ACTNUM'], least, most)
186
187    # then extract all extra cell properties, such as PORO, PERMX that
188    # may have been added
189    for prop_name in grid:
190        # ignore the standard properties; they have already been converted
191        # by specialized methods above
192        prop_upper = prop_name.upper()
193        std_prop = ((prop_upper == 'DIMENS') or (prop_upper == 'COORD') or
194                    (prop_upper == 'ZCORN') or (prop_upper == 'ACTNUM'))
195        # use the generic method to convert this property
196        if not std_prop:
197            sector[prop_name] = extract_cell_prop(grid[prop_name], least, most)
198
199    return sector

Extract a submodel from a full grid

Parameters
  • grid: Attributes of the full grid, like COORD, ZCORN, ACTNUM
  • least: Lower, left-most, back corner of submodel, (k1, j1, i1)
  • most: Upper, right-most, front corner of submodel, (k2, j2, i2) :returns: Attributes of the sector model
def main(*args):
202def main(*args):
203    """Read a data file to see if it parses OK."""
204    # setup simple logging where we prefix each line with a letter code
205    logging.basicConfig(level=logging.INFO,
206                        format="%(levelname).1s: %(message).76s")
207
208    # parse command-line arguments
209    parser = argparse.ArgumentParser()
210    parser.add_argument("infile", metavar="infile.grdecl", help="Input model")
211    parser.add_argument("outfile", metavar="outfile",
212                        help="Output sector model")
213    parser.add_argument("first", metavar="i1,j1,k1", type=parse_tuple,
214                        help="A corner of the sector model (one-based)")
215    parser.add_argument("last", metavar="i2,j2,k2", type=parse_tuple,
216                        help="The opposite corner of the sector (one-based)")
217    parser.add_argument("--dialect", choices=['ecl', 'cmg'], default='ecl')
218    parser.add_argument("--verbose", action='store_true')
219    parser.add_argument("--quiet", action='store_true')
220    cmd_args = parser.parse_args(*args)
221
222    # adjust the verbosity of the program
223    if cmd_args.verbose:
224        logging.getLogger(__name__).setLevel(logging.DEBUG)
225    if cmd_args.quiet:
226        logging.getLogger(__name__).setLevel(logging.NOTSET)
227
228    # read the input file
229    log.info('Reading full grid')
230    grid = pyr.read_grid(cmd_args.infile)
231
232    # get the two opposite corners that defines the submodel
233    log.info('Determining scope of sector model')
234    least, most = sort_tuples(cmd_args.first, cmd_args.last)
235    log.info('Sector model is (%d, %d, %d)-(%d, %d, %d)',
236             least[2]+1, least[1]+1, least[0]+1,
237             most[2]+1, most[1]+1, most[0]+1)
238
239    # extract the data for the submodel into a new grid
240    log.info('Extracting sector model from full grid')
241    submodel = extract_grid(grid, least, most)
242
243    # write this grid to output
244    log.info('Writing sector model to disk')
245    grdecl.write(cmd_args.outfile, submodel, cmd_args.dialect,
246                 multi_file=True)

Read a data file to see if it parses OK.