Esempio n. 1
0
    def set_param_flow(self, gb, no_flow=False, kn=1e3, method="mpfa"):
        # Set up flow field with uniform flow in y-direction
        kw = "flow"
        for g, d in gb:
            parameter_dictionary = {}

            perm = pp.SecondOrderTensor(kxx=np.ones(g.num_cells))
            parameter_dictionary["second_order_tensor"] = perm

            b_val = np.zeros(g.num_faces)
            if g.dim == 2:
                bound_faces = pp.face_on_side(g, ["ymin", "ymax"])
                if no_flow:
                    b_val[bound_faces[0]] = 1
                    b_val[bound_faces[1]] = 1
                bound_faces = np.hstack((bound_faces[0], bound_faces[1]))
                labels = np.array(["dir"] * bound_faces.size)
                parameter_dictionary["bc"] = pp.BoundaryCondition(
                    g, bound_faces, labels)

                y_max_faces = pp.face_on_side(g, "ymax")[0]
                b_val[y_max_faces] = 1
            else:
                parameter_dictionary["bc"] = pp.BoundaryCondition(g)
            parameter_dictionary["bc_values"] = b_val
            parameter_dictionary["mpfa_inverter"] = "python"

            d[pp.PARAMETERS] = pp.Parameters(g, [kw], [parameter_dictionary])
            d[pp.DISCRETIZATION_MATRICES] = {"flow": {}}

        gb.add_edge_props("kn")
        for e, d in gb.edges():
            mg = d["mortar_grid"]
            flow_dictionary = {
                "normal_diffusivity": 2 * kn * np.ones(mg.num_cells)
            }
            d[pp.PARAMETERS] = pp.Parameters(keywords=["flow"],
                                             dictionaries=[flow_dictionary])
            d[pp.DISCRETIZATION_MATRICES] = {"flow": {}}

        discretization_key = kw + "_" + pp.DISCRETIZATION

        for g, d in gb:
            # Choose discretization and define the solver
            if method == "mpfa":
                discr = pp.Mpfa(kw)
            elif method == "mvem":
                discr = pp.MVEM(kw)
            else:
                discr = pp.Tpfa(kw)

            d[discretization_key] = discr

        for _, d in gb.edges():
            d[discretization_key] = pp.RobinCoupling(kw, discr)
Esempio n. 2
0
    def disabled_test_mono_equals_multi(self):
        """
        test that the mono_dimensional elliptic solver gives the same answer as
        the grid bucket elliptic
        """
        g = pp.CartGrid([10, 10])
        g.compute_geometry()
        gb = pp.meshing.cart_grid([], [10, 10])
        param_g = pp.Parameters(g)

        def bc_val(g):
            left = g.face_centers[0] < 1e-6
            right = g.face_centers[0] > 10 - 1e-6

            bc_val = np.zeros(g.num_faces)
            bc_val[left] = -1
            bc_val[right] = 1
            return bc_val

        def bc_labels(g):
            bound_faces = g.tags["domain_boundary_faces"].nonzero()[0]
            bound_face_centers = g.face_centers[:, bound_faces]
            left = bound_face_centers[0] < 1e-6
            right = bound_face_centers[0] > 10 - 1e-6

            labels = np.array(["neu"] * bound_faces.size)
            labels[np.logical_or(right, left)] = "dir"
            bc_labels = pp.BoundaryCondition(g, bound_faces, labels)

            return bc_labels

        param_g.set_bc_val("flow", bc_val(g))
        param_g.set_bc("flow", bc_labels(g))

        gb.add_node_props(["param"])
        for sub_g, d in gb:
            d["param"] = pp.Parameters(sub_g)
            d["param"].set_bc_val("flow", bc_val(g))
            d["param"].set_bc("flow", bc_labels(sub_g))

        for e, d in gb.edges():
            gl, _ = gb.nodes_of_edge(e)
            d_l = gb.node_props(gl)
            d["kn"] = 1.0 / np.mean(d_l["param"].get_aperture())

        problem_mono = pp.EllipticModel(g, {"param": param_g})
        problem_mult = pp.EllipticModel(gb)

        p_mono = problem_mono.solve()
        p_mult = problem_mult.solve()

        self.assertTrue(np.allclose(p_mono, p_mult))
Esempio n. 3
0
    def test_non_zero_bc_val(self):
        """
        We mixed bc_val on domain boundary and fracture displacement in
        x-direction.
        """
        frac = np.array([[1, 1, 1], [1, 2, 1], [2, 2, 1], [2, 1, 1]]).T
        physdims = np.array([3, 3, 2])

        g = pp.meshing.cart_grid([frac], [3, 3, 2],
                                 physdims=physdims).grids_of_dimension(3)[0]
        data = {"param": pp.Parameters(g)}

        # Define boundary conditions
        bc_val = np.zeros((g.dim, g.num_faces))
        frac_slip = np.zeros((g.dim, g.num_faces))

        frac_bnd = g.tags["fracture_faces"]
        dom_bnd = g.tags["domain_boundary_faces"]

        frac_slip[0, frac_bnd] = np.ones(np.sum(frac_bnd))
        bc_val[:, dom_bnd] = g.face_centers[:, dom_bnd]

        bound = pp.BoundaryCondition(g, g.get_all_boundary_faces(), "dir")

        data["param"].set_bc("mechanics", bound)
        data["param"].set_bc_val("mechanics", bc_val.ravel("F"))
        data["param"].set_slip_distance(frac_slip.ravel("F"))
        solver = pp.FracturedMpsa()

        A, b = solver.matrix_rhs(g, data)
        u = np.linalg.solve(A.A, b)

        u_f = solver.extract_frac_u(g, u)
        u_c = solver.extract_u(g, u)
        u_c = u_c.reshape((3, -1), order="F")

        # Test traction
        frac_faces = g.frac_pairs
        frac_left = frac_faces[0]
        frac_right = frac_faces[1]

        T = solver.traction(g, data, u)
        T = T.reshape((3, -1), order="F")
        T_left = T[:, frac_left]
        T_right = T[:, frac_right]

        self.assertTrue(np.allclose(T_left, T_right))

        # we have u_lhs - u_rhs = 1 so u_lhs should be positive
        mid_ind = int(round(u_f.size / 2))
        u_left = u_f[:mid_ind]
        u_right = u_f[mid_ind:]

        true_diff = np.atleast_2d(np.array([1, 0, 0])).T
        u_left = u_left.reshape((3, -1), order="F")
        u_right = u_right.reshape((3, -1), order="F")
        self.assertTrue(np.all(np.abs(u_left - u_right - true_diff) < 1e-10))

        # should have a positive displacement for all cells
        self.assertTrue(np.all(u_c > 0))
