Esempio n. 1
0
def _tag_injection_cell(
    gb: pp.GridBucket, g: pp.Grid, pts: np.ndarray, length_scale
) -> None:
    """ Helper method to tag find closest point on g to pts

    The tag is set locally to g and to node props on gb.
    length_scale is used to log the unscaled distance to
    the injection cell from pts.

    Parameters
    ----------
    gb : pp.GridBucket
    g : pp.Grid
    pts : np.ndarray, shape: (3,1)
    length_scale : float

    """
    assert pts.shape == (3, 1), "We only consider one point; array needs shape 3x1"
    tags = np.zeros(g.num_cells)
    ids, dsts = g.closest_cell(pts, return_distance=True)
    tags[ids] = 1
    g.tags["well_cells"] = tags
    d = gb.node_props(g)
    pp.set_state(d, {"well": tags})

    # Log information on the injection point
    logger.info(
        f"Closest cell found has (unscaled) distance: {dsts[0] * length_scale:4f}\n"
        f"ideal (scaled) point coordinate: {pts.T}\n"
        f"nearest (scaled) cell center coordinate: {g.cell_centers[:, ids].T}\n"
    )
    def _tag_well_cells(self):
        """
        Tag well cells with unitary values, positive for injection cells and negative
        for production cells.
        """
        for g, d in self.gb:
            tags = np.zeros(g.num_cells)
            if g.dim < self._Nd:
                s = self.box["xmax"]
                ny = 25
                # Avoid specifying a point on a face (having non-unique nearest
                # cell centre neighbour) by adding eps
                eps = 0.01
                ny = 26  # cf grid construction
                p1 = np.array([[(s + eps) / 2], [11.7 * s / (ny)], [s / 2]])
                p2 = np.array([[s / 2], [(14.2) * s / ny], [(s + eps) / 2]])

                if d["node_number"] == 1:
                    distances = pp.distances.point_pointset(p1, g.cell_centers)
                    indexes = np.argsort(distances)
                    tags[indexes[0]] = 1  # injection well
                elif d["node_number"] == 2:
                    distances = pp.distances.point_pointset(p2, g.cell_centers)
                    indexes = np.argsort(distances)

                    tags[indexes[0]] = -1

            g.tags["well_cells"] = tags
            pp.set_state(d, {"well": tags.copy()})
Esempio n. 3
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)
 def initial_condition(self) -> None:
     for g, d in self.gb:
         # Initial value for the scalar variable.
         initial_scalar_value = np.zeros(g.num_cells)
         d[pp.STATE].update({self.scalar_variable: initial_scalar_value})
     for _, d in self.gb.edges():
         mg = d["mortar_grid"]
         initial_value = np.zeros(mg.num_cells)
         state = {self.mortar_scalar_variable: initial_value}
         pp.set_state(d, state)
Esempio n. 5
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 initial_condition(self, time=0):
        """
        Initial guess for Newton iteration.
        """
        gb = self.gb
        key_m = self.mechanics_parameter_key

        for g, d in gb:
            nc_nd = g.num_cells * self.Nd
            # Initial value for the scalar variable.
            initial_scalar_value = self.s_0 * np.ones(g.num_cells)

            if g.dim == self.Nd:
                initial_displacements = np.zeros(nc_nd)
                # Initialize displacement variable
                state = {
                    self.displacement_variable: initial_displacements,
                    self.scalar_variable: initial_scalar_value,
                    key_m: {
                        "bc_values": d[pp.PARAMETERS][key_m]["bc_values"]
                    },
                }
            elif g.dim == self.Nd - 1:
                # Initialize contact variable
                traction = np.vstack((
                    np.zeros((self.Nd - 1, g.num_cells)),
                    -100 * np.ones(g.num_cells),
                )).ravel(order="F")
                state = {
                    self.contact_traction_variable: traction,
                    "previous_iterate": {
                        self.contact_traction_variable: traction
                    },
                    self.scalar_variable: initial_scalar_value,
                }
            pp.set_state(d, state)

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

            initial_displacements = np.zeros(mg.num_cells * self.Nd)

            if mg.dim == self.Nd - 1:
                state = {
                    self.mortar_scalar_variable: np.zeros(mg.num_cells),
                    self.mortar_displacement_variable: initial_displacements,
                    "previous_iterate": {
                        self.mortar_displacement_variable:
                        initial_displacements
                    },
                }
                pp.set_state(d, state)
