Main Page   Class Hierarchy   Data Structures   File List   Data Fields   Globals  

hdyn_unpert.C

Go to the documentation of this file.
00001 
00002        //=======================================================//    _\|/_
00003       //  __  _____           ___                    ___       //      /|\ ~
00004      //  /      |      ^     |   \  |         ^     |   \     //          _\|/_
00005     //   \__    |     / \    |___/  |        / \    |___/    //            /|\ ~
00006    //       \   |    /___\   |  \   |       /___\   |   \   // _\|/_
00007   //     ___/   |   /     \  |   \  |____  /     \  |___/  //   /|\ ~
00008  //                                                       //            _\|/_
00009 //=======================================================//              /|\ ~
00010 
00011 // Initiate and maintain unperturbed motion.
00012 //
00013 // Externally visible functions:
00014 //
00015 //      void hdyn::print_unperturbed_binary_params
00016 //      void hdyn::update_dyn_from_kepler
00017 //      bool hdyn::is_close_pair
00018 //      bool hdyn::is_unperturbed_and_approaching
00019 //      void hdyn::startup_unperturbed_motion
00020 //      bool hdyn::integrate_unperturbed_motion
00021 //      real hdyn::set_unperturbed_timestep
00022 //      bool hdyn::is_weakly_perturbed
00023 //      bool hdyn::is_stable
00024 
00025 // To do:  Should keep track of tidal errors associated with termination
00026 //         of unperturbed motion.
00027 //
00028 //         Mixing of many types of motion is too complex.  Especially
00029 //         problematic is the identification of pericenter reflection by
00030 //         checking for recession, which can fail in a variety of ways
00031 //         in marginal cases.
00032 //
00033 //         Also, picking up unperturbed motion near pericenter bases the
00034 //         unperturbed timestep on an extremely short step, and can lead
00035 //         to scheduling problems.
00036 
00037 #include "hdyn.h"
00038 #include <star/dstar_to_kira.h>
00039 
00040 #define USE_DT_PERTURBERS               true
00041 
00042 //-------------------------------------------------------------------------
00043 //
00044 // Parameters now contained in hdyn::kira_options:
00045 //
00046 //#define OPTIMIZE_SCHEDULING           true
00047 //
00048 // Define what level of unperturbed motion is permitted by default:
00049 //
00050 //#define ALLOW_UNPERTURBED             true
00051 //
00052 //#define ALLOW_MULTIPLES               false   // "false" here turns off all
00053 //                                              // unperturbed multiple motion
00054 //
00055 //-------------------------------------------------------------------------
00056 
00057 // The following parameters define "unperturbed" motion.
00058 //
00059 // Note that gamma2 is used in determining membership in the perturber
00060 // list.  Note also that d_min_sq does not appear in this file, so the
00061 // criteria for unperturbed motion are independent of the criteria used
00062 // in constructing and maintaining the tree (except that unperturbed
00063 // motion can only occur in low-level nodes, so the binary must first
00064 // be picked up by hdyn_tree).
00065 //
00066 // Where relevant, numbers are tailored to the default gamma = 1.e-7.
00067 //
00068 //-------------------------------------------------------------------------
00069 //
00070 // OUTSIDE_SEMI:  Comparison of orbital separation with semi-major axis for
00071 // full merging.  Criterion is
00072 //
00073 //      SEP_FACTOR * separation > semi-major axis
00074 //
00075 // Macro is used three times, in is_unperturbed_and_approaching(),
00076 // is_stable(), and set_unperturbed_timestep().
00077 //
00078 // SEP_FACTOR is slightly greater than 1 in order to accommodate nearly
00079 // circular binaries
00080 
00081 #define SEP_EPSILON                     0.01    // arbitrary, but small
00082 #define SEP_FACTOR                      (1.0 + SEP_EPSILON)
00083 
00084 #define OUTSIDE_SEMI(sep, sma)          ((SEP_FACTOR)*(sep) > (sma))
00085 
00086 // The comparison is often performed on elements of a kepler structure (k):
00087 
00088 #define KEP_OUTSIDE_SEMI(k) \
00089     ((SEP_FACTOR)*((k).get_separation()) > ((k).get_semi_major_axis()))
00090 
00091 //-------------------------------------------------------------------------
00092 //
00093 // The remaining parameters were originally #defined here, but are
00094 // now part of the hdyn::kira_options class (Steve, 6/00).  Defaults
00095 // are defined in kira_defaults.h.
00096 //
00097 //
00098 // MIN_UNPERT_STEPS:  Minimum number of steps permitted in unperturbed motion.
00099 //
00100 //#define MIN_UNPERT_STEPS              5
00101 //
00102 // FULL_MERGE_TOLERANCE:  Perturbation limit (independent of gamma2,
00103 // as of 1/14/02) at which we permit merging of an entire binary orbit.
00104 // Used once, in function is_unperturbed_and_approaching().
00105 //
00106 //#define FULL_MERGE_TOLERANCE          1e-8
00107 //#define RELAX_FACTOR                  10      // relaxed factor to continue
00108 //
00109 // Allowing unperturbed motion at a greater value of gamma than is
00110 // used in determining neighbors is reasonable since dominant
00111 // perturbative terms are expected to be periodic, and so are
00112 // ineffective over a full orbit period.
00113 //
00114 // Relaxed criterion to continue is probably reasonable (for true
00115 // binaries only), as the first unperturbed step takes them to
00116 // apocenter, where the external perturbation is greater.
00117 //
00118 // PARTIAL_MERGE_FACTOR:  Factor below gamma2 at which we permit
00119 // merging of at least part of an orbit.  Used once, in function
00120 // is_unperturbed_and_approaching().
00121 //
00122 //#define PARTIAL_MERGE_FACTOR          1e-2
00123 //
00124 // FULL_MERGE_TOL_FOR_CLOSE_BINARY:  Absolute limit on perturbation_squared
00125 // (INDEPENDENT of gamma2, note) at which an unperturbed binary is regarded
00126 // as "close."  Used once, in function is_close_pair().
00127 //
00128 //#define FULL_MERGE_TOL_FOR_CLOSE_BINARY       1e-4
00129 //
00130 // The following parameters are used in is_stable() for assessing
00131 // multiple stability.
00132 //
00133 // MULTIPLE_MERGE_TOLERANCE:  Perturnation limit (independnt of gamma2,
00134 // as of 1/14/01) at which we permit merging of an inner binary orbit.
00135 // Defines "weakly perturbed outer orbit."
00136 //
00137 //#define MULTIPLE_MERGE_TOLERANCE              1e-8    // was 1.e-10
00138 //
00139 // UNCONDITIONAL_STABLE_FAC:  Simplest approach is to regard a multiple as
00140 // stable if
00141 //
00142 //      outer periastron / inner semi-major axis > UNCONDITIONAL_STABLE_FAC
00143 //
00144 // for (both) inner binaries.
00145 //
00146 //#define UNCONDITIONAL_STABLE_FAC              5       // was 9
00147 //
00148 // Aarseth and Mardling's criterion is a little more elaborate than a simple
00149 // distance ratio:
00150 //
00151 //      outer periastron / inner semi-major axis
00152 //          > AARSETH_STABLE_FAC * pow((1+q)*(1+e_outer)/sqrt(1-e_outer), 0.4)
00153 //
00154 // where e_outer is the eccentricity of the outer orbit and q is the mass
00155 // ratio m_outer / m_binary.  Note that 1+q = m_total/m_binary.
00156 //
00157 // An empirical inclination factor should also be included in either case.
00158 //
00159 //#define USE_AARSETH_CRITERION         false
00160 //#define AARSETH_STABLE_FAC            2.8
00161 //
00162 // PARTIAL_STABLE_FAC:  Allow inner binary to be unperturbed for part of
00163 // the outer orbit (questionable!) if
00164 //
00165 //      outer periastron / inner semi-major axis > PARTIAL_STABLE_FAC
00166 //
00167 //#define PARTIAL_STABLE_FAC            30      // large to reduce tidal effect
00168 //
00169 // This should really also contain a mass factor...
00170 
00171 //-------------------------------------------------------------------------
00172 
00173 // STATIC flags to keep track of unperturbed motion:
00174 
00175 static int init_binary_type;    // binary_type set by is_unperturbed_and...
00176 static int binary_type;         // eventual binary_type
00177 
00178 #define UNKNOWN_STATUS                  0
00179 #define STABLE_OUTER                    1
00180 #define STABLE_INNER                    2
00181 #define NOT_APPROACHING                 3
00182 #define PERICENTER_REFLECTION           4
00183 #define FULL_MERGER                     5
00184 #define CONTINUE_MERGER                 6
00185 #define PERTURBED                       7
00186 #define MULTIPLE_CM                     8
00187 #define UNPERTURBED_MULTIPLE_COMPONENT  9
00188 #define UNKNOWN_PERTURBERS              10
00189 #define DEFERRED                        11
00190 
00191 static char* bt[12] = {"unknown status",
00192                       "stable multiple outer binary",
00193                       "stable multiple inner binary",
00194                       "components not approaching",
00195                       "pericenter reflection",
00196                       "full binary merger",
00197                       "continue existing merger",
00198                       "perturbed binary",
00199                       "multiple system",
00200                       "unperturbed multiple component",
00201                       "no valid perturber list",
00202                       "unperturbed motion deferred"};
00203 
00204 static int multiple_type;
00205 
00206 #define NOT_MULTIPLE                    0
00207 #define FULL_MULTIPLE                   1
00208 #define APOCENTER_REFLECTION            2
00209 
00210 static char* tt[3] = {"not multiple",
00211                       "full_multiple merger",
00212                       "apocenter reflection"};
00213 
00214 //------------------------------------------------------------------------
00215 
00216 // Local functions:
00217 
00218 local inline real get_energy(hdyn* b, real& separation)
00219 {
00220     // Compute the energy of b relative to its binary sister.
00221 
00222     if (b->is_top_level_node()) return 0;
00223 
00224     hdyn* s = b->get_binary_sister();
00225     separation = abs(b->get_pos() - s->get_pos());
00226 
00227     return 0.5*square(b->get_vel() - s->get_vel())
00228               - b->get_parent()->get_mass() / separation;
00229 }
00230 
00231 local inline real get_semi(hdyn* bcm)
00232 {
00233     // Compute the semi-major axis of the binary daughters of bcm.
00234 
00235     real semi = 0;
00236 
00237     if (bcm->is_parent()) {
00238 
00239         // Get semi-major axis of the orbit.
00240         // Use existing kep if orbit is already unperturbed.
00241 
00242         if (bcm->get_oldest_daughter()->get_kepler()) {
00243             semi = bcm->get_oldest_daughter()->get_kepler()
00244                                              ->get_semi_major_axis();
00245         } else {
00246             real sep;
00247             real energy = get_energy(bcm->get_oldest_daughter(), sep);
00248             semi = -0.5 * bcm->get_mass() / energy;
00249         }
00250     }
00251 
00252     if (semi < 0) semi = -VERY_LARGE_NUMBER;
00253 
00254     return semi;
00255 }
00256 
00257 local inline real get_period(real mass, real sma)
00258 {
00259     // Compute the period of a binary with specified mass and
00260     // semi-major axis.
00261 
00262     return TWO_PI * sqrt(sma*sma*sma / mass);
00263 }
00264 
00265 local inline void print_found_multiple(hdyn* b,
00266                                        bool real_multiple,
00267                                        kepler& outer,
00268                                        real inner_semi1,
00269                                        real inner_semi2,
00270                                        real mass1,
00271                                        real mass2)
00272 {
00273     int p = cerr.precision(HIGH_PRECISION);
00274     cerr << endl;
00275 
00276     if (!real_multiple)
00277         cerr << endl << "impending multiple: ";
00278 
00279     cerr << b->get_parent()->format_label();
00280 
00281     if (real_multiple)
00282         cerr << " is a hierarchical stable multiple";
00283     else
00284         cerr << " is a multiple CM";
00285 
00286     cerr << endl << "    at system time " << b->get_system_time() << endl;
00287     cerr << "    components " << b->format_label();
00288     cerr << " and " << b->get_binary_sister()->format_label();
00289 
00290     if (!real_multiple)
00291         cerr << "; outer orbit is weakly perturbed:" << endl;
00292 
00293     cerr.precision(8);          // nonstandard precision
00294 
00295     cerr << "    perturbation = " << sqrt(b->get_perturbation_squared())
00296          << endl
00297          << "    outer peri   = " << outer.get_periastron()
00298          << "    inner semi   = ";
00299 
00300     if (inner_semi1 > 0) cerr << inner_semi1 << "  ";
00301     if (inner_semi2 > 0) cerr << inner_semi2;
00302 
00303     cerr << endl
00304          << "    outer period = " << outer.get_period()
00305          << "    inner period = ";
00306 
00307     if (inner_semi1 > 0) cerr << get_period(mass1, inner_semi1) << "  ";
00308     if (inner_semi2 > 0) cerr << get_period(mass2, inner_semi2);
00309 
00310     cerr << endl
00311          << "    parent dt = " << b->get_parent()->get_next_time()
00312                                         - b->get_system_time()
00313          << endl;
00314 
00315     cerr.precision(p);
00316 }
00317 
00318 local inline void print_startup_message(hdyn * b, int type, bool new_kep)
00319 {
00320     int p = cerr.precision(HIGH_PRECISION);
00321     cerr << endl;
00322 
00323     if (new_kep)
00324         cerr << "starting new";
00325     else
00326         cerr << "continuing existing";
00327 
00328     cerr << " unperturbed motion for "
00329          << b->format_label();
00330     cerr << " and " << b->get_binary_sister()->format_label();
00331 
00332     if (b->get_slow())
00333         cerr << " (slowdown = " << b->get_kappa() << ")";   // shouldn't occur
00334 //    else
00335 //      cerr << " (no slowdown)";
00336 
00337     cerr << endl;
00338 
00339     cerr << "    at time " << b->get_time()
00340          << "  " << " (system_time = " << b->get_system_time() << ")"
00341          << endl;
00342 
00343     cerr << "    " << bt[type] << " (binary_type = " << type << ")";
00344 
00345     cerr.precision(8);  // nonstandard precision
00346 
00347     if (b->get_kepler())
00348         cerr << "  period = " << b->get_kepler()->get_period();
00349 
00350     cerr << endl;
00351     cerr.precision(p);
00352 
00353     cerr << "    perturbation = " << sqrt(b->get_perturbation_squared());
00354 
00355     if (b->get_parent()->get_nn() && b->get_parent()->get_nn()->is_valid())
00356         cerr << "  (" << b->get_parent()->get_nn()->format_label() << ")";
00357 
00358     cerr << endl;
00359 
00360 #if 0
00361      if (streq(b->format_label(), "xxx"))
00362          b->get_kepler()->print_all();
00363 #endif
00364 
00365 }
00366 
00367 local inline void print_binary_data(hdyn* bi)
00368 {
00369     PRL(bi->get_perturbation_squared());
00370     PRL(bi->get_unperturbed_timestep());
00371     PRL(bi->get_fully_unperturbed());
00372     PRL(bi->get_d_nn_sq());
00373     PRL(bi->get_perturbation_radius_factor());
00374     PRL(bi->find_perturber_node());
00375     if (bi->find_perturber_node())
00376         PRL(bi->find_perturber_node()->get_valid_perturbers());
00377     PRL(bi->get_d_coll_sq());
00378     PRL(bi->get_coll()->format_label());
00379 }
00380 
00381 local inline bool is_multiple(hdyn* b)   // ("multiple" means 3 or more stars)
00382 {
00383     // Return true if low-level node b is one of the outer components
00384     // of a multiple system.
00385 
00386     if (b->is_top_level_node()) return false;
00387     return !( b->is_leaf() && b->get_binary_sister()->is_leaf() );
00388 }
00389 
00390 local inline real dt_overshoot(hdyn *b)
00391 {
00392     // Return the amount by which a binary's unperturbed step may
00393     // overshoot its parent's next step.
00394 
00395     if (!b->get_parent())
00396         return 0;
00397     else
00398         return 0.9 * b->get_parent()->get_timestep();
00399 }
00400 
00401 //----------------------------------------------------------------------
00402 
00403 #include "hdyn_inline.C"        // used only by check_perturbers()
00404                                 // and associated functions
00405 
00406 local inline real dt_perturbers(hdyn *b)
00407 {
00408     // Return the time for any perturber of multiple CM b to exceed
00409     // the threshhold for unperturbed multiple motion,  or -1 if no
00410     // perturber list exists.
00411 
00412     // Effectively replace gamma2 by multiple_merge_tolerance in
00413     // the usual perturbation criterion.
00414 
00415     hdyn *top = b->get_top_level_node();
00416     real t_min = VERY_LARGE_NUMBER;
00417 
00418     if (top->get_valid_perturbers()) {
00419 
00420         real scale = binary_scale(top);
00421         real gamma = sqrt(b->get_kira_options()->multiple_merge_tolerance);
00422 
00423         // Loop over perturbers.
00424 
00425         int np = top->get_n_perturbers();
00426         hdyn **plist = top->get_perturber_list();
00427 
00428         for (int j = 0; j < np; j++) {
00429 
00430             hdyn *p = plist[j];
00431             if (!p->is_top_level_node()) {            // inefficient -- covers
00432                 if (p->get_elder_sister()) continue;  // some components twice
00433                 p = p->get_top_level_node();
00434             }
00435 
00436             // Estimate the time needed for node p to perturb top
00437             // at level gamma.  The critical distance for p to be
00438             // such a perturber is rpert.
00439 
00440             real rpert3 = crit_separation_cubed(top, p->get_mass(),
00441                                                 scale, gamma);
00442 
00443             real rpert = pow(rpert3, 1.0/3);
00444 
00445             // Time estimate is a combination of the free-fall and
00446             // crossing times.
00447 
00448             vector dpos = top->get_pos() - p->get_pos();
00449             vector dvel = top->get_vel() - p->get_vel();
00450             vector dacc = top->get_acc() - p->get_acc();
00451 
00452             real dr = abs(dpos);
00453             real vr = dvel * dpos / dr;
00454             real ar = dacc * dpos / dr;
00455 
00456             real tr = time_to_radius(dr - rpert, vr, ar);
00457 
00458             if (tr < t_min) t_min = tr;
00459             if (tr <= 0) break;
00460         }
00461 
00462     } else
00463 
00464         t_min = -1;
00465 
00466     return t_min;
00467 }
00468 
00469 local void check_perturbers(hdyn *b)
00470 {
00471     // Print the time for any perturber of multiple CM b to exceed
00472     // the threshhold for unperturbed multiple motion.
00473 
00474     real t_min = dt_perturbers(b);
00475 
00476     if (t_min >= 0)
00477 
00478         cerr << "    " << b->get_top_level_node()->get_n_perturbers()
00479              << " perturbers, perturber crossing time = "
00480              << t_min << endl;
00481 
00482     else
00483 
00484         cerr << "    no valid perturber list" << endl;
00485 }
00486 
00487 //----------------------------------------------------------------------
00488 
00489 
00490 
00491 // Modifiers for unperturbed options:
00492 
00493 void set_allow_unperturbed(hdyn *b,
00494                            bool value)  // default = true
00495 {
00496     b->get_kira_options()->allow_unperturbed = value;
00497     if (!b->get_kira_options()->allow_unperturbed)
00498         b->get_kira_options()->allow_multiples = false;
00499 }
00500 
00501 void set_allow_multiples(hdyn *b,
00502                          bool value)    // defualt =true
00503 {
00504     b->get_kira_options()->allow_multiples = value;
00505     if (b->get_kira_options()->allow_multiples)
00506         b->get_kira_options()->allow_unperturbed = true;
00507 }
00508 
00509 void toggle_unperturbed(hdyn *b, int level)
00510 {
00511     if (level == 0)
00512         b->get_kira_options()->allow_unperturbed
00513             = !b->get_kira_options()->allow_unperturbed;
00514     else
00515         b->get_kira_options()->allow_multiples
00516             = !b->get_kira_options()->allow_multiples;
00517 
00518     // Consistency:
00519 
00520     if (!b->get_kira_options()->allow_unperturbed)
00521         b->get_kira_options()->allow_multiples = false;
00522     if (b->get_kira_options()->allow_multiples)
00523         b->get_kira_options()->allow_unperturbed = true;
00524 }
00525 
00526 void print_unperturbed_options(hdyn *b)
00527 {
00528     cerr << "Unperturbed options:  allow_unperturbed = "
00529          << b->get_kira_options()->allow_unperturbed
00530          << ",  allow_multiples = "
00531          << b->get_kira_options()->allow_multiples
00532          << endl;
00533 }
00534 
00535 
00536 
00537 void hdyn::print_unperturbed_binary_params()
00538 {
00539     cerr << "    perturbed timestep for " << format_label()
00540          << " is " << timestep << endl;
00541 
00542     cerr << "    pos*vel = " << pos*vel
00543          << endl
00544          << "    a = " << kep->get_semi_major_axis()
00545          << "  e = " << kep->get_eccentricity()
00546          << "  r = " << kep->get_separation()
00547          << endl
00548          << "    peri = " << kep->get_periastron()
00549          << "  apo = " << kep->get_semi_major_axis()
00550                                 * (1 + kep->get_eccentricity())
00551          << endl
00552          << "    period = " << kep->get_period()
00553          << "  perturbation = " << sqrt(perturbation_squared)
00554          << endl;
00555 
00556     PRI(4); PRL(unperturbed_timestep);
00557 
00558     PRI(4); PRL(get_parent()->get_next_time());
00559     PRI(4); PRL(get_parent()->timestep);
00560     PRI(4); print_nn(get_parent(), 2);
00561 }
00562 
00563 void hdyn::update_dyn_from_kepler()
00564 {
00565     if (diag->unpert_function_id) {
00566         cerr << ">> update_dyn_from_kepler for "
00567              << format_label() << endl;
00568     }
00569 
00570     hdyn *sister = get_binary_sister();
00571 
00572     // Note: if this function is called from integrate_unperturbed_motion,
00573     // time and system_time are already the same.  However, if it is called
00574     // from dissociate_binary, the binary is currently at a retarded time
00575     // and we must predict the kepler to system_time before terminating it.
00576 
00577     if (diag->report_end_unperturbed) {
00578         if (time != system_time) {
00579             cerr << "in update_dyn_from_kepler: ";
00580             PRC(time), PRL(system_time);
00581         }
00582     }
00583 
00584     time = system_time;
00585 
00586     // Update kepler to new time.  Special treatment is needed in the case
00587     // of slow binary motion, as the the kepler structure really describes
00588     // the orbit in tau, not time.  (However, the "time" attached to the
00589     // kepler is the actual time at which the unperturbed motion started.)
00590     // Note that this only works as coded because we are doing pericenter
00591     // reflection, and the kepler structure is about to be deleted.
00592 
00593     if (!slow)
00594         kep->transform_to_time(time);
00595     else
00596         kep->transform_to_time(time - unperturbed_timestep
00597                                         * (1 - 1.0/get_kappa()));
00598 
00599     // Note that the latter expression implies the former, as get_kappa() = 1
00600     // for binaries with slow = NULL, but it seems cleaner this way.
00601 
00602     // Compute dyn components from updated kepler values.
00603 
00604     real factor = sister->mass / (mass + sister->mass);
00605     real sfactor = factor - 1.0;
00606     pos = kep->get_rel_pos() * factor;
00607     vel = kep->get_rel_vel() * factor;
00608     pred_pos = pos;
00609     pred_vel = vel;
00610 
00611     prev_posvel = posvel;
00612     posvel = pos*vel;               // used in is_unperturbed_and_appproaching
00613 
00614     sister->time = time;
00615     sister->pred_pos = kep->get_rel_pos() * sfactor;
00616     sister->pred_vel = kep->get_rel_vel() * sfactor;
00617     sister->pos = kep->get_rel_pos() * sfactor;
00618     sister->vel = kep->get_rel_vel() * sfactor;
00619 
00620     clear_interaction();
00621     calculate_acc_and_jerk(true);       // recomputes perturbation_squared
00622                                         // based on updated pos and vel
00623     store_old_force();
00624 
00625     update_binary_sister(this);
00626 }
00627 
00628 bool hdyn::is_close_pair()
00629 {
00630     if (kep == NULL) return false;
00631     if (kep->get_energy() >= 0) return false;
00632 
00633     real rp = kep->get_periastron();
00634     real sum_radius = radius + get_binary_sister()->radius;
00635 
00636     if (rp < 5 * sum_radius) {
00637 
00638         // This criterion is *very bad*: it should reflect the
00639         // Roche radius...
00640 
00641         // When this test is done, the system is already close
00642         // to apocenter, so perturbation is reasonable.
00643 
00644         if (perturbation_squared
00645                 < options->full_merge_tol_for_close_binary) {
00646 
00647             // cerr << "time = " << system_time << " ";
00648 
00649             // pretty_print_node(cerr);
00650 
00651             // cerr << " close pair criterion ";
00652             // cerr << perturbation_squared << " " ;
00653             // PRC(rp); PRL(sum_radius);
00654 
00655             return true;
00656         }
00657     }
00658     return false;
00659 }
00660 
00661 
00662 
00663 // is_weakly_perturbed:  Return true iff 'this' system is lightly perturbed
00664 //                       and satisfies some other basic acceptance criteria
00665 //                       for the outer binary of an unperturbed multiple
00666 //                       system.
00667 //
00668 //                       New function written by Steve, 8/98.
00669 
00670 static char* wp[11] = {"unknown status",
00671                        "not low-level node",
00672                        "unbound orbit",
00673                        "no perturber node",
00674                        "no valid perturbers",
00675                        "perturbation too large",
00676                        "extended outer orbit",
00677                        "inside semi and perturbed",
00678                        "short parent time step",
00679                        "already uperturbed",
00680                        "weakly perturbed"};
00681 
00682 bool hdyn::is_weakly_perturbed(int& status)
00683 {
00684     if (diag->unpert_function_id) {
00685         cerr << ">> check is_weakly_perturbed for "
00686              << format_label() << " at time " << system_time << endl;
00687     }
00688 
00689     status = 0;
00690 
00691     if (!is_low_level_node()) {
00692         status = 1;
00693         return false;
00694     }
00695 
00696     // 'This' is a component of a binary, possibly a multiple.
00697     // Return true if the binary defined by this and its binary
00698     // sister is weakly perturbed (i.e. not strongly perturbed).
00699 
00700     real separation, energy;
00701 
00702     if ((energy = get_energy(this, separation)) >= 0) {
00703         status = 2;
00704         return false;
00705     }
00706 
00707     hdyn* pnode = find_perturber_node();
00708 
00709 #if 0
00710 
00711     // Old code: Don't proceed until perturber-list structure
00712     //           is properly set.
00713 
00714     if (!pnode) {
00715         status = 3;
00716         return false;
00717     }
00718 
00719 #else
00720 
00721     // New code mimics that for binaries in function
00722     // is_unperturbed_and_approaching().
00723     //                                      (Steve, 4/99)
00724 
00725     if (!pnode) {
00726 
00727         // Possible that a newly formed center of mass node hasn't
00728         // yet taken a step, so no perturber list exists.  However,
00729         // we shouldn't break up an unperturbed system.
00730 
00731         if (kep) {
00732 
00733             // Assume that some reorganization has just taken place in
00734             // the unperturbed multiple system of which 'this' is a
00735             // member, and allow the unperturbed motion to continue.
00736 
00737             status = 9;
00738             return true;
00739 
00740         } else {
00741 
00742             status = 3;
00743             return false;
00744 
00745         }
00746     }
00747 
00748 #endif
00749 
00750     if (!pnode->valid_perturbers) {
00751 
00752         // Really defer multiple motion in this case.
00753 
00754         status = 4;
00755         return false;
00756     }
00757 
00758     // Definition of "not weakly perturbed" (see *** note below):
00759 
00760     if (perturbation_squared > options->multiple_merge_tolerance) {
00761         status = 5;
00762         return false;
00763     }
00764 
00765     // Avoid merging if the outer orbit is too extended.
00766     // Should perhaps make this condition consistent with the
00767     // relax_factor used elsewhere?
00768 
00769     real semi_major_axis = -0.5 * parent->get_mass() / energy;
00770 
00771     if (semi_major_axis > 5 * separation) {       // 5 is ~arbitrary...
00772         status = 6;
00773         return false;
00774     }
00775 
00776     // Require the outer orbit to be outside its semi-major axis,
00777     // or completely unperturbed.
00778 
00779     // Note from Steve (9/00): the OUTSIDE_SEMI requirement here seems
00780     // unnecessary -- better to retain more compact configurations?
00781     // If it is changed, must modify logic in set_unperturbed_timestep
00782     // too.  *** However, use of perturbation_squared above is acceptable
00783     // only if system is outside peri, so maybe OK to keep this as is. ***
00784     // If we change the OUTSIDE_SEMI condition, then should use the
00785     // perturbation normalized to the sma...
00786 
00787     bool is_weakly_pert = false;
00788 
00789     if (OUTSIDE_SEMI(separation, semi_major_axis))
00790         is_weakly_pert = true;
00791     else if (pnode->n_perturbers == 0)
00792         is_weakly_pert = true;
00793 
00794     status = 7;
00795 
00796     if (is_weakly_pert) {
00797 
00798         // The following condition isn't directly related to the strength of
00799         // the perturbation, but may be checked if the binary turns out to
00800         // be stable.  May as well check these here and avoid the stability
00801         // check if it is not needed.
00802 
00803         // The minimum unperturbed step for a multiple is currently the
00804         // outer orbit period.  Return here if that will not be possible,
00805         // given the parent time step.
00806 
00807         real period = get_period(parent->get_mass(), semi_major_axis);
00808         real pdt2 = (real)(get_parent()->get_next_time() - time)
00809                                 + dt_overshoot(this);
00810 
00811         if (period <= pdt2 || USE_DT_PERTURBERS) {      // latter condition
00812                                                         // ==> consider later
00813             status = 10;
00814             return true;
00815 
00816         } else if (diag->report_multiple && diag->unpert_report_level > 0) {
00817 
00818             cerr << endl << "Multiple " << get_parent()->format_label()
00819                  << " is weakly perturbed, but parent time step is too short"
00820                  << endl
00821                  << "    period = " << period << "  pdt2 = " << pdt2
00822                  << "  parent step = " << get_parent()->timestep
00823                  << endl;
00824 
00825             check_perturbers(this);
00826 
00827 #if 0
00828             pp3(get_parent());
00829 
00830             print_binary_from_dyn_pair(this, get_younger_sister());
00831             cerr << endl;
00832             if (is_parent()) {
00833                 print_binary_from_dyn_pair(get_oldest_daughter(),
00834                                            get_oldest_daughter()
00835                                              ->get_younger_sister());
00836                 cerr << endl;
00837             }
00838             if (get_younger_sister()->is_parent()) {
00839                 print_binary_from_dyn_pair(get_younger_sister()
00840                                              ->get_oldest_daughter(),
00841                                            get_younger_sister()
00842                                              ->get_oldest_daughter()
00843                                              ->get_younger_sister());
00844                 cerr << endl;
00845             }
00846 #endif
00847 
00848         }
00849 
00850         status = 8;
00851 
00852     }
00853 
00854     return false;
00855 }
00856 
00857 
00858 
00859 #define NEAR_MULTIPLE_FAC       1.2
00860 
00861 // is_stable:  Return true iff this is a stable system.
00862 //
00863 //             New (recursive) function written by Steve, 8/98.
00864 //
00865 // Note: Apocenter reflection is somewhat suspect... (Steve, 4/99)
00866 
00867 bool hdyn::is_stable(int& status,
00868                      bool top_level)    // default = true
00869 {
00870     if (diag->unpert_function_id) {
00871         cerr << ">> check is_stable for "
00872              << format_label() << endl;
00873     }
00874 
00875     status = 0;
00876 
00877     if (!is_low_level_node()) {
00878         status = 1;
00879         return false;
00880     }
00881 
00882     // 'This' is a component of a binary, possibly a multiple.
00883     // Return true iff the binary defined by this and its binary
00884     // sister is stable.
00885     //
00886     // Unperturbed criteria for this to be a stable system:
00887     //
00888     // (a) the interior motion is stable, meaning that the ratio of
00889     //     the semimajor axis of each inner binary to the pericenter
00890     //     of the outer binary is less than some critical value.
00891     //
00892     // (b) each component is stable: a single star, a binary, or a
00893     //     stable multiple.
00894 
00895     hdyn* sister = get_binary_sister();
00896     if (!sister) {
00897         status = 2;
00898         return false;
00899     }
00900 
00901     // Binaries are stable:
00902 
00903     if (is_leaf() && sister->is_leaf()) {
00904         status = 3;
00905         return true;
00906     }
00907 
00908     // Set up a kepler structure describing the outer orbit.
00909     // Use existing kep if the outer orbit is already unperturbed.
00910 
00911     kepler outerkep;
00912 
00913     if (kep == NULL) {
00914         outerkep.set_time(time);
00915         outerkep.set_total_mass(parent->get_mass());
00916         outerkep.set_rel_pos(pos - sister->pos);
00917         outerkep.set_rel_vel(vel - sister->vel);
00918         outerkep.initialize_from_pos_and_vel();
00919     } else
00920         outerkep = *kep;
00921 
00922     real outer_peri = outerkep.get_periastron();
00923 
00924     real inner_semi1 = get_semi(this);
00925     real inner_semi2 = get_semi(sister);
00926     real inner_semi_sum = inner_semi1 + inner_semi2;
00927 
00928     if (inner_semi_sum < 0) {
00929         status = 4;
00930         return false;                   // unbound inner orbit
00931     } else if (inner_semi_sum == 0) {
00932         status = 5;
00933         return true;                    // binary is stable -- shouldn't happen
00934     }                                   // (already tested above)
00935 
00936     // Check criterion (a), applying the stability criterion to both
00937     // inner binaries, if appropriate.
00938 
00939     real peri_fac = 0;
00940 
00941     if (options->use_aarseth_criterion) {
00942 
00943         real e_outer = outerkep.get_eccentricity();
00944         real total_mass = outerkep.get_total_mass();
00945 
00946         // Apply Aarseth's criterion in the form
00947         //
00948         //    (outer_peri/AARSETH_STABLE_FAC)^5 * (1-e_outer)
00949         //                                      / (total_mass*(1+e_outer))^2
00950         //
00951         //              > semi_major_axis^5 / binary_mass^2
00952 
00953         real binary_fac = 0;
00954 
00955         if (inner_semi1 > 0)
00956             binary_fac = pow(inner_semi1, 5) / pow(mass, 2);
00957 
00958         if (inner_semi2 > 0)
00959             binary_fac = max(binary_fac, pow(inner_semi2, 5)
00960                                                 / pow(sister->mass, 2));
00961 
00962         if (binary_fac > 0)
00963             peri_fac = pow(outer_peri/options->aarseth_stable_fac, 5)
00964                                     * (1-e_outer)
00965                        / (binary_fac * pow(total_mass*(1+e_outer), 2));
00966 
00967     } else {
00968 
00969         // Use a simple distance criterion.  Note that, for binary-binary
00970         // systems, we use inner_semi_sum instead of the individual semi-major
00971         // axes.
00972 
00973         peri_fac = outer_peri / (inner_semi_sum
00974                                    * options->unconditional_stable_fac);
00975 
00976     }
00977 
00978     // NOTE: The above definition (either version) of peri_fac omits an
00979     // inclination factor that effectively reduces the critical outer
00980     // periastron by a factor of ~ 2 for retrograde orbits relative to
00981     // prograde orbits.  We need an additional factor of something like
00982     //
00983     //          (4  -  2 * i / PI) / 4
00984     // or
00985     //          (3 + cos i) / 4
00986     //
00987     // where i is the inclination angle between the inner and outer
00988     // orbits (i = 0 for prograde, PI for retrograde).
00989     //
00990     // Not clear what to do for binary-binary systems.  Assume that the
00991     // inclination of the wider binary is the important quantity.
00992 
00993     hdyn *c = this;
00994     if (inner_semi2 > inner_semi1) c = sister;
00995 
00996     c = c->get_oldest_daughter();
00997 
00998     // Determine the normal vector of the inner orbit (component c).
00999 
01000     real cos_i = 0;
01001 
01002     real dx2 = square(c->get_pos());
01003     real dv2 = square(c->get_vel());
01004 
01005     if (dx2 > 0 && dv2 > 0) {
01006         vector n_inner = c->get_pos() ^ c->get_vel() / sqrt(dx2 * dv2);
01007         cos_i = n_inner * outerkep.get_normal_unit_vector();
01008     }
01009 
01010     if (options->use_aarseth_criterion)
01011         peri_fac *= pow(4 / (3 + cos_i), 5);
01012     else
01013         peri_fac *= 4 / (3 + cos_i);
01014 
01015     bool stable_a = (peri_fac > 1);
01016 
01017     if (stable_a) {
01018 
01019         // Unconditionally stable multiple.  Note that the perturbations
01020         // on the inner binaries are never checked explicitly.
01021 
01022         if (top_level)
01023             multiple_type = FULL_MULTIPLE;
01024 
01025 #if 0
01026         PRC(cos_i);
01027         if (options->use_aarseth_criterion)
01028             PRL(pow(peri_fac, 0.2));
01029         else
01030             PRL(peri_fac);
01031 #endif
01032 
01033     } else {
01034 
01035         // Not fully stable.  Check for partial unperturbed motion.
01036         // NOTE: the validity of this procedure is questionable at best.
01037 
01038         if (top_level && options->partial_stable_fac*inner_semi_sum
01039                                         < outerkep.get_separation()) {
01040 
01041             // Conditionally stable multiple (part of outer orbit only).
01042             // (One of the few explicit xreal casts added by Steve, 5/00.)
01043 
01044             real peri_time = (xreal)outerkep.pred_advance_to_periastron()
01045                                 - time;
01046 
01047             if (peri_time > 0.8 * outerkep.get_period()) {   // 0.8 ~arbitrary
01048 
01049                 // (Note that the above requirement actually means
01050                 //  that the outer orbit is separating...)
01051 
01052                 if (top_level)
01053                     multiple_type = APOCENTER_REFLECTION;
01054 
01055                 stable_a = true;
01056 
01057             } else {
01058 
01059                 if (top_level && diag->report_impending_multiple_status)
01060                     cerr << "Outer orbit " << parent->format_label()
01061                          << " is partially unperturbed but not compact...\n";
01062             }
01063         }
01064     }
01065 
01066     // Check criterion (b).
01067 
01068     bool stable_b = stable_a;
01069 
01070     if (stable_a) {
01071 
01072         int status_1;
01073         if (is_parent())
01074             stable_b &= get_oldest_daughter()->is_stable(status_1, false);
01075 
01076         int status_2;
01077         if (sister->is_parent())
01078             stable_b &= sister->get_oldest_daughter()
01079                               ->is_stable(status_2, false);
01080 
01081         status = 100 + 10*status_1 + status_2;
01082     }
01083 
01084 
01085     // Diagnostic output:
01086 
01087     if (top_level) {
01088 
01089         if (diag->report_impending_multiple_status
01090             && !stable_a
01091             && (options->unconditional_stable_fac*inner_semi_sum // non-Aarseth
01092                         < NEAR_MULTIPLE_FAC*outer_peri           // only
01093                 || options->partial_stable_fac*inner_semi_sum
01094                         < NEAR_MULTIPLE_FAC*outerkep.get_separation())) {
01095 
01096             // Getting close to the stable multiple criterion...
01097 
01098             print_found_multiple(this, false, outerkep,
01099                                  inner_semi1, inner_semi2,
01100                                  mass, sister->mass);
01101         }
01102 
01103         if (stable_b && diag->report_multiple) {
01104 
01105             if (diag->multiple_report_level > 0) {
01106 
01107                 if (multiple_type == FULL_MULTIPLE)
01108                     print_found_multiple(this, true, outerkep,
01109                                          inner_semi1, inner_semi2,
01110                                          mass, sister->mass);
01111                 else if (multiple_type == APOCENTER_REFLECTION) {
01112 
01113                     int p = cerr.precision(HIGH_PRECISION);
01114                     cerr << "\nfound partially stable multiple "
01115                          << parent->format_label()
01116                          << " at time " << time << endl;
01117                     cerr.precision(p);
01118 
01119                     hdyn* pnode = find_perturber_node();
01120                     if (!pnode)
01121                         cerr << "pnode is NULL" << endl;
01122                     else {
01123                         PRC(pnode->format_label());
01124                         PRL(pnode->n_perturbers);
01125                     }
01126 
01127                     cerr << "outer period = " << outerkep.get_period() << endl;
01128                     cerr << "inner period = ";
01129                     if (inner_semi1 > 0)
01130                         cerr << get_period(mass, inner_semi1) << "  ";
01131                     if (inner_semi2 > 0)
01132                         cerr << get_period(sister->mass, inner_semi2);
01133                     cerr << endl;
01134                 }
01135             }
01136         }
01137 
01138         if (stable_a && !stable_b && diag->report_multiple) {
01139             cerr << "\nmultiple " << parent->format_label()
01140                  << " stable, components not, at time " << time
01141                  << endl;
01142         }
01143     }
01144 
01145     return stable_b;
01146 }
01147 
01148 
01149 
01150 bool hdyn::is_unperturbed_and_approaching()
01151 
01152 // Test unperturbed criterion for startup *or* continuation of unperturbed
01153 // motion.  The two are distinguished by the existence of a kepler structure.
01154 // Note that the notion of "unperturbed" is based on the magnitude of the
01155 // perturbation, not on the number of perturbers on the perturber list.
01156 // Note also that the validity of the perturber list is checked in the
01157 // binary case, but not (yet) for multiples.
01158 
01159 // Use of perturbation_squared is OK because the system must also be
01160 // outside its semi-major axis to be accepted for merger.  If we relax this
01161 // requirement, we should also normalize the perturbation to its value at
01162 // a separation equal to the semi-major axis.
01163 
01164 // By construction when this function is called, 'this' is already a
01165 // low-level node, so its binary properties are well defined.
01166 
01167 // **** Note from Steve, 7/99: this function is called at *every* perturbed
01168 // **** step, which seems excessive -- really only need to check immediately
01169 // **** after apocenter.  However, unperturbed periastron passages require
01170 // **** a little more care.  Is there an efficient way to implement this?
01171 
01172 // Function name refers to basic unperturbed criterion for simple binaries.
01173 // For multiples, criterion is considerably more complicated...
01174 
01175 {
01176     if (!options->allow_unperturbed) return false;      // fixes all binary
01177                                                         // problems!
01178 
01179     if (diag->unpert_function_id) {
01180         cerr << ">> check is_unperturbed_and_approaching for "
01181              << format_label() << " at time " << system_time << endl;
01182     }
01183 
01184     init_binary_type = binary_type = UNKNOWN_STATUS;    // should not occur
01185 
01186     // If this function returns true, then binary_type must be one of
01187     // the following:
01188     //
01189     //                  PERICENTER_REFLECTION   // for binaries
01190     //                  FULL_MERGER
01191     //                  CONTINUE_MERGER
01192     //                  UNPERTURBED_MULTIPLE_COMPONENT
01193     //
01194     //                  STABLE_OUTER            // for multiples
01195 
01196     if (is_multiple(this)) {
01197 
01198         init_binary_type = binary_type = MULTIPLE_CM;
01199 
01200         if (!options->allow_multiples) return false;    // fixes all multiple
01201                                                         // problems!!
01202 
01203         // Unperturbed criteria for this to be a stable system:
01204         //
01205         // (1) the outer binary is bound and stable, meaning that the
01206         //     perturbation is small; in addition, require that the
01207         //     current orbital phase be outside the semimajor axis.
01208         //
01209         // (2) the interior motion is stable, meaning that the ratio of
01210         //     the semimajor axis of the (or each) inner binary to the
01211         //     pericenter of the outer binary is less than some critical
01212         //     value.
01213         //
01214         // (3) each component is stable: a single star, a binary, or a
01215         //     stable multiple.
01216 
01217         multiple_type = NOT_MULTIPLE;
01218         int weak_stat;
01219 
01220         if (is_weakly_perturbed(weak_stat)) {
01221 
01222 #if 0
01223             cerr << endl
01224                  << format_label()
01225                  << " is weakly perturbed at time " << time
01226                  << "  perturbation = " << sqrt(perturbation_squared)
01227                  << endl;
01228 #endif
01229 
01230             int stable_stat;
01231             if (is_stable(stable_stat)) {
01232 
01233                 init_binary_type = binary_type = STABLE_OUTER;
01234                 return true;
01235 
01236                 // Note that multiples are detected only when the outer orbit
01237                 // is advanced.  Up to the point of multiple recognition,
01238                 // inner binaries are subject to the usual (more stringent)
01239                 // unperturbed criterion.  With this convention, 'this' is
01240                 // always one of the outer components of the multiple system.
01241 
01242             }
01243 
01244         } else {
01245 
01246 #if 0
01247             cerr << endl
01248                  << format_label() << " is not weakly perturbed at time "
01249                  << time
01250                  << endl
01251                  << "perturbation = " << sqrt(perturbation_squared)
01252                  << "  status = " << weak_stat
01253                  << " (" << wp[weak_stat] << ")"
01254                  << endl;
01255 #endif
01256 
01257         }
01258 
01259     } else {
01260 
01261         // Rest of function applies only to binaries.
01262 
01263         if (slow && slow->get_stop()) {
01264 
01265             // Slow binary is scheduled for termination, presumably because
01266             // we have already checked and expect full unperturbed motion to
01267             // start soon.  No need to continue checking in this case.
01268             // Return simply as a perturbed binary.
01269 
01270             // if (streq(format_label(), "100a"))
01271             //     cerr << "100a false slow stop..." << endl;
01272 
01273             init_binary_type = binary_type = PERTURBED;
01274             return false;
01275         }
01276 
01277         // The inner component of an unperturbed multiple must always
01278         // be regarded as unperturbed.  A side effect of this criterion
01279         // is that unperturbed multiples always become perturbed from
01280         // the top down.
01281 
01282         if (get_parent()->get_kepler()) {
01283 
01284             // Parent node is unperturbed.
01285 
01286             // if (streq(format_label(), "100a"))
01287             //     cerr << "100a true unpert par..." << endl;
01288 
01289             init_binary_type = binary_type = UNPERTURBED_MULTIPLE_COMPONENT;
01290             return true;
01291         }
01292 
01293         bool approaching = (posvel < 0);        // posvel is recomputed at the
01294                                                 // end of every perturbed step
01295 
01296         if (!approaching && !get_kepler() && !slow) {
01297 
01298             // Accept nearly circular perturbed binaries even if they are
01299             // separating slowly.  Unperturbed binaries will by construction
01300             // be approaching for fully unperturbed orbits, and receding
01301             // for pericenter reflection, so we shouldn't need to deal with
01302             // ambiguous cases.
01303 
01304             // Don't accept slow binaries in this case, as there is no
01305             // possibility of promotion to full unperturbed motion and
01306             // periastron reflection in a receding orbit may lead to
01307             // problems.
01308 
01309             // ***** Still do this at every outgoing step... (Steve, 7/99)
01310             // Work with squares to avoid sqrt() hidden in vector abs().
01311             // Possibly can use additional time criterion, as in slow binary
01312             // motion...  However, this doesn't seem to be a significant time
01313             // sink in the current code (Steve, 9/99).
01314 
01315             approaching = (posvel*posvel < 1.e-6 * square(pos)*square(vel));
01316         }
01317 
01318         if (!approaching) {
01319 
01320             // if (streq(format_label(), "100a"))
01321             //     cerr << "100a false not appr at " << get_time() << endl;
01322 
01323             init_binary_type = binary_type = NOT_APPROACHING;
01324             return false;
01325         }
01326 
01327         if (!find_perturber_node()) {
01328 
01329             // Possible that a newly formed center of mass node hasn't
01330             // yet taken a step, so no perturber list exists.  However,
01331             // we don't want to break up an unperturbed binary because
01332             // of this.  Set binary_type appropriately and return true
01333             // if this is already unperturbed.
01334 
01335             init_binary_type = binary_type = UNKNOWN_PERTURBERS;
01336 
01337             if (kep) {
01338 
01339                 // Binary is already unperturbed; call presumably came
01340                 // from integrate_unperturbed_motion().  Assume that some
01341                 // reorganization has just taken place in the multiple
01342                 // system of which the binary is a member, and allow the
01343                 // unperturbed motion to continue for now.
01344 
01345                 return true;
01346 
01347             } else {
01348 
01349                 // Binary is perturbed; call presumably came from kira
01350                 // after the relative motion was computed.  In this
01351                 // case, the perturbation should still be valid.
01352                 // Continue checking for unperturbed motion after a
01353                 // rudimentary consistency check.
01354 
01355                 if (perturbation_squared < 0 || perturbation_squared > 1)
01356                     return false;
01357 
01358             }
01359         }
01360 
01361         if (!get_kepler()
01362             && options->optimize_scheduling
01363             && !options->optimize_block) {
01364 
01365             // Only consider new unperturbed motion at certain phases
01366             // of the timestep cycle.
01367 
01368             int it = (int) get_system_time();
01369             real tt = get_system_time() - it;   // try to avoid overflow in it
01370             real dtt = timestep/get_kappa();    // true timestep
01371             it =(int)(tt/dtt + 0.1);            // should be a power of 2...
01372 
01373             // Arbitrarily require that it be a multiple of 4 (so we are
01374             // synchronized with steps two blocks up).  This may not really
01375             // be necessary, given the synchronization strategy used below.
01376 
01377             if (it%4 != 0) {
01378                 init_binary_type = binary_type = DEFERRED;
01379                 return false;
01380             }
01381         }
01382 
01383         // System is not a multiple, the components are "approaching," by
01384         // the above definition, and the perturbation is to be trusted.
01385 
01386         // Criterion is based on the real perturbation, not the slow-binary
01387         // version.  The value of perturbation_squared does *not* contain the
01388         // slowdown factor.
01389 
01390         // if (streq(format_label(), "100a"))
01391         //     cerr << "100a perturbation = " << perturbation_squared << endl;
01392 
01393         if ((perturbation_squared
01394                 < gamma2 * options->partial_merge_factor)
01395             && is_low_level_leaf()
01396             && younger_sister->is_low_level_leaf()) {
01397 
01398             // Sufficient to pick up the binary here as partly unperturbed;
01399             // partial merger may be promoted to full merger later.
01400 
01401             // if (streq(format_label(), "100a"))
01402             //     cerr << "100a true peri refl..." << endl;
01403 
01404             // PRC(perturbation_squared);
01405             // PRL(gamma2 * options->partial_merge_factor);
01406 
01407             init_binary_type = binary_type = PERICENTER_REFLECTION;
01408             return true;
01409 
01410         } else {
01411 
01412             real pert_fac = 1;
01413 
01414             if (!kep) {
01415 
01416                 // Use of perturbation_squared is OK here because we must
01417                 // be outside semi for full merger...  (Alternatively,
01418                 // we could always normalize the perturbation to separation
01419                 // equal to semi.)
01420 
01421                 if ((perturbation_squared < options->full_merge_tolerance)
01422                     && is_low_level_leaf()
01423                     && younger_sister->is_low_level_leaf()) {
01424 
01425                     // if (streq(format_label(), "100a"))
01426                     //     cerr << "checking 100a..." << endl;
01427 
01428                     // Note that the relevant "timestep" is the step in the
01429                     // absence of slow motion (i.e. when unperturbed motion
01430                     // would actually start).
01431 
01432                     // if (streq(format_label(), "100a")) {
01433                     //     PRL(get_parent()->timestep);
01434                     //     PRL(10 * timestep/get_kappa());
01435                     // }
01436 
01437                     // Not entirely clear what good this timestep
01438                     // limit does... (Steve, 9/00)
01439 
01440                     if (get_parent()->timestep
01441                           > 10 * timestep/get_kappa()) {  // 10 is ~arbitrary
01442 
01443                         kepler kepl;
01444                         hdyn *sister = get_binary_sister();
01445 
01446                         kepl.set_time(time);
01447                         kepl.set_total_mass(parent->get_mass());
01448                         kepl.set_rel_pos(pos - sister->pos);
01449                         kepl.set_rel_vel(vel - sister->vel);
01450                         kepl.initialize_from_pos_and_vel();
01451 
01452                         // if (streq(format_label(), "100a")) {
01453                         //      cerr << "100a kepl..." << endl;
01454                         //      PRL(kepl.get_energy());
01455                         //      PRL(KEP_OUTSIDE_SEMI(kepl));
01456                         //      PRL(get_parent()->get_next_time() - time);
01457                         //      PRL(kepl.get_period());
01458                         // }
01459 
01460                         if (kepl.get_energy() < 0.0) {
01461 
01462                             if (KEP_OUTSIDE_SEMI(kepl)) {  // necessary if we
01463                                                            // use the current
01464                                                            // perturbation...
01465 
01466                                 // Include assumption that the parent timestep
01467                                 // will increase by at least a factor of two
01468                                 // once unperturbed motion starts (see note
01469                                 // below).
01470 
01471                                 real dtp = get_parent()->get_next_time()
01472                                                 - time;
01473                                 if (!slow) dtp += get_parent()->get_timestep();
01474 
01475                                 // Require the period of a simple unperturbed
01476                                 // binary to fit into ~1 parent timestep.  For
01477                                 // multiples, this condition may be modified.
01478 
01479                                 if (kepl.get_period() < dtp) {
01480 
01481                                     // Eligible for full unperturbed motion.
01482                                     // Defer only if slow set.
01483 
01484                                     // Note from Steve (8/99).  This may lead to
01485                                     // a "race" condition in the case of slow
01486                                     // binary motion, as the parent time step in
01487                                     // the slow case will generally be longer
01488                                     // than in the normal case.  Thus, the slow
01489                                     // motion may think there is enough time to
01490                                     // allow unperturbed motion to start, but
01491                                     // this condition may not be met once normal
01492                                     // motion resumes.  The same issue exists
01493                                     // when unperturbed motion starts, as the
01494                                     // center of mass timestep will in general
01495                                     // increase significantly.  May be time to
01496                                     // reconsider the use of the parent time
01497                                     // step as a limiting factor here and below.
01498 
01499                                     // Don't permit a direct transition to
01500                                     // unperturbed motion for slow binaries,
01501                                     // as the slow motion has to end at the
01502                                     // proper phase in the orbit.  Present
01503                                     // strategy is to flag the slow motion
01504                                     // for termination, after which unperturbed
01505                                     // motion may begin normally.
01506 
01507                                     if (slow) {
01508 
01509                                         if (!slow->get_stop()) {
01510                                             cerr << endl
01511                                             << "is_unperturbed_and_approaching:"
01512                                             << " scheduling end of slow motion"
01513                                             << endl
01514                                             << "                               "
01515                                             << " for " << format_label()
01516                                             << " at time " << get_system_time()
01517                                             << endl;
01518 
01519                                             slow->set_stop();
01520                                         }
01521 
01522                                     } else {
01523 
01524                                         // PRC(perturbation_squared);
01525                                         // PRL(options->full_merge_tolerance);
01526 
01527                                         init_binary_type = binary_type
01528                                                          = FULL_MERGER;
01529 
01530                                         // Ready to return true, but must still
01531                                         // check perturbation at apocenter, to
01532                                         // avoid immediate termination of
01533                                         // unperturbed motion after the first
01534                                         // step, and repetition of this cycle
01535                                         // (which leads to systematic errors).
01536 
01537                                         // Factor by which to increase
01538                                         // perturbation for apocenter
01539                                         // comparison.
01540 
01541                                         pert_fac = pow(kepl.get_apastron()
01542                                                        / kepl.get_separation(),
01543                                                        6);
01544 
01545                                         // return true;
01546 
01547                                     }
01548                                 }
01549                             }
01550                         }
01551                     }
01552                 }
01553 
01554             }
01555 
01556             if (kep || binary_type == FULL_MERGER) {
01557 
01558                 // Note relaxed criterion for continuing unperturbed
01559                 // binary (but *not* multiple) motion.
01560 
01561                 real crit_pert2 = options->full_merge_tolerance
01562                                          * options->relax_factor;
01563 
01564                 // PRC(pert_fac*perturbation_squared); PRL(crit_pert2);
01565 
01566                 if (pert_fac*perturbation_squared < crit_pert2
01567                     || is_close_pair()) {
01568 
01569                     if (kep)
01570                         init_binary_type = binary_type = CONTINUE_MERGER;
01571 
01572                     return true;
01573 
01574                 }
01575             }
01576         }
01577     }
01578 
01579     // if (streq(format_label(), "100a"))
01580     //     cerr << "100a false pert at time " << get_time() << endl;
01581 
01582     init_binary_type = binary_type = PERTURBED;
01583     return false;
01584 }
01585 
01586 
01587 
01588 // startup_unperturbed_motion: only invoked if is_unperturbed_and_approaching
01589 //                             returns true...
01590 
01591 void hdyn::startup_unperturbed_motion()
01592 {
01593     // Begin treatment of unperturbed motion.  The main actions of this
01594     // function are (1) to create kepler structure(s) if necessary, and
01595     // (2) to determine the unperturbed time step(s).  May also modify
01596     // binary_type.
01597 
01598     // Note: In the case of unperturbed multiple motion, 'this' is one
01599     // of the outer components (see is_unperturbed_and_approaching).
01600 
01601     if (diag->unpert_function_id) {
01602         cerr << endl << ">> startup_unperturbed_motion for "
01603              << format_label() << " at time " << system_time << endl;
01604     }
01605 
01606     bool new_unpert = true;
01607     if (get_kepler()) new_unpert = false;  // may happen if this is the inner
01608                                            // component of a multiple system
01609 
01610     update_kepler_from_hdyn();  // creates a kepler structure if none exists...
01611 
01612     fully_unperturbed = false;  // may be set true in set_unperturbed_timestep()
01613 
01614     if (diag->report_start_unperturbed) {
01615         if (binary_type != PERICENTER_REFLECTION
01616             || diag->report_pericenter_reflection) {
01617             print_startup_message(this, binary_type, new_unpert);
01618         }
01619     }
01620 
01621     // Use of save_binary_type here may be somewhat redundant, since
01622     // init_binary_type seems to contain the same information...
01623 
01624     int save_binary_type = binary_type;  // from is_unperturbed_and_approaching
01625 
01626     // Some care is needed with slow binary motion.  If the binary will
01627     // simply be reflected around pericenter, we will likely want to
01628     // continue the slow motion afterwards, so we don't want to discard
01629     // the slow data structures.  However, the reflection must be done
01630     // in tau, while set_unperturbed_timestep() works with the "real"
01631     // binary period, so we need to replace timestep by dtau when
01632     // computing steps.  If the binary would normally have been
01633     // "promoted" to fully unperturbed motion, we probably want to defer
01634     // the full startup, continue with pericenter reflection only, and
01635     // schedule the slow motion for termination at the proper point in
01636     // the orbit.
01637 
01638     // Temporarily replace timestep by tau in case of slow motion.
01639 
01640     if (slow) timestep /= get_kappa();
01641 
01642     // Set_unperturbed_timestep will *not* promote to fully unperturbed
01643     // motion if slow is set, but instead will schedule the slow motion
01644     // for termination.
01645 
01646     // Used to be an int, changed to real because integers have too
01647     // small a range (SPZ:02/1998):
01648 
01649     real steps = set_unperturbed_timestep(true);    // unperturbed timestep
01650                                                     // will be set equal to
01651                                                     // timestep * steps, in
01652                                                     // *all* cases
01653 
01654     // Restore timestep in case of slow motion.
01655 
01656     if (slow) timestep *= get_kappa();
01657 
01658     // Note that we may run into precision problems if we promote a very
01659     // eccentric binary from pericenter reflection to full merger, as
01660     // timestep may be very short, making 'steps' very large.
01661 
01662     if (diag->report_start_unperturbed && binary_type != save_binary_type) {
01663 
01664         if (save_binary_type == PERICENTER_REFLECTION
01665             && !diag->report_pericenter_reflection)
01666             print_startup_message(this, save_binary_type, new_unpert);
01667 
01668         cerr << "    binary_type changed to " << binary_type
01669              << ":  " << bt[binary_type] << endl;
01670 
01671     }
01672 
01673     // If steps = 0, there is no reason to start unperturbed motion.
01674     // Moreover, if steps is 0, program goes into an infinite loop, so
01675     // discard the kepler structure and return.
01676 
01677     // Avoid kepler if steps is less than 5 or so, in order to avoid
01678     // unnecessary roundoff due to kepler conversion.
01679 
01680     if (steps < options->min_unpert_steps) {
01681 
01682         if (diag->report_start_unperturbed
01683             || (steps <= 0 && diag->report_zero_unpert_steps)) {
01684             cerr << "\nstartup_unperturbed_motion:  calculated step size = "
01685                  << steps << endl
01686                  << "do not apply unperturbed motion to " << format_label()
01687                  << " at time " << system_time << "\n";
01688         }
01689 
01690         delete kep;
01691         kep = NULL;
01692         unperturbed_timestep = -VERY_LARGE_NUMBER;
01693         get_binary_sister()->unperturbed_timestep = -VERY_LARGE_NUMBER;
01694         get_binary_sister()->kep = NULL;
01695 
01696         if (slow)
01697             slow->set_stop(false);
01698 
01699         return;
01700     }
01701 
01702     unperturbed_timestep = timestep * steps;
01703     get_binary_sister()->unperturbed_timestep = unperturbed_timestep;
01704 
01705     // More diagnostics.
01706 
01707     if (diag->report_start_unperturbed) {
01708         if (binary_type != PERICENTER_REFLECTION
01709             || diag->report_pericenter_reflection) {
01710 
01711             cerr << "    dt = " << timestep
01712                  << "  dt_unpert = " << unperturbed_timestep << endl;
01713 
01714             if (diag->unpert_report_level > 0)
01715                 print_unperturbed_binary_params();
01716 
01717         }
01718     }
01719 
01720     // If this is the outer binary of a multiple, the inner component has
01721     // already been synchronized (in kira); make it unperturbed too.
01722 
01723     if (is_multiple(this)) {
01724 
01725         binary_type = STABLE_INNER;     // for startup of inner component
01726 
01727         if (is_parent())
01728             get_oldest_daughter()
01729                 ->startup_unperturbed_motion();
01730 
01731         if (get_binary_sister()->is_parent())
01732             get_binary_sister()->get_oldest_daughter()
01733                                ->startup_unperturbed_motion();
01734     }
01735 
01736     // Finally, replace fully unperturbed components by CM on perturber
01737     // lists, if necessary.
01738 
01739     if (!RESOLVE_UNPERTURBED_PERTURBERS && fully_unperturbed) {
01740 
01741         // Make a list of leaves or unperturbed nodes below parent.
01742 
01743         hdynptr *cpt_list = new hdynptr[2*n_leaves()];
01744         int nl = 0;
01745 
01746         // Don't know which nodes or leaves might be on others' lists,
01747         // so simply list all possibilities.
01748 
01749         for_all_nodes(hdyn, get_parent(), bb)
01750             if (bb != get_parent()) cpt_list[nl++] = bb;
01751 
01752         correct_perturber_lists(get_root(), cpt_list, nl, get_parent());
01753         delete [] cpt_list;
01754     }
01755 
01756     // cerr << "Leaving startup_unperturbed_motion: "; PRL(fully_unperturbed);
01757 
01758     // On normal exit, a kepler structure exists, timestep is the last
01759     // perturbed timestep, suitable for restart if no orbital evolution
01760     // occurs, and unperturbed_timestep is an integral number of timesteps.
01761     // The hdyn is frozen at the instant at which unperturbed motion was
01762     // started or continued.  It is updated at each unperturbed step.
01763 
01764     // *** The only difference between pericenter reflection and full ***
01765     // *** unperturbed motion is the value of fully_unperturbed.      ***
01766 
01767     // For unperturbed multiple motion, each unperturbed component is
01768     // scheduled and updated separately.  Synchronization is maintained
01769     // by choice of time steps, but update times will in general *not*
01770     // be the same for all levels.
01771 
01772 #if 0
01773     putrq(get_parent()->get_dyn_story(), "unpert_startup_time",
01774           get_system_time());
01775     int ifull = fully_unperturbed;
01776     putiq(get_parent()->get_dyn_story(), "fully_unperturbed", ifull);
01777 #endif
01778 
01779 }
01780 
01781 
01782 
01783 real hdyn::set_unperturbed_timestep(bool check_phase)   // no default
01784 
01785 // Use of the argument check_phase:
01786 //
01787 // Argument check_phase = true indicates that we should check that the
01788 // system is outside its semi-major axis (using KEP_OUTSIDE_SEMI)
01789 // before applying full merging.
01790 //
01791 // This function is called only from startup_unperturbed_motion() and
01792 // integrate_unperturbed_motion().
01793 //
01794 // Argument check_phase is set true when this function is called from
01795 // startup_unperturbed_motion().  It is set to !force_time when the
01796 // call comes from integrate_unperturbed_motion().  The default value
01797 // for force_time is false (kira call uses this default).  It is
01798 // generally undesirable to set force_time to be true; however, it may
01799 // be set true by a call from integrate_node() if its arguments
01800 // integrate_unperturbed_motion and force_unperturbed_time are both
01801 // true (default: true, false).  Integrate_node() seems only to be
01802 // called from synchronize_node(), with values false and true.
01803 //
01804 // *** Thus, it appears that check_phase is *always* true when this
01805 // *** function is called!  We thus promote pericenter reflection to
01806 // *** full merger only when the orbit is "near" apocenter.  Hmmm...
01807 
01808 {
01809     // Determine and set the unperturbed timestep.  Zero return value
01810     // means no unperturbed motion.
01811     //
01812     // *** May change the value of binary_type. ***
01813     //
01814     // On arrival in this function, binary_type must be one of the
01815     // "true" values set by is_unperturbed_and_approaching.  The
01816     // MULTIPLE values get special treatment.  The "binary" values,
01817     // in order of *increasing* external perturbation, are:
01818     //
01819     //          PERICENTER_REFLECTION
01820     //          FULL_MERGER
01821     //          CONTINUE_MERGER
01822     //          UNPERTURBED_MULTIPLE_COMPONENT
01823     //
01824     // Since the binary has already been deemed "unperturbed", an
01825     // unperturbed step should be taken if possible, even if it
01826     // extends beyond the parent step.
01827 
01828     if (diag->unpert_function_id) {
01829         cerr << ">> set_unperturbed_timestep for "
01830              << format_label() << endl;
01831     }
01832 
01833     if (!check_phase) {
01834         // cerr << "set_unpert_timestep called with no test phase\n";
01835         // PRL(kep->get_energy());
01836     }
01837 
01838     hdyn * sister = get_binary_sister();
01839     real steps = 0;
01840 
01841     // Establish "default" outcome.
01842 
01843     if (is_multiple(this)) {
01844 
01845         // No default -- unperturbed motion does not occur if the
01846         // criteria in get_unperturbed_steps() are not met.
01847 
01848     } else {
01849 
01850         // Default is periastron reflection.  Determine the number of
01851         // perturbed steps to the reflection point.
01852 
01853         binary_type = PERICENTER_REFLECTION;
01854 
01855         // real peri_time = kep->pred_advance_to_periastron() - time;
01856 
01857         // Old code used pred_advance_to_periastron(), but this may be quite
01858         // inaccurate.  Better to use mean motion.  (Steve, 4/99)
01859 
01860         real mean_anomaly = kep->get_mean_anomaly();
01861         if (kep->get_energy() < 0)
01862             mean_anomaly = sym_angle(mean_anomaly);     // place in (-PI, PI]
01863 
01864         if (mean_anomaly >= 0) {
01865 
01866             // Note that we always expect mean_anomaly < 0, so this
01867             // should never happen:
01868 
01869             steps = 0;
01870 
01871         } else {
01872 
01873             real peri_time = -mean_anomaly / kep->get_mean_motion();
01874             steps = ceil(2 * peri_time / timestep);
01875 
01876             // Recall that ceil rounds to the next integer up, so we
01877             // overshoot slightly.  We *don't want to do this for slow
01878             // binaries, as we must terminate (components receding) at
01879             // the end of the step.
01880 
01881             if (slow) steps -= 1;
01882 
01883         }
01884     }
01885 
01886     if (kep->get_energy() < 0) {
01887 
01888         // Consider the possibility of complete merging in a bound orbit.
01889 
01890         if (is_multiple(this) && multiple_type == APOCENTER_REFLECTION) {
01891 
01892             // Determine if we are close to apocenter in a multiple.
01893 
01894             // Note: pred_advance_to_periastron() may not yield a very
01895             // accurate time...
01896             // (One of the few explicit xreal casts added by Steve, 5/00.)
01897 
01898             real apo_time = (xreal)kep->pred_advance_to_periastron()
01899                                   - time - (xreal)(0.5*kep->get_period());
01900             real steps = floor(((2 * apo_time) / timestep));
01901 
01902             // Note: "floor" here means the separation at the end of
01903             //        the step is slightly greater than at the start.
01904 
01905             if (diag->report_multiple) {
01906                 cerr << "multiple (" << tt[multiple_type] << "): "
01907                      << format_label() << endl;
01908                 PRC(apo_time), PRC(kep->get_period()); PRL(steps);
01909             }
01910 
01911             binary_type = STABLE_OUTER;
01912 
01913         } else if (!check_phase
01914                    || KEP_OUTSIDE_SEMI(*kep)         // also picks up multiple
01915                    || (get_parent()->kep != NULL)) {
01916 
01917             // Full merger criteria:
01918             //     (1) not checking phase (discouraged), or
01919             //     (2) system is outside its semi-major axis, or
01920             //     (3) parent is already unperturbed.
01921 
01922             // At this point, we would normally promote the binary to fully
01923             // unperturbed motion.  However, if slow motion is in progress,
01924             // don't allow that, but schedule the slow motion for termination
01925             // (to catch the promotion next time around).
01926 
01927             if (slow) {
01928 
01929                 if (!slow->get_stop()) {
01930                     cerr << "set_unperturbed_timestep (#1): "
01931                          << "scheduling end of slow motion"
01932                          << endl
01933                          << "                               for "
01934                          << format_label()
01935                          << " at time " << get_system_time() << endl;
01936                     slow->set_stop();
01937                 }
01938 
01939                 // See note on the use of the parent time step in function
01940                 // is_unperturbed_and_approaching().
01941 
01942             } else {
01943 
01944                 // The criteria in get_unperturbed_steps() will return zero
01945                 // if it is not possible to fit an integral number of orbits
01946                 // or an advance to apastron into the time allowed by the
01947                 // parent step.  If usteps <= 0, this must mean that the timing
01948                 // of the parent step is the problem.  In that case, we fall
01949                 // back to pericenter reflection.  However, if that occurs
01950                 // near apocenter (as it will if the orbit had previously been
01951                 // advanced to apastron), we convert it to a complete orbit.
01952 
01953                 // If usteps > 0, for a true binary, we "overshoot" slightly,
01954                 // ending at the next perturbed step past apocenter.  For a
01955                 // multiple, we don't proceed to apocenter.  Instead, we
01956                 // require an integral number of orbits ending just before
01957                 // (i.e. at separation outside) a complete period multiple.
01958 
01959                 // Note that this is one of only two places in kira where
01960                 // get_unperturbed_steps() is called.  (The other is in
01961                 // util/hdyn_kepler.C.)
01962 
01963                 real usteps = get_unperturbed_steps(!is_multiple(this));
01964 
01965                 // Unperturbed timestep will be set equal to timestep * usteps
01966                 // unless pericenter reflection is promoted to full merger.
01967 
01968                 // PRL(usteps);
01969 
01970                 if (usteps <= 0) {
01971 
01972                     if (diag->report_zero_unpert_steps) {
01973 
01974                         cerr << endl << "    set_unperturbed_timestep: "
01975                              << "zero step for unperturbed binary\n";
01976 
01977                         int p = cerr.precision(HIGH_PRECISION);
01978                         PRI(4); PRL(time);
01979                         cerr << "    parent: " << get_parent()->format_label()
01980                              << endl;
01981                         PRI(4); PRL((get_parent()->get_next_time()));
01982                         PRI(4); PRL(kep->get_period());
01983                         PRI(4); PRL(usteps);
01984                         cerr.precision(p);
01985                     }
01986 
01987                     // Note that steps and binary_type are left unchanged.
01988 
01989                 } else {
01990 
01991                     steps = usteps;
01992                     binary_type = FULL_MERGER;  // may be changed from
01993                                                 // CONTINUE_UNPERTURBED
01994                     fully_unperturbed = true;
01995 
01996                 }
01997             }
01998         }
01999 
02000         // Now steps is the number of perturbed time steps to the reflection
02001         // point (rounded to the nearest integer), or the number of perturbed
02002         // steps corresponding to an integral number of orbits (plus mapping
02003         // to apocenter for true binaries) if full merging is applied.  Note
02004         // that, in either case, the resulting unperturbed step is *not*
02005         // necessarily a power of 2, but it does retain the binary's place
02006         // in the block scheduling scheme if and when perturbed motion resumes.
02007 
02008         // ** Could improve scheduling by using time step near apocenter, not
02009         // ** promoting pericenter reflection to full unperturbed motion, and
02010         // ** restricting unperturbed motion to a power of two perturbed steps.
02011 
02012         // Special case occurs if the reflection is occurring near apocenter.
02013         // In this case, promote the step to a full merger for a single orbit.
02014 
02015         // Hmmm... Not altogether clear why we consider this separately here...
02016         //                                                      (Steve, 8/99)
02017 
02018         if (binary_type == PERICENTER_REFLECTION && KEP_OUTSIDE_SEMI(*kep)) {
02019 
02020             // Same treatment of slow binaries as above.
02021 
02022 #if 0
02023             // Flag this to see if it ever occurs (Steve, 9/00).
02024 
02025             cerr << endl
02026                  << "**** binary_type == PERICENTER_REFLECTION "
02027                  << "&& KEP_OUTSIDE_SEMI(*kep) ****"
02028                  << endl << endl;
02029 #endif
02030 
02031             if (slow) {
02032 
02033                 if (!slow->get_stop()) {
02034                     cerr << "set_unperturbed_timestep (#2): "
02035                          << "scheduling end of slow motion"
02036                          << endl
02037                          << "                               for "
02038                          << format_label()
02039                          << " at time " << get_system_time() << endl;
02040                     slow->set_stop();
02041                 }
02042 
02043                 // See note on the use of the parent time step in function
02044                 // is_unperturbed_and_approaching().
02045 
02046             } else {
02047                 steps = ceil(kep->get_period()/timestep);
02048                 binary_type = FULL_MERGER;
02049                 fully_unperturbed = true;
02050             }
02051         }
02052     }
02053 
02054     // Redundant comment: for slow binary motion, timestep is temporarily
02055     // dtau, so multiply unperturbed_timestep by kappa here (value is
02056     // actually overwritten in startup_unperturbed_motion anyway...).
02057 
02058     unperturbed_timestep = timestep * steps;
02059     if (slow) unperturbed_timestep *= get_kappa();
02060 
02061     sister->kep = kep;
02062     sister->unperturbed_timestep = unperturbed_timestep;
02063     sister->fully_unperturbed = fully_unperturbed;
02064 
02065     if (steps < 0) {
02066         cerr << endl
02067              << "set_unperturbed_timestep: steps negative!"
02068              << " -- the code will break soon...;-("
02069              << endl;
02070         PRC(timestep); PRC(steps); PRL(unperturbed_timestep);
02071         kep->print_all();
02072     }
02073 
02074     return steps;
02075 }
02076 
02077 
02078 
02079 local xreal latest_time(xreal t_min, xreal t_max, real dtblock,
02080                         real ma_min, real ma_max,
02081                         real mean_anomaly, real mean_motion)
02082 {
02083     // Determine the latest time (integer * dtblock) that lies between
02084     // t_min and t_max and has mean anomaly in the range (ma_min, ma_max).
02085     // Input mean_anomaly refers to time t_min.
02086     //
02087     // For use with optimized binary scheduling (Steve, 6/00).
02088 
02089     // On entry, should have -PI <= ma_min < ma_max < 0.
02090 
02091     if (ma_min < -PI) return t_min;
02092     if (ma_max >= 0) return t_min;
02093     if (ma_min >= ma_max) return t_min;
02094 
02095     real f = floor((real)t_max / dtblock);
02096     xreal t_last = dtblock * f;                 // target time on this block
02097 
02098     if (t_last <= t_min) return t_min;
02099 
02100     // t_last is the last block time in the allowed range.
02101     // Determine the mean anomaly at that time.
02102 
02103     real ma = sym_angle(mean_anomaly + mean_motion * (real)(t_last - t_min));
02104 
02105     if (ma > ma_min && ma < ma_max) return t_last;
02106 
02107     // Determine (modulo 2 PI) change in ma per dtblock.
02108 
02109     real dma = sym_angle(dtblock * mean_motion);
02110 
02111     if (dma == 0) return t_min;
02112 
02113     // Find how many backward steps of dtblock we must take for
02114     // mean_anomaly to move into the desired range.  Simplify the
02115     // calculation by placing the lower limit of the range at ma_min
02116     // effectively moving all quantities into the range (0, 2 PI).
02117 
02118     ma -= ma_min;
02119     ma_max -= ma_min;
02120     ma_min = 0;
02121 
02122     // Now we want to find a time t_last corresponding to 0 < ma < ma_max.
02123 
02124 //    PRC(t_min); PRC(t_max); PRL(dtblock);
02125 //    PRC(ma_max); PRC(ma/(2*M_PI)); PRL(dma/(2*M_PI));
02126 
02127     // Deal with the easy cases first...
02128 
02129     if (dma < 0 && dma > -ma_max) {
02130 
02131         // A single backward step will increase ma by 0 < -dma < ma_max.
02132         // By construction, we now have 0 < -dma < ma_max < ma < 2*M_PI.
02133 
02134         t_last -= dtblock * ceil((2*M_PI - ma) / (-dma));
02135 
02136     } else if (dma > 0 && dma < ma_max) {
02137 
02138         // A single backward step will decrease ma by 0 < dma < ma_max.
02139         // In this case, 0 < dma < ma_max < ma < 2*M_PI.
02140 
02141         t_last -= dtblock * ceil((ma - ma_max) / dma);
02142 
02143     } else {
02144 
02145         // Do it the hard way (step by step), for now.
02146 
02147         while (t_last > t_min) {
02148             t_last -= dtblock;
02149             ma -= dma;
02150             if (ma <= 0) ma += 2*M_PI;
02151             if (ma > 2*M_PI) ma -= 2*M_PI;
02152 //          PRC(t_last); PRL(ma);
02153             if (ma < ma_max) break;
02154         }
02155     }
02156 
02157     return t_last;
02158 }
02159 
02160 real hdyn::get_unperturbed_steps(bool to_apo,   // default true (for binary)
02161                                                 //   -- set false for multiple
02162                                  bool predict)  // default false; *never* used
02163 
02164 // Additional (first) argument to_apo added by Steve 8/5/98.
02165 //
02166 // Note from SPZ.  Previous implementation returned an int.
02167 // Stellar evolution allows binaries with orbital periods as
02168 // small as a few milliseconds (see Portegies Zwart 1998), so
02169 // return type was changed to real.
02170 //
02171 // int hdyn::get_unperturbed_steps(bool predict, bool to_apo)
02172 // real hdyn::get_unperturbed_steps(bool predict, bool to_apo)
02173 // unsigned long hdyn::get_unperturbed_steps(bool predict, bool to_apo)
02174 
02175 {
02176     // Return the number of current time steps to advance
02177     // unperturbed binary motion up to (but not too far past)
02178     // the next CM time step.
02179 
02180     // If to_apo is true (default), the step will continue on
02181     // to the next apocenter (before the CM step).
02182 
02183     // First check if parent is younger binary sister, in which
02184     // case its time step may not be "definitive."
02185 
02186     // Note from Steve, 7/17/97:  For unknown reasons, this
02187     // function fails to compile properly under g++ version
02188     // cygnus-2.7-96q4 on Dec UNIX V4.0B, at optimization
02189     // levels higher than 0...
02190 
02191     hdyn * p = get_parent();
02192     if (p->is_low_level_node() && p->get_elder_sister() != NULL)
02193         p = p->get_elder_sister();
02194 
02195     real pdt = p->get_next_time() - time;       // parent time step
02196 
02197     if (pdt > unpert_step_limit) {
02198         if (diag->report_continue_unperturbed)
02199             cerr << "get_unperturbed_steps: unperturbed step for "
02200                  << format_label()
02201                  << " limited by unpert_step_limit" << endl;
02202         pdt = unpert_step_limit;
02203     }
02204 
02205     // Next step must end after current system time.  (Not relevant
02206     // during normal unperturbed step, but needed when recomputing step
02207     // after asynchronous binary evolution.)
02208 
02209     if (pdt < system_time - time) {
02210         if (diag->report_continue_unperturbed)
02211             cerr << "get_unperturbed_steps: unpert step for "
02212                  << format_label()
02213                  << " increased to reach system_time" << endl;
02214         pdt = system_time - time;
02215     }
02216         
02217     if (pdt < 0) {
02218         cerr << endl << "get_unperturbed_steps: ";
02219         PRC(p->get_next_time()); PRL(time);
02220         pp3(p);
02221         cerr << "*no* corrective action taken\n";
02222         return 0;
02223     }
02224 
02225     real pdt2 = pdt + dt_overshoot(this);
02226 
02227     // Special treatment of multiple motion, since the cost of not
02228     // starting unperturbed motion is so high...
02229 
02230     if (USE_DT_PERTURBERS && is_multiple(this)) {
02231 
02232         // Include perturber crossing time in pdt...
02233 
02234         real pert_dt = dt_perturbers(this);
02235         if (pert_dt > 0) pdt2 = max(pdt2, 0.25*pert_dt);        // conservative
02236     }
02237 
02238     // Goal: to advance the binary by as great a time as possible,
02239     //       subject to the constraints that (a) we do not wish to
02240     //       go too far past the parent node's step, and (b) we do
02241     //       not wish to end up with a separation smaller than the
02242     //       current separation.  In the event of a conflict, it is
02243     //       better to break constraint (a) than constraint (b), as
02244     //       we will continue with the current perturbed time step
02245     //       on restart.  The actual unperturbed timestep will be
02246     //       an integral multiple of the perturbed step, to retain
02247     //       some semblance of block scheduling.
02248     //
02249     //       **** We do, however, wish to end up after the parent
02250     //       **** step, and not just before it (Steve, 9/99).
02251     //
02252     //  (1)  If to_apo is false (e.g. for unperturbed multiples, to
02253     //       minimize the tidal error), then we simply want to
02254     //       advance the system by an integral number of orbits.  If
02255     //       this is not possible within the parent step, or within
02256     //       half a parent time step after the present parent step,
02257     //       then we return zero steps (thus preventing unperturbed
02258     //       motion from starting or continuing).  The unperturbed
02259     //       step will *never* exceed the parent step by more than 1
02260     //       orbital period.
02261     //
02262     //       Since the unperturbed time step is constrained by the
02263     //       value of the perturbed step, it is not in general
02264     //       possible to advance a binary by an exact number of orbit
02265     //       periods.  Unperturbed motion is initiated only on the
02266     //       inward part of the binary orbit, so we take the time
02267     //       step to be slightly *less* than a whole number of
02268     //       periods, ensuring that the final separation is slightly
02269     //       greater than the initial one.  We also ensure that the
02270     //       step will always pass the next apocenter (assuming that
02271     //       a step is to be taken), even if that will violate either
02272     //       or both of constraints (a) and (b) above.
02273     //
02274     //  (2)  If to_apo is true (e.g. for true binaries, where we will
02275     //       neglect the tidal error associated with changing the
02276     //       binary phase), we will also try to map the orbit to (just
02277     //       after) the apocenter preceding the parent's next step.
02278     //       If necessary, we may advance to the apocenter beyond the
02279     //       parent time step, subject to the same restrictions as in
02280     //       (1) above.
02281 
02282     // Determine the integral number of orbits to take.
02283 
02284     // (Recall that floor rounds down to the next integer, so orb
02285     //  orbital periods end *before* pdt.)
02286 
02287     // real orb = floor(pdt / kep->get_period());
02288     real orb = ceil(pdt / kep->get_period());
02289 
02290     // (Recall that floor rounds down to the next integer, so orb
02291     //  orbital periods end *after* pdt.)
02292 
02293     if (orb <= 0) {
02294 
02295         // See if we can take an orbital step in less than half the
02296         // next parent step or the perturber crossing time (pdt2).
02297 
02298         if (kep->get_period() <= pdt2)
02299             orb = 1;
02300 
02301         // Note that, if we can't fit into pdt but we can into pdt2,
02302         // we conservatively take a step of only one period, regardless
02303         // of how many periods would actually fit into pdt2.
02304     }
02305 
02306     real mean_anomaly = sym_angle(kep->get_mean_anomaly());  // in (-PI, PI]
02307 
02308     // Determine number of perturbed steps in the next unperturbed step.
02309 
02310     real pert_steps = 0;
02311 
02312     if (to_apo) {
02313 
02314         // Get time to next apocenter.
02315 
02316         // Old code used pred_advance_to_periastron(), but this may be quite
02317         // inaccurate.  Better to use mean motion.  (Steve, 4/99)
02318 
02319         real apo_time = (M_PI - mean_anomaly) / kep->get_mean_motion();
02320 
02321         // Note added by Steve, 7/99.  If the unperturbed motion has for
02322         // some reason (e.g. rounding error) managed to end just before
02323         // apocenter, i.e. with mean_anomaly > 0), then apo_time could be
02324         // very short, possibly as small as 1 perturbed step.  In this case,
02325         // go to the *next* apocenter, in order to avoid very short steps.
02326         // (This is what we would do here if the step ended properly, with
02327         // mean_anomaly < 0.)
02328 
02329         if (mean_anomaly > 0.9*M_PI
02330             && kep->get_period() < pdt2) apo_time += kep->get_period();
02331 
02332         pert_steps = ceil( (max(0.0, orb - 1) * kep->get_period() + apo_time)
02333                                                                 / timestep
02334                      + 1);      // extra "1" to try to overcome
02335                                 // possible problems with roundoff
02336 
02337         // Use of ceil here ensures that the unperturbed step ends just after
02338         // the apocenter immediately preceding the last allowed full orbit.
02339         // If orb = 0, this step takes us to the next apocenter, regardless
02340         // of the parent step.
02341 
02342     } else {
02343 
02344         pert_steps = floor(orb * kep->get_period() / timestep);
02345 
02346         // Use of floor here ensures that the unperturbed step ends just
02347         // before an integral number of orbits.
02348 
02349         // However, this means that the binary separation at the end point
02350         // increases slowly (if we start with mean_anomaly < 0).  Must make
02351         // sure that the step really ends after apocenter (and not just
02352         // before).
02353     }
02354 
02355     // Recheck that step will advance beyond system_time.  By construction,
02356     // it should fall short by at most 1 period...
02357 
02358     if (time + pert_steps*timestep < system_time) {
02359         if (diag->report_continue_unperturbed)
02360             cerr << "get_unperturbed_steps: unpert step for "
02361                  << format_label()
02362                  << " increased to reach system_time" << endl;
02363         pert_steps += ceil(kep->get_period() / timestep);
02364     }
02365 
02366     // Check that we really will pass apocenter at end of step.
02367 
02368     real d_mean_anomaly = timestep * kep->get_mean_motion();
02369     real end_mean_anomaly = sym_angle(mean_anomaly
02370                                        + pert_steps * d_mean_anomaly);
02371     int  mcount = 0;
02372 
02373     if (end_mean_anomaly > 0) {
02374 
02375         // Mean anomaly should be negative for incoming motion at end of step.
02376         // A while loop here could be dangerous.  Better to check iteration
02377         // count as we go.
02378 
02379         mcount = (int)(kep->get_period() / timestep);
02380 
02381         while (end_mean_anomaly <= M_PI && mcount > 0) {
02382             end_mean_anomaly += d_mean_anomaly;
02383             pert_steps += 1;
02384             mcount--;
02385         }
02386 
02387         if (end_mean_anomaly >= M_PI) end_mean_anomaly -= 2*M_PI;
02388 
02389         if (false && mcount <= 0)
02390             cerr << "get_unperturbed_steps: mcount = 0 for "
02391                  << format_label() << " at time " << time << endl;
02392     }
02393 
02394     //-----------------------------------------------------------------
02395 
02396     // We have now determined the best unconstrained value of pert_steps.
02397     // Optionally try to modify the choice to improve scheduling.
02398 
02399     if (pert_steps > 0 && options->optimize_scheduling) {
02400 
02401         // Best (maximum) value for the number of perturbed steps to take
02402         // is pert_steps.  Try to force the actual step into the block scheme
02403         // in the best possible location for scheduling purposes.
02404         //
02405         // ~Arbitrary criterion: step must end after apocenter, but not
02406         // too far after it -- require -PI < mean_anomaly < -0.9 PI, say.
02407 
02408         // Note that this procedure overrides and largely ignores
02409         // the previous timestep determination...
02410 
02411         if (!options->optimize_block) {
02412 
02413             // Try to make pert_steps a multiple of the largest possible
02414             // power of 2.
02415             //
02416             // *** Note that the "base" (perturbed) step may be quite short,
02417             // *** so this may not actually help with overall synchronization
02418             // *** (Steve, 6/00)...
02419 
02420             // First determine the amount of "wiggle room."
02421 
02422             int minus = (int)((end_mean_anomaly + M_PI) / d_mean_anomaly);
02423             int plus = (int)((-0.9*M_PI - end_mean_anomaly) / d_mean_anomaly);
02424 
02425             // Limits should be unnecessary, but...
02426 
02427             if (minus > pert_steps/2) minus = (int)(pert_steps/2);
02428             if (plus > pert_steps/2) plus = (int)(pert_steps/2);
02429 
02430             // Desired range is  (pert_steps - minus)  to  (pert_steps + plus).
02431 
02432             real new_steps = pert_steps - minus;
02433             real p2 = 2;
02434             while (new_steps <= pert_steps + plus) {
02435 
02436                 if (fmod(new_steps, p2) != 0) new_steps += p2/2;
02437 
02438                 // Now new_steps is a multiple of p2.
02439 
02440                 p2 *= 2;
02441             }
02442 
02443             // On exit from the loop, new_steps is too big -- reduce it here.
02444 
02445             p2 /= 2;
02446             new_steps -= p2/2;
02447 
02448             // PRC(new_steps); PRL(p2);
02449 
02450             pert_steps = new_steps;
02451 
02452         } else {
02453 
02454             // NEW STRATEGY (Steve, 9/99, 6/00).  Choose the number of
02455             // perturbed steps in order to optimize the "block ranking" of
02456             // the unperturbed motion, i.e. the motion will be synchronized
02457             // with the highest block possible at the end of the next step.
02458             // OK to take a step much smaller than the optimal pert_steps,
02459             // so long as the overall synchronization is improved.  This
02460             // strategy should allow the motion to migrate to and remain in
02461             // a tolerably high block (in terms of "get_next_time") after a
02462             // few unperturbed steps.
02463             //
02464             // Schematic procedure:
02465             //
02466             //   1. Start at top block (dt = 1, nb = 0) and work down (dt
02467             //      /= 2; nb++) until dt = current (perturbed) timestep.
02468             //
02469             //   2. Within each block, determine the latest time (t =
02470             //      integer * 2^{-nb}) that lies within acceptable limits
02471             //      -- i.e. within the allowed time range and within the
02472             //      permitted phase window.
02473             //
02474             //   3. If such a time exists, accept it unconditionally as
02475             //      next_time and set pert_steps accordingly.
02476 
02477             // Define range of acceptable end-times.  Allow the step to
02478             // end up to half a parent timestep past the next parent step.
02479             // Choice of t_min means that we require an "optimized" step
02480             // of at least one orbit before we accept it.
02481 
02482             xreal t_min = time + kep->get_period();
02483             xreal t_max = p->get_next_time() + 0.5*p->get_timestep();
02484 
02485             // Define range of acceptable final mean anomalies.
02486 
02487             real ma_min = -0.999*M_PI;
02488             real ma_max = -0.900*M_PI;
02489 
02490             // Step will end at time t_next in the block defined by dtblock
02491             // if t_next > t_min.
02492 
02493             xreal t_next = t_min;
02494             real dtblock;
02495             int kb;
02496 
02497             for (kb = 0, dtblock = 1; dtblock >= timestep; kb++, dtblock /= 2)
02498                 if ((t_next = latest_time(t_min, t_max, dtblock,
02499                                           ma_min, ma_max,
02500                                           mean_anomaly, kep->get_mean_motion()))
02501                         > t_min)
02502                     break;
02503 
02504             // (These debugging lines can be quite expensive...)
02505 
02506 #if 0
02507             cerr << endl << "get_unperturbed_steps for "
02508                  << format_label() << " at time " << system_time<< ":"
02509                  << endl;
02510             PRI(4); PRL(pert_steps);
02511             PRI(4); PRL(get_effective_block(time));
02512             PRI(4); PRL(get_effective_block(timestep));
02513             PRI(4); PRL(get_effective_block(pert_steps*timestep));
02514             PRI(4); PRL(get_effective_block(time+pert_steps*timestep));
02515 
02516 #endif
02517             if (t_next <= t_min
02518                 || kb >= get_effective_block(time+pert_steps*timestep)) {
02519 
02520                 // Nothing to be gained from optimizing.  Don't
02521                 // change pert_steps.
02522 
02523 #if 0
02524                 cerr << "    retaining unoptimized pert_steps" << endl;
02525 #endif
02526 
02527             } else {
02528 
02529                 real old_steps = pert_steps;
02530                 pert_steps = floor(((real)(t_next - time)) / timestep + 0.1);
02531 
02532 #if 0
02533                 PRI(4); PRC(kb); PRC(dtblock); PRL(dtblock/timestep);
02534                 PRI(4); PRC(pert_steps); PRL(pert_steps/old_steps);
02535                 PRI(4); PRL(get_effective_block(pert_steps*timestep));
02536                 PRI(4); PRL(get_effective_block(time+pert_steps*timestep));
02537 
02538                 if (get_effective_block(time+pert_steps*timestep) != kb)
02539                     cerr << "    ????" << endl;
02540 #endif
02541 
02542             }
02543         }
02544     }
02545 
02546     return pert_steps;
02547 }
02548 
02549 
02550 
02551 bool hdyn::integrate_unperturbed_motion(bool& reinitialize,
02552                                         bool force_time)   // default = false
02553 {
02554     // Update unperturbed binary motion and check for continuation.
02555     // Return true iff binary is still unperturbed at end of step.
02556     //
02557     // Flag reinitialize is set true if a reinitialization of the system
02558     // is needed on return (e.g. after binary mass loss).
02559     //
02560     // Flag force_time will force the binary time to the current system
02561     // time if set.  Normally, binaries should be allowed to remain
02562     // asynchronous to preserve the proper phase.
02563 
02564     // NOTE: Integrating a binary to a time other than the scheduled
02565     //       end of its timestep may result in its being resolved at
02566     //       an undesirable part of its orbit, causing large errors.
02567 
02568     if (diag->unpert_function_id) {
02569         cerr << endl << ">> integrate_unperturbed_motion for "
02570              << format_label() << " at time " << system_time << endl;
02571     }
02572 
02573     bool unpert = true;
02574     reinitialize = false;
02575 
02576     if (!kep) return false;
02577 
02578     hdyn *sister = get_binary_sister();
02579     time += unperturbed_timestep;
02580 
02581     if (slow) slow->inc_tau(unperturbed_timestep/get_kappa());
02582 
02583     if (time != system_time) {
02584 
02585         // Note: this can happen in case of changing binary orbit
02586         //       because of stellar evolution.  Probably undesirable
02587         //       to terminate the motion in this case, and also need
02588         //       special care in determining new step.  TO BE FIXED...
02589 
02590         // It is also possible for this to occur when perturbed steps
02591         // become very short and we begin to lose significance in the
02592         // last bit.
02593         //                                              (Steve, 7/99)
02594 
02595         real dt = time - system_time;
02596         if (dt != 0) {
02597 
02598             if (!force_time && diag->report_continue_unperturbed) {
02599 
02600                 cerr << endl << "integrate_unperturbed_motion for "
02601                      << format_label() << endl;
02602 
02603                 int p = cerr.precision(HIGH_PRECISION);
02604                 cerr << "time and system_time are different:" << endl;
02605                 PRC(time); PRL(system_time);
02606 
02607                 // fprintf(stderr,
02608                 //      "       time = '%Lx'\nsystem_time = '%Lx'\n",
02609                 //      time, system_time);
02610 
02611                 cerr << "Forcing time to system time..." << endl;
02612                 cerr.precision(p);
02613             }
02614             time = system_time;
02615         }
02616     }
02617 
02618     // Store initial separation, for use below.
02619 
02620     real initial_sep_sq = kep->get_separation() * kep->get_separation();
02621 
02622     // An unperturbed step consists of transforming the kepler
02623     // structure to the new time, then updating the hdyn time, pos,
02624     // vel, acc, jerk, and perturbation, then determining if the
02625     // unperturbed motion is to continue.
02626 
02627     // Note:  Because the period is not exactly an integral number
02628     // of time steps, the mean and true anomalies, pos, vel, etc.,
02629     // will change slightly from one step to another.  This drift
02630     // may lead to restart problems, as the kepler structure
02631     // obtained using the new pos and vel may not be precisely the
02632     // same as the original kepler.  (SLWM, 3/98)
02633 
02634     // (Basically, the operation kepler --> (pos, vel) --> kepler is
02635     // not quite cyclic, leading to small phase errors that subsequently
02636     // grow in time.)
02637 
02638     update_dyn_from_kepler();       // update pos, vel, and perturbation;
02639                                     // note special treatment of slow motion
02640 
02641     // This update will introduce a tidal error (especially in unperturbed
02642     // multiples) if the unperturbed time step is not an integral multiple
02643     // of orbit periods.  The error may be acceptably small for binaries,
02644     // which are picked up with a more stringent unperturbed criterion (they
02645     // are continued with a relaxed criterion, but this is probably OK if
02646     // they are advanced to apocenter on the first step).  However, both
02647     // the inner and the outer binaries of an unperturbed multiple may be
02648     // quite strongly perturbed, and the tidal errors significant.
02649 
02650     // Fix:  always advance the inner and outer binaries by an *integral*
02651     //       number of periods in the unperturbed multiple case (and be
02652     //       very wary of partially unperturbed multiple motion...).
02653     //
02654     //                                                  (Steve, 5/8/98)
02655 
02656     // Possible alternatives are to allow the tidal error and either
02657     // simply keep a running total of such errors, or to absorb the
02658     // error into the motion (e.g. by correcting the outer orbit in
02659     // some way) -- neither is currently implemented.
02660 
02661     bool save_unpert = fully_unperturbed;
02662     fully_unperturbed = false;
02663 
02664     if (unperturbed_timestep < kep->get_period()) {
02665         kc->partial_unpert_orbit++;
02666         // PRL(kc->partial_unpert_orbit);
02667     }
02668     else {
02669         kc->full_unpert_step++;
02670         kc->full_unpert_orbit += (int) (unperturbed_timestep
02671                                                 / kep->get_period());
02672         // PRC(kc->full_unpert_step);
02673         // PRL(kc->full_unpert_orbit);
02674     }
02675 
02676 #if 0
02677     if (streq(format_label(), "xxx")) {
02678         cerr << "in integrate_unperturbed motion for " << format_label()
02679              << endl;
02680         get_kepler()->print_all();
02681     }
02682 #endif
02683 
02684     // Note that we use the same unperturbed criterion as in the main code,
02685     // and apply the same unperturbed_timestep function.
02686 
02687     bool is_u = is_unperturbed_and_approaching();
02688 
02689 #if 0
02690     if (streq(format_label(), "xxx")) {
02691         PRL(posvel);
02692         PRL(is_u);
02693     }
02694 #endif
02695 
02696     // int set_u = 0;
02697     // unsigned long set_u = 0;
02698     real set_u = 0;
02699 
02700     // cerr << "check set_unperturbed_timestep time: ";
02701     // PRC(cpu_time());
02702 
02703     if (is_u)
02704         set_u = set_unperturbed_timestep(!force_time);
02705 
02706     // PRL(cpu_time());
02707 
02708     // End of step.  Check to see if unperturbed motion can be extended.
02709 
02710     if (is_u && set_u) {
02711 
02712         // Is still unperturbed. Not much to do here now...
02713 
02714         if (diag->report_continue_unperturbed
02715             || (diag->report_multiple && is_multiple(this)
02716                 && (diag->multiple_report_level > 0
02717                     || diag->unpert_report_level > 0))) {
02718 
02719             int p = cerr.precision(HIGH_PRECISION);
02720             cerr << "\ncontinuing unperturbed motion for "
02721                  << format_label();
02722             cerr << " and " << get_binary_sister()->format_label()
02723                  << endl
02724                  << "    at time " << time << " (next: " << set_u << " steps)"
02725                  << endl;
02726 
02727             cerr.precision(p);
02728             cerr << "    dt = " << timestep
02729                  << "  dt_unpert = " << unperturbed_timestep
02730                  << "  period = " << get_kepler()->get_period()
02731                  << endl
02732                  << "    perturbation = " << sqrt(perturbation_squared)
02733                  << "  (" << bt[init_binary_type] << ")"
02734                  << endl;
02735 
02736             if (diag->unpert_report_level > 0)
02737                 print_unperturbed_binary_params();
02738 
02739         }
02740 
02741     } else {
02742 
02743         // Time to end the unperturbed motion.
02744 
02745         unpert = false;
02746 
02747         // Start by correcting any perturber lists containing the CM.
02748         // (This would be repaired anyway at the next perturbee CM step,
02749         // but that may be too late...)
02750 
02751         bool verbose = diag->report_end_unperturbed
02752                             || (is_multiple(this) && diag->report_multiple);
02753 
02754         // PRL(verbose);
02755         // PRL(save_unpert);
02756         // PRL(binary_type);
02757 
02758         // Expand the binary into components before removing the kepler
02759         // in order to avoid complaints from expand_perturber_lists().
02760 
02761         if (save_unpert && !RESOLVE_UNPERTURBED_PERTURBERS)
02762             expand_perturber_lists(get_root(), get_parent(), verbose);
02763 
02764         // NOTE: Ending the inner component of an unperturbed multiple
02765         //       should really end the outer component too.  Shouldn't
02766         //       happen, given the current "unperturbed" criteria.
02767 
02768         if (verbose) {
02769 
02770             // Pericenter reflection will be separating on termination.
02771 
02772             if (binary_type != NOT_APPROACHING  // (= end of peri reflection)
02773                 || diag->report_pericenter_reflection
02774                 || fully_unperturbed) {
02775 
02776                 int p = cerr.precision(HIGH_PRECISION);
02777                 cerr << "\nending unperturbed motion for "
02778                      << format_label();
02779                 cerr << " and " << get_binary_sister()->format_label()
02780                      << " at time " << time << endl;
02781                 cerr << bt[binary_type] << " (binary_type = " << binary_type;
02782 
02783                 cerr.precision(p);
02784                 cerr << ")  perturbation = " << sqrt(perturbation_squared);
02785 
02786                 hdyn *pnn = get_parent()->get_nn();
02787                 if (pnn && pnn->is_valid())
02788                     cerr << "  (" << pnn->format_label() << ")";
02789 
02790                 cerr << endl;
02791 
02792                 if (diag->unpert_report_level > 0
02793                     || diag->end_unpert_report_level > 0) {
02794 
02795                     print_unperturbed_binary_params();
02796 
02797                     if (binary_type != NOT_APPROACHING) {
02798 
02799                         hdyn* p = get_parent();
02800                         hdyn* pnn = p->get_nn();
02801 
02802                         print_binary_from_dyn_pair(this, get_binary_sister(),
02803                                                    0, 0, true);
02804                         cerr << endl;
02805                         if (pnn) {
02806                             print_binary_from_dyn_pair(p, pnn, 0, 0, true);
02807                             cerr << endl;
02808                         } else
02809                             cerr << "nn is NULL" << endl;
02810 
02811                         if (pnn
02812                             && (diag->unpert_report_level > 1
02813                                 || diag->end_unpert_report_level > 1)) {
02814 
02815                             pp3_minimal(get_top_level_node(), cerr);
02816 
02817                             // Attempt to estimate the work required to get
02818                             // past this encounter.
02819 
02820                             kepler outerkep;
02821 
02822                             outerkep.set_time(time);
02823                             outerkep.set_total_mass(p->get_mass());
02824                             outerkep.set_rel_pos(p->pos - pnn->pos);
02825                             outerkep.set_rel_vel(p->vel - pnn->vel);
02826                             outerkep.initialize_from_pos_and_vel();
02827 
02828                             real r_end = outerkep.get_separation();
02829                             if (perturbation_squared
02830                                   > options->full_merge_tolerance)
02831                                 r_end
02832                                   *= sqrt(perturbation_squared
02833                                            / options->full_merge_tolerance);
02834                             if (r_end > outerkep.get_apastron())
02835                                 r_end = 0.9999*outerkep.get_apastron();
02836                             real transit_time
02837                                 = 2 * (outerkep.pred_advance_to_radius(r_end)
02838                                        - (real)time);
02839 
02840                             real ave_step
02841                                 = timestep * sqrt(kep->get_periastron()
02842                                                   /kep->get_separation());
02843 
02844                             PRI(4); PRL(transit_time);
02845                             PRI(4); PRL(ave_step);
02846 
02847                             hdyn *pnode = find_perturber_node();
02848                             if (pnode) {
02849                                 cerr << "    estimate "
02850                                      << 2 * pnode->n_perturbers
02851                                           * (transit_time/ave_step) / 1e6
02852                                      << " million force calculations to"
02853                                      << " end of encounter"
02854                                      << endl;
02855                                 cerr << "    perturber node: "
02856                                      << pnode->format_label()
02857                                      << endl;
02858                                 cerr << "    n_pert = "
02859                                      << pnode->n_perturbers
02860                                      << endl;
02861                                 PRI(4); print_nn(find_perturber_node(), 2);
02862                             }
02863 
02864                             PRI(4); PRC(is_u); PRL(set_u);
02865                         }
02866                     }
02867                 }
02868             }
02869 
02870 #if 0
02871             if (streq(format_label(), "xxx"))
02872                 get_kepler()->print_all();
02873 #endif
02874 
02875         }
02876 
02877         bool update_dynamics[2] = {false, false};
02878 
02879         // Check for mass loss by binary evolution during the unperturbed
02880         // step.
02881         //
02882         //----------------------------------------------------------------
02883         //
02884         // Code now (4/99) somewhat redundant, as create_or_delete_binary
02885         // currently does *not* perform binary evolution, but simply
02886         // removes the dstar part of the CM node.
02887         //
02888         //----------------------------------------------------------------
02889 
02890         if (get_use_dstar() && has_dstar(this)) {
02891 
02892             create_or_delete_binary(get_parent(),
02893                                     update_dynamics);
02894 
02895             //cerr << "integrate_unperturbed_motion: "
02896             //     << "back from create_or_delete_binary" << endl << flush;
02897 
02898             // If a merger occurred in create_or_delete_binary, both
02899             // this and its coll particle have already been deleted!
02900             // Detect this by checking if this is still a valid node.
02901 
02902             if (!is_valid()) {
02903                 reinitialize = true;
02904                 return false;
02905             }
02906 
02907             // If the final orbit is radically different from the
02908             // initial one, we have to recompute the time step.
02909             // However, no need to do this if mass loss is forcing
02910             // a complete reinitialization of the N-body system.
02911 
02912             // update_dynamics[0] = true ==> full reinitialization needed
02913             // update_dynamics[1] = true ==> new binary timestep needed
02914 
02915             if (!update_dynamics[0] && update_dynamics[1]) {
02916 
02917                 if (diag->report_start_unperturbed    ||
02918                     diag->report_continue_unperturbed ||
02919                     diag->report_end_unperturbed)
02920                     cerr << "\nCorrected timestep for change "
02921                          << "in binary elements.\n";
02922 
02923                 update_dyn_from_kepler();   // may introduce a tidal error...
02924                                             // -- should include in de_total
02925 
02926                 set_first_timestep(0.5*kep->get_period());
02927                 get_binary_sister()->timestep = timestep;
02928 
02929                 get_parent()->mass = mass + get_binary_sister()->get_mass();
02930             }
02931 
02932             reinitialize = update_dynamics[0];
02933             if (reinitialize)
02934                 cerr << "integrate_unperturbed_motion: reinitialization "
02935                      << "forced by binary evolution" << endl
02936                      << "parent = " << get_parent()->format_label()
02937                      << " time = " << get_system_time() << endl;
02938         }
02939 
02940         // Finish up the termination of unperturbed motion (cf hdyn
02941         // constructor).
02942 
02943         delete kep;
02944         kep = NULL;
02945         get_binary_sister()->kep = NULL;
02946         unperturbed_timestep = -VERY_LARGE_NUMBER;
02947         get_binary_sister()->unperturbed_timestep = -VERY_LARGE_NUMBER;
02948         fully_unperturbed = false;
02949         get_binary_sister()->fully_unperturbed = false;
02950 
02951         // Check that the timestep is reasonable.
02952 
02953         if (!(update_dynamics[0] || update_dynamics[1])) {
02954 
02955             // Separation may have changed if phase was adjusted during step...
02956 
02957             real sep_sq = square(pos - get_binary_sister()->pos);
02958 
02959             if (sep_sq < 0.75*initial_sep_sq) {
02960 
02961                 // Time step is probably too long.  Reduce it appropriately.
02962 
02963                 real target_timestep = timestep
02964                                         * pow(sep_sq/initial_sep_sq, 0.75);
02965                 while (timestep > target_timestep) timestep /= 2;
02966 
02967                 if (diag->report_continue_unperturbed)
02968                     cerr << "\nintegrate_unperturbed_motion: timestep for "
02969                          << format_label() << " reduced to " << timestep
02970                          << " on restart at time " << time << endl;
02971 
02972                 get_binary_sister()->timestep = timestep;
02973             }
02974         }
02975     }
02976 
02977     return unpert;
02978 }

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