Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encounter Prediction Switching Condition for TRACE #788

Merged
merged 16 commits into from
Aug 15, 2024
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ REBOUND is an N-body integrator, i.e. a software package that can integrate the
* No dependencies on external libraries.
* Runs natively on Linux, MacOS, and Windows.
* Symplectic integrators ([WHFast](integrators/#whfast), [SEI](integrators/#sei), [LEAPFROG](integrators/#leapfrog), [EOS](integrators/#embedded-operator-splitting-method-eos))
* Hybrid symplectic integrators for planetary dynamics with close encounters ([MERCURIUS](integrators/#mercurius))
* Hybrid reversible integrators for planetary dynamics with arbitrary close encounters ([TRACE](integrators/#trace))
* Hybrid symplectic integrators for planetary dynamics with close encounters ([MERCURIUS](integrators/#mercurius))
* High order symplectic integrators for integrating planetary systems ([SABA](integrators/#saba), WH Kernel methods)
* High accuracy non-symplectic integrator with adaptive time-stepping ([IAS15](integrators/#ias15))
* Can integrate arbitrary user-defined ODEs that are coupled to N-body dynamics for tides, spin, etc
Expand Down
2 changes: 1 addition & 1 deletion docs/integrators.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ The `reb_integrator_mercurius` structure contains the configuration and data str

TRACE is a hybrid time-reversible integrator, based on the algorithm described in [Hernandez & Dehnen 2023](https://ui.adsabs.harvard.edu/abs/2023MNRAS.522.4639H/abstract).
It uses WHFast for long term integrations but switches time-reversibly to BS or IAS15 for all close encounters. TRACE is appropriate for systems with a dominant central mass that will occasionally have close encounters.
A paper describing the TRACE implementation is in preparation.
The TRACE implementation is described in [Lu, Hernandez & Rein](https://ui.adsabs.harvard.edu/abs/2024arXiv240503800L/abstract).


The following code enables TRACE and sets the critical radius to 4 Hill radii
Expand Down
1 change: 0 additions & 1 deletion rebound/integrators/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ def __repr__(self):
("_current_C", ctypes.c_uint),
("_force_accept", ctypes.c_uint),
]
# To be honest I'm not sure what these do: do we need this? - Tiger
@property
def S(self):
raise AttributeError("You can only set C function pointers from python.")
Expand Down
13 changes: 13 additions & 0 deletions rebound/tests/test_trace_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ def test_trace_encounter_condition(self):
for j in range(i+1,sim.N):
self.assertEqual(sim.ri_trace._current_Ks[i*sim.N+j],0)

def test_trace_encounter_prediction(self):
sim = rebound.Simulation()
sim.add(m=1)
sim.add(m=9.55e-4,x=5.2)
sim.add(x=5.3,y=0.36,vy=-7.2) # Non-encounter prediction misses this
sim.integrator = "TRACE"
sim.dt = 0.01
sim.ri_trace.r_crit_hill = 1
sim.step()

self.assertEqual(sim.ri_trace._encounter_N,3)



if __name__ == "__main__":
unittest.main()
63 changes: 49 additions & 14 deletions src/integrator_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,40 +41,76 @@

int reb_integrator_trace_switch_default(struct reb_simulation* const r, const unsigned int i, const unsigned int j){
struct reb_integrator_trace* const ri_trace = &(r->ri_trace);
const double h2 = r->dt/2.;

const double dxi = r->particles[i].x;
const double dyi = r->particles[i].y;
const double dzi = r->particles[i].z;

const double dxj = r->particles[j].x;
const double dyj = r->particles[j].y;
const double dzj = r->particles[j].z;

const double dx = dxi - dxj;
const double dy = dyi - dyj;
const double dz = dzi - dzj;
const double rp = dx*dx + dy*dy + dz*dz;

double dcriti6 = 0.0;
double dcritj6 = 0.0;

const double m0 = r->particles[0].m;

if (r->particles[i].m != 0){
const double dxi = r->particles[i].x; // in dh
const double dyi = r->particles[i].y;
const double dzi = r->particles[i].z;
const double di2 = dxi*dxi + dyi*dyi + dzi*dzi;
const double mr = r->particles[i].m/(3.*m0);
dcriti6 = di2*di2*di2*mr*mr;
}

if (r->particles[j].m != 0){
const double dxj = r->particles[j].x; // in dh
const double dyj = r->particles[j].y;
const double dzj = r->particles[j].z;
const double dj2 = dxj*dxj + dyj*dyj + dzj*dzj;
const double mr = r->particles[j].m/(3.*m0);
dcritj6 = dj2*dj2*dj2*mr*mr;
}

const double dx = r->particles[i].x - r->particles[j].x;
const double dy = r->particles[i].y - r->particles[j].y;
const double dz = r->particles[i].z - r->particles[j].z;
const double d2 = dx*dx + dy*dy + dz*dz;

double r_crit_hill2 = ri_trace->r_crit_hill*ri_trace->r_crit_hill;
double dcritmax6 = r_crit_hill2 * r_crit_hill2 * r_crit_hill2 * MAX(dcriti6,dcritj6);

return d2*d2*d2 < dcritmax6;
if (rp*rp*rp < dcritmax6) return 1;

const double dvx = r->particles[i].vx - r->particles[j].vx;
const double dvy = r->particles[i].vy - r->particles[j].vy;
const double dvz = r->particles[i].vz - r->particles[j].vz;
const double v2 = dvx*dvx + dvy*dvy + dvz*dvz;

const double qv = dx*dvx + dy*dvy + dz*dvz;
int d;

if (qv == 0.0){ // Small
// minimum is at present, which is already checked for
return 0;
}
else if (qv < 0){
d = 1;
}
else{
d = -1;
}

double dmin2;
double tmin = -d*qv/v2;
if (tmin < h2){
// minimum is in the window
dmin2 = rp - qv*qv/v2;
}
else{
dmin2 = rp + 2*d*qv*h2 + v2*h2*h2;
}

return dmin2*dmin2*dmin2 < dcritmax6;
}


int reb_integrator_trace_switch_peri_distance(struct reb_simulation* const r, const unsigned int j){
const struct reb_integrator_trace* const ri_trace = &(r->ri_trace);
const double peri = ri_trace->peri_crit_distance;
Expand Down Expand Up @@ -519,9 +555,9 @@ void reb_integrator_trace_part1(struct reb_simulation* r){
// These arrays are only used within one timestep.
// Can be recreated without loosing bit-wise reproducibility.
ri_trace->particles_backup = realloc(ri_trace->particles_backup,sizeof(struct reb_particle)*N);
ri_trace->particles_backup_kepler = realloc(ri_trace->particles_backup_kepler,sizeof(struct reb_particle)*N);
ri_trace->current_Ks = realloc(ri_trace->current_Ks,sizeof(int)*N*N);
ri_trace->encounter_map = realloc(ri_trace->encounter_map,sizeof(int)*N);
ri_trace->particles_backup_kepler = realloc(ri_trace->particles_backup_kepler,sizeof(struct reb_particle)*N);
ri_trace->N_allocated = N;
}

Expand Down Expand Up @@ -842,7 +878,6 @@ void reb_integrator_trace_reset(struct reb_simulation* r){
free(r->ri_trace.particles_backup_additional_forces);
r->ri_trace.particles_backup_additional_forces = NULL;


free(r->ri_trace.encounter_map);
r->ri_trace.encounter_map = NULL;

Expand Down
Loading