Esempio n. 7
0
    def well_cells(self) -> None:
        """
        Tag well cells with unity values, positive for injection cells and
        negative for production cells.
        """
        # Initiate all tags to zero
        for g, d in self.gb:
            tags = np.zeros(g.num_cells)
            g.tags["well_cells"] = tags
            pp.set_state(d, {"well": tags.copy()})

        # Set injection cells
        self.params.well_cells(self.params, self.gb)
Esempio n. 8
0
    def tag_tunnel_cells(self):
        """ Tag tunnel-shearzone intersections

        Compute the nearest cell for each shear zone that intersects each of the
        tunnels.

        tunnel_sz is the following table:

            borehole   x_gts    y_gts   z_gts shearzone
        591       AU  72.625  125.321  33.436      S1_1
        592       VE   9.735   88.360  35.419      S1_1
        593       AU  74.565  135.311  33.858      S1_2
        594       VE  10.917   95.617  34.431      S1_2
        595       AU  74.839  143.317  33.611      S1_3
        596       VE  18.560  107.674  34.916      S1_3
        597       AU  72.094  106.617  32.813      S3_1
        598       VE  22.420  113.208  33.608      S3_1
        599       AU  72.185  110.025  33.639      S3_2
        600       VE  25.125  118.238  33.762      S3_2
        """
        tunnel_cells_key = "tunnel_cells"
        shearzones = self.params.shearzone_names
        tunnels = ["AU", "VE"]
        # Fetch tunnel-shearzone intersections
        isc_data = self.params.isc_data
        df = isc_data.structures
        _mask = df["borehole"].isin(tunnels) & df["shearzone"].isin(shearzones)
        t_sz = df[_mask]
        keepcols = ["borehole", "x_gts", "y_gts", "z_gts", "shearzone"]
        tunnel_sz = t_sz[keepcols].reset_index(drop=True)

        gb = self.gb
        for g, d in gb:
            g.tags[tunnel_cells_key] = np.zeros(g.num_cells, dtype=bool)
            pp.set_state(d, {tunnel_cells_key: np.zeros(g.num_cells, dtype=bool)})

        def _tag_intersection_cell(row):
            _coord = (
                row[["x_gts", "y_gts", "z_gts"]].to_numpy(dtype=float).reshape((3, -1))
            )
            coord = _coord * (pp.METER / self.params.length_scale)
            shearzone = row["shearzone"]
            grid: pp.Grid = self.grids_by_name(shearzone)[0]
            data = gb.node_props(grid)
            tags = np.zeros(grid.num_cells, dtype=bool)
            ids, dsts = grid.closest_cell(coord, return_distance=True)
            tags[ids] = True
            grid.tags[tunnel_cells_key] |= tags
            data[pp.STATE][tunnel_cells_key] |= tags

        tunnel_sz.apply(_tag_intersection_cell, axis=1)
    def test_add_state_twice(self, empty_dict):
        """Add two state dictionaries.

        The existing foo value should be overwritten, while bar should be kept.
        """
        d = empty_dict
        d1 = {"foo": 1, "bar": 2}
        d2 = {"foo": 3, "spam": 4}

        pp.set_state(d, d1)
        pp.set_state(d, d2)
        for key, val in zip(["foo", "bar", "spam"], [3, 2, 4]):
            assert key in d[pp.STATE]
            assert d[pp.STATE][key] == val
 def well_cells(self):
     """
     Assign unitary values to injection cells (positive) and production cells
     (negative).
     
     The wells are marked by g.tags['well_cells'], and in the data dictionary
     of this well.
     
     """
     for g, d in self.gb:
         tags = np.zeros(g.num_cells)
         if g.dim == self.Nd - 1:  # We should not ask for frac_num in intersections
             if g.frac_num == 4:
                 tags[1] = 1
         g.tags["well_cells"] = tags
         pp.set_state(d, {"well": tags.copy()})
