Esempio n. 1
0
    def setVegaFluxes(self, filters, vega_fname=None):
        """
        Set vega reference fluxes for conversions

        Parameters
        ----------
        filters : list
            list of filters using the internally normalized namings
        vega_fname : str, optional
            name of the file with the vega model spectrum
        """
        # for optimization purpose: pre-compute
        with Vega(source=vega_fname) as v:
            _, vega_flux, _ = v.getFlux(filters)
        self.vega_flux = vega_flux
Esempio n. 2
0
    def setFilters(self, filters):
        """ set the filters and update the vega reference for the conversions

        Parameters
        ----------
        filters: sequence
            list of filters using the internally normalized namings
        """
        self.filters = filters

        # ASTs inputs are in vega mag whereas models are in flux units
        #     for optimization purpose: pre-compute
        with Vega() as v:
            _, vega_flux, _ = v.getFlux(filters)

        self.vega_flux = vega_flux
Esempio n. 3
0
    def setFilters(self, filters, vega_fname=None):
        """
        Set the filters and update the vega reference for the conversions

        Parameters
        ----------
        filters : list
            filters using the internally normalized namings
        vega_fname : str, optional
            filename of the vega database
        """
        self.filters = filters

        # ASTs inputs are in vega mag whereas models are in flux units
        #     for optimization purpose: pre-compute
        with Vega(source=vega_fname) as v:
            _, vega_flux, _ = v.getFlux(filters)

        self.vega_flux = vega_flux
Esempio n. 4
0
    def setFilters(self, filters):
        """ set the filters and update the vega reference for the conversions

        Parameters
        ----------
        filters: sequence
            list of filters using the internally normalized namings
        """
        self.filters = filters

        #Data "rates" are normalized to Vega already, fits are not using vega

        # for optimization purpose: pre-compute
        #   getting vega mags, require to open and read the content of one file.
        #   since getObs, calls getFlux, for each star you need to do this
        #   expensive operation
        with Vega() as v:
            _, vega_flux, _ = v.getFlux(filters)

        self.vega_flux = vega_flux
