Coverage for python/lsst/fgcmcal/fgcmCalibrateTractBase.py: 16%

Shortcuts 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

223 statements  

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 

34from lsst.utils.timer import timeMethod 

35 

36from .fgcmBuildStars import FgcmBuildStarsTask, FgcmBuildStarsConfig 

37from .fgcmFitCycle import FgcmFitCycleConfig 

38from .fgcmOutputProducts import FgcmOutputProductsTask 

39from .utilities import makeConfigDict, translateFgcmLut, translateVisitCatalog 

40from .utilities import computeApertureRadiusFromDataRef, extractReferenceMags 

41from .utilities import makeZptSchema, makeZptCat 

42from .utilities import makeAtmSchema, makeAtmCat 

43from .utilities import makeStdSchema, makeStdCat 

44from .focalPlaneProjector import FocalPlaneProjector 

45 

46import fgcm 

47 

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

49 

50 

51class FgcmCalibrateTractConfigBase(pexConfig.Config): 

52 """Config for FgcmCalibrateTract""" 

53 

54 fgcmBuildStars = pexConfig.ConfigurableField( 

55 target=FgcmBuildStarsTask, 

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

57 ) 

58 fgcmFitCycle = pexConfig.ConfigField( 

59 dtype=FgcmFitCycleConfig, 

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

61 ) 

62 fgcmOutputProducts = pexConfig.ConfigurableField( 

63 target=FgcmOutputProductsTask, 

64 doc="Task to output fgcm products", 

65 ) 

66 convergenceTolerance = pexConfig.Field( 

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

68 dtype=float, 

69 default=0.005, 

70 ) 

71 maxFitCycles = pexConfig.Field( 

72 doc="Maximum number of fit cycles", 

73 dtype=int, 

74 default=5, 

75 ) 

76 doDebuggingPlots = pexConfig.Field( 

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

78 dtype=bool, 

79 default=False, 

80 ) 

81 

82 def setDefaults(self): 

83 pexConfig.Config.setDefaults(self) 

84 

85 self.fgcmFitCycle.quietMode = True 

86 self.fgcmFitCycle.doPlots = False 

87 self.fgcmOutputProducts.doReferenceCalibration = False 

88 self.fgcmOutputProducts.doRefcatOutput = False 

89 self.fgcmOutputProducts.cycleNumber = 0 

90 self.fgcmOutputProducts.photoCal.applyColorTerms = False 

91 

92 def validate(self): 

93 super().validate() 

94 

95 for band in self.fgcmFitCycle.bands: 

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

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

98 raise pexConfig.FieldValidationError(FgcmFitCycleConfig.useRepeatabilityForExpGrayCutsDict, 

99 self, msg) 

100 

101 

102class FgcmCalibrateTractRunner(pipeBase.ButlerInitializedTaskRunner): 

103 """Subclass of TaskRunner for FgcmCalibrateTractTask 

104 

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. 

112 """ 

113 

114 @staticmethod 

115 def getTargetList(parsedCmd): 

116 """ 

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

118 list of dataRefs. 

119 """ 

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

121 

122 def __call__(self, args): 

123 """ 

124 Parameters 

125 ---------- 

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

127 

128 Returns 

129 ------- 

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

131 exitStatus (0: success; 1: failure) 

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

133 """ 

134 butler, dataRefList = args 

135 

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

137 

138 exitStatus = 0 

139 if self.doRaise: 

140 results = task.runDataRef(butler, dataRefList) 

141 else: 

142 try: 

143 results = task.runDataRef(butler, dataRefList) 

144 except Exception as e: 

145 exitStatus = 1 

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

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

148 traceback.print_exc(file=sys.stderr) 

149 

150 task.writeMetadata(butler) 

151 

152 if self.doReturnResults: 

153 return [pipeBase.Struct(exitStatus=exitStatus, 

154 results=results)] 

155 else: 

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

157 

