Exemple #1
0
    def source_temperature(self, g) -> np.ndarray:
        """
        Sources are handled by ScalarSource discretizations.
        The implicit scheme yields multiplication of the rhs by dt, but
        this is not incorporated in ScalarSource, hence we do it here.
        """
        injection, production = self.source_flow_rates()

        # Injection well
        t_in = -70
        weight = (self.density(g, dT=t_in * self.temperature_scale) *
                  self.fluid.specific_heat_capacity(self.background_temp_C) *
                  self.time_step / self.T_0_Kelvin)
        rhs = t_in * weight * injection * g.tags["well_cells"].clip(min=0)

        # Production well during phase III
        weight = (self.density(g) *
                  self.fluid.specific_heat_capacity(self.background_temp_C) *
                  self.time_step / self.T_0_Kelvin)
        dp, p1, p0 = self._variable_increment(g, "p", self.scalar_scale)

        lhs = (weight * production.clip(max=0) *
               g.tags["well_cells"].clip(max=0) / g.cell_volumes)
        # Set this directly into d to avoid additional return
        d = self.gb.node_props(g)
        pp.initialize_data(g, d, self.production_well_key,
                           {"mass_weight": lhs})
        return rhs
Exemple #2
0
    def _set_scalar_parameters(self) -> None:
        super()._set_scalar_parameters()

        tensor_scale = self.scalar_scale / self.length_scale**2
        kappa = self.params.get("kappa", 1.) * tensor_scale
        mass_weight = self.params.get("s0", 1.) * self.scalar_scale
        for g, d in self.gb:
            bc = self._bc_type_scalar(g)
            bc_values = self._bc_values_scalar(g)
            source_values = self._source_scalar(g)

            specific_volume = self._specific_volume(g)
            diffusivity = pp.SecondOrderTensor(kappa * specific_volume *
                                               np.ones(g.num_cells))

            alpha = self.params.get("alpha", 1.) * self._biot_alpha(g)
            pp.initialize_data(
                g,
                d,
                self.scalar_parameter_key,
                {
                    "bc": bc,
                    "bc_values": bc_values,
                    "mass_weight": mass_weight * specific_volume,
                    "biot_alpha": alpha,
                    "source": source_values,
                    "second_order_tensor": diffusivity,
                    "time_step": self.time_step,
                    "vector_source": np.zeros(g.num_cells * self.gb.dim_max()),
                    "ambient_dimension": self.gb.dim_max(),
                },
            )
    def _assign_data(self, u_bot, u_top):
        """
        u_bot and u_top are the u_h in the neighbouring cells on the two sides of the fracture.
        Ordering is hard-coded according to the grids defined in _make_grid
        """
        u_h = np.zeros(self.nd * self.g_h.num_cells)
        if self.nd == 3:
            u_h[0:6] = u_bot.ravel(order="f")
            u_h[12:18] = u_top.ravel(order="f")
        else:
            u_h[2:6] = u_bot.ravel(order="f")
            u_h[10:14] = u_top.ravel(order="f")

        # Project to interface
        e = (self.g_h, self.g_l)
        d_j = self.gb.edge_props(e)
        mg = d_j["mortar_grid"]
        trace = np.abs(pp.fvutils.vector_divergence(self.g_h)).T
        u_j = mg.primary_to_mortar_avg(nd=self.nd) * trace * u_h
        pp.set_iterate(d_j, {self.model.mortar_displacement_variable: u_j})

        # Parameters used by the propagation class
        for g, d in self.gb:
            if g.dim == self.nd:
                param = {"shear_modulus": self.mu, "poisson_ratio": self.poisson}
            else:
                param = {"SIFs_critical": np.ones((self.nd, g.num_faces))}
            pp.initialize_data(g, d, self.model.mechanics_parameter_key, param)
    def set_data(self, data, data_time):
        self.data = data
        self.data_time = data_time

        for g, d in self.gb:
            param = {}

            unity = np.ones(g.num_cells)
            zeros = np.zeros(g.num_cells)
            empty = np.empty(0)

            alpha = self.data.get("alpha", 2)

            d["deviation_from_plane_tol"] = 1e-4
            d["is_tangential"] = True
            d["tol"] = data["tol"]

            # assign permeability
            if g.dim < self.gb.dim_max():
                aperture = d[pp.STATE]["aperture"]
                aperture_initial = d[pp.STATE]["aperture_initial"]

                k = np.power(aperture / aperture_initial,
                             alpha + 1) * self.data["k_t"]
                perm = pp.SecondOrderTensor(kxx=k, kyy=1, kzz=1)
            else:
                porosity = d[pp.STATE]["porosity"]
                porosity_initial = d[pp.STATE]["porosity_initial"]

                k = np.power(porosity / porosity_initial,
                             alpha) * self.data["k"]
                perm = pp.SecondOrderTensor(kxx=k, kyy=k, kzz=1)

            # no source term is assumed by the user
            param["second_order_tensor"] = perm
            param["source"] = zeros

            # Boundaries
            b_faces = g.tags["domain_boundary_faces"].nonzero()[0]
            if b_faces.size:
                labels, param["bc_values"] = data["bc"](g, data, data["tol"])
                param["bc"] = pp.BoundaryCondition(g, b_faces, labels)
            else:
                param["bc_values"] = np.zeros(g.num_faces)
                param["bc"] = pp.BoundaryCondition(g, empty, empty)

            pp.initialize_data(g, d, self.model, param)

        for e, d in self.gb.edges():
            mg = d["mortar_grid"]
            g_l = self.gb.nodes_of_edge(e)[0]

            check_P = mg.slave_to_mortar_avg()
            aperture = self.gb.node_props(g_l, pp.STATE)["aperture"]
            aperture_initial = self.gb.node_props(g_l,
                                                  pp.STATE)["aperture_initial"]

            k = 2 * check_P * (np.power(aperture / aperture_initial, alpha - 1)
                               * self.data["k_n"])
            pp.initialize_data(mg, d, self.model, {"normal_diffusivity": k})
    def set_data(self, data, data_time):
        self.data = data
        self.data_time = data_time

        for g, d in self.gb:
            param_diff = {}
            param_adv = {}
            param_mass = {}
            param_source = {}

            unity = np.ones(g.num_cells)
            zeros = np.zeros(g.num_cells)
            empty = np.empty(0)

            d["Aavatsmark_transmissibilities"] = True
            d["tol"] = data["tol"]

            # assign permeability
            if g.dim < self.gb.dim_max():
                diff = data["d_t"] * d[pp.STATE]["aperture"]
            else:
                diff = data["d"] * d[pp.STATE]["porosity"]

            param_diff["second_order_tensor"] = pp.SecondOrderTensor(diff)

            param_mass["mass_weight"] = data["mass_weight"] / data_time["step"]
            param_source["source"] = zeros

            # Boundaries
            b_faces = g.tags["domain_boundary_faces"].nonzero()[0]
            if b_faces.size:
                labels_diff, labels_adv, bc_val = data["bc"](g, data, data["tol"])
                param_diff["bc"] = pp.BoundaryCondition(g, b_faces, labels_diff)
                param_adv["bc"] = pp.BoundaryCondition(g, b_faces, labels_adv)
            else:
                bc_val = np.zeros(g.num_faces)
                param_diff["bc"] = pp.BoundaryCondition(g, empty, empty)
                param_adv["bc"] = pp.BoundaryCondition(g, empty, empty)

            param_diff["bc_values"] = bc_val
            param_adv["bc_values"] = bc_val

            models = [self.diff_name, self.adv_name, self.mass_name, self.source_name]
            params = [param_diff, param_adv, param_mass, param_source]
            for model, param in zip(models, params):
                pp.initialize_data(g, d, model, param)

        for e, d in self.gb.edges():
            mg = d["mortar_grid"]
            g_l = self.gb.nodes_of_edge(e)[0]

            check_P = mg.slave_to_mortar_avg()
            aperture = self.gb.node_props(g_l, pp.STATE)["aperture"]

            diff = 2 * check_P * (self.data["d_n"] / aperture)

            models = [self.diff_name, self.adv_name]
            params = [{"normal_diffusivity": diff}, {}]
            for model, param in zip(models, params):
                pp.initialize_data(mg, d, model, param)
    def _source_temperature(self, g) -> np.ndarray:
        """
        Sources are handled by ScalarSource discretizations.
        The implicit scheme yields multiplication of the rhs by dt, but
        this is not incorporated in ScalarSource, hence we do it here.

        The sink (production well) is discretized using the MassMatrix
        discretization to ensure the extracted energy matches the current
        production well temperature.
        """
        injection, production = self._source_flow_rates()

        # Injection well
        dT = -30
        T_in = self.T_0_Kelvin + dT
        weight = (self._fluid_density(g, dT=dT * self.temperature_scale) *
                  self.fluid.specific_heat_capacity(self.background_temp_C) *
                  self.time_step / self.T_0_Kelvin)
        rhs = T_in * weight * injection * g.tags["well_cells"].clip(min=0)

        # Production well, discretized by MassMatrix on the lhs
        weight = (self._fluid_density(g) *
                  self.fluid.specific_heat_capacity(self.background_temp_C) *
                  self.time_step / self.T_0_Kelvin)

        lhs = (weight * production.clip(max=0) *
               g.tags["well_cells"].clip(max=0) / g.cell_volumes)
        # HACK: Set this directly into d to avoid additional return
        d = self.gb.node_props(g)
        pp.initialize_data(g, d, self.production_well_key,
                           {"mass_weight": lhs})
        return rhs
