예제 #1
0
def add_one_object_hrzn(sim: rebound.Simulation, object_name: str, epoch: datetime, hrzn: Dict):
    """Add one object to a simulation with data fromm horizons (cache or API)."""
    # Identifiers for this object
    object_id = name_to_id[object_name]
    key = (epoch, object_id)

    try:
        # Try to look up the object on the horizons cache
        p: horizon_entry = hrzn[key]
        sim.add(m=p.m, x=p.x, y=p.y, z=p.z, vx=p.vx, vy=p.vy, vz=p.vz, hash=rebound.hash(object_name))
    except KeyError:
        # Search string for the horizon API
        horizon_name = object_to_horizon_name[object_name]
        # Convert epoch to a horizon date string
        horizon_date: str = datetime_to_horizons(epoch)
        print(f'Searching Horizons as of {horizon_date}')
        # Add the particle
        sim.add(horizon_name, date=horizon_date)
        # Set the mass and hash of this particle
        p: rebound.Particle = sim.particles[-1]
        # p.m = mass_tbl[object_name]
        p.m = mass_tbl.get(object_name, 0.0)
        p.hash = rebound.hash(object_name)
        # Create an entry for this particle on the cache
        entry: horizon_entry = horizon_entry(m=p.m, x=p.x, y=p.y, z=p.z, vx=p.vx, vy=p.vy, vz=p.vz, 
                                             object_name=object_name, object_id=object_id, horizon_name=horizon_name)
        hrzn[key] = entry
        
        # Save the revised cache
        save_horizons_cache(hrzn)
예제 #2
0
    def process_row(sim: rebound.Simulation, fname: str, t: float, row: int):
        # Integrate to the current time step with an exact finish time
        sim.integrate(t, exact_finish_time=1)

        # Serialize the position and velocity
        sim.serialize_particle_data(xyz=q[row])
        sim.serialize_particle_data(vxvyvz=v[row])

        # Save a snapshot on multiples of save_step or the first / last row
        if (i % save_step == 0) or (row in (0, M - 1)):
            sim.simulationarchive_snapshot(filename=fname)

        # Save the orbital elements if applicable
        if save_elements:
            # Compute the orbital elements vs. the sun as primary
            primary = sim.particles['Sun']
            orbits = sim.calculate_orbits(primary=primary, jacobi_masses=False)
            # Save the elements on this date as an Nx6 array
            # This approach only traverses the (slow) Python list orbits one time
            elt_array = np.array([[orb.a, orb.e, orb.inc, orb.Omega, orb.omega, orb.f] \
                                  for orb in orbits])
            # Save the elements into the current row of the named orbital elements arrays
            # The LHS row mask starts at 1 b/c orbital elements are not computed for the first object (Sun)
            orb_a[row, 1:] = elt_array[:, 0]
            orb_e[row, 1:] = elt_array[:, 1]
            orb_inc[row, 1:] = elt_array[:, 2]
            orb_Omega[row, 1:] = elt_array[:, 3]
            orb_omega[row, 1:] = elt_array[:, 4]
            orb_f[row, 1:] = elt_array[:, 5]
예제 #3
0
def extend_sim_horizons(sim: rebound.Simulation, object_names: List[str], epoch: datetime) -> None:
    """Extend a rebound simulation with initial data from the NASA Horizons system"""
    # Generate list of missing object names
    hashes_present: Set[int] = set(p.hash.value for p in sim.particles)
    objects_missing: List[str] = [nm for nm in object_names if rebound.hash(nm).value not in hashes_present]
    
    # Add missing objects one at a time if not already present
    for object_name in objects_missing:
        add_one_object_hrzn(sim=sim, object_name=object_name, epoch=epoch, hrzn=hrzn)        
    
    # Move to center of mass
    sim.move_to_com()
