fix_mismatch.h
Go to the documentation of this file.
1 /*
2 This 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_DETECT_MISMATCH_H_
8 #define PORTAGE_DRIVER_DETECT_MISMATCH_H_
9 
10 #include <sys/time.h>
11 
12 #include <algorithm>
13 #include <vector>
14 #include <iterator>
15 #include <unordered_set>
16 #include <string>
17 #include <utility>
18 #include <iostream>
19 #include <type_traits>
20 #include <limits>
21 #include <numeric>
22 #include <stdexcept>
23 
25 
31 namespace Portage {
32 
33 using Wonton::Entity_kind;
34 using Wonton::Entity_type;
35 using Wonton::Weights_t;
36 
37 
38 // Helper function
39 //
40 // Get a mask indicating which entities on this processor have not
41 // been encountered on previous processors. Mask of 1 means this
42 // entity is being seen for the first time and should figure in any
43 // calculation, 0 means this entity has been encountered on a previous
44 // processor. This is useful for meshes where the partitioning of cells
45 // on ranks is not mutually exclusive.
46 
47 #ifdef PORTAGE_ENABLE_MPI
48 
49 template<Entity_kind onwhat, class Mesh_Wrapper>
50 void get_unique_entity_masks(Mesh_Wrapper const &mesh,
51  std::vector<int> *unique_mask,
52  MPI_Comm mycomm) {
53 
54  int rank = 0;
55  int nprocs = 1;
56  MPI_Comm_rank(mycomm, &rank);
57  MPI_Comm_size(mycomm, &nprocs);
58 
59 
60  // collect source and target cell volumes because we will need it
61  // a few times for each variable we process
62 
63  int nents = (onwhat == Entity_kind::CELL) ?
64  mesh.num_owned_cells() : mesh.num_owned_nodes();
65 
66  unique_mask->resize(nents, 1);
67 
68  if (nprocs > 1) {
69  std::vector<int> nents_all(nprocs, 0);
70  MPI_Allgather(&nents, 1, MPI_INT, &(nents_all[0]), 1, MPI_INT, mycomm);
71  int maxents = *std::max_element(nents_all.begin(), nents_all.end());
72 
73  std::vector<int> gids(maxents, -1);
74  for (int e = 0; e < nents; e++)
75  gids[e] = mesh.get_global_id(e, onwhat);
76 
77  std::vector<int> gids_all(nprocs*maxents);
78  MPI_Allgather(&(gids[0]), maxents, MPI_INT, &(gids_all[0]), maxents,
79  MPI_INT, mycomm);
80 
81  // Add gids from all lower ranks into a set (no duplicates)
82 
83  std::unordered_set<int> unique_gids;
84  for (int p = 0; p < rank; p++)
85  for (int e = 0; e < nents_all[p]; e++) {
86  int gid = gids_all[p*maxents+e];
87  unique_gids.insert(gid);
88  }
89 
90  // Now go through gids of this rank and mask the ones that are already
91  // occurring in other ranks.
92  for (int e = 0; e < nents; e++) {
93  auto itpair = unique_gids.insert(gids[e]);
94  if (!itpair.second)
95  (*unique_mask)[e] = 0; // ent already in set; mask this instance
96  }
97  }
98 } // get_unique_entity_masks
99 
100 #endif
101 
102 
103 
104 // Check if we have mismatch of mesh boundaries
105 // T is the target mesh, S is the source mesh
106 // T_i is the i'th cell of the target mesh and
107 // |*| signifies the extent/volume of an entity
108 //
109 // If sum_i(|T_i intersect S|) NE |S|, some source cells are not
110 // completely covered by the target mesh
111 //
112 // If sum_i(|T_i intersect S|) NE |T|, some target cells are not
113 // completely covered by the source mesh
114 //
115 // If there is a mismatch, adjust values for fields to account so that
116 // integral quantities are conserved and a constant field is readjusted
117 // to a different constant
118 
119 template<int D, Entity_kind onwhat,
120  class SourceMesh_Wrapper, class SourceState_Wrapper,
121  class TargetMesh_Wrapper, class TargetState_Wrapper>
123  public:
124 
125  MismatchFixer(SourceMesh_Wrapper const& source_mesh,
126  SourceState_Wrapper const& source_state,
127  TargetMesh_Wrapper const& target_mesh,
128  TargetState_Wrapper & target_state,
129  Wonton::Executor_type const *executor,
130  bool global_check=true) :
131  source_mesh_(source_mesh), source_state_(source_state),
132  target_mesh_(target_mesh), target_state_(target_state),
133  global_check_(global_check) {
134 
135 #ifdef PORTAGE_ENABLE_MPI
136  auto mpiexecutor = dynamic_cast<Wonton::MPIExecutor_type const *>(executor);
137  if (mpiexecutor && mpiexecutor->mpicomm != MPI_COMM_NULL) {
138  distributed_ = true;
139  mycomm_ = mpiexecutor->mpicomm;
140  MPI_Comm_rank(mycomm_, &rank_);
141  MPI_Comm_size(mycomm_, &nprocs_);
142  }
143 #endif
144 
145  } // MismatchFixer
146 
147 
148 
149 
154  Portage::vector<std::vector<Weights_t>> const & source_ents_and_weights) {
155 
156  // If we have already computed the mismatch, just return the result
157  if (computed_mismatch_) return mismatch_;
158 
159  nsourceents_ = (onwhat == Entity_kind::CELL) ?
160  source_mesh_.num_owned_cells() : source_mesh_.num_owned_nodes();
161 
162  // Get info about which entities on this processor should be
163  // masked out and not accounted for in calculations because they
164  // were already encountered on a lower rank processor. We have to
165  // do this because in our distributed runs, our source partitions
166  // don't form a strict tiling (no overlaps) after redistribution
167 
168  std::vector<int> source_ent_masks(nsourceents_, 1);
169 #ifdef PORTAGE_ENABLE_MPI
170  if (distributed_ && global_check_)
171  get_unique_entity_masks<onwhat, SourceMesh_Wrapper>(source_mesh_,
172  &source_ent_masks,
173  mycomm_);
174 #endif
175 
176  // collect volumes of entities that are not masked out and sum them up
177 
178  source_ent_volumes_.resize(nsourceents_, 0.0);
179  for (int s = 0; s < nsourceents_; s++)
180  source_ent_volumes_[s] = (onwhat == Entity_kind::CELL) ?
181  source_ent_masks[s]*source_mesh_.cell_volume(s) :
182  source_ent_masks[s]*source_mesh_.dual_cell_volume(s);
183 
184  source_volume_ =
185  std::accumulate(source_ent_volumes_.begin(), source_ent_volumes_.end(),
186  0.0);
187 
188  global_source_volume_ = source_volume_;
189 #ifdef PORTAGE_ENABLE_MPI
190  if (distributed_ && global_check_)
191  MPI_Allreduce(&source_volume_, &global_source_volume_, 1, MPI_DOUBLE,
192  MPI_SUM, mycomm_);
193 #endif
194 
195  // GLOBAL TARGET VOLUME
196  ntargetents_ = (onwhat == Entity_kind::CELL) ?
197  target_mesh_.num_owned_cells() : target_mesh_.num_owned_nodes();
198 
199  target_ent_volumes_.resize(ntargetents_, 0.0);
200  for (int t = 0; t < ntargetents_; t++)
201  target_ent_volumes_[t] = (onwhat == Entity_kind::CELL) ?
202  target_mesh_.cell_volume(t) : target_mesh_.dual_cell_volume(t);
203 
204  target_volume_ = std::accumulate(target_ent_volumes_.begin(),
205  target_ent_volumes_.end(), 0.0);
206 
207 
208  global_target_volume_ = target_volume_;
209 #ifdef PORTAGE_ENABLE_MPI
210  if (distributed_ && global_check_)
211  MPI_Allreduce(&target_volume_, &global_target_volume_, 1, MPI_DOUBLE,
212  MPI_SUM, mycomm_);
213 #endif
214 
215 
216  // GLOBAL INTERSECTION VOLUME
217  // In our initial redistribution phase, we will move as many
218  // source cells as needed from different partitions to cover the
219  // target partition, UNLESS NO SOURCE CELLS COVERING THE TARGET
220  // CELLS EXIST GLOBALLY. So, at this stage, we can just check
221  // on-rank intersections only
222 
223  xsect_volumes_.resize(ntargetents_, 0.0);
224  for (int t = 0; t < ntargetents_; t++) {
225  std::vector<Weights_t> const& sw_vec = source_ents_and_weights[t];
226  for (auto const& sw : sw_vec)
227  xsect_volumes_[t] += sw.weights[0];
228  }
229 
230  double xsect_volume = std::accumulate(xsect_volumes_.begin(),
231  xsect_volumes_.end(), 0.0);
232 
233  global_xsect_volume_ = xsect_volume;
234 #ifdef PORTAGE_ENABLE_MPI
235  if (distributed_ && global_check_)
236  MPI_Allreduce(&xsect_volume, &global_xsect_volume_, 1,
237  MPI_DOUBLE, MPI_SUM, mycomm_);
238 #endif
239 
240 
241 
242 
243  // Are some source cells not fully covered by target cells?
244  relvoldiff_source_ =
245  fabs(global_xsect_volume_-global_source_volume_)/global_source_volume_;
246  if (relvoldiff_source_ > voldifftol_) {
247 
248  mismatch_ = true;
249 
250  if (rank_ == 0)
251  std::cerr << "\n** MESH MISMATCH -" <<
252  " some source cells are not fully covered by the target mesh\n";
253 
254 #ifdef DEBUG
255  // Find one source cell (or dual cell) that is not fully covered
256  // by the target mesh and output its ID. Unfortunately, that means
257  // processing all source cells. We initialize each source cell to
258  // its volume and subtract any intersection volume we find between
259  // it and a target cell
260 
261  // Also, it will likely give a false positive in distributed
262  // meshes because a source cell may be covered by target cells
263  // from multiple processors
264 
265  std::vector<double> source_covered_vol(source_ent_volumes_);
266  for (auto it = target_mesh_.begin(onwhat, Entity_type::PARALLEL_OWNED);
267  it != target_mesh_.end(onwhat, Entity_type::PARALLEL_OWNED); it++) {
268  std::vector<Weights_t> const& sw_vec = source_ents_and_weights[*it];
269  for (auto const& sw : sw_vec)
270  source_covered_vol[sw.entityID] -= sw.weights[0];
271  }
272 
273  for (auto it = source_mesh_.begin(onwhat, Entity_type::PARALLEL_OWNED);
274  it != source_mesh_.end(onwhat, Entity_type::PARALLEL_OWNED); it++)
275  if (source_covered_vol[*it] > voldifftol_) {
276  if (onwhat == Entity_kind::CELL)
277  std::cerr << "Source cell " << *it <<
278  " not fully covered by target cells \n";
279  else
280  std::cerr << "Source dual cell " << *it <<
281  " not fully covered by target dual cells \n";
282  break;
283  }
284 #endif
285  }
286 
287  // Are some target cells not fully covered by source cells?
288 
289  relvoldiff_target_ =
290  fabs(global_xsect_volume_-global_target_volume_)/global_target_volume_;
291  if (relvoldiff_target_ > voldifftol_) {
292 
293  mismatch_ = true;
294 
295  if (rank_ == 0)
296  std::cerr << "\n** MESH MISMATCH -" <<
297  " some target cells are not fully covered by the source mesh\n";
298 
299 #ifdef DEBUG
300  // Find one target cell that is not fully covered by the source mesh and
301  // output its ID
302  for (auto it = target_mesh_.begin(onwhat, Entity_type::PARALLEL_OWNED);
303  it != target_mesh_.end(onwhat, Entity_type::PARALLEL_OWNED); it++) {
304  int t = *it;
305  double covered_vol = 0.0;
306  std::vector<Weights_t> const& sw_vec = source_ents_and_weights[t];
307  for (auto const& sw : sw_vec)
308  covered_vol += sw.weights[0];
309  if (fabs(covered_vol-target_ent_volumes_[t])/target_ent_volumes_[t] > voldifftol_) {
310  if (onwhat == Entity_kind::CELL)
311  std::cerr << "Target cell " << *it << " on rank " << rank_ <<
312  " not fully covered by source cells \n";
313  else
314  std::cerr << "Target dual cell " << *it << " on rank " << rank_ <<
315  " not fully covered by source dual cells \n";
316  break;
317  }
318  }
319 #endif
320 
321  }
322 
323  // compute layers
324  compute_layers();
325 
326  // set whether we have computed the mismatch (before any return)
327  computed_mismatch_ = true;
328 
329  return mismatch_;
330  }
331 
332 
336  bool has_mismatch() const {
337 
338  // make sure we have already computed the mismatch
339  assert(computed_mismatch_ && "check_mismatch must be called first!");
340 
341  return mismatch_;
342  }
343 
344 
345 
346 
376 
377  bool fix_mismatch(std::string const & src_var_name,
378  std::string const & trg_var_name,
379  double global_lower_bound = -std::numeric_limits<double>::max(),
380  double global_upper_bound = std::numeric_limits<double>::max(),
381  double conservation_tol = 1e2*std::numeric_limits<double>::epsilon(),
382  int maxiter = 5,
383  Partial_fixup_type partial_fixup_type =
385  Empty_fixup_type empty_fixup_type =
387 
388 
389  // Make sure the user isn't trying to do a global fixup without a global check.
390  // A serial run will always proceed.
391  if (distributed_ && !global_check_ &&
392  partial_fixup_type==Partial_fixup_type::SHIFTED_CONSERVATIVE) {
393  throw std::runtime_error(
394  "Cannot implement SHIFTED_CONSERVATIVE in a distributed run without MPI!");
395  }
396 
397  // Make sure the user isn't trying to extrapolate into empty cels without having
398  // computed layers first
399  if (empty_fixup_type==Empty_fixup_type::EXTRAPOLATE && !computed_layers_) {
400  throw std::runtime_error(
401  "Cannot extrapolate into empty cells without computing layers first!");
402  }
403 
404  // make sure we have already computed the mismatch
405  if (!computed_mismatch_) {
406  throw std::runtime_error("check_mismatch must be called first!");
407  }
408 
409  if (source_state_.field_type(onwhat, src_var_name) ==
410  Field_type::MESH_FIELD)
411  return fix_mismatch_meshvar(src_var_name, trg_var_name,
412  global_lower_bound, global_upper_bound,
413  conservation_tol, maxiter,
414  partial_fixup_type, empty_fixup_type);
415  return false;
416  }
417 
418 
420 
421  bool fix_mismatch_meshvar(std::string const & src_var_name,
422  std::string const & trg_var_name,
423  double global_lower_bound,
424  double global_upper_bound,
425  double conservation_tol = 1e2*std::numeric_limits<double>::epsilon(),
426  int maxiter = 5,
427  Partial_fixup_type partial_fixup_type =
429  Empty_fixup_type empty_fixup_type =
431 
432  static bool hit_lobound = false, hit_hibound = false;
433 
434  // Now process remap variables
435  double const *source_data;
436  source_state_.mesh_get_data(onwhat, src_var_name, &source_data);
437 
438  double *target_data;
439  target_state_.mesh_get_data(onwhat, trg_var_name, &target_data);
440 
441  if (partial_fixup_type == Partial_fixup_type::LOCALLY_CONSERVATIVE) {
442  // In interpolate step, we divided the accumulated integral (U)
443  // in a target cell by the intersection volume (v_i) instead of
444  // the target cell volume (v_c) to give a target field of u_t =
445  // U/v_i. In partially filled cells, this will preserve a
446  // constant source field but fill the cell with too much material
447  // (this is the equivalent of requesting Partial_fixup_type::CONSTANT).
448  // To restore conservation (as requested by
449  // Partial_fixup_type::LOCALLY_CONSERVATIVE), we undo the division by
450  // the intersection volume and then divide by the cell volume
451  // (u'_t = U/v_c = u_t*v_i/v_c). This does not affect the values
452  // in fully filled cells
453 
454  for (int t = 0; t < ntargetents_; t++) {
455  if (!is_cell_empty_[t]) {
456  if (fabs(xsect_volumes_[t]-target_ent_volumes_[t])/target_ent_volumes_[t] > voldifftol_)
457  target_data[t] *= xsect_volumes_[t]/target_ent_volumes_[t];
458  }
459  }
460  }
461 
462  if (empty_fixup_type != Empty_fixup_type::LEAVE_EMPTY) {
463  // Do something here to populate fully uncovered target
464  // cells. We have layers of empty cells starting out from fully
465  // or partially populated cells. We will assign every empty cell
466  // in a layer the average value of all its populated neighbors.
467  // IN A DISTRIBUTED MESH IT _IS_ POSSIBLE THAT AN EMPTY ENTITY
468  // WILL NOT HAVE ANY OWNED NEIGHBOR IN THIS PARTITION THAT HAS
469  // MEANINGFUL DATA TO EXTRAPOLATE FROM (we remap data only to
470  // owned entities)
471 
472  int curlayernum = 1;
473  for (std::vector<int> const& curlayer : emptylayers_) {
474  for (int ent : curlayer) {
475  std::vector<int> nbrs;
476  if (onwhat == Entity_kind::CELL)
477  target_mesh_.cell_get_node_adj_cells(ent, Entity_type::PARALLEL_OWNED,
478  &nbrs);
479  else
480  target_mesh_.node_get_cell_adj_nodes(ent, Entity_type::PARALLEL_OWNED,
481  &nbrs);
482 
483  double aveval = 0.0;
484  int nave = 0;
485  for (int nbr : nbrs) {
486  if (layernum_[nbr] < curlayernum) {
487  aveval += target_data[nbr];
488  nave++;
489  }
490  }
491  if (nave)
492  aveval /= nave;
493 #ifdef DEBUG
494  else
495  std::cerr <<
496  "No owned neighbors of empty entity to extrapolate data from\n";
497 #endif
498 
499  target_data[ent] = aveval;
500  }
501  curlayernum++;
502  }
503  }
504 
505 
506  if (partial_fixup_type == Partial_fixup_type::CONSTANT ||
507  partial_fixup_type == Partial_fixup_type::LOCALLY_CONSERVATIVE) {
508 
509  return true;
510 
511  } else if (partial_fixup_type == Partial_fixup_type::SHIFTED_CONSERVATIVE) {
512 
513  // At this point assume that all cells have some value in them
514  // for the variable
515 
516  // Now compute the net discrepancy between integrals over source
517  // and target. Excess comes from target cells not fully covered by
518  // source cells and deficit from source cells not fully covered by
519  // target cells
520 
521  double source_sum =
522  std::inner_product(source_data, source_data + nsourceents_,
523  source_ent_volumes_.begin(), 0.0);
524 
525  double global_source_sum = source_sum;
526 #ifdef PORTAGE_ENABLE_MPI
527  if (distributed_ && global_check_)
528  MPI_Allreduce(&source_sum, &global_source_sum, 1, MPI_DOUBLE, MPI_SUM,
529  mycomm_);
530 #endif
531 
532  double target_sum =
533  std::inner_product(target_data, target_data + ntargetents_,
534  target_ent_volumes_.begin(), 0.0);
535 
536  double global_target_sum = target_sum;
537 #ifdef PORTAGE_ENABLE_MPI
538  if (distributed_ && global_check_)
539  MPI_Allreduce(&target_sum, &global_target_sum, 1, MPI_DOUBLE, MPI_SUM,
540  mycomm_);
541 #endif
542 
543  double global_diff = global_target_sum - global_source_sum;
544  double reldiff = global_diff/global_source_sum;
545 
546  if (fabs(reldiff) < conservation_tol)
547  return true; // discrepancy is too small - nothing to do
548 
549  // Now redistribute the discrepancy among cells in proportion to
550  // their volume. This will restore conservation and if the
551  // original distribution was a constant it will make the field a
552  // slightly different constant. Do multiple iterations because
553  // we may have some leftover quantity that we were not able to
554  // add/subtract from some cells in any given iteration. We don't
555  // expect that process to take more than two iterations at the
556  // most.
557 
558  double adj_target_volume;
559  double global_adj_target_volume;
560  double global_covered_target_volume;
561  if (empty_fixup_type == Empty_fixup_type::LEAVE_EMPTY) {
562  double covered_target_volume = 0.0;
563  for (int t = 0; t < ntargetents_; t++) {
564  if (!is_cell_empty_[t]) {
565  covered_target_volume += target_ent_volumes_[t];
566  }
567  }
568  global_covered_target_volume = covered_target_volume;
569 #ifdef PORTAGE_ENABLE_MPI
570  if (distributed_ && global_check_)
571  MPI_Allreduce(&covered_target_volume, &global_covered_target_volume,
572  1, MPI_DOUBLE, MPI_SUM, mycomm_);
573 #endif
574  adj_target_volume = covered_target_volume;
575  global_adj_target_volume = global_covered_target_volume;
576  }
577  else {
578  adj_target_volume = target_volume_;
579  global_adj_target_volume = global_target_volume_;
580  }
581 
582  // sort of a "unit" discrepancy or difference per unit volume
583  double udiff = global_diff/global_adj_target_volume;
584 
585  int iter = 0;
586  while (fabs(reldiff) > conservation_tol && iter < maxiter) {
587  for (auto it = target_mesh_.begin(onwhat, Entity_type::PARALLEL_OWNED);
588  it != target_mesh_.end(onwhat, Entity_type::PARALLEL_OWNED); it++) {
589  int t = *it;
590  if (empty_fixup_type != Empty_fixup_type::LEAVE_EMPTY || !is_cell_empty_[t]) {
591 
592  if ((target_data[t]-udiff) < global_lower_bound) {
593  // Subtracting the full excess will make this cell violate the
594  // lower bound. So subtract only as much as will put this cell
595  // exactly at the lower bound
596 
597  target_data[t] = global_lower_bound;
598 
599  if (!hit_lobound) {
600  std::cerr << "Hit lower bound for cell " << t <<
601  " (and maybe other cells) on rank " << rank_ << "\n";
602  hit_lobound = true;
603  }
604 
605  // this cell is no longer in play for adjustment - so remove its
606  // volume from the adjusted target_volume
607 
608  adj_target_volume -= target_ent_volumes_[t];
609 
610  } else if ((target_data[t]-udiff) > global_upper_bound) { // udiff < 0
611  // Adding the full deficit will make this cell violate the
612  // upper bound. So add only as much as will put this cell
613  // exactly at the upper bound
614 
615  target_data[t] = global_upper_bound;
616 
617  if (!hit_hibound) {
618  std::cerr << "Hit upper bound for cell " << t <<
619  " (and maybe other cells) on rank " << rank_ << "\n";
620  hit_hibound = true;
621  }
622 
623  // this cell is no longer in play for adjustment - so remove its
624  // volume from the adjusted target_volume
625 
626  adj_target_volume -= target_ent_volumes_[t];
627 
628  } else {
629  // This is the equivalent of
630  // [curval*cellvol - diff*cellvol/meshvol]
631  // curval = ---------------------------------------
632  // cellvol
633 
634  target_data[t] -= udiff;
635 
636  }
637  } // only non-empty cells
638  } // iterate through mesh cells
639 
640  // Compute the new integral over all processors
641 
642  target_sum = std::inner_product(target_data, target_data + ntargetents_,
643  target_ent_volumes_.begin(), 0.0);
644 
645  global_target_sum = target_sum;
646 #ifdef PORTAGE_ENABLE_MPI
647  if (distributed_ && global_check_)
648  MPI_Allreduce(&target_sum, &global_target_sum, 1, MPI_DOUBLE, MPI_SUM,
649  mycomm_);
650 #endif
651 
652  // If we did not hit lower or upper bounds, this should be
653  // zero after the first iteration. If we did hit some bounds,
654  // then recalculate the discrepancy and discrepancy per unit
655  // volume, but only taking into account volume of cells that
656  // are not already at the bounds - if we use the entire mesh
657  // volume for the recalculation, the convergence slows down
658 
659  global_diff = global_target_sum - global_source_sum;
660 
661  global_adj_target_volume = adj_target_volume;
662 #ifdef PORTAGE_ENABLE_MPI
663  if (distributed_ && global_check_)
664  MPI_Allreduce(&adj_target_volume, &global_adj_target_volume, 1,
665  MPI_DOUBLE, MPI_SUM, mycomm_);
666 #endif
667 
668  udiff = global_diff/global_adj_target_volume;
669  reldiff = global_diff/global_source_sum;
670 
671 
672  // Now reset adjusted target mesh volume to be the full volume
673  // in preparation for the next iteration
674  if (empty_fixup_type == Empty_fixup_type::LEAVE_EMPTY)
675  global_adj_target_volume = global_covered_target_volume;
676  else
677  global_adj_target_volume = global_target_volume_;
678 
679  iter++;
680  } // while leftover is not zero
681 
682  if (fabs(reldiff) > conservation_tol) {
683  if (rank_ == 0) {
684  std::cerr << "Redistribution not entirely successfully for variable " <<
685  src_var_name << "\n";
686  std::cerr << "Relative conservation error is " << reldiff << "\n";
687  std::cerr << "Absolute conservation error is " << global_diff << "\n";
688  return false;
689  }
690  }
691 
692  return true;
693  } else {
694  std::cerr << "Unknown Partial fixup type\n";
695  return false;
696  }
697 
698  } // fix_mismatch_meshvar
699 
700  private:
701  SourceMesh_Wrapper const& source_mesh_;
702  SourceState_Wrapper const& source_state_;
703  TargetMesh_Wrapper const& target_mesh_;
704  TargetState_Wrapper & target_state_;
705  int nsourceents_, ntargetents_;
706  std::vector<double> source_ent_volumes_, target_ent_volumes_, xsect_volumes_;
707  double source_volume_, target_volume_;
708  double global_source_volume_, global_target_volume_, global_xsect_volume_;
709  double relvoldiff_source_, relvoldiff_target_, relvoldiff_;
710  std::vector<int> layernum_;
711  std::vector<bool> is_cell_empty_;
712  std::vector<std::vector<int>> emptylayers_;
713  bool mismatch_ = false;
714  int rank_ = 0, nprocs_ = 1;
715  double voldifftol_ = 1e2*std::numeric_limits<double>::epsilon();
716  bool distributed_ = false;
717  bool computed_mismatch_ = false;
718  bool global_check_=true;
719  bool computed_layers_=false;
720 #ifdef PORTAGE_ENABLE_MPI
721  MPI_Comm mycomm_ = MPI_COMM_NULL;
722 #endif
723 
724  // private function to compute empty cell layers
725  bool compute_layers(){
726 
727  if (computed_layers_) return true;
728 
729  // If there is no mismatch or a distributed run with no global check
730  // return the mismatch on this partition only and skip the layer computation.
731  // This is a reasonable thing to do because without a global check, the same cell
732  // might be in a different layer on different partitions or give different values.
733  // All other cases compute layers.
734  if (!mismatch_ || (distributed_ && !global_check_)) return mismatch_;
735 
736  // Discrepancy between intersection volume and source mesh volume PLUS
737  // Discrepancy between intersection volume and target mesh volume
738  relvoldiff_ = relvoldiff_source_ + relvoldiff_target_;
739 
740 
741  // Collect the empty target cells in layers starting from the ones
742  // next to partially or fully covered cells. At the end of this
743  // section, partially or fully covered cells will all have a layer
744  // number of 0 and empty cell layers will have positive layer
745  // numbers starting from 1
746 
747  is_cell_empty_.resize(ntargetents_, false);
748  std::vector<int> emptyents;
749  for (int t = 0; t < ntargetents_; t++) {
750  if (fabs(xsect_volumes_[t]) < std::numeric_limits<double>::epsilon()) {
751  emptyents.push_back(t);
752  is_cell_empty_[t] = true;
753  }
754  }
755  int nempty = emptyents.size();
756 
757 #ifdef ENABLE_DEBUG
758 
759  int global_nempty = nempty;
760 #ifdef PORTAGE_ENABLE_MPI
761  if (distributed_ && global_check_) {
762  int *nempty_all = new int[nprocs_];
763  MPI_Gather(&nempty, 1, MPI_INT, nempty_all, 1, MPI_INT, 0, mycomm_);
764  global_nempty = std::accumulate(nempty_all, nempty_all+nprocs_, 0.0);
765  delete [] nempty_all;
766  }
767 #endif
768 
769  if (rank_ == 0 && global_nempty) {
770  if (onwhat == Entity_kind::CELL)
771  std::cerr << "One or more target cells are not covered by " <<
772  "ANY source cells.\n" <<
773  " Will assign values based on their neighborhood\n";
774  else
775  std::cerr << "One or more target dual cells are not covered by " <<
776  "ANY source dual cells.\n" <<
777  " Will assign values based on their neighborhood\n";
778  }
779 #endif
780 
781  if (nempty) {
782  layernum_.resize(ntargetents_, 0);
783 
784  int nlayers = 0;
785  int ntagged = 0, old_ntagged = -1;
786  while (ntagged < nempty && ntagged > old_ntagged) {
787  old_ntagged = ntagged;
788 
789  std::vector<int> curlayerents;
790 
791  for (int ent : emptyents) {
792  if (layernum_[ent] != 0) continue;
793 
794  std::vector<int> nbrs;
795  if (onwhat == Entity_kind::CELL)
796  target_mesh_.cell_get_node_adj_cells(ent, Entity_type::ALL, &nbrs);
797  else
798  target_mesh_.node_get_cell_adj_nodes(ent, Entity_type::ALL, &nbrs);
799  for (int nbr : nbrs) {
800  if ((onwhat == Entity_kind::CELL &&
801  target_mesh_.cell_get_type(nbr) != Entity_type::PARALLEL_OWNED) ||
802  (onwhat == Entity_kind::NODE &&
803  target_mesh_.node_get_type(nbr) != Entity_type::PARALLEL_OWNED))
804  continue;
805  if (!is_cell_empty_[nbr] || layernum_[nbr] != 0) {
806  // At least one neighbor has some material or will
807  // receive some material (indicated by having a +ve
808  // layer number)
809 
810  curlayerents.push_back(ent);
811  break;
812  }
813  }
814  } // for (ent in emptyents)
815 
816  // Tag the current layer cells with the next layer number
817  for (int ent : curlayerents)
818  layernum_[ent] = nlayers+1;
819  ntagged += curlayerents.size();
820 
821  emptylayers_.push_back(curlayerents);
822  nlayers++;
823  }
824  } // if nempty
825 
826  return computed_layers_=true;
827 
828  } // compute_layers
829 }; // MismatchFixer
830 
831 } // namespace Portage
832 
833 #endif // PORTAGE_DRIVER_FIX_MISMATCH_H_
Definition: fix_mismatch.h:122
std::vector< T > vector
Definition: portage.h:238
bool fix_mismatch(std::string const &src_var_name, std::string const &trg_var_name, double global_lower_bound=-std::numeric_limits< double >::max(), double global_upper_bound=std::numeric_limits< double >::max(), double conservation_tol=1e2 *std::numeric_limits< double >::epsilon(), int maxiter=5, Partial_fixup_type partial_fixup_type=Partial_fixup_type::SHIFTED_CONSERVATIVE, Empty_fixup_type empty_fixup_type=Empty_fixup_type::EXTRAPOLATE)
Repair the remapped field to account for boundary mismatch.
Definition: fix_mismatch.h:377
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=1e2 *std::numeric_limits< double >::epsilon(), int maxiter=5, Partial_fixup_type partial_fixup_type=Partial_fixup_type::SHIFTED_CONSERVATIVE, Empty_fixup_type empty_fixup_type=Empty_fixup_type::EXTRAPOLATE)
Repair a remapped mesh field to account for boundary mismatch.
Definition: fix_mismatch.h:421
Definition: portage.h:114
bool has_mismatch() const
Return whether the mesh domains are mismatched (must be called after check_mismatch) ...
Definition: fix_mismatch.h:336
bool check_mismatch(Portage::vector< std::vector< Weights_t >> const &source_ents_and_weights)
Compute (and cache) whether the mesh domains are mismatched.
Definition: fix_mismatch.h:153
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
Definition: portage.h:134
MismatchFixer(SourceMesh_Wrapper const &source_mesh, SourceState_Wrapper const &source_state, TargetMesh_Wrapper const &target_mesh, TargetState_Wrapper &target_state, Wonton::Executor_type const *executor, bool global_check=true)
Definition: fix_mismatch.h:125
Definition: portage.h:114
Definition: coredriver.h:42
Definition: portage.h:114
Empty_fixup_type
Fixup options for empty cells.
Definition: portage.h:134