Coverage for python/lsst/faro/utils/tex_table.py: 22%

165 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-07-19 05:43 -0700

1# This file is part of faro. 

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 

22import astropy.units as u 

23import numpy as np 

24import treecorr 

25 

26 

27__all__ = ( 

28 "TraceSize", 

29 "PsfTraceSizeDiff", 

30 "E1", 

31 "E2", 

32 "E1Resids", 

33 "E2Resids", 

34 "RhoStatistics", 

35 "corrSpin0", 

36 "corrSpin2", 

37 "calculateTEx", 

38) 

39 

40 

41class TraceSize(object): 

42 """Functor to calculate trace radius size for sources. ixxColumn and 

43 iyyColumn are strings and must be defined in the Task Config. 

44 """ 

45 

46 def __init__(self, ixxColumn, iyyColumn): 

47 self.ixxColumn = ixxColumn 

48 self.iyyColumn = iyyColumn 

49 

50 def __call__(self, catalog): 

51 srcSize = np.sqrt(0.5 * (catalog[self.ixxColumn] + catalog[self.iyyColumn])) 

52 return np.array(srcSize) 

53 

54 

55class PsfTraceSizeDiff(object): 

56 """Functor to calculate trace radius size difference (%) between object and 

57 PSF model. Inherits from functor TraceSize. 

58 """ 

59 

60 def __init__(self, ixxColumn, iyyColumn, ixxPsfColumn, iyyPsfColumn): 

61 self.ixxColumn = ixxColumn 

62 self.iyyColumn = iyyColumn 

63 self.ixxPsfColumn = ixxPsfColumn 

64 self.iyyPsfColumn = iyyPsfColumn 

65 

66 self.traceSizeFunc = TraceSize(self.ixxColumn, self.iyyColumn) 

67 self.psfTraceSizeFunc = TraceSize(self.ixxPsfColumn, self.iyyPsfColumn) 

68 

69 def __call__(self, catalog): 

70 srcSize = self.traceSizeFunc(catalog) 

71 psfSize = self.psfTraceSizeFunc(catalog) 

72 sizeDiff = 100 * (srcSize - psfSize) / (0.5 * (srcSize + psfSize)) 

73 return np.array(sizeDiff) 

74 

75 

76class E1(object): 

77 """Function to calculate e1 ellipticities from a given catalog. 

78 Parameters 

79 ---------- 

80 ixxColumn : `str` 

81 The name of the for corresponding ixx ellipticities column. 

82 iyyColumn : `str` 

83 The name of the for corresponding iyy ellipticities column. 

84 ixyColumn : `str` 

85 The name of the for corresponding ixy ellipticities column. 

86 unitScale : `float`, optional 

87 A numerical scaling factor to multiply the ellipticity. 

88 shearConvention: `bool`, optional 

89 Option to use shear convention. When set to False, the distortion 

90 convention is used. 

91 Returns 

92 ------- 

93 e1 : `numpy.array` 

94 A numpy array of e1 ellipticity values. 

95 """ 

96 

97 def __init__( 

98 self, ixxColumn, iyyColumn, ixyColumn, unitScale=1.0, shearConvention=False 

99 ): 

100 self.ixxColumn = ixxColumn 

101 self.iyyColumn = iyyColumn 

102 self.ixyColumn = ixyColumn 

103 self.unitScale = unitScale 

104 self.shearConvention = shearConvention 

105 

106 def __call__(self, catalog): 

107 xx = catalog[self.ixxColumn] 

108 yy = catalog[self.iyyColumn] 

109 if self.shearConvention: 

110 xy = catalog[self.ixyColumn] 

111 e1 = (xx - yy) / (xx + yy + 2.0 * np.sqrt(xx * yy - xy ** 2)) 

112 else: 

113 e1 = (xx - yy) / (xx + yy) 

114 return np.array(e1) * self.unitScale 

115 

116 

117class E2(object): 

