Exemple #1
0
    def test_single_point_and_segment(self):
        p = np.array([0, 0])
        start = np.array([1, 0])
        end = np.array([1, 1])

        d, cp = cg.dist_points_segments(p, start, end)

        self.assertTrue(d[0, 0] == 1)
        self.assertTrue(np.allclose(cp[0, 0, :], np.array([1, 0])))
Exemple #2
0
    def test_flipped_lines(self):
        p = np.array([0, 0])
        start = np.array([[1, 0], [1, 1]]).T
        end = np.array([[1, 1], [1, 0]])

        d, cp = cg.dist_points_segments(p, start, end)
        known_d = np.array([1, 1])
        self.assertTrue(np.allclose(d[0], known_d))

        known_cp = np.array([[[1, 0], [1, 0]]])
        self.assertTrue(np.allclose(cp, known_cp))
Exemple #3
0
    def test_point_closest_segment_end(self):
        p = np.array([0, 0])
        start = np.array([[1, 0], [1, 1]]).T
        end = np.array([[2, 0], [2, 1]]).T

        d, cp = cg.dist_points_segments(p, start, end)

        known_d = np.array([1, np.sqrt(2)])
        self.assertTrue(np.allclose(d[0], known_d))

        known_cp = np.array([[[1, 0], [1, 1]]])
        self.assertTrue(np.allclose(cp, known_cp))
Exemple #4
0
    def test_single_point_many_segments(self):
        p = np.array([1, 1])
        start = np.array([[0, 0], [0, 1], [0, 2]]).T
        end = np.array([[2, 0], [2, 1], [2, 2]]).T

        d, cp = cg.dist_points_segments(p, start, end)

        self.assertTrue(d.shape[0] == 1)
        self.assertTrue(d.shape[1] == 3)

        known_d = np.array([1, 0, 1])
        self.assertTrue(np.allclose(d[0], known_d))

        known_cp = np.array([[[1, 0], [1, 1], [1, 2]]])
        self.assertTrue(np.allclose(cp, known_cp))
Exemple #5
0
    def test_many_points_single_segment(self):
        p = np.array([[0, 1], [1, 1], [1.5, 1], [2, 1], [3, 1]]).T
        start = np.array([1, 0])
        end = np.array([2, 0])

        d, cp = cg.dist_points_segments(p, start, end)

        self.assertTrue(d.shape[0] == 5)
        self.assertTrue(d.shape[1] == 1)

        known_d = np.array([np.sqrt(2), 1, 1, 1, np.sqrt(2)]).reshape((-1, 1))
        self.assertTrue(np.allclose(d, known_d))

        known_cp = np.array([[[1, 0]], [[1, 0]], [[1.5, 0]], [[2, 0]], [[2, 0]]])
        self.assertTrue(np.allclose(cp, known_cp))
Exemple #6
0
    def test_many_points_and_segments(self):
        p = np.array([[0, 0, 0], [1, 0, 0]]).T

        start = np.array([[0, 0, -1], [0, 1, 1]]).T
        end = np.array([[0, 0, 1], [1, 1, 1]]).T

        d, cp = cg.dist_points_segments(p, start, end)

        self.assertTrue(d.shape[0] == 2)
        self.assertTrue(d.shape[1] == 2)

        known_d = np.array([[0, np.sqrt(2)], [1, np.sqrt(2)]])
        self.assertTrue(np.allclose(d, known_d))

        known_cp = np.array([[[0, 0, 0], [0, 1, 1]], [[0, 0, 0], [1, 1, 1]]])
        self.assertTrue(np.allclose(cp, known_cp))