Esempio n. 5
0
def gen_SimObs_from_sedgrid(
    sedgrid,
    sedgrid_noisemodel,
    nsim=100,
    compl_filter="F475W",
    complcut=None,
    magcut=None,
    ranseed=None,
    vega_fname=None,
    weight_to_use="weight",
    age_prior_model=None,
    mass_prior_model=None,
):
    """
    Generate simulated observations using the physics and observation grids.
    The priors are sampled as they give the ensemble model for the stellar
    and dust distributions (IMF, Av distribution etc.).
    The physics model gives the SEDs based on the priors.
    The observation model gives the noise, bias, and completeness all of
    which are used in simulating the observations.

    Currently written to only work for the toothpick noisemodel.

    Parameters
    ----------
    sedgrid: grid.SEDgrid instance
        model grid

    sedgrid_noisemodel: beast noisemodel instance
        noise model data

    nsim : int
        number of observations to simulate

    compl_filter : str
        Filter to use for completeness (required for toothpick model).
        Set to max to use the max value in all filters.

    complcut : float (defualt=None)
        completeness cut for only including model seds above the cut
        where the completeness cut ranges between 0 and 1.

    magcut : float (defualt=None)
        faint-end magnitude cut for only including model seds brighter
        than the given magnitude in compl_filter.

    ranseed : int
        used to set the seed to make the results reproducable,
        useful for testing

    vega_fname : string
        filename for the vega info, useful for testing

    weight_to_use : string (default='weight')
        Set to either 'weight' (prior+grid), 'prior_weight', 'grid_weight',
        or 'uniform' (this option is valid only when nsim is supplied) to
        choose the weighting for SED selection.

    age_prior_model : dict
        age prior model in the BEAST dictonary format

    mass_prior_model : dict
        mass prior model in the BEAST dictonary format

    Returns
    -------
    simtable : astropy Table
        table giving the simulated observed fluxes as well as the
        physics model parmaeters
    """
    n_models, n_filters = sedgrid.seds.shape
    flux = sedgrid.seds

    # get the vega fluxes for the filters
    _, vega_flux, _ = Vega(source=vega_fname).getFlux(sedgrid.filters)

    # cache the noisemodel values
    model_bias = sedgrid_noisemodel["bias"]
    model_unc = np.fabs(sedgrid_noisemodel["error"])
    model_compl = sedgrid_noisemodel["completeness"]

    # only use models that have non-zero completeness in all filters
    # zero completeness means the observation model is not defined for that filters/flux
    ast_defined = model_compl > 0.0
    sum_ast_defined = np.sum(ast_defined, axis=1)
    goodobsmod = sum_ast_defined >= n_filters

    # completeness from toothpick model so n band completeness values
    # require only 1 completeness value for each model
    # max picked to best "simulate" how the photometry detection is done
    if compl_filter.lower() == "max":
        model_compl = np.max(model_compl, axis=1)
    else:
        short_filters = [
            filter.split(sep="_")[-1].upper() for filter in sedgrid.filters
        ]
        if compl_filter.upper() not in short_filters:
            raise NotImplementedError(
                "Requested completeness filter not present:" +
                compl_filter.upper() + "\nPossible filters:" +
                "\n".join(short_filters))

        filter_k = short_filters.index(compl_filter.upper())
        print("Completeness from %s" % sedgrid.filters[filter_k])
        model_compl = model_compl[:, filter_k]

    # if complcut is provided, only use models above that completeness cut
    # in addition to the non-zero completeness criterion
    if complcut is not None:
        goodobsmod = (goodobsmod) & (model_compl >= complcut)

    # if magcut is provided, only use models brighter than the magnitude cut
    # in addition to the non-zero completeness criterion
    if magcut is not None:
        fluxcut_compl_filter = 10**(-0.4 * magcut) * vega_flux[filter_k]
        goodobsmod = (goodobsmod) & (flux[:, filter_k] >= fluxcut_compl_filter)

    # initialize the random number generator
    rangen = default_rng(ranseed)

    # if the age and mass prior models are given, use them to determine the
    # total number of stars to simulate
    model_indx = np.arange(n_models)
    if (age_prior_model is not None) and (mass_prior_model is not None):
        nsim = 0
        # logage_range = [min(sedgrid["logA"]), max(sedgrid["logA"])]
        mass_range = [min(sedgrid["M_ini"]), max(sedgrid["M_ini"])]

        # compute the total mass and average mass of a star given the mass_prior_model
        nmass = 100
        masspts = np.logspace(np.log10(mass_range[0]), np.log10(mass_range[1]),
                              nmass)
        massprior = compute_mass_prior_weights(masspts, mass_prior_model)
        totmass = np.trapz(massprior, masspts)
        avemass = np.trapz(masspts * massprior, masspts) / totmass

        # compute the mass of the remaining stars at each age and
        # simulate the stars assuming everything is complete
        gridweights = sedgrid[weight_to_use]
        gridweights = gridweights / np.sum(gridweights)

        grid_ages = np.unique(sedgrid["logA"])
        ageprior = compute_age_prior_weights(grid_ages, age_prior_model)
        bin_boundaries = compute_bin_boundaries(grid_ages)
        bin_widths = np.diff(10**(bin_boundaries))
        totsim_indx = np.array([], dtype=int)
        for cage, cwidth, cprior in zip(grid_ages, bin_widths, ageprior):
            gmods = sedgrid["logA"] == cage
            cur_mass_range = [
                min(sedgrid["M_ini"][gmods]),
                max(sedgrid["M_ini"][gmods]),
            ]
            gmass = (masspts >= cur_mass_range[0]) & (masspts <=
                                                      cur_mass_range[1])
            curmasspts = masspts[gmass]
            curmassprior = massprior[gmass]
            totcurmass = np.trapz(curmassprior, curmasspts)

            # compute the mass remaining at each age -> this is the mass to simulate
            simmass = cprior * cwidth * totcurmass / totmass
            nsim_curage = int(round(simmass / avemass))

            # simluate the stars at the current age
            curweights = gridweights[gmods]
            curweights /= np.sum(curweights)
            cursim_indx = rangen.choice(model_indx[gmods],
                                        size=nsim_curage,
                                        p=curweights)

            totsim_indx = np.concatenate((totsim_indx, cursim_indx))

            nsim += nsim_curage
            # totsimcurmass = np.sum(sedgrid["M_ini"][cursim_indx])
            # print(cage, totcurmass / totmass, simmass, totsimcurmass, nsim_curage)

        totsimmass = np.sum(sedgrid["M_ini"][totsim_indx])
        print(f"number total simulated stars = {nsim}; mass = {totsimmass}")
        compl_choice = rangen.random(nsim)
        compl_indx = model_compl[totsim_indx] >= compl_choice
        sim_indx = totsim_indx[compl_indx]
        totcompsimmass = np.sum(sedgrid["M_ini"][sim_indx])
        print(
            f"number of simulated stars w/ completeness = {len(sim_indx)}; mass = {totcompsimmass}"
        )

    else:  # total number of stars to simulate set by command line input

        if weight_to_use == "uniform":
            # sample to get the indices of the picked models
            sim_indx = rangen.choice(model_indx[goodobsmod], nsim)

        else:
            gridweights = sedgrid[weight_to_use][goodobsmod] * model_compl[
                goodobsmod]
            gridweights = gridweights / np.sum(gridweights)

            # sample to get the indexes of the picked models
            sim_indx = rangen.choice(model_indx[goodobsmod],
                                     size=nsim,
                                     p=gridweights)

        print(f"number of simulated stars = {nsim}")

    # setup the output table
    ot = Table()
    qnames = list(sedgrid.keys())
    # simulated data
    for k, filter in enumerate(sedgrid.filters):
        simflux_wbias = flux[sim_indx, k] + model_bias[sim_indx, k]

        simflux = rangen.normal(loc=simflux_wbias,
                                scale=model_unc[sim_indx, k])

        bname = filter.split(sep="_")[-1].upper()
        fluxname = f"{bname}_FLUX"
        colname = f"{bname}_RATE"
        magname = f"{bname}_VEGA"
        ot[fluxname] = Column(simflux)
        ot[colname] = Column(ot[fluxname] / vega_flux[k])
        pindxs = ot[colname] > 0.0
        nindxs = ot[colname] <= 0.0
        ot[magname] = Column(ot[colname])
        ot[magname][pindxs] = -2.5 * np.log10(ot[colname][pindxs])
        ot[magname][nindxs] = 99.999

        # add in the physical model values in a form similar to
        # the output simulated (physics+obs models) values
        # useful if using the simulated data to interpolate ASTs
        #   (e.g. for MATCH)
        fluxname = f"{bname}_INPUT_FLUX"
        ratename = f"{bname}_INPUT_RATE"
        magname = f"{bname}_INPUT_VEGA"
        ot[fluxname] = Column(flux[sim_indx, k])
        ot[ratename] = Column(ot[fluxname] / vega_flux[k])
        pindxs = ot[ratename] > 0.0
        nindxs = ot[ratename] <= 0.0
        ot[magname] = Column(ot[ratename])
        ot[magname][pindxs] = -2.5 * np.log10(ot[ratename][pindxs])
        ot[magname][nindxs] = 99.999

    # model parmaeters
    for qname in qnames:
        ot[qname] = Column(sedgrid[qname][sim_indx])

    return ot
