23 """Run fgcmcal on a single tract. 
   32 import lsst.pex.config 
as pexConfig
 
   36 from .fgcmBuildStars 
import FgcmBuildStarsTask
 
   37 from .fgcmFitCycle 
import FgcmFitCycleConfig
 
   38 from .fgcmOutputProducts 
import FgcmOutputProductsTask
 
   39 from .utilities 
import makeConfigDict, translateFgcmLut, translateVisitCatalog
 
   40 from .utilities 
import computeCcdOffsets, computeApertureRadius, extractReferenceMags
 
   41 from .utilities 
import makeZptSchema, makeZptCat
 
   42 from .utilities 
import makeAtmSchema, makeAtmCat
 
   43 from .utilities 
import makeStdSchema, makeStdCat
 
   47 __all__ = [
'FgcmCalibrateTractConfig', 
'FgcmCalibrateTractTask', 
'FgcmCalibrateTractRunner']
 
   51     """Config for FgcmCalibrateTract""" 
   53     fgcmBuildStars = pexConfig.ConfigurableField(
 
   54         target=FgcmBuildStarsTask,
 
   55         doc=
"Task to load and match stars for fgcm",
 
   57     fgcmFitCycle = pexConfig.ConfigField(
 
   58         dtype=FgcmFitCycleConfig,
 
   59         doc=
"Config to run a single fgcm fit cycle",
 
   61     fgcmOutputProducts = pexConfig.ConfigurableField(
 
   62         target=FgcmOutputProductsTask,
 
   63         doc=
"Task to output fgcm products",
 
   65     convergenceTolerance = pexConfig.Field(
 
   66         doc=
"Tolerance on repeatability convergence (per band)",
 
   70     maxFitCycles = pexConfig.Field(
 
   71         doc=
"Maximum number of fit cycles",
 
   75     doDebuggingPlots = pexConfig.Field(
 
   76         doc=
"Make plots for debugging purposes?",
 
   82         pexConfig.Config.setDefaults(self)
 
   96             if not self.
fgcmFitCycle.useRepeatabilityForExpGrayCutsDict[band]:
 
   97                 msg = 
'Must set useRepeatabilityForExpGrayCutsDict[band]=True for all bands' 
   98                 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.useRepeatabilityForExpGrayCutsDict,
 
  103     """Subclass of TaskRunner for FgcmCalibrateTractTask 
  105     fgcmCalibrateTractTask.run() takes a number of arguments, one of which is 
  106     the butler (for persistence and mapper data), and a list of dataRefs 
  107     extracted from the command line.  This task runs on a constrained set 
  108     of dataRefs, typically a single tract. 
  109     This class transforms the process arguments generated by the ArgumentParser 
  110     into the arguments expected by FgcmCalibrateTractTask.run(). 
  111     This runner does not use any parallelization. 
  117         Return a list with one element: a tuple with the butler and 
  121         return [(parsedCmd.butler, parsedCmd.id.refList)]
 
  127         args: `tuple` with (butler, dataRefList) 
  131         exitStatus: `list` with `lsst.pipe.base.Struct` 
  132            exitStatus (0: success; 1: failure) 
  134         butler, dataRefList = args
 
  136         task = self.TaskClass(config=self.config, log=self.log)
 
  140             results = task.runDataRef(butler, dataRefList)
 
  143                 results = task.runDataRef(butler, dataRefList)
 
  144             except Exception 
