Esempio n. 1
0
def speed(ncells):
    mesh = UnitSquareMesh(ncells, ncells)

    V = VectorFunctionSpace(mesh, 'CG', 2)
    Q = FunctionSpace(mesh, 'CG', 1)
    W = [V, Q]

    u, p = list(map(TrialFunction, W))
    v, q = list(map(TestFunction, W))

    a = [[0] * len(W) for _ in range(len(W))]
    a[0][0] = inner(grad(u), grad(v)) * dx
    a[0][1] = inner(p, div(v)) * dx
    a[1][0] = inner(q, div(u)) * dx

    x, y = SpatialCoordinate(mesh)
    L = [
        inner(as_vector((y * x**2, sin(pi * (x + y)))), v) * dx,
        inner(x + y, q) * dx
    ]

    A0 = block_assemble(a)
    b0 = block_assemble(L)

    # First method
    bc = DirichletBC(V, Constant((0, 0)), 'on_boundary')
    bcs = [[bc], []]
    t = Timer('first')
    block_bc(bcs, True).apply(A0).apply(b0)
    dt0 = t.stop()

    dimW = sum(Wi.dim() for Wi in W)

    A, b = list(map(block_assemble, (a, L)))

    t = Timer('second')
    A, b = apply_bc(A, b, bcs)
    dt1 = t.stop()

    print('>>>', (b - b0).norm())

    # First method
    A, c = list(map(block_assemble, (a, L)))
    block_rhs_bc(bcs, A).apply(c)

    print('>>>', (b - c).norm())

    return dimW, dt0, dt1, dt0 / dt1
Esempio n. 2
0
L1 = inner(f, c)*dx

from block import block_assemble, block_bc, block_mat
from block.iterative import MinRes
from block.algebraic.petsc import ML, Cholesky

# lhs
AA = block_assemble([[a00, a01, a02],
                     [a10, a11, a12],
                     [a20, a21,   0]])
# rhs
b = block_assemble([L0, L1, 0])

# Collect boundary conditions
bcs = [[bc0, bc1], [], []]
block_bc(bcs, True).apply(AA).apply(b)

b22 = inner(p, q)*dx
B22 = assemble(b22)
# Possible action of preconditioner
BB = block_mat([[ML(AA[0, 0]), 0, 0],
                [0, Cholesky(AA[1, 1]), 0],
                [0, 0, Cholesky(B22)]])

AAinv = MinRes(AA, precond=BB, tolerance=1E-10, maxiter=500)
U, B, P = AAinv*b

p = Function(Q)
p.vector()[:] = P

# Plot solution
Esempio n. 3
0
def compute_A_P(domain):
    # names of params - all 1
    dt, alpha, alpha_BJS, s0, mu_f, mu_p, lbd_f, lbd_p, K, Cp = [float(1)]*10
    C_BJS = (mu_f * alpha_BJS) / sqrt(K)

    # measures
    dxGamma = Measure("dx", domain=domain.interface)
    dxDarcy = Measure("dx", domain=domain.porous_domain)
    dxStokes = Measure("dx", domain=domain.stokes_domain)

    # dsDarcy = Measure("ds", domain=domain.porous_domain,
    #                   subdomain_data=domain.porous_bdy_markers)
    # dsStokes = Measure("ds", domain=domain.stokes_domain,
    #                    subdomain_data=domain.stokes_bdy_markers)

    # test/trial functions
    W = function_spaces(domain)
    up, pp, dp, uf, pf, lbd = map(TrialFunction, W)
    vp, wp, ep, vf, wf, mu = map(TestFunction, W)

    # and their traces
    Tdp, Tuf = map(
        lambda x: Trace(x, domain.interface), [dp, uf]
    )
    Tep, Tvf = map(
        lambda x: Trace(x, domain.interface), [ep, vf]
    )

    # normals
    n_Gamma_f = OuterNormal(domain.interface,
                            [0.5] * domain.dimension)
    assert n_Gamma_f(Point(0.0, 0.0))[1] == -1

    n_Gamma_p = -n_Gamma_f

    Tup = Trace(up, domain.interface, restriction="-", normal=n_Gamma_f)
    Tvp = Trace(vp, domain.interface, restriction="-", normal=n_Gamma_f)


    # a bunch of forms
    af =  Constant(2 * mu_f) * inner(sym(grad(uf)), sym(grad(vf))) * dxStokes
    mpp = pp * wp * dx

    adp = Constant(mu_p / K) * inner(up, vp) * dxDarcy
    aep = (
        Constant(mu_p) * inner(sym(grad(dp)), sym(grad(ep))) * dxDarcy
        + Constant(lbd_p) * inner(div(dp), div(ep)) * dxDarcy
    )
    bpvp  = - inner(div(vp), pp) * dxDarcy
    bpvpt = - inner(div(up), wp) * dxDarcy
    bpep  = - inner(div(ep), pp) * dxDarcy
    bpept = - inner(div(dp), wp) * dxDarcy
    bf    = - inner(div(vf), pf) * dxStokes
    bft   = - inner(div(uf), wf) * dx

    # matrices living on the interface
    npvp, npep, nfvf = [
        lbd * dot(testfunc, n) * dxGamma
        for (testfunc, n) in [(Tvp, n_Gamma_p), (Tep, n_Gamma_p), (Tvf, n_Gamma_f)]
    ]
    npvpt, npept, nfvft = [
        mu * dot(trialfunc, n) * dxGamma
        for (trialfunc, n) in [(Tup, n_Gamma_p), (Tdp, n_Gamma_p), (Tuf, n_Gamma_f)]
    ]


    # to build sum_j ((a*tau_j), (b*tau_j)) we use a trick - see Thoughts
    svfuf, svfdp, sepuf, sepdp = [
        Constant(C_BJS) * (
            inner(testfunc, trialfunc)  * dxGamma
            - inner(testfunc, n_Gamma_f) * inner(trialfunc, n_Gamma_f) * dxGamma
        )
        for (testfunc, trialfunc) in [
            (Tvf, Tuf), (Tvf, Tdp), (Tep, Tuf), (Tep, Tdp)
        ]
    ]


    a = [
        [adp, bpvp, 0, 0, 0, npvp],
        [bpvpt, -Constant(s0/dt)*mpp, Constant(alpha/dt)*bpept, 0, 0, 0],
        [0, Constant(alpha/dt)*bpep, Constant(1/dt) * (aep + sepdp), -Constant(1/dt) * sepuf, 0, Constant(1/dt) * npep],
        [0, 0, -Constant(1/dt)*svfdp, af + svfuf, bf, nfvf],
        [0, 0, 0, bft, 0, 0],
        [npvpt, 0, Constant(1 / dt) * npept, nfvft, 0, 0],
    ]


    # quick sanity check
    N_unknowns = 6
    assert len(a) == N_unknowns
    for row in a:
        assert len(row) == N_unknowns


    ## the below cause A*P to have unbounded eigenvalues
    # homogeneous Dirichlet BCs
    up_bcs = [
        DirichletBC(
            W[0], Constant((0, 0)),
            domain.porous_bdy_markers, 2
        )
    ]

    dp_bcs = [
        DirichletBC(
            W[2], Constant((0, 0)),
            domain.porous_bdy_markers, 2
        )
    ]

    uf_bcs = [
        DirichletBC(
            W[3], Constant((0, 0)),
            domain.stokes_bdy_markers, 2
        )
    ]

    bcs = [
        up_bcs,
        [],                 # pp
        dp_bcs,
        uf_bcs,
        [],                 # pf
        []                  # lbd
    ]

    # bcs = [[] for _ in range(6)] # no bcs
    
    AA = ii_assemble(a)


    bbcs = block_bc(bcs, symmetric=True)
    AA = ii_convert(AA, "")
    AA = set_lg_map(AA)

    bbcs.apply(
        AA
    )
    AAm = ii_convert(AA)
    A = AAm.array()

    # block diagonal preconditioner
    P_up = inner(up, vp) * dxDarcy + inner(div(up), div(vp)) * dxDarcy # Hdiv
    P_pp = inner(pp, wp) * dxDarcy # L2
    P_dp = (inner(dp, ep) + inner(grad(dp), grad(ep))) * dxDarcy # H1
    P_uf = (inner(uf, vf) + inner(grad(uf), grad(vf))) * dxStokes # H1
    P_pf = (inner(pf, wf)) * dxStokes # H1
    P_up, P_pp, P_dp, P_uf, P_pf = map(
        lambda form: ii_convert(ii_assemble(form)),
        [P_up, P_pp, P_dp, P_uf, P_pf]
    )

    P_lbd = hsmg.HsNorm(W[-1], s=0.5)
    P_lbd * Function(W[-1]).vector() # enforces lazy computation

    P_lbd = P_lbd.matrix

    P = ii_convert(
        block_diag_mat([P_up, P_pp, P_dp, P_uf, P_pf, P_lbd]), ""
    )

    # TODO: should I use the bbcs object returned from the call to bbcs.apply() above here?
    # or would that only be for if i wanted to apply to vectors?
    bbcs.apply(
        P
    )
    P = ii_convert(P).array()
    return A, P
