parts.h
Go to the documentation of this file.
1 /*
2  This file is part of the Ristra portage project.
3  Please see the license file at the root of this repository, or at:
4  https://github.com/laristra/portage/blob/master/LICENSE
5 */
6 
7 #ifndef PORTAGE_DRIVER_PARTS_H
8 #define PORTAGE_DRIVER_PARTS_H
9 
10 #include <algorithm>
11 #include <utility>
12 #include <vector>
13 #include <map>
14 #include <iterator>
15 #include <unordered_set>
16 #include <string>
17 #include <utility>
18 #include <iostream>
19 #include <type_traits>
20 #include <limits>
21 
24 
25 #define DEBUG_PART_BY_PART 0
26 
32 namespace Portage {
33 
34  using Wonton::Entity_kind;
35  using Wonton::Entity_type;
36  using entity_weights_t = std::vector<Wonton::Weights_t>;
37 
38  template<class Mesh, class State>
39  class Part {
40  public:
44  Part() = default;
45 
53  Part(Mesh const& mesh, State& state, std::vector<int> const& cells)
54  : mesh_(mesh),
55  state_(state),
56  cells_(cells) // deep-copy
57  {
58  size_ = cells.size();
59  if (size_ > 0) {
60  volumes_.resize(size_);
61  lookup_.reserve(size_);
62 
63  // set relative indexing and populate lookup hashtables
64  for (int i = 0; i < size_; ++i) {
65  auto const& s = cells[i];
66  index_[s] = i;
67  lookup_.insert(s);
68  }
69  } else {
70  volumes_.clear();
71  lookup_.clear();
72  }
73  }
74 
75  ~Part() = default;
76 
82  const Mesh& mesh() const { return mesh_; }
83 
89  const State& state() const { return state_; }
90 
96  State& state() { return const_cast<State&>(state_); }
97 
103  std::vector<int> const& cells() const { return cells_; }
104 
111  bool contains(int id) const { return lookup_.count(id) == 1; }
112 
119  const int& index(int id) const { return index_.at(id); }
120 
126  const int& size() const { return size_; }
127 
134  const double& volume(int id) const {
135  assert(id < size_);
136  if (cached_volumes) {
137  return volumes_[id];
138  } else
139  throw std::runtime_error("Error: cells volumes not yet computed");
140  }
141 
149  template<Entity_type type = Entity_type::ALL>
150  std::vector<int> get_neighbors(int entity) const {
151  std::vector<int> neigh, filtered;
152  // first retrieve neighbors then filter them
153  mesh_.cell_get_node_adj_cells(entity, type, &neigh);
154  // filter then
155  filtered.reserve(neigh.size());
156  for (auto const& current : neigh) {
157  if (lookup_.count(current)) {
158  filtered.emplace_back(current);
159  }
160  }
161  return filtered;
162  }
163 
169  double total_volume() const {
170  if (cached_volumes) {
171  return std::accumulate(volumes_.begin(), volumes_.end(), 0.0);
172  } else
173  throw std::runtime_error("Error: cells volumes not yet computed");
174  }
175 
182  double compute_entity_volumes(const int* masks = nullptr) {
183  if (not cached_volumes) {
184  // check if entities mask should be used
185  bool const use_masks = masks != nullptr;
186 
187  // compute the volume of each entity of the part
188  Portage::for_each(cells_.begin(), cells_.end(), [&](int s) {
189  auto const& i = index_[s];
190  auto const& volume = mesh_.cell_volume(s);
191  volumes_[i] = (use_masks ? masks[s] * volume : volume);
192  });
193  // toggle flag
194  cached_volumes = true;
195  }
196  // finally accumulate them to retrieve the global volume
197  return total_volume();
198  }
199 
200  private:
201  // mesh and state
202  Mesh const& mesh_;
203  State& state_;
204 
205  int size_ = 0;
206  bool cached_volumes = false;
207 
208  // part data consist of a list of cells, their volumes and relative indices.
209  // we rely on a hashtable to have constant-time parts lookup queries in average case.
210  // it is intended for lookup purposes only, and is not meant to be iterated.
211  std::vector<int> cells_ = {};
212  std::vector<double> volumes_ = {};
213  std::map<int, int> index_ = {};
214  std::unordered_set<int> lookup_ = {};
215  };
216 
217 
229  template<int D,
230  class SourceMesh, class SourceState,
231  class TargetMesh = SourceMesh,
232  class TargetState = SourceState
233  >
234 class PartPair {
235  // shortcuts
236  using entity_weights_t = std::vector<Wonton::Weights_t>;
239 
240 public:
244  PartPair() = default;
245 
258  SourceMesh const& source_mesh, SourceState& source_state,
259  TargetMesh const& target_mesh, TargetState& target_state,
260  std::vector<int> const& source_entities,
261  std::vector<int> const& target_entities,
262  Wonton::Executor_type const* executor
263  ) : source_(source_mesh, source_state, source_entities),
264  target_(target_mesh, target_state, target_entities)
265  {
266 #ifdef PORTAGE_ENABLE_MPI
267  auto mpiexecutor = dynamic_cast<Wonton::MPIExecutor_type const *>(executor);
268  if (mpiexecutor && mpiexecutor->mpicomm != MPI_COMM_NULL) {
269  distributed_ = true;
270  mycomm_ = mpiexecutor->mpicomm;
271  MPI_Comm_rank(mycomm_, &rank_);
272  MPI_Comm_size(mycomm_, &nprocs_);
273  }
274 #endif
275 
276  // Get info about which entities on this processor should be
277  // masked out and not accounted for in calculations because they
278  // were already encountered on a lower rank processor. We have to
279  // do this because in our distributed runs, our source partitions
280  // don't form a strict tiling (no overlaps) after redistribution
281  int nb_masks = source_.mesh().num_owned_cells();
282 
283  source_entities_masks_.resize(nb_masks, 1);
284 #ifdef PORTAGE_ENABLE_MPI
285  if (distributed_) {
286  get_unique_entity_masks<Entity_kind::CELL, SourceMesh>(
287  source_.mesh(), &source_entities_masks_, mycomm_
288  );
289  }
290 #endif
291 
292  intersection_volumes_.resize(target_.size());
293  }
294 
298  ~PartPair() = default;
299 
305  bool has_mismatch() const { return has_mismatch_; }
306 
312  bool mismatch_tested() const { return is_mismatch_tested_; }
313 
319  const SourcePart& source() const { return source_; }
320 
326  const TargetPart& target() const { return target_; }
327 
334  double compute_intersect_volumes
335  (Portage::vector<entity_weights_t> const& source_weights) {
336  // retrieve target entities list
337  auto const& target_entities = target_.cells();
338 
339  // compute the intersected volume of each target part entity
340  Portage::for_each(target_entities.begin(), target_entities.end(), [&](int t) {
341  auto const& i = target_.index(t);
342  // accumulate moments
343  entity_weights_t const moments = source_weights[t];
344  intersection_volumes_[i] = 0.;
345  for (auto const& current : moments) {
346  // matched source cell should be in the source part
347  if (source_.contains(current.entityID))
348  intersection_volumes_[i] += current.weights[0];
350  std::printf("\tmoments[target:%d][source:%d]: %f\n"
351  , t, current.entityID, current.weights[0]);
352  #endif
353  }
354  #if DEBUG_PART_BY_PART
355  std::printf("intersect_volume[%02d]: %.3f\n", t, intersection_volumes_[i]);
356  #endif
357  });
358 
359  // accumulate values to retrieve total intersected volume
360  return std::accumulate(intersection_volumes_.begin(), intersection_volumes_.end(), 0.);
361  }
362 
383  bool check_mismatch(Portage::vector<entity_weights_t> const& source_weights) {
384 
385  // ------------------------------------------
386  // COMPUTE VOLUMES ON SOURCE AND TARGET PARTS
387  // ------------------------------------------
388  // collect volumes of entities that are not masked out and sum them up
389  double source_volume = source_.compute_entity_volumes(source_entities_masks_.data());
390  double target_volume = target_.compute_entity_volumes();
391  double intersect_volume = compute_intersect_volumes(source_weights);
392 
393  global_source_volume_ = source_volume;
394  global_target_volume_ = target_volume;
395  global_intersect_volume_ = intersect_volume;
396 
397 #ifdef PORTAGE_ENABLE_MPI
398  if (distributed_) {
399  MPI_Allreduce(&source_volume, &global_source_volume_, 1, MPI_DOUBLE, MPI_SUM, mycomm_);
400  MPI_Allreduce(&target_volume, &global_target_volume_, 1, MPI_DOUBLE, MPI_SUM, mycomm_);
401  MPI_Allreduce(&intersect_volume, &global_intersect_volume_, 1, MPI_DOUBLE, MPI_SUM, mycomm_);
402 
403  if (rank_ == 0) {
404  std::printf("source volume: %.3f\n", global_source_volume_);
405  std::printf("target volume: %.3f\n", global_target_volume_);
406  std::printf("intersect volume: %.3f\n", global_intersect_volume_);
407  }
408  }
409 #else
410  std::printf("source volume: %.3f\n", source_volume);
411  std::printf("target volume: %.3f\n", target_volume);
412  std::printf("intersect volume: %.3f\n", intersect_volume);
413 #endif
414 
415  // In our initial redistribution phase, we will move as many
416  // source cells as needed from different partitions to cover the
417  // target partition, UNLESS NO SOURCE CELLS COVERING THE TARGET
418  // CELLS EXIST GLOBALLY. So, at this stage, we can just check
419  // on-rank intersections only
420 
421  // Are some source cells not fully covered by target cells?
422  // Are some target cells not fully covered by source cells
423 
424  double const relative_voldiff_source =
425  std::abs(global_intersect_volume_ - global_source_volume_)
426  / global_source_volume_;
427 
428  double const relative_voldiff_target =
429  std::abs(global_intersect_volume_ - global_target_volume_)
430  / global_target_volume_;
431 
432  if (relative_voldiff_source > tolerance_) {
433  has_mismatch_ = true;
434 
435  if (rank_ == 0) {
436  std::fprintf(stderr, "\n** MESH MISMATCH - some source cells ");
437  std::fprintf(stderr, "are not fully covered by the target mesh\n");
438  }
439  }
440 
441  if (relative_voldiff_target > tolerance_) {
442  has_mismatch_ = true;
443 
444  if (rank_ == 0) {
445  std::fprintf(stderr, "\n** MESH MISMATCH - some target cells ");
446  std::fprintf(stderr, "are not fully covered by the source mesh\n");
447  }
448  }
449 
450  if (not has_mismatch_) {
451  is_mismatch_tested_ = true;
452  return false;
453  }
454 
455  // Discrepancy between intersection volume and source mesh volume PLUS
456  // Discrepancy between intersection volume and target mesh volume
457  relative_voldiff_ = relative_voldiff_source + relative_voldiff_target;
458 
459  // Collect the empty target cells in layers starting from the ones
460  // next to partially or fully covered cells. At the end of this
461  // section, partially or fully covered cells will all have a layer
462  // number of 0 and empty cell layers will have positive layer
463  // numbers starting from 1
464  std::vector<int> empty_entities;
465  int const target_part_size = target_.size();
466 
467  empty_entities.reserve(target_part_size);
468  is_cell_empty_.resize(target_part_size, false);
469 
470  for (auto&& entity : target_.cells()) {
471  auto const& i = target_.index(entity);
472  if (std::abs(intersection_volumes_[i]) < epsilon_) {
473  empty_entities.emplace_back(entity);
474  is_cell_empty_[i] = true;
475  }
476  }
477 
478  int nb_empty = empty_entities.size();
479  int global_nb_empty = nb_empty;
480 
481 #ifdef PORTAGE_ENABLE_MPI
482  if (distributed_) {
483  global_nb_empty = 0;
484  MPI_Reduce(&nb_empty, &global_nb_empty, 1, MPI_INT, MPI_SUM, 0, mycomm_);
485  }
486 #endif
487 
488  if (global_nb_empty > 0 and rank_ == 0) {
489  std::fprintf(stderr,
490  "One or more target cells are not covered by ANY source cells.\n"
491  "Will assign values based on their neighborhood\n"
492  );
493  }
494 
495  if (nb_empty > 0) {
496  layer_num_.resize(target_.size(), 0);
497 
498  int nb_layers = 0;
499  int nb_tagged = 0;
500  int old_nb_tagged = -1;
501 
502  while (nb_tagged < nb_empty and nb_tagged > old_nb_tagged) {
503  old_nb_tagged = nb_tagged;
504 
505  std::vector<int> current_layer_entities;
506 
507  for (auto&& entity : empty_entities) {
508  auto const& i = target_.index(entity);
509  // skip already set entities
510  if (layer_num_[i] == 0) {
511 
512  auto neighbors = target_.get_neighbors(entity);
513 
514  for (auto&& neigh : neighbors) {
515  auto const& j = target_.index(neigh);
516  // At least one neighbor has some material or will
517  // receive some material (indicated by having a +ve
518  // layer number)
519  if (not is_cell_empty_[j] or layer_num_[j] != 0) {
520  current_layer_entities.push_back(entity);
521  break;
522  }
523  }
524  }
525  }
526 
527  // Tag the current layer cells with the next layer number
528  for (auto&& entity : current_layer_entities) {
529  auto const& t = target_.index(entity);
530  layer_num_[t] = nb_layers + 1;
531  }
532  nb_tagged += current_layer_entities.size();
533 
534  empty_layers_.emplace_back(current_layer_entities);
535  nb_layers++;
536  }
537  }
538 
539  is_mismatch_tested_ = true;
540  return has_mismatch_;
541  }
542 
543 
581  bool fix_mismatch(std::string src_var_name,
582  std::string trg_var_name,
583  double global_lower_bound = -infinity_,
584  double global_upper_bound = infinity_,
585  double conservation_tol = tolerance_,
586  int maxiter = 5,
587  Partial_fixup_type partial_fixup_type = SHIFTED_CONSERVATIVE,
588  Empty_fixup_type empty_fixup_type = EXTRAPOLATE) const {
589 
590  if (source_.state().field_type(Entity_kind::CELL, src_var_name) == Field_type::MESH_FIELD) {
591  return fix_mismatch_meshvar(src_var_name, trg_var_name,
592  global_lower_bound, global_upper_bound,
593  conservation_tol, maxiter,
594  partial_fixup_type, empty_fixup_type);
595  } else
596  return false;
597  }
598 
612  bool fix_mismatch_meshvar(std::string const & src_var_name,
613  std::string const & trg_var_name,
614  double global_lower_bound,
615  double global_upper_bound,
616  double conservation_tol,
617  int maxiter,
618  Partial_fixup_type partial_fixup_type,
619  Empty_fixup_type empty_fixup_type) const {
620 
621  // valid only for part-by-part scenario
622  static bool hit_lower_bound = false;
623  static bool hit_higher_bound = false;
624 
625  // use aliases
626  auto const& source_entities = source_.cells();
627  auto const& target_entities = target_.cells();
628  auto const& source_state = source_.state();
629  auto& target_state = const_cast<TargetState&>(target_.state());
630 
631  // Now process remap variables
632  // WARNING: absolute indexing
633  double const* source_data;
634  double* target_data = nullptr;
635  source_state.mesh_get_data(Entity_kind::CELL, src_var_name, &source_data);
636  target_state.mesh_get_data(Entity_kind::CELL, trg_var_name, &target_data);
637 
638  if (partial_fixup_type == LOCALLY_CONSERVATIVE) {
639  // In interpolate step, we divided the accumulated integral (U)
640  // in a target cell by the intersection volume (v_i) instead of
641  // the target cell volume (v_c) to give a target field of u_t =
642  // U/v_i. In partially filled cells, this will preserve a
643  // constant source field but fill the cell with too much material
644  // (this is the equivalent of requesting Partial_fixup_type::CONSTANT).
645  // To restore conservation (as requested by
646  // Partial_fixup_type::LOCALLY_CONSERVATIVE), we undo the division by
647  // the intersection volume and then divide by the cell volume
648  // (u'_t = U/v_c = u_t*v_i/v_c). This does not affect the values
649  // in fully filled cells
650 
651  for (auto&& entity : target_entities) {
652  auto const& t = target_.index(entity);
653  if (not is_cell_empty_[t]) {
654 
655  #if DEBUG_PART_BY_PART
656  std::printf("fixing target_data[%d] with locally conservative fixup\n", entity);
657  std::printf("= before: %.3f", target_data[entity]);
658  #endif
659 
660  auto const relative_voldiff =
661  std::abs(intersection_volumes_[t] - target_.volume(t)) / target_.volume(t);
662 
663  if (relative_voldiff > tolerance_) {
664  target_data[entity] *= intersection_volumes_[t] / target_.volume(t);
665  }
666  #if DEBUG_PART_BY_PART
667  std::printf(", after: %.3f\n", target_data[entity]);
668  #endif
669  }
670  }
671  }
672 
673 
674  if (empty_fixup_type != LEAVE_EMPTY) {
675  // Do something here to populate fully uncovered target
676  // cells. We have layers of empty cells starting out from fully
677  // or partially populated cells. We will assign every empty cell
678  // in a layer the average value of all its populated neighbors.
679  // IN A DISTRIBUTED MESH IT _IS_ POSSIBLE THAT AN EMPTY ENTITY
680  // WILL NOT HAVE ANY OWNED NEIGHBOR IN THIS PARTITION THAT HAS
681  // MEANINGFUL DATA TO EXTRAPOLATE FROM (we remap data only to
682  // owned entities)
683  int current_layer_number = 1;
684 
685  for (auto const& current_layer : empty_layers_) {
686  for (auto&& entity : current_layer) {
687 
688  double averaged_value = 0.;
689  int nb_extrapol = 0;
690  auto neighbors =
691  target_.template get_neighbors<Entity_type::PARALLEL_OWNED>(entity);
692 
693  for (auto&& neigh : neighbors) {
694  auto const& i = target_.index(neigh);
695  if (layer_num_[i] < current_layer_number) {
696  averaged_value += target_data[neigh];
697  nb_extrapol++;
698  }
699  }
700  if (nb_extrapol > 0) {
701  averaged_value /= nb_extrapol;
702  }
703  #if DEBUG_PART_BY_PART
704  else {
705  std::fprintf(stderr,
706  "No owned neighbors of empty entity to extrapolate data from\n"
707  );
708  }
709  #endif
710  target_data[entity] = averaged_value;
711  }
712  current_layer_number++;
713  }
714  }
715 
716  // if the fixup scheme is constant or locally conservative then we're done
717  if (partial_fixup_type == CONSTANT or partial_fixup_type == LOCALLY_CONSERVATIVE) {
718  return true;
719  } else if (partial_fixup_type == SHIFTED_CONSERVATIVE) {
720 
721  // At this point assume that all cells have some value in them
722  // for the variable
723  // Now compute the net discrepancy between integrals over source
724  // and target. Excess comes from target cells not fully covered by
725  // source cells and deficit from source cells not fully covered by
726  // target cells
727  double source_sum = 0.;
728  double target_sum = 0.;
729 
730  for (auto&& s : source_entities) {
731  auto const& i = source_.index(s);
732  source_sum += source_data[s] * source_.volume(i);
733  }
734 
735  for (auto&& t : target_entities) {
736  auto const& i = target_.index(t);
737  target_sum += target_data[t] * target_.volume(i);
738  }
739 
740  double global_source_sum = source_sum;
741  double global_target_sum = target_sum;
742 
743 #ifdef PORTAGE_ENABLE_MPI
744  if (distributed_) {
745  MPI_Allreduce(
746  &source_sum, &global_source_sum, 1, MPI_DOUBLE, MPI_SUM, mycomm_
747  );
748 
749  MPI_Allreduce(
750  &target_sum, &global_target_sum, 1, MPI_DOUBLE, MPI_SUM, mycomm_
751  );
752  }
753 #endif
754 
755  double absolute_diff = global_target_sum - global_source_sum;
756  double relative_diff = absolute_diff / global_source_sum;
757 
758  if (std::abs(relative_diff) < conservation_tol) {
759  return true; // discrepancy is too small - nothing to do
760  }
761 
762  // Now redistribute the discrepancy among cells in proportion to
763  // their volume. This will restore conservation and if the
764  // original distribution was a constant it will make the field a
765  // slightly different constant. Do multiple iterations because
766  // we may have some leftover quantity that we were not able to
767  // add/subtract from some cells in any given iteration. We don't
768  // expect that process to take more than two iterations at the
769  // most.
770 
771  double adj_target_volume;
772  double global_adj_target_volume;
773  double global_covered_target_volume;
774 
775  if (empty_fixup_type == Empty_fixup_type::LEAVE_EMPTY) {
776  double covered_target_volume = 0.;
777  for (auto&& entity : target_entities) {
778  auto const& t = target_.index(entity);
779  if (not is_cell_empty_[t]) {
780  covered_target_volume += target_.volume(t);
781  }
782  }
783  global_covered_target_volume = covered_target_volume;
784 #ifdef PORTAGE_ENABLE_MPI
785  if (distributed_) {
786  MPI_Allreduce(
787  &covered_target_volume, &global_covered_target_volume,
788  1, MPI_DOUBLE, MPI_SUM, mycomm_
789  );
790  }
791 #endif
792  adj_target_volume = covered_target_volume;
793  global_adj_target_volume = global_covered_target_volume;
794  }
795  else {
796  adj_target_volume = target_.total_volume();
797  global_adj_target_volume = global_target_volume_;
798  }
799 
800  // sort of a "unit" discrepancy or difference per unit volume
801  double udiff = absolute_diff / global_adj_target_volume;
802 
803  int iter = 0;
804  while (std::abs(relative_diff) > conservation_tol and iter < maxiter) {
805 
806  for (auto&& entity : target_entities) {
807  auto const& t = target_.index(entity);
808  bool is_owned = target_.mesh().cell_get_type(entity) == Entity_type::PARALLEL_OWNED;
809  bool should_fix = (empty_fixup_type != LEAVE_EMPTY or not is_cell_empty_[t]);
810 
811  if (is_owned and should_fix) {
812  if ((target_data[entity] - udiff) < global_lower_bound) {
813  // Subtracting the full excess will make this cell violate the
814  // lower bound. So subtract only as much as will put this cell
815  // exactly at the lower bound
816  target_data[entity] = global_lower_bound;
817 
818  if (not hit_lower_bound) {
819  std::fprintf(stderr,
820  "Hit lower bound for cell %d (and maybe other ones) on rank %d\n",
821  t, rank_
822  );
823  hit_lower_bound = true;
824  }
825  // this cell is no longer in play for adjustment - so remove its
826  // volume from the adjusted target_volume
827  adj_target_volume -= target_.volume(t);
828 
829  } else if ((target_data[entity] - udiff) > global_upper_bound) { // udiff < 0
830  // Adding the full deficit will make this cell violate the
831  // upper bound. So add only as much as will put this cell
832  // exactly at the upper bound
833  target_data[entity] = global_upper_bound;
834 
835  if (not hit_higher_bound) {
836  std::fprintf(stderr,
837  "Hit upper bound for cell %d (and maybe other ones) on rank %d\n",
838  t, rank_
839  );
840  hit_higher_bound = true;
841  }
842 
843  // this cell is no longer in play for adjustment - so remove its
844  // volume from the adjusted target_volume
845  adj_target_volume -= target_.volume(t);
846  } else {
847  // This is the equivalent of
848  // [curval*cellvol - diff*cellvol/meshvol]
849  // curval = ---------------------------------------
850  // cellvol
851  target_data[entity] -= udiff;
852  }
853  } // only non-empty cells
854  } // iterate through mesh cells
855 
856  // Compute the new integral over all processors
857  target_sum = 0.;
858  for (auto&& entity : target_entities) {
859  auto const& t = target_.index(entity);
860  target_sum += target_.volume(t) * target_data[entity];
861  }
862 
863  global_target_sum = target_sum;
864 #ifdef PORTAGE_ENABLE_MPI
865  if (distributed_) {
866  MPI_Allreduce(
867  &target_sum, &global_target_sum, 1, MPI_DOUBLE, MPI_SUM, mycomm_
868  );
869  }
870 #endif
871 
872  // If we did not hit lower or upper bounds, this should be
873  // zero after the first iteration. If we did hit some bounds,
874  // then recalculate the discrepancy and discrepancy per unit
875  // volume, but only taking into account volume of cells that
876  // are not already at the bounds - if we use the entire mesh
877  // volume for the recalculation, the convergence slows down
878  absolute_diff = global_target_sum - global_source_sum;
879  global_adj_target_volume = adj_target_volume;
880 
881 #ifdef PORTAGE_ENABLE_MPI
882  if (distributed_) {
883  MPI_Allreduce(
884  &adj_target_volume, &global_adj_target_volume,
885  1, MPI_DOUBLE, MPI_SUM, mycomm_
886  );
887  }
888 #endif
889 
890  udiff = absolute_diff / global_adj_target_volume;
891  relative_diff = absolute_diff / global_source_sum;
892 
893  // Now reset adjusted target mesh volume to be the full volume
894  // in preparation for the next iteration
895  global_adj_target_volume = (
896  empty_fixup_type == LEAVE_EMPTY ? global_covered_target_volume
897  : global_target_volume_
898  );
899 
900  iter++;
901  } // while leftover is not zero
902 
903  if (std::abs(relative_diff) > conservation_tol) {
904  if (rank_ == 0) {
905  std::fprintf(stderr,
906  "Redistribution not entirely successfully for variable %s\n"
907  "Relative conservation error is %f\n"
908  "Absolute conservation error is %f\n",
909  src_var_name.data(), relative_diff, absolute_diff
910  );
911  return false;
912  }
913  }
914 
915  return true;
916  } else {
917  std::fprintf(stderr, "Unknown Partial fixup type\n");
918  return false;
919  }
920  }
921 
922 
923 private:
924  // source and target mesh parts
925  SourcePart source_;
926  TargetPart target_;
927 
928  bool is_mismatch_tested_ = false;
929  bool has_mismatch_ = false;
930 
931  // useful constants
932  static constexpr double infinity_ = std::numeric_limits<double>::max();
933  static constexpr double epsilon_ = std::numeric_limits<double>::epsilon();
934  static constexpr double tolerance_ = 1.E2 * epsilon_;
935 
936  std::vector<int> source_entities_masks_ = {};
937  std::vector<double> intersection_volumes_ = {};
938 
939  // data needed for mismatch checks
940  double global_source_volume_ = 0.;
941  double global_target_volume_ = 0.;
942  double global_intersect_volume_ = 0.;
943  double relative_voldiff_ = 0.;
944 
945  // empty target cells management
946  std::vector<int> layer_num_ = {};
947  std::vector<bool> is_cell_empty_ = {};
948  std::vector<std::vector<int>> empty_layers_ = {};
949 
950  // MPI
951  int rank_ = 0;
952  int nprocs_ = 1;
953  bool distributed_ = false;
954 #ifdef PORTAGE_ENABLE_MPI
955  MPI_Comm mycomm_ = MPI_COMM_NULL;
956 #endif
957 };
958 
959 } // end namespace Portage
960 #endif //PORTAGE_PARTS_H
bool check_mismatch(Portage::vector< entity_weights_t > const &source_weights)
Check and fix source/target boundaries mismatch.
Definition: parts.h:383
bool contains(int id) const
Check if a given entity is in the part.
Definition: parts.h:111
State & state()
Get a normal reference to the underlying state.
Definition: parts.h:96
std::vector< int > const & cells() const
Get a reference to the cell list.
Definition: parts.h:103
PartPair(SourceMesh const &source_mesh, SourceState &source_state, TargetMesh const &target_mesh, TargetState &target_state, std::vector< int > const &source_entities, std::vector< int > const &target_entities, Wonton::Executor_type const *executor)
Construct a source-target mesh parts pair.
Definition: parts.h:257
std::vector< T > vector
Definition: portage.h:238
Part(Mesh const &mesh, State &state, std::vector< int > const &cells)
Construct a mesh part object.
Definition: parts.h:53
const int & index(int id) const
Retrieve relative index of given entity.
Definition: parts.h:119
std::vector< int > get_neighbors(int entity) const
Retrieve the neighbors of the given entity on mesh part.
Definition: parts.h:150
bool fix_mismatch_meshvar(std::string const &src_var_name, std::string const &trg_var_name, double global_lower_bound, double global_upper_bound, double conservation_tol, int maxiter, Partial_fixup_type partial_fixup_type, Empty_fixup_type empty_fixup_type) const
Repair a remapped mesh field to account for boundary mismatch.
Definition: parts.h:612
void for_each(InputIterator first, InputIterator last, UnaryFunction f)
Definition: portage.h:264
Definition: portage.h:114
bool has_mismatch() const
Do source and target meshes have a boundary mismatch?
Definition: parts.h:305
Partial_fixup_type
Fixup options for partially filled cells.
Definition: portage.h:114
Definition: portage.h:134
double const epsilon
Numerical tolerance.
Definition: weight.h:34
bool mismatch_tested() const
Is mismatch already tested?
Definition: parts.h:312
Manages source and target sub-meshes for part-by-part remap. It detects boundaries mismatch and provi...
Definition: parts.h:234
const SourcePart & source() const
Retrieve a pointer to source mesh part.
Definition: parts.h:319
std::vector< Wonton::Weights_t > entity_weights_t
Definition: parts.h:36
const State & state() const
Get a constant reference to the underlying state.
Definition: parts.h:89
Definition: portage.h:134
const Mesh & mesh() const
Get a constant reference to the underlying mesh.
Definition: parts.h:82
#define DEBUG_PART_BY_PART
Definition: parts.h:25
Definition: portage.h:114
Part()=default
Default constructor.
const int & size() const
Get part size.
Definition: parts.h:126
Definition: coredriver.h:42
bool fix_mismatch(std::string src_var_name, std::string trg_var_name, double global_lower_bound=-infinity_, double global_upper_bound=infinity_, double conservation_tol=tolerance_, int maxiter=5, Partial_fixup_type partial_fixup_type=SHIFTED_CONSERVATIVE, Empty_fixup_type empty_fixup_type=EXTRAPOLATE) const
Repair the remapped field to account for boundary mismatch.
Definition: parts.h:581
Definition: parts.h:39
const TargetPart & target() const
Retrieve a pointer to target mesh part.
Definition: parts.h:326
~Part()=default
const double & volume(int id) const
Get the volume of the given entity.
Definition: parts.h:134
Definition: portage.h:114
double total_volume() const
Compute the total volume of the part.
Definition: parts.h:169
double compute_entity_volumes(const int *masks=nullptr)
Compute and store volumes of each cell of the part.
Definition: parts.h:182
Empty_fixup_type
Fixup options for empty cells.
Definition: portage.h:134