Пример #1
0
def split_and_check(grid_fname, num_subgrids):
    complete_g = SEDGrid(grid_fname)
    sub_fnames = subgridding_tools.split_grid(grid_fname, num_subgrids)

    # count the number of grid cells
    sub_seds = []
    sub_grids = []

    for sub_fname in sub_fnames:
        sub_g = SEDGrid(sub_fname)

        sub_seds.append(sub_g.seds)
        sub_grids.append(sub_g.grid)

        np.testing.assert_equal(complete_g.lamb, sub_g.lamb)
        if not complete_g.grid.colnames == sub_g.grid.colnames:
            raise AssertionError()

    sub_seds_reconstructed = np.concatenate(sub_seds)
    np.testing.assert_equal(sub_seds_reconstructed, complete_g.seds)

    sub_grids_reconstructed = np.concatenate(sub_grids)
    np.testing.assert_equal(sub_grids_reconstructed, complete_g.grid)

    # the split method skips anything that already exists, so if we
    # want to use this function multiple times for the same test
    # grid, we need to do this.
    for f in sub_fnames:
        os.remove(f)
Пример #2
0
    def test_trim_grid(self):
        """
        Generate trim the sed grid and noise model using cached versions of the
        both and compare the result to a cached version.
        """
        # read in the observed data
        obsdata = Observations(self.obs_fname_cache, self.settings.filters,
                               self.settings.obs_colnames)

        # get the modesedgrid
        modelsedgrid = SEDGrid(self.seds_fname_cache)

        # read in the noise model just created
        noisemodel_vals = noisemodel.get_noisemodelcat(self.noise_fname_cache)

        # trim the model sedgrid
        seds_trim_fname = tempfile.NamedTemporaryFile(suffix=".hd5").name
        noise_trim_fname = tempfile.NamedTemporaryFile(suffix=".hd5").name

        trim_models(
            modelsedgrid,
            noisemodel_vals,
            obsdata,
            seds_trim_fname,
            noise_trim_fname,
            sigma_fac=3.0,
        )

        # compare the new to the cached version
        compare_hdf5(self.seds_trim_fname_cache, seds_trim_fname, ctype="seds")
        compare_hdf5(self.noise_trim_fname_cache,
                     noise_trim_fname,
                     ctype="noise")
Пример #3
0
    def test_simobs(self):
        """
        Simulate observations using cached versions of the sed grid and noise model
        and compare the result to a cached version.
        """
        # download files specific to this test
        simobs_fname_cache = download_rename("beast_example_phat_simobs.fits")

        # get the physics model grid - includes priors
        modelsedgrid = SEDGrid(self.seds_fname_cache)

        # read in the noise model - includes bias, unc, and completeness
        noisegrid = noisemodel.get_noisemodelcat(self.noise_fname_cache)

        table_new = gen_SimObs_from_sedgrid(
            modelsedgrid,
            noisegrid,
            nsim=100,
            compl_filter="max",
            ranseed=1234,
        )

        # check that the simobs files are exactly the same
        table_cache = Table.read(simobs_fname_cache)

        # to avoid issues with uppercase vs lowercase column names, make them all
        # the same before comparing
        for col in table_new.colnames:
            table_new[col].name = col.upper()
        for col in table_cache.colnames:
            table_cache[col].name = col.upper()

        compare_tables(table_cache, table_new)
Пример #4
0
def gen_obsmodel(modelsedgridfile, source_density=None, use_rate=True):
    """
    Code to create filenames and run the toothpick noise model

    Parameters
    ----------
    modelsedgridfile : string
        path+name of the physics model grid file

    source_density : string (default=None)
        set to None if there's no source density info, otherwise set to
        a string of the form "#-#"

    use_rate : boolean (default=True)
        Choose whether to use the rate or magnitude when creating the noise
        model.  This should always be True, but is currently an option to be
        compatible with the phat_small example (which has no rate info).
        When that gets fixed, please remove this option!

    Returns
    -------
    noisefile : string
        name of the created noise file
    """

    print("")

    # noise and AST file names
    noisefile = modelsedgridfile.replace("seds", "noisemodel")
    astfile = datamodel.astfile

    # If we are treating regions with different
    # backgrounds/source densities separately, pick one of the
    # split ast files, and name noise file accordingly
    if source_density is not None:
        noisefile = noisefile.replace("noisemodel",
                                      "noisemodel_bin" + source_density)
        astfile = datamodel.astfile.replace(
            ".fits", "_bin" + source_density.replace("_", "-") + ".fits")

    # only create noise file if it doesn't already exist
    if not os.path.isfile(noisefile):

        print("creating " + noisefile)

        modelsedgrid = SEDGrid(modelsedgridfile)

        noisemodel.make_toothpick_noise_model(
            noisefile,
            astfile,
            modelsedgrid,
            absflux_a_matrix=datamodel.absflux_a_matrix,
            use_rate=use_rate,
        )

    else:
        print(noisefile + " already exists")

    return noisefile  # (same as noisefile)
Пример #5
0
def gen_obsmodel(settings, modelsedgridfile, source_density=None):
    """
    Code to create filenames and run the toothpick noise model

    Parameters
    ----------
    settings : beast.tools.beast_settings.beast_settings instance
        object with the beast settings

    modelsedgridfile : string
        path+name of the physics model grid file

    source_density : string (default=None)
        set to None if there's no source density info, otherwise set to
        a string of the form "#-#"

    Returns
    -------
    noisefile : string
        name of the created noise file
    """

    print("")

    # noise and AST file names
    noisefile = modelsedgridfile.replace("seds", "noisemodel")
    astfile = settings.astfile

    # If we are treating regions with different
    # backgrounds/source densities separately, pick one of the
    # split ast files, and name noise file accordingly
    if source_density is not None:
        noisefile = noisefile.replace("noisemodel",
                                      "noisemodel_bin" + source_density)
        astfile = settings.astfile.replace(
            ".fits", "_bin" + source_density.replace("_", "-") + ".fits")

    # only create noise file if it doesn't already exist
    if not os.path.isfile(noisefile):

        print("creating " + noisefile)

        modelsedgrid = SEDGrid(modelsedgridfile)

        noisemodel.make_toothpick_noise_model(
            noisefile,
            astfile,
            modelsedgrid,
            absflux_a_matrix=settings.absflux_a_matrix,
        )

    else:
        print(noisefile + " already exists")

    return noisefile  # (same as noisefile)
Пример #6
0
def test_trim_grid():

    # download the needed files
    vega_fname = download_rename("vega.hd5")
    seds_fname = download_rename("beast_example_phat_seds.grid.hd5")
    noise_fname = download_rename("beast_example_phat_noisemodel.grid.hd5")
    obs_fname = download_rename("b15_4band_det_27_A.fits")

    # download cached version of noisemodel on the sed grid
    noise_trim_fname_cache = download_rename(
        "beast_example_phat_noisemodel_trim.grid.hd5")
    seds_trim_fname_cache = download_rename(
        "beast_example_phat_seds_trim.grid.hd5")

    ################

    # read in the observed data
    filters = [
        "HST_WFC3_F275W",
        "HST_WFC3_F336W",
        "HST_ACS_WFC_F475W",
        "HST_ACS_WFC_F814W",
        "HST_WFC3_F110W",
        "HST_WFC3_F160W",
    ]
    basefilters = ["F275W", "F336W", "F475W", "F814W", "F110W", "F160W"]
    obs_colnames = [f.lower() + "_rate" for f in basefilters]

    obsdata = Observations(obs_fname,
                           filters,
                           obs_colnames,
                           vega_fname=vega_fname)

    # get the modesedgrid
    modelsedgrid = SEDGrid(seds_fname)

    # read in the noise model just created
    noisemodel_vals = noisemodel.get_noisemodelcat(noise_fname)

    # trim the model sedgrid
    seds_trim_fname = "beast_example_phat_seds_trim.grid.hd5"
    noise_trim_fname = seds_trim_fname.replace("_seds", "_noisemodel")

    trim_models(
        modelsedgrid,
        noisemodel_vals,
        obsdata,
        seds_trim_fname,
        noise_trim_fname,
        sigma_fac=3.0,
    )

    # compare the new to the cached version
    compare_hdf5(seds_trim_fname_cache, seds_trim_fname, ctype="seds")
    compare_hdf5(noise_trim_fname_cache, noise_trim_fname, ctype="noise")