118 """Function to calculate e2 ellipticities from a given catalog. 

119 Parameters 

120 ---------- 

121 ixxColumn : `str` 

122 The name of the for corresponding ixx ellipticities column. 

123 iyyColumn : `str` 

124 The name of the for corresponding iyy ellipticities column. 

125 ixyColumn : `str` 

126 The name of the for corresponding ixy ellipticities column. 

127 unitScale : `float`, optional 

128 A numerical scaling factor to multiply the ellipticity. 

129 shearConvention: `bool`, optional 

130 Option to use shear convention. When set to False, the distortion 

131 convention is used. 

132 Returns 

133 ------- 

134 e2 : `numpy.array` 

135 A numpy array of e2 ellipticity values. 

136 """ 

137 

138 def __init__( 

139 self, ixxColumn, iyyColumn, ixyColumn, unitScale=1.0, shearConvention=False 

140 ): 

141 self.ixxColumn = ixxColumn 

142 self.iyyColumn = iyyColumn 

143 self.ixyColumn = ixyColumn 

144 self.unitScale = unitScale 

145 self.shearConvention = shearConvention 

146 

147 def __call__(self, catalog): 

148 xx = catalog[self.ixxColumn] 

149 yy = catalog[self.iyyColumn] 

150 xy = catalog[self.ixyColumn] 

151 if self.shearConvention: 

152 e2 = (2.0 * xy) / (xx + yy + 2.0 * np.sqrt(xx * yy - xy ** 2)) 

153 else: 

154 e2 = (2.0 * xy) / (xx + yy) 

155 return np.array(e2) * self.unitScale 

156 

157 

158class E1Resids(object): 

159 """Functor to calculate e1 ellipticity residuals from an object catalog 

160 and PSF model. 

161 Parameters 

162 ---------- 

163 ixxColumn : `str` 

164 The name of the for corresponding ixx ellipticities column. 

165 iyyColumn : `str` 

166 The name of the for corresponding iyy ellipticities column. 

167 ixyColumn : `str` 

168 The name of the for corresponding ixy ellipticities column. 

169 ixxPsfColumn : `str` 

170 The name of the for corresponding ixx psf ellipticities column. 

171 iyyPsfColumn : `str` 

172 The name of the for corresponding iyy psf ellipticities column. 

173 ixyPsfColumn : `str` 

174 The name of the for corresponding ixy psf ellipticities column. 

175 unitScale : `float`, optional 

176 A numerical scaling factor to multiply both the object and PSF 

177 ellipticities. 

178 shearConvention: `bool`, optional 

179 Option to use shear convention. When set to False, the distortion 

180 convention is used. 

181 Returns 

182 ------- 

183 e1Resids : `numpy.array` 

184 A numpy array of e1 residual ellipticity values. 

185 """ 

186 

187 def __init__( 

188 self, 

189 ixxColumn, 

190 iyyColumn, 

191 ixxPsfColumn, 

192 iyyPsfColumn, 

193 ixyColumn, 

194 ixyPsfColumn, 

195 unitScale=1.0, 

196 shearConvention=False, 

197 ): 

198 self.ixxColumn = ixxColumn 

199 self.iyyColumn = iyyColumn 

200 self.ixyColumn = ixyColumn 

201 self.ixxPsfColumn = ixxPsfColumn 

202 self.iyyPsfColumn = iyyPsfColumn 

203 self.ixyPsfColumn = ixyPsfColumn 

204 

205 self.unitScale = unitScale 

206 self.shearConvention = shearConvention 

207 

208 def __call__(self, catalog): 

209 srcE1func = E1( 

210 self.ixxColumn, 

211 self.iyyColumn, 

212 self.ixyColumn, 

213 self.unitScale, 

214 self.shearConvention, 

215 ) 

216 psfE1func = E1( 

217 self.ixxPsfColumn, 

218 self.iyyPsfColumn, 

219 self.ixyPsfColumn, 

220 self.unitScale, 

221 self.shearConvention, 

222 ) 

