示例#1
0
    def test_merge_two_grids(self):
        # Merge two grids that have a common face. Check that global indices are
        # updated to match, and that they point to the same point coordinates
        data = np.ones(3)
        rows = np.array([0, 1, 2])
        cols = np.array([0, 0, 0])
        cf = sps.coo_matrix((data, (rows, cols)))

        data = np.ones(6)
        rows = np.array([0, 1, 1, 2, 2, 0])
        cols = np.array([0, 0, 1, 1, 2, 2])
        fn = sps.coo_matrix((data, (rows, cols)))
        nodes_1 = np.array([[0, 1, 0], [0, 0, 1], [0, 0, 0]])
        nodes_2 = np.array([[0, 1, 0], [0, 0, -1], [0, 0, 0]])
        g1 = MockGrid(2,
                      num_faces=3,
                      face_nodes=fn,
                      cell_faces=cf,
                      num_cells=1,
                      nodes=nodes_1)

        g2 = MockGrid(2,
                      num_faces=3,
                      face_nodes=fn,
                      cell_faces=cf,
                      num_cells=1,
                      nodes=nodes_2)
        g_11 = TensorGrid(np.array([0, 1]))
        g_11.global_point_ind = np.arange(2)
        g_22 = TensorGrid(np.array([0, 1]))
        g_22.global_point_ind = np.arange(2)

        gl = [[[g1], [g_11]], [[g2], [g_22]]]
        intersections = [np.array([1]), np.array([0])]

        list_of_grids, glob_ind = non_conforming.init_global_ind(gl)
        grid_list_1d = non_conforming.process_intersections(gl,
                                                            intersections,
                                                            glob_ind,
                                                            list_of_grids,
                                                            tol=1e-4)

        g_1d = grid_list_1d[0]
        ismem, maps = ismember_rows(g_1d.global_point_ind, g1.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g1.nodes[:, maps], g_1d.nodes)

        ismem, maps = ismember_rows(g_1d.global_point_ind, g2.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g2.nodes[:, maps], g_1d.nodes)
def make_maps(g0, g1, n_digits=8, offset=0.11):
    """
    Given two grid with the same nodes, faces and cells, the mappings between
    these entities are concstructed. Handles non-unique nodes and faces on next
    to fractures by exploiting neighbour information.
    Builds maps from g1 to g0, so g1.x[x_map]=g0.x, e.g.
    g1.tags[some_key][face_map] = g0.tags[some_key].
    g0 Reference grid
    g1 Other grid
    n_digits Tolerance in rounding before coordinate comparison
    offset: Weight determining how far the fracture neighbour nodes and faces
    are shifted (normally away from fracture) to ensure unique coordinates.
    """
    cell_map = sm.ismember_rows(
        np.around(g0.cell_centers, n_digits),
        np.around(g1.cell_centers, n_digits),
        sort=False,
    )[1]
    # Make face_centers unique by dragging them slightly away from the fracture

    fc0 = g0.face_centers.copy()
    fc1 = g1.face_centers.copy()
    n0 = g0.nodes.copy()
    n1 = g1.nodes.copy()
    fi0 = g0.tags["fracture_faces"]
    if np.any(fi0):
        fi1 = g1.tags["fracture_faces"]
        d0 = np.reshape(np.tile(g0.cell_faces[fi0, :].data, 3), (3, sum(fi0)))
        fn0 = g0.face_normals[:, fi0] * d0
        d1 = np.reshape(np.tile(g1.cell_faces[fi1, :].data, 3), (3, sum(fi1)))
        fn1 = g1.face_normals[:, fi1] * d1
        fc0[:, fi0] += fn0 * offset
        fc1[:, fi1] += fn1 * offset
        (ni0, fid0) = g0.face_nodes[:, fi0].nonzero()
        (ni1, fid1) = g1.face_nodes[:, fi1].nonzero()
        un, inv = np.unique(ni0, return_inverse=True)
        for i, node in enumerate(un):
            n0[:, node] += offset * np.mean(fn0[:, fid0[inv == i]], axis=1)
        un, inv = np.unique(ni1, return_inverse=True)
        for i, node in enumerate(un):
            n1[:, node] += offset * np.mean(fn1[:, fid1[inv == i]], axis=1)

    face_map = sm.ismember_rows(np.around(fc0, n_digits),
                                np.around(fc1, n_digits),
                                sort=False)[1]

    node_map = sm.ismember_rows(np.around(n0, n_digits),
                                np.around(n1, n_digits),
                                sort=False)[1]
    return cell_map, face_map, node_map
示例#3
0
    def test_face_centers_areas(self):
        face_nodes = self.g.face_nodes.indices.reshape((3, self.g.num_faces), order="F")
        ismem, ind_map = setmembership.ismember_rows(self.fn, face_nodes)
        self.assertTrue(np.all(ismem))

        self.assertTrue(np.allclose(self.face_areas, self.g.face_areas[ind_map]))
        self.assertTrue(np.allclose(self.face_center, self.g.face_centers[:, ind_map]))
示例#4
0
    def face_to_cell_map(g_2d, g_1d, loc_faces, loc_nodes):
        # Match faces in a 2d grid and cells in a 1d grid by identifying
        # face-nodes and cell-node relations.
        # loc_faces are faces in 2d grid that are known to coincide with
        # cells.
        # loc_nodes are indices of 2d nodes along the segment, sorted so that
        # the ordering coincides with nodes in 1d grid

        # face-node relation in higher dimensional grid
        fn = g_2d.face_nodes.indices.reshape((g_2d.dim, g_2d.num_faces), order="F")
        # Reduce to faces along segment
        fn_loc = fn[:, loc_faces]
        # Mapping from global (2d) indices to the local indices used in 1d
        # grid. This also account for a sorting of the nodes, so that the
        # nodes.
        ind_map = np.zeros(g_2d.num_faces)
        ind_map[loc_nodes] = np.arange(loc_nodes.size)
        # Face-node in local indices
        fn_loc = ind_map[fn_loc]
        # Handle special case
        if loc_faces.size == 1:
            fn_loc = fn_loc.reshape((2, 1))

        # Cell-node relation in 1d
        cn = g_1d.cell_nodes().indices.reshape((2, g_1d.num_cells), order="F")

        # Find cell index of each face
        ismem, ind = ismember_rows(fn_loc, cn)
        # Quality check, the grids should be conforming
        if not np.all(ismem):
            raise ValueError

        return ind
示例#5
0
    def test_merge_1d_permuted_nodes(self):
        g = TensorGrid(np.array([0, 1, 2]))
        g.nodes = np.array([[1, -1, 0], [0, 0, 0], [0, 0, 0]])
        g.global_point_ind = np.array([2, 0, 1])
        g.face_nodes.indices = np.array([1, 2, 0])

        h = TensorGrid(np.array([-1, 0, 1]))
        h.global_point_ind = np.array([0, 1, 2])
        g.compute_geometry()
        h.compute_geometry()
        c, _, _, _, _, _ = non_conforming.merge_1d_grids(g, h)

        ismem, maps = ismember_rows(c.global_point_ind, g.global_point_ind)
        assert ismem.sum() == c.num_nodes
        assert np.allclose(g.nodes[:, maps], c.nodes)
        ismem, maps = ismember_rows(c.global_point_ind, h.global_point_ind)
        assert ismem.sum() == c.num_nodes
        assert np.allclose(h.nodes[:, maps], c.nodes)