Exemple #7
0
    def set_data(self, data):
        self.data = data

        for g, d in self.gb:
            param = {}

            d["deviation_from_plane_tol"] = 1e-8
            d["is_tangential"] = True

            # assign permeability
            k = data["k"](g, d, self)

            # no source term is assumed by the user
            param["second_order_tensor"] = pp.SecondOrderTensor(kxx=k,
                                                                kyy=1,
                                                                kzz=1)
            param["source"] = data["source"](g, d, self)
            param["vector_source"] = data["vector_source"](g, d, self)

            # Boundaries
            b_faces = g.tags["domain_boundary_faces"].nonzero()[0]
            if b_faces.size:
                labels, param["bc_values"] = data["bc"](g, data, data["tol"],
                                                        self)
                param["bc"] = pp.BoundaryCondition(g, b_faces, labels)
            else:
                param["bc_values"] = np.zeros(g.num_faces)
                param["bc"] = pp.BoundaryCondition(g, np.empty(0), np.empty(0))

            pp.initialize_data(g, d, self.model, param)
Exemple #8
0
    def _set_mechanics_parameters(self) -> None:
        super()._set_mechanics_parameters()

        gb = self.gb
        for g, d in gb:
            # Rock parameters
            lam = self.params.get("lame_lambda", 1.) * np.ones(
                g.num_cells) / self.scalar_scale
            mu = self.params.get("lame_mu", 1.) * np.ones(
                g.num_cells) / self.scalar_scale
            C = pp.FourthOrderTensor(mu, lam)

            # Define boundary condition
            bc = self._bc_type_mechanics(g)
            # BC and source values
            bc_val = self._bc_values_mechanics(g)
            source_val = self._source_mechanics(g)

            pp.initialize_data(
                g,
                d,
                self.mechanics_parameter_key,
                {
                    "bc": bc,
                    "bc_values": bc_val,
                    "source": source_val,
                    "fourth_order_tensor": C,
                    "time_step": self.time_step,
                    "biot_alpha": self._biot_alpha(g),
                    "p_reference": np.zeros(g.num_cells),
                },
            )
    def set_permeability_from_aperture(self):
        """
        Cubic law in fractures, rock permeability in the matrix.
        """
        viscosity = self.fluid.dynamic_viscosity() / self.scalar_scale
        gb = self.gb
        key = self.scalar_parameter_key
        for g, d in gb:
            if g.dim < self.Nd:
                # Use cubic law in fractures
                apertures = self.compute_aperture(g)
                apertures_unscaled = apertures * self.length_scale
                k = np.power(apertures_unscaled, 2) / 12
                kxx = k / viscosity / self.length_scale**2
            else:
                # Use the rock permeability in the matrix
                kxx = (self.rock.PERMEABILITY / viscosity *
                       np.ones(g.num_cells) / self.length_scale**2)
            K = pp.SecondOrderTensor(kxx)
            d[pp.PARAMETERS][key]["second_order_tensor"] = K

        # Normal permeability inherited from fracture
        for e, d in gb.edges():
            mg = d["mortar_grid"]
            g_s, g_m = gb.nodes_of_edge(e)
            data_s = gb.node_props(g_s)
            a = self.compute_aperture(g_s)
            # We assume isotropic permeability in the fracture
            k_s = data_s[pp.PARAMETERS][
                self.scalar_parameter_key]["second_order_tensor"].values[0, 0]
            kn = 2 * mg.slave_to_mortar_int() * np.divide(k_s, a)
            pp.initialize_data(mg, d, self.scalar_parameter_key,
                               {"normal_diffusivity": kn})