223 

224 srcE1 = srcE1func(catalog) 

225 psfE1 = psfE1func(catalog) 

226 

227 e1Resids = srcE1 - psfE1 

228 return e1Resids 

229 

230 

231class E2Resids(object): 

232 """Functor to calculate e2 ellipticity residuals from an object catalog 

233 and PSF model. 

234 Parameters 

235 ---------- 

236 ixxColumn : `str` 

237 The name of the for corresponding ixx ellipticities column. 

238 iyyColumn : `str` 

239 The name of the for corresponding iyy ellipticities column. 

240 ixyColumn : `str` 

241 The name of the for corresponding ixy ellipticities column. 

242 ixxPsfColumn : `str` 

243 The name of the for corresponding ixx psf ellipticities column. 

244 iyyPsfColumn : `str` 

245 The name of the for corresponding iyy psf ellipticities column. 

246 ixyPsfColumn : `str` 

247 The name of the for corresponding ixy psf ellipticities column. 

248 unitScale : `float`, optional 

249 A numerical scaling factor to multiply both the object and PSF 

250 ellipticities. 

251 shearConvention: `bool`, optional 

252 Option to use shear convention. When set to False, the distortion 

253 convention is used. 

254 Returns 

255 ------- 

256 e2Resids : `numpy.array` 

257 A numpy array of e2 residual ellipticity values. 

258 """ 

259 

260 def __init__( 

261 self, 

262 ixxColumn, 

263 iyyColumn, 

264 ixxPsfColumn, 

265 iyyPsfColumn, 

266 ixyColumn, 

267 ixyPsfColumn, 

268 unitScale=1.0, 

269 shearConvention=False, 

270 ): 

271 self.ixxColumn = ixxColumn 

272 self.iyyColumn = iyyColumn 

273 self.ixyColumn = ixyColumn 

274 self.ixxPsfColumn = ixxPsfColumn 

275 self.iyyPsfColumn = iyyPsfColumn 

276 self.ixyPsfColumn = ixyPsfColumn 

277 self.unitScale = unitScale 

278 self.shearConvention = shearConvention 

279 

280 def __call__(self, catalog): 

281 srcE2func = E2( 

282 self.ixxColumn, 

283 self.iyyColumn, 

284 self.ixyColumn, 

285 self.unitScale, 

286 self.shearConvention, 

287 ) 

288 psfE2func = E2( 

289 self.ixxPsfColumn, 

290 self.iyyPsfColumn, 

291 self.ixyPsfColumn, 

292 self.unitScale, 

293 self.shearConvention, 

294 ) 

295 

296 srcE2 = srcE2func(catalog) 

297 psfE2 = psfE2func(catalog) 

298 

299 e2Resids = srcE2 - psfE2 

300 return e2Resids 

301 

302 

303class RhoStatistics(object): 

304 """Functor to compute Rho statistics given star catalog and PSF model. 

305 For detailed description of Rho statistics, refer to 

306 Rowe (2010) and Jarvis et al., (2016). 

307 Parameters 

308 ---------- 

309 column : `str` 

310 The name of the shape measurement algorithm. It should be one of 

311 ("base_SdssShape", "ext_shapeHSM_HsmSourceMoments"). 

312 psfColumn : `str` 

313 The name used for PSF shape measurements from the same algorithm. 

314 It must be one of ("base_SdssShape_psf", "ext_shapeHSM_HsmPsfMoments") 

315 and correspond to the algorithm name specified for ``column``. 

316 shearConvention: `bool`, optional 

317 Option to use shear convention. When set to False, the distortion 

318 convention is used. 

319 **kwargs 

320 Additional keyword arguments passed to treecorr. See 

321 https://rmjarvis.github.io/TreeCorr/_build/html/gg.html for details. 

322 Returns 

323 ------- 

324 rhoStats : `dict` [`int`, `treecorr.KKCorrelation` or 

325 `treecorr.GGCorrelation`] 

326 A dictionary with keys 0..5, containing one `treecorr.KKCorrelation` 

327 object (key 0) and five `treecorr.GGCorrelation` objects corresponding 

328 to Rho statistic indices. rho0 corresponds to autocorrelation function 

329 of PSF size residuals. 

330 """ 