Esempio n. 6
0
def gen_SimObs_from_sedgrid(sedgrid, sedgrid_noisemodel,
                            nsim=100, compl_filter='F475W',
                            ranseed=None, vega_fname=None):
    """
    Generate simulated observations using the physics and observation grids.
    The priors are sampled as they give the ensemble model for the stellar
    and dust distributions (IMF, Av distribution etc.).
    The physics model gives the SEDs based on the priors.
    The observation model gives the noise, bias, and completeness all of
    which are used in simulating the observations.

    Currently written to only work for the toothpick noisemodel.

    Parameters
    ----------
    sedgrid: grid.SEDgrid instance
        model grid

    sedgrid_noisemodel: beast noisemodel instance
        noise model data

    nsim : int
        number of observations to simulate

    compl_filter : str
        filter to use for completeness (required for toothpick model)

    ranseed : int
        used to set the seed to make the results reproducable
        useful for testing

    vega_fname : string
        filename for the vega info
        usefule for testing

    Returns
    -------
    simtable : astropy Table
        table giving the simulated observed fluxes as well as the
        physics model parmaeters
    """
    flux = sedgrid.seds
    n_models, n_filters = flux.shape

    # hack to get things to run for now
    short_filters = [filter.split(sep='_')[-1].lower()
                     for filter in sedgrid.filters]
    if compl_filter.lower() not in short_filters:
        print('requested completeness filter not present')
        print('%s requested' % compl_filter.lower())
        print('possible filters', short_filters)
        exit()
    filter_k = short_filters.index(compl_filter.lower())
    print('Completeness from %s' % sedgrid.filters[filter_k])

    # cache the noisemodel values
    model_bias = sedgrid_noisemodel.root.bias[:]
    model_unc = np.fabs(sedgrid_noisemodel.root.error[:])
    model_compl = sedgrid_noisemodel.root.completeness[:]

    # the combined prior and grid weights
    # using both as the grid weight needed to account for the finite size
    #   of each grid bin
    # if we change to interpolating between grid points, need to rethink this
    gridweights = sedgrid['weight']*model_compl[:, filter_k]
    # need to sum to 1
    gridweights = gridweights/np.sum(gridweights)

    # set the random seed - mainly for testing
    if not None:
        np.random.seed(ranseed)

    # sample to get the indexes of the picked models
    indx = range(n_models)
    sim_indx = np.random.choice(indx, size=nsim, p=gridweights)

    # get the vega fluxes for the filters
    _, vega_flux, _ = Vega(source=vega_fname).getFlux(sedgrid.filters)

    # setup the output table
    ot = Table()
    qnames = list(sedgrid.keys())
    # simulated data
    for k, filter in enumerate(sedgrid.filters):
        colname = '%s_rate' % filter.split(sep='_')[-1].lower()
        simflux_wbias = flux[sim_indx, k] + model_bias[sim_indx, k]
        simflux = np.random.normal(loc=simflux_wbias,
                                   scale=model_unc[sim_indx, k])
        ot[colname] = Column(simflux/vega_flux[k])
    # model parmaeters
    for qname in qnames:
        ot[qname] = Column(sedgrid[qname][sim_indx])

    return ot
