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]))
Example #2
0
    def _face_vector_to_scalar(self, nf, nd):
        """ Create a mapping from vector quantities on faces (stresses) to
        scalar quantities. The mapping is intended for the boundary
        discretization of the term div(u) (coupling term in the flow equation).

        Parameters:
            nf (int): Number of faces in the grid
        """
        rows = np.tile(np.arange(nf), ((nd, 1))).reshape((1, nd * nf),
                                                         order="F")[0]

        cols = fvutils.expand_indices_nd(np.arange(nf), nd)
        vals = np.ones(nf * nd)
        return sps.coo_matrix((vals, (rows, cols))).tocsr()
def compare_bc(g_1, data_0, data_1, face_map, phys="flow"):
    """
    Compare type and value of BCs.
    """
    BC_0 = data_0["param"].get_bc(phys)
    BC_1 = data_1["param"].get_bc(phys)
    vals_0 = data_0["param"].get_bc_val(phys)
    vals_1 = data_1["param"].get_bc_val(phys)

    assert np.all(BC_0.is_dir == BC_1.is_dir[face_map])
    assert np.all(BC_0.is_neu == BC_1.is_neu[face_map])
    if phys == "mechanics":
        boundary_face_map = fvutils.expand_indices_nd(face_map, g_1.dim)
    else:
        boundary_face_map = face_map
    assert np.all(vals_0 == vals_1[boundary_face_map])