331 

332 def __init__( 

333 self, 

334 ixxColumn, 

335 iyyColumn, 

336 ixxPsfColumn, 

337 iyyPsfColumn, 

338 raColumn, 

339 decColumn, 

340 ixyColumn, 

341 ixyPsfColumn, 

342 shearConvention=False, 

343 **kwargs 

344 ): 

345 self.ixxColumn = ixxColumn 

346 self.iyyColumn = iyyColumn 

347 self.ixyColumn = ixyColumn 

348 self.ixxPsfColumn = ixxPsfColumn 

349 self.iyyPsfColumn = iyyPsfColumn 

350 self.ixyPsfColumn = ixyPsfColumn 

351 self.shearConvention = shearConvention 

352 self.raColumn = raColumn 

353 self.decColumn = decColumn 

354 self.e1Func = E1( 

355 self.ixxPsfColumn, 

356 self.iyyPsfColumn, 

357 self.ixyPsfColumn, 

358 shearConvention=self.shearConvention, 

359 ) 

360 self.e2Func = E2( 

361 self.ixxPsfColumn, 

362 self.iyyPsfColumn, 

363 self.ixyPsfColumn, 

364 shearConvention=self.shearConvention, 

365 ) 

366 self.e1ResidsFunc = E1Resids( 

367 self.ixxColumn, 

368 self.iyyColumn, 

369 self.ixxPsfColumn, 

370 self.iyyPsfColumn, 

371 self.ixyColumn, 

372 self.ixyPsfColumn, 

373 shearConvention=self.shearConvention, 

374 ) 

375 self.e2ResidsFunc = E2Resids( 

376 self.ixxColumn, 

377 self.iyyColumn, 

378 self.ixxPsfColumn, 

379 self.iyyPsfColumn, 

380 self.ixyColumn, 

381 self.ixyPsfColumn, 

382 shearConvention=self.shearConvention, 

383 ) 

384 self.traceSizeFunc = TraceSize(self.ixxColumn, self.iyyColumn) 

385 self.psfTraceSizeFunc = TraceSize(self.ixxPsfColumn, self.iyyPsfColumn) 

386 self.kwargs = kwargs 

387 

388 def __call__(self, catalog): 

389 e1 = self.e1Func(catalog) 

390 e2 = self.e2Func(catalog) 

391 e1Res = self.e1ResidsFunc(catalog) 

392 e2Res = self.e2ResidsFunc(catalog) 

393 traceSize2 = self.traceSizeFunc(catalog) ** 2 

394 psfTraceSize2 = self.psfTraceSizeFunc(catalog) ** 2 

395 SizeRes = (traceSize2 - psfTraceSize2) / (0.5 * (traceSize2 + psfTraceSize2)) 

396 

397 isFinite = np.isfinite(e1Res) & np.isfinite(e2Res) & np.isfinite(SizeRes) 

398 e1 = e1[isFinite] 

399 e2 = e2[isFinite] 

400 e1Res = e1Res[isFinite] 

401 e2Res = e2Res[isFinite] 

402 SizeRes = SizeRes[isFinite] 

403 

404 # Scale the SizeRes by ellipticities 

405 e1SizeRes = e1 * SizeRes 

406 e2SizeRes = e2 * SizeRes 

407 

408 # Package the arguments to capture auto-/cross-correlations for the 

409 # Rho statistics. 