Esempio n. 11
0
    def initial_condition(self):
        """ Set initial guess for the variables.

        The displacement is set to zero in the Nd-domain, and at the fracture interfaces
        The displacement jump is thereby also zero.

        The contact pressure is set to zero in the tangential direction,
        and -1 (that is, in contact) in the normal direction.

        """

        for g, d in self.gb:
            if g.dim == self.Nd:
                # Initialize displacement variable
                state = {
                    self.displacement_variable: np.zeros(g.num_cells * self.Nd)
                }

            elif g.dim == self.Nd - 1:
                # Initialize contact variable
                traction = np.vstack(
                    (np.zeros((g.dim, g.num_cells)),
                     -1 * np.ones(g.num_cells))).ravel(order="F")
                state = {
                    "previous_iterate": {
                        self.contact_traction_variable: traction
                    },
                    self.contact_traction_variable: traction,
                }
            else:
                state = {}
            pp.set_state(d, state)

        for _, d in self.gb.edges():
            mg = d["mortar_grid"]

            if mg.dim == self.Nd - 1:
                size = mg.num_cells * self.Nd
                state = {
                    self.mortar_displacement_variable: np.zeros(size),
                    "previous_iterate": {
                        self.mortar_displacement_variable: np.zeros(size)
                    },
                }
            else:
                state = {}
            pp.set_state(d, state)
Esempio n. 12
0
    def set_state(self, u_mortar, contact_force):
        # Replacement for the method 'initial_condition'. Change name to avoid
        # conflict with different parameters.

        for g, d in self.gb:
            if g.dim == self._Nd:
                # Initialize displacement variable
                state = {
                    self.displacement_variable:
                    np.zeros(g.num_cells * self._Nd)
                }

            elif g.dim == self._Nd - 1:
                # Initialize contact variable
                traction = contact_force
                state = {
                    # Set value for previous iterate. This becomes what decides the
                    # state used in discretization.
                    pp.ITERATE: {
                        self.contact_traction_variable: traction
                    },
                    # Zero state in previous time step, just to avoid confusion
                    self.contact_traction_variable:
                    0 * traction,
                }
            else:
                state = {}
            pp.set_state(d, state)

        for _, d in self.gb.edges():
            mg = d["mortar_grid"]

            if mg.dim == self._Nd - 1:
                state = {
                    # Set a zero state in previous time step
                    self.mortar_displacement_variable:
                    0 * u_mortar,
                    # Set value for previous iterate. This becomes what decides the
                    # state used in discretization.
                    pp.ITERATE: {
                        self.mortar_displacement_variable: u_mortar
                    },
                }
            else:
                state = {}
            pp.set_state(d, state)
    def test_add_iterate_twice_and_state(self, empty_dict):
        """Add two state dictionaries.

        The existing foo value should be overwritten, while bar should be kept.
        Setting values in pp.STATE should not affect the iterate values.
        """
        d = empty_dict
        d1 = {"foo": 1, "bar": 2}
        d2 = {"foo": 3, "spam": 4}

        pp.set_iterate(d, d1)
        pp.set_iterate(d, d2)
        pp.set_state(d, {"foo": 5})
        for key, val in zip(["foo", "bar", "spam"], [3, 2, 4]):
            assert key in d[pp.STATE][pp.ITERATE]
            assert d[pp.STATE][pp.ITERATE][key] == val
        assert d[pp.STATE]["foo"] == 5
Esempio n. 14
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),
                },
            )
    def well_cells(self):
        """
        Tag well cells with unity values, positive for injection cells and
        negative for production cells.
        """
        # TODO: Use unscaled grid to find result.
        df = self.isc.borehole_plane_intersection()
        # Borehole-shearzone intersection of interest
        bh_sz = self.source_scalar_borehole_shearzone

        _mask = (df.shearzone == bh_sz["shearzone"]) & (df.borehole
                                                        == bh_sz["borehole"])
        result = df.loc[_mask, ("x_sz", "y_sz", "z_sz")]
        if result.empty:
            raise ValueError("No intersection found.")

        pts = result.to_numpy().T / self.length_scale
        assert pts.shape[1] == 1, "Should only be one intersection"
        tagged = False

        for g, d in self.gb:
            tags = np.zeros(g.num_cells)

            # Get name of grid
            grid_name = self.gb.node_props(g, "name")

            # We only tag cells in the desired fracture
            if grid_name == bh_sz['shearzone']:
                logger.info(
                    f"Tagging grid of name: {grid_name}, and dimension {g.dim}"
                )
                logger.info(f"Setting non-zero source value for pressure")

                ids, dsts = g.closest_cell(pts, return_distance=True)
                logger.info(f"Closest cell found has distance: {dsts[0]:4f}")

                # Tag the injection cell
                tags[ids] = 1
                tagged = True

            g.tags["well_cells"] = tags
            pp.set_state(d, {"well": tags.copy()})

        if not tagged:
            logger.warning("No injection cell was tagged.")
    def _initial_condition(self) -> None:
        """
        Initialize fracture pressure.

        Returns
        -------
        None
        """
        super()._initial_condition()
        for g, d in self.gb:
            if g.dim == self.Nd - 1:
                pp.set_state(
                    d,
                    {
                        self.scalar_variable:
                        self.params["boundary_traction"] * np.ones(g.num_cells)
                    },
                )
