class TransSubSeqProp(Proposal):
    """
    This class is here to describe a Transient Objects case scenario.
    """
    def __init__(self, lsstDB, propConf, propName, propFullName, sky, weather, sessionID, filters,
                 targetList=None, dbTableDict=None, log=False, logfile='./TransSubSeqProp.log', verbose=0,
                 transientConf=DefaultNEAConfigFile):
        """
        Standard initializer.

        lsstDB	    LSST DB access object
        propConf    file name containing the instance's configuration data
        propName    proposal 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 "./NearEarthProp.log".
        verbose:    Log verbosity: -1=none, 0=minimal, 1=wordy, >1=verbose
        transientConf: Near Earth Asteroid Configuration file

        """
        super(TransSubSeqProp, self).__init__(lsstDB=lsstDB, propConf=propConf, propName=propName,
                                              propFullName=propFullName, sky=sky, weather=weather,
                                              sessionID=sessionID, filters=filters, targetList=targetList,
                                              dbTableDict=dbTableDict, log=log, logfile=logfile,
                                              verbose=verbose)

        self.lsstDB = lsstDB
        self.verbose = verbose
        self.dbTableDict = dbTableDict

        # DataBase specifics
        self.dbTable = self.dbTableDict['field']
        self.dbRA = 'fieldRA'
        self.dbDec = 'fieldDec'
        self.dbID = 'fieldID'

        self.winners = []
        self.loosers = []

        self.obsHistory = None
#        self.seqHistory = None
        self.sessionID = sessionID

        # self.targets is a convenience dictionary. Its keys are
        # fieldIDs, its values are the corresponding RA and Dec.
        self.targets = {}
        self.tonightTargets = {}
        self.sequences = {}
        self.tonightSubseqsForTarget = {}

        # Create the ObsHistory instance and cleanup any leftover
        self.obsHistory = ObsHistory(lsstDB=self.lsstDB, dbTableDict=self.dbTableDict, log=self.log,
                                     logfile=self.logfile, verbose=self.verbose)

        self.obsHistory.cleanupProposal(self.propID, self.sessionID)

        # Create the SeqHistory instance and cleanup any leftover
#        self.seqHistory = SeqHistory (lsstDB=self.lsstDB,
#				      dbTableDict=self.dbTableDict,
#                                      log=self.log,
#                                      logfile=self.logfile,
#                                      verbose=self.verbose)

#        self.seqHistory.cleanupProposal (self.propID, self.sessionID)

        self.exclusiveBlockNeeded = False
        self.exclusiveObs = None
        self.exclusiveField = None
        self.exclusiveSubseq = None
        self.SeqCount = 0

        return

    def start(self):
        """
        Activate the TransientProp instance.

        """

        # Removes the subsequences that require 0 events
        N = len(self.subSeqName)
        for n in range(N):
            ix = N - n - 1
            if self.subSeqEvents[ix] == 0:
                self.subSeqName.pop(ix)
                self.subSeqNested.pop(ix)
                self.subSeqFilters.pop(ix)
                self.subSeqExposures.pop(ix)
                self.subSeqEvents.pop(ix)
                self.subSeqMaxMissed.pop(ix)
                self.subSeqInterval.pop(ix)
                self.subSeqWindowStart.pop(ix)
                self.subSeqWindowMax.pop(ix)
                self.subSeqWindowEnd.pop(ix)

        return

    def ComplyToFilterChangeBurstConstraint(self, filterBurstNumber, filterBurstTime, visitTime, readoutTime,
                                            filterTime):

        N = len(self.subSeqName)
        for n in range(N):
            subFilters = self.subSeqFilters[n].split(',')
            nfilters = len(subFilters)
            if nfilters > 1:
                print self.propConf
                print self.subSeqName[n]
                print subFilters
                subVisits = self.subSeqExposures[n].split(',')
                print subVisits
                T = []
                for k in range(nfilters):
                    tk = 0
                    if k > 0:
                        tk += filterTime
                    tk += int(subVisits[k]) * visitTime + (int(subVisits[k]) - 1) * readoutTime
                    T.append(tk)
                print T

                numIdxToCheck = nfilters - 1
                if numIdxToCheck >= filterBurstNumber:
                    for startIdx in range(0, numIdxToCheck - filterBurstNumber + 1):
                        tt = 0
                        for idx in range(startIdx, startIdx + filterBurstNumber):
                            tt += T[idx]
                        if tt < filterBurstTime:
                            print "REJECTED %f < %f" % (tt, filterBurstTime)
                            return False

        return True

    def startNight(self, dateProfile, moonProfile, startNewLunation, randomizeSequencesSelection, nRun,
                   mountedFiltersList):

        super(TransSubSeqProp, self).startNight(dateProfile, moonProfile, startNewLunation,
                                                mountedFiltersList)

        (date, mjd, lst_RAD) = dateProfile
        runProgress = date / YEAR / nRun

        self.tonightTargets = self.targets.copy()

        # deletes all sequences that did not start while the fields were
        # in the region for starting new sequences.
        notstarted = 0
        for fieldID in self.sequences.keys():
            if fieldID not in self.targetsNewSeq.keys():
                if self.sequences[fieldID].IsIdle():
                    # self.log.info('%sProp: startNight() deleted sequence field=%d at progress=%.3f%% '
                    #               'state=%d nevents=%d' % (self.propFullName, fieldID,
                    #                                        100*self.sequences[fieldID].GetProgress(),
                    #                                        self.sequences[fieldID].state,
                    #                                        self.sequences[fieldID].nAllEvents))
                    notstarted += 1
                    del self.sequences[fieldID]

        # counts the active sequences in the target region
        currentActiveSequences = 0
        coaddedProgress = 0.0
        coaddedNumber = 0
        for fieldID in self.sequences.keys():
            if fieldID in self.tonightTargets.keys():
                coaddedProgress += self.sequences[fieldID].GetProgress()
                coaddedNumber += 1
                if (not self.sequences[fieldID].IsLost()) and (not self.sequences[fieldID].IsComplete()):
                    currentActiveSequences += 1

        # creates the sequence object for all the new fields in the restricted area
        newseq = 0
        restartedlost = 0
        restartedcomplete = 0
        keptsequences = 0
        #listOfNewFields=self.targetsNewSeq.keys()
        listOfNewFields = sorted(self.targetsNewSeq.iterkeys())
        while (len(listOfNewFields) > 0 and currentActiveSequences < self.maxNumberActiveSequences and
                (coaddedProgress / max(coaddedNumber, 1) > runProgress or
                    currentActiveSequences < self.minNumberActiveSequences)):
            if randomizeSequencesSelection:
                fieldID = random.choice(listOfNewFields)
            else:
                fieldID = listOfNewFields[0]
            listOfNewFields.remove(fieldID)

            # create a new sequence object
            if fieldID not in self.sequences.keys():
                self.SeqCount += 1
                self.sequences[fieldID] = SuperSequence(self.propID, fieldID, self.SeqCount, self.WLtype,
                                                        self.numGroupedVisits,
                                                        self.masterSubSequence, self.subSeqName,
                                                        self.subSeqNested, self.subSeqFilters,
                                                        self.subSeqExposures, self.subSeqEvents,
                                                        self.subSeqMaxMissed, self.subSeqInterval,
                                                        self.subSeqWindowStart, self.subSeqWindowMax,
                                                        self.subSeqWindowEnd, self.overflowLevel,
                                                        self.progressToStartBoost, self.maxBoostToComplete)
                coaddedNumber += 1
                newseq += 1
                currentActiveSequences += 1
            # was sequence lost?
            elif self.sequences[fieldID].IsLost():
                if self.restartLostSequences:
                    self.SeqCount += 1
                    self.sequences[fieldID].Restart(self.SeqCount)
                    restartedlost += 1
                    currentActiveSequences += 1
                else:
                    # ZZZZ - mm debug 2nd delete of tonightTargets
                    # print "Prop[%d].startNight() while targetsNewSeq: seq lost: delete "\
                    #       "self.tonightTargets[%d] date = %d" % (self.propID, fieldID, date)
                    del self.tonightTargets[fieldID]
            # was sequence completed?
            elif self.sequences[fieldID].IsComplete():
                if self.restartCompleteSequences and not self.overflow:
                    self.SeqCount += 1
                    self.sequences[fieldID].Restart(self.SeqCount)
                    restartedcomplete += 1
                    currentActiveSequences += 1
                else:
                    # ZZZZ - mm debug 2nd delete of tonightTargets
                    # print "Prop[%d].startNight() while targetsNewSeq: seq complete: "\
                    #       "delete self.tonightTargets[%d] date = %d" % (self.propID, fieldID, date)
                    del self.tonightTargets[fieldID]
            #else:
            #    prog = self.sequences[fieldID].GetProgress()
            #    if prog >= 1.0:
            #    print "seqnNum=%i fieldID=%i progress=%f" % (self.sequences[fieldID].seqNum, fieldID, prog,
            #                                                 str(self.sequences[fieldID].allHistory),
            #                                                 self.sequences[fieldID].state)

        # From the broad target area, just keep the fields
        # associated to a target that is not lost and not complete
        self.tonightSubseqsForTarget = {}
        for fieldID in self.tonightTargets.keys():
            if fieldID in self.sequences.keys():
                complete_seq = self.sequences[fieldID].IsComplete() and not self.overflow
                if self.sequences[fieldID].IsLost() or complete_seq:
                    # ZZZZ - mm debug 2nd delete of tonightTargets
                    # print "Prop[%d].startNight() for tonightTargets() in self.sequences: seq is lost or "\
                    #       "complete: delete self.tonightTargets[%d] date = %d" % (self.propID, fieldID,
                    #                                                               date)
                    # print "how would we ever get here?"
                    del self.tonightTargets[fieldID]
                else:
                    keptsequences += 1
                    self.tonightSubseqsForTarget[fieldID] = list(self.sequences[fieldID].subSeqName)
            # only pursue targets that are already started sequences
            else:
                # ZZZZ - mm debug 2nd delete of tonightTargets
                # print "TSS.startNight() for tonightTargets() and not in self.sequences: delete "\
                #       "self.tonightTargets[%d] date = %d" % (fieldID, date)
                del self.tonightTargets[fieldID]

        if coaddedNumber > 0:
            self.globalProgress = coaddedProgress / coaddedNumber
        else:
            self.globalProgress = 0.0

        if self.log:
            self.log.info('%sProp: startNight() propID=%d Sequences: new=%i deletedidle=%i '
                          'restartedlost=%i restartedcomplete=%i total=%i targetprogress=%.3f%% '
                          'runprogress=%.3f%%' % (self.propFullName, self.propID, newseq, notstarted,
                                                  restartedlost, restartedcomplete, keptsequences,
                                                  100 * self.globalProgress, 100 * runProgress))

        return

    def GetProgressPerFilter(self):

        coaddedNumber = 0
        coaddedSubseqProgress = {}
        progressFilter = {}
        numsubseFilter = {}
        # for subseq in self.subSeqName:
        #     coaddedSubseqProgress[subseq] = 0
        for fieldID in self.sequences.keys():
            if not self.sequences[fieldID].IsLost():
                coaddedNumber += 1
                for subseq in self.sequences[fieldID].subSeqName:
                    progress = self.sequences[fieldID].GetProgress(subseq)
                    if subseq in coaddedSubseqProgress.keys():
                        coaddedSubseqProgress[subseq] += progress
                    else:
                        coaddedSubseqProgress[subseq] = progress

                    for filter in self.sequences[fieldID].GetFilterListForSubseq(subseq):
                        if filter in progressFilter.keys():
                            progressFilter[filter] += progress
                            numsubseFilter[filter] += 1
                        else:
                            progressFilter[filter] = progress
                            numsubseFilter[filter] = 1

        if coaddedNumber > 0:
            for subseq in coaddedSubseqProgress.keys():
                coaddedSubseqProgress[subseq] /= coaddedNumber
        if self.log:
            for subseq in coaddedSubseqProgress.keys():
                self.log.info('%sProp: GetProgressPerFilter() propID=%d Sub-Sequence progress: %20s = %.3f%%'
                              % (self.propFullName, self.propID, subseq, 100 * coaddedSubseqProgress[subseq]))

#	progressFilter = {}
#	numsubseFilter = {}
#	for ix in range(len(self.subSeqName)):
#	    subseq =  self.subSeqName[ix]
#	    for filter in self.subSeqFilters[ix].split(','):
#		if filter != '':
#		    if filter in progressFilter.keys():
#			progressFilter[filter] += coaddedSubseqProgress[subseq]
#			numsubseFilter[filter] += 1
#		    else:
#                        progressFilter[filter]  = coaddedSubseqProgress[subseq]
#                        numsubseFilter[filter]  = 1

        for filter in progressFilter.keys():
            progressFilter[filter] /= numsubseFilter[filter]
        if self.log:
            for filter in progressFilter.keys():
                self.log.info('%sProp: GetProgressPerFilter() propID=%d Filter progress: %10s = %.3f%%' %
                              (self.propFullName, self.propID, filter, 100 * progressFilter[filter]))

        return progressFilter

    def MissEvent(self, date, mjd, fieldID, subseq, obsHistID):

        self.sequences[fieldID].MissEvent(date, subseq, obsHistID)
        if self.log and self.verbose > 0 and not self.sequences[fieldID].IsLost():
            t_secs = date % 60
            t_mins = (date % 3600) / 60
            t_hour = (date % 86400) / 3600
            t_days = date / 86400
            progress = 100 * self.sequences[fieldID].GetProgress()
            self.log.info('%sProp: suggestObs() subevent MISSED for propID=%d field=%i subseq=%s '
                          't=%dd%02dh%02dm%02ds progress=%i%%' % (self.propFullName, self.propID, fieldID,
                                                                  subseq, t_days, t_hour, t_mins, t_secs,
                                                                  progress))

        filter = self.sequences[fieldID].GetNextFilter(subseq)
        obs = self.obsPool[fieldID][filter]
        obs.propID = self.propID
        obs.seqn = self.sequences[fieldID].seqNum
        obs.subsequence = subseq
        obs.pairNum = self.sequences[fieldID].GetPairNum(subseq)
        obs.date = date
        obs.mjd = mjd

        super(TransSubSeqProp, self).missObservation(obs)

        return

    def suggestObs(self, dateProfile, n=100, exclusiveObservation=None, minDistance2Moon=0.0, rawSeeing=0.0,
                   seeing=0.0, transparency=0.0, sdnight=0, sdtime=0):
        """
        Return the list of (at most) the n (currently) higher ranking
        observations.

        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)
                            moonPhase       current moon phase in range [0-100]
        n               number of observations to return.

        Return
        An array of the (at most) n highest ranking observations,
        ordered from the highest ranking obs to the lowest.
        """
        if self.log and self.verbose > 1:
            self.log.info('%sProp: suggestObs() propID=%d' % (self.propFullName, self.propID))

        # Copy the input vars
#        inFieldID = skyfields
#	inproximity = proximity
#        intargetProfiles = targetProfiles
        (date, mjd, lst_RAD) = dateProfile
        (moonRA_RAD, moonDec_RAD, moonPhase_PERCENT) = self.schedulingData.moonProfile[sdnight]

        # Check the start/end of observing cycle.
        # For NEA the lunation is checked
        if self.CheckObservingCycle(date):

            # Create a priority queue to choose the best n obs
            self.clearSuggestList()

            # If in an exclusive block, no new observation candidates. If this
            # proposal originated request, it should re-suggest observation.
            if exclusiveObservation is not None:
                # adjust counter for one obs
                # self.reuseRanking -= 1
                if exclusiveObservation.propID == self.propID:

                    # The exclusive block is for this proposal so we just suggest our exclusive observation
                    fieldID = exclusiveObservation.fieldID
                    subseq = self.exclusiveSubseq
                    rank = 1.0

#                    i = inFieldID.index (fieldID)
                    #airmass = self.schedulingData.airmass[fieldID][sdtime]

                    filter = self.sequences[fieldID].GetNextFilter(subseq)
                    #print filter
                    exclusiveBlockRequired = self.sequences[fieldID].GetExclusiveBlockNeed(subseq)
                    #print "exclusiveBlockRequired = %s" % (exclusiveBlockRequired)

                    recordFieldFilter = self.obsPool[fieldID][filter]
                    recordFieldFilter.propID = self.propID
                    recordFieldFilter.subsequence = subseq
                    recordFieldFilter.seqn = self.sequences[fieldID].seqNum
                    recordFieldFilter.pairNum = self.sequences[fieldID].GetPairNum(subseq)
                    recordFieldFilter.date = date
                    recordFieldFilter.mjd = mjd
                    recordFieldFilter.night = sdnight
                    recordFieldFilter.exposureTime = self.exposureTime
                    recordFieldFilter.propRank = rank
                    recordFieldFilter.maxSeeing = self.exclusiveObs.maxSeeing
                    recordFieldFilter.rawSeeing = self.exclusiveObs.rawSeeing
                    recordFieldFilter.seeing = self.exclusiveObs.seeing
                    recordFieldFilter.transparency = self.exclusiveObs.transparency
                    recordFieldFilter.cloudSeeing = self.exclusiveObs.cloudSeeing
                    recordFieldFilter.airmass = self.exclusiveObs.airmass
                    recordFieldFilter.skyBrightness = self.exclusiveObs.skyBrightness
                    recordFieldFilter.lst = lst_RAD
                    recordFieldFilter.altitude = self.exclusiveObs.altitude
                    recordFieldFilter.azimuth = self.exclusiveObs.azimuth
                    recordFieldFilter.distance2moon = self.exclusiveObs.distance2moon
                    recordFieldFilter.moonRA = self.exclusiveObs.moonRA
                    recordFieldFilter.moonDec = self.exclusiveObs.moonDec
                    recordFieldFilter.moonAlt = self.exclusiveObs.moonAlt
                    recordFieldFilter.moonPhase = self.exclusiveObs.moonPhase
                    recordFieldFilter.exclusiveBlockRequired = exclusiveBlockRequired

                    self.addToSuggestList(recordFieldFilter)
                    return self.getSuggestList(1)

                else:
                    # The exclusive block is not for this proposal so we don't propose observations
                    # but we must update the ranking and availability of the exclusive observation for
                    # correct serendipity
                    if exclusiveObservation.fieldID in self.tonightTargets.keys():
                        listOfFieldsToEvaluate = [exclusiveObservation.fieldID]
                    else:
                        listOfFieldsToEvaluate = []
#                    return []
                    numberOfObsToPropose = 0

            else:
                # Normal observation block, all proposals competing
                # If not time to rerank fields, return no suggestions.
                # if self.reuseRanking > 1:
                #     self.reuseRanking -= 1
                #     return []

                #listOfFieldsToEvaluate = self.tonightTargets.keys()
                listOfFieldsToEvaluate = sorted(self.tonightTargets.iterkeys())
                numberOfObsToPropose = n

                # ZZZ - This block needs attention. Do we get here? Does it make sense? - mm
                if self.exclusiveBlockNeeded:
                    # We needed an exclusive block to complete the current event
                    # so we miss this event and evaluate if the sequence is missed or complete
                    fieldID = self.exclusiveField
                    subseq = self.exclusiveSubseq

                    self.exclusiveBlockNeeded = False

                    obsHist = self.lsstDB.addMissedObservation(self.sequences[fieldID].GetNextFilter(subseq),
                                                               date, mjd, sdnight, lst_RAD, self.sessionID,
                                                               fieldID)
                    self.MissEvent(date, mjd, fieldID, subseq, obsHist.missedHistID)

            fields_received = len(listOfFieldsToEvaluate)
            fields_invisible = 0
            fields_moon = 0
            events_waiting = 0
            events_proposed = 0
            events_missed = 0
            seq_lost = 0
            seq_completed = 0
            events_nottonight = 0
            events_nofilter = 0
            events_noseeing = 0

            for fieldID in listOfFieldsToEvaluate:

                fieldRecordList = []

                # ra and dec variables are unused!!!!!
                #ra = self.tonightTargets[fieldID][0]
                #dec = self.tonightTargets[fieldID][1]
                #i = inFieldID.index (fieldID)

                if fieldID == self.last_observed_fieldID and self.last_observed_wasForThisProposal and \
                        not self.AcceptConsecutiveObs:
                    continue

                airmass = self.schedulingData.airmass[sdtime][fieldID]
                if airmass > self.maxAirmass:
                    if self.log and self.verbose > 2:
                        self.log.info('%sProp: suggestObs() propID=%d field=%i too low:%f' %
                                      (self.propFullName, self.propID, fieldID, airmass))
                    fields_invisible += 1
                    continue

                distance2moon = self.schedulingData.dist2moon[sdtime][fieldID]
                if distance2moon < minDistance2Moon:
                    fields_moon += 1
                    # remove the target for the rest of the night if it is too close to the moon
                    # ZZZZ - mm debug 2nd delete of tonightTargets
