예제 #1
0
    def __init__(self, keyword, grids):

        if isinstance(grids, list):
            self._grids = grids
        else:
            self._grids = [grids]

        dim = np.unique([g[0].dim for g in grids])

        low_dim_grids = [g[1] for g in grids]
        if not dim.size == 1:
            raise ValueError("Expected unique dimension of grids with contact problems")

        self._discretization = pp.ColoumbContact(
            keyword, ambient_dimension=dim[0], discr_h=pp.Mpsa(keyword)
        )
        self._name = "Biot stabilization term"
        self.keyword = keyword

        self.traction: _MergedOperator
        self.displacement: _MergedOperator
        self.rhs: _MergedOperator

        _wrap_discretization(
            self, self._discretization, grids, mat_dict_grids=low_dim_grids
        )
예제 #2
0
    def assign_discretizations(self):
        """
        Assign discretizations to the nodes and edges of the grid bucket.

        """
        # For the Nd domain we solve linear elasticity with mpsa.
        Nd = self.Nd
        gb = self.gb
        mpsa = pp.Mpsa(self.mechanics_parameter_key)
        # We need a void discretization for the contact traction variable defined on
        # the fractures.
        empty_discr = pp.VoidDiscretization(self.mechanics_parameter_key, ndof_cell=Nd)

        for g, d in gb:
            if g.dim == Nd:
                d[pp.DISCRETIZATION] = {self.displacement_variable: {"mpsa": mpsa}}
            elif g.dim == Nd - 1:
                d[pp.DISCRETIZATION] = {
                    self.contact_traction_variable: {"empty": empty_discr}
                }

        # Define the contact condition on the mortar grid
        coloumb = pp.ColoumbContact(self.mechanics_parameter_key, Nd, mpsa)
        contact = pp.PrimalContactCoupling(self.mechanics_parameter_key, mpsa, coloumb)

        for e, d in gb.edges():
            g_l, g_h = gb.nodes_of_edge(e)
            if g_h.dim == Nd:
                d[pp.COUPLING_DISCRETIZATION] = {
                    self.friction_coupling_term: {
                        g_h: (self.displacement_variable, "mpsa"),
                        g_l: (self.contact_traction_variable, "empty"),
                        (g_h, g_l): (self.mortar_displacement_variable, contact),
                    }
                }
    def setup(self):
        g = pp.CartGrid([5, 5])
        g.compute_geometry()
        stiffness = pp.FourthOrderTensor(np.ones(g.num_cells),
                                         np.ones(g.num_cells))
        bnd = pp.BoundaryConditionVectorial(g)

        specified_data = {
            "fourth_order_tensor": stiffness,
            "bc": bnd,
            "inverter": "python",
        }
        keyword = "mechanics"
        data = pp.initialize_default_data(g, {},
                                          keyword,
                                          specified_parameters=specified_data)

        discr = pp.Mpsa(keyword)
        discr.discretize(g, data)
        stress = data[pp.DISCRETIZATION_MATRICES][keyword][
            discr.stress_matrix_key]
        bound_stress = data[pp.DISCRETIZATION_MATRICES][keyword][
            discr.bound_stress_matrix_key]

        return g, stiffness, bnd, stress, bound_stress
    def test_cart_2d(self):
        g = pp.CartGrid([2, 1], physdims=(1, 1))
        g.compute_geometry()
        right = g.face_centers[0] > 1 - 1e-10
        top = g.face_centers[1] > 1 - 1e-10

        bc = pp.BoundaryConditionVectorial(g)
        bc.is_dir[:, top] = True
        bc.is_dir[0, right] = True

        bc.is_neu[bc.is_dir] = False

        g = true_2d(g)

        subcell_topology = pp.fvutils.SubcellTopology(g)
        # Mat boundary to subfaces
        bc = pp.fvutils.boundary_to_sub_boundary(bc, subcell_topology)
        bound_exclusion = pp.fvutils.ExcludeBoundaries(subcell_topology, bc,
                                                       g.dim)
        cell_node_blocks = np.array([[0, 0, 0, 0, 1, 1, 1, 1],
                                     [0, 1, 3, 4, 1, 2, 4, 5]])
        ncasym_np = np.arange(32 * 32).reshape((32, 32))
        ncasym = sps.csr_matrix(ncasym_np)
        pp.Mpsa("")._eliminate_ncasym_neumann(ncasym, subcell_topology,
                                              bound_exclusion,
                                              cell_node_blocks, 2)
        eliminate_ind = np.array([0, 1, 16, 17, 26, 27])
        ncasym_np[eliminate_ind] = 0
        self.assertTrue(np.allclose(ncasym.A, ncasym_np))
예제 #5
0
    def test_structured_triang(self):
        nx = 1
        ny = 1
        g = pp.StructuredTriangleGrid([nx, ny], physdims=[1, 1])
        g.compute_geometry()

        bot = g.face_centers[1] < 1e-10
        top = g.face_centers[1] > 1 - 1e-10
        left = g.face_centers[0] < 1e-10
        right = g.face_centers[0] > 1 - 1e-10

        is_dir = left
        is_neu = top
        is_rob = right + bot

        bnd = pp.BoundaryConditionVectorial(g)
        bnd.is_neu[:] = False

        bnd.is_dir[:, is_dir] = True
        bnd.is_rob[:, is_rob] = True
        bnd.is_neu[:, is_neu] = True

        sc_top = pp.fvutils.SubcellTopology(g)
        bnd = pp.fvutils.boundary_to_sub_boundary(bnd, sc_top)
        bnd_excl = pp.fvutils.ExcludeBoundaries(sc_top, bnd, g.dim)

        rhs = pp.Mpsa("")._create_bound_rhs(bnd, bnd_excl, sc_top, g, True)
        hf2f = pp.fvutils.map_hf_2_f(sc_top.fno_unique, sc_top.subfno_unique,
                                     g.dim)
        rhs = rhs * hf2f.T

        rhs_known = np.array([
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        ])

        self.assertTrue(np.all(np.abs(rhs_known - rhs) < 1e-12))
예제 #6
0
    def reconstruct_stress(self, previous_iterate: bool = False) -> None:
        """
        Compute the stress in the highest-dimensional grid based on the displacement
        states in that grid, adjacent interfaces and global boundary conditions.

        The stress is stored in the data dictionary of the highest-dimensional grid,
        in [pp.STATE]['stress'].

        Parameters:
            previous_iterate (boolean, optional): If True, use values from previous
                iteration to compute the stress. Defaults to False.

        """
        # Highest-dimensional grid and its data
        g = self._nd_grid()
        d = self.gb.node_props(g)

        # Pick the relevant displacemnet field
        if previous_iterate:
            u = d[pp.STATE][pp.ITERATE][self.displacement_variable]
        else:
            u = d[pp.STATE][self.displacement_variable]

        matrix_dictionary: Dict[str, sps.spmatrix] = d[
            pp.DISCRETIZATION_MATRICES][self.mechanics_parameter_key]

        # Make a discretization object to get hold of the right keys to access the
        # matrix_dictionary
        mpsa = pp.Mpsa(self.mechanics_parameter_key)
        # Stress contribution from internal cell center displacements
        stress: np.ndarray = matrix_dictionary[mpsa.stress_matrix_key] * u

        # Contributions from global boundary conditions
        bound_stress_discr: sps.spmatrix = matrix_dictionary[
            mpsa.bound_stress_matrix_key]
        global_bc_val: np.ndarray = d[pp.PARAMETERS][
            self.mechanics_parameter_key]["bc_values"]
        stress += bound_stress_discr * global_bc_val

        # Contributions from the mortar displacement variables
        for e, d_e in self.gb.edges():
            # Only contributions from interfaces to the highest dimensional grid
            mg: pp.MortarGrid = d_e["mortar_grid"]
            if mg.dim == self._Nd - 1:
                if previous_iterate:
                    u_e: np.ndarray = d_e[pp.STATE][pp.ITERATE][
                        self.mortar_displacement_variable]
                else:
                    u_e = d_e[pp.STATE][self.mortar_displacement_variable]

                stress += (bound_stress_discr *
                           mg.mortar_to_primary_avg(nd=self._Nd) * u_e)

        d[pp.STATE]["stress"] = stress
예제 #7
0
    def test_mpsa(self):
        self.setup()

        g, g_larger = self.g, self.g_larger

        specified_data = {
            "inverter": "python",
        }

        keyword = "mechanics"
        data_small = pp.initialize_default_data(
            g, {}, keyword, specified_parameters=specified_data)

        discr = pp.Mpsa(keyword)
        # Discretization on a small problem
        discr.discretize(g, data_small)

        # Perturb one node
        g_larger.nodes[0, 2] += 0.2
        # Faces that have their geometry changed
        update_faces = np.array([2, 21, 22])

        # Perturb the permeability in some cells on the larger grid
        mu, lmbda = np.ones(g_larger.num_cells), np.ones(g_larger.num_cells)

        high_coeff_cells = np.array([7, 12])
        stiff_larger = pp.FourthOrderTensor(mu, lmbda)

        specified_data_larger = {"fourth_order_tensor": stiff_larger}

        # Do a full discretization on the larger grid
        data_full = pp.initialize_default_data(
            g_larger, {}, keyword, specified_parameters=specified_data_larger)
        discr.discretize(g_larger, data_full)

        # Cells that will be marked as updated, either due to changed parameters or
        # the newly defined topology
        update_cells = np.union1d(self.new_cells, high_coeff_cells)

        updates = {
            "modified_cells": update_cells,
            #            "modified_faces": update_faces,
            "map_cells": self.cell_map,
            "map_faces": self.face_map,
        }

        # Data dictionary for the two-step discretization
        data_partial = pp.initialize_default_data(
            g_larger, {}, keyword, specified_parameters=specified_data_larger)
        data_partial["update_discretization"] = updates

        self._update_and_compare(data_small, data_partial, data_full, g_larger,
                                 [keyword], discr)
    def _assign_discretizations(self) -> None:
        """
        Assign discretizations to the nodes and edges of the grid bucket.

        Note the attribute subtract_fracture_pressure: Indicates whether or not to
        subtract the fracture pressure contribution for the contact traction. This
        should not be done if the scalar variable is temperature.
        """
        super()._assign_discretizations()

        # Shorthand
        key_s, key_m = self.scalar_parameter_key, self.mechanics_parameter_key
        var_s, var_d = self.scalar_variable, self.displacement_variable

        # Define discretization
        # For the Nd domain we solve linear elasticity with mpsa.
        mpsa = pp.Mpsa(key_m)
        mass_disc_s = pp.MassMatrix(key_s)
        source_disc_s = pp.ScalarSource(key_s)
        # Coupling discretizations

        # Assign node discretizations
        for g, d in self.gb:
            if g.dim == self.Nd:
                mpsa = d[pp.DISCRETIZATION][var_d]["mpsa"]

            elif g.dim == self.Nd - 1:
                d[pp.DISCRETIZATION].update({
                    var_s: {
                        "mass": mass_disc_s,
                        "source": source_disc_s,
                    },
                })

        fracture_scalar_to_force_balance = pp.FractureScalarToForceBalance(
            mpsa, mass_disc_s)

        for e, d in self.gb.edges():
            g_l, g_h = self.gb.nodes_of_edge(e)

            if g_h.dim == self.Nd:
                d[pp.COUPLING_DISCRETIZATION].update({
                    "fracture_scalar_to_force_balance": {
                        g_h: (var_d, "mpsa"),
                        g_l: (var_s, "mass"),
                        e: (
                            self.mortar_displacement_variable,
                            fracture_scalar_to_force_balance,
                        ),
                    }
                })
    def reconstruct_stress(self, previous_iterate: bool = False) -> None:
        """
        Compute the stress in the highest-dimensional grid based on the displacement
        states in that grid, adjacent interfaces and global boundary conditions.

        The stress is stored in the data dictionary of the highest-dimensional grid,
        in [pp.STATE]['stress'].

        Parameters:
            previous_iterate (boolean, optional): If True, use values from previous
                iteration to compute the stress. Defaults to False.

        """
        g = self._nd_grid()
        d = self.gb.node_props(g)

        mpsa = pp.Mpsa(self.mechanics_parameter_key)

        if previous_iterate:
            u = d[pp.STATE][pp.ITERATE][self.displacement_variable]
        else:
            u = d[pp.STATE][self.displacement_variable]

        matrix_dictionary = d[pp.DISCRETIZATION_MATRICES][
            self.mechanics_parameter_key]

        # Stress contribution from internal cell center displacements
        stress = matrix_dictionary[mpsa.stress_matrix_key] * u

        # Contributions from global boundary conditions
        bound_stress_discr = matrix_dictionary[mpsa.bound_stress_matrix_key]
        global_bc_val = d[pp.PARAMETERS][
            self.mechanics_parameter_key]["bc_values"]
        stress += bound_stress_discr * global_bc_val

        # Contributions from the mortar displacement variables
        for e, d_e in self.gb.edges():
            # Only contributions from interfaces to the highest dimensional grid
            mg = d_e["mortar_grid"]
            if mg.dim == self.Nd - 1:
                if previous_iterate:
                    u_e = d_e[pp.STATE][pp.ITERATE][
                        self.mortar_displacement_variable]
                else:
                    u_e = d_e[pp.STATE][self.mortar_displacement_variable]

                stress += bound_stress_discr * mg.mortar_to_master_avg(
                    nd=self.Nd) * u_e

        d[pp.STATE]["stress"] = stress