Exemple #10
0
    def add_transport_data(self):
        """
        Add the transport data to the grid bucket
        """
        keyword = self.transport_keyword
        self.gb.add_node_props(["param", "is_tangential"])

        for g, d in self.gb:
            param = {}
            d["is_tangential"] = True

            unity = np.ones(g.num_cells)
            zeros = np.zeros(g.num_cells)
            empty = np.empty(0)

            # Specific volume.
            specific_volume = np.power(
                self.param["aperture"], self.gb.dim_max() - g.dim
            )
            param["specific_volume"] = specific_volume
            # Tangential diffusivity
            if g.dim == self.gb.dim_max():
                kxx = self.param["Dm"] * unity
            else:
                kxx = self.param["Df"] * specific_volume * unity
            perm = pp.SecondOrderTensor(kxx)
            param["second_order_tensor"] = perm

            # Source term
            param["source"] = zeros

            # Mass weight
            param["mass_weight"] = specific_volume * self.param["porosity"] * unity

            # Boundaries
            bound_faces = g.get_boundary_faces()
            bc_val = np.zeros(g.num_faces)
            if bound_faces.size == 0:
                param["bc"] = pp.BoundaryCondition(g, empty, empty)
            else:
                bc_val = np.zeros(g.num_faces)
                bc_val[bound_faces] = 0
                param["bc"] = pp.BoundaryCondition(g, bound_faces, "dir")

            param["bc_values"] = bc_val

            pp.initialize_data(g, d, keyword, param)

        # Normal diffusivity
        for e, d in self.gb.edges():
            # Get higher dimensional grid
            g_h = self.gb.nodes_of_edge(e)[1]
            param_h = self.gb.node_props(g_h, pp.PARAMETERS)
            mg = d["mortar_grid"]
            specific_volume_h = (
                np.ones(mg.num_cells) * param_h[keyword]["specific_volume"]
            )
            dn = self.param["Dn"] * specific_volume_h / (self.param["aperture"] / 2)
            param = {"normal_diffusivity": dn}
            pp.initialize_data(e, d, keyword, param)
Exemple #11
0
def _setup_simulation_tracer(gb, data, direction):
    min_coord = gb.bounding_box()[0][direction]
    max_coord = gb.bounding_box()[1][direction]

    parameter_keyword = "transport"

    for g, d in gb:
        param = d[pp.PARAMETERS]
        transport_parameter_dictionary = {}
        param.update_dictionaries(parameter_keyword,
                                  transport_parameter_dictionary)
        param.set_from_other(parameter_keyword, "flow", ["aperture"])
        d[pp.DISCRETIZATION_MATRICES][parameter_keyword] = {}

        unity = np.ones(g.num_cells)

        if g.dim == gb.dim_max():
            porosity = 0.2 * unity
        else:
            porosity = 0.8 * unity

        specified_parameters = {"mass_weight": porosity}

        bound_faces = g.tags["domain_boundary_faces"].nonzero()[0]
        if bound_faces.size > 0:
            hit_out = np.where(
                np.abs(g.face_centers[direction, bound_faces] -
                       max_coord) < 1e-8)[0]
            hit_in = np.where(
                np.abs(g.face_centers[direction, bound_faces] -
                       min_coord) < 1e-8)[0]
            bound_type = np.array(["neu"] * bound_faces.size)
            bound_type[hit_out] = "dir"
            bound_type[hit_in] = "dir"
            bound = pp.BoundaryCondition(g, bound_faces.ravel("F"), bound_type)
            bc_val = np.zeros(g.num_faces)
            bc_val[bound_faces[hit_out]] = 0
            bc_val[bound_faces[hit_in]] = 1

            specified_parameters.update({"bc": bound, "bc_values": bc_val})

            d["inlet_faces"] = bound_faces[hit_in]
        else:
            bc = pp.BoundaryCondition(g)
            specified_parameters.update({
                "bc": bc,
                "bc_values": np.zeros(g.num_faces)
            })

        pp.initialize_data(g, d, parameter_keyword, specified_parameters)

    for e, d in gb.edges():

        d[pp.PARAMETERS].update_dictionaries(parameter_keyword, {})
        d[pp.DISCRETIZATION_MATRICES][parameter_keyword] = {}

    return gb
