opm-simulators
CollectDataOnIORank_impl.hpp
1 // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // vi: set et ts=4 sw=4 sts=4:
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 2 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  Consult the COPYING file in the top-level source directory of this
20  module for the precise wording of the license and the list of
21  copyright holders.
22 */
23 
24 #ifndef OPM_COLLECT_DATA_ON_IO_RANK_IMPL_HPP
25 #define OPM_COLLECT_DATA_ON_IO_RANK_IMPL_HPP
26 
27 #include <opm/simulators/flow/CollectDataOnIORank.hpp>
28 
29 #include <dune/common/version.hh>
30 #include <dune/grid/common/gridenums.hh>
31 #include <dune/grid/common/mcmgmapper.hh>
32 #include <dune/grid/common/partitionset.hh>
33 
34 #include <opm/grid/common/CartesianIndexMapper.hpp>
35 
36 #include <algorithm>
37 #include <cassert>
38 #include <stdexcept>
39 #include <string>
40 #include <vector>
41 
42 namespace {
43  std::vector<std::string> toVector(const std::set<std::string>& string_set)
44  {
45  return { string_set.begin(), string_set.end() };
46  }
47 }
48 
49 namespace Opm {
50 
51 // global id
53 {
54  int globalId_;
55  int localIndex_;
56  bool isInterior_;
57 
58 public:
60  : globalId_(-1)
61  , localIndex_(-1)
62  , isInterior_(true)
63  {}
64  void setGhost()
65  { isInterior_ = false; }
66 
67  void setId(int globalId)
68  { globalId_ = globalId; }
69  void setIndex(int localIndex)
70  { localIndex_ = localIndex; }
71 
72  int localIndex () const
73  { return localIndex_; }
74  int id () const
75  { return globalId_; }
76  bool isInterior() const
77  { return isInterior_; }
78 };
79 
80 using IndexMapType = std::vector<int>;
81 using IndexMapStorageType = std::vector<IndexMapType>;
82 using P2PCommunicatorType = Dune::Point2PointCommunicator<Dune::SimpleMessageBuffer>;
83 using MessageBufferType = typename P2PCommunicatorType::MessageBufferType;
84 class DistributeIndexMapping : public P2PCommunicatorType::DataHandleInterface
85 {
86 protected:
87  const std::vector<int>& distributedGlobalIndex_;
88  IndexMapType& localIndexMap_;
89  IndexMapStorageType& indexMaps_;
90  std::map<int, int> globalPosition_;
91  std::set<int>& recv_;
92  std::vector<int>& ranks_;
93 
94 public:
95  DistributeIndexMapping(const std::vector<int>& globalIndex,
96  const std::vector<int>& distributedGlobalIndex,
97  IndexMapType& localIndexMap,
98  IndexMapStorageType& indexMaps,
99  std::vector<int>& ranks,
100  std::set<int>& recv,
101  bool isIORank)
102  : distributedGlobalIndex_(distributedGlobalIndex)
103  , localIndexMap_(localIndexMap)
104  , indexMaps_(indexMaps)
105  , globalPosition_()
106  , recv_(recv)
107  , ranks_(ranks)
108  {
109  std::size_t size = globalIndex.size();
110  // create mapping globalIndex --> localIndex
111  if ( isIORank ) // ioRank
112  for (std::size_t index = 0; index < size; ++index)
113  globalPosition_.insert(std::make_pair(globalIndex[index], index));
114 
115  // we need to create a mapping from local to global
116  if (!indexMaps_.empty()) {
117  if (isIORank)
118  ranks_.resize(size, -1);
119  // for the ioRank create a localIndex to index in global state map
120  IndexMapType& indexMap = indexMaps_.back();
121  std::size_t localSize = localIndexMap_.size();
122  indexMap.resize(localSize);
123  for (std::size_t i = 0; i < localSize; ++i)
124  {
125  int id = distributedGlobalIndex_[localIndexMap_[i]];
126  indexMap[i] = id;
127  }
128  }
129  }
130 
132  {
133  if (!indexMaps_.size())
134  return;
135 
136  if ( ranks_.size() )
137  {
138  auto rankIt = recv_.begin();
139  // translate index maps from global cartesian to index
140  for (auto& indexMap: indexMaps_)
141  {
142  int rank = 0;
143  if (rankIt != recv_.end())
144  rank = *rankIt;
145 
146  for (auto&& entry: indexMap)
147  {
148  auto candidate = globalPosition_.find(entry);
149  assert(candidate != globalPosition_.end());
150  entry = candidate->second;
151  // Using max should be backwards compatible
152  ranks_[entry] = std::max(ranks_[entry], rank);
153  }
154  if (rankIt != recv_.end())
155  ++rankIt;
156  }
157  #ifndef NDEBUG
158  for (const auto& rank: ranks_)
159  assert(rank>=0);
160  #endif
161  }
162  }
163 
164  void pack(int link, MessageBufferType& buffer)
165  {
166  // we should only get one link
167  if (link != 0)
168  throw std::logic_error("link in method pack is not 0 as execpted");
169 
170  // pack all interior global cell id's
171  int size = localIndexMap_.size();
172  buffer.write(size);
173 
174  for (int index = 0; index < size; ++index) {
175  int globalIdx = distributedGlobalIndex_[localIndexMap_[index]];
176  buffer.write(globalIdx);
177  }
178  }
179 
180  void unpack(int link, MessageBufferType& buffer)
181  {
182  // get index map for current link
183  IndexMapType& indexMap = indexMaps_[link];
184  assert(!globalPosition_.empty());
185 
186  // unpack all interior global cell id's
187  int numCells = 0;
188  buffer.read(numCells);
189  indexMap.resize(numCells);
190  for (int index = 0; index < numCells; ++index) {
191  buffer.read(indexMap[index]);
192  }
193  }
194 };
195 
196 
198 template<class EquilMapper, class Mapper>
200 {
201 public:
202  ElementIndexScatterHandle(const EquilMapper& sendMapper, const Mapper& recvMapper, std::vector<int>& elementIndices)
203  : sendMapper_(sendMapper), recvMapper_(recvMapper), elementIndices_(elementIndices)
204  {}
205  using DataType = int;
206  bool fixedSize(int /*dim*/, int /*codim*/)
207  {
208  return true;
209  }
210 
211  template<class T>
212  std::size_t size(const T&)
213  {
214  return 1;
215  }
216  template<class B, class T>
217  void gather(B& buffer, const T& t)
218  {
219  buffer.write(sendMapper_.index(t));
220  }
221  template<class B, class T>
222  void scatter(B& buffer, const T& t, std::size_t)
223  {
224  buffer.read(elementIndices_[recvMapper_.index(t)]);
225  }
226 
227  bool contains(int dim, int codim)
228  {
229  return dim==3 && codim==0;
230  }
231 private:
232  const EquilMapper& sendMapper_;
233  const Mapper& recvMapper_;
234  std::vector<int>& elementIndices_;
235 };
236 
238 template<class Mapper>
240 {
241 public:
242  ElementIndexHandle(const Mapper& mapper, std::vector<int>& elementIndices)
243  : mapper_(mapper), elementIndices_(elementIndices)
244  {}
245  using DataType = int;
246  bool fixedSize(int /*dim*/, int /*codim*/)
247  {
248  return true;
249  }
250 
251  template<class T>
252  std::size_t size(const T&)
253  {
254  return 1;
255  }
256  template<class B, class T>
257  void gather(B& buffer, const T& t)
258  {
259  buffer.write(elementIndices_[mapper_.index(t)]);
260  }
261  template<class B, class T>
262  void scatter(B& buffer, const T& t, std::size_t)
263  {
264  buffer.read(elementIndices_[mapper_.index(t)]);
265  }
266 
267  bool contains(int dim, int codim)
268  {
269  return dim==3 && codim==0;
270  }
271 private:
272  const Mapper& mapper_;
273  std::vector<int>& elementIndices_;
274 };
275 
276 class PackUnPackCellData : public P2PCommunicatorType::DataHandleInterface
277 {
278  const data::Solution& localCellData_;
279  data::Solution& globalCellData_;
280 
281  const IndexMapType& localIndexMap_;
282  const IndexMapStorageType& indexMaps_;
283 
284 public:
285  PackUnPackCellData(const data::Solution& localCellData,
286  data::Solution& globalCellData,
287  const IndexMapType& localIndexMap,
288  const IndexMapStorageType& indexMaps,
289  std::size_t globalSize,
290  bool isIORank)
291  : localCellData_(localCellData)
292  , globalCellData_(globalCellData)
293  , localIndexMap_(localIndexMap)
294  , indexMaps_(indexMaps)
295  {
296  if (isIORank) {
297  // add missing data to global cell data
298  for (const auto& pair : localCellData_) {
299  const std::string& key = pair.first;
300  std::size_t containerSize = globalSize;
301  [[maybe_unused]] auto ret = globalCellData_.insert(key, pair.second.dim,
302  std::vector<double>(containerSize),
303  pair.second.target);
304  assert(ret.second);
305  }
306 
307  MessageBufferType buffer;
308  pack(0, buffer);
309 
310  // the last index map is the local one
311  doUnpack(indexMaps.back(), buffer);
312  }
313  }
314 
315  // pack all data associated with link
316  void pack(int link, MessageBufferType& buffer)
317  {
318  // we should only get one link
319  if (link != 0)
320  throw std::logic_error("link in method pack is not 0 as expected");
321 
322  // write all cell data registered in local state
323  for (const auto& pair : localCellData_) {
324  const auto& data = pair.second.data<double>();
325 
326  // write all data from local data to buffer
327  write(buffer, localIndexMap_, data);
328  }
329  }
330 
331  void doUnpack(const IndexMapType& indexMap, MessageBufferType& buffer)
332  {
333  // we loop over the data as
334  // its order governs the order the data got received.
335  for (auto& pair : localCellData_) {
336  const std::string& key = pair.first;
337  auto& data = globalCellData_.data<double>(key);
338 
339  //write all data from local cell data to buffer
340  read(buffer, indexMap, data);
341  }
342  }
343 
344  // unpack all data associated with link
345  void unpack(int link, MessageBufferType& buffer)
346  { doUnpack(indexMaps_[link], buffer); }
347 
348 protected:
349  template <class Vector>
350  void write(MessageBufferType& buffer,
351  const IndexMapType& localIndexMap,
352  const Vector& vector,
353  unsigned int offset = 0,
354  unsigned int stride = 1) const
355  {
356  unsigned int size = localIndexMap.size();
357  buffer.write(size);
358  assert(vector.size() >= stride * size);
359  for (unsigned int i=0; i<size; ++i)
360  {
361  unsigned int index = localIndexMap[i] * stride + offset;
362  assert(index < vector.size());
363  buffer.write(vector[index]);
364  }
365  }
366 
367  template <class Vector>
368  void read(MessageBufferType& buffer,
369  const IndexMapType& indexMap,
370  Vector& vector,
371  unsigned int offset = 0,
372  unsigned int stride = 1) const
373  {
374  unsigned int size = 0;
375  buffer.read(size);
376  assert(size == indexMap.size());
377  for (unsigned int i=0; i<size; ++i) {
378  unsigned int index = indexMap[i] * stride + offset;
379  assert(index < vector.size());
380  buffer.read(vector[index]);
381  }
382  }
383 };
384 
385 class PackUnPackWellData : public P2PCommunicatorType::DataHandleInterface
386 {
387  const data::Wells& localWellData_;
388  data::Wells& globalWellData_;
389 
390 public:
391  PackUnPackWellData(const data::Wells& localWellData,
392  data::Wells& globalWellData,
393  bool isIORank)
394  : localWellData_(localWellData)
395  , globalWellData_(globalWellData)
396  {
397  if (isIORank) {
398  MessageBufferType buffer;
399  pack(0, buffer);
400 
401  // pass a dummy_link to satisfy virtual class
402  int dummyLink = -1;
403  unpack(dummyLink, buffer);
404  }
405  }
406 
407  // pack all data associated with link
408  void pack(int link, MessageBufferType& buffer)
409  {
410  // we should only get one link
411  if (link != 0)
412  throw std::logic_error("link in method pack is not 0 as expected");
413 
414  localWellData_.write(buffer);
415  }
416 
417  // unpack all data associated with link
418  void unpack(int /*link*/, MessageBufferType& buffer)
419  { globalWellData_.read(buffer); }
420 
421 };
422 
423 class PackUnPackGroupAndNetworkValues : public P2PCommunicatorType::DataHandleInterface
424 {
425  const data::GroupAndNetworkValues& localGroupAndNetworkData_;
426  data::GroupAndNetworkValues& globalGroupAndNetworkData_;
427 
428 public:
429  PackUnPackGroupAndNetworkValues(const data::GroupAndNetworkValues& localGroupAndNetworkData,
430  data::GroupAndNetworkValues& globalGroupAndNetworkData,
431  const bool isIORank)
432  : localGroupAndNetworkData_ (localGroupAndNetworkData)
433  , globalGroupAndNetworkData_(globalGroupAndNetworkData)
434  {
435  if (! isIORank) { return; }
436 
437  MessageBufferType buffer;
438  this->pack(0, buffer);
439 
440  // pass a dummy_link to satisfy virtual class
441  const int dummyLink = -1;
442  this->unpack(dummyLink, buffer);
443  }
444 
445  // pack all data associated with link
446  void pack(int link, MessageBufferType& buffer)
447  {
448  // we should only get one link
449  if (link != 0) {
450  throw std::logic_error {
451  "link in method pack is not 0 as expected"
452  };
453  }
454 
455  // write all group and network (node/branch) data
456  this->localGroupAndNetworkData_.write(buffer);
457  }
458 
459  // unpack all data associated with link
460  void unpack(int /*link*/, MessageBufferType& buffer)
461  { this->globalGroupAndNetworkData_.read(buffer); }
462 };
463 
464 class PackUnPackBlockData : public P2PCommunicatorType::DataHandleInterface
465 {
466  const std::map<std::pair<std::string, int>, double>& localBlockData_;
467  std::map<std::pair<std::string, int>, double>& globalBlockValues_;
468 
469 public:
470  PackUnPackBlockData(const std::map<std::pair<std::string, int>, double>& localBlockData,
471  std::map<std::pair<std::string, int>, double>& globalBlockValues,
472  bool isIORank)
473  : localBlockData_(localBlockData)
474  , globalBlockValues_(globalBlockValues)
475  {
476  if (isIORank) {
477  MessageBufferType buffer;
478  pack(0, buffer);
479 
480  // pass a dummyLink to satisfy virtual class
481  int dummyLink = -1;
482  unpack(dummyLink, buffer);
483  }
484  }
485 
486  // pack all data associated with link
487  void pack(int link, MessageBufferType& buffer)
488  {
489  // we should only get one link
490  if (link != 0)
491  throw std::logic_error("link in method pack is not 0 as expected");
492 
493  // write all block data
494  unsigned int size = localBlockData_.size();
495  buffer.write(size);
496  for (const auto& map : localBlockData_) {
497  buffer.write(map.first.first);
498  buffer.write(map.first.second);
499  buffer.write(map.second);
500  }
501  }
502 
503  // unpack all data associated with link
504  void unpack(int /*link*/, MessageBufferType& buffer)
505  {
506  // read all block data
507  unsigned int size = 0;
508  buffer.read(size);
509  for (std::size_t i = 0; i < size; ++i) {
510  std::string name;
511  int idx;
512  double data;
513  buffer.read(name);
514  buffer.read(idx);
515  buffer.read(data);
516  globalBlockValues_[std::make_pair(name, idx)] = data;
517  }
518  }
519 };
520 
521 class PackUnPackWBPData : public P2PCommunicatorType::DataHandleInterface
522 {
523  const data::WellBlockAveragePressures& localWBPData_;
524  data::WellBlockAveragePressures& globalWBPValues_;
525 
526 public:
527  PackUnPackWBPData(const data::WellBlockAveragePressures& localWBPData,
528  data::WellBlockAveragePressures& globalWBPValues,
529  const bool isIORank)
530  : localWBPData_ (localWBPData)
531  , globalWBPValues_(globalWBPValues)
532  {
533  if (! isIORank) {
534  return;
535  }
536 
537  MessageBufferType buffer;
538  pack(0, buffer);
539 
540  // Pass a dummy link to satisfy base class API requirement
541  const int dummyLink = -1;
542  unpack(dummyLink, buffer);
543  }
544 
545  // Pack all data associated with link
546  void pack(int link, MessageBufferType& buffer)
547  {
548  // We should only get one link
549  if (link != 0) {
550  throw std::logic_error {
551  "link (" + std::to_string(link) +
552  ") in method pack() is not 0 as expected"
553  };
554  }
555 
556  // Write all local, per-well, WBP data
557  this->localWBPData_.write(buffer);
558  }
559 
560  // Unpack all data associated with link
561  void unpack([[maybe_unused]] const int link,
562  MessageBufferType& buffer)
563  {
564  this->globalWBPValues_.read(buffer);
565  }
566 };
567 
568 class PackUnPackWellTestState : public P2PCommunicatorType::DataHandleInterface
569 {
570 public:
571  PackUnPackWellTestState(const WellTestState& localWTestState,
572  WellTestState& globalWTestState,
573  bool isIORank)
574  : local_(localWTestState)
575  , global_(globalWTestState)
576  {
577  if (isIORank) {
578  MessageBufferType buffer;
579  pack(0, buffer);
580 
581  // pass a dummyLink to satisfy virtual class
582  int dummyLink = -1;
583  unpack(dummyLink, buffer);
584  }
585  }
586 
587  void pack(int link, MessageBufferType& buffer) {
588  if (link != 0)
589  throw std::logic_error("link in method pack is not 0 as expected");
590  this->local_.pack(buffer);
591  }
592 
593  void unpack(int, MessageBufferType& buffer) {
594  this->global_.unpack(buffer);
595  }
596 
597 private:
598  const WellTestState& local_;
599  WellTestState& global_;
600 };
601 
602 class PackUnPackAquiferData : public P2PCommunicatorType::DataHandleInterface
603 {
604  const data::Aquifers& localAquiferData_;
605  data::Aquifers& globalAquiferData_;
606 
607 public:
608  PackUnPackAquiferData(const data::Aquifers& localAquiferData,
609  data::Aquifers& globalAquiferData,
610  bool isIORank)
611  : localAquiferData_(localAquiferData)
612  , globalAquiferData_(globalAquiferData)
613  {
614  if (isIORank) {
615  MessageBufferType buffer;
616  pack(0, buffer);
617 
618  // pass a dummyLink to satisfy virtual class
619  int dummyLink = -1;
620  unpack(dummyLink, buffer);
621  }
622  }
623 
624  // pack all data associated with link
625  void pack(int link, MessageBufferType& buffer)
626  {
627  // we should only get one link
628  if (link != 0)
629  throw std::logic_error("link in method pack is not 0 as expected");
630 
631  int size = localAquiferData_.size();
632  buffer.write(size);
633  for (const auto& [key, data] : localAquiferData_) {
634  buffer.write(key);
635  data.write(buffer);
636  }
637  }
638 
639  // unpack all data associated with link
640  void unpack(int /*link*/, MessageBufferType& buffer)
641  {
642  int size;
643  buffer.read(size);
644  for (int i = 0; i < size; ++i) {
645  int key;
646  buffer.read(key);
647  data::AquiferData data;
648  data.read(buffer);
649 
650  auto& aq = this->globalAquiferData_[key];
651 
652  this->unpackCommon(data, aq);
653 
654  if (auto const* aquFet = data.typeData.get<data::AquiferType::Fetkovich>();
655  aquFet != nullptr)
656  {
657  this->unpackAquFet(*aquFet, aq);
658  }
659  else if (auto const* aquCT = data.typeData.get<data::AquiferType::CarterTracy>();
660  aquCT != nullptr)
661  {
662  this->unpackCarterTracy(*aquCT, aq);
663  }
664  else if (auto const* aquNum = data.typeData.get<data::AquiferType::Numerical>();
665  aquNum != nullptr)
666  {
667  this->unpackNumericAquifer(*aquNum, aq);
668  }
669  }
670  }
671 
672 private:
673  void unpackCommon(const data::AquiferData& data, data::AquiferData& aq)
674  {
675  aq.aquiferID = std::max(aq.aquiferID, data.aquiferID);
676  aq.pressure = std::max(aq.pressure, data.pressure);
677  aq.initPressure = std::max(aq.initPressure, data.initPressure);
678  aq.datumDepth = std::max(aq.datumDepth, data.datumDepth);
679  aq.fluxRate += data.fluxRate;
680  aq.volume += data.volume;
681  }
682 
683  void unpackAquFet(const data::FetkovichData& aquFet, data::AquiferData& aq)
684  {
685  if (! aq.typeData.is<data::AquiferType::Fetkovich>()) {
686  auto* tData = aq.typeData.create<data::AquiferType::Fetkovich>();
687  *tData = aquFet;
688  }
689  else {
690  const auto& src = aquFet;
691  auto& dst = *aq.typeData.getMutable<data::AquiferType::Fetkovich>();
692 
693  dst.initVolume = std::max(dst.initVolume , src.initVolume);
694  dst.prodIndex = std::max(dst.prodIndex , src.prodIndex);
695  dst.timeConstant = std::max(dst.timeConstant, src.timeConstant);
696  }
697  }
698 
699  void unpackCarterTracy(const data::CarterTracyData& aquCT, data::AquiferData& aq)
700  {
701  if (! aq.typeData.is<data::AquiferType::CarterTracy>()) {
702  auto* tData = aq.typeData.create<data::AquiferType::CarterTracy>();
703  *tData = aquCT;
704  }
705  else {
706  const auto& src = aquCT;
707  auto& dst = *aq.typeData.getMutable<data::AquiferType::CarterTracy>();
708 
709  dst.timeConstant = std::max(dst.timeConstant , src.timeConstant);
710  dst.influxConstant = std::max(dst.influxConstant, src.influxConstant);
711  dst.waterDensity = std::max(dst.waterDensity , src.waterDensity);
712  dst.waterViscosity = std::max(dst.waterViscosity, src.waterViscosity);
713 
714  dst.dimensionless_time = std::max(dst.dimensionless_time , src.dimensionless_time);
715  dst.dimensionless_pressure = std::max(dst.dimensionless_pressure, src.dimensionless_pressure);
716  }
717  }
718 
719  void unpackNumericAquifer(const data::NumericAquiferData& aquNum, data::AquiferData& aq)
720  {
721  if (! aq.typeData.is<data::AquiferType::Numerical>()) {
722  auto* tData = aq.typeData.create<data::AquiferType::Numerical>();
723  *tData = aquNum;
724  }
725  else {
726  const auto& src = aquNum;
727  auto& dst = *aq.typeData.getMutable<data::AquiferType::Numerical>();
728 
729  std::ranges::transform(src.initPressure, dst.initPressure, dst.initPressure.begin(),
730  [](const double p0_1, const double p0_2)
731  { return std::max(p0_1, p0_2); });
732  }
733  }
734 };
735 
736 class PackUnpackInterRegFlows : public P2PCommunicatorType::DataHandleInterface
737 {
738  const InterRegFlowMap& localInterRegFlows_;
739  InterRegFlowMap& globalInterRegFlows_;
740 
741 public:
742  PackUnpackInterRegFlows(const InterRegFlowMap& localInterRegFlows,
743  InterRegFlowMap& globalInterRegFlows,
744  const bool isIORank)
745  : localInterRegFlows_(localInterRegFlows)
746  , globalInterRegFlows_(globalInterRegFlows)
747  {
748  if (! isIORank) { return; }
749 
750  MessageBufferType buffer;
751  this->pack(0, buffer);
752 
753  // pass a dummy_link to satisfy virtual class
754  const int dummyLink = -1;
755  this->unpack(dummyLink, buffer);
756  }
757 
758  // pack all data associated with link
759  void pack(int link, MessageBufferType& buffer)
760  {
761  // we should only get one link
762  if (link != 0) {
763  throw std::logic_error {
764  "link in method pack is not 0 as expected"
765  };
766  }
767 
768  // write all inter-region flow data
769  this->localInterRegFlows_.write(buffer);
770  }
771 
772  // unpack all data associated with link
773  void unpack(int /*link*/, MessageBufferType& buffer)
774  { this->globalInterRegFlows_.read(buffer); }
775 };
776 
777 class PackUnpackFlows : public P2PCommunicatorType::DataHandleInterface
778 {
779  const std::array<FlowsData<double>, 3>& localFlows_;
780  std::array<FlowsData<double>, 3>& globalFlows_;
781 
782 public:
783  PackUnpackFlows(const std::array<FlowsData<double>, 3> & localFlows,
784  std::array<FlowsData<double>, 3>& globalFlows,
785  const bool isIORank)
786  : localFlows_(localFlows)
787  , globalFlows_(globalFlows)
788  {
789  if (! isIORank) { return; }
790 
791  MessageBufferType buffer;
792  this->pack(0, buffer);
793 
794  // pass a dummy_link to satisfy virtual class
795  const int dummyLink = -1;
796  this->unpack(dummyLink, buffer);
797  }
798 
799  void pack(int link, MessageBufferType& buffer)
800  {
801  if (link != 0)
802  throw std::logic_error("link in method pack is not 0 as expected");
803  for (int i = 0; i < 3; ++i) {
804  const auto& name = localFlows_[i].name;
805  buffer.write(name);
806  const std::size_t size = localFlows_[i].indices.size();
807  buffer.write(size);
808  for (std::size_t j = 0; j < size; ++j) {
809  const auto& nncIdx = localFlows_[i].indices[j];
810  buffer.write(nncIdx);
811  const auto& flows = localFlows_[i].values[j];
812  buffer.write(flows);
813  }
814  }
815  }
816 
817  void unpack(int /*link*/, MessageBufferType& buffer)
818  {
819  for (int i = 0; i < 3; ++i) {
820  std::string name;
821  buffer.read(name);
822  globalFlows_[i].name = name;
823  std::size_t size = 0;
824  buffer.read(size);
825  for (unsigned int j = 0; j < size; ++j) {
826  int nncIdx;
827  double data;
828  buffer.read(nncIdx);
829  buffer.read(data);
830  if (nncIdx < 0)
831  continue;
832  // This array is initialized in the collect(...) method below
833  globalFlows_[i].values[nncIdx] = data;
834  }
835  }
836  }
837 };
838 
839 template <class Grid, class EquilGrid, class GridView>
841 CollectDataOnIORank(const Grid& grid, const EquilGrid* equilGrid,
842  const GridView& localGridView,
843  const Dune::CartesianIndexMapper<Grid>& cartMapper,
844  const Dune::CartesianIndexMapper<EquilGrid>* equilCartMapper,
845  const std::set<std::string>& fipRegionsInterregFlow)
846  : toIORankComm_(grid.comm())
847  , globalInterRegFlows_(InterRegFlowMap::createMapFromNames(toVector(fipRegionsInterregFlow)))
848 {
849  // Build index maps only when reordering is needed; skip in parallel runs for CpGrid with LGRs
850  if ((!needsReordering && !isParallel()) || (isParallel() && (grid.maxLevel()>0)))
851  return;
852 
853  const CollectiveCommunication& comm = grid.comm();
854 
855  {
856  std::set<int> send, recv;
857  using EquilGridView = typename EquilGrid::LeafGridView;
858  typename std::is_same<Grid, EquilGrid>::type isSameGrid;
859 
860  typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView> ElementMapper;
861  ElementMapper elemMapper(localGridView, Dune::mcmgElementLayout());
862  sortedCartesianIdx_.reserve(localGridView.size(0));
863 
864  for (const auto& elem : elements(localGridView, Dune::Partitions::interior))
865  {
866  auto idx = elemMapper.index(elem);
867  sortedCartesianIdx_.push_back(cartMapper.cartesianIndex(idx));
868  }
869 
870  std::ranges::sort(sortedCartesianIdx_);
871  localIdxToGlobalIdx_.resize(localGridView.size(0), -1);
872 
873  // the I/O rank receives from all other ranks
874  if (isIORank()) {
875  // We need a mapping from local to global grid, here we
876  // use equilGrid which represents a view on the global grid
877  // reserve memory
878  const std::size_t globalSize = equilGrid->leafGridView().size(0);
879  globalCartesianIndex_.resize(globalSize, -1);
880  const EquilGridView equilGridView = equilGrid->leafGridView();
881 
882  using EquilElementMapper = Dune::MultipleCodimMultipleGeomTypeMapper<EquilGridView>;
883  EquilElementMapper equilElemMapper(equilGridView, Dune::mcmgElementLayout());
884 
885  // Scatter the global index to local index for lookup during restart
886  if constexpr (isSameGrid) {
887  ElementIndexScatterHandle<EquilElementMapper,ElementMapper> handle(equilElemMapper, elemMapper, localIdxToGlobalIdx_);
888  grid.scatterData(handle);
889  }
890 
891  // loop over all elements (global grid) and store Cartesian index
892  for (const auto& elem : elements(equilGrid->leafGridView())) {
893  int elemIdx = equilElemMapper.index(elem);
894  int cartElemIdx = equilCartMapper->cartesianIndex(elemIdx);
895  globalCartesianIndex_[elemIdx] = cartElemIdx;
896  }
897 
898  for (int i = 0; i < comm.size(); ++i) {
899  if (i != ioRank)
900  recv.insert(i);
901  }
902  }
903  else
904  {
905  // all other simply send to the I/O rank
906  send.insert(ioRank);
907 
908  // Scatter the global index to local index for lookup during restart
909  // This is a bit hacky since the type differs from the iorank.
910  // But should work since we only receive, i.e. use the second parameter.
911  if constexpr (isSameGrid) {
912  ElementIndexScatterHandle<ElementMapper, ElementMapper> handle(elemMapper, elemMapper, localIdxToGlobalIdx_);
913  grid.scatterData(handle);
914  }
915  }
916 
917  // Sync the global element indices
918  if constexpr (isSameGrid) {
919  ElementIndexHandle<ElementMapper> handle(elemMapper, localIdxToGlobalIdx_);
920  grid.communicate(handle, Dune::InteriorBorder_All_Interface,
921  Dune::ForwardCommunication);
922  }
923 
924  localIndexMap_.clear();
925  const std::size_t gridSize = grid.size(0);
926  localIndexMap_.reserve(gridSize);
927 
928  // store the local Cartesian index
929  IndexMapType distributedCartesianIndex;
930  distributedCartesianIndex.resize(gridSize, -1);
931 
932  // A mapping for the whole grid (including the ghosts) is needed for restarts
933  for (const auto& elem : elements(localGridView, Dune::Partitions::interior)) {
934  int elemIdx = elemMapper.index(elem);
935  distributedCartesianIndex[elemIdx] = cartMapper.cartesianIndex(elemIdx);
936 
937  // only store interior element for collection
938  assert(elem.partitionType() == Dune::InteriorEntity);
939 
940  localIndexMap_.push_back(elemIdx);
941  }
942 
943  // insert send and recv linkage to communicator
944  toIORankComm_.insertRequest(send, recv);
945 
946  // need an index map for each rank
947  indexMaps_.clear();
948  indexMaps_.resize(comm.size());
949 
950  // distribute global id's to io rank for later association of dof's
951  DistributeIndexMapping distIndexMapping(globalCartesianIndex_,
952  distributedCartesianIndex,
953  localIndexMap_,
954  indexMaps_,
955  globalRanks_,
956  recv,
957  isIORank());
958  toIORankComm_.exchange(distIndexMapping);
959  }
960 }
961 
962 template <class Grid, class EquilGrid, class GridView>
963 void CollectDataOnIORank<Grid,EquilGrid,GridView>::
964 collect(const data::Solution& localCellData,
965  const std::map<std::pair<std::string, int>, double>& localBlockData,
966  std::map<std::pair<std::string, int>, double>& extraBlockData,
967  const data::Wells& localWellData,
968  const data::WellBlockAveragePressures& localWBPData,
969  const data::GroupAndNetworkValues& localGroupAndNetworkData,
970  const data::Aquifers& localAquiferData,
971  const WellTestState& localWellTestState,
972  const InterRegFlowMap& localInterRegFlows,
973  const std::array<FlowsData<double>, 3>& localFlowsn,
974  const std::array<FlowsData<double>, 3>& localFloresn)
975 {
976  globalCellData_ = {};
977  globalBlockData_.clear();
978  std::map<std::pair<std::string,int>,double> globalExtraBlockData;
979  globalWellData_.clear();
980  globalWBPData_.values.clear();
981  globalGroupAndNetworkData_.clear();
982  globalAquiferData_.clear();
983  globalWellTestState_.clear();
984  this->globalInterRegFlows_.clear();
985  globalFlowsn_ = {};
986  globalFloresn_ = {};
987 
988  // index maps only have to be build when reordering is needed
989  if(!needsReordering && !isParallel())
990  return;
991 
992  // this also linearises the local buffers on ioRank
993  PackUnPackCellData packUnpackCellData {
994  localCellData,
995  this->globalCellData_,
996  this->localIndexMap_,
997  this->indexMaps_,
998  this->numCells(),
999  this->isIORank()
1000  };
1001 
1002  if (! isParallel()) {
1003  // no need to collect anything.
1004  return;
1005  }
1006 
1007  // Set the right sizes for Flowsn and Floresn
1008  for (int i = 0; i < 3; ++i) {
1009  const std::size_t sizeFlr = localFloresn[i].indices.size();
1010  globalFloresn_[i].resize(sizeFlr);
1011  const std::size_t sizeFlo = localFlowsn[i].indices.size();
1012  globalFlowsn_[i].resize(sizeFlo);
1013  }
1014 
1015  PackUnPackWellData packUnpackWellData {
1016  localWellData,
1017  this->globalWellData_,
1018  this->isIORank()
1019  };
1020 
1021  PackUnPackGroupAndNetworkValues packUnpackGroupAndNetworkData {
1022  localGroupAndNetworkData,
1023  this->globalGroupAndNetworkData_,
1024  this->isIORank()
1025  };
1026 
1027  PackUnPackBlockData packUnpackBlockData {
1028  localBlockData,
1029  this->globalBlockData_,
1030  this->isIORank()
1031  };
1032 
1033  PackUnPackBlockData packUnpackExtraBlockData {
1034  extraBlockData,
1035  globalExtraBlockData,
1036  this->isIORank()
1037  };
1038 
1039  PackUnPackWBPData packUnpackWBPData {
1040  localWBPData,
1041  this->globalWBPData_,
1042  this->isIORank()
1043  };
1044 
1045  PackUnPackAquiferData packUnpackAquiferData {
1046  localAquiferData,
1047  this->globalAquiferData_,
1048  this->isIORank()
1049  };
1050 
1051  PackUnPackWellTestState packUnpackWellTestState {
1052  localWellTestState,
1053  this->globalWellTestState_,
1054  this->isIORank()
1055  };
1056 
1057  PackUnpackInterRegFlows packUnpackInterRegFlows {
1058  localInterRegFlows,
1059  this->globalInterRegFlows_,
1060  this->isIORank()
1061  };
1062 
1063  PackUnpackFlows packUnpackFlowsn {
1064  localFlowsn,
1065  this->globalFlowsn_,
1066  this->isIORank()
1067  };
1068 
1069  PackUnpackFlows packUnpackFloresn {
1070  localFloresn,
1071  this->globalFloresn_,
1072  this->isIORank()
1073  };
1074 
1075  toIORankComm_.exchange(packUnpackCellData);
1076  toIORankComm_.exchange(packUnpackWellData);
1077  toIORankComm_.exchange(packUnpackGroupAndNetworkData);
1078  toIORankComm_.exchange(packUnpackBlockData);
1079  toIORankComm_.exchange(packUnpackExtraBlockData);
1080  toIORankComm_.exchange(packUnpackWBPData);
1081  toIORankComm_.exchange(packUnpackAquiferData);
1082  toIORankComm_.exchange(packUnpackWellTestState);
1083  toIORankComm_.exchange(packUnpackInterRegFlows);
1084  toIORankComm_.exchange(packUnpackFlowsn);
1085  toIORankComm_.exchange(packUnpackFloresn);
1086 
1087 #ifndef NDEBUG
1088  // make sure every process is on the same page
1089  toIORankComm_.barrier();
1090 #endif
1091 
1092  extraBlockData = globalExtraBlockData;
1093 }
1094 
1095 template <class Grid, class EquilGrid, class GridView>
1096 int CollectDataOnIORank<Grid,EquilGrid,GridView>::
1097 localIdxToGlobalIdx(unsigned localIdx) const
1098 {
1099  if (!isParallel()) {
1100  return localIdx;
1101  }
1102 
1103  if (this->localIdxToGlobalIdx_.empty()) {
1104  throw std::logic_error("index map is not created on this rank");
1105  }
1106 
1107  if (localIdx >= this->localIdxToGlobalIdx_.size()) {
1108  throw std::logic_error("local index is outside map range");
1109  }
1110 
1111  return this->localIdxToGlobalIdx_[localIdx];
1112 }
1113 
1114 template <class Grid, class EquilGrid, class GridView>
1115 bool CollectDataOnIORank<Grid,EquilGrid,GridView>::
1116 isCartIdxOnThisRank(int cartIdx) const
1117 {
1118  if (! this->isParallel()) {
1119  return true;
1120  }
1121 
1122  assert (! needsReordering);
1123 
1124  auto candidate = std::lower_bound(this->sortedCartesianIdx_.begin(),
1125  this->sortedCartesianIdx_.end(),
1126  cartIdx);
1127 
1128  return (candidate != sortedCartesianIdx_.end())
1129  && (*candidate == cartIdx);
1130 }
1131 
1132 } // end namespace Opm
1133 
1134 #endif // OPM_COLLECT_DATA_ON_IO_RANK_IMPL_HPP
Definition: CollectDataOnIORank_impl.hpp:777
Definition: CollectDataOnIORank_impl.hpp:423
Definition: CollectDataOnIORank_impl.hpp:602
void read(MessageBufferType &buffer)
Reconstitute internal object representation from MPI message buffer.
Definition: InterRegFlows.hpp:323
Communication handle to scatter the global index.
Definition: CollectDataOnIORank_impl.hpp:239
Definition: CollectDataOnIORank.hpp:55
Definition: CollectDataOnIORank_impl.hpp:52
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: blackoilbioeffectsmodules.hh:45
Definition: CollectDataOnIORank_impl.hpp:464
Inter-region flow accumulation maps for all region definition arrays.
Definition: InterRegFlows.hpp:178
void write(MessageBufferType &buffer) const
Serialise internal representation to MPI message buffer.
Definition: InterRegFlows.hpp:296
Communication handle to scatter the global index.
Definition: CollectDataOnIORank_impl.hpp:199
Definition: CollectDataOnIORank_impl.hpp:276
Definition: CollectDataOnIORank_impl.hpp:736
Simple container for FLOWS data.
Definition: FlowsData.hpp:31
Definition: CollectDataOnIORank_impl.hpp:521
Definition: CollectDataOnIORank_impl.hpp:84
Definition: CollectDataOnIORank_impl.hpp:385
Definition: CollectDataOnIORank_impl.hpp:568
Definition: CollectDataOnIORank.hpp:49