#                print "Prop[%d].suggestObs():too close to moon - delete self.tonightTargets[%d] date = %d" \
#                    % (self.propID, fieldID, date)
                    del self.tonightTargets[fieldID]
                    continue

                # if not self.sequences[fieldID].HasEventsTonight(date):
                #     seq_nottonight += 1
                #     del self.tonightTargets[fieldID]
                #     continue
                #.............................................................
                # Gets the list of possible filters based on the sky brightness
                skyBrightness = self.schedulingData.brightness[sdtime][fieldID]
                allowedFilterList = self.allowedFiltersForBrightness(skyBrightness)
                filterSeeingList = self.filters.computeFilterSeeing(seeing, airmass)
                #rankForFilters = self.RankFilters(fieldID, filterSeeingList)
                #.............................................................
                for subseq in self.tonightSubseqsForTarget[fieldID]:

                    # Prevents that a lost sequence due to a missed event
                    # keeps beeing evaluated for the other subsequences
                    # wasting time and triggering an error when deleting
                    # the already deleted field from the target list.
                    if self.sequences[fieldID].IsLost():
                        continue

                    allfiltersavailable = True
                    for f in self.sequences[fieldID].GetFilterListForSubseq(subseq):
                        if f not in allowedFilterList:
                            allfiltersavailable = False
                            events_nofilter += 1
                        elif filterSeeingList[f] > self.FilterMaxSeeing[f]:
                            allfiltersavailable = False
                            events_noseeing += 1

                    if allfiltersavailable is False:
                        continue

                    # Boost factor according to the remaining observable days on sky
                    if self.DaysLeftToStartBoost > 0.0 and self.rankDaysLeftMax != 0.0:
                        observableDaysLeft = max((self.ha_twilight[fieldID] + self.ha_maxairmass) * 15.0, 0.0)
                        rankDaysLeft = max(1.0 - observableDaysLeft / self.DaysLeftToStartBoost, 0.0)
                    else:
                        rankDaysLeft = 0.0

                    if self.WLtype or self.sequences[fieldID].IsActive(subseq):
                        (rankTime, timeWindow) = self.sequences[fieldID].RankTimeWindow(subseq, date)
                        rankLossRisk = max(1.0 - 0.5 *
                                           self.sequences[fieldID].GetRemainingAllowedMisses(subseq),
                                           0.0)
                    elif self.sequences[fieldID].IsIdle(subseq):
                        rankTime = self.rankIdleSeq / self.rankTimeMax
                        timeWindow = True
                        rankLossRisk = 0.0
                    else:
                        rankTime = 0.0
                        timeWindow = False
                        rankLossRisk = 0.0

                    if rankTime == -0.1:
                        events_nottonight += 1
                        self.tonightSubseqsForTarget[fieldID].remove(subseq)
                    elif rankTime > 0.0:
                        events_proposed += 1
                        # print 'ha_twilight=' + str(self.ha_twilight[fieldID]) + ' ha_maxairmass='\
                        #       + str(self.ha_maxairmass) + ' observableDaysLeft=' + str(observableDaysLeft)\
                        #       + ' rankDaysLeft=' + str(rankDaysLeft)

                        if timeWindow:
                            factor = self.rankTimeMax
                        else:
                            if self.globalProgress < 1.0:
                                    factor = self.rankIdleSeq / (1.0 - self.globalProgress)
                            elif self.overflowLevel > 0.0:
                                factor = self.rankIdleSeq / (self.overflowLevel / self.globalProgress)

                        rank = rankTime * factor + rankLossRisk * self.rankLossRiskMax \
                            + rankDaysLeft * self.rankDaysLeftMax

                        filter = self.sequences[fieldID].GetNextFilter(subseq)
                        #print 'fieldID='+str(fieldID)+' subseq='+str(subseq)+' filter='+str(filter)
                        exclusiveBlockRequired = self.sequences[fieldID].GetExclusiveBlockNeed(subseq)

                        # Create the corresponding Observation
                        recordFieldFilter = self.obsPool[fieldID][filter]
                        #recordFieldFilter.sessionID = sessionID
                        recordFieldFilter.propID = self.propID
                        recordFieldFilter.subsequence = subseq
                        #recordFieldFilter.fieldID = fieldID
                        #recordFieldFilter.filter = filter
                        recordFieldFilter.seqn = self.sequences[fieldID].seqNum
                        recordFieldFilter.pairNum = self.sequences[fieldID].GetPairNum(subseq)
                        recordFieldFilter.date = date
                        recordFieldFilter.mjd = mjd
                        recordFieldFilter.night = sdnight
                        recordFieldFilter.exposureTime = self.exposureTime
                        #recordFieldFilter.slewTime = slewTime
                        #recordFieldFilter.rotatorSkyPos = 0.0
                        #recordFieldFilter.rotatorTelPos = 0.0
                        recordFieldFilter.propRank = rank
                        #recordFieldFilter.finRank = finRank
                        recordFieldFilter.maxSeeing = self.FilterMaxSeeing[filter]
                        recordFieldFilter.rawSeeing = rawSeeing
                        recordFieldFilter.seeing = filterSeeingList[filter]
                        recordFieldFilter.transparency = transparency
                        #recordFieldFilter.cloudSeeing = intargetProfiles[i][4]
                        recordFieldFilter.airmass = airmass
                        recordFieldFilter.skyBrightness = skyBrightness
                        #recordFieldFilter.ra = ra
                        #recordFieldFilter.dec = dec
                        recordFieldFilter.lst = lst_RAD
                        recordFieldFilter.altitude = self.schedulingData.alt[sdtime][fieldID]
                        recordFieldFilter.azimuth = self.schedulingData.az[sdtime][fieldID]
                        recordFieldFilter.parallactic = self.schedulingData.pa[sdtime][fieldID]
                        recordFieldFilter.distance2moon = distance2moon
                        recordFieldFilter.moonRA = moonRA_RAD
                        recordFieldFilter.moonDec = moonDec_RAD
                        #recordFieldFilter.moonAlt = intargetProfiles[i][8]
                        recordFieldFilter.moonPhase = moonPhase_PERCENT
                        recordFieldFilter.exclusiveBlockRequired = exclusiveBlockRequired

                        fieldRecordList.append(recordFieldFilter)

                    elif rankTime < 0.0:

                        events_missed += 1

                        oh_filter = self.sequences[fieldID].GetNextFilter(subseq)
                        obsHist = self.lsstDB.addMissedObservation(oh_filter, date, mjd, sdnight, lst_RAD,
                                                                   self.sessionID, fieldID)
                        self.MissEvent(date, mjd, fieldID, subseq, obsHist.missedHistID)

                        if self.sequences[fieldID].IsLost():
                            if self.log and self.verbose > 0:
                                self.log.info('%sProp: suggestObs() sequence LOST for propID=%d field=%i '
                                              't=%.0f event missed' % (self.propFullName, self.propID,
                                                                       fieldID, date))

                            # Update the SeqHistory database
                            seq = self.sequences[fieldID]
                            seqHist = self.lsstDB.addSeqHistory(seq.date, date, seq.seqNum, seq.GetProgress(),
                                                                seq.GetNumTargetEvents(),
                                                                seq.GetNumActualEvents(),
                                                                MAX_MISSED_EVENTS, 0, fieldID,
                                                                self.sessionID, self.propID)
                            for obsID in seq.GetListObsID():
                                self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID, self.sessionID)

                            for misID in seq.GetListMisID():
                                self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID,
                                                                       self.sessionID)

                            seq_lost += 1
                            # ZZZZ - mm debug 2nd delete of tonightTargets
                            print "Prop[%d].suggestObs() seq lost: delete self.tonightTargets[%d] date = %d"\
                                  % (self.propID, fieldID, date)
                            del self.tonightTargets[fieldID]
                            # Also remove the posible previously considered subsequences
                            # as the whole sequence is now lost they must not be proposed.
                            fieldRecordList = []
                        # it is also possible that the missed event was the last needed for completing the
                        # sequence in such a case the sequence object determines the completion of the
                        # sequence.
                        elif self.sequences[fieldID].IsComplete():
                            self.CompleteSequence(fieldID, date)
                            seq_completed += 1
                            fieldRecordList = []

                    else:
                        # rankTime==0.0
                        events_waiting += 1

                if self.tonightSubseqsForTarget[fieldID] == []:
                    # ZZZZ - mm debug 2nd delete of tonightTargets
                    # print "Prop[%d].suggestObs() no subseqs for target: delete self.tonightTargets[%d] "\
                    #       "date = %d" % (self.propID, fieldID, date)
                    del self.tonightTargets[fieldID]

                for record in fieldRecordList:
                    self.addToSuggestList(record)

            if self.log and self.verbose > 0:
                self.log.info('%sProp: suggestObs() propID=%d : Fields received=%i invisible=%i moon=%i '
                              'Events nottonight=%i waiting=%i nofilter=%i noseeing=%i proposed=%i missed=%i '
                              'Sequences lost=%i completed=%i' % (self.propFullName, self.propID,
                                                                  fields_received, fields_invisible,
                                                                  fields_moon, events_nottonight,
                                                                  events_waiting, events_nofilter,
                                                                  events_noseeing, events_proposed,
                                                                  events_missed, seq_lost, seq_completed))

            # Choose the n highest ranking observations
            # self.reuseRanking = self.reuseRankingCount
            return self.getSuggestList(numberOfObsToPropose)

        else:
            # The cycle has ended and next one hasn't started yet (full moon for NEA)
            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, observation, obsHistID, twilightProfile):
        """
        Registers the fact that the indicated observation took place.
        This is, the corresponding event in the sequence of the indicated
        fieldID has been executed, and the sequence can continue.

        Input
        obs     an Observation instance
        winslewTime  slew time required for the winning observation

        Return
        None

        Raise
        Exception if Observation History update fails
        """

        if not self.IsObservingCycle():
            self.last_observed_wasForThisProposal = False
            return None

        # if self.log and self.verbose > 1:
        #     self.log.info('%sProp: closeObservation()' % (self.propFullName))

        obs = super(TransSubSeqProp, self).closeObservation(observation, obsHistID, twilightProfile)

        if obs is not None:
            self.sequences[obs.fieldID].ObserveEvent(obs.date, obs.subsequence, obsHistID)
            progress = self.sequences[obs.fieldID].GetProgress()

            if self.log and self.verbose > 0:
                t_secs = obs.date % 60
                t_mins = (obs.date % 3600) / 60
                t_hour = (obs.date % 86400) / 3600
                t_days = obs.date / 86400

                if self.sequences[obs.fieldID].IsEventInProgress(obs.subsequence):
                    progrmod = '+'
                else:
                    progrmod = ''
                self.log.info('%s: closeObservation() propID=%d field=%d filter=%s propRank=%.4f '
                              'finRank=%.4f t=%dd%02dh%02dm%02ds progress=%d%s%%' % (self.propConf,
                                                                                     self.propID, obs.fieldID,
                                                                                     obs.filter, obs.propRank,
                                                                                     obs.finRank, t_days,
                                                                                     t_hour, t_mins, t_secs,
                                                                                     int(100 * progress),
                                                                                     progrmod))

            if obs.exclusiveBlockRequired:
                self.exclusiveBlockNeeded = True
                self.exclusiveObs = copy.copy(obs)
                self.exclusiveField = obs.fieldID
                self.exclusiveSubseq = obs.subsequence
            else:
                self.exclusiveBlockNeeded = False

            # if sequence is complete, then deletes target from tonight's list.
            if progress == 1.0:
                self.CompleteSequence(obs.fieldID, obs.date)
                self.exclusiveBlockNeeded = False

        return obs

    def CompleteSequence(self, fieldID, date):

        # Update sequence history DB
        seq = self.sequences[fieldID]
        seqHist = self.lsstDB.addSeqHistory(seq.date, date, seq.seqNum, seq.GetProgress(),
                                            seq.GetNumTargetEvents(), seq.GetNumActualEvents(),
                                            SUCCESS, 0, fieldID, self.sessionID, self.propID)
        for obsID in seq.GetListObsID():
            self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID, self.sessionID)

        for misID in seq.GetListMisID():
            self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID, self.sessionID)

        if not self.overflow:
            # ZZZZ - mm debug 2nd delete of tonightTargets
            # print "Prop[%d].CompleteSequence() delete self.tonightTargets[%d] date = %d" % (self.propID,
            #                                                                                 fieldID, date)
            if fieldID in self.tonightTargets.keys():
                del self.tonightTargets[fieldID]

        if self.log and self.verbose > 0:
            self.log.info('%sProp: CompleteSequence() sequence COMPLETE for propID=%d field=%d' %
                          (self.propFullName, self.propID, fieldID))

        return

    def FinishSequences(self, obsdate):
        """
        Finishes the current sequences.
        """
        if self.log:
            self.log.info('%sProp: FinishSequences()' % (self.propFullName))

        for fieldID in self.sequences.keys():
            if (not self.sequences[fieldID].IsComplete()) and (not self.sequences[fieldID].IsLost()):
                if self.log:
                    self.log.info('%sProp: suggestObs() propID=%d sequence LOST for field=%i end of cycle' %
                                  (self.propFullName, self.propID, fieldID))
                self.sequences[fieldID].Abort()

                # Update sequence history DB
                seq = self.sequences[fieldID]
                seqHist = self.lsstDB.addSeqHistory(seq.date, obsdate, seq.seqNum, seq.GetProgress(),
                                                    seq.GetNumTargetEvents(), seq.GetNumActualEvents(),
                                                    CYCLE_END, 0, fieldID, self.sessionID, self.propID)
                for obsID in seq.GetListObsID():
                    self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID, self.sessionID)

                for misID in seq.GetListMisID():
                    self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID, self.sessionID)

        return

    def closeProposal(self, time):
        """
        Finishes the current sequences.
        """
        if self.log:
            self.log.info('%sProp: closeProposal()' % (self.propFullName))

        for fieldID in self.sequences.keys():
            if (not self.sequences[fieldID].IsComplete()) and (not self.sequences[fieldID].IsLost()):
                if self.log:
                    self.log.info('%sProp: closeProposal() propID=%d sequence LOST for field=%i end of '
                                  'simulation' % (self.propFullName, self.propID, fieldID))
                #self.sequences[fieldID].Abort()

                # Update sequence history DB
                seq = self.sequences[fieldID]
                seqHist = self.lsstDB.addSeqHistory(seq.date, time, seq.seqNum, seq.GetProgress(),
                                                    seq.GetNumTargetEvents(), seq.GetNumActualEvents(),
                                                    SIMULATION_END, 0, fieldID, self.sessionID, self.propID)
                for obsID in seq.GetListObsID():
                    self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID, self.sessionID)

                for misID in seq.GetListMisID():
                    self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID, self.sessionID)

        # delete OlapField user-defined region table
        if self.userRegion[0] is not None:
            overlappingField = "OlapField_%d_%d" % (self.sessionID, self.propID)
            # Result from below is never used
            self.lsstDB.dropTable(overlappingField)
        return

    def RestartSequences(self):

        if self.log:
            self.log.info('%sProp: RestartFinishedSequences() propID=%d' % (self.propFullName, self.propID))

        for fieldID in self.sequences.keys():
            if self.sequences[fieldID].IsLost() or self.sequences[fieldID].IsComplete():
                self.SeqCount += 1
                self.sequences[fieldID].Restart(self.SeqCount)
                if self.log and self.verbose > 0:
                    self.log.info('%sProp: RestartSequences() sequence for propID=%d field=%i restarted' %
                                  (self.propFullName, self.propID, fieldID))

        return

    def updateTargetList(self, dateProfile, obsProfile, sky, fov):

        return
Exemplo n.º 2
0
class WeakLensingProp(Proposal):
    """
    This class is here to describe a simple case scenario: a Proposal 
    type of object that only cares about seeing values and slew time.
    It does not care about particular fields, filters or anythin else,
    just seeing and slew time.
    """
    def __init__(self,
                 lsstDB,
                 schedulingData,
                 sky,
                 weather,
                 sessionID,
                 filters,
                 fov,
                 weakLensConf=DefaultWLConfigFile,
                 targetList=None,
                 dbTableDict=None,
                 log=True,
                 logfile='./Proposal.log',
                 verbose=0):
        """
        Standard initializer.
        
	lsstDB:     LSST DB Access object        
	sky:        an AsronomycalSky instance.
        weather:    a Weather instance.
        sessionID:  An integer identifying this particular run.
        filters:    ...
        fov:        FoV of the instrument.
        weakLensConf: Weak Lensing Proposal Configuration file
        exposureTime:    Exposure time in seconds.
        maxSeeing:  Maximum acceptable seeing.
        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         ...
        logfile     ...
        verbose:    integer specifying the verbosity level (defaults 
                    to 0 meaning quite).
        """

        super(WeakLensingProp, self).__init__(lsstDB=lsstDB,
                                              propConf=weakLensConf,
                                              propName='WL',
                                              propFullName='WeakLensing',
                                              sky=sky,
                                              weather=weather,
                                              sessionID=sessionID,
                                              filters=filters,
                                              targetList=targetList,
                                              dbTableDict=dbTableDict,
                                              log=log,
                                              logfile=logfile,
                                              verbose=verbose)

        self.lsstDB = lsstDB
        if (self.log and self.verbose > 1):
            self.log.info('WeakLensingProp: init() propID=%d' % (self.propID))

        self.schedulingData = schedulingData
        self.weakLensConf = weakLensConf
        self.GoalVisitsFieldFilter = {}
        self.sky = sky

        config, pairs = readConfFile(weakLensConf)

        self.nextNight = 0
        try:
            self.maxAirmass = config['MaxAirmass']
        except:
            pass

        try:
            self.exposureTime = config['ExposureTime']
        except:
            pass

        try:
            self.goalNVisits = config['NVisits']
        except:
            self.goalNVisits = 30.
        try:
            filterVisits = config["Filter_Visits"]
        except:
            filterVisits = []
            for filter in self.FilterNames:
                filterVisits.append(self.goalNVisits)
        # if singleton, make into a list of one
        if not isinstance(filterVisits, list):
            filterVisits = [filterVisits]

        for ix in range(len(self.FilterNames)):
            self.GoalVisitsFieldFilter[self.FilterNames[ix]] = filterVisits[ix]
        print 'GoalVisitsFieldFilter for propID=%d %s = %s' % (
            self.propID, self.propFullName, str(self.GoalVisitsFieldFilter))

        try:
            self.maxNeedAfterOverflow = config['MaxNeedAfterOverflow']
        except:
            self.maxNeedAfterOverflow = 0.5

        try:
            self.ProgressToStartBoost = config['ProgressToStartBoost']
        except:
            self.ProgressToStartBoost = 1.0

        try:
            self.MaxBoostToComplete = config['MaxBoostToComplete']
        except:
            self.MaxBoostToComplete = 0.0

        try:
            self.scale = config['RankScale']
        except:
            self.scale = 0.1

        try:
            self.taperB = config['taperB']
        except:
            self.taperB = 180.
        try:
            self.taperL = config['taperL']
        except:
            self.taperL = 5.
        try:
            self.peakL = config['peakL']
        except:
            self.peakL = 25.
        try:
            self.deltaLST = config['deltaLST']
        except:
            self.deltaLST = 60.
        try:
            self.maxReach = config['maxReach']
        except:
            self.maxReach = 80.
        try:
            self.minTransparency = config['minTransparency']
        except:
            self.minTransparency = 9.

        if (config.has_key('userRegion')):
            self.userRegion = config["userRegion"]
        else:
            self.userRegion = None

        if (not isinstance(self.userRegion, list)):
            # turn it into a list with one entry
            save = self.userRegion
            self.userRegion = []
            self.userRegion.append(save)

        try:
            self.maxProximityBonus = config['MaxProximityBonus']
        except:
            self.maxProximityBonus = 1.0

        # store config to DB
        for line in pairs:
            storeParam(self.lsstDB, self.sessionID, self.propID, 'weakLensing',
                       line['index'], line['key'], line['val'])

        self.dbField = dbTableDict['field']

        if (self.goalNVisits <= 0):
            self.goalNVisits = 1.

        # Setup FieldFilter visit history for later ObsHistory DB  ingest
        self.fieldVisits = {}
        self.lastFieldVisit = {}
        self.lastFieldFilterVisit = {}
        self.lastTarget = (0.0, 0.0)

        # Setup the relative importance of each filter. For
        # convenience we use a dictionary of the form {filter: N}
        # where N is the desired fraction of the total number of
        # observations for that particular filter. If all the filters
        # were equally desirable, then N would be always equal to
        # 1. / total_number_of_filters.
        n = 0.

        # filterNames now is assigned in the parent class using the
        # filters' configuration file.
        # If this proposal wants to observe in a subset of this filter
        # list, this subset should be specified in the proposal's
        # configuration file.

        self.visits = {}
        self.GoalVisitsField = 0

        for filter in (self.FilterNames):
            if filter in self.GoalVisitsFieldFilter.keys():
                self.visits[filter] = {}
                self.GoalVisitsField += self.GoalVisitsFieldFilter[filter]
        print('GoalVisitsField = %i' % (self.GoalVisitsField))

        # DataBase specifics
        self.dbTableDict = dbTableDict

        self.dbField = self.dbTableDict['field']
        # If user-defined regions have been defined, build new FieldDB
        if not (self.userRegion[0] == None):
            self.dbField = self.buildUserRegionDB(self.userRegion,
                                                  self.dbField)

        print "WeakLensingProp:init: dbField: %s" % (self.dbField)

        self.winners = []

        self.obsHistory = None
        self.sessionID = sessionID

        # self.targets is a convenience dictionary. Its keys are
        # fieldIDs, its values are the corresponding RA and Dec.
        self.targets = {}

        # Create the ObsHistory instance and cleanup any leftover
        self.obsHistory = ObsHistory(lsstDB=self.lsstDB,
                                     dbTableDict=self.dbTableDict,
                                     log=self.log,
                                     logfile=self.logfile,
                                     verbose=self.verbose)

        self.obsHistory.cleanupProposal(self.propID, self.sessionID)

        self.ha_maxairmass = sky.getHAforAirmass(self.maxAirmass)

        return

    def start(self):
        """
        Activate the WeakLensingProp instance.
        """
        if (self.log and self.verbose > 1):
            self.log.info('WeakLensingProp: start() propID=%d' % (self.propID))

        # PAUSE
        yield hold, self
        return

    def startNight(self, dateProfile, moonProfile, startNewLunation,
                   randomizeSequencesSelection, nRun, mountedFiltersList):

        super(WeakLensingProp,
              self).startNight(dateProfile, moonProfile, startNewLunation,
                               mountedFiltersList)

    def GetProgressPerFilter(self):

        coaddedFilterProgress = {}
        allfields = []
        filters = self.visits.keys()
        for filter in filters:
            coaddedFilterProgress[filter] = 0
            fields = self.visits[filter].keys()
            allfields += fields
            for fieldID in fields:
                coaddedFilterProgress[filter] += self.visits[filter][fieldID]

        progressFilter = {}
        for filter in filters:
            if self.GoalVisitsFieldFilter[filter] > 0:
                if len(allfields) > 0:
                    progressFilter[filter] = float(
                        coaddedFilterProgress[filter]) / len(
                            allfields) / self.GoalVisitsFieldFilter[filter]
                else:
                    # no fileds observed for this filter
                    progressFilter[filter] = 0.0
            else:
                # no observations required for this filter
                progress[filter] = 1.0

        if (self.log):
            for filter in progressFilter.keys():
                self.log.info(
                    '%sProp: GetProgressPerFilter() propID=%d Filter progress: %10s = %.3f%%'
                    % (self.propFullName, self.propID, filter,
                       100 * progressFilter[filter]))

        return progressFilter

    def suggestObs(self,
                   dateProfile,
                   moonProfile,
                   n=1,
                   skyfields=None,
                   proximity=None,
                   targetProfiles=None,
                   exclusiveObservation=None,
                   minDistance2Moon=0.0):
        """
        Return the list of (at most) the n (currently) higher ranking 
        observations.
        
        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)
                            moonPhase       current moon phase in range [0-100]
        n               number of observations to return.
        skyfields       array of fieldIDs  (with index-synced to proximity)
        proximity       array of proximity distance between current 
                        telescope position and each entry in skyfields (with
                        index-synced to skyfields)
        targetProfiles: array of [AirMass, 
                                Sky brightness,
                                Filterlist,
                                transparency,
                                cloudSeeing,
                                distance2moon,
                                altitude,
                                rawSeeing,
                                moonAltitude]
                        values (dictionary-keyed to fieldID)
        
        Return
        An array of the (at most) n highest ranking observations, 
        ordered from the highest ranking obs to the lowest.
        """
        if (self.log and self.verbose > 1):
            self.log.info('WeakLensingProp: suggestObs() propID=%d' %
                          (self.propID))

        # If in an exclusive block, no new observation candidates. Return null.
