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
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
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
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
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)
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
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
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
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)
# 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)
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)