as e:
 
  146                 task.log.fatal(
"Failed: %s" % e)
 
  147                 if not isinstance(e, pipeBase.TaskError):
 
  148                     traceback.print_exc(file=sys.stderr)
 
  150         task.writeMetadata(butler)
 
  152         if self.doReturnResults:
 
  153             return [pipeBase.Struct(exitStatus=exitStatus,
 
  156             return [pipeBase.Struct(exitStatus=exitStatus)]
 
  160         Run the task, with no multiprocessing 
  164         parsedCmd: `lsst.pipe.base.ArgumentParser` parsed command line 
  169         if self.precall(parsedCmd):
 
  171             resultList = self(targetList[0])
 
  178     Calibrate a single tract using fgcmcal 
  181     ConfigClass = FgcmCalibrateTractConfig
 
  182     RunnerClass = FgcmCalibrateTractRunner
 
  183     _DefaultName = 
"fgcmCalibrateTract" 
  187         Instantiate an `FgcmCalibrateTractTask`. 
  191         butler : `lsst.daf.persistence.Butler` 
  194         pipeBase.CmdLineTask.__init__(self, **kwargs)
 
  197     def _makeArgumentParser(cls):
 
  198         """Create an argument parser""" 
  201         parser.add_id_argument(
"--id", 
"calexp", help=
"Data ID, e.g. --id visit=6789",
 
  202                                ContainerClass=PerTractCcdDataIdContainer)
 
  207     def _getMetadataName(self):
 
  213         Run full FGCM calibration on a single tract, including building star list, 
  214         fitting multiple cycles, and making outputs. 
  218         butler:  `lsst.daf.persistence.Butler` 
  219         dataRefs: `list` of `lsst.daf.persistence.ButlerDataRef` 
  220            Data references for the input visits. 
  221            If this is an empty list, all visits with src catalogs in 
  222            the repository are used. 
  223            Only one individual dataRef from a visit need be specified 
  224            and the code will find the other source catalogs from 
  229         RuntimeError: Raised if `config.fgcmBuildStars.doReferenceMatches` is 
  230            not True, or if fgcmLookUpTable is not available, or if 
  231            doSubtractLocalBackground is True and aperture radius cannot be 
  235         if not butler.datasetExists(
'fgcmLookUpTable'):
 
  236             raise RuntimeError(
"Must run FgcmCalibrateTract with an fgcmLookUpTable")
 
  238         if not self.config.fgcmBuildStars.doReferenceMatches:
 
  239             raise RuntimeError(
"Must run FgcmCalibrateTract with fgcmBuildStars.doReferenceMatches")
 
  240         if self.config.fgcmBuildStars.checkAllCcds:
 
  241             raise RuntimeError(
"Cannot run FgcmCalibrateTract with fgcmBuildStars.checkAllCcds set to True")
 
  243         self.makeSubtask(
"fgcmBuildStars", butler=butler)
 
  244         self.makeSubtask(
"fgcmOutputProducts", butler=butler)
 
  248         calibFluxApertureRadius = 
None 
  249         if self.config.fgcmBuildStars.doSubtractLocalBackground:
 
  250             sourceSchema = butler.get(
'src_schema').schema
 
  253                                                                 self.config.fgcmBuildStars.instFluxField)
 
  254             except (RuntimeError, LookupError):
 
  255                 raise RuntimeError(
"Could not determine aperture radius from %s. " 
  256                                    "Cannot use doSubtractLocalBackground." %
 
  257                                    (self.config.instFluxField))
 
  260         tract = dataRefs[0].dataId[
'tract']
 
  261         self.log.
info(
"Running on tract %d" % (tract))
 
  264         groupedDataRefs = self.fgcmBuildStars.findAndGroupDataRefs(butler, dataRefs)
 
  265         camera = butler.get(
'camera')
 
  266         visitCat = self.fgcmBuildStars.fgcmMakeVisitCatalog(camera, groupedDataRefs)
 
  267         rad = calibFluxApertureRadius
 
  268         fgcmStarObservationCat = self.fgcmBuildStars.fgcmMakeAllStarObservations(groupedDataRefs,
 
  270                                                                                  calibFluxApertureRadius=rad)
 
  272         fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat = \
 
  273             self.fgcmBuildStars.fgcmMatchStars(butler,
 
  275                                                fgcmStarObservationCat)
 
  278         lutCat = butler.get(
'fgcmLookUpTable')
 
  280                                                          dict(self.config.fgcmFitCycle.filterMap))
 
  286         camera = butler.get(
'camera')
 
  287         configDict = 
