Source code for rubin_sim.phot_utils.photometric_parameters

__all__ = ("PhotometricParameters", "DustValues")

import os

import numpy as np
from rubin_scheduler.data import get_data_dir

from .bandpass import Bandpass
from .sed import Sed


[docs] class DustValues: """Calculate extinction values Parameters ---------- R_v : `float` Extinction law parameter (3.1). bandpassDict : `dict` A dict with keys of filtername and values of rubin_sim.phot_utils.Bandpass objects. Default of None will load the standard ugrizy bandpasses. ref_ev : `float` The reference E(B-V) value to use. Things in MAF assume 1. Note ---- The value that dust_values calls "ax1" is equivalent to r_x in any filter. And r_x * ebv = A_x (the extinction due to dust in any bandpass). DustValues.r_x is also provided as a copy of DustValues.ax1 .. eventually ax1 may be deprecated in favor of r_x. """ def __init__(self, r_v=3.1, bandpass_dict=None, ref_ebv=1.0): # Calculate dust extinction values self.ax1 = {} if bandpass_dict is None: bandpass_dict = {} root_dir = os.path.join(get_data_dir(), "throughputs", "baseline") for f in ["u", "g", "r", "i", "z", "y"]: bandpass_dict[f] = Bandpass() bandpass_dict[f].read_throughput(os.path.join(root_dir, f"total_{f}.dat")) for filtername in bandpass_dict: wavelen_min = bandpass_dict[filtername].wavelen.min() wavelen_max = bandpass_dict[filtername].wavelen.max() testsed = Sed() testsed.set_flat_sed(wavelen_min=wavelen_min, wavelen_max=wavelen_max, wavelen_step=1.0) self.ref_ebv = ref_ebv # Calculate non-dust-extincted magnitude flatmag = testsed.calc_mag(bandpass_dict[filtername]) # Add dust a, b = testsed.setup_ccm_ab() testsed.add_dust(a, b, ebv=self.ref_ebv, r_v=r_v) # Calculate difference due to dust when EBV=1.0 # (m_dust = m_nodust - Ax, Ax > 0) self.ax1[filtername] = testsed.calc_mag(bandpass_dict[filtername]) - flatmag # Add the R_x term, to start to transition toward this name. self.r_x = self.ax1.copy()
def make_dict(value, bandpass_names=("u", "g", "r", "i", "z", "y", "any")): newdict = {} for f in bandpass_names: newdict[f] = value return newdict class DefaultPhotometricParameters: """ This class will just contain a bunch of dict which store the default PhotometricParameters for LSST Bandpasses Users should not access this class (which is why it is not included in the __all__ declaration for this file). It is only used to initialize PhotometricParameters for a bandpass name. """ # Obviously, some of these parameters (effarea, gain, platescale, # darkcurrent, and readnoise) will not change as a function of bandpass; # we are just making them dicts here to be consistent with # everything else (and to make it possible for # PhotometricParameters to access them using the bandpass name # passed to its constructor) # # Note: all dicts contain an 'any' key which will be the default # value if an unknown bandpass is asked for # # 'any' values should be kept consistent with r band bandpass_names = ["u", "g", "r", "i", "z", "y", "any"] # exposure time in seconds exptime_sec = 15.0 exptime = make_dict(exptime_sec) # number of exposures nexp_n = 2 nexp = make_dict(nexp_n) # effective area in cm^2 effarea_cm2 = np.pi * (6.423 / 2.0 * 100) ** 2 effarea = make_dict(effarea_cm2) # electrons per ADU gain_adu = 2.3 gain = make_dict(gain_adu) # electrons per pixel per exposure readnoise_e = 8.8 readnoise = make_dict(readnoise_e) # electrons per pixel per second darkcurrent_e = 0.2 darkcurrent = make_dict(darkcurrent_e) # electrons per pixel per exposure othernoise_e = 0.0 othernoise = make_dict(othernoise_e) # arcseconds per pixel platescale_as = 0.2 platescale = make_dict(platescale_as) # systematic squared error in magnitudes # see Table 14 of the SRD document # https://docushare.lsstcorp.org/docushare/dsweb/Get/LPM-17 sigma_sys = { "u": 0.0075, "g": 0.005, "r": 0.005, "i": 0.005, "z": 0.0075, "y": 0.0075, "any": 0.005, } class PhotometricParameters: def __init__( self, exptime=None, nexp=None, effarea=None, gain=None, readnoise=None, darkcurrent=None, othernoise=None, platescale=None, sigma_sys=None, bandpass=None, ): """Store photometric parameters for SNR calculations. Parameters ---------- exptime : `float` Exposure time in seconds (per exposure). None will default to value from DefaultPhotometricParameters. nexp : `int` Number of exposures per visit. None will default to value from DefaultPhotometricParameters. effarea : `float` Effective area in cm^2. None will default to value from DefaultPhotometricParameters. gain : `float` Electrons per ADU. None will default to value from DefaultPhotometricParameters. readnoise : `float` Electrons per pixel per exposure. None will default to value from DefaultPhotometricParameters. darkcurrent : `float` Electons per pixel per second. None will default to value from DefaultPhotometricParameters. othernoise : `float` Electrons per pixel per exposure. None will default to value from DefaultPhotometricParameters. platescale : `float` Arcseconds per pixel. None will default to value from DefaultPhotometricParameters. sigma_sys : `float` Systematic error in magnitudes. None will default to value from DefaultPhotometricParameters. bandpass : `str` The name of the bandpass for these parameters. Examples -------- If `bandpass` is set to an LSST bandpass, the constructor will initialize PhotometricParameters to LSST default values for that bandpass, excepting any parameters that have been set by hand. e.g. >>> myPhotParams = PhotometricParameters(nexp=3, bandpass='u') will initialize a PhotometricParameters object to `u` band defaults, except with 3 exposures instead of 2. A bandpass value of None will use defaults from LSST `r` band where appropriate. """ # readnoise, darkcurrent and othernoise are measured in electrons. # This is taken from the specifications document LSE-30 on Docushare # Section 3.4.2.3 states that the total noise per pixel shall # be 12.7 electrons per visit which the defaults sum to # (remember to multply darkcurrent by the number of seconds # in an exposure=15). [9 e- per 15 second exposure] self._exptime = None self._nexp = None self._effarea = None self._gain = None self._platescale = None self._sigma_sys = None self._readnoise = None self._darkcurrent = None self._othernoise = None self._bandpass = bandpass defaults = DefaultPhotometricParameters() if bandpass is None: bandpass_key = "any" # This is so we do not set the self._bandpass member variable # without the user's explicit consent, but we can still access # default values from the PhotometricParameterDefaults else: bandpass_key = bandpass if bandpass_key in defaults.bandpass_names: self._exptime = defaults.exptime[bandpass_key] self._nexp = defaults.nexp[bandpass_key] self._effarea = defaults.effarea[bandpass_key] self._gain = defaults.gain[bandpass_key] self._platescale = defaults.platescale[bandpass_key] self._sigma_sys = defaults.sigma_sys[bandpass_key] self._readnoise = defaults.readnoise[bandpass_key] self._darkcurrent = defaults.darkcurrent[bandpass_key] self._othernoise = defaults.othernoise[bandpass_key] if exptime is not None: self._exptime = exptime if nexp is not None: self._nexp = nexp if effarea is not None: self._effarea = effarea if gain is not None: self._gain = gain if platescale is not None: self._platescale = platescale if sigma_sys is not None: self._sigma_sys = sigma_sys if readnoise is not None: self._readnoise = readnoise if darkcurrent is not None: self._darkcurrent = darkcurrent if othernoise is not None: self._othernoise = othernoise failure_message = "" failure_ct = 0 if self._exptime is None: failure_message += "did not set exptime\n" failure_ct += 1 if self._nexp is None: failure_message += "did not set nexp\n" failure_ct += 1 if self._effarea is None: failure_message += "did not set effarea\n" failure_ct += 1 if self._gain is None: failure_message += "did not set gain\n" failure_ct += 1 if self._platescale is None: failure_message += "did not set platescale\n" failure_ct += 1 if self._sigma_sys is None: failure_message += "did not set sigma_sys\n" failure_ct += 1 if self._readnoise is None: failure_message += "did not set readnoise\n" failure_ct += 1 if self._darkcurrent is None: failure_message += "did not set darkcurrent\n" failure_ct += 1 if self._othernoise is None: failure_message += "did not set othernoise\n" failure_ct += 1 if failure_ct > 0: raise RuntimeError("In PhotometricParameters:\n%s" % failure_message) @property def bandpass(self): """ The name of the bandpass associated with these parameters. Can be None. """ return self._bandpass @bandpass.setter def bandpass(self, value): raise RuntimeError( "You should not be setting bandpass on the fly; " + "Just instantiate a new case of PhotometricParameters" ) @property def exptime(self): """ exposure time in seconds """ return self._exptime @exptime.setter def exptime(self, value): raise RuntimeError( "You should not be setting exptime on the fly; " + "Just instantiate a new case of PhotometricParameters" ) @property def nexp(self): """ number of exposures """ return self._nexp @nexp.setter def nexp(self, value): raise RuntimeError( "You should not be setting nexp on the fly; " + "Just instantiate a new case of PhotometricParameters" ) @property def effarea(self): """ effective area in cm^2 """ return self._effarea @effarea.setter def effarea(self, value): raise RuntimeError( "You should not be setting effarea on the fly; " + "Just instantiate a new case of PhotometricParameters" ) @property def gain(self): """ electrons per ADU """ return self._gain @gain.setter def gain(self, value): raise RuntimeError( "You should not be setting gain on the fly; " + "Just instantiate a new case of PhotometricParameters" ) @property def platescale(self): """ arcseconds per pixel """ return self._platescale @platescale.setter def platescale(self, value): raise RuntimeError( "You should not be setting platescale on the fly; " + "Just instantiate a new case of PhotometricParameters" ) @property def readnoise(self): """ electrons per pixel per exposure """ return self._readnoise @readnoise.setter def readnoise(self, value): raise RuntimeError( "You should not be setting readnoise on the fly; " + "Just instantiate a new case of PhotometricParameters" ) @property def darkcurrent(self): """ electrons per pixel per second """ return self._darkcurrent @darkcurrent.setter def darkcurrent(self, value): raise RuntimeError( "You should not be setting darkcurrent on the fly; " + "Just instantiate a new case of PhotometricParameters" ) @property def othernoise(self): """ electrons per pixel per exposure """ return self._othernoise @othernoise.setter def othernoise(self, value): raise RuntimeError( "You should not be setting othernoise on the fly; " + "Just instantiate a new case of PhotometricParameters" ) @property def sigma_sys(self): """ systematic error in magnitudes """ return self._sigma_sys @sigma_sys.setter def sigma_sys(self, value): raise RuntimeError( "You should not be setting sigma_sys on the fly; " + "Just instantiate a new case of PhotometricParameters" )