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