Esempio n. 4
0
    def test_p1_1d(self):
        g = pp.structured.CartGrid(1, 1)
        g.compute_geometry()

        kxx = np.ones(g.num_cells)
        perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1)
        bn = g.get_boundary_nodes()
        bc = pp.BoundaryConditionNode(g, bn, bn.size * ["neu"])

        solver = pp.P1(physics="flow")

        param = pp.Parameters(g)
        param.set_tensor(solver, perm)
        param.set_bc(solver, bc)
        M = solver.matrix(g, {"param": param}).todense()

        M_known = np.matrix([[1., -1.], [-1., 1.]])

        self.assertTrue(np.allclose(M, M_known))

        solver = pp.P1MassMatrix(physics="flow")
        M = solver.matrix(g, {"param": param}).todense()

        M_known = np.matrix([[2., 1.], [1., 2.]]) / 6.

        self.assertTrue(np.allclose(M, M_known))
Esempio n. 5
0
def setup_cart_2d(nx):
    frac1 = np.array([[0.2, 0.8], [0.5, 0.5]])
    frac2 = np.array([[0.5, 0.5], [0.8, 0.2]])
    fracs = [frac1, frac2]
    gb = pp.meshing.cart_grid(fracs, nx, physdims=[1, 1])
    gb.compute_geometry()
    gb.assign_node_ordering()
    kw = "flow"
    aperture = 0.01 / np.max(nx)
    for g, d in gb:
        a = np.power(aperture, gb.dim_max() - g.dim) * np.ones(g.num_cells)
        kxx = np.ones(g.num_cells) * a
        perm = pp.SecondOrderTensor(kxx)
        specified_parameters = {"second_order_tensor": perm}

        if g.dim == 2:
            bound_faces = g.tags["domain_boundary_faces"].nonzero()[0]
            bound = pp.BoundaryCondition(g, bound_faces.ravel("F"),
                                         ["dir"] * bound_faces.size)
            bc_val = np.zeros(g.num_faces)
            bc_val[bound_faces] = g.face_centers[1, bound_faces]
            specified_parameters.update({"bc": bound, "bc_values": bc_val})
        # Initialize data and matrix dictionaries in d
        pp.initialize_default_data(g, d, kw, specified_parameters)

    for e, d in gb.edges():
        # Compute normal permeability
        gl, _ = gb.nodes_of_edge(e)
        kn = 1.0 / aperture
        data = {"normal_diffusivity": kn}
        # Add parameters
        d[pp.PARAMETERS] = pp.Parameters(keywords=[kw], dictionaries=[data])
        # Initialize matrix dictionary
        d[pp.DISCRETIZATION_MATRICES] = {kw: {}}
    return gb
Esempio n. 6
0
    def test_rt0_1d_ani(self):
        g = pp.structured.CartGrid(3, 1)
        g.compute_geometry()

        kxx = 1. / (np.sin(g.cell_centers[0, :]) + 1)
        perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1)
        bf = g.get_boundary_faces()
        bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"])
        solver = pp.RT0(physics="flow")

        param = pp.Parameters(g)
        param.set_tensor(solver, perm)
        param.set_bc(solver, bc)
        M = solver.matrix(g, {"param": param}).todense()

        M_known = np.matrix(
            [
                [0.12954401, 0.06477201, 0., 0., 1., 0., 0.],
                [0.06477201, 0.29392463, 0.08219031, 0., -1., 1., 0.],
                [0., 0.08219031, 0.3577336, 0.09667649, 0., -1., 1.],
                [0., 0., 0.09667649, 0.19335298, 0., 0., -1.],
                [1., -1., 0., 0., 0., 0., 0.],
                [0., 1., -1., 0., 0., 0., 0.],
                [0., 0., 1., -1., 0., 0., 0.],
            ]
        )

        self.assertTrue(np.allclose(M, M.T))
        self.assertTrue(np.allclose(M, M_known))
Esempio n. 7
0
    def initial_condition(self) -> None:
        """
        Initial value for the Darcy fluxes. TODO: Add to THM.
        """
        for g, d in self.gb:
            d[pp.PARAMETERS] = pp.Parameters()
            d[pp.PARAMETERS].update_dictionaries(
                [self.mechanics_parameter_key, self.scalar_parameter_key,]
            )
        self.update_all_apertures(to_iterate=False)
        self.update_all_apertures()
        super().initial_condition()

        for g, d in self.gb:

            d[pp.STATE]["cell_centers"] = g.cell_centers.copy()
            p0 = self.initial_scalar(g)
            state = {
                self.scalar_variable: p0,
                "u_exp_0": np.zeros(g.num_cells),
                "aperture_0": self.aperture(g) * self.length_scale,
            }
            iterate = {
                self.scalar_variable: p0,
            }  # For initial flux

            pp.set_state(d, state)
            pp.set_iterate(d, iterate)
Esempio n. 8
0
    def test_periodic_bc_with_fractues(self):
        """
        We set up the test case      P
                                |--------|
                                | g2|    |
                              D | g3x  g0| D
                                | g1|    |
                                |--------|
                                    P

        where D are dirichlet boundaries and P the periodic boundaries.
        We construct periodic solution
               p = sin(pi/xmax * (x - x0)) * cos(2*pi/ymax * (y - y0))
        which gives a source term on the rhs.

        The domain is a 2D domain with two fractures (g1, and g2) connected
        through the periodic bc and 0D intersection (g3)
        """
        n = 8
        xmax = 1
        ymax = 1
        gb = self.generate_grid_with_fractures(n, xmax, ymax)
        tol = 1e-6

        def analytic_p(x):
            x = x.copy()
            shiftx = xmax / 4
            shifty = ymax / 3
            x[0] = x[0] - shiftx
            x[1] = x[1] - shifty
            p = np.sin(np.pi / xmax * x[0]) * np.cos(2 * np.pi / ymax * x[1])
            px = ((np.pi / xmax) * np.cos(np.pi / xmax * x[0]) *
                  np.cos(2 * np.pi / ymax * x[1]))
            py = (2 * np.pi / ymax * np.sin(np.pi / xmax * x[0]) *
                  np.sin(2 * np.pi / ymax * x[1]))
            pxx = -(np.pi / xmax)**2 * p
            pyy = -(2 * np.pi / ymax)**2 * p
            return p, np.vstack([px, py]), pxx + pyy

        for g, d in gb:
            d["param"] = pp.Parameters(g)
            left = g.face_centers[0] < tol
            right = g.face_centers[0] > xmax - tol
            dir_bc = left + right
            d["param"].set_bc("flow", pp.BoundaryCondition(g, dir_bc, "dir"))
            bc_val = np.zeros(g.num_faces)
            bc_val[dir_bc], _, _ = analytic_p(g.face_centers[:, dir_bc])

            d["param"].set_bc_val("flow", bc_val)
            aperture = 1e-6**(2 - g.dim)
            d["param"].set_aperture(aperture)

            pa, _, lpc = analytic_p(g.cell_centers)
            src = -lpc * g.cell_volumes * aperture
            d["param"].set_source("flow", src)

        for _, d in gb.edges():
            d["kn"] = 1e10

        self.solve(gb, analytic_p)
