def get_spectrum(self, model_param: Dict[str, Union[float, List[float]]], spec_res: float, smooth: bool = False) -> box.ModelBox: """ Function for calculating a Planck spectrum or a combination of multiple Planck spectra. Parameters ---------- model_param : dict Dictionary with the 'teff' (K), 'radius' (Rjup), and 'distance' (pc). The values of 'teff' and 'radius' can be a single float, or a list with floats for a combination of multiple Planck functions, e.g. {'teff': [1500., 1000.], 'radius': [1., 2.], 'distance': 10.}. spec_res : float Spectral resolution. smooth : bool Returns ------- species.core.box.ModelBox Box with the Planck spectrum. """ if 'teff' in model_param and isinstance(model_param['teff'], list): model_param = self.update_parameters(model_param) wavel_points = read_util.create_wavelengths(self.wavel_range, spec_res) n_planck = 0 for item in model_param: if item[:4] == 'teff': n_planck += 1 if n_planck == 1: if 'radius' in model_param and 'distance' in model_param: scaling = ((model_param['radius']*constants.R_JUP) / (model_param['distance']*constants.PARSEC))**2 else: scaling = 1. flux = self.planck(wavel_points, model_param['teff'], scaling) # (W m-2 um-1) else: flux = np.zeros(wavel_points.shape) for i in range(n_planck): if f'radius_{i}' in model_param and 'distance' in model_param: scaling = ((model_param[f'radius_{i}']*constants.R_JUP) / (model_param['distance']*constants.PARSEC))**2 else: scaling = 1. flux += self.planck(wavel_points, model_param[f'teff_{i}'], scaling) # (W m-2 um-1) if smooth: flux = read_util.smooth_spectrum(wavel_points, flux, spec_res) model_box = box.create_box(boxtype='model', model='planck', wavelength=wavel_points, flux=flux, parameters=model_param, quantity='flux') if n_planck == 1 and 'radius' in model_param: model_box.parameters['luminosity'] = 4. * np.pi * ( model_box.parameters['radius'] * constants.R_JUP)**2 * constants.SIGMA_SB * \ model_box.parameters['teff']**4. / constants.L_SUN # (Lsun) elif n_planck > 1: lum_total = 0. for i in range(n_planck): if f'radius_{i}' in model_box.parameters: # Add up the luminosity of the blackbody components (Lsun) surface = 4. * np.pi * (model_box.parameters[f'radius_{i}']*constants.R_JUP)**2 lum_total += surface * constants.SIGMA_SB * \ model_box.parameters[f'teff_{i}']**4. / constants.L_SUN if lum_total > 0.: model_box.parameters['luminosity'] = lum_total return model_box
def add_btsettl(input_path: str, database: h5py._hl.files.File, wavel_range: Optional[Tuple[float, float]], teff_range: Optional[Tuple[float, float]], spec_res: Optional[float]) -> None: """ Function for adding the BT-Settl atmospheric models (solar metallicity) to the database. The spectra had been downloaded from the Theoretical spectra web server (http://svo2.cab.inta-csic.es/svo/theory/newov2/index.php?models=bt-settl) and resampled to a spectral resolution of 5000 from 0.1 to 100 um. Parameters ---------- input_path : str Folder where the data is located. database : h5py._hl.files.File Database. wavel_range : tuple(float, float), None Wavelength range (um). The original wavelength points are used if set to ``None``. teff_range : tuple(float, float), None Effective temperature range (K). All temperatures are selected if set to ``None``. spec_res : float, None Spectral resolution. Not used if ``wavel_range`` is set to ``None``. Returns ------- NoneType None """ if not os.path.exists(input_path): os.makedirs(input_path) input_file = 'bt-settl.tgz' data_folder = os.path.join(input_path, 'bt-settl/') data_file = os.path.join(input_path, input_file) if not os.path.exists(data_folder): os.makedirs(data_folder) url = 'https://people.phys.ethz.ch/~ipa/tstolker/bt-settl.tgz' if not os.path.isfile(data_file): print('Downloading Bt-Settl model spectra (130 MB)...', end='', flush=True) urllib.request.urlretrieve(url, data_file) print(' [DONE]') print('Unpacking BT-Settl model spectra (130 MB)...', end='', flush=True) tar = tarfile.open(data_file) tar.extractall(data_folder) tar.close() print(' [DONE]') teff = [] logg = [] flux = [] if wavel_range is not None and spec_res is not None: wavelength = read_util.create_wavelengths(wavel_range, spec_res) else: wavelength = None for _, _, file_list in os.walk(data_folder): for filename in sorted(file_list): if filename[:9] == 'bt-settl_': file_split = filename.split('_') teff_val = float(file_split[2]) logg_val = float(file_split[4]) if teff_range is not None: if teff_val < teff_range[0] or teff_val > teff_range[1]: continue print_message = f'Adding BT-Settl model spectra... {filename}' print(f'\r{print_message:<69}', end='') data_wavel, data_flux = np.loadtxt(os.path.join( data_folder, filename), unpack=True) teff.append(teff_val) logg.append(logg_val) if wavel_range is None or spec_res is None: if wavelength is None: wavelength = np.copy(data_wavel) # (um) if np.all(np.diff(wavelength) < 0): raise ValueError( 'The wavelengths are not all sorted by increasing value.' ) flux.append(data_flux) # (W m-2 um-1) else: flux_resample = spectres.spectres(wavelength, data_wavel, data_flux, spec_errs=None, fill=np.nan, verbose=False) if np.isnan(np.sum(flux_resample)): raise ValueError( f'Resampling is only possible if the new wavelength ' f'range ({wavelength[0]} - {wavelength[-1]} um) falls ' f'sufficiently far within the wavelength range ' f'({data_wavel[0]} - {data_wavel[-1]} um) of the input ' f'spectra.') flux.append(flux_resample) # (W m-2 um-1) print_message = 'Adding BT-Settl model spectra... [DONE]' print(f'\r{print_message:<69}') data_sorted = data_util.sort_data(np.asarray(teff), np.asarray(logg), None, None, None, wavelength, np.asarray(flux)) data_util.write_data('bt-settl', ['teff', 'logg'], database, data_sorted)
def add_drift_phoenix(input_path: str, database: h5py._hl.files.File, wavel_range: Optional[Tuple[float, float]] = None, teff_range: Optional[Tuple[float, float]] = None, spec_res: float = None) -> None: """ Function for adding the DRIFT-PHOENIX atmospheric models to the database. The original spectra were downloaded from http://svo2.cab.inta-csic.es/theory/newov2/index.php?models=drift and have been resampled to a spectral resolution of R = 2000 from 0.1 to 50 um. Parameters ---------- input_path : str Folder where the data is located. database : h5py._hl.files.File Database. wavel_range : tuple(float, float), None Wavelength range (um). The full wavelength range (0.1-50 um) is stored if set to ``None``. Only used in combination with ``spec_res``. teff_range : tuple(float, float), None Effective temperature range (K). All available temperatures are stored if set to ``None``. spec_res : float, None Spectral resolution. The data is stored with the spectral resolution of the input spectra (R = 2000) if set to ``None``. Only used in combination with ``wavel_range``. Returns ------- NoneType None """ if not os.path.exists(input_path): os.makedirs(input_path) input_file = 'drift-phoenix.tgz' url = 'https://people.phys.ethz.ch/~ipa/tstolker/drift-phoenix.tgz' data_folder = os.path.join(input_path, 'drift-phoenix/') data_file = os.path.join(input_path, input_file) if not os.path.exists(data_folder): os.makedirs(data_folder) if not os.path.isfile(data_file): print('Downloading DRIFT-PHOENIX model spectra (229 MB)...', end='', flush=True) urllib.request.urlretrieve(url, data_file) print(' [DONE]') print('Unpacking DRIFT-PHOENIX model spectra (229 MB)...', end='', flush=True) tar = tarfile.open(data_file) tar.extractall(data_folder) tar.close() print(' [DONE]') teff = [] logg = [] feh = [] flux = [] if wavel_range is not None and spec_res is not None: wavelength = read_util.create_wavelengths(wavel_range, spec_res) else: wavelength = None for _, _, files in os.walk(data_folder): for filename in files: if filename[:14] == 'drift-phoenix_': file_split = filename.split('_') teff_val = float(file_split[2]) logg_val = float(file_split[4]) feh_val = float(file_split[6]) if teff_range is not None: if teff_val < teff_range[0] or teff_val > teff_range[1]: continue print_message = f'Adding DRIFT-PHOENIX model spectra... {filename}' print(f'\r{print_message:<88}', end='') data_wavel, data_flux = np.loadtxt(os.path.join( data_folder, filename), unpack=True) teff.append(teff_val) logg.append(logg_val) feh.append(feh_val) if wavel_range is None or spec_res is None: if wavelength is None: wavelength = np.copy(data_wavel) # (um) if np.all(np.diff(wavelength) < 0): raise ValueError( 'The wavelengths are not all sorted by increasing value.' ) flux.append(data_flux) # (W m-2 um-1) else: flux_resample = spectres.spectres(wavelength, data_wavel, data_flux, spec_errs=None, fill=np.nan, verbose=False) if np.isnan(np.sum(flux_resample)): raise ValueError( f'Resampling is only possible if the new wavelength ' f'range ({wavelength[0]} - {wavelength[-1]} um) falls ' f'sufficiently far within the wavelength range ' f'({data_wavel[0]} - {data_wavel[-1]} um) of the input ' f'spectra.') flux.append(flux_resample) # (W m-2 um-1) print_message = 'Adding DRIFT-PHOENIX model spectra... [DONE]' print(f'\r{print_message:<88}') data_sorted = data_util.sort_data(np.asarray(teff), np.asarray(logg), np.asarray(feh), None, None, wavelength, np.asarray(flux)) data_util.write_data('drift-phoenix', ['teff', 'logg', 'feh'], database, data_sorted)
def add_ames_dusty(input_path: str, database: h5py._hl.files.File, wavel_range: Optional[Tuple[float, float]] = None, teff_range: Optional[Tuple[float, float]] = None, spec_res: float = None) -> None: """ Function for adding the AMES-Dusty atmospheric models to the database. The original spectra have been resampled to a spectral resolution of R = 2000 from 0.5 to 40 um. Note that a few of the spectra contain NaNs due to their limited, original wavelength coverage. Parameters ---------- input_path : str Folder where the data is located. database : h5py._hl.files.File Database. wavel_range : tuple(float, float), None Wavelength range (um). The full wavelength range (0.5-40 um) is stored if set to ``None``. Only used in combination with ``spec_res``. teff_range : tuple(float, float), None Effective temperature range (K). All available temperatures are stored if set to ``None``. spec_res : float, None Spectral resolution. The data is stored with the spectral resolution of the input spectra (R = 2000) if set to ``None``. Only used in combination with ``wavel_range``. Returns ------- NoneType None """ if not os.path.exists(input_path): os.makedirs(input_path) input_file = 'ames-dusty.tgz' url = 'https://people.phys.ethz.ch/~ipa/tstolker/ames-dusty.tgz' data_folder = os.path.join(input_path, 'ames-dusty/') data_file = os.path.join(input_path, input_file) if not os.path.exists(data_folder): os.makedirs(data_folder) if not os.path.isfile(data_file): print('Downloading AMES-Dusty model spectra (59 MB)...', end='', flush=True) urllib.request.urlretrieve(url, data_file) print(' [DONE]') print('Unpacking AMES-Dusty model spectra (59 MB)...', end='', flush=True) tar = tarfile.open(data_file) tar.extractall(data_folder) tar.close() print(' [DONE]') teff = [] logg = [] flux = [] if wavel_range is not None and spec_res is not None: wavelength = read_util.create_wavelengths(wavel_range, spec_res) else: wavelength = None for _, _, files in os.walk(data_folder): for filename in files: if filename[:11] == 'ames-dusty_': file_split = filename.split('_') teff_val = float(file_split[2]) logg_val = float(file_split[4]) if teff_range is not None: if teff_val < teff_range[0] or teff_val > teff_range[1]: continue print_message = f'Adding AMES-Dusty model spectra... {filename}' print(f'\r{print_message:<73}', end='') data_wavel, data_flux = np.loadtxt(os.path.join(data_folder, filename), unpack=True) teff.append(teff_val) logg.append(logg_val) if wavel_range is None or spec_res is None: if wavelength is None: wavelength = np.copy(data_wavel) # (um) if np.all(np.diff(wavelength) < 0): raise ValueError('The wavelengths are not all sorted by increasing value.') # if np.isnan(np.sum(data_flux)): # Three of the files contain partially NaNs due to a more limited # wavelength coverage in the original spectra (before using spectres) # data_flux = np.full(data_wavel.shape[0], np.nan) flux.append(data_flux) # (W m-2 um-1) else: flux_resample = spectres.spectres(wavelength, data_wavel, data_flux, spec_errs=None, fill=np.nan, verbose=False) if np.isnan(np.sum(flux_resample)): raise ValueError(f'Resampling is only possible if the new wavelength ' f'range ({wavelength[0]} - {wavelength[-1]} um) falls ' f'sufficiently far within the wavelength range ' f'({data_wavel[0]} - {data_wavel[-1]} um) of the input ' f'spectra.') flux.append(flux_resample) # (W m-2 um-1) print_message = 'Adding AMES-Dusty model spectra... [DONE]' print(f'\r{print_message:<73}') data_sorted = data_util.sort_data(np.asarray(teff), np.asarray(logg), None, None, None, wavelength, np.asarray(flux)) data_util.write_data('ames-dusty', ['teff', 'logg'], database, data_sorted)
def add_exo_rem(input_path: str, database: h5py._hl.files.File, wavel_range: Optional[Tuple[float, float]] = None, teff_range: Optional[Tuple[float, float]] = None, spec_res: Optional[float] = None) -> None: """ Function for adding the Exo-REM atmospheric models to the database. Parameters ---------- input_path : str Folder where the data is located. database : h5py._hl.files.File Database. wavel_range : tuple(float, float), None Wavelength range (um). The original wavelength points with a spectral resolution of 5000 are used if set to ``None``. teff_range : tuple(float, float), None Effective temperature range (K). All temperatures are selected if set to ``None``. spec_res : float, None Spectral resolution. Not used if ``wavel_range`` is set to ``None``. Returns ------- NoneType None """ if not os.path.exists(input_path): os.makedirs(input_path) input_file = 'exo-rem.tgz' url = 'https://people.phys.ethz.ch/~ipa/tstolker/exo-rem.tgz' data_folder = os.path.join(input_path, 'exo-rem/') data_file = os.path.join(data_folder, input_file) if not os.path.exists(data_folder): os.makedirs(data_folder) if not os.path.isfile(data_file): print('Downloading Exo-REM model spectra (790 MB)...', end='', flush=True) urllib.request.urlretrieve(url, data_file) print(' [DONE]') print('Unpacking Exo-REM model spectra (790 MB)...', end='', flush=True) tar = tarfile.open(data_file) tar.extractall(data_folder) tar.close() print(' [DONE]') teff = [] logg = [] feh = [] co_ratio = [] flux = [] if wavel_range is not None and spec_res is not None: wavelength = read_util.create_wavelengths(wavel_range, spec_res) else: wavelength = None for _, _, files in os.walk(data_folder): for filename in files: if filename[:8] == 'exo-rem_': file_split = filename.split('_') teff_val = float(file_split[2]) logg_val = float(file_split[4]) feh_val = float(file_split[6]) co_val = float(file_split[8]) if logg_val == 5.: continue if co_val in [0.8, 0.85]: continue if teff_range is not None: if teff_val < teff_range[0] or teff_val > teff_range[1]: continue print_message = f'Adding Exo-REM model spectra... {filename}' print(f'\r{print_message:<84}', end='') data_wavel, data_flux = np.loadtxt(os.path.join( data_folder, filename), unpack=True) teff.append(teff_val) logg.append(logg_val) feh.append(feh_val) co_ratio.append(co_val) if wavel_range is None or spec_res is None: if wavelength is None: wavelength = np.copy(data_wavel) # (um) if np.all(np.diff(wavelength) < 0): raise ValueError( 'The wavelengths are not all sorted by increasing value.' ) flux.append(data_flux) # (W m-2 um-1) else: flux_resample = spectres.spectres(wavelength, data_wavel, data_flux, spec_errs=None, fill=np.nan, verbose=False) if np.isnan(np.sum(flux_resample)): raise ValueError( f'Resampling is only possible if the new wavelength ' f'range ({wavelength[0]} - {wavelength[-1]} um) falls ' f'sufficiently far within the wavelength range ' f'({data_wavel[0]} - {data_wavel[-1]} um) of the input ' f'spectra.') flux.append(flux_resample) # (W m-2 um-1) print_message = 'Adding Exo-REM model spectra... [DONE]' print(f'\r{print_message:<84}') print('Grid points with the following parameters have been excluded:') print(' - log(g) = 5') print(' - C/O = 0.8') print(' - C/O = 0.85') data_sorted = data_util.sort_data(np.asarray(teff), np.asarray(logg), np.asarray(feh), np.asarray(co_ratio), None, wavelength, np.asarray(flux)) data_util.write_data('exo-rem', ['teff', 'logg', 'feh', 'co'], database, data_sorted)
def get_model(self, model_param: Dict[str, float], spec_res: Optional[float] = None, wavel_resample: Optional[np.ndarray] = None, magnitude: bool = False, smooth: bool = False) -> box.ModelBox: """ Function for extracting a model spectrum by linearly interpolating the model grid. Parameters ---------- model_param : dict Dictionary with the model parameters and values. The values should be within the boundaries of the grid. The grid boundaries of the spectra in the database can be obtained with :func:`~species.read.read_model.ReadModel.get_bounds()`. spec_res : float, None Spectral resolution that is used for smoothing the spectrum with a Gaussian kernel when ``smooth=True`` and/or resampling the spectrum when ``wavel_range`` of ``FitModel`` is not ``None``. The original wavelength points are used if both ``spec_res`` and ``wavel_resample`` are set to ``None``, or if ``smooth`` is set to ``True``. wavel_resample : np.ndarray, None Wavelength points (um) to which the spectrum is resampled. In that case, ``spec_res`` can still be used for smoothing the spectrum with a Gaussian kernel. magnitude : bool Normalize the spectrum with a flux calibrated spectrum of Vega and return the magnitude instead of flux density. smooth : bool If ``True``, the spectrum is smoothed with a Gaussian kernel to the spectral resolution of ``spec_res``. This requires either a uniform spectral resolution of the input spectra (fast) or a uniform wavelength spacing of the input spectra (slow). Returns ------- species.core.box.ModelBox Box with the model spectrum. """ if smooth and spec_res is None: warnings.warn('The \'spec_res\' argument is required for smoothing the spectrum when ' '\'smooth\' is set to True.') grid_bounds = self.get_bounds() extra_param = ['radius', 'distance', 'mass', 'luminosity', 'lognorm_radius', 'lognorm_sigma', 'lognorm_ext', 'ism_ext', 'ism_red', 'powerlaw_max', 'powerlaw_exp', 'powerlaw_ext'] for key in self.get_parameters(): if key not in model_param.keys(): raise ValueError(f'The \'{key}\' parameter is required by \'{self.model}\'. ' f'The mandatory parameters are {self.get_parameters()}.') if model_param[key] < grid_bounds[key][0]: raise ValueError(f'The input value of \'{key}\' is smaller than the lower ' f'boundary of the model grid ({model_param[key]} < ' f'{grid_bounds[key][0]}).') if model_param[key] > grid_bounds[key][1]: raise ValueError(f'The input value of \'{key}\' is larger than the upper ' f'boundary of the model grid ({model_param[key]} > ' f'{grid_bounds[key][1]}).') for key in model_param.keys(): if key not in self.get_parameters() and key not in extra_param: warnings.warn(f'The \'{key}\' parameter is not required by \'{self.model}\' so ' f'the parameter will be ignored. The mandatory parameters are ' f'{self.get_parameters()}.') if 'mass' in model_param and 'radius' not in model_param: mass = 1e3 * model_param['mass'] * constants.M_JUP # (g) radius = math.sqrt(1e3 * constants.GRAVITY * mass / (10.**model_param['logg'])) # (cm) model_param['radius'] = 1e-2 * radius / constants.R_JUP # (Rjup) if self.spectrum_interp is None: self.interpolate_model() if self.wavel_range is None: wl_points = self.get_wavelengths() self.wavel_range = (wl_points[0], wl_points[-1]) parameters = [] if 'teff' in model_param: parameters.append(model_param['teff']) if 'logg' in model_param: parameters.append(model_param['logg']) if 'feh' in model_param: parameters.append(model_param['feh']) if 'co' in model_param: parameters.append(model_param['co']) if 'fsed' in model_param: parameters.append(model_param['fsed']) flux = self.spectrum_interp(parameters)[0] if 'radius' in model_param: model_param['mass'] = read_util.get_mass(model_param['logg'], model_param['radius']) if 'distance' in model_param: scaling = (model_param['radius']*constants.R_JUP)**2 / \ (model_param['distance']*constants.PARSEC)**2 flux *= scaling if smooth: flux = read_util.smooth_spectrum(wavelength=self.wl_points, flux=flux, spec_res=spec_res) if wavel_resample is not None: flux = spectres.spectres(wavel_resample, self.wl_points, flux, spec_errs=None, fill=np.nan, verbose=True) elif spec_res is not None and not smooth: index = np.where(np.isnan(flux))[0] if index.size > 0: raise ValueError('Flux values should not contains NaNs. Please make sure that ' 'the parameter values and the wavelength range are within ' 'the grid boundaries as stored in the database.') wavel_resample = read_util.create_wavelengths( (self.wl_points[0], self.wl_points[-1]), spec_res) indices = np.where((wavel_resample > self.wl_points[0]) & (wavel_resample < self.wl_points[-2]))[0] wavel_resample = wavel_resample[indices] flux = spectres.spectres(wavel_resample, self.wl_points, flux, spec_errs=None, fill=np.nan, verbose=True) if magnitude: quantity = 'magnitude' with h5py.File(self.database, 'r') as h5_file: try: h5_file['spectra/calibration/vega'] except KeyError: h5_file.close() species_db = database.Database() species_db.add_spectrum('vega') h5_file = h5py.File(self.database, 'r') readcalib = read_calibration.ReadCalibration('vega', filter_name=None) calibbox = readcalib.get_spectrum() if wavel_resample is not None: new_spec_wavs = wavel_resample else: new_spec_wavs = self.wl_points flux_vega, _ = spectres.spectres(new_spec_wavs, calibbox.wavelength, calibbox.flux, spec_errs=calibbox.error, fill=np.nan, verbose=True) flux = -2.5*np.log10(flux/flux_vega) else: quantity = 'flux' if np.isnan(np.sum(flux)): warnings.warn(f'The resampled spectrum contains {np.sum(np.isnan(flux))} NaNs, ' f'probably because the original wavelength range does not fully ' f'encompass the new wavelength range. The happened with the ' f'following parameters: {model_param}.') if wavel_resample is None: wavelength = self.wl_points else: wavelength = wavel_resample # is_finite = np.where(np.isfinite(flux))[0] # # if wavel_resample is None: # wavelength = self.wl_points[is_finite] # else: # wavelength = wavel_resample[is_finite] # # if wavelength.shape[0] == 0: # raise ValueError(f'The model spectrum is empty. Perhaps the grid could not be ' # f'interpolated at {model_param} because zeros are stored in the ' # f'database.') model_box = box.create_box(boxtype='model', model=self.model, wavelength=wavelength, flux=flux, parameters=model_param, quantity=quantity) if 'lognorm_radius' in model_param and 'lognorm_sigma' in model_param and \ 'lognorm_ext' in model_param: model_box.flux = self.apply_lognorm_ext(model_box.wavelength, model_box.flux, model_param['lognorm_radius'], model_param['lognorm_sigma'], model_param['lognorm_ext']) if 'powerlaw_max' in model_param and 'powerlaw_exp' in model_param and \ 'powerlaw_ext' in model_param: model_box.flux = self.apply_powerlaw_ext(model_box.wavelength, model_box.flux, model_param['powerlaw_max'], model_param['powerlaw_exp'], model_param['powerlaw_ext']) if 'ism_ext' in model_param and 'ism_red' in model_param: model_box.flux = self.apply_ism_ext(model_box.wavelength, model_box.flux, model_param['ism_ext'], model_param['ism_red']) if 'radius' in model_box.parameters: model_box.parameters['luminosity'] = 4. * np.pi * ( model_box.parameters['radius'] * constants.R_JUP)**2 * constants.SIGMA_SB * \ model_box.parameters['teff']**4. / constants.L_SUN # (Lsun) return model_box
def get_spectrum( self, model_param: Optional[Dict[str, float]] = None, apply_mask: bool = False, spec_res: Optional[float] = None, extrapolate: bool = False, min_wavelength: Optional[float] = None, ) -> box.SpectrumBox: """ Function for selecting the calibration spectrum. Parameters ---------- model_param : dict, None Model parameters. Should contain the 'scaling' value. Not used if set to ``None``. apply_mask : bool Exclude negative values and NaN values. spec_res : float, None Spectral resolution. Original wavelength points are used if set to ``None``. extrapolate : bool Extrapolate to 6 um by fitting a power law function. min_wavelength : float, None Minimum wavelength used for fitting the power law function. All data is used if set to ``None``. Returns ------- species.core.box.SpectrumBox Box with the spectrum. """ with h5py.File(self.database, "r") as h5_file: data = np.asarray(h5_file[f"spectra/calibration/{self.tag}"]) wavelength = np.asarray(data[0, ]) flux = np.asarray(data[1, ]) error = np.asarray(data[2, ]) if apply_mask: indices = np.where(flux > 0.0)[0] wavelength = wavelength[indices] flux = flux[indices] error = error[indices] if model_param is not None: flux = model_param["scaling"] * flux error = model_param["scaling"] * error if self.wavel_range is None: wl_index = np.ones(wavelength.size, dtype=bool) else: wl_index = ((flux > 0.0) & (wavelength > self.wavel_range[0]) & (wavelength < self.wavel_range[1])) count = np.count_nonzero(wl_index) if count > 0: index = np.where(wl_index)[0] if index[0] > 0: wl_index[index[0] - 1] = True if index[-1] < len(wl_index) - 1: wl_index[index[-1] + 1] = True wavelength = wavelength[wl_index] flux = flux[wl_index] error = error[wl_index] if extrapolate: def _power_law(wavelength, offset, scaling, power_index): return offset + scaling * wavelength**power_index if min_wavelength: indices = np.where(wavelength > min_wavelength)[0] else: indices = np.arange(0, wavelength.size, 1) popt, pcov = curve_fit( f=_power_law, xdata=wavelength[indices], ydata=flux[indices], p0=(0.0, np.mean(flux[indices]), -1.0), sigma=error[indices], ) sigma = np.sqrt(np.diag(pcov)) print("Fit result for f(x) = a + b*x^c:") print(f"a = {popt[0]} +/- {sigma[0]}") print(f"b = {popt[1]} +/- {sigma[1]}") print(f"c = {popt[2]} +/- {sigma[2]}") while wavelength[-1] <= 6.0: wl_add = wavelength[-1] + wavelength[-1] / 1000.0 wavelength = np.append(wavelength, wl_add) flux = np.append(flux, _power_law(wl_add, popt[0], popt[1], popt[2])) error = np.append(error, 0.0) if spec_res is not None: wavelength_new = read_util.create_wavelengths( (wavelength[0], wavelength[-1]), spec_res) flux_new, error_new = spectres.spectres( wavelength_new, wavelength, flux, spec_errs=error, fill=0.0, verbose=True, ) wavelength = wavelength_new flux = flux_new error = error_new return box.create_box( boxtype="spectrum", spectrum="calibration", wavelength=wavelength, flux=flux, error=error, name=self.tag, )
def add_petitcode_hot_cloudy(input_path: str, database: h5py._hl.files.File, wavel_range: Optional[Tuple[float, float]] = None, teff_range: Optional[Tuple[float, float]] = None, spec_res: Optional[float] = 1000.) -> None: """ Function for adding the petitCODE hot cloudy atmospheric models to the database. Parameters ---------- input_path : str Folder where the data is located. database : h5py._hl.files.File Database. wavel_range : tuple(float, float), None Wavelength range (um). The original wavelength points are used if set to None. teff_range : tuple(float, float), None Effective temperature range (K). All temperatures are selected if set to None. spec_res : float, None Spectral resolution. Not used if ``wavel_range`` is set to None. Returns ------- NoneType None """ if not os.path.exists(input_path): os.makedirs(input_path) data_folder = os.path.join(input_path, 'petitcode-hot-cloudy/') url = 'https://people.phys.ethz.ch/~ipa/tstolker/petitcode-hot-cloudy.tgz' data_file = os.path.join(input_path, 'petitcode-hot-cloudy.tgz') if not os.path.isfile(data_file): print('Downloading petitCODE hot cloudy model spectra (276 MB)...', end='', flush=True) urllib.request.urlretrieve(url, data_file) print(' [DONE]') print('Unpacking petitCODE hot cloudy model spectra (276 MB)...', end='', flush=True) tar = tarfile.open(data_file) tar.extractall(data_folder) tar.close() print(' [DONE]') teff = [] logg = [] feh = [] co_ratio = [] fsed = [] flux = [] if wavel_range is not None: wavelength = read_util.create_wavelengths(wavel_range, spec_res) else: wavelength = None for _, _, files in os.walk(data_folder): for filename in files: file_split = filename.split('_') teff_val = float(file_split[2]) logg_val = float(file_split[4]) feh_val = float(file_split[6]) co_ratio_val = float(file_split[8]) fsed_val = float(file_split[10]) if teff_range is not None: if teff_val < teff_range[0] or teff_val > teff_range[1]: continue print_message = f'Adding petitCODE hot cloudy model spectra... {filename}' print(f'\r{print_message:<111}', end='') data = np.loadtxt(os.path.join(data_folder, filename)) teff.append(teff_val) logg.append(logg_val) feh.append(feh_val) co_ratio.append(co_ratio_val) fsed.append(fsed_val) if wavel_range is None: if wavelength is None: # (cm) -> (um) wavelength = data[:, 0] * 1e4 if np.all(np.diff(wavelength) < 0): raise ValueError( 'The wavelengths are not all sorted by increasing value.' ) # (erg s-1 cm-2 Hz-1) -> (W m-2 um-1) flux.append(data[:, 1] * 1e-9 * constants.LIGHT / (wavelength * 1e-6)**2) else: # (cm) -> (um) data_wavel = data[:, 0] * 1e4 # (erg s-1 cm-2 Hz-1) -> (W m-2 um-1) data_flux = data[:, 1] * 1e-9 * constants.LIGHT / (data_wavel * 1e-6)**2 try: flux.append( spectres.spectres(wavelength, data_wavel, data_flux)) except ValueError: flux.append(np.zeros(wavelength.shape[0])) warnings.warn( 'The wavelength range should fall within the range of the ' 'original wavelength sampling. Storing zeros instead.') print_message = 'Adding petitCODE hot cloudy model spectra... [DONE]' print(f'\r{print_message:<111}') data_sorted = data_util.sort_data(np.asarray(teff), np.asarray(logg), np.asarray(feh), np.asarray(co_ratio), np.asarray(fsed), wavelength, np.asarray(flux)) data_util.write_data('petitcode-hot-cloudy', ['teff', 'logg', 'feh', 'co', 'fsed'], database, data_sorted)
def get_spectrum( self, model_param: Dict[str, Union[float, List[float]]], spec_res: float, smooth: bool = False, wavel_resample: Optional[np.ndarray] = None, ) -> box.ModelBox: """ Function for calculating a Planck spectrum or a combination of multiple Planck spectra. The spectrum is calculated at :math:`R = 500`. Afterwards, an optional smoothing and wavelength resampling can be applied. Parameters ---------- model_param : dict Dictionary with the 'teff' (K), 'radius' (Rjup), and 'parallax' (mas) or 'distance' (pc). The values of 'teff' and 'radius' can be a single float, or a list with floats for a combination of multiple Planck functions, e.g. {'teff': [1500., 1000.], 'radius': [1., 2.], 'distance': 10.}. spec_res : float Spectral resolution that is used for smoothing the spectrum with a Gaussian kernel when ``smooth=True``. smooth : bool If ``True``, the spectrum is smoothed to the spectral resolution of ``spec_res``. wavel_resample : np.ndarray, None Wavelength points (um) to which the spectrum will be resampled. The resampling is applied after the optional smoothing to ``spec_res`` when ``smooth=True``. Returns ------- species.core.box.ModelBox Box with the Planck spectrum. """ if "teff" in model_param and isinstance(model_param["teff"], list): model_param = self.update_parameters(model_param) wavel_points = read_util.create_wavelengths(self.wavel_range, 500.0) n_planck = 0 for item in model_param: if item[:4] == "teff": n_planck += 1 if n_planck == 1: if "radius" in model_param and "parallax" in model_param: scaling = ( (model_param["radius"] * constants.R_JUP) / (1e3 * constants.PARSEC / model_param["parallax"]) ) ** 2 elif "radius" in model_param and "distance" in model_param: scaling = ( (model_param["radius"] * constants.R_JUP) / (model_param["distance"] * constants.PARSEC) ) ** 2 else: scaling = 1.0 flux = self.planck( wavel_points, model_param["teff"], scaling ) # (W m-2 um-1) else: flux = np.zeros(wavel_points.shape) for i in range(n_planck): if f"radius_{i}" in model_param and "parallax" in model_param: scaling = ( (model_param[f"radius_{i}"] * constants.R_JUP) / (1e3 * constants.PARSEC / model_param["parallax"]) ) ** 2 elif f"radius_{i}" in model_param and "distance" in model_param: scaling = ( (model_param[f"radius_{i}"] * constants.R_JUP) / (model_param["distance"] * constants.PARSEC) ) ** 2 else: scaling = 1.0 flux += self.planck( wavel_points, model_param[f"teff_{i}"], scaling ) # (W m-2 um-1) if smooth: flux = read_util.smooth_spectrum(wavel_points, flux, spec_res) model_box = box.create_box( boxtype="model", model="planck", wavelength=wavel_points, flux=flux, parameters=model_param, quantity="flux", ) if wavel_resample is not None: flux = spectres.spectres( wavel_resample, wavel_points, flux, spec_errs=None, fill=np.nan, verbose=True, ) model_box.wavelength = wavel_resample model_box.flux = flux if n_planck == 1 and "radius" in model_param: model_box.parameters["luminosity"] = ( 4.0 * np.pi * (model_box.parameters["radius"] * constants.R_JUP) ** 2 * constants.SIGMA_SB * model_box.parameters["teff"] ** 4.0 / constants.L_SUN ) # (Lsun) elif n_planck > 1: lum_total = 0.0 for i in range(n_planck): if f"radius_{i}" in model_box.parameters: # Add up the luminosity of the blackbody components (Lsun) surface = ( 4.0 * np.pi * (model_box.parameters[f"radius_{i}"] * constants.R_JUP) ** 2 ) lum_total += ( surface * constants.SIGMA_SB * model_box.parameters[f"teff_{i}"] ** 4.0 / constants.L_SUN ) if lum_total > 0.0: model_box.parameters["luminosity"] = lum_total return model_box
def integrate_flux( self, wavel_int: Tuple[float, float], interp_kind: str = "linear", plot_filename: Optional[str] = "int_line.pdf", ) -> Union[np.float64, np.float64]: """ Method for calculating the integrated line flux and error. The spectrum is first interpolated to :math:`R = 100000` and then integrated across the specified wavelength range with the composite trapezoidal rule of ``np.trapz``. The error is estimated with a Monte Carlo approach from 1000 samples. Parameters ---------- wavel_int : tuple(float, float) Wavelength range (um) across which the flux will be integrated. interp_kind : str Kind of interpolation kind for ``scipy.interpolate.interp1d`` (default: 'linear'). plot_filename : str, None Filename for the plot with the interpolated line profile. The plot is shown in an interface window if the argument is set to ``None``. Returns ------- float Integrated line flux (W m-2). float Flux error (W m-2). """ if plot_filename is None: print("Plotting integrated line...", end="", flush=True) else: print(f"Plotting integrated line: {plot_filename}...", end="", flush=True) n_samples = 1000 wavel_high_res = read_util.create_wavelengths(wavel_int, 1e5) # Creating plot mpl.rcParams["font.serif"] = ["Bitstream Vera Serif"] mpl.rcParams["font.family"] = "serif" plt.rc("axes", edgecolor="black", linewidth=2) plt.rcParams["axes.axisbelow"] = False plt.figure(1, figsize=(6, 3)) gs = mpl.gridspec.GridSpec(1, 1) gs.update(wspace=0, hspace=0, left=0, right=1, bottom=0, top=1) ax1 = plt.subplot(gs[0, 0]) ax2 = ax1.twiny() ax1.tick_params( axis="both", which="major", colors="black", labelcolor="black", direction="in", width=1, length=5, labelsize=12, top=False, bottom=True, left=True, right=True, ) ax1.tick_params( axis="both", which="minor", colors="black", labelcolor="black", direction="in", width=1, length=3, labelsize=12, top=False, bottom=True, left=True, right=True, ) ax2.tick_params( axis="both", which="major", colors="black", labelcolor="black", direction="in", width=1, length=5, labelsize=12, top=True, bottom=False, left=False, right=True, ) ax2.tick_params( axis="both", which="minor", colors="black", labelcolor="black", direction="in", width=1, length=3, labelsize=12, top=True, bottom=False, left=False, right=True, ) ax1.set_xlabel("Wavelength (µm)", fontsize=16) ax1.set_ylabel("Flux (W m$^{-2}$ µm$^{-1}$)", fontsize=16) ax2.set_xlabel("Velocity (km s$^{-1}$)", fontsize=16) ax1.get_xaxis().set_label_coords(0.5, -0.12) ax1.get_yaxis().set_label_coords(-0.1, 0.5) ax2.get_xaxis().set_label_coords(0.5, 1.12) ax1.plot( self.spectrum[:, 0], self.spectrum[:, 1], color="black", label=self.spec_name, ) ax2.plot(self.spec_vrad, self.spectrum[:, 1], ls="-", lw=0.0) flux_sample = np.zeros(n_samples) fwhm_sample = np.zeros(n_samples) mean_sample = np.zeros(n_samples) vrad_sample = np.zeros(n_samples) lum_sample = np.zeros(n_samples) for i in range(n_samples): # Sample fluxes from random errors spec_rand = np.random.normal(self.spectrum[:, 1], self.spectrum[:, 2]) # Interpolate sampled spectrum spec_interp = interp1d( self.spectrum[:, 0], spec_rand, kind=interp_kind, bounds_error=False ) # Resample to high-resolution wavelengths flux_rand = spec_interp(wavel_high_res) # Integrate line flux (W m-2) flux_sample[i] = np.trapz(flux_rand, wavel_high_res) # Line luminosity (Lsun) lum_sample[i] = ( 4.0 * np.pi * (1e3 * constants.PARSEC / self.parallax) ** 2 * flux_sample[i] ) lum_sample[i] /= constants.L_SUN # (Lsun) # Weighted (with flux) mean wavelength (um) mean_sample[i] = np.trapz( wavel_high_res * flux_rand, wavel_high_res ) / np.trapz(flux_rand, wavel_high_res) # Radial velocity (km s-1) vrad_sample[i] = ( 1e-3 * constants.LIGHT * (mean_sample[i] - self.lambda_rest) / self.lambda_rest ) # Find full width at half maximum spline = InterpolatedUnivariateSpline( wavel_high_res, flux_rand - np.max(flux_rand) / 2.0 ) root = spline.roots() diff = root - mean_sample[i] root1 = np.amax(diff[diff < 0.0]) root2 = np.amin(diff[diff > 0.0]) fwhm_sample[i] = 1e-3 * constants.LIGHT * (root2 - root1) / mean_sample[i] # Add 30 samples to the plot if i == 0: ax1.plot( wavel_high_res, flux_rand, ls="-", lw=0.5, color="gray", alpha=0.4, label="Random samples", ) elif i < 30: ax1.plot( wavel_high_res, flux_rand, ls="-", lw=0.5, color="gray", alpha=0.4 ) # Line flux from original, interpolated spectrum spec_interp = interp1d( self.spectrum[:, 0], self.spectrum[:, 1], kind=interp_kind, bounds_error=False, ) flux_high_res = spec_interp(wavel_high_res) line_flux = np.trapz(flux_high_res, wavel_high_res) ax1.plot( wavel_high_res, flux_high_res, color="tab:blue", label="High resolution" ) ax1.legend(loc="upper right", frameon=False, fontsize=12.0) print(" [DONE]") if plot_filename is None: plt.show() else: plt.savefig(plot_filename, bbox_inches="tight") plt.clf() plt.close() wavel_mean, wavel_std = np.mean(mean_sample), np.std(mean_sample) print(f"Mean wavelength (nm): {1e3*wavel_mean:.2f} +/- {1e3*wavel_std:.2f}") fwhm_mean, fwhm_std = np.mean(fwhm_sample), np.std(fwhm_sample) print(f"FWHM (km s-1): {fwhm_mean:.2f} +/- {fwhm_std:.2f}") vrad_mean, vrad_std = np.mean(vrad_sample), np.std(vrad_sample) print(f"Radial velocity (km s-1): {vrad_mean:.1f} +/- {vrad_std:.1f}") line_error = np.std(flux_sample) print(f"Line flux (W m-2): {line_flux:.2e} +/- {line_error:.2e}") lum_mean, lum_std = np.mean(lum_sample), np.std(lum_sample) print(f"Line luminosity (Lsun): {lum_mean:.2e} +/- {lum_std:.2e}") return line_flux, line_error
def add_petitcode_hot_clear(input_path: str, database: h5py._hl.files.File, data_folder: str, wavel_range: Optional[Tuple[float, float]] = None, teff_range: Optional[Tuple[float, float]] = None, spec_res: Optional[float] = 1000.) -> None: """ Function for adding the petitCODE hot clear atmospheric models to the database. Parameters ---------- input_path : str Folder where the data is located. database : h5py._hl.files.File Database. data_folder : str Path with input data. wavel_range : tuple(float, float), None Wavelength range (um). The original wavelength points are used if set to None. teff_range : tuple(float, float), None Effective temperature range (K). All temperatures are selected if set to None. spec_res : float, None Spectral resolution. Not used if ``wavel_range`` is set to None. Returns ------- NoneType None """ if not os.path.exists(input_path): os.makedirs(input_path) teff = [] logg = [] feh = [] co_ratio = [] flux = [] if wavel_range is not None: wavelength = read_util.create_wavelengths(wavel_range, spec_res) else: wavelength = None for _, _, files in os.walk(data_folder): for filename in files: teff_val = float(filename[9:13]) logg_val = float(filename[19:23]) feh_val = float(filename[28:32]) co_ratio_val = float(filename[36:40]) if teff_range is not None: if teff_val < teff_range[0] or teff_val > teff_range[1]: continue print_message = f'Adding petitCODE hot clear model spectra... {filename}' print(f'\r{print_message:<100}', end='') data = np.loadtxt(os.path.join(data_folder, filename)) teff.append(teff_val) logg.append(logg_val) feh.append(feh_val) co_ratio.append(co_ratio_val) if wavel_range is None: if wavelength is None: # (cm) -> (um) wavelength = data[:, 0] * 1e4 if np.all(np.diff(wavelength) < 0): raise ValueError( 'The wavelengths are not all sorted by increasing value.' ) # (erg s-1 cm-2 Hz-1) -> (W m-2 um-1) flux.append(data[:, 1] * 1e-9 * constants.LIGHT / (wavelength * 1e-6)**2) else: # (cm) -> (um) data_wavel = data[:, 0] * 1e4 # (erg s-1 cm-2 Hz-1) -> (W m-2 um-1) data_flux = data[:, 1] * 1e-9 * constants.LIGHT / (data_wavel * 1e-6)**2 try: flux.append( spectres.spectres(wavelength, data_wavel, data_flux)) except ValueError: flux.append(np.zeros(wavelength.shape[0])) warnings.warn( 'The wavelength range should fall within the range of the ' 'original wavelength sampling. Storing zeros instead.') print_message = 'Adding petitCODE hot clear model spectra... [DONE]' print(f'\r{print_message:<100}') data_sorted = data_util.sort_data(np.asarray(teff), np.asarray(logg), np.asarray(feh), np.asarray(co_ratio), None, wavelength, np.asarray(flux)) data_util.write_data('petitcode-hot-clear', ['teff', 'logg', 'feh', 'co'], database, data_sorted)
def add_model_grid( model_name: str, input_path: str, database: h5py._hl.files.File, wavel_range: Optional[Tuple[float, float]], teff_range: Optional[Tuple[float, float]], spec_res: Optional[float], ) -> None: """ Function for adding a grid of model spectra to the database. The original spectra had been resampled to logarithmically- spaced wavelengths, so at a constant resolution, :math:`\\lambda/\\Delta\\lambda`. This function downloads the model grid, unpacks the tar file, and adds the spectra and parameters to the database. Parameters ---------- model_name : str Name of the model grid. input_path : str Folder where the data is located. database : h5py._hl.files.File Database. wavel_range : tuple(float, float), None Wavelength range (um). The original wavelength points are used if set to ``None``. teff_range : tuple(float, float), None Effective temperature range (K). All temperatures are selected if set to ``None``. spec_res : float, None Spectral resolution for resampling. Not used if ``wavel_range`` is set to ``None`` and/or ``spec_res`` is set to ``None`` Returns ------- NoneType None """ data_file = pathlib.Path(__file__).parent.resolve() / "model_data.json" with open(data_file, "r", encoding="utf-8") as json_file: model_data = json.load(json_file) if model_name in model_data.keys(): model_info = model_data[model_name] else: raise ValueError( f"The {model_name} atmospheric model is not available. " f"Please choose one of the following models: " f"'ames-cond', 'ames-dusty', 'atmo', 'bt-settl', " f"'bt-nextgen', 'drift-phoexnix', 'petitcode-cool-clear', " f"'petitcode-cool-cloudy', 'petitcode-hot-clear', " f"'petitcode-hot-cloudy', 'exo-rem', 'bt-settl-cifist', " f"'bt-cond', 'bt-cond-feh', 'blackbody', 'sonora-cholla', " f"'sonora-bobcat', 'sonora-bobcat-co', 'koester-wd'") if model_name == "bt-settl": warnings.warn("It is recommended to use the CIFIST " "grid of the BT-Settl, because it is " "a newer version. In that case, set " "model='bt-settl-cifist' when using " "add_model of Database.") if not os.path.exists(input_path): os.makedirs(input_path) input_file = f"{model_name}.tgz" data_folder = os.path.join(input_path, model_name) data_file = os.path.join(input_path, input_file) if not os.path.exists(data_folder): os.makedirs(data_folder) url = f"https://home.strw.leidenuniv.nl/~stolker/species/{model_name}.tgz" if not os.path.isfile(data_file): print( f"Downloading {model_info['name']} model " f"spectra ({model_info['file size']})...", end="", flush=True, ) urllib.request.urlretrieve(url, data_file) print(" [DONE]") print( f"Unpacking {model_info['name']} model " f"spectra ({model_info['file size']})...", end="", flush=True, ) tar = tarfile.open(data_file) tar.extractall(data_folder) tar.close() print(" [DONE]") if "information" in model_info: print(f"Model information: {model_info['information']}") if "reference" in model_info: print(f"Please cite {model_info['reference']} when " f"using {model_info['name']} in a publication") if "url" in model_info: print(f"Reference URL: {model_info['url']}") teff = [] if "logg" in model_info["parameters"]: logg = [] else: logg = None if "feh" in model_info["parameters"]: feh = [] else: feh = None if "c_o_ratio" in model_info["parameters"]: c_o_ratio = [] else: c_o_ratio = None if "fsed" in model_info["parameters"]: fsed = [] else: fsed = None if "log_kzz" in model_info["parameters"]: log_kzz = [] else: log_kzz = None flux = [] if wavel_range is not None and spec_res is not None: wavelength = read_util.create_wavelengths(wavel_range, spec_res) print(f"Wavelength range (um) = {wavel_range[0]} - {wavel_range[1]}") print(f"Spectral resolution = {spec_res}") else: wavelength = None print(f"Wavelength range (um) = " f"{model_info['wavelength range'][0]} - " f"{model_info['wavelength range'][1]}") print(f"Spectral resolution = {model_info['resolution']}") if teff_range is None: print( f"Teff range (K) = {model_info['teff range'][0]} - {model_info['teff range'][1]}" ) else: print(f"Teff range (K) = {teff_range[0]} - {teff_range[1]}") print_message = "" for _, _, file_list in os.walk(data_folder): for filename in sorted(file_list): if filename[:len(model_name)] == model_name: file_split = filename.split("_") param_index = file_split.index("teff") + 1 teff_val = float(file_split[param_index]) if teff_range is not None: if teff_val < teff_range[0] or teff_val > teff_range[1]: continue teff.append(teff_val) if logg is not None: param_index = file_split.index("logg") + 1 logg.append(float(file_split[param_index])) if feh is not None: param_index = file_split.index("feh") + 1 feh.append(float(file_split[param_index])) if c_o_ratio is not None: param_index = file_split.index("co") + 1 c_o_ratio.append(float(file_split[param_index])) if fsed is not None: param_index = file_split.index("fsed") + 1 fsed.append(float(file_split[param_index])) if log_kzz is not None: param_index = file_split.index("logkzz") + 1 log_kzz.append(float(file_split[param_index])) empty_message = len(print_message) * " " print(f"\r{empty_message}", end="") print_message = ( f"Adding {model_info['name']} model spectra... {filename}") print(f"\r{print_message}", end="") data_wavel, data_flux = np.loadtxt(os.path.join( data_folder, filename), unpack=True) if wavel_range is None or spec_res is None: if wavelength is None: wavelength = np.copy(data_wavel) # (um) if np.all(np.diff(wavelength) < 0): raise ValueError( "The wavelengths are not all sorted by increasing value." ) flux.append(data_flux) # (W m-2 um-1) else: flux_resample = spectres.spectres( wavelength, data_wavel, data_flux, spec_errs=None, fill=np.nan, verbose=False, ) if np.isnan(np.sum(flux_resample)): raise ValueError( f"Resampling is only possible if the new wavelength " f"range ({wavelength[0]} - {wavelength[-1]} um) falls " f"sufficiently far within the wavelength range " f"({data_wavel[0]} - {data_wavel[-1]} um) of the input " f"spectra.") flux.append(flux_resample) # (W m-2 um-1) empty_message = len(print_message) * " " print(f"\r{empty_message}", end="") print_message = f"Adding {model_info['name']} model spectra... [DONE]" print(f"\r{print_message}") if logg is not None: logg = np.asarray(logg) if feh is not None: feh = np.asarray(feh) if c_o_ratio is not None: c_o_ratio = np.asarray(c_o_ratio) if fsed is not None: fsed = np.asarray(fsed) if log_kzz is not None: log_kzz = np.asarray(log_kzz) data_sorted = data_util.sort_data( np.asarray(teff), logg, feh, c_o_ratio, fsed, log_kzz, wavelength, np.asarray(flux), ) data_util.write_data(model_name, model_info["parameters"], database, data_sorted)