2021-01-02 18:23:39 +01:00
|
|
|
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
|
2021-03-22 13:02:50 +01:00
|
|
|
from dataclasses import dataclass
|
2021-01-02 18:23:39 +01:00
|
|
|
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
|
2020-03-31 15:39:59 +02:00
|
|
|
|
2021-02-19 13:57:47 +01:00
|
|
|
import rebound
|
2021-03-22 13:02:50 +01:00
|
|
|
import yaml
|
2021-02-08 21:05:44 +01:00
|
|
|
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
|
2021-02-08 21:05:44 +01:00
|
|
|
from merge import merge_particles
|
2021-03-08 15:43:45 +01:00
|
|
|
from radius_utils import PlanetaryRadius
|
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-03-08 19:29:40 +01:00
|
|
|
third_kepler_law, solar_radius, git_hash, check_heartbeat_needs_recompile
|
2020-03-31 15:39:59 +02:00
|
|
|
|
2020-12-30 20:41:30 +01: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
|
|
|
|
2021-02-08 21:05:44 +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
|
|
|
|
|
|
|
|
|
2021-03-22 13:02:50 +01:00
|
|
|
@dataclass
|
|
|
|
class Parameters:
|
|
|
|
initcon_file: str
|
|
|
|
perfect_merging: bool
|
2021-05-03 16:53:34 +02:00
|
|
|
no_merging: bool = False
|
2021-03-22 13:02:50 +01:00
|
|
|
|
|
|
|
|
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:
|
2021-03-23 11:22:17 +01:00
|
|
|
parameters = Parameters(perfect_merging=True, 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
|
2021-05-03 16:50:29 +02:00
|
|
|
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
|
2021-02-10 14:40:01 +01:00
|
|
|
extradata.meta.git_hash = git_hash()
|
2021-02-19 13:57:47 +01:00
|
|
|
extradata.meta.git_hash = rebound.__githash__
|
2021-03-22 13:02:50 +01:00
|
|
|
extradata.meta.perfect_merging = parameters.perfect_merging
|
|
|
|
extradata.meta.initcon_file = parameters.initcon_file
|
2021-05-03 16:50:29 +02:00
|
|
|
extradata.meta.no_merging = parameters.no_merging
|
2021-01-08 15:10:38 +01:00
|
|
|
|
2021-03-22 13:02:50 +01:00
|
|
|
initcon = Path(parameters.initcon_file).read_text()
|
2021-01-08 15:10:38 +01:00
|
|
|
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()))
|
2021-02-11 21:14:29 +01:00
|
|
|
hash = unique_hash(extradata)
|
2021-01-08 15:10:38 +01:00
|
|
|
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}")
|
2021-03-08 15:43:45 +01:00
|
|
|
print("adding rest to mmf")
|
|
|
|
mmf += diff
|
2021-01-08 15:10:38 +01:00
|
|
|
assert cmf + mmf + wmf - 1 <= 1e-10
|
|
|
|
if i > num_embryos + 3:
|
|
|
|
object_type = "planetesimal"
|
|
|
|
else:
|
|
|
|
object_type = "embryo"
|
2020-12-30 20:41:30 +01:00
|
|
|
else:
|
2021-01-08 15:10:38 +01:00
|
|
|
wmf = mmf = 0
|
|
|
|
cmf = 1
|
|
|
|
if columns[1] == 0:
|
|
|
|
object_type = "sun"
|
|
|
|
else:
|
|
|
|
object_type = "gas giant"
|
|
|
|
extradata.pdata[hash.value] = ParticleData(
|
|
|
|
water_mass_fraction=wmf,
|
2021-03-08 15:43:45 +01:00
|
|
|
core_mass_fraction=cmf,
|
2021-02-03 22:04:34 +01:00
|
|
|
type=object_type,
|
|
|
|
total_mass=columns[0]
|
2021-01-02 18:23:39 +01:00
|
|
|
)
|
2021-01-08 15:10:38 +01:00
|
|
|
|
|
|
|
if columns[1] == 0: # that should not be needed, but nevertheless is
|
2021-02-08 19:03:51 +01:00
|
|
|
part = Particle(m=columns[0], hash=hash, r=solar_radius / astronomical_unit)
|
2021-01-08 15:10:38 +01:00
|
|
|
else:
|
|
|
|
part = Particle(
|
|
|
|
m=columns[0], a=columns[1], e=columns[2],
|
2021-03-23 13:21:20 +01:00
|
|
|
inc=columns[3], omega=columns[4],
|
2021-01-08 15:10:38 +01:00
|
|
|
Omega=columns[5], M=columns[6],
|
|
|
|
simulation=sim,
|
|
|
|
hash=hash,
|
2021-03-08 19:29:40 +01:00
|
|
|
r=PlanetaryRadius(columns[0], wmf, cmf).total_radius / astronomical_unit
|
2021-01-08 15:10:38 +01:00
|
|
|
)
|
|
|
|
sim.add(part)
|
|
|
|
i += 1
|
|
|
|
assert sim.N == num_planetesimals + num_embryos + 3
|
|
|
|
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
|
2021-02-11 21:14:29 +01:00
|
|
|
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()
|
|
|
|
|
2021-02-10 14:40:01 +01:00
|
|
|
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
|
|
|
|
|
2021-03-08 19:29:40 +01:00
|
|
|
check_heartbeat_needs_recompile()
|
2021-02-08 21:05:44 +01:00
|
|
|
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)
|
2021-02-08 21:05:44 +01:00
|
|
|
sim.heartbeat = clibheartbeat.heartbeat
|
|
|
|
innermost_semimajor_axis = third_kepler_law(
|
|
|
|
orbital_period=sim.dt * year * MIN_TIMESTEP_PER_ORBIT
|
|
|
|
) / astronomical_unit
|
|
|
|
print(f"innermost semimajor axis is {innermost_semimajor_axis}")
|
|
|
|
|
|
|
|
c_double.in_dll(clibheartbeat, "min_distance_from_sun_squared").value = innermost_semimajor_axis ** 2
|
|
|
|
c_double.in_dll(clibheartbeat, "max_distance_from_sun_squared").value = 30 ** 2
|
|
|
|
|
2020-12-30 20:41:30 +01:00
|
|
|
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}%")
|
|
|
|
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-08 21:05:44 +01:00
|
|
|
|
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
|
|
|
|
|
2021-02-08 21:05:44 +01:00
|
|
|
escape: 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
|
|
|
|
print(c_int.in_dll(clibheartbeat, "hb_sun_collision_index").value, "found")
|
|
|
|
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()
|