Example #1
0
def setup_discr_tpfa(gb, key="flow"):
    """ Setup the discretization Tpfa. """
    discr = pp.Tpfa(key)
    p_trace = pp.CellDofFaceDofMap(key)
    interface = pp.FluxPressureContinuity(key, discr, p_trace)

    for g, d in gb:
        if g.dim == gb.dim_max():
            d[pp.PRIMARY_VARIABLES] = {key: {"cells": 1}}
            d[pp.DISCRETIZATION] = {key: {"flux": discr}}
        else:
            d[pp.PRIMARY_VARIABLES] = {key: {"cells": 1}}
            d[pp.DISCRETIZATION] = {key: {"flux": p_trace}}

    for e, d in gb.edges():
        g_slave, g_master = gb.nodes_of_edge(e)
        d[pp.PRIMARY_VARIABLES] = {key: {"cells": 1}}
        d[pp.COUPLING_DISCRETIZATION] = {
            "flux": {
                g_slave: (key, "flux"),
                g_master: (key, "flux"),
                e: (key, interface),
            }
        }

    return pp.Assembler(gb), (discr, p_trace)
Example #2
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))
Example #3
0
    def solve(self, gb, analytic_p):

        # Parameter-discretization keyword:
        kw = "flow"
        # Terms
        key_flux = "flux"
        key_src = "src"
        # Primary variables
        key_p = "pressure"  # pressure name
        key_m = "mortar"  # mortar name

        tpfa = pp.Tpfa(kw)
        src = pp.ScalarSource(kw)
        for g, d in gb:
            d[pp.DISCRETIZATION] = {key_p: {key_src: src, key_flux: tpfa}}
            d[pp.PRIMARY_VARIABLES] = {key_p: {"cells": 1}}

        for e, d in gb.edges():
            g1, g2 = gb.nodes_of_edge(e)
            d[pp.PRIMARY_VARIABLES] = {key_m: {"cells": 1}}
            if g1.dim == g2.dim:
                mortar_disc = pp.FluxPressureContinuity(kw, tpfa)
            else:
                mortar_disc = pp.RobinCoupling(kw, tpfa)
            d[pp.COUPLING_DISCRETIZATION] = {
                key_flux: {
                    g1: (key_p, key_flux),
                    g2: (key_p, key_flux),
                    e: (key_m, mortar_disc),
                }
            }

        assembler = pp.Assembler(gb)
        assembler.discretize()
        A, b = assembler.assemble_matrix_rhs()
        x = sps.linalg.spsolve(A, b)

        assembler.distribute_variable(x)

        # test pressure
        for g, d in gb:
            ap, _, _ = analytic_p(g.cell_centers)
            self.assertTrue(np.max(np.abs(d[pp.STATE][key_p] - ap)) < 5e-2)

        # test mortar solution
        for e, d_e in gb.edges():
            mg = d_e["mortar_grid"]
            g1, g2 = gb.nodes_of_edge(e)
            if g1 == g2:
                left_to_m = mg.master_to_mortar_avg()
                right_to_m = mg.slave_to_mortar_avg()
            else:
                continue
            d1 = gb.node_props(g1)
            d2 = gb.node_props(g2)

            _, analytic_flux, _ = analytic_p(g1.face_centers)
            # the aperture is assumed constant
            left_flux = np.sum(analytic_flux * g1.face_normals[:2], 0)
            left_flux = left_to_m * (
                d1[pp.DISCRETIZATION_MATRICES][kw]["bound_flux"] * left_flux
            )
            # right flux is negative lambda
            right_flux = np.sum(analytic_flux * g2.face_normals[:2], 0)
            right_flux = -right_to_m * (
                d2[pp.DISCRETIZATION_MATRICES][kw]["bound_flux"] * right_flux
            )
            self.assertTrue(np.max(np.abs(d_e[pp.STATE][key_m] - left_flux)) < 5e-2)
            self.assertTrue(np.max(np.abs(d_e[pp.STATE][key_m] - right_flux)) < 5e-2)