Exemple #12
0
    def set_permeability(self):
        """
        Cubic law in fractures, rock permeability in the matrix.
        If "blocking_perm" is present in self.params, this value is used for
        Fracture 2.
        """
        # Viscosity has units of Pa s, and is consequently divided by the scalar scale.
        viscosity = self.fluid.dynamic_viscosity() / self.scalar_scale
        gb = self.gb
        key = self.scalar_parameter_key
        from_iterate = True
        blocking_perm = self.params.get("blocking_perm", None)
        for g, d in gb:
            if g.dim < self.Nd:
                # Set fracture permeability
                specific_volumes = self.specific_volumes(g, from_iterate)

                if d["node_number"] == 1 or blocking_perm is None:
                    # Use cubic law in fractures. First compute the unscaled
                    # permeability
                    apertures = self.aperture(g, from_iterate=from_iterate)
                    apertures_unscaled = apertures * self.length_scale
                    k = np.power(apertures_unscaled, 2) / 12 / viscosity
                else:
                    # Blocking and intersection
                    k = blocking_perm
                d[pp.PARAMETERS][key]["perm_nu"] = k
                # Multiply with the cross-sectional area
                k = k * specific_volumes
                # Divide by fluid viscosity and scale back
                kxx = k / self.length_scale ** 2

            else:
                # Use the rock permeability in the matrix
                kxx = (
                    self.rock.PERMEABILITY
                    / viscosity
                    * np.ones(g.num_cells)
                    / self.length_scale ** 2
                )
            K = pp.SecondOrderTensor(kxx)
            d[pp.PARAMETERS][key]["second_order_tensor"] = K

        # Normal permeability inherited from the neighboring fracture g_l
        for e, d in gb.edges():
            mg = d["mortar_grid"]
            g_l, _ = gb.nodes_of_edge(e)
            data_l = gb.node_props(g_l)
            a = self.aperture(g_l, from_iterate)
            V = self.specific_volumes(g_l, from_iterate)
            # We assume isotropic permeability in the fracture, i.e. the normal
            # permeability equals the tangential one
            k_s = data_l[pp.PARAMETERS][key]["second_order_tensor"].values[0, 0]
            # Division through half the aperture represents taking the (normal) gradient
            kn = mg.slave_to_mortar_int() * np.divide(k_s, a * V / 2)
            pp.initialize_data(mg, d, key, {"normal_diffusivity": kn})
Exemple #13
0
    def set_parameters_cell_basis(self, gb: pp.GridBucket, data: Dict):
        """
        Assign parameters for the micro gb. Very simple for now, this must be improved.

        Args:
            gb (TYPE): the micro gb.

        Returns:
            None.

        """
        # First initialize data
        for g, d in gb:

            d["Aavatsmark_transmissibilities"] = True

            domain_boundary = np.logical_and(
                g.tags["domain_boundary_faces"],
                np.logical_not(g.tags["fracture_faces"]),
            )

            boundary_faces = np.where(domain_boundary)[0]
            if domain_boundary.size > 0:
                bc_type = boundary_faces.size * ["dir"]
            else:
                bc_type = np.empty(0)

            bc = pp.BoundaryCondition(g, boundary_faces, bc_type)
            if hasattr(g, "face_on_macro_bound"):
                micro_ind = g.face_on_macro_bound
                macro_ind = g.macro_face_ind

                bc.is_neu[micro_ind] = data["bc_macro"]["bc"].is_neu[macro_ind]
                bc.is_dir[micro_ind] = data["bc_macro"]["bc"].is_dir[macro_ind]

            param = {"bc": bc}
            perm = data["g_data"](g)["second_order_tensor"]
            param["second_order_tensor"] = perm
            param["specific_volume"] = data["g_data"](g)["specific_volume"]

            # Use python inverter for mpfa for small problems, where it does not pay off
            # to fire up numba. The set threshold value is somewhat randomly picked.
            if g.num_cells < 100:
                param["mpfa_inverter"] = "python"

            pp.initialize_default_data(g, d, self.keyword, param)

        for e, d in gb.edges():
            mg = d["mortar_grid"]
            g1, g2 = gb.nodes_of_edge(e)
            param = {}
            if not hasattr(g1, "is_auxiliary") or not g1.is_auxiliary:
                check_P = mg.secondary_to_mortar_avg()
                param.update(data["e_data"](mg, g1, g2, check_P))

            pp.initialize_data(mg, d, self.keyword, param)