#        if (exclusiveObservation != None):
# adjust counter for one obs
#            self.reuseRanking -= 1
#            return []
#        else:
# If not time to rerank fields, return no suggestions.
#            if self.reuseRanking > 1:
#                self.reuseRanking -= 1
#                return []

        if (exclusiveObservation != None):
            if exclusiveObservation.fieldID in self.targets.keys():
                listOfFieldsToEvaluate = [exclusiveObservation.fieldID]
            else:
                listOfFieldsToEvaluate = []
            numberOfObsToPropose = 0
        else:
            listOfFieldsToEvaluate = sorted(self.targets.iterkeys())
            numberOfObsToPropose = n

#        listOfFieldsToEvaluate = self.targets.keys()
#	listOfFieldsToEvaluate = sorted(self.targets.iterkeys())
#        numberOfObsToPropose = n

# Create a priority queue to choose the best n obs
        self.clearSuggestList()

        # Copy the input vars
        inFieldID = skyfields
        inproximity = proximity
        intargetProfiles = targetProfiles
        (date, mjd, lst_RAD) = dateProfile
        (moonRA_RAD, moonDec_RAD, moonPhase_PERCENT) = moonProfile

        # Get the number of times we observed in each field
        # The idea here is to make sure that, over one year, we end up
        # with a balanced set of observations. For the Weak Lensing
        # proposal, we want to observe each field-filter combo approx.
        # GOAL times/year. This means that we do not want to observe
        # each field-filter more than 6 times per lunation or once per
        # night.
        # numVisits is a dict of the form {filter: {fieldID: n}}
        #        visits = self.obsHistory.getNVisitsPerFilterField (self.propID,
        #                                    self.sessionID, fieldID=None)

        #..................   T o   D o  .................................
        # Compute the normalization constant for SignalToNoise (SNR) ranking
        #..................   T o   D o  .................................

        fields_received = len(listOfFieldsToEvaluate)
        fields_invisible = 0
        fields_moon = 0
        ffilter_allowed = 0
        ffilter_badseeing = 0
        ffilter_proposed = 0

        # Adjust the partial rank so that we end with
        # self.sumFieldFilterGoal for all field/filter combos
        # Adjust the scaling
        needTonight = self.GoalVisitsTonight - self.VisitsTonight
        if needTonight > 0:
            GlobalNeedFactor = float(needTonight) / self.GoalVisitsTonight
        else:
            GlobalNeedFactor = (self.maxNeedAfterOverflow /
                                (self.VisitsTonight - self.GoalVisitsTonight +
                                 1)) / self.GoalVisitsTonight

        for fieldID in listOfFieldsToEvaluate:
            i = inFieldID.index(fieldID)
            ra = self.targets[fieldID][0]
            dec = self.targets[fieldID][1]

            if (fieldID == self.last_observed_fieldID) and (
                    self.last_observed_wasForThisProposal) and (
                        not self.AcceptConsecutiveObs):
                continue

            #-----------------------------------------------------------
            #       Field cuts
            #-----------------------------------------------------------
            # First, discard all the targets which are not visible right now.
            airmass = intargetProfiles[i][0]
            if airmass > self.maxAirmass:
                fields_invisible += 1
                if self.log and self.verbose > 1:
                    self.log.info(
                        'TOSS: propID=%d field=%d  WeakLensingProp: suggestObs(): too low:%f'
                        % (self.propID, fieldID, airmass))
            #DBGprint "Toss: Field: %d ra:%f dec:%f am:%f > 5 " % (fieldID,ra,dec,airmass)
                continue

            distance2moon = intargetProfiles[i][5]
            if distance2moon < minDistance2Moon:
                fields_moon += 1
                # remove the target for the rest of the night if it is too close to the moon
                del self.targets[fieldID]
                continue

            nVisits = {}
            progress = {}
            progress_avg = 0.0
            for filter in self.FilterNames:
                try:
                    nVisits[filter] = self.visits[filter][fieldID]
                except:
                    nVisits[filter] = 0.
                progress[filter] = nVisits[
                    filter] / self.GoalVisitsFieldFilter[filter]
                progress_avg += min(progress[filter], 1.0) / len(
                    self.FilterNames)

            FieldNeedFactor = 1.0 - progress_avg
            if self.ProgressToStartBoost < progress_avg < 1.0:
                FieldNeedFactor += self.MaxBoostToComplete * (
                    progress_avg - self.ProgressToStartBoost) / (
                        1.0 - self.ProgressToStartBoost)

            skyBrightness = intargetProfiles[i][1]
            allowedFilterList = self.allowedFiltersForBrightness(skyBrightness)
            filterSeeingList = intargetProfiles[i][2]
            #print ".....field:%d ra:%f dec:%f am:%f phs:%f brite:%f" % (fieldID, ra,dec, airmass, moonPhase, intargetProfiles[i][1]) , filterSeeingList

            for filter in allowedFilterList:
                ffilter_allowed += 1
                #-----------------------------------------------------------
                #       Field/filter cuts
                #-----------------------------------------------------------
                if filterSeeingList[filter] > self.FilterMaxSeeing[filter]:
                    ffilter_badseeing += 1
                    #DBGprint "TOSS: fld:%d fltr:%s ra:%f dec%f airms:%f phs:%f  airAdjSee:%f > maxSee:%f" % (fieldID,filter,ra,dec,airmass,moonPhase,filterSeeingList[filter],self.FilterMaxSeeing[filter])
                    if self.log and self.verbose > 1:
                        self.log.info(
                            'TOSS: propID=%d field=%d  filter=%s  WeakLensingProp: suggestObs(): bad seeing:%f'
                            % (self.propID, fieldID, filter,
                               filterSeeingList[filter]))
                    continue

            #-----------------------------------------------------------
            #           Ranking
            #-----------------------------------------------------------
            # Assign the priority to the fields. The priority/rank is
            # reflecting the fact that we want to end up observing in
            # each filter with a given frequency (see above).

            # The partial rank for this field/filter varies between
            # 0 and 1. It is 0 if we already have self.filterVisits[filter]
            # visits. It is 1 if we have no visit...
                if GlobalNeedFactor > 0.0:
                    if FieldNeedFactor > 0.0:
                        if progress[filter] < 1.0:
                            FilterNeedFactor = 1.0 - progress[filter]
                            rank = self.scale * 0.5 * (
                                FieldNeedFactor +
                                FilterNeedFactor) / GlobalNeedFactor
                        else:
                            rank = 0.0
                    else:
                        FilterNeedFactor = (
                            self.maxNeedAfterOverflow /
                            (nVisits[filter] -
                             self.GoalVisitsFieldFilter[filter] +
                             1)) / self.GoalVisitsFieldFilter[filter]
                        rank = self.scale * FilterNeedFactor / GlobalNeedFactor
                else:
                    rank = 0.0

                if (rank > 0.0):
                    ffilter_proposed += 1
                    #print "WeakLensing", intargetProfiles[i]
                    # Edit corresponding Observation instance in self.obsPool
                    recordFieldFilter = self.obsPool[fieldID][filter]
                    #recordFieldFilter.sessionID = sessionID
                    #recordFieldFilter.propID = propID
                    #recordFieldFilter.fieldID = fieldID
                    #recordFieldFilter.filter = filter
                    #recordFieldFilter.seqn = seqn
                    recordFieldFilter.date = date
                    recordFieldFilter.mjd = mjd
                    #recordFieldFilter.exposureTime = exposureTime
                    #recordFieldFilter.slewTime = slewTime
                    #recordFieldFilter.rotatorSkyPos = 0.0
                    #recordFieldFilter.rotatorTelPos = 0.0
                    recordFieldFilter.propRank = rank
                    #recordFieldFilter.finRank = finRank
                    recordFieldFilter.maxSeeing = self.FilterMaxSeeing[filter]
                    recordFieldFilter.rawSeeing = intargetProfiles[i][7]
                    recordFieldFilter.seeing = filterSeeingList[filter]
                    recordFieldFilter.transparency = intargetProfiles[i][3]
                    recordFieldFilter.cloudSeeing = intargetProfiles[i][4]
                    recordFieldFilter.airmass = airmass
                    recordFieldFilter.skyBrightness = skyBrightness
                    #recordFieldFilter.ra = ra
                    #recordFieldFilter.dec = dec
                    recordFieldFilter.lst = lst_RAD
                    recordFieldFilter.altitude = intargetProfiles[i][6]
                    recordFieldFilter.azimuth = intargetProfiles[i][9]
                    recordFieldFilter.distance2moon = intargetProfiles[i][5]
                    recordFieldFilter.moonRA = moonRA_RAD
                    recordFieldFilter.moonDec = moonDec_RAD
                    recordFieldFilter.moonAlt = intargetProfiles[i][8]
                    recordFieldFilter.moonPhase = moonPhase_PERCENT

                    self.addToSuggestList(recordFieldFilter, inproximity[i])
            #print "WLRANK: f:%d am:%f see:%f brt:%f phs:%f dt:%d flt:%s rnk:%f" % (fieldID, airmass,filterSeeingList[filter],intargetProfiles[i][1],moonPhase, date, filter, rank)

        if self.log and self.verbose > 0:
            self.log.info(
                '%sProp: suggestObs() propID=%d : Fields received=%i invisible=%i moon=%i Field-Filters allowed=%i badseeing=%i proposed=%i'
                % (self.propFullName, self.propID, fields_received,
                   fields_invisible, fields_moon, ffilter_allowed,
                   ffilter_badseeing, ffilter_proposed))

        # Chose the n highest ranking observations
#        self.reuseRanking = self.reuseRankingCount
        return self.getSuggestList(numberOfObsToPropose)

    def closeObservation(self, observation, obsHistID, twilightProfile):

        if (self.log and self.verbose > 1):
            self.log.info('WeakLensingProp: closeObservation() propID=%d' %
                          (self.propID))

        obs = super(WeakLensingProp,
                    self).closeObservation(observation, obsHistID,
                                           twilightProfile)

        if obs != None:
            try:
                self.visits[obs.filter][obs.fieldID] += 1
            except:
                self.visits[obs.filter][obs.fieldID] = 1
            self.VisitsTonight += 1

            progress = self.visits[obs.filter][
                obs.fieldID] / self.GoalVisitsFieldFilter[obs.filter]

            if (self.log and self.verbose > 0):
                t_secs = obs.date % 60
                t_mins = (obs.date % 3600) / 60
                t_hour = (obs.date % 86400) / 3600
                t_days = (obs.date) / 86400

                self.log.info(
                    'WeakLensingProp: closeObservation() propID=%d field=%d filter=%s propRank=%.4f finRank=%.4f t=%dd%02dh%02dm%02ds progress=%d%%'
                    % (self.propID, obs.fieldID, obs.filter, obs.propRank,
                       obs.finRank, t_days, t_hour, t_mins, t_secs,
                       int(100 * progress)))

            #print ('%i %i' % (self.visits[obs.filter][obs.fieldID], self.VisitsTonight))
        return obs

    def updateTargetList(self, dateProfile, obsProfile, sky, fov):
        """
        Update the list of potentially visible fields given a LST and
        a latitude. The range in coordinates of the selected fields is
        RA:  [LST-60; LST+60] (degrees)
        Dec: [lat-60; lat+60] (degrees)

        This version uses Sun.py and computes RA limits at the
        nautical twilight.

        Input:
        dateProfile ....
        obsProfile  ....
        sky         AstronomicalSky instance
        fov         Field of view of the telescope

        Return
        fields      A dictionary of the form {fieldID: (ra, dec)}
        """
        ## Benchmark memory use - start
        #m0 = memory()
        #r0 = resident()
        #s0 = stacksize()
        ##self.log.info("WL: updateTargetList entry: mem: %d resMem: %d stack: %d" % (m0, r0, s0))

        if (self.log):
            self.log.info('Proposal:updateTargetList propID=%d' %
                          (self.propID))
        dbFov = 'fieldFov'
        dbRA = 'fieldRA'
        dbDec = 'fieldDec'
        #dbL = 'fieldGL'
        #dbB = 'fieldGB'
        dbID = 'fieldID'

        (date, mjd, lst_RAD) = dateProfile
        (lon_RAD, lat_RAD, elev_M, epoch_MJD, d1, d2, d3) = obsProfile

        # MJD -> calendar date
        (yy, mm, dd) = mjd2gre(mjd)[:3]

        # determine twilight times based on user param: TwilightBoundary
        s = Sun.Sun()
        (sunRise, sunSet) = s.__sunriset__(yy, mm, dd, lon_RAD * RAD2DEG,
                                           lat_RAD * RAD2DEG,
                                           self.twilightBoundary, 0)
        #(sunRise, sunSet) = s.nauticalTwilight (yy, mm, dd, lon_RAD*RAD2DEG, lat_RAD*RAD2DEG)

        # RAA following overkill for simulation
        #if (date >= sunSet):            # Beginning of the night
        #    (sunRise, dummy) = s.nauticalTwilight (yy, mm, dd+1, lon_DEG, lat_DEG)
        #else:                           # End of the night
        #    (dummy, sunSet) = s.nauticalTwilight (yy, mm, dd-1, lon_DEG, lat_DEG)

        # Compute RA min (at twilight)
        date_MJD = int(mjd) + (sunSet / 24.)
        raMin = (
            (slalib.sla_gmst(date_MJD) + lon_RAD) * RAD2DEG) - self.deltaLST

        # Compute RA max (at twilight)
        date_MJD = int(mjd) + (sunRise / 24.)
        raMax = (
            (slalib.sla_gmst(date_MJD) + lon_RAD) * RAD2DEG) + self.deltaLST

        # Make sure that both raMin and raMax are in the [0; 360] range
        raMin = normalize(angle=raMin, min=0., max=360, degrees=True)
        raMax = normalize(angle=raMax, min=0., max=360, degrees=True)

        # self.targets is a convenience dictionary. Its keys are
        # fieldIDs, its values are the corresponding RA and Dec.
        fields = {}
        fovEpsilon1 = fov - .01
        fovEpsilon2 = fov + .01

        sql = 'SELECT %s, %s, %s from %s ' % (dbRA, dbDec, dbID, self.dbField)

        sql += 'WHERE %s BETWEEN %f AND %f AND ' % (dbFov, fovEpsilon1,
                                                    fovEpsilon2)

        # subtract galactic exclusion zone
        taperB = self.taperB
        taperL = self.taperL
        peakL = self.peakL
        band = peakL - taperL
        if ((taperB != 0.) & (taperL != 0.)):
            sql += '( (fieldGL < 180. AND abs(fieldGB) > (%f - (%f * abs(fieldGL)) / %f) ) OR ' % (
                peakL, band, taperB)
            sql += '(fieldGL > 180. AND abs(fieldGB) > (%f - (%f * abs(fieldGL-360.)) / %f))) AND ' % (
                peakL, band, taperB)
        # subtract un-viewable sky
        if (raMax > raMin):
            sql += '%s BETWEEN %f AND %f AND ' % (dbRA, raMin, raMax)
        elif (raMax < raMin):
            sql += '(%s BETWEEN %f AND 360.0 OR ' % (dbRA, raMin)
            sql += '%s BETWEEN 0.0 AND %f) AND ' % (dbRA, raMax)
        else:
            sql += '%s BETWEEN 0.0 AND 360.0 AND ' % (dbRA)

        DecLimit = math.acos(1. / float(self.maxAirmass)) * RAD2DEG
        sql += '%s BETWEEN %f AND %f  and %f < %s < %f order by FieldRA,FieldDec' % (
            dbDec, (lat_RAD * RAD2DEG) - DecLimit,
            (lat_RAD * RAD2DEG) + DecLimit, -abs(self.maxReach), dbDec,
            abs(self.maxReach))

        # Send the query to the DB
        (n, res) = self.lsstDB.executeSQL(sql)

        # Build the output dictionary
        for (ra, dec, fieldID) in res:
            fields[fieldID] = (ra, dec)

        self.targets = fields

        self.computeTargetsHAatTwilight(lst_RAD)

        self.NumberOfFieldsTonight = len(self.targets)
        #print('NumberOfFieldsTonight = %i' % (self.NumberOfFieldsTonight))
        self.GoalVisitsTonight = self.GoalVisitsField * self.NumberOfFieldsTonight
        #print('Goal Visits with Tonight targets = %i' % (self.GoalVisitsTonight))
        self.VisitsTonight = 0
        for filter in self.visits.keys():
            #print('visits in %s = %i' % (filter, len(self.visits[filter].keys())))
            for field in self.visits[filter].keys():
                if self.targets.has_key(field):
                    self.VisitsTonight += self.visits[filter][field]
                    #print field
        #print('Visits up to Tonight for propID=%d for current targets = %i' % (self.propID,self.VisitsTonight))
        print('*** Found %d WL fields for propID=%d ***' %
              (len(res), self.propID))

        ## Benchmark memory use - exit
        #m1 = memory()
        #r1 = resident()
        #s1 = stacksize()
        ##self.log.info("WL: updateTargetList:(entry:exit) mem: %d:%d resMem: %d:%d stack: %d:%d" % (m0,m1, r0,r1, s0,s1))
        #print("WL: updateTargetList:(entry:exit) mem: %d:%d resMem: %d:%d stack: %d:%d" % (m0,m1, r0,r1, s0,s1))

        self.schedulingData.updateTargets(fields, self.propID, dateProfile)

        return (fields)

    def closeProposal(self, time):

        # delete OlapField user-defined region table
        if not (self.userRegion[0] == None):
            overlappingField = "OlapField_%d_%d" % (self.sessionID,
                                                    self.propID)
            result = self.lsstDB.dropTable(overlappingField)

        return