Esempio n. 4
0
    def get_solver(self):
        """Returns an iterator over solution values. Values are returned as a 
        list of Functions, with the ordering being [up, pp, dp, uf, pf, lbd]. 
        First returned value is initial conditions."""

        # names of params
        # print("Using dt = {}").format(dt)
        dt = self.params["dt"]
        # print("Using dt = {}").format(dt)

        alpha = self.params["alpha"]
        alpha_BJS = self.params["alpha_BJS"]
        s0 = self.params["s0"]
        mu_f = self.params["mu_f"]
        mu_p = self.params["mu_p"]
        lbd_p = self.params["lbd_p"]
        _K = self.params["K"]
        Cp = self.params["Cp"]

        C_BJS = (mu_f * alpha_BJS) / sqrt(_K)

        # # set permeability to something else on vessel wall
        # KM, K0 = 0.1*_K, _K
        # D = self.domain.D_tunnel
        # w = 1
        # wpDh = (w+D)/2
        # class Permeability(Expression):
        #     def eval(self, value, x):
        #         if x[1] > wpDh or x[1] < -wpDh:
        #             value[0] = K0
        #         else:
        #             value[0] = KM

        # K = Permeability(degree=3)
        # K = interpolate(K, FunctionSpace(self.domain.porous_domain, "CG", 1)) # performance?
        # File("permeability.pvd") << K

        # f**k the vessel wall
        K = Constant(_K)

        self.params["K"] = K

        # names of things needed to build matrices
        dxGamma = Measure("dx", domain=self.domain.interface)
        dxDarcy = Measure("dx", domain=self.domain.porous_domain)
        dxStokes = Measure("dx", domain=self.domain.stokes_domain)

        dsDarcy = Measure("ds",
                          domain=self.domain.porous_domain,
                          subdomain_data=self.domain.porous_bdy_markers)
        dsStokes = Measure("ds",
                           domain=self.domain.stokes_domain,
                           subdomain_data=self.domain.stokes_bdy_markers)

        up, pp, dp, uf, pf, lbd = map(TrialFunction, self.W)
        vp, wp, ep, vf, wf, mu = map(TestFunction, self.W)
        up_prev, pp_prev, dp_prev, uf_prev, pf_prev, lbd_prev = map(
            Function, self.W)

        # thank you, Miro!
        Tdp, Tuf = map(lambda x: Trace(x, self.domain.interface), [dp, uf])

        Tep, Tvf = map(lambda x: Trace(x, self.domain.interface), [ep, vf])

        n_Gamma_f = self.domain.interface_normal_f
        n_Gamma_p = self.domain.interface_normal_p

        # should be removed when not in the MMS domain
        # D = self.domain.D_tunnel
        # assert n_Gamma_f(Point(0, D/2.))[1] == 1
        # assert n_Gamma_f(Point(0, -D/2.))[1] == -1
        # # assert n_Gamma_p(Point(0, 1/3.))[1] == -1

        # # tau = Constant(((0, -1),
        # #                 (1, 0))) * n_Gamma_f

        Tup = Trace(up, self.domain.interface)
        Tvp = Trace(vp, self.domain.interface)

        # a bunch of forms
        af = Constant(2 * mu_f) * inner(sym(grad(uf)), sym(
            grad(vf))) * dxStokes

        mpp = pp * wp * dx

        adp = Constant(mu_f) / K * inner(up, vp) * dxDarcy
        aep = (Constant(2 * mu_p) * inner(sym(grad(dp)), sym(grad(ep))) *
               dxDarcy + Constant(lbd_p) * inner(div(dp), div(ep)) * dxDarcy)
        bpvp = -inner(div(vp), pp) * dxDarcy
        bpvpt = -inner(div(up), wp) * dxDarcy
        bpep = -inner(div(ep), pp) * dxDarcy
        bpept = -inner(div(dp), wp) * dxDarcy
        bf = -inner(div(vf), pf) * dxStokes
        bft = -inner(div(uf), wf) * dxStokes

        # matrices living on the interface
        npvp, npep, nfvf = [
            lbd * inner(testfunc, n) * dxGamma
            for (testfunc,
                 n) in [(Tvp, n_Gamma_p), (Tep, n_Gamma_p), (Tvf, n_Gamma_f)]
        ]
        npvpt, npept, nfvft = [
            mu * inner(trialfunc, n) * dxGamma
            for (trialfunc,
                 n) in [(Tup, n_Gamma_p), (Tdp, n_Gamma_p), (Tuf, n_Gamma_f)]
        ]

        svfuf, svfdp, sepuf, sepdp = [
            Constant(C_BJS) * (inner(testfunc, trialfunc) * dxGamma - inner(
                testfunc, n_Gamma_f) * inner(trialfunc, n_Gamma_f) * dxGamma)
            # Constant(C_BJS) * (
            #     inner(testfunc, tau) * inner(trialfunc, tau) * dxGamma
            # )
            for (testfunc,
                 trialfunc) in [(Tvf, Tuf), (Tvf, Tdp), (Tep, Tuf), (Tep, Tdp)]
        ]

        Co = Constant
        a = [
            [adp, bpvp, 0, 0, 0, npvp],
            [bpvpt, -Co(s0 / dt) * mpp,
             Co(alpha / dt) * bpept, 0, 0, 0],
            [
                0,
                Constant(alpha / dt) * bpep,
                Co(1 / dt) * (aep + sepdp), -Co(1 / dt) * sepuf, 0,
                Co(1 / dt) * npep
            ],
            [0, 0, Co(-1 / dt) * svfdp, af + svfuf, bf, nfvf],
            [0, 0, 0, bft, 0, 0],
            [npvpt, 0, Co(1 / dt) * npept, nfvft, 0, 0],
        ]

        AA = ii_assemble(a)

        def compute_RHS(dp_prev, pp_prev, neumann_bcs, t):
            nf = FacetNormal(self.domain.stokes_domain)
            np = FacetNormal(self.domain.porous_domain)

            Tdp_prev = Trace(dp_prev, self.domain.interface)
            s_vp, s_wp, s_ep, s_vf, s_wf = self.get_source_terms()

            # update t in source terms
            for expr in (s_vp, s_wp, s_ep, s_vf, s_wf):
                expr.t = t

            # update t in neumann bcs
            for prob_name in ["biot", "darcy", "stokes"]:
                for expr in neumann_bcs[prob_name].values():
                    expr.t = t

            biot_neumann_terms = sum(  # val = sigma_p
                (inner(dot(val, np), ep) * dsDarcy(subdomain)
                 for subdomain, val in neumann_bcs["biot"].items()))
            stokes_neumann_terms = sum(  # val = sigma_f
                (inner(dot(val, nf), vf) * dsStokes(subdomain)
                 for subdomain, val in neumann_bcs["stokes"].items()))
            darcy_neumann_terms = sum(  # val = -pp
                (dot(val * np, vp) * dsDarcy(subdomain)
                 for subdomain, val in neumann_bcs["darcy"].items()))

            bpp = (Constant(s0 / dt) * pp_prev * wp * dxDarcy +
                   Constant(alpha / dt) * inner(div(dp_prev), wp) * dxDarcy)

            L_Cp_vp = Constant(Cp) * inner(Tvp, n_Gamma_p) * dxGamma
            L_Cp_ep = Constant(Cp) * inner(Tep, n_Gamma_p) * dxGamma

            L_BJS_vf = -Constant(C_BJS / dt) * (
                inner(Tdp_prev, Tvf) * dxGamma -
                inner(Tdp_prev, n_Gamma_f) * inner(Tvf, n_Gamma_f) * dxGamma
                # inner(Tdp_prev, tau) * inner(Tvf, tau) * dxGamma
            )
            L_BJS_ep = Constant(C_BJS / dt) * (
                inner(Tdp_prev, Tep) * dxGamma -
                inner(Tdp_prev, n_Gamma_f) * inner(Tep, n_Gamma_f) * dxGamma
                # inner(Tdp_prev, tau) * inner(Tep, tau) * dxGamma
            )

            L_mult = Constant(1 / dt) * inner(Tdp_prev,
                                              n_Gamma_p) * mu * dxGamma

            # source terms
            S_vp = (Constant(mu_f) / K) * inner(s_vp, vp) * dxDarcy
            S_wp = inner(s_wp, wp) * dxDarcy
            S_ep = inner(s_ep, ep) * dxDarcy
            S_vf = inner(s_vf, vf) * dxStokes
            S_wf = inner(s_wf, wf) * dxStokes

            L = [
                L_Cp_vp + S_vp + darcy_neumann_terms, -(bpp + S_wp),
                Co(1 / dt) * (L_Cp_ep + S_ep + L_BJS_ep + biot_neumann_terms),
                S_vf + L_BJS_vf + stokes_neumann_terms, -S_wf, L_mult
            ]
            return L

        up_bcs = [
            DirichletBC(self.W[0], self._dirichlet_bcs["darcy"][subdomain_num],
                        self.domain.porous_bdy_markers, subdomain_num)
            for subdomain_num in self._dirichlet_bcs["darcy"]
        ]

        dp_bcs = [
            DirichletBC(self.W[2], self._dirichlet_bcs["biot"][subdomain_num],
                        self.domain.porous_bdy_markers, subdomain_num)
            for subdomain_num in self._dirichlet_bcs["biot"]
        ]

        uf_bcs = [
            DirichletBC(self.W[3],
                        self._dirichlet_bcs["stokes"][subdomain_num],
                        self.domain.stokes_bdy_markers, subdomain_num)
            for subdomain_num in self._dirichlet_bcs["stokes"]
        ]

        bcs = [
            up_bcs,
            [],  # pp
            dp_bcs,
            uf_bcs,
            [],  # pf
            []  # lbd
        ]

        def update_t_in_dirichlet_bcs(t):
            all_bc_exprs = (self._dirichlet_bcs["darcy"].values() +
                            self._dirichlet_bcs["biot"].values() +
                            self._dirichlet_bcs["stokes"].values())
            for expr in all_bc_exprs:
                expr.t = t

        bbcs = block_bc(bcs, symmetric=True)
        AA = ii_convert(AA, "")
        AA = set_lg_map(AA)
        bbcs = bbcs.apply(AA)
        AAm = ii_convert(AA)
        # assert np.all(AAm.array() == AAm.array())

        w = ii_Function(self.W)

        solver = LUSolver('default')
        solver.set_operator(AAm)
        solver.parameters['reuse_factorization'] = True

        # alright, we're all set - now set initial conditions and solve
        t = 0

        initial_conditions = self.get_initial_conditions()
        for func, func_0 in zip([up_prev, pp_prev, dp_prev, uf_prev, pf_prev],
                                initial_conditions):
            func_0.t = 0
            func.assign(func_0)

        yield t, [up_prev, pp_prev, dp_prev, uf_prev, pf_prev, lbd_prev]

        while True:
            # solve for next time step
            t += dt
            update_t_in_dirichlet_bcs(t)

            L = compute_RHS(dp_prev, pp_prev, self._neumann_bcs, t)
            bb = ii_assemble(L)
            bbcs.apply(bb)
            bbm = ii_convert(bb)

            solver.solve(w.vector(), bbm)

            for i, func in enumerate(
                [up_prev, pp_prev, dp_prev, uf_prev, pf_prev, lbd_prev]):
                func.assign(w[i])

            yield t, w
