Esempio n. 1
0
    def generate_2d_grid(self, n, xmax, ymax):
        g1 = pp.CartGrid([xmax * n, ymax * n], physdims=[xmax, ymax])
        g1.compute_geometry()
        gb = pp.GridBucket()

        gb.add_nodes(g1)
        tol = 1e-6
        left_faces = np.argwhere(g1.face_centers[1] > ymax - tol).ravel()
        right_faces = np.argwhere(g1.face_centers[1] < 0 + tol).ravel()
        val = np.ones(left_faces.size, dtype=np.bool)
        shape = [g1.num_faces, g1.num_faces]

        face_faces = sps.coo_matrix((val, (right_faces, left_faces)), shape=shape)

        gb.add_edge((g1, g1), face_faces)

        mg = pp.TensorGrid(np.linspace(0, xmax, n + 1))
        mg.nodes[1] = ymax

        mg.compute_geometry()

        d_e = gb.edge_props((g1, g1))
        d_e["mortar_grid"] = pp.MortarGrid(g1.dim - 1, {"0": mg}, face_faces)
        gb.assign_node_ordering()
        return gb
Esempio n. 2
0
    def generate_grid_with_fractures(self, n, xmax, ymax):
        x = [xmax / 3] * 2
        y = [0, ymax / 2]
        fracs = [np.array([x, y])]
        y = [ymax / 2, ymax]
        fracs.append(np.array([x, y]))

        gb = pp.meshing.cart_grid(fracs, [xmax * n, ymax * n], physdims=[xmax, ymax])

        tol = 1e-6
        for g, d in gb:
            # map right faces to left
            left = np.argwhere(g.face_centers[1] < tol).ravel()
            right = np.argwhere(g.face_centers[1] > ymax - tol).ravel()
            g.face_centers[1, right] = 0

            g.left = left
            g.right = right

        # now create mappings between two grids of equal dimension
        for dim in [2, 1]:
            grids = gb.grids_of_dimension(dim)
            for i, gi in enumerate(grids):
                if gi.left.size < 1:
                    continue
                for j, gj in enumerate(grids):
                    if gj.right.size < 1:
                        continue
                    face_faces = self.match_grids(gi, gj)

                    if np.sum(face_faces) == 0:
                        continue

                    gb.add_edge((gi, gj), face_faces)
                    d_e = gb.edge_props((gi, gj))

                    di = gb.node_props(gi)
                    dj = gb.node_props(gj)

                    if di["node_number"] < dj["node_number"]:
                        # gj is left
                        g_m, _, _ = pp.partition.extract_subgrid(
                            gj, gj.right, faces=True
                        )
                        face_faces = face_faces.T
                    else:
                        # gi is left
                        g_m, _, _ = pp.partition.extract_subgrid(
                            gi, gi.left, faces=True
                        )

                    d_e["mortar_grid"] = pp.MortarGrid(
                        gi.dim - 1, {"0": g_m}, face_faces
                    )

        gb.compute_geometry()  # basically reset g.face_centers[,right]
        gb.assign_node_ordering()

        return gb
Esempio n. 3
0
def test_pickle_mortar_grid(g):
    fn = 'tmp.grid'
    g.compute_geometry()
    mg = pp.MortarGrid(g.dim, {0: g, 1: g})

    pickle.dump(mg, open(fn, 'wb'))
    mg_read = pickle.load(open(fn, 'rb'))

    test_utils.compare_mortar_grids(mg, mg_read)

    mg_one_sided = pp.MortarGrid(g.dim, {0: g})

    pickle.dump(mg, open(fn, 'wb'))
    mg_read = pickle.load(open(fn, 'rb'))

    test_utils.compare_mortar_grids(mg_one_sided, mg_read)

    test_utils.delete_file(fn)