Пример #7
0
def split_and_check(grid_fname, num_subgrids):
    """
    Split a sed grid into subgrids and test the contents of the subgrids
    are as expected and concatenating the subgrid components (seds, grid)
    gives the full sed grid.

    Parameters
    ----------
    grid_fname : str
        filename for the sed grid

    num_subgrids : int
        number of subgrids to split the sed grid into
    """
    complete_g = SEDGrid(grid_fname)
    sub_fnames = subgridding_tools.split_grid(grid_fname, num_subgrids)

    # count the number of grid cells
    sub_seds = []
    sub_grids = []

    for sub_fname in sub_fnames:
        sub_g = SEDGrid(sub_fname)

        sub_seds.append(sub_g.seds)
        sub_grids.append(sub_g.grid)

        np.testing.assert_equal(complete_g.lamb, sub_g.lamb)
        if not complete_g.grid.colnames == sub_g.grid.colnames:
            raise AssertionError()

    sub_seds_reconstructed = np.concatenate(sub_seds)
    np.testing.assert_equal(sub_seds_reconstructed, complete_g.seds)

    sub_grids_reconstructed = np.concatenate(sub_grids)
    np.testing.assert_equal(sub_grids_reconstructed, complete_g.grid)

    # the split method skips anything that already exists, so if we
    # want to use this function multiple times for the same test
    # grid, we need to do this.
    for f in sub_fnames:
        os.remove(f)
Пример #8
0
def merge_grids(seds_fname, sub_names):
    """
    Merges a set of grids into one big grid. The grids need to have the
    same columns

    Parameters
    ----------
    seds_fname: string
        path for the output file

    sub_names: list of strings
        paths for the input grids
    """

    if not os.path.isfile(seds_fname):
        for n in sub_names:
            print("Appending {} to {}".format(n, seds_fname))
            g = SEDGrid(n)
            g.write(seds_fname, append=True)
    else:
        print("{} already exists".format(seds_fname))
Пример #9
0
def split_grid(grid_fname, num_subgrids, overwrite=False):
    """
    Splits a spectral or sed grid (they are the same class actually)
    according to grid point index (so basically, arbitrarily).

    Parameters
    ----------
    grid_fname: string
        file name of the existing grid to be split up

    num_subgrids: integer
        the number of parts the grid should be split into

    overwrite: bool
        any subgrids that already exist will be deleted if set to True.
        If set to False, skip over any grids that are already there.

    Returns
    -------
    list of string
        the names of the newly created subgrid files
    """

    g = SEDGrid(grid_fname, backend="disk")

    fnames = []

    num_seds = len(g.seds)
    slices = uniform_slices(num_seds, num_subgrids)
    for i, slc in enumerate(slices):

        subgrid_fname = grid_fname.replace(".hd5", "sub{}.hd5".format(i))
        fnames.append(subgrid_fname)
        if os.path.isfile(subgrid_fname):
            if overwrite:
                os.remove(subgrid_fname)
            else:
                print("{} already exists. Skipping.".format(subgrid_fname))
                continue

        print("constructing subgrid " + str(i))

        # Load a slice as a SEDGrid object
        sub_g = SEDGrid(
            g.lamb[:],
            seds=g.seds[slc],
            grid=Table(g.grid[slc]),
            backend="memory",
        )
        if g.filters is not None:
            sub_g.header["filters"] = " ".join(g.filters)

        # Save it to a new file
        sub_g.write(subgrid_fname, append=False)

    return fnames
Пример #10
0
def test_toothpick_noisemodel():

    # download the needed files
    asts_fname = download_rename("fake_stars_b15_27_all.hd5")
    filter_fname = download_rename("filters.hd5")
    vega_fname = download_rename("vega.hd5")
    hst_fname = download_rename("hst_whitedwarf_frac_covar.fits")
    seds_fname = download_rename("beast_example_phat_seds.grid.hd5")

    # download cached version of noisemodel on the sed grid
    noise_fname_cache = download_rename(
        "beast_example_phat_noisemodel.grid.hd5")

    ################
    # get the modesedgrid on which to generate the noisemodel
    modelsedgrid = SEDGrid(seds_fname)

    # absflux calibration covariance matrix for HST specific filters (AC)
    filters = [
        "HST_WFC3_F275W",
        "HST_WFC3_F336W",
        "HST_ACS_WFC_F475W",
        "HST_ACS_WFC_F814W",
        "HST_WFC3_F110W",
        "HST_WFC3_F160W",
    ]
    absflux_a_matrix = hst_frac_matrix(filters,
                                       hst_fname=hst_fname,
                                       filterLib=filter_fname)

    # generate the AST noise model
    noise_fname = "/tmp/beast_example_phat_noisemodel.grid.hd5"
    noisemodel.make_toothpick_noise_model(
        noise_fname,
        asts_fname,
        modelsedgrid,
        absflux_a_matrix=absflux_a_matrix,
        vega_fname=vega_fname,
        use_rate=False,
    )

    # compare the new to the cached version
    compare_hdf5(noise_fname_cache, noise_fname)
Пример #11
0
    def test_toothpick_noisemodel(self):
        """
        Generate the nosiemodel (aka observationmodel) using a cached version of
        the artifical star test results (ASTs) and compare the result to a cached
        version.
        """

        # get the modelsedgrid on which to generate the noisemodel
        modelsedgrid = SEDGrid(self.seds_fname_cache)

        # generate the AST noise model
        noise_fname = tempfile.NamedTemporaryFile(suffix=".hd5").name
        noisemodel.make_toothpick_noise_model(
            noise_fname,
            self.asts_fname_cache,
            modelsedgrid,
            absflux_a_matrix=self.settings.absflux_a_matrix,
        )

        # compare the new to the cached version
        compare_hdf5(self.noise_fname_cache, noise_fname)
Пример #12
0
def test_simobs():

    # download the needed files
    vega_fname = download_rename("vega.hd5")
    seds_fname = download_rename("beast_example_phat_seds.grid.hd5")
    noise_fname = download_rename("beast_example_phat_noisemodel.grid.hd5")

    # download cached version of noisemodel on the sed grid
    simobs_fname_cache = download_rename("beast_example_phat_simobs.fits")

    ################

    # get the physics model grid - includes priors
    modelsedgrid = SEDGrid(seds_fname)

    # read in the noise model - includes bias, unc, and completeness
    noisegrid = noisemodel.get_noisemodelcat(noise_fname)

    table_new = gen_SimObs_from_sedgrid(
        modelsedgrid,
        noisegrid,
        nsim=100,
        compl_filter="f475w",
        ranseed=1234,
        vega_fname=vega_fname,
    )

    # check that the simobs files are exactly the same
    table_cache = Table.read(simobs_fname_cache)

    # to avoid issues with uppercase vs lowercase column names, make them all
    # the same before comparing
    for col in table_new.colnames:
        table_new[col].name = col.upper()
    for col in table_cache.colnames:
        table_cache[col].name = col.upper()

    compare_tables(table_cache, table_new)