158 def run(self, parsedCmd): 

159 """ 

160 Run the task, with no multiprocessing 

161 

162 Parameters 

163 ---------- 

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

165 """ 

166 

167 resultList = [] 

168 

169 if self.precall(parsedCmd): 

170 targetList = self.getTargetList(parsedCmd) 

171 resultList = self(targetList[0]) 

172 

173 return resultList 

174 

175 

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

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

178 """ 

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

180 """ 

181 Instantiate an `FgcmCalibrateTractTask`. 

182 

183 Parameters 

184 ---------- 

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

186 """ 

187 super().__init__(**kwargs) 

188 self.makeSubtask("fgcmBuildStars", initInputs=initInputs, butler=butler) 

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

190 

191 # no saving of metadata for now 

192 def _getMetadataName(self): 

193 return None 

194 

195 @timeMethod 

196 def runDataRef(self, butler, dataRefs): 

197 """ 

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

199 fitting multiple cycles, and making outputs. 

200 

201 Parameters 

202 ---------- 

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

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

205 Data references for the input visits. 

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

207 references. 

208 

209 Raises 

210 ------ 

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

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

213 doSubtractLocalBackground is True and aperture radius cannot be 

214 determined. 

215 """ 

216 datasetType = dataRefs[0].butlerSubset.datasetType 

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

218 

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

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

221 

222 if not self.config.fgcmBuildStars.doReferenceMatches: 

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

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

225 if self.config.fgcmBuildStars.checkAllCcds: 

226 raise RuntimeError("Cannot run FgcmCalibrateTract with " 

227 "fgcmBuildStars.checkAllCcds set to True") 

228 

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

230 camera = butler.get('camera') 

231 

232 dataRefDict = {} 

233 dataRefDict['camera'] = camera 

234 dataRefDict['source_catalogs'] = dataRefs 

235 dataRefDict['sourceSchema'] = butler.get('src_schema', immediate=True).schema 

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

237 

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

239 

240 visitDataRefName = self.config.fgcmBuildStars.visitDataRefName 

241 ccdDataRefName = self.config.fgcmBuildStars.ccdDataRefName 

242 

243 if struct.photoCalibs is not None: 

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

245 

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

247 butler.put(photoCalib, 'fgcm_tract_photoCalib', 

248 dataId={visitDataRefName: visit, 

249 ccdDataRefName: detector, 

250 'filter': physicalFilter, 

251 'tract': tract}) 

252 

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

254 

255 if struct.atmospheres is not None: 

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

257 for visit, atm in struct.atmospheres: 

258 butler.put(atm, "transmission_atmosphere_fgcm_tract", 

259 dataId={visitDataRefName: visit, 

260 'tract': tract}) 

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

262 

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

264 

265 def run(self, dataRefDict, tract, 

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

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

268 

269 Parameters 

270 ---------- 

271 dataRefDict : `dict` 

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

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

274 dataRef dictionary with the following keys. Note that all 

275 keys need not be set based on config parameters. 

276 

277 ``"camera"`` 

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

279 ``"source_catalogs"`` 

280 `list` of dataRefs for input source catalogs. 

281 ``"sourceSchema"`` 

282 Schema for the source catalogs. 

283 ``"fgcmLookUpTable"`` 

284 dataRef for the FGCM look-up table. 

285 ``"calexps"`` 

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

287 ``"fgcmPhotoCalibs"`` 

288 `dict` of output photoCalib dataRefs. Key is 

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

290 Present if doZeropointOutput is True. 

291 ``"fgcmTransmissionAtmospheres"`` 

292 `dict` of output atmosphere transmission dataRefs. 

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

294 Present if doAtmosphereOutput is True. 

295 tract : `int` 

296 Tract number 

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

298 Reference object loader object for fgcmBuildStars. 

299 returnCatalogs : `bool`, optional 

300 Return photoCalibs as per-visit exposure catalogs. 

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

302 Gen2 butler used for reference star outputs 

303 

304 Returns 

305 ------- 

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

307 Output structure with keys: 

308 

309 offsets : `np.ndarray` 

310 Final reference offsets, per band. 

311 repeatability : `np.ndarray` 

312 Raw fgcm repeatability for bright stars, per band. 

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

314 Generator that returns (visit, transmissionCurve) tuples. 

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

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

317 (returned if returnCatalogs is False). 

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

319 Generator that returns (visit, exposureCatalog) tuples. 

320 (returned if returnCatalogs is True). 

321 """ 

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

