opm-simulators
AquiferCarterTracy.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 #ifndef OPM_AQUIFERCT_HEADER_INCLUDED
22 #define OPM_AQUIFERCT_HEADER_INCLUDED
23 
24 #include <opm/common/utility/numeric/linearInterpolation.hpp>
25 
26 #include <opm/input/eclipse/EclipseState/Aquifer/AquiferCT.hpp>
27 
28 #include <opm/output/data/Aquifer.hpp>
29 
30 #include <opm/simulators/aquifers/AquiferAnalytical.hpp>
31 
32 #include <cstddef>
33 #include <stdexcept>
34 #include <utility>
35 #include <vector>
36 
37 namespace Opm
38 {
39 
40 template <typename TypeTag>
41 class AquiferCarterTracy : public AquiferAnalytical<TypeTag>
42 {
43 public:
45 
46  using typename Base::BlackoilIndices;
47  using typename Base::ElementContext;
48  using typename Base::Eval;
49  using typename Base::FluidState;
50  using typename Base::FluidSystem;
51  using typename Base::IntensiveQuantities;
52  using typename Base::RateVector;
53  using typename Base::Scalar;
54  using typename Base::Simulator;
55  using typename Base::ElementMapper;
56 
57  AquiferCarterTracy(const std::vector<Aquancon::AquancCell>& connections,
58  const Simulator& simulator,
59  const AquiferCT::AQUCT_data& aquct_data)
60  : Base(aquct_data.aquiferID, connections, simulator)
61  , aquct_data_(aquct_data)
62  {}
63 
64  static AquiferCarterTracy serializationTestObject(const Simulator& simulator)
65  {
66  AquiferCarterTracy result({}, simulator, {});
67 
68  result.pressure_previous_ = {1.0, 2.0, 3.0};
69  result.pressure_current_ = {4.0, 5.0};
70  result.Qai_ = {{6.0}};
71  result.rhow_ = 7.0;
72  result.W_flux_ = 8.0;
73  result.fluxValue_ = 9.0;
74  result.dimensionless_time_ = 10.0;
75  result.dimensionless_pressure_ = 11.0;
76 
77  return result;
78  }
79 
80  void endTimeStep() override
81  {
82  for (const auto& q : this->Qai_) {
83  this->W_flux_ += q * this->simulator_.timeStepSize();
84  }
85  this->fluxValue_ = this->W_flux_.value();
86  const auto& comm = this->simulator_.vanguard().grid().comm();
87  comm.sum(&this->fluxValue_, 1);
88  }
89 
90  data::AquiferData aquiferData() const override
91  {
92  data::AquiferData data;
93  data.aquiferID = this->aquiferID();
94  // TODO: not sure how to get this pressure value yet
95  data.pressure = this->pa0_;
96  data.fluxRate = 0.;
97  for (const auto& q : this->Qai_) {
98  data.fluxRate += q.value();
99  }
100  data.volume = this->W_flux_.value();
101  data.initPressure = this->pa0_;
102 
103  auto* aquCT = data.typeData.template create<data::AquiferType::CarterTracy>();
104 
105  aquCT->dimensionless_time = this->dimensionless_time_;
106  aquCT->dimensionless_pressure = this->dimensionless_pressure_;
107  aquCT->influxConstant = this->aquct_data_.influxConstant();
108 
109  if (!this->co2store_or_h2store_()) {
110  aquCT->timeConstant = this->aquct_data_.timeConstant();
111  aquCT->waterDensity = this->aquct_data_.waterDensity();
112  aquCT->waterViscosity = this->aquct_data_.waterViscosity();
113  } else {
114  aquCT->waterDensity = this->rhow_;
115  aquCT->timeConstant = this->Tc_;
116  const auto x = this->aquct_data_.porosity * this->aquct_data_.total_compr * this->aquct_data_.inner_radius * this->aquct_data_.inner_radius;
117  aquCT->waterViscosity = this->Tc_ * this->aquct_data_.permeability / x;
118  }
119 
120  return data;
121  }
122 
123  template<class Serializer>
124  void serializeOp(Serializer& serializer)
125  {
126  serializer(static_cast<Base&>(*this));
127  serializer(fluxValue_);
128  serializer(dimensionless_time_);
129  serializer(dimensionless_pressure_);
130  }
131 
132  bool operator==(const AquiferCarterTracy& rhs) const
133  {
134  return static_cast<const AquiferAnalytical<TypeTag>&>(*this) == rhs &&
135  this->fluxValue_ == rhs.fluxValue_ &&
136  this->dimensionless_time_ == rhs.dimensionless_time_ &&
137  this->dimensionless_pressure_ == rhs.dimensionless_pressure_;
138  }
139 
140 protected:
141  // Variables constants
142  AquiferCT::AQUCT_data aquct_data_;
143 
144  Scalar beta_; // Influx constant
145  // TODO: it is possible it should be a AD variable
146  Scalar fluxValue_{0}; // value of flux
147 
148  Scalar dimensionless_time_{0};
149  Scalar dimensionless_pressure_{0};
150 
151  void assignRestartData(const data::AquiferData& xaq) override
152  {
153  this->fluxValue_ = xaq.volume;
154  this->rhow_ = this->aquct_data_.waterDensity();
155  }
156 
157  std::pair<Scalar, Scalar>
158  getInfluenceTableValues(const Scalar td_plus_dt)
159  {
160  // We use the opm-common numeric linear interpolator
161  this->dimensionless_pressure_ =
162  linearInterpolation(this->aquct_data_.dimensionless_time,
163  this->aquct_data_.dimensionless_pressure,
164  this->dimensionless_time_);
165 
166  const auto PItd =
167  linearInterpolation(this->aquct_data_.dimensionless_time,
168  this->aquct_data_.dimensionless_pressure,
169  td_plus_dt);
170 
171  const auto PItdprime =
172  linearInterpolationDerivative(this->aquct_data_.dimensionless_time,
173  this->aquct_data_.dimensionless_pressure,
174  td_plus_dt);
175 
176  return std::make_pair(PItd, PItdprime);
177  }
178 
179  Scalar dpai(const int idx) const
180  {
181  const auto gdz =
182  this->gravity_() * (this->cell_depth_.at(idx) - this->aquiferDepth());
183 
184  const auto dp = this->pa0_ + this->rhow_*gdz
185  - this->pressure_previous_.at(idx);
186 
187  return dp;
188  }
189 
190  // This function implements Eqs 5.8 and 5.9 of the EclipseTechnicalDescription
191  std::pair<Scalar, Scalar>
192  calculateEqnConstants(const int idx, const Simulator& simulator)
193  {
194  const Scalar td_plus_dt = (simulator.timeStepSize() + simulator.time()) / this->Tc_;
195  this->dimensionless_time_ = simulator.time() / this->Tc_;
196 
197  const auto [PItd, PItdprime] = this->getInfluenceTableValues(td_plus_dt);
198 
199  const auto denom = this->Tc_ * (PItd - this->dimensionless_time_*PItdprime);
200  const auto a = (this->beta_*dpai(idx) - this->fluxValue_*PItdprime) / denom;
201  const auto b = this->beta_ / denom;
202 
203  return std::make_pair(a, b);
204  }
205 
206  std::size_t pvtRegionIdx() const
207  {
208  return this->aquct_data_.pvttableID - 1;
209  }
210 
211  // This function implements Eq 5.7 of the EclipseTechnicalDescription
212  void calculateInflowRate(int idx, const Simulator& simulator) override
213  {
214  const auto [a, b] = this->calculateEqnConstants(idx, simulator);
215 
216  this->Qai_.at(idx) = this->alphai_.at(idx) *
217  (a - b*(this->pressure_current_.at(idx) - this->pressure_previous_.at(idx)));
218  }
219 
220  void calculateAquiferConstants() override
221  {
222  this->Tc_ = this->co2store_or_h2store_()
223  ? this->timeConstantCO2Store()
224  : this->aquct_data_.timeConstant();
225 
226  this->beta_ = this->aquct_data_.influxConstant();
227  }
228 
229  void calculateAquiferCondition() override
230  {
231  if (this->solution_set_from_restart_) {
232  return;
233  }
234 
235  if (! this->aquct_data_.initial_pressure.has_value()) {
236  this->aquct_data_.initial_pressure =
237  this->calculateReservoirEquilibrium();
238 
239  const auto& tables = this->simulator_.vanguard()
240  .eclState().getTableManager();
241 
242  this->aquct_data_.finishInitialisation(tables);
243  }
244 
245  this->pa0_ = this->aquct_data_.initial_pressure.value();
246  if (this->aquct_data_.initial_temperature.has_value()) {
247  this->Ta0_ = this->aquct_data_.initial_temperature.value();
248  }
249 
250  this->rhow_ = this->co2store_or_h2store_()
251  ? this->waterDensityCO2Store()
252  : this->aquct_data_.waterDensity();
253  }
254 
255  Scalar aquiferDepth() const override
256  {
257  return this->aquct_data_.datum_depth;
258  }
259 
260 private:
261  Scalar timeConstantCO2Store() const
262  {
263  const Scalar press = this->aquct_data_.initial_pressure.value();
264  const auto temp = this->reservoirTemperatureCO2Store();
265 
266  auto waterViscosity = Scalar { 0 };
267  if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
268  const auto rs = Scalar { 0 }; // no dissolved CO2
269  waterViscosity = FluidSystem::oilPvt()
270  .viscosity(pvtRegionIdx(), temp, press, rs);
271  }
272  else if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
273  const auto salt = Scalar { 0 };
274  const auto rsw = Scalar { 0 };
275  waterViscosity = FluidSystem::waterPvt()
276  .viscosity(pvtRegionIdx(), temp, press, rsw, salt);
277  }
278  else {
279  OPM_THROW(std::runtime_error, "water or oil phase is needed to run CO2Store.");
280  }
281 
282  const auto x = this->aquct_data_.porosity * this->aquct_data_.total_compr
283  * this->aquct_data_.inner_radius * this->aquct_data_.inner_radius;
284 
285  return waterViscosity * x / this->aquct_data_.permeability;
286  }
287 
288  Scalar waterDensityCO2Store() const
289  {
290  const Scalar press = this->aquct_data_.initial_pressure.value();
291  const auto temp = this->reservoirTemperatureCO2Store();
292 
293  if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
294  const auto& pvt = FluidSystem::oilPvt();
295  const auto reg = this->pvtRegionIdx();
296 
297  const auto rs = Scalar { 0 }; // no dissolved CO2
298  return pvt.inverseFormationVolumeFactor(reg, temp, press, rs)
299  * pvt.oilReferenceDensity(reg);
300  }
301  else if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
302  const auto& pvt = FluidSystem::waterPvt();
303  const auto reg = this->pvtRegionIdx();
304 
305  const auto salinity = Scalar { 0 };
306  const auto rsw = Scalar { 0 };
307 
308  return pvt.inverseFormationVolumeFactor(reg, temp, press, rsw, salinity)
309  * pvt.waterReferenceDensity(reg);
310  }
311  else {
312  OPM_THROW(std::runtime_error, "water or oil phase is needed to run CO2Store.");
313  }
314  }
315 
316  Scalar reservoirTemperatureCO2Store() const
317  {
318  return this->aquct_data_.initial_temperature.has_value()
319  ? this->aquct_data_.initial_temperature.value()
320  : FluidSystem::reservoirTemperature();
321  }
322 
323 }; // class AquiferCarterTracy
324 
325 } // namespace Opm
326 
327 #endif
Definition: AquiferCarterTracy.hpp:41
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: blackoilbioeffectsmodules.hh:45
Definition: AquiferAnalytical.hpp:56