Esempio n. 9
0
    def _set_data(self):
        """Create a Parameter object and assign data based on the returned
        values from the functions (e.g., self.source(t))
        """
        if "param" not in self._data:
            self._data["param"] = pp.Parameters(self.grid())
        self._data["param"].set_tensor(self.physics, self.diffusivity())
        self._data["param"].set_bc(self.physics, self.bc())
        self._data["param"].set_bc_val(self.physics, self.bc_val(0.0))
        self._data["param"].set_source(self.physics, self.source(0.0))

        if self.porosity() is not None:
            self._data["param"].set_porosity(self.porosity())
        if self.aperture() is not None:
            self._data["param"].set_aperture(self.aperture())
        if self.rock_specific_heat() is not None:
            self._data["param"].set_rock_specific_heat(
                self.rock_specific_heat())
        if self.fluid_specific_heat() is not None:
            self._data["param"].set_fluid_specific_heat(
                self.fluid_specific_heat())
        if self.rock_density() is not None:
            self._data["param"].set_rock_density(self.rock_density())
        if self.fluid_density() is not None:
            self._data["param"].set_fluid_density(self.fluid_density())
def add_constant_darcy_flux(gb, upwind, flux, a):
    """
    Adds the constant darcy_flux to all internal and mortar faces, the latter
    in the "mortar_solution" field.
    gb - grid bucket
    upwind- upwind discretization class
    flux - 3 x 1 flux at all faces [u, v, w]
    a - cross-sectional area of the fractures.
    """
    for g, d in gb:
        aperture = np.ones(g.num_cells) * np.power(a, gb.dim_max() - g.dim)
        d[pp.PARAMETERS]["transport"]["darcy_flux"] = upwind.darcy_flux(
            g, flux, aperture)
    for e, d in gb.edges():
        g_h = gb.nodes_of_edge(e)[1]
        p_h = gb.node_props(g_h, pp.PARAMETERS)
        darcy_flux = p_h["transport"]["darcy_flux"]
        sign = np.zeros(g_h.num_faces)
        sign[g_h.get_all_boundary_faces()] = g_h.sign_of_faces(
            g_h.get_all_boundary_faces())
        mg = d["mortar_grid"]
        sign = mg.master_to_mortar_avg() * sign
        #        d["param"] = pp.Parameters(g_h)
        darcy_flux_e = sign * (d["mortar_grid"].master_to_mortar_avg() *
                               darcy_flux)
        if pp.PARAMETERS not in d:
            d[pp.PARAMETERS] = pp.Parameters(mg, ["transport"],
                                             [{
                                                 "darcy_flux": darcy_flux_e
                                             }])
        else:
            d[pp.PARAMETERS]["transport"]["darcy_flux"] = darcy_flux_e
Esempio n. 11
0
    def test_convergence_rt0_2d_iso_simplex_exact(self):

        p_ex = lambda pt: 2 * pt[0, :] - 3 * pt[1, :] - 9

        for i in np.arange(5):
            g = pp.simplex.StructuredTriangleGrid([3 + i] * 2, [1, 1])
            g.compute_geometry()

            kxx = np.ones(g.num_cells)
            perm = pp.SecondOrderTensor(3, kxx=kxx, kyy=kxx, kzz=1)
            bf = g.get_boundary_faces()
            bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"])
            bc_val = np.zeros(g.num_faces)
            bc_val[bf] = p_ex(g.face_centers[:, bf])

            solver = pp.RT0(physics="flow")

            param = pp.Parameters(g)
            param.set_tensor(solver, perm)
            param.set_bc(solver, bc)
            param.set_bc_val(solver, bc_val)
            M, rhs = solver.matrix_rhs(g, {"param": param})

            up = sps.linalg.spsolve(M, rhs)
            p = solver.extract_p(g, up)
            err = np.sum(np.abs(p - p_ex(g.cell_centers)))

            self.assertTrue(np.isclose(err, 0))
Esempio n. 12
0
    def test_rt0_1d_iso(self):
        g = pp.structured.CartGrid(3, 1)
        g.compute_geometry()

        kxx = np.ones(g.num_cells)
        perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1)
        bf = g.get_boundary_faces()
        bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"])

        solver = pp.RT0(physics="flow")

        param = pp.Parameters(g)
        param.set_tensor(solver, perm)
        param.set_bc(solver, bc)
        M = solver.matrix(g, {"param": param}).todense()

        M_known = np.matrix(
            [
                [0.11111111, 0.05555556, 0., 0., 1., 0., 0.],
                [0.05555556, 0.22222222, 0.05555556, 0., -1., 1., 0.],
                [0., 0.05555556, 0.22222222, 0.05555556, 0., -1., 1.],
                [0., 0., 0.05555556, 0.11111111, 0., 0., -1.],
                [1., -1., 0., 0., 0., 0., 0.],
                [0., 1., -1., 0., 0., 0., 0.],
                [0., 0., 1., -1., 0., 0., 0.],
            ]
        )

        self.assertTrue(np.allclose(M, M.T))
        self.assertTrue(np.allclose(M, M_known))
