def form_stars(sink, initial_mass_function=settings.stars_initial_mass_function, lower_mass_limit=settings.stars_lower_mass_limit, upper_mass_limit=settings.stars_upper_mass_limit, local_sound_speed=0.2 | units.kms, logger=None, randomseed=None, **keyword_arguments): """ Let a sink form stars. """ logger = logger or logging.getLogger(__name__) if randomseed is not None: logger.info("setting random seed to %i", randomseed) numpy.random.seed(randomseed) # sink_initial_density = sink.mass / (4/3 * numpy.pi * sink.radius**3) initialised = sink.initialised or False if not initialised: logger.debug("Initialising sink %i for star formation", sink.key) next_mass = generate_next_mass( initial_mass_function=initial_mass_function, lower_mass_limit=lower_mass_limit, upper_mass_limit=upper_mass_limit, ) # sink.next_number_of_stars = len(next_mass) # sink.next_total_mass = next_mass.sum() sink.next_primary_mass = next_mass[0] # if sink.next_number_of_stars > 1: # sink.next_secondary_mass = next_mass[1] # if sink.next_number_of_stars > 2: # sink.next_tertiary_mass = next_mass[2] sink.initialised = True if sink.mass < sink.next_primary_mass: logger.debug("Sink %i is not massive enough for the next star", sink.key) return [sink, Particles()] # We now have the first star that will be formed. # Next, we generate a list of stellar masses, so that the last star in the # list is just one too many for the sink's mass. mass_left = sink.mass - sink.next_primary_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) new_stars = Particles(number_of_stars) new_stars.age = 0 | units.Myr new_stars[0].mass = sink.next_primary_mass new_stars[1:].mass = masses[:-1] sink.next_primary_mass = masses[-1] # if sink.next_number_of_stars > 1: # new_stars[1].mass = sink.next_secondary_mass # if sink.next_number_of_stars > 2: # new_stars[2].mass = sink.next_tertiary_mass new_stars.position = sink.position new_stars.velocity = sink.velocity # Random position within the sink radius radius = sink.radius rho = numpy.random.random(number_of_stars) * 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) y = rho * sin(phi) * sin(theta) z = rho * cos(phi) new_stars.x += x new_stars.y += y new_stars.z += z # Random velocity, sample magnitude from gaussian with local sound speed # like Wall et al (2019) # temperature = 10 | units.K try: local_sound_speed = sink.u.sqrt() except AttributeError: local_sound_speed = local_sound_speed # or (gamma * local_pressure / density).sqrt() velocity_magnitude = numpy.random.normal( # loc=0.0, # <- since we already added the velocity of the sink scale=local_sound_speed.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) vy = velocity_magnitude * sin(velocity_phi) * sin(velocity_theta) vz = velocity_magnitude * cos(velocity_phi) new_stars.vx += vx new_stars.vy += vy new_stars.vz += vz new_stars.origin_cloud = sink.key # For Pentacle, this is the PP radius new_stars.radius = 0.05 | units.parsec sink.mass -= new_stars.total_mass() # TODO: fix sink's momentum etc # EDIT: Do not shrink the sinks at this point, but rather when finished # forming stars. # # Shrink the sink's (accretion) radius to prevent it from accreting # # relatively far away gas and moving a lot # sink.radius = ( # (sink.mass / sink_initial_density) # / (4/3 * numpy.pi) # )**(1/3) # cleanup # sink.initialised = False new_stars.birth_mass = new_stars.mass return [sink, new_stars]