Esempio n. 5
0
AA = ii_convert(ii_assemble(a), "")
# NOTE: some conversion (typically with transpose) will leave
# the matrix in a wrong state from PETSc point of view (why?)
# From the experience the typical fix is setting the local-to-global
# map for the matrix. So we do it here just in case
set_lg_map(AA)

bcs = [
    [
        DirichletBC(
            W[0], Expression(("1", "1"), degree=2), "on_boundary"
        )
    ],
    [],
    [
        DirichletBC(
            W[2], Expression(("1", "1"), degree=2), "on_boundary"
        )
    ],
    [
        DirichletBC(
            W[3], Expression(("1", "1"), degree=2), "on_boundary"
        )
    ],
    [],
    [],
]

bbcs = block_bc(bcs, symmetric=False)
bbcs.apply(AA)
Esempio n. 6
0
    def time_solve(self,
                   nonlinear_tol=1e-10,
                   iter_tol=1e-8,
                   maxNonlinIters=50,
                   maxLinIters=200,
                   show=0,
                   print_norm=True,
                   save_freq=1,
                   save_initial=True,
                   lin_solver="mumps"):
        """
        Loop through the time interval using the time step specified
        in the MechanicsProblem config dictionary.


        Parameters
        ----------

        nonlinear_tol : float (default 1e-10)
            Tolerance used to terminate Newton's method.
        iter_tol : float (default 1e-8)
            Tolerance used to terminate the iterative linear solver.
        maxNonlinIters : int (default 50)
            Maximum number of iterations for Newton's method.
        maxLinIters : int (default 200)
            Maximum number of iterations for iterative linear solver.
        show : int (default 0)
            Amount of information for iterative.LGMRES to show. See
            documentation of this class for different log levels.
        print_norm : bool (default True)
            True if user wishes to see the norm at every linear iteration
            and False otherwise.
        save_freq : int (default int)
            The frequency at which the solution is to be saved if the problem is
            unsteady. E.g., save_freq = 10 if the user wishes to save the solution
            every 10 time steps.
        save_initial : bool (default True)
            True if the user wishes to save the initial condition and False otherwise.
        lin_solver : str (default "mumps")
            Name of the linear solver to be used for steady compressible elastic
            problems. See the dolfin.solve documentation for a list of available
            linear solvers.


        Returns
        -------

        None


        """

        t, tf = self._mp.config['formulation']['time']['interval']
        t0 = t
        dt = self._mp.config['formulation']['time']['dt']
        count = 0  # Used to check if file(s) should be saved.

        lhs, rhs = self.ufl_lhs_rhs()

        # Need to be more specific here.
        bcs = block.block_bc(self._mp.dirichlet_bcs.values(), False)

        rank = dlf.MPI.rank(MPI_COMM_WORLD)

        p = self._mp.pressure
        u = self._mp.displacement
        v = self._mp.velocity
        f_objs = [self._file_pressure, self._file_disp, self._file_vel]

        # Save initial condition
        if save_initial:
            _write_objects(f_objs, t=t, close=False, u=u, v=v, p=p)
            if self._file_hdf5 is not None:
                _write_objects(self._file_hdf5, t=t, close=False, u=u, p=p)
            if self._file_xdmf is not None:
                _write_objects(self._file_xdmf, t=t, close=False, u=u, p=p)

        while t < (tf - dt / 10.0):

            # Set to the next time step
            t += dt

            # Update expressions that depend on time
            self._mp.update_time(t, t0)

            # Print the current time
            if not rank:
                print('*' * 30)
                print('t = %3.5f' % t)

            # Solve the nonlinear equation(s) at current time step.
            self.nonlinear_solve(lhs,
                                 rhs,
                                 bcs,
                                 nonlinear_tol=nonlinear_tol,
                                 iter_tol=iter_tol,
                                 maxNonlinIters=maxNonlinIters,
                                 maxLinIters=maxLinIters,
                                 show=show,
                                 print_norm=print_norm,
                                 lin_solver=lin_solver)

            # Prepare for the next time step
            if self._mp.displacement0 != 0:
                self._mp.displacement0.assign(self._mp.displacement)
            self._mp.velocity0.assign(self._mp.velocity)

            t0 = t
            count += 1

            MPI.COMM_WORLD.Barrier()

            if not count % save_freq:
                _write_objects(f_objs, t=t, close=False, u=u, v=v, p=p)
                if self._file_hdf5 is not None:
                    _write_objects(self._file_hdf5, t=t, close=False, u=u, p=p)
                if self._file_xdmf is not None:
                    _write_objects(self._file_xdmf, t=t, close=False, u=u, p=p)

        return None
