Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of fgcmcal. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21"""Base class for running fgcmcal on a single tract using src tables 

22or sourceTable_visit tables. 

23""" 

24 

25import sys 

26import traceback 

27import abc 

28 

29import numpy as np 

30 

31import lsst.daf.persistence as dafPersist 

32import lsst.pex.config as pexConfig 

33import lsst.pipe.base as pipeBase 

34 

35from .fgcmBuildStars import FgcmBuildStarsTask, FgcmBuildStarsConfig 

36from .fgcmFitCycle import FgcmFitCycleConfig 

37from .fgcmOutputProducts import FgcmOutputProductsTask 

38from .utilities import makeConfigDict, translateFgcmLut, translateVisitCatalog 

39from .utilities import computeCcdOffsets, computeApertureRadiusFromDataRef, extractReferenceMags 

40from .utilities import makeZptSchema, makeZptCat 

41from .utilities import makeAtmSchema, makeAtmCat 

42from .utilities import makeStdSchema, makeStdCat 

43 

44import fgcm 

45 

46__all__ = ['FgcmCalibrateTractConfigBase', 'FgcmCalibrateTractBaseTask', 'FgcmCalibrateTractRunner'] 

47 

48 

49class FgcmCalibrateTractConfigBase(pexConfig.Config): 

50 """Config for FgcmCalibrateTract""" 

51 

52 fgcmBuildStars = pexConfig.ConfigurableField( 

53 target=FgcmBuildStarsTask, 

54 doc="Task to load and match stars for fgcm", 

55 ) 

56 fgcmFitCycle = pexConfig.ConfigField( 

57 dtype=FgcmFitCycleConfig, 

58 doc="Config to run a single fgcm fit cycle", 

59 ) 

60 fgcmOutputProducts = pexConfig.ConfigurableField( 

61 target=FgcmOutputProductsTask, 

62 doc="Task to output fgcm products", 

63 ) 

64 convergenceTolerance = pexConfig.Field( 

65 doc="Tolerance on repeatability convergence (per band)", 

66 dtype=float, 

67 default=0.005, 

68 ) 

69 maxFitCycles = pexConfig.Field( 

70 doc="Maximum number of fit cycles", 

71 dtype=int, 

72 default=5, 

73 ) 

74 doDebuggingPlots = pexConfig.Field( 

75 doc="Make plots for debugging purposes?", 

76 dtype=bool, 

77 default=False, 

78 ) 

79 

80 def setDefaults(self): 

81 pexConfig.Config.setDefaults(self) 

82 

83 self.fgcmFitCycle.quietMode = True 

84 self.fgcmOutputProducts.doReferenceCalibration = False 

85 self.fgcmOutputProducts.doRefcatOutput = False 

86 self.fgcmOutputProducts.cycleNumber = 0 

87 self.fgcmOutputProducts.photoCal.applyColorTerms = False 

88 

89 def validate(self): 

90 super().validate() 

91 

92 for band in self.fgcmFitCycle.bands: 

93 if not self.fgcmFitCycle.useRepeatabilityForExpGrayCutsDict[band]: 

94 msg = 'Must set useRepeatabilityForExpGrayCutsDict[band]=True for all bands' 

95 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.useRepeatabilityForExpGrayCutsDict, 

96 self, msg) 

97 

98 

99class FgcmCalibrateTractRunner(pipeBase.ButlerInitializedTaskRunner): 

100 """Subclass of TaskRunner for FgcmCalibrateTractTask 

101 

102 fgcmCalibrateTractTask.run() takes a number of arguments, one of which is 

103 the butler (for persistence and mapper data), and a list of dataRefs 

104 extracted from the command line. This task runs on a constrained set 

105 of dataRefs, typically a single tract. 

106 This class transforms the process arguments generated by the ArgumentParser 

107 into the arguments expected by FgcmCalibrateTractTask.run(). 

108 This runner does not use any parallelization. 

109 """ 

