Beispiel #1
0
 def wrapper(*args, **kwargs):
     e_message = "DEBUGGING ASSISTANT: make sure the parameters_dict contains " \
         "all the necessary parameters spelt correctly. " \
         "Accepted parameters are: 'tau', 'T0', 'constant', 'alpha', 'beta'"
     try:
         func(*args, **kwargs)
     except KeyError as e:
         raise HokiKeyError(f"{e} has not been defined and I need it. " +
                            e_message)
Beispiel #2
0
    def __init__(self,
                 input_folder,
                 output_folder,
                 imf,
                 binary=True,
                 verbose=False):
        if verbose:
            _print_welcome()

        # Check population type
        star = "bin" if binary else "sin"

        # check IMF key
        if imf not in BPASS_IMFS:
            raise HokiKeyError(
                f"{imf} is not a BPASS IMF. Please select a correct IMF.")

        # Setup the numpy output
        output = np.empty(self._shape(), dtype=np.float64)

        # loop over all the metallicities and load all the spectra
        for num, metallicity in enumerate(BPASS_METALLICITIES):
            print_progress_bar(num, 12)
            # Check if file exists
            assert isfile(f"{input_folder}/spectra-{star}-{imf}.{metallicity}.dat"),\
                   "HOKI ERROR: This file does not exist, or its path is incorrect."
            output[num] = self._load_single(
                f"{input_folder}/{self._input_name()}-{star}-{imf}.{metallicity}.dat"
            )

        np.save(f"{output_folder}/{self._output_name()}-{star}-{imf}", output)
        # pickle the datafile
        self.output = output
        print(
            f"Compiled data stored in {output_folder} as '{self._output_name()}-{star}-{imf}.npy'"
        )
        if verbose:
            _print_exit_message()
        return
Beispiel #3
0
    def make(self, mag_filter, col_filters):
        """
        Make the CMD - a.k.a fill the grid

        Notes
        ------
            - This may take a few seconds to a minute to run.
            - The colour will be mag_filter - filter2
            - If you later call CMD.plot() you will obtain a contour plot of mag_filter against mag_filter-filter2

        Parameters
        ----------
        mag_filter : str
            Magnitude Axis Filter
        col_filters : list of 2 str
            The two filters for the colour. The colours will be col_filter[0]-col_filter[1].

        Returns
        -------
        None
        """

        self.grid = np.zeros(
            (len(BPASS_TIME_BINS), len(self.mag_range), len(self.col_range)))

        self.mag_filter = str(mag_filter)
        # self.filter2 = str(filter2)

        # check list and that list is len(2)
        try:
            self.col_filter1, self.col_filter2 = str(col_filters[0]), str(
                col_filters[1])
        except TypeError as e:
            err_m = 'Python said: ' + str(
                e
            ) + '\nDEBUGGING ASSISTANT: col_filter must be a list or tuple of 2 strings'
            raise HokiFormatError(err_m)

        assert os.path.isdir(MODELS_PATH), f"DEBUGGING ASSISTANT: Directory MODELS_PATH = {MODELS_PATH} not found.\n " \
                                           f"You can change MODELS_PATH by hoki.constants.set_models_path([MYPATH])." \
                                           f"If you've just done that and it looks like it didn't work, try to restart " \
                                           f"your notebook or terminal ;). "

        self.path = MODELS_PATH

        # FIND THE KEYS TO THE COLUMNS OF INTEREST IN DUMMY

        col_keys = [
            'timestep', 'age', self.col_filter1, self.col_filter2, 'M1',
            'log(R1)', 'log(L1)'
        ]

        try:
            cols = tuple([self.dummy_dict[key] for key in col_keys])
        except KeyError as e:
            err_m='Python said: '+str(e)+'\nDEBUGGING ASSISTANT: \nOne or both of the chosen filters do not correspond ' \
                                         'to a valid filter key. Here is a list of valid filters - ' \
                                         'input them as string:\n'+str(list(self.dummy_dict.keys())[49:-23])
            raise HokiKeyError(err_m)

        # LOOPING OVER EACH LINE IN THE INPUT FILE
        for filename, model_imf, mixed_imf, mixed_age, model_type in zip(
                self.bpass_input.filenames, self.bpass_input.model_imf,
                self.bpass_input.mixed_imf, self.bpass_input.mixed_age,
                self.bpass_input.types):

            # PRE PROCESSING THE MODEL INPUTS

            # The model_imf and mixed_imf have different precisions because of the FORTRAN code
            # model_imf is double precision but mixed_imf is double precision. We round to take care of that.
            model_imf = round(model_imf, 6)
            mixed_imf = round(mixed_imf, 6)

            # some mixed ages come out negative - it should be taken into account by setting them to 0
            if mixed_age == np.inf:
                mixed_age = 0

            # LOADING THE DATA FILE
            # Making sure it exists - If not keep the name in a list
            try:
                self._my_data = np.loadtxt(self.path + filename,
                                           unpack=True,
                                           usecols=cols)
            except (FileNotFoundError, OSError):
                self._file_does_not_exist.append(filename)
                continue

            # MAKING THE COLOUR
            try:
                colours = [
                    filt1 - filt2
                    for filt1, filt2 in zip(self._my_data[2], self._my_data[3])
                ]
            except TypeError:
                # Sometimes there is only one row - i.e. the star did not evolve.
                # Then the zip will fail - These are stars that have not evolved and there is
                # very few of them so we are skipping them for now.
                continue

            # LIST WHICH BINS IN THE GRID EACH COLOUR AND MAGNITUDE BELONGS TO
            self._col_bins = [
                np.abs((self.col_range - c)).argmin() if self.col_range[np.abs(
                    (self.col_range - c)).argmin()] <= c else np.abs(
                        (self.col_range - c)).argmin() - 1 for c in colours
            ]

            self._mag_bins = [
                np.abs(
                    (self.mag_range - mag)).argmin() if self.mag_range[np.abs(
                        (self.mag_range - mag)).argmin()] <= mag else np.abs(
                            (self.mag_range - mag)).argmin() - 1
                for mag in self._my_data[3]
            ]

            # MIXED AGE = 0.0 OR NAN CASE (i.e. no rejuvination)
            if np.isnan(mixed_age) or float(mixed_age) == 0.0:
                self._ages = self._my_data[1]
                # first line is always zero and will mess up the log so we take care of that
                self._log_ages = np.concatenate(
                    (np.array([0]), np.log10(self._my_data[1, 1:])))
                self._log_ages = [
                    age if age >= 6.0 else 6.0 for age in self._log_ages
                ]
                self._fill_grid_with(model_imf, model_type)

            # MIXED AGE NON ZERO CASE (i.e. rejuvination has occured)
            else:
                # MODEL IMF = MIXED IMF (These models only occur after rejuvination)
                if np.isclose(model_imf, mixed_imf, atol=1e-05):
                    self._ages = self._my_data[1] + mixed_age
                    self._log_ages = np.log10(self._my_data[1] + mixed_age)
                    self._fill_grid_with(mixed_imf, model_type)

                #  MODEL INF != MIXED IMF (These can occur with or without rejuvination)
                else:
                    # NON REJUVINATED MODELS
                    self._ages = self._my_data[1]
                    self._log_ages = np.concatenate(
                        (np.array([0]), np.log10(self._my_data[1, 1:])))
                    self._log_ages = [
                        age if age >= 6.0 else 6.0 for age in self._log_ages
                    ]
                    self._fill_grid_with(model_imf - mixed_imf, model_type)

                    # REJUVINATED MODELS
                    self._ages = self._my_data[1] + mixed_age
                    self._log_ages = np.log10(self._my_data[1] + mixed_age)
                    self._fill_grid_with(mixed_imf, model_type)