예제 #10
0
    def assign_mechanics_discretizations(self) -> None:
        """
        Assign discretizations to the nodes and edges of the grid bucket.
        """
        gb = self.gb
        nd = self.Nd
        # Shorthand
        key_m, var_m = self.mechanics_parameter_key, self.displacement_variable
        var_contact = self.contact_traction_variable
        var_mortar = self.mortar_displacement_variable
        discr_key, coupling_discr_key = pp.DISCRETIZATION, pp.COUPLING_DISCRETIZATION

        # For the Nd domain we solve linear elasticity with mpsa.
        mpsa = pp.Mpsa(key_m)

        # We need a void discretization for the contact traction variable defined on
        # the fractures.
        empty_discr = pp.VoidDiscretization(key_m, ndof_cell=nd)

        # Assign node discretizations
        for g, d in gb:
            add_nonpresent_dictionary(d, discr_key)

            if g.dim == nd:
                d[discr_key].update(
                    {var_m: {"mpsa": mpsa},}
                )

            elif g.dim == nd - 1:
                d[discr_key].update(
                    {var_contact: {"empty": empty_discr},}
                )

        # Define the contact condition on the mortar grid
        coloumb = pp.ColoumbContact(key_m, nd, mpsa)
        contact = pp.PrimalContactCoupling(key_m, mpsa, coloumb)

        for e, d in gb.edges():
            g_l, g_h = gb.nodes_of_edge(e)
            add_nonpresent_dictionary(d, coupling_discr_key)
            if g_h.dim == nd:
                d[coupling_discr_key].update(
                    {
                        self.friction_coupling_term: {
                            g_h: (var_m, "mpsa"),
                            g_l: (var_contact, "empty"),
                            e: (var_mortar, contact),
                        },
                    }
                )
예제 #11
0
    def reconstruct_stress(self, previous_iterate: bool = False) -> None:
        """ Compute the stress in the highest-dimensional grid based on the displacement
        states in that grid, adjacent interfaces and global boundary conditions.

        The stress is stored in the data dictionary of the highest-dimensional grid,
        in [pp.STATE]["stress"].

        Parameters:
            previous_iterate : bool
                If True, use values from previous iteration to compute the stress.
                Default: False.

        """
        # TODO: Currently 'reconstruct_stress' does not work if 'previous_iterate = True'
        #  since the displacement variable on Nd-grid is not stored in pp.ITERATE.
        if previous_iterate is True:
            raise ValueError("Not yet implemented.")
        g = self._nd_grid()
        d = self.gb.node_props(g)
        key_m = self.mechanics_parameter_key
        var_m = self.displacement_variable
        var_mortar = self.mortar_displacement_variable

        mpsa = pp.Mpsa(self.mechanics_parameter_key)

        if previous_iterate:
            u = d[pp.STATE][pp.ITERATE][var_m]
        else:
            u = d[pp.STATE][var_m]

        matrix_dictionary = d[pp.DISCRETIZATION_MATRICES][key_m]

        # Stress contribution from internal cell center displacements
        stress = matrix_dictionary[mpsa.stress_matrix_key] * u

        # Contributions from global boundary conditions
        bound_stress_discr = matrix_dictionary[mpsa.bound_stress_matrix_key]
        global_bc_val = d[pp.PARAMETERS][key_m]["bc_values"]
        stress += bound_stress_discr * global_bc_val

        # Contributions from the mortar displacement variables
        for e, d_e in self.gb.edges():
            # Only contributions from interfaces to the highest dimensional grid
            mg: pp.MortarGrid = d_e["mortar_grid"]
            if mg.dim == self.Nd - 1:
                u_e = d_e[pp.STATE][var_mortar]

                stress += bound_stress_discr * mg.mortar_to_master_avg(nd=self.Nd) * u_e

        d[pp.STATE]["stress"] = stress
    def test_bound_cell_node_keyword(self):
        # Compute update for a single cell on the
        g, stiffness, bnd, stress, bound_stress = self.setup()

        # inner_cell = 10
        nodes_of_cell = np.array([12, 13, 18, 19])
        faces_of_cell = np.array([12, 13, 40, 45])

        bnd = pp.BoundaryConditionVectorial(g)
        specified_data = {
            "fourth_order_tensor": stiffness,
            "bc": bnd,
            "inverter": "python",
            "specified_nodes": np.array([nodes_of_cell]),
        }
        keyword = "mechanics"
        data = pp.initialize_default_data(g, {},
                                          keyword,
                                          specified_parameters=specified_data)

        discr = pp.Mpsa(keyword)
        discr.discretize(g, data)

        partial_stress = data[pp.DISCRETIZATION_MATRICES][keyword][
            discr.stress_matrix_key]
        partial_bound = data[pp.DISCRETIZATION_MATRICES][keyword][
            discr.bound_stress_matrix_key]

        active_faces = data[pp.PARAMETERS][keyword]["active_faces"]

        self.assertTrue(faces_of_cell.size == active_faces.size)
        self.assertTrue(
            np.all(np.sort(faces_of_cell) == np.sort(active_faces)))

        faces_of_cell = self.expand_indices_nd(faces_of_cell, g.dim)
        diff_stress = (stress - partial_stress).todense()
        diff_bound = (bound_stress - partial_bound).todense()

        self.assertTrue(np.max(np.abs(diff_stress[faces_of_cell])) == 0)
        self.assertTrue(np.max(np.abs(diff_bound[faces_of_cell])) == 0)

        # Only the faces of the central cell should be non-zero.
        pp.fvutils.remove_nonlocal_contribution(faces_of_cell, 1,
                                                partial_stress, partial_bound)
        self.assertTrue(np.max(np.abs(partial_stress.data)) == 0)
        self.assertTrue(np.max(np.abs(partial_bound.data)) == 0)
예제 #13
0
    def __init__(self, keyword, grids):
        if isinstance(grids, list):
            self._grids = grids
        else:
            self._grids = [grids]
        self._discretization = pp.Mpsa(keyword)
        self._name = "Mpsa"

        self.keyword = keyword

        # Declear attributes, these will be initialized by the below call to the
        # discretization wrapper.

        self.stress: _MergedOperator
        self.bound_stress: _MergedOperator
        self.bound_displacement_cell: _MergedOperator
        self.bound_displacement_face: _MergedOperator

        _wrap_discretization(self, self._discretization, grids)
예제 #14
0
    def set_parameters(self, g, data_node, mg, data_edge):
        """
        Set the parameters for the simulation. The stress is given in GPa.
        """
        # Define the finite volume sub grid
        s_t = pp.fvutils.SubcellTopology(g)

        # Rock parameters
        rock = pp.Granite()
        lam = rock.LAMBDA * np.ones(g.num_cells) / self.pressure_scale
        mu = rock.MU * np.ones(g.num_cells) / self.pressure_scale
        F = self._friction_coefficient(g, mg, data_edge, s_t)

        k = pp.FourthOrderTensor(g.dim, mu, lam)

        # Define boundary regions
        top = g.face_centers[g.dim - 1] > np.max(g.nodes[1]) - 1e-9
        bot = g.face_centers[g.dim - 1] < np.min(g.nodes[1]) + 1e-9

        top_hf = top[s_t.fno_unique]
        bot_hf = bot[s_t.fno_unique]

        # Define boundary condition on sub_faces
        bc = pp.BoundaryConditionVectorial(g, top + bot, 'dir')
        bc = pp.fvutils.boundary_to_sub_boundary(bc, s_t)

        # Set the boundary values
        u_bc = np.zeros((g.dim, s_t.num_subfno_unique))
        u_bc[1, top_hf] = -0.002
        u_bc[0, top_hf] = 0.005

        # Find the continuity points
        eta = 1 / 3
        eta_vec = eta * np.ones(s_t.num_subfno_unique)

        cont_pnt = g.face_centers[:g.dim, s_t.fno_unique] + eta_vec * (
            g.nodes[:g.dim, s_t.nno_unique] -
            g.face_centers[:g.dim, s_t.fno_unique])

        # collect parameters in dictionary
        key = 'mech'
        key_m = 'mech'

        data_node = pp.initialize_data(
            g, data_node, key, {
                'bc': bc,
                'bc_values': u_bc.ravel('F'),
                'source': 0,
                'fourth_order_tensor': k,
                'mpsa_eta': eta_vec,
                'cont_pnt': cont_pnt,
                'rock': rock,
            })

        pp.initialize_data(mg, data_edge, key_m, {
            'friction_coeff': F,
            'c': 100
        })

        # Define discretization
        # For the 2D domain we solve linear elasticity with mpsa.
        mpsa = pp.Mpsa(key)
        data_node[pp.PRIMARY_VARIABLES] = {'u': {'cells': g.dim}}
        data_node[pp.DISCRETIZATION] = {'u': {'mpsa': mpsa}}

        # And define a Robin condition on the mortar grid
        contact = pp.numerics.interface_laws.elliptic_interface_laws.RobinContact(
            key_m, mpsa)
        data_edge[pp.PRIMARY_VARIABLES] = {'lam': {'cells': g.dim}}
        data_edge[pp.COUPLING_DISCRETIZATION] = {
            'robin_disc': {
                g: ('u', 'mpsa'),
                g: ('u', 'mpsa'),
                (g, g): ('lam', contact),
            }
        }
        return key, key_m