410 args = { 

411 0: (SizeRes, None), 

412 1: (e1Res, e2Res, None, None), 

413 2: (e1, e2, e1Res, e2Res), 

414 3: (e1SizeRes, e2SizeRes, None, None), 

415 4: (e1Res, e2Res, e1SizeRes, e2SizeRes), 

416 5: (e1, e2, e1SizeRes, e2SizeRes), 

417 } 

418 

419 ra = catalog[self.raColumn][isFinite] * 60.0 # arcmin 

420 dec = catalog[self.decColumn][isFinite] * 60.0 # arcmin 

421 

422 # Pass the appropriate arguments to the correlator and build a dict 

423 rhoStats = { 

424 rhoIndex: corrSpin2( 

425 ra, 

426 dec, 

427 *(args[rhoIndex]), 

428 raUnits="arcmin", 

429 decUnits="arcmin", 

430 **self.kwargs 

431 ) 

432 for rhoIndex in range(1, 6) 

433 } 

434 rhoStats[0] = corrSpin0( 

435 ra, dec, *(args[0]), raUnits="arcmin", decUnits="arcmin", **self.kwargs 

436 ) 

437 

438 return rhoStats 

439 

440 

441def corrSpin0( 

442 ra, dec, k1, k2=None, raUnits="degrees", decUnits="degrees", **treecorrKwargs 

443): 

444 """Function to compute correlations between at most two scalar fields. 

445 This is used to compute Rho0 statistics, given the appropriate spin-0 

446 (scalar) fields, usually fractional size residuals. 

447 Parameters 

448 ---------- 

449 ra : `numpy.array` 

450 The right ascension values of entries in the catalog. 

451 dec : `numpy.array` 

452 The declination values of entries in the catalog. 

453 k1 : `numpy.array` 

454 The primary scalar field. 

455 k2 : `numpy.array`, optional 

456 The secondary scalar field. 

457 Autocorrelation of the primary field is computed if `None` (default). 

458 raUnits : `str`, optional 

459 Unit of the right ascension values. 

460 Valid options are "degrees", "arcmin", "arcsec", "hours" or "radians". 

461 decUnits : `str`, optional 

462 Unit of the declination values. 

463 Valid options are "degrees", "arcmin", "arcsec", "hours" or "radians". 

464 **treecorrKwargs 

465 Keyword arguments to be passed to `treecorr.KKCorrelation`. 

466 Returns 

467 ------- 

468 xy : `treecorr.KKCorrelation` 

469 A `treecorr.KKCorrelation` object containing the correlation function. 

470 """ 

471 

472 xy = treecorr.KKCorrelation(**treecorrKwargs) 

473 catA = treecorr.Catalog(ra=ra, dec=dec, k=k1, ra_units=raUnits, dec_units=decUnits) 

474 if k2 is None: 

475 # Calculate the auto-correlation 

476 xy.process(catA) 

477 else: 

478 catB = treecorr.Catalog( 

479 ra=ra, dec=dec, k=k2, ra_units=raUnits, dec_units=decUnits 

480 ) 

481 # Calculate the cross-correlation 

482 xy.process(catA, catB) 

483 

484 return xy 

485 

486 

487def corrSpin2( 

488 ra, 

489 dec, 

490 g1a, 

491 g2a, 

492 g1b=None, 

493 g2b=None, 

494 raUnits="degrees", 

495 decUnits="degrees", 

496 **treecorrKwargs 

497): 

