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

298 lines
11 KiB
Python
Raw Normal View History

import re
2020-03-31 15:39:59 +02:00
import time
2021-04-13 17:10:58 +02:00
from ctypes import Structure, c_uint32, c_double, c_uint, cdll, c_int, create_string_buffer, c_char_p
from dataclasses import dataclass
from pathlib import Path
2021-01-02 16:30:39 +01:00
from shutil import copy
2021-02-03 22:04:34 +01:00
from sys import argv
2021-09-13 15:56:15 +02:00
from typing import Tuple
2020-03-31 15:39:59 +02:00
2021-02-19 13:57:47 +01:00
import rebound
import yaml
from rebound import Simulation, Particle, NoParticles, SimulationArchive
2021-01-21 14:28:29 +01:00
from rebound.simulation import POINTER_REB_SIM, reb_collision
2021-02-08 19:03:51 +01:00
from scipy.constants import astronomical_unit, mega, year
2020-03-31 15:39:59 +02:00
from extradata import ExtraData, ParticleData
from merge import merge_particles
2021-02-08 19:03:51 +01:00
from utils import unique_hash, filename_from_argv, innermost_period, total_momentum, process_friendlyness, total_mass, \
2021-06-26 20:42:41 +02:00
third_kepler_law, solar_radius, git_hash, check_heartbeat_needs_recompile, PlanetaryRadius, set_process_title
2020-03-31 15:39:59 +02:00
MIN_TIMESTEP_PER_ORBIT = 20
2020-03-31 15:39:59 +02:00
2021-01-24 12:13:21 +01:00
abort = False
2021-01-08 15:10:38 +01:00
class hb_event(Structure):
_fields_ = [("hash", c_uint32),
("time", c_double),
("new", c_uint)]
hash: int
time: float
new: int
hb_event_list = hb_event * 500
@dataclass
class Parameters:
initcon_file: str
massloss_method: str
2021-05-03 16:53:34 +02:00
no_merging: bool = False
2021-09-13 15:56:15 +02:00
def add_particles_from_conditions_file(sim: Simulation, ed: ExtraData,
input_file: str, testrun=False) -> Tuple[int, int]:
initcon = Path(input_file).read_text()
num_embryos = int(re.search(r"Generated (\d+) minor bodies", initcon, re.MULTILINE).group(1))
num_planetesimals = int(re.search(r"Generated (\d+) small bodies", initcon, re.MULTILINE).group(1))
sim.N_active = num_embryos + 3
i = 1
for line in initcon.split("\n"):
if line.startswith("#") or line.startswith("ERROR") or line == "\n" or not line:
continue
columns = list(map(float, line.split()))
hash = unique_hash(ed)
if len(columns) > 7:
cmf, mmf, wmf = columns[7:]
total_fractions = cmf + mmf + wmf
if total_fractions != 1:
diff = 1 - total_fractions
print(f"fractions don't add up by {diff}")
print("adding rest to mmf")
mmf += diff
assert cmf + mmf + wmf - 1 <= 1e-10
if i > num_embryos + 3:
object_type = "planetesimal"
else:
object_type = "embryo"
else:
wmf = mmf = 0
cmf = 1
if columns[1] == 0:
object_type = "sun"
else:
object_type = "gas giant"
ed.pdata[hash.value] = ParticleData(
water_mass_fraction=wmf,
core_mass_fraction=cmf,
type=object_type,
total_mass=columns[0]
)
if columns[1] == 0: # that should not be needed, but nevertheless is
part = Particle(m=columns[0], hash=hash, r=solar_radius / astronomical_unit)
else:
inc = columns[3]
if testrun:
# force particles to collide early when running tests
inc /= 1000
part = Particle(
m=columns[0], a=columns[1], e=columns[2],
inc=inc, omega=columns[4],
Omega=columns[5], M=columns[6],
simulation=sim,
hash=hash,
r=PlanetaryRadius(columns[0], wmf, cmf).total_radius / astronomical_unit
)
sim.add(part)
i += 1
assert sim.N == num_planetesimals + num_embryos + 3
return num_planetesimals, num_embryos
2021-02-03 22:04:34 +01:00
def main(fn: Path, testrun=False):
2021-01-24 12:13:21 +01:00
global abort
2021-01-08 15:10:38 +01:00
start = time.perf_counter()
if not fn.with_suffix(".bin").exists():
2021-03-22 13:10:57 +01:00
if not testrun:
with open(fn.with_suffix(".yaml")) as f:
2021-03-22 13:17:45 +01:00
parameters = Parameters(**yaml.safe_load(f))
2021-03-22 13:10:57 +01:00
else:
parameters = Parameters(massloss_method="rbf", initcon_file="initcon/conditions_many.input")
2021-01-08 15:10:38 +01:00
# set up a fresh simulation
sim = Simulation()
sim.units = ('yr', 'AU', 'kg')
# sim.boundary = "open"
# boxsize = 100
# sim.configure_box(boxsize)
sim.integrator = "mercurius"
sim.dt = 1e-2
sim.ri_ias15.min_dt = 0.0001 / 365
if not parameters.no_merging:
sim.collision = "direct"
2021-01-08 15:10:38 +01:00
sim.ri_mercurius.hillfac = 3.
2021-02-19 19:58:22 +01:00
sim.testparticle_type = 1
2021-01-23 14:41:41 +01:00
tmax = 200 * mega
2021-01-08 15:10:38 +01:00
num_savesteps = 20000
2021-02-03 22:04:34 +01:00
if testrun:
tmax /= 200000
num_savesteps /= 1000
2021-01-08 15:10:38 +01:00
per_savestep = tmax / num_savesteps
extradata = ExtraData()
# times = np.linspace(0., tmax, savesteps)
extradata.meta.tmax = tmax
extradata.meta.per_savestep = per_savestep
extradata.meta.num_savesteps = num_savesteps
extradata.meta.git_hash = git_hash()
2021-08-16 12:57:22 +02:00
extradata.meta.rebound_hash = rebound.__githash__
extradata.meta.massloss_method = parameters.massloss_method
extradata.meta.initcon_file = parameters.initcon_file
extradata.meta.no_merging = parameters.no_merging
2021-01-08 15:10:38 +01:00
2021-09-13 15:56:15 +02:00
num_planetesimals, num_embryos = \
add_particles_from_conditions_file(sim, extradata, parameters.initcon_file, testrun)
2021-01-08 15:10:38 +01:00
sim.move_to_com()
extradata.meta.initial_N = sim.N
extradata.meta.initial_N_planetesimal = num_planetesimals
extradata.meta.initial_N_embryo = num_embryos
2021-02-04 13:42:22 +01:00
extradata.history.append(
energy=sim.calculate_energy(),
momentum=total_momentum(sim),
total_mass=total_mass(sim),
time=sim.t,
N=sim.N,
N_active=sim.N_active
)
2021-01-08 15:10:38 +01:00
cputimeoffset = walltimeoffset = 0
t = 0
else:
if fn.with_suffix(".lock").exists():
raise FileExistsError("Lock file found, is the simulation currently running?")
copy(fn.with_suffix(".bin"), fn.with_suffix(".bak.bin"))
copy(fn.with_suffix(".extra.json"), fn.with_suffix(".extra.bak.json"))
sa = SimulationArchive(str(fn.with_suffix(".bin")))
2021-02-04 13:42:22 +01:00
extradata = ExtraData.load(fn)
2021-01-08 15:10:38 +01:00
tmax = extradata.meta.tmax
per_savestep = extradata.meta.per_savestep
sim = sa[-1]
t = round(sim.t + per_savestep)
print(f"continuing from {t}")
2021-01-08 15:10:38 +01:00
sim.move_to_com()
2021-01-25 15:20:45 +01:00
sim.ri_mercurius.recalculate_coordinates_this_timestep = 1
2021-01-08 15:10:38 +01:00
sim.integrator_synchronize()
if extradata.meta.git_hash != git_hash():
print("warning: The saved output was originally run with another version of the code")
print(f"original: {extradata.meta.git_hash}")
print(f"current: {git_hash()}")
2021-01-08 15:10:38 +01:00
num_savesteps = extradata.meta.num_savesteps
cputimeoffset = extradata.meta.cputime
walltimeoffset = extradata.meta.walltime
check_heartbeat_needs_recompile()
clibheartbeat = cdll.LoadLibrary("heartbeat/heartbeat.so")
2021-04-13 17:10:58 +02:00
clibheartbeat.init_logfile.argtypes = [c_char_p]
logfile = create_string_buffer(128)
logfile.value = str(fn.with_suffix(".energylog.csv")).encode()
clibheartbeat.init_logfile(logfile)
sim.heartbeat = clibheartbeat.heartbeat
innermost_semimajor_axis = third_kepler_law(
orbital_period=sim.dt * year * MIN_TIMESTEP_PER_ORBIT
2021-09-13 15:57:46 +02:00
) / astronomical_unit * 1.1
print(f"innermost semimajor axis is {innermost_semimajor_axis}")
c_double.in_dll(clibheartbeat, "min_distance_from_sun_squared").value = innermost_semimajor_axis ** 2
2021-08-20 18:00:40 +02:00
c_double.in_dll(clibheartbeat, "max_distance_from_sun_squared").value = 150 ** 2
assert sim.dt < innermost_period(sim) / MIN_TIMESTEP_PER_ORBIT
2021-01-08 15:10:38 +01:00
2021-01-21 14:28:29 +01:00
def collision_resolve_handler(sim_p: POINTER_REB_SIM, collision: reb_collision) -> int:
2021-01-24 12:13:21 +01:00
global abort # needed as exceptions don't halt integration
try:
return merge_particles(sim_p, collision, ed=extradata)
except BaseException as exception:
print("exception during collision_resolve")
print(exception)
abort = True
sim_p.contents._status = 1
raise exception
2021-01-21 14:28:29 +01:00
sim.collision_resolve = collision_resolve_handler
2021-01-08 15:10:38 +01:00
# show_orbits(sim)
fn.with_suffix(".lock").touch()
print("start")
while t <= tmax:
print()
print(f"{t / tmax * 100:.2f}%")
2021-06-26 20:42:41 +02:00
set_process_title(fn, t / tmax, sim.N)
2021-01-08 15:10:38 +01:00
try:
print(f"integrating until {t}")
sim.integrate(t, exact_finish_time=0)
print("dt", sim.dt)
print("t", t)
t += per_savestep
except NoParticles:
print("No Particles left")
abort = True
print("N", sim.N)
print("N_active", sim.N_active)
2021-02-11 14:39:56 +01:00
print("fraction", innermost_period(sim) / MIN_TIMESTEP_PER_ORBIT)
assert sim.dt < innermost_period(sim) / MIN_TIMESTEP_PER_ORBIT
escape: hb_event
2021-08-20 18:00:40 +02:00
wide_orbit: hb_event
sun_collision: hb_event
for escape in hb_event_list.in_dll(clibheartbeat, "hb_escapes"):
if not escape.new:
continue
print("escape:", escape.time, escape.hash)
extradata.pdata[escape.hash].escaped = escape.time
escape.new = 0 # make sure to not handle it again
c_int.in_dll(clibheartbeat, "hb_escape_index").value = 0
for sun_collision in hb_event_list.in_dll(clibheartbeat, "hb_sun_collisions"):
if not sun_collision.new:
continue
print("sun collision:", sun_collision.time, sun_collision.hash)
extradata.pdata[sun_collision.hash].collided_with_sun = sun_collision.time
sun_collision.new = 0
2021-08-20 18:00:40 +02:00
c_int.in_dll(clibheartbeat, "hb_sun_collision_index").value = 0
for wide_orbit in hb_event_list.in_dll(clibheartbeat, "hb_wide_orbits"):
if not wide_orbit.new:
continue
print("wide orbit:", wide_orbit.time, wide_orbit.hash)
extradata.pdata[wide_orbit.hash].wide_orbit = wide_orbit.time
wide_orbit.new = 0
c_int.in_dll(clibheartbeat, "hb_sun_collision_index").value = 0
2021-01-08 15:10:38 +01:00
sim.simulationarchive_snapshot(str(fn.with_suffix(".bin")))
extradata.meta.walltime = time.perf_counter() - start + walltimeoffset
extradata.meta.cputime = time.process_time() + cputimeoffset
extradata.meta.current_time = t
2021-02-04 13:42:22 +01:00
extradata.history.append(
energy=sim.calculate_energy(),
momentum=total_momentum(sim),
total_mass=total_mass(sim),
time=sim.t,
N=sim.N,
N_active=sim.N_active
)
extradata.save(fn)
2021-01-08 15:10:38 +01:00
if abort:
2021-02-11 14:39:56 +01:00
print("aborted")
2021-01-08 15:10:38 +01:00
exit(1)
print("finished")
fn.with_suffix(".lock").unlink()
if __name__ == '__main__':
fn = filename_from_argv()
2021-01-23 14:41:41 +01:00
process_friendlyness(fn)
2021-02-03 22:04:34 +01:00
testrun = False
if len(argv) > 2 and argv[2] == "test":
testrun = True
2021-01-08 15:10:38 +01:00
try:
2021-02-03 22:04:34 +01:00
main(fn, testrun)
2021-01-08 15:10:38 +01:00
except KeyboardInterrupt:
print("aborting")
lockfile = fn.with_suffix(".lock")
print(f"deleting {lockfile}")
lockfile.unlink()