PFASST++
boris_sweeper_impl.hpp
Go to the documentation of this file.
1 #include "boris_sweeper.hpp"
2 
3 #include <cassert>
4 #include <cmath>
5 #include <cstdio>
6 #include <complex>
7 #include <cstdlib>
8 #include <ios>
9 #include <iostream>
10 #include <map>
11 #include <vector>
12 using namespace std;
13 
14 #include <Eigen/Core>
15 
16 #include <boost/format.hpp>
17 
18 #include <pfasst.hpp>
19 #include <pfasst/config.hpp>
20 #include <pfasst/logging.hpp>
22 
23 #include "particle.hpp"
24 #include "particle_cloud.hpp"
25 #include "particle_util.hpp"
27 
28 
29 #define BCVLOG(level) CVLOG(level, "Boris") << this->log_indent->indent(level)
30 
31 
32 /*
33  * VLOG Levels for 'Boris' logger:
34  * 1: predict, sweep, setup
35  * 2: same as 1 but more verose (will include basic transfer notes)
36  * 3: boris_solve
37  * 4: update_position, update_velocity
38  * 5: same as 3+4 but more verbose (will include verbose transfer notes)
39  * 6: integrate, evaluate
40  * 7: build_rhs
41  * 8: compute_residual, exact solution
42  * 9: printing, verbose save/advance/spread
43  */
44 
45 namespace pfasst
46 {
47  namespace examples
48  {
49  namespace boris
50  {
51  template<typename precision>
52  void ParticleError<precision>::log(el::base::type::ostream_t& os) const
53  {
54  os << "pos: [" << x << " " << y << " " << z << "]\tvel: [" << y << " " << v << " " << w << "]";
55  }
56 
57 
58  template<typename precision>
59  static void init_opts()
60  {
61  pfasst::config::options::add_option<size_t>("Boris-SDC", "num_particles", "number of particles in the cloud");
62  pfasst::config::options::add_option<precision>("Boris-SDC", "epsilon", "Boris' epsilon");
63  pfasst::config::options::add_option<precision>("Boris-SDC", "omega_e", "E-field constant");
64  pfasst::config::options::add_option<precision>("Boris-SDC", "omega_b", "B-field constant");
65  }
66 
67  template<typename precision>
68  static void init_logs()
69  {
71  pfasst::log::add_custom_logger("SolverBinding");
73  }
74 
75 
76  LogIndent::LogIndent()
77  {
78  this->vlog_levels.fill(0);
79  }
80  LogIndent::~LogIndent()
81  {}
82 
83  void LogIndent::increment(const size_t vlevel)
84  {
85  this->vlog_levels.at(vlevel - 1)++;
86  }
87  void LogIndent::decrement(const size_t vlevel)
88  {
89  this->vlog_levels.at(vlevel - 1)--;
90  }
91  const string LogIndent::indent(const size_t vlevel) const
92  {
93  size_t count = 0;
94  for(size_t vl = 0; vl < vlevel; ++vl) {
95  count += this->vlog_levels.at(vl);
96  }
97  return string(count * 2, ' ');
98  }
99 
100 
101  template<typename scalar, typename time>
103  BorisSweeper<scalar, time>::build_rhs(const size_t m, const bool previous) const
104  {
105  BCVLOG(7) << "building rhs for node " << m << " of "
106  << ((previous) ? "previous" : "current") << " sweep";
107  this->log_indent->increment(7);
108  acceleration_type rhs = (previous) ? this->saved_forces[m] : this->forces[m];
109  BCVLOG(7) << "e-forces: " << rhs;
110 
111  if (previous) {
112  rhs += cross_prod(this->saved_particles[m]->velocities(), this->saved_b_vecs[m]);
113  } else {
114  rhs += cross_prod(this->particles[m]->velocities(), this->b_vecs[m]);
115  }
116 
117  BCVLOG(7) << "=> rhs: " << rhs;
118  this->log_indent->decrement(7);
119  return rhs;
120  }
121 
122  template<typename scalar, typename time>
124  {
125  BCVLOG(8) << "computing max residual";
126  this->log_indent->increment(8);
127 
128  this->residual(this->get_controller()->get_step_size(), this->residuals);
129 
130  scalar max_residual = scalar(0.0);
131 
132  for (size_t m = 1; m < this->residuals.size(); ++m) {
133  auto residual_m = dynamic_pointer_cast<encap_type>(this->residuals[m]);
134  assert(residual_m);
135  max_residual = std::max(max_residual, residual_m->norm0());
136  }
137 
138  BCVLOG(8) << "=> max residual: " << max_residual;
139  return max_residual;
140  }
141 
142  template<typename scalar, typename time>
143  void BorisSweeper<scalar, time>::write_center_to_file(const size_t iter, const size_t sweep,
144  const ParticleComponent<scalar>& center,
145  const scalar energy, const scalar drift, const scalar residual)
146  {
147  BCVLOG(9) << "writing center particle to file";
148  this->data_stream_fmt % (iter+1) % sweep % -1
149  % center[0] % center[1] % center[2]
150  // cppcheck-suppress zerodiv
151  % 0 % 0 % 0
152  % energy % drift % residual;
153  this->data_stream << this->data_stream_fmt << endl;
154  }
155 
156  template<typename scalar, typename time>
157  void BorisSweeper<scalar, time>::write_particle_cloud_to_file(const size_t iter, const size_t sweep,
158  const shared_ptr<encap_type>& cloud,
159  const scalar energy, const scalar drift,
160  const scalar residual,
161  const bool with_center)
162  {
163  this->log_indent->increment(9);
164  for (size_t p = 0; p < cloud->size(); ++p) {
165  BCVLOG(9) << "writing cloud particle " << p << " to file";
166  // cppcheck-suppress zerodiv
167  this->data_stream_fmt % (iter+1) % sweep % p
168  % cloud->positions()[p * cloud->dim()] % cloud->positions()[p * cloud->dim() + 1] % cloud->positions()[p * cloud->dim() + 2]
169  % cloud->velocities()[p * cloud->dim()] % cloud->velocities()[p * cloud->dim() + 1] % cloud->velocities()[p * cloud->dim() + 2]
170  % energy % drift % residual;
171  this->data_stream << this->data_stream_fmt << endl;
172  }
173  if (with_center) {
174  this->write_center_to_file(iter, sweep, cloud->center_of_mass(), energy, drift, residual);
175  }
176  this->log_indent->decrement(9);
177  }
178 
179  template<typename scalar, typename time>
180  void BorisSweeper<scalar, time>::update_position(const size_t m, const time dt, const time ds)
181  {
182  BCVLOG(4) << "updating position (" << m << "->" << m+1 << ") with dt=" << dt << ", ds=" << ds;
183  this->log_indent->increment(4);
184  this->particles[m+1]->positions() = this->particles[m]->positions();
185  BCVLOG(4) << "old: " << this->particles[m+1]->positions();
186  this->log_indent->increment(5);
187  // + delta_nodes_{m+1} * v_{0}
188  this->particles[m+1]->positions() += this->start_particles->velocities() * ds;
189  BCVLOG(5) << "+= " << this->start_particles->velocities() << " * " << ds;
190  // + \sum_{l=1}^{m} sx_{m+1,l}^{x} (f_{l}^{k+1} - f_{l}^{k})
191  for (size_t l = 0; l <= m; l++) {
192  auto rhs_this = this->build_rhs(l);
193  auto rhs_prev = this->build_rhs(l, true);
194  this->particles[m+1]->positions() += rhs_this * dt * dt * this->sx_mat(m+1, l);
195  BCVLOG(5) << "+= " << rhs_this << " * " << dt * dt << " * " << this->sx_mat(m+1, l);
196  this->particles[m+1]->positions() -= rhs_prev * dt * dt * this->sx_mat(m+1, l);
197  BCVLOG(5) << "-= " << rhs_prev << " * " << dt * dt << " * " << this->sx_mat(m+1, l);
198  }
199 
200  this->particles[m+1]->positions() += this->ss_integrals[m+1];
201  BCVLOG(5) << "+= " << this->ss_integrals[m+1];
202  this->log_indent->decrement(5);
203  this->log_indent->decrement(4);
204  }
205 
206  template<typename scalar, typename time>
207  void BorisSweeper<scalar, time>::update_velocity(const size_t m, const time ds, const vector<time>& nodes)
208  {
209  BCVLOG(4) << "updating velocity (" << m << "->" << m+1 << ") with ds=" << ds;
210  this->log_indent->increment(4);
211  velocity_type c_k_term = cloud_component_factory<scalar>(this->particles[0]->size(), this->particles[0]->dim());
212  zero(c_k_term); // TODO: check if this is required
213 
214  BCVLOG(5) << "c_k: " << c_k_term;
215  this->log_indent->increment(5);
216  // - delta_nodes_{m} / 2 * f_{m+1}^{k}
217  auto t1 = this->build_rhs(m+1, true);
218  c_k_term -= t1 * 0.5 * ds;
219  BCVLOG(5) << "-= 0.5 * " << t1 << " * " << ds << " => " << c_k_term;
220  // - delta_nodes_{m} / 2 * f_{m}^{k}
221  auto t2 = this->build_rhs(m, true);
222  c_k_term -= t2 * 0.5 * ds;
223  BCVLOG(5) << "-= 0.5 * " << t2 << " * " << ds << " => " << c_k_term;
224  // + s_integral[m]
225  c_k_term += this->s_integrals[m+1];
226  BCVLOG(5) << "+= " << this->s_integrals[m+1];
227  this->log_indent->decrement(5);
228  BCVLOG(4) << "=> c_k: " << c_k_term;
229 
230  // doing Boris' magic
231  this->boris_solve(nodes[m], nodes[m+1], ds, m, c_k_term);
232  this->log_indent->decrement(4);
233  }
234 
235 
236  template<typename scalar, typename time>
238  const string& data_file)
239  : impl_solver(impl_solver)
240  , errors()
241  , exact_updated(false)
242  , log_indent(make_shared<LogIndent>())
243  , f_evals(0)
244  , data_stream(data_file, ios_base::out | ios_base::trunc)
245  {
246  assert(data_stream.is_open() && data_stream.good());
247  CLOG(INFO, "Boris") << "writing particle data to: " << data_file;
248  // CSV format specification: [step],[iter],[particle],[x],[y],[z],[u],[v],[w],[energy],[drift],[residual]
249  this->DATA_STREAM_FORMAT_STR = "%d,%d,%d,%.16f,%.16f,%.16f,%.16f,%.16f,%.16f,%.16f,%.16f,%.16f";
250  // i sw p x y z u v w engy edr res
251  BCVLOG(2) << "formatting string: '" << this->DATA_STREAM_FORMAT_STR << "'";
252  this->data_stream_fmt = boost::format(this->DATA_STREAM_FORMAT_STR);
253  }
254 
255 
256  template<typename scalar, typename time>
258  {
259  CLOG(INFO, "Boris") << "number force computations: " << this->f_evals;
260  }
261 
262 
263  template<typename scalar, typename time>
264  void BorisSweeper<scalar, time>::set_state(shared_ptr<const encap_type> u0, size_t m)
265  {
266  this->particles[m]->copy(u0);
267  }
268 
269  template<typename scalar, typename time>
270  void BorisSweeper<scalar, time>::set_start_state(shared_ptr<const encap_type> u0)
271  {
272  this->start_particles->operator=(*(u0.get()));
273  }
274 
275  template<typename scalar, typename time>
276  shared_ptr<Encapsulation<time>> BorisSweeper<scalar, time>::get_state(size_t m) const
277  {
278  return this->particles[m];
279  }
280 
281  template<typename scalar, typename time>
282  shared_ptr<Encapsulation<time>> BorisSweeper<scalar, time>::get_start_state() const
283  {
284  return this->start_particles;
285  }
286 
287  template<typename scalar, typename time>
288  shared_ptr<typename BorisSweeper<scalar, time>::acceleration_type>
290  {
291  return this->tau_q_corrections[m];
292  }
293 
294  template<typename scalar, typename time>
295  shared_ptr<typename BorisSweeper<scalar, time>::acceleration_type>
297  {
298  return this->tau_qq_corrections[m];
299  }
300 
301  template<typename scalar, typename time>
302  shared_ptr<Encapsulation<time>> BorisSweeper<scalar, time>::get_saved_state(size_t m) const
303  {
304  return this->saved_particles[m];
305  }
306 
307  template<typename scalar, typename time>
309  {
310  BCVLOG(1) << "computing and setting initial energy";
311  this->log_indent->increment(1);
312 
313  auto p0 = this->start_particles;
314  BCVLOG(2) << "initial particles: " << p0;
315  this->initial_energy = this->impl_solver->energy(p0, this->get_controller()->get_time());
316  CLOG(INFO, "Boris") << "initial total energy of system: " << this->initial_energy;
317 
318  this->write_particle_cloud_to_file(0, 0, p0, this->initial_energy, 0, 0);
319  this->log_indent->decrement(1);
320  }
321 
322  template<typename scalar, typename time>
324  {
325  shared_ptr<encap_type> q_cast = dynamic_pointer_cast<encap_type>(q);
326  assert(q_cast);
327  this->exact(q_cast, t);
328  }
329 
330  template<typename scalar, typename time>
331  void BorisSweeper<scalar, time>::exact(shared_ptr<encap_type> q, const time t)
332  {
333  this->exact(*(q.get()), t);
334  }
335 
336  template<typename scalar, typename time>
338  {
339  BCVLOG(8) << "computing exact solution at t=" << t;
340  this->log_indent->increment(8);
341  UNUSED(t);
342  if (!this->exact_updated) {
343  typedef complex<scalar> C;
344  C i(0.0, 1.0);
345  auto initial = this->particles[0];
346  scalar x0 = initial->positions()[0],
347  y0 = initial->positions()[1],
348  z0 = initial->positions()[2],
349  u0 = initial->velocities()[0],
350  v0 = initial->velocities()[1],
351  w0 = initial->velocities()[2],
352  omega_e = this->impl_solver->omega_e(),
353  omega_b = this->impl_solver->omega_b(),
354  epsilon = this->impl_solver->epsilon();
355  time dt = this->get_controller()->get_step_size();
356 
357  C omega_tilde = sqrt(-2.0 * epsilon) * omega_e;
358  q.positions()[2] = (z0 * cos(omega_tilde * (scalar)(dt))
359  + w0 / omega_tilde * sin(omega_tilde * (scalar)(dt))).real();
360 
361  C sqrt_in_omega = sqrt(pow(omega_b, 2) + 4.0 * epsilon * pow(omega_e, 2));
362  C omega_minus = 0.5 * (omega_b - sqrt_in_omega);
363  C omega_plus = 0.5 * (omega_b + sqrt_in_omega);
364 
365  C r_minus = (omega_plus * x0 + v0) / (omega_plus - omega_minus);
366  C r_plus = x0 - r_minus;
367 
368  C i_minus = (omega_plus * y0 - u0) / (omega_plus - omega_minus);
369  C i_plus = y0 - i_minus;
370 
371  C x_y_move = (r_plus + i * i_plus) * exp(- i * omega_plus * (scalar)(dt))
372  + (r_minus + i * i_minus) * exp(- i * omega_minus * (scalar)(dt));
373  q.positions()[0] = x_y_move.real();
374  q.positions()[1] = x_y_move.imag();
375 
376  q.velocities()[2] = (- z0 * omega_tilde * sin(omega_tilde * (scalar)(dt)) + w0 * cos(omega_tilde * (scalar)(dt))).real();
377  C u_v_move = (- i * omega_plus * (r_plus + i * i_plus)) * exp(-i * omega_plus * (scalar)(dt))
378  - (i * omega_minus * (r_minus + i * i_minus)) * exp(-i * omega_minus * (scalar)(dt));
379  q.velocities()[0] = u_v_move.real();
380  q.velocities()[1] = u_v_move.imag();
381 
382  this->exact_cache = make_shared<encap_type>(q);
383  this->exact_updated = true;
384  } else {
385  BCVLOG(8) << "exact solution has been computed previously.";
386  q = *(this->exact_cache.get());
387  }
388  BCVLOG(8) << "exact solution at t=" << t << ": " << q;
389  this->log_indent->decrement(8);
390  }
391 
392  template<typename scalar, typename time>
393  void BorisSweeper<scalar, time>::echo_error(const time t, bool predict)
394  {
395  auto end = this->end_particles;
396  ErrorTuple<scalar> e_tuple;
397  scalar e_end = this->impl_solver->energy(end, t);
398  e_tuple.e_drift = abs(this->initial_energy - e_end);
399  e_tuple.res = this->compute_residual_max();
400 
401  size_t n = this->get_controller()->get_step();
402  size_t k = this->get_controller()->get_iteration();
403  error_index nk(n, k);
404 
405  CLOG(INFO, "Boris") << boost::format(this->FORMAT_STR) % (n+1) % k % (this->coarse ? "coarse" : "fine") % e_tuple.res % e_tuple.e_drift % e_end;
406  BCVLOG(9) << "particle at t_end: " << end;
407 
408  // exact anlytical solution only valid for 1-particle-system
409  if (this->particles[0]->size() == 1) {
410  shared_ptr<encap_type> ex = dynamic_pointer_cast<encap_type>(this->get_factory()->create(pfasst::encap::solution));
411  this->exact(ex, t);
412 
413  e_tuple.p_err.x = ex->positions()[0] - end->positions()[0];
414  e_tuple.p_err.y = ex->positions()[1] - end->positions()[1];
415  e_tuple.p_err.z = ex->positions()[2] - end->positions()[2];
416  e_tuple.p_err.u = ex->velocities()[0] - end->velocities()[0];
417  e_tuple.p_err.v = ex->velocities()[1] - end->velocities()[1];
418  e_tuple.p_err.w = ex->velocities()[2] - end->velocities()[2];
419 
420  BCVLOG(9) << "absolute error at end point: " << e_tuple.p_err;
421  }
422  this->errors.insert(pair<error_index, ErrorTuple<scalar>>(nk, e_tuple));
423 
424  this->write_particle_cloud_to_file(n, k, end, e_end, e_tuple.e_drift, e_tuple.res);
425  }
426 
427  template<typename scalar, typename time>
429  {
430  return this->errors;
431  }
432 
433  template<typename scalar, typename time>
435  {
437  BCVLOG(1) << "setting up Boris Sweeper for " << ((coarse) ? "coarse" : "fine") << " level";
438  this->log_indent->increment(1);
439  this->coarse = coarse;
440  auto const nodes = this->get_nodes();
441  assert(nodes.size() >= 1);
442  const size_t nnodes = nodes.size();
443  const size_t num_s_integrals = this->quadrature->left_is_node() ? nnodes : nnodes - 1;
444  BCVLOG(2) << "there will be " << num_s_integrals << " integrals for " << nnodes << " nodes";
445 
446  // compute delta nodes
447  this->delta_nodes = vector<time>(nnodes, time(0.0));
448  for (size_t m = 1; m < nnodes; m++) {
449  this->delta_nodes[m] = nodes[m] - nodes[m - 1];
450  }
451 
452  this->start_particles = dynamic_pointer_cast<encap_type>(this->get_factory()->create(pfasst::encap::solution));
453  this->end_particles = dynamic_pointer_cast<encap_type>(this->get_factory()->create(pfasst::encap::solution));
454 
455  this->energy_evals.resize(nnodes);
456  for (size_t m = 0; m < nnodes; ++m) {
457  this->particles.push_back(dynamic_pointer_cast<encap_type>(this->get_factory()->create(pfasst::encap::solution)));
458  this->residuals.push_back(dynamic_pointer_cast<encap_type>(this->get_factory()->create(pfasst::encap::solution)));
459  this->saved_particles.push_back(dynamic_pointer_cast<encap_type>(this->get_factory()->create(pfasst::encap::solution)));
460  this->forces.push_back(cloud_component_factory<scalar>(this->particles[m]->size(), this->particles[m]->dim()));
461  this->saved_forces.push_back(cloud_component_factory<scalar>(this->particles[m]->size(), this->particles[m]->dim()));
462  this->b_vecs.push_back(cloud_component_factory<scalar>(this->particles[m]->size(), this->particles[m]->dim()));
463  this->saved_b_vecs.push_back(cloud_component_factory<scalar>(this->particles[m]->size(), this->particles[m]->dim()));
464  if (coarse) {
465  this->tau_q_corrections.push_back(make_shared<acceleration_type>(cloud_component_factory<scalar>(this->particles[m]->size(), this->particles[m]->dim())));
466  this->tau_qq_corrections.push_back(make_shared<acceleration_type>(cloud_component_factory<scalar>(this->particles[m]->size(), this->particles[m]->dim())));
467  }
468  }
469 
470  for (size_t m = 0; m < num_s_integrals; ++m) {
471  this->s_integrals.push_back(cloud_component_factory<scalar>(this->particles[m]->size(), this->particles[m]->dim()));
472  this->ss_integrals.push_back(cloud_component_factory<scalar>(this->particles[m]->size(), this->particles[m]->dim()));
473  }
474 
475  this->q_mat = this->get_quadrature()->get_q_mat();
476  this->qq_mat = this->q_mat * this->q_mat;
477  this->s_mat = Matrix<time>(nnodes, nnodes);
478  this->s_mat.fill(time(0.0));
479  this->qt_mat = Matrix<time>(nnodes, nnodes);
480  this->qt_mat.fill(time(0.0));
481  this->qx_mat = Matrix<time>(nnodes, nnodes);
482  this->qx_mat.fill(time(0.0));
483  this->ss_mat = Matrix<time>(nnodes, nnodes);
484  this->ss_mat.fill(time(0.0));
485  this->sx_mat = Matrix<time>(nnodes, nnodes);
486  this->sx_mat.fill(time(0.0));
487  this->st_mat = Matrix<time>(nnodes, nnodes);
488  this->st_mat.fill(time(0.0));
489 
490  // building rules for Q_E and Q_I:
491  // Q_E is striclty lower diagonal matrix with delta nodes of column index
492  // Q_I is lower diagonal matrix with first row and column all zero and delta nodes of
493  // column index minus one
494  Matrix<time> qe_mat = Matrix<time>(nnodes, nnodes);
495  qe_mat.fill(time(0.0));
496  Matrix<time> qi_mat = Matrix<time>(nnodes, nnodes);
497  qi_mat.fill(time(0.0));
498  for (size_t i = 0; i < nnodes; ++i) {
499  for (size_t j = 0; j < nnodes; ++j) {
500  if (j < i) { qe_mat(i, j) = delta_nodes[j + 1]; }
501  if (j > 0 && j <= i) { qi_mat(i, j) = delta_nodes[j]; }
502  }
503  }
504 
505  // Q_T = 0.5 * (Q_E + Q_I)
506  this->qt_mat = 0.5 * (qe_mat + qi_mat);
507 
508  // Q_x = Q_E * Q_T + 0.5 * (Q_E ∘ Q_E)
509  // (only first term, i.e. matrix product)
510  this->qx_mat = qe_mat * qt_mat;
511 
512  // compute QQ, S, SS, Q_T, Q_x
513  for (size_t i = 1; i < nnodes; i++) {
514  for (size_t j = 0; j < nnodes; j++) {
515  // continuation of Q_x
516  // (i.e. Hadamard product of Q_E)
517  this->qx_mat(i, j) += 0.5 * qe_mat(i, j) * qe_mat(i, j);
518  }
519 
520  this->s_mat.row(i) = this->q_mat.row(i) - this->q_mat.row(i - 1);
521  this->ss_mat.row(i) = this->qq_mat.row(i) - this->qq_mat.row(i - 1);
522  this->sx_mat.row(i) = this->qx_mat.row(i) - this->qx_mat.row(i - 1);
523  this->st_mat.row(i) = this->qt_mat.row(i) - this->qt_mat.row(i - 1);
524  }
525 
526  size_t nsteps = this->get_controller()->get_end_time() / this->get_controller()->get_step_size();
527  size_t digit_step = to_string(nsteps + 1).length();
528  size_t digit_iter = to_string(this->get_controller()->get_max_iterations() - 1).length();
529  this->FORMAT_STR = "step: %|" + to_string(digit_step) + "| iter: %|" + to_string(digit_iter) + "| (%-6s)"
530  + " residual: %10.4e energy drift: %10.4e total energy: %10.2f";
531 
532  UNUSED(coarse);
533  this->log_indent->decrement(1);
534  }
535 
536  template<typename scalar, typename time>
537  void BorisSweeper<scalar, time>::integrate(time dt, vector<shared_ptr<Encapsulation<time>>> dst) const
538  {
539  throw NotImplementedYet("Boris::integrate for basic Encap type");
540  }
541 
542  template<typename scalar, typename time>
543  void BorisSweeper<scalar, time>::integrate(time dt, vector<shared_ptr<acceleration_type>> dst_q,
544  vector<shared_ptr<acceleration_type>> dst_qq) const
545  {
546  BCVLOG(6) << "integrating over dt=" << dt;
547  this->log_indent->increment(6);
548  const size_t nnodes = this->get_nodes().size();
549 
550  vector<acceleration_type> rhs(nnodes);
551  for (size_t m = 0; m < nnodes; ++m) {
552  rhs[m] = this->build_rhs(m);
553  }
554 
555  for (size_t m = 0; m < nnodes; ++m) {
556  zero(dst_q[m]);
557  zero(dst_qq[m]);
558  for (size_t n = 0; n < nnodes; ++n) {
559  // for positions
560  *(dst_qq[m].get()) += rhs[n] * dt * dt * this->qq_mat(m, n)
561  + this->start_particles->velocities() * dt * this->q_mat(m, n);
562  // for velocities
563  *(dst_q[m].get()) += rhs[n] * dt * this->q_mat(m, n);
564  }
565  BCVLOG(6) << "integral(QQ)[" << m << "]: " << *(dst_qq[m].get());
566  BCVLOG(6) << "integral(Q)[" << m << "]: " << *(dst_q[m].get());
567  }
568  this->log_indent->decrement(6);
569  }
570 
571  template<typename scalar, typename time>
572  void BorisSweeper<scalar, time>::residual(time dt, vector<shared_ptr<Encapsulation<time>>> dst) const
573  {
574  BCVLOG(8) << "computing residual";
575  const auto nodes = this->get_nodes();
576  const size_t nnodes = nodes.size();
577  assert(dst.size() == nnodes);
578 
579  vector<shared_ptr<encap_type>> dst_cast(nnodes);
580  vector<shared_ptr<acceleration_type>> q_int(nnodes), qq_int(nnodes);
581  for (size_t m = 0; m < nnodes; ++m) {
582  dst_cast[m] = dynamic_pointer_cast<encap_type>(dst[m]);
583  assert(dst_cast[m]);
584 
585  qq_int[m] = make_shared<acceleration_type>(this->start_particles->size() * this->start_particles->dim());
586  q_int[m] = make_shared<acceleration_type>(this->start_particles->size() * this->start_particles->dim());
587  }
588 
589  // cf. pySDC: QF(u)
590  this->integrate(dt, q_int, qq_int);
591 
592  for (size_t m = 1; m < nnodes; ++m) {
593  BCVLOG(8) << "for node " << m;
594  zero(dst_cast[m]->positions());
595  zero(dst_cast[m]->velocities());
596 
597  dst_cast[m]->positions() = *(qq_int[m].get());
598  dst_cast[m]->velocities() = *(q_int[m].get());
599 
600  // cf. pySDC: L.u[0] - L.u[m+1] (in boris_2nd_order#compute_residual())
601  dst_cast[m]->positions() += this->start_particles->positions() - this->particles[m]->positions();
602  dst_cast[m]->velocities() += this->start_particles->velocities() - this->particles[m]->velocities();
603 
604  // add tau correction (if available)
605  if (this->tau_q_corrections.size() > 0 && this->tau_qq_corrections.size()) {
606  dst_cast[m]->positions() += *(this->tau_qq_corrections[m].get());
607  dst_cast[m]->velocities() += *(this->tau_q_corrections[m].get());
608  }
609  }
610 
611  this->log_indent->decrement(8);
612  }
613 
614  template<typename scalar, typename time>
616  {
617  BCVLOG(2) << "advancing to next step";
618  this->log_indent->increment(2);
619  this->start_particles->copy(this->end_particles);
620  this->energy_evals.front() = this->energy_evals.back();
621  this->forces.front() = this->forces.back();
622  this->b_vecs.front() = this->b_vecs.back();
623  this->exact_updated = false;
624  BCVLOG(9) << "new starting values:";
625  BCVLOG(9) << " => start_particles: " << this->start_particles;
626  BCVLOG(9) << " => energies: " << this->energy_evals.front();
627  BCVLOG(9) << " => forces: " << this->forces.front();
628  BCVLOG(9) << " => b_vecs: " << this->b_vecs.front();
629  this->log_indent->decrement(2);
630  }
631 
632  template<typename scalar, typename time>
634  {
635  time t = this->get_controller()->get_time() + this->get_controller()->get_step_size() * this->delta_nodes[m];
636  BCVLOG(2) << "computing forces at node " << m << " (t=" << t << ")";
637  this->log_indent->increment(2);
638 
639 #ifndef BORIS_SAME_LEVELS
640  if (this->coarse) {
641  BCVLOG(2) << "only external electric field (because on coarse level)";
642  // don't compute internal electric forces when on coarse level
643  this->forces[m] = this->impl_solver->external_e_field_evaluate(this->particles[m], t);
644  } else {
645  BCVLOG(2) << "internal and external electric field (because not on coarse level)";
646  this->forces[m] = this->impl_solver->e_field_evaluate(this->particles[m], t);
647  }
648 #else
649  BCVLOG(2) << "internal and external electric field";
650  this->forces[m] = this->impl_solver->e_field_evaluate(this->particles[m], t);
651 #endif
652  this->b_vecs[m] = this->impl_solver->b_field_vecs(this->particles[m], t);
653 
654  BCVLOG(9) << "for particles:" << this->particles[m];
655  BCVLOG(9) << " => e_forces:" << this->forces[m];
656  BCVLOG(9) << " => b_vecs: " << this->b_vecs[m];
657  this->log_indent->decrement(2);
658  this->f_evals++;
659  }
660 
661  template<typename scalar, typename time>
663  {
664  BCVLOG(1) << "predicting with initial particle cloud: " << *(this->start_particles.get());
665  this->log_indent->increment(1);
666  // in any case copy as we have a simple spread as predict
667  this->particles[0]->copy(this->start_particles);
668 
669  this->spread();
670  for (size_t m = 0; m < this->particles.size(); ++m) {
671  this->evaluate(m);
672  }
673 
674  // in any case copy as we have a simple spread as predict
675  this->end_particles->copy(this->particles.back());
676 
677  this->save();
678  this->log_indent->decrement(1);
679  }
680 
681  template<typename scalar, typename time>
683  {
684  const auto nodes = this->get_nodes();
685  const size_t nnodes = nodes.size();
686  assert(nnodes >= 1);
687  time t = this->get_controller()->get_time();
688  time dt = this->get_controller()->get_step_size();
689  BCVLOG(1) << "sweeping for t=" << t << " and dt=" << dt;
690  this->log_indent->increment(1);
691  BCVLOG(2) << "with nodes: " << nodes;
692  BCVLOG(2) << "initial: " << *(this->start_particles.get());
693  BCVLOG(2) << "previous particles:";
694  for (size_t m = 0; m < nnodes; ++m) {
695  BCVLOG(2) << " [" << m << "]: " << *(this->saved_particles[m].get());
696  }
697  BCVLOG(2) << "current particles:";
698  for (size_t m = 0; m < nnodes; ++m) {
699  BCVLOG(2) << " [" << m << "]: " << *(this->particles[m].get());
700  }
701 
702  this->impl_solver->energy(this->particles.front(), t);
703 
704  // compute integrals
705  BCVLOG(1) << "computing integrals";
706  if (this->get_quadrature()->left_is_node()) {
707  // starting at m=1 as m=0 will only add zeros
708  for (size_t m = 1; m < nnodes; m++) {
709  zero(this->s_integrals[m]);
710  zero(this->ss_integrals[m]);
711  for (size_t l = 0; l < nnodes; l++) {
712  auto rhs = this->build_rhs(l);
713  this->s_integrals[m] += rhs * dt * this->s_mat(m, l);
714  this->ss_integrals[m] += rhs * dt * dt * this->ss_mat(m, l);
715  }
716  }
717  if (this->tau_q_corrections.size() > 0 && this->tau_qq_corrections.size()) {
718  BCVLOG(2) << "adding FAS correction to integrals";
719  this->log_indent->increment(2);
720  for (size_t m = 0; m < nnodes; ++m) {
721  BCVLOG(2) << "+= tau_q[" << m << "] (<" << this->tau_q_corrections[m] << ">" << *(this->tau_q_corrections[m].get()) << ")";
722  BCVLOG(2) << "+= tau_qq[" << m << "] (<" << this->tau_qq_corrections[m] << ">" << *(this->tau_qq_corrections[m].get()) << ")";
723  this->s_integrals[m] += *(this->tau_q_corrections[m].get());
724  this->ss_integrals[m] += *(this->tau_qq_corrections[m].get());
725  if (m > 0) {
726  BCVLOG(2) << "-= tau_q[" << m-1 << "] (<" << this->tau_q_corrections[m-1] << ">" << *(this->tau_q_corrections[m-1].get()) << ")";
727  BCVLOG(2) << "-= tau_qq[" << m-1 << "] (<" << this->tau_qq_corrections[m-1] << ">" << *(this->tau_qq_corrections[m-1].get()) << ")";
728  this->s_integrals[m] -= *(this->tau_q_corrections[m-1].get());
729  this->ss_integrals[m] -= *(this->tau_qq_corrections[m-1].get());
730  }
731  }
732  this->log_indent->decrement(2);
733  }
734  } else {
735  throw NotImplementedYet("left-is-NOT-node");
736  }
737  BCVLOG(2) << "s_int: " << this->s_integrals;
738  BCVLOG(2) << "ss_int: " << this->ss_integrals;
739 
740  this->evaluate(0);
741 
742  for (size_t m = 0; m < nnodes - 1; m++) {
743  time ds = dt * this->delta_nodes[m+1];
744  BCVLOG(1) << "node " << m << " (ds=" << ds << ")";
745  this->log_indent->increment(1);
746  BCVLOG(2) << "old m+1 particle: " << this->particles[m+1];
747 
749  //
750  // x_{m+1}^{k+1} = x_{m}^{k+1}
751  this->update_position(m, dt, ds);
752  BCVLOG(1) << "new positions: " << this->particles[m+1]->positions();
753 
754  // evaluate electric field with new position
755 #ifndef BORIS_SAME_LEVELS
756  if (this->coarse) {
757  BCVLOG(2) << "only external electric field (because on coarse level)";
758  // don't compute internal electric forces when on coarse level
759  this->forces[m+1] = this->impl_solver->external_e_field_evaluate(this->particles[m+1], t + nodes[m]);
760  } else {
761  BCVLOG(2) << "internal and external electric field (because not on coarse level)";
762  this->forces[m+1] = this->impl_solver->e_field_evaluate(this->particles[m+1], t + nodes[m]);
763  }
764 #else
765  BCVLOG(2) << "internal and external electric field";
766  this->forces[m+1] = this->impl_solver->e_field_evaluate(this->particles[m+1], t + nodes[m]);
767 #endif
768 
770  this->update_velocity(m, ds, nodes);
771  BCVLOG(1) << "new velocities: " << this->particles[m+1]->velocities();
772 
773  this->log_indent->decrement(1);
774  }
775 
776  if (this->get_quadrature()->right_is_node()) {
777  this->end_particles->copy(this->particles.back());
778  } else {
779  throw NotImplementedYet("right-is-NOT-node");
780  }
781 
782  this->save();
783  this->log_indent->decrement(1);
784  }
785 
786  template<typename scalar, typename time>
787  void BorisSweeper<scalar, time>::save(bool initial_only)
788  {
789  BCVLOG(2) << "saving current state" << ((initial_only) ? " (only initial)" : "");
790  this->log_indent->increment(2);
791  if (initial_only) {
792  this->saved_particles[0] = make_shared<encap_type>(*(this->particles[0].get()));
793  this->saved_forces[0] = this->forces[0];
794  this->saved_b_vecs[0] = this->b_vecs[0];
795  } else {
796  for (size_t m = 0; m < this->saved_particles.size(); m++) {
797  BCVLOG(9) << "node " << m;
798  BCVLOG(9) << " particle: " << this->particles[m];
799  this->saved_particles[m] = make_shared<encap_type>(*(this->particles[m].get()));
800  BCVLOG(9) << " previous_particle: " << this->saved_particles[m];
801  }
802  BCVLOG(9) << "forces: " << this->forces;
803  this->saved_forces = this->forces;
804  BCVLOG(9) << "saved_forces: " << this->saved_forces;
805  BCVLOG(9) << "b_vecs: " << this->b_vecs;
806  this->saved_b_vecs = this->b_vecs;
807  BCVLOG(9) << "saved_b_vecs: " << this->saved_b_vecs;
808  }
809  this->log_indent->decrement(2);
810  }
811 
812  template<typename scalar, typename time>
814  {
815  for (size_t m = 1; m < this->particles.size(); ++m) {
816  this->set_state(const_pointer_cast<const encap_type>(this->particles[0]), m);
817  }
818  }
819 
820  template<typename scalar, typename time>
822  {
823  time t = this->get_controller()->get_time();
824  time dt = this->get_controller()->get_step_size();
825  this->echo_error(t + dt);
826  }
827 
828  template<typename scalar, typename time>
830  {
831  time t = this->get_controller()->get_time();
832  time dt = this->get_controller()->get_step_size();
833  this->echo_error(t + dt);
834  }
835 
836  template<typename scalar, typename time>
838  {}
839 
840  template<typename scalar, typename time>
842  {
843  this->start_particles->post(comm, tag);
844  }
845 
846  template<typename scalar, typename time>
847  void BorisSweeper<scalar, time>::send(ICommunicator* comm, int tag, bool blocking)
848  {
849  this->end_particles->send(comm, tag, blocking);
850  }
851 
852  template<typename scalar, typename time>
853  void BorisSweeper<scalar, time>::recv(ICommunicator* comm, int tag, bool blocking)
854  {
855  this->start_particles->recv(comm, tag, blocking);
856  if (this->quadrature->left_is_node()) {
857  this->particles[0]->copy(this->start_particles);
858  }
859  }
860 
861  template<typename scalar, typename time>
863  {
864  if (comm->rank() == comm->size() - 1) {
865  this->start_particles->copy(this->end_particles);
866  }
867  this->start_particles->broadcast(comm);
868  }
869 
870  template<typename scalar, typename time>
871  void BorisSweeper<scalar, time>::boris_solve(const time tm, const time t_next, const time ds, const size_t m,
872  const velocity_type& c_k_term)
873  {
874  BCVLOG(3) << "solving with Boris' method";
875  this->log_indent->increment(3);
876  const size_t npart = this->start_particles->size();
877  UNUSED(t_next);
878 
879  velocity_type c_k_term_half = c_k_term / scalar(2.0);
880  BCVLOG(5) << "c_k_term/2: " << c_k_term_half;
881  vector<scalar> beta = cmp_wise_div(this->particles[m]->charges(), this->particles[m]->masses()) / scalar(2.0) * ds;
882  BCVLOG(5) << "beta: " << beta;
883  acceleration_type e_forces_mean = (this->forces[m] + this->forces[m+1]) / scalar(2.0);
884  BCVLOG(5) << "e_mean: " << e_forces_mean << " (<=" << this->forces[m] << " +" << this->forces[m+1] << " / 2)";
885 
886  // first Boris' drift
887  // v^{-} = v^{k}
888  velocity_type v_minus = this->particles[m]->velocities();
889  // + \beta * E_{mean} + c^{k} / 2
890  v_minus += e_forces_mean * beta + c_k_term_half;
891  BCVLOG(3) << "v-: " << v_minus;
892 
893  // Boris' kick
894  vector<scalar> b_field_vector = this->impl_solver->get_b_field_vector();
895  velocity_type boris_t = kronecker(beta, b_field_vector);
896  velocity_type v_prime = v_minus + cross_prod(v_minus, boris_t);
897  BCVLOG(3) << "v': " << v_prime;
898 
899  // final Boris' drift
900  vector<scalar> boris_t_sqr = norm_sq_npart(boris_t, npart); // particle-wise scalar product of boris_t
901 // for (size_t p = 0; p < boris_t.size(); ++p) {
902 // boris_t_sqr[p] = pow(norm0(boris_t[p]), 2);
903 // }
904  velocity_type boris_s = (boris_t * scalar(2.0)) / (boris_t_sqr + scalar(1.0));
905  velocity_type v_plus = v_minus + cross_prod(v_prime, boris_s);
906  BCVLOG(3) << "v+: " << v_plus;
907 
908 // assert(abs(v_minus.norm0() - v_plus.norm0()) <= 10e-8);
909 
910  this->particles[m+1]->velocities() = v_plus + e_forces_mean * beta + c_k_term_half;
911  this->log_indent->decrement(3);
912  }
913  } // ::pfasst::examples::boris
914  } // ::pfasst::examples
915 } // ::pfasst
virtual void setup(bool coarse=false) override
Setup (allocate etc) the sweeper.
static void zero(vector< precision > &data)
virtual void post_predict() override
Hook automatically run after each completed predict.
virtual void set_state(shared_ptr< const encap_type > u0, size_t m)
virtual shared_ptr< acceleration_type > get_tau_qq_as_force(size_t m) const
virtual void broadcast(ICommunicator *comm) override
virtual void residual(time dt, vector< shared_ptr< Encapsulation< time >>> dst) const override
Compute residual at each SDC node (including FAS corrections).
#define BCVLOG(level)
map< error_index, ErrorTuple< scalar >> error_map
virtual void save(bool initial_only=false) override
Save states (and/or function values) at all nodes.
pair< size_t, size_t > error_index
Not implemented yet exception.
Definition: interfaces.hpp:29
virtual void boris_solve(const time tm, const time t_next, const time ds, const size_t m, const velocity_type &c_k_term)
static vector< precision > kronecker(const vector< precision > &first, const vector< precision > &second)
STL namespace.
void cross_prod(const double first[DIM], const double second[DIM], double cross_prod[DIM])
virtual void advance() override
Advance from one time step to the next.
virtual void spread() override
Initialize solution values at all time nodes with meaningful values.
ParticleCloudComponent< precision > & positions()
static void add_custom_logger(const string &id)
Provides convenient way of adding additional named loggers.
Definition: logging.hpp:360
virtual void post_sweep() override
Hook automatically run after each completed sweep.
virtual void post(ICommunicator *comm, int tag) override
ParticleCloudComponent< precision > & velocities()
vector< precision > ParticleComponent
Definition: particle.hpp:27
virtual void exact(shared_ptr< Encapsulation< time >> q, time t)
virtual int size()=0
virtual error_map< scalar > get_errors() const
virtual void setup(bool coarse) override
Setup (allocate etc) the sweeper.
virtual shared_ptr< Encapsulation< time > > get_state(size_t m) const override
Retrieve solution values of current iteration at time node index m.
virtual shared_ptr< Encapsulation< time > > get_saved_state(size_t m) const override
Retrieve solution values of previous iteration at time node index m.
static vector< precision > norm_sq_npart(const vector< precision > &data, const size_t npart)
static vector< precision > cross_prod(const vector< precision > &first, const vector< precision > &second)
virtual int rank()=0
virtual shared_ptr< Encapsulation< time > > get_start_state() const
virtual shared_ptr< acceleration_type > get_tau_q_as_force(size_t m) const
static vector< precision > cmp_wise_div(const vector< precision > &first, const vector< precision > &second)
ParticleCloudComponent< scalar > velocity_type
static precision max(const vector< precision > &data)
Data/solution encapsulation.
virtual void recv(ICommunicator *comm, int tag, bool blocking) override
ParticleCloudComponent< scalar > acceleration_type
tuple t
Definition: plot.py:12
virtual void send(ICommunicator *comm, int tag, bool blocking) override
virtual void post_step() override
Hook automatically run after each completed time step.
#define UNUSED(expr)
Denoting unused function parameters for omitting compiler warnings.
Definition: globals.hpp:32
virtual void predict(bool initial) override
Perform a predictor sweep.
virtual void echo_error(const time t, bool predict=false)
Abstract interface for communicators.
Definition: interfaces.hpp:70
Eigen::Matrix< coeff, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > Matrix
virtual void sweep() override
Perform one SDC sweep/iteration.
virtual void set_start_state(shared_ptr< const encap_type > u0)
float dt
Definition: plot.py:10
virtual void integrate(time dt, vector< shared_ptr< Encapsulation< time >>> dst) const override
Integrates values of right hand side at all time nodes \( t \in [0,M-1] \) simultaneously.