opm-simulators
NonlinearSystem_impl.hpp
1 #ifndef OPM_FLOW_NONLINEAR_SYSTEM_IMPL_HEADER_INCLUDED
2 #define OPM_FLOW_NONLINEAR_SYSTEM_IMPL_HEADER_INCLUDED
3 /*
4  Copyright 2026, SINTEF Digital
5 
6  This file is part of the Open Porous Media project (OPM).
7 
8  OPM is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  OPM is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with OPM. If not, see <http://www.gnu.org/licenses/>.
20 */
21 #ifndef OPM_FLOW_NONLINEAR_SYSTEM_HEADER_INCLUDED
22 #include <config.h>
23 #include <opm/simulators/flow/NonlinearSystem.hpp>
24 #endif
25 
26 #include <dune/common/timer.hh>
27 
28 #include <opm/common/ErrorMacros.hpp>
29 #include <opm/common/TimingMacros.hpp>
30 
31 #include <cmath>
32 #include <stdexcept>
33 #include <string>
34 #include <utility>
35 
36 namespace Opm {
37 
38 template <class TypeTag>
39 SimulatorReportSingle
40 NonlinearSystem<TypeTag>::
41 assembleReservoir(const SimulatorTimerInterface&)
42 {
43  return assembleReservoir(well_model_);
44 }
45 
46 template <class TypeTag>
47 void
48 NonlinearSystem<TypeTag>::
49 updateTUNING(const Tuning& tuning)
50 {
51  applyTUNING(param_, tuning);
52 }
53 
54 template <class TypeTag>
55 void
56 NonlinearSystem<TypeTag>::
57 updateTUNINGDP(const TuningDp& tuning_dp)
58 {
59  applyTUNINGDP(param_, tuning_dp);
60 }
61 
62 template <class TypeTag>
63 void
64 NonlinearSystem<TypeTag>::
65 updateSolution(const GlobalEqVector& dx)
66 {
67  OPM_TIMEBLOCK(updateSolution);
68 
69  const bool shouldStore = shouldStoreSolutionUpdate();
70  if (shouldStore) {
71  prepareSolutionUpdate();
72  }
73 
74  auto& newtonMethod = simulator_.model().newtonMethod();
75  auto& solution = simulator_.model().solution(/*timeIdx=*/0);
76 
77  newtonMethod.applyUpdate(/*nextSolution=*/solution,
78  /*curSolution=*/solution,
79  /*update=*/dx,
80  /*resid=*/dx);
81 
82  {
83  OPM_TIMEBLOCK(invalidateAndUpdateIntensiveQuantities);
84  simulator_.model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0);
85  }
86 
87  if (shouldStore) {
88  storeSolutionUpdate(dx);
89  }
90 }
91 
92 template <class TypeTag>
93 template <class LogFailure>
94 void
95 NonlinearSystem<TypeTag>::
96 addReservoirConvergenceMetrics(ConvergenceReport& report,
97  const int componentIdx,
98  const std::string_view componentName,
99  const std::span<const Scalar> residuals,
100  const std::span<const ConvergenceReport::ReservoirFailure::Type> types,
101  const std::span<const Scalar> tolerances,
102  const Scalar maxResidualAllowed,
103  LogFailure&& logFailure) const
104 {
105  if (residuals.size() != types.size() || residuals.size() != tolerances.size()) {
106  OPM_THROW(std::logic_error, "Mismatched reservoir convergence metric sizes.");
107  }
108 
109  using CR = ConvergenceReport;
110  for (std::size_t metricIdx = 0; metricIdx < residuals.size(); ++metricIdx) {
111  const auto residual = residuals[metricIdx];
112  const auto type = types[metricIdx];
113  const auto tolerance = tolerances[metricIdx];
114 
115  if (std::isnan(residual)) {
116  report.setReservoirFailed({type, CR::Severity::NotANumber, componentIdx});
117  logFailure("NaN residual for " + std::string(componentName) + " equation.");
118  }
119  else if (residual > maxResidualAllowed) {
120  report.setReservoirFailed({type, CR::Severity::TooLarge, componentIdx});
121  logFailure("Too large residual for " + std::string(componentName) + " equation.");
122  }
123  else if (residual < 0.0) {
124  report.setReservoirFailed({type, CR::Severity::Normal, componentIdx});
125  logFailure("Negative residual for " + std::string(componentName) + " equation.");
126  }
127  else if (residual > tolerance) {
128  report.setReservoirFailed({type, CR::Severity::Normal, componentIdx});
129  }
130 
131  report.setReservoirConvergenceMetric(type, componentIdx, residual, tolerance);
132  }
133 }
134 
135 template <class TypeTag>
136 NonlinearSystem<TypeTag>::
137 NonlinearSystem(Simulator& simulator,
138  const ModelParameters& param,
139  WellModel& wellModel,
140  const bool terminal_output)
141  : simulator_(simulator)
142  , grid_(simulator_.vanguard().grid())
143  , terminal_output_(terminal_output)
144  , param_(param)
145  , well_model_(wellModel)
146  , current_relaxation_(1.0)
147  , dx_old_(simulator_.model().numGridDof())
148 {}
149 
150 template <class TypeTag>
151 void
152 NonlinearSystem<TypeTag>::
153 initialLinearization(SimulatorReportSingle& report,
154  const int,
155  const int,
156  const SimulatorTimerInterface&)
157 {
158  failureReport_ = SimulatorReportSingle();
159 
160  Dune::Timer perfTimer;
161  perfTimer.start();
162  report.total_linearizations = 1;
163 
164  try {
165  report += this->assembleReservoir(well_model_);
166  report.assemble_time += perfTimer.stop();
167 
168  // Mark timestep initialized after assembling, because well models can use
169  // needsTimestepInit() to trigger per-step setup during assembly.
170  simulator_.problem().markTimestepInitialized();
171  }
172  catch (...) {
173  report.assemble_time += perfTimer.stop();
174  failureReport_ += report;
175  throw;
176  }
177 }
178 
179 template <class TypeTag>
180 SimulatorReportSingle
181 NonlinearSystem<TypeTag>::
182 prepareStep(const SimulatorTimerInterface& timer)
183 {
184  SimulatorReportSingle report;
185  Dune::Timer perfTimer;
186  perfTimer.start();
187 
188  const int lastStepFailed = timer.lastStepFailed();
189  if (grid_.comm().size() > 1 && grid_.comm().max(lastStepFailed) != grid_.comm().min(lastStepFailed)) {
190  OPM_THROW(std::runtime_error,
191  "Misalignment of the parallel simulation run in prepareStep "
192  "- the previous step succeeded on some ranks but failed on others.");
193  }
194 
195  if (lastStepFailed) {
196  simulator_.problem().updateFailed();
197  }
198  else {
199  simulator_.problem().advanceTimeLevel();
200  }
201 
202  // The model still needs the report-step time context even though flow owns time stepping.
203  simulator_.setTime(timer.simulationTimeElapsed());
204  simulator_.setTimeStepSize(timer.currentStepLength());
205 
206  simulator_.problem().resetIterationForNewTimestep();
207  simulator_.problem().beginTimeStep();
208 
209  report.pre_post_time += perfTimer.stop();
210  return report;
211 }
212 
213 template <class TypeTag>
214 template <class WellModelType>
215 SimulatorReportSingle
216 NonlinearSystem<TypeTag>::
217 assembleReservoir(WellModelType& wellModel)
218 {
219  simulator_.problem().beginIteration();
220  simulator_.model().linearizer().linearizeDomain();
221  simulator_.problem().endIteration();
222  return wellModel.lastReport();
223 }
224 
225 template <class TypeTag>
226 template <class ModelParametersType>
227 void
228 NonlinearSystem<TypeTag>::
229 applyTUNING(ModelParametersType& param,
230  const Tuning& tuning)
231 {
232  param.tolerance_cnv_ = tuning.TRGCNV;
233  param.tolerance_cnv_relaxed_ = tuning.XXXCNV;
234  param.tolerance_mb_ = tuning.TRGMBE;
235  param.tolerance_mb_relaxed_ = tuning.XXXMBE;
236  param.newton_max_iter_ = tuning.NEWTMX;
237  param.newton_min_iter_ = tuning.NEWTMN;
238 }
239 
240 template <class TypeTag>
241 template <class ModelParametersType>
242 void
243 NonlinearSystem<TypeTag>::
244 applyTUNINGDP(ModelParametersType& param,
245  const TuningDp& tuning_dp)
246 {
247  param.tolerance_max_dp_ = tuning_dp.TRGDDP;
248  param.tolerance_max_ds_ = tuning_dp.TRGDDS;
249  param.tolerance_max_drs_ = tuning_dp.TRGDDRS;
250  param.tolerance_max_drv_ = tuning_dp.TRGDDRV;
251 }
252 
253 template <class TypeTag>
254 template <class ValueType>
255 std::tuple<ValueType, ValueType>
256 NonlinearSystem<TypeTag>::
257 convergenceReduction(Parallel::Communication comm,
258  const ValueType primaryVolumeLocal,
259  const ValueType secondaryVolumeLocal,
260  std::vector<ValueType>& sumValues,
261  std::vector<ValueType>& maxValues,
262  std::vector<ValueType>& averagedValues)
263 {
264  OPM_TIMEBLOCK(convergenceReduction);
265 
266  ValueType primaryVolume = primaryVolumeLocal;
267  ValueType secondaryVolume = secondaryVolumeLocal;
268 
269  if (comm.size() > 1) {
270  std::vector<ValueType> sumBuffer;
271  std::vector<ValueType> maxBuffer;
272  const int numComp = averagedValues.size();
273  sumBuffer.reserve(2 * numComp + 2);
274  maxBuffer.reserve(numComp);
275 
276  for (int compIdx = 0; compIdx < numComp; ++compIdx) {
277  sumBuffer.push_back(averagedValues[compIdx]);
278  sumBuffer.push_back(sumValues[compIdx]);
279  maxBuffer.push_back(maxValues[compIdx]);
280  }
281 
282  sumBuffer.push_back(primaryVolume);
283  sumBuffer.push_back(secondaryVolume);
284 
285  comm.sum(sumBuffer.data(), sumBuffer.size());
286  comm.max(maxBuffer.data(), maxBuffer.size());
287 
288  for (int compIdx = 0, buffIdx = 0; compIdx < numComp; ++compIdx, ++buffIdx) {
289  averagedValues[compIdx] = sumBuffer[buffIdx];
290  ++buffIdx;
291  sumValues[compIdx] = sumBuffer[buffIdx];
292  }
293 
294  for (int compIdx = 0; compIdx < numComp; ++compIdx) {
295  maxValues[compIdx] = maxBuffer[compIdx];
296  }
297 
298  primaryVolume = sumBuffer[sumBuffer.size() - 2];
299  secondaryVolume = sumBuffer.back();
300  }
301 
302  return {primaryVolume, secondaryVolume};
303 }
304 
305 } // namespace Opm
306 
307 #endif // OPM_FLOW_NONLINEAR_SYSTEM_IMPL_HEADER_INCLUDED
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45