Exemple #14
0
    def _copy_biot_discretizations(self) -> None:
        """The Biot discretization is designed to discretize a single term of the
        grad_p type. It should not be difficult to generalize this, but pending such
        an update, the below code copies the discretization matrices from the flow
        related keywords to those of the temperature.

        """
        g: pp.Grid = self.gb.grids_of_dimension(self._Nd)[0]
        d: Dict = self.gb.node_props(g)
        beta = self._biot_beta(g)
        alpha = self._biot_alpha(g)

        # For grad p term of u equation
        weight_grad_t = beta / alpha

        # Account for scaling
        weight_grad_t *= self.temperature_scale / self.scalar_scale

        # Stabilization is derived from the grad p discretization
        weight_stabilization = beta / alpha
        weight_stabilization *= self.temperature_scale / self.scalar_scale

        # The stabilization terms appear in the T/p equations, whereof only the first
        # is divided by T_0_Kelvin.
        weight_stabilization *= 1 / self.T_0_Kelvin

        # Matrix dictionaries for the different subproblems
        matrices_s = d[pp.DISCRETIZATION_MATRICES][self.scalar_parameter_key]
        matrices_ms = d[pp.DISCRETIZATION_MATRICES][self.mechanics_parameter_key]
        matrices_t = d[pp.DISCRETIZATION_MATRICES][self.temperature_parameter_key]

        matrices_mt = {}
        matrices_t["div_u"] = matrices_s["div_u"].copy()
        matrices_t["bound_div_u"] = matrices_s["bound_div_u"].copy()
        matrices_mt["grad_p"] = weight_grad_t * matrices_ms["grad_p"]
        matrices_t["biot_stabilization"] = (
            weight_stabilization * matrices_s["biot_stabilization"]
        )
        matrices_mt["bound_displacement_pressure"] = (
            weight_grad_t * matrices_ms["bound_displacement_pressure"]
        )
        # For div u term of t equation
        weight_div_u = beta
        key_m_from_t = self.mechanics_temperature_parameter_key
        d[pp.DISCRETIZATION_MATRICES][key_m_from_t] = matrices_mt
        pp.initialize_data(
            g,
            d,
            key_m_from_t,
            {"biot_alpha": weight_div_u},
        )
        bc_dict = {"bc_values": self._bc_values_mechanics(g)}
        state = {key_m_from_t: bc_dict}
        pp.set_state(d, state)
    def _set_scalar_parameters(self) -> None:
        tensor_scale = self.scalar_scale / self.length_scale ** 2
        kappa = 1 * tensor_scale
        mass_weight = 1 * self.scalar_scale
        for g, d in self.gb:
            bc = self._bc_type_scalar(g)
            bc_values = self._bc_values_scalar(g)
            source_values = self._source_scalar(g)

            specific_volume = self._specific_volume(g)
            diffusivity = pp.SecondOrderTensor(
                kappa * specific_volume * np.ones(g.num_cells)
            )

            alpha = self._biot_alpha(g)
            pp.initialize_data(
                g,
                d,
                self.scalar_parameter_key,
                {
                    "bc": bc,
                    "bc_values": bc_values,
                    "mass_weight": mass_weight * specific_volume,
                    "biot_alpha": alpha,
                    "source": source_values,
                    "second_order_tensor": diffusivity,
                    "time_step": self.time_step,
                },
            )

        # Assign diffusivity in the normal direction of the fractures.
        for e, data_edge in self.gb.edges():
            g_l, g_h = self.gb.nodes_of_edge(e)
            mg = data_edge["mortar_grid"]
            a_l = self._aperture(g_l)
            # Take trace of and then project specific volumes from g_h
            v_h = (
                mg.primary_to_mortar_avg()
                * np.abs(g_h.cell_faces)
                * self._specific_volume(g_h)
            )
            # Division by a/2 may be thought of as taking the gradient in the normal
            # direction of the fracture.
            normal_diffusivity = kappa * 2 / (mg.secondary_to_mortar_avg() * a_l)
            # The interface flux is to match fluxes across faces of g_h,
            # and therefore need to be weighted by the corresponding
            # specific volumes
            normal_diffusivity *= v_h
            data_edge = pp.initialize_data(
                e,
                data_edge,
                self.scalar_parameter_key,
                {"normal_diffusivity": normal_diffusivity},
            )
def assign_parameters(time):

    nf = g.num_faces
    nc = g.num_cells
    fn = g.face_normals
    fc = g.face_centers
    cc = g.cell_centers
    V = g.cell_volumes
    
    # Permeability tensor
    perm = pp.SecondOrderTensor(K * np.ones(nc))

    # Boundary condtions
    top = np.where(np.abs(fc[1] - 1) < 1e-5)[0]
    bottom = np.where(np.abs(fc[1]) < 1e-5)[0]
    left = np.where(np.abs(fc[0]) < 1e-5)[0]
    right = np.where(np.abs(fc[0] - 1) < 1e-5)[0]
    
    bc_faces = g.get_boundary_faces()
    bc_type = np.array(bc_faces.size * ["neu"])
    bc_type[np.in1d(bc_faces, left)] = "dir"
    bc_type[np.in1d(bc_faces, right)] = "dir"
    bc = pp.BoundaryCondition(g, faces=bc_faces, cond=bc_type)
                    
    bc_values = np.zeros(g.num_faces)
    pf = p_ex(fc[0], fc[1], time * np.ones(nf)) # exact face pressures
    adv_f = advec_ex(fc[0], fc[1], time * np.ones(nf)) # exact advective velocities
    Adv_f = adv_f[0] * fn[0] + adv_f[1] * fn[1] # exact advective fluxes
    
    bc_values[top] = np.abs(Adv_f[top])
    bc_values[bottom] = np.abs(Adv_f[bottom])
    bc_values[left] = pf[left]
    bc_values[right] = pf[right]
    
    source_term = f_ex(cc[0], cc[1], time * np.ones(nc)) * V

    # Initialize data dictionary
    specified_data = {
        "second_order_tensor": perm,
        "bc": bc,
        "bc_values": bc_values,
        "source": source_term,
        "mass_weight": phi * np.ones(g.num_cells),
    }
    
    # Assing (or update) parameters
    if time == 0:
        pp.initialize_data(g, d, param_key, specified_data)
    else:
        d[pp.PARAMETERS][param_key]["bc_values"] = bc_values
        d[pp.PARAMETERS][param_key]["source"] = source_term
