Main Page   Class Hierarchy   Data Structures   File List   Data Fields   Globals  

double_star.C

Go to the documentation of this file.
00001 //
00002 //
00003 // double_star.C
00004 //
00005 
00006 #include "double_star.h"
00007 
00008 #define REPORT_BINARY_EVOLUTION    false
00009 #define REPORT_RECURSIVE_EVOLUTION false
00010 #define REPORT_FUNCTION_NAMES      false
00011 #define REPORT_TRANFER_STABILITY   false
00012 
00013 // (SPZ+GN: 28 Jul 2000) Obsolete
00014 //#define MAXIMUM_BINARY_UPDATE_TIMESTEP cnsts.star_to_dyn(binary_update_time_fraction) 
00015 // GIJS: If you want to increase the timestep for population synthesis,
00016 //       please use the the parameter: MAXIMUM_BINARY_UPDATE_TIMESTEP
00017 //       For example by defining:
00018 //       #define MAXIMUM_BINARY_UPDATE_TIMESTEP 0.9
00019 //       Which should be identical to your previous results.
00020 
00021 double_star * new_double_star(node* n, real sma, real ecc, 
00022                               real start_time, int id,  // Defaults specified
00023                               binary_type type)         // in double_star.h
00024     {
00025      double_star* element = new double_star(n);
00026      element->initialize(type, sma, ecc, start_time, id);
00027 
00028      element->set_donor_timescale(element->get_primary(), true);
00029      
00030      element->determine_minimal_timestep();
00031 
00032      element->evolve_element(start_time);
00033 
00034      element->post_constructor();
00035 
00036      return element;
00037 }
00038 
00039 double_star::double_star(node* n) : star(n) {
00040 
00041            semi=eccentricity=binary_age=minimal_timestep=velocity=0;
00042            donor_timescale=0;
00043            identity = donor_identity=0;
00044            donor_type=NAS;
00045            first_contact=FALSE;
00046 }
00047 
00048 void double_star::initialize(binary_type type, real sma, 
00049                         real ecc, real age, int id) {
00050    
00051       semi         = sma;
00052       eccentricity = ecc;
00053       bin_type     = type;
00054       binary_age   = age;
00055       identity     = id;
00056 
00057       initial.semi         = semi;
00058       initial.q            = mass_ratio();
00059       initial.mass_prim    = get_primary()->get_total_mass();
00060       initial.eccentricity = eccentricity;
00061 
00062       get_primary()->set_identity(0);
00063       get_secondary()->set_identity(1);
00064       
00065       refresh_memory();       //implemented 25-1-95
00066 }
00067 
00068 real double_star::mass_ratio()           {
00069      real mp = get_primary()->get_total_mass();
00070      real ms = get_secondary()->get_total_mass();
00071 
00072      real q = ms/mp;
00073      if (q>1) q = mp/ms;
00074 
00075      return q;
00076   }
00077 
00078 real double_star::get_evolve_timestep() {
00079 
00080   real delta_t = cnsts.safety(maximum_timestep); //donor_timescale;
00081 
00082   if (bin_type != Merged) {
00083     if (get_primary()->get_evolve_timestep() <
00084         get_secondary()->get_evolve_timestep())
00085       return min(delta_t, get_primary()->get_evolve_timestep());
00086     else
00087       return min(delta_t, get_secondary()->get_evolve_timestep());
00088   }
00089   else {
00090     return min(delta_t, get_primary()->get_evolve_timestep());
00091   }
00092 
00093 }
00094 
00095 // Note Common-envelope and spiral-in are not detected.
00096 binary_type double_star::obtain_binary_type() {
00097 
00098   binary_type type_of_binary = Unknown_Binary_Type;
00099   
00100   real rl_p = roche_radius(get_primary());
00101   real rl_s = roche_radius(get_secondary());
00102 
00103   real rp = get_primary()->get_effective_radius();
00104   real rs = get_secondary()->get_effective_radius();
00105 
00106   //  cerr << "obtain_binary_type: " << identity
00107   //       << " rp:" <<log10(rl_p/rp)
00108   //       << " rs:" << log10(rl_s/rs) 
00109   //       << " a/r* = " << semi/max(rp, rs);
00110   // Just for memory and output routines.
00111   // Primary fills Roche-lobe.
00112   if (semi <= 0 && eccentricity <= 0)
00113     type_of_binary = Merged;
00114   else if (eccentricity >= 1)
00115     type_of_binary = Disrupted;
00116   else if (rp >= rl_p || rs > rl_s)
00117     type_of_binary = Semi_Detached;
00118   else if (rp >= rl_p && rs >= rl_s)
00119     type_of_binary = Contact;
00120   else if (semi <= rp * cnsts.parameters(tidal_circularization_radius) ||
00121            semi <= rs * cnsts.parameters(tidal_circularization_radius)) 
00122     type_of_binary = Synchronized;
00123   else
00124     type_of_binary = Detached;
00125 
00126   //  cerr << " type= " << type_string(type_of_binary) << endl;
00127   
00128   return type_of_binary;
00129 }
00130 
00131 real double_star::roche_radius(star * str) {
00132   // Eggleton PP., ApJ, 1983, 268, 368.
00133 
00134   // (SPZ+GN: 28 Jul 2000)
00135   // semi-major axis van merged and disrupted is now zero (for convenience)
00136   // So, we require this safety...
00137   if (bin_type == Merged || bin_type == Disrupted)
00138     return VERY_LARGE_NUMBER;
00139 
00140   real mr = str->get_total_mass()
00141     / str->get_companion()->get_total_mass();
00142   real q1_3 = pow(mr, cnsts.mathematics(one_third));
00143   real q2_3 = pow(q1_3, 2);                  //pow(mr, TWO_THIRD);
00144   
00145   return semi*0.49*q2_3/(0.6*q2_3 + log(1 + q1_3));
00146 }
00147 
00148 real double_star::roche_radius(const real a, const real m1, const real m2) {
00149 
00150   real q = m1/m2;
00151   real q1_3 = pow(q, cnsts.mathematics(one_third));
00152   real q2_3 = pow(q1_3, 2);   //pow(mr, TWO_THIRD);
00153   
00154   return a*0.49*q2_3/(0.6*q2_3 + log(1 + q1_3));
00155        }
00156 
00157 void double_star::put_element() {
00158 //              should be made starlab compatible
00159 
00160 
00161      ofstream outfile("binev.data", ios::app|ios::out);
00162      if(!outfile) cerr << "error: couldn't create file binev.data"<<endl;
00163 
00164           outfile << identity << "\t" << binary_age << endl;
00165           outfile << identity << endl;
00166           get_primary()->put_element();
00167           get_secondary()->put_element();
00168 
00169      initial.put_element();
00170      outfile << bin_type << " " << eccentricity << " "
00171              << get_period() << " " << semi << " " << velocity << "\n" << endl;
00172 
00173      outfile.close();
00174         }
00175 
00176 void double_star::put_hrd(ostream & s) {
00177 
00178         s << "2\n  ";
00179         get_primary()->put_hrd(s);
00180         s << "  "; 
00181         get_secondary()->put_hrd(s);
00182    }
00183 
00184 //      Pretty print.
00185 void double_star::print_status() {
00186         cout << "status at time = " << binary_age << endl;
00187 
00188         if (get_primary()->no_of_elements()>1) 
00189            get_primary()->print_status();
00190         if (get_secondary()->no_of_elements()>1)   
00191            get_secondary()->print_status();
00192 
00193         cout << type_string(bin_type);
00194         switch (bin_type) {
00195            case Merged: 
00196               cout << " {" << type_string(get_primary()->get_element_type())
00197                    << "}" << endl;
00198               cout << "M = "<<get_primary()->get_total_mass()
00199                    << "\t R = "<<get_primary()->get_effective_radius()
00200                    << "\t V = "<<get_primary()->get_velocity() << endl;
00201               break;
00202            case Disrupted:
00203               cout << " )" << type_string(get_primary()->get_element_type())
00204                    << ", " << type_string(get_secondary()->get_element_type())
00205                    << "(" << endl;
00206               cout << "M = "<<get_primary()->get_total_mass()
00207                    << "\t R = "<<get_primary()->get_effective_radius()
00208                    << "\t V = "<<get_primary()->get_velocity() << endl;
00209               cout << "m = "<<get_secondary()->get_total_mass()
00210                    << "\t r = "<<get_secondary()->get_effective_radius()
00211                    << "\t v = "<<get_secondary()->get_velocity() << endl;
00212               break;
00213            default:
00214               if (get_primary()->get_spec_type(Rl_filling))
00215                  cout << " [";
00216               else
00217                  cout << " (";
00218               cout << type_string(get_primary()->get_element_type())
00219                    << ", " << type_string(get_secondary()->get_element_type());
00220               if (get_secondary()->get_spec_type(Rl_filling))
00221                  cout << "]" << endl;
00222               else
00223                  cout << ")" << endl;
00224               cout << "M = "<<get_primary()->get_total_mass()
00225                    << "\t R = "<<get_primary()->get_effective_radius() << endl;
00226               cout << "m = "<<get_secondary()->get_total_mass()
00227                    << "\t r = "<<get_secondary()->get_effective_radius() << endl;
00228               cout << "a = "<<semi<<"\t e = "<<eccentricity
00229                    << "\t v = "<<get_velocity() << endl;
00230            }
00231      }
00232 
00233 void double_star::put_state() {
00234   
00235   star *a, *b;
00236   if (get_use_hdyn()) {
00237    a = get_primary();
00238    b = get_secondary();
00239   }
00240   else {
00241     a = get_initial_primary();
00242     b = get_initial_secondary();
00243   }
00244 
00245         switch (bin_type) {
00246            case Merged:
00247               cerr << " {"; 
00248               get_primary()->put_state();
00249               cerr << "}" << endl;
00250               break;
00251            case Disrupted:
00252               cerr << " )";
00253               a->put_state();
00254               cerr << ", "; 
00255               b->put_state();
00256               cerr << "(" << endl;
00257               break;
00258            default:
00259               if (a->get_spec_type(Rl_filling))
00260                  cerr << " [";
00261               else
00262                  cerr << " (";
00263               a->put_state();
00264               cerr << ", ";
00265               b->put_state();
00266               if (b->get_spec_type(Rl_filling))
00267                 cerr << "]";
00268               else
00269                 cerr << ")";
00270 
00271              real pday = get_period();
00272               if (pday<0.04) 
00273                 cerr << "   Porb = " << pday*1440 << " minutes";
00274               else if (pday<1) 
00275                 cerr << "   Porb = " << pday*24 << " hours";
00276               else if (pday<365.25) 
00277                 cerr << "   Porb = " << pday << " days";
00278               else
00279                 cerr << "   Porb = " << pday/365.25 << " years";              
00280            }
00281      }
00282 
00283 void double_star::print_roche() {
00284 //      Gives compatible output for EPJ Roche program.
00285   
00286      if (bin_type!=Merged) {
00287        get_initial_primary()->print_roche();
00288        get_initial_secondary()->print_roche();
00289      }
00290      else get_primary()->print_roche();
00291 
00292      cerr<<get_period();
00293    }
00294 
00295 void double_star::dump(ostream & s, bool brief) {
00296 //      Zero IQ dump program.
00297 
00298   if (brief) {
00299     star* stp = get_initial_primary();
00300     star* sts = get_initial_secondary();
00301 
00302     real m1 = stp->get_total_mass();
00303     real m2 = sts->get_total_mass();
00304 
00305     real primary_roche_lobe, secondary_roche_lobe;
00306     if (bin_type==Merged || bin_type==Disrupted) {
00307         primary_roche_lobe   = 2 * stp->get_effective_radius();
00308         secondary_roche_lobe = 2 * sts->get_effective_radius();
00309     }
00310     else {
00311         primary_roche_lobe   = roche_radius(semi, m1, m2);
00312         secondary_roche_lobe = roche_radius(semi, m2, m1);
00313     }
00314     
00315     s << identity << " "
00316       << binary_age << " "
00317       << semi << " "
00318       << eccentricity << "    "
00319 
00320       << stp->get_node()->format_label() << " "   
00321       << stp->get_element_type() << " "
00322       << m1 << " "
00323       << primary_roche_lobe - stp->get_effective_radius() << "    "
00324 
00325       << sts->get_node()->format_label() << " "   
00326       << sts->get_element_type() << " "
00327       << m2 << " "
00328       << secondary_roche_lobe - sts->get_effective_radius() 
00329       << endl;
00330 
00331       //get_initial_primary()->dump(s, brief);
00332       //      s << "    ";
00333       //    get_initial_secondary()->dump(s, brief);
00334       //    s << endl;
00335     
00336   }
00337   else {
00338       if(bin_type!=Merged) {
00339 
00340       int n_elements = no_of_elements();
00341 
00342       s << n_elements
00343         << "\n " << identity
00344         << " " << binary_age
00345         << " " << bin_type
00346         << " " << eccentricity
00347         << " " << get_period()
00348         << " " << semi
00349         << " " << velocity
00350         << endl;
00351       initial.dump(s);
00352 
00353       s << "  ";
00354       get_primary()->dump(s, brief);
00355       s << "  ";
00356       get_secondary()->dump(s, brief);
00357       }
00358       else get_primary()->dump(s, brief);
00359   }
00360 }
00361 
00362 void double_star::dump(char * filename, bool brief) {
00363 
00364 
00365   ofstream s(filename, ios::app|ios::out);
00366   if (!s) cerr << "error: couldn't create file " << filename <<endl;
00367 
00368   if (brief) {
00369     star* stp = get_initial_primary();
00370     star* sts = get_initial_secondary();
00371 
00372     real m1 = stp->get_total_mass();
00373     real m2 = sts->get_total_mass();
00374     real primary_roche_lobe, secondary_roche_lobe;
00375 
00376     if (bin_type==Merged || bin_type==Disrupted) {
00377         primary_roche_lobe   = 2 * stp->get_effective_radius();
00378         secondary_roche_lobe = 2 * sts->get_effective_radius();
00379     }
00380     else {
00381         primary_roche_lobe   = roche_radius(semi, m1, m2);
00382         secondary_roche_lobe = roche_radius(semi, m2, m1);
00383     }
00384 
00385     s << identity << " "
00386       << binary_age << " "
00387       << semi << " "
00388       << eccentricity << "    "
00389 
00390       << stp->get_node()->format_label() << " "   
00391       << stp->get_element_type() << " "
00392       << m1 << " "
00393       << primary_roche_lobe - stp->get_effective_radius() << "    "
00394 
00395       << sts->get_node()->format_label() << " "   
00396       << sts->get_element_type() << " "
00397       << m2 << " "
00398       << secondary_roche_lobe - sts->get_effective_radius()
00399       << endl;
00400       
00401   }
00402   else {    
00403       int n_elements = no_of_elements();
00404 
00405       s << n_elements
00406         << "\n " << identity
00407         << " " << binary_age
00408         << " " << bin_type
00409         << " " << eccentricity
00410         << " " << get_period()
00411         << " " << semi
00412         << " " << velocity
00413         << endl;
00414       initial.dump(s);
00415 
00416       s << "  ";
00417       get_primary()->dump(s, brief);
00418       s << "  ";
00419       get_secondary()->dump(s, brief);
00420    }
00421   
00422   s.close();
00423 }
00424 
00425 void double_star::adjust_binary_after_wind_loss(star * donor,
00426                                                 const real md_dot,
00427                                                 const real dt) {
00428 // Binary separation is adjusterd upon mass loss by
00429 // stellar wind of one of the companions. 
00430 // Fraction of the mass lost by the donor is dumped on its
00431 // companion.
00432 // Actually this routine is only applicable to
00433 // non-dynamically evolving systems.
00434 // In a N-body system the dynamics should take care of the
00435 // stellar wind mass loss.
00436 
00437 
00438 //              check if object is still bound.
00439      if (bin_type!=Merged && bin_type!=Disrupted) {
00440         star* accretor = donor->get_companion();
00441 
00442         real M_old = get_total_mass();
00443         real old_donor_mass = donor->get_total_mass();
00444         real old_accretor_mass = accretor->get_total_mass();
00445 
00446         real ma_dot = accretor->accrete_from_stellar_wind(md_dot, dt);
00447         donor = donor->reduce_mass(md_dot);
00448 
00449         real M_new = get_total_mass();
00450         real new_donor_mass = donor->get_total_mass();
00451         real new_accretor_mass = accretor->get_total_mass();
00452 
00453         if (md_dot>0 && ma_dot>=0) {
00454           // real alpha = 1 - ma_dot/md_dot;
00455           // semi *= pow(pow(new_donor_mass/old_donor_mass, 1-alpha)
00456           //      *  new_accretor_mass/old_accretor_mass, -2)
00457           //      *  M_old/M_new;
00458           // Changed alpha to eta (SPZ+GN:09/1998)
00459            real eta =  ma_dot/md_dot;
00460            semi *= pow(pow(new_donor_mass/old_donor_mass, eta)
00461                 *  new_accretor_mass/old_accretor_mass, -2)
00462                 *  M_old/M_new;
00463         }
00464      }
00465      else {
00466        donor = donor->reduce_mass(md_dot);
00467        donor->get_companion()->set_spec_type(Accreting, false);
00468      }
00469 
00470 //cerr<<" a-->"<<semi<<endl;
00471 }
00472 
00473 real double_star::angular_momentum() {
00474 //      Determine the angular momentum of the binary.
00475 
00476 //      Shore, S.N., Livio, M., Heuvel, EPJ, 1992, in Interacting Binaries
00477 //      (edt. N Nussbaumer and A. Orr) Spirnger verlag p.\ 389.
00478         real omega = TWO_PI/(get_period()*cnsts.physics(days));
00479         real mp = get_primary()->get_total_mass()*cnsts.parameters(solar_mass);
00480         real ms = get_secondary()->get_total_mass()*cnsts.parameters(solar_mass);
00481         real a =  semi*cnsts.parameters(solar_radius);
00482 
00483         return omega*mp*ms*a*a*(1-eccentricity*eccentricity)/(mp+ms);
00484      }
00485 
00486 void double_star::circularize() {
00487 //      Tidal circularization with 
00488 //      conservation of angular momentum.
00489 //      Binary is not nesecerely circularized but 
00490 //      periostron adapted to current TIDAL_RANGE.
00491 //      This method is more realistic than instantanious 
00492 //      curcularization.
00493 
00494      real pericenter = semi*(1-eccentricity);
00495 
00496      if((bin_type != Merged && bin_type != Disrupted)          &&
00497         (pericenter<= cnsts.parameters(tidal_circularization_radius)
00498                     * get_primary()->get_effective_radius()         ||
00499          pericenter<= cnsts.parameters(tidal_circularization_radius)
00500                     * get_secondary()->get_effective_radius())      &&
00501         (eccentricity>0 && eccentricity<1.)) /*safety*/         {
00502             real peri_new = cnsts.parameters(tidal_circularization_radius)
00503                           * max(get_primary()->get_effective_radius(),
00504                                        get_secondary()->get_effective_radius());
00505             real circ_semi = semi*(1-eccentricity*eccentricity);
00506             real new_ecc = circ_semi/peri_new - 1;
00507 //cerr<<"old: "<<" "<<semi<<" "<<eccentricity<<" "<<pericenter<<endl;
00508 //cerr<<"new: "<<circ_semi<<" "<<new_ecc<<" "<<peri_new<<endl;
00509             if(new_ecc<=0) {
00510               semi=circ_semi;
00511               eccentricity = 0;
00512             }
00513             else {
00514               semi = circ_semi/(1 - new_ecc*new_ecc);
00515               eccentricity = new_ecc;
00516             }
00517 //cerr<<"final: "<<semi<<" "<<eccentricity<<endl;
00518             //semi *= (1-eccentricity*eccentricity);
00519             //eccentricity = 0;
00520             real rotation_period = cnsts.physics(days)*get_period();
00521             get_primary()->set_rotation_period(rotation_period);
00522             get_secondary()->set_rotation_period(rotation_period);
00523         }
00524 //force_circularization();      //      For model E.
00525 }
00526 
00527 //      Makes binaries immediately circular.
00528 void double_star::force_circularization() {
00529 
00530 
00531      if((bin_type != Merged && bin_type != Disrupted) &&
00532         (eccentricity>0 && eccentricity<1.)) /*safety*/          {
00533             semi *= (1-eccentricity*eccentricity);
00534             eccentricity = 0;
00535             real rotation_period = cnsts.physics(days)*get_period();
00536             get_primary()->set_rotation_period(rotation_period);
00537             get_secondary()->set_rotation_period(rotation_period);
00538         }
00539 }
00540 
00541 //      Minimum timestep determination is needed for
00542 //      preventing infinit integration upon dynamical timescale
00543 //      mass-transfer.
00544 void double_star::determine_minimal_timestep() {
00545 
00546      minimal_timestep = cnsts.safety(timestep_factor)*donor_timescale;
00547 
00548      if(minimal_timestep<cnsts.safety(minimum_timestep)) {
00549         cerr << " minimal_timestep < safety minimum ";
00550         cerr << " in double_star::determine_minimal_timestep: ";
00551         cerr << minimal_timestep << endl;
00552         minimal_timestep = cnsts.safety(minimum_timestep);
00553      }
00554 }
00555 
00556 // (SPZ+GN: 28 Jul 2000)
00557 // Limits timestep with safety which may be different for 
00558 // dynamics or pop. synth.
00559 real double_star::internal_time_step(const real evolve_timestep) {
00560 
00561   real dt;
00562   if (get_use_hdyn()) {
00563 
00564     dt = cnsts.star_to_dyn(binary_update_time_fraction) * evolve_timestep;
00565   } 
00566   else {
00567 
00568     dt = cnsts.safety(maximum_binary_update_time_fraction) 
00569        * evolve_timestep;
00570   }
00571 
00572   return dt;
00573 }
00574 
00575 real double_star::determine_dt(const real ageint, const real time_done) {
00576 //      Determined the evolution timestep.
00577 //      This is the hard of the program.
00578 //      DO NOT CHANGE THIS ROUTINE!
00579 
00580      real dtime = ageint - time_done;
00581 
00582      // (SPZ+GN: 28 Jul 2000)
00583      // Introduced local internal_time_step(...)
00584      real dtime_ev = internal_time_step(get_primary()->get_evolve_timestep());
00585 
00586      if (bin_type != Merged) {
00587        dtime_ev = min(dtime_ev, internal_time_step(
00588                                 get_secondary()->get_evolve_timestep()));
00589 
00590        dtime = min(dtime, dtime_ev);
00591 
00592        if(bin_type != Disrupted) {
00593 
00594          real dt_orbit = orbital_timescale();
00595 
00596          dtime = min(dtime, dt_orbit);
00597          
00598 //       if(dtime>minimal_timestep && bin_type==Semi_Detached) 
00599 //         dtime = minimal_timestep;
00600        }
00601      }
00602      else 
00603        dtime = min(dtime, dtime_ev);
00604 
00605      if(dtime>cnsts.safety(maximum_timestep))
00606        dtime = cnsts.safety(maximum_timestep);
00607      if(dtime<cnsts.safety(minimum_timestep)) 
00608        dtime = cnsts.safety(minimum_timestep);
00609 
00610      return dtime;
00611 }
00612 
00613 // First time a dstar is initialized nucleair_evolution_timescale
00614 // is set. (SPZ+GN:24 Sep 1998)
00615 void double_star::set_donor_timescale(star* donor,
00616                                       bool first) { // default = false
00617   
00618 //cerr << "set donor timescale"<<endl;
00619 
00620   if (first) {
00621 //    donor_timescale = donor->nucleair_evolution_timescale();
00622 //    current_mass_transfer_type = Nuclear;
00623     // (SPZ+GN: 28 Jul 2000)
00624     // First step may be shaky and it is not clear what is best
00625     // Therefore, to be conservative, we use
00626     // the thermal timescale of the ACCRETOR!
00627     // So, Mdot is computed for conservative mass trasnfer, but
00628     // on the shortest possible timestep.
00629     donor_timescale = donor->get_companion()->kelvin_helmholds_timescale();
00630     current_mass_transfer_type = Unknown;
00631   }
00632   else
00633     donor_timescale =
00634       donor->mass_transfer_timescale(current_mass_transfer_type);
00635   
00636   donor_identity = donor->get_identity();
00637   donor_type = donor->get_element_type();
00638 
00639 //cerr << "donor timescale set"<<endl;
00640   
00641 }
00642 
00643 #if 0
00644 // Not used any more due to replacement of safeties.
00645 void double_star::binary_in_contact(const real dt) {
00646 //      Search which star is actually filling its Roche lobe.
00647 
00648      real rl_p = roche_radius(get_primary());
00649      real rl_s = roche_radius(get_secondary());
00650 
00651      if (get_primary()->get_effective_radius() >= rl_p) {
00652         if (get_secondary()->get_effective_radius() < rl_s) {
00653            if (!ready_for_mass_transfer(get_primary())) return;
00654            semi_detached(get_primary(), get_secondary(), dt);
00655         }
00656         else
00657           contact_binary(dt);   // used to be called: common_envelope(dt);
00658      }
00659      else if (get_secondary()->get_effective_radius() >= rl_s) {
00660         if (!ready_for_mass_transfer(get_secondary())) return;
00661         semi_detached(get_secondary(), get_primary(), dt);
00662      }
00663 
00664 //     cerr<<"leave: binary_in_contact"<<endl;
00665 }
00666 
00667 bool double_star::ready_for_mass_transfer(star* donor) {
00668 //      Make sure that the star that fills its Roche lobe is the same as
00669 //      the one that provided its mass-transfer timescale.
00670 
00671      real old_dt_min = minimal_timestep;
00672      bool mass_transfer_allowed = TRUE;
00673 
00674      mass_transfer_type type = Unknown;
00675      
00676      if (donor->get_identity()!=donor_identity) {
00677         donor_identity    = donor->get_identity();
00678         donor_type        = donor->get_element_type();
00679         donor_timescale   = donor->mass_transfer_timescale(type);
00680 
00681         determine_minimal_timestep();
00682 
00683         if(old_dt_min<=minimal_timestep) 
00684            mass_transfer_allowed = TRUE;
00685         else
00686            mass_transfer_allowed = FALSE;
00687      }
00688 
00689      return mass_transfer_allowed;
00690 }
00691 
00692 #endif
00693        
00694 
00695 void double_star::semi_detached(star* donor,
00696                                 star* accretor,
00697                                 real dt) {
00698 //      Binary is semi-detached.
00699 //      Donor should lose mass while the accretor should accrete.
00700 //    cerr << "semi_detached" <<endl;
00701 
00702   if (!stable(donor)) {
00703     cerr << "semi_deatched not stable => ::common_envelope" << endl; 
00704     
00705     common_envelope();
00706   }
00707   else if (get_current_mass_transfer_type()==Dynamic) {
00708     cerr << "dynamic mass transfer => ::dynamic_mass_transfer" << endl; 
00709     
00710     common_envelope();
00711 // in common_envelope dynamic_mass_trasfer is called
00712 //    dynamic_mass_transfer(donor,accretor);
00713   }
00714   else {
00715 
00716     if (REPORT_TRANFER_STABILITY) {
00717       cerr << "stable mass transfer => ::perform_mass_transfer" << endl; 
00718     }
00719 
00720     perform_mass_transfer(dt, donor, accretor);
00721   }
00722 
00723   //get_seba_counters()->semi_detached++;
00724 
00725 }
00726 
00727 // Is not used but provides interesting options.
00728 void double_star::contact(star* donor,
00729                           star* accretor,
00730                           real dt) {
00731 //      Both stars in contact.
00732 //      W Uma (stable) system or spiral-in may occur.
00733 
00734 //    cerr<<"double_star::contact()"<<endl;
00735 //      Mass loss according to AM accretor.
00736      real M_old = get_total_mass();
00737      real old_donor_mass = donor->get_total_mass();
00738      real old_accretor_mass = accretor->get_total_mass();
00739      //real q_old = old_accretor_mass/old_donor_mass;
00740 
00741 //      Old mechanism
00742 //     real md_dot = 0;
00743 //     md_dot = donor->subtrac_mass_from_donor(dt, md_dot);
00744 
00745 //      New mechanism
00746      real md_dot=0;
00747      donor = donor->subtrac_mass_from_donor(dt, md_dot);
00748 /*
00749  *    try { 
00750  *       donor->subtrac_mass_from_donor(dt, md_dot);
00751  *      }
00752  *    catch (star * element) {
00753  *       donor = element;
00754  *    }
00755  */
00756      if (md_dot>0) {
00757         real ma_dot = accretor->add_mass_to_accretor(md_dot, dt);
00758 
00759         real M_new = get_total_mass();
00760         real new_donor_mass = donor->get_total_mass();
00761         real new_accretor_mass = accretor->get_total_mass();
00762         //real q_new = new_accretor_mass/new_donor_mass;
00763 
00764         real a_fr;
00765         if (!accretor->remnant()) {
00766           //real beta = 3;
00767            a_fr  = pow(old_donor_mass*old_accretor_mass
00768                  / (new_donor_mass*new_accretor_mass), 2);
00769            semi *= pow(M_new/M_old,
00770                        2*cnsts.parameters(specific_angular_momentum_loss) + 1)*a_fr;
00771         }
00772         else {
00773            real alpha = 1 - ma_dot/md_dot;
00774            if (alpha<1) {
00775               a_fr  = (new_donor_mass/old_donor_mass)
00776                     * pow(new_accretor_mass/old_accretor_mass, 1/(1-alpha));
00777               semi *= (M_old/M_new)/pow(a_fr, 2);
00778            }
00779            else {
00780               a_fr  = exp(2*(new_donor_mass-old_donor_mass)/new_accretor_mass);
00781               semi *= (M_old/M_new)*a_fr/pow(new_donor_mass/old_donor_mass, 2);
00782            }
00783         }
00784 
00785       // redundant due to implementation of new force donor timescale
00786       //                                         (SPZ+GN:23 Sep 1998)
00787 #if 0   
00788        real t_donor = donor->mass_transfer_timescale();
00789        if (((q_old<=1 && q_new>=1) ||
00790           (t_donor<0.1*donor_timescale || t_donor>10*donor_timescale)) &&
00791           (bin_type!=Disrupted && bin_type!=Merged)) {
00792           set_donor_timescale(t_donor,
00793                               donor->get_element_type(),
00794                               donor->get_identity());
00795           determine_minimal_timestep();
00796        }
00797 #endif
00798        
00799      }
00800 
00801      //get_seba_counters()->contact++;
00802 
00803      //     if (bin_type!=Merged && bin_type!=Disrupted)
00804      //        update_binary_parameters();
00805 }
00806 
00807 bool  double_star::stable(star* st) {   // default = NULL
00808 //      Stability test for mass transfer.
00809 //      Single stellar angular momentum should be smaller than
00810 //      1/3-th the binary angular momentum.
00811 
00812     // do not allow mass transfer between planets
00813 //    if(st->get_total_mass() < 2*cnsts.safety(minimum_mass_step)) {
00814 //      return false;
00815 //    }
00816 
00817   real J_star;
00818   if(st) {
00819     J_star = st->angular_momentum();
00820   }
00821   else {
00822     real J_prim = get_primary()->angular_momentum();
00823     real J_sec  = get_secondary()->angular_momentum();
00824 
00825     J_star = max(J_prim, J_sec);
00826 
00827   }
00828   
00829   real J_bin  = angular_momentum();
00830   
00831   if(REPORT_TRANFER_STABILITY) {
00832     PRC(J_star);PRL(J_bin);
00833   }
00834 
00835   if (J_star >= J_bin *
00836       cnsts.parameters(Darwin_Riemann_instability_factor)) {
00837 
00838     cerr << "Spiral-in: Darwin Riemann instability"<<endl;
00839     
00840     return FALSE;
00841   }
00842  
00843   return TRUE;
00844 }
00845 
00846 #if 0
00847 // is not used any more (SPZ+GN:28 Sep 1998)
00848 void double_star::update_binary_parameters() {
00849 //      Update the evolved binary.
00850 
00851      real primary_mass = get_primary()->get_total_mass();
00852      real secondary_mass = get_secondary()->get_total_mass();
00853 
00854      get_primary()->set_previous_radius(get_primary()->get_radius());
00855      get_secondary()->set_previous_radius(get_secondary()->get_radius());
00856 //cerr<<"leave: double_star::update_binary_parameters()"<<endl;
00857 }
00858 #endif
00859 
00860 void double_star::perform_mass_transfer(const real dt,
00861                                         star * donor,
00862                                         star * accretor) {
00863 //      Adjust binary parameter according to the model
00864 //      Take account of mass lost and transfered from the donor to the
00865 //      accretor.
00866 
00867 
00868   if (REPORT_TRANFER_STABILITY) {
00869     cerr<<"enter perform_mass_transfer("<<dt<<", "
00870         <<donor->get_element_type()<<", "<<accretor->get_element_type()
00871         <<") semi= "<< semi<<endl;
00872   }
00873 
00874 //      Mass loss according to AM accretor.
00875      real M_old = get_total_mass();
00876      real old_donor_mass = donor->get_total_mass();
00877      real old_accretor_mass = accretor->get_total_mass();
00878      //real q_old = old_accretor_mass/old_donor_mass;
00879 
00880      real md_dot=0;
00881      donor = donor->subtrac_mass_from_donor(dt, md_dot);
00882 
00883 
00884      if (md_dot>0) {
00885         real ma_dot = accretor->add_mass_to_accretor(md_dot, dt);
00886 
00887         real M_new = get_total_mass();
00888         real new_donor_mass = donor->get_total_mass();
00889         real new_accretor_mass = accretor->get_total_mass();
00890         //real q_new = new_accretor_mass/new_donor_mass;
00891 
00892         real a_fr;
00893         if (!accretor->remnant()) {
00894 
00895           // General case: semi-conservative mass transfer.
00896           
00897            a_fr  = pow(old_donor_mass*old_accretor_mass
00898                  / (new_donor_mass*new_accretor_mass), 2);
00899            semi *= pow(M_new/M_old,
00900                        2*cnsts.parameters(specific_angular_momentum_loss)
00901                        + 1)*a_fr;
00902         }
00903         else {
00904 
00905           // Special case: mass transfer to compact object as accretor.
00906           //               Two possibilities:
00907           //               1) eta>0: mass lost as wind from accretor.
00908           //               2) eta==0: exponential spiral-in.
00909           
00910            real eta = ma_dot/md_dot; 
00911            if (eta>0) {
00912              a_fr  = (new_donor_mass/old_donor_mass)
00913                    * pow(new_accretor_mass/old_accretor_mass, 1/eta);
00914              semi *= (M_old/M_new)/pow(a_fr, 2); 
00915            }
00916            else {
00917              a_fr  = exp(2*(new_donor_mass-old_donor_mass)
00918                          /new_accretor_mass); 
00919              semi *= (M_old/M_new)*a_fr
00920                    / pow(new_donor_mass/old_donor_mass, 2);
00921            }
00922            
00923            //cerr<<"new_semi: "<< a_fr<<" " <<semi<<endl;
00924            //   Let outer component accrete from mass lost of inner binary.
00925            //   Routine block for triple evolution.
00926            //   Tricky but fun!
00927            //   if (is_binary_component()) {
00928            if (is_star_in_binary()) {
00929              real mdot_triple = M_old - M_new;
00930              if (mdot_triple>0) {
00931                get_binary()->adjust_triple_after_wind_loss(this,
00932                                                            mdot_triple, dt);
00933              }
00934              else if (is_binary_component()) {
00935                if(mdot_triple<0) {
00936                  cerr << "enter perform_mass_transfer(" << dt << ", "
00937                       << donor->get_element_type()<<", "
00938                       <<accretor->get_element_type()
00939                       <<")"<<endl;
00940                  cerr<<"Mass lost during non-conservative mass transfer is negative."
00941                      <<"mlost ="<<mdot_triple<<endl;
00942                }
00943              }
00944              else {
00945                cerr<<"Presumed binary component is not a binary root, "
00946                    << "nor a hierarchical root"<<endl;
00947              }
00948            }
00949         }
00950      }
00951 
00952 #if 0
00953   switch(current_mass_transfer_type) {
00954       case Nuclear:           get_seba_counters()->nuclear++;
00955            break;     
00956       case AML_driven:        get_seba_counters()->aml_driven++;
00957            break;     
00958       case Thermal:           get_seba_counters()->thermal++;
00959            break;     
00960       case Dynamic:           get_seba_counters()->dynamic++;
00961            break;     
00962   }
00963 #endif
00964 }
00965 
00966 // final part of ::perform_mass_transfer()... 
00967 #if 0
00968         // Since donor timescale is set within recursive with
00969         // the appropriate donor this part is redundant.
00970         // (SPZ+GN:23 Sep 1998)
00971 //      Adjust mass-transfer timescale if needed.
00972         real t_donor = donor->mass_transfer_timescale();
00973         if (((q_old<=1 && q_new>=1) ||
00974             (t_donor<0.1*donor_timescale || t_donor>10*donor_timescale)) && 
00975            (bin_type!=Disrupted && bin_type!=Merged)) {
00976               set_donor_timescale(t_donor, 
00977                            donor->get_element_type(), 
00978                            donor->get_identity());
00979               determine_minimal_timestep();
00980         }
00981 #endif
00982         
00983 
00984 void double_star::post_sn_companion_velocity(star* companion,
00985                                              const real mdot) {
00986 //              Result of impact on companion velocity!
00987 //              Temorary removed!
00988         return ;
00989 
00990 //              Statement never reached.
00991 //              Removed 16_01_94!
00992 //cerr<<"void double_star::post_sn_companion_velocity()"<<endl;
00993 //cerr<<"pre sn velocity: "<< companion->get_velocity();
00994      real new_total_binary_mass = get_total_mass()-mdot;
00995      real vel = abs((1 - 2*new_total_binary_mass/get_total_mass())
00996                            *pow(get_total_mass()
00997                            /companion->get_total_mass(), 2));
00998      //if(vel<=-1) vel = -1;
00999      vel = companion->get_velocity()*sqrt(1 + vel);
01000      companion->set_velocity(vel);
01001 //cerr<<"post sn velocity: "<< companion->get_velocity()<<endl;
01002 }
01003 
01004 void double_star::instantaneous_element() {
01005 
01006   try_zero_timestep();
01007 }
01008 
01009 
01010 // evolve_element 
01011 // evolves a binary to the new time which is close to end_time 
01012 // NOTE: the new time of the binary is generally != end_time
01013 //       but slightly smaller.
01014 //       The BINARY_UPDATE_TIMESTEP must therefore be small.
01015 void double_star::evolve_element(const real end_time) 
01016 {
01017   //cerr<<"Evolve the binary"<<endl;
01018   //    real current_time = binary_age
01019   //                      + BINARY_UPDATE_TIMESTEP*get_evolve_timestep();
01020   //  real current_time = binary_age
01021   //                    + get_evolve_timestep();
01022     
01023     // Add try_zero_timestep for the case that current_time==end_time.
01024     // Might be completely wrong, anyway, numerical precision prevents
01025     // such a constructiona anyway.
01026     // Implemented by (SPZ:2/1998)
01027     //if (current_time<=end_time)
01028 
01029     real current_time = binary_age;
01030     if (binary_age<end_time)
01031         do {
01032           if (REPORT_BINARY_EVOLUTION) {
01033             cerr << "converge binary to system age at t = "
01034                  << current_time << "  binary age = " << binary_age
01035                  << "  time step is " << get_evolve_timestep() << endl;
01036           }
01037 
01038 //        current_time = min(end_time, 
01039 //                           current_time+BINARY_UPDATE_TIMESTEP *
01040 //                           get_evolve_timestep();
01041 //        current_time += BINARY_UPDATE_TIMESTEP * get_evolve_timestep();
01042 
01043           // Attempt a small timestep for consistency reasons.
01044           // but this timestep may be a bit too small.
01045           // current_time += cnsts.safety(binary_update_time_fraction) 
01046           //               * get_evolve_timestep();
01047 
01048           // A bigger timestep may be allowed, but the choise of the 
01049           // timestep must be consistemt, 
01050           // in steps of binary_update_time_fraction
01051           current_time += internal_time_step(get_evolve_timestep());
01052             
01053           // safety: Never evolve passed end_time.
01054           if (current_time>=end_time)
01055              break;
01056               
01057           evolve_the_binary(current_time);
01058 
01059         } while (binary_age<end_time);
01060     else if (end_time == 0)
01061       try_zero_timestep();
01062     else // (SPZ: 21 Mar 2001): added the circularizaton in case....
01063      circularize();
01064 }
01065 
01066 void double_star::try_zero_timestep() {
01067 
01068   if (bin_type!=Merged && bin_type!=Disrupted) {
01069 
01070      star *p = get_primary();
01071      star *s = get_secondary();
01072      if (p) 
01073          p->evolve_element(binary_age);
01074 
01075      if(s) {
01076          s->evolve_element(binary_age);
01077      }
01078 
01079      circularize();
01080 
01081     real rl_p = roche_radius(get_primary());
01082     real rl_s = roche_radius(get_secondary());
01083 
01084     real rp = get_primary()->get_effective_radius();
01085     real rs = get_secondary()->get_effective_radius();
01086 
01087     // Just for memory and output routines.
01088     // Primary fills Roche-lobe.
01089        if (rp >= rl_p) {
01090          //get_primary()->set_effective_radius(rl_p);
01091           get_primary()->set_spec_type(Rl_filling);
01092           get_primary()->set_spec_type(Accreting, false);
01093           get_secondary()->set_spec_type(Accreting);
01094        }
01095        else
01096          get_primary()->set_spec_type(Rl_filling, false);
01097 
01098        if (rs >= rl_s) {
01099 
01100          //get_secondary()->set_effective_radius(rl_s);
01101          get_secondary()->set_spec_type(Rl_filling);
01102          get_secondary()->set_spec_type(Accreting, false);
01103          get_primary()->set_spec_type(Accreting);
01104 
01105           // One could check here for contact binary to cause
01106           // the secondary to be the donor.
01107        }
01108        else
01109          get_secondary()->set_spec_type(Rl_filling, false);
01110   }
01111 
01112 }
01113 
01114 void double_star::evolve_the_binary(const real end_time) {
01115 
01116 //      Evolve both stars synchonously.
01117 
01118      real dt, old_binary_age;
01119      real time_done = 0;
01120      real ageint = end_time - binary_age;
01121 
01122      do {
01123        //cerr<<"evolve_the_binary loop "; PRC(end_time); PRL(ageint);
01124        
01125         determine_minimal_timestep();
01126         dt = determine_dt(ageint, time_done);
01127 
01128 //      PRC(ageint);PRC(time_done);PRL(dt);
01129         
01130         recursive_counter = 0;             
01131         old_binary_age = binary_age;
01132         
01133         get_primary()->set_previous_radius(get_primary()
01134                      ->get_effective_radius());
01135         get_secondary()->set_previous_radius(get_secondary()
01136                        ->get_effective_radius());
01137 
01138         // Stellar wind mass loss is taken care of by the stars themselves.
01139         // line removed at 20/08/97 by SPZ
01140         // perform_wind_loss(dt);
01141 
01142         refresh_memory();
01143 
01144         recursive_binary_evolution(dt, binary_age+dt);
01145         calculate_velocities();
01146         time_done += binary_age-old_binary_age; 
01147      }
01148      while(time_done<ageint);
01149 }
01150 
01151 void double_star::recursive_binary_evolution(real dt,
01152                                 const real end_time) {
01153 //      Evolve two stars synchronously.
01154 //      If one star start filling its Roche-lobe
01155 //      return one time-step and search for the moment
01156 //      of Roche lobe contact.
01157 //      Then start to transfer mass.
01158 
01159     recursive_counter++;
01160     if (REPORT_RECURSIVE_EVOLUTION) {
01161       cerr << "recursive_binary_evolution(dt " << dt 
01162            << ", end_time " << end_time << "): " <<bin_type<< endl;
01163       cerr<<dt<<" "<<binary_age<<" "<<end_time<< " "<<recursive_counter<<endl;
01164       cerr << "recursive # " << recursive_counter << endl;
01165       dump(cerr, false);
01166     }
01167       
01168     if(recursive_counter>=cnsts.safety(maximum_recursive_calls)) {
01169         cerr << "WARNING: ";
01170         cerr << "recursive_binary_evolution(dt " << dt 
01171             << ", end_time " << end_time << "): " <<bin_type<< endl;
01172         cerr<<dt<<" "<<binary_age<<" "<<end_time<<endl;
01173         cerr << "recursive_counter == " << recursive_counter << endl;
01174         dump(cerr);
01175 
01176         //get_seba_counters()->recursive_overflow++;
01177 
01178         return;
01179     }
01180 
01181     if (binary_age>=end_time) return;
01182     
01183     dt = min(dt, determine_dt(end_time, binary_age));
01184     binary_age += dt;
01185 
01186      // Angular momentum loss must be computed here: binary can merge!
01187      // However must be before evolve_element of stars, since the first step
01188      // of the recursive binev reaches to far, causing the a giant to become
01189      // huge: -> merger due to magnetic braking!
01190      angular_momentum_loss(dt);
01191        
01192      star *p = get_primary();
01193      star *s = get_secondary();
01194      if (p) 
01195        p->evolve_element(binary_age);
01196 
01197      if (bin_type!=Merged && s) 
01198           s->evolve_element(binary_age);
01199 
01200      p = s = NULL;
01201 
01202      if (bin_type!=Merged && bin_type!=Disrupted) {
01203 
01204        circularize();
01205 
01206        real rl_p = roche_radius(get_primary());
01207        real rl_s = roche_radius(get_secondary());
01208 
01209 //       real rp = get_primary()->get_effective_radius();
01210 //       real rs = get_secondary()->get_effective_radius();
01211        real rp = get_primary()->get_radius();
01212        real rs = get_secondary()->get_radius();
01213 
01214        star* donor    = get_primary();
01215        star* accretor = get_secondary();
01216 
01217        // Primary fills Roche-lobe?
01218        if (rp >= rl_p) {
01219           get_primary()->set_spec_type(Rl_filling);
01220           get_primary()->set_spec_type(Accreting, false);
01221           get_secondary()->set_spec_type(Accreting);
01222        }
01223        else
01224          get_primary()->set_spec_type(Rl_filling, false);
01225 
01226        if (rs >= rl_s) {
01227 
01228          donor  = get_secondary();
01229          accretor = get_primary();
01230 
01231          get_secondary()->set_spec_type(Rl_filling);
01232          get_secondary()->set_spec_type(Accreting, false);
01233          get_primary()->set_spec_type(Accreting);
01234 
01235          // One could check here for contact binary to cause
01236          // the secondary to be the donor.
01237        }
01238        else
01239          get_secondary()->set_spec_type(Rl_filling, false);
01240 
01241          
01242        // Donor timescale is determined after donor is determined
01243        // which happens further down (SPZ+GN:22 Sep 1998)
01244        //force_donor_timescale(donor);
01245        //determine_minimal_timestep();
01246 
01247 //              Determines if we really have to do with a mass
01248 //              transferring binary.
01249        if (rp >= rl_p    ||
01250            rs >= rl_s) {
01251          if (REPORT_RECURSIVE_EVOLUTION)
01252            cerr << " (donor, accretor) = (" << donor->get_identity()
01253                 << ", " << accretor->get_identity()
01254                 << ")";
01255          
01256          // set the mass transfer timescale to the
01257          // timescale of the newly determined donor
01258          // implemented (SPZ+GN:23 Sep 1998)
01259          set_donor_timescale(donor);
01260          //      cerr <<"dts"<<endl;
01261          determine_minimal_timestep();
01262          //      cerr <<"dt min"<<endl;
01263 
01264          // a bit more carefull before announcing contact (SPZ:  2 Jun 1999)
01265           if (rp >= rl_p &&       // removed since (SPZ+GN:19 Nov 1998)
01266               rs >= rl_s){       //  || bin_type==Contact) {
01267             // Proper implementation if ri = effective_radius()
01268             //         current_mass_transfer_type == Dynamic) || 
01269             //        (get_primary()->get_radius()>= rl_s &&
01270             //         get_secondary()->get_radius()>= rl_s)) {
01271 
01272 
01273             if (!first_contact) {
01274 
01275               if (REPORT_RECURSIVE_EVOLUTION) 
01276                 cerr << "\tFirst contact" << endl;
01277 
01278               first_contact=true;
01279 
01280               // Swithced off for now (SPZ June 2001)
01281               //donor->first_roche_lobe_contact_story(
01282                 //     accretor->get_element_type());
01283               //accretor->first_roche_lobe_contact_story(
01284                //         donor->get_element_type());
01285               cerr << "First Roche-lobe contact for: ";
01286               put_state();
01287               cerr << endl;
01288               dump("SeBa.data", true);
01289             }
01290             if (REPORT_RECURSIVE_EVOLUTION) {
01291               cerr << "\tContact" << endl;
01292               put_state();
01293               cerr << endl;
01294               dump(cerr, false);
01295             }
01296 
01297             bin_type = Contact;
01298             contact_binary(dt);   // used to be called: common_envelope(dt);
01299             refresh_memory();
01300             if (binary_age>=end_time)
01301               return;
01302             else {
01303               refresh_memory();
01304               real max_dt = end_time - binary_age;
01305               //              dt = min(end_time - binary_age,
01306               //                       determine_dt(end_time, binary_age));
01307               recursive_binary_evolution(max_dt, end_time);
01308               return;
01309             }
01310           }
01311           else if(dt<=minimal_timestep) {
01312             if (REPORT_RECURSIVE_EVOLUTION) {
01313               cerr << "\tMass transfer" << endl;
01314               put_state();
01315               cerr << endl;
01316               dump(cerr, false);
01317             }
01318             
01319             bin_type = Semi_Detached;
01320             
01321             if (!first_contact) {
01322               if (REPORT_RECURSIVE_EVOLUTION)
01323                 cerr << "\tFirst contact" << endl;
01324 
01325                first_contact=true;
01326                donor->first_roche_lobe_contact_story(
01327                       accretor->get_element_type());
01328                cerr << "First Roche-lobe contact for: ";
01329                put_state();
01330                cerr << endl;
01331                dump("SeBa.data", true);
01332                // print_roche();
01333 
01334               //get_seba_counters()->first_rlof++;
01335 
01336              }
01337             
01338 //          dump(cerr, false);
01339             semi_detached(donor, accretor, dt);
01340             //was binary_in_contact(dt) with redundant safeties.
01341 //          dump(cerr, false);
01342               
01343               refresh_memory();
01344               if (binary_age>=end_time) return;
01345 
01346               dt = minimal_timestep;
01347               // minimal_timestep can be too large for just formed helium_star!
01348               //real max_dt = determine_dt(end_time, binary_age);
01349               // Removed (SPZ+GN:24 Sep 1998)
01350               //dt = min(minimal_timestep, max_dt);
01351               //------dt = cnsts.safety(minimum_timestep);  ------
01352               //--------------------------------------------------
01353               if (binary_age + dt > end_time ||
01354                   (bin_type==Merged || bin_type == Disrupted)) 
01355                  dt = end_time - binary_age;
01356 
01357               recursive_binary_evolution(dt, end_time);
01358               return;
01359            }
01360            else {
01361              if (REPORT_RECURSIVE_EVOLUTION) {
01362                 cerr << "\tTotal recall" << endl;
01363                 put_state();
01364                 cerr << endl;
01365                 dump(cerr, false);
01366              }
01367              
01368               bin_type = Semi_Detached;
01369               recall_memory();
01370               dt *= 0.5;
01371               recursive_binary_evolution(dt, end_time);
01372               return;
01373            }
01374         }
01375         else if(bin_type == Semi_Detached || bin_type == Contact) {
01376 
01377            bin_type = Detached;
01378            get_primary()->set_spec_type(Rl_filling, false);
01379            get_secondary()->set_spec_type(Rl_filling, false);
01380 
01381            if (REPORT_RECURSIVE_EVOLUTION) {
01382              cerr << "\tRestep" << endl;
01383              put_state();
01384              cerr << endl;
01385              dump(cerr, false);
01386            }
01387 
01388            //Removed (22/09/1998)
01389            //force_donor_timescale(donor);
01390            //determine_minimal_timestep();
01391            //(SPZ+GN:24 Sep 1998)
01392            // dt = min(dt, determine_dt(end_time, binary_age));
01393            if (dt>minimal_timestep) {
01394              //if (dt>cnsts.safety(minimum_timestep)) {
01395              refresh_memory();
01396            }
01397            // Half timestep increases converging to Roche-lobe contact.
01398            // but can cause dt = end_time-binary_age.
01399            // dt *= 0.5
01400 
01401            //get_seba_counters()->detached++;
01402            
01403            recursive_binary_evolution(dt, end_time);
01404            return;
01405         }
01406         else {
01407           if (REPORT_RECURSIVE_EVOLUTION) {
01408             cerr << "\tDetached" << endl;
01409              put_state();
01410              cerr << endl;
01411              dump(cerr, false);
01412           }
01413 
01414           if(bin_type != Detached) {
01415             cerr<< "ERROR in ::recursive bin_type != Detached "<<endl;
01416             cerr<< "where should be Detached"<<endl;
01417             dump(cerr, false);
01418             bin_type = Detached;
01419           }
01420         
01421           // (GN+SPZ May  3 1999) effective_radius 
01422           get_primary()->
01423             set_effective_radius(get_primary()->get_radius());
01424           get_secondary()->
01425             set_effective_radius(get_secondary()->get_radius());
01426 
01427           refresh_memory();
01428 
01429           // Gijs Returns
01430 //        cerr << "Gijs Returns but continue...." << endl;
01431 //        dump(cerr, false);
01432 
01433           real max_dt = end_time - binary_age;
01434           if (max_dt < end_time - binary_age) {
01435             cerr << "Time step reduction in double_star::recursive..."
01436                  << endl;
01437             cerr << "     max_dt (" << max_dt
01438                  << ") < end_time - binary_age ("
01439                  << end_time - binary_age << ")." << endl;
01440           }
01441               
01442            recursive_binary_evolution(max_dt, end_time);
01443            return;
01444         }
01445      }
01446      else {
01447        if (REPORT_RECURSIVE_EVOLUTION) {
01448          cerr << "\tTerminated" << endl;
01449          put_state();
01450          cerr << endl;
01451          dump(cerr, false);
01452        }
01453        get_primary()->set_spec_type(Rl_filling, false);
01454        get_secondary()->set_spec_type(Rl_filling, false);
01455        // (SPZ+GN: 28 Jul 2000) was a 'Gedoogde bug' but now ok.
01456        // merged object should not accrete.
01457        get_primary()->set_spec_type(Accreting, false);
01458        get_secondary()->set_spec_type(Accreting, false);
01459   
01460        // Merged and disrupted binaries.
01461 
01462        refresh_memory();
01463        real max_dt = end_time - binary_age;
01464        recursive_binary_evolution(max_dt, end_time);
01465      }
01466 }
01467 
01468 
01469 // redundant due to implementation of new force donor timescale
01470 //                                         (SPZ+GN:23 Sep 1998)
01471 #if 0
01472 void double_star::set_donor_timescale(star * donor) {
01473 
01474 //cerr <<"void double_star::set_donor_timescale(donor="
01475 //     << donor->get_element_type()<<")"<<endl;
01476 
01477   
01478      if (bin_type!=Merged && bin_type!=Disrupted) {
01479        int id          = donor->get_identity();
01480        stellar_type st = donor->get_element_type();
01481        mass_transfer_type type = Unknown;
01482        real t          = donor->mass_transfer_timescale(type);
01483        current_mass_transfer_type = type;
01484         
01485          if (st!=NAS) {
01486            if (donor_identity!=id) {
01487               donor_identity = id;
01488               donor_type = st;
01489               donor_timescale = t;
01490            }
01491            else if (donor_timescale<=0 || donor_type!=st) {
01492              donor_identity = id;
01493              donor_type = st;
01494              donor_timescale = t;
01495            }
01496          }
01497      }
01498 }
01499 
01500 
01501 void double_star::set_donor_timescale(const real t, const stellar_type st,
01502                                       const int id) {
01503 
01504      if (bin_type!=Merged && bin_type!=Disrupted) {
01505         if (st!=NAS) {
01506            if (donor_identity==id) {
01507               donor_type = st;
01508               donor_timescale = t;
01509            }
01510            else if(donor_timescale<=0) {
01511               donor_identity = id;
01512               donor_type = st;
01513               donor_timescale = t;
01514            }
01515         }
01516      }
01517 
01518 }
01519 #endif
01520 
01521 // Determine in which way common_envelope systems spiral-in
01522 void double_star::common_envelope() {
01523 
01524   //cerr << "Common_envelope"<<endl;
01525 
01526      if (bin_type!=Merged && bin_type!=Disrupted) {
01527        
01528        if (!stable()) {
01529          cerr << "Unstable"<<endl;
01530 
01531          if (get_primary()->giant_star()) {
01532            if (get_secondary()->giant_star()) 
01533               double_spiral_in();
01534            else
01535               spiral_in(get_primary(), get_secondary());
01536          }
01537          else if (get_secondary()->giant_star())
01538            spiral_in(get_secondary(), get_primary());
01539          else {
01540            cerr << "Merger double_star::common_envelope" << endl;
01541            dump(cerr, false);
01542            
01543            merge_elements(get_primary(), get_secondary());
01544          }
01545        }
01546        else {
01547            if (get_primary()->giant_star()) {
01548              if (get_secondary()->giant_star()) 
01549                double_spiral_in();
01550              else if (get_secondary()->remnant())
01551                spiral_in(get_primary(),get_secondary());
01552              else
01553                dynamic_mass_transfer(get_primary(), get_secondary());
01554            }
01555            else if (get_secondary()->giant_star()) {
01556              if (get_primary()->remnant())
01557                spiral_in(get_secondary(),get_primary());
01558              else
01559                dynamic_mass_transfer(get_secondary(), get_primary());
01560            }
01561            else {
01562              cerr << "Merger double_star::common_envelope" << endl;
01563              dump(cerr, false);
01564            
01565              merge_elements(get_primary(), get_secondary());
01566            }
01567        }           
01568 
01569        //get_seba_counters()->common_envelope++;
01570      }
01571 }
01572 
01573 // common-envelope for two giant stars in contact.
01574 // results in spiral in of both cores in both envelopes.
01575 // final separation is computed from ejection of both giant envelopes.
01576 // if one of the cores fills its Roche-lobe a merger occurs.
01577 // The merger product loses some mass.
01578 void double_star::double_spiral_in() {
01579 
01580        star *p = get_primary();
01581        star *s = get_secondary();
01582        
01583        real mcore_p = p->get_core_mass();
01584        real menv_p = p->get_envelope_mass();
01585        real mtot_p = mcore_p + menv_p;
01586        real mcore_s = s->get_core_mass();
01587        real menv_s = s->get_envelope_mass();
01588        real mtot_s = mcore_s + menv_s;
01589 
01590        real r_p = roche_radius(p);
01591        real r_s = roche_radius(s);
01592 
01593        real alpha_lambda = cnsts.parameters(common_envelope_efficiency)
01594                          * cnsts.parameters(envelope_binding_energy);
01595 
01596        real post_ce_orbital_energy = mtot_p*menv_p/(alpha_lambda*r_p)
01597                                    + mtot_s*menv_s/(alpha_lambda*r_s)
01598                                    + mtot_p*mtot_s/(2 * semi);
01599 
01600        real post_ce_semi = mcore_p*mcore_s/(2*post_ce_orbital_energy);
01601        
01602        real rl_p = roche_radius(post_ce_semi, mcore_p, mcore_s);
01603        real rl_s = roche_radius(post_ce_semi, mcore_s, mcore_p);
01604 
01605        if (p->get_core_radius() >= rl_p    ||
01606            s->get_core_radius() >= rl_s) {
01607 
01608 #if 0       
01609          // see double_star::spiral_in()
01610           
01611          real semi_p = p->get_core_radius()
01612                      / roche_radius(1, p->get_core_mass(),
01613                                        s->get_core_mass());
01614          real semi_s = s->get_core_radius()
01615                      / roche_radius(1, s->get_core_mass(),
01616                                        p->get_core_mass());
01617 
01618           real post_ce_semi = max(semi_p, semi_s);
01619 
01620           // the mass lost before the two stars merge is computed from
01621           // change in orbital energy assuming that
01622           // the final separation << initial separation.
01623           
01624           real mass_loss_fr = post_ce_semi
01625                             / (alpha_lambda * mtot_p * mtot_s)
01626                             * (pow(mtot_p, 2)/r_p + pow(mtot_s, 2)/r_s);
01627 
01628           real mlf_limit = (menv_p + menv_s)/(mtot_p + mtot_s);
01629 #endif
01630          
01631          
01632          // see double_star::spiral_in()
01633 
01634          real semi_p = p->get_core_radius()
01635                      / roche_radius(1, mcore_p, mcore_s);
01636          real semi_s = s->get_core_radius()
01637                      / roche_radius(1, mcore_s, mcore_p);
01638          
01639          real post_ce_semi = max(semi_p, semi_s);
01640 
01641          // the mass lost before the two stars merge is computed from
01642          // change in orbital energy assuming that
01643          // the final separation << initial separation.
01644          // (GN Oct 27 1998) was wrong, we assumed f = (1-f) !
01645          //real mass_loss_fr = post_ce_semi
01646          //                  / (alpha_lambda * mtot_p * mtot_s)
01647          //                  * (pow(mtot_p, 2)/r_p + pow(mtot_s, 2)/r_s);
01648 
01649 
01650          // assume mass left after merger = k * m_tot
01651          // ( (1-k) [ M^2/R + m^2/r] ) / alpha_lambda =
01652          //                              k^2 M m /(2 af) -  M m /(2 ai)
01653          // is quadratic equation in k with
01654          // a = M m / (2 af)
01655          // b = [ M^2/R + m^2/r] / alpha_lambda
01656          // c = -b - M m / (2 ai)
01657          //
01658          // (GN Oct 27 1998) solve k with abc-equation
01659 
01660          real a = (mtot_p * mtot_s)/(2 * post_ce_semi);
01661          real b = (pow(mtot_p, 2)/r_p + pow(mtot_s, 2)/r_s)
01662                 /  alpha_lambda;
01663          real c = -b -(mtot_p * mtot_s)/(2 * semi);
01664 
01665          real mass_not_lost_fraction = (-b + sqrt(pow(b, 2) - 4 * a * c))
01666                                      / (2 * a);
01667 
01668          real mass_loss_fr = 1 - mass_not_lost_fraction;
01669          real mlf_limit = (menv_p + menv_s)/(mtot_p + mtot_s);
01670 
01671          if (mass_loss_fr >= 1) {
01672               cerr << "ERROR: in double_star::double_spiral_in()" << endl;
01673               cerr << "         Fraction of mass lost (" << mass_loss_fr 
01674                    << ") greater then 1" << endl;
01675             
01676             dump(cerr, false);
01677             mass_loss_fr = 0.99;
01678           }
01679           else if (mass_loss_fr > mlf_limit) {
01680               cerr << "WARNING: in double_star::double_spiral_in()" << endl;
01681               cerr << "         Fraction of mass lost (" << mass_loss_fr 
01682                    << ") greater then limit (" << mlf_limit << ")" << endl;
01683               dump(cerr, false);
01684           }
01685 
01686           // Only envelope mass may be removed to prevent
01687           // helium star formation.
01688           real mlost_p = min(mass_loss_fr*mtot_p, 0.99*menv_p);
01689           p->reduce_mass(mlost_p);
01690           real mlost_s = min(mass_loss_fr*mtot_s, 0.99*menv_s);
01691           s->reduce_mass(mlost_s);
01692 
01693           cerr << "Merger double_star::double_spiral_in()"<<endl;
01694           dump(cerr, false);
01695 
01696           merge_elements(p, s);
01697 
01698         } 
01699         else {
01700 
01701           cerr << "Survival double_star::double_spiral_in()"<<endl;
01702           dump(cerr, false);
01703           
01704           semi = post_ce_semi;
01705           p = p->reduce_mass(menv_p);
01706           s = s->reduce_mass(menv_s);
01707         }
01708 
01709        //get_seba_counters()->double_spiral_in++;
01710 }
01711 
01712 
01713 void double_star::spiral_in(star* larger,
01714                             star* smaller) {
01715 //      Compare total binary energy with the binding energy of the
01716 //      donor's envelope.
01717 //      If two stars are in contact after the common envelope,
01718 //      stars are merged.
01719 
01720      if (bin_type!=Merged && bin_type!=Disrupted) {
01721 
01722         real mcore_l = larger->get_core_mass();
01723         real menv_l = larger->get_envelope_mass();
01724         real mtot_l = mcore_l + menv_l;
01725 
01726 //      PRC(mcore_l);PRC(menv_l);PRL(mtot_l);
01727 
01728         // What is accreted by the accretor should be reduce in the donor.
01729         // (SPZ:  2 Jun 1999)
01730         //real m_acc = smaller->accretion_limit(m_env_l, 
01731         //             cnsts.parameters(spiral_in_time));
01732         real m_acc = 0;
01733         real mtot_s = smaller->get_total_mass() + m_acc;
01734         menv_l -= m_acc;
01735 
01736         real r_lobe = roche_radius(larger)/semi;
01737 
01738         real alpha_lambda = cnsts.parameters(common_envelope_efficiency)
01739                           * cnsts.parameters(envelope_binding_energy);
01740         real a_spi = semi*(mcore_l/mtot_l)/(1. + (2.*menv_l
01741                    / (alpha_lambda *r_lobe*mtot_s)));
01742        
01743         real rl_l = roche_radius(a_spi, mcore_l, mtot_s);
01744         real rl_s = roche_radius(a_spi, mtot_s, mcore_l);
01745 
01746         // Merger
01747         if (larger->get_core_radius()>=rl_l     ||
01748            smaller->get_radius()>=rl_s)         {
01749 
01750           // Determine the minimum separation before either the
01751           // core of the larger star or
01752           // its companion (the smaller star) fills its Roche-lobe.
01753           // substituted for: 
01754           // real da = semi*(1 - r_lobe) - smaller->get_effective_radius();
01755           // (SPZ+GN: 1 Oct 1998)
01756           
01757           real sma_larger = larger->get_core_radius()
01758                           / roche_radius(1, larger->get_core_mass(),
01759                                             smaller->get_total_mass());
01760           real sma_smaller = smaller->get_effective_radius()
01761                            / roche_radius(1, smaller->get_total_mass(),
01762                                              larger->get_core_mass());
01763           real sma_post_ce = max(sma_larger, sma_smaller);
01764 
01765           real mass_lost = (mtot_l*mtot_s/(2*sma_post_ce) 
01766                              - mtot_l*mtot_s/(2*semi))
01767                          / (mtot_l/(alpha_lambda*r_lobe*semi) 
01768                              + mtot_s/(2*sma_post_ce));
01769 
01770           //real da = semi - sma_post_ce;
01771           //real semi_fr = 1-da/semi;
01772           //      real mass_lost = (1 - semi_fr)
01773           //           / (1./mtot_l 
01774           //           + 2*semi_fr/(mtot_s
01775           //     *   cnsts.parameters(common_envelope_efficiency)
01776           //         *   cnsts.parameters(envelope_binding_energy)
01777           //     *   r_lobe));
01778 
01779            if (mass_lost>=menv_l) {
01780               mass_lost = 0.99*menv_l;
01781            }
01782            
01783            cerr << "Merger double_star::spiral_in()"<<endl;
01784            dump(cerr, false);
01785 
01786 
01787            //           larger->set_core_mass(mcl_old);
01788            //           larger->set_envelope_mass(mel_old);
01789            larger = larger->reduce_mass(mass_lost);
01790 
01791            if (mtot_l>mtot_s) 
01792              merge_elements(larger, smaller);
01793            else 
01794              merge_elements(smaller, larger);
01795         } 
01796         else {
01797           semi = a_spi;
01798 
01799           // note that the accreted mass to the smaller 
01800           // has not affected the spiral in. (SPZ:  2 Jun 1999)
01801           menv_l -= smaller->add_mass_to_accretor(
01802                              larger->get_envelope_mass(),
01803                              cnsts.parameters(spiral_in_time));
01804           smaller->set_effective_radius(smaller->get_radius());
01805           larger = larger->reduce_mass(larger->get_envelope_mass());
01806 
01807           // redundant due to implementation of new force donor timescale
01808           //                                         (SPZ+GN:23 Sep 1998)
01809           //set_donor_timescale(smaller);
01810           //determine_minimal_timestep();  
01811         }
01812      }
01813 
01814      //get_seba_counters()->spiral_in++;
01815 
01816 //cerr<<"leave spiral_in: semi = "<<semi<<endl;
01817 }
01818 
01819 void double_star::merge_elements(star* consumer,
01820                                  star* dinner) {
01821 
01822 //cerr<<"void double_star::merge_elements()"<<endl;
01823 //cerr<<"bin: "<<identity<<endl;
01824 
01825   bin_type = Merged;
01826 
01827   if (!get_use_hdyn()) {
01828     consumer->set_velocity(velocity); 
01829     dinner->set_velocity(velocity);
01830     
01831 // (GN+SPZ Apr 28 1999) star type can change (see single_star::merge_elements)
01832     consumer = consumer->merge_elements(dinner);
01833     semi = 0;
01834     dinner->set_envelope_mass(0);
01835     dinner->set_core_mass(0);
01836   }
01837   else {
01838     // solved in dstar_to_dyn merge_elements
01839   }
01840 
01841 //  dump("binev.data", false);
01842 cerr << "Merger is: "<<endl;
01843   dump(cerr, false);
01844   dump("SeBa.data", true);
01845 
01846   //get_seba_counters()->mergers++;
01847 
01848 }
01849 
01850 // This function is not used at the moment.
01851 // But should be used.
01852 // Mass loss must be performed for both
01853 // stars simultaniously.
01854 void double_star::perform_wind_loss(const real dt) {
01855 
01856   star* p = get_primary();
01857   star* s = get_secondary();
01858   p->stellar_wind(dt);
01859   s->stellar_wind(dt);
01860   p = s = NULL;
01861 
01862   //cerr<<"aMm'="<<semi<<" "<<get_primary()->get_total_mass()<<" "
01863   //                        <<get_secondary()->get_total_mass()<<endl;
01864 
01865 }
01866 
01867 void double_star::angular_momentum_loss(const real dt) {
01868 //      Lose angular momentum.
01869 //      First due to magnetic breaking then by gravitational 
01870 //      wave radiation.
01871 
01872   if (bin_type != Merged && bin_type != Disrupted) {
01873       magnetic_stellar_wind(dt);
01874       // gravrad is called from within magnetic_stellar_wind
01875       // because binary can merge or spiral-in
01876   }
01877 }
01878 
01879 //      Lose angular momentum due to magnetic breaking.
01880 //      Rappaport, Verbunt and Joss 1983.
01881 //
01882 //      Magnetic breaking takes place if the binary eccentricity
01883 //      becomes smaller than COROTATION_E_LIMIT due to tidal circularization.
01884 void double_star::magnetic_stellar_wind(const real dt) {
01885 
01886 //cerr<<"void double_star::magnetic_stellar_wind(dt="<<dt<<)"<<endl;
01887 
01888     real magnetic_braking_aml = mb_angular_momentum_loss();
01889   
01890     real a_dot = 2*dt*magnetic_braking_aml;
01891     real semi_new = semi*(1 + a_dot);
01892 
01893     if (semi_new<=0) {
01894 
01895       cerr << "Merger::magnetic_stellar_wind"<<endl;
01896       dump(cerr, false);
01897 
01898       if (get_primary()->get_effective_radius() >
01899           get_secondary()->get_effective_radius())
01900         merge_elements(get_primary(), get_secondary());
01901       else
01902         merge_elements(get_secondary(), get_primary());
01903 
01904       //get_seba_counters()->aml_mergers++;
01905 
01906     }
01907     else if(semi_new <= get_primary()->get_core_radius() +
01908                         get_secondary()->get_core_radius() ) {
01909       semi = semi_new;
01910 cerr << "magnetic stellar wind => ::common_envelope" << endl; 
01911       common_envelope();
01912       //      if (get_primary()->get_effective_radius() >
01913       //  get_secondary()->get_effective_radius())
01914       //        spiral_in(get_secondary(), get_primary());
01915       //      else
01916       //spiral_in(get_primary(), get_secondary());
01917     }
01918     else { 
01919       semi = semi_new;
01920       gravrad(dt);
01921     }
01922 }
01923 
01924 //      eccentricity variation due to the radiation
01925 //      of gravitational waves.
01926 //      Peters. P.C., 1964, {\em Phys. Rev.}, {\bf 136}, 1224.
01927 real double_star::de_dt_gwr(const real e) {
01928 
01929     real de_dt = (-305/15)*e*(1 + (121/304)*e*e)/pow(1-e*e, 2.5);
01930     de_dt *= get_primary()->get_total_mass()
01931            * get_secondary()->get_total_mass()
01932            *  get_total_mass()/pow(semi, 4.);
01933 
01934         return de_dt;
01935 }
01936 
01937 //      Equations from:
01938 //      Peters P.C., 1964, Phys. Ref., 136, 4B, B1224.
01939 void double_star::gravrad(const real dt) {
01940 
01941      real G3M3_C5R4 = pow(cnsts.physics(G)
01942                     * cnsts.parameters(solar_mass), 3.)
01943                     / (pow(cnsts.physics(C), 5.)
01944                     * pow(cnsts.parameters(solar_radius), 4.));
01945 
01946 //      Only appicable to small separation.
01947      if(semi/sqrt(get_total_mass())<=6.) {
01948         real e_new = eccentricity 
01949                    + G3M3_C5R4*de_dt_gwr(eccentricity)
01950                    * dt*cnsts.physics(Myear);
01951 
01952         real a_new;
01953         if (e_new > 0 && eccentricity > 0) {
01954            real c0 = (semi*(1-pow(eccentricity,2))/pow(eccentricity, 12./19.))
01955                    / pow(1 + 121*pow(eccentricity, 2.)/304, 870./2299.);
01956        
01957            a_new = (c0*pow(e_new, 12./19.)/(1-pow(e_new, 2.)))
01958                  * pow(1 + 121*pow(e_new, 2.)/304, 870./2299.);
01959         }
01960         else {
01961            e_new = 0;           // Circularize
01962            semi *= (1-eccentricity*eccentricity);
01963            real G3M3_C5 = pow(cnsts.physics(G)
01964                               *cnsts.parameters(solar_mass), 3.)
01965                         / pow(cnsts.physics(C), 5.);
01966            real c0 = G3M3_C5* 256
01967                    * get_primary()->get_total_mass()
01968                    * get_secondary()->get_total_mass()
01969                    * get_total_mass()/5.;
01970            c0 = pow(semi*cnsts.parameters(solar_radius), 4.)
01971               - c0*dt*cnsts.physics(Myear);
01972            c0 = max(c0, 0.);
01973            a_new = pow(c0, 0.25)/cnsts.parameters(solar_radius);
01974         }
01975     
01976         if (a_new<=0) {
01977 
01978           cerr << "Merger::gravrad"<<endl;
01979           dump(cerr, false);
01980 
01981           if (get_primary()->get_total_mass() >
01982               get_secondary()->get_total_mass())
01983             merge_elements(get_primary(), get_secondary());
01984           else
01985             merge_elements(get_secondary(), get_primary());
01986 
01987           //get_seba_counters()->gwr_mergers++;
01988         }
01989         else if(a_new <= get_primary()->get_core_radius() +
01990                          get_secondary()->get_core_radius() ) {
01991           semi=a_new;
01992           eccentricity = e_new;
01993 cerr << "gravrad => ::common_envelope" << endl; 
01994           common_envelope();
01995           //      if (get_primary()->get_effective_radius() >
01996           //  get_secondary()->get_effective_radius())
01997           //spiral_in(get_secondary(), get_primary());
01998           //      else
01999           //spiral_in(get_primary(), get_secondary());
02000         }
02001         else {
02002           semi=a_new;
02003           eccentricity = e_new;
02004         }
02005      }
02006 }
02007 
02008 #if 0
02009 real double_star::mdot_according_to_roche_radius_change(star* donor,
02010                                                    star* accretor) {
02011 //      Mass lost from donor due to loss of angular momentum by
02012 //      magnetic braking and gravitational wave-radiation.
02013 //      mass-transfer timescale becomes comparable to
02014 //      themal time-scale.
02015 
02016 //cerr<<"real double_star::mdot_according_to_roche_radius_change() = ";
02017         real mdot = 0;
02018      if (bin_type != Merged && bin_type != Disrupted) {
02019 
02020         real r_don = donor->get_effective_radius();
02021         real m_don = donor->get_total_mass();
02022         real m_acc = accretor->get_total_mass();
02023 
02024         real zeta_lobe = zeta(donor, accretor);
02025         real J_mb = 0;
02026         if (donor->magnetic()) {
02027            real c_mb = -3.8e-30*cnsts.parameters(solar_mass)*cnsts.physics(G)
02028                      / cnsts.parameters(solar_radius);
02029            J_mb = cnsts.physics(Myear)*c_mb * pow(m_don+m_acc, 2)
02030                 * pow(r_don, cnsts.parameters(magnetic_braking_exponent))
02031                 / (m_acc*pow(semi, 5)); 
02032         }
02033 
02034         real J_gr=0;
02035         if (semi/sqrt(get_total_mass())<=6.) {
02036            real c_gr = -32*pow(cnsts.parameters(solar_mass)*cnsts.physics(G), 3)
02037                      / (5*pow(cnsts.physics(C), 5)
02038                      *    pow(cnsts.parameters(solar_radius), 4));
02039            J_gr = cnsts.physics(Myear)*c_gr*m_don*m_acc*(m_don+m_acc)/pow(semi, 4);
02040         }
02041 
02042 //              On the nomination for checking!
02043         real drdot_dr=0;        //No stellar size variation.
02044         mdot = m_don*abs((2*(J_mb+J_gr) - drdot_dr)
02045              /           (zeta_lobe + 2*(5./6. - m_don/m_acc)));
02046      }
02047       
02048 //cerr<<mdot<<endl;
02049      return mdot;
02050 }
02051 #endif
02052 
02053 //      Mass lost from donor due to loss of angular momentum by
02054 //      magnetic braking and gravitational wave-radiation.
02055 //      mass-transfer timescale becomes comparable to
02056 //      themal time-scale.
02057 // New implementations (SPZ+GN:22 Sep 1998)
02058 real double_star::mdot_according_to_roche_radius_change(star* donor,
02059                                                         star* accretor) {
02060   real mdot = 0;
02061   if (bin_type != Merged && bin_type != Disrupted) {
02062 
02063     real m_don = donor->get_total_mass();
02064     real m_acc = accretor->get_total_mass();
02065 
02066     real zeta_th = donor->zeta_thermal();
02067     real rl = roche_radius(donor);
02068     
02069     //    real dm = 0.01*min(m_don, m_acc);
02070     // we use minimum_mass_step also in ::zeta
02071 // (GN+SPZ May  3 1999) in real mass transfer timestep we use 
02072 // delta m \sim timestep_factor * relative_mass
02073 // so
02074 //    real dm = cnsts.safety(minimum_mass_step);
02075 //    dm can become larger than donor mass.
02076 //    real dm = cnsts.safety(timestep_factor) * donor->get_relative_mass();
02077     real dm = cnsts.safety(timestep_factor) * donor->get_total_mass();
02078 
02079     real rl_2 = roche_radius(semi, m_don-dm, m_acc+dm);
02080 
02081     real J_mb = mb_angular_momentum_loss();
02082     real J_gwr = gwr_angular_momentum_loss(m_don, m_acc, semi);
02083 
02084     // Note: dm must be negative, because we talk about the mass losing star
02085     real zeta_dimensionless_lobe = (rl_2 - rl)*m_don/(-1.*dm*rl);
02086     mdot = m_don*abs((2*(J_mb+J_gwr))
02087          / (2*(m_don/m_acc-1.) - zeta_th + zeta_dimensionless_lobe));
02088 
02089    real mdot_analytic = m_don*J_gwr/(m_don/m_acc - 2./3);
02090 
02091   }
02092 
02093   return mdot;
02094 }
02095 
02096 
02097 //      Compute radial orbital velocities a binary components.
02098 //      neglecting the eccenticity.
02099 void double_star::calculate_velocities() {
02100 
02101      if(bin_type != Merged && bin_type != Disrupted) {
02102         real mu = get_primary()->get_total_mass()/get_total_mass();
02103         real mean_r = semi*(1+0.5*eccentricity*eccentricity);
02104         real v_rel = sqrt(cnsts.physics(G)*get_total_mass()
02105                           *cnsts.parameters(solar_mass)
02106                           *((2/(mean_r*cnsts.parameters(solar_radius)))
02107                          - (1/(semi*cnsts.parameters(solar_radius)))));
02108         v_rel /= cnsts.physics(km_per_s);
02109         get_primary()->set_velocity((1-mu)*v_rel);
02110         get_secondary()->set_velocity(mu*v_rel);
02111      }
02112 }
02113 
02114 //      Alternative of the double_star::contact();
02115 //      No mass transfer occurs during stable case.
02116 //void double_star::common_envelope(real dt) {
02117 void double_star::contact_binary(real dt) {
02118 
02119     if (REPORT_FUNCTION_NAMES)
02120       cerr << "::contact_binary(dt = "<<dt << ")" << endl;
02121 
02122 // for the moment: only [ms,ms] stable, but what about [bd,ms] and [he,ms] etc.
02123     if (get_primary()->get_element_type() == Main_Sequence && 
02124         get_secondary()->get_element_type() == Main_Sequence) {
02125 
02126       // 'stable' contact binaries:
02127       // double_star::contact transferres matter from secondary to
02128       // primary: results in flip-flop, which rejuvenates both stars
02129       // (This might be not so bad, PPE private communication)
02130       // For now: do nothing
02131         
02132 //        if (primary->get_effective_radius()>
02133 //            secondary->get_effective_radius()) {
02134 //           if (ready_for_mass_transfer(secondary))
02135 //              contact(secondary, primary, dt);
02136 //        }
02137 //        else {
02138 //           if (ready_for_mass_transfer(primary)) 
02139 //              contact(primary, secondary, dt);
02140 //        }
02141 
02142         //get_seba_counters()->contact++;
02143 
02144     } else {
02145 
02146       common_envelope();
02147     }
02148 
02149 }
02150 
02151 // (SPZ+GN: 28 Jul 2000)
02152 // Dynamic mass transfer as in Eq. 5 of Nelemans VYPZ 2000 A&A in press
02153 
02154 void double_star::dynamic_mass_transfer(star* larger, star* smaller) {
02155   
02156 //  cerr << "double_star::dynamic_mass_transfer()" << endl;
02157 
02158   if (bin_type!=Merged && bin_type!=Disrupted) {
02159  
02160         real M_core = larger->get_core_mass();
02161         real M_env  = larger->get_envelope_mass();
02162         real M_tot  = M_env + M_core;
02163 
02164         // At the moment no mass is accreted dureing dynamic mass tansfer
02165         // May be changed easily.
02166         real m_acc = 0;
02167         real m_new  = smaller->get_total_mass() - m_acc;
02168         M_env -= m_acc;
02169         
02170 // (GN Sep 22 1999) ang.mom balance: \Delta J = \gamma * J * \Delta M / M
02171 // See Eq. 5 of Nelemans VYPZ 2000 A&A
02172         real gamma = cnsts.parameters(dynamic_mass_transfer_gamma);
02173 
02174         real J_i = sqrt(semi) * (M_tot  * m_new)/sqrt(M_tot  + m_new);
02175         real J_f_over_sqrt_af = (M_core * m_new)/sqrt(M_core + m_new);
02176         real J_lost           = gamma * M_env * J_i/ (M_tot  + m_new);
02177         real sqrt_a_f         = max(0.,(J_i - J_lost)/J_f_over_sqrt_af);
02178         real a_f              = pow(sqrt_a_f, 2);
02179 
02180         if(REPORT_BINARY_EVOLUTION) {
02181 
02182           PRL(pow(M_tot/M_core, 2));
02183           PRL((M_tot + m_new)/(M_core + m_new));
02184           PRL(exp(-2. * M_env/m_new));
02185         }
02186 
02187           real rl_l = roche_radius(a_f, M_core, m_new);
02188           real rl_s = roche_radius(a_f, m_new, M_core);
02189 
02190         // Merger
02191         // Note that we hare want the equilibrium radius of the accretor
02192         // The effective radius of the accretor is already used to
02193         // determine how conservative mass transfer is. (SPZ: 2 Jun 1999)
02194           if (larger->get_core_radius()>=rl_l   ||
02195               smaller->get_radius()>=rl_s)      {
02196 
02197             PRC(smaller->get_radius());PRC(rl_s);PRL(a_f);
02198 
02199             cerr << "Merger double_star::dynamic_mass_transfer" << endl;
02200             dump(cerr, false);
02201             merge_elements(larger,smaller);
02202           }
02203           else {
02204 
02205 
02206             if(REPORT_BINARY_EVOLUTION) {
02207 
02208               cerr << "semi before = " << semi << endl;
02209               cerr << "semi after = " << a_f << endl;
02210             
02211               real Eorb1 = (M_tot * m_new)/(2 * semi);
02212               real Ebind = (M_tot * (M_tot - M_core))/larger->get_radius();
02213               real Eorb2 = (M_core * m_new) / (2*a_f);
02214 
02215               PRC(Eorb1);PRC(Eorb2);PRL(Ebind);
02216               cerr << "(Eorb2 - Eorb1)/Ebind = "
02217                    << (Eorb2 - Eorb1)/Ebind << endl;
02218             }
02219 
02220             semi = a_f; 
02221             smaller->add_mass_to_accretor(larger->get_envelope_mass(),
02222                      cnsts.parameters(spiral_in_time));
02223             smaller->set_effective_radius(smaller->get_radius());
02224 
02225             larger = larger->reduce_mass(larger->get_envelope_mass());
02226 
02227           }
02228 
02229         //get_seba_counters()->dynamic++;
02230 
02231   }
02232 
02233 //  cerr <<"Leave ::dynamics mass transfer"<<endl;
02234 }
02235 
02236 // (SPZ+GN: 28 Jul 2000) 
02237 // Old 'beta' mechanism, not used any more.
02238 #if 0
02239 // (GN Apr 22 1999) artifical experiment to make giant - main_sequence
02240 // contact system much less dramatic spiral-in (no separation change!)
02241 void double_star::dynamic_mass_transfer(star* larger, star* smaller) {
02242   
02243 //  cerr << "double_star::dynamic_mass_transfer()" << endl;
02244 
02245   if (bin_type!=Merged && bin_type!=Disrupted) {
02246        
02247 // beta is cnst or specific ang. mom of donor/accretor
02248         real beta = cnsts.parameters(specific_angular_momentum_loss);
02249 // donor
02250 //      real beta = mtot_s/mtot_l;
02251 // accretor
02252 //      real beta = mtot_l/mtot_s;
02253 
02254 //        real J_i = sqrt(semi)*(mtot_l*mtot_s)/sqrt(mtot_l + mtot_s);
02255 //        real J_f_over_sqrt_af = (mcore_l*mtot_s)/sqrt(mcore_l + mtot_s);
02256 //        real J_lost = beta * menv_l * J_i / (mtot_l + mtot_s);
02257 //        real sqrt_a_f = max(0.,(J_i - J_lost)/J_f_over_sqrt_af);
02258 
02259 //      cerr <<"larger " << endl;
02260 //      larger->dump(cerr, false);
02261 //      cerr<< endl;
02262 //      cerr <<"smaller " << endl;
02263 //      smaller->dump(cerr, false);
02264 //        cerr << endl;
02265 
02266  
02267         real M_core = larger->get_core_mass();
02268         real M_env  = larger->get_envelope_mass();
02269         real M_tot  = M_env + M_core;
02270 
02271         // What is accreted by the accretor should be reduce in the donor.
02272         //m_acc -= smaller->accretion_limit(M_env, 
02273         //                  cnsts.parameters(spiral_in_time));
02274         real m_acc = 0;
02275         real m_new  = smaller->get_total_mass() - m_acc;
02276         M_env -= m_acc;
02277         
02278           real a_f = semi*pow(M_tot/M_core,2)
02279                          *pow((M_core + m_new)/(M_tot + m_new), 
02280                               2 * beta + 1);
02281   
02282           real Eorb1 = (M_tot * m_new)/(2 * semi);
02283           real Ebind = (M_tot * (M_tot - M_core))/larger->get_radius();
02284 
02285           real rl_l = roche_radius(a_f, M_core, m_new);
02286           real rl_s = roche_radius(a_f, m_new, M_core);
02287 
02288 
02289         // Merger
02290         // Note that we hare want the equilibrium radius of the accretor
02291         // The effective radius of the accretor is already used to
02292         // determine how conservative mass transfer is. (SPZ: 2 Jun 1999)
02293           if (larger->get_core_radius()>=rl_l   ||
02294               smaller->get_radius()>=rl_s)      {
02295 
02296             PRC(smaller->get_radius());PRC(rl_s);PRL(a_f);
02297 
02298             cerr << "Merger double_star::dynamic_mass_transfer" << endl;
02299             dump(cerr, false);
02300             merge_elements(larger,smaller);
02301           }
02302           else {
02303 
02304             cerr << "semi before = " << semi << endl;
02305             semi = a_f; 
02306             cerr << "semi after = " << semi << endl;
02307             
02308             real Eorb2 = (M_core * m_new) / (2*semi);
02309 
02310             PRC(Eorb1);PRC(Eorb2);PRL(Ebind);
02311             cerr << "(Eorb2 - Eorb1)/Ebind = " << (Eorb2 - Eorb1)/Ebind << endl;
02312             smaller->add_mass_to_accretor(larger->get_envelope_mass(),
02313                      cnsts.parameters(spiral_in_time));
02314             smaller->set_effective_radius(smaller->get_radius());
02315 
02316             larger = larger->reduce_mass(larger->get_envelope_mass());
02317             // larger->set_effective_radius(larger->get_radius());
02318           }
02319 
02320         //get_seba_counters()->dynamic++;
02321 
02322   }
02323 
02324 //  cerr <<"Leave ::dynamics mass transfer"<<endl;
02325 }
02326 #endif
02327 
02328 bool double_star::low_mass_star() {
02329   
02330     return (get_total_mass()<=cnsts.parameters(low_mass_star_mass_limit))
02331            ?true: false;
02332 }
02333 
02334 bool double_star::medium_mass_star() {
02335   
02336   return (!low_mass_star() && !high_mass_star())?true:false;
02337 }
02338 
02339 bool double_star::high_mass_star() {
02340   
02341   return (get_total_mass()>cnsts.parameters(medium_mass_star_mass_limit))
02342          ?true: false;
02343 }
02344 
02345 
02346 void double_star::refresh_memory() {
02347 
02348      star *p = get_primary();
02349      star *s = get_secondary();
02350      get_primary()->refresh_memory();
02351      get_secondary()->refresh_memory();
02352      p=s=NULL;
02353 
02354      previous.binary_age      = binary_age;
02355      previous.semi            = semi;
02356      previous.eccentricity    = eccentricity;
02357 
02358      previous.donor_timescale = donor_timescale;
02359 
02360 }
02361 
02362 void double_star::recall_memory() {
02363 
02364      star *p = get_primary();
02365      star *s = get_secondary();
02366      p->recall_memory();
02367      s->recall_memory();
02368      p=s=NULL;
02369 
02370      binary_age      = previous.binary_age;
02371      semi            = previous.semi;
02372      eccentricity    = previous.eccentricity;
02373 
02374      donor_timescale = previous.donor_timescale;
02375 }
02376 
02377 // New version of ::zeta (SPZ+GN:22 Sep 1998)
02378 // To be debugged....?
02379 // method: transfer planet and see how the compaion would react.
02380 // then compute respons of orbit and compute zeta
02381 // Problem:
02382 // Non conservative mass transfer can lead to stabilization (?!)
02383 // of the orbit and thus conservative mass transfer
02384 real double_star::zeta(star * donor, 
02385                        star * accretor) {
02386 //      Find zeta Roche-lobe.
02387   
02388      if (bin_type!=Merged && bin_type!=Disrupted) {
02389 
02390 #if 0       
02391        real md_dot, ma_dot;
02392        real dt = cnsts.safety(minimum_timestep);
02393        if (get_donor_timescale()>0) {
02394          md_dot=donor->get_relative_mass()*dt/get_donor_timescale();
02395        }
02396        else {
02397          md_dot=cnsts.safety(minimum_mass_step);  //safety
02398        }
02399        md_dot = min(md_dot, donor->get_envelope_mass())
02400               + cnsts.safety(minimum_mass_step);
02401        ma_dot = accretor->accretion_limit(md_dot, dt);
02402 #endif
02403 
02404        // Use total mass here as md_dot is only used to determine zeta
02405        real md_dot = min(donor->get_total_mass(), 
02406                          cnsts.safety(minimum_mass_step));
02407        //       real md_dot = min(donor->get_envelope_mass(), 
02408        //                        cnsts.safety(minimum_mass_step));
02409        PRC(md_dot);PRC(donor_timescale);PRL(donor->get_relative_mass());
02410        real dt = md_dot * donor_timescale/donor->get_relative_mass();
02411        real ma_dot = accretor->accretion_limit(md_dot, dt);
02412 
02413        real M_old = get_total_mass();
02414        real old_donor_mass = donor->get_total_mass();
02415        real old_accretor_mass = accretor->get_total_mass();
02416        real q_old = old_accretor_mass/old_donor_mass;
02417 
02418        real M_new = get_total_mass() - md_dot +  ma_dot;
02419        real new_donor_mass = donor->get_total_mass() - md_dot;
02420        real new_accretor_mass = accretor->get_total_mass() + ma_dot;
02421        real q_new = new_accretor_mass/new_donor_mass;
02422 
02423        real a_fr, new_semi;
02424        real beta = cnsts.parameters(specific_angular_momentum_loss);
02425        a_fr  = pow(old_donor_mass*old_accretor_mass
02426              / (new_donor_mass*new_accretor_mass), 2);
02427        new_semi = semi*pow(M_new/M_old, 2*beta + 1)*a_fr;
02428        
02429        real a_dot=0;
02430 
02431        real magnetic_braking_aml = mb_angular_momentum_loss();
02432        real grav_rad_aml =  gwr_angular_momentum_loss(get_primary()
02433                                                       ->get_total_mass(),
02434                                                       get_secondary()
02435                                                       ->get_total_mass(),
02436                                                       semi);
02437 
02438        //       PRC(magnetic_braking_aml);PRL(grav_rad_aml);
02439        //       PRC(dt);PRC(semi);
02440        a_dot = 2*dt*semi*(magnetic_braking_aml+grav_rad_aml);
02441        //       PRC(a_dot);
02442        new_semi += a_dot;
02443        //PRL(a_dot);
02444        
02445        real rl = roche_radius(donor);
02446 
02447        real rl_d = roche_radius(new_semi,
02448                                 new_donor_mass,
02449                                 new_accretor_mass);
02450 
02451 //       PRC(new_semi);PRC(rl_d);PRL(rl);
02452        real d_lnr = (rl_d - rl)/rl;
02453        real d_lnm = (new_donor_mass - donor->get_total_mass()) 
02454                   /  donor->get_total_mass();
02455        
02456        real zeta;
02457        if(d_lnm==0) {
02458          cerr << "WARNING: d_lnm (= " << d_lnm << ") has an illegal value"
02459               << endl;
02460          zeta = 0;
02461          dump(cerr, true);
02462        }
02463        else {
02464          zeta = d_lnr/d_lnm;
02465        }
02466 
02467        //       PRC(M_old);PRL(M_new);
02468        //       PRC(old_donor_mass);PRL(new_donor_mass);
02469        //       PRC(old_accretor_mass);PRL(new_accretor_mass);
02470 
02471        //       PRC(a_dot);PRC(dt);PRC(semi);PRL(new_semi);
02472        //       PRC(rl);PRC(rl_d);PRC(d_lnr);PRL(d_lnm);
02473        //       PRL(zeta);
02474 
02475        return zeta;
02476      }
02477 
02478      // Merged or disrupted.
02479      return 1;
02480 }
02481      
02482 void double_star::enhance_cluster_profile(cluster_profile& c_prof) {
02483 
02484       double_profile binary;
02485       //make_profile(identity, initial.start_time, binary, initial);
02486       binary.enhance_double_profile(this);
02487       c_prof.enhance_cluster_profile(binary);      
02488    }
02489 
02490 // Used by determine_dt
02491 real double_star::orbital_timescale() {
02492 
02493   real magnetic_braking_aml = mb_angular_momentum_loss();
02494   real grav_rad_aml =  gwr_angular_momentum_loss(get_primary()
02495                                                  ->get_total_mass(),
02496                                                  get_secondary()
02497                                                  ->get_total_mass(),
02498                                                  semi);
02499   real dt = cnsts.safety(maximum_timestep);
02500 
02501   real aml_loss = abs(magnetic_braking_aml)
02502                 + abs(grav_rad_aml);
02503   // about 10 steps to lose ang. momentum.
02504 
02505   if (aml_loss> 0) 
02506     dt=.1/aml_loss;
02507 
02508   return dt;
02509 }
02510 
02511 // Peters & Mathews, 1963/4
02512 real double_star::gwr_angular_momentum_loss(const real m_prim,
02513                                             const real m_sec,
02514                                             const real sma) {
02515 
02516   real m_tot = m_prim + m_sec;
02517   real J_gwr = 0;
02518   
02519   if(sma/sqrt(m_tot)<=6.) {
02520   
02521     real c_gwr = -32*pow(cnsts.parameters(solar_mass)*
02522                         cnsts.physics(G), 3)
02523                / (5*pow(cnsts.physics(C), 5)*
02524                  pow(cnsts.parameters(solar_radius), 4));
02525 
02526     J_gwr = c_gwr*m_prim*m_sec*m_tot/pow(sma, 4);  
02527 
02528   }
02529   
02530   return J_gwr*cnsts.physics(Myear);
02531 }
02532 
02533 // Rappaport, Verbunt & Joss, 1983
02534 real double_star::mb_angular_momentum_loss() {
02535     
02536   real c_mb = -3.8e-30*cnsts.parameters(solar_mass)*cnsts.physics(G)
02537             /          cnsts.parameters(solar_radius);
02538 
02539   real J_mb_prim = 0;
02540   if (get_primary()->magnetic() && eccentricity<=
02541       cnsts.parameters(corotation_eccentricity))
02542     J_mb_prim = c_mb * pow(get_total_mass(), 2)
02543               * pow(get_primary()->get_effective_radius(),
02544                     cnsts.parameters(magnetic_braking_exponent))
02545               / (get_secondary()->get_total_mass()*pow(semi, 5));
02546   
02547   real J_mb_sec = 0;
02548   if (get_secondary()->magnetic() && eccentricity<=
02549       cnsts.parameters(corotation_eccentricity))
02550         J_mb_sec = c_mb * pow(get_total_mass(), 2)
02551                  * pow(get_secondary()->get_effective_radius(),
02552                        cnsts.parameters(magnetic_braking_exponent))
02553                  / (get_primary()->get_total_mass()*pow(semi, 5));
02554 
02555   real J_mb=(J_mb_prim+J_mb_sec)*cnsts.physics(Myear);
02556 
02557   return J_mb;
02558 
02559 }
02560 
02561 //              The circularization radius for the accretion stream 
02562 //              for star with mass m1 is fiven by 
02563 //              (Shore, S., Livio, M., vanden Heuvel EPJ., saas-Fee
02564 //              Advanced Course 22 for Astronomy and Astrophysics, 
02565 //              Interacting Binaries p145):
02566 real double_star::circularization_radius(const real m1, const real m2) {
02567   
02568      real q = m2/m1;
02569      real r_c = semi*(1+q)*pow(0.5 - 0.227 * log10(q), 4);
02570      
02571      return r_c;
02572 }
02573 
02574 
02575 real double_star::get_period() {
02576 
02577      real p = 2*PI*sqrt(pow(semi*cnsts.parameters(solar_radius), 3.)
02578                         / (cnsts.physics(G)*get_total_mass()
02579                            *cnsts.parameters(solar_mass)))
02580             /  cnsts.physics(days);
02581           
02582      if(p<=0) p=1;
02583           
02584      return p; 
02585 }
02586 
02587 
02588 real double_star::potential_energy() {
02589   
02590      real GM2_R = cnsts.physics(G)*pow(cnsts.parameters(solar_mass), 2)
02591                 / cnsts.parameters(solar_radius);
02592      real u = GM2_R*get_primary()->get_total_mass()
02593             * get_secondary()->get_total_mass()/semi;
02594      
02595      return -u;
02596 }
02597 real double_star::kinetic_energy() {
02598   
02599      real M_km_s = cnsts.parameters(solar_mass)
02600                  * pow(cnsts.physics(km_per_s), 2);
02601      real k = 0.5*M_km_s*get_total_mass()*velocity*velocity;
02602      
02603      return k;
02604 }
02605 
02606 real double_star::total_energy() {
02607   return kinetic_energy() + potential_energy();
02608 }
02609 
02610 
02611 

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