double estimate_merging_time(int halonr, int mother_halonr, int p) { int central_halonr; double coulomb, mergtime, SatelliteMass, SatelliteRadius, MotherHaloRvir; /** @brief Binney & Tremaine 1987 - 7.26 merging time for satellites due to * dynamical friction. After Delucia2007 *2, shown to agree with * Kolchin2008 simulations in Delucia2010. This is set when a galaxy * becomes a type 2 or being a type 1 \f$M_{\rm{star}}>M_{\rm{vir}}\f$. * In DeLucia2007 they could only merge into a type 0, now (after * guo2010) they can merge into a type 1. */ /* recipe updated for more accurate merging time (see BT eq 7.26), now satellite radius at previous timestep is included */ central_halonr = Halo[Halo[halonr].Descendant].FirstProgenitor; if(Gal[p].Type == 1) central_halonr=mother_halonr; if(central_halonr == halonr) { terminate("can't be...!\n"); } coulomb = log(Halo[mother_halonr].Len / ((double) Halo[halonr].Len) + 1); /* should include stellar+cold gas in SatelliteMass! */ SatelliteMass = get_virial_mass(halonr)+(Gal[p].DiskMass+Gal[p].BulgeMass); SatelliteRadius = separation_halo(central_halonr,halonr)/(1 + ZZ[Halo[halonr].SnapNum]); int j; for (j = 0; j < 3; j++) Gal[p].DistanceToCentralGal[j] = wrap(Halo[central_halonr].Pos[j] - Halo[halonr].Pos[j], BoxSize); MotherHaloRvir = get_virial_radius(mother_halonr); if(SatelliteRadius > MotherHaloRvir) SatelliteRadius = MotherHaloRvir; if(SatelliteMass > 0.0) { mergtime = 1.17 * SatelliteRadius * SatelliteRadius * get_virial_velocity(mother_halonr) / (coulomb * G * SatelliteMass); // Binney & Tremaine Eq.7.26 /* change introduced by Delucia2007 to fit observations */ mergtime = 2.*mergtime; } else mergtime = -99999.9; return mergtime; }
void prepare_galaxy_for_output(int filenr, int tree, struct GALAXY *g, struct GALAXY_OUTPUT *o) { int j, step; o->SnapNum = g->SnapNum; o->Type = g->Type; // assume that because there are so many files, the trees per file will be less than 100000 // required for limits of long long if(LastFile>=10000) { assert( g->GalaxyNr < TREE_MUL_FAC ); // breaking tree size assumption assert(tree < (FILENR_MUL_FAC/10)/TREE_MUL_FAC); o->GalaxyIndex = g->GalaxyNr + TREE_MUL_FAC * tree + (FILENR_MUL_FAC/10) * filenr; assert( (o->GalaxyIndex - g->GalaxyNr - TREE_MUL_FAC*tree)/(FILENR_MUL_FAC/10) == filenr ); assert( (o->GalaxyIndex - g->GalaxyNr -(FILENR_MUL_FAC/10)*filenr) / TREE_MUL_FAC == tree ); assert( o->GalaxyIndex - TREE_MUL_FAC*tree - (FILENR_MUL_FAC/10)*filenr == g->GalaxyNr ); o->CentralGalaxyIndex = HaloGal[HaloAux[Halo[g->HaloNr].FirstHaloInFOFgroup].FirstGalaxy].GalaxyNr + TREE_MUL_FAC * tree + (FILENR_MUL_FAC/10) * filenr; } else { assert( g->GalaxyNr < TREE_MUL_FAC ); // breaking tree size assumption assert(tree < FILENR_MUL_FAC/TREE_MUL_FAC); o->GalaxyIndex = g->GalaxyNr + TREE_MUL_FAC * tree + FILENR_MUL_FAC * filenr; assert( (o->GalaxyIndex - g->GalaxyNr - TREE_MUL_FAC*tree)/FILENR_MUL_FAC == filenr ); assert( (o->GalaxyIndex - g->GalaxyNr -FILENR_MUL_FAC*filenr) / TREE_MUL_FAC == tree ); assert( o->GalaxyIndex - TREE_MUL_FAC*tree - FILENR_MUL_FAC*filenr == g->GalaxyNr ); o->CentralGalaxyIndex = HaloGal[HaloAux[Halo[g->HaloNr].FirstHaloInFOFgroup].FirstGalaxy].GalaxyNr + TREE_MUL_FAC * tree + FILENR_MUL_FAC * filenr; } o->SAGEHaloIndex = g->HaloNr; o->SAGETreeIndex = tree; o->SimulationHaloIndex = Halo[g->HaloNr].MostBoundID; o->mergeType = g->mergeType; o->mergeIntoID = g->mergeIntoID; o->mergeIntoSnapNum = g->mergeIntoSnapNum; o->dT = g->dT * UnitTime_in_s / SEC_PER_MEGAYEAR; for(j = 0; j < 3; j++) { o->Pos[j] = g->Pos[j]; o->Vel[j] = g->Vel[j]; o->Spin[j] = Halo[g->HaloNr].Spin[j]; } o->Len = g->Len; o->Mvir = g->Mvir; o->CentralMvir = get_virial_mass(Halo[g->HaloNr].FirstHaloInFOFgroup); o->Rvir = get_virial_radius(g->HaloNr); // output the actual Rvir, not the maximum Rvir o->Vvir = get_virial_velocity(g->HaloNr); // output the actual Vvir, not the maximum Vvir o->Vmax = g->Vmax; o->VelDisp = Halo[g->HaloNr].VelDisp; o->ColdGas = g->ColdGas; o->StellarMass = g->StellarMass; o->BulgeMass = g->BulgeMass; o->HotGas = g->HotGas; o->EjectedMass = g->EjectedMass; o->BlackHoleMass = g->BlackHoleMass; o->ICS = g->ICS; o->MetalsColdGas = g->MetalsColdGas; o->MetalsStellarMass = g->MetalsStellarMass; o->MetalsBulgeMass = g->MetalsBulgeMass; o->MetalsHotGas = g->MetalsHotGas; o->MetalsEjectedMass = g->MetalsEjectedMass; o->MetalsICS = g->MetalsICS; o->SfrDisk = 0.0; o->SfrBulge = 0.0; o->SfrDiskZ = 0.0; o->SfrBulgeZ = 0.0; // NOTE: in Msun/yr for(step = 0; step < STEPS; step++) { o->SfrDisk += g->SfrDisk[step] * UnitMass_in_g / UnitTime_in_s * SEC_PER_YEAR / SOLAR_MASS / STEPS; o->SfrBulge += g->SfrBulge[step] * UnitMass_in_g / UnitTime_in_s * SEC_PER_YEAR / SOLAR_MASS / STEPS; if(g->SfrDiskColdGas[step] > 0.0) o->SfrDiskZ += g->SfrDiskColdGasMetals[step] / g->SfrDiskColdGas[step] / STEPS; if(g->SfrBulgeColdGas[step] > 0.0) o->SfrBulgeZ += g->SfrBulgeColdGasMetals[step] / g->SfrBulgeColdGas[step] / STEPS; } o->DiskScaleRadius = g->DiskScaleRadius; if (g->Cooling > 0.0) o->Cooling = log10(g->Cooling * UnitEnergy_in_cgs / UnitTime_in_s); else o->Cooling = 0.0; if (g->Heating > 0.0) o->Heating = log10(g->Heating * UnitEnergy_in_cgs / UnitTime_in_s); else o->Heating = 0.0; o->QuasarModeBHaccretionMass = g->QuasarModeBHaccretionMass; o->TimeOfLastMajorMerger = g->TimeOfLastMajorMerger * UnitTime_in_Megayears; o->TimeOfLastMinorMerger = g->TimeOfLastMinorMerger * UnitTime_in_Megayears; o->OutflowRate = g->OutflowRate * UnitMass_in_g / UnitTime_in_s * SEC_PER_YEAR / SOLAR_MASS; //infall properties if(g->Type != 0) { o->infallMvir = g->infallMvir; o->infallVvir = g->infallVvir; o->infallVmax = g->infallVmax; } else { o->infallMvir = 0.0; o->infallVvir = 0.0; o->infallVmax = 0.0; } }
int join_galaxies_of_progenitors(int halonr, int ngalstart) { int ngal, prog, mother_halo=-1, i, j, first_occupied, lenmax, lenoccmax, centralgal; double previousMvir, previousVvir, previousVmax; int step; lenmax = 0; lenoccmax = 0; first_occupied = Halo[halonr].FirstProgenitor; prog = Halo[halonr].FirstProgenitor; if(prog >=0) if(HaloAux[prog].NGalaxies > 0) lenoccmax = -1; // Find most massive progenitor that contains an actual galaxy // Maybe FirstProgenitor never was FirstHaloInFOFGroup and thus has no galaxy while(prog >= 0) { if(Halo[prog].Len > lenmax) { lenmax = Halo[prog].Len; mother_halo = prog; } if(lenoccmax != -1 && Halo[prog].Len > lenoccmax && HaloAux[prog].NGalaxies > 0) { lenoccmax = Halo[prog].Len; first_occupied = prog; } prog = Halo[prog].NextProgenitor; } ngal = ngalstart; prog = Halo[halonr].FirstProgenitor; while(prog >= 0) { for(i = 0; i < HaloAux[prog].NGalaxies; i++) { assert(ngal < FoF_MaxGals); // This is the cruical line in which the properties of the progenitor galaxies // are copied over (as a whole) to the (temporary) galaxies Gal[xxx] in the current snapshot // After updating their properties and evolving them // they are copied to the end of the list of permanent galaxies HaloGal[xxx] Gal[ngal] = HaloGal[HaloAux[prog].FirstGalaxy + i]; Gal[ngal].HaloNr = halonr; Gal[ngal].dT = -1.0; // this deals with the central galaxies of (sub)halos if(Gal[ngal].Type == 0 || Gal[ngal].Type == 1) { // this halo shouldn't hold a galaxy that has already merged; remove it from future processing if(Gal[ngal].mergeType != 0) { Gal[ngal].Type = 3; continue; } // remember properties from the last snapshot previousMvir = Gal[ngal].Mvir; previousVvir = Gal[ngal].Vvir; previousVmax = Gal[ngal].Vmax; if(prog == first_occupied) { // update properties of this galaxy with physical properties of halo 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]; } Gal[ngal].Len = Halo[halonr].Len; Gal[ngal].Vmax = Halo[halonr].Vmax; Gal[ngal].deltaMvir = get_virial_mass(halonr) - Gal[ngal].Mvir; if(get_virial_mass(halonr) > Gal[ngal].Mvir) { Gal[ngal].Rvir = get_virial_radius(halonr); // use the maximum Rvir in model Gal[ngal].Vvir = get_virial_velocity(halonr); // use the maximum Vvir in model } Gal[ngal].Mvir = get_virial_mass(halonr); Gal[ngal].Cooling = 0.0; Gal[ngal].Heating = 0.0; Gal[ngal].QuasarModeBHaccretionMass = 0.0; Gal[ngal].OutflowRate = 0.0; Gal[ngal].Lx_bol = 0.0; for(step = 0; step < STEPS; step++) { Gal[ngal].SfrDisk[step] = Gal[ngal].SfrBulge[step] = 0.0; Gal[ngal].SfrDiskColdGas[step] = Gal[ngal].SfrDiskColdGasMetals[step] = 0.0; Gal[ngal].SfrBulgeColdGas[step] = Gal[ngal].SfrBulgeColdGasMetals[step] = 0.0; } if(halonr == Halo[halonr].FirstHaloInFOFgroup) { // a central galaxy Gal[ngal].mergeType = 0; Gal[ngal].mergeIntoID = -1; Gal[ngal].MergTime = 999.9; Gal[ngal].DiskScaleRadius = get_disk_radius(halonr, ngal); Gal[ngal].Type = 0; } else { // a satellite with subhalo Gal[ngal].mergeType = 0; Gal[ngal].mergeIntoID = -1; if(Gal[ngal].Type == 0) // remember the infall properties before becoming a subhalo { Gal[ngal].infallMvir = previousMvir; Gal[ngal].infallVvir = previousVvir; Gal[ngal].infallVmax = previousVmax; } if(Gal[ngal].Type == 0 || Gal[ngal].MergTime > 999.0) // here the galaxy has gone from type 1 to type 2 or otherwise doesn't have a merging time. Gal[ngal].MergTime = estimate_merging_time(halonr, Halo[halonr].FirstHaloInFOFgroup, ngal); Gal[ngal].Type = 1; } } else { // an orhpan satellite galaxy - these will merge or disrupt within the current timestep Gal[ngal].deltaMvir = -1.0*Gal[ngal].Mvir; Gal[ngal].Mvir = 0.0; if(Gal[ngal].MergTime > 999.0 || Gal[ngal].Type == 0) { // here the galaxy has gone from type 0 to type 2 - merge it! Gal[ngal].MergTime = 0.0; Gal[ngal].infallMvir = previousMvir; Gal[ngal].infallVvir = previousVvir; Gal[ngal].infallVmax = previousVmax; } Gal[ngal].Type = 2; } } ngal++; } prog = Halo[prog].NextProgenitor; } if(ngal == 0) { // We have no progenitors with galaxies. This means we create a new galaxy. init_galaxy(ngal, halonr); ngal++; } // Per Halo there can be only one Type 0 or 1 galaxy, all others are Type 2 (orphan) // In fact, this galaxy is very likely to be the first galaxy in the halo if // first_occupied==FirstProgenitor and the Type0/1 galaxy in FirstProgenitor was also the first one // This cannot be guaranteed though for the pathological first_occupied!=FirstProgenitor case for(i = ngalstart, centralgal = -1; i < ngal; i++) { if(Gal[i].Type == 0 || Gal[i].Type == 1) { assert(centralgal == -1); centralgal = i; } } for(i = ngalstart; i < ngal; i++) Gal[i].CentralGal = centralgal; return ngal; }