Beispiel #4
0
def emissivities_all_z(data_path, imf, binary=True):
    """
    Load all BPASS emissivities from files.

    Notes
    -----
    The first time this function is run on a folder it will generate an npy
    file containing all the BPASS emissivities for faster loading in the
    future. It stores the file in the same folder with the name:
    `all_ionizing-[bin/sin]-[imf].npy`.

    The emissivities are just read from file and not normalised.


    Input
    -----
    data_path : `str`
        The path to the folder containing the BPASS files.

    binary : `bool`
        Use the binary files or just the single stars. Default=True

    imf : `str`
        BPASS Identifier of the IMF to be used.
        The accepted IMF identifiers are:
        - `"imf_chab100"`
        - `"imf_chab300"`
        - `"imf100_100"`
        - `"imf100_300"`
        - `"imf135_100"`
        - `"imf135_300"`
        - `"imfall_300"`
        - `"imf170_100"`
        - `"imf170_300"`

    Returns
    -------
    emissivities : `numpy.ndarray` (N_Z, N_age, 4) [(metallicity, log_age, band)]
        A 3D numpy array containing all the BPASS emissivities (Nion [1/s],
        L_Halpha [ergs/s], L_FUV [ergs/s/A], L_NUV [ergs/s/A]) for a specific
        imf and binary or single star population.
        Usage: spectra[1][2][0]
                (gives Nion for Z=0.0001 and log_age=6.2)
    """
    # Check population type
    star = "bin" if binary else "sin"

    # check IMF key
    if imf not in BPASS_IMFS:
        raise HokiKeyError(
            f"{imf} is not a BPASS IMF. Please select a correct IMF.")

    # check if data_path is a string
    if not isinstance(data_path, str):
        raise HokiTypeError("The folder location is expected to be a string.")

    # Check if compiled spectra are already present in data folder
    if os.path.isfile(f"{data_path}/all_ionizing-{star}-{imf}.npy"):
        print("Load precompiled file.")
        emissivities = np.load(f"{data_path}/all_ionizing-{star}-{imf}.npy")
        print("Done Loading.")

    # Compile the spectra for faster reading next time otherwise
    else:
        print("Compiled file not found. Data will be compiled.")
        res = hoki.data_compilers.EmissivityCompiler(data_path,
                                                     data_path,
                                                     imf,
                                                     binary=binary)
        emissivities = res.output
    return emissivities
