def setup_preconditioner(W, which, eps): from block.algebraic.petsc import AMG from block.algebraic.petsc import LumpedInvDiag, LU from hsmg import HsNorm u1, ub, p1, u2, p2, lambda_ = map(TrialFunction, W) v1, vb, q1, v2, q2, beta_ = map(TestFunction, W) # Neither is super spectacular # Completely block diagonal preconditioner with H1 on the bubble space if which == 0: b00 = inner(grad(u1), grad(v1)) * dx + inner(u1, v1) * dx B00 = AMG(ii_assemble(b00)) bbb = inner(grad(ub), grad(vb)) * dx + inner(ub, vb) * dx Bbb = LumpedInvDiag(ii_assemble(bbb)) b11 = inner(p1, q1) * dx B11 = AMG(ii_assemble(b11)) b22 = inner(div(u2), div(v2)) * dx + inner(u2, v2) * dx B22 = LU(ii_assemble(b22)) b33 = inner(p2, q2) * dx B33 = LumpedInvDiag(ii_assemble(b33)) B44 = inverse(HsNorm(W[-1], s=0.5)) return block_diag_mat([B00, Bbb, B11, B22, B33, B44]) # Monolithic for MINI velocity b = [[0, 0], [0, 0]] b[0][0] = inner(grad(u1), grad(v1)) * dx + inner(u1, v1) * dx b[0][1] = inner(grad(ub), grad(v1)) * dx + inner(ub, v1) * dx b[1][0] = inner(grad(u1), grad(vb)) * dx + inner(u1, vb) * dx b[1][1] = inner(grad(ub), grad(vb)) * dx + inner(ub, vb) * dx # Make into a monolithic matrix B00 = AMG(ii_convert(ii_assemble(b))) b11 = inner(p1, q1) * dx B11 = AMG(ii_assemble(b11)) b22 = inner(div(u2), div(v2)) * dx + inner(u2, v2) * dx B22 = LU(ii_assemble(b22)) b33 = inner(p2, q2) * dx B33 = LumpedInvDiag(ii_assemble(b33)) B44 = inverse(HsNorm(W[-1], s=0.5)) # So this is a 5x5 matrix BB = block_diag_mat([B00, B11, B22, B33, B44]) # But the preconditioner has to be 6x6; reduce 6 to 6 by comcat # first two, rest stays same R = ReductionOperator([2, 3, 4, 5, 6], W) return (R.T) * BB * R
def setup_preconditioner(W, which, eps): ''' This is a block diagonal preconditioner based on Hdiv x L2 x (H0.5 \cap sqrt(eps)*L2) or ? ''' from block.algebraic.petsc import LU from block.algebraic.petsc import LumpedInvDiag from hsmg import HsNorm S, V, Q = W # Hdiv sigma, tau = TrialFunction(S), TestFunction(S) b00 = inner(div(sigma), div(tau))*dx + inner(sigma, tau)*dx # Inverted exactly B00 = LU(ii_assemble(b00)) # L2 u, v = TrialFunction(V), TestFunction(V) b11 = inner(u, v)*dx # Inverted by BoomerAMG B11 = LumpedInvDiag(ii_assemble(b11)) # The Q norm via spectral B22 = inverse(HsNorm(Q, s=0.5) + eps*HsNorm(Q, s=0.0))# The norm is inverted exactly return block_diag_mat([B00, B11, B22])
def main(n): '''Solves grad-div problem in 2d with HypreAMS preconditioning''' # Exact solution x, y = sp.symbols('x[0] x[1]') u = sp.sin(pi * x * (1 - x) * y * (1 - y)) sp_div = lambda f: f[0].diff(x, 1) + f[1].diff(y, 1) sp_grad = lambda f: sp.Matrix([f.diff(x, 1), f.diff(y, 1)]) sigma = sp_grad(u) f = -sp_div(sigma) + u sigma_expr, u_expr, f_expr = list(map(as_expression, (sigma, u, f))) # The discrete problem mesh = UnitSquareMesh(n, n) V = FunctionSpace(mesh, 'RT', 1) Q = FunctionSpace(mesh, 'DG', 0) W = (V, Q) sigma, u = list(map(TrialFunction, W)) tau, v = list(map(TestFunction, W)) a00 = inner(sigma, tau) * dx a01 = inner(div(tau), u) * dx a10 = inner(div(sigma), v) * dx a11 = -inner(u, v) * dx L0 = inner(Constant((0, 0)), tau) * dx L1 = inner(-f_expr, v) * dx AA = block_assemble([[a00, a01], [a10, a11]]) bb = block_assemble([L0, L1]) # b00 = inner(sigma, tau)*dx + inner(div(sigma), div(tau))*dx # B00 = LU(assemble(b00)) B00 = HypreAMS(V) b11 = inner(u, v) * dx B11 = LumpedInvDiag(assemble(b11)) BB = block_mat([[B00, 0], [0, B11]]) AAinv = MinRes(AA, precond=BB, tolerance=1e-10, maxiter=500, show=2) # Compute solution sigma_h, u_h = AAinv * bb sigma_h, u_h = Function(V, sigma_h), Function(Q, u_h) niters = len(AAinv.residuals) - 1 # error = sqrt(errornorm(sigma_expr, sigma_h, 'Hdiv', degree_rise=1)**2 + # errornorm(u_expr, u_h, 'L2', degree_rise=1)**2) hmin = mesh.mpi_comm().tompi4py().allreduce(mesh.hmin(), pyMPI.MIN) error = 1. return hmin, V.dim() + Q.dim(), niters, error
def setup_preconditioner(W, which, eps): ''' This is a block diagonal preconditioner based on H1 x H1 x (H-0.5 \cap \sqrt{eps-1}*L2) or ... ''' # NOTE for eps large the fractional term is expected to dominte from block.algebraic.petsc import AMG from block.algebraic.petsc import LumpedInvDiag from hsmg import HsNorm V1, V2, Q = W if which == 0: # H1 print '\tUsing H1 x H1 x (sqrt(1./%g)*L2 \cap H-0.5) preconditioner' % eps u1, v1 = TrialFunction(V1), TestFunction(V1) b00 = inner(grad(u1), grad(v1)) * dx + inner(u1, v1) * dx # Inverted by BoomerAMG B00 = AMG(ii_assemble(b00)) u2, v2 = TrialFunction(V2), TestFunction(V2) b11 = inner(grad(u2), grad(v2)) * dx + inner(u2, v2) * dx # Inverted by BoomerAMG B11 = AMG(ii_assemble(b11)) # The Q norm via spectral, the norm is inverted exactly B22 = inverse((HsNorm(Q, s=-0.5) + (eps**-1) * HsNorm(Q, s=0.0))) else: print '\tUsing (H1 \cap H0.5) x (H1 \cap H0.5) x sqrt(%g)*L2 preconditioner' % eps iface = Q.mesh() dxGamma = dx(domain=iface) # H1 u1, v1 = TrialFunction(V1), TestFunction(V1) Tu1, Tv1 = Trace(u1, iface), Trace(v1, iface) b00 = inner(grad(u1), grad(v1)) * dx + inner(u1, v1) * dx + inner( Tu1, Tv1) * dxGamma # Inverted by BoomerAMG B00 = AMG(ii_convert(ii_assemble(b00))) u2, v2 = TrialFunction(V2), TestFunction(V2) Tu2, Tv2 = Trace(u2, iface), Trace(v2, iface) b11 = inner(grad(u2), grad(v2)) * dx + inner(u2, v2) * dx + inner( Tu2, Tv2) * dxGamma # Inverted by BoomerAMG B11 = AMG(ii_convert(ii_assemble(b11))) # The Q norm via spectral p, q = TrialFunction(Q), TestFunction(Q) b22 = Constant(1. / eps) * inner(p, q) * dxGamma B22 = LumpedInvDiag(ii_assemble(b22)) return block_diag_mat([B00, B11, B22])
def setup_preconditioner(W, which, eps): from block.algebraic.petsc import AMG from block.algebraic.petsc import LumpedInvDiag, LU from hsmg import HsNorm u1, p1, u2, p2, lambda_ = map(TrialFunction, W) v1, q1, v2, q2, beta_ = map(TestFunction, W) # This is not spectacular b00 = inner(grad(u1), grad(v1)) * dx + inner(u1, v1) * dx B00 = AMG(ii_assemble(b00)) b11 = inner(p1, q1) * dx B11 = LumpedInvDiag(ii_assemble(b11)) b22 = inner(div(u2), div(v2)) * dx + inner(u2, v2) * dx B22 = LU(ii_assemble(b22)) b33 = inner(p2, q2) * dx B33 = LumpedInvDiag(ii_assemble(b33)) B44 = inverse(HsNorm(W[-1], s=0.5)) return block_diag_mat([B00, B11, B22, B33, B44])
def setup_preconditioner(W, which, eps): '''The preconditioner''' from block.algebraic.petsc import AMG, LumpedInvDiag from block import block_transpose from hsmg import HsNorm u, ub, p, lambda_ = map(TrialFunction, W) v, vb, q, beta_ = map(TestFunction, W) # A block diagonal preconditioner if which == 0: b00 = inner(grad(u), grad(v)) * dx + inner(u, v) * dx B00 = AMG(ii_assemble(b00)) b11 = inner(grad(ub), grad(vb)) * dx + inner(ub, vb) * dx B11 = LumpedInvDiag(ii_assemble(b11)) b22 = inner(p, q) * dx B22 = AMG(ii_assemble(b22)) M = W[-1] Mi = M.sub(0).collapse() B33 = inverse(VectorizedOperator(HsNorm(Mi, s=-0.5), M)) return block_diag_mat([B00, B11, B22, B33]) # Preconditioner monolithic in Stokes velocity b = [[0, 0], [0, 0]] b[0][0] = inner(grad(u), grad(v)) * dx + inner(u, v) * dx b[0][1] = inner(grad(ub), grad(v)) * dx + inner(ub, v) * dx b[1][0] = inner(grad(u), grad(vb)) * dx + inner(u, vb) * dx b[1][1] = inner(grad(ub), grad(vb)) * dx + inner(ub, vb) * dx # Make into a monolithic matrix B00 = AMG(ii_convert(ii_assemble(b))) b11 = inner(p, q) * dx B11 = AMG(ii_assemble(b11)) M = W[-1] Mi = M.sub(0).collapse() B22 = inverse(VectorizedOperator(HsNorm(Mi, s=-0.5), M)) # So this is a 3x3 matrix BB = block_diag_mat([B00, B11, B22]) # But the preconditioner has to be 4x4 R = ReductionOperator([2, 3, 4], W) return (R.T) * BB * R
def setup_preconditioner(W, which, eps=None): ''' This is a block diagonal preconditioner based on H1 x L2 x H^{-0.5} ''' from block.algebraic.petsc import LumpedInvDiag from xii.linalg.matrix_utils import as_petsc from numpy import hstack from petsc4py import PETSc from hsmg import HsNorm V, Q, Y = W # H1 u, v = TrialFunction(V), TestFunction(V) b00 = inner(grad(u), grad(v)) * dx + inner(u, v) * dx # NOTE: since interpolation is broken with MINI I don't interpolate # here the RM basis to attach the vectros to matrix A = assemble(b00) A = as_petsc(A) # Setup the preconditioner in petsc pc = PETSc.PC().create() pc.setType(PETSc.PC.Type.HYPRE) pc.setOperators(A) # Other options opts = PETSc.Options() opts.setValue('pc_hypre_boomeramg_cycle_type', 'V') opts.setValue('pc_hypre_boomeramg_relax_type_all', 'symmetric-SOR/Jacobi') opts.setValue('pc_hypre_boomeramg_coarsen_type', 'Falgout') pc.setFromOptions() # Wrap for cbc.block B00 = BlockPC(pc) p, q = TrialFunction(Q), TestFunction(Q) B11 = LumpedInvDiag(assemble(inner(p, q) * dx)) # The Y norm via spectral Yi = Y.sub(0).collapse() B22 = inverse(VectorizedOperator(HsNorm(Yi, s=-0.5), Y)) return block_diag_mat([B00, B11, B22])
def setup_preconditioner(W, which, eps=None): ''' This is a block diagonal preconditioner based on H1 x H-0.5 or (H1 \cap H0.5) x L2 ''' from block.algebraic.petsc import AMG from block.algebraic.petsc import LumpedInvDiag from hsmg import HsNorm V, Q = W if which == 0: print 'Using H1 x H-0.5 preconditioner' # H1 u, v = TrialFunction(V), TestFunction(V) b00 = inner(grad(u), grad(v)) * dx + inner(u, v) * dx # Inverted by BoomerAMG B00 = AMG(ii_assemble(b00)) # The Q norm via spectral B11 = inverse(HsNorm(Q, s=-0.5)) # The norm is inverted exactly else: print 'Using (H1 \cap H0.5) x L2 preconditioner' bdry = Q.mesh() dxGamma = dx(domain=bdry) # Cap space u, v = TrialFunction(V), TestFunction(V) Tu, Tv = Trace(u, bdry), Trace(v, bdry) b00 = inner(grad(u), grad(v)) * dx + inner(u, v) * dx + inner( Tu, Tv) * dxGamma # Inverted by BoomrAMG B00 = AMG(ii_convert(ii_assemble(b00))) # We don't have to work so hard for multiplier p, q = TrialFunction(Q), TestFunction(Q) B11 = LumpedInvDiag(ii_assemble(inner(p, q) * dx)) return block_diag_mat([B00, B11])
def solve_problem(ncells, eps, solver_params): '''Optim problem on [0, 1]^2''' # Made up f = Expression('x[0] + x[1]', degree=1) mesh = UnitSquareMesh(*(ncells, ) * 2) bmesh = BoundaryMesh(mesh, 'exterior') Q = FunctionSpace(mesh, 'DG', 0) V = FunctionSpace(mesh, 'CG', 1) B = FunctionSpace(mesh, 'CG', 1) W = [Q, V, B] p, u, lmbda = list(map(TrialFunction, W)) q, v, beta = list(map(TestFunction, W)) Tu = Trace(u, bmesh) Tv = Trace(v, bmesh) # The line integral dxGamma = Measure('dx', domain=bmesh) a = [[0] * len(W) for _ in range(len(W))] a[0][0] = Constant(eps) * inner(p, q) * dx a[0][2] = inner(q, lmbda) * dx # We put zero on the diagonal temporarily and then replace by # the fractional laplacian a[1][1] = 0 a[1][2] = inner(grad(v), grad(lmbda)) * dx + inner(v, lmbda) * dx a[2][0] = inner(p, beta) * dx a[2][1] = inner(grad(u), grad(beta)) * dx + inner(u, beta) * dx L = [ inner(Constant(0), q) * dx, inner(Constant(0), v) * dx, # Same replacement idea here inner(Constant(0), beta) * dx ] # All but (1, 1) and 1 are final AA, bb = list(map(ii_assemble, (a, L))) # Now I want and operator which corresponds to (Tv, (-Delta^{0.5} T_u))_bdry TV = FunctionSpace(bmesh, 'CG', 1) T = PETScMatrix(trace_mat_no_restrict(V, TV)) # The fractional laplacian nodal->dual fDelta = HsNorm(TV, s=0.5) # This should be it A11 = block_transpose(T) * fDelta * T # Now, while here we can also comute the rhs contribution # (Tv, (-Delta^{0.5}, f)) f_vec = interpolate(f, V).vector() b1 = A11 * f_vec bb[1] = b1 AA[1][1] = A11 wh = ii_Function(W) # Direct solve if not solver_params: AAm, bbm = list(map(ii_convert, (AA, bb))) LUSolver('umfpack').solve(AAm, wh.vector(), bbm) return wh, -1 # Magne like precond if False: # Preconditioner like in the L2 case but with L2 bdry replaced b00 = Constant(eps) * inner(p, q) * dx B00 = LumpedInvDiag(ii_assemble(b00)) H1 = ii_assemble(inner(grad(v), grad(u)) * dx + inner(v, u) * dx) # From dual to nodal R = LumpedInvDiag(ii_assemble(inner(u, v) * dx)) # The whole matrix to be inverted is then (second term is H2 approx) B11 = collapse(A11 + eps * H1 * R * H1) # And the inverse B11 = AMG(B11, parameters={'pc_hypre_boomeramg_cycle_type': 'W'}) b22 = Constant(1. / eps) * inner(lmbda, beta) * dx B22 = LumpedInvDiag(ii_assemble(b22)) # A bit like SZ else: b00 = Constant(eps) * inner(p, q) * dx B00 = LumpedInvDiag(ii_assemble(b00)) # L2 \cap eps H1 H1 = assemble( Constant(eps) * (inner(grad(v), grad(u)) * dx + inner(v, u) * dx)) B11 = AMG(collapse(A11 + H1)) # (1./eps)*L2 \cap 1./sqrt(eps)H1 a = Constant(1./eps)*inner(beta, lmbda)*dx + \ Constant(1./sqrt(eps))*(inner(grad(beta), grad(lmbda))*dx + inner(beta, lmbda)*dx) B22 = AMG(assemble(a)) BB = block_diag_mat([B00, B11, B22]) # Want the iterations to start from random (iterative) wh.block_vec().randomize() # Default is minres if '-ksp_type' not in solver_params: solver_params['-ksp_type'] = 'minres' opts = PETSc.Options() for key, value in solver_params.items(): opts.setValue(key, None if value == 'none' else value) ksp = PETSc.KSP().create() ksp.setOperators(ii_PETScOperator(AA)) ksp.setPC(ii_PETScPreconditioner(BB, ksp)) ksp.setNormType(PETSc.KSP.NormType.NORM_PRECONDITIONED) ksp.setFromOptions() ksp.solve(as_petsc_nest(bb), wh.petsc_vec()) niters = ksp.getIterationNumber() return wh, niters
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)
def setup_preconditioner(W, which, eps=None): ''' Mirror the structure of preconditioner proposed in Robust preconditioners for PDE-constrained optimization with limited observations; Mardal and Nielsen and Nordaas, BIT 2017 or Schoeberl and Zulehner's Symmetric indefinite preconditioners for saddle point problems with applications to PDE constrained optimization problems ''' from block.algebraic.petsc import AMG from block.algebraic.petsc import LumpedInvDiag from xii.linalg.convert import collapse print('WHICH is', which) (Q, V, B) = W p, u, lmbda = list(map(TrialFunction, W)) q, v, beta = list(map(TestFunction, W)) bmesh = BoundaryMesh(Q.mesh(), 'exterior') Tu = Trace(u, bmesh) Tv = Trace(v, bmesh) # The line integral dxGamma = Measure('dx', domain=bmesh) # Nielsen if which == 0: b00 = Constant(eps) * inner(p, q) * dx B00 = LumpedInvDiag(ii_assemble(b00)) M_bdry = ii_convert(ii_assemble(inner(Tu, Tv) * dxGamma)) # H2 norm with H1 elements A = ii_assemble(inner(grad(v), grad(u)) * dx + inner(v, u) * dx) # From dual to nodal M = LumpedInvDiag(ii_assemble(inner(u, v) * dx)) # The whole matrix to be inverted is then (second term is H2 approx) B11 = collapse(M_bdry + eps * A * M * A) # And the inverse B11 = AMG(B11, parameters={'pc_hypre_boomeramg_cycle_type': 'W'}) b22 = Constant(1. / eps) * inner(lmbda, beta) * dx B22 = LumpedInvDiag(ii_assemble(b22)) # SZ else: print('X') # eps*L2 b00 = Constant(eps) * inner(p, q) * dx B00 = LumpedInvDiag(ii_assemble(b00)) # L2 \cap eps H1 a = inner(u, v) * ds + Constant(eps) * (inner(grad(v), grad(u)) * dx + inner(v, u) * dx) B11 = AMG(assemble(a)) # (1./eps)*L2 \cap 1./sqrt(eps)H1 a = Constant(1./eps)*inner(beta, lmbda)*dx + \ Constant(1./sqrt(eps))*(inner(grad(beta), grad(lmbda))*dx + inner(beta, lmbda)*dx) B22 = AMG(assemble(a)) return block_diag_mat([B00, B11, B22])
def calculate_v(self, state, dt): """ Calculate :math:`\\vec{v}` according to the algorithm introduced above. *Arguments* state (:class:`State`) The simulation state containing the magnetization configuration. dt (:class:`float`) The time-step size. """ with Timer("Calculate v"): # Initialize mesh and function spaces VV = state.VectorFunctionSpace() VS = state.FunctionSpace() v = TrialFunction(VV) w = TestFunction(VV) ####################################################### # Forms ####################################################### f_ex = Constant(-2.0 / state.scale**2 * Constants.gamma / Constants.mu0) \ * state.material.Aex / state.material.ms # Bilinear form for LLG a = state.material.alpha * dot(v, w) * state.dx('magnetic') a += dot(cross(state.m, v), w) * state.dx('magnetic') L = zero() # Effective field contributions for term in self.terms: L += term.form_rhs(state, w) * state.dx('magnetic') a += term.form_lhs( state, w, Constant(0.5 * dt) * v) * state.dx('magnetic') ####################################################### # Assembly ####################################################### # assemble llg A = assemble(a, keep_diagonal=True) A.ident_zeros() b = assemble(L) # assemble constraint #B = DofAssembler.assemble(ScalarProductMatrix(VS, VV, state.m)) #BT = DofAssembler.assemble(TransScalarProductMatrix(VS, VV, state.m)) v = TestFunction(state.FunctionSpace()) u = TrialFunction(state.VectorFunctionSpace()) B = assemble(v * dot(state.m, u) * dP) v = TestFunction(state.VectorFunctionSpace()) u = TrialFunction(state.FunctionSpace()) BT = assemble(u * dot(state.m, v) * dP) # assemble block matrix AA = block_mat([[A, BT], [B, 0]]) bb = block_vec([b, BT.create_vec()]) Ap = ILU(A) Bp = InvDiag(collapse(B * LumpedInvDiag(A) * BT)) AAp = block_mat([[Ap, BT], [0, -Bp]]).scheme('sgs') ####################################################### # Solve the system ####################################################### AAinv = BiCGStab(AA, precond=AAp, tolerance=1e-8 / dt) u, foo = AAinv * bb v = Function(VV) v.vector()[:] = u return v
def calculate_v(self, state, dt): """ Calculate :math:`\\vec{v}` according to the algorithm introduced above. *Arguments* state (:class:`State`) The simulation state containing the magnetization configuration. dt (:class:`float`) The time-step size. """ with Timer("Calculate v"): cache = self._assemble_cache if cache.requires_update(state): if isinstance(state.mesh, WrappedMesh): raise Exception("Use simple mesh with shell instead of WrappedMesh.") # setup function spaces cache.VV = VectorFunctionSpace(state.mesh, "CG", 1) cache.VD = FunctionSpace(state.mesh, "CG", self.demag_order) cache.VL = FunctionSpace(state.mesh, "CG", 1) # setup some stuff for the demag field transformation_order = 2 if (self.demag_order == 1) else 2 shell_width = state.mesh.coordinates().max(axis=0).min() / 2.0 sample_size = state.mesh.coordinates().max(axis=0) - shell_width cache.gx = MetricMatrix.create_for_cube(sample_size, 0, transformation_order) cache.gy = MetricMatrix.create_for_cube(sample_size, 1, transformation_order) cache.gz = MetricMatrix.create_for_cube(sample_size, 2, transformation_order) # Test and Trial Functions u = TrialFunction(cache.VD) v = TrialFunction(cache.VV) sigma = TrialFunction(cache.VL) w1 = TestFunction(cache.VD) w2 = TestFunction(cache.VV) w3 = TestFunction(cache.VL) ####################################################### # Define weak forms ####################################################### f_ex = (- 2.0 * state.material.Aex * Constants.gamma) / \ (Constants.mu0 * state.material.ms * state.scale**2) # Bilinear form a12 = - 0.5 * Constant(dt) * inner(v, grad(w1)) * state.dx('magnetic') # Demag Field, Implicit RHS a11 = inner(grad(w1), grad(u)) * state.dx('all') \ + inner(grad(w1), grad(u)) * state.dx(1000) \ + inner(grad(w1), cache.gx * grad(u)) * state.dx(1001) \ + inner(grad(w1), cache.gy * grad(u)) * state.dx(1002) \ + inner(grad(w1), cache.gz * grad(u)) * state.dx(1003) a21 = - Constant(state.material.ms * Constants.gamma) * inner(grad(u), w2) * state.dx('magnetic') # Demag a22 = Constant(state.material.alpha) * dot(v, w2) * state.dx('magnetic') # LLG a22 += dot(cross(state.m, v), w2) * state.dx('magnetic') a22 += - 0.5 * Constant(dt * f_ex) * Dx(v[i],j) * Dx(w2[i],j) * state.dx('magnetic') # Exchange # Linear form L1 = inner(state.m, grad(w1)) * state.dx('magnetic') # Demag Field L2 = Constant(f_ex) * Dx(state.m[i],j) * Dx(w2[i],j) * state.dx('magnetic') # Exchange for term in self.terms: # Additional Terms L2 += term.form_rhs(state, w2) * state.dx('magnetic') #a22 += term.form_lhs(state, w2, Constant(0.5 * dt) * v) * state.dx('magnetic') ####################################################### # Define boundary conditions ####################################################### bc1 = DirichletBC(cache.VD, Constant(0.0), DomainBoundary()) ####################################################### # Assemble the system ####################################################### A11, b1 = assemble_system(a11, L1, bc1) A12 = assemble(a12) A21 = assemble(a21) A22 = assemble(a22, keep_diagonal=True); A22.ident_zeros() v23 = TestFunction(state.VectorFunctionSpace()) u23 = TrialFunction(state.FunctionSpace()) A23 = assemble(u23 * dot(state.m, v23) * dP) v32 = TestFunction(state.FunctionSpace()) u32 = TrialFunction(state.VectorFunctionSpace()) A32 = assemble(v32 * dot(state.m, u32) * dP) #A23 = DofAssembler.assemble(TransScalarProductMatrix(cache.VL, cache.VV, state.m)) #A32 = DofAssembler.assemble(ScalarProductMatrix(cache.VL, cache.VV, state.m)) b2 = assemble(L2) ####################################################### # Schur Ansatz ####################################################### A11p = ILU(A11) A11inv = ConjGrad(A11, precond=A11p) z1 = A11inv * b1 S11 = A22 - A21 * A11inv * A12 S = block_mat([[S11, A23], [A32, 0]]) Sp11 = ILU(A22) Sp22 = InvDiag(collapse(A32 * LumpedInvDiag(A22) * A23)) Sp = block_mat([[Sp11, A23], [0, -Sp22]]).scheme('sgs') Sinv = BiCGStab(S, precond=Sp, tolerance=1e-8/dt) b = block_vec([b2 - A21*z1, 0]) v, _ = Sinv * b return Function(cache.VV, v)