Exemple #17
0
    def add_flow_data(self):
        """
        Add the flow data to the grid bucket
        """
        keyword = self.flow_keyword
        # Iterate over nodes and assign data
        for g, d in self.gb:
            param = {}
            # Shorthand notation
            unity = np.ones(g.num_cells)
            zeros = np.zeros(g.num_cells)

            # Specific volume.
            specific_volume = np.power(
                self.param["aperture"], self.gb.dim_max() - g.dim
            )
            param["specific_volume"] = specific_volume
            # Tangential permeability
            if g.dim == self.gb.dim_max():
                kxx = self.param["km"]
            else:
                kxx = self.param["kf"] * specific_volume

            perm = pp.SecondOrderTensor(kxx * unity)
            param["second_order_tensor"] = perm

            # Source term
            param["source"] = zeros

            # Boundaries
            bound_faces = g.get_boundary_faces()
            bc_val = np.zeros(g.num_faces)

            param["bc"] = pp.BoundaryCondition(g, bound_faces, "dir")
            param["bc_values"] = bc_val

            pp.initialize_data(g, d, keyword, param)

        # Loop over edges and set coupling parameters
        for e, d in self.gb.edges():
            # Get higher dimensional grid
            g_h = self.gb.nodes_of_edge(e)[1]
            param_h = self.gb.node_props(g_h, pp.PARAMETERS)
            mg = d["mortar_grid"]
            specific_volume_h = (
                np.ones(mg.num_cells) * param_h[keyword]["specific_volume"]
            )
            kn = self.param["kn"] * specific_volume_h / (self.param["aperture"] / 2)
            param = {"normal_diffusivity": kn}
            pp.initialize_data(e, d, keyword, param)
    def set_scalar_parameters(self):
        gb = self.gb
        self.Nd = gb.dim_max()

        tensor_scale = self.scalar_scale / self.length_scale**2
        kappa = 1 * tensor_scale
        mass_weight = 1
        alpha = self.biot_alpha()
        for g, d in gb:
            bc = self.bc_type_scalar(g)
            bc_values = self.bc_values_scalar(g)
            source_values = self.source_scalar(g)

            a = self.compute_aperture(g)
            specific_volume = np.power(a,
                                       self.gb.dim_max() - g.dim) * np.ones(
                                           g.num_cells)
            diffusivity = pp.SecondOrderTensor(kappa * specific_volume *
                                               np.ones(g.num_cells))

            pp.initialize_data(
                g,
                d,
                self.scalar_parameter_key,
                {
                    "bc": bc,
                    "bc_values": bc_values,
                    "mass_weight": mass_weight * specific_volume,
                    "biot_alpha": alpha,
                    "source": source_values,
                    "second_order_tensor": diffusivity,
                    "time_step": self.time_step,
                },
            )

        # Assign diffusivity in the normal direction of the fractures.
        for e, data_edge in self.gb.edges():
            g1, _ = self.gb.nodes_of_edge(e)

            a = self.compute_aperture(g1)
            mg = data_edge["mortar_grid"]

            normal_diffusivity = 2 / kappa * mg.slave_to_mortar_int() * a

            data_edge = pp.initialize_data(
                e,
                data_edge,
                self.scalar_parameter_key,
                {"normal_diffusivity": normal_diffusivity},
            )
Exemple #19
0
    def _set_data_nodes(self):
        """ Method to set the data for the nodes (grids) of the grid bucket

        """
        for g, d in self.gb:

            param = {}
            param_mass = {}

            unity = np.ones(g.num_cells)
            zeros = np.zeros(g.num_cells)
            empty = np.empty(0)

            d["tol"] = self.data["tol"]

            # Boundaries
            b_faces = g.tags["domain_boundary_faces"].nonzero()[0]
            if b_faces.size:
                labels, bc_val = self.bc_flag(self.gb, g, self.data,
                                              self.data["tol"])
                param["bc"] = pp.BoundaryCondition(g, b_faces, labels)
            else:
                bc_val = np.zeros(g.num_faces)
                param["bc"] = pp.BoundaryCondition(g, empty, empty)

            param["bc_values"] = bc_val
            pp.initialize_data(g, d, self.discr_name, param)
            d[pp.PARAMETERS][self.discr_name][self.flux] = d[pp.STATE][
                self.flow.flux]

            param_mass["mass_weight"] = 1. / self.time_step
            pp.initialize_data(g, d, self.mass_name, param_mass)

            # set the primary variable
            d[pp.PRIMARY_VARIABLES] = {self.variable: {"cells": 1}}
            # set the discretization
            node_discr = self.discr(self.discr_name)
            node_mass = self.mass(self.mass_name)
            d[pp.DISCRETIZATION] = {
                self.variable: {
                    self.discr_name: node_discr,
                    self.mass_name: node_mass
                }
            }
            # set the discretization matrix
            d[pp.DISCRETIZATION_MATRICES] = {
                self.discr_name: {},
                self.mass_name: {}
            }
Exemple #20
0
 def set_mechanics_parameters(self) -> None:
     """
     Set the parameters for the simulation.
     """
     super().set_mechanics_parameters()
     for g, d in self.gb:
         if g.dim == self.Nd:
             pp.initialize_data(
                 g,
                 d,
                 self.mechanics_temperature_parameter_key,
                 {
                     "biot_alpha": self.biot_beta(g),
                     "bc_values": self.bc_values_mechanics(g),
                 },
             )
Exemple #21
0
def set_params_disrcetize(g, ambient_dim, method, periodic=False):
    g.compute_geometry()
    keyword = "flow"

    if periodic:
        south = g.face_centers[1] < np.min(g.nodes[1]) + 1e-8
        north = g.face_centers[1] > np.max(g.nodes[1]) - 1e-8
        bc = pp.BoundaryCondition(g, north + south, "per")
        south_idx = np.argwhere(south).ravel()
        north_idx = np.argwhere(north).ravel()
        bc.set_periodic_map(np.vstack((south_idx, north_idx)))
    else:
        bc = pp.BoundaryCondition(g)
    k = pp.SecondOrderTensor(np.ones(g.num_cells))

    params = {
        "bc": bc,
        "second_order_tensor": k,
        "mpfa_inverter": "python",
        "ambient_dimension": ambient_dim,
    }

    data = pp.initialize_data(g, {}, keyword, params)
    if method == "mpfa":
        discr = pp.Mpfa(keyword)
    elif method == "tpfa":
        discr = pp.Tpfa(keyword)
    discr.discretize(g, data)

    flux = data[pp.DISCRETIZATION_MATRICES][keyword][discr.flux_matrix_key]
    vector_source = data[pp.DISCRETIZATION_MATRICES][keyword][
        discr.vector_source_matrix_key]
    div = pp.fvutils.scalar_divergence(g)
    return flux, vector_source, div
