11import numpy as np
22import scipy .sparse as sp
3- from scipy .sparse .linalg import spsolve
3+ from scipy .sparse .linalg import spsolve , gmres , inv
44
55from pySDC .core .Errors import ParameterError , ProblemError
6- from pySDC .core .Problem import ptype
6+ from pySDC .core .Problem import ptype , WorkCounter
77from pySDC .helpers import problem_helper
88from pySDC .implementations .datatype_classes .mesh import mesh , imex_mesh
99
@@ -46,6 +46,9 @@ def __init__(self, problem_params, dtype_u=mesh, dtype_f=mesh):
4646 'nvars' : 2 ** 7 ,
4747 'newton_tol' : 1e-8 ,
4848 'newton_iter' : 99 ,
49+ 'lintol' : 1e-8 ,
50+ 'liniter' : 99 ,
51+ 'direct_solver' : True ,
4952 }
5053
5154 for key in problem_params .keys ():
@@ -92,7 +95,10 @@ def __init__(self, problem_params, dtype_u=mesh, dtype_f=mesh):
9295
9396 self .leak = np .logical_and (self .xv > self .params .leak_range [0 ], self .xv < self .params .leak_range [1 ])
9497
95- self .total_newton_iter = 0 # store here how many iterations you needed for the Newton solver over the entire run to extract the desired information in the hooks
98+ self .work_counters ['newton' ] = WorkCounter ()
99+ self .work_counters ['rhs' ] = WorkCounter ()
100+ if not self .params .direct_solver :
101+ self .work_counters ['linear' ] = WorkCounter ()
96102
97103 def eval_f_non_linear (self , u , t ):
98104 """
@@ -136,6 +142,7 @@ def eval_f(self, u, t):
136142 """
137143 f = self .dtype_f (self .init )
138144 f [:] = self .A .dot (u .flatten ()).reshape (self .params .nvars ) + self .eval_f_non_linear (u , t )
145+ self .work_counters ['rhs' ]()
139146 return f
140147
141148 def solve_system (self , rhs , factor , u0 , t ):
@@ -182,9 +189,20 @@ def get_non_linear_Jacobian(u):
182189
183190 u = self .dtype_u (u0 )
184191 res = np .inf
192+ delta = np .zeros_like (u )
193+
194+ # construct a preconditioner for the space solver
195+ if not self .params .direct_solver :
196+ M = inv (self .Id - factor * self .A )
197+
185198 for n in range (0 , self .params .newton_iter ):
186199 # assemble G such that G(u) = 0 at the solution of the step
187200 G = u - factor * self .eval_f (u , t ) - rhs
201+ self .work_counters [
202+ 'rhs'
203+ ].niter -= (
204+ 1 # Work regarding construction of the Jacobian etc. should count into the Newton iterations only
205+ )
188206
189207 res = np .linalg .norm (G , np .inf )
190208 if res <= self .params .newton_tol and n > 0 : # we want to make at least one Newton iteration
@@ -194,15 +212,27 @@ def get_non_linear_Jacobian(u):
194212 J = self .Id - factor * (self .A + get_non_linear_Jacobian (u ))
195213
196214 # solve the linear system
197- delta = spsolve (J , G )
215+ if self .params .direct_solver :
216+ delta = spsolve (J , G )
217+ else :
218+ delta , info = gmres (
219+ J ,
220+ G ,
221+ x0 = delta ,
222+ M = M ,
223+ tol = self .params .lintol ,
224+ maxiter = self .params .liniter ,
225+ atol = 0 ,
226+ callback = self .work_counters ['linear' ],
227+ )
198228
199229 if not np .isfinite (delta ).all ():
200230 break
201231
202232 # update solution
203233 u = u - delta
204234
205- self . total_newton_iter += n
235+ self . work_counters [ 'newton' ]()
206236
207237 return u
208238
@@ -258,6 +288,7 @@ def eval_f(self, u, t):
258288 f .impl [:] = self .A .dot (u .flatten ()).reshape (self .params .nvars )
259289 f .expl [:] = self .eval_f_non_linear (u , t )
260290
291+ self .work_counters ['rhs' ]()
261292 return f
262293
263294 def solve_system (self , rhs , factor , u0 , t ):
0 commit comments