Main Page   Class Hierarchy   Data Structures   File List   Data Fields   Globals  

low_n3_evol.C

Go to the documentation of this file.
00001 
00019 
00020 //                9/7/95:  Added "double unperturbed" motion near
00021 //                         close binary periastron.  SMcM
00022 
00023 // Starlab library function.
00024 
00025 #include "sdyn3.h"
00026 
00027 #ifndef TOOLBOX
00028 
00029 #define UNPERTURBED_DIAG false
00030 #define PRECISION 12
00031 
00032 local void restore_pos_and_vel(kepler& inner, kepler& outer, real time,
00033                                int n_transform,
00034                                sdyn3* b1, sdyn3* b2, sdyn3* b3,
00035                                vector& cmpos, vector& cmvel)
00036 {
00037     inner.transform_to_time(time);
00038     if (n_transform == 2) outer.transform_to_time(time);
00039 
00040     // Reconstruct the new sdyn3s.  Note that kepler_pair_to_triple
00041     // will set pos and vel, but we really want to change new_pos
00042     // and new_vel.
00043 
00044     kepler_pair_to_triple(inner, outer, b1, b2, b3);
00045 
00046     for_all_daughters(sdyn3, b1->get_parent(), bb) {
00047         bb->inc_pos(cmpos);
00048         bb->inc_vel(cmvel);
00049         bb->store_old_into_new();
00050     }
00051 }
00052 
00053 local real total_energy(sdyn3* b)
00054 {
00055     real k = 0, u = 0, dissipation = 0;
00056 
00057     for_all_daughters(sdyn3, b, bi) {
00058         k += bi->get_mass() * bi->get_vel() * bi->get_vel();
00059         dissipation += bi->get_energy_dissipation();
00060         for (sdyn3 * bj = bi->get_younger_sister(); bj != NULL;
00061              bj = bj->get_younger_sister())
00062             u -= bi->get_mass() * bj->get_mass()
00063                   / abs(bi->get_pos() - bj->get_pos());
00064     }
00065 
00066     return 0.5*k + u + dissipation;
00067 }
00068 
00069 static int save_flag = 1;
00070 static real last_error = 0, last_test3 = 0;
00071 
00072 local void print_unperturbed_diag(sdyn3* b, sdyn3* b1, sdyn3* b2, sdyn3* b3,
00073                                   kepler& inner, kepler& outer,
00074                                   char* header)
00075 {
00076     if (UNPERTURBED_DIAG) {
00077 
00078         int p = cerr.precision(PRECISION);      // nonstandard precision
00079 
00080         if (header) cerr << header << endl;
00081 
00082         cerr << "  inner binary:"
00083              << "    time = "
00084              << inner.get_time() + (real)b->get_time_offset()
00085              << "  t_peri = " << inner.get_time_of_periastron_passage()
00086                                         + (real)b->get_time_offset() << endl
00087              << "    r = " << inner.get_separation()
00088              << "  a = " << inner.get_semi_major_axis()
00089              << "  e = " << inner.get_eccentricity() << endl
00090              << "    mass = " << inner.get_total_mass() << endl
00091              << "    pos = " << inner.get_rel_pos() << endl
00092              << "    vel = " << inner.get_rel_vel() << endl
00093              << "    th  = " << inner.get_true_anomaly() << endl
00094              << "    l = " << inner.get_longitudinal_unit_vector() << endl
00095              << "    n = " << inner.get_normal_unit_vector() << endl;
00096 
00097         cerr << "  energy test 1:  " << inner.get_energy()
00098              << " ?= " << 0.5 * square(b1->get_vel() - b2->get_vel())
00099                          - (b1->get_mass() + b2->get_mass())
00100                              / abs(b1->get_pos() - b2->get_pos()) << endl;
00101 
00102         cerr << "  outer binary:"
00103              << "    R = " << outer.get_separation()
00104              << "  a = " << outer.get_semi_major_axis()
00105              << "  e = " << outer.get_eccentricity() << endl
00106              << "    mass = " << outer.get_total_mass() << endl
00107              << "    pos = " << outer.get_rel_pos() << endl
00108              << "    vel = " << outer.get_rel_vel() << endl
00109              << "    th  = " << outer.get_true_anomaly() << endl
00110              << "    l = " << outer.get_longitudinal_unit_vector() << endl
00111              << "    n = " << outer.get_normal_unit_vector() << endl;
00112 
00113         vector bcm_pos = (b1->get_mass() * b1->get_pos()
00114                            + b2->get_mass() * b2->get_pos())
00115                              / (b1->get_mass() + b2->get_mass());
00116         vector bcm_vel = (b1->get_mass() * b1->get_vel()
00117                            + b2->get_mass() * b2->get_vel())
00118                              / (b1->get_mass() + b2->get_mass());
00119 
00120         cerr << "  energy test 2:  " << outer.get_energy()
00121              << " ?= " << 0.5 * square(b3->get_vel() - bcm_vel)
00122                          - (b1->get_mass() + b2->get_mass() + b3->get_mass())
00123                              / abs(b3->get_pos() - bcm_pos) << endl;
00124 
00125         real r = inner.get_separation();
00126         real R = outer.get_separation();
00127         cerr << "  check r: " << r << " ?= "
00128              << abs(b2->get_pos() - b1->get_pos()) << endl;
00129         cerr << "  check R: " << R << " ?= "
00130              << abs(b3->get_pos() - bcm_pos) << endl;
00131 
00132         // This term is basically the error incurred by the assumption
00133         // of double unperturbed motion (apart from higher-order terms):
00134 
00135         real m1 = b1->get_mass();
00136         real m2 = b2->get_mass();
00137         real m3 = b3->get_mass();
00138         real test3 = - m1*m3 / abs(b3->get_pos() - b1->get_pos())
00139                      - m2*m3 / abs(b3->get_pos() - b2->get_pos())
00140                      + (m1+m2)*m3 / outer.get_separation();
00141 
00142         real coeff = (m1*m2/(m1+m2)) * (0.5*m3/pow(R,3));
00143         real term1 = coeff * r*r;
00144         real term2 = coeff * (-3) * pow(inner.get_rel_pos()
00145                                      * outer.get_rel_pos() / R, 2);
00146 
00147         cerr << "  energy test 3:  " << test3
00148              << " =? " << term1 + term2 << endl;
00149 
00150         real error = total_energy(b) - b->get_e_tot_init();
00151         cerr << "  energy error = " << error << endl;
00152 
00153         if (save_flag) {
00154             last_test3 = test3;
00155             last_error = error;
00156         } else {
00157             cerr << "  delta(test3) = "
00158                  << test3 - last_test3 << endl;
00159             cerr << "  delta(error) = "
00160                  << error - last_error << endl;
00161         }
00162 
00163         save_flag = 1 - save_flag;
00164         cerr.precision(p);
00165 
00166         cerr << flush;
00167     }
00168 }
00169 
00170 local void print_perturbed_error(kepler& inner, kepler& outer,
00171                                  sdyn3* b1, real dt)
00172 {
00173     // On entry, inner is in its initial state; outer has been
00174     // advanced to the final time.
00175 
00176     cerr << "  inner, outer time = " << inner.get_time()
00177          << "  " << outer.get_time() << endl;
00178 
00179     // Evolve outer binary to the time of inner periastron and get
00180     // relevant parameters.
00181 
00182     outer.transform_to_time(outer.get_time() - 0.5*dt);
00183 
00184     real M = outer.get_total_mass() - inner.get_total_mass();
00185     real R = abs(outer.get_separation());
00186     real X = outer.get_rel_pos() * inner.get_longitudinal_unit_vector();
00187     real Y = outer.get_rel_pos() * inner.get_transverse_unit_vector();
00188     real Z = outer.get_rel_pos() * inner.get_normal_unit_vector();
00189 
00190     // Evaluate the inner binary integral.
00191 
00192     real E = inner.get_energy();
00193     real e = inner.get_eccentricity();
00194 
00195     if (E >= 0 || e <= 0 || e >= 1) return;     // Safety check!
00196 
00197     real a = inner.get_semi_major_axis();
00198     real h = inner.get_angular_momentum();
00199     real r = inner.get_separation();
00200     real th = inner.get_true_anomaly();
00201 
00202     real rp = a * (1 - e);
00203     real ra = a * (1 + e);
00204 
00205     // Attempt to predict the energy change in the quadrupole approximation.
00206 
00207     real red_mass = b1->get_mass()
00208                         * (1 - b1->get_mass()/inner.get_total_mass());
00209     real coeff = 6*red_mass*M*X*Y/pow(R,5);
00210 
00211     // Energy terms come from algebra and Maple, and are highly suspect!
00212 
00213     real mu = cos(th);
00214     real I1 = a*a*pow(1-e*e,2) *
00215                 (-sin(th) * (2 - e*e + mu*e*(3-2*e*e))
00216                           / (e*(1-e*e)*pow(1+mu*e,2))
00217                  + (asin((mu+e)/(1+mu*e))-M_PI/2)
00218                           * (2-3*e*e) / (e*e*pow(1-e*e,1.5))
00219                  + 2*th / (e*e));
00220 
00221     real f1 = pow(a*e,2) - pow(a-r,2);
00222     real f2 = atan((r-a*(1-e*e))/sqrt((1-e*e)*f1)) - M_PI/2;
00223     real f3 = asin((a-r)/(a*e)) - M_PI/2;
00224     real I2 = (h/(e*e*sqrt(2*abs(E)))) *
00225                 ((e*e-2)*sqrt(f1) + 2*a*pow(1-e*e,1.5)*f2 - a*f3*(2-3*e*e));
00226 
00227     cerr << "  predicted potential change = "
00228          << coeff * r*r * cos(th) * sin(th)
00229          << endl;
00230     cerr << "  predicted error = " << coeff * (I1 + I2) << "\n";
00231     cerr << "  terms:  I1, I2 = " << I1 << "  " << I2 << endl;
00232 
00233     // The terms for dv are more reliable...(?)
00234 
00235     real j0 = asin((a-r)/(a*e)) - M_PI/2;
00236     real term1 = 1.5*a*a*e*e * j0;
00237     real term2 = (a*(0.5+e*e)+0.5*r) * sqrt(a*a*e*e-(a-r)*(a-r));
00238     real I = (term1 + term2) / (e*sqrt(2*abs(E)));
00239     real coeffdv = -2*M/pow(R,5) * I;
00240 
00241     vector dv = coeffdv * (-(R*R - 3*X*X) * inner.get_longitudinal_unit_vector()
00242                                  - 3*X*Y  * inner.get_transverse_unit_vector()
00243                                  - 3*X*Z  * inner.get_normal_unit_vector());
00244 
00245     // We have added a "-" to the longitudinal term here because inner
00246     // is not yet advanced to the final time (v_long --> -v_long).
00247 
00248     cerr << "  predicted dv = " << dv << endl;
00249     cerr << "  associated dE = " << red_mass*dv*inner.get_rel_vel() << endl;
00250     cerr << "  terms:  " << term1 << "  " << term2 << endl;
00251 }
00252 
00253 static real tol_23 = -1;
00254 
00255 local bool unperturbed_step(sdyn3* b,           // n-body system pointer
00256                             real tidal_tolerance,
00257                             real& true_dt,      // new timestep
00258                             int&  collision_flag)
00259 {
00260     // Check for and implement unperturbed motion.
00261 
00262     // Only take an unperturbed step if the unperturbed criterion is met
00263     // by both the old and the final systems.  (This is necessary for time
00264     // symmetry, and is probably a good idea anyway.)
00265 
00266     // Return TRUE iff an unperturbed step was taken.
00267     // If so,
00268     //    1. replace new_pos and new_vel by updated quantities,
00269     //    2. set true_dt and collision_flag,
00270 
00271     // Need to make the initial cut reasonably efficient.  Use the
00272     // nearest-neighbor information returned by the integrator: each
00273     // body has nn, nn_dr2, nn_label set.  We may even want to copy
00274     // SJA and have some sort of time-step criterion!
00275 
00276     if (tidal_tolerance <= 0) return FALSE;
00277 
00278     sdyn3* bi = b->get_oldest_daughter();
00279     sdyn3* bj = bi->get_younger_sister();
00280     sdyn3* bk = bj->get_younger_sister();
00281 
00282     real min_sep_sq = min(min(bi->get_nn_dr2(), bj->get_nn_dr2()),
00283                           bk->get_nn_dr2());
00284     real max_sep_sq = max(max(bi->get_nn_dr2(), bj->get_nn_dr2()),
00285                           bk->get_nn_dr2());
00286 
00287     // The following mass factor represents a "worst-case" scenario
00288     // (see the comments about tidal factors in scatter3.C).  For
00289     // binary components 1 and 2, the tidal term should really be
00290     //
00291     //          (m1 + m2) * tolerance / (m1 + m2 + m3).
00292     //
00293     // For now, at least, we take the minimum possible binary mass.
00294 
00295     if (tol_23 < 0) {
00296         real total_mass = bi->get_mass() + bj->get_mass() + bk->get_mass();
00297         real max_mass = max(max(bi->get_mass(), bj->get_mass()),
00298                             bk->get_mass());
00299         tol_23 = pow((1 - max_mass/total_mass) * tidal_tolerance, 0.6666667);
00300     }
00301 
00302     if (min_sep_sq > max_sep_sq * tol_23) return FALSE;
00303 
00304     // -------------------------------------------------------------------
00305     // Passed the first cut!
00306 
00307     // Now determine the binary components, and calculate the separation
00308     // between the binary center of mass and the third body more carefully.
00309 
00310     sdyn3 *b1, *b2, *b3;
00311 
00312     // Find a component of the binary:
00313 
00314     if (min_sep_sq == bi->get_nn_dr2())
00315         b1 = bi;
00316     else if (min_sep_sq == bj->get_nn_dr2())
00317         b1 = bj;
00318     else
00319         b1 = bk;
00320 
00321     // Other component:
00322 
00323     b2 = (sdyn3*)b1->get_nn();
00324     if (!b2) return FALSE;      // No neighbor defined yet.
00325 
00326     // Third star:
00327 
00328     if (bi != b1 && bi != b2)
00329         b3 = bi;
00330     else if (bj != b1 && bj != b2)
00331         b3 = bj;
00332     else
00333         b3 = bk;
00334 
00335     // Binary components must be approaching.
00336 
00337     vector r = b2->get_pos() - b1->get_pos();
00338 
00339     if (r * (b2->get_vel() - b1->get_vel()) >= 0) return FALSE;
00340 
00341     // Binary components must be close compared to the distance
00342     // to the third body
00343 
00344     real m12 = b1->get_mass() + b2->get_mass();
00345     real m123 = m12 + b3->get_mass();
00346 
00347     // Center of mass position of the inner binary:
00348     
00349     vector binary_cmpos = (b1->get_mass() * b1->get_pos()
00350                             + b2->get_mass() * b2->get_pos()) / m12;
00351 
00352     vector R = b3->get_pos() - binary_cmpos;
00353 
00354     real true_tol_23 =  pow((m12/m123) * tidal_tolerance, 0.6666667);
00355     if (square(r) > square(R) * true_tol_23) return FALSE;
00356 
00357     // -------------------------------------------------------------------
00358     // We have unperturbed motion.  Keep track of the system center of
00359     // mass (which will be lost by the kepler structures below).
00360 
00361     vector system_cmpos = (m12 * binary_cmpos
00362                            + b3->get_mass() * b3->get_pos()) / m123;
00363 
00364     // Center of mass velocity of the inner binary:
00365 
00366     vector binary_cmvel = (b1->get_mass() * b1->get_vel()
00367                             + b2->get_mass() * b2->get_vel()) / m12;
00368 
00369     vector system_cmvel = (m12 * binary_cmvel
00370                             + b3->get_mass() * b3->get_vel()) / m123;
00371 
00372     // Make kepler structures out of the inner and outer orbits.
00373 
00374     kepler inner, outer;
00375 
00376     // Inner orbit:
00377 
00378     set_kepler_from_sdyn3(inner, b1, b2);
00379 
00380     // Outer orbit:
00381 
00382     outer.set_time(b3->get_time());
00383     outer.set_total_mass(m123);    
00384     outer.set_rel_pos(R);
00385     outer.set_rel_vel(b3->get_vel() - binary_cmvel);
00386  
00387     outer.initialize_from_pos_and_vel();
00388 
00389     real t_init = inner.get_time();
00390     real t_peri = inner.get_time_of_periastron_passage();
00391 
00392     // Update the closest-approach variables.  Assume that the pointers
00393     // and labels don't need to be changed.
00394 
00395     real peri_sq = inner.get_periastron() * inner.get_periastron();
00396     b1->set_nn_dr2(min(b1->get_nn_dr2(), peri_sq));
00397     b2->set_nn_dr2(min(b2->get_nn_dr2(), peri_sq));
00398     b1->set_min_nn_dr2(min(b1->get_min_nn_dr2(), peri_sq));
00399     b2->set_min_nn_dr2(min(b2->get_min_nn_dr2(), peri_sq));
00400 
00401     print_unperturbed_diag(b, b1, b2, b3,
00402                            inner, outer,
00403                            "Beginning unperturbed motion");
00404 
00405     // Check for a collision at binary periastron.
00406 
00407     if (inner.get_periastron() <= b1->get_radius() + b2->get_radius()) {
00408 
00409         // Move to periastron and return.
00410 
00411         restore_pos_and_vel(inner, outer, t_peri, 2,
00412                             b1, b2, b3,
00413                             system_cmpos, system_cmvel);
00414         collision_flag = 1;
00415         true_dt = t_peri - t_init;
00416 
00417         return TRUE;
00418     }
00419 
00420     real dt = 2*(t_peri - t_init);
00421 
00422     // Require that both the old and the final configurations pass
00423     // the "unperturbed" test.
00424 
00425     outer.transform_to_time(t_init + dt);
00426 
00427     real sep = outer.get_separation();
00428     if (square(r) > sep * sep * true_tol_23) {
00429 
00430         // Restore the original system.
00431 
00432         if (UNPERTURBED_DIAG)
00433             cerr <<
00434     "unperturbed_step: failed unperturbed test after unperturbed step!\n";
00435 
00436         restore_pos_and_vel(inner, outer, t_init, 2, b1, b2, b3,
00437                             system_cmpos, system_cmvel);
00438 
00439         save_flag = 1;
00440         return FALSE;
00441     }
00442 
00443     if (UNPERTURBED_DIAG) print_perturbed_error(inner, outer, b1, dt);
00444 
00445     // Advance the inner orbit to an outgoing inner binary at the
00446     // same separation (outer orbit has already been transformed).
00447 
00448     restore_pos_and_vel(inner, outer, t_init + dt, 1, b1, b2, b3,
00449                         system_cmpos, system_cmvel);
00450     true_dt = dt;
00451 
00452     print_unperturbed_diag(b, b1, b2, b3,
00453                            inner, outer,
00454                            "Ending unperturbed motion");
00455 
00456     // Note that old and new quantities are the same on exit.
00457 
00458     return TRUE;
00459 }
00460 
00461 local void predict(sdyn3* b,            // n-body system pointer
00462                    real dt)             // timestep
00463 {
00464     // Predict the entire system from the current time to time + dt.
00465 
00466     if(b->get_oldest_daughter() !=NULL)
00467         for_all_daughters(sdyn3, b, bb)
00468             predict(bb, dt);
00469     else
00470         b->taylor_pred_new_pos_and_vel(dt);
00471 }
00472 
00473 local void correct(sdyn3* b,            // n-body system pointer
00474                    real new_dt,         // new timestep
00475                    real prev_new_dt)    // previous new timestep
00476 {
00477     // Apply Hermite corrector step to the entire system.
00478 
00479     if(b->get_oldest_daughter() !=NULL)
00480         for_all_daughters(sdyn3, b, bb)
00481             correct(bb, new_dt, prev_new_dt);
00482     else {
00483         b->correct_new_acc_and_jerk(new_dt, prev_new_dt);
00484         b->correct_new_pos_and_vel(new_dt);
00485     }
00486 }
00487 
00488 local void calculate_energy(sdyn3* b, real& ekin, real& epot)
00489 {
00490     ekin = epot = 0;
00491     for_all_daughters(sdyn3, b, bb) {
00492         epot += bb->get_mass() * bb->get_pot();
00493         ekin += 0.5 * bb->get_mass() * (bb->get_vel() * bb->get_vel());
00494     }
00495     epot *= 0.5;
00496 }
00497 
00498 typedef real (*tfp)(sdyn3*, real);
00499 
00500 local bool step(sdyn3* b,       // sdyn3 array
00501                 real& t,        // time
00502                 real eps,       // softening length
00503                 real eta,       // time step parameter
00504                 real dt,        // time step of the integration 
00505                 real max_dt,    // maximum time step (to end of the integration)
00506                 int& end_flag,  // to flag that integration end is reached
00507                 tfp  the_tfp,   // timestep function pointer
00508                 int  n_iter,    // number of iterations
00509                 int  x_flag,    // exact-time termination flag
00510                 int  s_flag)    // symmetric timestep ?
00511 {
00512     // Take a timestep, symmetrizing (n_iter iterations) if requested.
00513     // Note that the actual time step taken will in general not equal dt.
00514 
00515     bool unpert_flag = FALSE;
00516 
00517     real true_dt = dt; 
00518     int collision_flag = 0;
00519 
00520     if (eps > 0 ||
00521         !(unpert_flag = unperturbed_step(b, DEFAULT_TIDAL_TOL_FACTOR,
00522                                          true_dt, collision_flag))) {
00523 
00524         // Predict all particles to time + dt.
00525 
00526         predict(b, dt);
00527 
00528         real new_dt = dt; 
00529         for (int i = 0; i <= n_iter; i++) {
00530 
00531             // Get acc and jerk from current "predicted" quantities.
00532 
00533             b->calculate_new_acc_and_jerk_from_new(b, eps*eps,
00534                                                    n_iter - i,     // hack
00535                                                    collision_flag);
00536 
00537             real prev_new_dt = new_dt;
00538             if (s_flag && i < n_iter) {
00539 
00540                 // Iterate to enforce time symmetry.
00541 
00542                 real end_point_dt = the_tfp(b, eta);
00543 
00544                 // Piet's mysterious accelerator:
00545 
00546                 new_dt = 0.5 * (dt + end_point_dt);
00547                 new_dt = dt + 0.5 * (end_point_dt - dt) * (new_dt/prev_new_dt);
00548 
00549                 // See if we must force termination at a particular time.
00550 
00551                 if (x_flag) {
00552                     if (new_dt >= max_dt) {
00553                         end_flag = 1;
00554                         new_dt = max_dt;
00555                     } else
00556                         end_flag = 0;
00557                 }
00558             }
00559 
00560             // Correct all particles for this (sub-)timestep.
00561 
00562             correct(b, new_dt, prev_new_dt);
00563         }
00564 
00565         true_dt = new_dt;
00566 
00567     } else      // Need acc and jerk if we aren't about to quit.
00568 
00569         if (!collision_flag)
00570             b->calculate_new_acc_and_jerk_from_new(b, eps*eps,
00571                                                    0, collision_flag);
00572         
00573     // Update all dynamical quantities:
00574 
00575     for_all_daughters(sdyn3, b, bb) bb->store_new_into_old();
00576     t += true_dt;
00577 
00578     if (collision_flag) end_flag = 1;
00579 
00580     return unpert_flag;
00581 }
00582 
00583 local void initialize(sdyn3* b, // sdyn3 array                   
00584                       real eps) // softening length             
00585 {
00586     predict(b, 0);
00587 
00588     int collision_flag;
00589     b->calculate_new_acc_and_jerk_from_new(b, eps*eps, 1, collision_flag);
00590 
00591     for_all_daughters(sdyn3, b, bb)
00592         bb->store_new_into_old();
00593 }
00594 
00595 // NOTE: If we want to use the_tfp as a variable function pointer,
00596 // we MUST NOT make these timestep functions local!
00597 
00598 real constant_timestep(sdyn3* b, real eta)
00599 {
00600     if (b == NULL) eta = 0; // To keep an HP compiler happy...
00601     return  eta;
00602 }
00603 
00604 real dynamic_timestep(sdyn3* b, real eta)
00605 {
00606     real global_min_encounter_time_sq = VERY_LARGE_NUMBER;
00607     real global_min_free_fall_time_sq = VERY_LARGE_NUMBER;
00608 
00609     for_all_daughters(sdyn3, b, bb) {
00610         global_min_encounter_time_sq =
00611             min(global_min_encounter_time_sq, bb->get_min_encounter_time_sq());
00612         global_min_free_fall_time_sq =
00613             min(global_min_free_fall_time_sq, bb->get_min_free_fall_time_sq());
00614     }
00615 
00616     return eta *
00617         sqrt(min(global_min_encounter_time_sq, global_min_free_fall_time_sq));
00618 }
00619 
00620 local tfp get_timestep_function_ptr(char* timestep_name)
00621 {
00622     if (streq(timestep_name, "constant_timestep"))
00623         return constant_timestep;
00624     else if (streq(timestep_name, "dynamic_timestep"))
00625         return dynamic_timestep;
00626     else {
00627         cerr << "get_timestep_function_ptr: no timestep function implemented"
00628              << " with name `" << timestep_name << "'" << endl;
00629         exit(1);
00630     }
00631     return (tfp)NULL; // To keep an HP g++ happy!
00632 }
00633 
00634 local void start_up(sdyn3* b, real& n_steps)
00635 {
00636     if (b->get_oldest_daughter() !=NULL) {
00637 
00638         if ((n_steps = b->get_n_steps()) == 0)
00639             b->prepare_root();
00640 
00641         for_all_daughters(sdyn3, b, bb)
00642             start_up(bb, n_steps);
00643 
00644     } else
00645         if (n_steps == 0) b->prepare_branch();
00646 }
00647 
00648 local void clean_up(sdyn3 * b, real n)
00649 {
00650     b->set_n_steps(n);
00651 }
00652 
00653 int system_in_cube(sdyn3* b, real cube_size)
00654 {
00655     for_all_daughters(sdyn3, b, bb)
00656         for (int k = 0; k < 3; k++)
00657             if (abs(bb->get_pos()[k]) > cube_size) return 0;
00658     return 1;
00659 }
00660 
00661 #define N_STEP_CHECK 1000          // Interval for checking CPU time
00662 
00663 #define PERT_OUTPUT 2
00664 real last_unpert = -VERY_LARGE_NUMBER;
00665 
00666 void low_n3_evolve(sdyn3* b,       // sdyn3 array
00667                    real delta_t,   // time span of the integration
00668                    real dt_out,    // output time interval
00669                    real dt_snap,   // snapshot output interval
00670                    real snap_cube_size,
00671                    real eps,       // softening length 
00672                    real eta,       // time step parameter
00673                    int  x_flag,    // exact-time termination flag
00674                    char* timestep_name,
00675                    int  s_flag,    // symmetric timestep ?
00676                    int  n_iter,    // number of iterations
00677                    real n_max,     // if > 0: max. number of integration steps
00678                    real cpu_time_check,
00679                    real dt_print,  // external print interval
00680                    sdyn3_print_fp
00681                         print)     // pointer to external print function
00682 {
00683     real t = b->get_time();
00684 
00685     real t_end = t + delta_t;      // final time, at the end of the integration
00686     real t_out = t + dt_out;       // time of next diagnostic output
00687     real t_snap = t + dt_snap;     // time of next snapshot;
00688     real t_print = t + dt_print;   // time of next printout;
00689 
00690     bool unpert;
00691 
00692     real n_steps;
00693     int  count_steps = 0;
00694     real cpu_init = cpu_time();
00695     real cpu_save = cpu_init;
00696 
00697     tfp the_tfp = get_timestep_function_ptr(timestep_name);
00698 
00699     start_up(b, n_steps);
00700     initialize(b, eps);
00701 
00702     int p = cerr.precision(PRECISION);
00703 
00704     real ekin, epot;
00705     calculate_energy(b, ekin, epot);
00706 
00707     if (t_out <= t_end && n_steps == 0) {
00708         cerr << "Time = " << t + (real)b->get_time_offset()
00709              << "  n_steps = " << n_steps
00710              << "  Etot = " << ekin + epot << endl;
00711     }
00712 
00713     if (b->get_n_steps() == 0) {           // should be better interfaced
00714                                            // with start_up: to be done
00715         b->set_e_tot_init(ekin + epot);
00716         b->clear_de_tot_abs_max();
00717     }
00718 
00719     int end_flag = 0;
00720     while (t < t_end && !end_flag) {
00721 
00722         real max_dt = t_end - t;
00723         real dt = the_tfp(b, eta);
00724 
00725         end_flag = 0;
00726         if (dt > max_dt && x_flag) {
00727             end_flag = 1;
00728             dt = max_dt;
00729         }
00730 
00731         unpert = step(b, t, eps, eta, dt, max_dt, end_flag, the_tfp, n_iter,
00732                       x_flag, s_flag);
00733 
00734         // Time may be offst.  System time will be correct.
00735 
00736         b->set_system_time(b->get_system_time()+dt);
00737 
00738         b->set_time(t);                    // should be prettified some time
00739         for_all_daughters(sdyn3, b, bi)
00740             bi->set_time(t);
00741 
00742         n_steps += 1;                      // (Note that n_steps is real)
00743         count_steps++;
00744 
00745         if (unpert) last_unpert = n_steps;
00746 
00747 //      Check for (trivial) output to cerr...
00748 
00749         if (t >= t_out
00750             || (UNPERTURBED_DIAG && n_steps - last_unpert < PERT_OUTPUT)) {
00751             calculate_energy(b, ekin, epot);
00752             int pp = cerr.precision(PRECISION);
00753             cerr << "Time = " << t + (real)b->get_time_offset()
00754                  << "  n_steps = " << n_steps
00755                  << "  dE = " << ekin + epot - b->get_e_tot_init() << endl;
00756             while (t >= t_out) t_out += dt_out;
00757             cerr.precision(pp);
00758         }
00759 
00760 //      ...and (not-so-trivial) output handled elsewhere.
00761 
00762         if (print && t >= t_print) {
00763             (*print)(b);
00764             t_print += dt_print;
00765         }
00766 
00767 //      Output a snapshot to cout at the scheduled time, or at end of run.
00768 
00769         if (n_max > 0 && n_steps >= n_max) end_flag = 1;
00770 
00771         if (end_flag || t >= t_snap) {
00772             if ((t >= t_snap) && system_in_cube(b, snap_cube_size)) {
00773                 put_node(cout, *b);
00774                 cout << flush; 
00775                 t_snap += dt_snap;         // too early to get clean_up info?
00776             }
00777         }
00778 
00779 //      Check the number of steps and the CPU time every N_STEP_CHECK steps.
00780 //      Note that the printed CPU time is the time since this routine was
00781 //      entered.
00782 
00783         if (count_steps >= N_STEP_CHECK) {
00784 
00785             count_steps = 0;
00786 
00787             if (b->get_n_steps() > MAX_N_STEPS) return;
00788 
00789             if (cpu_time() - cpu_save > cpu_time_check) {
00790                 cpu_save = cpu_time();
00791                 calculate_energy(b, ekin, epot);
00792                 int p = cerr.precision(STD_PRECISION);
00793                 cerr << "low_n3_evolve:  CPU time = " << cpu_save - cpu_init;
00794                 cerr.precision(PRECISION);
00795                 cerr << "  time = " << t + (real)b->get_time_offset();
00796                 cerr.precision(STD_PRECISION);
00797                 cerr << "  offset = " << b->get_time_offset() << endl;
00798                 cerr << "                n_steps = " << n_steps
00799                      << "  Etot = " << ekin + epot
00800                      << "  dt = " << dt
00801                      << endl << flush;
00802                 cerr.precision(p);
00803             }
00804         }
00805     }
00806 
00807     cerr.precision(p);
00808     clean_up(b, n_steps);       // too late for snapshot?
00809 }
00810 
00811 #else
00812 
00813 main(int argc, char **argv)
00814 {
00815     sdyn3* b;              // pointer to the nbody system
00816     int  n_iter = 0;       // number of iterations (0: explicit; >=1: implicit)
00817     real n_max = -1;       // if > 0: maximum number of integration steps
00818     
00819     real delta_t = 1;      // time span of the integration
00820     real eta = 0.02;       // time step parameter (for fixed time step,
00821                            //   equal to the time step size; for variable
00822                            //   time step, a multiplication factor)
00823 
00824     real dt_out = VERY_LARGE_NUMBER;
00825                            // output time interval
00826     real dt_snap = VERY_LARGE_NUMBER;
00827                            // snap output interval
00828     real snap_cube_size = 10;
00829 
00830     real cpu_time_check = 3600;
00831 
00832     real eps = 0.05;       // softening length             
00833     char* timestep_name = "constant_timestep";
00834 
00835     check_help();
00836 
00837     extern char *poptarg;
00838     int c;
00839     char* param_string = "A:c:C:d:D:e:f:n:qr:st:xz:";
00840 
00841     bool a_flag = FALSE;
00842     bool d_flag = FALSE;
00843     bool D_flag = FALSE;
00844     bool e_flag = FALSE;
00845     bool f_flag = FALSE;
00846     bool n_flag = FALSE;
00847     bool q_flag = FALSE;
00848     bool r_flag = FALSE;
00849     bool s_flag = FALSE;   // symmetric timestep ?
00850     bool t_flag = FALSE;
00851     bool x_flag = FALSE;   // if true: termination at the exact time of
00852                            //          of the final output, by
00853                            //          adjustment of the last time step;
00854                            // if false: no adjustment of the last time step,
00855                            //           as a consequence the time of final
00856                            //           output might be slightly later than
00857                            //           the time specified.
00858     bool  z_flag = FALSE;  // to specify termination after n_max steps
00859 
00860     while ((c = pgetopt(argc, argv, param_string)) != -1)
00861         switch(c) {
00862             case 'A': a_flag = TRUE;
00863                       eta = atof(poptarg);
00864                       break;
00865             case 'c': cpu_time_check = atof(poptarg);
00866                       break;
00867             case 'C': snap_cube_size = atof(poptarg);
00868                       break;
00869             case 'd': d_flag = TRUE;
00870                       dt_out = atof(poptarg);
00871                       break;
00872             case 'D': D_flag = TRUE;
00873                       dt_snap = atof(poptarg);
00874                       break;
00875             case 'e': e_flag = TRUE;
00876                       eps = atof(poptarg);
00877                       break;
00878             case 'f': f_flag = TRUE;
00879                       timestep_name = poptarg;
00880                       break;
00881             case 'n': n_flag = TRUE;
00882                       n_iter = atoi(poptarg);
00883                       break;
00884             case 'q': q_flag = TRUE;
00885                       break;
00886             case 's': s_flag = TRUE;
00887                       break;
00888             case 't': t_flag = TRUE;
00889                       delta_t = atof(poptarg);
00890                       break;
00891             case 'x': x_flag = TRUE;
00892                       break;
00893             case 'z': z_flag = TRUE;
00894                       n_max = atof(poptarg);
00895                       break;
00896             case '?': params_to_usage(cerr, argv[0], param_string);
00897                       get_help();
00898         }            
00899 
00900     if (!q_flag) {
00901 
00902         // Check input arguments and echo defaults.
00903 
00904         if (!t_flag) cerr << "default delta_t = " << delta_t << "\n";
00905         if (!a_flag) cerr << "default eta = " << eta << "\n";
00906         if (!d_flag) cerr << "default dt_out = " << dt_out << "\n";
00907         if (!e_flag) cerr << "default eps = " << eps << "\n";
00908         if (!f_flag) cerr << "default timestep_name = " << timestep_name
00909                           << "\n";
00910         if (!n_flag) cerr << "default n_iter = " << n_iter << "\n";
00911         if (!s_flag) cerr << "s_flag (symmetric timestep ?) = FALSE" << "\n";
00912         if (!x_flag) cerr << "default termination: not at exact t_end" << "\n";
00913         if (!z_flag) cerr << "default n_max = " << n_max << "\n";
00914     }
00915 
00916     if (!D_flag) dt_snap = delta_t;
00917 
00918     b = get_sdyn3(cin);
00919     b->log_history(argc, argv);
00920     cpu_init();
00921 
00922     low_n3_evolve(b, delta_t, dt_out, dt_snap, eps, eta, snap_cube_size,
00923                   x_flag, timestep_name, s_flag, n_iter, n_max,
00924                   cpu_time_check);
00925 }
00926 
00927 #endif

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