Main Page   Class Hierarchy   Data Structures   File List   Data Fields   Globals  

hdyn_ev.C

Go to the documentation of this file.
00001 
00002        //=======================================================//    _\|/_
00003       //  __  _____           ___                    ___       //      /|\ ~
00004      //  /      |      ^     |   \  |         ^     |   \     //          _\|/_
00005     //   \__    |     / \    |___/  |        / \    |___/    //            /|\ ~
00006    //       \   |    /___\   |  \   |       /___\   |   \   // _\|/_
00007   //     ___/   |   /     \  |   \  |____  /     \  |___/  //   /|\ ~
00008  //                                                       //            _\|/_
00009 //=======================================================//              /|\ ~
00010 
00011 //  hdyn_ev.C: functions related to orbit integration within the hdyn class.
00012 //.............................................................................
00013 //    version 1:  Nov 1994   Piet Hut, Jun Makino, Steve McMillan
00014 //    version 2:
00015 //.............................................................................
00016 //  This file includes:
00017 //    - node synchronization functions
00018 //    - functions related to time steps
00019 //    - function for finding the next node to integrate the orbit of
00020 //    - functions for initialization
00021 //    - predictor and corrector steps for the Hermite integration scheme
00022 //    - functions for calculating acceleration & jerk, for two given nodes
00023 //    - functions for determining which nodes should exert forces on each other
00024 //    - driver function for a one-time-step integration of a node
00025 //.............................................................................
00026 
00027 // Externally visible functions:
00028 //
00029 //      void hdyn::synchronize_node
00030 //      void hdyn::set_first_timestep
00031 //      void hdyn::update
00032 //      bool hdyn::correct
00033 //      void hdyn::flat_calculate_acc_and_jerk
00034 //      void hdyn::perturber_acc_and_jerk_on_leaf
00035 //      void hdyn::tree_walk_for_partial_acc_and_jerk_on_leaf
00036 //      void hdyn::calculate_partial_acc_and_jerk_on_leaf
00037 //      void hdyn::calculate_partial_acc_and_jerk
00038 //      void hdyn::create_low_level_perturber_list
00039 //      void hdyn::calculate_acc_and_jerk_on_low_level_node
00040 //      void hdyn::calculate_acc_and_jerk_on_top_level_node
00041 //      void hdyn::top_level_node_prologue_for_force_calculation
00042 //      void hdyn::top_level_node_real_force_calculation
00043 //      void hdyn::top_level_node_epilogue_force_calculation
00044 //      void hdyn::calculate_acc_and_jerk
00045 //      void hdyn::integrate_node
00046 //
00047 //      void update_binary_sister
00048 //      hdyn* node_to_move
00049 //      void get_nodes_to_move
00050 //      void initialize_system_phase1
00051 //      void correct_acc_and_jerk
00052 //      void clean_up_hdyn_ev
00053 //      void synchronize_tree
00054 
00055 //  More memo...
00056 //
00057 //  The procedure to determine nn/coll is somewhat sorted out. For nn,
00058 //  the following functions clear nn and d_nn_sq as initialization
00059 //  --- flat_calculate_acc_and_jerk
00060 //  --- perturber_acc_and_jerk
00061 //
00062 //  The following functions do not clear
00063 //  --- calculate_partial_acc_and_jerk(_on_leaf)
00064 //  --- tree_walk_for_...
00065 //  These functions still update nn and d_nn_sq (which are supplied as
00066 //  arguments, not the class members)
00067 //  Thus, if these functions are called to determine nn without preceding
00068 //  call to (e.g.) flat_calculate_acc_and_jerk, d_nn_sq must be initialized
00069 //  to some large number.
00070 //
00071 //  The pointer coll and  d_coll_sq are initialized in the following three
00072 //  functions.
00073 //   --- flat_calculate_acc_and_jerk
00074 //   --- perturber_acc_and_jerk
00075 //   --- calculate_acc_and_jerk (The top-level routine for force calculation)
00076 //   In practice, it might be okay to initialize only at the top-level routine.
00077 //   The pointer coll is different from nn not just by the definition but
00078 //   because of the fact that it is meaningfully defined only for leafs
00079 //   (physical particles) now. Because of this difference, coll is
00080 //   changed always in place, without propagating the pointer to nodes in
00081 //   higher levels.
00082 //   This means that the pointer coll of a leaf can be overwritten during
00083 //   the force calculation of a CM node. This cannot cause any problem, I
00084 //   believe.
00085 
00086 // MEMO 8 Aug 1996
00087 //
00088 // collision radius is calculated only for leaves (physical particles)
00089 //
00090 // the actual quantity stored in d_coll_sq is
00091 //
00092 //              rij^2 - (d_i + d_2)^2
00093 //
00094 
00095 // MEMO from JM (23 Dec 1995):
00096 //
00097 // Changes over the last few days:
00098 //
00099 // calculate_partial_acc_and_jerk(_on_leaf) has changed
00100 // interface (and algorithm!!!).  It used to take two
00101 // node pointers, top and mask.  Top was assumed to be
00102 // the ancestor of the mask, and mask the ancestor of this.
00103 // It calculated the force from all leaves under top except
00104 // for those under mask.
00105 //
00106 // Now it takes three arguments, top, common and mask.  Common
00107 // must be the common ancestor of top and mask (common = top is OK)
00108 // and the routine calculates the force from the subtree under top.
00109 //
00110 // This change was necessary to implement perturbation correction.
00111 
00112 //.........................................................................
00113 
00114 // NOTE from Steve to Steve, 7/2/97...
00115 //
00116 // Perturber lists are associated with TOP-LEVEL and parent nodes.
00117 // Kepler pointers are associated with the elder binary component...
00118 // Perturbations   are associated with the binary components...
00119 //
00120 // Perturber lists are recomputed at each top-level CM step
00121 //
00122 // Problem: a binary in a deeply nested hierarchy may be handled
00123 //          very inefficiently, as it uses the top-level perturber
00124 //          list, which may be quite large.
00125 
00126 // ****  Perturber lists added to all CM nodes (Steve, 8/98).  ****
00127 // ****  Updated at CM step, based on parent perturber list.   ****
00128 // ****  Binary component perturber lists are INDEPENDENT.     ****
00129 
00130 // #define ALLOW_LOW_LEVEL_PERTURBERS           true    // now defined
00131                                                         // in hdyn.h
00132 
00133 // Also, no longer resolve an unperturbed binary into components for
00134 // purposes of computing its center of mass motion, and optionally allow
00135 // unperturbed centers of mass to remain on a perturber list without
00136 // resolving them into their components.  (See the ~warning below.)
00137 
00138 // #define RESOLVE_UNPERTURBED_PERTURBERS       false   // now defined
00139                                                         // in hdyn.h
00140 
00141 //.........................................................................
00142 
00143 #include "hdyn.h"
00144 #include "hdyn_inline.C"
00145 
00146 #define INITIAL_STEP_LIMIT      0.0625  // Arbitrary limit on the first step
00147 #define USE_POINT_MASS          true
00148 
00149 // General debugging flag:
00150 
00151 static bool dbg = false;
00152 
00153 
00154 //=============================================================================
00155 //  node synchronization functions
00156 //=============================================================================
00157 
00158 //-----------------------------------------------------------------------------
00159 // update_binary_sister: make sure that the companion of binary component
00160 //                       bi is consistent by updating its time and mirroring
00161 //                       bi's pos, vel, acc, and jerk.
00162 //-----------------------------------------------------------------------------
00163 
00164 void update_binary_sister(hdyn * bi)
00165 {
00166     hdyn *sister = bi->get_binary_sister();
00167 
00168     sister->set_time(bi->get_time());
00169     sister->set_timestep(bi->get_timestep());
00170 
00171     real factor = -bi->get_mass() / sister->get_mass();
00172     sister->set_pos(factor * bi->get_pos());
00173     sister->set_vel(factor * bi->get_vel());
00174     sister->set_acc(factor * bi->get_acc());
00175 
00176     // Note from Steve, 10/12/98: It is apparently possible for an
00177     // unperturbed binary component to have jerk = Infinity if it
00178     // has never taken a perturbed step, and this can lead to overflow
00179     // here.  Check for this explicitly.  (Not entirely clear how the
00180     // infinity arises...  May be due to GRAPE-4 overflow.)
00181 
00182     real j0 = bi->get_jerk()[0];
00183 
00184     if (j0 < VERY_LARGE_NUMBER && j0 > -VERY_LARGE_NUMBER)
00185         sister->set_jerk(factor * bi->get_jerk());
00186     else {
00187         cerr << "update_binary_sister():  setting jerk = 0 for "
00188              << bi->format_label() << " at time " << bi->get_system_time()
00189              << endl;
00190         bi->set_jerk(vector(0,0,0));
00191         sister->set_jerk(vector(0,0,0));
00192     }
00193 
00194     sister->set_pot(-factor * bi->get_pot());
00195     sister->store_old_force();
00196 
00197     sister->set_perturbation_squared(bi->get_perturbation_squared());
00198 }
00199 
00200 //-----------------------------------------------------------------------------
00201 // synchronize_node -- force the time of a node to a specified value,
00202 //                     by integrating it forward in time to system_time.
00203 //-----------------------------------------------------------------------------
00204 
00205 void hdyn::synchronize_node()
00206 {
00207     bool do_diag = (diag->ev && diag->check_diag(this));
00208 
00209     if (do_diag)
00210         cerr << "Enter synchronize_node for " << format_label() << endl;
00211 
00212     if (time == system_time) return;
00213 
00214     if (is_low_level_node()
00215         && (get_parent()->get_oldest_daughter() != this)) {
00216         if (do_diag)
00217             cerr << "Low-level node: "<<format_label()<<endl<<flush;
00218         get_elder_sister()->synchronize_node();
00219         update_binary_sister(get_elder_sister());
00220         return;
00221     }
00222 
00223     real old_timestep = timestep;
00224     timestep = system_time - time;
00225 
00226     if (timestep < 0.0) {
00227 
00228         if (do_diag)
00229             cerr << "synchronize_node: negative timestep..." << system_time
00230                  << " " << time << flush << endl;
00231         put_node(cerr, *this, options->print_xreal);
00232         exit(-1);
00233     }
00234 
00235     if (do_diag) {
00236         cerr << "Synchronize node " << format_label()
00237              << " at time " << system_time << endl
00238              <<" ...before integrate_node " << endl << flush;
00239         if (is_low_level_node()) pp3(get_parent());
00240         cerr << flush;
00241     }
00242 
00243     // cerr << "Energy before "; print_recalculated_energies(get_root(), 1);
00244 
00245     // NOTE: the "false" here means that we do NOT attempt to
00246     //       synchronize unperturbed binaries...
00247 
00248     integrate_node(get_root(), false, true);
00249 
00250     // cerr << "Energy after "; print_recalculated_energies(get_root(), 1);
00251 
00252     if (do_diag)
00253         cerr <<"After integrate_node "<<endl;
00254 
00255     // NOTE: Timestep adjustment below is not clean...
00256 
00257 //    cerr << format_label() << " "; PRC(old_timestep);
00258 
00259     while (fmod(time, old_timestep) != 0.0)
00260         old_timestep *= 0.5;
00261     timestep = old_timestep;
00262 
00263 //    PRL(timestep);
00264 
00265     if (is_low_level_node())
00266         update_binary_sister(this);
00267 
00268     if (do_diag)
00269         cerr << "Leave synchronize_node for " << format_label() << endl;
00270 }
00271 
00272 //=============================================================================
00273 //  functions related to time steps
00274 //=============================================================================
00275 
00276 //-----------------------------------------------------------------------------
00277 // set_first_timestep -- calculate the first timestep using acc, jerk,
00278 //                       and pot (acceleration, jerk, potential).
00279 //                       The expression used here is:
00280 //                                                 ________
00281 //               1              /   | acc |       V | pot |   \         ~
00282 //   timestep = ---  eta * min (   ---------  ,  -----------   )        ~
00283 //               32             \   | jerk |       | acc |    /         ~
00284 //
00285 //  The first expression gives the time scale on which the acceleration is
00286 //  changing significantly.
00287 //  The second expression gives an estimate for the free fall time
00288 //  in the case of near-zero initial velocity.
00289 //-----------------------------------------------------------------------------
00290 
00291 void hdyn::set_first_timestep(real additional_step_limit) // default = 0
00292 {
00293     real eta_init = eta / 32;   // initial accuracy parameter
00294 
00295     real j2 = jerk * jerk;
00296     real a2 = acc * acc;
00297 
00298     real dt_adot = eta_init/4;  // time step based on rate of change of acc
00299     real dt_ff   = eta_init/4;  // time step based on free-fall time
00300     real dt;                    // minimum of the two
00301 
00302     if (j2 > 0)
00303         dt_adot = eta_init * sqrt(a2 / j2);
00304 
00305     if (pot < 0 && a2 > 0)
00306         dt_ff = eta_init * sqrt(-pot / a2);
00307 
00308     dt = min(dt_adot, dt_ff);
00309 
00310     // Apply an upper limit to the time step:
00311 
00312     dt = min(dt, eta_init/4);   // eta_init/4 is arbitrary limit...
00313 
00314     real true_limit = step_limit;
00315     if (additional_step_limit > 0) true_limit = min(true_limit,
00316                                                     additional_step_limit);
00317 
00318     timestep = adjust_number_to_power(dt, true_limit);
00319 
00320     if (time != 0)
00321         while (fmod(time, timestep) != 0)
00322             timestep *= 0.5;
00323 
00324     xreal tnext = time + timestep;
00325 
00326     if (timestep == 0 || time == tnext) {
00327 
00328         cerr << endl << "set_first_timestep: dt = 0 at time "
00329              << get_system_time() << endl;
00330 
00331         PRC(dt_adot); PRL(dt_ff);
00332         PRC(a2); PRC(j2); PRL(pot);
00333         pp3(this);
00334 
00335         cerr << endl << endl << "System dump:" << endl << endl;
00336 
00337         pp3(get_root());
00338         exit(0);
00339     }
00340 }
00341 
00342 
00343 //-----------------------------------------------------------------------------
00344 // new_timestep:  Calculate the next timestep following the Aarseth
00345 //                formula (Aarseth 1985), including necessary adjustments
00346 //                for the hierarchical timestep scheme.
00347 //-----------------------------------------------------------------------------
00348 
00349 static int count = 0;
00350 
00351 local inline real new_timestep(vector& at3,             // 3rd order term
00352                                vector& bt2,             // 2nd order term
00353                                vector& jerk,            // 1st order term
00354                                vector& acc,             // 0th order term
00355                                real timestep,           // old timestep
00356                                real time,               // present time
00357                                real correction_factor,
00358                                hdyn * b,
00359                                real pert_sq)
00360 {
00361     if (timestep == 0)
00362         cerr << "new_timestep: old timestep = 0" << endl;
00363 
00364     // N.B. "timestep" may really be dtau (if b->get_kappa() > 1).
00365 
00366     real newstep, altstep;
00367     bool keplstep = (pert_sq >= 0 && pert_sq < 0.0001
00368                      && b->is_low_level_node()
00369                      && b->get_kira_options()->allow_keplstep);
00370 
00371     real dist, dtff2, dtv2;
00372 
00373     bool timestep_check = (b->get_kira_diag()->timestep_check
00374                            && b->get_kira_diag()->check_diag(b));
00375 
00376     if (keplstep) {
00377 
00378         // Use a simple "kepler" time step criterion if b is a binary
00379         // and the perturbation is fairly small.
00380 
00381         hdyn* s = b->get_younger_sister();
00382 
00383         if (s) {                                // excessively cautious?
00384 
00385             dist = abs(b->get_pos() - s->get_pos());
00386             dtff2 = dist*dist*dist / (2*b->get_parent()->get_mass());
00387             dtv2  = square(b->get_pos()) / square(b->get_vel());
00388 
00389             newstep =
00390                 altstep = 0.5 * correction_factor       // 0.5 is empirical
00391                               * b->get_eta()
00392                               * sqrt(min(dtff2, dtv2));
00393 
00394             // PRC(dist); PRC(square(b->get_vel())); PRC(dtff2); PRL(dtv2);
00395 
00396         } else
00397 
00398             keplstep = false;
00399 
00400     }
00401 
00402     real a2, j2, k2, l2;
00403     real tmp1, tmp2, tmp3, tmp4;
00404 
00405     if (!keplstep || timestep_check) {
00406 
00407         // Simple criterion:
00408         // real newstep = b->get_eta() * sqrt(sqrt(a2 / k2));
00409 
00410         // Use the Aarseth criterion here.
00411 
00412         real dt2 = timestep * timestep;
00413         real dt4 = dt2 * dt2;
00414         real dt6 = dt4 * dt2;
00415 
00416         // A note on terminology: these scaled quantities are simply the
00417         // derivatives of the acceleration -- a, j = a', k = a'', l = a'''.
00418         // Old notation called j2 a1scaled2, k2 a2scaled2, and l2 a3scaled2.
00419         // I prefer the new names... (Steve, 9/99)
00420 
00421         // real a3scaled2 = 36 * at3 * at3 / dt6;       // = (a''')^2
00422         // real a2scaled2 = 4 * bt2 * bt2 / dt4;        // = (a'')^2
00423         // real a1scaled2 = jerk * jerk;                // = (a')^2
00424 
00425         l2 = 36 * at3 * at3 / dt6;                      // = (a''')^2
00426         k2 = 4 * bt2 * bt2 / dt4;                       // = (a'')^2
00427         j2 = jerk * jerk;                               // = (a')^2
00428         a2 = acc * acc;
00429 
00430         real aarsethstep = 0;
00431 
00432         // Not completely clear why this option should be tied to
00433         // diag->grape...  (But the most likely place for a problem to
00434         // occur probably is in the GRAPE acc and jerk.)
00435 
00436         if (!b->get_kira_diag()->grape) {
00437 
00438             // The simple way:
00439 
00440             aarsethstep = b->get_eta() * sqrt((sqrt(a2 * k2) + j2)
00441                                                / (sqrt(j2 * l2) + k2));
00442 
00443             // Note that overflow may conceivably occur here or below,
00444             // as the higher derivatives may become very large...
00445 
00446         } else {
00447 
00448             // Break the calculation up into pieces, for debugging purposes.
00449 
00450             // Numerator:
00451 
00452             real sqrt1 = 0;
00453             tmp1 = a2 * k2;
00454             //  if (tmp1 > 0 && tmp1 < VERY_LARGE_NUMBER) sqrt1 = sqrt(tmp1);
00455             if (tmp1 > 0) sqrt1 = sqrt(tmp1);
00456             tmp2 = sqrt1 + j2;
00457 
00458             // Denominator:
00459 
00460             real sqrt3 = 0;
00461             tmp3 = j2 * l2;
00462             //  if (tmp3 > 0 && tmp3 < VERY_LARGE_NUMBER) sqrt3 = sqrt(tmp3);
00463             if (tmp3 > 0) sqrt3 = sqrt(tmp3);
00464             tmp4 = sqrt3 + k2;
00465 
00466             //  if (tmp2 > 0 && tmp2 < VERY_LARGE_NUMBER
00467             //      && tmp4 > 0 && tmp4 < VERY_LARGE_NUMBER)
00468             if (tmp2 > 0 && tmp4 > 0)
00469                 aarsethstep = b->get_eta() * sqrt(tmp2/tmp4);
00470 
00471             // Stop and flag an error if any of the intermediates
00472             // exceeds VERY_LARGE_NUMBER.
00473 
00474             if (tmp1 >= 0 && tmp2 >= 0 && tmp3 >= 0 && tmp4 >= 0
00475                 && tmp1 < VERY_LARGE_NUMBER
00476                 && tmp2 < VERY_LARGE_NUMBER
00477                 && tmp3 < VERY_LARGE_NUMBER
00478                 && tmp4 < VERY_LARGE_NUMBER) {  // test negation to catch NaN,
00479                                                 // which always tests false
00480             } else {
00481 
00482                 cerr.precision(HIGH_PRECISION);
00483                 cerr << endl << "new_timestep:  found errors at time "
00484                      << b->get_system_time() << endl;
00485 
00486                 PRL(acc);
00487                 PRL(jerk);
00488                 PRL(bt2);
00489                 PRL(at3);
00490                 PRC(abs(acc)); PRL(abs(jerk));
00491                 PRC(a2); PRL(j2);
00492                 PRC(abs(bt2)); PRL(abs(at3));
00493                 PRC(k2); PRL(l2);
00494                 PRC(tmp1); PRC(tmp2); PRC(tmp3); PRL(tmp4);
00495 
00496                 plot_stars(b);
00497 
00498                 cerr << endl;
00499                 pp3(b, cerr, -1);
00500 
00501                 cerr << endl << endl
00502                      << "Top-level node dump:"
00503                      << endl << endl;
00504 
00505                 pp3(b->get_top_level_node());
00506                 exit(0);
00507             }
00508 
00509 #if 0
00510             // Handy to look at timestep details (e.g. inconsistent acc
00511             // and jerk in an external potential...).
00512 
00513             cerr << endl;
00514             PRL(b->format_label());
00515             PRL(acc);
00516             PRL(jerk);
00517             PRL(bt2);
00518             PRL(at3);
00519             PRC(abs(acc)); PRL(abs(jerk));
00520             PRC(a2); PRL(j2);
00521             PRC(abs(bt2)); PRL(abs(at3));
00522             PRC(k2); PRL(l2);
00523             PRC(tmp1); PRC(tmp2); PRC(tmp3); PRL(tmp4);
00524 #endif
00525 
00526         }
00527 
00528         newstep = aarsethstep * correction_factor;
00529 #if 0
00530         PRC(timestep); PRL(newstep);
00531 #endif
00532     }
00533 
00534     if (keplstep && timestep_check) {
00535 
00536         if (newstep < 1.e-13 || altstep < 1.e-13) {
00537 
00538             int p = cerr.precision(HIGH_PRECISION);
00539             cerr << endl << "in new_timestep:" << endl;
00540 
00541             PRC(b->format_label()); PRC(dist);
00542             PRC(b->get_posvel()); PRL(pert_sq);
00543             PRC(newstep); PRC(altstep); PRL(altstep/newstep);
00544 
00545             if (count > 0 || altstep/newstep > 10) {
00546                 PRL(b->format_label());
00547                 PRC(b->get_system_time()); xprint(b->get_system_time());
00548                 PRC(b->get_time()); xprint(b->get_time());
00549                 PRC(b->get_t_pred()); xprint(b->get_t_pred());
00550                 PRL(timestep);
00551                 xreal xdt = b->get_t_pred() - b->get_time();
00552                 PRC(xdt); xprint(xdt);
00553                 PRL(acc);
00554                 PRL(jerk);
00555                 PRL(bt2);
00556                 PRL(at3);
00557                 PRC(abs(acc)); PRL(abs(jerk));
00558                 PRC(abs(bt2)); PRL(abs(at3));
00559                 PRC(a2); PRL(j2);
00560                 PRC(k2); PRL(l2);
00561                 PRC(tmp1); PRL(tmp2);
00562                 PRC(tmp3); PRL(tmp4);
00563                 if (altstep/newstep > 10) count++;
00564                 if (count > 100) exit(1);
00565             }
00566             cerr.precision(p);
00567         }
00568 
00569         newstep = altstep;      // comment out to retain Aarseth step
00570 
00571     }
00572 
00573     // The natural time step is newstep.  Force it into an appropriate
00574     // power-of-two block.  A halving of timestep is always OK.  To
00575     // preserve the synchronization, a doubling is OK only if the
00576     // current time could be reached by an integral number of doubled
00577     // time steps (see use of fmod below).
00578 
00579     if (newstep < timestep)
00580 
00581         return 0.5 * timestep;
00582 
00583     else if (newstep < 2 * timestep)
00584 
00585         return timestep;
00586 
00587     else if (fmod(time, 2 * timestep * b->get_kappa()) != 0.0
00588              || 2 * timestep * b->get_kappa() > b->get_step_limit())
00589 
00590         return timestep;
00591 
00592     else {
00593 
00594         // Added by Steve 7/28/98 to deal with pathological ICs from SPZ...
00595         // Do not double if this is a strongly perturbed center of mass node...
00596 
00597         if (b->is_leaf()
00598             || b->get_oldest_daughter()->get_perturbation_squared() < 1)
00599             return 2 * timestep;
00600         else
00601             return timestep;
00602     }
00603 }
00604 
00605 //-----------------------------------------------------------------------------
00606 // update:  Update the time and the timestep.
00607 //-----------------------------------------------------------------------------
00608 
00609 void hdyn::update(vector& bt2, vector& at3)    // pass arguments to
00610                                                       // avoid recomputation
00611 {
00612     time += timestep;
00613     real dt = timestep;
00614 
00615     if (slow) {
00616         dt = slow->get_dtau();
00617         slow->inc_tau(dt);
00618     }
00619 
00620     // vector at3 = 2 * (old_acc - acc) + dt * (old_jerk + jerk);
00621     // vector bt2 = -3 * (old_acc - acc) - dt * (2 * old_jerk + jerk);
00622 
00623     // Reminder:        at3  =  a''' dt^3 / 6           (a' = j)
00624     //                  bt2  =  a''  dt^2 / 2
00625 
00626 #if 0
00627     if (time > 0) {
00628         cerr << endl;
00629         PRI(4); PRC(index); PRL(dt);
00630         PRI(4); PRL(old_acc);
00631         PRI(4); PRL(acc);
00632         PRI(4); PRL(old_jerk);
00633         PRI(4); PRL(jerk);
00634         PRI(4); PRL((acc-old_acc)/dt);
00635         PRI(4); PRL(2*bt2/(dt*dt));
00636         PRI(4); PRL((jerk-old_jerk)/dt);
00637         PRI(4); PRL(6*at3/(dt*dt*dt));
00638     }
00639 #endif
00640 
00641     // Define correction factor for use in new_timestep.
00642 
00643     real correction_factor = 1;
00644 
00645     if (is_low_level_node()) {
00646 
00647         // Correction factor reduces the timestep in a close binary,
00648         // in order to improve energy errors.
00649 
00650         real m = mass/mbar;
00651         real pot_sq = m*m*d_min_sq/(pos*pos);
00652 
00653         // Steve 8/98:  1. 0.125 here is conservative -- expect *cumulative*
00654         //                 energy error to be ~fourth order.
00655         //              2. Could rewrite to replace pow() if necessary...
00656         //                 *** see kira_approx.C ***
00657 
00658         // Hmmm...  This pow() seems to fall victim to the strange math.h
00659         // bug in Red Hat Linux 5.  In that case, use Steve's approximate
00660         // version instead.
00661 
00662         // correction_factor = pow(pot_sq, -0.1);
00663 
00664         if (pot_sq > 1)
00665             // correction_factor = pow(pot_sq, -0.125);
00666             correction_factor = pow_approx(pot_sq);     // -0.125 is built in!
00667 
00668         // correction_factor = pow(pos*pos/d_min_sq, 0.025);
00669 
00670         // May be desirable to place limits on the correction...
00671 
00672         if (correction_factor > 1.0) correction_factor = 1.0;
00673         if (correction_factor < 0.2) correction_factor = 0.2;
00674 
00675 #if 0
00676         if (perturbation_squared < 0.0001 && pot_sq > 1
00677             && timestep < 1.e-11) {
00678             PRC(format_label()); PRL(correction_factor);
00679         }
00680 #endif
00681 
00682     }
00683 
00684     real new_dt = new_timestep(at3, bt2, jerk, acc, dt, time,
00685                                correction_factor, this, perturbation_squared);
00686 
00687     if (new_dt <= 0) {
00688         cerr << "update:  dt = " << new_dt
00689              << " at time " << get_system_time();
00690         pp3(this,cerr);
00691     }
00692 
00693     // Computed new_dt will be timestep if slow not set; dtau otherwise.
00694 
00695     if (slow) {
00696         slow->set_dtau(new_dt);
00697         timestep = get_kappa() * new_dt;
00698     } else
00699         timestep = new_dt;
00700 
00701     // Update posvel = pos*vel (for use in binary handling).
00702 
00703     if (is_low_level_node()) {
00704         prev_posvel = posvel;
00705         posvel = pos * vel;
00706     }
00707 
00708     // Finally, store a'' for use in prediction of (top-level, for now) nodes.
00709 
00710     if (is_top_level_node())
00711         k_over_18 = bt2 / (9*dt*dt);
00712 
00713 #if 0
00714         if (new_dt < 1.e-11) {
00715             int p = cerr.precision(HIGH_PRECISION);
00716             cerr << endl << "in update:" << endl;;
00717             PRC(time);
00718             PRC(dt); PRL(abs(pos));
00719             PRL(pred_pos);
00720             PRL(pos);
00721             PRL(pred_vel);
00722             PRL(vel);
00723             PRL(old_acc);
00724             PRL(acc);
00725             PRL(old_jerk);
00726             PRL(jerk);
00727             PRL(-3 * (old_acc - acc));
00728             PRL(-dt * (2 * old_jerk + jerk));
00729             PRL(bt2);
00730             PRL(2 * (old_acc - acc));
00731             PRL(dt * (old_jerk + jerk));
00732             PRL(at3);
00733             PRC(new_dt), PRL(timestep);
00734             cerr << endl;
00735             cerr.precision(p);
00736         }
00737 #endif
00738 
00739 }
00740 
00741 //=============================================================================
00742 //  function for finding the next node to integrate
00743 //=============================================================================
00744 
00745 //-----------------------------------------------------------------------------
00746 // node_to_move:  Return the node with minimum t+dt.
00747 //-----------------------------------------------------------------------------
00748 
00749 hdyn *node_to_move(hdyn * b,
00750                    real & tmin)
00751 {
00752     hdyn *bmin = NULL;
00753     if (b->is_parent())
00754         for_all_daughters(hdyn, b, d) {
00755             hdyn *btmp;
00756             real ttmp = VERY_LARGE_NUMBER;
00757             btmp = node_to_move(d, ttmp);
00758             if (ttmp < tmin) {
00759                 tmin = ttmp;
00760                 bmin = btmp;
00761             }
00762         }
00763 
00764     if (b->get_parent() != NULL)
00765         if (tmin > b->get_next_time()) {
00766             bmin = b;
00767             tmin = b->get_next_time();
00768         }
00769     return bmin;
00770 }
00771 
00772 //-----------------------------------------------------------------------------
00773 // get_nodes_to_move:  Return the node(s) with minimum t+dt.  Use a recursive
00774 //                     loop to traverse the system.
00775 //-----------------------------------------------------------------------------
00776 
00777 void get_nodes_to_move(hdyn * b,
00778                        hdyn * list[],
00779                        int &nlist,
00780                        real & tmin)
00781 {
00782     hdyn *bmin = NULL;
00783 
00784     if (b->get_parent() == NULL) {                      // b is root: initialize
00785         nlist = 0;
00786         tmin = VERY_LARGE_NUMBER;
00787     }
00788 
00789     if (b->is_parent())
00790         for_all_daughters(hdyn, b, d) {
00791             get_nodes_to_move(d, list, nlist, tmin);
00792         }
00793 
00794     if (b->get_parent() != NULL) {
00795 
00796         if (tmin > b->get_next_time()) {
00797             nlist = 1;
00798             tmin = b->get_next_time();
00799             list[0] = b;
00800 
00801         } else if (tmin == b->get_next_time()) {        // NOTE: we are assuming
00802             list[nlist] = b;                            // that equality is OK
00803             nlist++;                                    // here because all
00804             if (b->is_low_level_node()) {               // times are powers of 2
00805                 hdyn *bs = b->get_binary_sister();
00806                 for (int j = nlist - 2; j >= 0; j--) {
00807                     if (list[j] == bs) {
00808                         nlist--;
00809                         j = -1;
00810                     }
00811                 }
00812             }
00813         }
00814     }
00815 }
00816 
00817 //=============================================================================
00818 //  functions for initialization
00819 //=============================================================================
00820 
00821 //-----------------------------------------------------------------------------
00822 // initialize_system_phase1:  Set current time and "predicted" position
00823 //                            and velocity for all nodes.
00824 //-----------------------------------------------------------------------------
00825 
00826 void initialize_system_phase1(hdyn * b,  xreal t)
00827 {
00828     if (b->get_oldest_daughter() != NULL)
00829         for_all_daughters(hdyn, b, d)
00830             initialize_system_phase1(d, t);
00831 
00832     if (b->get_kepler() == NULL && b->get_unperturbed_timestep() <= 0) {
00833 
00834         b->set_time(t);
00835         b->set_system_time(t);
00836         b->init_pred();
00837 
00838     }
00839 }
00840 
00841 //=============================================================================
00842 //  Predictor and corrector steps for the Hermite integration scheme
00843 //=============================================================================
00844 
00845 //-----------------------------------------------------------------------------
00846 // predict_loworder_all:  Predict the positions of all nodes at time t.
00847 //                        Now defined in _dyn_/_dyn_ev.C (Steve, 6/99).
00848 //-----------------------------------------------------------------------------
00849 
00850 //void predict_loworder_all(hdyn * b,
00851 //                          xreal t)
00852 //{
00853 //    if (!b) return;                   // shouldn't happen -- flag as error?
00854 //
00855 //    if (b->is_parent())
00856 //      for_all_daughters(hdyn, b, d)
00857 //          predict_loworder_all(d, t);
00858 //
00859 //    b->predict_loworder(t);
00860 //}
00861 
00862 //-----------------------------------------------------------------------------
00863 // correct_and_update:  Apply the corrector for 3rd-order hermite scheme
00864 //                      and update time and timestep.
00865 //
00866 // See Makino & Aarseth (PASJ 1992) for derivation of corrector terms.
00867 //
00868 //-----------------------------------------------------------------------------
00869 
00870 local inline void get_derivs(vector& acc, vector& jerk,
00871                              vector& old_acc, vector& old_jerk,
00872                              real dt, vector& bt2, vector& at3)
00873 {
00874     bt2 = -3 * (old_acc - acc) - dt * (2 * old_jerk + jerk);
00875     at3 =  2 * (old_acc - acc) + dt * (old_jerk + jerk);
00876 }
00877 
00878 local inline void update_derivs_from_perturbed(vector& acc_p,
00879                                                vector& jerk_p,
00880                                                vector& old_acc_p,
00881                                                vector& old_jerk_p,
00882                                                real kdt,
00883                                                real ki2,        // not used
00884                                                real ki3,        // not used
00885                                                vector& acc,
00886                                                vector& jerk,
00887                                                vector& bt2,
00888                                                vector& at3,
00889                                                bool print = false)
00890 {
00891     // Return here to neglect all perturbed terms and compute slow binary
00892     // CM motion in the center of mass approximation.
00893 
00894     // if (CM_ONLY) return;
00895 
00896     vector bt2_p, at3_p;
00897     get_derivs(acc_p, jerk_p, old_acc_p, old_jerk_p,
00898                kdt, bt2_p, at3_p);
00899 
00900     // Don't scale here -- should be implicit in acc and jerk.
00901 
00902     // bt2_p *= ki2;
00903     // at3_p *= ki3;
00904 
00905     acc += acc_p;
00906     jerk += jerk_p;
00907     bt2 += bt2_p;
00908     at3 += at3_p;
00909 
00910     if (print) {
00911         cerr << endl;
00912         PRC(abs(acc-acc_p)); PRL(abs(acc_p));
00913         PRC(abs(jerk-jerk_p)); PRL(abs(jerk_p));
00914         PRC(abs(bt2-bt2_p)); PRL(abs(bt2_p));
00915         PRC(abs(at3-at3_p)); PRL(abs(at3_p));
00916     }
00917 }
00918 
00919 local inline void correct_slow(slow_binary *s, real dt,
00920                                vector &acc, vector &jerk,
00921                                vector &bt2, vector &at3,
00922                                int id)
00923 {
00924 
00925     // Internal slow motion operates on timescale dtau, not dt.
00926     // This effectively reduces vel by kappa, acc_p by kappa^2,
00927     // jerk_p by kappa^3, etc.
00928 
00929     real kap = s->get_kappa();
00930 
00931     real ki = 1/kap;
00932     real ki2 = ki*ki;
00933     real ki3 = ki2*ki;
00934 
00935     // Note from Steve, 9/99: the manipulations here would be much
00936     // simpler if we had direct (friend?) access to the acc_p (etc.)
00937     // data in the slow_binary class -- can this be done?
00938     //
00939     // For now, simply work with copies and copy back changes as
00940     // necessary.
00941 
00942     vector acc_p = s->get_acc_p();
00943     vector jerk_p = s->get_jerk_p();
00944 
00945     // Scale acc_p and jerk_p first because the old_ versions are
00946     // already scaled.
00947 
00948     acc_p *= ki2;
00949     jerk_p *= ki3;
00950 
00951     // Propogate the changes back to the class.
00952 
00953     s->set_acc_p(acc_p);
00954     s->set_jerk_p(jerk_p);
00955 
00956 #if 0
00957 
00958     // Critical point for consistency is that the computed d(acc)
00959     // should be approximately kap*dt*jerk before scaling, or
00960     // dt*jerk afterwards...
00961 
00962     PRL(acc_p-old_acc_p);
00963     PRL(0.5*dt*(jerk_p+old_jerk_p));
00964 
00965 #endif
00966 
00967     vector old_acc_p = s->get_old_acc_p();
00968     vector old_jerk_p = s->get_old_jerk_p();
00969 
00970     update_derivs_from_perturbed(acc_p, jerk_p, old_acc_p, old_jerk_p,
00971                                  dt, ki2, ki3,
00972                                  acc, jerk, bt2, at3);
00973 }
00974 
00975 #define ONE12 0.0833333333333333333333
00976 #define ONE3  0.3333333333333333333333
00977 
00978 bool hdyn::correct_and_update()
00979 {
00980 #if 0
00981     if (is_low_level_node()) {
00982         cerr << "old_acc  " << old_acc << endl;
00983         cerr << "    acc  " << acc << endl;
00984         cerr << "old_jerk " << old_jerk << endl;
00985         cerr << "    jerk " << jerk << endl;
00986         cerr << " dt " << timestep << endl;
00987         cerr << "pos " << pos << endl;
00988         cerr << "vel " << vel << endl;
00989         cerr << "pos " << pred_pos << endl;
00990         cerr << "vel " << pred_vel << endl;
00991         cerr << "pos " << get_younger_sister()->pos << endl;
00992         cerr << "vel " << get_younger_sister()->vel << endl;
00993         cerr << "pos " << get_younger_sister()->pred_pos << endl;
00994         cerr << "vel " << get_younger_sister()->pred_vel << endl;
00995     }
00996 #endif
00997 
00998     real dt = timestep;
00999     if (slow) dt = slow->get_dtau();
01000 
01001     // For slow binaries and their perturbers, acc and jerk are computed
01002     // in the center of mass approximation, with the perturbative terms
01003     // saved separately for special treatment, but old_acc and old_jerk
01004     // necessarily include the (adjusted) perturbative terms, as they are
01005     // used in prediction.  Must adjust them here before proceeding with
01006     // the correction.  Note that this is the only function where such
01007     // modification occurs.
01008 
01009     hdyn *od = get_oldest_daughter();
01010     bool is_top_level = is_top_level_node();
01011 
01012     // For treatment of low-level slow binaries...
01013 
01014     int low_slow_comp = 0;              // indicates which component, if
01015                                         // any, is slow (0: neither, 1: od,
01016                                         // 2: ys, 3: both)
01017     hdyn *sb = NULL;
01018 
01019     if (is_top_level) {
01020 
01021         if (od && od->slow) {                   // slow binary CM
01022 
01023             old_acc -= od->slow->get_old_acc_p();
01024             old_jerk -= od->slow->get_old_jerk_p();
01025 
01026         } else if (sp) {                        // slow binary perturber
01027 
01028             slow_perturbed *s = sp;
01029             while (s) {                         // correct for all perturbees
01030                 old_acc -= s->get_old_acc_p();
01031                 old_jerk -= s->get_old_jerk_p();
01032                 s = s->get_next();
01033             }
01034         }
01035 
01036     } else {
01037 
01038         if (od && od->slow) {
01039             low_slow_comp = 1;          // this is a low-level slow binary CM
01040             sb = od;
01041         }
01042 
01043         hdyn *od2 = get_younger_sister()->get_oldest_daughter();
01044 
01045         if (od2 && od2->slow) {
01046 
01047             low_slow_comp += 2;         // sister is a low-level slow binary CM
01048 
01049             if (!sb || sb->get_kappa() < od2->get_kappa()) sb = od2;
01050         }
01051 
01052 
01053         low_slow_comp = 0;              // *** not implemented, for now... ***
01054 
01055 
01056         if (low_slow_comp) {
01057 
01058             // Low-level slow binaries are resolved into components
01059             // for purposes of computing the sister interaction...
01060 
01061             // Need to remove the perturbative portions of both the old
01062             // and current accs and jerks.  Compute the acc and jerk
01063             // terms here.  Old info is stored in the slow data structure
01064             // of the (elder) slow binary sb.
01065 
01066             old_acc -= sb->slow->get_old_acc_p();
01067             old_jerk -= sb->slow->get_old_jerk_p();
01068 
01069             // Compute relative acceleration and jerk due to sister
01070             // in the point-mass approximation.
01071             //
01072             // Hmmm.....
01073             //
01074             // What we need is to compute the 2-body force in the point-mass
01075             // approximation, but taking the external perturbation into account.
01076             // Then subtracting the non-point-mass force leaves just the
01077             // perturbative part of the interaction, for correction below.
01078             //
01079             // However, it looks as though calculate_partial_acc_and jerk()
01080             // computes only the force on this node, not the relative force
01081             // between the components; it also seems to resolve the second
01082             // component if it is a binary, which is not what we want here
01083             // (However, specifying point mass mode will do the right thing
01084             // if the first component is a binary...)
01085             //
01086             // Function calculate_acc_and_jerk_on_low_level_node() uses
01087             // calculate_partial_acc_and jerk(), so it isn't what we want
01088             // either...
01089             //
01090             // If we redo this calculation by hand, we end up computing the
01091             // perturber interaction twice...
01092 
01093 
01094             vector a_2b, j_2b;
01095             real p_2b;
01096             hdyn *p_nn_sister;
01097             real d_min_sister = VERY_LARGE_NUMBER;
01098 
01099 
01100             calculate_partial_acc_and_jerk(get_parent(), get_parent(), this,
01101                                            a_2b, j_2b, p_2b,
01102                                            d_min_sister, p_nn_sister,
01103                                            USE_POINT_MASS,
01104                                            get_parent()->find_perturber_node(),
01105                                            this);             // node to charge
01106 
01107 
01108             // Save CM pieces only in acc and jerk; store the rest in acc_p
01109             // and jerk_p.
01110 
01111             PRL(old_acc);
01112             PRL(acc);
01113             PRL(a_2b);
01114 
01115             vector dx = pred_pos - get_younger_sister()->pred_pos;
01116             real r2 = dx*dx;
01117             vector a = -get_younger_sister()->mass * dx / pow(r2, 1.5);
01118 
01119             PRL(a);
01120 
01121             // pp3(get_parent());
01122 
01123             sb->slow->set_acc_p(acc-a_2b);
01124             sb->slow->set_jerk_p(jerk-j_2b);
01125 
01126             acc = a_2b;
01127             jerk = j_2b;
01128         }
01129     }
01130 
01131     vector bt2, at3;
01132     get_derivs(acc, jerk, old_acc, old_jerk, dt, bt2, at3);
01133 
01134     // Reminder:        bt2  =  a''  dt^2 / 2           (a' = j)
01135     //                  at3  =  a''' dt^3 / 6
01136 
01137     // For slow systems or perturbers, the derivatives at this point
01138     // are in the center of mass approximation.  We now include the
01139     // perturbative terms separately.  There are three circumstances in
01140     // which a perturbative term must be included: (1) this is the CM
01141     // of a slow binary, (2) this is (the top_level_node of) a perturber
01142     // of one or more slow binaries, and (3) this is part of a multiple
01143     // system containing a slow binary.  In each case, acc and jerk are
01144     // the now center-of-mass part only; the relevant correction terms
01145     // are saved in slow->acc_p and slow->jerk_p, or in the corresponding
01146     // entries in the slow_perturbed list.
01147 
01148     // As currently coded, a slow binary sees any other slow binaries on
01149     // its perturber list in the center-of-mass approximation only, so the
01150     // issue of how to scale the slow-slow interaction doesn't arise.
01151 
01152 #if 0
01153 
01154     PRL(acc-old_acc);
01155     PRL(0.5*dt*(jerk+old_jerk));
01156 
01157 #endif
01158 
01159     if (is_top_level) {
01160 
01161         if (od && od->slow)                             // case (1)
01162 
01163             correct_slow(od->slow, dt, acc, jerk, bt2, at3, 1);
01164 
01165         else if (sp) {                                  // case (2)
01166 
01167             // Loop over all slow binaries known to be perturbed by this node.
01168             // Scale the derivatives, but do not apply any correction if kappa
01169             // has changed or the old_ quantities are zero -- the default is
01170             // thus to use the CM approximation in those cases.  Function
01171             // store_old_force will properly set the old_ quantities later.
01172 
01173             slow_perturbed *s = sp;
01174             bool cleanup = false;
01175 
01176             // cerr << "correct: checking slow_perturbed list of "
01177             //      << format_label() << endl;
01178 
01179             while (s) {
01180 
01181                 hdyn *bj = (hdyn*)s->get_node();
01182 
01183                 od = NULL;
01184                 if (bj && bj->is_valid())
01185                     od = bj->get_oldest_daughter();
01186 
01187                 if (od && od->get_slow()) {
01188 
01189                     // We have a valid slow binary node.  Only compute
01190                     // the correction if kappa is what we expect and
01191                     // old_acc_p is nonzero.
01192 
01193                     bool corr = true;
01194 
01195                     int kap = s->get_kappa();
01196                     if (kap != od->get_kappa()) {
01197 
01198                         kap = od->get_kappa();
01199                         s->set_kappa(kap);
01200 
01201                         corr = false;
01202 
01203 #if 0
01204                         cerr << "correct: skipping correction for "
01205                              << bj->format_label()
01206                              << " because kappa doesn't match: " << endl;
01207                         PRC(kap); PRL(od->get_kappa());
01208 #endif
01209                     }
01210 
01211                     real ki = 1.0/kap;
01212                     real ki2 = ki*ki;
01213                     real ki3 = ki2*ki;
01214 
01215                     vector acc_p = s->get_acc_p();      // as above, better to
01216                     vector jerk_p = s->get_jerk_p();    // have _dyn_ as friend?
01217 
01218                     // Scale acc_p and jerk_p (see comment above).
01219 
01220                     acc_p *= ki2;
01221                     jerk_p *= ki3;
01222 
01223                     // Propogate the changes back to the class.
01224 
01225                     s->set_acc_p(acc_p);
01226                     s->set_jerk_p(jerk_p);
01227 
01228                     if (corr) {
01229 
01230                         // Apply the correction if the old_ quantities are set.
01231 
01232                         vector old_acc_p = s->get_old_acc_p();
01233 
01234                         if (square(old_acc_p) > 0) {
01235 
01236                             vector old_jerk_p = s->get_old_jerk_p();
01237                             real kdt = dt; // kap*dt;
01238 
01239                             update_derivs_from_perturbed(acc_p, jerk_p,
01240                                                          old_acc_p, old_jerk_p,
01241                                                          kdt, ki2, ki3,
01242                                                          acc, jerk, bt2, at3);
01243 
01244 #if 0
01245                             cerr << "correct: corrected " << format_label();
01246                             cerr << " for slow binary " << bj->format_label()
01247                                  << endl;
01248 #endif
01249 
01250                         } else {
01251 
01252 #if 0
01253                             cerr << "correct: skipping correction for "
01254                                  << bj->format_label()
01255                                  << " because old_acc_p isn't set" << endl;
01256 #endif
01257 
01258                         }
01259                     }
01260 
01261                 } else {
01262 
01263                     // This shouldn't happen, but may conceivably occur when
01264                     // the perturber lists have been disrupted.  Do nothing,
01265                     // but flag a warning for now.
01266 
01267                     int p = cerr.precision(INT_PRECISION);
01268                     cerr << "correct: warning: inconsistency in slow_perturbed "
01269                          << "correction" << endl
01270                          << "         for node " << format_label()
01271                          << " at time " << get_time()
01272                          << endl;
01273                     cerr.precision(p);
01274 
01275                     PRC(bj); PRL(bj->format_label());
01276                     PRL(od);
01277                     if (od) PRL(od->get_slow());
01278 
01279                     cleanup = true;
01280 
01281                 }
01282 
01283                 s = s->get_next();
01284             }
01285 
01286             if (cleanup)
01287                 check_slow_perturbed(diag->slow_perturbed);
01288 
01289         }
01290 
01291     } else if (low_slow_comp)                           // case (3)
01292 
01293         correct_slow(sb->slow, dt, acc, jerk, bt2, at3, 2);
01294 
01295     vector new_pos = pred_pos + (0.05 * at3 + ONE12 * bt2) * dt * dt;
01296     vector new_vel = pred_vel + (0.25 * at3 + ONE3 * bt2) * dt;
01297 
01298 #if defined(STARLAB_HAS_GRAPE4) || defined(STARLAB_HAS_GRAPE4)
01299 
01300     // Note from Steve (10/01):
01301     //
01302     // Need to check for possible hardware errors (at least on the GRAPE-4).
01303     // By construction, no quantity should change much from one step to the
01304     // next.  Thus, a large change in acc or jerk (on in new_vel relative to
01305     // vel, which combines the two in a natural way) probably indicates a
01306     // problem.  One difficulty with checking new_vel is that supernovae may
01307     // result in legal but large kick velocities.  However, these should
01308     // be applied *after* the dynamical step, in which case both the old and
01309     // the new velocities should be large.
01310     //
01311     // The GRAPE-4 error of 10/01 occurs exclusively in acc and in only one
01312     // component, usually (but not always) z.  Thus, another possible check
01313     // might be to see if the velocity, acc, or jerk (whichever is "large")
01314     // is directed predominantly along one coordinate axis.
01315     //
01316     // Probably best to use vel as an indicator, then apply successively
01317     // finer criteria before declaring a problem.  Once a problem is found,
01318     // we simply quit this function, returning false.  It is up to the
01319     // calling function to take corrective action.  Currently, this normally
01320     // consists of recomputing the acc and jerk on the front end and calling
01321     // this function again (just one retry, so we should be sure not to flag
01322     // high-speed neutron stars...).
01323     //
01324     // Note also that, if the acc/jerk error is sufficiently small that it
01325     // doesn't register significantly in vel, then there is no need to take
01326     // action, as the problem seems to disappear from one GRAPE call to the
01327     // next.
01328 
01329     real old_v = abs1(vel);
01330 
01331     // Numbers here are somewhat arbitrary (but see above note).
01332 
01333     // real new_v = abs1(new_vel);
01334     // if (new_v/old_v > 1000 || new_v > 1.e6) {        // too loose...
01335 
01336     real dv = abs1(new_vel-vel);
01337     if (dv > old_v && dv > 0.5) {
01338 
01339         // Possible runaway -- speed has changed significantly.
01340 
01341         // Refine the possibilities before flagging an error.
01342         // Neutron star shouldn't show a large delta(vel), and the acc
01343         // or jerk should be good indicators of problems in any case.
01344 
01345         if (abs1(acc-old_acc) > abs1(old_acc)
01346             || abs1(jerk-old_jerk) > 5*abs1(old_jerk)) {
01347 
01348             if (diag->grape && diag->grape_level > 0) {
01349 
01350                 cerr << endl << "correct: possible hardware error at time "
01351                      << get_system_time() << endl;
01352 
01353 #if defined(STARLAB_HAS_GRAPE4)
01354                 PRL(get_grape_chip(this));      // direct access to data
01355                                                 // in hdyn_grape4.C
01356 #endif
01357 
01358 #if 0
01359                 cerr << endl << "pp3 with old pos and vel:" << endl;
01360                 pp3(this);
01361 #else
01362                 PRL(old_acc);
01363                 PRL(old_jerk);
01364                 PRL(acc);
01365                 PRL(jerk);
01366 #endif
01367             }
01368 
01369             // cerr << endl << endl << "System dump:" << endl << endl;
01370             // pp3(get_root());
01371 
01372             // Options: quit
01373             //          restart
01374             //          flag and continue
01375             //          discard new_pos and new_vel, retain
01376             //              old acc and jerk and continue
01377             //          flag, recompute acc and jerk, and continue  <--
01378 
01379             // Flag the problem internally.
01380 
01381             char tmp[128];
01382             sprintf(tmp, "runaway in correct at time %f", time);
01383             log_comment(tmp);
01384 
01385             int n_runaway = 0;
01386             if (find_qmatch(get_log_story(), "n_runaway"))
01387                 n_runaway = getiq(get_log_story(), "n_runaway");
01388 
01389             n_runaway++;
01390             if (diag->grape && diag->grape_level > 0)
01391                 PRL(n_runaway);
01392 
01393             if (n_runaway > 10)
01394                 exit(0);                // pretty liberal, as errors are
01395                                         // usually associated with pipes,
01396                                         // not stars...
01397 
01398             putiq(get_log_story(), "n_runaway", n_runaway);
01399 
01400             return false;               // trigger a retry on return; don't
01401                                         // even bother with update
01402         }
01403     }
01404 
01405 #endif
01406 
01407     pos = new_pos;
01408     vel = new_vel;
01409 
01410     //--------------------------------------------------------
01411     // Call update here to avoid recomputation of bt2 and at3.
01412     //--------------------------------------------------------
01413 
01414     update(bt2, at3);
01415 
01416     return true;                // normal return value
01417 }
01418 
01419 
01420 //
01421 // Some notes on the force-evaluation functions (Steve, 7/98).
01422 // ----------------------------------------------------------
01423 //
01424 // Functions (note: "force" <--> "acc, jerk, and potential"):
01425 //
01426 // accumulate_acc_and_jerk
01427 //      compute force due to a specified particle
01428 //
01429 // hdyn::flat_calculate_acc_and_jerk
01430 //      compute force on top-level node 'this' due to all other
01431 //      top-level nodes in the system (masking allowed)
01432 //
01433 // hdyn::perturber_acc_and_jerk_on_leaf
01434 //      compute force on 'this' leaf due to its top-level
01435 //      perturber list
01436 //
01437 // hdyn::tree_walk_for_partial_acc_and_jerk_on_leaf
01438 //      accumulate force on 'this' leaf while traversing the portion
01439 //      of the tree under a specified node, excluding everything
01440 //      under a mask node.  Currently, the descent STOPS at an
01441 //      unperturbed center of mass.
01442 //
01443 // hdyn::calculate_partial_acc_and_jerk_on_leaf
01444 //      compute the force on `this' leaf from all particles under
01445 //      a specified node, excluding everything under a mask node
01446 //
01447 // hdyn::calculate_partial_acc_and_jerk
01448 //      compute the force on `this' node or leaf from all particles
01449 //      under a specified node, excluding everything under a mask node
01450 //
01451 // hdyn::calculate_acc_and_jerk_on_low_level_node
01452 //      compute the force on `this' low-level node or leaf
01453 //
01454 // hdyn::calculate_acc_and_jerk_on_top_level_node
01455 //      compute the force on `this' top-level node or leaf
01456 //
01457 // hdyn::calculate_acc_and_jerk
01458 //      compute the force on `this' node or leaf
01459 //
01460 // correct_acc_and_jerk
01461 //      correct computed force in situations where GRAPE-style functions
01462 //      lead to incorrect results
01463 //
01464 //
01465 // To allow for efficient utilization of the GRAPE hardware from kira,
01466 // function calculate_acc_and_jerk_on_top_level_node is divided into
01467 // calls to the following functions:
01468 //
01469 // hdyn::top_level_node_prologue_for_force_calculation
01470 //      performs entire calculation in exact case; setup otherwise
01471 // hdyn::top_level_node_real_force_calculation
01472 //      top-level force calculation only
01473 //          calls flat_calculate_acc_and_jerk
01474 //      REPLACED by grape4/6_calculate_acc_and_jerk in kira if GRAPE flag set
01475 // hdyn::top_level_node_epilogue_force_calculation
01476 //      performs remainder (non-GRAPE) of force calculation
01477 //          calls calculate_partial_acc_and_jerk
01478 //
01479 
01480 //
01481 // Calling sequences (schematic, "exact" = false)
01482 //
01483 //                               calculate_acc_and_jerk
01484 //
01485 //                                          |
01486 //                                          |
01487 //                     ------------------------------------------
01488 //                    |                                          |
01489 //                    v                                          v
01490 //
01491 // calculate_acc_and_jerk_on_top_level_node   calculate_acc_and_jerk_on_low_level_node
01492 //
01493 //                    |                                          |
01494 //                    v                                          v
01495 //
01496 //     flat_calculate_acc_and_jerk               calculate_partial_acc_and_jerk
01497 //      (GRAPE-style, or GRAPE itself)           (relative motion, perturbation)
01498 //                  +
01499 //     calculate_partial_acc_and_jerk                            |
01500 //       (perturbations, etc.)                                   |
01501 //                                                               |
01502 //                   |                                           |
01503 //                    -------------------------------------------
01504 //                                          |
01505 //                                          |
01506 //                                          v
01507 //
01508 //                           (calculate_partial_acc_and_jerk)
01509 //
01510 //                                          |
01511 //                                          |
01512 //                    -------------------------------------------
01513 //                   |                                           |
01514 //                   v                                           v
01515 //
01516 // calculate_partial_acc_and_jerk_on_leaf        calculate_partial_acc_and_jerk
01517 //                                                 (RECURSIVE, for acc and jerk
01518 //                   |                                         on a node)
01519 //                   |
01520 //                   |-------------------------------------------
01521 //                   |                                           |
01522 //                   v                                           v
01523 //
01524 //     perturber_acc_and_jerk_on_leaf       tree_walk_for_partial_acc_and_jerk_on_leaf
01525 //       (if perturber list used)
01526 //                                                               |
01527 //                   |-------------------------------------------
01528 //                   |                                           |
01529 //                   v                                           v
01530 //
01531 //      accumulate_acc_and_jerk             tree_walk_for_partial_acc_and_jerk_on_leaf
01532 //                                            (RECURSIVE, if source node is not
01533 //                                                        a leaf)
01534 //
01535 
01536 
01537 //=============================================================================
01538 //  functions for calculating acceleration & jerk, for given nodes this and b
01539 //=============================================================================
01540 
01541 //-----------------------------------------------------------------------------
01542 // accumulate_acc_and_jerk:  Accumulate acc, jerk, and potential
01543 //           from particle b.  Note that the relative position and
01544 //           velocity of b relative to the calling particle are
01545 //           calculated by the caller and passed as arguments.
01546 //           The only reason to pass b here is to access its mass.
01547 //
01548 //           Note: this function is only referenced from three places:
01549 //
01550 //              flat_calculate_acc_and_jerk()                   --> direct
01551 //              perturber_acc_and_jerk_on_leaf()                --> indirect
01552 //              tree_walk_for_partial_acc_and_jerk_on_leaf()    --> indirect
01553 //
01554 //-----------------------------------------------------------------------------
01555 
01556 inline void accumulate_acc_and_jerk(hdyn* b,
01557                                     vector& d_pos,
01558                                     vector& d_vel,
01559                                     real eps2,
01560                                     vector& a,
01561                                     vector& j,
01562                                     real& p,
01563                                     real& distance_squared)
01564 {
01565     distance_squared = d_pos * d_pos;
01566     real r2inv = 1.0 / (distance_squared + eps2);
01567     real a3 = -3.0 * (d_pos * d_vel) * r2inv;
01568     real mrinv = b->get_mass() * sqrt(r2inv);
01569     p -= mrinv;
01570     real mr3inv = mrinv * r2inv;
01571     a += mr3inv * d_pos;
01572     j += mr3inv * (d_vel + a3 * d_pos);
01573 }
01574 
01575 
01576 //-----------------------------------------------------------------------------
01577 // flat_calculate_acc_and_jerk: calculate acc and jerk on top node 'this'
01578 // due to all other top-level nodes in the system except 'mask', always in
01579 // the point-mass approximation, optionally determining a perturber list.
01580 //
01581 // (Overloaded function)
01582 //-----------------------------------------------------------------------------
01583 
01584 void hdyn::flat_calculate_acc_and_jerk(hdyn * b,        // root node
01585                                        bool make_perturber_list)
01586 {
01587     if (diag->ev_function_id && diag->check_diag(this)) {
01588         cerr << "    flat_calculate_acc_and_jerk for "
01589              << format_label() << endl;
01590     }
01591 
01592     acc = jerk = 0;
01593     pot = 0;
01594 
01595     // Initialize both nn and coll:
01596 
01597     nn = NULL;
01598     d_nn_sq = VERY_LARGE_NUMBER;
01599     coll = NULL;
01600     d_coll_sq = VERY_LARGE_NUMBER;
01601 
01602     real distance_squared;
01603 
01604     for_all_daughters(hdyn, b, bi)      
01605         if (bi != this) {
01606 
01607             vector d_pos = bi->get_pred_pos() - pred_pos;
01608             vector d_vel = bi->get_pred_vel() - pred_vel;
01609             accumulate_acc_and_jerk(bi,                         // (inlined)
01610                                     d_pos, d_vel,
01611                                     eps2, acc, jerk, pot, distance_squared);
01612 
01613             // Note that the nn and coll passed to update_nn_coll here
01614             // are the actual pointers, so this update changes the
01615             // data in b.
01616 
01617             real sum_of_radii = get_sum_of_radii(this, bi);
01618             update_nn_coll(this, 1,             // (1 = ID)     // (inlined)
01619                            distance_squared, bi, d_nn_sq, nn,
01620                            sum_of_radii, d_coll_sq, coll);
01621 
01622             // Note: we do *not* take the slowdown factor into account
01623             //       when computing the perturber list.
01624 
01625             // See equivalent code for use with GRAPE-4 in
01626             // hdyn_grape4.C/get_neighbors_and_adjust_h2.
01627 
01628             if (make_perturber_list
01629                 && is_perturber(this, bi->mass,                 // (inlined)
01630                                 distance_squared,
01631                                 perturbation_radius_factor)) {
01632                 if (n_perturbers < MAX_PERTURBERS) {
01633                     perturber_list[n_perturbers] = bi;
01634 #if 0
01635                     cerr << "added " << bi->format_label();
01636                     cerr << " to perturber list of "
01637                          << format_label()
01638                          << endl;
01639 #endif
01640                 }
01641                 n_perturbers++;
01642             }
01643         }
01644 }
01645 
01646 void hdyn::perturber_acc_and_jerk_on_leaf(vector &a,
01647                                           vector &j,
01648                                           real &p,
01649                                           real &p_d_nn_sq,
01650                                           hdyn *&p_nn,
01651                                           hdyn *pnode,
01652                                           hdyn *step_node)
01653 {
01654     // Calculate acc and jerk on leaf 'this' due to the perturber
01655     // list associated with node pnode.
01656 
01657     // The input arguments p_d_nn_sq and p_nn may be the actual
01658     // d_nn_sq and nn, or copies.
01659 
01660     if (diag->ev_function_id && diag->check_diag(this)) {
01661         cerr << "        perturber_acc_and_jerk_on_leaf for "
01662              << format_label() << endl;
01663     }
01664 
01665     dbg_message("perturber_acc_and_jerk_on_leaf", this);
01666 
01667     a = j = 0.0;
01668     p = 0;
01669 
01670     // Do *not* set p_d_nn_sq = VERY_LARGE_NUMBER here!
01671     //                                          (Steve, 11/00)
01672 
01673     d_coll_sq = VERY_LARGE_NUMBER;
01674 
01675     if (!pnode->valid_perturbers || pnode->n_perturbers <= 0)
01676         return;
01677 
01678     // Added &hdyn:: to four "hdyn_something_relative_to_root" (Steve, 6/27/97).
01679 
01680     // Removed all four "hdyn_something_relative_to_root" references, which are
01681     // quite inefficient (Steve, 8/20/98).
01682 
01683     // Determine absolute position and velocity of 'this', using explicit code.
01684 
01685     vector d_pos = -pred_pos;
01686     vector d_vel = -pred_vel;
01687 
01688     hdyn* par = get_parent();
01689     if (par) {
01690         hdyn* gpar = par->get_parent();
01691         while (gpar) {
01692             d_pos -= par->pred_pos;
01693             d_vel -= par->pred_vel;
01694             par = gpar;
01695             gpar = gpar->get_parent();
01696         }
01697     }
01698 
01699     // vector d_pos = -hdyn_something_relative_to_root(this,
01700     //                                              &hdyn::get_pred_pos);
01701     // vector d_vel = -hdyn_something_relative_to_root(this,
01702     //                                              &hdyn::get_pred_vel);
01703 
01704     // Loop over the perturber list.
01705 
01706     vector d_pos_p;
01707     vector d_vel_p;
01708 
01709     bool reset = false;
01710 
01711     for (int i = 0; i < pnode->n_perturbers; i++) {
01712 
01713         hdyn *bb = pnode->perturber_list[i];
01714 
01715         if (!bb || !bb->is_valid()) {
01716 
01717             // Should not occur, and the logic in the calling function
01718             // calculate_partial_acc_and_jerk_on_leaf assumes that it
01719             // does not occur (so we can't just quit here).  Use any
01720             // valid perturbers on the list, but flag a problem and
01721             // force recomputation of the list.
01722 
01723             if (!reset) {
01724 
01725                 cerr << endl
01726                      << "warning: perturber_acc_and_jerk_on_leaf:"
01727                      << endl
01728                      << "         NULL or invalid perturber "
01729                      << bb << " for node "
01730                      << format_label() << " at time " << time
01731                      << endl
01732                      << "         forcing recomputation of perturber list"
01733                      << endl;
01734 
01735                 pnode->valid_perturbers = false;
01736                 reset = true;
01737             }
01738 
01739             // Go on to the next perturber on the list.
01740 
01741             continue;
01742         }
01743 
01744         // Seems to be necessary the first time through -- perturbers are
01745         // apparently not (necessarily) predicted elsewhere (Steve 8/13/98):
01746 
01747         if (!elder_sister)
01748             predict_loworder_all(bb, time);
01749 
01750         // Determine absolute position and velocity of perturber, using
01751         // explicit code rather than hdyn_something_relative_to_root.
01752 
01753 //      if (bb->is_top_level_node()) {          // (time saver)
01754 //          d_pos_p = d_pos + bb->pred_pos;
01755 //          d_vel_p = d_vel + bb->pred_vel;
01756 //      } else {
01757 //          d_pos_p = d_pos +
01758 //              hdyn_something_relative_to_root(bb, &hdyn::get_pred_pos);
01759 //          d_vel_p = d_vel +
01760 //              hdyn_something_relative_to_root(bb, &hdyn::get_pred_vel);
01761 //      }
01762 
01763         d_pos_p = d_pos + bb->pred_pos;
01764         d_vel_p = d_vel + bb->pred_vel;
01765 
01766         hdyn* par = bb->get_parent();
01767         if (par) {
01768             hdyn* gpar = par->get_parent();
01769             while (gpar) {
01770                 d_pos_p += par->pred_pos;
01771                 d_vel_p += par->pred_vel;
01772                 par = gpar;
01773                 gpar = gpar->get_parent();
01774             }
01775         }
01776 
01777         real d2_bb;
01778         
01779         accumulate_acc_and_jerk(bb, d_pos_p, d_vel_p,           // (inlined)
01780                                 eps2, a, j, p, d2_bb);
01781         
01782         // Note: p_d_nn_sq and p_nn come from the argument list,
01783         // and may be copies of d_nn_sq and nn, or the real thing.
01784         // However, d_coll_sq and coll are real, so this update
01785         // actually changes the coll data.
01786 
01787         real sum_of_radii = get_sum_of_radii(this, bb);
01788         update_nn_coll(this, 2,                                 // (inlined)
01789                        d2_bb, bb, p_d_nn_sq, p_nn,
01790                        sum_of_radii, d_coll_sq, coll);
01791     }
01792 
01793     step_node->inc_indirect_force(pnode->n_perturbers);         // bookkeeping
01794 }
01795 
01796 
01797 //-----------------------------------------------------------------------------
01798 // tree_walk_for_partial_acc_and_jerk_on_leaf:  Accumulate acc and jerk
01799 //      while traversing the portion of the tree under source node b,
01800 //      excluding everything under node mask (inclusive).  Vectors offset_pos
01801 //      and offset_vel MUST be the position and velocity of b relative to
01802 //      the field point (i.e. the point where acc and jerk are wanted).
01803 //
01804 //      On return, a, j, and p are the acc, jerk, and pot at the field
01805 //      point, updated to include the effect of b (modulo the value of
01806 //      point_mass_flag), p_d_nn_sq is the minimum distance squared
01807 //      from any component of b to the field point, and p_nn is an
01808 //      updated pointer to the the nearest neighbor of the field point.
01809 //
01810 //      In the present code, the field point is the location of 'this'.
01811 //      It is ASSUMED that 'mask' is 'this' or an ancestor of 'this'.
01812 //-----------------------------------------------------------------------------
01813 
01814 // This routine calculates the force by simple recursion.  If a leaf is
01815 // reached, calculate the force.  Otherwise descend the tree.
01816 //
01817 // NOTE: RECURSIVE LOOPS are quite inefficient on some (most?) systems.
01818 // However, by construction, this function is only used in circumstances
01819 // where a system-wide O(N) traversal of the system is NOT needed, so the
01820 // inefficiency is tolerable.  All functions that require O(N) operations
01821 // are coded as explicit loops (see calculate_acc_and_jerk_on_top_level_node).
01822 
01823 void hdyn::tree_walk_for_partial_acc_and_jerk_on_leaf(hdyn *b,
01824                                                       hdyn *mask,
01825                                                       vector& offset_pos,
01826                                                       vector& offset_vel,
01827                                                       vector& a,
01828                                                       vector& j,
01829                                                       real& p,
01830                                                       real& p_d_nn_sq,
01831                                                       hdyn * &p_nn,
01832                                                       bool point_mass_flag,
01833                                                       hdyn *step_node)
01834 {
01835     // The input arguments p_d_nn_sq and p_nn may be the actual
01836     // d_nn_sq and nn, or copies.
01837 
01838     if (diag->ev_function_id && diag->check_diag(this)) {
01839         cerr << "        tree_walk_for_partial_acc_and_jerk_on_leaf for "
01840              << format_label() << endl;
01841         cerr << "        b = " << b->format_label();
01842         cerr << ", mask = " << mask->format_label() << endl;
01843     }
01844 
01845     dbg_message("tree_walk_for_partial_acc_and_jerk_on_leaf", this, b);
01846 
01847     if (b == mask)
01848         return;
01849 
01850     // Stop descent of the tree at an unperturbed CM, *except* when
01851     // that CM is the parent of 'this' node (to get the correct 2-body
01852     // internal force).
01853 
01854     if (b->is_leaf()
01855         || (b->is_top_level_node() && point_mass_flag)
01856         || (b->kep != NULL && b != parent)
01857         ) {
01858 
01859         if (b != this) {
01860             real d2;
01861 
01862             accumulate_acc_and_jerk(b, offset_pos, offset_vel,    // (inlined)
01863                                     eps2, a, j, p, d2);
01864 
01865             step_node->inc_indirect_force();
01866 
01867             // Note: p_d_nn_sq and p_nn come from the argument list,
01868             // and may be copies of d_nn_sq and nn, or the real thing.
01869             // However, d_coll_sq and coll are real, so this update
01870             // actually changes the coll data.
01871 
01872             real sum_of_radii = get_sum_of_radii(this, b);
01873             update_nn_coll(this, 3,
01874                            d2, b, p_d_nn_sq, p_nn,
01875                            sum_of_radii, d_coll_sq, coll);
01876 
01877         } else {
01878 
01879             // Probably can't occur...
01880 
01881             cerr << "In tree_walk_for_partial_acc_and_jerk_on_leaf:\n";
01882             cerr << "b = "; b->pretty_print_node(cerr);
01883             cerr << "  mask = "; mask->pretty_print_node(cerr);
01884 
01885         }
01886 
01887     } else {
01888 
01889         for_all_daughters(hdyn, b, d) {                // recursive loop
01890 
01891             vector ppos = offset_pos + d->get_pred_pos();
01892             vector pvel = offset_vel + d->get_pred_vel();
01893 
01894             tree_walk_for_partial_acc_and_jerk_on_leaf(d, mask,
01895                                                        ppos, pvel,
01896                                                        a, j, p,
01897                                                        p_d_nn_sq, p_nn,
01898                                                        point_mass_flag,
01899                                                        step_node);
01900         }
01901     }
01902 }
01903 
01904 //=============================================================================
01905 //  functions for determining which nodes should exert forces on each other
01906 //=============================================================================
01907 
01908 //-----------------------------------------------------------------------------
01909 // calculate_partial_acc_and_jerk_on_leaf:  Calculate the force on `this' from
01910 //      all particles under `top', excluding everything under node mask.
01911 //      This routine first calculates the position of `top' relative to `this',
01912 //      then calls tree_walk_for_partial_acc_and_jerk_on_leaf.
01913 //-----------------------------------------------------------------------------
01914 
01915 void hdyn::calculate_partial_acc_and_jerk_on_leaf(hdyn * top,
01916                                                   hdyn * common,
01917                                                   hdyn * mask,
01918                                                   vector& a,
01919                                                   vector& j,
01920                                                   real & p,
01921                                                   real & p_d_nn_sq,
01922                                                   hdyn * &p_nn,
01923                                                   bool point_mass_flag,
01924                                                   hdyn * pnode,
01925                                                   hdyn * step_node)
01926 {
01927     // The input arguments p_d_nn_sq and p_nn may be the actual
01928     // d_nn_sq and nn, or copies.
01929 
01930     if (diag->ev_function_id && diag->check_diag(this)) {
01931         cerr << "      calculate_partial_acc_and_jerk_on_leaf for "
01932              << format_label() << endl;
01933         cerr << "      top = " << top->format_label();
01934         cerr << ", common = " << common->format_label();
01935         cerr << ", mask = " << mask->format_label() << endl;
01936     }
01937 
01938     dbg_message("calculate_partial_acc_and_jerk_on_leaf", this);
01939     dbg_message("                 from particle", top);
01940 
01941     a = j = 0.0;
01942     p = 0;
01943 
01944     // Determine absolute position and velocity of "this":
01945 
01946     vector d_pos = 0;
01947     vector d_vel = 0;
01948 
01949     // Loop over the appropriate perturber list for external perturbations,
01950     // and traverse the tree below top, masked by mask, for internal
01951     // perturbations due to other members of the parent clump.
01952 
01953     if (pnode)
01954         perturber_acc_and_jerk_on_leaf(a, j, p,
01955                                        p_d_nn_sq, p_nn,
01956                                        pnode, step_node);
01957 
01958     if (top == mask) return;
01959 
01960     hdyn *b;
01961     for (b = this; b != common; b = b->get_parent()) {
01962         d_pos -= b->get_pred_pos();
01963         d_vel -= b->get_pred_vel();
01964     }
01965 
01966     for (b = top; b != common; b = b->get_parent()) {
01967         d_pos += b->get_pred_pos();
01968         d_vel += b->get_pred_vel();
01969     }
01970 
01971     tree_walk_for_partial_acc_and_jerk_on_leaf(top, mask, d_pos, d_vel,
01972                                                a, j, p,
01973                                                p_d_nn_sq, p_nn,
01974                                                point_mass_flag,
01975                                                step_node);
01976 }
01977 
01978 //-----------------------------------------------------------------------------
01979 // calculate_partial_acc_and_jerk:  Calculate the force on `this' from
01980 //      all particles under `top', excluding everything under node mask.
01981 //      This routine recursively calls itself and calculates the weighted
01982 //      average of all forces on its daughters.  For a leaf, it calls
01983 //      calculate_partial_acc_and_jerk_on_leaf.  The recursion is OK in
01984 //      this case because it is used only to descend within nodes, not to
01985 //      traverse the entire system.
01986 //-----------------------------------------------------------------------------
01987 
01988 void hdyn::calculate_partial_acc_and_jerk(hdyn * top,
01989                                           hdyn * common,
01990                                           hdyn * mask,
01991                                           vector& a,
01992                                           vector& j,
01993                                           real & p,
01994                                           real & p_d_nn_sq,
01995                                           hdyn * &p_nn,
01996                                           bool point_mass_flag,
01997                                           hdyn* pnode,
01998                                           hdyn* step_node)
01999 {
02000     // The input arguments p_d_nn_sq and p_nn may be the actual
02001     // d_nn_sq and nn, or copies.  If this is a node, the nn returned
02002     // is the closer of its component nns.
02003 
02004     if (diag->ev_function_id && diag->check_diag(this)) {
02005         cerr << "    calculate_partial_acc_and_jerk for "
02006              << format_label() << endl;
02007         cerr << "    top = " << top->format_label();
02008         cerr << ", common = " << common->format_label();
02009         cerr << ", mask = " << mask->format_label() << endl;
02010     }
02011 
02012     dbg_message("calculate_partial_acc_and_jerk", this);
02013 
02014     // Operations of this function:
02015     //
02016     // (1) Calculate acc and jerk on a top-level node due to all
02017     //     other top-level nodes, in the point-mass approximation
02018     //     (this is what the GRAPE hardware does...).
02019     // (2) Calculate masked acc and jerk on a node due to the portion
02020     //     of the tree below top.
02021     // (3) Calculate acc and jerk on "this" node due to the particles
02022     //     in its perturber list, recursively looping over daughters.
02023     // (4) Calculate acc and jerk on "this" node due to the particles
02024     //     in its perturber list, in the point-mass approximation.
02025     // (5) Determine the perturber list of a binary during its
02026     //     center-of-mass step.
02027 
02028     a = j = 0;
02029     p = 0;
02030     real mtot = 0;
02031 
02032     // Don't resolve an unperturbed binary (Steve, 8/20/98).
02033 
02034     if (is_leaf() || point_mass_flag || get_oldest_daughter()->kep) {
02035 
02036         calculate_partial_acc_and_jerk_on_leaf(top, common, mask,
02037                                                a, j, p,
02038                                                p_d_nn_sq, p_nn,
02039                                                point_mass_flag,
02040                                                pnode, step_node);
02041     } else {
02042 
02043         vector a_daughter, a_save;
02044         vector j_daughter;
02045         real p_daughter;
02046         real m_daughter;
02047 
02048         for_all_daughters(hdyn, this, bd) {
02049 
02050             // Note from Steve, 7/24/98.  Because of the way nns and
02051             // colls are passed around, if we compute the acc and jerk
02052             // on a leaf in order to obtain the acc and jerk on its parent
02053             // node, the nn information of the leaf will be preserved
02054             // because p_nn here is actually the node nn, but the coll
02055             // information will be overwritten.  If the coll of bd is the
02056             // other binary component (as is often the case), an error
02057             // will be made.
02058 
02059             // The best solution would be to treat colls in exactly the
02060             // same way as nn pointers.  For now, however, we just save
02061             // and restore the coll data of any node referenced here.
02062 
02063             hdyn* save_coll = bd->get_coll();           // save the
02064             real save_d_coll_sq = bd->get_d_coll_sq();  // coll data
02065 
02066             m_daughter = bd->get_mass();
02067             bd->calculate_partial_acc_and_jerk(top, common, mask,
02068                                                a_daughter,
02069                                                j_daughter, p_daughter,
02070                                                p_d_nn_sq, p_nn,
02071                                                point_mass_flag,
02072                                                pnode, step_node);
02073             a += m_daughter * a_daughter;
02074             j += m_daughter * j_daughter;
02075             p += m_daughter * p_daughter;
02076             mtot += m_daughter;
02077 
02078             if (bd->get_elder_sister() == NULL)         // in case we want
02079                 a_save = a_daughter;                    // the perturbation
02080 
02081             bd->set_coll(save_coll);                    // restore the
02082             bd->set_d_coll_sq(save_d_coll_sq);          // coll data
02083 
02084         }
02085         real minv = 1 / mtot;
02086         a *= minv;
02087         j *= minv;
02088         p *= minv;
02089 
02090         // Special case:  It is helpful to know the perturbation on a
02091         // top-level node whose neighbor list has overflowed (or which
02092         // is being calculated exactly).  In that case, the following
02093         // should all be true:
02094         //
02095         //      top = common = root
02096         //      mask = this
02097         //      this is top-level node
02098         //      point_mass_flag = false
02099         //      pnode = NULL
02100         //
02101         //      valid_perturbers = false
02102         //      n_perturbers <= 0
02103         //
02104 
02105         // In this case, we should set perturbation_squared before
02106         // leaving the function.  Note that perturbation_squared is
02107         // attached to the daughter nodes, but the perturber list,
02108         // valid_perturbers, and n_perturbers are properties of the
02109         // parent.
02110 
02111         if (!point_mass_flag
02112             && !pnode
02113             && top->is_root()
02114             && common == top
02115             && mask == this
02116             && !valid_perturbers) {
02117 
02118             // (We probably don't need to check all these!)
02119 
02120             // "Daughter" quantities refer to the younger daughter on
02121             // leaving the loop.  The vector a_2b is the two-body
02122             // relative acceleration of the components (computed here
02123             // in the point-mass approximation).
02124 
02125             hdyn* od = get_oldest_daughter();
02126             hdyn* yd = get_oldest_daughter()->get_younger_sister();
02127 
02128             // Perturbation_squared does *not* contain the slowdown factor.
02129 
02130             real pscale = m_daughter / (m_daughter + mass);
02131             real r2 = square(od->pos - yd->pos);
02132             od->perturbation_squared =
02133                         square(pscale * (a_save - a_daughter))
02134                                 * square(r2/mass);
02135         }
02136     }
02137 }
02138 
02139 
02140 // check_add_perturber:  check if p is a perturber of 'this' and add it
02141 //                       to the perturber list if necessary.
02142 
02143 void hdyn::check_add_perturber(hdyn* p, vector& this_pos)
02144 {
02145     vector ppos = hdyn_something_relative_to_root(p, &hdyn::get_pred_pos);
02146 
02147 //     bool print = get_top_level_node()->n_leaves() >= 4;
02148 //     if (print) {
02149 //      cerr << "check_add_perturber:  checking " << p->format_label()
02150 //           << " at time " << system_time;
02151 //      cerr << " for node " << format_label() << endl;
02152 //      PRL(mass);
02153 //      PRL(square(this_pos - ppos));
02154 //      PRL(perturbation_radius_factor);
02155 //    }
02156 
02157     // Note: we do *not* take the slowdown factor into account
02158     //       when computing the perturber list.
02159 
02160     if (is_perturber(this, p->mass,
02161                      square(this_pos - ppos),
02162                      perturbation_radius_factor)) {       // (inlined)
02163 
02164         // If p is a compound system, include all components, without
02165         // checking if they satisfy the perturbation criteria individually.
02166 
02167         if (RESOLVE_UNPERTURBED_PERTURBERS) {
02168             for_all_leaves(hdyn, p, pp) {
02169                 if (n_perturbers < MAX_PERTURBERS)
02170                     perturber_list[n_perturbers] = pp;
02171                 n_perturbers++;
02172             }
02173         } else {
02174             for_all_leaves_or_unperturbed(hdyn, p, pp) {
02175                 if (n_perturbers < MAX_PERTURBERS)
02176                     perturber_list[n_perturbers] = pp;
02177                 n_perturbers++;
02178             }
02179         }
02180 //      if (print) cerr << "...accepted" << endl;
02181     } //else
02182 //      if (print) cerr << "...rejected" << endl;
02183 }
02184 
02185 void hdyn::create_low_level_perturber_list(hdyn* pnode)
02186 {
02187     new_perturber_list();
02188 
02189     perturbation_radius_factor
02190         = define_perturbation_radius_factor(this, gamma23);
02191 
02192     vector this_pos = hdyn_something_relative_to_root(this,
02193                                                       &hdyn::get_pred_pos);
02194     // First add sisters, aunts, etc. up to pnode...
02195 
02196     hdyn* p = this;
02197     while (p != pnode) {
02198         check_add_perturber(p->get_binary_sister(), this_pos);
02199         p = p->get_parent();
02200     }
02201 
02202     // ...then accept any node on the pnode list that satisfies the
02203     // inner node perturbation criterion.
02204 
02205     for (int i = 0; i < pnode->n_perturbers; i++)
02206         check_add_perturber(pnode->perturber_list[i], this_pos);
02207 
02208     valid_perturbers = true;
02209 
02210     if (n_perturbers > MAX_PERTURBERS)
02211 
02212         remove_perturber_list();
02213 
02214     else {
02215 
02216 //      if (get_top_level_node()->n_leaves() >= 4) {
02217 //          cerr << ">>>> this->"; PRL(format_label());
02218 //          cerr << ">>>> "; PRL(pnode->format_label());
02219 //          cerr << ">>>> "; PRL(pnode->n_perturbers);
02220 //          cerr << ">>>> this->"; PRL(n_perturbers);
02221 //          print_perturber_list();
02222 //      }
02223 
02224     }
02225 }
02226 
02227 //-----------------------------------------------------------------------------
02228 // calculate_acc_and_jerk_on_low_level_node:  Calculate the acc (etc) on
02229 //         one component of a binary node in the following steps:
02230 //
02231 // First, calculate the perturbation on the relative motion as the
02232 // the difference between the accelerations of the node and that of
02233 // its sister, multiplied by the fractional mass of the sister.
02234 // These accelerations do not include the mutual interaction between
02235 // the node and its sister.
02236 //
02237 // Second, calculate the acceleration due to the sister.
02238 //
02239 // Third, calculate the total acceleration, given by the sum of the
02240 // perturbation acc and the acc from the sister.
02241 //
02242 // Note that all three components are calculated by one call to
02243 // calculate_partial_acc_and_jerk.
02244 //-----------------------------------------------------------------------------
02245 
02246 void hdyn::calculate_acc_and_jerk_on_low_level_node()
02247 {
02248     if (diag->ev_function_id && diag->check_diag(this)) {
02249         cerr << "  calculate_acc_and_jerk_on_low_level_node for "
02250              << format_label() << endl;
02251     }
02252 
02253     dbg_message("calculate_acc_and_jerk_on_low_level_node", this);
02254 
02255     hdyn *root = get_root();
02256 
02257     if (parent->get_oldest_daughter()->get_younger_sister()
02258                                      ->get_younger_sister() != NULL)
02259         err_exit("calculate_acc_and_jerk_on_low_level_node: Not binary node");
02260 
02261     hdyn *sister = get_binary_sister();
02262 
02263     real mtot = 0;
02264 
02265     vector apert1, jpert1;
02266     vector apert2, jpert2;
02267     real p_dummy;
02268     hdyn *top;
02269 
02270     // New formulation of perturber lists introduced by Steve 8/98:
02271 
02272     // Perturber list may not necessarily be associated with the
02273     // top-level node.  Any CM node above this node may have a
02274     // valid perturber list.  Use the lowest-level one.
02275 
02276     // Now pass a pointer to the node containing the perturber list
02277     // (NULL ==> no valid list) instead of bool "use_perturber_list"
02278     // flag.  Also, since the low-level perturber list includes all
02279     // members of the parent "clump" except those below the parent
02280     // node, we need only descend the tree below pnode to pick up the
02281     // remaining perturbations.
02282 
02283     hdyn* top_level = get_top_level_node();
02284     hdyn* pnode = find_perturber_node();
02285 
02286     if (pnode) {
02287         top = pnode;
02288         kc->pert_step++;
02289         kc->pert_with_list += pnode->n_perturbers;
02290     } else {
02291         top = root;
02292         kc->pert_without_list++;
02293     }
02294 
02295     // Acceleration and jerk on this component due to rest of system:
02296 
02297     nn = NULL;
02298     d_nn_sq = VERY_LARGE_NUMBER;
02299 
02300     calculate_partial_acc_and_jerk(top, top, get_parent(),
02301                                    apert1, jpert1, p_dummy,
02302                                    d_nn_sq, nn,
02303                                    !USE_POINT_MASS,             // explicit loop
02304                                    pnode,                       // with list
02305                                    this);                       // node to charge
02306 
02307     // Acceleration and jerk on other component due to rest of system:
02308 
02309     sister->set_nn(NULL);
02310     sister->set_d_nn_sq(VERY_LARGE_NUMBER);
02311 
02312     sister->calculate_partial_acc_and_jerk(top, top, get_parent(),
02313                                            apert2, jpert2, p_dummy,
02314                                            sister->d_nn_sq, sister->nn,
02315                                            !USE_POINT_MASS,     // explicit loop
02316                                            pnode,               // with list
02317                                            this);               // node to charge
02318 
02319     // Note:  The first two calls to calculate_partial_acc_and_jerk pass
02320     // the d_nn_sq and nn from the hdyn, so the hdyn data are actually
02321     // updated by update_nn_coll.  The third call (below) passes local
02322     // variables, so it has no direct effect on d_nn_sq and nn.  (Test
02323     // afterwards if d_nn_sq and nn must be updated.)  The coll data
02324     // are always updated by these calls.
02325 
02326     // Relative acceleration and jerk due to other (sister) component:
02327 
02328     vector a_2b, j_2b;
02329     real p_2b;
02330 
02331     hdyn *p_nn_sister;
02332     real d_min_sister = VERY_LARGE_NUMBER;
02333     calculate_partial_acc_and_jerk(get_parent(), get_parent(), this,
02334                                    a_2b, j_2b, p_2b,
02335                                    d_min_sister, p_nn_sister,
02336                                    !USE_POINT_MASS,
02337                                    NULL,                        // no perturbers
02338                                    this);                       // node to charge
02339 
02340     real m2 = sister->get_mass();
02341     real pscale = m2 / (m2 + mass);
02342 
02343     set_acc_and_jerk_and_pot(a_2b + pscale * (apert1 - apert2) * get_kappa(),
02344                              j_2b + pscale * (jpert1 - jpert2), p_2b);
02345 
02346     // Note that perturbation_squared does *not* contain the slowdown factor.
02347 
02348     perturbation_squared = square(pscale * (apert1 - apert2)) / square(a_2b);
02349 
02350 #if 0
02351     if (is_low_level_node()) {
02352         PRC(time); PRL(format_label());
02353         PRL(abs(apert1 - apert2)/abs(jpert1 - jpert2));
02354     }
02355 #endif
02356 
02357 #if 0
02358     if (slow) {
02359         PRL(pred_pos);
02360         PRL(get_binary_sister()->pred_pos);
02361         PRL(a_2b);
02362 
02363         real m2 = get_binary_sister()->mass;
02364         vector sep = pred_pos * (1 + mass/m2);
02365         real r2 = sep*sep;
02366         vector a2 = -m2*sep / (r2*sqrt(r2));
02367         PRL(a2);
02368 
02369         PRL(pscale * (apert1 - apert2) * get_kappa());
02370     }
02371 #endif
02372 
02373     if (d_nn_sq > d_min_sister) {
02374 
02375         nn = p_nn_sister;
02376         d_nn_sq = d_min_sister;
02377 
02378 #if 0
02379         if (get_real_system_time() > 13.62265) {
02380             cerr << "update_nn_coll(" << 999 << "): ";
02381             PRL(format_label());
02382             PRI(4); PRC(nn->format_label()); PRL(sqrt(d_nn_sq));
02383         }
02384 #endif
02385     }
02386 
02387     // if (name_is("xxx")) {
02388     //     print_nn(this, 2);
02389     //     print_coll(this, 2);
02390     // }
02391 
02392     // The above procedure will fail to correctly determine the sister's
02393     // nearest neighbor.  Correct this here.  Note that, in this case,
02394     // the sister's nn may be a node (this), not a leaf; however, the
02395     // stored distance will accurately reflect the actual distance to the
02396     // nn leaf.  Too inconvenient and inefficient to routinely descend the
02397     // tree below this to (re)locate the nn leaf here -- however, we MUST
02398     // take this "feature" into account in new_sister_node (hdyn_tree.C)
02399     // when assessing the need for tree reconfiguration.
02400 
02401     if (d_min_sister < sister->get_d_nn_sq()) {
02402 
02403         sister->set_nn(this);
02404         sister->set_d_nn_sq(d_min_sister);
02405 
02406         // (OK to have sister->nn be a CM node.)
02407     }
02408 
02409     if ((nn == NULL) || (get_binary_sister()->get_nn() == NULL)) {
02410         cerr << "calculate_acc_and_jerk_on_low_level_node:  nn = NULL:\n";
02411         PRL(top_level->n_perturbers);
02412         pp3(this, cerr);
02413         pp3(top_level, cerr);
02414     }
02415 
02416     valid_perturbers = false;
02417 
02418     if (ALLOW_LOW_LEVEL_PERTURBERS && pnode) {
02419 
02420         // Create/revise the low-level perturber list(s).
02421 
02422         // Could revise list as we compute the force, but the distances used
02423         // should be from the center of mass, not from one of the components.
02424         // This may be a little less efficient, but it is much easier to code,
02425         // so keep it for now.  (Steve, 8/98)
02426 
02427         if (is_parent())
02428             create_low_level_perturber_list(pnode);
02429 
02430         if (get_binary_sister()->is_parent())
02431             get_binary_sister()->
02432                 create_low_level_perturber_list(pnode);
02433     }
02434 }
02435 
02436 local inline int n_leaves_or_unperturbed(hdyn* b)
02437 {
02438     // Like n_leaves, but counting fully unperturbed center of mass nodes
02439     // as single particles.
02440 
02441     hdyn* od = b->get_oldest_daughter();
02442     if (od == NULL || (od->get_kepler() && od->get_fully_unperturbed())) {
02443         return 1;
02444     } else {
02445         int n = 0;
02446         for_all_daughters(hdyn, b, bb)
02447             n += n_leaves_or_unperturbed(bb);
02448         return n;
02449     }
02450 }
02451 
02452 // expand_nodes:  expand center-of-mass nodes on a perturber list into
02453 //                single stars (i.e. leaves).
02454 //
02455 //                NOTE from Steve, 8/20/98:  we (optionally) no longer
02456 //                expand fully unperturbed binaries.  This leaves the system
02457 //                in a slightly dangerous state, as it is possible that
02458 //                an unperturbed binary center of mass perturbing another
02459 //                binary may vanish before it can be resolved back into
02460 //                its components.  This is unlikely to occur dynamically,
02461 //                as the binary will first become perturbed and the
02462 //                components restored on the next center of mass time step.
02463 //                However, it can (and does!) happen.  To avoid this, all
02464 //                unperturbed binaries are expanded in integrate_unperturbed
02465 //                if the unperturbed motion is terminated.
02466 //
02467 //                Nodes may be deleted as a result of stellar evolution (see
02468 //                merge_nodes), but the components are merged into the center
02469 //                of mass in that case.  (Nodes are also deleted as they
02470 //                escape, but this affects all particles, not just binaries.)
02471 //
02472 //                Binaries undergoing partial unperturbed motion (pericenter
02473 //                reflection) are always expanded.
02474 
02475 local inline bool expand_nodes(int &n, hdyn ** list, bool debug = false)
02476 {
02477      if (debug)
02478          cerr << endl << "in expand_nodes, n = " << n << endl;
02479 
02480     for (int i = 0; i < n; i++) {
02481 
02482         if (debug)
02483             cerr << i << ".  " << list[i] << "  "
02484                  << list[i]->format_label() << endl;
02485 
02486         if (!list[i]->is_leaf()) {
02487 
02488             if (debug)
02489                 cerr << "not a leaf" << endl;
02490 
02491             int nl;
02492 
02493             if (RESOLVE_UNPERTURBED_PERTURBERS)
02494                 nl = list[i]->n_leaves();
02495             else
02496                 nl = n_leaves_or_unperturbed(list[i]);          // <-- new
02497 
02498             if (debug) {
02499                 pp3(list[i]->get_top_level_node());
02500                 PRL(nl);
02501             }
02502 
02503             if (n + nl - 1 > MAX_PERTURBERS)
02504                 return false;
02505 
02506             // Make room for the expansion.
02507 
02508             int j;
02509             for (j = n - 1; j > i; j--)
02510                 list[j + nl - 1] = list[j];
02511 
02512             // Add the new nodes to the list.
02513 
02514             j = 0;
02515             hdyn *tmp = list[i];
02516 
02517             if (debug)
02518                 PRL(tmp->format_label());
02519 
02520             if (RESOLVE_UNPERTURBED_PERTURBERS) {
02521                 for_all_leaves(hdyn, tmp, b) {
02522                     list[i + j] = b;
02523                     j++;
02524                 }
02525             } else {
02526 
02527                 for_all_leaves_or_unperturbed(hdyn, tmp, b) {   // <-- new
02528                     list[i + j] = b;
02529                     if (debug)
02530                         cerr << "added " << b->format_label() << endl;
02531                     j++;
02532                 }
02533             }
02534 
02535             n += nl - 1;
02536             i += nl - 1;
02537 
02538             if (debug && j != nl)
02539                 cerr << "expand_nodes -- oops... "
02540                      << j << " != " << nl << endl;
02541         }
02542     }
02543 
02544     return true;
02545 }
02546 
02547 //-----------------------------------------------------------------------------
02548 //  NOTE: calculate_acc_and_jerk_on_top_level_node is now split into three
02549 //  pieces to allow use of GRAPE hardware.
02550 //-----------------------------------------------------------------------------
02551 
02552 void hdyn::calculate_acc_and_jerk_on_top_level_node(bool exact)
02553 {
02554     if (diag->ev_function_id && diag->check_diag(this)) {
02555         cerr << "  calculate_acc_and_jerk_on_top_level_node for "
02556              << format_label() << endl;
02557     }
02558 
02559     top_level_node_prologue_for_force_calculation(exact);
02560 
02561     if (!exact) {
02562         top_level_node_real_force_calculation();
02563         top_level_node_epilogue_force_calculation();
02564     }
02565 
02566 }
02567 
02568 
02569 void hdyn::top_level_node_prologue_for_force_calculation(bool exact)
02570 {
02571     if (diag->ev_function_id && diag->check_diag(this)) {
02572         cerr << "  top_level_node_prologue_for_force_calculation for "
02573              << format_label() << endl;
02574     }
02575 
02576     hdyn *root = get_root();
02577     d_coll_sq = VERY_LARGE_NUMBER;
02578     coll = NULL;
02579 
02580     if (exact) {
02581 
02582         // Perform the entire force calculation.
02583 
02584         n_perturbers = 0;
02585         valid_perturbers = false;
02586 
02587         nn = NULL;
02588         d_nn_sq = VERY_LARGE_NUMBER;
02589 
02590         calculate_partial_acc_and_jerk(root, root, this,
02591                                        acc, jerk, pot, d_nn_sq, nn,
02592                                        !USE_POINT_MASS,
02593                                        NULL,            // no perturbers
02594                                        this);           // node to charge
02595 
02596     } else if (is_parent()) {
02597 
02598         // Set up computation of perturber list.
02599 
02600 //      if (system_time > 169.2975) {
02601 
02602 //          cerr << "hdyn_ev: " << 41121 << endl << flush;
02603 //          pp3(this);
02604 
02605 //          cerr << "about to make a new tmp real array" << endl << flush;
02606 //          real *xxx = new real[MAX_PERTURBERS];
02607 //          PRL(xxx);
02608 //          xxx[0] = 42;
02609 //          delete [] xxx;
02610 
02611 //          cerr << "about to make a new tmp hdynptr array" << endl << flush;
02612 //          hdynptr *yyy = new hdynptr[MAX_PERTURBERS];
02613 //          PRL(yyy);
02614 //          yyy[0] = (hdynptr)42;
02615 //          delete [] yyy;
02616 
02617 //          cerr << "about to make real hdynptr array" << endl << flush;
02618 //      }
02619 
02620         new_perturber_list();
02621 
02622 //      if (system_time > 169.2975) {
02623 //          PRL(perturber_list);
02624 //          cerr << "hdyn_ev: " << 41122 << endl << flush;
02625 //      }
02626 
02627         perturbation_radius_factor
02628                 = define_perturbation_radius_factor(this, gamma23);
02629 
02630 //      if (system_time > 169.2975) {
02631 //          PRL(perturbation_radius_factor);
02632 //          cerr << "hdyn_ev: " << 41123 << endl << flush;
02633 //      }
02634 
02635     }
02636 }
02637 
02638 void hdyn::top_level_node_real_force_calculation()
02639 {
02640     // ***************************************************************
02641     // **** This function is NEVER called if GRAPE is available   ****
02642     // **** -- it is replaced by grape4/6_calculate_acc_and_jerk. ****
02643     // ***************************************************************
02644 
02645     if (diag->ev_function_id && diag->check_diag(this)) {
02646         cerr << "  top_level_node_real_force_calculation for "
02647              << format_label() << endl;
02648     }
02649 
02650     // Special treatment of traversal of top level only (for
02651     // efficiency, and for GRAPE implementation).
02652 
02653     // Calculate forces first in the POINT-MASS approximation,
02654     // without using any perturber list, and construct a new list,
02655     // in the case of CM nodes.
02656 
02657     // Should NOT be called in exact case.
02658 
02659     hdyn * root = get_root();
02660 
02661     if (is_parent()) {
02662 
02663         if (get_oldest_daughter()->slow)
02664             clear_perturbers_slow_perturbed(this);
02665 
02666         n_perturbers = 0;
02667         valid_perturbers = true;
02668     }
02669 
02670     flat_calculate_acc_and_jerk(root, is_parent());
02671 
02672     if (nn == NULL) {
02673         pretty_print_node(cerr); cerr << " nn NULL after flat " << endl;
02674     }
02675 }
02676 
02677 void hdyn::top_level_node_epilogue_force_calculation()
02678 {
02679     if (diag->ev_function_id && diag->check_diag(this)) {
02680         cerr << "  top_level_node_epilogue_force_calculation for "
02681              << format_label() << endl;
02682     }
02683 
02684 #if 0
02685     if (time > 13.62265) {
02686         cerr << "top_level_node_epilogue_force_calculation(1): " << endl;
02687         PRC(format_label()); PRC(nn->format_label()); PRL(sqrt(d_nn_sq));
02688     }
02689 #endif
02690 
02691     // Should NOT be called in exact case.
02692 
02693     // Take care of the case where the nearest neighbor is
02694     // a complex node, since otherwise nn may disappear.
02695     // (Jan 21, 1998, JM and SPZ)
02696 
02697     // Exclude the case of nn = this (possible when GRAPE is used.)
02698     // (Sep 9, 1998, SLWM)
02699 
02700     if (nn != this) {
02701         while (nn->is_parent()) {
02702             // cerr << "nn correction for "; pretty_print_node(cerr);
02703             // cerr << "and  "; nn->pretty_print_node(cerr);
02704 
02705             nn = nn->get_oldest_daughter();
02706 
02707             // cerr << " as   ";   nn->pretty_print_node(cerr);
02708             // cerr << endl;
02709         }
02710     }
02711 
02712 #if 0
02713     if (time > 13.62265) {
02714         cerr << "top_level_node_epilogue_force_calculation(2): " << endl;
02715         PRC(format_label()); PRC(nn->format_label()); PRL(sqrt(d_nn_sq));
02716     }
02717 #endif
02718 
02719     hdyn* od = get_oldest_daughter();
02720     if (!od) return;
02721 
02722     // Apply center-of-mass correction to binary node.
02723 
02724     hdyn * root = get_root();
02725         
02726     // if (is_parent()) {
02727     //     print_label(cerr); cerr << " nn before epilogue ";
02728     //     n->print_label(cerr); cerr << " ";
02729     //     RL(d_nn_sq);
02730     // }
02731 
02732     if (n_perturbers > MAX_PERTURBERS) {
02733         
02734         // Perturber list has overflowed.  Use the entire tree (below).
02735 
02736         valid_perturbers = false;
02737         kc->perturber_overflow++;
02738         
02739     } else if (n_perturbers > 0) {
02740         
02741         // Use the perturber list to correct the center-of-mass force.
02742         // First, subtract out center-of-mass contribution:
02743         
02744         vector a_cm, a_p, j_cm, j_p;
02745         real p_p;
02746         calculate_partial_acc_and_jerk(this, this, this,
02747                                        a_cm, j_cm, p_p, d_nn_sq, nn,
02748                                        USE_POINT_MASS,              // explicit
02749                                        this,                        // loop
02750                                        this);
02751 
02752 #if 0
02753         if (time > 13.62265) {
02754             cerr << "top_level_node_epilogue_force_calculation(3a): " << endl;
02755             PRC(format_label()); PRC(nn->format_label()); PRL(sqrt(d_nn_sq));
02756         }
02757 #endif
02758 
02759         // (Note: 'this' is OK here because this is a top-level node.)
02760 
02761         // acc -= a_cm;
02762         // jerk -= j_cm;
02763 
02764         pot -= p_p;
02765 
02766         // Replace nodes by components on the perturber list and
02767         // add in contribution due to components.
02768 
02769         bool debug = false;
02770 
02771         if (!expand_nodes(n_perturbers, perturber_list, debug)) {
02772         
02773             // Perturber list has overflowed.  Use the entire tree (below).
02774             // Note: may cause problems if this is a slow binary...
02775         
02776             valid_perturbers = false;
02777             kc->perturber_overflow++;
02778         
02779         } else {
02780 
02781             // (Explicit loop)
02782         
02783             calculate_partial_acc_and_jerk(this, this, this,
02784                                            a_p, j_p, p_p, d_nn_sq, nn,
02785                                            !USE_POINT_MASS,
02786                                            this,
02787                                            this);
02788 
02789 #if 0
02790             if (time > 13.62265) {
02791                 cerr << "top_level_node_epilogue_force_calculation(3b): "
02792                      << endl;
02793                 PRC(format_label()); PRC(nn->format_label());
02794                 PRL(sqrt(d_nn_sq));
02795             }
02796 #endif
02797 
02798             // Include the "slow" term in correcting acc and jerk.  The
02799             // back reaction is handled in correct_acc_and_jerk.
02800 
02801             // In the case of slow binary motion, on exit from this function,
02802             // acc and jerk remain computed in the point-mass approximation;
02803             // the perturbative corrections are stored separately.
02804 
02805             // This may not be quite right if one of the perturbers is
02806             // itself a slow binary.  However, the procedure followed in
02807             // correct_and_update() should properly treat the dominant
02808             // term due to the internal motion of this node.
02809 
02810 
02811 
02812 
02813 //          if (time >= xreal(2296, 3651856000000000000)) {
02814 //              cerr << endl << "in top_level_node_epilogue_force_calculation"
02815 //                   << endl;
02816 //              int p = cerr.precision(HIGH_PRECISION);
02817 //              PRL(format_label());
02818 //              pp3(get_top_level_node());
02819 //              cerr << endl;
02820 //              cerr.precision(p);
02821 //          }
02822 
02823 
02824 
02825 
02826             if (!od->slow) {
02827 
02828                 // Normal correction in non-slow case.
02829 
02830                 acc += a_p - a_cm;
02831                 jerk += j_p - j_cm;
02832 
02833             } else {
02834 
02835                 // Keep the CM approximation and save the perturbation
02836                 // for use in correct_and_update().
02837 
02838                 od->slow->set_acc_p(a_p - a_cm);
02839                 od->slow->set_jerk_p(j_p - j_cm);
02840 
02841                 // The new perturber list is valid and intact.  Update
02842                 // the slow_perturbed lists of the top_level_nodes of all
02843                 // perturbers.
02844 
02845                 for (int j = 0; j < n_perturbers; j++) {
02846                     hdyn *pert_top = perturber_list[j]->get_top_level_node();
02847 #if 0
02848                     cerr << "adding " << format_label();
02849                     cerr << " to slow_perturbed list of "
02850                          << pert_top->format_label()
02851                          << endl;
02852 #endif
02853                     pert_top->add_slow_perturbed(this, diag->slow_perturbed);
02854                 }
02855             }
02856 
02857             pot += p_p;
02858         
02859         }
02860 
02861         // if (nn == NULL) {
02862         //     pretty_print_node(cerr);
02863         //     cerr << " nn NULL after add back "<<endl;
02864         // }
02865 
02866 #if 0
02867         if (time > 13.62265) {
02868             cerr << "top_level_node_epilogue_force_calculation(4): " << endl;
02869             PRC(format_label()); PRC(nn->format_label()); PRL(sqrt(d_nn_sq));
02870         }
02871 #endif
02872     }
02873 
02874     // Recompute the force exactly if the perturber list has overflowed.
02875 
02876     if (!valid_perturbers) {
02877         n_perturbers = -1;
02878         calculate_partial_acc_and_jerk(root, root, this,
02879                                        acc, jerk, pot, d_nn_sq, nn,
02880                                        !USE_POINT_MASS,
02881                                        NULL,            // no perturbers
02882                                        this);
02883     }
02884 
02885 #if 0
02886     if (time > 13.62265) {
02887         cerr << "top_level_node_epilogue_force_calculation(5): " << endl;
02888         PRC(format_label()); PRC(nn->format_label()); PRL(sqrt(d_nn_sq));
02889     }
02890 #endif
02891 
02892     // NOTE:  On exit, if valid_perturbers is true, and
02893     //        RESOLVE_UNPERTURBED_PERTURBERS is true, then
02894     //        perturber_list contains *leaves only.*
02895     //
02896     //        If valid_perturbers is false, then the force on 'this'
02897     //        node has been computed exactly; no correction is needed.
02898 
02899     // if (nn == NULL) {
02900     //     pretty_print_node(cerr);
02901     //     cerr << " nn NULL after corrections "<<endl;
02902     // }
02903 
02904     // if (is_parent()) {
02905     //     print_label(cerr); cerr << " nn after epilogue ";
02906     //     nn->print_label(cerr); cerr << " ";
02907     //     PRL(d_nn_sq);
02908     //     PRC(valid_perturbers); PRL(n_perturbers);
02909     // }
02910 
02911 }
02912 
02913 
02914 //-----------------------------------------------------------------------------
02915 // calculate_acc_and_jerk:  Generic routine to calculate the acceleration
02916 // of a node.  Call appropriate functions depending on whether or not the
02917 // node is at the top level of the tree.
02918 //-----------------------------------------------------------------------------
02919 
02920 void hdyn::calculate_acc_and_jerk(bool exact)
02921 {
02922     if (diag->ev_function_id && diag->check_diag(this)) {
02923         cerr << "calculate_acc_and_jerk for "
02924              << format_label() << endl;
02925     }
02926 
02927     dbg_message("calculate_acc_and_jerk", this);
02928 
02929     d_coll_sq = VERY_LARGE_NUMBER;
02930     coll = NULL;
02931     if (is_low_level_node())
02932         get_binary_sister()->set_d_coll_sq(VERY_LARGE_NUMBER);
02933 
02934     if (is_top_level_node())
02935         calculate_acc_and_jerk_on_top_level_node(exact);
02936     else
02937         calculate_acc_and_jerk_on_low_level_node();
02938 
02939     // NOTE: On return, we must correct the acc and jerk on:
02940     //
02941     //       (1) a top-level C.M. node due to other nodes not on its
02942     //           perturber list, but on whose perturber lists it lies.
02943     // and
02944     //       (2) a single top-level node due to any C.M.s on whose perturber
02945     //           lists it lies.
02946 
02947     if (nn == NULL) {
02948          pretty_print_node(cerr); cerr << " nn NULL after calc " << endl;
02949     }
02950 }
02951 
02952 
02953 //=========================================================================
02954 //
02955 // correct_acc_and_jerk:  By construction, the acc and jerk on a top-level
02956 //                        node bi_top will be computed incorrectly if it is a
02957 //                        perturber of another top-level node bj that is not
02958 //                        itself a perturber of bi_top.  (Note that if bi_top
02959 //                        is a leaf, then this latter criterion is necessarily
02960 //                        satisfied.)  The reason that the force calculation
02961 //                        requires correction is that it is written to enable
02962 //                        efficient computation of the top-level interactions
02963 //                        with the GRAPE.  The calculation is first performed
02964 //                        in the point-mass approximation.  Then the acc and
02965 //                        jerk on bi_top due to its perturbers are corrected
02966 //                        to resolve bi_top into its components.  However, if
02967 //                        bi_top is a perturber of bj and bj is not a perturber
02968 //                        of bi_top, no correction has yet been applied.
02969 //                        Apply it here.
02970 //
02971 //=========================================================================
02972 
02973 // apply_correction: bi is a top-level node on the integration list,
02974 //                   whose interaction with bj has not been correctly
02975 //                   calculated.  Include the missing (tidal) terms here.
02976 
02977 local inline void apply_correction(hdyn * bj, hdyn * bi)
02978 {
02979     vector a_c = 0, j_c = 0;
02980     real p_c = 0;
02981     vector a_p = 0, j_p = 0;
02982     real p_p = 0;
02983     real dum_d_sq = VERY_LARGE_NUMBER;
02984     hdyn *dum_nn = NULL;
02985 
02986     if (bj->get_kira_diag()->correct) {
02987         cerr << "correcting " << bi->format_label();
02988         cerr << " for " << bj->format_label() << " at "
02989              << bi->get_system_time() << endl;
02990     }
02991 
02992     // cerr << "before correction" << endl;
02993     // PRL(bi->get_acc());
02994     // PRL(bj->get_acc());
02995 
02996     bi->calculate_partial_acc_and_jerk(bj, bi->get_parent(), bi,
02997                                        a_c, j_c, p_c, dum_d_sq, dum_nn,
02998                                        USE_POINT_MASS,
02999                                        NULL,            // no perturbers
03000                                        bi);
03001     bi->calculate_partial_acc_and_jerk(bj, bi->get_parent(), bi,
03002                                        a_p, j_p, p_p, dum_d_sq, dum_nn,
03003                                        !USE_POINT_MASS,
03004                                        NULL,            // no perturbers
03005                                        bi);
03006 
03007     // Separate out the "perturbative" components of acc and jerk if bj is
03008     // a slow binary.  See also top_level_node_epilogue_force_calculation().
03009 
03010     kira_diag *kd = bi->get_kira_diag();
03011 
03012     hdyn *od = bj->get_oldest_daughter();
03013     if (od && od->get_slow()) {
03014 
03015         // Save the perturbative acc and jerk for use in the corrector.
03016         // Don't update the center-of-mass quantities yet.  Because the
03017         // correction is different for different slowdown factors, it
03018         // is necessary to save the perturbations from different slow
03019         // binaries separately, hence the unpleasant bookkeeping...
03020 
03021         // *** Need to monitor these lists to ensure that they don't
03022         // *** get out of control!
03023 
03024         // Procedure:   (1) See if bj is on the list of slow binaries
03025         //                  associated with node bi, and check that
03026         //                  its kappa hasn't changed.
03027         //              (2) If it is, modify the entry.  If not, create
03028         //                  a new entry (but retain the center of mass
03029         //                  approximation for this step).
03030         //              (3) Also clean up the list by removing links to
03031         //                  invalid nodes.
03032 
03033         slow_perturbed *s = bi->find_slow_perturbed(bj);
03034         if (!s) {
03035 
03036             // Shouldn't happen...
03037 
03038             cerr << "apply_correction: adding " << bj->format_label();
03039             cerr << " to slow_perturbed list of " << bi->format_label()
03040                  << endl;
03041             bj->print_perturber_list(cerr);
03042 
03043             s = bi->add_slow_perturbed(bj, kd->slow_perturbed);
03044         }
03045 
03046         if (s) {
03047             s->set_acc_p(a_p - a_c);
03048             s->set_jerk_p(j_p - j_c);
03049         }
03050 
03051         // Failure (!s) shouldn't occur here, but if it does, the default
03052         // is that we simply proceed in the center of mass approximation.
03053 
03054         bi->check_slow_perturbed(kd->slow_perturbed);   // clean up if necessary
03055 
03056 #if 0
03057         cerr << "apply_correction: slow_perturbed treatment for bi = "
03058              << bi->format_label();
03059         cerr << " and bj = " << bj->format_label() << endl;
03060         PRL(bi->count_slow_perturbed());
03061 #endif
03062 
03063     } else {
03064 
03065         // Normal correction of acc and jerk.
03066 
03067         bi->inc_acc(a_p - a_c);
03068         bi->inc_jerk(j_p - j_c);
03069 
03070         bi->check_slow_perturbed(kd->slow_perturbed);   // clean up if necessary
03071     }
03072 
03073     bi->inc_pot(p_p - p_c);
03074 
03075     // Note: changes to indirect_force counter are handled at lower levels.
03076 
03077     // We should update nn etc here, since otherwise
03078     // perturbers of a perturbed binary see the binary
03079     // as the nearest neighbor, which is unsafe.
03080     // The following correction added Jan 21 1998.
03081 
03082     if (bi->get_d_nn_sq() > dum_d_sq) {
03083 
03084         bi->set_d_nn_sq(dum_d_sq);
03085         bi->set_nn(dum_nn);
03086 
03087 #if 0
03088         if (bi->get_real_system_time() > 13.6235) {
03089             cerr << "nn correction for ";   bi->pretty_print_node(cerr);
03090             cerr << " and ";   bj->pretty_print_node(cerr);
03091             cerr << " as  ";   bi->get_nn()->pretty_print_node(cerr);
03092             cerr << "  new distance = " << bi->get_d_nn_sq() <<endl;
03093         }
03094 #endif
03095     }
03096 
03097     bi->get_kira_counters()->force_correction++;
03098 
03099     // cerr << "after correction" << endl;
03100     // PRL(bi->get_acc());
03101     // PRL(bj->get_acc());
03102 }
03103 
03104 
03105 //-----------------------------------------------------------------------------
03106 //
03107 // need_correction:  return a pointer to a node whose force must be corrected.
03108 //
03109 //     bj: a perturbed top-level CM node
03110 //     bi: one of bj's perturbers
03111 //     t:  present system time
03112 //
03113 // The requirement is as follows:
03114 //
03115 //     a) the actual pointer returned is the top_level_node of bi (btop)
03116 //     b) bi must be the left-most leaf of the tree under btop, to
03117 //        guarantee uniqueness (i.e. that the correction is applied only once
03118 //     c) btop must be in the present integration block
03119 //     d) bj must not be on the perturber list of btop
03120 //
03121 //
03122 // ***** OLD CODE -- goes with old version of correct_acc_and_jerk. *****
03123 //
03124 //-----------------------------------------------------------------------------
03125 
03126 local inline hdyn *need_correction(hdyn * bj, hdyn * bi)
03127 {
03128     hdyn *btop = bi->get_top_level_node();
03129 
03130     if (!btop->is_on_integration_list()) return NULL;
03131 
03132     // Now we know that btop is on the integration list.
03133     // If it is a single star, it needs correction.
03134 
03135     if (btop ->is_leaf()) return btop;
03136 
03137     hdyn *b_oldest = btop;
03138     hdyn *bret = NULL;
03139 
03140     // btop is the top-level node of a clump requiring correction.
03141     // Test if bi is the left-most ("oldest") leaf of the clump, in
03142     // order to guarantee that btop is corrected only once.
03143 
03144     // Find the left-most leaf.
03145 
03146     while (b_oldest->is_parent())
03147         b_oldest = b_oldest->get_oldest_daughter();
03148 
03149     // Note that bi can be a top-level node in the case
03150     // of correction from a node without valid perturber
03151     // list.
03152 
03153     if ( (b_oldest == bi) || (btop == bi)) {
03154 
03155         bret = btop;
03156 
03157         // No need to apply a correction if some component of bj is a
03158         // perturber of btop (interaction already properly calculated).
03159 
03160         // Top-level node btop is on the perturber list of node bj.
03161         // Return NULL iff bj is a perturber of btop.
03162 
03163         // Well, what if perturber list of btop is INVALID?  This in practice
03164         // *should not* occur unless previous force calculation is exact...
03165         // ...or if neighbor-list overflow has occurred (Steve 7/98).
03166 
03167         if (btop->get_valid_perturbers()) {
03168             for (int j = 0; j < btop->get_n_perturbers(); j++)
03169                 if (btop->get_perturber_list()[j]->get_top_level_node() == bj)
03170                     bret = NULL;
03171         }
03172 
03173         // Code modified by Steve 7/98
03174 
03175         else
03176             bret = NULL;        // btop force should be correct in this case...
03177 
03178     }
03179 
03180     return bret;
03181 }
03182 
03183 
03184 //-----------------------------------------------------------------------------
03185 //
03186 // correct_acc_and_jerk:  OLD version.  Inefficient, but works...
03187 //
03188 // To avoid the use of inverse perturber lists, we identify nodes bi_top
03189 // and bj by scanning across the entire top-level (bj), then checking for
03190 // perturbers (bi).  Correction, if necessary, is applied to bi_top, the
03191 // top-level node of bi.  Membership on the integration list is
03192 // determined by checking the flag on_integration_list(), so there is no
03193 // need for the list itself to be provided to this routine.
03194 //
03195 // Note from Steve, 9/98.  This is a very inefficient function, as it checks
03196 // all the perturber lists of all nodes, even though in practice very few
03197 // corrections are actually applied.  The new version below uses the list
03198 // of perturbed binaries to speed things up.
03199 //
03200 //-----------------------------------------------------------------------------
03201 
03202 // Static data:
03203 
03204 static int work_size = 0;
03205 static hdyn ** nodes = NULL;
03206 static int nnodes = -1;
03207 
03208 // Allow possibility of cleaning up if necessary:
03209 
03210 void clean_up_hdyn_ev() {if (nodes) delete [] nodes;}
03211 
03212 void correct_acc_and_jerk(hdyn * root,          // OLD!
03213                           bool& reset)
03214 {
03215 
03216     if (nnodes == -1) reset = true;
03217 
03218     if (reset) {
03219 
03220         // initialize the array of node pointers
03221 
03222         // cerr << "correct_acc_and_jerk, reset\n";
03223 
03224         int n = 0;
03225         for_all_daughters(hdyn, root, bb)
03226             if (bb->is_parent()) n++;
03227         if  (work_size < n) {
03228             nodes = new hdynptr[n*2+10];        // DEC C++ doesn't like (hdyn*)
03229             work_size = n*2+10;
03230         }
03231         n = 0;
03232         for_all_daughters(hdyn, root, bbb) {
03233             if (bbb->is_parent()) {
03234                 nodes[n] = bbb;
03235                 n++ ;
03236             }
03237         }
03238         nnodes = n;
03239     }
03240 
03241     reset = false;
03242     for (int j = 0; j < nnodes; j++) {
03243 
03244         hdyn * bj = nodes[j];
03245 
03246         // for_all_daughters(hdyn, root, bj) {
03247 
03248         if (!bj->is_valid()) {
03249 
03250             // Invalid node ==> should not occur; better reconstruct
03251             // the internal list if found.
03252 
03253             cerr << "correct_acc_and_jerk: warning: invalid node at time "
03254                  << root->get_system_time() << endl;
03255             reset = true;
03256 
03257         } else if (bj->is_parent()
03258                    && bj->get_oldest_daughter()->get_kepler() == NULL) {
03259 
03260             hdyn *bi_top;
03261 
03262             // bj is a perturbed top-level CM node.
03263 
03264             if (bj->get_valid_perturbers()) {
03265 
03266                 // Look in bj's perturber list for something to correct.
03267 
03268                 for (int i = 0; i < bj->get_n_perturbers(); i++) {
03269 
03270                     hdyn *bi = bj->get_perturber_list()[i];
03271 
03272                     // Flag an invalid perturber...
03273 
03274                     if (!bi->is_valid()) {
03275 
03276                         cerr << "warning: correct_acc_and_jerk: "
03277                              << "invalid perturber #" << i
03278                              << " (" << bi << ")"
03279                              << "         for " << bj->format_label()
03280                              << " at time " << root->get_system_time()
03281                              << endl
03282                              << "         forcing recomputation "
03283                              << " of perturber list"
03284                              << endl;
03285 
03286                         // Overkill:
03287                         //
03288                         // pp3(root);
03289                         // pp3(bi);
03290                         // exit(1);
03291 
03292                         bj->set_valid_perturbers(false);
03293                         break;
03294 
03295                     } else {
03296 
03297                         if ((bi_top = need_correction(bj, bi))
03298                             != NULL) {
03299                             apply_correction(bj, bi_top);
03300                         }
03301                     }
03302                 }
03303 
03304             } else {
03305 
03306                 // Look at all top-level nodes for something to correct.
03307 
03308                 // Note (J.M. 96-Aug-5):
03309                 // Actually, one needs to loop over particles in current
03310                 // block only...  Not implemented yet.
03311 
03312                 for_all_daughters(hdyn, root, bi) {
03313                     if (bi != bj) {
03314                         if ((bi_top = need_correction(bj, bi))
03315                               != NULL) {
03316                             apply_correction(bj, bi_top);
03317                         }
03318                     }
03319                 }
03320             }
03321         }
03322     }
03323 }
03324 
03325 
03326 //-----------------------------------------------------------------------------
03327 //
03328 // check_and_apply_correction:  helper function for use with new version
03329 //                              of correct_acc_and_jerk.
03330 //
03331 //-----------------------------------------------------------------------------
03332 
03333 local inline void check_and_apply_correction(hdyn * bj, hdyn * bi)
03334 {
03335     // On entry, bj is a perturbed binary.  Node bi is on bj's perturber
03336     // list, if it exists.  Otherwise, bi is a node in the current time
03337     // step block.  We want to check if the top-level node of bi (btop)
03338     // needs correction.  Criteria are:
03339     //
03340     //      1. btop is on the integration list (time = system time)
03341     //      2. btop is not perturbed by bj
03342 
03343     hdyn *btop = bi->get_top_level_node();
03344 
03345     if (btop->is_on_integration_list()) {
03346 
03347         // Node btop is on the integration list and effectively perturbs bj.
03348 
03349         // If btop is a single star or unperturbed, it needs correction.
03350         // If not, apply more checks.
03351 
03352         // Hmmm.  It is possible, although it is not supposed to happen,
03353         // that the individual components of an unperturbed binary or
03354         // multiple system may appear on a perturber list even when
03355         // RESOLVE_UNPERTURBED_PERTURBERS is set false.  We take the
03356         // defensive position here of checking the components of all
03357         // binaries, even unperturbed systems.
03358 
03359         if (btop->is_parent()
03360 
03361 //          && !btop->get_oldest_daughter()->get_kepler()) {  // check moved
03362                                                               // to (*) below
03363             ) {
03364 
03365             // Since perturbers are determined in the CM approximation
03366             // and subsequently are expanded into component leaves, all
03367             // leaves should be on the list if one is.  Test if bi is
03368             // the left-most ("oldest") leaf of the clump, in order to
03369             // guarantee that btop is corrected only once.
03370 
03371             // Find the left-most leaf.
03372 
03373             hdyn *b_oldest = btop;
03374 
03375             while (b_oldest->is_parent())
03376                 b_oldest = b_oldest->get_oldest_daughter();
03377 
03378             // Note that bi can be a top-level node in the case of correction
03379             // from a node without valid perturber list.
03380 
03381             // cerr << "b_oldest = " << b_oldest->format_label() << endl;
03382 
03383             if (b_oldest != bi && btop != bi) return;
03384 
03385             if (!btop->get_oldest_daughter()->get_kepler()) {  //  (*) <---
03386 
03387                 // Node btop is perturbed.  See if some component of bj
03388                 // is a perturber of btop.
03389 
03390                 // What if perturber list of btop is INVALID?  This in
03391                 // practice *should not* occur unless previous force
03392                 // calculation was exact, or if neighbor-list overflow
03393                 // has occurred (Steve 7/98).
03394 
03395                 if (!btop->get_valid_perturbers()) return;
03396 
03397                 for (int j = 0; j < btop->get_n_perturbers(); j++)
03398                     if (btop->get_perturber_list()[j]->get_top_level_node()
03399                                 == bj)
03400                         return;
03401             }
03402         }
03403 
03404         // Need to apply a correction to btop.
03405 
03406         apply_correction(bj, btop);
03407 
03408     }
03409 }
03410 
03411 
03412 //-----------------------------------------------------------------------------
03413 //
03414 // NEW version of correct_acc_and_jerk uses perturbed binary list to
03415 // speed up identification of systems that need correction.
03416 //
03417 // Only calling function is calculate_acc_and_jerk_for_list() (kira_ev.C).
03418 //
03419 //-----------------------------------------------------------------------------
03420 
03421 void correct_acc_and_jerk(hdyn** next_nodes,    // NEW
03422                           int n_next)
03423 {
03424     if (n_next < 1) return;
03425 
03426     // Look on the list of top-level perturbed nodes for nodes
03427     // perturbed by a node in the current time step block.
03428 
03429     hdyn* root = next_nodes[0]->get_root();
03430     hdyn** perturbed_list = root->get_perturbed_list();
03431 
03432     if (!perturbed_list) {
03433 
03434         // No perturbed binary list.  Revert to the old code!
03435 
03436         bool reset = true;
03437         correct_acc_and_jerk(root, reset);
03438         return;
03439     }
03440 
03441     int n_perturbed = root->get_n_perturbed();
03442 
03443     for (int j = 0; j < n_perturbed; j++) {
03444 
03445         hdyn * bj = perturbed_list[j];  // bj is a perturbed top-level CM node
03446 
03447         if (bj->is_valid()                      // should not be necessary...
03448             && bj->get_oldest_daughter()        // also should not be necessary
03449 
03450             && !bj->get_oldest_daughter()       // could be partially
03451                   ->get_kepler()) {             //     unperturbed motion
03452 
03453             if (bj->get_valid_perturbers()) {
03454 
03455                 // Look in bj's perturber list for something to correct.
03456 
03457                 for (int i = 0; i < bj->get_n_perturbers(); i++) {
03458 
03459                     hdyn *bi = bj->get_perturber_list()[i];
03460 
03461                     // Flag an invalid perturber...
03462 
03463                     if (!bi->is_valid()) {
03464 
03465                         cerr << "warning: correct_acc_and_jerk: "
03466                              << "invalid perturber #" << i
03467                              << " (" << bi << ")" << endl;
03468                         cerr << "         for " << bj->format_label()
03469                              << " at time " << root->get_system_time()
03470                              << endl
03471                              << "         forcing recomputation "
03472                              << " of perturber list"
03473                              << endl;
03474 
03475                         bj->set_valid_perturbers(false);
03476                         break;
03477 
03478                     } else {
03479 
03480                         // bi is a valid node on bj's perturber list.
03481 
03482                         check_and_apply_correction(bj, bi);
03483                     }
03484                 }
03485 
03486             } else {
03487 
03488                 // Look at all top-level nodes associated with the current
03489                 // block for something to correct.
03490 
03491                 for (int i = 0; i < n_next; i++)
03492                     if (next_nodes[i] != bj
03493                         && next_nodes[i]->is_top_level_node())
03494                         check_and_apply_correction(bj, next_nodes[i]);
03495 
03496             }
03497         }
03498     }
03499 }
03500 
03501 
03502 //=============================================================================
03503 //  driver function for a one-time-step integration of this node
03504 //=============================================================================
03505 
03506 //-----------------------------------------------------------------------------
03507 //  integrate_node -- advance this node by one time step
03508 //-----------------------------------------------------------------------------
03509 
03510 void hdyn::integrate_node(hdyn * root,
03511                           bool integrate_unperturbed,   // default = true
03512                           bool force_unperturbed_time)  // default = false
03513 
03514 // From Steve: force_unperturbed_time can cause an unperturbed binary
03515 // to be advanced to an undesirable phase.
03516 
03517 {
03518     if (diag->ev_function_id && diag->check_diag(this)) {
03519         cerr << "integrate_node for " << format_label()
03520              <<" at time " << time  + timestep << endl;
03521         // pretty_print_node(cerr);
03522     }
03523 
03524     if (kep == NULL) {
03525 
03526         clear_interaction();
03527         calculate_acc_and_jerk(true);
03528         set_valid_perturbers(false);
03529 
03530         if (tidal_type && is_top_level_node()) {
03531             real dpot;
03532             vector dacc, djerk;
03533             get_external_acc(this, pred_pos, pred_vel,
03534                              dpot, dacc, djerk);
03535             inc_pot(dpot);
03536             inc_acc(dacc);
03537             inc_jerk(djerk);
03538         }
03539 
03540         correct_and_update();                   // note: no retry on error
03541         // update();
03542 
03543         store_old_force();
03544 
03545         // Note that old_acc = acc at the end of a step.
03546 
03547     } else if (integrate_unperturbed) {
03548 
03549         if (eps2 == 0) {
03550 
03551             bool reinitialize;
03552             integrate_unperturbed_motion(reinitialize,
03553                                          force_unperturbed_time);
03554 
03555             if (reinitialize)
03556                 cerr << endl
03557                      << "integrate_node: received reinitialization flag "
03558                      << "during synchronization..." << endl;
03559 
03560         }
03561         else
03562             err_exit(
03563                 "integrate_node: unperturbed binary with non-zero softening");
03564     }
03565 }
03566 
03567 void synchronize_tree(hdyn * b)                 // update all top-level nodes
03568 {                                               // better to use GRAPE if we can
03569     if (!b->is_root())
03570         b->synchronize_node();
03571 
03572     for_all_daughters(hdyn, b, bb)
03573         synchronize_tree(bb);
03574 }
03575 
03576 //=======================================================================//
03577 //  +---------------+        _\|/_        +------------------------------\\ ~
03578 //  |  the end of:  |         /|\         |  src/dyn/evolve/hdyn_ev.C
03579 //  +---------------+                     +------------------------------//
03580 //========================= STARLAB =====================================\\ ~

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