예제 #15
0
    def _assign_discretizations(self) -> None:
        """
        Assign discretizations to the nodes and edges of the grid bucket.

        """
        # For the Nd domain we solve linear elasticity with mpsa.
        Nd = self._Nd
        gb = self.gb

        if not self._use_ad:
            mpsa = pp.Mpsa(self.mechanics_parameter_key)
            # We need a void discretization for the contact traction variable defined on
            # the fractures.
            empty_discr = pp.VoidDiscretization(self.mechanics_parameter_key,
                                                ndof_cell=Nd)

            for g, d in gb:
                if g.dim == Nd:
                    d[pp.DISCRETIZATION] = {
                        self.displacement_variable: {
                            "mpsa": mpsa
                        }
                    }
                elif g.dim == Nd - 1:
                    d[pp.DISCRETIZATION] = {
                        self.contact_traction_variable: {
                            "empty": empty_discr
                        }
                    }

            # Define the contact condition on the mortar grid
            coloumb = pp.ColoumbContact(self.mechanics_parameter_key, Nd, mpsa)
            contact = pp.PrimalContactCoupling(self.mechanics_parameter_key,
                                               mpsa, coloumb)

            for e, d in gb.edges():
                g_l, g_h = gb.nodes_of_edge(e)
                if g_h.dim == Nd:
                    d[pp.COUPLING_DISCRETIZATION] = {
                        self.friction_coupling_term: {
                            g_h: (self.displacement_variable, "mpsa"),
                            g_l: (self.contact_traction_variable, "empty"),
                            (g_h, g_l):
                            (self.mortar_displacement_variable, contact),
                        }
                    }
        else:

            dof_manager = pp.DofManager(gb)
            eq_manager = pp.ad.EquationManager(gb, dof_manager)

            g_primary: pp.Grid = gb.grids_of_dimension(Nd)[0]
            g_frac: List[pp.Grid] = gb.grids_of_dimension(Nd - 1).tolist()
            num_frac_cells = np.sum([g.num_cells for g in g_frac])

            if len(gb.grids_of_dimension(Nd)) != 1:
                raise NotImplementedError("This will require further work")

            edge_list = [(g_primary, g) for g in g_frac]

            mortar_proj = pp.ad.MortarProjections(edges=edge_list,
                                                  gb=gb,
                                                  nd=self._Nd)
            subdomain_proj = pp.ad.SubdomainProjections(gb=gb, nd=self._Nd)
            tangential_proj_lists = [
                gb.node_props(
                    gf,
                    "tangential_normal_projection").project_tangential_normal(
                        gf.num_cells) for gf in g_frac
            ]
            tangential_proj = pp.ad.Matrix(
                sps.block_diag(tangential_proj_lists))

            mpsa_ad = mpsa_ad = pp.ad.MpsaAd(self.mechanics_parameter_key,
                                             g_primary)
            bc_ad = pp.ad.BoundaryCondition(self.mechanics_parameter_key,
                                            grids=[g_primary])
            div = pp.ad.Divergence(grids=[g_primary], dim=g_primary.dim)

            # Primary variables on Ad form
            u = eq_manager.variable(g_primary, self.displacement_variable)
            u_mortar = eq_manager.merge_variables([
                (e, self.mortar_displacement_variable) for e in edge_list
            ])
            contact_force = eq_manager.merge_variables([
                (g, self.contact_traction_variable) for g in g_frac
            ])

            u_mortar_prev = u_mortar.previous_timestep()

            # Stress in g_h
            stress = (mpsa_ad.stress * u + mpsa_ad.bound_stress * bc_ad +
                      mpsa_ad.bound_stress *
                      subdomain_proj.face_restriction(g_primary) *
                      mortar_proj.mortar_to_primary_avg * u_mortar)

            # momentum balance equation in g_h
            momentum_eq = pp.ad.Expression(div * stress,
                                           dof_manager,
                                           name="momentuum",
                                           grid_order=[g_primary])

            coloumb_ad = pp.ad.ColoumbContactAd(self.mechanics_parameter_key,
                                                edge_list)
            jump_rotate = (tangential_proj *
                           subdomain_proj.cell_restriction(g_frac) *
                           mortar_proj.mortar_to_secondary_avg *
                           mortar_proj.sign_of_mortar_sides)

            # Contact conditions
            jump_discr = coloumb_ad.displacement * jump_rotate * u_mortar
            tmp = np.ones(num_frac_cells * self._Nd)
            tmp[self._Nd - 1::self._Nd] = 0
            exclude_normal = pp.ad.Matrix(
                sps.dia_matrix((tmp, 0), shape=(tmp.size, tmp.size)))
            # Rhs of contact conditions
            rhs = (coloumb_ad.rhs + exclude_normal * coloumb_ad.displacement *
                   jump_rotate * u_mortar_prev)
            contact_conditions = coloumb_ad.traction * contact_force + jump_discr - rhs
            contact_eq = pp.ad.Expression(contact_conditions,
                                          dof_manager,
                                          grid_order=g_frac,
                                          name="contact")

            # Force balance

            mat = None
            for _, d in gb.edges():
                mg: pp.MortarGrid = d["mortar_grid"]
                if mg.dim < self._Nd - 1:
                    continue

                faces_on_fracture_surface = mg.primary_to_mortar_int().tocsr(
                ).indices
                m = pp.grid_utils.switch_sign_if_inwards_normal(
                    g_primary, self._Nd, faces_on_fracture_surface)
                if mat is None:
                    mat = m
                else:
                    mat += m

            sign_switcher = pp.ad.Matrix(mat)

            # Contact from primary grid and mortar displacements (via primary grid)
            contact_from_primary_mortar = (
                mortar_proj.primary_to_mortar_int *
                subdomain_proj.face_prolongation(g_primary) * sign_switcher *
                stress)
            contact_from_secondary = (
                mortar_proj.sign_of_mortar_sides *
                mortar_proj.secondary_to_mortar_int *
                subdomain_proj.cell_prolongation(g_frac) *
                tangential_proj.transpose() * contact_force)
            force_balance_eq = pp.ad.Expression(
                contact_from_primary_mortar + contact_from_secondary,
                dof_manager,
                name="force_balance",
                grid_order=edge_list,
            )
            #            eq2 = pp.ad.Equation(contact_from_secondary, dof_manager).to_ad(gb)

            eq_manager.equations += [momentum_eq, contact_eq, force_balance_eq]

            self._eq_manager = eq_manager
예제 #16
0
    def test_cart_2d(self):
        nx = 1
        ny = 1
        g = pp.CartGrid([nx, ny], physdims=[2, 2])
        g.compute_geometry()
        g = make_true_2d(g)
        sc_top = pp.fvutils.SubcellTopology(g)

        D_g, CC = pp.Mpsa("")._reconstruct_displacement(g, sc_top, eta=0)

        D_g_known = np.array([
            [
                -1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1.0,
                0.0,
            ],
            [
                0.0,
                -1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1.0,
            ],
        ])

        CC_known = np.array([
            [1, 0.0],
            [0, 1.0],
            [1, 0.0],
            [0, 1.0],
            [1, 0.0],
            [0, 1.0],
            [1, 0.0],
            [0, 1.0],
            [1, 0.0],
            [0, 1.0],
            [1, 0.0],
            [0, 1.0],
            [1, 0.0],
            [0, 1.0],
            [1, 0.0],
            [0, 1.0],
        ])

        self.assertTrue(np.all(np.abs(D_g - D_g_known) < 1e-12))
        self.assertTrue(np.all(np.abs(CC - CC_known) < 1e-12))
예제 #17
0
    def test_cart_3d(self):
        g = pp.CartGrid([1, 1, 1], physdims=[2, 2, 2])
        g.compute_geometry()
        sc_top = pp.fvutils.SubcellTopology(g)

        D_g, CC = pp.Mpsa("")._reconstruct_displacement(g, sc_top, eta=1)

        data = np.array([
            -1.0,
            -1.0,
            -1.0,
            -1.0,
            -1.0,
            -1.0,
            -1.0,
            -1.0,
            -1.0,
            1.0,
            -1.0,
            -1.0,
            1.0,
            -1.0,
            -1.0,
            1.0,
            -1.0,
            -1.0,
            -1.0,
            1.0,
            -1.0,
            -1.0,
            1.0,
            -1.0,
            -1.0,
            1.0,
            -1.0,
            1.0,
            1.0,
            -1.0,
            1.0,
            1.0,
            -1.0,
            1.0,
            1.0,
            -1.0,
            -1.0,
            -1.0,
            1.0,
            -1.0,
            -1.0,
            1.0,
            -1.0,
            -1.0,
            1.0,
            1.0,
            -1.0,
            1.0,
            1.0,
            -1.0,
            1.0,
            1.0,
            -1.0,
            1.0,
            -1.0,
            1.0,
            1.0,
            -1.0,
            1.0,
            1.0,
            -1.0,
            1.0,
            1.0,
            1.0,
            1.0,
            1.0,
            1.0,
            1.0,
            1.0,
            1.0,
            1.0,
            1.0,
        ])

        indices = np.array([
            0,
            24,
            48,
            1,
            25,
            49,
            2,
            26,
            50,
            12,
            33,
            51,
            13,
            34,
            52,
            14,
            35,
            53,
            3,
            36,
            57,
            4,
            37,
            58,
            5,
            38,
            59,
            15,
            45,
            54,
            16,
            46,
            55,
            17,
            47,
            56,
            9,
            27,
            60,
            10,
            28,
            61,
            11,
            29,
            62,
            21,
            30,
            63,
            22,
            31,
            64,
            23,
            32,
            65,
            6,
            39,
            69,
            7,
            40,
            70,
            8,
            41,
            71,
            18,
            42,
            66,
            19,
            43,
            67,
            20,
            44,
            68,
        ])

        indptr = np.array([
            0,
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10,
            11,
            12,
            13,
            14,
            15,
            16,
            17,
            18,
            19,
            20,
            21,
            22,
            23,
            24,
            25,
            26,
            27,
            28,
            29,
            30,
            31,
            32,
            33,
            34,
            35,
            36,
            37,
            38,
            39,
            40,
            41,
            42,
            43,
            44,
            45,
            46,
            47,
            48,
            49,
            50,
            51,
            52,
            53,
            54,
            55,
            56,
            57,
            58,
            59,
            60,
            61,
            62,
            63,
            64,
            65,
            66,
            67,
            68,
            69,
            70,
            71,
            72,
        ])

        CC_known = np.array([
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
        ])

        D_g_known = sps.csc_matrix((data, indices, indptr))

        self.assertTrue(np.all(np.abs(D_g - D_g_known).A < 1e-12))
        self.assertTrue(np.all(np.abs(CC - CC_known) < 1e-12))