Beispiel #5
0
def spectra_all_z(data_path, imf, binary=True):
    """
    Load all BPASS spectra from files.

    Notes
    -----
    The first time this function is ran on a folder it will generate a pickle
    file containing all the BPASS spectra per metallicity for faster loading
    in the future. It stores the file in the same folder with the name:
    `all_spectra-[bin/sin]-[imf].pkl`

    The spectra are just read from file and not normalised.


    Input
    -----
    data_path : `str`
        The path to the folder containing the BPASS spectra.

    binary : `bool`
        Use the binary files or just the single stars. Default=True

    imf : `str`
        BPASS Identifier of the IMF to be used.
        The accepted IMF identifiers are:
        - `"imf_chab100"`
        - `"imf_chab300"`
        - `"imf100_100"`
        - `"imf100_300"`
        - `"imf135_100"`
        - `"imf135_300"`
        - `"imfall_300"`
        - `"imf170_100"`
        - `"imf170_300"`


    Returns
    -------
    spectra : `numpy.ndarray` (13, 51, 100000) [(metallicity, log_age, wavelength)]
        A 3D numpy array containing all the BPASS spectra for a specific imf
        and binary or single star population.
        Usage: spectra[1][2][1000]
                (gives L_\\odot for Z=0.0001 and log_age=6.2 at 999 Angstrom)

    """
    # Check population type
    star = "bin" if binary else "sin"

    # check IMF key
    if imf not in BPASS_IMFS:
        raise HokiKeyError(
            f"{imf} is not a BPASS IMF. Please select a correct IMF.")

    # check if data_path is a string
    if not isinstance(data_path, str):
        raise HokiTypeError("The folder location is expected to be a string.")

    # check if compiled file exists
    if os.path.isfile(f"{data_path}/all_spectra-{star}-{imf}.npy"):
        print("Loading precompiled file.")
        spectra = np.load(f"{data_path}/all_spectra-{star}-{imf}.npy")
        print("Done Loading.")
    # Otherwise compile
    else:
        print("Compiled file not found. Data will be compiled")
        spec = hoki.data_compilers.SpectraCompiler(data_path,
                                                   data_path,
                                                   imf,
                                                   binary=binary)
        spectra = spec.output
    return spectra
Beispiel #6
0
def rates_all_z(data_path, imf, binary=True):
    """
    Loads the BPASS supernova event files.

    Notes
    -----
    The rates are just read from file and not normalised.

    Input
    -----
    data_path : `str`
        The filepath to the folder containing the BPASS data

    binary : `bool`
        Use the binary files or just the single stars. Default=True

    imf : `str`
        BPASS Identifier of the IMF to be used.
        The accepted IMF identifiers are:
        - `"imf_chab100"`
        - `"imf_chab300"`
        - `"imf100_100"`
        - `"imf100_300"`
        - `"imf135_100"`
        - `"imf135_300"`
        - `"imfall_300"`
        - `"imf170_100"`
        - `"imf170_300"`

    Returns
    -------
    `pandas.DataFrame` (51, (8,13)) (log_age, (event_types, metallicity)
        A pandas MultiIndex dataframe containing the BPASS number of events
        per metallicity per type.
        Usage:   rates.loc[log_age, (type, metallicity)]
        Example: rates.loc[6.5, ("Ia", 0.02)]


        Notes
        -----
        This dataframe has the following structure.
        The index is the log_age as a float.
        The column is a `pandas.MultiIndex` with the event types
        (level=0, `float`) and the metallicity (level=1, `float`)

        |Event Type | Ia      | IIP      |  ... | PISNe | low_mass |
        |Metallicity| 0.00001 | 0.00001  |  ... |  0.04 |    0.04  |
        | log_age   |---------|----------|------|-------|----------|
        |    6.0    |
        |    ...    |                  Event Rate values
        |    11.0   |

    """

    # Check population type
    star = "bin" if binary else "sin"

    # Check if the given IMF is in the accepted IMFs
    if imf not in BPASS_IMFS:
        raise HokiKeyError(
            f"{imf} is not a BPASS IMF. Please select a correct IMF.\n"\
            "These can be found in the documentation of this function.")

    # Create the output DataFrame
    arrays = [BPASS_NUM_METALLICITIES, BPASS_EVENT_TYPES]
    columns = pd.MultiIndex.from_product(arrays,
                                         names=["Metallicicty", "Event Type"])

    rates = pd.DataFrame(index=BPASS_TIME_BINS,
                         columns=columns,
                         dtype=np.float64)
    rates.index.name = "log_age"

    # load supernova count files
    for num, metallicity in enumerate(BPASS_METALLICITIES):
        data = model_output(
            f"{data_path}/supernova-{star}-{imf}.{metallicity}.dat")
        data = data.loc[:, slice(BPASS_EVENT_TYPES[0], BPASS_EVENT_TYPES[-1])]

        rates.loc[:, (BPASS_NUM_METALLICITIES[num],
                      slice(None))] = data.to_numpy()

    # swap metallicity and event type
    return rates.swaplevel(0, 1, axis=1)