misc.grid.unstruct

Convert cornerpoint grids to unstructured grids

:example: import pyresito.grid.unstruct as us import pyresito.io.grdecl as grdecl

g = grdecl.read('~/proj/cmgtools/bld/overlap.grdecl')
  1"""\
  2Convert cornerpoint grids to unstructured grids
  3
  4:example:
  5    import pyresito.grid.unstruct as us
  6    import pyresito.io.grdecl as grdecl
  7
  8    g = grdecl.read('~/proj/cmgtools/bld/overlap.grdecl')
  9"""
 10# pylint: disable=too-few-public-methods, multiple-statements
 11import numpy as np
 12
 13
 14class Ridge (object):
 15    """A ridge consists of two points, anchored in each their pillar. We only
 16    need to store the z-values, because the x- and y- values are determined by
 17    the pillar themselves.
 18    """
 19    __slots__ = ['left', 'right']
 20
 21    def __init__(self, left, right):
 22        self.left = left
 23        self.right = right
 24
 25    def is_not_below(self, other):
 26        """Weak ordering of ridges based on vertical placement.
 27
 28        :param other:  Ridge to be compared to this object.
 29        :type other:   :class:`Ridge`
 30        :returns:      True if no point on self is below any on the other,
 31                       None if the ridges cross, and False if there is a point
 32                       on the other ridge that is above any on self.
 33        :rtype:        Boolean
 34        """
 35        # test each side separately. if self is at the same level as other,
 36        # this should count positively towards the test (i.e. it is regarded
 37        # as "at least as high"), so use less-or-equal.
 38        left_above = other.left <= self.left
 39        right_above = other.right <= self.right
 40
 41        # if both sides are at least as high, then self is regarded as at
 42        # least as high as other, and vice versa. if one side is lower and
 43        # one side is higher, then the ridges cross.
 44        if left_above:
 45            return True if right_above else None
 46        else:
 47            return None if right_above else False
 48
 49
 50class Face (object):
 51    """A (vertical) face consists of two ridges, because all of the faces in a
 52    hexahedron can be seen as (possibly degenerate) quadrilaterals.
 53    """
 54    __slots__ = ['top', 'btm']
 55
 56    def __init__(self, top, btm):
 57        self.top = top
 58        self.btm = btm
 59
 60    def is_above(self, other):
 61        """Weak ordering of faces based on vertical placement.
 62
 63        :param other:   Face to be compared to this object.
 64        :type other:    :class:`Face`
 65        :returns:       True if all points in face self is above all points in
 66                        face other, False otherwise
 67        :rtype:         Boolean
 68        """
 69        # if the bottom of self is aligned with the top of other, then the
 70        # face itself is considered to be above. since the ridge test also
 71        # has an indeterminate result, we test explicitly like this
 72        return True if self.btm.is_not_below(other.top) else False
 73
 74
 75def conv(grid):
 76    """Convert a cornerpoint grid to an unstructured grid.
 77
 78    :param grid:  Cornerpoint grid to be converted.
 79    :type grid:   dict, with 'COORD', 'ZCORN', 'ACTNUM'
 80    :returns:     Unstructured grid
 81    :rtype:       dict
 82    """
 83    # extract the properties of interest from the cornerpoint grid
 84    zcorn = grid['ZCORN']
 85    actnum = grid['ACTNUM']
 86    ni, nj, nk = grid['DIMENS']
 87
 88    # zcorn has now dimensionality (k, b, j, f, i, r) and actnum is (k, j, i);
 89    # permute the cubes to get the (b, k)-dimensions varying quickest, to avoid
 90    # page faults when we move along pillars/columns
 91    zcorn = np.transpose(zcorn,  axes=[2, 3, 4, 5, 1, 0])
 92    actnum = np.transpose(actnum, axes=[1, 2, 0])
 93
 94    # memory allocation: number of unique cornerpoints along each pillar, and
 95    # the index of each cornerpoint into the global list of vertices
 96    num_cp = np.empty((nj + 1, ni + 1), dtype=np.int32)
 97    ndx_cp = np.empty((nk, 2, nj, 2, ni, 2), dtype=np.int32)
 98
 99    # each pillar is connected to at most 2*2 columns (front/back, right/left),
