Main Page   Class Hierarchy   Data Structures   File List   Data Fields   Globals  

hier3.C

Go to the documentation of this file.
00001 
00002        //=======================================================//    _\|/_
00003       //  __  _____           ___                    ___       //      /|\
00004      //  /      |      ^     |   \  |         ^     |   \     //          _\|/_
00005     //   \__    |     / \    |___/  |        / \    |___/    //            /|\
00006    //       \   |    /___\   |  \   |       /___\   |   \   // _\|/_
00007   //     ___/   |   /     \  |   \  |____  /     \  |___/  //   /|\     
00008  //                                                       //            _\|/_
00009 //=======================================================//              /|\
00010 
00057 
00058 // Note the function hier3 is completely deterministic.
00059 // No randomization is performed at that level.
00060 
00061 #include "scatter3.h"
00062 
00063 #ifndef TOOLBOX
00064 
00065 // No library functions.
00066 
00067 #else
00068 
00069 // make_hier_init:  Set up a template initial state.
00070 
00071 #define DEFAULT_A_OUT   10
00072 #define DEFAULT_R_STOP 100
00073 
00074 local void make_hier_init(initial_state3 & init)
00075 {
00076     init.m2 = 0.5;              // mass of secondary in target binary
00077     init.m3 = 0.5;              // mass of projectile (m1 + m2 = 1)
00078     init.r1 = 0;                // radius of star #1
00079     init.r2 = 0;                // radius of star #2
00080     init.r3 = 0;                // radius of star #3
00081 
00082     init.sma = 1;               // inner binary semi-major axis
00083     init.ecc = 0;               // inner binary eccentricity
00084 
00085     // Not used (for unbound systems):
00086 
00087     init.v_inf = 0;
00088     init.rho = 0;
00089 
00090     init.a_out = DEFAULT_A_OUT; // outer binary semi-major axis
00091     init.e_out = 0;             // outer binary eccentricity
00092     init.r_stop = DEFAULT_R_STOP;
00093                                 // final separation
00094     init.tidal_tol_factor = DEFAULT_TIDAL_TOL_FACTOR;
00095     init.eta
00096           = DEFAULT_ETA;        // accuracy parameter
00097 
00098     initialize_bodies(init.system);
00099     init.id = get_initial_seed() + get_n_rand();
00100 }
00101 
00102 // init_to_sdyn3:  Convert an initial state to an sdyn3 for integration.
00103 
00104 local sdyn3 * init_to_sdyn3(initial_state3 & init)
00105 {
00106     set_kepler_tolerance(2);
00107 
00108     kepler k1;          // inner binary
00109 
00110     real mean_anomaly = 0;
00111     if (init.ecc == 1)
00112         mean_anomaly = -M_PI;
00113     else
00114         mean_anomaly = randinter(-M_PI, M_PI);
00115 
00116     make_standard_kepler(k1, 0, 1, -0.5 / init.sma, init.ecc,
00117                          1,     // value unimportant unless init.ecc = 1
00118                          mean_anomaly,
00119                          1);    // 1 ==> align with x, y axes
00120 
00121 #if 0
00122     cerr << endl << "Inner binary:" << endl;
00123     k1.print_all(cerr);
00124 #endif
00125 
00126     if (init.e_out == 1)
00127         err_exit("Linear outer binary not allowed!");
00128 
00129     kepler k3;          // outer binary.
00130 
00131     real m2 = init.m2;
00132     real m3 = init.m3;
00133     real mtotal = 1 + m3;
00134 
00135     make_standard_kepler(k3, 0, mtotal, -0.5 * mtotal / init.a_out, init.e_out,
00136                          1,     // value unimportant unless init.e_out = 1
00137                          M_PI); // start at apastron
00138     set_orientation(k3, init.phase);
00139 
00140 #if 0
00141     cerr << endl << "Outer binary:" << endl << flush;
00142     k3.print_all(cerr);
00143 #endif
00144 
00145     sdyn3 * b;
00146     b = set_up_dynamics(m2, m3, k1, k3);
00147 
00148     // Set up radii:
00149 
00150     sdyn3 * bb = b->get_oldest_daughter();
00151     bb->set_radius(init.r1);
00152     bb = bb->get_younger_sister();
00153     bb->set_radius(init.r2);
00154     bb = bb->get_younger_sister();
00155     bb->set_radius(init.r3);
00156 
00157     return  b;
00158 }
00159 
00160 // check_init:  Make sure an initial state is sensible.
00161 
00162 local void check_init(initial_state3 & init)
00163 {
00164     if (init.m2 > 1) err_exit("check_init: m1 < 0");
00165     if (init.m2 < 0) err_exit("check_init: m2 < 0");
00166     if (init.m3 < 0) err_exit("check_init: m3 < 0");
00167     if (init.sma <= 0) err_exit("check_init: inner semi-major axis <= 0");
00168     if (init.ecc < 0) err_exit("check_init: inner eccentricity < 0");
00169     if (init.ecc > 1) err_exit("check_init: inner eccentricity > 1");
00170     if (init.a_out <= 0) err_exit("check_init: outer semi-major axis <= 0");
00171     if (init.e_out < 0) err_exit("check_init: outer eccentricity < 0");
00172     if (init.e_out > 1) err_exit("check_init: outer eccentricity > 1");
00173     if (init.r1 < 0) err_exit("check_init: r1 < 0");
00174     if (init.r2 < 0) err_exit("check_init: r2 < 0");
00175     if (init.r3 < 0) err_exit("check_init: r3 < 0");
00176     if (init.eta <= 0) err_exit("check_init: eta <= 0");
00177 }
00178 
00179 local void print_elements(sdyn3* b)
00180 {
00181     // Compute and print out the essential orbital elements of the
00182     // assumed ((b1,b2),b3) hierarchical system.  Very inefficient!
00183 
00184     sdyn3* b1 = b->get_oldest_daughter();
00185     sdyn3* b2 = b1->get_younger_sister();
00186     sdyn3* b3 = b2->get_younger_sister();
00187 
00188     // Create a center of mass node.
00189 
00190     sdyn3 cm;
00191     cm.set_mass(b1->get_mass()+b2->get_mass());
00192     cm.set_pos((b1->get_mass()*b1->get_pos()+b2->get_mass()*b2->get_pos())
00193                 / cm.get_mass());
00194     cm.set_vel((b1->get_mass()*b1->get_vel()+b2->get_mass()*b2->get_vel())
00195                 / cm.get_mass());
00196 
00197     // Construct keplers describing the inner and outer orbits.
00198 
00199     kepler k1, k3;
00200     initialize_kepler_from_dyn_pair(k1, b1, b2, true);
00201     initialize_kepler_from_dyn_pair(k3, b3, &cm, true);
00202 
00203     // Print out vital information.
00204 
00205     cerr << b->get_system_time()      << "  "
00206          << k1.get_semi_major_axis()  << "  "
00207          << k3.get_semi_major_axis()  << "  "
00208          << k1.get_eccentricity()     << "  "
00209          << k3.get_eccentricity()     << "  "
00210          << 0.1 * k3.get_periastron() << "  "
00211          << 0.1 * k3.get_periastron() / k1.get_apastron() << "  "
00212          << endl;
00213 }
00214 
00215 #define PERT_TOL 0.5
00216 
00217 local real period_ratio(sdyn3* a, sdyn3* b, sdyn3* c, bool debug = false)
00218 {
00219     // Return the ratio of the squares of the periods of the inner
00220     // and outer orbits, if both are bound.  Return 0 otherwise.
00221 
00222     if (debug) {
00223         cerr << "period_ratio:  a = " << a->format_label();
00224         cerr << "  b = " << b->format_label();
00225         cerr << "  c = " << c->format_label() << endl;
00226     }
00227 
00228     // First determine the relative energy of the a-b motion.
00229 
00230     real ma = a->get_mass();
00231     real mb = b->get_mass();
00232     real mc = c->get_mass();
00233 
00234     real mab = ma + mb;
00235     vector rab = b->get_pos() - a->get_pos();
00236     vector vab = b->get_vel() - a->get_vel();
00237 
00238     real dab = abs(rab);
00239     real eab = 0.5*square(vab) - mab/dab;
00240     if (debug) {
00241         PRI(4); PRC(dab); PRC(eab);
00242     }
00243 
00244     if (eab >= 0) {
00245         if (debug) cerr << endl;
00246         return 0;
00247     }
00248 
00249     // Now look at the "outer" pair ((a,b),c).
00250 
00251     // Determine the position and velocity of the CM of a and b.
00252 
00253     vector cmpos = (ma*a->get_pos() + mb*b->get_pos()) / mab;
00254     vector cmvel = (ma*a->get_vel() + mb*b->get_vel()) / mab;
00255 
00256     real mabc = mab + mc;
00257     vector rc = c->get_pos() - cmpos;
00258     vector vc = c->get_vel() - cmvel;
00259 
00260     // EXTRA CONDITION: require that c not be a dominant perturber of (a,b).
00261 
00262     real dc = abs(rc);
00263 
00264     // Multiplies here because of annoying pow bug in RH Linux...
00265 
00266     if (mc*dab*dab*dab > PERT_TOL*mab*dc*dc*dc) return 0;
00267 
00268     real ec = 0.5*square(vc) - mabc/dc;
00269     if (debug) {
00270         PRC(abs(rc)), PRL(ec);
00271     }
00272 
00273     if (ec >= 0) return 0;
00274 
00275     // Now we know that both the inner and the outer pairs are bound.
00276     // Compute the squared period ratio.
00277 
00278     real ee = eab/ec;   // defeat annoying pow bug...
00279 
00280     return (mabc/mab) * ee * ee;
00281 }
00282 
00283 local int outer_component(sdyn3* b, bool debug = false)
00284 {
00285     // Determine the index of the outer component of the "most hierarchical"
00286     // system, according to the criterion of Eggleton & Kiseleva (1995).
00287 
00288     // Hmmm...  Seems that this can fail quite easily, giving a large period
00289     // to a spurious "outer" system and hence identifying the wrong star as
00290     // the outermost member (Steve 5/99).
00291 
00292     // E&K criterion is simply to maximize the ratio of outer to inner
00293     // periods.  Probably better to incorporate the perturbation due to
00294     // the third member as part of the criterion (Steve 5/99).
00295 
00296     sdyn3* b1 = b->get_oldest_daughter();
00297     sdyn3* b2 = b1->get_younger_sister();
00298     sdyn3* b3 = b2->get_younger_sister();
00299 
00300     // Test all possible combinations of inner and outer binaries.
00301 
00302     real ratio1 = period_ratio(b2, b3, b1, debug);
00303     real ratio2 = period_ratio(b3, b1, b2, debug);
00304     real ratio3 = period_ratio(b1, b2, b3, debug);
00305 
00306     if (debug) {
00307         cerr << "outer_component:  ";
00308         PRC(ratio1), PRC(ratio2), PRL(ratio3);
00309     }
00310 
00311     real  ratio_max = max(max(ratio1, ratio2), ratio3);
00312 
00313     if (ratio_max <= 1)
00314         return 0;
00315     else if (ratio_max == ratio1)
00316         return 1;
00317     else if (ratio_max == ratio2)
00318         return 2;
00319     else
00320         return 3;
00321 }
00322 
00323 // hier3:  Perform integration of a hirarchical three-body system,
00324 //         initializing from a specified state.
00325 
00326 local void hier3(initial_state3 & init,
00327                  real cpu_time_check,
00328                  real dt_out,         // diagnostic output interval
00329                  real dt_snap,        // snapshot output interval
00330                  real snap_cube_size, // limit output to particles within cube
00331                  real dt_print,       // print output interval
00332                  real t_end,
00333                  bool quiet = false)
00334 {
00335     check_init(init);
00336     sdyn3 * b = init_to_sdyn3(init);
00337 
00338     if (!b) err_exit("Unable to initialize system...");
00339 
00340     // Save some initial data:
00341 
00342     sdyn3_to_system(b, init.system);
00343 
00344     real e_init = energy(b);
00345     real cpu_init = cpu_time();
00346     real cpu = cpu_init;
00347     
00348     // Evolve the system until the interaction is over or a collision occurs.
00349 
00350     int n_kepler = 0;
00351 
00352     do {
00353 
00354         int n_stars = 0;
00355         for_all_daughters(sdyn3, b, bb) n_stars++;
00356 
00357         tree3_evolve(b, CHECK_INTERVAL, dt_out, dt_snap, snap_cube_size,
00358                      init.eta, cpu_time_check,
00359                      dt_print, print_elements);
00360 
00361         // Stop immediately if hierarchy has changed.
00362 
00363         if (outer_component(b) != 3) break;
00364 
00365         // Check the CPU time.  Note that the printed CPU time is the
00366         // time since this routine was entered.
00367 
00368         if (cpu_time() - cpu > cpu_time_check) {
00369 
00370             if (!quiet) {
00371 
00372                 if (cpu == cpu_init)
00373                     cerr << endl; // (May be waiting in mid-line!)
00374 
00375                 int p = cerr.precision(STD_PRECISION);
00376                 cerr << "hier3:  CPU time = " << cpu_time() - cpu_init
00377                      << "  time = " << b->get_system_time()
00378                      << "  n_steps = " << b->get_n_steps()
00379                      << endl;
00380                 cerr << "           n_osc = " << b->get_n_ssd_osc()
00381                      << "  n_kepler = " << n_kepler
00382                      << endl << flush;
00383                 cerr.precision(p);
00384             }
00385             cpu = cpu_time();
00386         }
00387 
00388         merge_collisions(b);
00389 
00390         // See if the number of stars has decreased.
00391 
00392         for_all_daughters(sdyn3, b, bbb) n_stars--;
00393         if (n_stars > 0) {
00394             if (dt_snap < VERY_LARGE_NUMBER
00395                 && system_in_cube(b, snap_cube_size)) {
00396                 put_node(cout, *b);
00397                 cout << flush;
00398             }
00399         }
00400 
00401     } while (b->get_system_time() < t_end &&
00402              !extend_or_end_scatter(b, init));  // this is the same termination
00403                                                 // condition as for scattering
00404                                                 // experiments -- sufficient,
00405                                                 // but probably unnecessarily
00406                                                 // stringent.
00407 
00408     // In all cases, the final system should be a properly configured
00409     // 1-, 2-, or 3-body system.  Print it for the last time, to reflect
00410     // final state, if the "-D" command-line option was specified.
00411 
00412     if (dt_snap < VERY_LARGE_NUMBER && system_in_cube(b, snap_cube_size)) {
00413         put_node(cout, *b);
00414         cout << flush;
00415     }
00416 
00417     int p = cerr.precision(STD_PRECISION);
00418     cerr << "t = " << b->get_system_time()
00419          << "  t/t_end = " << b->get_system_time()/t_end
00420          << "  outer = " << outer_component(b)
00421          << endl;
00422     cerr.precision(p);
00423 
00424     if (!quiet) outer_component(b, true);
00425 
00426     // Delete the 3-body sytem.
00427 
00428     sdyn3 * bi = b->get_oldest_daughter();
00429     while (bi) {
00430         sdyn3 * tmp = bi->get_younger_sister();
00431         delete bi;
00432         bi = tmp;
00433     }
00434     delete b;
00435 }
00436 
00437 main(int argc, char **argv)
00438 {
00439     initial_state3 init;
00440     make_hier_init(init);
00441 
00442     int  seed       = 0;        // seed for random number generator
00443     int n_rand      = 0;        // number of times to invoke the generator
00444                                 // before starting for real
00445     int  n_experiments = 1;     // default: only one run
00446     real dt_out     =           // output time interval
00447           VERY_LARGE_NUMBER;
00448     real dt_snap    =           // output time interval
00449           VERY_LARGE_NUMBER;
00450     real dt_print   =           // print time interval
00451           VERY_LARGE_NUMBER;
00452 
00453     real cpu_time_check = 3600;
00454     real snap_cube_size = 10;
00455 
00456     int planar_flag = 0;
00457     int psi_flag = 0;
00458     real psi = 0;
00459 
00460     real outer_peri = 0;
00461     bool peri_flag = false;
00462 
00463     bool quiet = false;
00464 
00465     bool X_flag = false, Y_flag = false;    // flags for using Eggleton
00466                                             // and Kiseleva parameters
00467 
00468     real X_KE = 10, Y_KE = 4;               // set above flags true to make
00469                                             // these the defaults
00470 
00471     real T_stab = 100;
00472 
00473     check_help();
00474 
00475     extern char *poptarg;
00476     int c;
00477     char* param_string
00478         = "a:A:c:C:d:D:e:E:g:m:M:n:N:o:pPq:QR:s:S:t:T:x:X:y:Y:z:";
00479 
00480     while ((c = pgetopt(argc, argv, param_string)) != -1)
00481         switch(c) {
00482 
00483             case 'a': init.a_out = atof(poptarg);
00484                       X_flag = false;
00485                       break;
00486             case 'A': init.eta = atof(poptarg);
00487                       break;
00488             case 'c': cpu_time_check = 3600*atof(poptarg);// (Specify in hours)
00489                       break;
00490             case 'C': snap_cube_size = atof(poptarg);
00491                       break;
00492             case 'd': dt_out = atof(poptarg);
00493                       if (dt_out < 0) dt_out = pow(2.0, dt_out);
00494                       break;
00495             case 'D': dt_snap = atof(poptarg);
00496                       if (dt_snap < 0) dt_snap = pow(2.0, dt_snap);
00497                       break;
00498             case 'e': init.ecc = atof(poptarg);
00499                       peri_flag = false;
00500                       break;
00501             case 'E': init.e_out = atof(poptarg);
00502                       peri_flag = false;
00503                       Y_flag = false;
00504                       break;
00505             case 'g': init.tidal_tol_factor = atof(poptarg);
00506                       break;
00507             case 'm': init.m2 = atof(poptarg);
00508                       break;
00509             case 'M': init.m3 = atof(poptarg);
00510                       break;
00511             case 'n': n_experiments = atoi(poptarg);
00512                       break;
00513             case 'N': n_rand = atoi(poptarg);
00514                       break;
00515             case 'o': psi = atof(poptarg);
00516                       psi_flag = 1;
00517                       break;
00518             case 'p': planar_flag = 1;
00519                       break;
00520             case 'P': planar_flag = -1;
00521                       break;
00522             case 'q': outer_peri = atof(poptarg);
00523                       peri_flag = true;
00524                       Y_flag = false;
00525                       break;
00526             case 'Q': quiet = !quiet;
00527                       break;
00528             case 'R': init.r_stop = atof(poptarg);
00529                       break;
00530             case 's': seed = atoi(poptarg);
00531                       break;
00532             case 'S': init.r_stop = atof(poptarg);
00533                       break;
00534             case 't': dt_print = atof(poptarg);
00535                       if (dt_print < 0) dt_print = pow(2.0, dt_print);
00536                       break;
00537             case 'T': T_stab = pow(10.0, atof(poptarg));
00538                       break;
00539             case 'x': init.r1 = atof(poptarg);
00540                       break;
00541             case 'X': X_KE =atof(poptarg);
00542                       X_flag = true;
00543                       break; 
00544             case 'y': init.r2 = atof(poptarg);
00545                       break;
00546             case 'Y': Y_KE =atof(poptarg);
00547                       peri_flag = false;
00548                       Y_flag = true;
00549                       break; 
00550             case 'z': init.r3 = atof(poptarg);
00551                       break;
00552             case '?': params_to_usage(cerr, argv[0], param_string);
00553                       get_help();
00554         }            
00555 
00556     if (init.m2 > 1) {
00557         cerr << "hier3: init.m2 = " << init.m2 << " > 1" << endl;
00558         exit(1);
00559     }
00560 
00561     if (X_flag) {
00562 
00563         // Convert period ratio X_KE to outer semi-major axis.
00564         // Inner semi-major axis = 1 by convention.
00565 
00566         if (X_KE <= 1) err_exit("X_KE <= 1");
00567 
00568         init.a_out = pow((1+init.m3)*X_KE*X_KE, 1.0/3.0);
00569     }
00570 
00571     if (Y_flag) {
00572 
00573         // Convert periastron ratio Y_KE to outer eccentricity.
00574         // Note that Y_KE is outer periastron / inner apastron.
00575 
00576         if (Y_KE <= 1) err_exit("Y_KE <= 1");
00577 
00578         init.e_out = 1 - (init.sma*(1+init.ecc) * Y_KE) / init.a_out;
00579     }
00580 
00581     if (peri_flag) {
00582 
00583         // Convert outer periastron to eccentricity.
00584 
00585         if (outer_peri <= 0) err_exit("outer_peri <= 0");
00586 
00587         init.e_out = 1 - outer_peri / init.a_out;;
00588     }
00589 
00590     cpu_init();
00591     int random_seed = srandinter(seed, n_rand);
00592 
00593     for (int i = 0; i < n_experiments; i++) {
00594 
00595         if (!quiet) {
00596             if (n_experiments > 1) cerr << i+1 << ": ";
00597 
00598             cerr << "Random seed = " << get_initial_seed()
00599                  << "  n_rand = " << get_n_rand() << endl << flush;
00600         }
00601 
00602         init.id = get_initial_seed() + get_n_rand();
00603 
00604         // Phase angles will be such that the inner binary is at
00605         // periastron, the outer binary at apastron.  Orbital
00606         // orientation will be random, by default.
00607 
00608         // In the planar case, it may be desireable to
00609         // control the orientation of the outer orbit.  The
00610         // angle between the inner and outer orbital axes in
00611         // the planar case is init.phase.psi.  Specify psi
00612         // in *degrees*, with psi = 0 meaning that the outer
00613         // periastron lies along the positive x-axis.
00614 
00615         randomize_angles(init.phase);
00616 
00617         if (planar_flag == 1) {
00618             init.phase.cos_theta = 1;   // Planar prograde
00619             if (psi_flag) init.phase.psi = psi * M_PI / 180.0;
00620         } else if (planar_flag == -1) {
00621             init.phase.cos_theta = -1;  // Planar retrograde
00622             if (psi_flag) init.phase.psi = psi * M_PI / 180.0;
00623         }
00624 
00625         hier3(init, cpu_time_check,
00626               dt_out, dt_snap, snap_cube_size, dt_print,
00627               2*M_PI*T_stab*sqrt(pow(init.a_out, 3)/(1+init.m3)),
00628               quiet);
00629     }
00630 }
00631 
00632 #endif

Generated at Sun Feb 24 09:57:04 2002 for STARLAB by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001