Esempio n. 4
0
    def generate_grids(self, n, xmax, ymax, split):
        g1 = pp.CartGrid([split * n, ymax * n], physdims=[split, ymax])
        g2 = pp.CartGrid([(xmax - split) * n, ymax * n], physdims=[xmax - split, ymax])
        g2.nodes[0] += split

        g1.compute_geometry()
        g2.compute_geometry()
        grids = [g2, g1]

        gb = pp.GridBucket()

        [gb.add_nodes(g) for g in grids]
        [g2, g1] = gb.grids_of_dimension(2)

        tol = 1e-6
        if np.any(g2.cell_centers[0] > split):
            right_grid = g2
            left_grid = g1
        else:
            right_grid = g1
            left_grid = g2

        gb.set_node_prop(left_grid, "node_number", 1)
        gb.set_node_prop(right_grid, "node_number", 0)
        left_faces = np.argwhere(left_grid.face_centers[0] > split - tol).ravel()
        right_faces = np.argwhere(right_grid.face_centers[0] < split + tol).ravel()
        val = np.ones(left_faces.size, dtype=np.bool)
        shape = [right_grid.num_faces, left_grid.num_faces]

        face_faces = sps.coo_matrix((val, (right_faces, left_faces)), shape=shape)

        gb.add_edge((right_grid, left_grid), face_faces)

        mg = pp.TensorGrid(np.array([split] * ((n + 1) * ymax)))
        mg.nodes[1] = np.linspace(0, ymax, (n + 1) * ymax)
        mg.compute_geometry()
        d_e = gb.edge_props((g1, g2))
        d_e["mortar_grid"] = pp.MortarGrid(g1.dim - 1, {"0": mg}, face_faces)
        d_e["edge_number"] = 0

        return gb