110 

111 @staticmethod 

112 def getTargetList(parsedCmd): 

113 """ 

114 Return a list with one element: a tuple with the butler and 

115 list of dataRefs. 

116 """ 

117 return [(parsedCmd.butler, parsedCmd.id.refList)] 

118 

119 def __call__(self, args): 

120 """ 

121 Parameters 

122 ---------- 

123 args: `tuple` with (butler, dataRefList) 

124 

125 Returns 

126 ------- 

127 exitStatus: `list` with `lsst.pipe.base.Struct` 

128 exitStatus (0: success; 1: failure) 

129 May also contain results if `self.doReturnResults` is `True`. 

130 """ 

131 butler, dataRefList = args 

132 

133 task = self.TaskClass(config=self.config, log=self.log) 

134 

135 exitStatus = 0 

136 if self.doRaise: 

137 results = task.runDataRef(butler, dataRefList) 

138 else: 

139 try: 

140 results = task.runDataRef(butler, dataRefList) 

141 except Exception as e: 

142 exitStatus = 1 

143 task.log.fatal("Failed: %s" % e) 

144 if not isinstance(e, pipeBase.TaskError): 

145 traceback.print_exc(file=sys.stderr) 

146 

147 task.writeMetadata(butler) 

148 

149 if self.doReturnResults: 

150 return [pipeBase.Struct(exitStatus=exitStatus, 

151 results=results)] 

152 else: 

153 return [pipeBase.Struct(exitStatus=exitStatus)] 

154 

155 def run(self, parsedCmd): 

156 """ 

157 Run the task, with no multiprocessing 

158 

159 Parameters 

160 ---------- 

161 parsedCmd: `lsst.pipe.base.ArgumentParser` parsed command line 

162 """ 

163 

164 resultList = [] 

165 

166 if self.precall(parsedCmd): 

167 targetList = self.getTargetList(parsedCmd) 

168 resultList = self(targetList[0]) 

169 

170 return resultList 

171 

172 

173class FgcmCalibrateTractBaseTask(pipeBase.PipelineTask, pipeBase.CmdLineTask, abc.ABC): 

174 """Base class to calibrate a single tract using fgcmcal 

175 """ 

176 def __init__(self, butler=None, **kwargs): 

177 """ 

178 Instantiate an `FgcmCalibrateTractTask`. 

179 

180 Parameters 

181 ---------- 

182 butler : `lsst.daf.persistence.Butler`, optional 

183 """ 

184 super().__init__(**kwargs) 

185 self.makeSubtask("fgcmBuildStars", butler=butler) 

186 self.makeSubtask("fgcmOutputProducts", butler=butler) 

187 

188 # no saving of metadata for now 

189 def _getMetadataName(self): 

190 return None 

191 

192 @pipeBase.timeMethod 

193 def runDataRef(self, butler, dataRefs): 

194 """ 

195 Run full FGCM calibration on a single tract, including building star list, 

196 fitting multiple cycles, and making outputs. 

197 

198 Parameters 

199 ---------- 

200 butler: `lsst.daf.persistence.Butler` 

201 dataRefs: `list` of `lsst.daf.persistence.ButlerDataRef` 

202 Data references for the input visits. 

203 These may be either per-ccd "src" or per-visit"sourceTable_visit" 

204 references. 

205 

206 Raises 

207 ------ 

208 RuntimeError: Raised if `config.fgcmBuildStars.doReferenceMatches` is 

209 not True, or if fgcmLookUpTable is not available, or if 

210 doSubtractLocalBackground is True and aperture radius cannot be 

211 determined. 

212 """ 

213 datasetType = dataRefs[0].butlerSubset.datasetType 

214 self.log.info("Running with %d %s dataRefs" % (len(dataRefs), datasetType)) 

215 

216 if not butler.datasetExists('fgcmLookUpTable'): 