示例#6
0
    def test_ismember_rows_with_sort(self):
        a = np.array([[1, 3, 3, 1, 7], [3, 3, 2, 3, 0]])
        b = np.array([[3, 1, 3, 5, 3], [3, 3, 2, 1, 2]])
        ma, ia = setmembership.ismember_rows(a, b)

        ma_known = np.array([1, 1, 1, 1, 0], dtype=bool)
        ia_known = np.array([1, 0, 2, 1])

        self.assertTrue(np.allclose(ma, ma_known))
        self.assertTrue(np.allclose(ia, ia_known))
示例#7
0
    def test_ismember_rows_no_sort(self):
        a = np.array([[1, 3, 3, 1, 7], [3, 3, 2, 3, 0]])
        b = np.array([[3, 1, 2, 5, 3], [3, 3, 3, 1, 1]])
        ma, ia = setmembership.ismember_rows(a, b, sort=False)

        ma_known = np.array([1, 1, 0, 1, 0], dtype=bool)
        ia_known = np.array([1, 0, 1])

        assert np.allclose(ma, ma_known)
        assert np.allclose(ia, ia_known)
示例#8
0
    def cells_from_faces(g, fi):
        # Find cells of faces, specified by face indices fi.
        # It is assumed that fi is on the boundary, e.g. there is a single
        # cell for each element in fi.
        f, ci, _ = sps.find(g.cell_faces[fi])
        assert f.size == fi.size, "We assume fi are boundary faces"

        ismem, ind_map = ismember_rows(fi, fi[f], sort=False)
        assert np.all(ismem)
        return ci[ind_map]
示例#9
0
    def test_ismember_rows_1d(self):
        a = np.array([0, 2, 1, 13, 0])
        b = np.array([2, 4, 13, 0])

        ma, ia = setmembership.ismember_rows(a, b)

        ma_known = np.array([1, 1, 0, 1, 1], dtype=bool)
        ia_known = np.array([3, 0, 2, 3])

        self.assertTrue(np.allclose(ma, ma_known))
        self.assertTrue(np.allclose(ia, ia_known))
示例#10
0
    def test_ismember_rows_unqual_sizes_1(self):
        # b larger than b
        a = np.array([[1, 3, 3, 1, 7], [3, 3, 2, 3, 0]])
        b = np.array([[3, 1, 2, 5, 3, 4, 7], [3, 3, 3, 1, 9, 9, 9]])
        ma, ia = setmembership.ismember_rows(a, b)

        ma_known = np.array([1, 1, 1, 1, 0], dtype=bool)
        ia_known = np.array([1, 0, 2, 1])

        assert np.allclose(ma, ma_known)
        assert np.allclose(ia, ia_known)
示例#11
0
    def test_ismember_rows_double_occurence_a_no_b(self):
        # There are duplicate occurences in a that are not found in b
        a = np.array([[1, 3, 3, 1, 7], [3, 3, 2, 3, 0]])
        b = np.array([[3, 2, 5], [3, 3, 1]])
        ma, ia = setmembership.ismember_rows(a, b)

        ma_known = np.array([0, 1, 1, 0, 0], dtype=bool)
        ia_known = np.array([0, 1])

        assert np.allclose(ma, ma_known)
        assert np.allclose(ia, ia_known)
示例#12
0
    def test_ismember_rows_double_occurence_a_and_b(self):
        # There are duplicate occurences in a, and the same item is found in b
        a = np.array([[1, 3, 3, 1, 7], [3, 3, 2, 3, 0]])
        b = np.array([[3, 1, 2, 5, 3], [3, 3, 3, 1, 1]])
        ma, ia = setmembership.ismember_rows(a, b)

        ma_known = np.array([1, 1, 1, 1, 0], dtype=bool)
        ia_known = np.array([1, 0, 2, 1])

        assert np.allclose(ma, ma_known)
        assert np.allclose(ia, ia_known)
示例#13
0
    def test_ismember_rows_1d(self):
        a = np.array([0, 2, 1, 3, 0])
        b = np.array([2, 4, 3])

        ma, ia = setmembership.ismember_rows(a, b)

        ma_known = np.array([0, 1, 0, 1, 0], dtype=bool)
        ia_known = np.array([0, 2])

        assert np.allclose(ma, ma_known)
        assert np.allclose(ia, ia_known)
示例#14
0
    def duplicate_without_dimension(
        self, dim: int
    ) -> Tuple["GridBucket", Dict[str, Dict]]:
        """
        Remove all the nodes of dimension dim and add new edges between their
        neighbors by calls to remove_node.

        Parameters:
            dim (int): Dimension for which all grids should be removed.

        Returns:
            pp.GridBucket: Copy of this GridBucket, with all grids of dimension dim
                removed, and new edges between the neighbors of removed grids.
            Dict: Information on removed grids. Keys:
                "eliminated_nodes": List of all grids that were removed.
                "neighbours": List with neighbors of the eliminated grids. Sorted in
                    the same order as eliminated_nodes. Node ordering is updated.
                "neigbours_old": Same as neighbours, but with the original node
                    ordering.

        """

        gb_copy = self.copy()
        grids_of_dim = gb_copy.grids_of_dimension(dim)
        grids_of_dim_old = self.grids_of_dimension(dim)
        # The node numbers are copied for each grid, so they can be used to
        # make sure we use the same grids (g and g_old) below.
        nn_new = [gb_copy.node_props(g, "node_number") for g in grids_of_dim]
        nn_old = [self.node_props(g, "node_number") for g in grids_of_dim_old]
        _, old_in_new = setmembership.ismember_rows(
            np.array(nn_new), np.array(nn_old), sort=False
        )
        neighbours_dict = {}
        neighbours_dict_old = {}
        eliminated_nodes = {}
        for i, g in enumerate(grids_of_dim):
            # Eliminate the node and add new gb edges:
            neighbours = gb_copy.eliminate_node(g)
            # Keep track of which nodes were connected to each of the eliminated
            # nodes. Note that the key is the node number in the old gb, whereas
            # the neighbours lists refer to the copy grids.
            g_old = grids_of_dim_old[old_in_new[i]]
            neighbours_dict[i] = neighbours
            neighbours_dict_old[i] = self.node_neighbors(g_old)
            eliminated_nodes[i] = g_old

        elimination_data = {
            "neighbours": neighbours_dict,
            "neighbours_old": neighbours_dict_old,
            "eliminated_nodes": eliminated_nodes,
        }

        return gb_copy, elimination_data  # type: ignore
示例#15
0
    def test_issue_123(self):
        # Reported bug #123.
        # Referred to an implementation of ismember which is now replaced.
        a = np.array([[0., 1., 1., 0., 0.2, 0.8, 0.5, 0.5, 0.5],
                      [0., 0., 1., 1., 0.5, 0.5, 0.8, 0.2, 0.5]])
        b = np.array([[0, 1, 1, 0], [0, 0, 1, 1]])
        ma, ia = setmembership.ismember_rows(a, b)

        ma_known = np.array([1, 1, 1, 1, 0, 0, 0, 0, 0], dtype=bool)
        ia_known = np.array([0, 1, 2, 1])

        assert np.allclose(ma, ma_known)
        assert np.allclose(ia, ia_known)