Esempio n. 17
0
def initial_condition(data_dictionary, variable_flow, variable_mechanics,
                      exact_data):
    """
    Establishes initial condition.

    Parameters:
        data_dictionary (Dictionary)  : Model's data dictionary
        variable_flow (String)        : Primary variable of the flow problem
        variable_mechanics (String)   : Primary varibale of the mechanics problem
        exact_data (Dictionary)       : Containing the solutions and the
                                        top boundary condition.
    """

    d_0 = exact_data[variable_mechanics][0]
    p_0 = exact_data[variable_flow][0]

    state = {variable_mechanics: d_0, variable_flow: p_0}
    pp.set_state(data_dictionary, state)
Esempio n. 18
0
def nd_sides_shearzone_injection_cell(
    params: FlowParameters, gb: pp.GridBucket, reset_frac_tags: bool = True,
) -> None:
    """ Tag the Nd cells surrounding a shear zone injection point

    Parameters
    ----------
    params : FlowParameters
        parameters that contain "source_scalar_borehole_shearzone"
        (with "shearzone", and "borehole") and "length_scale".
    gb : pp.GridBucket
        grid bucket
    reset_frac_tags : bool [Default: True]
        if set to False, keep injection tag in the shear zone.
    """
    # Shorthand
    shearzone = params.source_scalar_borehole_shearzone.get("shearzone")

    # First, tag the fracture cell, and get the tag
    shearzone_injection_cell(params, gb)
    fracture = gb.get_grids(lambda g: gb.node_props(g, "name") == shearzone)[0]
    tags = fracture.tags["well_cells"]
    # Second, map the cell to the Nd grid
    nd_grid: pp.Grid = gb.grids_of_dimension(gb.dim_max())[0]
    data_edge = gb.edge_props((fracture, nd_grid))
    mg: pp.MortarGrid = data_edge["mortar_grid"]

    slave_to_master_face = mg.mortar_to_master_int() * mg.slave_to_mortar_int()
    face_to_cell = nd_grid.cell_faces.T
    slave_to_master_cell = face_to_cell * slave_to_master_face
    nd_tags = np.abs(slave_to_master_cell) * tags

    # Set tags on the nd-grid
    nd_grid.tags["well_cells"] = nd_tags
    ndd = gb.node_props(nd_grid)
    pp.set_state(ndd, {"well": tags})

    if reset_frac_tags:
        # reset tags on the fracture
        zeros = np.zeros(fracture.num_cells)
        fracture.tags["well_cells"] = zeros
        d = gb.node_props(fracture)
        pp.set_state(d, {"well": zeros})
def _tag_ivar_well_cells(_, gb: pp.GridBucket) -> None:
    """
    Tag well cells with unitary values, positive for injection cells and negative
    for production cells.
    """
    box = gb.bounding_box(as_dict=True)
    nd = gb.dim_max()
    for g, d in gb:
        tags = np.zeros(g.num_cells)
        if g.dim < nd:
            point = np.array([[(box["xmin"] + box["xmax"]) / 2], [box["ymax"]], [0],])
            distances = pp.distances.point_pointset(point, g.cell_centers)
            indexes = np.argsort(distances)
            if d["node_number"] == 1:
                tags[indexes[-1]] = 1  # injection
            elif d["node_number"] == 3:
                tags[indexes[-1]] = -1  # production
                # write_well_cell_to_csv(g, indexes[-1], self)
        g.tags["well_cells"] = tags
        pp.set_state(d, {"well": tags.copy()})
    def initial_condition(self):
        """
        Initial guess for Newton iteration, scalar variable and bc_values (for time
        discretization).

        When stimulation phase is reached, we use displacements of last solution in
        initialize phase as initial condition for the cell displacements.
        """
        super().initial_condition()
        # TODO: Set hydrostatic initial condition

        # TODO: Scale variables
        if self.current_phase > 0:  # Stimulation phase

            for g, d in self.gb:
                if g.dim == self.Nd:
                    initial_displacements = d["initial_cell_displacements"]
                    pp.set_state(
                        d, {self.displacement_variable: initial_displacements})

            for e, d in self.gb.edges():
                if e[0].dim == self.Nd:
                    try:
                        initial_displacements = d["initial_cell_displacements"]
                    except KeyError:
                        logger.warning(
                            "We got KeyError on d['initial_cell_displacements']."
                        )
                        mg = d["mortar_grid"]
                        initial_displacements = np.zeros(mg.num_cells *
                                                         self.Nd)
                    state = {
                        self.mortar_displacement_variable:
                        initial_displacements,
                        "previous_iterate": {
                            self.mortar_displacement_variable:
                            initial_displacements,
                        },
                    }
                    pp.set_state(d, state)
