예제 #1
0
    def test_biot(self):
        self.setup()

        g, g_larger = self.g, self.g_larger

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

        mechanics_keyword = "mechanics"
        flow_keyword = "flow"
        data_small = pp.initialize_default_data(
            g, {}, mechanics_keyword, specified_parameters=specified_data)

        def add_flow_data(g, d):
            d[pp.DISCRETIZATION_MATRICES][flow_keyword] = {}
            d[pp.PARAMETERS][flow_keyword] = {
                "bc": pp.BoundaryCondition(g),
                "second_order_tensor":
                pp.SecondOrderTensor(np.ones(g.num_cells)),
                "bc_values": np.zeros(g.num_faces),
                "inverter": "python",
                "mass_weight": np.ones(g.num_cells),
                "biot_alpha": 1,
            }

        discr = pp.Biot(mechanics_keyword=mechanics_keyword,
                        flow_keyword=flow_keyword)

        add_flow_data(g, data_small)

        discr.discretize(g, data_small)
        # Discretization on a small problem

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

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

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

        specified_data_larger = {
            "fourth_order_tensor": stiff_larger,
            "biot_alpha": 1
        }

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

        discr.discretize(g_larger, data_full)

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

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

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

        self._update_and_compare(
            data_small,
            data_partial,
            data_full,
            g_larger,
            keywords=[flow_keyword, mechanics_keyword],
            discr=discr,
        )
예제 #2
0
    def test_one_cell_a_time_node_keyword(self):
        # Update one and one cell, and verify that the result is the same as
        # with a single computation. The test is similar to what will happen
        # with a memory-constrained splitting.
        g = pp.CartGrid([3, 3])
        g.compute_geometry()

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

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

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

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

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

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

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

            discr.discretize(g, data)

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

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

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

            stress += partial_stress
            bound_stress += partial_bound

        self.assertTrue((stress_full - stress).max() < 1e-8)
        self.assertTrue((stress_full - stress).min() > -1e-8)
        self.assertTrue((bound_stress - bound_stress_full).max() < 1e-8)
        self.assertTrue((bound_stress - bound_stress_full).min() > -1e-8)
예제 #3
0
    def _test_one_cell_a_time_node_keyword(self):
        # EK: this test makes less sense after the notion of partial updates were
        # changed (git commit 41f754940). Decactive the test for now, pending an update
        # of the notion of discretization updates for FV methods.

        # Update one and one cell, and verify that the result is the same as
        # with a single computation. The test is similar to what will happen
        # with a memory-constrained splitting.
        g = pp.CartGrid([3, 3])
        g.compute_geometry()

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

        nd = g.dim
        nf = g.num_faces
        nc = g.num_cells

        grad_p = sps.csr_matrix((nf * nd, nc))
        div_u = sps.csr_matrix((nc, nc * nd))
        bound_div_u = sps.csr_matrix((nc, nf * nd))
        stab = sps.csr_matrix((nc, nc))
        bound_displacement_pressure = sps.csr_matrix((nf * nd, nc))

        faces_covered = np.zeros(g.num_faces, np.bool)
        cells_covered = np.zeros(g.num_cells, np.bool)

        bnd = pp.BoundaryConditionVectorial(g)
        specified_data = {
            "fourth_order_tensor": stiffness,
            "bc": bnd,
            #            "inverter": "python",
            "biot_alpha": 1,
        }
        keyword_mech = "mechanics"
        keyword_flow = "flow"
        data = pp.initialize_default_data(g, {},
                                          keyword_mech,
                                          specified_parameters=specified_data)
        data = pp.initialize_default_data(g, data, keyword_flow)

        discr = pp.Biot()
        discr.discretize(g, data)

        div_u_full = data[pp.DISCRETIZATION_MATRICES][keyword_flow][
            discr.div_u_matrix_key]
        bound_div_u_full = data[pp.DISCRETIZATION_MATRICES][keyword_flow][
            discr.bound_div_u_matrix_key]
        stab_full = data[pp.DISCRETIZATION_MATRICES][keyword_flow][
            discr.stabilization_matrix_key]
        grad_p_full = data[pp.DISCRETIZATION_MATRICES][keyword_mech][
            discr.grad_p_matrix_key]
        bound_pressure_full = data[pp.DISCRETIZATION_MATRICES][keyword_mech][
            discr.bound_pressure_matrix_key]

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

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

            discr.discretize(g, data)

            partial_div_u = data[pp.DISCRETIZATION_MATRICES][keyword_flow][
                discr.div_u_matrix_key]
            partial_bound_div_u = data[pp.DISCRETIZATION_MATRICES][
                keyword_flow][discr.bound_div_u_matrix_key]
            partial_grad_p = data[pp.DISCRETIZATION_MATRICES][keyword_mech][
                discr.grad_p_matrix_key]
            partial_stab = data[pp.DISCRETIZATION_MATRICES][keyword_flow][
                discr.stabilization_matrix_key]
            partial_bound_pressure = data[pp.DISCRETIZATION_MATRICES][
                keyword_mech][discr.bound_pressure_matrix_key]

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

            if np.any(faces_covered):
                del_faces = self.expand_indices_nd(
                    np.where(faces_covered)[0], g.dim)
                del_cells = np.where(cells_covered)[0]
                pp.fvutils.remove_nonlocal_contribution(
                    del_cells, 1, partial_div_u, partial_bound_div_u,
                    partial_stab)
                # del_faces is already expanded, set dimension to 1
                pp.fvutils.remove_nonlocal_contribution(
                    del_faces, 1, partial_grad_p, partial_bound_pressure)

            faces_covered[active_faces] = True
            cells_covered[ci] = True

            div_u += partial_div_u
            bound_div_u += partial_bound_div_u
            grad_p += partial_grad_p
            stab += partial_stab
            bound_displacement_pressure += partial_bound_pressure

        self.assertTrue((div_u_full - div_u).max() < 1e-8)
        self.assertTrue((bound_div_u_full - bound_div_u).min() > -1e-8)
        self.assertTrue((grad_p_full - grad_p).max() < 1e-8)
        self.assertTrue((stab_full - stab).min() > -1e-8)
        self.assertTrue(
            (bound_displacement_pressure - bound_pressure_full).min() > -1e-8)
