def step(self, u1, u0, t, dt, tol=1.0e-10, maxiter=1000, verbose=True, krylov='gmres', preconditioner='ilu' ): L, b = self.problem.get_system(t + dt) A = self.M + dt * L v = TestFunction(self.problem.V) rhs = assemble(u0 * v * self.problem.dx_multiplier * self.problem.dx) rhs.axpy(dt, b) # Apply boundary conditions. for bc in self.problem.get_bcs(t + dt): bc.apply(A, rhs) solver = KrylovSolver(krylov, preconditioner) solver.parameters['relative_tolerance'] = tol solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['maximum_iterations'] = maxiter solver.parameters['monitor_convergence'] = verbose P = self.problem.get_preconditioner(t + dt) if P: solver.set_operators(A, self.M + dt * P) else: solver.set_operator(A) solver.solve(u1.vector(), rhs) return
def stokes_solve( up_out, mu, u_bcs, p_bcs, f, dx=dx, verbose=True, tol=1.0e-10, maxiter=1000 ): # Some initial sanity checks. assert mu > 0.0 WP = up_out.function_space() # Translate the boundary conditions into the product space. new_bcs = [] for k, bcs in enumerate([u_bcs, p_bcs]): for bc in bcs: space = bc.function_space() C = space.component() if len(C) == 0: new_bcs.append(DirichletBC(WP.sub(k), bc.value(), bc.domain_args[0])) elif len(C) == 1: new_bcs.append(DirichletBC(WP.sub(k).sub(int(C[0])), bc.value(), bc.domain_args[0])) else: raise RuntimeError('Illegal number of subspace components.') # TODO define p*=-1 and reverse sign in the end to get symmetric system? # Define variational problem (u, p) = TrialFunctions(WP) (v, q) = TestFunctions(WP) r = Expression('x[0]', degree=1, domain=WP.mesh()) print("mu = %e" % mu) # build system a = mu * inner(r * grad(u), grad(v)) * 2 * pi * dx \ - ((r * v[0]).dx(0) + (r * v[1]).dx(1)) * p * 2 * pi * dx \ + ((r * u[0]).dx(0) + (r * u[1]).dx(1)) * q * 2 * pi * dx #- div(r*v)*p* 2*pi*dx \ #+ q*div(r*u)* 2*pi*dx L = inner(f, v) * 2 * pi * r * dx A, b = assemble_system(a, L, new_bcs) mode = 'lu' if mode == 'lu': solve(A, up_out.vector(), b, 'lu') elif mode == 'gmres': # For preconditioners for the Stokes system, see # # Fast iterative solvers for discrete Stokes equations; # J. Peters, V. Reichelt, A. Reusken. # prec = mu * inner(r * grad(u), grad(v)) * 2 * pi * dx \ - p * q * 2 * pi * r * dx P, btmp = assemble_system(prec, L, new_bcs) solver = KrylovSolver('tfqmr', 'amg') #solver = KrylovSolver('gmres', 'amg') solver.set_operators(A, P) solver.parameters['monitor_convergence'] = verbose solver.parameters['report'] = verbose solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['relative_tolerance'] = tol solver.parameters['maximum_iterations'] = maxiter # Solve solver.solve(up_out.vector(), b) elif mode == 'fieldsplit': raise NotImplementedError('Fieldsplit solver not yet implemented.') # For an assortment of preconditioners, see # # Performance and analysis of saddle point preconditioners # for the discrete steady-state Navier-Stokes equations; # H.C. Elman, D.J. Silvester, A.J. Wathen; # Numer. Math. (2002) 90: 665-688; # <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.145.3554>. # # Set up field split. W = SubSpace(WP, 0) P = SubSpace(WP, 1) u_dofs = W.dofmap().dofs() p_dofs = P.dofmap().dofs() prec = PETScPreconditioner() prec.set_fieldsplit([u_dofs, p_dofs], ['u', 'p']) PETScOptions.set('pc_type', 'fieldsplit') PETScOptions.set('pc_fieldsplit_type', 'additive') PETScOptions.set('fieldsplit_u_pc_type', 'lu') PETScOptions.set('fieldsplit_p_pc_type', 'jacobi') # Create Krylov solver with custom preconditioner. solver = PETScKrylovSolver('gmres', prec) solver.set_operator(A) return
def solve_maxwell(V, dx, Mu, Sigma, # dictionaries omega, f_list, # list of dictionaries convections, # dictionary bcs=None, tol=1.0e-12, compute_residuals=True, verbose=False ): '''Solve the complex-valued time-harmonic Maxwell system in 2D cylindrical coordinates. :param V: function space for potentials :param dx: measure :param omega: current frequency :type omega: float :param f_list: list of right-hand sides :param convections: convection terms by subdomains :type convections: dictionary :param bcs: Dirichlet boundary conditions :param tol: solver tolerance :type tol: float :param verbose: solver verbosity :type verbose: boolean :rtype: list of functions ''' # For the exact solution of the magnetic scalar potential, see # <http://www.physics.udel.edu/~jim/PHYS809_10F/Class_Notes/Class_26.pdf>. # Here, the value of \phi along the rotational axis is specified as # # phi(z) = 2 pi I / c * (z/|z| - z/sqrt(z^2 + a^2)) # # where 'a' is the radius of the coil. This expression contradicts what is # specified by [Chaboudez97]_ who claim that phi=0 is the natural value # at the symmetry axis. # # For more analytic expressions, see # # Simple Analytic Expressions for the Magnetic Field of a Circular # Current Loop; # James Simpson, John Lane, Christopher Immer, and Robert Youngquist; # <http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20010038494_2001057024.pdf>. # # Check if boundary conditions on phi are explicitly provided. if not bcs: # Create Dirichlet boundary conditions. # In the cylindrically symmetric formulation, the magnetic vector # potential is given by # # A = e^{i omega t} phi(r,z) e_{theta}. # # It is natural to demand phi=0 along the symmetry axis r=0 to avoid # discontinuities there. # Also, this makes sure that the system is well-defined (see comment # below). # def xzero(x, on_boundary): return on_boundary and abs(x[0]) < DOLFIN_EPS bcs = DirichletBC(V * V, (0.0, 0.0), xzero) # # Concerning the boundary conditions for the rest of the system: # At the other boundaries, it is not uncommon (?) to set so-called # impedance boundary conditions; see, e.g., # # Chaboudez et al., # Numerical Modeling in Induction Heating for Axisymmetric # Geometries, # IEEE Transactions on Magnetics, vol. 33, no. 1, Jan 1997, # <http://www.esi-group.com/products/casting/publications/Articles_PDF/InductionaxiIEEE97.pdf>. # # or # # <ftp://ftp.math.ethz.ch/pub/sam-reports/reports/reports2010/2010-39.pdf>. # # TODO review those, references don't seem to be too accurate # Those translate into Robin-type boundary conditions (and are in fact # sometimes called that, cf. # https://en.wikipedia.org/wiki/Robin_boundary_condition). # The classical reference is # # Impedance boundary conditions for imperfectly conducting # surfaces, # T.B.A. Senior, # <http://link.springer.com/content/pdf/10.1007/BF02920074>. # #class OuterBoundary(SubDomain): # def inside(self, x, on_boundary): # return on_boundary and abs(x[0]) > DOLFIN_EPS #boundaries = FacetFunction('size_t', mesh) #boundaries.set_all(0) #outer_boundary = OuterBoundary() #outer_boundary.mark(boundaries, 1) #ds = Measure('ds')[boundaries] ##n = FacetNormal(mesh) ##a += - 1.0/Mu[i] * dot(grad(r*ur), n) * vr * ds(1) \ ## - 1.0/Mu[i] * dot(grad(r*ui), n) * vi * ds(1) ##L += - 1.0/Mu[i] * 1.0 * vr * ds(1) \ ## - 1.0/Mu[i] * 1.0 * vi * ds(1) ## This is -n.grad(r u) = u: #a += 1.0/Mu[i] * ur * vr * ds(1) \ # + 1.0/Mu[i] * ui * vi * ds(1) # Create the system matrix, preconditioner, and the right-hand sides. # For preconditioners, there are two approaches. The first one, described # in # # Algebraic Multigrid for Complex Symmetric Systems; # D. Lahaye, H. De Gersem, S. Vandewalle, and K. Hameyer; # <https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=877730> # # doesn't work too well here. # The matrix P, created in _build_system(), provides a better alternative. # For more details, see documentation in _build_system(). # A, P, b_list, M, W = _build_system(V, dx, Mu, Sigma, # dictionaries omega, f_list, # list of dicts convections, # dict bcs ) #from matplotlib import pyplot as pp #rows, cols, values = M.data() #from scipy.sparse import csr_matrix #M_matrix = csr_matrix((values, cols, rows)) ##from matplotlib import pyplot as pp ###pp.spy(M_matrix, precision=1e-3, marker='.', markersize=5) ##pp.spy(M_matrix) ##pp.show() ## colormap #cmap = pp.cm.gray_r #M_dense = M_matrix.todense() #from matplotlib.colors import LogNorm #im = pp.imshow(abs(M_dense), cmap=cmap, interpolation='nearest', norm=LogNorm()) ##im = pp.imshow(abs(M_dense), cmap=cmap, interpolation='nearest') ##im = pp.imshow(abs(A_r), cmap=cmap, interpolation='nearest') ##im = pp.imshow(abs(A_i), cmap=cmap, interpolation='nearest') #pp.colorbar() #pp.show() #exit() #print A #rows, cols, values = A.data() #from scipy.sparse import csr_matrix #A_matrix = csr_matrix((values, cols, rows)) ###pp.spy(A_matrix, precision=1e-3, marker='.', markersize=5) ##pp.spy(A_matrix) ##pp.show() ## colormap #cmap = pp.cm.gray_r #A_dense = A_matrix.todense() ##A_r = A_dense[0::2][0::2] ##A_i = A_dense[1::2][0::2] #cmap.set_bad('r') ##im = pp.imshow(abs(A_dense), cmap=cmap, interpolation='nearest', norm=LogNorm()) #im = pp.imshow(abs(A_dense), cmap=cmap, interpolation='nearest') ##im = pp.imshow(abs(A_r), cmap=cmap, interpolation='nearest') ##im = pp.imshow(abs(A_i), cmap=cmap, interpolation='nearest') #pp.colorbar() #pp.show() # prepare solver solver = KrylovSolver('gmres', 'amg') solver.set_operators(A, P) # The PDE for A has huge coefficients (order 10^8) all over. Hence, if # relative residual is set to 10^-6, the actual residual will still be of # the order 10^2. While this isn't too bad (after all the equations are # upscaled by a large factor), one can choose a very small relative # tolerance here to get a visually pleasing residual norm. solver.parameters['relative_tolerance'] = 1.0e-12 solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['maximum_iterations'] = 100 solver.parameters['report'] = verbose solver.parameters['monitor_convergence'] = verbose phi_list = [] for k, b in enumerate(b_list): with Message('Computing coil ring %d/%d...' % (k + 1, len(b_list))): # Define goal functional for adaptivity. # Adaptivity not working for subdomains, cf. # https://bugs.launchpad.net/dolfin/+bug/872105. #(phi_r, phi_i) = split(phi) #M = (phi_r*phi_r + phi_i*phi_i) * dx(2) phi_list.append(Function(W)) phi_list[-1].rename('phi%d' % k, 'phi%d' % k) solver.solve(phi_list[-1].vector(), b) ## Adaptive mesh refinement. #_adaptive_mesh_refinement(dx, # phi_list[-1], # Mu, Sigma, omega, # convections, # f_list[k] # ) #exit() if compute_residuals: # Sanity check: Compute residuals. # This is quite the good test that we haven't messed up # real/imaginary in the above formulation. r_r, r_i = _build_residuals(V, dx, phi_list[-1], omega, Mu, Sigma, convections, voltages ) def xzero(x, on_boundary): return on_boundary and abs(x[0]) < DOLFIN_EPS subdomain_indices = Mu.keys() # Solve an FEM problem to get the corresponding residual function # out. # This is exactly what we need here! :) u = TrialFunction(V) v = TestFunction(V) a = zero() * dx(0) for i in subdomain_indices: a += u * v * dx(i) # TODO don't hard code the boundary conditions like this R_r = Function(V) solve(a == r_r, R_r, bcs=DirichletBC(V, 0.0, xzero) ) # TODO don't hard code the boundary conditions like this R_i = Function(V) solve(a == r_i, R_i, bcs=DirichletBC(V, 0.0, xzero) ) nrm_r = norm(R_r) info('||r_r|| = %e' % nrm_r) nrm_i = norm(R_i) info('||r_i|| = %e' % nrm_i) res_norm = sqrt(nrm_r * nrm_r + nrm_i * nrm_i) info('||r|| = %e' % res_norm) plot(R_r, title='R_r') plot(R_i, title='R_i') interactive() #exit() return phi_list
def solve( V, dx, Mu, Sigma, # dictionaries omega, f_list, # list of dictionaries convections, # dictionary f_degree=None, bcs=None, tol=1.0e-12, verbose=False, ): """Solve the complex-valued time-harmonic Maxwell system in 2D cylindrical coordinates .. math:: \\div\\left(\\frac{1}{\\mu r} \\nabla(r\\phi)\\right) + \\left\\langle u, \\frac{1}{r} \\nabla(r\\phi)\\right\\rangle + i \\sigma \\omega \\phi = f with finite elements. :param V: function space for potentials :param dx: measure :param Mu: mu per subdomain :type Mu: dictionary :param Sigma: sigma per subdomain :type Sigma: dictionary :param omega: current frequency :type omega: float :param f_list: list of right-hand sides for each of which a solution will be computed :param convections: convection, defined per subdomain :type convections: dictionary :param bcs: Dirichlet boundary conditions :param tol: relative solver tolerance :type tol: float :param verbose: solver verbosity :type verbose: boolean :rtype: list of functions """ # For the exact solution of the magnetic scalar potential, see # <http://www.physics.udel.edu/~jim/PHYS809_10F/Class_Notes/Class_26.pdf>. # Here, the value of \\phi along the rotational axis is specified as # # phi(z) = 2 pi I / c * (z/|z| - z/sqrt(z^2 + a^2)) # # where 'a' is the radius of the coil. This expression contradicts what is # specified by [Chaboudez97]_ who claim that phi=0 is the natural value # at the symmetry axis. # # For more analytic expressions, see # # Simple Analytic Expressions for the Magnetic Field of a Circular # Current Loop; # James Simpson, John Lane, Christopher Immer, and Robert Youngquist; # <http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20010038494_2001057024.pdf>. # # Check if boundary conditions on phi are explicitly provided. if not bcs: # Create Dirichlet boundary conditions. # In the cylindrically symmetric formulation, the magnetic vector # potential is given by # # A = e^{i omega t} phi(r,z) e_{theta}. # # It is natural to demand phi=0 along the symmetry axis r=0 to avoid # discontinuities there. # Also, this makes sure that the system is well-defined (see comment # below). # def xzero(x, on_boundary): return on_boundary and abs(x[0]) < DOLFIN_EPS ee = V.ufl_element() * V.ufl_element() VV = FunctionSpace(V.mesh(), ee) bcs = DirichletBC(VV, (0.0, 0.0), xzero) # # Concerning the boundary conditions for the rest of the system: # At the other boundaries, it is not uncommon (?) to set so-called # impedance boundary conditions; see, e.g., # # Chaboudez et al., # Numerical Modeling in Induction Heating for Axisymmetric # Geometries, # IEEE Transactions on Magnetics, vol. 33, no. 1, Jan 1997, # <http://www.esi-group.com/products/casting/publications/Articles_PDF/InductionaxiIEEE97.pdf>. # # or # # <ftp://ftp.math.ethz.ch/pub/sam-reports/reports/reports2010/2010-39.pdf>. # # TODO review those, references don't seem to be too accurate # Those translate into Robin-type boundary conditions (and are in fact # sometimes called that, cf. # https://en.wikipedia.org/wiki/Robin_boundary_condition). # The classical reference is # # Impedance boundary conditions for imperfectly conducting surfaces, # T.B.A. Senior, # <http://link.springer.com/content/pdf/10.1007/BF02920074>. # # class OuterBoundary(SubDomain): # def inside(self, x, on_boundary): # return on_boundary and abs(x[0]) > DOLFIN_EPS # boundaries = MeshFunction( # 'size_t', mesh, # mesh.topology().dim() - 1 # ) # boundaries.set_all(0) # outer_boundary = OuterBoundary() # outer_boundary.mark(boundaries, 1) # ds = Measure('ds')[boundaries] # #n = FacetNormal(mesh) # #a += - 1.0/Mu[i] * dot(grad(r*ur), n) * vr * ds(1) \ # # - 1.0/Mu[i] * dot(grad(r*ui), n) * vi * ds(1) # #L += - 1.0/Mu[i] * 1.0 * vr * ds(1) \ # # - 1.0/Mu[i] * 1.0 * vi * ds(1) # # This is -n.grad(r u) = u: # a += 1.0/Mu[i] * ur * vr * ds(1) \ # + 1.0/Mu[i] * ui * vi * ds(1) # Create the system matrix, preconditioner, and the right-hand sides. # For preconditioners, there are two approaches. The first one, described # in # # Algebraic Multigrid for Complex Symmetric Systems; # D. Lahaye, H. De Gersem, S. Vandewalle, and K. Hameyer; # <https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=877730> # # doesn't work too well here. # The matrix P, created in build_system(), provides a better alternative. # For more details, see documentation in build_system(). # A, P, b_list, _, W = build_system( V, dx, Mu, Sigma, omega, f_list, f_degree, convections, bcs ) # prepare solver # Don't use 'amg', since that defaults to `ml_amg` if available which # crashes # <https://bitbucket.org/fenics-project/docker/issues/61/petsc-vectorfunctionspace-amg-malloc>. solver = KrylovSolver("gmres", "hypre_amg") solver.set_operators(A, P) # The PDE for A has huge coefficients (order 10^8) all over. Hence, if # relative residual is set to 10^-6, the actual residual will still be of # the order 10^2. While this isn't too bad (after all the equations are # upscaled by a large factor), one can choose a very small relative # tolerance here to get a visually pleasing residual norm. solver.parameters["relative_tolerance"] = tol solver.parameters["absolute_tolerance"] = 0.0 solver.parameters["maximum_iterations"] = 100 solver.parameters["report"] = verbose solver.parameters["monitor_convergence"] = verbose phi_list = [] for k, b in enumerate(b_list): phi_list.append(Function(W)) phi_list[-1].rename("phi{}".format(k), "phi{}".format(k)) solver.solve(phi_list[-1].vector(), b) return phi_list
def ab2tr_step0(u0, P, f, # right-hand side rho, mu, dudt_bcs=[], p_bcs=[], eps=1.0e-4, # relative error tolerance verbose=True ): # Make sure that the initial velocity is divergence-free. alpha = norm(u0, 'Hdiv0') if abs(alpha) > DOLFIN_EPS: warn('Initial velocity not divergence-free (||u||_div = %e).' % alpha ) # Get the initial u0' and p0 by solving the linear equation system # # [M C] [u0'] [f0 - (K+N(u0)u0)] # [C^T 0] [p0 ] = [ g0' ], # # i.e., # # rho u0' + nabla(p0) = f0 + mu\Delta(u0) - rho u0.nabla(u0), # div(u0') = 0. # W = u0.function_space() WP = W*P # Translate the boundary conditions into product space. See # <http://fenicsproject.org/qa/703/boundary-conditions-in-product-space>. dudt_bcs_new = [] for dudt_bc in dudt_bcs: dudt_bcs_new.append(DirichletBC(WP.sub(0), dudt_bc.value(), dudt_bc.user_sub_domain())) p_bcs_new = [] for p_bc in p_bcs: p_bcs_new.append(DirichletBC(WP.sub(1), p_bc.value(), p_bc.user_sub_domain())) new_bcs = dudt_bcs_new + p_bcs_new (u, p) = TrialFunctions(WP) (v, q) = TestFunctions(WP) #a = rho * dot(u, v) * dx + dot(grad(p), v) * dx \ a = rho * inner(u, v) * dx - p * div(v) * dx \ - div(u) * q * dx L = _rhs_weak(u0, v, f, rho, mu) A, b = assemble_system(a, L, new_bcs) # Similar preconditioner as for the Stokes problem. # TODO implement something better! prec = rho * inner(u, v) * dx \ - p*q*dx M, _ = assemble_system(prec, L, new_bcs) solver = KrylovSolver('gmres', 'amg') solver.parameters['monitor_convergence'] = verbose solver.parameters['report'] = verbose solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['relative_tolerance'] = 1.0e-6 solver.parameters['maximum_iterations'] = 10000 # Associate operator (A) and preconditioner matrix (M) solver.set_operators(A, M) #solver.set_operator(A) # Solve up = Function(WP) solver.solve(up.vector(), b) # Get sub-functions dudt0, p0 = up.split() # Choosing the first step size for the trapezoidal rule can be tricky. # Chapters 2.7.4a, 2.7.4e of the book # # Incompressible flow and the finite element method, # volume 1: advection-diffusion; # P.M. Gresho, R.L. Sani, # # give some hints. # # eps ... relative error tolerance # tau ... estimate of the initial 'time constant' tau = None if tau: dt0 = tau * eps**(1.0/3.0) else: # Choose something 'reasonably small'. dt0 = 1.0e-3 # Alternative: # Use a dissipative scheme like backward Euler or BDF2 for the first # couple of steps. This makes sure that noisy initial data is damped # out. return dudt0, p0, dt0
def solve( V, dx, Mu, Sigma, # dictionaries omega, f_list, # list of dictionaries convections, # dictionary f_degree=None, bcs=None, tol=1.0e-12, verbose=False, ): """Solve the complex-valued time-harmonic Maxwell system in 2D cylindrical coordinates .. math:: \\div\\left(\\frac{1}{\\mu r} \\nabla(r\\phi)\\right) + \\left\\langle u, \\frac{1}{r} \\nabla(r\\phi)\\right\\rangle + i \\sigma \\omega \\phi = f with finite elements. :param V: function space for potentials :param dx: measure :param Mu: mu per subdomain :type Mu: dictionary :param Sigma: sigma per subdomain :type Sigma: dictionary :param omega: current frequency :type omega: float :param f_list: list of right-hand sides for each of which a solution will be computed :param convections: convection, defined per subdomain :type convections: dictionary :param bcs: Dirichlet boundary conditions :param tol: relative solver tolerance :type tol: float :param verbose: solver verbosity :type verbose: boolean :rtype: list of functions """ # For the exact solution of the magnetic scalar potential, see # <http://www.physics.udel.edu/~jim/PHYS809_10F/Class_Notes/Class_26.pdf>. # Here, the value of \\phi along the rotational axis is specified as # # phi(z) = 2 pi I / c * (z/|z| - z/sqrt(z^2 + a^2)) # # where 'a' is the radius of the coil. This expression contradicts what is # specified by [Chaboudez97]_ who claim that phi=0 is the natural value # at the symmetry axis. # # For more analytic expressions, see # # Simple Analytic Expressions for the Magnetic Field of a Circular # Current Loop; # James Simpson, John Lane, Christopher Immer, and Robert Youngquist; # <http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20010038494_2001057024.pdf>. # # Check if boundary conditions on phi are explicitly provided. if not bcs: # Create Dirichlet boundary conditions. # In the cylindrically symmetric formulation, the magnetic vector # potential is given by # # A = e^{i omega t} phi(r,z) e_{theta}. # # It is natural to demand phi=0 along the symmetry axis r=0 to avoid # discontinuities there. # Also, this makes sure that the system is well-defined (see comment # below). # def xzero(x, on_boundary): return on_boundary and abs(x[0]) < DOLFIN_EPS ee = V.ufl_element() * V.ufl_element() VV = FunctionSpace(V.mesh(), ee) bcs = DirichletBC(VV, (0.0, 0.0), xzero) # # Concerning the boundary conditions for the rest of the system: # At the other boundaries, it is not uncommon (?) to set so-called # impedance boundary conditions; see, e.g., # # Chaboudez et al., # Numerical Modeling in Induction Heating for Axisymmetric # Geometries, # IEEE Transactions on Magnetics, vol. 33, no. 1, Jan 1997, # <http://www.esi-group.com/products/casting/publications/Articles_PDF/InductionaxiIEEE97.pdf>. # # or # # <ftp://ftp.math.ethz.ch/pub/sam-reports/reports/reports2010/2010-39.pdf>. # # TODO review those, references don't seem to be too accurate # Those translate into Robin-type boundary conditions (and are in fact # sometimes called that, cf. # https://en.wikipedia.org/wiki/Robin_boundary_condition). # The classical reference is # # Impedance boundary conditions for imperfectly conducting surfaces, # T.B.A. Senior, # <http://link.springer.com/content/pdf/10.1007/BF02920074>. # # class OuterBoundary(SubDomain): # def inside(self, x, on_boundary): # return on_boundary and abs(x[0]) > DOLFIN_EPS # boundaries = MeshFunction( # 'size_t', mesh, # mesh.topology().dim() - 1 # ) # boundaries.set_all(0) # outer_boundary = OuterBoundary() # outer_boundary.mark(boundaries, 1) # ds = Measure('ds')[boundaries] # #n = FacetNormal(mesh) # #a += - 1.0/Mu[i] * dot(grad(r*ur), n) * vr * ds(1) \ # # - 1.0/Mu[i] * dot(grad(r*ui), n) * vi * ds(1) # #L += - 1.0/Mu[i] * 1.0 * vr * ds(1) \ # # - 1.0/Mu[i] * 1.0 * vi * ds(1) # # This is -n.grad(r u) = u: # a += 1.0/Mu[i] * ur * vr * ds(1) \ # + 1.0/Mu[i] * ui * vi * ds(1) # Create the system matrix, preconditioner, and the right-hand sides. # For preconditioners, there are two approaches. The first one, described # in # # Algebraic Multigrid for Complex Symmetric Systems; # D. Lahaye, H. De Gersem, S. Vandewalle, and K. Hameyer; # <https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=877730> # # doesn't work too well here. # The matrix P, created in build_system(), provides a better alternative. # For more details, see documentation in build_system(). # A, P, b_list, _, W = build_system(V, dx, Mu, Sigma, omega, f_list, f_degree, convections, bcs) # prepare solver # Don't use 'amg', since that defaults to `ml_amg` if available which # crashes # <https://bitbucket.org/fenics-project/docker/issues/61/petsc-vectorfunctionspace-amg-malloc>. solver = KrylovSolver("gmres", "hypre_amg") solver.set_operators(A, P) # The PDE for A has huge coefficients (order 10^8) all over. Hence, if # relative residual is set to 10^-6, the actual residual will still be of # the order 10^2. While this isn't too bad (after all the equations are # upscaled by a large factor), one can choose a very small relative # tolerance here to get a visually pleasing residual norm. solver.parameters["relative_tolerance"] = tol solver.parameters["absolute_tolerance"] = 0.0 solver.parameters["maximum_iterations"] = 100 solver.parameters["report"] = verbose solver.parameters["monitor_convergence"] = verbose phi_list = [] for k, b in enumerate(b_list): phi_list.append(Function(W)) phi_list[-1].rename("phi{}".format(k), "phi{}".format(k)) solver.solve(phi_list[-1].vector(), b) return phi_list
def solve(WP, bcs, mu, f, verbose=True, tol=1.0e-13, max_iter=500): # Some initial sanity checks. assert mu > 0.0 # Define variational problem (u, p) = TrialFunctions(WP) (v, q) = TestFunctions(WP) # Build system. # The sign of the div(u)-term is somewhat arbitrary since the right-hand # side is 0 here. We can either make the system symmetric or positive- # definite. # On a second note, we have # # \int grad(p).v = - \int p * div(v) + \int_\Gamma p n.v. # # Since, we have either p=0 or n.v=0 on the boundary, we could as well # replace the term dot(grad(p), v) by -p*div(v). # a = mu * inner(grad(u), grad(v))*dx \ - p * div(v) * dx \ - q * div(u) * dx # a = mu * inner(grad(u), grad(v))*dx + dot(grad(p), v) * dx \ # - div(u) * q * dx L = inner(f, v) * dx A, b = assemble_system(a, L, bcs) # Use the preconditioner as recommended in # <http://fenicsproject.org/documentation/dolfin/dev/python/demo/pde/stokes-iterative/python/documentation.html>, # # prec = inner(grad(u), grad(v))*dx - p*q*dx # # although it doesn't seem to be too efficient. # The sign on the last term doesn't matter. prec = mu * inner(grad(u), grad(v))*dx \ - p*q*dx M, _ = assemble_system(prec, L, bcs) # solver = KrylovSolver('tfqmr', 'hypre_amg') solver = KrylovSolver('gmres', 'hypre_amg') solver.set_operators(A, M) # For an assortment of preconditioners, see # # Performance and analysis of saddle point preconditioners # for the discrete steady-state Navier-Stokes equations; # H.C. Elman, D.J. Silvester, A.J. Wathen; # Numer. Math. (2002) 90: 665-688; # <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.145.3554>. # # Set up field split. # <https://fenicsproject.org/qa/12856/fieldsplit-petscpreconditioner_set_fieldsplit-arguments> # PETScOptions.set('ksp_view') # PETScOptions.set('ksp_monitor_true_residual') # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'additive') # PETScOptions.set('pc_fieldsplit_detect_saddle_point') # PETScOptions.set('fieldsplit_0_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_0_pc_type', 'lu') # PETScOptions.set('fieldsplit_1_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_1_pc_type', 'jacobi') # solver = PETScKrylovSolver('gmres') # solver.set_operator(A) # solver.set_from_options() # http://scicomp.stackexchange.com/questions/7288/which-preconditioners-and-solver-in-petsc-for-indefinite-symmetric-systems-sho # PETScOptions.set('pc_type', 'fieldsplit') # #PETScOptions.set('pc_fieldsplit_type', 'schur') # #PETScOptions.set('pc_fieldsplit_schur_fact_type', 'upper') # PETScOptions.set('pc_fieldsplit_detect_saddle_point') # #PETScOptions.set('fieldsplit_u_pc_type', 'lsc') # #PETScOptions.set('fieldsplit_u_ksp_type', 'preonly') # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('fieldsplit_u_pc_type', 'hypre') # PETScOptions.set('fieldsplit_u_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_p_pc_type', 'jacobi') # PETScOptions.set('fieldsplit_p_ksp_type', 'preonly') # # From PETSc/src/ksp/ksp/examples/tutorials/ex42-fsschur.opts: # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'SCHUR') # PETScOptions.set('pc_fieldsplit_schur_fact_type', 'UPPER') # PETScOptions.set('fieldsplit_p_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi') # From # # Composable Linear Solvers for Multiphysics; # J. Brown, M. Knepley, D.A. May, L.C. McInnes, B. Smith; # <http://www.computer.org/csdl/proceedings/ispdc/2012/4805/00/4805a055-abs.html>; # <http://www.mcs.anl.gov/uploads/cels/papers/P2017-0112.pdf>. # # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'schur') # PETScOptions.set('pc_fieldsplit_schur_factorization_type', 'upper') # # # PETScOptions.set('fieldsplit_u_ksp_type', 'cg') # PETScOptions.set('fieldsplit_u_ksp_rtol', 1.0e-6) # PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi') # PETScOptions.set('fieldsplit_u_sub_pc_type', 'cholesky') # # # PETScOptions.set('fieldsplit_p_ksp_type', 'fgmres') # PETScOptions.set('fieldsplit_p_ksp_constant_null_space') # PETScOptions.set('fieldsplit_p_pc_type', 'lsc') # # # PETScOptions.set('fieldsplit_p_lsc_ksp_type', 'cg') # PETScOptions.set('fieldsplit_p_lsc_ksp_rtol', 1.0e-2) # PETScOptions.set('fieldsplit_p_lsc_ksp_constant_null_space') # #PETScOptions.set('fieldsplit_p_lsc_ksp_converged_reason') # PETScOptions.set('fieldsplit_p_lsc_pc_type', 'bjacobi') # PETScOptions.set('fieldsplit_p_lsc_sub_pc_type', 'icc') solver.parameters['monitor_convergence'] = verbose solver.parameters['report'] = verbose solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['relative_tolerance'] = tol solver.parameters['maximum_iterations'] = max_iter solver.parameters['error_on_nonconvergence'] = True # Solve up = Function(WP) solver.solve(up.vector(), b) # Get sub-functions u, p = up.split(True) return u, p