def _new_galactics_model(halo_number_of_particles, unit_system_converter=None, do_scale=False, verbose=False, **keyword_arguments): code = keyword_arguments.pop("code") instance = code(unit_converter=unit_system_converter) instance.parameters.halo_number_of_particles = halo_number_of_particles for (key, value) in keyword_arguments.iteritems(): setattr(instance.parameters, key, value) if verbose: print "adopted galaxy model parameters:" print instance.parameters instance.generate_particles() result = instance.particles.copy() if hasattr(instance, "gas_particles") and len(instance.gas_particles) > 0: resultgas = instance.gas_particles.copy() else: resultgas = Particles() instance.stop() if len(resultgas) > 0: allpart = ParticlesSuperset([result, resultgas]) else: allpart = result allpart.move_to_center() # do_scale *is* possible for case with unit converter # note that in case of scaling the output galaxy parameters may be very different from the model # parameters input if do_scale: print "Warning: do_scale for a large galactics model may be very slow" if verbose: print "Warning: do_scale typically changes the galaxy scale parameters quite a lot from the input parameters" if len(resultgas) > 0: # this is fixable raise Exception( "scaling of galaxy models with gas currently not possible") allpart.scale_to_standard(convert_nbody=unit_system_converter) if not unit_system_converter is None: result = ParticlesWithUnitsConverted( result, unit_system_converter.as_converter_from_si_to_generic()) result = result.copy() if len(resultgas) > 0: resultgas = ParticlesWithUnitsConverted( resultgas, unit_system_converter.as_converter_from_si_to_generic()) resultgas = resultgas.copy() if len(resultgas) > 0: # resultincludes the gas particles (but not under the same keys) return resultgas, result else: return result
def test5(self): print("Testing SinkParticles accrete, one particle within two sinks' radii") particles = Particles(10) particles.radius = 42.0 | units.RSun particles.mass = list(range(1,11)) | units.MSun particles.position = [[i, 2*i, 3*i] for i in range(10)] | units.parsec particles.velocity = [[i, 0, -i] for i in range(10)] | units.km/units.s particles.age = list(range(10)) | units.Myr copy = particles.copy() sinks = SinkParticles(particles[[3, 7]], sink_radius=[4,12]|units.parsec,looping_over=self.looping_over) self.assertEqual(sinks.sink_radius, [4.0, 12.0] | units.parsec) self.assertEqual(sinks.mass, [4.0, 8.0] | units.MSun) self.assertEqual(sinks.position, [[3, 6, 9], [7, 14, 21]] | units.parsec) sinks.accrete(particles) self.assertEqual(len(particles), 4) # 6 particles were accreted self.assertEqual(sinks.mass, [12.0, 40.0] | units.MSun) # mass of sinks increased self.assertEqual(sinks.get_intersecting_subset_in(particles).mass, [12.0, 40.0] | units.MSun) # original particles' masses match self.assertEqual(particles.total_mass(), copy.total_mass()) # total mass is conserved self.assertEqual(particles.center_of_mass(), copy.center_of_mass()) # center of mass is conserved self.assertEqual(particles.center_of_mass_velocity(), copy.center_of_mass_velocity()) # center of mass velocity is conserved self.assertEqual(particles.total_momentum(), copy.total_momentum()) # momentum is conserved self.assertEqual(particles.total_angular_momentum()+sinks.angular_momentum.sum(axis=0), copy.total_angular_momentum()) # angular_momentum is conserved
def test5(self): print "Testing SinkParticles accrete, one particle within two sinks' radii" particles = Particles(10) particles.radius = 42.0 | units.RSun particles.mass = range(1,11) | units.MSun particles.position = [[i, 2*i, 3*i] for i in range(10)] | units.parsec particles.velocity = [[i, 0, -i] for i in range(10)] | units.km/units.s particles.age = range(10) | units.Myr copy = particles.copy() sinks = SinkParticles(particles[[3, 7]], sink_radius=[4,12]|units.parsec,looping_over=self.looping_over) self.assertEqual(sinks.sink_radius, [4.0, 12.0] | units.parsec) self.assertEqual(sinks.mass, [4.0, 8.0] | units.MSun) self.assertEqual(sinks.position, [[3, 6, 9], [7, 14, 21]] | units.parsec) sinks.accrete(particles) self.assertEqual(len(particles), 4) # 6 particles were accreted self.assertEqual(sinks.mass, [12.0, 40.0] | units.MSun) # mass of sinks increased self.assertEqual(sinks.get_intersecting_subset_in(particles).mass, [12.0, 40.0] | units.MSun) # original particles' masses match self.assertEqual(particles.total_mass(), copy.total_mass()) # total mass is conserved self.assertEqual(particles.center_of_mass(), copy.center_of_mass()) # center of mass is conserved self.assertEqual(particles.center_of_mass_velocity(), copy.center_of_mass_velocity()) # center of mass velocity is conserved self.assertEqual(particles.total_momentum(), copy.total_momentum()) # momentum is conserved self.assertEqual(particles.total_angular_momentum()+sinks.angular_momentum.sum(axis=0), copy.total_angular_momentum()) # angular_momentum is conserved
def _new_galactics_model(halo_number_of_particles, unit_system_converter=None, do_scale=False, verbose=False, **keyword_arguments): code=keyword_arguments.pop("code") instance = code(unit_converter=unit_system_converter, redirection="none" if verbose else "null") instance.parameters.halo_number_of_particles = halo_number_of_particles for (key, value) in keyword_arguments.iteritems(): setattr(instance.parameters, key, value) if verbose: print "adopted galaxy model parameters:" print instance.parameters instance.generate_particles() result = instance.particles.copy() if hasattr(instance,"gas_particles") and len(instance.gas_particles)>0: resultgas=instance.gas_particles.copy() else: resultgas=Particles() instance.stop() if len(resultgas)>0: allpart=ParticlesSuperset([result, resultgas]) else: allpart=result allpart.move_to_center() # do_scale *is* possible for case with unit converter # note that in case of scaling the output galaxy parameters may be very different from the model # parameters input if do_scale: print "Warning: do_scale for a large galactics model may be very slow" if verbose: print "Warning: do_scale typically changes the galaxy scale parameters quite a lot from the input parameters" if len(resultgas)>0: # this is fixable raise Exception("scaling of galaxy models with gas currently not possible") allpart.scale_to_standard(convert_nbody=unit_system_converter) if not unit_system_converter is None: result = ParticlesWithUnitsConverted(result, unit_system_converter.as_converter_from_si_to_generic()) result = result.copy() if len(resultgas)>0: resultgas = ParticlesWithUnitsConverted(resultgas, unit_system_converter.as_converter_from_si_to_generic()) resultgas = resultgas.copy() if len(resultgas)>0: # resultincludes the gas particles (but not under the same keys) return resultgas, result else: return result
def test2(self): colliders = Particles(2) colliders.mass = [5, 5] | units.kg colliders.position = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]] | units.m colliders.velocity = [[0.0, 0.0, 0.0], [2.0, 2.0, 2.0]] | units.m / units.s merged = StickySpheres(mass_loss=0.2).handle_collision(colliders[0], colliders[1]) self.assertTrue(isinstance(merged, Particles)) self.assertEqual(merged.mass, 8 | units.kg) self.assertAlmostEqual(merged.position, [0.5, 0.5, 0.5] | units.m) self.assertAlmostEqual(merged.velocity, [1.0, 1.0, 1.0] | units.m / units.s) copy = colliders.copy() copy.move_to_center() self.assertAlmostEqual(colliders.kinetic_energy(), merged.as_set().kinetic_energy() / 0.8 + copy.kinetic_energy())
def test1(self): colliders = Particles(2) colliders.mass = [5, 2] | units.kg colliders.position = [[0.0, 0.0, 0.0], [0.7, 1.4, -0.35]] | units.m colliders.velocity = [[0.4, -0.6, 0.0], [0.0, 0.0, -3.0]] | units.m / units.s self.assertAlmostEqual(colliders.center_of_mass_velocity().length(), 1.0 | units.m / units.s) merged = StickySpheres().handle_collision(colliders[0], colliders[1]) self.assertTrue(isinstance(merged, Particles)) self.assertEqual(merged.mass, 7 | units.kg) self.assertAlmostEqual(merged.position, [0.2, 0.4, -0.1] | units.m) self.assertAlmostEqual(merged.velocity, ([2.0, -3.0, -6.0] | units.m / units.s) / 7.0) self.assertAlmostEqual(merged.velocity.length(), 1.0 | units.m / units.s) copy = colliders.copy() copy.move_to_center() self.assertAlmostEqual(colliders.kinetic_energy(), merged.as_set().kinetic_energy() + copy.kinetic_energy())
def result(self): masses, x,y,z, vx,vy,vz, internal_energies = self.new_model() result = Particles(self.actualN) result.mass = nbody_system.mass.new_quantity(masses) result.x = nbody_system.length.new_quantity(x) result.y = nbody_system.length.new_quantity(y) result.z = nbody_system.length.new_quantity(z) result.vx = nbody_system.speed.new_quantity(vx) result.vy = nbody_system.speed.new_quantity(vy) result.vz = nbody_system.speed.new_quantity(vz) result.u = nbody_system.specific_energy.new_quantity(internal_energies) if not self.convert_nbody is None: result = ParticlesWithUnitsConverted(result, self.convert_nbody.as_converter_from_si_to_generic()) result = result.copy() return result
def result(self): masses, positions, velocities, internal_energies = self.new_model() result = Particles(self.actual_number_of_particles) result.mass = nbody_system.mass.new_quantity(masses) result.position = nbody_system.length.new_quantity(positions) result.velocity = nbody_system.speed.new_quantity(velocities) result.u = nbody_system.specific_energy.new_quantity(internal_energies) result.position -= result.center_of_mass() if self.do_scale: scale_factor = (result.potential_energy(G=nbody_system.G)) / (-0.5 | nbody_system.energy) result.position *= scale_factor if not self.convert_nbody is None: result = ParticlesWithUnitsConverted(result, self.convert_nbody.as_converter_from_si_to_generic()) result = result.copy() return result
def rgb_frame( stars, dryrun=False, vmax=None, percentile=0.9995, multi_psf=False, sourcebands="ubvri", image_width=12. | units.parsec, image_size=[1024, 1024], mapper_factory=None, gas=None, mapper_code=None, zoom_factor=1.0, psf_type="hubble", psf_sigma=1.0, verbose=True, ): if gas is None: gas = Particles() if verbose: print("luminosities..") for band in sourcebands: setattr( stars, band + "_band", 4 * numpy.pi * stars.radius**2 * filter_band_flux( "bess-" + band + ".pass", lambda x: B_lambda(x, stars.temperature), ), ) if verbose: print("..raw images..") if mapper_code != "gridify": # Use mapper to make raw (pre-convolved) images mapper = mapper_factory() stars_in_mapper = mapper.particles.add_particles(stars) gas_in_mapper = mapper.particles.add_particles(gas) mapper.parameters.projection_direction = [0, 0, 1] mapper.parameters.upvector = [0, -1, 0] raw_images = dict() for band in sourcebands: assign_weights_and_opacities( band, stars_in_mapper, gas_in_mapper, stars, gas, Nstar=500, ) # mapper.particles.weight = getattr( # stars, # band+"_band" # ).value_in(units.LSun) im = mapper.image.pixel_value raw_images[band] = numpy.fliplr(im) mapper.stop() else: # Use simpler python mapping script from .gridify import map_to_grid stars_in_mapper = stars.copy() gas_in_mapper = gas.copy() raw_images = dict() for band in sourcebands: assign_weights_and_opacities( band, stars_in_mapper, gas_in_mapper, stars, gas, Nstar=500, ) allparticles = Particles() allparticles.add_particles(stars_in_mapper) allparticles.add_particles(gas_in_mapper) im = map_to_grid( allparticles.x, allparticles.y, weights=allparticles.weight, image_size=image_size, image_width=image_width, ) raw_images[band] = im convolved_images = dict() if verbose: print("..convolving..") if psf_type == "hubble": psf = get_psf(zoom_factor=zoom_factor) if multi_psf: a = numpy.arange(image_size[0]) / float(image_size[0] - 1) b = numpy.arange(image_size[1]) / float(image_size[1] - 1) w1 = numpy.outer(a, b) w2 = numpy.outer(1. - a, b) w3 = numpy.outer(a, 1. - b) w4 = numpy.outer(1. - a, 1. - b) for key, val in list(raw_images.items()): im1 = Convolve(val, psf[key + '0']) im2 = Convolve(val, psf[key + '1']) im3 = Convolve(val, psf[key + '2']) im4 = Convolve(val, psf[key + '3']) convolved_images[key] = (w1 * im1 + w2 * im2 + w3 * im3 + w4 * im4) else: for key, val in list(raw_images.items()): im1 = Convolve(val, psf[key + '0']) convolved_images[key] = im1 elif psf_type == "gaussian": for key, val in list(raw_images.items()): im1 = gaussian_filter(val, sigma=psf_sigma, order=0) convolved_images[key] = im1 if verbose: print("..conversion to rgb") filter_data = get_filter_data() source = [filter_data['bess-' + x + '.pass'] for x in sourcebands] target = [xyz_data['x'], xyz_data['y'], xyz_data['z']] conv = ColorConverter(source, target) ubv = numpy.array([convolved_images[x] for x in sourcebands]) xyz = numpy.tensordot(conv.conversion_matrix, ubv, axes=(1, 0)) conv_xyz_to_lin = XYZ_to_sRGB_linear() srgb_l = numpy.tensordot( conv_xyz_to_lin.conversion_matrix, xyz, axes=(1, 0), ) if dryrun or vmax is None: flat_sorted = numpy.sort(srgb_l.flatten()) n = len(flat_sorted) vmax = flat_sorted[int(1. - 3 * (1. - percentile) * n)] print(("vmax:", vmax)) if dryrun: return vmax conv_lin_to_sRGB = sRGB_linear_to_sRGB() srgb = conv_lin_to_sRGB.convert(srgb_l / vmax) # r = numpy.fliplr(srgb[0, :, :]) # g = numpy.fliplr(srgb[1, :, :]) # b = numpy.fliplr(srgb[2, :, :]) rgb = numpy.zeros(image_size[0] * image_size[1] * 3) rgb = rgb.reshape(image_size[0], image_size[1], 3) rgb[:, :, 0] = srgb[0, :, :].T rgb[:, :, 1] = srgb[1, :, :].T rgb[:, :, 2] = srgb[2, :, :].T image = dict( pixels=rgb, size=rgb.size, ) return vmax, image
class SimpleXSplitSet(SimpleX): def __init__(self,**options): SimpleX.__init__(self,**options) self.gas_particles=Particles() self.src_particles=Particles() def commit_particles(self): sites=self.gas_particles.copy() sites.flux=0. | units.s**-1 for p in self.src_particles: nearest=sites.find_closest_particle_to(p.x,p.y,p.z) nearest.flux+=p.luminosity self.particles.add_particles(sites) self.simplex_to_gas_channel=self.particles.new_channel_to(self.gas_particles) self.overridden().commit_particles() if hasattr(sites,"du_dt"): attributes=["du_dt"] channel=sites.new_channel_to(self.particles) channel.copy_attributes(attributes) del sites def recommit_particles(self): sites=self.gas_particles.copy() sites.flux=0. | units.s**-1 for p in self.src_particles: nearest=sites.find_closest_particle_to(p.x,p.y,p.z) nearest.flux+=p.luminosity # sites.synchronize_to(self.particles) add_set=sites.difference(self.particles) remove_set=self.particles.difference(sites) if len(remove_set)>0: self.particles.remove_particles(remove_set) if len(add_set)>0: self.particles.add_particles(add_set) self.overridden().recommit_particles() channel=sites.new_channel_to(self.particles) attributes=["x","y","z","rho","xion","u","flux"] if hasattr(sites,"metallicity"): attributes.append("metallicity") if hasattr(sites,"du_dt"): attributes.append("du_dt") channel.copy_attributes(attributes) del sites self.overridden().recommit_particles() def evolve_model(self,tend): self.overridden().evolve_model(tend) self.simplex_to_gas_channel.copy_attributes(["xion","u","metallicity"]) def define_state(self, object): CommonCode.define_state(self, object) object.add_transition('INITIALIZED','EDIT','commit_parameters') object.add_transition('RUN','CHANGE_PARAMETERS_RUN','before_set_parameter', False) object.add_transition('EDIT','CHANGE_PARAMETERS_EDIT','before_set_parameter', False) object.add_transition('UPDATE','CHANGE_PARAMETERS_UPDATE','before_set_parameter', False) object.add_transition('CHANGE_PARAMETERS_RUN','RUN','recommit_parameters') object.add_transition('CHANGE_PARAMETERS_EDIT','EDIT','recommit_parameters') object.add_transition('CHANGE_PARAMETERS_UPDATE','UPDATE','recommit_parameters') object.add_method('CHANGE_PARAMETERS_RUN', 'before_set_parameter') object.add_method('CHANGE_PARAMETERS_EDIT', 'before_set_parameter') object.add_method('CHANGE_PARAMETERS_UPDATE','before_set_parameter') object.add_method('CHANGE_PARAMETERS_RUN', 'before_get_parameter') object.add_method('CHANGE_PARAMETERS_EDIT', 'before_get_parameter') object.add_method('CHANGE_PARAMETERS_UPDATE','before_get_parameter') object.add_method('RUN', 'before_get_parameter') object.add_method('EDIT', 'before_get_parameter') object.add_method('UPDATE','before_get_parameter') object.add_method('EVOLVED','before_get_parameter') object.add_method('RUNCOMMIT','before_get_parameter') object.add_method('EDIT', 'new_particle') object.add_method('EDIT', 'delete_particle') object.add_transition('EDIT', 'RUNCOMMIT', 'commit_particles') object.add_transition('RUN', 'UPDATE', 'new_particle', False) object.add_transition('RUN', 'UPDATE', 'delete_particle', False) object.add_transition('UPDATE', 'RUN', 'recommit_particles') object.add_transition('RUN','RUNCOMMIT', 'recommit_particles') object.add_transition('RUNCOMMIT','RUN', 'auto_go_to_run') object.add_transition('RUNCOMMIT', 'EVOLVED', 'evolve_model', False) # object.add_method('EVOLVED', 'evolve_model') object.define_state('RUNCOMMIT') object.add_transition('EVOLVED','RUN', 'synchronize_model') object.add_method('RUN', 'synchronize_model') object.add_method('RUN', 'get_state') object.add_method('RUN', 'get_density') object.add_method('RUN', 'get_position') object.add_method('RUN', 'get_flux') object.add_method('RUN', 'get_ionisation') object.add_method('RUN', 'get_internal_energy') object.add_method('RUN', 'set_dinternal_energy_dt') object.add_method('RUN', 'get_dinternal_energy_dt') object.add_method('UPDATE', 'set_dinternal_energy_dt') object.add_method('UPDATE', 'get_dinternal_energy_dt') object.add_method('INITIALIZED', 'set_hilbert_order') object.add_method('INITIALIZED', 'set_box_size') object.add_method('INITIALIZED', 'set_timestep') object.add_method('INITIALIZED', 'set_source_Teff') object.add_method('INITIALIZED', 'set_number_frequency_bins') object.add_method('INITIALIZED', 'set_thermal_evolution') object.add_method('INITIALIZED', 'set_blackbody_spectrum') object.add_method('INITIALIZED', 'set_metal_cooling') object.add_method('INITIALIZED', 'set_recombination_radiation') object.add_method('INITIALIZED', 'set_collisional_ionization')
def main(): numpy.random.seed(42) evo_headstart = 0.0 | units.Myr dt_base = 0.001 | units.Myr dt = dt_base time = 0 | units.Myr time_end = 8 | units.Myr Tmin = 22 | units.K gas_density = 5e3 | units.amu * units.cm**-3 increase_vol = 2 Ngas = increase_vol**3 * 10000 Mgas = increase_vol**3 * 1000 | units.MSun # Mgas = Ngas | units.MSun volume = Mgas / gas_density # 4/3 * pi * r**3 radius = (volume / (pi * 4/3))**(1/3) radius = increase_vol * radius # 15 | units.parsec gasconverter = nbody_system.nbody_to_si(Mgas, radius) # gasconverter = nbody_system.nbody_to_si(1 | units.pc, 1 | units.MSun) # gasconverter = nbody_system.nbody_to_si(1e10 | units.cm, 1e10 | units.g) # NOTE: make stars first - so that it remains the same random # initialisation even when we change the number of gas particles if len(sys.argv) > 1: gas = read_set_from_file(sys.argv[1], "amuse") stars = read_set_from_file(sys.argv[2], "amuse") stars.position = stars.position * 3 else: # stars = new_star_cluster( # stellar_mass=1000 | units.MSun, effective_radius=3 | units.parsec # ) # stars.velocity = stars.velocity * 2.0 from amuse.datamodel import Particles Nstars = 100 stars = Particles(Nstars) stars.position = [0, 0, 0] | units.pc stars.velocity = [0, 0, 0] | units.kms stars.mass = new_kroupa_mass_distribution(Nstars, mass_min=1 | units.MSun).reshape(Nstars) # 25 | units.MSun gas = molecular_cloud(targetN=Ngas, convert_nbody=gasconverter).result # gas.velocity = gas.velocity * 0.5 gas.u = temperature_to_u(100 | units.K) # gas.x = gas.x # gas.y = gas.y # gas.z = gas.z # gas.h_smooth = (gas.mass / gas_density / (4/3) / pi)**(1/3) # print(gas.h_smooth.mean()) # gas = read_set_from_file("gas_initial.hdf5", "amuse") # gas.density = gas_density # print(gas.h_smooth.mean()) # exit() u_now = gas.u #print(gasconverter.to_nbody(gas[0].u)) #print(constants.kB.value_in(units.erg * units.K**-1)) #print((constants.kB * 6.02215076e23).value_in(units.erg * units.K**-1)) #print(gasconverter.to_nbody(temperature_to_u(10 | units.K))) #tempiso = 2.d0/3.d0*ui/(Rg/gmwvar/uergg) # print(nbody_system.length**2 / nbody_system.time**2) # print(gasconverter.to_si(1 | nbody_system.length**2 / nbody_system.time**2).value_in(units.kms**2)) # print(gasconverter.to_nbody(temperature_to_u(Tmin))) # Rg = (constants.kB * 6.02214076e23).value_in(units.erg * units.K**-1) # gmwvar = 1.2727272727 # uergg = nbody_system.length**2 * nbody_system.time**-2 # uergg = 6.6720409999999996E-8 # print(Rg) # print( # 2.0/3.0*gasconverter.to_nbody(temperature_to_u(Tmin))/(Rg/gmwvar/uergg) # ) # #tempiso, ui, Rg, gmwvar, uergg, udist, utime 1.7552962911187030E-018 2.5778500859241771E-003 83140000.000000000 1.2727272727272725 6.6720409999999996E-008 1.0000000000000000 3871.4231866737564 # u = 3./2. * Tmin.value_in(units.K) * (Rg/gmwvar/uergg) # print(u) # print( # 2.0/3.0*u/(Rg/gmwvar/uergg) # ) # print(u, Rg, gmwvar, uergg) # print(temperature_to_u(10 | units.K).value_in(units.kms**2)) u = temperature_to_u(20 | units.K) #print(gasconverter.to_nbody(u)) #print(u_to_temperature(u).value_in(units.K)) # exit() # gas.u = u | units.kms**2 # exit() # print(gasconverter.to_nbody(gas.u.mean())) # print(gasconverter.to_si(gas.u.mean()).value_in(units.kms**2)) # exit() gas.du_dt = (u_now - u_now) / dt # zero, but in the correct units # stars = read_set_from_file("stars.amuse", "amuse") # write_set_to_file(stars, 'stars.amuse', 'amuse', append_to_file=False) # stars.velocity *= 3 # stars.vx += 0 | units.kms # stars.vy += 0 | units.kms M = stars.total_mass() + Mgas R = stars.position.lengths().mean() converter = nbody_system.nbody_to_si(M, R) # exit() # gas = new_plummer_gas_model(Ngas, gasconverter) # gas = molecular_cloud(targetN=Ngas, convert_nbody=gasconverter).result # gas.u = temperature_to_u(Tmin) # gas = read_set_from_file("gas.amuse", "amuse") # print(stars.mass == stars.mass.max()) print(len(stars.mass)) print(len(stars.mass == stars.mass.max())) print(stars[0]) print(stars[stars.mass == stars.mass.max()]) mms = stars[stars.mass == stars.mass.max()] print("Most massive star: %s" % mms.mass) print("Gas particle mass: %s" % gas[0].mass) evo = SeBa() # sph = Fi(converter, mode="openmp") phantomconverter = nbody_system.nbody_to_si( default_settings.gas_rscale, default_settings.gas_mscale, ) sph = Phantom(phantomconverter, redirection="none") sph.parameters.ieos = 2 sph.parameters.icooling = 1 sph.parameters.alpha = 0.1 sph.parameters.gamma = 5/3 sph.parameters.rho_crit = 1e17 | units.amu * units.cm**-3 sph.parameters.h_soft_sinkgas = 0.1 | units.parsec sph.parameters.h_soft_sinksink = 0.1 | units.parsec sph.parameters.h_acc = 0.1 | units.parsec # print(sph.parameters) stars_in_evo = evo.particles.add_particles(stars) channel_stars_evo_from_code = stars_in_evo.new_channel_to( stars, attributes=[ "age", "radius", "mass", "luminosity", "temperature", "stellar_type", ], ) channel_stars_evo_from_code.copy() # try: # sph.parameters.timestep = dt # except: # print("SPH code doesn't support setting the timestep") sph.parameters.stopping_condition_maximum_density = \ 5e-16 | units.g * units.cm**-3 # sph.parameters.beta = 1. # sph.parameters.C_cour = sph.parameters.C_cour / 4 # sph.parameters.C_force = sph.parameters.C_force / 4 print(sph.parameters) stars_in_sph = stars.copy() # sph.sink_particles.add_particles(stars) # stars_in_sph = sph.sink_particles.add_particles(stars) channel_stars_grav_to_code = stars.new_channel_to( # sph.sink_particles, # sph.dm_particles, stars_in_sph, attributes=["mass"] ) channel_stars_grav_from_code = stars_in_sph.new_channel_to( stars, attributes=["x", "y", "z", "vx", "vy", "vz"], ) # We don't want to accrete gas onto the stars/sinks stars_in_sph.radius = 0 | units.RSun # stars_in_sph = sph.dm_particles.add_particles(stars) # try: # sph.parameters.isothermal_flag = True # sph.parameters.integrate_entropy_flag = False # sph.parameters.gamma = 1 # except: # print("SPH code doesn't support setting isothermal flag") gas_in_code = sph.gas_particles.add_particles(gas) # print(gasconverter.to_nbody(gas_in_code[0].u).value_in(nbody_system.specific_energy)) # ui = temperature_to_u(10 | units.K) # Rg = constants.kB * 6.02214179e+23 # gmwvar = (1.4/1.1) | units.g # uergg = 1.# | nbody_system.specific_energy # print("gmwvar = %s"%gasconverter.to_si(gmwvar)) # print("Rg = %s"% gasconverter.to_si(Rg)) # print("ui = %s"% gasconverter.to_si(ui)) # #print("uergg = %s"% gasconverter.to_nbody(uergg)) # print("uergg = %s" % gasconverter.to_si(1 | nbody_system.specific_energy).in_(units.cm**2 * units.s**-2)) # print("****** %s" % ((2.0/3.0)*ui/(Rg/gmwvar/uergg)) + "*****") # print(gasconverter.to_nbody(Rg)) # print((ui).in_(units.cm**2*units.s**-2)) # #exit() # sph.evolve_model(1 | units.day) # write_set_to_file(sph.gas_particles, "gas_initial.hdf5", "amuse") # exit() channel_gas_to_code = gas.new_channel_to( gas_in_code, attributes=[ "x", "y", "z", "vx", "vy", "vz", "u", ] ) # mass is never updated, and if sph is in isothermal mode u is not reliable channel_gas_from_code = gas_in_code.new_channel_to( gas, attributes=[ "x", "y", "z", "vx", "vy", "vz", "density", "pressure", "rho", "u", "h_smooth", ], ) channel_gas_from_code.copy() # Initialise values for density etc sph_particle_mass = gas[0].mass # 0.1 | units.MSun r_max = 0.1 | units.parsec wind = stellar_wind.new_stellar_wind( sph_particle_mass, mode="heating", r_max=r_max, derive_from_evolution=True, tag_gas_source=True, # target_gas=gas, # timestep=dt, ) stars_in_wind = wind.particles.add_particles(stars) channel_stars_wind_to_code = stars.new_channel_to( stars_in_wind, attributes=[ "x", "y", "z", "vx", "vy", "vz", "age", "radius", "mass", "luminosity", "temperature", "stellar_type", ], ) channel_stars_wind_to_code.copy() # reference_mu = 2.2 | units.amu gasvolume = (4./3.) * numpy.pi * ( gas.position - gas.center_of_mass() ).lengths().mean()**3 rho0 = gas.total_mass() / gasvolume print(rho0.value_in(units.g * units.cm**-3)) # exit() # cooling_flag = "thermal_model" # cooling = Cooling( cooling = SimplifiedThermalModelEvolver( # gas_in_code, gas, Tmin=Tmin, # T0=30 | units.K, # n0=rho0/reference_mu ) cooling.model_time = sph.model_time # cooling_to_code = cooling.particles.new_channel_to(gas start_mass = ( stars.mass.sum() + (gas.mass.sum() if not gas.is_empty() else 0 | units.MSun) ) step = 0 plotnr = 0 com = stars_in_sph.center_of_mass() plot_hydro_and_stars( time, sph, stars=stars, sinks=None, L=20, # N=100, filename="phantom-coolthermalwindtestplot-%04i.png" % step, title="time = %06.2f %s" % (time.value_in(units.Myr), units.Myr), gasproperties=["density", "temperature"], # colorbar=True, starscale=1, offset_x=com[0].value_in(units.parsec), offset_y=com[1].value_in(units.parsec), thickness=5 | units.parsec, ) dt = dt_base sph.parameters.time_step = dt delta_t = phantomconverter.to_si(2**(-16) | nbody_system.time) print("delta_t: %s" % delta_t.in_(units.day)) # small_step = True small_step = False plot_every = 100 subplot_factor = 10 subplot_enabled = False subplot = 0 while time < time_end: time += dt print("Gas mean u: %s" % (gas.u.mean().in_(units.erg/units.MSun))) print("Evolving to t=%s (%s)" % (time, gasconverter.to_nbody(time))) step += 1 evo.evolve_model(evo_headstart+time) print(evo.particles.stellar_type.max()) channel_stars_evo_from_code.copy() channel_stars_grav_to_code.copy() if COOLING: channel_gas_from_code.copy() cooling.evolve_for(dt/2) channel_gas_to_code.copy() print( "min/max temp in gas: %s %s" % ( u_to_temperature(gas_in_code.u.min()).in_(units.K), u_to_temperature(gas_in_code.u.max()).in_(units.K), ) ) if small_step: # Take small steps until a full timestep is done. # Each substep is 2* as long as the last until dt is reached print("Doing small steps") # print(u_to_temperature(sph.gas_particles[0].u)) # print(sph.gas_particles[0].u) old_dt = dt_base substeps = 2**8 dt = old_dt / substeps dt_done = 0 * old_dt sph.parameters.time_step = dt print("adjusted dt to %s, base dt is %s" % ( dt.in_(units.Myr), dt_base.in_(units.Myr), ) ) sph.evolve_model(sph.model_time + dt) dt_done += dt while dt_done < old_dt: sph.parameters.time_step = dt print("adjusted dt to %s, base dt is %s" % ( dt.in_(units.Myr), dt_base.in_(units.Myr), ) ) sph.evolve_model(sph.model_time + dt) dt_done += dt dt = min(2*dt, old_dt-dt_done) dt = max(dt, old_dt/substeps) dt = dt_base sph.parameters.time_step = dt print( "adjusted dt to %s" % sph.parameters.time_step.in_(units.Myr) ) small_step = False print("Finished small steps") # print(u_to_temperature(sph.gas_particles[0].u)) # print(sph.gas_particles[0].u) # exit() else: sph.evolve_model(time) channel_gas_from_code.copy() channel_stars_grav_from_code.copy() u_previous = u_now u_now = gas.u gas.du_dt = (u_now - u_previous) / dt channel_stars_wind_to_code.copy() wind.evolve_model(time) # channel_stars_wind_from_code.copy() if COOLING: channel_gas_from_code.copy() cooling.evolve_for(dt/2) channel_gas_to_code.copy() if wind.has_new_wind_particles(): subplot_enabled = True wind_p = wind.create_wind_particles() # nearest = gas.find_closest_particle_to(wind_p.x, wind_p.y, wind_p.z) # wind_p.h_smooth = nearest.h_smooth wind_p.h_smooth = 100 | units.au print("u: %s / T: %s" % (wind_p.u.mean(), u_to_temperature(wind_p.u.mean()))) # max_e = (1e44 | units.erg) / wind_p[0].mass # max_e = 10 * gas.u.mean() # max_e = (1.e48 | units.erg) / wind_p[0].mass # wind_p[wind_p.u > max_e].u = max_e # wind_p[wind_p.u > max_e].h_smooth = 0.1 | units.parsec # print(wind_p.position) print( "time: %s, wind energy: %s" % (time, (wind_p.u * wind_p.mass).sum()) ) print( "wind temperature: %s" % (u_to_temperature(wind_p.u)) ) print( "gas particles: %i (total mass %s)" % (len(wind_p), wind_p.total_mass()) ) # for windje in wind_p: # # print(windje) # source = stars[stars.key == windje.source][0] # windje.position += source.position # windje.velocity += source.velocity # # print(source) # # print(windje) # # exit() gas.add_particles(wind_p) gas_in_code.add_particles(wind_p) # for wp in wind_p: # print(wp) print("Wind particles added") if True: # wind_p.u.max() > gas_in_code.u.max(): print("Setting dt to very short") small_step = True # dt = 0.1 | units.yr h_min = gas.h_smooth.min() # delta_t = determine_short_timestep(sph, wind_p, h_min=h_min) # print("delta_t is set to %s" % delta_t.in_(units.yr)) # else: # small_step = True print( "time: %s sph: %s dM: %s" % ( time.in_(units.Myr), sph.model_time.in_(units.Myr), ( stars.total_mass() + ( gas.total_mass() if not gas.is_empty() else (0 | units.MSun) ) - start_mass ) ) ) # com = sph.sink_particles.center_of_mass() # com = sph.dm_particles.center_of_mass() com = stars.center_of_mass() print("STEP: %i step%%plot_every: %i" % (step, step % plot_every)) if step % plot_every == 0: plotnr = plotnr + 1 plot_hydro_and_stars( time, sph, # stars=sph.sink_particles, # stars=sph.dm_particles, stars=stars, sinks=None, L=20, # N=100, # image_size_scale=10, filename="phantom-coolthermalwindtestplot-%04i.png" % plotnr, # int(step/plot_every), title="time = %06.2f %s" % (time.value_in(units.Myr), units.Myr), gasproperties=["density", "temperature"], # colorbar=True, starscale=1, offset_x=com[0].value_in(units.parsec), offset_y=com[1].value_in(units.parsec), thickness=5 | units.parsec, ) # write_set_to_file(gas, "gas.amuse", "amuse", append_to_file=False) # write_set_to_file(stars, "stars.amuse", "amuse", append_to_file=False) elif ( subplot_enabled and ((step % (plot_every/subplot_factor)) == 0) ): plotnr = plotnr + 1 subplot += 1 plot_hydro_and_stars( time, sph, # stars=sph.sink_particles, # stars=sph.dm_particles, stars=stars, sinks=None, L=20, # N=100, # image_size_scale=10, filename="phantom-coolthermalwindtestplot-%04i.png" % plotnr, # int(step/plot_every), title="time = %06.2f %s" % (time.value_in(units.Myr), units.Myr), gasproperties=["density", "temperature"], # colorbar=True, starscale=1, offset_x=com[0].value_in(units.parsec), offset_y=com[1].value_in(units.parsec), thickness=5 | units.parsec, ) if subplot % subplot_factor == 0: subplot_enabled = False print( "Average temperature of gas: %s" % ( u_to_temperature(gas.u).mean().in_(units.K) ) ) return
class SimpleXSplitSet(SimpleX): def __init__(self, **options): SimpleX.__init__(self, **options) self.gas_particles = Particles() self.src_particles = Particles() def commit_particles(self): sites = self.gas_particles.copy() sites.flux = 0. | units.s**-1 for p in self.src_particles: nearest = sites.find_closest_particle_to(p.x, p.y, p.z) nearest.flux += p.luminosity self.particles.add_particles(sites) self.simplex_to_gas_channel = self.particles.new_channel_to( self.gas_particles) self.overridden().commit_particles() if hasattr(sites, "du_dt"): attributes = ["du_dt"] channel = sites.new_channel_to(self.particles) channel.copy_attributes(attributes) del sites def recommit_particles(self): sites = self.gas_particles.copy() sites.flux = 0. | units.s**-1 for p in self.src_particles: nearest = sites.find_closest_particle_to(p.x, p.y, p.z) nearest.flux += p.luminosity # sites.synchronize_to(self.particles) add_set = sites.difference(self.particles) remove_set = self.particles.difference(sites) if len(remove_set) > 0: self.particles.remove_particles(remove_set) if len(add_set) > 0: self.particles.add_particles(add_set) self.overridden().recommit_particles() channel = sites.new_channel_to(self.particles) attributes = ["x", "y", "z", "rho", "xion", "u", "flux"] if hasattr(sites, "metallicity"): attributes.append("metallicity") if hasattr(sites, "du_dt"): attributes.append("du_dt") channel.copy_attributes(attributes) del sites self.overridden().recommit_particles() def evolve_model(self, tend): self.overridden().evolve_model(tend) self.simplex_to_gas_channel.copy_attributes( ["xion", "u", "metallicity"]) def define_state(self, handler): CommonCode.define_state(self, handler) handler.add_transition('INITIALIZED', 'EDIT', 'commit_parameters') handler.add_transition('RUN', 'CHANGE_PARAMETERS_RUN', 'before_set_parameter', False) handler.add_transition('EDIT', 'CHANGE_PARAMETERS_EDIT', 'before_set_parameter', False) handler.add_transition('UPDATE', 'CHANGE_PARAMETERS_UPDATE', 'before_set_parameter', False) handler.add_transition('CHANGE_PARAMETERS_RUN', 'RUN', 'recommit_parameters') handler.add_transition('CHANGE_PARAMETERS_EDIT', 'EDIT', 'recommit_parameters') handler.add_transition('CHANGE_PARAMETERS_UPDATE', 'UPDATE', 'recommit_parameters') handler.add_method('CHANGE_PARAMETERS_RUN', 'before_set_parameter') handler.add_method('CHANGE_PARAMETERS_EDIT', 'before_set_parameter') handler.add_method('CHANGE_PARAMETERS_UPDATE', 'before_set_parameter') handler.add_method('CHANGE_PARAMETERS_RUN', 'before_get_parameter') handler.add_method('CHANGE_PARAMETERS_EDIT', 'before_get_parameter') handler.add_method('CHANGE_PARAMETERS_UPDATE', 'before_get_parameter') handler.add_method('RUN', 'before_get_parameter') handler.add_method('EDIT', 'before_get_parameter') handler.add_method('UPDATE', 'before_get_parameter') handler.add_method('EVOLVED', 'before_get_parameter') handler.add_method('RUNCOMMIT', 'before_get_parameter') handler.add_method('EDIT', 'new_particle') handler.add_method('EDIT', 'delete_particle') handler.add_transition('EDIT', 'RUNCOMMIT', 'commit_particles') handler.add_transition('RUN', 'UPDATE', 'new_particle', False) handler.add_transition('RUN', 'UPDATE', 'delete_particle', False) handler.add_transition('UPDATE', 'RUN', 'recommit_particles') handler.add_transition('RUN', 'RUNCOMMIT', 'recommit_particles') handler.add_transition('RUNCOMMIT', 'RUN', 'auto_go_to_run') handler.add_transition('RUNCOMMIT', 'EVOLVED', 'evolve_model', False) # handler.add_method('EVOLVED', 'evolve_model') handler.define_state('RUNCOMMIT') handler.add_transition('EVOLVED', 'RUN', 'synchronize_model') handler.add_method('RUN', 'synchronize_model') handler.add_method('RUN', 'get_state') handler.add_method('RUN', 'get_density') handler.add_method('RUN', 'get_position') handler.add_method('RUN', 'get_flux') handler.add_method('RUN', 'get_ionisation') handler.add_method('RUN', 'get_internal_energy') handler.add_method('RUN', 'set_dinternal_energy_dt') handler.add_method('RUN', 'get_dinternal_energy_dt') handler.add_method('UPDATE', 'set_dinternal_energy_dt') handler.add_method('UPDATE', 'get_dinternal_energy_dt') handler.add_method('INITIALIZED', 'set_hilbert_order') handler.add_method('INITIALIZED', 'set_box_size') handler.add_method('INITIALIZED', 'set_timestep') handler.add_method('INITIALIZED', 'set_source_Teff') handler.add_method('INITIALIZED', 'set_number_frequency_bins') handler.add_method('INITIALIZED', 'set_thermal_evolution') handler.add_method('INITIALIZED', 'set_blackbody_spectrum') handler.add_method('INITIALIZED', 'set_metal_cooling') handler.add_method('INITIALIZED', 'set_recombination_radiation') handler.add_method('INITIALIZED', 'set_collisional_ionization')
def make_model(self): call=[self._exec]+self.arguments() print(call) mameclot=Popen(call, stdout=PIPE,stderr=PIPE,executable=os.path.join(self._bin_path,self._exec)) (out,err)=mameclot.communicate() print(err) outsplit=out.decode().strip().split("\n") errsplit=err.decode().strip().split("\n") if self.mass_ratio==0: nline=errsplit[6].split() mline=errsplit[7].split() rline=errsplit[8].split() N1=int(nline[2]) N2=0 mscale=(float(mline[4])/float(mline[2])) | units.MSun rscale=(float(rline[4])/float(mline[2])) | units.parsec else: nline=errsplit[8].split() n2line=errsplit[22].split() mline=errsplit[9].split() rline=errsplit[10].split() N1=int(nline[2]) N2=int(n2line[2]) mscale=(float(mline[4])/float(mline[2])) | units.MSun rscale=(float(rline[4])/float(mline[2])) | units.parsec print(N1,N2) N=len( outsplit) parts=Particles(N) masses=numpy.zeros((N,)) energy=numpy.zeros((N,)) position=numpy.zeros((N,3)) velocity=numpy.zeros((N,3)) for i,line in enumerate(outsplit): l=line.split() masses[i]=float(l[0]) position[i,0:3]=[float(l[1]),float(l[2]),float(l[3])] velocity[i,0:3]=[float(l[4]),float(l[5]),float(l[6])] energy[i]=float(l[7]) parts.mass=masses | nbody_system.mass parts.position=position | nbody_system.length parts.velocity=velocity | nbody_system.speed parts.specific_energy=energy| nbody_system.specific_energy parts.move_to_center() if self.convert_to_physical: print("mass scale:", mscale) print("length scale:", rscale) convert_nbody=nbody_system.nbody_to_si(mscale,rscale) parts = ParticlesWithUnitsConverted(parts, convert_nbody.as_converter_from_si_to_generic()) parts = parts.copy() self._all=parts self._cluster1=parts[:N1] self._cluster2=parts[N1:]
def make_model(self): call = [self._exec] + self.arguments() print call mameclot = Popen(call, stdout=PIPE, stderr=PIPE, executable=os.path.join(self._bin_path, self._exec)) (out, err) = mameclot.communicate() print err outsplit = out.strip().split("\n") errsplit = err.strip().split("\n") if self.mass_ratio == 0: nline = errsplit[6].split() mline = errsplit[7].split() rline = errsplit[8].split() N1 = int(nline[2]) N2 = 0 mscale = (float(mline[4]) / float(mline[2])) | units.MSun rscale = (float(rline[4]) / float(mline[2])) | units.parsec else: nline = errsplit[8].split() n2line = errsplit[22].split() mline = errsplit[9].split() rline = errsplit[10].split() N1 = int(nline[2]) N2 = int(n2line[2]) mscale = (float(mline[4]) / float(mline[2])) | units.MSun rscale = (float(rline[4]) / float(mline[2])) | units.parsec print N1, N2 N = len(outsplit) parts = Particles(N) masses = numpy.zeros((N,)) energy = numpy.zeros((N,)) position = numpy.zeros((N, 3)) velocity = numpy.zeros((N, 3)) for i, line in enumerate(outsplit): l = line.split() masses[i] = float(l[0]) position[i, 0:3] = [float(l[1]), float(l[2]), float(l[3])] velocity[i, 0:3] = [float(l[4]), float(l[5]), float(l[6])] energy[i] = float(l[7]) parts.mass = masses | nbody_system.mass parts.position = position | nbody_system.length parts.velocity = velocity | nbody_system.speed parts.specific_energy = energy | nbody_system.specific_energy parts.move_to_center() if self.convert_to_physical: print "mass scale:", mscale print "length scale:", rscale convert_nbody = nbody_system.nbody_to_si(mscale, rscale) parts = ParticlesWithUnitsConverted(parts, convert_nbody.as_converter_from_si_to_generic()) parts = parts.copy() self._all = parts self._cluster1 = parts[:N1] self._cluster2 = parts[N1:]
def form_stars_from_group_older_version( group_index, sink_particles, newly_removed_gas, lower_mass_limit=settings.stars_lower_mass_limit, upper_mass_limit=settings.stars_upper_mass_limit, local_sound_speed=0.2 | units.kms, minimum_sink_mass=0.01 | units.MSun, logger=None, randomseed=None, shrink_sinks=True, **keyword_arguments): """ Form stars from specific group of sinks. NOTE: This is the older version where removed gas is considered as star-forming region. This is now being updated to the above latest version. """ logger = logger or logging.getLogger(__name__) logger.info("Using form_stars_from_group on group %i", group_index) if randomseed is not None: logger.info("Setting random seed to %i", randomseed) numpy.random.seed(randomseed) # Sanity check: each sink particle must be in a group. ungrouped_sinks = sink_particles.select_array(lambda x: x <= 0, ['in_group']) if not ungrouped_sinks.is_empty(): logger.info( "WARNING: There exist ungrouped sinks. Something is wrong!") return None # Consider only group with input group index from here onwards. group = sink_particles[sink_particles.in_group == group_index] # Sanity check: group must have at least a sink if group.is_empty(): logger.info( "WARNING: There is no sink in the group: Something is wrong!") return None number_of_sinks = len(group) logger.info("%i sinks found in group #%i: %s", number_of_sinks, group_index, group.key) group_mass = group.total_mass() logger.info("Group mass: %s", group_mass.in_(units.MSun)) next_mass = generate_next_mass( initial_mass_function=initial_mass_function, lower_mass_limit=lower_mass_limit, upper_mass_limit=upper_mass_limit, )[0][0] try: # Within a group, group_next_primary_mass values are either # a mass, or 0 MSun. If all values are 0 MSun, this is a # new group. Else, only interested on the non-zero value. The # non-zero values are the same. logger.info('SANITY CHECK: group_next_primary_mass %s', group.group_next_primary_mass) if group.group_next_primary_mass.max() == 0 | units.MSun: logger.info('Initiate group #%i for star formation', group_index) group.group_next_primary_mass = next_mass else: next_mass = group.group_next_primary_mass.max() # This happens for the first ever assignment of this attribute except AttributeError: logger.info( 'AttributeError exception: Initiate group #%i for star formation', group_index) group.group_next_primary_mass = next_mass logger.info("Next mass is %s", next_mass) if group_mass < next_mass: logger.info("Group #%i is not massive enough for the next star", group_index) return None # Form stars from the leftover group sink mass mass_left = group_mass - next_mass masses = new_masses( stellar_mass=mass_left, lower_mass_limit=lower_mass_limit, upper_mass_limit=upper_mass_limit, initial_mass_function=settings.stars_initial_mass_function, ) number_of_stars = len(masses) logger.info("%i stars created in group #%i with %i sinks", number_of_stars, group_index, number_of_sinks) new_stars = Particles(number_of_stars) new_stars.age = 0 | units.Myr new_stars[0].mass = next_mass new_stars[1:].mass = masses[:-1] group.group_next_primary_mass = masses[-1] new_stars = new_stars.sorted_by_attribute("mass").reversed() logger.info("Group's next primary mass is %s", group.group_next_primary_mass[0]) # Create placeholders for attributes of new_stars new_stars.position = [0, 0, 0] | units.pc new_stars.velocity = [0, 0, 0] | units.kms new_stars.origin_cloud = group[0].key new_stars.star_forming_radius = 0 | units.pc new_stars.star_forming_u = local_sound_speed**2 # Find the newly removed gas in the group removed_gas = Particles() if not newly_removed_gas.is_empty(): for s in group: removed_gas_by_this_sink = ( newly_removed_gas[newly_removed_gas.accreted_by_sink == s.key]) removed_gas.add_particles(removed_gas_by_this_sink) logger.info("%i removed gas found in this group", len(removed_gas)) # Star forming regions that contain the removed gas and the group # of sinks if not removed_gas.is_empty(): removed_gas.radius = removed_gas.h_smooth star_forming_regions = group.copy() star_forming_regions.density = ( star_forming_regions.initial_density / 1000 ) # /1000 to reduce likelihood of forming stars in sinks star_forming_regions.accreted_by_sink = star_forming_regions.key try: star_forming_regions.u = star_forming_regions.u except AttributeError: star_forming_regions.u = local_sound_speed**2 star_forming_regions.add_particles(removed_gas.copy()) star_forming_regions.sorted_by_attribute("density").reversed() # Generate a probability list of star forming region indices the # stars should associate to probabilities = (star_forming_regions.density / star_forming_regions.density.sum()) probabilities /= probabilities.sum() # Ensure sum is exactly 1 logger.info("Max & min probabilities: %s, %s", probabilities.max(), probabilities.min()) logger.info("%i star forming regions", len(star_forming_regions)) def delta_positions_and_velocities(new_stars, star_forming_regions, probabilities): """ Assign positions and velocities of stars in the star forming regions according to the probability distribution """ number_of_stars = len(new_stars) # Create an index list of removed gas from probability list sample = numpy.random.choice(len(star_forming_regions), number_of_stars, p=probabilities) # Assign the stars to the removed gas according to the sample star_forming_regions_sampled = star_forming_regions[sample] new_stars.position = star_forming_regions_sampled.position new_stars.velocity = star_forming_regions_sampled.velocity new_stars.origin_cloud = star_forming_regions_sampled.accreted_by_sink new_stars.star_forming_radius = star_forming_regions_sampled.radius try: new_stars.star_forming_u = star_forming_regions_sampled.u except AttributeError: new_stars.star_forming_u = local_sound_speed**2 # Random position of stars within the sink radius they assigned to rho = (numpy.random.random(number_of_stars) * new_stars.star_forming_radius) theta = (numpy.random.random(number_of_stars) * (2 * numpy.pi | units.rad)) phi = (numpy.random.random(number_of_stars) * numpy.pi | units.rad) x = (rho * sin(phi) * cos(theta)).value_in(units.pc) y = (rho * sin(phi) * sin(theta)).value_in(units.pc) z = (rho * cos(phi)).value_in(units.pc) X = list(zip(*[x, y, z])) | units.pc # Random velocity, sample magnitude from gaussian with local sound # speed like Wall et al (2019) # temperature = 10 | units.K # or (gamma * local_pressure / density).sqrt() velocity_magnitude = numpy.random.normal( # loc=0.0, # <- since we already added the velocity of the sink scale=new_stars.star_forming_u.sqrt().value_in(units.kms), size=number_of_stars, ) | units.kms velocity_theta = (numpy.random.random(number_of_stars) * (2 * numpy.pi | units.rad)) velocity_phi = (numpy.random.random(number_of_stars) * (numpy.pi | units.rad)) vx = (velocity_magnitude * sin(velocity_phi) * cos(velocity_theta)).value_in(units.kms) vy = (velocity_magnitude * sin(velocity_phi) * sin(velocity_theta)).value_in(units.kms) vz = (velocity_magnitude * cos(velocity_phi)).value_in(units.kms) V = list(zip(*[vx, vy, vz])) | units.kms return X, V dX, dV = delta_positions_and_velocities(new_stars, star_forming_regions, probabilities) logger.info("Updating new stars...") new_stars.position += dX new_stars.velocity += dV # For Pentacle, this is the PP radius new_stars.radius = 0.05 | units.parsec # mass_ratio = 1 - new_stars.total_mass()/group.total_mass() # group.mass *= mass_ratio excess_star_mass = 0 | units.MSun for s in group: logger.info('Sink mass before reduction: %s', s.mass.in_(units.MSun)) total_star_mass_nearby = ( new_stars[new_stars.origin_cloud == s.key]).total_mass() # To prevent sink mass becomes negative if s.mass > minimum_sink_mass: if (s.mass - total_star_mass_nearby) <= minimum_sink_mass: excess_star_mass += (total_star_mass_nearby - s.mass + minimum_sink_mass) logger.info('Sink mass goes below %s; excess mass is now %s', minimum_sink_mass.in_(units.MSun), excess_star_mass.in_(units.MSun)) s.mass = minimum_sink_mass else: s.mass -= total_star_mass_nearby else: excess_star_mass += total_star_mass_nearby logger.info( 'Sink mass is already <= minimum mass allowed; ' 'excess mass is now %s', excess_star_mass.in_(units.MSun)) logger.info('Sink mass after reduction: %s', s.mass.in_(units.MSun)) # Reduce all sinks in group equally with the excess star mass logger.info('Reducing all sink mass equally with excess star mass...') mass_ratio = 1 - excess_star_mass / group.total_mass() group.mass *= mass_ratio logger.info("Total sink mass in group: %s", group.total_mass().in_(units.MSun)) if shrink_sinks: group.radius = ((group.mass / group.initial_density) / (4 / 3 * numpy.pi))**(1 / 3) logger.info("New radii: %s", group.radius.in_(units.pc)) return new_stars