PFASST++
particle_cloud_impl.hpp
Go to the documentation of this file.
1 #include "particle_cloud.hpp"
2 
3 #include <algorithm>
4 #include <cassert>
5 #include <functional>
6 #include <random>
7 #include <string>
8 using namespace std;
9 
10 #include <boost/format.hpp>
11 
12 #include <pfasst/globals.hpp>
13 #include <pfasst/config.hpp>
14 #include <pfasst/logging.hpp>
15 #ifdef WITH_MPI
16  #include <mpi.h>
17 #endif
18 
19 #include "particle_util.hpp"
20 
21 #define PFASST_RANDOM_SEED 42
22 
23 
24 namespace pfasst
25 {
26  namespace examples
27  {
28  namespace boris
29  {
30 #ifdef WITH_MPI
31  template<typename precision>
32  inline mpi::MPICommunicator& ParticleCloud<precision>::as_mpi(ICommunicator* comm)
33  {
34  auto mpi = dynamic_cast<mpi::MPICommunicator*>(comm);
35  assert(mpi);
36  return *mpi;
37  }
38 #endif
39 
40 
41  template<typename precision>
42  ParticleCloud<precision>::ParticleCloud(const size_t num_particles, const size_t dim,
43  const precision default_charge, const precision default_mass)
44  : _dim(dim)
45  , _num_particles(num_particles)
46  , _positions(num_particles * dim)
47  , _velocities(num_particles * dim)
48  , _charges(num_particles, default_charge)
49  , _masses(num_particles, default_mass)
50  , _default_charge(default_charge)
51  , _default_mass(default_mass)
52 #ifdef WITH_MPI
53  , recv_request(2)
54  , send_request(2)
55 #endif
56  {
57  this->zero();
58  }
59 
60  template<typename precision>
62  {}
63 
64  template<typename precision>
66  {
67  fill(this->_positions.begin(), this->_positions.end(), precision(0.0));
68  fill(this->_velocities.begin(), this->_velocities.end(), precision(0.0));
69  fill(this->_charges.begin(), this->_charges.end(), this->_default_charge);
70  fill(this->_masses.begin(), this->_masses.end(), this->_default_mass);
71 #ifdef WITH_MPI
72  fill(this->recv_request.begin(), this->recv_request.end(), MPI_REQUEST_NULL);
73  fill(this->send_request.begin(), this->send_request.end(), MPI_REQUEST_NULL);
74 #endif
75  }
76 
77  template<typename precision>
79  {
80  shared_ptr<const ParticleCloud<precision>> other_c = dynamic_pointer_cast<const ParticleCloud<precision>>(other);
81  assert(other_c);
82 // this->copy(other_c);
83 // }
84 
85 // template<typename precision>
86 // void ParticleCloud<precision>::copy(shared_ptr<const ParticleCloud<precision>> other)
87 // {
88  this->_dim = other_c->dim();
89  this->_num_particles = other_c->size();
90  this->_positions = other_c->positions();
91  this->_velocities = other_c->velocities();
92  this->_charges = other_c->charges();
93  this->_masses = other_c->masses();
94  this->_default_charge = other_c->_default_charge;
95  this->_default_mass = other_c->_default_mass;
96  }
97 
98  /*
99  template<typename precision>
100  void ParticleCloud<precision>::extend(const size_t new_size)
101  {
102  if (new_size <= this->_num_particles) {
103  throw invalid_argument("new size (" + to_string(new_size) + ") is not larger current size ("
104  + to_string(this->_num_particles) + ")");
105  }
106 
107  this->_positions.resize(new_size * this->dim());
108  fill(this->_positions.begin() + this->_num_particles, this->_positions.end(), precision(0.0));
109  this->_velocities.resize(new_size * this->dim());
110  fill(this->_velocities.begin() + this->_num_particles, this->_velocities.end(), precision(0.0));
111  this->_masses.resize(new_size);
112  fill(this->_masses.begin() + this->_num_particles, this->_masses.end(), this->_default_mass);
113  this->_charges.resize(new_size);
114  fill(this->_charges.begin() + this->_num_particles, this->_charges.end(), this->_default_charge);
115 
116  this->_num_particles = new_size;
117  }
118 
119  template<typename precision>
120  void ParticleCloud<precision>::erase(const size_t pos)
121  {
122  if (pos >= this->_num_particles) {
123  throw out_of_range("index to erase (" + to_string(pos) + ") is out of bounds ("
124  + to_string(this->_num_particles) + ")");
125  }
126 
127  this->_positions.erase(this->_positions.cbegin() + pos, this->_positions.cbegin() + pos + 1);
128  this->_velocities.erase(this->_velocities.cbegin() + pos, this->_velocities.cbegin() + pos + 1);
129  this->_charges.erase(this->_charges.cbegin() + pos, this->_charges.cbegin() + pos + 1);
130  this->_masses.erase(this->_masses.cbegin() + pos, this->_masses.cbegin() + pos + 1);
131  this->_num_particles--;
132  }
133 
134  template<typename precision>
135  void ParticleCloud<precision>::push_back(const shared_ptr<Particle<precision>>& particle)
136  {
137  if (particle->DIM() != this->_dim) {
138  throw invalid_argument("particles dimension (" + to_string(particle->DIM())
139  + ") does not match cloud dimension ("+to_string(this->_dim)+")");
140  }
141 
142  this->_positions.push_back(particle->pos());
143  this->_velocities.push_back(particle->vel());
144  this->_charges.push_back(particle->charge());
145  this->_masses.push_back(particle->mass());
146  this->_num_particles++;
147  }
148 
149  template<typename precision>
150  void ParticleCloud<precision>::insert(const size_t pos, const shared_ptr<Particle<precision>>& particle)
151  {
152  if (particle->DIM() != this->_dim) {
153  throw invalid_argument("particles dimension (" + to_string(particle->DIM())
154  + ") does not match cloud dimension ("+to_string(this->_dim)+")");
155  }
156 
157  this->_positions.insert(pos, particle->pos());
158  this->_velocities.insert(pos, particle->vel());
159  this->_charges.insert(pos, particle->charge());
160  this->_masses.insert(pos, particle->mass());
161  this->_num_particles++;
162  }
163  */
164 
165  template<typename precision>
166  inline size_t ParticleCloud<precision>::size() const
167  {
168  return this->_num_particles;
169  }
170 
171  template<typename precision>
172  inline size_t ParticleCloud<precision>::dim() const
173  {
174  return this->_dim;
175  }
176 
177  template<typename precision>
179  {
180  return this->_positions;
181  }
182 
183  template<typename precision>
185  {
186  return this->_positions;
187  }
188 
189  template<typename precision>
191  {
192  return this->_velocities;
193  }
194 
195  template<typename precision>
197  {
198  return this->_velocities;
199  }
200 
201  template<typename precision>
203  {
204  return this->_charges;
205  }
206 
207  template<typename precision>
208  const vector<precision>& ParticleCloud<precision>::charges() const
209  {
210  return this->_charges;
211  }
212 
213  template<typename precision>
214  vector<precision>& ParticleCloud<precision>::masses()
215  {
216  return this->_masses;
217  }
218 
219  template<typename precision>
220  const vector<precision>& ParticleCloud<precision>::masses() const
221  {
222  return this->_masses;
223  }
224 
225  template<typename precision>
226  vector<shared_ptr<Particle<precision>>> ParticleCloud<precision>::particles() const
227  {
228  vector<shared_ptr<Particle<precision>>> particles(this->size());
229  for (size_t index = 0; index < this->size(); ++index) {
230  particles[index] = this->operator[](index);
231  }
232  return particles;
233  }
234 
235  template<typename precision>
237  {
238  ParticleComponent<precision> center(this->dim());
239  for (size_t p = 0; p < this->size(); ++p) {
240  // conter += position[p]
241  std::transform(center.cbegin(), center.cend(), this->positions().cbegin() + (p * this->dim()),
242  center.begin(), std::plus<precision>());
243  }
244  return center / this->size();
245  }
246 
247  template<typename precision>
248  shared_ptr<Particle<precision>> ParticleCloud<precision>::operator[](const size_t index) const
249  {
250  shared_ptr<Particle<precision>> particle = make_shared<Particle<precision>>(this->dim());
251  copy_n(this->positions().cbegin() + (index * this->dim()), this->dim(), particle->pos().begin());
252  copy_n(this->velocities().cbegin() + (index * this->dim()), this->dim(), particle->vel().begin());
253  particle->set_charge(this->_charges[index]);
254  particle->set_mass(this->_masses[index]);
255  return particle;
256  }
257 
258  template<typename precision>
259  shared_ptr<Particle<precision>> ParticleCloud<precision>::at(const size_t index) const
260  {
261  assert(this->size() > index);
262  return this->operator[](index);
263  }
264 
265  template<typename precision>
266  void ParticleCloud<precision>::set_at(const size_t index, const shared_ptr<Particle<precision>>& particle)
267  {
268  assert(this->size() > index);
269  assert(particle->dim() == this->dim());
270  copy_n(particle->pos().cbegin(), this->dim(), this->positions().begin() + (index * this->dim()));
271  copy_n(particle->vel().cbegin(), this->dim(), this->velocities().begin() + (index * this->dim()));
272  this->_masses[index] = particle->mass();
273  this->_charges[index] = particle->charge();
274  }
275 
276  template<typename precision>
278  {
279  VLOG_FUNC_START("ParticleCloud") << " center:" << center;
280  VLOG(3) << LOG_INDENT << "distributing " << this->size() << " particles around center " << center;
281  assert(this->size() > 0);
282 
283  precision scale = 1000.0;
284 
285  default_random_engine rd_gen(PFASST_RANDOM_SEED);
286  precision max_pos = max(center->pos());
287  precision max_vel = max(center->vel());
288  uniform_real_distribution<precision> dist_pos(- max_pos / scale, max_pos / scale);
289  uniform_real_distribution<precision> dist_vel(- max_vel / scale, max_vel / scale);
290  VLOG(4) << LOG_INDENT << "random displacement range for";
291  VLOG(4) << LOG_INDENT << " ... position: " << boost::format("[%.4f, %.4f]") % dist_pos.min() % dist_pos.max();
292  VLOG(4) << LOG_INDENT << " ... velocity: " << boost::format("[%.4f, %.4f]") % dist_vel.min() % dist_vel.max();
293 
294  size_t p = 0;
295 
296  if (this->size() % 2 == 1) {
297  this->set_at(p, center);
298  VLOG(5) << LOG_INDENT << "setting p=1 to center";
299  p++;
300  }
301  for (;p < this->size(); ++p) {
302  ParticleComponent<precision> pos_rand(this->dim());
303  ParticleComponent<precision> vel_rand(this->dim());
304  std::transform(center->pos().cbegin(), center->pos().cend(), pos_rand.begin(),
305  [&](const precision& c) { return c + dist_pos(rd_gen); });
306  std::transform(center->pos().cbegin(), center->pos().cend(), vel_rand.begin(),
307  [&](const precision& c) { return c + dist_vel(rd_gen); });
308  std::copy(pos_rand.cbegin(), pos_rand.cend(), this->positions().begin() + (p * this->dim()));
309  std::copy(vel_rand.cbegin(), vel_rand.cend(), this->velocities().begin() + (p * this->dim()));
310  VLOG(5) << LOG_INDENT << "p=" << (p+1) << ": " << this->at(p);
311  }
312  assert(p == this->size());
313  VLOG(3) << LOG_INDENT << "center after distribute: " << this->center_of_mass();
314  }
315 
316  template<typename precision>
318  {
319  return std::max(max_abs(this->positions()), max_abs(this->velocities()));
320  }
321 
322 #ifdef WITH_MPI
323  template<typename precision>
324  void ParticleCloud<precision>::post(ICommunicator* comm, int tag)
325  {
326  auto& mpi = as_mpi(comm);
327  if (mpi.size() == 1) { return; }
328  if (mpi.rank() == 0) { return; }
329 
330  int err = MPI_Irecv(this->positions().data(),
331  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
332  (mpi.rank() - 1) % mpi.size(), tag, mpi.comm, &(this->recv_request[0]));
333  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
334  err = MPI_Irecv(this->velocities().data(),
335  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
336  (mpi.rank() - 1) % mpi.size(), tag + 1, mpi.comm, &(this->recv_request[1]));
337  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
338  }
339 
340  template<typename precision>
341  void ParticleCloud<precision>::recv(ICommunicator* comm, int tag, bool blocking)
342  {
343  auto& mpi = as_mpi(comm);
344  if (mpi.size() == 1) { return; }
345  if (mpi.rank() == 0) { return; }
346 
347  int err;
348  MPI_Status stat;
349  if (blocking) {
350  err = MPI_Recv(this->positions().data(),
351  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
352  (mpi.rank() - 1) % mpi.size(), tag, mpi.comm, &stat);
353  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
354 
355  err = MPI_Recv(this->velocities().data(),
356  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
357  (mpi.rank() - 1) % mpi.size(), tag + 1, mpi.comm, &stat);
358  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
359  } else {
360  for (auto req : this->recv_request) {
361  err = MPI_Wait(&req, &stat);
362  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
363  }
364  }
365  }
366 
367  template<typename precision>
368  void ParticleCloud<precision>::send(ICommunicator* comm, int tag, bool blocking)
369  {
370  auto& mpi = as_mpi(comm);
371  if (mpi.size() == 1) { return; }
372  if (mpi.rank() == mpi.size() - 1) { return; }
373 
374  int err = MPI_SUCCESS;
375  if (blocking) {
376  err = MPI_Send(this->positions().data(),
377  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
378  (mpi.rank() + 1) % mpi.size(), tag, mpi.comm);
379  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
380  err = MPI_Send(this->velocities().data(),
381  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
382  (mpi.rank() + 1) % mpi.size(), tag + 1, mpi.comm);
383  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
384  } else {
385  MPI_Status stat;
386  for (auto req : this->send_request) {
387  err = MPI_Wait(&req, &stat);
388  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
389  }
390 
391  err = MPI_Isend(this->positions().data(),
392  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
393  (mpi.rank() + 1) % mpi.size(), tag, mpi.comm, &send_request[0]);
394  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
395  err = MPI_Isend(this->velocities().data(),
396  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
397  (mpi.rank() + 1) % mpi.size(), tag + 1, mpi.comm, &send_request[1]);
398  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
399  }
400  }
401 
402  template<typename precision>
403  void ParticleCloud<precision>::broadcast(ICommunicator* comm)
404  {
405  auto& mpi = as_mpi(comm);
406  int err = MPI_Bcast(this->positions().data(),
407  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
408  comm->size() - 1, mpi.comm);
409  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
410  err = MPI_Bcast(this->velocities().data(),
411  sizeof(precision) * this->size() * this->dim(), MPI_CHAR,
412  comm->size() - 1, mpi.comm);
413  if (err != MPI_SUCCESS) { throw mpi::MPIError(); }
414  }
415 #endif
416 
417  template<typename precision>
418  void ParticleCloud<precision>::log(el::base::type::ostream_t& os) const
419  {
420  os << fixed << setprecision(LOG_PRECISION);
421  os << "ParticleCloud(n=" << this->size() << ", d=" << this->dim() << ", particles=" << this->particles() << ")";
422  os.unsetf(ios_base::floatfield);
423  }
424 
425 
426  template<typename precision>
427  static precision distance(const Particle<precision>& first, const Particle<precision>& second)
428  {
429  assert(first.dim() == second.dim());
430  vector<precision> diff = first.pos() - second.pos();
431  return norm0(diff);
432  }
433 
434  template<typename precision>
435  static precision distance(const shared_ptr<Particle<precision>> first, const shared_ptr<Particle<precision>> second)
436  {
437  return distance(*(first.get()), *(second.get()));
438  }
439 
440  template<typename precision>
441  static vector<precision> distance_to_reference(const ParticleCloud<precision>& cloud,
442  const Particle<precision>& reference)
443  {
444  vector<precision> distances(cloud.size());
445  for (size_t i = 0; i < distances.size(); ++i) {
446  distances[i] = distance(*cloud[i], reference);
447  }
448  return distances;
449  }
450 
451  template<typename precision>
452  static vector<precision> distance_to_reference(const shared_ptr<ParticleCloud<precision>>& cloud,
453  const shared_ptr<Particle<precision>>& reference)
454  {
455  return distance_to_reference(*cloud, *reference);
456  }
457 
458 
459  template<typename precision>
460  inline MAKE_LOGGABLE(shared_ptr<ParticleCloud<precision>>, sp_cloud, os)
461  {
462  os << "<" << addressof(sp_cloud) << ">";
463  sp_cloud->log(os);
464  return os;
465  }
466 
467  template<typename precision>
468  inline MAKE_LOGGABLE(shared_ptr<const ParticleCloud<precision>>, sp_cloud, os)
469  {
470  os << "<" << addressof(sp_cloud) << ">";
471  sp_cloud->log(os);
472  return os;
473  }
474 
475 
476  template<typename precision>
477  ParticleCloudFactory<precision>::ParticleCloudFactory(const size_t num_particles, const size_t dim,
478  const precision default_charge,
479  const precision default_mass)
480  : _num_particles(num_particles)
481  , _dim(dim)
482  , _default_charge(default_charge)
483  , _default_mass(default_mass)
484  {}
485 
486  template<typename precision>
488  {
489  return this->_num_particles;
490  }
491 
492  template<typename precision>
494  {
495  return this->_dim;
496  }
497 
498  template<typename precision>
499  shared_ptr<encap::Encapsulation<precision>>
501  {
502  return make_shared<ParticleCloud<precision>>(this->num_particles(), this->dim(),
503  this->_default_charge, this->_default_mass);
504  }
505  } // ::pfasst::examples::boris
506  } // ::pfasst::examples
507 } // ::pfasst
MAKE_LOGGABLE(shared_ptr< ParticleCloud< precision >>, sp_cloud, os)
ParticleComponent< precision > center_of_mass() const
ParticleCloudFactory(const size_t num_particles, const size_t dim, const precision default_charge, const precision default_mass)
static vector< precision > distance_to_reference(const ParticleCloud< precision > &cloud, const Particle< precision > &reference)
void set_at(const size_t index, const shared_ptr< Particle< precision >> &particle)
void distribute_around_center(const shared_ptr< Particle< precision >> &center)
ParticleComponent< precision > & pos()
tuple data
Definition: plot.py:7
virtual void broadcast(ICommunicator *comm)
Broadcasting this data structure to all processes in comm.
virtual void copy(shared_ptr< const encap::Encapsulation< precision >> other)
virtual void zero() override
Zeroes out all values of this data structure.
STL namespace.
static precision max_abs(const vector< precision > &data)
#define VLOG_FUNC_START(scope)
Definition: logging.hpp:176
static precision distance(const Particle< precision > &first, const Particle< precision > &second)
ParticleCloudComponent< precision > & positions()
ParticleCloudComponent< precision > & velocities()
virtual void recv(ICommunicator *comm, int tag, bool blocking)
Receive values to store in this data structure.
vector< precision > ParticleCloudComponent
#define LOG_INDENT
Utility macro for creating identation depending on current stack position.
Definition: logging.hpp:200
vector< precision > ParticleComponent
Definition: particle.hpp:27
#define LOG_PRECISION
Definition: logging.hpp:192
vector< shared_ptr< Particle< precision > > > particles() const
shared_ptr< Particle< precision > > at(const size_t index) const
virtual void send(ICommunicator *comm, int tag, bool blocking)
Send values stored in this data structure.
virtual void log(el::base::type::ostream_t &os) const
static precision max(const vector< precision > &data)
virtual shared_ptr< encap::Encapsulation< precision > > create(const encap::EncapType)
Actual method to create Encapsulation object of specific type.
virtual precision norm0() const
Computes the \( 0 \)-norm of the data structure's values.
Abstract interface for communicators.
Definition: interfaces.hpp:70
shared_ptr< Particle< precision > > operator[](const size_t index) const
#define PFASST_RANDOM_SEED
static precision norm0(const vector< precision > &data)
virtual void post(ICommunicator *comm, int tag)