Exemplo n.º 1
0
def main():
    # Fixed settings
    stellar_evolution = True
    se_code = "SeBa"
    length_unit = units.parsec
    dpi = 600
    percentile = 0.9995  # for determining vmax

    # Parse arguments
    args = new_argument_parser()
    starsfilename = args.starsfilename
    gasfilename = args.gasfilename
    followfilename = args.followfilename
    imagefilename = args.imagefilename
    imagetype = args.imagetype
    vmax = args.vmax if args.vmax > 0 else None
    n_fieldstars = args.n_fieldstars
    filetype = args.filetype
    contours = args.contours
    np.random.seed(args.seed)
    plot_axes = args.plot_axes
    angle_x = args.angle_x | units.deg
    angle_y = args.angle_y | units.deg
    angle_z = args.angle_z | units.deg
    sourcebands = args.sourcebands
    psf_type = args.psf_type.lower()
    psf_sigma = args.psf_sigma
    age = args.age | units.Myr
    image_width = args.width | units.parsec
    pixels = args.pixels
    frames = args.frames
    if followfilename is not None:
        use_com = True
    else:
        use_com = args.use_com
    x_offset = args.x_offset | units.parsec
    y_offset = args.y_offset | units.parsec
    z_offset = args.z_offset | units.parsec
    extinction = args.calculate_extinction

    # Derived settings
    if psf_type not in ["hubble", "gaussian"]:
        print(("Invalid PSF type: %s" % psf_type))
        exit()
    image_size = [pixels, pixels]
    # If the nr of pixels is changed, zoom the PSF accordingly.
    zoom_factor = pixels / 2048.

    if starsfilename:
        stars = read_set_from_file(
            starsfilename,
            filetype,
            close_file=True,
        )
        if stellar_evolution and (age > 0 | units.Myr):
            print((
                "Calculating luminosity/temperature for %s old stars..."
                % (age)
            ))
            evolve_to_age(stars, age, stellar_evolution=se_code)
        if use_com:
            if followfilename is not None:
                followstars = read_set_from_file(
                    followfilename, filetype, close_file=True,
                )
                center_on_these_stars = followstars.get_intersecting_subset_in(
                    stars,
                )
            else:
                center_on_these_stars = stars
            com = center_on_these_stars.center_of_mass()
            x_offset, y_offset, z_offset = com
        stars.x -= x_offset
        stars.y -= y_offset
        stars.z -= z_offset
    else:
        stars = Particles()

    if n_fieldstars:
        minage = 400 | units.Myr
        maxage = 12 | units.Gyr
        fieldstars = new_field_stars(
            n_fieldstars,
            width=image_width,
            height=image_width,
        )
        fieldstars.age = (
            minage
            + (
                np.random.sample(n_fieldstars)
                * (maxage - minage)
            )
        )
        evolve_to_age(fieldstars, 0 | units.yr, stellar_evolution=se_code)
        stars.add_particles(fieldstars)

    if gasfilename:
        gas = read_set_from_file(
            gasfilename,
            filetype,
            close_file=True,
        )
        if use_com:
            if stars.is_empty():
                com = gas.center_of_mass()
                x_offset, y_offset, z_offset = com
        gas.x -= x_offset
        gas.y -= y_offset
        gas.z -= z_offset
        # Gadget and Fi disagree on the definition of h_smooth.
        # For gadget, need to divide by 2 to get the Fi value (??)
        gas.h_smooth *= 0.5
        gas.radius = gas.h_smooth

        # Select only the relevant gas particles (plus a margin)
        minx = (1.1 * -image_width/2)
        maxx = (1.1 * image_width/2)
        miny = (1.1 * -image_width/2)
        maxy = (1.1 * image_width/2)
        gas_ = gas.select(
            lambda x, y:
            x > minx
            and x < maxx
            and y > miny
            and y < maxy,
            ["x", "y"]
        )
        gas = gas_
    else:
        gas = Particles()
    # gas.h_smooth = 0.05 | units.parsec

    converter = nbody_system.nbody_to_si(
        stars.total_mass() if not stars.is_empty() else gas.total_mass(),
        image_width,
    )

    # Initialise figure and axes
    fig = initialise_image(
        dpi=dpi,
        image_size=image_size,
        length_unit=length_unit,
        image_width=image_width,
        plot_axes=plot_axes,
        x_offset=x_offset,
        y_offset=y_offset,
        z_offset=z_offset,
    )
    ax = fig.get_axes()[0]
    xmin, xmax = ax.get_xlim()
    ymin, ymax = ax.get_ylim()

    for frame in range(frames):
        fig = initialise_image(fig)

        if (frame != 0) or (frames == 1):
            if not stars.is_empty():
                rotate(stars, angle_x, angle_y, angle_z)
            if not gas.is_empty():
                rotate(gas, angle_x, angle_y, angle_z)

        image, vmax = make_image(
            stars=stars if not stars.is_empty() else None,
            gas=gas if not gas.is_empty() else None,
            converter=converter,
            image_width=image_width,
            image_size=image_size,
            percentile=percentile,
            calc_temperature=True,
            age=age,
            vmax=vmax,
            sourcebands=sourcebands,
            zoom_factor=zoom_factor,
            psf_type=psf_type,
            psf_sigma=psf_sigma,
            return_vmax=True,
            extinction=extinction,
        )

        if not stars.is_empty():
            ax.imshow(
                image,
                origin='lower',
                extent=[
                    xmin,
                    xmax,
                    ymin,
                    ymax,
                ],
            )
            if contours and not gas.is_empty():
                gascontours = column_density_map(
                    gas,
                    zoom_factor=zoom_factor,
                    image_width=image_width,
                    image_size=image_size,
                )
                gascontours[np.isnan(gascontours)] = 0.0
                vmax = np.max(gascontours) / 2
                # vmin = np.min(image[np.where(image > 0.0)])
                vmin = vmax / 100
                levels = 10**(
                    np.linspace(
                        np.log10(vmin),
                        np.log10(vmax),
                        num=5,
                    )
                )[1:]
                # print(vmin, vmax)
                # print(levels)
                ax.contour(
                    origin='lower',
                    levels=levels,
                    colors="white",
                    linewidths=0.1,
                    extent=[
                        xmin,
                        xmax,
                        ymin,
                        ymax,
                    ],
                )
        else:
            image = column_density_map(
                gas,
                image_width=image_width,
                image_size=image_size,
            )

            ax.imshow(
                image,
                origin='lower',
                extent=[
                    xmin,
                    xmax,
                    ymin,
                    ymax,
                ],
                cmap="gray",
            )

        if frames > 1:
            savefilename = "%s-%06i.%s" % (
                imagefilename if imagefilename is not None else "test",
                frame,
                imagetype,
            )
        else:
            savefilename = "%s.%s" % (
                imagefilename if imagefilename is not None else "test",
                imagetype,
            )
        plt.savefig(
            savefilename,
            dpi=dpi,
        )