def conv_test(n):

    # Analytical solution
    def mandel_solution(g, Nx, Ny, times, F, B, nu_u, nu, c_f, mu_s):

        # Some needed parameters
        a = np.max(g.face_centers[0])  # a = Lx
        x_cntr = g.cell_centers[0][:Nx]  # [m] vector of x-centers
        y_cntr = g.cell_centers[1][::Nx]  # [m] vector of y-centers

        # Solutions to tan(x) - ((1-nu)/(nu_u-nu)) x = 0
        """
        This is somehow tricky, we have to solve the equation numerically in order to
        find all the positive solutions to the equation. Later we will use them to 
        compute the infinite sums. Experience has shown that 200 roots are more than enough to
        achieve accurate results. Note that we find the roots using the bisection method.
        """
        f = lambda x: np.tan(x) - (
            (1 - nu) /
            (nu_u - nu)) * x  # define the algebraic eq. as a lambda function
        n_series = 200  # number of roots
        a_n = np.zeros(n_series)  # initializing roots array
        x0 = 0  # initial point
        for i in range(0, len(a_n)):
            a_n[i] = opt.bisect(
                f,  # function
                x0 + np.pi / 4,  # left point 
                x0 + np.pi / 2 -
                10000 * 2.2204e-16,  # right point (a tiny bit less than pi/2)
                xtol=1e-30,  # absolute tolerance
                rtol=1e-15  # relative tolerance
            )
            x0 += np.pi  # apply a phase change of pi to get the next root

        # Creating dictionary to store analytical solutions
        mandel_sol = dict()
        mandel_sol['p'] = np.zeros((len(times), len(x_cntr)))
        mandel_sol['u_x'] = np.zeros((len(times), len(x_cntr)))
        mandel_sol['u_y'] = np.zeros((len(times), len(y_cntr)))
        mandel_sol['sigma_yy'] = np.zeros((len(times), len(x_cntr)))

        # Terms needed to compute the solutions (these are constants)
        p0 = (2 * F * B * (1 + nu_u)) / (3 * a)
        ux0_1 = ((F * nu) / (2 * mu_s * a))
        ux0_2 = -((F * nu_u) / (mu_s * a))
        ux0_3 = F / mu_s
        uy0_1 = (-F * (1 - nu)) / (2 * mu_s * a)
        uy0_2 = (F * (1 - nu_u) / (mu_s * a))
        sigma0_1 = -F / a
        sigma0_2 = (-2 * F * B * (nu_u - nu)) / (a * (1 - nu))
        sigma0_3 = (2 * F) / a

        # Saving solutions for the initial conditions
        mandel_sol['p'][0] = ((F * B * (1 + nu_u)) / (3 * a)) * np.ones(Nx)
        mandel_sol['u_x'][0] = (F * nu_u * x_cntr) / (2 * mu_s * a)
        mandel_sol['u_y'][0] = ((-F * (1 - nu_u)) / (2 * mu_s * a)) * y_cntr

        # Storing solutions for the subsequent time steps
        for ii in range(1, len(times)):

            # Analytical Pressures
            p_sum = 0
            for n in range(len(a_n)):
                p_sum += (((np.sin(a_n[n])) /
                           (a_n[n] - (np.sin(a_n[n]) * np.cos(a_n[n])))) *
                          (np.cos((a_n[n] * x_cntr) / a) - np.cos(a_n[n])) *
                          np.exp((-(a_n[n]**2) * c_f * times[ii]) / (a**2)))

            mandel_sol['p'][ii] = p0 * p_sum

            # Analytical horizontal displacements
            ux_sum1 = 0
            ux_sum2 = 0
            for n in range(len(a_n)):
                ux_sum1 += ((np.sin(a_n[n]) * np.cos(a_n[n])) /
                            (a_n[n] - np.sin(a_n[n]) * np.cos(a_n[n])) *
                            np.exp((-(a_n[n]**2) * c_f * times[ii]) / (a**2)))
                ux_sum2 += ((np.cos(a_n[n]) /
                             (a_n[n] - (np.sin(a_n[n]) * np.cos(a_n[n])))) *
                            np.sin(a_n[n] * (x_cntr / a)) * np.exp(
                                (-(a_n[n]**2) * c_f * times[ii]) / (a**2)))
            mandel_sol['u_x'][ii] = (
                ux0_1 + ux0_2 * ux_sum1) * x_cntr + ux0_3 * ux_sum2

            # Analytical vertical displacements
            uy_sum = 0
            for n in range(len(a_n)):
                uy_sum += (((np.sin(a_n[n]) * np.cos(a_n[n])) /
                            (a_n[n] - np.sin(a_n[n]) * np.cos(a_n[n]))) *
                           np.exp((-(a_n[n]**2) * c_f * times[ii]) / (a**2)))
            mandel_sol['u_y'][ii] = (uy0_1 + uy0_2 * uy_sum) * y_cntr

            # Analitical vertical stress
            sigma_sum1 = 0
            sigma_sum2 = 0
            for n in range(len(a_n)):
                sigma_sum1 += (((np.sin(a_n[n])) /
                                (a_n[n] - (np.sin(a_n[n]) * np.cos(a_n[n])))) *
                               np.cos(a_n[n] * (x_cntr / a)) * np.exp(
                                   (-(a_n[n]**2) * c_f * times[ii]) / (a**2)))
                sigma_sum2 += (((np.sin(a_n[n]) * np.cos(a_n[n])) /
                                (a_n[n] - np.sin(a_n[n]) * np.cos(a_n[n]))) *
                               np.exp(
                                   (-(a_n[n]**2) * c_f * times[ii]) / (a**2)))
            mandel_sol['sigma_yy'][ii] = (sigma0_1 + sigma0_2 * sigma_sum1) + (
                sigma0_3 * sigma_sum2)

        return mandel_sol

    # Computing the initial condition

    def get_mandel_init_cond(g, F, B, nu_u, mu_s):

        # Initialing pressure and displacement arrays
        p0 = np.zeros(g.num_cells)
        u0 = np.zeros(g.num_cells * 2)

        # Some needed parameters
        a = np.max(g.face_centers[0])  # a = Lx

        p0 = ((F * B * (1 + nu_u)) / (3 * a)) * np.ones(g.num_cells)
        u0[::2] = (F * nu_u * g.cell_centers[0]) / (2 * mu_s * a)
        u0[1::2] = ((-F * (1 - nu_u)) / (2 * mu_s * a)) * g.cell_centers[1]

        return p0, u0

    # Getting the time-dependent boundary condition

    def get_mandel_bc(g, y_max, times, F, B, nu_u, nu, c_f, mu_s):

        # Initializing top boundary array
        u_top = np.zeros((len(times), len(y_max)))

        # Some needed parameters
        a = np.max(g.face_centers[0])  # a = Lx
        b = np.max(g.face_centers[1])  # b = Ly
        y_top = g.face_centers[1][
            y_max]  # [m] y-coordinates at the top boundary

        # Solutions to tan(x) - ((1-nu)/(nu_u-nu)) x = 0
        """
        This is somehow tricky, we have to solve the equation numerically in order to
        find all the positive solutions to the equation. Later we will use them to 
        compute the infinite sums. Experience has shown that 200 roots are more than enough to
        achieve accurate results. Note that we find the roots using the bisection method.
        """
        f = lambda x: np.tan(x) - (
            (1 - nu) /
            (nu_u - nu)) * x  # define the algebraic eq. as a lambda function
        n_series = 200  # number of roots
        a_n = np.zeros(n_series)  # initializing roots array
        x0 = 0  # initial point
        for i in range(0, len(a_n)):
            a_n[i] = opt.bisect(
                f,  # function
                x0 + np.pi / 4,  # left point 
                x0 + np.pi / 2 -
                10000 * 2.2204e-16,  # right point (a tiny bit less than pi/2)
                xtol=1e-30,  # absolute tolerance
                rtol=1e-15  # relative tolerance
            )
            x0 += np.pi  # apply a phase change of pi to get the next root

        # Terms needed to compute the solutions (these are constants)
        uy0_1 = (-F * (1 - nu)) / (2 * mu_s * a)
        uy0_2 = (F * (1 - nu_u) / (mu_s * a))

        # For the initial condition:
        u_top[0] = ((-F * (1 - nu_u)) / (2 * mu_s * a)) * b

        for i in range(1, len(times)):
            # Analytical vertical displacements at the top boundary
            uy_sum = 0
            for n in range(len(a_n)):
                uy_sum += (((np.sin(a_n[n]) * np.cos(a_n[n])) /
                            (a_n[n] - np.sin(a_n[n]) * np.cos(a_n[n]))) *
                           np.exp((-(a_n[n]**2) * c_f * times[i]) / (a**2)))

            u_top[i] = (uy0_1 + uy0_2 * uy_sum) * y_top

        # Returning array of u_y at the top boundary
        return u_top

    # Getting mechanics boundary conditions

    def get_bc_mechanics(g, u_top, times, b_faces, x_min, x_max, west, east,
                         y_min, y_max, south, north):

        # Setting the tags at each boundary side for the mechanics problem
        labels_mech = np.array([None] * b_faces.size)
        labels_mech[west] = 'dir_x'  # roller
        labels_mech[east] = 'neu'  # traction free
        labels_mech[south] = 'dir_y'  # roller
        labels_mech[
            north] = 'dir_y'  # roller (with non-zero displacement in the vertical direction)

        # Constructing the bc object for the mechanics problem
        bc_mech = pp.BoundaryConditionVectorial(g, b_faces, labels_mech)

        # Constructing the boundary values array for the mechanics problem
        bc_val_mech = np.zeros((
            len(times),
            g.num_faces * g.dim,
        ))

        for i in range(len(times)):

            # West side boundary conditions (mech)
            bc_val_mech[i][2 * x_min] = 0  # [m]
            bc_val_mech[i][2 * x_min + 1] = 0  # [Pa]

            # East side boundary conditions (mech)
            bc_val_mech[i][2 * x_max] = 0  # [Pa]
            bc_val_mech[i][2 * x_max + 1] = 0  # [Pa]

            # South Side boundary conditions (mech)
            bc_val_mech[i][2 * y_min] = 0  # [Pa]
            bc_val_mech[i][2 * y_min + 1] = 0  # [m]

            # North Side boundary conditions (mech)
            bc_val_mech[i][2 * y_max] = 0  # [Pa]
            bc_val_mech[i][2 * y_max + 1] = u_top[i]  # [m]

        return bc_mech, bc_val_mech

    # Getting flow boundary conditions

    def get_bc_flow(g, b_faces, x_min, x_max, west, east, y_min, y_max, south,
                    north):

        # Setting the tags at each boundary side for the mechanics problem
        labels_flow = np.array([None] * b_faces.size)
        labels_flow[west] = 'neu'  # no flow
        labels_flow[east] = 'dir'  # constant pressure
        labels_flow[south] = 'neu'  # no flow
        labels_flow[north] = 'neu'  # no flow

        # Constructing the bc object for the flow problem
        bc_flow = pp.BoundaryCondition(g, b_faces, labels_flow)

        # Constructing the boundary values array for the flow problem
        bc_val_flow = np.zeros(g.num_faces)

        # West side boundary condition (flow)
        bc_val_flow[x_min] = 0  # [Pa]

        # East side boundary condition (flow)
        bc_val_flow[x_max] = 0  # [m^3/s]

        # South side boundary condition (flow)
        bc_val_flow[y_min] = 0  # [m^3/s]

        # North side boundary condition (flow)
        bc_val_flow[y_max] = 0  # [m^3/s]

        return bc_flow, bc_val_flow

    # ## Setting up the grid

    # In[7]:

    Nx = 40
    Ny = 40
    Lx = 100
    Ly = 10
    g = pp.CartGrid([Nx, Ny], [Lx, Ly])
    #g.nodes = g.nodes + 1E-7*np.random.randn(3, g.num_nodes)
    g.compute_geometry()
    V = g.cell_volumes

    # Physical parameters

    # Skeleton parameters
    mu_s = 2.475E+09  # [Pa] Shear modulus
    lambda_s = 1.65E+09  # [Pa] Lame parameter
    K_s = (2 / 3) * mu_s + lambda_s  # [Pa] Bulk modulus
    E_s = mu_s * ((9 * K_s) / (3 * K_s + mu_s))  # [Pa] Young's modulus
    nu_s = (3 * K_s - 2 * mu_s) / (2 * (3 * K_s + mu_s)
                                   )  # [-] Poisson's coefficient
    k_s = 100 * 9.869233E-13  # [m^2] Permeabiliy

    # Fluid parameters
    mu_f = 10.0E-3  # [Pa s] Dynamic viscosity

    # Porous medium parameters
    alpha_biot = 1.  # [m^2] Intrinsic permeability
    S_m = 6.0606E-11  # [1/Pa] Specific Storage
    K_u = K_s + (alpha_biot**2) / S_m  # [Pa] Undrained bulk modulus
    B = alpha_biot / (S_m * K_u)  # [-] Skempton's coefficient
    nu_u = (3 * nu_s + B *
            (1 - 2 * nu_s)) / (3 - B *
                               (1 - 2 * nu_s))  # [-] Undrained Poisson's ratio
    c_f = (2 * k_s * (B**2) * mu_s * (1 - nu_s) *
           (1 + nu_u)**2) / (9 * mu_f * (1 - nu_u) *
                             (nu_u - nu_s))  # [m^2/s] Fluid diffusivity

    # Creating second and fourth order tensors

    # Permeability tensor
    perm = pp.SecondOrderTensor(g.dim, k_s * np.ones(g.num_cells))
    # Stiffness matrix
    constit = pp.FourthOrderTensor(g.dim, mu_s * np.ones(g.num_cells),
                                   lambda_s * np.ones(g.num_cells))
    # Time parameters

    t0 = 0  # [s] Initial time
    tf = 100  # [s] Final simulation time
    tLevels = 100  # [-] Time levels
    times = np.linspace(t0, tf, tLevels + 1)  # [s] Vector of time evaluations
    dt = np.diff(times)  # [s] Vector of time steps

    # Boundary conditions pre-processing

    b_faces = g.tags['domain_boundary_faces'].nonzero()[0]

    # Extracting indices of boundary faces w.r.t g
    x_min = b_faces[g.face_centers[0, b_faces] < 0.0001]
    x_max = b_faces[g.face_centers[0, b_faces] > 0.9999 * Lx]
    y_min = b_faces[g.face_centers[1, b_faces] < 0.0001]
    y_max = b_faces[g.face_centers[1, b_faces] > 0.9999 * Ly]

    # Extracting indices of boundary faces w.r.t b_faces
    west = np.in1d(b_faces, x_min).nonzero()
    east = np.in1d(b_faces, x_max).nonzero()
    south = np.in1d(b_faces, y_min).nonzero()
    north = np.in1d(b_faces, y_max).nonzero()

    # Applied load and top boundary condition
    F_load = 6.8E+8  # [N/m] Applied load
    u_top = get_mandel_bc(g, y_max, times, F_load, B, nu_u, nu_s, c_f,
                          mu_s)  # [m] Vector of imposed vertical displacements

    # MECHANICS BOUNDARY CONDITIONS
    bc_mech, bc_val_mech = get_bc_mechanics(g, u_top, times, b_faces, x_min,
                                            x_max, west, east, y_min, y_max,
                                            south, north)
    # FLOW BOUNDARY CONDITIONS
    bc_flow, bc_val_flow = get_bc_flow(g, b_faces, x_min, x_max, west, east,
                                       y_min, y_max, south, north)

    # Initialiazing solution and solver dicitionaries

    # Solution dictionary
    sol = dict()
    sol['time'] = np.zeros(tLevels + 1, dtype=float)
    sol['displacement'] = np.zeros((tLevels + 1, g.num_cells * g.dim),
                                   dtype=float)
    sol['displacement_faces'] = np.zeros(
        (tLevels + 1, g.num_faces * g.dim * 2), dtype=float)
    sol['pressure'] = np.zeros((tLevels + 1, g.num_cells), dtype=float)
    sol['traction'] = np.zeros((tLevels + 1, g.num_faces * g.dim), dtype=float)
    sol['flux'] = np.zeros((tLevels + 1, g.num_faces), dtype=float)
    sol['iter'] = np.array([], dtype=int)
    sol['time_step'] = np.array([], dtype=float)
    sol['residual'] = np.array([], dtype=float)

    # Solver dictionary
    newton_param = dict()
    newton_param['tol'] = 1E-6  # maximum tolerance
    newton_param['max_iter'] = 20  # maximum number of iterations
    newton_param['res_norm'] = 1000  # initializing residual
    newton_param['iter'] = 1  # iteration

    # Discrete operators and discrete equations

    # Flow operators

    F = lambda x: biot_F * x  # Flux operator
    boundF = lambda x: biot_boundF * x  # Bound Flux operator
    compat = lambda x: biot_compat * x  # Compatibility operator (Stabilization term)
    divF = lambda x: biot_divF * x  # Scalar divergence operator

    # Mechanics operators

    S = lambda x: biot_S * x  # Stress operator
    boundS = lambda x: biot_boundS * x  # Bound Stress operator
    divU = lambda x: biot_divU * x  # Divergence of displacement field
    divS = lambda x: biot_divS * x  # Vector divergence operator
    gradP = lambda x: biot_divS * biot_gradP * x  # Pressure gradient operator
    boundDivU = lambda x: biot_boundDivU * x  # Bound Divergence of displacement operator
    boundUCell = lambda x: biot_boundUCell * x  # Contribution of displacement at cells -> Face displacement
    boundUFace = lambda x: biot_boundUFace * x  # Contribution of bc_mech at the boundaries -> Face displacement
    boundUPressure = lambda x: biot_boundUPressure * x  # Contribution of pressure at cells -> Face displacement

    # Discrete equations

    # Generalized Hooke's law
    T = lambda u, bc_val_mech: S(u) + boundS(bc_val_mech)

    # Momentum conservation equation (I)
    u_eq1 = lambda u, bc_val_mech: divS(T(u, bc_val_mech))

    # Momentum conservation equation (II)
    u_eq2 = lambda p: -gradP(p)

    # Darcy's law
    Q = lambda p: (1. / mu_f) * (F(p) + boundF(bc_val_flow))

    # Mass conservation equation (I)
    p_eq1 = lambda u, u_n, bc_val_mech, bc_val_mech_n: alpha_biot * (divU(
        u - u_n) + boundDivU(bc_val_mech - bc_val_mech_n))

    # Mass conservation equation (II)
    p_eq2 = lambda p, p_n, dt: (p - p_n) * S_m * V + divF(Q(
        p)) * dt + alpha_biot * compat(p - p_n)

    # Creating AD variables

    # Retrieve initial conditions
    p_init, u_init = get_mandel_init_cond(g, F_load, B, nu_u, mu_s)

    # Create displacement AD-variable
    u_ad = Ad_array(u_init.copy(), sps.diags(np.ones(g.num_cells * g.dim)))

    # Create pressure AD-variable
    p_ad = Ad_array(p_init.copy(), sps.diags(np.ones(g.num_cells)))

    # The time loop

    tt = 0  # time counter

    while times[tt] < times[-1]:

        ################################
        # Initializing data dictionary #
        ################################

        d = dict()  # initialize dictionary to store data

        ################################
        #  Creating the data objects   #
        ################################

        # Mechanics data object
        specified_parameters_mech = {
            "fourth_order_tensor": constit,
            "bc": bc_mech,
            "biot_alpha": 1.,
            "bc_values": bc_val_mech[tt],
            "mass_weight": S_m
        }

        pp.initialize_default_data(g, d, "mechanics",
                                   specified_parameters_mech)

        # Flow data object
        specified_parameters_flow = {
            "second_order_tensor": perm,
            "bc": bc_flow,
            "biot_alpha": 1.,
            "bc_values": bc_val_flow,
            "mass_weight": S_m,
            "time_step": dt[tt - 1]
        }

        pp.initialize_default_data(g, d, "flow", specified_parameters_flow)

        ################################
        #  CALLING MPFA/MPSA ROUTINES  #
        ################################

        # Biot discretization
        solver_biot = pp.Biot("mechanics", "flow")
        solver_biot.discretize(g, d)

        # Mechanics discretization matrices
        biot_S = d['discretization_matrices']['mechanics']['stress']
        biot_boundS = d['discretization_matrices']['mechanics']['bound_stress']
        biot_divU = d['discretization_matrices']['mechanics']['div_d']
        biot_gradP = d['discretization_matrices']['mechanics']['grad_p']
        biot_boundDivU = d['discretization_matrices']['mechanics'][
            'bound_div_d']
        biot_boundUCell = d['discretization_matrices']['mechanics'][
            'bound_displacement_cell']
        biot_boundUFace = d['discretization_matrices']['mechanics'][
            'bound_displacement_face']
        biot_boundUPressure = d['discretization_matrices']['mechanics'][
            'bound_displacement_pressure']
        biot_divS = pp.fvutils.vector_divergence(g)

        # Flow discretization matrices
        biot_F = d['discretization_matrices']['flow']['flux']
        biot_boundF = d['discretization_matrices']['flow']['bound_flux']
        biot_compat = d['discretization_matrices']['flow'][
            'biot_stabilization']
        biot_divF = pp.fvutils.scalar_divergence(g)

        ################################
        #  Saving Initial Condition    #
        ################################

        if times[tt] == 0:
            sol['pressure'][tt] = p_ad.val
            sol['displacement'][tt] = u_ad.val
            sol['displacement_faces'][tt] = (
                boundUCell(sol['displacement'][tt]) +
                boundUFace(bc_val_mech[tt]) +
                boundUPressure(sol['pressure'][tt]))
            sol['time'][tt] = times[tt]
            sol['traction'][tt] = T(u_ad.val, bc_val_mech[tt])
            sol['flux'][tt] = Q(p_ad.val)

        tt += 1  # increasing time counter

        ################################
        #  Solving the set of PDE's    #
        ################################

        # Displacement and pressure at the previous time step
        u_n = u_ad.val.copy()
        p_n = p_ad.val.copy()

        # Updating residual and iteration at each time step
        newton_param.update({'res_norm': 1000, 'iter': 1})

        # Newton loop
        while newton_param['res_norm'] > newton_param['tol'] and newton_param[
                'iter'] <= newton_param['max_iter']:

            # Calling equations
            eq1 = u_eq1(u_ad, bc_val_mech[tt])
            eq2 = u_eq2(p_ad)
            eq3 = p_eq1(u_ad, u_n, bc_val_mech[tt], bc_val_mech[tt - 1])
            eq4 = p_eq2(p_ad, p_n, dt[tt - 1])

            # Assembling Jacobian of the coupled system
            J_mech = np.hstack(
                (eq1.jac, eq2.jac))  # Jacobian blocks (mechanics)
            J_flow = np.hstack((eq3.jac, eq4.jac))  # Jacobian blocks (flow)
            J = sps.bmat(np.vstack((J_mech, J_flow)),
                         format='csc')  # Jacobian (coupled)

            # Determining residual of the coupled system
            R_mech = eq1.val + eq2.val  # Residual (mechanics)
            R_flow = eq3.val + eq4.val  # Residual (flow)
            R = np.hstack((R_mech, R_flow))  # Residual (coupled)

            y = sps.linalg.spsolve(J, -R)  #
            u_ad.val = u_ad.val + y[:g.dim * g.num_cells]  # Newton update
            p_ad.val = p_ad.val + y[g.dim * g.num_cells:]  #

            newton_param['res_norm'] = np.linalg.norm(R)  # Updating residual

            if newton_param['res_norm'] <= newton_param[
                    'tol'] and newton_param['iter'] <= newton_param['max_iter']:
                print('Iter: {} \t Error: {:.8f} [m]'.format(
                    newton_param['iter'], newton_param['res_norm']))
            elif newton_param['iter'] > newton_param['max_iter']:
                print('Error: Newton method did not converge!')
            else:
                newton_param['iter'] += 1

        ################################
        #      Saving the variables    #
        ################################
        sol['iter'] = np.concatenate(
            (sol['iter'], np.array([newton_param['iter']])))
        sol['residual'] = np.concatenate(
            (sol['residual'], np.array([newton_param['res_norm']])))
        sol['time_step'] = np.concatenate((sol['time_step'], dt))
        sol['pressure'][tt] = p_ad.val
        sol['displacement'][tt] = u_ad.val
        sol['displacement_faces'][tt] = (boundUCell(sol['displacement'][tt]) +
                                         boundUFace(bc_val_mech[tt]) +
                                         boundUPressure(sol['pressure'][tt]))
        sol['time'][tt] = times[tt]
        sol['traction'][tt] = T(u_ad.val, bc_val_mech[tt])
        sol['flux'][tt] = Q(p_ad.val)

    # Calling analytical solution

    sol_mandel = mandel_solution(g, Nx, Ny, times, F_load, B, nu_u, nu_s, c_f,
                                 mu_s)

    # Creating analytical and numerical results arrays

    p_num = (Lx * sol['pressure'][-1][:Nx]) / F
    p_ana = (Lx * sol_mandel['p'][-1]) / F

    # Returning values

    return p_num, p_ana
    def set_mechanics_parameters(self):
        """ Mechanical parameters.
        Note that we divide the momentum balance equation by self.scalar_scale. 
        A homogeneous initial temperature is assumed.
        """
        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)

                # In the momentum balance, the coefficient hits the scalar, and should
                # not be scaled. Same goes for the energy balance, where we divide all
                # terms by T_0, hence the term originally beta K T d(div u) / dt becomes
                # beta K d(div u) / dt = coupling_coefficient d(div u) / dt.
                coupling_coefficient = self.biot_alpha(g)

                pp.initialize_data(
                    g,
                    d,
                    self.mechanics_parameter_key,
                    {
                        "bc": bc,
                        "bc_values": bc_values,
                        "source": sources,
                        "fourth_order_tensor": C,
                        "biot_alpha": coupling_coefficient,
                        "time_step": self.time_step,
                    },
                )
                pp.initialize_data(
                    g,
                    d,
                    self.mechanics_temperature_parameter_key,
                    {
                        "biot_alpha": self.biot_beta(g),
                        "bc_values": bc_values
                    },
                )
            elif g.dim == self.Nd - 1:

                pp.initialize_data(
                    g,
                    d,
                    self.mechanics_parameter_key,
                    {
                        "friction_coefficient": self._friction_coefficient(g),
                        "contact_mechanics_numerical_parameter": 10000,
                    },
                )

        for e, d in gb.edges():
            mg = d["mortar_grid"]
            # Parameters for the surface diffusion. Not used as of now.
            pp.initialize_data(
                mg,
                d,
                self.mechanics_parameter_key,
                {
                    "mu": self.rock.MU,
                    "lambda": self.rock.LAMBDA
                },
            )