Esempio n. 7
0
AA = ii_convert(ii_assemble(a), "")
# NOTE: some conversion (typically with transpose) will leave
# the matrix in a wrong state from PETSc point of view (why?)
# From the experience the typical fix is setting the local-to-global
# map for the matrix. So we do it here just in case
set_lg_map(AA)

bcs = [
    [
        DirichletBC(
            W[0], Expression(("1", "1"), degree=2), "on_boundary"
        )
    ],
    [],
    [
        DirichletBC(
            W[2], Expression(("1", "1"), degree=2), "on_boundary"
        )
    ],
    [
        DirichletBC(
            W[3], Expression(("1", "1"), degree=2), "on_boundary"
        )
    ],
    [],
    [],
]

bbcs = block_bc(bcs, symmetric=False)
bbcs.apply(AA)
Esempio n. 8
0
def compute_A_P_BDS(domain, params):
    """Computes A, P for Biot/Darcy/Stokes problem."""

    # # names of params - all 1
    # dt, alpha, alpha_BJS, s0, mu_f, mu_p, lbd_f, lbd_p, K, Cp = [float(1)]*10
    dt = params["dt"]
    alpha = params["alpha"]
    alpha_BJS = params["alpha_BJS"]
    s0 = params["s0"]
    mu_f = params["mu_f"]
    mu_p = params["mu_p"]
    lbd_p = params["lbd_p"]
    K = params["K"]
    Cp = params["Cp"]

    C_BJS = (mu_f * alpha_BJS) / sqrt(K)

    # measures
    dxGamma = Measure("dx", domain=domain.interface)
    dxDarcy = Measure("dx", domain=domain.porous_domain)
    dxStokes = Measure("dx", domain=domain.stokes_domain)

    dsDarcy = Measure("ds",
                      domain=domain.porous_domain,
                      subdomain_data=domain.porous_bdy_markers)
    dsStokes = Measure("ds",
                       domain=domain.stokes_domain,
                       subdomain_data=domain.stokes_bdy_markers)

    # test/trial functions
    W = function_spaces(domain)
    up, pp, dp, uf, pf, lbd = map(TrialFunction, W)
    vp, wp, ep, vf, wf, mu = map(TestFunction, W)

    # and their traces
    # Tdp, Tuf = map(
    #     lambda x: Trace(x, domain.interface), [dp, uf]
    # )
    # Tep, Tvf = map(
    #     lambda x: Trace(x, domain.interface), [ep, vf]
    # )

    # # normals
    # n_Gamma_f = OuterNormal(domain.interface,
    #                         [0.5] * domain.dimension)
    # assert n_Gamma_f(Point(0.0, 0.0))[1] == -1

    # n_Gamma_p = -n_Gamma_f

    # Tup = Trace(up, domain.interface, restriction="-", normal=n_Gamma_f)
    # Tvp = Trace(vp, domain.interface, restriction="-", normal=n_Gamma_f)

    # a bunch of forms
    # stokes
    af = Constant(2 * mu_f) * inner(sym(grad(uf)), sym(grad(vf))) * dxStokes
    bf = -inner(div(vf), pf) * dxStokes
    bft = -inner(div(uf), wf) * dxStokes

    # biot
    mpp = pp * wp * dx
    adp = Constant(mu_p / K) * inner(up, vp) * dxDarcy
    aep = (Constant(mu_p) * inner(sym(grad(dp)), sym(grad(ep))) * dxDarcy +
           Constant(lbd_p) * inner(div(dp), div(ep)) * dxDarcy)
    bpvp = -inner(div(vp), pp) * dxDarcy
    bpvpt = -inner(div(up), wp) * dxDarcy
    bpep = -inner(div(ep), pp) * dxDarcy
    bpept = -inner(div(dp), wp) * dxDarcy

    Co = Constant

    # biot
    # a = [
    #         [adp, bpvp, 0],
    #         [bpvpt, -Co(s0 / dt) * mpp, Co(alpha / dt) * bpept],
    #         [0, Constant(alpha / dt) * bpep, Co(1 / dt) * aep],
    # ]

    # biot
    # a = [
    #     [af, bf],
    #     [bft, 0],
    # ]

    # both w/o multiplier
    a = [
        [adp, bpvp, 0, 0, 0],
        [bpvpt, -Co(s0 / dt) * mpp,
         Co(alpha / dt) * bpept, 0, 0],
        [0, Constant(alpha / dt) * bpep,
         Co(1 / dt) * aep, 0, 0],
        [0, 0, 0, af, bf],
        [0, 0, 0, bft, 0],
    ]

    # # with multiplier
    # a = [
    #         [adp, bpvp, 0, 0, 0, 0],
    #         [bpvpt, -Co(s0 / dt) * mpp, Co(alpha / dt) * bpept, 0, 0, 0],
    #         [0, Constant(alpha / dt) * bpep, Co(1 / dt) * aep, 0, 0, 0],
    #         [0, 0, 0, af, bf, 0],
    #         [0, 0, 0, bft, 0, 0],
    #         [0, 0, 0, 0, 0, inner(mu, lbd) * dxGamma]
    # ]

    # homogeneous Dirichlet BCs
    up_bcs = [
        DirichletBC(W[0], Constant((0, 0)), domain.porous_bdy_markers, i)
        for i in [1, 2]
    ]

    dp_bcs = [
        DirichletBC(W[2], Constant((0, 0)), domain.porous_bdy_markers, i)
        for i in [1, 2]
    ]

    uf_bcs = [
        DirichletBC(W[3], Constant((0, 0)), domain.stokes_bdy_markers, i)
        for i in [
            1
        ]  # if there are dirichlet BCs all over, pressure gets underdetermined
    ]

    bcs = [
        up_bcs,
        [],  # pp
        dp_bcs,
        uf_bcs,
        [],  # pf
    ]

    AA = ii_assemble(a)

    bbcs = block_bc(bcs, symmetric=True)
    AA = ii_convert(AA, "")
    # AA = set_lg_map(AA)

    bbcs.apply(AA)

    A = ii_convert(AA).array()
    # A = AAm.array()

    # weighted block diagonal preconditioner
    P_up = Constant(mu_p / K) * inner(up, vp) * dxDarcy
    P_pp = Constant(s0 / dt + 1 /
                    (mu_p + lbd_p)) * inner(pp, wp) * dxDarcy + Constant(
                        K / mu_p) * inner(grad(pp), grad(wp)) * dxDarcy
    P_dp = (Constant(mu_p / dt) * inner(grad(dp), grad(ep))) * dxDarcy

    P_uf = (Constant(mu_f) * inner(grad(uf), grad(vf))) * dxStokes
    P_pf = (Constant(1 / mu_f) * inner(pf, wf)) * dxStokes

    P_up, P_pp, P_dp, P_uf, P_pf = map(
        # P_up, P_pp, P_dp = map(
        # P_uf, P_pf = map(
        lambda form: ii_convert(ii_assemble(form)),
        [P_up, P_pp, P_dp, P_uf, P_pf])

    P_lbd = hsmg.HsNorm(W[-1], s=0.5)
    P_lbd * Function(W[-1]).vector()  # enforces lazy computation

    P_lbd = P_lbd.matrix

    P = ii_convert(
        block_diag_mat([
            P_up,
            P_pp,
            P_dp,
            P_uf,
            P_pf,
            # P_lbd
        ]),
        "")

    # TODO: should I use the bbcs object returned from the call to bbcs.apply() above here?
    # or would that only be for if i wanted to apply to vectors?
    bbcs.apply(P)
    P = ii_convert(P).array()
    return A, P
