#include "rebound.h"
#include "rebound_internal.h"
#include <math.h>
#include <assert.h>
#include "particle.h"
#include "gravity.h"
#include "boundary.h"
#include "integrator_sei.h"
#include "binarydata.h"
struct reb_integrator_sei_state {
double lastdt; double sindt; double tandt; double sindtz; double tandtz; };
void reb_integrator_sei_step(struct reb_simulation* r, void* state);
void* reb_integrator_sei_create();
void reb_integrator_sei_free(void* p);
const struct reb_binarydata_field_descriptor reb_integrator_sei_field_descriptor_list[];
const struct reb_integrator reb_integrator_sei = {
.documentation =
"The Symplectic Epicycle Integrator (SEI), is a mixed variable second order "
"symplectic integrator for the shearing sheet. Its main purpose is to simulate "
"planetary rings in a local corotating frame. See [Rein & Tremaine (2011)] for "
"details. The (vertical) epicyclic frequency `OMEGA` (`OMEGAZ`) needs to be set "
"in the simulation. In previous versions of REBOUND, these variable were defined "
"in the integrator, not the simulation. "
"\n\n"
"[Rein & Tremaine (2011)]: https://ui.adsabs.harvard.edu/abs/2011MNRAS.415.3168R/abstract\n",
.step = reb_integrator_sei_step,
.create = reb_integrator_sei_create,
.free = reb_integrator_sei_free,
.field_descriptor_list = reb_integrator_sei_field_descriptor_list,
};
const struct reb_binarydata_field_descriptor reb_integrator_sei_field_descriptor_list[] = {
{ "", REB_DOUBLE, "lastdt", offsetof(struct reb_integrator_sei_state, lastdt), 0, 0, 0},
{ "", REB_DOUBLE, "sindt", offsetof(struct reb_integrator_sei_state, sindt), 0, 0, 0},
{ "", REB_DOUBLE, "tandt", offsetof(struct reb_integrator_sei_state, tandt), 0, 0, 0},
{ "", REB_DOUBLE, "sindtz", offsetof(struct reb_integrator_sei_state, sindtz), 0, 0, 0},
{ "", REB_DOUBLE, "tandtz", offsetof(struct reb_integrator_sei_state, tandtz), 0, 0, 0},
{ 0 }, };
static void operator_H012(double dt, const struct reb_integrator_sei_state sei, struct reb_particle* p, double OMEGA, double OMEGAZ);
static void operator_phi1(double dt, struct reb_particle* p);
void* reb_integrator_sei_create(){
struct reb_integrator_sei_state* sei = calloc(sizeof(struct reb_integrator_sei_state),1);
return sei;
}
void reb_integrator_sei_free(void* p){
struct reb_integrator_sei_state* sei = p;
free(sei);
}
void reb_integrator_sei_step(struct reb_simulation* const r, void* state){
struct reb_integrator_sei_state* sei = state;
r->gravity_ignore_terms = REB_GRAVITY_IGNORE_TERMS_NONE;
const int N = r->N;
struct reb_particle* const particles = r->particles;
if (sei->lastdt!=r->dt){
if (r->OMEGAZ==-1){
r->OMEGAZ=r->OMEGA;
}
sei->sindt = sin(r->OMEGA*(-r->dt/2.));
sei->tandt = tan(r->OMEGA*(-r->dt/4.));
sei->sindtz = sin(r->OMEGAZ*(-r->dt/2.));
sei->tandtz = tan(r->OMEGAZ*(-r->dt/4.));
sei->lastdt = r->dt;
}
#pragma omp parallel for schedule(guided)
for (int i=0;i<N;i++){
operator_H012(r->dt, *sei, &(particles[i]),r->OMEGA,r->OMEGAZ);
}
r->t+=r->dt/2.;
reb_simulation_update_acceleration(r);
#pragma omp parallel for schedule(guided)
for (int i=0;i<N;i++){
operator_phi1(r->dt, &(particles[i]));
operator_H012(r->dt, *sei, &(particles[i]),r->OMEGA,r->OMEGAZ);
}
r->t+=r->dt/2.;
r->dt_last_done = r->dt;
}
static void operator_H012(double dt, const struct reb_integrator_sei_state ri_sei, struct reb_particle* p, double OMEGA, double OMEGAZ){
const double zx = p->z * OMEGAZ;
const double zy = p->vz;
const double zt1 = zx - ri_sei.tandtz*zy;
const double zyt = ri_sei.sindtz*zt1 + zy;
const double zxt = zt1 - ri_sei.tandtz*zyt;
p->z = zxt/OMEGAZ;
p->vz = zyt;
const double aO = 2.*p->vy + 4.*p->x*OMEGA; const double bO = p->y*OMEGA - 2.*p->vx;
const double ys = (p->y*OMEGA-bO)/2.; const double xs = (p->x*OMEGA-aO);
const double xst1 = xs - ri_sei.tandt*ys;
const double yst = ri_sei.sindt*xst1 + ys;
const double xst = xst1 - ri_sei.tandt*yst;
p->x = (xst+aO) /OMEGA;
p->y = (yst*2.+bO) /OMEGA - 3./4.*aO*dt;
p->vx = yst;
p->vy = -xst*2. -3./2.*aO;
}
static void operator_phi1(double dt, struct reb_particle* p){
p->vx += p->ax * dt;
p->vy += p->ay * dt;
p->vz += p->az * dt;
}