opm-simulators
BlackoilAquiferModel_impl.hpp
1 /*
2  Copyright 2017 TNO - Heat Transfer & Fluid Dynamics, Modelling & Optimization of the Subsurface
3  Copyright 2017 Statoil ASA.
4 
5  This file is part of the Open Porous Media project (OPM).
6 
7  OPM is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  OPM is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with OPM. If not, see <http://www.gnu.org/licenses/>.
19 */
20 #ifndef OPM_BLACKOILAQUIFERMODEL_IMPL_HEADER_INCLUDED
21 #define OPM_BLACKOILAQUIFERMODEL_IMPL_HEADER_INCLUDED
22 
23 // Improve IDE experience
24 #ifndef OPM_BLACKOILAQUIFERMODEL_HEADER_INCLUDED
25 #include <config.h>
26 #include <opm/simulators/aquifers/BlackoilAquiferModel.hpp>
27 #endif
28 
29 #include <opm/simulators/aquifers/AquiferConstantFlux.hpp>
30 
31 #include <opm/common/ErrorMacros.hpp>
32 
33 #include <fmt/format.h>
34 
35 #include <algorithm>
36 #include <memory>
37 #include <stdexcept>
38 #include <string_view>
39 
40 namespace Opm
41 {
42 
43 template <typename TypeTag>
44 BlackoilAquiferModel<TypeTag>::BlackoilAquiferModel(Simulator& simulator)
45  : simulator_(simulator)
46 {
47  // Grid needs to support Facetag
48  using Grid = std::remove_const_t<std::remove_reference_t<decltype(simulator.vanguard().grid())>>;
49  static_assert(SupportsFaceTag<Grid>::value, "Grid has to support assumptions about face tag.");
50 
51  init();
52 }
53 
54 template <typename TypeTag>
55 void
56 BlackoilAquiferModel<TypeTag>::initialSolutionApplied()
57 {
58  this->computeConnectionAreaFraction();
59 
60  for (auto& aquifer : this->aquifers) {
61  aquifer->initialSolutionApplied();
62  }
63 }
64 
65 template <typename TypeTag>
66 void
67 BlackoilAquiferModel<TypeTag>::initFromRestart(const data::Aquifers& aquiferSoln)
68 {
69  this->computeConnectionAreaFraction();
70 
71  for (auto& aquifer : this->aquifers) {
72  aquifer->initFromRestart(aquiferSoln);
73  }
74 }
75 
76 template <typename TypeTag>
77 void
78 BlackoilAquiferModel<TypeTag>::beginEpisode()
79 {
80  // Probably function name beginReportStep() is more appropriate.
81  //
82  // Basically, we want to update the aquifer related information from
83  // SCHEDULE setup in this section it is the beginning of a report step
84 
85  this->createDynamicAquifers(this->simulator_.episodeIndex());
86 
87  this->computeConnectionAreaFraction();
88 }
89 
90 template <typename TypeTag>
91 void
92 BlackoilAquiferModel<TypeTag>::beginIteration()
93 {}
94 
95 template <typename TypeTag>
96 void
97 BlackoilAquiferModel<TypeTag>::beginTimeStep()
98 {
99  for (auto& aquifer : this->aquifers) {
100  aquifer->beginTimeStep();
101  }
102 }
103 
104 template <typename TypeTag>
105 template <class Context>
106 void
107 BlackoilAquiferModel<TypeTag>::addToSource(RateVector& rates,
108  const Context& context,
109  unsigned spaceIdx,
110  unsigned timeIdx) const
111 {
112  for (auto& aquifer : this->aquifers) {
113  aquifer->addToSource(rates, context, spaceIdx, timeIdx);
114  }
115 }
116 
117 template <typename TypeTag>
118 void
119 BlackoilAquiferModel<TypeTag>::addToSource(RateVector& rates,
120  unsigned globalSpaceIdx,
121  unsigned timeIdx) const
122 {
123  for (auto& aquifer : this->aquifers) {
124  aquifer->addToSource(rates, globalSpaceIdx, timeIdx);
125  }
126 }
127 
128 template <typename TypeTag>
129 void
130 BlackoilAquiferModel<TypeTag>::endIteration()
131 {}
132 
133 template <typename TypeTag>
134 void
135 BlackoilAquiferModel<TypeTag>::endTimeStep()
136 {
137  using NumAq = AquiferNumerical<TypeTag>;
138 
139  for (const auto& aquifer : this->aquifers) {
140  aquifer->endTimeStep();
141  const NumAq* num = dynamic_cast<const NumAq*>(aquifer.get());
142  if (num) {
143  this->simulator_.vanguard().grid().comm().barrier();
144  }
145  }
146 }
147 
148 template <typename TypeTag>
149 void
150 BlackoilAquiferModel<TypeTag>::endEpisode()
151 {}
152 
153 template <typename TypeTag>
154 template <class Restarter>
155 void
156 BlackoilAquiferModel<TypeTag>::serialize(Restarter& /* res */)
157 {
158  // TODO (?)
159  throw std::logic_error("BlackoilAquiferModel::serialize() is not yet implemented");
160 }
161 
162 template <typename TypeTag>
163 template <class Restarter>
164 void
165 BlackoilAquiferModel<TypeTag>::deserialize(Restarter& /* res */)
166 {
167  // TODO (?)
168  throw std::logic_error("BlackoilAquiferModel::deserialize() is not yet implemented");
169 }
170 
171 // Initialize the aquifers in the deck
172 template <typename TypeTag>
173 void BlackoilAquiferModel<TypeTag>::init()
174 {
175  if (this->simulator_.vanguard().eclState().aquifer().active()) {
176  this->initializeStaticAquifers();
177  }
178 
179  if (this->needRestartDynamicAquifers()) {
180  this->initializeRestartDynamicAquifers();
181  }
182 }
183 
184 template<typename TypeTag>
185 data::Aquifers BlackoilAquiferModel<TypeTag>::aquiferData() const
186 {
187  data::Aquifers data;
188  for (const auto& aqu : this->aquifers) {
189  data.insert_or_assign(aqu->aquiferID(), aqu->aquiferData());
190  }
191 
192  return data;
193 }
194 
195 template<typename TypeTag>
196 template<class Serializer>
197 void BlackoilAquiferModel<TypeTag>::
198 serializeOp(Serializer& serializer)
199 {
200  for (auto& aiPtr : this->aquifers) {
201  auto* ct = dynamic_cast<AquiferCarterTracy<TypeTag>*>(aiPtr.get());
202  auto* fetp = dynamic_cast<AquiferFetkovich<TypeTag>*>(aiPtr.get());
203  auto* num = dynamic_cast<AquiferNumerical<TypeTag>*>(aiPtr.get());
204  auto* flux = dynamic_cast<AquiferConstantFlux<TypeTag>*>(aiPtr.get());
205  if (ct) {
206  serializer(*ct);
207  } else if (fetp) {
208  serializer(*fetp);
209  } else if (num) {
210  serializer(*num);
211  } else if (flux) {
212  serializer(*flux);
213  } else {
214  OPM_THROW(std::logic_error, "Error serializing BlackoilAquiferModel: unknown aquifer type");
215  }
216  }
217 }
218 
219 template <typename TypeTag>
220 void BlackoilAquiferModel<TypeTag>::initializeRestartDynamicAquifers()
221 {
222  const auto rstStep = this->simulator_.vanguard().eclState()
223  .getInitConfig().getRestartStep() - 1;
224 
225  this->createDynamicAquifers(rstStep);
226 }
227 
228 template <typename TypeTag>
229 void BlackoilAquiferModel<TypeTag>::initializeStaticAquifers()
230 {
231  const auto& aquifer =
232  this->simulator_.vanguard().eclState().aquifer();
233 
234  for (const auto& aquCT : aquifer.ct()) {
235  auto aquCTPtr = this->template createAnalyticAquiferPointer
236  <AquiferCarterTracy<TypeTag>>(aquCT, aquCT.aquiferID, "Carter-Tracy");
237 
238  if (aquCTPtr != nullptr) {
239  this->aquifers.push_back(std::move(aquCTPtr));
240  }
241  }
242 
243  for (const auto& aquFetp : aquifer.fetp()) {
244  auto aquFetpPtr = this->template createAnalyticAquiferPointer
245  <AquiferFetkovich<TypeTag>>(aquFetp, aquFetp.aquiferID, "Fetkovich");
246 
247  if (aquFetpPtr != nullptr) {
248  this->aquifers.push_back(std::move(aquFetpPtr));
249  }
250  }
251 
252  for (const auto& [id, aquFlux] : aquifer.aquflux()) {
253  // Make sure not dummy constant flux aquifers
254  if (! aquFlux.active) { continue; }
255 
256  auto aquFluxPtr = this->template createAnalyticAquiferPointer
257  <AquiferConstantFlux<TypeTag>>(aquFlux, id, "Constant Flux");
258 
259  if (aquFluxPtr != nullptr) {
260  this->aquifers.push_back(std::move(aquFluxPtr));
261  }
262  }
263 
264  if (aquifer.hasNumericalAquifer()) {
265  for (const auto& aquNum : aquifer.numericalAquifers().aquifers()) {
266  auto aquNumPtr = std::make_unique<AquiferNumerical<TypeTag>>
267  (aquNum.second, this->simulator_);
268 
269  this->aquifers.push_back(std::move(aquNumPtr));
270  }
271  }
272 }
273 
274 template <typename TypeTag>
275 bool BlackoilAquiferModel<TypeTag>::needRestartDynamicAquifers() const
276 {
277  const auto& initconfig =
278  this->simulator_.vanguard().eclState().getInitConfig();
279 
280  if (! initconfig.restartRequested()) {
281  return false;
282  }
283 
284  return this->simulator_.vanguard()
285  .schedule()[initconfig.getRestartStep() - 1].hasAnalyticalAquifers();
286 }
287 
288 template <typename TypeTag>
289 template <typename AquiferType, typename AquiferData>
290 std::unique_ptr<AquiferType>
291 BlackoilAquiferModel<TypeTag>::
292 createAnalyticAquiferPointer(const AquiferData& aqData,
293  const int aquiferID,
294  std::string_view aqType) const
295 {
296  const auto& connections =
297  this->simulator_.vanguard().eclState().aquifer().connections();
298 
299  if (! connections.hasAquiferConnections(aquiferID)) {
300  const auto msg = fmt::format(fmt::runtime("No valid connections for {} aquifer {}. "
301  "Aquifer {} will be ignored."),
302  aqType, aquiferID, aquiferID);
303  OpmLog::warning(msg);
304 
305  return {};
306  }
307 
308  return std::make_unique<AquiferType>
309  (connections.getConnections(aquiferID), this->simulator_, aqData);
310 }
311 
312 template <typename TypeTag>
313 void BlackoilAquiferModel<TypeTag>::createDynamicAquifers(const int episode_index)
314 {
315  const auto& sched = this->simulator_.vanguard().schedule()[episode_index];
316 
317  for (const auto& [id, aquFlux] : sched.aqufluxs) {
318  auto aquPos =
319  std::ranges::find_if(this->aquifers,
320  [Id = id](const auto& aquPtr)
321  { return aquPtr->aquiferID() == Id; });
322 
323  if (aquPos == std::end(this->aquifers)) {
324  // An aquifer with this 'id' does not yet exist in
325  // the collection managed by this object. Create it.
326  auto aquFluxPtr = this->template createAnalyticAquiferPointer
327  <AquiferConstantFlux<TypeTag>>(aquFlux, id, "Constant Flux");
328 
329  if (aquFluxPtr != nullptr) {
330  this->aquifers.push_back(std::move(aquFluxPtr));
331  }
332  }
333  else {
334  auto aquFluxPtr = dynamic_cast<AquiferConstantFlux<TypeTag>*>(aquPos->get());
335  if (aquFluxPtr == nullptr) {
336  // If the aquifers can return types easily, we might be able
337  // to give a better message with type information.
338  const auto msg =
339  fmt::format("Aquifer {} is updated with constant flux "
340  "aquifer keyword AQUFLUX at report step {}, "
341  "while it might be specified to be a "
342  "different type of aquifer before this. "
343  "We do not support the conversion between "
344  "different types of aquifer.\n", id, episode_index);
345 
346  OPM_THROW(std::runtime_error, msg);
347  }
348 
349  aquFluxPtr->updateAquifer(aquFlux);
350  }
351  }
352 }
353 
354 template <typename TypeTag>
355 void BlackoilAquiferModel<TypeTag>::computeConnectionAreaFraction() const
356 {
357  auto maxAquID =
358  std::accumulate(this->aquifers.begin(), this->aquifers.end(), 0,
359  [](const int aquID, const auto& aquifer)
360  { return std::max(aquID, aquifer->aquiferID()); });
361 
362  maxAquID = this->simulator_.vanguard().grid().comm().max(maxAquID);
363 
364  auto totalConnArea = std::vector<Scalar>(maxAquID, 0.0);
365  for (const auto& aquifer : this->aquifers) {
366  totalConnArea[aquifer->aquiferID() - 1] += aquifer->totalFaceArea();
367  }
368 
369  this->simulator_.vanguard().grid().comm().sum(totalConnArea.data(), maxAquID);
370 
371  for (auto& aquifer : this->aquifers) {
372  aquifer->computeFaceAreaFraction(totalConnArea);
373  }
374 }
375 
376 } // namespace Opm
377 
378 #endif
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: blackoilbioeffectsmodules.hh:45