217 raise RuntimeError("Must run FgcmCalibrateTract with an fgcmLookUpTable") 

218 

219 if not self.config.fgcmBuildStars.doReferenceMatches: 

220 raise RuntimeError("Must run FgcmCalibrateTract with fgcmBuildStars.doReferenceMatches") 

221 if isinstance(self.config.fgcmBuildStars, FgcmBuildStarsConfig): 

222 if self.config.fgcmBuildStars.checkAllCcds: 

223 raise RuntimeError("Cannot run FgcmCalibrateTract with " 

224 "fgcmBuildStars.checkAllCcds set to True") 

225 

226 tract = int(dataRefs[0].dataId['tract']) 

227 camera = butler.get('camera') 

228 

229 dataRefDict = {} 

230 dataRefDict['camera'] = camera 

231 dataRefDict['source_catalogs'] = dataRefs 

232 dataRefDict['sourceSchema'] = butler.dataRef('src_schema') 

233 dataRefDict['fgcmLookUpTable'] = butler.dataRef('fgcmLookUpTable') 

234 

235 struct = self.run(dataRefDict, tract, butler=butler, returnCatalogs=False) 

236 

237 visitDataRefName = self.config.fgcmBuildStars.visitDataRefName 

238 ccdDataRefName = self.config.fgcmBuildStars.ccdDataRefName 

239 

240 if struct.photoCalibs is not None: 

241 self.log.info("Outputting photoCalib files.") 

242 

243 for visit, detector, physicalFilter, photoCalib in struct.photoCalibs: 

244 butler.put(photoCalib, 'fgcm_tract_photoCalib', 

245 dataId={visitDataRefName: visit, 

246 ccdDataRefName: detector, 

247 'filter': physicalFilter, 

248 'tract': tract}) 

249 

250 self.log.info("Done outputting photoCalib files.") 

251 

252 if struct.atmospheres is not None: 

253 self.log.info("Outputting atmosphere files.") 

254 for visit, atm in struct.atmospheres: 

255 butler.put(atm, "transmission_atmosphere_fgcm_tract", 

256 dataId={visitDataRefName: visit, 

257 'tract': tract}) 

258 self.log.info("Done outputting atmosphere transmissions.") 

259 

260 return pipeBase.Struct(repeatability=struct.repeatability) 

261 

262 def run(self, dataRefDict, tract, 

263 buildStarsRefObjLoader=None, returnCatalogs=True, butler=None): 

264 """Run the calibrations for a single tract with fgcm. 

265 

266 Parameters 

267 ---------- 

268 dataRefDict : `dict` 

269 All dataRefs are `lsst.daf.persistence.ButlerDataRef` (gen2) or 

270 `lsst.daf.butler.DeferredDatasetHandle` (gen3) 

271 dataRef dictionary with the following keys. Note that all 

272 keys need not be set based on config parameters. 

273 

274 ``"camera"`` 

275 Camera object (`lsst.afw.cameraGeom.Camera`) 

276 ``"source_catalogs"`` 

277 `list` of dataRefs for input source catalogs. 

278 ``"sourceSchema"`` 

279 Schema for the source catalogs. 

280 ``"fgcmLookUpTable"`` 

281 dataRef for the FGCM look-up table. 

282 ``"calexps"`` 

283 `list` of dataRefs for the input calexps (Gen3 only) 

284 ``"fgcmPhotoCalibs"`` 

285 `dict` of output photoCalib dataRefs. Key is 

286 (tract, visit, detector). (Gen3 only) 

287 Present if doZeropointOutput is True. 

288 ``"fgcmTransmissionAtmospheres"`` 

289 `dict` of output atmosphere transmission dataRefs. 

290 Key is (tract, visit). (Gen3 only) 

291 Present if doAtmosphereOutput is True. 

292 tract : `int` 

293 Tract number 

294 buildStarsRefObjLoader : `lsst.meas.algorithms.ReferenceObjectLoader`, optional 

295 Reference object loader object for fgcmBuildStars. 

296 returnCatalogs : `bool`, optional 

297 Return photoCalibs as per-visit exposure catalogs. 

298 butler : `lsst.daf.persistence.Butler`, optional 

299 Gen2 butler used for reference star outputs 

300 

301 Returns 

302 ------- 

303 outstruct : `lsst.pipe.base.Struct` 

304 Output structure with keys: 

305 

306 offsets : `np.ndarray` 

307 Final reference offsets, per band. 

308 repeatability : `np.ndarray` 

309 Raw fgcm repeatability for bright stars, per band. 

310 atmospheres : `generator` [(`int`, `lsst.afw.image.TransmissionCurve`)] 

311 Generator that returns (visit, transmissionCurve) tuples. 

312 photoCalibs : `generator` [(`int`, `int`, `str`, `lsst.afw.image.PhotoCalib`)] 

313 Generator that returns (visit, ccd, filtername, photoCalib) tuples. 

314 (returned if returnCatalogs is False). 

315 photoCalibCatalogs : `generator` [(`int`, `lsst.afw.table.ExposureCatalog`)] 

316 Generator that returns (visit, exposureCatalog) tuples. 

317 (returned if returnCatalogs is True). 

318 """ 