Esempio n. 13
0
    def test_dual_rt0_1d_iso_line(self):
        g = pp.structured.CartGrid(3, 1)
        R = cg.rot(np.pi / 6., [0, 0, 1])
        g.nodes = np.dot(R, g.nodes)
        g.compute_geometry()

        kxx = np.ones(g.num_cells)
        perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1)
        perm.rotate(R)

        bf = g.get_boundary_faces()
        bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"])
        solver = pp.RT0(physics="flow")

        param = pp.Parameters(g)
        param.set_tensor(solver, perm)
        param.set_bc(solver, bc)
        M = solver.matrix(g, {"param": param}).todense()

        # Matrix computed with an already validated code
        M_known = np.matrix(
            [
                [0.11111111, 0.05555556, 0., 0., 1., 0., 0.],
                [0.05555556, 0.22222222, 0.05555556, 0., -1., 1., 0.],
                [0., 0.05555556, 0.22222222, 0.05555556, 0., -1., 1.],
                [0., 0., 0.05555556, 0.11111111, 0., 0., -1.],
                [1., -1., 0., 0., 0., 0., 0.],
                [0., 1., -1., 0., 0., 0., 0.],
                [0., 0., 1., -1., 0., 0., 0.],
            ]
        )

        self.assertTrue(np.allclose(M, M.T))
        self.assertTrue(np.allclose(M, M_known))
Esempio n. 14
0
    def test_p1_convergence_1d_exact(self):

        p_ex = lambda pt: 2 * pt[0, :] - 3

        for i in np.arange(5):
            g = pp.structured.CartGrid(3 + i, 1)
            g.compute_geometry()

            kxx = np.ones(g.num_cells)
            perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1)
            bn = g.get_boundary_nodes()
            bc = pp.BoundaryConditionNode(g, bn, bn.size * ["dir"])
            bc_val = np.zeros(g.num_nodes)
            bc_val[bn] = p_ex(g.nodes[:, bn])

            solver = pp.P1(physics="flow")

            param = pp.Parameters(g)
            param.set_tensor(solver, perm)
            param.set_bc(solver, bc)
            param.set_bc_val(solver, bc_val)
            M, rhs = solver.matrix_rhs(g, {"param": param})

            p = sps.linalg.spsolve(M, rhs)
            err = np.sum(np.abs(p - p_ex(g.nodes)))

            self.assertTrue(np.isclose(err, 0))
Esempio n. 15
0
def initialize_data(g,
                    data: Dict,
                    keyword: str,
                    specified_parameters: Optional[Dict] = None) -> Dict:
    """Initialize a data dictionary for a single keyword.

    The initialization consists of adding a parameter dictionary and initializing a
    matrix dictionary in the proper fields of data. If there is a Parameters object
    in data, the new keyword is added using the update_dictionaries method.

    Args:
        g: The grid. Can be either standard grid, or mortar grid.
        data: Outer data dictionary, to which the parameters will be added.
        keyword: String identifying the parameters.
        specified_parameters: A dictionary with specified parameters, defaults to empty
            dictionary.

    Returns:
        data: The filled dictionary.
    """
    if not specified_parameters:
        specified_parameters = {}
    add_discretization_matrix_keyword(data, keyword)
    if pp.PARAMETERS in data:
        data[pp.PARAMETERS].update_dictionaries([keyword],
                                                [specified_parameters])
    else:
        data[pp.PARAMETERS] = pp.Parameters(g, [keyword],
                                            [specified_parameters])
    return data
Esempio n. 16
0
    def test_rt0_2d_iso_simplex_surf(self):
        g = pp.simplex.StructuredTriangleGrid([1, 1], [1, 1])
        R = cg.rot(-np.pi / 4., [1, 1, -1])
        g.nodes = np.dot(R, g.nodes)
        g.compute_geometry()

        kxx = np.ones(g.num_cells)
        perm = pp.SecondOrderTensor(3, kxx=kxx, kyy=kxx, kzz=1)
        perm.rotate(R)

        bf = g.get_boundary_faces()
        bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"])
        solver = pp.RT0(physics="flow")

        param = pp.Parameters(g)
        param.set_tensor(solver, perm)
        param.set_bc(solver, bc)
        M = solver.matrix(g, {"param": param}).todense()

        # Matrix computed with an already validated code
        M_known = np.matrix(
            [
                [0.33333333, 0., 0., -0.16666667, 0., -1., 0.],
                [0., 0.33333333, 0., 0., -0.16666667, 0., -1.],
                [0., 0., 0.33333333, 0., 0., -1., 1.],
                [-0.16666667, 0., 0., 0.33333333, 0., -1., 0.],
                [0., -0.16666667, 0., 0., 0.33333333, 0., -1.],
                [-1., 0., -1., -1., 0., 0., 0.],
                [0., -1., 1., 0., -1., 0., 0.],
            ]
        )

        self.assertTrue(np.allclose(M, M.T))
        # We test only the mass-Hdiv part
        self.assertTrue(np.allclose(M, M_known))
Esempio n. 17
0
    def test_p1_2d_ani_simplex_surf(self):
        g = pp.simplex.StructuredTriangleGrid([1, 1], [1, 1])
        g.compute_geometry()

        kxx = np.square(g.cell_centers[1, :]) + 1
        kyy = np.square(g.cell_centers[0, :]) + 1
        kxy = -np.multiply(g.cell_centers[0, :], g.cell_centers[1, :])
        perm = pp.SecondOrderTensor(3, kxx=kxx, kyy=kyy, kxy=kxy, kzz=1)

        R = cg.rot(np.pi / 3., [1, 1, 0])
        perm.rotate(R)
        g.nodes = np.dot(R, g.nodes)
        g.compute_geometry()

        bn = g.get_boundary_nodes()
        bc = pp.BoundaryConditionNode(g, bn, bn.size * ["neu"])
        solver = pp.P1(physics="flow")

        param = pp.Parameters(g)
        param.set_tensor(solver, perm)
        param.set_bc(solver, bc)
        M = solver.matrix(g, {"param": param}).todense()

        # Matrix computed with an already validated code
        M_known = np.matrix(
            [
                [1.11111111, -0.66666667, -0.66666667, 0.22222222],
                [-0.66666667, 1.5, 0., -0.83333333],
                [-0.66666667, 0., 1.5, -0.83333333],
                [0.22222222, -0.83333333, -0.83333333, 1.44444444],
            ]
        )

        self.assertTrue(np.allclose(M, M.T))
        self.assertTrue(np.allclose(M, M_known))
