void reincorporate_gas(int p, double dt) { double reincorporated, fraction, reinc_time; reincorporated = 0.; mass_checks("reincorporate_gas #1",p); /* DeLucia2007 -> Mdot_eject=-gama_ej * M_ejected/tdyn */ if(ReIncorporationRecipe == 0) reincorporated = ReIncorporationFactor * Gal[p].EjectedMass / (Gal[p].Rvir / Gal[p].Vvir) * dt; /* Guo2010 -> Mdot_eject=-gama_ej * M_ejected/tdyn * Vvir/220 */ else if(ReIncorporationRecipe == 1) reincorporated = ReIncorporationFactor * Gal[p].EjectedMass / (Gal[p].Rvir / Gal[p].Vvir) * Gal[p].Vvir/220. *dt ; /* Henriques2012b Mdot_eject=-gama_ej*M_ejected*M_vir * Mvir should be in units of 1e12, but inside the * code Mvir is already in units of 1.e10*/ else if(ReIncorporationRecipe == 2) { // Oppenheimer & Dave 2008 /* reinc_time= pow(Gal[p].Mvir/Hubble_h,-0.6)*ReIncorporationFactor/UnitTime_in_years; if(Gal[p].Mvir/Hubble_h > 500.) reinc_time= pow(500.,-0.6)*ReIncorporationFactor/UnitTime_in_years; reincorporated = Gal[p].EjectedMass / reinc_time * dt; */ reinc_time= (Hubble_h/Gal[p].Mvir)*(ReIncorporationFactor/UnitTime_in_years); reincorporated = Gal[p].EjectedMass / reinc_time * dt; // reinc_time= (Hubble_h)*(ReIncorporationFactor/UnitTime_in_years); // reincorporated = Gal[p].EjectedMass / reinc_time * dt; /*reinc_time= ReIncorporationFactor/UnitTime_in_years * pow((1+ZZ[Gal[p].SnapNum]),ReincZpower) * pow(Gal[p].Vvir/220.,-ReincVelocitypower) / (hubble_of_z(Gal[p].HaloNr)); reincorporated = Gal[p].EjectedMass / reinc_time *dt ;*/ } if(FeedbackRecipe == 1) { reincorporated = ReIncorporationFactor * Gal[p].EjectedMass / (Gal[p].Rvir * min(FeedbackEjectionEfficiency,1.)*sqrt(EtaSNcode * EnergySNcode)/(Gal[p].Vvir*Gal[p].Vvir)) * Gal[p].Vvir/220. * 1.e-6* dt ; //reincorporated = ReIncorporationFactor * Gal[p].EjectedMass / (Gal[p].Rvir / Gal[p].Vvir) * // min(FeedbackEjectionEfficiency,1.) * Gal[p].Vvir/220. *dt ; //reincorporated = ReIncorporationFactor * Gal[p].EjectedMass / (Gal[p].Rvir / Gal[p].Vvir) * Gal[p].Vvir/220. *dt ; //reincorporated = 0.; } if (reincorporated > Gal[p].EjectedMass) reincorporated = Gal[p].EjectedMass; mass_checks("reincorporate_gas #1.5",p); /*Update ejected and hot gas contents*/ if (Gal[p].EjectedMass > 0.) { fraction=((float)reincorporated)/Gal[p].EjectedMass; //printf("%s","Gas Reincorporated: "); //printf("%lf \n",reincorporated); transfer_gas(p,"Hot",p,"Ejected",fraction,"reincorporate_gas", __LINE__); } mass_checks("reincorporate_gas #2",p); }
void add_galaxies_together(int t, int p) { /** @brief All the components of the satellite galaxy are added to the * correspondent component of the central galaxy. Cold gas spin * is updated and a bulge is formed at the central galaxy, with * the stars of the satellite if BulgeFormationInMinorMergersOn=1. * * TODO Even though galaxy p has been set to type 3 (ie a non-galaxy), it would * make mass conservation more explicit to zero the properties of galaxy p after * the merger. * TODO Correct artificial diffusion of metals when BulgeFormationInMinorMergersOn=1. */ int outputbin, j; float tspin[3],tmass,pmass; /* t central, p satellite */ mass_checks("add_galaxies_together #0",p); mass_checks("add_galaxies_together #0.1",t); /* angular momentum transfer between gas*/ tmass= Gal[t].ColdGas; pmass= Gal[p].ColdGas; Gal[t].MergeSat +=(Gal[p].DiskMass+Gal[p].BulgeMass); Gal[p].MergeSat=0.; transfer_gas(t,"Cold",p,"Cold",1.); transfer_gas(t,"Hot",p,"Hot",1.); transfer_gas(t,"Ejected",p,"Ejected",1.); //TODO chose move to ejected or hot if(BulgeFormationInMinorMergersOn) transfer_stars(t,"Bulge",p,"Disk",1.); else transfer_stars(t,"Disk",p,"Disk",1.); transfer_stars(t,"Bulge",p,"Bulge",1.); transfer_stars(t,"ICM",p,"ICM",1.); Gal[t].BlackHoleMass += Gal[p].BlackHoleMass; Gal[p].BlackHoleMass=0.; Gal[t].BlackHoleGas += Gal[p].BlackHoleGas; Gal[p].BlackHoleGas=0.; Gal[t].StarMerge += Gal[p].StarMerge; Gal[p].StarMerge=0.; mass_checks("add_galaxies_together #1",p); mass_checks("add_galaxies_together #1.1",t); /*update the gas spin*/ for(j=0;j<3;j++) tspin[j]=Gal[t].GasSpin[j]*tmass+Gal[t].HaloSpin[j]*pmass; if (Gal[t].ColdGas != 0) for (j=0;j<3;j++) Gal[t].GasSpin[j]=tspin[j]/(Gal[t].ColdGas); #ifdef SAVE_MEMORY Gal[t].Sfr += Gal[p].Sfr; #else for(outputbin = 0; outputbin < NOUT; outputbin++) Gal[t].Sfr[outputbin] += Gal[p].Sfr[outputbin]; #endif if(BulgeFormationInMinorMergersOn) { #ifdef SAVE_MEMORY Gal[t].SfrBulge += Gal[p].Sfr; #else for(outputbin = 0; outputbin < NOUT; outputbin++) Gal[t].SfrBulge[outputbin] += Gal[p].Sfr[outputbin]; #endif } /* Add the black hole accretion rates. This makes little sense but is not * used if the superior BlackHoleGrowth==1 switch is on. */ Gal[t].QuasarAccretionRate += Gal[p].QuasarAccretionRate; Gal[t].RadioAccretionRate += Gal[p].RadioAccretionRate; #ifndef POST_PROCESS_MAGS /* Add the luminosities of the satellite and central galaxy */ #ifdef OUTPUT_REST_MAGS for(outputbin = 0; outputbin < NOUT; outputbin++) { for(j = 0; j < NMAG; j++) { Gal[t].Lum[j][outputbin] += Gal[p].Lum[j][outputbin]; Gal[t].YLum[j][outputbin] += Gal[p].YLum[j][outputbin]; #ifdef ICL Gal[t].ICLLum[j][outputbin] += Gal[p].ICLLum[j][outputbin]; #endif } if(BulgeFormationInMinorMergersOn) { for(j = 0; j < NMAG; j++) { Gal[t].LumBulge[j][outputbin] += Gal[p].Lum[j][outputbin]; Gal[t].YLumBulge[j][outputbin] += Gal[p].YLum[j][outputbin]; } } else { for(j = 0; j < NMAG; j++) { Gal[t].LumBulge[j][outputbin] += Gal[p].LumBulge[j][outputbin]; Gal[t].YLumBulge[j][outputbin] += Gal[p].YLumBulge[j][outputbin]; } } Gal[t].MassWeightAge[outputbin] += Gal[p].MassWeightAge[outputbin]; } #endif // OUTPUT_REST_MAGS #ifdef COMPUTE_OBS_MAGS for(outputbin = 0; outputbin < NOUT; outputbin++) { for(j = 0; j < NMAG; j++) { Gal[t].ObsLum[j][outputbin] += Gal[p].ObsLum[j][outputbin]; Gal[t].ObsYLum[j][outputbin] += Gal[p].ObsYLum[j][outputbin]; #ifdef ICL Gal[t].ObsICL[j][outputbin] += Gal[p].ObsICL[j][outputbin]; #endif #ifdef OUTPUT_MOMAF_INPUTS Gal[t].dObsLum[j][outputbin] += Gal[p].dObsLum[j][outputbin]; Gal[t].dObsYLum[j][outputbin] += Gal[p].dObsYLum[j][outputbin]; #endif } if(BulgeFormationInMinorMergersOn) { for(j = 0; j < NMAG; j++) { Gal[t].ObsLumBulge[j][outputbin] += Gal[p].ObsLum[j][outputbin]; Gal[t].ObsYLumBulge[j][outputbin] += Gal[p].ObsYLum[j][outputbin]; #ifdef OUTPUT_MOMAF_INPUTS Gal[t].dObsLumBulge[j][outputbin] += Gal[p].dObsLum[j][outputbin]; Gal[t].dObsYLumBulge[j][outputbin] += Gal[p].dObsYLum[j][outputbin]; #endif } } else { for(j = 0; j < NMAG; j++) { Gal[t].ObsLumBulge[j][outputbin] += Gal[p].ObsLumBulge[j][outputbin]; Gal[t].ObsYLumBulge[j][outputbin] += Gal[p].ObsYLumBulge[j][outputbin]; #ifdef OUTPUT_MOMAF_INPUTS Gal[t].dObsLumBulge[j][outputbin] += Gal[p].dObsLumBulge[j][outputbin]; Gal[t].dObsYLumBulge[j][outputbin] += Gal[p].dObsYLumBulge[j][outputbin]; #endif } } } #endif //COMPUTE_OBS_MAGS #endif //POST_PROCESS_MAGS }
double collisional_starburst_recipe(double mass_ratio, int merger_centralgal, int centralgal, double time, double deltaT, int nstep) //ROB: New variable 'nstep' added, for use in update_yields() { /** @brief If StarBurstRecipe = 1 (since Croton2006), the Somerville 2001 * model of bursts is used. The burst can happen for both major * and minor mergers, with a fraction of the added cold gas from * the satellite and central being consumed. SN Feedback from * starformation is computed and the sizes of bulge and disk * followed (not done for the other burst mode).*/ double mstars, reheated_mass, ejected_mass, fac, metallicitySF; double CentralVvir, eburst, MergeCentralVvir, Ggas, EjectVmax, EjectVvir; double SN_Energy, Reheat_Energy; /* This is the major and minor merger starburst recipe of Somerville 2001. * The coefficients in eburst are taken from TJ Cox's PhD thesis and should * be more accurate then previous. */ Ggas=Gal[merger_centralgal].ColdGas; /* the bursting fraction given the mass ratio */ /* m_dot = 0.56*(m_sat/m_central)^0.7*m_gas */ eburst = SfrBurstEfficiency * pow(mass_ratio, SfrBurstSlope); //eburst = 0.56 * pow(mass_ratio, 0.7); mstars = eburst * Gal[merger_centralgal].ColdGas; if(mstars < 0.0) mstars = 0.0; /* this bursting results in SN feedback on the cold/hot gas * TODO this is the same code used in star_formation_and_feedback.c * and should be merged if possible. */ if(FeedbackRecipe == 0 || FeedbackRecipe == 1) { if (Gal[merger_centralgal].Type == 0) reheated_mass = FeedbackReheatingEpsilon * mstars*(.5+1./pow(Gal[merger_centralgal].Vmax/ReheatPreVelocity,ReheatSlope)); else reheated_mass = FeedbackReheatingEpsilon * mstars*(.5+1./pow(Gal[merger_centralgal].InfallVmax/ReheatPreVelocity,ReheatSlope)); } //Make sure that the energy used does not exceed the SN energy (central subhalo Vvir used) if(FeedbackRecipe == 0 || FeedbackRecipe == 1) { if (reheated_mass * Gal[merger_centralgal].Vvir * Gal[merger_centralgal].Vvir > mstars * EtaSNcode * EnergySNcode) reheated_mass = mstars * EtaSNcode * EnergySNcode / Gal[merger_centralgal].Vvir / Gal[merger_centralgal].Vvir; } /* cant use more cold gas than is available! so balance SF and feedback */ if((mstars + reheated_mass) > Gal[merger_centralgal].ColdGas) { fac = Gal[merger_centralgal].ColdGas / (mstars + reheated_mass); mstars *= fac; reheated_mass *= fac; } if (merger_centralgal == centralgal) { EjectVmax=Gal[centralgal].Vmax; EjectVvir=Gal[centralgal].Vvir;// main halo Vvir } else { EjectVmax=Gal[merger_centralgal].InfallVmax; EjectVvir=Gal[merger_centralgal].Vvir; //central subhalo Vvir } /* determine ejection*/ if(FeedbackRecipe == 0) { ejected_mass = (FeedbackEjectionEfficiency* (EtaSNcode * EnergySNcode) *mstars * min(1./FeedbackEjectionEfficiency,.5+1/pow(EjectVmax/EjectPreVelocity,EjectSlope)) - reheated_mass*EjectVvir*EjectVvir) /(EjectVvir*EjectVvir); } else if(FeedbackRecipe == 1) { SN_Energy = .5 * mstars * (EtaSNcode * EnergySNcode); Reheat_Energy = .5 * reheated_mass * EjectVvir * EjectVvir; ejected_mass = (SN_Energy - Reheat_Energy)/(0.5 * FeedbackEjectionEfficiency*(EtaSNcode * EnergySNcode)); if(FeedbackEjectionEfficiency*(EtaSNcode * EnergySNcode)<EjectVvir*EjectVvir) ejected_mass =0.0; } // Finished calculating mass exchanges, so just check that none are negative if (reheated_mass < 0.0) reheated_mass = 0.0; if (ejected_mass < 0.0) ejected_mass = 0.0; /* update the star formation rate */ #ifdef SAVE_MEMORY Gal[merger_centralgal].Sfr += mstars / deltaT; #else for(outputbin = 0; outputbin < NOUT; outputbin++) { if(Halo[halonr].SnapNum == ListOutputSnaps[outputbin]) { Gal[merger_centralgal].Sfr[outputbin] += mstars / deltaT; break; } } #endif /* Store the value of the metallicity of the cold phase when SF occurs. * Used to update luminosities below */ #ifdef METALS //metallicitySF = Gal[merger_centralgal].MetalsColdGas.type2/Gal[merger_centralgal].ColdGas; metallicitySF = metals_total(Gal[merger_centralgal].MetalsColdGas)/Gal[merger_centralgal].ColdGas; #else metallicitySF = Gal[merger_centralgal].MetalsColdGas/Gal[merger_centralgal].ColdGas; #endif mass_checks("collisional_starburst_recipe #1",merger_centralgal); if (mstars > 0.) update_from_star_formation(merger_centralgal, mstars, deltaT/STEPS, nstep); mass_checks("collisional_starburst_recipe #2",merger_centralgal); // Do not call if Gal[merger_centralgal].ColdGas < 1e-8? if (reheated_mass + ejected_mass > 0.) update_from_feedback(merger_centralgal, centralgal, reheated_mass, ejected_mass); #ifndef POST_PROCESS_MAGS /* update the luminosities due to the stars formed */ if (mstars > 0.0) add_to_luminosities(merger_centralgal, mstars, time, metallicitySF); #endif if (Ggas > 0.) return mstars/Ggas; else return 0.0; }
/* Note: halonr is here the FOF-background subhalo (i.e. main halo) */ void evolve_galaxies(int halonr, int ngal, int treenr, int cenngal) { int p, q, nstep, centralgal, merger_centralgal, currenthalo, prevgal, start, i; double infallingGas, deltaT, Zcurr; double time, previoustime, newtime; double AGNaccreted, t_Edd; #ifdef STAR_FORMATION_HISTORY double age_in_years; #endif // Eddington time in code units // code units are UnitTime_in_s/Hubble_h t_Edd=1.42e16*Hubble_h/UnitTime_in_s; //previoustime = NumToTime(Gal[0].SnapNum); previoustime = NumToTime(Halo[halonr].SnapNum-1); newtime = NumToTime(Halo[halonr].SnapNum); /* Time between snapshots */ deltaT = previoustime - newtime; /* Redshift of current Snapnum */ Zcurr = ZZ[Halo[halonr].SnapNum]; centralgal = Gal[0].CentralGal; for (p =0;p<ngal;p++) mass_checks("Evolve_galaxies #0",p); //print_galaxy("\n\ncheck1", centralgal, halonr); if(Gal[centralgal].Type != 0 || Gal[centralgal].HaloNr != halonr) terminate("Something wrong here ..... \n"); /* Update all galaxies to same star-formation history time-bins. * Needed in case some galaxy has skipped a snapshot. */ #ifdef STAR_FORMATION_HISTORY age_in_years=(Age[0]-previoustime)*UnitTime_in_years/Hubble_h; //ROB: age_in_years is in units of "real years"! nstep=0; for (p=0; p<ngal; p++) sfh_update_bins(p,Halo[halonr].SnapNum-1,nstep,age_in_years); #endif /* Handle the transfer of mass between satellites and central galaxies */ deal_with_satellites(centralgal, ngal); /* Delete inconsequential galaxies */ for (p =0;p<ngal;p++) if (Gal[p].Type ==2 && Gal[p].ColdGas+Gal[p].DiskMass+Gal[p].BulgeMass <1.e-8) Gal[p].Type = 3; else mass_checks("Evolve_galaxies #0.1",p); /* Calculate how much hot gas needs to be accreted to give the correct baryon fraction * in the main halo. This is the universal fraction, less any reduction due to reionization. */ infallingGas = infall_recipe(centralgal, ngal, Zcurr); Gal[centralgal].PrimordialAccretionRate=infallingGas/deltaT; /* All the physics are computed in a number of intervals between snapshots * equal to STEPS */ for (nstep = 0; nstep < STEPS; nstep++) { /* time to present of the current step */ time = previoustime - (nstep + 0.5) * (deltaT / STEPS); /* Update all galaxies to the star-formation history time-bins of current step*/ #ifdef STAR_FORMATION_HISTORY age_in_years=(Age[0]-time)*UnitTime_in_years/Hubble_h; for (p=0; p<ngal; p++) sfh_update_bins(p,Halo[halonr].SnapNum-1,nstep,age_in_years); #endif /* Infall onto central galaxy only, if required to make up a baryon deficit */ #ifndef GUO10 #ifndef GUO13 if (infallingGas > 0.) #endif #endif add_infall_to_hot(centralgal, infallingGas / STEPS); mass_checks("Evolve_galaxies #0.5",centralgal); for (p = 0; p < ngal; p++) { /* don't treat galaxies that have already merged */ if(Gal[p].Type == 3) continue; mass_checks("Evolve_galaxies #1",p); if (Gal[p].Type == 0 || Gal[p].Type == 1) { reincorporate_gas(p, deltaT / STEPS); /* determine cooling gas given halo properties and add it to the cold phase*/ mass_checks("Evolve_galaxies #1.5",p); compute_cooling(p, deltaT / STEPS, ngal); } } //this must be separated as now satellite AGN can heat central galaxies //therefore the AGN from all satellites must be computed, in a loop inside this function, //before gas is cooled into central galaxies (only suppress cooling, the gas is not actually heated) if(AGNRadioModeModel != 5) do_AGN_heating(deltaT / STEPS, ngal); for (p = 0; p < ngal; p++) { cool_gas_onto_galaxy(p, deltaT / STEPS); mass_checks("Evolve_galaxies #2",p); starformation(p, centralgal, time, deltaT / STEPS, nstep); mass_checks("Evolve_galaxies #3",p); //print_galaxy("check3", centralgal, halonr); } /* Check for merger events */ for(p = 0; p < ngal; p++) { if(Gal[p].Type == 2 || (Gal[p].Type == 1 && Gal[p].MergeOn == 1)) /* satellite galaxy */ { Gal[p].MergTime -= deltaT / STEPS; if(Gal[p].MergTime < 0.0) { NumMergers++; if(Gal[p].Type == 1) for(q = 0; q < ngal; q++) if(Gal[q].Type == 2 && Gal[p].CentralGal == p) Gal[q].CentralGal = cenngal; if(Gal[p].Type == 2) merger_centralgal = Gal[p].CentralGal; else merger_centralgal = cenngal; mass_checks("Evolve_galaxies #4",p); mass_checks("Evolve_galaxies #4",merger_centralgal); mass_checks("Evolve_galaxies #4",centralgal); deal_with_galaxy_merger(p, merger_centralgal, centralgal, time, deltaT, nstep); mass_checks("Evolve_galaxies #5",p); mass_checks("Evolve_galaxies #5",merger_centralgal); mass_checks("Evolve_galaxies #5",centralgal); } } }//loop on all galaxies to detect mergers #ifdef DETAILED_METALS_AND_MASS_RETURN //DELAYED ENRICHMENT AND MASS RETURN + FEEDBACK: No fixed yield or recycling fraction anymore. FB synced with enrichment for (p = 0; p < ngal; p++) update_yields_and_return_mass(p, centralgal, deltaT/STEPS, nstep); #endif }/* end move forward in interval STEPS */ for(p = 0; p < ngal; p++) { if(Gal[p].Type == 2) { #ifndef UPDATETYPETWO int jj; float tmppos; for(jj = 0; jj < 3; jj++) { tmppos = wrap(Gal[p].DistanceToCentralGal[jj],BoxSize); tmppos *= 2.*sqrt(Gal[p].MergTime/Gal[p].OriMergTime); Gal[p].Pos[jj] = Gal[p].MergCentralPos[jj] + tmppos; if(Gal[p].Pos[jj] < 0) Gal[p].Pos[jj] = BoxSize + Gal[p].Pos[jj]; if(Gal[p].Pos[jj] > BoxSize) Gal[p].Pos[jj] = Gal[p].Pos[jj] - BoxSize; } #endif /* Disruption of type 2 galaxies. Type 1 galaxies are not disrupted since usually * bayonic component is more compact than dark matter.*/ if(DisruptionModel==0) disrupt(p); } } for (p =0;p<ngal;p++) mass_checks("Evolve_galaxies #6",p); #ifdef COMPUTE_SPECPHOT_PROPERTIES #ifndef POST_PROCESS_MAGS int n; /* If this is an output snapshot apply the dust model to each galaxy */ for(n = 0; n < NOUT; n++) { if(Halo[halonr].SnapNum == ListOutputSnaps[n]) { for(p = 0; p < ngal; p++) dust_model(p, n, halonr); break; } } #endif //POST_PROCESS_MAGS #endif //COMPUTE_SPECPHOT_PROPERTIES /* now save the galaxies of all the progenitors (and free the associated storage) */ int prog = Halo[halonr].FirstProgenitor; while(prog >= 0) { int currentgal; for(i = 0, currentgal = HaloAux[prog].FirstGalaxy; i < HaloAux[prog].NGalaxies; i++) { int nextgal = HaloGal[currentgal].NextGalaxy; /* this will write this galaxy to an output file and free the storage associate with it */ output_galaxy(treenr, HaloGal[currentgal].HeapIndex); currentgal = nextgal; } prog = Halo[prog].NextProgenitor; } for(p = 0, prevgal = -1, currenthalo = -1, centralgal = -1, start = NGalTree; p < ngal; p++) { if(Gal[p].HaloNr != currenthalo) { currenthalo = Gal[p].HaloNr; HaloAux[currenthalo].FirstGalaxy = -1; HaloAux[currenthalo].NGalaxies = 0; } mass_checks("Evolve_galaxies #7",p); if(Gal[p].Type != 3) { if(NHaloGal >= MaxHaloGal) { int oldmax = MaxHaloGal; AllocValue_MaxHaloGal *= ALLOC_INCREASE_FACTOR; MaxHaloGal = AllocValue_MaxHaloGal; if(MaxHaloGal<NHaloGal+1) MaxHaloGal=NHaloGal+1; HaloGal = myrealloc_movable(HaloGal, sizeof(struct GALAXY) * MaxHaloGal); HaloGalHeap = myrealloc_movable(HaloGalHeap, sizeof(int) * MaxHaloGal); for(i = oldmax; i < MaxHaloGal; i++) HaloGalHeap[i] = i; } Gal[p].SnapNum = Halo[currenthalo].SnapNum; #ifndef GUO10 #ifdef UPDATETYPETWO update_type_two_coordinate_and_velocity(treenr, p, Gal[0].CentralGal); #endif #endif /* when galaxies are outputed, the slot is filled with the * last galaxy in the heap. New galaxies always take the last spot */ int nextgal = HaloGalHeap[NHaloGal]; HaloGal[nextgal] = Gal[p]; HaloGal[nextgal].HeapIndex = NHaloGal; if(HaloAux[currenthalo].FirstGalaxy < 0) HaloAux[currenthalo].FirstGalaxy = nextgal; if(prevgal >= 0) HaloGal[prevgal].NextGalaxy = nextgal; prevgal = nextgal; HaloAux[currenthalo].NGalaxies++; NHaloGal++; #ifdef GALAXYTREE if(NGalTree >= MaxGalTree) { AllocValue_MaxGalTree *= ALLOC_INCREASE_FACTOR; MaxGalTree = AllocValue_MaxGalTree; if(MaxGalTree<NGalTree+1) MaxGalTree=NGalTree+1; GalTree = myrealloc_movable(GalTree, sizeof(struct galaxy_tree_data) * MaxGalTree); } HaloGal[nextgal].GalTreeIndex = NGalTree; memset(&GalTree[NGalTree], 0, sizeof(struct galaxy_tree_data)); GalTree[NGalTree].HaloGalIndex = nextgal; GalTree[NGalTree].SnapNum = Halo[currenthalo].SnapNum; GalTree[NGalTree].NextProgGal = -1; GalTree[NGalTree].DescendantGal = -1; GalTree[NGalTree].FirstProgGal = Gal[p].FirstProgGal; if(Gal[p].Type == 0) centralgal = NGalTree; NGalTree++; #endif } } #ifdef GALAXYTREE for(p = start; p < NGalTree; p++) { if(centralgal < 0) terminate("centralgal < 0"); GalTree[p].FOFCentralGal = centralgal; } #endif report_memory_usage(&HighMark, "evolve_galaxies"); }
void deal_with_galaxy_merger(int p, int merger_centralgal, int centralgal, double time, double deltaT, int nstep) { /** @brief Deals with the physics triggered by mergers, according to the mass * fraction of the merger \f$(M_{\rm{sat}}/M_{\rm{central}}><0.3)\f$. * Add the cold and stellar phase of the satellite galaxy to the central * one, form a bulge at the central galaxy with the stars from the * satellite in a minor merger if BulgeFormationInMinorMergersOn=1. * Grows black hole through accretion from cold gas "quasar mode". * If StarBurstRecipe = 0, the Somerville 2001 model * of bursts is used, SN Feedback from starformation is computed and * the sizes of bulge and disk followed. When a major merger occurs, * the disk of both merging galaxies is completely destroyed to form * a bulge. */ double mi, ma, mass_ratio, Mcstar, Mcgas, Mcbulge, Mpstar, Mpgas,Mpbulge; double frac; #ifdef GALAXYTREE int q; mass_checks("deal_with_galaxy_merger #0",p); mass_checks("deal_with_galaxy_merger #0",merger_centralgal); mass_checks("deal_with_galaxy_merger #0",centralgal); q = Gal[merger_centralgal].FirstProgGal; if(q >= 0) { while(GalTree[q].NextProgGal >= 0) q = GalTree[q].NextProgGal; GalTree[q].NextProgGal = Gal[p].FirstProgGal; if(GalTree[q].NextProgGal >= NGalTree) { printf("q=%d p=%d GalTree[q].NextProgGal=%d NGalTree=%d\n", q, p, GalTree[q].NextProgGal, NGalTree); terminate("problem"); } } if(q < 0) terminate("may not happen"); q = GalTree[q].NextProgGal; if(HaloGal[GalTree[q].HaloGalIndex].GalTreeIndex != q) terminate("inconsistency"); HaloGal[GalTree[q].HaloGalIndex].MergeOn = 2; if(Gal[p].Type == 1) HaloGal[GalTree[q].HaloGalIndex].MergeOn = 3; #endif /* flag galaxy as finished */ Gal[p].Type = 3; /* calculate mass ratio of merging galaxies */ mi = Gal[p].DiskMass+Gal[p].BulgeMass+Gal[p].ColdGas; ma = Gal[merger_centralgal].DiskMass+Gal[merger_centralgal].BulgeMass+Gal[merger_centralgal].ColdGas; if(max(mi,ma) > 0.) mass_ratio = min(mi,ma) / max(mi,ma); else mass_ratio = 1.0; /* record the gas and stellar component mass of merger central and satellite * galaxies before the before merger */ Mcstar=(Gal[merger_centralgal].DiskMass+Gal[merger_centralgal].BulgeMass); Mcbulge=Gal[merger_centralgal].BulgeMass; Mcgas=Gal[merger_centralgal].ColdGas; Mpstar=(Gal[p].DiskMass+Gal[p].BulgeMass); Mpbulge=Gal[p].BulgeMass; Mpgas=Gal[p].ColdGas; mass_checks("deal_with_galaxy_merger #1",p); mass_checks("deal_with_galaxy_merger #1",merger_centralgal); mass_checks("deal_with_galaxy_merger #1",centralgal); /* Add the cold and stellar phase of the merged galaxy to the central one. Also form a bulge if BulgeFormationInMinorMergersOn is set on, but only with the stars from the satellite. In a major merger (dealt at the burst make_bulge_from_burst) all the stars in the central and satellite end up in the central bulge. */ add_galaxies_together(merger_centralgal, p); mass_checks("deal_with_galaxy_merger #2",p); mass_checks("deal_with_galaxy_merger #2",merger_centralgal); mass_checks("deal_with_galaxy_merger #2",centralgal); /* grow black hole through accretion from cold disk during mergers, as in * Kauffmann & Haehnelt (2000) + minor mergers - Quasar Mode */ if(AGNrecipeOn > 0) grow_black_hole(merger_centralgal, mass_ratio, deltaT); mass_checks("deal_with_galaxy_merger #3",p); mass_checks("deal_with_galaxy_merger #3",merger_centralgal); mass_checks("deal_with_galaxy_merger #3",centralgal); if (StarBurstRecipe == 0) { /* Starburst as in Somerville 2001, with feedback computed inside. */ frac=collisional_starburst_recipe(mass_ratio, merger_centralgal, centralgal, time, deltaT, nstep); bulgesize_from_merger(mass_ratio,merger_centralgal,p,Mcstar,Mcbulge,Mcgas, Mpstar,Mpbulge,Mpgas,frac); mass_checks("deal_with_galaxy_merger #3.5",p); mass_checks("deal_with_galaxy_merger #3.5",merger_centralgal); mass_checks("deal_with_galaxy_merger #3.5",centralgal); if(mass_ratio > ThreshMajorMerger) make_bulge_from_burst(merger_centralgal); } mass_checks("deal_with_galaxy_merger #4",p); mass_checks("deal_with_galaxy_merger #4",merger_centralgal); mass_checks("deal_with_galaxy_merger #4",centralgal); /* If we are in the presence of a minor merger, check disk stability (the disk * is completely destroyed in major mergers)*/ if(TrackDiskInstability) if(mass_ratio < ThreshMajorMerger && (Gal[merger_centralgal].DiskMass+Gal[merger_centralgal].BulgeMass) > 0.0) check_disk_instability(merger_centralgal); /* Not supported option to shrink bulge sizes in gas rich mergers */ #ifdef SHRINKINRICHMERGER if (Mcgas+Mcstar+Mpgas+Mpstar > 1.e-8 ) { Gal[merger_centralgal].BulgeSize /= 1+pow((Mcgas+Mpgas)/(Mcgas+Mcstar+Mpgas+Mpstar)/0.15,1.); if (Gal[merger_centralgal].BulgeSize < 1.e-8) Gal[merger_centralgal].BulgeSize = 1.e-8; } #endif if ((Gal[merger_centralgal].BulgeMass > 1.e-6 && Gal[merger_centralgal].BulgeSize == 0.0) || (Gal[merger_centralgal].BulgeMass == 0.0 && Gal[merger_centralgal].BulgeSize >1.e-6)) { printf("central: stellarmass %f, bulgemass %f, bulgesize %f, coldgas %f,gasdisk %f,stellardisk %f \n", (Gal[merger_centralgal].DiskMass+Gal[merger_centralgal].BulgeMass),Gal[merger_centralgal].BulgeMass, Gal[merger_centralgal].BulgeSize,Gal[merger_centralgal].ColdGas,Gal[merger_centralgal].GasDiskRadius, Gal[merger_centralgal].StellarDiskRadius); exit(0); } if (DiskRadiusMethod == 2) { get_gas_disk_radius(merger_centralgal); get_stellar_disk_radius(merger_centralgal); } mass_checks("deal_with_galaxy_merger #5",p); mass_checks("deal_with_galaxy_merger #5",merger_centralgal); mass_checks("deal_with_galaxy_merger #5",centralgal); }
/**@brief construct_galaxies() recursively runs the semi-analytic model. * For each halo it checks if its main progenitor has been done, then * if all the halos in the FOF of its main progenitor have been * done and then runs the SAM in the current halo. This means that * for the first time its called it will walk up the tree into the * haloes in the earliest snapshot. * * When it finds a halo that needs to be done it calls * join_galaxies_of_progenitors and evolve_galaxies. */ void construct_galaxies(int filenr, int treenr, int halonr) { static int halosdone = 0; int prog, fofhalo, ngal, cenngal, p; HaloAux[halonr].DoneFlag = 1; halosdone++; prog = Halo[halonr].FirstProgenitor; while(prog >= 0) //If halo has a progenitor { if(HaloAux[prog].DoneFlag == 0) //If progenitor hasn't been done yet construct_galaxies(filenr, treenr, prog); prog = Halo[prog].NextProgenitor; //Jump to next halo in progenitors FOF } //Now check for the progenitors of all the halos in the current FOF group fofhalo = Halo[halonr].FirstHaloInFOFgroup; //Starting at the first halo in current FOF if(HaloAux[fofhalo].HaloFlag == 0) //If it hasn't been done { HaloAux[fofhalo].HaloFlag = 1; //mark as to do while(fofhalo >= 0) //go through all the halos in current FOF { prog = Halo[fofhalo].FirstProgenitor; while(prog >= 0) //build its progenitors { if(HaloAux[prog].DoneFlag == 0) construct_galaxies(filenr, treenr, prog); prog = Halo[prog].NextProgenitor; } fofhalo = Halo[fofhalo].NextHaloInFOFgroup; //Jump to next halo in FOF } } /* At this point, the galaxies for all progenitors of this halo have been * properly constructed. Also, the galaxies of the progenitors of all other * halos in the same FOF-group have been constructed as well. We can hence go * ahead and construct all galaxies for the subhalos in this FOF halo, and * evolve them in time. */ fofhalo = Halo[halonr].FirstHaloInFOFgroup; if(HaloAux[fofhalo].HaloFlag == 1) //If it is marked as an halo to do { ngal = 0; HaloAux[fofhalo].HaloFlag = 2; cenngal = set_merger_center(fofhalo); //Find type 0 for type 1 to merge into /*For all the halos in the current FOF join all the progenitor galaxies together * ngals will be the total number of galaxies in the current FOF*/ while(fofhalo >= 0) { ngal = join_galaxies_of_progenitors(fofhalo, ngal, &cenngal); fofhalo = Halo[fofhalo].NextHaloInFOFgroup; } /*Evolve the Galaxies -> SAM! */ evolve_galaxies(Halo[halonr].FirstHaloInFOFgroup, ngal, treenr, cenngal); for (p =0;p<ngal;p++) mass_checks("Construct_galaxies #1",p); } }
/**@brief join_galaxies_of_progenitors() updates the properties of the * galaxy from the dark matter halo properties and deals with * merging clocks. This routine is called by construct_galaxies * for every halo in the FOF being constructed. When there is no * galaxy in the Halo of FirstProgenitor, the first_occupied * pointer is changed to a subhalo which have the maximum mass. * * For a central galaxy it just updates its properties. For * satellites it needs to know its most massive (or only progenitor) * to keep track of the merging clock. It also finds the central * galaxies into which galaxies should merge. Type 1's * can merge if their baryonic mass is bigger than the dark matter * mass and type 2's can merge into them. Once the type 1's merge * into a type 0 all its satellites will have the merging clock * into the type 0 reset . * */ int join_galaxies_of_progenitors(int halonr, int ngalstart, int *cenngal) { int ngal, prog, i, j, first_occupied, lenmax, centralgal, mostmassive; lenmax = 0; first_occupied = Halo[halonr].FirstProgenitor; prog = Halo[halonr].FirstProgenitor; /* When there is no galaxy in the Halo of FirstProgenitor, the first_occupied * pointer is changed to a subhalo which have the maximum mass (This should * only happen in the case that the leaf on the firstprogenitor branch occurs * as a subhalo, in that case no galaxy would be assigned to it). */ if(prog >= 0) //If halo has progenitors { if(HaloAux[prog].NGalaxies == 0) //if progenitor has no galaxies while(prog >= 0) { int currentgal; for(i = 0, currentgal = HaloAux[prog].FirstGalaxy; i < HaloAux[prog].NGalaxies; i++) { if(HaloGal[currentgal].Type == 0 || HaloGal[currentgal].Type == 1) { if(Halo[prog].Len > lenmax) { lenmax = Halo[prog].Len; first_occupied = prog; //define the new first_occupied } } currentgal = HaloGal[currentgal].NextGalaxy; } prog = Halo[prog].NextProgenitor; } } lenmax = 0; prog = Halo[halonr].FirstProgenitor; mostmassive = Halo[halonr].FirstProgenitor; /* loop through all the progenitors and get the halo mass and ID * of the most massive*/ while(prog >= 0) { if(Halo[prog].Len > lenmax) { lenmax = Halo[prog].Len; mostmassive = prog; } prog = Halo[prog].NextProgenitor; } ngal = ngalstart; prog = Halo[halonr].FirstProgenitor; while(prog >= 0) { int currentgal; for(i = 0, currentgal = HaloAux[prog].FirstGalaxy; i < HaloAux[prog].NGalaxies; i++) { if(ngal >= MaxGal) { AllocValue_MaxGal *= ALLOC_INCREASE_FACTOR; MaxGal = AllocValue_MaxGal; if(MaxGal<ngal+1) MaxGal=ngal+1; Gal = myrealloc_movable(Gal, sizeof(struct GALAXY) * MaxGal); } if(*cenngal==currentgal) *cenngal=ngal; /* Copy galaxy properties from progenitor, * except for those that need initialising */ Gal[ngal] = HaloGal[currentgal]; Gal[ngal].HaloNr = halonr; Gal[ngal].CoolingRadius = 0.0; Gal[ngal].CoolingGas = 0.0; Gal[ngal].PrimordialAccretionRate = 0.0; Gal[ngal].CoolingRate = 0.0; Gal[ngal].CoolingRate_beforeAGN = 0.0; Gal[ngal].Sfr = 0.0; Gal[ngal].SfrBulge = 0.0; Gal[ngal].QuasarAccretionRate=0.0; Gal[ngal].RadioAccretionRate=0.0; #ifdef GALAXYTREE Gal[ngal].FirstProgGal = HaloGal[currentgal].GalTreeIndex; /* CHECK */ #endif // To fail this check means that we copy in a failed galaxy mass_checks("Middle of join_galaxies_of_progenitors",ngal); /* Update Properties of this galaxy with physical properties of halo */ /* this deals with the central galaxies of subhalos */ if(Gal[ngal].Type == 0 || Gal[ngal].Type == 1) { if(prog == first_occupied) { #ifdef HALOPROPERTIES Gal[ngal].HaloM_Mean200 = Halo[halonr].M_Mean200; Gal[ngal].HaloM_Crit200 = Halo[halonr].M_Crit200; Gal[ngal].HaloM_TopHat = Halo[halonr].M_TopHat; Gal[ngal].HaloVelDisp = Halo[halonr].VelDisp; Gal[ngal].HaloVmax = Halo[halonr].Vmax; #endif Gal[ngal].MostBoundID = Halo[halonr].MostBoundID; for(j = 0; j < 3; j++) { Gal[ngal].Pos[j] = Halo[halonr].Pos[j]; Gal[ngal].Vel[j] = Halo[halonr].Vel[j]; #ifdef HALOPROPERTIES Gal[ngal].HaloPos[j] = Halo[halonr].Pos[j]; Gal[ngal].HaloVel[j] = Halo[halonr].Vel[j]; #endif } Gal[ngal].Len = Halo[halonr].Len; // FOFCentralGal property in case that is different from FirstGalaxy if(halonr == Halo[halonr].FirstHaloInFOFgroup) update_centralgal(ngal, halonr); else update_type_1(ngal, halonr, prog); if(DiskRadiusModel == 1 || DiskRadiusModel == 2) { Gal[ngal].GasDiskRadius = get_disk_radius(halonr, ngal); Gal[ngal].StellarDiskRadius = Gal[ngal].GasDiskRadius; } Gal[ngal].Vmax = Halo[halonr].Vmax; } else //type 2 galaxies { update_type_2(ngal, halonr, prog, mostmassive); } } /* Note: Galaxies that are already type=2 do not need a special treatment at this point */ if(Gal[ngal].Type < 0 || Gal[ngal].Type > 2) terminate("Unknown galaxy type\n"); ngal++; currentgal = HaloGal[currentgal].NextGalaxy; } prog = Halo[prog].NextProgenitor; } /* If there are no progenitors with galaxies, a new galaxy is created. * However, if it's a subhalo, no galaxy is placed, since it would stay * at zero luminosity. */ if(ngal == 0) { *cenngal=0; if(Halo[halonr].FirstHaloInFOFgroup == halonr) { init_galaxy(ngal, halonr); ngal++; } } /* satelites (type 2's) will preferably merge onto this type 1 rather than the type 0 */ for(i = ngalstart, centralgal = -1; i < ngal; i++) if(Gal[i].Type == 0 || Gal[i].Type == 1) { if(centralgal != -1) terminate("Subhalo hosts more than one Type 0/1\n"); centralgal = i; } for(i = ngalstart; i < ngal; i++) { Gal[i].CentralGal = centralgal; if(centralgal != -1) for(j = 0; j < 3; j++) Gal[i].MergCentralPos[j] = Gal[centralgal].Pos[j]; } /* Satellites whose type 1 has merged into type 0, will be reset to merge * into the type 0. */ if(centralgal == -1 && ngal != ngalstart) { for(i = ngalstart; i < ngal; i++) { Gal[i].CentralGal = *cenngal; for(j = 0; j < 3; j++) Gal[i].MergCentralPos[j] = Gal[*cenngal].Pos[j]; } } for (i = ngalstart; i<ngal; i++) mass_checks("Bottom of join_galaxies_of_progenitors",i); report_memory_usage(&HighMark, "join_galaxies"); return ngal; }
void disrupt(int p, int centralgal) { double rho_sat, rho_cen; double cen_mass, r_sat, radius; int outputbin, j, q; /* If the main halo density at the pericentre (closest point in the orbit * to the central galaxy)is larger than the satellite's density at the * half mass radius, the satellite is completely disrupted. Note that since * the satellite is a type 2 the only mass components remaining and * contributing to the density are the cold gas and stellar mass. */ //TODO If we are passing in centralgal then we should not set it here centralgal=Gal[p].CentralGal; mass_checks("Top of disrupt",centralgal); mass_checks("Top of disrupt",p); /* Radius calculated at the peri-point */ radius=peri_radius(p, centralgal); if (radius < 0) { terminate("must be wrong \n"); } /* Calculate the density of the main central halo at radius (the peri-centre). * The tidal forces are caused by the dark matter of the main halo, hence Mvir * is used. */ cen_mass=Gal[centralgal].Mvir*radius/Gal[centralgal].Rvir; rho_cen=cen_mass/pow3(radius); /* Calculate the density of the satellite's baryonic material */ if (Gal[p].DiskMass+Gal[p].BulgeMass>0) { /* Calculate the rho according to the real geometry */ r_sat = sat_radius(p); /* Or use radius at the mean radius of the stellar mass */ /* r_sat=(Gal[p].BulgeMass*Gal[p].BulgeSize+(Gal[p].StellarMass-Gal[p].BulgeMass) * *Gal[p].StellarDiskRadius/3*1.68) * /(Gal[p].StellarMass); */ /* to calculate the density */ //rho_sat=Gal[p].StellarMass/pow2(r_sat); rho_sat=(Gal[p].DiskMass+Gal[p].BulgeMass+Gal[p].ColdGas)/pow3(r_sat); } else rho_sat=0.0; /* If density of the main halo is larger than that of the satellite baryonic * component, complete and instantaneous disruption is assumed. Galaxy becomes * a type 3 and all its material is transferred to the central galaxy. */ if (rho_cen > rho_sat) { Gal[p].Type = 3; #ifdef GALAXYTREE q = Gal[Gal[p].CentralGal].FirstProgGal; if (q >= 0) { // add progenitors of Gal[p] to the list of progentitors of Gal[p].CentralGal while (GalTree[q].NextProgGal >= 0) q = GalTree[q].NextProgGal; GalTree[q].NextProgGal = Gal[p].FirstProgGal; if(GalTree[q].NextProgGal >= NGalTree) { printf("q=%d p=%d GalTree[q].NextProgGal=%d NGalTree=%d\n", q, p, GalTree[q].NextProgGal, NGalTree); terminate("problem"); } } if(q < 0) terminate("this shouldn't happen"); // TODO if !(q>=0) shouldn't we set // Gal[Gal[p].CentralGal].FirstProgGal to Gal[p].FirstProgGal ?? q = GalTree[q].NextProgGal; if(q < 0) terminate("inconsistency"); if(HaloGal[GalTree[q].HaloGalIndex].GalTreeIndex != q) terminate("inconsistency"); HaloGal[GalTree[q].HaloGalIndex].DisruptOn = 1; #endif /* Put gas component to the central galaxy hot gas and stellar material into the ICM. * Note that the staellite should have no extended components. */ // TODO Shouldn't the stars end up in the bulge? - this is a close merger transfer_gas(centralgal,"Hot",p,"Cold",1.); transfer_gas(centralgal,"Hot",p,"Hot",1.); transfer_stars(centralgal,"ICM",p,"Disk",1.); transfer_stars(centralgal,"ICM",p,"Bulge",1.); /* Add satellite's luminosity into the luminosity of the ICL * component of the central galaxy. */ #ifdef ICL for(outputbin = 0; outputbin < NOUT; outputbin++) { for(j = 0; j < NMAG; j++) { #ifdef OUTPUT_REST_MAGS Gal[centralgal].ICLLum[j][outputbin] += Gal[p].Lum[j][outputbin]; #endif #ifdef COMPUTE_OBS_MAGS Gal[centralgal].ObsICL[j][outputbin] += Gal[p].ObsLum[j][outputbin]; #endif } } #endif } mass_checks("Bottom of disrupt",centralgal); mass_checks("Bottom of disrupt",p); }
/* Note: halonr is here the FOF-background subhalo (i.e. main halo) */ void evolve_galaxies(int halonr, int ngal, int treenr, int cenngal) { int jj, p, q, nstep, centralgal, merger_centralgal, currenthalo, prevgal, start, i; double infallingGas, coolingGas, deltaT, Zcurr; double time, previoustime, newtime; double AGNaccreted, t_Edd; #ifdef STAR_FORMATION_HISTORY double age_in_years; #endif #ifdef HT09_DISRUPTION double CentralRadius, CentralMass, SatelliteRadius, SatelliteMass; #endif // Eddington time in code units // Bizarrely, code units are UnitTime_in_s/Hubble_h t_Edd=1.42e16*Hubble_h/UnitTime_in_s; //previoustime = NumToTime(Gal[0].SnapNum); previoustime = NumToTime(Halo[halonr].SnapNum-1); newtime = NumToTime(Halo[halonr].SnapNum); /* Time between snapshots */ deltaT = previoustime - newtime; /* Redshift of current Snapnum */ Zcurr = ZZ[Halo[halonr].SnapNum]; //if(halonr == 83) // for(p=0;p<ngal;p++) // printf("check halonr=%d id=%d type=%d\n", halonr, p,Gal[p].Type); centralgal = Gal[0].CentralGal; for (p =0;p<ngal;p++) mass_checks("Evolve_galaxies #0",p); if(Gal[centralgal].Type != 0 || Gal[centralgal].HaloNr != halonr) terminate("Something wrong here ..... \n"); /* Update all galaxies to same star-formation history time-bins. * Needed in case some galaxy has skipped a snapshot. */ #ifdef STAR_FORMATION_HISTORY age_in_years=(Age[0]-previoustime)*UnitTime_in_years/Hubble_h; //ROB: age_in_years is in units of "real years"! nstep=0; for (p=0; p<ngal; p++) sfh_update_bins(p,Halo[halonr].SnapNum-1,nstep,age_in_years); #endif //if(halonr == 84) // print_galaxy("check00", centralgal, halonr); //for(p=0;p<ngal;p++) // printf("prog=%d\n",Halo[halonr].FirstProgenitor); /* Handle the transfer of mass between satellites and central galaxies */ deal_with_satellites(centralgal, ngal); /* Delete inconsequential galaxies */ for (p =0;p<ngal;p++) if (Gal[p].Type ==2 && Gal[p].ColdGas+Gal[p].DiskMass+Gal[p].BulgeMass <1.e-8) Gal[p].Type = 3; else mass_checks("Evolve_galaxies #0.1",p); /* Calculate how much hot gas needs to be accreted to give the correct baryon fraction * in the main halo. This is the universal fraction, less any reduction due to reionization. */ infallingGas = infall_recipe(centralgal, ngal, Zcurr); Gal[centralgal].PrimordialAccretionRate=infallingGas/deltaT; //if(halonr > 35 && halonr < 40) // print_galaxy("check02", centralgal, halonr); /* All the physics are computed in a number of intervals between snapshots * equal to STEPS */ for (nstep = 0; nstep < STEPS; nstep++) { //printf("step=%d\n",nstep); /* time to present of the current step */ time = previoustime - (nstep + 0.5) * (deltaT / STEPS); /* Update all galaxies to the star-formation history time-bins of current step*/ #ifdef STAR_FORMATION_HISTORY age_in_years=(Age[0]-time)*UnitTime_in_years/Hubble_h; for (p=0; p<ngal; p++) sfh_update_bins(p,Halo[halonr].SnapNum-1,nstep,age_in_years); #endif //if(halonr > 35 && halonr < 40) // print_galaxy("check02.1", centralgal, halonr); /* Infall onto central galaxy only, if required to make up a baryon deficit */ #ifndef GUO13 #ifndef GUO10 if (infallingGas > 0.) #endif #endif add_infall_to_hot(centralgal, infallingGas / STEPS); //if(halonr == 84) // print_galaxy("check02.5", centralgal, halonr); mass_checks("Evolve_galaxies #0.5",centralgal); for (p = 0; p < ngal; p++) { //if((halonr > 28 && halonr < 32) || Gal[p].HaloNr==52) //if(Gal[p].SnapNum==31 || (halonr > 28 && halonr < 31)) // if(halonr ==140) // print_galaxy("check03", p, halonr); /* don't treat galaxies that have already merged */ if(Gal[p].Type == 3) continue; mass_checks("Evolve_galaxies #1",p); if (Gal[p].Type == 0 || Gal[p].Type == 1) { if((ReIncorporationRecipe == 0 && Gal[p].Type==0) || ReIncorporationRecipe > 0) reincorporate_gas(p, deltaT / STEPS); //if(halonr > 28 && halonr < 31) // print_galaxy("check04", p, halonr); /* determine cooling gas given halo properties and add it to the cold phase*/ mass_checks("Evolve_galaxies #1.5",p); coolingGas = cooling_recipe(p, deltaT / STEPS); cool_gas_onto_galaxy(p, coolingGas); //if(halonr > 28 && halonr < 31) //if(halonr ==140) //print_galaxy("check05", p, halonr); } mass_checks("Evolve_galaxies #2",p); #ifdef H2_AND_RINGS gas_inflow(p, deltaT / STEPS); //if(halonr > 38 && halonr < 40) //print_galaxy("check06", p, halonr); #endif /* stars form*/ starformation(p, centralgal, time, deltaT / STEPS, nstep); //int ii; //for (ii = 0; ii < ngal; ii++) // if(halonr > 28 && halonr < 31) //if(halonr ==140) // print_galaxy("check07", ii, halonr); mass_checks("Evolve_galaxies #3",p); } //for (p = 0; p < ngal; p++) /* Check for merger events */ //if(Gal[p].Type == 1) //for(p = 0; p < -1; p++) for(p = 0; p < ngal; p++) { //if(halonr == 84) // print_galaxy("check07.01", p, halonr); #ifdef MERGE01 if(Gal[p].Type == 2 || (Gal[p].Type == 1 && Gal[p].MergeOn == 1)) /* satellite galaxy */ #else if(Gal[p].Type == 2) #endif { Gal[p].MergTime -= deltaT / STEPS; #ifdef HT09_DISRUPTION Gal[p].MergRadius -= get_deltar(p, deltaT/STEPS ); if(Gal[p].MergRadius<0.) Gal[p].MergRadius=0.; //printf("merge radius=%f detlar=%f\n",Gal[p].MergRadius, 100.*get_deltar(p, deltaT/STEPS )); disruption_code (p, time); /* a merger has occured! */ //MergRadius is tracked for type 2's subject to disruption while MergTime is tracked for type 1's // if( ( Gal[p].Type == 2 && (Gal[p].MergRadius < Gal[centralgal].StellarDiskRadius+Gal[centralgal].BulgeSize || Gal[p].BulgeMass+Gal[p].DiskMass == 0) ) // || (Gal[p].Type == 1 && Gal[p].MergTime < 0.0)) if( Gal[p].MergRadius < Gal[centralgal].StellarDiskRadius+Gal[centralgal].BulgeSize || Gal[p].BulgeMass+Gal[p].DiskMass == 0 ) //if(Gal[p].MergTime < 0.0 || Gal[p].BulgeMass+Gal[p].DiskMass == 0) #else if(Gal[p].MergTime < 0.0) #endif { NumMergers++; #ifdef MERGE01 if(Gal[p].Type == 1) for(q = 0; q < ngal; q++) if(Gal[q].Type == 2 && Gal[p].CentralGal == p) Gal[q].CentralGal = cenngal; if(Gal[p].Type == 2) merger_centralgal = Gal[p].CentralGal; else merger_centralgal = cenngal; #else merger_centralgal = Gal[p].CentralGal; #endif mass_checks("Evolve_galaxies #4",p); mass_checks("Evolve_galaxies #4",merger_centralgal); mass_checks("Evolve_galaxies #4",centralgal); //if(halonr == 140) //print_galaxy("check08", p, halonr); //if(halonr == 140) //print_galaxy("check09", merger_centralgal, halonr); deal_with_galaxy_merger(p, merger_centralgal, centralgal, time, deltaT, nstep); //if(halonr == 140) //print_galaxy("check10", p, halonr); //if(halonr == 140) //print_galaxy("check11", merger_centralgal, halonr); mass_checks("Evolve_galaxies #5",p); mass_checks("Evolve_galaxies #5",merger_centralgal); mass_checks("Evolve_galaxies #5",centralgal); } }// if(Gal[p].Type == 2) }//loop on all galaxies to detect mergers /* Cool gas onto AGN */ if (BlackHoleGrowth == 1) { for (p = 0; p < ngal; p++) { AGNaccreted=min(Gal[p].BlackHoleGas, Gal[p].BlackHoleMass*BlackHoleAccretionRate*deltaT/(STEPS*t_Edd)); if (AGNaccreted > 0.) { Gal[p].BlackHoleMass += AGNaccreted; Gal[p].BlackHoleGas -= AGNaccreted; // Instantaneous accretion rate. This will get overwritten on each mini-step but that's OK Gal[p].QuasarAccretionRate = AGNaccreted*STEPS/deltaT; } } } //DELAYED ENRICHMENT AND MASS RETURN + FEEDBACK: No fixed yield or recycling fraction anymore. FB synced with enrichment for (p = 0; p < ngal; p++) { #ifdef DETAILED_METALS_AND_MASS_RETURN update_yields_and_return_mass(p, centralgal, deltaT/STEPS, nstep); #endif } #ifdef ALL_SKY_LIGHTCONE int nr, istep, ix, iy, iz; istep = Halo[halonr].SnapNum*STEPS + nstep; Gal[p].SnapNum = Halo[halonr].SnapNum; for (p = 0; p < ngal; p++) for (nr = 0; nr < NCONES; nr++) for (ix = 0; ix < NREPLICA; ix++) for (iy = 0; iy < NREPLICA; iy++) for (iz = 0; iz < NREPLICA; iz++) inside_lightcone(p, istep, nr, ix, iy, iz); #endif }/* end move forward in interval STEPS */ /* check the bulge size*/ //checkbulgesize_main(ngal); for(p = 0; p < ngal; p++) { if(Gal[p].Type == 2) { //if(halonr == 140) //print_galaxy("check12", p, halonr); /*#ifdef UPDATETYPETWO update_type_two_coordinate_and_velocity(treenr, p, centralgal); #else*/ #ifndef UPDATETYPETWO int jj; float tmppos; for(jj = 0; jj < 3; jj++) { tmppos = wrap(Gal[p].DistanceToCentralGal[jj],BoxSize); tmppos *= (Gal[p].MergTime/Gal[p].OriMergTime); Gal[p].Pos[jj] = Gal[p].MergCentralPos[jj] + tmppos; if(Gal[p].Pos[jj] < 0) Gal[p].Pos[jj] = BoxSize + Gal[p].Pos[jj]; if(Gal[p].Pos[jj] > BoxSize) Gal[p].Pos[jj] = Gal[p].Pos[jj] - BoxSize; } #endif /* Disruption of type 2 galaxies. Type 1 galaxies are not disrupted since usually * bayonic component is more compact than dark matter.*/ #ifdef DISRUPTION //if(halonr == 84) //print_galaxy("check13", p, halonr); disrupt(p, Gal[p].CentralGal); //if(halonr == 84) //print_galaxy("check014", p, halonr); #endif } //if(halonr > 20 && halonr < 31) //if(halonr ==140) // print_galaxy("check015", p, halonr); } for (p =0;p<ngal;p++) mass_checks("Evolve_galaxies #6",p); #ifdef COMPUTE_SPECPHOT_PROPERTIES #ifndef POST_PROCESS_MAGS int n; /* If this is an output snapshot apply the dust model to each galaxy */ for(n = 0; n < NOUT; n++) { if(Halo[halonr].SnapNum == ListOutputSnaps[n]) { for(p = 0; p < ngal; p++) dust_model(p, n, halonr); break; } } #endif //POST_PROCESS_MAGS #endif //COMPUTE_SPECPHOT_PROPERTIES /* now save the galaxies of all the progenitors (and free the associated storage) */ int prog = Halo[halonr].FirstProgenitor; while(prog >= 0) { int currentgal; for(i = 0, currentgal = HaloAux[prog].FirstGalaxy; i < HaloAux[prog].NGalaxies; i++) { int nextgal = HaloGal[currentgal].NextGalaxy; /* this will write this galaxy to an output file and free the storage associate with it */ output_galaxy(treenr, HaloGal[currentgal].HeapIndex); currentgal = nextgal; } prog = Halo[prog].NextProgenitor; } for(p = 0, prevgal = -1, currenthalo = -1, centralgal = -1, start = NGalTree; p < ngal; p++) { if(Gal[p].HaloNr != currenthalo) { currenthalo = Gal[p].HaloNr; HaloAux[currenthalo].FirstGalaxy = -1; HaloAux[currenthalo].NGalaxies = 0; } mass_checks("Evolve_galaxies #7",p); /* may be wrong (what/why?) */ if(Gal[p].Type != 3) { if(NHaloGal >= MaxHaloGal) { int oldmax = MaxHaloGal; AllocValue_MaxHaloGal *= ALLOC_INCREASE_FACTOR; MaxHaloGal = AllocValue_MaxHaloGal; if(MaxHaloGal<NHaloGal+1) MaxHaloGal=NHaloGal+1; HaloGal = myrealloc_movable(HaloGal, sizeof(struct GALAXY) * MaxHaloGal); HaloGalHeap = myrealloc_movable(HaloGalHeap, sizeof(int) * MaxHaloGal); for(i = oldmax; i < MaxHaloGal; i++) HaloGalHeap[i] = i; } Gal[p].SnapNum = Halo[currenthalo].SnapNum; #ifndef GUO10 #ifdef UPDATETYPETWO update_type_two_coordinate_and_velocity(treenr, p, Gal[0].CentralGal); #endif #endif /* when galaxies are outputed, the slot is filled with the * last galaxy in the heap. New galaxies always take the last spot */ int nextgal = HaloGalHeap[NHaloGal]; HaloGal[nextgal] = Gal[p]; HaloGal[nextgal].HeapIndex = NHaloGal; if(HaloAux[currenthalo].FirstGalaxy < 0) HaloAux[currenthalo].FirstGalaxy = nextgal; if(prevgal >= 0) HaloGal[prevgal].NextGalaxy = nextgal; prevgal = nextgal; HaloAux[currenthalo].NGalaxies++; NHaloGal++; #ifdef GALAXYTREE if(NGalTree >= MaxGalTree) { AllocValue_MaxGalTree *= ALLOC_INCREASE_FACTOR; MaxGalTree = AllocValue_MaxGalTree; if(MaxGalTree<NGalTree+1) MaxGalTree=NGalTree+1; GalTree = myrealloc_movable(GalTree, sizeof(struct galaxy_tree_data) * MaxGalTree); } HaloGal[nextgal].GalTreeIndex = NGalTree; memset(&GalTree[NGalTree], 0, sizeof(struct galaxy_tree_data)); GalTree[NGalTree].HaloGalIndex = nextgal; GalTree[NGalTree].SnapNum = Halo[currenthalo].SnapNum; GalTree[NGalTree].NextProgGal = -1; GalTree[NGalTree].DescendantGal = -1; GalTree[NGalTree].FirstProgGal = Gal[p].FirstProgGal; if(Gal[p].Type == 0) centralgal = NGalTree; NGalTree++; #endif } } #ifdef GALAXYTREE for(p = start; p < NGalTree; p++) { if(centralgal < 0) terminate("centralgal < 0"); GalTree[p].FOFCentralGal = centralgal; } #endif report_memory_usage(&HighMark, "evolve_galaxies"); }
/** @brief Updates cold, hot and external gas components due to SN * reheating and ejection. */ void update_from_feedback(int p, int centralgal, double reheated_mass, double ejected_mass) { double dis=0.; double massremain; double fraction; int merger_centre; //mass_checks("update_from_feedback #1",p); if(Gal[p].ColdGas > 0.) { //REHEAT // if galaxy is a type 1 or a type 2 orbiting a type 1 with hot gas being striped, //some of the reheated and ejected masses goes to the type 0 and some stays in the type 1 if(Gal[p].Type ==0) { transfer_gas(p,"Hot",p,"Cold",((float)reheated_mass)/Gal[p].ColdGas,"update_from_feedback", __LINE__); } else if(Gal[p].Type <3) { if(Gal[p].Type ==1) merger_centre=centralgal; else if(Gal[p].Type ==2) merger_centre=Gal[p].CentralGal; dis=separation_gal(centralgal,Gal[p].CentralGal)/(1+ZZ[Halo[Gal[centralgal].HaloNr].SnapNum]); //compute share of reheated mass if (dis<Gal[centralgal].Rvir && Gal[Gal[p].CentralGal].Type == 1) { //mass that remains on type1 (the rest goes to type 0) for reheat - massremain, for eject - ejected mass massremain=reheated_mass*Gal[p].HotRadius/Gal[p].Rvir; ejected_mass = ejected_mass*Gal[p].HotRadius/Gal[p].Rvir; if (massremain > reheated_mass) massremain = reheated_mass; } else massremain=reheated_mass; //needed due to precision issues, since we first remove massremain and then (reheated_mass-massremain) //from the satellite into the type 0 and type 1 the fraction might not add up on the second call //since Gal[p].ColdGas is a float and reheated_mass & massremain are doubles if((massremain + reheated_mass)>Gal[p].ColdGas) massremain=Gal[p].ColdGas-reheated_mass; //transfer massremain transfer_gas(Gal[p].CentralGal,"Hot",p,"Cold",massremain/Gal[p].ColdGas,"update_from_feedback", __LINE__); //transfer reheated_mass-massremain from galaxy to the type 0 if (reheated_mass > massremain) if(Gal[p].ColdGas > 0.) //if the reheat to itself, left cold gas below limit do not reheat to central transfer_gas(centralgal,"Hot",p,"Cold",(reheated_mass-massremain)/Gal[p].ColdGas,"update_from_feedback", __LINE__); }//types }//if(Gal[p].ColdGas > 0.) mass_checks("update_from_feedback #2",p); //DO EJECTION OF GAS if (Gal[Gal[p].CentralGal].HotGas > 0.) { if (ejected_mass > Gal[Gal[p].CentralGal].HotGas) ejected_mass = Gal[Gal[p].CentralGal].HotGas; //either eject own gas or merger_centre gas for ttype 2's fraction=((float)ejected_mass)/Gal[Gal[p].CentralGal].HotGas; if (Gal[Gal[p].CentralGal].Type == 1) { /* If type 1, or type 2 orbiting type 1 near type 0 */ if (FateOfSatellitesGas == 0) transfer_gas(Gal[p].CentralGal,"Ejected",Gal[p].CentralGal,"Hot",fraction,"update_from_feedback", __LINE__); else if (FateOfSatellitesGas == 1) { if (dis < Gal[centralgal].Rvir) transfer_gas(centralgal,"Hot",Gal[p].CentralGal,"Hot",fraction,"update_from_feedback", __LINE__); else transfer_gas(Gal[p].CentralGal,"Ejected",Gal[p].CentralGal,"Hot",fraction,"update_from_feedback", __LINE__); } } else // If galaxy type 0 or type 2 merging into type 0 transfer_gas(centralgal,"Ejected",Gal[p].CentralGal,"Hot",fraction,"update_from_feedback", __LINE__); }//(Gal[Gal[p].CentralGal].HotGas > 0.) }
/** @brief Main recipe, calculates the fraction of cold gas turned into stars due * to star formation; the fraction of mass instantaneously recycled and * returned to the cold gas; the fraction of gas reheated from cold to hot, * ejected from hot to external and returned from ejected to hot due to * SN feedback. */ void starformation(int p, int centralgal, double time, double dt, int nstep) { /*! Variables: reff-Rdisk, tdyn=Rdisk/Vmax, strdot=Mstar_dot, stars=strdot*dt*/ double tdyn, strdot=0., stars, cold_crit, metallicitySF; if(Gal[p].Type == 0) { tdyn = Gal[p].GasDiskRadius / Gal[p].Vmax; cold_crit = SfrColdCrit * Gal[p].Vmax/200. * Gal[p].GasDiskRadius*100.; } else { tdyn = Gal[p].GasDiskRadius / Gal[p].InfallVmax; cold_crit = SfrColdCrit * Gal[p].InfallVmax/200. * Gal[p].GasDiskRadius*100.; } //standard star formation law (Croton2006, Delucia2007, Guo2010) if(StarFormationModel == 0) { if(Gal[p].ColdGas > cold_crit) strdot = SfrEfficiency * (Gal[p].ColdGas - cold_crit) / tdyn; else strdot = 0.0; } /*if(StarFormationModel == 1) { strdot = ALTERNATIVE STAR FORMATION LAW }*/ /* Note that Units of dynamical time are Mpc/Km/s - no conversion on dt needed */ stars = strdot * dt; if(stars < 0.0) terminate("***error stars<0.0***\n"); //otherwise cold gas and stars share material in update_stars_due_to_reheat #ifdef FEEDBACK_COUPLED_WITH_MASS_RETURN if(stars > Gal[p].ColdGas) stars = Gal[p].ColdGas; #endif mass_checks("recipe_starform #1",p); mass_checks("recipe_starform #1.1",centralgal); /* update for star formation * updates Mcold, StellarMass, MetalsMcold and MetalsStellarMass * in Guo2010 case updates the stellar spin -> hardwired, not an option */ /* Store the value of the metallicity of the cold phase when SF occurs */ if (Gal[p].ColdGas > 0.) metallicitySF= metals_total(Gal[p].MetalsColdGas)/Gal[p].ColdGas; else metallicitySF=0.; if (stars > 0.) update_stars_due_to_reheat(p, centralgal, &stars); mass_checks("recipe_starform #2",p); mass_checks("recipe_starform #2.1",centralgal); /* update the star formation rate */ /*Sfr=stars/(dt*steps)=strdot*dt/(dt*steps)=strdot/steps -> average over the STEPS*/ Gal[p].Sfr += stars / (dt * STEPS); // update_from_star_formation can only be called // after SD_feeedback recipe since stars need to be re_set once the reheated mass is known // (star formation and feedback share the same fraction of cold gas) if (stars > 0.) update_from_star_formation(p, stars, false, nstep); // false indicates not a burst update_massweightage(p, stars, time); #ifndef FEEDBACK_COUPLED_WITH_MASS_RETURN /* ifdef FEEDBACK_COUPLED_WITH_MASS_RETURN feedback is only called * when stars die, inside DETAILED_METALS_AND_MASS_RETURN */ if (stars > 0.) SN_feedback(p, centralgal, stars, "ColdGas"); #endif #ifdef COMPUTE_SPECPHOT_PROPERTIES #ifndef POST_PROCESS_MAGS /* Update the luminosities due to the stars formed */ if (stars > 0.0) add_to_luminosities(p, stars, time, dt, metallicitySF); #endif //NDEF POST_PROCESS_MAGS #endif //COMPUTE_SPECPHOT_PROPERTIES if(Gal[p].DiskMass > 0.0) check_disk_instability(p); if (DiskRadiusModel== 0) get_stellar_disk_radius(p); }
/* there are two modes for supernova feedback corresponding to when the mass returning * by dying stars is returned to the cold gas - reheat and ejection; and when the mass * is returned to the hot gas - onle ejection.*/ void SN_feedback(int p, int centralgal, double stars, char feedback_location[]) { double CentralVvir, MergeCentralVvir=0., EjectVmax, EjectVvir, SN_Energy, Reheat_Energy, fac; double reheated_mass=0., ejected_mass=0.; /* SN FEEDBACK MODEL */ /* In Guo2010 type 1s can eject, reincorporate gas and get gas from their * own satellites (is not sent to the type 0 galaxy as in Delucia2007), * for gas flow computations: * If satellite is inside Rvir of main halo, Vvir of main halo used * If it is outside, the Vvir of its central subhalo is used. */ if (strcmp(feedback_location,"HotGas")==0) reheated_mass = 0.; else if (strcmp(feedback_location,"ColdGas")==0) { CentralVvir = Gal[centralgal].Vvir; // main halo Vvir MergeCentralVvir = Gal[Gal[p].CentralGal].Vvir; //central subhalo Vvir mass_checks("recipe_starform #0",p); mass_checks("recipe_starform #0.1",centralgal); // Feedback depends on the circular velocity of the host halo // Guo2010 - eq 18 & 19 if(FeedbackReheatingModel == 0) { if (Gal[Gal[p].CentralGal].Type == 0) reheated_mass = FeedbackReheatingEpsilon * stars * (.5+1./pow(Gal[Gal[p].CentralGal].Vmax/ReheatPreVelocity,ReheatSlope)); else reheated_mass = FeedbackReheatingEpsilon * stars * (.5+1./pow(Gal[Gal[p].CentralGal].InfallVmax/ReheatPreVelocity,ReheatSlope)); if (reheated_mass * Gal[Gal[p].CentralGal].Vvir * Gal[Gal[p].CentralGal].Vvir > stars * (EtaSNcode * EnergySNcode)) reheated_mass = stars * (EtaSNcode * EnergySNcode) / (Gal[Gal[p].CentralGal].Vvir * Gal[Gal[p].CentralGal].Vvir); } /*else if(FeedbackReheatingModel == 1) { reheated_mass = ALTERNATIVE Reheating LAW ; }*/ if(reheated_mass > Gal[p].ColdGas) reheated_mass = Gal[p].ColdGas; }// end if feedback_location /* Determine ejection (for FeedbackEjectionModel 2 we have the dependence on Vmax) * Guo2010 - eq 22 * Note that satellites can now retain gas and have their own gas cycle*/ if (Gal[Gal[p].CentralGal].Type == 0) { EjectVmax=Gal[centralgal].Vmax; EjectVvir=Gal[centralgal].Vvir;// main halo Vvir } else { EjectVmax=Gal[Gal[p].CentralGal].InfallVmax; EjectVvir=Gal[Gal[p].CentralGal].Vvir; //central subhalo Vvir } if(FeedbackEjectionModel == 0) { ejected_mass = (FeedbackEjectionEfficiency* (EtaSNcode * EnergySNcode) * stars * min(1./FeedbackEjectionEfficiency, .5+1/pow(EjectVmax/EjectPreVelocity,EjectSlope)) - reheated_mass*EjectVvir*EjectVvir) /(EjectVvir*EjectVvir); } else if(FeedbackEjectionModel == 1)//the ejected material is assumed to have V_SN { SN_Energy = .5 * stars * (EtaSNcode * EnergySNcode); Reheat_Energy = .5 * reheated_mass * EjectVvir * EjectVvir; ejected_mass = (SN_Energy - Reheat_Energy)/(0.5 * FeedbackEjectionEfficiency*(EtaSNcode * EnergySNcode)); //if VSN^2<Vvir^2 nothing is ejected if(FeedbackEjectionEfficiency*(EtaSNcode * EnergySNcode)<EjectVvir*EjectVvir) ejected_mass =0.0; } // Finished calculating mass exchanges, so just check that none are negative if (reheated_mass < 0.0) reheated_mass = 0.0; if (ejected_mass < 0.0) ejected_mass = 0.0; /* Update For Feedback */ /* update cold, hot, ejected gas fractions and respective metallicities * there are a number of changes introduced by Guo2010 concerning where * the gas ends up */ //ejected_mass = 0.01*Gal[centralgal].HotGas; if (reheated_mass + ejected_mass > 0.) update_from_feedback(p, centralgal, reheated_mass, ejected_mass); }
//void update_from_star_formation(int p, double time, double stars, double metallicity) void update_from_star_formation(int p, double stars, bool flag_burst, int nstep) { int i; double fraction; double stars_to_add=0.; if(Gal[p].ColdGas <= 0. || stars <= 0.) { printf("update_from_star_formation: Gal[p].ColdGas <= 0. || stars <= 0.\n"); exit(0); } /* If DETAILED_METALS_AND_MASS_RETURN, no longer an assumed instantaneous * recycled fraction. Mass is returned over time via SNe and AGB winds. * Update the Stellar Spin when forming stars */ #ifndef DETAILED_METALS_AND_MASS_RETURN stars_to_add=(1 - RecycleFraction) * stars; #else stars_to_add=stars; #endif if (Gal[p].DiskMass+stars_to_add > 1.e-8) for (i = 0; i < 3; i++) Gal[p].StellarSpin[i]=((Gal[p].StellarSpin[i])*(Gal[p].DiskMass) + stars_to_add*Gal[p].GasSpin[i])/(Gal[p].DiskMass+stars_to_add); /* Update Gas and Metals from star formation */ mass_checks("update_from_star_formation #0",p); fraction=stars_to_add/Gal[p].ColdGas; #ifdef STAR_FORMATION_HISTORY Gal[p].sfh_DiskMass[Gal[p].sfh_ibin]+=stars_to_add; //ROB: Now, all SF gas is put in SFH array ("recycled' mass will return to gas phase over time) Gal[p].sfh_MetalsDiskMass[Gal[p].sfh_ibin] = metals_add(Gal[p].sfh_MetalsDiskMass[Gal[p].sfh_ibin],Gal[p].MetalsColdGas,fraction); #ifdef INDIVIDUAL_ELEMENTS Gal[p].sfh_ElementsDiskMass[Gal[p].sfh_ibin] = elements_add(Gal[p].sfh_ElementsDiskMass[Gal[p].sfh_ibin],Gal[p].ColdGas_elements,fraction); #endif #ifdef TRACK_BURST if (flag_burst) Gal[p].sfh_BurstMass[Gal[p].sfh_ibin]+=stars_to_add; #endif #endif Gal[p].MetalsDiskMass=metals_add(Gal[p].MetalsDiskMass,Gal[p].MetalsColdGas,fraction); Gal[p].MetalsColdGas=metals_add(Gal[p].MetalsColdGas,Gal[p].MetalsColdGas,-fraction); //GLOBAL PROPERTIES Gal[p].DiskMass += stars_to_add; Gal[p].ColdGas -= stars_to_add; #ifdef INDIVIDUAL_ELEMENTS Gal[p].DiskMass_elements=elements_add(Gal[p].DiskMass_elements,Gal[p].ColdGas_elements,fraction); Gal[p].ColdGas_elements=elements_add(Gal[p].ColdGas_elements,Gal[p].ColdGas_elements,-fraction); #endif #ifdef TRACK_BURST if (flag_burst) Gal[p].BurstMass+=stars_to_add; #endif mass_checks("update_from_star_formation #1",p); /* Formation of new metals - instantaneous recycling approximation - only SNII * Also recompute the metallicity of the cold phase.*/ #ifndef DETAILED_METALS_AND_MASS_RETURN /* stars used because the Yield is defined as a fraction of * all stars formed, not just long lived */ Gal[p].MetalsColdGas += Yield * stars; #endif if (DiskRadiusModel == 0) get_stellar_disk_radius(p); }
void compute_cooling(int p, double dt, int ngal) { double Vvir, Rvir, x, lambda, tcool, rcool, temp, tot_hotMass, tot_metals, HotRadius; double coolingGas, logZ, rho_rcool, rho0; mass_checks("cooling_recipe #1",p); tot_hotMass = Gal[p].HotGas; tot_metals = metals_total(Gal[p].MetalsHotGas); if(tot_hotMass > 1.0e-6) { Vvir = Gal[p].Vvir; Rvir = Gal[p].Rvir; tcool = Rvir / Vvir; // tcool = t_dynamical = Rvir/Vvir /* temp -> Temperature of the Gas in Kelvin, obtained from * hidrostatic equilibrium KT=0.5*mu_p*(Vc)^2 assuming Vvir~Vc */ temp = 35.9 * Vvir * Vvir; if (Gal[p].Type == 0) HotRadius = Gal[p].Rvir; else HotRadius = Gal[p].HotRadius; if(tot_metals > 0) logZ = log10(tot_metals / tot_hotMass); else logZ = -10.0; //eq. 3 and 4 Guo2010 lambda = get_metaldependent_cooling_rate(log10(temp), logZ); x = PROTONMASS * BOLTZMANN * temp / lambda; // now this has units sec g/cm^3 x /= (UnitDensity_in_cgs * UnitTime_in_s); // now in internal units rho_rcool = x / (0.28086 * tcool); /* an isothermal density profile for the hot gas is assumed here */ rho0 = tot_hotMass / (4 * M_PI * HotRadius); rcool = sqrt(rho0 / rho_rcool); if (Gal[p].CoolingRadius < rcool) Gal[p].CoolingRadius = rcool; //if Hotradius is used, when galaxies become type 1's there will be a discontinuity in the cooling if(rcool > Rvir) // INFALL DOMINATED REGIME //coolingGas = tot_hotMass; - Delucia 2007 /*comes in to keep the continuity (Delucia2004) */ coolingGas = tot_hotMass / (HotRadius / Vvir) * dt; else // HOT PHASE REGIME /*coolingGas = (tot_hotMass / Rvir) * (rcool / tcool) * dt */ coolingGas = (tot_hotMass / HotRadius) * (rcool / tcool) * dt ; //Photoionizing background if (log10(temp) < 4.0) coolingGas = 0.; if(coolingGas > tot_hotMass) coolingGas = tot_hotMass; else if(coolingGas < 0.0) coolingGas = 0.0; } else { coolingGas = 0.0; } Gal[p].CoolingGas = coolingGas; mass_checks("cooling_recipe #1.5",p); }
void do_AGN_heating(double dt, int ngal) { double AGNrate, AGNheating, AGNaccreted, AGNcoeff, fraction, EDDrate, FreeFallRadius; double dist, HotGas, HotRadius, Rvir, Vvir, Mvir; double LeftOverEnergy, CoolingGas, AGNAccretedFromCentral; int p, FoFCentralGal; if(AGNRadioModeModel == 0) { for (p = 0; p < ngal; p++) if(Gal[p].Type == 0) FoFCentralGal=p; } for (p = 0; p < ngal; p++) { Gal[p].CoolingRate_beforeAGN += Gal[p].CoolingGas / (dt*STEPS); AGNrate=0.; LeftOverEnergy = 0.; HotGas = Gal[p].HotGas; HotRadius = Gal[p].HotRadius; CoolingGas = Gal[p].CoolingGas; Mvir = Gal[p].Mvir; Rvir = Gal[p].Rvir; Vvir = Gal[p].Vvir; if(HotGas > 0.0) { if(AGNRadioModeModel == 0) AGNrate = AgnEfficiency * (UnitTime_in_s*SOLAR_MASS)/(UNITMASS_IN_G*SEC_PER_YEAR) * Gal[p].BlackHoleMass/Hubble_h * (HotGas/Hubble_h) * 10.; else if(AGNRadioModeModel == 2) { //empirical (standard) accretion recipe - Eq. 10 in Croton 2006 AGNrate = AgnEfficiency / (UNITMASS_IN_G / UnitTime_in_s * SEC_PER_YEAR / SOLAR_MASS) * (Gal[p].BlackHoleMass / 0.01) * pow3(Vvir / 200.0) * ((HotGas / HotRadius * Rvir / Mvir) / 0.1); } else if(AGNRadioModeModel == 3 || AGNRadioModeModel == 4) { double x, lambda, temp, logZ, tot_metals; tot_metals = metals_total(Gal[p].MetalsHotGas); /* temp -> Temperature of the Gas in Kelvin, obtained from * hidrostatic equilibrium KT=0.5*mu_p*(Vc)^2 assuming Vvir~Vc */ temp = 35.9 * Vvir * Vvir; if(tot_metals > 0) logZ = log10(tot_metals / HotGas); else logZ = -10.0; lambda = get_metaldependent_cooling_rate(log10(temp), logZ); x = PROTONMASS * BOLTZMANN * temp / lambda; // now this has units sec g/cm^3 x /= (UnitDensity_in_cgs * UnitTime_in_s); // now in internal units /* Bondi-Hoyle accretion recipe -- efficiency = 0.15 * Eq. 29 in Croton 2006 */ if(AGNRadioModeModel == 3) AGNrate = (2.5 * M_PI * G) * (0.75 * 0.6 * x) * Gal[p].BlackHoleMass * 0.15; else if(AGNRadioModeModel == 4) { /* Cold cloud accretion recipe -- trigger: Rff = 50 Rdisk, * and accretion rate = 0.01% cooling rate * Eq. 25 in Croton 2006 */ FreeFallRadius = HotGas / (6.0 * 0.6 * x * Rvir * Vvir) / HotRadius * Rvir; if(Gal[p].BlackHoleMass > 0.0 && FreeFallRadius < Gal[p].GasDiskRadius * 50.0) AGNrate = 0.0001 * CoolingGas / dt; else AGNrate = 0.0; } } /* Eddington rate */ /* Note that this assumes an efficiency of 50% * - it ignores the e/(1-e) factor in L = e/(1-e) Mdot c^2 */ EDDrate = 1.3e48 * Gal[p].BlackHoleMass / (UnitEnergy_in_cgs / UnitTime_in_s) / 9e10; /* accretion onto BH is always limited by the Eddington rate */ if(AGNrate > EDDrate) AGNrate = EDDrate; /* accreted mass onto black hole the value of dt puts an h factor into AGNaccreted as required for code units */ AGNaccreted = AGNrate * dt; /* cannot accrete more mass than is available! */ if(AGNaccreted > HotGas) AGNaccreted = HotGas; /* coefficient to heat the cooling gas back to the virial temperature of the halo */ /* 1.34e5 = sqrt(2*eta*c^2), eta=0.1 (standard efficiency) and c in km/s * Eqs. 11 & 12 in Croton 2006 */ AGNcoeff = (1.34e5 / Vvir) * (1.34e5 / Vvir); /* cooling mass that can be suppressed from AGN heating */ AGNheating = AGNcoeff * AGNaccreted; if(AGNRadioModeModel == 0 && Gal[p].Type==1) { if(dist < Gal[FoFCentralGal].Rvir) { if(AGNheating > (Gal[p].CoolingGas + Gal[FoFCentralGal].CoolingGas)) { AGNheating = (Gal[p].CoolingGas + Gal[FoFCentralGal].CoolingGas); AGNaccreted = (Gal[p].CoolingGas + Gal[FoFCentralGal].CoolingGas) / AGNcoeff; } if(AGNheating > Gal[p].CoolingGas) LeftOverEnergy = AGNheating - Gal[p].CoolingGas; } } else if(AGNheating > Gal[p].CoolingGas) AGNaccreted = Gal[p].CoolingGas / AGNcoeff; /* limit heating to cooling rate */ if(AGNheating > Gal[p].CoolingGas) AGNheating = Gal[p].CoolingGas; /* accreted mass onto black hole */ Gal[p].BlackHoleMass += AGNaccreted; //ROB: transfer_mass functions should be used here Gal[p].RadioAccretionRate += AGNaccreted / (dt*STEPS); fraction=AGNaccreted/Gal[p].HotGas; Gal[p].HotGas -= AGNaccreted; Gal[p].MetalsHotGas = metals_add(Gal[p].MetalsHotGas,Gal[p].MetalsHotGas, -fraction); #ifdef INDIVIDUAL_ELEMENTS Gal[p].HotGas_elements = elements_add(Gal[p].HotGas_elements,Gal[p].HotGas_elements,-fraction); #endif #ifdef METALS_SELF Gal[p].MetalsHotGasSelf = metals_add(Gal[p].MetalsHotGasSelf,Gal[p].MetalsHotGasSelf,-fraction); #endif } else AGNheating = 0.0; Gal[p].CoolingGas -= AGNheating; if(Gal[p].CoolingGas < 0.0) Gal[p].CoolingGas = 0.0; Gal[p].CoolingRate += Gal[p].CoolingGas / (dt*STEPS); if(AGNRadioModeModel == 0 && LeftOverEnergy>0.) { Gal[FoFCentralGal].CoolingGas -= LeftOverEnergy; if(Gal[FoFCentralGal].CoolingGas < 0.0) Gal[FoFCentralGal].CoolingGas = 0.0; else Gal[FoFCentralGal].CoolingRate -= LeftOverEnergy / (dt*STEPS); } mass_checks("cooling_recipe #2.",p); } }
/** @brief Main recipe, calculates the fraction of cold gas turned into stars due * to star formation; the fraction of mass instantaneously recycled and * returned to the cold gas; the fraction of gas reheated from cold to hot, * ejected from hot to external and returned from ejected to hot due to * SN feedback. */ void starformation_and_feedback(int p, int centralgal, double time, double dt, int nstep) { /*! Variables: reff-Rdisk, tdyn=Rdisk/Vmax, strdot=Mstar_dot, stars=strdot*dt*/ double tdyn, strdot, stars, reheated_mass, ejected_mass, fac, cold_crit, metallicitySF, CentralVvir, MergeCentralVvir, EjectVmax, EjectVvir, SN_Energy, Reheat_Energy; //standard star formation law (Croton2006, Delucia2007, Guo2010) if(StarFormationRecipe == 0) { if(Gal[p].Type == 0) tdyn = Gal[p].GasDiskRadius / Gal[p].Vmax; else tdyn = Gal[p].GasDiskRadius / Gal[p].InfallVmax; if(Gal[p].Type == 0) cold_crit = 0.19 * Gal[p].Vmax * Gal[p].GasDiskRadius; else cold_crit = 0.19 * Gal[p].InfallVmax * Gal[p].GasDiskRadius; if(Gal[p].ColdGas > cold_crit) strdot = SfrEfficiency * (Gal[p].ColdGas - cold_crit) / tdyn * pow(Gal[p].Vvir / SfrLawPivotVelocity, SfrLawSlope); else strdot = 0.0; } //No dependce on Tdyn if(StarFormationRecipe == 1) { if(Gal[p].ColdGas > 1.e-7) strdot = SfrEfficiency * (Gal[p].ColdGas) / (2.e-5) * pow(Gal[p].Vvir / SfrLawPivotVelocity, SfrLawSlope); else strdot = 0.0; } #ifdef H2FORMATION /* use H2 formation model from Krumholz */ Not yet implemented H2Mass=cal_H2(p); strdot = SfrEfficiency * H2Mass / tdyn; #endif /*TODO - Note that Units of dynamical time are Mpc/Km/s - no conversion on dt needed * be mentioned 3.06e19 to 3.15e19 */ stars = strdot * dt; if(stars < 0.0) terminate("***error stars<0.0***\n"); /* SN FEEDBACK RECIPES */ /* In Guo2010 type 1s can eject, reincorporate gas and get gas from their * own satellites (is not sent to the type 0 galaxy as in Delucia2007), * for gas flow computations: * If satellite is inside Rvir of main halo, Vvir of main halo used * If it is outside, the Vvir of its central subhalo is used. */ CentralVvir = Gal[centralgal].Vvir; // main halo Vvir MergeCentralVvir = Gal[Gal[p].CentralGal].Vvir; //central subhalo Vvir mass_checks("recipe_starform #0",p); mass_checks("recipe_starform #0.1",centralgal); /* Feedback depends on the circular velocity of the host halo * Guo2010 - eq 18 & 19*/ if(FeedbackRecipe == 0) { if (Gal[Gal[p].CentralGal].Type == 0) reheated_mass = FeedbackReheatingEpsilon * stars* (.5+1./pow(Gal[Gal[p].CentralGal].Vmax/ReheatPreVelocity,ReheatSlope)); else reheated_mass = FeedbackReheatingEpsilon * stars* (.5+1./pow(Gal[Gal[p].CentralGal].InfallVmax/ReheatPreVelocity,ReheatSlope)); } //Make sure that the energy used in reheat does not exceed the SN energy (central subhalo Vvir used) if (FeedbackRecipe == 0 || FeedbackRecipe == 1) { if (reheated_mass * Gal[Gal[p].CentralGal].Vvir * Gal[Gal[p].CentralGal].Vvir > stars * (EtaSNcode * EnergySNcode)) reheated_mass = stars * (EtaSNcode * EnergySNcode) / (Gal[Gal[p].CentralGal].Vvir * Gal[Gal[p].CentralGal].Vvir); } /* cant use more cold gas than is available! so balance SF and feedback */ if((stars + reheated_mass) > Gal[p].ColdGas) { fac = Gal[p].ColdGas / (stars + reheated_mass); stars *= fac; reheated_mass *= fac; } /* Determine ejection (for FeedbackRecipe 2 we have the dependence on Vmax) * Guo2010 - eq 22 * Note that satellites can now retain gas and have their own gas cycle*/ if (Gal[Gal[p].CentralGal].Type == 0) { EjectVmax=Gal[centralgal].Vmax; EjectVvir=Gal[centralgal].Vvir;// main halo Vvir } else { EjectVmax=Gal[Gal[p].CentralGal].InfallVmax; EjectVvir=Gal[Gal[p].CentralGal].Vvir; //central subhalo Vvir } if(FeedbackRecipe == 0) { ejected_mass = (FeedbackEjectionEfficiency* (EtaSNcode * EnergySNcode) * stars * min(1./FeedbackEjectionEfficiency, .5+1/pow(EjectVmax/EjectPreVelocity,EjectSlope)) - reheated_mass*EjectVvir*EjectVvir) /(EjectVvir*EjectVvir); } else if(FeedbackRecipe == 1)//the ejected material is assumed to have V_SN { SN_Energy = .5 * stars * (EtaSNcode * EnergySNcode); Reheat_Energy = .5 * reheated_mass * EjectVvir * EjectVvir; ejected_mass = (SN_Energy - Reheat_Energy)/(0.5 * FeedbackEjectionEfficiency*(EtaSNcode * EnergySNcode)); //if VSN^2<Vvir^2 nothing is ejected if(FeedbackEjectionEfficiency*(EtaSNcode * EnergySNcode)<EjectVvir*EjectVvir) ejected_mass =0.0; } // Finished calculating mass exchanges, so just check that none are negative if (reheated_mass < 0.0) reheated_mass = 0.0; if (ejected_mass < 0.0) ejected_mass = 0.0; /* update the star formation rate */ #ifdef SAVE_MEMORY /*Sfr=stars/(dt*steps)=strdot*dt/(dt*steps)=strdot/steps -> average over the STEPS*/ Gal[p].Sfr += stars / (dt * STEPS); #else for(outputbin = 0; outputbin < NOUT; outputbin++) { if(Halo[halonr].SnapNum == ListOutputSnaps[outputbin]) { Gal[p].Sfr[outputbin] += stars / (dt * STEPS); break; } } #endif mass_checks("recipe_starform #1",p); mass_checks("recipe_starform #1.1",centralgal); /* update for star formation * updates Mcold, StellarMass, MetalsMcold and MetalsStellarMass * in Guo2010 case updates the stellar spin -> hardwired, not an option */ /* Store the value of the metallicity of the cold phase when SF occurs */ if (Gal[p].ColdGas > 0.) #ifdef METALS //metallicitySF=Gal[p].MetalsColdGas.type2/Gal[p].ColdGas; metallicitySF= metals_total(Gal[p].MetalsColdGas)/Gal[p].ColdGas; #else metallicitySF=Gal[p].MetalsColdGas/Gal[p].ColdGas; #endif else