1
0
Fork 0
mirror of https://github.com/Findus23/rebound-collisions.git synced 2024-09-19 15:53:48 +02:00
rebound-collisions/merge.py

211 lines
7.6 KiB
Python
Raw Normal View History

2021-01-02 16:30:39 +01:00
from copy import copy
2021-01-21 14:28:29 +01:00
from pprint import pprint
2021-03-08 19:38:35 +01:00
from typing import Tuple, Optional
2020-03-31 15:39:59 +02:00
import numpy as np
from numpy import linalg, sqrt
from rebound import Simulation, Particle
2021-01-21 14:28:29 +01:00
from rebound.simulation import POINTER_REB_SIM, reb_collision
2021-01-02 16:30:39 +01:00
from scipy.constants import astronomical_unit, G
2020-03-31 15:39:59 +02:00
2021-01-02 16:30:39 +01:00
from extradata import ExtraData, ParticleData, CollisionMeta, Input
2021-03-08 19:38:35 +01:00
from merge_interpolation import Interpolation
2021-03-08 15:43:45 +01:00
from radius_utils import PlanetaryRadius
from utils import unique_hash, clamp
2020-03-31 15:39:59 +02:00
2021-03-08 19:38:35 +01:00
interpolation: Optional[Interpolation] = None # global rbf cache
2020-03-31 15:39:59 +02:00
2021-03-23 11:22:17 +01:00
def get_mass_fractions(input_data: Input, perfect_merging: bool) -> Tuple[float, float, float, CollisionMeta]:
2021-03-08 19:38:35 +01:00
global interpolation
2021-01-02 16:30:39 +01:00
print("v_esc", input_data.escape_velocity)
print("v_orig,v_si", input_data.velocity_original, input_data.velocity_si)
print("v/v_esc", input_data.velocity_esc)
data = copy(input_data)
if data.gamma > 1:
data.gamma = 1 / data.gamma
data.alpha = clamp(data.alpha, 0, 60)
data.velocity_esc = clamp(data.velocity_esc, 1, 5)
2020-03-31 15:39:59 +02:00
m_ceres = 9.393e+20
m_earth = 5.9722e+24
2021-01-02 16:30:39 +01:00
data.projectile_mass = clamp(data.projectile_mass, 2 * m_ceres, 2 * m_earth)
data.gamma = clamp(data.gamma, 1 / 10, 1)
2020-03-31 15:39:59 +02:00
2021-03-23 11:22:17 +01:00
if perfect_merging:
water_retention = mantle_retention = core_retention = 1
else:
if not interpolation:
interpolation = Interpolation()
water_retention, mantle_retention, core_retention = \
interpolation.interpolate(data.alpha, data.velocity_esc, data.projectile_mass, data.gamma)
2021-01-02 16:30:39 +01:00
metadata = CollisionMeta()
metadata.interpolation_input = [data.alpha, data.velocity_esc, data.projectile_mass, data.gamma]
metadata.input = input_data
metadata.adjusted_input = data
metadata.raw_water_retention = water_retention
2021-03-08 15:43:45 +01:00
metadata.raw_mantle_retention = mantle_retention
metadata.raw_core_retention = core_retention
2020-03-31 15:39:59 +02:00
water_retention = clamp(water_retention, 0, 1)
2021-03-08 15:43:45 +01:00
mantle_retention = clamp(mantle_retention, 0, 1)
core_retention = clamp(core_retention, 0, 1)
2020-03-31 15:39:59 +02:00
2021-01-02 16:30:39 +01:00
metadata.water_retention = water_retention
2021-03-08 15:43:45 +01:00
metadata.mantle_retention = mantle_retention
metadata.core_retention = core_retention
2020-03-31 19:12:27 +02:00
2021-03-08 15:43:45 +01:00
return water_retention, mantle_retention, core_retention, metadata
2020-03-31 15:39:59 +02:00
2021-01-21 14:28:29 +01:00
def merge_particles(sim_p: POINTER_REB_SIM, collision: reb_collision, ed: ExtraData):
2021-01-21 16:45:31 +01:00
print("--------------")
2020-03-31 15:39:59 +02:00
print("colliding")
2021-01-21 14:28:29 +01:00
sim: Simulation = sim_p.contents
print("current time step", sim.dt)
2021-01-21 16:45:31 +01:00
print("mode", sim.ri_mercurius.mode)
print(f"p1 is {collision.p1}")
print(f"p2 is {collision.p2}")
# the assignment to cp1 or cp2 seems to be random
2021-01-21 14:28:29 +01:00
# also look at a copy instead of the original particles
# to avoid issues after they have been modified
cp1: Particle = sim.particles[collision.p1].copy()
cp2: Particle = sim.particles[collision.p2].copy()
2020-03-31 15:39:59 +02:00
# just calling the more massive one the target to keep its type/name
2020-12-30 23:16:28 +01:00
# Sun<->Protoplanet -> Sun
# and to keep collsions mostly reproducable
2021-01-21 18:50:33 +01:00
if cp1.m > cp2.m:
target = cp1
projectile = cp2
else: # also when masses are the same
target = cp2
projectile = cp1
if collision.p1 > collision.p2:
lower_index_particle_index = collision.p2
2021-01-21 18:50:33 +01:00
else:
lower_index_particle_index = collision.p1
print(f"colliding {target.hash.value} ({ed.pd(target).type}) "
f"with {projectile.hash.value} ({ed.pd(projectile).type})")
projectile_wmf = ed.pd(projectile).water_mass_fraction
2021-03-08 15:43:45 +01:00
projectile_cmf = ed.pd(projectile).core_mass_fraction
target_wmf = ed.pd(target).water_mass_fraction
2021-03-08 15:43:45 +01:00
target_cmf = ed.pd(target).core_mass_fraction
2020-03-31 15:39:59 +02:00
2020-12-30 23:16:28 +01:00
# get the velocities, velocity differences and unit vector as numpy arrays
# all units are in sytem units (so AU/year)
v1 = np.array(target.vxyz)
v2 = np.array(projectile.vxyz)
r1 = np.array(target.xyz)
r2 = np.array(projectile.xyz)
2021-01-21 14:28:29 +01:00
vdiff = v2 - v1
rdiff = r2 - r1
vdiff_n = linalg.norm(vdiff)
rdiff_n = linalg.norm(rdiff)
print("dt", sim.dt)
2021-01-25 15:20:45 +01:00
# during a collision ias15 should always be used, otherwise something weird has happend
assert sim.ri_mercurius.mode == 1
2021-01-21 14:28:29 +01:00
print("rdiff", rdiff)
print("vdiff", vdiff)
print("sum_radii", target.r + projectile.r)
2021-01-21 14:28:29 +01:00
print("rdiff_n", rdiff_n)
print("vdiff_n", vdiff_n)
ang = float(np.degrees(np.arccos(np.dot(rdiff, vdiff) / (rdiff_n * vdiff_n))))
if ang > 90:
ang = 180 - ang
2021-01-02 16:30:39 +01:00
print("angle_deg", ang)
2021-01-21 14:28:29 +01:00
print()
2020-12-30 23:16:28 +01:00
# get mass fraction
gamma = projectile.m / target.m
2020-03-31 15:39:59 +02:00
2021-01-02 16:30:39 +01:00
# calculate mutual escape velocity (for norming the velocities in the interpolation) in SI units
escape_velocity = sqrt(2 * G * (target.m + projectile.m) / ((target.r + projectile.r) * astronomical_unit))
2020-03-31 15:39:59 +02:00
print("interpolating")
2020-12-30 23:16:28 +01:00
# let interpolation calculate water and mass retention fraction
# meta is just a bunch of intermediate results that will be logged to help
# understand the collisions better
2021-01-02 16:30:39 +01:00
input_data = Input(
alpha=ang,
2021-01-21 14:28:29 +01:00
velocity_original=vdiff_n,
2021-01-02 16:30:39 +01:00
escape_velocity=escape_velocity,
gamma=gamma,
projectile_mass=projectile.m,
2021-01-02 16:30:39 +01:00
target_water_fraction=target_wmf,
projectile_water_fraction=projectile_wmf,
)
2021-03-23 11:22:17 +01:00
water_ret, mantle_ret, core_ret, meta = get_mass_fractions(input_data, ed.meta.perfect_merging)
2020-03-31 15:39:59 +02:00
2021-01-02 16:30:39 +01:00
meta.collision_velocities = (v1.tolist(), v2.tolist())
meta.collision_positions = (target.xyz, projectile.xyz)
meta.collision_radii = (target.r, projectile.r)
2021-01-02 16:30:39 +01:00
hash = unique_hash(ed) # hash for newly created particle
2020-12-30 23:16:28 +01:00
# handle loss of water and core mass
water_mass = target.m * target_wmf + projectile.m * projectile_wmf
2021-03-08 15:43:45 +01:00
core_mass = target.m * target_cmf + projectile.m * projectile_cmf
mantle_mass = target.m + projectile.m - water_mass - core_mass
2020-03-31 15:39:59 +02:00
water_mass *= water_ret
2021-03-08 15:43:45 +01:00
mantle_mass *= mantle_ret
core_mass *= core_ret
2020-03-31 15:39:59 +02:00
2021-03-08 15:43:45 +01:00
total_mass = water_mass + mantle_mass + core_mass
2020-03-31 15:39:59 +02:00
final_wmf = water_mass / total_mass
2021-03-08 15:43:45 +01:00
final_cmf = core_mass / total_mass
2020-03-31 15:39:59 +02:00
print(final_wmf)
2020-12-30 23:16:28 +01:00
# create new object preserving momentum
merged_planet = (target * target.m + projectile * projectile.m) / total_mass
2020-03-31 15:39:59 +02:00
merged_planet.m = total_mass
merged_planet.hash = hash
2021-03-08 15:43:45 +01:00
merged_planet.r = PlanetaryRadius(merged_planet.m, final_wmf, final_cmf).total_radius / astronomical_unit
ed.pdata[hash.value] = ParticleData(
water_mass_fraction=final_wmf,
2021-03-08 15:43:45 +01:00
core_mass_fraction=final_cmf,
type=ed.pd(target).type,
2021-02-03 22:04:34 +01:00
total_mass=total_mass
)
2020-03-31 15:39:59 +02:00
2021-01-02 16:30:39 +01:00
meta.final_wmf = final_wmf
meta.final_radius = merged_planet.r
meta.target_wmf = target_wmf
meta.projectile_wmf = projectile_wmf
meta.time = sim.t
2021-01-21 14:28:29 +01:00
pprint(meta)
2020-03-31 15:39:59 +02:00
ed.tree.add(target, projectile, merged_planet, meta)
2020-03-31 15:39:59 +02:00
sim.particles[lower_index_particle_index] = merged_planet
sim.move_to_com()
sim.integrator_synchronize()
2021-01-21 14:28:29 +01:00
sim.ri_mercurius.recalculate_coordinates_this_timestep = 1
sim.ri_mercurius.recalculate_dcrit_this_timestep = 1
2021-01-21 16:45:31 +01:00
print("collision finished")
print("--------------")
2021-01-21 14:28:29 +01:00
# from rebound docs:
# A return value of 0 indicates that both particles remain in the simulation.
# A return value of 1 (2) indicates that particle 1 (2) should be removed from the simulation.
# A return value of 3 indicates that both particles should be removed from the simulation.
# always keep lower index particle and delete other one
# this keeps the N_active working
if lower_index_particle_index == collision.p1:
print("deleting p2")
2021-01-21 18:50:33 +01:00
return 2
2021-02-26 21:12:39 +01:00
elif lower_index_particle_index == collision.p2:
print("deleting p1")
2021-01-21 18:50:33 +01:00
return 1
2021-02-26 21:12:39 +01:00
else:
raise ValueError("invalid index")