Main Page   Class Hierarchy   Data Structures   File List   Data Fields   Globals  

hdyn_grape6.C

Go to the documentation of this file.
00001 
00002        //=======================================================//    _\|/_
00003       //  __  _____           ___                    ___       //      /|\
00004      //  /      |      ^     |   \  |         ^     |   \     //          _\|/_
00005     //   \__    |     / \    |___/  |        / \    |___/    //            /|\
00006    //       \   |    /___\   |  \   |       /___\   |   \   // _\|/_
00007   //     ___/   |   /     \  |   \  |____  /     \  |___/  //   /|\     
00008  //                                                       //            _\|/_
00009 //=======================================================//              /|\
00010 
00011 //  hdyn_grape6.C:  functions to use GRAPE-6.
00012 //
00013 //    version 1:  Jul 2000   Steve McMillan, Jun Makino
00014 //    version 2:  Dec 2001   Steve McMillan
00015 //.............................................................................
00016 //
00017 //      Externally visible functions (note that the GRAPE version
00018 //      is not specified):
00019 //
00020 //      void check_release_grape
00021 //      void grape_calculate_energies
00022 //      void grape_calculate_acc_and_jerk
00023 //      void grape_calculate_densities
00024 //      void clean_up_hdyn_grape
00025 //
00026 //.............................................................................
00027 
00028 #include "hdyn.h"
00029 #include "grape6.h"
00030 #include "hdyn_inline.C"
00031 
00032 // Set DEBUG =  0 for no debugging info
00033 //              1 for a substantial amount of high-level info
00034 //              2 for tons of output!
00035 
00036 #define DEBUG   0
00037 
00038 // Allow possibility of no neighbor list (former SPZ convention
00039 // reversed by Steve, 6/01):
00040 
00041 //#define NO_G6_NEIGHBOUR_LIST
00042 
00043 // Convenient to allow inline functions to be separated out for
00044 // debugging and profiling purposes.
00045 
00046 // #define INLINE       inline
00047 #define INLINE
00048 
00049 #define ONE2    0.5
00050 #define ONE6    0.16666666666666666667
00051 
00052 #define NRETRY  10
00053 
00054 // Certain GRAPE functions will call themselves up to NRETRY times in
00055 // the event of a hardware error, to try to fix the problem.  A nonzero
00056 // return thus means that NRETRY hardware resets have already been
00057 // attempted, and have failed.  The calling function should probably
00058 // then stop and call Jun...
00059 
00060 // Static declarations:
00061 // -------------------
00062 
00063 static int cluster_id = 0;              // GRAPE-6 cluster to use
00064 static real grape_time_offset = 0;      // offset time to maintain precision
00065 
00066 // Static j-arrays, created in initialize_grape_arrays:
00067 
00068 static hdyn **node_list = NULL;         // "reverse index pointers":
00069                                         // node_list[i]->get_grape_index() = i
00070 
00071 static hdyn **current_nodes = NULL;     // current top-level nodes
00072 static hdyn **previous_nodes = NULL;    // previous top-level nodes
00073 
00074 // Arrays current_nodes and previous_nodes are used only in
00075 // grape_calculate_acc_and_jerk().
00076 
00077 static int grape_nj_max = 0;            // maximum size of static j-arrays
00078 static int n_previous_nodes = 0;        // number of "previous" nodes
00079 
00080 // GRAPE state indicators:
00081 
00082 static bool grape_is_open = false;
00083 static bool grape_first_attach = true;
00084 static bool grape_was_used_to_calculate_potential = false;
00085 
00086 
00087 
00088 //  **************************************************************************
00089 //  *                                                                        *
00090 //  * Local functions used by more than one other function (global or local) *
00091 //  *                                                                        *
00092 //  **************************************************************************
00093 //
00094 //              reattach_grape
00095 //              send_j_node_to_grape
00096 //              initialize_grape_arrays
00097 //              force_by_grape
00098 //              hw_err_exit
00099 
00100 
00101 
00102 local void reattach_grape(real time, char *id, kira_options *ko)
00103 
00104 // Reattach the GRAPE-6.
00105 
00106 // Called by:   grape_calculate_energies()                      // global
00107 //              grape_calculate_acc_and_jerk()                  // global
00108 //              grape_calculate_densities()                     // global
00109 
00110 {
00111         cerr << id << ":  ";
00112         if (!grape_first_attach) cerr << "re";
00113         cerr << "attaching GRAPE at time "
00114              << time << endl << flush;
00115 
00116         g6_open_(&cluster_id);
00117         if (ko) ko->grape_last_cpu = cpu_time();
00118         grape_is_open = true;
00119 
00120         if (DEBUG) {
00121             cerr << "GRAPE successfully attached"
00122                  << endl << flush;
00123         }
00124 }
00125 
00126 
00127 
00128 local INLINE void send_j_node_to_grape(hdyn *b, bool predict = false)
00129 
00130 // Send node b to the GRAPE-6 j-particle list.
00131 
00132 // Called by:   initialize_grape_arrays                         // local
00133 //              send_all_leaves                                 // local
00134 //              grape_calculate_acc_and_jerk                    // global
00135 
00136 {
00137     if (DEBUG > 1) {
00138         cerr << "  send_j_node_to_grape:  ";
00139         PRC(b->format_label()); PRL(predict);
00140     }
00141 
00142     int grape_index = b->get_grape_index();
00143 
00144     real t = b->get_time() - grape_time_offset;                 // 0 <= t <= 1
00145     real dt = b->get_timestep();
00146     if (dt <= 0) dt = 1;
00147     real m = b->get_mass();
00148 
00149     vector pos;
00150     if (predict)
00151 
00152         // Only interested in pot or density in this case, so we just need
00153         // to predict particle positions.
00154 
00155         pos = hdyn_something_relative_to_root(b, &hdyn::get_pred_pos);
00156 
00157     else
00158         pos = b->get_pos();
00159 
00160     vector vel = b->get_vel();
00161 
00162     vector a2 = ONE2 * b->get_acc();            // would be more efficient
00163     vector j6 = ONE6 * b->get_jerk();           // to store these in hdyn...
00164 
00165     vector k18 = b->get_k_over_18();            // is stored in hdyn
00166 
00167     if (DEBUG > 1) {
00168         PRI(4); PRC(cluster_id); PRL(grape_index);
00169         PRI(4); PRC(t); PRC(dt); PRL(m);
00170         PRI(4); PRL(j6);
00171         PRI(4); PRL(a2);
00172         PRI(4); PRL(vel);
00173         PRI(4); PRL(pos);
00174     }
00175     
00176     g6_set_j_particle_(&cluster_id,
00177                        &grape_index,                            // address
00178                        &grape_index,                            // index
00179                        &t, &dt, &m,
00180                        &k18[0],                                 // for now
00181                        &j6[0],
00182                        &a2[0],
00183                        &vel[0],
00184                        &pos[0]);
00185 
00186     if (DEBUG > 1) {
00187         cerr << "  ...sent to GRAPE-6" << endl << flush;
00188     }
00189 }
00190 
00191 
00192 
00193 local int initialize_grape_arrays(hdyn *b,              // root node
00194                                   bool reset_counters = true,
00195                                   bool predict = false)
00196 
00197 // Initialize arrays associated with GRAPE-6 j-particles.
00198 // Return the total number of j-particles sent to the GRAPE.
00199 
00200 // Called by:   grape_calculate_acc_and_jerk()                  // global
00201 //              grape_calculate_densities()                     // global
00202 
00203 {
00204     if (DEBUG) {
00205         cerr << "initialize_grape_arrays at time "
00206              << b->get_real_system_time()<< ":  ";
00207         PRC(reset_counters); PRL(predict);
00208     }
00209 
00210     int nj = b->n_daughters();
00211 
00212     if (DEBUG)
00213         PRL(nj);
00214 
00215     if (grape_nj_max <= 0 || grape_nj_max < nj) {
00216 
00217         // Reset the node_list array.
00218 
00219         if (node_list) delete [] node_list;
00220         if (current_nodes) delete [] current_nodes;
00221         if (previous_nodes) delete [] previous_nodes;
00222 
00223         grape_nj_max = 3*nj + 10;                               // cautious!
00224         node_list = NULL;                                       // flag to set
00225     }
00226 
00227     if (!node_list) {
00228 
00229         if (DEBUG)
00230             PRL(grape_nj_max);
00231 
00232         node_list = new hdynptr[grape_nj_max];
00233         current_nodes = new hdynptr[grape_nj_max];
00234         previous_nodes = new hdynptr[grape_nj_max];
00235     }
00236 
00237     // See if we need to increase the time offset.
00238 
00239     if (b->get_real_system_time() - grape_time_offset > 1)
00240         grape_time_offset += 1;
00241 
00242     // Set up the j-particle data.
00243 
00244     nj = 0;
00245     for_all_daughters(hdyn, b, bb) {
00246 
00247         // For now, let GRAPE index = address.
00248 
00249         bb->set_grape_index(nj);
00250         node_list[nj++] = bb;
00251         
00252         // Copy the node to the GRAPE with index = address.
00253 
00254         send_j_node_to_grape(bb, predict);
00255 
00256         if (reset_counters)
00257             bb->set_grape_nb_count(0);
00258     }
00259 
00260     if (DEBUG) {
00261         cerr << endl << "Initialized GRAPE-6 arrays, "; PRL(nj);
00262         cerr << "...leaving initialize_grape_arrays" << endl;
00263     }
00264 
00265     return nj;
00266 }
00267 
00268 
00269 
00270 local hdyn *find_and_print_nn(hdyn *b)
00271 {
00272     real d2min = VERY_LARGE_NUMBER;
00273     hdyn *bmin = NULL;
00274     for_all_daughters(hdyn, b->get_root(), bb) {
00275         if (bb != b) {
00276             real d2 = square(bb->get_pred_pos() - b->get_pred_pos());
00277             if (d2 < d2min) {
00278                 d2min = d2;
00279                 bmin = bb;
00280             }
00281         }
00282     }
00283     if (bmin) {
00284         cerr << "    found true nn of " << b->format_label();
00285         cerr << " = " << bmin->format_label()
00286              << ";  grape_index = " << bmin->get_grape_index()
00287              << "  at distance " << sqrt(d2min) << endl;
00288     } else
00289         cerr << "    can't find true nn for " << b->format_label() << endl;
00290 
00291     return bmin;
00292 }
00293 
00294 
00295 
00296 local void reset_grape(hdyn *b)                                 // root node
00297 
00298 // Perform a "hard" reset of the grape interface to try to recover
00299 // from a hardware error.
00300 
00301 // Called by:   force_by_grape                                  // global
00302 
00303 {
00304     // First two lines will result in a slow reopening of the GRAPE
00305     // (as when the hardware is attached for the first time).
00306 
00307     g6_reset_(&cluster_id);
00308     g6_reset_fofpga_(&cluster_id);
00309 
00310     g6_close_(&cluster_id);
00311     g6_open_(&cluster_id);
00312 
00313     cerr << endl << "*** Reset GRAPE-6 at time "
00314          << b->get_real_system_time() << endl;
00315 
00316     // Reload the j-arrays.
00317 
00318     initialize_grape_arrays(b, false);
00319 }
00320 
00321 
00322 
00323 // These arrays are actually local to force_by_grape, but it is convenient
00324 // to make them globally accessible in order to permit cleanup.
00325 
00326 static int    *iindex = NULL;
00327 static vector *ipos   = NULL;
00328 static vector *ivel   = NULL;
00329 static vector *iacc   = NULL;
00330 static vector *ijerk  = NULL;
00331 static real   *ipot   = NULL;
00332 static real   *ih2    = NULL;
00333 static int    *inn    = NULL;
00334 static real   eps2    = 0;
00335 
00336 //-------------------------------------------------------------------------
00337 
00338 local INLINE int force_by_grape(xreal xtime,
00339                                 hdyn *nodes[], int ni,
00340                                 int nj, int n_pipes,
00341                                 bool pot_only = false,
00342                                 int level = 0)
00343 
00344 // Calculate the force on ni nodes due to nj particles already
00345 // stored in GRAPE memory, using up to n_pipes hardware pipes.
00346 // Return 0 iff no problems occurred.
00347 
00348 // Called by:   force_by_grape_on_all_leaves                    // local
00349 //              get_force_and_neighbors                         // local
00350 //              grape_calculate_densities()                     // global
00351 
00352 {
00353     static char *func = "force_by_grape";
00354 
00355     if (ni <= 0) return 0;
00356     if (ni > n_pipes) err_exit("force_by_grape: ni too large\n");
00357 
00358     if (DEBUG > 1) {
00359         cerr << "  " << func << ":  ";
00360         PRC(xtime); PRC(ni); PRC(nj); PRL(pot_only);
00361     }
00362 
00363     if (!iindex) {
00364 
00365         // Create the i-particle arrays.
00366 
00367         iindex = new int[n_pipes];
00368         ipos   = new vector[n_pipes];
00369         ivel   = new vector[n_pipes];
00370         iacc   = new vector[n_pipes];
00371         ijerk  = new vector[n_pipes];
00372         ipot   = new real[n_pipes];
00373         ih2    = new real[n_pipes];
00374         inn    = new int[n_pipes];
00375         eps2   = nodes[0]->get_eps2();
00376     }
00377 
00378     // Send the current time to the GRAPE.
00379 
00380     real time = xtime - grape_time_offset;
00381     g6_set_ti_(&cluster_id, &time);
00382 
00383     // Pack the i-particle data and start the GRAPE calculation.
00384 
00385     if (DEBUG > 1) {
00386         cerr << "  loading nodes..."
00387              << endl << flush;
00388     }
00389 
00390 //    PRL(ni);
00391 
00392     for (int i = 0; i < ni; i++) {
00393 
00394 //      PRC(i); PRL(nodes[i]);
00395 
00396         iindex[i] = nodes[i]->get_grape_index();
00397 
00398 #if 0
00399         // Old version:
00400 
00401         if (pot_only)
00402 
00403             // Nodes in this case are leaves, not necessarily top-level.
00404 
00405             ipos[i] = hdyn_something_relative_to_root(nodes[i],
00406                                                       &hdyn::get_pred_pos);
00407         else
00408             ipos[i]   = nodes[i]->get_pred_pos();
00409 
00410         ivel[i]   = nodes[i]->get_pred_vel();
00411         iacc[i]   = nodes[i]->get_old_acc();
00412         ijerk[i]  = ONE6*nodes[i]->get_old_jerk();
00413         ipot[i]   = nodes[i]->get_pot();
00414         ih2[i]    = nodes[i]->get_grape_rnb_sq();
00415 
00416 #else
00417 
00418         // New version.  Suggested by Jun (May 8 2001) to prevent the
00419         // "call Jun" message.  Implemented by SPZ.  Corrected by Steve.
00420         // Mainly, we want to avoid sending zeroes to g6calc_firsthalf()
00421         // in acc, jerk, or pot, as these are used for scaling purposes.
00422 
00423         if (pot_only) {
00424 
00425             // Nodes in this case are leaves, not necessarily top-level.
00426 
00427             ipos[i] = hdyn_something_relative_to_root(nodes[i],
00428                                                       &hdyn::get_pred_pos);
00429 
00430 #if 0
00431             // Set some NOT-SO-LARGE NUMBERS to acc and jerk to avoid
00432             // overflow flag.
00433 
00434             iacc[i]   = vector(1);
00435             ijerk[i]  = vector(1);
00436 #endif
00437 
00438         } else {
00439 
00440             ipos[i]   = nodes[i]->get_pred_pos();
00441 #if 0
00442             iacc[i]   = nodes[i]->get_old_acc();
00443             ijerk[i]  = ONE6*nodes[i]->get_old_jerk();
00444 
00445 
00446 
00447             // Added for pentium linux G6 (SPZ: 12 Sept 2001)
00448             // Modified by Steve (2 Oct 2001).
00449 
00450             if (abs(iacc[i]) <= VERY_SMALL_NUMBER || 
00451                 abs(ijerk[i]) <= VERY_SMALL_NUMBER) {
00452                 cerr << "WARNING: Initializing acc and jerk from zero."
00453                      << endl;
00454               iacc[i]   = vector(1);
00455               ijerk[i]  = vector(1);
00456             }
00457 #endif
00458 
00459         }
00460 
00461         ivel[i]   = nodes[i]->get_pred_vel();
00462         ih2[i]    = nodes[i]->get_grape_rnb_sq();
00463         iacc[i]   = nodes[i]->get_old_acc();
00464         ijerk[i]  = ONE6*nodes[i]->get_old_jerk();
00465         ipot[i]   = nodes[i]->get_pot();
00466 
00467         // Must take care with pot, acc, and jerk.  Quite expensive to
00468         // check the abs of a vector, so try more indirect tests first.
00469         // Normally,  these values will be zero only at initialization,
00470         // but we may also encounter this immediately after a new CM
00471         // node is created.
00472 
00473         if (abs(ipot[i]) <= VERY_SMALL_NUMBER
00474             || abs(abs(iacc[i][0])) <= VERY_SMALL_NUMBER) {
00475 
00476             ipot[i] = 1;
00477             if (abs(iacc[i]) <= VERY_SMALL_NUMBER ||
00478                 abs(ijerk[i]) <= VERY_SMALL_NUMBER) {
00479                 // cerr << "WARNING: Initializing acc and jerk from zero."
00480                 //      << endl;
00481                 iacc[i]   = vector(1);
00482                 ijerk[i]  = vector(1);
00483             }
00484         }
00485 
00486 #endif
00487 
00488         if (DEBUG > 1) {
00489             if (i > 0) cerr << endl;
00490             PRI(4); PRC(i); PRC(iindex[i]); PRL(nodes[i]->format_label());
00491             PRI(4); PRL(ipos[i]);
00492             PRI(4); PRL(ivel[i]);
00493             PRI(4); PRL(iacc[i]);
00494             PRI(4); PRL(ijerk[i]);
00495         }
00496     }
00497 
00498     // Clear neighbor radii for unused pipes (added by SPZ, May 8 2001).
00499 
00500     for (int i = ni; i < n_pipes; i++)
00501         ih2[i] = 0;
00502 
00503     g6calc_firsthalf_(&cluster_id, &nj, &ni, iindex,
00504                       ipos, ivel, iacc, ijerk, ipot,
00505                       &eps2, ih2);
00506 
00507     int error = g6calc_lasthalf2_(&cluster_id, &nj, &ni, iindex,
00508                                   ipos, ivel, &eps2, ih2,
00509                                   iacc, ijerk, ipot, inn);
00510 
00511     if (DEBUG > 1) {
00512         cerr << endl << "  ...results:"
00513              << endl << flush;
00514     }
00515 
00516     if (!error) {
00517 
00518         for (int i = 0; i < ni; i++) {
00519 
00520             // Hmmm...  Looks like it is possible for data to return
00521             // wrong even though the error flag is not set.  The best
00522             // we can do for now is to check that inn is within range
00523             // and pot < 0.  Not perfect, but... (Steve, 7/00)
00524 
00525             if (inn[i] < 0 || inn[i] >= nj || ipot[i] > 0) {
00526                 error = 42;
00527                 break;
00528             }
00529 
00530             if (pot_only)
00531 
00532                 nodes[i]->set_pot(ipot[i]);
00533 
00534             else {
00535 
00536                 hdyn *nn = node_list[inn[i]];
00537                 real d_nn_sq = 0;
00538 
00539                 if (DEBUG > 1) {
00540                     if (i > 0) cerr << endl;
00541                     PRI(4); PRC(i); PRC(iindex[i]);
00542                             PRL(nodes[i]->format_label());
00543                     PRI(4); PRL(iacc[i]);
00544                     PRI(4); PRL(ijerk[i]);
00545                     PRI(4); PRL(ipot[i]);
00546                     PRI(4); PRC(inn[i]); PR(nn);
00547                 }
00548 
00549                 if (nn) {
00550 
00551                     d_nn_sq = square(ipos[i] - nn->get_pred_pos());
00552 
00553                     if (DEBUG > 1) {
00554                         PRI(2); PRL(nn->format_label());
00555                     }
00556 
00557                 } else {
00558 
00559                     // Set nn = nodes[i] for handling elsewhere.
00560 
00561                     nn = nodes[i];
00562 
00563                     if (DEBUG > 1)
00564                         cerr << endl;
00565                 }
00566 
00567                 if (DEBUG > 1) {
00568                     hdyn *true_nn = find_and_print_nn(nodes[i]);
00569                     if (true_nn != nn)
00570                         cerr << "    *** error: nn != true_nn" << endl;
00571                 }
00572                 
00573                 nodes[i]->set_acc_and_jerk_and_pot(iacc[i], ijerk[i], ipot[i]);
00574                 nodes[i]->set_nn(nn);
00575                 nodes[i]->set_d_nn_sq(d_nn_sq);
00576 
00577 
00578 #if 0000
00579                 if (nodes[i]->name_is("(5394,21337)")
00580                      || nodes[i]->name_is("(21337,5394)")) {
00581                     cerr << func << ": ";
00582                     PRC(nodes[i]); PRC(inn[i]); PRC(nn); PRL(d_nn_sq);
00583                 }
00584 #endif
00585 
00586 
00587                 if (DEBUG > 1) {
00588 
00589                     // Cross-check:
00590 
00591                     bool found_index = false;
00592                     for_all_daughters(hdyn, nodes[0]->get_root(), bb)
00593                         if (bb->get_grape_index() == inn[i]) {
00594                             cerr << "    check: found grape_index = " << inn[i]
00595                                  << " for node " << bb->format_label()
00596                                  << endl;
00597                             found_index = true;
00598                             if (bb != nn)
00599                                 cerr << "    *** error: nn mismatch" << endl;
00600                         }
00601                     if (!found_index)
00602                         cerr << "    *** error: grape_index " << inn[i]
00603                              << " not found" << endl;
00604                 }
00605             }
00606         }
00607     }
00608 
00609     // Retest error because it could have been set in the i-loop above.
00610 
00611     if (error) {
00612 
00613         // Make NRETRY attempts (recursively) to reset and correct the
00614         // error before returning a "true" error condition.
00615 
00616         cerr << "*** " << func << "(" << level << "):  hardware error "
00617              << error << " at time " << time << ",  ni = " << ni << endl;
00618 
00619         if (level < NRETRY) {
00620 
00621             cerr << "Resetting GRAPE and retrying..." << endl;
00622             reset_grape(nodes[0]->get_root());
00623 
00624             error = force_by_grape(xtime, nodes, ni, nj, n_pipes, pot_only,
00625                                    level+1);
00626         }
00627     }
00628 
00629     if (DEBUG > 1) {
00630         cerr << "  ...leaving " << func << "(" << level <<"):  ";
00631         PRL(error);
00632     }
00633 
00634     return error;
00635 }
00636 
00637 
00638 
00639 local void hw_err_exit(char *func, int id, hdyn *b)
00640 
00641 // Exit following a serious hardware error...
00642 
00643 // Called by:   grape_calculate_energies()                      // global
00644 //              grape_calculate_acc_and_jerk()                  // global
00645 //              grape_calculate_densities()                     // global
00646 
00647 {
00648     char buf[256];
00649     sprintf(buf, "%s[%d]:  time to call Jun!", func, id);
00650 
00651     // Dump out a copy of the system.
00652 
00653     char *dumpfile = "hw_error_dump";
00654 
00655     ofstream dump(dumpfile);
00656     if (dump) {
00657         put_node(dump, *b->get_root(), b->get_kira_options()->print_xreal);
00658         dump.close();
00659         cerr << "Data written to file " << dumpfile << endl;
00660     }
00661     
00662     err_exit(buf);
00663 }
00664 
00665 
00666 
00667 //  *************************************************************************
00668 //  *************************************************************************
00669 //  **                                                                     **
00670 //  **  Globally visible GRAPE-6 functions (and dedicated local helpers).  **
00671 //  **                                                                     **
00672 //  *************************************************************************
00673 //  *************************************************************************
00674 
00675 
00676 //  **********************************************************************
00677 //  *                                                                    *
00678 //  *  check_release_grape:  Accessor for GRAPE release/attach.          *
00679 //  *                                                                    *
00680 //  **********************************************************************
00681 
00682 
00683 void check_release_grape(kira_options *ko, xreal time, bool verbose)
00684 {
00685 #ifdef SHARE_GRAPE
00686 
00687     if (DEBUG) {
00688         cerr << "GRAPE CPU check:  ";
00689         PRL(cpu_time());
00690     }
00691 
00692     if (cpu_time() - ko->grape_last_cpu > ko->grape_max_cpu) {
00693 
00694         int p = cerr.precision(STD_PRECISION);
00695         cerr << endl << "Releasing GRAPE-6 at time " << time << " after ";
00696         cerr.precision(2);
00697         cerr << cpu_time() - ko->grape_last_cpu <<" CPU sec" << endl;
00698         cerr.precision(p);
00699 
00700         g6_close_(&cluster_id);
00701         grape_is_open = false;
00702         grape_first_attach = false;
00703 
00704         cerr << endl;
00705     }
00706 
00707 #endif
00708 }
00709 
00710 
00711 
00712 //  *********************************************************************
00713 //  *                                                                   *
00714 //  *  grape_calculate_energies:  Calculate total energy of the system  *
00715 //  *                             (requires GRAPE reset after use).     *
00716 //  *                                                                   *
00717 //  *********************************************************************
00718 
00719 local inline bool use_cm_approx(hdyn *bb)
00720 {
00721     if (bb->get_kepler()) {
00722 
00723         // Treat the binary as unperturbed for purposes of computing
00724         // its energy if the estimated tidal effect of its neighbors
00725         // is enegligible.  For now, accept any unperturbed binary.
00726 
00727 //      cerr << "CM approx: "; PRL(bb->format_label());
00728         return true;
00729     }
00730     return false;
00731 }
00732 
00733 
00734 
00735 local int send_all_leaves_to_grape(hdyn *b,             // root node
00736                                    real& e_unpert,      // unperturbed energy
00737                                    bool cm = false)     // use CM approx
00738 
00739 // Send predicted positions (velocities, etc. are not needed to
00740 // compute the potentials) of all leaves to GRAPE j-particle memory.
00741 // Return the total number of leaves sent to the GRAPE.
00742 
00743 // Called by:   grape_calculate_energies()                      // global
00744 
00745 {
00746 //    if (b->get_system_time() > 173.077
00747 //      && b->get_system_time() < 173.08) {
00748     if (DEBUG) {
00749         cerr << "  send_all_leaves_to_grape"
00750              << endl << flush;
00751     }
00752 
00753     int nj = 0;
00754     e_unpert = 0;
00755 
00756 //    if (b->get_system_time() > 173.077
00757 //      && b->get_system_time() < 173.08) PRL(cm);
00758 
00759     if (!cm) {
00760         for_all_leaves(hdyn, b, bb) {
00761 
00762             // In center of mass approximation, replace bb by its parent.
00763             // The order of traversal of the tree means that this should
00764             // occur at the elder component of a binary, and will skip the
00765             // other component.  The while loop should also take care of
00766             // unperturbed multiples!  For efficiency, keep and return a
00767             // running sum of all internal energies excluded.
00768 
00769             bool reset_bb = false;
00770 
00771 //          if (b->get_system_time() > 173.077
00772 //              && b->get_system_time() < 173.08) pp3(bb, cerr, -1);
00773 
00774             while (use_cm_approx(bb)) {
00775                 hdyn *sis = bb->get_younger_sister();
00776                 hdyn *par = bb->get_parent();
00777                 real reduced_mass = bb->get_mass() * sis->get_mass()
00778                                                    / par->get_mass();
00779                 e_unpert += reduced_mass * bb->get_kepler()->get_energy();
00780                 bb = par;
00781                 reset_bb = true;
00782             }
00783 
00784 //          if (b->get_system_time() > 173.077
00785 //              && b->get_system_time() < 173.08) PRL(reset_bb);
00786 
00787             // Must be careful to avoid an infinite loop (because of the
00788             // logic used by for_all_leaves():
00789 
00790             if (reset_bb) {
00791 //              PRL(e_unpert);
00792 //              PRL(bb->format_label());
00793                 bb = bb->get_oldest_daughter()->get_younger_sister()
00794                                               ->next_node(b);
00795 //              PRL(bb);
00796                 if (!bb) break;
00797 //              PRL(bb->format_label());
00798             }
00799 
00800 //          if (b->get_system_time() > 173.077
00801 //              && b->get_system_time() < 173.08) PRL(bb->get_external_field());
00802 
00803             // For now, let GRAPE index = address.
00804 
00805             bb->set_grape_index(nj++);
00806         
00807             // Copy the leaf to the GRAPE with index = address.
00808 
00809             send_j_node_to_grape(bb, true);     // "true" ==> send predicted pos
00810         }
00811 
00812     } else {
00813 
00814         for_all_daughters(hdyn, b, bb) {
00815 
00816             // For now, let GRAPE index = address.
00817 
00818             bb->set_grape_index(nj++);
00819         
00820             // Copy the node to the GRAPE with index = address.
00821 
00822 //          if (b->get_system_time() > 173.077
00823 //              && b->get_system_time() < 173.08) PRL(bb->get_external_field());
00824 
00825             send_j_node_to_grape(bb, true);     // "true" ==> send predicted pos
00826         }
00827     }
00828 
00829     if (DEBUG) {
00830 //    if (b->get_system_time() > 173.077) {
00831         cerr << "  ...leaving send_all_leaves_to_grape:  ";
00832         PRL(nj);
00833     }
00834 
00835     return nj;
00836 }
00837 
00838 
00839 
00840 local bool force_by_grape_on_all_leaves(hdyn *b,                // root node
00841                                         int nj,
00842                                         bool cm = false)        // CM approx
00843 
00844 // Compute the forces on all particles due to all other particles.
00845 // Only interested in determining the potential energies.
00846 
00847 // Called by:   grape_calculate_energies()                      // global
00848 
00849 {
00850     if (DEBUG) {
00851         cerr << "  force_by_grape_on_all_leaves:  ";
00852         PRL(nj);
00853     }
00854 
00855     int n_pipes = g6_npipes_();
00856     hdyn **ilist = new hdynptr[n_pipes];
00857 
00858     // Compute particle forces, n_pipes at a time.
00859 
00860     bool status = false;
00861 
00862     int ip = 0;
00863     if (!cm) {
00864         for_all_leaves(hdyn, b, bb) {
00865             ilist[ip++] = bb;
00866             if (ip == n_pipes) {
00867                 status |= force_by_grape(b->get_system_time(),
00868                                          ilist, ip, nj, n_pipes,
00869                                          true); // "true" ==> compute pot only
00870                 ip = 0;
00871             }
00872         }
00873     } else {
00874         for_all_daughters(hdyn, b, bb) {
00875             ilist[ip++] = bb;                   // replicated code...
00876             if (ip == n_pipes) {
00877                 status |= force_by_grape(b->get_system_time(),
00878                                          ilist, ip, nj, n_pipes,
00879                                          true); // "true" ==> compute pot only
00880                 ip = 0;
00881             }
00882         }
00883     }
00884 
00885     if (ip)
00886         status |= force_by_grape(b->get_system_time(),
00887                                  ilist, ip, nj, n_pipes,
00888                                  true);
00889 
00890     if (DEBUG) {
00891         cerr << "  ...leaving force_by_grape_on_all_leaves:  ";
00892         PRL(status);
00893     }
00894 
00895     delete [] ilist;
00896     return status;
00897 }
00898 
00899 
00900 
00901 //                                              *****************************
00902 //                                              *****************************
00903 //                                              ***                       ***
00904 //                                              ***  The global function  ***
00905 //                                              ***                       ***
00906 //                                              *****************************
00907 //                                              *****************************
00908 
00909 
00910 void grape_calculate_energies(hdyn *b,                  // root node
00911                               real &epot,
00912                               real &ekin,
00913                               real &etot,
00914                               bool cm)                  // default = false
00915 {
00916     if (DEBUG) {
00917         cerr << endl << "grape_calculate_energies..."
00918              << endl << flush;
00919     }
00920 
00921     if (!grape_is_open)
00922         reattach_grape(b->get_real_system_time(),
00923                        "grape_calculate_energies", b->get_kira_options());
00924 
00925     real e_unpert;
00926     int nj =  send_all_leaves_to_grape(b, e_unpert, cm);
00927 
00928     if (force_by_grape_on_all_leaves(b, nj, cm)) {
00929 
00930         cerr << "grape_calculate_energies: "
00931              << "error on return from force_by_grape_on_all_leaves()"
00932              << endl;
00933 
00934         hw_err_exit("grape_calculate_energies", 1, b);
00935     }
00936 
00937     grape_was_used_to_calculate_potential = true;       // trigger a reset
00938                                                         // next time around
00939 
00940     epot = ekin = etot = 0;
00941 
00942     if (!cm) {
00943         for_all_leaves(hdyn, b, bb) {
00944 
00945             // Logic here follows that in send_all_leaves_to_grape().
00946 
00947             bool reset_bb = false;
00948 
00949             while (use_cm_approx(bb)) {
00950                 bb = bb->get_parent();
00951                 reset_bb = true;
00952             }
00953             if (reset_bb) {
00954                 bb = bb->get_oldest_daughter()->get_younger_sister()
00955                                               ->next_node(b);
00956                 if (!bb) break;
00957             }
00958 
00959             real mi = bb->get_mass();
00960             epot += 0.5*mi*bb->get_pot();
00961             vector vel = hdyn_something_relative_to_root(bb, &hdyn::get_vel);
00962             ekin += 0.5*mi*vel*vel;
00963         }
00964     } else {
00965         for_all_daughters(hdyn, b, bb) {
00966             real mi = bb->get_mass();
00967             epot += 0.5*mi*bb->get_pot();
00968             vector vel = bb->get_vel();
00969             ekin += 0.5*mi*vel*vel;
00970         }
00971     }
00972     etot = ekin + epot + e_unpert;
00973 
00974     if (DEBUG) {
00975         cerr << "...leaving grape_calculate_energies...  ";
00976         PRL(etot);
00977         cerr << endl;
00978     }
00979 }
00980 
00981 
00982 
00983 //  ***********************************************************************
00984 //  *                                                                     *
00985 //  *  grape_calculate_acc_and_jerk: Use the GRAPE hardware to compute    *
00986 //  *                                accs and jerks on a list of nodes.   *
00987 //  *                                                                     *
00988 //  ***********************************************************************
00989 
00990 
00991 local INLINE bool set_grape_neighbor_radius(hdyn * b, int nj_on_grape)
00992 
00993 // Adjust the GRAPE neighbor radius to some reasonable value.
00994 
00995 // Called by:   grape_calculate_acc_and_jerk()                  // global
00996 
00997 {
00998     static char *func = "set_grape_neighbor_radius";
00999 
01000     b->set_grape_rnb_sq(0.0);
01001 
01002     if (b->is_leaf()) {
01003 
01004         // For a single particle, since GRAPE-6 always computes the nn,
01005         // we only need to deal with neighbor lists if our radius is
01006         // nonzero (which we assume implies that all stellar radii are
01007         // nonzero and hence we must compute colls) and grape_nb_count
01008         // is zero.
01009 
01010         // Note that the nn from the GRAPE depends on distance only, so
01011         // is not necessarily as good as the criterion used elsewhere
01012         // in kira.  May be necessary also to use the neighbor lists in
01013         // cases where masses can differ greatly from the mean.
01014         // *** To be studied (Steve, 7/00). ***
01015 
01016         if (b->get_grape_nb_count() == 0 && b->get_radius() > 0) {
01017 
01018             real rnb_sq = 4*b->get_radius()*b->get_radius();
01019 
01020             // If the node has a valid nearest neighbor pointer, try
01021             // to use the nn distance as well.
01022 
01023             if (b->get_nn() && b->get_nn() != b
01024                 && b->get_d_nn_sq() < 0.1*VERY_LARGE_NUMBER) {
01025 
01026                 rnb_sq = max(rnb_sq, b->get_d_nn_sq());
01027 
01028             } else {
01029 
01030                 // Node does not know its nearest neighbor.
01031                 // Note connections between d_min, r90, and rnn.
01032 
01033                 real r90_sq = b->get_d_min_sq() / square(b->get_d_min_fac());
01034                 real rnn_sq = r90_sq * pow((real)nj_on_grape, 4.0/3);
01035 
01036                 // NB: rnn_sq ~ square of the average interparticle spacing.
01037 
01038                 rnb_sq = max(rnb_sq, rnn_sq);
01039             }
01040 
01041             b->set_grape_rnb_sq(rnb_sq);
01042         }
01043 
01044     } else {
01045 
01046         // For a node, we will want to compute the perturbers, so we
01047         // need a larger value of grape_rnb_sq.  As with coll, only
01048         // bother to compute the list if grape_nb_count = 0, unless
01049         // we need to rebuild the perturber list.
01050 
01051         if (b->get_grape_nb_count() == 0 || !b->get_valid_perturbers()) {
01052 
01053             // Old code:
01054             //
01055             // real rnb_sq = b->get_perturbation_radius_factor();
01056 
01057             // If the node has a valid nearest neighbor pointer, use
01058             // the nn distance as well.
01059 
01060             // if (b->get_nn() && b->get_nn() != b
01061             //  && b->get_d_nn_sq() < 0.1* VERY_LARGE_NUMBER)
01062             //  rnb_sq = max(rnb_sq, b->get_d_nn_sq());
01063 
01064             // b->set_grape_rnb_sq(rnb_sq);
01065 
01066             // New code (GRAPE-4 and GRAPE-6 versions; Steve, 12/01):
01067 
01068             real r_pert2 = perturbation_scale_sq(b, b->get_gamma23());
01069 
01070             hdyn *nn = b->get_nn();
01071             if (nn && nn != b && b->get_d_nn_sq() < 0.1* VERY_LARGE_NUMBER)
01072                 r_pert2 = max(r_pert2, b->get_d_nn_sq());
01073 
01074             // Note that this may cause overflow...
01075 
01076             b->set_grape_rnb_sq(r_pert2);
01077 
01078 //          cerr << func << ": computing perturbers for node "
01079 //               << b->format_label() << endl << flush;
01080 
01081         }
01082     }
01083 
01084     return (b->get_grape_rnb_sq() > 0);         // NB using rnb > 0 as a flag
01085 }
01086 
01087 
01088 
01089 local INLINE bool get_force_and_neighbors(xreal xtime,
01090                                           hdyn *nodes[], int ni,
01091                                           int nj_on_grape, int n_pipes,
01092                                           bool need_neighbors,
01093                                           int level = 0)
01094 
01095 // Calculate the forces on the specified i-list of nodes, then read the
01096 // GRAPE neighbor list if needed.   Deal with hardware neighbor list
01097 // problems before returning.  Return true iff neighbor list overflow
01098 // occurs.
01099 
01100 // Called by:   get_coll_and_perturbers                         // local
01101 //              grape_calculate_acc_and_jerk                    // global
01102 
01103 {
01104     static char *func = "get_force_and_neighbors";
01105 
01106     if (ni <= 0) return false;
01107 
01108 //    cerr << "entering " << func << ": ";
01109 //    PRC(ni); PRC(nj_on_grape); PRL(n_pipes);
01110 //    PRL(nodes[0]);
01111 
01112     if (force_by_grape(xtime, nodes, ni, nj_on_grape, n_pipes)) {
01113 
01114         // Hardware error has persisted despite NRETRY GRAPE reset(s).
01115         // Give up...
01116 
01117         hw_err_exit(func, 1, nodes[0]);
01118     }
01119 
01120     bool error = false;
01121 
01122     if (need_neighbors) {
01123 
01124         // At least one i-particle needs coll or perturber information.
01125         // Bring all neighbor lists from the GRAPE to the front end.
01126 
01127       int status = 0;
01128 #ifndef NO_G6_NEIGHBOUR_LIST
01129       status = g6_read_neighbour_list_(&cluster_id);
01130 #endif
01131         if (status) {
01132 
01133             // An error has occurred.  Flag it and take appropriate action...
01134 
01135             cerr << func << ":  error getting GRAPE neighbor data: ";
01136             PRL(status);
01137 
01138             if (status > 0) {
01139 
01140                 // Hardware perturber lists on the GRAPE have overflowed.
01141                 // Calling function must reduce neighbor radii and repeat
01142                 // this group of particles.
01143 
01144                 error = true;
01145 
01146             } else {
01147 
01148                 // An internal error has occurred -- do a hard
01149                 // reset, repeat the force calculation and reread
01150                 // the neighbor lists.
01151 
01152                 // Make NRETRY attempts to reset to correct the problem
01153                 // before giving up.
01154 
01155                 cerr << "*** " << func << "(" << level
01156                      << "):  hardware error " << error << " at time "
01157                      << xtime << ",  ni = " << ni << endl;
01158 
01159                 if (level < NRETRY) {
01160 
01161                     cerr << "Resetting GRAPE and retrying..." << endl;
01162                     reset_grape(nodes[0]->get_root());
01163 
01164                     error = get_force_and_neighbors(xtime, nodes, ni,
01165                                                     nj_on_grape, n_pipes,
01166                                                     need_neighbors,
01167                                                     level+1);
01168                 } else
01169 
01170                     hw_err_exit(func, 2, nodes[0]);
01171             }
01172         }
01173     }
01174 
01175     return error;
01176 }
01177 
01178 
01179 
01180 local INLINE void swap(hdynptr ilist[], int i, int j)
01181 {
01182     hdyn *tmp = ilist[i];
01183     ilist[i] = ilist[j];
01184     ilist[j] = tmp;
01185 }
01186 
01187 local INLINE int sort_nodes_and_reduce_rnb(hdynptr ilist[], int ni)
01188 
01189 // Reorder the list of i-nodes to place those with rnb = 0 at the start.
01190 // Reduce rnb for the remaining nodes and return the location on the
01191 // new list of the first node with rnb > 0.
01192 
01193 // Called by:   grape_calculate_acc_and_jerk()                  // global
01194 
01195 {
01196     static char *func = "sort_nodes_and_reduce_rnb";
01197 
01198     cerr << "In " << func << "() after neighbor-list overflow at time "
01199          << ilist[0]->get_system_time() << endl;
01200 
01201     int inext = ni;
01202 
01203     // First move all the rnb>0 nodes to the end of the list.
01204     // The rnb>0 nodes will start at inext.
01205 
01206     int imax = ni;
01207     real rnb_max = 0;
01208     for (int i = ni-1; i >= 0; i--) {
01209         if (ilist[i]->get_grape_rnb_sq() > 0) {
01210             if (i < --inext) swap(ilist, i, inext);
01211             if (ilist[inext]->get_grape_rnb_sq() > rnb_max) {
01212                 imax = inext;
01213                 rnb_max = ilist[inext]->get_grape_rnb_sq();
01214             }
01215         }           
01216     }
01217 
01218     // ...then place the CM node with the biggest neighbor radius (a
01219     // guess at the node that caused the overflow) at inext...
01220 
01221     if (imax != inext) swap(ilist, inext, imax);
01222 
01223     // ...then adjust the neighbor radii of the others (starting at inext),
01224     // reducing radii for all leaves but only the *first* CM node found
01225     // (in location inext, by construction).
01226 
01227     for (int i = inext; i < ni; i++) {
01228         if (ilist[i]->is_leaf() || i == inext)
01229             ilist[i]->set_grape_rnb_sq(0.5*ilist[i]->get_grape_rnb_sq());
01230     }
01231 
01232 //    PRC(ni); PRL(inext);
01233     return inext;
01234 }
01235 
01236 
01237 
01238 // Neighbor list space is shared by get_neighbors_and_adjust_h2 (acc_and_jerk)
01239 // and count_neighbors_and_adjust_h2 (densities) -- will probably be merged.
01240 
01241 #define MAX_NEIGHBORS           (16*MAX_PERTURBERS)
01242 #define RNB_INCREASE_FAC        2.0
01243 #define RNB_DECREASE_FAC        0.8
01244 
01245 static int max_neighbors = MAX_NEIGHBORS;
01246 static int neighbor_list[MAX_NEIGHBORS];
01247 
01248 // The reason for the large choice of MAX_NEIGHBORS here is to ensure
01249 // that, even if the perturber list overflows, we still have room to
01250 // store the full neighbor list, so the nn and coll pointers will still
01251 // be correctly set.  The size of the neighbor sphere must be reduced
01252 // if overflow occurs, or else the nn pointers will be unreliable.
01253 
01254 //-------------------------------------------------------------------------
01255 
01256 local INLINE int get_neighbors_and_adjust_h2(hdyn * b, int pipe)
01257 
01258 // Set nn, coll, d_nn_sq and d_coll_sq, for particle b (pipe specified),
01259 // and compute perturber lists for parent nodes.  Possible returns are:
01260 //
01261 //      0       All OK, no further action needed.
01262 //      1       Array overflow, reduce grape_rnb_sq and retry.
01263 //      2       No neighbors or no coll, increase grape_rnb_sq and retry.
01264 //
01265 // The value of grape_rnb_sq is changed here.  Retrying is up to the
01266 // calling function.
01267 
01268 // Called by:   get_coll_and_perturbers()                       // local
01269 
01270 {
01271     static char *func = "get_neighbors_and_adjust_h2";
01272 
01273     // Get the list of b's neighbors from the GRAPE, if available.
01274 
01275     int n_neighbors = 0;
01276     int status = 0;
01277 
01278 #ifndef NO_G6_NEIGHBOUR_LIST
01279     status = g6_get_neighbour_list_(&cluster_id,
01280                                     &pipe,
01281                                     &max_neighbors,
01282                                     &n_neighbors,
01283                                     neighbor_list);
01284 #endif
01285 
01286     if (status) {
01287 
01288         // GRAPE found too many neighbors:  n_neighbors >= max_neighbors,
01289         // so the kira perturber list will overflow.  Flag an error for
01290         // now (maybe unnecessary), and modify the neighbor radius.  We
01291         // reduce the size of the neighbor sphere to a point that will
01292         // still ensure too many perturbers, but which will not overflow
01293         // the neighbor list array.
01294 
01295         // Note from Steve (12/01): Looks like n_neighbors returns equal
01296         // to max_neighbors when overflow occurs, so we can't use the
01297         // value of n_neighbors to reduce grape_rnb_sq.  Instead, just
01298         // reduce the neighbor radius by a factor of 2.
01299 
01300         if (n_neighbors >= max_neighbors) {     // should be redundant...
01301 
01302             // real rnb_fac = pow(max_neighbors/(real)n_neighbors, 0.6666667);
01303             // b->set_grape_rnb_sq(rnb_fac*b->get_grape_rnb_sq());
01304 
01305             b->set_grape_rnb_sq(0.25*b->get_grape_rnb_sq());
01306 
01307 #if 0
01308             cerr << func << ":  neighbor list overflow for "
01309                  << b->format_label() << " (pipe "
01310                  << pipe << ")" << endl;
01311             cerr << "    at time " << b->get_system_time() << "  ";
01312             PRC(n_neighbors); PRL(max_neighbors);
01313             cerr << "    new rnb = " << sqrt(b->get_grape_rnb_sq())
01314                  << "  status = 1" << endl;
01315 #else
01316             // Default short diagnostic message:
01317 
01318             cerr << func << ": node " << b->format_label()
01319                  << " time " << b->get_system_time()
01320                  << " n_n " << n_neighbors
01321                  << endl << flush;
01322 #endif
01323 
01324             return 1;
01325         }
01326     }
01327 
01328 
01329 #if 0000
01330     if (b->name_is("(5394,21337)") || b->name_is("(21337,5394)")) {
01331         cerr << func << ": "; PRL(b);
01332         PRI(4); PRC(b->get_grape_rnb_sq()); PRL(n_neighbors);
01333     }
01334 #endif
01335 
01336 
01337     status = 2;
01338 
01339     if (n_neighbors > 0) {
01340 
01341         // Found at least one neighbor -- find the nearest and
01342         // determine the perturber list for a center-of-mass node.
01343         // Note that, if we get here, we recompute the nn pointer
01344         // for this particle, (possibly) overriding the result
01345         // returned by the GRAPE hardware.
01346 
01347         status = 0;
01348 
01349         real dmin_sq = VERY_LARGE_NUMBER;
01350         hdyn *bmin = NULL;
01351         real dcmin_sq = VERY_LARGE_NUMBER;
01352         hdyn *cmin = NULL;
01353 
01354         int npl = 0;
01355         hdyn **pl = NULL;
01356         real rpfac = 0;
01357 
01358         if (b->is_parent() && b->get_valid_perturbers()) {
01359 
01360             if (b->get_oldest_daughter()->get_slow())
01361                 clear_perturbers_slow_perturbed(b);
01362 
01363             b->new_perturber_list();
01364             // b->set_valid_perturbers(true);   // set in calling function,
01365                                                 // used as flag here
01366 
01367             pl = b->get_perturber_list();
01368             rpfac = b->get_perturbation_radius_factor();
01369         }
01370 
01371         for (int j = 0; j < n_neighbors; j++) {
01372 
01373             hdyn *bb = node_list[neighbor_list[j]];
01374 
01375             // bb is the j-th neighbor of b (list not ordered).
01376 
01377             if (bb != b) {              // bb = b shouldn't occur...
01378 
01379                 vector diff = b->get_pred_pos() - bb->get_pred_pos();
01380                 real d2 = diff * diff;
01381 
01382                 real sum_of_radii = get_sum_of_radii(b, bb);
01383                 update_nn_coll(b, 100,          // (100 = ID)       // inlined
01384                                d2, bb, dmin_sq, bmin,
01385                                sum_of_radii,
01386                                dcmin_sq, cmin);
01387 
01388                 // Recompute the perturber list for parent nodes.
01389                 // See equivalent code for use without GRAPE in
01390                 // hdyn_ev.C/flat_calculate_acc_and_jerk.
01391 
01392                 if (b->is_parent() && b->get_valid_perturbers()) {
01393 
01394                     if (is_perturber(b, bb->get_mass(),
01395                                      d2, rpfac)) {                  // inlined
01396 
01397                         if (npl < MAX_PERTURBERS)
01398                             pl[npl] = bb;
01399 
01400                         npl++;
01401                     }
01402                 }
01403             }
01404         }
01405 
01406         if (b->is_parent() && b->get_valid_perturbers()) {
01407 
01408             b->set_n_perturbers(npl);
01409 
01410             if (npl > MAX_PERTURBERS) {
01411                 b->remove_perturber_list();
01412 
01413 //              cerr << func << ": too many perturbers for "
01414 //                   << b->format_label()
01415 //                   << " -- deleting list" << endl << flush;
01416 
01417             }
01418         }
01419 
01420         if (bmin) {
01421             b->set_nn(bmin);
01422             b->set_d_nn_sq(dmin_sq);
01423 
01424 
01425 #if 0000
01426             if (b->name_is("(5394,21337)") || b->name_is("(21337,5394)")) {
01427                 cerr << "get_nbrs:  ";
01428                 PRC(b); PRC(bmin); PRL(dmin_sq);
01429             }
01430 #endif
01431 
01432 
01433         } else
01434             status = 2;
01435 
01436         if (cmin) {
01437             b->set_coll(cmin);
01438             b->set_d_coll_sq(dcmin_sq);
01439         } else
01440             status = 2;
01441     }
01442 
01443 
01444 #if 0000
01445     if (b->name_is("(5394,21337)") || b->name_is("(21337,5394)")) {
01446         cerr << func << ": ";
01447         PRC(b->get_grape_rnb_sq()); PRL(n_neighbors);
01448     }
01449 #endif
01450 
01451 
01452     // If no nearest neighbor or coll is found, enlarge the neighbor-sphere
01453     // radius and try again.
01454 
01455     if (status)
01456         b->set_grape_rnb_sq(RNB_INCREASE_FAC*b->get_grape_rnb_sq());
01457 
01458     return status;
01459 }
01460 
01461 
01462 
01463 #define MAX_FORCE_COUNT 20
01464 
01465 local INLINE int get_coll_and_perturbers(xreal xtime,
01466                                          hdynptr *ilist, int ni,
01467                                          real h2_crit,
01468                                          int nj_on_grape, int n_pipes)
01469 
01470 // Compute the colls, nns, and perturber lists for those nodes on the
01471 // i-list that require them.  Return the location following the last
01472 // node successfully treated.
01473 
01474 // Called by:   grape_calculate_acc_and_jerk()                  // global
01475 
01476 {
01477     static char *func = "get_coll_and_perturbers";
01478 
01479     int inext = 0;
01480 
01481 //    cerr << func << ": "; PRL(ni);
01482 
01483     for (int ip = 0; ip < ni; ip++) {
01484 
01485         int pipe = ip;
01486         hdyn *bb = ilist[ip];
01487 
01488         if (bb->get_grape_rnb_sq() > 0) {
01489 
01490             int count_force = 1;
01491             int status;
01492 
01493             while ((status = get_neighbors_and_adjust_h2(bb, pipe))) {
01494 
01495 //              if (bb->is_parent()) {
01496 //                  PRC(bb->get_time()); PRC(bb->format_label()); PRL(status);
01497 //                  PRC(count_force); PRL(bb->get_grape_rnb_sq());
01498 //              }
01499 
01500                 // Neighbor list must be recomputed:
01501                 //
01502                 //      status = 1  ==>  decrease radius
01503                 //      status = 2  ==>  increase radius
01504                 //
01505                 // The value of grape_rnb_sq has already been adjusted.
01506 
01507                 if (count_force > MAX_FORCE_COUNT
01508                     || (status == 2 && bb->get_grape_rnb_sq() > h2_crit)) {
01509 
01510                     // Give up -- can't find a neighbor.  Flag with nn = bb
01511                     // (not really necessary, but probably harmless), and set
01512                     // perturbers invalid, if relevant.
01513 
01514                     bb->set_nn(bb);                     // kira checks
01515                                                         // for nn = bb
01516                     bb->set_d_nn_sq(2*h2_crit);
01517 
01518                     if (bb->is_parent())
01519                         bb->set_valid_perturbers(false);
01520 
01521 
01522 #if 0000
01523                     if (bb->name_is("(5394,21337)")
01524                          || bb->name_is("(21337,5394)")) {
01525                         cerr << func << ":  setting nn = bb for ";
01526                         PRC(bb); PRL(bb->get_d_nn_sq());
01527                         PRC(status); PRL(count_force);
01528                     }
01529 #endif
01530 
01531 
01532                     break;                              // go on to next ip
01533 
01534                 } else {
01535 
01536                     // We have modified the neighbor sphere and must
01537                     // recompute the force to get the new neighbor list.
01538                     // Currently, we simply iterate until the neighbors
01539                     // for this particle are found (or we give up), then
01540                     // exit, returning the next ip on the list.
01541 
01542                     // Reducing rnb for a CM node means that we
01543                     // can't construct a valid perturber list from
01544                     // the neighbor information, so flag that here.
01545 
01546                     if (status == 1 && bb->is_parent())
01547                         bb->set_valid_perturbers(false);
01548 
01549 
01550 #if 00000
01551                     if (bb->name_is("(5394,21337)")
01552                          || bb->name_is("(21337,5394)")) {
01553                         cerr << func << ": recomputing force for ";
01554                         PRC(ip); PRL(bb);
01555                         PRL(ilist[ip]);
01556                     }
01557 #endif
01558 
01559 
01560                     // Recompute just this particle -- it will go in pipe 0.
01561 
01562                     pipe = 0;
01563                     ip = ni;                    // force exit from "for" loop
01564 
01565                     while (get_force_and_neighbors(xtime,
01566                                                    &bb,
01567                                                    1,
01568                                                    nj_on_grape, n_pipes,
01569                                                    true)) {
01570 
01571                         // The GRAPE neighbor list has overflowed; we must
01572                         // decrease the neighbor radius.  Jun says that
01573                         // there is no useful information in the returned
01574                         // number of neighbors, so just reduce the radius
01575                         // by a fixed factor.  Note that the reduction
01576                         // factor is much closer to 1 than the expansion
01577                         // factor used in get_neighbors_and_adjust_h2(),
01578                         // so we shouldn't run into an infinite loop.
01579                         // Nevertheless, cautiously count iterations
01580                         // and impose a (generous) upper limit.
01581 
01582                         bb->set_grape_rnb_sq(RNB_DECREASE_FAC
01583                                              * bb->get_grape_rnb_sq());
01584                         count_force++;
01585 
01586                         if (bb->is_parent())
01587                             bb->set_valid_perturbers(false);
01588 
01589                     }
01590 
01591                 }               // if (count_force...) {} else
01592 
01593 
01594 #if 0000
01595                     if (bb->name_is("(5394,21337)")
01596                          || bb->name_is("(21337,5394)")) {
01597                         cerr << func << ": repeating while loop with ";
01598                         PRL(bb->get_grape_rnb_sq());
01599                     }
01600 #endif
01601 
01602 
01603             }                   // while ((status = get_...))
01604 
01605         }                       // if (bb->get_grape_rnb_sq()...)
01606 
01607         inext++;
01608     }                           // for (ip...)
01609 
01610     return inext;
01611 }
01612 
01613 
01614 
01615 //                                              *****************************
01616 //                                              *****************************
01617 //                                              ***                       ***
01618 //                                              ***  The global function  ***
01619 //                                              ***                       ***
01620 //                                              *****************************
01621 //                                              *****************************
01622 
01623 
01624 void grape_calculate_acc_and_jerk(hdyn **next_nodes,
01625                                   int n_next,
01626                                   xreal xtime,
01627                                   bool restart)
01628 
01629 //  This function is called from kira_calculate_top_level_acc_and_jerk,
01630 //  which is called only from calculate_acc_and_jerk_for_list.
01631 
01632 {
01633     static char *func = "grape_calculate_acc_and_jerk";
01634 
01635     static int n_pipes = 0;             // number of pipelines to use
01636 
01637     static int nj_on_grape;             // current number of j-particles
01638                                         // in GRAPE memory
01639 
01640     if (DEBUG) {
01641         cerr << endl << func << "..." << endl << flush;
01642     }
01643 
01644     if (n_pipes == 0) n_pipes = g6_npipes_();
01645 
01646     if (n_next <= 0) return;
01647     hdyn *root = next_nodes[0]->get_root(); 
01648     kira_options *ko = root->get_kira_options();
01649 
01650     //------------------------------------------------------------------
01651 
01652     // Test the state of the GRAPE and open it if necessary.
01653     // If restart is true, we must reinitialize the GRAPE interface
01654     // after a change in the tree or other kira configuration.
01655     // 
01656     // (The GRAPE release check is now performed externally.  The main
01657     //  advantage to doing the check here was that we only had to do it
01658     //  once.  However a major disadvantage was that the hardware could
01659     //  get tied up unnecessarily by a process that was stuck elsewhere
01660     //  in the code (e.g. in a multiple encounter.)
01661     //
01662     // It is necessary to know when a restart has been triggered ONLY by
01663     // the release/reattachment of the GRAPE hardware.  Indicator is:
01664     //
01665     //          grape_reattached = true
01666 
01667     bool grape_reattached = false;
01668 
01669     if (!grape_is_open) {
01670 
01671         reattach_grape((real)xtime, func, ko);
01672 
01673         if (!restart) grape_reattached = true;
01674 
01675         // Restart irrespective of the actual argument.
01676 
01677         restart = true;
01678     }
01679 
01680     if (grape_was_used_to_calculate_potential) {
01681         restart = true;
01682         grape_was_used_to_calculate_potential = false;
01683     }
01684 
01685     if (restart) {
01686 //      cerr << "restarting GRAPE at time " << xtime << endl << flush;
01687         nj_on_grape = initialize_grape_arrays(root, !grape_reattached);
01688         n_previous_nodes = 0;
01689     }
01690 
01691     //------------------------------------------------------------------
01692 
01693     // Store the particles in the previous block to GRAPE memory
01694     // (i.e. update GRAPE for the previous step).
01695 
01696     for (int i = 0; i < n_previous_nodes; i++)
01697         send_j_node_to_grape(previous_nodes[i]);
01698 
01699     //------------------------------------------------------------------
01700 
01701     int i, n_top;
01702 
01703     // Create the list of top-level nodes in the present block step.
01704 
01705     for (n_top = i = 0; i < n_next; i++)
01706         if (next_nodes[i]->is_top_level_node())
01707             current_nodes[n_top++] = next_nodes[i];
01708 
01709     // Now n_top is the number of top-level nodes in the current list.
01710 
01711     // Initialize neighbor and perturber information and save a copy of
01712     // the current_nodes list as previous_nodes (for use next time around).
01713 
01714     n_previous_nodes = n_top;
01715     bool need_neighbors = false;
01716 
01717     for (i = 0; i < n_top; i++) {
01718 
01719         hdyn *bb = previous_nodes[i] = current_nodes[i];
01720 
01721         // Set a reasonable h2 value for this node.
01722 
01723         need_neighbors |= set_grape_neighbor_radius(bb, nj_on_grape);
01724 
01725         // Use valid perturbers to indicate whether the neighbor list
01726         // should be used to construct a perturber list.
01727 
01728         if (bb->is_parent())
01729             bb->set_valid_perturbers(true);
01730     }
01731 
01732     if (DEBUG) {
01733         cerr << func << ":  ";
01734         PRC(xtime); PRC(n_next); PRL(n_top);
01735     }
01736 
01737     //------------------------------------------------------------------
01738 
01739     // Calculate the force on the current_nodes list.
01740 
01741     real h2_crit = 8192 * current_nodes[0]->get_d_min_sq();
01742 
01743     // We will stop expanding the GRAPE neighbor sphere (in those cases
01744     // where expansion is indicated -- finding nearest neighbors and colls)
01745     // once its size exceeds the critical value h2_crit.  However, it is
01746     // OK to set grape_rnb_sq greater than h2_crit -- the neighbor sphere
01747     // then simply won't be expanded if no neighbors are found.
01748 
01749     // *** Should contain a factor of ~(m_max/<m>)^2, but not crucial...
01750 
01751     // Note:  for equal-mass systems and standard units, this critical
01752     //        radius is less than the interparticle spacing for N > ~1000.
01753 
01754     // Compute the i-forces in chunks of maximum size n_pipes.
01755     // Must recompute need_neighbors separately for each chunk.
01756 
01757     i = 0;
01758     while (i < n_top) {
01759 
01760         int ni = min(n_pipes, n_top - i);
01761 
01762         need_neighbors = false;
01763         for (int ip = 0; ip < ni; ip++)
01764             if (current_nodes[i+ip]->get_grape_rnb_sq() > 0) {
01765                 need_neighbors = true;
01766                 break;
01767             }
01768 
01769         if (DEBUG > 1) {
01770             PRI(2); PRC(i); PRC(ni); PRL(need_neighbors);
01771         }
01772 
01773         // Get the forces on particles i through i + ni - 1 and
01774         // read the GRAPE neighbor list, if necessary.  Function
01775         // get_force_and_neighbors (via force_by_grape) also sets
01776         // nn pointers.
01777 
01778         if (get_force_and_neighbors(xtime, current_nodes + i, ni,
01779                                     nj_on_grape, n_pipes, need_neighbors))
01780 
01781             // Hardware neighbor list overflow.  Restructure the list,
01782             // reduce neighbor radii, and retry starting with those nodes
01783             // for which colls are actually needed.  Forces and nns are
01784             // OK at this point, but neighbor lists are not.
01785 
01786             i += sort_nodes_and_reduce_rnb(current_nodes+i, ni);
01787 
01788         else if (need_neighbors) {
01789 
01790             // Neighbor lists are OK.  Get colls and perturber lists.
01791 
01792             i += get_coll_and_perturbers(xtime, current_nodes+i, ni,
01793                                          h2_crit, nj_on_grape, n_pipes);
01794         } else
01795 
01796             i += ni;
01797 
01798     }
01799 
01800     //------------------------------------------------------------------
01801 
01802     // Update the grape_nb_count flags.
01803 
01804     for (i = 0; i < n_top ; i++) {
01805 
01806         hdyn *bb = current_nodes[i];
01807 
01808 
01809 #if 0000
01810         if (bb->name_is("(5394,21337)")
01811             || bb->name_is("(21337,5394)"))
01812             cerr << "leaving " << func << endl << endl << flush;
01813 #endif
01814 
01815 
01816         // Frequency of coll checks is every fourth force calculation.
01817         // xx Frequency of perturber checks is every other force calculation.
01818 
01819         if (bb->is_leaf())
01820             bb->set_grape_nb_count((bb->get_grape_nb_count() + 1)%4);
01821         else
01822 
01823 //          bb->set_grape_nb_count((bb->get_grape_nb_count() + 1)%2);
01824 
01825 //          *** If we reduce the frequency of perturber checks, then we
01826 //          *** must be sure to restore the CMs on the perturber list,
01827 //          *** as the correction to the CM force depends on it...
01828 //          ***
01829 //          *** Some care is needed if we do reduce the frequency, as
01830 //          *** nodes may vanish or merge.  (However, the list contains
01831 //          *** only single stars after correction, so CM changes
01832 //          *** shouldn't be a problem).
01833 //          ***                                         (Steve, 6/01)
01834 
01835             bb->set_grape_nb_count(0);
01836     }
01837 
01838     if (DEBUG) {
01839         cerr << "...leaving " << func << endl << endl << flush;
01840     }
01841 }
01842 
01843 
01844 
01845 //  **********************************************************************
01846 //  *                                                                    *
01847 //  * grape_calculate_densities:  Determine particle densities, giving   *
01848 //  *                             zero density to particles with no      *
01849 //  *                             neighbor within sqrt(h2_crit).         *
01850 //  *                                                                    *
01851 //  **********************************************************************
01852 
01853 //  **********************************************************************
01854 //  *****  NOTE that the "new" density algorithm from hdyn_grape4.C  *****
01855 //  *****  should be incorporated here too.                          *****
01856 //  **********************************************************************
01857 
01858 local INLINE void set_grape_density_radius(hdyn *b, real rnn_sq)
01859 
01860 // For a single particle, try to adjust the initial radius so that
01861 // it will contain just ~10-20 neighbors (set r = 3*d_nn, if known).
01862 
01863 // Called by:   grape_calculate_densities()                     // global
01864 
01865 {
01866     if (b->get_nn() != NULL && b->get_nn() != b
01867         && b->get_d_nn_sq() < 0.1* VERY_LARGE_NUMBER
01868         && b->get_d_nn_sq() > 0) {
01869 
01870         // Node seems to have a valid nearest neighbor pointer.
01871 
01872         // Old code:
01873         //
01874         // b->set_grape_rnb_sq(9 * b->get_d_nn_sq());
01875 
01876         // New code (from GRAPE-4 version):
01877 
01878         // Modify the initial guess for particles with a close nn.
01879 
01880         if (DEBUG) {
01881             cerr << "nn OK for " << b->format_label() << ",  ";
01882             PRC(rnn_sq); PRL(sqrt(b->get_d_nn_sq()));
01883             PRL(b->get_nn()->format_label());
01884         }
01885 
01886 #if 0
01887         if (b->get_d_nn_sq() < rnn_sq)
01888             rnn_sq = sqrt(rnn_sq * b->get_d_nn_sq());   // empirical
01889         else
01890             rnn_sq = b->get_d_nn_sq();
01891 #endif
01892 
01893         rnn_sq = 5*b->get_d_nn_sq();
01894 
01895     } else {
01896 
01897         // Node does not know its nearest neighbor.  Value of d_nn_sq
01898         // is where the neighbor search stopped.
01899 
01900         // Old:
01901         //
01902         // b->set_grape_rnb_sq(9 * pow(b->get_d_min_sq(), 1.0/3.0));  // (??)
01903 
01904         rnn_sq = 4*max(rnn_sq, b->get_d_nn_sq());
01905 
01906         if (DEBUG)
01907             cerr << "no nn for " << b->format_label() << ",  ";
01908     }
01909 
01910     b->set_grape_rnb_sq(rnn_sq);
01911     if (DEBUG) PRL(sqrt(b->get_grape_rnb_sq()));
01912 }
01913 
01914 
01915 
01916 local void check_neighbors(hdyn *b, real rnb_sq, int indent = 0)
01917 
01918 // (The hard way...)
01919 
01920 {
01921     int nn = 0;
01922     b = b->get_top_level_node();
01923     for_all_daughters(hdyn, b->get_root(), bb)
01924         if (bb != b)
01925             if (square(bb->get_pos() - b->get_pos()) < rnb_sq) nn++;
01926     PRI(indent);
01927     cerr << "check:  " << nn << " neighbors within " << sqrt(rnb_sq)
01928          << " of " << b->format_label() << endl;
01929 }
01930 
01931 
01932 
01933 #define N_DENS  12      // density is based on the 12th nearest neighbor
01934 
01935 local INLINE bool count_neighbors_and_adjust_h2(hdyn * b, int pipe)
01936 
01937 // Determine density from the neighbor list, or increase the radius
01938 // of the neighbor sphere.
01939 
01940 // Called by:   grape_calculate_densities()                     // global
01941 
01942 {
01943     // Get the list of neighbors from the GRAPE.
01944 
01945     int n_neighbors = 0;
01946     int status = 0;
01947 #ifndef NO_G6_NEIGHBOUR_LIST
01948     status = g6_get_neighbour_list_(&cluster_id,
01949                                     &pipe,
01950                                     &max_neighbors,
01951                                     &n_neighbors,
01952                                     neighbor_list);
01953 #endif
01954 
01955 //    cerr << "  GRAPE "; PRC(max_neighbors); PRL(n_neighbors);
01956 //    check_neighbors(b, b->get_grape_rnb_sq(), 2);
01957 
01958     if (status) {
01959 
01960         // GRAPE has found too many neighbors:  n_neighbors >= max_neighbors.
01961         // Attempt to reduce the size of the neighbor sphere to contain
01962         // (say) 10*N_DENS stars.  Note (Steve, 12/01) that n_neighbors
01963         // returns equal to max_neighbors in case of overflow.
01964 
01965         if (n_neighbors >= max_neighbors) {     // should be redundant...
01966 
01967             real rnb_fac = pow(10*N_DENS/(real)n_neighbors, 0.6666667);
01968             b->set_grape_rnb_sq(rnb_fac*b->get_grape_rnb_sq());
01969 
01970             cerr << "count_neighbors_and_adjust_h2: "
01971                  << "neighbor list overflow for "
01972                  << b->format_label() << " (pipe "
01973                  << pipe << ")" << endl;
01974             PRC(n_neighbors); PRC(max_neighbors);
01975             cerr << "new grape_rnb = " << sqrt(b->get_grape_rnb_sq())
01976                  << endl;
01977 
01978             return false;
01979         }
01980     }
01981 
01982     if (n_neighbors < N_DENS) {
01983 
01984         // Too few neighbors.  Try again.
01985 
01986         if (DEBUG > 1) {
01987             cerr << "  increasing grape_rnb_sq for " << b->format_label()
01988                  << " (n_neighbors = " << n_neighbors << ", grape_rnb = "
01989                  << sqrt(b->get_grape_rnb_sq()) << ")" << endl;
01990         }
01991 
01992         // Old:
01993         //
01994         // real fac = 2;
01995         // if (n_neighbors < 4) fac = 4;
01996 
01997 #if 0
01998         real fac = 1.25;
01999         if (n_neighbors < 12) fac *= 1.25;
02000         if (n_neighbors < 8) fac *= 1.6;
02001         if (n_neighbors < 4) fac *= 1.6;
02002         if (n_neighbors < 2) fac *= 1.6;
02003 #endif
02004 
02005         real fac = min(2.0, pow((N_DENS+3.0)/(1.0+n_neighbors), 1.5));
02006 
02007         b->set_grape_rnb_sq(fac * b->get_grape_rnb_sq());
02008         return false;
02009     }
02010 
02011     // Make a list of nodes to send to compute_density().
02012 
02013     dyn **dynlist = new dynptr[n_neighbors];
02014 
02015     real d_max = 0;
02016     for (int j = 0; j < n_neighbors; j++) {
02017 
02018         hdyn *bb = node_list[neighbor_list[j]];
02019         dynlist[j] = (dyn*)bb;
02020 
02021         // bb is the j-th neighbor of b.
02022 
02023         if (DEBUG > 1) {
02024             if (bb != b) {
02025                 vector diff = b->get_pred_pos() - bb->get_pred_pos();
02026                 real d2 = diff * diff;
02027                 d_max = max(d_max, d2);
02028             }
02029         }
02030     }
02031 
02032     if (DEBUG > 1) {
02033         real grape_rnb = sqrt(b->get_grape_rnb_sq());
02034         d_max = sqrt(d_max);
02035         cerr << "  " << b->format_label() << ": ";
02036         PRC(n_neighbors), PRC(grape_rnb), PRL(d_max);
02037     }
02038 
02039     compute_density(b, N_DENS, dynlist, n_neighbors);   // writes to dyn story
02040 
02041     // Strangely, compute_density sometimes fails to write the proper
02042     // info to the dyn story.  Reason and circumstances still unknown...
02043 
02044     if (DEBUG > 1) {
02045         if (find_qmatch(b->get_dyn_story(), "density_time")
02046             && find_qmatch(b->get_dyn_story(), "density_k_level")
02047             && find_qmatch(b->get_dyn_story(), "density")) {
02048 
02049             real density_time = getrq(b->get_dyn_story(), "density_time");
02050             int  density_k    = getiq(b->get_dyn_story(), "density_k_level");
02051             real density      = getrq(b->get_dyn_story(), "density");
02052 
02053             PRI(4); cerr << b->format_label() << ": ";
02054             PRC(density_time); PRC(density_k); PRL(density);
02055             PRI(4); PRL(b->get_pos());
02056             check_neighbors(b, b->get_grape_rnb_sq(), 4);
02057 
02058         } else {
02059 
02060             PRI(4); cerr << "density data unavailable for "
02061                          << b->format_label() << endl;
02062         }
02063     }
02064 
02065     delete [] dynlist;
02066     return true;
02067 }
02068 
02069 
02070 
02071 local INLINE bool get_densities(xreal xtime, hdyn *nodes[],
02072                                 int ni, real h2_crit,
02073                                 int nj_on_grape, int n_pipes)
02074 {
02075     // Compute the densities of the ni particles in nodes[].
02076     // Return true iff an error occurred and densities could
02077     // not be determined.
02078 
02079     static char *func = "get_densities";
02080     bool error = false;
02081 
02082     if (DEBUG)
02083         cerr << func << "..." << nodes[0]->format_label()
02084              << "  " << nodes[0]->get_grape_rnb_sq() << endl << flush;
02085 
02086     if (ni < 0) return error;                   // should never happen
02087 
02088     // Get the forces and neighbor lists for the current group of particles.
02089 
02090     int status = 0;
02091     if (status = get_force_and_neighbors(xtime, nodes, ni,
02092                                          nj_on_grape, n_pipes, true)) {
02093 
02094         // Neighbor list overflow.  Return with error = true will cause
02095         // the calling function to reduce neighbor radii and retry.
02096 
02097         cerr << func << ": " << "error 1 getting GRAPE neighbor data: "; 
02098         PRL(status);
02099 
02100         error = true;
02101 
02102     } else {
02103 
02104         // Determine densities for the present block of particles.
02105 
02106         for (int ip = 0; ip < ni; ip++) {
02107 
02108             int n_retry = 0;
02109             hdyn *bb = nodes[ip];
02110 
02111             if (bb->get_grape_rnb_sq() > 0) {       // neighbors still needed
02112 
02113                 while (!count_neighbors_and_adjust_h2(bb, ip)) {
02114 
02115                     // (Function sets density on success.)
02116 
02117                     if (bb->get_grape_rnb_sq() > h2_crit) {
02118 
02119                         // Write zero density to the dyn story.
02120 
02121                         putrq(bb->get_dyn_story(), "density_time",
02122                               (real)bb->get_system_time());
02123                         putrq(bb->get_dyn_story(), "density", 0.0);
02124 
02125                         if (DEBUG > 1) {
02126                             PRI(2); PR(bb->get_grape_rnb_sq());
02127                             cerr << " too large for "
02128                                  << bb->format_label() << endl;
02129                             PRI(2); PRL(bb->get_pos());
02130                             check_neighbors(bb, bb->get_grape_rnb_sq(), 2);
02131                         }
02132 
02133                         break;
02134 
02135                     } else {
02136 
02137                         // Changed the neighbor sphere size; recompute all
02138                         // forces and neighbor lists (probably overkill).
02139 
02140                         // If this becomes an issue, can do better by setting
02141                         // neighbor radii for successfully handled particles
02142                         // to zero.  Also, we could restart after those
02143                         // particles, as in grape_calculate_acc_and_jerk().
02144                         //                                      [Steve, 6/01]
02145 
02146                         if (++n_retry > 20) 
02147                             hw_err_exit(func, 2, nodes[0]);
02148 
02149                         if (DEBUG > 1
02150                             || (DEBUG > 0 && n_retry > 4 && n_retry%5 == 0)
02151                             || (n_retry > 9 && n_retry%10 == 0) ) {
02152                             PRI(2); cerr << func << ": recomputing forces for "
02153                                 << nodes[0]->format_label() << " + " << ni-1
02154                                 << endl;
02155                             cerr << "                 first rnb_sq = "
02156                                  << nodes[0]->get_grape_rnb_sq()
02157                                  << ",  n_retry = " << n_retry << endl;
02158                         }
02159 
02160                         int status = 0;
02161                         if (status = get_force_and_neighbors(xtime, nodes, ni,
02162                                                 nj_on_grape, n_pipes, true)) {
02163 
02164                             cerr << func << ": "
02165                                  << "error 2 getting GRAPE neighbor data: "; 
02166                             PRL(status);
02167 
02168                             error = true;
02169 
02170                             ip = ni;    // force exit from for loop
02171                             break;
02172                         }
02173                     }
02174                 }
02175 
02176                 if (!error)
02177                     bb->set_grape_rnb_sq(0);    // don't recalculate density
02178             }
02179         }
02180     }
02181 
02182     if (DEBUG) {
02183         cerr << "leaving " << func << "...";
02184         PRL(error);
02185     }
02186 
02187     return error;
02188 }
02189 
02190 
02191 
02192 //                                              *****************************
02193 //                                              *****************************
02194 //                                              ***                       ***
02195 //                                              ***  The global function  ***
02196 //                                              ***                       ***
02197 //                                              *****************************
02198 //                                              *****************************
02199 
02200 
02201 void grape_calculate_densities(hdyn* b,                 // root node
02202                                real h2_crit)            // default = 4
02203 {
02204     static char *func = "grape_calculate_densities";
02205 
02206     if (DEBUG) {
02207         cerr << endl << func << "..."; PRL(h2_crit);
02208     }
02209 
02210 #ifdef NO_G6_NEIGHBOUR_LIST
02211     cerr << "No hardware neighbor list available..." << endl;
02212     return;
02213 #endif
02214 
02215     static int max_neighbors = MAX_PERTURBERS;
02216     static int neighbor_list[MAX_PERTURBERS];
02217 
02218     if (!grape_is_open)
02219         reattach_grape(b->get_real_system_time(), func, b->get_kira_options());
02220 
02221     // Copy all (predicted pos) top-level nodes to the GRAPE hardware.
02222 
02223     int nj_on_grape = initialize_grape_arrays(b,
02224                                               true,     // (irrelevant)
02225                                               true);    // predict pos
02226 
02227     // Make a list of top-level nodes.
02228 
02229     hdyn **top_nodes = new hdynptr[nj_on_grape];
02230 
02231     int n_top = 0;
02232     for_all_daughters(hdyn, b, bb)
02233         top_nodes[n_top++] = bb;
02234 
02235     // Set h2 values.
02236 
02237     // New code from GRAPE-4 version:
02238 
02239     // Determine rnn_sq ~ square of the average interparticle spacing,
02240     // for use as a scale in setting h2.  Note the connections between
02241     // d_min, r90, and rnn.
02242 
02243     real r90_sq = b->get_d_min_sq() / square(b->get_d_min_fac());
02244     real rnn_sq = r90_sq * pow((real)nj_on_grape, 4.0/3);
02245 
02246     for (int j = 0; j < n_top; j++)
02247 //      set_grape_density_radius(top_nodes[j], h2_crit);
02248         set_grape_density_radius(top_nodes[j], rnn_sq);
02249 
02250     int n_pipes = g6_npipes_();
02251 
02252     int n_retry = 0;
02253     int count = 0;
02254 
02255     // Compute the densities in chunks of maximum size n_pipes.
02256 
02257     int i = 0;
02258 
02259     while (i < n_top) {
02260 
02261         int inext = i;
02262         int ni = min(n_pipes, n_top - i);
02263 
02264 #ifdef SPZ_GRAPE6
02265 
02266         // Use one pipeline only to (try to) avoid neighbor list overflow...
02267         // Implemented by SPZ on May 8 2001.
02268         // May no longer be necessary (Steve, 6/01).
02269 
02270         ni = 1;
02271 #endif
02272 
02273         // Get the forces on particles i through i + ni - 1, determine
02274         // their neighbors and hence their densities.
02275 
02276         // Code follows that in grape_calculate_acc_and_jerk.
02277         // May be possible to combine the two...
02278 
02279         if (get_densities(b->get_system_time(),
02280                           top_nodes + i, ni, h2_crit,
02281                           nj_on_grape, n_pipes)) {
02282 
02283             // The neighbor list overflowed.  Reduce all current neighbor
02284             // radii and try again.
02285 
02286             if (DEBUG) {
02287                 cerr << "reducing neighbor radii: i = " << i
02288                      << ", first rnb_sq = " << top_nodes[i]->get_grape_rnb_sq()
02289                      << endl;
02290             }
02291 
02292             // Reduction factor is 0.9 for now...
02293 
02294             for (int j = i; j < i + ni; j++) {
02295                 hdyn *bb = top_nodes[j];
02296                 bb->set_grape_rnb_sq(0.9 * bb->get_grape_rnb_sq());
02297             }
02298 
02299             n_retry++;
02300             if (++count > 20) {
02301 
02302                 // Too many retries.  Quit in confusion...
02303 
02304                 cerr << func << ": "
02305                      << "error getting GRAPE neighbor data: " << endl;
02306 
02307                 hw_err_exit(func, 1, b);
02308             }
02309 
02310         } else {
02311 
02312             // Move on to the next block of particles.
02313 
02314             i += ni;
02315             count = 0;
02316         }
02317     }
02318 
02319     // Timestamp the root node.
02320 
02321     putrq(b->get_dyn_story(), "density_time", (real)b->get_system_time());
02322 
02323     if (n_retry > 10) {
02324         cerr << func << ":  ";
02325         PRL(n_retry);
02326     }
02327 
02328     if (DEBUG)
02329         cerr << "...leaving " << func << endl << endl << flush;
02330 
02331     // Force cleanup later.
02332 
02333     grape_was_used_to_calculate_potential = true;
02334     delete [] top_nodes;
02335 }
02336 
02337 
02338 
02339 //  **********************************************************************
02340 //  *                                                                    *
02341 //  * External cleanup -- delete local static arrays.                    *
02342 //  *                                                                    *
02343 //  **********************************************************************
02344 
02345 void clean_up_hdyn_grape()
02346 
02347 // Explicitly delete static local data.
02348 
02349 {
02350     // From initialize_grape_arrays()/grape_calculate_acc_and_jerk():
02351 
02352     if (node_list) delete [] node_list;
02353     if (current_nodes) delete [] current_nodes;
02354     if (previous_nodes) delete [] previous_nodes;
02355 
02356     // From force_by_grape():
02357 
02358     if (iindex) delete [] iindex;
02359     if (ipos) delete [] ipos;
02360     if (ivel) delete [] ivel;
02361     if (iacc) delete [] iacc;
02362     if (ijerk) delete [] ijerk;
02363     if (ipot) delete [] ipot;
02364     if (ih2) delete [] ih2;
02365     if (inn) delete [] inn;
02366 }

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