#include <string.h>
#include "rebound.h"
#include "particle.h"
#include "tools.h"
#include "gravity.h"
#include "transformations.h"
#include "integrator_whfast.h"
#include "integrator_saba.h"
#include "binarydata.h"
#define MAX(a, b) ((a) < (b) ? (b) : (a))
#define MIN(a, b) ((a) > (b) ? (b) : (a))
void reb_integrator_saba_step(struct reb_simulation* r, void* state);
void reb_integrator_saba_synchronize(struct reb_simulation* r, void* state);
void* reb_integrator_saba_create();
void reb_integrator_saba_free(void* p);
const struct reb_binarydata_field_descriptor reb_integrator_saba_field_descriptor_list[];
const struct reb_integrator reb_integrator_saba = {
.documentation =
"SABA are symplectic integrators developed by [Laskar & Robutel (2001)] "
"and [Blanes et al. (2013)]. The implementation in REBOUND supports "
"SABA1, SABA2, SABA3, and SABA4 as well as the corrected versions "
"SABAC1, SABAC2, SABAC3, and SABAC4. Different correctors can be selected. "
"In addition, the following methods with various generalized orders "
"are supported: SABA(8,4,4), SABA(8,6,4), SABA(10,6,4). "
"See [Rein, Tamayo & Brown (2019)]for details on how these methods work. "
"\n\n"
"[Laskar & Robutel (2001)]: https://ui.adsabs.harvard.edu/abs/2001CeMDA..80...39L/abstract\n"
"[Blanes et al. (2013)]: https://ui.adsabs.harvard.edu/abs/2012arXiv1208.0689B/abstract\n"
"[Rein, Tamayo & Brown (2019)]: https://ui.adsabs.harvard.edu/abs/2019MNRAS.489.4632R/abstract\n"
,
.step = reb_integrator_saba_step,
.create = reb_integrator_saba_create,
.synchronize = reb_integrator_saba_synchronize,
.free = reb_integrator_saba_free,
.field_descriptor_list = reb_integrator_saba_field_descriptor_list,
};
const struct reb_binarydata_field_descriptor reb_integrator_saba_field_descriptor_list[] = {
{ "This flag has the same functionality as in WHFast. Default is 1. Setting "
"this to 0 will provide a speedup, but care must be taken with synchronizing "
"integration steps and modifying particles.",
REB_UINT, "safe_mode", offsetof(struct reb_integrator_saba_state, safe_mode), 0, 0, 0},
{ "This parameter specifies which SABA integrator type is used.",
REB_INT, "type", offsetof(struct reb_integrator_saba_state, type), 0, 0, REB_GENERATE_ENUM_DESCRIPTORS(REB_INTEGRATOR_SABA_TYPE)},
{ "This flag determines if the inertial coordinates generated are "
"discarded in subsequent timesteps (cached Jacobi coordinates are "
"used instead). The default is 0. Set this flag to 1 if you "
"require outputs and bit-wise reproducibility",
REB_UINT, "keep_unsynchronized", offsetof(struct reb_integrator_saba_state, keep_unsynchronized), 0, 0, 0},
{ "", REB_POINTER, "p_jh", offsetof(struct reb_integrator_saba_state, p_jh), offsetof(struct reb_integrator_saba_state, N_allocated), sizeof(struct reb_particle), 0},
{ 0 }, };
void* reb_integrator_saba_create(){
struct reb_integrator_saba_state* saba = calloc(sizeof(struct reb_integrator_saba_state),1);
saba->type = REB_INTEGRATOR_SABA_TYPE_10_6_4;
saba->safe_mode = 1;
saba->keep_unsynchronized = 0;
return saba;
}
void reb_integrator_saba_free(void* p){
struct reb_integrator_saba_state* saba = p;
free(saba->p_jh);
free(saba->p_temp);
free(saba);
}
static int reb_saba_stages(const int type){
switch(type){
case REB_INTEGRATOR_SABA_TYPE_1:
case REB_INTEGRATOR_SABA_TYPE_CM_1:
case REB_INTEGRATOR_SABA_TYPE_CL_1:
return 1;
case REB_INTEGRATOR_SABA_TYPE_2:
case REB_INTEGRATOR_SABA_TYPE_CM_2:
case REB_INTEGRATOR_SABA_TYPE_CL_2:
return 2;
case REB_INTEGRATOR_SABA_TYPE_3:
case REB_INTEGRATOR_SABA_TYPE_CM_3:
case REB_INTEGRATOR_SABA_TYPE_CL_3:
return 3;
case REB_INTEGRATOR_SABA_TYPE_4:
case REB_INTEGRATOR_SABA_TYPE_CM_4:
case REB_INTEGRATOR_SABA_TYPE_CL_4:
return 4;
case REB_INTEGRATOR_SABA_TYPE_H_8_4_4:
return 6;
case REB_INTEGRATOR_SABA_TYPE_10_4:
case REB_INTEGRATOR_SABA_TYPE_8_6_4:
return 7;
case REB_INTEGRATOR_SABA_TYPE_10_6_4:
case REB_INTEGRATOR_SABA_TYPE_H_8_6_4:
return 8;
case REB_INTEGRATOR_SABA_TYPE_H_10_6_4:
return 9;
default:
return 0;
}
}
static const double reb_saba_c[10][5] = {
{0.5, }, {0.2113248654051871177454256097490212721762, 0.5773502691896257645091487805019574556476, }, {0.1127016653792583114820734600217600389167, 0.3872983346207416885179265399782399610833, }, {0.06943184420297371238802675555359524745214, 0.2605776340045981552106403648947824089476,
0.3399810435848562648026657591032446872006, }, {0.04706710064597250612947887637243678556564, 0.1847569354170881069247376193702560968574,
0.2827060056798362053243616565541452479160, -0.01453004174289681837857815229683813033908, }, {0.0711334264982231177779387300061549964174, 0.241153427956640098736487795326289649618,
0.521411761772814789212136078067994229991, -0.333698616227678005726562603400438876027, }, {0.03809449742241219545697532230863756534060, 0.1452987161169137492940200726606637497442,
0.2076276957255412507162056113249882065158, 0.4359097036515261592231548624010651844006,
-0.6538612258327867093807117373907094120024, }, {0.2741402689434018761640565440378637101205, -0.1075684384401642306251105297063236526845,
-0.0480185025906016926911954171508475065370, 0.7628933441747280943044988056386148982021}, {0.06810235651658372084723976682061164571212, 0.2511360387221033233072829580455350680082,
-0.07507264957216562516006821767601620052338, -0.009544719701745007811488218957217113269121,
0.5307579480704471776340674235341732001443}, {0.04731908697653382270404371796320813250988, 0.2651105235748785159539480036185693201078,
-0.009976522883811240843267468164812380613143, -0.05992919973494155126395247987729676004016,
0.2574761120673404534492282264603316880356}, };
static const double reb_saba_d[10][5] = {
{1., },
{0.5,},
{0.2777777777777777777777777777777777777778, 0.4444444444444444444444444444444444444444,},
{0.1739274225687269286865319746109997036177, 0.3260725774312730713134680253890002963823},
{ 0.1188819173681970199453503950853885936957, 0.2410504605515015657441667865901651105675,
-0.2732866667053238060543113981664559460630, 0.8267085775712504407295884329818044835997, }, { 0.183083687472197221961703757166430291072, 0.310782859898574869507522291054262796375,
-0.0265646185119588006972121379164987592663, 0.0653961422823734184559721793911134363710, }, { 0.09585888083707521061077150377145884776921, 0.2044461531429987806805077839164344779763,
0.2170703479789911017143385924306336714532, -0.01737538195906509300561788011852699719871, }, {0.6408857951625127177322491164716010349386, -0.8585754489567828565881283246356000103664,
0.7176896537942701388558792081639989754277}, {0.1684432593618954534310382697756917558148, 0.4243177173742677224300351657407231801453,
-0.5858109694681756812309015355404036521923, 0.4930499927320125053698281000239887162321}, {0.1196884624585322035312864297489892143852, 0.3752955855379374250420128537687503199451,
-0.4684593418325993783650820409805381740605, 0.3351397342755897010393098942949569049275,
0.2766711191210800975049457263356834696055}, };
static const double reb_saba_cc[4] = {
0.08333333333333333333333333333333333333333, 0.01116454968463011276968973577058865137738, 0.005634593363122809402267823769797538671562, 0.003396775048208601331532157783492144, };
static void reb_saba_corrector_step(struct reb_simulation* r, struct reb_integrator_saba_state* saba, double cc){
struct reb_particle* const p_j = saba->p_jh;
struct reb_particle* const particles = r->particles;
const size_t N = r->N;
switch (saba->type/0x100){
case 1: reb_transformations_jacobi_to_inertial_pos(particles, p_j, particles, N, N);
reb_simulation_update_acceleration(r);
reb_integrator_whfast_calculate_jerk(r, saba->p_jh);
for (size_t i=0; i<N; i++){
const double prefact = r->dt*r->dt;
particles[i].ax = prefact*p_j[i].ax;
particles[i].ay = prefact*p_j[i].ay;
particles[i].az = prefact*p_j[i].az;
}
reb_integrator_whfast_interaction_step(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI, cc*r->dt);
break;
case 2: {
if (saba->N_allocated_temp != N){
saba->N_allocated_temp = N;
saba->p_temp = realloc(saba->p_temp,sizeof(struct reb_particle)*N);
}
struct reb_particle* p_temp = saba->p_temp;
reb_transformations_jacobi_to_inertial_pos(particles, p_j, particles, N, N);
reb_simulation_update_acceleration(r);
reb_transformations_inertial_to_jacobi_acc(particles, p_j, particles, N, N);
memcpy(p_temp,p_j,r->N*sizeof(struct reb_particle));
const double prefac1 = r->dt*r->dt/12.;
for (size_t i=1;i<N;i++){
p_j[i].x += prefac1 * p_temp[i].ax;
p_j[i].y += prefac1 * p_temp[i].ay;
p_j[i].z += prefac1 * p_temp[i].az;
}
reb_transformations_jacobi_to_inertial_pos(particles, p_j, particles, N, N);
reb_simulation_update_acceleration(r);
reb_transformations_inertial_to_jacobi_acc(particles, p_j, particles, N, N);
const double prefact = cc*r->dt*12.;
for (size_t i=1;i<N;i++){
p_j[i].vx += prefact*(p_j[i].ax - p_temp[i].ax);
p_j[i].vy += prefact*(p_j[i].ay - p_temp[i].ay);
p_j[i].vz += prefact*(p_j[i].az - p_temp[i].az);
p_j[i].x = p_temp[i].x;
p_j[i].y = p_temp[i].y;
p_j[i].z = p_temp[i].z;
}
}
break;
default:
return;
};
}
void reb_integrator_saba_step(struct reb_simulation* const r, void* state){
struct reb_integrator_saba_state* saba = state;
struct reb_particle* restrict const particles = r->particles;
const int type = saba->type;
const int stages = reb_saba_stages(type);
const size_t N = r->N;
if (r->N_var_config>0){
reb_simulation_error(r, "Variational particles are not supported in the SABA integrator.");
return;
}
if (saba->keep_unsynchronized==1 && saba->safe_mode==1){
reb_simulation_error(r, "saba->keep_unsynchronized == 1 is not compatible with safe_mode. Must set saba->safe_mode = 0.");
}
if (type!=0x0 && type!=0x1 && type!=0x2 && type!=0x3 &&
type!=0x100 && type!=0x101 && type!=0x102 && type!=0x103 &&
type!=0x200 && type!=0x201 && type!=0x202 && type!=0x203 &&
type!=0x4 && type!=0x5 && type!=0x6 &&
type!=0x7 && type!=0x8 && type!=0x9 ){
reb_simulation_error(r, "Invalid SABA integrator type used.");
return;
}
if (type>=0x100){
r->gravity = REB_GRAVITY_JACOBI;
}else{
r->gravity_ignore_terms = REB_GRAVITY_IGNORE_TERMS_BETWEEN_0_AND_1;
}
if (saba->N_allocated != N){
saba->N_allocated = N;
saba->p_jh = realloc(saba->p_jh,sizeof(struct reb_particle)*N);
r->did_modify_particles = 1;
}
if (saba->safe_mode || r->did_modify_particles){
reb_integrator_whfast_from_inertial(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI);
}
if (type>=0x100){ if (r->is_synchronized){
reb_saba_corrector_step(r, saba, reb_saba_cc[type%0x100]);
}else{
reb_saba_corrector_step(r, saba, 2.*reb_saba_cc[type%0x100]);
}
reb_integrator_whfast_kepler_step(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI, reb_saba_c[type%0x100][0]*r->dt);
reb_integrator_whfast_com_step(r, saba->p_jh, reb_saba_c[type%0x100][0]*r->dt);
}else{ if (r->is_synchronized){
reb_integrator_whfast_kepler_step(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI, reb_saba_c[type%0x100][0]*r->dt);
reb_integrator_whfast_com_step(r, saba->p_jh, reb_saba_c[type%0x100][0]*r->dt);
}else{
reb_integrator_whfast_kepler_step(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI, 2.*reb_saba_c[type%0x100][0]*r->dt);
reb_integrator_whfast_com_step(r, saba->p_jh, 2.*reb_saba_c[type%0x100][0]*r->dt);
}
}
reb_integrator_whfast_to_inertial(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI);
reb_simulation_update_acceleration(r);
if (saba->p_jh==NULL){
reb_simulation_error(r, "Something went terribly wrong in the SABA integrator: p_jh==NULL.\n");
return;
}
reb_integrator_whfast_interaction_step(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI, reb_saba_d[type%0x100][0]*r->dt);
for(int j=1;j<stages;j++){
{
int i = j;
if (j>stages/2){
i = stages-j;
}
reb_integrator_whfast_kepler_step(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI, reb_saba_c[type%0x100][i]*r->dt);
reb_integrator_whfast_com_step(r, saba->p_jh, reb_saba_c[type%0x100][i]*r->dt);
}
{
int i = j;
if (j>(stages-1)/2){
i = stages-j-1;
}
reb_transformations_jacobi_to_inertial_pos(particles, saba->p_jh, particles, N, N);
reb_simulation_update_acceleration(r);
reb_integrator_whfast_interaction_step(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI, reb_saba_d[type%0x100][i]*r->dt);
}
}
if (saba->type>=0x100){ reb_integrator_whfast_kepler_step(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI, reb_saba_c[type%0x100][0]*r->dt);
reb_integrator_whfast_com_step(r, saba->p_jh, reb_saba_c[type%0x100][0]*r->dt);
}
r->is_synchronized = 0;
if (saba->safe_mode){
reb_integrator_saba_synchronize(r, saba);
}
r->t+=r->dt;
r->dt_last_done = r->dt;
}
void reb_integrator_saba_synchronize(struct reb_simulation* const r, void* state){
struct reb_integrator_saba_state* saba = state;
int type = saba->type;
struct reb_particle* sync_pj = NULL;
if (saba->keep_unsynchronized){
sync_pj = malloc(sizeof(struct reb_particle)*r->N);
memcpy(sync_pj,saba->p_jh,r->N*sizeof(struct reb_particle));
}
if (r->is_synchronized == 0){
const int N = r->N;
if (type>=0x100){ reb_saba_corrector_step(r, saba, reb_saba_cc[type%0x100]);
}else{
reb_integrator_whfast_kepler_step(r, saba->p_jh, REB_INTEGRATOR_WHFAST_COORDINATES_JACOBI, reb_saba_c[type%0x100][0]*r->dt);
reb_integrator_whfast_com_step(r, saba->p_jh, reb_saba_c[type%0x100][0]*r->dt);
}
reb_transformations_jacobi_to_inertial_posvel(r->particles, saba->p_jh, r->particles, N, N);
if (saba->keep_unsynchronized){
memcpy(saba->p_jh,sync_pj,r->N*sizeof(struct reb_particle));
free(sync_pj);
}else{
r->is_synchronized = 1;
}
}
}