319 self.log.info("Running on tract %d", (tract)) 

320 

321 # Compute the aperture radius if necessary. This is useful to do now before 

322 # any heavy lifting has happened (fail early). 

323 calibFluxApertureRadius = None 

324 if self.config.fgcmBuildStars.doSubtractLocalBackground: 

325 try: 

326 field = self.config.fgcmBuildStars.instFluxField 

327 calibFluxApertureRadius = computeApertureRadiusFromDataRef(dataRefDict['source_catalogs'][0], 

328 field) 

329 except RuntimeError: 

330 raise RuntimeError("Could not determine aperture radius from %s. " 

331 "Cannot use doSubtractLocalBackground." % 

332 (field)) 

333 

334 # Run the build stars tasks 

335 

336 # Note that we will need visitCat at the end of the procedure for the outputs 

337 if isinstance(butler, dafPersist.Butler): 

338 # Gen2 

339 groupedDataRefs = self.fgcmBuildStars._findAndGroupDataRefs(dataRefDict['camera'], 

340 dataRefDict['source_catalogs'], 

341 butler=butler) 

342 else: 

343 # Gen3 

344 cdrd = dataRefDict['calexps'] 

345 groupedDataRefs = self.fgcmBuildStars._findAndGroupDataRefs(dataRefDict['camera'], 

346 dataRefDict['source_catalogs'], 

347 calexpDataRefDict=cdrd) 

348 visitCat = self.fgcmBuildStars.fgcmMakeVisitCatalog(dataRefDict['camera'], groupedDataRefs) 

349 rad = calibFluxApertureRadius 

350 fgcmStarObservationCat = self.fgcmBuildStars.fgcmMakeAllStarObservations(groupedDataRefs, 

351 visitCat, 

352 dataRefDict['sourceSchema'], 

353 dataRefDict['camera'], 

354 calibFluxApertureRadius=rad) 

355 

356 if self.fgcmBuildStars.config.doReferenceMatches: 

357 lutDataRef = dataRefDict['fgcmLookUpTable'] 

358 if buildStarsRefObjLoader is not None: 

359 self.fgcmBuildStars.makeSubtask("fgcmLoadReferenceCatalog", 

360 refObjLoader=buildStarsRefObjLoader) 

361 else: 

362 self.fgcmBuildStars.makeSubtask("fgcmLoadReferenceCatalog", butler=butler) 

363 else: 

364 lutDataRef = None 

365 

366 fgcmStarIdCat, fgcmStarIndicesCat, fgcmRefCat = \ 