def biot_convergence_in_space(N):
    # coding: utf-8

    # ### Source terms and analytical solutions

    # In[330]:

    def source_flow(g, tau):

        x1 = g.cell_centers[0]
        x2 = g.cell_centers[1]

        f_flow = tau*(2*np.sin(2*np.pi*x2) - \
                 4*x1*np.pi**2*np.sin(2*np.pi*x2)*(x1 - 1)) - \
                 x1*np.sin(2*np.pi*x2) - \
                 np.sin(2*np.pi*x2)*(x1 - 1) + \
                 2*np.pi*np.cos(2*np.pi*x2)*np.sin(2*np.pi*x1)

        return f_flow

    def source_mechanics(g):

        x1 = g.cell_centers[0]
        x2 = g.cell_centers[1]

        f_mech = np.zeros(g.num_cells * g.dim)

        f_mech[::2] = 6*np.sin(2*np.pi*x2) - \
                      x1*np.sin(2*np.pi*x2) -  \
                      np.sin(2*np.pi*x2)*(x1 - 1) - \
                      8*np.pi**2*np.cos(2*np.pi*x1)*np.cos(2*np.pi*x2) - \
                      4*x1*np.pi**2*np.sin(2*np.pi*x2)*(x1 - 1)

        f_mech[1::2] = 4*np.pi*np.cos(2*np.pi*x2)*(x1 - 1) + \
                       16*np.pi**2*np.sin(2*np.pi*x1)*np.sin(2*np.pi*x2) + \
                       4*x1*np.pi*np.cos(2*np.pi*x2) - \
                       2*x1*np.pi*np.cos(2*np.pi*x2)*(x1 - 1)

        return f_mech

    def analytical(g):

        sol = dict()
        x1 = g.cell_centers[0]
        x2 = g.cell_centers[1]

        sol['u'] = np.zeros(g.num_cells * g.dim)
        sol['u'][::2] = x1 * (1 - x1) * np.sin(2 * np.pi * x2)
        sol['u'][1::2] = np.sin(2 * np.pi * x1) * np.sin(2 * np.pi * x2)

        sol['p'] = sol['u'][::2]

        return sol

    # ### Getting mechanics boundary conditions

    # In[331]:

    def get_bc_mechanics(g, b_faces, x_min, x_max, west, east, y_min, y_max,
                         south, north):

        # Setting the tags at each boundary side for the mechanics problem
        labels_mech = np.array([None] * b_faces.size)
        labels_mech[west] = 'dir'
        labels_mech[east] = 'dir'
        labels_mech[south] = 'dir'
        labels_mech[north] = 'dir'

        # Constructing the bc object for the mechanics problem
        bc_mech = pp.BoundaryConditionVectorial(g, b_faces, labels_mech)

        # Constructing the boundary values array for the mechanics problem
        bc_val_mech = np.zeros(g.num_faces * g.dim)

        return bc_mech, bc_val_mech

    # ### Getting flow boundary conditions

    # In[332]:

    def get_bc_flow(g, b_faces, x_min, x_max, west, east, y_min, y_max, south,
                    north):

        # Setting the tags at each boundary side for the mechanics problem
        labels_flow = np.array([None] * b_faces.size)
        labels_flow[west] = 'dir'
        labels_flow[east] = 'dir'
        labels_flow[south] = 'dir'
        labels_flow[north] = 'dir'

        # Constructing the bc object for the flow problem
        bc_flow = pp.BoundaryCondition(g, b_faces, labels_flow)

        # Constructing the boundary values array for the flow problem
        bc_val_flow = np.zeros(g.num_faces)

        return bc_flow, bc_val_flow

    # ### Setting up the grid

    # In[333]:

    Nx = Ny = N
    Lx = 1
    Ly = 1
    g = pp.CartGrid([Nx, Ny], [Lx, Ly])
    g.compute_geometry()
    V = g.cell_volumes

    # ### Physical parameters

    # In[334]:

    # Skeleton parameters
    mu_s = 1  # [Pa] Shear modulus
    lambda_s = 1  # [Pa] Lame parameter
    K_s = (2 / 3) * mu_s + lambda_s  # [Pa] Bulk modulus
    E_s = mu_s * ((9 * K_s) / (3 * K_s + mu_s))  # [Pa] Young's modulus
    nu_s = (3 * K_s - 2 * mu_s) / (2 * (3 * K_s + mu_s)
                                   )  # [-] Poisson's coefficient
    k_s = 1  # [m^2] Permeabiliy

    # Fluid parameters
    mu_f = 1  # [Pa s] Dynamic viscosity

    # Porous medium parameters
    alpha_biot = 1.  # [m^2] Intrinsic permeability
    S_m = 0  # [1/Pa] Specific Storage

    # ### Creating second and fourth order tensors

    # In[335]:

    # Permeability tensor
    perm = pp.SecondOrderTensor(g.dim, k_s * np.ones(g.num_cells))

    # Stiffness matrix
    constit = pp.FourthOrderTensor(g.dim, mu_s * np.ones(g.num_cells),
                                   lambda_s * np.ones(g.num_cells))

    # ### Time parameters

    # In[336]:

    t0 = 0  # [s] Initial time
    tf = 1  # [s] Final simulation time
    tLevels = 1  # [-] Time levels
    times = np.linspace(t0, tf, tLevels + 1)  # [s] Vector of time evaluations
    dt = np.diff(times)  # [s] Vector of time steps

    # ### Boundary conditions pre-processing

    # In[337]:

    b_faces = g.tags['domain_boundary_faces'].nonzero()[0]

    # Extracting indices of boundary faces w.r.t g
    x_min = b_faces[g.face_centers[0, b_faces] < 0.0001]
    x_max = b_faces[g.face_centers[0, b_faces] > 0.9999 * Lx]
    y_min = b_faces[g.face_centers[1, b_faces] < 0.0001]
    y_max = b_faces[g.face_centers[1, b_faces] > 0.9999 * Ly]

    # Extracting indices of boundary faces w.r.t b_faces
    west = np.in1d(b_faces, x_min).nonzero()
    east = np.in1d(b_faces, x_max).nonzero()
    south = np.in1d(b_faces, y_min).nonzero()
    north = np.in1d(b_faces, y_max).nonzero()

    # Mechanics boundary conditions
    bc_mech, bc_val_mech = get_bc_mechanics(g, b_faces, x_min, x_max, west,
                                            east, y_min, y_max, south, north)
    # FLOW BOUNDARY CONDITIONS
    bc_flow, bc_val_flow = get_bc_flow(g, b_faces, x_min, x_max, west, east,
                                       y_min, y_max, south, north)

    # ### Initialiazing solution and solver dicitionaries

    # In[338]:

    # Solution dictionary
    sol = dict()
    sol['time'] = np.zeros(tLevels + 1, dtype=float)
    sol['displacement'] = np.zeros((tLevels + 1, g.num_cells * g.dim),
                                   dtype=float)
    sol['displacement_faces'] = np.zeros(
        (tLevels + 1, g.num_faces * g.dim * 2), dtype=float)
    sol['pressure'] = np.zeros((tLevels + 1, g.num_cells), dtype=float)
    sol['traction'] = np.zeros((tLevels + 1, g.num_faces * g.dim), dtype=float)
    sol['flux'] = np.zeros((tLevels + 1, g.num_faces), dtype=float)
    sol['iter'] = np.array([], dtype=int)
    sol['time_step'] = np.array([], dtype=float)
    sol['residual'] = np.array([], dtype=float)

    # Solver dictionary
    newton_param = dict()
    newton_param['tol'] = 1E-8  # maximum tolerance
    newton_param['max_iter'] = 20  # maximum number of iterations
    newton_param['res_norm'] = 1000  # initializing residual
    newton_param['iter'] = 1  # iteration

    # ### Discrete operators and discrete equations

    # ### Flow operators

    # In[339]:

    F = lambda x: biot_F * x  # Flux operator
    boundF = lambda x: biot_boundF * x  # Bound Flux operator
    compat = lambda x: biot_compat * x  # Compatibility operator (Stabilization term)
    divF = lambda x: biot_divF * x  # Scalar divergence operator

    # ### Mechanics operators

    # In[340]:

    S = lambda x: biot_S * x  # Stress operator
    boundS = lambda x: biot_boundS * x  # Bound Stress operator
    divU = lambda x: biot_divU * x  # Divergence of displacement field
    divS = lambda x: biot_divS * x  # Vector divergence operator
    gradP = lambda x: biot_divS * biot_gradP * x  # Pressure gradient operator
    boundDivU = lambda x: biot_boundDivU * x  # Bound Divergence of displacement operator
    boundUCell = lambda x: biot_boundUCell * x  # Contribution of displacement at cells -> Face displacement
    boundUFace = lambda x: biot_boundUFace * x  # Contribution of bc_mech at the boundaries -> Face displacement
    boundUPressure = lambda x: biot_boundUPressure * x  # Contribution of pressure at cells -> Face displacement

    # ### Discrete equations

    # In[341]:

    # Source terms
    f_mech = source_mechanics(g)
    f_flow = source_flow(g, dt[0])

    # Generalized Hooke's law
    T = lambda u: S(u) + boundS(bc_val_mech)

    # Momentum conservation equation (I)
    u_eq1 = lambda u: divS(T(u))

    # Momentum conservation equation (II)
    u_eq2 = lambda p: -gradP(p) + f_mech * V[0]

    # Darcy's law
    Q = lambda p: (1. / mu_f) * (F(p) + boundF(bc_val_flow))

    # Mass conservation equation (I)
    p_eq1 = lambda u, u_n: alpha_biot * divU(u - u_n)

    # Mass conservation equation (II)
    p_eq2 = lambda p, p_n, dt: (p - p_n) * S_m * V + divF(Q(
        p)) * dt + alpha_biot * compat(p - p_n) * V[0] - (f_flow / dt) * V[0]

    # ## Creating AD variables

    # In[343]:

    # Create displacement AD-variable
    u_ad = Ad_array(np.zeros(g.num_cells * 2),
                    sps.diags(np.ones(g.num_cells * g.dim)))

    # Create pressure AD-variable
    p_ad = Ad_array(np.zeros(g.num_cells), sps.diags(np.ones(g.num_cells)))

    # ## Performing discretization

    # In[344]:

    d = dict()  # initialize dictionary to store data

    # Mechanics data object
    specified_parameters_mech = {
        "fourth_order_tensor": constit,
        "bc": bc_mech,
        "biot_alpha": 1.,
        "bc_values": bc_val_mech
    }
    pp.initialize_default_data(g, d, "mechanics", specified_parameters_mech)

    # Flow data object
    specified_parameters_flow = {
        "second_order_tensor": perm,
        "bc": bc_flow,
        "biot_alpha": 1.,
        "bc_values": bc_val_flow
    }
    pp.initialize_default_data(g, d, "flow", specified_parameters_flow)

    # Biot discretization
    solver_biot = pp.Biot("mechanics", "flow")
    solver_biot.discretize(g, d)

    # Mechanics discretization matrices
    biot_S = d['discretization_matrices']['mechanics']['stress']
    biot_boundS = d['discretization_matrices']['mechanics']['bound_stress']
    biot_divU = d['discretization_matrices']['mechanics']['div_d']
    biot_gradP = d['discretization_matrices']['mechanics']['grad_p']
    biot_boundDivU = d['discretization_matrices']['mechanics']['bound_div_d']
    biot_boundUCell = d['discretization_matrices']['mechanics'][
        'bound_displacement_cell']
    biot_boundUFace = d['discretization_matrices']['mechanics'][
        'bound_displacement_face']
    biot_boundUPressure = d['discretization_matrices']['mechanics'][
        'bound_displacement_pressure']
    biot_divS = pp.fvutils.vector_divergence(g)

    # Flow discretization matrices
    biot_F = d['discretization_matrices']['flow']['flux']
    biot_boundF = d['discretization_matrices']['flow']['bound_flux']
    biot_compat = d['discretization_matrices']['flow']['biot_stabilization']
    biot_divF = pp.fvutils.scalar_divergence(g)

    # Saving initial condition
    sol['pressure'][0] = p_ad.val
    sol['displacement'][0] = u_ad.val
    sol['displacement_faces'][0] = (boundUCell(sol['displacement'][0]) +
                                    boundUFace(bc_val_mech) +
                                    boundUPressure(sol['pressure'][0]))
    sol['time'][0] = times[0]
    sol['traction'][0] = T(u_ad.val)
    sol['flux'][0] = Q(p_ad.val)

    # ## The time loop

    # In[345]:

    tt = 0  # time counter

    while times[tt] < times[-1]:

        tt += 1  # increasing time counter

        # Displacement and pressure at the previous time step
        u_n = u_ad.val.copy()
        p_n = p_ad.val.copy()

        # Updating residual and iteration at each time step
        newton_param.update({'res_norm': 1000, 'iter': 1})

        # Newton loop
        while newton_param['res_norm'] > newton_param['tol'] and newton_param[
                'iter'] <= newton_param['max_iter']:

            # Calling equations
            eq1 = u_eq1(u_ad)
            eq2 = u_eq2(p_ad)
            eq3 = p_eq1(u_ad, u_n)
            eq4 = p_eq2(p_ad, p_n, dt[tt - 1])

            # Assembling Jacobian of the coupled system
            J_mech = np.hstack(
                (eq1.jac, eq2.jac))  # Jacobian blocks (mechanics)
            J_flow = np.hstack((eq3.jac, eq4.jac))  # Jacobian blocks (flow)
            J = sps.bmat(np.vstack((J_mech, J_flow)),
                         format='csc')  # Jacobian (coupled)

            # Determining residual of the coupled system
            R_mech = eq1.val + eq2.val  # Residual (mechanics)
            R_flow = eq3.val + eq4.val  # Residual (flow)
            R = np.hstack((R_mech, R_flow))  # Residual (coupled)

            y = sps.linalg.spsolve(J, -R)  #
            u_ad.val = u_ad.val + y[:g.dim * g.num_cells]  # Newton update
            p_ad.val = p_ad.val + y[g.dim * g.num_cells:]  #

            newton_param['res_norm'] = np.linalg.norm(R)  # Updating residual

            if newton_param['res_norm'] <= newton_param[
                    'tol'] and newton_param['iter'] <= newton_param['max_iter']:
                print('Iter: {} \t Error: {:.8f} [m]'.format(
                    newton_param['iter'], newton_param['res_norm']))
            elif newton_param['iter'] > newton_param['max_iter']:
                print('Error: Newton method did not converge!')
            else:
                newton_param['iter'] += 1

        # Saving variables
        sol['iter'] = np.concatenate(
            (sol['iter'], np.array([newton_param['iter']])))
        sol['residual'] = np.concatenate(
            (sol['residual'], np.array([newton_param['res_norm']])))
        sol['time_step'] = np.concatenate((sol['time_step'], dt))
        sol['pressure'][tt] = p_ad.val
        sol['displacement'][tt] = u_ad.val
        sol['displacement_faces'][tt] = (boundUCell(sol['displacement'][tt]) +
                                         boundUFace(bc_val_mech) +
                                         boundUPressure(sol['pressure'][tt]))
        sol['time'][tt] = times[tt]
        sol['traction'][tt] = T(u_ad.val)
        sol['flux'][tt] = Q(p_ad.val)

        # Determining analytical solution
        sol_anal = analytical(g)

        # Determining norms
        p_norm = np.linalg.norm(sol_anal['p'] - sol['pressure'][-1]) / (
            np.linalg.norm(sol['pressure'][-1]))
        u_mag_num = np.sqrt(sol['displacement'][-1][::2]**2 +
                            sol['displacement'][-1][1::2]**2)
        u_mag_ana = np.sqrt(sol_anal['u'][::2]**2 + sol_anal['u'][1::2]**2)
        u_norm = np.linalg.norm(u_mag_ana -
                                u_mag_num) / np.linalg.norm(u_mag_num)

        return p_norm, u_norm