示例#16
0
    def target_2_source_nodes(self, g_src, g_trg):
        """
        Find the local node mapping from a source grid to a target grid.

        target_2_source_nodes(..) returns the mapping from g_src -> g_trg such
        that g_trg.nodes[:, map] == g_src.nodes. E.g., if the target grid is the
        highest dim grid, target_2_source_nodes will equal the global node
        numbering.

        """
        node_source = np.atleast_2d(g_src.global_point_ind)
        node_target = np.atleast_2d(g_trg.global_point_ind)
        _, trg_2_src_nodes = setmembership.ismember_rows(
            node_source.astype(np.int32), node_target.astype(np.int32))
        return trg_2_src_nodes
示例#17
0
def update_global_point_ind(grid_list, old_ind, new_ind):
    """Update global point indices in a list of grids.

    The method replaces indices in the attribute global_point_ind in the grid.
    The update is done in place.

    Parameters:
        grid_list (list of grids): Grids to be updated.
        old_ind (np.array): Old global indices, to be replaced.
        new_ind (np.array): New indices.

    """
    for g in grid_list:
        ismem, o2n = ismember_rows(old_ind, g.global_point_ind)
        g.global_point_ind[o2n] = new_ind[ismem]
示例#18
0
def obtain_interdim_mappings(lg,
                             fn,
                             n_per_face,
                             ensure_matching_face_cell=True,
                             **kwargs):
    """
    Find mappings between faces in higher dimension and cells in the lower
    dimension

    Parameters:
        lg: Lower dimensional grid.
        fn: Higher dimensional face-node relation.
        n_per_face: Number of nodes per face in the higher-dimensional grid.
        ensure_matching_face_cell: Boolean, defaults to True. If True, an
            assertion is made that all lower-dimensional cells corresponds to a
            higher dimensional cell.

    """
    if lg.dim > 0:
        cn_loc = lg.cell_nodes().indices.reshape((n_per_face, lg.num_cells),
                                                 order='F')
        cn = lg.global_point_ind[cn_loc]
        cn = np.sort(cn, axis=0)
    else:
        cn = np.array([lg.global_point_ind])
        # We also know that the higher-dimensional grid has faces
        # of a single node. This sometimes fails, so enforce it.
        if cn.ndim == 1:
            fn = fn.ravel()
    is_mem, cell_2_face = setmembership.ismember_rows(cn.astype(np.int32),
                                                      fn.astype(np.int32),
                                                      sort=False)
    # An element in cell_2_face gives, for all cells in the
    # lower-dimensional grid, the index of the corresponding face
    # in the higher-dimensional structure.
    if not (np.all(is_mem) or np.all(~is_mem)):
        if ensure_matching_face_cell:
            raise ValueError(
                '''Either all cells should have a corresponding face in a higher
            dim grid or no cells should have a corresponding face in a higher
            dim grid. This likely is related to gmsh behavior. ''')
        else:
            warnings.warn('''Found inconsistency between cells and higher
                          dimensional faces. Continuing, fingers crossed''')
    low_dim_cell = np.where(is_mem)[0]
    return cell_2_face, low_dim_cell
def compare_discretizations(
    g_1,
    lhs_0,
    lhs_1,
    rhs_0,
    rhs_1,
    cell_maps,
    face_maps,
    phys="flow",
    fractured_mpsa=False,
):
    """
    Assumes dofs sorted as cell_maps. Not neccessarily true for multiple
    fractures.
    """
    if fractured_mpsa:
        # The dofs are at the cell centers
        dof_map_cells = fvutils.expand_indices_nd(cell_maps[0], g_1.dim)

        # and faces on either side of the fracture. Find the order of the g_1
        # frac faces among the g_0 frac faces.
        frac_faces_loc = sm.ismember_rows(face_maps[0],
                                          g_1.frac_pairs.ravel("C"),
                                          sort=False)[1]
        # And expand to the dofs, one for each dimension for each face. For two
        # faces f0 and f1 in 3d, the order is
        # u(f0). v(f0), w(f0), u(f1). v(f1), w(f1)
        frac_indices = fvutils.expand_indices_nd(frac_faces_loc, g_1.dim)
        # Account for the cells
        frac_indices += g_1.num_cells * g_1.dim
        global_dof_map = np.concatenate((dof_map_cells, frac_indices))
    elif phys == "mechanics":
        global_dof_map = fvutils.expand_indices_nd(cell_maps[0], g_1.dim)
        global_dof_map = np.array(global_dof_map, dtype=int)
    else:
        global_dof_map = np.concatenate(
            (cell_maps[0], cell_maps[1] + cell_maps[0].size))
    mapped_lhs = lhs_1[global_dof_map][:, global_dof_map]
    assert np.isclose(np.sum(np.absolute(lhs_0 - mapped_lhs)), 0)
    assert np.all(np.isclose(rhs_0, rhs_1[global_dof_map]))
示例#20
0
    def duplicate_without_dimension(self, dim):
        """
        Remove all the nodes of dimension dim and add new edges between their
        neighbors by calls to remove_node.

        """

        gb_copy = self.copy()
        grids_of_dim = gb_copy.grids_of_dimension(dim)
        grids_of_dim_old = self.grids_of_dimension(dim)
        # The node numbers are copied for each grid, so they can be used to
        # make sure we use the same grids (g and g_old) below.
        nn_new = gb_copy.nodes_prop(grids_of_dim, 'node_number')
        nn_old = self.nodes_prop(grids_of_dim_old, 'node_number')
        _, old_in_new = setmembership.ismember_rows(np.array(nn_new),
                                                    np.array(nn_old),
                                                    sort=False)
        neighbours_dict = {}
        neighbours_dict_old = {}
        eliminated_nodes = {}
        for i, g in enumerate(grids_of_dim):
            # Eliminate the node and add new gb edges:
            neighbours = gb_copy.eliminate_node(g)
            # Keep track of which nodes were connected to each of the eliminated
            # nodes. Note that the key is the node number in the old gb, whereas
            # the neighbours lists refer to the copy grids.
            g_old = grids_of_dim_old[old_in_new[i]]
            neighbours_dict[i] = neighbours
            neighbours_dict_old[i] = self.node_neighbors(g_old)
            eliminated_nodes[i] = g_old

        elimination_data = {
            'neighbours': neighbours_dict,
            'neighbours_old': neighbours_dict_old,
            'eliminated_nodes': eliminated_nodes
        }

        return gb_copy, elimination_data