Esempio n. 7
0
def plot_ast(ast_file, sed_grid_file=None):
    """
    Make a histogram of the AST fluxes.  If an SED grid is given, also plot
    a comparison histogram of the SED fluxes.

    The histogram bins are set by the bins originally used to create the ASTs
    (using the flux bin method), which are saved in
       ast_file.replace('inputAST','ASTfluxbins')
    and are automatically read in.

    Output plot is saved in the same location/name as ast_file, but with a .png
    instead of .txt.

    Parameters
    ----------
    ast_file : string
        name of AST input file

    sed_grid_file : string (default=None)
        name of SED grid file
    """

    # read in AST info
    ast_table = Table.read(ast_file, format="ascii")
    ast_fluxbins = Table.read(ast_file.replace("inputAST", "ASTfluxbins"),
                              format="ascii")

    # get filter names (and it's even in order!)
    filter_cols = [col for col in ast_table.colnames if "_" in col]
    filter_list = [col[-5:] for col in filter_cols]
    n_filter = len(filter_list)

    # if chosen, read in model grid
    if sed_grid_file is not None:
        modelsedgrid = SEDGrid(sed_grid_file)
        with Vega() as v:
            _, vega_flux, _ = v.getFlux(filter_cols)
        sedsMags = -2.5 * np.log10(modelsedgrid.seds[:] / vega_flux)

    # make a histogram for each filter
    fig = plt.figure(figsize=(7, 4 * n_filter))

    for f, filt in enumerate(filter_list):

        # names of table columns with bin values
        min_bin_col = [
            b for b in ast_fluxbins.colnames if ("mins" in b and filt in b)
        ][0]
        max_bin_col = [
            b for b in ast_fluxbins.colnames if ("maxs" in b and filt in b)
        ][0]
        # use those to make a bin list
        bin_list = np.append(ast_fluxbins[min_bin_col],
                             ast_fluxbins[max_bin_col][-1])

        # make histograms
        ax = plt.subplot(n_filter, 1, f + 1)

        ast_col = [b for b in ast_table.colnames if filt in b][0]

        plt.hist(
            ast_table[ast_col],
            bins=bin_list,
            density=True,
            facecolor="black",
            edgecolor="none",
            alpha=0.3,
            label="ASTs",
        )

        if sed_grid_file is not None:
            plt.hist(
                sedsMags[:, f],
                bins=bin_list,
                density=True,
                histtype="step",
                facecolor="none",
                edgecolor="black",
                label="Model grid",
            )

        # labels
        ax.tick_params(axis="both", which="major", labelsize=13)
        ax.set_xlim(ax.get_xlim()[::-1])
        plt.xlabel(filt + " (Vega mag)", fontsize=14)
        plt.ylabel("Normalized Histogram", fontsize=14)

        # add legend in first plot
        if f == 0:
            ax.legend(fontsize=14)

    plt.tight_layout()

    fig.savefig(ast_file.replace(".txt", ".png"))
    plt.close(fig)