Esempio n. 5
0
def _update_mortar_grid(g_h: pp.Grid, g_l: pp.Grid, d_e: Dict[str, Any],
                        new_cells, new_faces_h):

    mg_old = d_e["mortar_grid"]

    # Face-cell map. This has been updated during splitting, thus it has
    # the shapes of the new grids
    face_cells = d_e["face_cells"]

    cells, faces, _ = sps.find(face_cells)

    # If this is ever broken, we have a problem
    other_side_old = mg_old._ind_face_on_other_side

    other_side_new = np.copy(other_side_old)

    # Make sure that the + and - side of the new mortar cells is
    # coherent with those already in place. This may not be strictly
    # necessary, as the normal vectors of the grid will be adjusted
    # locally to the +- convention, however, it will ease the interpretation
    # of results, including debugging.

    #
    for ci in new_cells:
        # Find the occurences of this new cell in the face-cell map.
        # There should be exactly two of these.
        hit = np.where(ci == cells)[0]
        assert hit.size == 2
        # Find the faces in the higher-dimensional grid that correspond
        # to this new cell
        loc_faces = faces[hit]

        # The new faces will be on each side of the fracture, and
        # there will be at least one node not shared by the faces.
        # We need to pick one of the faces, and find its neighboring
        # faces along the fracture, on the same side of the fracture.
        # The sign of the new face (in the mortar grid) will be the
        # same as the old one

        # We need to focus on split nodes, or else we risk finding neighboring
        # faces on both sides of the fracture.
        # Nodes of both local faces
        local_nodes_0 = g_h.face_nodes[:, loc_faces[0]].indices
        local_nodes_1 = g_h.face_nodes[:, loc_faces[1]].indices

        # Nodes that belong only to the first local face
        local_nodes_0_only = np.setdiff1d(local_nodes_0, local_nodes_1)

        # Get the other faces of these nodes. These will include both faces
        # on the fracture, and faces internal to g_h
        _, other_faces, _ = sps.find(g_h.face_nodes[local_nodes_0_only])

        # Pick those of the other faces that were not added during splitting
        old_other_faces = np.setdiff1d(other_faces, new_faces_h)

        if np.any(np.in1d(old_other_faces, other_side_old)):
            other_side_new = np.append(other_side_new, loc_faces[0])
        else:
            other_side_new = np.append(other_side_new, loc_faces[1])

    # The new mortar grid is constructed to be matching with g_l.
    # If splitting is undertaken for a non-matching grid, all bets are off.
    side_grids = {1: g_l, 2: g_l}
    mg_new = pp.MortarGrid(g_l.dim,
                           side_grids,
                           d_e["face_cells"],
                           face_duplicate_ind=other_side_new)

    d_e["mortar_grid"] = mg_new
    def create_grid(self):
        """
        Domain is unit square. One fracture, centered on (0.5, 0.5), tilted
        according to self.angle.

        """

        angle = self.angle

        corners = np.array([[0, 1, 1, 0], [0, 0, 1, 1], [0, 0, 0, 0]])

        # The fracture points always have x coordinates 0.2 and 0.8
        # The y-coordinates are set so that the fracture forms the prescribed angle with
        # the x-axis
        frac_pt = np.array(
            [[0.2, 0.8],
             [0.5 - 0.3 * np.tan(angle), 0.5 + 0.3 * np.tan(angle)], [0, 0]])

        nodes = np.hstack((corners, frac_pt))

        rows = np.array([
            [0, 1],
            [1, 5],
            [5, 0],
            [1, 2],
            [2, 5],
            [2, 4],
            [2, 3],
            [3, 0],
            [3, 4],
            [4, 0],
            [4, 5],
            [4, 5],
        ]).ravel()
        cols = np.vstack((np.arange(12), np.arange(12))).ravel("F")
        data = np.ones_like(rows)

        fn_2d = sps.coo_matrix((data, (rows, cols)), shape=(6, 12)).tocsc()
        rows = np.array([[0, 1, 2], [1, 3, 4], [4, 5, 10], [5, 6, 8],
                         [7, 8, 9], [2, 11, 9]]).ravel()
        cols = np.tile(np.arange(6), (3, 1)).ravel("F")
        data = np.array(
            [-1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1])
        cf_2d = sps.coo_matrix((data, (rows, cols)), shape=(12, 6)).tocsc()

        g_2d = pp.Grid(2, nodes, fn_2d, cf_2d, "mock_2d_grid")
        g_2d.compute_geometry()

        fn_1d = sps.csc_matrix(np.array([[1, 0], [0, 1]]))
        cf_1d = sps.csc_matrix(np.array([[-1], [1]]))

        g_1d = pp.Grid(1, frac_pt, fn_1d, cf_1d, "mock_1d_grid")
        g_1d.compute_geometry()

        gb = pp.GridBucket()
        gb.add_nodes([g_2d, g_1d])

        # Construct mortar grid
        side_grids = {
            mortar_grid.LEFT_SIDE: g_1d.copy(),
            mortar_grid.RIGHT_SIDE: g_1d.copy(),
        }

        data = np.array([1, 1])
        row = np.array([0, 0])
        col = np.array([10, 11])
        face_cells_mortar = sps.coo_matrix(
            (data, (row, col)),
            shape=(g_1d.num_cells, g_2d.num_faces)).tocsc()

        mg = pp.MortarGrid(1, side_grids, face_cells_mortar)

        edge = (g_2d, g_1d)
        gb.add_edge(edge, face_cells_mortar)
        d = gb.edge_props(edge)

        d["mortar_grid"] = mg

        self.gb = gb
        self._Nd = 2

        self.g1 = g_1d
        self.g2 = g_2d
        self.edge = edge
        self.mg = mg