示例#21
0
def triangle_grid(points, edges, domain, constraints=None, **kwargs):
    """
    Generate a gmsh grid in a 2D domain with fractures.

    The function uses modified versions of pygmsh and mesh_io,
    both downloaded from github.

    To be added:
    Functionality for tuning gmsh, including grid size, refinements, etc.

    Parameters
    ----------
    fracs: (dictionary) Two fields: points (2 x num_points) np.ndarray,
        edges (2 x num_lines) connections between points, defines fractures.
    box: (dictionary) keys xmin, xmax, ymin, ymax, [together bounding box
        for the domain]
    constraints (np.array, optional): Index of edges that only act as constraints
        in meshing, but do not generate lower-dimensional grids.
    **kwargs: To be explored.

    Returns
    -------
    list (length 3): For each dimension (2 -> 0), a list of all grids in
        that dimension.

    Examples
    p = np.array([[-1, 1, 0, 0], [0, 0, -1, 1]])
    lines = np.array([[0, 2], [1, 3]])
    char_h = 0.5 * np.ones(p.shape[1])
    tags = np.array([1, 3])
    fracs = {'points': p, 'edges': lines}
    box = {'xmin': -2, 'xmax': 2, 'ymin': -2, 'ymax': 2}
    g = triangle_grid(fracs, box)

    """
    logger.info("Create 2d mesh")

    # Unified description of points and lines for domain, and fractures
    pts_all, lines, domain_pts = _merge_domain_fracs_2d(
        domain, points, edges, constraints)
    assert np.all(np.diff(lines[:2], axis=0) != 0)

    tol = kwargs.get("tol", 1e-4)
    pts_split, lines_split = _segment_2d_split(pts_all, lines, tol)

    # We find the end points that are shared by more than one intersection
    intersections = _find_intersection_points(lines_split)

    # Gridding size
    if "mesh_size_frac" in kwargs.keys():
        # Tag points at the domain corners
        logger.info("Determine mesh size")
        tm = time.time()
        boundary_pt_ind = ismember_rows(pts_split, domain_pts, sort=False)[0]
        mesh_size, pts_split, lines_split = tools.determine_mesh_size(
            pts_split, boundary_pt_ind, lines_split, **kwargs)

        logger.info("Done. Elapsed time " + str(time.time() - tm))
    else:
        mesh_size = None

    # gmsh options

    meshing_algorithm = kwargs.get("meshing_algorithm")
    # Create a writer of gmsh .geo-files
    gw = gmsh_interface.GmshWriter(
        pts_split,
        lines_split,
        domain=domain,
        mesh_size=mesh_size,
        intersection_points=intersections,
        meshing_algorithm=meshing_algorithm,
    )

    # File name for communication with gmsh
    file_name = kwargs.get("file_name", "gmsh_frac_file")
    kwargs.pop("file_name", str())

    in_file = file_name + ".geo"

    gw.write_geo(in_file)

    _run_gmsh(file_name, **kwargs)
    return triangle_grid_from_gmsh(file_name, **kwargs)
示例#22
0
    def test_merge_three_grids_no_common_point(self):
        # Merge three grids: One in the mid
        data = np.ones(3)
        rows = np.array([0, 1, 2])
        cols = np.array([0, 0, 0])
        cf_1 = sps.coo_matrix((data, (rows, cols)))

        data = np.ones(6)
        rows = np.array([0, 1, 2, 1, 3, 4])
        cols = np.array([0, 0, 0, 1, 1, 1])
        cf_2 = sps.coo_matrix((data, (rows, cols)))

        data = np.ones(6)
        rows = np.array([0, 1, 1, 2, 2, 0])
        cols = np.array([0, 0, 1, 1, 2, 2])
        fn_1 = sps.coo_matrix((data, (rows, cols)))

        data = np.ones(10)
        rows = np.array([0, 1, 1, 3, 3, 0, 1, 2, 2, 3])
        cols = np.array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4])
        fn_2 = sps.coo_matrix((data, (rows, cols)))

        nodes_1 = np.array([[0, 1, 1, 0], [0, 0, 1, 1], [0, 0, 0, 0]])
        nodes_2 = np.array([[0, 1, 0], [0, 0, -1], [0, 0, 0]])
        nodes_3 = np.array([[0, 1, 0], [2, 1, 1], [0, 0, 0]])

        # Middle grid, unit square divided into two. Will have neighbors on top and
        # bottom.
        g1 = MockGrid(2,
                      num_faces=5,
                      face_nodes=fn_2,
                      cell_faces=cf_2,
                      num_cells=2,
                      nodes=nodes_1)
        # Neighbor on bottom
        g2 = MockGrid(2,
                      num_faces=3,
                      face_nodes=fn_1,
                      cell_faces=cf_1,
                      num_cells=1,
                      nodes=nodes_2)
        # Neighbor on top.
        g3 = MockGrid(2,
                      num_faces=3,
                      face_nodes=fn_1,
                      cell_faces=cf_1,
                      num_cells=1,
                      nodes=nodes_3)

        # Bottom 1d grid, as seen from g1
        g_11 = TensorGrid(np.array([0, 1]))
        g_11.global_point_ind = np.arange(2)
        # Top 1d grid, as seen from g1
        g_13 = TensorGrid(np.array([0, 1]))
        g_13.nodes = np.array([[0, 1], [1, 1], [0, 0]])
        # Note global point indices here, in accordance with the ordering in
        # nodes_1
        g_13.global_point_ind = np.array([2, 3])

        # Bottom 1d grid, as seen from g2
        g_22 = TensorGrid(np.array([0, 1]))
        g_22.global_point_ind = np.arange(2)
        # Top 1d grid, as seen from g3
        g_33 = TensorGrid(np.array([1, 2]))
        g_33.nodes = np.array([[0, 1], [1, 1], [0, 0]])
        # Global point indices, as ordered in nodes_3
        g_33.global_point_ind = np.array([1, 2])

        gl = [[[g1], [g_11, g_13]], [[g2], [g_22]], [[g3], [g_33]]]
        intersections = [np.array([1, 2]), np.array([0]), np.array([0])]

        list_of_grids, glob_ind = non_conforming.init_global_ind(gl)
        grid_list_1d = non_conforming.process_intersections(gl,
                                                            intersections,
                                                            glob_ind,
                                                            list_of_grids,
                                                            tol=1e-4)
        assert len(grid_list_1d) == 2

        g_1d = grid_list_1d[0]
        ismem, maps = ismember_rows(g_1d.global_point_ind, g1.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g1.nodes[:, maps], g_1d.nodes)
        ismem, maps = ismember_rows(g_1d.global_point_ind, g2.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g2.nodes[:, maps], g_1d.nodes)

        g_1d = grid_list_1d[1]
        ismem, maps = ismember_rows(g_1d.global_point_ind, g1.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g1.nodes[:, maps], g_1d.nodes)
        ismem, maps = ismember_rows(g_1d.global_point_ind, g3.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g3.nodes[:, maps], g_1d.nodes)