class WeakLensingProp (Proposal):
    """
    This class is here to describe a simple case scenario: a Proposal 
    type of object that only cares about seeing values and slew time.
    It does not care about particular fields, filters or anythin else,
    just seeing and slew time.
    """
    def __init__ (self, 
                  lsstDB,
		  schedulingData,
		  sky, 
                  weather,
                  sessionID,
                  filters,
                  fov,
                  weakLensConf=DefaultWLConfigFile,
                  targetList=None, 
                  dbTableDict=None,
                  log=True,
                  logfile='./Proposal.log',
                  verbose=0):
        """
        Standard initializer.
        
	lsstDB:     LSST DB Access object        
	sky:        an AsronomycalSky instance.
        weather:    a Weather instance.
        sessionID:  An integer identifying this particular run.
        filters:    ...
        fov:        FoV of the instrument.
        weakLensConf: Weak Lensing Proposal Configuration file
        exposureTime:    Exposure time in seconds.
        maxSeeing:  Maximum acceptable seeing.
        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         ...
        logfile     ...
        verbose:    integer specifying the verbosity level (defaults 
                    to 0 meaning quite).
        """

        super (WeakLensingProp, self).__init__ (lsstDB=lsstDB,
						propConf=weakLensConf,
                                                propName='WL',
						propFullName='WeakLensing',
                                                sky=sky, 
                                                weather=weather, 
                                                sessionID=sessionID,
                                                filters=filters,
                                                targetList=targetList,
                                                dbTableDict=dbTableDict,
                                                log=log,
                                                logfile=logfile,
                                                verbose=verbose)
        
	self.lsstDB = lsstDB        
	if (self.log and self.verbose > 1):
           self.log.info('WeakLensingProp: init() propID=%d' %(self.propID))
        
	self.schedulingData = schedulingData
        self.weakLensConf = weakLensConf
        self.GoalVisitsFieldFilter = {}
        self.sky = sky

        config, pairs = readConfFile (weakLensConf)
        
        self.nextNight = 0
        try:
            self.maxAirmass = config['MaxAirmass']
        except:
            pass

        try:
            self.exposureTime = config['ExposureTime']
        except:
            pass

        try:
            self.goalNVisits = config['NVisits']
        except:
            self.goalNVisits = 30.
        try:
            filterVisits = config["Filter_Visits"]
        except:
            filterVisits = []
            for filter in self.FilterNames:
                filterVisits.append(self.goalNVisits)
        # if singleton, make into a list of one
        if not isinstance (filterVisits, list):
            filterVisits = [filterVisits]

        for ix in range(len(self.FilterNames)):
            self.GoalVisitsFieldFilter[self.FilterNames[ix]] = filterVisits[ix]
        print 'GoalVisitsFieldFilter for propID=%d %s = %s' % (self.propID, self.propFullName, str(self.GoalVisitsFieldFilter))
        
        try:
            self.maxNeedAfterOverflow = config['MaxNeedAfterOverflow']
        except:
            self.maxNeedAfterOverflow = 0.5

        try:
            self.ProgressToStartBoost = config['ProgressToStartBoost']
        except:
            self.ProgressToStartBoost = 1.0
                                                                                                           
        try:
            self.MaxBoostToComplete   = config['MaxBoostToComplete']
        except:
            self.MaxBoostToComplete   = 0.0
                                                                                                           
        try:
            self.scale = config['RankScale']
        except:
            self.scale = 0.1

        try:
            self.taperB = config['taperB']
        except:
            self.taperB = 180.
        try:
            self.taperL = config['taperL']
        except:
            self.taperL = 5.
        try:
            self.peakL = config['peakL']
        except:
            self.peakL = 25.
        try:
            self.deltaLST = config['deltaLST']
        except:
            self.deltaLST = 60.
        try:
            self.maxReach = config['maxReach']
        except:
            self.maxReach = 80.
        try:
            self.minTransparency = config['minTransparency']
        except:
            self.minTransparency = 9.

        if ( config.has_key ('userRegion')) :
            self.userRegion =  config["userRegion"]
        else :
            self.userRegion =  None
            
        if (not isinstance(self.userRegion,list)):
            # turn it into a list with one entry
            save = self.userRegion
            self.userRegion = []
            self.userRegion.append(save)

        try:
            self.maxProximityBonus = config['MaxProximityBonus']
        except:
            self.maxProximityBonus = 1.0

        # store config to DB
        for line in pairs:
            storeParam (self.lsstDB, self.sessionID, self.propID, 'weakLensing',
                        line['index'], line['key'], line['val'])

        self.dbField = dbTableDict['field']

        if (self.goalNVisits <= 0):
            self.goalNVisits = 1.

        # Setup FieldFilter visit history for later ObsHistory DB  ingest
        self.fieldVisits = {}
        self.lastFieldVisit = {}
        self.lastFieldFilterVisit = {}
        self.lastTarget = (0.0,0.0)

        
        # Setup the relative importance of each filter. For 
        # convenience we use a dictionary of the form {filter: N}
        # where N is the desired fraction of the total number of 
        # observations for that particular filter. If all the filters
        # were equally desirable, then N would be always equal to 
        # 1. / total_number_of_filters.
        n = 0.

        # filterNames now is assigned in the parent class using the
        # filters' configuration file.
        # If this proposal wants to observe in a subset of this filter
        # list, this subset should be specified in the proposal's
        # configuration file.

        self.visits={}
        self.GoalVisitsField=0

        for filter in (self.FilterNames):
	    if filter in self.GoalVisitsFieldFilter.keys():
                self.visits[filter]={}
                self.GoalVisitsField += self.GoalVisitsFieldFilter[filter]
        print('GoalVisitsField = %i' % (self.GoalVisitsField))

        # DataBase specifics
        self.dbTableDict = dbTableDict

        self.dbField = self.dbTableDict['field']
        # If user-defined regions have been defined, build new FieldDB
        if not (self.userRegion[0] == None) :
            self.dbField = self.buildUserRegionDB(self.userRegion,self.dbField)

        print "WeakLensingProp:init: dbField: %s" % (self.dbField)
        
        self.winners = []
                
        self.obsHistory = None
        self.sessionID = sessionID
        
        # self.targets is a convenience dictionary. Its keys are 
        # fieldIDs, its values are the corresponding RA and Dec.
        self.targets = {}
        
        # Create the ObsHistory instance and cleanup any leftover
        self.obsHistory = ObsHistory (lsstDB=self.lsstDB,
				      dbTableDict=self.dbTableDict,
                                      log=self.log,
                                      logfile=self.logfile,
                                      verbose=self.verbose)

        self.obsHistory.cleanupProposal (self.propID, self.sessionID)

        self.ha_maxairmass = sky.getHAforAirmass(self.maxAirmass)
                
        return
    
    def start (self):
        """
        Activate the WeakLensingProp instance.
        """
        if (self.log and self.verbose > 1):
           self.log.info('WeakLensingProp: start() propID=%d' %(self.propID))

        
        # PAUSE
        yield hold, self
        return
    
    def startNight(self,dateProfile,moonProfile,startNewLunation,randomizeSequencesSelection, nRun, mountedFiltersList):

        super (WeakLensingProp, self).startNight (dateProfile,moonProfile,startNewLunation, mountedFiltersList)

    def GetProgressPerFilter(self):

        coaddedFilterProgress = {}
	allfields = []
	filters = self.visits.keys()
        for filter in filters:
            coaddedFilterProgress[filter] = 0
	    fields = self.visits[filter].keys()
	    allfields += fields
	    for fieldID in fields:
		coaddedFilterProgress[filter] += self.visits[filter][fieldID]

        progressFilter = {}
	for filter in filters:
	    if self.GoalVisitsFieldFilter[filter] > 0:
		if len(allfields) > 0:
		    progressFilter[filter] = float(coaddedFilterProgress[filter])/len(allfields)/self.GoalVisitsFieldFilter[filter]
		else:
		    # no fileds observed for this filter
		    progressFilter[filter] = 0.0
	    else:
		# no observations required for this filter
		progress[filter] = 1.0

        if ( self.log ):
            for filter in progressFilter.keys():
                self.log.info('%sProp: GetProgressPerFilter() propID=%d Filter progress: %10s = %.3f%%' % (self.propFullName, self.propID, filter, 100*progressFilter[filter]))

	return progressFilter

    def suggestObs (self, 
                    dateProfile, 
                    moonProfile,
                    n=1,
                    skyfields=None, 
                    proximity=None,
                    targetProfiles=None,
		    exclusiveObservation=None,
		    minDistance2Moon=0.0):
        """
        Return the list of (at most) the n (currently) higher ranking 
        observations.
        
        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)
                            moonPhase       current moon phase in range [0-100]
        n               number of observations to return.
        skyfields       array of fieldIDs  (with index-synced to proximity)
        proximity       array of proximity distance between current 
                        telescope position and each entry in skyfields (with
                        index-synced to skyfields)
        targetProfiles: array of [AirMass, 
                                Sky brightness,
                                Filterlist,
                                transparency,
                                cloudSeeing,
                                distance2moon,
                                altitude,
                                rawSeeing,
                                moonAltitude]
                        values (dictionary-keyed to fieldID)
        
        Return
        An array of the (at most) n highest ranking observations, 
        ordered from the highest ranking obs to the lowest.
        """
        if (self.log and self.verbose > 1):
           self.log.info('WeakLensingProp: suggestObs() propID=%d' %(self.propID))

        # If in an exclusive block, no new observation candidates. Return null.
#        if (exclusiveObservation != None):
            # adjust counter for one obs
#            self.reuseRanking -= 1
#            return []
#        else:
            # If not time to rerank fields, return no suggestions.
#            if self.reuseRanking > 1:
#                self.reuseRanking -= 1
#                return []

        if (exclusiveObservation != None):
            if exclusiveObservation.fieldID in self.targets.keys():
                listOfFieldsToEvaluate = [exclusiveObservation.fieldID]
            else:
                listOfFieldsToEvaluate = []
            numberOfObsToPropose = 0
        else:
            listOfFieldsToEvaluate = sorted(self.targets.iterkeys())
            numberOfObsToPropose = n

#        listOfFieldsToEvaluate = self.targets.keys()
#	listOfFieldsToEvaluate = sorted(self.targets.iterkeys())
#        numberOfObsToPropose = n

        # Create a priority queue to choose the best n obs
        self.clearSuggestList()
        
        # Copy the input vars
        inFieldID = skyfields
        inproximity = proximity
        intargetProfiles = targetProfiles
        (date,mjd,lst_RAD) = dateProfile
        (moonRA_RAD,moonDec_RAD,moonPhase_PERCENT) = moonProfile

        
        # Get the number of times we observed in each field
        # The idea here is to make sure that, over one year, we end up
        # with a balanced set of observations. For the Weak Lensing
        # proposal, we want to observe each field-filter combo approx.
        # GOAL times/year. This means that we do not want to observe 
        # each field-filter more than 6 times per lunation or once per
        # night.
        # numVisits is a dict of the form {filter: {fieldID: n}}
#        visits = self.obsHistory.getNVisitsPerFilterField (self.propID,
#                                    self.sessionID, fieldID=None)
        

        #..................   T o   D o  .................................
        # Compute the normalization constant for SignalToNoise (SNR) ranking
        #..................   T o   D o  .................................

	fields_received   = len(listOfFieldsToEvaluate)
	fields_invisible  = 0
	fields_moon       = 0
	ffilter_allowed   = 0
	ffilter_badseeing = 0
	ffilter_proposed  = 0

        # Adjust the partial rank so that we end with 
        # self.sumFieldFilterGoal for all field/filter combos
        # Adjust the scaling
	needTonight = self.GoalVisitsTonight - self.VisitsTonight
	if needTonight > 0:
	    GlobalNeedFactor = float(needTonight) / self.GoalVisitsTonight
	else:
	    GlobalNeedFactor = (self.maxNeedAfterOverflow/(self.VisitsTonight-self.GoalVisitsTonight+1)) / self.GoalVisitsTonight

        for fieldID in listOfFieldsToEvaluate:
            i = inFieldID.index(fieldID)
            ra = self.targets[fieldID][0]
            dec = self.targets[fieldID][1]

            if (fieldID==self.last_observed_fieldID) and (self.last_observed_wasForThisProposal) and (not self.AcceptConsecutiveObs):
                continue
                
            #-----------------------------------------------------------
            #       Field cuts
            #-----------------------------------------------------------
            # First, discard all the targets which are not visible right now.
            airmass = intargetProfiles[i][0]
            if airmass > self.maxAirmass :
		fields_invisible += 1
                if self.log and self.verbose>1 :
                    self.log.info('TOSS: propID=%d field=%d  WeakLensingProp: suggestObs(): too low:%f' % (self.propID,fieldID,airmass))
                #DBGprint "Toss: Field: %d ra:%f dec:%f am:%f > 5 " % (fieldID,ra,dec,airmass) 
                continue

	    distance2moon = intargetProfiles[i][5]
            if distance2moon < minDistance2Moon:
                fields_moon += 1
		# remove the target for the rest of the night if it is too close to the moon
		del self.targets[fieldID]
                continue
	
	    nVisits  = {}
	    progress = {}
	    progress_avg = 0.0
	    for filter in self.FilterNames:
                try:
                    nVisits[filter] = self.visits[filter][fieldID]
                except:
                    nVisits[filter] = 0.
		progress[filter] = nVisits[filter] / self.GoalVisitsFieldFilter[filter]
		progress_avg += min(progress[filter],1.0)/len(self.FilterNames)

	    FieldNeedFactor = 1.0 - progress_avg
	    if self.ProgressToStartBoost < progress_avg < 1.0:
		FieldNeedFactor += self.MaxBoostToComplete*(progress_avg-self.ProgressToStartBoost)/(1.0-self.ProgressToStartBoost)

            skyBrightness = intargetProfiles[i][1]
            allowedFilterList = self.allowedFiltersForBrightness(skyBrightness)
            filterSeeingList = intargetProfiles[i][2]
            #print ".....field:%d ra:%f dec:%f am:%f phs:%f brite:%f" % (fieldID, ra,dec, airmass, moonPhase, intargetProfiles[i][1]) , filterSeeingList
            
            for filter in allowedFilterList:
		ffilter_allowed += 1
                #-----------------------------------------------------------
                #       Field/filter cuts
                #-----------------------------------------------------------
                if filterSeeingList[filter]  > self.FilterMaxSeeing[filter] :
		    ffilter_badseeing += 1
                    #DBGprint "TOSS: fld:%d fltr:%s ra:%f dec%f airms:%f phs:%f  airAdjSee:%f > maxSee:%f" % (fieldID,filter,ra,dec,airmass,moonPhase,filterSeeingList[filter],self.FilterMaxSeeing[filter])
                    if self.log and self.verbose>1 :
                        self.log.info('TOSS: propID=%d field=%d  filter=%s  WeakLensingProp: suggestObs(): bad seeing:%f' % (self.propID,fieldID,filter,filterSeeingList[filter]))
                    continue

                #-----------------------------------------------------------
                #           Ranking
                #-----------------------------------------------------------
                # Assign the priority to the fields. The priority/rank is
                # reflecting the fact that we want to end up observing in
                # each filter with a given frequency (see above).
                    
                # The partial rank for this field/filter varies between 
                # 0 and 1. It is 0 if we already have self.filterVisits[filter] 
                # visits. It is 1 if we have no visit...
		if GlobalNeedFactor > 0.0:
		    if FieldNeedFactor > 0.0:
			if progress[filter] < 1.0:
			    FilterNeedFactor = 1.0 - progress[filter]
			    rank = self.scale * 0.5*(FieldNeedFactor + FilterNeedFactor) / GlobalNeedFactor
			else:
			    rank = 0.0
		    else:
			FilterNeedFactor = (self.maxNeedAfterOverflow/(nVisits[filter]-self.GoalVisitsFieldFilter[filter]+1)) / self.GoalVisitsFieldFilter[filter]
		        rank = self.scale * FilterNeedFactor / GlobalNeedFactor
		else:
		    rank = 0.0

 
                if (rank >0.0):
		    ffilter_proposed += 1
                    #print "WeakLensing", intargetProfiles[i]
                    # Edit corresponding Observation instance in self.obsPool
                    recordFieldFilter = self.obsPool[fieldID][filter]
                    #recordFieldFilter.sessionID = sessionID
                    #recordFieldFilter.propID = propID
                    #recordFieldFilter.fieldID = fieldID
                    #recordFieldFilter.filter = filter
                    #recordFieldFilter.seqn = seqn
                    recordFieldFilter.date = date
                    recordFieldFilter.mjd = mjd
                    #recordFieldFilter.exposureTime = exposureTime
                    #recordFieldFilter.slewTime = slewTime
                    #recordFieldFilter.rotatorSkyPos = 0.0
                    #recordFieldFilter.rotatorTelPos = 0.0
                    recordFieldFilter.propRank = rank
                    #recordFieldFilter.finRank = finRank
                    recordFieldFilter.maxSeeing = self.FilterMaxSeeing[filter]
                    recordFieldFilter.rawSeeing = intargetProfiles[i][7]
                    recordFieldFilter.seeing = filterSeeingList[filter]
                    recordFieldFilter.transparency = intargetProfiles[i][3]
                    recordFieldFilter.cloudSeeing = intargetProfiles[i][4]
                    recordFieldFilter.airmass = airmass
                    recordFieldFilter.skyBrightness = skyBrightness
                    #recordFieldFilter.ra = ra
                    #recordFieldFilter.dec = dec
                    recordFieldFilter.lst = lst_RAD
                    recordFieldFilter.altitude = intargetProfiles[i][6]
                    recordFieldFilter.azimuth  = intargetProfiles[i][9]
                    recordFieldFilter.distance2moon = intargetProfiles[i][5]
                    recordFieldFilter.moonRA = moonRA_RAD
                    recordFieldFilter.moonDec = moonDec_RAD
                    recordFieldFilter.moonAlt = intargetProfiles[i][8]
                    recordFieldFilter.moonPhase = moonPhase_PERCENT

                    self.addToSuggestList(recordFieldFilter, inproximity[i])
                #print "WLRANK: f:%d am:%f see:%f brt:%f phs:%f dt:%d flt:%s rnk:%f" % (fieldID, airmass,filterSeeingList[filter],intargetProfiles[i][1],moonPhase, date, filter, rank)

        if self.log and self.verbose>0:
	    self.log.info('%sProp: suggestObs() propID=%d : Fields received=%i invisible=%i moon=%i Field-Filters allowed=%i badseeing=%i proposed=%i' % (self.propFullName, self.propID, fields_received, fields_invisible, fields_moon, ffilter_allowed, ffilter_badseeing, ffilter_proposed))

        # Chose the n highest ranking observations
#        self.reuseRanking = self.reuseRankingCount
        return self.getSuggestList(numberOfObsToPropose)
    
    
    def closeObservation (self, observation, obsHistID, twilightProfile):

        if (self.log and self.verbose > 1):
           self.log.info('WeakLensingProp: closeObservation() propID=%d' %(self.propID))

        obs = super (WeakLensingProp, self).closeObservation(observation, obsHistID, twilightProfile)

        if obs != None:
            try:
                self.visits[obs.filter][obs.fieldID] += 1
            except:
                self.visits[obs.filter][obs.fieldID]  = 1
            self.VisitsTonight += 1
 
	    progress = self.visits[obs.filter][obs.fieldID]/self.GoalVisitsFieldFilter[obs.filter]

            if (self.log and self.verbose>0):
                t_secs = obs.date%60
                t_mins = (obs.date%3600)/60
                t_hour = (obs.date%86400)/3600
                t_days = (obs.date)/86400

                self.log.info('WeakLensingProp: closeObservation() propID=%d field=%d filter=%s propRank=%.4f finRank=%.4f t=%dd%02dh%02dm%02ds progress=%d%%' % (self.propID, obs.fieldID, obs.filter, obs.propRank, obs.finRank, t_days, t_hour, t_mins, t_secs, int(100*progress)))

            #print ('%i %i' % (self.visits[obs.filter][obs.fieldID], self.VisitsTonight))
        return obs
        

    def updateTargetList (self, dateProfile, obsProfile, sky, fov ):
        """
        Update the list of potentially visible fields given a LST and
        a latitude. The range in coordinates of the selected fields is
        RA:  [LST-60; LST+60] (degrees)
        Dec: [lat-60; lat+60] (degrees)

        This version uses Sun.py and computes RA limits at the
        nautical twilight.

        Input:
        dateProfile ....
        obsProfile  ....
        sky         AstronomicalSky instance
        fov         Field of view of the telescope

        Return
        fields      A dictionary of the form {fieldID: (ra, dec)}
        """
        ## Benchmark memory use - start
        #m0 = memory()
        #r0 = resident()
        #s0 = stacksize()
        ##self.log.info("WL: updateTargetList entry: mem: %d resMem: %d stack: %d" % (m0, r0, s0))

        if ( self.log):
            self.log.info ('Proposal:updateTargetList propID=%d' %(self.propID))
        dbFov = 'fieldFov'
        dbRA = 'fieldRA'
        dbDec = 'fieldDec'
        #dbL = 'fieldGL'
        #dbB = 'fieldGB'
        dbID = 'fieldID'
         
        (date,mjd,lst_RAD) = dateProfile
        (lon_RAD,lat_RAD,elev_M,epoch_MJD,d1,d2,d3) = obsProfile

        # MJD -> calendar date
        (yy, mm, dd) = mjd2gre (mjd)[:3]

        # determine twilight times based on user param: TwilightBoundary
        s = Sun.Sun ()
        (sunRise, sunSet) = s.__sunriset__ (yy, mm, dd, lon_RAD*RAD2DEG, lat_RAD*RAD2DEG,self.twilightBoundary,0)
        #(sunRise, sunSet) = s.nauticalTwilight (yy, mm, dd, lon_RAD*RAD2DEG, lat_RAD*RAD2DEG)

        # RAA following overkill for simulation
        #if (date >= sunSet):            # Beginning of the night
        #    (sunRise, dummy) = s.nauticalTwilight (yy, mm, dd+1, lon_DEG, lat_DEG)
        #else:                           # End of the night
        #    (dummy, sunSet) = s.nauticalTwilight (yy, mm, dd-1, lon_DEG, lat_DEG)

        # Compute RA min (at twilight)
        date_MJD = int (mjd) + (sunSet / 24.)
        raMin = ((slalib.sla_gmst(date_MJD) + lon_RAD) * RAD2DEG) - self.deltaLST
                                                                                
        # Compute RA max (at twilight)
        date_MJD = int (mjd) + (sunRise / 24.)
        raMax = ((slalib.sla_gmst(date_MJD) + lon_RAD) * RAD2DEG) + self.deltaLST

        # Make sure that both raMin and raMax are in the [0; 360] range
        raMin = normalize (angle=raMin, min=0., max=360, degrees=True)
        raMax = normalize (angle=raMax, min=0., max=360, degrees=True)

        # self.targets is a convenience dictionary. Its keys are
        # fieldIDs, its values are the corresponding RA and Dec.
        fields = {}
        fovEpsilon1 = fov - .01
        fovEpsilon2 = fov + .01

        sql = 'SELECT %s, %s, %s from %s ' % (dbRA,
                                              dbDec,
                                              dbID,
                                              self.dbField)

        sql += 'WHERE %s BETWEEN %f AND %f AND ' % (dbFov,
                                                    fovEpsilon1,
                                                    fovEpsilon2)

        # subtract galactic exclusion zone
        taperB = self.taperB
        taperL = self.taperL
        peakL = self.peakL
        band = peakL - taperL
        if ( (taperB != 0.) & (taperL != 0.) ) :
           sql += '( (fieldGL < 180. AND abs(fieldGB) > (%f - (%f * abs(fieldGL)) / %f) ) OR ' % (peakL,
                    band,
                    taperB)
           sql += '(fieldGL > 180. AND abs(fieldGB) > (%f - (%f * abs(fieldGL-360.)) / %f))) AND ' % (peakL,
                    band,
                    taperB)
        # subtract un-viewable sky
        if (raMax > raMin):
            sql += '%s BETWEEN %f AND %f AND ' % (dbRA,
                                                  raMin,
                                                  raMax)
        elif (raMax < raMin):
            sql += '(%s BETWEEN %f AND 360.0 OR ' % (dbRA,
                                                     raMin)
            sql += '%s BETWEEN 0.0 AND %f) AND ' % (dbRA,
                                                    raMax)
        else:
            sql += '%s BETWEEN 0.0 AND 360.0 AND ' % (dbRA)

        DecLimit = math.acos(1./float(self.maxAirmass)) * RAD2DEG
        sql += '%s BETWEEN %f AND %f  and %f < %s < %f order by FieldRA,FieldDec' % (dbDec,
                                         (lat_RAD*RAD2DEG)-DecLimit,
                                         (lat_RAD*RAD2DEG)+DecLimit,
                                         -abs(self.maxReach),dbDec,abs(self.maxReach))

        # Send the query to the DB
        (n, res) = self.lsstDB.executeSQL (sql)

        # Build the output dictionary
        for (ra, dec, fieldID) in res:
            fields[fieldID] = (ra, dec)

        self.targets = fields

        self.computeTargetsHAatTwilight(lst_RAD)

        self.NumberOfFieldsTonight = len(self.targets)
        #print('NumberOfFieldsTonight = %i' % (self.NumberOfFieldsTonight))
        self.GoalVisitsTonight = self.GoalVisitsField * self.NumberOfFieldsTonight
        #print('Goal Visits with Tonight targets = %i' % (self.GoalVisitsTonight))
        self.VisitsTonight = 0
        for filter in self.visits.keys():
            #print('visits in %s = %i' % (filter, len(self.visits[filter].keys())))
            for field in self.visits[filter].keys():
                if self.targets.has_key(field):
                    self.VisitsTonight += self.visits[filter][field]
                    #print field
        #print('Visits up to Tonight for propID=%d for current targets = %i' % (self.propID,self.VisitsTonight))
        print ('*** Found %d WL fields for propID=%d ***' % (len (res),self.propID))

        ## Benchmark memory use - exit
        #m1 = memory()
        #r1 = resident()
        #s1 = stacksize()
        ##self.log.info("WL: updateTargetList:(entry:exit) mem: %d:%d resMem: %d:%d stack: %d:%d" % (m0,m1, r0,r1, s0,s1))
        #print("WL: updateTargetList:(entry:exit) mem: %d:%d resMem: %d:%d stack: %d:%d" % (m0,m1, r0,r1, s0,s1))

	self.schedulingData.updateTargets(fields, self.propID, dateProfile)

        return (fields)

    def closeProposal(self, time):

        # delete OlapField user-defined region table
        if not (self.userRegion[0] == None):
            overlappingField = "OlapField_%d_%d" %(self.sessionID,self.propID)
            result = self.lsstDB.dropTable(overlappingField)

	return