예제 #18
0
    def set_parameters(self, g, data_node, mg, data_edge):
        """
        Set the parameters for the simulation. The stress is given in GPa.
        """
        # Define the finite volume sub grid
        s_t = pp.fvutils.SubcellTopology(g)

        # Rock parameters
        rock = pp.Granite()
        lam = rock.LAMBDA * np.ones(g.num_cells) / self.pressure_scale
        mu = rock.MU * np.ones(g.num_cells) / self.pressure_scale
        k = pp.FourthOrderTensor(g.dim, mu, lam)
        F = self._friction_coefficient(g, mg, data_edge, s_t)

        # Define boundary regions
        east = g.face_centers[0] > np.max(g.nodes[0]) - 1e-9
        west = g.face_centers[0] < np.min(g.nodes[0]) + 1e-9
        north = g.face_centers[1] > np.max(g.nodes[1]) - 1e-9
        south = g.face_centers[1] < np.min(g.nodes[1]) + 1e-9
        top = g.face_centers[2] > np.max(g.nodes[2]) - 1e-9
        bot = g.face_centers[2] < np.min(g.nodes[2]) + 1e-9

        top_hf = top[s_t.fno_unique]
        bot_hf = bot[s_t.fno_unique]

        # define boundary condition
        bc = pp.BoundaryConditionVectorial(g)
        bc.is_dir[0, east] = True  # Rolling
        bc.is_dir[0, west] = True  # Rolling
        bc.is_dir[1, north] = True  # Rolling
        bc.is_dir[1, south] = True  # Rolling
        bc.is_dir[:, bot] = True  # Dirichlet
        bc.is_neu[bc.is_dir + bc.is_rob] = False

        # extract boundary condition to subcells
        bc = pp.fvutils.boundary_to_sub_boundary(bc, s_t)

        # Give boundary condition values
        A = g.face_areas[s_t.fno_unique][top_hf] / 3  #* pp.length_scale**g.dim
        u_bc = np.zeros((g.dim, s_t.num_subfno_unique))
        u_bc[2, top_hf] = -4.5 * pp.MEGA * pp.PASCAL / self.pressure_scale * A

        # Find the continuity points
        eta = 1 / 3
        eta_vec = eta * np.ones(s_t.num_subfno_unique)

        cont_pnt = g.face_centers[:g.dim, s_t.fno_unique] + eta_vec * (
            g.nodes[:g.dim, s_t.nno_unique] -
            g.face_centers[:g.dim, s_t.fno_unique])

        # collect parameters in dictionary
        key = 'mech'
        key_m = 'mech'

        data_node = pp.initialize_data(
            g, data_node, key, {
                'bc': bc,
                'bc_values': u_bc.ravel('F'),
                'source': 0,
                'fourth_order_tensor': k,
                'mpsa_eta': eta_vec,
                'cont_pnt': cont_pnt,
                'rock': rock,
            })
        pp.initialize_data(
            mg, data_edge, key_m, {
                'friction_coeff': F,
                'c': 100 * pp.GIGA * pp.PASCAL / self.pressure_scale
            })

        # Define discretization
        # Solve linear elasticity with mpsa
        mpsa = pp.Mpsa(key)
        data_node[pp.PRIMARY_VARIABLES] = {'u': {'cells': g.dim}}
        data_node[pp.DISCRETIZATION] = {'u': {'mpsa': mpsa}}
        # Add a Robin condition to the mortar grid
        contact = pp.numerics.interface_laws.elliptic_interface_laws.RobinContact(
            key_m, mpsa)
        data_edge[pp.PRIMARY_VARIABLES] = {'lam': {'cells': g.dim}}
        data_edge[pp.COUPLING_DISCRETIZATION] = {
            'robin_disc': {
                g: ('u', 'mpsa'),
                g: ('u', 'mpsa'),
                (g, g): ('lam', contact),
            }
        }
        return key, key_m
예제 #19
0
    def test_simplex_2d(self):
        nx = 1
        ny = 1
        g = pp.StructuredTriangleGrid([nx, ny], physdims=[1, 1])
        g.compute_geometry()
        g = make_true_2d(g)
        sc_top = pp.fvutils.SubcellTopology(g)

        D_g, CC = pp.Mpsa("")._reconstruct_displacement(g, sc_top, eta=0)

        D_g_known = np.array([
            [
                -1 / 6,
                -1 / 3,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                -1 / 6,
                -1 / 3,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                -1 / 6,
                -1 / 3,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1 / 6,
                -1 / 3,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1 / 3,
                -1 / 6,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1 / 3,
                -1 / 6,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1 / 3,
                -1 / 6,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1 / 3,
                -1 / 6,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                -1 / 12,
                1 / 12,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 12,
                -1 / 12,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                -1 / 12,
                1 / 12,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 12,
                -1 / 12,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1 / 12,
                1 / 12,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 12,
                -1 / 12,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1 / 12,
                1 / 12,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 12,
                -1 / 12,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 3,
                1 / 6,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 3,
                1 / 6,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 3,
                1 / 6,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 3,
                1 / 6,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 6,
                1 / 3,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 6,
                1 / 3,
                0.0,
                0.0,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 6,
                1 / 3,
                0.0,
                0.0,
            ],
            [
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1 / 6,
                1 / 3,
            ],
        ])

        CC_known = np.array([
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
            [0.5, 0.0, 0.5, 0.0],
            [0.0, 0.5, 0.0, 0.5],
            [0.5, 0.0, 0.5, 0.0],
            [0.0, 0.5, 0.0, 0.5],
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
        ])

        self.assertTrue(np.all(np.abs(D_g - D_g_known).A < 1e-12))
        self.assertTrue(np.all(np.abs(CC - CC_known) < 1e-12))
예제 #20
0
    def set_parameters(self, g, data_node, mg, data_edge):
        """
        Set the parameters for the simulation. The stress is given in GPa.
        """
        # First set the parameters used in the pure elastic simulation
        key_m, key_c = super().set_parameters(g, data_node, mg, data_edge)
        key_f = 'flow'

        if not key_m == key_c:
            raise ValueError('Mechanics keyword must equal contact keyword')
        self.key_m = key_m
        self.key_f = key_f
        # Set fluid parameters
        kxx = self.k * np.ones(g.num_cells) / self.length_scale**2
        viscosity = self.viscosity / self.pressure_scale
        K = pp.SecondOrderTensor(g.dim, kxx / viscosity)

        # Define Biot parameters
        alpha = 1
        dt = self.end_time / 20
        # Define the finite volume sub grid
        s_t = pp.fvutils.SubcellTopology(g)

        # Define boundary conditions for flow
        top = g.face_centers[2] > np.max(g.nodes[2]) - 1e-9
        bot = g.face_centers[2] < np.min(g.nodes[2]) + 1e-9
        east = g.face_centers[0] > np.max(g.nodes[0]) - 1e-9
        bc_flow = pp.BoundaryCondition(g, top, 'dir')
        bc_flow = pp.fvutils.boundary_to_sub_boundary(bc_flow, s_t)

        # Set boundary condition values.
        p_bc = self.bc_values(g, dt, key_f)

        # Set initial solution
        u0 = np.zeros(g.dim * g.num_cells)
        p0 = np.zeros(g.num_cells)
        lam_u0 = np.zeros(g.dim * mg.num_cells)
        u_bc0 = self.bc_values(g, 0, key_m)
        u_bc = self.bc_values(g, dt, key_m)
        # Collect parameters in dictionaries

        # Add biot parameters to mechanics
        data_node[pp.PARAMETERS][key_m]['biot_alpha'] = alpha
        data_node[pp.PARAMETERS][key_m]['time_step'] = dt
        data_node[pp.PARAMETERS][key_m]['bc_values'] = u_bc
        data_node[pp.PARAMETERS][key_m]['state'] = {
            'displacement': u0,
            'bc_values': u_bc0
        }

        data_edge[pp.PARAMETERS][key_c]['state'] = lam_u0

        # Add fluid flow dictionary
        data_node = pp.initialize_data(
            g, data_node, key_f, {
                'bc': bc_flow,
                'bc_values': p_bc.ravel('F'),
                'second_order_tensor': K,
                'mass_weight': self.S,
                'aperture': np.ones(g.num_cells),
                'biot_alpha': alpha,
                'time_step': dt,
                'state': p0,
            })

        # Define discretization.
        # For the domain we solve linear elasticity with mpsa and fluid flow with mpfa.
        # In addition we add a storage term (ImplicitMassMatrix) to the fluid mass balance.
        # The coupling terms are:
        # BiotStabilization, pressure contribution to the div u term.
        # GrapP, pressure contribution to stress equation.
        # div_u, displacement contribution to div u term.
        data_node[pp.PRIMARY_VARIABLES] = {
            "u": {
                "cells": g.dim
            },
            "p": {
                "cells": 1
            }
        }

        mpfa_disc = discretizations.ImplicitMpfa(key_f)
        data_node[pp.DISCRETIZATION] = {
            "u": {
                "div_sigma": pp.Mpsa(key_m)
            },
            "p": {
                "flux": mpfa_disc,
                "mass": discretizations.ImplicitMassMatrix(key_f),
                "stab": pp.BiotStabilization(key_f),
            },
            "u_p": {
                "grad_p": pp.GradP(key_m)
            },
            "p_u": {
                "div_u": pp.DivD(key_m)
            },
        }

        # On the mortar grid we define two variables and sets of equations. The first
        # adds a Robin condition to the elasticity equation. The second enforces full
        # fluid pressure and flux continuity over the fractures. We also have to be
        # carefull to obtain the contribution of the coupling discretizations gradP on
        # the Robin contact condition, and the contribution from the mechanical mortar
        # variable on the div_u term.

        # Contribution from fluid pressure on displacement jump at fractures
        gradP_disp = pp.numerics.interface_laws.elliptic_interface_laws.RobinContactBiotPressure(
            key_m, pp.numerics.fv.biot.GradP(key_m))
        # Contribution from mechanics mortar on div_u term
        div_u_lam = pp.numerics.interface_laws.elliptic_interface_laws.DivU_StressMortar(
            key_m, pp.numerics.fv.biot.DivD(key_m))
        # gradP_disp and pp.RobinContact will now give the correct Robin contact
        # condition.
        # div_u (from above) and div_u_lam will now give the correct div u term in the
        # fluid mass balance
        data_edge[pp.PRIMARY_VARIABLES] = {"lam_u": {"cells": g.dim}}
        data_edge[pp.COUPLING_DISCRETIZATION] = {
            "robin_discretization": {
                g: ("u", "div_sigma"),
                g: ("u", "div_sigma"),
                (g, g): ("lam_u", pp.RobinContact(key_m, pp.Mpsa(key_m))),
            },
            "p_contribution_to_displacement": {
                g: ("p", "flux"
                    ),  # "flux" should be "grad_p", but the assembler does not
                g: ("p", "flux"
                    ),  # support this. However, in FV this is not used anyway.
                (g, g): ("lam_u", gradP_disp),
            },
            "lam_u_contr_2_div_u": {
                g: ("p", "flux"),  # "flux" -> "div_u"
                g: ("p", "flux"),
                (g, g): ("lam_u", div_u_lam),
            },
        }
        # Discretize with biot
        pp.Biot(key_m, key_f).discretize(g, data_node)
        return key_m, key_f