Exemple #22
0
 def _set_scalar_parameters(self) -> None:
     """Set parameters for the pressure / mass conservation equation."""
     # Most values are handled as if this was a poro-elastic problem
     super()._set_scalar_parameters()
     for g, d in self.gb:
         t2s_coupling = (
             self._scalar_temperature_coupling_coefficient(g)
             * self._specific_volume(g)
             * self.temperature_scale
         )
         pp.initialize_data(
             g,
             d,
             self.t2s_parameter_key,
             {"mass_weight": t2s_coupling, "time_step": self.time_step},
         )
    def set_scalar_parameters(self):

        for g, d in self.gb:
            a = self.compute_aperture(g)
            specific_volumes = self.specific_volumes(g)

            # Define boundary conditions for flow
            bc = self.bc_type_scalar(g)
            # Set boundary condition values
            bc_values = self.bc_values_scalar(g)

            biot_coefficient = self.biot_alpha(g)
            compressibility = self.fluid.COMPRESSIBILITY

            mass_weight = compressibility * self.porosity(g)
            if g.dim == self.Nd:
                mass_weight += (biot_coefficient -
                                self.porosity(g)) / self.rock.BULK_MODULUS
            mass_weight *= self.scalar_scale * specific_volumes

            pp.initialize_data(
                g,
                d,
                self.scalar_parameter_key,
                {
                    "bc": bc,
                    "bc_values": bc_values,
                    "mass_weight": mass_weight,
                    "biot_alpha": biot_coefficient,
                    "time_step": self.time_step,
                    "source": self.aperture_update(g, d),
                },
            )

            t2s_coupling = (self.scalar_temperature_coupling_coefficient(g) *
                            specific_volumes * self.temperature_scale)
            pp.initialize_data(
                g,
                d,
                self.t2s_parameter_key,
                {
                    "mass_weight": t2s_coupling,
                    "time_step": self.time_step
                },
            )

        self.set_permeability_from_aperture()
Exemple #24
0
    def _initial_condition(self) -> None:
        """
        In addition to the values set by the parent class, we set initial value for the
        temperature variable, and a previous iterate value for the scalar value. The
        latter is used for computation of Darcy fluxes, needed for the advective term of
        the energy equation. The Darcy flux parameter is also initialized.
        """
        super()._initial_condition()

        for g, d in self.gb:
            # Initial value for the scalar variable.
            cell_zeros = np.zeros(g.num_cells)
            state = {self.temperature_variable: cell_zeros}
            iterate = {self.scalar_variable: cell_zeros}  # For initial flux
            pp.set_state(d, state)
            pp.set_iterate(d, iterate)
            # Initial Darcy fluxes for advective flux.
            pp.initialize_data(
                g,
                d,
                self.temperature_parameter_key,
                {
                    "darcy_flux": np.zeros(g.num_faces),
                },
            )

        for e, d in self.gb.edges():
            mg = d["mortar_grid"]
            cell_zeros = np.zeros(mg.num_cells)
            state = {
                self.mortar_temperature_variable: cell_zeros,
                self.mortar_temperature_advection_variable: cell_zeros,
            }
            iterate = {self.mortar_scalar_variable: cell_zeros}

            pp.set_state(d, state)
            pp.set_iterate(d, iterate)
            # Initial Darcy fluxes for advective flux.
            d = pp.initialize_data(
                e,
                d,
                self.temperature_parameter_key,
                {
                    "darcy_flux": np.zeros(mg.num_cells),
                },
            )
Exemple #25
0
 def _set_mechanics_parameters(self) -> None:
     """
     Set the parameters for the simulation.
     """
     super()._set_mechanics_parameters()
     for g, d in self.gb:
         if g.dim == self._Nd:
             pp.initialize_data(
                 g,
                 d,
                 self.mechanics_temperature_parameter_key,
                 {
                     "biot_alpha": self._biot_beta(g),
                     "bc_values": self._bc_values_mechanics(g),
                     "p_reference": np.zeros(g.num_cells),
                 },
             )
Exemple #26
0
    def test_assembly(self):
        # Test the assemble_matrix_rhs method, with vector sources included.
        # The rest of the setup is identical to that in
        # self.test_2d_horizontal_ambient_dim_2()

        # Random size of the domain
        dx = np.random.rand(1)[0]

        # 2x2 grid of the random size
        g = pp.CartGrid([2, 2], [2 * dx, 2 * dx])

        # Hhe vector source is a 2-vector per cell
        ambient_dim = 2

        g.compute_geometry()

        bc = pp.BoundaryCondition(g)
        k = pp.SecondOrderTensor(np.ones(g.num_cells))

        # Make source strength another random number
        grav_strength = np.random.rand(1)

        # introduce a source term in x-direction
        g_x = np.zeros(g.num_cells * ambient_dim)
        g_x[::ambient_dim] = -1 * grav_strength

        params = {
            "bc": bc,
            "bc_values": np.zeros(g.num_faces),
            "second_order_tensor": k,
            "mpfa_inverter": "python",
            "ambient_dimension": ambient_dim,
            "vector_source": g_x,
        }

        data = pp.initialize_data(g, {}, self.keyword, params)

        discr = pp.Mpfa(self.keyword)
        discr.discretize(g, data)

        A, b = discr.assemble_matrix_rhs(g, data)

        p_x = np.linalg.pinv(A.toarray()).dot(b)

        # The solution should be higher in the first x-row of cells, with magnitude
        # controlled by grid size and source stregth
        self.assertTrue(np.allclose(p_x[0] - p_x[1], dx * grav_strength))
        self.assertTrue(np.allclose(p_x[2] - p_x[3], dx * grav_strength))
        # The solution should be equal for equal x-coordinate
        self.assertTrue(np.allclose(p_x[0], p_x[2]))
        self.assertTrue(np.allclose(p_x[1], p_x[3]))

        data[pp.STATE] = {"pressure": p_x}
        pp.fvutils.compute_darcy_flux(g, data=data)