class TransientProp (Proposal):
    """
    This class is here to describe a Transient Objects case scenario. 
    """
    def __init__ (self,
		  lsstDB, 
                  propConf,
                  propName,
		  propFullName,
                  sky, 
                  weather,
                  sessionID,
                  filters,
                  targetList=None, 
                  dbTableDict=None,
                  log=False,
                  logfile='./TransientProp.log',
                  verbose=0,
                  transientConf=DefaultNEAConfigFile):
        """
        Standard initializer.
        
	lsstDB      LSST DB access object        
	propConf    file name containing the instance's configuration data
        propName    proposal 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 "./NearEarthProp.log".
        verbose:    Log verbosity: -1=none, 0=minimal, 1=wordy, >1=verbose
        transientConf: Near Earth Asteroid Configuration file

        """
        super (TransientProp, self).__init__ (lsstDB=lsstDB,
					      propConf=propConf,
                                              propName=propName,
					      propFullName=propFullName,
                                              sky=sky, 
                                              weather=weather, 
                                              sessionID=sessionID,
                                              filters=filters,
                                              targetList=targetList,
                                              dbTableDict=dbTableDict,
                                              log=log,
                                              logfile=logfile,
                                              verbose=verbose)
        
	self.lsstDB = lsstDB        
	self.verbose     = verbose
        self.dbTableDict = dbTableDict
        
        # DataBase specifics
        self.dbTable = self.dbTableDict['field']
        self.dbRA = 'fieldRA'
        self.dbDec = 'fieldDec'
        self.dbID = 'fieldID'
        
        self.winners = []
        self.loosers = []
        
        self.obsHistory = None
#        self.seqHistory = None
        self.sessionID = sessionID

        # self.targets is a convenience dictionary. Its keys are 
        # fieldIDs, its values are the corresponding RA and Dec.
        self.targets = {}
        self.tonightTargets = {}
        self.sequences= {}
	self.blockedTargets = []

        # Create the ObsHistory instance and cleanup any leftover
        self.obsHistory = ObsHistory (lsstDB=self.lsstDB,
				      dbTableDict=self.dbTableDict,
                                      log=self.log,
                                      logfile=self.logfile,
                                      verbose=self.verbose)
        
        self.obsHistory.cleanupProposal (self.propID, self.sessionID)
        
        # Create the SeqHistory instance and cleanup any leftover
#        self.seqHistory = SeqHistory (lsstDB=self.lsstDB,
#				      dbTableDict=self.dbTableDict,
#                                      log=self.log,
#                                      logfile=self.logfile,
#                                      verbose=self.verbose)
        
#        self.seqHistory.cleanupProposal (self.propID, self.sessionID)

	self.SeqCount = 0

        return

    
    def start (self):
        """
        Activate the TransientProp instance.

        """

        # PAUSE
#        yield hold, self
        return

    def startNight(self,dateProfile,moonProfile,startNewLunation,randomizeSequencesSelection,nRun, mountedFiltersList):

        super (TransientProp, self).startNight (dateProfile,moonProfile,startNewLunation, mountedFiltersList)

        self.tonightTargets = self.targets.copy()

        date = 0
        duration   =self.seqDuration
        distrib = IvezicDistribution(date,
                                     duration,
                                     intervals  =self.visitIntervals,
                                     repeats    =self.cycleRepeats,
                                     repeatdelay=self.cycleInterval )
        filters = self.seqFilters
	if not isinstance(filters, list):
	    filters = [filters]

        # deletes all sequences that did not start while the fields were
        # in the region for starting new sequences.
        notstarted = 0
        for fieldID in self.sequences.keys():
            if not fieldID in self.targetsNewSeq.keys():
                if self.sequences[fieldID].IsIdle():
                    notstarted+=1
                    del self.sequences[fieldID]

        # counts the active sequences in the target region
	currentActiveSequences = 0
	for fieldID in self.sequences.keys():
	    if fieldID in self.tonightTargets.keys():
		if (not self.sequences[fieldID].IsLost()) and (not self.sequences[fieldID].IsComplete()):
		    currentActiveSequences+=1

        # creates the sequence object for all the new fields in the restricted area
        newseq=0
	restartedlost=0
	restartedcomplete=0
	keptsequences=0
#        listOfNewFields=self.targetsNewSeq.keys()
	listOfNewFields=sorted(self.targetsNewSeq.iterkeys())
	while (len(listOfNewFields)>0 and currentActiveSequences<self.maxNumberActiveSequences):
		if randomizeSequencesSelection:
		    fieldID=random.choice(listOfNewFields)
		else:
		    fieldID=listOfNewFields[0]
		listOfNewFields.remove(fieldID)
                if not fieldID in self.sequences.keys():
		    self.SeqCount += 1
                    self.sequences[fieldID]=IvezicSequence(self.propID,
	    						   fieldID,
							   self.SeqCount,
						           filters,
						           distrib,
						           date,
						           duration,
						           self.maxMissedEvents)
                    newseq+=1
		    currentActiveSequences+=1
                elif self.sequences[fieldID].IsLost():
        	    if self.restartLostSequences:
			self.SeqCount += 1
		        self.sequences[fieldID].Restart(self.SeqCount)
		        restartedlost+=1
			currentActiveSequences+=1
		    else:
                        del self.tonightTargets[fieldID]

	        elif self.sequences[fieldID].IsComplete():
		    if self.restartCompleteSequences and not self.blockCompleteSeqConsecutiveLunations:
			self.SeqCount += 1
                        self.sequences[fieldID].Restart(self.SeqCount)
		        restartedcomplete+=1
			currentActiveSequences+=1
                    else:
                        del self.tonightTargets[fieldID]

	# From the broad target area, just keep the fields
	# associated to a target that is not lost and not complete
	for fieldID in self.tonightTargets.keys():
	    if fieldID in self.sequences.keys():
                if self.sequences[fieldID].IsLost() or self.sequences[fieldID].IsComplete():
		    del self.tonightTargets[fieldID]
		else:
                    keptsequences+=1
	    else:
		del self.tonightTargets[fieldID]

        if ( self.log ):
            self.log.info('%sProp: startNight() propID=%d added %i new sequences, deleted %i notstarted, restarted %i lost, restarted %i complete, kept %i' % (self.propFullName, self.propID,newseq, notstarted, restartedlost, restartedcomplete, keptsequences))

        return

    def GetProgressPerFilter(self):
                                                                                                                                                                                                                             
        coaddedNumber = 0
        coaddedProgress = 0
        for fieldID in self.sequences.keys():
            if not self.sequences[fieldID].IsLost():
                coaddedNumber += 1
                coaddedProgress += self.sequences[fieldID].GetProgress()
	if coaddedNumber > 0:
	    coaddedProgress /= coaddedNumber
        if ( self.log ):
            self.log.info('%sProp: GetProgressPerFilter() propID=%d Sequence progress: %.3f%%' % (self.propFullName, self.propID, 100*coaddedProgress))
                                                                                                                                                                                                                    
        progressFilter = {}
        for filter in self.seqFilters:
            progressFilter[filter]  = coaddedProgress
        if ( self.log ):
            for filter in progressFilter.keys():
                self.log.info('%sProp: GetProgressPerFilter() propID=%d Filter progress: %10s = %.3f%%' % (self.propFullName, self.propID, filter, 100*progressFilter[filter]))
                                                                                                                                                                                                                             
        return progressFilter

    def RankWindow(self, deltat, scale=300.0):
        """
        Evaluates the priority of an event according to the proximity to the
        event time.

        Input
        deltat: the proximity in seconds to the event (current T - event T)
        scale:  the time scale for the window. Default 5 minutes.
        """

        w_start  = self.windowStart*scale
        w_inflex = self.windowMax  *scale
        w_end    = self.windowEnd  *scale

        if deltat <= w_start-DAY:
            rank = -0.1
        elif   deltat <= w_start:
	    rank = 0.0
        elif deltat <= w_inflex:
            rank = (deltat-w_start)/(w_inflex-w_start)
        elif deltat <= w_end:
            rank = 1.0
        else:
            rank = -1.0
            
        return rank

    def suggestObs (self, 
                    dateProfile, 
                    moonProfile,
                    n=100,
                    skyfields=None, 
                    proximity=None,
                    targetProfiles=None,
		    exclusiveObservation=None,
		    minDistance2Moon=0.0):
        """
        Return the list of (at most) the n (currently) higher ranking
        observations.

        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)
                            moonPhase       current moon phase in range [0-100]
        n               number of observations to return.
        skyfields       array of fieldIDs  (with index-synced to proximity)
        proximity       array of proximity distance between current
                        telescope position and each entry in skyfields (with
                        index-synced to skyfields)
        targetProfiles: array of [AirMass,
                                Sky brightness,
                                Filterlist,
                                transparency,
                                cloudSeeing,
                                distance2moon,
                                altitude,
                                rawSeeing,
                                moonAlt]
                        values (dictionary-keyed to fieldID)

        Return
        An array of the (at most) n highest ranking observations,
        ordered from the highest ranking obs to the lowest.
        """
        if ( self.log and self.verbose > 1 ):
           self.log.info('%sProp: suggestObs() propID=%d' %(self.TransientType, self.propID))
        # do we need to rerank fields?
#        if self.reuseRanking > 1:
#            self.reuseRanking -= 1
#            return []

        # Copy the input vars
        inFieldID = skyfields
        inproximity = proximity
        intargetProfiles = targetProfiles
        (date,mjd,lst_RAD) = dateProfile
        (moonRA_RAD,moonDec_RAD,moonPhase_PERCENT) = moonProfile

        # Create a priority queue to choose the best n obs
	# before checking observing cycle to assure the clearance
	# of winners and losers list avoiding wrong serendipity observations.
        self.clearSuggestList()

	# Check the start/end of observing cycle.
	# For NEA the lunation is checked
	if self.CheckObservingCycle(date):

            # First of all, discard all the targets which are not visible
            # right now.
            fields_received=len(self.tonightTargets)
            fields_invisible=0
	    fields_moon=0
            fields_nottoday=0
            fields_waiting=0
            fields_missed=0
            fields_lost=0
            fields_completed=0
            fields_proposed=0
            fieldfilter_proposed=0

	    if (exclusiveObservation != None):
		if exclusiveObservation.fieldID in self.tonightTargets.keys():
                    listOfFieldsToEvaluate = [exclusiveObservation.fieldID]
		else:
		    listOfFieldsToEvaluate = []
                numberOfObsToPropose = 0
	    else:
#                listOfFieldsToEvaluate = self.tonightTargets.keys()
		listOfFieldsToEvaluate = sorted(self.tonightTargets.iterkeys())
                numberOfObsToPropose = n

            for fieldID in listOfFieldsToEvaluate:
                ra = self.tonightTargets[fieldID][0]
                dec = self.tonightTargets[fieldID][1]
                i = inFieldID.index (fieldID)

                #=============================================================
                # RAA --- To Be Done  04 May 2005
                # Factor into ranking the proximity of this target to all fields
                # check all target fields for this local proposal
                #
                #    # ? If proximity == 0 then want to set factor to infinite?
                #
                #    # do something with the telescope's proximity to this Field
                #    .....  inproximity[i]   ....
                #=============================================================

                if (fieldID==self.last_observed_fieldID) and (self.last_observed_wasForThisProposal) and (not self.AcceptConsecutiveObs):
                    continue

                airmass = intargetProfiles[i][0]
                if (airmass > self.maxAirmass):
                    if self.log and self.verbose>2:
                        self.log.info('%sProp: suggestObs() propID=%d field=%i too low:%f' % (self.TransientType, self.propID,fieldID,airmass))
                    fields_invisible+=1
                    continue

                distance2moon = intargetProfiles[i][5]
                if (distance2moon < minDistance2Moon):
                    fields_moon+=1
                    # remove the target for the rest of the night if it is too close to the moon
                    del self.tonightTargets[fieldID]
                    continue

                #.............................................................
                # Gets the list of possible filters based on the sky brightness
                skyBrightness = intargetProfiles[i][1]
                allowedFilterList = self.allowedFiltersForBrightness(skyBrightness)
                filterSeeingList = intargetProfiles[i][2]
                #.............................................................

                # Obtains the time for the next event in the sequence for this field
                if self.sequences[fieldID].IsActive():
		    try:
                    	nextdate = self.sequences[fieldID].nextDate()
		    except:
			print 'fieldID = '+str(fieldID)
			raise(IndexError, 'fieldID')

                    # Obtains the interval between the next event and the previous
                    # to quantify the precision required for next event.
                    nextperiod = self.sequences[fieldID].nextPeriod()

                    # Computes the base ranking based on the proximity of the next
                    # event.
                    deltaT = date - nextdate
                    rankTime = self.RankWindow(deltaT, nextperiod)
                    rankLossRisk = max(1.0-0.5*self.sequences[fieldID].GetRemainingAllowedMisses(), 0.0)
                else:
                    rankTime = self.rankIdleSeq
                    rankLossRisk = 0.0

                if rankTime == -0.1:
                    # if more than one day is needed for next event, then remove
                    # the target from the list, it will be received next night if it
                    # is visible.
                    fields_nottoday+=1
                    del self.tonightTargets[fieldID]

                elif rankTime > 0.0:
                    fields_proposed+=1

                    rankForFilters = self.RankFilters(fieldID, filterSeeingList, allowedFilterList)
                    # Boost factor according to the remaining observable days on sky
                    if self.DaysLeftToStartBoost > 0.0 and self.rankDaysLeftMax != 0.0:
                        observableDaysLeft = max((self.ha_twilight[fieldID]+self.ha_maxairmass) * 15.0, 0.0)
                        rankDaysLeft = max(1.0-observableDaysLeft/self.DaysLeftToStartBoost, 0.0)
                    else:
                        rankDaysLeft = 0.0

#                    for filter in rankForFilters.keys():
                    for filter in sorted(rankForFilters.iterkeys()):
		
                        rank = rankTime*self.rankTimeMax + rankForFilters[filter]*self.rankFilter + rankLossRisk*self.rankLossRiskMax + rankDaysLeft*self.rankDaysLeftMax

                        # Create the corresponding Observation
                        recordFieldFilter = self.obsPool[fieldID][filter]
                        #recordFieldFilter.sessionID = sessionID
                        #recordFieldFilter.propID = propID
                        #recordFieldFilter.fieldID = fieldID
                        #recordFieldFilter.filter = filter
                        recordFieldFilter.seqn = self.sequences[fieldID].seqNum
                        recordFieldFilter.date = date
                        recordFieldFilter.mjd = mjd
                        recordFieldFilter.exposureTime = self.exposureTime
                        #recordFieldFilter.slewTime = slewTime
                        #recordFieldFilter.rotatorSkyPos = 0.0
                        #recordFieldFilter.rotatorTelPos = 0.0
                        recordFieldFilter.propRank = rank
                        #recordFieldFilter.finRank = finRank
                        recordFieldFilter.maxSeeing = self.maxSeeing
                        recordFieldFilter.rawSeeing = intargetProfiles[i][7]
                        recordFieldFilter.seeing = filterSeeingList[filter]
                        recordFieldFilter.transparency = intargetProfiles[i][3]
                        recordFieldFilter.cloudSeeing = intargetProfiles[i][4]
                        recordFieldFilter.airmass = airmass
                        recordFieldFilter.skyBrightness = skyBrightness
                        #recordFieldFilter.ra = ra
                        #recordFieldFilter.dec = dec
                        recordFieldFilter.lst = lst_RAD
                        recordFieldFilter.altitude = intargetProfiles[i][6]
                        recordFieldFilter.azimuth  = intargetProfiles[i][9]
                        recordFieldFilter.distance2moon = intargetProfiles[i][5]
                        recordFieldFilter.moonRA = moonRA_RAD
                        recordFieldFilter.moonDec = moonDec_RAD
                        recordFieldFilter.moonAlt = intargetProfiles[i][8]
                        recordFieldFilter.moonPhase = moonPhase_PERCENT

                        self.addToSuggestList (recordFieldFilter, inproximity[i])
                        fieldfilter_proposed+=1

                elif rankTime < 0.0:

                    fields_missed+=1

                    obsHist = self.lsstDB.addMissedObservation(self.sequences[fieldID].GetNextFilter(), date, mjd, 0, lst_RAD, self.sessionID, fieldID)
                    self.sequences[fieldID].missEvent(date, obsHist.obsHistID)

                    if self.log and self.verbose>0 and not self.sequences[fieldID].IsLost():
                        t_secs = date%60
                        t_mins = (date%3600)/60
                        t_hour = (date%86400)/3600
                        t_days = (date)/86400

			self.log.info('%sProp: suggestObs() event MISSED for propID=%d field=%i t=%dd%02dh%02dm%02ds progress=%i%%' % (self.TransientType, self.propID, fieldID, t_days, t_hour, t_mins, t_secs, 100*self.sequences[fieldID].GetProgress()))

                    # now the sequence object determines if the sequence is lost, according to the
                    # number of missed events and the maximum allowed.
                    if self.sequences[fieldID].IsLost():
                        if self.log and self.verbose>0:
                            self.log.info('%sProp: suggestObs() sequence LOST for propID=%d field=%i t=%.0f event missed' % (self.TransientType, self.propID
,fieldID, date))

                        # Update the SeqHistory database
                        seq = self.sequences[fieldID]
                        seqHist = self.lsstDB.addSeqHistory(seq.date,
                                                    date,
                                                    seq.seqNum,
                                                    seq.GetProgress(),
                                                    seq.GetNumTargetEvents(),
                                                    seq.GetNumActualEvents(),
                                                    MAX_MISSED_EVENTS,
                                                    0,
                                                    fieldID,
                                                    self.sessionID,
                                                    self.propID)
		        for obsID in seq.GetListObsID():
		            self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID, self.sessionID)

		        for misID in seq.GetListMisID():
		            self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID, self.sessionID)

                        fields_lost+=1
                        del self.tonightTargets[fieldID]
                    # it is also possible that the missed event was the last needed for completing the sequence
                    # in such a case the sequence object determines the completion of the sequence.
                    elif self.sequences[fieldID].IsComplete():
                        self.CompleteSequence(fieldID, date)
                        fields_completed+=1

                else:
                    fields_waiting+=1

            if self.log and self.verbose>0:
                self.log.info('%sProp: suggestObs() propID=%d fields count: received=%i invisible=%i moon=%i nottoday=%i waiting=%i proposed=%i missed=%i lost=%i completed=%i fieldfilter=%i' % (self.TransientType, self.propID, fields_received, fields_invisible, fields_moon, fields_nottoday, fields_waiting, fields_proposed, fields_missed, fields_lost, fields_completed, fieldfilter_proposed))

            # Choose the n highest ranking observations