367 self.fgcmBuildStars.fgcmMatchStars(visitCat, 

368 fgcmStarObservationCat, 

369 lutDataRef=lutDataRef) 

370 

371 # Load the LUT 

372 lutCat = dataRefDict['fgcmLookUpTable'].get() 

373 fgcmLut, lutIndexVals, lutStd = translateFgcmLut(lutCat, 

374 dict(self.config.fgcmFitCycle.physicalFilterMap)) 

375 del lutCat 

376 

377 # Translate the visit catalog into fgcm format 

378 fgcmExpInfo = translateVisitCatalog(visitCat) 

379 

380 configDict = makeConfigDict(self.config.fgcmFitCycle, self.log, dataRefDict['camera'], 

381 self.config.fgcmFitCycle.maxIterBeforeFinalCycle, 

382 True, False, tract=tract) 

383 # Turn off plotting in tract mode 

384 configDict['doPlots'] = False 

385 

386 # Use the first orientation. 

387 # TODO: DM-21215 will generalize to arbitrary camera orientations 

388 ccdOffsets = computeCcdOffsets(dataRefDict['camera'], fgcmExpInfo['TELROT'][0]) 

389 

390 # Set up the fit cycle task 

391 

392 noFitsDict = {'lutIndex': lutIndexVals, 

393 'lutStd': lutStd, 

394 'expInfo': fgcmExpInfo, 

395 'ccdOffsets': ccdOffsets} 

396 

397 fgcmFitCycle = fgcm.FgcmFitCycle(configDict, useFits=False, 

398 noFitsDict=noFitsDict, noOutput=True) 

399 

400 # We determine the conversion from the native units (typically radians) to 

401 # degrees for the first star. This allows us to treat coord_ra/coord_dec as 

402 # numpy arrays rather than Angles, which would we approximately 600x slower. 

403 conv = fgcmStarObservationCat[0]['ra'].asDegrees() / float(fgcmStarObservationCat[0]['ra']) 

404 

405 # To load the stars, we need an initial parameter object 

406 fgcmPars = fgcm.FgcmParameters.newParsWithArrays(fgcmFitCycle.fgcmConfig, 

407 fgcmLut, 

408 fgcmExpInfo) 

409 

410 # Match star observations to visits 

411 # Only those star observations that match visits from fgcmExpInfo['VISIT'] will 

412 # actually be transferred into fgcm using the indexing below. 

413 

414 obsIndex = fgcmStarIndicesCat['obsIndex'] 

415 visitIndex = np.searchsorted(fgcmExpInfo['VISIT'], 

416 fgcmStarObservationCat['visit'][obsIndex]) 

417 

418 refMag, refMagErr = extractReferenceMags(fgcmRefCat, 

419 self.config.fgcmFitCycle.bands, 

420 self.config.fgcmFitCycle.physicalFilterMap) 

421 refId = fgcmRefCat['fgcm_id'][:] 

422 

423 fgcmStars = fgcm.FgcmStars(fgcmFitCycle.fgcmConfig) 

424 fgcmStars.loadStars(fgcmPars, 

425 fgcmStarObservationCat['visit'][obsIndex], 

426 fgcmStarObservationCat['ccd'][obsIndex], 

427 fgcmStarObservationCat['ra'][obsIndex] * conv, 

428 fgcmStarObservationCat['dec'][obsIndex] * conv, 

429 fgcmStarObservationCat['instMag'][obsIndex], 

430 fgcmStarObservationCat['instMagErr'][obsIndex], 

431 fgcmExpInfo['FILTERNAME'][visitIndex], 

432 fgcmStarIdCat['fgcm_id'][:], 

433 fgcmStarIdCat['ra'][:], 

434 fgcmStarIdCat['dec'][:], 

435 fgcmStarIdCat['obsArrIndex'][:], 

436 fgcmStarIdCat['nObs'][:], 

437 obsX=fgcmStarObservationCat['x'][obsIndex], 

438 obsY=fgcmStarObservationCat['y'][obsIndex], 

439 obsDeltaMagBkg=fgcmStarObservationCat['deltaMagBkg'][obsIndex], 

440 psfCandidate=fgcmStarObservationCat['psf_candidate'][obsIndex], 

441 refID=refId, 

442 refMag=refMag, 

443 refMagErr=refMagErr, 

444 flagID=None, 

445 flagFlag=None, 

446 computeNobs=True) 

