opm-simulators
CompWellModel_impl.hpp
1 /*
2  Copyright 2024 SINTEF Digital
3 
4  This file is part of the Open Porous Media project (OPM).
5 
6  OPM is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OPM is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OPM. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef OPM_COMPOSITIONAL_WELL_MODEL_IMPL_HPP
21 #define OPM_COMPOSITIONAL_WELL_MODEL_IMPL_HPP
22 
23 // Improve IDE experience
24 #ifndef OPM_COMPOSITIONAL_WELL_MODEL_HPP
25 #include <config.h>
26 #include <flowexperimental/comp/wells/CompWellModel.hpp>
27 #endif
28 
29 #include <algorithm>
30 
31 #include <opm/input/eclipse/EclipseState/EclipseState.hpp>
32 #include <opm/input/eclipse/Schedule/Schedule.hpp>
33 #include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
34 
35 namespace Opm {
36 
37 template <typename TypeTag>
38 CompWellModel<TypeTag>::CompWellModel(Simulator& simulator, const NewtonIterationContext& /*iter_ctx*/)
39  : WellConnectionModule(*this, simulator.gridView().comm())
40  , simulator_(simulator)
41  , schedule_(simulator.vanguard().schedule())
42  , summary_state_(simulator.vanguard().summaryState())
43  , ecl_state_(simulator.vanguard().eclState())
44  , comm_(simulator.gridView().comm())
45  , comp_config_(ecl_state_.compositionalConfig())
46  , comp_well_states_(comp_config_)
47  , last_valid_comp_well_states_(comp_config_)
48 {
49  local_num_cells_ = simulator.gridView().size(0);
50 }
51 
52 template <typename TypeTag>
53 void
54 CompWellModel<TypeTag>::
55 beginReportStep(unsigned report_step)
56 {
57  // TODO: not considering the parallel running yet
58  report_step_start_events_ = schedule_[report_step].wellgroup_events();
59  wells_ecl_ = schedule_.getWells(report_step);
60 
61  constexpr auto events_mask = ScheduleEvents::WELL_STATUS_CHANGE |
62  ScheduleEvents::REQUEST_OPEN_WELL |
63  ScheduleEvents::REQUEST_SHUT_WELL;
64  for (const auto& well_ecl : wells_ecl_) {
65  if (!well_ecl.hasConnections()) {
66  continue;
67  }
68 
69  if (!report_step_start_events_.hasEvent(well_ecl.name(), events_mask)) {
70  continue;
71  }
72 
73  if (well_ecl.getStatus() == WellStatus::OPEN) {
74  well_open_times_.insert_or_assign(well_ecl.name(), simulator_.time());
75  well_close_times_.erase(well_ecl.name());
76  }
77  else if (well_ecl.getStatus() == WellStatus::SHUT) {
78  well_close_times_.insert_or_assign(well_ecl.name(), simulator_.time());
79  well_open_times_.erase(well_ecl.name());
80  }
81  }
82 
83  initWellConnectionData();
84  initWellState();
85  // Save the initial accepted state for this report step.
86  last_valid_comp_well_states_.copyDynamicStateFrom(comp_well_states_);
87 }
88 
89 template <typename TypeTag>
90 void
91 CompWellModel<TypeTag>::
92 beginTimeStep()
93 {
94  createWellContainer();
95  initWellContainer();
96 }
97 
98 template <typename TypeTag>
99 void
100 CompWellModel<TypeTag>::
101 restoreLastValidState()
102 {
103  comp_well_states_.copyDynamicStateFrom(last_valid_comp_well_states_);
104 }
105 
106 template <typename TypeTag>
107 void
108 CompWellModel<TypeTag>::
109 endTimeStep()
110 {
111  // Persist the accepted well state so failed retries restart from the last
112  // successful timestep rather than from the beginning of the report step.
113  last_valid_comp_well_states_.copyDynamicStateFrom(comp_well_states_);
114 }
115 
116 template <typename TypeTag>
117 void
118 CompWellModel<TypeTag>::
119 init()
120 {
121  simulator_.model().addAuxiliaryModule(this);
122 }
123 
124 template <typename TypeTag>
125 void
126 CompWellModel<TypeTag>::
127 createWellContainer()
128 {
129  // const auto& schedule = simulator_.vanguard().schedule();
130  const auto nw = wells_ecl_.size(); // not considering the parallel running yet
131  well_container_.clear();
132  for (auto w = 0 * nw; w < nw; ++w) {
133  const auto& well_name = wells_ecl_[w].name();
134  if (comp_well_states_.has(well_name)
135  && comp_well_states_[well_name].status == WellStatus::SHUT) {
136  continue;
137  }
138 
139  well_container_.emplace_back(std::make_shared<CompWell<TypeTag>>(wells_ecl_[w], w, well_connection_data_[w]));
140  }
141 }
142 
143 template <typename TypeTag>
144 void
145 CompWellModel<TypeTag>::
146 initWellContainer()
147 {
148  for (auto& well : well_container_) {
149  well->init();
150  }
151 }
152 
153 template <typename TypeTag>
154 void
155 CompWellModel<TypeTag>::
156 initWellConnectionData()
157 {
158  // TODO: we need to consider the parallel running
159  // we can refer to the BlackoilWellModelGeneric::initializeWellPerfData()
160  well_connection_data_.resize(wells_ecl_.size());
161 
162  int well_index = 0;
163  for (const auto& well : wells_ecl_) {
164  int connection_index = 0;
165  const auto& well_connections = well.getConnections();
166  auto& well_connection_data = well_connection_data_[well_index];
167 
168  well_connection_data.reserve(well_connections.size());
169  for (const auto& connection : well_connections) {
170  const auto active_index =
171  this->compressedIndexForInterior(connection.global_index());
172 
173  const auto connIsOpen =
174  connection.state() == Connection::State::OPEN;
175 
176  if (connIsOpen && (active_index >= 0)) {
177  auto& pd = well_connection_data_[well_index].emplace_back();
178 
179  pd.cell_index = active_index;
180  pd.connection_transmissibility_factor = connection.CF();
181  pd.satnum_id = connection.satTableId();
182  pd.ecl_index = connection_index;
183  }
184  ++connection_index;
185  }
186  ++well_index;
187  }
188 
189 }
190 
191 template <typename TypeTag>
192 void
193 CompWellModel<TypeTag>::
194 initWellState()
195 {
196  // TODO: the following might need to be adjusted based on understanding
197  const auto pressIx = []()
198  {
199  if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) ) {
200  return FluidSystem::oilPhaseIdx;
201  }
202  if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) ) {
203  return FluidSystem::gasPhaseIdx;
204  }
205  assert(false && "the usage of oil and gas phase is not correct");
206  return FluidSystem::gasPhaseIdx;
207  }();
208 
209  auto cell_pressure = std::vector<Scalar>(this->local_num_cells_, Scalar{0.});
210  auto cell_mole_fractions = std::vector<std::vector<Scalar>>(this->local_num_cells_,
211  std::vector<Scalar>(FluidSystem::numComponents, Scalar{0.}));
212 
213  auto cell_temperature = std::vector<Scalar>(this->local_num_cells_, Scalar{0.});
214 
215  auto elemCtx = ElementContext { this->simulator_ };
216  const auto& gridView = this->simulator_.vanguard().gridView();
217 
218  OPM_BEGIN_PARALLEL_TRY_CATCH();
219  for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
220  elemCtx.updatePrimaryStencil(elem);
221  elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
222 
223  const auto ix = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
224  const auto& fs = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0).fluidState();
225 
226  cell_pressure[ix] = fs.pressure(pressIx).value();
227  // TODO: we are not simulating dynamic temperature, so all the phases and cells have the same temperature for now
228  cell_temperature[ix] = fs.temperature(0).value();
229  for (unsigned compIdx = 0; compIdx < FluidSystem::numComponents; ++compIdx) {
230  cell_mole_fractions[ix][compIdx] = fs.moleFraction(compIdx).value();
231  }
232  }
233  OPM_END_PARALLEL_TRY_CATCH("ComposotionalWellModel::initializeWellState() failed: ",
234  this->simulator_.vanguard().grid().comm());
235 
236  // Start each report step from freshly initialized schedule state. Retry
237  // recovery still comes from last_valid_comp_well_states_ via
238  // restoreLastValidState()/endTimeStep(). Passing the last valid state as
239  // prev_well_state here would carry dynamic state across report steps, but
240  // that currently changes regression results, so we pass nullptr for now.
241  this->comp_well_states_.init(this->wells_ecl_,
242  cell_pressure, cell_temperature[0], cell_mole_fractions, this->well_connection_data_,
243  this->summary_state_,
244  /*prev_well_state=*/nullptr);
245 }
246 
247 
248 template <typename TypeTag>
249 std::size_t
250 CompWellModel<TypeTag>::
251 compressedIndexForInterior(std::size_t cartesian_cell_idx) const
252 {
253  return simulator_.vanguard().compressedIndexForInterior(cartesian_cell_idx);
254 }
255 
256 template <typename TypeTag>
257 std::vector<int>
258 CompWellModel<TypeTag>::
259 getCellsForConnections(const Well& well) const
260 {
261  std::vector<int> wellCells;
262  // All possible connections of the well
263  const auto& connectionSet = well.getConnections();
264  wellCells.reserve(connectionSet.size());
265 
266  for (const auto& connection : connectionSet)
267  {
268  int compressed_idx = compressedIndexForInterior(connection.global_index());
269  if (compressed_idx >= 0) { // Ignore connections in inactive/remote cells.
270  wellCells.push_back(compressed_idx);
271  }
272  }
273 
274  return wellCells;
275 
276 }
277 
278 template <typename TypeTag>
279 void
280 CompWellModel<TypeTag>::
281 beginIteration()
282 {
283  // do we need to do every iteration here?
284  const auto& grid = simulator_.vanguard().grid();
285  const auto& gridView = grid.leafGridView();
286  ElementContext elemCtx(simulator_);
287  for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
288  elemCtx.updatePrimaryStencil(elem);
289  elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
290  }
291 
292  assemble(simulator_.timeStepSize());
293 }
294 
295 template <typename TypeTag>
296 void
297 CompWellModel<TypeTag>::
298 assemble(const double dt)
299 {
300  const int iterationIdx = simulator_.problem().iterationContext().iteration();
301 
302  if (iterationIdx == 0) {
303  this->calculateExplicitQuantities();
304  }
305  for (auto& well : well_container_) {
306  auto& well_state = comp_well_states_[well->name()];
307  well->iterateWellEq(simulator_, dt, well_state);
308  // currently we use the converged assembly of well equations directly without a new assembling
309  // well->assembleWellEq(simulator_, well_state, dt);
310  }
311 }
312 
313 template <typename TypeTag>
314 void
315 CompWellModel<TypeTag>::
316 calculateExplicitQuantities()
317 {
318  for (auto& well : well_container_) {
319  const auto& well_state = comp_well_states_[well->name()];
320  well->calculateExplicitQuantities(simulator_, well_state);
321  }
322 }
323 
324 template <typename TypeTag>
325 void
326 CompWellModel<TypeTag>::
327 computeTotalRatesForDof(RateVector& rate,
328  unsigned globalIdx) const {
329  for (const auto& well: well_container_) {
330  well->addCellRates(rate, globalIdx);
331  }
332 }
333 
334 template<typename TypeTag>
335 void
336 CompWellModel<TypeTag>::
337 recoverWellSolutionAndUpdateWellState(const BVector& x)
338 {
339  {
340  for (const auto& well : well_container_) {
341  const auto& cells = well->cells();
342  x_local_.resize(cells.size());
343 
344  for (size_t i = 0; i < cells.size(); ++i) {
345  x_local_[i] = x[cells[i]];
346  }
347  auto& ws = this->comp_well_states_[well->name()];
348  well->recoverWellSolutionAndUpdateWellState(x_local_, ws);
349  }
350  }
351 }
352 
353 template <typename TypeTag>
354 bool
355 CompWellModel<TypeTag>::
356 forceShutWellByName(const std::string& well_name,
357  double simulation_time,
358  bool)
359 {
360  int well_was_shut = 0;
361 
362  if (comp_well_states_.has(well_name)) {
363  auto& well_state = comp_well_states_[well_name];
364  if (well_state.status != WellStatus::SHUT) {
365  well_state.status = WellStatus::SHUT;
366  well_close_times_.insert_or_assign(well_name, simulation_time);
367  well_open_times_.erase(well_name);
368 
369  if (last_valid_comp_well_states_.has(well_name)) {
370  last_valid_comp_well_states_[well_name].status = WellStatus::SHUT;
371  }
372 
373  std::erase_if(well_container_,
374  [&well_name](const auto& well)
375  { return well->name() == well_name; });
376 
377  well_was_shut = 1;
378  }
379  }
380 
381  well_was_shut = comm_.max(well_was_shut);
382  return well_was_shut == 1;
383 }
384 
385 template <typename TypeTag>
386 bool
387 CompWellModel<TypeTag>::
388 getWellConvergence() const
389 {
390  bool converged = true;
391  for (const auto& well : this->well_container_) {
392  converged = converged && well->getConvergence();
393  }
394  return converged;
395 }
396 
397 template <typename TypeTag>
398 data::Wells
399 CompWellModel<TypeTag>::
400 wellData() const
401 {
402  return this->comp_well_states_.report();
403 }
404 
405 } // end of namespace Opm
406 
407 #endif
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45