예제 #21
0
    def test_assemble_biot(self):
        """ Test the assembly of the Biot problem using the assembler.

        The test checks whether the discretization matches that of the Biot class.
        """
        gb = pp.meshing.cart_grid([], [2, 1])
        g = gb.grids_of_dimension(2)[0]
        d = gb.node_props(g)
        # Parameters identified by two keywords
        kw_m = "mechanics"
        kw_f = "flow"
        variable_m = "displacement"
        variable_f = "pressure"
        bound_mech, bound_flow = self.make_boundary_conditions(g)
        initial_disp, initial_pressure, initial_state = self.make_initial_conditions(
            g, x0=0, y0=0, p0=0)
        state = {
            variable_f: initial_pressure,
            variable_m: initial_disp,
            kw_m: {
                "bc_values": np.zeros(g.num_faces * g.dim)
            },
        }
        parameters_m = {"bc": bound_mech, "biot_alpha": 1}

        parameters_f = {"bc": bound_flow, "biot_alpha": 1}
        pp.initialize_default_data(g, d, kw_m, parameters_m)
        pp.initialize_default_data(g, d, kw_f, parameters_f)
        pp.set_state(d, state)
        # Discretize the mechanics related terms using the Biot class
        biot_discretizer = pp.Biot()
        biot_discretizer._discretize_mech(g, d)

        # Set up the structure for the assembler. First define variables and equation
        # term names.
        v_0 = variable_m
        v_1 = variable_f
        term_00 = "stress_divergence"
        term_01 = "pressure_gradient"
        term_10 = "displacement_divergence"
        term_11_0 = "fluid_mass"
        term_11_1 = "fluid_flux"
        term_11_2 = "stabilization"
        d[pp.PRIMARY_VARIABLES] = {v_0: {"cells": g.dim}, v_1: {"cells": 1}}
        d[pp.DISCRETIZATION] = {
            v_0: {
                term_00: pp.Mpsa(kw_m)
            },
            v_1: {
                term_11_0: pp.MassMatrix(kw_f),
                term_11_1: pp.Mpfa(kw_f),
                term_11_2: pp.BiotStabilization(kw_f),
            },
            v_0 + "_" + v_1: {
                term_01: pp.GradP(kw_m)
            },
            v_1 + "_" + v_0: {
                term_10: pp.DivU(kw_m)
            },
        }
        # Assemble. Also discretizes the flow terms (fluid_mass and fluid_flux)
        general_assembler = pp.Assembler(gb)
        general_assembler.discretize(term_filter=["fluid_mass", "fluid_flux"])
        A, b = general_assembler.assemble_matrix_rhs()

        # Re-discretize and assemble using the Biot class
        A_class, b_class = biot_discretizer.matrix_rhs(g, d, discretize=False)

        # Make sure the variable ordering of the matrix assembled by the assembler
        # matches that of the Biot class.
        grids = [g, g]
        variables = [v_0, v_1]
        A, b = permute_matrix_vector(
            A,
            b,
            general_assembler.block_dof,
            general_assembler.full_dof,
            grids,
            variables,
        )

        # Compare the matrices and rhs vectors
        self.assertTrue(np.all(np.isclose(A.A, A_class.A)))
        self.assertTrue(np.all(np.isclose(b, b_class)))
예제 #22
0
    def test_cart_3d(self):
        g = pp.CartGrid([1, 1, 1])
        g.compute_geometry()

        bot = g.face_centers[2] < 1e-10
        top = g.face_centers[2] > 1 - 1e-10
        south = g.face_centers[1] < 1e-10
        north = g.face_centers[1] > 1 - 1e-10
        west = g.face_centers[0] < 1e-10
        east = g.face_centers[0] > 1 - 1e-10

        is_dir = south + top
        is_neu = east + west
        is_rob = north + bot

        bnd = pp.BoundaryConditionVectorial(g)
        bnd.is_neu[:] = False

        bnd.is_dir[:, is_dir] = True
        bnd.is_rob[:, is_rob] = True
        bnd.is_neu[:, is_neu] = True

        sc_top = pp.fvutils.SubcellTopology(g)
        bnd = pp.fvutils.boundary_to_sub_boundary(bnd, sc_top)
        bnd_excl = pp.fvutils.ExcludeBoundaries(sc_top, bnd, g.dim)

        rhs = pp.Mpsa("")._create_bound_rhs(bnd, bnd_excl, sc_top, g, True)
        hf2f = pp.fvutils.map_hf_2_f(sc_top.fno_unique, sc_top.subfno_unique,
                                     g.dim)
        rhs = rhs * hf2f.T

        rhs_indptr = np.array(
            [
                0,
                1,
                2,
                3,
                4,
                5,
                6,
                7,
                8,
                9,
                10,
                11,
                12,
                13,
                14,
                15,
                16,
                17,
                18,
                19,
                20,
                21,
                22,
                23,
                24,
                25,
                26,
                27,
                28,
                29,
                30,
                31,
                32,
                33,
                34,
                35,
                36,
                37,
                38,
                39,
                40,
                41,
                42,
                43,
                44,
                45,
                46,
                47,
                48,
                49,
                50,
                51,
                52,
                53,
                54,
                55,
                56,
                57,
                58,
                59,
                60,
                61,
                62,
                63,
                64,
                65,
                66,
                67,
                68,
                69,
                70,
                71,
                72,
            ],
            dtype=np.int32,
        )
        rhs_indices = np.array(
            [
                0,
                0,
                0,
                0,
                3,
                3,
                3,
                3,
                1,
                1,
                1,
                1,
                4,
                4,
                4,
                4,
                2,
                2,
                2,
                2,
                5,
                5,
                5,
                5,
                9,
                9,
                9,
                9,
                12,
                12,
                12,
                12,
                10,
                10,
                10,
                10,
                13,
                13,
                13,
                13,
                11,
                11,
                11,
                11,
                14,
                14,
                14,
                14,
                6,
                6,
                6,
                6,
                15,
                15,
                15,
                15,
                7,
                7,
                7,
                7,
                16,
                16,
                16,
                16,
                8,
                8,
                8,
                8,
                17,
                17,
                17,
                17,
            ],
            dtype=np.int32,
        )
        rhs_data = np.array([
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            1,
            -1.0,
            -1.0,
            -1.0,
            -1.0,
            1.0,
            1.0,
            1.0,
            1.0,
            -1.0,
            -1.0,
            -1.0,
            -1.0,
            1.0,
            1.0,
            1.0,
            1.0,
            -1.0,
            -1.0,
            -1.0,
            -1.0,
            1.0,
            1.0,
            1.0,
            1.0,
        ])
        rhs_known = sps.csr_matrix((rhs_data, rhs_indices, rhs_indptr),
                                   shape=(72, 18))
        self.assertTrue(np.all(np.abs((rhs_known - rhs).data) < 1e-12))
예제 #23
0
    def test_mixed_condition(self):
        nx = 2
        ny = 1
        g = pp.CartGrid([nx, ny], physdims=[1, 1])
        g.compute_geometry()

        bot = g.face_centers[1] < 1e-10
        top = g.face_centers[1] > 1 - 1e-10
        left = g.face_centers[0] < 1e-10
        right = g.face_centers[0] > 1 - 1e-10

        bnd = pp.BoundaryConditionVectorial(g)
        bnd.is_neu[:] = False

        bnd.is_dir[0, left] = True
        bnd.is_neu[1, left] = True

        bnd.is_rob[0, right] = True
        bnd.is_dir[1, right] = True

        bnd.is_neu[0, bot] = True
        bnd.is_rob[1, bot] = True

        bnd.is_rob[0, top] = True
        bnd.is_dir[1, top] = True

        sc_top = pp.fvutils.SubcellTopology(g)
        bnd = pp.fvutils.boundary_to_sub_boundary(bnd, sc_top)
        bnd_excl = pp.fvutils.ExcludeBoundaries(sc_top, bnd, g.dim)

        rhs = pp.Mpsa("")._create_bound_rhs(bnd, bnd_excl, sc_top, g, True)
        hf2f = pp.fvutils.map_hf_2_f(sc_top.fno_unique, sc_top.subfno_unique,
                                     g.dim)
        rhs = rhs * hf2f.T

        rhs_known = np.array([
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                1.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                1.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0,
                0.0, 0.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 1.0
            ],
            [
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 1.0
            ],
        ])

        self.assertTrue(np.all(np.abs(rhs_known - rhs) < 1e-12))
예제 #24
0
def discretize(
    grid_bucket,
    data_dictionary,
    parameter_keyword_flow,
    parameter_keyword_mechanics,
    variable_flow,
    variable_mechanics,
):
    """
    Discretize the problem.

    Parameters:
        grid_bucket (PorePy object):          Grid bucket
        data_dictionary (Dict):               Model's data dictionary
        parameter_keyword_flow (String):      Keyword for the flow parameter
        parameter_keyword_mechanics (String): Keyword for the mechanics parameter
        variable_flow (String):               Primary variable of the flow problem
        variable_mechanics (String):          Primary variable of the mechanics problem

    Output:
        assembler (PorePy object):            Assembler containing discretization
    """

    # The Mpfa discretization assumes unit viscosity. Hence we need to
    # overwrite the class to include it.
    class ImplicitMpfa(pp.Mpfa):
        def assemble_matrix_rhs(self, g, d):
            """
            Overwrite MPFA method to be consistent with Biot's
            time discretization and inclusion of viscosity in Darcy's law
            """
            viscosity = d[pp.PARAMETERS][self.keyword]["viscosity"]
            a, b = super().assemble_matrix_rhs(g, d)
            dt = d[pp.PARAMETERS][self.keyword]["time_step"]
            return a * (1 / viscosity) * dt, b * (1 / viscosity) * dt

    # Redefining input parameters
    gb = grid_bucket
    g = gb.grids_of_dimension(2)[0]
    d = data_dictionary
    kw_f = parameter_keyword_flow
    kw_m = parameter_keyword_mechanics
    v_0 = variable_mechanics
    v_1 = variable_flow

    # Discretize the subproblems using Biot's class, which employs
    # MPSA for the mechanics problem and MPFA for the flow problem
    biot_discretizer = pp.Biot(kw_m, kw_f, v_0, v_1)
    biot_discretizer._discretize_mech(g, d)  # discretize mech problem
    biot_discretizer._discretize_flow(g, d)  # discretize flow problem

    # Names of the five terms of the equation + additional stabilization term.
    ####################################### Term in the Biot equation:
    term_00 = "stress_divergence"  ######## div symmetric grad u
    term_01 = "pressure_gradient"  ######## alpha grad p
    term_10 = "displacement_divergence"  ## d/dt alpha div u
    term_11_0 = "fluid_mass"  ############# d/dt beta p
    term_11_1 = "fluid_flux"  ############# div (rho g - K grad p)
    term_11_2 = "stabilization"  ##########

    # Store in the data dictionary and specify discretization objects.
    d[pp.PRIMARY_VARIABLES] = {v_0: {"cells": g.dim}, v_1: {"cells": 1}}
    d[pp.DISCRETIZATION] = {
        v_0: {
            term_00: pp.Mpsa(kw_m)
        },
        v_1: {
            term_11_0: ImplicitMassMatrix(kw_f, v_1),
            term_11_1: ImplicitMpfa(kw_f),
            term_11_2: pp.BiotStabilization(kw_f, v_1),
        },
        v_0 + "_" + v_1: {
            term_01: pp.GradP(kw_m)
        },
        v_1 + "_" + v_0: {
            term_10: pp.DivU(kw_m, kw_f, v_0)
        },
    }

    assembler = pp.Assembler(gb)
    # Discretize the flow and accumulation terms - the other are already handled
    # by the biot_discretizer
    assembler.discretize(term_filter=[term_11_0, term_11_1])

    return assembler