Esempio n. 8
0
def calc_depth(
    physgrid_list,
    noise_model_list,
    completeness_value=0.5,
    vega_mag=True,
    vega_fname=None,
):
    """
    Calculate the observation depth of a field using the completeness.  Some
    fields have low completeness at both faint and bright fluxes; this finds
    the faintest flux at which the completeness exceeds the given value(s).

    Parameters
    ----------
    physgrid_list : string or list of strings
        Name of the physics model file.  If there are multiple physics model
        grids (i.e., if there are subgrids), list them all here.

    noise_model_list : string or list of strings
        Name of the noise model file.  If there are multiple files for
        physgrid_list (because of subgrids), list the noise model file
        associated with each physics model file.

    completeness_value : float or list of floats
        The completeness(es) at which to evaluate the depth. Completeness is
        defined in the range 0.0 to 1.0.

    vega_mag : boolean (default=True)
        If True, return results in Vega mags. Otherwise returns flux in
        erg/s/cm^2/A.

    vega_fname : string
        filename for the vega info (useful for testing)


    Returns
    -------
    depth_dict : dictionary
        keys are the filters present in the grid, each value is the flux or Vega
        mag for each of the requested completeness values

    """

    # ------ Reading in data

    # If there are subgrids, we can't read them all into memory.  Therefore,
    # we'll go through each one and just grab the relevant parts.
    compl_table_list = []

    # make a table for each physics model + noise model
    for physgrid, noise_model in zip(
        np.atleast_1d(physgrid_list), np.atleast_1d(noise_model_list)
    ):

        # get the physics model grid - includes priors
        modelsedgrid = SEDGrid(str(physgrid))
        if hasattr(modelsedgrid.seds, "read"):
            sed_grid = modelsedgrid.seds.read()
        else:
            sed_grid = modelsedgrid.seds
        # get list of filters
        filter_list = modelsedgrid.filters

        # read in the noise model
        noisegrid = noisemodel.get_noisemodelcat(str(noise_model))
        # get the completeness
        model_compl = noisegrid["completeness"]

        # put it all into a table
        table_dict = {filt: sed_grid[:, f] for f, filt in enumerate(filter_list)}
        table_dict.update(
            {filt + "_compl": model_compl[:, f] for f, filt in enumerate(filter_list)}
        )

        # append to the list
        compl_table_list.append(Table(table_dict))

    # stack all the tables into one
    compl_table = vstack(compl_table_list)

    # if chosen, get the vega fluxes for the filters
    if vega_mag:
        _, vega_flux, _ = Vega(source=vega_fname).getFlux(filter_list)

    # ------ Calculate depth

    # initialize dictionary to hold results
    depth_dict = {filt: [] for filt in filter_list}

    # grab numbers for each filter
    for f, filt in enumerate(filter_list):

        use_sed = compl_table[filt]
        use_comp = compl_table[filt + "_compl"]

        # get sorted versions of data
        sort_ind = np.argsort(use_sed)
        sort_sed = use_sed[sort_ind]
        sort_comp = use_comp[sort_ind]

        # grab depths
        for compl in np.atleast_1d(completeness_value):

            # first check whether the noise model even covers this completeness
            # (in case there weren't sufficient ASTs)
            if (compl < np.min(sort_comp)) or (compl > np.max(sort_comp)):
                depth_dict[filt].append(np.nan)
                continue

            # find first instance of completeness > N
            first_ind = np.where(sort_comp > compl)[0][0]
            # corresponding flux
            comp_flux = sort_sed[first_ind]
            # save it
            if vega_mag:
                depth_dict[filt].append(-2.5 * np.log10(comp_flux / vega_flux[f]))
            else:
                depth_dict[filt].append(comp_flux)

    # return the results
    return depth_dict