#            self.reuseRanking = self.reuseRankingCount
            return self.getSuggestList(numberOfObsToPropose)

	else:
	    # The cycle has ended and next one hasn't started yet (full moon for NEA)
	    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, observation, twilightProfile, obsHistID):
        """
        Registers the fact that the indicated observation took place.
        This is, the corresponding event in the sequence of the indicated
        fieldID has been executed, and the sequence can continue.
        
        Input
        obs     an Observation instance
        winslewTime  slew time required for the winning observation
        
        Return
        None
        
        Raise
        Exception if Observation History update fails
        """

	if not self.IsObservingCycle():
            self.last_observed_wasForThisProposal = False
	    return None

        if ( self.log and self.verbose > 1 ):
            self.log.info('TransientProp: closeObservation()')

        obs = super (TransientProp, self).closeObservation(observation, twilightProfile, obsHistID)

        if obs != None:
            self.sequences[obs.fieldID].closeEvent(obs.date, obs.filter)
            progress = self.sequences[obs.fieldID].GetProgress()

            if self.log and self.verbose>0:
                t_secs = obs.date%60
                t_mins = (obs.date%3600)/60
                t_hour = (obs.date%86400)/3600
                t_days = (obs.date)/86400

                self.log.info('%sProp: closeObservation() propID=%d field=%d filter=%s propRank=%.4f finRank=%.4f t=%dd%02dh%02dm%02ds progress=%d%%' % (self.propFullName, self.propID, obs.fieldID, obs.filter, obs.propRank, obs.finRank, t_days, t_hour, t_mins, t_secs, int(100*progress)))

            # if sequence is complete, then deletes target from tonight's list.
            if progress == 1.0:
		self.CompleteSequence(obs.fieldID, obs.date)
       
        return obs

    def CompleteSequence(self, fieldID, date):

	# Update sequence history DB
        seq = self.sequences[fieldID]
        seqHist = self.lsstDB.addSeqHistory(seq.date,
                                date,
                                seq.seqNum,
                                seq.GetProgress(),
                                seq.GetNumTargetEvents(),
                                seq.GetNumActualEvents(),
                                SUCCESS,
                                0,
                                fieldID,
                                self.sessionID,
                                self.propID)
        for obsID in seq.GetListObsID():
            self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID, self.sessionID)

        for misID in seq.GetListMisID():
            self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID, self.sessionID)

        del self.tonightTargets[fieldID]

        if (self.log and self.verbose >0):
            self.log.info('%sProp: closeObservation() sequence COMPLETE for propID=%d field=%d' % (self.propFullName, self.propID, fieldID))

	return

    def FinishSequences(self, obsdate):
        """
        Finishes the current sequences.
        """
        if ( self.log ):
            self.log.info ('TransientProp: FinishSequences()')

        for fieldID in self.sequences.keys():
            if self.sequences[fieldID].IsProgressing():
                if ( self.log ):
                    self.log.info('%sProp: FinishSequences() propID=%d sequence LOST for field=%i end of cycle' % (self.propFullName, self.propID, fieldID))
                self.sequences[fieldID].SetLost()

                # Update sequence history DB
                seq = self.sequences[fieldID]
                seqHist = self.lsstDB.addSeqHistory(seq.date,
                                        obsdate,
                                        seq.seqNum,
                                        seq.GetProgress(),
                                        seq.GetNumTargetEvents(),
                                        seq.GetNumActualEvents(),
                                        CYCLE_END,
                                        0,
                                        fieldID,
                                        self.sessionID,
                                        self.propID)
	        for obsID in seq.GetListObsID():
        	    self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID, self.sessionID)

	        for misID in seq.GetListMisID():
        	    self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID, self.sessionID)

        return

    def closeProposal(self, time):
        """
        Finishes the current sequences.
        """
        if ( self.log ):
            self.log.info ('%sProp: closeProposal()' % (self.propFullName))

        for fieldID in self.sequences.keys():
            if self.sequences[fieldID].IsProgressing():
                if ( self.log ):
                    self.log.info('%sProp: closeProposal() propID=%d sequence LOST for field=%i end of simulation' % (self.propFullName, self.propID, fieldID))
                self.sequences[fieldID].SetLost()

                # Update sequence history DB
                seq = self.sequences[fieldID]
                seqHist = self.lsstDB.addSeqHistory(seq.date,
                                        time,
                                        seq.seqNum,
                                        seq.GetProgress(),
                                        seq.GetNumTargetEvents(),
                                        seq.GetNumActualEvents(),
                                        SIMULATION_END,
                                        0,
                                        fieldID,
                                        self.sessionID,
                                        self.propID)
	        for obsID in seq.GetListObsID():
        	    self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID, self.sessionID)

	        for misID in seq.GetListMisID():
        	    self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID, self.sessionID)

        # delete OlapField user-defined region table
        if not (self.userRegion[0] == None):
            overlappingField = "OlapField_%d_%d" %(self.sessionID,self.propID)
            result = self.lsstDB.dropTable(overlappingField)

        return

    def RestartSequences(self):

        if (self.log): 
            self.log.info('TransientProp: RestartFinishedSequences() propID=%d' %(self.propID))

        for fieldID in self.sequences.keys():
            if self.sequences[fieldID].IsLost() or (self.sequences[fieldID].IsComplete() and not fieldID in self.blockedTargets):
		self.SeqCount += 1
                self.sequences[fieldID].Restart(self.SeqCount)
                if self.log and self.verbose>0:
                    self.log.info('TransientProp: RestartSequences() sequence for propID=%d field=%i restarted' % (self.propID,fieldID))

        return 

    def updateTargetList (self, dateProfile, obsProfile, sky, fov ):

        return
Exemplo n.º 5
0
class TransientProp(Proposal):
    """
    This class is here to describe a Transient Objects case scenario. 
    """
    def __init__(self,
                 lsstDB,
                 propConf,
                 propName,
                 propFullName,
                 sky,
                 weather,
                 sessionID,
                 filters,
                 targetList=None,
                 dbTableDict=None,
                 log=False,
                 logfile='./TransientProp.log',
                 verbose=0,
                 transientConf=DefaultNEAConfigFile):
        """
        Standard initializer.
        
	lsstDB      LSST DB access object        
	propConf    file name containing the instance's configuration data
        propName    proposal 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 "./NearEarthProp.log".
        verbose:    Log verbosity: -1=none, 0=minimal, 1=wordy, >1=verbose
        transientConf: Near Earth Asteroid Configuration file

        """
        super(TransientProp, self).__init__(lsstDB=lsstDB,
                                            propConf=propConf,
                                            propName=propName,
                                            propFullName=propFullName,
                                            sky=sky,
                                            weather=weather,
                                            sessionID=sessionID,
                                            filters=filters,
                                            targetList=targetList,
                                            dbTableDict=dbTableDict,
                                            log=log,
                                            logfile=logfile,
                                            verbose=verbose)

        self.lsstDB = lsstDB
        self.verbose = verbose
        self.dbTableDict = dbTableDict

        # DataBase specifics
        self.dbTable = self.dbTableDict['field']
        self.dbRA = 'fieldRA'
        self.dbDec = 'fieldDec'
        self.dbID = 'fieldID'

        self.winners = []
        self.loosers = []

        self.obsHistory = None
        #        self.seqHistory = None
        self.sessionID = sessionID

        # self.targets is a convenience dictionary. Its keys are
        # fieldIDs, its values are the corresponding RA and Dec.
        self.targets = {}
        self.tonightTargets = {}
        self.sequences = {}
        self.blockedTargets = []

        # Create the ObsHistory instance and cleanup any leftover
        self.obsHistory = ObsHistory(lsstDB=self.lsstDB,
                                     dbTableDict=self.dbTableDict,
                                     log=self.log,
                                     logfile=self.logfile,
                                     verbose=self.verbose)

        self.obsHistory.cleanupProposal(self.propID, self.sessionID)

        # Create the SeqHistory instance and cleanup any leftover
        #        self.seqHistory = SeqHistory (lsstDB=self.lsstDB,
        #				      dbTableDict=self.dbTableDict,
        #                                      log=self.log,
        #                                      logfile=self.logfile,
        #                                      verbose=self.verbose)

        #        self.seqHistory.cleanupProposal (self.propID, self.sessionID)

        self.SeqCount = 0

        return

    def start(self):
        """
        Activate the TransientProp instance.

        """

        # PAUSE
        #        yield hold, self
        return

    def startNight(self, dateProfile, moonProfile, startNewLunation,
                   randomizeSequencesSelection, nRun, mountedFiltersList):

        super(TransientProp,
              self).startNight(dateProfile, moonProfile, startNewLunation,
                               mountedFiltersList)

        self.tonightTargets = self.targets.copy()

        date = 0
        duration = self.seqDuration
        distrib = IvezicDistribution(date,
                                     duration,
                                     intervals=self.visitIntervals,
                                     repeats=self.cycleRepeats,
                                     repeatdelay=self.cycleInterval)
        filters = self.seqFilters
        if not isinstance(filters, list):
            filters = [filters]

        # deletes all sequences that did not start while the fields were
        # in the region for starting new sequences.
        notstarted = 0
        for fieldID in self.sequences.keys():
            if not fieldID in self.targetsNewSeq.keys():
                if self.sequences[fieldID].IsIdle():
                    notstarted += 1
                    del self.sequences[fieldID]

        # counts the active sequences in the target region
        currentActiveSequences = 0
        for fieldID in self.sequences.keys():
            if fieldID in self.tonightTargets.keys():
                if (not self.sequences[fieldID].IsLost()) and (
                        not self.sequences[fieldID].IsComplete()):
                    currentActiveSequences += 1

        # creates the sequence object for all the new fields in the restricted area
        newseq = 0
        restartedlost = 0
        restartedcomplete = 0
        keptsequences = 0
        #        listOfNewFields=self.targetsNewSeq.keys()
        listOfNewFields = sorted(self.targetsNewSeq.iterkeys())
        while (len(listOfNewFields) > 0
               and currentActiveSequences < self.maxNumberActiveSequences):
            if randomizeSequencesSelection:
                fieldID = random.choice(listOfNewFields)
            else:
                fieldID = listOfNewFields[0]
            listOfNewFields.remove(fieldID)
            if not fieldID in self.sequences.keys():
                self.SeqCount += 1
                self.sequences[fieldID] = IvezicSequence(
                    self.propID, fieldID, self.SeqCount, filters, distrib,
                    date, duration, self.maxMissedEvents)
                newseq += 1
                currentActiveSequences += 1
            elif self.sequences[fieldID].IsLost():
                if self.restartLostSequences:
                    self.SeqCount += 1
                    self.sequences[fieldID].Restart(self.SeqCount)
                    restartedlost += 1
                    currentActiveSequences += 1
                else:
                    del self.tonightTargets[fieldID]

            elif self.sequences[fieldID].IsComplete():
                if self.restartCompleteSequences and not self.blockCompleteSeqConsecutiveLunations:
                    self.SeqCount += 1
                    self.sequences[fieldID].Restart(self.SeqCount)
                    restartedcomplete += 1
                    currentActiveSequences += 1
                else:
                    del self.tonightTargets[fieldID]

# From the broad target area, just keep the fields
# associated to a target that is not lost and not complete
        for fieldID in self.tonightTargets.keys():
            if fieldID in self.sequences.keys():
                if self.sequences[fieldID].IsLost(
                ) or self.sequences[fieldID].IsComplete():
                    del self.tonightTargets[fieldID]
                else:
                    keptsequences += 1
            else:
                del self.tonightTargets[fieldID]

        if (self.log):
            self.log.info(
                '%sProp: startNight() propID=%d added %i new sequences, deleted %i notstarted, restarted %i lost, restarted %i complete, kept %i'
                % (self.propFullName, self.propID, newseq, notstarted,
                   restartedlost, restartedcomplete, keptsequences))

        return

    def GetProgressPerFilter(self):

        coaddedNumber = 0
        coaddedProgress = 0
        for fieldID in self.sequences.keys():
            if not self.sequences[fieldID].IsLost():
                coaddedNumber += 1
                coaddedProgress += self.sequences[fieldID].GetProgress()
        if coaddedNumber > 0:
            coaddedProgress /= coaddedNumber
        if (self.log):
            self.log.info(
                '%sProp: GetProgressPerFilter() propID=%d Sequence progress: %.3f%%'
                % (self.propFullName, self.propID, 100 * coaddedProgress))

        progressFilter = {}
        for filter in self.seqFilters:
            progressFilter[filter] = coaddedProgress
        if (self.log):
            for filter in progressFilter.keys():
                self.log.info(
                    '%sProp: GetProgressPerFilter() propID=%d Filter progress: %10s = %.3f%%'
                    % (self.propFullName, self.propID, filter,
                       100 * progressFilter[filter]))

        return progressFilter

    def RankWindow(self, deltat, scale=300.0):
        """
        Evaluates the priority of an event according to the proximity to the
        event time.

        Input
        deltat: the proximity in seconds to the event (current T - event T)
        scale:  the time scale for the window. Default 5 minutes.
        """

        w_start = self.windowStart * scale
        w_inflex = self.windowMax * scale
        w_end = self.windowEnd * scale

        if deltat <= w_start - DAY:
            rank = -0.1
        elif deltat <= w_start:
            rank = 0.0
        elif deltat <= w_inflex:
            rank = (deltat - w_start) / (w_inflex - w_start)
        elif deltat <= w_end:
            rank = 1.0
        else:
            rank = -1.0

        return rank

    def suggestObs(self,
                   dateProfile,
                   moonProfile,
                   n=100,
                   skyfields=None,
                   proximity=None,
                   targetProfiles=None,
                   exclusiveObservation=None,
                   minDistance2Moon=0.0):
        """
        Return the list of (at most) the n (currently) higher ranking
        observations.

        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)
                            moonPhase       current moon phase in range [0-100]
        n               number of observations to return.
        skyfields       array of fieldIDs  (with index-synced to proximity)
        proximity       array of proximity distance between current
                        telescope position and each entry in skyfields (with
                        index-synced to skyfields)
        targetProfiles: array of [AirMass,
                                Sky brightness,
                                Filterlist,
                                transparency,
                                cloudSeeing,
                                distance2moon,
                                altitude,
                                rawSeeing,
                                moonAlt]
                        values (dictionary-keyed to fieldID)

        Return
        An array of the (at most) n highest ranking observations,
        ordered from the highest ranking obs to the lowest.
        """
        if (self.log and self.verbose > 1):
            self.log.info('%sProp: suggestObs() propID=%d' %
                          (self.TransientType, self.propID))
        # do we need to rerank fields?
#        if self.reuseRanking > 1:
#            self.reuseRanking -= 1
#            return []

# Copy the input vars
        inFieldID = skyfields
        inproximity = proximity
        intargetProfiles = targetProfiles
        (date, mjd, lst_RAD) = dateProfile
        (moonRA_RAD, moonDec_RAD, moonPhase_PERCENT) = moonProfile

        # Create a priority queue to choose the best n obs
        # before checking observing cycle to assure the clearance
        # of winners and losers list avoiding wrong serendipity observations.
        self.clearSuggestList()

        # Check the start/end of observing cycle.
        # For NEA the lunation is checked
        if self.CheckObservingCycle(date):

            # First of all, discard all the targets which are not visible
            # right now.
            fields_received = len(self.tonightTargets)
            fields_invisible = 0
            fields_moon = 0
            fields_nottoday = 0
            fields_waiting = 0
            fields_missed = 0
            fields_lost = 0
            fields_completed = 0
            fields_proposed = 0
            fieldfilter_proposed = 0

            if (exclusiveObservation != None):
                if exclusiveObservation.fieldID in self.tonightTargets.keys():
                    listOfFieldsToEvaluate = [exclusiveObservation.fieldID]
                else:
                    listOfFieldsToEvaluate = []
                numberOfObsToPropose = 0
            else:
                #                listOfFieldsToEvaluate = self.tonightTargets.keys()
                listOfFieldsToEvaluate = sorted(self.tonightTargets.iterkeys())
                numberOfObsToPropose = n

            for fieldID in listOfFieldsToEvaluate:
                ra = self.tonightTargets[fieldID][0]
                dec = self.tonightTargets[fieldID][1]
                i = inFieldID.index(fieldID)

                #=============================================================
                # RAA --- To Be Done  04 May 2005
                # Factor into ranking the proximity of this target to all fields
                # check all target fields for this local proposal
                #
                #    # ? If proximity == 0 then want to set factor to infinite?
                #
                #    # do something with the telescope's proximity to this Field
                #    .....  inproximity[i]   ....
                #=============================================================

                if (fieldID == self.last_observed_fieldID) and (
                        self.last_observed_wasForThisProposal) and (
                            not self.AcceptConsecutiveObs):
                    continue

                airmass = intargetProfiles[i][0]
                if (airmass > self.maxAirmass):
                    if self.log and self.verbose > 2:
                        self.log.info(
                            '%sProp: suggestObs() propID=%d field=%i too low:%f'
                            % (self.TransientType, self.propID, fieldID,
                               airmass))
                    fields_invisible += 1
                    continue

                distance2moon = intargetProfiles[i][5]
                if (distance2moon < minDistance2Moon):
                    fields_moon += 1
                    # remove the target for the rest of the night if it is too close to the moon
                    del self.tonightTargets[fieldID]
                    continue

                #.............................................................
                # Gets the list of possible filters based on the sky brightness
                skyBrightness = intargetProfiles[i][1]
                allowedFilterList = self.allowedFiltersForBrightness(
                    skyBrightness)
                filterSeeingList = intargetProfiles[i][2]
                #.............................................................

                # Obtains the time for the next event in the sequence for this field
                if self.sequences[fieldID].IsActive():
                    try:
                        nextdate = self.sequences[fieldID].nextDate()
                    except:
                        print 'fieldID = ' + str(fieldID)
                        raise (IndexError, 'fieldID')

                # Obtains the interval between the next event and the previous
                # to quantify the precision required for next event.
                    nextperiod = self.sequences[fieldID].nextPeriod()

                    # Computes the base ranking based on the proximity of the next
                    # event.
                    deltaT = date - nextdate
                    rankTime = self.RankWindow(deltaT, nextperiod)
                    rankLossRisk = max(
                        1.0 - 0.5 *
                        self.sequences[fieldID].GetRemainingAllowedMisses(),
                        0.0)
                else:
                    rankTime = self.rankIdleSeq
                    rankLossRisk = 0.0

                if rankTime == -0.1:
                    # if more than one day is needed for next event, then remove
                    # the target from the list, it will be received next night if it
                    # is visible.
                    fields_nottoday += 1
                    del self.tonightTargets[fieldID]

                elif rankTime > 0.0:
                    fields_proposed += 1

                    rankForFilters = self.RankFilters(fieldID,
                                                      filterSeeingList,
                                                      allowedFilterList)
                    # Boost factor according to the remaining observable days on sky
                    if self.DaysLeftToStartBoost > 0.0 and self.rankDaysLeftMax != 0.0:
                        observableDaysLeft = max(
                            (self.ha_twilight[fieldID] + self.ha_maxairmass) *
                            15.0, 0.0)
                        rankDaysLeft = max(
                            1.0 -
                            observableDaysLeft / self.DaysLeftToStartBoost,
                            0.0)
                    else:
                        rankDaysLeft = 0.0

#                    for filter in rankForFilters.keys():
                    for filter in sorted(rankForFilters.iterkeys()):

                        rank = rankTime * self.rankTimeMax + rankForFilters[
                            filter] * self.rankFilter + rankLossRisk * self.rankLossRiskMax + rankDaysLeft * self.rankDaysLeftMax

                        # Create the corresponding Observation
                        recordFieldFilter = self.obsPool[fieldID][filter]
                        #recordFieldFilter.sessionID = sessionID
                        #recordFieldFilter.propID = propID
                        #recordFieldFilter.fieldID = fieldID
                        #recordFieldFilter.filter = filter
                        recordFieldFilter.seqn = self.sequences[fieldID].seqNum
                        recordFieldFilter.date = date
                        recordFieldFilter.mjd = mjd
                        recordFieldFilter.exposureTime = self.exposureTime
                        #recordFieldFilter.slewTime = slewTime
                        #recordFieldFilter.rotatorSkyPos = 0.0
                        #recordFieldFilter.rotatorTelPos = 0.0
                        recordFieldFilter.propRank = rank
                        #recordFieldFilter.finRank = finRank
                        recordFieldFilter.maxSeeing = self.maxSeeing
                        recordFieldFilter.rawSeeing = intargetProfiles[i][7]
                        recordFieldFilter.seeing = filterSeeingList[filter]
                        recordFieldFilter.transparency = intargetProfiles[i][3]
                        recordFieldFilter.cloudSeeing = intargetProfiles[i][4]
                        recordFieldFilter.airmass = airmass
                        recordFieldFilter.skyBrightness = skyBrightness
                        #recordFieldFilter.ra = ra
                        #recordFieldFilter.dec = dec
                        recordFieldFilter.lst = lst_RAD
                        recordFieldFilter.altitude = intargetProfiles[i][6]
                        recordFieldFilter.azimuth = intargetProfiles[i][9]
                        recordFieldFilter.distance2moon = intargetProfiles[i][
                            5]
                        recordFieldFilter.moonRA = moonRA_RAD
                        recordFieldFilter.moonDec = moonDec_RAD
                        recordFieldFilter.moonAlt = intargetProfiles[i][8]
                        recordFieldFilter.moonPhase = moonPhase_PERCENT

                        self.addToSuggestList(recordFieldFilter,
                                              inproximity[i])
                        fieldfilter_proposed += 1

                elif rankTime < 0.0:

                    fields_missed += 1

                    obsHist = self.lsstDB.addMissedObservation(
                        self.sequences[fieldID].GetNextFilter(), date, mjd, 0,
                        lst_RAD, self.sessionID, fieldID)
                    self.sequences[fieldID].missEvent(date, obsHist.obsHistID)

                    if self.log and self.verbose > 0 and not self.sequences[
                            fieldID].IsLost():
                        t_secs = date % 60
                        t_mins = (date % 3600) / 60
                        t_hour = (date % 86400) / 3600
                        t_days = (date) / 86400

                        self.log.info(
                            '%sProp: suggestObs() event MISSED for propID=%d field=%i t=%dd%02dh%02dm%02ds progress=%i%%'
                            % (self.TransientType, self.propID, fieldID,
                               t_days, t_hour, t_mins, t_secs,
                               100 * self.sequences[fieldID].GetProgress()))

                    # now the sequence object determines if the sequence is lost, according to the
                    # number of missed events and the maximum allowed.
                    if self.sequences[fieldID].IsLost():
                        if self.log and self.verbose > 0:
                            self.log.info(
                                '%sProp: suggestObs() sequence LOST for propID=%d field=%i t=%.0f event missed'
                                % (self.TransientType, self.propID, fieldID,
                                   date))

                        # Update the SeqHistory database
                        seq = self.sequences[fieldID]
                        seqHist = self.lsstDB.addSeqHistory(
                            seq.date, date, seq.seqNum, seq.GetProgress(),
                            seq.GetNumTargetEvents(), seq.GetNumActualEvents(),
                            MAX_MISSED_EVENTS, 0, fieldID, self.sessionID,
                            self.propID)
                        for obsID in seq.GetListObsID():
                            self.lsstDB.addSeqHistoryObsHistory(
                                seqHist.sequenceID, obsID, self.sessionID)

                        for misID in seq.GetListMisID():
                            self.lsstDB.addSeqHistoryMissedHistory(
                                seqHist.sequenceID, misID, self.sessionID)

                        fields_lost += 1
                        del self.tonightTargets[fieldID]
                    # it is also possible that the missed event was the last needed for completing the sequence
                    # in such a case the sequence object determines the completion of the sequence.
                    elif self.sequences[fieldID].IsComplete():
                        self.CompleteSequence(fieldID, date)
                        fields_completed += 1

                else:
                    fields_waiting += 1

            if self.log and self.verbose > 0:
                self.log.info(
                    '%sProp: suggestObs() propID=%d fields count: received=%i invisible=%i moon=%i nottoday=%i waiting=%i proposed=%i missed=%i lost=%i completed=%i fieldfilter=%i'
                    % (self.TransientType, self.propID, fields_received,
                       fields_invisible, fields_moon, fields_nottoday,
                       fields_waiting, fields_proposed, fields_missed,
                       fields_lost, fields_completed, fieldfilter_proposed))

            # Choose the n highest ranking observations