Example #4
0
    def set_variables_discretizations_cell_basis(self, gb):
        """
        Assign variables, and set discretizations for the micro gb.

        NOTE: keywords and variable names are hardcoded here. This should be centralized.
        @Eirik we are keeping the same nomenclature, since the gb are different maybe we can
        change it a bit

        Args:
            gb (TYPE): the micro gb.

        Returns:
            None.

        """
        # Use mpfa for the fine-scale problem for now. We may generalize this at some
        # point, but that should be a technical detail.
        fine_scale_dicsr = pp.Mpfa(self.keyword)
        # In 1d, mpfa will end up calling tpfa, so use this directly, in a hope that
        # the reduced amount of boilerplate will save some time.
        fine_scale_dicsr_1d = pp.Tpfa(self.keyword)

        void_discr = pp.EllipticDiscretizationZeroPermeability(self.keyword)

        for g, d in gb:
            d[pp.PRIMARY_VARIABLES] = {
                self.cell_variable: {
                    "cells": 1,
                    "faces": 0
                }
            }
            if g.dim > 1:
                d[pp.DISCRETIZATION] = {
                    self.cell_variable: {
                        self.cell_discr: fine_scale_dicsr
                    }
                }
            else:
                d[pp.DISCRETIZATION] = {
                    self.cell_variable: {
                        self.cell_discr: fine_scale_dicsr_1d
                    }
                }

            d[pp.DISCRETIZATION_MATRICES] = {self.keyword: {}}

        # Loop over the edges in the GridBucket, define primary variables and discretizations
        # NOTE: No need to differ between Mpfa and Tpfa here; their treatment of interface
        # discretizations are the same.
        for e, d in gb.edges():
            g1, g2 = gb.nodes_of_edge(e)

            # Set the mortar variable.
            # NOTE: This is overriden in the case where the higher-dimensional grid is
            # auxiliary, see below.
            d[pp.PRIMARY_VARIABLES] = {self.mortar_variable: {"cells": 1}}

            # The type of lower-dimensional discretization depends on whether this is a
            # (part of a) fracture, or a transition between two line or surface grids.
            if g1.dim == 2 and g2.dim == 2:
                mortar_discr = pp.FluxPressureContinuity(
                    self.keyword, fine_scale_dicsr, fine_scale_dicsr)
            elif hasattr(g1, "is_auxiliary") and g1.is_auxiliary:
                if g2.dim > 1:
                    # This is a connection between a surface and an auxiliary line.
                    # Impose continuity conditions over the line.

                    assert g1.dim == 1  # Cannot imagine this is not True
                    mortar_discr = pp.FluxPressureContinuity(
                        self.keyword, fine_scale_dicsr, void_discr)
                else:
                    # Connection between a 1d line (an interaction region edge) and a
                    # point along that line (could be a coner along the edge.
                    mortar_discr = pp.FluxPressureContinuity(
                        self.keyword, fine_scale_dicsr_1d, void_discr)
            elif hasattr(g2, "is_auxiliary") and g2.is_auxiliary:
                # This is an auxiliary line being cut by a point, which should then
                # be a fracture point. Impose no condition here.
                assert g2.dim == 1
                # No variable for this edge - we have no equation for it.
                d[pp.PRIMARY_VARIABLES] = {}
                continue
            else:
                # Standard coupling, with resistance, for the final case.
                if g1.dim > 1:
                    mortar_discr = pp.RobinCoupling(self.keyword,
                                                    fine_scale_dicsr,
                                                    fine_scale_dicsr)
                elif g1.dim == 1:
                    mortar_discr = pp.RobinCoupling(self.keyword,
                                                    fine_scale_dicsr,
                                                    fine_scale_dicsr_1d)
                else:
                    mortar_discr = pp.RobinCoupling(self.keyword,
                                                    fine_scale_dicsr_1d,
                                                    fine_scale_dicsr_1d)

            d[pp.COUPLING_DISCRETIZATION] = {
                self.mortar_discr: {
                    g1: (self.cell_variable, self.cell_discr),
                    g2: (self.cell_variable, self.cell_discr),
                    e: (self.mortar_variable, mortar_discr),
                }
            }
            d[pp.DISCRETIZATION_MATRICES] = {self.keyword: {}}