Пример #13
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
Пример #14
0
def plot_noisemodel(
    sed_file,
    noise_file_list,
    plot_file,
    samp=100,
    color=["black", "red", "gold", "lime", "xkcd:azure"],
    label=None,
):
    """
    Make a plot of the noise model: for each of the bandsm make plots of bias
    and uncertainty as a function of flux

    If there are multiple files in noise_file_list, each of them will be
    overplotted in each panel.

    Parameters
    ----------
    sed_file : string
        path+name of the SED grid file

    noise_file_list : list of strings
        path+name of the noise model file(s)

    plot_file : string
        name of the file to save the plot

    samp : int (default=100)
        plotting all of the SED points takes a long time for a viewer to load,
        so set this to plot every Nth point

    color : list of strings (default=['black','red','gold','lime','xkcd:azure'])
        colors to cycle through when making plots

    label : list of strings (default=None)
        if set, use these labels in a legend for each item in noise_file_list
    """

    # read in the SED grid
    print("* reading SED grid file")
    sed_object = SEDGrid(sed_file)
    if hasattr(sed_object.seds, "read"):
        sed_grid = sed_object.seds.read()
    else:
        sed_grid = sed_object.seds
    filter_list = sed_object.filters
    n_filter = len(filter_list)

    # figure
    fig, ax = plt.subplots(nrows=3, ncols=n_filter, figsize=(25, 15))

    # setup the plots
    fontsize = 12
    font = {"size": fontsize}

    plt.rc("font", **font)

    plt.rc("lines", linewidth=2)
    plt.rc("axes", linewidth=2)
    plt.rc("xtick.major", width=2)
    plt.rc("ytick.major", width=2)

    # go through noise files
    for n, nfile in enumerate(np.atleast_1d(noise_file_list)):

        print("* reading " + nfile)

        # read in the values
        noisemodel_vals = noisemodel.get_noisemodelcat(nfile)

        # extract error and bias
        noise_err = noisemodel_vals["error"]
        noise_bias = noisemodel_vals["bias"]
        noise_compl = noisemodel_vals["completeness"]

        # plot things
        for f, filt in enumerate(filter_list):

            # error is negative where it's been extrapolated -> trim those
            good_err = np.where(noise_err[:, f] > 0)[0]
            plot_sed = sed_grid[good_err, f][::samp]
            plot_err = noise_err[good_err, f][::samp]
            plot_bias = noise_bias[good_err, f][::samp]
            plot_compl = noise_compl[good_err, f][::samp]

            # bias
            bax = ax[0, f]
            bax.plot(
                np.log10(plot_sed),
                plot_bias / plot_sed,
                marker="o",
                linestyle="none",
                mew=0,
                ms=2,
                color=color[n % len(color)],
                alpha=0.1,
            )
            if label is not None:
                bax.set_label(label[n])

            bax.tick_params(axis="both", which="major")
            # ax.set_xlim(ax.get_xlim()[::-1])
            bax.set_xlabel("log " + filt)
            bax.set_ylabel(r"Bias ($\mu$/F)")

            # error
            eax = ax[1, f]
            eax.plot(
                np.log10(plot_sed),
                plot_err / plot_sed,
                marker="o",
                linestyle="none",
                mew=0,
                ms=2,
                color=color[n % len(color)],
                alpha=0.1,
            )
            if label is not None:
                eax.set_label(label[n])

            eax.tick_params(axis="both", which="major")
            # ax.set_xlim(ax.get_xlim()[::-1])
            eax.set_xlabel("log " + filt)
            eax.set_ylabel(r"Error ($\sigma$/F)")

            # completeness
            cax = ax[2, f]
            cax.plot(
                np.log10(plot_sed),
                plot_compl,
                marker="o",
                linestyle="none",
                mew=0,
                ms=2,
                color=color[n % len(color)],
                alpha=0.1,
            )
            if label is not None:
                cax.set_label(label[n])

            cax.tick_params(axis="both", which="major")
            # ax.set_xlim(ax.get_xlim()[::-1])
            cax.set_xlabel("log " + filt)
            cax.set_ylabel(r"Completeness")

            # do a legend if this is
            # (a) the leftmost panel
            # (b) the last line to be added
            # (c) there are labels set
            if (f == 0) and (n == len(noise_file_list) - 1) and (label
                                                                 is not None):
                leg = bax.legend(fontsize=12)
                for lh in leg.legendHandles:
                    lh._legmarker.set_alpha(1)
                leg = eax.legend(fontsize=12)
                for lh in leg.legendHandles:
                    lh._legmarker.set_alpha(1)

    plt.tight_layout()

    fig.savefig(plot_file)
    plt.close(fig)
Пример #15
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
Пример #16
0
def plot_noisemodel(
    sed_file,
    noise_file_list,
    plot_file,
    samp=100,
    cmap_name='viridis',
):
    """
    Make a plot of the noise model: for each of the bandsm make plots of bias
    and uncertainty as a function of flux

    If there are multiple files in noise_file_list, each of them will be
    overplotted in each panel.

    Parameters
    ----------
    sed_file : string
        path+name of the SED grid file

    noise_file_list : list of strings
        path+name of the noise model file(s)

    plot_file : string
        name of the file to save the plot

    samp : int (default=100)
        plotting all of the SED points takes a long time for a viewer to load,
        so set this to plot every Nth point

    cmap_name : string (default=plt.cm.viridis)
        name of a color map to use
    """

    # read in the SED grid
    print("* reading SED grid file")
    sed_object = SEDGrid(sed_file)
    if hasattr(sed_object.seds, "read"):
        sed_grid = sed_object.seds.read()
    else:
        sed_grid = sed_object.seds
    filter_list = sed_object.filters
    n_filter = len(filter_list)

    # figure
    fig, ax = plt.subplots(nrows=3, ncols=n_filter, figsize=(25, 15))

    # setup the plots
    fontsize = 12
    font = {"size": fontsize}

    plt.rc("font", **font)

    plt.rc("lines", linewidth=2)
    plt.rc("axes", linewidth=2)
    plt.rc("xtick.major", width=2)
    plt.rc("ytick.major", width=2)

    plt.set_cmap(cmap_name)

    # go through noise files after sorting them according to
    # their SD bin number
    noise_file_list.sort(key=lambda f: int(''.join(filter(str.isdigit, f))))
    bin_label = [re.findall(r"bin\d+", x)[0] for x in noise_file_list]
    for n, nfile in enumerate(np.atleast_1d(noise_file_list)):

        print("* reading " + nfile)

        # read in the values
        noisemodel_vals = noisemodel.get_noisemodelcat(nfile)

        # extract error and bias
        noise_err = noisemodel_vals["error"]
        noise_bias = noisemodel_vals["bias"]
        noise_compl = noisemodel_vals["completeness"]

        # plot things
        for f, filt in enumerate(filter_list):

            # error is negative where it's been extrapolated -> trim those
            good_err = np.where(noise_err[:, f] > 0)[0]
            plot_sed = sed_grid[good_err, f][::samp]
            plot_err = noise_err[good_err, f][::samp]
            plot_bias = noise_bias[good_err, f][::samp]
            plot_compl = noise_compl[good_err, f][::samp]

            # bias
            bax = ax[0, f]
            bax.plot(
                np.log10(plot_sed),
                plot_bias / plot_sed,
                marker="o",
                linestyle="none",
                mew=0,
                ms=2,
                alpha=0.1,
                label='SD %s' % (bin_label[n]),
            )

            bax.tick_params(axis="both", which="major")
            bax.set_xlabel("log " + filt)
            bax.set_ylabel(r"Bias ($\mu$/F)")
            leg = bax.legend(loc='lower right', markerscale=3)
            for lh in leg.legendHandles:
                lh._legmarker.set_alpha(1)

            # error
            eax = ax[1, f]
            eax.plot(
                np.log10(plot_sed),
                plot_err / plot_sed,
                marker="o",
                linestyle="none",
                mew=0,
                ms=2,
                alpha=0.1,
            )

            eax.tick_params(axis="both", which="major")
            eax.set_xlabel("log " + filt)
            eax.set_ylabel(r"Error ($\sigma$/F)")

            # completeness
            cax = ax[2, f]
            cax.plot(
                np.log10(plot_sed),
                plot_compl,
                marker="o",
                linestyle="none",
                mew=0,
                ms=2,
                alpha=0.1,
            )

            cax.tick_params(axis="both", which="major")
            cax.set_xlabel("log " + filt)
            cax.set_ylabel(r"Completeness")

    plt.tight_layout()

    fig.savefig(plot_file, dpi=300)
    plt.close(fig)
Пример #17
0
        "trimfile", help="file with modelgrid, obsfiles, filebase to use"
    )
    args = parser.parse_args()

    start_time = time.clock()

    # read in trim file
    f = open(args.trimfile, "r")
    file_lines = list(f)

    # physics model grid name
    modelfile = file_lines[0].rstrip()

    # get the modesedgrid on which to generate the noisemodel
    print("Reading the model grid files = ", modelfile)
    modelsedgrid = SEDGrid(modelfile)

    new_time = time.clock()
    print("time to read: ", (new_time - start_time) / 60.0, " min")

    old_noisefile = ""
    for k in range(1, len(file_lines)):

        print("\n\n")

        # file names
        noisefile, obsfile, filebase = file_lines[k].split()

        # make sure the proper directories exist
        if not os.path.isdir(os.path.dirname(filebase)):
            os.makedirs(os.path.dirname(filebase))