Esempio n. 9
0
def pick_models_toothpick_style(
    sedgrid_fname,
    filters,
    Nfilter,
    N_fluxes,
    min_N_per_flux,
    outfile=None,
    outfile_params=None,
    bins_outfile=None,
    bright_cut=None,
):
    """
    Creates a fake star catalog from a BEAST model grid. The chosen seds
    are optimized for the toothpick model, by working with a given
    number of flux bins, and making sure that every flux bin is covered
    by at least a given number of models (for each filter individually,
    which is how the toothpick model works).

    Parameters
    ----------
    sedgrid_fname: string
        BEAST model grid from which the models are picked (hdf5 file)

    filters: list of string
        Names of the filters, to be used as columns of the output table

    Nfilter: integer
        In how many filters a fake star needs to be brighter than the
        mag_cut value

    N_fluxes: integer
        The number of flux bins into which the dynamic range of the
        model grid in each filter is divided

    min_N_per_flux: integer
        Minimum number of model seds that need to fall into each bin

    outfile: string
        Output path for the models (optional). If this file already
        exists, the chosen seds are loaded from this file instead.

    outfile_params: string (default=None)
        If a file name is given, the physical parameters associated with
        each model will be written to disk

    bins_outfile: string
        Output path for a file containing the flux bin limits for each
        filter, and the number of samples for each (optional)

    bright_cut: list of float
        List of magnitude limits for each filter (won't sample model
        SEDs that are too bright)

    Returns
    -------
    sedsMags: astropy Table
        A table containing the selected model seds (columns are named
        after the filters)

    """
    if outfile is not None and os.path.isfile(outfile):
        print(
            "{} already exists. Will attempt to load SEDs for ASTs from there."
            .format(outfile))
        t = Table.read(outfile, format="ascii")
        return t

    with Vega() as v:
        vega_f, vega_flux, lambd = v.getFlux(filters)

    modelsedgrid = FileSEDGrid(sedgrid_fname)

    sedsMags = -2.5 * np.log10(modelsedgrid.seds[:] / vega_flux)
    Nf = sedsMags.shape[1]

    # Check if logL=-9.999 model points sliently sneak through
    if min(modelsedgrid.grid["logL"]) < -9:
        warnings.warn('There are logL=-9.999 model points in the SED grid!')
        print('Excluding those SED models from selecting input ASTs')
        idxs = np.where(modelsedgrid.grid["logL"] > -9)[0]
        sedsMags = sedsMags[idxs]

    # Set up a number of flux bins for each filter
    maxes = np.amax(sedsMags, axis=0)
    mins = np.amin(sedsMags, axis=0)

    bin_edges = np.zeros((N_fluxes + 1, Nf))  # indexed on [fluxbin, nfilters]
    for f in range(Nf):
        bin_edges[:, f] = np.linspace(mins[f], maxes[f], N_fluxes + 1)
    bin_mins = bin_edges[:-1, :]
    bin_maxs = bin_edges[1:, :]
    if not len(bin_mins) == len(bin_maxs) == N_fluxes:
        raise AssertionError()

    bin_count = np.zeros((N_fluxes, Nf))
    chosen_idxs = []
    counter = 0
    successes = 0
    include_mask = np.full(idxs.shape, True, dtype=bool)
    chunksize = 100000
    while True:
        counter += 1
        # pick some random models
        rand_idx = np.random.choice(idxs[include_mask], size=chunksize)
        randomseds = sedsMags[rand_idx, :]

        # Find in which bin each model belongs, for each filter
        fluxbins = np.zeros(randomseds.shape, dtype=int)
        for fltr in range(Nf):
            fluxbins[:, fltr] = np.digitize(randomseds[:, fltr],
                                            bin_maxs[:, fltr])

        # Clip in place (models of which the flux is equal to the max
        # are assigned bin nr N_fluxes. Move these down to bin nr
        # N_fluxes - 1)
        np.clip(fluxbins, a_min=0, a_max=N_fluxes - 1, out=fluxbins)

        add_these = np.full((len(rand_idx)), False, dtype=bool)
        for r in range(len(rand_idx)):
            # If any of the flux bins that this model falls into does
            # not have enough samples yet, add it to the list of model
            # spectra to be output
            if (bin_count[fluxbins[r, :], range(Nf)] < min_N_per_flux).any():
                bin_count[fluxbins[r, :], range(Nf)] += 1
                successes += 1
                add_these[r] = True

            # If all these bins are full...
            else:
                # ... do not include this model again, since we will reject it
                # anyway.
                include_mask[idxs == rand_idx] = False

        # Add the approved models
        chosen_idxs.extend(rand_idx[add_these])

        # If some of the randomly picked models were not added
        if not add_these.any():
            # ... check if we have enough samples everywhere, or if all
            # the models have been exhausted (and hence the bins are
            # impossible to fill).
            enough_samples = (bin_count.flatten() >= min_N_per_flux).all()
            still_models_left = include_mask.any()
            if enough_samples or not still_models_left:
                break

        if not counter % 10:
            print("Sampled {} models. {} successfull seds. Ratio = {}".format(
                counter * chunksize, successes,
                successes / counter / chunksize))
            print("Bin array:")
            print(bin_count)

    # Gather the selected model seds in a table
    sedsMags = Table(sedsMags[chosen_idxs, :], names=filters)

    if outfile is not None:
        ascii.write(
            sedsMags,
            outfile,
            overwrite=True,
            formats={k: "%.5f"
                     for k in sedsMags.colnames},
        )

    # if chosen, save the corresponding model parameters
    if outfile_params is not None:
        grid_dict = {}
        for key in list(modelsedgrid.grid.keys()):
            grid_dict[key] = modelsedgrid.grid[key][chosen_idxs]
        grid_dict['sedgrid_indx'] = chosen_idxs
        ast_params = Table(grid_dict)
        ast_params.write(outfile_params, overwrite=True)

    if bins_outfile is not None:
        bin_info_table = Table()
        col_bigarrays = [bin_mins, bin_maxs, bin_count]
        col_basenames = ["bin_mins_", "bin_maxs_", "bin_count_"]
        for fltr, filter_name in enumerate(filters):
            for bigarray, basename in zip(col_bigarrays, col_basenames):
                bin_info_table.add_column(
                    Column(bigarray[:, fltr], name=basename + filter_name))
        ascii.write(bin_info_table, bins_outfile, overwrite=True)

    return sedsMags