Esempio n. 21
0
    def copy_biot_discretizations(self) -> None:
        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
        d[pp.PARAMETERS][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)
Esempio n. 22
0
    def _tag_well_cells(self):
        """
        Tag well cells with unitary values, positive for injection cells and negative
        for production cells.
        """
        for g, d in self.gb:
            tags = np.zeros(g.num_cells)
            if g.dim < self.Nd:
                point = np.array(
                    [
                        [(self.box["xmin"] + self.box["xmax"]) / 2],
                        [self.box["ymin"]],
                        [0],
                    ]
                )
                distances = pp.distances.point_pointset(point, g.cell_centers)
                indexes = np.argsort(distances)
                if d["node_number"] == 1:
                    tags[indexes[-1]] = 1  # injection

            g.tags["well_cells"] = tags
            pp.set_state(d, {"well": tags.copy()})
Esempio n. 23
0
    def initial_condition(self):
        """
        Initial guess for Newton iteration.
        """

        for g, d in self.gb:
            if g.dim == self.Nd:
                # Initialize displacement variable
                state = {
                    self.displacement_variable: np.zeros(g.num_cells * self.Nd)
                }

            elif g.dim == self.Nd - 1:
                # Initialize contact variable
                traction = np.vstack(
                    (np.zeros((g.dim, g.num_cells)),
                     -1 * np.ones(g.num_cells))).ravel(order="F")
                state = {
                    "previous_iterate": {
                        self.contact_traction_variable: traction
                    }
                }
            else:
                state = {}
            pp.set_state(d, state)

        for _, d in self.gb.edges():
            mg = d["mortar_grid"]

            if mg.dim == self.Nd - 1:
                size = mg.num_cells * self.Nd
                state = {
                    self.mortar_displacement_variable:
                    np.zeros(mg.num_cells * self.Nd),
                    "previous_iterate": {
                        self.mortar_displacement_variable: np.zeros(size)
                    },
                }
                pp.set_state(d, state)