Esempio n. 9
0
    def get_solver(self):
        """Returns an iterator over solution values. Values are returned as a 
        list of Functions, with the ordering being [up, pp, dp, uf, pf, lbd]. 
        First returned value is initial conditions."""

        # names of params

        dt = self.params["dt"]
        alpha = self.params["alpha"]
        alpha_BJS = self.params["alpha_BJS"]
        s0 = self.params["s0"]
        mu_f = self.params["mu_f"]
        mu_p = self.params["mu_p"]
        lbd_p = self.params["lbd_p"]
        K = self.params["K"]
        Cp = self.params["Cp"]

        C_BJS = (mu_f * alpha_BJS) / sqrt(K)

        # names of things needed to build matrices
        dxGamma = Measure("dx", domain=self.domain.interface)
        dxDarcy = Measure("dx", domain=self.domain.porous_domain)
        dxStokes = Measure("dx", domain=self.domain.stokes_domain)

        dsDarcy = Measure("ds",
                          domain=self.domain.porous_domain,
                          subdomain_data=self.domain.porous_bdy_markers)
        dsStokes = Measure("ds",
                           domain=self.domain.stokes_domain,
                           subdomain_data=self.domain.stokes_bdy_markers)

        up, pp, dp, uf, pf, lbd = map(TrialFunction, self.W)
        vp, wp, ep, vf, wf, mu = map(TestFunction, self.W)
        up_prev, pp_prev, dp_prev, uf_prev, pf_prev, lbd_prev = map(
            Function, self.W)

        # thank you, Miro!
        Tdp, Tuf = map(lambda x: Trace(x, self.domain.interface), [dp, uf])

        Tep, Tvf = map(lambda x: Trace(x, self.domain.interface), [ep, vf])

        # last argument is a point in the interior of the
        #  domain the normal should point outwards from
        n_Gamma_f = OuterNormal(self.domain.interface,
                                [0.5] * self.domain.dimension)
        # should be removed when not in the MMS domain
        assert n_Gamma_f(Point(0.0, 0.0))[1] == -1

        n_Gamma_p = OuterNormal(self.domain.interface,
                                [-0.5] * self.domain.dimension)
        assert n_Gamma_p(Point(0.0, 0.0))[1] == 1

        # tau = Constant(((0, -1),
        #                 (1, 0))) * n_Gamma_f

        Tup = Trace(up, self.domain.interface)
        Tvp = Trace(vp, self.domain.interface)

        # a bunch of forms
        af = Constant(2 * mu_f) * inner(sym(grad(uf)), sym(
            grad(vf))) * dxStokes

        mpp = pp * wp * dx

        adp = Constant(mu_p / K) * inner(up, vp) * dxDarcy
        aep = (Constant(2 * mu_p) * inner(sym(grad(dp)), sym(grad(ep))) *
               dxDarcy + Constant(lbd_p) * inner(div(dp), div(ep)) * dxDarcy)
        bpvp = -inner(div(vp), pp) * dxDarcy
        bpvpt = -inner(div(up), wp) * dxDarcy
        bpep = -inner(div(ep), pp) * dxDarcy
        bpept = -inner(div(dp), wp) * dxDarcy
        bf = -inner(div(vf), pf) * dxStokes
        bft = -inner(div(uf), wf) * dxStokes

        # matrices living on the interface
        npvp, npep, nfvf = [
            lbd * inner(testfunc, n) * dxGamma
            for (testfunc,
                 n) in [(Tvp, n_Gamma_p), (Tep, n_Gamma_p), (Tvf, n_Gamma_f)]
        ]
        npvpt, npept, nfvft = [
            mu * inner(trialfunc, n) * dxGamma
            for (trialfunc,
                 n) in [(Tup, n_Gamma_p), (Tdp, n_Gamma_p), (Tuf, n_Gamma_f)]
        ]

        svfuf, svfdp, sepuf, sepdp = [
            Constant(C_BJS) * (inner(testfunc, trialfunc) * dxGamma - inner(
                testfunc, n_Gamma_f) * inner(trialfunc, n_Gamma_f) * dxGamma)
            # Constant(C_BJS) * (
            #     inner(testfunc, tau) * inner(trialfunc, tau) * dxGamma
            # )
            for (testfunc,
                 trialfunc) in [(Tvf, Tuf), (Tvf, Tdp), (Tep, Tuf), (Tep, Tdp)]
        ]

        Co = Constant
        a = [[0] * len(self.W) for _ in range(len(self.W))]

        #    [adp, bpvp, 0, 0, 0, npvp],
        a[0][0] = adp
        a[0][1] = bpvp
        a[0][5] = npvp
        #    [bpvpt, -Co(s0/dt)*mpp, Co(alpha/dt)*bpept, 0, 0, 0],

        a[1][0] = bpvpt
        a[1][1] = -Co(s0 / dt) * mpp
        a[1][2] = Co(alpha / dt) * bpept
        #    [0, Constant(alpha/dt)*bpep, Co(1/dt)*(aep + Co(1/dt)*sepdp),
        # -Co(1/dt)*sepuf, 0, Co(1/dt)*npep],
        a[2][1] = Constant(alpha / dt) * bpep
        a[2][2] = Co(1 / dt) * (aep + Co(1 / dt) * sepdp)
        a[2][3] = -Co(1 / dt) * sepuf
        a[2][5] = Co(1 / dt) * npep
        # [0, 0, Co(-1/dt)*svfdp, af + svfuf, bf, nfvf],
        a[3][2] = Co(-1 / dt) * svfdp
        a[3][3] = af + svfuf
        a[3][4] = bf
        a[3][5] = nfvf
        # [0, 0, 0, bft, 0, 0],
        a[4][3] = bft
        # [npvpt, 0, Co(1/dt)*npept, nfvft, 0, 0],
        a[5][0] = npvpt
        a[5][2] = Co(1 / dt) * npept
        a[5][3] = nfvft

        def compute_RHS(dp_prev, pp_prev, neumann_bcs, t):
            nf = FacetNormal(self.domain.stokes_domain)
            np = FacetNormal(self.domain.porous_domain)

            Tdp_prev = Trace(dp_prev, self.domain.interface)
            s_vp, s_wp, s_ep, s_vf, s_wf = self.get_source_terms()

            # update t in source terms
            for expr in (s_vp, s_wp, s_ep, s_vf, s_wf):
                expr.t = t

            # update t in neumann bcs
            for prob_name in ["biot", "darcy", "stokes"]:
                for expr in neumann_bcs[prob_name].keys():
                    expr.t = t

            biot_neumann_terms, darcy_neumann_terms, stokes_neumann_terms = (
                sum([
                    inner(testfunc, neumann_bcs[prob_name][subdomain]) *
                    measure(subdomain) for subdomain in neumann_bcs[prob_name]
                ]) for prob_name, testfunc, measure in zip(
                    ["biot", "darcy", "stokes"], [ep, vp, vf],
                    [dsDarcy, dsDarcy, dsStokes]))

            L_neumann_darcy = (inner(vp, np) * Constant(0) * dsDarcy +
                               darcy_neumann_terms)

            L_neumann_biot = (Constant(0) * inner(np, ep) * dsDarcy +
                              biot_neumann_terms)
            L_neumann_stokes = (Constant(0) * inner(nf, vf) * dsStokes +
                                stokes_neumann_terms)

            bpp = (Constant(s0 / dt) * pp_prev * wp * dxDarcy +
                   Constant(alpha / dt) * inner(div(dp_prev), wp) * dxDarcy)

            L_Cp_vp = Constant(Cp) * inner(Tvp, n_Gamma_p) * dxGamma
            L_Cp_ep = Constant(Cp) * inner(Tep, n_Gamma_p) * dxGamma

            L_BJS_vf = -Constant(C_BJS / dt) * (
                inner(Tdp_prev, Tvf) * dxGamma -
                inner(Tdp_prev, n_Gamma_f) * inner(Tvf, n_Gamma_f) * dxGamma
                # inner(Tdp_prev, tau) * inner(Tvf, tau) * dxGamma
            )
            L_BJS_ep = Constant(C_BJS / dt) * (
                inner(Tdp_prev, Tep) * dxGamma -
                inner(Tdp_prev, n_Gamma_f) * inner(Tep, n_Gamma_f) * dxGamma
                # inner(Tdp_prev, tau) * inner(Tep, tau) * dxGamma
            )

            L_mult = Constant(1 / dt) * inner(Tdp_prev,
                                              n_Gamma_p) * mu * dxGamma

            # source terms
            S_vp = inner(s_vp, vp) * dxDarcy
            S_wp = inner(s_wp, wp) * dxDarcy
            S_ep = inner(s_ep, ep) * dxDarcy
            S_vf = inner(s_vf, vf) * dxStokes
            S_wf = inner(s_wf, wf) * dxStokes

            L = [
                L_Cp_vp + Constant(mu_p / K) * S_vp, -(bpp + S_wp),
                Co(1 / dt) * (L_Cp_ep + S_ep + L_BJS_ep), S_vf + L_BJS_vf,
                -S_wf, L_mult
            ]
            return L

        up_bcs = [
            DirichletBC(self.W[0], self._dirichlet_bcs["darcy"][subdomain_num],
                        self.domain.porous_bdy_markers, subdomain_num)
            for subdomain_num in self._dirichlet_bcs["darcy"]
        ]

        dp_bcs = [
            DirichletBC(self.W[2], self._dirichlet_bcs["biot"][subdomain_num],
                        self.domain.porous_bdy_markers, subdomain_num)
            for subdomain_num in self._dirichlet_bcs["biot"]
        ]

        uf_bcs = [
            DirichletBC(self.W[3],
                        self._dirichlet_bcs["stokes"][subdomain_num],
                        self.domain.stokes_bdy_markers, subdomain_num)
            for subdomain_num in self._dirichlet_bcs["stokes"]
        ]

        bcs = [
            up_bcs,
            [],  # pp
            dp_bcs,
            uf_bcs,
            [],  # pf
            []  # lbd
        ]

        AA = ii_assemble(a)

        def update_t_in_dirichlet_bcs(t):
            all_bc_exprs = (self._dirichlet_bcs["darcy"].values() +
                            self._dirichlet_bcs["biot"].values() +
                            self._dirichlet_bcs["stokes"].values())
            for expr in all_bc_exprs:
                expr.t = t

        bbcs = block_bc(bcs, symmetric=True)
        AA = ii_convert(AA, "")
        AA = set_lg_map(AA)
        bbcs = bbcs.apply(AA)
        AAm = ii_convert(AA)
        # assert np.all(AAm.array() == AAm.array())

        w = ii_Function(self.W)

        solver = LUSolver('default')
        solver.set_operator(AAm)
        solver.parameters['reuse_factorization'] = True

        # alright, we're all set - now set initial conditions and solve
        t = 0

        initial_conditions = self.get_initial_conditions()
        for func, func_0 in zip([up_prev, pp_prev, dp_prev, uf_prev, pf_prev],
                                initial_conditions):
            func_0.t = 0
            func.assign(func_0)

        yield t, [up_prev, pp_prev, dp_prev, uf_prev, pf_prev, lbd_prev]

        while True:
            # solve for next time step
            t += dt
            update_t_in_dirichlet_bcs(t)

            L = compute_RHS(dp_prev, pp_prev, self._neumann_bcs, t)
            bb = ii_assemble(L)
            bbcs.apply(bb)
            bbm = ii_convert(bb)

            solver.solve(w.vector(), bbm)

            for i, func in enumerate(
                [up_prev, pp_prev, dp_prev, uf_prev, pf_prev, lbd_prev]):
                func.assign(w[i])

            yield t, w