Esempio n. 18
0
    def test_p1_1d_ani(self):
        g = pp.structured.CartGrid(3, 1)
        g.compute_geometry()

        kxx = np.sin(g.cell_centers[0, :]) + 1
        perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1)
        bn = g.get_boundary_nodes()
        bc = pp.BoundaryConditionNode(g, bn, bn.size * ["neu"])
        solver = pp.P1(physics="flow")

        param = pp.Parameters(g)
        param.set_tensor(solver, perm)
        param.set_bc(solver, bc)
        M = solver.matrix(g, {"param": param}).todense()

        M_known = np.matrix(
            [
                [3.4976884, -3.4976884, 0., 0.],
                [-3.4976884, 7.93596501, -4.43827662, 0.],
                [0., -4.43827662, 9.65880718, -5.22053056],
                [0., 0., -5.22053056, 5.22053056],
            ]
        )

        self.assertTrue(np.allclose(M, M.T))
        self.assertTrue(np.allclose(M, M_known))
Esempio n. 19
0
    def test_rt0_2d_ani_simplex(self):
        g = pp.simplex.StructuredTriangleGrid([1, 1], [1, 1])
        g.compute_geometry()

        al = np.square(g.cell_centers[1, :]) + np.square(g.cell_centers[0, :]) + 1
        kxx = (np.square(g.cell_centers[0, :]) + 1) / al
        kyy = (np.square(g.cell_centers[1, :]) + 1) / al
        kxy = np.multiply(g.cell_centers[0, :], g.cell_centers[1, :]) / al

        perm = pp.SecondOrderTensor(3, kxx=kxx, kyy=kyy, kxy=kxy, kzz=1)

        bf = g.get_boundary_faces()
        bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"])
        solver = pp.RT0(physics="flow")

        param = pp.Parameters(g)
        param.set_tensor(solver, perm)
        param.set_bc(solver, bc)
        M = solver.matrix(g, {"param": param}).todense()

        # Matrix computed with an already validated code
        M_known = np.matrix(
            [
                [0.39814815, 0., -0.0462963, -0.15740741, 0., -1., 0.],
                [0., 0.39814815, 0.0462963, 0., -0.15740741, 0., -1.],
                [-0.0462963, 0.0462963, 0.46296296, 0.00925926, -0.00925926, -1., 1.],
                [-0.15740741, 0., 0.00925926, 0.34259259, 0., -1., 0.],
                [0., -0.15740741, -0.00925926, 0., 0.34259259, 0., -1.],
                [-1., 0., -1., -1., 0., 0., 0.],
                [0., -1., 1., 0., -1., 0., 0.],
            ]
        )

        self.assertTrue(np.allclose(M, M.T))
        self.assertTrue(np.allclose(M, M_known))
Esempio n. 20
0
def setup_cart_2d(nx):
    frac1 = np.array([[0.2, 0.8], [0.5, 0.5]])
    frac2 = np.array([[0.5, 0.5], [0.8, 0.2]])
    fracs = [frac1, frac2]
    gb = pp.meshing.cart_grid(fracs, nx, physdims=[1, 1])
    gb.compute_geometry()
    gb.assign_node_ordering()
    gb.add_node_props(["param"])
    for g, d in gb:
        kxx = np.ones(g.num_cells)
        perm = pp.SecondOrderTensor(gb.dim_max(), kxx)
        a = 0.01 / np.max(nx)
        a = np.power(a, gb.dim_max() - g.dim)
        param = pp.Parameters(g)
        param.set_tensor("flow", perm)
        param.set_aperture(a)
        if g.dim == 2:
            bound_faces = g.tags["domain_boundary_faces"].nonzero()[0]
            bound = pp.BoundaryCondition(g, bound_faces.ravel("F"),
                                         ["dir"] * bound_faces.size)
            bc_val = np.zeros(g.num_faces)
            bc_val[bound_faces] = g.face_centers[1, bound_faces]
            param.set_bc("flow", bound)
            param.set_bc_val("flow", bc_val)

        d["param"] = param

    for e, d in gb.edges():
        gl, _ = gb.nodes_of_edge(e)
        d_l = gb.node_props(gl)
        d["kn"] = 1. / np.mean(d_l["param"].get_aperture())

    return gb
Esempio n. 21
0
    def test_two_cart_grids(self):
        """
        We set up the test case -----|---------
                                |    |        |
                                | g1 |    g2  |
                                |    |        |
                                -----|---------
        with a linear pressure increase from left to right
        """
        n = 2
        xmax = 3
        ymax = 1
        split = 2
        gb = self.generate_grids(n, xmax, ymax, split)
        tol = 1e-6
        for g, d in gb:
            left = g.face_centers[0] < tol
            right = g.face_centers[0] > xmax - tol
            dir_bc = left + right
            bound = pp.BoundaryCondition(g, dir_bc, "dir")
            bc_val = np.zeros(g.num_faces)
            bc_val[left] = xmax
            bc_val[right] = 0
            specified_parameters = {"bc": bound, "bc_values": bc_val}
            pp.initialize_default_data(g, d, "flow", specified_parameters)

        for e, d in gb.edges():
            mg = d["mortar_grid"]
            d[pp.PARAMETERS] = pp.Parameters(mg, ["flow"], [{}])
            pp.params.data.add_discretization_matrix_keyword(d, "flow")
        # assign discretization
        data_key = "flow"
        tpfa = pp.Tpfa(data_key)
        coupler = pp.FluxPressureContinuity(data_key, tpfa)
        assembler = test_utils.setup_flow_assembler(gb,
                                                    tpfa,
                                                    data_key,
                                                    coupler=coupler)
        test_utils.solve_and_distribute_pressure(gb, assembler)

        # test pressure
        for g, d in gb:
            self.assertTrue(
                np.allclose(d[pp.STATE]["pressure"], xmax - g.cell_centers[0]))

        # test mortar solution
        for e, d_e in gb.edges():
            mg = d_e["mortar_grid"]
            g2, g1 = gb.nodes_of_edge(e)
            master_to_m = mg.master_to_mortar_avg()
            slave_to_m = mg.slave_to_mortar_avg()

            master_area = master_to_m * g1.face_areas
            slave_area = slave_to_m * g2.face_areas

            self.assertTrue(
                np.allclose(d_e[pp.STATE]["mortar_flux"] / master_area, 1))
            self.assertTrue(
                np.allclose(d_e[pp.STATE]["mortar_flux"] / slave_area, 1))
