|
| 1 | +# In this example, we lookback the trajectories of MW's satellites |
| 2 | +# The original src/data/MW_satellites_origin.csv are extracted from battaglia2022gaia_Astron. Astrophys. - Gaia early DR3 systemic motions of local group dwarf galaxies and orbital properties with a massive Large Magellanic Cloud |
| 3 | +# and converted to Galactocentric coordinates using astropy. |
| 4 | +# The parameters of LMC are from vasiliev2021tango_MNRAS - Tango for three sagittarius, LMC, and the milky way |
| 5 | + |
| 6 | +using Unitful, UnitfulAstro |
| 7 | +using StructArrays |
| 8 | +using DataFrames, CSV |
| 9 | +using GLMakie |
| 10 | +using ProgressMeter |
| 11 | + |
| 12 | +using AstroIC |
| 13 | +using AstroNbodySim |
| 14 | +using WaveDM |
| 15 | + |
| 16 | +using PhysicalParticles |
| 17 | + |
| 18 | + |
| 19 | +df_MW_satellites = AstroIC.load_data_MW_satellites() # load from src/data/MW_satellites.csv |
| 20 | + |
| 21 | + |
| 22 | +##### General parameters |
| 23 | + |
| 24 | +Np = 1000 |
| 25 | +# Np = 5000 |
| 26 | +# Np = 10000 |
| 27 | +# Np = 50000 |
| 28 | +GravitySolver = Tree() |
| 29 | +# GravitySolver = DirectSum() |
| 30 | +pids = [1] |
| 31 | + |
| 32 | +TimeEnd = 6.0u"Gyr" |
| 33 | +TimeStep = 0.0u"Gyr" # adaptive if zero. |
| 34 | +# TimeStep = 1e-5u"Gyr" |
| 35 | +TimeBetweenSnapshots = 0.1u"Gyr" |
| 36 | + |
| 37 | +file_LMC = joinpath(@__DIR__, "output/LMC_traj_lookback_Vasiliev2021", "analysis.csv") |
| 38 | +flag_load_LMC = false # If false, re-compute the trajectory of LMC |
| 39 | +# flag_load_LMC = true # If true and the file exists, skip trajectory lookback of LMC |
| 40 | +Np_LMC = 200 |
| 41 | +# Np_LMC = 1000 |
| 42 | +# Np_LMC = 10000 |
| 43 | + |
| 44 | +##### Initialize MW model |
| 45 | +@info "Initializing MW's baryons" |
| 46 | +#TODO: WaveDM.jl |
| 47 | +baryon_particles = test_MW_MOND(; |
| 48 | + V = (x,y,z,ψ)->0.0, |
| 49 | + Nx = 32, |
| 50 | + Np, |
| 51 | + baryon_mode = :particles_static, |
| 52 | + export_particles = true, |
| 53 | +); |
| 54 | + |
| 55 | +sim_force_baryon = Simulation(baryon_particles; |
| 56 | + GravitySolver, |
| 57 | + pids, |
| 58 | +); |
| 59 | + |
| 60 | + |
| 61 | +@info "Initializing MW's halo" |
| 62 | +MW_model_halo = Zhao(1.55e7u"Msun/kpc^3", 11.75u"kpc", 1.19, 2.95, 0.95) |
| 63 | +MW_ρ_halo_func = (r)->GalacticDynamics.density(MW_model_halo, r) |
| 64 | +MW_table_r = collect(0.1:0.1:100) * u"kpc"; |
| 65 | +MW_ρ_halo = MW_ρ_halo_func.(MW_table_r); |
| 66 | +MW_table_mass_halo = 4π * cumul_integrate(MW_table_r, MW_table_r.^2 .* MW_ρ_halo); |
| 67 | +MW_table_acc_halo = -C.G * MW_table_mass_halo ./ MW_table_r.^2; |
| 68 | +MW_table_pot_halo = -C.G * MW_table_mass_halo ./ MW_table_r; |
| 69 | +MW_table_pot_halo[1:end-2] .-= [C.G * 4π * NumericalIntegration.integrate(MW_table_r[i:end], MW_ρ_halo[i:end] .* MW_table_r[i:end]) for i in eachindex(MW_table_r)[1:end-2]]; |
| 70 | +spl_acc = Spline1D(ustrip.(u"kpc", MW_table_r), ustrip.(u"kpc/Gyr^2", MW_table_acc_halo); k=1) |
| 71 | +spl_pot = Spline1D(ustrip.(u"kpc", MW_table_r), ustrip.(u"kpc^2/Gyr^2", MW_table_pot_halo); k=1) |
| 72 | + |
| 73 | + |
| 74 | +### MW background force without LMC |
| 75 | +function MW_bg_force(p::AbstractParticle, t; |
| 76 | + SofteningLength = 0.01u"kpc", |
| 77 | + GravitySolver = Tree(), |
| 78 | + # GravitySolver = DirectSum(), |
| 79 | + sim_force_baryon = sim_force_baryon, |
| 80 | + spl_acc = spl_acc, |
| 81 | +) |
| 82 | + acc_DM = spl_acc(ustrip(u"kpc", norm(p.Pos))) * ustrip(normalize(p.Pos)) * u"kpc/Gyr^2" |
| 83 | + acc_b = compute_force(sim_force_baryon, p.Pos, SofteningLength, GravitySolver, CPU())[1] |
| 84 | + return acc = acc_b + acc_DM |
| 85 | +end |
| 86 | + |
| 87 | +p1x(sim::Simulation) = find_particle(sim, 1).Pos.x |
| 88 | +p1y(sim::Simulation) = find_particle(sim, 1).Pos.y |
| 89 | +p1z(sim::Simulation) = find_particle(sim, 1).Pos.z |
| 90 | + |
| 91 | +analysers = Dict( |
| 92 | + "x" => p1x, |
| 93 | + "y" => p1y, |
| 94 | + "z" => p1z, |
| 95 | +) |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | +##### LMC |
| 100 | +if flag_load_LMC && isfile(file_LMC) |
| 101 | + @info "Loading LMC trajectory from $(file_LMC)" |
| 102 | + df_traj_LMC = DataFrame(CSV.File(file_LMC)); |
| 103 | +else |
| 104 | + @info "Simulating LMC..." |
| 105 | + ### Vasiliev 2021 |
| 106 | + model_LMC = tNFW(5.2629e6u"Msun/kpc^3", 8.5u"kpc" * 1.38^0.6, 85u"kpc" * 1.38^0.6) |
| 107 | + config_LMC = SphericalSystem(STAR, Np_LMC, |
| 108 | + x -> GalacticDynamics.density(model_LMC, x) * 4π * x^2, |
| 109 | + ) |
| 110 | + particles_LMC = generate(config_LMC; |
| 111 | + MaxRadius = 85.0u"kpc" * 1.38^0.6 * 1.5, |
| 112 | + ); |
| 113 | + particles_LMC.Mass .= 1.38e11u"Msun"/Np_LMC; # make sure the total mass is 1.38e11 Msun |
| 114 | + |
| 115 | + |
| 116 | + tidal_initial_pos = PVector(-0.6, -41.3, -27.1, u"kpc") # Galactocentric |
| 117 | + tidal_initial_vel = -PVector(-63.9, -213.8, 206.6, u"km/s") #? Negative for lookback |
| 118 | + |
| 119 | + test_particles = StructArray([ |
| 120 | + Star(uAstro; id = 1), |
| 121 | + ]) |
| 122 | + test_particles.Pos[1] = tidal_initial_pos |
| 123 | + test_particles.Vel[1] = tidal_initial_vel |
| 124 | + |
| 125 | + sim_traj_lookback = Simulation( |
| 126 | + deepcopy(test_particles); |
| 127 | + bgforce = Function[MW_bg_force], |
| 128 | + analysers, |
| 129 | + TimeEnd = 6.0u"Gyr", |
| 130 | + TimeStep = 0.0u"Gyr", # adaptive if zero. |
| 131 | + # TimeStep = 1e-5u"Gyr", |
| 132 | + TimeBetweenSnapshots = 0.1u"Gyr", |
| 133 | + Realtime = false, |
| 134 | + OutputDir = "output/LMC_traj_lookback_Vasiliev2021", |
| 135 | + ); |
| 136 | + run(sim_traj_lookback); |
| 137 | + |
| 138 | + |
| 139 | + df_traj_LMC = DataFrame(CSV.File(joinpath("output/LMC_traj_lookback_Vasiliev2021", "analysis.csv"))); |
| 140 | +end |
| 141 | + |
| 142 | + |
| 143 | +function MW_bg_force_LMC(p::AbstractParticle, t; |
| 144 | + SofteningLength = 0.01u"kpc", |
| 145 | + GravitySolver = Tree(), |
| 146 | + # GravitySolver = DirectSum(), |
| 147 | + sim_force_baryon = sim_force_baryon, |
| 148 | + spl_acc = spl_acc, |
| 149 | + df_traj = df_traj_LMC, |
| 150 | + particles_LMC = particles_LMC, |
| 151 | +) |
| 152 | + acc_DM = spl_acc(ustrip(u"kpc", norm(p.Pos))) * ustrip(normalize(p.Pos)) * u"kpc/Gyr^2" |
| 153 | + acc_b = compute_force(sim_force_baryon, p.Pos, SofteningLength, GravitySolver, CPU())[1] |
| 154 | + |
| 155 | + if ustrip(u"Gyr", t) <= minimum(df_traj.time) |
| 156 | + id = 1 |
| 157 | + else |
| 158 | + id = findfirstvalue(df_traj.time, ustrip(u"Gyr", t)) |
| 159 | + end |
| 160 | + |
| 161 | + particles_LMC_t = deepcopy(particles_LMC) |
| 162 | + particles_LMC_t.Pos .+= PVector(df_traj.x[id], df_traj.y[id], df_traj.z[id]) * u"kpc" |
| 163 | + acc_LMC = AstroNbodySim.compute_force_at_point(p.Pos, particles_LMC_t, C.G, SofteningLength) #? DirectSum for LMC particles |
| 164 | + return acc = acc_b + acc_DM + acc_LMC |
| 165 | +end |
| 166 | + |
| 167 | + |
| 168 | +@showprogress for i in eachindex(df_MW_satellites.Galaxy) |
| 169 | + @info "Simulating galaxy $(df_MW_satellites.Galaxy[i]) without LMC" |
| 170 | + tidal_initial_pos = PVector(df_MW_satellites.X[i], df_MW_satellites.Y[i], df_MW_satellites.Z[i]) # Galactocentric |
| 171 | + tidal_initial_vel = -PVector(df_MW_satellites.v_X[i], df_MW_satellites.v_Y[i], df_MW_satellites.v_Z[i]) #? Negative for lookback |
| 172 | + @info tidal_initial_pos |
| 173 | + @info tidal_initial_vel |
| 174 | + |
| 175 | + test_particles = StructArray([ |
| 176 | + Star(uAstro; id = 1), |
| 177 | + ]) |
| 178 | + test_particles.Pos[1] = tidal_initial_pos |
| 179 | + test_particles.Vel[1] = tidal_initial_vel |
| 180 | + |
| 181 | + sim_traj_lookback = Simulation( |
| 182 | + deepcopy(test_particles); |
| 183 | + bgforce = Function[MW_bg_force], |
| 184 | + analysers, |
| 185 | + TimeEnd, |
| 186 | + TimeStep, |
| 187 | + TimeBetweenSnapshots, |
| 188 | + Realtime = false, |
| 189 | + OutputDir = joinpath(@__DIR__, "output/traj_lookback_$(df_MW_satellites.Galaxy[i])_no_LMC"), |
| 190 | + ); |
| 191 | + run(sim_traj_lookback); |
| 192 | + |
| 193 | + |
| 194 | + @info "Simulating galaxy $(df_MW_satellites.Galaxy[i]) with LMC" |
| 195 | + tidal_initial_pos = PVector(df_MW_satellites.X[i], df_MW_satellites.Y[i], df_MW_satellites.Z[i]) # Galactocentric |
| 196 | + tidal_initial_vel = -PVector(df_MW_satellites.v_X[i], df_MW_satellites.v_Y[i], df_MW_satellites.v_Z[i]) #? Negative for lookback |
| 197 | + |
| 198 | + test_particles = StructArray([ |
| 199 | + Star(uAstro; id = 1), |
| 200 | + ]) |
| 201 | + test_particles.Pos[1] = tidal_initial_pos |
| 202 | + test_particles.Vel[1] = tidal_initial_vel |
| 203 | + |
| 204 | + sim_traj_lookback = Simulation( |
| 205 | + deepcopy(test_particles); |
| 206 | + bgforce = Function[MW_bg_force_LMC], |
| 207 | + analysers, |
| 208 | + TimeEnd, |
| 209 | + TimeStep, |
| 210 | + TimeBetweenSnapshots, |
| 211 | + Realtime = false, |
| 212 | + OutputDir = joinpath(@__DIR__, "output/traj_lookback_$(df_MW_satellites.Galaxy[i])_with_LMC"), |
| 213 | + ); |
| 214 | + run(sim_traj_lookback); |
| 215 | + |
| 216 | + #TODO release memory of simulations |
| 217 | +end |
| 218 | + |
0 commit comments