Main Page   Class Hierarchy   Data Structures   File List   Data Fields   Globals  

scale.C

Go to the documentation of this file.
00001  
00036 
00037 // Steve McMillan, April 1993
00038 //
00039 // Significant changes to both command-line options and internal operation
00040 // by SMcM, July 2001 -- added external fields and redefined virial radius.
00041 
00042 // ***  MUST make sure that the definitions of virial radius and virial  ***
00043 // ***  equilibrium are consistent between scale and sys_stats.          ***
00044 
00045 #include "dyn.h"
00046 
00047 #ifndef TOOLBOX
00048 
00049 real get_mass(dyn *b)
00050 {
00051     real mass = 0;
00052     for_all_daughters(dyn, b, bb)
00053         mass += bb->get_mass();
00054     return mass;
00055 }
00056 
00057 void scale_mass(dyn* b, real mscale)
00058 {
00059     for_all_nodes(dyn, b, bb)                   // N.B. all nodes
00060         bb->scale_mass(mscale);
00061 }
00062 
00063 void scale_pos(dyn* b, real rscale,
00064                vector com_pos)                  // default = 0
00065 {
00066     b->set_pos(com_pos + rscale*(b->get_pos()-com_pos));
00067 
00068     for_all_daughters(dyn, b, bb)               // N.B. all daughters
00069         bb->set_pos(com_pos
00070                      + rscale*(bb->get_pos()-com_pos));
00071 }
00072 
00073 void scale_vel(dyn* b, real vscale,
00074                vector com_vel)                  // default = 0
00075 {
00076     b->set_vel(com_vel + vscale*(b->get_vel()-com_vel));
00077 
00078      for_all_daughters (dyn, b, bb)             // N.B. all daughters
00079         bb->set_vel(com_vel
00080                      + vscale*(bb->get_vel()-com_vel));
00081 }
00082 
00083 real get_top_level_kinetic_energy(dyn *b)       // top-level nodes only
00084 {
00085     real kinetic_energy = 0;
00086 
00087     for_all_daughters(dyn, b, bb)
00088         kinetic_energy += bb->get_mass() * square(bb->get_vel());
00089 
00090     kinetic_energy *= 0.5;
00091     return kinetic_energy;
00092 }
00093 
00094 real get_kinetic_energy(dyn *b)                 // all nodes
00095 {
00096     real kinetic_energy = 0;
00097 
00098     for_all_leaves(dyn, b, bb)
00099         kinetic_energy += bb->get_mass()
00100             * square(something_relative_to_root(bb, &dyn::get_vel));
00101 
00102     kinetic_energy *= 0.5;
00103     return kinetic_energy;
00104 }
00105 
00106 // NOTE: get_top_level_energies does *not* resolve binaries, and
00107 // does not include any external fields.
00108 
00109 void get_top_level_energies(dyn *b, real eps2,
00110                             real& potential_energy,
00111                             real& kinetic_energy)
00112 {
00113     real total_energy;
00114     calculate_energies(b, eps2,
00115                        potential_energy, kinetic_energy, total_energy,
00116                        true);           // ==> CM approximation
00117 }
00118 
00119 void scale_virial(dyn *b, real q,
00120                   real potential_energy,                // internal
00121                   real& kinetic_energy,                 // internal
00122                   vector com_vel)                       // default = 0
00123 {
00124     // Set the virial ratio by scaling the velocities.
00125     // Also rescale the kinetic energy.
00126 
00127     if (q > 0) q = -q;          // only need specify |Q|
00128 
00129     real vscale = sqrt(q*potential_energy/kinetic_energy);
00130     scale_vel(b, vscale, com_vel);
00131     kinetic_energy = q*potential_energy;
00132 }
00133 
00134 real scale_energy(dyn * b,
00135                   real e, real& energy,                 // internal
00136                   vector com_pos,                       // default = 0
00137                   vector com_vel)                       // default = 0
00138 {
00139     // Set the energy by scaling positions and velocities, keeping
00140     // the virial ratio fixed.  Note that eps = 0 is implicit.
00141 
00142     if (energy >= 0) return 1;
00143     if (e > 0) e = -e;  // only need specify |E| and only E < 0 makes sense!
00144 
00145     real xscale = energy/e;
00146     real vscale = sqrt(1./xscale);
00147 
00148     scale_pos(b, xscale, com_pos);
00149     scale_vel(b, vscale, com_vel);
00150 
00151     energy = e;
00152     return xscale;
00153 }
00154 
00155 void scale(dyn *b, real eps,
00156            bool c_flag,
00157            bool e_flag, real e,
00158            bool m_flag, real m,
00159            bool q_flag, real q,
00160            bool r_flag, real r,
00161            bool debug,                               // default = false
00162            void (*top_level_energies)(dyn*, real,    // default =
00163                                       real&, real&)) // get_top_level_energies()
00164 {
00165     // Another consistency check:
00166 
00167     if (q_flag && find_qmatch(b->get_log_story(), "alpha3_over_alpha1")) {
00168         warning("scale: can't reset virial ratio for aniso_king model");
00169         q_flag = false;
00170     }
00171 
00172     // Check for physical parameters.
00173 
00174     real phys_mass, phys_length, phys_time;
00175     bool phys = get_physical_scales(b, phys_mass, phys_length, phys_time);
00176 
00177     // If phys, we are using physical units.  Phys_mass, length, and time
00178     // are the equivalents of 1 N-body unit, in Msun, pc, and Myr.
00179 
00180     if (phys) {
00181 
00182         // Flag a non-standard choice of units if physical units have
00183         // been specified (see note in --help text).
00184 
00185 
00186         if ((m_flag && !twiddles(m, 1))
00187              || (r_flag && !twiddles(r, 1))
00188              || (q_flag && !twiddles(q, 0.5))
00189              || (e_flag && !twiddles(abs(e), 0.25)))
00190             cerr << "scale:  non-standard choice of units for system"
00191                  << " with physical data" << endl;
00192     }
00193 
00194     // Optionally transform to the center of mass frame.
00195 
00196     if (c_flag) {
00197         cerr << "scale:  transforming to com frame" << endl;
00198         b->to_com();
00199     }
00200 
00201 
00202     // See if we are dealing with test particles only (still can set
00203     // mass and radius, but velocity scaling will ignore the internal
00204     // potential).
00205 
00206     check_set_ignore_internal(b);
00207 
00208 
00209     // Define various relevant quantities.  Trust the data in the input
00210     // snapshot, if current.
00211 
00212     // Always need to know the mass.
00213 
00214     real mass = 0;
00215 
00216     if (b->get_system_time() == 0
00217         && find_qmatch(b->get_log_story(), "initial_mass"))
00218         mass = getrq(b->get_log_story(), "initial_mass");
00219     else
00220         mass = get_mass(b);
00221 
00222     if (debug) {
00223         cerr << "debug: "; PRC(mass); PRL(get_mass(b));
00224     }
00225 
00226     // Need to know some energies if the E, Q, or R flags are set.
00227     // Note that the definition of r_virial now includes *only* the
00228     // internal potential energy.
00229 
00230     real r_virial = 0, pot_int = 0, pot_ext = 0, kin = 0;
00231 
00232     if (e_flag || r_flag || q_flag) {
00233         if (b->get_system_time() == 0
00234             && find_qmatch(b->get_log_story(), "initial_rvirial")) {
00235 
00236             // Avoid N^2 calculation if possible.
00237 
00238             r_virial = getrq(b->get_log_story(), "initial_rvirial");
00239             pot_int = -0.5*mass*mass/r_virial;
00240             kin = get_top_level_kinetic_energy(b);
00241 
00242             if (debug) {
00243 
00244                 // Basic checks:
00245 
00246                 real pe_int, ke_tot;
00247                 top_level_energies(b, eps*eps, pe_int, ke_tot);
00248                 cerr << "debug: "; PRC(pot_int); PRC(pe_int);
00249                 cerr << "debug: "; PRC(kin); PRL(ke_tot);
00250             }
00251 
00252         } else {
00253 
00254             // Compute the potential energy.
00255 
00256             top_level_energies(b, eps*eps, pot_int, kin);
00257             r_virial = -0.5*mass*mass/pot_int;
00258         }
00259 
00260         // Get external potential parameters (including any tidal field)
00261         // from the input data and compute the external potential energy
00262         // (excluding any tidal field).
00263 
00264         check_set_external(b);
00265         pot_ext = get_external_pot(b);
00266 
00267         if (debug) {
00268             cerr << "debug: "; PRL(pot_ext);
00269         }
00270 
00271         // Variable kira_initial_jacobi_radius is set by check_set_external,
00272         // but it will be rescaled, so just remove it.
00273 
00274         rmq(b->get_log_story(), "kira_initial_jacobi_radius");
00275 
00276         // If an external (non-tidal) field exists, we probably won't
00277         // naturally want to specify the energy.  In addition, the
00278         // procedure for doing this is complicated (iterative, and may
00279         // not converge).  For now, at least, only allow e to be set
00280         // in the case of no external fields.
00281 
00282         if (e_flag && pot_ext != 0)
00283             err_exit("Can't set energy in case of external field.");
00284     }
00285 
00286     // The relevant kinetic energy for scaling should be in the center
00287     // of mass frame.  Compute the CM velocity here and correct.
00288 
00289     vector com_pos, com_vel;
00290     compute_com(b, com_pos, com_vel);
00291 
00292     real com_kin = 0.5*mass*square(com_vel);
00293 
00294     if (debug) {
00295         cerr << "debug: "; PRC(com_vel); PRL(com_kin);
00296     }
00297 
00298     // First do the mass scaling, if any.  NOTE: Scaling the total mass
00299     // changes both the total energy and the virial ratio.  No attempt is
00300     // made to preserve either in cases where they are not specified on
00301     // the command line.
00302 
00303     if (m_flag) {
00304         real mfac = m/mass;
00305         // PRL(mfac);
00306 
00307         scale_mass(b, mfac);
00308 
00309         mass = m;
00310         pot_int *= mfac*mfac;
00311         pot_ext *= mfac;
00312         kin *= mfac;
00313         com_kin *= mfac;
00314 
00315         cerr << "scale:  "; PRL(mass);
00316         if (debug) {
00317             cerr << "debug: "; PRL(com_kin);
00318         }
00319     }
00320 
00321     // Simplest choice now is r_flag; e_flag is more complicated,
00322     // particularly in the presence of an external field.
00323 
00324     if (r_flag) {
00325 
00326         // Rescale all positions to force the virial radius to the
00327         // specified value.
00328 
00329         real oldvir = pot_int + get_external_virial(b);   // denominator of
00330                                                           // virial ratio
00331         if (debug) {
00332             cerr << "debug: "; PRL((kin-com_kin)/oldvir);
00333         }
00334 
00335         real rfac =  r/r_virial;
00336         if (debug) {
00337             cerr << "debug: "; PRL(rfac);
00338         }
00339         scale_pos(b, rfac, com_pos);
00340 
00341         r_virial = r;
00342         pot_int /= rfac;
00343 
00344         pot_ext = get_external_pot(b);
00345         if (debug) {
00346             cerr << "debug: "; PRL(pot_ext);
00347         }
00348 
00349         cerr << "scale:  "; PRL(r_virial);
00350 
00351         // Rescale all internal velocities to preserve the virial
00352         // ratio.  Scaling is already OK in case of a tidal field,
00353         // so get_external_virial() includes only non-tidal terms.
00354 
00355         if (debug) {
00356             cerr << "debug: "; PRC(pot_int); PRL(get_external_virial(b));
00357         }
00358         real vfac = (pot_int + get_external_virial(b))/oldvir;
00359         if (vfac <= 0)
00360             cerr << "scale: unable to preserve virial ratio" << endl;
00361         else {
00362             vfac = sqrt(vfac);
00363             if (debug) {
00364                 cerr << "debug: "; PRL(vfac);
00365             }
00366             scale_vel(b, vfac, com_vel);
00367             kin = com_kin + vfac*vfac*(kin - com_kin);
00368         }
00369 
00370         if (debug) {
00371 
00372             // Check:
00373 
00374             real pe_int, ke;
00375             top_level_energies(b, eps*eps, pe_int, ke);
00376             real p_ext = get_external_pot(b);
00377             real vir = get_external_virial(b);
00378 
00379             cerr << "debug: "; PRC(pot_int); PRL(pe_int);
00380             cerr << "debug: "; PRC(kin); PRL(ke);
00381             cerr << "debug: "; PRC(p_ext); PRL((ke-com_kin)/(pe_int+vir));
00382             cerr << "debug: "; PRL(-0.5*mass*mass/pe_int);
00383         }
00384     }
00385 
00386     if (e_flag) {
00387 
00388         // Attempt to set the energy while holding the virial ratio
00389         // constant, or else set both e and q.  Assume no external
00390         // fields (see above).  Procedure is OK as is in the presence
00391         // of a tidal field.
00392 
00393         if (q_flag) {
00394             kin -= com_kin;
00395             scale_virial(b, q, pot_int, kin, com_vel);  // scales kin
00396             kin += com_kin;
00397 
00398             // NOTE: Changing the virial ratio changes the total kinetic
00399             // energy of the system, but preserves the potential energy
00400             // and total mass.
00401 
00402             q_flag = false;
00403 
00404             cerr << "scale:  "; PRL(q);
00405         }
00406 
00407         // NOTE: The energy scaling is done on the assumption that eps = 0,
00408         //       and preserves the value of the virial ratio in that case.
00409         //       The total mass is always left unchanged.
00410 
00411         real ee = kin - com_kin + pot_int;              // internal energy
00412         real fac = scale_energy(b, e, ee, com_pos, com_vel);
00413         ee += com_kin;                                  // total_energy
00414 
00415         // Update all relevant quantities.
00416 
00417         kin = com_kin + (kin - com_kin)/fac;
00418         pot_int /= fac;
00419         r_virial *= fac;
00420 
00421         // For Roche-lobe filling systems, this also rescales
00422         // alpha1 and alpha3 by fac^{-3}.
00423 
00424         cerr << "scale:  "; PRL(e);
00425     }
00426 
00427     if (q_flag) {
00428 
00429         // Scale the velocities to set q and preserve r_virial.
00430 
00431         real vir = get_external_virial(b);
00432         PRL(vir);
00433 
00434         real denominator = vir;
00435         if (!b->get_ignore_internal()) denominator += pot_int;
00436 
00437         real qvir = -(kin - com_kin) / denominator;
00438         real vfac = sqrt(q/qvir);
00439         PRL(vfac);
00440 
00441         scale_vel(b, vfac, com_vel);
00442         kin = com_kin + vfac*vfac*(kin - com_kin);
00443 
00444         cerr << "scale:  "; PRL(q);
00445         if (debug) {
00446             cerr << "debug: "; PRL((kin-com_kin)/denominator);
00447         }
00448     }
00449 
00450     // Update the root log story -- probably best to do this only if
00451     // system_time = 0.
00452 
00453     if (b->get_system_time() == 0) {
00454         putrq(b->get_log_story(), "initial_mass", mass, HIGH_PRECISION);
00455         putrq(b->get_log_story(), "initial_rvirial", r_virial);
00456         putrq(b->get_dyn_story(), "initial_total_energy", kin+pot_int+pot_ext);
00457     }
00458 }
00459 
00460 bool parse_scale_main(int argc, char *argv[],
00461                       real& eps, bool& c_flag,
00462                       bool& e_flag, real& e,
00463                       bool& m_flag, real& m,
00464                       bool& q_flag, real& q,
00465                       bool& r_flag, real& r,
00466                       bool& debug)
00467 {
00468     // Defaults:
00469 
00470     debug = false;
00471     c_flag = false;
00472     e_flag = false;
00473     m_flag = false;
00474     q_flag = false;
00475     r_flag = false;
00476     bool eps_flag = false;
00477     bool s_flag = false;
00478 
00479     m = 0, q = -1, e = 0, r = 0;
00480     eps = 0;
00481 
00482     extern char *poptarg;
00483     int c;
00484     char* param_string = "cdm:M:q:Q:e:E:r:R:sS";
00485 
00486     while ((c = pgetopt(argc, argv, param_string)) != -1)
00487         switch(c) {
00488 
00489             case 'c': c_flag = true;
00490                       break;
00491             case 'd': debug = true;
00492                       break;
00493             case 'E': e_flag = true;
00494                       r_flag = false;
00495                       e = atof(poptarg);
00496                       break;
00497             case 'e': eps_flag = true;
00498                       eps = atof(poptarg);
00499                       break;
00500             case 'M':
00501             case 'm': m_flag = true;
00502                       m = atof(poptarg);
00503                       break;
00504             case 'Q':
00505             case 'q': q_flag = true;
00506                       q = atof(poptarg);
00507                       break;
00508             case 'R':
00509             case 'r': r_flag = true;
00510                       e_flag = false;
00511                       r = atof(poptarg);
00512                       break;
00513             case 'S':
00514             case 's': s_flag = true;
00515                       m_flag = true;
00516                       r_flag = true;
00517                       q_flag = true;
00518                       m = 1;
00519                       q = 0.5;
00520                       r = 1;
00521                       break;
00522             case '?': params_to_usage(cerr, argv[0], param_string);
00523                       return false;
00524         }
00525 
00526     if (e_flag && e >= 0) warning("scale: specified energy >= 0");
00527     if (m_flag && m <= 0) warning("scale: specified mass <= 0");
00528     if (q_flag && q <  0) warning("scale: specified virial ratio < 0");
00529     if (r_flag && r <= 0) warning("scale: specified virial radius <= 0");
00530 
00531     return true;
00532 }
00533 
00534 #else
00535 
00536 main(int argc, char ** argv)
00537 {
00538     real m = 0, q = -1, e = 0, r = 0;
00539     real eps = 0;
00540 
00541     bool debug = false;
00542     bool c_flag = false;
00543     bool e_flag = false;
00544     bool m_flag = false;
00545     bool q_flag = false;
00546     bool r_flag = false;
00547 
00548     if (!parse_scale_main(argc, argv, eps,
00549                           c_flag,
00550                           e_flag, e, m_flag, m,
00551                           q_flag, q, r_flag, r,
00552                           debug)) {
00553         get_help();
00554         exit(1);
00555     }
00556 
00557     dyn *b = get_dyn(cin);
00558     b->log_history(argc, argv);
00559 
00560     scale(b, eps, c_flag, e_flag, e, m_flag, m, q_flag, q, r_flag, r, debug);
00561 
00562     put_node(cout, *b);
00563 }
00564 
00565 #endif

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