Esempio n. 10
0
def mini_block(n):
    '''Just check MMS'''
    mesh = UnitSquareMesh(n, n)
    # Just approx
    f_space = VectorFunctionSpace(mesh, 'DG', 1)
    h_space = TensorFunctionSpace(mesh, 'DG', 1)

    u_int = interpolate(u_exact, VectorFunctionSpace(mesh, 'CG', 2))
    p_int = interpolate(p_exact, FunctionSpace(mesh, 'CG', 2))

    f = project(-div(grad(u_int)) + grad(p_int), f_space)
    h = project(-p_int * Identity(2) + grad(u_int), h_space)

    # ----------------

    V = VectorFunctionSpace(mesh, 'Lagrange', 1)
    Vb = VectorFunctionSpace(mesh, 'Bubble', 3)
    Q = FunctionSpace(mesh, 'Lagrange', 1)
    W = [V, Vb, Q]

    u, ub, p = list(map(TrialFunction, W))
    v, vb, q = list(map(TestFunction, W))

    n = FacetNormal(mesh)

    a = [[0] * len(W) for _ in range(len(W))]
    a[0][0] = inner(grad(u), grad(v)) * dx
    a[0][2] = -inner(div(v), p) * dx
    a[2][0] = -inner(div(u), q) * dx

    a[1][1] = inner(grad(ub), grad(vb)) * dx
    a[1][2] = -inner(div(vb), p) * dx
    a[2][1] = -inner(div(ub), q) * dx

    a[0][1] = inner(grad(v), grad(ub)) * dx
    a[1][0] = inner(grad(vb), grad(u)) * dx

    # NOTE: bubbles don't contribute to surface
    L = [
        inner(dot(h, n), v) * ds + inner(f, v) * dx,
        inner(f, vb) * dx,
        inner(Constant(0), q) * dx
    ]
    # Bubbles don't have bcs on the surface
    bcs = [[DirichletBC(W[0], u_exact, 'near(x[0], 0)')], [], []]

    AA = block_assemble(a)
    bb = block_assemble(L)

    block_bc(bcs, True).apply(AA).apply(bb)

    # Preconditioner
    B0 = AMG(AA[0][0])

    H1_Vb = inner(grad(ub), grad(vb)) * dx + inner(ub, vb) * dx
    B1 = LumpedInvDiag(assemble(H1_Vb))

    L2_Q = assemble(inner(p, q) * dx)
    B2 = LumpedInvDiag(L2_Q)

    BB = block_mat([[B0, 0, 0], [0, B1, 0], [0, 0, B2]])

    x0 = AA.create_vec()
    x0.randomize()

    AAinv = MinRes(AA,
                   precond=BB,
                   tolerance=1e-10,
                   maxiter=500,
                   relativeconv=True,
                   show=2,
                   initial_guess=x0)

    # Compute solution
    u, ub, p = AAinv * bb

    uh_ = Function(V, u)
    uhb = Function(Vb, ub)
    ph = Function(Q, p)

    uh = Sum([uh_, uhb], degree=1)

    return uh, ph, sum(Wi.dim() for Wi in W)