def advdiff(gb, discr, param, bc_flag):

    model = "transport"

    model_data_adv, model_data_diff, model_data_src = data_advdiff(
        gb, model, param, bc_flag
    )

    # discretization operator names
    adv_id = "advection"
    diff_id = "diffusion"
    src_id = "source"

    # variable names
    variable = "scalar"
    mortar_adv = "lambda_" + variable + "_" + adv_id
    mortar_diff = "lambda_" + variable + "_" + diff_id

    # save variable name for the post-process
    param["scalar"] = variable

    discr_adv = pp.Upwind(model_data_adv)
    discr_adv_interface = pp.CellDofFaceDofMap(model_data_adv)

    discr_diff = pp.Tpfa(model_data_diff)
    discr_diff_interface = pp.CellDofFaceDofMap(model_data_diff)

    coupling_adv = pp.UpwindCoupling(model_data_adv)
    coupling_diff = pp.FluxPressureContinuity(
        model_data_diff, discr_diff, discr_diff_interface
    )

    # mass term
    mass_id = "mass"
    discr_mass = pp.MassMatrix(model_data_adv)
    discr_mass_interface = pp.CellDofFaceDofMap(model_data_adv)

    discr_src = pp.ScalarSource(model_data_src)

    for g, d in gb:
        d[pp.PRIMARY_VARIABLES] = {variable: {"cells": 1}}
        if g.dim == gb.dim_max():
            d[pp.DISCRETIZATION] = {
                variable: {adv_id: discr_adv,
                diff_id: discr_diff,
                mass_id: discr_mass,
                src_id: discr_src}
            }
        else:
            d[pp.DISCRETIZATION] = {
                variable: {adv_id: discr_adv_interface,
                diff_id: discr_diff_interface,
                mass_id: discr_mass_interface}
            }

    for e, d in gb.edges():
        g_slave, g_master = gb.nodes_of_edge(e)
        d[pp.PRIMARY_VARIABLES] = {mortar_adv: {"cells": 1}, mortar_diff: {"cells": 1}}

        d[pp.COUPLING_DISCRETIZATION] = {
            adv_id: {
                g_slave: (variable, adv_id),
                g_master: (variable, adv_id),
                e: (mortar_adv, coupling_adv),
            },
            diff_id: {
                g_slave: (variable, diff_id),
                g_master: (variable, diff_id),
                e: (mortar_diff, coupling_diff),
            },
        }

    # setup the advection-diffusion problem
    assembler = pp.Assembler(gb, active_variables=[variable, mortar_diff, mortar_adv])
    logger.info("Assemble the advective and diffusive terms of the transport problem")
    block_A, block_b = assembler.assemble_matrix_rhs(add_matrices=False)
    logger.info("done")

    # unpack the matrices just computed
    diff_name = diff_id + "_" + variable
    adv_name = adv_id + "_" + variable
    mass_name = mass_id + "_" + variable
    source_name = src_id + "_" + variable

    diff_coupling_name = diff_id + "_" + mortar_diff + "_" + variable + "_" + variable
    adv_coupling_name = adv_id + "_" + mortar_adv + "_" + variable + "_" + variable

    # need a sign for the convention of the conservation equation
    M = block_A[mass_name]
    A = block_A[diff_name] + block_A[diff_coupling_name] + \
        block_A[adv_name] + block_A[adv_coupling_name]
    b = block_b[diff_name] + block_b[diff_coupling_name] + \
        block_b[adv_name] + block_b[adv_coupling_name] + \
        block_b[source_name]

    M_t = M.copy() / param["time_step"] * param.get("mass_weight", 1)
    M_r = M.copy() * param.get("reaction", 0)

    # Perform an LU factorization to speedup the solver
    IE_solver = sps.linalg.factorized((M_t + A + M_r).tocsc())

    # time loop
    logger.info("Prepare the exporting")
    save = pp.Exporter(gb, "solution", folder=param["folder"])
    logger.info("done")

    variables = [variable, param["pressure"], "frac_num", "cell_volumes"]
    if discr["scheme"] is pp.MVEM or discr["scheme"] is pp.RT0:
        variables.append(param["P0_flux"])

    # assign the initial condition
    x = np.zeros(A.shape[0])
    assembler.distribute_variable(x)
    for g, d in gb:
        if g.dim == gb.dim_max():
            d[pp.STATE][variable] = param.get("init_trans", 0) * np.ones(g.num_cells)

    x = assembler.merge_variable(variable)

    outflow = np.zeros(param["n_steps"])

    logger.info("Start the time loop with " + str(param["n_steps"]) + " steps")
    for i in np.arange(param["n_steps"]):
        logger.info("Solve the linear system for time step " + str(i))
        x = IE_solver(b + M_t.dot(x))
        logger.info("done")

        logger.info("Variable post-process")
        assembler.distribute_variable(x)
        logger.info("done")

        logger.info("Export variable")
        save.write_vtk(variables, time_step=i)
        logger.info("done")

        logger.info("Compute the production")
        outflow[i] = compute_outflow(gb, param)
        logger.info("done")

    time = np.arange(param["n_steps"]) * param["time_step"]
    save.write_pvd(time)

    logger.info("Save outflow on file")
    file_out = param["folder"] + "/outflow.csv"
    data = np.vstack((time, outflow)).T
    np.savetxt(file_out, data, delimiter=",")
    logger.info("done")

    logger.info("Save dof on file")
    file_out = param["folder"] + "/dof_transport.csv"
    np.savetxt(file_out, get_dof(assembler), delimiter=",", fmt="%d")
    logger.info("done")
