Пример #1
0
def _append_face_geometry_fracture_grid(
    g: pp.Grid, n_new_faces: int, new_centers: np.ndarray
) -> None:
    """
    Appends and updates faces geometry information for new faces. Also updates
    num_faces.
    """
    g.face_normals = np.append(g.face_normals, np.zeros((3, n_new_faces)), axis=1)
    g.face_areas = np.append(g.face_areas, np.ones(n_new_faces))
    g.face_centers = np.append(g.face_centers, new_centers, axis=1)
    g.num_faces += n_new_faces
Пример #2
0
def _duplicate_specific_faces(gh: pp.Grid, frac_id: np.ndarray) -> np.ndarray:
    """
    Duplicate faces of gh specified by frac_id.
    """

    # Find which of the faces to split are tagged with a standard face tag,
    # that is, as fracture, tip or domain_boundary
    rem = tags.all_face_tags(gh.tags)[frac_id]

    # Set the faces to be split to fracture faces
    # Q: Why only if the face already had a tag (e.g., why [rem])?
    # Possible answer: We wil not split them (see redefinition of frac_id below),
    # but want them to be tagged as fracture_faces
    gh.tags["fracture_faces"][frac_id[rem]] = True
    # Faces to be split should not be tip
    gh.tags["tip_faces"][frac_id] = False

    # Only consider previously untagged faces for splitting
    frac_id = frac_id[~rem]
    if frac_id.size == 0:
        return frac_id

    # Expand the face-node relation to include duplicated nodes
    # Do this by directly manipulating the CSC-format of the matrix
    # Nodes of the target faces
    node_start = gh.face_nodes.indptr[frac_id]
    node_end = gh.face_nodes.indptr[frac_id + 1]
    nodes = gh.face_nodes.indices[mcolon(node_start, node_end)]

    # Start point for the new columns. They will be appended to the matrix, thus
    # the offset of the previous size of gh.face_nodes
    added_node_pos = np.cumsum(node_end -
                               node_start) + gh.face_nodes.indptr[-1]
    # Sanity checks
    assert added_node_pos.size == frac_id.size
    assert added_node_pos[-1] - gh.face_nodes.indptr[-1] == nodes.size
    # Expand row-data by adding node indices
    gh.face_nodes.indices = np.hstack((gh.face_nodes.indices, nodes))
    # Expand column pointers
    gh.face_nodes.indptr = np.hstack((gh.face_nodes.indptr, added_node_pos))
    # Expand data array
    gh.face_nodes.data = np.hstack(
        (gh.face_nodes.data, np.ones(nodes.size, dtype=bool)))
    # Update matrix shape
    gh.face_nodes._shape = (gh.num_nodes,
                            gh.face_nodes.shape[1] + frac_id.size)
    assert gh.face_nodes.indices.size == gh.face_nodes.indptr[-1]

    # We also copy the attributes of the original faces.
    gh.num_faces += frac_id.size
    gh.face_normals = np.hstack((gh.face_normals, gh.face_normals[:, frac_id]))
    gh.face_areas = np.append(gh.face_areas, gh.face_areas[frac_id])
    gh.face_centers = np.hstack((gh.face_centers, gh.face_centers[:, frac_id]))

    # Not sure if this still does the correct thing. Might have to
    # send in a logical array instead of frac_id.
    gh.tags["fracture_faces"][frac_id] = True
    gh.tags["tip_faces"][frac_id] = False
    update_fields = gh.tags.keys()
    update_values: List[List[np.ndarray]] = [[]] * len(update_fields)
    for i, key in enumerate(update_fields):
        # faces related tags are doubled and the value is inherit from the original
        if key.endswith("_faces"):
            update_values[i] = gh.tags[key][frac_id]
    tags.append_tags(gh.tags, update_fields, update_values)

    return frac_id
Пример #3
0
def _update_geometry(
    g_h: pp.Grid,
    g_l: pp.Grid,
    new_cells: np.ndarray,
    n_old_cells_l: int,
    n_old_faces_l: int,
) -> None:
    # Update geometry on each iteration to ensure correct tags.

    # The geometry of the higher-dimensional grid can be computed straightforwardly.
    g_h.compute_geometry()

    if g_h.dim == 2:
        # 1d geometry computation is valid also for manifolds
        g_l.compute_geometry()
    else:
        # The implementation of 2d compute_geometry() assumes that the
        # grid is planar. The simplest option is to treat one cell at
        # a time, and then merge the arrays at the end.

        # Initialize arrays for geometric quantities
        fa = np.empty(0)  # Face areas
        fc = np.empty((3, 0))  # Face centers
        fn = np.empty((3, 0))  # Face normals
        cv = np.empty(0)  # Cell volumes
        cc = np.empty((3, 0))  # Cell centers
        # Many of the faces will have their quantities computed twice,
        # once from each side. Keep track of which faces we are dealing with
        face_ind = np.array([], dtype=np.int)

        for ci in new_cells:
            sub_g, fi, _ = pp.partition.extract_subgrid(g_l, ci)
            sub_g.compute_geometry()

            fa = np.append(fa, sub_g.face_areas)
            fc = np.append(fc, sub_g.face_centers, axis=1)
            fn = np.append(fn, sub_g.face_normals, axis=1)
            cv = np.append(cv, sub_g.cell_volumes)
            cc = np.append(cc, sub_g.cell_centers, axis=1)

            face_ind = np.append(face_ind, fi)

        # The new cell geometry is composed of values from the previous grid, and
        # the values computed one by one for the new cells
        g_l.cell_volumes = np.hstack((g_l.cell_volumes[:n_old_cells_l], cv))
        g_l.cell_centers = np.hstack((g_l.cell_centers[:, :n_old_cells_l], cc))

        # For the faces, more work is needed
        face_areas = np.zeros(g_l.num_faces)
        face_centers = np.zeros((3, g_l.num_faces))
        face_normals = np.zeros((3, g_l.num_faces))

        # For the old faces, transfer already computed values
        face_areas[:n_old_faces_l] = g_l.face_areas[:n_old_faces_l]
        face_centers[:, :n_old_faces_l] = g_l.face_centers[:, :n_old_faces_l]
        face_normals[:, :n_old_faces_l] = g_l.face_normals[:, :n_old_faces_l]

        for fi in range(n_old_faces_l, g_l.num_faces):
            # Geometric quantities for this face
            hit = np.where(face_ind == fi)[0]
            # There should be 1 or 2 hits
            assert hit.size > 0 and hit.size < 3

            # For areas and centers, the computations based on the two neighboring
            # cells should give the same result. Check, and then use the value.
            mean_area = np.mean(fa[hit])
            mean_center = np.mean(fc[:, hit], axis=1)
            assert np.allclose(fa[hit], mean_area)
            assert np.allclose(fc[:, hit], mean_center.reshape((3, 1)))
            face_areas[fi] = mean_area
            face_centers[:, fi] = mean_center

            # The normal is more difficult, since this is not unique.
            # The direction of the normal vectors computed from subgrids should be
            # consistent with the +- convention in the main grid.

            # Normal vectors found for this global face
            normals = fn[:, hit]
            if normals.size == 3:
                normals = normals.reshape((3, 1))

            # For the moment, use the mean of the two values.
            mean_normal = np.mean(normals, axis=1)

            face_normals[:, fi] = mean_normal / np.linalg.norm(
                mean_normal) * mean_area

        # Sanity check
        # assert np.allclose(np.linalg.norm(face_normals, axis=0), face_areas)

        # Store computed values
        g_l.face_areas = face_areas
        g_l.face_centers = face_centers
        g_l.face_normals = face_normals