예제 #25
0
    gb = pp.meshing.cart_grid(frac, [9, 3])
    split_scheme = [[np.array([1]), np.array([8])], [np.array([49]), np.array([55])]]
    return gb, split_scheme


# The main test function
@pytest.mark.parametrize(
    "geometry",
    [
        _two_fractures_overlapping_regions,
        _two_fractures_non_overlapping_regions,
        _two_fractures_regions_become_overlapping,
    ],
)
@pytest.mark.parametrize(
    "method", [pp.Mpfa("flow"), pp.Mpsa("mechanics"), pp.Biot("mechanics", "flow")]
)
def test_propagation(geometry, method):
    # Method to test partial discretization (aimed at finite volume methods) under
    # fracture propagation. The test is based on first discretizing, and then do one
    # or several fracture propagation steps. after each step, we do a partial
    # update of the discretization scheme, and compare with a full discretization on
    # the newly split grid. The test fails unless all discretization matrices generated
    # are identical.
    #
    # NOTE: Only the highest-dimensional grid in the GridBucket is used.

    # Get GridBucket and splitting schedule
    gb, faces_to_split = geometry()

    g = gb.grids_of_dimension(gb.dim_max())[0]
예제 #26
0
    def assign_discretisations(self):
        """
        Assign discretizations to the nodes and edges of the grid bucket.

        Note the attribute subtract_fracture_pressure: Indicates whether or not to
        subtract the fracture pressure contribution for the contact traction. This
        should not be done if the scalar variable is temperature.
        """
        # Shorthand
        key_s, key_m = self.scalar_parameter_key, self.mechanics_parameter_key
        var_s, var_d = self.scalar_variable, self.displacement_variable

        # Define discretization
        # For the Nd domain we solve linear elasticity with mpsa.
        mpsa = pp.Mpsa(key_m)
        empty_discr = pp.VoidDiscretization(key_m, ndof_cell=self.Nd)
        # Scalar discretizations (all dimensions)
        diff_disc_s = IE_discretizations.ImplicitMpfa(key_s)
        mass_disc_s = IE_discretizations.ImplicitMassMatrix(key_s, var_s)
        source_disc_s = pp.ScalarSource(key_s)
        # Coupling discretizations
        # All dimensions
        div_u_disc = pp.DivU(
            key_m,
            key_s,
            variable=var_d,
            mortar_variable=self.mortar_displacement_variable,
        )
        # Nd
        grad_p_disc = pp.GradP(key_m)
        stabilization_disc_s = pp.BiotStabilization(key_s, var_s)

        # Assign node discretizations
        for g, d in self.gb:
            if g.dim == self.Nd:
                d[pp.DISCRETIZATION] = {
                    var_d: {
                        "mpsa": mpsa
                    },
                    var_s: {
                        "diffusion": diff_disc_s,
                        "mass": mass_disc_s,
                        "stabilization": stabilization_disc_s,
                        "source": source_disc_s,
                    },
                    var_d + "_" + var_s: {
                        "grad_p": grad_p_disc
                    },
                    var_s + "_" + var_d: {
                        "div_u": div_u_disc
                    },
                }
            elif g.dim == self.Nd - 1:
                d[pp.DISCRETIZATION] = {
                    self.contact_traction_variable: {
                        "empty": empty_discr
                    },
                    var_s: {
                        "diffusion": diff_disc_s,
                        "mass": mass_disc_s,
                        "source": source_disc_s,
                    },
                }

        # Define edge discretizations for the mortar grid
        contact_law = pp.ColoumbContact(self.mechanics_parameter_key, self.Nd)
        contact_discr = pp.PrimalContactCoupling(self.mechanics_parameter_key,
                                                 mpsa, contact_law)
        # Account for the mortar displacements effect on scalar balance in the matrix,
        # as an internal boundary contribution, fracture, aperture changes appear as a
        # source contribution.
        div_u_coupling = pp.DivUCoupling(self.displacement_variable,
                                         div_u_disc, div_u_disc)
        # Account for the pressure contributions to the force balance on the fracture
        # (see contact_discr).
        # This discretization needs the keyword used to store the grad p discretization:
        grad_p_key = key_m
        matrix_scalar_to_force_balance = pp.MatrixScalarToForceBalance(
            grad_p_key, mass_disc_s, mass_disc_s)
        if self.subtract_fracture_pressure:
            fracture_scalar_to_force_balance = pp.FractureScalarToForceBalance(
                mass_disc_s, mass_disc_s)

        for e, d in self.gb.edges():
            g_l, g_h = self.gb.nodes_of_edge(e)

            if g_h.dim == self.Nd:
                d[pp.COUPLING_DISCRETIZATION] = {
                    self.friction_coupling_term: {
                        g_h: (var_d, "mpsa"),
                        g_l: (self.contact_traction_variable, "empty"),
                        (g_h, g_l):
                        (self.mortar_displacement_variable, contact_discr),
                    },
                    self.scalar_coupling_term: {
                        g_h: (var_s, "diffusion"),
                        g_l: (var_s, "diffusion"),
                        e: (
                            self.mortar_scalar_variable,
                            pp.RobinCoupling(key_s, diff_disc_s),
                        ),
                    },
                    "div_u_coupling": {
                        g_h: (
                            var_s,
                            "mass",
                        ),  # This is really the div_u, but this is not implemented
                        g_l: (var_s, "mass"),
                        e: (self.mortar_displacement_variable, div_u_coupling),
                    },
                    "matrix_scalar_to_force_balance": {
                        g_h: (var_s, "mass"),
                        g_l: (var_s, "mass"),
                        e: (
                            self.mortar_displacement_variable,
                            matrix_scalar_to_force_balance,
                        ),
                    },
                }
                if self.subtract_fracture_pressure:
                    d[pp.COUPLING_DISCRETIZATION].update({
                        "matrix_scalar_to_force_balance": {
                            g_h: (var_s, "mass"),
                            g_l: (var_s, "mass"),
                            e: (
                                self.mortar_displacement_variable,
                                fracture_scalar_to_force_balance,
                            ),
                        }
                    })
            else:
                raise ValueError(
                    "assign_discretizations assumes no fracture intersections."
                )
    def test_one_cell_a_time_node_keyword(self):
        # Update one and one cell, and verify that the result is the same as
        # with a single computation. The test is similar to what will happen
        # with a memory-constrained splitting.
        g = pp.CartGrid([3, 3])
        g.compute_geometry()

        # Assign random permeabilities, for good measure
        np.random.seed(42)
        mu = np.random.random(g.num_cells)
        lmbda = np.random.random(g.num_cells)
        stiffness = pp.FourthOrderTensor(mu=mu, lmbda=lmbda)

        stress = sps.csr_matrix((g.num_faces * g.dim, g.num_cells * g.dim))
        bound_stress = sps.csr_matrix(
            (g.num_faces * g.dim, g.num_faces * g.dim))
        faces_covered = np.zeros(g.num_faces, np.bool)

        bnd = pp.BoundaryConditionVectorial(g)
        specified_data = {
            "fourth_order_tensor": stiffness,
            "bc": bnd,
            "inverter": "python",
        }
        keyword = "mechanics"
        data = pp.initialize_default_data(g, {},
                                          keyword,
                                          specified_parameters=specified_data)

        discr = pp.Mpsa(keyword)
        discr.discretize(g, data)

        stress_full = data[pp.DISCRETIZATION_MATRICES][keyword][
            discr.stress_matrix_key]
        bound_stress_full = data[pp.DISCRETIZATION_MATRICES][keyword][
            discr.bound_stress_matrix_key]

        cn = g.cell_nodes()
        for ci in range(g.num_cells):
            ind = np.zeros(g.num_cells)
            ind[ci] = 1
            nodes = np.squeeze(np.where(cn * ind > 0))

            data[pp.PARAMETERS][keyword]["specified_nodes"] = nodes

            discr.discretize(g, data)

            partial_stress = data[pp.DISCRETIZATION_MATRICES][keyword][
                discr.stress_matrix_key]
            partial_bound = data[pp.DISCRETIZATION_MATRICES][keyword][
                discr.bound_stress_matrix_key]

            active_faces = data[pp.PARAMETERS][keyword]["active_faces"]

            if np.any(faces_covered):
                del_faces = self.expand_indices_nd(
                    np.where(faces_covered)[0], g.dim)
                # del_faces is already expanded, set dimension to 1
                pp.fvutils.remove_nonlocal_contribution(
                    del_faces, 1, partial_stress, partial_bound)
            faces_covered[active_faces] = True

            stress += partial_stress
            bound_stress += partial_bound

        self.assertTrue((stress_full - stress).max() < 1e-8)
        self.assertTrue((stress_full - stress).min() > -1e-8)
        self.assertTrue((bound_stress - bound_stress_full).max() < 1e-8)
        self.assertTrue((bound_stress - bound_stress_full).min() > -1e-8)
예제 #28
0
    return gb, split_scheme


# The main test function
@pytest.mark.parametrize(
    "geometry",
    [
        _two_fractures_overlapping_regions,
        _two_fractures_non_overlapping_regions,
        _two_fractures_regions_become_overlapping,
    ],
)
@pytest.mark.parametrize(
    "method",
    [pp.Mpfa("flow"),
     pp.Mpsa("mechanics"),
     pp.Biot("mechanics", "flow")])