Пример #18
0
def make_extinguished_sed_grid(
    project,
    specgrid,
    filters,
    av=[0.0, 5, 0.1],
    rv=[0.0, 5, 0.2],
    fA=None,
    av_prior_model={"name": "flat"},
    rv_prior_model={"name": "flat"},
    fA_prior_model={"name": "flat"},
    extLaw=None,
    add_spectral_properties_kwargs=None,
    absflux_cov=False,
    verbose=True,
    seds_fname=None,
    filterLib=None,
    info_fname=None,
    **kwargs,
):
    """
    Create SED model grid integrated with filters and dust extinguished

    Parameters
    ----------
    project: str
        project name

    specgrid: SpectralGrid object
        spectral grid to transform

    filters: sequence
        ordered sequence of filters to use to extract the photometry
        filter names are the full names in core.filters

    av: sequence
        sequence of Av values to sample

    av_prior_model: dict
        dict including prior model name and parameters

    rv: sequence
        sequence of Rv values to sample

    rv_prior_model: dict
        dict including prior model name and parameters

    fA: sequence (optional)
        sequence of fA values to sample (depending on extLaw definition)

    fA_prior_model: dict
        dict including prior model name and parameters

    extLaw: extinction.ExtLaw
        extinction law to use during the process

    add_spectral_properties_kwargs: dict
        keyword arguments to call :func:`add_spectral_properties`
        to add model properties from the spectra into the grid property table

    asbflux_cov: boolean
        set to calculate the absflux covariance matrices for each model
        (can be very slow!!!  But it is the right thing to do)

    seds_fname: str
        full filename to save the sed grid into

    filterLib:  str
        full filename to the filter library hd5 file

    info_fname : str
        Set to specify the filename to save beast info to, otherwise
        saved to project/project_beast_info.asdf

    Returns
    -------
    fname: str
       name of saved file

    g: SpectralGrid object
        spectral grid to transform
    """

    # create the dust grid arrays
    avs = np.arange(av[0], av[1] + 0.5 * av[2], av[2])
    rvs = np.arange(rv[0], rv[1] + 0.5 * rv[2], rv[2])
    if fA is not None:
        fAs = np.arange(fA[0], fA[1] + 0.5 * fA[2], fA[2])
    else:
        fAs = [1.0]

    # create SED file name if needed
    if seds_fname is None:
        seds_fname = "%s/%s_seds.grid.hd5" % (project, project)

    # generate extinguished grids if SED file doesn't exist
    if not os.path.isfile(seds_fname):

        extLaw = extLaw or extinction.Cardelli()

        if verbose:
            print("Make SEDS")

        if fA is not None:
            g = creategrid.make_extinguished_grid(
                specgrid,
                filters,
                extLaw,
                avs,
                rvs,
                fAs,
                av_prior_model=av_prior_model,
                rv_prior_model=rv_prior_model,
                fA_prior_model=fA_prior_model,
                add_spectral_properties_kwargs=add_spectral_properties_kwargs,
                absflux_cov=absflux_cov,
                filterLib=filterLib,
            )
        else:
            g = creategrid.make_extinguished_grid(
                specgrid,
                filters,
                extLaw,
                avs,
                rvs,
                av_prior_model=av_prior_model,
                rv_prior_model=rv_prior_model,
                add_spectral_properties_kwargs=add_spectral_properties_kwargs,
                absflux_cov=absflux_cov,
            )

        # write to disk
        if hasattr(g, "write"):
            g.write(seds_fname)
        else:
            for gk in g:
                gk.write(seds_fname, append=True)

    # save info to the beast info file
    info = {
        "av_input": av,
        "rv_input": rv,
        "fA_input": fA,
        "avs": avs,
        "rvs": rvs,
        "fAs": fAs,
        "av_prior_model": av_prior_model,
        "rv_prior_model": rv_prior_model,
        "fA_prior_model": fA_prior_model,
    }
    if info_fname is None:
        info_fname = f"{project}/{project}_beast_info.asdf"
    add_to_beast_info_file(info_fname, info)

    g = SEDGrid(seds_fname, backend="memory")

    return (seds_fname, g)
Пример #19
0
def subgrid_info(grid_fname, noise_fname=None):
    """
    Generates a list of mins and maxes of all the quantities in the given grid

    Parameters
    ----------
    grid_fname: string
        path to a beast grid file (hd5 format)

    noise_fname: string
        Path to the noise model file for the given grid (hd5 format)
        (optional). If this is given, the mins/maxes for the full model
        fluxes are added too, under the name 'log'+filter+'_wd_bias'
        (needs to conform to the name used in fit.py).

    Returns
    -------
    info_dict: dictionary
        {name of quantity [string]: {'min': min, 'max': max, 'unique': unique values}}
    """

    # Use the disk backend to minimize the memory usage
    sedgrid = SEDGrid(grid_fname, backend="disk")
    seds = sedgrid.seds

    info_dict = {}

    qnames = sedgrid.keys()
    for q in qnames:
        qvals = sedgrid[q]
        qmin = np.amin(qvals)
        qmax = np.amax(qvals)
        qunique = np.unique(qvals)
        info_dict[q] = {}
        info_dict[q]["min"] = qmin
        info_dict[q]["max"] = qmax
        info_dict[q]["unique"] = qunique

    if noise_fname is not None:
        noisemodel = get_noisemodelcat(noise_fname)

        # The following is also in fit.py, so we're kind of doing double
        # work here, but it's necessary if we want to know the proper
        # ranges for these values.
        full_model_flux = seds[:] + noisemodel["bias"]
        logtempseds = np.array(full_model_flux)
        full_model_flux = (np.sign(logtempseds) *
                           np.log1p(np.abs(logtempseds * math.log(10))) /
                           math.log(10))

        filters = sedgrid.filters
        for i, f in enumerate(filters):
            f_fluxes = full_model_flux[:, i]
            # Be sure to cut out the -100's in the calculation of the minimum
            qmin = np.amin(f_fluxes[f_fluxes > -99.99])
            qmax = np.amax(f_fluxes)
            qunique = np.unique(qvals)

            q = "symlog" + f + "_wd_bias"
            info_dict[q] = {}
            info_dict[q]["min"] = qmin
            info_dict[q]["max"] = qmax
            info_dict[q]["unique"] = qunique

    print("Gathered grid info for {}".format(grid_fname))
    return info_dict
