1 // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi: set et ts=4 sw=2 sts=2:
3 #ifndef DUNE_PDELAB_SOLVER_NEWTON_HH
4 #define DUNE_PDELAB_SOLVER_NEWTON_HH
5 
6 #include <dune/common/exceptions.hh>
7 #include <dune/common/ios_state.hh>
8 
9 #include <dune/pdelab/constraints/common/constraints.hh>
10 #include <dune/pdelab/solver/newtonerrors.hh>
11 #include <dune/pdelab/solver/linesearch.hh>
12 #include <dune/pdelab/solver/terminate.hh>
13 #include <dune/pdelab/solver/utility.hh>
14 
15 namespace Dune::PDELab
16 {
17   namespace Impl
18   {
19     // Some SFinae magic to execute setReuse(bool) on a backend
20     template<typename T1, typename = void>
21     struct HasSetReuse
22       : std::false_type
23     {};
24 
25     template<typename T>
26     struct HasSetReuse<T, decltype(std::declval<T>().setReuse(true), void())>
27       : std::true_type
28     {};
29 
30     template<typename T>
setLinearSystemReuse(T & solver_backend,bool reuse,std::true_type)31     inline void setLinearSystemReuse(T& solver_backend, bool reuse, std::true_type)
32     {
33       if (!solver_backend.getReuse() && reuse)
34         dwarn << "WARNING: Newton needed to override your choice to reuse the linear system in order to work!" << std::endl;
35     solver_backend.setReuse(reuse);
36     }
37 
38     template<typename T>
setLinearSystemReuse(T &,bool,std::false_type)39     inline void setLinearSystemReuse(T&, bool, std::false_type)
40     {}
41 
42     template<typename T>
setLinearSystemReuse(T & solver_backend,bool reuse)43     inline void setLinearSystemReuse(T& solver_backend, bool reuse)
44     {
45       setLinearSystemReuse(solver_backend, reuse, HasSetReuse<T>());
46     }
47   }
48 
49 
50   /** \brief Newton solver for solving non-linear problems
51    *
52    * - The line search and the termination criterion can be changed at runtime
53    *   by the setTerminate() and the setLineSearch() methods.
54    *
55    * - If Newton is created using the default parameters it is an inexact
56    *   Newton since the default reduction for the linear systems is quite
57    *   low. You can change this through setMinLinearReduction()
58    *
59    * \tparam GridOperator_ Grid operator for evaluation of residual and Jacobian
60    * \tparam LinearSolver_ Solver backend for solving linear system of equations
61    */
62   template <typename GridOperator_, typename LinearSolver_>
63   class NewtonMethod
64   {
65   public:
66     //! Type of the grid operator
67     using GridOperator = GridOperator_;
68 
69     //! Type of the linear solver
70     using LinearSolver = LinearSolver_;
71 
72     //! Type of the domain (solution)
73     using Domain = typename GridOperator::Traits::Domain;
74 
75     //! Type of the range (residual)
76     using Range = typename GridOperator::Traits::Range;
77 
78     //! Type of the Jacobian matrix
79     using Jacobian = typename GridOperator::Traits::Jacobian;
80 
81     //! Number type
82     using Real = typename Dune::FieldTraits<typename Domain::ElementType>::real_type;
83 
84     //! Type of results
85     using Result = PDESolverResult<Real>;
86 
87     //! Type of line search interface
88     using LineSearch = LineSearchInterface<Domain>;
89 
90     //! Return results
result() const91     const Result& result() const
92     {
93       if (not _resultValid)
94         DUNE_THROW(NewtonError, "NewtonMethod::result() called before NewtonMethod::solve()");
95       return _result;
96     }
97 
prepareStep(Domain & solution)98     virtual void prepareStep(Domain& solution)
99     {
100       _reassembled = false;
101       if (_result.defect/_previousDefect > _reassembleThreshold){
102         if (_hangingNodeModifications){
103           auto dirichletValues = solution;
104           // Set all non dirichlet values to zero
105           Dune::PDELab::set_shifted_dofs(_gridOperator.localAssembler().trialConstraints(), 0.0, dirichletValues);
106           // Set all constrained DOFs to zero in solution
107           Dune::PDELab::set_constrained_dofs(_gridOperator.localAssembler().trialConstraints(), 0.0, solution);
108           // Copy correct Dirichlet values back into solution vector
109           Dune::PDELab::copy_constrained_dofs(_gridOperator.localAssembler().trialConstraints(), dirichletValues, solution);
110           // Interpolate periodic constraints / hanging nodes
111           _gridOperator.localAssembler().backtransform(solution);
112         }
113         if constexpr (!linearSolverIsMatrixFree<LinearSolver>()){
114           if (_verbosity>=3)
115             std::cout << "      Reassembling matrix..." << std::endl;
116           *_jacobian = Real(0.0);
117           _gridOperator.jacobian(solution, *_jacobian);
118           _reassembled = true;
119         }
120       }
121 
122       _linearReduction = _minLinearReduction;
123       // corner case: ForceIteration==true. Use _minLinearReduction when initial defect is less than AbsoluteLimit.
124       if (not _fixedLinearReduction && !(_result.iterations==0 && _result.first_defect<_absoluteLimit)){
125         // Determine maximum defect, where Newton is converged.
126         using std::min;
127         using std::max;
128         auto stop_defect = max(_result.first_defect*_reduction, _absoluteLimit);
129 
130         // To achieve second order convergence of newton we need a linear
131         // reduction of at least current_defect^2/prev_defect^2.  For the
132         // last newton step a linear reduction of
133         // 1/10*end_defect/current_defect is sufficient for convergence.
134         _linearReduction =
135           max( stop_defect/(10*_result.defect),
136             min(_minLinearReduction, _result.defect*_result.defect/(_previousDefect*_previousDefect)) );
137         }
138 
139         if (_verbosity >= 3)
140           std::cout << "      requested linear reduction:       "
141                     << std::setw(12) << std::setprecision(4) << std::scientific
142                     << _linearReduction << std::endl;
143     }
144 
linearSolve()145     virtual void linearSolve()
146     {
147       if (_verbosity >= 4)
148         std::cout << "      Solving linear system..." << std::endl;
149 
150       if constexpr (!linearSolverIsMatrixFree<LinearSolver>()){
151         // If the jacobian was not reassembled we might save some work in the solver backend
152         Impl::setLinearSystemReuse(_linearSolver, not _reassembled);
153       }
154 
155       // Solve the linear system
156       _correction = 0.0;
157       if constexpr (linearSolverIsMatrixFree<LinearSolver>()){
158         _linearSolver.apply(_correction, _residual, _linearReduction);
159       }
160       else{
161         _linearSolver.apply(*_jacobian, _correction, _residual, _linearReduction);
162       }
163 
164       if (not _linearSolver.result().converged)
165         DUNE_THROW(NewtonLinearSolverError,
166                    "NewtonMethod::linearSolve(): Linear solver did not converge "
167                    "in " << _linearSolver.result().iterations << " iterations");
168       if (_verbosity >= 4)
169         std::cout << "          linear solver iterations:     "
170                   << std::setw(12) << _linearSolver.result().iterations << std::endl
171                   << "          linear defect reduction:      "
172                   << std::setw(12) << std::setprecision(4) << std::scientific
173                   << _linearSolver.result().reduction << std::endl;
174     }
175 
176     //! Solve the nonlinear problem using solution as initial guess and for storing the result
apply(Domain & solution)177     virtual void apply(Domain& solution)
178     {
179       // Reset solver statistics
180       _result.clear();
181       _resultValid = true;
182 
183       // Store old ios flags (will be reset when this goes out of scope)
184       ios_base_all_saver restorer(std::cout);
185 
186       // Prepare time measuring
187       using Clock = std::chrono::steady_clock;
188       using Duration = Clock::duration;
189       auto assembler_time = Duration::zero();
190       auto linear_solver_time = Duration::zero();
191       auto line_search_time   = Duration::zero();
192       auto to_seconds =
193         [](Duration duration){
194           return std::chrono::duration<double>(duration).count();
195         };
196       auto start_solve = Clock::now();
197 
198       //=========================
199       // Calculate initial defect
200       //=========================
201       updateDefect(solution);
202       _result.first_defect = _result.defect;
203       _previousDefect = _result.defect;
204 
205       if (_verbosity >= 2)
206         std::cout << "  Initial defect: "
207                   << std::setw(12) << std::setprecision(4) << std::scientific
208                   << _result.defect << std::endl;
209 
210       //==========================
211       // Calculate Jacobian matrix
212       //==========================
213       if constexpr (!linearSolverIsMatrixFree<LinearSolver>()){
214         if (not _jacobian)
215           _jacobian = std::make_shared<Jacobian>(_gridOperator);
216       }
217 
218       //=========================
219       // Nonlinear iteration loop
220       //=========================
221       while (not _terminate->terminate()){
222         if(_verbosity >= 3)
223           std::cout << "  Newton iteration " << _result.iterations
224                     << " --------------------------------" << std::endl;
225 
226         //=============
227         // Prepare step
228         //=============
229         auto start = Clock::now();
230         try{
231           prepareStep(solution);
232         }
233         catch (...)
234         {
235           // Keep track of statistics when the method fails. We record
236           // independently the time spent in non-converging attempts.
237           // Check OneStepMethod to see how these data are propagated.
238           auto end = Clock::now();
239           assembler_time += end-start;
240           _result.assembler_time = to_seconds(assembler_time);
241           throw;
242         }
243         auto end = Clock::now();
244         assembler_time += end -start;
245         _result.assembler_time = to_seconds(assembler_time);
246 
247         // Store defect
248         _previousDefect = _result.defect;
249 
250         //====================
251         // Solve linear system
252         //====================
253         start = Clock::now();
254 
255         // Important: In the matrix-free case we need to set the current linearization point!
256         if constexpr (linearSolverIsMatrixFree<LinearSolver>()){
257           _linearSolver.setLinearizationPoint(solution);
258         }
259         try{
260           linearSolve();
261         }
262         catch (...)
263         {
264           // Separately catch statistics for linear solver failures.
265           end = Clock::now();
266           linear_solver_time += end-start;
267           _result.linear_solver_time = to_seconds(linear_solver_time);
268           _result.linear_solver_iterations = _linearSolver.result().iterations;
269           throw;
270         }
271         end = Clock::now();
272         linear_solver_time += end -start;
273         _result.linear_solver_time = to_seconds(linear_solver_time);
274         _result.linear_solver_iterations = _linearSolver.result().iterations;
275 
276         //===================================
277         // Do line search and update solution
278         //===================================
279         start = Clock::now();
280         _lineSearch->lineSearch(solution, _correction);
281         // lineSearch(solution);
282         end = Clock::now();
283         line_search_time += end -start;
284 
285         //========================================
286         // Store statistics and create some output
287         //========================================
288         _result.reduction = _result.defect/_result.first_defect;
289         _result.iterations++;
290         _result.conv_rate = std::pow(_result.reduction, 1.0/_result.iterations);
291 
292         if (_verbosity >= 3)
293           std::cout << "      linear solver time:               "
294                     << std::setw(12) << std::setprecision(4) << std::scientific
295                     << to_seconds(linear_solver_time) << std::endl
296                     << "      defect reduction (this iteration):"
297                     << std::setw(12) << std::setprecision(4) << std::scientific
298                     << _result.defect/_previousDefect << std::endl
299                     << "      defect reduction (total):         "
300                     << std::setw(12) << std::setprecision(4) << std::scientific
301                     << _result.reduction << std::endl
302                     << "      new defect:                       "
303                     << std::setw(12) << std::setprecision(4) << std::scientific
304                     << _result.defect << std::endl;
305         if (_verbosity == 2)
306           std::cout << "  Newton iteration "
307                     << std::setw(2)
308                     << _result.iterations
309                     << ".  New defect: "
310                     << std::setw(12) << std::setprecision(4) << std::scientific
311                     << _result.defect
312                     << ".  Reduction (this): "
313                     << std::setw(12) << std::setprecision(4) << std::scientific
314                     << _result.defect/_previousDefect
315                     << ".  Reduction (total): "
316                     << std::setw(12) << std::setprecision(4) << std::scientific
317                     << _result.reduction
318                     << std::endl;
319       } // end while loop of nonlinear iterations
320 
321       auto end_solve = Clock::now();
322       auto solve_time = end_solve - start_solve;
323       _result.elapsed = to_seconds(solve_time);
324 
325       if (_verbosity == 1)
326         std::cout << "  Newton converged after "
327                   << std::setw(2)
328                   << _result.iterations
329                   << " iterations.  Reduction: "
330                   << std::setw(12) << std::setprecision(4) << std::scientific
331                   << _result.reduction
332                   << "   (" << std::setprecision(4) << _result.elapsed << "s)"
333                   << std::endl;
334 
335       if constexpr (!linearSolverIsMatrixFree<LinearSolver>()){
336         if (not _keepMatrix)
337           _jacobian.reset();
338       }
339     }
340 
341     //! Update _residual and defect in _result
updateDefect(Domain & solution)342     virtual void updateDefect(Domain& solution)
343     {
344       if (_hangingNodeModifications){
345         auto dirichletValues = solution;
346         // Set all non dirichlet values to zero
347         Dune::PDELab::set_shifted_dofs(_gridOperator.localAssembler().trialConstraints(), 0.0, dirichletValues);
348         // Set all constrained DOFs to zero in solution
349         Dune::PDELab::set_constrained_dofs(_gridOperator.localAssembler().trialConstraints(), 0.0, solution);
350         // Copy correct Dirichlet values back into solution vector
351         Dune::PDELab::copy_constrained_dofs(_gridOperator.localAssembler().trialConstraints(), dirichletValues, solution);
352         // Interpolate periodic constraints / hanging nodes
353         _gridOperator.localAssembler().backtransform(solution);
354       }
355 
356       _residual = 0.0;
357       _gridOperator.residual(solution, _residual);
358 
359       // Use the maximum norm as a stopping criterion. This helps loosen the tolerance
360       // when solving for stationary solutions of nonlinear time-dependent problems.
361       // The default is to use the Euclidean norm (in the else-block) as before
362       if (_useMaxNorm){
363         auto rankMax = Backend::native(_residual).infinity_norm();
364         _result.defect = _gridOperator.testGridFunctionSpace().gridView().comm().max(rankMax);
365       }
366       else
367         _result.defect =  _linearSolver.norm(_residual);
368     }
369 
370     //! Set how much output you get
setVerbosityLevel(unsigned int verbosity)371     void setVerbosityLevel(unsigned int verbosity)
372     {
373       if (_gridOperator.trialGridFunctionSpace().gridView().comm().rank()>0)
374         _verbosity = 0;
375       else
376         _verbosity = verbosity;
377     }
378 
379     //! Get verbosity level
getVerbosityLevel() const380     unsigned int getVerbosityLevel() const
381     {
382       return _verbosity;
383     }
384 
385     //! Set reduction Newton needs to achieve
setReduction(Real reduction)386     void setReduction(Real reduction)
387     {
388       _reduction = reduction;
389     }
390 
391     //! Get reduction
getReduction() const392     Real getReduction() const
393     {
394       return _reduction;
395     }
396 
397     //! Set absolute convergence limit
setAbsoluteLimit(Real absoluteLimit)398     void setAbsoluteLimit(Real absoluteLimit)
399     {
400       _absoluteLimit = absoluteLimit;
401     }
402 
getAbsoluteLimit() const403     Real getAbsoluteLimit() const
404     {
405       return _absoluteLimit;
406     }
407 
408     //! Set whether the jacobian matrix should be kept across calls to apply().
setKeepMatrix(bool b)409     void setKeepMatrix(bool b)
410     {
411       _keepMatrix = b;
412     }
413 
414     //! Set whether to use the maximum norm for stopping criteria.
setUseMaxNorm(bool b)415     void setUseMaxNorm(bool b)
416     {
417       _useMaxNorm = b;
418     }
419 
420     //! Does the problem have hanging nodes
setHangingNodeModifications(bool b)421     void setHangingNodeModifications(bool b)
422     {
423       _hangingNodeModifications = b;
424     }
425 
426 
427     //! Return whether the jacobian matrix is kept across calls to apply().
keepMatrix() const428     bool keepMatrix() const
429     {
430       return _keepMatrix;
431     }
432 
433     //! Discard the stored Jacobian matrix.
discardMatrix()434     void discardMatrix()
435     {
436       if(_jacobian)
437         _jacobian.reset();
438     }
439 
440     /**\brief Set the minimal reduction in the linear solver
441      *
442      * \note with minLinearReduction > 0, the linear reduction will be
443      * determined as mininum of the minLinearReduction and the linear reduction
444      * needed to achieve second order Newton convergence. (As long as you are
445      * not using a fixed linear reduction)
446      */
setMinLinearReduction(Real minLinearReduction)447     void setMinLinearReduction(Real minLinearReduction)
448     {
449       _minLinearReduction = minLinearReduction;
450     }
451 
452     /** \brief Set wether to use a fixed reduction in the linear solver
453      *
454      * \note If fixedLinearReduction is true, the linear reduction rate will
455      *  always be fixed to minLinearReduction.
456      */
setFixedLinearReduction(bool fixedLinearReduction)457     void setFixedLinearReduction(bool fixedLinearReduction)
458     {
459       _fixedLinearReduction = fixedLinearReduction;
460     }
461 
462     /** \brief Set a threshold, when the linear operator is reassembled
463      *
464      * We allow to keep the linear operator over several newton iterations. If
465      * the reduction in the newton drops below a given threshold the linear
466      * operator is reassembled to ensure convergence.
467      */
setReassembleThreshold(Real reassembleThreshold)468     void setReassembleThreshold(Real reassembleThreshold)
469     {
470       _reassembleThreshold = reassembleThreshold;
471     }
472 
473     /** \brief Interpret a parameter tree as a set of options for the newton solver
474      *
475      *  Possible parameters:
476      *
477      *  example configuration:
478      *
479      *  \code
480      *  [newton_parameters]
481      *  ReassembleThreshold = 0.1
482      *  AbsoluteLimit = 1e-6
483      *  Reduction = 1e-4
484      *  MinLinearReduction = 1e-3
485      *  MaxIterations = 15
486      *  LineSearchDampingFactor = 0.7
487      *  \endcode
488      *
489      *  and invocation in the code:
490      *  \code
491      *  newton.setParameters(param.sub("NewtonParameters"));
492      *  \endcode
493      *
494      *  This can also be used to set single parameters like this
495      *
496      *  \code
497      *  Dune::ParameterTree ptree;
498      *  ptree["verbosity"] = "4";
499      *  newton.setParameters(ptree);
500      *  \endcode
501      */
setParameters(const ParameterTree & parameterTree)502     void setParameters(const ParameterTree& parameterTree){
503       _verbosity = parameterTree.get("VerbosityLevel", _verbosity);
504       _reduction = parameterTree.get("Reduction", _reduction);
505       _absoluteLimit = parameterTree.get("AbsoluteLimit", _absoluteLimit);
506       _keepMatrix = parameterTree.get("KeepMatrix", _keepMatrix);
507       _useMaxNorm = parameterTree.get("UseMaxNorm", _useMaxNorm);
508       _hangingNodeModifications = parameterTree.get("HangingNodeModifications", _hangingNodeModifications);
509       _minLinearReduction = parameterTree.get("MinLinearReduction", _minLinearReduction);
510       _fixedLinearReduction = parameterTree.get("FixedLinearReduction", _fixedLinearReduction);
511       _reassembleThreshold = parameterTree.get("ReassembleThreshold", _reassembleThreshold);
512 
513       // first create the linesearch, depending on the parameter
514       std::string lineSearchStrategy = parameterTree.get("LineSearchStrategy","hackbuschReusken");
515       auto strategy = lineSearchStrategyFromString(lineSearchStrategy);
516       _lineSearch = createLineSearch(*this, strategy);
517 
518       // now set parameters
519       if (parameterTree.hasSub("Terminate")){
520         _terminate->setParameters(parameterTree.sub("Terminate"));
521       }
522       else{
523         ParameterTree terminateTree;
524         terminateTree["MaxIterations"] = std::to_string(parameterTree.get("MaxIterations", 40));
525         terminateTree["ForceIteration"] = std::to_string(parameterTree.get("ForceIteration", false));
526         _terminate->setParameters(terminateTree);
527       }
528       if (parameterTree.hasSub("LineSearch")){
529         _lineSearch->setParameters(parameterTree.sub("LineSearch"));
530       }
531       else{
532         ParameterTree lineSearchTree;
533         lineSearchTree["MaxIterations"] = std::to_string(parameterTree.get("LineSearchMaxIterations", 10));
534         lineSearchTree["DampingFactor"] = std::to_string(parameterTree.get("LineSearchDampingFactor", 0.5));
535         lineSearchTree["AcceptBest"] = std::to_string(parameterTree.get("LineSearchAcceptBest", false));
536         _lineSearch->setParameters(lineSearchTree);
537       }
538     }
539 
540     //! Set the termination criterion
setTerminate(std::shared_ptr<TerminateInterface> terminate)541     void setTerminate(std::shared_ptr<TerminateInterface> terminate)
542     {
543       _terminate = terminate;
544     }
545 
546     //! Return a pointer to the stored termination criterion
getTerminate() const547     std::shared_ptr<TerminateInterface> getTerminate() const
548     {
549       return _terminate;
550     }
551 
552     /**\brief Set the line search
553      *
554      * See getLineSearch() for already implemented line searches
555      */
setLineSearch(std::shared_ptr<LineSearch> lineSearch)556     void setLineSearch(std::shared_ptr<LineSearch> lineSearch)
557     {
558       _lineSearch = lineSearch;
559     }
560 
561     //! Return a pointer to the stored line search
getLineSearch() const562     std::shared_ptr<LineSearch> getLineSearch() const
563     {
564       return _lineSearch;
565     }
566 
567     /**\brief Output NewtonMethod parameters
568      *
569      * Setting parameters using ParameterTree is quite error prone.
570      * Checking parameters without setting up the debugger can be useful.
571      */
printParameters(const std::string & _name="NewtonMethod") const572     void printParameters(const std::string& _name = "NewtonMethod") const
573     {
574       // Change boolalpha flag to output true/false in full, not 0/1.
575       // Restoring to previous setting is guaranteed.
576       auto ioflags = std::cout.flags();
577       std::cout.setf(std::ios_base::boolalpha);
578       std::cout << _name << " parameters:\n";
579       std::cout << "Verbosity............... " << _verbosity << std::endl;
580       std::cout << "Reduction............... " << _reduction << std::endl;
581       std::cout << "AbsoluteLimit........... " << _absoluteLimit << std::endl;
582       std::cout << "KeepMatrix.............. " << _keepMatrix << std::endl;
583       std::cout << "UseMaxNorm.............. " << _useMaxNorm << std::endl;
584       std::cout << "MinLinearReduction...... " << _minLinearReduction << std::endl;
585       std::cout << "FixedLinearReduction.... " << _fixedLinearReduction << std::endl;
586       std::cout << "ReassembleThreshold..... " << _reassembleThreshold << std::endl;
587       std::cout << "HangingNodeModifications " << _hangingNodeModifications << std::endl;
588       try
589       {
590         _terminate->printParameters();
591         _lineSearch->printParameters();
592       }
593       catch (...)
594       {
595         // guarantee restoring previous std::cout flags
596         std::cout.flags(ioflags);
597         throw;
598       }
599       std::cout.flags(ioflags);
600     }
601 
602     //! Construct Newton using default parameters with default parameters
603     /**
604        in p
605      */
NewtonMethod(const GridOperator & gridOperator,LinearSolver & linearSolver)606     NewtonMethod(
607       const GridOperator& gridOperator,
608       LinearSolver& linearSolver)
609       : _gridOperator(gridOperator)
610       , _linearSolver(linearSolver)
611       , _residual(gridOperator.testGridFunctionSpace())
612       , _correction(gridOperator.trialGridFunctionSpace())
613     {
614       _terminate = std::make_shared<DefaultTerminate<NewtonMethod>> (*this);
615       _lineSearch = createLineSearch(*this, LineSearchStrategy::hackbuschReusken);
616     }
617 
618     //! Construct Newton passing a parameter tree
NewtonMethod(const GridOperator & gridOperator,LinearSolver & linearSolver,const ParameterTree & parameterTree)619     NewtonMethod(
620       const GridOperator& gridOperator,
621       LinearSolver& linearSolver,
622       const ParameterTree& parameterTree)
623       : _gridOperator(gridOperator)
624       , _linearSolver(linearSolver)
625       , _residual(gridOperator.testGridFunctionSpace())
626       , _correction(gridOperator.trialGridFunctionSpace())
627 
628     {
629       _terminate = std::make_shared<DefaultTerminate<NewtonMethod>> (*this);
630       setParameters(parameterTree);
631     }
632 
~NewtonMethod()633     virtual ~NewtonMethod() {}
634 
635   private:
636     const GridOperator& _gridOperator;
637     LinearSolver& _linearSolver;
638 
639     // Vectors and Jacobi matrix we set up only once
640     Range _residual;
641     Domain _correction;
642     std::shared_ptr<Jacobian> _jacobian;
643     std::shared_ptr<Domain> _previousSolution;
644 
645     std::shared_ptr<TerminateInterface> _terminate;
646     std::shared_ptr<LineSearch> _lineSearch;
647 
648     // Class for storing results
649     Result _result;
650     bool _resultValid = false; // result class only valid after calling apply
651     Real _previousDefect = 0.0;
652 
653     // Remember if jacobian was reassembled in prepareStep
654     bool _reassembled = true; // will be set in prepare step
655     Real _linearReduction = 0.0; // will be set in prepare step
656 
657     // User parameters
658     unsigned int _verbosity = 0;
659     Real _reduction = 1e-8;
660     Real _absoluteLimit = 1e-12;
661     bool _keepMatrix = true;
662     bool _useMaxNorm = false;
663 
664     // Special treatment if we have hanging nodes
665     bool _hangingNodeModifications = false;
666 
667     // User parameters for prepareStep()
668     Real _minLinearReduction = 1e-3;
669     bool _fixedLinearReduction = false;
670     Real _reassembleThreshold = 0.0;
671   };
672 
673 } // namespace Dune::PDELab
674 
675 #endif
676