SlowDown Algorithmic Regularization (SDAR)
Algorithmic Regularization with slowdown method for integrating few-body motions
hermite_integrator.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "Common/Float.h"
4 #include "Common/list.h"
9 #include "Hermite/neighbor.h"
10 #include "Hermite/profile.h"
11 #include <map>
12 
13 namespace H4{
14 
16  void printFeatures(std::ostream & fout) {
17 #ifdef ADJUST_GROUP_PRINT
18  fout<<"Print adjust group information\n";
19 #endif
20  }
21 
23  void printDebugFeatures(std::ostream & fout) {
24 #ifdef HERMITE_DEBUG
25  fout<<"Debug mode: HERMITE\n";
26 #endif
27  }
28 
30  template <class Tmethod>
32  public:
33  //Float r_break_crit; ///> the distance criterion to break the groups
34  //Float r_neighbor_crit; ///> the distance for neighbor search
35  Tmethod interaction;
37 #ifdef ADJUST_GROUP_PRINT
38  bool adjust_group_write_flag;
39  std::ofstream fgroup;
40 #endif
41 
42 #ifdef ADJUST_GROUP_PRINT
43  HermiteManager(): interaction(), step(), adjust_group_write_flag(true), fgroup() {}
44 #endif
45 
46 
48 
50  bool checkParams() {
51  //ASSERT(r_break_crit>=0.0);
52  //ASSERT(r_neighbor_crit>=0.0);
53  ASSERT(interaction.checkParams());
54  ASSERT(step.checkParams());
55 #ifdef ADJUST_GROUP_PRINT
56  ASSERT(!adjust_group_write_flag||(adjust_group_write_flag&&fgroup.is_open()));
57 #endif
58  return true;
59  }
60 
62  void print(std::ostream & _fout) const{
63  //_fout<<"r_break_crit : "<<r_break_crit<<std::endl
64  //<<"r_neighbor_crit : "<<r_neighbor_crit<<std::endl;
65  interaction.print(_fout);
66  step.print(_fout);
67  }
68 
69 
71 
75  void printColumnTitle(std::ostream & _fout, const int _width=20) {
76  interaction.printColumnTitle(_fout, _width);
77  step.printColumnTitle(_fout, _width);
78  }
79 
81 
85  void printColumn(std::ostream & _fout, const int _width=20){
86  interaction.printColumn(_fout, _width);
87  step.printColumn(_fout, _width);
88  }
89 
91 
93  void writeBinary(FILE *_fp) const {
94  //size_t size = sizeof(*this) - sizeof(interaction) - sizeof(step);
95  //fwrite(this, size, 1, _fp);
96  interaction.writeBinary(_fp);
97  step.writeBinary(_fp);
98 #ifdef ADJUST_GROUP_PRINT
99  fwrite(&adjust_group_write_flag, sizeof(bool),1,_fp);
100 #endif
101  }
102 
104 
106  void readBinary(FILE *_fin) {
107  //size_t size = sizeof(*this) - sizeof(interaction) - sizeof(step);
108  //size_t rcount = fread(this, size, 1, _fin);
109  //if (rcount<1) {
110  // std::cerr<<"Error: Data reading fails! requiring data number is 1, only obtain "<<rcount<<".\n";
111  // abort();
112  //}
113  interaction.readBinary(_fin);
114  step.readBinary(_fin);
115 #ifdef ADJUST_GROUP_PRINT
116  size_t rcount = fread(&adjust_group_write_flag, sizeof(bool),1,_fin);
117  if (rcount<1) {
118  std::cerr<<"Error: Data reading fails! requiring data number is 1, only obtain "<<rcount<<".\n";
119  abort();
120  }
121 #endif
122  }
123 
124  };
125 
128  public:
136 
137  HermiteEnergy(): etot_ref(0.0), ekin(0.0), epot(0.0), epert(0.0), de_cum(0.0), de_binary_interrupt(0.0), de_modify_single(0.0) {}
138 
140  void clear() {
141  etot_ref = ekin = epot = epert = 0.0;
143  }
144 
147  return ekin + epot + epert - etot_ref;
148  }
149 
151  Float getEtot() const {
152  return ekin + epot + epert;
153  }
154 
156 
160  void printColumnTitle(std::ostream & _fout, const int _width=20) {
161  _fout<<std::setw(_width)<<"dE"
162  <<std::setw(_width)<<"Etot_ref"
163  <<std::setw(_width)<<"Ekin"
164  <<std::setw(_width)<<"Epot"
165  <<std::setw(_width)<<"Epert"
166  <<std::setw(_width)<<"dE_cum"
167  <<std::setw(_width)<<"dE_intr"
168  <<std::setw(_width)<<"dE_mod";
169  }
170 
172 
176  void printColumn(std::ostream & _fout, const int _width=20){
177  _fout<<std::setw(_width)<<getEnergyError()
178  <<std::setw(_width)<<etot_ref
179  <<std::setw(_width)<<ekin
180  <<std::setw(_width)<<epot
181  <<std::setw(_width)<<epert
182  <<std::setw(_width)<<de_cum
183  <<std::setw(_width)<<de_binary_interrupt
184  <<std::setw(_width)<<de_modify_single;
185  }
186  };
187 
189  template <class Tparticle, class Tpcm, class Tpert, class TARpert, class Tacc, class TARacc, class Tinfo>
191  private:
194 
195  // time
196  Float time_;
197  Float time_offset_;
198  Float time_next_min_;
199  Float dt_limit_; // maximum step size allown for next integration step calculation
200 
201  // Energy
202  Float energy_init_ref_; // initial energy reference
203  HermiteEnergy energy_; // true energy
204  HermiteEnergy energy_sd_; // slowdown energy
205 
206  // active particle number
207  int n_act_single_;
208  int n_act_group_;
209 
210  // initial particle number
211  int n_init_single_;
212  int n_init_group_;
213 
214  // group offset
215  int index_offset_group_;
216 
217  // interrupt group
218  int interrupt_group_dt_sorted_group_index_;
219  AR::InterruptBinary<Tparticle> interrupt_binary_;
220  COMM::List<int> index_group_merger_;
221 
222  // flags
223  bool initial_system_flag_;
224  bool modify_system_flag_;
225 
226  // arrays
227  // sorted time index list for select active particles
228  COMM::List<int> index_dt_sorted_single_; // index of particles with time next sorted order (small to large)
229  COMM::List<int> index_dt_sorted_group_; // index list of active groups
230 
231  // index list to store the groups with members resolved/only cm in Hermite integration
232  COMM::List<int> index_group_resolve_; // index of resolved group for integration
233  COMM::List<int> index_group_cm_; // index of group with cm for integration
234 
235  // particle prediction, force, time array
236  COMM::List<Tparticle> pred_; // predictor
237  COMM::List<ForceH4> force_; // force
238  COMM::List<Float> time_next_; // next integrated time of particles
239 
240  // table of mask to show which particles are removed from the integration
241  COMM::List<int> index_group_mask_; // index list to record the masked groups
242  COMM::List<bool> table_group_mask_; // bool mask to indicate whether the particle of index (group) (same index of table) is masked (true) or used (false)
243  COMM::List<bool> table_single_mask_; // bool mask to indicate whether the particle of index (single) (same index of table) is masked (true) or used (false)
244 
245  public:
250  COMM::List<ARSym> groups; // integrator for sub-groups
251  COMM::List<Neighbor<Tparticle>> neighbors; // neighbor information of particles
252  Tpert perturber; // external perturber
253  Tinfo info;
254  Profile profile; // profile to measure the status
255 
257 
259  bool checkParams() {
260  ASSERT(manager!=NULL);
261  ASSERT(manager->checkParams());
262  ASSERT(ar_manager!=NULL);
263  ASSERT(ar_manager->checkParams());
264  ASSERT(perturber.checkParams());
265  ASSERT(info.checkParams());
266  return true;
267  }
268 
270  HermiteIntegrator(): time_(0.0), time_offset_(0.0), time_next_min_(0.0), dt_limit_(0.0),
271  energy_init_ref_(0.0),
272  energy_(), energy_sd_(),
273  n_act_single_(0), n_act_group_(0),
274  n_init_single_(0), n_init_group_(0),
275  index_offset_group_(0),
276  interrupt_group_dt_sorted_group_index_(-1), interrupt_binary_(), index_group_merger_(),
277  initial_system_flag_(false), modify_system_flag_(false),
278  index_dt_sorted_single_(), index_dt_sorted_group_(),
279  index_group_resolve_(), index_group_cm_(),
280  pred_(), force_(), time_next_(),
281  index_group_mask_(), table_group_mask_(), table_single_mask_(), step(),
282  manager(NULL), ar_manager(NULL), particles(), groups(), neighbors(), perturber(), info(), profile() {}
283 
285  void clear() {
286  time_ = 0.0;
287  time_offset_ = 0.0;
288  time_next_min_ = 0.0;
289  dt_limit_ = 0.0;
290  energy_init_ref_ = 0.0;
291  energy_.clear();
292  energy_sd_.clear();
293  n_act_single_ = n_act_group_ = n_init_single_ = n_init_group_ = 0;
294  index_offset_group_ = 0;
295  interrupt_group_dt_sorted_group_index_ = -1;
296  interrupt_binary_.clear();
297  index_group_merger_.clear();
298  initial_system_flag_ = false;
299  modify_system_flag_ = false;
300 
301  particles.clear();
302  groups.clear();
303  index_dt_sorted_single_.clear();
304  index_dt_sorted_group_.clear();
305  index_group_resolve_.clear();
306  index_group_cm_.clear();
307  index_group_mask_.clear();
308  table_group_mask_.clear();
309  table_single_mask_.clear();
310  pred_.clear();
311  force_.clear();
312  time_next_.clear();
313  neighbors.clear();
314  perturber.clear();
315  info.clear();
316  profile.clear();
317  }
318 
320 
323  // reserve memory
324  const int nmax = particles.getSizeMax();
325  const int nmax_group = groups.getSizeMax();
326  const int nmax_tot = nmax + nmax_group;
327  ASSERT(nmax>0);
328 
329  index_dt_sorted_single_.setMode(COMM::ListMode::local);
330  index_dt_sorted_group_.setMode(COMM::ListMode::local);
331  index_group_merger_.setMode(COMM::ListMode::local);
332  index_group_resolve_.setMode(COMM::ListMode::local);
333  index_group_cm_.setMode(COMM::ListMode::local);
334  index_group_mask_.setMode(COMM::ListMode::local);
335  table_group_mask_.setMode(COMM::ListMode::local);
336  table_single_mask_.setMode(COMM::ListMode::local);
339  time_next_.setMode(COMM::ListMode::local);
341 
342  index_group_merger_.reserveMem(nmax_group);
343 
344  index_dt_sorted_single_.reserveMem(nmax);
345  index_dt_sorted_group_.reserveMem(nmax_group);
346 
347  index_group_resolve_.reserveMem(nmax_group);
348  index_group_cm_.reserveMem(nmax_group);
349 
350  index_group_mask_.reserveMem(nmax_group);
351  table_group_mask_.reserveMem(nmax_group);
352  table_single_mask_.reserveMem(nmax);
353 
354  // total allocation size includes both single and group c.m. size
355  index_offset_group_ = nmax;
356  pred_.reserveMem(nmax_tot);
357  force_.reserveMem(nmax_tot);
358  time_next_.reserveMem(nmax_tot);
359 
360  // reserve for neighbor list
361  neighbors.reserveMem(nmax);
362  auto* nb_ptr = neighbors.getDataAddress();
363  for (int i=0; i<nmax; i++) {
364  nb_ptr[i].neighbor_address.setMode(COMM::ListMode::local);
365  nb_ptr[i].neighbor_address.reserveMem(nmax_tot);
366  }
367  }
368 
369  private:
371 
378  void calcDt2ndList(const int* _index_single,
379  const int _n_single,
380  const int* _index_group,
381  const int _n_group,
382  const Float _dt_limit) {
383  ASSERT(manager!=NULL);
384  // for single
385  for (int i=0; i<_n_single; i++){
386  const int k = _index_single[i];
387  const Float* acc0 = force_[k].acc0;
388  const Float* acc1 = force_[k].acc1;
389  const Float dt =step.calcBlockDt2nd(acc0, acc1, _dt_limit);
390  particles[k].dt = dt;
391  neighbors[k].initial_step_flag = false;
392  }
393  // for group
394  for (int i=0; i<_n_group; i++) {
395  const int k = _index_group[i];
396  const int kf = k + index_offset_group_;
397  const Float* acc0 = force_[kf].acc0;
398  const Float* acc1 = force_[kf].acc1;
399  const Float dt =step.calcBlockDt2nd(acc0, acc1, std::min(_dt_limit, groups[k].info.dt_limit));
400  groups[k].particles.cm.dt = dt;
401  groups[k].perturber.initial_step_flag = false;
402  }
403  }
404 
405 
406 
408 
410  void predictAll(const Float _time_pred) {
411  static thread_local const Float inv3 = 1.0 / 3.0;
412  // single
413  const int n_single = index_dt_sorted_single_.getSize();
414  ASSERT(n_single <= particles.getSize());
415  const auto* ptcl = particles.getDataAddress();
416  auto* pred = pred_.getDataAddress();
417  for (int k=0; k<n_single; k++){
418  const int i = index_dt_sorted_single_[k];
419  const Float dt = _time_pred - ptcl[i].time;
420  ASSERT(i<particles.getSize());
421  ASSERT(i>=0);
422  pred[i].pos[0] = ptcl[i].pos[0] + dt*(ptcl[i].vel[0] + 0.5*dt*(ptcl[i].acc0[0] + inv3*dt*ptcl[i].acc1[0]));
423  pred[i].pos[1] = ptcl[i].pos[1] + dt*(ptcl[i].vel[1] + 0.5*dt*(ptcl[i].acc0[1] + inv3*dt*ptcl[i].acc1[1]));
424  pred[i].pos[2] = ptcl[i].pos[2] + dt*(ptcl[i].vel[2] + 0.5*dt*(ptcl[i].acc0[2] + inv3*dt*ptcl[i].acc1[2]));
425 
426  pred[i].vel[0] = ptcl[i].vel[0] + dt*(ptcl[i].acc0[0] + 0.5*dt*ptcl[i].acc1[0]);
427  pred[i].vel[1] = ptcl[i].vel[1] + dt*(ptcl[i].acc0[1] + 0.5*dt*ptcl[i].acc1[1]);
428  pred[i].vel[2] = ptcl[i].vel[2] + dt*(ptcl[i].acc0[2] + 0.5*dt*ptcl[i].acc1[2]);
429 
430  pred[i].mass = ptcl[i].mass;
431  }
432  // group
433  const int n_group = index_dt_sorted_group_.getSize();
434  ASSERT(n_group <= groups.getSize());
435  auto* group_ptr = groups.getDataAddress();
436  for (int k=0; k<n_group; k++) {
437  const int i = index_dt_sorted_group_[k];
438  ASSERT(i<groups.getSize());
439  ASSERT(i>=0);
440  const auto& pcm = group_ptr[i].particles.cm;
441  const Float dt = _time_pred - pcm.time;
442  ASSERT(dt>=0.0);
443  // group predictor is after single predictor with offset n_single
444  auto& predcm = pred[i+index_offset_group_];
445  predcm.pos[0] = pcm.pos[0] + dt*(pcm.vel[0] + 0.5*dt*(pcm.acc0[0] + inv3*dt*pcm.acc1[0]));
446  predcm.pos[1] = pcm.pos[1] + dt*(pcm.vel[1] + 0.5*dt*(pcm.acc0[1] + inv3*dt*pcm.acc1[1]));
447  predcm.pos[2] = pcm.pos[2] + dt*(pcm.vel[2] + 0.5*dt*(pcm.acc0[2] + inv3*dt*pcm.acc1[2]));
448 
449  predcm.vel[0] = pcm.vel[0] + dt*(pcm.acc0[0] + 0.5*dt*pcm.acc1[0]);
450  predcm.vel[1] = pcm.vel[1] + dt*(pcm.acc0[1] + 0.5*dt*pcm.acc1[1]);
451  predcm.vel[2] = pcm.vel[2] + dt*(pcm.acc0[2] + 0.5*dt*pcm.acc1[2]);
452 
453  predcm.mass = pcm.mass;
454  }
455  }
456 
458 
464  void correctAndCalcDt4thOne(H4Ptcl& _pi, ForceH4& _fi, const Float _dt_limit, const bool _init_step_flag) {
465  static thread_local const Float inv3 = 1.0 / 3.0;
466  const Float dt = _pi.dt;
467  const Float h = 0.5 * dt;
468  const Float hinv = 2.0 / dt;
469  const Float A0p[3] = {_fi.acc0[0] + _pi.acc0[0], _fi.acc0[1] + _pi.acc0[1], _fi.acc0[2] + _pi.acc0[2]};
470  const Float A0m[3] = {_fi.acc0[0] - _pi.acc0[0], _fi.acc0[1] - _pi.acc0[1], _fi.acc0[2] - _pi.acc0[2]};
471  const Float A1p[3] = {(_fi.acc1[0] + _pi.acc1[0])*h, (_fi.acc1[1] + _pi.acc1[1])*h, (_fi.acc1[2] + _pi.acc1[2])*h};
472  const Float A1m[3] = {(_fi.acc1[0] - _pi.acc1[0])*h, (_fi.acc1[1] - _pi.acc1[1])*h, (_fi.acc1[2] - _pi.acc1[2])*h};
473 
474  const Float vel_new[3] = {_pi.vel[0] + h*( A0p[0] - inv3*A1m[0] ),
475  _pi.vel[1] + h*( A0p[1] - inv3*A1m[1] ),
476  _pi.vel[2] + h*( A0p[2] - inv3*A1m[2] )};
477  _pi.pos[0] += h*( (_pi.vel[0] + vel_new[0]) + h*(-inv3*A0m[0]));
478  _pi.pos[1] += h*( (_pi.vel[1] + vel_new[1]) + h*(-inv3*A0m[1]));
479  _pi.pos[2] += h*( (_pi.vel[2] + vel_new[2]) + h*(-inv3*A0m[2]));
480 
481  _pi.vel[0] = vel_new[0];
482  _pi.vel[1] = vel_new[1];
483  _pi.vel[2] = vel_new[2];
484 
485  _pi.acc0[0] = _fi.acc0[0];
486  _pi.acc0[1] = _fi.acc0[1];
487  _pi.acc0[2] = _fi.acc0[2];
488 
489  _pi.acc1[0] = _fi.acc1[0];
490  _pi.acc1[1] = _fi.acc1[1];
491  _pi.acc1[2] = _fi.acc1[2];
492 
493  _pi.time += dt;
494 
495  const Float acc3[3] = {(1.5*hinv*hinv*hinv) * (A1p[0] - A0m[0]),
496  (1.5*hinv*hinv*hinv) * (A1p[1] - A0m[1]),
497  (1.5*hinv*hinv*hinv) * (A1p[2] - A0m[2])};
498  const Float acc2[3] = {(0.5*hinv*hinv) * A1m[0] + h*acc3[0],
499  (0.5*hinv*hinv) * A1m[1] + h*acc3[1],
500  (0.5*hinv*hinv) * A1m[2] + h*acc3[2]};
501 
502 #ifdef HERMITE_DEBUG_ACC
503  _pi.acc2[0] = acc2[0];
504  _pi.acc2[1] = acc2[1];
505  _pi.acc2[2] = acc2[2];
506 
507  _pi.acc3[0] = acc3[0];
508  _pi.acc3[1] = acc3[1];
509  _pi.acc3[2] = acc3[2];
510 #endif
511 
512  _pi.pot = _fi.pot;
513 
514  const Float dt_old = _pi.dt;
515  if(_init_step_flag) {
516  _pi.dt = step.calcBlockDt2nd(_pi.acc0, _pi.acc1, _dt_limit);
517 #ifdef HERMITE_DEBUG
518  std::cerr<<"Initial step flag on: pi.id: "<<_pi.id<<" step size: "<<_pi.dt<<" time: "<<_pi.time<<std::endl;
519 #endif
520  }
521  else _pi.dt = step.calcBlockDt4th(_pi.acc0, _pi.acc1, acc2, acc3, _dt_limit);
522 
523  ASSERT((dt_old > 0.0 && _pi.dt >0.0));
524  (void)dt_old;
525  }
526 
528 
535  void correctAndCalcDt4thList(const int* _index_single,
536  const int _n_single,
537  const int* _index_group,
538  const int _n_group,
539  const Float _dt_limit) {
540  ASSERT(_n_single<=index_dt_sorted_single_.getSize());
541  ASSERT(_n_group<=index_dt_sorted_group_.getSize());
542 
543  // single
544  auto* ptcl = particles.getDataAddress();
545  ForceH4* force = force_.getDataAddress();
546  for(int i=0; i<_n_single; i++){
547  const int k = _index_single[i];
548  correctAndCalcDt4thOne(ptcl[k], force[k], _dt_limit, neighbors[k].initial_step_flag);
549  neighbors[k].initial_step_flag = false;
550  }
551  // group
552  auto* group_ptr = groups.getDataAddress();
553  for(int i=0; i<_n_group; i++) {
554  const int k = _index_group[i];
555  auto& groupi = group_ptr[k];
556  correctAndCalcDt4thOne(groupi.particles.cm, force[k+index_offset_group_], std::min(_dt_limit, groupi.info.dt_limit), groupi.perturber.initial_step_flag);
557  groupi.perturber.initial_step_flag = false;
558  //groupi.correctCenterOfMassDrift();
559  }
560  }
561 
563 
565  void checkGroupResolve(const int _n_group) {
566  if(_n_group==0) return;
567  //AR::SlowDown sd;
568  ASSERT(_n_group<=index_dt_sorted_group_.getSize());
569  for (int i=0; i<_n_group; i++) {
570  const int k = index_dt_sorted_group_[i];
571  auto& groupk = groups[k];
572  // remove soft perturbation
573  //sd.initialSlowDownReference(groupk.slowdown.getSlowDownFactorReference(), groupk.slowdown.getSlowDownFactorMax());
574  //const Float pert_out = std::max(0.0, groupk.slowdown.getPertOut()-groupk.perturber.soft_pert_min);
575  //const Float pert_in= groupk.slowdown.getPertIn();
576  //const Float kappa = sd.calcSlowDownFactor(pert_in, pert_out);
577  // calculate non soft perturbation
578  groupk.perturber.checkGroupResolve();
579  }
580  }
581 
583 
586  void writeBackResolvedGroupAndCreateJParticleList(const bool _write_back_flag) {
587  index_group_resolve_.resizeNoInitialize(0);
588  index_group_cm_.resizeNoInitialize(0);
589  const int n_group_org = index_dt_sorted_group_.getSize();
590  auto* group_ptr = groups.getDataAddress();
591  auto* ptcl = pred_.getDataAddress();
592  for (int i=0; i<n_group_org; i++) {
593  int k = index_dt_sorted_group_[i];
594  ASSERT(k<groups.getSize());
595  ASSERT(k>=0);
596  auto& group_k = group_ptr[k];
597  if (group_k.perturber.need_resolve_flag) {
598  if (_write_back_flag) group_k.writeBackSlowDownParticles(ptcl[k+index_offset_group_]);
599  index_group_resolve_.addMember(k);
600  }
601  else index_group_cm_.addMember(k);
602  }
603  }
604 
606 
618  void insertParticleIndexToGroup(const int _i,
619  const int _j,
620  int* _used_mask,
621  int* _new_group_particle_index_origin,
622  int* _new_n_group_offset,
623  int& _new_n_particle,
624  int& _new_n_group,
625  int* _break_group_index_with_offset,
626  int& _n_break_no_add,
627  const int _n_break) {
628  int insert_group=-1, insert_index=-1;
629  if(_used_mask[_i]>=0) {
630  insert_group = _used_mask[_i];
631  insert_index = _j;
632  }
633  else if(_used_mask[_j]>=0) {
634  insert_group = _used_mask[_j];
635  insert_index = _i;
636  }
637  // the case of merging group
638  if(insert_group>=0) {
639  // check insert particle number
640  int insert_n = 1;
641  if (insert_index>=index_offset_group_) insert_n = groups[insert_index-index_offset_group_].particles.getSize();
642 
643  // shift the first insert_n index in last group to last
644  int last_group_offset = _new_n_group_offset[_new_n_group-1];
645  for (int j=0; j<insert_n; j++)
646  _new_group_particle_index_origin[_new_n_particle++] = _new_group_particle_index_origin[last_group_offset+j];
647 
648  // in the case insert_group is not the last group, shift the offset and also the first insert_n index of each group into right group.
649  if(insert_group<_new_n_group-1) {
650  for (int k=_new_n_group-1; k>insert_group; k--) {
651  int k0_group_offset = _new_n_group_offset[k-1];
652  for (int j=0; j<insert_n; j++)
653  _new_group_particle_index_origin[_new_n_group_offset[k]++] = _new_group_particle_index_origin[k0_group_offset+j];
654  }
655  }
656  // replace the first position of insert_group with insert ptcl
657  int insert_group_offset=_new_n_group_offset[insert_group];
658  if(insert_n>1) {
659  auto& group = groups[insert_index-index_offset_group_];
660  for (int j=0; j<insert_n; j++)
661  _new_group_particle_index_origin[insert_group_offset++] = group.info.particle_index[j];
662  // add group index to break list
663  _break_group_index_with_offset[_n_break+_n_break_no_add] = insert_index;
664  _n_break_no_add++;
665  }
666  else _new_group_particle_index_origin[insert_group_offset] = insert_index;
667  _used_mask[insert_index] = insert_group;
668  }
669  else { // new group case
670  _new_n_group_offset[_new_n_group] = _new_n_particle;
671  if (_i>=index_offset_group_) {
672  auto& group = groups[_i-index_offset_group_];
673  int insert_n = group.particles.getSize();
674  for (int j=0; j<insert_n; j++)
675  _new_group_particle_index_origin[_new_n_particle++] = group.info.particle_index[j];
676  // add group index to break list
677  _break_group_index_with_offset[_n_break+_n_break_no_add] = _i;
678  _n_break_no_add++;
679  }
680  else _new_group_particle_index_origin[_new_n_particle++] = _i;
681  // used mask regist the current staying group of particle _i
682  _used_mask[_i] = _new_n_group;
683 
684  if (_j>=index_offset_group_) {
685  auto& group = groups[_j-index_offset_group_];
686  int insert_n = group.particles.getSize();
687  for (int j=0; j<insert_n; j++)
688  _new_group_particle_index_origin[_new_n_particle++] = group.info.particle_index[j];
689  // add group index to break list
690  _break_group_index_with_offset[_n_break+_n_break_no_add] = _j;
691  _n_break_no_add++;
692  }
693  else _new_group_particle_index_origin[_new_n_particle++] = _j;
694  // used mask regist the current staying group of particle _j
695  _used_mask[_j] = _new_n_group;
696 
697  _new_n_group++;
698  }
699  }
700 
702 
708  template <class Tpi>
709  inline void calcOneSingleAccJerkNB(ForceH4 &_fi,
710  Neighbor<Tparticle> &_nbi,
711  const Tpi &_pi,
712  const int _pid) {
713  // clear force
714  _fi.clear();
715  _nbi.resetNeighbor();
716 
717  // single list
718  const int* single_list = index_dt_sorted_single_.getDataAddress();
719  const int n_single = index_dt_sorted_single_.getSize();
720  auto* ptcl = pred_.getDataAddress();
721  for (int i=0; i<n_single; i++) {
722  const int j = single_list[i];
723  ASSERT(j<pred_.getSize());
724  const auto& pj = ptcl[j];
725  if (_pid==pj.id) continue;
726  if (pj.mass==0) continue;
727  Float r2 = manager->interaction.calcAccJerkPairSingleSingle(_fi, _pi, pj);
728  ASSERT(r2>0.0);
729  _nbi.checkAndAddNeighborSingle(r2, particles[j], neighbors[j], j);
730  }
731 
732  auto* group_ptr = groups.getDataAddress();
733 
734  // resolved group list
735  const int n_group_resolve = index_group_resolve_.getSize();
736  for (int i=0; i<n_group_resolve; i++) {
737  const int j =index_group_resolve_[i];
738  auto& groupj = group_ptr[j];
739  if (_pid==groupj.particles.cm.id) continue;
740  if (groupj.particles.cm.mass==0) continue;
741  Float r2 = manager->interaction.calcAccJerkPairSingleGroupMember(_fi, _pi, groupj);
742  ASSERT(r2>0.0);
743  _nbi.checkAndAddNeighborGroup(r2, groupj, j+index_offset_group_);
744  }
745 
746  // cm group list
747  const int n_group_cm = index_group_cm_.getSize();
748  for (int i=0; i<n_group_cm; i++) {
749  const int j = index_group_cm_[i];
750  auto& groupj = group_ptr[j];
751  if (_pid==groupj.particles.cm.id) continue;
752  if (groupj.particles.cm.mass==0) continue;
753  // used predicted particle instead of original cm
754  const auto& pj = ptcl[j+index_offset_group_];
755  ASSERT(j+index_offset_group_<pred_.getSize());
756  ASSERT(_pi.id!=pj.id);
757  Float r2 = manager->interaction.calcAccJerkPairSingleGroupCM(_fi, _pi, groupj, pj);
758  ASSERT(r2>0.0);
759  _nbi.checkAndAddNeighborGroup(r2, groupj, j+index_offset_group_);
760  }
761  ASSERT(_nbi.n_neighbor_group + _nbi.n_neighbor_single == _nbi.neighbor_address.getSize());
762 
763 #ifdef HERMITE_PERT_FORCE
764  // perturber
765  manager->interaction.calcAccJerkPerturber(_fi, _pi, particles.cm, perturber);
766 #endif
767  }
768 
770 
775  template <class Tpi, class Tgroupi>
776  inline void calcOneGroupCMAccJerkNB(ForceH4 &_fi,
777  Tgroupi &_groupi,
778  const Tpi &_pi) {
779  // clear force
780  _fi.clear();
781  auto& nbi = _groupi.perturber;
782  nbi.resetNeighbor();
783 
784  // single list
785  const int* single_list = index_dt_sorted_single_.getDataAddress();
786  const int n_single = index_dt_sorted_single_.getSize();
787  auto* ptcl = pred_.getDataAddress();
788  for (int i=0; i<n_single; i++) {
789  const int j = single_list[i];
790  ASSERT(j<pred_.getSize());
791  const auto& pj = ptcl[j];
792  if (_pi.id==pj.id) continue;
793  if (pj.mass==0.0) continue;
794  Float r2 = manager->interaction.calcAccJerkPairGroupCMSingle(_fi, _groupi, _pi, pj);
795  ASSERT(r2>0.0);
796  nbi.checkAndAddNeighborSingle(r2, particles[j], neighbors[j], j);
797  }
798 
799  auto* group_ptr = groups.getDataAddress();
800 
801  // resolved group list
802  const int n_group_resolve = index_group_resolve_.getSize();
803  for (int i=0; i<n_group_resolve; i++) {
804  const int j =index_group_resolve_[i];
805  auto& groupj = group_ptr[j];
806  if(_pi.id==groupj.particles.cm.id) continue;
807  if (groupj.particles.cm.mass==0.0) continue;
808  Float r2 = manager->interaction.calcAccJerkPairGroupCMGroupMember(_fi, _groupi, _pi, groupj);
809  ASSERT(r2>0.0);
810  nbi.checkAndAddNeighborGroup(r2, groupj, j+index_offset_group_);
811  }
812 
813  // cm group list
814  const int n_group_cm = index_group_cm_.getSize();
815  for (int i=0; i<n_group_cm; i++) {
816  const int j = index_group_cm_[i];
817  auto& groupj = group_ptr[j];
818  if (_pi.id==groupj.particles.cm.id) continue;
819  if (groupj.particles.cm.mass==0.0) continue;
820  // used predicted particle instead of original cm
821  const auto& pj = ptcl[j+index_offset_group_];
822  ASSERT(j+index_offset_group_<pred_.getSize());
823  Float r2 = manager->interaction.calcAccJerkPairGroupCMGroupCM(_fi, _groupi, _pi, groupj, pj);
824  ASSERT(r2>0.0);
825  nbi.checkAndAddNeighborGroup(r2, groupj, j+index_offset_group_);
826  }
827  ASSERT(nbi.n_neighbor_group + nbi.n_neighbor_single == nbi.neighbor_address.getSize());
828 
829 #ifdef HERMITE_PERT_FORCE
830  // perturber
831  manager->interaction.calcAccJerkPerturber(_fi, _pi, particles.cm, perturber);
832 #endif
833  }
834 
836 
841  template <class Tpi, class Tgroupi>
842  inline void calcOneGroupMemberAccJerkNB(ForceH4 &_fi,
843  Tgroupi &_groupi,
844  const Tpi &_pi) {
845  auto& nbi = _groupi.perturber;
846  nbi.resetNeighbor();
847 
848  // only get neighbors
849  // single list
850  const int* single_list = index_dt_sorted_single_.getDataAddress();
851  const int n_single = index_dt_sorted_single_.getSize();
852  auto* ptcl = pred_.getDataAddress();
853  for (int i=0; i<n_single; i++) {
854  const int j = single_list[i];
855  ASSERT(j<pred_.getSize());
856  const auto& pj = ptcl[j];
857  if (pj.mass==0.0) continue;
858  ASSERT(_pi.id!=pj.id);
859  Float r2 = manager->interaction.calcR2Pair(_pi, pj);
860  ASSERT(r2>0.0);
861  nbi.checkAndAddNeighborSingle(r2, particles[j], neighbors[j], j);
862  }
863 
864  auto* group_ptr = groups.getDataAddress();
865 
866  // resolved group list
867  const int n_group_resolve = index_group_resolve_.getSize();
868  for (int i=0; i<n_group_resolve; i++) {
869  const int j =index_group_resolve_[i];
870  // used predicted particle instead of original cm
871  const auto& pj = ptcl[j+index_offset_group_];
872  auto& groupj = group_ptr[j];
873  if(_pi.id==pj.id) continue;
874  if (pj.mass==0.0) continue;
875  Float r2 = manager->interaction.calcR2Pair(_pi, pj);
876  ASSERT(r2>0.0);
877  nbi.checkAndAddNeighborGroup(r2, groupj, j+index_offset_group_);
878  }
879 
880  // cm group list
881  const int n_group_cm = index_group_cm_.getSize();
882  for (int i=0; i<n_group_cm; i++) {
883  const int j = index_group_cm_[i];
884  auto& groupj = group_ptr[j];
885  // used predicted particle instead of original cm
886  const auto& pj = ptcl[j+index_offset_group_];
887  if (pj.mass==0.0) continue;
888  ASSERT(_pi.id!=pj.id);
889  ASSERT(j+index_offset_group_<pred_.getSize());
890  Float r2 = manager->interaction.calcR2Pair(_pi, pj);
891  ASSERT(r2>0.0);
892  nbi.checkAndAddNeighborGroup(r2, groupj, j+index_offset_group_);
893  }
894  ASSERT(nbi.n_neighbor_group + nbi.n_neighbor_single == nbi.neighbor_address.getSize());
895 
896  // clear force
897  _fi.clear();
898  auto* force_ptr = force_.getDataAddress();
899  auto* neighbor_ptr = neighbors.getDataAddress();
900  auto* member_adr = _groupi.particles.getOriginAddressArray();
901  const int n_member = _groupi.particles.getSize();
902 #ifdef HERMITE_DEBUG
903  Float mcm = 0.0;
904 #endif
905  for (int j=0; j<n_member; j++) {
906  // get particle index from memory address offset
907  //const int kj = int((Tparticle*)member_adr[j]-ptcl);
908  const int kj = _groupi.info.particle_index[j];
909  ASSERT(kj<particles.getSize());
910  ASSERT(kj>=0);
911  auto& pkj = *(Tparticle*)member_adr[j];
912  ASSERT((void*)member_adr[j]==(void*)&particles[kj]);
913  auto& fkj = force_ptr[kj];
914  auto& nbkj = neighbor_ptr[kj];
915  calcOneSingleAccJerkNB(fkj, nbkj, pkj, _pi.id);
916 
917  // replace the cm. force with the summation of members
918  _fi.acc0[0] += pkj.mass * fkj.acc0[0];
919  _fi.acc0[1] += pkj.mass * fkj.acc0[1];
920  _fi.acc0[2] += pkj.mass * fkj.acc0[2];
921 
922  _fi.acc1[0] += pkj.mass * fkj.acc1[0];
923  _fi.acc1[1] += pkj.mass * fkj.acc1[1];
924  _fi.acc1[2] += pkj.mass * fkj.acc1[2];
925 
926  _fi.pot += pkj.mass * fkj.pot;
927 #ifdef HERMITE_DEBUG
928  mcm += pkj.mass;
929 #endif
930  }
931 #ifdef HERMITE_DEBUG
932  ASSERT(abs(mcm-_pi.mass)<1e-10);
933 #endif
934  _fi.acc0[0] /= _pi.mass;
935  _fi.acc0[1] /= _pi.mass;
936  _fi.acc0[2] /= _pi.mass;
937 
938  _fi.acc1[0] /= _pi.mass;
939  _fi.acc1[1] /= _pi.mass;
940  _fi.acc1[2] /= _pi.mass;
941 
942  _fi.pot /= _pi.mass;
943 
944  }
945 
947 
953  inline void calcAccJerkNBList(const int* _index_single,
954  const int _n_single,
955  const int* _index_group,
956  const int _n_group) {
957 
958  // predictor
959  //auto* ptcl = particles.getDataAddress();
960  auto* pred_ptr = pred_.getDataAddress();
961  auto* force_ptr = force_.getDataAddress();
962  auto* neighbor_ptr = neighbors.getDataAddress();
963  for (int k=0; k<_n_single; k++) {
964  const int i = _index_single[k];
965  auto& pi = pred_ptr[i];
966  auto& fi = force_ptr[i];
967  auto& nbi = neighbor_ptr[i];
968  calcOneSingleAccJerkNB(fi, nbi, pi, pi.id);
969  }
970 
971  // for group active particles
972  auto* group_ptr = groups.getDataAddress();
973 
974  for (int k=0; k<_n_group; k++) {
975  const int i = _index_group[k];
976  auto& groupi = group_ptr[i];
977  // use predictor of cm
978  auto& pi = pred_ptr[i+index_offset_group_];
979  auto& fi = force_ptr[i+index_offset_group_];
980  if (groupi.particles.cm.mass>0) {
981  if (groupi.perturber.need_resolve_flag) calcOneGroupMemberAccJerkNB(fi, groupi, pi);
982  else calcOneGroupCMAccJerkNB(fi, groupi, pi);
983  }
984  else calcOneSingleAccJerkNB(fi, groupi.perturber, pi, pi.id);
985  }
986  }
987 
989  void reduceGroupCMStepByHalfAndSortDtIndex(const int _index) {
990  int i = _index;
991  int k = index_dt_sorted_group_[i];
992 
993  auto& pcm = groups[k].particles.cm;
994  // reduce step size to get one more step
995  pcm.dt *= 0.5;
996  time_next_[k+index_offset_group_] = pcm.time + pcm.dt;
997  if (_index>0) {
998  int kp = index_dt_sorted_group_[i-1];
999  while (time_next_[k+index_offset_group_]<time_next_[kp+index_offset_group_]) {
1000  // swap index
1001  int tmp=index_dt_sorted_group_[i-1];
1002  index_dt_sorted_group_[i-1] = index_dt_sorted_group_[i];
1003  index_dt_sorted_group_[i] = tmp;
1004 
1005  // update i to the new position of k
1006  i--;
1007  if (i==0) break; // if moved to the begining, break
1008  else kp = index_dt_sorted_group_[i-1];
1009  }
1010  }
1011  time_next_min_ = std::min(time_next_min_, time_next_[k+index_offset_group_]);
1012 
1013  // in case the particle move to the boundary of act group, increase n_act_group by one
1014  if (_index>=n_act_group_ && i<=n_act_group_ && n_act_group_<index_dt_sorted_group_.getSize()) n_act_group_++;
1015  }
1016 
1017 
1019 
1025  void updateTimeNextList(const int* _index_single,
1026  const int _n_single,
1027  const int* _index_group,
1028  const int _n_group) {
1029  // for single
1030  for (int i=0; i<_n_single; i++){
1031  const int k = _index_single[i];
1032  ASSERT(k<time_next_.getSize());
1033  time_next_[k] = particles[k].time + particles[k].dt;
1034  }
1035 
1036  // for group
1037  for (int i=0; i<_n_group; i++) {
1038  const int k = _index_group[i];
1039  const int kf = k + index_offset_group_;
1040  ASSERT(kf<time_next_.getSize());
1041  auto& pcm = groups[k].particles.cm;
1042  time_next_[kf] = pcm.time + pcm.dt;
1043  }
1044  }
1045 
1047 
1051  Float calcDrDv(const H4Ptcl& _p1, const H4Ptcl& _p2) {
1052  Float dx[3],dv[3];
1053  dx[0] = _p1.pos[0] - _p2.pos[0];
1054  dx[1] = _p1.pos[1] - _p2.pos[1];
1055  dx[2] = _p1.pos[2] - _p2.pos[2];
1056 
1057  dv[0] = _p1.vel[0] - _p2.vel[0];
1058  dv[1] = _p1.vel[1] - _p2.vel[1];
1059  dv[2] = _p1.vel[2] - _p2.vel[2];
1060 
1061  Float drdv= dx[0]*dv[0] + dx[1]*dv[1] + dx[2]*dv[2];
1062 
1063  return drdv;
1064  }
1065 
1066  // for get sorted index of single
1067  class SortIndexDtSingle{
1068  private:
1069  Float * time_;
1070  public:
1071  SortIndexDtSingle(Float* _time): time_(_time) {}
1072  bool operator() (const int & left, const int & right) const {
1073  return time_[left] < time_[right];
1074  }
1075  };
1076 
1077  // for get sorted index of group
1078  class SortIndexDtGroup{
1079  private:
1080  Float * time_;
1081  int offset_;
1082  public:
1083  SortIndexDtGroup(Float * _time, int _offset): time_(_time), offset_(_offset) {}
1084  bool operator() (const int & left, const int & right) const {
1085  return time_[left+offset_] < time_[right+offset_];
1086  }
1087  };
1088 
1089  // ! remove index from bool table
1090  void removeDtIndexFromTable(COMM::List<int>& _index, int& _n_act, int& _n_init, COMM::List<bool>& _table) {
1091  int i = 0;
1092  int imove = 0;
1093  int n_index = _index.getSize();
1094  while (i<n_index) {
1095  if (_table[_index[i]]) {
1096  if (i<_n_act) _n_act--;
1097  if (i<_n_init) _n_init--;
1098  imove++;
1099  n_index--;
1100  }
1101  else i++;
1102  if (i>=n_index) break;
1103  _index[i] = _index[i+imove];
1104  }
1105  ASSERT(_n_act>=0);
1106  ASSERT(_n_act<=n_index);
1107  ASSERT(_n_init>=0);
1108  ASSERT(_n_init<=n_index);
1109  _index.resizeNoInitialize(n_index);
1110  }
1111 
1112  // ! remove group particle address of a list from table_group_mask_
1116  int removeNBGroupAddressFromTable(COMM::List<NBAdr<Tparticle>> & _adr) {
1117  int k = 0;
1118  int k_last = _adr.getSize()-1;
1119  int n_rm = 0;
1120  while (k<=k_last) {
1121  if (_adr[k].index>=index_offset_group_) {
1122  const int kg = _adr[k].index-index_offset_group_;
1123  if (table_group_mask_[kg]) {
1124  _adr[k] = _adr[k_last];
1125  k_last--;
1126  n_rm++;
1127  }
1128  else k++;
1129  }
1130  else k++;
1131  }
1132  _adr.resizeNoInitialize(k_last+1);
1133  return n_rm;
1134  }
1135 
1136  // ! remove group particle address of a list from table_group_mask_
1140  int removeNBSingleAddressFromTable(COMM::List<NBAdr<Tparticle>> & _adr) {
1141  int k = 0;
1142  int k_last = _adr.getSize()-1;
1143  int n_rm = 0;
1144  while (k<=k_last) {
1145  if (_adr[k].index<index_offset_group_) {
1146  if (table_single_mask_[_adr[k].index]) {
1147  _adr[k] = _adr[k_last];
1148  k_last--;
1149  n_rm++;
1150  }
1151  else k++;
1152  }
1153  else k++;
1154  }
1155  _adr.resizeNoInitialize(k_last+1);
1156  return n_rm;
1157  }
1158 
1159 
1160  public:
1161 
1163 
1165  void initialSystemSingle(const Float _time_sys) {
1166  ASSERT(checkParams());
1167  particles.setModifiedFalse();
1168 
1169  initial_system_flag_ = true;
1170 
1171  // start case, set n_init_single_ and n_init_group_ consistent with index_dt_sorted array
1172  // set particle numbers
1173  const int n_particle = particles.getSize();
1174  ASSERT(n_particle>1);
1175 
1176  // use increase since addgroups may already increase the sizes
1177  pred_.increaseSizeNoInitialize(n_particle);
1178  force_.increaseSizeNoInitialize(n_particle);
1179  time_next_.increaseSizeNoInitialize(n_particle);
1180 
1181  // set number of lists
1182  neighbors.resizeNoInitialize(n_particle);
1183  index_dt_sorted_single_.resizeNoInitialize(n_particle);
1184  table_single_mask_.resizeNoInitialize(n_particle);
1185 
1186  for (int i=0; i<n_particle; i++) {
1187  // initial index_dt_sorted_single_ first to allow initialIntegration work
1188  index_dt_sorted_single_[i] = i;
1189  table_single_mask_[i] = false;
1190  }
1191  // set initial time
1192  time_ = _time_sys;
1193 
1194  n_init_single_ = index_dt_sorted_single_.getSize();
1195  // if initial step, n_act are not given initially
1196  n_act_single_ = n_init_single_;
1197  }
1198 
1200 
1205  /* Add particles from single (hermite.particles) to groups.
1206  Add new groups into index_dt_sorted_group_ and increase n_init_group_.
1207  set single mask to true for added particles
1208  */
1209  void addGroups(const int* _particle_index, const int* _n_group_offset, const int _n_group) {
1210  ASSERT(checkParams());
1211  ASSERT(!particles.isModified());
1212  ASSERT(initial_system_flag_);
1213 
1214  if (_n_group==0) return;
1215 
1216  modify_system_flag_ = true;
1217 
1218  // gether the new index in groups first
1219  int group_index[_n_group];
1220  for (int i=0; i<_n_group; i++) {
1221  // check suppressed group
1222  int igroup;
1223  if(index_group_mask_.getSize()>0) {
1224  igroup = index_group_mask_.getLastMember();
1225  table_group_mask_[igroup] = false;
1226  index_group_mask_.decreaseSizeNoInitialize(1);
1227  }
1228  else {
1229  // add new
1230  igroup = groups.getSize();
1231  table_group_mask_.addMember(false);
1232  groups.increaseSizeNoInitialize(1);
1233  // also increase the cm predictor force and time_next
1234  pred_.increaseSizeNoInitialize(1);
1235  force_.increaseSizeNoInitialize(1);
1236  time_next_.increaseSizeNoInitialize(1);
1237  }
1238  group_index[i] = igroup;
1239  }
1240 
1241  // add new group
1242  for (int i=0; i<_n_group; i++) {
1243  auto& group_new = groups[group_index[i]];
1244 
1245  // set manager
1246  ASSERT(ar_manager!=NULL);
1247  group_new.manager = ar_manager;
1248 
1249  // allocate memory
1250  const int n_particle = _n_group_offset[i+1] - _n_group_offset[i];
1251  ASSERT(n_particle>0);
1252  ASSERT(n_particle<=particles.getSize());
1253  group_new.particles.setMode(COMM::ListMode::copy);
1254  group_new.particles.reserveMem(n_particle);
1255  group_new.reserveIntegratorMem();
1256  const int nmax_tot = particles.getSizeMax() + groups.getSizeMax();
1257  group_new.perturber.neighbor_address.setMode(COMM::ListMode::local);
1258  group_new.perturber.neighbor_address.reserveMem(nmax_tot);
1259  group_new.info.reserveMem(n_particle);
1260 
1261  // Add members to AR
1262  for(int j=_n_group_offset[i]; j<_n_group_offset[i+1]; j++) {
1263  const int p_index = _particle_index[j];
1264  ASSERT(p_index<particles.getSize());
1265  group_new.particles.addMemberAndAddress(particles[p_index]);
1266  group_new.info.particle_index.addMember(p_index);
1267  group_new.info.r_break_crit = std::max(group_new.info.r_break_crit, particles[p_index].getRGroup());
1268  Float r_neighbor_crit = particles[p_index].getRNeighbor();
1269  group_new.perturber.r_neighbor_crit_sq = std::max(group_new.perturber.r_neighbor_crit_sq, r_neighbor_crit*r_neighbor_crit);
1270  // update single mask table
1271  ASSERT(table_single_mask_[p_index]==false);
1272  table_single_mask_[p_index] = true;
1273  }
1274 
1275  // calculate the c.m.
1276  group_new.particles.calcCenterOfMass();
1277  // set id to group_index + 1
1278  group_new.particles.cm.id = - (group_index[i]+1);
1279  // shift to c.m. frame
1280  group_new.particles.shiftToCenterOfMassFrame();
1281 
1282  // get binarytree
1283  group_new.info.generateBinaryTree(group_new.particles,ar_manager->interaction.gravitational_constant);
1284 
1285 #ifdef ADJUST_GROUP_DEBUG
1286  std::cerr<<"Add new group, index: "<<group_index[i]<<" Member_index: ";
1287  for (int k=0; k<group_new.particles.getSize(); k++)
1288  std::cerr<<group_new.info.particle_index[k]<<" ";
1289  std::cerr<<"r_break_crit: "<<group_new.info.r_break_crit;
1290  std::cerr<<std::endl;
1291  COMM::Binary& bin = group_new.info.getBinaryTreeRoot();
1292  bin.printColumnTitle(std::cerr);
1293  std::cerr<<std::endl;
1294  bin.printColumn(std::cerr);
1295  std::cerr<<std::endl;
1296 #endif
1297 
1298  // initial perturber
1299  group_new.perturber.need_resolve_flag = true;
1300  }
1301 
1302  // modify the sorted_dt array
1303  const int n_group_old = index_dt_sorted_group_.getSize();
1304  index_dt_sorted_group_.increaseSizeNoInitialize(_n_group);
1305  for (int i=n_group_old-1; i>=0; i--) {
1306  index_dt_sorted_group_[i+_n_group] = index_dt_sorted_group_[i];
1307  }
1308  for (int i=0; i<_n_group; i++) {
1309  index_dt_sorted_group_[i] = group_index[i];
1310  }
1311  n_init_group_ += _n_group;
1312  n_act_group_ += _n_group;
1313 
1314  // remove single from index_dt_sorted
1315  removeDtIndexFromTable(index_dt_sorted_single_, n_act_single_, n_init_single_, table_single_mask_);
1316  // clear neighbor lists and add group index
1317  int n_group = index_dt_sorted_group_.getSize();
1318  for (int i=0; i<n_group; i++) {
1319  const int k = index_dt_sorted_group_[i];
1320  auto& nbk = groups[k].perturber;
1321  // remove single index
1322  int n_rm_single= removeNBSingleAddressFromTable(nbk.neighbor_address);
1323  nbk.n_neighbor_single -= n_rm_single;
1324  ASSERT(nbk.n_neighbor_single>=0);
1325  // add new group index
1326  for (int j=0; j<_n_group; j++) {
1327  const int jk = group_index[j];
1328  if (k==jk) continue;
1329  nbk.neighbor_address.addMember(NBAdr<Tparticle>(&groups[jk].particles, jk+index_offset_group_));
1330  nbk.n_neighbor_group++;
1331  }
1332  }
1333  }
1334 
1336 
1339  void readGroupConfigureAscii(std::istream& _fin) {
1340  int n_group;
1341  _fin>>n_group;
1342  ASSERT(!_fin.eof());
1343  int n_group_offset[n_group+1];
1344 
1345  if (n_group>0) {
1346  for (int i=0; i<=n_group; i++) {
1347  _fin>>n_group_offset[i];
1348  ASSERT(!_fin.eof());
1349  }
1350  int n_members = n_group_offset[n_group];
1351  ASSERT(n_members>1);
1352  int particle_index[n_members];
1353  for (int i=0; i<n_members; i++) {
1354  _fin>>particle_index[i];
1355  ASSERT(!_fin.eof());
1356  }
1357 
1358  addGroups(particle_index, n_group_offset, n_group);
1359  }
1360  }
1361 
1363  /* Split one group to two sub-groups based on the binarytree upper-most pair
1364  If sub-groups have only one particles, return it to the single list
1365  Notice if groups are removed, the group index are not changed, only the removed groups are masked and cleared for reusing
1366  @param[out] _new_group_particle_index_origin: particle index (for hermite particle data) of new group members
1367  @param[out] _new_n_group_offset: group member boundary, first value is defined already
1368  @param[out] _new_n_group: number of new group
1369  @param[in] _break_group_index_with_offset: break group index in groups
1370  @param[in] _n_break_no_add: number of break groups without adding new particles
1371  @param[in] _n_break: number of break groups
1372  */
1373  void breakGroups(int* _new_group_particle_index_origin,
1374  int* _new_n_group_offset,
1375  int& _new_n_group,
1376  const int* _break_group_index_with_offset,
1377  const int _n_break_no_add,
1378  const int _n_break) {
1379  ASSERT(checkParams());
1380  ASSERT(!particles.isModified());
1381  ASSERT(initial_system_flag_);
1382  ASSERT(_n_break<=index_dt_sorted_group_.getSize());
1383 
1384  if (_n_break==0 && _n_break_no_add==0) return;
1385 
1386  modify_system_flag_=true;
1387  int new_index_single[particles.getSize()];
1388  int n_single_new=0;
1389 
1390  for (int k=0; k<_n_break; k++) {
1391  const int i = _break_group_index_with_offset[k] - index_offset_group_;
1392  ASSERT(i>=0&&i<groups.getSize());
1393 
1394  // before break group, first accummulating energy change
1396 
1397  auto& groupi = groups[i];
1398  const int n_member =groupi.particles.getSize();
1399  int particle_index_origin[n_member];
1400  int ibreak = groupi.info.getTwoBranchParticleIndexOriginFromBinaryTree(particle_index_origin, groupi.particles.getDataAddress());
1401 
1402 #ifdef ADJUST_GROUP_DEBUG
1403  auto& sd_root = groupi.info.getBinaryTreeRoot().slowdown;
1404  std::cerr<<"Break Group: "
1405  <<" Time: "<<time_
1406  <<" k: "<<std::setw(2)<<i
1407  <<" N_member: "<<std::setw(4)<<groupi.particles.getSize()
1408  <<" step: "<<std::setw(12)<<groupi.profile.step_count_sum
1409  <<" step(tsyn): "<<std::setw(10)<<groupi.profile.step_count_tsyn_sum
1410 // <<" step(sum): "<<std::setw(12)<<profile.ar_step_count
1411 // <<" step_tsyn(sum): "<<std::setw(12)<<profile.ar_step_count_tsyn
1412  <<" Pert_In: "<<std::setw(20)<<sd_root.getPertIn()
1413  <<" Pert_Out: "<<std::setw(20)<<sd_root.getPertOut()
1414  <<" SD: "<<std::setw(20)<<sd_root.getSlowDownFactor()
1415  <<" SD(org): "<<std::setw(20)<<sd_root.getSlowDownFactorOrigin();
1416  auto& bin = groupi.info.getBinaryTreeRoot();
1417  std::cerr<<" semi: "<<std::setw(20)<<bin.semi
1418  <<" ecc: "<<std::setw(20)<<bin.ecc
1419  <<" NB: "<<std::setw(4)<<groupi.perturber.neighbor_address.getSize()
1420  <<std::endl;
1421 #endif
1422 
1423 #ifdef ADJUST_GROUP_PRINT
1424  if (manager->adjust_group_write_flag) {
1425  groupi.printGroupInfo(1, manager->fgroup, WRITE_WIDTH, &(particles.cm));
1426  }
1427 #endif
1428 
1429  // clear group
1430  groupi.particles.shiftToOriginFrame();
1431  groupi.particles.template writeBackMemberAll<Tparticle>();
1432  groupi.clear();
1433  table_group_mask_[i] = true;
1434  index_group_mask_.addMember(i);
1435 
1436  // check single case
1437  // left side
1438  if (ibreak==1) {
1439  ASSERT(particle_index_origin[0]<particles.getSize());
1440  ASSERT(table_single_mask_[particle_index_origin[0]]==true);
1441  table_single_mask_[particle_index_origin[0]] = false;
1442  if (particles[particle_index_origin[0]].mass > 0.0) // no zero mass particle
1443  new_index_single[n_single_new++] = particle_index_origin[0];
1444  }
1445  else {
1446  // check first how many particles have mass>0
1447  int count_mass = 0;
1448  int index_has_mass_last = -1;
1449  for (int j=0; j<ibreak; j++) {
1450  if (particles[particle_index_origin[j]].mass > 0.0) {
1451  count_mass++;
1452  index_has_mass_last = particle_index_origin[j];
1453  }
1454  }
1455  if (count_mass>=2) { // only form new group when at least two members have mass >0
1456  int offset = _new_n_group_offset[_new_n_group];
1457  for (int j=0; j<ibreak; j++) {
1458  ASSERT(table_single_mask_[particle_index_origin[j]]==true);
1459  table_single_mask_[particle_index_origin[j]] = false;
1460  _new_group_particle_index_origin[offset++] = particle_index_origin[j];
1461  }
1462  _new_n_group_offset[++_new_n_group] = offset;
1463  }
1464  else if (count_mass==1) { // add single if only one particle has mass >0
1465  ASSERT(index_has_mass_last<particles.getSize());
1466  ASSERT(index_has_mass_last>=0);
1467  ASSERT(table_single_mask_[index_has_mass_last]==true);
1468  table_single_mask_[index_has_mass_last] = false;
1469  new_index_single[n_single_new++] = index_has_mass_last;
1470  }
1471  }
1472  // right side
1473  if (n_member-ibreak==1) {
1474  ASSERT(particle_index_origin[ibreak]<particles.getSize());
1475  ASSERT(table_single_mask_[particle_index_origin[ibreak]]==true);
1476  table_single_mask_[particle_index_origin[ibreak]] = false;
1477  if (particles[particle_index_origin[ibreak]].mass >0.0)
1478  new_index_single[n_single_new++] = particle_index_origin[ibreak];
1479  }
1480  else {
1481  // check first how many particles have mass>0
1482  int count_mass = 0;
1483  int index_has_mass_last = -1;
1484  for (int j=ibreak; j<n_member; j++) {
1485  if (particles[particle_index_origin[j]].mass > 0.0) {
1486  count_mass++;
1487  index_has_mass_last = particle_index_origin[j];
1488  }
1489  }
1490  if (count_mass>=2) { // only form new group when at least two members have mass >0
1491  int offset = _new_n_group_offset[_new_n_group];
1492  for (int j=ibreak; j<n_member; j++) {
1493  ASSERT(table_single_mask_[particle_index_origin[j]]==true);
1494  table_single_mask_[particle_index_origin[j]] = false;
1495  _new_group_particle_index_origin[offset++] = particle_index_origin[j];
1496  }
1497  _new_n_group_offset[++_new_n_group] = offset;
1498  }
1499  else if (count_mass==1) { // add single if only one particle has mass >0
1500  ASSERT(index_has_mass_last<particles.getSize());
1501  ASSERT(index_has_mass_last>=0);
1502  ASSERT(table_single_mask_[index_has_mass_last]==true);
1503  table_single_mask_[index_has_mass_last] = false;
1504  new_index_single[n_single_new++] = index_has_mass_last;
1505  }
1506  }
1507  }
1508 
1509  // modify the sorted_dt array of single
1510  const int n_single_old = index_dt_sorted_single_.getSize();
1511  index_dt_sorted_single_.increaseSizeNoInitialize(n_single_new);
1512  for (int i=n_single_old-1; i>=0; i--) {
1513  index_dt_sorted_single_[i+n_single_new] = index_dt_sorted_single_[i];
1514  }
1515  for (int i=0; i<n_single_new; i++) {
1516  index_dt_sorted_single_[i] = new_index_single[i];
1517  }
1518  n_init_single_ += n_single_new;
1519  n_act_single_ += n_single_new;
1520 
1521  // only clear case
1522  for (int k=_n_break; k<_n_break+_n_break_no_add; k++) {
1523  const int i = _break_group_index_with_offset[k] - index_offset_group_;
1524 
1525  // before break group, first accummulating energy change
1527 
1528  auto& groupi = groups[i];
1529  groupi.particles.shiftToOriginFrame();
1530  groupi.particles.template writeBackMemberAll<Tparticle>();
1531  const int n_member = groupi.particles.getSize();
1532  ASSERT(n_member==groupi.info.particle_index.getSize());
1533  for (int j=0; j<n_member; j++) {
1534  const int kj = groupi.info.particle_index[j];
1535  ASSERT(kj<particles.getSize());
1536  table_single_mask_[kj] = false;
1537  }
1538  groupi.clear();
1539  table_group_mask_[i] = true;
1540  index_group_mask_.addMember(i);
1541  }
1542 
1543  // clear index_dt_sorted_
1544  removeDtIndexFromTable(index_dt_sorted_group_, n_act_group_, n_init_group_, table_group_mask_);
1545  // clear neighbor lists and add single index
1546  int n_group = index_dt_sorted_group_.getSize();
1547  for (int i=0; i<n_group; i++) {
1548  const int k = index_dt_sorted_group_[i];
1549  auto& nbk = groups[k].perturber;
1550  // remove group index
1551  int n_rm_group = removeNBGroupAddressFromTable(nbk.neighbor_address);
1552  nbk.n_neighbor_group -= n_rm_group;
1553  ASSERT(nbk.n_neighbor_group>=0);
1554  // add single index
1555  for (int j=0; j<n_single_new; j++) {
1556  const int jk = new_index_single[j];
1557  nbk.neighbor_address.addMember(NBAdr<Tparticle>(&particles[jk],jk));
1558  nbk.n_neighbor_single++;
1559  }
1560  }
1561  }
1562 
1564 
1575  void checkBreak(int* _break_group_index_with_offset, int& _n_break, const bool _start_flag) {
1576  const int n_group_tot = index_dt_sorted_group_.getSize();
1577  if (n_group_tot==0) return;
1578 
1579  bool merge_mask[n_group_tot];
1580  for (int k=0; k<n_group_tot; k++) merge_mask[k] = false;
1581 
1582  // check merger case
1583  const int n_merger = index_group_merger_.getSize();
1584  for (int i=0; i<n_merger; i++) {
1585  const int k = index_group_merger_[i];
1586  ASSERT(k<groups.getSize());
1587  ASSERT(table_group_mask_[k]==false);
1588  auto& groupk = groups[k];
1589 #ifdef ADJUST_GROUP_DEBUG
1590  auto& bin_root = groupk.info.getBinaryTreeRoot();
1591  std::cerr<<"Break merger group: i_group: "<<k
1592  <<" N_member: "<<groupk.particles.getSize()
1593  <<" ecca: "<<bin_root.ecca
1594  <<" separation : "<<bin_root.r
1595  <<" apo: "<<bin_root.semi*(1.0+bin_root.ecc)
1596  <<" dt: "<<groupk.particles.cm.dt
1597  <<" r_crit: "<<groupk.info.r_break_crit
1598  <<std::endl;
1599 #endif
1600  // generate binary tree in order to move zero mass to the outer most
1601  groupk.info.generateBinaryTree(groupk.particles,ar_manager->interaction.gravitational_constant);
1602  _break_group_index_with_offset[_n_break++] = k + index_offset_group_;
1603  merge_mask[k] = true;
1604 
1605  }
1606  index_group_merger_.resizeNoInitialize(0);
1607 
1608  // kappa_org criterion for break group kappa_org>kappa_org_crit
1609  const Float kappa_org_crit = 1e-2;
1610  for (int i=0; i<n_group_tot; i++) {
1611  const int k = index_dt_sorted_group_[i];
1612  ASSERT(table_group_mask_[k]==false);
1613  if (merge_mask[k]) continue;
1614  auto& groupk = groups[k];
1615 
1616  const int n_member = groupk.particles.getSize();
1617 
1618  // generate binary tree
1619  groupk.info.generateBinaryTree(groupk.particles,ar_manager->interaction.gravitational_constant);
1620 
1621  auto& bin_root = groupk.info.getBinaryTreeRoot();
1622  bool outgoing_flag = false; // Indicate whether it is a outgoing case or income case
1623 
1624  // check binary case
1625  // ecc anomaly indicates outgoing (ecca>0) or income (ecca<0)
1626  if (bin_root.semi>0.0 && bin_root.ecca>0.0) {
1627  outgoing_flag = true;
1628  // check whether separation is larger than distance criterion.
1629  if (bin_root.r > groupk.info.r_break_crit) {
1630 #ifdef ADJUST_GROUP_DEBUG
1631  std::cerr<<"Break group: binary escape, time: "<<time_<<" i_group: "<<k<<" N_member: "<<n_member<<" ecca: "<<bin_root.ecca<<" separation : "<<bin_root.r<<" r_crit: "<<groupk.info.r_break_crit<<std::endl;
1632 #endif
1633  _break_group_index_with_offset[_n_break++] = k + index_offset_group_;
1634  continue;
1635  }
1636 
1637  // in case apo is larger than distance criterion
1638  Float apo = bin_root.semi * (1.0 + bin_root.ecc);
1639  if (apo>groupk.info.r_break_crit) {
1640  Float dr2, drdv;
1641 
1642  groupk.info.getDrDv(dr2, drdv, *bin_root.getLeftMember(), *bin_root.getRightMember());
1643  ASSERT(drdv>=0.0);
1644 
1645  // check whether next step the separation is larger than distance criterion
1646  // Not sure whether it can work correctly or not:
1647  // rp = v_r * dt + r
1648  Float dr = drdv/bin_root.r*groups[k].particles.cm.dt;
1649  Float rp = dr + bin_root.r;
1650  if (rp >groupk.info.r_break_crit) {
1651  // in case r is too small, avoid too early quit of group
1652  Float rph = 0.5*dr + bin_root.r;
1653  if ( rph < groupk.info.r_break_crit && bin_root.r <0.2*groupk.info.r_break_crit) {
1654  if (getNextTime()>time_+groups[k].particles.cm.dt) {
1655 #ifdef ADJUST_GROUP_DEBUG
1656  std::cerr<<"Binary will escape but dr is too small, reduce cm step by half, time: "<<time_<<" i_group: "<<k<<" N_member: "<<n_member<<" ecca: "<<bin_root.ecca<<" separation : "<<bin_root.r<<" apo: "<<apo<<" r_pred: "<<rp<<" drdv: "<<drdv<<" dt: "<<groups[k].particles.cm.dt<<" r_crit: "<<groupk.info.r_break_crit<<std::endl;
1657 #endif
1658  reduceGroupCMStepByHalfAndSortDtIndex(i);
1659  }
1660  }
1661  else {
1662 #ifdef ADJUST_GROUP_DEBUG
1663  std::cerr<<"Break group: binary will escape, time: "<<time_<<" i_group: "<<k<<" N_member: "<<n_member<<" ecca: "<<bin_root.ecca<<" separation : "<<bin_root.r<<" apo: "<<apo<<" r_pred: "<<rp<<" drdv: "<<drdv<<" dt: "<<groups[k].particles.cm.dt<<" r_crit: "<<groupk.info.r_break_crit<<std::endl;
1664 #endif
1665  _break_group_index_with_offset[_n_break++] = k + index_offset_group_;
1666  }
1667  continue;
1668  }
1669  }
1670 
1671  }
1672 
1673  // check hyperbolic case
1674  if (bin_root.semi<0.0) {
1675  // hyperbolic case, ecca is not correctly calculated
1676  Float dr2, drdv;
1677  groupk.info.getDrDv(dr2, drdv, *bin_root.getLeftMember(), *bin_root.getRightMember());
1678  if (drdv>0.0) {
1679  outgoing_flag = true;
1680  // check distance criterion
1681  if (bin_root.r > groupk.info.r_break_crit) {
1682 #ifdef ADJUST_GROUP_DEBUG
1683  std::cerr<<"Break group: hyperbolic escape, time: "<<time_<<" i_group: "<<k<<" N_member: "<<n_member<<" drdv: "<<drdv<<" separation : "<<bin_root.r<<" r_crit: "<<groupk.info.r_break_crit<<std::endl;
1684 #endif
1685  _break_group_index_with_offset[_n_break++] = k + index_offset_group_;
1686  continue;
1687  }
1688  // check for next step
1689  Float dr = drdv/bin_root.r*groups[k].particles.cm.dt;
1690  Float rp = dr + bin_root.r;
1691  if (rp > groupk.info.r_break_crit) {
1692  // in case r is too small, avoid too early quit of group
1693  Float rph = 0.5*dr + bin_root.r;
1694  if ( rph < groupk.info.r_break_crit && bin_root.r <0.2*groupk.info.r_break_crit) {
1695  if (getNextTime()>time_+groups[k].particles.cm.dt) {
1696 #ifdef ADJUST_GROUP_DEBUG
1697  std::cerr<<"Hyperbolic will escape but dr is too small, reduce cm step by half first, time: "<<time_<<" i_group: "<<k<<" N_member: "<<n_member<<" drdv: "<<drdv<<" separation : "<<bin_root.r<<" r_pred: "<<rp<<" drdv: "<<drdv<<" dt: "<<groups[k].particles.cm.dt<<" r_crit: "<<groupk.info.r_break_crit<<std::endl;
1698 #endif
1699  reduceGroupCMStepByHalfAndSortDtIndex(i);
1700  }
1701  }
1702  else {
1703 #ifdef ADJUST_GROUP_DEBUG
1704  std::cerr<<"Break group: hyperbolic will escape, time: "<<time_<<" i_group: "<<k<<" N_member: "<<n_member<<" drdv: "<<drdv<<" separation : "<<bin_root.r<<" r_pred: "<<rp<<" drdv: "<<drdv<<" dt: "<<groups[k].particles.cm.dt<<" r_crit: "<<groupk.info.r_break_crit<<std::endl;
1705 #endif
1706  _break_group_index_with_offset[_n_break++] = k + index_offset_group_;
1707  }
1708  continue;
1709  }
1710  }
1711 
1712  }
1713 
1714  // check perturbation
1715  // only check further if it is outgoing case
1716  if (outgoing_flag) {
1717 
1718  AR::SlowDown sd;
1719  auto& sd_group = groupk.info.getBinaryTreeRoot().slowdown;
1720  sd.initialSlowDownReference(sd_group.getSlowDownFactorReference(),sd_group.getSlowDownFactorMax());
1721  sd.timescale = sd_group.timescale;
1722  sd.period = sd_group.period;
1723 
1724  if (n_member==2) {
1725  // check strong perturbed binary case
1726  // calculate slowdown in a consistent way like in checknewgroup to avoid switching
1727  // fcm may not properly represent the perturbation force (perturber mass is unknown)
1728  sd.pert_in = ar_manager->interaction.calcPertFromBinary(bin_root);
1729  Float* acc_cm = groupk.particles.cm.acc0;
1730  Float fcm[3] = {acc_cm[0]*bin_root.mass, acc_cm[1]*bin_root.mass, acc_cm[2]*bin_root.mass };
1731  sd.pert_out= ar_manager->interaction.calcPertFromForce(fcm, bin_root.mass, bin_root.mass);
1732  sd.calcSlowDownFactor();
1733  Float kappa_org = sd.getSlowDownFactorOrigin();
1734 
1735  if (kappa_org<kappa_org_crit && !_start_flag) {
1736  // in binary case, only break when apo is larger than distance criterion
1737  Float apo = bin_root.semi * (1.0 + bin_root.ecc);
1738  if (apo>groupk.info.r_break_crit||bin_root.semi<0.0) {
1739 #ifdef ADJUST_GROUP_DEBUG
1740  std::cerr<<"Break group: strong perturbed, time: "<<time_<<" i_group: "<<k<<" N_member: "<<n_member;
1741  std::cerr<<" index: ";
1742  for (int i=0; i<n_member; i++)
1743  std::cerr<<groupk.info.particle_index[i]<<" ";
1744  auto& sd_root = groupk.info.getBinaryTreeRoot().slowdown;
1745  std::cerr<<" pert_in: "<<sd_root.pert_in
1746  <<" pert_out: "<<sd_root.pert_out
1747  <<" kappa_org: "<<kappa_org
1748  <<" dr: "<<bin_root.r
1749  <<" semi: "<<bin_root.semi
1750  <<" ecc: "<<bin_root.ecc
1751  <<" r_break: "<<groupk.info.r_break_crit
1752  <<std::endl;
1753 #endif
1754  _break_group_index_with_offset[_n_break++] = k + index_offset_group_;
1755  continue;
1756  }
1757  }
1758  }
1759 #if (!defined AR_SLOWDOWN_ARRAY) && (!defined AR_SLOWDOWN_TREE)
1760  // check few-body inner perturbation (suppress when use slowdown inner AR)
1761  else {
1762  for (int j=0; j<2; j++) {
1763  if (bin_root.isMemberTree(j)) {
1764  auto* bin_sub = bin_root.getMemberAsTree(j);
1765 // Float semi_db = 2.0*bin_sub->semi;
1766 // // inner hyperbolic case
1767 // if(semi_db<0.0 && abs(groupk.getEnergyError()/groupk.getEtot())<100.0*groupk.manager->energy_error_relative_max && bin_root->ecca>0.0) {
1768 //#ifdef ADJUST_GROUP_DEBUG
1769 // std::cerr<<"Break group: inner member hyperbolic, time: "<<time_<<" i_group: "<<k<<" i_member: "<<j<<" semi: "<<semi_db<<" ecca: "<<bin_sub->ecca<<std::endl;
1770 //#endif
1771 // _break_group_index_with_offset[_n_break++] = k + index_offset_group_;
1772 // break;
1773 // }
1774 
1775  // check inner binary slowdown factor
1776  if (bin_sub->semi>0.0) {
1777 
1778  Float apo_in = bin_sub->semi*(1+bin_sub->ecc);
1779  sd.pert_in = ar_manager->interaction.calcPertFromMR(apo_in, bin_sub->m1, bin_sub->m2);
1780 
1781  // present slowdown
1782  sd.pert_out = ar_manager->interaction.calcPertFromMR(bin_root.r, bin_root.m1, bin_root.m2);
1783  sd.calcSlowDownFactor();
1784  Float kappa_in = sd.getSlowDownFactorOrigin();
1785 
1786  // in case slowdown >1
1787  if (kappa_in>1.0) {
1788  Float kappa_in_max = NUMERIC_FLOAT_MAX;
1789  // if outer is binary, estimate slowdown max (apo_out)
1790  if (bin_root.semi>0.0) {
1791  Float apo_out = bin_root.semi*(1+bin_root.ecc);
1792  sd.pert_out = ar_manager->interaction.calcPertFromMR(apo_out, bin_root.m1, bin_root.m2);
1793  sd.calcSlowDownFactor();
1794 
1795  kappa_in_max = sd.getSlowDownFactorOrigin();
1796  }
1797 
1798  // if slowdown factor is large, break the group
1799  if (kappa_in_max>5.0) {
1800  // avoid quit at high energy error phase
1801  if (abs(groupk.getEnergyError()/groupk.getEtotRef())<100.0/(1-std::min(bin_sub->ecc,bin_root.ecc))*groupk.manager->energy_error_relative_max) {
1802 #ifdef ADJUST_GROUP_DEBUG
1803  std::cerr<<"Break group: inner kappa large, time: "<<time_<<" i_group: "<<k<<" i_member: "<<j<<" kappa_in:"<<kappa_in<<" kappa_in(max):"<<kappa_in_max
1804  <<" Energy error:"<<groupk.getEnergyError()
1805  <<" Etot ref:"<<groupk.getEtotRef()
1806  <<" ecc(in):"<<bin_sub->ecc
1807  <<" ecc(out):"<<bin_root.ecc
1808  <<std::endl;
1809 #endif
1810  _break_group_index_with_offset[_n_break++] = k + index_offset_group_;
1811  break;
1812  }
1813  }
1814  }
1815  }
1816  }
1817  }
1818  }
1819 #endif
1820  }
1821  }
1822  }
1823 
1825 
1835  void checkNewGroup(int* _new_group_particle_index_origin,
1836  int* _new_n_group_offset,
1837  int& _new_n_group,
1838  int* _break_group_index_with_offset,
1839  int& _n_break_no_add,
1840  const int _n_break,
1841  const bool _start_flag) {
1842  // kappa_org criterion for new group kappa_org>kappa_org_crit
1843  const Float kappa_org_crit = 1e-2;
1844 
1845  const int n_particle = particles.getSize();
1846  const int n_group = groups.getSize();
1847  ASSERT(index_offset_group_==n_particle);
1848 
1849  int new_n_particle = 0;
1850  // used_mask store the current group index of particle i if it is already in new group
1851  int used_mask[n_particle+n_group];
1852  // -1 means not yet used
1853  for (int k=0; k<n_particle; k++) used_mask[k] = table_single_mask_[k]?-2:-1;
1854  for (int k=0; k<n_group; k++) used_mask[k+index_offset_group_] = table_group_mask_[k]?-2:-1;
1855  // -2 means break groups/masked groups
1856  for (int k=0; k<_n_break; k++) used_mask[_break_group_index_with_offset[k]] = -2;
1857 
1858  //const Float r_crit_sq = manager->r_break_crit*manager->r_break_crit;
1859 
1860  // gether list together
1861  //const int n_check = n_act_single_+n_act_group_;
1862  //int check_index[n_check];
1863  //for (int k=0; k<n_act_single_; k++) check_index[k] = index_dt_sorted_single_[k];
1864  //for (int k=0; k<n_act_group_; k++) check_index[k+n_act_single_] = index_dt_sorted_group_[k] + index_offset_group_;
1865 
1866  // check only active particles
1867  // single case
1868  for (int k=0; k<n_act_single_; k++) {
1869  const int i = index_dt_sorted_single_[k];
1870  const int j = neighbors[i].r_min_index;
1871  ASSERT(j<n_particle+n_group);
1872  if(j<0) continue;
1873 
1874  const Float dr2 = neighbors[i].r_min_sq;
1875  ASSERT(dr2>0.0);
1876 
1877  auto& pi = particles[i];
1878 
1879  // avoid zero mass particle
1880  if (pi.mass==0.0) continue;
1881 
1882  // distance criterion
1883  Float r_crit = pi.getRGroup();
1884  if (j<index_offset_group_)
1885  r_crit = std::max(particles[j].getRGroup(), r_crit);
1886  else
1887  r_crit = std::max(groups[j-index_offset_group_].info.r_break_crit, r_crit);
1888  Float r_crit_sq = r_crit*r_crit;
1889 
1890  if (dr2 < r_crit_sq) {
1891 
1892  // avoid break/masked member
1893  if(used_mask[j]==-2) continue;
1894 
1895  // avoid double count
1896  if(used_mask[i]>=0 && used_mask[j]>=0) continue;
1897 
1898  H4Ptcl* pj;
1899  // neighbor is single
1900  if (j<index_offset_group_) {
1901  pj = &particles[j];
1902  }
1903  else {
1904  const int jg = j-index_offset_group_;
1905 //#ifndef AR_SLOWDOWN_ARRAY
1906 // // in case without AR slowdown inner, avoid form AR when inner kappa is >1.0
1907 // Float kappa_org_j = groups[jg].info.getBinaryTreeRoot().slowdown.getSlowDownFactorOrigin();
1908 // if (kappa_org_j>1.0) continue;
1909 //#endif
1910  pj = &groups[jg].particles.cm;
1911  }
1912 
1913 
1914  // avoid zero mass particle
1915  if(pj->mass==0.0) continue;
1916 
1917  // this increase AR total step too much
1918  //bool add_flag=false;
1919  //Float semi, ecc, dr, drdv;
1920  //AR::BinaryTree<Tparticle>::particleToSemiEcc(semi,ecc,dr,drdv, pi, *pj, ar_manager->interaction.gravitational_constant);
1921  //else {
1922  // Float rp = drdv/dr*dt_limit_ + dr;
1923  // if (rp < r_crit) add_flag = true;
1924  //}
1925 
1926  Float drdv = calcDrDv(pi, *pj);
1927  // only inwards or first step case
1928  if(drdv<0.0||_start_flag) {
1929  //Float mcm = pi.mass + pj->mass;
1930  Float fcm[3] = {pi.mass*pi.acc0[0] + pj->mass*pj->acc0[0],
1931  pi.mass*pi.acc0[1] + pj->mass*pj->acc0[1],
1932  pi.mass*pi.acc0[2] + pj->mass*pj->acc0[2]};
1933 
1934  AR::SlowDown sd;
1935  Float mcm = pi.mass + pj->mass;
1936 #ifdef AR_SLOWDOWN_MASSRATIO
1937  const Float mass_ratio = ar_manager->slowdown_mass_ref/mcm;
1939 #else
1941 #endif
1942  sd.pert_in = ar_manager->interaction.calcPertFromMR(sqrt(dr2), pi.mass, pj->mass);
1943  sd.pert_out = ar_manager->interaction.calcPertFromForce(fcm, mcm, mcm);
1944 
1945  sd.calcSlowDownFactor();
1946  Float kappa_org = sd.getSlowDownFactorOrigin();
1947 
1948  // avoid strong perturbed case, estimate perturbation
1949  // if kappa_org < criterion, avoid to form new group, should be consistent as checkbreak
1950  if(kappa_org<kappa_org_crit) continue;
1951 
1952 #ifdef ADJUST_GROUP_DEBUG
1953  if (j<index_offset_group_) {
1954  std::cerr<<"Find new group: time: "<<time_
1955  <<" index: "<<i<<" "<<j
1956  <<" dr: "<<sqrt(dr2)
1957  <<" kappa_org: "<<kappa_org<<"\n";
1958  }
1959  else {
1960  auto& bin_root = groups[j-index_offset_group_].info.getBinaryTreeRoot();
1961  std::cerr<<"Find new group: time: "<<time_
1962  <<" dr: "<<sqrt(dr2)
1963  <<" kappa_org: "<<kappa_org<<"\n"
1964  <<" index slowdown apo \n"
1965  <<"i1 "
1966  <<std::setw(8)<<i
1967  <<std::setw(16)<<0
1968  <<std::setw(16)<<0;
1969  std::cerr<<"\ni2 "
1970  <<std::setw(8)<<j
1971  <<std::setw(16)<<bin_root.slowdown.getSlowDownFactorOrigin()
1972  <<std::setw(16)<<bin_root.semi*(1.0+bin_root.ecc);
1973  std::cerr<<std::endl;
1974  }
1975 #endif
1976  insertParticleIndexToGroup(i, j, used_mask, _new_group_particle_index_origin, _new_n_group_offset, new_n_particle, _new_n_group, _break_group_index_with_offset, _n_break_no_add, _n_break);
1977  }
1978  }
1979  }
1980 
1981  // group case
1982  for (int k=0; k<n_act_group_; k++) {
1983  const int ig = index_dt_sorted_group_[k];
1984  const int i = ig + index_offset_group_;
1985  auto& groupi = groups[ig];
1986  if (used_mask[i]==-2) continue;
1987 
1988  const int j = groupi.perturber.r_min_index;
1989  ASSERT(j<n_particle+n_group);
1990  if(j<0) continue;
1991 
1992  const Float dr2 = groupi.perturber.r_min_sq;
1993  ASSERT(dr2>0.0);
1994 
1995  Float r_crit = groupi.info.r_break_crit;
1996  if (j<index_offset_group_)
1997  r_crit = std::max(particles[j].getRGroup(), r_crit);
1998  else
1999  r_crit = std::max(groups[j-index_offset_group_].info.r_break_crit, r_crit);
2000  Float r_crit_sq = r_crit*r_crit;
2001 
2002  // distance criterion
2003  if (dr2 < r_crit_sq) {
2004 
2005  // avoid break member
2006  if(used_mask[j]==-2) continue;
2007 
2008  // avoid double count
2009  if(used_mask[i]>=0 && used_mask[j]>=0) continue;
2010 
2011  auto& pi = groupi.particles.cm;
2012 
2013 //#ifndef AR_SLOWDOWN_ARRAY
2014 // // avoid kappa>1.0
2015 // Float kappa_org_i = groupi.info.getBinaryTreeRoot().slowdown.getSlowDownFactorOrigin();
2016 // if (kappa_org_i>1.0) continue;
2017 //#endif
2018 
2019  H4Ptcl* pj;
2020  // neighbor is single
2021  if (j<index_offset_group_) {
2022  //if (kappa_org_i>1.0) continue;
2023  pj = &particles[j];
2024  }
2025  else {
2026  const int jg = j-index_offset_group_;
2027 //#ifndef AR_SLOWDOWN_ARRAY
2028 // Float kappa_org_j = groups[jg].getBinaryTreeRoot().slowdown.getSlowDownFactorOrigin();
2029 // if (kappa_org_j>1.0) continue;
2030 //#endif
2031 
2032  //if (kappa_org_i>1.0&&kappa_org_j>1.0) continue;
2033  pj = &groups[jg].particles.cm;
2034 
2035  // unknown, for test
2036  //if (kappa_org_i*kappa_org_j>1.0) continue;
2037  }
2038  // only inwards or first step case
2039  Float drdv = calcDrDv(pi, *pj);
2040  if(drdv<0.0||_start_flag) {
2041 
2042  //Float mcm = pi.mass + pj->mass;
2043  Float fcm[3] = {pi.mass*pi.acc0[0] + pj->mass*pj->acc0[0],
2044  pi.mass*pi.acc0[1] + pj->mass*pj->acc0[1],
2045  pi.mass*pi.acc0[2] + pj->mass*pj->acc0[2]};
2046 
2047  AR::SlowDown sd;
2048  Float mcm = pi.mass + pj->mass;
2049 #ifdef AR_SLOWDOWN_MASSRATIO
2050  const Float mass_ratio = ar_manager->slowdown_mass_ref/mcm;
2052 #else
2054 #endif
2055  sd.pert_in = ar_manager->interaction.calcPertFromMR(sqrt(dr2), pi.mass, pj->mass);
2056  // fcm may not properly represent the perturbation force (perturber mass is unknown)
2057  sd.pert_out = ar_manager->interaction.calcPertFromForce(fcm, mcm, mcm);
2058 
2059  sd.calcSlowDownFactor();
2060  Float kappa_org = sd.getSlowDownFactorOrigin();
2061 
2062  // avoid strong (outside) perturbed case, estimate perturbation
2063  // if fratiosq >1.5, avoid to form new group, should be consistent as checkbreak
2064  if(kappa_org<kappa_org_crit) continue;
2065 
2066 #ifdef ADJUST_GROUP_DEBUG
2067  auto& bini = groupi.info.getBinaryTreeRoot();
2068  std::cerr<<"Find new group: time: "<<time_
2069  <<" dr: "<<sqrt(dr2)
2070  <<" kappa_org: "<<kappa_org
2071  <<"\n index slowdown apo \n"
2072  <<"i1 "
2073  <<std::setw(8)<<i
2074  <<std::setw(16)<<bini.slowdown.getSlowDownFactorOrigin()
2075  <<std::setw(16)<<bini.semi*(1.0+bini.ecc);
2076  if(j<index_offset_group_) {
2077  std::cerr<<"\ni2 "
2078  <<std::setw(8)<<j
2079  <<std::setw(16)<<0
2080  <<std::setw(16)<<0;
2081  }
2082  else {
2083  auto& binj = groups[j-index_offset_group_].info.getBinaryTreeRoot();
2084  Float kappaj = binj.slowdown.getSlowDownFactorOrigin();
2085  std::cerr<<"\ni2 "
2086  <<std::setw(8)<<j
2087  <<std::setw(16)<<kappaj
2088  <<std::setw(16)<<binj.semi*(1.0+binj.ecc);
2089  }
2090  std::cerr<<std::endl;
2091 #endif
2092  insertParticleIndexToGroup(i, j, used_mask, _new_group_particle_index_origin, _new_n_group_offset, new_n_particle, _new_n_group, _break_group_index_with_offset, _n_break_no_add, _n_break);
2093  }
2094  }
2095  }
2096  // for total number of members
2097  _new_n_group_offset[_new_n_group] = new_n_particle;
2098  ASSERT(new_n_particle<=particles.getSize());
2099  }
2100 
2101 
2103  /* check break and new groups and modify the groups
2104  @param[in] _start_flag: indicate this is the first adjust of the groups in the integration
2105  */
2106  void adjustGroups(const bool _start_flag) {
2107  ASSERT(checkParams());
2108  ASSERT(!particles.isModified());
2109  ASSERT(initial_system_flag_);
2111  modify_system_flag_=true;
2112 
2113  // check
2114  // break index (with index_offset_group)
2115  int break_group_index_with_offset[groups.getSize()+1];
2116  int n_break = 0;
2117  int n_break_no_add = 0;
2118  // new index
2119  int new_group_particle_index[particles.getSize()];
2120  int new_n_group_offset[groups.getSizeMax()+1];
2121  int new_n_group = 0;
2122 
2123  checkBreak(break_group_index_with_offset, n_break, _start_flag);
2124  checkNewGroup(new_group_particle_index, new_n_group_offset, new_n_group, break_group_index_with_offset, n_break_no_add, n_break, _start_flag);
2125  ASSERT(n_break<=groups.getSize());
2126  ASSERT(new_n_group<=groups.getSizeMax());
2127  profile.break_group_count += n_break;
2128  profile.new_group_count += new_n_group;
2129 
2130  // integrate modified single/groups to current time
2131  integrateToTimeList(time_, new_group_particle_index, new_n_group_offset[new_n_group]);
2132  integrateToTimeList(time_, break_group_index_with_offset, n_break);
2133 
2134  // break groups
2135  breakGroups(new_group_particle_index, new_n_group_offset, new_n_group, break_group_index_with_offset, n_break_no_add, n_break);
2136  addGroups(new_group_particle_index, new_n_group_offset, new_n_group);
2137 
2138  // initial integration (cannot do it here, in the case AR perturber need initialization first)
2139  // initialIntegration();
2140  }
2141 
2143 
2149 
2150  ASSERT(!particles.isModified());
2151  ASSERT(initial_system_flag_);
2152 
2153  modify_system_flag_=false;
2154 
2155 
2156  // check table size and index_dt_sorted size
2157 #ifdef HERMITE_DEBUG
2158  int particle_index_count[particles.getSize()];
2159  int group_index_count[groups.getSize()];
2160  for (int i=0; i<particles.getSize(); i++) particle_index_count[i]=0;
2161  for (int i=0; i<groups.getSize(); i++) group_index_count[i]=0;
2162  // single list
2163  for (int i=0; i<index_dt_sorted_single_.getSize(); i++) {
2164  ASSERT(index_dt_sorted_single_[i]<particles.getSize());
2165  particle_index_count[index_dt_sorted_single_[i]]++;
2166  }
2167  for (int i=0; i<index_dt_sorted_group_.getSize(); i++) {
2168  ASSERT(index_dt_sorted_group_[i]<groups.getSize());
2169  group_index_count[index_dt_sorted_group_[i]]++;
2170  }
2171  ASSERT(table_single_mask_.getSize()==particles.getSize());
2172  for (int i=0; i<table_single_mask_.getSize(); i++) {
2173  if (table_single_mask_[i]) particle_index_count[i]++;
2174  }
2175  ASSERT(table_group_mask_.getSize()==groups.getSize());
2176  for (int i=0; i<table_group_mask_.getSize(); i++) {
2177  if (table_group_mask_[i]) group_index_count[i]++;
2178  }
2179  for (int i=0; i<particles.getSize(); i++)
2180  ASSERT(particle_index_count[i]==1||(particle_index_count[i]==0&&particles[i].mass==0.0));
2181  for (int i=0; i<groups.getSize(); i++)
2182  ASSERT(group_index_count[i]==1);
2183 
2184  ASSERT(n_init_group_<=index_dt_sorted_group_.getSize());
2185  ASSERT(n_act_group_<=index_dt_sorted_group_.getSize());
2186  ASSERT(n_init_single_<=index_dt_sorted_single_.getSize());
2187  ASSERT(n_act_single_<=index_dt_sorted_single_.getSize());
2188 #endif
2189 
2190  if (n_init_single_==0&&n_init_group_==0) return;
2191 
2192  // single
2193  int* index_single = index_dt_sorted_single_.getDataAddress();
2194 
2195  auto* ptcl = particles.getDataAddress();
2196  for(int i=0; i<n_init_single_; i++){
2197  int k = index_single[i];
2198  ptcl[k].acc0[0] = ptcl[k].acc0[1] = ptcl[k].acc0[2] = 0.0;
2199  ptcl[k].acc1[0] = ptcl[k].acc1[1] = ptcl[k].acc1[2] = 0.0;
2200  ptcl[k].time = time_;
2201  ptcl[k].dt = 0.0;
2202  pred_[k] = ptcl[k];
2203  // set neighbor radius
2204  neighbors[k].clearNoFreeMem();
2205  Float r_neighbor_crit = ptcl[k].getRNeighbor();
2206  neighbors[k].r_neighbor_crit_sq = r_neighbor_crit*r_neighbor_crit;
2207 
2208  // check parameters
2209  ASSERT(neighbors[k].checkParams());
2210  }
2211 
2212  //group
2213  int* index_group = index_dt_sorted_group_.getDataAddress();
2214 
2215  auto* group_ptr = groups.getDataAddress();
2216  for(int i=0; i<n_init_group_; i++){
2217  int k = index_group[i];
2218  ASSERT(table_group_mask_[k]==false);
2219  // initial cm
2220  auto& pcm = group_ptr[k].particles.cm;
2221  pcm.acc0[0] = pcm.acc0[1] = pcm.acc0[2] = 0.0;
2222  pcm.acc1[0] = pcm.acc1[1] = pcm.acc1[2] = 0.0;
2223  pcm.time = time_;
2224  pcm.dt = 0.0;
2225  ASSERT(k+index_offset_group_<pred_.getSize());
2226  pred_[k+index_offset_group_] = pcm;
2227  }
2228 
2229  dt_limit_ = step.calcNextDtLimit(time_);
2230 
2231  // slowdown not yet initialized, cannot check
2232  // checkGroupResolve(n_init_group_);
2233  writeBackResolvedGroupAndCreateJParticleList(false);
2234 
2235  calcAccJerkNBList(index_single, n_init_single_, index_group, n_init_group_);
2236 
2237  // store predicted force
2238  for(int i=0; i<n_init_single_; i++){
2239  const int k = index_single[i];
2240  ptcl[k].acc0[0] = force_[k].acc0[0];
2241  ptcl[k].acc0[1] = force_[k].acc0[1];
2242  ptcl[k].acc0[2] = force_[k].acc0[2];
2243  ptcl[k].acc1[0] = force_[k].acc1[0];
2244  ptcl[k].acc1[1] = force_[k].acc1[1];
2245  ptcl[k].acc1[2] = force_[k].acc1[2];
2246  }
2247 
2248  for(int i=0; i<n_init_group_; i++){
2249  const int k = index_group[i];
2250  auto& pcm = group_ptr[k].particles.cm;
2251  auto& fcm = force_[k+index_offset_group_];
2252 
2253  pcm.acc0[0] = fcm.acc0[0];
2254  pcm.acc0[1] = fcm.acc0[1];
2255  pcm.acc0[2] = fcm.acc0[2];
2256  pcm.acc1[0] = fcm.acc1[0];
2257  pcm.acc1[1] = fcm.acc1[1];
2258  pcm.acc1[2] = fcm.acc1[2];
2259 
2260  // initial group integration
2261  group_ptr[k].initialIntegration(time_);
2262 
2263  // if >2 particles, initial slowdown perturbation and period
2264  //if (group_ptr[k].particles.getSize()>2) {
2265  // auto& bin_root = group_ptr[k].info.binarytree.getLastMember();
2266  // group_ptr[k].slowdown.calcPertInBinary(bin_root.semi, bin_root.m1, bin_root.m2);
2267  // group_ptr[k].slowdown.calcSlowDownFactor();
2268  //}
2269 
2270  // set time offset
2271  group_ptr[k].info.time_offset = time_offset_;
2272  // set hermite time limit
2273  group_ptr[k].info.dt_limit = step.getDtMax();
2274  // in the case of wide binary, make sure the next time step not exceed r_in
2275  auto& bin_root = group_ptr[k].info.getBinaryTreeRoot();
2276  if (bin_root.semi*(1.0+bin_root.ecc)>group_ptr[k].info.r_break_crit||bin_root.semi<0) {
2277  // ecca <0 indicate binary go to peri-center, ecca=0.0 indicate peri-center, 0-t_peri indicate the time to peri-center
2278  // In the initial step, ecca can >0.0
2279  // get boundary position ecca
2280  Float ecc_anomaly = bin_root.calcEccAnomaly(group_ptr[k].info.r_break_crit);
2281  Float mean_anomaly = bin_root.calcMeanAnomaly(ecc_anomaly, bin_root.ecc);
2282  Float kappa = bin_root.slowdown.getSlowDownFactor();
2283  // get cross boundary position timescale (half the time to peri-center from boundary), notice slowdown factor should be included
2284  // the integrated orbital phase is slow-down by kappa
2285  Float t_peri = 0.5*abs(mean_anomaly/12.5663706144*bin_root.period)*kappa;
2286  Float dt_limit = group_ptr[k].info.dt_limit;
2287  while(dt_limit > t_peri) dt_limit *= 0.5;
2288  group_ptr[k].info.dt_limit = dt_limit;
2289  }
2290 
2291  // check parameters
2292  ASSERT(group_ptr[k].info.checkParams());
2293  ASSERT(group_ptr[k].perturber.checkParams());
2294 
2295 #ifdef ADJUST_GROUP_PRINT
2296  if (manager->adjust_group_write_flag) {
2297  group_ptr[k].printGroupInfo(0, manager->fgroup, WRITE_WIDTH, &(particles.cm));
2298  }
2299 #endif
2300 
2301  }
2302 
2303  calcDt2ndList(index_single, n_init_single_, index_group, n_init_group_, dt_limit_);
2304 
2305  updateTimeNextList(index_single, n_init_single_, index_group, n_init_group_);
2306 
2307  // reset n_init
2308  n_init_single_ = n_init_group_ = 0;
2309  }
2310 
2312 
2316  ASSERT(checkParams());
2317  ASSERT(!particles.isModified());
2318  ASSERT(initial_system_flag_);
2319  ASSERT(!modify_system_flag_);
2320  ASSERT(n_init_group_==0&&n_init_single_==0);
2321 
2322  // get next time
2323  Float time_next = getNextTime();
2324 
2326  interrupt_group_dt_sorted_group_index_ = -1;
2327  interrupt_binary_.clear();
2328  }
2329 
2330 #ifdef HERMITE_DEBUG
2331  if (interrupt_binary_.status!=AR::InterruptStatus::none) ASSERT(interrupt_group_dt_sorted_group_index_>=0);
2332 #endif
2333 
2334  // integrate groups loop
2335  const int n_group_tot = index_dt_sorted_group_.getSize();
2336  const int i_start = interrupt_group_dt_sorted_group_index_>=0 ? interrupt_group_dt_sorted_group_index_ : 0;
2337  int interrupt_index_dt_group_list[n_group_tot];
2338  int n_interrupt_change_dt=0;
2339 
2340  for (int i=i_start; i<n_group_tot; i++) {
2341  const int k = index_dt_sorted_group_[i];
2342 
2343 #ifdef HERMITE_DEBUG
2344  ASSERT(table_group_mask_[k]==false);
2345  if (i!=interrupt_group_dt_sorted_group_index_)
2346  ASSERT(abs(groups[k].getTime()-time_)<=ar_manager->time_error_max);
2347 #endif
2348  // get ds estimation
2349  groups[k].info.calcDsAndStepOption(ar_manager->step.getOrder(), ar_manager->interaction.gravitational_constant, ar_manager->ds_scale);
2350 
2351  // group integration
2352  interrupt_binary_ = groups[k].integrateToTime(time_next);
2353 
2354  // profile
2355  profile.ar_step_count += groups[k].profile.step_count;
2356  profile.ar_step_count_tsyn += groups[k].profile.step_count_tsyn;
2357 
2358  // record interrupt group and quit
2359  if (interrupt_binary_.status!=AR::InterruptStatus::none) {
2360  // particle cm is the old cm in original frame
2361  auto& pcm = groups[k].particles.cm;
2362 
2363  if (interrupt_binary_.status==AR::InterruptStatus::merge||interrupt_binary_.status==AR::InterruptStatus::destroy) {
2364  index_group_merger_.addMember(k);
2365  }
2366  else {
2367  // set initial step flag
2368  groups[k].perturber.initial_step_flag=true;
2369 
2370  if (pcm.time + pcm.dt >time_next) {
2371  ASSERT(i>=n_act_group_);
2372  // set cm step to reach time_next
2373  pcm.dt = time_next - pcm.time;
2374  time_next_[k+index_offset_group_] = time_next;
2375  // recored index in index_dt_sort of the interrupt case for moving later
2376  interrupt_index_dt_group_list[n_interrupt_change_dt++] = i;
2377  }
2378  }
2379 
2380  // correct cm potential energy
2381  //Float dm = correctMassChangePotEnergyBinaryIter(*interrupt_binary_.adr);
2382  // notice the bin_root represent new c.m. in rest frame
2383  auto& bink = groups[k].info.getBinaryTreeRoot();
2384  Float dm = bink.mass - pcm.mass;
2385  Float de_pot = force_[k+index_offset_group_].pot*dm;
2386  energy_.de_cum += de_pot;
2387  energy_.de_binary_interrupt += de_pot;
2388  energy_sd_.de_cum += de_pot;
2389  energy_sd_.de_binary_interrupt += de_pot;
2390 
2391  // correct kinetic energy of cm
2392  ASSERT(!groups[k].particles.isOriginFrame());
2393  // first remove mass loss kinetic energy assuming original cm velocity
2394  auto& vcm = pcm.vel;
2395  auto& vbin = bink.vel;
2396  auto& vbin_bk = groups[k].info.vcm_record;
2397  //Float vbcm[3] = {vcm[0] + vbin_bk[0], vcm[1] + vbin_bk[1], vcm[2] + vbin_bk[2]};
2398  Float de_kin = 0.5*dm*(vcm[0]*vcm[0]+vcm[1]*vcm[1]+vcm[2]*vcm[2]);
2399  // then correct c.m. motion if the c.m. velocity is shifted,
2400  // this can be by adding m_cm(new) * v_cm(new;rest) \dot v_cm(old;origin)
2401  Float dvbin[3] = {vbin[0] - vbin_bk[0], vbin[1] - vbin_bk[1], vbin[2] - vbin_bk[2]};
2402  de_kin += bink.mass*(dvbin[0]*vcm[0]+dvbin[1]*vcm[1]+dvbin[2]*vcm[2]);
2403  energy_.de_cum += de_kin;
2404  energy_.de_binary_interrupt += de_kin;
2405  energy_sd_.de_cum += de_kin;
2406  energy_sd_.de_binary_interrupt += de_kin;
2407 
2408  // back up vcm for later on perturbation kinetic energy correction
2409  vbin_bk[0] = vbin[0];
2410  vbin_bk[1] = vbin[1];
2411  vbin_bk[2] = vbin[2];
2412 
2413 
2414  // update particle dm, velocity should not change to be consistent with frame
2415  pcm.mass += dm;
2416  //particles.cm.mass += dm;
2417 
2418  if (ar_manager->interrupt_detection_option==2) { // quit integration
2419  interrupt_group_dt_sorted_group_index_ = i;
2420  ASSERT(time_next - interrupt_binary_.time_now + ar_manager->time_error_max >= 0.0);
2421  return interrupt_binary_;
2422  }
2423  }
2424 
2425  ASSERT(abs(groups[k].getTime()-time_next)<=ar_manager->time_error_max);
2426  }
2427 
2428  // update index_dt_sorted_group_ due to the change of dt
2429  if (n_interrupt_change_dt>0) {
2430  // shift position starts from the last interrupt index in sorted list (right to left)
2431  int ishift_start = interrupt_index_dt_group_list[n_interrupt_change_dt-1];
2432  // back up group index
2433  int interrupt_index_group_list[n_interrupt_change_dt];
2434  interrupt_index_group_list[0] = index_dt_sorted_group_[ishift_start];
2435  // this is to record the offset need to shift index in sorted list
2436  int shift_offset=1;
2437  // scan from last interrupt index to first
2438  for (int i=n_interrupt_change_dt-2; i>=0; i--) {
2439  // shift left edge
2440  int k = interrupt_index_dt_group_list[i];
2441  // back up next index
2442  interrupt_index_group_list[shift_offset] = index_dt_sorted_group_[k];
2443 
2444  for (int j=ishift_start; j>k+shift_offset; j--) {
2445  index_dt_sorted_group_[j] = index_dt_sorted_group_[j-shift_offset];
2446  }
2447  // set new starting position
2448  ishift_start = k+shift_offset;
2449  // increase offset
2450  shift_offset++;
2451  }
2452  ASSERT(shift_offset==n_interrupt_change_dt);
2453  // shift the range from the first k to beginning of sorted list
2454  for (int j=ishift_start; j>=shift_offset; j--) {
2455  index_dt_sorted_group_[j] = index_dt_sorted_group_[j-shift_offset];
2456  }
2457 
2458  // add all interrupted group indice in front of index_dt_sorted_group_
2459  for (int i=0; i<n_interrupt_change_dt; i++) {
2460  index_dt_sorted_group_[i] = interrupt_index_group_list[i];
2461  }
2462  n_act_group_ += n_interrupt_change_dt;
2463  n_act_group_ = std::min(n_act_group_, n_group_tot);
2464 
2465 #ifdef HERMITE_DEBUG
2466  // check whether the new list is consistent with table_group_mask_
2467  int table_check_mask[n_group_tot];
2468  for (int i=0; i<n_group_tot; i++) table_check_mask[i]=0;
2469  for (int i=0; i<index_dt_sorted_group_.getSize(); i++) table_check_mask[index_dt_sorted_group_[i]]++;
2470  for (int i=0; i<n_group_tot; i++) {
2471  ASSERT((table_check_mask[i]==1&&!table_group_mask_[i])||(table_check_mask[i]==0&&table_group_mask_[i]));
2472  }
2473  for (int i=0; i<n_act_group_; i++) {
2474  ASSERT(time_next_[index_dt_sorted_group_[i]+index_offset_group_] == time_next);
2475  }
2476  if (n_act_group_<n_group_tot) {
2477  ASSERT(time_next_[index_dt_sorted_group_[n_act_group_]+index_offset_group_] > time_next);
2478  }
2479 #endif
2480  }
2481 
2482  // when no interrupt, clear interrupt records
2483  //interrupt_group_dt_sorted_group_index_ = -1;
2484  //interrupt_binary_.clear();
2485 
2486  return interrupt_binary_;
2487  }
2488 
2491  int mod_index[n_act_single_];
2492  int n_mod=0;
2493  for (int i=0; i<n_act_single_; i++) {
2494  int k = index_dt_sorted_single_[i];
2495  // use pred particle to back up velocity, it will be update at predictionAll, thus safe to change
2496  auto& pk = particles[k];
2497  auto& rbk = pred_[k].pos;
2498  auto& vbk = pred_[k].vel;
2499  auto& mbk = pred_[k].mass;
2500  vbk[0] = pk.vel[0];
2501  vbk[1] = pk.vel[1];
2502  vbk[2] = pk.vel[2];
2503  mbk = pk.mass;
2504 
2505  int modified_flag=ar_manager->interaction.modifyOneParticle(pk, time_+time_offset_, getNextTime()+time_offset_);
2506  if (modified_flag) {
2507  auto& v = pk.vel;
2508 #ifdef HERMITE_DEBUG_PRINT
2509  std::cerr<<"Modify one particle: modify_flag: "<<modified_flag
2510  <<" time: "<<pk.time
2511  <<" dt: "<<pk.dt
2512  <<" mass_bk: "<<mbk
2513  <<" mass_new: "<<pk.mass
2514  <<" vel_bk: "<<vbk[0]<<" "<<vbk[1]<<" "<<vbk[2]
2515  <<" vel_new: "<<v[0]<<" "<<v[1]<<" "<<v[2]
2516  <<std::endl;
2517 #endif
2518  // correct potential energy
2519  Float de_pot = force_[k].pot*(pk.mass-mbk);
2520  energy_.de_cum += de_pot;
2521  energy_.de_modify_single += de_pot;
2522  energy_sd_.de_cum += de_pot;
2523  energy_sd_.de_modify_single += de_pot;
2524 
2525  // correct kinetic energy
2526  // first calc original kinetic energy;
2527  Float de_kin = -0.5*mbk*(vbk[0]*vbk[0]+vbk[1]*vbk[1]+vbk[2]*vbk[2]);
2528  // then new energy
2529  de_kin += 0.5*pk.mass*(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
2530  energy_.de_cum += de_kin;
2531  energy_.de_modify_single += de_kin;
2532  energy_sd_.de_cum += de_kin;
2533  energy_sd_.de_modify_single += de_kin;
2534 
2535  neighbors[k].initial_step_flag = true;
2536 
2537  // use predictor as template particle with mass of dm
2538  mbk = pk.mass-mbk;
2539  rbk[0] = pk.pos[0];
2540  rbk[1] = pk.pos[1];
2541  rbk[2] = pk.pos[2];
2542  mod_index[n_mod++] = k;
2543 
2544  //update time next
2545  time_next_[k] = pk.time + pk.dt;
2546  }
2547  }
2548 
2549  // fix over-corrected potential
2550  if (n_mod>1) {
2551  Float de_pot = 0.0;
2552  for (int i=0; i<n_mod; i++) {
2553  for (int j=0; j<i; j++) {
2554  de_pot += manager->interaction.calcEnergyPotSingleSingle(pred_[mod_index[i]],pred_[mod_index[j]]);
2555  }
2556  }
2557  energy_.de_cum += de_pot;
2558  energy_.de_modify_single += de_pot;
2559  energy_sd_.de_cum += de_pot;
2560  energy_sd_.de_modify_single += de_pot;
2561  }
2562  }
2563 
2564 
2566 
2569  ASSERT(checkParams());
2570  ASSERT(!particles.isModified());
2571  ASSERT(initial_system_flag_);
2572  ASSERT(!modify_system_flag_);
2573  ASSERT(n_init_group_==0&&n_init_single_==0);
2575 
2576  // get next time
2577  Float time_next = getNextTime();
2578 
2579  dt_limit_ = step.calcNextDtLimit(time_next);
2580 
2581  // prediction positions
2582  predictAll(time_next);
2583 
2584  // check resolve status
2585  checkGroupResolve(n_act_group_);
2586  writeBackResolvedGroupAndCreateJParticleList(true);
2587 
2588  int* index_single = index_dt_sorted_single_.getDataAddress();
2589  int* index_group = index_dt_sorted_group_.getDataAddress();
2590 
2591  calcAccJerkNBList(index_single, n_act_single_, index_group, n_act_group_);
2592 
2593  correctAndCalcDt4thList(index_single, n_act_single_, index_group, n_act_group_, dt_limit_);
2594 
2595  updateTimeNextList(index_single, n_act_single_, index_group, n_act_group_);
2596 
2597  // update time
2598  time_ = time_next;
2599 
2600  // profile
2601  profile.hermite_single_step_count += n_act_single_;
2602  profile.hermite_group_step_count += n_act_group_;
2603  }
2604 
2606 
2611  void integrateToTimeList(const Float _time_next,
2612  const int* _particle_index,
2613  const int _n_particle) {
2614  ASSERT(checkParams());
2615  ASSERT(!particles.isModified());
2616  ASSERT(initial_system_flag_);
2617 
2618  // adjust dt
2619  int index_single_select[_n_particle];
2620  int n_single_select =0;
2621  int index_group_select[groups.getSize()];
2622  int n_group_select =0;
2623 
2624  auto* ptcl = particles.getDataAddress();
2625  auto* group_ptr = groups.getDataAddress();
2626  for (int i=0; i<_n_particle; i++){
2627  const int k = _particle_index[i];
2628  if (k<index_offset_group_) {
2629  if (table_single_mask_[k]) continue;
2630  if (ptcl[k].time>=_time_next) continue;
2631  ptcl[k].dt = _time_next - ptcl[k].time;
2632  index_single_select[n_single_select++] = k;
2633  }
2634  else {
2635  const int kg = k - index_offset_group_;
2636  if (table_group_mask_[kg]) continue;
2637  auto& pcm = group_ptr[kg].particles.cm;
2638  if (pcm.time>=_time_next) continue;
2639  pcm.dt = _time_next - pcm.time;
2640  index_group_select[n_group_select++] = kg;
2641  }
2642  }
2643 
2644 
2645  if (n_single_select>0 || n_group_select>0) {
2646 
2647  calcAccJerkNBList(index_single_select, n_single_select, index_group_select, n_group_select);
2648 
2649  correctAndCalcDt4thList(index_single_select, n_single_select, index_group_select, n_group_select, dt_limit_);
2650 
2651  updateTimeNextList(index_single_select, n_single_select, index_group_select, n_group_select);
2652  }
2653  }
2654 
2656 
2659  // sort single
2660  std::sort(index_dt_sorted_single_.getDataAddress(), index_dt_sorted_single_.getDataAddress()+n_act_single_, SortIndexDtSingle(time_next_.getDataAddress()));
2661  // sort group
2662  std::sort(index_dt_sorted_group_.getDataAddress(), index_dt_sorted_group_.getDataAddress()+n_act_group_, SortIndexDtGroup(time_next_.getDataAddress(), index_offset_group_));
2663 
2664  // get minimum next time from single and group
2665  time_next_min_ = NUMERIC_FLOAT_MAX;
2666  if (index_dt_sorted_single_.getSize()>0) time_next_min_ = time_next_[index_dt_sorted_single_[0]];
2667  if (index_dt_sorted_group_.getSize()>0) time_next_min_ = std::min(time_next_min_, time_next_[index_dt_sorted_group_[0]+index_offset_group_]);
2668  ASSERT(time_next_min_>0.0);
2669 
2670  // find active singles
2671  const int n_singles = index_dt_sorted_single_.getSize();
2672  for(n_act_single_=0; n_act_single_<n_singles; n_act_single_++){
2673  if (time_next_min_ < time_next_[index_dt_sorted_single_[n_act_single_]]) break;
2674  }
2675 
2676 #ifdef HERMITE_DEBUG
2677  for (int i=0; i<n_singles-1; i++) {
2678  const int k= index_dt_sorted_single_[i];
2679  const int k1= index_dt_sorted_single_[i+1];
2680  ASSERT(particles[k].dt<=particles[k1].dt);
2681  ASSERT(time_next_[k]<=time_next_[k1]);
2682  }
2683 #endif
2684 
2685  // find active groups
2686  const int n_groups = index_dt_sorted_group_.getSize();
2687  for(n_act_group_=0; n_act_group_<n_groups; n_act_group_++){
2688  if (time_next_min_ < time_next_[index_dt_sorted_group_[n_act_group_] + index_offset_group_]) break;
2689  }
2690 
2691 #ifdef HERMITE_DEBUG
2692  for (int i=0; i<n_groups-1; i++) {
2693  const int k= index_dt_sorted_group_[i];
2694  const int k1= index_dt_sorted_group_[i+1];
2695  ASSERT(groups[k].particles.cm.dt<=groups[k1].particles.cm.dt);
2696  ASSERT(k<groups.getSize());
2697  ASSERT(k1<groups.getSize());
2698  ASSERT(time_next_[k+index_offset_group_]<=time_next_[k1+index_offset_group_]);
2699  }
2700 #endif
2701 
2702  ASSERT(!(n_act_single_==0&&n_act_group_==0));
2703 
2704  }
2705 
2708  const int n_group = index_dt_sorted_group_.getSize();
2709  for (int i=0; i<n_group; i++) {
2710  const int k = index_dt_sorted_group_[i];
2711  ASSERT(table_group_mask_[k]==false);
2712  groups[k].template writeBackParticlesOriginFrame<Tparticle>();
2713  }
2714  }
2715 
2717 
2719  void accumDESlowDownChangeBreakGroup(const int _igroup) {
2720  auto& groupi = groups[_igroup];
2721  Float de_binary_interrupt = groupi.getDEChangeBinaryInterrupt();
2722  energy_.de_binary_interrupt += de_binary_interrupt;
2723  energy_.de_cum += de_binary_interrupt;
2724  energy_sd_.de_cum += groupi.getDESlowDownChangeCum();
2725  energy_sd_.de_binary_interrupt += groupi.getDESlowDownChangeBinaryInterrupt();
2726  // add the change due to the shutdown of slowdown
2727  Float etot = groupi.getEkin() + groupi.getEpot();
2728  Float etot_sd = groupi.getEkinSlowDown() + groupi.getEpotSlowDown();
2729  energy_sd_.de_cum += etot - etot_sd;
2730  // add kinetic correction due to vcm change
2731  auto& vcm = groupi.particles.cm.vel;
2732  auto& bink = groupi.info.getBinaryTreeRoot();
2733  auto& vbin = bink.vel;
2734  auto& vbin_bk = groupi.info.vcm_record;
2735  Float dvbin[3] = {vbin[0] - vbin_bk[0], vbin[1] - vbin_bk[1], vbin[2] - vbin_bk[2]};
2736  Float de_kin = bink.mass*(dvbin[0]*vcm[0]+dvbin[1]*vcm[1]+dvbin[2]*vcm[2]);
2737  Float epert = manager->interaction.calcEnergyPertOneGroup(groupi, perturber);
2738  energy_.de_cum -= epert - de_kin;
2739  energy_sd_.de_cum -= epert - de_kin ;
2740  }
2741 
2744  const int n_group = index_dt_sorted_group_.getSize();
2745  ASSERT(n_group <= groups.getSize());
2746  for (int k=0; k<n_group; k++) {
2747  const int i = index_dt_sorted_group_[k];
2748  ASSERT(i<groups.getSize());
2749  ASSERT(i>=0);
2750  auto& gi = groups[i];
2751 
2752  // interrupt energy
2753  Float de_binary_interrupt = gi.getDEChangeBinaryInterrupt();
2754  energy_.de_binary_interrupt += de_binary_interrupt;
2755  energy_.de_cum += de_binary_interrupt;
2756  gi.resetDEChangeBinaryInterrupt();
2757 
2758  // slowdown energy
2759  energy_sd_.de_cum += gi.getDESlowDownChangeCum();
2760  gi.resetDESlowDownChangeCum();
2761  // slowdown interrupt energy
2762  energy_sd_.de_binary_interrupt += gi.getDESlowDownChangeBinaryInterrupt();
2763  gi.resetDESlowDownChangeBinaryInterrupt();
2764  }
2765  }
2766 
2767  // correct energy due to mass change
2768  /*
2769  @param[in] _bin: interrupted binary to correct
2770  \return total mass change
2771  Float correctMassChangePotEnergyBinaryIter(AR::BinaryTree<Tparticle>& _bin) {
2772  Float dm_tot = 0.0;
2773  for (int k=0; k<2; k++) {
2774  if (_bin.isMemberTree(k)) dm_tot += correctMassChangePotEnergyBinaryIter(*_bin.getMemberAsTree(k));
2775  else {
2776  //int i = _bin.getMemberIndex(k);
2777  //Float& pot = force_[i].pot;
2778  auto* pi = _bin.getMember(k);
2779  dm_tot += pi->dm;
2780  //Float de_pot = pot*pi->dm;
2781  de_sd_change_cum_ += de_pot;
2782  de_sd_change_interrupt_ += de_pot;
2783  pi->dm = 0.0;
2784  }
2785  }
2786  return dm_tot;
2787  }
2788  */
2789 
2791 
2793  void calcEnergySlowDown(const bool _initial_flag = false) {
2795  // use a tempare array instead of modify pred_. As a good design, the function of calc energy should not influence integration.
2796  Tparticle ptmp[particles.getSize()];
2797  const auto* ptcl = particles.getDataAddress();
2798  const auto* pred = pred_.getDataAddress();
2799 
2800  // in single case, if particle time is not the current time, used predicted particle
2801  const int n_single = index_dt_sorted_single_.getSize();
2802  for (int k=0; k<n_single; k++){
2803  const int i = index_dt_sorted_single_[k];
2804  if (ptcl[i].time == time_) ptmp[i] = ptcl[i];
2805  else ptmp[i] = pred[i];
2806  }
2807 
2808  // in binary case, if c.m. is not up to date, correct member pos and vel
2809  const int n_group = index_dt_sorted_group_.getSize();
2810  ASSERT(n_group <= groups.getSize());
2811  auto* group_ptr = groups.getDataAddress();
2812  for (int k=0; k<n_group; k++) {
2813  const int i = index_dt_sorted_group_[k];
2814  const auto& groupi = group_ptr[i];
2815  const auto& pcm = groupi.particles.cm;
2816  auto& predcm = pred[i+index_offset_group_];
2817 
2818  const int n_member = groupi.particles.getSize();
2819  for (int j=0; j<n_member; j++) {
2820  const int kj = groupi.info.particle_index[j];
2821  ASSERT(kj<particles.getSize());
2822  ptmp[kj] = ptcl[kj];
2823  // if not up to date, add difference of predicted and current cm pos and vel to predicted members
2824  if (pcm.time < time_ ) {
2825  ptmp[kj].pos[0] += predcm.pos[0] - pcm.pos[0];
2826  ptmp[kj].pos[1] += predcm.pos[1] - pcm.pos[1];
2827  ptmp[kj].pos[2] += predcm.pos[2] - pcm.pos[2];
2828  ptmp[kj].vel[0] += predcm.vel[0] - pcm.vel[0];
2829  ptmp[kj].vel[1] += predcm.vel[1] - pcm.vel[1];
2830  ptmp[kj].vel[2] += predcm.vel[2] - pcm.vel[2];
2831  }
2832  }
2833  }
2834 
2835  // since a part of particles are not up to date, used predict particles to calculate energy,
2836  // notice pred_.size is larger than particles.size because group c.m. is in pred_.
2837  manager->interaction.calcEnergy(energy_, ptmp, particles.getSize(), groups.getDataAddress(), index_dt_sorted_group_.getDataAddress(), index_dt_sorted_group_.getSize(), perturber);
2839  // slowdown energy
2840  energy_sd_.ekin = energy_.ekin;
2841  energy_sd_.epot = energy_.epot;
2842  energy_sd_.epert= energy_.epert;
2843  for (int i=0; i<groups.getSize(); i++) {
2844  auto& gi = groups[i];
2845  energy_sd_.ekin -= gi.getEkin();
2846  energy_sd_.epot -= gi.getEpot();
2847 
2848  energy_sd_.ekin += gi.getEkinSlowDown();
2849  energy_sd_.epot += gi.getEpotSlowDown();
2850  }
2851  if (_initial_flag) {
2852  energy_init_ref_ = energy_.getEtot();
2853  }
2854  energy_.etot_ref = energy_init_ref_ + energy_.de_cum;
2855  energy_sd_.etot_ref = energy_init_ref_ + energy_sd_.de_cum;
2856  }
2857 
2860  return energy_sd_.getEnergyError();
2861  }
2862 
2864 
2867  return energy_.getEnergyError();
2868  }
2869 
2871 
2873  Float getEtot() const {
2874  return energy_.getEtot();
2875  }
2876 
2878 
2880  Float getEtotRef() const {
2881  return energy_.etot_ref;
2882  }
2883 
2885 
2888  return energy_sd_.getEtot();
2889  }
2890 
2892 
2895  return energy_sd_.etot_ref;
2896  }
2897 
2899 
2901  Float getEkin() const {
2902  return energy_.ekin;
2903  }
2904 
2906 
2909  return energy_sd_.ekin;
2910  }
2911 
2913 
2915  Float getEpot() const {
2916  return energy_.epot;
2917  }
2918 
2920 
2923  return energy_sd_.epot;
2924  }
2925 
2928  return energy_sd_.de_cum;
2929  }
2930 
2933  energy_sd_.de_cum = 0.0;
2934  }
2935 
2938  return energy_sd_.de_binary_interrupt;
2939  }
2940 
2943  energy_sd_.de_binary_interrupt = 0.0;
2944  }
2945 
2948  return energy_sd_.de_modify_single;
2949  }
2950 
2953  energy_sd_.de_modify_single = 0.0;
2954  }
2955 
2958  return energy_.de_cum;
2959  }
2960 
2963  energy_.de_cum = 0.0;
2964  }
2965 
2968  return energy_.de_binary_interrupt;
2969  }
2970 
2973  energy_.de_binary_interrupt = 0.0;
2974  }
2975 
2978  return energy_.de_modify_single;
2979  }
2980 
2983  energy_.de_modify_single = 0.0;
2984  }
2985 
2987  Float getNextTime() const {
2988  return time_next_min_;
2989  }
2990 
2992  Float getTimeInt() const {
2993  return time_;
2994  }
2995 
2997  Float getTime() const {
2998  return time_+time_offset_;
2999  }
3000 
3002  void setTimeOffset(const Float& _time_offset) {
3003  time_offset_ = _time_offset;
3004  }
3005 
3007 
3010  if (interrupt_group_dt_sorted_group_index_>=0)
3011  return index_dt_sorted_group_[interrupt_group_dt_sorted_group_index_];
3012  else return -1;
3013  }
3014 
3016 
3019  return interrupt_binary_;
3020  }
3021 
3023  int getNActSingle() const{
3024  return n_act_single_;
3025  }
3026 
3028  int getNActGroup() const{
3029  return n_act_group_;
3030  }
3031 
3033  int getNInitGroup() const{
3034  return n_init_group_;
3035  }
3036 
3038  int getNInitSingle() const{
3039  return n_init_single_;
3040  }
3041 
3043  int getNGroup() const {
3044  return index_dt_sorted_group_.getSize();
3045  }
3046 
3048  int getNSingle() const {
3049  return index_dt_sorted_single_.getSize();
3050  }
3051 
3053  int getIndexOffsetGroup() const {
3054  return index_offset_group_;
3055  }
3056 
3059  return index_dt_sorted_single_.getDataAddress();
3060  }
3061 
3064  return index_dt_sorted_group_.getDataAddress();
3065  }
3066 
3068 
3075  void printColumnTitle(std::ostream & _fout, const int _width, const int _n_sd_list[], const int _n_group, const int _n_sd_tot) {
3076  _fout<<std::setw(_width)<<"Time"
3077  <<std::setw(_width)<<"dE"
3078  <<std::setw(_width)<<"Etot_ref"
3079  <<std::setw(_width)<<"Ekin"
3080  <<std::setw(_width)<<"Epot"
3081  <<std::setw(_width)<<"Epert"
3082  <<std::setw(_width)<<"dE_cum"
3083  <<std::setw(_width)<<"dE_intr"
3084  <<std::setw(_width)<<"dE_mod"
3085  <<std::setw(_width)<<"dE_SD"
3086  <<std::setw(_width)<<"Etot_SD_ref"
3087  <<std::setw(_width)<<"Ekin_SD"
3088  <<std::setw(_width)<<"Epot_SD"
3089  <<std::setw(_width)<<"Epert_SD"
3090  <<std::setw(_width)<<"dE_SD_cum"
3091  <<std::setw(_width)<<"dE_SD_intr"
3092  <<std::setw(_width)<<"dE_SD_mod"
3093  <<std::setw(_width)<<"N_SD";
3094 #if (defined AR_SLOWDOWN_ARRAY) || (defined AR_SLOWDOWN_TREE)
3095  AR::SlowDown sd_empty;
3096  int n_sd_count = 0;
3097  for (int i=0; i<_n_group; i++) {
3098  n_sd_count += _n_sd_list[i];
3099  for (int j=0; j<_n_sd_list[i]; j++)
3100  sd_empty.printColumnTitle(_fout, _width);
3101  }
3102  ASSERT(_n_sd_tot == n_sd_count);
3103 #endif
3104  perturber.printColumnTitle(_fout, _width);
3105  info.printColumnTitle(_fout, _width);
3106  profile.printColumnTitle(_fout, _width);
3107  particles.printColumnTitle(_fout, _width);
3108  }
3109 
3111 
3118  void printColumn(std::ostream & _fout, const int _width, const int _n_sd_list[], const int _n_group, const int _n_sd_tot){
3119  _fout<<std::setw(_width)<<time_;
3120  energy_.printColumn(_fout, _width);
3121  energy_sd_.printColumn(_fout, _width);
3122  _fout<<std::setw(_width)<<_n_sd_tot;
3123  AR::SlowDown sd_empty;
3124  int n_group_now = groups.getSize();
3125 #if (defined AR_SLOWDOWN_ARRAY) || (defined AR_SLOWDOWN_TREE)
3126  int n_sd_count = 0;
3127  for (int i=0; i<_n_group; i++) {
3128  n_sd_count += _n_sd_list[i];
3129  if (i<n_group_now) {
3130  auto & gi = groups[i];
3131 #ifdef AR_SLOWDOWN_ARRAY
3132  int n_sd_in = gi.binary_slowdown.getSize();
3133  for (int j=0; j<_n_sd_list[i]; j++) {
3134  if (j<n_sd_in) gi.binary_slowdown[j]->slowdown.printColumn(_fout, _width);
3135  else sd_empty.printColumn(_fout, _width);
3136  }
3137 #else
3138  int n_sd_in = gi.info.binarytree.getSize();
3139  for (int j=0; j<_n_sd_list[i]; j++) {
3140  if (j<n_sd_in) gi.info.binarytree[j].slowdown.printColumn(_fout, _width);
3141  else sd_empty.printColumn(_fout, _width);
3142  }
3143 #endif
3144  }
3145  else {
3146  for (int j=0; j<_n_sd_list[i]; j++) sd_empty.printColumn(_fout, _width);
3147  sd_empty.printColumn(_fout, _width);
3148  }
3149  }
3150  ASSERT(_n_sd_tot == n_sd_count);
3151 #endif
3152  perturber.printColumn(_fout, _width);
3153  info.printColumn(_fout, _width);
3154  profile.printColumn(_fout, _width);
3155  particles.printColumn(_fout, _width);
3156  }
3157 
3160  std::map<Float, int> stephist;
3161  for(int i=0; i<index_dt_sorted_single_.getSize(); i++) {
3162  int k = index_dt_sorted_single_[i];
3163  Float dt = particles[k].dt;
3164  std::map<Float, int>::iterator p = stephist.find(dt);
3165  if (p==stephist.end()) stephist[dt] = 1;
3166  else stephist[dt]++;
3167  }
3168  for(int i=0; i<index_dt_sorted_group_.getSize(); i++) {
3169  int k = index_dt_sorted_group_[i];
3170  Float dt=groups[k].particles.cm.dt;
3171  std::map<Float, int>::iterator p = stephist.find(dt);
3172  if (p==stephist.end()) stephist[dt] = 1;
3173  else stephist[dt]++;
3174  }
3175  std::cerr<<"Step hist: time = "<<time_<<"\n";
3176  for(auto i=stephist.begin(); i!=stephist.end(); i++) {
3177  std::cerr<<std::setw(24)<<i->first;
3178  }
3179  std::cerr<<std::endl;
3180  for(auto i=stephist.begin(); i!=stephist.end(); i++) {
3181  std::cerr<<std::setw(24)<<i->second;
3182  }
3183  std::cerr<<std::endl;
3184  }
3185 
3186  };
3187 }
COMM::List::getDataAddress
Ttype * getDataAddress() const
return member data array address
Definition: list.h:170
H4::HermiteIntegrator::readGroupConfigureAscii
void readGroupConfigureAscii(std::istream &_fin)
Add group based on a configure file.
Definition: hermite_integrator.h:1339
H4::ParticleH4::acc0
Float acc0[3]
Definition: hermite_particle.h:41
H4::BlockTimeStep4th::calcBlockDt4th
Float calcBlockDt4th(const Float *acc0, const Float *acc1, const Float *acc2, const Float *acc3, const Float _dt_limit) const
Calculate 4th order block time step.
Definition: block_time_step.h:223
H4::HermiteIntegrator::calcEnergySlowDown
void calcEnergySlowDown(const bool _initial_flag=false)
calculate slowdown energy
Definition: hermite_integrator.h:2793
H4::HermiteIntegrator::integrateToTimeList
void integrateToTimeList(const Float _time_next, const int *_particle_index, const int _n_particle)
Integration a list of particle to current time (ingore dt)
Definition: hermite_integrator.h:2611
H4::HermiteEnergy::ekin
Float ekin
kinetic energy
Definition: hermite_integrator.h:130
H4::HermiteIntegrator::resetDEChangeCum
void resetDEChangeCum()
reset cumulative energy change
Definition: hermite_integrator.h:2962
H4::Profile
Definition: Hermite/profile.h:4
block_time_step.h
H4::HermiteIntegrator::getDEChangeModifySingle
Float getDEChangeModifySingle() const
get cumulative energy change due to modification of one particle
Definition: hermite_integrator.h:2977
AR::TimeTransformedSymplecticManager::ds_scale
Float ds_scale
minimum real time step allown
Definition: symplectic_integrator.h:63
AR::SlowDown::timescale
Float timescale
Definition: slow_down.h:22
H4::HermiteIntegrator::initialIntegration
void initialIntegration()
Initial Hermite integrator.
Definition: hermite_integrator.h:2148
neighbor.h
H4::HermiteIntegrator::getNSingle
int getNSingle() const
get N single
Definition: hermite_integrator.h:3048
H4::HermiteEnergy::de_modify_single
Float de_modify_single
energy change due to modify function
Definition: hermite_integrator.h:135
H4::HermiteIntegrator::resetDESlowDownChangeBinaryInterrupt
void resetDESlowDownChangeBinaryInterrupt()
reset cumulative slowdown energy change due to interruption
Definition: hermite_integrator.h:2942
H4::HermiteIntegrator::getNInitGroup
int getNInitGroup() const
get initial number of particles of groups
Definition: hermite_integrator.h:3033
H4::HermiteIntegrator::printColumn
void printColumn(std::ostream &_fout, const int _width, const int _n_sd_list[], const int _n_group, const int _n_sd_tot)
print data of class members using column style
Definition: hermite_integrator.h:3118
H4::HermiteIntegrator::getDEChangeBinaryInterrupt
Float getDEChangeBinaryInterrupt() const
get cumulative energy change due to interruption
Definition: hermite_integrator.h:2967
H4::Profile::hermite_single_step_count
UInt64 hermite_single_step_count
Definition: Hermite/profile.h:7
Float.h
H4::HermiteIntegrator::HermiteIntegrator
HermiteIntegrator()
constructor
Definition: hermite_integrator.h:270
H4::HermiteIntegrator::getInterruptBinary
AR::InterruptBinary< Tparticle > & getInterruptBinary()
get interrupt binary (tree) address
Definition: hermite_integrator.h:3018
COMM::List::clear
void clear()
Clear function.
Definition: list.h:76
AR::InterruptBinary::time_now
Float time_now
Definition: interrupt.h:14
H4::HermiteEnergy::de_cum
Float de_cum
cumulative energy change
Definition: hermite_integrator.h:133
NUMERIC_FLOAT_MAX
const Float NUMERIC_FLOAT_MAX
Definition: Float.h:29
H4::HermiteIntegrator::writeBackGroupMembers
void writeBackGroupMembers()
write back group members to particles
Definition: hermite_integrator.h:2707
H4::HermiteIntegrator::checkParams
bool checkParams()
check whether parameters values are correct
Definition: hermite_integrator.h:259
AR::TimeTransformedSymplecticManager::slowdown_pert_ratio_ref
Float slowdown_pert_ratio_ref
scaling factor to determine ds
Definition: symplectic_integrator.h:64
H4::HermiteIntegrator::sortDtAndSelectActParticle
void sortDtAndSelectActParticle()
sort time step array and select active particles
Definition: hermite_integrator.h:2658
AR::TimeTransformedSymplecticManager::step
SymplecticStep step
class contain interaction function
Definition: symplectic_integrator.h:73
H4::HermiteEnergy::printColumn
void printColumn(std::ostream &_fout, const int _width=20)
print data of class members using column style
Definition: hermite_integrator.h:176
H4::HermiteIntegrator::getNInitSingle
int getNInitSingle() const
get initial number of particles of singles
Definition: hermite_integrator.h:3038
H4::Profile::hermite_group_step_count
UInt64 hermite_group_step_count
Definition: Hermite/profile.h:8
H4::HermiteIntegrator::printStepHist
void printStepHist()
print step histogram
Definition: hermite_integrator.h:3159
H4::printFeatures
void printFeatures(std::ostream &fout)
print features
Definition: hermite_integrator.h:16
WRITE_WIDTH
const int WRITE_WIDTH
Definition: Float.h:30
COMM::Binary
Binary parameter class.
Definition: binary_tree.h:12
H4::HermiteIntegrator::addGroups
void addGroups(const int *_particle_index, const int *_n_group_offset, const int _n_group)
add groups from lists of particle index
Definition: hermite_integrator.h:1209
H4::HermiteEnergy::printColumnTitle
void printColumnTitle(std::ostream &_fout, const int _width=20)
print titles of class members using column style
Definition: hermite_integrator.h:160
H4::printDebugFeatures
void printDebugFeatures(std::ostream &fout)
print features
Definition: hermite_integrator.h:23
AR::InterruptStatus::destroy
@ destroy
H4::BlockTimeStep4th::calcBlockDt2nd
Float calcBlockDt2nd(const Float *_acc0, const Float *_acc1, const Float _dt_limit) const
Calculate 2nd order block time step.
Definition: block_time_step.h:195
H4::HermiteManager
Hermite manager class.
Definition: hermite_integrator.h:31
AR::SlowDown::printColumnTitle
static void printColumnTitle(std::ostream &_fout, const int _width=20)
print titles of class members using column style
Definition: slow_down.h:193
H4::HermiteIntegrator::groups
COMM::List< ARSym > groups
Definition: hermite_integrator.h:250
H4::HermiteIntegrator::particles
COMM::ParticleGroup< H4Ptcl, Tpcm > particles
Definition: hermite_integrator.h:249
COMM::List::addMember
void addMember(const T &_member)
copy one member and its address
Definition: list.h:234
profile.h
H4::HermiteIntegrator::resetDESlowDownChangeCum
void resetDESlowDownChangeCum()
reset cumulative slowdown energy change due to slowdown change
Definition: hermite_integrator.h:2932
AR::InterruptBinary
Binary interrupt recoreder.
Definition: interrupt.h:12
AR::TimeTransformedSymplecticManager::checkParams
bool checkParams()
check whether parameters values are correct
Definition: symplectic_integrator.h:86
H4::BlockTimeStep4th::printColumnTitle
void printColumnTitle(std::ostream &_fout, const int _width=20)
print titles of class members using column style
Definition: block_time_step.h:255
H4::HermiteIntegrator::ar_manager
AR::TimeTransformedSymplecticManager< TARacc > * ar_manager
integration manager
Definition: hermite_integrator.h:248
H4::Profile::printColumn
void printColumn(std::ostream &_fout, const int _width=20)
print data of class members using column style
Definition: Hermite/profile.h:42
AR::SlowDown::pert_out
Float pert_out
Definition: slow_down.h:21
H4::HermiteIntegrator::getSortDtIndexGroup
int * getSortDtIndexGroup()
get sorted dt index of groups
Definition: hermite_integrator.h:3063
AR::TimeTransformedSymplecticIntegrator
Time Transformed Symplectic integrator class for a group of particles.
Definition: symplectic_integrator.h:175
H4::HermiteIntegrator::getEtot
Float getEtot() const
Get current total energy from ekin and epot.
Definition: hermite_integrator.h:2873
H4::BlockTimeStep4th::readBinary
void readBinary(FILE *_fin)
read class data to file with binary format
Definition: block_time_step.h:282
H4::HermiteIntegrator::getIndexOffsetGroup
int getIndexOffsetGroup() const
get group index offset (group index + N_single_max)
Definition: hermite_integrator.h:3053
H4::HermiteIntegrator
Hermite integrator class.
Definition: hermite_integrator.h:190
H4::HermiteIntegrator::step
BlockTimeStep4th step
Definition: hermite_integrator.h:246
H4::BlockTimeStep4th::calcNextDtLimit
Float calcNextDtLimit(const Float _time)
calculate the maximum time step limit for next block step based on the input (current) time
Definition: block_time_step.h:160
H4::HermiteIntegrator::manager
HermiteManager< Tacc > * manager
time step calculator
Definition: hermite_integrator.h:247
AR::InterruptStatus::merge
@ merge
H4::ParticleH4
particle type for AR integrator, not necessary anymore
Definition: hermite_particle.h:6
H4::HermiteIntegrator::info
Tinfo info
information of the system
Definition: hermite_integrator.h:253
H4::HermiteIntegrator::modifySingleParticles
void modifySingleParticles()
modify single particles due to external functions, update energy
Definition: hermite_integrator.h:2490
H4::HermiteIntegrator::getDESlowDownChangeBinaryInterrupt
Float getDESlowDownChangeBinaryInterrupt() const
get cumulative slowdown energy change due to interruption
Definition: hermite_integrator.h:2937
COMM::ParticleGroup
Particle group class to store and manage a group of particle.
Definition: particle_group.h:13
COMM::List::increaseSizeNoInitialize
void increaseSizeNoInitialize(const int _n)
increase size without initialization
Definition: list.h:246
H4::HermiteIntegrator::getInterruptGroupIndex
int getInterruptGroupIndex() const
get interrupt group index
Definition: hermite_integrator.h:3009
H4::HermiteIntegrator::getEtotSlowDownRef
Float getEtotSlowDownRef() const
Get current total integrated energy with inner slowdown.
Definition: hermite_integrator.h:2894
H4::HermiteIntegrator::getNextTime
Float getNextTime() const
get next time for intergration
Definition: hermite_integrator.h:2987
H4::HermiteIntegrator::clear
void clear()
clear function
Definition: hermite_integrator.h:285
H4::BlockTimeStep4th::getDtMax
Float getDtMax() const
get maximum time step
Definition: block_time_step.h:144
AR::SlowDown::calcSlowDownFactor
Float calcSlowDownFactor()
calculate slowdown factor based on perturbation and inner acceleration
Definition: slow_down.h:74
H4::HermiteIntegrator::integrateSingleOneStepAct
void integrateSingleOneStepAct()
Integration single active particles and update steps.
Definition: hermite_integrator.h:2568
H4::BlockTimeStep4th::print
void print(std::ostream &_fout) const
Definition: block_time_step.h:244
H4::HermiteIntegrator::checkNewGroup
void checkNewGroup(int *_new_group_particle_index_origin, int *_new_n_group_offset, int &_new_n_group, int *_break_group_index_with_offset, int &_n_break_no_add, const int _n_break, const bool _start_flag)
check the pair with distance below r_crit for ptcl in adr_dt_sorted_
Definition: hermite_integrator.h:1835
ar_information.h
COMM::List::getSize
int getSize() const
get current member number
Definition: list.h:151
Float
double Float
Definition: Float.h:25
H4::HermiteIntegrator::getEpotSlowDown
Float getEpotSlowDown() const
Get current potential energy with slowdown.
Definition: hermite_integrator.h:2922
H4::HermiteIntegrator::getEpot
Float getEpot() const
Get current potential energy.
Definition: hermite_integrator.h:2915
H4::HermiteIntegrator::getDESlowDownChangeModifySingle
Float getDESlowDownChangeModifySingle() const
get cumulative slowdown energy change due to modification of one particle
Definition: hermite_integrator.h:2947
H4::NBAdr
Definition: neighbor.h:14
H4::HermiteIntegrator::adjustGroups
void adjustGroups(const bool _start_flag)
adjust groups
Definition: hermite_integrator.h:2106
AR::TimeTransformedSymplecticManager::interaction
Tmethod interaction
1: detect interruption; 0: no detection
Definition: symplectic_integrator.h:72
H4::HermiteIntegrator::getDEChangeCum
Float getDEChangeCum() const
get cumulative energy change
Definition: hermite_integrator.h:2957
AR::TimeTransformedSymplecticManager::slowdown_timescale_max
Float slowdown_timescale_max
slowdown perturbation /inner ratio reference factor
Definition: symplectic_integrator.h:68
H4::HermiteIntegrator::resetDEChangeModifySingle
void resetDEChangeModifySingle()
reset cumulative energy change due to modification of one particle
Definition: hermite_integrator.h:2982
hermite_particle.h
H4::HermiteIntegrator::neighbors
COMM::List< Neighbor< Tparticle > > neighbors
Definition: hermite_integrator.h:251
H4::HermiteEnergy::epot
Float epot
potential energy
Definition: hermite_integrator.h:131
symplectic_integrator.h
AR::InterruptStatus::none
@ none
H4::HermiteManager::step
BlockTimeStep4th step
class contain interaction function
Definition: hermite_integrator.h:36
H4::HermiteIntegrator::breakGroups
void breakGroups(int *_new_group_particle_index_origin, int *_new_n_group_offset, int &_new_n_group, const int *_break_group_index_with_offset, const int _n_break_no_add, const int _n_break)
break groups
Definition: hermite_integrator.h:1373
H4::HermiteIntegrator::perturber
Tpert perturber
Definition: hermite_integrator.h:252
COMM::Binary::printColumnTitle
static void printColumnTitle(std::ostream &_fout, const int _width=20)
print titles of class members using column style
Definition: binary_tree.h:380
H4::Profile::printColumnTitle
void printColumnTitle(std::ostream &_fout, const int _width=20)
print titles of class members using column style
Definition: Hermite/profile.h:28
H4::HermiteEnergy::clear
void clear()
clear function
Definition: hermite_integrator.h:140
H4::HermiteIntegrator::getNActGroup
int getNActGroup() const
get active number of particles of groups
Definition: hermite_integrator.h:3028
H4::HermiteIntegrator::getEnergyError
Float getEnergyError() const
get energy error
Definition: hermite_integrator.h:2866
H4::HermiteIntegrator::printColumnTitle
void printColumnTitle(std::ostream &_fout, const int _width, const int _n_sd_list[], const int _n_group, const int _n_sd_tot)
print titles of class members using column style
Definition: hermite_integrator.h:3075
H4::HermiteIntegrator::getTime
Float getTime() const
get current real time
Definition: hermite_integrator.h:2997
H4::Profile::ar_step_count
UInt64 ar_step_count
Definition: Hermite/profile.h:9
H4::TimeStep4th::checkParams
bool checkParams()
check whether parameters values are correct
Definition: block_time_step.h:18
H4::HermiteEnergy::getEtot
Float getEtot() const
calc total energy
Definition: hermite_integrator.h:151
H4::HermiteIntegrator::setTimeOffset
void setTimeOffset(const Float &_time_offset)
set time offset
Definition: hermite_integrator.h:3002
COMM::List< int >
H4::HermiteIntegrator::profile
Profile profile
Definition: hermite_integrator.h:254
H4::HermiteIntegrator::getNActSingle
int getNActSingle() const
get active number of particles of singles
Definition: hermite_integrator.h:3023
H4::HermiteIntegrator::getEkin
Float getEkin() const
Get current kinetic energy.
Definition: hermite_integrator.h:2901
H4::HermiteManager::printColumnTitle
void printColumnTitle(std::ostream &_fout, const int _width=20)
print titles of class members using column style
Definition: hermite_integrator.h:75
H4::HermiteManager::checkParams
bool checkParams()
time step calculator
Definition: hermite_integrator.h:50
H4::HermiteIntegrator::accumDESlowDownChangeBreakGroup
void accumDESlowDownChangeBreakGroup(const int _igroup)
correct Etot slowdown reference due to the groups change
Definition: hermite_integrator.h:2719
AR::InterruptBinary::status
InterruptStatus status
Definition: interrupt.h:16
H4::Profile::new_group_count
UInt64 new_group_count
Definition: Hermite/profile.h:12
AR::SlowDown::pert_in
Float pert_in
Definition: slow_down.h:20
H4::HermiteEnergy::HermiteEnergy
HermiteEnergy()
Definition: hermite_integrator.h:137
H4::HermiteManager::interaction
Tmethod interaction
Definition: hermite_integrator.h:35
H4::HermiteManager::writeBinary
void writeBinary(FILE *_fp) const
write class data to file with binary format
Definition: hermite_integrator.h:93
H4::HermiteEnergy::de_binary_interrupt
Float de_binary_interrupt
energy change due to interruption
Definition: hermite_integrator.h:134
H4::BlockTimeStep4th
Definition: block_time_step.h:120
H4::HermiteIntegrator::initialSystemSingle
void initialSystemSingle(const Float _time_sys)
Initial system array.
Definition: hermite_integrator.h:1165
H4::HermiteIntegrator::accumDESlowDownChange
void accumDESlowDownChange()
correct Etot slowdown reference due to the groups change
Definition: hermite_integrator.h:2743
COMM::List::resizeNoInitialize
void resizeNoInitialize(const int _n)
increase size without initialization
Definition: list.h:268
H4::HermiteIntegrator::getNGroup
int getNGroup() const
get N groups
Definition: hermite_integrator.h:3043
H4::HermiteIntegrator::resetDESlowDownChangeModifySingle
void resetDESlowDownChangeModifySingle()
reset cumulative slowdown energy change due to modification of one particle
Definition: hermite_integrator.h:2952
H4::Profile::ar_step_count_tsyn
UInt64 ar_step_count_tsyn
Definition: Hermite/profile.h:10
H4::HermiteIntegrator::getEtotRef
Float getEtotRef() const
Get current total integrated energy.
Definition: hermite_integrator.h:2880
H4
Definition: ar_information.h:9
H4::HermiteIntegrator::checkBreak
void checkBreak(int *_break_group_index_with_offset, int &_n_break, const bool _start_flag)
Check break condition.
Definition: hermite_integrator.h:1575
H4::HermiteIntegrator::getEtotSlowDown
Float getEtotSlowDown() const
Get current total energy with slowdown from ekin_sd and epot_sd.
Definition: hermite_integrator.h:2887
H4::Profile::clear
void clear()
Definition: Hermite/profile.h:16
H4::HermiteIntegrator::getEkinSlowDown
Float getEkinSlowDown() const
Get current kinetic energy with slowdown.
Definition: hermite_integrator.h:2908
COMM::ListMode::local
@ local
H4::HermiteEnergy::etot_ref
Float etot_ref
total energy for reference
Definition: hermite_integrator.h:129
H4::HermiteManager::printColumn
void printColumn(std::ostream &_fout, const int _width=20)
print data of class members using column style
Definition: hermite_integrator.h:85
H4::BlockTimeStep4th::printColumn
void printColumn(std::ostream &_fout, const int _width=20)
print data of class members using column style
Definition: block_time_step.h:266
COMM::List::setMode
void setMode(const ListMode _mode)
set mode
Definition: list.h:39
H4::HermiteIntegrator::getSortDtIndexSingle
int * getSortDtIndexSingle()
get sorted dt index of singles
Definition: hermite_integrator.h:3058
H4::Profile::break_group_count
UInt64 break_group_count
Definition: Hermite/profile.h:11
H4::HermiteManager::print
void print(std::ostream &_fout) const
print parameters
Definition: hermite_integrator.h:62
list.h
H4::HermiteIntegrator::reserveIntegratorMem
void reserveIntegratorMem()
reserve memory for system
Definition: hermite_integrator.h:322
AR::SlowDown::initialSlowDownReference
void initialSlowDownReference(const Float _kappa_ref, const Float _timescale_max)
initialize slow-down parameters
Definition: slow_down.h:42
H4::HermiteIntegrator::getDESlowDownChangeCum
Float getDESlowDownChangeCum() const
get cumulative slowdown energy change due to slowdown change
Definition: hermite_integrator.h:2927
AR::SlowDown::getSlowDownFactorOrigin
Float getSlowDownFactorOrigin() const
Get original slow-down factor.
Definition: slow_down.h:102
H4::HermiteEnergy::epert
Float epert
perturbation extra energy
Definition: hermite_integrator.h:132
AR::SlowDown
Slow-down parameter control class.
Definition: slow_down.h:11
COMM::List::getLastMember
Ttype & getLastMember()
return last member
Definition: list.h:180
H4::NBType::group
@ group
H4::HermiteIntegrator::integrateGroupsOneStep
AR::InterruptBinary< Tparticle > & integrateGroupsOneStep()
Integrate groups.
Definition: hermite_integrator.h:2315
COMM::List::decreaseSizeNoInitialize
void decreaseSizeNoInitialize(const int _n)
increase size without initialization
Definition: list.h:257
H4::BlockTimeStep4th::writeBinary
void writeBinary(FILE *_fp) const
write class data to file with binary format
Definition: block_time_step.h:275
H4::HermiteManager::readBinary
void readBinary(FILE *_fin)
read class data to file with binary format
Definition: hermite_integrator.h:106
COMM::List::reserveMem
void reserveMem(const int _nmax)
Memory allocation for storing members.
Definition: list.h:48
H4::HermiteEnergy::getEnergyError
Float getEnergyError() const
calc energy error
Definition: hermite_integrator.h:146
AR::InterruptBinary::clear
void clear()
Definition: interrupt.h:29
H4::HermiteIntegrator::getTimeInt
Float getTimeInt() const
get current integration time
Definition: hermite_integrator.h:2992
COMM::ListMode::copy
@ copy
AR::SlowDown::period
Float period
Definition: slow_down.h:23
AR::TimeTransformedSymplecticManager::time_error_max
Float time_error_max
Definition: symplectic_integrator.h:60
H4::HermiteEnergy
Hermite energy.
Definition: hermite_integrator.h:127
AR::SlowDown::printColumn
void printColumn(std::ostream &_fout, const int _width=20)
print data of class members using column style
Definition: slow_down.h:204
AR::TimeTransformedSymplecticManager::interrupt_detection_option
int interrupt_detection_option
maximum step counts
Definition: symplectic_integrator.h:70
H4::HermiteIntegrator::resetDEChangeBinaryInterrupt
void resetDEChangeBinaryInterrupt()
reset cumulative energy change due to interruption
Definition: hermite_integrator.h:2972
AR::TimeTransformedSymplecticManager< TARacc >
H4::HermiteIntegrator::getEnergyErrorSlowDown
Float getEnergyErrorSlowDown() const
get slowdown energy error
Definition: hermite_integrator.h:2859
AR::SymplecticStep::getOrder
int getOrder() const
get Symplectic order
Definition: symplectic_step.h:330