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#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>
27#endif
28
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
40namespace Opm
41{
42
43template <typename TypeTag>
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
54template <typename TypeTag>
55void
57{
58 this->computeConnectionAreaFraction();
59
60 for (auto& aquifer : this->aquifers) {
61 aquifer->initialSolutionApplied();
62 }
63}
64
65template <typename TypeTag>
66void
67BlackoilAquiferModel<TypeTag>::initFromRestart(const data::Aquifers& aquiferSoln)
68{
69 this->computeConnectionAreaFraction();
70
71 for (auto& aquifer : this->aquifers) {
72 aquifer->initFromRestart(aquiferSoln);
73 }
74}
75
76template <typename TypeTag>
77void
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
90template <typename TypeTag>
91void
93{}
94
95template <typename TypeTag>
96void
98{
99 for (auto& aquifer : this->aquifers) {
100 aquifer->beginTimeStep();
101 }
102}
103
104template <typename TypeTag>
105template <class Context>
106void
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
117template <typename TypeTag>
118void
120 unsigned globalSpaceIdx,
121 unsigned timeIdx) const
122{
123 for (auto& aquifer : this->aquifers) {
124 aquifer->addToSource(rates, globalSpaceIdx, timeIdx);
125 }
126}
127
128template <typename TypeTag>
129void
131{}
132
133template <typename TypeTag>
134void
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
148template <typename TypeTag>
149void
151{}
152
153template <typename TypeTag>
154template <class Restarter>
155void
157{
158 // TODO (?)
159 throw std::logic_error("BlackoilAquiferModel::serialize() is not yet implemented");
160}
161
162template <typename TypeTag>
163template <class Restarter>
164void
166{
167 // TODO (?)
168 throw std::logic_error("BlackoilAquiferModel::deserialize() is not yet implemented");
169}
170
171// Initialize the aquifers in the deck
172template <typename TypeTag>
174{
175 if (this->simulator_.vanguard().eclState().aquifer().active()) {
176 this->initializeStaticAquifers();
177 }
178
179 if (this->needRestartDynamicAquifers()) {
180 this->initializeRestartDynamicAquifers();
181 }
182}
183
184template<typename TypeTag>
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
195template<typename TypeTag>
196template<class Serializer>
198serializeOp(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
219template <typename TypeTag>
221{
222 const auto rstStep = this->simulator_.vanguard().eclState()
223 .getInitConfig().getRestartStep() - 1;
224
225 this->createDynamicAquifers(rstStep);
226}
227
228template <typename TypeTag>
229void 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
274template <typename TypeTag>
275bool 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
288template <typename TypeTag>
289template <typename AquiferType, typename AquiferData>
290std::unique_ptr<AquiferType>
291BlackoilAquiferModel<TypeTag>::
292createAnalyticAquiferPointer(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("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
312template <typename TypeTag>
313void 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::find_if(std::begin(this->aquifers),
320 std::end(this->aquifers),
321 [Id = id](const auto& aquPtr)
322 {
323 return aquPtr->aquiferID() == Id;
324 });
325
326 if (aquPos == std::end(this->aquifers)) {
327 // An aquifer with this 'id' does not yet exist in
328 // the collection managed by this object. Create it.
329 auto aquFluxPtr = this->template createAnalyticAquiferPointer
330 <AquiferConstantFlux<TypeTag>>(aquFlux, id, "Constant Flux");
331
332 if (aquFluxPtr != nullptr) {
333 this->aquifers.push_back(std::move(aquFluxPtr));
334 }
335 }
336 else {
337 auto aquFluxPtr = dynamic_cast<AquiferConstantFlux<TypeTag>*>(aquPos->get());
338 if (aquFluxPtr == nullptr) {
339 // If the aquifers can return types easily, we might be able
340 // to give a better message with type information.
341 const auto msg =
342 fmt::format("Aquifer {} is updated with constant flux "
343 "aquifer keyword AQUFLUX at report step {}, "
344 "while it might be specified to be a "
345 "different type of aquifer before this. "
346 "We do not support the conversion between "
347 "different types of aquifer.\n", id, episode_index);
348
349 OPM_THROW(std::runtime_error, msg);
350 }
351
352 aquFluxPtr->updateAquifer(aquFlux);
353 }
354 }
355}
356
357template <typename TypeTag>
358void BlackoilAquiferModel<TypeTag>::computeConnectionAreaFraction() const
359{
360 auto maxAquID =
361 std::accumulate(this->aquifers.begin(), this->aquifers.end(), 0,
362 [](const int aquID, const auto& aquifer)
363 { return std::max(aquID, aquifer->aquiferID()); });
364
365 maxAquID = this->simulator_.vanguard().grid().comm().max(maxAquID);
366
367 auto totalConnArea = std::vector<Scalar>(maxAquID, 0.0);
368 for (const auto& aquifer : this->aquifers) {
369 totalConnArea[aquifer->aquiferID() - 1] += aquifer->totalFaceArea();
370 }
371
372 this->simulator_.vanguard().grid().comm().sum(totalConnArea.data(), maxAquID);
373
374 for (auto& aquifer : this->aquifers) {
375 aquifer->computeFaceAreaFraction(totalConnArea);
376 }
377}
378
379} // namespace Opm
380
381#endif
Definition: AquiferCarterTracy.hpp:42
Definition: AquiferConstantFlux.hpp:39
Definition: AquiferFetkovich.hpp:38
Definition: AquiferNumerical.hpp:45
void endTimeStep() override
Definition: AquiferNumerical.hpp:129
Class for handling the blackoil aquifer model.
Definition: BlackoilAquiferModel.hpp:50
void beginTimeStep()
Definition: BlackoilAquiferModel_impl.hpp:97
void initFromRestart(const data::Aquifers &aquiferSoln)
Definition: BlackoilAquiferModel_impl.hpp:67
BlackoilAquiferModel(Simulator &simulator)
Definition: BlackoilAquiferModel_impl.hpp:44
void init()
Definition: BlackoilAquiferModel_impl.hpp:173
void endEpisode()
Definition: BlackoilAquiferModel_impl.hpp:150
void endTimeStep()
Definition: BlackoilAquiferModel_impl.hpp:135
void serializeOp(Serializer &serializer)
Definition: BlackoilAquiferModel_impl.hpp:198
void deserialize(Restarter &res)
Definition: BlackoilAquiferModel_impl.hpp:165
data::Aquifers aquiferData() const
Definition: BlackoilAquiferModel_impl.hpp:185
void serialize(Restarter &res)
Definition: BlackoilAquiferModel_impl.hpp:156
void beginIteration()
Definition: BlackoilAquiferModel_impl.hpp:92
void addToSource(RateVector &rates, const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Definition: BlackoilAquiferModel_impl.hpp:107
void initialSolutionApplied()
Definition: BlackoilAquiferModel_impl.hpp:56
void endIteration()
Definition: BlackoilAquiferModel_impl.hpp:130
void beginEpisode()
Definition: BlackoilAquiferModel_impl.hpp:78
Definition: SupportsFaceTag.hpp:34
Definition: blackoilboundaryratevector.hh:39