The FARGO_THORIN code developer's guide
 All Data Structures Files Functions Variables Typedefs Macros
ReboundInterface.c
Go to the documentation of this file.
1 /**
2  * @file ReboundInterface.c
3  *
4  * @brief Contains the functions interfacing FARGO with
5  * the REBOUND package.
6  *
7  * @author Ondřej Chrenko <chrenko@sirrah.troja.mff.cuni.cz>
8  *
9  * @details The default setup uses the IAS15 integrator to
10  * propagate the planets in time and employs
11  * the direct collision search with merging, if turned on.
12  * This can be easily reprogrammed if needed.
13  *
14  * Note: This version has no test particles, rsim->N could be
15  * used directly as the loop limit. But rsim->N_active is used instead
16  * so that test particles could be easily implemented. If rsim->N is used,
17  * it indicates that a loop would in principle handle test particles as well.
18  *
19  * @section LICENSE
20  * Copyright (c) 2017 Ondřej Chrenko. See the LICENSE file of the
21  * distribution.
22  *
23  */
24 
25 #include "fargo.h"
26 
27 extern real OmegaFrame;
28 extern boolean Corotating;
29 
30 /** If the REBOUND collision search is successful,
31  * this function merges the bodies, outputs information
32  * about the merger event and reorganises the particle list. */
33 int ResolveCollisions (rsim, coll)
34 struct reb_simulation *rsim;
35 struct reb_collision coll;
36 {
37  int value=0, pi, pj, pk=0;
38  struct reb_particle* const particles = rsim->particles; /* note: only designed for merging planets at this point !!! */
39  pi = coll.p1; /* array indices of particles participating in the collision have no strict order! both i<j and j<i are possible */
40  pj = coll.p2;
41  if (CPU_Master) {
42  printf ("\n Merger detected. Merging planets %d and %d\n", particles[pi].hash, particles[pj].hash);
43  masterprint ("Previous no. of planets: %d\n", rsim->N_active-1);
44  fprintf (mergers, "%.12g\t%d\t%d\n", rsim->t, pi, pj);
45  fprintf (mergers, "%d\t%f\t%.8g\n", particles[pi].hash, particles[pi].m, particles[pi].r);
46  fprintf (mergers, "%#.18g\t%#.18g\t%#.18g\n", particles[pi].x, particles[pi].y, particles[pi].z);
47  fprintf (mergers, "%#.18g\t%#.18g\t%#.18g\n", particles[pi].vx, particles[pi].vy, particles[pi].vz);
48  fprintf (mergers, "%d\t%f\t%.8g\n", particles[pj].hash, particles[pj].m, particles[pj].r);
49  fprintf (mergers, "%#.18g\t%#.18g\t%#.18g\n", particles[pj].x, particles[pj].y, particles[pj].z);
50  fprintf (mergers, "%#.18g\t%#.18g\t%#.18g\n", particles[pj].vx, particles[pj].vy, particles[pj].vz);
51  }
52  value = reb_collision_resolve_merge (rsim, coll);
53  rsim->N_active--; /* change number of massive bodies accordingly */
54  if (CPU_Master) {
55  switch (value) {
56  case 1 :
57  ; pk = pj; /* i-th particle will be discarded, j-th particle replaced by the merger -> output it */
58  break;
59  case 2 :
60  ; pk = pi; /* vice versa */
61  break;
62  }
63  fprintf (mergers, "%d\t%f\t%.8g\n", particles[pk].hash, particles[pk].m, particles[pk].r);
64  fprintf (mergers, "%#.18g\t%#.18g\t%#.18g\n", particles[pk].x, particles[pk].y, particles[pk].z);
65  fprintf (mergers, "%#.18g\t%#.18g\t%#.18g\n", particles[pk].vx, particles[pk].vy, particles[pk].vz);
66  masterprint ("No. of planets after the merger: %d\n", rsim->N_active-1);
67  }
68  return value;
69 }
70 
71 /** A simple discard routine which looks for
72  * planets that were scattered/migrated away from
73  * the FARGO grid. Must be called from the heliocentric
74  * frame. */
75 void DiscardParticlesDist (rsim, dt)
76 struct reb_simulation *rsim;
77 real dt;
78 {
79  int Nact, i;
80  real xr, yr, r2;
81  boolean isremoved;
82  boolean const keepsorted=YES;
83  struct reb_particle* const particles = rsim->particles;
84  /* ----- */
85  Nact = rsim->N_active;
86  for (i=1; i<Nact; i++) {
87  xr = particles[i].x;
88  yr = particles[i].y;
89  r2 = xr*xr + yr*yr; // check whether the planet's midplane projection left the FARGO grid
90  if (r2 < RMIN*RMIN || r2 > RMAX*RMAX) {
91  masterprint ("\nPlanet %d discarded. Reason: it is not in the (RMIN, RMAX) domain.\n", particles[i].hash);
92  masterprint ("Previous no. of planets: %d\n", rsim->N_active-1);
93  if (CPU_Master) {
94  fprintf (discard, "%.12g\n", PhysicalTime+dt);
95  fprintf (discard, "%d\t%f\t%#.8g\n", particles[i].hash, particles[i].m, particles[i].r);
96  fprintf (discard, "%#.18g\t%#.18g\t%#.18g\n", particles[i].x, particles[i].y, particles[i].z);
97  fprintf (discard, "%#.18g\t%#.18g\t%#.18g\n", particles[i].vx, particles[i].vy, particles[i].vz);
98  }
99  isremoved = reb_remove (rsim, i, keepsorted);
100  if (isremoved == NO) {
101  masterprint ("\nWarning! Failed to remove planet with id %d and loop index %d\n", particles[i].hash, i);
102  }
103  masterprint ("No. of planets after the discard: %d\n", rsim->N_active-1); // no need to change N_active manually, reb_remove does this now
104  }
105  }
106 }
107 
108 /** Fills the rebound simulation structure
109  * with parameters inherited from FARGO.
110  * The integrator type can be easily changed from here. */
112 struct reb_simulation *rsim;
113 {
114  /* rsim->integrator = REB_INTEGRATOR_WHFAST; alternative integrators could be placed here... */
115  rsim->integrator = REB_INTEGRATOR_IAS15;
116  rsim->ri_ias15.epsilon = IAS15PRECISSION;
117  rsim->ri_ias15.min_dt = IAS15MINDT;
118  if (Collisions == YES) {
119  rsim->collision = REB_COLLISION_DIRECT;
120  rsim->collision_resolve = ResolveCollisions;
121  } else {
122  rsim->collision = REB_COLLISION_NONE;
123  }
124 }
125 
126 /** Initialises a rebound simulation coupled with FARGO */
127 struct reb_simulation *SetupReboundSimulation (sys, plfile)
128 PlanetarySystem *sys;
129 char *plfile;
130 {
131  FILE *input;
132  char s[512], nm[512], filename[256], *s1;
133  int npl, i, err;
134  float mass, a, e, inc, Omega, omega, f;
135  real rho, radius, rad;
136  /* ----- */
137  struct reb_simulation *rsim = reb_create_simulation ();
138  SetupIntegratorParams (rsim);
139  npl = sys->nb;
140  rsim->N_active = npl + 1; // N_active = number of massive particles, i.e. planets + primary
141  rad = PI/180.0; // angles to radians conversion
142  struct reb_particle primary = {0}; // initialize the primary --->
143  primary.hash = 0; // new version uses 'hash' member instead of 'id' member of reb_particle structure
144  primary.m = 1.0;
145  reb_add (rsim, primary); // <---
146  input = fopen (plfile, "r"); // initialize the planets; some stuff is similar to InitPlanetarySystem() in psys.c
147  if (input == NULL) {
148  fprintf (stderr, "Error : can't find '%s'.\n", filename);
149  prs_exit (1);
150  }
152  i = 1; // REBOUND has the primary at i=0
153  while (fgets(s, 510, input) != NULL) {
154  sscanf(s, "%s ", nm);
155  if (isalpha(s[0])) {
156  s1 = s + strlen(nm);
157  // input as the planet mass and orbital elements. Accretion and FEELDISK options are managed by parameters. FEELOTHERS=YES is implicit.
158  sscanf(s1 + strspn(s1, "\t :=>_"), "%f %f %f %f %f %f %f", \
159  &mass, &a, &e, &inc, &Omega, &omega, &f);
160  err = 0;
161  struct reb_particle p = reb_tools_orbit_to_particle_err (G, primary, \
162  (real)mass, (real)a, (real)e, (real)inc*rad, (real)Omega*rad, (real)omega*rad, (real)f*rad, &err);
163  if (err != 0) {
164  masterprint("Error: Planet no. %d cannot be initialized \
165  using the input parameters. Terminating now...\n", i);
166  prs_exit (1);
167  }
168  p.hash = i;
169  radius = pow(3.0*(real)mass/4.0/PI/rho , 1.0/3.0);
170  p.r = radius; // future 2DO - inflation parameter could be implemented here
171  reb_add (rsim, p);
172  sys->mass[i-1] = p.m; // [i-1] because FARGO has the 1st planet at i=0
173  sys->x[i-1] = p.x;
174  sys->y[i-1] = p.y;
175  sys->z[i-1] = p.z;
176  sys->vx[i-1] = p.vx;
177  sys->vy[i-1] = p.vy;
178  sys->vz[i-1] = p.vz;
179  sys->acc[i-1] = ACCRETIONRATE;
180  sys->FeelDisk[i-1] = FeelDisk;
181  i++;
182  }
183  }
184  fclose (input);
185  sprintf (filename, "%snbody.orbits.dat", OUTPUTDIR);
186  plout = fopenp (filename, "w"); // "w" should empty the file if it exists
187  sprintf (filename, "%snbody.discard.dat", OUTPUTDIR);
188  discard = fopenp (filename, "w");
189  if (Collisions == YES) {
190  sprintf (filename, "%snbody.mergers.dat", OUTPUTDIR);
191  mergers = fopenp (filename, "w");
192  }
193  return rsim;
194 }
195 
196 /** Performs an integration step of the N-body problem */
197 void AdvanceSystemRebound (sys, rsim, dt)
198 PlanetarySystem *sys;
199 struct reb_simulation *rsim;
200 real dt;
201 {
202  int i;
203  real ttarget;
204  struct reb_particle com;
205  struct reb_particle* const particles = rsim->particles; /* asterisk means that the pointer is not to be changed, unlike the stuff it points to */
206  for (i=1;i<rsim->N_active;i++) { /* only planets have to be synchronised */
207  particles[i].m = sys->mass[i-1]; /* masses can change due to accretion etc */
208  particles[i].x = sys->x[i-1];
209  particles[i].y = sys->y[i-1];
210  particles[i].z = sys->z[i-1];
211  particles[i].vx = sys->vx[i-1];
212  particles[i].vy = sys->vy[i-1];
213  particles[i].vz = sys->vz[i-1];
214  }
215  com = reb_get_com (rsim); /* a particle representing the centre of mass */
216  for (i=0;i<rsim->N;i++) { /* convert helioc->baryc */
217  particles[i].x -= com.x;
218  particles[i].y -= com.y;
219  particles[i].z -= com.z;
220  particles[i].vx -= com.vx;
221  particles[i].vy -= com.vy;
222  particles[i].vz -= com.vz;
223  }
224  rsim->t = PhysicalTime;
225  rsim->dt = dt;
226  ttarget = PhysicalTime + dt;
227  if (rsim->integrator==REB_INTEGRATOR_IAS15) reb_integrator_ias15_clear (rsim); /* dt and planet stuff might have changed since the last step due to e.g. CFL; planet-disk interactions ... */
228  reb_integrate (rsim, ttarget); /* ... one cannot use estimates from the last step and has to reset auxiliary arrays of IAS15 */
229  for (i=1;i<rsim->N;i++) { /* convert back to heliocentric (to apply disk-planet interactions and rotations in FARGO */
230  particles[i].x -= particles[0].x;
231  particles[i].y -= particles[0].y;
232  particles[i].z -= particles[0].z;
233  particles[i].vx -= particles[0].vx;
234  particles[i].vy -= particles[0].vy;
235  particles[i].vz -= particles[0].vz;
236  }
237  particles[0].x = 0.0; /* just to be sure that the origin = the primary */
238  particles[0].y = 0.0;
239  particles[0].z = 0.0;
240  particles[0].vx = 0.0;
241  particles[0].vy = 0.0;
242  particles[0].vz = 0.0;
243  DiscardParticlesDist (rsim, dt); // !has to be called after the helioc. transformation
244  /* Here we do not put all planetary stuff back to 'sys', this is done later on
245  (after rotations etc) in SynchronizeFargoRebound ().*/
246 }
247 
248 /** Synchronises the planetary system between the
249  * REBOUND integration and the FARGO simulation. */
250 void SynchronizeFargoRebound (sys, rsim)
251 PlanetarySystem *sys;
252 struct reb_simulation *rsim;
253 {
254  int i;
255  boolean lessplanets = NO;
256  struct reb_particle* const particles = rsim->particles;
257  if (sys->nb > rsim->N_active-1) { /* if there are more planets in FARGO than in REBOUND */
258  lessplanets = YES; /* something must have happened */
259  sys->nb = rsim->N_active-1; /* so shrink the planetary array in FARGO so all the loops still work */
260  }
261  if (sys->nb < rsim->N_active-1) {
262  printf ("Error! There are more planets in REBOUND than in FARGO! Terminating now...\n");
263  prs_exit (1);
264  }
265  for (i=1;i<rsim->N_active;i++) { /* put planetary stuff back */
266  sys->x[i-1] = particles[i].x;
267  sys->y[i-1] = particles[i].y;
268  sys->z[i-1] = particles[i].z;
269  sys->vx[i-1] = particles[i].vx;
270  sys->vy[i-1] = particles[i].vy;
271  sys->vz[i-1] = particles[i].vz;
272  if (lessplanets) {
273  sys->mass[i-1] = particles[i].m;
274  }
275  }
276 }
277 
278 /** A simple time step restriction in order
279  * not to miss a collision. This should be in principle
280  * always be overridden by the IAS15 time step division. */
281 void MinStepForRebound (rsim)
282 struct reb_simulation *rsim;
283 {
284  int Nact, i, j;
285  real dt, dtnew, r2, rv;
286  struct reb_particle* const particles = rsim->particles;
287  Nact = rsim->N_active;
288  dt = 1.e30;
289  for (i=1; i<Nact; i++) { // loop over planet pairs, make a check as e.g. (5) in Pierens et al. (2013)
290  for (j=i+1; j<Nact; j++) {
291  r2 = (particles[i].x - particles[j].x)*(particles[i].x - particles[j].x) + \
292  (particles[i].y - particles[j].y)*(particles[i].y - particles[j].y) + \
293  (particles[i].z - particles[j].z)*(particles[i].z - particles[j].z);
294  rv = (particles[i].x - particles[j].x)*(particles[i].vx - particles[j].vx) + \
295  (particles[i].y - particles[j].y)*(particles[i].vy - particles[j].vy) + \
296  (particles[i].z - particles[j].z)*(particles[i].vz - particles[j].vz);
297  dtnew = fabs(r2/rv);
298  if (dtnew < dt) {
299  dt = dtnew;
300  }
301  }
302  }
303  dt *= 0.25; // safety factor
304  dt *= dt; // final time needs to be squared before combined in CFL
305  invdtreb_sq = 1.0/dt;
306 }
307 
308 /** Calculates and outputs the orbital elements. */
309 void OutputElements (rsim) // note: plout is a global file name
310 struct reb_simulation *rsim;
311 {
312  int Nact, i;
313  struct reb_orbit orbit;
314  struct reb_particle* const particles = rsim->particles;
315  if (CPU_Rank != CPU_Number-1) return;
316  Nact = rsim->N_active;
317  for (i=1; i<Nact; i++) {
318  orbit = reb_tools_particle_to_orbit (G, particles[i], particles[0]);
319  fprintf (plout, "%d\t%.12g\t%.12g\t%.12g\t%.12g\t%.12g\t%.12g\t%.12g\t%.12g\t%.12g\t%.12g\t%.12g\t%.12g\n", \
320  particles[i].hash, PhysicalTime, orbit.a, orbit.e, orbit.inc, \
321  orbit.Omega, orbit.omega, orbit.f, particles[i].m, particles[i].r,
322  particles[i].x, particles[i].y, particles[i].z);
323  }
324 }
325 
326 /** Stores the entire Rebound simulation
327  * in a binary file. Useful for restarts. */
328 void OutputNbodySimulation (nout, rsim)
329 int nout;
330 struct reb_simulation *rsim;
331 {
332  char filename[256];
333  if (!CPU_Master) return;
334  printf ("Writing the N-body simulation file...\n");
335  printf ("\tNumber of planets: %d\n", rsim->N_active-1);
336  sprintf (filename, "%snbody.simulation%d.dat", OUTPUTDIR, nout);
337  reb_output_binary (rsim, filename);
338  printf ("\t\t...done\n");
339  fflush (stdout);
340 }
341 
342 /** Part of the restart process. */
343 struct reb_simulation *RestartReboundSimulation (sys, nrestart)
344 PlanetarySystem *sys;
345 int nrestart;
346 {
347  int i;
348  char filename[256];
349  struct reb_particle *particles;
350  /* ----- */
351  if (CPU_Master) printf ("\n\033[1mRestarting N-body part...\033[0m\n");
352  fflush (stdout);
353  sprintf (filename, "%snbody.simulation%d.dat", OUTPUTDIR, nrestart);
354  struct reb_simulation *rsim = reb_create_simulation_from_binary(filename);
355  SetupIntegratorParams (rsim);
356  if (CPU_Master) printf ("\033[1mNo problem!\033[0m Function pointers have been reset via the SetupIntegratorParams() function.\n");
357  particles = rsim->particles;
358  PhysicalTime = rsim->t;
359  sys->nb = rsim->N_active - 1; // get proper 'nb' in case of e.g. previous planetary mergers
360  for (i=1; i<rsim->N_active; i++) {
361  sys->mass[i-1] = particles[i].m;
362  sys->x[i-1] = particles[i].x;
363  sys->y[i-1] = particles[i].y;
364  sys->z[i-1] = particles[i].z;
365  sys->vx[i-1] = particles[i].vx;
366  sys->vy[i-1] = particles[i].vy;
367  sys->vz[i-1] = particles[i].vz;
368  sys->acc[i-1] = ACCRETIONRATE;
369  sys->FeelDisk[i-1] = FeelDisk;
370  }
371  sprintf (filename, "%snbody.orbits.dat", OUTPUTDIR);
372  plout = fopenp (filename, "a"); // "a" won't empty the file if it exists
373  sprintf (filename, "%snbody.discard.dat", OUTPUTDIR);
374  discard = fopenp (filename, "a");
375  if (Collisions == YES) {
376  sprintf (filename, "%snbody.mergers.dat", OUTPUTDIR);
377  mergers = fopenp (filename, "a");
378  }
379  if (CPU_Master) {
380  printf ("%d active planets at restart.\n", rsim->N_active - 1);
381  printf ("\n\033[1m...done\n\n\033[0m");
382  }
383  return rsim;
384 }
double real
Definition of the type 'real' used throughout the code.
Definition: types.h:20
int CPU_Number
Definition: global.h:2
real PLANETARYDENSITY
Definition: param_noex.h:72
#define YES
Definition: types.h:46
int ResolveCollisions(struct reb_simulation *rsim, struct reb_collision coll)
If the REBOUND collision search is successful, this function merges the bodies, outputs information a...
boolean FeelDisk
Definition: global.h:26
real invdtreb_sq
Definition: global.h:19
char OUTPUTDIR[512]
Definition: param_noex.h:11
boolean Collisions
Definition: global.h:26
struct reb_simulation * RestartReboundSimulation(PlanetarySystem *sys, int nrestart)
Part of the restart process.
FILE * fopenp(char *string, char *mode)
Definition: LowTasks.c:163
void OutputNbodySimulation(int nout, struct reb_simulation *rsim)
Stores the entire Rebound simulation in a binary file.
void SynchronizeFargoRebound(PlanetarySystem *sys, struct reb_simulation *rsim)
Synchronises the planetary system between the REBOUND integration and the FARGO simulation.
real * mass
Masses of the planets.
Definition: types.h:98
Contains all the information about a planetary system at a given instant in time. ...
Definition: types.h:96
static real a[7]
Definition: EnergySources.c:27
real PhysicalTime
Definition: global.h:20
struct reb_simulation * SetupReboundSimulation(PlanetarySystem *sys, char *plfile)
Initialises a rebound simulation coupled with FARGO.
void MinStepForRebound(struct reb_simulation *rsim)
A simple time step restriction in order not to miss a collision.
#define G
Definition: fondam.h:11
FILE * discard
Definition: global.h:44
real ACCRETIONRATE
Definition: param_noex.h:81
#define RHO2CGS
Definition: fondam.h:38
void OutputElements(struct reb_simulation *rsim)
Calculates and outputs the orbital elements.
FILE * mergers
Definition: global.h:44
boolean Corotating
Definition: Interpret.c:30
void AdvanceSystemRebound(PlanetarySystem *sys, struct reb_simulation *rsim, real dt)
Performs an integration step of the N-body problem.
real IAS15MINDT
Definition: param_noex.h:76
void SetupIntegratorParams(struct reb_simulation *rsim)
Fills the rebound simulation structure with parameters inherited from FARGO.
#define NO
Definition: types.h:47
real RMAX
Definition: param_noex.h:21
real IAS15PRECISSION
Definition: param_noex.h:75
void DiscardParticlesDist(struct reb_simulation *rsim, real dt)
A simple discard routine which looks for planets that were scattered/migrated away from the FARGO gri...
Contains all the include directives requested by the code.
int CPU_Rank
Definition: global.h:1
FILE * plout
Definition: global.h:44
real OmegaFrame
Definition: global.h:20
boolean CPU_Master
Definition: global.h:3
#define PI
Definition: fondam.h:12
void prs_exit(int numb)
Definition: LowTasks.c:33
void masterprint(const char *template,...)
Definition: LowTasks.c:40