Source code for exosim.tasks.sed.loadPhoenix

import glob
import os

import numpy as np
from astropy import units as u
from astropy.io import fits

import exosim.models.signal as signal
import exosim.utils.checks as checks
from exosim.tasks.task import Task


[docs]class LoadPhoenix(Task): """ Loads a star SED from a grid of Phoenix spectra or from a specific Phoenix file. Returns ------- :class:`~exosim.models.signal.Sed` Star Sed Examples -------- >>> from exosim.tasks.sed import LoadPhoenix >>> loadPhoenix = LoadPhoenix() Prepare the star >>> from astropy import constants as cc >>> import astropy.units as u >>> import numpy as np >>> D= 12.975 * u.pc >>> T= 3016 * u.K >>> M= 0.15 * u.Msun >>> R= 0.218 * u.Rsun >>> z= 0.0 >>> g = (cc.G * M.si / R.si ** 2).to(u.cm / u.s ** 2) >>> logg = np.log10(g.value) Load the sed from a directory >>> sed = loadPhoenix(path = phoenix_directory, T=T, D=D, R=R, z=z, logg=logg) or load the sed from a file >>> sed = loadPhoenix(filename = phoenix_file, D=D, R=R) Notes -------- This class can either load the SED from a file or select the most suitable file from the Phoenix spectra path, given the proper information on the star. In the former case the `filename` keyword is needed, in the latter are required the keywords `path`, `T`, `z`, `logg`. Not providing the right keywords would result in an error. Raises -------- InputError if neither `filename` or `path` are given. KeyError if a needed parameter is missing in the user inputs. """ def __init__(self): """ Parameters ----------- R: :class:`~astropy.units.Quantity` or float star radius. If no units are attached is considered as expressed in `m` D: :class:`~astropy.units.Quantity` or float star distance. If no units are attached is considered as expressed in `m` filename: str (optional) Phoenix file path path: str (optional) Phoenix directory path T: :class:`~astropy.units.Quantity` or float (optional) star temperature. If no units are attached is considered as expressed in `K` z: float (optional) star metallicity. logg: float (optional) star logG. """ self.add_task_param("R", "star radius") self.add_task_param("D", "star distannce") self.add_task_param("T", "star temperature", None) self.add_task_param("logg", "star logG", None) self.add_task_param("z", "star metallicity", None) self.add_task_param("path", "phoenix spectra path", None) self.add_task_param("filename", "phoenix file name", None)
[docs] def execute(self): path = self.get_task_param("path") filename = self.get_task_param("filename") R = self.get_task_param("R") D = self.get_task_param("D") T = self.get_task_param("T") logg = self.get_task_param("logg") z = self.get_task_param("z") if not filename and not path: # TODO add Phoenix system path option if not path path = os.environ.get("PHOENIX_PATH", None) if ( filename ): # if filename is given it ignores anything else and it uses that fits_file_name = filename else: if path: if not os.path.exists(path): # TODO test this self.error( "to load a phoenix model indicate a model file name or the phonix path" ) raise OSError( "to load a phoenix model indicate a model file name or the phonix path" ) if z is None: z = 0.0 self.debug("star metallicity missing. zero is assumed.") if T is None: self.error("star temperature missing") raise KeyError("star temperature missing") if hasattr(T, "unit"): T = T.to(u.K) else: T = T * u.K self.debug("no units found for T: Kelvin are assumed.") if not logg: self.error("star logg missing") raise KeyError("star logg missing") fits_file_name = self.get_phoenix_model_filename( path, T, logg, z ) else: self.error("phoenix path missing") raise OSError("phoenix path missing") sed = self.load(fits_file_name) sed.metadata["phoenix_file"] = fits_file_name if R is None: self.error("star radius missing") raise KeyError("star radius missing") if D is None: self.error("star distance missing") raise KeyError("star distance missing") R = checks.check_units(R, u.m, self) D = checks.check_units(D, u.m, self) sed *= (R / D) ** 2 self.debug("phoenix sed loaded: {}".format(sed.data)) self.set_output(sed)
[docs] def load(self, ph_file): """ Returs a sed given a Phoenix filename Parameters ---------- ph_file: str filename Returns --------- :class:`~exosim.models.signal.Sed` Star Sed """ with fits.open(ph_file) as hdu: str_unit = hdu[1].header["TUNIT1"] wl_ph = hdu[1].data.field("Wavelength") * u.Unit(str_unit) str_unit = hdu[1].header["TUNIT2"] sed_ph = hdu[1].data.field("Flux") * u.Unit(str_unit) idx = np.nonzero(np.diff(wl_ph)) wl_ph = wl_ph[idx] sed_ph = sed_ph[idx] sed = signal.Sed(spectral=wl_ph, data=sed_ph) return sed
[docs] def get_phoenix_model_filename(self, path, T, logg, z): """ It returns the name of the phoenix file that best matches the input star parameters. Parameters ---------- path: str Phoenix directory path T: :class:`~astropy.units.Quantity` star temperature logg: float star logG z: float star metallicity Returns ------- str Phoenix file name """ sed_name = glob.glob(os.path.join(path, "*.BT-Settl.spec.fits.gz")) if len(sed_name) == 0: self.error("No stellar SED files found") raise OSError("No stellar SED files found") sed_T_list = np.array( [float(os.path.basename(k)[3:8]) for k in sed_name] ) sed_Logg_list = np.array( [float(os.path.basename(k)[9:12]) for k in sed_name] ) sed_Z_list = np.array( [float(os.path.basename(k)[13:16]) for k in sed_name] ) idx = np.argmin( np.abs(sed_T_list - np.round(T.value / 100.0)) + np.abs(sed_Logg_list - logg) + np.abs(sed_Z_list - z) ) ph_file = sed_name[idx] self.debug("phoenix file selected: {}".format(ph_file)) return ph_file