Main Page   Class Hierarchy   Data Structures   File List   Data Fields   Globals  

test3.C

Go to the documentation of this file.
00001 
00002 // scatter.C: Perform three-body scattering experiments.
00003 //
00004 // Input is an initial state, output is intermediate and final states.
00005 // No reference is made to scatter profiles in this file.
00006 
00007 // Note the function scatter3 is completely deterministic.
00008 // No randomization is performed at this level.
00009 
00010 // The program scatter3 randomizes all phase angles prior to invoking
00011 // the scatter3 function.  Other parameters are fully determined, either
00012 // by default or on the command line.
00013 
00014 #include "scatter3.h"
00015 
00016 // Set up the orientation of the outer orbit with respect to the inner
00017 // binary [which always lies in the (x-y) plane].
00018 
00019 // See "sigma3.h" for details.
00020 
00021 local void set_orientation(kepler &k, phase3 &p)
00022 {
00023     real mu = p.cos_theta;
00024     real sin_theta = sqrt(1 - mu * mu);
00025 
00026     // Construct the normal vector:
00027 
00028     vector n = vector(sin_theta*cos(p.phi), sin_theta*sin(p.phi), mu);
00029 
00030     // Construct unit vectors a and b perpendicular to n:
00031 
00032     vector temp = vector(1, 0, 0);
00033     if (abs(n[0]) > .5) temp = vector(0, 1, 0); // temp is not parallel to n
00034     if (n[2] < 0) temp = -temp;
00035 
00036     vector b = n ^ temp;
00037     b /= abs(b);
00038     vector a = b ^ n;
00039     if (n[2] < 0) a = -a;       // Force (a, b) to be (x, y) for n = +/-z
00040     
00041     // Construct *random* unit vectors l and t perpendicular to each
00042     // other and to n (psi = 0 ==> periastron along a):
00043 
00044     vector l = cos(p.psi)*a + sin(p.psi)*b;
00045     vector t = n ^ l;
00046 
00047     k.set_orientation(l, t, n);
00048     k.initialize_from_shape_and_phase();
00049 }
00050 
00051 local real potential_energy(sdyn3 * b)
00052 {
00053     real u = 0;
00054 
00055     for_all_daughters(sdyn3, b, bi) {
00056         for (sdyn3 * bj = bi->get_younger_sister(); bj != NULL;
00057              bj = bj->get_younger_sister())
00058           u -= bi->get_mass() * bj->get_mass()
00059                / abs(bi->get_pos() - bj->get_pos());
00060     }
00061 
00062     return u;
00063 }
00064 
00065 local real energy(sdyn3 * b)
00066 {
00067     real k = 0, u = 0, dissipation = 0;
00068 
00069     for_all_daughters(sdyn3, b, bi) {
00070         k += bi->get_mass() * bi->get_vel() * bi->get_vel();
00071         dissipation += bi->get_energy_dissipation();
00072         for (sdyn3 * bj = bi->get_younger_sister(); bj != NULL;
00073              bj = bj->get_younger_sister())
00074           u -= bi->get_mass() * bj->get_mass()
00075                / abs(bi->get_pos() - bj->get_pos());
00076     }
00077 
00078     return 0.5*k + u + dissipation;
00079 }
00080 
00081 local void kepler_pair_to_triple(kepler & k1,   // Inner binary (b1 + b2)
00082                                  kepler & k3,   // Outer binary
00083                                  sdyn3 * b1, sdyn3 * b2, sdyn3 * b3)
00084 
00085 // Construct positions and velocities from the specified kepler structures.
00086 // Assume that labels. masses and times are handled elsewhere.
00087 
00088 {
00089     // Set up the outer orbit first.  Note that the center of mass of
00090     // the three-body system is taken to be at rest at the origin.
00091 
00092     // Make no assumptions about masses!
00093 
00094     real m1 = b1->get_mass();
00095     real m2 = b2->get_mass();
00096     real m3 = b3->get_mass();
00097 
00098     real m12 = m1 + m2;
00099     real m123 = m12 + m3;
00100 
00101     // relative position and velocity from (1,2) to 3
00102 
00103     b3->set_pos(m12 * k3.get_rel_pos() / m123);
00104     b3->set_vel(m12 * k3.get_rel_vel() / m123);
00105     
00106     b1->set_pos(-m3 * k3.get_rel_pos() / m123);
00107     b1->set_vel(-m3 * k3.get_rel_vel() / m123);
00108 
00109     b2->set_pos(b1->get_pos());
00110     b2->set_vel(b1->get_vel());
00111 
00112     // Then set up the inner binary.
00113 
00114     b1->inc_pos(-m2 * k1.get_rel_pos() / m12);      // rel_pos is from 1 to 2
00115     b1->inc_vel(-m2 * k1.get_rel_vel() / m12);
00116 
00117     b2->inc_pos( m1 * k1.get_rel_pos() / m12);
00118     b2->inc_vel( m1 * k1.get_rel_vel() / m12);
00119 
00120 }
00121 
00122 local sdyn3 * set_up_dynamics(real m2,      // mass of secondary in binary
00123                               real m3,      // mass of projectile (m1 + m2 = 1)
00124                               kepler & k1,  // inner binary
00125                               kepler & k3)  // outer binary
00126     {
00127     // Create bodies for integration.
00128 
00129     sdyn3 * b = mksdyn3(3);                // pointer to the 3-body system
00130 
00131     // Initialize the dynamics.
00132 
00133     b->set_time(k3.get_time());
00134     for_all_daughters(sdyn3, b, bb)
00135         bb->set_time(k3.get_time());
00136 
00137     sdyn3 * b1 = b->get_oldest_daughter();
00138     sdyn3 * b2 = b1->get_younger_sister();
00139     sdyn3 * b3 = b2->get_younger_sister();
00140 
00141     b1->set_label(1);
00142     b2->set_label(2);
00143     b3->set_label(3);
00144 
00145     b->set_mass(1 + m3);
00146     b1->set_mass(1 - m2);
00147     b2->set_mass(m2);
00148     b3->set_mass(m3);
00149 
00150     kepler_pair_to_triple(k1, k3, b1, b2, b3);
00151 
00152     return b;
00153     }
00154 
00155 // init_to_sdyn3: convert an initial state to an sdyn3 for integration.
00156 
00157 local sdyn3 * init_to_sdyn3(initial_state3 & init, final_state3 & final)
00158 {
00159     set_kepler_tolerance(2);
00160 
00161     kepler k1;  // Inner binary.
00162 
00163     real peri = 1; // Default value (unimportant unless init.ecc = 1).
00164     if (init.ecc == 1) peri = 0;
00165 
00166     make_standard_kepler(k1, 0, 1, -0.5 / init.sma, init.ecc,
00167                          peri, init.phase.mean_anomaly);
00168 
00169 //    cout << endl << "Inner binary:" << endl;
00170 //    k1.print_all(cout);
00171 
00172     // Multiply the incoming velocity by vcrit.
00173 
00174     real m2 = init.m2;
00175     real m3 = init.m3;
00176     real mtotal = 1 + m3;
00177     real v_inf = init.v_inf * sqrt( (1 - m2) * m2 * mtotal / m3 );
00178     
00179     real energy3 = .5 * v_inf * v_inf;
00180     real ang_mom3 = init.rho * v_inf;
00181 
00182     real ecc3 = (energy3 == 0 ? 1
00183                               : sqrt( 1 + 2 * energy3 
00184                                             * pow(ang_mom3/mtotal, 2)));
00185 
00186     // Special case: if v_inf = 0, assume rho is periastron.
00187 
00188     real virial_ratio = init.rho;
00189 
00190     kepler k3;  // Outer binary.
00191 
00192     make_standard_kepler(k3, 0, mtotal, energy3, ecc3, virial_ratio, 0);
00193 
00194     // Radius for "unperturbed" inner binary:
00195 
00196     real r_unp = (init.sma + init.r3)
00197                      * pow(TIDAL_TOL_FACTOR / mtotal, -1/3.0);
00198 
00199     if (r_unp <= k3.get_periastron()) {
00200         final.descriptor = preservation;
00201         final.sma = k1.get_semi_major_axis();
00202         final.ecc = k1.get_eccentricity();
00203         final.outer_separation = k3.get_periastron();
00204         final.escaper = 3;
00205         final.error = 0;
00206         final.time = 0;
00207         final.n_steps = 0;
00208         final.virial_ratio = k3.get_energy() * k3.get_periastron() 
00209                                                 / k3.get_total_mass() - 1;
00210 
00211         // Sufficiently large stellar radii (sum larger than the binary
00212         // periastron) will cause an immediate collision to occur, so we
00213         // must check for it explicitly here.
00214 
00215         if (k1.get_periastron() < init.r1 + init.r2) {
00216             final.descriptor = merger_escape_3;
00217             final.sma = final.ecc = -1;
00218         }
00219 
00220         return NULL;
00221     }
00222 
00223     init.r_init = max(init.r_init_min, min(init.r_init_max, r_unp));
00224 
00225     k1.transform_to_time(k3.return_to_radius(init.r_init));
00226 
00227     set_orientation(k3, init.phase);
00228 
00229 //    cout << endl << "Outer binary:" << endl;
00230 //    k3.print_all(cout);
00231 
00232     sdyn3 * b;
00233     b = set_up_dynamics(m2, m3, k1, k3);
00234 
00235 //  Set up radii:
00236 
00237     sdyn3 * bb = b->get_oldest_daughter();
00238     bb->set_radius(init.r1);
00239     bb = bb->get_younger_sister();
00240     bb->set_radius(init.r2);
00241     bb = bb->get_younger_sister();
00242     bb->set_radius(init.r3);
00243 
00244     return  b;
00245 }
00246 
00247 local void extend_orbits(sdyn3 * b1, sdyn3 * b2, sdyn3 * b3)
00248 
00249 // Analytically extend the orbits of the [[1,2],3] hierarchical system.
00250 
00251     {
00252     if ( (b1->get_time() != b2->get_time())
00253          || (b1->get_time() != b3->get_time()) )
00254         err_exit("extend_orbits: inconsistent times");
00255     
00256     real time = b3->get_time();
00257     
00258     kepler inner, outer;
00259     
00260     set_kepler_from_sdyn3(inner, b1, b2);
00261     
00262     real m1 = b1->get_mass();
00263     real m2 = b2->get_mass();
00264     real m3 = b3->get_mass();
00265     real m12 = m1 + m2;
00266     real m123 = m12 + m3;
00267     
00268     outer.set_time(time);
00269     outer.set_total_mass(m123);
00270     
00271     // Center of mass position and velocity of the inner binary:
00272     
00273     vector cmr = (m1 * b1->get_pos() + m2 * b2->get_pos()) / m12;
00274     vector cmv = (m1 * b1->get_vel() + m2 * b2->get_vel()) / m12;
00275     
00276     outer.set_rel_pos(b3->get_pos() - cmr);
00277     outer.set_rel_vel(b3->get_vel() - cmv);
00278     
00279     outer.initialize_from_pos_and_vel();
00280     
00281     // Map forward in time by an integral number of inner binary periods
00282     // to an incoming configuration as close as possible to the outgoing one.
00283     
00284     int inner_orbits = 2 * (int) ((outer.pred_advance_to_apastron() - time)
00285                                   / inner.get_period() + 0.5);
00286     time += inner_orbits * inner.get_period();
00287     outer.transform_to_time(time);
00288     
00289     // Note that no modification is needed to the inner orbit
00290     // except to increase its time.
00291     
00292     inner.set_time(time);
00293     
00294     // Reconstruct the new sdyn3s:
00295     
00296     b1->get_parent()->set_time(time);
00297     b1->set_time(time);
00298     b2->set_time(time);
00299     b3->set_time(time);
00300     
00301     kepler_pair_to_triple(inner, outer, b1, b2, b3);
00302     
00303 //    cerr << "extend_orbits:  " << inner_orbits << " inner orbits" << endl;
00304 //    cerr << "outer orbit: " << endl;
00305 //    outer.print_elements(cerr);
00306 //    cerr << "inner orbit: " << endl;
00307 //    inner.print_elements(cerr);
00308     
00309     }
00310 
00311 local int escape(sdyn3 * bi, sdyn3 * bj, sdyn3 * bk,
00312                  real ejk,                              //(for convenience)
00313                  real r_stop, 
00314                  intermediate_state3 & inter,
00315                  final_state3 & final)
00316 
00317 // Return true iff bi is escaping from the center of mass of bj and bk.
00318 // Perform analytic continuation if appropriate.
00319 // On entry, we already know that i is "widely" separated from j and k.
00320 
00321 {
00322     final.descriptor = unknown_final;
00323     if (ejk >= 0) return 0;             // Inner pair not bound.
00324 
00325     real mi = bi->get_mass();
00326     real mj = bj->get_mass();
00327     real mk = bk->get_mass();
00328     real mjk = mj + mk;
00329 
00330     // Center of mass position and velocity of j-k "binary":
00331 
00332     vector cmr = (mj * bj->get_pos() + mk * bk->get_pos()) / mjk;
00333     vector cmv = (mj * bj->get_vel() + mk * bk->get_vel()) / mjk;
00334 
00335     // Return immediately if third particle is approaching binary.
00336 
00337     vector dv = bi->get_vel() - cmv;
00338     vector dr = bi->get_pos() - cmr;
00339 
00340     if (dr*dv <= 0) return 0;
00341 
00342     real sep = abs(dr);
00343     real virial_ratio = .5 * dv * dv * sep / (mi + mj + mk);
00344 
00345     if (sep > r_stop) {    // Emergency stop: should really be put elsewhere!
00346 
00347         real ajk = 0.5 * mjk / abs(ejk);
00348 
00349         final.descriptor = stopped;
00350 
00351         final.sma = ajk;
00352         real ang_mom = abs((bk->get_pos() - bj->get_pos())
00353                            ^ (bk->get_vel() - bj->get_vel()));
00354 
00355         real ecc2 = 1 - ang_mom * ang_mom / (mjk * ajk);
00356         if (ecc2 < 1.e-10) {
00357 
00358             // Take care with small eccentricities! 
00359 
00360             kepler k;
00361             set_kepler_from_sdyn3(k, bj, bk);
00362             final.ecc = k.get_eccentricity();
00363 
00364         } else
00365             final.ecc = sqrt(ecc2);
00366 
00367         final.escaper = bi->get_index();
00368         final.virial_ratio = virial_ratio;
00369         final.outer_separation = sep;
00370 
00371         return 1;
00372     }
00373 
00374     // Test for sufficiently unperturbed inner binary.
00375     
00376     real ajk = 0.5 * mjk / abs(ejk);
00377 
00378     // Determine limiting radius for "negligible" tidal perturbation
00379     // Note that the term in the denominator is mjk + mi, not just
00380     // mi, in order to prevent problems with low-mass third stars.
00381 
00382     real rlimit = (ajk + bi->get_radius())
00383                      * pow(TIDAL_TOL_FACTOR * mjk / (mi + mjk), -1/3.0);
00384 
00385     if (sep < rlimit) return 0;
00386 
00387     // Test for sufficiently non-perturbed outer binary. The safety
00388     // factor is reduced at large radii.
00389 
00390     real scaled_safety_factor = ENERGY_SAFETY_FACTOR * rlimit / sep;
00391 
00392 //    int p = cerr.precision(STD_PRECISION);
00393 //    cerr << "inner unperturbed, sep, rlim, factor, |v - 1| = " << endl;
00394 //    cerr << "   " << sep <<" "<< rlimit <<" "<< scaled_safety_factor
00395 //       <<" "<< abs(virial_ratio - 1) << endl;
00396 //    cerr.precision(p);
00397 
00398     if (abs(virial_ratio - 1) < scaled_safety_factor) return 0;
00399 
00400     // Now outer binary is either clearly bound or clearly unbound.
00401 
00402     if (virial_ratio > 1) {
00403 
00404         // Body bi is escaping.
00405 
00406         if (bi->get_index() == 1)       // Initial binary was (1,2).
00407             final.descriptor = exchange_1;
00408         else if (bi->get_index() == 2)
00409             final.descriptor = exchange_2;
00410         else if (bi->get_index() == 3)
00411             final.descriptor = preservation;
00412         else
00413             final.descriptor = unknown_final;
00414 
00415         final.sma = ajk;
00416         real ang_mom = abs((bk->get_pos() - bj->get_pos())
00417                            ^ (bk->get_vel() - bj->get_vel()));
00418 
00419         real ecc2 = 1 - ang_mom * ang_mom / (mjk * ajk);
00420         if (ecc2 < 1.e-10) {
00421 
00422             // Take care with small eccentricities! 
00423 
00424             kepler k;
00425             set_kepler_from_sdyn3(k, bj, bk);
00426             final.ecc = k.get_eccentricity();
00427 
00428         } else
00429             final.ecc = sqrt(ecc2);
00430 
00431         final.escaper = bi->get_index();
00432         final.virial_ratio = virial_ratio;
00433         final.outer_separation = sep;
00434 
00435         return 1;
00436 
00437     } else {
00438 
00439         // Analytically extend the orbits by an integral number
00440         // of binary periods.
00441 
00442 //      real e = energy(bi->get_parent());
00443 
00444         extend_orbits(bj, bk, bi);
00445         inter.n_kepler++;
00446 
00447 //      cerr << "energy error = " << energy(bi->get_parent()) - e << endl;
00448 
00449     }
00450     
00451     return 0;
00452 }
00453 
00454 // set_merger_mass_and_radius: determine mass and radius of merger product
00455 
00456 void set_merger_mass_and_radius(sdyn3 * bn, sdyn3 * bi, sdyn3 * bj)
00457     {
00458     bn->set_mass(bi->get_mass() + bj->get_mass());       // No mass loss
00459     bn->set_radius(bi->get_radius() + bj->get_radius()); // R \propto M
00460     }
00461 
00462 // set_merger_dyn: determine pos, vel, acc and jerk of merger product,
00463 //                 and determine energy dissipation
00464 
00465 void set_merger_dyn(sdyn3 * bn, sdyn3 * bi, sdyn3 * bj)
00466     {
00467     real  mi = bi->get_mass();
00468     real  mj = bj->get_mass();
00469     real  m_inv = 1/(mi + mj);
00470     
00471     bn->set_pos((mi*bi->get_pos() + mj*bj->get_pos())*m_inv);
00472     bn->set_vel((mi*bi->get_vel() + mj*bj->get_vel())*m_inv);
00473     bn->set_acc((mi*bi->get_acc() + mj*bj->get_acc())*m_inv);
00474     bn->set_jerk((mi*bi->get_jerk() + mj*bj->get_jerk())*m_inv);
00475 
00476     vector d_pos = bi->get_pos() - bj->get_pos();
00477     real rij = sqrt(d_pos*d_pos);
00478 
00479     vector d_vel = bi->get_vel() - bj->get_vel();
00480     real vij2 = d_vel*d_vel;
00481 
00482     real eij_pot = -mi * mj / rij;
00483     real eij_kin = 0.5 * mi * mj * m_inv * vij2;
00484 
00485     // Include tidal term from spectator star, if any:
00486 
00487     sdyn3 * b = bi->get_parent();
00488     sdyn3 * bk = NULL;
00489 
00490     for_all_daughters(sdyn3, b, bb)
00491         if (bb != bi && bb != bj) bk = bb;
00492 
00493     real tidal_pot = 0;
00494     if (bk)
00495         {
00496         real mn = mi + mj;
00497         real mk = bk->get_mass();
00498         tidal_pot = -mn * mk / abs(bn->get_pos() - bk->get_pos())
00499                     + mi * mk / abs(bi->get_pos() - bk->get_pos())
00500                     + mj * mk / abs(bj->get_pos() - bk->get_pos());
00501         }
00502 
00503     bn->set_energy_dissipation(bi->get_energy_dissipation()
00504                                + bj->get_energy_dissipation()
00505                                + eij_pot + eij_kin
00506                                - tidal_pot);
00507 
00508     }
00509 
00510 // merge: replace two particles by their center of mass.
00511 
00512 void merge(sdyn3 * bi, sdyn3 * bj)
00513     {
00514     sdyn3 * b = bi->get_parent();
00515     if (b != bj->get_parent()) err_exit("merge: parent conflict...");
00516 
00517     // Note: any stories attached to the particles are lost.
00518 
00519     sdyn3 * bn = new sdyn3();
00520 
00521 //    int p = cerr.precision(STD_PRECISION);
00522 //    cerr << "entering merge(" << bi->get_index() << ", " << bj->get_index()
00523 //         << ") with r1 = " << bi->get_pos() << "\n                     "
00524 //         << " and r2 = " << bj->get_pos()   << endl
00525 //         << "         at t = " << b->get_time() << endl;
00526 //    cerr.precision(p);
00527 
00528     set_merger_mass_and_radius(bn, bi, bj);
00529     set_merger_dyn(bn, bi, bj);
00530     bn->set_time(bi->get_time());
00531 
00532     int max_index = 0;
00533     for_all_daughters(sdyn3, b, bb)
00534         if (max_index < bb->get_index())
00535             max_index = bb->get_index();
00536 
00537     bn->set_label(max_index+1);
00538 
00539     detach_node_from_general_tree(*bi);
00540     detach_node_from_general_tree(*bj);
00541   
00542     add_node(*bn, *b);
00543     }
00544 
00545 // merge_collisions: recursively merge any stars in contact.
00546 
00547 local void merge_collisions(sdyn3 * b)
00548     {
00549     int coll = 1;
00550     while (coll)
00551         {
00552         coll = 0;
00553         for_all_daughters(sdyn3, b, bi)
00554             {
00555             if (coll) break;
00556             for (sdyn3 * bj = bi->get_younger_sister(); bj != NULL; 
00557                  bj = bj->get_younger_sister())
00558                 {
00559                 if ( abs(bi->get_pos() - bj->get_pos())
00560                         < (bi->get_radius() + bj->get_radius()) )
00561                     {
00562                     merge(bi, bj);
00563                     coll = 1;
00564                     break;
00565                     }
00566                 }
00567             }
00568         }
00569     }
00570 
00571 // triple_escape: return 1 if star 3 is unbound with respect to the center
00572 //                of mass of stars 1 and 2 (distance r3) and is a negligible
00573 //                perturber of the (1,2) hyperbolic orbit.
00574 
00575 local int triple_escape(real e12, real r3, real m12, real m3)
00576 {
00577     if (e12 < 0) return 0; // e12 = energy per unit reduced mass of pair (1,2)
00578 
00579     real a12 = 0.5 * m12 / e12;
00580 
00581     // The m12 in the denominator here includes the tidal effect of (1,2) on 3.
00582 
00583     if (r3 > a12 * pow(TIDAL_TOL_FACTOR * m12 / (m3 + m12), -1/3.0))
00584         return 1;
00585     else
00586         return 0;
00587 }
00588 
00589 local int extend_or_end_scatter3(sdyn3 * b, real r_stop,
00590                                  intermediate_state3 & inter,
00591                                  final_state3 & final)
00592     {
00593 
00594     b->to_com();          // map the whole system to center-of-mass frame
00595 
00596     sdyn3 * b1 = b->get_oldest_daughter();
00597     sdyn3 * b2 = b1->get_younger_sister();
00598     sdyn3 * b3 = b2->get_younger_sister();
00599 
00600     real r12 = abs(b1->get_pos() - b2->get_pos());
00601     real r23 = abs(b2->get_pos() - b3->get_pos());
00602     real r31 = abs(b3->get_pos() - b1->get_pos());
00603 
00604     real m1 = b1->get_mass();
00605     real m2 = b2->get_mass();
00606     real m3 = b3->get_mass();
00607 
00608     real k1 = 0.5 * m1 * b1->get_vel() * b1->get_vel();
00609     real k2 = 0.5 * m2 * b2->get_vel() * b2->get_vel();
00610     real k3 = 0.5 * m3 * b3->get_vel() * b3->get_vel();
00611 
00612     real phi12 = m1 * m2 / r12;
00613     real phi23 = m2 * m3 / r23;
00614     real phi31 = m3 * m1 / r31;
00615 
00616     real mu12 = m1 * m2 / (m1 + m2);
00617     real mu23 = m2 * m3 / (m2 + m3);
00618     real mu31 = m3 * m1 / (m3 + m1);
00619 
00620     vector dv = b2->get_vel() - b1->get_vel();
00621     real k12 = 0.5 * mu12 * dv * dv;
00622     real vr12 = dv * (b2->get_pos() - b1->get_pos());
00623 
00624     dv = b3->get_vel() - b2->get_vel();
00625     real k23 = 0.5 * mu23 * dv * dv;
00626     real vr23 = dv * (b3->get_pos() - b2->get_pos());
00627 
00628     dv = b1->get_vel() - b3->get_vel();
00629     real k31 = 0.5 * mu31 * dv * dv;
00630     real vr31 = dv * (b1->get_pos() - b3->get_pos());
00631 
00632     // First test for ionization.
00633 
00634     if (k1 + k2 + k3 >= phi12 + phi23 + phi31
00635         && r12 > LARGE_SEPARATION 
00636         && r23 > LARGE_SEPARATION
00637         && r31 > LARGE_SEPARATION
00638         && k12 > phi12 && k23 > phi23 && k31 > phi31
00639         && vr12 > 0 && vr23 > 0 && vr31 > 0
00640         && triple_escape(k12 - phi12, min(r23, r31), m1 + m2, m3)
00641         && triple_escape(k23 - phi23, min(r31, r12), m2 + m3, m1)
00642         && triple_escape(k31 - phi31, min(r12, r23), m3 + m1, m2)) {
00643 
00644               final.descriptor = ionization;
00645               final.virial_ratio = min(min(k12/phi12, k23/phi23), k31/phi31);
00646               final.outer_separation = -1;
00647               final.escaper = -1;                // -1 means all escaping
00648               return 1;
00649     }
00650 
00651     // Now test the closest pair and the third star for escape.
00652     // Function "escape" also does analytic extension, if appropriate.
00653     // Note that the emergency stopping criterion R > r_stop returns
00654     // looking like an escape has occurred. This should be decoupled from
00655     // the escape test (someday).
00656 
00657     if (r12 * LARGE_SEPARATION_FACTOR < (r23 + r31))
00658         return escape(b3, b1, b2, (k12 - phi12)/mu12, r_stop, inter, final);
00659 
00660     if (r23 * LARGE_SEPARATION_FACTOR < (r31 + r12))
00661         return escape(b1, b2, b3, (k23 - phi23)/mu23, r_stop, inter, final);
00662 
00663     if (r31 * LARGE_SEPARATION_FACTOR < (r12 + r23))
00664         return escape(b2, b3, b1, (k31 - phi31)/mu31, r_stop, inter, final);
00665 
00666     return 0;
00667     }
00668 
00669 local int extend_or_end_scatter2(sdyn3 * b, final_state3 & final)
00670 {
00671     sdyn3 * d1 = b->get_oldest_daughter();
00672     sdyn3 * d2 = d1->get_younger_sister();
00673     kepler k;
00674 
00675     sdyn3 * merger = NULL;
00676     sdyn3 * single = NULL;
00677 
00678     if (d1->get_index() > 3)
00679         {
00680         merger = d1;
00681         single = d2;
00682         }
00683     else
00684         {
00685         merger = d2;
00686         single = d1;
00687         }
00688 
00689     set_kepler_from_sdyn3(k, d1, d2);
00690 
00691     real closest_approach = k.get_periastron();
00692     if (k.get_energy() >= 0
00693         && (d1->get_pos() - d2->get_pos())
00694             * (d1->get_vel() - d2->get_vel()) > 0)
00695         closest_approach = k.get_separation();
00696 
00697     if (closest_approach < d1->get_radius() + d2->get_radius()) {
00698 
00699         merge(d1, d2);
00700         d1 = b->get_oldest_daughter();
00701 
00702         final.descriptor = triple_merger;
00703         final.escaper = 0;                // 0 means no escaper
00704         final.outer_separation = 0;
00705         final.virial_ratio = -1;
00706 
00707     } else {
00708 
00709         real closest_approach_squared = closest_approach * closest_approach;
00710 
00711         merger->set_min_nn_dr2(closest_approach_squared);
00712         merger->set_min_nn_label(single->get_index());
00713 
00714         if (closest_approach_squared < single->get_min_nn_dr2())
00715             {
00716             single->set_min_nn_dr2(closest_approach_squared);
00717             single->set_min_nn_label(merger->get_index());
00718             }
00719 
00720 
00721         if (k.get_energy() < 0) {
00722 
00723             if (single->get_index() == 1)
00724                 final.descriptor = merger_binary_1;
00725             else if (single->get_index() == 2)
00726                 final.descriptor = merger_binary_2;
00727             else if (single->get_index() == 3)
00728                 final.descriptor = merger_binary_3;
00729             else
00730                 final.descriptor = unknown_final;
00731 
00732             final.sma = k.get_semi_major_axis();
00733             final.ecc = k.get_eccentricity();
00734             final.outer_separation = -1;
00735             final.escaper = 0;
00736             final.virial_ratio = -1;
00737 
00738         } else {
00739 
00740             if (single->get_index() == 1)
00741                 final.descriptor = merger_escape_1;
00742             else if (single->get_index() == 2)
00743                 final.descriptor = merger_escape_2;
00744             else if (single->get_index() == 3)
00745                 final.descriptor = merger_escape_3;
00746             else
00747                 final.descriptor = unknown_final;
00748 
00749             final.outer_separation = k.get_separation();
00750             final.escaper = single->get_index();
00751             final.virial_ratio = k.get_energy() * k.get_separation() 
00752                                                 / k.get_total_mass() - 1;
00753         }
00754     }
00755 
00756     return 1;
00757 }
00758 
00759 local int extend_or_end_scatter1(final_state3 & final)
00760 {
00761     final.descriptor = triple_merger;
00762     final.escaper = 0;                      // 0 denotes no escaper
00763     final.outer_separation = 0;
00764     final.virial_ratio = -1;
00765 
00766     return 1;
00767 }
00768 
00769 // extend_or_end_scatter:
00770 //      set the final state and return 1 iff the scattering is over
00771 //      analytically extend the scattering if appropriate
00772 
00773 local int extend_or_end_scatter(sdyn3 * b, real r_stop,
00774                                 intermediate_state3 & inter,
00775                                 final_state3 & final)
00776 {
00777     // Place an absolute limit on the total number of steps:
00778 
00779     if (b->get_n_steps() > MAX_N_STEPS) {
00780         final.descriptor = stopped;
00781         return 1;
00782     }
00783 
00784     int n = 0;
00785     for_all_daughters(sdyn3, b, bb) n++;
00786 
00787     // Set some default values for these quantities:
00788 
00789     final.sma = -1;
00790     final.ecc = -1;
00791 
00792     if (n == 3) 
00793         return extend_or_end_scatter3(b, r_stop, inter, final);
00794     else if (n == 2)
00795         return extend_or_end_scatter2(b, final);
00796     else if (n == 1)
00797         return extend_or_end_scatter1(final);
00798     else {
00799         cerr << "extend_or_end_scatter: n = " << n << endl;
00800         exit(1);
00801     }
00802     return 0; // To keep HP g++ happy!
00803 }
00804 
00805 // check_init: make sure an initial state is sensible.
00806 
00807 local void check_init(initial_state3 & init)
00808     {
00809     if (init.m2 > 1) err_exit("check_init: m1 < 0");
00810     if (init.m2 < 0) err_exit("check_init: m2 < 0");
00811     if (init.m3 < 0) err_exit("check_init: m3 < 0");
00812     if (init.sma <= 0) err_exit("check_init: semi-major axis <= 0");
00813     if (init.ecc < 0) err_exit("check_init: eccentricity < 0");
00814     if (init.ecc > 1) err_exit("check_init: eccentricity > 1");
00815     if (init.r1 < 0) err_exit("check_init: r1 < 0");
00816     if (init.r2 < 0) err_exit("check_init: r2 < 0");
00817     if (init.r3 < 0) err_exit("check_init: r3 < 0");
00818     if (init.v_inf < 0) err_exit("check_init: v_inf < 0");
00819     if (init.rho < 0) err_exit("check_init: rho < 0");
00820     if (init.eta <= 0) err_exit("check_init: eta <= 0");
00821     }
00822 
00823 // sdyn3_to_system: save sdyn3 dynamical information in the specified array
00824 
00825 local void sdyn3_to_system(sdyn3 * root, body * system)
00826 {
00827     int n = 0;
00828     for_all_daughters(sdyn3, root, bb) {
00829         system[n].index = bb->get_index();
00830         system[n].mass  = bb->get_mass();
00831         for (int kp = 0; kp < 3; kp++) system[n].pos[kp] = bb->get_pos()[kp];
00832         for (int kv = 0; kv < 3; kv++) system[n].vel[kv] = bb->get_vel()[kv];
00833         n++;
00834     }
00835 }
00836 
00837 // scatter3: perform a three-body scattering, initializing from a specified
00838 //           state and returning intermediate- and final-state structures.
00839 
00840 void scatter3(initial_state3 & init,
00841               intermediate_state3 & inter,
00842               final_state3 & final,
00843               real cpu_time_check,
00844               real dt_out,         // diagnostic output interval
00845               real dt_snap,        // snapshot output interval
00846               real snap_cube_size, // limit output to particles within cube
00847               real dt_print,       // print output interval
00848               sdyn3_print_fp p)    // function to print output
00849 {
00850     static int count = 0;
00851 
00852     check_init(init);
00853     sdyn3 * b = init_to_sdyn3(init, final);
00854 
00855 
00856     //--------------------------------------------------
00857 
00858 
00859     // Just print out info and return...
00860 
00861     sdyn3* b1 = b->get_oldest_daughter();
00862     sdyn3* b2 = b1->get_younger_sister();
00863     sdyn3* b3 = b2->get_younger_sister();
00864     
00865     // First look at 1-2 vector:
00866 
00867     vector dx = b1->get_pos() - b2->get_pos();
00868     cout << ++count
00869          << "  " << 180*atan2(dx[1], dx[0])/PI
00870          << "  " << dx[2]/abs(dx);
00871 
00872     // Then the outer orbit:
00873 
00874     vector cm = (b1->get_mass()*b1->get_pos()
00875                    + b2->get_mass()*b2->get_pos())
00876                      / (b1->get_mass() + b2->get_mass());
00877     dx = b3->get_pos() - cm;
00878         
00879     cout << "  " << 180*atan2(dx[1], dx[0])/PI
00880          << "  " << dx[2]/abs(dx);
00881 
00882     cout << endl;
00883 
00884 
00885     //--------------------------------------------------
00886 
00887 
00888     // Delete the 3-body sytem.
00889 
00890     sdyn3 * bi = b->get_oldest_daughter();
00891     while (bi) {
00892         sdyn3 * tmp = bi->get_younger_sister();
00893         delete bi;
00894         bi = tmp;
00895     }
00896     delete b;
00897 }
00898 
00899 main(int argc, char **argv)
00900     {
00901 
00902     initial_state3 init;
00903     make_standard_init(init);
00904 
00905     int  seed       = 0;        // seed for random number generator
00906     int n_rand      = 0;        // number of times to invoke the generator
00907                                 // before starting for real
00908     int  n_experiments = 1;     // default: only one run
00909     real dt_out     =           // output time interval
00910           VERY_LARGE_NUMBER;
00911     real dt_snap    =           // output time interval
00912           VERY_LARGE_NUMBER;
00913 
00914     real cpu_time_check = 3600;
00915     real snap_cube_size = 10;
00916 
00917     int planar_flag = 0;
00918 
00919     bool  b_flag = FALSE;
00920     bool  q_flag = FALSE;
00921     bool  Q_flag = FALSE;
00922 
00923     extern char *poptarg;
00924     int  pgetopt(int, char **, char *), c;
00925 
00926     while ((c = pgetopt(argc, argv,
00927                 "A:bc:C:d:D:e:L:m:M:n:N:pPqQr:R:s:S:U:v:x:y:z:")) != -1)
00928         switch(c)
00929             {
00930             case 'A': init.eta = atof(poptarg);
00931                       break;
00932             case 'b': b_flag = 1 - b_flag;
00933                       break;
00934             case 'c': cpu_time_check = 3600*atof(poptarg);// (Specify in hours)
00935                       break;
00936             case 'C': snap_cube_size = atof(poptarg);
00937                       break;
00938             case 'd': dt_out = atof(poptarg);
00939                       break;
00940             case 'D': dt_snap = atof(poptarg);
00941                       break;
00942             case 'e': init.ecc = atof(poptarg);
00943                       break;
00944             case 'L': init.r_init_min = atof(poptarg);
00945                       break;
00946             case 'm': init.m2 = atof(poptarg);
00947                       break;
00948             case 'M': init.m3 = atof(poptarg);
00949                       break;
00950             case 'n': n_experiments = atoi(poptarg);
00951                       break;
00952             case 'N': n_rand = atoi(poptarg);
00953                       break;
00954             case 'p': planar_flag = 1;
00955                       break;
00956             case 'P': planar_flag = -1;
00957                       break;
00958             case 'q': q_flag = 1 - q_flag;
00959                       break;
00960             case 'Q': Q_flag = 1 - Q_flag;
00961                       break;
00962             case 'r': init.rho = atof(poptarg);
00963                       break;
00964             case 'R': init.r_stop = init.r_init_min
00965                                   = init.r_init_max
00966                                   = atof(poptarg);
00967                       break;
00968             case 's': seed = atoi(poptarg);
00969                       break;
00970             case 'S': init.r_stop = atof(poptarg);
00971                       break;
00972             case 'U': init.r_init_max = atof(poptarg);
00973                       break;
00974             case 'v': init.v_inf = atof(poptarg);
00975                       break;
00976             case 'x': init.r1 = atof(poptarg);
00977                       break;
00978             case 'y': init.r2 = atof(poptarg);
00979                       break;
00980             case 'z': init.r3 = atof(poptarg);
00981                       break;
00982             case '?': cerr <<
00983                      "usage: scatter3 [-A #] [-b] [-c #] [-C #] [-d #] [-D #] "
00984                       << "[-e #] [-L #] [-m #] [-M #] [-n #] [-N #] [-p] "
00985                       << "[-P] [-q] [-Q] [-r #] [-R #] [-s #] "
00986                       << "[-S #] [-U #] [-v #] [-x #] [-y #] [-z #]" << endl;
00987                       exit(1);
00988             }            
00989 
00990     if (Q_flag) q_flag = TRUE;
00991 
00992     if (init.m2 > 1)
00993         {
00994         cerr << "sigma3: init.m2 = " << init.m2 << " > 1" << endl;
00995         exit(1);
00996         }
00997 
00998     cpu_init();
00999     int random_seed = srandinter(seed, n_rand);
01000 
01001     while (n_experiments--) {
01002 
01003 //      cerr << "Random seed = " << get_initial_seed()
01004 //           << "  n_rand = " << get_n_rand() << flush;
01005 
01006         randomize_angles(init.phase);
01007 
01008         if (planar_flag == 1)
01009             init.phase.cos_theta = 1;   // Planar prograde
01010         else if (planar_flag == -1)
01011             init.phase.cos_theta = -1;  // Planar retrograde
01012 
01013         intermediate_state3 inter;
01014         final_state3 final;
01015 
01016         real cpu = cpu_time();
01017         scatter3(init, inter, final, cpu_time_check,
01018                  dt_out, dt_snap, snap_cube_size);
01019     }
01020 }

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