323 

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

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

326 calibFluxApertureRadius = None 

327 if self.config.fgcmBuildStars.doSubtractLocalBackground: 

328 try: 

329 field = self.config.fgcmBuildStars.instFluxField 

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

331 field) 

332 except RuntimeError: 

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

334 "Cannot use doSubtractLocalBackground." % 

335 (field)) 

336 

337 # Run the build stars tasks 

338 

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

340 if isinstance(butler, dafPersist.Butler): 

341 # Gen2 

342 groupedDataRefs = self.fgcmBuildStars._findAndGroupDataRefsGen2(butler, dataRefDict['camera'], 

343 dataRefDict['source_catalogs']) 

344 else: 

345 # Gen3 

346 groupedDataRefs = self.fgcmBuildStars._groupDataRefs(dataRefDict['sourceTableDataRefDict'], 

347 dataRefDict['visitSummaryDataRefDict']) 

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, lutIndexVals[0]['FILTERNAMES'], 

383 tract=tract) 

384 

385 focalPlaneProjector = FocalPlaneProjector(dataRefDict['camera'], 

386 self.config.fgcmFitCycle.defaultCameraOrientation) 

387 

388 # Set up the fit cycle task 

389 

390 noFitsDict = {'lutIndex': lutIndexVals, 

391 'lutStd': lutStd, 

392 'expInfo': fgcmExpInfo, 

393 'focalPlaneProjector': focalPlaneProjector} 

394 

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

396 noFitsDict=noFitsDict, noOutput=True) 

397 

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

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

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

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

402 

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

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

405 fgcmLut, 

406 fgcmExpInfo) 

407 

408 # Match star observations to visits 

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

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

411 

412 obsIndex = fgcmStarIndicesCat['obsIndex'] 

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

414 fgcmStarObservationCat['visit'][obsIndex]) 

415 

416 refMag, refMagErr = extractReferenceMags(fgcmRefCat, 

417 self.config.fgcmFitCycle.bands, 

418 self.config.fgcmFitCycle.physicalFilterMap) 

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

420 

421 fgcmStars = fgcm.FgcmStars(fgcmFitCycle.fgcmConfig) 

422 fgcmStars.loadStars(fgcmPars, 

423 fgcmStarObservationCat['visit'][obsIndex], 

424 fgcmStarObservationCat['ccd'][obsIndex], 

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

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

427 fgcmStarObservationCat['instMag'][obsIndex], 

428 fgcmStarObservationCat['instMagErr'][obsIndex], 

429 fgcmExpInfo['FILTERNAME'][visitIndex], 

430 fgcmStarIdCat['fgcm_id'][:], 

431 fgcmStarIdCat['ra'][:], 

432 fgcmStarIdCat['dec'][:], 

433 fgcmStarIdCat['obsArrIndex'][:], 

434 fgcmStarIdCat['nObs'][:], 

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

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

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

438 obsDeltaAper=fgcmStarObservationCat['deltaMagAper'][obsIndex], 

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

440 refID=refId, 

441 refMag=refMag, 

442 refMagErr=refMagErr, 

443 flagID=None, 

444 flagFlag=None, 

445 computeNobs=True) 

446 

447 # Clear out some memory 

448 del fgcmStarIdCat 

449 del fgcmStarIndicesCat 

450 del fgcmRefCat 

