Fast Methods for Cosmological Simulations
FastSim serves as a tool for quick N-body simulations in modified gravity.
poisson.cpp
Go to the documentation of this file.
1 #include <complex>
2 #include <fstream>
3 #include <iostream>
4 #include <fftw3.h>
5 #include "poisson_solver.h"
6 
7 //===================================================
8 //
9 // Example of how to use the multigrid solver
10 // We solve the Poisson equation
11 //
12 // D^2 u = S
13 //
14 // for some source (set to give a desired solution)
15 //
16 // Select NDIM and DType above and define the two
17 // functions f_analytical and laplacian_f_analytical
18 //
19 //===================================================
20 
21 //==========================
22 // Dimension of the equation
23 //==========================
24 #define _NDIM 1
25 
26 //=======================================================
27 // The type of the equation (double,float,complex,...)
28 //=======================================================
29 typedef std::complex<double> DType;
30 
31 //==========================
32 // Methods below
33 //==========================
34 void solve_with_fft(Grid<_NDIM, std::complex<double> >& drho, Grid<_NDIM, std::complex<double> > &sol);
36 void set_rho( MultiGrid<_NDIM, DType> &rho_mg, Grid<_NDIM, DType> &analytical_solution);
37 DType f_analytical(double x);
39 
40 //============================================================
41 // The analytical solution we try to recover
42 //============================================================
43 
44 std::complex<double> f_analytical(double x){
45  const double pi = std::acos(-1);
46  const std::complex<double> I(0, 1);
47  return std::exp(2.0 * pi * I * x);
48 }
49 
50 std::complex<double> laplacian_f_analytical(double x){
51  const double pi = std::acos(-1);
52  const std::complex<double> I(0, 1);
53  return - 4.0 * pi * pi * std::exp(2.0 * pi * I * x);
54 }
55 
56 //============================================================
57 // Set source rho in D^2 phi = rho analytically
58 //============================================================
59 
60 void set_rho( MultiGrid<_NDIM, DType> &rho_mg, Grid<_NDIM, DType> &analytical_solution){
61  int N = rho_mg.get_N();
62  int Ntot = rho_mg.get_Ntot();
63  DType *rho = rho_mg.get_y(0);
64  DType *yanal = analytical_solution.get_y();
65 
66  DType rhomean = 0.0;
67  for(int i = 0; i < Ntot; i++){
68  double xx = ((i % N) + 0.5) / double(N);
69 
70  // Set source rho such that solution to D^2 phi = rho is phi = e^{ 2 pi i x}
71  rho[i] = laplacian_f_analytical(xx);
72  yanal[i] = f_analytical(xx);
73 
74  // Box average of rho
75  rhomean += rho[i];
76  }
77  rhomean = rhomean/double(Ntot);
78  std::cout << "set_rho :: Box average of source |<rho>| = " << std::sqrt(std::norm(rhomean)) << std::endl;
79 
80  // Restrict down source to all levels
81  rho_mg.restrict_down_all();
82 }
83 
84 //============================================================
85 // Solve the equation D^2 phi = drho using FFTW
86 //============================================================
87 
89  int Ntot = drho.get_Ntot();
90  Grid<_NDIM, std::complex<double> > drho_complex (Ntot);
91  Grid<_NDIM, std::complex<double> > sol_complex (Ntot);
92  for(int i = 0; i < Ntot; i++){
93  drho_complex[i] = drho[i];
94  sol_complex[i] = drho[i];
95  }
96  solve_with_fft(drho_complex, sol_complex);
97  for(int i = 0; i < Ntot; i++){
98  sol[i] = sol_complex[i].real();
99  }
100 }
101 
102 void solve_with_fft(Grid<_NDIM, std::complex<double> >& drho, Grid<_NDIM, std::complex<double> > &sol){
103  int N = drho.get_N(), Nover2 = N/2, Ntot = drho.get_Ntot();
104 
105  fftw_complex *in = reinterpret_cast<fftw_complex*>(drho.get_y());
106  fftw_complex *out = reinterpret_cast<fftw_complex*>(sol.get_y());
107 
108 #if _NDIM == 1
109  fftw_plan fwd = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
110  fftw_plan bwd = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
111 #elif _NDIM == 2
112  fftw_plan fwd = fftw_plan_dft_2d(N, N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
113  fftw_plan bwd = fftw_plan_dft_2d(N, N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
114 #elif _NDIM == 3
115  fftw_plan fwd = fftw_plan_dft_3d(N, N, N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
116  fftw_plan bwd = fftw_plan_dft_3d(N, N, N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
117 #else
118  std::vector<int> inds(_NDIM, N);
119  fftw_plan fwd = fftw_plan_dft(_NDIM, &inds[0], in, out, FFTW_FORWARD, FFTW_ESTIMATE);
120  fftw_plan bwd = fftw_plan_dft(_NDIM, &inds[0], out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
121 #endif
122 
123  // Transform
124  fftw_execute(fwd);
125 
126  // Divide by laplacian and normalize
127  double fac = -1.0 / ( 4.0 * std::acos(-1) * std::acos(-1) * double(Ntot) );
128  for(int i = 0; i < Ntot; i++){
129  double k2 = 0.0;
130  int ind = 0;
131  for(int j = 0, Npow = 1; j < _NDIM; j++, Npow *= N){
132  int coord = i / Npow % N;
133  int kval = coord < Nover2 ? coord : coord - N;
134  k2 += kval * kval;
135  ind += coord * Npow;
136  }
137  if(i > 0){
138  double facnow = fac/k2;
139  out[ind][0] *= facnow;
140  out[ind][1] *= facnow;
141  } else {
142  out[0][0] = 0.0;
143  out[0][1] = 0.0;
144  }
145  }
146 
147  // Transform back
148  fftw_execute(bwd);
149 }
150 
151 int main(int argv, char ** argc){
152 
153  int NgridPerDim = 256;
154  bool verbose = true;
155  MultiGrid<_NDIM, DType> rho(NgridPerDim);
156  Grid<_NDIM, DType> analytical_solution(NgridPerDim);
157 
158  // Make density grid
159  set_rho(rho, analytical_solution);
160 
161  // Set up solver
162  PoissonSolver<_NDIM, DType> sol(NgridPerDim, verbose);
163 
164  // Add pointer to the source grid to the solver
165  sol.add_external_grid(&rho);
166 
167  // Set options
168  sol.set_epsilon(1e-10);
169  sol.set_ngs_sweeps(2,2);
170 
171  // Solve the equation
172  sol.solve();
173 
174  // Copy over solution
175  Grid<_NDIM, DType> solution = sol.get_grid();
176 
177  // Compute the fractional error wrt the analytical solution
178  Grid<_NDIM, DType> err = (solution - analytical_solution) / (analytical_solution) * std::complex<double>(100.0);
179  std::cout << "The rms error in the solution: " << err.rms_norm() << " %" << std::endl;
180 
181  Grid<_NDIM,DType> fft_sol = rho.get_grid(0);
182  solve_with_fft(rho.get_grid(0), fft_sol);
183 
184  // Output solution(s)
185  std::ofstream fp("test_poisson_solver_complex.txt");
186  int N = sol.get_N(0);
187  for(int i = 0; i < N; i++){
188  fp << (i+0.5)/double(N) << " " << solution[i].real() << " " << fft_sol[i].real() << " " << analytical_solution[i].real() << std::endl;
189  }
190 
191  // Clean up all memory
192  sol.clear();
193 }
194 
Grid< NDIM, T > & get_grid(size_t level=0)
double double
Definition: precision.hpp:19
void set_ngs_sweeps(size_t ngs_fine, size_t ngs_coarse)
void restrict_down_all()
Definition: multigrid.cpp:209
size_t get_Ntot(size_t level=0) const
Definition: multigrid.cpp:98
void solve_with_fft(Grid< 1, std::complex< double > > &drho, Grid< 1, std::complex< double > > &sol)
Definition: poisson.cpp:102
void set_epsilon(double eps_converge)
Exit_Status solve()
size_t get_N(size_t level=0) const
Definition: grid.h:21
size_t get_N(size_t level=0) const
Definition: multigrid.cpp:90
void set_rho(MultiGrid< 1, DType > &rho_mg, Grid< 1, DType > &analytical_solution)
Definition: poisson.cpp:60
T * get_y()
Definition: grid.cpp:41
std::ostream & cout()
size_t get_Ntot() const
Definition: grid.cpp:141
Grid< NDIM, T > sqrt(Grid< NDIM, T > lhs)
Definition: grid.h:231
#define _NDIM
Definition: poisson.cpp:24
static CCL_BEGIN_DECLS double x[111][8]
T * get_y(size_t level=0)
Definition: multigrid.cpp:49
DType laplacian_f_analytical(double x)
Definition: poisson.cpp:50
Grid< NDIM, T > & get_grid(size_t level=0)
Definition: multigrid.cpp:17
void add_external_grid(MultiGrid< NDIM, T > *field)
double rms_norm()
Definition: grid.h:238
int main(int argv, char **argc)
Definition: poisson.cpp:151
DType f_analytical(double x)
Definition: poisson.cpp:44
std::complex< double > DType
Definition: poisson.cpp:29