예제 #7
0
파일: data.py 프로젝트: keileg/simulations
def assign_parameters(
    grid_object,
    data_dictionary,
    parameter_keyword_flow,
    parameter_keyword_mechanics,
    boundary_conditions_dictionary,
):
    """
    Assign data to the model, which will later be used to discretize
    the coupled problem.

    Parameters:
        grid_object (PorePy object):           PorePy grid object
        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
        boundary_conditions_dictionary (Dict): Dictionary containing boundary conditions
    """

    # Renaming input data
    g = grid_object
    d = data_dictionary
    kw_f = parameter_keyword_flow
    kw_m = parameter_keyword_mechanics
    bc_dict = boundary_conditions_dictionary

    # Assing flow data

    # Retrieve data for the flow problem
    k = d[pp.PARAMETERS][kw_f]["permeability"]
    alpha_biot = d[pp.PARAMETERS][kw_f]["alpha_biot"]
    S_m = d[pp.PARAMETERS][kw_f]["specific_storage"]
    dt = d[pp.PARAMETERS][kw_f]["time_step"]

    bc_flow = bc_dict[kw_f]["bc"]
    bc_flow_values = bc_dict[kw_f]["bc_values"]

    # Create second order tensor object
    kxx = k * np.ones(g.num_cells)
    perm = pp.SecondOrderTensor(kxx)

    # Create specified parameters dicitionary
    specified_parameters_flow = {
        "second_order_tensor": perm,
        "biot_alpha": alpha_biot,
        "bc": bc_flow,
        "bc_values": bc_flow_values,
        "time_step": dt,
        "mass_weight": S_m * np.ones(g.num_cells),
    }

    # Initialize the flow data
    d = pp.initialize_default_data(g, d, kw_f, specified_parameters_flow)

    # Assign mechanics data

    # Retrieve data for the mechanics problem
    mu = d[pp.PARAMETERS][kw_m]["lame_mu"]
    lmbda = d[pp.PARAMETERS][kw_m]["lame_lambda"]
    alpha_biot = d[pp.PARAMETERS][kw_m]["alpha_biot"]

    bc_mech = bc_dict[kw_m]["bc"]
    bc_mech_values = bc_dict[kw_m]["bc_values"][1]

    # Create fourth order tensor
    mu_lame = mu * np.ones(g.num_cells)
    lambda_lame = lmbda * np.ones(g.num_cells)
    constit = pp.FourthOrderTensor(mu_lame, lambda_lame)

    # Create specified parameters dicitionary
    specified_parameters_mechanics = {
        "fourth_order_tensor": constit,
        "biot_alpha": alpha_biot,
        "bc": bc_mech,
        "bc_values": bc_mech_values,
    }

    # Initialize the mechanics
    d = pp.initialize_default_data(g, d, kw_m, specified_parameters_mechanics)

    # Save boundary conditions in d[pp.STATE]
    pp.set_state(d, {kw_m: {"bc_values": bc_dict[kw_m]["bc_values"][0]}})
예제 #8
0
    def set_parameters(self, g, data_node, mg, data_edge):
        """
        Set the parameters for the simulation. The stress is given in GPa.
        """
        # Define the finite volume sub grid
        s_t = pp.fvutils.SubcellTopology(g)

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

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

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

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

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

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

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

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

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

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

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

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

        # And define a Robin condition on the mortar grid
        contact = pp.numerics.interface_laws.elliptic_interface_laws.RobinContact(
            key_m, mpsa)
        data_edge[pp.PRIMARY_VARIABLES] = {'lam': {'cells': g.dim}}
        data_edge[pp.COUPLING_DISCRETIZATION] = {
            'robin_disc': {
                g: ('u', 'mpsa'),
                g: ('u', 'mpsa'),
                (g, g): ('lam', contact),
            }
        }
        return key, key_m