MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
glass.c
Go to the documentation of this file.
1 #include <mpi.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <string.h>
5 #include <omp.h>
6 
7 #include <gsl/gsl_rng.h>
8 
9 #include "allvars.h"
10 #include "proto.h"
11 
12 #include <libgadget/petapm.h>
13 #include <libgadget/walltime.h>
14 #include <libgadget/utils.h>
16 #include <libgadget/gravity.h>
17 
18 static void potential_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex * value);
19 static void force_x_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex * value);
20 static void force_y_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex * value);
21 static void force_z_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex * value);
22 static void readout_force_x(PetaPM *pm, int i, double * mesh, double weight);
23 static void readout_force_y(PetaPM *pm, int i, double * mesh, double weight);
24 static void readout_force_z(PetaPM *pm, int i, double * mesh, double weight);
26 {
27  {"ForceX", force_x_transfer, readout_force_x},
28  {"ForceY", force_y_transfer, readout_force_y},
29  {"ForceZ", force_z_transfer, readout_force_z},
30  {NULL, NULL, NULL},
31 };
32 
34 
35 static PetaPMRegion * _prepare(PetaPM * pm, PetaPMParticleStruct * pstruct, void * userdata, int * Nregions);
36 
37 static void glass_force(PetaPM * pm, double t_f, struct ic_part_data * ICP, const int NumPart);
38 static void glass_stats(struct ic_part_data * ICP, int NumPart);
39 
40 int
41 setup_glass(IDGenerator * idgen, PetaPM * pm, double shift, int seed, double mass, struct ic_part_data * ICP, const double UnitLength_in_cm, const char * OutputDir)
42 {
43  gsl_rng * rng = gsl_rng_alloc(gsl_rng_ranlxd1);
44  int ThisTask;
45  MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
46  gsl_rng_set(rng, seed + ThisTask);
47  memset(ICP, 0, idgen->NumPart*sizeof(struct ic_part_data));
48 
49  int i;
50  /* Note: this loop should nto be omp because
51  * of the call to gsl_rng_uniform*/
52  for(i = 0; i < idgen->NumPart; i ++) {
53  int k;
54  idgen_create_pos_from_index(idgen, i, &ICP[i].Pos[0]);
55  /* a spread of 3 will kill most of the grid anisotropy structure;
56  * and still being local */
57  for(k = 0; k < 3; k++) {
58  double rand = idgen->BoxSize / idgen->Ngrid * 3 * (gsl_rng_uniform(rng) - 0.5);
59  ICP[i].Pos[k] += shift + rand;
60  }
61  ICP[i].Mass = mass;
62  }
63 
64  gsl_rng_free(rng);
65 
66  char * fn = fastpm_strdup_printf("powerspectrum-glass-%08X", seed);
67  glass_evolve(pm, 14, fn, ICP, idgen->NumPart, UnitLength_in_cm, OutputDir);
68  myfree(fn);
69 
70  return idgen->NumPart;
71 }
72 
73 void glass_evolve(PetaPM * pm, int nsteps, const char * pkoutname, struct ic_part_data * ICP, const int NumPart, const double UnitLength_in_cm, const char * OutputDir)
74 {
75  int i;
76  int step = 0;
77  double t_x = 0;
78  double t_v = 0;
79  double t_f = 0;
80 
81  /*Allocate memory for a power spectrum*/
82  powerspectrum_alloc(pm->ps, pm->Nmesh, omp_get_max_threads(), 0, pm->BoxSize*UnitLength_in_cm);
83 
84  glass_force(pm, t_x, ICP, NumPart);
85 
86  /* Our pick of the units ensures there is an oscillation period of 2 * M_PI.
87  *
88  * (don't ask me how this worked -- I think I failed this problem in physics undergrad. )
89  * We use 4 steps per oscillation, and end at
90  *
91  * 12 + 1 = 13, the first time phase is M_PI / 2, a close encounter to the minimum.
92  *
93  * */
94  for(step = 0; step < nsteps; step++) {
95  /* leap-frog, K D D F K */
96  double dt = M_PI / 2; /* step size */
97  double hdt = 0.5 * dt; /* half a step */
98  int d;
99  /*
100  * Use inverted gravity with a damping term proportional to the velocity.
101  *
102  * The magic setup was studied in
103  * https://github.com/rainwoodman/fastpm-python/blob/1be020b/Example/Glass.ipynb
104  * */
105 
106  /* Kick */
107  for(i = 0; i < NumPart; i ++) {
108  for(d = 0; d < 3; d ++) {
109  /* mind the damping term */
110  ICP[i].Vel[d] += (ICP[i].Disp[d] - ICP[i].Vel[d]) * hdt;
111  }
112  }
113  t_x += hdt;
114 
115  /* Drift */
116  for(i = 0; i < NumPart; i ++) {
117  for(d = 0; d < 3; d ++) {
118  ICP[i].Pos[d] += ICP[i].Vel[d] * dt;
119  }
120  }
121  t_v += dt;
122 
123  glass_force(pm, t_x, ICP, NumPart);
124  t_f = t_x;
125 
126  /* Kick */
127  for(i = 0; i < NumPart; i ++) {
128  for(d = 0; d < 3; d ++) {
129  /* mind the damping term */
130  ICP[i].Vel[d] += (ICP[i].Disp[d] - ICP[i].Vel[d]) * hdt;
131  }
132  }
133 
134  t_x += hdt;
135  message(0, "Generating glass, step = %d, t_f= %g, t_v = %g, t_x = %g\n", step, t_f / (2 * M_PI), t_v / (2 *M_PI), t_x / (2 * M_PI));
136  glass_stats(ICP, NumPart);
137 
138  /*Now save the power spectrum*/
139  powerspectrum_save(pm->ps, OutputDir, pkoutname, t_f, 1.0);
140  }
141 
142  /*We are done with the power spectrum, free it*/
143  powerspectrum_free(pm->ps);
144 }
145 
146 
147 static void
148 glass_stats(struct ic_part_data * ICP, int NumPart) {
149  int i;
150  double disp2 = 0;
151  double vel2 = 0;
152  double n = NumPart;
153  for(i = 0; i < NumPart; i++)
154  {
155  int k;
156  for(k = 0; k < 3; k++)
157  {
158  double dis = ICP[i].Disp[k];
159  double vel = ICP[i].Vel[k];
160  disp2 += dis * dis;
161  vel2 += vel * vel;
162  }
163  }
164  MPI_Allreduce(MPI_IN_PLACE, &disp2, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
165  MPI_Allreduce(MPI_IN_PLACE, &vel2, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
166  MPI_Allreduce(MPI_IN_PLACE, &n, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
167 
168  message(0, "Force std = %g, vel std = %g\n", sqrt(disp2 / n), sqrt(vel2 / n));
169 }
170 
172 {
174  int NumPart;
175 };
176 /*Global to pass the particle data to the readout functions*/
177 static struct ic_part_data * curICP;
178 
179 /* Computes the gravitational force on the PM grid
180  * and saves the total matter power spectrum.*/
181 static void glass_force(PetaPM * pm, double t_f, struct ic_part_data * ICP, const int NumPart) {
182 
183  PetaPMParticleStruct pstruct = {
184  ICP,
185  sizeof(ICP[0]),
186  (char*) &ICP[0].Pos[0] - (char*) ICP,
187  (char*) &ICP[0].Mass - (char*) ICP,
188  NULL,
189  NULL,
190  NumPart,
191  };
192  curICP = ICP;
193 
194  int i;
195  #pragma omp parallel for
196  for(i = 0; i < NumPart; i++)
197  {
198  ICP[i].Disp[0] = ICP[i].Disp[1] = ICP[i].Disp[2] = 0;
199  }
200 
201  powerspectrum_zero(pm->ps);
202 
203  struct ic_prep_data icprep = {ICP, NumPart};
204  /*
205  * we apply potential transfer immediately after the R2C transform,
206  * Therefore the force transfer functions are based on the potential,
207  * not the density.
208  * */
209  petapm_force(pm, _prepare, &global_functions, functions, &pstruct, &icprep);
210 
211  powerspectrum_sum(pm->ps);
212  walltime_measure("/LongRange");
213 }
214 
215 static double pot_factor;
216 
217 static PetaPMRegion *
218 _prepare(PetaPM * pm, PetaPMParticleStruct * pstruct, void * userdata, int * Nregions)
219 {
220  struct ic_prep_data * icprep = (struct ic_prep_data *) userdata;
221  int NumPart = icprep->NumPart;
222  struct ic_part_data * ICP = icprep->curICP;
223 
224  /* dimensionless invert gravity;
225  *
226  * pot = nabla ^ -2 delta
227  *
228  * (2pi / L) ** -2 is for kint -> k.
229  * Need to divide by mean mass per cell to get delta */
230  pot_factor = -1 * (-1) * pow(2 * M_PI / pm->BoxSize, -2);
231 
232  PetaPMRegion * regions = (PetaPMRegion *) mymalloc2("Regions", sizeof(PetaPMRegion));
233  int k;
234  int r = 0;
235  int i;
236  double min[3] = {pm->BoxSize, pm->BoxSize, pm->BoxSize};
237  double max[3] = {0, 0, 0.};
238  double totmass = 0;
239 
240  for(i = 0; i < NumPart; i ++) {
241  for(k = 0; k < 3; k ++) {
242  if(min[k] > ICP[i].Pos[k])
243  min[k] = ICP[i].Pos[k];
244  if(max[k] < ICP[i].Pos[k])
245  max[k] = ICP[i].Pos[k];
246  }
247 
248  totmass += ICP[i].Mass;
249  }
250 
251  MPI_Allreduce(MPI_IN_PLACE, &totmass, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
252 
253  /* 1 / pow(pm->Nmesh, 3) is included by the FFT, so just use total mass. */
254  pot_factor /= totmass;
255 
256  for(k = 0; k < 3; k ++) {
257  regions[r].offset[k] = floor(min[k] / pm->BoxSize * pm->Nmesh - 1);
258  regions[r].size[k] = ceil(max[k] / pm->BoxSize * pm->Nmesh + 2);
259  regions[r].size[k] -= regions[r].offset[k];
260  }
261 
262  /* setup the internal data structure of the region */
263  petapm_region_init_strides(&regions[r]);
264  *Nregions = 1;
265 
266  walltime_measure("/PMgrav/Regions");
267  return regions;
268 }
269 
270 
271 /********************
272  * transfer functions for
273  *
274  * potential from mass in cell
275  *
276  * and
277  *
278  * force from potential
279  *
280  *********************/
281 
282 static void potential_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex *value) {
283 
284  double f = 1.0;
285  const double smth = 1.0 / k2;
286  /* the CIC deconvolution kernel is
287  *
288  * sinc_unnormed(k_x L / 2 pm->Nmesh) ** 2
289  *
290  * k_x = kpos * 2pi / L
291  *
292  * */
293  /*
294  * first decovolution is CIC in par->mesh
295  * second decovolution is correcting readout
296  * I don't understand the second yet!
297  * */
298  const double fac = pot_factor * smth * f * f;
299 
300  /*Compute the power spectrum*/
301  powerspectrum_add_mode(pm->ps, k2, kpos, value, f, pm->Nmesh);
302 
303  if(k2 == 0) {
304  /* Remove zero mode corresponding to the mean.*/
305  value[0][0] = 0.0;
306  value[0][1] = 0.0;
307  return;
308  }
309 
310  value[0][0] *= fac;
311  value[0][1] *= fac;
312 }
313 
314 /* the transfer functions for force in fourier space applied to potential */
315 /* super lanzcos in CH6 P 122 Digital Filters by Richard W. Hamming */
316 static double diff_kernel(double w) {
317 /* order N = 1 */
318 /*
319  * This is the same as GADGET-2 but in fourier space:
320  * see gadget-2 paper and Hamming's book.
321  * c1 = 2 / 3, c2 = 1 / 12
322  * */
323  return 1 / 6.0 * (8 * sin (w) - sin (2 * w));
324 }
325 
326 static void force_transfer(PetaPM *pm, int k, pfft_complex * value) {
327  double tmp0;
328  double tmp1;
329  /*
330  * negative sign is from force_x = - Del_x pot
331  *
332  * filter is i K(w)
333  * */
334  double fac = -1 * diff_kernel (k * (2 * M_PI / pm->Nmesh)) * (pm->Nmesh / pm->BoxSize);
335  tmp0 = - value[0][1] * fac;
336  tmp1 = value[0][0] * fac;
337  value[0][0] = tmp0;
338  value[0][1] = tmp1;
339 }
340 static void force_x_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex * value) {
341  force_transfer(pm, kpos[0], value);
342 }
343 static void force_y_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex * value) {
344  force_transfer(pm, kpos[1], value);
345 }
346 static void force_z_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex * value) {
347  force_transfer(pm, kpos[2], value);
348 }
349 static void readout_force_x(PetaPM *pm, int i, double * mesh, double weight) {
350  curICP[i].Disp[0] += weight * mesh[0];
351 }
352 static void readout_force_y(PetaPM *pm, int i, double * mesh, double weight) {
353  curICP[i].Disp[1] += weight * mesh[0];
354 }
355 static void readout_force_z(PetaPM *pm, int i, double * mesh, double weight) {
356  curICP[i].Disp[2] += weight * mesh[0];
357 }
void message(int where, const char *fmt,...)
Definition: endrun.c:175
int setup_glass(IDGenerator *idgen, PetaPM *pm, double shift, int seed, double mass, struct ic_part_data *ICP, const double UnitLength_in_cm, const char *OutputDir)
Definition: glass.c:41
static double diff_kernel(double w)
Definition: glass.c:316
static void glass_stats(struct ic_part_data *ICP, int NumPart)
Definition: glass.c:148
static void force_x_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex *value)
Definition: glass.c:340
void glass_evolve(PetaPM *pm, int nsteps, const char *pkoutname, struct ic_part_data *ICP, const int NumPart, const double UnitLength_in_cm, const char *OutputDir)
Definition: glass.c:73
static void readout_force_z(PetaPM *pm, int i, double *mesh, double weight)
Definition: glass.c:355
static void readout_force_x(PetaPM *pm, int i, double *mesh, double weight)
Definition: glass.c:349
static void force_z_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex *value)
Definition: glass.c:346
static void force_y_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex *value)
Definition: glass.c:343
static void readout_force_y(PetaPM *pm, int i, double *mesh, double weight)
Definition: glass.c:352
static PetaPMRegion * _prepare(PetaPM *pm, PetaPMParticleStruct *pstruct, void *userdata, int *Nregions)
Definition: glass.c:218
static void potential_transfer(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex *value)
Definition: glass.c:282
static double pot_factor
Definition: glass.c:215
static struct ic_part_data * curICP
Definition: glass.c:177
static PetaPMGlobalFunctions global_functions
Definition: glass.c:33
static PetaPMFunctions functions[]
Definition: glass.c:25
static void force_transfer(PetaPM *pm, int k, pfft_complex *value)
Definition: glass.c:326
static void glass_force(PetaPM *pm, double t_f, struct ic_part_data *ICP, const int NumPart)
Definition: glass.c:181
void powerspectrum_add_mode(Power *PowerSpectrum, const int64_t k2, const int kpos[3], pfft_complex *const value, const double invwindow, double Nmesh)
Definition: gravpm.c:327
void measure_power_spectrum(PetaPM *pm, int64_t k2, int kpos[3], pfft_complex *value)
Definition: gravpm.c:361
#define mymalloc2(name, size)
Definition: mymalloc.h:16
#define myfree(x)
Definition: mymalloc.h:19
void petapm_region_init_strides(PetaPMRegion *region)
Definition: petapm.c:813
void petapm_force(PetaPM *pm, petapm_prepare_func prepare, PetaPMGlobalFunctions *global_functions, PetaPMFunctions *functions, PetaPMParticleStruct *pstruct, void *userdata)
Definition: petapm.c:353
static double UnitLength_in_cm
Definition: power.c:26
void powerspectrum_save(Power *ps, const char *OutputDir, const char *filename, const double Time, const double D1)
Definition: powerspectrum.c:92
void powerspectrum_free(Power *ps)
Definition: powerspectrum.c:44
void powerspectrum_sum(Power *ps)
Definition: powerspectrum.c:54
void powerspectrum_zero(Power *ps)
Definition: powerspectrum.c:35
void powerspectrum_alloc(Power *ps, const int nbins, const int nthreads, const int MassiveNuLinResp, const double BoxSize_in_cm)
Definition: powerspectrum.c:16
void idgen_create_pos_from_index(IDGenerator *idgen, int index, double pos[3])
Definition: zeldovich.c:79
char * fastpm_strdup_printf(const char *fmt,...)
Definition: string.c:41
int Ngrid
Definition: proto.h:12
double BoxSize
Definition: proto.h:13
int NumPart
Definition: proto.h:14
Definition: petapm.h:62
int Nmesh
Definition: petapm.h:68
double BoxSize
Definition: petapm.h:70
Power ps[1]
Definition: petapm.h:76
Definition: petapm.h:7
ptrdiff_t size[3]
Definition: petapm.h:10
ptrdiff_t offset[3]
Definition: petapm.h:9
float Mass
Definition: allvars.h:14
float Vel[3]
Definition: allvars.h:11
double Pos[3]
Definition: allvars.h:10
float Disp[3]
Definition: allvars.h:12
struct ic_part_data * curICP
Definition: glass.c:173
int NumPart
Definition: glass.c:174
int ThisTask
Definition: test_exchange.c:23
#define walltime_measure(name)
Definition: walltime.h:8