Esempio n. 24
0
    def test_assemble_biot_rhs_transient(self):
        """ Test the assembly of a Biot problem with a non-zero rhs using the assembler.

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

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

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

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

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

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

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

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

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

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

        # Check that the solution has converged to the expected, uniform steady state
        # dictated by the BCs.
        self.assertTrue(
            np.all(np.isclose(x_i, np.ones((g.dim + 1) * g.num_cells))))
        "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


#%% Set initial states
for g, d in gb:
    cc = g.cell_centers
    pp.set_state(d)
    pp.set_iterate(d)
    d[pp.STATE][pressure_var] = p_ex(cc[0], cc[1], time * np.ones_like(cc[0]))
    d[pp.STATE][pp.ITERATE][pressure_var] = d[pp.STATE][pressure_var].copy()

#%% AD variables and manager
grid_list = [g for g, _ in gb]
dof_manager = pp.DofManager(gb)
equation_manager = pp.ad.EquationManager(gb, dof_manager)
p = equation_manager.merge_variables([(g, pressure_var) for g in grid_list])
p_m = p.previous_iteration()
p_n = p.previous_timestep()

#%% We let the density to be a non-linear function of the pressure
def rho(p):
    if isinstance(p, pp.ad.Ad_array):
Esempio n. 26
0
    def update_all_apertures(self, to_iterate=True):
        """
        To better control the aperture computation, it is done for the entire gb by a
        single function call. This also allows us to ensure the fracture apertures
        are updated before the intersection apertures are inherited.
        """
        gb = self.gb
        for g, d in gb:

            apertures = np.ones(g.num_cells)
            if g.dim == (self.Nd - 1):
                # Initial aperture

                apertures *= self.initial_aperture
                # Reconstruct the displacement solution on the fracture
                g_h = gb.node_neighbors(g)[0]
                data_edge = gb.edge_props((g, g_h))
                if pp.STATE in data_edge:
                    u_mortar_local = self.reconstruct_local_displacement_jump(
                        data_edge, from_iterate=to_iterate
                    )
                    apertures -= u_mortar_local[-1].clip(max=0)
            if to_iterate:
                pp.set_iterate(
                    d,
                    {"aperture": apertures.copy(), "specific_volume": apertures.copy()},
                )
            else:
                state = {
                    "aperture": apertures.copy(),
                    "specific_volume": apertures.copy(),
                }
                pp.set_state(d, state)

        for g, d in gb:
            parent_apertures = []
            num_parent = []
            if g.dim < (self.Nd - 1):
                for edges in gb.edges_of_node(g):
                    e = edges[0]
                    g_h = e[0]

                    if g_h == g:
                        g_h = e[1]

                    if g_h.dim == (self.Nd - 1):
                        d_h = gb.node_props(g_h)
                        if to_iterate:
                            a_h = d_h[pp.STATE][pp.ITERATE]["aperture"]
                        else:
                            a_h = d_h[pp.STATE]["aperture"]
                        a_h_face = np.abs(g_h.cell_faces) * a_h
                        mg = gb.edge_props(e)["mortar_grid"]
                        # Assumes g_h is master
                        a_l = (
                            mg.mortar_to_slave_avg()
                            * mg.master_to_mortar_avg()
                            * a_h_face
                        )
                        parent_apertures.append(a_l)
                        num_parent.append(np.sum(mg.mortar_to_slave_int().A, axis=1))
                    else:
                        raise ValueError("Intersection points not implemented in 3d")
                parent_apertures = np.array(parent_apertures)
                num_parents = np.sum(np.array(num_parent), axis=0)

                apertures = np.sum(parent_apertures, axis=0) / num_parents

                specific_volumes = np.power(apertures, self.Nd - g.dim)
                if to_iterate:
                    pp.set_iterate(
                        d,
                        {
                            "aperture": apertures.copy(),
                            "specific_volume": specific_volumes.copy(),
                        },
                    )
                else:
                    state = {
                        "aperture": apertures.copy(),
                        "specific_volume": specific_volumes.copy(),
                    }
                    pp.set_state(d, state)

        return apertures
    def update_all_apertures(self, to_iterate=True):
        """
        To better control the aperture computation, it is done for the entire gb by a
        single function call. This also allows us to ensure the fracture apertures
        are updated before the intersection apertures are inherited.
        The aperture of a fracture is the initial aperture + normal displacement jump.
        """
        gb = self.gb
        for g, d in gb:

            apertures = np.ones(g.num_cells)
            if g.dim == (self.Nd - 1):
                # Initial aperture

                apertures *= self.initial_aperture
                # Reconstruct the displacement solution on the fracture
                g_h = gb.node_neighbors(g)[0]
                data_edge = gb.edge_props((g, g_h))
                if pp.STATE in data_edge:
                    u_mortar_local = self.reconstruct_local_displacement_jump(
                        data_edge, from_iterate=to_iterate)
                    # Magnitudes of normal and tangential components
                    norm_u_n = np.absolute(u_mortar_local[-1])
                    norm_u_tau = np.linalg.norm(u_mortar_local[:-1], axis=0)
                    # Add contributions
                    slip_angle = np.pi / 2
                    apertures += norm_u_n + np.cos(slip_angle) * norm_u_tau
            if to_iterate:
                state = {
                    "iterate_aperture": apertures,
                    "iterate_specific_volume": apertures,
                }
            else:
                state = {"aperture": apertures, "specific_volume": apertures}
            pp.set_state(d, state)
        for g, d in gb:
            parent_apertures = []
            if g.dim < (self.Nd - 1):
                for edges in gb.edges_of_node(g):
                    e = edges[0]
                    g_h = e[0]

                    if g_h == g:
                        g_h = e[1]

                    if g_h.dim == (self.Nd - 1):
                        d_h = gb.node_props(g_h)
                        if to_iterate:
                            a_h = d_h[pp.STATE]["iterate_aperture"]
                        else:
                            a_h = d_h[pp.STATE]["aperture"]
                        a_h_face = np.abs(g_h.cell_faces) * a_h
                        mg = gb.edge_props(e)["mortar_grid"]
                        # Assumes g_h is master
                        a_l = (mg.mortar_to_slave_int() *
                               mg.master_to_mortar_int() * a_h_face)
                        parent_apertures.append(a_l)
                    else:
                        raise ValueError(
                            "Intersection points not implemented in 3d")
                parent_apertures = np.array(parent_apertures)
                apertures = np.mean(parent_apertures, axis=0)
                specific_volumes = np.product(parent_apertures, axis=0)
                if to_iterate:
                    state = {
                        "iterate_aperture": apertures,
                        "iterate_specific_volume": specific_volumes,
                    }
                else:
                    state = {
                        "aperture": apertures,
                        "specific_volume": specific_volumes
                    }
                pp.set_state(d, state)

        return apertures
 def test_add_empty_state(self, empty_dict):
     """Add an empty state dictionary"""
     d = empty_dict
     pp.set_state(d)
     assert pp.STATE in d
Esempio n. 29
0
def solve_mandel(
    grid_bucket,
    data_dictionary,
    parameter_keyword_flow,
    parameter_keyword_mechanics,
    variable_flow,
    variable_mechanics,
    assembler,
    boundary_conditions_dictionary,
):
    """
    The problem is soved by looping through all the time levels. The ouput
    is a dictionary containing the solutions for pressure and displacement
    for all discrete times.

    Parameters:
        grid_bucket (PorePy object):           Grid bucket
        data_dictionary (Dict):                Model's data dictionary
        parameter_keyword_flow (String):       Keyword for the flow parameter
        parameter_keyword_mechanics (String):  Keyword for the mechanics parameter
        variable_flow (String):                Primary variable of the flow problem
        variable_mechanics (String):           Primary variable of the mechanics problem
        assembler (PorePy object):             Assembler containing discretization
        boundary_conditions_dictionary (Dict): Dictionary containing boundary conditions

    Output
        sol (Dict):                            Dictionary containing numerial solutions
    """

    # Renaming variables
    gb = grid_bucket
    g = gb.grids_of_dimension(2)[0]
    d = data_dictionary
    kw_f = parameter_keyword_flow
    kw_m = parameter_keyword_mechanics
    variable_f = variable_flow
    variable_m = variable_mechanics
    bc_dict = boundary_conditions_dictionary

    # Retrieving time values from the data dicitionary
    time_values = d[pp.PARAMETERS][kw_f]["time_values"]

    # Create a dictionary to store the solutions
    sol = {
        variable_f: np.zeros((len(time_values), g.num_cells)),
        variable_m: np.zeros((len(time_values), g.dim * g.num_cells)),
    }
    sol[variable_f][0] = d[pp.STATE][variable_f]
    sol[variable_m][0] = d[pp.STATE][variable_m]

    # For convience, create pressure and displacement variables
    pressure = d[pp.STATE][variable_f]
    displacement = d[pp.STATE][variable_m]

    # Assemble equations
    # NOTE: The structure of the linear system is time-independent
    assembler = pp.Assembler(gb)

    # Time loop
    for t in range(len(time_values) - 1):

        # Update data for current time
        pp.set_state(d, {variable_m: displacement, variable_f: pressure})
        pp.set_state(d, {kw_m: {"bc_values": bc_dict[kw_m]["bc_values"][t]}})
        d[pp.PARAMETERS][kw_m]["bc_values"] = bc_dict[kw_m]["bc_values"][t + 1]

        # Assemble matrix and rhs and solve
        A, b = assembler.assemble_matrix_rhs()
        x = sps.linalg.spsolve(A, b)

        # Distribute primary variables
        assembler.distribute_variable(x)
        displacement = d[pp.STATE][variable_m]
        pressure = d[pp.STATE][variable_f]

        # Save in solution dictionary
        sol["pressure"][t + 1] = pressure
        sol["displacement"][t + 1] = displacement

        # Print progress on console
        sys.stdout.write(
            "\rSimulation progress: %d%%"
            % (np.ceil((t / (len(time_values) - 2)) * 100))
        )
        sys.stdout.flush()

    sys.stdout.write("\nThe simulation has ended without any errors!\n")

    return sol
Esempio n. 30
0
    def test_assemble_biot(self):
        """ Test the assembly of the Biot problem using the assembler.

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

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

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

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

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

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