Esempio n. 11
0
# Rhs components
L0 = inner(f, v) * dx
L1 = inner(f, c) * dx

from block import block_assemble, block_bc, block_mat
from block.iterative import MinRes
from block.algebraic.petsc import ML, Cholesky

# lhs
AA = block_assemble([[a00, a01, a02], [a10, a11, a12], [a20, a21, 0]])
# rhs
b = block_assemble([L0, L1, 0])

# Collect boundary conditions
bcs = [[bc0, bc1], [], []]
block_bc(bcs, True).apply(AA).apply(b)

b22 = inner(p, q) * dx
B22 = assemble(b22)
# Possible action of preconditioner
BB = block_mat([[ML(AA[0, 0]), 0, 0], [0, Cholesky(AA[1, 1]), 0],
                [0, 0, Cholesky(B22)]])

AAinv = MinRes(AA, precond=BB, tolerance=1E-10, maxiter=500)
U, B, P = AAinv * b

p = Function(Q)
p.vector()[:] = P

# Plot solution
plot(p)
Esempio n. 12
0
def mini_block(n):
    '''Just check MMS'''
    mesh = UnitSquareMesh(n, n)
    # Just approx
    f_space = VectorFunctionSpace(mesh, 'DG', 1)
    h_space = TensorFunctionSpace(mesh, 'DG', 1)

    u_int = interpolate(u_exact, VectorFunctionSpace(mesh, 'CG', 2))
    p_int = interpolate(p_exact, FunctionSpace(mesh, 'CG', 2))
        
    f = project(-div(grad(u_int)) + grad(p_int), f_space)
    h = project(-p_int*Identity(2) + grad(u_int), h_space)

    # ----------------

    V = VectorFunctionSpace(mesh, 'Lagrange', 1)
    Vb = VectorFunctionSpace(mesh, 'Bubble', 3)
    Q = FunctionSpace(mesh, 'Lagrange', 1)
    W = [V, Vb, Q]

    u, ub, p = map(TrialFunction, W)
    v, vb, q = map(TestFunction, W)

    n = FacetNormal(mesh)

    a = [[0]*len(W) for _ in range(len(W))]
    a[0][0] = inner(grad(u), grad(v))*dx
    a[0][2] = - inner(div(v), p)*dx
    a[2][0] = - inner(div(u), q)*dx

    a[1][1] = inner(grad(ub), grad(vb))*dx
    a[1][2] = - inner(div(vb), p)*dx
    a[2][1] = - inner(div(ub), q)*dx

    a[0][1] = inner(grad(v), grad(ub))*dx
    a[1][0] = inner(grad(vb), grad(u))*dx

    # NOTE: bubbles don't contribute to surface
    L = [inner(dot(h, n), v)*ds + inner(f, v)*dx,
         inner(f, vb)*dx,
         inner(Constant(0), q)*dx]
    # Bubbles don't have bcs on the surface
    bcs = [[DirichletBC(W[0], u_exact, 'near(x[0], 0)')], [], []]

    AA = block_assemble(a)
    bb = block_assemble(L)

    block_bc(bcs, True).apply(AA).apply(bb)

    # Preconditioner
    B0 = AMG(AA[0][0])
    
    H1_Vb = inner(grad(ub), grad(vb))*dx + inner(ub, vb)*dx
    B1 = LumpedInvDiag(assemble(H1_Vb))
    
    L2_Q = assemble(inner(p, q)*dx)
    B2 = LumpedInvDiag(L2_Q)

    BB = block_mat([[B0, 0, 0],
                    [0, B1, 0],
                    [0, 0, B2]])
    
    x0 = AA.create_vec()
    x0.randomize()

    AAinv = MinRes(AA, precond=BB, tolerance=1e-10, maxiter=500, relativeconv=True, show=2,
                   initial_guess=x0)

    # Compute solution
    u, ub, p = AAinv * bb

    uh_ = Function(V, u)
    uhb = Function(Vb, ub)
    ph = Function(Q, p)
    
    uh = Sum([uh_, uhb], degree=1)
                
    return uh, ph, sum(Wi.dim() for Wi in W)