Exemple #27
0
 def set_scalar_parameters(self) -> None:
     """ Set parameters for the pressure / mass conservation equation.
     """
     # Most values are handled as if this was a poro-elastic problem
     super().set_scalar_parameters()
     for g, d in self.gb:
         a = self.compute_aperture(g)
         specific_volume = np.power(a,
                                    self.gb.dim_max() - g.dim) * np.ones(
                                        g.num_cells)
         t2s_coupling = (self.scalar_temperature_coupling_coefficient(g) *
                         specific_volume * self.temperature_scale)
         pp.initialize_data(
             g,
             d,
             self.t2s_parameter_key,
             {
                 "mass_weight": t2s_coupling,
                 "time_step": self.time_step
             },
         )
    def set_mechanics_parameters(self):
        gb = self.gb
        for g, d in gb:
            if g.dim == self.Nd:
                # Rock parameters
                rock = self.rock
                lam = rock.LAMBDA * np.ones(g.num_cells) / self.scalar_scale
                mu = rock.MU * np.ones(g.num_cells) / self.scalar_scale
                C = pp.FourthOrderTensor(mu, lam)

                bc = self.bc_type_mechanics(g)

                bc_values = self.bc_values_mechanics(g)
                sources = self.source_mechanics(g)
                pp.initialize_data(
                    g,
                    d,
                    self.mechanics_parameter_key,
                    {
                        "bc": bc,
                        "bc_values": bc_values,
                        "source": sources,
                        "fourth_order_tensor": C,
                        "biot_alpha": self.biot_alpha()
                    },
                )
                d[pp.PARAMETERS].set_from_other(
                    self.mechanics_parameter_key,
                    self.scalar_parameter_key,
                    ["aperture", "time_step"],
                )
            elif g.dim == self.Nd - 1:
                friction = self._set_friction_coefficient(g)
                pp.initialize_data(
                    g,
                    d,
                    self.mechanics_parameter_key,
                    {"friction_coefficient": friction},
                )
                pp.initialize_data(g, d, self.mechanics_parameter_key, {})

        for e, d in gb.edges():
            mg = d["mortar_grid"]
            # Parameters for the surface diffusion. No clue about values
            mu = self.rock.MU
            lmbda = self.rock.LAMBDA
            pp.initialize_data(mg, d, self.mechanics_parameter_key, {
                "mu": mu,
                "lambda": lmbda
            })
Exemple #29
0
    def _set_data_nodes(self):
        """ Method to set the data for the nodes (grids) of the grid bucket

        """
        for g, d in self.gb:

            param = {}

            d["Aavatsmark_transmissibilities"] = True
            d["tol"] = self.data["tol"]

            # assign permeability
            if g.dim == 2:
                perm = pp.SecondOrderTensor(kxx=self.data["kxx"], kyy=self.data["kyy"], kxy=self.data["kxy"])
                param["second_order_tensor"] = perm
            elif g.dim == 1:
                k = self.data["k"][g.frac_num]
                perm = pp.SecondOrderTensor(kxx=k*np.ones(g.num_cells))
                param["second_order_tensor"] = perm

            # Boundaries
            b_faces = g.tags["domain_boundary_faces"].nonzero()[0]
            if b_faces.size:
                labels, bc_val = self.bc_flag(self.gb, g, self.data, self.data["tol"])
                param["bc"] = pp.BoundaryCondition(g, b_faces, labels)
            else:
                bc_val = np.zeros(g.num_faces)
                param["bc"] = pp.BoundaryCondition(g, np.empty(0), np.empty(0))

            param["bc_values"] = bc_val
            pp.initialize_data(g, d, self.model, param)

            # set the primary variable
            d[pp.PRIMARY_VARIABLES] = {self.variable: {"cells": 1}}
            # set the discretization
            node_discr = self.discr(self.model)
            d[pp.DISCRETIZATION] = {self.variable: {self.discr_name: node_discr}}
            # set the discretization matrix
            d[pp.DISCRETIZATION_MATRICES] = {self.model: {}}
Exemple #30
0
    def vector_source(self):
        """ Set gravity as a vector source term in the fluid flow equations"""
        if not self.params.gravity:
            return

        gb = self.gb
        scalar_key = self.scalar_parameter_key
        ls, ss = self.params.length_scale, self.params.scalar_scale
        for g, d in gb:
            # minus sign to convert from positive z downward (depth) to positive upward.
            gravity = -pp.GRAVITY_ACCELERATION * self.density(g) * (ls / ss)
            vector_source = np.zeros((self.Nd, g.num_cells))
            vector_source[-1, :] = gravity
            vector_params = {
                "vector_source": vector_source.ravel("F"),
                "ambient_dimension": self.Nd,
            }
            pp.initialize_data(g, d, scalar_key, vector_params)

        for e, de in gb.edges():
            mg: pp.MortarGrid = de["mortar_grid"]
            g_l, _ = gb.nodes_of_edge(e)
            a_l = self.aperture(g_l, scaled=True)

            # Compute gravity on the slave grid
            rho_g = -pp.GRAVITY_ACCELERATION * self.density(g_l) * (ls / ss)

            # Multiply by (a/2) to "cancel out" the normal gradient of the diffusivity
            # (see also self.set_permeability_from_aperture)
            gravity_l = rho_g * (a_l / 2)

            # Take the gravity from the slave grid and project to the interface
            gravity_mg = mg.slave_to_mortar_avg() * gravity_l

            vector_source = np.zeros((self.Nd, mg.num_cells))
            vector_source[-1, :] = gravity_mg
            gravity = vector_source.ravel("F")

            pp.initialize_data(mg, de, scalar_key, {"vector_source": gravity})