def __init__(self, lsstDB, propConf, propName, propFullName, sky, weather, sessionID, filters, targetList=None, dbTableDict=None, log=False, logfile='./Proposal.log', verbose=0): """ Standard initializer. lsstDB LSST DB access object propConf file containing this instance's configuration data propName specific proposal's name sky: an AsronomycalSky instance. weather: a Weather instance. sessionID: An integer identifying this particular run. filters: a Filters instance targetList: the name (with path) of the TEXT file containing the field list. It is assumed that the target list is a three column list of RA, Dec and field ID. RA and Dec are assumed to be in decimal degrees; filed ID is assumed to be an integer uniquely identifying any give field. dbTableDict: log False if not set, else: log = logging.getLogger("...") logfile Name (and path) of the desired log file. Defaults "./Instrument.log". verbose: Log verbosity: 0=minimal, 1=wordy, >1=very verbose """ # Simulation.Process.__init__ (self) self.lsstDB = lsstDB self.sessionID = sessionID self.dbTableDict = dbTableDict self.propConf = propConf self.propName = propName self.propFullName = propFullName # Setup logging if (verbose < 0): logfile = "/dev/null" elif not log: print "Setting up Proposal logger" log = logging.getLogger("Proposal") hdlr = logging.FileHandler(logfile) formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s") hdlr.setFormatter(formatter) log.addHandler(hdlr) log.setLevel(logging.INFO) self.log = log self.logfile = logfile self.verbose = verbose self.missedHistory = MissedHistory(lsstDB=self.lsstDB, dbTableDict=self.dbTableDict, log=self.log, logfile=self.logfile, verbose=self.verbose) if self.log and self.verbose > 1: self.log.info('Proposal: init()') config_dict, pairs = readConfFile(propConf) try: self.relativeProposalPriority = config_dict['RelativeProposalPriority'] except: self.relativeProposalPriority = 1.0 if self.log and self.verbose: self.log.info('%sProp: relative proposal priority = %f' % (self.propFullName, self.relativeProposalPriority)) try: self.targetlist = self.loadTargetList(targetList) except: if self.log and self.verbose: self.log.info('Proposal: init: No or invalid target list.') self.targetlist = None try: self.AcceptSerendipity = eval(str(config_dict['AcceptSerendipity'])) except: self.AcceptSerendipity = True try: self.AcceptConsecutiveObs = eval(str(config_dict['AcceptConsecutiveObs'])) except: self.AcceptConsecutiveObs = True self.maxSeeing = config_dict["MaxSeeing"] # self.reuseRankingCount = config_dict['reuseRankingCount'] try: self.hiatusNights = config_dict['HiatusNextNight'] except: self.hiatusNights = 0 try: self.twilightBoundary = config['TwilightBoundary'] except: self.twilightBoundary = -18.0 try: filterNames = config_dict["Filter"] filterMinBrig = config_dict["Filter_MinBrig"] filterMaxBrig = config_dict["Filter_MaxBrig"] except: filters_dict, pairs = readConfFile(DefaultFiltersConfigFile) filterNames = filters_dict["Filter_Defined"] filterMinBrig = filters_dict["Filter_MinBrig"] filterMaxBrig = filters_dict["Filter_MaxBrig"] if not isinstance(filterNames, list): filterNames = [filterNames] if not isinstance(filterMinBrig, list): filterMinBrig = [filterMinBrig] if not isinstance(filterMaxBrig, list): filterMaxBrig = [filterMaxBrig] try: filterMaxSeeing = config_dict["Filter_MaxSeeing"] except: filterMaxSeeing = [] for i in range(len(filterNames)): filterMaxSeeing.append(self.maxSeeing) if not isinstance(filterMaxSeeing, list): filterMaxSeeing = [filterMaxSeeing] self.FilterNames = [] self.FilterMinBrig = {} self.FilterMaxBrig = {} self.FilterMaxSeeing = {} for ix in range(len(filterNames)): self.FilterNames.append(filterNames[ix]) self.FilterMinBrig[filterNames[ix]] = filterMinBrig[ix] self.FilterMaxBrig[filterNames[ix]] = filterMaxBrig[ix] self.FilterMaxSeeing[filterNames[ix]] = filterMaxSeeing[ix] self.FilterNames = sorted(self.FilterNames) self.log.info('propConf=%s filters=%s' % (self.propConf, str(self.FilterNames))) try: self.StartTime = eval(str(config_dict["StartTime"])) except: self.StartTime = None try: self.StopTime = eval(str(config_dict["StopTime"])) except: self.StopTime = None # load object instances self.sky = sky self.filters = filters self.weather = weather # the scheduled observations self.observations = [] # the observations that have been already # carried out (and removed from self.observations) self.completedObservations = [] # the current observation sequence self.sequence = [] self.queue = [] self.winners = [] self.loosers = [] self.getPropID() self.log.info('FilterNames for propID=%d %s = %s' % (self.propID, self.propFullName, str(self.FilterNames))) self.log.info('FilterMinBrig for propID=%d %s = %s' % (self.propID, self.propFullName, repr(self.FilterMinBrig))) self.log.info('FilterMaxBrig for propID=%d %s = %s' % (self.propID, self.propFullName, repr(self.FilterMaxBrig))) self.log.info('FilterMaxSeeing for propID=%d %s = %s' % (self.propID, self.propFullName, str(self.FilterMaxSeeing))) return
class Proposal(object): def __init__(self, lsstDB, propConf, propName, propFullName, sky, weather, sessionID, filters, targetList=None, dbTableDict=None, log=False, logfile='./Proposal.log', verbose=0): """ Standard initializer. lsstDB LSST DB access object propConf file containing this instance's configuration data propName specific proposal's name sky: an AsronomycalSky instance. weather: a Weather instance. sessionID: An integer identifying this particular run. filters: a Filters instance targetList: the name (with path) of the TEXT file containing the field list. It is assumed that the target list is a three column list of RA, Dec and field ID. RA and Dec are assumed to be in decimal degrees; filed ID is assumed to be an integer uniquely identifying any give field. dbTableDict: log False if not set, else: log = logging.getLogger("...") logfile Name (and path) of the desired log file. Defaults "./Instrument.log". verbose: Log verbosity: 0=minimal, 1=wordy, >1=very verbose """ # Simulation.Process.__init__ (self) self.lsstDB = lsstDB self.sessionID = sessionID self.dbTableDict = dbTableDict self.propConf = propConf self.propName = propName self.propFullName = propFullName # Setup logging if (verbose < 0): logfile = "/dev/null" elif not log: print "Setting up Proposal logger" log = logging.getLogger("Proposal") hdlr = logging.FileHandler(logfile) formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s") hdlr.setFormatter(formatter) log.addHandler(hdlr) log.setLevel(logging.INFO) self.log = log self.logfile = logfile self.verbose = verbose self.missedHistory = MissedHistory(lsstDB=self.lsstDB, dbTableDict=self.dbTableDict, log=self.log, logfile=self.logfile, verbose=self.verbose) if self.log and self.verbose > 1: self.log.info('Proposal: init()') config_dict, pairs = readConfFile(propConf) try: self.relativeProposalPriority = config_dict['RelativeProposalPriority'] except: self.relativeProposalPriority = 1.0 if self.log and self.verbose: self.log.info('%sProp: relative proposal priority = %f' % (self.propFullName, self.relativeProposalPriority)) try: self.targetlist = self.loadTargetList(targetList) except: if self.log and self.verbose: self.log.info('Proposal: init: No or invalid target list.') self.targetlist = None try: self.AcceptSerendipity = eval(str(config_dict['AcceptSerendipity'])) except: self.AcceptSerendipity = True try: self.AcceptConsecutiveObs = eval(str(config_dict['AcceptConsecutiveObs'])) except: self.AcceptConsecutiveObs = True self.maxSeeing = config_dict["MaxSeeing"] # self.reuseRankingCount = config_dict['reuseRankingCount'] try: self.hiatusNights = config_dict['HiatusNextNight'] except: self.hiatusNights = 0 try: self.twilightBoundary = config['TwilightBoundary'] except: self.twilightBoundary = -18.0 try: filterNames = config_dict["Filter"] filterMinBrig = config_dict["Filter_MinBrig"] filterMaxBrig = config_dict["Filter_MaxBrig"] except: filters_dict, pairs = readConfFile(DefaultFiltersConfigFile) filterNames = filters_dict["Filter_Defined"] filterMinBrig = filters_dict["Filter_MinBrig"] filterMaxBrig = filters_dict["Filter_MaxBrig"] if not isinstance(filterNames, list): filterNames = [filterNames] if not isinstance(filterMinBrig, list): filterMinBrig = [filterMinBrig] if not isinstance(filterMaxBrig, list): filterMaxBrig = [filterMaxBrig] try: filterMaxSeeing = config_dict["Filter_MaxSeeing"] except: filterMaxSeeing = [] for i in range(len(filterNames)): filterMaxSeeing.append(self.maxSeeing) if not isinstance(filterMaxSeeing, list): filterMaxSeeing = [filterMaxSeeing] self.FilterNames = [] self.FilterMinBrig = {} self.FilterMaxBrig = {} self.FilterMaxSeeing = {} for ix in range(len(filterNames)): self.FilterNames.append(filterNames[ix]) self.FilterMinBrig[filterNames[ix]] = filterMinBrig[ix] self.FilterMaxBrig[filterNames[ix]] = filterMaxBrig[ix] self.FilterMaxSeeing[filterNames[ix]] = filterMaxSeeing[ix] self.FilterNames = sorted(self.FilterNames) self.log.info('propConf=%s filters=%s' % (self.propConf, str(self.FilterNames))) try: self.StartTime = eval(str(config_dict["StartTime"])) except: self.StartTime = None try: self.StopTime = eval(str(config_dict["StopTime"])) except: self.StopTime = None # load object instances self.sky = sky self.filters = filters self.weather = weather # the scheduled observations self.observations = [] # the observations that have been already # carried out (and removed from self.observations) self.completedObservations = [] # the current observation sequence self.sequence = [] self.queue = [] self.winners = [] self.loosers = [] self.getPropID() self.log.info('FilterNames for propID=%d %s = %s' % (self.propID, self.propFullName, str(self.FilterNames))) self.log.info('FilterMinBrig for propID=%d %s = %s' % (self.propID, self.propFullName, repr(self.FilterMinBrig))) self.log.info('FilterMaxBrig for propID=%d %s = %s' % (self.propID, self.propFullName, repr(self.FilterMaxBrig))) self.log.info('FilterMaxSeeing for propID=%d %s = %s' % (self.propID, self.propFullName, str(self.FilterMaxSeeing))) return def __del__(self): """ Destructor: close the log file. """ try: self.log.close() except: pass del(self) return def IsActive(self, date, nightCnt): """ Is proposal active right now? """ if self.StartTime is not None: if self.StartTime > date: return False if self.StopTime is not None: if self.StopTime < date: return False # proposal is activated but skips nights if self.nextNight != nightCnt: return False return True def IsActiveTonight(self, date, nightCnt): """ Is proposal active sometime tonight? If so, we will get its target list. """ if self.StartTime is not None: if self.StartTime > date + 24 * 3600: return False if self.StopTime is not None: if self.StopTime < date: return False # proposal is activated but skips nights if self.nextNight != nightCnt: return False return True def GetPriority(self): return self.relativeProposalPriority def buildUserRegionDB(self, regions, fieldTable): """ Build a short-term DB containing a user-specified subset of the FieldDB. The DB table will need to be deleted at the end of the run or during a subsequent garbage collection run on the DB. Input regions list of (ra,dec,diameter) defining cone of interest for a user defined region. All parameters are radians. fieldTable base Field DB table from which to draw the overlapping fields Output tablename temporary Field DB table name """ # Create short-term overlappingField table overlappingField = "OlapField_%d_%d" % (self.sessionID, self.propID) # sql = 'drop table if exists %s; ' %(overlappingField) # print sql # # Send the SQL commmand to the DB # (n, res) = self.lsstDB.executeSQL (sql) # sql = 'create table %s (fieldID int unsigned not null primary key,' % (overlappingField) # sql += 'fieldFov float NOT NULL,fieldRA float NOT NULL,fieldDec float NOT NULL,fieldGL float NOT '\ # 'NULL,fieldGB float NOT NULL,fieldEL float NOT NULL,fieldEB float NOT NULL);' # # Send the SQL commmand to the DB # (n, res) = self.lsstDB.executeSQL(sql) olapTable = self.lsstDB.createOlapTable(overlappingField) # Load entire FieldDB in-core sql = 'select fieldID,fieldFov,fieldRA,fieldDec,fieldGL,fieldGB,fieldEL,fieldEB from %s order by '\ 'fieldID;' % (fieldTable) # Send the SQL commmand to the DB (n, res) = self.lsstDB.executeSQL(sql) # for each entry in regions: # parse into components, #ra_rad = [] #dec_rad = [] #diameter_div2_rad = [] for k in range(len(regions)): ra_deg, dec_deg, diameter_deg = regions[k].split(',', 3) ra_rad = float(ra_deg) * DEG2RAD dec_rad = float(dec_deg) * DEG2RAD diameter_div2_rad = float(diameter_deg) * DEG2RAD / 2 # for each entry in FieldDB closestField = None closestDistance = None for (id, fov, ra, dec, gl, gb, el, eb) in res: # convert (ra,dec) to radians ofRa = ra * DEG2RAD ofDec = dec * DEG2RAD # Create distance measure to determine overlapping region&field ofFov_div2 = fov * DEG2RAD / 2 distance = dist(ra_rad, dec_rad, ofRa, ofDec) if distance < (ofFov_div2 + diameter_div2_rad): if closestDistance is None: closestDistance = distance closestField = (id, fov, ra, dec, gl, gb, el, eb) else: if distance < closestDistance: closestDistance = distance closestField = (id, fov, ra, dec, gl, gb, el, eb) if closestField is not None: (id, fov, ra, dec, gl, gb, el, eb) = closestField self.lsstDB.addOlap(olapTable, id, fov, ra, dec, gl, gb, el, eb) self.log.info('Proposal: buildUserRegionDB(): Field=%i, RA=%f DEC=%f' % (id, ra, dec)) return olapTable def getPropID(self): """ Create an entry in the self.dbTableDict['proposal'] and fetch the key which have been assigned to us. Raise Exception if there are errors in the SQL or if the connection to the database fails. """ if self.log and self.verbose > 1: self.log.info('Proposal: getPropID()') # Get the short hostname self.host = os.getenv('OPSIM_HOSTNAME') if self.host is None or self.host == "": import socket self.host = socket.gethostname().split('.', 1)[0] self.host = self.host.replace('-', '_') # Get the object ID of self self.objID = id(self) ## Remove data from the previous run where session, host and object are ## the same. This should not happen but seems to sometimes .... #sql = 'DELETE FROM %s WHERE ' % (self.dbTableDict["proposal"]) #sql += 'sessionId=%d AND ' % (self.sessionID) #sql += 'objectHost="%s" AND ' % (self.host) #sql += 'objectID=%d' % (self.objID) #(n,res) = self.lsstDB.executeSQL (sql) # Create a new entry # sql = 'INSERT INTO %s VALUES (NULL, ' % (self.dbTableDict["proposal"]) # sql += '"%s", ' % (self.propConf) # sql += '"%s", ' % (self.propName) # sql += '%d, ' % (self.sessionID) # sql += '%d, ' % (self.objID) # sql += '"%s" )' % (self.host) # (n,res) = self.lsstDB.executeSQL (sql) # # # Now fetch the propID we got assigned # sql = 'SELECT propID FROM %s WHERE ' % (self.dbTableDict["proposal"]) # sql += 'propConf="%s" AND ' % (self.propConf) # sql += 'propName="%s" AND ' % (self.propName) # sql += 'sessionID=%d AND ' % (self.sessionID) # sql += 'objectHost="%s" AND ' % (self.host) # sql += 'objectID=%d' % (self.objID) # (n,res) = self.lsstDB.executeSQL (sql) # self.propID = res[0][0] oProposal = self.lsstDB.addProposal(self.propConf, self.propName, self.sessionID, self.host, self.objID) self.propID = oProposal.propID return def transparencyOK(self, currentTransparency): """ Routine to determine if current cloud transparency is within range of proposal's minimum cloud transparency. Input currentTransparency value from Cloud DB for current timestep Output True, if within proposal's limit; else, False """ if currentTransparency < self.minTransparency: return True return False def startNewYear(self): """ Routine to handle all year-end requirements """ self.log.info("Proposal:startNewYear") return def startNight(self, dateProfile, moonProfile, startNewLunation, mountedFiltersList): """ Update the target list and do any other beginning of Night setup step. Input dateProfile: current profile of date as list: (date, mjd,lst_RAD) where: date in seconds from Jan 1 of simulated year. mjd - modified Julian date lst_RAD - local sidereal time at site (radians) moonProfile: current profile of the moon as list: (moonRA_RAD,moonDec_RAD, moonPhase_PERCENT) startNewLunation: True -> new lunation starting, False otherwise Return None """ self.log.info("Proposal:startNight propID=%d" % (self.propID)) # Create a pool of Observation instances (& garbage collect old Obs?) self.obsPool = {} for fieldID in self.targets.keys(): (ra, dec) = self.targets[fieldID] self.obsPool[fieldID] = {} for filter in self.filters.filterNames: self.obsPool[fieldID][filter] = Observation(dateProfile=dateProfile, moonProfile=moonProfile, proposal=self, ra=ra, dec=dec, filter=filter, maxSeeing=self.maxSeeing, exposureTime=self.exposureTime, fieldID=fieldID, slewTime=-1., log=self.log, logfile=self.logfile, verbose=self.verbose) self.last_observed_fieldID = None self.last_observed_filter = None self.last_observed_wasForThisProposal = False self.mountedFiltersList = mountedFiltersList # rank all targets # self.reuseRanking = 0 return def start(self): """ Activate the Proposal instance in the simulation and create the appropriate number of Observation objects. Activate each Observation object and configure them. """ if self.log and self.verbose > 1: self.log.info('Proposal: start() propID=%d' % (self.propID)) mu = AVGPRIORITY sigma = mu * (SIGMAPERCENT / 100.) for target in self.targets: ra = target[0] dec = target[1] #id = target[2] t = EXPTIME pri = random.normalvariate(mu, sigma) # create the observation with the desired exposure time # and priority. self.observations.append(Observation(proposal=self, ra=ra, dec=dec, exposureTime=t, priority=pri, verbose=self.verbose)) # wait 1 second before sumbitting the next observation # yield hold, self return def loadTargetList(self, fileName=None): """ load a target list (RA Dec ID) from fileName. """ if self.log and self.verbose > 1: self.log.info('Proposal: loadTargetList(): Targets file = %s' % (fileName)) t = [] list = file(fileName).readlines() for line in list: line = string.strip(line) # skip comments (identified by a #) if (line[0] == '#'): continue # try and parse the line, skip the line # in case of error (silently) try: (ra, dec, id) = string.split(line) except: continue ra = float(ra) dec = float(dec) id = int(float(id)) if self.log and self.verbose > 2: self.log.info("ra=%f dec=%f id=%d" % (ra, dec, id)) t.append((ra, dec, id)) return t # Accessor Methods def getSeeing(self): """ Query the Weather instance for the current seeing value. """ return self.weather.getSeeing(now()) # Ranker Functionality # def applyCuts (self): # """ # Discard fields by applying pre-defined cuts. It is assumed # that cuts are to be appied as of now(). # # This method is invoked by suggestObs() and should not be # called directly. # """ # return # def applyBonuses (self): # """ # Assign fields a ranking (in absolute scale) by applying # pre-defined bonus rules. It is assumed that bonuses are to be # evaluated as of now(). # # This method is invoked by suggestObs() and should not be # called directly. # """ # return def suggestObs(self, dateProfile, moonProfile, n=1, fieldID=None, AmSbFlS=None): """ Return the list of the n (currently) higher ranking fields. This method is the public interface to the Proposal class. Input dateProfile current date profile of: (date,mjd,lst_RAD) where: date simulated time (s) mjd lst_RAD local sidereal time (radians) moonProfile current moon profile of: (moonRA_RAD,moonDec_RAD,moonPhase_PERCENT) n number of observations to return. fieldID: array of fieldIDs AmSbFlS: array of [AirMass, Sky brightness, Filterlist, Seeing] values (synced to fieldID) Return An array of n Observation objects. """ if self.log and self.verbose > 1: self.log.info('Proposal: suggestObs()') # self.applyCuts () # self.applyBonuses () return def getFieldCoordinates(self, fieldID): """ Given a fieldID, fetch the corresponding values for RA and Dec Input fieldID: a field identifier (long int) Return (ra, dec) in decimal degrees Raise Exception if fieldID is unknown. """ return (self.targets[fieldID]) def setMaxSeeing(self, seeing): """ Setter method for self.seeing Input seeing: float Return None """ self.maxSeeing = float(seeing) return def setSlewTime(self, slewTime): """ Setter method for self.maxSlewTime Input slewTime: float Return None """ self.maxSlewTime = float(slewTime) return def closeObservation(self, obs, obsHistID, twilightProfile): """ Remove the corresponding field from self.targets Input obs temp Observation instance containing only essential identifying data to match with real/complete Proposal Observation records winSlewTime the slew time required by the winning Observation finRank ... Return None Raise Exception if Observation History update fails """ if self.log and self.verbose > 1: self.log.info('Proposal: closeObservation() propID=%d' % (self.propID)) #self.log.info("Proposal:closeObs: #winners:%d #losers:%d" %( len(self.winners),len(self.loosers))) self.last_observed_fieldID = obs.fieldID self.last_observed_filter = obs.filter self.last_observed_wasForThisProposal = False winner = None # Find obs in self.winners, count if exposure time was equal to or greater than desired exposure time. for o in self.winners: # if (o.fieldID == obs.fieldID and o.filter == obs.filter and o.exposureTime == obs.exposureTime): if (o.fieldID == obs.fieldID and o.filter == obs.filter): if o.exposureTime <= obs.exposureTime: winner = o # print "Proposal: closeObs date=%d field=%d filter=%s propID=%d dist2moon=%f" %\ # (obs.date, obs.fieldID, obs.filter, self.propID, o.distance2moon) self.winners.remove(o) break # If the observation was not found among the winners, # look for it in the losers set. if winner is None: for o in self.loosers: # if o.fieldID == obs.fieldID and o.filter == obs.filter and # o.exposureTime >= obs.exposureTime: if o.fieldID == obs.fieldID and o.filter == obs.filter: if o.exposureTime <= obs.exposureTime: winner = o self.loosers.remove(o) break # if winner is not None: # # It found it! Serendipitus # if self.log and self.verbose > 0: # self.log.info('Proposal: closeObservation() propID=%d SERENDIPITOUS' % (self.propID)) if winner is not None: self.last_observed_wasForThisProposal = True self.lsstDB.addObsHistoryProposal(self.propID, obsHistID, self.sessionID, winner.propRank) self.log.info("propID=%i night=%i date=%i field=%i filter=%s expTime=%f visitTime=%f " "lst=%f" % (self.propID, winner.night, winner.date, winner.fieldID, winner.filter, winner.exposureTime, winner.visitTime, winner.lst)) self.log.info(" propRank=%f airmass=%f brightness=%f filtBright=%f rawSeeing=%f " "seeing=%f" % (winner.propRank, winner.airmass, winner.skyBrightness, winner.filterSkyBright, winner.rawSeeing, winner.seeing)) self.log.info(" alt=%f az=%f pa=%f moonRA=%f moonDec=%f moonPh=%f dist2moon=%f " "transp=%f" % (winner.altitude, winner.azimuth, winner.parallactic, winner.moonRA_RAD, winner.moonDec_RAD, winner.moonPhase, winner.distance2moon, winner.transparency)) # self.log.info(" solarE=%f sunAlt=%f sunAz=%f moonAlt=%f moonAz=%f moonBright=%f " # "darkBright=%f" % (winner.solarElong, winner.sunAlt, winner.sunAz, winner.moonAlt, # winner.moonAz, winner.moonBright, winner.darkBright)) # Update suggested Observation with actual observing conditions # obsfound.date = obs.date # obsfound.mjd = obs.mjd # obsfound.lst = obs.lst # obsfound.finRank = obs.finRank # obsfound.slewTime = obs.slewTime # obsfound.rotatorSkyPos = obs.rotatorSkyPos # obsfound.rotatorTelPos = obs.rotatorTelPos # obsfound.altitude = obs.altitude # obsfound.azimuth = obs.azimuth # obsfound.sunAlt = obs.sunAlt # obsfound.sunAz = obs.sunAz # obsfound.exposureTime = obs.exposureTime # obsfound.night = obs.night # get most current skyBrightness # dateProfile = obs.date, obs.mjd, obs.lst # moonProfile = (obsfound.moonRA_RAD, obsfound.moonDec_RAD, # obsfound.moonPhase) # (skyBright,distance2moon,moonAlt_RAD,brightProfile) = \ # self.sky.getSkyBrightness (obsfound.fieldID, # obsfound.ra, obsfound.dec, # obsfound.altitude, # dateProfile, # moonProfile, # twilightProfile) #print "latest skyBright = %f\toriginal skyBright = %f" % (skyBright, obsfound.skyBrightness) # (obsfound.phaseAngle, obsfound.extinction, obsfound.rScatter, # obsfound.mieScatter, obsfound.moonIllum, # obsfound.moonBright, obsfound.darkBright) = brightProfile # store new value since it is most accurate # obsfound.skyBrightness = skyBright # obsfound.distance2moon = distance2moon # obsfound.moonAlt = moonAlt_RAD # calculate airmass of actual observation # obsfound.airmass = 1/math.cos(1.5708 - obsfound.altitude) # calculate current solar elongation in DEGREES # target = (obsfound.ra*DEG2RAD, obsfound.dec*DEG2RAD) # solarElong_RAD = self.sky.getPlanetDistance ('Sun',target, obs.date) # obsfound.solarElong = math.degrees(solarElong_RAD) # Derive skyBrightness for filter. Interpolate if needed. - MM # (sunrise, sunset) = twilightProfile # set y skybrightness for any kind of sky # if (obsfound.filter == 'y'): # obsfound.filterSkyBright = 17.3 # else: # g,r,i,z,u # If moon below horizon, use new moon offset for filter brightness - MM # if (math.degrees(obsfound.moonAlt) <= -6.0): # adjustBright = filterOffset[obsfound.filter,0.] # Interpolate if needed. Note: moonPhase is a float not int # elif (obsfound.moonPhase not in skyBrightKeys): # i = 0 # while (skyBrightKeys[i] < obsfound.moonPhase): # i = i+1 # find upper and lower bound # upperMoonPhase = skyBrightKeys[i] # lowerMoonPhase = skyBrightKeys[i-1] # lowerAdjustBright = filterOffset[obsfound.filter,lowerMoonPhase] # upperAdjustBright = filterOffset[obsfound.filter,upperMoonPhase] # linear interpolation # adjustBright = lowerAdjustBright + (((obsfound.moonPhase - lowerMoonPhase) * # (upperAdjustBright - lowerAdjustBright))/(upperMoonPhase - lowerMoonPhase)) # else: # moon not set and moon phase is key # adjustBright = filterOffset[obsfound.filter, obsfound.moonPhase] # obsfound.filterSkyBright = obsfound.skyBrightness + adjustBright # #z sky brightness should never be under 17.0 # if (obsfound.filter == 'z') and (obsfound.filterSkyBright < 17.0): # obsfound.filterSkyBright = 17.0 # If twilight, set brightness for z and y # if ( obs.date < sunset) or (obs.date > sunrise): # if (obsfound.filter == 'z') or (obsfound.filter == 'y'): # obsfound.filterSkyBright = 17.0 # build visit history fieldID = obs.fieldID filter = obs.filter try: self.fieldVisits[fieldID] += 1 except: self.fieldVisits[fieldID] = 0 winner.fieldVisits = self.fieldVisits[fieldID] try: winner.fieldInterval = obs.date - self.lastFieldVisit[fieldID] except: winner.fieldInterval = 0 self.lastFieldVisit[fieldID] = obs.date if fieldID not in self.lastFieldFilterVisit: self.lastFieldFilterVisit[fieldID] = {filter: obs.date} else: if filter not in self.lastFieldFilterVisit[fieldID]: self.lastFieldFilterVisit[fieldID][filter] = obs.date winner.fieldFilterInterval = obs.date - self.lastFieldFilterVisit[fieldID][filter] self.lastFieldFilterVisit[fieldID][filter] = obs.date # obsfound.slewDistance = slalib.sla_dsep(self.lastTarget[0], self.lastTarget[1], # obsfound.ra * DEG2RAD, obsfound.dec * DEG2RAD) self.lastTarget = (winner.ra * DEG2RAD, winner.dec * DEG2RAD) # update the ObsHistory DB with the new observation # self.obsHistory.addObservation (obsfound, self.sessionID) return winner def missObservation(self, obs): self.missedHistory.addMissed(obs, self.sessionID) return def clearSuggestList(self): self.queue = [] return def addToSuggestList(self, observation): # Compute an internal rank considering proximity and use it as # the sorting key to the heap. # rankProximity = self.maxProximityBonus/(proximity+0.1)/0.1 rankInternal = observation.propRank # Add the relative proposal priority coefficient. observation.propRank *= self.relativeProposalPriority # Add it to queue (heappush places min on top, so invert rank) heapq.heappush(self.queue, (-rankInternal, observation)) return def getSuggestList(self, n=1): # Choose the n highest ranking observations self.winners = [] for i in range(n): try: self.winners.append(heapq.heappop(self.queue)[1]) if self.log and self.verbose > 1: self.log.info('Proposal: suggestObs() propID=%d field=%i propRank=%.2f' % (self.propID, self.winners[-1].fieldID, self.winners[-1].propRank)) except: break # ZZZ - MM commented out. Why should we sort the list by fieldID? #self.winners.sort(compareWinners) self.loosers = [] while self.AcceptSerendipity: try: self.loosers.append(heapq.heappop(self.queue)[1]) except: break return (self.winners) def computeTargetsHAatTwilight(self, lst_twilight_RAD): self.ha_twilight = {} # for field in self.targets.keys(): for field in sorted(self.targets.iterkeys()): (ra, dec) = self.targets[field] ha = (lst_twilight_RAD - ra * DEG2RAD) * 12.0 / math.pi if ha < -12.0: ha += 24 elif ha > 12: ha -= 24 self.ha_twilight[field] = ha def allowedFiltersForBrightness(self, brightness): """ Computes a list of adequate filters according to the sky brightness """ # NOTE Require sky brightness to be precomputed and passed as parameter filterList = [] for filter in self.FilterNames: if (filter in self.mountedFiltersList) and \ (self.FilterMinBrig[filter] < brightness < self.FilterMaxBrig[filter]): filterList.append(filter) return filterList
class Proposal(object): def __init__(self, lsstDB, propConf, propName, propFullName, sky, weather, sessionID, filters, targetList=None, dbTableDict=None, log=False, logfile='./Proposal.log', verbose=0): """ Standard initializer. lsstDB LSST DB access object propConf file containing this instance's configuration data propName specific proposal's name sky: an AsronomycalSky instance. weather: a Weather instance. sessionID: An integer identifying this particular run. filters: a Filters instance targetList: the name (with path) of the TEXT file containing the field list. It is assumed that the target list is a three column list of RA, Dec and field ID. RA and Dec are assumed to be in decimal degrees; filed ID is assumed to be an integer uniquely identifying any give field. dbTableDict: log False if not set, else: log = logging.getLogger("...") logfile Name (and path) of the desired log file. Defaults "./Instrument.log". verbose: Log verbosity: 0=minimal, 1=wordy, >1=very verbose """ # Simulation.Process.__init__ (self) self.lsstDB = lsstDB self.sessionID = sessionID self.dbTableDict = dbTableDict self.propConf = propConf self.propName = propName self.propFullName = propFullName # Setup logging if (verbose < 0): logfile = "/dev/null" elif not log: print "Setting up Proposal logger" log = logging.getLogger("Proposal") hdlr = logging.FileHandler(logfile) formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s") hdlr.setFormatter(formatter) log.addHandler(hdlr) log.setLevel(logging.INFO) self.log = log self.logfile = logfile self.verbose = verbose self.missedHistory = MissedHistory(lsstDB=self.lsstDB, dbTableDict=self.dbTableDict, log=self.log, logfile=self.logfile, verbose=self.verbose) if self.log and self.verbose > 1: self.log.info('Proposal: init()') config_dict, pairs = readConfFile(propConf) try: self.relativeProposalPriority = config_dict['RelativeProposalPriority'] except: self.relativeProposalPriority = 1.0 if self.log and self.verbose: self.log.info('%sProp: relative proposal priority = %f' % (self.propFullName, self.relativeProposalPriority)) try: self.targetlist = self.loadTargetList(targetList) except: if self.log and self.verbose: self.log.info('Proposal: init: No or invalid target list.') self.targetlist = None try: self.AcceptSerendipity = eval(str(config_dict['AcceptSerendipity'])) except: self.AcceptSerendipity = True try: self.AcceptConsecutiveObs = eval(str(config_dict['AcceptConsecutiveObs'])) except: self.AcceptConsecutiveObs = True self.maxSeeing = config_dict["MaxSeeing"] # self.reuseRankingCount = config_dict['reuseRankingCount'] try: self.hiatusNights = config_dict['HiatusNextNight'] except: self.hiatusNights = 0 try: self.twilightBoundary = config['TwilightBoundary'] except: self.twilightBoundary = -18.0 try: filterNames = config_dict["Filter"] filterMinBrig = config_dict["Filter_MinBrig"] filterMaxBrig = config_dict["Filter_MaxBrig"] except: filters_dict, pairs = readConfFile(DefaultFiltersConfigFile) filterNames = filters_dict["Filter_Defined"] filterMinBrig = filters_dict["Filter_MinBrig"] filterMaxBrig = filters_dict["Filter_MaxBrig"] if not isinstance(filterNames, list): filterNames = [filterNames] if not isinstance(filterMinBrig, list): filterMinBrig = [filterMinBrig] if not isinstance(filterMaxBrig, list): filterMaxBrig = [filterMaxBrig] try: filterMaxSeeing = config_dict["Filter_MaxSeeing"] except: filterMaxSeeing = [] for i in range(len(filterNames)): filterMaxSeeing.append(self.maxSeeing) if not isinstance(filterMaxSeeing, list): filterMaxSeeing = [filterMaxSeeing] self.FilterNames = [] self.FilterMinBrig = {} self.FilterMaxBrig = {} self.FilterMaxSeeing = {} for ix in range(len(filterNames)): self.FilterNames.append(filterNames[ix]) self.FilterMinBrig[filterNames[ix]] = filterMinBrig[ix] self.FilterMaxBrig[filterNames[ix]] = filterMaxBrig[ix] self.FilterMaxSeeing[filterNames[ix]] = filterMaxSeeing[ix] self.FilterNames = sorted(self.FilterNames) self.log.info('propConf=%s filters=%s' % (self.propConf, str(self.FilterNames))) try: self.StartTime = eval(str(config_dict["StartTime"])) except: self.StartTime = None try: self.StopTime = eval(str(config_dict["StopTime"])) except: self.StopTime = None # load object instances self.sky = sky self.filters = filters self.weather = weather # the scheduled observations self.observations = [] # the observations that have been already # carried out (and removed from self.observations) self.completedObservations = [] # the current observation sequence self.sequence = [] self.queue = [] self.winners = [] self.loosers = [] self.getPropID() self.log.info('FilterNames for propID=%d %s = %s' % (self.propID, self.propFullName, str(self.FilterNames))) self.log.info('FilterMinBrig for propID=%d %s = %s' % (self.propID, self.propFullName, repr(self.FilterMinBrig))) self.log.info('FilterMaxBrig for propID=%d %s = %s' % (self.propID, self.propFullName, repr(self.FilterMaxBrig))) self.log.info('FilterMaxSeeing for propID=%d %s = %s' % (self.propID, self.propFullName, str(self.FilterMaxSeeing))) return def __del__(self): """ Destructor: close the log file. """ try: self.log.close() except: pass super(Proposal, self).__del__() return def IsActive(self, date, nightCnt): """ Is proposal active right now? """ if self.StartTime is not None: if self.StartTime > date: return False if self.StopTime is not None: if self.StopTime < date: return False # proposal is activated but skips nights if self.nextNight != nightCnt: return False return True def IsActiveTonight(self, date, nightCnt): """ Is proposal active sometime tonight? If so, we will get its target list. """ if self.StartTime is not None: if self.StartTime > date + 24 * 3600: return False if self.StopTime is not None: if self.StopTime < date: return False # proposal is activated but skips nights if self.nextNight != nightCnt: return False return True def GetPriority(self): return self.relativeProposalPriority def buildUserRegionDB(self, regions, fieldTable): """ Build a short-term DB containing a user-specified subset of the FieldDB. The DB table will need to be deleted at the end of the run or during a subsequent garbage collection run on the DB. Input regions list of (ra,dec,diameter) defining cone of interest for a user defined region. All parameters are radians. fieldTable base Field DB table from which to draw the overlapping fields Output tablename temporary Field DB table name """ # Create short-term overlappingField table overlappingField = "OlapField_%d_%d" % (self.sessionID, self.propID) # sql = 'drop table if exists %s; ' %(overlappingField) # print sql # # Send the SQL commmand to the DB # (n, res) = self.lsstDB.executeSQL (sql) # sql = 'create table %s (fieldID int unsigned not null primary key,' % (overlappingField) # sql += 'fieldFov float NOT NULL,fieldRA float NOT NULL,fieldDec float NOT NULL,fieldGL float NOT '\ # 'NULL,fieldGB float NOT NULL,fieldEL float NOT NULL,fieldEB float NOT NULL);' # # Send the SQL commmand to the DB # (n, res) = self.lsstDB.executeSQL(sql) olapTable = self.lsstDB.createOlapTable(overlappingField) # Load entire FieldDB in-core sql = 'select fieldID,fieldFov,fieldRA,fieldDec,fieldGL,fieldGB,fieldEL,fieldEB from %s order by '\ 'fieldID;' % (fieldTable) # Send the SQL commmand to the DB (n, res) = self.lsstDB.executeSQL(sql) # for each entry in regions: # parse into components, #ra_rad = [] #dec_rad = [] #diameter_div2_rad = [] for k in range(len(regions)): ra_deg, dec_deg, diameter_deg = regions[k].split(',', 3) ra_rad = float(ra_deg) * DEG2RAD dec_rad = float(dec_deg) * DEG2RAD diameter_div2_rad = float(diameter_deg) * DEG2RAD / 2 # for each entry in FieldDB closestField = None closestDistance = None for (id, fov, ra, dec, gl, gb, el, eb) in res: # convert (ra,dec) to radians ofRa = ra * DEG2RAD ofDec = dec * DEG2RAD # Create distance measure to determine overlapping region&field ofFov_div2 = fov * DEG2RAD / 2 distance = dist(ra_rad, dec_rad, ofRa, ofDec) if distance < (ofFov_div2 + diameter_div2_rad): if closestDistance is None: closestDistance = distance closestField = (id, fov, ra, dec, gl, gb, el, eb) else: if distance < closestDistance: closestDistance = distance closestField = (id, fov, ra, dec, gl, gb, el, eb) if closestField is not None: (id, fov, ra, dec, gl, gb, el, eb) = closestField self.lsstDB.addOlap(olapTable, id, fov, ra, dec, gl, gb, el, eb) self.log.info('Proposal: buildUserRegionDB(): Field=%i, RA=%f DEC=%f' % (id, ra, dec)) return olapTable def getPropID(self): """ Create an entry in the self.dbTableDict['proposal'] and fetch the key which have been assigned to us. Raise Exception if there are errors in the SQL or if the connection to the database fails. """ if self.log and self.verbose > 1: self.log.info('Proposal: getPropID()') # Get the short hostname self.host = os.getenv('OPSIM_HOSTNAME') if self.host is None or self.host == "": import socket self.host = socket.gethostname().split('.', 1)[0] self.host = self.host.replace('-', '_') # Get the object ID of self self.objID = id(self) ## Remove data from the previous run where session, host and object are ## the same. This should not happen but seems to sometimes .... #sql = 'DELETE FROM %s WHERE ' % (self.dbTableDict["proposal"]) #sql += 'sessionId=%d AND ' % (self.sessionID) #sql += 'objectHost="%s" AND ' % (self.host) #sql += 'objectID=%d' % (self.objID) #(n,res) = self.lsstDB.executeSQL (sql) # Create a new entry # sql = 'INSERT INTO %s VALUES (NULL, ' % (self.dbTableDict["proposal"]) # sql += '"%s", ' % (self.propConf) # sql += '"%s", ' % (self.propName) # sql += '%d, ' % (self.sessionID) # sql += '%d, ' % (self.objID) # sql += '"%s" )' % (self.host) # (n,res) = self.lsstDB.executeSQL (sql) # # # Now fetch the propID we got assigned # sql = 'SELECT propID FROM %s WHERE ' % (self.dbTableDict["proposal"]) # sql += 'propConf="%s" AND ' % (self.propConf) # sql += 'propName="%s" AND ' % (self.propName) # sql += 'sessionID=%d AND ' % (self.sessionID) # sql += 'objectHost="%s" AND ' % (self.host) # sql += 'objectID=%d' % (self.objID) # (n,res) = self.lsstDB.executeSQL (sql) # self.propID = res[0][0] oProposal = self.lsstDB.addProposal(self.propConf, self.propName, self.sessionID, self.host, self.objID) self.propID = oProposal.propID return def transparencyOK(self, currentTransparency): """ Routine to determine if current cloud transparency is within range of proposal's minimum cloud transparency. Input currentTransparency value from Cloud DB for current timestep Output True, if within proposal's limit; else, False """ if currentTransparency < self.minTransparency: return True return False def startNewYear(self): """ Routine to handle all year-end requirements """ self.log.info("Proposal:startNewYear") return def startNight(self, dateProfile, moonProfile, startNewLunation, mountedFiltersList): """ Update the target list and do any other beginning of Night setup step. Input dateProfile: current profile of date as list: (date, mjd,lst_RAD) where: date in seconds from Jan 1 of simulated year. mjd - modified Julian date lst_RAD - local sidereal time at site (radians) moonProfile: current profile of the moon as list: (moonRA_RAD,moonDec_RAD, moonPhase_PERCENT) startNewLunation: True -> new lunation starting, False otherwise Return None """ self.log.info("Proposal:startNight propID=%d" % (self.propID)) # Create a pool of Observation instances (& garbage collect old Obs?) self.obsPool = {} for fieldID in self.targets.keys(): (ra, dec) = self.targets[fieldID] self.obsPool[fieldID] = {} for filter in self.filters.filterNames: self.obsPool[fieldID][filter] = Observation(dateProfile=dateProfile, moonProfile=moonProfile, proposal=self, ra=ra, dec=dec, filter=filter, maxSeeing=self.maxSeeing, exposureTime=self.exposureTime, fieldID=fieldID, slewTime=-1., log=self.log, logfile=self.logfile, verbose=self.verbose) self.last_observed_fieldID = None self.last_observed_filter = None self.last_observed_wasForThisProposal = False self.mountedFiltersList = mountedFiltersList # rank all targets # self.reuseRanking = 0 return def start(self): """ Activate the Proposal instance in the simulation and create the appropriate number of Observation objects. Activate each Observation object and configure them. """ if self.log and self.verbose > 1: self.log.info('Proposal: start() propID=%d' % (self.propID)) mu = AVGPRIORITY sigma = mu * (SIGMAPERCENT / 100.) for target in self.targets: ra = target[0] dec = target[1] #id = target[2] t = EXPTIME pri = random.normalvariate(mu, sigma) # create the observation with the desired exposure time # and priority. self.observations.append(Observation(proposal=self, ra=ra, dec=dec, exposureTime=t, priority=pri, verbose=self.verbose)) # wait 1 second before sumbitting the next observation # yield hold, self return def loadTargetList(self, fileName=None): """ load a target list (RA Dec ID) from fileName. """ if self.log and self.verbose > 1: self.log.info('Proposal: loadTargetList(): Targets file = %s' % (fileName)) t = [] list = file(fileName).readlines() for line in list: line = string.strip(line) # skip comments (identified by a #) if (line[0] == '#'): continue # try and parse the line, skip the line # in case of error (silently) try: (ra, dec, id) = string.split(line) except: continue ra = float(ra) dec = float(dec) id = int(float(id)) if self.log and self.verbose > 2: self.log.info("ra=%f dec=%f id=%d" % (ra, dec, id)) t.append((ra, dec, id)) return t # Accessor Methods def getSeeing(self): """ Query the Weather instance for the current seeing value. """ return self.weather.getSeeing(now()) # Ranker Functionality # def applyCuts (self): # """ # Discard fields by applying pre-defined cuts. It is assumed # that cuts are to be appied as of now(). # # This method is invoked by suggestObs() and should not be # called directly. # """ # return # def applyBonuses (self): # """ # Assign fields a ranking (in absolute scale) by applying # pre-defined bonus rules. It is assumed that bonuses are to be # evaluated as of now(). # # This method is invoked by suggestObs() and should not be # called directly. # """ # return def suggestObs(self, dateProfile, moonProfile, n=1, fieldID=None, AmSbFlS=None): """ Return the list of the n (currently) higher ranking fields. This method is the public interface to the Proposal class. Input dateProfile current date profile of: (date,mjd,lst_RAD) where: date simulated time (s) mjd lst_RAD local sidereal time (radians) moonProfile current moon profile of: (moonRA_RAD,moonDec_RAD,moonPhase_PERCENT) n number of observations to return. fieldID: array of fieldIDs AmSbFlS: array of [AirMass, Sky brightness, Filterlist, Seeing] values (synced to fieldID) Return An array of n Observation objects. """ if self.log and self.verbose > 1: self.log.info('Proposal: suggestObs()') # self.applyCuts () # self.applyBonuses () return def getFieldCoordinates(self, fieldID): """ Given a fieldID, fetch the corresponding values for RA and Dec Input fieldID: a field identifier (long int) Return (ra, dec) in decimal degrees Raise Exception if fieldID is unknown. """ return (self.targets[fieldID]) def setMaxSeeing(self, seeing): """ Setter method for self.seeing Input seeing: float Return None """ self.maxSeeing = float(seeing) return def setSlewTime(self, slewTime): """ Setter method for self.maxSlewTime Input slewTime: float Return None """ self.maxSlewTime = float(slewTime) return def closeObservation(self, obs, obsHistID, twilightProfile): """ Remove the corresponding field from self.targets Input obs temp Observation instance containing only essential identifying data to match with real/complete Proposal Observation records winSlewTime the slew time required by the winning Observation finRank ... Return None Raise Exception if Observation History update fails """ if self.log and self.verbose > 1: self.log.info('Proposal: closeObservation() propID=%d' % (self.propID)) #self.log.info("Proposal:closeObs: #winners:%d #losers:%d" %( len(self.winners),len(self.loosers))) self.last_observed_fieldID = obs.fieldID self.last_observed_filter = obs.filter self.last_observed_wasForThisProposal = False winner = None # Find obs in self.winners, count if exposure time was equal to or greater than desired exposure time. for o in self.winners: # if (o.fieldID == obs.fieldID and o.filter == obs.filter and o.exposureTime == obs.exposureTime): if (o.fieldID == obs.fieldID and o.filter == obs.filter): if o.exposureTime <= obs.exposureTime: winner = o # print "Proposal: closeObs date=%d field=%d filter=%s propID=%d dist2moon=%f" %\ # (obs.date, obs.fieldID, obs.filter, self.propID, o.distance2moon) self.winners.remove(o) break # If the observation was not found among the winners, # look for it in the losers set. if winner is None: for o in self.loosers: # if o.fieldID == obs.fieldID and o.filter == obs.filter and # o.exposureTime >= obs.exposureTime: if o.fieldID == obs.fieldID and o.filter == obs.filter: if o.exposureTime <= obs.exposureTime: winner = o self.loosers.remove(o) break # if winner is not None: # # It found it! Serendipitus # if self.log and self.verbose > 0: # self.log.info('Proposal: closeObservation() propID=%d SERENDIPITOUS' % (self.propID)) if winner is not None: self.last_observed_wasForThisProposal = True self.lsstDB.addObsHistoryProposal(self.propID, obsHistID, self.sessionID, winner.propRank) self.log.info("propID=%i night=%i date=%i field=%i filter=%s expTime=%f visitTime=%f " "lst=%f" % (self.propID, winner.night, winner.date, winner.fieldID, winner.filter, winner.exposureTime, winner.visitTime, winner.lst)) self.log.info(" propRank=%f airmass=%f brightness=%f filtBright=%f rawSeeing=%f " "seeing=%f" % (winner.propRank, winner.airmass, winner.skyBrightness, winner.filterSkyBright, winner.rawSeeing, winner.seeing)) self.log.info(" alt=%f az=%f pa=%f moonRA=%f moonDec=%f moonPh=%f dist2moon=%f " "transp=%f" % (winner.altitude, winner.azimuth, winner.parallactic, winner.moonRA_RAD, winner.moonDec_RAD, winner.moonPhase, winner.distance2moon, winner.transparency)) # self.log.info(" solarE=%f sunAlt=%f sunAz=%f moonAlt=%f moonAz=%f moonBright=%f " # "darkBright=%f" % (winner.solarElong, winner.sunAlt, winner.sunAz, winner.moonAlt, # winner.moonAz, winner.moonBright, winner.darkBright)) # Update suggested Observation with actual observing conditions # obsfound.date = obs.date # obsfound.mjd = obs.mjd # obsfound.lst = obs.lst # obsfound.finRank = obs.finRank # obsfound.slewTime = obs.slewTime # obsfound.rotatorSkyPos = obs.rotatorSkyPos # obsfound.rotatorTelPos = obs.rotatorTelPos # obsfound.altitude = obs.altitude # obsfound.azimuth = obs.azimuth # obsfound.sunAlt = obs.sunAlt # obsfound.sunAz = obs.sunAz # obsfound.exposureTime = obs.exposureTime # obsfound.night = obs.night # get most current skyBrightness # dateProfile = obs.date, obs.mjd, obs.lst # moonProfile = (obsfound.moonRA_RAD, obsfound.moonDec_RAD, # obsfound.moonPhase) # (skyBright,distance2moon,moonAlt_RAD,brightProfile) = \ # self.sky.getSkyBrightness (obsfound.fieldID, # obsfound.ra, obsfound.dec, # obsfound.altitude, # dateProfile, # moonProfile, # twilightProfile) #print "latest skyBright = %f\toriginal skyBright = %f" % (skyBright, obsfound.skyBrightness) # (obsfound.phaseAngle, obsfound.extinction, obsfound.rScatter, # obsfound.mieScatter, obsfound.moonIllum, # obsfound.moonBright, obsfound.darkBright) = brightProfile # store new value since it is most accurate # obsfound.skyBrightness = skyBright # obsfound.distance2moon = distance2moon # obsfound.moonAlt = moonAlt_RAD # calculate airmass of actual observation # obsfound.airmass = 1/math.cos(1.5708 - obsfound.altitude) # calculate current solar elongation in DEGREES # target = (obsfound.ra*DEG2RAD, obsfound.dec*DEG2RAD) # solarElong_RAD = self.sky.getPlanetDistance ('Sun',target, obs.date) # obsfound.solarElong = math.degrees(solarElong_RAD) # Derive skyBrightness for filter. Interpolate if needed. - MM # (sunrise, sunset) = twilightProfile # set y skybrightness for any kind of sky # if (obsfound.filter == 'y'): # obsfound.filterSkyBright = 17.3 # else: # g,r,i,z,u # If moon below horizon, use new moon offset for filter brightness - MM # if (math.degrees(obsfound.moonAlt) <= -6.0): # adjustBright = filterOffset[obsfound.filter,0.] # Interpolate if needed. Note: moonPhase is a float not int # elif (obsfound.moonPhase not in skyBrightKeys): # i = 0 # while (skyBrightKeys[i] < obsfound.moonPhase): # i = i+1 # find upper and lower bound # upperMoonPhase = skyBrightKeys[i] # lowerMoonPhase = skyBrightKeys[i-1] # lowerAdjustBright = filterOffset[obsfound.filter,lowerMoonPhase] # upperAdjustBright = filterOffset[obsfound.filter,upperMoonPhase] # linear interpolation # adjustBright = lowerAdjustBright + (((obsfound.moonPhase - lowerMoonPhase) * # (upperAdjustBright - lowerAdjustBright))/(upperMoonPhase - lowerMoonPhase)) # else: # moon not set and moon phase is key # adjustBright = filterOffset[obsfound.filter, obsfound.moonPhase] # obsfound.filterSkyBright = obsfound.skyBrightness + adjustBright # #z sky brightness should never be under 17.0 # if (obsfound.filter == 'z') and (obsfound.filterSkyBright < 17.0): # obsfound.filterSkyBright = 17.0 # If twilight, set brightness for z and y # if ( obs.date < sunset) or (obs.date > sunrise): # if (obsfound.filter == 'z') or (obsfound.filter == 'y'): # obsfound.filterSkyBright = 17.0 # build visit history fieldID = obs.fieldID filter = obs.filter try: self.fieldVisits[fieldID] += 1 except: self.fieldVisits[fieldID] = 0 winner.fieldVisits = self.fieldVisits[fieldID] try: winner.fieldInterval = obs.date - self.lastFieldVisit[fieldID] except: winner.fieldInterval = 0 self.lastFieldVisit[fieldID] = obs.date if fieldID not in self.lastFieldFilterVisit: self.lastFieldFilterVisit[fieldID] = {filter: obs.date} else: if filter not in self.lastFieldFilterVisit[fieldID]: self.lastFieldFilterVisit[fieldID][filter] = obs.date winner.fieldFilterInterval = obs.date - self.lastFieldFilterVisit[fieldID][filter] self.lastFieldFilterVisit[fieldID][filter] = obs.date # obsfound.slewDistance = slalib.sla_dsep(self.lastTarget[0], self.lastTarget[1], # obsfound.ra * DEG2RAD, obsfound.dec * DEG2RAD) self.lastTarget = (winner.ra * DEG2RAD, winner.dec * DEG2RAD) # update the ObsHistory DB with the new observation # self.obsHistory.addObservation (obsfound, self.sessionID) return winner def missObservation(self, obs): self.missedHistory.addMissed(obs, self.sessionID) return def clearSuggestList(self): self.queue = [] return def addToSuggestList(self, observation): # Compute an internal rank considering proximity and use it as # the sorting key to the heap. # rankProximity = self.maxProximityBonus/(proximity+0.1)/0.1 rankInternal = observation.propRank # Add the relative proposal priority coefficient. observation.propRank *= self.relativeProposalPriority # Add it to queue (heappush places min on top, so invert rank) heapq.heappush(self.queue, (-rankInternal, observation)) return def getSuggestList(self, n=1): # Choose the n highest ranking observations self.winners = [] for i in range(n): try: self.winners.append(heapq.heappop(self.queue)[1]) if self.log and self.verbose > 1: self.log.info('Proposal: suggestObs() propID=%d field=%i propRank=%.2f' % (self.propID, self.winners[-1].fieldID, self.winners[-1].propRank)) except: break # ZZZ - MM commented out. Why should we sort the list by fieldID? #self.winners.sort(compareWinners) self.loosers = [] while self.AcceptSerendipity: try: self.loosers.append(heapq.heappop(self.queue)[1]) except: break return (self.winners) def computeTargetsHAatTwilight(self, lst_twilight_RAD): self.ha_twilight = {} # for field in self.targets.keys(): for field in sorted(self.targets.iterkeys()): (ra, dec) = self.targets[field] ha = (lst_twilight_RAD - ra * DEG2RAD) * 12.0 / math.pi if ha < -12.0: ha += 24 elif ha > 12: ha -= 24 self.ha_twilight[field] = ha def allowedFiltersForBrightness(self, brightness): """ Computes a list of adequate filters according to the sky brightness """ # NOTE Require sky brightness to be precomputed and passed as parameter filterList = [] for filter in self.FilterNames: if (filter in self.mountedFiltersList) and \ (self.FilterMinBrig[filter] < brightness < self.FilterMaxBrig[filter]): filterList.append(filter) return filterList