def test_propagation(geometry, method):
    # Method to test partial discretization (aimed at finite volume methods) under
    # fracture propagation. The test is based on first discretizing, and then do one
    # or several fracture propagation steps. after each step, we do a partial
    # update of the discretization scheme, and compare with a full discretization on
    # the newly split grid. The test fails unless all discretization matrices generated
    # are identical.
    #
    # NOTE: Only the highest-dimensional grid in the GridBucket is used.

    # Get GridBucket and splitting schedule
    gb, faces_to_split = geometry()

    g = gb.grids_of_dimension(gb.dim_max())[0]
    def _assign_discretizations(self) -> None:
        """
        Assign discretizations to the nodes and edges of the grid bucket.

        Note the attribute subtract_fracture_pressure: Indicates whether or not to
        subtract the fracture pressure contribution for the contact traction. This
        should not be done if the scalar variable is temperature.
        """
        if not self._use_ad:

            # Shorthand
            key_s, key_m = self.scalar_parameter_key, self.mechanics_parameter_key
            var_s, var_d = self.scalar_variable, self.displacement_variable

            # Define discretization
            # For the Nd domain we solve linear elasticity with mpsa.
            mpsa = pp.Mpsa(key_m)
            empty_discr = pp.VoidDiscretization(key_m, ndof_cell=self._Nd)
            # Scalar discretizations (all dimensions)
            diff_disc_s = IE_discretizations.ImplicitMpfa(key_s)
            mass_disc_s = IE_discretizations.ImplicitMassMatrix(key_s, var_s)
            source_disc_s = pp.ScalarSource(key_s)
            # Coupling discretizations
            # All dimensions
            div_u_disc = pp.DivU(
                key_m,
                key_s,
                variable=var_d,
                mortar_variable=self.mortar_displacement_variable,
            )
            # Nd
            grad_p_disc = pp.GradP(key_m)
            stabilization_disc_s = pp.BiotStabilization(key_s, var_s)

            # Assign node discretizations
            for g, d in self.gb:
                if g.dim == self._Nd:
                    d[pp.DISCRETIZATION] = {
                        var_d: {"mpsa": mpsa},
                        var_s: {
                            "diffusion": diff_disc_s,
                            "mass": mass_disc_s,
                            "stabilization": stabilization_disc_s,
                            "source": source_disc_s,
                        },
                        var_d + "_" + var_s: {"grad_p": grad_p_disc},
                        var_s + "_" + var_d: {"div_u": div_u_disc},
                    }

                elif g.dim == self._Nd - 1:
                    d[pp.DISCRETIZATION] = {
                        self.contact_traction_variable: {"empty": empty_discr},
                        var_s: {
                            "diffusion": diff_disc_s,
                            "mass": mass_disc_s,
                            "source": source_disc_s,
                        },
                    }
                else:
                    d[pp.DISCRETIZATION] = {
                        var_s: {
                            "diffusion": diff_disc_s,
                            "mass": mass_disc_s,
                            "source": source_disc_s,
                        }
                    }

            # Define edge discretizations for the mortar grid
            contact_law = pp.ColoumbContact(
                self.mechanics_parameter_key, self._Nd, mpsa
            )
            contact_discr = pp.PrimalContactCoupling(
                self.mechanics_parameter_key, mpsa, contact_law
            )
            # Account for the mortar displacements effect on scalar balance in the matrix,
            # as an internal boundary contribution, fracture, aperture changes appear as a
            # source contribution.
            div_u_coupling = pp.DivUCoupling(
                self.displacement_variable, div_u_disc, div_u_disc
            )
            # Account for the pressure contributions to the force balance on the fracture
            # (see contact_discr).
            # This discretization needs the keyword used to store the grad p discretization:
            grad_p_key = key_m
            matrix_scalar_to_force_balance = pp.MatrixScalarToForceBalance(
                grad_p_key, mass_disc_s, mass_disc_s
            )
            if self.subtract_fracture_pressure:
                fracture_scalar_to_force_balance = pp.FractureScalarToForceBalance(
                    mass_disc_s, mass_disc_s
                )

            for e, d in self.gb.edges():
                g_l, g_h = self.gb.nodes_of_edge(e)

                if g_h.dim == self._Nd:
                    d[pp.COUPLING_DISCRETIZATION] = {
                        self.friction_coupling_term: {
                            g_h: (var_d, "mpsa"),
                            g_l: (self.contact_traction_variable, "empty"),
                            (g_h, g_l): (
                                self.mortar_displacement_variable,
                                contact_discr,
                            ),
                        },
                        self.scalar_coupling_term: {
                            g_h: (var_s, "diffusion"),
                            g_l: (var_s, "diffusion"),
                            e: (
                                self.mortar_scalar_variable,
                                pp.RobinCoupling(key_s, diff_disc_s),
                            ),
                        },
                        "div_u_coupling": {
                            g_h: (
                                var_s,
                                "mass",
                            ),  # This is really the div_u, but this is not implemented
                            g_l: (var_s, "mass"),
                            e: (self.mortar_displacement_variable, div_u_coupling),
                        },
                        "matrix_scalar_to_force_balance": {
                            g_h: (var_s, "mass"),
                            g_l: (var_s, "mass"),
                            e: (
                                self.mortar_displacement_variable,
                                matrix_scalar_to_force_balance,
                            ),
                        },
                    }
                    if self.subtract_fracture_pressure:
                        d[pp.COUPLING_DISCRETIZATION].update(
                            {
                                "fracture_scalar_to_force_balance": {
                                    g_h: (var_s, "mass"),
                                    g_l: (var_s, "mass"),
                                    e: (
                                        self.mortar_displacement_variable,
                                        fracture_scalar_to_force_balance,
                                    ),
                                }
                            }
                        )
                else:
                    d[pp.COUPLING_DISCRETIZATION] = {
                        self.scalar_coupling_term: {
                            g_h: (var_s, "diffusion"),
                            g_l: (var_s, "diffusion"),
                            e: (
                                self.mortar_scalar_variable,
                                pp.RobinCoupling(key_s, diff_disc_s),
                            ),
                        }
                    }

        else:

            gb = self.gb
            Nd = self._Nd
            dof_manager = pp.DofManager(gb)
            eq_manager = pp.ad.EquationManager(gb, dof_manager)

            g_primary = gb.grids_of_dimension(Nd)[0]
            g_frac = gb.grids_of_dimension(Nd - 1).tolist()

            grid_list = [
                g_primary,
                *g_frac,
                *gb.grids_of_dimension(Nd - 2),
                *gb.grids_of_dimension(Nd - 3),
            ]

            if len(gb.grids_of_dimension(Nd)) != 1:
                raise NotImplementedError("This will require further work")

            edge_list_highest = [(g_primary, g) for g in g_frac]
            edge_list = [e for e, _ in gb.edges()]

            mortar_proj_scalar = pp.ad.MortarProjections(edges=edge_list, gb=gb, nd=1)
            mortar_proj_vector = pp.ad.MortarProjections(
                edges=edge_list_highest, gb=gb, nd=self._Nd
            )
            subdomain_proj_scalar = pp.ad.SubdomainProjections(gb=gb)
            subdomain_proj_vector = pp.ad.SubdomainProjections(gb=gb, nd=self._Nd)

            tangential_normal_proj_list = []
            normal_proj_list = []
            for gf in g_frac:
                proj = gb.node_props(gf, "tangential_normal_projection")
                tangential_normal_proj_list.append(
                    proj.project_tangential_normal(gf.num_cells)
                )
                normal_proj_list.append(proj.project_normal(gf.num_cells))

            tangential_normal_proj = pp.ad.Matrix(
                sps.block_diag(tangential_normal_proj_list)
            )
            normal_proj = pp.ad.Matrix(sps.block_diag(normal_proj_list))

            # Ad representation of discretizations
            mpsa_ad = pp.ad.BiotAd(self.mechanics_parameter_key, g_primary)
            grad_p_ad = pp.ad.GradPAd(self.mechanics_parameter_key, g_primary)

            mpfa_ad = pp.ad.MpfaAd(self.scalar_parameter_key, grid_list)
            mass_ad = pp.ad.MassMatrixAd(self.scalar_parameter_key, grid_list)
            robin_ad = pp.ad.RobinCouplingAd(self.scalar_parameter_key, edge_list)

            div_u_ad = pp.ad.DivUAd(
                self.mechanics_parameter_key,
                grids=g_primary,
                mat_dict_keyword=self.scalar_parameter_key,
            )
            stab_biot_ad = pp.ad.BiotStabilizationAd(
                self.scalar_parameter_key, g_primary
            )

            coloumb_ad = pp.ad.ColoumbContactAd(
                self.mechanics_parameter_key, edge_list_highest
            )

            bc_ad = pp.ad.BoundaryCondition(
                self.mechanics_parameter_key, grids=[g_primary]
            )
            div_vector = pp.ad.Divergence(grids=[g_primary], dim=g_primary.dim)

            # Primary variables on Ad form
            u = eq_manager.variable(g_primary, self.displacement_variable)
            u_mortar = eq_manager.merge_variables(
                [(e, self.mortar_displacement_variable) for e in edge_list_highest]
            )
            contact_force = eq_manager.merge_variables(
                [(g, self.contact_traction_variable) for g in g_frac]
            )
            p = eq_manager.merge_variables(
                [(g, self.scalar_variable) for g in grid_list]
            )
            mortar_flux = eq_manager.merge_variables(
                [(e, self.mortar_scalar_variable) for e in edge_list]
            )

            u_prev = u.previous_timestep()
            u_mortar_prev = u_mortar.previous_timestep()
            p_prev = p.previous_timestep()

            # Reference pressure, corresponding to an initial stress free state
            p_reference = pp.ad.ParameterArray(
                param_keyword=self.mechanics_parameter_key,
                array_keyword="p_reference",
                grids=[g_primary],
                gb=gb,
            )

            # Stress in g_h
            stress = (
                mpsa_ad.stress * u
                + mpsa_ad.bound_stress * bc_ad
                + mpsa_ad.bound_stress
                * subdomain_proj_vector.face_restriction(g_primary)
                * mortar_proj_vector.mortar_to_primary_avg
                * u_mortar
                + grad_p_ad.grad_p
                * subdomain_proj_scalar.cell_restriction(g_primary)
                * p
                # The reference pressure is only defined on g_primary, thus there is no need
                # for a subdomain projection.
                - grad_p_ad.grad_p * p_reference
            )

            momentum_eq = pp.ad.Expression(
                div_vector * stress, dof_manager, "momentuum", grid_order=[g_primary]
            )

            jump = (
                subdomain_proj_vector.cell_restriction(g_frac)
                * mortar_proj_vector.mortar_to_secondary_avg
                * mortar_proj_vector.sign_of_mortar_sides
            )
            jump_rotate = tangential_normal_proj * jump

            # Contact conditions
            num_frac_cells = np.sum([g.num_cells for g in g_frac])

            jump_discr = coloumb_ad.displacement * jump_rotate * u_mortar
            tmp = np.ones(num_frac_cells * self._Nd)
            tmp[self._Nd - 1 :: self._Nd] = 0
            exclude_normal = pp.ad.Matrix(
                sps.dia_matrix((tmp, 0), shape=(tmp.size, tmp.size))
            )
            # Rhs of contact conditions
            rhs = (
                coloumb_ad.rhs
                + exclude_normal * coloumb_ad.displacement * jump_rotate * u_mortar_prev
            )
            contact_conditions = coloumb_ad.traction * contact_force + jump_discr - rhs
            contact_eq = pp.ad.Expression(
                contact_conditions, dof_manager, "contact", grid_order=g_frac
            )

            # Force balance
            mat = None
            for _, d in gb.edges():
                mg: pp.MortarGrid = d["mortar_grid"]
                if mg.dim < self._Nd - 1:
                    continue

                faces_on_fracture_surface = mg.primary_to_mortar_int().tocsr().indices
                m = pp.grid_utils.switch_sign_if_inwards_normal(
                    g_primary, self._Nd, faces_on_fracture_surface
                )
                if mat is None:
                    mat = m
                else:
                    mat += m

            sign_switcher = pp.ad.Matrix(mat)

            # Contact from primary grid and mortar displacements (via primary grid)
            contact_from_primary_mortar = (
                mortar_proj_vector.primary_to_mortar_int
                * subdomain_proj_vector.face_prolongation(g_primary)
                * sign_switcher
                * stress
            )
            contact_from_secondary = (
                mortar_proj_vector.sign_of_mortar_sides
                * mortar_proj_vector.secondary_to_mortar_int
                * subdomain_proj_vector.cell_prolongation(g_frac)
                * tangential_normal_proj.transpose()
                * contact_force
            )
            if self.subtract_fracture_pressure:
                # This gives an error because of -=

                mat = []

                for e in edge_list_highest:
                    mg = gb.edge_props(e, "mortar_grid")

                    faces_on_fracture_surface = (
                        mg.primary_to_mortar_int().tocsr().indices
                    )
                    sgn, _ = g_primary.signs_and_cells_of_boundary_faces(
                        faces_on_fracture_surface
                    )
                    fracture_normals = g_primary.face_normals[
                        : self._Nd, faces_on_fracture_surface
                    ]
                    outwards_fracture_normals = sgn * fracture_normals

                    data = outwards_fracture_normals.ravel("F")
                    row = np.arange(g_primary.dim * mg.num_cells)
                    col = np.tile(np.arange(mg.num_cells), (g_primary.dim, 1)).ravel(
                        "F"
                    )
                    n_dot_I = sps.csc_matrix((data, (row, col)))
                    # i) The scalar contribution to the contact stress is mapped to
                    # the mortar grid  and multiplied by n \dot I, with n being the
                    # outwards normals on the two sides.
                    # Note that by using different normals for the two sides, we do not need to
                    # adjust the secondary pressure with the corresponding signs by applying
                    # sign_of_mortar_sides as done in PrimalContactCoupling.
                    # iii) The contribution should be subtracted so that we balance the primary
                    # forces by
                    # T_contact - n dot I p,
                    # hence the minus.
                    mat.append(n_dot_I * mg.secondary_to_mortar_int(nd=1))
                # May need to do this as for tangential projections, additive that is
                normal_matrix = pp.ad.Matrix(sps.block_diag(mat))

                p_frac = subdomain_proj_scalar.cell_restriction(g_frac) * p

                contact_from_secondary2 = normal_matrix * p_frac
            force_balance_eq = pp.ad.Expression(
                contact_from_primary_mortar
                + contact_from_secondary
                + contact_from_secondary2,
                dof_manager,
                "force_balance",
                grid_order=edge_list_highest,
            )

            bc_val_scalar = pp.ad.BoundaryCondition(
                self.scalar_parameter_key, grid_list
            )

            div_scalar = pp.ad.Divergence(grids=grid_list)

            dt = self.time_step

            # FIXME: Need bc for div_u term, including previous time step
            accumulation_primary = (
                div_u_ad.div_u * (u - u_prev)
                + stab_biot_ad.stabilization
                * subdomain_proj_scalar.cell_restriction(g_primary)
                * (p - p_prev)
                + div_u_ad.bound_div_u
                * subdomain_proj_vector.face_restriction(g_primary)
                * mortar_proj_vector.mortar_to_primary_int
                * (u_mortar - u_mortar_prev)
            )

            # Accumulation term on the fractures.
            frac_vol = np.hstack([g.cell_volumes for g in g_frac])
            vol_mat = pp.ad.Matrix(
                sps.dia_matrix((frac_vol, 0), shape=(num_frac_cells, num_frac_cells))
            )
            accumulation_fracs = (
                vol_mat * normal_proj * jump * (u_mortar - u_mortar_prev)
            )

            accumulation_all = mass_ad.mass * (p - p_prev)

            flux = (
                mpfa_ad.flux * p
                + mpfa_ad.bound_flux * bc_val_scalar
                + mpfa_ad.bound_flux
                * mortar_proj_scalar.mortar_to_primary_int
                * mortar_flux
            )
            flow_md = (
                dt
                * (  # Time scaling of flux terms, both inter-dimensional and from
                    # the higher dimension
                    div_scalar * flux
                    - mortar_proj_scalar.mortar_to_secondary_int * mortar_flux
                )
                + accumulation_all
                + subdomain_proj_scalar.cell_prolongation(g_primary)
                * accumulation_primary
                + subdomain_proj_scalar.cell_prolongation(g_frac) * accumulation_fracs
            )

            interface_flow_eq = robin_ad.mortar_scaling * (
                mortar_proj_scalar.primary_to_mortar_avg
                * mpfa_ad.bound_pressure_cell
                * p
                + mortar_proj_scalar.primary_to_mortar_avg
                * mpfa_ad.bound_pressure_face
                * (
                    mortar_proj_scalar.mortar_to_primary_int * mortar_flux
                    + bc_val_scalar
                )
                - mortar_proj_scalar.secondary_to_mortar_avg * p
                + robin_ad.mortar_discr * mortar_flux
            )

            flow_eq = pp.ad.Expression(
                flow_md, dof_manager, "flow on nodes", grid_order=grid_list
            )
            interface_eq = pp.ad.Expression(
                interface_flow_eq,
                dof_manager,
                "flow on interface",
                grid_order=edge_list,
            )

            eq_manager.equations += [
                momentum_eq,
                contact_eq,
                force_balance_eq,
                flow_eq,
                interface_eq,
            ]
            self._eq_manager = eq_manager