451 

452 fgcmFitCycle.setLUT(fgcmLut) 

453 fgcmFitCycle.setStars(fgcmStars, fgcmPars) 

454 

455 converged = False 

456 cycleNumber = 0 

457 

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

459 previousParInfo = None 

460 previousParams = None 

461 previousSuperStar = None 

462 

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

464 

465 fgcmFitCycle.fgcmConfig.updateCycleNumber(cycleNumber) 

466 

467 if cycleNumber > 0: 

468 # Use parameters from previous cycle 

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

470 fgcmExpInfo, 

471 previousParInfo, 

472 previousParams, 

473 previousSuperStar) 

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

475 # cycle 

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

477 fgcmStarObservationCat['instMagErr'][obsIndex]) 

478 fgcmFitCycle.initialCycle = False 

479 

480 fgcmFitCycle.setPars(fgcmPars) 

481 fgcmFitCycle.finishSetup() 

482 

483 fgcmFitCycle.run() 

484 

485 # Grab the parameters for the next cycle 

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

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

488 

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

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

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

492 continue 

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

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

495 

496 # Check for convergence 

497 if np.alltrue((previousReservedRawRepeatability 

498 - fgcmFitCycle.fgcmPars.compReservedRawRepeatability) 

499 < self.config.convergenceTolerance): 

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

501 converged = True 

502 else: 

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

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

505 fgcmFitCycle.fgcmConfig.precomputeSuperStarInitialCycle = False 

506 fgcmFitCycle.fgcmConfig.freezeStdAtmosphere = False 

507 previousReservedRawRepeatability[:] = fgcmFitCycle.fgcmPars.compReservedRawRepeatability 

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

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

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

511 continue 

512 cut = fgcmFitCycle.updatedPhotometricCut[i] * 1000.0 

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

514 

515 cycleNumber += 1 

516 

517 # Log warning if not converged 

518 if not converged: 

519 self.log.warning("Maximum number of fit cycles exceeded (%d) without convergence.", cycleNumber) 

520 

521 # Do final clean-up iteration 

522 fgcmFitCycle.fgcmConfig.freezeStdAtmosphere = False 

523 fgcmFitCycle.fgcmConfig.resetParameters = False 

524 fgcmFitCycle.fgcmConfig.maxIter = 0 

525 fgcmFitCycle.fgcmConfig.outputZeropoints = True 

526 fgcmFitCycle.fgcmConfig.outputStandards = True 

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

528 fgcmFitCycle.fgcmConfig.updateCycleNumber(cycleNumber) 

529 fgcmFitCycle.initialCycle = False 

530 

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

532 fgcmExpInfo, 

533 previousParInfo, 

534 previousParams, 

535 previousSuperStar) 

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

537 fgcmStarObservationCat['instMagErr'][obsIndex]) 

538 fgcmFitCycle.setPars(fgcmPars) 

539 fgcmFitCycle.finishSetup() 

540 

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

542 fgcmFitCycle.run() 

543 

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

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

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

547 continue 

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

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

550 

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

552 

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

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

555 

556 zptSchema = makeZptSchema(superStarChebSize, zptChebSize) 

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

558 

559 atmSchema = makeAtmSchema() 

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

561 

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

563 stdSchema = makeStdSchema(len(goodBands)) 

564 stdCat = makeStdCat(stdSchema, stdStruct, goodBands) 

565 

566 outStruct = self.fgcmOutputProducts.generateTractOutputProducts(dataRefDict, 

567 tract, 

568 visitCat, 

569 zptCat, atmCat, stdCat, 

570 self.config.fgcmBuildStars, 

571 returnCatalogs=returnCatalogs, 

572 butler=butler) 

573 

574 outStruct.repeatability = fgcmFitCycle.fgcmPars.compReservedRawRepeatability 

575 

576 fgcmFitCycle.freeSharedMemory() 

577 

578 return outStruct