Source code for pyEDITH.astrophysical_scene

import numpy as np
from scipy.interpolate import interp1d
import astropy.units as u
import astropy.constants as const
from .units import *
from . import utils
from astropy.coordinates import SkyCoord
import logging

logger = logging.getLogger("pyEDITH")
# def calc_flux_zero_point_synphot(lam: u.Quantity):

#     # calculates a zeropoint Vega spectrum using boxcar filters
#     from synphot import SourceSpectrum, SpectralElement, Observation, Box1D
#     from synphot.exceptions import DisjointError
#     import numpy as np
#     import astropy.units as u

#     # load the vega spectrum
#     vega = SourceSpectrum.from_vega()

#     # convert wavelength grid to angstrom
#     lam = lam.to(u.AA)
#     dlam = np.gradient(lam)
#     dlam = dlam.to(u.AA)

#     vega_fluxes = np.empty(len(lam))

#     # treat the wavelength bins as box filters
#     for i in range(len(lam)):
#         filt_ctr = lam[i]
#         filt_width = dlam[i]

#         # boxcar filter
#         boxcar = Box1D(amplitude=1, x_0=filt_ctr.value, width=filt_width.value)
#         filt = SpectralElement(boxcar, wavelengths=lam)

#         # calculate an observation of Vega with filter
#         try:
#             obs = Observation(vega, filt, binset=lam)
#         except :
#             #print("Box filter extends beyond wavelength range. Setting flux as nan for this spectral bin.")
#             vega_fluxes[i] = np.nan
#             continue

#         # integrate observation
#         flux_photlam = obs.effstim(flux_unit="photlam")

#         # convert to pyEDITH units
#         flux = flux_photlam.to(u.photon/u.s/u.cm**2/u.nm)

#         vega_fluxes[i] = flux.value

#     return vega_fluxes * u.photon/u.s/u.cm**2/u.nm