def flow(gb, discr, param, bc_flag):

    model = "flow"

    model_data = data_flow(gb, discr, model, param, bc_flag)

    # discretization operator name
    flux_id = "flux"

    # master variable name
    variable = "flow_variable"
    mortar = "lambda_" + variable

    # post process variables
    pressure = "pressure"
    flux = "darcy_flux"  # it has to be this one

    # save variable name for the advection-diffusion problem
    param["pressure"] = pressure
    param["flux"] = flux
    param["mortar_flux"] = mortar

    discr_scheme = discr["scheme"](model_data)
    discr_interface = pp.CellDofFaceDofMap(model_data)

    coupling = pp.FluxPressureContinuity(model_data, discr_scheme, discr_interface)

    # define the dof and discretization for the grids
    for g, d in gb:
        if g.dim == gb.dim_max():
            d[pp.PRIMARY_VARIABLES] = {variable: discr["dof"]}
            d[pp.DISCRETIZATION] = {variable: {flux_id: discr_scheme}}
        else:
            d[pp.PRIMARY_VARIABLES] = {variable: {"cells": 1}}
            d[pp.DISCRETIZATION] = {variable: {flux_id: discr_interface}}

    # define the interface terms to couple the grids
    for e, d in gb.edges():
        g_slave, g_master = gb.nodes_of_edge(e)
        d[pp.PRIMARY_VARIABLES] = {mortar: {"cells": 1}}
        d[pp.COUPLING_DISCRETIZATION] = {
            flux: {
                g_slave: (variable, flux_id),
                g_master: (variable, flux_id),
                e: (mortar, coupling),
            }
        }

    # solution of the darcy problem
    assembler = pp.Assembler(gb)

    logger.info("Assemble the flow problem")
    A, b = assembler.assemble_matrix_rhs()
    logger.info("done")

    logger.info("Solve the linear system")
    x = sps.linalg.spsolve(A, b)
    logger.info("done")

    logger.info("Variable post-process")
    assembler.distribute_variable(x)

    # extract the pressure from the solution
    for g, d in gb:
        if g.dim == 2:
            d[pp.STATE][pressure] = discr_scheme.extract_pressure(g, d[pp.STATE][variable], d)
            d[pp.STATE][flux] = discr_scheme.extract_flux(g, d[pp.STATE][variable], d)
        else:
            d[pp.STATE][pressure] = np.zeros(g.num_cells)
            d[pp.STATE][flux] = np.zeros(g.num_faces)

    # export the P0 flux reconstruction only for some scheme
    if discr["scheme"] is pp.MVEM or discr["scheme"] is pp.RT0:
        P0_flux = "P0_flux"
        param["P0_flux"] = P0_flux
        pp.project_flux(gb, discr_scheme, flux, P0_flux, mortar)

    logger.info("done")

    logger.info("Save dof on file")
    file_out = param["folder"] + "/dof_flow.csv"
    np.savetxt(file_out, get_dof(assembler), delimiter=",", fmt="%d")
    logger.info("done")