Exemplo n.º 2
0
def main():
    mode = []

    # Fixed settings
    stellar_evolution = True
    se_code = "SeBa"
    length_unit = units.parsec
    dpi = 600
    percentile = 0.9995  # for determining vmax

    # Parse arguments
    args = new_argument_parser()
    starsfilename = args.starsfilename
    gasfilename = args.gasfilename
    imagefilename = args.imagefilename
    imagetype = args.imagetype
    vmax = args.vmax if args.vmax > 0 else None
    n_fieldstars = args.n_fieldstars
    filetype = args.filetype
    contours = args.contours
    np.random.seed(args.seed)
    plot_axes = args.plot_axes
    angle_x = args.angle_x | units.deg
    angle_y = args.angle_y | units.deg
    angle_z = args.angle_z | units.deg
    sourcebands = args.sourcebands
    psf_type = args.psf_type.lower()
    psf_sigma = args.psf_sigma
    age = args.age | units.Myr
    image_width = args.width | units.parsec
    pixels = args.pixels
    frames = args.frames

    # Derived settings
    if args.calculate_extinction:
        mode.append("extinction")
    if psf_type not in ["hubble", "gaussian"]:
        print(("Invalid PSF type: %s" % psf_type))
        exit()
    image_size = [pixels, pixels]
    # If the nr of pixels is changed, zoom the PSF accordingly.
    zoom_factor = pixels / 2048.

    if starsfilename:
        stars = read_set_from_file(
            starsfilename,
            filetype,
            close_file=True,
        )
        if stellar_evolution and (age > 0 | units.Myr):
            print(("Calculating luminosity/temperature for %s old stars..." %
                   (age)))
            evolve_to_age(stars, age, stellar_evolution=se_code)
        com = stars.center_of_mass()
        stars.position -= com
    else:
        stars = Particles()

    if n_fieldstars:
        minage = 400 | units.Myr
        maxage = 12 | units.Gyr
        fieldstars = new_field_stars(
            n_fieldstars,
            width=image_width,
            height=image_width,
        )
        fieldstars.age = (minage + (np.random.sample(n_fieldstars) *
                                    (maxage - minage)))
        evolve_to_age(fieldstars, 0 | units.yr, stellar_evolution=se_code)
        stars.add_particles(fieldstars)
    if not stars.is_empty():
        mode.append("stars")

    if gasfilename:
        gas = read_set_from_file(
            gasfilename,
            filetype,
            close_file=True,
        )
        if "stars" not in mode:
            com = gas.center_of_mass()
        gas.position -= com
        # Gadget and Fi disagree on the definition of h_smooth.
        # For gadget, need to divide by 2 to get the Fi value (??)
        gas.h_smooth *= 0.5
        gas.radius = gas.h_smooth
    else:
        gas = Particles()
    if not gas.is_empty():
        mode.append("gas")
        if contours:
            mode.append("contours")
    # gas.h_smooth = 0.05 | units.parsec

    converter = nbody_system.nbody_to_si(
        stars.total_mass() if "stars" in mode else gas.total_mass(),
        image_width,
    )

    # Initialise figure and axes
    fig = initialise_image(
        dpi=dpi,
        image_size=image_size,
        length_unit=length_unit,
        image_width=image_width,
        plot_axes=plot_axes,
    )
    ax = fig.get_axes()[0]
    xmin, xmax = ax.get_xlim()
    ymin, ymax = ax.get_ylim()

    for frame in range(frames):
        fig = initialise_image(fig)

        if (frame != 0) or (frames == 1):
            if not stars.is_empty():
                rotate(stars, angle_x, angle_y, angle_z)
            if not gas.is_empty():
                rotate(gas, angle_x, angle_y, angle_z)

        image, vmax = make_image(
            stars,
            gas,
            mode=mode,
            converter=converter,
            image_width=image_width,
            image_size=image_size,
            percentile=percentile,
            calc_temperature=True,
            age=age,
            vmax=vmax,
            sourcebands=sourcebands,
            zoom_factor=zoom_factor,
            psf_type=psf_type,
            psf_sigma=psf_sigma,
            return_vmax=True,
        )

        if "stars" in mode:
            ax.imshow(
                image,
                origin='lower',
                extent=[
                    xmin,
                    xmax,
                    ymin,
                    ymax,
                ],
            )
            if ("contours" in mode) and ("gas" in mode):
                gascontours = column_density_map(
                    gas,
                    zoom_factor=zoom_factor,
                    image_width=image_width,
                    image_size=image_size,
                )
                gascontours[np.isnan(gascontours)] = 0.0
                vmax = np.max(gascontours) / 2
                # vmin = np.min(image[np.where(image > 0.0)])
                vmin = vmax / 100
                levels = 10**(np.linspace(
                    np.log10(vmin),
                    np.log10(vmax),
                    num=5,
                ))[1:]
                # print(vmin, vmax)
                # print(levels)
                ax.contour(
                    gascontours,
                    origin='lower',
                    levels=levels,
                    colors="white",
                    linewidths=0.1,
                    extent=[
                        xmin,
                        xmax,
                        ymin,
                        ymax,
                    ],
                )
        else:
            image = column_density_map(
                gas,
                image_width=image_width,
                image_size=image_size,
            )

            ax.imshow(
                image,
                origin='lower',
                extent=[
                    xmin,
                    xmax,
                    ymin,
                    ymax,
                ],
                cmap="gray",
            )

        plt.savefig(
            "%s-%06i.%s" % (
                imagefilename,
                frame,
                imagetype,
            ),
            dpi=dpi,
        )
Exemplo n.º 3
0
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