makeConfigDict(self.config.fgcmFitCycle, self.log, camera,
 
  288                                     self.config.fgcmFitCycle.maxIterBeforeFinalCycle,
 
  289                                     True, 
False, tract=tract)
 
  291         configDict[
'doPlots'] = 
False 
  300         noFitsDict = {
'lutIndex': lutIndexVals,
 
  302                       'expInfo': fgcmExpInfo,
 
  303                       'ccdOffsets': ccdOffsets}
 
  305         fgcmFitCycle = fgcm.FgcmFitCycle(configDict, useFits=
False,
 
  306                                          noFitsDict=noFitsDict, noOutput=
True)
 
  311         conv = fgcmStarObservationCat[0][
'ra'].asDegrees() / float(fgcmStarObservationCat[0][
'ra'])
 
  314         fgcmPars = fgcm.FgcmParameters.newParsWithArrays(fgcmFitCycle.fgcmConfig,
 
  322         obsIndex = fgcmStarIndicesCat[
'obsIndex']
 
  323         visitIndex = np.searchsorted(fgcmExpInfo[
'VISIT'],
 
  324                                      fgcmStarObservationCat[
'visit'][obsIndex])
 
  327                                                  self.config.fgcmFitCycle.bands,
 
  328                                                  self.config.fgcmFitCycle.filterMap)
 
  329         refId = fgcmRefCat[
'fgcm_id'][:]
 
  331         fgcmStars = fgcm.FgcmStars(fgcmFitCycle.fgcmConfig)
 
  332         fgcmStars.loadStars(fgcmPars,
 
  333                             fgcmStarObservationCat[
'visit'][obsIndex],
 
  334                             fgcmStarObservationCat[
'ccd'][obsIndex],
 
  335                             fgcmStarObservationCat[
'ra'][obsIndex] * conv,
 
  336                             fgcmStarObservationCat[
'dec'][obsIndex] * conv,
 
  337                             fgcmStarObservationCat[
'instMag'][obsIndex],
 
  338                             fgcmStarObservationCat[
'instMagErr'][obsIndex],
 
  339                             fgcmExpInfo[
'FILTERNAME'][visitIndex],
 
  340                             fgcmStarIdCat[
'fgcm_id'][:],
 
  341                             fgcmStarIdCat[
'ra'][:],
 
  342                             fgcmStarIdCat[
'dec'][:],
 
  343                             fgcmStarIdCat[
'obsArrIndex'][:],
 
  344                             fgcmStarIdCat[
'nObs'][:],
 
  345                             obsX=fgcmStarObservationCat[
'x'][obsIndex],
 
  346                             obsY=fgcmStarObservationCat[
'y'][obsIndex],
 
  347                             psfCandidate=fgcmStarObservationCat[
'psf_candidate'][obsIndex],
 
  355         fgcmFitCycle.setLUT(fgcmLut)
 
  356         fgcmFitCycle.setStars(fgcmStars)
 
  361         del fgcmStarIndicesCat
 
  367         previousReservedRawRepeatability = np.zeros(fgcmPars.nBands) + 1000.0
 
  368         previousParInfo = 
None 
  369         previousParams = 
None 
  370         previousSuperStar = 
None 
  372         while (
not converged 
and cycleNumber < self.config.maxFitCycles):
 
  374             fgcmFitCycle.fgcmConfig.updateCycleNumber(cycleNumber)
 
  378                 fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(fgcmFitCycle.fgcmConfig,
 
  385                 fgcmFitCycle.fgcmStars.reloadStarMagnitudes(fgcmStarObservationCat[
'instMag'][obsIndex],
 
  386                                                             fgcmStarObservationCat[
'instMagErr'][obsIndex])
 
  387                 fgcmFitCycle.initialCycle = 
False 
  389             fgcmFitCycle.setPars(fgcmPars)
 
  390             fgcmFitCycle.finishSetup()
 
  395             previousParInfo, previousParams = fgcmFitCycle.fgcmPars.parsToArrays()
 
  396             previousSuperStar = fgcmFitCycle.fgcmPars.parSuperStarFlat.copy()
 
  398             self.log.
info(
"Raw repeatability after cycle number %d is:" % (cycleNumber))
 
  399             for i, band 
in enumerate(fgcmFitCycle.fgcmPars.bands):
 
  400                 if not fgcmFitCycle.fgcmPars.hasExposuresInBand[i]:
 
  402                 rep = fgcmFitCycle.fgcmPars.compReservedRawRepeatability[i] * 1000.0
 
  403                 self.log.
info(
"  Band %s, repeatability: %.2f mmag" % (band, rep))
 
  406             if np.alltrue((previousReservedRawRepeatability -
 
  407                            fgcmFitCycle.fgcmPars.compReservedRawRepeatability) <
 
  408                           self.config.convergenceTolerance):
 
  409                 self.log.
info(
"Raw repeatability has converged after cycle number %d." % (cycleNumber))
 
  412                 fgcmFitCycle.fgcmConfig.expGrayPhotometricCut[:] = fgcmFitCycle.updatedPhotometricCut
 
  413                 fgcmFitCycle.fgcmConfig.expGrayHighCut[:] = fgcmFitCycle.updatedHighCut
 
  414                 fgcmFitCycle.fgcmConfig.precomputeSuperStarInitialCycle = 
False 
  415                 fgcmFitCycle.fgcmConfig.freezeStdAtmosphere = 
False 
  416                 previousReservedRawRepeatability[:] = fgcmFitCycle.fgcmPars.compReservedRawRepeatability
 
  417                 self.log.
info(
"Setting exposure gray photometricity cuts to:")
 
  418                 for i, band 
in enumerate(fgcmFitCycle.fgcmPars.bands):
 
  419                     if not fgcmFitCycle.fgcmPars.hasExposuresInBand[i]:
 
  421                     cut = fgcmFitCycle.updatedPhotometricCut[i] * 1000.0
 
  422                     self.log.
info(
"  Band %s, photometricity cut: %.2f mmag" % (band, cut))
 
  428             self.log.
warn(
"Maximum number of fit cycles exceeded (%d) without convergence." % (cycleNumber))
 
  431         fgcmFitCycle.fgcmConfig.freezeStdAtmosphere = 
False 
  432         fgcmFitCycle.fgcmConfig.resetParameters = 
False 
  433         fgcmFitCycle.fgcmConfig.maxIter = 0
 
  434         fgcmFitCycle.fgcmConfig.outputZeropoints = 
True 
  435         fgcmFitCycle.fgcmConfig.outputStandards = 
True 
  436         fgcmFitCycle.fgcmConfig.doPlots = self.config.doDebuggingPlots
 
  437         fgcmFitCycle.fgcmConfig.updateCycleNumber(cycleNumber)
 
  438         fgcmFitCycle.initialCycle = 
False 
  440         fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(fgcmFitCycle.fgcmConfig,
 
  445         fgcmFitCycle.fgcmStars.reloadStarMagnitudes(fgcmStarObservationCat[
'instMag'][obsIndex],
 
  446                                                     fgcmStarObservationCat[
'instMagErr'][obsIndex])
 
  447         fgcmFitCycle.setPars(fgcmPars)
 
  448         fgcmFitCycle.finishSetup()
 
  450         self.log.
info(
"Running final clean-up fit cycle...")
 
  453         self.log.
info(
"Raw repeatability after clean-up cycle is:")
 
  454         for i, band 
in enumerate(fgcmFitCycle.fgcmPars.bands):
 
  455             if not fgcmFitCycle.fgcmPars.hasExposuresInBand[i]:
 
  457             rep = fgcmFitCycle.fgcmPars.compReservedRawRepeatability[i] * 1000.0
 
  458             self.log.
info(
"  Band %s, repeatability: %.2f mmag" % (band, rep))
 
  462         superStarChebSize = fgcmFitCycle.fgcmZpts.zpStruct[
'FGCM_FZPT_SSTAR_CHEB'].shape[1]
 
  463         zptChebSize = fgcmFitCycle.fgcmZpts.zpStruct[
'FGCM_FZPT_CHEB'].shape[1]
 
  466         zptCat = 
makeZptCat(zptSchema, fgcmFitCycle.fgcmZpts.zpStruct)
 
  469         atmCat = 
makeAtmCat(atmSchema, fgcmFitCycle.fgcmZpts.atmStruct)
 
  471         stdStruct, goodBands = fgcmFitCycle.fgcmStars.retrieveStdStarCatalog(fgcmFitCycle.fgcmPars)
 
  473         stdCat = 
makeStdCat(stdSchema, stdStruct, goodBands)
 
  475         outStruct = self.fgcmOutputProducts.generateTractOutputProducts(butler, tract,
 
  477                                                                         zptCat, atmCat, stdCat,
 
  478                                                                         self.config.fgcmBuildStars,
 
  479                                                                         self.config.fgcmFitCycle)
 
  480         outStruct.repeatability = fgcmFitCycle.fgcmPars.compReservedRawRepeatability