Esempio n. 22
0
def add_data(gb, domain, kf, mesh_value):
    """
    Define the permeability, apertures, boundary conditions and sources
    """
    gb.add_node_props(['param'])
    tol = 1e-5
    a = 1e-4

    for g, d in gb:
        param = pp.Parameters(g)

        # Assign apertures
        a_dim = np.power(a, gb.dim_max() - g.dim)
        aperture = np.ones(g.num_cells) * a_dim
        param.set_aperture(aperture)

        # Permeability
        # Use fracture value in the fractures, i.e., the lower dimensional grids
        k_frac = np.power(kf, g.dim < gb.dim_max())
        p = pp.SecondOrderTensor(3, np.ones(g.num_cells) * k_frac)
        param.set_tensor('flow', p)
        param.set_tensor('flow', p)

        # Source term
        param.set_source('flow', np.zeros(g.num_cells))

        # Boundaries
        bound_faces = g.tags['domain_boundary_faces'].nonzero()[0]
        if bound_faces.size != 0:
            bound_face_centers = g.face_centers[:, bound_faces]

            left = bound_face_centers[0, :] < domain['xmin'] + tol
            right = bound_face_centers[0, :] > domain['xmax'] - tol

            labels = np.array(['neu'] * bound_faces.size)
            labels[right] = 'dir'

            bc_val = np.zeros(g.num_faces)

            if g.dim == 2:
                # Account for the double inflow on the matrix-fracture overlap
                left_mid = np.array(
                    np.absolute(g.face_centers[1, bound_faces[left]] -
                                0.5) < mesh_value)
                bc_val[bound_faces[left]] = - g.face_areas[bound_faces[left]] \
                    + left_mid * .5 * a
            else:
                bc_val[bound_faces[left]] = - \
                    g.face_areas[bound_faces[left]] * a

            bc_val[bound_faces[right]] = np.ones(np.sum(right))

            param.set_bc('flow', pp.BoundaryCondition(g, bound_faces, labels))
            param.set_bc_val('flow', bc_val)
        else:
            param.set_bc("flow",
                         pp.BoundaryCondition(g, np.empty(0), np.empty(0)))

        d['param'] = param
Esempio n. 23
0
def add_data(gb, domain, kf):
    """
    Define the permeability, apertures, boundary conditions
    """
    gb.add_node_props(["param", "is_tangential"])
    tol = 1e-5
    a = 1e-4

    for g, d in gb:
        param = pp.Parameters(g)

        # Permeability
        kxx = np.ones(g.num_cells) * np.power(kf, g.dim < gb.dim_max())
        if g.dim == 2:
            perm = pp.SecondOrderTensor(g.dim, kxx=kxx, kyy=kxx, kzz=1)
        else:
            perm = pp.SecondOrderTensor(g.dim, kxx=kxx, kyy=1, kzz=1)
        param.set_tensor("flow", perm)

        # Source term
        param.set_source("flow", np.zeros(g.num_cells))

        # Assign apertures
        aperture = np.power(a, gb.dim_max() - g.dim)
        param.set_aperture(np.ones(g.num_cells) * aperture)

        # Boundaries
        bound_faces = g.tags["domain_boundary_faces"].nonzero()[0]
        if bound_faces.size != 0:
            bound_face_centers = g.face_centers[:, bound_faces]

            left = bound_face_centers[0, :] < domain["xmin"] + tol
            right = bound_face_centers[0, :] > domain["xmax"] - tol

            labels = np.array(["neu"] * bound_faces.size)
            labels[right] = "dir"

            bc_val = np.zeros(g.num_faces)
            bc_val[bound_faces[left]] = -aperture * g.face_areas[bound_faces[left]]
            bc_val[bound_faces[right]] = 1

            param.set_bc("flow", pp.BoundaryCondition(g, bound_faces, labels))
            param.set_bc_val("flow", bc_val)
        else:
            param.set_bc("flow", pp.BoundaryCondition(g, np.empty(0), np.empty(0)))

        d["is_tangential"] = True
        d["param"] = param

    # Assign coupling permeability
    gb.add_edge_props("kn")
    for e, d in gb.edges():
        g_l = gb.nodes_of_edge(e)[0]
        mg = d["mortar_grid"]
        check_P = mg.low_to_mortar_avg()

        gamma = check_P * gb.node_props(g_l, "param").get_aperture()
        d["kn"] = kf * np.ones(mg.num_cells) / gamma
Esempio n. 24
0
    def test_unit_slip(self):
        """
        test unit slip of fractures
        """
        frac = np.array([[1, 1, 1], [1, 2, 1], [2, 2, 1], [2, 1, 1]]).T
        physdims = np.array([3, 3, 2])
        g = pp.meshing.cart_grid([frac], [3, 3, 2],
                                 physdims=physdims).grids_of_dimension(3)[0]

        data = {"param": pp.Parameters(g)}
        bound = pp.BoundaryConditionVectorial(g, g.get_all_boundary_faces(),
                                              "dir")

        data["param"].set_bc("mechanics", bound)

        frac_slip = np.zeros((g.dim, g.num_faces))
        frac_bnd = g.tags["fracture_faces"]
        frac_slip[:, frac_bnd] = np.ones((g.dim, np.sum(frac_bnd)))

        data["param"].set_slip_distance(frac_slip.ravel("F"))

        solver = pp.FracturedMpsa()

        A, b = solver.matrix_rhs(g, data, inverter="python")
        u = np.linalg.solve(A.A, b)

        u_f = solver.extract_frac_u(g, u)
        u_c = solver.extract_u(g, u)
        u_c = u_c.reshape((3, -1), order="F")

        # obtain fracture faces and cells
        frac_faces = g.frac_pairs
        frac_left = frac_faces[0]
        frac_right = frac_faces[1]

        cell_left = np.ravel(np.argwhere(g.cell_faces[frac_left, :])[:, 1])
        cell_right = np.ravel(np.argwhere(g.cell_faces[frac_right, :])[:, 1])

        # Test traction
        T = solver.traction(g, data, u)
        T = T.reshape((3, -1), order="F")
        T_left = T[:, frac_left]
        T_right = T[:, frac_right]

        self.assertTrue(np.allclose(T_left, T_right))

        # we have u_lhs - u_rhs = 1 so u_lhs should be positive
        self.assertTrue(np.all(u_c[:, cell_left] > 0))
        self.assertTrue(np.all(u_c[:, cell_right] < 0))
        mid_ind = int(round(u_f.size / 2))
        u_left = u_f[:mid_ind]
        u_right = u_f[mid_ind:]
        self.assertTrue(np.all(np.abs(u_left - u_right - 1) < 1e-10))

        # fracture displacement should be symetric since everything else is
        # symetric
        self.assertTrue(np.allclose(u_left, 0.5))
        self.assertTrue(np.allclose(u_right, -0.5))