Esempio n. 10
0
def pick_models(
    sedgrid_fname,
    filters,
    mag_cuts,
    Nfilter=3,
    N_stars=70,
    Nrealize=20,
    outfile=None,
    outfile_params=None,
    bright_cut=None,
    vega_fname=None,
    ranseed=None,
):
    """Creates a fake star catalog from a BEAST model grid

    Parameters
    ----------
    sedgrid_fname: string
        BEAST model grid from which the models are picked (hdf5 file)

    filters: list of string
        Names of the filters

    mag_cuts: list
        List of magnitude limits for each filter

    Nfilter: Integer
             In how many filters, you want a fake star to be brighter
             than the limit (mag_cut) (default = 3)

    N_stars: Integer
               Number of stellar models picked per a single log(age)
               (default=70)

    Nrealize: Integer
              Number of realization of each models (default = 20)

    outfile: str
        If a file name is given, the selected models will be written to
        disk

    outfile_params: str
        If a file name is given, the physical parameters associated with
        each model will be written to disk

    bright_cut: list of float
        Same as mag_cuts, but for the bright end

    vega_fname: str
        filename of vega file

    ranseed : int
        used to set the seed to make the results reproducable
        useful for testing

    Returns
    -------
    astropy Table of selected models
    - and optionally -
    ascii file: A list of selected models, written to 'outfile'
    fits file: the corresponding physical parameters, written to 'outfile_params'
    """

    with Vega(source=vega_fname) as v:  # Get the vega fluxes
        vega_f, vega_flux, lamb = v.getFlux(filters)

    # gridf = h5py.File(sedgrid_fname)
    modelsedgrid = FileSEDGrid(sedgrid_fname)

    # Convert to Vega mags
    # sedsMags = -2.5 * np.log10(gridf['seds'][:] / vega_flux)
    sedsMags = -2.5 * np.log10(modelsedgrid.seds[:] / vega_flux)

    # make sure Nfilters isn't larger than the total number of filters
    if Nfilter > len(filters):
        Nfilter = len(filters)

    # Select the models above the magnitude limits in N filters
    idxs = mag_limits(sedsMags,
                      mag_cuts,
                      Nfilter=Nfilter,
                      bright_cut=bright_cut)
    # grid_cut = gridf['grid'][list(idxs)]
    cols = {}
    for key in list(modelsedgrid.grid.keys()):
        cols[key] = modelsedgrid.grid[key][idxs]
    grid_cut = Table(cols)

    # Sample the model grid uniformly
    prime_params = np.column_stack(
        (grid_cut["logA"], grid_cut["M_ini"], grid_cut["Av"]))
    search_age = np.unique(prime_params[:, 0])

    N_sample = N_stars
    model_ind = []  # indices for the model grid
    ast_params = grid_cut[[]]  # the corresponding model parameters

    # set the random seed - mainly for testing
    if not None:
        np.random.seed(ranseed)

    for iage in search_age:
        tmp, = np.where(prime_params[:, 0] == iage)
        new_ind = np.random.choice(tmp, N_sample)
        model_ind.append(new_ind)
        [ast_params.add_row(grid_cut[new_ind[i]]) for i in range(len(new_ind))]

    index = np.repeat(idxs[np.array(model_ind).reshape((-1))], Nrealize)
    sedsMags = Table(sedsMags[index, :], names=filters)

    if outfile is not None:
        ascii.write(
            sedsMags,
            outfile,
            overwrite=True,
            formats={k: "%.5f"
                     for k in sedsMags.colnames},
        )

    if outfile_params is not None:
        ast_params.write(outfile_params, overwrite=True)

    return sedsMags
Esempio n. 11
0
from beast.observationmodel.vega import Vega

if __name__ == '__main__':
    filters = [
        'HST_WFC3_F275W', 'HST_WFC3_F336W', 'HST_ACS_WFC_F475W',
        'HST_ACS_WFC_F814W', 'HST_WFC3_F110W', 'HST_WFC3_F160W'
    ]

    with Vega() as v:
        _, vega_flux, _ = v.getFlux(filters)

    for cfilt, cvflux in zip(filters, vega_flux):
        print(cfilt, cvflux)