Пример #20
0
def plot_completeness(
    physgrid_list,
    noise_model_list,
    output_plot_filename,
    param_list=["Av", "Rv", "logA", "f_A", "M_ini", "Z", "distance"],
    compl_filter="F475W",
):
    """
    Make visualization of the completeness
    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.

    param_list : list of strings
        names of the parameters to plot

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

    output_plot_filename : string
        name of the file in which to save the output plot

    """

    n_params = len(param_list)

    # 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))
        # get list of filters
        short_filters = [
            filter.split(sep="_")[-1].upper()
            for filter in modelsedgrid.filters
        ]
        if compl_filter.upper() not in short_filters:
            raise ValueError("requested completeness filter not present")
        filter_k = short_filters.index(compl_filter.upper())
        print("Completeness from {0}".format(modelsedgrid.filters[filter_k]))

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

        # put it all into a table
        table_dict = {x: modelsedgrid[x] for x in param_list}
        table_dict["compl"] = model_compl[:, filter_k]

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

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

    # import pdb; pdb.set_trace()

    # figure
    fig = plt.figure(figsize=(4 * n_params, 4 * n_params))

    # label font sizes
    label_font = 25
    tick_font = 22

    # load in color map
    cmap = matplotlib.cm.get_cmap("magma")

    # iterate through the panels
    for i, pi in enumerate(param_list):
        for j, pj in enumerate(param_list[i:], i):

            print("plotting {0} and {1}".format(pi, pj))

            # not along diagonal
            if i != j:

                # set up subplot
                plt.subplot(n_params, n_params, i + j * (n_params) + 1)
                ax = plt.gca()

                # create image and labels
                x_col, x_bins, x_label = setup_axis(compl_table, pi)
                y_col, y_bins, y_label = setup_axis(compl_table, pj)
                compl_image, _, _, _ = binned_statistic_2d(
                    x_col,
                    y_col,
                    compl_table["compl"],
                    statistic="mean",
                    bins=(x_bins, y_bins),
                )

                # plot points
                im = plt.imshow(
                    compl_image.T,
                    # np.random.random((4,4)),
                    extent=(
                        np.min(x_bins),
                        np.max(x_bins),
                        np.min(y_bins),
                        np.max(y_bins),
                    ),
                    cmap="magma",
                    vmin=0,
                    vmax=1,
                    aspect="auto",
                    origin="lower",
                )

                ax.tick_params(
                    axis="both",
                    which="both",
                    direction="in",
                    labelsize=tick_font,
                    bottom=True,
                    top=True,
                    left=True,
                    right=True,
                )

                # axis labels and ticks
                if i == 0:
                    ax.set_ylabel(y_label, fontsize=label_font)
                    # ax.get_yaxis().set_label_coords(-0.35,0.5)
                else:
                    ax.set_yticklabels([])
                if j == n_params - 1:
                    ax.set_xlabel(x_label, fontsize=label_font)
                    plt.xticks(rotation=-45)
                else:
                    ax.set_xticklabels([])

            # along diagonal
            if i == j:

                # set up subplot
                plt.subplot(n_params, n_params, i + j * (n_params) + 1)
                ax = plt.gca()

                # create histogram and labels
                x_col, x_bins, x_label = setup_axis(compl_table, pi)
                compl_hist, _, _ = binned_statistic(
                    x_col,
                    compl_table["compl"],
                    statistic="mean",
                    bins=x_bins,
                )
                # make histogram
                _, _, patches = plt.hist(x_bins[:-1],
                                         x_bins,
                                         weights=compl_hist)
                # color each bar by its completeness
                for c, comp in enumerate(compl_hist):
                    patches[c].set_color(cmap(comp))
                    patches[c].set_linewidth = 0.1
                # make a black outline so it stands out as a histogram
                plt.hist(x_bins[:-1],
                         x_bins,
                         weights=compl_hist,
                         histtype="step",
                         color="k")
                # axis ranges
                plt.xlim(np.min(x_bins), np.max(x_bins))
                plt.ylim(0, 1.05)

                ax.tick_params(axis="y",
                               which="both",
                               length=0,
                               labelsize=tick_font)
                ax.tick_params(axis="x",
                               which="both",
                               direction="in",
                               labelsize=tick_font)

                # axis labels and ticks
                ax.set_yticklabels([])
                if i < n_params - 1:
                    ax.set_xticklabels([])
                if i == n_params - 1:
                    ax.set_xlabel(x_label, fontsize=label_font)
                    plt.xticks(rotation=-45)

    # plt.subplots_adjust(wspace=0.05, hspace=0.05)
    plt.tight_layout()

    # add a colorbar
    gs = GridSpec(nrows=20, ncols=n_params)
    cax = fig.add_subplot(gs[0, 2:])
    cbar = plt.colorbar(im, cax=cax, orientation="horizontal")
    cbar.set_label("Completeness", fontsize=label_font)
    cbar.ax.tick_params(labelsize=tick_font)
    gs.tight_layout(fig)

    fig.savefig(output_plot_filename)
    plt.close(fig)
Пример #21
0
def make_extinguished_grid(
    spec_grid,
    filter_names,
    extLaw,
    avs,
    rvs,
    fAs=None,
    av_prior_model={"name": "flat"},
    rv_prior_model={"name": "flat"},
    fA_prior_model={"name": "flat"},
    chunksize=0,
    add_spectral_properties_kwargs=None,
    absflux_cov=False,
    filterLib=None,
):
    """
    Extinguish spectra and extract an SEDGrid through given series of filters
    (all wavelengths in stellar SEDs and filter response functions are assumed
    to be in Angstroms)

    Parameters
    ----------
    spec_grid: string or grid.SpectralGrid
        if string:
        spec_grid is the filename to the grid file with stellar spectra
        the backend to load this grid will be the minimal invasive: 'HDF'
        if possible, 'cache' otherwise.

        if not a string, expecting the corresponding SpectralGrid instance
        (backend already setup)

    filter_names: list
        list of filter names according to the filter lib

    Avs: sequence
        Av values to iterate over

    av_prior_model: list
        list including prior model name and parameters

    Rvs: sequence
        Rv values to iterate over

    rv_prior_model: list
        list including prior model name and parameters

    fAs: sequence (optional)
        f_A values to iterate over
        f_A can be omitted if the extinction Law does not use it or allow
        fixed values

    fA_prior_model: list
        list including prior model name and parameters

    chunksize: int, optional (default=0)
        number of extinction model variations to generate at each cycle.
        Note that this means len(spec_grid * chunksize)
        If default <= 0, all models will be returned at once.

    filterLib:  str
        full filename to the filter library hd5 file

    add_spectral_properties_kwargs: dict
        keyword arguments to call :func:`add_spectral_properties` at each
        iteration to add model properties from the spectra into the grid
        property table

    asbflux_cov: boolean
        set to calculate the absflux covariance matrices for each model
        (can be very slow!!!  But it is the right thing to do)

    Returns
    -------
    g: grid.SpectralGrid
        final grid of reddened SEDs and models
    """
    # Check inputs
    # ============
    # get the stellar grid (no dust yet)
    # if string is provided try to load the most memory efficient backend
    # otherwise use a cache-type backend (load only when needed)
    if isinstance(spec_grid, str):
        ext = spec_grid.split(".")[-1]
        if ext in ["hdf", "hd5", "hdf5"]:
            g0 = SpectralGrid(spec_grid, backend="disk")
        else:
            g0 = SpectralGrid(spec_grid, backend="cache")
    else:
        helpers.type_checker("spec_grid", spec_grid, SpectralGrid)
        g0 = spec_grid

    # Tag fA usage
    if fAs is None:
        with_fA = False
    else:
        with_fA = True

    # get the min/max R(V) values necessary for the grid point definition
    min_Rv = min(rvs)
    max_Rv = max(rvs)

    # Create the sampling mesh
    # ========================
    # basically the dot product from all input 1d vectors
    # setup interation over the full dust parameter grid
    if with_fA:
        dustpriors = PriorWeightsDust(avs, av_prior_model, rvs, rv_prior_model,
                                      fAs, fA_prior_model)

        it = np.nditer(np.ix_(avs, rvs, fAs))
        niter = np.size(avs) * np.size(rvs) * np.size(fAs)
        npts, pts = _make_dust_fA_valid_points_generator(it, min_Rv, max_Rv)

        # Pet the user
        print("""number of initially requested points = {0:d}
              number of valid points = {1:d} (based on restrictions in R(V)
                 versus f_A plane)
              """.format(niter, npts))

        if npts == 0:
            raise AttributeError("No valid points")
    else:
        dustpriors = PriorWeightsDust(avs, av_prior_model, rvs, rv_prior_model,
                                      [1.0], fA_prior_model)

        it = np.nditer(np.ix_(avs, rvs))
        npts = np.size(avs) * np.size(rvs)
        pts = ((float(ak), float(rk)) for ak, rk in it)

    # Generate the Grid
    # =================
    N0 = len(g0.grid)
    N = N0 * npts

    if chunksize <= 0:
        print("Generating a final grid of {0:d} points".format(N))
    else:
        print("Generating a final grid of {0:d} points in {1:d} pieces".format(
            N, int(float(N0) / chunksize + 1.0)))

    if chunksize <= 0:
        chunksize = npts

    if add_spectral_properties_kwargs is not None:
        nameformat = add_spectral_properties_kwargs.pop("nameformat",
                                                        "{0:s}") + "_wd"

    for chunk_pts in helpers.chunks(pts, chunksize):
        # iter over chunks of models

        # setup chunk outputs
        cols = {"Av": np.zeros(N, dtype=float), "Rv": np.zeros(N, dtype=float)}

        if with_fA:
            cols["Rv_A"] = np.zeros(N, dtype=float)
            cols["f_A"] = np.zeros(N, dtype=float)

        keys = list(g0.keys())
        for key in keys:
            cols[key] = np.zeros(N, dtype=float)

        n_filters = len(filter_names)
        _seds = np.zeros((N, n_filters), dtype=float)
        if absflux_cov:
            n_offdiag = ((n_filters**2) - n_filters) / 2
            _cov_diag = np.zeros((N, n_filters), dtype=float)
            _cov_offdiag = np.zeros((N, n_offdiag), dtype=float)

        for count, pt in enumerate(tqdm(chunk_pts, desc="SED grid")):

            if with_fA:
                Av, Rv, f_A = pt
                dust_prior_weight = dustpriors.get_weight(Av, Rv, f_A)
                Rv_MW = extLaw.get_Rv_A(Rv, f_A)
                r = g0.applyExtinctionLaw(extLaw,
                                          Av=Av,
                                          Rv=Rv,
                                          f_A=f_A,
                                          inplace=False)
                # add extra "spectral bands" if requested
                if add_spectral_properties_kwargs is not None:
                    r = add_spectral_properties(
                        r,
                        nameformat=nameformat,
                        filterLib=filterLib,
                        **add_spectral_properties_kwargs)
                temp_results = r.getSEDs(filter_names, filterLib=filterLib)
                # adding the dust parameters to the models
                cols["Av"][N0 * count:N0 * (count + 1)] = Av
                cols["Rv"][N0 * count:N0 * (count + 1)] = Rv
                cols["f_A"][N0 * count:N0 * (count + 1)] = f_A
                cols["Rv_A"][N0 * count:N0 * (count + 1)] = Rv_MW

            else:
                Av, Rv = pt
                dust_prior_weight = dustpriors.get_weight(Av, Rv, 1.0)
                r = g0.applyExtinctionLaw(extLaw, Av=Av, Rv=Rv, inplace=False)

                if add_spectral_properties_kwargs is not None:
                    r = add_spectral_properties(
                        r,
                        nameformat=nameformat,
                        filterLib=filterLib,
                        **add_spectral_properties_kwargs)
                temp_results = r.getSEDs(filter_names, filterLib=filterLib)
                # adding the dust parameters to the models
                cols["Av"][N0 * count:N0 * (count + 1)] = Av
                cols["Rv"][N0 * count:N0 * (count + 1)] = Rv

            # get new attributes if exist
            for key in list(temp_results.grid.keys()):
                if key not in keys:
                    k1 = N0 * count
                    k2 = N0 * (count + 1)
                    cols.setdefault(key, np.zeros(
                        N, dtype=float))[k1:k2] = temp_results.grid[key]

            # compute the fractional absflux covariance matrices
            if absflux_cov:
                absflux_covmats = calc_absflux_cov_matrices(
                    r, temp_results, filter_names)
                _cov_diag[N0 * count:N0 * (count + 1)] = absflux_covmats[0]
                _cov_offdiag[N0 * count:N0 * (count + 1)] = absflux_covmats[1]

            # assign the extinguished SEDs to the output object
            _seds[N0 * count:N0 * (count + 1)] = temp_results.seds[:]

            # copy the rest of the parameters
            for key in keys:
                cols[key][N0 * count:N0 * (count + 1)] = g0.grid[key]

            # multiply existing prior weights by the dust prior weight
            cols["weight"][N0 * count:N0 * (count + 1)] *= dust_prior_weight
            cols["prior_weight"][N0 * count:N0 *
                                 (count + 1)] *= dust_prior_weight

            if count == 0:
                cols["lamb"] = temp_results.lamb[:]

        _lamb = cols.pop("lamb")

        # free the memory of temp_results
        # del temp_results
        # del tempgrid

        # Ship
        if absflux_cov:
            g = SEDGrid(
                _lamb,
                seds=_seds,
                cov_diag=_cov_diag,
                cov_offdiag=_cov_offdiag,
                grid=Table(cols),
                backend="memory",
            )
        else:
            g = SEDGrid(_lamb, seds=_seds, grid=Table(cols), backend="memory")

        g.header["filters"] = " ".join(filter_names)

        yield g
