def evolve_cluster_in_galaxy(options_file): opt = options_reader(options_file) timestep = opt.options['timestep'] # in Myr tend = opt.options['tend'] # in Myr times = np.arange(0.0, tend, timestep) | units.Myr cluster = oc_code(opt) cluster_code = cluster.code galaxy_code = gizmo_interface(opt) snap_reader = snapshot_reader(opt, galaxy_code) stars = cluster_code.particles.copy() if opt.options['axisymmetric']: import astropy.units as u import gala.dynamics as gd import gala.potential as gp pos = [opt.options['axi_Rinit'], 0, 0] * u.kpc vel = [0, 0, 0] * u.km / u.s mw = gp.MilkyWayPotential() phase = gd.PhaseSpacePosition(pos, vel) vc = mw.circular_velocity(phase).to_value(u.km / u.s) | units.kms stars = cluster_code.particles.copy() stars.x += opt.options['axi_Rinit'] | units.kpc stars.vy += vc stars.z += opt.options['axi_zinit'] | units.kpc else: pos = galaxy_code.chosen_position_z0 vel = galaxy_code.chosen_velocity_z0 stars.x += pos[0] | units.kpc stars.y += pos[1] | units.kpc stars.z += pos[2] | units.kpc stars.vx += vel[0] | units.kms stars.vy += vel[1] | units.kms stars.vz += vel[2] | units.kms channel = stars.new_channel_to(cluster_code.particles) channel.copy_attributes(["x", "y", "z", "vx", "vy", "vz"]) system = Bridge(timestep=timestep, use_threading=False) system.add_system(cluster_code, (galaxy_code, )) system.add_system(galaxy_code) converter = cluster_code.unit_converter for i, t in enumerate(tqdm(times)): system.evolve_model(t, timestep=timestep | units.Myr) bound_com = bound.center_of_mass().value_in(units.kpc) snap_reader.process_snapshot(system, galaxy_code, bound_com, i, t) snap_reader.finish_sim() cluster_code.stop()
def local_relax(gas_particles, hydro, gravity_field=None, monitor_func=check_energy_conservation, bridge_options=dict()): """ Relax a set of SPH particles by evolving it with a hydrodynamics code, while imposing critical damping on the particle velocities. :argument gas_particles: The set of SPH particles :argument hydro: The hydrodynamics code :argument gravity_field Background gravitational field, must support get_gravity_at_point :argument monitor_func For monitoring progress each step. User-defined function or "energy" :argument bridge_options: Keyword options passed to Bridge """ if monitor_func == "energy": monitor_func = monitor_energy t_end_in_t_dyn = 0.1 # Relax for this many dynamical timescales t_end = t_end_in_t_dyn \ * gas_particles.dynamical_timescale(mass_fraction=0.9) n_steps = 10 velocity_damp_factor = 1.0 - (2.0*numpy.pi*t_end_in_t_dyn) \ /n_steps # Critical damping in_hydro = hydro.gas_particles.add_particles(gas_particles) if gravity_field is None: system = hydro else: system = Bridge(timestep=(t_end / n_steps).as_quantity_in(units.yr), **bridge_options) system.add_system(hydro, [gravity_field]) for i_step, time in enumerate(t_end * numpy.linspace(1.0 / n_steps, 1.0, n_steps)): system.evolve_model(time) hydro.gas_particles.velocity = velocity_damp_factor \ * hydro.gas_particles.velocity monitor_func(system, i_step, time, n_steps) return in_hydro.copy()
def gravity_and_stellar_evolution(number_of_stars, size, end_time, sync_timestep=1 | units.Myr, plot_timestep=10 | units.Myr): stars, converter = create_stars(number_of_stars, size) gravity = Hermite(converter) stellar = SeBa() bridge = Bridge() gravity.particles.add_particles(stars) stellar.particles.add_particles(stars) bridge.add_system(gravity) bridge.add_system(stellar) bridge.channels.add_channel( stellar.particles.new_channel_to(gravity.particles, attributes=["mass", "radius"])) bridge.timestep = sync_timestep plot_channels = Channels() plot_channels.add_channel(stellar.particles.new_channel_to(stars)) plot_channels.add_channel(gravity.particles.new_channel_to(stars)) time = 0 | units.Myr while time <= end_time: bridge.evolve_model(time) plot_channels.copy() plot_results(stars, time) time += plot_timestep
def test9(self): print("Testing FastKick for Bridge: evolving a binary") particles = Particles(2) particles.mass = [3.0, 1.0] | units.MSun particles.position = [0, 0, 0] | units.AU particles.velocity = [0, 0, 0] | units.km / units.s particles[1].x = 2.0 | units.AU particles[1].vy = (constants.G * (4.0 | units.MSun) / (2.0 | units.AU)).sqrt() particles.move_to_center() primary_sys = new_gravity_code(particles[:1]) secondary_sys = new_gravity_code(particles[1:]) primary = primary_sys.particles[0] P = 2 * math.pi * primary.x / primary.vy converter = nbody_system.nbody_to_si(1.0 | units.MSun, 1.0 | units.AU) kick_from_primary = CalculateFieldForCodesUsingReinitialize( self.new_fastkick_instance(converter), (primary_sys, )) kick_from_secondary = CalculateFieldForCodesUsingReinitialize( self.new_fastkick_instance(converter), (secondary_sys, )) bridgesys = Bridge(timestep=P / 64.0) bridgesys.add_system(primary_sys, (kick_from_secondary, )) bridgesys.add_system(secondary_sys, (kick_from_primary, )) position_at_start = primary.position.x bridgesys.evolve_model(P / 4.0) self.assertAlmostRelativeEqual(position_at_start, primary.position.y, 2) bridgesys.evolve_model(P / 2.0) self.assertAlmostRelativeEqual(position_at_start, -primary.position.x, 2) bridgesys.evolve_model(P) kick_from_primary.code.stop() kick_from_secondary.code.stop() self.assertAlmostRelativeEqual(position_at_start, primary.position.x, 2)
def evolve_coupled_system(binary_system, giant_system, t_end, n_steps, do_energy_evolution_plot, previous_data=None): directsum = CalculateFieldForParticles(particles=giant_system.particles, gravity_constant=constants.G) directsum.smoothing_length_squared = giant_system.parameters.gas_epsilon**2 coupled_system = Bridge(timestep=(t_end / (2 * n_steps)), verbose=False, use_threading=True) coupled_system.add_system(binary_system, (directsum,), False) coupled_system.add_system(giant_system, (binary_system,), False) times = (t_end * list(range(1, n_steps+1)) / n_steps).as_quantity_in(units.day) if previous_data: with open(previous_data, 'rb') as file: (all_times, potential_energies, kinetic_energies, thermal_energies, giant_center_of_mass, ms1_position, ms2_position, giant_center_of_mass_velocity, ms1_velocity, ms2_velocity) = pickle.load(file) all_times.extend(times + all_times[-1]) else: all_times = times if do_energy_evolution_plot: potential_energies = coupled_system.particles.potential_energy().as_vector_with_length(1).as_quantity_in(units.erg) kinetic_energies = coupled_system.particles.kinetic_energy().as_vector_with_length(1).as_quantity_in(units.erg) thermal_energies = coupled_system.gas_particles.thermal_energy().as_vector_with_length(1).as_quantity_in(units.erg) else: potential_energies = kinetic_energies = thermal_energies = None giant_center_of_mass = [] | units.RSun ms1_position = [] | units.RSun ms2_position = [] | units.RSun giant_center_of_mass_velocity = [] | units.km / units.s ms1_velocity = [] | units.km / units.s ms2_velocity = [] | units.km / units.s i_offset = len(giant_center_of_mass) giant_total_mass = giant_system.particles.total_mass() ms1_mass = binary_system.particles[0].mass ms2_mass = binary_system.particles[1].mass print(" Evolving for", t_end) for i_step, time in enumerate(times): coupled_system.evolve_model(time) print(" Evolved to:", time, end=' ') if do_energy_evolution_plot: potential_energies.append(coupled_system.particles.potential_energy()) kinetic_energies.append(coupled_system.particles.kinetic_energy()) thermal_energies.append(coupled_system.gas_particles.thermal_energy()) giant_center_of_mass.append(giant_system.particles.center_of_mass()) ms1_position.append(binary_system.particles[0].position) ms2_position.append(binary_system.particles[1].position) giant_center_of_mass_velocity.append(giant_system.particles.center_of_mass_velocity()) ms1_velocity.append(binary_system.particles[0].velocity) ms2_velocity.append(binary_system.particles[1].velocity) a_giant, e_giant = calculate_orbital_elements(ms1_mass, ms2_mass, ms1_position, ms2_position, ms1_velocity, ms2_velocity, giant_total_mass, giant_center_of_mass, giant_center_of_mass_velocity) print("Outer Orbit:", time.in_(units.day), a_giant[-1].in_(units.AU), e_giant[-1], ms1_mass.in_(units.MSun), ms2_mass.in_(units.MSun), giant_total_mass.in_(units.MSun)) if i_step % 10 == 9: snapshotfile = os.path.join("snapshots", "hydro_triple_{0:=04}_gas.amuse".format(i_step + i_offset)) write_set_to_file(giant_system.gas_particles, snapshotfile, format='amuse') snapshotfile = os.path.join("snapshots", "hydro_triple_{0:=04}_core.amuse".format(i_step + i_offset)) write_set_to_file(giant_system.dm_particles, snapshotfile, format='amuse') snapshotfile = os.path.join("snapshots", "hydro_triple_{0:=04}_binary.amuse".format(i_step + i_offset)) write_set_to_file(binary_system.particles, snapshotfile, format='amuse') datafile = os.path.join("snapshots", "hydro_triple_{0:=04}_info.amuse".format(i_step + i_offset)) with open(datafile, 'wb') as outfile: pickle.dump((all_times[:len(giant_center_of_mass)], potential_energies, kinetic_energies, thermal_energies, giant_center_of_mass, ms1_position, ms2_position, giant_center_of_mass_velocity, ms1_velocity, ms2_velocity), outfile) figname1 = os.path.join("plots", "hydro_triple_small{0:=04}.png".format(i_step + i_offset)) figname2 = os.path.join("plots", "hydro_triple_large{0:=04}.png".format(i_step + i_offset)) print(" - Hydroplots are saved to: ", figname1, "and", figname2) for plot_range, plot_name in [(8|units.AU, figname1), (40|units.AU, figname2)]: if HAS_PYNBODY: pynbody_column_density_plot(coupled_system.gas_particles, width=plot_range, vmin=26, vmax=32) scatter(coupled_system.dm_particles.x, coupled_system.dm_particles.y, c="w") else: pyplot.figure(figsize = [16, 16]) sph_particles_plot(coupled_system.gas_particles, gd_particles=coupled_system.dm_particles, view=plot_range*[-0.5, 0.5, -0.5, 0.5]) pyplot.savefig(plot_name) pyplot.close() coupled_system.stop() make_movie() if do_energy_evolution_plot: energy_evolution_plot(all_times[:len(kinetic_energies)-1], kinetic_energies, potential_energies, thermal_energies) print(" Calculating semimajor axis and eccentricity evolution for inner binary") # Some temporary variables to calculate semimajor_axis and eccentricity evolution total_mass = ms1_mass + ms2_mass rel_position = ms1_position - ms2_position rel_velocity = ms1_velocity - ms2_velocity separation_in = rel_position.lengths() speed_squared_in = rel_velocity.lengths_squared() # Now calculate the important quantities: semimajor_axis_binary = (constants.G * total_mass * separation_in / (2 * constants.G * total_mass - separation_in * speed_squared_in)).as_quantity_in(units.AU) eccentricity_binary = numpy.sqrt(1.0 - (rel_position.cross(rel_velocity)**2).sum(axis=1) / (constants.G * total_mass * semimajor_axis_binary)) print(" Calculating semimajor axis and eccentricity evolution of the giant's orbit") # Some temporary variables to calculate semimajor_axis and eccentricity evolution rel_position = ((ms1_mass * ms1_position + ms2_mass * ms2_position)/total_mass - giant_center_of_mass) rel_velocity = ((ms1_mass * ms1_velocity + ms2_mass * ms2_velocity)/total_mass - giant_center_of_mass_velocity) total_mass += giant_total_mass separation = rel_position.lengths() speed_squared = rel_velocity.lengths_squared() # Now calculate the important quantities: semimajor_axis_giant = (constants.G * total_mass * separation / (2 * constants.G * total_mass - separation * speed_squared)).as_quantity_in(units.AU) eccentricity_giant = numpy.sqrt(1.0 - (rel_position.cross(rel_velocity)**2).sum(axis=1) / (constants.G * total_mass * semimajor_axis_giant)) orbit_parameters_plot(semimajor_axis_binary, semimajor_axis_giant, all_times[:len(semimajor_axis_binary)]) orbit_ecc_plot(eccentricity_binary, eccentricity_giant, all_times[:len(eccentricity_binary)]) orbit_parameters_plot(separation_in.as_quantity_in(units.AU), separation.as_quantity_in(units.AU), all_times[:len(eccentricity_binary)], par_symbol="r", par_name="separation") orbit_parameters_plot(speed_squared_in.as_quantity_in(units.km**2 / units.s**2), speed_squared.as_quantity_in(units.km**2 / units.s**2), all_times[:len(eccentricity_binary)], par_symbol="v^2", par_name="speed_squared")
# Send the Initial Stellear Mass to init_mass Attribute of the Master Set # This is for FRESCO Compatibility as SEBA is Odd about Ages ... MasterSet.init_mass = MasterSet.mass # Copies over the SEV Desired Stellar Traits to the Master Set sev_code.evolve_model(1 | units.yr) sev_to_MS_channel.copy() bridge_code.channels.copy() # Begin Evolving the Cluster while time < end_time: sys.stdout.flush() # Kick the Gravity Bridge time += delta_t bridge_code.evolve_model(time) util.update_MasterSet(gravity, multiples_code, sev_code) # Copy the index (ID) as used in the module to the id field in # memory. The index is not copied by default, as different # codes may have different indices for the same particle and # we don't want to overwrite silently. grav_to_MS_channel.copy_attribute("index_in_code", "id") # Copy values from the module to the set in memory. grav_to_MS_channel.copy() # Copies over the SEV Desired Stellar Traits to the Master Set sev_to_MS_channel.copy() # Write Out the Data Every 5 Time Steps if step_index%5 == 0: write.write_time_step(MasterSet, time, cluster_name)
util.resolve_supernova(supernova_detection, Stellar_Bodies, t_start) channel_from_sev_to_stellar.copy_attributes( ["mass", "luminosity", "stellar_type", "temperature", "age"]) channel_from_gravitating_to_multi.copy_attributes( ["mass", "vx", "vy", "vz"]) # Ensuring that Multiples Picks up All Desired Systems gravity_code.parameters.begin_time = t_start gravity_code.parameters.zero_step_mode = 1 multiples_code.evolve_model(gravity_code.model_time) gravity_code.parameters.zero_step_mode = 0 # Ensuring the Gravity Code Starts at the Right Time t_current = t_start bridge_code.evolve_model(t_current, timestep=t_start / 4.) min_r = LargeScaleConverter.to_nbody(1000 | units.AU) print( [r for r in gravity_code.particles.radius if r.number <= min_r.number]) # ------------------------------------- # # Evolving the Cluster # # ------------------------------------- # # TODO: Implement Leap-Frog Coupling of Stellar Evolution & Gravity step_index = 0 #t_catch = t_start + (2000 | units.yr) E0 = print_diagnostics(multiples_code)
class grav_gas_sse(object): def __init__(self, grav_code, gas_code, se_code, grav_couple_code, conv, mgas, star_parts, gas_parts, dt_feedback, dt_fast, grav_parameters=(), gas_parameters=(), couple_parameters=(), feedback_efficiency=.01, feedback_radius=0.01 | units.parsec, total_feedback_energy=zero, evo_particles=None, star_particles_addition=None, start_time_offset=zero, feedback_safety=1.e-4 * 1.e51 | units.erg, feedback_dt=(10. | units.yr, 7500. | units.yr), feedback_period=20000. | units.yr, feedback_lasttime=None, grav_code_extra=dict(mode='gpu', redirection='none'), gas_code_extra=dict(number_of_workers=3, use_gl=False, redirection='none'), se_code_extra=dict(redirection='none'), grav_couple_code_extra=dict()): self.codes = (gas_code, grav_code, se_code, grav_couple_code) self.parameters = (grav_parameters, gas_parameters, couple_parameters) self.conv = conv self.sph = sys_from_parts(gas_code, gasparts=gas_parts, parameters=gas_parameters, converter=conv, extra=gas_code_extra) self.grav = sys_from_parts(grav_code, parts=star_parts, parameters=grav_parameters, converter=conv, extra=grav_code_extra) print gas_parameters print print self.sph.parameters #~self.sph_grav=reinitializecopycat(grav_couple_code, (self.sph,self.grav), conv, #~parameters=couple_parameters, extra=grav_couple_code_extra) #~self.star_grav=reinitializecopycat(grav_couple_code, (self.sph,), conv, #~parameters=couple_parameters, extra=grav_couple_code_extra) new_couple_code1 = grav_couple_code(conv, **grav_couple_code_extra) for param, value in couple_parameters: new_couple_code1.parameters.__setattr__(param, value) self.sph_grav = CalculateFieldForCodesUsingReinitialize( new_couple_code1, (self.sph, self.grav)) new_couple_code2 = grav_couple_code(conv, **grav_couple_code_extra) for param, value in couple_parameters: new_couple_code2.parameters.__setattr__(param, value) self.star_grav = CalculateFieldForCodesUsingReinitialize( new_couple_code2, (self.sph, )) self.fast = Bridge(verbose=True, timestep=dt_fast) self.fast.add_system(self.sph, (self.sph_grav, ), False) self.fast.add_system(self.grav, (self.star_grav, ), False) self.evo = se_code(**se_code_extra) self.evo.initialize_module_with_default_parameters() if evo_particles is None: self.evo.particles.add_particles(star_parts) else: if len(evo_particles) != len(star_parts): print "evo parts != star parts" raise Exception self.evo.particles.add_particles(evo_particles) self.total_feedback_energy = total_feedback_energy self.dt_feedback = dt_feedback self.dt_fast = dt_fast self.mgas = mgas self.time = self.fast.model_time + start_time_offset self.time_offset = start_time_offset if star_particles_addition is None: self.star_particles_addition = star_parts.empty_copy() self.star_particles_addition.Emech_last_feedback = 0. | units.erg else: self.star_particles_addition = star_particles_addition self.feedback_efficiency = feedback_efficiency self.feedback_radius = feedback_radius self.feedback_safety = feedback_safety self.feedback_dt = feedback_dt self.feedback_period = feedback_period if self.feedback_period < 1.0001 * self.dt_feedback: print "feedback_period < dt_feedback, resetting to dt_feedback=", \ self.dt_feedback.in_(units.yr) self.feedback_period = 1.0001 * self.dt_feedback if feedback_lasttime is None: self.feedback_lasttime = self.time - 2 * self.feedback_period else: self.feedback_lasttime = feedback_lasttime def evolve_model(self, tend): dt = self.dt_feedback time = self.time while time < tend - dt / 2: time += dt print self.feedback_lasttime.in_(units.yr), time.in_( units.yr), self.feedback_period.in_(units.yr) # if time > self.feedback_lasttime+self.feedback_period: # self.sph.parameters.max_size_timestep=self.feedback_dt[1] # print "long timestep" # else: # self.sph.parameters.max_size_timestep=self.feedback_dt[0] # print "short timestep" self.fast.evolve_model(time - self.time_offset) print self.fast.model_time, ',', self.sph.model_time, self.grav.model_time self.evo.evolve_model(self.fast.model_time) energy_added = self.mechanical_feedback() print energy_added.in_(units.erg), self.feedback_safety.in_( units.erg) print self.feedback_lasttime.in_(units.yr), time.in_(units.yr) if energy_added > self.feedback_safety: self.feedback_lasttime = time self.time = self.fast.model_time + self.time_offset def mechanical_feedback(self): star_particles = self.star_particles.copy_to_memory() channel = self.particles.new_channel_to(star_particles) channel.copy_attributes(["x", "y", "z", "vx", "vy", "vz"]) channel.copy_attribute("mass", "grav_mass") del channel channel = self.star_particles_addition.new_channel_to(star_particles) channel.copy_attribute("Emech_last_feedback") del channel new_sph = datamodel.Particles(0) star_particles.dmass = star_particles.grav_mass - star_particles.mass star_particles.u = ( star_particles.Emech - star_particles.Emech_last_feedback) / star_particles.dmass if numpy.any((star_particles.Emech - star_particles.Emech_last_feedback) < zero): print "feedback error" raise Exception losers = star_particles.select_array(lambda x: x > self.mgas, ["dmass"]) while len(losers) > 0: add = datamodel.Particles(len(losers)) add.mass = self.mgas add.h_smooth = 0. | units.parsec dx, dy, dz = uniform_unit_sphere(len(losers)).make_xyz() add.x = losers.x + self.feedback_radius * dx add.y = losers.y + self.feedback_radius * dy add.z = losers.z + self.feedback_radius * dz add.vx = losers.vx add.vy = losers.vy add.vz = losers.vz add.u = self.feedback_efficiency * losers.u losers.grav_mass -= self.mgas losers.Emech_last_feedback += self.mgas * losers.u new_sph.add_particles(add) losers = star_particles.select_array( lambda x, y: x - y > self.mgas, ["grav_mass", "mass"]) print "gas particles added:", len(new_sph) if len(new_sph) == 0: return zero self.sph.gas_particles.add_particles(new_sph) feedback_energy_added = (new_sph.mass * new_sph.u).sum() self.total_feedback_energy = self.total_feedback_energy + feedback_energy_added channel = star_particles.new_channel_to(self.particles) channel.copy_attribute("grav_mass", "mass") del channel channel = star_particles.new_channel_to(self.star_particles_addition) channel.copy_attribute("Emech_last_feedback") del channel return feedback_energy_added def synchronize_model(self): return self.fast.synchronize_model() @property def kinetic_energy(self): return self.fast.kinetic_energy @property def potential_energy(self): return self.fast.potential_energy @property def thermal_energy(self): return self.fast.thermal_energy @property def feedback_energy(self): return self.total_feedback_energy @property def model_time(self): return self.time @property def particles(self): return self.fast.particles @property def gas_particles(self): return self.fast.gas_particles @property def star_particles(self): return self.evo.particles def dump_system_state(self, filename): from amuse.io import write_set_to_file import cPickle write_set_to_file(self.grav.particles, filename + ".grav", "amuse", append_to_file=False) write_set_to_file(self.gas_particles, filename + ".gas", "amuse", append_to_file=False) write_set_to_file(self.star_particles, filename + ".evo", "amuse", append_to_file=False) write_set_to_file(self.star_particles_addition, filename + ".add", "amuse", append_to_file=False) f = open(filename + ".data", 'wb') print self.total_feedback_energy cPickle.dump( (self.codes, self.conv, self.parameters, self.mgas, self.feedback_efficiency, self.feedback_radius, self.time, self.dt_feedback, self.dt_fast, self.total_feedback_energy.in_( 1.e51 * units.erg), self.feedback_safety, self.feedback_dt, self.feedback_period, self.feedback_lasttime), f) f.close() @classmethod def load_system_state(cls, filename, new_gas_options=(), grav_code_extra=dict(mode='gpu', redirection='none'), gas_code_extra=dict(number_of_workers=3, use_gl=False, redirection='none'), se_code_extra=dict(redirection='none'), grav_couple_code_extra=dict()): from amuse.io import read_set_from_file import cPickle star_parts = read_set_from_file(filename + ".grav", 'amuse') gas_parts = read_set_from_file(filename + ".gas", 'amuse') evo = read_set_from_file(filename + ".evo", 'amuse') add = read_set_from_file(filename + ".add", 'amuse') f = open(filename + ".data", 'r') data = cPickle.load(f) f.close() gas_code = data[0][0] grav_code = data[0][1] se_code = data[0][2] grav_couple_code = data[0][3] conv = data[1] gravp = data[2][0] gasp = data[2][1] cp = data[2][2] mgas = data[3] fe = data[4] fr = data[5] to = data[6] dt_feedback = data[7] dt_fast = data[8] tfe = data[9] fs = data[10] fdt = data[11] fp = data[12] flt = data[13] gasp = gasp + new_gas_options return conv, cls(grav_code, gas_code, se_code, grav_couple_code, conv, mgas, star_parts, gas_parts, dt_feedback, dt_fast, grav_parameters=gravp, gas_parameters=gasp, couple_parameters=cp, feedback_efficiency=fe, feedback_radius=fr, total_feedback_energy=tfe, evo_particles=evo, star_particles_addition=add, start_time_offset=to, feedback_safety=fs, feedback_dt=fdt, feedback_period=fp, feedback_lasttime=flt, grav_code_extra=grav_code_extra, gas_code_extra=gas_code_extra, se_code_extra=se_code_extra, grav_couple_code_extra=grav_couple_code_extra)
gravity_code.parameters.begin_time = t_start # ------------------------------------- # # Evolving the Cluster # # ------------------------------------- # # TODO: Implement Leap-Frog Coupling of Stellar Evolution & Gravity t_current = t_start step_index = 0 E0 = print_diagnostics(multiples_code) while t_current <= t_end: # Increase the Current Time by the Time-Step t_current += delta_t # Evolve the Gravitational Codes ( via Bridge Code) bridge_code.evolve_model(t_current) # Sync the Gravitational Codes w/ the "Gravitating_Bodies" Superset channel_from_multi_to_gravitating.copy_attributes( ['x', 'y', 'z', 'vx', 'vy', 'vz']) # (On a Copy) Recursively Expand All Top-Level Parent Particles & Update Subsets # Note: This Updates the Children's Positions Relative to their Top-Level Parent's Position subset_sync = ChildUpdater() subset_sync.update_children_bodies(multiples_code, Individual_Stars, Planets) # Evolve the Stellar Codes (via SEV Code with Channels) # TODO: Ensure Binaries are Evolved Correctly (See Section 3.2.8) sev_code.evolve_model(t_current)
class GravityHydroStellar(object): def __init__(self, gravity, hydro, stellar, gravity_to_hydro, hydro_to_gravity, time_step_bridge, time_step_feedback, feedback_gas_particle_mass, feedback_efficiency=0.01, feedback_radius=0.01 | units.parsec, verbose=True): self.gravity = gravity self.hydro = hydro self.stellar = stellar self.gravity_to_hydro = gravity_to_hydro self.hydro_to_gravity = hydro_to_gravity self.time_step_bridge = time_step_bridge self.time_step_feedback = time_step_feedback self.feedback_gas_particle_mass = feedback_gas_particle_mass self.feedback_efficiency = feedback_efficiency self.feedback_radius = feedback_radius self.verbose = verbose self.bridge = Bridge(verbose=verbose, timestep=self.time_step_bridge, use_threading=False) self.bridge.add_system(self.hydro, (self.gravity_to_hydro, )) self.bridge.add_system(self.gravity, (self.hydro_to_gravity, )) self.star_particles = self.stars_with_mechanical_luminosity( self.stellar.particles) self.total_feedback_energy = zero self.current_time = 0.0 | units.Myr def stars_with_mechanical_luminosity(self, particles): result = ParticlesOverlay(particles) def lmech_function(temperature, mass, luminosity, radius): T = temperature.value_in(units.K) M = mass.value_in(units.MSun) L = luminosity.value_in(units.LSun) R = radius.value_in(units.RSun) # Reimers wind below 8000K mass_loss = (4.0e-13 | units.MSun / units.yr) * L * R / M # ... wind above 8000K i = numpy.where(temperature > 8000 | units.K)[0] mass_loss[i] = (10**-24.06 | units.MSun / units.yr) * ( L**2.45 * M**(-1.1) * T.clip(1000, 8.0e4)**(1.31)) t4 = numpy.log10(T * 1.0e-4).clip(0.0, 1.0) v_terminal = (30 + 4000 * t4) | units.km / units.s return 0.5 * mass_loss * v_terminal**2 result.add_calculated_attribute( "mechanical_luminosity", lmech_function, attributes_names=["temperature", "mass", "luminosity", "radius"]) result.L_mech = result.mechanical_luminosity result.E_mech = (0.0 | units.Myr) * result.L_mech result.E_mech_last_feedback = result.E_mech result.previous_mass = result.mass # Store the mass of the star at the last moment of feedback return result def evolve_model(self, end_time): while self.current_time < end_time - 0.5 * self.time_step_feedback: self.current_time += self.time_step_feedback if self.verbose: time_begin = time.time() print print "GravityHydroStellar: Start evolving..." self.bridge.evolve_model(self.current_time) self.stellar.evolve_model(self.current_time) if self.verbose: print "GravityHydroStellar: Evolved to:", self.current_time model_times = [ getattr(self, code).model_time.as_quantity_in(units.Myr) for code in ["stellar", "bridge", "gravity", "hydro"] ] print " (Stellar: {0}, Bridge: {1}, Gravity: {2}, Hydro: {3})".format( *model_times) print "GravityHydroStellar: Call mechanical_feedback" self.mechanical_feedback(self.time_step_feedback) if self.verbose: print "GravityHydroStellar: store system state" self.store_system_state() if self.verbose: print "GravityHydroStellar: Iteration took {0} seconds.".format( time.time() - time_begin) def mechanical_feedback(self, time_step): L_mech_new = self.star_particles.mechanical_luminosity self.star_particles.E_mech += time_step * 0.5 * ( self.star_particles.L_mech + L_mech_new) self.star_particles.L_mech = L_mech_new self.star_particles.dmass = self.star_particles.previous_mass - self.star_particles.mass self.star_particles.n_feedback_particles = numpy.array( self.star_particles.dmass / self.feedback_gas_particle_mass).astype(int) losers = self.star_particles.select_array(lambda x: x > 0, ["n_feedback_particles"]) if self.verbose: print "GravityHydroStellar: Number of particles providing mechanical feedback during this step:", len( losers) if len(losers) == 0: return channel = self.gravity.particles.new_channel_to(losers) channel.copy_attributes(["x", "y", "z", "vx", "vy", "vz"]) number_of_new_particles = losers.n_feedback_particles.sum() new_sph_all = Particles(number_of_new_particles) new_sph_all.mass = self.feedback_gas_particle_mass new_sph_all.h_smooth = 0.0 | units.parsec offsets = self.draw_random_position_offsets(number_of_new_particles) i = 0 for loser in losers: i_next = i + loser.n_feedback_particles new = new_sph_all[i:i_next] new.position = loser.position + offsets[i:i_next] new.velocity = loser.velocity new.u = self.feedback_efficiency * ( loser.E_mech - loser.E_mech_last_feedback) / loser.dmass i = i_next losers.previous_mass -= self.feedback_gas_particle_mass * losers.n_feedback_particles losers.E_mech_last_feedback = losers.E_mech channel = losers.new_channel_to(self.gravity.particles) channel.copy_attribute("previous_mass", "mass") if self.verbose: print "GravityHydroStellar: New SPH particles:" print new_sph_all self.hydro.gas_particles.add_particles(new_sph_all) self.total_feedback_energy += (new_sph_all.mass * new_sph_all.u).sum() def draw_random_position_offsets(self, number_of_new_particles): r = numpy.random.uniform(0.0, 1.0, number_of_new_particles) theta = numpy.random.uniform(0.0, numpy.pi, number_of_new_particles) phi = numpy.random.uniform(0.0, 2 * numpy.pi, number_of_new_particles) return self.feedback_radius * numpy.array( (r * sin(theta) * cos(phi), r * sin(theta) * sin(phi), r * cos(theta))).transpose() @property def kinetic_energy(self): return self.bridge.kinetic_energy @property def potential_energy(self): return self.bridge.potential_energy @property def thermal_energy(self): return self.bridge.thermal_energy @property def feedback_energy(self): return self.total_feedback_energy @property def model_time(self): return self.bridge.model_time @property def particles(self): return self.bridge.particles @property def gas_particles(self): return self.bridge.gas_particles def store_system_state(self): snapshot_number = int(0.5 + self.current_time / self.time_step_feedback) filename = os.path.join( "snapshots", "cluster_snapshot_{0:=06}_".format(snapshot_number)) stars = Particles(keys=self.star_particles.key) self.star_particles.copy_values_of_attributes_to([ "mass", "temperature", "stellar_type", "radius", "luminosity", "age", "L_mech", "E_mech", "E_mech_last_feedback", "previous_mass" ], stars) self.gravity.particles.copy_values_of_attributes_to( ["x", "y", "z", "vx", "vy", "vz"], stars) write_set_to_file(stars, filename + "stars.amuse", "amuse") write_set_to_file(self.hydro.gas_particles, filename + "gas.amuse", "amuse") with open(filename + "info.pkl", "wb") as outfile: cPickle.dump( (self.gravity.unit_converter, self.hydro.unit_converter, self.time_step_bridge, self.time_step_feedback, self.feedback_gas_particle_mass, self.feedback_efficiency, self.feedback_radius, self.verbose, self.total_feedback_energy, self.current_time), outfile) def load_system_state(self, info_file, stars_file): stars = read_set_from_file(stars_file, 'amuse') channel = stars.new_channel_to(self.star_particles) channel.copy_attributes( ["L_mech", "E_mech", "E_mech_last_feedback", "previous_mass"]) with open(info_file, "rb") as infile: (tmp1, tmp2, self.time_step_bridge, self.time_step_feedback, self.feedback_gas_particle_mass, self.feedback_efficiency, self.feedback_radius, self.verbose, self.total_feedback_energy, self.current_time) = cPickle.load(infile) def stop(self): print "STOP fieldcode" self.bridge.codes[0].field_codes[0].code.stop() print "STOP fieldcode" self.bridge.codes[1].field_codes[0].code.stop() print "STOP bridge" self.bridge.stop() print "STOP stellar" self.stellar.stop()