Esempio n. 12
0
def supplement_ast(
    sedgrid_fname,
    filters,
    nAST=1000,
    existingASTfile=None,
    outASTfile=None,
    outASTfile_params=None,
    mag_cuts=None,
    color_cuts=None,
):
    """
    Creates an additional fake star catalog from a BEAST model grid
    that fulfills the customized conditions to supplement input ASTs.
    If the existing input AST parameter file is given, already selected
    models will be excluded from this process. The input artificial
    stars are picked randomly from the remaining models.

    Parameters
    ----------
    sedgrid_fname: string
        BEAST model grid from which the models are picked (hdf5 file)

    filters: list of string
        Names of the filters

    nAST: int
        Number of unique additional ASTs per source density bin

    existingASTfile: string (optional, default=None)
        Name of the existing input AST parameter file. If not None,
        the models that were already listed in the existing list Will
        be removed by default

    outASTfile: string (optional, default=None)
        Output file name for the chosen models

    outASTfile_params: string (optional, default=None)
        If a file name is given, the physical parameters associated with
        each model will be written to disk

    mag_cut: dictionary (optional, default=None)
        Dictionary of bright and faint magnitude limits for given filters.
        The way to specify the cuts is by updating the "ast_suppl_maglimit" key
        in the beast_settings file. This is a dictionary that includes information
        for the magnitude cuts as a function of the filters included in observation.

        For example, for a field observed with HST_WFC3_F336W, HST_WFC3_F475W,
        and HST_WFC3_F814W, to set a magnitude range limit of 16<HST_WFC3_F475W<28 mag,
        and 15<HST_WFC3_F814W<27 mag you need to set the following within the beast_settings file:

        # specify that the ast_supplement mode should be on
        ast_supplement = True

        # initialize and populate the dictionary of desired magnitude limits
        ast_suppl_maglimits = {}
        # the magntidue limits are defined by the filter and a list of the limits in magnitudes
        ast_suppl_maglimits["HST_WFC3_F475W"] = [16,28]
        ast_suppl_maglimits["HST_WFC3_F814W"] = [15,27]

        # set the key word
        ast_suppl_maglimit = ast_suppl_maglimits

    color_cut: dictionary (optional, default=None)
        Dictionary of red color limits for given filters.
        The way to specify the cuts is by updating the "ast_suppl_colorlimit" key
        in the beast_settings file. This is a dictionary that includes information
        for the color cuts as a function of the filters included in observation.

        For example, for a field observed with HST_WFC3_F336W, HST_WFC3_F475W,
        and HST_WFC3_F814W, to set a color range limit of HST_WFC3_F475W-HST_WFC3_F814W<6,
        HST_WFC3_F336W-HST_WFC3_F475W<5 and HST_WFC3_F336W-HST_WFC3_F814W<4, you need
        to set the following within the beast_settings file:

        # specify that the ast_supplement mode should be on
        ast_supplement = True

        # initialize the dictionary of desired magnitude limits
        ast_suppl_colorlimits = {}

        # the color limits are defined by the first filter in the color (e.g, X for X-Y),
        # and the input is a list including the second filter (e.g., Y for X-Y) and the
        # color limit in magnitudes
        ast_suppl_colorlimits["HST_WFC3_F475W"] = [["HST_WFC3_F814W",6]]
        ast_suppl_colorlimits["HST_WFC3_F336W"] = [["HST_WFC3_F475W",5], ["HST_WFC3_F814W",4]]

        # set the key word
        ast_suppl_colorlimit =  ast_suppl_colorlimits

    Returns
    -------
    sedsMags: astropy Table
        A table containing the selected model seds (columns are named
        after the filters)

    """

    with Vega() as v:
        vega_f, vega_flux, lambd = v.getFlux(filters)

    modelsedgrid = SEDGrid(sedgrid_fname)

    # Convert to Vega mags
    sedsMags = -2.5 * np.log10(modelsedgrid.seds[:] / vega_flux)

    Nseds = sedsMags.shape[0]
    sedsIndx = np.arange(Nseds)

    if existingASTfile is not None and os.path.isfile(existingASTfile):
        print("{} exists. Will attempt to load SEDs for ASTs from there \
            and remove those SEDs from the SED grid".format(existingASTfile))
        print("existing AST file", existingASTfile)
        t = Table.read(existingASTfile, format="fits")
        sedsMags = np.delete(sedsMags, t["sedgrid_indx"], axis=0)
        sedsIndx = np.delete(sedsIndx, t["sedgrid_indx"])
        Nseds = sedsMags.shape[0]

    # Apply selection conditions if supplied
    # Just magnitude cuts
    print("mag_cuts", mag_cuts)
    print("color_cuts", color_cuts)
    if mag_cuts is not None:
        cond = np.ones(Nseds, dtype=bool)
        for key in list(mag_cuts.keys()):
            idx_filter = [i for i, iflt in enumerate(filters) if key in iflt]
            bright_cut = mag_cuts[key][0]
            faint_cut = mag_cuts[key][1]
            tmp_cond = np.logical_and(
                (sedsMags[:, idx_filter] >= bright_cut),
                (sedsMags[:, idx_filter] <= faint_cut),
            )

            if color_cuts is not None:
                if key in color_cuts:
                    for limit in color_cuts[key]:

                        idx_color_filter = [
                            i for i, iflt in enumerate(filters)
                            if limit[0] in iflt
                        ]
                        tmp_cond = np.logical_and(
                            tmp_cond,
                            (sedsMags[:, idx_filter] -
                             sedsMags[:, idx_color_filter] <= limit[1]),
                        )
            cond = np.logical_and(cond, tmp_cond.ravel())

        sedsMags = sedsMags[cond, :]
        sedsIndx = sedsIndx[cond]

    # Randomly select models
    # Supplementing ASTs does not need to follow
    # the toothpick-way selection
    chosen_idxs = np.random.choice(len(sedsIndx), nAST)
    sedsIndx = sedsIndx[chosen_idxs]

    # Gather the selected model seds in a table
    sedsMags = Table(sedsMags[chosen_idxs, :], names=filters)

    if outASTfile is not None:
        ascii.write(
            sedsMags,
            outASTfile,
            overwrite=True,
            formats={k: "%.5f"
                     for k in sedsMags.colnames},
        )

    # if chosen, save the corresponding model parameters
    if outASTfile_params is not None:
        grid_dict = {}
        for key in list(modelsedgrid.grid.keys()):
            grid_dict[key] = modelsedgrid.grid[key][sedsIndx]
        grid_dict["sedgrid_indx"] = sedsIndx
        ast_params = Table(grid_dict)
        ast_params.write(outASTfile_params, overwrite=True)

    return sedsMags