OGS
TimeLoop.cpp
Go to the documentation of this file.
1 
11 #include "TimeLoop.h"
12 
13 #include "BaseLib/Error.h"
14 #include "BaseLib/RunTime.h"
16 #include "MathLib/LinAlg/LinAlg.h"
20 #include "ProcessData.h"
23 namespace
24 {
26  NumLib::EquationSystem& eq_sys,
29 {
30  using Tag = NumLib::NonlinearSolverTag;
31  switch (nl_tag)
32  {
33  case Tag::Picard:
34  {
36  auto& eq_sys_ = static_cast<EqSys&>(eq_sys);
37  if (auto* nl_solver =
39  &nonlinear_solver);
40  nl_solver != nullptr)
41  {
42  nl_solver->setEquationSystem(eq_sys_, conv_crit);
43  }
44  else
45  {
46  OGS_FATAL(
47  "Could not cast nonlinear solver to Picard type solver.");
48  }
49  break;
50  }
51  case Tag::Newton:
52  {
54  auto& eq_sys_ = static_cast<EqSys&>(eq_sys);
55 
56  if (auto* nl_solver =
58  &nonlinear_solver);
59  nl_solver != nullptr)
60  {
61  nl_solver->setEquationSystem(eq_sys_, conv_crit);
62  }
63 #ifdef USE_PETSC
64  else if (auto* nl_solver =
65  dynamic_cast<NumLib::PETScNonlinearSolver*>(
66  &nonlinear_solver);
67  nl_solver != nullptr)
68  {
69  nl_solver->setEquationSystem(eq_sys_, conv_crit);
70  }
71 #endif // USE_PETSC
72  else
73  {
74  OGS_FATAL(
75  "Could not cast nonlinear solver to Newton type solver.");
76  }
77  break;
78  }
79  }
80 }
81 
83 {
84  return process_data.process.isMonolithicSchemeUsed();
85 }
86 } // namespace
87 
88 namespace ProcessLib
89 {
91  double const t, double const dt,
92  std::vector<std::unique_ptr<ProcessData>> const& per_process_data,
93  std::vector<GlobalVector*> const& _process_solutions)
94 {
95  for (auto& process_data : per_process_data)
96  {
97  auto const process_id = process_data->process_id;
98  auto& pcs = process_data->process;
99  pcs.preTimestep(_process_solutions, t, dt, process_id);
100  }
101 }
102 
104  double const t, double const dt,
105  std::vector<std::unique_ptr<ProcessData>> const& per_process_data,
106  std::vector<GlobalVector*> const& process_solutions,
107  std::vector<GlobalVector*> const& process_solutions_prev,
108  std::vector<std::size_t>& xdot_vector_ids)
109 {
110  std::vector<GlobalVector*> x_dots;
111  x_dots.reserve(per_process_data.size());
112  xdot_vector_ids.resize(per_process_data.size());
113 
114  std::size_t cnt = 0;
115  for (auto& process_data : per_process_data)
116  {
117  auto const process_id = process_data->process_id;
118  auto const& ode_sys = *process_data->tdisc_ode_sys;
119  auto const& time_discretization = *process_data->time_disc;
120 
121  x_dots.emplace_back(&NumLib::GlobalVectorProvider::provider.getVector(
122  ode_sys.getMatrixSpecifications(process_id), xdot_vector_ids[cnt]));
123  cnt++;
124 
125  time_discretization.getXdot(*process_solutions[process_id],
126  *process_solutions_prev[process_id],
127  *x_dots[process_id]);
128  }
129 
130  // All _per_process_data share the first process.
131  bool const is_staggered_coupling =
132  !isMonolithicProcess(*per_process_data[0]);
133 
134  for (auto& process_data : per_process_data)
135  {
136  auto const process_id = process_data->process_id;
137  auto& pcs = process_data->process;
138 
139  if (is_staggered_coupling)
140  {
141  CoupledSolutionsForStaggeredScheme coupled_solutions(
142  process_solutions);
143  pcs.setCoupledSolutionsForStaggeredScheme(&coupled_solutions);
144  }
145  auto& x_dot = *x_dots[process_id];
146  pcs.computeSecondaryVariable(t, dt, process_solutions, x_dot,
147  process_id);
148  pcs.postTimestep(process_solutions, t, dt, process_id);
149  }
150  for (auto& x_dot : x_dots)
151  {
153  }
154 }
155 
156 template <NumLib::ODESystemTag ODETag>
158  ProcessData& process_data,
160 {
161  using Tag = NumLib::NonlinearSolverTag;
162  // A concrete Picard solver
163  using NonlinearSolverPicard = NumLib::NonlinearSolver<Tag::Picard>;
164  // A concrete Newton solver
165  using NonlinearSolverNewton = NumLib::NonlinearSolver<Tag::Newton>;
166 
167  if (dynamic_cast<NonlinearSolverPicard*>(&process_data.nonlinear_solver))
168  {
169  // The Picard solver can also work with a Newton-ready ODE,
170  // because the Newton ODESystem derives from the Picard ODESystem.
171  // So no further checks are needed here.
172 
173  process_data.tdisc_ode_sys = std::make_unique<
175  process_data.process_id, ode_sys, *process_data.time_disc);
176  }
177  // TODO (naumov) Provide a function to nonlinear_solver to distinguish the
178  // types. Could be handy, because a nonlinear solver could handle both types
179  // like PETScSNES.
180  else if ((dynamic_cast<NonlinearSolverNewton*>(
181  &process_data.nonlinear_solver) != nullptr)
182 #ifdef USE_PETSC
183  || (dynamic_cast<NumLib::PETScNonlinearSolver*>(
184  &process_data.nonlinear_solver) != nullptr)
185 #endif // USE_PETSC
186  )
187  {
188  // The Newton-Raphson method needs a Newton-ready ODE.
189 
190  using ODENewton = NumLib::ODESystem<ODETag, Tag::Newton>;
191  if (auto* ode_newton = dynamic_cast<ODENewton*>(&ode_sys))
192  {
193  process_data.tdisc_ode_sys = std::make_unique<
195  process_data.process_id, *ode_newton, *process_data.time_disc);
196  }
197  else
198  {
199  OGS_FATAL(
200  "You are trying to solve a non-Newton-ready ODE with the"
201  " Newton-Raphson method. Aborting");
202  }
203  }
204  else
205  {
206  OGS_FATAL("Encountered unknown nonlinear solver type. Aborting");
207  }
208 }
209 
211 {
212  setTimeDiscretizedODESystem(process_data, process_data.process);
213 }
214 
215 std::pair<std::vector<GlobalVector*>, std::vector<GlobalVector*>>
217  double const t0,
218  std::vector<std::unique_ptr<ProcessData>> const& per_process_data)
219 {
220  std::vector<GlobalVector*> process_solutions;
221  std::vector<GlobalVector*> process_solutions_prev;
222 
223  for (auto& process_data : per_process_data)
224  {
225  auto const process_id = process_data->process_id;
226  auto& ode_sys = *process_data->tdisc_ode_sys;
227 
228  // append a solution vector of suitable size
229  process_solutions.emplace_back(
231  ode_sys.getMatrixSpecifications(process_id)));
232  process_solutions_prev.emplace_back(
234  ode_sys.getMatrixSpecifications(process_id)));
235  }
236 
237  for (auto& process_data : per_process_data)
238  {
239  auto& pcs = process_data->process;
240  auto const process_id = process_data->process_id;
241  pcs.setInitialConditions(process_solutions, process_solutions_prev, t0,
242  process_id);
243 
244  auto& time_disc = *process_data->time_disc;
245  time_disc.setInitialState(t0); // push IC
246  }
247 
248  return {process_solutions, process_solutions_prev};
249 }
250 
252  std::vector<std::unique_ptr<ProcessData>> const& per_process_data,
253  std::vector<GlobalVector*>
254  process_solutions,
255  std::vector<GlobalVector*> const& process_solutions_prev)
256 {
257  INFO("Calculate non-equilibrium initial residuum.");
258  for (auto& process_data : per_process_data)
259  {
260  auto& ode_sys = *process_data->tdisc_ode_sys;
261 
262  auto& time_disc = *process_data->time_disc;
263  auto& nonlinear_solver = process_data->nonlinear_solver;
264  auto& conv_crit = *process_data->conv_crit;
265 
266  auto const nl_tag = process_data->nonlinear_solver_tag;
267  setEquationSystem(nonlinear_solver, ode_sys, conv_crit, nl_tag);
268  // dummy values to handle the time derivative terms more or less
269  // correctly, i.e. to ignore them.
270  double const t = 0;
271  double const dt = 1;
272  time_disc.nextTimestep(t, dt);
273  nonlinear_solver.calculateNonEquilibriumInitialResiduum(
274  process_solutions, process_solutions_prev,
275  process_data->process_id);
276  }
277 }
278 
280  std::vector<GlobalVector*>& x, std::vector<GlobalVector*> const& x_prev,
281  std::size_t const timestep, double const t, double const delta_t,
282  ProcessData const& process_data, Output& output_control,
283  std::size_t& xdot_id)
284 {
285  auto& process = process_data.process;
286  int const process_id = process_data.process_id;
287  auto& time_disc = *process_data.time_disc;
288  auto& conv_crit = *process_data.conv_crit;
289  auto& ode_sys = *process_data.tdisc_ode_sys;
290  auto& nonlinear_solver = process_data.nonlinear_solver;
291  auto const nl_tag = process_data.nonlinear_solver_tag;
292 
293  setEquationSystem(nonlinear_solver, ode_sys, conv_crit, nl_tag);
294 
295  // Note: Order matters!
296  // First advance to the next timestep, then set known solutions at that
297  // time, afterwards pass the right solution vector and time to the
298  // preTimestep() hook.
299 
300  time_disc.nextTimestep(t, delta_t);
301 
302  auto const post_iteration_callback =
303  [&](int iteration, std::vector<GlobalVector*> const& x)
304  {
305  output_control.doOutputNonlinearIteration(process, process_id, timestep,
306  t, iteration, x);
307  };
308 
309  auto const nonlinear_solver_status =
310  nonlinear_solver.solve(x, x_prev, post_iteration_callback, process_id);
311 
312  if (!nonlinear_solver_status.error_norms_met)
313  {
314  return nonlinear_solver_status;
315  }
316 
318  ode_sys.getMatrixSpecifications(process_id), xdot_id);
319 
320  time_disc.getXdot(*x[process_id], *x_prev[process_id], x_dot);
321 
322  process.postNonLinearSolver(*x[process_id], x_dot, t, delta_t, process_id);
324 
325  return nonlinear_solver_status;
326 }
327 
328 TimeLoop::TimeLoop(std::unique_ptr<Output>&& output,
329  std::vector<std::unique_ptr<ProcessData>>&& per_process_data,
330  const int global_coupling_max_iterations,
331  std::vector<std::unique_ptr<NumLib::ConvergenceCriterion>>&&
332  global_coupling_conv_crit,
333  const double start_time, const double end_time)
334  : _output(std::move(output)),
335  _per_process_data(std::move(per_process_data)),
336  _start_time(start_time),
337  _end_time(end_time),
338  _global_coupling_max_iterations(global_coupling_max_iterations),
339  _global_coupling_conv_crit(std::move(global_coupling_conv_crit))
340 {
341 }
342 
344 {
345  for (auto& process_data : _per_process_data)
346  {
347  auto const& x = *_process_solutions[process_data->process_id];
348 
349  // Create a vector to store the solution of the last coupling iteration
351  MathLib::LinAlg::copy(x, x0);
352 
353  // append a solution vector of suitable size
354  _solutions_of_last_cpl_iteration.emplace_back(&x0);
355  }
356 }
357 
358 double TimeLoop::computeTimeStepping(const double prev_dt, double& t,
359  std::size_t& accepted_steps,
360  std::size_t& rejected_steps)
361 {
362  bool all_process_steps_accepted = true;
363  // Get minimum time step size among step sizes of all processes.
364  double dt = std::numeric_limits<double>::max();
365 
366  bool is_initial_step = false;
367  for (std::size_t i = 0; i < _per_process_data.size(); i++)
368  {
369  auto& ppd = *_per_process_data[i];
370  const auto& timestepper = ppd.timestepper;
371  if (timestepper->getTimeStep().steps() == 0)
372  {
373  is_initial_step = true;
374  }
375 
376  auto& time_disc = ppd.time_disc;
377  auto const& x = *_process_solutions[i];
378  auto const& x_prev = *_process_solutions_prev[i];
379 
380  auto const& conv_crit = ppd.conv_crit;
381  const MathLib::VecNormType norm_type =
382  (conv_crit) ? conv_crit->getVectorNormType()
384 
385  const double solution_error =
386  (timestepper->isSolutionErrorComputationNeeded())
387  ? ((t == timestepper->begin())
388  ? 0. // Always accepts the zeroth step
389  : time_disc->computeRelativeChangeFromPreviousTimestep(
390  x, x_prev, norm_type))
391  : 0.;
392 
393  if (!ppd.nonlinear_solver_status.error_norms_met)
394  {
395  timestepper->setAcceptedOrNot(false);
396  }
397  else
398  {
399  timestepper->setAcceptedOrNot(true);
400  }
401 
402  auto [step_accepted, timestepper_dt] = timestepper->next(
403  solution_error, ppd.nonlinear_solver_status.number_iterations);
404 
405  if (!step_accepted &&
406  // In case of FixedTimeStepping, which makes timestepper->next(...)
407  // return false when the ending time is reached.
408  t + std::numeric_limits<double>::epsilon() < timestepper->end())
409  {
410  // Not all processes have accepted steps.
411  all_process_steps_accepted = false;
412  }
413 
414  if (!ppd.nonlinear_solver_status.error_norms_met)
415  {
416  WARN(
417  "Time step will be rejected due to nonlinear solver "
418  "divergence.");
419  all_process_steps_accepted = false;
420  }
421 
422  if (timestepper_dt > std::numeric_limits<double>::epsilon() ||
423  std::abs(t - timestepper->end()) <
424  std::numeric_limits<double>::epsilon())
425  {
426  dt = std::min(timestepper_dt, dt);
427  }
428  }
429 
430  if (all_process_steps_accepted)
431  {
433  }
434  else
435  {
437  }
438 
439  if (!is_initial_step)
440  {
441  if (all_process_steps_accepted)
442  {
443  accepted_steps++;
444  _last_step_rejected = false;
445  }
446  else
447  {
448  if (t < _end_time || std::abs(t - _end_time) <
449  std::numeric_limits<double>::epsilon())
450  {
451  t -= prev_dt;
452  rejected_steps++;
453  _last_step_rejected = true;
454  }
455  }
456  }
457 
458  // Adjust step size if t < _end_time, while t+dt exceeds the end time
459  if (t < _end_time && t + dt > _end_time)
460  {
461  dt = _end_time - t;
462  }
463 
465  _output->getFixedOutputTimes());
466  // Check whether the time stepping is stabilized
467  if (std::abs(dt - prev_dt) < std::numeric_limits<double>::epsilon())
468  {
470  {
471  OGS_FATAL(
472  "The new step size of {:g} is the same as that of the previous "
473  "rejected time step. \nPlease re-run ogs with a proper "
474  "adjustment in the numerical settings, \ne.g those for time "
475  "stepper, local or global non-linear solver.",
476  dt);
477  }
478  else
479  {
480  DBUG("The time stepping is stabilized with the step size of {:g}.",
481  dt);
482  }
483  }
484 
485  // Reset the time step with the minimum step size, dt
486  // Update the solution of the previous time step.
487  for (std::size_t i = 0; i < _per_process_data.size(); i++)
488  {
489  const auto& ppd = *_per_process_data[i];
490  auto& timestepper = ppd.timestepper;
491  if (all_process_steps_accepted)
492  {
493  timestepper->resetCurrentTimeStep(dt);
494  }
495 
496  if (t == timestepper->begin())
497  {
498  continue;
499  }
500 
501  auto& x = *_process_solutions[i];
502  auto& x_prev = *_process_solutions_prev[i];
503  if (all_process_steps_accepted)
504  {
505  MathLib::LinAlg::copy(x, x_prev); // pushState
506  }
507  else
508  {
509  if (t < _end_time || std::abs(t - _end_time) <
510  std::numeric_limits<double>::epsilon())
511  {
512  WARN(
513  "Time step {:d} was rejected {:d} times and it will be "
514  "repeated with a reduced step size.",
515  accepted_steps + 1, _repeating_times_of_rejected_step);
516  MathLib::LinAlg::copy(x_prev, x); // popState
517  }
518  }
519  }
520 
521  return dt;
522 }
523 
526 {
527  for (auto& process_data : _per_process_data)
528  {
529  auto& pcs = process_data->process;
530  int const process_id = process_data->process_id;
531  _output->addProcess(pcs);
532 
533  setTimeDiscretizedODESystem(*process_data);
534 
535  if (auto* conv_crit =
537  process_data->conv_crit.get()))
538  {
539  conv_crit->setDOFTable(pcs.getDOFTable(process_id), pcs.getMesh());
540  }
541  }
542 
543  // initial solution storage
546 
547  // All _per_process_data share the first process.
548  bool const is_staggered_coupling =
550 
551  if (is_staggered_coupling)
552  {
554  }
555 
556  // Output initial conditions
557  {
558  const bool output_initial_condition = true;
559  outputSolutions(output_initial_condition, 0, _start_time, *_output,
561  }
562 }
563 
564 /*
565  * TODO:
566  * Now we have a structure inside the time loop which is very similar to the
567  * nonlinear solver. And admittedly, the control flow inside the nonlinear
568  * solver is rather complicated. Maybe in the future one can introduce an
569  * abstraction that can do both the convergence checks of the coupling loop and
570  * of the nonlinear solver.
571  */
573 {
574  // All _per_process_data share the first process.
575  bool const is_staggered_coupling =
577 
578  bool non_equilibrium_initial_residuum_computed = false;
579  double t = _start_time;
580  std::size_t accepted_steps = 0;
581  std::size_t rejected_steps = 0;
582  NumLib::NonlinearSolverStatus nonlinear_solver_status;
583 
584  double dt = computeTimeStepping(0.0, t, accepted_steps, rejected_steps);
585 
586  while (t < _end_time)
587  {
588  BaseLib::RunTime time_timestep;
589  time_timestep.start();
590 
591  t += dt;
592  const double prev_dt = dt;
593 
594  const std::size_t timesteps = accepted_steps + 1;
595  // TODO(wenqing): , input option for time unit.
596  INFO(
597  "=== Time stepping at step #{:d} and time {:g} with step size {:g}",
598  timesteps, t, dt);
599 
600  // Check element deactivation:
601  for (auto& process_data : _per_process_data)
602  {
603  process_data->process.updateDeactivatedSubdomains(
604  t, process_data->process_id);
605  }
606 
607  if (!non_equilibrium_initial_residuum_computed)
608  {
611  non_equilibrium_initial_residuum_computed = true;
612  }
613 
616 
617  if (is_staggered_coupling)
618  {
619  nonlinear_solver_status =
621  }
622  else
623  {
624  nonlinear_solver_status =
625  solveUncoupledEquationSystems(t, dt, timesteps);
626  }
627 
628  // Run post time step only if the last iteration was successful.
629  // Otherwise it runs the risks to get the same errors as in the last
630  // iteration, an exception thrown in assembly, for example.
631  if (nonlinear_solver_status.error_norms_met)
632  {
636  }
637 
638  INFO("[time] Time step #{:d} took {:g} s.", timesteps,
639  time_timestep.elapsed());
640 
641  double const current_time = t;
642  // _last_step_rejected is also checked in computeTimeStepping.
643  dt = computeTimeStepping(prev_dt, t, accepted_steps, rejected_steps);
644 
645  if (!_last_step_rejected)
646  {
647  const bool output_initial_condition = false;
648  outputSolutions(output_initial_condition, timesteps, current_time,
650  }
651 
652  if (std::abs(t - _end_time) < std::numeric_limits<double>::epsilon() ||
653  t + dt > _end_time)
654  {
655  break;
656  }
657 
658  if (dt < std::numeric_limits<double>::epsilon())
659  {
660  WARN(
661  "Time step size of {:g} is too small.\n"
662  "Time stepping stops at step {:d} and at time of {:g}.",
663  dt, timesteps, t);
664  break;
665  }
666  }
667 
668  INFO(
669  "The whole computation of the time stepping took {:d} steps, in which\n"
670  "\t the accepted steps are {:d}, and the rejected steps are {:d}.\n",
671  accepted_steps + rejected_steps, accepted_steps, rejected_steps);
672 
673  // output last time step
674  if (nonlinear_solver_status.error_norms_met)
675  {
676  const bool output_initial_condition = false;
677  outputSolutions(output_initial_condition,
678  accepted_steps + rejected_steps, t, *_output,
680  }
681 
682  return nonlinear_solver_status.error_norms_met;
683 }
684 
686  const double t, const double dt, const std::size_t timestep_id,
687  ProcessData const& process_data, std::vector<GlobalVector*>& x,
688  std::vector<GlobalVector*> const& x_prev, Output& output,
689  std::size_t& xdot_id)
690 {
691  BaseLib::RunTime time_timestep_process;
692  time_timestep_process.start();
693 
694  auto const nonlinear_solver_status = solveOneTimeStepOneProcess(
695  x, x_prev, timestep_id, t, dt, process_data, output, xdot_id);
696 
697  INFO("[time] Solving process #{:d} took {:g} s in time step #{:d} ",
698  process_data.process_id, time_timestep_process.elapsed(), timestep_id);
699 
700  return nonlinear_solver_status;
701 }
702 
703 static constexpr std::string_view timestepper_cannot_reduce_dt =
704  "Time stepper cannot reduce the time step size further.";
705 
707  const double t, const double dt, const std::size_t timestep_id)
708 {
709  NumLib::NonlinearSolverStatus nonlinear_solver_status;
710 
711  _xdot_vector_ids.resize(_per_process_data.size());
712  std::size_t cnt = 0;
713 
714  for (auto& process_data : _per_process_data)
715  {
716  auto const process_id = process_data->process_id;
717  nonlinear_solver_status = solveMonolithicProcess(
718  t, dt, timestep_id, *process_data, _process_solutions,
720  cnt++;
721 
722  process_data->nonlinear_solver_status = nonlinear_solver_status;
723  if (!nonlinear_solver_status.error_norms_met)
724  {
725  ERR("The nonlinear solver failed in time step #{:d} at t = {:g} s "
726  "for process #{:d}.",
727  timestep_id, t, process_id);
728 
729  if (!process_data->timestepper->canReduceTimestepSize())
730  {
731  // save unsuccessful solution
732  _output->doOutputAlways(
733  process_data->process, process_id, timestep_id, t,
734  process_data->nonlinear_solver_status.number_iterations,
737  }
738 
739  return nonlinear_solver_status;
740  }
741  }
742 
743  return nonlinear_solver_status;
744 }
745 
748  const double t, const double dt, const std::size_t timestep_id)
749 {
750  // Coupling iteration
752  {
753  // Set the flag of the first iteration be true.
754  for (auto& conv_crit : _global_coupling_conv_crit)
755  {
756  conv_crit->preFirstIteration();
757  }
758  }
759  auto resetCouplingConvergenceCriteria = [&]()
760  {
761  for (auto& conv_crit : _global_coupling_conv_crit)
762  {
763  conv_crit->reset();
764  }
765  };
766 
767  NumLib::NonlinearSolverStatus nonlinear_solver_status{false, -1};
768  bool coupling_iteration_converged = true;
769  for (int global_coupling_iteration = 0;
770  global_coupling_iteration < _global_coupling_max_iterations;
771  global_coupling_iteration++, resetCouplingConvergenceCriteria())
772  {
773  // TODO(wenqing): use process name
774  coupling_iteration_converged = true;
775  _xdot_vector_ids.resize(_per_process_data.size());
776  std::size_t cnt = 0;
777  for (auto& process_data : _per_process_data)
778  {
779  auto const process_id = process_data->process_id;
780  BaseLib::RunTime time_timestep_process;
781  time_timestep_process.start();
782 
783  // The following setting of coupled_solutions can be removed only if
784  // the CoupledSolutionsForStaggeredScheme and related functions are
785  // removed totally from the computation of the secondary variable
786  // and from post-time functions.
787  CoupledSolutionsForStaggeredScheme coupled_solutions(
789 
790  process_data->process.setCoupledSolutionsForStaggeredScheme(
791  &coupled_solutions);
792 
793  nonlinear_solver_status = solveOneTimeStepOneProcess(
794  _process_solutions, _process_solutions_prev, timestep_id, t, dt,
795  *process_data, *_output, _xdot_vector_ids[cnt]);
796  cnt++;
797  process_data->nonlinear_solver_status = nonlinear_solver_status;
798 
799  INFO(
800  "[time] Solving process #{:d} took {:g} s in time step #{:d} "
801  "coupling iteration #{:d}",
802  process_id, time_timestep_process.elapsed(), timestep_id,
803  global_coupling_iteration);
804 
805  if (!nonlinear_solver_status.error_norms_met)
806  {
807  WARN(
808  "The nonlinear solver failed in time step #{:d} at t = "
809  "{:g} s for process #{:d}.",
810  timestep_id, t, process_id);
811  _last_step_rejected = true;
812  return nonlinear_solver_status;
813  }
814 
815  // Check the convergence of the coupling iteration
816  auto& x = *_process_solutions[process_id];
817  auto& x_old = *_solutions_of_last_cpl_iteration[process_id];
818  if (global_coupling_iteration > 0)
819  {
820  MathLib::LinAlg::axpy(x_old, -1.0, x); // save dx to x_old
821  INFO(
822  "------- Checking convergence criterion for coupled "
823  "solution of process #{:d} -------",
824  process_id);
825  _global_coupling_conv_crit[process_id]->checkDeltaX(x_old, x);
826  coupling_iteration_converged =
827  coupling_iteration_converged &&
828  _global_coupling_conv_crit[process_id]->isSatisfied();
829  }
830  MathLib::LinAlg::copy(x, x_old);
831  } // end of for (auto& process_data : _per_process_data)
832 
833  if (coupling_iteration_converged && global_coupling_iteration > 0)
834  {
835  break;
836  }
837 
838  if (!nonlinear_solver_status.error_norms_met)
839  {
840  return nonlinear_solver_status;
841  }
842  }
843 
844  if (!coupling_iteration_converged)
845  {
846  WARN(
847  "The coupling iterations reaches its maximum number in time step "
848  "#{:d} at t = {:g} s",
849  timestep_id, t);
850  }
851 
852  {
853  for (auto& process_data : _per_process_data)
854  {
855  auto& pcs = process_data->process;
856  int const process_id = process_data->process_id;
857  auto& ode_sys = *process_data->tdisc_ode_sys;
858  pcs.solveReactionEquation(_process_solutions,
859  _process_solutions_prev, t, dt, ode_sys,
860  process_id);
861  }
862  }
863 
864  return nonlinear_solver_status;
865 }
866 
867 template <typename OutputClass, typename OutputClassMember>
868 void TimeLoop::outputSolutions(bool const output_initial_condition,
869  unsigned timestep, const double t,
870  OutputClass& output_object,
871  OutputClassMember output_class_member) const
872 {
873  // All _per_process_data share the first process.
874  bool const is_staggered_coupling =
876 
877  for (auto& process_data : _per_process_data)
878  {
879  // If nonlinear solver diverged, the solution has already been
880  // saved.
881  if (!process_data->nonlinear_solver_status.error_norms_met)
882  {
883  continue;
884  }
885 
886  auto const process_id = process_data->process_id;
887  auto& pcs = process_data->process;
888 
889  if (!is_staggered_coupling && output_initial_condition)
890  {
891  auto const& ode_sys = *process_data->tdisc_ode_sys;
892  // dummy value to handle the time derivative terms more or less
893  // correctly, i.e. to ignore them.
894  double const dt = 1;
895  process_data->time_disc->nextTimestep(t, dt);
896 
898  ode_sys.getMatrixSpecifications(process_id));
899  x_dot.setZero();
900 
901  pcs.preTimestep(_process_solutions, _start_time, dt, process_id);
902  // Update secondary variables, which might be uninitialized, before
903  // output.
904  pcs.computeSecondaryVariable(_start_time, dt, _process_solutions,
905  x_dot, process_id);
906 
908  }
909  if (is_staggered_coupling && output_initial_condition)
910  {
911  CoupledSolutionsForStaggeredScheme coupled_solutions(
913 
914  process_data->process.setCoupledSolutionsForStaggeredScheme(
915  &coupled_solutions);
916  process_data->process
917  .setCoupledTermForTheStaggeredSchemeToLocalAssemblers(
918  process_id);
919 
920  auto const& ode_sys = *process_data->tdisc_ode_sys;
921  // dummy value to handle the time derivative terms more or less
922  // correctly, i.e. to ignore them.
923  double const dt = 1;
924  process_data->time_disc->nextTimestep(t, dt);
925 
927  ode_sys.getMatrixSpecifications(process_id));
928  x_dot.setZero();
929 
930  pcs.preTimestep(_process_solutions, _start_time, dt, process_id);
931  // Update secondary variables, which might be uninitialized, before
932  // output.
933  pcs.computeSecondaryVariable(_start_time, dt, _process_solutions,
934  x_dot, process_id);
935 
937  }
938  (output_object.*output_class_member)(
939  pcs, process_id, timestep, t,
940  process_data->nonlinear_solver_status.number_iterations,
942  }
943 }
944 
946 {
947  for (auto* x : _process_solutions)
948  {
950  }
951  for (auto* x : _process_solutions_prev)
952  {
954  }
955 
956  for (auto* x : _solutions_of_last_cpl_iteration)
957  {
959  }
960 }
961 
962 } // namespace ProcessLib
#define OGS_FATAL(...)
Definition: Error.h:26
void INFO(char const *fmt, Args const &... args)
Definition: Logging.h:32
void ERR(char const *fmt, Args const &... args)
Definition: Logging.h:42
void DBUG(char const *fmt, Args const &... args)
Definition: Logging.h:27
void WARN(char const *fmt, Args const &... args)
Definition: Logging.h:37
Definition of the RunTime class.
Count the running time.
Definition: RunTime.h:29
double elapsed() const
Get the elapsed time in seconds.
Definition: RunTime.h:42
void start()
Start the timer.
Definition: RunTime.h:32
Global vector based on Eigen vector.
Definition: EigenVector.h:26
virtual GlobalVector & getVector(std::size_t &id)=0
Get an uninitialized vector with the given id.
virtual void releaseVector(GlobalVector const &x)=0
void doOutputNonlinearIteration(Process const &process, const int process_id, int const timestep, const double t, const int iteration, std::vector< GlobalVector * > const &x)
Definition: Output.cpp:435
void doOutput(Process const &process, const int process_id, int const timestep, const double t, int const iteration, std::vector< GlobalVector * > const &x)
Definition: Output.cpp:399
void doOutputLastTimestep(Process const &process, const int process_id, int const timestep, const double t, int const iteration, std::vector< GlobalVector * > const &x)
Definition: Output.cpp:418
bool isMonolithicSchemeUsed() const
Definition: Process.h:102
void outputSolutions(bool const output_initial_condition, unsigned timestep, const double t, OutputClass &output_object, OutputClassMember output_class_member) const
Definition: TimeLoop.cpp:868
std::vector< GlobalVector * > _solutions_of_last_cpl_iteration
Definition: TimeLoop.h:126
const double _start_time
Definition: TimeLoop.h:115
std::vector< std::size_t > _xdot_vector_ids
Definition: TimeLoop.h:131
NumLib::NonlinearSolverStatus solveUncoupledEquationSystems(const double t, const double dt, const std::size_t timestep_id)
Member to solver non coupled systems of equations, which can be a single system of equations,...
Definition: TimeLoop.cpp:706
std::vector< std::unique_ptr< ProcessData > > _per_process_data
Definition: TimeLoop.h:111
const double _end_time
Definition: TimeLoop.h:116
double computeTimeStepping(const double prev_dt, double &t, std::size_t &accepted_steps, std::size_t &rejected_steps)
Definition: TimeLoop.cpp:358
std::vector< std::unique_ptr< NumLib::ConvergenceCriterion > > _global_coupling_conv_crit
Convergence criteria of processes for the global coupling iterations.
Definition: TimeLoop.h:122
void setCoupledSolutions()
Definition: TimeLoop.cpp:343
const int _global_coupling_max_iterations
Maximum iterations of the global coupling.
Definition: TimeLoop.h:119
std::vector< GlobalVector * > _process_solutions
Definition: TimeLoop.h:108
std::unique_ptr< Output > _output
Definition: TimeLoop.h:110
void initialize()
initialize output, convergence criterion, etc.
Definition: TimeLoop.cpp:525
int _repeating_times_of_rejected_step
Definition: TimeLoop.h:114
TimeLoop(std::unique_ptr< Output > &&output, std::vector< std::unique_ptr< ProcessData >> &&per_process_data, const int global_coupling_max_iterations, std::vector< std::unique_ptr< NumLib::ConvergenceCriterion >> &&global_coupling_conv_crit, const double start_time, const double end_time)
Definition: TimeLoop.cpp:328
std::vector< GlobalVector * > _process_solutions_prev
Definition: TimeLoop.h:109
NumLib::NonlinearSolverStatus solveCoupledEquationSystemsByStaggeredScheme(const double t, const double dt, const std::size_t timestep_id)
Member to solver coupled systems of equations by the staggered scheme.
Definition: TimeLoop.cpp:747
NonlinearSolverTag
Tag used to specify which nonlinear solver will be used.
Definition: Types.h:20
void axpy(PETScVector &y, double const a, PETScVector const &x)
Definition: LinAlg.cpp:57
void copy(PETScVector const &x, PETScVector &y)
Definition: LinAlg.cpp:37
VecNormType
Norm type. Not declared as class type in order to use the members as integers.
Definition: LinAlgEnums.h:22
double possiblyClampDtToNextFixedTime(double const t, double const dt, std::vector< double > const &fixed_output_times)
void postTimestepForAllProcesses(double const t, double const dt, std::vector< std::unique_ptr< ProcessData >> const &per_process_data, std::vector< GlobalVector * > const &process_solutions, std::vector< GlobalVector * > const &process_solutions_prev, std::vector< std::size_t > &xdot_vector_ids)
Definition: TimeLoop.cpp:103
static NumLib::NonlinearSolverStatus solveMonolithicProcess(const double t, const double dt, const std::size_t timestep_id, ProcessData const &process_data, std::vector< GlobalVector * > &x, std::vector< GlobalVector * > const &x_prev, Output &output, std::size_t &xdot_id)
Definition: TimeLoop.cpp:685
static constexpr std::string_view timestepper_cannot_reduce_dt
Definition: TimeLoop.cpp:703
void setTimeDiscretizedODESystem(ProcessData &process_data, NumLib::ODESystem< ODETag, NumLib::NonlinearSolverTag::Picard > &ode_sys)
Definition: TimeLoop.cpp:157
void calculateNonEquilibriumInitialResiduum(std::vector< std::unique_ptr< ProcessData >> const &per_process_data, std::vector< GlobalVector * > process_solutions, std::vector< GlobalVector * > const &process_solutions_prev)
Definition: TimeLoop.cpp:251
void preTimestepForAllProcesses(double const t, double const dt, std::vector< std::unique_ptr< ProcessData >> const &per_process_data, std::vector< GlobalVector * > const &_process_solutions)
Definition: TimeLoop.cpp:90
std::pair< std::vector< GlobalVector * >, std::vector< GlobalVector * > > setInitialConditions(double const t0, std::vector< std::unique_ptr< ProcessData >> const &per_process_data)
Definition: TimeLoop.cpp:216
NumLib::NonlinearSolverStatus solveOneTimeStepOneProcess(std::vector< GlobalVector * > &x, std::vector< GlobalVector * > const &x_prev, std::size_t const timestep, double const t, double const delta_t, ProcessData const &process_data, Output &output_control, std::size_t &xdot_id)
Definition: TimeLoop.cpp:279
bool isMonolithicProcess(ProcessLib::ProcessData const &process_data)
Definition: TimeLoop.cpp:82
void setEquationSystem(NumLib::NonlinearSolverBase &nonlinear_solver, NumLib::EquationSystem &eq_sys, NumLib::ConvergenceCriterion &conv_crit, NumLib::NonlinearSolverTag nl_tag)
Definition: TimeLoop.cpp:25
static NUMLIB_EXPORT VectorProvider & provider
Status of the non-linear solver.
std::unique_ptr< NumLib::TimeDiscretization > time_disc
Definition: ProcessData.h:65
std::unique_ptr< NumLib::ConvergenceCriterion > conv_crit
Definition: ProcessData.h:63
NumLib::NonlinearSolverBase & nonlinear_solver
Definition: ProcessData.h:61
std::unique_ptr< NumLib::EquationSystem > tdisc_ode_sys
type-erased time-discretized ODE system
Definition: ProcessData.h:67
NumLib::NonlinearSolverTag const nonlinear_solver_tag
Definition: ProcessData.h:60