#            self.reuseRanking = self.reuseRankingCount
            return self.getSuggestList(numberOfObsToPropose)

        else:
            # The cycle has ended and next one hasn't started yet (full moon for NEA)
            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, observation, twilightProfile, obsHistID):
        """
        Registers the fact that the indicated observation took place.
        This is, the corresponding event in the sequence of the indicated
        fieldID has been executed, and the sequence can continue.
        
        Input
        obs     an Observation instance
        winslewTime  slew time required for the winning observation
        
        Return
        None
        
        Raise
        Exception if Observation History update fails
        """

        if not self.IsObservingCycle():
            self.last_observed_wasForThisProposal = False
            return None

        if (self.log and self.verbose > 1):
            self.log.info('TransientProp: closeObservation()')

        obs = super(TransientProp,
                    self).closeObservation(observation, twilightProfile,
                                           obsHistID)

        if obs != None:
            self.sequences[obs.fieldID].closeEvent(obs.date, obs.filter)
            progress = self.sequences[obs.fieldID].GetProgress()

            if self.log and self.verbose > 0:
                t_secs = obs.date % 60
                t_mins = (obs.date % 3600) / 60
                t_hour = (obs.date % 86400) / 3600
                t_days = (obs.date) / 86400

                self.log.info(
                    '%sProp: closeObservation() propID=%d field=%d filter=%s propRank=%.4f finRank=%.4f t=%dd%02dh%02dm%02ds progress=%d%%'
                    % (self.propFullName, self.propID, obs.fieldID, obs.filter,
                       obs.propRank, obs.finRank, t_days, t_hour, t_mins,
                       t_secs, int(100 * progress)))

            # if sequence is complete, then deletes target from tonight's list.
            if progress == 1.0:
                self.CompleteSequence(obs.fieldID, obs.date)

        return obs

    def CompleteSequence(self, fieldID, date):

        # Update sequence history DB
        seq = self.sequences[fieldID]
        seqHist = self.lsstDB.addSeqHistory(seq.date, date, seq.seqNum,
                                            seq.GetProgress(),
                                            seq.GetNumTargetEvents(),
                                            seq.GetNumActualEvents(), SUCCESS,
                                            0, fieldID, self.sessionID,
                                            self.propID)
        for obsID in seq.GetListObsID():
            self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID,
                                                self.sessionID)

        for misID in seq.GetListMisID():
            self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID,
                                                   self.sessionID)

        del self.tonightTargets[fieldID]

        if (self.log and self.verbose > 0):
            self.log.info(
                '%sProp: closeObservation() sequence COMPLETE for propID=%d field=%d'
                % (self.propFullName, self.propID, fieldID))

        return

    def FinishSequences(self, obsdate):
        """
        Finishes the current sequences.
        """
        if (self.log):
            self.log.info('TransientProp: FinishSequences()')

        for fieldID in self.sequences.keys():
            if self.sequences[fieldID].IsProgressing():
                if (self.log):
                    self.log.info(
                        '%sProp: FinishSequences() propID=%d sequence LOST for field=%i end of cycle'
                        % (self.propFullName, self.propID, fieldID))
                self.sequences[fieldID].SetLost()

                # Update sequence history DB
                seq = self.sequences[fieldID]
                seqHist = self.lsstDB.addSeqHistory(seq.date, obsdate,
                                                    seq.seqNum,
                                                    seq.GetProgress(),
                                                    seq.GetNumTargetEvents(),
                                                    seq.GetNumActualEvents(),
                                                    CYCLE_END, 0, fieldID,
                                                    self.sessionID,
                                                    self.propID)
                for obsID in seq.GetListObsID():
                    self.lsstDB.addSeqHistoryObsHistory(
                        seqHist.sequenceID, obsID, self.sessionID)

                for misID in seq.GetListMisID():
                    self.lsstDB.addSeqHistoryMissedHistory(
                        seqHist.sequenceID, misID, self.sessionID)

        return

    def closeProposal(self, time):
        """
        Finishes the current sequences.
        """
        if (self.log):
            self.log.info('%sProp: closeProposal()' % (self.propFullName))

        for fieldID in self.sequences.keys():
            if self.sequences[fieldID].IsProgressing():
                if (self.log):
                    self.log.info(
                        '%sProp: closeProposal() propID=%d sequence LOST for field=%i end of simulation'
                        % (self.propFullName, self.propID, fieldID))
                self.sequences[fieldID].SetLost()

                # Update sequence history DB
                seq = self.sequences[fieldID]
                seqHist = self.lsstDB.addSeqHistory(seq.date, time, seq.seqNum,
                                                    seq.GetProgress(),
                                                    seq.GetNumTargetEvents(),
                                                    seq.GetNumActualEvents(),
                                                    SIMULATION_END, 0, fieldID,
                                                    self.sessionID,
                                                    self.propID)
                for obsID in seq.GetListObsID():
                    self.lsstDB.addSeqHistoryObsHistory(
                        seqHist.sequenceID, obsID, self.sessionID)

                for misID in seq.GetListMisID():
                    self.lsstDB.addSeqHistoryMissedHistory(
                        seqHist.sequenceID, misID, self.sessionID)

        # delete OlapField user-defined region table
        if not (self.userRegion[0] == None):
            overlappingField = "OlapField_%d_%d" % (self.sessionID,
                                                    self.propID)
            result = self.lsstDB.dropTable(overlappingField)

        return

    def RestartSequences(self):

        if (self.log):
            self.log.info(
                'TransientProp: RestartFinishedSequences() propID=%d' %
                (self.propID))

        for fieldID in self.sequences.keys():
            if self.sequences[fieldID].IsLost() or (
                    self.sequences[fieldID].IsComplete()
                    and not fieldID in self.blockedTargets):
                self.SeqCount += 1
                self.sequences[fieldID].Restart(self.SeqCount)
                if self.log and self.verbose > 0:
                    self.log.info(
                        'TransientProp: RestartSequences() sequence for propID=%d field=%i restarted'
                        % (self.propID, fieldID))

        return

    def updateTargetList(self, dateProfile, obsProfile, sky, fov):

        return
Exemplo n.º 6
0
class TransSubSeqProp(Proposal):
    """
    This class is here to describe a Transient Objects case scenario.
    """
    def __init__(self,
                 lsstDB,
                 propConf,
                 propName,
                 propFullName,
                 sky,
                 weather,
                 sessionID,
                 filters,
                 targetList=None,
                 dbTableDict=None,
                 log=False,
                 logfile='./TransSubSeqProp.log',
                 verbose=0,
                 transientConf=DefaultNEAConfigFile):
        """
        Standard initializer.

        lsstDB	    LSST DB access object
        propConf    file name containing the instance's configuration data
        propName    proposal 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 "./NearEarthProp.log".
        verbose:    Log verbosity: -1=none, 0=minimal, 1=wordy, >1=verbose
        transientConf: Near Earth Asteroid Configuration file

        """
        super(TransSubSeqProp, self).__init__(lsstDB=lsstDB,
                                              propConf=propConf,
                                              propName=propName,
                                              propFullName=propFullName,
                                              sky=sky,
                                              weather=weather,
                                              sessionID=sessionID,
                                              filters=filters,
                                              targetList=targetList,
                                              dbTableDict=dbTableDict,
                                              log=log,
                                              logfile=logfile,
                                              verbose=verbose)

        self.lsstDB = lsstDB
        self.verbose = verbose
        self.dbTableDict = dbTableDict

        # DataBase specifics
        self.dbTable = self.dbTableDict['field']
        self.dbRA = 'fieldRA'
        self.dbDec = 'fieldDec'
        self.dbID = 'fieldID'

        self.winners = []
        self.loosers = []

        self.obsHistory = None
        #        self.seqHistory = None
        self.sessionID = sessionID

        # self.targets is a convenience dictionary. Its keys are
        # fieldIDs, its values are the corresponding RA and Dec.
        self.targets = {}
        self.tonightTargets = {}
        self.sequences = {}
        self.tonightSubseqsForTarget = {}

        # Create the ObsHistory instance and cleanup any leftover
        self.obsHistory = ObsHistory(lsstDB=self.lsstDB,
                                     dbTableDict=self.dbTableDict,
                                     log=self.log,
                                     logfile=self.logfile,
                                     verbose=self.verbose)

        self.obsHistory.cleanupProposal(self.propID, self.sessionID)

        # Create the SeqHistory instance and cleanup any leftover
        #        self.seqHistory = SeqHistory (lsstDB=self.lsstDB,
        #				      dbTableDict=self.dbTableDict,
        #                                      log=self.log,
        #                                      logfile=self.logfile,
        #                                      verbose=self.verbose)

        #        self.seqHistory.cleanupProposal (self.propID, self.sessionID)

        self.exclusiveBlockNeeded = False
        self.exclusiveObs = None
        self.exclusiveField = None
        self.exclusiveSubseq = None
        self.SeqCount = 0

        return

    def start(self):
        """
        Activate the TransientProp instance.

        """

        # Removes the subsequences that require 0 events
        N = len(self.subSeqName)
        for n in range(N):
            ix = N - n - 1
            if self.subSeqEvents[ix] == 0:
                self.subSeqName.pop(ix)
                self.subSeqNested.pop(ix)
                self.subSeqFilters.pop(ix)
                self.subSeqExposures.pop(ix)
                self.subSeqEvents.pop(ix)
                self.subSeqMaxMissed.pop(ix)
                self.subSeqInterval.pop(ix)
                self.subSeqWindowStart.pop(ix)
                self.subSeqWindowMax.pop(ix)
                self.subSeqWindowEnd.pop(ix)

        return

    def ComplyToFilterChangeBurstConstraint(self, filterBurstNumber,
                                            filterBurstTime, visitTime,
                                            readoutTime, filterTime):

        N = len(self.subSeqName)
        for n in range(N):
            subFilters = self.subSeqFilters[n].split(',')
            nfilters = len(subFilters)
            if nfilters > 1:
                print self.propConf
                print self.subSeqName[n]
                print subFilters
                subVisits = self.subSeqExposures[n].split(',')
                print subVisits
                T = []
                for k in range(nfilters):
                    tk = 0
                    if k > 0:
                        tk += filterTime
                    tk += int(subVisits[k]) * visitTime + (int(subVisits[k]) -
                                                           1) * readoutTime
                    T.append(tk)
                print T

                numIdxToCheck = nfilters - 1
                if numIdxToCheck >= filterBurstNumber:
                    for startIdx in range(
                            0, numIdxToCheck - filterBurstNumber + 1):
                        tt = 0
                        for idx in range(startIdx,
                                         startIdx + filterBurstNumber):
                            tt += T[idx]
                        if tt < filterBurstTime:
                            print "REJECTED %f < %f" % (tt, filterBurstTime)
                            return False

        return True

    def startNight(self, dateProfile, moonProfile, startNewLunation,
                   randomizeSequencesSelection, nRun, mountedFiltersList):

        super(TransSubSeqProp,
              self).startNight(dateProfile, moonProfile, startNewLunation,
                               mountedFiltersList)

        (date, mjd, lst_RAD) = dateProfile
        runProgress = date / YEAR / nRun

        self.tonightTargets = self.targets.copy()

        # deletes all sequences that did not start while the fields were
        # in the region for starting new sequences.
        notstarted = 0
        for fieldID in self.sequences.keys():
            if fieldID not in self.targetsNewSeq.keys():
                if self.sequences[fieldID].IsIdle():
                    # self.log.info('%sProp: startNight() deleted sequence field=%d at progress=%.3f%% '
                    #               'state=%d nevents=%d' % (self.propFullName, fieldID,
                    #                                        100*self.sequences[fieldID].GetProgress(),
                    #                                        self.sequences[fieldID].state,
                    #                                        self.sequences[fieldID].nAllEvents))
                    notstarted += 1
                    del self.sequences[fieldID]

        # counts the active sequences in the target region
        currentActiveSequences = 0
        coaddedProgress = 0.0
        coaddedNumber = 0
        for fieldID in self.sequences.keys():
            if fieldID in self.tonightTargets.keys():
                coaddedProgress += self.sequences[fieldID].GetProgress()
                coaddedNumber += 1
                if (not self.sequences[fieldID].IsLost()) and (
                        not self.sequences[fieldID].IsComplete()):
                    currentActiveSequences += 1

        # creates the sequence object for all the new fields in the restricted area
        newseq = 0
        restartedlost = 0
        restartedcomplete = 0
        keptsequences = 0
        #listOfNewFields=self.targetsNewSeq.keys()
        listOfNewFields = sorted(self.targetsNewSeq.iterkeys())
        while (len(listOfNewFields) > 0
               and currentActiveSequences < self.maxNumberActiveSequences and
               (coaddedProgress / max(coaddedNumber, 1) > runProgress
                or currentActiveSequences < self.minNumberActiveSequences)):
            if randomizeSequencesSelection:
                fieldID = random.choice(listOfNewFields)
            else:
                fieldID = listOfNewFields[0]
            listOfNewFields.remove(fieldID)

            # create a new sequence object
            if fieldID not in self.sequences.keys():
                self.SeqCount += 1
                self.sequences[fieldID] = SuperSequence(
                    self.propID, fieldID, self.SeqCount, self.WLtype,
                    self.numGroupedVisits, self.masterSubSequence,
                    self.subSeqName, self.subSeqNested, self.subSeqFilters,
                    self.subSeqExposures, self.subSeqEvents,
                    self.subSeqMaxMissed, self.subSeqInterval,
                    self.subSeqWindowStart, self.subSeqWindowMax,
                    self.subSeqWindowEnd, self.overflowLevel,
                    self.progressToStartBoost, self.maxBoostToComplete)
                coaddedNumber += 1
                newseq += 1
                currentActiveSequences += 1
            # was sequence lost?
            elif self.sequences[fieldID].IsLost():
                if self.restartLostSequences:
                    self.SeqCount += 1
                    self.sequences[fieldID].Restart(self.SeqCount)
                    restartedlost += 1
                    currentActiveSequences += 1
                else:
                    # ZZZZ - mm debug 2nd delete of tonightTargets
                    # print "Prop[%d].startNight() while targetsNewSeq: seq lost: delete "\
                    #       "self.tonightTargets[%d] date = %d" % (self.propID, fieldID, date)
                    del self.tonightTargets[fieldID]
            # was sequence completed?
            elif self.sequences[fieldID].IsComplete():
                if self.restartCompleteSequences and not self.overflow:
                    self.SeqCount += 1
                    self.sequences[fieldID].Restart(self.SeqCount)
                    restartedcomplete += 1
                    currentActiveSequences += 1
                else:
                    # ZZZZ - mm debug 2nd delete of tonightTargets
                    # print "Prop[%d].startNight() while targetsNewSeq: seq complete: "\
                    #       "delete self.tonightTargets[%d] date = %d" % (self.propID, fieldID, date)
                    del self.tonightTargets[fieldID]
            #else:
            #    prog = self.sequences[fieldID].GetProgress()
            #    if prog >= 1.0:
            #    print "seqnNum=%i fieldID=%i progress=%f" % (self.sequences[fieldID].seqNum, fieldID, prog,
            #                                                 str(self.sequences[fieldID].allHistory),
            #                                                 self.sequences[fieldID].state)

        # From the broad target area, just keep the fields
        # associated to a target that is not lost and not complete
        self.tonightSubseqsForTarget = {}
        for fieldID in self.tonightTargets.keys():
            if fieldID in self.sequences.keys():
                complete_seq = self.sequences[fieldID].IsComplete(
                ) and not self.overflow
                if self.sequences[fieldID].IsLost() or complete_seq:
                    # ZZZZ - mm debug 2nd delete of tonightTargets
                    # print "Prop[%d].startNight() for tonightTargets() in self.sequences: seq is lost or "\
                    #       "complete: delete self.tonightTargets[%d] date = %d" % (self.propID, fieldID,
                    #                                                               date)
                    # print "how would we ever get here?"
                    del self.tonightTargets[fieldID]
                else:
                    keptsequences += 1
                    self.tonightSubseqsForTarget[fieldID] = list(
                        self.sequences[fieldID].subSeqName)
            # only pursue targets that are already started sequences
            else:
                # ZZZZ - mm debug 2nd delete of tonightTargets
                # print "TSS.startNight() for tonightTargets() and not in self.sequences: delete "\
                #       "self.tonightTargets[%d] date = %d" % (fieldID, date)
                del self.tonightTargets[fieldID]

        if coaddedNumber > 0:
            self.globalProgress = coaddedProgress / coaddedNumber
        else:
            self.globalProgress = 0.0

        if self.log:
            self.log.info(
                '%sProp: startNight() propID=%d Sequences: new=%i deletedidle=%i '
                'restartedlost=%i restartedcomplete=%i total=%i targetprogress=%.3f%% '
                'runprogress=%.3f%%' %
                (self.propFullName, self.propID, newseq, notstarted,
                 restartedlost, restartedcomplete, keptsequences,
                 100 * self.globalProgress, 100 * runProgress))

        return

    def GetProgressPerFilter(self):

        coaddedNumber = 0
        coaddedSubseqProgress = {}
        progressFilter = {}
        numsubseFilter = {}
        # for subseq in self.subSeqName:
        #     coaddedSubseqProgress[subseq] = 0
        for fieldID in self.sequences.keys():
            if not self.sequences[fieldID].IsLost():
                coaddedNumber += 1
                for subseq in self.sequences[fieldID].subSeqName:
                    progress = self.sequences[fieldID].GetProgress(subseq)
                    if subseq in coaddedSubseqProgress.keys():
                        coaddedSubseqProgress[subseq] += progress
                    else:
                        coaddedSubseqProgress[subseq] = progress

                    for filter in self.sequences[
                            fieldID].GetFilterListForSubseq(subseq):
                        if filter in progressFilter.keys():
                            progressFilter[filter] += progress
                            numsubseFilter[filter] += 1
                        else:
                            progressFilter[filter] = progress
                            numsubseFilter[filter] = 1

        if coaddedNumber > 0:
            for subseq in coaddedSubseqProgress.keys():
                coaddedSubseqProgress[subseq] /= coaddedNumber
        if self.log:
            for subseq in coaddedSubseqProgress.keys():
                self.log.info(
                    '%sProp: GetProgressPerFilter() propID=%d Sub-Sequence progress: %20s = %.3f%%'
                    % (self.propFullName, self.propID, subseq,
                       100 * coaddedSubseqProgress[subseq]))

#	progressFilter = {}
#	numsubseFilter = {}
#	for ix in range(len(self.subSeqName)):
#	    subseq =  self.subSeqName[ix]
#	    for filter in self.subSeqFilters[ix].split(','):
#		if filter != '':
#		    if filter in progressFilter.keys():
#			progressFilter[filter] += coaddedSubseqProgress[subseq]
#			numsubseFilter[filter] += 1
#		    else:
#                        progressFilter[filter]  = coaddedSubseqProgress[subseq]
#                        numsubseFilter[filter]  = 1

        for filter in progressFilter.keys():
            progressFilter[filter] /= numsubseFilter[filter]
        if self.log:
            for filter in progressFilter.keys():
                self.log.info(
                    '%sProp: GetProgressPerFilter() propID=%d Filter progress: %10s = %.3f%%'
                    % (self.propFullName, self.propID, filter,
                       100 * progressFilter[filter]))

        return progressFilter

    def MissEvent(self, date, mjd, fieldID, subseq, obsHistID):

        self.sequences[fieldID].MissEvent(date, subseq, obsHistID)
        if self.log and self.verbose > 0 and not self.sequences[
                fieldID].IsLost():
            t_secs = date % 60
            t_mins = (date % 3600) / 60
            t_hour = (date % 86400) / 3600
            t_days = date / 86400
            progress = 100 * self.sequences[fieldID].GetProgress()
            self.log.info(
                '%sProp: suggestObs() subevent MISSED for propID=%d field=%i subseq=%s '
                't=%dd%02dh%02dm%02ds progress=%i%%' %
                (self.propFullName, self.propID, fieldID, subseq, t_days,
                 t_hour, t_mins, t_secs, progress))

        filter = self.sequences[fieldID].GetNextFilter(subseq)
        obs = self.obsPool[fieldID][filter]
        obs.propID = self.propID
        obs.seqn = self.sequences[fieldID].seqNum
        obs.subsequence = subseq
        obs.pairNum = self.sequences[fieldID].GetPairNum(subseq)
        obs.date = date
        obs.mjd = mjd

        super(TransSubSeqProp, self).missObservation(obs)

        return

    def suggestObs(self,
                   dateProfile,
                   n=100,
                   exclusiveObservation=None,
                   minDistance2Moon=0.0,
                   rawSeeing=0.0,
                   seeing=0.0,
                   transparency=0.0,
                   sdnight=0,
                   sdtime=0):
        """
        Return the list of (at most) the n (currently) higher ranking
        observations.

        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)
                            moonPhase       current moon phase in range [0-100]
        n               number of observations to return.

        Return
        An array of the (at most) n highest ranking observations,
        ordered from the highest ranking obs to the lowest.
        """
        if self.log and self.verbose > 1:
            self.log.info('%sProp: suggestObs() propID=%d' %
                          (self.propFullName, self.propID))

        # Copy the input vars