447 

448 # Clear out some memory 

449 del fgcmStarIdCat 

450 del fgcmStarIndicesCat 

451 del fgcmRefCat 

452 

453 fgcmFitCycle.setLUT(fgcmLut) 

454 fgcmFitCycle.setStars(fgcmStars, fgcmPars) 

455 

456 converged = False 

457 cycleNumber = 0 

458 

459 previousReservedRawRepeatability = np.zeros(fgcmPars.nBands) + 1000.0 

460 previousParInfo = None 

461 previousParams = None 

462 previousSuperStar = None 

463 

464 while (not converged and cycleNumber < self.config.maxFitCycles): 

465 

466 fgcmFitCycle.fgcmConfig.updateCycleNumber(cycleNumber) 

467 

468 if cycleNumber > 0: 

469 # Use parameters from previous cycle 

470 fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(fgcmFitCycle.fgcmConfig, 

471 fgcmExpInfo, 

472 previousParInfo, 

473 previousParams, 

474 previousSuperStar) 

475 # We need to reset the star magnitudes and errors for the next 

476 # cycle 

477 fgcmFitCycle.fgcmStars.reloadStarMagnitudes(fgcmStarObservationCat['instMag'][obsIndex], 

478 fgcmStarObservationCat['instMagErr'][obsIndex]) 

479 fgcmFitCycle.initialCycle = False 

480 

481 fgcmFitCycle.setPars(fgcmPars) 

482 fgcmFitCycle.finishSetup() 

483 

484 fgcmFitCycle.run() 

485 

486 # Grab the parameters for the next cycle 

487 previousParInfo, previousParams = fgcmFitCycle.fgcmPars.parsToArrays() 

488 previousSuperStar = fgcmFitCycle.fgcmPars.parSuperStarFlat.copy() 

489 

490 self.log.info("Raw repeatability after cycle number %d is:" % (cycleNumber)) 

491 for i, band in enumerate(fgcmFitCycle.fgcmPars.bands): 

492 if not fgcmFitCycle.fgcmPars.hasExposuresInBand[i]: 

493 continue 

494 rep = fgcmFitCycle.fgcmPars.compReservedRawRepeatability[i] * 1000.0 

495 self.log.info(" Band %s, repeatability: %.2f mmag" % (band, rep)) 

496 

497 # Check for convergence 

498 if np.alltrue((previousReservedRawRepeatability 

499 - fgcmFitCycle.fgcmPars.compReservedRawRepeatability) 

500 < self.config.convergenceTolerance): 

501 self.log.info("Raw repeatability has converged after cycle number %d." % (cycleNumber)) 

502 converged = True 

503 else: 

504 fgcmFitCycle.fgcmConfig.expGrayPhotometricCut[:] = fgcmFitCycle.updatedPhotometricCut 

505 fgcmFitCycle.fgcmConfig.expGrayHighCut[:] = fgcmFitCycle.updatedHighCut 

506 fgcmFitCycle.fgcmConfig.precomputeSuperStarInitialCycle = False 

507 fgcmFitCycle.fgcmConfig.freezeStdAtmosphere = False 

508 previousReservedRawRepeatability[:] = fgcmFitCycle.fgcmPars.compReservedRawRepeatability 

509 self.log.info("Setting exposure gray photometricity cuts to:") 

510 for i, band in enumerate(fgcmFitCycle.fgcmPars.bands): 