예제 #4
0
def main():
    args = sys.argv[1:]
    from argparse import ArgumentParser
    psr = ArgumentParser(
        description="Convert Mercury6 output into REBOUND archive.")
    psr.add_argument('input_dir', type=str, help='Simulation directory')
    psr.add_argument('-o',
                     '--output_dir',
                     type=str,
                     help='Output directory.',
                     default='./rebsims/')
    args = psr.parse_args(args)
    indir = args.input_dir
    outdir = args.output_dir

    sim_name = os.path.basename(os.path.normpath(indir))

    # Get surviving planets from element.out
    try:
        el_out = open(os.path.join(indir, 'element.out'), 'r')
        pl_remain = []
        plan_re = re.compile(r'^\s*(plan\d+)')
        for l in el_out:
            match = re.search(plan_re, l)
            if match:
                pl_remain.append(match.group(1))
    except:
        print("Could not find element.out. Run element6.for.")
        raise

    # Create simulation
    sim = Simulation()
    sim.units = ('day', 'AU', 'Msun')
    # Add central star
    sim.add(m=1)
    # Add planets
    for pl in pl_remain:
        # Read last line of appropriate file
        pl_file = os.path.join(indir, pl + '.aei')
        with open(pl_file, 'rb') as f:
            f.seek(-2, os.SEEK_END)
            while f.read(1) != b'\n':
                f.seek(-2, os.SEEK_CUR)
            final = f.readline().decode()
            # Assume Cartesian positions and velocities.
            t, x, y, z, u, v, w, m = [float(s) for s in final.split()]
            sim.add(m=m, x=x, y=y, z=z, vx=u, vy=v, vz=w)

    outfile = os.path.join(outdir, sim_name + '.bin')
    sim.save(outfile)
    return
예제 #5
0
def make_sim_asteroids(sim_base: rebound.Simulation, 
                       ast_elt: pd.DataFrame, 
                       n0: int, n1: int,
                       progbar: bool = False) -> Tuple[rebound.Simulation, List[str]]:
    """
    Create a simulation with the selected asteroids by their ID numbers.
    INPUTS:
    sim_base: the base simulation, with e.g. the sun, planets, and selected moons
    ast_elt: the DataFrame with asteroid orbital elements at the specified epoch
    n0: the first asteroid number to add, inclusive
    n1: the last asteroid number to add, exclusive
    """
    # Start with a copy of the base simulation
    sim = sim_base.copy()
    # Set the number of active particles to the base simulation
    # https://rebound.readthedocs.io/en/latest/ipython/Testparticles.html
    sim.N_active = sim_base.N

    # Add the specified asteroids one at a time
    mask = (n0 <= ast_elt.Num) & (ast_elt.Num < n1)
    nums = ast_elt.index[mask]
    iters = tqdm_console(nums) if progbar else nums
    for num in iters:
        # Unpack the orbital elements
        a = ast_elt.a[num]
        e = ast_elt.e[num]
        inc = ast_elt.inc[num]
        Omega = ast_elt.Omega[num]
        omega = ast_elt.omega[num]
        M = ast_elt.M[num]
        name = ast_elt.Name[num]
        # Set the primary to the sun (NOT the solar system barycenter!)
        primary = sim.particles['Sun']
        # Add the new asteroid
        sim.add(m=0.0, a=a, e=e, inc=inc, Omega=Omega, omega=omega, M=M, primary=primary)
        # Set the hash to the asteroid's name
        sim.particles[-1].hash = rebound.hash(name)

    # The corresponding list of asteroid names
    asteroid_names = [name for name in ast_elt.Name[nums]]

    # Return the new simulation including the asteroids
    return sim, asteroid_names
예제 #6
0
def reorder_particles(sim: Simulation, ed: ExtraData) -> None:
    """
    probably not needed anymore
    """
    exit()
    particles_by_hash: Dict[int, Particle] = {}
    hashes = []
    suns = []
    gas_giants = []
    embryos = []
    planetesimals = []
    original_N = sim.N
    p: Particle
    for p in sim.particles:
        hash_value = p.hash.value
        # save a copy of the particles
        particles_by_hash[hash_value] = p.copy()
        type = ed.pd(p).type
        if type == "sun":
            suns.append(hash_value)
            # keep sun in the simulation to avoid empty particles list
            continue
        elif type == "gas giant":
            gas_giants.append(hash_value)
        elif type == "embryo":
            embryos.append(hash_value)
        elif type == "planetesimal":
            planetesimals.append(hash_value)
        else:
            raise ValueError(f"unknown type: {type}")
        hashes.append(p.hash)

    for hash in hashes:
        sim.remove(hash=hash)
    ordered_particles = gas_giants + embryos + planetesimals
    assert len(suns) + len(ordered_particles) == original_N
    particle: Particle
    for h in ordered_particles:
        particle = particles_by_hash[h]
        sim.add(particle)
    # print(list(sim.particles))
    # exit()
    sim.N_active = len(suns) + len(ordered_particles) - len(planetesimals)
    # mark objects > N_active as testparticle_type = 1
    # this means they become semi-active bodies
    # TODO: double-check meaning
    sim.testparticle_type = 1
    assert sim.N == original_N
