class StreamFunction(Field): def before_first_compute(self, get): u = get("Velocity") assert len(u) == 2, "Can only compute stream function for 2D problems" V = u.function_space() spaces = SpacePool(V.mesh()) degree = V.ufl_element().degree() V = spaces.get_space(degree, 0) psi = TrialFunction(V) self.q = TestFunction(V) a = dot(grad(psi), grad(self.q)) * dx() self.bc = DirichletBC(V, Constant(0), DomainBoundary()) self.A = assemble(a) self.L = Vector() self.bc.apply(self.A) self.solver = KrylovSolver(self.A, "cg") self.psi = Function(V) def compute(self, get): u = get("Velocity") assemble(dot(u[1].dx(0) - u[0].dx(1), self.q) * dx(), tensor=self.L) self.bc.apply(self.L) self.solver.solve(self.psi.vector(), self.L) #solve(self.A, self.psi.vector(), self.L) return self.psi
def les_setup(u_, mesh, KineticEnergySGS, assemble_matrix, CG1Function, nut_krylov_solver, bcs, **NS_namespace): """ Set up for solving the Kinetic Energy SGS-model. """ DG = FunctionSpace(mesh, "DG", 0) CG1 = FunctionSpace(mesh, "CG", 1) dim = mesh.geometry().dim() delta = Function(DG) delta.vector().zero() delta.vector().axpy(1.0, assemble(TestFunction(DG) * dx)) delta.vector().set_local(delta.vector().array()**(1. / dim)) delta.vector().apply('insert') Ck = KineticEnergySGS["Ck"] ksgs = interpolate(Constant(1E-7), CG1) bc_ksgs = DirichletBC(CG1, 0, "on_boundary") A_mass = assemble_matrix(TrialFunction(CG1) * TestFunction(CG1) * dx) nut_form = Ck * delta * sqrt(ksgs) bcs_nut = derived_bcs(CG1, bcs['u0'], u_) nut_ = CG1Function(nut_form, mesh, method=nut_krylov_solver, bcs=bcs_nut, bounded=True, name="nut") At = Matrix() bt = Vector(nut_.vector()) ksgs_sol = KrylovSolver("bicgstab", "additive_schwarz") #ksgs_sol.parameters["preconditioner"]["structure"] = "same_nonzero_pattern" ksgs_sol.parameters["error_on_nonconvergence"] = False ksgs_sol.parameters["monitor_convergence"] = False ksgs_sol.parameters["report"] = False del NS_namespace return locals()
def _change_degree(self, degree, *args, **kwargs): gc.collect() with self.global_preprocessing_time: logger.debug('Creating function space...') self._V = FunctionSpace(self._mesh, "CG", degree) logger.debug('Done. Creating integration subdomains...') self.create_integration_subdomains() logger.debug('Done. Creating test function...') self._v = TestFunction(self._V) logger.debug('Done. Creating potential function...') self._potential_function = Function(self._V) logger.debug('Done. Creating trial function...') self._potential_trial = TrialFunction(self._V) logger.debug('Done. Creating LHS part of equation...') self._a = self._lhs() logger.debug('Done. Assembling linear equation matrix...') self._terms_with_unknown = assemble(self._a) logger.debug('Done. Defining boundary condition...') self._dirichlet_bc = self._boundary_condition(*args, **kwargs) logger.debug('Done. Applying boundary condition to the matrix...') self._dirichlet_bc.apply(self._terms_with_unknown) logger.debug('Done. Creating solver...') self._solver = KrylovSolver("cg", "ilu") self._solver.parameters["maximum_iterations"] = self.MAX_ITER self._solver.parameters["absolute_tolerance"] = 1E-8 logger.debug('Done. Solver created.') self._degree = degree
class StreamFunction(Field): def before_first_compute(self, get): u = get("Velocity") assert len(u) == 2, "Can only compute stream function for 2D problems" V = u.function_space() spaces = SpacePool(V.mesh()) degree = V.ufl_element().degree() V = spaces.get_space(degree, 0) psi = TrialFunction(V) self.q = TestFunction(V) a = dot(grad(psi), grad(self.q))*dx() self.bc = DirichletBC(V, Constant(0), DomainBoundary()) self.A = assemble(a) self.L = Vector() self.bc.apply(self.A) self.solver = KrylovSolver(self.A, "cg") self.psi = Function(V) def compute(self, get): u = get("Velocity") assemble(dot(u[1].dx(0)-u[0].dx(1), self.q)*dx(), tensor=self.L) self.bc.apply(self.L) self.solver.solve(self.psi.vector(), self.L) #solve(self.A, self.psi.vector(), self.L) return self.psi
def les_setup(u_, mesh, KineticEnergySGS, assemble_matrix, CG1Function, nut_krylov_solver, bcs, **NS_namespace): """ Set up for solving the Kinetic Energy SGS-model. """ DG = FunctionSpace(mesh, "DG", 0) CG1 = FunctionSpace(mesh, "CG", 1) dim = mesh.geometry().dim() delta = Function(DG) delta.vector().zero() delta.vector().axpy(1.0, assemble(TestFunction(DG)*dx)) delta.vector().set_local(delta.vector().array()**(1./dim)) delta.vector().apply('insert') Ck = KineticEnergySGS["Ck"] ksgs = interpolate(Constant(1E-7), CG1) bc_ksgs = DirichletBC(CG1, 0, "on_boundary") A_mass = assemble_matrix(TrialFunction(CG1)*TestFunction(CG1)*dx) nut_form = Ck * delta * sqrt(ksgs) bcs_nut = derived_bcs(CG1, bcs['u0'], u_) nut_ = CG1Function(nut_form, mesh, method=nut_krylov_solver, bcs=bcs_nut, bounded=True, name="nut") At = Matrix() bt = Vector(nut_.vector()) ksgs_sol = KrylovSolver("bicgstab", "additive_schwarz") ksgs_sol.parameters["preconditioner"]["structure"] = "same_nonzero_pattern" ksgs_sol.parameters["error_on_nonconvergence"] = False ksgs_sol.parameters["monitor_convergence"] = False ksgs_sol.parameters["report"] = False del NS_namespace return locals()
def get_poisson_steps(pts, cells, tol): # Still can't initialize a mesh from points, cells filename = "mesh.xdmf" if cells.shape[1] == 3: meshio.write_points_cells(filename, pts, {"triangle": cells}) else: assert cells.shape[1] == 4 meshio.write_points_cells(filename, pts, {"tetra": cells}) mesh = Mesh() with XDMFFile(filename) as f: f.read(mesh) os.remove(filename) os.remove("mesh.h5") # build Laplace matrix with Dirichlet boundary using dolfin V = FunctionSpace(mesh, "Lagrange", 1) u = TrialFunction(V) v = TestFunction(V) a = inner(grad(u), grad(v)) * dx u0 = Constant(0.0) bc = DirichletBC(V, u0, "on_boundary") f = Constant(1.0) L = f * v * dx A = assemble(a) b = assemble(L) bc.apply(A, b) # solve(A, x, b, "cg") solver = KrylovSolver("cg", "none") solver.parameters["absolute_tolerance"] = 0.0 solver.parameters["relative_tolerance"] = tol x = Function(V) x_vec = x.vector() num_steps = solver.solve(A, x_vec, b) # # convert to scipy matrix # A = as_backend_type(A).mat() # ai, aj, av = A.getValuesCSR() # A = scipy.sparse.csr_matrix( # (av, aj, ai), shape=(A.getLocalSize()[0], A.getSize()[1]) # ) # # ev = eigvals(A.todense()) # ev_max = scipy.sparse.linalg.eigs(A, k=1, which="LM")[0][0] # assert numpy.abs(ev_max.imag) < 1.0e-15 # ev_max = ev_max.real # ev_min = scipy.sparse.linalg.eigs(A, k=1, which="SM")[0][0] # assert numpy.abs(ev_min.imag) < 1.0e-15 # ev_min = ev_min.real # cond = ev_max / ev_min # solve poisson system, count num steps # b = numpy.ones(A.shape[0]) # out = pykry.gmres(A, b) # num_steps = len(out.resnorms) return num_steps
def __init__(self): mesh = UnitSquareMesh(200, 200) self.V = FunctionSpace(mesh, 'Lagrange', 2) test, trial = TestFunction(self.V), TrialFunction(self.V) M = assemble(inner(test, trial) * dx) #self.M = assemble(inner(test, trial)*dx) self.solverM = KrylovSolver("cg", "amg") self.solverM.set_operator(M)
def __missing__(self, key): assert len(key) == 4 form, bcs, solver_type, preconditioner_type = key sol = KrylovSolver(solver_type, preconditioner_type) sol.parameters["preconditioner"]["structure"] = "same" sol.parameters["error_on_nonconvergence"] = False sol.parameters["monitor_convergence"] = False sol.parameters["report"] = False self[key] = sol return self[key]
def step(self, u1, u0, t, dt, tol=1.0e-10, maxiter=100, verbose=True ): v = TestFunction(self.problem.V) L, b = self.problem.get_system(t) Lu0 = Function(self.problem.V) L.mult(u0.vector(), Lu0.vector()) rhs = assemble(u0 * v * self.problem.dx_multiplier * self.problem.dx) rhs.axpy(dt, -Lu0.vector() + b) A = self.M # Apply boundary conditions. for bc in self.problem.get_bcs(t + dt): bc.apply(A, rhs) # Both Jacobi-and AMG-preconditioners are order-optimal for the mass # matrix, Jacobi is a little more lightweight. solver = KrylovSolver('cg', 'jacobi') solver.parameters['relative_tolerance'] = tol solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['maximum_iterations'] = maxiter solver.parameters['monitor_convergence'] = verbose solver.set_operator(A) solver.solve(u1.vector(), rhs) return
def step(self, u1, u0, t, dt, tol=1.0e-10, verbose=True, maxiter=1000, krylov='gmres', preconditioner='ilu' ): v = TestFunction(self.problem.V) L0, b0 = self.problem.get_system(t) L1, b1 = self.problem.get_system(t + dt) Lu0 = Function(self.problem.V) L0.mult(u0.vector(), Lu0.vector()) rhs = assemble(u0 * v * self.problem.dx_multiplier * self.problem.dx) rhs.axpy(-dt * 0.5, Lu0.vector()) rhs.axpy(+dt * 0.5, b0 + b1) A = self.M + 0.5 * dt * L1 # 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 solver.set_operator(A) solver.solve(u1.vector(), rhs) return
def __init__(self, fem, grounded_plate_at): self.GROUNDED_PLATE_AT = grounded_plate_at self.fem = fem self.CONDUCTIVITY = list(fem.CONDUCTIVITY) self.BOUNDARY_CONDUCTIVITY = list(fem.BOUNDARY_CONDUCTIVITY) self.solver = KrylovSolver("cg", "ilu") self.solver.parameters["maximum_iterations"] = 1000 self.solver.parameters["absolute_tolerance"] = 1E-8 self.V = fem._fm.function_space self.v = fem._fm.test_function() self.u = fem._fm.trial_function() self.dx = fem._dx self.ds = fem._ds
def __init__(self, form, Space, bcs=[], name="x", matvec=[None, None], method="default", solver_type="cg", preconditioner_type="default"): Function.__init__(self, Space, name=name) self.form = form self.method = method self.bcs = bcs self.matvec = matvec self.trial = trial = TrialFunction(Space) self.test = test = TestFunction(Space) Mass = inner(trial, test)*dx() self.bf = inner(form, test)*dx() self.rhs = Vector(self.vector()) if method.lower() == "default": self.A = A_cache[(Mass, tuple(bcs))] self.sol = KrylovSolver(solver_type, preconditioner_type) self.sol.parameters["preconditioner"]["structure"] = "same" self.sol.parameters["error_on_nonconvergence"] = False self.sol.parameters["monitor_convergence"] = False self.sol.parameters["report"] = False elif method.lower() == "lumping": assert Space.ufl_element().degree() < 2 self.A = A_cache[(Mass, tuple(bcs))] ones = Function(Space) ones.vector()[:] = 1. self.ML = self.A * ones.vector() self.ML.set_local(1. / self.ML.array())
def solve_alpha_M_beta_F(self, alpha, beta, b, t): """Solve :code:`alpha * M * u + beta * F(u, t) = b` with Dirichlet conditions. """ matrix = alpha * self.M + beta * self.A # See above for float conversion right_hand_side = -float(beta) * self.b.copy() if b: right_hand_side += b for bc in self.dirichlet_bcs: bc.apply(matrix, right_hand_side) # TODO proper preconditioner for convection if self.convection: # Use HYPRE-Euclid instead of ILU for parallel computation. # However, this PC sometimes fails. # solver = KrylovSolver('gmres', 'hypre_euclid') # Fallback: solver = LUSolver() else: solver = KrylovSolver("gmres", "hypre_amg") solver.parameters["relative_tolerance"] = 1.0e-13 solver.parameters["absolute_tolerance"] = 0.0 solver.parameters["maximum_iterations"] = 100 solver.parameters["monitor_convergence"] = True solver.set_operator(matrix) u = Function(self.Q) solver.solve(u.vector(), right_hand_side) return u
def solve_alpha_M_beta_F(self, alpha, beta, b, t): # Solve alpha * M * u + beta * F(u, t) = b for u. A = alpha * self.M + beta * self.A rhs = b - beta * self.b self.bcs.apply(A, rhs) solver = KrylovSolver('gmres', 'ilu') solver.parameters['relative_tolerance'] = 1.0e-13 solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['maximum_iterations'] = 100 solver.parameters['monitor_convergence'] = True solver.set_operator(A) u = Function(self.V) solver.solve(u.vector(), rhs) return u
def _set_degree(self, degree): self._fm.degree = degree with self.global_preprocessing_time: logger.debug('Creating integration subdomains...') self.create_integration_subdomains() logger.debug('Done. Creating test function...') self._v = self._fm.test_function() logger.debug('Done. Creating potential function...') self._potential_function = self._fm.function() logger.debug('Done. Creating trial function...') self._potential_trial = self._fm.trial_function() logger.debug('Done. Creating LHS part of equation...') self._a = self._lhs() logger.debug('Done. Creating base potential formula...') self._base_potential_expression = self._potential_expression() self._base_potential_gradient_normal_expression = self._potential_gradient_normal( ) logger.debug('Done. Creating solver...') self._solver = KrylovSolver("cg", "ilu") self._solver.parameters["maximum_iterations"] = self.MAX_ITER self._solver.parameters["absolute_tolerance"] = 1E-8 logger.debug('Done. Solver created.')
def main_slice_fem(mesh, subdomains, boundaries, src_pos, snk_pos): sigma_ROI = Constant(params.sigma_roi) sigma_SLICE = Constant(params.sigma_slice) sigma_SALINE = Constant(params.sigma_saline) sigma_AIR = Constant(0.) V = FunctionSpace(mesh, "CG", 2) v = TestFunction(V) u = TrialFunction(V) phi = Function(V) dx = Measure("dx")(subdomain_data=subdomains) ds = Measure("ds")(subdomain_data=boundaries) a = inner(sigma_ROI * grad(u), grad(v))*dx(params.roivol) + \ inner(sigma_SLICE * grad(u), grad(v))*dx(params.slicevol) + \ inner(sigma_SALINE * grad(u), grad(v))*dx(params.salinevol) L = Constant(0) * v * dx A = assemble(a) b = assemble(L) x_pos, y_pos, z_pos = src_pos point = Point(x_pos, y_pos, z_pos) delta = PointSource(V, point, 1.) delta.apply(b) x_pos, y_pos, z_pos = snk_pos point1 = Point(x_pos, y_pos, z_pos) delta1 = PointSource(V, point1, -1.) delta1.apply(b) solver = KrylovSolver("cg", "ilu") solver.parameters["maximum_iterations"] = 1000 solver.parameters["absolute_tolerance"] = 1E-8 solver.parameters["monitor_convergence"] = True info(solver.parameters, True) # set_log_level(PROGRESS) does not work in fenics 2018.1.0 solver.solve(A, phi.vector(), b) ele_pos_list = params.ele_coords vals = extract_pots(phi, ele_pos_list) # np.save(os.path.join('results', save_as), vals) return vals
def solve_alpha_M_beta_F(self, alpha, beta, b, t): # Solve alpha * M * u + beta * F(u, t) = b for u. A = alpha * self.M + beta * self.A f.t = t v = TestFunction(self.V) b2 = assemble(f * v * dx) rhs = b.vector() - beta * b2 self.bcs.apply(A, rhs) solver = \ KrylovSolver('gmres', 'ilu') if alpha < 0.0 or beta > 0.0 \ else KrylovSolver('cg', 'amg') solver.parameters['relative_tolerance'] = 1.0e-13 solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['maximum_iterations'] = 100 solver.parameters['monitor_convergence'] = False solver.set_operator(A) u = Function(self.V) solver.solve(u.vector(), rhs) return u
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 before_first_compute(self, get): u = get(self.valuename) if isinstance(u, Function): if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): rank = len(u.ufl_shape) else: rank = u.rank() if rank == 0: self.f = Function(u.function_space()) elif rank >= 1: # Assume all subpaces are equal V = u.function_space().extract_sub_space([0]).collapse() mesh = V.mesh() el = V.ufl_element() self.f = Function(V) # Find out if we can operate directly on vectors, or if we have to use a projection # We can operate on vectors if all sub-dofmaps are ordered the same way # For simplicity, this is only tested for CG- or DG0-spaces # (this might always be true for these spaces, but better to be safe than sorry ) self.use_project = True if el.family() == "Lagrange" or (el.family() == "Discontinuous Lagrange" and el.degree() == 0): #dm = u.function_space().dofmap() dm0 = V.dofmap() self.use_project = False for i in xrange(u.function_space().num_sub_spaces()): Vi = u.function_space().extract_sub_space( [i]).collapse() dmi = Vi.dofmap() try: # For 1.6.0+ and newer diff = Vi.tabulate_dof_coordinates( ) - V.tabulate_dof_coordinates() except: # For 1.6.0 and older diff = dmi.tabulate_all_coordinates( mesh) - dm0.tabulate_all_coordinates(mesh) if len(diff) > 0: max_diff = max(abs(diff)) else: max_diff = 0.0 max_diff = MPI.max(mpi_comm_world(), max_diff) if max_diff > 1e-12: self.use_project = True break self.assigner = FunctionAssigner( [V] * u.function_space().num_sub_spaces(), u.function_space()) self.subfuncs = [ Function(V) for _ in range(u.function_space().num_sub_spaces()) ] # IF we have to use a projection, build projection matrix only once if self.use_project: self.v = TestFunction(V) M = assemble(inner(self.v, TrialFunction(V)) * dx) self.projection = KrylovSolver("cg", "default") self.projection.set_operator(M) elif isinstance(u, Iterable) and all( isinstance(_u, Number) for _u in u): pass elif isinstance(u, Number): pass else: # Don't know how to handle object cbc_warning( "Don't know how to calculate magnitude of object of type %s." % type(u))
class Magnitude(MetaField): """ Compute the magnitude of a Function-evaluated Field. Supports function spaces where all subspaces are equal. """ def before_first_compute(self, get): u = get(self.valuename) if isinstance(u, Function): if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): rank = len(u.ufl_shape) else: rank = u.rank() if rank == 0: self.f = Function(u.function_space()) elif rank >= 1: # Assume all subpaces are equal V = u.function_space().extract_sub_space([0]).collapse() mesh = V.mesh() el = V.ufl_element() self.f = Function(V) # Find out if we can operate directly on vectors, or if we have to use a projection # We can operate on vectors if all sub-dofmaps are ordered the same way # For simplicity, this is only tested for CG- or DG0-spaces # (this might always be true for these spaces, but better to be safe than sorry ) self.use_project = True if el.family() == "Lagrange" or (el.family() == "Discontinuous Lagrange" and el.degree() == 0): #dm = u.function_space().dofmap() dm0 = V.dofmap() self.use_project = False for i in xrange(u.function_space().num_sub_spaces()): Vi = u.function_space().extract_sub_space( [i]).collapse() dmi = Vi.dofmap() try: # For 1.6.0+ and newer diff = Vi.tabulate_dof_coordinates( ) - V.tabulate_dof_coordinates() except: # For 1.6.0 and older diff = dmi.tabulate_all_coordinates( mesh) - dm0.tabulate_all_coordinates(mesh) if len(diff) > 0: max_diff = max(abs(diff)) else: max_diff = 0.0 max_diff = MPI.max(mpi_comm_world(), max_diff) if max_diff > 1e-12: self.use_project = True break self.assigner = FunctionAssigner( [V] * u.function_space().num_sub_spaces(), u.function_space()) self.subfuncs = [ Function(V) for _ in range(u.function_space().num_sub_spaces()) ] # IF we have to use a projection, build projection matrix only once if self.use_project: self.v = TestFunction(V) M = assemble(inner(self.v, TrialFunction(V)) * dx) self.projection = KrylovSolver("cg", "default") self.projection.set_operator(M) elif isinstance(u, Iterable) and all( isinstance(_u, Number) for _u in u): pass elif isinstance(u, Number): pass else: # Don't know how to handle object cbc_warning( "Don't know how to calculate magnitude of object of type %s." % type(u)) def compute(self, get): u = get(self.valuename) if isinstance(u, Function): if not hasattr(self, "use_project"): self.before_first_compute(get) if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): rank = len(u.ufl_shape) else: rank = u.rank() if rank == 0: self.f.vector().zero() self.f.vector().axpy(1.0, u.vector()) self.f.vector().abs() return self.f elif rank >= 1: if self.use_project: b = assemble(sqrt(inner(u, u)) * self.v * dx(None)) self.projection.solve(self.f.vector(), b) else: self.assigner.assign(self.subfuncs, u) self.f.vector().zero() for i in xrange(u.function_space().num_sub_spaces()): vec = self.subfuncs[i].vector() vec.apply('') self.f.vector().axpy(1.0, vec * vec) try: sqrt_in_place(self.f.vector()) except: r = self.f.vector().local_range() self.f.vector()[r[0]:r[1]] = np.sqrt( self.f.vector()[r[0]:r[1]]) self.f.vector().apply('') return self.f elif isinstance(u, Iterable) and all( isinstance(_u, Number) for _u in u): return np.sqrt(sum(_u**2 for _u in u)) elif isinstance(u, Number): return abs(u) else: # Don't know how to handle object cbc_warning( "Don't know how to calculate magnitude of object of type %s. Returning object." % type(u)) return u
def __call__(self, degree=3, y=0., standard_deviation=1.): V = FunctionSpace(self._mesh, "CG", degree) v = TestFunction(V) potential_trial = TrialFunction(V) potential = Function(V) dx = Measure("dx")(subdomain_data=self._subdomains) # ds = Measure("ds")(subdomain_data=self._boundaries) csd = Expression(f''' x[1] >= {self.H} ? 0 : a * exp({-0.5 / standard_deviation ** 2} * ((x[0])*(x[0]) + (x[1] - {y})*(x[1] - {y}) + (x[2])*(x[2]) )) ''', degree=degree, a=1.0) self.a = csd.a = 1.0 / assemble( csd * Measure("dx", self._mesh)) # print(assemble(csd * Measure("dx", self._mesh))) L = csd * v * dx known_terms = assemble(L) # a = (inner(grad(potential_trial), grad(v)) # * (Constant(self.SALINE_CONDUCTIVITY) * dx(self.SALINE_VOL) # + Constant(self.SLICE_CONDUCTIVITY) * (dx(self.SLICE_VOL) # + dx(self.ROI_VOL)))) a = sum( Constant(conductivity) * inner(grad(potential_trial), grad(v)) * dx(domain) for domain, conductivity in [ (self.SALINE_VOL, self.SALINE_CONDUCTIVITY), (self.SLICE_VOL, self.SLICE_CONDUCTIVITY), (self.ROI_VOL, self.SLICE_CONDUCTIVITY), ]) terms_with_unknown = assemble(a) dirchlet_bc = DirichletBC( V, Constant(2.0 * 0.25 / (self.RADIUS * np.pi * self.SALINE_CONDUCTIVITY)), # 2.0 becaue of dielectric base duplicating # the current source # slice conductivity and thickness considered # negligible self._boundaries, self.MODEL_DOME) dirchlet_bc.apply(terms_with_unknown, known_terms) solver = KrylovSolver("cg", "ilu") solver.parameters["maximum_iterations"] = MAX_ITER solver.parameters["absolute_tolerance"] = 1E-8 # solver.parameters["monitor_convergence"] = True start = datetime.datetime.now() try: self.iterations = solver.solve(terms_with_unknown, potential.vector(), known_terms) return potential except RuntimeError as e: self.iterations = MAX_ITER logger.warning("Solver failed: {}".format(repr(e))) return None finally: self.time = datetime.datetime.now() - start
class _SubtractionPointSourcePotentialFEM(object): def __init__(self, config): self._fm = FunctionManagerINI(config) self._setup_mesh(self._fm.getpath('fem', 'mesh')[:-5]) self._load_config(self._fm.getpath('fem', 'config')) self.global_preprocessing_time = fc.Stopwatch() self.local_preprocessing_time = fc.Stopwatch() self.solving_time = fc.Stopwatch() self._set_degree(self.degree) def _load_mesh_data(self, path, name, dim): with XDMFFile(path) as fh: mvc = MeshValueCollection("size_t", self._fm.mesh, dim) fh.read(mvc, name) return cpp.mesh.MeshFunctionSizet(self._fm.mesh, mvc) def _setup_mesh(self, mesh): self._boundaries = self._load_mesh_data(mesh + '_boundaries.xdmf', "boundaries", 2) self._subdomains = self._load_mesh_data(mesh + '_subdomains.xdmf', "subdomains", 3) # self._facet_normal = FacetNormal(self._fm.mesh) def _load_config(self, config): self.config = configparser.ConfigParser() self.config.read(config) @property def degree(self): return self._fm.degree @degree.setter def degree(self, degree): if degree != self.degree: self._set_degree(degree) def _set_degree(self, degree): self._fm.degree = degree with self.global_preprocessing_time: logger.debug('Creating integration subdomains...') self.create_integration_subdomains() logger.debug('Done. Creating test function...') self._v = self._fm.test_function() logger.debug('Done. Creating potential function...') self._potential_function = self._fm.function() logger.debug('Done. Creating trial function...') self._potential_trial = self._fm.trial_function() logger.debug('Done. Creating LHS part of equation...') self._a = self._lhs() logger.debug('Done. Creating base potential formula...') self._base_potential_expression = self._potential_expression() self._base_potential_gradient_normal_expression = self._potential_gradient_normal( ) logger.debug('Done. Creating solver...') self._solver = KrylovSolver("cg", "ilu") self._solver.parameters["maximum_iterations"] = self.MAX_ITER self._solver.parameters["absolute_tolerance"] = 1E-8 logger.debug('Done. Solver created.') def create_integration_subdomains(self): self._dx = Measure("dx")(subdomain_data=self._subdomains) self._ds = Measure("ds")(subdomain_data=self._boundaries) def _lhs(self): return sum( inner( Constant(c) * grad(self._potential_trial), grad(self._v)) * self._dx(x) for x, c in self.CONDUCTIVITY) @property def CONDUCTIVITY(self): for section in self.config.sections(): if self._is_conductive_volume(section): yield (self.config.getint(section, 'volume'), self.config.getfloat(section, 'conductivity')) def _is_conductive_volume(self, section): return (self.config.has_option(section, 'volume') and self.config.has_option(section, 'conductivity')) @property def BOUNDARY_CONDUCTIVITY(self): for section in self.config.sections(): if self._is_conductive_boundary(section): yield (self.config.getint(section, 'surface'), self.config.getfloat(section, 'conductivity')) def _is_conductive_boundary(self, section): return (self.config.has_option(section, 'surface') and self.config.has_option(section, 'conductivity')) def _solve(self): self.iterations = self._solver.solve( self._terms_with_unknown, self._potential_function.vector(), self._known_terms) def solve(self, x, y, z): with self.local_preprocessing_time: logger.debug('Creating RHS...') L = self._rhs(x, y, z) logger.debug('Done. Assembling linear equation vector...') self._known_terms = assemble(L) logger.debug('Done. Assembling linear equation matrix...') self._terms_with_unknown = assemble(self._a) logger.debug('Done.') self._modify_linear_equation(x, y, z) try: logger.debug('Solving linear equation...') with self.solving_time: self._solve() logger.debug('Done.') return self._potential_function except RuntimeError as e: self.iterations = self.MAX_ITER logger.warning("Solver failed: {}".format(repr(e))) return None def _rhs(self, x, y, z): base_conductivity = self.base_conductivity(x, y, z) self._setup_expression(self._base_potential_expression, base_conductivity, x, y, z) self._setup_expression( self._base_potential_gradient_normal_expression, base_conductivity, x, y, z) return ( -sum((inner( (Constant(c - base_conductivity) * grad(self._base_potential_expression)), grad(self._v)) * self._dx(x) for x, c in self.CONDUCTIVITY if c != base_conductivity)) # # Eq. 18 at Piastra et al 2018 - sum( Constant(c) # * inner(self._facet_normal, # grad(self._base_potential_expression)) * self._base_potential_gradient_normal_expression * self._v * self._ds(s) for s, c in self.BOUNDARY_CONDUCTIVITY)) def _setup_expression(self, expression, base_conductivity, x, y, z): expression.conductivity = base_conductivity expression.src_x = x expression.src_y = y expression.src_z = z
class AcousticWave(): """ Solution of forward and adjoint equations for acoustic inverse problem """ def __init__(self, functionspaces_V): """ Input: functionspaces_V = dict containing functionspaces for state/adj ('V') and med param ('Vl' for lambda and 'Vr' for rho) """ self.readV(functionspaces_V) self.verbose = False # print info self.lump = False # Lump the mass matrix self.lumpD = False # Lump the ABC matrix self.timestepper = None # 'backward', 'centered' self.exact = None # exact solution at final time self.u0init = None # provides u(t=t0) self.utinit = None # provides u_t(t=t0) self.u1init = None # provides u1 = u(t=t0+/-Dt) self.bc = None self.abc = False self.ftime = lambda t: 0.0 # ftime(tt) = src term @ t=tt (in np.array()) self.set_fwd() # default is forward problem def copy(self): """(hard) copy constructor""" newobj = self.__class__({'V':self.V, 'Vl':self.Vl, 'Vr':self.Vr}) newobj.lump = self.lump newobj.timestepper = self.timestepper newobj.exact = self.exact newobj.utinit = self.utinit newobj.u1init = self.u1init newobj.bc = self.bc if self.abc == True: newobj.set_abc(self.V.mesh(), self.class_bc_abc, self.lumpD) newobj.ftime = self.ftime newobj.update({'lambda':self.lam, 'rho':self.rho, \ 't0':self.t0, 'tf':self.tf, 'Dt':self.Dt, \ 'u0init':self.u0init, 'utinit':self.utinit, 'u1init':self.u1init}) return newobj def readV(self, functionspaces_V): # Solutions: self.V = functionspaces_V['V'] self.test = TestFunction(self.V) self.trial = TrialFunction(self.V) self.u0 = Function(self.V) # u(t-Dt) self.u1 = Function(self.V) # u(t) self.u2 = Function(self.V) # u(t+Dt) self.rhs = Function(self.V) self.sol = Function(self.V) # Parameters: self.Vl = functionspaces_V['Vl'] self.lam = Function(self.Vl) self.Vr = functionspaces_V['Vr'] self.rho = Function(self.Vr) if functionspaces_V.has_key('Vm'): self.Vm = functionspaces_V['Vm'] self.mu = Function(self.Vm) self.elastic = True assert(False) else: self.elastic = False self.weak_k = inner(self.lam*nabla_grad(self.trial), \ nabla_grad(self.test))*dx self.weak_m = inner(self.rho*self.trial,self.test)*dx def set_abc(self, mesh, class_bc_abc, lumpD=False): self.abc = True # False means zero-Neumann all-around if lumpD: self.lumpD = True abc_boundaryparts = FacetFunction("size_t", mesh) class_bc_abc.mark(abc_boundaryparts, 1) self.ds = Measure("ds")[abc_boundaryparts] self.weak_d = inner(sqrt(self.lam*self.rho)*self.trial, self.test)*self.ds(1) self.class_bc_abc = class_bc_abc # to make copies def set_fwd(self): self.fwdadj = 1.0 self.ftime = None def set_adj(self): self.fwdadj = -1.0 self.ftime = None def get_tt(self, nn): if self.fwdadj > 0.0: return self.times[nn] else: return self.times[-nn-1] def update(self, parameters_m): assert not self.timestepper == None, "You need to set a time stepping method" # Time options: if parameters_m.has_key('t0'): self.t0 = parameters_m['t0'] if parameters_m.has_key('tf'): self.tf = parameters_m['tf'] if parameters_m.has_key('Dt'): self.Dt = parameters_m['Dt'] if parameters_m.has_key('t0') or parameters_m.has_key('tf') or parameters_m.has_key('Dt'): self.Nt = int(round((self.tf-self.t0)/self.Dt)) self.Tf = self.t0 + self.Dt*self.Nt self.times = np.linspace(self.t0, self.Tf, self.Nt+1) assert isequal(self.times[1]-self.times[0], self.Dt, 1e-16), "Dt modified" self.Dt = self.times[1] - self.times[0] assert isequal(self.Tf, self.tf, 1e-2), "Final time differs by more than 1%" if not isequal(self.Tf, self.tf, 1e-12): print 'Final time modified from {} to {} ({}%)'.\ format(self.tf, self.Tf, abs(self.Tf-self.tf)/self.tf) # Initial conditions: if parameters_m.has_key('u0init'): self.u0init = parameters_m['u0init'] if parameters_m.has_key('utinit'): self.utinit = parameters_m['utinit'] if parameters_m.has_key('u1init'): self.u1init = parameters_m['u1init'] if parameters_m.has_key('um1init'): self.um1init = parameters_m['um1init'] # Medium parameters: setfct(self.lam, parameters_m['lambda']) if self.verbose: print 'lambda updated ' if self.elastic == True: setfct(self.mu, parameters_m['mu']) if self.verbose: print 'mu updated' if self.verbose: print 'assemble K', self.K = assemble(self.weak_k) if self.verbose: print ' -- K assembled' if parameters_m.has_key('rho'): setfct(self.rho, parameters_m['rho']) # Mass matrix: if self.verbose: print 'rho updated\nassemble M', Mfull = assemble(self.weak_m) if self.lump: self.solverM = LumpedMatrixSolverS(self.V) self.solverM.set_operator(Mfull, self.bc) self.M = self.solverM else: if mpisize == 1: self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True else: self.solverM = KrylovSolver('cg', 'amg') self.solverM.parameters['report'] = False self.M = Mfull if not self.bc == None: self.bc.apply(Mfull) self.solverM.set_operator(Mfull) if self.verbose: print ' -- M assembled' # Matrix D for abs BC if self.abc == True: if self.verbose: print 'assemble D', Mfull = assemble(self.weak_m) Dfull = assemble(self.weak_d) if self.lumpD: self.D = LumpedMatrixSolverS(self.V) self.D.set_operator(Dfull, None, False) if self.lump: self.solverMplD = LumpedMatrixSolverS(self.V) self.solverMplD.set_operators(Mfull, Dfull, .5*self.Dt, self.bc) self.MminD = LumpedMatrixSolverS(self.V) self.MminD.set_operators(Mfull, Dfull, -.5*self.Dt, self.bc) else: self.D = Dfull if self.verbose: print ' -- D assembled' else: self.D = 0.0 #@profile def solve(self): """ General solver method """ if self.timestepper == 'backward': def iterate(tt): self.iteration_backward(tt) elif self.timestepper == 'centered': def iterate(tt): self.iteration_centered(tt) else: print "Time stepper not implemented" sys.exit(1) if self.verbose: print 'Compute solution' solout = [] # Store computed solution # u0: tt = self.get_tt(0) if self.verbose: print 'Compute solution -- time {}'.format(tt) setfct(self.u0, self.u0init) solout.append([self.u0.vector().array(), tt]) # Compute u1: if not self.u1init == None: self.u1 = self.u1init else: assert(not self.utinit == None) setfct(self.rhs, self.ftime(tt)) self.rhs.vector().axpy(-self.fwdadj, self.D*self.utinit.vector()) self.rhs.vector().axpy(-1.0, self.K*self.u0.vector()) if not self.bc == None: self.bc.apply(self.rhs.vector()) self.solverM.solve(self.sol.vector(), self.rhs.vector()) setfct(self.u1, self.u0) self.u1.vector().axpy(self.fwdadj*self.Dt, self.utinit.vector()) self.u1.vector().axpy(0.5*self.Dt**2, self.sol.vector()) tt = self.get_tt(1) if self.verbose: print 'Compute solution -- time {}'.format(tt) solout.append([self.u1.vector().array(), tt]) # Iteration for nn in xrange(2, self.Nt+1): iterate(tt) # Advance to next time step setfct(self.u0, self.u1) setfct(self.u1, self.u2) tt = self.get_tt(nn) if self.verbose: print 'Compute solution -- time {}, rhs {}'.\ format(tt, np.max(np.abs(self.ftime(tt)))) solout.append([self.u1.vector().array(),tt]) if self.fwdadj > 0.0: assert isequal(tt, self.Tf, 1e-16), \ 'tt={}, Tf={}, reldiff={}'.format(tt, self.Tf, abs(tt-self.Tf)/self.Tf) else: assert isequal(tt, self.t0, 1e-16), \ 'tt={}, t0={}, reldiff={}'.format(tt, self.t0, abs(tt-self.t0)) return solout, self.computeerror() def iteration_centered(self, tt): setfct(self.rhs, (self.Dt**2)*self.ftime(tt)) self.rhs.vector().axpy(-1.0, self.MminD*self.u0.vector()) self.rhs.vector().axpy(2.0, self.M*self.u1.vector()) self.rhs.vector().axpy(-self.Dt**2, self.K*self.u1.vector()) if not self.bc == None: self.bc.apply(self.rhs.vector()) self.solverMplD.solve(self.u2.vector(), self.rhs.vector()) def iteration_backward(self, tt): setfct(self.rhs, self.Dt*self.ftime(tt)) self.rhs.vector().axpy(-self.Dt, self.K*self.u1.vector()) self.rhs.vector().axpy(-1.0, self.D*(self.u1.vector()-self.u0.vector())) if not self.bc == None: self.bc.apply(self.rhs.vector()) self.solverM.solve(self.sol.vector(), self.rhs.vector()) setfct(self.u2, 2.0*self.u1.vector()) self.u2.vector().axpy(-1.0, self.u0.vector()) self.u2.vector().axpy(self.Dt, self.sol.vector()) def computeerror(self): return self.computerelativeerror() def computerelativeerror(self): if not self.exact == None: #MM = assemble(inner(self.trial, self.test)*dx) MM = self.M norm_ex = np.sqrt(\ (MM*self.exact.vector()).inner(self.exact.vector())) diff = self.exact.vector() - self.u1.vector() if norm_ex > 1e-16: return np.sqrt((MM*diff).inner(diff))/norm_ex else: return np.sqrt((MM*diff).inner(diff)) else: return [] def computeabserror(self): if not self.exact == None: MM = self.M diff = self.exact.vector() - self.u1.vector() return np.sqrt((MM*diff).inner(diff)) else: return []
source_z=7.85, source_y=0, sigma_2=0.1, A=(2 * np.pi * 0.1) ** -1.5, degree=DEGREE) #B_inv = assemble(csd * Measure('dx', mesh)) # csd_at = interpolate(csd, V) # csd_at(0, 0, 7.2) < 0 def boundary(x, on_boundary): return x[1] <= NECK_AT bc = DirichletBC(V, Constant(0.), boundary) solver = KrylovSolver("cg", "ilu") solver.parameters["maximum_iterations"] = 1100 solver.parameters["absolute_tolerance"] = 1E-8 #solver.parameters["monitor_convergence"] = True SOURCES = [] DBG = {} TMP_FILENAME = f'proof_of_concept_fem_dirchlet_newman_CTX_deg_{DEGREE}_.npz' try: fh = np.load(TMP_FILENAME) except FileNotFoundError: print('no previous results found') previously_solved = set()
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
class SphericalModelFEM(object): def __init__(self, fem, grounded_plate_at): self.GROUNDED_PLATE_AT = grounded_plate_at self.fem = fem self.CONDUCTIVITY = list(fem.CONDUCTIVITY) self.BOUNDARY_CONDUCTIVITY = list(fem.BOUNDARY_CONDUCTIVITY) self.solver = KrylovSolver("cg", "ilu") self.solver.parameters["maximum_iterations"] = 1000 self.solver.parameters["absolute_tolerance"] = 1E-8 self.V = fem._fm.function_space self.v = fem._fm.test_function() self.u = fem._fm.trial_function() self.dx = fem._dx self.ds = fem._ds def function(self): return self.fem._fm.function() def reciprocal_correction_potential(self, x, y, z): """ .. deprecated:: Moved to `SphereOnGroundedPlatePointSourcePotentialFEM` class (rewritten) and available as its `.solve()` method. Preserved as the original code not obfuscated by the `_SubtractionPointSourcePotentialFEM` class protocol. Parameters ---------- x y z Returns ------- """ dx_src = f'(x[0] - src_x)' dy_src = f'(x[1] - src_y)' dz_src = f'(x[2] - src_z)' r_src2 = f'({dx_src} * {dx_src} + {dy_src} * {dy_src} + {dz_src} * {dz_src})' r_src = f'sqrt({r_src2})' r_sphere2 = '(x[0] * x[0] + x[1] * x[1] + x[2] * x[2])' dot_src = f'({dx_src} * x[0] + {dy_src} * x[1] + {dz_src} * x[2]) / sqrt({r_src2} * {r_sphere2})' potential_exp = Expression(f''' {0.25 / np.pi} / conductivity * (1.0 / {r_src}) ''', degree=self.fem.degree, domain=self.fem._fm._mesh, conductivity=0.33, src_x=0.0, src_y=0.0, src_z=0.0) minus_potential_exp = Expression(f''' {-0.25 / np.pi} / conductivity * (1.0 / {r_src}) ''', degree=self.fem.degree, domain=self.fem._fm._mesh, conductivity=0.33, src_x=0.0, src_y=0.0, src_z=0.0) potential_grad_dot = Expression(f''' x[2] >= {self.GROUNDED_PLATE_AT} ? -1 * {-0.25 / np.pi} / conductivity * ({dot_src} / {r_src2}) : 0 ''', degree=self.fem.degree, domain=self.fem._fm._mesh, conductivity=0.33, src_x=0.0, src_y=0.0, src_z=0.0) conductivity = fem.base_conductivity(x, y, z) for expr in [potential_exp, potential_grad_dot, minus_potential_exp]: expr.conductivity = conductivity expr.src_x = x expr.src_y = y expr.src_z = z print(' solving') a = sum( inner(Constant(c) * grad(self.u), grad(self.v)) * self.dx(i) for i, c in self.CONDUCTIVITY) L = (-sum( inner((Constant(c - conductivity) * grad(potential_exp)), grad(self.v)) * self.dx(i) for i, c in self.CONDUCTIVITY if c != conductivity) + sum( Constant(c) * potential_grad_dot * self.v * self.ds(i) for i, c in self.BOUNDARY_CONDUCTIVITY)) return self.solve(L, a, minus_potential_exp) def source_potential(self, csd=None, src=None): if csd is None: csd = self.callable_as_function(src.csd) a = sum( Constant(c) * inner(grad(self.u), grad(self.v)) * self.dx(i) for i, c in self.CONDUCTIVITY) L = csd * self.v * self.dx return self.solve(L, a, Constant(0)) def source_correction(self, src): potential_f = self.callable_as_function(src.potential) conductivity = self.fem.base_conductivity(x_ele, y_ele, z_ele) print(' solving') a = sum( inner(Constant(c) * grad(self.u), grad(self.v)) * self.dx(i) for i, c in self.CONDUCTIVITY) L = (-sum( inner((Constant(c - conductivity) * grad(potential_f)), grad(self.v)) * self.dx(i) for i, c in self.CONDUCTIVITY if c != conductivity) - sum( Constant(c) * inner(grad(potential_f), dolfin.FacetNormal(self.fem._fm.mesh)) * self.v * self.ds(i) for i, c in self.BOUNDARY_CONDUCTIVITY)) neg_potential_f = NegativePotential(src.potential, self.fem._fm.mesh) return self.solve(L, a, neg_potential_f) def callable_as_function(self, f): n = self.V.dim() d = self.fem._fm.mesh.geometry().dim() dof_coordinates = self.V.tabulate_dof_coordinates() dof_coordinates.resize((n, d)) dof_x = dof_coordinates[:, 0] dof_y = dof_coordinates[:, 1] dof_z = dof_coordinates[:, 2] g = fem._fm.function() g.vector()[:] = f(dof_x, dof_y, dof_z) return g def solve(self, L, a, plate_potential): A = assemble(a) b = assemble(L) # print(' assembled') dirichlet_bc = DirichletBC(self.V, plate_potential, (lambda x, on_boundary: on_boundary and x[2] < self.GROUNDED_PLATE_AT)) dirichlet_bc.apply(A, b) # print(' modified') f = self.function() self.solver.solve(A, f.vector(), b) # print(' solved') return f
def update(self, parameters_m): assert not self.timestepper == None, "You need to set a time stepping method" # Time options: if parameters_m.has_key('t0'): self.t0 = parameters_m['t0'] if parameters_m.has_key('tf'): self.tf = parameters_m['tf'] if parameters_m.has_key('Dt'): self.Dt = parameters_m['Dt'] if parameters_m.has_key('t0') or parameters_m.has_key('tf') or parameters_m.has_key('Dt'): self.Nt = int(round((self.tf-self.t0)/self.Dt)) self.Tf = self.t0 + self.Dt*self.Nt self.times = np.linspace(self.t0, self.Tf, self.Nt+1) assert isequal(self.times[1]-self.times[0], self.Dt, 1e-16), "Dt modified" self.Dt = self.times[1] - self.times[0] assert isequal(self.Tf, self.tf, 1e-2), "Final time differs by more than 1%" if not isequal(self.Tf, self.tf, 1e-12): print 'Final time modified from {} to {} ({}%)'.\ format(self.tf, self.Tf, abs(self.Tf-self.tf)/self.tf) # Initial conditions: if parameters_m.has_key('u0init'): self.u0init = parameters_m['u0init'] if parameters_m.has_key('utinit'): self.utinit = parameters_m['utinit'] if parameters_m.has_key('u1init'): self.u1init = parameters_m['u1init'] if parameters_m.has_key('um1init'): self.um1init = parameters_m['um1init'] # Medium parameters: setfct(self.lam, parameters_m['lambda']) if self.verbose: print 'lambda updated ' if self.elastic == True: setfct(self.mu, parameters_m['mu']) if self.verbose: print 'mu updated' if self.verbose: print 'assemble K', self.K = assemble(self.weak_k) if self.verbose: print ' -- K assembled' if parameters_m.has_key('rho'): setfct(self.rho, parameters_m['rho']) # Mass matrix: if self.verbose: print 'rho updated\nassemble M', Mfull = assemble(self.weak_m) if self.lump: self.solverM = LumpedMatrixSolverS(self.V) self.solverM.set_operator(Mfull, self.bc) self.M = self.solverM else: if mpisize == 1: self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True else: self.solverM = KrylovSolver('cg', 'amg') self.solverM.parameters['report'] = False self.M = Mfull if not self.bc == None: self.bc.apply(Mfull) self.solverM.set_operator(Mfull) if self.verbose: print ' -- M assembled' # Matrix D for abs BC if self.abc == True: if self.verbose: print 'assemble D', Mfull = assemble(self.weak_m) Dfull = assemble(self.weak_d) if self.lumpD: self.D = LumpedMatrixSolverS(self.V) self.D.set_operator(Dfull, None, False) if self.lump: self.solverMplD = LumpedMatrixSolverS(self.V) self.solverMplD.set_operators(Mfull, Dfull, .5*self.Dt, self.bc) self.MminD = LumpedMatrixSolverS(self.V) self.MminD.set_operators(Mfull, Dfull, -.5*self.Dt, self.bc) else: self.D = Dfull if self.verbose: print ' -- D assembled' else: self.D = 0.0
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 forward(mu_expression, lmbda_expression, rho, Lx=10, Ly=10, t_end=1, omega_p=5, amplitude=5000, center=0, target=False): Lpml = Lx / 10 #c_p = cp(mu.vector(), lmbda.vector(), rho) max_velocity = 200 #c_p.max() stable_hx = stable_dx(max_velocity, omega_p) nx = int(Lx / stable_hx) + 1 #nx = max(nx, 60) ny = int(Ly * nx / Lx) + 1 mesh = mesh_generator(Lx, Ly, Lpml, nx, ny) used_hx = Lx / nx dt = stable_dt(used_hx, max_velocity) cfl_ct = cfl_constant(max_velocity, dt, used_hx) print(used_hx, stable_hx) print(cfl_ct) #time.sleep(10) PE = FunctionSpace(mesh, "DG", 0) mu = interpolate(mu_expression, PE) lmbda = interpolate(lmbda_expression, PE) m = 2 R = 10e-8 t = 0.0 gamma = 0.50 beta = 0.25 ff = MeshFunction("size_t", mesh, mesh.geometry().dim() - 1) Dirichlet(Lx, Ly, Lpml).mark(ff, 1) # Create function spaces VE = VectorElement("CG", mesh.ufl_cell(), 1, dim=2) TE = TensorElement("DG", mesh.ufl_cell(), 0, shape=(2, 2), symmetry=True) W = FunctionSpace(mesh, MixedElement([VE, TE])) F = FunctionSpace(mesh, "CG", 2) V = W.sub(0).collapse() M = W.sub(1).collapse() alpha_0 = Alpha_0(m, stable_hx, R, Lpml) alpha_1 = Alpha_1(alpha_0, Lx, Lpml, degree=2) alpha_2 = Alpha_2(alpha_0, Ly, Lpml, degree=2) beta_0 = Beta_0(m, max_velocity, R, Lpml) beta_1 = Beta_1(beta_0, Lx, Lpml, degree=2) beta_2 = Beta_2(beta_0, Ly, Lpml, degree=2) alpha_1 = interpolate(alpha_1, F) alpha_2 = interpolate(alpha_2, F) beta_1 = interpolate(beta_1, F) beta_2 = interpolate(beta_2, F) a_ = alpha_1 * alpha_2 b_ = alpha_1 * beta_2 + alpha_2 * beta_1 c_ = beta_1 * beta_2 Lambda_e = as_tensor([[alpha_2, 0], [0, alpha_1]]) Lambda_p = as_tensor([[beta_2, 0], [0, beta_1]]) a_ = alpha_1 * alpha_2 b_ = alpha_1 * beta_2 + alpha_2 * beta_1 c_ = beta_1 * beta_2 Lambda_e = as_tensor([[alpha_2, 0], [0, alpha_1]]) Lambda_p = as_tensor([[beta_2, 0], [0, beta_1]]) # Set up boundary condition bc = DirichletBC(W.sub(0), Constant(("0.0", "0.0")), ff, 1) # Create measure for the source term dx = Measure("dx", domain=mesh) ds = Measure("ds", domain=mesh, subdomain_data=ff) # Set up initial values u0 = Function(V) u0.set_allow_extrapolation(True) v0 = Function(V) a0 = Function(V) U0 = Function(M) V0 = Function(M) A0 = Function(M) # Test and trial functions (u, S) = TrialFunctions(W) (w, T) = TestFunctions(W) g = ModifiedRickerPulse(0, omega_p, amplitude, center) F = rho * inner(a_ * N_ddot(u, u0, a0, v0, dt, beta) \ + b_ * N_dot(u, u0, v0, a0, dt, beta, gamma) + c_ * u, w) * dx \ + inner(N_dot(S, U0, V0, A0, dt, beta, gamma).T * Lambda_e + S.T * Lambda_p, grad(w)) * dx \ - inner(g, w) * ds \ + inner(compliance(a_ * N_ddot(S, U0, A0, V0, dt, beta) + b_ * N_dot(S, U0, V0, A0, dt, beta, gamma) + c_ * S, u, mu, lmbda), T) * dx \ - 0.5 * inner(grad(u) * Lambda_p + Lambda_p * grad(u).T + grad(N_dot(u, u0, v0, a0, dt, beta, gamma)) * Lambda_e \ + Lambda_e * grad(N_dot(u, u0, v0, a0, dt, beta, gamma)).T, T) * dx \ a, L = lhs(F), rhs(F) # Assemble rhs (once) A = assemble(a) # Create GMRES Krylov solver solver = KrylovSolver(A, "gmres") # Create solution function S = Function(W) if target: xdmffile_u = XDMFFile("inversion_temporal_file/target/u.xdmf") pvd = File("inversion_temporal_file/target/u.pvd") xdmffile_u.write(u0, t) timeseries_u = TimeSeries( "inversion_temporal_file/target/u_timeseries") else: xdmffile_u = XDMFFile("inversion_temporal_file/obs/u.xdmf") xdmffile_u.write(u0, t) timeseries_u = TimeSeries("inversion_temporal_file/obs/u_timeseries") rec_counter = 0 while t < t_end - 0.5 * dt: t += float(dt) if rec_counter % 10 == 0: print( '\n\rtime: {:.3f} (Progress: {:.2f}%)'.format( t, 100 * t / t_end), ) g.t = t # Assemble rhs and apply boundary condition b = assemble(L) bc.apply(A, b) # Compute solution solver.solve(S.vector(), b) (u, U) = S.split(True) # Update previous time step update(u, u0, v0, a0, beta, gamma, dt) update(U, U0, V0, A0, beta, gamma, dt) xdmffile_u.write(u, t) pvd << (u, t) timeseries_u.store(u.vector(), t) energy = inner(u, u) * dx E = assemble(energy) print("E = ", E) print(u.vector().max())
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 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
class OasisFunction(Function): """Function with more or less efficient projection methods of associated linear form. The matvec option is provided for letting the right hand side be computed through a fast matrix vector product. Both the matrix and the Coefficient of the required vector must be provided. method = "default" Solve projection with regular linear algebra using solver_type and preconditioner_type method = "lumping" Solve through lumping of mass matrix """ def __init__(self, form, Space, bcs=[], name="x", matvec=[None, None], method="default", solver_type="cg", preconditioner_type="default"): Function.__init__(self, Space, name=name) self.form = form self.method = method self.bcs = bcs self.matvec = matvec self.trial = trial = TrialFunction(Space) self.test = test = TestFunction(Space) Mass = inner(trial, test)*dx() self.bf = inner(form, test)*dx() self.rhs = Vector(self.vector()) if method.lower() == "default": self.A = A_cache[(Mass, tuple(bcs))] self.sol = KrylovSolver(solver_type, preconditioner_type) self.sol.parameters["preconditioner"]["structure"] = "same" self.sol.parameters["error_on_nonconvergence"] = False self.sol.parameters["monitor_convergence"] = False self.sol.parameters["report"] = False elif method.lower() == "lumping": assert Space.ufl_element().degree() < 2 self.A = A_cache[(Mass, tuple(bcs))] ones = Function(Space) ones.vector()[:] = 1. self.ML = self.A * ones.vector() self.ML.set_local(1. / self.ML.array()) def assemble_rhs(self): """ Assemble right hand side (form*test*dx) in projection """ if not self.matvec[0] is None: mat, func = self.matvec self.rhs.zero() self.rhs.axpy(1.0, mat*func.vector()) else: assemble(self.bf, tensor=self.rhs) def __call__(self, assemb_rhs=True): """ Compute the projection """ timer = Timer("Projecting {}".format(self.name())) if assemb_rhs: self.assemble_rhs() for bc in self.bcs: bc.apply(self.rhs) if self.method.lower() == "default": self.sol.solve(self.A, self.vector(), self.rhs) else: self.vector()[:] = self.rhs * self.ML
F = rho * inner(a_ * N_ddot(u, u0, a0, v0, dt, beta) \ + b_ * N_dot(u, u0, v0, a0, dt, beta, gamma) + c_ * u, w) * dx \ + inner(N_dot(S, U0, V0, A0, dt, beta, gamma).T * Lambda_e + S.T * Lambda_p, grad(w)) * dx \ - inner(g, w) * ds \ + inner(compliance(a_ * N_ddot(S, U0, A0, V0, dt, beta) + b_ * N_dot(S, U0, V0, A0, dt, beta, gamma) + c_ * S, u, mu, lmbda), T) * dx \ - 0.5 * inner(grad(u) * Lambda_p + Lambda_p * grad(u).T + grad(N_dot(u, u0, v0, a0, dt, beta, gamma)) * Lambda_e \ + Lambda_e * grad(N_dot(u, u0, v0, a0, dt, beta, gamma)).T, T) * dx \ a, L = lhs(F), rhs(F) # Assemble rhs (once) A = assemble(a) # Create GMRES Krylov solver solver = KrylovSolver(A, "gmres") # Create solution function S = Function(W) experiment_count_file = open("experiment_counter", 'rb') experiment_count = pickle.load(experiment_count_file) experiment_count_file.close() paraview_file_name = "experiment_{}".format(experiment_count) info_file_name = "{}_experiments_info/info_n{}.txt".format( type_of_medium, experiment_count) experiment_count += 1 experiment_count_file = open("experiment_counter", 'wb')
class _FEM_Base(object): MAX_ITER = 10000 def __init__(self, mesh_path=None): self.global_preprocessing_time = Stopwatch() self.local_preprocessing_time = Stopwatch() self.solving_time = Stopwatch() if mesh_path is not None: self.PATH = mesh_path logger.debug('Loading mesh...') # with XDMFFile(self.PATH + '.xdmf') as fh: # self._mesh = Mesh() # fh.read(self._mesh) # # with XDMFFile(self.PATH + '_boundaries.xdmf') as fh: # mvc = MeshValueCollection("size_t", self._mesh, 2) # fh.read(mvc, "boundaries") # self._boundaries = cpp.mesh.MeshFunctionSizet(self._mesh, mvc) # # with XDMFFile(self.PATH + '_subdomains.xdmf') as fh: # mvc = MeshValueCollection("size_t", self._mesh, 3) # fh.read(mvc, "subdomains") # self._subdomains = cpp.mesh.MeshFunctionSizet(self._mesh, mvc) self._mesh = Mesh(self.PATH + '.xml') self._subdomains = MeshFunction("size_t", self._mesh, self.PATH + '_physical_region.xml') self._boundaries = MeshFunction("size_t", self._mesh, self.PATH + '_facet_region.xml') logger.debug('Done.') self._degree = None def _change_degree(self, degree, *args, **kwargs): gc.collect() with self.global_preprocessing_time: logger.debug('Creating function space...') self._V = FunctionSpace(self._mesh, "CG", degree) logger.debug('Done. Creating integration subdomains...') self.create_integration_subdomains() logger.debug('Done. Creating test function...') self._v = TestFunction(self._V) logger.debug('Done. Creating potential function...') self._potential_function = Function(self._V) logger.debug('Done. Creating trial function...') self._potential_trial = TrialFunction(self._V) logger.debug('Done. Creating LHS part of equation...') self._a = self._lhs() logger.debug('Done. Assembling linear equation matrix...') self._terms_with_unknown = assemble(self._a) logger.debug('Done. Defining boundary condition...') self._dirichlet_bc = self._boundary_condition(*args, **kwargs) logger.debug('Done. Applying boundary condition to the matrix...') self._dirichlet_bc.apply(self._terms_with_unknown) logger.debug('Done. Creating solver...') self._solver = KrylovSolver("cg", "ilu") self._solver.parameters["maximum_iterations"] = self.MAX_ITER self._solver.parameters["absolute_tolerance"] = 1E-8 logger.debug('Done. Solver created.') self._degree = degree def create_integration_subdomains(self): self._dx = Measure("dx")(subdomain_data=self._subdomains) def __call__(self, degree, *args, **kwargs): if degree != self._degree: self._change_degree(degree, *args, **kwargs) gc.collect() with self.local_preprocessing_time: L = self._rhs(degree, *args, **kwargs) logger.debug('Done. Assembling linear equation vector...') known_terms = assemble(L) logger.debug('Done. Applying boundary condition to the vector...') self._dirichlet_bc.apply(known_terms) logger.debug('Done.') try: logger.debug('Solving linear equation...') gc.collect() with self.solving_time: self._solve(known_terms) logger.debug('Done.') return self._potential_function except RuntimeError as e: self.iterations = self.MAX_ITER logger.warning("Solver failed: {}".format(repr(e))) return None def _rhs(self, degree, *args, **kwargs): logger.debug('Creating CSD expression...') csd = self._make_csd(degree, *args, **kwargs) logger.debug('Done. Normalizing...') self.a = csd.a = self._csd_normalization_factor(csd) logger.debug('Done. Creating RHS part of equation...') return csd * self._v * self._dx def _solve(self, known_terms): self.iterations = self._solver.solve( self._terms_with_unknown, self._potential_function.vector(), known_terms)
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
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