511 if not fgcmFitCycle.fgcmPars.hasExposuresInBand[i]: 

512 continue 

513 cut = fgcmFitCycle.updatedPhotometricCut[i] * 1000.0 

514 self.log.info(" Band %s, photometricity cut: %.2f mmag" % (band, cut)) 

515 

516 cycleNumber += 1 

517 

518 # Log warning if not converged 

519 if not converged: 

520 self.log.warn("Maximum number of fit cycles exceeded (%d) without convergence." % (cycleNumber)) 

521 

522 # Do final clean-up iteration 

523 fgcmFitCycle.fgcmConfig.freezeStdAtmosphere = False 

524 fgcmFitCycle.fgcmConfig.resetParameters = False 

525 fgcmFitCycle.fgcmConfig.maxIter = 0 

526 fgcmFitCycle.fgcmConfig.outputZeropoints = True 

527 fgcmFitCycle.fgcmConfig.outputStandards = True 

528 fgcmFitCycle.fgcmConfig.doPlots = self.config.doDebuggingPlots 

529 fgcmFitCycle.fgcmConfig.updateCycleNumber(cycleNumber) 

530 fgcmFitCycle.initialCycle = False 

531 

532 fgcmPars = fgcm.FgcmParameters.loadParsWithArrays(fgcmFitCycle.fgcmConfig, 

533 fgcmExpInfo, 

534 previousParInfo, 

535 previousParams, 

536 previousSuperStar) 

537 fgcmFitCycle.fgcmStars.reloadStarMagnitudes(fgcmStarObservationCat['instMag'][obsIndex], 

538 fgcmStarObservationCat['instMagErr'][obsIndex]) 

539 fgcmFitCycle.setPars(fgcmPars) 

540 fgcmFitCycle.finishSetup() 

541 

542 self.log.info("Running final clean-up fit cycle...") 

543 fgcmFitCycle.run() 

544 

545 self.log.info("Raw repeatability after clean-up cycle is:") 

546 for i, band in enumerate(fgcmFitCycle.fgcmPars.bands): 

547 if not fgcmFitCycle.fgcmPars.hasExposuresInBand[i]: 

548 continue 

549 rep = fgcmFitCycle.fgcmPars.compReservedRawRepeatability[i] * 1000.0 

550 self.log.info(" Band %s, repeatability: %.2f mmag" % (band, rep)) 

551 

552 # Do the outputs. Need to keep track of tract. 

553 

554 superStarChebSize = fgcmFitCycle.fgcmZpts.zpStruct['FGCM_FZPT_SSTAR_CHEB'].shape[1] 

555 zptChebSize = fgcmFitCycle.fgcmZpts.zpStruct['FGCM_FZPT_CHEB'].shape[1] 

556 

557 zptSchema = makeZptSchema(superStarChebSize, zptChebSize) 

558 zptCat = makeZptCat(zptSchema, fgcmFitCycle.fgcmZpts.zpStruct) 

559 

560 atmSchema = makeAtmSchema() 

561 atmCat = makeAtmCat(atmSchema, fgcmFitCycle.fgcmZpts.atmStruct) 

562 

563 stdStruct, goodBands = fgcmFitCycle.fgcmStars.retrieveStdStarCatalog(fgcmFitCycle.fgcmPars) 

564 stdSchema = makeStdSchema(len(goodBands)) 

565 stdCat = makeStdCat(stdSchema, stdStruct, goodBands) 

566 

567 outStruct = self.fgcmOutputProducts.generateTractOutputProducts(dataRefDict, 

568 tract, 

569 visitCat, 

570 zptCat, atmCat, stdCat, 

571 self.config.fgcmBuildStars, 

572 returnCatalogs=returnCatalogs, 

573 butler=butler) 

574 

575 outStruct.repeatability = fgcmFitCycle.fgcmPars.compReservedRawRepeatability 

576 

577 return outStruct