100    # and each column has at most 2*nk (one top and one bottom) corners
101    corn_z = np.empty((2, 2, 2, nk), dtype=np.float32)
102    corn_i = np.empty((2, 2, 2, nk), dtype=np.int32)
103    corn_j = np.empty((2, 2, 2, nk), dtype=np.int32)
104    corn_a = np.empty((2, 2, 2, nk), dtype=np.bool)
105
106    # get all unique points that are hinged to a certain pillar (p, q)
107    for q, p in np.ndindex((nj + 1, ni + 1)):
108        # reset number of corners found for this column
109        num_corn_z = 0
110
111        for f, r in np.ndindex((2, 2)):  # front/back, right/left
112            # calculate the cell index of the column at this position
113            j = q - f
114            i = p - r
115
116            for b in range(2):  # bottom/top
117
118                # copy depth values for this corner; notice that we have
119                # pivoted the zcorn matrix so that values going upwards are in
120                # last dim.
121                corn_z[f, r, b, :] = zcorn[j, f, i, r, b, :]
122
123                # same for active numbers, but this is reused for top/bottom;
124                # if the cell is inactive, then both top and bottom point are.
125                corn_a[f, r, b, :] = actnum[j, i, :]
126
127                # save the original indices into these auxiliary arrays so that
128                # we can figure out where each point came from after they are
129                # sorted
130                corn_i[f, r, b] = i
131                corn_j[f, r, b] = j
class Ridge:
15class Ridge (object):
16    """A ridge consists of two points, anchored in each their pillar. We only
17    need to store the z-values, because the x- and y- values are determined by
18    the pillar themselves.
19    """
20    __slots__ = ['left', 'right']
21
22    def __init__(self, left, right):
23        self.left = left
24        self.right = right
25
26    def is_not_below(self, other):
27        """Weak ordering of ridges based on vertical placement.
28
29        :param other:  Ridge to be compared to this object.
30        :type other:   :class:`Ridge`
31        :returns:      True if no point on self is below any on the other,
32                       None if the ridges cross, and False if there is a point
33                       on the other ridge that is above any on self.
34        :rtype:        Boolean
35        """
36        # test each side separately. if self is at the same level as other,
37        # this should count positively towards the test (i.e. it is regarded
38        # as "at least as high"), so use less-or-equal.
39        left_above = other.left <= self.left
40        right_above = other.right <= self.right
41
42        # if both sides are at least as high, then self is regarded as at
43        # least as high as other, and vice versa. if one side is lower and
44        # one side is higher, then the ridges cross.
45        if left_above:
46            return True if right_above else None
47        else:
48            return None if right_above else False

A ridge consists of two points, anchored in each their pillar. We only need to store the z-values, because the x- and y- values are determined by the pillar themselves.

Ridge(left, right)
22    def __init__(self, left, right):
23        self.left = left
24        self.right = right
left
right
def is_not_below(self, other):
26    def is_not_below(self, other):
27        """Weak ordering of ridges based on vertical placement.
28
29        :param other:  Ridge to be compared to this object.
30        :type other:   :class:`Ridge`
31        :returns:      True if no point on self is below any on the other,
32                       None if the ridges cross, and False if there is a point
33                       on the other ridge that is above any on self.
34        :rtype:        Boolean
35        """
36        # test each side separately. if self is at the same level as other,
37        # this should count positively towards the test (i.e. it is regarded
38        # as "at least as high"), so use less-or-equal.
39        left_above = other.left <= self.left
40        right_above = other.right <= self.right
41
42        # if both sides are at least as high, then self is regarded as at
43        # least as high as other, and vice versa. if one side is lower and
44        # one side is higher, then the ridges cross.
45        if left_above:
46            return True if right_above else None
47        else:
48            return None if right_above else False

Weak ordering of ridges based on vertical placement.

Parameters
  • other: Ridge to be compared to this object. :returns: True if no point on self is below any on the other, None if the ridges cross, and False if there is a point on the other ridge that is above any on self.
class Face:
51class Face (object):
52    """A (vertical) face consists of two ridges, because all of the faces in a
53    hexahedron can be seen as (possibly degenerate) quadrilaterals.
54    """
55    __slots__ = ['top', 'btm']
56
57    def __init__(self, top, btm):
58        self.top = top
59        self.btm = btm
60
61    def is_above(self, other):
62        """Weak ordering of faces based on vertical placement.
63
64        :param other:   Face to be compared to this object.
65        :type other:    :class:`Face`
66        :returns:       True if all points in face self is above all points in
67                        face other, False otherwise
68        :rtype:         Boolean
69        """
70        # if the bottom of self is aligned with the top of other, then the
71        # face itself is considered to be above. since the ridge test also
72        # has an indeterminate result, we test explicitly like this
73        return True if self.btm.is_not_below(other.top) else False

