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