예제 #30
0
    def test_assemble_biot_rhs_transient(self):
        """ Test the assembly of a Biot problem with a non-zero rhs using the assembler.

        The test checks whether the discretization matches that of the Biot class and
        that the solution reaches the expected steady state.
        """
        gb = pp.meshing.cart_grid([], [3, 3], physdims=[1, 1])
        g = gb.grids_of_dimension(2)[0]
        d = gb.node_props(g)

        # Parameters identified by two keywords. Non-default parameters of somewhat
        # arbitrary values are assigned to make the test more revealing.
        kw_m = "mechanics"
        kw_f = "flow"
        variable_m = "displacement"
        variable_f = "pressure"
        bound_mech, bound_flow = self.make_boundary_conditions(g)
        val_mech = np.ones(g.dim * g.num_faces)
        val_flow = np.ones(g.num_faces)
        initial_disp, initial_pressure, initial_state = self.make_initial_conditions(
            g, x0=1, y0=2, p0=0)
        dt = 1e0
        biot_alpha = 0.6
        state = {
            variable_f: initial_pressure,
            variable_m: initial_disp,
            kw_m: {
                "bc_values": val_mech
            },
        }
        parameters_m = {
            "bc": bound_mech,
            "bc_values": val_mech,
            "time_step": dt,
            "biot_alpha": biot_alpha,
        }
        parameters_f = {
            "bc": bound_flow,
            "bc_values": val_flow,
            "time_step": dt,
            "biot_alpha": biot_alpha,
            "mass_weight": 0.1 * np.ones(g.num_cells),
        }
        pp.initialize_default_data(g, d, kw_m, parameters_m)
        pp.initialize_default_data(g, d, kw_f, parameters_f)
        pp.set_state(d, state)
        # Initial condition fot the Biot class
        #        d["state"] = initial_state

        # Set up the structure for the assembler. First define variables and equation
        # term names.
        v_0 = variable_m
        v_1 = variable_f
        term_00 = "stress_divergence"
        term_01 = "pressure_gradient"
        term_10 = "displacement_divergence"
        term_11_0 = "fluid_mass"
        term_11_1 = "fluid_flux"
        term_11_2 = "stabilization"
        d[pp.PRIMARY_VARIABLES] = {v_0: {"cells": g.dim}, v_1: {"cells": 1}}
        d[pp.DISCRETIZATION] = {
            v_0: {
                term_00: pp.Mpsa(kw_m)
            },
            v_1: {
                term_11_0: IE_discretizations.ImplicitMassMatrix(kw_f, v_1),
                term_11_1: IE_discretizations.ImplicitMpfa(kw_f),
                term_11_2: pp.BiotStabilization(kw_f),
            },
            v_0 + "_" + v_1: {
                term_01: pp.GradP(kw_m)
            },
            v_1 + "_" + v_0: {
                term_10: pp.DivU(kw_m)
            },
        }

        # Discretize the mechanics related terms using the Biot class
        biot_discretizer = pp.Biot()
        biot_discretizer._discretize_mech(g, d)

        general_assembler = pp.Assembler(gb)
        # Discretize terms that are not handled by the call to biot_discretizer
        general_assembler.discretize(term_filter=["fluid_mass", "fluid_flux"])

        times = np.arange(5)
        for _ in times:
            # Assemble. Also discretizes the flow terms (fluid_mass and fluid_flux)
            A, b = general_assembler.assemble_matrix_rhs()

            # Assemble using the Biot class
            A_class, b_class = biot_discretizer.matrix_rhs(g,
                                                           d,
                                                           discretize=False)

            # Make sure the variable ordering of the matrix assembled by the assembler
            # matches that of the Biot class.
            grids = [g, g]
            variables = [v_0, v_1]
            A, b = permute_matrix_vector(
                A,
                b,
                general_assembler.block_dof,
                general_assembler.full_dof,
                grids,
                variables,
            )

            # Compare the matrices and rhs vectors
            self.assertTrue(np.all(np.isclose(A.A, A_class.A)))
            self.assertTrue(np.all(np.isclose(b, b_class)))

            # Store the current solution for the next time step.
            x_i = sps.linalg.spsolve(A_class, b_class)
            u_i = x_i[:(g.dim * g.num_cells)]
            p_i = x_i[(g.dim * g.num_cells):]
            state = {variable_f: p_i, variable_m: u_i}
            pp.set_state(d, state)

        # Check that the solution has converged to the expected, uniform steady state
        # dictated by the BCs.
        self.assertTrue(
            np.all(np.isclose(x_i, np.ones((g.dim + 1) * g.num_cells))))