Esempio n. 25
0
    def set_params(self, gb, kn, kf):

        kw = "flow"

        for g, d in gb:
            parameter_dictionary = {}

            aperture = np.power(1e-2, gb.dim_max() - g.dim)
            parameter_dictionary["aperture"] = aperture * np.ones(g.num_cells)

            b_val = np.zeros(g.num_faces)
            if g.dim == 2:
                bound_faces = pp.face_on_side(g, ["ymin", "ymax"])
                bound_faces = np.hstack((bound_faces[0], bound_faces[1]))
                labels = np.array(["dir"] * bound_faces.size)

                parameter_dictionary["bc"] = pp.BoundaryCondition(
                    g, bound_faces, labels)

                y_max_faces = pp.face_on_side(g, "ymax")[0]
                b_val[y_max_faces] = 1

                perm = pp.SecondOrderTensor(kxx=np.ones(g.num_cells))
                parameter_dictionary["second_order_tensor"] = perm

            else:
                perm = pp.SecondOrderTensor(kxx=kf * np.ones(g.num_cells))
                parameter_dictionary["second_order_tensor"] = perm

                parameter_dictionary["bc"] = pp.BoundaryCondition(g)
            parameter_dictionary["bc_values"] = b_val
            parameter_dictionary["mpfa_inverter"] = "python"

            d[pp.PARAMETERS] = pp.Parameters(g, [kw], [parameter_dictionary])
            d[pp.DISCRETIZATION_MATRICES] = {"flow": {}}

        gb.add_edge_props("kn")
        for _, d in gb.edges():
            mg = d["mortar_grid"]
            flow_dictionary = {
                "normal_diffusivity": kn * np.ones(mg.num_cells)
            }
            d[pp.PARAMETERS] = pp.Parameters(keywords=["flow"],
                                             dictionaries=[flow_dictionary])
            d[pp.DISCRETIZATION_MATRICES] = {"flow": {}}
Esempio n. 26
0
def add_data_advection(gb, domain, tol):

    # Porosity
    phi_m = 1e-1
    phi_f = 9 * 1e-1

    # Density
    rho_w = 1e3  # kg m^{-3}
    rho_s = 2 * 1e3  # kg m^{-3}

    # heat capacity
    c_w = 4 * 1e3  # J kg^{-1} K^{-1}
    c_s = 8 * 1e2  # J kg^{-1} K^{-1}

    c_m = phi_m * rho_w * c_w + (1 - phi_m) * rho_s * c_s
    c_f = phi_f * rho_w * c_w + (1 - phi_f) * rho_s * c_s

    for g, d in gb:
        param = d["param"]

        rock = g.dim == gb.dim_max()
        source = np.zeros(g.num_cells)
        param.set_source("transport", source)

        param.set_porosity(1)
        param.set_discharge(d["discharge"])

        bound_faces = g.tags["domain_boundary_faces"].nonzero()[0]
        if bound_faces.size != 0:
            bound_face_centers = g.face_centers[:, bound_faces]

            bottom = bound_face_centers[1, :] < domain["ymin"] + tol
            left = bound_face_centers[0, :] < domain["xmin"] + tol
            right = bound_face_centers[0, :] > domain["xmax"] - tol
            boundary = np.logical_or(left, right)
            labels = np.array(["neu"] * bound_faces.size)
            labels[boundary] = ["dir"]

            bc_val = np.zeros(g.num_faces)
            bc_val[bound_faces[left]] = 1

            param.set_bc("transport",
                         pp.BoundaryCondition(g, bound_faces, labels))
            param.set_bc_val("transport", bc_val)
        else:
            param.set_bc("transport",
                         pp.BoundaryCondition(g, np.empty(0), np.empty(0)))
        d["param"] = param

    # Assign coupling discharge
    gb.add_edge_prop("param")
    for e, d in gb.edges_props():
        g = gb.sorted_nodes_of_edge(e)[1]
        discharge = gb.node_prop(g, "param").get_discharge()
        d["param"] = pp.Parameters(g)
        d["param"].set_discharge(discharge)
Esempio n. 27
0
def add_data(gb, domain, kf):
    """
    Define the permeability, apertures, boundary conditions
    """
    gb.add_node_props(['param'])
    tol = 1e-5
    a = 1e-4

    for g, d in gb:
        param = pp.Parameters(g)

        # Permeability
        kxx = np.ones(g.num_cells) * np.power(kf, g.dim < gb.dim_max())
        if g.dim == 2:
            perm = pp.SecondOrderTensor(g.dim, kxx=kxx, kyy=kxx, kzz=1)
        else:
            perm = pp.SecondOrderTensor(g.dim, kxx=kxx, kyy=1, kzz=1)
        param.set_tensor("flow", perm)

        # Source term
        param.set_source("flow", np.zeros(g.num_cells))

        # Assign apertures
        aperture = np.power(a, gb.dim_max() - g.dim)
        param.set_aperture(np.ones(g.num_cells) * aperture)

        # Boundaries
        bound_faces = g.tags['domain_boundary_faces'].nonzero()[0]
        if bound_faces.size != 0:
            bound_face_centers = g.face_centers[:, bound_faces]

            left = bound_face_centers[0, :] < domain['xmin'] + tol
            right = bound_face_centers[0, :] > domain['xmax'] - tol

            labels = np.array(['neu'] * bound_faces.size)
            labels[right] = 'dir'

            bc_val = np.zeros(g.num_faces)
            bc_val[bound_faces[left]] = -aperture \
                * g.face_areas[bound_faces[left]]
            bc_val[bound_faces[right]] = 1

            param.set_bc("flow", pp.BoundaryCondition(g, bound_faces, labels))
            param.set_bc_val("flow", bc_val)
        else:
            param.set_bc("flow",
                         pp.BoundaryCondition(g, np.empty(0), np.empty(0)))

        d['param'] = param

    # Assign coupling permeability
    gb.add_edge_props('kn')
    for e, d in gb.edges():
        gn = gb.nodes_of_edge(e)
        aperture = np.power(a, gb.dim_max() - gn[0].dim)
        d['kn'] = np.ones(gn[0].num_cells) * kf / aperture