Пример #22
0
def test_grid_warnings():
    with pytest.raises(ValueError) as exc:
        SEDGrid(backend="hdf")
    assert exc.value.args[0] == "hdf backend not supported"

    with pytest.raises(ValueError) as exc:
        SEDGrid("test.txt")
    assert exc.value.args[0] == "txt file type not supported"

    # define grid contents
    n_bands = 3
    # filter_names = ["BAND1", "BAND2", "BAND3"]
    n_models = 100
    lamb = [1.0, 2.0, 3.0]
    seds = np.zeros((n_models, n_bands))
    # cov_diag = np.full((n_models, n_bands), 0.1)
    # n_offdiag = ((n_bands ** 2) - n_bands) // 2
    # cov_offdiag = np.full((n_models, n_offdiag), 1.0)
    cols = {"Av": [1.0, 1.1, 1.3], "Rv": [2.0, 3.0, 4.0]}
    header = {"Origin": "test_code"}
    gtable = Table(cols)
    gtable.meta = header

    with pytest.raises(ValueError) as exc:
        SEDGrid(lamb)
    assert exc.value.args[0] == "seds or grid not passed"

    for ftype in ["fits", "hdf"]:
        with pytest.raises(ValueError) as exc:
            a = SEDGrid(lamb, seds=seds, grid=gtable)
            a.grid = cols
            a.write(f"testgridwriteerror.{ftype}")
        assert exc.value.args[0] == "Only astropy.Table are supported"

        with pytest.raises(ValueError) as exc:
            a = SEDGrid(lamb, seds=seds, grid=gtable)
            a.grid = None
            a.write(f"testgridwriteerror.{ftype}")
        assert exc.value.args[
            0] == "Full data set not specified (lamb, seds, grid)"
