from __future__ import print_function
# Collection of utilities for MAF that relate to Opsim specifically.
import os
import numpy as np
from .outputUtils import printDict
__all__ = ['writeConfigs', 'getFieldData', 'getSimData',
'scaleBenchmarks', 'calcCoaddedDepth']
[docs]def writeConfigs(opsimDb, outDir):
"""
Convenience function to get the configuration information from the opsim database and write
this information to text files 'configSummary.txt' and 'configDetails.txt'.
Parameters
----------
opsimDb : OpsimDatabase
The opsim database from which to pull the opsim configuration information.
Opsim SQLite databases save this configuration information in their config table.
outputDir : str
The path to the output directory, where to write the config*.txt files.
"""
configSummary, configDetails = opsimDb.fetchConfig()
outfile = os.path.join(outDir, 'configSummary.txt')
f = open(outfile, 'w')
printDict(configSummary, 'Summary', f)
f.close()
outfile = os.path.join(outDir, 'configDetails.txt')
f = open(outfile, 'w')
printDict(configDetails, 'Details', f)
f.close()
[docs]def getFieldData(opsimDb, sqlconstraint):
"""
Find the fields (ra/dec/fieldID) relevant for a given sql constraint.
If the opsimDb contains a Fields table, it uses
:meth:`OpsimDatabase.fetchFieldsFromFieldTable()`
to get the fields. If the opsimDb contains only a Summary, it uses
:meth:`OpsimDatabase.fetchFieldsFromSummaryTable()`.
Parameters
----------
opsimDb : OpsimDatabase
An opsim database to use to query for field information.
sqlconstraint : str
A SQL constraint to apply to the query (i.e. find all fields for DD proposal)
Returns
-------
numpy.ndarray
A numpy structured array containing the field information. This data will ALWAYS be in radians.
"""
# Get all fields used for all proposals.
if 'proposalId' not in sqlconstraint:
propids, propTags = opsimDb.fetchPropInfo()
propids = list(propids.keys())
else:
# Parse the propID out of the sqlconstraint.
# example: sqlconstraint: filter = r and (propid = 219 or propid = 155) and propid!= 90
sqlconstraint = sqlconstraint.replace('=', ' = ').replace('(', '').replace(')', '')
sqlconstraint = sqlconstraint.replace("'", '').replace('"', '')
# Allow for choosing all but a particular proposal.
sqlconstraint = sqlconstraint.replace('! =', ' !=')
sqlconstraint = sqlconstraint.replace(' ', ' ')
sqllist = sqlconstraint.split(' ')
propids = []
nonpropids = []
i = 0
while i < len(sqllist):
if sqllist[i].lower() == 'proposalid':
i += 1
if sqllist[i] == "=":
i += 1
propids.append(int(sqllist[i]))
elif sqllist[i] == '!=':
i += 1
nonpropids.append(int(sqllist[i]))
i += 1
if len(propids) == 0:
propids, propTags = opsimDb.fetchPropInfo()
propids = list(propids.keys())
if len(nonpropids) > 0:
for nonpropid in nonpropids:
if nonpropid in propids:
propids.remove(nonpropid)
# And query the field Table.
if 'Field' in opsimDb.tables:
# The field table is always in degrees.
fieldData = opsimDb.fetchFieldsFromFieldTable(propids, degreesToRadians=True)
# Or give up and query the summary table.
else:
fieldData = opsimDb.fetchFieldsFromSummaryTable(sqlconstraint)
return fieldData
[docs]def getSimData(opsimDb, sqlconstraint, dbcols, stackers=None, groupBy='default', tableName=None):
"""
Query an opsim database for the needed data columns and run any required stackers.
Parameters
----------
opsimDb : OpsimDatabase
sqlconstraint : str
SQL constraint to apply to query for observations.
dbcols : list of str
Columns required from the database.
stackers : list of Stackers
Stackers to be used to generate additional columns.
tableName : str
Name of the table to query.
distinctExpMJD : bool
Only select observations with a distinct expMJD value. This is overriden if groupBy is not expMJD.
groupBy : str
Column name to group SQL results by.
Returns
-------
numpy.ndarray
A numpy structured array with columns resulting from dbcols + stackers, for observations matching
the SQLconstraint.
"""
# Get data from database.
simData = opsimDb.fetchMetricData(dbcols, sqlconstraint, groupBy=groupBy, tableName=tableName)
if len(simData) == 0:
raise UserWarning('No data found matching sqlconstraint %s' % (sqlconstraint))
# Now add the stacker columns.
if stackers is not None:
for s in stackers:
simData = s.run(simData)
return simData
[docs]def scaleBenchmarks(runLength, benchmark='design'):
"""
Set the design and stretch values of the number of visits, area of the footprint,
seeing values, FWHMeff values, skybrightness, and single visit depth (based on SRD values).
Scales number of visits for the length of the run, relative to 10 years.
Parameters
----------
runLength : float
The length (in years) of the run.
benchmark : str
design or stretch - which version of the SRD values to return.
requested is another option, in which case the values of the number of visits requested
by the OpSim run (recorded in the Config table) is returned.
Returns
-------
dict of floats
A dictionary containing the number of visits, area of footprint, seeing and FWHMeff values,
skybrightness and single visit depth for either the design or stretch SRD values.
"""
# Set baseline (default) numbers for the baseline survey length (10 years).
baseline = 10.
design = {}
stretch = {}
design['nvisitsTotal'] = 825
stretch['nvisitsTotal'] = 1000
design['Area'] = 18000
stretch['Area'] = 20000
design['nvisits']={'u':56,'g':80, 'r':184, 'i':184, 'z':160, 'y':160}
stretch['nvisits']={'u':70,'g':100, 'r':230, 'i':230, 'z':200, 'y':200}
design['skybrightness'] = {'u':21.8, 'g':22., 'r':21.3, 'i':20.0, 'z':19.1, 'y':17.5} # mag/sq arcsec
stretch['skybrightness'] = {'u':21.8, 'g':22., 'r':21.3, 'i':20.0, 'z':19.1, 'y':17.5}
design['seeing'] = {'u':0.77, 'g':0.73, 'r':0.7, 'i':0.67, 'z':0.65, 'y':0.63} # arcsec - old seeing values
stretch['seeing'] = {'u':0.77, 'g':0.73, 'r':0.7, 'i':0.67, 'z':0.65, 'y':0.63}
design['FWHMeff'] = {'u':0.92, 'g':0.87, 'r':0.83, 'i':0.80, 'z':0.78, 'y':0.76} # arcsec - new FWHMeff values (scaled from old seeing)
stretch['FWHMeff'] = {'u':0.92, 'g':0.87, 'r':0.83, 'i':0.80, 'z':0.78, 'y':0.76}
design['singleVisitDepth'] = {'u':23.9,'g':25.0, 'r':24.7, 'i':24.0, 'z':23.3, 'y':22.1}
stretch['singleVisitDepth'] = {'u':24.0,'g':25.1, 'r':24.8, 'i':24.1, 'z':23.4, 'y':22.2}
# Scale the number of visits.
if runLength != baseline:
scalefactor = float(runLength) / float(baseline)
# Calculate scaled value for design and stretch values of nvisits, per filter.
for f in design['nvisits']:
design['nvisits'][f] = int(np.floor(design['nvisits'][f] * scalefactor))
stretch['nvisits'][f] = int(np.floor(stretch['nvisits'][f] * scalefactor))
if benchmark == 'design':
return design
elif benchmark == 'stretch':
return stretch
else:
raise ValueError("Benchmark value %s not understood: use 'design' or 'stretch'" % (benchmark))
[docs]def calcCoaddedDepth(nvisits, singleVisitDepth):
"""
Calculate the coadded depth expected for a given number of visits and single visit depth.
Parameters
----------
nvisits : dict of ints or floats
Dictionary (per filter) of number of visits
singleVisitDepth : dict of floats
Dictionary (per filter) of the single visit depth
Returns
-------
dict of floats
Dictionary of coadded depths per filter.
"""
coaddedDepth = {}
for f in nvisits:
if f not in singleVisitDepth:
raise ValueError('Filter keys in nvisits and singleVisitDepth must match')
coaddedDepth[f] = float(1.25 * np.log10(nvisits[f] * 10**(0.8*singleVisitDepth[f])))
if not np.isfinite(coaddedDepth[f]):
coaddedDepth[f] = singleVisitDepth[f]
return coaddedDepth