예제 #7
0
def main():
    sim = Simulation()
    rebx = Extras(sim)
    rebx.register_param("water", "REBX_TYPE_DOUBLE")
    sim.units = ('yr', 'AU', 'Msun')
    sim.add(m=1.0)  # Sun
    for planet in data:
        part = Particle(a=planet[0],
                        e=planet[1],
                        inc=radians(planet[2]),
                        omega=radians(planet[3]),
                        Omega=radians(planet[4]),
                        M=radians(planet[5]),
                        m=planet[6],
                        simulation=sim)
        sim.add(part)
    max_n = sim.N
    sim.particles[1].params["water"] = 0.3
    print("start")
    tmax = 1e5
    savesteps = 1000
    times = np.linspace(0., tmax, savesteps)
    sim.move_to_com()
    sim.automateSimulationArchive("sa.bin", interval=savesteps)
    for i, t in enumerate(times):
        sim.integrate(t)
        print(f"done: {i / 10}% ({sim.N}")
    with open("sa.meta.json", "w") as f:
        meta = {"tmax": tmax, "savesteps": savesteps, "max_n": max_n}
        json.dump(meta, f)
예제 #8
0
def make_traj_from_sim(sim: rebound.Simulation, n_years: int, sample_freq: int,
                       debug_print: bool):
    """
    Make an array of training data points for the general 3 body problem from a simulation.
    Creates one trajectory with the same initial configuration.
    INPUTS:
    sim: rebound Simulation object
    n_years: the number of years of data to simulate, e.g. 2
    sample_freq: number of sample points per year, e.g. 365
    RETURNS:
    input_dict: a dictionary with the input fields t, q0, v0
    output_dict: a dictionary with the output fields q, v, a, q0_rec, v0_rec, T, H, H, L
    """
    # Number of samples including both start and end
    N = n_years * sample_freq + 1

    # Unpack the masses and wrap into array m
    ps = sim.particles
    p0, p1, p2 = ps
    m0, m1, m2 = p0.m, p1.m, p2.m
    m = np.array([m0, m1, m2], dtype=np.float32)

    # Array shapes
    num_particles = 3
    space_dims = 3
    traj_shape = (N, num_particles, space_dims)
    # the number of orbital elements is the number of particles minus 1; no elements for primary
    elt_shape = (N, num_particles - 1)
    mom_shape = (N, 3)

    # Initialize cartesian entries to zero vectors
    q = np.zeros(traj_shape, dtype=np.float32)
    v = np.zeros(traj_shape, dtype=np.float32)
    a = np.zeros(traj_shape, dtype=np.float32)

    # Initialize orbital elements over time to zero vectors
    orb_a = np.zeros(elt_shape, dtype=np.float32)
    orb_e = np.zeros(elt_shape, dtype=np.float32)
    orb_inc = np.zeros(elt_shape, dtype=np.float32)
    orb_Omega = np.zeros(elt_shape, dtype=np.float32)
    orb_omega = np.zeros(elt_shape, dtype=np.float32)
    orb_f = np.zeros(elt_shape, dtype=np.float32)

    # Initialize placeholders for kinetic and potential energy
    T = np.zeros(N, dtype=np.float32)
    U = np.zeros(N, dtype=np.float32)

    # Initialize momentum and angular momentum
    P = np.zeros(mom_shape, dtype=np.float32)
    L = np.zeros(mom_shape, dtype=np.float32)

    # The coefficients for gravitational potential energy on particle pairs
    k_01 = sim.G * m0 * m1
    k_02 = sim.G * m0 * m2
    k_12 = sim.G * m1 * m2

    # Set the times for snapshots
    ts = np.linspace(0.0, n_years, N, dtype=np.float32)

    # The particles for the 3 bodies
    p0 = sim.particles[0]
    p1 = sim.particles[1]
    p2 = sim.particles[2]

    # Simulate the orbits
    # Start by integrating backward, then forward for a small step
    # This allows rebound to correctly initialize the acceleration
    sim.integrate(-1E-6, exact_finish_time=1)
    sim.integrate(1E-6, exact_finish_time=1)
    for i, t in enumerate(ts):
        # Integrate to the current time step with an exact finish time
        sim.integrate(t, exact_finish_time=1)

        # Save the position
        q[i] = [[p0.x, p0.y, p0.z], [p1.x, p1.y, p1.z], [p2.x, p2.y, p2.z]]
        # Save the velocity
        v[i] = [[p0.vx, p0.vy, p0.vz], [p1.vx, p1.vy, p1.vz],
                [p2.vx, p2.vy, p2.vz]]
        # Save the acceleration
        a[i] = [[p0.ax, p0.ay, p0.az], [p1.ax, p1.ay, p1.az],
                [p2.ax, p2.ay, p2.az]]

        # Calculate the two orbits
        orb1 = p1.calculate_orbit()
        orb2 = p2.calculate_orbit()

        # Save the orbital elements
        orb_a[i] = [orb1.a, orb2.a]
        orb_e[i] = [orb1.e, orb2.e]
        orb_inc[i] = [orb1.inc, orb2.inc]
        orb_Omega[i] = [orb1.Omega, orb2.Omega]
        orb_omega[i] = [orb1.omega, orb2.omega]
        orb_f[i] = [orb1.f, orb2.f]

        # Extract the 3 positions
        q0 = q[i, 0]
        q1 = q[i, 1]
        q2 = q[i, 2]

        # Extract the 3 velocities
        v0 = v[i, 0]
        v1 = v[i, 1]
        v2 = v[i, 2]

        # Kinetic energy
        T0 = 0.5 * m0 * np.sum(np.square(v0))
        T1 = 0.5 * m1 * np.sum(np.square(v1))
        T2 = 0.5 * m2 * np.sum(np.square(v2))
        T[i] = T0 + T1 + T2

        # Potential energy; 3 pairs of interacting particles (3 choose 2)
        r_01 = np.linalg.norm(q1 - q0)
        r_02 = np.linalg.norm(q2 - q0)
        r_12 = np.linalg.norm(q2 - q1)
        U[i] = -(k_01 / r_01 + k_02 / r_02 + k_12 / r_12)

        # The momentum vector; should be zero in the COM frame
        mv0 = m0 * v0
        mv1 = m1 * v1
        mv2 = m2 * v2
        P[i] = mv0 + mv1 + mv2

        # The angular momentum vector; should be constant by conservation of angular momentum
        L[i] = np.cross(q0, mv0) + np.cross(q1, mv1) + np.cross(q2, mv2)

    # The total energy is the sum of kinetic and potential; should be constant by conservation of energy
    H = T + U

    # The initial position and velocity
    q0 = q[0]
    v0 = v[0]

    # Assemble the input dict
    inputs = {'t': ts, 'q0': q0, 'v0': v0, 'm': m}

    # Assemble the output dict
    outputs = {
        # the trajectory
        'q': q,
        'v': v,
        'a': a,

        # the orbital elements over time
        'orb_a': orb_a,
        'orb_e': orb_e,
        'orb_inc': orb_inc,
        'orb_Omega': orb_Omega,
        'orb_omega': orb_omega,
        'orb_f': orb_f,

        # the initial conditions, which should be recovered
        'q0_rec': q0,
        'v0_rec': v0,

        # the energy, momentum and angular momentum, which should be conserved
        'T': T,
        'U': U,
        'H': H,
        'P': P,
        'L': L
    }

    # Return the dicts
    return (inputs, outputs)