[docs] def calc_flux_zero_point( lambd: u.Quantity, output_unit: str = "pcgs", perlambd: bool = False, AB: bool = False, ) -> u.Quantity: """ Calculate the flux zero point for given wavelengths. This function calculates the flux zero point for a given set of wavelengths. By default, it returns Johnson zero points. If the 'AB' flag is set, it will return AB flux zero points. Parameters ---------- lambd : astropy.units.Quantity Wavelengths. Must be in units of length. output_unit : str, optional Output unit type. Possible values are: - "jy" for output in Jy - "cgs" for output in erg s^-1 cm^-2 Hz^-1 - "pcgs" for output in photons s^-1 cm^-2 Hz^-1 Default is "pcgs". perlambd : bool, optional If True, convert all output values to per wavelength instead of per frequency. Default is False. AB : bool, optional If True, use AB magnitude system instead of Johnson. Default is False. Returns ------- astropy.units.Quantity Flux zero points for the given wavelengths in the specified units. Raises ------ ValueError If output_unit is not specified or is invalid, or if incompatible options are selected. """ if output_unit not in ["jy", "cgs", "pcgs"]: raise ValueError( 'Invalid output_unit value. Possible values are: "jy" for output in Jy, ' '"cgs" for output in erg s^-1 cm^-2 Hz^-1, ' '"pcgs" for output in photons s^-1 cm^-2 Hz^-1.' ) if output_unit == "jy" and perlambd: raise ValueError("Cannot set Jy and perlambd") # Ensure lambd is in the correct units (cm for CGS) lambd = lambd.to(CM) # Convert constants to CGS h_cgs = const.h.cgs.value c_cgs = const.c.cgs.value # Calculate the zero point if AB: # AB magnitude system f0 = 3631 * JANSKY # AB zero point else: # Johnson magnitude system known_lambd = ( np.array( [0.36, 0.44, 0.55, 0.71, 0.97, 1.25, 1.60, 2.22, 3.54, 4.80, 10.6, 21.0] ) * WAVELENGTH ) known_zeropoint_jy = ( np.array([1823, 4130, 3781, 2941, 2635, 1603, 1075, 667, 288, 170, 36, 9.4]) * JANSKY ) # Interpolation in log-log space interp = interp1d( np.log10(known_lambd.value), np.log10(known_zeropoint_jy.value), kind="cubic", fill_value="extrapolate", ) logf0 = interp(np.log10(lambd.to_value(WAVELENGTH))) f0 = 10.0**logf0 * JANSKY # Convert to desired output units if output_unit == "cgs": # Convert to erg / (s * cm^2 * Hz) f0 = f0.to(SPECTRAL_FLUX_DENSITY_CGS) elif output_unit == "pcgs": # Convert to photons / (s * cm^2 * Hz) f0 = f0.to( PHOTON_FLUX_DENSITY_HZ, equivalencies=u.spectral_density(lambd), ) # Convert to per wavelength if requested if perlambd: if output_unit == "cgs": f0 = f0.to( SPECTRAL_FLUX_DENSITY_CGS_WAVELENGTH, equivalencies=u.spectral_density(lambd), ) elif output_unit == "pcgs": f0 = f0.to( PHOTON_FLUX_DENSITY_CGS_WAVELENGTH, equivalencies=u.spectral_density(lambd), ) logger.info(f"Flux zero point calculated at {lambd} in units of {f0.unit}") return f0
[docs] def calc_exozodi_flux( M_V: u.Quantity, vmag: u.Quantity, nexozodis: u.Quantity, lambd: u.Quantity, lambdmag: u.Quantity, ) -> u.Quantity: """ Calculate the exozodiacal light flux for given stellar and observational parameters. This function computes the exozodiacal light flux for a set of stars, taking into account their absolute and apparent magnitudes, the number of exozodis, and the observational wavelengths. Parameters ---------- M_V : Quantity V band absolute magnitude of stars. vmag : Quantity V band apparent magnitude of stars. nexozodis : Quantity Number of exozodis for each star. lambd : Quantity Wavelength in microns (vector of length nlambd). lambdmag : Quantity Apparent magnitude of stars at each lambd (array nlambd). F0lambd : Quantity Flux zero point at wavelength lambd (vector of length nlambd). Returns ------- Quantity Exozodi surface brightness in units of photons s^-1 cm^-2 arcsec^-2 nm^-1 / F0. This is equivalent to 10^(-0.4*magOmega_EZ). """ if len(lambd) > 1: if len(lambd) != lambdmag.shape[0]: raise ValueError( "ERROR. lambdmag must have dimensions nstars x nlambd, where nlambd is the length of lambd." ) vmag_1zodi = ( 22.0 * SURFACE_BRIGHTNESS ) # V band surface brightness of 1 zodi in mag arcsec^-2 vflux_1zodi = 10.0 ** ( -0.4 * vmag_1zodi.value ) # V band flux (modulo the F0 factor out front) M_V_sun = 4.83 * MAGNITUDE # V band absolute magnitude of Sun # Now, multiply by the ratio of the star's counts received at lambd to those # received at V band nlambd = len(lambd) flux_exozodi_lambd = np.zeros((nlambd)) * DIMENSIONLESS # modulo factor F0 # Adjust flux for each wavelength for ilambd in range(nlambd): flux_exozodi_lambd[ilambd] = ( nexozodis * vflux_1zodi * 10.0 ** (-0.4 * (M_V - M_V_sun).value) * 10.0 ** (-0.4 * (lambdmag[ilambd] - vmag).value) ) return ( flux_exozodi_lambd * INV_SQUARE_ARCSEC ) # CONVERTING FROM FLUX TO SURFACE BRIGHTNESS (modulo F0 that gets multiplied later)
[docs] def calc_zodi_flux( dec: u.Quantity, ra: u.Quantity, lambd: u.Quantity, F0: u.Quantity, # starshade: bool = False, # ss_elongation: u.Quantity = None, ) -> u.Quantity: """ Calculate the zodiacal light flux for given celestial coordinates and wavelengths. This function computes the zodiacal light flux based on the target's position in the sky, observation wavelengths, and whether a starshade is used. It uses the model from Leinert et al. (1998) to calculate the zodiacal light intensity. Parameters ---------- dec : Quantity Declination of targets in degrees (J2000 equatorial coordinate). ra : Quantity Right ascension of targets in degrees (J2000 equatorial coordinate). lambd : Quantity Wavelengths in microns (vector of length nlambd). F0 : Quantity Flux zero points at wavelengths lambd (vector of length nlambd). Returns ------- np.ndarray Zodi surface brightness in units of photons s^-1 cm^-2 arcsec^-2 nm^-1 / F0. This is equivalent to 10^(-0.4*magOmega_ZL). - Multiply by F0 to get photons s^-1 cm^-2 arcsec^-2 nm^-1 - Multiply by energy of photons to get erg s^-1 cm^-2 arcsec^-2 nm^-1 The output array has dimensions (nlambd, nstars). Raises ------ ValueError If F0 and lambd have different lengths, or if starshade mode is inconsistent with ss_elongation. Note ---- - The function uses the zodiacal light model from Leinert et al. (1998). - For coronagraph mode, it assumes observations near solar longitude of 135 degrees. - Starshade functionality is currently not fully implemented. References: Leinert, C., et al. (1998). The 1997 reference of diffuse night sky brightness. Astronomy and Astrophysics Supplement Series, 127(1), 1-99. """ # if starshade and ss_elongation is None: # raise ValueError( # "ERROR. You have set the STARSHADE flag. Must specify SS_ELONGATION in degrees." # ) # if not starshade and ss_elongation is not None: # raise ValueError( # "ERROR. You must enable STARSHADE mode if you are setting SS_ELONGATION in degrees." # ) # Convert equatorial coordinates to ecliptic coordinates coords = SkyCoord(ra=ra, dec=dec, frame="icrs") ecl_coords = coords.barycentrictrueecliptic beta = ecl_coords.lat.rad # all we need is the sine of the latitude # Use absolute value of the sin of beta (symmetry about ecliptic plane) sinbeta = np.abs(np.sin(beta)) # SOURCE: Leinert et al. (1998) # Define solar longitude and beta values for interpolation beta_vector = np.array([0.0, 5, 10, 15, 20, 25, 30, 45, 60, 75]) * DEG sollong_vector = ( np.array( [ 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180.0, ] ) * DEG ) # Table 17 values (assumed to be in some brightness units) table17 = ( np.array( [ [-1, -1, -1, 3140, 1610, 985, 640, 275, 150, 100], [-1, -1, -1, 2940, 1540, 945, 625, 271, 150, 100], [-1, -1, 4740, 2470, 1370, 865, 590, 264, 148, 100], [11500, 6780, 3440, 1860, 1110, 755, 525, 251, 146, 100], [6400, 4480, 2410, 1410, 910, 635, 454, 237, 141, 99], [3840, 2830, 1730, 1100, 749, 545, 410, 223, 136, 97], [2480, 1870, 1220, 845, 615, 467, 365, 207, 131, 95], [1650, 1270, 910, 680, 510, 397, 320, 193, 125, 93], [1180, 940, 700, 530, 416, 338, 282, 179, 120, 92], [910, 730, 555, 442, 356, 292, 250, 166, 116, 90], [505, 442, 352, 292, 243, 209, 183, 134, 104, 86], [338, 317, 269, 227, 196, 172, 151, 116, 93, 82], [259, 251, 225, 193, 166, 147, 132, 104, 86, 79], [212, 210, 197, 170, 150, 133, 119, 96, 82, 77], [188, 186, 177, 154, 138, 125, 113, 90, 77, 74], [179, 178, 166, 147, 134, 122, 110, 90, 77, 73], [179, 178, 165, 148, 137, 127, 116, 96, 79, 72], [196, 192, 179, 165, 151, 141, 131, 104, 82, 72], [230, 212, 195, 178, 163, 148, 134, 105, 83, 72], ] ) * SPECTRAL_RADIANCE ) # For coronagraph, assume observations near solar longitude of 135 degrees j = np.argmin(np.abs(sollong_vector - 135 * DEG)) k = np.argmin(np.abs(sollong_vector - 90 * DEG)) # Interpolate to get zodi brightness factor from scipy.interpolate import interp1d interp = interp1d( np.sin(beta_vector), table17[j] / table17[k, 0], kind="cubic", fill_value="extrapolate", ) f = interp(sinbeta) * DIMENSIONLESS # Wavelength dependence (fits to Table 19 in Leinert et al 1998) zodi_lambd = ( np.array( [ 0.2, 0.3, 0.4, 0.5, 0.7, 0.9, 1.0, 1.2, 2.2, 3.5, 4.8, 12, 25, 60, 100, 140, ] ) * WAVELENGTH ) zodi_blambd = ( np.array( [ 2.5e-8, 5.3e-7, 2.2e-6, 2.6e-6, 2.0e-6, 1.3e-6, 1.2e-6, 8.1e-7, 1.7e-7, 5.2e-8, 1.2e-7, 7.5e-7, 3.2e-7, 1.8e-8, 3.2e-9, 6.9e-10, ] ) * SPECTRAL_RADIANCE ) # Convert to erg s^-1 cm^-2 arcsec^-2 nm^-1 zodi_blambd = zodi_blambd.to(SPECTRAL_RADIANCE_CGS) # Interpolate to get zodi brightness at desired wavelengths interp = interp1d( np.log10(zodi_lambd.value), np.log10(zodi_blambd.value), kind="cubic" ) blambd = 10 ** interp(np.log10(lambd.to_value(WAVELENGTH))) * zodi_blambd.unit # Convert to photon flux # I90fabsfco = blambd / (u.h * u.c / lambd) I90fabsfco = blambd.to( PHOTON_SPECTRAL_RADIANCE, equivalencies=u.spectral_density(lambd), ) # Divide by F0 I90fabsfco = I90fabsfco / F0 # Calculate final zodi flux nlambd = len(lambd) flux_zodi = u.Quantity(np.zeros((nlambd)), unit=I90fabsfco.unit) for ilambd in range(nlambd): flux_zodi[ilambd] = f * I90fabsfco[ilambd] return flux_zodi # 1/arcsec^2 (UNITS OF SPECTRAL RADIANCE)
[docs] class AstrophysicalScene: """ A class representing an astrophysical scene for exoplanet observation simulations. This class encapsulates various astrophysical parameters and methods to calculate zodi and exozodi fluxes for a set of target stars and their potential exoplanets. Parameters ---------- dist : float Distance to stars in parsecs vmag : float Stellar magnitudes at V band mag : ndarray Stellar magnitudes at desired wavelengths stellar_angular_diameter_arcsec : float Angular diameter of stars in arcseconds nzodis : float Amount of exozodi around target stars in "zodis" ra : float Right ascension of target stars in degrees dec : float Declination of target stars in degrees separation : float Separation of planets in arcseconds xp : float X-coordinate of planets in arcseconds yp : float Y-coordinate of planets in arcseconds deltamag : ndarray Magnitude difference between planets and host stars min_deltamag : float Brightest planet to resolve at the IWA F0V : float Flux zero point for V band F0 : ndarray Flux zero points for prescribed wavelengths M_V : float Absolute V band magnitudes of target stars Fzodi_list : ndarray Zodiacal light fluxes Fexozodi_list : ndarray Exozodiacal light fluxes Fbinary_list : ndarray Binary star fluxes (currently ignored) Fp_over_Fs : ndarray Flux of planets """ def __init__(self): """ Initialize the AstrophysicalScene object with default values for output arrays. """ # Constants # Zero Point Flux in the V Band self.F0V = ( calc_flux_zero_point( lambd=0.55 * WAVELENGTH, output_unit="pcgs", perlambd=True ) ).to( PHOTON_FLUX_DENSITY, equivalencies=u.spectral_density(0.55 * WAVELENGTH) ) # convert to photons cm^-2 nm^-1 s^-1 return
[docs] def load_configuration(self, parameters: dict) -> None: """ Load configuration parameters for the simulation from a dictionary. This method initializes various attributes of the AstrophysicalScene object using the provided parameters dictionary. Parameters ---------- parameters : dict A dictionary containing simulation parameters including target star parameters, planet parameters, and observational parameters. Returns ------- None Raises ------ ValueError If a required parameter is missing from the input dictionary. """ # -------- INPUTS --------- # distance to star (pc) # used to be (ntargs array) now scalar self.dist = parameters["distance"] * DISTANCE # Calculate zero point flux at lambda if "F0" in parameters.keys(): self.F0 = parameters["F0"] * PHOTON_FLUX_DENSITY else: self.F0 = calc_flux_zero_point( lambd=parameters["wavelength"] * WAVELENGTH, output_unit="pcgs", perlambd=True, ).to( PHOTON_FLUX_DENSITY, equivalencies=u.spectral_density(parameters["wavelength"] * WAVELENGTH), ) # [nlambda] # look for delta_mag_min or Fp_min/Fs. If not there, default to vals. This hides this feature from the user. if "delta_mag_min" in parameters: self.min_deltamag = parameters["delta_mag_min"] * MAGNITUDE else: self.min_deltamag = 26 * MAGNITUDE if "Fp_min/Fs" in parameters: self.Fp_min_over_Fs = parameters["Fp_min/Fs"] * DIMENSIONLESS else: self.Fp_min_over_Fs = 1e-10 * DIMENSIONLESS # Determine if user provided magnitudes or fluxes if all(param in parameters for param in ["magV", "mag", "delta_mag"]): # User provided magnitudes # stellar mag (not absolute mag!!) at V band # used to be (ntargs array) now scalar self.vmag = parameters["magV"] * MAGNITUDE # stellar mag at desired lambd # used to be (ntargs array) now scalar self.mag = parameters["mag"] * MAGNITUDE # difference in mag between planet and host star # (nmeananom x norbits x ntargs array) self.deltamag = parameters["delta_mag"] * MAGNITUDE # brightest planet to resolve w/ photon counting detector evaluated at # the IWA, sets the time between counts (ntargs array) # Convert magnitudes to RELATIVE fluxes (modulo F0) self.Fs_over_F0 = 10 ** (-0.4 * self.mag.value) * DIMENSIONLESS self.Fp_over_Fs = 10 ** (-0.4 * self.deltamag.value) * DIMENSIONLESS self.Fp_min_over_Fs = 10 ** (-0.4 * self.min_deltamag.value) * DIMENSIONLESS elif all(param in parameters for param in ["Fstar_10pc", "Fp/Fs"]): # Load fluxes Fstar_10pc = parameters["Fstar_10pc"] * PHOTON_FLUX_DENSITY if ( "FstarV_10pc" not in parameters and parameters["observing_mode"] == "IFS" ): logger.warning( "`FstarV_10pc` not specified in parameters. Calculating internally..." ) # from synphot import SourceSpectrum, SpectralElement, Observation # from synphot.models import Empirical1D # # if given a spectrum, calculate the v-band flux using synphot # tempSpec = SourceSpectrum(Empirical1D, points=parameters["wavelength"] * WAVELENGTH, lookup_table=Fstar_10pc) # johnson_v = SpectralElement.from_filter('johnson_v') # obsTemp = Observation(tempSpec, johnson_v) # # # unit conversion is necessary because V-band flux is in units of [photons/s/cm^2], # # and not PHOTON_FLUX_DENSITY since an integration over wavelength was performed. # # Doing this to make the units line up. # Fstar_V_10pc = obsTemp.integrate(flux_unit=PHOTON_FLUX_DENSITY).value * PHOTON_FLUX_DENSITY # the above is for calculating the zero-point over a bandpass. What is actually going on here is # that the zeropoint calculation is for a single wavelength, lam=0.55 um. # let's change this up and interpolate func = interp1d(parameters["wavelength"], Fstar_10pc) Fstar_V_10pc = ( func(0.55) * PHOTON_FLUX_DENSITY ) # interpolate to single wavelength, not a bandpass elif ( "FstarV_10pc" not in parameters and parameters["observing_mode"] == "IMAGER" ): raise ValueError("FstarV_10pc missing in parameters.") else: Fstar_V_10pc = parameters["FstarV_10pc"] * PHOTON_FLUX_DENSITY Fstar_absolute = Fstar_10pc * (10 * DISTANCE / self.dist) ** 2 Fstar_V_absolute = Fstar_V_10pc * (10 * DISTANCE / self.dist) ** 2 self.Fp_over_Fs = parameters["Fp/Fs"] * DIMENSIONLESS # Convert to relative fluxes (used internally) self.Fs_over_F0 = (Fstar_absolute / self.F0).to(DIMENSIONLESS) # Calculate vmag (needed for zodi) self.vmag = -2.5 * np.log10(Fstar_V_absolute / self.F0V) * MAGNITUDE self.mag = -2.5 * np.log10(Fstar_absolute / self.F0) * MAGNITUDE # Calculate deltamag and min_deltamag (used only to validate) self.deltamag = -2.5 * np.log10(self.Fp_over_Fs) * MAGNITUDE self.min_deltamag = -2.5 * np.log10(self.Fp_min_over_Fs) * MAGNITUDE else: missing_mag_params = [ param for param in ["magV", "mag", "delta_mag"] if param not in parameters ] missing_flux_params = [ param for param in ["Fstar_10pc", "FstarV_10pc", "Fp/Fs"] if param not in parameters ] raise ValueError( f"Insufficient parameters provided. You must provide either:\n" f"1. All magnitude parameters: {', '.join(['magV', 'mag', 'delta_mag'])}\n" f" Missing: {', '.join(missing_mag_params)}\n" f"OR\n" f"2. All flux parameters: {', '.join(['Fstar_10pc', 'FstarV_10pc', 'Fp/Fs',])}\n" f" Missing: {', '.join(missing_flux_params)}" ) # convert stellar radius into stellar angular diameter (in arcsec) stellar_radius_arcsec = ( to_arcsec( (parameters["stellar_radius"] * const.R_sun).to(LENGTH), (self.dist.to(LENGTH)), ) * ARCSEC ) # angular diameter of star (arcsec) # used to be (ntargs array) now scalar self.stellar_angular_diameter_arcsec = 2 * stellar_radius_arcsec # amount of exozodi around target star ("zodis") # used to be (ntargs array) now scalar self.nzodis = parameters["nzodis"] * ZODI # right ascension of target star used to estimate zodi (deg) # used to be (ntargs array) now scalar self.ra = parameters["ra"] * DEG # declination of target star used to estimate zodi (deg) # used to be (ntargs array) now scalar self.dec = parameters["dec"] * DEG # Planet parameters # separation of planet (arcseconds) (nmeananom x norbits x ntargs array) # NOTE FOR NOW IT IS ASSUMED TO BE ON THE X AXIS # SO THAT XP = SP (input) and YP = 0 if "separation" in parameters.keys(): self.separation = parameters["separation"] * ARCSEC elif "semimajor_axis" in parameters.keys(): self.separation = ( to_arcsec( (parameters["semimajor_axis"] * const.au).to(LENGTH), (self.dist.to(LENGTH)), ) * ARCSEC ) else: raise ValueError( "Either separation [arcsec] or semimajor_axis [AU] must be provided." ) self.xp = self.separation.copy() self.yp = self.separation.copy() * 0.0 # set the exozodi PPF if "ez_PPF" in parameters.keys(): if not isinstance(parameters["ez_PPF"], (list, np.ndarray)): self.ez_PPF = parameters["ez_PPF"] * np.ones_like(self.Fp_over_Fs) else: assert len(parameters["ez_PPF"]) == len( self.Fp_over_Fs ), "length of ez_PPF does not match length of Fp_over_Fs" self.ez_PPF = np.array(parameters["ez_PPF"]) else: logger.warning( "ez_PPF not set. Assuming EZ subtraction to Poisson limit (ez_PPF = inf)" ) self.ez_PPF = np.inf * np.ones_like(self.Fp_over_Fs)
[docs] def calculate_zodi_exozodi(self, parameters: dict) -> None: """ Calculate zodiacal and exozodiacal light fluxes for the given observation. This method computes the flux zero points, zodiacal light fluxes, and exozodiacal light fluxes for the target stars based on the provided observation parameters. Parameters ---------- parameters : dict A dictionary containing simulation parameters including target star parameters, planet parameters, and observational parameters. Must include a "wavelength" key containing the wavelength array Returns ------- None Raises ------ AttributeError If the scene has not been properly configured (missing required attributes) KeyError If parameters dictionary is missing required keys """ # Validate that scene has been properly configured required_attributes = ["vmag", "dist", "dec", "ra", "F0", "nzodis", "mag"] missing_attrs = [ attr for attr in required_attributes if not hasattr(self, attr) ] if missing_attrs: raise AttributeError( f"AstrophysicalScene must be configured before calculating zodi/exozodi. " f"Missing attributes: {', '.join(missing_attrs)}. " f"Please call load_configuration() first." ) # Validate parameters dictionary if "wavelength" not in parameters: raise KeyError("parameters dictionary must contain 'wavelength' key") # calculate flux at zero point for the V band and the prescribed lambda self.M_V = ( self.vmag - 5.0 * np.log10(self.dist.value) * MAGNITUDE + 5.0 * MAGNITUDE ) # calculate absolute V band mag of target self.Fzodi_list = calc_zodi_flux( self.dec, self.ra, parameters["wavelength"] * WAVELENGTH, self.F0 ) # [nlambda] self.Fexozodi_list = calc_exozodi_flux( self.M_V, self.vmag, self.nzodis, parameters["wavelength"] * WAVELENGTH, self.mag, ) # [nlambda] self.Fbinary_list = ( np.full((len(parameters["wavelength"])), 0.0) * DIMENSIONLESS ) # this code ignores stray light from binaries # [nlambda]
[docs] def validate_configuration(self): """ Check that mandatory variables are there and have the right format. There can be other variables, but they are not needed for the calculation. """ expected_args = { "dist": DISTANCE, # "vmag": MAGNITUDE, # "mag": MAGNITUDE, "stellar_angular_diameter_arcsec": ARCSEC, "nzodis": ZODI, "ra": DEG, "dec": DEG, "separation": ARCSEC, "xp": ARCSEC, "yp": ARCSEC, # "deltamag": MAGNITUDE, # "min_deltamag": MAGNITUDE, "F0V": PHOTON_FLUX_DENSITY, "F0": PHOTON_FLUX_DENSITY, # "M_V": MAGNITUDE, "Fzodi_list": INV_SQUARE_ARCSEC, "Fexozodi_list": INV_SQUARE_ARCSEC, "Fbinary_list": DIMENSIONLESS, "Fp_over_Fs": DIMENSIONLESS, "Fs_over_F0": DIMENSIONLESS, "ez_PPF": DIMENSIONLESS, } utils.validate_attributes(self, expected_args)
[docs] def regrid_spectra(self, parameters, observation): """function to re-grid onto a new wavelength grid if the user specified this option""" # spectra to regrid: F0, Fzodi_list, Fexozodi_list, Fbinary_list, Fp_over_Fs, Fs_over_F0 logger.info("Re-gridding spectra onto ETC wavelength grid...") self.F0 = utils.regrid_spec_gaussconv( parameters["wavelength"], self.F0, observation.wavelength.value, observation.delta_wavelength.value, ) self.Fzodi_list = utils.regrid_spec_gaussconv( parameters["wavelength"], self.Fzodi_list, observation.wavelength.value, observation.delta_wavelength.value, ) self.Fexozodi_list = utils.regrid_spec_gaussconv( parameters["wavelength"], self.Fexozodi_list, observation.wavelength.value, observation.delta_wavelength.value, ) self.Fbinary_list = utils.regrid_spec_gaussconv( parameters["wavelength"], self.Fbinary_list, observation.wavelength.value, observation.delta_wavelength.value, ) self.Fp_over_Fs = utils.regrid_spec_gaussconv( parameters["wavelength"], self.Fp_over_Fs, observation.wavelength.value, observation.delta_wavelength.value, ) self.Fs_over_F0 = utils.regrid_spec_gaussconv( parameters["wavelength"], self.Fs_over_F0, observation.wavelength.value, observation.delta_wavelength.value, ) self.ez_PPF = utils.regrid_spec_gaussconv( parameters["wavelength"], self.ez_PPF, observation.wavelength.value, observation.delta_wavelength.value, )