def setup(self): # Set up nonlinear CH solver based on provided linear solver factory = PETScFactory.instance() solver_ch = NewtonSolver(self.comm(), self.data["solver"]["CH"]["lin"], factory) solver_ch.parameters['absolute_tolerance'] = 1E-8 solver_ch.parameters['relative_tolerance'] = 1E-16 solver_ch.parameters['maximum_iterations'] = 25 #solver_ch.parameters['relaxation_parameter'] = 1.0 #solver_ch.parameters['error_on_nonconvergence'] = False #solver_ch.parameters['convergence_criterion'] = 'incremental' assert solver_ch.parameters["linear_solver"] == "user_defined" assert solver_ch.parameters["preconditioner"] == "user_defined" assert "nln" not in self.data["solver"]["CH"] self.data["solver"]["CH"]["nln"] = solver_ch # Set up NS solver if hasattr(self.data["solver"]["NS"], "ksp"): ksp = getattr(self.data["solver"]["NS"], "ksp")() if isinstance(ksp, PCDKSP): forms = self.data["forms"] bcs_ns = self.data["bcs_ns"] bc_pcd = self.data["model"].bcs()["pcd"] self.data["pcd_assembler"] = PCDAssembler( forms["lin"]["lhs"], # A forms["lin"]["rhs"], # b bcs_ns, # bcs forms["pcd"]["a_pc"], # P gp=forms["pcd"]["gp"], # B^T ap=forms["pcd"]["ap"], kp=forms["pcd"]["kp"], mp=forms["pcd"]["mp"], mu=forms["pcd"]["mu"], bcs_pcd=bc_pcd) self.data["pcd_assembler"].get_pcd_form("mp").constant = False self.data["pcd_assembler"].get_pcd_form("mu").constant = False #self.data["pcd_assembler"].get_pcd_form("gp").phantom = False self._flags["init_pcd_called"] = False else: info("") info("'LUSolver' will be applied to the Navier-Stokes subproblem.") info("")
def __init__(self, *args, **kwargs): """ Create nonlinear solver for :py:class:`Monolithic <muflon.functions.discretization.Monolithic>` discretization scheme. See :py:class:`Solver <muflon.solving.solvers.Solver>` for the list of valid initialization arguments. """ super(Monolithic, self).__init__(*args, **kwargs) # Adjust forms DS = self.data["model"].discretization_scheme() w = DS.solution_ctl()[0] F = self.data["forms"]["nln"] J = derivative(F, w) # Adjust bcs bcs = [] _bcs = self.data["model"].bcs() for bc_v in _bcs.get("v", []): assert isinstance(bc_v, tuple) assert len(bc_v) == len(w.sub(1)) bcs += [bc for bc in bc_v if bc is not None] bcs += [bc for bc in _bcs.get("p", [])] # FIXME: Deal with possible bcs for ``phi`` and ``th`` # Adjust solver solver = NewtonSolver() solver.parameters['absolute_tolerance'] = 1E-8 solver.parameters['relative_tolerance'] = 1E-16 solver.parameters['maximum_iterations'] = 10 solver.parameters['linear_solver'] = "mumps" #solver.parameters['relaxation_parameter'] = 1.0 # Preparation for tackling singular systems null_space = None if self._flags["fix_p"]: null_space, null_fcn = DS.build_pressure_null_space() self.data["null_fcn"] = null_fcn # Store solvers and collect other data self.data["solver"] = solver self.data["problem"] = Monolithic.Problem(F, bcs, J, null_space) self.data["sol_fcn"] = w
# .. index:: # single: Newton solver; (in Cahn-Hilliard demo) # # The DOLFIN Newton solver requires a # :py:class:`NonlinearProblem<dolfin.cpp.NonlinearProblem>` object to # solve a system of nonlinear equations. Here, we are using the class # ``CahnHilliardEquation``, which was declared at the beginning of the # file, and which is a sub-class of # :py:class:`NonlinearProblem<dolfin.cpp.NonlinearProblem>`. We need to # instantiate objects of both ``CahnHilliardEquation`` and # :py:class:`NewtonSolver <dolfin.cpp.NewtonSolver>`:: # Create nonlinear problem and Newton solver problem = CahnHilliardEquation(a, L) solver = NewtonSolver(MPI.comm_world) solver.convergence_criterion = "incremental" solver.rtol = 1e-6 # The setting of ``convergence_criterion`` to ``"incremental"`` specifies # that the Newton solver should compute a norm of the solution increment # to check for convergence (the other possibility is to use # ``"residual"``, or to provide a user-defined check). The tolerance for # convergence is specified by ``rtol``. # # To run the solver and save the output to a VTK file for later # visualization, the solver is advanced in time from :math:`t_{n}` to # :math:`t_{n+1}` until a terminal time :math:`T` is reached:: # Output file file = XDMFFile(MPI.comm_world, "output.xdmf")
def compute_tentative_velocity( time_step_method, rho, mu, u, p0, dt, u_bcs, f, W, my_dx, tol ): """Compute the tentative velocity via .. math:: \\rho (u_0 + (u\\cdot\\nabla)u) = \\mu \\frac{1}{r} \\div(r \\nabla u) + \\rho g. """ class TentativeVelocityProblem(NonlinearProblem): def __init__(self, ui, time_step_method, rho, mu, u, p0, dt, bcs, f, my_dx): super(TentativeVelocityProblem, self).__init__() W = ui.function_space() v = TestFunction(W) self.bcs = bcs r = SpatialCoordinate(ui.function_space().mesh())[0] def me(uu, ff): return _momentum_equation(uu, v, p0, ff, rho, mu, my_dx) self.F0 = rho * dot(ui - u[0], v) / dt * 2 * pi * r * my_dx if time_step_method == "forward euler": self.F0 += me(u[0], f[0]) elif time_step_method == "backward euler": self.F0 += me(ui, f[1]) else: assert ( time_step_method == "crank-nicolson" ), "Unknown time stepper '{}'".format( time_step_method ) self.F0 += 0.5 * (me(u[0], f[0]) + me(ui, f[1])) self.jacobian = derivative(self.F0, ui) self.reset_sparsity = True return # pylint: disable=unused-argument def F(self, b, x): # We need to evaluate F at x, so we have to make sure that self.F0 # is assembled for ui=x. We could use a self.ui and set # # self.ui.vector()[:] = x # # here. One way around this copy is to instantiate this class with # the same Function ui that is then used for the solver.solve(). assemble(self.F0, tensor=b, form_compiler_parameters={"optimize": True}) for bc in self.bcs: bc.apply(b, x) return def J(self, A, x): # We can ignore x; see comment at F(). assemble( self.jacobian, tensor=A, form_compiler_parameters={"optimize": True} ) for bc in self.bcs: bc.apply(A) self.reset_sparsity = False return solver = NewtonSolver() solver.parameters["maximum_iterations"] = 10 solver.parameters["absolute_tolerance"] = tol solver.parameters["relative_tolerance"] = 0.0 solver.parameters["report"] = True # While GMRES+ILU converges if the time step is small enough, increasing # the time step slows down convergence dramatically in some cases. This # makes the step fail, and the adaptive time stepper will decrease the step # size. This size can be _very_ small such that simulation take forever. # For now, just use a direct solver. Choose UMFPACK over SuperLU since the # docker image doesn't contain SuperLU yet, cf. # <https://bitbucket.org/fenics-project/docker/issues/64/add-superlu>. # TODO come up with an appropriate GMRES preconditioner here solver.parameters["linear_solver"] = "umfpack" ui = Function(W) step_problem = TentativeVelocityProblem( ui, time_step_method, rho, mu, u, p0, dt, u_bcs, f, my_dx ) # Take u[0] as initial guess. ui.assign(u[0]) solver.solve(step_problem, ui.vector()) # Make sure ui is from W. This should happen anyways, but somehow doesn't. # TODO find out why not ui = project(ui, W) # div_u = 1/r * div(r*ui) return ui
def step(self, dt, u1, p1, u, p0, u_bcs, p_bcs, f0=None, f1=None, verbose=True, tol=1.0e-10 ): '''General pressure projection scheme as described in section 3.4 of :cite:`GMS06`. ''' # Some initial sanity checkups. assert dt > 0.0 # Define coefficients k = Constant(dt) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Compute tentative velocity step. # rho (u[-1] + (u.\nabla)u) = mu 1/r \div(r \nabla u) + rho g. with Message('Computing tentative velocity'): solver = NewtonSolver() solver.parameters['maximum_iterations'] = 5 solver.parameters['absolute_tolerance'] = tol solver.parameters['relative_tolerance'] = 0.0 solver.parameters['report'] = True # The nonlinear term makes the problem generally nonsymmetric. solver.parameters['linear_solver'] = 'gmres' # If the nonsymmetry is too strong, e.g., if u[-1] is large, then # AMG preconditioning might not work very well. # Use HYPRE-Euclid instead of ILU for parallel computation. solver.parameters['preconditioner'] = 'hypre_euclid' solver.parameters['krylov_solver']['relative_tolerance'] = tol solver.parameters['krylov_solver']['absolute_tolerance'] = 0.0 solver.parameters['krylov_solver']['maximum_iterations'] = 1000 solver.parameters['krylov_solver']['monitor_convergence'] = verbose ui = Function(self.W) step_problem = \ TentativeVelocityProblem(ui, self.theta, self.rho, self.mu, u, p0, k, u_bcs, f0, f1, stabilization=self.stabilization, dx=self.dx ) # Take u[-1] as initial guess. ui.assign(u[-1]) solver.solve(step_problem, ui.vector()) #div_u = 1/r * div(r*ui) #plot(div_u) #interactive() # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - with Message('Computing pressure correction'): # The pressure correction is based on the update formula # # ( dphi/dr ) # rho/dt (u_{n+1}-u*) + ( dphi/dz ) = 0 # (1/r dphi/dtheta) # # with # # phi = p_{n+1} - p* # # and # # 1/r d/dr (r ur_{n+1}) # + d/dz ( uz_{n+1}) # + 1/r d/dtheta( utheta_{n+1}) = 0 # # With the assumption that u does not change in the direction # theta, one derives # # - 1/r * div(r * \nabla phi) = 1/r * rho/dt div(r*(u_{n+1} - u*)) # - 1/r * n. (r * \nabla phi) = 1/r * rho/dt n.(r*(u_{n+1} - u*)) # # In its weak form, this is # # \int r * \grad(phi).\grad(q) *2*pi = # - rho/dt \int div(r*u*) q *2*p # - rho/dt \int_Gamma n.(r*(u_{n+1}-u*)) q *2*pi. # # (The terms 1/r cancel with the volume elements 2*pi*r.) # If Dirichlet boundary conditions are applied to both u* and u_n # (the latter in the final step), the boundary integral vanishes. # alpha = 1.0 #alpha = 3.0 / 2.0 rotational_form = False self._pressure_poisson(p1, p0, self.mu, ui, self.rho * alpha * ui / k, p_bcs=p_bcs, rotational_form=rotational_form, tol=tol, verbose=verbose ) #plot(ui, title='u intermediate') ##plot(f, title='f') ##plot(ui[1], title='u intermediate[1]') #plot(div(ui), title='div(u intermediate)') #plot(p1, title='p1') #interactive() # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Velocity correction. # U = u[-1] - dt/rho \nabla (p1-p0). with Message('Computing velocity correction'): u = TrialFunction(self.W) v = TestFunction(self.W) a3 = dot(u, v) * dx phi = Function(self.P) phi.assign(p1) if p0: phi -= p0 if rotational_form: r = Expression('x[0]', degree=1, domain=self.W.mesh()) div_ui = 1/r * (r * ui[0]).dx(0) + ui[1].dx(1) phi += self.mu * div_ui L3 = dot(ui, v) * dx \ - k / self.rho * (phi.dx(0) * v[0] + phi.dx(1) * v[1]) * dx # Jacobi preconditioning for mass matrix is order-optimal. solve( a3 == L3, u1, bcs=u_bcs, solver_parameters={ 'linear_solver': 'iterative', 'symmetric': True, 'preconditioner': 'jacobi', 'krylov_solver': {'relative_tolerance': tol, 'absolute_tolerance': 0.0, 'maximum_iterations': 100, 'monitor_convergence': verbose} } ) #u = project(ui - k/rho * grad(phi), V) # div_u = 1/r * div(r*u) r = Expression('x[0]', degree=1, domain=self.W.mesh()) div_u1 = 1.0 / r * (r * u1[0]).dx(0) + u1[1].dx(1) info('||u||_div = %e' % sqrt(assemble(div_u1 * div_u1 * dx))) #plot(div_u) #interactive() ## Ha! TODO remove #u1.vector()[:] = ui.vector() return