예제 #9
0
 def process_row(sim: rebound.Simulation, t: float, row: int):
     # Integrate to the current time step with an exact finish time
     sim.integrate(t, exact_finish_time=1)
     
     # Serialize the positions of earth and all the bodies
     sim.serialize_particle_data(xyz=q[row])
예제 #10
0
def make_archive_impl(fname_archive: str, sim_epoch: rebound.Simulation,
                      object_names: List[str], epoch: datetime, dt0: datetime,
                      dt1: datetime, time_step: int, save_step: int,
                      save_elements: bool, progbar: bool) -> None:
    """
    Create a rebound simulation archive and save it to disk.
    INPUTS:
        fname_archive: the file name to save the archive to
        sim_epoch: rebound simulation object as of the epoch time; to be integrated in both directions
        object_names: the user names of all the objects in the simulation
        epoch: a datetime corresponding to sim_epoch
        dt0: the earliest datetime to simulate back to
        dt1: the latest datetime to simulate forward to
        time_step: the time step in days for the simulation
        save_step: the interval for saving snapshots to the simulation archive
        save_elements: flag indiciting whether to save orbital elements
        progbar: flag - whether to display a progress bar
    """

    # Convert epoch, start and end times relative to a base date of the simulation start
    # This way, time is indexed from t0=0 to t1 = (dt1-dt0)
    epoch_t: float = datetime_to_mjd(epoch, dt0)
    t0: float = datetime_to_mjd(dt0, dt0)
    t1: float = datetime_to_mjd(dt1, dt0)

    # Create copies of the simulation to integrate forward and backward
    sim_fwd: rebound.Simulation = sim_epoch.copy()
    sim_back: rebound.Simulation = sim_epoch.copy()

    # Set the time counter on both simulation copies to the epoch time
    sim_fwd.t = epoch_t
    sim_back.t = epoch_t

    # Set the times for snapshots in both directions;
    ts: np.array = np.arange(t0, t1 + time_step, time_step)
    idx: int = np.searchsorted(ts, epoch_t, side='left')
    ts_fwd: np.array = ts[idx:]
    ts_back: np.array = ts[:idx][::-1]
    # The epochs corresponding to the times in ts
    epochs: List[datetime] = [dt0 + timedelta(t) for t in ts]

    # File names for forward and backward integrations
    fname_fwd: str = fname_archive.replace('.bin', '_fwd.bin')
    fname_back: str = fname_archive.replace('.bin', '_back.bin')

    # Number of snapshots
    M_back: int = len(ts_back)
    M_fwd: int = len(ts_fwd)
    M: int = M_back + M_fwd
    # Number of particles
    N: int = sim_epoch.N

    # Initialize arrays for the position and velocity
    shape_qv: Tuple[int] = (M, N, 3)
    q: np.array = np.zeros(shape_qv, dtype=np.float64)
    v: np.array = np.zeros(shape_qv, dtype=np.float64)

    # Initialize arrays for orbital elements if applicable

    if save_elements:
        # Arrays for a, e, inc, Omega, omega, f
        shape_elt: Tuple[int] = (M, N)
        orb_a: np.array = np.zeros(shape_elt, dtype=np.float64)
        orb_e: np.array = np.zeros(shape_elt, dtype=np.float64)
        orb_inc: np.array = np.zeros(shape_elt, dtype=np.float64)
        orb_Omega: np.array = np.zeros(shape_elt, dtype=np.float64)
        orb_omega: np.array = np.zeros(shape_elt, dtype=np.float64)
        orb_f: np.array = np.zeros(shape_elt, dtype=np.float64)

        # Wrap these into a named tuple
        elts: OrbitalElement = \
            OrbitalElement(a=orb_a, e=orb_e, inc=orb_inc,
                           Omega=orb_Omega, omega=orb_omega, f=orb_f)
    else:
        elts = OrbitalElement()

    # Subfunction: process one row of the loop
    def process_row(sim: rebound.Simulation, fname: str, t: float, row: int):
        # Integrate to the current time step with an exact finish time
        sim.integrate(t, exact_finish_time=1)

        # Serialize the position and velocity
        sim.serialize_particle_data(xyz=q[row])
        sim.serialize_particle_data(vxvyvz=v[row])

        # Save a snapshot on multiples of save_step or the first / last row
        if (i % save_step == 0) or (row in (0, M - 1)):
            sim.simulationarchive_snapshot(filename=fname)

        # Save the orbital elements if applicable
        if save_elements:
            # Compute the orbital elements vs. the sun as primary
            primary = sim.particles['Sun']
            orbits = sim.calculate_orbits(primary=primary, jacobi_masses=False)
            # Save the elements on this date as an Nx6 array
            # This approach only traverses the (slow) Python list orbits one time
            elt_array = np.array([[orb.a, orb.e, orb.inc, orb.Omega, orb.omega, orb.f] \
                                  for orb in orbits])
            # Save the elements into the current row of the named orbital elements arrays
            # The LHS row mask starts at 1 b/c orbital elements are not computed for the first object (Sun)
            orb_a[row, 1:] = elt_array[:, 0]
            orb_e[row, 1:] = elt_array[:, 1]
            orb_inc[row, 1:] = elt_array[:, 2]
            orb_Omega[row, 1:] = elt_array[:, 3]
            orb_omega[row, 1:] = elt_array[:, 4]
            orb_f[row, 1:] = elt_array[:, 5]

    # Integrate the simulation forward in time
    idx_fwd: List[Tuple[int, float]] = list(enumerate(ts_fwd))
    if progbar:
        idx_fwd = tqdm_console(idx_fwd)
    for i, t in idx_fwd:
        # Row index for position data
        row: int = M_back + i
        # Process this row
        process_row(sim=sim_fwd, fname=fname_fwd, t=t, row=row)

    # Integrate the simulation backward in time
    idx_back: List[Tuple[int, float]] = list(enumerate(ts_back))
    if progbar:
        idx_back = tqdm_console(idx_back)
    for i, t in idx_back:
        # Row index for position data
        row: int = M_back - (i + 1)
        # Process this row
        process_row(sim=sim_back, fname=fname_back, t=t, row=row)

    # Load the archives with the forward and backward snapshots
    sa_fwd: rebound.SimulationArchive = rebound.SimulationArchive(fname_fwd)
    sa_back: rebound.SimulationArchive = rebound.SimulationArchive(fname_back)

    # Filename for numpy arrays of position and velocity
    fname_np: str = fname_archive.replace('.bin', '.npz')

    # Save the epochs as a numpy array
    epochs_np: np.array = np.array(epochs)
    # Save the object names as a numpy array of strings
    object_names_np: np.array = np.array(object_names)

    # Save the object name hashes
    hashes: np.array = np.zeros(N, dtype=np.uint32)
    sim_epoch.serialize_particle_data(hash=hashes)

    # Combine the forward and backward archives in forward order from t0 to t1
    sims = chain(reversed(sa_back), sa_fwd)
    # Process each simulation snapshot in turn
    for i, sim in enumerate(sims):
        # Save a snapshot on multiples of save_step
        sim.simulationarchive_snapshot(fname_archive)

    # Save the numpy arrays with the object hashes, position and velocity
    np.savez(fname_np,
             q=q,
             v=v,
             elts=elts,
             ts=ts,
             epochs_np=epochs_np,
             hashes=hashes,
             object_names_np=object_names_np)

    # Delete the forward and backward archives
    os.remove(fname_fwd)
    os.remove(fname_back)
