Swarm-NG  1.1
montecarlo_ecclimit.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  * Copyright (C) 2009-2010 by Eric Ford & the Swarm-NG Development Team *
3  * *
4  * This program is free software; you can redistribute it and/or modify *
5  * it under the terms of the GNU General Public License as published by *
6  * the Free Software Foundation; either version 3 of the License. *
7  * *
8  * This program is distributed in the hope that it will be useful, *
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
11  * GNU General Public License for more details. *
12  * *
13  * You should have received a copy of the GNU General Public License *
14  * along with this program; if not, write to the *
15  * Free Software Foundation, Inc., *
16  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
17  ************************************************************************/
18 
31 #include <iostream>
32 #include <fstream>
33 #include <math.h>
34 #include <signal.h>
35 #include <ctime>
36 #include <unistd.h>
37 #include "swarm/swarm.h"
38 #include "swarm/snapshot.hpp"
39 #include "swarm/log/log.hpp"
40 #include "random.hpp"
41 #include "kepler.hpp"
42 
43 #define SYNC cudaThreadSynchronize()
44 
45 
46 using namespace swarm;
47 using namespace std;
48 
49 config cfg;
51 void inspect(defaultEnsemble &ens, const int sys, const int bod )
52 {
53  fprintf(stderr,"%d %d: %lg (%lg %lg %lg) (%lg %lg %lg) \n", sys, bod,
54  ens[sys][bod].mass(),
55  ens[sys][bod][0].pos(),
56  ens[sys][bod][1].pos(),
57  ens[sys][bod][2].pos(),
58  ens[sys][bod][0].vel(),
59  ens[sys][bod][1].vel(),
60  ens[sys][bod][2].vel()
61  );
62 }
63 
64 int maxsysid = 0;
66 void generate_initial_conditions_for_system(const config& cfg, defaultEnsemble &ens, const int sysidx, const int sysid)
67 {
68  double time_init = cfg.optional("time_init", 0.0);
69  const bool use_jacobi = cfg.optional("use_jacobi", 0);
70  bool keplerian_ephemeris = cfg.optional("use_keplerian", 1);
71  bool transit_ephemeris = cfg.optional("use_transit", 0);
72  if(transit_ephemeris) keplerian_ephemeris = 0;
73  assert(!(keplerian_ephemeris && transit_ephemeris)); // find a better way
74 
75  ens[sysidx].id() = sysid;
76  ens[sysidx].time() = time_init;
77  ens[sysidx].set_active();
78  if(sysid>maxsysid) maxsysid = sysid;
79 
80  // set sun to unit mass and at origin
81  double mass_star = draw_value_from_config(cfg,"mass_star",0.00003,100.);
82  double x=0, y=0, z=0, vx=0, vy=0, vz=0;
83  ens.set_body(sysidx, 0, mass_star, x, y, z, vx, vy, vz);
84 
85  // If omitted or value of zero, then default to cycling through body id to make eccentric
86  int ecc_body = cfg.optional("ecc_body", 0);
87  if(ecc_body==0)
88  { ecc_body = 1+(sysid*(ens.nbod()-1))/ens.nsys(); }
89  double mass_enclosed = mass_star;
90  for(unsigned int bod=1;bod<ens.nbod();++bod)
91  {
92  double mass_planet = draw_value_from_config(cfg,"mass",bod,0.,mass_star);
93  mass_enclosed += mass_planet;
94 
95  double a, e, i, O, w, M;
96  if(transit_ephemeris)
97  {
98  double period = draw_value_from_config(cfg,"period",bod,0.2,365250.);
99  double epoch = draw_value_from_config(cfg,"epoch",bod,-365250.,365250.);
100  a = pow((period/365.25)*(period/365.25)*mass_enclosed,1.0/3.0);
101  if(bod==ecc_body)
102  e = draw_value_from_config(cfg,"ecc",bod,0.,1.);
103  else
104  e = 0.;
105  i = draw_value_from_config(cfg,"inc",bod,-180.,180.);
106  O = draw_value_from_config(cfg,"node",bod,-720.,720.);
107  w = draw_value_from_config(cfg,"omega",bod,-720.,720.);
108 
109  i *= M_PI/180.;
110  O *= M_PI/180.;
111  w *= M_PI/180.;
112 
113  double T = (0.5*M_PI-w)+2.0*M_PI*((epoch/period)-floor(epoch/period));
114  double E = atan2(sqrt(1.-e)*(1.+e)*sin(T),e+cos(T));
115  M = E-e*sin(E);
116  }
117  else if (keplerian_ephemeris)
118  {
119  a = draw_value_from_config(cfg,"a",bod,0.001,10000.);
120  if(bod==ecc_body)
121  e = draw_value_from_config(cfg,"ecc",bod,0.,1.);
122  else
123  e = 0.;
124  i = draw_value_from_config(cfg,"inc",bod,-180.,180.);
125  O = draw_value_from_config(cfg,"node",bod,-720.,720.);
126  w = draw_value_from_config(cfg,"omega",bod,-720.,720.);
127  M = draw_value_from_config(cfg,"meananom",bod,-720.,720.);
128 
129  i *= M_PI/180.;
130  O *= M_PI/180.;
131  w *= M_PI/180.;
132  M *= M_PI/180.;
133  }
134 
135  double mass = use_jacobi ? mass_enclosed : mass_star+mass_planet;
136  if(cfg.count("verbose")&&(sysid<10))
137  std::cout << "# Drawing sysid= " << sysid << " bod= " << bod << ' ' << mass_planet << " " << a << ' ' << e << ' ' << i*180./M_PI << ' ' << O*180./M_PI << ' ' << w*180./M_PI << ' ' << M*180./M_PI << '\n';
138 
139  calc_cartesian_for_ellipse(x,y,z,vx,vy,vz, a, e, i, O, w, M, mass);
140 
141  // printf("%d %d: %lg (%lg %lg %lg) (%lg %lg %lg) \n", sysidx, bod, mass_planet, x,y,z,vx,vy,vz);
142 
143  if(use_jacobi)
144  {
145  double bx, by, bz, bvx, bvy, bvz;
146  ens.get_barycenter(sysidx,bx,by,bz,bvx,bvy,bvz,bod-1);
147  x += bx; y += by; z += bz;
148  vx += bvx; vy += bvy; vz += bvz;
149  }
150 
151  // assign body a mass, position and velocity
152  ens.set_body(sysidx, bod, mass_planet, x, y, z, vx, vy, vz);
153 
154  if(cfg.count("verbose")&&(sysid<10))
155  {
156  double x_t = ens.x(sysidx,bod);
157  double y_t = ens.y(sysidx,bod);
158  double z_t = ens.z(sysidx,bod);
159  double vx_t = ens.vx(sysidx,bod);
160  double vy_t = ens.vy(sysidx,bod);
161  double vz_t = ens.vz(sysidx,bod);
162 
163  std::cout << " x= " << x << "=" << x_t << " ";
164  std::cout << " y= " << y << "=" << y_t << " ";
165  std::cout << " z= " << z << "=" << z_t << " ";
166  std::cout << "vx= " << vx << "=" << vx_t << " ";
167  std::cout << "vy= " << vy << "=" << vy_t << " ";
168  std::cout << "vz= " << vz << "=" << vz_t << "\n";
169  }
170 
171  } // end loop over bodies
172 
173  // Shift into barycentric frame
174  ens.get_barycenter(sysidx,x,y,z,vx,vy,vz);
175  for(unsigned int bod=0;bod<ens.nbod();++bod)
176  {
177  ens.set_body(sysidx, bod, ens.mass(sysidx,bod),
178  ens.x(sysidx,bod)-x, ens.y(sysidx,bod)-y, ens.z(sysidx,bod)-z,
179  ens.vx(sysidx,bod)-vx, ens.vy(sysidx,bod)-vy, ens.vz(sysidx,bod)-vz);
180  } // end loop over bodies
181 }
182 
198 {
199  defaultEnsemble ens = defaultEnsemble::create( cfg.require("nbod",0), cfg.require("nsys",0) );
200  std::cerr << "# nsystems = " << ens.nsys() << " nbodies/system = " << ens.nbod() << "\n";
201 
202  // std::cerr << "# Set initial time for all systems = ";
203  double time_init = cfg.optional("time_init", 0.0);
204  // std::cerr << time_init << ".\n";
205 
206  const bool use_jacobi = cfg.optional("use_jacobi", 0);
207  bool keplerian_ephemeris = cfg.optional("use_keplerian", 1);
208  bool transit_ephemeris = cfg.optional("use_transit", 0);
209  if(transit_ephemeris) keplerian_ephemeris = 0;
210  assert(!(keplerian_ephemeris && transit_ephemeris)); // find a better way
211 
212  for(unsigned int sys=0;sys<ens.nsys();++sys)
213  {
214  generate_initial_conditions_for_system(cfg,ens,sys,sys);
215  } // end loop over systems
216  return ens;
217 }
218 
220 void print_system(const swarm::ensemble& ens, const int systemid, std::ostream &os = std::cout)
221 {
222  enum {
223  JACOBI, BARYCENTRIC, ASTROCENTRIC
224  } COORDINATE_SYSTEM = BARYCENTRIC;
225  const bool use_jacobi = cfg.optional("use_jacobi", 0);
226  if(use_jacobi) COORDINATE_SYSTEM = JACOBI;
227 
228 
229 
230  std::streamsize cout_precision_old = os.precision();
231  os.precision(10);
232  os << "sys_idx= " << systemid << " sys_id= " << ens[systemid].id() << " time= " << ens.time(systemid) << "\n";
233  double star_mass = ens.mass(systemid,0);
234  double mass_effective = star_mass;
235  double bx, by, bz, bvx, bvy, bvz;
236  switch(COORDINATE_SYSTEM)
237  {
238  case JACOBI:
239  ens.get_barycenter(systemid,bx,by,bz,bvx,bvy,bvz,0);
240  break;
241 
242  case BARYCENTRIC:
243  ens.get_barycenter(systemid,bx,by,bz,bvx,bvy,bvz);
244  break;
245 
246  case ASTROCENTRIC:
247  ens.get_body(systemid,0,star_mass,bx,by,bz,bvx,bvy,bvz);
248  break;
249  }
250 
251  for(unsigned int bod=1;bod<ens.nbod();++bod) // Skip star since printing orbits
252  {
253  // std::cout << "pos= (" << ens.x(systemid, bod) << ", " << ens.y(systemid, bod) << ", " << ens.z(systemid, bod) << ") vel= (" << ens.vx(systemid, bod) << ", " << ens.vy(systemid, bod) << ", " << ens.vz(systemid, bod) << ").\n";
254  double mass = ens.mass(systemid,bod);
255 
256  switch(COORDINATE_SYSTEM)
257  {
258  case JACOBI:
259  ens.get_barycenter(systemid,bx,by,bz,bvx,bvy,bvz,bod-1);
260  mass_effective += mass;
261  break;
262 
263  case BARYCENTRIC:
264  mass_effective = star_mass + mass;
265  break;
266 
267  case ASTROCENTRIC:
268  mass_effective = star_mass + mass;
269  break;
270  }
271  double x = ens.x(systemid,bod)-bx;
272  double y = ens.y(systemid,bod)-by;
273  double z = ens.z(systemid,bod)-bz;
274  double vx = ens.vx(systemid,bod)-bvx;
275  double vy = ens.vy(systemid,bod)-bvy;
276  double vz = ens.vz(systemid,bod)-bvz;
277 
278  double a, e, i, O, w, M;
279  calc_keplerian_for_cartesian(a,e,i,O,w,M, x,y,z,vx,vy,vz, mass_effective);
280  i *= 180/M_PI;
281  O *= 180/M_PI;
282  w *= 180/M_PI;
283  M *= 180/M_PI;
284  // os << " b= " << bod << " m= " << mass << " a= " << a << " e= " << e << " i= " << i << " Omega= " << O << " omega= " << w << " M= " << M << "\n";
285  os << ens[systemid].id() << " " << bod << " " << mass << " " << a << " " << e << " " << i << " " << O << " " << w << " " << M << "\n";
286  }
287 
288  os.precision(cout_precision_old);
289  os << std::flush;
290 }
291 
293 void print_selected_systems(swarm::ensemble& ens, std::vector<unsigned int> systemindices, std::ostream &os = std::cout)
294 {
295  for(unsigned int i=0; i<systemindices.size(); ++i)
296  print_system(ens,systemindices[i], os);
297 }
299 void print_selected_systems_for_demo(swarm::ensemble& ens, unsigned int nprint, std::ostream &os = std::cout)
300 {
301  if(nprint>ens.nsys()) nprint = ens.nsys();
302  for(unsigned int systemid = 0; systemid< nprint; ++systemid)
303  print_system(ens,systemid,os);
304 }
305 
308 {
309  // find the stable ones and output the initial conditions for the stable
310  // ones in keplerian coordinates
311  // std::cerr << "# Finding stable system ids\n";
312  std::vector<unsigned int> stable_system_indices, unstable_system_indices;
313  for(int i = 0; i < ens.nsys() ; i++ )
314  {
315  if(ens[i].is_disabled())
316  unstable_system_indices.push_back(i);
317  else
318  stable_system_indices.push_back(i);
319  }
320 
321  // std::cerr << "# Writing stable system ids\n";
322  if(cfg.count("stable_init_output"))
323  {
324  ofstream stable_init_output( cfg.optional("stable_init_output", string("stable_init_output.txt")).c_str() );
325  print_selected_systems(ens_init,stable_system_indices, stable_init_output);
326  }
327 
328  if(cfg.count("stable_final_output"))
329  {
330  ofstream stable_final_output( cfg.optional("stable_final_output", string("stable_final_output.txt")).c_str() );
331  print_selected_systems(ens,stable_system_indices, stable_final_output);
332  }
333 
334  if(cfg.count("unstable_init_output"))
335  {
336  ofstream unstable_init_output( cfg.optional("unstable_init_output", string("unstable_init_output.txt")).c_str() );
337  print_selected_systems(ens_init,unstable_system_indices, unstable_init_output);
338  }
339 
340  if(cfg.count("unstable_final_output"))
341  {
342  ofstream unstable_final_output( cfg.optional("unstable_final_output", string("unstable_final_output.txt")).c_str() );
343  print_selected_systems(ens,unstable_system_indices, unstable_final_output);
344  }
345 
346 }
347 
348 std::vector<std::vector<double> > calc_semimajor_axes(defaultEnsemble& ens)
349 {
350  std::vector<std::vector<double> > semimajor_axes(ens.nsys(),std::vector<double>(ens.nbod(),0.));
351 
352  for(int sys_idx = 0; sys_idx < ens.nsys() ; sys_idx++)
353  {
354  int sys_id = ens[sys_idx].id();
355  assert(sys_id>=0);
356  assert(sys_id<ens.nsys());
357  double star_mass = ens.mass(sys_idx,0);
358  double mass_effective = star_mass;
359  double bx, by, bz, bvx, bvy, bvz;
360  ens.get_barycenter(sys_idx,bx,by,bz,bvx,bvy,bvz,0);
361  for(unsigned int bod=1;bod<ens.nbod();++bod) // Skip star since calculating orbits
362  {
363  double mass = ens.mass(sys_idx,bod);
364  mass_effective += mass;
365  ens.get_barycenter(sys_idx,bx,by,bz,bvx,bvy,bvz,bod-1);
366  double x = ens.x(sys_idx,bod)-bx;
367  double y = ens.y(sys_idx,bod)-by;
368  double z = ens.z(sys_idx,bod)-bz;
369  double vx = ens.vx(sys_idx,bod)-bvx;
370  double vy = ens.vy(sys_idx,bod)-bvy;
371  double vz = ens.vz(sys_idx,bod)-bvz;
372  double a, e, i, O, w, M;
373  calc_keplerian_for_cartesian(a,e,i,O,w,M, x,y,z,vx,vy,vz, mass_effective);
374  semimajor_axes[sys_id][bod-1] = a;
375  }
376  }
377  return semimajor_axes;
378 }
380 void disable_unstable_systems(defaultEnsemble& ens, const std::vector<std::vector<double> >& semimajor_axes_init, const double deltaa_threshold )
381 {
382  for(int sys_idx = 0; sys_idx < ens.nsys() ; sys_idx++)
383  {
384  if(ens[sys_idx].is_disabled() ) continue;
385  bool disable = false;
386  int sys_id = ens[sys_idx].id();
387  double star_mass = ens.mass(sys_idx,0);
388  double mass_effective = star_mass;
389  double bx, by, bz, bvx, bvy, bvz;
390  ens.get_barycenter(sys_idx,bx,by,bz,bvx,bvy,bvz,0);
391  for(unsigned int bod=1;bod<ens.nbod();++bod) // Skip star since calculating orbits
392  {
393  // std::cout << "body= " << bod << ": ";
394  // std::cout << "pos= (" << ens.x(sys_idx, bod) << ", " << ens.y(sys_idx, bod) << ", " << ens.z(sys_idx, bod) << ") vel= (" << ens.vx(sys_idx, bod) << ", " << ens.vy(sys_idx, bod) << ", " << ens.vz(sys_idx, bod) << ").\n";
395  double mass = ens.mass(sys_idx,bod);
396  mass_effective += mass;
397  ens.get_barycenter(sys_idx,bx,by,bz,bvx,bvy,bvz,bod-1);
398  double x = ens.x(sys_idx,bod)-bx;
399  double y = ens.y(sys_idx,bod)-by;
400  double z = ens.z(sys_idx,bod)-bz;
401  double vx = ens.vx(sys_idx,bod)-bvx;
402  double vy = ens.vy(sys_idx,bod)-bvy;
403  double vz = ens.vz(sys_idx,bod)-bvz;
404  double a, e, i, O, w, M;
405  calc_keplerian_for_cartesian(a,e,i,O,w,M, x,y,z,vx,vy,vz, mass_effective);
406  i *= 180/M_PI;
407  O *= 180/M_PI;
408  w *= 180/M_PI;
409  M *= 180/M_PI;
410 
411  if(!((e>=0.)&&(e<1.0))) { disable = true; }
412  if(!((a>0.)&&(a<10.0))) { disable = true; }
413  assert(sys_id>=0);
414  assert(sys_id<semimajor_axes_init.size());
415  assert(bod-1>=0);
416  assert(bod-1<semimajor_axes_init[sys_id].size());
417  double da = a - semimajor_axes_init[sys_id][bod-1];
418  if(fabs(da) >= deltaa_threshold * semimajor_axes_init[sys_id][bod-1])
419  { disable = true; }
420 
421  if(disable)
422  {
423  if(cfg.count("verbose"))
424  std::cout << "# Disabling idx=" << sys_idx << " id=" << sys_id << " b=" << bod << " a= " << a << " e= " << e << " i= " << i << " Omega= " << O << " omega= " << w << " M= " << M << "\n";
425  break;
426  }
427  }
428  if(disable) ens[sys_idx].set_disabled();
429  }
430 }
431 
432 bool needs_shrinking( const defaultEnsemble& ens )
433 {
434  // This is the ratio we use when we are shrinking
435  // if the ratio of active ones to all is less
436  // than this number then we trim the fat
437  const double critical_ratio = 0.75;
438 
439  int count_disabled = number_of_disabled_systems( ens ) ;
440  double ratio = double(count_disabled) / double( ens.nsys() );
441 
442  return ratio > critical_ratio;
443 }
444 
445 
450 {
451  if(cfg.count("snapshot")) snapshot::save( ens, cfg["snapshot"] );
452 }
453 
454 
463 {
464  int nsys = ens.nsys();
465  int active_nsys = nsys - number_of_disabled_systems( ens ) ;
466  // WARNING: Needed to add this to prevent segfaults. TODO: Figure out why.
467  if(active_nsys==0) return ens;
468  int nbod = ens.nbod();
469 
470  defaultEnsemble active_ens = defaultEnsemble::create( nbod, active_nsys );
471 
472  // Copy the active ones to the new ensemble
473  for(int i = 0, j = 0; (i < nsys) && (j < active_nsys); i++)
474  {
475  if( !ens[i].is_disabled() )
476  {
477  ens[i].copyTo( active_ens[j] );
478  j++;
479  }
480  }
481 
482  return active_ens;
483 }
484 
485 
490 {
491  int nsys = ens.nsys();
492  // Replace the systems that have been disabled
493  for(int i = 0; i < nsys; i++)
494  {
495  if( ens[i].is_disabled() )
496  {
497  generate_initial_conditions_for_system(cfg,ens,i,maxsysid+1);
498  }
499  }
500 }
501 
502 
503 void reactivate_systems(defaultEnsemble&ens)
504 {
505  for(int i = 0; i < ens.nsys() ; i++)
506  {
507  if(ens[i].is_inactive())
508  ens.set_active(i);
509  }
510 }
511 
512 volatile bool integration_loop_not_aborted_yet = true;
519 void ctrl_c_trap(int)
520 {
521  fprintf(stderr, "# Break requested... Finishing the current GPU kernel call and will then save the results before exitting.\n");
522  integration_loop_not_aborted_yet = false;
523 }
524 void catch_ctrl_c()
525 {
526  signal(SIGINT, &ctrl_c_trap );
527 }
528 
529 int main(int argc, char* argv[] )
530 {
531  // We keep it simple, later on one can use boost::program_options to
532  // have more options
533  // but now we only use two configuration files. It is because the
534  // initial conditions configuration file can get really big and
535  // it has very little to do with other configuration options.
536  if(argc < 3) cout << "Usage: montecarlo_ecclimit <integration configuration> <initial conditions configuration>" << endl;
537 
538  // First one is the configuration for integration
539  string integ_configfile = argv[1];
540  // the second one is the configuration for generating
541  // initial conditions it is used by \ref generate_ensemble_with_randomized_initial_conditions
542  string initc_configfile = argv[2];
543 
544  cfg = config::load(integ_configfile);
545 
546  int pid = getpid();
547  int seed_default = (int) time(NULL);
548  seed_default = seed_default ^ (pid + (pid << 15) );
549  int seed = cfg.optional("seed", seed_default);
550  srand(seed);
551 
552  // 1.read keplerian coordinates from a file
553  // 2.generate guesses based on the keplerian coordinates
554  // 3.convert keplerian coordinates to an ensemble
555  // The following line that is taken from swarm_tutorial_montecarlo.cpp
556  // does the first three steps. Its pretty amazing.
557  defaultEnsemble ens ;
558  if( cfg.count("input") )
559  { ens = snapshot::load(cfg["input"]); }
560  else
561  {
563  }
564 
565  // save the ensemble as a snapshot
566  if(cfg.count("initial_snapshot"))
567  { snapshot::save( ens, cfg["initial_snapshot"] ); }
568 
569  defaultEnsemble ens_init = ens.clone() ;
570  std::vector<std::vector<double> > semimajor_axes_init = calc_semimajor_axes(ens);
571 
572  // We want to find the ones that are really stable, so we integrate for
573  // a really long time and over time we get rid of unstable ones.
574  double destination_time = cfg.optional("destination_time", 1.0E6);
575 
576 
577  swarm::init(cfg);
578  Pintegrator integ = integrator::create(cfg);
579  integ->set_ensemble(ens);
580  integ->set_destination_time ( destination_time );
581  // We can set the following two items if we really need
582  // longer integrations before we stop for checking the
583  // ensemble and saving snapshots.
584  // one kernel call to allow for prompt CPU pruning of unstable systems
585  int max_kernel_calls_per_integrate = cfg.optional("max_kernel_calls_per_integrate",1);
586  // 10^2 inner orbits at 200 time steps per inner orbit
587  int max_itterations_per_kernel_call = cfg.optional("max_itterations_per_kernel_call",20000);
588  integ->set_max_attempts( max_kernel_calls_per_integrate );
589  integ->set_max_iterations ( max_itterations_per_kernel_call );
590  SYNC;
591 
592  // integrate ensemble
593  // - drop the unstable ones as you go
594  // - redistribute the systems for better efficiency
595  // - save the results periodically
596  // This can be an infitie loop. but we can always get out of this
597  // the best way to do it is to use Ctrl-C. Further wecan
598  // define Ctrl-C signal handler to get out
599  // of this loop in a safe way. But we really want this loop
600  // to run for a long time
601  reactivate_systems(ens);
602  // EBF Experiment trying to expose host log.
604  integ->flush_log();
605 
606  catch_ctrl_c();
607  while( number_of_active_systems(ens) > 0 && integration_loop_not_aborted_yet ) {
608 
609  // 1. Integrate, we could use core_integrate but the general integrate
610  // saves all the headache. We should only use core_integrate if we are
611  // going to do everything on GPU, but since we are saving a snapshot in
612  // the middle, there's no point. It also has a nice for loop and can
613  // to several kernel calls.
614  integ->integrate();
615 
616  // 2. CPU-based tests to identify systems that can be terminated
617  int active_ones = number_of_active_systems(ens);
618  const double deltaa_frac_threshold = cfg.optional("deltaa_frac_threshold", 0.5);
619  disable_unstable_systems( ens, semimajor_axes_init, deltaa_frac_threshold );
620  // double med_deltaE = find_median_energy_conservation_error(ens, ens_init );
621  double max_deltaE = find_max_energy_conservation_error(ens, ens_init );
622  std::cerr << "# Time: " << ens.time_ranges().min << " " << ens.time_ranges().median << " " << ens.time_ranges().max << " Systems: " << ens.nsys() << ", " << active_ones << ", ";
623  active_ones = number_of_active_systems(ens);
624  std::cerr << active_ones << " max|dE/E|= " << max_deltaE << "\n";
625 
626  // EBF Experiment trying to expose host log.
628  integ->flush_log();
629 
630  if (!cfg.count("input") && cfg.count("replace_unstable") )
631  {
632  int nsys_to_replace = number_of_disabled_systems( ens ) ;
633  if( nsys_to_replace >0 )
634  {
636  integ->set_ensemble( ens );
637  }
638  }
639  else
640  {
641  // 3. Now we need to get rid of the inactive ones. There
642  // should be some criteria, whatever it is we are
643  // going to put it in a function and call it \ref needs_shrinking
644  if ( needs_shrinking( ens ) )
645  {
646  // Now this function will take care of trimming for us
647  // We need to create a new ensemble of a smaller size
648  // thats why we need to call set_ensemble again. because
649  // the GPU ensemble should get recreated for us.
650  // we need better memory management to safely allow
651  // the following statement. Right now we don't have a
652  // very good automatic memory management
653  // std::cerr << "# Removing disabled systems\n";
654  ens = trim_disabled_systems( ens );
655  // std::cerr << "# Testing if ens has any systems left.\n";
656  if(ens.nsys()>0)
657  {
658  // std::cerr << "# Setting ensemble for integrator\n";
659  // WARNING: This appears to be the cause of SEGFAULT
660  // if the ensemble is empty.
661  integ->set_ensemble( ens );
662  }
663  // std::cerr << "# Time: " << ens.time_ranges() << " Total Systems: " << ens.nsys() << " Active Systems: " << active_ones << ", ";
664  // active_ones = number_of_active_systems(ens);
665  // std::cerr << active_ones << "\n";
666  }
667  }
668  // std::cerr << std::endl;
669 
670  // 4. We need to save a snapshot in case system crashes or someone
671  // gets tired of it and hits Ctrl-C
672  // std::cerr << "# Saving snapshot\n";
673  save_snapshot( ens );
674  write_stable_systems(ens,ens_init);
675  }
676 
677  // Now we are at the end of the system, before we examine
678  // the output we need to
679  // std::cerr << "# Removing disabled systems\n";
680  if(number_of_active_systems(ens)>0)
681  {
682  ens = trim_disabled_systems( ens );
683  // std::cerr << "# Saving snapshot\n";
684  save_snapshot( ens );
685  }
686 
687  write_stable_systems(ens,ens_init);
688 }
689