|
| 1 | +import numpy |
| 2 | +import math |
| 3 | +from amuse.lab import * |
| 4 | +from amuse import datamodel |
| 5 | +from amuse.units import quantities |
| 6 | +from amuse.ext.rotating_bridge import Rotating_Bridge |
| 7 | +from amuse_galaxia.interface import BarAndSpirals3D |
| 8 | +from amuse.ext.composition_methods import * |
| 9 | +from matplotlib import pyplot as plt |
| 10 | + |
| 11 | + |
| 12 | +class drift_without_gravity(object): |
| 13 | + """ |
| 14 | + This class is to make the ev of non interactig particles by using bridge. |
| 15 | + """ |
| 16 | + |
| 17 | + def __init__(self, particles, time=0 | units.Myr): |
| 18 | + self.particles = particles |
| 19 | + self.model_time = time |
| 20 | + |
| 21 | + def evolve_model(self, t_end): |
| 22 | + dt = t_end - self.model_time |
| 23 | + self.particles.position += self.particles.velocity * dt |
| 24 | + self.model_time = t_end |
| 25 | + |
| 26 | + @property |
| 27 | + def potential_energy(self): |
| 28 | + return quantities.zero |
| 29 | + |
| 30 | + @property |
| 31 | + def kinetic_energy(self): |
| 32 | + return ( |
| 33 | + 0.5 * self.particles.mass * self.particles.velocity.lengths() ** 2 |
| 34 | + ).sum() |
| 35 | + |
| 36 | + |
| 37 | +I = 0 |
| 38 | + |
| 39 | + |
| 40 | +class IntegrateOrbit(object): |
| 41 | + """ |
| 42 | + This class makes the integration of the Sun in the Milky Way |
| 43 | + by using BarAndSpirals3D. |
| 44 | + galaxy(): Function that sets the desired Galactic model. Any question on the parameters, contact me |
| 45 | + creation_particles_noinertial(): creates a parti le set in a rotating frame |
| 46 | + noinertial_to_inertial(): converts data from rotating to inertial frame |
| 47 | + get_pos_vel_and_orbit(): Makes the evolution of the particle set |
| 48 | + """ |
| 49 | + |
| 50 | + def __init__( |
| 51 | + self, |
| 52 | + t_end=10 | units.Myr, |
| 53 | + dt_bridge=0.5 | units.Myr, |
| 54 | + method=SPLIT_6TH_SS_M13, |
| 55 | + phase_bar=0, |
| 56 | + phase_spiral=0, |
| 57 | + omega_spiral=-20 | (units.kms / units.kpc), |
| 58 | + amplitude=650 | (units.kms**2 / units.kpc), |
| 59 | + m=4, |
| 60 | + omega_bar=-50 | (units.kms / units.kpc), |
| 61 | + mass_bar=1.1e10 | units.MSun, |
| 62 | + ): |
| 63 | + # Simulation parameters |
| 64 | + self.t_end = t_end |
| 65 | + self.dt_bridge = dt_bridge |
| 66 | + self.method = method |
| 67 | + self.time = 0 | units.Myr |
| 68 | + # galaxy parameters |
| 69 | + self.omega = 0 | (units.kms / units.kpc) |
| 70 | + self.initial_phase = 0 |
| 71 | + self.bar_phase = phase_bar |
| 72 | + self.spiral_phase = phase_spiral |
| 73 | + self.omega_spiral = omega_spiral |
| 74 | + self.amplitude = amplitude |
| 75 | + self.rsp = 3.12 | units.kpc |
| 76 | + self.m = m |
| 77 | + self.tan_pitch_angle = 0.227194425 |
| 78 | + self.omega_bar = omega_bar |
| 79 | + self.mass_bar = mass_bar |
| 80 | + self.aaxis_bar = 3.12 | units.kpc |
| 81 | + self.axis_ratio_bar = 0.37 |
| 82 | + return |
| 83 | + |
| 84 | + def galaxy(self): |
| 85 | + global I |
| 86 | + galaxy = BarAndSpirals3D( |
| 87 | + redirection="file", redirect_stdout_file="GAL{0}.log".format(I) |
| 88 | + ) |
| 89 | + I = I + 1 |
| 90 | + galaxy.kinetic_energy = quantities.zero |
| 91 | + galaxy.potential_energy = quantities.zero |
| 92 | + galaxy.parameters.bar_contribution = True |
| 93 | + galaxy.parameters.bar_phase = self.bar_phase |
| 94 | + galaxy.parameters.omega_bar = self.omega_bar |
| 95 | + galaxy.parameters.mass_bar = self.mass_bar |
| 96 | + galaxy.parameters.aaxis_bar = self.aaxis_bar |
| 97 | + galaxy.parameters.axis_ratio_bar = self.axis_ratio_bar |
| 98 | + galaxy.parameters.spiral_contribution = False |
| 99 | + galaxy.parameters.spiral_phase = self.spiral_phase |
| 100 | + galaxy.parameters.omega_spiral = self.omega_spiral |
| 101 | + galaxy.parameters.amplitude = self.amplitude |
| 102 | + galaxy.parameters.rsp = self.rsp |
| 103 | + galaxy.parameters.m = self.m |
| 104 | + galaxy.parameters.tan_pitch_angle = self.tan_pitch_angle |
| 105 | + galaxy.commit_parameters() |
| 106 | + self.omega = galaxy.parameters.omega_system |
| 107 | + self.initial_phase = galaxy.parameters.initial_phase |
| 108 | + print("INITIAL_PHASE:", self.initial_phase) |
| 109 | + |
| 110 | + galaxy.kinetic_energy = quantities.zero |
| 111 | + galaxy.potential_energy = quantities.zero |
| 112 | + return galaxy |
| 113 | + |
| 114 | + def creation_particles_noinertial(self, particles): |
| 115 | + """ |
| 116 | + makes trans in a counterclockwise frame. |
| 117 | + If the Galaxy only has bar or only spiral arms, the frame corotates with |
| 118 | + the bar or with the spiral arms. If the Galaxy has bar and spiral arms, the frame corotates with the bar |
| 119 | + """ |
| 120 | + no_inertial_system = particles.copy() |
| 121 | + angle = self.initial_phase + self.omega * self.time |
| 122 | + C1 = particles.vx + self.omega * particles.y |
| 123 | + C2 = particles.vy - self.omega * particles.x |
| 124 | + no_inertial_system.x = particles.x * numpy.cos(angle) + particles.y * numpy.sin( |
| 125 | + angle |
| 126 | + ) |
| 127 | + no_inertial_system.y = -particles.x * numpy.sin( |
| 128 | + angle |
| 129 | + ) + particles.y * numpy.cos(angle) |
| 130 | + no_inertial_system.z = particles.z |
| 131 | + no_inertial_system.vx = C1 * numpy.cos(angle) + C2 * numpy.sin(angle) |
| 132 | + no_inertial_system.vy = C2 * numpy.cos(angle) - C1 * numpy.sin(angle) |
| 133 | + no_inertial_system.vz = particles.vz |
| 134 | + return no_inertial_system |
| 135 | + |
| 136 | + def noinertial_to_inertial(self, part_noin, part_in): |
| 137 | + # makes trans in a counterclockwise frame |
| 138 | + angle = self.initial_phase + self.omega * self.time |
| 139 | + C1 = part_noin.vx - part_noin.y * self.omega |
| 140 | + C2 = part_noin.vy + part_noin.x * self.omega |
| 141 | + part_in.x = part_noin.x * numpy.cos(angle) - part_noin.y * numpy.sin(angle) |
| 142 | + part_in.y = part_noin.x * numpy.sin(angle) + part_noin.y * numpy.cos(angle) |
| 143 | + part_in.z = part_noin.z |
| 144 | + part_in.vx = C1 * numpy.cos(angle) - C2 * numpy.sin(angle) |
| 145 | + part_in.vy = C1 * numpy.sin(angle) + C2 * numpy.cos(angle) |
| 146 | + part_in.vz = part_noin.vz |
| 147 | + return |
| 148 | + |
| 149 | + def testing_potential_and_force(self, galaxy, x, y, z): |
| 150 | + dx, dy, dz = 0.001 | units.kpc, 0.001 | units.kpc, 0.001 | units.kpc |
| 151 | + phi1x = galaxy.get_potential_at_point(0 | units.kpc, (x + dx), y, z) |
| 152 | + phi2x = galaxy.get_potential_at_point(0 | units.kpc, (x - dx), y, z) |
| 153 | + f1x = -(phi1x - phi2x) / (2 * dx) |
| 154 | + phi1y = galaxy.get_potential_at_point(0 | units.kpc, x, (y + dy), z) |
| 155 | + phi2y = galaxy.get_potential_at_point(0 | units.kpc, x, (y - dy), z) |
| 156 | + f1y = -(phi1y - phi2y) / (2 * dy) |
| 157 | + phi1z = galaxy.get_potential_at_point(0 | units.kpc, x, y, (z + dz)) |
| 158 | + phi2z = galaxy.get_potential_at_point(0 | units.kpc, x, y, (z - dz)) |
| 159 | + f1z = -(phi1z - phi2z) / (2 * dz) |
| 160 | + fx, fy, fz = galaxy.get_gravity_at_point(0 | units.kpc, x, y, z) |
| 161 | + print("analytic", "numerical") |
| 162 | + print( |
| 163 | + fx.value_in(100 * units.kms**2 / units.kpc), |
| 164 | + f1x.value_in(100 * units.kms**2 / units.kpc), |
| 165 | + ) |
| 166 | + print( |
| 167 | + fy.value_in(100 * units.kms**2 / units.kpc), |
| 168 | + f1y.value_in(100 * units.kms**2 / units.kpc), |
| 169 | + ) |
| 170 | + print( |
| 171 | + fz.value_in(100 * units.kms**2 / units.kpc), |
| 172 | + f1z.value_in(100 * units.kms**2 / units.kpc), |
| 173 | + ) |
| 174 | + return |
| 175 | + |
| 176 | + def get_pos_vel_and_orbit(self, particle_set): |
| 177 | + # particle_set.velocity= (-1)*particle_set.velocity |
| 178 | + |
| 179 | + filename = "sunandM67.hdf5" |
| 180 | + write_set_to_file( |
| 181 | + particle_set.savepoint(self.time), |
| 182 | + filename, |
| 183 | + "hdf5", |
| 184 | + append_to_file=False, |
| 185 | + overwrite_file=True, |
| 186 | + ) |
| 187 | + |
| 188 | + MW = self.galaxy() |
| 189 | + print("OMEGA:", self.omega.as_quantity_in(1 / units.Gyr)) |
| 190 | + particle_rot = self.creation_particles_noinertial(particle_set) |
| 191 | + gravless = drift_without_gravity(particle_rot) |
| 192 | + |
| 193 | + system = Rotating_Bridge( |
| 194 | + self.omega, timestep=self.dt_bridge, verbose=False, method=self.method |
| 195 | + ) |
| 196 | + system.add_system(gravless, (MW,), False) |
| 197 | + system.add_system(MW, (), False) # This is to update time inside the interface |
| 198 | + |
| 199 | + Ei = ( |
| 200 | + system.potential_energy |
| 201 | + + system.kinetic_energy |
| 202 | + + system.jacobi_potential_energy |
| 203 | + ) |
| 204 | + energy = [] |
| 205 | + |
| 206 | + dmin = (particle_set[0].position - particle_set[1].position).length() |
| 207 | + tmin = 0 | units.Myr |
| 208 | + d = [] | units.kpc |
| 209 | + t = [] | units.Myr |
| 210 | + d.append(dmin) |
| 211 | + t.append(self.time) |
| 212 | + while self.time < self.t_end - self.dt_bridge / 2: |
| 213 | + self.time += self.dt_bridge |
| 214 | + system.evolve_model(self.time) |
| 215 | + self.noinertial_to_inertial(particle_rot, particle_set) |
| 216 | + |
| 217 | + Ef = ( |
| 218 | + system.potential_energy |
| 219 | + + system.kinetic_energy |
| 220 | + + system.jacobi_potential_energy |
| 221 | + ) |
| 222 | + dje = (Ef - Ei) / Ei |
| 223 | + energy.append(dje) |
| 224 | + |
| 225 | + d.append((particle_set[0].position - particle_set[1].position).length()) |
| 226 | + t.append(self.time) |
| 227 | + if d[-1] < dmin: |
| 228 | + dmin = d[-1] |
| 229 | + tmin = self.time |
| 230 | + |
| 231 | + x = particle_set.x |
| 232 | + y = particle_set.y |
| 233 | + write_set_to_file( |
| 234 | + particle_set.savepoint(self.time), filename, "hdf5", overwrite_file=True |
| 235 | + ) |
| 236 | + |
| 237 | + print("minimum", tmin.in_(units.Myr), dmin.in_(units.parsec)) |
| 238 | + bar_angle = self.bar_phase + (self.omega_bar * self.time) |
| 239 | + spiral_angle = self.spiral_phase + (self.omega_spiral * self.time) |
| 240 | + |
| 241 | + return ( |
| 242 | + self.time, |
| 243 | + particle_set[0].x.value_in(units.kpc), |
| 244 | + particle_set[0].y.value_in(units.kpc), |
| 245 | + particle_set[0].z.value_in(units.kpc), |
| 246 | + particle_set[0].vx.value_in(units.kms), |
| 247 | + particle_set[0].vy.value_in(units.kms), |
| 248 | + particle_set[0].vz.value_in(units.kms), |
| 249 | + bar_angle, |
| 250 | + spiral_angle, |
| 251 | + t, |
| 252 | + d, |
| 253 | + ) |
| 254 | + |
| 255 | + |
| 256 | +def Sun_and_M67_in_the_Galaxy(): |
| 257 | + |
| 258 | + bodies = Particles(2) |
| 259 | + Sun = bodies[0] |
| 260 | + v_LSR = (-10, 5.2, 7.2) | units.kms |
| 261 | + Sun.mass = 1 | units.MSun |
| 262 | + Sun.radius = 1 | units.RSun |
| 263 | + Sun.position = (8.4, 0.0, 0.017) | units.kpc |
| 264 | + Sun.velocity = (-11.4, 232, 7.41) | units.kms # SPZ2009 |
| 265 | + |
| 266 | + M67 = bodies[1] |
| 267 | + M67.mass = 50000 | units.MSun |
| 268 | + M67.radius = 3 | units.parsec |
| 269 | + M67.position = Sun.position + ((0.766, 0.0, 0.49) | units.kpc) |
| 270 | + M67.velocity = Sun.velocity + ((31.92, -21.66, -8.87) | units.kms) |
| 271 | + |
| 272 | + bodies.velocity *= -1 |
| 273 | + |
| 274 | + simulation_time = 4600.0 | units.Myr |
| 275 | + dt_bridge = 5 | units.Myr |
| 276 | + OS = 20 | (units.kms / units.kpc) |
| 277 | + OB = 40 | (units.kms / units.kpc) |
| 278 | + A = 1300 | (units.kms**2 / units.kpc) |
| 279 | + M = 1.4e10 | units.MSun |
| 280 | + m = 2 |
| 281 | + phi_bar, phi_sp = -0.34906, -0.34906 |
| 282 | + inte = IntegrateOrbit( |
| 283 | + t_end=simulation_time, |
| 284 | + dt_bridge=dt_bridge, |
| 285 | + phase_bar=phi_bar, |
| 286 | + phase_spiral=phi_sp, |
| 287 | + omega_spiral=OS, |
| 288 | + omega_bar=OB, |
| 289 | + amplitude=A, |
| 290 | + m=m, |
| 291 | + mass_bar=M, |
| 292 | + ) |
| 293 | + |
| 294 | + MW = inte.galaxy() |
| 295 | + print(MW.parameters) |
| 296 | + print(MW.get_phi21()) |
| 297 | + |
| 298 | + print("Backwards integration") |
| 299 | + time, xf, yf, zf, vxf, vyf, vzf, bar_angle, spiral_angle, t1, d1 = ( |
| 300 | + inte.get_pos_vel_and_orbit(bodies) |
| 301 | + ) |
| 302 | + print("Birth position of the Sun:", xf, yf, zf, vxf, vyf, vzf) |
| 303 | + print("---") |
| 304 | + print("time after backward integration:", time) |
| 305 | + |
| 306 | + plt.style.use("../lib/matplotlibrc") |
| 307 | + figure = plt.figure()# figsize=(16, 12)) |
| 308 | + ax = plt.gca() |
| 309 | + ax.minorticks_on() # switch on the minor ticks |
| 310 | + ax.locator_params(nbins=3) |
| 311 | + |
| 312 | + x_label = "t [Gyr]" |
| 313 | + y_label = "d [kpc]" |
| 314 | + plt.xlabel(x_label) |
| 315 | + plt.ylabel(y_label) |
| 316 | + plt.plot(-t1.value_in(units.Gyr), d1.value_in(units.kpc), lw=3) |
| 317 | + plt.ylim(0, 6) |
| 318 | + plt.xlim(-5, 0) |
| 319 | + plt.savefig("sun_and_M67") |
| 320 | + plt.show() |
| 321 | + |
| 322 | + """ |
| 323 | + from matplotlib import pyplot |
| 324 | + from amuse.plot import scatter, plot |
| 325 | + plot(-1*t1, d1, c = "k") |
| 326 | + pyplot.semilogy() |
| 327 | + pyplot.show() |
| 328 | + """ |
| 329 | + |
| 330 | + |
| 331 | +if __name__ in ("__main__", "__plot__"): |
| 332 | + set_printing_strategy( |
| 333 | + "custom", # nbody_converter = converter, |
| 334 | + preferred_units=[units.MSun, units.kpc, units.Myr], |
| 335 | + precision=4, |
| 336 | + prefix="", |
| 337 | + separator=" [", |
| 338 | + suffix="]", |
| 339 | + ) |
| 340 | + Sun_and_M67_in_the_Galaxy() |
0 commit comments