driver_swarm.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 #ifndef SRC_DRIVER_SWARM_H_
7 #define SRC_DRIVER_SWARM_H_
8 
9 #include <algorithm>
10 #include <vector>
11 #include <iterator>
12 #include <string>
13 #include <utility>
14 #include <iostream>
15 #include <type_traits>
16 #include <cmath>
17 
19 #include "portage/support/timer.h"
20 #include "portage/support/basis.h"
21 #include "portage/support/weight.h"
26 
27 #ifdef PORTAGE_ENABLE_MPI
29 #endif
30 
31 namespace Portage { namespace Meshfree {
32  // avoid very long type names.
34 
47 template <template <int, class, class> class Search,
48  template <int, class, class> class Accumulate,
49  template<int, class> class Estimate,
50  int dim,
51  class SourceSwarm, class SourceState,
52  class TargetSwarm = SourceSwarm,
53  class TargetState = SourceState>
54 class SwarmDriver {
55 public:
77  SwarmDriver(SourceSwarm& source_swarm,
78  SourceState& source_state,
79  TargetSwarm const& target_swarm,
80  TargetState& target_state,
81  SmoothingLengths const& smoothing_lengths,
82  Weight::Kernel const kernel_type = Weight::B4,
83  Weight::Geometry const support_geom_type = Weight::ELLIPTIC,
84  WeightCenter const center=Gather)
85  : source_swarm_(source_swarm),
86  target_swarm_(target_swarm),
87  source_state_(source_state),
88  target_state_(target_state),
89  smoothing_lengths_(smoothing_lengths) {
90 
91  assert(dim == source_swarm.space_dimension());
92  assert(dim == target_swarm.space_dimension());
93  weight_center_ = center;
94  check_sizes_and_set_types(center, kernel_type, support_geom_type);
96  }
97 
121  SwarmDriver(SourceSwarm& source_swarm,
122  SourceState& source_state,
123  TargetSwarm const& target_swarm,
124  TargetState& target_state,
125  SmoothingLengths const& smoothing_lengths,
126  Portage::vector<Weight::Kernel> const& kernel_types,
127  Portage::vector<Weight::Geometry> const& geom_types,
128  WeightCenter const center = Gather)
129  : source_swarm_(source_swarm),
130  target_swarm_(target_swarm),
131  source_state_(source_state),
132  target_state_(target_state),
133  smoothing_lengths_(smoothing_lengths),
134  kernel_types_(kernel_types),
135  geom_types_(geom_types) {
136 
137  assert(dim == source_swarm.space_dimension());
138  assert(dim == target_swarm.space_dimension());
139  weight_center_ = center;
140  check_sizes(center);
142  }
143 
167  SwarmDriver(SourceSwarm& source_swarm,
168  SourceState& source_state,
169  TargetSwarm const& target_swarm,
170  TargetState& target_state,
171  SmoothingLengths const& smoothing_lengths,
172  Portage::vector<Point<dim>> const& source_extents,
173  Portage::vector<Point<dim>> const& target_extents,
174  WeightCenter const center = Gather)
175  : source_swarm_(source_swarm),
176  target_swarm_(target_swarm),
177  source_state_(source_state),
178  target_state_(target_state),
179  smoothing_lengths_(smoothing_lengths) {
180 
181  assert(dim == source_swarm.space_dimension());
182  assert(dim == target_swarm.space_dimension());
183  weight_center_ = center;
184 
185  int const swarm_size = get_swarm_size();
186 
187 #ifdef DEBUG
188  int const nb_source = source_swarm_.num_particles(Wonton::PARALLEL_OWNED);
189  int const nb_target = target_swarm_.num_particles(Wonton::PARALLEL_OWNED);
190 
191  switch (weight_center_) {
192  case Gather: assert(target_extents.size() == unsigned(nb_target)); break;
193  case Scatter: assert(source_extents.size() == unsigned(nb_source)); break;
194  default: throw std::runtime_error("invalid weight center type");
195  }
196 
197  assert(smoothing_lengths.size() == unsigned(swarm_size));
198 #endif
199 
200  kernel_types_.resize(swarm_size, Weight::POLYRAMP);
201  geom_types_.resize(swarm_size, Weight::FACETED);
202  source_extents_ = source_extents;
203  target_extents_ = target_extents;
204  }
205 
209  SwarmDriver(const SwarmDriver&) = delete;
210 
214  SwarmDriver& operator = (const SwarmDriver &) = delete;
215 
219  ~SwarmDriver() = default;
220 
227  void set_remap_var_names(std::vector<std::string> const& remap_var_names) {
228  // remap variable names same in source and target swarm
229  set_remap_var_names(remap_var_names, remap_var_names);
230  }
231 
246  void set_remap_var_names(std::vector<std::string> const& source_vars,
247  std::vector<std::string> const& target_vars,
248  EstimateType const estimator_type = LocalRegression,
249  basis::Type const basis_type = basis::Unitary,
250  oper::Type const operator_spec = oper::LastOperator,
251  Portage::vector<oper::Domain> const& operator_domains = {},
252  Portage::vector<std::vector<Point<dim>>> const& operator_data = {},
253  std::string part_field = "NONE",
254  double part_tolerance = 0.0,
255  SmoothingLengths const& part_smoothing = {}) {
256 
257  assert(source_vars.size() == target_vars.size());
258  // variables names
259  source_vars_ = source_vars;
260  target_vars_ = target_vars;
261 
262  // estimator and operator
263  estimator_type_ = estimator_type;
264  basis_type_ = basis_type;
265  operator_spec_ = operator_spec;
266  operator_domains_ = operator_domains;
267  operator_data_ = operator_data;
268 #ifdef DEBUG
269  if (operator_spec_ != oper::LastOperator) {
270  unsigned const num_target_particles = target_swarm_.num_owned_particles();
271  assert(operator_domains_.size() == num_target_particles);
272  assert(operator_data_.size() == num_target_particles);
273  }
274 #endif
275  // part-by-particle
276  part_field_ = part_field;
277  part_tolerance_ = part_tolerance;
278  part_smoothing_ = part_smoothing;
279  }
280 
286  std::vector<std::string> source_vars() const { return source_vars_; }
287 
293  std::vector<std::string> target_vars() const { return target_vars_; }
294 
301  void run(Wonton::Executor_type const* executor = nullptr, bool report_time = true) {
302 
303  int rank = 0;
304  int nprocs = 1;
305  bool distributed = false;
306 
307 #ifdef PORTAGE_ENABLE_MPI
308  MPI_Comm comm = MPI_COMM_NULL;
309  auto mpiexecutor = dynamic_cast<Wonton::MPIExecutor_type const*>(executor);
310  if (mpiexecutor && mpiexecutor->mpicomm != MPI_COMM_NULL) {
311  comm = mpiexecutor->mpicomm;
312  MPI_Comm_rank(comm, &rank);
313  MPI_Comm_size(comm, &nprocs);
314  distributed = nprocs > 1;
315  }
316 #endif
317 
318  if (rank == 0)
319  std::cout << "in SwarmDriver::run() ... " << std::endl;
320 
321  // useful aliases
322  using Searcher = Search<dim, SourceSwarm, TargetSwarm>;
323  using Accumulator = Accumulate<dim, SourceSwarm, TargetSwarm>;
324  using Estimator = Estimate<dim, SourceState>;
325 
326  int nb_source = source_swarm_.num_particles(Wonton::PARALLEL_OWNED);
327  int nb_target = target_swarm_.num_particles(Wonton::PARALLEL_OWNED);
328  int nb_fields = source_vars_.size();
329 
330  float tot_seconds = 0.0, tot_seconds_dist = 0.0,
331  tot_seconds_srch = 0.0, tot_seconds_xsect = 0.0,
332  tot_seconds_interp = 0.0;
333  //struct timeval begin_timeval, end_timeval, diff_timeval;
334  auto tic = timer::now();
335 
336  //DISTRIBUTE
337  // This step would change the input source swarm and its state
338  // if after distribution it receives particles from other
339  // ranks.
340  // For the scatter scheme, the smoothing_lengths will also
341  // be changed.
342 #ifdef PORTAGE_ENABLE_MPI
343  if (distributed) {
344  MPI_Particle_Distribute<dim> distributor(mpiexecutor);
345  //For scatter scheme, the smoothing_lengths_, kernel_types_
346  //and geom_types_ are also communicated and changed for the
347  //source swarm.
348  distributor.distribute(source_swarm_, source_state_,
349  target_swarm_, target_state_,
350  smoothing_lengths_, source_extents_, target_extents_,
351  kernel_types_, geom_types_, weight_center_);
352 
353  tot_seconds_dist = timer::elapsed(tic, true);
354  }
355 #endif
356 
357  // SEARCH
358  Portage::vector<std::vector<int>> candidates(nb_target);
359 
360  // Get an instance of the desired search algorithm type which is expected
361  // to be a functor with an operator() of the right form
362  Searcher search(source_swarm_, target_swarm_,
363  source_extents_, target_extents_, weight_center_);
364 
365  Portage::transform(target_swarm_.begin(Wonton::PARTICLE, Wonton::PARALLEL_OWNED),
366  target_swarm_.end(Wonton::PARTICLE, Wonton::PARALLEL_OWNED),
367  candidates.begin(), search);
368 
369  tot_seconds_srch = timer::elapsed(tic);
370 
371  // In case of faceted, scatter, and parts, obtain part assignments on target particles.
372  // Eliminate neighbors that aren't in the same part.
373  // It is assumed that the faceted weight function will be non-zero only on the
374  // source cell it came from, which is achieved by using a smoothing factor of 1/2.
375  // It is also assumed no target point will appear in more than one source cell.
376  if (geom_types_[0] == Weight::FACETED and weight_center_ == Scatter and part_field_!="NONE") {
377 
378 
379  // make storage for target part assignments
380  nb_source = source_swarm_.num_particles(Wonton::PARALLEL_OWNED);
381  nb_target = target_swarm_.num_particles(Wonton::PARALLEL_OWNED);
382 
383  // get source part assignments
384  auto source_field_part = source_state_.get_field_dbl(part_field_);
385  std::vector<double> target_field_part(nb_target);
386 
387  // create accumulator to evaluate weight function on source cells
388  Portage::vector<Weight::Kernel> step_kern(nb_source, Weight::STEP);
389  Accumulator accumulator(source_swarm_, target_swarm_,
390  estimator_type_, weight_center_,
391  step_kern, geom_types_, part_smoothing_, basis_type_,
392  operator_spec_, operator_domains_, operator_data_);
393 
394  // loop over target points and set part assignments
395 
396  for (int i = 0; i < nb_target; i++) {
397  std::vector<int> possibles = candidates[i];
398  for (auto&& neighbor : possibles) {
399  double weight = accumulator.weight(i, neighbor);
400  if (weight > 0.) {
401  double part_val = source_field_part[neighbor];
402  target_field_part[i] = part_val;
403 #undef DEBUG_HERE
404 #ifdef DEBUG_HERE
405  if (dim == 2) {
406  auto p1 = source_swarm_.get_particle_coordinates(nbr);
407  auto p2 = target_swarm_.get_particle_coordinates(i);
408  double h = part_smoothing_[neighbor][0][2];
409 
410  std::cout << "i=" << i << " " << " neighbor=" << neighbor << " ";
411  std::cout << std::abs(p2[0]-p1[0])/h <<", "<< std::abs(p2[1]-p1[1])/h;
412  std::cout << " w= "<< weight <<" part= "<< part_val << std::endl;
413  }
414 #endif
415 #undef DEBUG_HERE
416  }
417  }
418  }
419 
420  // loop over target points and dismiss neighbors that aren't in the same part
421  for (int i = 0; i < nb_target; i++) {
422  std::vector<int> new_candidates;
423  std::vector<int> possibles = candidates[i];
424  for (auto&& neighbor : possibles) {
425  if (std::abs(target_field_part[i] - source_field_part[neighbor]) < part_tolerance_) {
426  new_candidates.emplace_back(neighbor);
427  }
428  }
429  candidates[i] = new_candidates;
430  }
431  }
432 
433  // ACCUMULATE (build moment matrix, calculate shape functions)
434  // EQUIVALENT TO INTERSECT IN MESH-MESH REMAP
435  tic = timer::now();
436 
437  // Get an instance of the desired accumulate algorithm type which is
438  // expected to be a functor with an operator() of the right form
439  Accumulator accumulate(source_swarm_, target_swarm_,
440  estimator_type_, weight_center_, kernel_types_,
441  geom_types_, smoothing_lengths_, basis_type_,
442  operator_spec_, operator_domains_, operator_data_);
443 
444  Portage::vector<std::vector<Weights_t>> source_points_and_multipliers(nb_target);
445 
446  // For each particle in the target swarm get the shape functions
447  // (multipliers for source particle values)
448  Portage::transform(target_swarm_.begin(Wonton::PARTICLE, Wonton::PARALLEL_OWNED),
449  target_swarm_.end(Wonton::PARTICLE, Wonton::PARALLEL_OWNED),
450  candidates.begin(), source_points_and_multipliers.begin(),
451  accumulate);
452 
453  tot_seconds_xsect = timer::elapsed(tic, true);
454 
455  // ESTIMATE (one variable at a time)
456  nb_fields = source_vars_.size();
457  if (rank == 0)
458  std::cout << "number of variables to remap is " << nb_fields << std::endl;
459 
460  // Get an instance of the desired interpolate algorithm type
461  Estimator estimator(source_state_);
462 
463  for (int i = 0; i < nb_fields; ++i) {
464  //amh: ?? add back accuracy output statement??
465  if (rank == 0)
466  std::cout << "Remap "<< source_vars_[i] <<" to "<< target_vars_[i] << std::endl;
467 
468  estimator.set_variable(source_vars_[i]);
469 
470  // This populates targetField with the values returned by the
471  // remapper operator
472 
473  // ***************** NOTE NOTE NOTE NOTE ********************
474  // THE CURRENT SWARM_STATE DOES NOT HAVE AN OPERATOR TO RETURN
475  // A RAW POINTER TO THE DATA. INSTEAD IT RETURNS THIS REQUIRES
476  // A SPECIFIC TYPE OF THE SWARM_STATE CLASS. THIS IS A BAD
477  // IDEA BECAUSE IT FORCES ALL OTHER SWARM_STATE CLASSES TO
478  // HAVE THIS SAME DEFINITION. IT ALSO LEADS TO THE UGLY USAGE
479  // WE SEE BELOW BECAUSE WE NEED A PORTAGE POINTER TO BE ABLE
480  // TO USE THRUST
481 
482  // TODO: perform a deep-copy back to target state
483  auto& target_data = target_state_.get_field(target_vars_[i]);
484  Portage::pointer<double> target_field(target_data.data());
485 
486  Portage::transform(target_swarm_.begin(Entity_kind::PARTICLE, Entity_type::PARALLEL_OWNED),
487  target_swarm_.end(Entity_kind::PARTICLE, Entity_type::PARALLEL_OWNED),
488  source_points_and_multipliers.begin(),
489  target_field, estimator);
490 
491  tot_seconds_interp = timer::elapsed(tic, true);
492  tot_seconds = tot_seconds_dist + tot_seconds_srch +
493  tot_seconds_xsect + tot_seconds_interp;
494 
495  if (report_time) {
496  std::cout << "Swarm Transform Time Rank " << rank << " (s): " << tot_seconds << std::endl;
497  std::cout << " Swarm Distribution Time Rank " << rank << " (s): " << tot_seconds_dist << std::endl;
498  std::cout << " Swarm Search Time Rank " << rank << " (s): " << tot_seconds_srch << std::endl;
499  std::cout << " Swarm Accumulate Time Rank " << rank << " (s): " << tot_seconds_xsect << std::endl;
500  std::cout << " Swarm Estimate Time Rank " << rank << " (s): " << tot_seconds_interp << std::endl;
501 
502  // put out neighbor statistics
503  int nnbrmax = 0;
504  int nnbrmin = nb_target;
505  int nnbravg = 0;
506  int nnbrsum = 0;
507  double nnbrsdev = 0;
508 
509  for (int j=0; j < nb_target; j++) {
510  int n = 0;
511  std::vector<Weights_t> wts = source_points_and_multipliers[j];
512  for (auto&& wt : wts) {
513  if (std::abs(wt.weights[0]) > 0.0)
514  n++;
515  }
516  if (n > nnbrmax) nnbrmax = n;
517  if (n < nnbrmin) nnbrmin = n;
518  nnbrsum += n;
519  }
520  nnbravg = nnbrsum / nb_target;
521 
522  for (int j=0; j < nb_target; j++) {
523  int n = 0;
524  std::vector<Weights_t> wts = source_points_and_multipliers[j];
525  for (auto&& wt : wts) {
526  if (std::abs(wt.weights[0]) > 0.0)
527  n++;
528  }
529  nnbrsdev += std::pow(n - nnbravg, 2);
530  }
531  nnbrsdev = std::sqrt(nnbrsdev / nb_target);
532 
533  std::cout << "Max number of neighbors: " << nnbrmax << std::endl;
534  std::cout << "Min number of neighbors: " << nnbrmin << std::endl;
535  std::cout << "Avg number of neighbors: " << nnbravg << std::endl;
536  std::cout << "Std Dev for number of neighbors: " << nnbrsdev << std::endl;
537  }
538  }
539 
540  } // run
541 
542 protected:
548  int get_swarm_size() const {
549  return (weight_center_ == Scatter
550  ? source_swarm_.num_particles(Wonton::PARALLEL_OWNED)
551  : target_swarm_.num_particles(Wonton::PARALLEL_OWNED));
552  }
553 
559  void check_sizes(WeightCenter const weight_center) {
560 #ifdef DEBUG
561  unsigned const swarm_size = get_swarm_size();
562  assert(smoothing_lengths_.size() == swarm_size);
563  assert(kernel_types_.size() == swarm_size);
564  assert(geom_types_.size() == swarm_size);
565 #endif
566  }
567 
575  void check_sizes_and_set_types(WeightCenter const weight_center,
576  Weight::Kernel const kernel_type,
577  Weight::Geometry const support_geom_type) {
578  int const swarm_size = get_swarm_size();
579  assert(smoothing_lengths_.size() == unsigned(swarm_size));
580  kernel_types_.resize(swarm_size, kernel_type);
581  geom_types_.resize(swarm_size, support_geom_type);
582  }
583 
589  if (weight_center_ == Gather) {
590  int const nb_target = target_swarm_.num_particles(Wonton::PARALLEL_OWNED);
591  assert(smoothing_lengths_.size() == unsigned(nb_target));
592  target_extents_.resize(nb_target);
593 
594  for (int i = 0; i < nb_target; i++) {
595  if (geom_types_[i] == Weight::FACETED) {
596  throw std::runtime_error("FACETED geometry is not available here");
597  }
598  std::vector<std::vector<double>> temp = smoothing_lengths_[i];
599  target_extents_[i] = Point<dim>(temp[0]);
600  }
601  } else if (weight_center_ == Scatter) {
602  int const nb_source = source_swarm_.num_particles(Wonton::PARALLEL_OWNED);
603  assert(smoothing_lengths_.size() == unsigned(nb_source));
604  source_extents_.resize(nb_source);
605 
606  for (int i = 0; i < nb_source; i++) {
607  if (geom_types_[i] == Weight::FACETED) {
608  throw std::runtime_error("FACETED geometry is not available here");
609  }
610  std::vector<std::vector<double>> temp = smoothing_lengths_[i];
611  source_extents_[i] = Point<dim>(temp[0]);
612  }
613  }
614  }
615 
616 private:
617  SourceSwarm& source_swarm_;
618  TargetSwarm const& target_swarm_;
619  SourceState& source_state_;
620  TargetState& target_state_;
621  std::vector<std::string> source_vars_ {};
622  std::vector<std::string> target_vars_ {};
623  WeightCenter weight_center_ = Gather;
624  SmoothingLengths smoothing_lengths_ {};
625  Portage::vector<Weight::Kernel> kernel_types_ {};
626  Portage::vector<Weight::Geometry> geom_types_ {};
627  Portage::vector<Point<dim>> source_extents_ {};
628  Portage::vector<Point<dim>> target_extents_ {};
629  EstimateType estimator_type_ {};
630  basis::Type basis_type_ {};
631  oper::Type operator_spec_ {};
632  Portage::vector<oper::Domain> operator_domains_ {};
633  Portage::vector<std::vector<Point<dim>>> operator_data_ {};
634  std::string part_field_ = "";
635  double part_tolerance_ = 0.0;
637 }; // class SwarmDriver
638 
639 }} // namespace Portage::Meshfree
640 
641 #endif // SRC_DRIVER_SWARM_H_
std::chrono::high_resolution_clock::time_point now()
Get current time.
Definition: timer.h:19
Definition: weight.h:304
void run(Wonton::Executor_type const *executor=nullptr, bool report_time=true)
Perform the remap.
Definition: driver_swarm.h:301
std::vector< T > vector
Definition: portage.h:238
OutputIterator transform(InputIterator first, InputIterator last, OutputIterator result, UnaryFunction op)
Definition: portage.h:250
~SwarmDriver()=default
Destructor.
void check_sizes_and_set_types(WeightCenter const weight_center, Weight::Kernel const kernel_type, Weight::Geometry const support_geom_type)
Check sizes according to weight center, set kernel and geometry.
Definition: driver_swarm.h:575
int get_swarm_size() const
Get swarm size according to weight center type.
Definition: driver_swarm.h:548
T * pointer
Definition: portage.h:241
float elapsed(std::chrono::high_resolution_clock::time_point &tic, bool reset=false)
Get elapsed time in seconds.
Definition: timer.h:27
SwarmDriver(SourceSwarm &source_swarm, SourceState &source_state, TargetSwarm const &target_swarm, TargetState &target_state, SmoothingLengths const &smoothing_lengths, Weight::Kernel const kernel_type=Weight::B4, Weight::Geometry const support_geom_type=Weight::ELLIPTIC, WeightCenter const center=Gather)
Constructor using a unique kernel_type and support geometry for all particles.
Definition: driver_swarm.h:77
Provides an interface to remap variables from one swarm to another.
Definition: driver_swarm.h:54
Definition: weight.h:304
void set_extents_from_smoothing_lengths()
Setup search boxes around source and target points for non-faceted weights.
Definition: driver_swarm.h:588
std::vector< std::string > target_vars() const
Get all target swarm variables names.
Definition: driver_swarm.h:293
void set_remap_var_names(std::vector< std::string > const &remap_var_names)
Specify the names of the variables to be interpolated.
Definition: driver_swarm.h:227
Definition: operator.h:30
Definition: weight.h:333
Type
Definition: basis.h:20
Definition: basis.h:20
Geometry
Geometry types.
Definition: weight.h:333
void check_sizes(WeightCenter const weight_center)
Check sizes according to weight center type.
Definition: driver_swarm.h:559
Type
Definition: operator.h:30
Definition: coredriver.h:42
std::vector< std::string > source_vars() const
Get all source swarm variables names.
Definition: driver_swarm.h:286
SwarmDriver(SourceSwarm &source_swarm, SourceState &source_state, TargetSwarm const &target_swarm, TargetState &target_state, SmoothingLengths const &smoothing_lengths, Portage::vector< Weight::Kernel > const &kernel_types, Portage::vector< Weight::Geometry > const &geom_types, WeightCenter const center=Gather)
Constructor with search extents computed inline.
Definition: driver_swarm.h:121
Portage::vector< std::vector< std::vector< double > >> SmoothingLengths
Definition: driver_swarm.h:33
Kernel
Kernel types.
Definition: weight.h:304
SwarmDriver & operator=(const SwarmDriver &)=delete
Disabled assignment operator.
SwarmDriver(SourceSwarm &source_swarm, SourceState &source_state, TargetSwarm const &target_swarm, TargetState &target_state, SmoothingLengths const &smoothing_lengths, Portage::vector< Point< dim >> const &source_extents, Portage::vector< Point< dim >> const &target_extents, WeightCenter const center=Gather)
Constructor with search extents specified.
Definition: driver_swarm.h:167
Definition: weight.h:304
Definition: weight.h:333
void set_remap_var_names(std::vector< std::string > const &source_vars, std::vector< std::string > const &target_vars, EstimateType const estimator_type=LocalRegression, basis::Type const basis_type=basis::Unitary, oper::Type const operator_spec=oper::LastOperator, Portage::vector< oper::Domain > const &operator_domains={}, Portage::vector< std::vector< Point< dim >>> const &operator_data={}, std::string part_field="NONE", double part_tolerance=0.0, SmoothingLengths const &part_smoothing={})
Specify the names of the variables to be interpolated.
Definition: driver_swarm.h:246