498 """Function to compute correlations between at most two shear-like fields. 

499 This is used to compute Rho statistics, given the appropriate spin-2 

500 (shear-like) fields. 

501 Parameters 

502 ---------- 

503 ra : `numpy.array` 

504 The right ascension values of entries in the catalog. 

505 dec : `numpy.array` 

506 The declination values of entries in the catalog. 

507 g1a : `numpy.array` 

508 The first component of the primary shear-like field. 

509 g2a : `numpy.array` 

510 The second component of the primary shear-like field. 

511 g1b : `numpy.array`, optional 

512 The first component of the secondary shear-like field. 

513 Autocorrelation of the primary field is computed if `None` (default). 

514 g2b : `numpy.array`, optional 

515 The second component of the secondary shear-like field. 

516 Autocorrelation of the primary field is computed if `None` (default). 

517 raUnits : `str`, optional 

518 Unit of the right ascension values. 

519 Valid options are "degrees", "arcmin", "arcsec", "hours" or "radians". 

520 decUnits : `str`, optional 

521 Unit of the declination values. 

522 Valid options are "degrees", "arcmin", "arcsec", "hours" or "radians". 

523 **treecorrKwargs 

524 Keyword arguments to be passed to `treecorr.GGCorrelation`. 

525 Returns 

526 ------- 

527 xy : `treecorr.GGCorrelation` 

528 A `treecorr.GGCorrelation` object containing the correlation function. 

529 """ 

530 xy = treecorr.GGCorrelation(**treecorrKwargs) 

531 catA = treecorr.Catalog( 

532 ra=ra, dec=dec, g1=g1a, g2=g2a, ra_units=raUnits, dec_units=decUnits 

533 ) 

534 if g1b is None or g2b is None: 

535 # Calculate the auto-correlation 

536 xy.process(catA) 

537 else: 

538 catB = treecorr.Catalog( 

539 ra=ra, dec=dec, g1=g1b, g2=g2b, ra_units=raUnits, dec_units=decUnits 

540 ) 

541 # Calculate the cross-correlation 

542 xy.process(catA, catB) 

543 

544 return xy 

545 

546 

547def calculateTEx(catalog, config, currentBand): 

548 """Compute ellipticity residual correlation metrics using parquet table as input. 

549 Parameters 

550 ---------- 

551 catalog : `pandas datafram` 

552 The catalog on which TE values will be calculated. 

553 config : `pex config` 

554 Task configuration. 

555 prependString : `str` 

556 The string to prepend to the band-specific columns. Typically a single letter 

557 filter e.g. 'g'. 

558 Returns 

559 ------- 

560 result : `dict` 

561 A dictionary with entries for radius, corr, and corrErr. 

562 """ 

563 

564 ixxColumn = config._getColumnName("ixx", currentBand) 

565 iyyColumn = config._getColumnName("iyy", currentBand) 

566 ixyColumn = config._getColumnName("ixy", currentBand) 

567 ixxPsfColumn = config._getColumnName("ixxPsf", currentBand) 

568 iyyPsfColumn = config._getColumnName("ixxPsf", currentBand) 

569 ixyPsfColumn = config._getColumnName("ixxPsf", currentBand) 

570 

571 nMinSources = 50 

572 if len(catalog) < nMinSources: 

573 return {"nomeas": np.nan * u.Unit("")} 

574 

575 treecorrKwargs = dict( 

576 nbins=config.nbins, 

577 min_sep=config.minSep, 

578 max_sep=config.maxSep, 

579 sep_units="arcmin", 

580 ) 

581 

582 rhoStatisticsFunc = RhoStatistics( 

583 ixxColumn, 

584 iyyColumn, 

585 ixxPsfColumn, 

586 iyyPsfColumn, 

587 config._getColumnName("ra"), 

588 config._getColumnName("dec"), 

589 ixyColumn, 

590 ixyPsfColumn, 

591 shearConvention=config.shearConvention, 

592 **treecorrKwargs 

593 ) 

594 xy = rhoStatisticsFunc(catalog)[config.rhoStat] 

595 

596 radius = np.exp(xy.meanlogr) * u.arcmin 

597 if config.rhoStat == 0: 

598 corr = xy.xi * u.Unit("") 

599 corrErr = np.sqrt(xy.varxip) * u.Unit("") 

600 else: 

601 corr = xy.xip * u.Unit("") 

602 corrErr = np.sqrt(xy.varxip) * u.Unit("") 

603 

604 result = dict(radius=radius, corr=corr, corrErr=corrErr) 

605 return result