#        inFieldID = skyfields
#	inproximity = proximity
#        intargetProfiles = targetProfiles
        (date, mjd, lst_RAD) = dateProfile
        (moonRA_RAD, moonDec_RAD,
         moonPhase_PERCENT) = self.schedulingData.moonProfile[sdnight]

        # Check the start/end of observing cycle.
        # For NEA the lunation is checked
        if self.CheckObservingCycle(date):

            # Create a priority queue to choose the best n obs
            self.clearSuggestList()

            # If in an exclusive block, no new observation candidates. If this
            # proposal originated request, it should re-suggest observation.
            if exclusiveObservation is not None:
                # adjust counter for one obs
                # self.reuseRanking -= 1
                if exclusiveObservation.propID == self.propID:

                    # The exclusive block is for this proposal so we just suggest our exclusive observation
                    fieldID = exclusiveObservation.fieldID
                    subseq = self.exclusiveSubseq
                    rank = 1.0

                    #                    i = inFieldID.index (fieldID)
                    #airmass = self.schedulingData.airmass[fieldID][sdtime]

                    filter = self.sequences[fieldID].GetNextFilter(subseq)
                    #print filter
                    exclusiveBlockRequired = self.sequences[
                        fieldID].GetExclusiveBlockNeed(subseq)
                    #print "exclusiveBlockRequired = %s" % (exclusiveBlockRequired)

                    recordFieldFilter = self.obsPool[fieldID][filter]
                    recordFieldFilter.propID = self.propID
                    recordFieldFilter.subsequence = subseq
                    recordFieldFilter.seqn = self.sequences[fieldID].seqNum
                    recordFieldFilter.pairNum = self.sequences[
                        fieldID].GetPairNum(subseq)
                    recordFieldFilter.date = date
                    recordFieldFilter.mjd = mjd
                    recordFieldFilter.night = sdnight
                    recordFieldFilter.exposureTime = self.exposureTime
                    recordFieldFilter.propRank = rank
                    recordFieldFilter.maxSeeing = self.exclusiveObs.maxSeeing
                    recordFieldFilter.rawSeeing = self.exclusiveObs.rawSeeing
                    recordFieldFilter.seeing = self.exclusiveObs.seeing
                    recordFieldFilter.transparency = self.exclusiveObs.transparency
                    recordFieldFilter.cloudSeeing = self.exclusiveObs.cloudSeeing
                    recordFieldFilter.airmass = self.exclusiveObs.airmass
                    recordFieldFilter.skyBrightness = self.exclusiveObs.skyBrightness
                    recordFieldFilter.lst = lst_RAD
                    recordFieldFilter.altitude = self.exclusiveObs.altitude
                    recordFieldFilter.azimuth = self.exclusiveObs.azimuth
                    recordFieldFilter.distance2moon = self.exclusiveObs.distance2moon
                    recordFieldFilter.moonRA = self.exclusiveObs.moonRA
                    recordFieldFilter.moonDec = self.exclusiveObs.moonDec
                    recordFieldFilter.moonAlt = self.exclusiveObs.moonAlt
                    recordFieldFilter.moonPhase = self.exclusiveObs.moonPhase
                    recordFieldFilter.exclusiveBlockRequired = exclusiveBlockRequired

                    self.addToSuggestList(recordFieldFilter)
                    return self.getSuggestList(1)

                else:
                    # The exclusive block is not for this proposal so we don't propose observations
                    # but we must update the ranking and availability of the exclusive observation for
                    # correct serendipity
                    if exclusiveObservation.fieldID in self.tonightTargets.keys(
                    ):
                        listOfFieldsToEvaluate = [exclusiveObservation.fieldID]
                    else:
                        listOfFieldsToEvaluate = []
#                    return []
                    numberOfObsToPropose = 0

            else:
                # Normal observation block, all proposals competing
                # If not time to rerank fields, return no suggestions.
                # if self.reuseRanking > 1:
                #     self.reuseRanking -= 1
                #     return []

                #listOfFieldsToEvaluate = self.tonightTargets.keys()
                listOfFieldsToEvaluate = sorted(self.tonightTargets.iterkeys())
                numberOfObsToPropose = n

                # ZZZ - This block needs attention. Do we get here? Does it make sense? - mm
                if self.exclusiveBlockNeeded:
                    # We needed an exclusive block to complete the current event
                    # so we miss this event and evaluate if the sequence is missed or complete
                    fieldID = self.exclusiveField
                    subseq = self.exclusiveSubseq

                    self.exclusiveBlockNeeded = False

                    obsHist = self.lsstDB.addMissedObservation(
                        self.sequences[fieldID].GetNextFilter(subseq), date,
                        mjd, sdnight, lst_RAD, self.sessionID, fieldID)
                    self.MissEvent(date, mjd, fieldID, subseq,
                                   obsHist.missedHistID)

            fields_received = len(listOfFieldsToEvaluate)
            fields_invisible = 0
            fields_moon = 0
            events_waiting = 0
            events_proposed = 0
            events_missed = 0
            seq_lost = 0
            seq_completed = 0
            events_nottonight = 0
            events_nofilter = 0
            events_noseeing = 0

            for fieldID in listOfFieldsToEvaluate:

                fieldRecordList = []

                # ra and dec variables are unused!!!!!
                #ra = self.tonightTargets[fieldID][0]
                #dec = self.tonightTargets[fieldID][1]
                #i = inFieldID.index (fieldID)

                if fieldID == self.last_observed_fieldID and self.last_observed_wasForThisProposal and \
                        not self.AcceptConsecutiveObs:
                    continue

                airmass = self.schedulingData.airmass[sdtime][fieldID]
                if airmass > self.maxAirmass:
                    if self.log and self.verbose > 2:
                        self.log.info(
                            '%sProp: suggestObs() propID=%d field=%i too low:%f'
                            %
                            (self.propFullName, self.propID, fieldID, airmass))
                    fields_invisible += 1
                    continue

                distance2moon = self.schedulingData.dist2moon[sdtime][fieldID]
                if distance2moon < minDistance2Moon:
                    fields_moon += 1
                    # remove the target for the rest of the night if it is too close to the moon
                    # ZZZZ - mm debug 2nd delete of tonightTargets
                    #                print "Prop[%d].suggestObs():too close to moon - delete self.tonightTargets[%d] date = %d" \
                    #                    % (self.propID, fieldID, date)
                    del self.tonightTargets[fieldID]
                    continue

                # if not self.sequences[fieldID].HasEventsTonight(date):
                #     seq_nottonight += 1
                #     del self.tonightTargets[fieldID]
                #     continue
                #.............................................................
                # Gets the list of possible filters based on the sky brightness
                skyBrightness = self.schedulingData.brightness[sdtime][fieldID]
                allowedFilterList = self.allowedFiltersForBrightness(
                    skyBrightness)
                filterSeeingList = self.filters.computeFilterSeeing(
                    seeing, airmass)
                #rankForFilters = self.RankFilters(fieldID, filterSeeingList)
                #.............................................................
                for subseq in self.tonightSubseqsForTarget[fieldID]:

                    # Prevents that a lost sequence due to a missed event
                    # keeps beeing evaluated for the other subsequences
                    # wasting time and triggering an error when deleting
                    # the already deleted field from the target list.
                    if self.sequences[fieldID].IsLost():
                        continue

                    allfiltersavailable = True
                    for f in self.sequences[fieldID].GetFilterListForSubseq(
                            subseq):
                        if f not in allowedFilterList:
                            allfiltersavailable = False
                            events_nofilter += 1
                        elif filterSeeingList[f] > self.FilterMaxSeeing[f]:
                            allfiltersavailable = False
                            events_noseeing += 1

                    if allfiltersavailable is False:
                        continue

                    # Boost factor according to the remaining observable days on sky
                    if self.DaysLeftToStartBoost > 0.0 and self.rankDaysLeftMax != 0.0:
                        observableDaysLeft = max(
                            (self.ha_twilight[fieldID] + self.ha_maxairmass) *
                            15.0, 0.0)
                        rankDaysLeft = max(
                            1.0 -
                            observableDaysLeft / self.DaysLeftToStartBoost,
                            0.0)
                    else:
                        rankDaysLeft = 0.0

                    if self.WLtype or self.sequences[fieldID].IsActive(subseq):
                        (rankTime,
                         timeWindow) = self.sequences[fieldID].RankTimeWindow(
                             subseq, date)
                        rankLossRisk = max(
                            1.0 - 0.5 * self.sequences[fieldID].
                            GetRemainingAllowedMisses(subseq), 0.0)
                    elif self.sequences[fieldID].IsIdle(subseq):
                        rankTime = self.rankIdleSeq / self.rankTimeMax
                        timeWindow = True
                        rankLossRisk = 0.0
                    else:
                        rankTime = 0.0
                        timeWindow = False
                        rankLossRisk = 0.0

                    if rankTime == -0.1:
                        events_nottonight += 1
                        self.tonightSubseqsForTarget[fieldID].remove(subseq)
                    elif rankTime > 0.0:
                        events_proposed += 1
                        # print 'ha_twilight=' + str(self.ha_twilight[fieldID]) + ' ha_maxairmass='\
                        #       + str(self.ha_maxairmass) + ' observableDaysLeft=' + str(observableDaysLeft)\
                        #       + ' rankDaysLeft=' + str(rankDaysLeft)

                        if timeWindow:
                            factor = self.rankTimeMax
                        else:
                            if self.globalProgress < 1.0:
                                factor = self.rankIdleSeq / (
                                    1.0 - self.globalProgress)
                            elif self.overflowLevel > 0.0:
                                factor = self.rankIdleSeq / (
                                    self.overflowLevel / self.globalProgress)

                        rank = rankTime * factor + rankLossRisk * self.rankLossRiskMax \
                            + rankDaysLeft * self.rankDaysLeftMax

                        filter = self.sequences[fieldID].GetNextFilter(subseq)
                        #print 'fieldID='+str(fieldID)+' subseq='+str(subseq)+' filter='+str(filter)
                        exclusiveBlockRequired = self.sequences[
                            fieldID].GetExclusiveBlockNeed(subseq)

                        # Create the corresponding Observation
                        recordFieldFilter = self.obsPool[fieldID][filter]
                        #recordFieldFilter.sessionID = sessionID
                        recordFieldFilter.propID = self.propID
                        recordFieldFilter.subsequence = subseq
                        #recordFieldFilter.fieldID = fieldID
                        #recordFieldFilter.filter = filter
                        recordFieldFilter.seqn = self.sequences[fieldID].seqNum
                        recordFieldFilter.pairNum = self.sequences[
                            fieldID].GetPairNum(subseq)
                        recordFieldFilter.date = date
                        recordFieldFilter.mjd = mjd
                        recordFieldFilter.night = sdnight
                        recordFieldFilter.exposureTime = self.exposureTime
                        #recordFieldFilter.slewTime = slewTime
                        #recordFieldFilter.rotatorSkyPos = 0.0
                        #recordFieldFilter.rotatorTelPos = 0.0
                        recordFieldFilter.propRank = rank
                        #recordFieldFilter.finRank = finRank
                        recordFieldFilter.maxSeeing = self.FilterMaxSeeing[
                            filter]
                        recordFieldFilter.rawSeeing = rawSeeing
                        recordFieldFilter.seeing = filterSeeingList[filter]
                        recordFieldFilter.transparency = transparency
                        #recordFieldFilter.cloudSeeing = intargetProfiles[i][4]
                        recordFieldFilter.airmass = airmass
                        recordFieldFilter.skyBrightness = skyBrightness
                        #recordFieldFilter.ra = ra
                        #recordFieldFilter.dec = dec
                        recordFieldFilter.lst = lst_RAD
                        recordFieldFilter.altitude = self.schedulingData.alt[
                            sdtime][fieldID]
                        recordFieldFilter.azimuth = self.schedulingData.az[
                            sdtime][fieldID]
                        recordFieldFilter.parallactic = self.schedulingData.pa[
                            sdtime][fieldID]
                        recordFieldFilter.distance2moon = distance2moon
                        recordFieldFilter.moonRA = moonRA_RAD
                        recordFieldFilter.moonDec = moonDec_RAD
                        #recordFieldFilter.moonAlt = intargetProfiles[i][8]
                        recordFieldFilter.moonPhase = moonPhase_PERCENT
                        recordFieldFilter.exclusiveBlockRequired = exclusiveBlockRequired

                        fieldRecordList.append(recordFieldFilter)

                    elif rankTime < 0.0:

                        events_missed += 1

                        oh_filter = self.sequences[fieldID].GetNextFilter(
                            subseq)
                        obsHist = self.lsstDB.addMissedObservation(
                            oh_filter, date, mjd, sdnight, lst_RAD,
                            self.sessionID, fieldID)
                        self.MissEvent(date, mjd, fieldID, subseq,
                                       obsHist.missedHistID)

                        if self.sequences[fieldID].IsLost():
                            if self.log and self.verbose > 0:
                                self.log.info(
                                    '%sProp: suggestObs() sequence LOST for propID=%d field=%i '
                                    't=%.0f event missed' %
                                    (self.propFullName, self.propID, fieldID,
                                     date))

                            # Update the SeqHistory database
                            seq = self.sequences[fieldID]
                            seqHist = self.lsstDB.addSeqHistory(
                                seq.date, date, seq.seqNum, seq.GetProgress(),
                                seq.GetNumTargetEvents(),
                                seq.GetNumActualEvents(), MAX_MISSED_EVENTS, 0,
                                fieldID, self.sessionID, self.propID)
                            for obsID in seq.GetListObsID():
                                self.lsstDB.addSeqHistoryObsHistory(
                                    seqHist.sequenceID, obsID, self.sessionID)

                            for misID in seq.GetListMisID():
                                self.lsstDB.addSeqHistoryMissedHistory(
                                    seqHist.sequenceID, misID, self.sessionID)

                            seq_lost += 1
                            # ZZZZ - mm debug 2nd delete of tonightTargets
                            print "Prop[%d].suggestObs() seq lost: delete self.tonightTargets[%d] date = %d"\
                                  % (self.propID, fieldID, date)
                            del self.tonightTargets[fieldID]
                            # Also remove the posible previously considered subsequences
                            # as the whole sequence is now lost they must not be proposed.
                            fieldRecordList = []
                        # it is also possible that the missed event was the last needed for completing the
                        # sequence in such a case the sequence object determines the completion of the
                        # sequence.
                        elif self.sequences[fieldID].IsComplete():
                            self.CompleteSequence(fieldID, date)
                            seq_completed += 1
                            fieldRecordList = []

                    else:
                        # rankTime==0.0
                        events_waiting += 1

                if self.tonightSubseqsForTarget[fieldID] == []:
                    # ZZZZ - mm debug 2nd delete of tonightTargets
                    # print "Prop[%d].suggestObs() no subseqs for target: delete self.tonightTargets[%d] "\
                    #       "date = %d" % (self.propID, fieldID, date)
                    del self.tonightTargets[fieldID]

                for record in fieldRecordList:
                    self.addToSuggestList(record)

            if self.log and self.verbose > 0:
                self.log.info(
                    '%sProp: suggestObs() propID=%d : Fields received=%i invisible=%i moon=%i '
                    'Events nottonight=%i waiting=%i nofilter=%i noseeing=%i proposed=%i missed=%i '
                    'Sequences lost=%i completed=%i' %
                    (self.propFullName, self.propID, fields_received,
                     fields_invisible, fields_moon, events_nottonight,
                     events_waiting, events_nofilter, events_noseeing,
                     events_proposed, events_missed, seq_lost, seq_completed))

            # Choose the n highest ranking observations
            # self.reuseRanking = self.reuseRankingCount
            return self.getSuggestList(numberOfObsToPropose)

        else:
            # The cycle has ended and next one hasn't started yet (full moon for NEA)
            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, observation, obsHistID, twilightProfile):
        """
        Registers the fact that the indicated observation took place.
        This is, the corresponding event in the sequence of the indicated
        fieldID has been executed, and the sequence can continue.

        Input
        obs     an Observation instance
        winslewTime  slew time required for the winning observation

        Return
        None

        Raise
        Exception if Observation History update fails
        """

        if not self.IsObservingCycle():
            self.last_observed_wasForThisProposal = False
            return None

        # if self.log and self.verbose > 1:
        #     self.log.info('%sProp: closeObservation()' % (self.propFullName))

        obs = super(TransSubSeqProp,
                    self).closeObservation(observation, obsHistID,
                                           twilightProfile)

        if obs is not None:
            self.sequences[obs.fieldID].ObserveEvent(obs.date, obs.subsequence,
                                                     obsHistID)
            progress = self.sequences[obs.fieldID].GetProgress()

            if self.log and self.verbose > 0:
                t_secs = obs.date % 60
                t_mins = (obs.date % 3600) / 60
                t_hour = (obs.date % 86400) / 3600
                t_days = obs.date / 86400

                if self.sequences[obs.fieldID].IsEventInProgress(
                        obs.subsequence):
                    progrmod = '+'
                else:
                    progrmod = ''
                self.log.info(
                    '%s: closeObservation() propID=%d field=%d filter=%s propRank=%.4f '
                    'finRank=%.4f t=%dd%02dh%02dm%02ds progress=%d%s%%' %
                    (self.propConf, self.propID, obs.fieldID, obs.filter,
                     obs.propRank, obs.finRank, t_days, t_hour, t_mins, t_secs,
                     int(100 * progress), progrmod))

            if obs.exclusiveBlockRequired:
                self.exclusiveBlockNeeded = True
                self.exclusiveObs = copy.copy(obs)
                self.exclusiveField = obs.fieldID
                self.exclusiveSubseq = obs.subsequence
            else:
                self.exclusiveBlockNeeded = False

            # if sequence is complete, then deletes target from tonight's list.
            if progress == 1.0:
                self.CompleteSequence(obs.fieldID, obs.date)
                self.exclusiveBlockNeeded = False

        return obs

    def CompleteSequence(self, fieldID, date):

        # Update sequence history DB
        seq = self.sequences[fieldID]
        seqHist = self.lsstDB.addSeqHistory(seq.date, date, seq.seqNum,
                                            seq.GetProgress(),
                                            seq.GetNumTargetEvents(),
                                            seq.GetNumActualEvents(), SUCCESS,
                                            0, fieldID, self.sessionID,
                                            self.propID)
        for obsID in seq.GetListObsID():
            self.lsstDB.addSeqHistoryObsHistory(seqHist.sequenceID, obsID,
                                                self.sessionID)

        for misID in seq.GetListMisID():
            self.lsstDB.addSeqHistoryMissedHistory(seqHist.sequenceID, misID,
                                                   self.sessionID)

        if not self.overflow:
            # ZZZZ - mm debug 2nd delete of tonightTargets
            # print "Prop[%d].CompleteSequence() delete self.tonightTargets[%d] date = %d" % (self.propID,
            #                                                                                 fieldID, date)
            if fieldID in self.tonightTargets.keys():
                del self.tonightTargets[fieldID]

        if self.log and self.verbose > 0:
            self.log.info(
                '%sProp: CompleteSequence() sequence COMPLETE for propID=%d field=%d'
                % (self.propFullName, self.propID, fieldID))

        return

    def FinishSequences(self, obsdate):
        """
        Finishes the current sequences.
        """
        if self.log:
            self.log.info('%sProp: FinishSequences()' % (self.propFullName))

        for fieldID in self.sequences.keys():
            if (not self.sequences[fieldID].IsComplete()) and (
                    not self.sequences[fieldID].IsLost()):
                if self.log:
                    self.log.info(
                        '%sProp: suggestObs() propID=%d sequence LOST for field=%i end of cycle'
                        % (self.propFullName, self.propID, fieldID))
                self.sequences[fieldID].Abort()

                # Update sequence history DB
                seq = self.sequences[fieldID]
                seqHist = self.lsstDB.addSeqHistory(seq.date, obsdate,
                                                    seq.seqNum,
                                                    seq.GetProgress(),
                                                    seq.GetNumTargetEvents(),
                                                    seq.GetNumActualEvents(),
                                                    CYCLE_END, 0, fieldID,
                                                    self.sessionID,
                                                    self.propID)
                for obsID in seq.GetListObsID():
                    self.lsstDB.addSeqHistoryObsHistory(
                        seqHist.sequenceID, obsID, self.sessionID)

                for misID in seq.GetListMisID():
                    self.lsstDB.addSeqHistoryMissedHistory(
                        seqHist.sequenceID, misID, self.sessionID)

        return

    def closeProposal(self, time):
        """
        Finishes the current sequences.
        """
        if self.log:
            self.log.info('%sProp: closeProposal()' % (self.propFullName))

        for fieldID in self.sequences.keys():
            if (not self.sequences[fieldID].IsComplete()) and (
                    not self.sequences[fieldID].IsLost()):
                if self.log:
                    self.log.info(
                        '%sProp: closeProposal() propID=%d sequence LOST for field=%i end of '
                        'simulation' %
                        (self.propFullName, self.propID, fieldID))
                #self.sequences[fieldID].Abort()

                # Update sequence history DB
                seq = self.sequences[fieldID]
                seqHist = self.lsstDB.addSeqHistory(seq.date, time, seq.seqNum,
                                                    seq.GetProgress(),
                                                    seq.GetNumTargetEvents(),
                                                    seq.GetNumActualEvents(),
                                                    SIMULATION_END, 0, fieldID,
                                                    self.sessionID,
                                                    self.propID)
                for obsID in seq.GetListObsID():
                    self.lsstDB.addSeqHistoryObsHistory(
                        seqHist.sequenceID, obsID, self.sessionID)

                for misID in seq.GetListMisID():
                    self.lsstDB.addSeqHistoryMissedHistory(
                        seqHist.sequenceID, misID, self.sessionID)

        # delete OlapField user-defined region table
        if self.userRegion[0] is not None:
            overlappingField = "OlapField_%d_%d" % (self.sessionID,
                                                    self.propID)
            # Result from below is never used
            self.lsstDB.dropTable(overlappingField)
        return

    def RestartSequences(self):

        if self.log:
            self.log.info('%sProp: RestartFinishedSequences() propID=%d' %
                          (self.propFullName, self.propID))

        for fieldID in self.sequences.keys():
            if self.sequences[fieldID].IsLost(
            ) or self.sequences[fieldID].IsComplete():
                self.SeqCount += 1
                self.sequences[fieldID].Restart(self.SeqCount)
                if self.log and self.verbose > 0:
                    self.log.info(
                        '%sProp: RestartSequences() sequence for propID=%d field=%i restarted'
                        % (self.propFullName, self.propID, fieldID))

        return

    def updateTargetList(self, dateProfile, obsProfile, sky, fov):

        return