A (vertical) face consists of two ridges, because all of the faces in a hexahedron can be seen as (possibly degenerate) quadrilaterals.

Face(top, btm)
57    def __init__(self, top, btm):
58        self.top = top
59        self.btm = btm
top
btm
def is_above(self, other):
61    def is_above(self, other):
62        """Weak ordering of faces based on vertical placement.
63
64        :param other:   Face to be compared to this object.
65        :type other:    :class:`Face`
66        :returns:       True if all points in face self is above all points in
67                        face other, False otherwise
68        :rtype:         Boolean
69        """
70        # if the bottom of self is aligned with the top of other, then the
71        # face itself is considered to be above. since the ridge test also
72        # has an indeterminate result, we test explicitly like this
73        return True if self.btm.is_not_below(other.top) else False

Weak ordering of faces based on vertical placement.

Parameters
  • other: Face to be compared to this object. :returns: True if all points in face self is above all points in face other, False otherwise
def conv(grid):
 76def conv(grid):
 77    """Convert a cornerpoint grid to an unstructured grid.
 78
 79    :param grid:  Cornerpoint grid to be converted.
 80    :type grid:   dict, with 'COORD', 'ZCORN', 'ACTNUM'
 81    :returns:     Unstructured grid
 82    :rtype:       dict
 83    """
 84    # extract the properties of interest from the cornerpoint grid
 85    zcorn = grid['ZCORN']
 86    actnum = grid['ACTNUM']
 87    ni, nj, nk = grid['DIMENS']
 88
 89    # zcorn has now dimensionality (k, b, j, f, i, r) and actnum is (k, j, i);
 90    # permute the cubes to get the (b, k)-dimensions varying quickest, to avoid
 91    # page faults when we move along pillars/columns
 92    zcorn = np.transpose(zcorn,  axes=[2, 3, 4, 5, 1, 0])
 93    actnum = np.transpose(actnum, axes=[1, 2, 0])
 94
 95    # memory allocation: number of unique cornerpoints along each pillar, and
 96    # the index of each cornerpoint into the global list of vertices
 97    num_cp = np.empty((nj + 1, ni + 1), dtype=np.int32)
 98    ndx_cp = np.empty((nk, 2, nj, 2, ni, 2), dtype=np.int32)
 99
100    # each pillar is connected to at most 2*2 columns (front/back, right/left),
101    # and each column has at most 2*nk (one top and one bottom) corners
102    corn_z = np.empty((2, 2, 2, nk), dtype=np.float32)
103    corn_i = np.empty((2, 2, 2, nk), dtype=np.int32)
104    corn_j = np.empty((2, 2, 2, nk), dtype=np.int32)
105    corn_a = np.empty((2, 2, 2, nk), dtype=np.bool)
106
107    # get all unique points that are hinged to a certain pillar (p, q)
108    for q, p in np.ndindex((nj + 1, ni + 1)):
109        # reset number of corners found for this column
110        num_corn_z = 0
111
112        for f, r in np.ndindex((2, 2)):  # front/back, right/left
113            # calculate the cell index of the column at this position
114            j = q - f
115            i = p - r
116
117            for b in range(2):  # bottom/top
118
119                # copy depth values for this corner; notice that we have
120                # pivoted the zcorn matrix so that values going upwards are in
121                # last dim.
122                corn_z[f, r, b, :] = zcorn[j, f, i, r, b, :]
123
124                # same for active numbers, but this is reused for top/bottom;
125                # if the cell is inactive, then both top and bottom point are.
126                corn_a[f, r, b, :] = actnum[j, i, :]
127
128                # save the original indices into these auxiliary arrays so that
129                # we can figure out where each point came from after they are
130                # sorted
131                corn_i[f, r, b] = i
132                corn_j[f, r, b] = j

Convert a cornerpoint grid to an unstructured grid.

Parameters
  • grid: Cornerpoint grid to be converted. :returns: Unstructured grid