Fast Methods for Cosmological Simulations
FastSim serves as a tool for quick N-body simulations in modified gravity.
chameleon.cpp
Go to the documentation of this file.
1 
10 #include "chameleon.hpp"
11 #include "core_app.h"
12 #include "core_power.h"
13 #include "core_mesh.h"
14 #include "core_out.h"
15 #include "integration.hpp"
16 #include "params.hpp"
17 #include "multigrid_solver.h"
18 
19 #include <algorithm>
20 
21 /*****************************/
24 namespace{
25 
31 typedef double CHI_PREC_t;
32 
38 constexpr FTYPE_t MPL = (FTYPE_t)1;
39 
45 constexpr FTYPE_t c_kms = (FTYPE_t)299792.458;
46 
52 constexpr CHI_PREC_t MARK_CHI_BOUND_COND = (CHI_PREC_t)-2;
53 
59 constexpr CHI_PREC_t SWITCH_BIS_NEW = (CHI_PREC_t)0.1;
60 
66 constexpr CHI_PREC_t CHI_MIN = (CHI_PREC_t)-1;
67 
72 constexpr double CONVERGENCE_RES = 0;
73 constexpr double CONVERGENCE_RES_MIN = 0;
74 constexpr double CONVERGENCE_ERR = 0.97;
75 constexpr double CONVERGENCE_ERR_MIN = 0.8;
76 constexpr size_t CONVERGENCE_NUM_FAIL = 5;
77 constexpr size_t CONVERGENCE_BI_STEPS = 5;
78 constexpr size_t CONVERGENCE_BI_STEPS_INIT = 3;
79 constexpr CHI_PREC_t CONVERGENCE_BI_DCHI = (CHI_PREC_t)1e-2;
80 constexpr CHI_PREC_t CONVERGENCE_BI_L = (CHI_PREC_t)1e-2;
81 
87 
88 template<typename T>
89 void transform_Mesh_to_Grid(const Mesh& mesh, Grid<3, T> &grid)
90 {/* copy data in Mesh 'N*N*(N+2)' onto MultiGrid 'N*N*N' */
91  size_t ix, iy, iz;
92  const size_t N_tot = grid.get_Ntot();
93  const size_t N = grid.get_N();
94 
95  if (mesh.N != N) throw std::range_error("Mesh of a different size than Grid!");
96 
97  #pragma omp parallel for private(ix, iy, iz)
98  for (size_t i = 0; i < N_tot; ++i)
99  {
100  ix = i % N;
101  iy = i / N % N;
102  iz = i / (N*N);
103  grid[i] = mesh(ix, iy, iz);
104  }
105 }
106 
107 template<typename T>
109 {
110  transform_Mesh_to_Grid(mesh, mltgrid.get_grid());
111  mltgrid.restrict_down_all();
112 }
113 
114 template<typename T>
115 void transform_Grid_to_Mesh(Mesh& mesh, const Grid<3, T> &grid)
116 {/* copy data in MultiGrid 'N*N*N' onto Mesh 'N*N*(N+2)' */
117  size_t ix, iy, iz;
118  const size_t N_tot = grid.get_Ntot();
119  const size_t N = grid.get_N();
120 
121  if (mesh.N != N) throw std::range_error("Mesh of a different size than Grid!");
122 
123  #pragma omp parallel for private(ix, iy, iz)
124  for (size_t i = 0; i < N_tot; ++i)
125  {
126  ix = i % N;
127  iy = i / N % N;
128  iz = i / (N*N);
129  mesh(ix, iy, iz) = grid[i];
130  }
131 }
132 
133 template<typename T>
134 void transform_MultiGrid_to_Mesh(Mesh& mesh, const MultiGrid<3, T> &mltgrid){ transform_Grid_to_Mesh(mesh, mltgrid.get_grid()); }
135 
136 template<typename T>
138 
139 template<typename T>
140 T min(const std::vector<T>& data){ return *std::min_element(data.begin(), data.end()); }
141 
142 FTYPE_t min(const Mesh& data){ return min(data.data); }
143 
144 template<typename T>
145 FTYPE_t min(const Grid<3, T> &grid){ return min(grid.get_vec()); }
146 
147 template<typename T>
148 FTYPE_t min(const MultiGrid<3, T> &grid){ return min(grid.get_grid()); }
149 
155 template<typename T>
156 class ChiSolver : public MultiGridSolver<3, T>
157 {
158 public:
159  // Parameters
160  const T n;
161  const T beta;
162  const T chi_0;
163  const T phi_prefactor;
166 
167  // convergence parameters
168  size_t m_conv_stop = 0; // number of unsuccessful sweeps
169  double m_rms_stop_min; // iterate at least until _rms_res < m_conv_eps_min
170  double m_err_stop; // stop iteration when: 1 > err > m_err_stop
171  double m_err_stop_min; // iterate at least until: err > m_err_stop_min
172  size_t m_num_fail; // give up converging if number of failed iteration (err > 1) is > m_num_fail
173 
174  // bisection convergence parameters
175  size_t m_max_bisection_steps; // at given point perfom max this number of inteval halving
176  T m_dchi_stop; // if change in chi is small, stop halving
177  T m_l_stop; // if residuum is small, stop halving
178 
179  // variables for checking solution in deep-screened regime, for each level
180  std::vector<std::map<size_t, T>> fix_vals;
181 
182 
183  ChiSolver(size_t N, unsigned int Nmin, const Sim_Param& sim, bool verbose = true) :
184  MultiGridSolver<3, T>(N, Nmin, verbose), n(sim.chi_opt.n), beta(sim.chi_opt.beta), chi_0(2*beta*MPL*sim.chi_opt.phi),
185  phi_prefactor( // prefactor for Poisson equation for gravitational potential, [] = (h/Mpc)^2
186  // 4*pi*G*rho_m,0 + computing units [Mpc/h]
187  3/2.*sim.cosmo.Omega_m*pow(sim.cosmo.H0 * sim.cosmo.h / c_kms ,2)
188  ),
189  chi_prefactor_0( // dimensionless prefactor to poisson equation
190  // beta*rho_m,0 / (Mpl*chi_0) + computing units [Mpc/h]; additional a^(-3 -3/(1-n)) at each timestep
191  phi_prefactor*pow(sim.box_opt.box_size ,2) / sim.chi_opt.phi
192  ),
193  fix_vals(this->get_Nlevel())
194  {
195  if ((n <= 0) || (n >= 1) || (chi_0 <= 0)) throw std::out_of_range("invalid values of chameleon power-law potential parameters");
196  }
197 
198  ChiSolver(size_t N, const Sim_Param& sim, bool verbose = true) : ChiSolver(N, 2, sim, verbose) {}
199 
200  T chi_a(T a) const
201  {
202  return chi_0*pow(a, 3/(1-n));
203  }
204 
205  T chi_force_units(T a) const
206  {
207  return beta/MPL*chi_a(a)/phi_prefactor;
208  }
209 
210  void get_chi_k(Mesh& rho_k)
211  {/* transform input density in k-space into linear prediction for chameleon field,
212  includes w(k) corrections for interpolation of particles */
213  const size_t N = rho_k.N;
214  const size_t l_half = rho_k.length/2;
215  const T mass_sq = (1-n)*chi_prefactor/pow(2*PI, 2); // dimensionless square mass, with derivative factor k* = 2*PI / L
216  const T chi_a_n = -1/(1-n); // prefactor for chi(k), in chi_a units
217 
218  T k2, g_k;
219 
220  #pragma omp parallel for private(k2, g_k)
221  for(size_t i=0; i < l_half;i++){
222  k2 = get_k_sq(N, i);
223  if (k2 == 0)
224  {
225  rho_k[2*i] = 0;
226  rho_k[2*i+1] = 0;
227  }
228  else
229  {
230  g_k = chi_a_n/(k2+mass_sq)*mass_sq; // Green function
231  rho_k[2*i] *= g_k;
232  rho_k[2*i+1] *= g_k;
233  }
234  }
235  }
236 
237  bool check_surr_dens(T const* const rho_grid, std::vector<size_t> index_list, size_t i, size_t N)
238  {/* internal method for finding highest density in nearby points */
239  // never fix bulk field in under-dense region
240  if (rho_grid[i] <= 0) return false;
241 
242  // check surrounding points if theres is higher density
243  this->get_neighbor_gridindex(index_list, i, N);
244  for(size_t i_s : index_list) if (rho_grid[i_s] > rho_grid[i]) return false;
245 
246  // if current point has the highest density, fix chameleon value
247  return true;
248  }
249 
250  void set_time(T a, const Cosmo_Param& cosmo)
251  {
252  chi_prefactor = chi_prefactor_0*pow(a, -3.*(2.-n)/(1.-n));
253  }
254 
255  T get_chi_prefactor() const { return chi_prefactor; }
256  T get_phi_prefactor() const { return phi_prefactor; }
257 
258  T l_operator(const T chi_i, const size_t level, const std::vector<size_t>& index_list, const bool addsource, const T h) const
259  {/* The dicretized equation L(phi) */
260  // Solution and pde-source grid at current level
261  const size_t i = index_list[0];
262  T const* const chi = this->get_y(level); // solution
263  const T rho = this->get_external_field(level, 0)[i];
264 
265  // do not change values in screened regions
266  if (rho == MARK_CHI_BOUND_COND) return 0;
267 
268  // The right hand side of the PDE
269  T source = (1 + rho - pow(chi_i - CHI_MIN, n - 1)) * chi_prefactor;
270 
271  // Compute the standard kinetic term [D^2 phi] (in 1D this is phi''_i = phi_{i+1} + phi_{i-1} - 2 phi_{i} )
272  T kinetic = -2*chi_i*3; // chi, '-2*3' is factor in 3D discrete laplacian
273  // go through all surrounding points
274  for(auto it = index_list.begin() + 1; it < index_list.end(); ++it) kinetic += chi[*it];
275 
276  // source term arising rom restricting the equation down to the lower level
277  if( level > 0 && addsource ) source += this->get_multigrid_source(level, i);
278 
279  // The discretized equation of motion L_{ijk...}(phi) = 0
280  return kinetic/(h*h) - source;
281  }
282 
283  T l_operator(const size_t level, const std::vector<size_t>& index_list, const bool addsource, const T h) const override
284  {/* The dicretized equation L(phi) */
285  // Solution and pde-source grid at current level
286  const size_t i = index_list[0];
287  T const* const chi = this->get_y(level); // solution
288  const T chi_i = chi[i];
289  return l_operator(chi_i, level, index_list, addsource, h);
290  }
291 
292  // Differential of the L operator: dL_{ijk...}/dphi_{ijk...}
293  T dl_operator(const size_t level, const std::vector<size_t>& index_list, const T h) const override
294  {
295  // solution
296  const T chi = this->get_y(level)[ index_list[0] ];
297 
298  // Derivative of source
299  const T dsource = chi_prefactor*(1-n)*pow(chi - CHI_MIN, n-2);
300 
301  // Derivative of kinetic term
302  const T dkinetic = -2.0*3;
303 
304  return dkinetic/(h*h) - dsource;
305  }
306 
307  bool find_opposite_l_sign(const T f1, const T l1, T df, T& f2, T& l2, const size_t level, const std::vector<size_t>& index_list, const T h) const
308  {/* find such 'f2' that 'l_operator(f2)' has opposite sign than l1
309  use df as a guess in which direction to be looking */
310  f2 = f1;
311  for (size_t j = 0; j < CONVERGENCE_BI_STEPS_INIT; ++j)
312  {
313  f2 += df;
314  if (f2 <= CHI_MIN)
315  {
316  const size_t i = index_list[0];
317  const T rho = this->get_external_field(level, 0)[i];
318 
319  f2 = chi_min(rho);
320  j = CONVERGENCE_BI_STEPS_INIT;//< end of loop but first check for an improvement
321  }
322  l2 = l_operator(f2, level, index_list, true, h);
323  if (l1*l2 <= 0) return true;
324  }
325  return false;
326  }
327 
328  bool check_bisection_convergence(const T df_new, const T l_new) const
329  {
330  return ((std::abs(l_new) < m_l_stop) || (std::abs(df_new) < m_dchi_stop));
331  }
332 
333  T bisection_step(T& f1, T& l1, T& f2, T& l2, const size_t level, const std::vector<size_t>& index_list, const T h) const
334  {/* given 'f1' and 'f2' with different signs of l_operator(f_i) perform one step of bisection:
335  f_new = (f1 + f2) / 2
336  change whichever l_operator(f_i) has the same sign as l_operator(f_new)
337  return value indicates convergence -- 0 (unphysical for chameleon) not, otherwise yes*/
338 
339  const T f_new = (f1 + f2) / 2;
340  const T l_new = l_operator(f_new, level, index_list, true, h);
341 
342  if (check_bisection_convergence(f2 - f_new, l_new)) return f_new;
343 
344  if (l1*l_new > 0) {
345  f1 = f_new;
346  l1 = l_new;
347 
348  } else {
349  f2 = f_new;
350  l2 = l_new;
351  }
352 
353  return CHI_MIN;
354  }
355 
356  T bisection(T f1, T l1, const T df, const size_t level, const std::vector<size_t>& index_list, const T h) const
357  {/* initialize bisection solver -- find two values with opposite value of l_operator -- and start iterating */
358  T f_new, f2, l2;
359 
360  // get initial guess -- return when failed
361  if (!find_opposite_l_sign(f1, l1, df, f2, l2, level, index_list, h)) return f2;
362 
363  // iterate
364  for (size_t i = 0; i < m_max_bisection_steps; ++i){
365  f_new = bisection_step(f1, l1, f2, l2, level, index_list, h);
366  if (f_new != CHI_MIN)
367  {
368  return f_new;
369  }
370  }
371 
372  // failed iteration
373  return f1;
374  }
375 
376 
377  T upd_operator(const T f, const size_t level, const std::vector<size_t>& index_list, const T h) const override
378  {/* Method for updating solution:
379  if df is large, try bisection, otherwise Newton`s method
380  try Newton`s method and check for unphysical values */
381  T l = l_operator(level, index_list, true, h);
382  T dl = dl_operator(level, index_list, h);
383  T df = -l/dl;
384  T df_rel = std::abs(df/(f - CHI_MIN));
385 
386  static_assert((SWITCH_BIS_NEW < 1), "Newton`s method cannot be allowed with negative values. Adjust 'SWITCH_BIS_NEW < 1'.");
387 
388  return df_rel < SWITCH_BIS_NEW ? f + df : bisection(f, l, df / 2, level, index_list, h);
389  }
390 
391  void correct_sol(Grid<3,T>& f, const Grid<3,T>& corr, const size_t level) override
392  {/* Method for correcting solution when going up,
393  check for unphysical values */
394 
395  const size_t Ntot = f.get_Ntot();
396  const T * const rho = this->get_external_field(level, 0);
397 
398  // bisection variables
399  const size_t N = this->get_N(level);
400  const T h = 1.0/T( N );
401  std::vector<size_t> index_list;
402 
403  #pragma omp parallel for private(index_list)
404  for(size_t i = 0; i < Ntot; i++)
405  {
406  // do not change values in screened regions
407  if (rho[i] == MARK_CHI_BOUND_COND) continue;
408  else if (std::abs(corr[i]/(f[i] - CHI_MIN)) < SWITCH_BIS_NEW) f[i] += corr[i];
409  else{
410  this->get_neighbor_gridindex(index_list, i, N);
411  T l = l_operator(level, index_list, true, h);
412  f[i] = bisection(f[i], l, corr[i] / 2, level, index_list, h);
413  }
414  }
415  }
416 
423  {
425  const double _rms_res_i = this->_rms_res_i;
426  const double _rms_res = this->_rms_res;
427  const double _rms_res_old = this->_rms_res_old;
428  const double _verbose = this->_verbose;
429  const double _istep_vcycle = this->_istep_vcycle;
430  const double _eps_converge = this->_eps_converge;
431  const double _maxsteps = this->_maxsteps;
432 
434  const double err = _rms_res_old != 0.0 ? _rms_res/_rms_res_old : 0.0;
435  ES converged = ES::ITERATE;
436 
438  bool res_below_optimal_tresh = (_rms_res < _eps_converge) || (_eps_converge == 0.0);
439  bool res_below_minimal_tresh = (_rms_res < m_rms_stop_min) || (m_rms_stop_min == 0.0);
440  bool fast_convergence = err < m_err_stop_min;
441  bool slow_convergence = err > m_err_stop;
442  bool worse_solution = err > 1;
443  bool over_max_steps = _istep_vcycle >= _maxsteps;
444 
446  auto print_success = [=](size_t m_conv_stop){
447  BOOST_LOG_TRIVIAL(debug) << "\n\tSUCCESS: res = " << _rms_res << ", err = " << err << ", num_err = " << m_conv_stop << " (istep = " << _istep_vcycle << ")";
448  };
449  auto print_failure = [=](size_t m_conv_stop){
450  BOOST_LOG_TRIVIAL(debug) << "\tFAILURE: res = " << _rms_res << ", err = " << err << ", num_err = " << m_conv_stop << " (istep = " << _istep_vcycle << ")";
451  };
452 
453  auto print_iterate = [=](size_t m_conv_stop){
454  BOOST_LOG_TRIVIAL(debug) << "\tITERATE: res = " << _rms_res << ", err = " << err << ", num_err = " << m_conv_stop << " (istep = " << _istep_vcycle << ")";
455  };
456 
458  if (_verbose){
459  BOOST_LOG_TRIVIAL(debug) << " Checking for convergence at step = " << _istep_vcycle;
460  BOOST_LOG_TRIVIAL(debug) << " Residual = " << _rms_res << " Residual_old = " << _rms_res_old;
461  BOOST_LOG_TRIVIAL(debug) << " Residual_i = " << _rms_res_i << " Err = " << err;
462  }
463 
465  if (res_below_optimal_tresh && !fast_convergence){
466  print_success(m_conv_stop);
467  converged = ES::SUCCESS;
468  }
469  else if (worse_solution)
470  {
471  if(_verbose) print_failure(m_conv_stop);
472  m_conv_stop++;
473  converged = ES::ITERATE;
474  }
475  else if (slow_convergence)
476  {
477  if (res_below_minimal_tresh)
478  {
479  print_success(m_conv_stop);
480  converged = ES::SLOW;
481  }
482  else
483  {
484  m_conv_stop++;
485  if (m_conv_stop >= m_num_fail)
486  {
487  print_failure(m_conv_stop);
488  converged = ES::FAILURE;
489  }
490  else
491  {
492  if(_verbose) print_iterate(m_conv_stop);
493  converged = ES::ITERATE;
494  }
495  }
496  }
497  else
498  {
499  if (m_conv_stop >= m_num_fail)
500  {
501  print_failure(m_conv_stop);
502  converged = ES::FAILURE;
503  }
504  else
505  {
506  if(_verbose) print_iterate(m_conv_stop);
507  converged = ES::ITERATE;
508  }
509  }
510 
511  if(over_max_steps)
512  {
513  print_failure(m_conv_stop);
514  converged = ES::MAX_STEPS;
515  }
516 
517  if (converged != ES::ITERATE)
518  {
519  m_conv_stop = 0;
520  }
521  return converged;
522  }
523 
524  void check_solution(size_t level, Grid<3,T>& chi) override
525  {
526  for (auto fv : fix_vals[level]) chi[fv.first] = fv.second;
527  }
528 
529  void set_convergence(double eps, double err_stop, double err_stop_min, double rms_stop_min, size_t num_fail)
530  {
531  this->set_epsilon(eps);
532  m_err_stop = err_stop;
533  m_err_stop_min = err_stop_min;
534  m_rms_stop_min = rms_stop_min;
535  m_num_fail = num_fail;
536  }
537 
538  void set_bisection_convergence(size_t max_bi_step, T dchi_stop, T l_stop)
539  {
540  m_max_bisection_steps = max_bi_step;
541  m_dchi_stop = dchi_stop;
542  m_l_stop = l_stop;
543  }
544 
546  {// set convergence -- compensate for units in which we compute laplace operator
547  const double err_mod = get_chi_prefactor();
548  set_convergence(err_mod*CONVERGENCE_RES, CONVERGENCE_ERR, CONVERGENCE_ERR_MIN, err_mod*CONVERGENCE_RES_MIN, CONVERGENCE_NUM_FAIL);
549  set_bisection_convergence(CONVERGENCE_BI_STEPS_INIT, CONVERGENCE_BI_DCHI, CONVERGENCE_BI_L);
550  }
551 
552  // set chameleon guess to bulk value, need to set time and add rho before call to this function
554  {
555  if (!chi_prefactor) throw std::out_of_range("invalid value of scale factor");
556  if (!this->get_external_field_size()) throw std::out_of_range("overdensity not set");
557  BOOST_LOG_TRIVIAL(debug) << "Setting initial guess for chameleon field...";
558 
559  T* const f = this->get_y(); // initial guess
560  T const* const rho = this->get_external_field(0, 0); // overdensity
561  const size_t N_tot = this->get_Ntot();
562 
563  #pragma omp parallel for
564  for (size_t i = 0; i < N_tot; ++i)
565  {
566  f[i] = chi_min(rho[i]);
567  }
568  }
569 
570  void set_linear_sol_at_level(Mesh& rho, const FFTW_PLAN_TYPE& p_F, const FFTW_PLAN_TYPE& p_B, size_t level)
571  {/* set chameleon guess to linear prediction at specific level */
572  // check we have the right grids
573  if (this->get_N(level) != rho.N) throw std::range_error("Mesh of a different size than Grid at current level!");
574 
575  // get delta(k)
576  fftw_execute_dft_r2c(p_F, rho);
577 
578  // get dchi(k)
579  get_chi_k(rho);
580 
581  // get dchi(x)
582  fftw_execute_dft_c2r(p_B, rho);
583 
584  // transform dchi into chi, in chi_a units this means only 'chi = 1 + dchi' */
585  rho += 1 + CHI_MIN;
586 
587  // copy dchi(x) onto Grid at level
588  transform_Mesh_to_Grid(rho, this->get_grid(level));
589  }
590 
591  void set_linear_recursively(size_t level)
592  {
593  // we are at the bottom level
594  if (level >= this->get_Nlevel()) return;
595 
596  // extract parameters
597  const size_t N = this->get_N(level);
598 
599  // create temporary Mesh to compute linear potential, copy density
600  Mesh rho(N);
601  transform_Grid_to_Mesh(rho, this->get_external_grid(level, 0));
602 
603  // initialize FFTW
604  const FFTW_PLAN_TYPE p_F = FFTW_PLAN_R2C(N, N, N, rho.real(), rho.complex(), FFTW_ESTIMATE);
605  const FFTW_PLAN_TYPE p_B = FFTW_PLAN_C2R(N, N, N, rho.complex(), rho.real(), FFTW_ESTIMATE);
606 
607  // set linear prediction
608  set_linear_sol_at_level(rho, p_F, p_B, level);
609 
610  // solve linear prediction for the next level
611  set_linear_recursively(level + 1);
612  }
613 
614  void set_linear(Mesh& rho, const FFTW_PLAN_TYPE& p_F, const FFTW_PLAN_TYPE& p_B)
615  {/* set chameleon guess to linear prediction */
616 
617  // solve level = 0, use already allocated space and created plans
618  set_linear_sol_at_level(rho, p_F, p_B, 0);
619 
620  // recursively solve at level > 0, create new grids and plans (small)
621  set_linear_recursively(1);
622  }
623 
624  void set_screened(size_t level = 0)
625  {/* check solution for invalid values (non-linear regime), fix values in high density regions, try to improve guess in others */
626  if (level >= this->get_Nlevel()) return;
627 
628  Grid<3, T>& chi = this->get_grid(level); // guess
629  const size_t N_tot = this->get_Ntot(level);
630  const size_t N = this->get_N(level);
631  T* const rho_grid = this->get_external_field(level, 0); // overdensity
632  std::vector<size_t> index_list;
633 
634  #pragma omp parallel for private(index_list)
635  for (size_t i = 0; i < N_tot; ++i)
636  {
637  if (chi[i] <= CHI_MIN) // non-linear regime
638  {
639  if (check_surr_dens(rho_grid, index_list, i, N)) chi[i] = CHI_MIN;
640  else chi[i] = rho_grid[i] > 0 ? chi_min(rho_grid[i]) : 1/2. + CHI_MIN;//< phi_s / 2 in underdense region as starting point
641  }
642  }
643 
644  fix_vals[level].clear();
645 
646  // writing into map, do not use omp
647  for (size_t i = 0; i < N_tot; ++i)
648  {
649  if (chi[i] == CHI_MIN) // fix chameleon to bulk value, set unphysical density to indicate screened regime
650  {
651 
652  chi[i] = chi_min(rho_grid[i]);
653  rho_grid[i] = MARK_CHI_BOUND_COND;
654  fix_vals[level].emplace(i, chi[i]);
655  }
656  }
657 
658  size_t num_high_density = fix_vals[level].size();
659  BOOST_LOG_TRIVIAL(debug) << "Identified and fixed " << num_high_density << "(" << std::setprecision(2) << num_high_density*100.0/N_tot << "%) points at level " << level;
660 
661  set_screened(level + 1);
662  }
663 
664  T chi_min(T delta) const
665  {/* get chi_bulk for given overdensity */
666  if (delta > -1) return std::pow(1+delta, 1/(n-1)) + CHI_MIN;
667  else if (delta == -1) return 1 + CHI_MIN;
668  else throw std::range_error("invalid values of density: " + std::to_string(delta) + " < -1)");
669  }
670 
671 }; // class ChiSolver end
672 }
673 
674 /****************************/
684 {
685 public:
686  // CONSTRUCTOR
687  ChiImpl(const Sim_Param &sim):
688  sol(sim.box_opt.mesh_num, sim, false), drho(sim.box_opt.mesh_num), N_level_orig(sol.get_Nlevel()), x_0(sim.x_0())
689  {
690  // EFFICIENTLY ALLOCATE VECTOR OF MESHES
691  chi_force.reserve(3);
692  for(size_t i = 0; i < 3; i++){
693  chi_force.emplace_back(sim.box_opt.mesh_num);
694  }
695 
696  // ALLOCATED MEMORY
697  memory_alloc = sizeof(FTYPE_t)*chi_force[0].length*chi_force.size();
698  memory_alloc += sizeof(CHI_PREC_t)*8*(sol.get_Ntot()-1)/7 // MultiGrid<3, CHI_PREC_t>
699  *3; // _f, _res, _source
700  memory_alloc += sizeof(CHI_PREC_t)*8*(sol.get_Ntot()-1)/7;// MultiGrid<3, CHI_PREC_t> drho
701 
702  // SET CHI SOLVER
703  sol.add_external_grid(&drho);
704  sol.set_bisection_convergence(CONVERGENCE_BI_STEPS_INIT, CONVERGENCE_BI_DCHI, CONVERGENCE_BI_L);
705  }
706 
707  // VARIABLES
708  ChiSolver<CHI_PREC_t> sol;
710  std::vector<Mesh> chi_force;
711  uint64_t memory_alloc;
712 
713  // METHODS
714  void solve(FTYPE_t a, const std::vector<Particle_v<FTYPE_t>>& particles, const Sim_Param &sim, const FFTW_PLAN_TYPE& p_F, const FFTW_PLAN_TYPE& p_B)
715  {
717  sol.set_time(a, sim.cosmo);
718 
720  sol.set_def_convergence();
721 
723  BOOST_LOG_TRIVIAL(debug) << "Storing density distribution...";
724  get_rho_from_par(particles, chi_force[0], sim);
725  transform_Mesh_to_MultiGrid(chi_force[0], drho);
726 
728  BOOST_LOG_TRIVIAL(debug) << "Setting linear guess for chameleon field...";
729  if (sim.chi_opt.linear)
730  {
732  sol.set_linear_sol_at_level(chi_force[0], p_F, p_B, 0);
733  }
734  else
735  {
737  sol.set_linear(chi_force[0], p_F, p_B);
738  sol.set_screened();
739 
741  BOOST_LOG_TRIVIAL(debug) << "Solving equations of motion for chameleon field...";
742  solve_multigrid();
743  solve_finest();
744  }
745  }
746 
747  void gen_pow_spec_binned(const Sim_Param &sim, Data_Vec<FTYPE_t,2>& pwr_spec_binned, const FFTW_PLAN_TYPE& p_F)
748  {
749  transform_MultiGridSolver_to_Mesh(chi_force[0], sol); // - get solution
750  fftw_execute_dft_r2c(p_F, chi_force[0]); // - get chi(k)
751  pwr_spec_k_init(chi_force[0], chi_force[0]); // - get chi(k)^2, NO w_k correction
752  ::gen_pow_spec_binned(sim, chi_force[0], pwr_spec_binned); // - get average Pk
753  }
754 
755  void get_chi_force(const FFTW_PLAN_TYPE& p_F, const FFTW_PLAN_TYPE& p_B)
756  {
757  transform_MultiGridSolver_to_Mesh(chi_force[0], sol); // - get solution
758  fftw_execute_dft_r2c(p_F, chi_force[0]); // - get chi(k)
759  gen_displ_k_cic(chi_force, chi_force[0]); // - get -k*chi(k)
760  fftw_execute_dft_c2r_triple(p_B, chi_force);// - get chi force
761  }
762 
763  void kick_step_w_chi(const Cosmo_Param &cosmo, const FTYPE_t a, const FTYPE_t da, std::vector<Particle_v<FTYPE_t>>& particles, const std::vector< Mesh> &force_field)
764  {
765  const size_t Np = particles.size();
766  Vec_3D<FTYPE_t> force;
767  const FTYPE_t D = growth_factor(a, cosmo);
768  const FTYPE_t OL = cosmo.Omega_L()*pow(a,3);
769  const FTYPE_t Om = cosmo.Omega_m;
770  const FTYPE_t OLa = OL/(Om+OL);
771 
773  const FTYPE_t f1 = 3/(2*a)*(1 + OLa);
774  const FTYPE_t f2 = 3/(2*a)*(1 - OLa)*D/a;
776  const FTYPE_t f3 = a/D*sol.chi_force_units(a)/pow2(x_0);
777 
778  #pragma omp parallel for private(force)
779  for (size_t i = 0; i < Np; i++)
780  {
781  force.fill(0.);
782  assign_from(force_field, particles[i].position, force);
783  assign_from(chi_force, particles[i].position, force, f3);
784  force = force*f2 - particles[i].velocity*f1;
785  particles[i].velocity += force*da;
786  }
787  }
788 
789  void kick_step_w_chi_no_momentum(const Cosmo_Param &cosmo, const FTYPE_t a, const FTYPE_t da, std::vector<Particle_v<FTYPE_t>>& particles, const std::vector< Mesh> &vel_field)
790  {
791  const size_t Np = particles.size();
792  Vec_3D<FTYPE_t> vel;
793  const FTYPE_t D = growth_factor(a, cosmo);
794  const FTYPE_t dDda = growth_change(a, cosmo); // dD / da
795 
797  const FTYPE_t f3 = a/D*sol.chi_force_units(a)/pow2(x_0);
798 
799  #pragma omp parallel for private(vel)
800  for (size_t i = 0; i < Np; i++)
801  {
802  vel.fill(0.);
803  assign_from(vel_field, particles[i].position, vel);
804  assign_from(chi_force, particles[i].position, vel, f3);
805  particles[i].velocity = vel*dDda;
806  }
807  }
808 
809 private:
810  const size_t N_level_orig;
811  const FTYPE_t x_0;
812 
814  {
815  sol.set_ngs_sweeps(3, 6);
816  sol.set_maxsteps(30);
817  ES status = sol.solve();
818  }
819 
821  {
822  sol.set_ngs_sweeps(5, 0);
823  sol.set_Nlevel(1);
824  sol.set_maxsteps(30);
825  ES status = sol.solve();
826  sol.set_Nlevel(N_level_orig);
827  }
828 };
829 
830 App_Var_Chi::App_Var_Chi(const Sim_Param &sim, const std::string& app_short, const std::string& app_long):
831  App_Var<Particle_v<FTYPE_t>>(sim, app_short, app_long), m_impl(new ChiImpl(sim))
832 {
833  memory_alloc += m_impl->memory_alloc;
834 }
835 
837  App_Var_Chi(sim, "CHI", "Chameleon gravity (FP)") {}
838 
839 App_Var_Chi::~App_Var_Chi() = default;
840 
842 {
843  /* Print standard output */
845 
846  /* Chameleon power spectrum */
847  if (sim.out_opt.print_pwr)
848  {
849  m_impl->solve(a(), particles, sim, p_F, p_B); // get solution for current time
850  m_impl->gen_pow_spec_binned(sim, pwr_spec_binned, p_F); // get chameleon power spectrum
851  print_pow_spec(pwr_spec_binned, get_out_dir(), "_chi" + get_z_suffix()); // print
852  }
853 }
854 
856 {// Leapfrog method for chameleon gravity (frozen-potential)
857  auto kick_step = [&]()
858  {
859  m_impl->solve(a_half(), particles, sim, p_F, p_B);
860  m_impl->get_chi_force(p_F, p_B);
861  m_impl->kick_step_w_chi(sim.cosmo, a_half(), da(), particles, app_field);
862  };
864 }
865 
867  App_Var_Chi(sim, "CHI_FF", "Chameleon gravity (FF)") {}
868 
870 {// Leapfrog method for chameleon gravity (frozen-flow)
871  auto kick_step = [&]()
872  {
873  m_impl->solve(a_half(), particles, sim, p_F, p_B);
874  m_impl->get_chi_force(p_F, p_B);
875  m_impl->kick_step_w_chi_no_momentum(sim.cosmo, a_half(), da(), particles, app_field);
876  };
878 }
879 
double min(const MultiGrid< 3, T > &grid)
Definition: chameleon.cpp:148
Chi_Opt chi_opt
Definition: params.hpp:210
class containing core variables and methods for approximations
Definition: app_var.hpp:41
bool print_pwr
Definition: params.hpp:87
constexpr CHI_PREC_t CHI_MIN
minimum value of chameleon field, in chi_a units it is &#39;0&#39;, in phi units it is &#39;-1&#39; ...
Definition: chameleon.cpp:66
T dl_operator(const size_t level, const std::vector< size_t > &index_list, const T h) const override
Definition: chameleon.cpp:293
Grid< NDIM, T > & get_grid(size_t level=0)
FFTW_COMPLEX_TYPE * complex()
get fftw_complex pointer to data
Definition: class_mesh.hpp:111
MultiGrid< 3, CHI_PREC_t > drho
Definition: chameleon.cpp:709
void correct_sol(Grid< 3, T > &f, const Grid< 3, T > &corr, const size_t level) override
Definition: chameleon.cpp:391
constexpr size_t CONVERGENCE_BI_STEPS_INIT
maximal number of steps inside bisection initialization method
Definition: chameleon.cpp:78
void transform_MultiGridSolver_to_Mesh(Mesh &mesh, const MultiGridSolver< 3, T > &sol)
Definition: chameleon.cpp:137
T l_operator(const T chi_i, const size_t level, const std::vector< size_t > &index_list, const bool addsource, const T h) const
Definition: chameleon.cpp:258
void kick_step_w_chi_no_momentum(const Cosmo_Param &cosmo, const double a, const double da, std::vector< Particle_v< double >> &particles, const std::vector< Mesh > &vel_field)
Definition: chameleon.cpp:789
void get_rho_from_par(const std::vector< T > &particles, Mesh &rho, const Sim_Param &sim)
Definition: core_app.cpp:278
functions handling output of the program
void transform_MultiGrid_to_Mesh(Mesh &mesh, const MultiGrid< 3, T > &mltgrid)
Definition: chameleon.cpp:134
double get_k_sq(size_t N, size_t index)
Definition: core_mesh.cpp:38
void restrict_down_all()
Definition: multigrid.cpp:209
void stream_kick_stream(const double da, std::vector< Particle_v< double >> &particles, std::function< void()> kick_step, size_t per)
Definition: integration.cpp:24
double CHI_PREC_t
accuracy of chameleon solver
Definition: chameleon.cpp:31
class handling particles (position only)
Box_Opt box_opt
Definition: params.hpp:202
handle cosmological functions like power spectrum, growth, etc.
constexpr double MPL
mass & chi in units of Planck mass
Definition: chameleon.cpp:38
T * real()
Definition: class_mesh.hpp:36
: class storing simulation parameters
Definition: params.hpp:193
bool check_bisection_convergence(const T df_new, const T l_new) const
Definition: chameleon.cpp:328
void solve(double a, const std::vector< Particle_v< double >> &particles, const Sim_Param &sim, const FFTW_PLAN_TYPE &p_F, const FFTW_PLAN_TYPE &p_B)
Definition: chameleon.cpp:714
void fftw_execute_dft_r2c(const FFTW_PLAN_TYPE &p_F, Mesh &rho)
compute forward (real to complex) FFT on mesh (inplace)
Definition: core_mesh.cpp:247
const T chi_prefactor_0
dimensionless prefactor to poisson equation at a = 1
Definition: chameleon.cpp:164
T bisection(T f1, T l1, const T df, const size_t level, const std::vector< size_t > &index_list, const T h) const
Definition: chameleon.cpp:356
: creates a mesh of N*N*(N+2) cells
Definition: class_mesh.hpp:95
void fftw_execute_dft_c2r_triple(const FFTW_PLAN_TYPE &p_B, std::vector< Mesh > &rho)
compute three backward (complex to real) FFTs on vector of meshes (inplace)
Definition: core_mesh.cpp:263
ChiSolver(size_t N, unsigned int Nmin, const Sim_Param &sim, bool verbose=true)
Definition: chameleon.cpp:183
void upd_pos() override
Definition: chameleon.cpp:869
Definition: grid.h:21
void print_pow_spec(const Data_Vec< double, 2 > &pwr_spec_binned, std::string out_dir, std::string suffix)
Definition: core_out.cpp:134
bool find_opposite_l_sign(const T f1, const T l1, T df, T &f2, T &l2, const size_t level, const std::vector< size_t > &index_list, const T h) const
Definition: chameleon.cpp:307
void transform_Grid_to_Mesh(Mesh &mesh, const Grid< 3, T > &grid)
Definition: chameleon.cpp:115
void kick_step_w_chi(const Cosmo_Param &cosmo, const double a, const double da, std::vector< Particle_v< double >> &particles, const std::vector< Mesh > &force_field)
Definition: chameleon.cpp:763
T pow2(T base)
Definition: precision.hpp:52
std::vector< T > data
Definition: class_mesh.hpp:33
void set_time(T a, const Cosmo_Param &cosmo)
Definition: chameleon.cpp:250
constexpr double CONVERGENCE_ERR_MIN
do not stop if solution is still improving
Definition: chameleon.cpp:75
bool linear
Definition: params.hpp:186
ES check_convergence() override
check if solution already converged
Definition: chameleon.cpp:422
functions for integration of particle trajectories
int iz
Definition: mfunc_bm.py:17
size_t get_N() const
Definition: grid.cpp:135
#define FFTW_PLAN_C2R
Definition: precision.hpp:30
void fftw_execute_dft_c2r(const FFTW_PLAN_TYPE &p_B, Mesh &rho)
compute backward (complex to real) FFT on mesh (inplace)
Definition: core_mesh.cpp:253
: class to solve chameleon equations of motion
Definition: chameleon.cpp:156
void set_bisection_convergence(size_t max_bi_step, T dchi_stop, T l_stop)
Definition: chameleon.cpp:538
const std::unique_ptr< ChiImpl > m_impl
Definition: chameleon.hpp:44
void transform_Mesh_to_MultiGrid(const Mesh &mesh, MultiGrid< 3, T > &mltgrid)
Definition: chameleon.cpp:108
std::vector< Mesh > app_field
Definition: app_var.hpp:63
Data_Vec< double, 2 > pwr_spec_binned
Definition: app_var.hpp:68
< end of anonymous namespace (private definitions)
Definition: chameleon.hpp:35
const T beta
Chameleon coupling constant.
Definition: chameleon.cpp:161
void gen_pow_spec_binned(const Sim_Param &sim, const Mesh &power_aux, Data_Vec< double, 2 > &pwr_spec_binned)
Definition: core_app.cpp:460
size_t get_Ntot() const
Definition: grid.cpp:141
ChiSolver(size_t N, const Sim_Param &sim, bool verbose=true)
Definition: chameleon.cpp:198
declaration in params.hpp
Definition: core_power.h:19
double Omega_L() const
Definition: params.hpp:38
cosmological & CCL parameters
Definition: params.hpp:22
constexpr size_t CONVERGENCE_NUM_FAIL
stop when number of failed steps is over
Definition: chameleon.cpp:76
Cosmo_Param cosmo
Definition: params.hpp:206
std::string get_z_suffix() const
const std::vector< T > & get_vec() const
Definition: grid.cpp:51
T l_operator(const size_t level, const std::vector< size_t > &index_list, const bool addsource, const T h) const override
Definition: chameleon.cpp:283
T upd_operator(const T f, const size_t level, const std::vector< size_t > &index_list, const T h) const override
Definition: chameleon.cpp:377
interface for common functions for all types of approximations
void get_neighbor_gridindex(std::vector< size_t > &index_list, size_t i, size_t N)
constexpr CHI_PREC_t MARK_CHI_BOUND_COND
unphysical value of overdensity (< -1) used to indicate that chameleon filed at this point should not...
Definition: chameleon.cpp:52
std::vector< Mesh > chi_force
Definition: chameleon.cpp:710
std::string get_out_dir() const
void set_convergence(double eps, double err_stop, double err_stop_min, double rms_stop_min, size_t num_fail)
Definition: chameleon.cpp:529
Out_Opt out_opt
Definition: params.hpp:204
constexpr size_t CONVERGENCE_BI_STEPS
maximal number of steps inside bisection rootfindg method
Definition: chameleon.cpp:77
constexpr double CONVERGENCE_RES
stop when total (rms) residual below
Definition: chameleon.cpp:72
ChiSolver< CHI_PREC_t > sol
Definition: chameleon.cpp:708
App_Var_Chi_FF(const Sim_Param &sim)
Definition: chameleon.cpp:866
constexpr double CONVERGENCE_ERR
stop when improvements between steps slow below
Definition: chameleon.cpp:74
constexpr CHI_PREC_t CONVERGENCE_BI_L
stop bisection method when residual below
Definition: chameleon.cpp:80
various simulation parameters
std::vector< Particle_v< double > > particles
Definition: app_var.hpp:65
std::vector< std::map< size_t, T > > fix_vals
<index, value>
Definition: chameleon.cpp:180
void set_linear_sol_at_level(Mesh &rho, const FFTW_PLAN_TYPE &p_F, const FFTW_PLAN_TYPE &p_B, size_t level)
Definition: chameleon.cpp:570
size_t N
Definition: class_mesh.hpp:102
constexpr double PI
Definition: precision.hpp:37
#define FFTW_PLAN_TYPE
Definition: precision.hpp:26
constexpr CHI_PREC_t SWITCH_BIS_NEW
when the relative change in solution is less than this, use Newton`s method. Bisection method otherwi...
Definition: chameleon.cpp:59
T bisection_step(T &f1, T &l1, T &f2, T &l2, const size_t level, const std::vector< size_t > &index_list, const T h) const
Definition: chameleon.cpp:333
size_t length
Definition: class_mesh.hpp:32
bool check_surr_dens(T const *const rho_grid, std::vector< size_t > index_list, size_t i, size_t N)
Definition: chameleon.cpp:237
basic functions to work with mesh
constexpr double c_kms
speed of light [km / s]
Definition: chameleon.cpp:45
void print_output() override
Definition: chameleon.cpp:841
const T phi_prefactor
prefactor for Poisson equation for gravitational potential
Definition: chameleon.cpp:163
double Omega_m
Definition: params.hpp:36
float pow(float base, unsigned long int exp)
Definition: precision.hpp:39
void pwr_spec_k_init(const Mesh &rho_k, Mesh &power_aux)
Definition: core_app.cpp:349
App_Var_Chi(const Sim_Param &sim)
Definition: chameleon.cpp:836
constexpr CHI_PREC_t CONVERGENCE_BI_DCHI
stop bisection method when chi doesn`t chanege
Definition: chameleon.cpp:79
T chi_prefactor
time-dependent prefactor
Definition: chameleon.cpp:165
void check_solution(size_t level, Grid< 3, T > &chi) override
Definition: chameleon.cpp:524
ChiImpl(const Sim_Param &sim)
Definition: chameleon.cpp:687
Grid< NDIM, T > & get_grid(size_t level=0)
Definition: multigrid.cpp:17
size_t mesh_num
Definition: params.hpp:58
void transform_Mesh_to_Grid(const Mesh &mesh, Grid< 3, T > &grid)
Definition: chameleon.cpp:89
const double x_0
Definition: chameleon.cpp:811
void get_chi_force(const FFTW_PLAN_TYPE &p_F, const FFTW_PLAN_TYPE &p_B)
Definition: chameleon.cpp:755
MultiGridSolver< 3, CHI_PREC_t >::Exit_Status ES
acces Exit_Status namespace
Definition: chameleon.cpp:86
void gen_displ_k_cic(std::vector< Mesh > &vel_field, const Mesh &pot_k)
Definition: core_app.cpp:625
void upd_pos() override
Definition: chameleon.cpp:855
constexpr double CONVERGENCE_RES_MIN
do not stop if solution didn`t converge below
Definition: chameleon.cpp:73
void assign_from(const Mesh &field, const Vec_3D< double > &position, double &value, double mod)
Definition: core_mesh.cpp:224
void gen_pow_spec_binned(const Sim_Param &sim, Data_Vec< double, 2 > &pwr_spec_binned, const FFTW_PLAN_TYPE &p_F)
Definition: chameleon.cpp:747
chameleon model of gravity interface
void set_linear(Mesh &rho, const FFTW_PLAN_TYPE &p_F, const FFTW_PLAN_TYPE &p_B)
Definition: chameleon.cpp:614
const size_t N_level_orig
Definition: chameleon.cpp:810
double growth_change(double a, const Cosmo_Param &cosmo)
Definition: core_power.cpp:447
#define FFTW_PLAN_R2C
Definition: precision.hpp:29