Пример #23
0
def test_sedgrid(cformat, cback, copygrid):
    """
    Tests of the SEDGrid class
    """
    n_bands = 3
    filter_names = ["BAND1", "BAND2", "BAND3"]
    n_models = 100
    lamb = [1.0, 2.0, 3.0]
    seds = np.zeros((n_models, n_bands))
    cov_diag = np.full((n_models, n_bands), 0.1)
    n_offdiag = ((n_bands**2) - n_bands) // 2
    cov_offdiag = np.full((n_models, n_offdiag), 1.0)
    cols = {"Av": [1.0, 1.1, 1.3], "Rv": [2.0, 3.0, 4.0]}
    header = {"Origin": "test_code"}
    gtable = Table(cols)
    gtable.meta = header

    tgrid = SEDGrid(
        lamb,
        seds=seds,
        grid=gtable,
        header=header,
        cov_diag=cov_diag,
        cov_offdiag=cov_offdiag,
        backend="memory",
    )
    tgrid.header["filters"] = " ".join(filter_names)

    # check that the grid has the expected properties
    expected_props = [
        "lamb",
        "seds",
        "cov_diag",
        "cov_offdiag",
        "grid",
        "nbytes",
        "filters",
        "header",
        "keys",
    ]
    for cprop in expected_props:
        assert hasattr(tgrid, cprop), f"missing {cprop} property"

    np.testing.assert_allclose(tgrid.lamb, lamb, err_msg="lambdas not equal")
    np.testing.assert_allclose(tgrid.seds, seds, err_msg="seds not equal")
    np.testing.assert_allclose(tgrid.cov_diag,
                               cov_diag,
                               err_msg="covdiag not equal")
    np.testing.assert_allclose(tgrid.cov_offdiag,
                               cov_offdiag,
                               err_msg="covoffdiag not equal")
    assert isinstance(tgrid.nbytes,
                      (int, np.integer)), "grid nbytes property not integer"
    compare_tables(tgrid.grid, gtable)
    assert tgrid.grid.keys() == list(cols.keys()), "colnames of grid not equal"
    assert tgrid.filters == filter_names, "filters of grid not equal"

    # test writing and reading to disk
    print(f"testing {cformat} file format")
    tfile = NamedTemporaryFile(suffix=cformat)

    # write the file
    tgrid.write(tfile.name)

    # read in the file using different backends
    if (cback == "disk") and (cformat == ".fits"):  # not supported
        return True

    print(f"    testing {cback} backend")
    dgrid_in = SEDGrid(tfile.name, backend=cback)

    # test making a copy
    print(f"    testing copygrid={copygrid}")
    if copygrid:
        dgrid = dgrid_in.copy()
    else:
        dgrid = dgrid_in
    print(dgrid)

    for cprop in expected_props:
        assert hasattr(dgrid, cprop), f"missing {cprop} property"

    # check that the grid has the expected values

    # this test is having a problem in the online travis ci
    # it someone manages to access another file with HST filter names!
    # no idea way.  Works fine offline.
    # assert dgrid.filters == filter_names, "{cformat} file filters not equal"

    assert len(dgrid) == n_bands, f"{cformat} file len not equal"

    np.testing.assert_allclose(
        dgrid.lamb, lamb, err_msg=f"{cformat} file grid lambdas not equal")
    np.testing.assert_allclose(dgrid.seds,
                               seds,
                               err_msg=f"{cformat} file grid seds not equal")
    np.testing.assert_allclose(
        dgrid.cov_diag,
        cov_diag,
        err_msg=f"{cformat} file grid cov_diag not equal",
    )
    np.testing.assert_allclose(
        dgrid.cov_offdiag,
        cov_offdiag,
        err_msg=f"{cformat} file grid cov_offdiag not equal",
    )
    assert isinstance(
        dgrid.nbytes,
        (int, np.integer)), f"{cformat} file grid nbytes property not integer"

    dTable = dgrid.grid
    if (cback == "disk") and (cformat == ".hdf"):
        dTable = read_table_hdf5(dgrid.grid)
    compare_tables(dTable, gtable, otag=f"{cformat} file")

    assert dTable.keys() == list(
        cols.keys()), f"{cformat} file colnames of grid not equal"

    assert dgrid.keys() == tgrid.keys(
    ), f"{cformat} file colnames of grid not equal"

    # final copy - needed for disk backend to get the now defined variables
    print(dgrid)

    dgrid_fin = dgrid.copy()

    print(dgrid_fin)
Пример #24
0
def plot_toothpick_details(asts_filename, seds_filename, savefig=False):
    """
    Plot the details of the toothpick noisemodel creation for each filter.
    These plots show the individual AST results as points as
    (flux_in - flux_out)/flux_in.  In addition, the binned values of these
    points are plotted giving the bias term in the observation model.
    Error bars around the binned bias values give the binned sigma term of
    the observation model.  Finally, as a separate column of plots the
    binned completeness in each filter is plotted.

    Parameters
    ----------
    asts_filename : str
        filename with the AST results

    seds_filename : str
        filename with the SED grid (used just for the filter information)

    savefig : str (default=False)
        to save the figure, set this to the file extension (e.g., 'png', 'pdf')
    """
    sedgrid = SEDGrid(seds_filename, backend="cache")

    # read in AST results
    model = toothpick.MultiFilterASTs(asts_filename, sedgrid.filters)

    # set the column mappings as the external file is BAND_VEGA or BAND_IN
    model.set_data_mappings(upcase=True,
                            in_pair=("in", "in"),
                            out_pair=("out", "rate"))

    # compute binned biases, uncertainties, and completeness as a function of band flux
    model.fit_bins(nbins=50, completeness_mag_cut=-10)

    nfilters = len(sedgrid.filters)
    fig, ax = plt.subplots(nrows=nfilters,
                           ncols=2,
                           figsize=(14, 10),
                           sharex=True)
    set_params()

    for i, cfilter in enumerate(sedgrid.filters):
        mag_in = model.data[model.filter_aliases[cfilter + "_in"]]
        flux_out = model.data[model.filter_aliases[cfilter + "_out"]]

        flux_in = (10**(-0.4 * mag_in)) * model.vega_flux[i]
        flux_out *= model.vega_flux[i]

        gvals = flux_out != 0.0

        ax[i, 0].plot(
            flux_in[gvals],
            (flux_in[gvals] - flux_out[gvals]) / flux_in[gvals],
            "ko",
            alpha=0.1,
            markersize=2,
        )

        # not all bins are filled with good data
        ngbins = model._nasts[i]

        ax[i, 0].plot(
            model._fluxes[0:ngbins, i],
            model._biases[0:ngbins, i] / model._fluxes[0:ngbins, i],
            "b-",
        )

        ax[i, 0].errorbar(
            model._fluxes[0:ngbins, i],
            model._biases[0:ngbins, i] / model._fluxes[0:ngbins, i],
            yerr=model._sigmas[0:ngbins, i] / model._fluxes[0:ngbins, i],
            fmt="bo",
            markersize=2,
            alpha=0.5,
        )

        ax[i, 0].set_ylim(-5e0, 5e0)
        ax[i, 0].set_xscale("log")
        ax[i, 0].set_ylabel(r"$(F_i - F_o)/F_i$")

        ax[i, 1].plot(
            model._fluxes[0:ngbins, i],
            model._compls[0:ngbins, i],
            "b-",
        )

        ax[i, 1].yaxis.tick_right()
        ax[i, 1].yaxis.set_label_position("right")
        ax[i, 1].set_ylim(0, 1)
        ax[i, 1].set_xscale("log")
        sfilt = cfilter.split("_")[-1]
        ax[i, 1].set_ylabel(f"C({sfilt})")

    ax[nfilters - 1, 0].set_xlabel(r"$F_i$")
    ax[nfilters - 1, 1].set_xlabel(r"$F_i$")

    # add in the zero line
    # do after all the data has been plotted to get the full x range
    pxrange = ax[0, 0].get_xlim()
    for i, cfilter in enumerate(sedgrid.filters):
        ax[i, 0].plot(pxrange, [0.0, 0.0], "k--", alpha=0.5)

    # figname
    basename = asts_filename.replace(".fits", "_plot")

    fig.tight_layout()

    # save or show fig
    if savefig:
        fig.savefig("{}.{}".format(basename, savefig))
    else:
        plt.show()
Пример #25
0
def fit_submodel(
    settings,
    photometry_file,
    modelsedgrid_file,
    noise_file,
    pdf_max_nbins,
    stats_file,
    pdf_file,
    pdf2d_file,
    pdf2d_param_list,
    lnp_file,
    grid_info_file=None,
    resume=False,
):
    """
    Code to run the SED fitting

    Parameters
    ----------
    settings : beast.tools.beast_settings.beast_settings instance
        object with the beast settings

    photometry_file : string
        path+name of the photometry file

    modelsedgrid_file : string
        path+name of the physics model grid file

    noise_file : string
        path+name of the noise model file

    pdf_max_nbins : int
        Maxiumum number of bins to use for the 1D and 2D PDFs

    stats_file : string
        path+name of the file to contain stats output

    pdf_file : string
        path+name of the file to contain 1D PDF output

    pdf2d_file : string
        path+name of the file to contain 2D PDF output

    pdf2d_param_list: list of strings or None
        parameters for which to make 2D PDFs (or None)

    lnp_file : string
        path+name of the file to contain log likelihood output

    grid_info_file : string (default=None)
        path+name for pickle file that contains dictionary with subgrid
        min/max/n_unique (required for a run with subgrids)

    resume : boolean (default=False)
        choose whether to resume existing run or start over


    Returns
    -------
    noisefile : string
        name of the created noise file

    """

    # read in the photometry catalog
    obsdata = Observations(photometry_file,
                           settings.filters,
                           obs_colnames=settings.obs_colnames)

    # check if it's a subgrid run by looking in the file name
    if "gridsub" in modelsedgrid_file:
        subgrid_run = True
        print("loading grid_info_dict from " + grid_info_file)
        with open(grid_info_file, "rb") as p:
            grid_info_dict = pickle.loads(p.read())
    else:
        subgrid_run = False

    # load the SED grid and noise model
    modelsedgrid = SEDGrid(modelsedgrid_file)
    noisemodel_vals = noisemodel.get_noisemodelcat(noise_file)

    if subgrid_run:
        fit.summary_table_memory(
            obsdata,
            noisemodel_vals,
            modelsedgrid,
            resume=resume,
            threshold=-10.0,
            save_every_npts=100,
            lnp_npts=500,
            max_nbins=pdf_max_nbins,
            stats_outname=stats_file,
            pdf1d_outname=pdf_file,
            pdf2d_outname=pdf2d_file,
            pdf2d_param_list=pdf2d_param_list,
            grid_info_dict=grid_info_dict,
            lnp_outname=lnp_file,
            do_not_normalize=True,
            surveyname=settings.surveyname,
        )
        print("Done fitting on grid " + modelsedgrid_file)

    else:

        fit.summary_table_memory(
            obsdata,
            noisemodel_vals,
            modelsedgrid,
            resume=resume,
            threshold=-10.0,
            save_every_npts=100,
            lnp_npts=500,
            max_nbins=pdf_max_nbins,
            stats_outname=stats_file,
            pdf1d_outname=pdf_file,
            pdf2d_outname=pdf2d_file,
            pdf2d_param_list=pdf2d_param_list,
            lnp_outname=lnp_file,
            surveyname=settings.surveyname,
        )
        print("Done fitting on grid " + modelsedgrid_file)