示例#23
0
    def test_merge_three_grids_hanging_node_shared_node(self):
        # Merge three grids, where a central cell share one face each with the two
        # other. Importantly, one node will be involved in both shared faces.
        data = np.ones(10)
        rows = np.array([0, 1, 1, 2, 2, 3, 3, 0, 3, 1])
        cols = np.array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4])
        fn_1 = sps.coo_matrix((data, (rows, cols)))

        data = np.ones(6)
        rows = np.array([0, 1, 1, 2, 2, 0])
        cols = np.array([0, 0, 1, 1, 2, 2])
        fn_2 = sps.coo_matrix((data, (rows, cols)))

        data = np.ones(6)
        rows = np.array([0, 4, 3, 1, 2, 4])
        cols = np.array([0, 0, 0, 1, 1, 1])
        cf_1 = sps.coo_matrix((data, (rows, cols)))

        data = np.ones(3)
        rows = np.array([0, 1, 2])
        cols = np.array([0, 0, 0])
        cf_2 = sps.coo_matrix((data, (rows, cols)))

        nodes_1 = np.array([[0, 1, 2, 1], [0, 0, 0, 1], [0, 0, 0, 0]])
        nodes_2 = np.array([[0, 2, 1], [0, 0, -1], [0, 0, 0]])
        nodes_3 = np.array([[0, 1, 0], [0, 1, 1], [0, 0, 0]])
        # Central grid
        g1 = MockGrid(2,
                      num_faces=5,
                      face_nodes=fn_1,
                      cell_faces=cf_1,
                      num_cells=2,
                      nodes=nodes_1)
        # First neighboring grid
        g2 = MockGrid(2,
                      num_faces=3,
                      face_nodes=fn_2,
                      cell_faces=cf_2,
                      num_cells=1,
                      nodes=nodes_2)
        g3 = MockGrid(2,
                      num_faces=3,
                      face_nodes=fn_2,
                      cell_faces=cf_2,
                      num_cells=1,
                      nodes=nodes_3)

        # First 1d grid, as seen from g1
        g_11 = TensorGrid(np.array([0, 1, 2]))
        g_11.global_point_ind = np.arange(3)
        # Second 1d grid, as seen from g1
        g_13 = TensorGrid(np.array([0, 1]))
        g_13.nodes = np.array([[0, 1], [0, 1], [0, 0]])
        # Point indices adjusted according to ordering in nodes_1
        g_13.global_point_ind = np.array([0, 3])

        # First 1d grid, as seen from g2
        g_22 = TensorGrid(np.array([0, 2]))
        g_22.global_point_ind = np.arange(2)
        # Second 1d grid, as seen from g3
        g_33 = TensorGrid(np.array([0, 1]))
        g_33.nodes = np.array([[0, 1], [0, 1], [0, 0]])
        g_33.global_point_ind = np.arange(2)

        gl = [[[g1], [g_11, g_13]], [[g2], [g_22]], [[g3], [g_33]]]
        intersections = [np.array([1, 2]), np.array([0]), np.array([0])]

        list_of_grids, glob_ind = non_conforming.init_global_ind(gl)
        grid_list_1d = non_conforming.process_intersections(gl,
                                                            intersections,
                                                            glob_ind,
                                                            list_of_grids,
                                                            tol=1e-4)
        assert len(grid_list_1d) == 2

        g_1d = grid_list_1d[0]
        ismem, maps = ismember_rows(g_1d.global_point_ind, g1.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g1.nodes[:, maps], g_1d.nodes)
        ismem, maps = ismember_rows(g_1d.global_point_ind, g2.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g2.nodes[:, maps], g_1d.nodes)

        g_1d = grid_list_1d[1]
        ismem, maps = ismember_rows(g_1d.global_point_ind, g1.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g1.nodes[:, maps], g_1d.nodes)
        ismem, maps = ismember_rows(g_1d.global_point_ind, g3.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(g3.nodes[:, maps], g_1d.nodes)
示例#24
0
    def test_merge_three_grids_internal_intersection_no_hanging_node_reverse_order(
            self):
        # Merge three grids, where a central cell share one face each with the two
        # other. Importantly, one node will be involved in both shared faces.
        data = np.ones(16)
        rows = np.array([0, 1, 1, 2, 0, 3, 0, 4, 1, 3, 1, 4, 2, 3, 2, 4])
        cols = np.array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7])
        fn = sps.coo_matrix((data, (rows, cols)))

        data = np.ones(12)
        rows = np.array([0, 5, 3, 1, 7, 5, 0, 2, 4, 1, 4, 6])
        cols = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3])
        cf = sps.coo_matrix((data, (rows, cols)))

        nodes_1 = np.array([[-1, 0, 1, 0, 0], [0, 0, 0, -1, 1],
                            [0, 0, 0, 0, 0]])
        nodes_2 = np.array([[-1, 0, 1, 0, 0], [0, 0, 0, 0, 0],
                            [0, 0, 0, -1, 1]])
        nodes_3 = np.array([[0, 0, 0, 0, 0], [-1, 0, 1, 0, 0],
                            [0, 0, 0, -1, 1]])
        # Central grid
        gxy = MockGrid(2,
                       num_faces=8,
                       face_nodes=fn,
                       cell_faces=cf,
                       num_cells=4,
                       nodes=nodes_1)
        # First neighboring grid
        gxz = MockGrid(2,
                       num_faces=8,
                       face_nodes=fn,
                       cell_faces=cf,
                       num_cells=4,
                       nodes=nodes_2)
        gyz = MockGrid(2,
                       num_faces=8,
                       face_nodes=fn,
                       cell_faces=cf,
                       num_cells=4,
                       nodes=nodes_3)

        # First 1d grid, as seen from g1
        g_1x = TensorGrid(np.array([0, 1, 2]))
        g_1x.nodes = np.array([[1, 0, -1], [0, 0, 0], [0, 0, 0]])
        g_1x.global_point_ind = np.array([2, 1, 0])
        #g_1x.face_nodes.indices = np.array([1, 2, 0])
        # Third 1d grid, as seen from g1
        g_1y = TensorGrid(np.array([0, 1, 2]))
        g_1y.nodes = np.array([[0, 0, 0], [-1, 0, 1], [0, 0, 0]])
        # Point indices adjusted according to ordering in nodes_1
        g_1y.global_point_ind = np.array([3, 1, 4])

        # First 1d grid, as seen from g2
        g_2x = TensorGrid(np.array([0, 1, 2]))
        g_2x.nodes = np.array([[-1, 0, 1], [0, 0, 0], [0, 0, 0]])
        g_2x.global_point_ind = np.arange(3)
        # Third 1d grid, as seen from g2
        g_2z = TensorGrid(np.array([0, 1, 2]))
        g_2z.nodes = np.array([[0, 0, 0], [0, 0, 0], [-1, 0, 1]])
        # Point indices adjusted according to ordering in nodes_1
        g_2z.global_point_ind = np.array([3, 1, 4])

        g_3y = TensorGrid(np.array([0, 1, 2]))
        g_3y.nodes = np.array([[0, 0, 0], [-1, 0, 1], [0, 0, 0]])
        g_3y.global_point_ind = np.arange(3)
        # Second 1d grid, as seen from g1
        g_3z = TensorGrid(np.array([0, 1, 2]))
        g_3z.nodes = np.array([[0, 0, 0], [0, 0, 0], [-1, 0, 1]])
        # Point indices adjusted according to ordering in nodes_1
        g_3z.global_point_ind = np.array([3, 1, 4])

        gl = [[[gxy], [g_1x, g_1y]], [[gxz], [g_2x, g_2z]],
              [[gyz], [g_3y, g_3z]]]
        intersections = [np.array([1, 2]), np.array([0, 2]), np.array([0, 1])]

        list_of_grids, glob_ind = non_conforming.init_global_ind(gl)
        grid_list_1d = non_conforming.process_intersections(gl,
                                                            intersections,
                                                            glob_ind,
                                                            list_of_grids,
                                                            tol=1e-4)
        assert len(grid_list_1d) == 3

        g_1d = grid_list_1d[0]
        ismem, maps = ismember_rows(g_1d.global_point_ind,
                                    gxy.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(gxy.nodes[:, maps], g_1d.nodes)
        ismem, maps = ismember_rows(g_1d.global_point_ind,
                                    gxz.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(gxz.nodes[:, maps], g_1d.nodes)

        g_1d = grid_list_1d[1]
        ismem, maps = ismember_rows(g_1d.global_point_ind,
                                    gxy.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(gxy.nodes[:, maps], g_1d.nodes)
        ismem, maps = ismember_rows(g_1d.global_point_ind,
                                    gyz.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(gyz.nodes[:, maps], g_1d.nodes)

        g_1d = grid_list_1d[2]
        ismem, maps = ismember_rows(g_1d.global_point_ind,
                                    gxz.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(gxz.nodes[:, maps], g_1d.nodes)

        ismem, maps = ismember_rows(g_1d.global_point_ind,
                                    gyz.global_point_ind)
        assert ismem.sum() == g_1d.num_nodes
        assert np.allclose(gyz.nodes[:, maps], g_1d.nodes)
示例#25
0
def update_nodes(
    g, g_1d, new_grid_1d, this_in_combined, sort_ind, global_ind_offset, list_of_grids
):
    """Update a 2d grid to conform to a new grid along a 1d line.

    Intended use: A 1d mesh that is embedded in a 2d mesh (along a fracture)
    has been updated / refined. This function then updates the node information
    in the 2d grid.

    Parameters:
        g (grid, dim==2): Main grid to update. Has faces along a fracture.
        g_1d (grid, dim==1): Original line grid along the fracture.
        new_grid_1d (grid, dim==1): New line grid, formed by merging two
            coinciding, but non-matching grids along a fracture.
        this_in_combined (np.ndarray): Which nodes in the new grid are also in
            the old one, as returned by merge_1d_grids().
        sort_ind (np.ndarray): Sorting indices of the coordinates of the old
            grid, as returned by merge_1d_grids().
        list_of_grids (list): Grids for all dimensions.

    Returns:


    """
    nodes_per_face = 2
    # Face-node relation for the grid, in terms of local and global indices
    fn = g.face_nodes.indices.reshape((nodes_per_face, g.num_faces), order="F")
    fn_glob = np.sort(g.global_point_ind[fn], axis=0)

    # Mappings between faces in 2d grid and cells in 1d
    # 2d faces along the 1d grid will be deleted.
    delete_faces, cell_1d = fractools.obtain_interdim_mappings(
        g_1d, fn_glob, nodes_per_face
    )

    # All 1d cells should be identified with 2d faces
    assert (
        cell_1d.size == g_1d.num_cells
    ), """ Failed to find mapping between
        1d cells and 2d faces"""

    # The nodes of identified faces on the 2d grid will be deleted
    delete_nodes = np.unique(fn[:, delete_faces])

    # Nodes to be added will have indicies towards the end
    num_nodes_orig = g.num_nodes
    num_delete_nodes = delete_nodes.size

    # Define indices of new nodes.
    new_nodes = num_nodes_orig - delete_nodes.size + np.arange(new_grid_1d.num_nodes)

    # Adjust node indices in the face-node relation
    # First, map nodes between 1d and 2d grids. Use sort_ind here to map
    # indices of g_1d to the same order as the new grid
    _, node_map_1d_2d = ismember_rows(
        g.global_point_ind[delete_nodes], g_1d.global_point_ind
    )
    tmp = np.arange(g.num_nodes)
    adjustment = np.zeros_like(tmp)
    adjustment[delete_nodes] = 1
    node_adjustment = tmp - np.cumsum(adjustment)
    # Nodes along the 1d grid are deleted and inserted again. Let the
    # adjutsment point to the restored nodes.
    # node_map_1d_2d maps from ordering in delete_nodes to ordering of 1d
    # points (old_grid). this_in_combined then maps further to the ordering of
    # the new 1d grid
    node_adjustment[delete_nodes] = (
        g.num_nodes - num_delete_nodes + this_in_combined[node_map_1d_2d]
    )

    g.face_nodes.indices = node_adjustment[g.face_nodes.indices]

    # Update node coordinates and global indices for 2d mesh
    g.nodes = np.hstack((g.nodes, new_grid_1d.nodes))

    new_global_points = new_grid_1d.global_point_ind
    g.global_point_ind = np.append(g.global_point_ind, new_global_points)

    # Global index of deleted points
    old_global_pts = g.global_point_ind[delete_nodes]

    # Update any occurences of the old points in other grids. When sewing
    # together a DFN grid, this may involve more and more updates as common
    # nodes are found along intersections.

    # The new grid should also be added to the list, if it is not there before
    if new_grid_1d not in list_of_grids:
        list_of_grids.append(new_grid_1d)
    update_global_point_ind(
        list_of_grids,
        old_global_pts,
        new_global_points[this_in_combined[node_map_1d_2d]],
    )

    # Delete old nodes
    g.nodes = np.delete(g.nodes, delete_nodes, axis=1)
    g.global_point_ind = np.delete(g.global_point_ind, delete_nodes)

    g.num_nodes = g.nodes.shape[1]
    return new_nodes, delete_faces, global_ind_offset
示例#26
0
def merge_1d_grids(g, h, global_ind_offset=0, tol=1e-4):
    """Merge two 1d grids with non-matching nodes to a single grid.

    The grids should have common start and endpoints. They can be into 3d space
    in a genreal way.

    The function is primarily intended for merging non-conforming DFN grids.

    Parameters:
        g: 1d tensor grid.
        h: 1d tensor grid
        glob_ind_offset (int, defaults to 0): Off set for the global point
            index of the new grid.
        tol (double, defaults to 1e-4): Tolerance for when two nodes are merged
            into one.

    Returns:
        TensorGrid: New tensor grid, containing the combined node definition.
        int: New global ind offset, increased by the number of cells in the
            combined grid.
        np.array (int): Indices of common nodes (after sorting) of g and the
            new grid.
        np.array (int): Indices of common nodes (after sorting) of h and the
            new grid.
        np.array (int): Permutation indices that sort the node coordinates of
            g. The common indices between g and the new grid are found as
            new_grid.nodes[:, g_in_combined] = g.nodes[:, sorted]
        np.array (int): Permutation indices that sort the node coordinates of
            h. The common indices between h and the new grid are found as
            new_grid.nodes[:, h_in_combined] = h.nodes[:, sorted]

    """

    # Nodes of the two 1d grids, combine them
    gp = g.nodes
    hp = h.nodes
    combined = np.hstack((gp, hp))

    num_g = gp.shape[1]
    num_h = hp.shape[1]

    # Keep track of where we put the indices of the original grids
    g_in_full = np.arange(num_g)
    h_in_full = num_g + np.arange(num_h)

    # The tolerance should not be larger than the smallest distance between
    # two points on any of the grids.
    diff_gp = np.min(pp.distances.pointset(gp, True))
    diff_hp = np.min(pp.distances.pointset(hp, True))
    min_diff = np.minimum(tol, 0.5 * np.minimum(diff_gp, diff_hp))

    # Uniquify points
    combined_unique, _, new_2_old = unique_columns_tol(combined, tol=min_diff)
    # Follow locations of the original grid points
    g_in_unique = new_2_old[g_in_full]
    h_in_unique = new_2_old[h_in_full]

    # The combined nodes must be sorted along their natural line.
    # Find the dimension with the largest spatial extension, and sort those
    # coordinates
    max_coord = combined_unique.max(axis=1)
    min_coord = combined_unique.min(axis=1)
    dx = max_coord - min_coord
    sort_dim = np.argmax(dx)

    sort_ind = np.argsort(combined_unique[sort_dim])
    combined_sorted = combined_unique[:, sort_ind]

    # Follow the position of the orginial nodes through sorting
    _, g_sorted = ismember_rows(g_in_unique, sort_ind)
    _, h_sorted = ismember_rows(h_in_unique, sort_ind)

    num_new_grid = combined_sorted.shape[1]

    # Create a new 1d grid.
    # First use a 1d coordinate to initialize topology
    new_grid = pp.TensorGrid(np.arange(num_new_grid))
    # Then set the right, 3d coordinates
    new_grid.nodes = pp.map_geometry.force_point_collinearity(combined_sorted)

    # Set global point indices
    new_grid.global_point_ind = global_ind_offset + np.arange(num_new_grid)
    global_ind_offset += num_new_grid

    return (
        new_grid,
        global_ind_offset,
        g_sorted,
        h_sorted,
        np.arange(num_g),
        np.arange(num_h),
    )
def check_equivalent_buckets(buckets, decimals=12):
    """
    Checks agreement between number of cells, faces and nodes, their
    coordinates and the connectivity matrices cell_faces and face_nodes. Also
    checks the face tags.

    """
    dim_h = buckets[0].dim_max()
    dim_l = dim_h - 1
    num_buckets = len(buckets)
    cell_maps_h, face_maps_h = [], []
    cell_maps_l, face_maps_l = num_buckets * [{}], num_buckets * [{}]

    # Check that all buckets have the same number of grids in the lower dimension
    num_grids_l: int = len(buckets[0].grids_of_dimension(dim_h - 1))
    for bucket in buckets:
        assert len(bucket.grids_of_dimension(dim_h - 1)) == num_grids_l

    for d in range(dim_l, dim_h + 1):
        for target_grid in range(len(buckets[0].grids_of_dimension(d))):

            n_cells, n_faces, n_nodes = np.empty(0), np.empty(0), np.empty(0)
            nodes, face_centers, cell_centers = [], [], []
            cell_faces, face_nodes = [], []
            for bucket in buckets:
                g = bucket.grids_of_dimension(d)[target_grid]
                n_cells = np.append(n_cells, g.num_cells)
                n_faces = np.append(n_faces, g.num_faces)
                n_nodes = np.append(n_nodes, g.num_nodes)
                cell_faces.append(g.cell_faces)
                face_nodes.append(g.face_nodes)
                cell_centers.append(g.cell_centers)
                face_centers.append(g.face_centers)
                nodes.append(g.nodes)

            # Check that all buckets have the same number of cells, faces and nodes
            assert np.unique(n_cells).size == 1
            assert np.unique(n_faces).size == 1
            assert np.unique(n_nodes).size == 1

            # Check that the coordinates agree
            cell_centers = np.round(cell_centers, decimals)
            nodes = np.round(nodes, decimals)
            face_centers = np.round(face_centers, decimals)
            for i in range(1, num_buckets):
                assert np.all(
                    sm.ismember_rows(cell_centers[0], cell_centers[i])[0])
                assert np.all(
                    sm.ismember_rows(face_centers[0], face_centers[i])[0])
                assert np.all(sm.ismember_rows(nodes[0], nodes[i])[0])

            # Now we know all nodes, faces and cells are in all grids, we map them
            # to prepare cell_faces and face_nodes comparison
            g_0 = buckets[0].grids_of_dimension(d)[target_grid]
            for i in range(1, num_buckets):
                bucket = buckets[i]
                g = bucket.grids_of_dimension(d)[target_grid]
                cell_map, face_map, node_map = make_maps(
                    g_0, g, bucket.dim_max())
                mapped_cf = g.cell_faces[face_map][:, cell_map]
                mapped_fn = g.face_nodes[node_map][:, face_map]

                assert np.sum(np.abs(g_0.cell_faces) != np.abs(mapped_cf)) == 0
                assert np.sum(np.abs(g_0.face_nodes) != np.abs(mapped_fn)) == 0
                if g.dim == dim_h:
                    face_maps_h.append(face_map)
                    cell_maps_h.append(cell_map)
                else:
                    cell_maps_l[i][g] = cell_map
                    face_maps_l[i][g] = face_map

                # Also loop on the standard face tags to check that they are
                # identical between the buckets.
                tag_keys = tags.standard_face_tags()
                for key in tag_keys:
                    assert np.all(
                        np.isclose(g_0.tags[key], g.tags[key][face_map]))

    # Mortar grids
    g_h_0 = buckets[0].grids_of_dimension(dim_h)[0]
    for target_grid in range(len(buckets[0].grids_of_dimension(dim_l))):
        g_l_0 = buckets[0].grids_of_dimension(dim_l)[target_grid]

        mg_0 = buckets[0].edge_props((g_h_0, g_l_0), "mortar_grid")
        proj_0 = mg_0.primary_to_mortar_int()
        for i in range(1, num_buckets):
            g_l_i = buckets[i].grids_of_dimension(dim_l)[target_grid]
            g_h_i = buckets[i].grids_of_dimension(dim_h)[0]
            mg_i = buckets[i].edge_props((g_h_i, g_l_i), "mortar_grid")
            proj_i = mg_i.primary_to_mortar_int()
            cm = cell_maps_l[i][g_l_i]
            cm_extended = np.append(cm, cm + cm.size)
            fm = face_maps_h[i - 1]
            mapped_fc = proj_i[cm_extended, :][:, fm]
            assert np.sum(np.absolute(proj_0) - np.absolute(mapped_fc)) == 0
    return cell_maps_h, cell_maps_l, face_maps_h, face_maps_l
示例#28
0
def split_fractures(bucket, **kwargs):
    """
    Wrapper function to split all fractures. For each grid in the bucket,
    we locate the corresponding lower-dimensional grids. The faces and
    nodes corresponding to these grids are then split, creating internal
    boundaries.

    Parameters
    ----------
    bucket    - A grid bucket
    **kwargs:
        offset    - FLOAT, defaults to 0. Will perturb the nodes around the
                    faces that are split. NOTE: this is only for visualization.
                    E.g., the face centers are not perturbed.

    Returns
    -------
    bucket    - A valid bucket where the faces are split at
                internal boundaries.


    Examples
    >>> import numpy as np
    >>> from gridding.fractured import meshing, split_grid
    >>> from viz.exporter import export_vtk
    >>>
    >>> f_1 = np.array([[-1, 1, 1, -1 ], [0, 0, 0, 0], [-1, -1, 1, 1]])
    >>> f_2 = np.array([[0, 0, 0, 0], [-1, 1, 1, -1 ], [-.7, -.7, .8, .8]])
    >>> f_set = [f_1, f_2]
    >>> domain = {'xmin': -2, 'xmax': 2,
            'ymin': -2, 'ymax': 2, 'zmin': -2, 'zmax': 2}
    >>> bucket = meshing.create_grid(f_set, domain)
    >>> [g.compute_geometry() for g,_ in bucket]
    >>>
    >>> split_grid.split_fractures(bucket, offset=0.1)
    >>> export_vtk(bucket, "grid")
    """

    offset = kwargs.get("offset", 0)

    # For each vertex in the bucket we find the corresponding lower-
    # dimensional grids.
    for gh, _ in bucket:
        # add new field to grid
        gh.frac_pairs = np.zeros((2, 0), dtype=np.int32)
        if gh.dim < 1:
            # Nothing to do. We can not split 0D grids.
            continue
        # Find connected vertices and corresponding edges.
        neigh = np.array(bucket.node_neighbors(gh))

        # Find the neighbours that are lower dimensional
        is_low_dim_grid = np.where([w.dim < gh.dim for w in neigh])
        edges = [(gh, w) for w in neigh[is_low_dim_grid]]
        if len(edges) == 0:
            # No lower dim grid. Nothing to do.
            continue

        face_cells = [bucket.edge_props(e, "face_cells") for e in edges]
        # We split all the faces that are connected to a lower-dim grid.
        # The new faces will share the same nodes and properties (normals,
        # etc.)
        face_cells = split_faces(gh, face_cells)

        for e, f in zip(edges, face_cells):
            bucket.add_edge_props("face_cells", [e])
            bucket.edge_props(e)["face_cells"] = f

        # We now find which lower-dim nodes correspond to which higher-
        # dim nodes. We split these nodes according to the topology of
        # the connected higher-dim cells. At a X-intersection we split
        # the node into four, while at the fracture boundary it is not split.

        gl = [e[1] for e in edges]
        gl_2_gh_nodes = []
        for g in gl:
            source = np.atleast_2d(g.global_point_ind).astype(np.int32)
            target = np.atleast_2d(gh.global_point_ind).astype(np.int32)
            _, mapping = setmembership.ismember_rows(source, target)
            gl_2_gh_nodes.append(mapping)

        split_nodes(gh, gl, gl_2_gh_nodes, offset)

    # Remove zeros from cell_faces

    [g.cell_faces.eliminate_zeros() for g, _ in bucket]
    [g.update_boundary_node_tag() for g, _ in bucket]
    return bucket
示例#29
0
def triangle_grid(fracs, domain, do_snap_to_grid=False, **kwargs):
    """
    Generate a gmsh grid in a 2D domain with fractures.

    The function uses modified versions of pygmsh and mesh_io,
    both downloaded from github.

    To be added:
    Functionality for tuning gmsh, including grid size, refinements, etc.

    Parameters
    ----------
    fracs: (dictionary) Two fields: points (2 x num_points) np.ndarray,
        edges (2 x num_lines) connections between points, defines fractures.
    box: (dictionary) keys xmin, xmax, ymin, ymax, [together bounding box
        for the domain]
    do_snap_to_grid (boolean, optional): If true, points are snapped to an
        underlying Cartesian grid with resolution tol before geometry
        computations are carried out. This used to be the standard, but
        indications are it is better not to do this. This keyword construct is
        a stop-gap measure to invoke the old functionality if desired. This
        option will most likely dissapear in the future.
    **kwargs: To be explored.

    Returns
    -------
    list (length 3): For each dimension (2 -> 0), a list of all grids in
        that dimension.

    Examples
    p = np.array([[-1, 1, 0, 0], [0, 0, -1, 1]])
    lines = np.array([[0, 2], [1, 3]])
    char_h = 0.5 * np.ones(p.shape[1])
    tags = np.array([1, 3])
    fracs = {'points': p, 'edges': lines}
    box = {'xmin': -2, 'xmax': 2, 'ymin': -2, 'ymax': 2}
    g = triangle_grid(fracs, box)

    """
    logger.info("Create 2d mesh")

    # Verbosity level
    verbose = kwargs.get("verbose", 1)

    # File name for communication with gmsh
    file_name = kwargs.get("file_name", "gmsh_frac_file")
    kwargs.pop("file_name", str())

    tol = kwargs.get("tol", 1e-4)

    in_file = file_name + ".geo"
    out_file = file_name + ".msh"

    # Pick out fracture points, and their connections
    frac_pts = fracs["points"]
    frac_con = fracs["edges"]

    # Unified description of points and lines for domain, and fractures
    pts_all, lines, domain_pts = __merge_domain_fracs_2d(
        domain, frac_pts, frac_con)

    # Snap to underlying grid before comparing points
    if do_snap_to_grid:
        pts_all = cg.snap_to_grid(pts_all, tol)

    assert np.all(np.diff(lines[:2], axis=0) != 0)

    # Ensure unique description of points
    pts_all, _, old_2_new = unique_columns_tol(pts_all, tol=tol)
    lines[:2] = old_2_new[lines[:2]]
    to_remove = np.where(lines[0, :] == lines[1, :])[0]
    lines = np.delete(lines, to_remove, axis=1)

    # In some cases the fractures and boundaries impose the same constraint
    # twice, although it is not clear why. Avoid this by uniquifying the lines.
    # This may disturb the line tags in lines[2], but we should not be
    # dependent on those.
    li = np.sort(lines[:2], axis=0)
    _, new_2_old, old_2_new = unique_columns_tol(li, tol=tol)
    lines = lines[:, new_2_old]

    assert np.all(np.diff(lines[:2], axis=0) != 0)

    # We split all fracture intersections so that the new lines do not
    # intersect, except possible at the end points
    logger.info("Remove edge crossings")
    tm = time.time()
    pts_split, lines_split = cg.remove_edge_crossings(pts_all,
                                                      lines,
                                                      tol=tol,
                                                      snap=do_snap_to_grid)
    logger.info("Done. Elapsed time " + str(time.time() - tm))

    # Ensure unique description of points
    if do_snap_to_grid:
        pts_split = cg.snap_to_grid(pts_split, tol)

    pts_split, _, old_2_new = unique_columns_tol(pts_split, tol=tol)
    lines_split[:2] = old_2_new[lines_split[:2]]
    to_remove = np.where(lines[0, :] == lines[1, :])[0]
    lines = np.delete(lines, to_remove, axis=1)

    # Remove lines with the same start and end-point.
    # This can be caused by L-intersections, or possibly also if the two
    # endpoints are considered equal under tolerance tol.
    remove_line_ind = np.where(np.diff(lines_split[:2], axis=0)[0] == 0)[0]
    lines_split = np.delete(lines_split, remove_line_ind, axis=1)

    # TODO: This operation may leave points that are not referenced by any
    # lines. We should probably delete these.

    # We find the end points that are shared by more than one intersection
    intersections = __find_intersection_points(lines_split)

    # Gridding size
    if "mesh_size_frac" in kwargs.keys():
        # Tag points at the domain corners
        logger.info("Determine mesh size")
        tm = time.time()
        boundary_pt_ind = ismember_rows(pts_split, domain_pts, sort=False)[0]
        mesh_size, pts_split, lines_split = tools.determine_mesh_size(
            pts_split, boundary_pt_ind, lines_split, **kwargs)

        logger.info("Done. Elapsed time " + str(time.time() - tm))
    else:
        mesh_size = None

    # gmsh options

    meshing_algorithm = kwargs.get("meshing_algorithm")

    # Create a writer of gmsh .geo-files
    gw = gmsh_interface.GmshWriter(
        pts_split,
        lines_split,
        domain=domain,
        mesh_size=mesh_size,
        intersection_points=intersections,
        meshing_algorithm=meshing_algorithm,
    )
    gw.write_geo(in_file)

    triangle_grid_run_gmsh(file_name, **kwargs)
    return triangle_grid_from_gmsh(file_name, **kwargs)