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)
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
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)
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
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
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)