def add_data(gb, domain, kf):
    """
    Define the permeability, apertures, boundary conditions
    """
    gb.add_node_props(["param", "is_tangential"])
    tol = 1e-5
    a = 1e-4

    for g, d in gb:
        # Assign aperture
        a_dim = np.power(a, gb.dim_max() - g.dim)
        aperture = np.ones(g.num_cells) * a_dim

        # Effective permeability, scaled with aperture.
        kxx = np.ones(g.num_cells) * np.power(kf,
                                              g.dim < gb.dim_max()) * aperture
        if g.dim == 2:
            perm = pp.SecondOrderTensor(kxx=kxx, kyy=kxx, kzz=1)
        else:
            perm = pp.SecondOrderTensor(kxx=kxx, kyy=1, kzz=1)

        specified_parameters = {"second_order_tensor": perm}
        # Boundaries
        bound_faces = g.tags["domain_boundary_faces"].nonzero()[0]
        if bound_faces.size != 0:
            bound_face_centers = g.face_centers[:, bound_faces]

            left = bound_face_centers[0, :] < domain["xmin"] + tol
            right = bound_face_centers[0, :] > domain["xmax"] - tol

            labels = np.array(["neu"] * bound_faces.size)
            labels[right] = "dir"

            bc_val = np.zeros(g.num_faces)
            bc_val[
                bound_faces[left]] = -a_dim * g.face_areas[bound_faces[left]]
            bc_val[bound_faces[right]] = 1

            bound = pp.BoundaryCondition(g, bound_faces, labels)
            specified_parameters.update({"bc": bound, "bc_values": bc_val})
        else:
            bound = pp.BoundaryCondition(g, np.empty(0), np.empty(0))
            specified_parameters.update({"bc": bound})

        d["is_tangential"] = True
        pp.initialize_default_data(g, d, "flow", specified_parameters)

    # Assign coupling permeability
    for _, d in gb.edges():
        mg = d["mortar_grid"]
        kn = 2 * kf * np.ones(mg.num_cells) / a
        d[pp.PARAMETERS] = pp.Parameters(mg, ["flow"],
                                         [{
                                             "normal_diffusivity": kn
                                         }])
        d[pp.DISCRETIZATION_MATRICES] = {"flow": {}}
Esempio n. 29
0
def setup_3d(nx, simplex_grid=False):
    f1 = np.array([[0.2, 0.2, 0.8, 0.8], [0.2, 0.8, 0.8, 0.2],
                   [0.5, 0.5, 0.5, 0.5]])
    f2 = np.array([[0.2, 0.8, 0.8, 0.2], [0.5, 0.5, 0.5, 0.5],
                   [0.2, 0.2, 0.8, 0.8]])
    f3 = np.array([[0.5, 0.5, 0.5, 0.5], [0.2, 0.8, 0.8, 0.2],
                   [0.2, 0.2, 0.8, 0.8]])
    fracs = [f1, f2, f3]
    if not simplex_grid:
        gb = pp.meshing.cart_grid(fracs, nx, physdims=[1, 1, 1])
    else:
        mesh_kwargs = {}
        mesh_size = 0.3
        mesh_kwargs = {
            "mesh_size_frac": mesh_size,
            "mesh_size_bound": 2 * mesh_size,
            "mesh_size_min": mesh_size / 20,
        }
        domain = {"xmin": 0, "ymin": 0, "xmax": 1, "ymax": 1}
        gb = pp.meshing.simplex_grid(fracs, domain, **mesh_kwargs)

    gb.add_node_props(["param"])
    for g, d in gb:
        a = 0.01 / np.max(nx)
        a = np.power(a, gb.dim_max() - g.dim)
        param = pp.Parameters(g)
        param.set_aperture(a)

        # BoundaryCondition
        left = g.face_centers[0] < 1e-6
        top = g.face_centers[2] > 1 - 1e-6
        dir_faces = np.argwhere(left)
        bc_cond = pp.BoundaryCondition(g, dir_faces, ["dir"] * dir_faces.size)
        bc_val = np.zeros(g.num_faces)
        bc_val[dir_faces] = 3
        bc_val[top] = 2.4
        param.set_bc("flow", bc_cond)
        param.set_bc_val("flow", bc_val)

        # Source and sink
        src = np.zeros(g.num_cells)
        src[0] = np.pi
        src[-1] = -np.pi
        param.set_source("flow", src)
        d["param"] = param

    gb.add_edge_props("kn")
    for e, d in gb.edges():
        g = gb.nodes_of_edge(e)[0]
        mg = d["mortar_grid"]
        check_P = mg.slave_to_mortar_avg()
        d["kn"] = 1 / (check_P * gb.node_props(g, "param").get_aperture())

    return gb
Esempio n. 30
0
def add_data_darcy(gb, domain, tol):
    gb.add_node_props(["param", "if_tangent"])

    apert = 1e-2

    km = 7.5 * 1e-10  # 2.5*1e-11

    kf = 5 * 1e-5

    for g, d in gb:

        param = pp.Parameters(g)
        d["if_tangent"] = True
        if g.dim == gb.dim_max():
            kxx = km
        else:
            kxx = kf

        perm = pp.SecondOrderTensor(g.dim, kxx * np.ones(g.num_cells))
        param.set_tensor("flow", perm)

        param.set_source("flow", np.zeros(g.num_cells))

        param.set_aperture(np.power(apert, gb.dim_max() - g.dim))

        bound_faces = g.tags["domain_boundary_faces"].nonzero()[0]
        if bound_faces.size != 0:
            bound_face_centers = g.face_centers[:, bound_faces]

            top = bound_face_centers[2, :] > domain["zmax"] - tol
            bottom = bound_face_centers[2, :] < domain["zmin"] + tol

            boundary = np.logical_or(top, bottom)

            labels = np.array(["neu"] * bound_faces.size)
            labels[boundary] = ["dir"]

            bc_val = np.zeros(g.num_faces)
            p = np.abs(domain["zmax"] - domain["zmin"]) * 1e3 * 9.81
            bc_val[bound_faces[bottom]] = p

            param.set_bc("flow", pp.BoundaryCondition(g, bound_faces, labels))
            param.set_bc_val("flow", bc_val)
        else:
            param.set_bc("flow", pp.BoundaryCondition(g, np.empty(0), np.empty(0)))

        d["param"] = param

    # Assign coupling permeability
    gb.add_edge_prop("kn")
    for e, d in gb.edges_props():
        g = gb.sorted_nodes_of_edge(e)[0]
        d["kn"] = kf / gb.node_prop(g, "param").get_aperture()