def make_plots(all_particles, disk_only, i=0): for j, particles in enumerate([all_particles, disk_only]): if HAS_PYNBODY: temp_particles = particles.copy() temp_particles.u = 1 | units.ms**2 temp = Gadget2() temp.gas_particles.add_particles(temp_particles) temp.commit_particles() pynbody_column_density_plot(temp.gas_particles, width=300 | units.kpc, units='m_p cm^-2', vmin=17, vmax=23) temp.stop() else: native_plot.gca().set_aspect("equal") native_plot.xlim(-150, 150) native_plot.ylim(-150, 150) plot(particles.x.as_quantity_in(units.kpc), particles.y.as_quantity_in(units.kpc), 'r.') native_plot.savefig( os.path.join( "plots", "plot_galaxy_merger_{0}_{1:=04}.png".format( "disk" if j else "total", i))) native_plot.close()
def simulate_merger(galaxy1, galaxy2, dt=25. | units.Myr, tend=3. | units.Gyr): converter = nbody_system.nbody_to_si(1.0e9 | units.MSun, 1. | units.kpc) dynamics = Gadget2(converter, number_of_workers=2) dynamics.parameters.epsilon_squared = (1. | units.kpc)**2 dynamics.parameters.max_size_timestep = dt # max internal timestep for Gadget dynamics.parameters.time_max = 2**9 * dt # max possible evolve time for Gadget, 2**k *dt should be > tend set1 = dynamics.particles.add_particles(galaxy1) set2 = dynamics.particles.add_particles(galaxy2) galaxies_without_halo = set1[:NDISK] + set2[:NDISK] make_plots(dynamics.particles, galaxies_without_halo) print "starting simulation.." i = 0 while dynamics.model_time < tend: i = i + 1 dynamics.evolve_model(i * dt) print "evolved to:", dynamics.model_time.as_quantity_in( units.Myr), "/", tend make_plots(dynamics.particles, galaxies_without_halo, i) dynamics.stop()
def new_instance_of_gadget2_code(self): result = Gadget2(self.convert_generic_units, number_of_workers=self.number_of_workers, mode="periodic", **self.hydro_code_options) result.initialize_code() return result
def test6(self): length_unit = units.parsec speed_unit = units.parsec / units.Myr mass_unit = units.MSun instance = Gadget2() self._run_addition_removal_test(instance, length_unit, speed_unit, mass_unit)
def new_hydro(gas, dynamical_timescale, converter): if False: hydro = Gadget2(converter, number_of_workers=8, redirection="file", redirect_file="gadget.log") hydro.parameters.time_max = 3 * dynamical_timescale hydro.parameters.max_size_timestep = dynamical_timescale / 100 hydro.parameters.time_limit_cpu = 1.0 | units.Gyr else: hydro = Fi(converter, mode='openmp', redirection="file", redirect_file="fi.log") hydro.parameters.timestep = dynamical_timescale / 100 hydro.parameters.eps_is_h_flag = True return hydro
def setup_codes(self): # 1 code is created and started self.code = Gadget2(self.converter, redirection='none', number_of_workers=4) # 2 parameters are set self.code.parameters.epsilon_squared = 0.0000001 | nbody_system.length**2 self.dmA = self.code.dm_particles.add_particles(self.subClusterA.dm) self.gasA = self.code.gas_particles.add_particles(self.subClusterA.gas) self.dmB = self.code.dm_particles.add_particles(self.subClusterB.dm) self.gasB = self.code.gas_particles.add_particles(self.subClusterB.gas) self.code.commit_particles()
def setup_codes(self): # 1 code is created and started self.code = Gadget2(self.converter, redirection='none', number_of_workers=4) # 2 parameters are set self.code.parameters.epsilon_squared = 0.0000001 | nbody_system.length**2 self.dmA = self.code.dm_particles.add_particles(self.subClusterA.dm) self.gasA = self.code.gas_particles.add_particles(self.subClusterA.gas) self.dmB = self.code.dm_particles.add_particles(self.subClusterB.dm) self.gasB = self.code.gas_particles.add_particles(self.subClusterB.gas) self.code.commit_particles() self.Etot_init = self.code.kinetic_energy + self.code.potential_energy + self.code.thermal_energy print "Ekin:", self.code.kinetic_energy print "Epot:", self.code.potential_energy print "Eth:", self.code.thermal_energy
def simulate_merger(galaxy1, galaxy2): converter = nbody_system.nbody_to_si(1.0e12 | units.MSun, 100 | units.kpc) dynamics = Gadget2(converter, number_of_workers=2) dynamics.parameters.epsilon_squared = 0.0000001 | nbody_system.length**2 set1 = dynamics.particles.add_particles(galaxy1) set2 = dynamics.particles.add_particles(galaxy2) galaxies_without_halo = set1[:10000] + set2[:10000] make_plots(dynamics.particles, galaxies_without_halo) for i in range(1, 101): dynamics.evolve_model(i * (25 | units.Myr)) print dynamics.model_time.as_quantity_in(units.Myr) make_plots(dynamics.particles, galaxies_without_halo, i) dynamics.stop()
def slowtest1(self): stellar_evolution = self.new_instance(MESA) stellar_evolution.particles.add_particles( Particles(2, mass=[1.0, 5.0] | units.MSun)) stellar_evolution.evolve_model(10.0 | units.Myr) initial_separation = stellar_evolution.particles.radius.sum() sph_particles_1 = convert_stellar_model_to_SPH( stellar_evolution.particles[0], 200, seed=12345).gas_particles sph_particles_2 = convert_stellar_model_to_SPH( stellar_evolution.particles[1], 1000, seed=12345).gas_particles stellar_evolution.stop() initial_speed = 10.0 | units.km / units.s sph_particles_2.x += initial_separation sph_particles_1.vx += initial_speed all_sph_particles = ParticlesSuperset( [sph_particles_1, sph_particles_2]) all_sph_particles.move_to_center() t_end = 4.0e3 | units.s print "Evolving to:", t_end n_steps = 4 unit_system_converter = ConvertBetweenGenericAndSiUnits( 1.0 | units.RSun, 1.0 | units.MSun, t_end) hydro_code = Gadget2(unit_system_converter) hydro_code.gas_particles.add_particles(all_sph_particles) pyplot.ion() for time in [i * t_end / n_steps for i in range(1, n_steps + 1)]: hydro_code.evolve_model(time) pyplot.close('all') pyplot.figure() pynbody_column_density_plot(hydro_code.gas_particles, width=10 | units.RSun) pyplot.draw() pyplot.figure() loglog(hydro_code.gas_particles.position.lengths_squared(), hydro_code.gas_particles.pressure, 'bo') pyplot.draw() hydro_code.stop() sleep(3) pyplot.ioff()
def integrate(cluster, outdir): # TODO: check what converter actually does. # If I set the converter to the same units as Gadget2/Toycluster2, are the internal units the same? # NB Toycluster is cgs, the converter is SI converter = nbody_system.nbody_to_si(1.0e10 | units.MSun, 1 | units.kpc) dynamics = Gadget2(converter, number_of_workers=4, redirection='none') dynamics.parameters.epsilon_squared = 0.0000001 | nbody_system.length**2 print dynamics.parameters dm = dynamics.dm_particles.add_particles(cluster.dm) make_plot(dynamics.particles, outdir) for i in range(1, 101): dynamics.evolve_model(i * (1 | units.Myr)) print dynamics.model_time.as_quantity_in(units.Myr) make_plot(dynamics.particles, outdir, i) dynamics.stop()
def new_hydro(gas, converter, time_step): hydro = Gadget2(converter, node_label="cartesius", number_of_workers=6, redirection="file", redirect_file="gas_code.log") hydro.parameters.gas_epsilon = converter.to_si( 0.01 | nbody_system.length) # or eps_is_h_flag hydro.parameters.n_smooth = 64 hydro.parameters.n_smooth_tol = 0.005 | units.none hydro.parameters.time_max = 32.0 | units.Myr hydro.parameters.max_size_timestep = time_step hydro.parameters.time_limit_cpu = 1.0 | units.yr hydro.gas_particles.add_particles(gas) hydro.commit_particles() eps = hydro.gas_particles.radius.as_quantity_in(units.parsec) print "Gravitational softening for gas (mean, min, max):", eps.mean( ), eps.min(), eps.max() return hydro
def new_hydro(gas, converter, time_step, distributed): if distributed is None: # distributed_kwargs = dict(number_of_workers=24, label="hydro") distributed_kwargs = dict(number_of_workers=16) else: # distributed_kwargs = dict(label="local", number_of_workers=6, number_of_nodes=1) distributed_kwargs = dict(label="hydro", number_of_workers=16, number_of_nodes=1) if "cartesius" in distributed.resources.name: distributed.pilots.add_pilot(new_cartesius_pilot()) print "Waiting for Cartesius reservation" distributed.wait_for_pilots() print "Start hydro" if True: hydro = Gadget2( converter, redirection="file", redirect_file="gas_code.log", **distributed_kwargs) # hydro.parameters.n_smooth = 64 # hydro.parameters.n_smooth_tol = 0.005 |units.none hydro.parameters.time_max = 32.0 | units.Myr hydro.parameters.max_size_timestep = time_step hydro.parameters.time_limit_cpu = 1.0 | units.yr else: distributed_kwargs['number_of_threads'] = distributed_kwargs['number_of_workers'] distributed_kwargs['number_of_workers'] = 1 print distributed_kwargs hydro = Fi( converter, mode='openmp', redirection="file", redirect_file="gas_code.log", **distributed_kwargs) hydro.parameters.eps_is_h_flag = True hydro.parameters.timestep = time_step hydro.parameters.verbosity = 99 hydro.gas_particles.add_particles(gas) hydro.commit_particles() eps = hydro.gas_particles.radius.as_quantity_in(units.parsec) print "Gravitational softening for gas (mean, min, max):", eps.mean(), eps.min(), eps.max() return hydro
def new_particles(self): input_file = os.path.join(get_path_to_results(), "test_sph_to_star_input.hdf5") if os.path.exists(input_file): return read_set_from_file(input_file, "hdf5") stellar_evolution = EVtwin() stellar_evolution.particles.add_particle( Particle(mass=1.0 | units.MSun)) stellar_evolution.evolve_model(100.0 | units.Myr) particles = convert_stellar_model_to_SPH( stellar_evolution.particles[0], 500, seed=12345).gas_particles stellar_evolution.stop() hydrodynamics = Gadget2( ConvertBetweenGenericAndSiUnits(1.0 | units.MSun, 1.0 | units.RSun, 1.0e3 | units.s)) hydrodynamics.gas_particles.add_particles(particles) hydrodynamics.evolve_model(1.0 | units.s) hydrodynamics.gas_particles.copy_values_of_attributes_to( ["density", "u", "pressure"], particles) hydrodynamics.stop() write_set_to_file(particles, input_file, "hdf5") return particles
def new_gas_code_gadget(self): result = Gadget2(self.converter) return result
def test3(self): print("Demonstrate new_sink_particles usage (using Gadget2)") UnitLength = 1.0 | units.kpc UnitMass = 1.0e10 | units.MSun UnitVelocity = 1.0 | units.km / units.s convert_nbody = nbody_system.nbody_to_si(UnitLength, UnitMass) converter = ConvertBetweenGenericAndSiUnits(UnitLength, UnitMass, UnitVelocity) number_gas_particles = 1000 gas = new_evrard_gas_sphere(number_gas_particles, convert_nbody, do_scale=True, seed=12345) sph_code = Gadget2(converter) sph_code.initialize_code() sph_code.parameters.stopping_condition_maximum_density = 10 * UnitMass / UnitLength**3 sph_code.gas_particles.add_particles(gas) self.assertIsOfOrder(max(sph_code.gas_particles.density), UnitMass / UnitLength**3) density_limit_detection = sph_code.stopping_conditions.density_limit_detection density_limit_detection.enable() sph_code.evolve_model(10.0 | units.Myr) self.assertTrue(density_limit_detection.is_set()) self.assertTrue(sph_code.model_time < 10.0 | units.Myr) print("density_limit exceeded at t =", sph_code.model_time.as_quantity_in(units.Myr)) self.assertEqual(len(density_limit_detection.particles()), 1) self.assertTrue(density_limit_detection.particles().density > 10 * UnitMass / UnitLength**3) clumps = density_limit_detection.particles().copy() sph_code.gas_particles.remove_particles(clumps) clumps_in_code = sph_code.dm_particles.add_particles(clumps) sinks = new_sink_particles(clumps_in_code,looping_over=self.looping_over) self.assertEqual(sinks.sink_radius, clumps.radius) self.assertAlmostRelativeEqual(sinks.mass, UnitMass / number_gas_particles, 10) self.assertAlmostRelativeEqual(sinks.position, clumps.position, 10) self.assertEqual(len(sph_code.gas_particles), number_gas_particles - 1) self.assertAlmostRelativeEqual(sph_code.particles.total_mass(), UnitMass, 10) self.assertAlmostRelativeEqual(sph_code.gas_particles.total_mass(), UnitMass - sinks.total_mass(), 10) self.assertEqual(set(sinks.get_attribute_names_defined_in_store()) - set(["sink_radius","lx","ly","lz"]), set(sph_code.particles.get_attribute_names_defined_in_store())) sinks.accrete(sph_code.gas_particles) self.assertAlmostRelativeEqual(sinks.mass, 3 * UnitMass / number_gas_particles, 10) self.assertEqual(len(sph_code.gas_particles), number_gas_particles - 3) self.assertAlmostRelativeEqual(sph_code.particles.total_mass(), UnitMass, 10) self.assertAlmostRelativeEqual(sph_code.gas_particles.total_mass(), UnitMass - sinks.total_mass(), 10) sinks.accrete(sph_code.particles) # Nothing happens: gas already gone, and cannot accrete itself self.assertAlmostRelativeEqual(sinks.mass, 3 * UnitMass / number_gas_particles, 10) self.assertAlmostRelativeEqual(sph_code.particles.total_mass(), UnitMass, 10) steps = 0 while True: sph_code.evolve_model(sph_code.model_time + (0.1 | units.Myr)) sinks.sink_radius = 4 * clumps_in_code.radius sinks.accrete(sph_code.gas_particles) steps += 1 if density_limit_detection.is_set(): break self.assertEqual(len(sph_code.gas_particles), number_gas_particles - 7) self.assertAlmostRelativeEqual(sinks.mass, 7 * UnitMass / number_gas_particles, 10) self.assertAlmostRelativeEqual(sph_code.particles.total_mass(), UnitMass, 10) self.assertAlmostRelativeEqual(sph_code.gas_particles.total_mass(), UnitMass - sinks.total_mass(), 10) self.assertTrue(density_limit_detection.is_set()) self.assertEqual(steps, 5) self.assertTrue(sph_code.model_time < 10.0 | units.Myr) print("density_limit exceeded at t =", sph_code.model_time.as_quantity_in(units.Myr)) self.assertEqual(len(density_limit_detection.particles()), 5) self.assertTrue((density_limit_detection.particles().density > 10 * UnitMass / UnitLength**3).all()) clumps = density_limit_detection.particles().copy() sph_code.gas_particles.remove_particles(clumps) clumps_in_code = sph_code.dm_particles.add_particles(clumps) sinks.add_sinks(clumps_in_code, sink_radius=0.1|units.kpc) self.assertEqual(sinks[1:].sink_radius, 0.1 | units.kpc) self.assertEqual(len(sph_code.gas_particles), number_gas_particles - 12) self.assertAlmostRelativeEqual(sinks.mass[1:], UnitMass / number_gas_particles, 10) self.assertAlmostRelativeEqual(sinks.position[1:], clumps.position, 10) self.assertAlmostRelativeEqual(sph_code.particles.total_mass(), UnitMass, 10) self.assertAlmostRelativeEqual(sph_code.gas_particles.total_mass(), UnitMass - sinks.total_mass(), 10) sinks.accrete(sph_code.gas_particles) self.assertEqual(len(sph_code.gas_particles), number_gas_particles - 66) self.assertAlmostRelativeEqual(sph_code.particles.total_mass().as_quantity_in(units.MSun), UnitMass, 10) self.assertAlmostRelativeEqual(sinks.mass, [7.0, 13.0, 15.0, 9.0, 11.0, 11.0] * UnitMass / number_gas_particles, 10)
from amuse.io import write_set_to_file from amuse.ext.spherical_model import new_gas_plummer_distribution, new_plummer_distribution from amuse.community.gadget2.interface import Gadget2 from amuse.community.fastkick.interface import FastKick gas = new_gas_plummer_distribution(1000, virial_radius=1 | units.parsec, total_mass=1000 | units.MSun, type="fcc") stars = new_plummer_distribution(10, virial_radius=1 | units.parsec, total_mass=100 | units.MSun, type="sobol") dynamical_timescale = gas.dynamical_timescale() converter = nbody_system.nbody_to_si(dynamical_timescale, 1 | units.parsec) hydro = Gadget2(converter, number_of_workers=2) hydro.parameters.time_max = 3 * dynamical_timescale hydro.parameters.max_size_timestep = dynamical_timescale / 100 hydro.parameters.time_limit_cpu = 1.0 | units.Gyr gravity_field_code = FastKick(converter) gravity_field_code.particles.add_particles(stars) relaxed_gas = relax(gas, hydro, gravity_field=gravity_field_code, monitor_func="energy", bridge_options=dict(verbose=True)) gravity_field_code.stop() hydro.stop() write_set_to_file(relaxed_gas, "gas_relaxed.amuse", "amuse")
def gadget(self): return Gadget2(convert_generic_units)
tend = 1. | units.Myr dt = 10000 | units.yr print(u**0.5).in_(units.kms) print((L / u**0.5) / dt) particles = box(N, L, rho, u) UnitLength = L UnitMass = particles.mass.sum() UnitVelocity = units.kms convert = generic_unit_converter.ConvertBetweenGenericAndSiUnits( UnitLength, UnitMass, UnitVelocity) sph = Gadget2(convert, mode='periodic_nogravity') #,redirection='none') sph.parameters.periodic_box_size = L sph.gas_particles.add_particles(particles) i = 0 t = 0. | units.Myr while t < (tend - dt / 2): t = t + dt i = i + 1 sph.evolve_model(t) print t.in_(units.Myr), sph.model_time.in_(units.Myr) rho = make_map(sph, L) f = pyplot.figure(figsize=(8, 8)) LL = L.number
def evolve_model(self): self.initialize_data() self.ism_code = Gadget2(self.converter, number_of_workers=self.n_core)#, debugger='gdb') self.ism_code.parameters.time_max = 1024*self.dt self.ism_code.parameters.n_smooth = 64 self.ism_code.parameters.n_smooth_tol = 2./64. self.ism_code.parameters.artificial_viscosity_alpha = 0.1 self.ism_code.parameters.epsilon_squared = (1. | units.AU)**2. self.all_particles = Particles() write_set_to_file(self.star, self.filename, "hdf5", append_to_file=False) write_set_to_file(self.all_particles, self.filename, "hdf5", append_to_file=False) self.initial_disc_particles = self.create_disc() self.all_particles.add_particles(self.initial_disc_particles) self.ism_code.gas_particles.add_particles(self.initial_disc_particles) #You can only add a sink after adding gas particles #starinsph refers to the corresponding particle set/id in the community code starinsph = self.ism_code.dm_particles.add_particles(self.star) #Use the build-in sink particle routine from amuse.ext.sink. Sink_radius needs to be defined manually otherwise the particle radius in gadget is taken, #which does not corresponding to the particle radius in the framework (since 'particles' in gadget do not have a radius, it is set to 0.01 | generic_unit_system.length #and corresponds to the gas gravitational smoothing epsilon. sink = new_sink_particles(starinsph, sink_radius= self.star.radius) self.channel_from_ismcode_to_framework = self.ism_code.gas_particles.new_channel_to(self.all_particles) time = 0. | units.yr while time <= (self.tend+self.dt/2.): print("Adding new slice of ISM...") newslice=self.make_slice() self.ism_code.gas_particles.add_particles(newslice) self.all_particles.add_particles(newslice) start = timing.time() print("=======================================================") print("Evolving to time = ", time.value_in(units.yr), " of ", self.tend.value_in(units.yr)," years...") self.ism_code.evolve_model(time) print("This took ", (timing.time() - start), " s") out_of_bounds = self.ism_code.gas_particles.select_array(lambda x,y,z:(x > self.cylinder_radius)|((z**2+y**2).sqrt() >= self.cylinder_radius), ["x","y","z"]) if len(out_of_bounds)>0: print("Removing ", len(out_of_bounds), " particles from the code because they were out of bounds") self.ism_code.gas_particles.remove_particles(out_of_bounds) self.ism_code.gas_particles.synchronize_to(self.all_particles) sink.accrete(self.ism_code.gas_particles) write_set_to_file(self.star, self.filename, "hdf5") write_set_to_file(self.all_particles, self.filename, "hdf5") time += self.dt print("=======================================================") self.ism_code.stop()
def new_sph_code_gadget(self): result = Gadget2(self.converter) result.dm_particles.add_particles(self.new_particles_cluster()) result.gas_particles.add_particles(self.new_gas_cluster()) result.commit_particles() return result
def Run(N_CORES, DM_halo_r_limit, star_disk_R_limit, star_disk_z_limit, star_bulge_r_limit): start_time = time.time() Print("Generating dark matter halo...", start_time) DM_halo_particles = gen_particles_Einasto(N=10**2, mass=2 * 10**12, N_CORES=N_CORES, r_limit=DM_halo_r_limit) Print("Generating disk stars...", start_time) star_disk_particles = gen_particles_exp_sech(N=10**2, mass=4 * 10**10, N_CORES=N_CORES, R_limit=star_disk_R_limit, z_limit=star_disk_z_limit) Print("Generating bulge stars...", start_time) star_bulge_particles = gen_particles_Jaffe(N=10**1, mass=2 * 10**10, N_CORES=N_CORES, r_limit=star_bulge_r_limit) Print("Generating disc gas...", start_time) gas_disk_particles = gen_particles_exp_sech(N=10**2, mass=1 * 10**10, N_CORES=N_CORES, R_limit=star_disk_R_limit, z_limit=star_disk_z_limit) Print("Generating halo gas...", start_time) gas_halo_particles = gen_particles_Einasto(N=10**2, mass=1 * 10**10, N_CORES=N_CORES, r_limit=star_disk_R_limit * 2) for p in datamodel.ParticlesSuperset( [gas_disk_particles.as_set(), gas_halo_particles.as_set()]): p.u = 1 | units.km**2 / units.s**2 Print("Assigning velocities to non-halo particles...", start_time) assign_star_velocities( datamodel.ParticlesSuperset([ star_disk_particles, star_bulge_particles, gas_disk_particles, gas_halo_particles ]), datamodel.ParticlesSuperset([ star_disk_particles, star_bulge_particles, gas_disk_particles, gas_halo_particles, DM_halo_particles ]), 2 * math.sqrt(star_disk_R_limit**2 + star_disk_z_limit**2)) Print("Assigning velocities to halo particles...", start_time) assign_halo_velocities( datamodel.ParticlesSuperset([DM_halo_particles, gas_halo_particles])) Print("Initializing Gadget2 code...", start_time) SE = Gadget2(number_of_workers=N_CORES - 1) Gadget_config(SE) Print("Adding particles to the code...", start_time) SE.dm_particles.add_particles(DM_halo_particles) SE.dm_particles.add_particles(star_disk_particles) SE.dm_particles.add_particles(star_bulge_particles) SE.gas_particles.add_particles(gas_disk_particles) SE.gas_particles.add_particles(gas_halo_particles) #io.write_set_to_file(SE.particles, 'output.dat', 'gadget') Print("Starting the simulation...", start_time) for t in np.arange(0, 1000, 1): SE.evolve_model(t | units.Myr) Plots(SE.dm_particles, SE.gas_particles, t) Print("Timestep " + str(t + 1) + ", screenshots saved!", start_time)
particles_gal, converter_gal = initialize_nfw_model(5e6, mass=10**15.4, scale_radius=10**2.8) particles_sat, converter_sat = initialize_nfw_model(1e6, mass=1e12, scale_radius=10., cutoff_radius=5.) particles_sat.x += 63.25 | units.kpc particles_sat.y += 78.14 | units.kpc particles_sat.z += 6.26 | units.kpc particles_sat.vx += 29.27 | units.kms particles_sat.vy += -37.54 | units.kms particles_sat.vz += -580.96 | units.kms gravity = Gadget2(converter_sat, number_of_workers=16) gravity.parameters.epsilon_squared = (0.1 | units.kpc)**2 gravity.parameters.max_size_timestep = dt gravity.parameters.time_max = time_max set1 = gravity.particles.add_particles(particles_gal) set2 = gravity.particles.add_particles(particles_sat) sat_stars = gravity.particles.copy()[-int(1e5):] amuse.io.write_set_to_file(sat_stars, _DATADIR + 'snap_inital.csv', format='csv') i = 0 while gravity.model_time < tend: i = i + 1 gravity.evolve_model(i * dt)