#include <string.h>
#include "rebound.h"
#include "particle.h"
#include "tools.h"
#include "gravity.h"
#include "boundary.h"
#include "integrator_janus.h"
#include "binarydata.h"
void reb_integrator_janus_step(struct reb_simulation* r, void* state);
void reb_integrator_janus_synchronize(struct reb_simulation* r, void* state);
void* reb_integrator_janus_create();
void reb_integrator_janus_free(void* p);
const struct reb_binarydata_field_descriptor reb_integrator_janus_field_descriptor_list[];
const struct reb_integrator reb_integrator_janus = {
.documentation =
"Janus is a bit-wise time-reversible high-order symplectic integrator "
"using a mix of floating point and integer arithmetic. The basic idea and "
"this specific implementation is described in [Rein & Tamayo (2018)]."
"\n\n"
"[Rein & Tamayo (2018)]: https://ui.adsabs.harvard.edu/abs/2018MNRAS.473.3351R/abstract\n"
,
.step = reb_integrator_janus_step,
.create = reb_integrator_janus_create,
.free = reb_integrator_janus_free,
.synchronize = reb_integrator_janus_synchronize,
.field_descriptor_list = reb_integrator_janus_field_descriptor_list,
};
const struct reb_binarydata_field_descriptor reb_integrator_janus_field_descriptor_list[] = {
{ "Scale of the problem. Positions get divided by this number before the conversion to an integer. Default: 1e-16.",
REB_DOUBLE, "scale_pos", offsetof(struct reb_integrator_janus_state, scale_pos), 0, 0, 0},
{ "Scale of the problem. Velocities get divided by this number before the conversion to an integer. Default: 1e-16.",
REB_DOUBLE, "scale_vel", offsetof(struct reb_integrator_janus_state, scale_vel), 0, 0, 0},
{ "The order of the scheme. Supported values: 2, 4, 6, 8, 10. Default is 6.",
REB_UINT, "order", offsetof(struct reb_integrator_janus_state, order), 0, 0, 0},
{ "If this flag is set, then JANUS will recalculate the integer coordinates from floating point coordinates at the next timestep.",
REB_UINT, "recalculate_integer_coordinates_this_timestep", offsetof(struct reb_integrator_janus_state, recalculate_integer_coordinates_this_timestep), 0, 0, 0},
{ "", REB_POINTER, "p_int", offsetof(struct reb_integrator_janus_state, p_int), offsetof(struct reb_integrator_janus_state, N_allocated), sizeof(struct reb_particle_int), 0},
{ 0 }, };
void* reb_integrator_janus_create(){
struct reb_integrator_janus_state* janus = calloc(sizeof(struct reb_integrator_janus_state),1);
janus->recalculate_integer_coordinates_this_timestep = 0;
janus->order = 6;
janus->scale_pos = 1e-16;
janus->scale_vel = 1e-16;
return janus;
}
void reb_integrator_janus_free(void* state){
struct reb_integrator_janus_state* janus = state;
free(janus->p_int);
free(janus);
}
struct reb_janus_scheme {
unsigned int order; unsigned int stages; double gamma[17]; };
static struct reb_janus_scheme s1odr2 = {
.order = 2,
.stages = 1,
.gamma = { 1.,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
static struct reb_janus_scheme s5odr4 = {
.order = 4,
.stages = 5,
.gamma= { 0.41449077179437573714,
0.41449077179437573714,
-0.65796308717750294857,
0,0,0,0,0,0,0,0,0,0,0,0,0,0
}
};
static struct reb_janus_scheme s9odr6a = {
.order = 6,
.stages = 9,
.gamma= { 0.39216144400731413928,
0.33259913678935943860,
-0.70624617255763935981,
0.082213596293550800230,
0.79854399093482996340,
0,0,0,0,0,0,0,0,0,0,0,0
}
};
static struct reb_janus_scheme s15odr8 = {
.order = 8,
.stages = 15,
.gamma= { .74167036435061295345,
-.40910082580003159400,
.19075471029623837995,
-.57386247111608226666,
.29906418130365592384,
.33462491824529818378,
.31529309239676659663,
-.79688793935291635402,
0,0,0,0,0,0,0,0,0
}
};
static struct reb_janus_scheme s33odr10c = {
.order = 10,
.stages = 33,
.gamma= { 0.12313526870982994083,
0.77644981696937310520,
0.14905490079567045613,
-0.17250761219393744420,
-0.54871240818800177942,
0.14289765421841842100,
-0.31419193263986861997,
0.12670943739561041022,
0.17444734584181312998,
0.44318544665428572929,
-0.81948900568299084419,
0.13382545738489583020,
0.64509023524410605020,
-0.71936337169922060719,
0.20951381813463649682,
-0.26828113140636051966,
0.83647216092348048955
}
};
static double gg(struct reb_janus_scheme s, unsigned int stage){
if (stage<(s.stages+1)/2){
return s.gamma[stage];
}else{
return s.gamma[ (s.stages-1-stage)%17 ]; }
}
static void to_int(struct reb_particle_int* psi, struct reb_particle* ps, size_t N, double scale_pos, double scale_vel){
for(size_t i=0; i<N; i++){
psi[i].x = ps[i].x/scale_pos;
psi[i].y = ps[i].y/scale_pos;
psi[i].z = ps[i].z/scale_pos;
psi[i].vx = ps[i].vx/scale_vel;
psi[i].vy = ps[i].vy/scale_vel;
psi[i].vz = ps[i].vz/scale_vel;
}
}
static void to_double(struct reb_particle* ps, struct reb_particle_int* psi, size_t N, double scale_pos, double scale_vel){
for(size_t i=0; i<N; i++){
ps[i].x = ((double)psi[i].x)*scale_pos;
ps[i].y = ((double)psi[i].y)*scale_pos;
ps[i].z = ((double)psi[i].z)*scale_pos;
ps[i].vx = ((double)psi[i].vx)*scale_vel;
ps[i].vy = ((double)psi[i].vy)*scale_vel;
ps[i].vz = ((double)psi[i].vz)*scale_vel;
}
}
static void drift(struct reb_simulation* r, double dt, double scale_pos, double scale_vel){
struct reb_integrator_janus_state* janus = r->integrator.state;
const size_t N = r->N;
for(size_t i=0; i<N; i++){
janus->p_int[i].x += (REB_PARTICLE_INT_TYPE)(dt*(double)janus->p_int[i].vx*scale_vel/scale_pos) ;
janus->p_int[i].y += (REB_PARTICLE_INT_TYPE)(dt*(double)janus->p_int[i].vy*scale_vel/scale_pos) ;
janus->p_int[i].z += (REB_PARTICLE_INT_TYPE)(dt*(double)janus->p_int[i].vz*scale_vel/scale_pos) ;
}
}
static void kick(struct reb_simulation* r, double dt, double scale_vel){
struct reb_integrator_janus_state* janus = r->integrator.state;
const size_t N = r->N;
for(size_t i=0; i<N; i++){
janus->p_int[i].vx += (REB_PARTICLE_INT_TYPE)(dt*r->particles[i].ax/scale_vel) ;
janus->p_int[i].vy += (REB_PARTICLE_INT_TYPE)(dt*r->particles[i].ay/scale_vel) ;
janus->p_int[i].vz += (REB_PARTICLE_INT_TYPE)(dt*r->particles[i].az/scale_vel) ;
}
}
void reb_integrator_janus_step(struct reb_simulation* r, void* state){
r->gravity_ignore_terms = REB_GRAVITY_IGNORE_TERMS_NONE;
struct reb_integrator_janus_state* janus = state;
const size_t N = r->N;
const double dt = r->dt;
const double scale_vel = janus->scale_vel;
const double scale_pos = janus->scale_pos;
if (janus->N_allocated != N){
janus->N_allocated = N;
janus->p_int = realloc(janus->p_int, sizeof(struct reb_particle_int)*N);
janus->recalculate_integer_coordinates_this_timestep = 1;
}
if (janus->recalculate_integer_coordinates_this_timestep==1){
to_int(janus->p_int, r->particles, N, scale_pos, scale_vel);
janus->recalculate_integer_coordinates_this_timestep = 0;
}
struct reb_janus_scheme s;
switch (janus->order){
case 2:
s = s1odr2;
break;
case 4:
s = s5odr4;
break;
case 6:
s = s9odr6a;
break;
case 8:
s = s15odr8;
break;
case 10:
s = s33odr10c;
break;
default:
s = s1odr2;
reb_simulation_error(r,"Order not supported in JANUS.");
}
drift(r,gg(s,0)*dt/2.,scale_pos,scale_vel);
to_double(r->particles, janus->p_int, r->N, scale_pos, scale_vel);
reb_simulation_update_acceleration(r);
kick(r,gg(s,0)*dt, scale_vel);
for (unsigned int i=1; i<s.stages; i++){
drift(r,(gg(s,i-1)+gg(s,i))*dt/2.,scale_pos,scale_vel);
to_double(r->particles, janus->p_int, N, scale_pos, scale_vel);
reb_simulation_update_acceleration(r);
kick(r,gg(s,i)*dt, scale_vel);
}
drift(r,gg(s,s.stages-1)*dt/2.,scale_pos,scale_vel);
reb_integrator_janus_synchronize(r, state);
r->t += r->dt;
}
void reb_integrator_janus_synchronize(struct reb_simulation* r, void* state){
struct reb_integrator_janus_state* janus = state;
if (janus->N_allocated==r->N){
to_double(r->particles, janus->p_int, r->N, janus->scale_pos, janus->scale_vel);
}
}