Esempio n. 7
0
def extrude_grid_bucket(gb: pp.GridBucket, z: np.ndarray) -> Tuple[pp.GridBucket, Dict]:
    """ Extrude a GridBucket by extending all fixed-dimensional grids in the z-direction.

    In practice, the original grid bucket will be 2d, and the result is 3d.

    The returned GridBucket is fully functional, including mortar grids on the gb edges.
    The data dictionaries on nodes and edges are mainly empty. Data can be transferred from
    the original GridBucket via the returned map between old and new grids.

    Parameters:
        gb (pp.GridBukcet): Mixed-dimensional grid to be extruded. Should be 2d.
        z (np.ndarray): z-coordinates of the nodes in the extruded grid. Should be
            either non-negative or non-positive, and be sorted in increasing or
            decreasing order, respectively.

    Returns:
        gb (pp.GridBucket): Mixed-dimensional grid, 3d. The data dictionaries on nodes and
            edges are mostly empty.
        dict: Mapping from individual grids in the old bucket to the corresponding
            extruded grids in the new one. The dictionary values are a namedtuple with
            elements grid (new grid), cell_map and face_map, where the two latter
            describe mapping between the new and old grid, see extrude_grid for details.

    """

    # New GridBucket. to be filled in
    gb_new = pp.GridBucket()

    # Data structure for mapping between old and new grids
    g_map = {}

    # Container for grid information
    Mapping = namedtuple("mapping", ["grid", "cell_map", "face_map"])

    # Loop over all grids in the old bucket, extrude the grid, save mapping information
    for g, _ in gb:
        g_new, cell_map, face_map = extrude_grid(g, z)

        if hasattr(g, "frac_num"):
            g_new.frac_num = g.frac_num

        gb_new.add_nodes([g_new])

        g_map[g] = Mapping(g_new, cell_map, face_map)

    # Loop over all edges in the old grid, create corresponding edges in the new gb.
    # Also define mortar_grids
    for e, d in gb.edges():

        # grids of the old edge, extruded version of each grid
        gl, gh = gb.nodes_of_edge(e)
        gl_new = g_map[gl].grid
        gh_new = g_map[gh].grid

        # Next, we need the cell-face mapping for the new grid.
        # The idea is to first find the old map, then replace each cell-face relation
        # with the set of cells and faces (exploiting first that the new grids are
        # matching due to the extrusion algorithm, and second that the cell-map and
        # face-map stores indices in increasing layer index, so that the first cell
        # and first face both are in the first layer, thus they match, etc.).
        face_cells_old = d["face_cells"]

        # cells (in low-dim grid) and faces in high-dim grid that define the same
        # geometric quantity
        cells, faces, _ = sps.find(face_cells_old)

        # Cell-map for the low-dimensional grid, face-map for the high-dim
        cell_map = g_map[gl].cell_map
        face_map = g_map[gh].face_map

        # Data structure for the new face-cell map
        rows = np.empty(0, dtype=np.int)
        cols = np.empty(0, dtype=np.int)

        # The standard MortarGrid __init__ assumes that when faces are split because of
        # a fracture, the faces are ordered with one side first, then the other. This
        # will not be True for this layered construction. Instead, keep track of all
        # faces that should be moved to the other side.
        face_on_other_side = np.empty(0, dtype=np.int)

        # Loop over cells in gl would not have been as clean, as each cell is associated
        # with faces on both sides
        # Faces are found from the high-dim grid, cells in the low-dim grid
        for idx in range(faces.size):
            rows = np.hstack((rows, cell_map[cells[idx]]))
            cols = np.hstack((cols, face_map[faces[idx]]))

            # Here, we tacitly assume that the original grid had its faces split in the
            # standard way, that is, all faces on one side have index lower than any
            # face on the other side.
            if faces[idx] > np.median(faces):
                face_on_other_side = np.hstack(
                    (face_on_other_side, face_map[faces[idx]])
                )

        data = np.ones(rows.size, dtype=np.bool)
        # Create new face-cell map
        face_cells_new = sps.coo_matrix(
            (data, (rows, cols)), shape=(gl_new.num_cells, gh_new.num_faces)
        ).tocsc()

        # Define the new edge
        e = (gh_new, gl_new)
        # Add to new gb, together with the new face-cell map
        gb_new.add_edge(e, face_cells_new)

        # Create a mortar grid, add to data of new edge
        side_g = {
            mortar_grid.LEFT_SIDE: gl_new.copy(),
            mortar_grid.RIGHT_SIDE: gl_new.copy(),
        }

        # Construct mortar grid, with instructions on which faces belong to which side
        mg = pp.MortarGrid(
            gl_new.dim, side_g, face_cells_new, face_duplicate_ind=face_on_other_side
        )

        d_new = gb_new.edge_props(e)

        d_new["mortar_grid"] = mg

    return gb_new, g_map