예제 #11
0
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
예제 #12
0
def main(fn: Path, testrun=False):
    global abort
    start = time.perf_counter()

    if not fn.with_suffix(".bin").exists():
        if not testrun:
            with open(fn.with_suffix(".yaml")) as f:
                parameters = Parameters(**yaml.safe_load(f))
        else:
            parameters = Parameters(
                massloss_method="rbf",
                initcon_file="initcon/conditions_many.input")
        # 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"
        sim.ri_mercurius.hillfac = 3.
        sim.testparticle_type = 1
        tmax = 200 * mega
        num_savesteps = 20000
        if testrun:
            tmax /= 200000
            num_savesteps /= 1000
        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()
        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

        num_planetesimals, num_embryos = \
            add_particles_from_conditions_file(sim, extradata, parameters.initcon_file, testrun)

        sim.move_to_com()
        extradata.meta.initial_N = sim.N
        extradata.meta.initial_N_planetesimal = num_planetesimals
        extradata.meta.initial_N_embryo = num_embryos
        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)
        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")))
        extradata = ExtraData.load(fn)
        tmax = extradata.meta.tmax
        per_savestep = extradata.meta.per_savestep
        sim = sa[-1]
        t = round(sim.t + per_savestep)
        print(f"continuing from {t}")
        sim.move_to_com()
        sim.ri_mercurius.recalculate_coordinates_this_timestep = 1
        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()}")
        num_savesteps = extradata.meta.num_savesteps
        cputimeoffset = extradata.meta.cputime
        walltimeoffset = extradata.meta.walltime

    check_heartbeat_needs_recompile()
    clibheartbeat = cdll.LoadLibrary("heartbeat/heartbeat.so")
    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) / 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
    c_double.in_dll(clibheartbeat,
                    "max_distance_from_sun_squared").value = 150**2

    assert sim.dt < innermost_period(sim) / MIN_TIMESTEP_PER_ORBIT

    def collision_resolve_handler(sim_p: POINTER_REB_SIM,
                                  collision: reb_collision) -> int:
        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

    sim.collision_resolve = collision_resolve_handler

    # show_orbits(sim)

    fn.with_suffix(".lock").touch()
    print("start")

    while t <= tmax:
        print()
        print(f"{t / tmax * 100:.2f}%")
        set_process_title(fn, t / tmax, sim.N)
        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)

        print("fraction", innermost_period(sim) / MIN_TIMESTEP_PER_ORBIT)
        assert sim.dt < innermost_period(sim) / MIN_TIMESTEP_PER_ORBIT

        escape: hb_event
        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
        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
        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
        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)
        if abort:
            print("aborted")
            exit(1)
    print("finished")
    fn.with_suffix(".lock").unlink()