Пример #26
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)

    modelsedgrid = SEDGrid(sedgrid_fname)

    # Convert to Vega mags
    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)
    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
Пример #27
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)
Пример #28
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 = SEDGrid(sedgrid_fname)

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

    # 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
Пример #29
0
def simulate_obs(
    physgrid_list,
    noise_model_list,
    output_catalog,
    nsim=100,
    compl_filter="F475W",
    weight_to_use='weight',
    ranseed=None,
):
    """
    Wrapper for creating a simulated photometry.

    Parameters
    ----------
    physgrid_list : 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, and they will
        each be sampled nsim/len(physgrid_list) times.

    noise_model_list : 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.

    output_catalog : string
        Name of the output simulated photometry catalog

    n_sim : int (default=100)
        Number of simulated objects to create.  If nsim/len(physgrid_list) isn't
        an integer, this will be increased so that each grid has the same
        number of samples.

    compl_filter : string (default='F475W')
        filter name to use for completeness

    weight_to_use : string (default='weight')
        Set to either 'weight' (prior+grid), 'prior_weight', or 'grid_weight' to
        choose the weighting for SED selection.

    ranseed : int
        seed for random number generator

    """

    # numbers of samples to do
    # (ensure there are enough for even sampling of multiple model grids)
    n_phys = len(physgrid_list)
    samples_per_grid = int(np.ceil(nsim / n_phys))

    # list to hold all simulation tables
    simtable_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))

        # read in the noise model - includes bias, unc, and completeness
        noisegrid = noisemodel.get_noisemodelcat(str(noise_model))

        # generate the table
        simtable = gen_SimObs_from_sedgrid(
            modelsedgrid,
            noisegrid,
            nsim=samples_per_grid,
            compl_filter=compl_filter,
            weight_to_use=weight_to_use,
            ranseed=ranseed,
        )

        # append to the list
        simtable_list.append(simtable)

    # stack all the tables into one and write it out
    vstack(simtable_list).write(output_catalog, overwrite=True)
def generate_files_for_tests(run_beast=True, run_tools=True):
    """
    Use the metal_small example to generate a full set of files for the BEAST
    regression tests.

    Parameters
    ----------
    run_beast : boolean (default=True)
        if True, run the BEAST

    run_tools : boolean (default=True)
        if True, run the code to generate things for tools
    """

    # read in BEAST settings
    settings_orig = beast_settings.beast_settings("beast_settings.txt")
    # also make a version with subgrids
    settings_subgrids = copy.deepcopy(settings_orig)
    settings_subgrids.n_subgrid = 2
    settings_subgrids.project = f"{settings_orig.project}_subgrids"

    # ==========================================
    # run the beast for each set of settings
    # ==========================================

    if run_beast:

        for settings in [settings_orig, settings_subgrids]:

            # -----------------
            # physics model
            # -----------------
            create_physicsmodel.create_physicsmodel(
                settings,
                nsubs=settings.n_subgrid,
                nprocs=1,
            )

            # -----------------
            # ASTs
            # -----------------

            # currently only works for no subgrids
            if settings.n_subgrid == 1:
                make_ast_inputs.make_ast_inputs(settings,
                                                pick_method="flux_bin_method")

            # -----------------
            # obs model
            # -----------------
            create_obsmodel.create_obsmodel(
                settings,
                use_sd=False,
                nsubs=settings.n_subgrid,
                nprocs=1,
                use_rate=True,
            )

            # -----------------
            # trimming
            # -----------------

            # make file names
            file_dict = create_filenames.create_filenames(
                settings, use_sd=False, nsubs=settings.n_subgrid)

            # read in the observed data
            obsdata = Observations(settings.obsfile, settings.filters,
                                   settings.obs_colnames)

            for i in range(settings.n_subgrid):

                # get the modesedgrid on which to generate the noisemodel
                modelsedgridfile = file_dict["modelsedgrid_files"][i]
                modelsedgrid = SEDGrid(modelsedgridfile)

                # read in the noise model just created
                noisemodel_vals = noisemodel.get_noisemodelcat(
                    file_dict["noise_files"][i])

                # trim the model sedgrid
                sed_trimname = file_dict["modelsedgrid_trim_files"][i]
                noisemodel_trimname = file_dict["noise_trim_files"][i]

                trim_grid.trim_models(
                    modelsedgrid,
                    noisemodel_vals,
                    obsdata,
                    sed_trimname,
                    noisemodel_trimname,
                    sigma_fac=3.0,
                )

            # -----------------
            # fitting
            # -----------------

            run_fitting.run_fitting(
                settings,
                use_sd=False,
                nsubs=settings.n_subgrid,
                nprocs=1,
                pdf2d_param_list=["Av", "M_ini", "logT"],
                pdf_max_nbins=200,
            )

            # -----------------
            # merging
            # -----------------

            # it'll automatically skip for no subgrids
            merge_files.merge_files(settings,
                                    use_sd=False,
                                    nsubs=settings.n_subgrid)

            print("\n\n")

    # ==========================================
    # reference files for assorted tools
    # ==========================================

    if run_tools:

        # -----------------
        # compare_spec_type
        # -----------------

        # the input settings
        input = {
            "spec_ra": [72.67213351],
            "spec_dec": [-67.71720515],
            "spec_type": ["A"],
            "spec_subtype": [0],
            "lumin_class": ["IV"],
            "match_radius": 0.2,
        }

        # run it
        output = compare_spec_type.compare_spec_type(
            settings_orig.obsfile,
            "{0}/{0}_stats.fits".format(settings_orig.project),
            **input,
        )

        # save the inputs and outputs
        asdf.AsdfFile({
            "input": input,
            "output": output
        }).write_to("{0}/{0}_compare_spec_type.asdf".format(
            settings_orig.project))

        # -----------------
        # star_type_probability
        # -----------------

        # input settings
        input = {
            "output_filebase": None,
            "ext_O_star_params": {
                "min_M_ini": 10,
                "min_Av": 0.5,
                "max_Av": 5
            },
        }

        # run it
        output = star_type_probability.star_type_probability(
            "{0}/{0}_pdf1d.fits".format(settings_orig.project),
            "{0}/{0}_pdf2d.fits".format(settings_orig.project),
            **input,
        )

        # save the inputs and outputs
        asdf.AsdfFile({
            "input": input,
            "output": output
        }).write_to("{0}/{0}_star_type_probability.asdf".format(
            settings_orig.project))

    # ==========================================
    # asdf file permissions
    # ==========================================

    # for unknown reasons, asdf currently writes files with permissions set
    # to -rw-------.  This changes it to -rw-r--r-- (like the rest of the
    # BEAST files) so Karl can easily copy them over to the cached file
    # website.

    # list of asdf files
    asdf_files = glob.glob("*/*.asdf")
    # go through each one to change permissions
    for fname in asdf_files:
        os.chmod(fname,
                 stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)