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