Exemple #7
0
def _match_grids_along_line_from_geometry(mg, g_new, g_old, tol):

    # The purpose of this function is to construct a mapping between faces in
    # the old and new grid. Specifically, we need to match faces that lies on
    # the 1d segment identified by the mortar grid, and get the right area
    # weightings when the two grids do not conform.
    #
    # The algorithm is technical, partly because we also need to differ between
    # the left and right side of the segment, as these will belong to different
    # mortar grids.
    #
    # The main steps are:
    #   1) Identify faces in the old grid along the segment via the existing
    #      mapping between mortar grid and higher dimensional grid. Use this
    #      to define the geometry of the segment.
    #   2) Define positive and negative side of the segment, and split cells
    #      and faces along the segement according to this criterion.
    #   3) For all sides (pos, neg), pick out faces in the old and new grid,
    #      and match them up. Extend the mapping to go from all faces in the
    #      two grids.
    #
    # Known weak points: Identification of geometric objects, in particular
    # points, is based on a geometric tolerance. For very fine, or bad, grids
    # this may give trouble.

    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]

    def create_1d_from_nodes(nodes):
        # From a set of nodes, create a 1d grid. duplicate nodes are removed
        # and we verify that the nodes are indeed colinear
        assert cg.is_collinear(nodes, tol=tol)
        sort_ind = cg.argsort_point_on_line(nodes, tol=tol)
        n = nodes[:, sort_ind]
        unique_nodes, _, _ = unique_columns_tol(n, tol=tol)
        g = TensorGrid(np.arange(unique_nodes.shape[1]))
        g.nodes = unique_nodes
        g.compute_geometry()
        return g, sort_ind

    def nodes_of_faces(g, fi):
        # Find nodes of a set of faces.
        f = np.zeros(g.num_faces)
        f[fi] = 1
        nodes = np.where(g.face_nodes * f > 0)[0]
        return nodes

    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
        assert np.all(ismem)
        return ind

    # First create a virtual 1d grid along the line, using nodes from the old grid
    # Identify faces in the old grid that is on the boundary
    _, faces_on_boundary_old, _ = sps.find(mg.high_to_mortar_int)
    # Find the nodes of those faces
    nodes_on_boundary_old = nodes_of_faces(g_old, faces_on_boundary_old)
    nodes_1d_old = g_old.nodes[:, nodes_on_boundary_old]

    # Normal vector of the line. Somewhat arbitrarily chosen as the first one.
    # This may be prone to rounding errors.
    normal = g_old.face_normals[:, faces_on_boundary_old[0]].reshape((3, 1))

    # Create first version of 1d grid, we really only need start and endpoint
    g_aux, _ = create_1d_from_nodes(nodes_1d_old)

    # Start, end and midpoint
    start = g_aux.nodes[:, 0]
    end = g_aux.nodes[:, -1]
    mp = 0.5 * (start + end).reshape((3, 1))

    # Find cells in 2d close to the segment
    bound_cells_old = cells_from_faces(g_old, faces_on_boundary_old)
    # This may occur if the mortar grid is one sided (T-intersection)
    #    assert bound_cells_old.size > 1, 'Have not implemented this. Not difficult though'
    # Vector from midpoint to cell centers. Check which side the cells are on
    # relative to normal vector.
    # We are here assuming that the segment is not too curved (due to rounding
    # errors). Pain to come.
    cc_old = g_old.cell_centers[:, bound_cells_old]
    side_old = np.sign(np.sum(((cc_old - mp) * normal), axis=0))

    # Find cells on the positive and negative side, relative to the positioning
    # in cells_from_faces
    pos_side_old = np.where(side_old > 0)[0]
    neg_side_old = np.where(side_old < 0)[0]
    assert pos_side_old.size + neg_side_old.size == side_old.size

    both_sides_old = [pos_side_old, neg_side_old]

    # Then virtual 1d grid for the new grid. This is a bit more involved,
    # since we need to identify the nodes by their coordinates.
    # This part will be prone to rounding errors, in particular for
    # bad cell shapes.
    nodes_new = g_new.nodes

    # Represent the 1d line by its start and end point, as pulled
    # from the old 1d grid (known coordinates)
    # Find distance from the
    dist, _ = cg.dist_points_segments(nodes_new, start, end)
    # Look for points in the new grid with a small distance to the
    # line
    hit = np.argwhere(dist.ravel() < tol).reshape((1, -1))[0]

    # Depending on geometric tolerance and grid quality, hit
    # may contain nodes that are close to the 1d line, but not on it
    # To improve the results, also require that the faces are boundary faces

    # We know we are in 2d, thus all faces have two nodes
    # We can do the same trick in 3d, provided we have simplex grids
    # but this will fail on Cartesian or polyhedral grids
    fn = g_new.face_nodes.indices.reshape((2, g_new.num_faces), order="F")
    fn_in_hit = np.isin(fn, hit)
    # Faces where all points are found in hit
    faces_by_hit = np.where(np.all(fn_in_hit, axis=0))[0]
    faces_on_boundary_new = np.where(g_new.tags["fracture_faces"].ravel())[0]
    # Only consider faces both in hit, and that are boundary
    faces_on_boundary_new = np.intersect1d(faces_by_hit, faces_on_boundary_new)

    # Cells along the segment, from the new grid
    bound_cells_new = cells_from_faces(g_new, faces_on_boundary_new)
    #    assert bound_cells_new.size > 1, 'Have not implemented this. Not difficult though'
    cc_new = g_new.cell_centers[:, bound_cells_new]
    side_new = np.sign(np.sum(((cc_new - mp) * normal), axis=0))

    pos_side_new = np.where(side_new > 0)[0]
    neg_side_new = np.where(side_new < 0)[0]
    assert pos_side_new.size + neg_side_new.size == side_new.size

    both_sides_new = [pos_side_new, neg_side_new]

    # Mapping matrix.
    matrix = sps.coo_matrix((g_old.num_faces, g_new.num_faces))

    for so, sn in zip(both_sides_old, both_sides_new):

        if sn.size == 0 or so.size == 0:
            continue
        # Pick out faces along boundary in old grid, uniquify nodes, and
        # define auxiliary grids
        loc_faces_old = faces_on_boundary_old[so]
        loc_nodes_old = np.unique(nodes_of_faces(g_old, loc_faces_old))
        g_aux_old, sort_ind_old = create_1d_from_nodes(
            g_old.nodes[:, loc_nodes_old])

        # Similar for new grid
        loc_faces_new = faces_on_boundary_new[sn]
        loc_nodes_new = np.unique(fn[:, loc_faces_new])
        g_aux_new, sort_ind_new = create_1d_from_nodes(
            nodes_new[:, loc_nodes_new])

        # Map from global faces to faces along segment in old grid
        n_loc_old = loc_faces_old.size
        face_map_old = sps.coo_matrix(
            (np.ones(n_loc_old), (np.arange(n_loc_old), loc_faces_old)),
            shape=(n_loc_old, g_old.num_faces),
        )

        # Map from global faces to faces along segment in new grid
        n_loc_new = loc_faces_new.size
        face_map_new = sps.coo_matrix(
            (np.ones(n_loc_new), (np.arange(n_loc_new), loc_faces_new)),
            shape=(n_loc_new, g_new.num_faces),
        )

        # Map from faces along segment in old to new grid. Consists of three
        # stages: faces in old to cells in 1d version of old, between 1d cells
        # in old and new, cells in new to faces in new

        # From faces to cells in old grid
        rows = face_to_cell_map(g_old, g_aux_old, loc_faces_old,
                                loc_nodes_old[sort_ind_old])
        cols = np.arange(rows.size)
        face_to_cell_old = sps.coo_matrix((np.ones(rows.size), (rows, cols)))

        # Mapping between cells in 1d grid
        weights, new_cells, old_cells = match_grids_1d(g_aux_new, g_aux_old,
                                                       tol)
        between_cells = sps.csr_matrix((weights, (old_cells, new_cells)))

        # From faces to cell in new grid
        rows = face_to_cell_map(g_new, g_aux_new, loc_faces_new,
                                loc_nodes_new[sort_ind_new])
        cols = np.arange(rows.size)
        cell_to_face_new = sps.coo_matrix((np.ones(rows.size), (rows, cols)))

        face_map_segment = face_to_cell_old * between_cells * cell_to_face_new

        face_map = face_map_old.T * face_map_segment * face_map_new

        matrix += face_map

    return matrix.tocsr()