def __init__(self, magZ=23, magEZ=22, varEZ=0, cachedir=None, **specs): #start the outspec self._outspec = {} # get cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir specs['cachedir'] = self.cachedir # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) self.magZ = float(magZ) # 1 zodi brightness (per arcsec2) self.magEZ = float(magEZ) # 1 exo-zodi brightness (per arcsec2) self.varEZ = float( varEZ) # exo-zodi variation (variance of log-normal dist) self.fZ0 = 10**(-0.4 * self.magZ) / u.arcsec**2 # default zodi brightness self.fEZ0 = 10**( -0.4 * self.magEZ) / u.arcsec**2 # default exo-zodi brightness assert self.varEZ >= 0, "Exozodi variation must be >= 0" # populate outspec for att in self.__dict__: if att not in ['vprint', '_outspec']: dat = self.__dict__[att] self._outspec[att] = dat.value if isinstance( dat, u.Quantity) else dat
def __init__(self, cachedir=None, whichPlanetPhaseFunction='lambert', **specs): #start the outspec self._outspec = {} # cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir specs['cachedir'] = self.cachedir # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) #Select which Phase Function to use assert isinstance(whichPlanetPhaseFunction, str), "whichPlanetPhaseFunction is not a string" self.whichPlanetPhaseFunction = whichPlanetPhaseFunction if whichPlanetPhaseFunction == 'quasiLambertPhaseFunction': from EXOSIMS.util.phaseFunctions import quasiLambertPhaseFunction self.calc_Phi = quasiLambertPhaseFunction elif whichPlanetPhaseFunction == 'hyperbolicTangentPhaseFunc': from EXOSIMS.util.phaseFunctions import hyperbolicTangentPhaseFunc self.calc_Phi = hyperbolicTangentPhaseFunc #else: if whichPlanetPhaseFunction == 'lambert': Default, Do nothing self._outspec['whichPlanetPhaseFunction'] = whichPlanetPhaseFunction #Define Phase Function Inverse betas = np.linspace(start=0.,stop=np.pi,num=1000,endpoint=True)*u.rad Phis = self.calc_Phi(betas) self.betaFunction = PchipInterpolator(-Phis,betas) #the -Phis ensure the function monotonically increases
def __init__(self, dMagLim=25, minComp=0.1, cachedir=None, **specs): #start the outspec self._outspec = {} # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) #if specs contains a completeness_spec then we are going to generate separate instances #of planet population and planet physical model for completeness and for the rest of the sim if 'completeness_specs' in specs: if not specs['completeness_specs'].has_key('modules'): specs['completeness_specs']['modules'] = {} if not specs['completeness_specs']['modules'].has_key('PlanetPhysicalModel'): specs['completeness_specs']['modules']['PlanetPhysicalModel'] = specs['modules']['PlanetPhysicalModel'] if not specs['completeness_specs']['modules'].has_key('PlanetPopulation'): specs['completeness_specs']['modules']['PlanetPopulation'] = specs['modules']['PlanetPopulation'] self.PlanetPopulation = get_module(specs['completeness_specs']['modules']['PlanetPopulation'],'PlanetPopulation')(**specs['completeness_specs']) else: self.PlanetPopulation = get_module(specs['modules']['PlanetPopulation'],'PlanetPopulation')(**specs) # copy phyiscal model object up to attribute self.PlanetPhysicalModel = self.PlanetPopulation.PlanetPhysicalModel # loading attributes self.dMagLim = float(dMagLim) self.minComp = float(minComp) # find the cache directory self.cachedir = get_cache_dir(cachedir) # populate outspec self._outspec['dMagLim'] = self.dMagLim self._outspec['minComp'] = self.minComp self._outspec['cachedir'] = self.cachedir
def __init__(self, fixedPlanPerStar=None, Min=None, cachedir=None, **specs): #start the outspec self._outspec = {} # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # save fixed number of planets to generate self.fixedPlanPerStar = fixedPlanPerStar self._outspec['fixedPlanPerStar'] = fixedPlanPerStar # get cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir # check if KnownRVPlanetsUniverse has correct input modules if specs['modules']['SimulatedUniverse'] == 'KnownRVPlanetsUniverse': val = specs['modules']['TargetList'] == 'KnownRVPlanetsTargetList' \ and specs['modules']['PlanetPopulation'] == 'KnownRVPlanets' assert val == True, 'KnownRVPlanetsUniverse must use KnownRVPlanetsTargetList and KnownRVPlanets' else: val = specs['modules']['TargetList'] == 'KnownRVPlanetsTargetList' \ or specs['modules']['PlanetPopulation'] == 'KnownRVPlanets' assert val == False, 'KnownRVPlanetsTargetList or KnownRVPlanets should not be used with this SimulatedUniverse' # import TargetList class self.TargetList = get_module(specs['modules']['TargetList'], 'TargetList')(**specs) # bring inherited class objects to top level of Simulated Universe TL = self.TargetList self.StarCatalog = TL.StarCatalog self.PlanetPopulation = TL.PlanetPopulation self.PlanetPhysicalModel = TL.PlanetPhysicalModel self.OpticalSystem = TL.OpticalSystem self.ZodiacalLight = TL.ZodiacalLight self.BackgroundSources = TL.BackgroundSources self.PostProcessing = TL.PostProcessing self.Completeness = TL.Completeness # initial constant mean anomaly assert type(Min) is int or type(Min) is float or Min is None, 'Min may be int, float, or None' if Min is not None: self.Min = float(Min)*u.deg else: self.Min = Min # list of possible planet attributes self.planet_atts = ['plan2star', 'a', 'e', 'I', 'O', 'w', 'M0', 'Min', 'Rp', 'Mp', 'p', 'r', 'v', 'd', 's', 'phi', 'fEZ', 'dMag', 'WA'] # generate orbital elements, albedos, radii, and masses self.gen_physical_properties(**specs) # find initial position-related parameters: position, velocity, planet-star # distance, apparent separation, surface brightness of exo-zodiacal light self.init_systems()
def __init__(self, cachedir=None, **specs): #start the outspec self._outspec = {} # get cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True))
def __init__(self, cachedir=None, **specs): self._outspec = {} # cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) return
def __init__(self, missionStart=60634, missionLife=0.1, missionPortion=1, OBduration=np.inf, missionSchedule=None, cachedir=None, **specs): _outspec = {} #start the outspec self._outspec = {} # get cache directory self.cachedir = get_cache_dir(cachedir) # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # illegal value checks assert missionLife >= 0, "Need missionLife >= 0, got %f"%missionLife # arithmetic on missionPortion fails if it is outside the legal range assert missionPortion > 0 and missionPortion <= 1, \ "Require missionPortion in the interval [0,1], got %f"%missionPortion # OBduration must be positive nonzero assert OBduration*u.d > 0*u.d, "Required OBduration positive nonzero, got %f"%OBduration # set up state variables # tai scale specified because the default, utc, requires accounting for leap # seconds, causing warnings from astropy.time when time-deltas are added self.missionStart = Time(float(missionStart), format='mjd', scale='tai')#the absolute date of mission start must have scale tai self.missionPortion = float(missionPortion)#the portion of missionFinishNorm the instrument can observe for # set values derived from quantities above self.missionLife = float(missionLife)*u.year#the total amount of time since mission start that can elapse MUST BE IN YEAR HERE FOR OUTSPEC self.missionFinishAbs = self.missionStart + self.missionLife.to('day')#the absolute time the mission can possibly end # initialize values updated by functions self.currentTimeNorm = 0.*u.day#the current amount of time since mission start that has elapsed self.currentTimeAbs = self.missionStart#the absolute mission time # initialize observing block times arrays. #An Observing Block is a segment of time over which observations may take place self.init_OB(str(missionSchedule), OBduration*u.d) # initialize time spend using instrument self.exoplanetObsTime = 0*u.day # populate outspec for att in self.__dict__: if att not in ['vprint','_outspec']: dat = self.__dict__[att] self._outspec[att] = dat.value if isinstance(dat,(u.Quantity,Time)) else dat
def __init__(self, missionStart=60634, missionLife=0.1, missionPortion=1, OBduration=np.inf, missionSchedule=None, cachedir=None, **specs): _outspec = {} #start the outspec self._outspec = {} # get cache directory self.cachedir = get_cache_dir(cachedir) # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # illegal value checks assert missionLife >= 0, "Need missionLife >= 0, got %f"%missionLife # arithmetic on missionPortion fails if it is outside the legal range assert missionPortion > 0 and missionPortion <= 1, \ "Require missionPortion in the interval [0,1], got %f"%missionPortion # OBduration must be positive nonzero assert OBduration*u.d > 0*u.d, "Required OBduration positive nonzero, got %f"%OBduration # set up state variables # tai scale specified because the default, utc, requires accounting for leap # seconds, causing warnings from astropy.time when time-deltas are added self.missionStart = Time(float(missionStart), format='mjd', scale='tai')#the absolute date of mission start must have scale tai self.missionPortion = float(missionPortion)#the portion of missionFinishNorm the instrument can observe for # set values derived from quantities above self.missionLife = float(missionLife)*u.year#the total amount of time since mission start that can elapse MUST BE IN YEAR HERE FOR OUTSPEC self.missionFinishAbs = self.missionStart + self.missionLife.to('day')#the absolute time the mission can possibly end # initialize values updated by functions self.currentTimeNorm = 0.*u.day#the current amount of time since mission start that has elapsed self.currentTimeAbs = self.missionStart#the absolute mission time # initialize observing block times arrays. #An Observing Block is a segment of time over which observations may take place self.init_OB(str(missionSchedule), OBduration*u.d) # initialize time spend using instrument self.exoplanetObsTime = 0*u.day # populate outspec for att in self.__dict__.keys(): if att not in ['vprint','_outspec']: dat = self.__dict__[att] self._outspec[att] = dat.value if isinstance(dat,(u.Quantity,Time)) else dat
def __init__(self, ntargs=1, cachedir=None, **specs): #start the outspec self._outspec = {} # get cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir specs['cachedir'] = self.cachedir # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # ntargs must be an integer >= 1 self.ntargs = max(int(ntargs), 1) # list of astropy attributes self.dist = np.ones(ntargs) * u.pc # distance self.parx = self.dist.to('mas', equivalencies=u.parallax()) # parallax self.coords = SkyCoord(ra=np.zeros(ntargs) * u.deg, dec=np.zeros(ntargs) * u.deg, distance=self.dist) # ICRS coordinates self.pmra = np.zeros(ntargs) * u.mas / u.yr # proper motion in RA self.pmdec = np.zeros(ntargs) * u.mas / u.yr # proper motion in DEC self.rv = np.zeros(ntargs) * u.km / u.s # radial velocity # list of non-astropy attributes self.Name = np.array(['Prototype'] * ntargs) # star names self.Spec = np.array(['G'] * ntargs) # spectral types self.Umag = np.zeros(ntargs) # U magnitude self.Bmag = np.zeros(ntargs) # B magnitude self.Vmag = np.zeros(ntargs) # V magnitude self.Rmag = np.zeros(ntargs) # R magnitude self.Imag = np.zeros(ntargs) # I magnitude self.Jmag = np.zeros(ntargs) # J magnitude self.Hmag = np.zeros(ntargs) # H magnitude self.Kmag = np.zeros(ntargs) # K magnitude self.BV = np.zeros(ntargs) # B-V Johnson magnitude self.MV = np.zeros(ntargs) # absolute V magnitude self.BC = np.zeros(ntargs) # bolometric correction self.L = np.ones(ntargs) # stellar luminosity in ln(SolLum) self.Binary_Cut = np.zeros(ntargs, dtype=bool) # binary closer than 10 arcsec # populate outspecs self._outspec['ntargs'] = self.ntargs
def __init__(self, cachedir=None, **specs): #start the outspec self._outspec = {} # cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir specs['cachedir'] = self.cachedir # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) #Define Phase Function Inverse betas = np.linspace(start=0.,stop=np.pi,num=1000,endpoint=True)*u.rad Phis = self.calc_Phi(betas) self.betaFunction = PchipInterpolator(-Phis,betas) #the -Phis ensure the function monotonically increases return
def __init__(self, cachedir=None, **specs): self.cachedir = get_cache_dir(cachedir) classpath = os.path.split(inspect.getfile(self.__class__))[0] filename = 'SIMBAD300' pklpath = os.path.join(self.cachedir, filename + '.pkl') matpath = os.path.join(classpath, filename + '.mat') # check if given filename exists as .pkl file already if os.path.exists(pklpath): self.populatepkl(pklpath, **specs) self.vprint('Loaded %s.pkl star catalog'%filename) # check if given filename exists as a .mat file but not .pkl file elif os.path.exists(matpath): self.SIMBAD_mat2pkl(matpath, pklpath) self.populatepkl(pklpath, **specs) self.vprint('Loaded %s.mat star catalog'%filename) # otherwise print error else: self.vprint('Could not load SIMBAD300 star catalog')
def __init__(self, cachedir=None, **specs): self.cachedir = get_cache_dir(cachedir) classpath = os.path.split(inspect.getfile(self.__class__))[0] filename = 'SIMBAD300' pklpath = os.path.join(self.cachedir, filename + '.pkl') matpath = os.path.join(classpath, filename + '.mat') # check if given filename exists as .pkl file already if os.path.exists(pklpath): self.populatepkl(pklpath, **specs) print('Loaded %s.pkl star catalog' % filename) # check if given filename exists as a .mat file but not .pkl file elif os.path.exists(matpath): self.SIMBAD_mat2pkl(matpath, pklpath) self.populatepkl(pklpath, **specs) print('Loaded %s.mat star catalog' % filename) # otherwise print error else: print('Could not load SIMBAD300 star catalog')
def __init__(self, dMagLim=25, minComp=0.1, cachedir=None, **specs): #start the outspec self._outspec = {} # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) #if specs contains a completeness_spec then we are going to generate separate instances #of planet population and planet physical model for completeness and for the rest of the sim if 'completeness_specs' in specs: if specs['completeness_specs'] == None: specs['completeness_specs'] = {} specs['completeness_specs']['modules'] = {} if not 'modules' in specs['completeness_specs']: specs['completeness_specs']['modules'] = {} if not 'PlanetPhysicalModel' in specs['completeness_specs']['modules']: specs['completeness_specs']['modules']['PlanetPhysicalModel'] = specs['modules']['PlanetPhysicalModel'] if not 'PlanetPopulation' in specs['completeness_specs']['modules']: specs['completeness_specs']['modules']['PlanetPopulation'] = specs['modules']['PlanetPopulation'] self.PlanetPopulation = get_module(specs['completeness_specs']['modules']['PlanetPopulation'],'PlanetPopulation')(**specs['completeness_specs']) else: self.PlanetPopulation = get_module(specs['modules']['PlanetPopulation'],'PlanetPopulation')(**specs) # copy phyiscal model object up to attribute self.PlanetPhysicalModel = self.PlanetPopulation.PlanetPhysicalModel # loading attributes self.dMagLim = float(dMagLim) self.minComp = float(minComp) # find the cache directory self.cachedir = get_cache_dir(cachedir) # populate outspec self._outspec['dMagLim'] = self.dMagLim self._outspec['minComp'] = self.minComp self._outspec['completeness_specs'] = specs.get('completeness_specs') self._outspec['cachedir'] = self.cachedir
def __init__(self, magZ=23, magEZ=22, varEZ=0, cachedir=None, **specs): #start the outspec self._outspec = {} # get cache directory self.cachedir = get_cache_dir(cachedir) # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) self.magZ = float(magZ) # 1 zodi brightness (per arcsec2) self.magEZ = float(magEZ) # 1 exo-zodi brightness (per arcsec2) self.varEZ = float(varEZ) # exo-zodi variation (variance of log-normal dist) self.fZ0 = 10**(-0.4*self.magZ)/u.arcsec**2 # default zodi brightness self.fEZ0 = 10**(-0.4*self.magEZ)/u.arcsec**2 # default exo-zodi brightness assert self.varEZ >= 0, "Exozodi variation must be >= 0" # populate outspec for att in self.__dict__: if att not in ['vprint','_outspec']: dat = self.__dict__[att] self._outspec[att] = dat.value if isinstance(dat, u.Quantity) else dat
def __init__(self, arange=[0.1, 100.], erange=[0.01, 0.99], Irange=[0., 180.], Orange=[0., 360.], wrange=[0., 360.], prange=[0.1, 0.6], Rprange=[1., 30.], Mprange=[1., 4131.], scaleOrbits=False, constrainOrbits=False, eta=0.1, cachedir=None, **specs): #start the outspec self._outspec = {} # get the cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir specs['cachedir'] = self.cachedir # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # check range of parameters self.arange = self.checkranges(arange, 'arange') * u.AU self.erange = self.checkranges(erange, 'erange') self.Irange = self.checkranges(Irange, 'Irange') * u.deg self.Orange = self.checkranges(Orange, 'Orange') * u.deg self.wrange = self.checkranges(wrange, 'wrange') * u.deg self.prange = self.checkranges(prange, 'prange') self.Rprange = self.checkranges(Rprange, 'Rprange') * u.earthRad self.Mprange = self.checkranges(Mprange, 'Mprange') * u.earthMass assert isinstance(scaleOrbits, bool), "scaleOrbits must be boolean" # scale planetary orbits by sqrt(L) self.scaleOrbits = scaleOrbits assert isinstance(constrainOrbits, bool), "constrainOrbits must be boolean" # constrain planetary orbital radii to sma range self.constrainOrbits = constrainOrbits # derive orbital radius range from quantities above ar = self.arange.to('AU').value er = self.erange if self.constrainOrbits: self.rrange = [ar[0], ar[1]] * u.AU else: self.rrange = [ar[0] * (1. - er[1]), ar[1] * (1. + er[1])] * u.AU assert isinstance(eta, numbers.Number) and (eta > 0),\ "eta must be strictly positive" # global occurrence rate defined as expected number of planets per # star in a given universe self.eta = eta # albedo is constant for planetary radius range self.pfromRp = False # populate all attributes to outspec for att in self.__dict__: if att not in ['vprint', '_outspec']: dat = copy.copy(self.__dict__[att]) self._outspec[att] = dat.value if isinstance( dat, u.Quantity) else dat # define prototype distributions of parameters (uniform and log-uniform) self.uniform = lambda x, v: np.array( (np.array(x) >= v[0]) & (np.array(x) <= v[1]), dtype=float, ndmin=1) / (v[1] - v[0]) self.logunif = lambda x, v: np.array( (np.array(x) >= v[0]) & (np.array(x) <= v[1]), dtype=float, ndmin=1) / (x * np.log(v[1] / v[0])) # import PlanetPhysicalModel self.PlanetPhysicalModel = get_module( specs['modules']['PlanetPhysicalModel'], 'PlanetPhysicalModel')(**specs)
def __init__(self, fixedPlanPerStar=None, Min=None, cachedir=None, lucky_planets=False, commonSystemInclinations=None, **specs): #start the outspec self._outspec = {} # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) self.lucky_planets = lucky_planets self._outspec['lucky_planets'] = lucky_planets self.commonSystemInclinations = commonSystemInclinations self._outspec['commonSystemInclinations'] = commonSystemInclinations # save fixed number of planets to generate self.fixedPlanPerStar = fixedPlanPerStar self._outspec['fixedPlanPerStar'] = fixedPlanPerStar # get cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir specs['cachedir'] = self.cachedir # check if KnownRVPlanetsUniverse has correct input modules if specs['modules']['SimulatedUniverse'] == 'KnownRVPlanetsUniverse': val = specs['modules']['TargetList'] == 'KnownRVPlanetsTargetList' \ and specs['modules']['PlanetPopulation'] == 'KnownRVPlanets' assert val == True, 'KnownRVPlanetsUniverse must use KnownRVPlanetsTargetList and KnownRVPlanets' else: val = specs['modules']['TargetList'] == 'KnownRVPlanetsTargetList' \ or specs['modules']['PlanetPopulation'] == 'KnownRVPlanets' assert val == False, 'KnownRVPlanetsTargetList or KnownRVPlanets should not be used with this SimulatedUniverse' # import TargetList class self.TargetList = get_module(specs['modules']['TargetList'], 'TargetList')(**specs) # bring inherited class objects to top level of Simulated Universe TL = self.TargetList self.StarCatalog = TL.StarCatalog self.PlanetPopulation = TL.PlanetPopulation self.PlanetPhysicalModel = TL.PlanetPhysicalModel self.OpticalSystem = TL.OpticalSystem self.ZodiacalLight = TL.ZodiacalLight self.BackgroundSources = TL.BackgroundSources self.PostProcessing = TL.PostProcessing self.Completeness = TL.Completeness # initial constant mean anomaly assert type(Min) is int or type( Min) is float or Min is None, 'Min may be int, float, or None' if Min is not None: self.Min = float(Min) * u.deg else: self.Min = Min # list of possible planet attributes self.planet_atts = [ 'plan2star', 'a', 'e', 'I', 'O', 'w', 'M0', 'Min', 'Rp', 'Mp', 'p', 'r', 'v', 'd', 's', 'phi', 'fEZ', 'dMag', 'WA' ] # generate orbital elements, albedos, radii, and masses self.gen_physical_properties(**specs) # find initial position-related parameters: position, velocity, planet-star # distance, apparent separation, surface brightness of exo-zodiacal light self.init_systems()
def __init__(self, FAP=3e-7, MDP=1e-3, ppFact=1.0, FAdMag0=15, cachedir=None, **specs): #start the outspec self._outspec = {} # get cache directory self.cachedir = get_cache_dir(cachedir) # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) self.FAP = float(FAP) # false alarm probability self.MDP = float(MDP) # missed detection probability # check for post-processing factor, function of the working angle if isinstance(ppFact, str): pth = os.path.normpath(os.path.expandvars(ppFact)) assert os.path.isfile(pth), "%s is not a valid file." % pth dat = fits.open(pth)[0].data assert len(dat.shape) == 2 and 2 in dat.shape, \ "Wrong post-processing gain data shape." WA, G = (dat[0], dat[1]) if dat.shape[0] == 2 else (dat[:, 0], dat[:, 1]) assert np.all(G > 0) and np.all(G <= 1), \ "Post-processing gain must be positive and smaller than 1." # gain outside of WA values defaults to 1 Ginterp = scipy.interpolate.interp1d(WA, G, kind='cubic', fill_value=1., bounds_error=False) self.ppFact = lambda s: np.array(Ginterp(s.to('arcsec').value), ndmin=1) elif isinstance(ppFact, numbers.Number): assert ppFact > 0 and ppFact <= 1, \ "Post-processing gain must be positive and smaller than 1." self.ppFact = lambda s, G=float(ppFact): G # check for minimum FA delta magnitude, function of the working angle if isinstance(FAdMag0, str): pth = os.path.normpath(os.path.expandvars(FAdMag0)) assert os.path.isfile(pth), "%s is not a valid file." % pth dat = fits.open(pth)[0].data assert len(dat.shape) == 2 and 2 in dat.shape, \ "Wrong FAdMag0 data shape." WA, G = (dat[0], dat[1]) if dat.shape[0] == 2 else (dat[:, 0], dat[:, 1]) # gain outside of WA values defaults to 25 Ginterp = scipy.interpolate.interp1d(WA, G, kind='cubic', fill_value=25., bounds_error=False) self.FAdMag0 = lambda s: np.array(Ginterp(s.to('arcsec').value), ndmin=1) elif isinstance(FAdMag0, numbers.Number): self.FAdMag0 = lambda s, G=float(FAdMag0): G # populate outspec for att in self.__dict__.keys(): if att not in ['vprint', 'ppFact', 'FAdMag0', '_outspec']: dat = self.__dict__[att] self._outspec[att] = dat.value if isinstance( dat, u.Quantity) else dat # populate with values which may be interpolants self._outspec['ppFact'] = ppFact self._outspec['FAdMag0'] = FAdMag0 # instantiate background sources object self.BackgroundSources = get_module( specs['modules']['BackgroundSources'], 'BackgroundSources')(**specs)
def __init__(self, obscurFac=0.1, shapeFac=np.pi / 4, pupilDiam=4, intCutoff=50, dMag0=15, WA0=None, scienceInstruments=None, QE=0.9, optics=0.5, FoV=10, pixelNumber=1000, pixelSize=1e-5, sread=1e-6, idark=1e-4, CIC=1e-3, texp=100, radDos=0, PCeff=0.8, ENF=1, Rs=50, lenslSamp=2, starlightSuppressionSystems=None, lam=500, BW=0.2, occ_trans=0.2, core_thruput=0.1, core_contrast=1e-10, core_platescale=None, PSF=np.ones((3, 3)), ohTime=1, observingModes=None, SNR=5, timeMultiplier=1., IWA=None, OWA=None, ref_dMag=3, ref_Time=0, cachedir=None, use_char_minintTime=False, **specs): #start the outspec self._outspec = {} # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # load all values with defaults self.obscurFac = float( obscurFac) # obscuration factor (fraction of PM area) self.shapeFac = float(shapeFac) # shape factor self.pupilDiam = float(pupilDiam) * u.m # entrance pupil diameter self.intCutoff = float(intCutoff) * u.d # integration time cutoff self.dMag0 = float(dMag0) # favorable dMag for calc_minintTime self.ref_dMag = float(ref_dMag) # reference star dMag for RDI self.ref_Time = float( ref_Time) # fraction of time spent on ref star for RDI self.use_char_minintTime = use_char_minintTime # pupil collecting area (obscured PM) self.pupilArea = (1 - self.obscurFac) * self.shapeFac * self.pupilDiam**2 # spectral flux density ~9.5e7 [ph/s/m2/nm] @ 500nm # F0(lambda) function of wavelength, based on Traub et al. 2016 (JATIS): self.F0 = lambda l: 1e4*10**(4.01 - (l.to('nm').value - 550)/770) \ *u.ph/u.s/u.m**2/u.nm # get cache directory self.cachedir = get_cache_dir(cachedir) # loop through all science Instruments (must have one defined) assert scienceInstruments, "No science instrument defined." self.scienceInstruments = scienceInstruments self._outspec['scienceInstruments'] = [] for ninst, inst in enumerate(self.scienceInstruments): assert isinstance( inst, dict), "Science instruments must be defined as dicts." assert 'name' in inst and isinstance(inst['name'], basestring), \ "All science instruments must have key name." # populate with values that may be filenames (interpolants) inst['QE'] = inst.get('QE', QE) self._outspec['scienceInstruments'].append(inst.copy()) # quantum efficiency if isinstance(inst['QE'], basestring): pth = os.path.normpath(os.path.expandvars(inst['QE'])) assert os.path.isfile(pth), "%s is not a valid file." % pth dat = fits.open(pth)[0].data assert len(dat.shape) == 2 and 2 in dat.shape, \ param_name + " wrong data shape." lam, D = (dat[0], dat[1]) if dat.shape[0] == 2 else (dat[:, 0], dat[:, 1]) assert np.all(D >= 0) and np.all(D <= 1), \ "QE must be positive and smaller than 1." # parameter values outside of lam Dinterp = scipy.interpolate.interp1d(lam.astype(float), D.astype(float), kind='cubic', fill_value=0., bounds_error=False) inst['QE'] = lambda l: np.array(Dinterp(l.to('nm').value), ndmin=1) / u.photon elif isinstance(inst['QE'], numbers.Number): assert inst['QE'] >= 0 and inst['QE'] <= 1, \ "QE must be positive and smaller than 1." inst['QE'] = lambda l, QE=float(inst['QE']): np.array( [QE] * l.size, ndmin=1) / u.photon # load detector specifications inst['optics'] = float(inst.get( 'optics', optics)) # attenuation due to optics inst['FoV'] = float(inst.get('FoV', FoV)) * u.arcsec # field of view inst['pixelNumber'] = int(inst.get('pixelNumber', pixelNumber)) # array format inst['pixelSize'] = float(inst.get('pixelSize', pixelSize)) * u.m # pixel pitch inst['pixelScale'] = inst.get( 'pixelScale', 2 * inst['FoV'].value / inst['pixelNumber']) * u.arcsec # pixel pitch inst['idark'] = float(inst.get('idark', idark)) / u.s # dark-current rate inst['CIC'] = float(inst.get('CIC', CIC)) # clock-induced-charge inst['sread'] = float(inst.get('sread', sread)) # effective readout noise inst['texp'] = float(inst.get( 'texp', texp)) * u.s # exposure time per frame inst['ENF'] = float(inst.get('ENF', ENF)) # excess noise factor inst['PCeff'] = float(inst.get( 'PCeff', PCeff)) # photon counting efficiency # parameters specific to spectrograph if 'spec' in inst['name'].lower(): # spectral resolving power inst['Rs'] = float(inst.get('Rs', Rs)) # lenslet sampling, number of pixel per lenslet rows or cols inst['lenslSamp'] = float(inst.get('lenslSamp', lenslSamp)) else: inst['Rs'] = 1. inst['lenslSamp'] = 1. # calculate focal and f-number inst['focal'] = inst['pixelSize'].to('m') / inst['pixelScale'].to( 'rad').value inst['fnumber'] = float(inst['focal'] / self.pupilDiam) # populate detector specifications to outspec for att in inst: if att not in ['QE']: dat = inst[att] self._outspec['scienceInstruments'][ninst][att] = dat.value \ if isinstance(dat, u.Quantity) else dat # loop through all starlight suppression systems (must have one defined) assert starlightSuppressionSystems, "No starlight suppression systems defined." self.starlightSuppressionSystems = starlightSuppressionSystems self.haveOcculter = False self._outspec['starlightSuppressionSystems'] = [] for nsyst, syst in enumerate(self.starlightSuppressionSystems): assert isinstance(syst,dict),\ "Starlight suppression systems must be defined as dicts." assert 'name' in syst and isinstance(syst['name'],basestring),\ "All starlight suppression systems must have key name." # populate with values that may be filenames (interpolants) syst['occ_trans'] = syst.get('occ_trans', occ_trans) syst['core_thruput'] = syst.get('core_thruput', core_thruput) syst['core_contrast'] = syst.get('core_contrast', core_contrast) syst['core_mean_intensity'] = syst.get( 'core_mean_intensity') # no default syst['core_area'] = syst.get('core_area', 0.) # if zero, will get from lam/D syst['PSF'] = syst.get('PSF', PSF) self._outspec['starlightSuppressionSystems'].append(syst.copy()) # attenuation due to optics specific to the coronagraph (defaults to 1) # e.g. polarizer, Lyot stop, extra flat mirror syst['optics'] = float(syst.get('optics', 1.)) # set an occulter, for an external or hybrid system syst['occulter'] = syst.get('occulter', False) if syst['occulter'] == True: self.haveOcculter = True # handle inf OWA if syst.get('OWA') == 0: syst['OWA'] = np.Inf # when provided, always use deltaLam instead of BW (bandwidth fraction) syst['lam'] = float(syst.get( 'lam', lam)) * u.nm # central wavelength (nm) syst['deltaLam'] = float( syst.get('deltaLam', syst['lam'].to('nm').value * syst.get('BW', BW))) * u.nm # bandwidth (nm) syst['BW'] = float(syst['deltaLam'] / syst['lam']) # bandwidth fraction # default lam and BW updated with values from first instrument if nsyst == 0: lam, BW = syst.get('lam').value, syst.get('BW') # get coronagraph input parameters syst = self.get_coro_param(syst, 'occ_trans') syst = self.get_coro_param(syst, 'core_thruput') syst = self.get_coro_param(syst, 'core_contrast', fill=1.) syst = self.get_coro_param(syst, 'core_mean_intensity') syst = self.get_coro_param(syst, 'core_area') syst['core_platescale'] = syst.get('core_platescale', core_platescale) # get PSF if isinstance(syst['PSF'], basestring): pth = os.path.normpath(os.path.expandvars(syst['PSF'])) assert os.path.isfile(pth), "%s is not a valid file." % pth hdr = fits.open(pth)[0].header dat = fits.open(pth)[0].data assert len(dat.shape) == 2, "Wrong PSF data shape." assert np.any(dat), "PSF must be != 0" syst['PSF'] = lambda l, s, P=dat: P else: assert np.any(syst['PSF']), "PSF must be != 0" syst['PSF'] = lambda l, s, P=np.array(syst['PSF']).astype(float ): P # loading system specifications syst['IWA'] = syst.get( 'IWA', 0.1 if IWA is None else IWA) * u.arcsec # inner WA syst['OWA'] = syst.get( 'OWA', np.Inf if OWA is None else OWA) * u.arcsec # outer WA syst['ohTime'] = float(syst.get('ohTime', ohTime)) * u.d # overhead time # populate system specifications to outspec for att in syst: if att not in [ 'occ_trans', 'core_thruput', 'core_contrast', 'core_mean_intensity', 'core_area', 'PSF' ]: dat = syst[att] self._outspec['starlightSuppressionSystems'][nsyst][att] \ = dat.value if isinstance(dat, u.Quantity) else dat # loop through all observing modes # if no observing mode defined, create a default mode if observingModes == None: inst = self.scienceInstruments[0] syst = self.starlightSuppressionSystems[0] observingModes = [{ 'detectionMode': True, 'instName': inst['name'], 'systName': syst['name'] }] self.observingModes = observingModes self._outspec['observingModes'] = [] for nmode, mode in enumerate(self.observingModes): assert isinstance( mode, dict), "Observing modes must be defined as dicts." assert 'instName' in mode and 'systName' in mode, \ "All observing modes must have key instName and systName." assert np.any([mode['instName'] == inst['name'] for inst in \ self.scienceInstruments]), "The mode's instrument name " \ + mode['instName'] + " does not exist." assert np.any([mode['systName'] == syst['name'] for syst in \ self.starlightSuppressionSystems]), "The mode's system name " \ + mode['systName'] + " does not exist." self._outspec['observingModes'].append(mode.copy()) # loading mode specifications mode['SNR'] = float(mode.get('SNR', SNR)) mode['timeMultiplier'] = float( mode.get('timeMultiplier', timeMultiplier)) mode['detectionMode'] = mode.get('detectionMode', False) mode['inst'] = [inst for inst in self.scienceInstruments \ if inst['name'] == mode['instName']][0] mode['syst'] = [syst for syst in self.starlightSuppressionSystems \ if syst['name'] == mode['systName']][0] # get mode wavelength and bandwidth (get system's values by default) # when provided, always use deltaLam instead of BW (bandwidth fraction) syst_lam = mode['syst']['lam'].to('nm').value syst_BW = mode['syst']['BW'] mode['lam'] = float(mode.get('lam', syst_lam)) * u.nm mode['deltaLam'] = float(mode.get('deltaLam', mode['lam'].value \ *mode.get('BW',syst_BW)))*u.nm mode['BW'] = float(mode['deltaLam'] / mode['lam']) # get mode IWA and OWA: rescale if the mode wavelength is different than # the wavelength at which the system is defined mode['IWA'] = mode['syst']['IWA'] mode['OWA'] = mode['syst']['OWA'] if mode['lam'] != mode['syst']['lam']: mode['IWA'] = mode['IWA'] * mode['lam'] / mode['syst']['lam'] mode['OWA'] = mode['OWA'] * mode['lam'] / mode['syst']['lam'] # radiation dosage, goes from 0 (beginning of mission) to 1 (end of mission) mode['radDos'] = float(mode.get('radDos', radDos)) # check for only one detection mode allModes = self.observingModes detModes = list( filter(lambda mode: mode['detectionMode'] == True, allModes)) assert len(detModes) <= 1, "More than one detection mode specified." # if not specified, default detection mode is first imager mode if len(detModes) == 0: imagerModes = list( filter(lambda mode: 'imag' in mode['inst']['name'], allModes)) if imagerModes: imagerModes[0]['detectionMode'] = True # if no imager mode, default detection mode is first observing mode else: allModes[0]['detectionMode'] = True # load favorable working angle (WA0) for calc_minintTime, # or calculate it from detection IWA-OWA midpoint value try: self.WA0 = float(WA0) * u.arcsec except TypeError: mode = list( filter(lambda mode: mode['detectionMode'] == True, self.observingModes))[0] self.WA0 = 2. * mode['IWA'] if np.isinf( mode['OWA']) else (mode['IWA'] + mode['OWA']) / 2. # populate fundamental IWA and OWA as required IWAs = [ x.get('IWA') for x in self.observingModes if x.get('IWA') is not None ] if IWA is not None: self.IWA = float(IWA) * u.arcsec elif IWAs: self.IWA = min(IWAs) else: raise ValueError("Could not determine fundamental IWA.") OWAs = [ x.get('OWA') for x in self.observingModes if x.get('OWA') is not None ] if OWA is not None: self.OWA = float(OWA) * u.arcsec if OWA != 0 else np.inf * u.arcsec elif OWAs: self.OWA = max(OWAs) else: raise ValueError("Could not determine fundamental OWA.") assert self.IWA < self.OWA, "Fundamental IWA must be smaller that the OWA." # populate outspec with all OpticalSystem scalar attributes for att in self.__dict__: if att not in [ 'vprint', 'F0', 'scienceInstruments', 'starlightSuppressionSystems', 'observingModes', '_outspec' ]: dat = self.__dict__[att] self._outspec[att] = dat.value if isinstance( dat, u.Quantity) else dat
def __init__(self, missionStart=60634, staticStars=True, keepStarCatalog=False, fillPhotometry=False, explainFiltering=False, filterBinaries=True, filterSubM=False, cachedir=None, **specs): #start the outspec self._outspec = {} # get cache directory self.cachedir = get_cache_dir(cachedir) # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # validate TargetList inputs assert isinstance(staticStars, bool), "staticStars must be a boolean." assert isinstance(keepStarCatalog, bool), "keepStarCatalog must be a boolean." assert isinstance(fillPhotometry, bool), "fillPhotometry must be a boolean." assert isinstance(explainFiltering, bool), "explainFiltering must be a boolean." assert isinstance(filterBinaries, bool), "filterBinaries must be a boolean." assert isinstance(filterSubM, bool), "filterSubM must be a boolean." self.staticStars = bool(staticStars) self.keepStarCatalog = bool(keepStarCatalog) self.fillPhotometry = bool(fillPhotometry) self.explainFiltering = bool(explainFiltering) self.filterBinaries = bool(filterBinaries) self.filterSubM = bool(filterSubM) # check if KnownRVPlanetsTargetList is using KnownRVPlanets if specs['modules']['TargetList'] == 'KnownRVPlanetsTargetList': assert specs['modules']['PlanetPopulation'] == 'KnownRVPlanets', \ 'KnownRVPlanetsTargetList must use KnownRVPlanets' else: assert specs['modules']['PlanetPopulation'] != 'KnownRVPlanets', \ 'This TargetList cannot use KnownRVPlanets' # check if KnownRVPlanetsTargetList is using KnownRVPlanets if specs['modules']['TargetList'] == 'KnownRVPlanetsTargetList': assert specs['modules']['PlanetPopulation'] == 'KnownRVPlanets', \ 'KnownRVPlanetsTargetList must use KnownRVPlanets' else: assert specs['modules']['PlanetPopulation'] != 'KnownRVPlanets', \ 'This TargetList cannot use KnownRVPlanets' # populate outspec for att in self.__dict__: if att not in ['vprint','_outspec']: dat = self.__dict__[att] self._outspec[att] = dat.value if isinstance(dat, u.Quantity) else dat # get desired module names (specific or prototype) and instantiate objects self.StarCatalog = get_module(specs['modules']['StarCatalog'], 'StarCatalog')(**specs) self.OpticalSystem = get_module(specs['modules']['OpticalSystem'], 'OpticalSystem')(**specs) self.ZodiacalLight = get_module(specs['modules']['ZodiacalLight'], 'ZodiacalLight')(**specs) self.PostProcessing = get_module(specs['modules']['PostProcessing'], 'PostProcessing')(**specs) self.Completeness = get_module(specs['modules']['Completeness'], 'Completeness')(**specs) # bring inherited class objects to top level of Simulated Universe self.BackgroundSources = self.PostProcessing.BackgroundSources #if specs contains a completeness_spec then we are going to generate separate instances #of planet population and planet physical model for completeness and for the rest of the sim if 'completeness_specs' in specs: self.PlanetPopulation = get_module(specs['modules']['PlanetPopulation'],'PlanetPopulation')(**specs) self.PlanetPhysicalModel = self.PlanetPopulation.PlanetPhysicalModel else: self.PlanetPopulation = self.Completeness.PlanetPopulation self.PlanetPhysicalModel = self.Completeness.PlanetPhysicalModel # list of possible Star Catalog attributes self.catalog_atts = ['Name', 'Spec', 'parx', 'Umag', 'Bmag', 'Vmag', 'Rmag', 'Imag', 'Jmag', 'Hmag', 'Kmag', 'dist', 'BV', 'MV', 'BC', 'L', 'coords', 'pmra', 'pmdec', 'rv', 'Binary_Cut'] # now populate and filter the list self.populate_target_list(**specs) # generate any completeness update data needed self.Completeness.gen_update(self) self.filter_target_list(**specs) # have target list, no need for catalog now (unless asked to retain) if not self.keepStarCatalog: self.StarCatalog = specs['modules']['StarCatalog'] # add nStars to outspec self._outspec['nStars'] = self.nStars # if staticStars is True, the star coordinates are taken at mission start, # and are not propagated during the mission self.starprop_static = None if self.staticStars is True: allInds = np.arange(self.nStars,dtype=int) missionStart = Time(float(missionStart), format='mjd', scale='tai') self.starprop_static = lambda sInds, currentTime, eclip=False, \ c1=self.starprop(allInds, missionStart, eclip=False), \ c2=self.starprop(allInds, missionStart, eclip=True): \ c1[sInds] if eclip==False else c2[sInds]
def __init__(self, arange=[0.1,100.], erange=[0.01,0.99], Irange=[0.,180.], Orange=[0.,360.], wrange=[0.,360.], prange=[0.1,0.6], Rprange=[1.,30.], Mprange=[1.,4131.], scaleOrbits=False, constrainOrbits=False, eta=0.1, cachedir=None, **specs): #start the outspec self._outspec = {} # get the cache directory self.cachedir = get_cache_dir(cachedir) # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # check range of parameters self.arange = self.checkranges(arange,'arange')*u.AU self.erange = self.checkranges(erange,'erange') self.Irange = self.checkranges(Irange,'Irange')*u.deg self.Orange = self.checkranges(Orange,'Orange')*u.deg self.wrange = self.checkranges(wrange,'wrange')*u.deg self.prange = self.checkranges(prange,'prange') self.Rprange = self.checkranges(Rprange,'Rprange')*u.earthRad self.Mprange = self.checkranges(Mprange,'Mprange')*u.earthMass assert isinstance(scaleOrbits, bool), "scaleOrbits must be boolean" # scale planetary orbits by sqrt(L) self.scaleOrbits = scaleOrbits assert isinstance(constrainOrbits,bool), "constrainOrbits must be boolean" # constrain planetary orbital radii to sma range self.constrainOrbits = constrainOrbits # derive orbital radius range from quantities above ar = self.arange.to('AU').value er = self.erange if self.constrainOrbits: self.rrange = [ar[0], ar[1]]*u.AU else: self.rrange = [ar[0]*(1. - er[1]), ar[1]*(1. + er[1])]*u.AU assert isinstance(eta, numbers.Number) and (eta > 0),\ "eta must be strictly positive" # global occurrence rate defined as expected number of planets per # star in a given universe self.eta = eta # albedo is constant for planetary radius range self.pfromRp = False # populate all attributes to outspec for att in self.__dict__: if att not in ['vprint','_outspec']: dat = copy.copy(self.__dict__[att]) self._outspec[att] = dat.value if isinstance(dat, u.Quantity) else dat # define prototype distributions of parameters (uniform and log-uniform) self.uniform = lambda x,v: np.array((np.array(x) >=v [0]) & (np.array(x) <= v[1]), dtype=float, ndmin=1) / (v[1] - v[0]) self.logunif = lambda x,v: np.array((np.array(x) >= v[0]) & (np.array(x) <= v[1]), dtype=float, ndmin=1) / (x*np.log(v[1]/v[0])) # import PlanetPhysicalModel self.PlanetPhysicalModel = get_module(specs['modules']['PlanetPhysicalModel'], 'PlanetPhysicalModel')(**specs)
def __init__(self, missionStart=60634, staticStars=True, keepStarCatalog=False, fillPhotometry=False, explainFiltering=False, filterBinaries=True, filterSubM=False, cachedir=None, filter_for_char=False, earths_only=False, **specs): #start the outspec self._outspec = {} # get cache directory self.cachedir = get_cache_dir(cachedir) self._outspec['cachedir'] = self.cachedir specs['cachedir'] = self.cachedir # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # validate TargetList inputs assert isinstance(staticStars, bool), "staticStars must be a boolean." assert isinstance(keepStarCatalog, bool), "keepStarCatalog must be a boolean." assert isinstance(fillPhotometry, bool), "fillPhotometry must be a boolean." assert isinstance(explainFiltering, bool), "explainFiltering must be a boolean." assert isinstance(filterBinaries, bool), "filterBinaries must be a boolean." assert isinstance(filterSubM, bool), "filterSubM must be a boolean." self.staticStars = bool(staticStars) self.keepStarCatalog = bool(keepStarCatalog) self.fillPhotometry = bool(fillPhotometry) self.explainFiltering = bool(explainFiltering) self.filterBinaries = bool(filterBinaries) self.filterSubM = bool(filterSubM) self.filter_for_char = bool(filter_for_char) self.earths_only = bool(earths_only) # check if KnownRVPlanetsTargetList is using KnownRVPlanets if specs['modules']['TargetList'] == 'KnownRVPlanetsTargetList': assert specs['modules']['PlanetPopulation'] == 'KnownRVPlanets', \ 'KnownRVPlanetsTargetList must use KnownRVPlanets' else: assert specs['modules']['PlanetPopulation'] != 'KnownRVPlanets', \ 'This TargetList cannot use KnownRVPlanets' # check if KnownRVPlanetsTargetList is using KnownRVPlanets if specs['modules']['TargetList'] == 'KnownRVPlanetsTargetList': assert specs['modules']['PlanetPopulation'] == 'KnownRVPlanets', \ 'KnownRVPlanetsTargetList must use KnownRVPlanets' else: assert specs['modules']['PlanetPopulation'] != 'KnownRVPlanets', \ 'This TargetList cannot use KnownRVPlanets' # populate outspec for att in self.__dict__: if att not in ['vprint', '_outspec']: dat = self.__dict__[att] self._outspec[att] = dat.value if isinstance( dat, u.Quantity) else dat # get desired module names (specific or prototype) and instantiate objects self.StarCatalog = get_module(specs['modules']['StarCatalog'], 'StarCatalog')(**specs) self.OpticalSystem = get_module(specs['modules']['OpticalSystem'], 'OpticalSystem')(**specs) self.ZodiacalLight = get_module(specs['modules']['ZodiacalLight'], 'ZodiacalLight')(**specs) self.PostProcessing = get_module(specs['modules']['PostProcessing'], 'PostProcessing')(**specs) self.Completeness = get_module(specs['modules']['Completeness'], 'Completeness')(**specs) # bring inherited class objects to top level of Simulated Universe self.BackgroundSources = self.PostProcessing.BackgroundSources #if specs contains a completeness_spec then we are going to generate separate instances #of planet population and planet physical model for completeness and for the rest of the sim if 'completeness_specs' in specs: self.PlanetPopulation = get_module( specs['modules']['PlanetPopulation'], 'PlanetPopulation')(**specs) self.PlanetPhysicalModel = self.PlanetPopulation.PlanetPhysicalModel else: self.PlanetPopulation = self.Completeness.PlanetPopulation self.PlanetPhysicalModel = self.Completeness.PlanetPhysicalModel # list of possible Star Catalog attributes self.catalog_atts = [ 'Name', 'Spec', 'parx', 'Umag', 'Bmag', 'Vmag', 'Rmag', 'Imag', 'Jmag', 'Hmag', 'Kmag', 'dist', 'BV', 'MV', 'BC', 'L', 'coords', 'pmra', 'pmdec', 'rv', 'Binary_Cut', 'closesep', 'closedm', 'brightsep', 'brightdm' ] # now populate and filter the list self.populate_target_list(**specs) # generate any completeness update data needed self.Completeness.gen_update(self) self.filter_target_list(**specs) # have target list, no need for catalog now (unless asked to retain) if not self.keepStarCatalog: self.StarCatalog = specs['modules']['StarCatalog'] # add nStars to outspec self._outspec['nStars'] = self.nStars # if staticStars is True, the star coordinates are taken at mission start, # and are not propagated during the mission self.starprop_static = None if self.staticStars is True: allInds = np.arange(self.nStars, dtype=int) missionStart = Time(float(missionStart), format='mjd', scale='tai') self.starprop_static = lambda sInds, currentTime, eclip=False, \ c1=self.starprop(allInds, missionStart, eclip=False), \ c2=self.starprop(allInds, missionStart, eclip=True): \ c1[sInds] if eclip==False else c2[sInds]
def __init__(self, obscurFac=0.1, shapeFac=np.pi/4, pupilDiam=4, intCutoff=50, dMag0=15, WA0=None, scienceInstruments=None, QE=0.9, optics=0.5, FoV=10, pixelNumber=1000, pixelSize=1e-5, sread=1e-6, idark=1e-4, CIC=1e-3, texp=100, radDos=0, PCeff=0.8, ENF=1, Rs=50, lenslSamp=2, starlightSuppressionSystems=None, lam=500, BW=0.2, occ_trans=0.2, core_thruput=0.1, core_contrast=1e-10, core_platescale=None, PSF=np.ones((3,3)), ohTime=1, observingModes=None, SNR=5, timeMultiplier=1., IWA=None, OWA=None, ref_dMag=3, ref_Time=0, cachedir=None, use_char_minintTime=False, **specs): #start the outspec self._outspec = {} # load the vprint function (same line in all prototype module constructors) self.vprint = vprint(specs.get('verbose', True)) # load all values with defaults self.obscurFac = float(obscurFac) # obscuration factor (fraction of PM area) self.shapeFac = float(shapeFac) # shape factor self.pupilDiam = float(pupilDiam)*u.m # entrance pupil diameter self.intCutoff = float(intCutoff)*u.d # integration time cutoff self.dMag0 = float(dMag0) # favorable dMag for calc_minintTime self.ref_dMag = float(ref_dMag) # reference star dMag for RDI self.ref_Time = float(ref_Time) # fraction of time spent on ref star for RDI self.use_char_minintTime = use_char_minintTime # pupil collecting area (obscured PM) self.pupilArea = (1 - self.obscurFac)*self.shapeFac*self.pupilDiam**2 # spectral flux density ~9.5e7 [ph/s/m2/nm] @ 500nm # F0(lambda) function of wavelength, based on Traub et al. 2016 (JATIS): self.F0 = lambda l: 1e4*10**(4.01 - (l.to('nm').value - 550)/770) \ *u.ph/u.s/u.m**2/u.nm # get cache directory self.cachedir = get_cache_dir(cachedir) # loop through all science Instruments (must have one defined) assert scienceInstruments, "No science instrument defined." self.scienceInstruments = scienceInstruments self._outspec['scienceInstruments'] = [] for ninst, inst in enumerate(self.scienceInstruments): assert isinstance(inst, dict), "Science instruments must be defined as dicts." assert 'name' in inst and isinstance(inst['name'], basestring), \ "All science instruments must have key name." # populate with values that may be filenames (interpolants) inst['QE'] = inst.get('QE', QE) self._outspec['scienceInstruments'].append(inst.copy()) # quantum efficiency if isinstance(inst['QE'], basestring): pth = os.path.normpath(os.path.expandvars(inst['QE'])) assert os.path.isfile(pth), "%s is not a valid file."%pth dat = fits.open(pth)[0].data assert len(dat.shape) == 2 and 2 in dat.shape, \ param_name + " wrong data shape." lam, D = (dat[0], dat[1]) if dat.shape[0] == 2 else (dat[:,0], dat[:,1]) assert np.all(D >= 0) and np.all(D <= 1), \ "QE must be positive and smaller than 1." # parameter values outside of lam Dinterp = scipy.interpolate.interp1d(lam.astype(float), D.astype(float), kind='cubic', fill_value=0., bounds_error=False) inst['QE'] = lambda l: np.array(Dinterp(l.to('nm').value), ndmin=1)/u.photon elif isinstance(inst['QE'], numbers.Number): assert inst['QE'] >= 0 and inst['QE'] <= 1, \ "QE must be positive and smaller than 1." inst['QE'] = lambda l, QE=float(inst['QE']): np.array([QE]*l.size, ndmin=1)/u.photon # load detector specifications inst['optics'] = float(inst.get('optics', optics)) # attenuation due to optics inst['FoV'] = float(inst.get('FoV', FoV))*u.arcsec # field of view inst['pixelNumber'] = int(inst.get('pixelNumber', pixelNumber)) # array format inst['pixelSize'] = float(inst.get('pixelSize', pixelSize))*u.m # pixel pitch inst['pixelScale'] = inst.get('pixelScale', 2*inst['FoV'].value/inst['pixelNumber'])*u.arcsec # pixel pitch inst['idark'] = float(inst.get('idark', idark))/u.s # dark-current rate inst['CIC'] = float(inst.get('CIC', CIC)) # clock-induced-charge inst['sread'] = float(inst.get('sread', sread)) # effective readout noise inst['texp'] = float(inst.get('texp', texp))*u.s # exposure time per frame inst['ENF'] = float(inst.get('ENF', ENF)) # excess noise factor inst['PCeff'] = float(inst.get('PCeff', PCeff)) # photon counting efficiency # parameters specific to spectrograph if 'spec' in inst['name'].lower(): # spectral resolving power inst['Rs'] = float(inst.get('Rs', Rs)) # lenslet sampling, number of pixel per lenslet rows or cols inst['lenslSamp'] = float(inst.get('lenslSamp', lenslSamp)) else: inst['Rs'] = 1. inst['lenslSamp'] = 1. # calculate focal and f-number inst['focal'] = inst['pixelSize'].to('m')/inst['pixelScale'].to('rad').value inst['fnumber'] = float(inst['focal']/self.pupilDiam) # populate detector specifications to outspec for att in inst: if att not in ['QE']: dat = inst[att] self._outspec['scienceInstruments'][ninst][att] = dat.value \ if isinstance(dat, u.Quantity) else dat # loop through all starlight suppression systems (must have one defined) assert starlightSuppressionSystems, "No starlight suppression systems defined." self.starlightSuppressionSystems = starlightSuppressionSystems self.haveOcculter = False self._outspec['starlightSuppressionSystems'] = [] for nsyst,syst in enumerate(self.starlightSuppressionSystems): assert isinstance(syst,dict),\ "Starlight suppression systems must be defined as dicts." assert 'name' in syst and isinstance(syst['name'],basestring),\ "All starlight suppression systems must have key name." # populate with values that may be filenames (interpolants) syst['occ_trans'] = syst.get('occ_trans', occ_trans) syst['core_thruput'] = syst.get('core_thruput', core_thruput) syst['core_contrast'] = syst.get('core_contrast', core_contrast) syst['core_mean_intensity'] = syst.get('core_mean_intensity') # no default syst['core_area'] = syst.get('core_area', 0.) # if zero, will get from lam/D syst['PSF'] = syst.get('PSF', PSF) self._outspec['starlightSuppressionSystems'].append(syst.copy()) # attenuation due to optics specific to the coronagraph (defaults to 1) # e.g. polarizer, Lyot stop, extra flat mirror syst['optics'] = float(syst.get('optics', 1.)) # set an occulter, for an external or hybrid system syst['occulter'] = syst.get('occulter', False) if syst['occulter'] == True: self.haveOcculter = True # handle inf OWA if syst.get('OWA') == 0: syst['OWA'] = np.Inf # when provided, always use deltaLam instead of BW (bandwidth fraction) syst['lam'] = float(syst.get('lam', lam))*u.nm # central wavelength (nm) syst['deltaLam'] = float(syst.get('deltaLam', syst['lam'].to('nm').value* syst.get('BW', BW)))*u.nm # bandwidth (nm) syst['BW'] = float(syst['deltaLam']/syst['lam']) # bandwidth fraction # default lam and BW updated with values from first instrument if nsyst == 0: lam, BW = syst.get('lam').value, syst.get('BW') # get coronagraph input parameters syst = self.get_coro_param(syst, 'occ_trans') syst = self.get_coro_param(syst, 'core_thruput') syst = self.get_coro_param(syst, 'core_contrast', fill=1.) syst = self.get_coro_param(syst, 'core_mean_intensity') syst = self.get_coro_param(syst, 'core_area') syst['core_platescale'] = syst.get('core_platescale', core_platescale) # get PSF if isinstance(syst['PSF'], basestring): pth = os.path.normpath(os.path.expandvars(syst['PSF'])) assert os.path.isfile(pth), "%s is not a valid file."%pth hdr = fits.open(pth)[0].header dat = fits.open(pth)[0].data assert len(dat.shape) == 2, "Wrong PSF data shape." assert np.any(dat), "PSF must be != 0" syst['PSF'] = lambda l, s, P=dat: P else: assert np.any(syst['PSF']), "PSF must be != 0" syst['PSF'] = lambda l, s, P=np.array(syst['PSF']).astype(float): P # loading system specifications syst['IWA'] = syst.get('IWA', 0.1 if IWA is None else IWA)*u.arcsec # inner WA syst['OWA'] = syst.get('OWA', np.Inf if OWA is None else OWA)*u.arcsec# outer WA syst['ohTime'] = float(syst.get('ohTime', ohTime))*u.d # overhead time # populate system specifications to outspec for att in syst: if att not in ['occ_trans', 'core_thruput', 'core_contrast', 'core_mean_intensity', 'core_area', 'PSF']: dat = syst[att] self._outspec['starlightSuppressionSystems'][nsyst][att] \ = dat.value if isinstance(dat, u.Quantity) else dat # loop through all observing modes # if no observing mode defined, create a default mode if observingModes == None: inst = self.scienceInstruments[0] syst = self.starlightSuppressionSystems[0] observingModes = [{'detectionMode': True, 'instName': inst['name'], 'systName': syst['name']}] self.observingModes = observingModes self._outspec['observingModes'] = [] for nmode, mode in enumerate(self.observingModes): assert isinstance(mode, dict), "Observing modes must be defined as dicts." assert 'instName' in mode and 'systName' in mode, \ "All observing modes must have key instName and systName." assert np.any([mode['instName'] == inst['name'] for inst in \ self.scienceInstruments]), "The mode's instrument name " \ + mode['instName'] + " does not exist." assert np.any([mode['systName'] == syst['name'] for syst in \ self.starlightSuppressionSystems]), "The mode's system name " \ + mode['systName'] + " does not exist." self._outspec['observingModes'].append(mode.copy()) # loading mode specifications mode['SNR'] = float(mode.get('SNR', SNR)) mode['timeMultiplier'] = float(mode.get('timeMultiplier', timeMultiplier)) mode['detectionMode'] = mode.get('detectionMode', False) mode['inst'] = [inst for inst in self.scienceInstruments \ if inst['name'] == mode['instName']][0] mode['syst'] = [syst for syst in self.starlightSuppressionSystems \ if syst['name'] == mode['systName']][0] # get mode wavelength and bandwidth (get system's values by default) # when provided, always use deltaLam instead of BW (bandwidth fraction) syst_lam = mode['syst']['lam'].to('nm').value syst_BW = mode['syst']['BW'] mode['lam'] = float(mode.get('lam', syst_lam))*u.nm mode['deltaLam'] = float(mode.get('deltaLam', mode['lam'].value \ *mode.get('BW',syst_BW)))*u.nm mode['BW'] = float(mode['deltaLam']/mode['lam']) # get mode IWA and OWA: rescale if the mode wavelength is different than # the wavelength at which the system is defined mode['IWA'] = mode['syst']['IWA'] mode['OWA'] = mode['syst']['OWA'] if mode['lam'] != mode['syst']['lam']: mode['IWA'] = mode['IWA']*mode['lam']/mode['syst']['lam'] mode['OWA'] = mode['OWA']*mode['lam']/mode['syst']['lam'] # radiation dosage, goes from 0 (beginning of mission) to 1 (end of mission) mode['radDos'] = float(mode.get('radDos', radDos)) # check for only one detection mode allModes = self.observingModes detModes = list(filter(lambda mode: mode['detectionMode'] == True, allModes)) assert len(detModes) <= 1, "More than one detection mode specified." # if not specified, default detection mode is first imager mode if len(detModes) == 0: imagerModes = list(filter(lambda mode: 'imag' in mode['inst']['name'], allModes)) if imagerModes: imagerModes[0]['detectionMode'] = True # if no imager mode, default detection mode is first observing mode else: allModes[0]['detectionMode'] = True # load favorable working angle (WA0) for calc_minintTime, # or calculate it from detection IWA-OWA midpoint value try: self.WA0 = float(WA0)*u.arcsec except TypeError: mode = list(filter(lambda mode: mode['detectionMode'] == True, self.observingModes))[0] self.WA0 = 2.*mode['IWA'] if np.isinf(mode['OWA']) else (mode['IWA'] + mode['OWA'])/2. # populate fundamental IWA and OWA as required IWAs = [x.get('IWA') for x in self.observingModes if x.get('IWA') is not None] if IWA is not None: self.IWA = float(IWA)*u.arcsec elif IWAs: self.IWA = min(IWAs) else: raise ValueError("Could not determine fundamental IWA.") OWAs = [x.get('OWA') for x in self.observingModes if x.get('OWA') is not None] if OWA is not None: self.OWA = float(OWA)*u.arcsec if OWA != 0 else np.inf*u.arcsec elif OWAs: self.OWA = max(OWAs) else: raise ValueError("Could not determine fundamental OWA.") assert self.IWA < self.OWA, "Fundamental IWA must be smaller that the OWA." # populate outspec with all OpticalSystem scalar attributes for att in self.__dict__: if att not in ['vprint', 'F0', 'scienceInstruments', 'starlightSuppressionSystems', 'observingModes','_outspec']: dat = self.__dict__[att] self._outspec[att] = dat.value if isinstance(dat, u.Quantity) else dat