Example #4
0
    def _discretize_mech(self, g, data):
        """
        Discretization of poro-elasticity by the MPSA-W method.

        Implementation needs (in addition to those mentioned in mpsa function):
            1) Fields for non-zero boundary conditions. Should be simple.
            2) Split return value grad_p into forces and a divergence operator,
            so that we can compute Biot forces on a face.

        Parameters:
            g (core.grids.grid): grid to be discretized
            k (core.constit.second_order_tensor) permeability tensor
            bound_mech: Boundary condition object for mechancis
            bound_flow: Boundary condition object for flow.
            constit (porepy.bc.bc.BoundaryCondition) class for boundary values
            faces (np.ndarray) faces to be considered. Intended for partial
                discretization, may change in the future
            eta Location of pressure continuity point. Should be 1/3 for simplex
                grids, 0 otherwise. On boundary faces with Dirichlet conditions,
                eta=0 will be enforced.
            inverter (string) Block inverter to be used, either numba (default),
                cython or python. See fvutils.invert_diagonal_blocks for details.

        Returns:
            scipy.sparse.csr_matrix (shape num_faces * dim, num_cells * dim): stres
                discretization, in the form of mapping from cell displacement to
                face stresses.
            scipy.sparse.csr_matrix (shape num_faces * dim, num_faces * dim):
                discretization of boundary conditions. Interpreted as istresses
                induced by the boundary condition (both Dirichlet and Neumann). For
                Neumann, this will be the prescribed stress over the boundary face,
                and possibly stress on faces having nodes on the boundary. For
                Dirichlet, the values will be stresses induced by the prescribed
                displacement.  Incorporation as a right hand side in linear system
                by multiplication with divergence operator.
            scipy.sparse.csr_matrix (shape num_cells * dim, num_cells): Forces from
                the pressure gradient (I*p-term), represented as body forces.
                TODO: Should rather be represented as forces on faces.
            scipy.sparse.csr_matrix (shape num_cells, num_cells * dim): Trace of
                strain matrix, cell-wise.
            scipy.sparse.csr_matrix (shape num_cells x num_cells): Stabilization
                term.

        Example:
            # Set up a Cartesian grid
            g = structured.CartGrid([5, 5])
            c = tensor.FourthOrderTensor(g.dim, np.ones(g.num_cells))
            k = tensor.SecondOrderTensor(g.dim, np.ones(g.num_cells))

            # Dirirchlet boundary conditions for mechanics
            bound_faces = g.get_all_boundary_faces().ravel()
            bnd = bc.BoundaryCondition(g, bound_faces, ['dir'] * bound_faces.size)

            # Use no boundary conditions for flow, will default to homogeneous
            # Neumann.

            # Discretization
            stress, bound_stress, grad_p, div_d, stabilization = biot(g, c, bnd)
            flux, bound_flux = mpfa(g, k, None)

            # Source in the middle of the domain
            q_mech = np.zeros(g.num_cells * g.dim)

            # Divergence operator for the grid
            div_mech = fvutils.vector_divergence(g)
            div_flow = fvutils.scalar_divergence(g)
            a_mech = div_mech * stress
            a_flow = div_flow * flux

            a_biot = sps.bmat([[a_mech, grad_p], [div_d, a_flow +
                                                           stabilization]])

            # Zero boundary conditions by default.

            # Injection in the middle of the domain
            rhs = np.zeros(g.num_cells * (g.dim + 1))
            rhs[g.num_cells * g.dim + np.ceil(g.num_cells / 2)] = 1
            x = sps.linalg.spsolve(A, rhs)

            u_x = x[0:g.num_cells * g.dim: g.dim]
            u_y = x[1:g.num_cells * g.dim: g.dim]
            p = x[g.num_cells * gdim:]

        """
        param = data["param"]
        bound_mech = param.get_bc("mechanics")
        bound_flow = param.get_bc("flow")
        constit = param.get_tensor("mechanics")

        eta = data.get("eta", 0)
        inverter = data.get("inverter", None)

        # The grid coordinates are always three-dimensional, even if the grid
        # is really 2D. This means that there is not a 1-1 relation between the
        # number of coordinates of a point / vector and the real dimension.
        # This again violates some assumptions tacitly made in the
        # discretization (in particular that the number of faces of a cell that
        # meets in a vertex equals the grid dimension, and that this can be
        # used to construct an index of local variables in the discretization).
        # These issues should be possible to overcome, but for the moment, we
        # simply force 2D grids to be proper 2D.
        if g.dim == 2:
            g = g.copy()
            g.cell_centers = np.delete(g.cell_centers, (2), axis=0)
            g.face_centers = np.delete(g.face_centers, (2), axis=0)
            g.face_normals = np.delete(g.face_normals, (2), axis=0)
            g.nodes = np.delete(g.nodes, (2), axis=0)

            constit.c = np.delete(constit.c, (2, 5, 6, 7, 8), axis=0)
            constit.c = np.delete(constit.c, (2, 5, 6, 7, 8), axis=1)
        nd = g.dim

        # Define subcell topology
        subcell_topology = fvutils.SubcellTopology(g)
        # Obtain mappings to exclude boundary faces for mechanics
        bound_exclusion_mech = fvutils.ExcludeBoundaries(
            subcell_topology, bound_mech, nd)
        # ... and flow
        bound_exclusion_flow = fvutils.ExcludeBoundaries(
            subcell_topology, bound_flow, nd)

        num_subhfno = subcell_topology.subhfno.size

        num_nodes = np.diff(g.face_nodes.indptr)
        sgn = g.cell_faces[subcell_topology.fno, subcell_topology.cno].A

        # The pressure gradient term in the mechanics equation is discretized
        # as a force on the faces. The right hand side is thus formed of the
        # normal vectors.
        def build_rhs_normals_single_dimension(dim):
            val = (g.face_normals[dim, subcell_topology.fno] * sgn /
                   num_nodes[subcell_topology.fno])
            mat = sps.coo_matrix(
                (val.squeeze(),
                 (subcell_topology.subfno, subcell_topology.cno)),
                shape=(subcell_topology.num_subfno, subcell_topology.num_cno),
            )
            return mat

        rhs_normals = build_rhs_normals_single_dimension(0)
        for iter1 in range(1, nd):
            this_dim = build_rhs_normals_single_dimension(iter1)
            rhs_normals = sps.vstack([rhs_normals, this_dim])

        rhs_normals = bound_exclusion_mech.exclude_dirichlet_nd(rhs_normals)

        num_dir_subface = (bound_exclusion_mech.exclude_neu.shape[1] -
                           bound_exclusion_mech.exclude_neu.shape[0]) * nd
        # No right hand side for cell displacement equations.
        rhs_normals_displ_var = sps.coo_matrix((
            nd * subcell_topology.num_subfno - num_dir_subface,
            subcell_topology.num_cno,
        ))

        # Why minus?
        rhs_normals = -sps.vstack([rhs_normals, rhs_normals_displ_var])
        del rhs_normals_displ_var

        # Call core part of MPSA
        hook, igrad, rhs_cells, cell_node_blocks, hook_normal = mpsa.mpsa_elasticity(
            g, constit, subcell_topology, bound_exclusion_mech, eta, inverter)

        # Output should be on face-level (not sub-face)
        hf2f = fvutils.map_hf_2_f(subcell_topology.fno_unique,
                                  subcell_topology.subfno_unique, nd)

        # Stress discretization
        stress = hf2f * hook * igrad * rhs_cells

        # Right hand side for boundary discretization
        rhs_bound = mpsa.create_bound_rhs(bound_mech, bound_exclusion_mech,
                                          subcell_topology, g)
        # Discretization of boundary values
        bound_stress = hf2f * hook * igrad * rhs_bound

        # Face-wise gradient operator. Used for the term grad_p in Biot's
        # equations.
        rows = fvutils.expand_indices_nd(subcell_topology.cno, nd)
        cols = np.arange(num_subhfno * nd)
        vals = np.tile(sgn, (nd, 1)).ravel("F")
        div_gradp = sps.coo_matrix(
            (vals, (rows, cols)),
            shape=(subcell_topology.num_cno * nd, num_subhfno * nd),
        ).tocsr()

        #        del hook, rhs_bound
        del rows, cols, vals

        grad_p = div_gradp * hook_normal * igrad * rhs_normals
        # assert np.allclose(grad_p.sum(axis=0), np.zeros(g.num_cells))

        del hook_normal, div_gradp

        div = self._subcell_gradient_to_cell_scalar(g, cell_node_blocks)

        div_d = div * igrad * rhs_cells

        # The boundary discretization of the div_d term is represented directly
        # on the cells, instead of going via the faces.
        bound_div_d = div * igrad * rhs_bound
        del rhs_cells

        stabilization = div * igrad * rhs_normals

        data["stress"] = stress
        data["bound_stress"] = bound_stress
        data["grad_p"] = grad_p
        data["div_d"] = div_d
        data["stabilization"] = stabilization
        data["bound_div_d"] = bound_div_d