OGS
StaggeredCoupling-impl.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#pragma once
5
6#include <algorithm>
7
8#include "BaseLib/Error.h"
9#include "BaseLib/RunTime.h"
11#include "StaggeredCoupling.h"
12
13namespace NumLib
14{
15template <typename ProcessData, typename Output>
17 const double t, const double dt, const std::size_t timestep_id,
18 std::vector<GlobalVector*>& process_solutions,
19 std::vector<GlobalVector*> const& process_solutions_prev,
20 std::vector<std::unique_ptr<ProcessData>> const& per_process_data,
21 std::vector<Output> const& outputs,
22 ProcessSolver<ProcessData, Output> const& solve_one_time_step_one_process)
23{
24 auto [nonlinear_solver_status, coupling_iteration_converged,
25 number_of_coupling_iterations] =
27 timestep_id, process_solutions, process_solutions_prev,
28 per_process_data, outputs,
29 solve_one_time_step_one_process);
30
31 number_of_global_coupling_iterations_ = number_of_coupling_iterations;
32
33 // A failed per-process nonlinear solver is already reported as such and
34 // leaves the coupling loop early with unmet error norms. Reaching the
35 // iteration limit with all processes converged individually is the
36 // remaining failure mode: the coupling residuum is not converged, hence
37 // the solution must not be accepted.
38 if (!coupling_iteration_converged &&
39 nonlinear_solver_status.error_norms_met)
40 {
41 WARN(
42 "The coupling iterations reached their maximum number {:d} in time "
43 "step #{:d} at t = {:g} s. The time step will be rejected.",
44 global_coupling_max_iterations_, timestep_id, t);
45
46 nonlinear_solver_status.error_norms_met = false;
47 // The time loop decides on the acceptance of the time step by means of
48 // the per-process nonlinear solver states.
49 for (auto const& process_data : per_process_data)
50 {
51 process_data->nonlinear_solver_status.error_norms_met = false;
52 }
53 }
54
55 return nonlinear_solver_status;
56}
57
58template <typename ProcessData, typename Output>
60 int const global_coupling_iteration,
61 CouplingNode const& regular_coupling_node, const double t, const double dt,
62 const std::size_t timestep_id,
63 std::vector<GlobalVector*>& process_solutions,
64 std::vector<GlobalVector*> const& process_solutions_prev,
65 std::vector<std::unique_ptr<ProcessData>> const& per_process_data,
66 std::vector<Output> const& outputs,
67 ProcessSolver<ProcessData, Output> const& solve_one_time_step_one_process)
68{
69 BaseLib::RunTime time_timestep_process;
70 time_timestep_process.start();
71
72 auto const process_id = regular_coupling_node.process_id;
73 auto const& process_name = regular_coupling_node.process_name;
74 INFO("Solve process #{:d} (named as {:s})", process_id, process_name);
75
76 auto& process_data = *(per_process_data[process_id]);
77
78 process_data.nonlinear_solver_status = solve_one_time_step_one_process(
79 process_solutions, process_solutions_prev, timestep_id, t, dt,
80 process_data, outputs);
81
82 INFO(
83 "[time] Solving process #{:d} (named as {:s}) took {:g} s in "
84 "time step #{} coupling iteration #{}.",
85 process_id, process_name, time_timestep_process.elapsed(), timestep_id,
86 global_coupling_iteration);
87
88 return process_data.nonlinear_solver_status;
89}
90
91template <typename ProcessData, typename Output>
92std::tuple<NumLib::NonlinearSolverStatus, bool, int>
94 std::vector<CouplingNodeVariant>& coupling_nodes, const int max_iterations,
95 const double t, const double dt, const std::size_t timestep_id,
96 std::vector<GlobalVector*>& process_solutions,
97 std::vector<GlobalVector*> const& process_solutions_prev,
98 std::vector<std::unique_ptr<ProcessData>> const& per_process_data,
99 std::vector<Output> const& outputs,
100 ProcessSolver<ProcessData, Output> const& solve_one_time_step_one_process)
101{
102 setFirstIterationIndicator(coupling_nodes);
103
104 NumLib::NonlinearSolverStatus nonlinear_solver_status{true, -1};
105
106 bool coupling_iteration_converged = true;
107 int number_of_coupling_iterations = 0;
108 // Maximum number of coupling iterations over all nested sub-couplings.
109 int number_of_sub_coupling_iterations = 0;
110 for (int global_coupling_iteration = 0;
111 global_coupling_iteration < max_iterations;
112 global_coupling_iteration++,
113 resetCouplingConvergenceCriteria(coupling_nodes))
114 {
115 coupling_iteration_converged = true;
116 number_of_coupling_iterations = global_coupling_iteration + 1;
117
118 INFO("Global coupling iteration #{:d} started.",
119 global_coupling_iteration);
120 BaseLib::RunTime coupling_iteration_timer;
121 coupling_iteration_timer.start();
122
123 for (auto& coupling_node : coupling_nodes)
124 {
125 // For the dummy root node, perform sub-coupling computation.
126 if (std::holds_alternative<RootCouplingNode>(coupling_node))
127 {
128 auto const [local_nonlinear_solver_status,
129 local_coupling_iteration_converged,
130 local_number_of_coupling_iterations] =
132 coupling_node, t, dt, timestep_id, process_solutions,
133 process_solutions_prev, per_process_data, outputs,
134 solve_one_time_step_one_process);
135
136 number_of_sub_coupling_iterations =
137 std::max(number_of_sub_coupling_iterations,
138 local_number_of_coupling_iterations);
139
140 if (!local_nonlinear_solver_status.error_norms_met)
141 {
142 coupling_iteration_converged = false;
143 return {local_nonlinear_solver_status,
144 coupling_iteration_converged,
145 std::max(number_of_coupling_iterations,
146 number_of_sub_coupling_iterations)};
147 }
148
149 coupling_iteration_converged =
150 coupling_iteration_converged &&
151 local_coupling_iteration_converged;
152 continue;
153 }
154
155 CouplingNode const& regular_coupling_node =
156 std::get<CouplingNode>(coupling_node);
157
158 nonlinear_solver_status = executeSingleIteration(
159 global_coupling_iteration, regular_coupling_node, t, dt,
160 timestep_id, process_solutions, process_solutions_prev,
161 per_process_data, outputs, solve_one_time_step_one_process);
162
163 if (!nonlinear_solver_status.error_norms_met)
164 {
165 WARN(
166 "The nonlinear solver failed in time step #{:d} at t = "
167 "{:g} s for process {:s}.",
168 timestep_id, t, regular_coupling_node.process_name);
169 coupling_iteration_converged = false;
170 return {nonlinear_solver_status, coupling_iteration_converged,
171 std::max(number_of_coupling_iterations,
172 number_of_sub_coupling_iterations)};
173 }
174
175 auto const& x =
176 *process_solutions[regular_coupling_node.process_id];
177
178 // It is unnecessary to check convergence for a process at the first
179 // iteration
180 if (global_coupling_iteration > 0)
181 {
182 coupling_iteration_converged = checkCouplingConvergence(
183 coupling_iteration_converged, regular_coupling_node, x);
184 }
185
186 updatePreviousSolution(regular_coupling_node.process_id, x);
187
188 } // end of for (auto& process_data : _per_process_data)
189
190 // At least to run two coupling iterations, meaning that the coupling
191 // has at least two coupling nodes.
192 INFO("Global coupling iteration #{:d} took {:g} s.",
193 global_coupling_iteration, coupling_iteration_timer.elapsed());
194 if (coupling_iteration_converged && global_coupling_iteration > 0)
195 {
196 break;
197 }
198 }
199
200 return {nonlinear_solver_status, coupling_iteration_converged,
201 std::max(number_of_coupling_iterations,
202 number_of_sub_coupling_iterations)};
203}
204
205template <typename ProcessData, typename Output>
206std::tuple<NumLib::NonlinearSolverStatus, bool, int>
208 CouplingNodeVariant& coupling_node, const double t, const double dt,
209 const std::size_t timestep_id,
210 std::vector<GlobalVector*>& process_solutions,
211 std::vector<GlobalVector*> const& process_solutions_prev,
212 std::vector<std::unique_ptr<ProcessData>> const& per_process_data,
213 std::vector<Output> const& outputs,
214 ProcessSolver<ProcessData, Output> const& solve_one_time_step_one_process)
215{
216 INFO("--- Execute sub-coupling:");
217 RootCouplingNode& root_coupling_node =
218 std::get<RootCouplingNode>(coupling_node);
219 const int local_max_iterations =
220 std::get<CouplingNode>(root_coupling_node.sub_coupling_nodes.front())
221 .max_iterations;
222
223 auto const [sub_nonlinear_solver_status, sub_coupling_iteration_converged,
224 sub_number_of_coupling_iterations] =
226 root_coupling_node.sub_coupling_nodes, local_max_iterations, t, dt,
227 timestep_id, process_solutions, process_solutions_prev,
228 per_process_data, outputs, solve_one_time_step_one_process);
229
230 INFO("--- End sub-coupling.");
231 return {sub_nonlinear_solver_status, sub_coupling_iteration_converged,
232 sub_number_of_coupling_iterations};
233}
234
235} // namespace NumLib
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
Count the running time.
Definition RunTime.h:18
double elapsed() const
Get the elapsed time in seconds.
Definition RunTime.h:31
void start()
Start the timer.
Definition RunTime.h:21
NumLib::NonlinearSolverStatus executeSingleIteration(int const global_coupling_iteration, CouplingNode const &regular_coupling_node, const double t, const double dt, const std::size_t timestep_id, std::vector< GlobalVector * > &process_solutions, std::vector< GlobalVector * > const &process_solutions_prev, std::vector< std::unique_ptr< ProcessData > > const &per_process_data, std::vector< Output > const &outputs, ProcessSolver< ProcessData, Output > const &solve_one_time_step_one_process)
const int global_coupling_max_iterations_
Maximum iteration number of the coupling loop of the staggered scheme.
void resetCouplingConvergenceCriteria(std::vector< CouplingNodeVariant > const &coupling_nodes)
void setFirstIterationIndicator(std::vector< CouplingNodeVariant > const &coupling_nodes)
Set the indicator of the first staggered coupling iteration be true.
std::tuple< NumLib::NonlinearSolverStatus, bool, int > executeSubCoupling(CouplingNodeVariant &coupling_node, const double t, const double dt, const std::size_t timestep_id, std::vector< GlobalVector * > &process_solutions, std::vector< GlobalVector * > const &process_solutions_prev, std::vector< std::unique_ptr< ProcessData > > const &per_process_data, std::vector< Output > const &outputs, ProcessSolver< ProcessData, Output > const &solve_one_time_step_one_process)
std::vector< CouplingNodeVariant > coupling_nodes_
std::function< NumLib::NonlinearSolverStatus( std::vector< GlobalVector * > &, std::vector< GlobalVector * > const &, std::size_t const, double const, double const, ProcessData const &, std::vector< Output > const &)> ProcessSolver
std::tuple< NumLib::NonlinearSolverStatus, bool, int > executeConcrete(std::vector< CouplingNodeVariant > &coupling_nodes, const int max_iterations, const double t, const double dt, const std::size_t timestep_id, std::vector< GlobalVector * > &process_solutions, std::vector< GlobalVector * > const &process_solutions_prev, std::vector< std::unique_ptr< ProcessData > > const &per_process_data, std::vector< Output > const &outputs, ProcessSolver< ProcessData, Output > const &solve_one_time_step_one_process)
NumLib::NonlinearSolverStatus execute(const double t, const double dt, const std::size_t timestep_id, std::vector< GlobalVector * > &process_solutions, std::vector< GlobalVector * > const &process_solutions_prev, std::vector< std::unique_ptr< ProcessData > > const &per_process_data, std::vector< Output > const &outputs, ProcessSolver< ProcessData, Output > const &solve_one_time_step_one_process)
bool checkCouplingConvergence(const bool convergence_of_last_process, CouplingNode const &coupling_node, GlobalVector const &x) const
void updatePreviousSolution(int const process_id, GlobalVector const &x)
std::variant< CouplingNode, RootCouplingNode > CouplingNodeVariant
Information of a coupling node.
Status of the non-linear solver.
std::vector< CouplingNodeVariant > sub_coupling_nodes