Coverage for pesummary/gw/conversions/nrutils.py: 53.5%

202 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-05-02 08:42 +0000

1# Licensed under an MIT style license -- see LICENSE.md 

2 

3import numpy as np 

4from pesummary.utils.utils import logger 

5from pesummary.utils.decorators import bound_samples 

6 

7__author__ = ["Charlie Hoy <charlie.hoy@ligo.org>"] 

8 

9try: 

10 from lalinference.imrtgr import nrutils 

11 import lalsimulation 

12 from lalsimulation import ( 

13 SimInspiralGetSpinFreqFromApproximant, SIM_INSPIRAL_SPINS_CASEBYCASE, 

14 SIM_INSPIRAL_SPINS_FLOW 

15 ) 

16except ImportError: 

17 pass 

18 

19try: 

20 from lalsimulation.nrfits.eval_fits import eval_nrfit as _eval_nrfit 

21 NRSUR_MODULE = True 

22except (ModuleNotFoundError, ImportError): 

23 NRSUR_MODULE = False 

24 

25LPEAK_FITS = ["UIB2016", "Healyetal"] 

26FINALMASS_FITS = ["UIB2016", "Healyetal"] 

27FINALSPIN_FITS = ["UIB2016", "Healyetal", "HBR2016"] 

28 

29NRSUR_FITS = ["final_mass", "final_spin", "final_kick"] 

30NRSUR_MODEL = "NRSur7dq4Remnant" 

31 

32 

33def bbh_final_mass_non_spinning_Panetal(*args): 

34 """Return the final mass of the BH resulting from the merger of a non 

35 spinning BBH using the fit from Pan et al: Phys Rev D 84, 124052 (2011). 

36 

37 Parameters 

38 ---------- 

39 mass_1: float/np.ndarray 

40 float/array of masses for the primary object 

41 mass_2: float/np.ndarray 

42 float/array of masses for the secondary object 

43 """ 

44 return nrutils.bbh_final_mass_non_spinning_Panetal(*args) 

45 

46 

47@bound_samples(minimum=-1., maximum=1., logger_level="debug") 

48def bbh_final_spin_non_spinning_Panetal(*args): 

49 """Return the final spin of the BH resulting from the merger of a non 

50 spinning BBH using the fit from Pan et al: Phys Rev D 84, 124052 (2011). 

51 

52 Parameters 

53 ---------- 

54 mass_1: float/np.ndarray 

55 float/array of masses for the primary object 

56 mass_2: float/np.ndarray 

57 float/array of masses for the secondary object 

58 """ 

59 return nrutils.bbh_final_spin_non_spinning_Panetal(*args) 

60 

61 

62@bound_samples(minimum=-1., maximum=1., logger_level="debug") 

63def bbh_final_spin_non_precessing_Healyetal(*args, **kwargs): 

64 """Return the final spin of the BH resulting from the merger of a BBH for an 

65 aligned-spin system using the fit from Healy and Lousto: arXiv:1610.09713 

66 

67 Parameters 

68 ---------- 

69 mass_1: float/np.ndarray 

70 float/array of masses for the primary object 

71 mass_2: float/np.ndarray 

72 float/array of masses for the secondary object 

73 spin_1z: float/np.ndarray 

74 float/array of primary spin aligned with the orbital angular momentum 

75 spin_2z: float/np.ndarray 

76 float/array of secondary spin aligned with the orbital angular momentum 

77 version: str, optional 

78 version of the fitting coefficients you wish to use. Default are the 

79 fits from 2016 

80 """ 

81 if "version" not in kwargs.keys(): 

82 kwargs.update({"version": "2016"}) 

83 return nrutils.bbh_final_spin_non_precessing_Healyetal(*args, **kwargs) 

84 

85 

86def bbh_final_mass_non_precessing_Healyetal(*args, **kwargs): 

87 """Return the final mass of the BH resulting from the merger of a BBH for an 

88 aligned-spin system using the fit from Healy et al. If no version specified, 

89 the default fit is Healy and Lousto: arXiv:1610.09713 

90 

91 Parameters 

92 ---------- 

93 mass_1: float/np.ndarray 

94 float/array of masses for the primary object 

95 mass_2: float/np.ndarray 

96 float/array of masses for the secondary object 

97 spin_1z: float/np.ndarray 

98 float/array of primary spin aligned with the orbital angular momentum 

99 spin_2z: float/np.ndarray 

100 float/array of secondary spin aligned with the orbital angular momentum 

101 version: str, optional 

102 version of the fitting coefficients you wish to use. Default are the 

103 fits from 2016 

104 final_spin: float/np.ndarray, optional 

105 float/array of precomputed final spins 

106 """ 

107 chif = kwargs.pop("final_spin", None) 

108 kwargs.update({"chif": chif}) 

109 if "version" not in kwargs.keys(): 

110 kwargs.update({"version": "2016"}) 

111 return nrutils.bbh_final_mass_non_precessing_Healyetal(*args, **kwargs) 

112 

113 

114def bbh_final_mass_non_precessing_Husaetal(*args): 

115 """Return the final mass of the BH resulting from the merge of a BBH for an 

116 aligned-spin system using the fit from Husa et al: arXiv:1508.07250 

117 

118 Parameters 

119 ---------- 

120 mass_1: float/np.ndarray 

121 float/array of masses for the primary object 

122 mass_2: float/np.ndarray 

123 float/array of masses for the secondary object 

124 spin_1z: float/np.ndarray 

125 float/array of primary spin aligned with the orbital angular momentum 

126 spin_2z: float/np.ndarray 

127 float/array of secondary spin aligned with the orbital angular momentum 

128 """ 

129 return nrutils.bbh_final_mass_non_precessing_Husaetal(*args) 

130 

131 

132@bound_samples(minimum=-1., maximum=1., logger_level="debug") 

133def bbh_final_spin_non_precessing_Husaetal(*args): 

134 """Return the final spin of the BH resulting from the merger of a BBH for an 

135 aligned-spin system using the fit from Husa et al: arXiv:1508.07250 

136 

137 Parameters 

138 ---------- 

139 mass_1: float/np.ndarray 

140 float/array of masses for the primary object 

141 mass_2: float/np.ndarray 

142 float/array of masses for the secondary object 

143 spin_1z: float/np.ndarray 

144 float/array of primary spin aligned with the orbital angular momentum 

145 spin_2z: float/np.ndarray 

146 float/array of secondary spin aligned with the orbital angular momentum 

147 """ 

148 return nrutils.bbh_final_spin_non_precessing_Husaetal(*args) 

149 

150 

151def bbh_final_mass_non_precessing_UIB2016(*args, **kwargs): 

152 """Return the final mass of the BH resulting from the merger of a BBH for an 

153 aligned-spin system using the fit from https://arxiv.org/abs/1611.00332 

154 

155 Parameters 

156 ---------- 

157 mass_1: float/np.ndarray 

158 float/array of masses for the primary object 

159 mass_2: float/np.ndarray 

160 float/array of masses for the secondary object 

161 spin_1z: float/np.ndarray 

162 float/array of primary spin aligned with the orbital angular momentum 

163 spin_2z: float/np.ndarray 

164 float/array of secondary spin aligned with the orbital angular momentum 

165 version: str, optional 

166 version of the fitting coefficients you wish to use 

167 """ 

168 return nrutils.bbh_final_mass_non_precessing_UIB2016(*args, **kwargs) 

169 

170 

171@bound_samples(minimum=-1., maximum=1., logger_level="debug") 

172def bbh_final_spin_non_precessing_UIB2016(*args, **kwargs): 

173 """Return the final spin of the BH resulting from the merger of a BBH for an 

174 aligned-spin system using the fit from https://arxiv.org/abs/1611.00332 

175 

176 Parameters 

177 ---------- 

178 mass_1: float/np.ndarray 

179 float/array of masses for the primary object 

180 mass_2: float/np.ndarray 

181 float/array of masses for the secondary object 

182 spin_1z: float/np.ndarray 

183 float/array of primary spin aligned with the orbital angular momentum 

184 spin_2z: float/np.ndarray 

185 float/array of secondary spin aligned with the orbital angular momentum 

186 version: str, optional 

187 version of the fitting coefficients you wish to use 

188 """ 

189 return nrutils.bbh_final_spin_non_precessing_UIB2016(*args, **kwargs) 

190 

191 

192@bound_samples(minimum=-1., maximum=1., logger_level="debug") 

193def bbh_final_spin_non_precessing_HBR2016(*args, **kwargs): 

194 """Return the final spin of the BH resulting from the merger of a BBH for an 

195 aligned-spin system using the fit from Hofmann, Barausse, and Rezzolla 

196 ApJL 825, L19 (2016) 

197 

198 Parameters 

199 ---------- 

200 mass_1: float/np.ndarray 

201 float/array of masses for the primary object 

202 mass_2: float/np.ndarray 

203 float/array of masses for the secondary object 

204 spin_1z: float/np.ndarray 

205 float/array of primary spin aligned with the orbital angular momentum 

206 spin_2z: float/np.ndarray 

207 float/array of secondary spin aligned with the orbital angular momentum 

208 version: str, optional 

209 version of the fitting coefficients you wish to use 

210 """ 

211 return nrutils.bbh_final_spin_non_precessing_HBR2016(*args, **kwargs) 

212 

213 

214@bound_samples(maximum=1., logger_level="debug") 

215def _bbh_final_spin_precessing_using_non_precessing_fit( 

216 mass_1, mass_2, spin_1z, spin_2z, fit 

217): 

218 """Return the final spin of a BH results from the merger of a BH for a 

219 precessing system using non_precessing fits 

220 

221 Parameters 

222 ---------- 

223 mass_1: float/np.ndarray 

224 float/array of masses for the primary object 

225 mass_2: float/np.ndarray 

226 float/array of masses for the secondary object 

227 spin_1z: float/np.ndarray 

228 float/array of primary spin aligned with the orbital angular momentum 

229 spin_2z: float/np.ndarray 

230 float/array of secondary spin aligned with the orbital angular momentum 

231 fit: str 

232 name of the NR fit you wish to use 

233 """ 

234 return nrutils.bbh_final_spin_precessing( 

235 mass_1, mass_2, np.abs(spin_1z), np.abs(spin_2z), 

236 0.5 * np.pi * (1 - np.sign(spin_1z)), 

237 0.5 * np.pi * (1 - np.sign(spin_2z)), 

238 np.zeros_like(mass_1), fit 

239 ) 

240 

241 

242def bbh_final_spin_Panetal(*args): 

243 """Return the final spin of the BH resulting from the merger of a BBH for a 

244 precessing system using the fit from Pan et al: Phys Rev D 84, 124052 (2011) 

245 

246 Parameters 

247 ---------- 

248 mass_1: float/np.ndarray 

249 float/array of masses for the primary object 

250 mass_2: float/np.ndarray 

251 float/array of masses for the secondary object 

252 spin_1z: float/np.ndarray 

253 float/array of primary spin aligned with the orbital angular momentum 

254 spin_2z: float/np.ndarray 

255 float/array of secondary spin aligned with the orbital angular momentum 

256 """ 

257 return _bbh_final_spin_precessing_using_non_precessing_fit(*args, "Pan2011") 

258 

259 

260def bbh_final_spin_precessing_Healyetal(*args): 

261 """Return the final spin of the BH resulting from the merger of a BBH for a 

262 precessing using the fit from Healy et al. If no version specified, 

263 the default fit is Healy and Lousto: arXiv:1610.09713 

264 

265 Parameters 

266 ---------- 

267 mass_1: float/np.ndarray 

268 float/array of masses for the primary object 

269 mass_2: float/np.ndarray 

270 float/array of masses for the secondary object 

271 spin_1z: float/np.ndarray 

272 float/array of primary spin aligned with the orbital angular momentum 

273 spin_2z: float/np.ndarray 

274 float/array of secondary spin aligned with the orbital angular momentum 

275 """ 

276 return _bbh_final_spin_precessing_using_non_precessing_fit(*args, "HL2016") 

277 

278 

279def bbh_final_spin_precessing_UIB2016(*args): 

280 """Return the final spin of the BH resulting from the merger of a BBH for a 

281 precessing using the fit from David Keitel, Xisco Jimenez Forteza, 

282 Sascha Husa, Lionel London et al. arxiv:1612.09566v1 

283 

284 Parameters 

285 ---------- 

286 mass_1: float/np.ndarray 

287 float/array of masses for the primary object 

288 mass_2: float/np.ndarray 

289 float/array of masses for the secondary object 

290 spin_1z: float/np.ndarray 

291 float/array of primary spin aligned with the orbital angular momentum 

292 spin_2z: float/np.ndarray 

293 float/array of secondary spin aligned with the orbital angular momentum 

294 """ 

295 return _bbh_final_spin_precessing_using_non_precessing_fit(*args, "UIB2016") 

296 

297 

298@bound_samples(maximum=1., logger_level="debug") 

299def _bbh_final_spin_precessing_projected( 

300 mass_1, mass_2, a_1, a_2, tilt_1, tilt_2, phi_12, function=None 

301): 

302 """Project the precessing spins along the orbital angular momentum and 

303 calculate the final spin of the BH with an aligned-spin fit from the 

304 literature augmenting it with the leading contribution from the in-plane 

305 spins 

306 

307 Parameters 

308 ---------- 

309 mass_1: float/np.ndarray 

310 float/array of masses for the primary object 

311 mass_2: float/np.ndarray 

312 float/array of masses for the secondary object 

313 a_1: float/np.ndarray 

314 float/array of primary spin magnitudes 

315 a_2: float/np.ndarray 

316 float/array of secondary spin magnitudes 

317 tilt_1: float/np.ndarray 

318 float/array of primary spin tilt angle from the orbital angular momentum 

319 tilt_2: float/np.ndarray 

320 float/array of secondary spin tilt angle from the orbital angular 

321 momentum 

322 phi_12: float/np.ndarray 

323 float/array of samples for the angle between the in-plane spin 

324 components 

325 """ 

326 spin_1z = a_1 * np.cos(tilt_1) 

327 spin_2z = a_2 * np.cos(tilt_2) 

328 final_spin_aligned = function(mass_1, mass_2, spin_1z, spin_2z) 

329 final_spin_aligned_squared = final_spin_aligned * final_spin_aligned 

330 total_mass = mass_1 + mass_2 

331 

332 a_1perp = mass_1 * mass_1 * a_1 * np.sin(tilt_1) 

333 a_2perp = mass_2 * mass_2 * a_2 * np.sin(tilt_2) 

334 a_perp_squared = ( 

335 a_1perp * a_1perp + a_2perp * a_2perp 

336 + 2. * a_1perp * a_2perp * np.cos(phi_12) 

337 ) 

338 return (final_spin_aligned_squared + a_perp_squared / (total_mass)**4.)**0.5 

339 

340 

341def bbh_final_spin_precessing_projected_Healyetal(*args): 

342 """Return the final spin of the BH calculated from projected spins using 

343 the fit from Healy and Lousto: arXiv:1610.09713 augmenting it with the 

344 leading contribution from the in-plane spins 

345 

346 Parameters 

347 ---------- 

348 mass_1: float/np.ndarray 

349 float/array of masses for the primary object 

350 mass_2: float/np.ndarray 

351 float/array of masses for the secondary object 

352 a_1: float/np.ndarray 

353 float/array of primary spin magnitudes 

354 a_2: float/np.ndarray 

355 float/array of secondary spin magnitudes 

356 tilt_1: float/np.ndarray 

357 float/array of primary spin tilt angle from the orbital angular momentum 

358 tilt_2: float/np.ndarray 

359 float/array of secondary spin tilt angle from the orbital angular 

360 momentum 

361 phi_12: float/np.ndarray 

362 float/array of samples for the angle between the in-plane spin 

363 components 

364 """ 

365 return _bbh_final_spin_precessing_projected( 

366 *args, function=bbh_final_spin_precessing_Healyetal 

367 ) 

368 

369 

370def bbh_final_spin_precessing_projected_UIB2016(*args): 

371 """Return the final spin of the BH calculated from projected spins using 

372 the fit by David Keitel, Xisco Jimenez Forteza, Sascha Husa, Lionel London 

373 et al. arxiv:1612.09566v1 augmenting it with the leading contribution from 

374 the in-plane spins 

375 

376 Parameters 

377 ---------- 

378 mass_1: float/np.ndarray 

379 float/array of masses for the primary object 

380 mass_2: float/np.ndarray 

381 float/array of masses for the secondary object 

382 a_1: float/np.ndarray 

383 float/array of primary spin magnitudes 

384 a_2: float/np.ndarray 

385 float/array of secondary spin magnitudes 

386 tilt_1: float/np.ndarray 

387 float/array of primary spin tilt angle from the orbital angular momentum 

388 tilt_2: float/np.ndarray 

389 float/array of secondary spin tilt angle from the orbital angular 

390 momentum 

391 phi_12: float/np.ndarray 

392 float/array of samples for the angle between the in-plane spin 

393 components 

394 """ 

395 return _bbh_final_spin_precessing_projected( 

396 *args, function=bbh_final_spin_precessing_UIB2016 

397 ) 

398 

399 

400@bound_samples(maximum=1., logger_level="debug") 

401def bbh_final_spin_precessing_HBR2016(*args, **kwargs): 

402 """Return the final spin of the BH resulting from the merger of a BBH for a 

403 precessing system using the fit from Hofmann, Barausse, and Rezzolla ApJL 

404 825, L19 (2016) 

405 

406 Parameters 

407 ---------- 

408 mass_1: float/np.ndarray 

409 float/array of masses for the primary object 

410 mass_2: float/np.ndarray 

411 float/array of masses for the secondary object 

412 a_1: float/np.ndarray 

413 float/array of primary spin magnitudes 

414 a_2: float/np.ndarray 

415 float/array of secondary spin magnitudes 

416 tilt_1: float/np.ndarray 

417 float/array of primary spin tilt angle from the orbital angular momentum 

418 tilt_2: float/np.ndarray 

419 float/array of secondary spin tilt angle from the orbital angular 

420 momentum 

421 phi_12: float/np.ndarray 

422 float/array of samples for the angle between the in-plane spin 

423 components 

424 version: str, optional 

425 version of the fitting coefficients you wish to use 

426 """ 

427 return nrutils.bbh_final_spin_precessing_HBR2016(*args, **kwargs) 

428 

429 

430def bbh_peak_luminosity_non_precessing_T1600018(*args): 

431 """Return the peak luminosity (in units of 10^56 ergs/s) of an aligned-spin 

432 BBH using the fit by Sascha Husa, Xisco Jimenez Forteza, David Keitel 

433 [LIGO-T1500598] using 5th order in chieff 

434 

435 Parameters 

436 ---------- 

437 mass_1: float/np.ndarray 

438 float/array of masses for the primary object 

439 mass_2: float/np.ndarray 

440 float/array of masses for the secondary object 

441 spin_1z: float/np.ndarray 

442 float/array of primary spin aligned with the orbital angular momentum 

443 spin_2z: float/np.ndarray 

444 float/array of secondary spin aligned with the orbital angular momentum 

445 """ 

446 return nrutils.bbh_peak_luminosity_non_precessing_T1600018(*args) 

447 

448 

449def bbh_peak_luminosity_non_precessing_UIB2016(*args): 

450 """Return the peak luminosity (in units of 10^56 ergs/s) of an aligned-spin 

451 BBH using the fit by David Keitel, Xisco Jimenez Forteza, Sascha Husa, 

452 Lionel London et al. arxiv:1612.09566v1 

453 

454 Parameters 

455 ---------- 

456 mass_1: float/np.ndarray 

457 float/array of masses for the primary object 

458 mass_2: float/np.ndarray 

459 float/array of masses for the secondary object 

460 spin_1z: float/np.ndarray 

461 float/array of primary spin aligned with the orbital angular momentum 

462 spin_2z: float/np.ndarray 

463 float/array of secondary spin aligned with the orbital angular momentum 

464 """ 

465 return nrutils.bbh_peak_luminosity_non_precessing_UIB2016(*args) 

466 

467 

468def bbh_peak_luminosity_non_precessing_Healyetal(*args): 

469 """Return the peak luminosity (in units of 10^56 ergs/s) of an aligned-spin 

470 BBH using the fit from Healy and Lousto: arXiv:1610.09713 

471 

472 Parameters 

473 ---------- 

474 mass_1: float/np.ndarray 

475 float/array of masses for the primary object 

476 mass_2: float/np.ndarray 

477 float/array of masses for the secondary object 

478 spin_1z: float/np.ndarray 

479 float/array of primary spin aligned with the orbital angular momentum 

480 spin_2z: float/np.ndarray 

481 float/array of secondary spin aligned with the orbital angular momentum 

482 """ 

483 return nrutils.bbh_peak_luminosity_non_precessing_Healyetal(*args) 

484 

485 

486class PeakLuminosityFits(object): 

487 UIB2016 = bbh_peak_luminosity_non_precessing_UIB2016 

488 Healyetal = bbh_peak_luminosity_non_precessing_Healyetal 

489 T1600018 = bbh_peak_luminosity_non_precessing_T1600018 

490 

491 

492class FinalMassFits(object): 

493 Panetal = bbh_final_mass_non_spinning_Panetal 

494 Healyetal = bbh_final_mass_non_precessing_Healyetal 

495 Husaetal = bbh_final_mass_non_precessing_Husaetal 

496 UIB2016 = bbh_final_mass_non_precessing_UIB2016 

497 

498 

499class FinalSpinFits(object): 

500 Panetal = bbh_final_spin_non_spinning_Panetal 

501 Healyetal = bbh_final_spin_non_precessing_Healyetal 

502 Husaetal = bbh_final_spin_non_precessing_Husaetal 

503 UIB2016 = bbh_final_spin_non_precessing_UIB2016 

504 HBR2016 = bbh_final_spin_non_precessing_HBR2016 

505 

506 

507class FinalSpinPrecessingFits(object): 

508 Healyetal = bbh_final_spin_precessing_projected_Healyetal 

509 UIB2016 = bbh_final_spin_precessing_projected_UIB2016 

510 HBR2016 = bbh_final_spin_precessing_HBR2016 

511 

512 

513def _bbh_average_quantity( 

514 *args, fits=None, cls=None, quantity=None, return_fits_used=False 

515): 

516 """Average the result from multiple fits 

517 

518 Parameters 

519 ---------- 

520 *args: tuple 

521 tuple of arguments for the fitting functions 

522 fits: list 

523 list of fits that you wish to use 

524 cls: class 

525 class which maps a string to a given fitting function 

526 quantity: str 

527 quantity that you are combining results for 

528 return_fits_used: Bool, optional 

529 if True, return the fits that were used to calculate the average 

530 """ 

531 data, used_fits = [], [] 

532 for fit in fits: 

533 if hasattr(cls, fit): 

534 function = getattr(cls, fit) 

535 used_fits.append(str(function).replace("<", "").replace(">", "")) 

536 data.append(function(*args)) 

537 logger.info( 

538 "Averaging the {} from the following fits: {}".format( 

539 quantity, ", ".join(used_fits) 

540 ) 

541 ) 

542 if return_fits_used: 

543 return np.mean(data, axis=0), used_fits 

544 return np.mean(data, axis=0) 

545 

546 

547def bbh_final_mass_average(*args, fits=FINALMASS_FITS, return_fits_used=False): 

548 """Return the final mass averaged across multiple fits 

549 

550 Parameters 

551 ---------- 

552 mass_1: float/np.ndarray 

553 float/array of masses for the primary object 

554 mass_2: float/np.ndarray 

555 float/array of masses for the secondary object 

556 spin_1z: float/np.ndarray 

557 float/array of primary spin aligned with the orbital angular momentum 

558 spin_2z: float/np.ndarray 

559 float/array of secondary spin aligned with the orbital angular momentum 

560 fits: list, optional 

561 list of fits that you wish to use 

562 return_fits_used: Bool, optional 

563 if True, return the fits that were used to calculate the average 

564 """ 

565 return _bbh_average_quantity( 

566 *args, fits=fits, cls=FinalMassFits, quantity="final mass", 

567 return_fits_used=return_fits_used 

568 ) 

569 

570 

571def bbh_final_spin_average_non_precessing( 

572 *args, fits=FINALSPIN_FITS, return_fits_used=False 

573): 

574 """Return the final spin averaged across multiple non-precessing fits 

575 

576 Parameters 

577 ---------- 

578 mass_1: float/np.ndarray 

579 float/array of masses for the primary object 

580 mass_2: float/np.ndarray 

581 float/array of masses for the secondary object 

582 spin_1z: float/np.ndarray 

583 float/array of primary spin aligned with the orbital angular momentum 

584 spin_2z: float/np.ndarray 

585 float/array of secondary spin aligned with the orbital angular momentum 

586 fits: list, optional 

587 list of fits that you wish to use 

588 return_fits_used: Bool, optional 

589 if True, return the fits that were used to calculate the average 

590 """ 

591 return _bbh_average_quantity( 

592 *args, fits=fits, cls=FinalSpinFits, quantity="final spin", 

593 return_fits_used=return_fits_used 

594 ) 

595 

596 

597def bbh_final_spin_average_precessing( 

598 *args, fits=FINALSPIN_FITS, return_fits_used=False 

599): 

600 """Return the final spin averaged across multiple fits 

601 

602 Parameters 

603 ---------- 

604 mass_1: float/np.ndarray 

605 float/array of masses for the primary object 

606 mass_2: float/np.ndarray 

607 float/array of masses for the secondary object 

608 a_1: float/np.ndarray 

609 float/array of primary spin magnitudes 

610 a_2: float/np.ndarray 

611 float/array of secondary spin magnitudes 

612 tilt_1: float/np.ndarray 

613 float/array of primary spin tilt angle from the orbital angular momentum 

614 tilt_2: float/np.ndarray 

615 float/array of secondary spin tilt angle from the orbital angular 

616 momentum 

617 phi_12: float/np.ndarray 

618 float/array of samples for the angle between the in-plane spin 

619 components 

620 fits: list, optional 

621 list of fits that you wish to use 

622 return_fits_used: Bool, optional 

623 if True, return the fits that were used to calculate the average 

624 """ 

625 return _bbh_average_quantity( 

626 *args, fits=fits, cls=FinalSpinPrecessingFits, quantity="final spin", 

627 return_fits_used=return_fits_used 

628 ) 

629 

630 

631def bbh_peak_luminosity_average(*args, fits=LPEAK_FITS, return_fits_used=False): 

632 """Return the peak luminosity (in units of 10^56 ergs/s) averaged across 

633 multiple fits. 

634 

635 Parameters 

636 ---------- 

637 mass_1: float/np.ndarray 

638 float/array of masses for the primary object 

639 mass_2: float/np.ndarray 

640 float/array of masses for the secondary object 

641 spin_1z: float/np.ndarray 

642 float/array of primary spin aligned with the orbital angular momentum 

643 spin_2z: float/np.ndarray 

644 float/array of secondary spin aligned with the orbital angular momentum 

645 fits: list, optional 

646 list of fits that you wish to use 

647 return_fits_used: Bool, optional 

648 if True, return the fits that were used to calculate the average 

649 """ 

650 return _bbh_average_quantity( 

651 *args, fits=fits, cls=PeakLuminosityFits, quantity="peak luminosity", 

652 return_fits_used=return_fits_used 

653 ) 

654 

655 

656def eval_nrfit(*args, **kwargs): 

657 from contextlib import contextmanager 

658 import ctypes 

659 import io 

660 import os 

661 import sys 

662 import tempfile 

663 

664 libc = ctypes.CDLL(None) 

665 c_stdout = ctypes.c_void_p.in_dll(libc, 'stdout') 

666 

667 @contextmanager 

668 def stdout_redirector(stream): 

669 original_stdout_fd = sys.stdout.fileno() 

670 

671 def _redirect_stdout(to_fd): 

672 """Redirect stdout to the given file descriptor.""" 

673 libc.fflush(c_stdout) 

674 sys.stdout.close() 

675 os.dup2(to_fd, original_stdout_fd) 

676 sys.stdout = io.TextIOWrapper(os.fdopen(original_stdout_fd, 'wb')) 

677 

678 saved_stdout_fd = os.dup(original_stdout_fd) 

679 try: 

680 tfile = tempfile.TemporaryFile(mode='w+b') 

681 _redirect_stdout(tfile.fileno()) 

682 yield 

683 _redirect_stdout(saved_stdout_fd) 

684 tfile.flush() 

685 tfile.seek(0, io.SEEK_SET) 

686 stream.write(tfile.read()) 

687 finally: 

688 tfile.close() 

689 os.close(saved_stdout_fd) 

690 

691 f = io.BytesIO() 

692 with stdout_redirector(f): 

693 NRSurrogate_kwargs = kwargs.copy() 

694 approximant = kwargs.get("approximant", None) 

695 f_low = kwargs.get("f_low", None) 

696 f_ref = kwargs.get("f_ref", None) 

697 spinfreq_enum = SimInspiralGetSpinFreqFromApproximant( 

698 getattr(lalsimulation, approximant) 

699 ) 

700 if spinfreq_enum == SIM_INSPIRAL_SPINS_CASEBYCASE: 

701 raise ValueError( 

702 "Unable to evolve spins as '{}' does not have a set frequency " 

703 "at which the spins are defined".format(approximant) 

704 ) 

705 f_start = float(np.where( 

706 np.array(spinfreq_enum == SIM_INSPIRAL_SPINS_FLOW), f_low, f_ref 

707 )) 

708 NRSurrogate_kwargs["f_ref"] = f_start 

709 NRSurrogate_kwargs.pop("f_low") 

710 NRSurrogate_kwargs.pop("approximant") 

711 data = _eval_nrfit(*args, **NRSurrogate_kwargs) 

712 return data 

713 

714 

715def NRSur_fit( 

716 mass_1, mass_2, a_1, a_2, tilt_1, tilt_2, phi_12, phi_jl, theta_jn, phi_ref, 

717 f_low=20., f_ref=20., model=NRSUR_MODEL, fits=NRSUR_FITS, return_fits_used=False, 

718 approximant=None, **kwargs 

719): 

720 """Return the NR fits based on a chosen NRSurrogate model 

721 

722 Parameters 

723 ---------- 

724 mass_1: float/np.ndarray 

725 float/array of masses for the primary object. In units of solar mass 

726 mass_2: float/np.ndarray 

727 float/array of masses for the secondary object. In units of solar mass 

728 a_1: float/np.ndarray 

729 float/array of primary spin magnitudes 

730 a_2: float/np.ndarray 

731 float/array of secondary spin magnitudes 

732 tilt_1: float/np.ndarray 

733 float/array of primary spin tilt angle from the orbital angular momentum 

734 tilt_2: float/np.ndarray 

735 float/array of secondary spin tilt angle from the orbital angular 

736 momentum 

737 phi_12: float/np.ndarray 

738 float/array of samples for the angle between the in-plane spin 

739 components 

740 phi_jl: float/np.ndarray 

741 float/array of samples for the azimuthal angle of the orbital angular momentum 

742 around the total orbital angular momentum 

743 theta_jn: float/np.ndarray 

744 float/array of samples for the angle between the total angular momentum and 

745 the line of sight 

746 phi_ref: float/np.ndarray 

747 float/array of samples for the reference phase used in the analysis 

748 f_low: float 

749 the low frequency cut-off used in the analysis 

750 f_ref: float/np.ndarray, optional 

751 the reference frequency used in the analysis 

752 model: str, optional 

753 NRSurrogate model that you wish to use for the calculation 

754 fits: list, optional 

755 list of fits that you wish to evaluate 

756 approximant: str, optional 

757 The approximant that was used to generate the posterior samples 

758 kwargs: dict, optional 

759 optional kwargs that are passed directly to the 

760 `lalsimulation.nrfits.eval_fits.eval_nrfit` function 

761 """ 

762 from lal import MSUN_SI, C_SI 

763 from .spins import component_spins 

764 from .utils import magnitude_from_vector 

765 from pesummary.utils.utils import iterator 

766 import copy 

767 

768 if not NRSUR_MODULE: 

769 raise ImportError( 

770 "Unable to import `lalsimulation.nrfits.eval_fits`. This is likely " 

771 "due to the installed version of lalsimulation. Please update." 

772 ) 

773 

774 fits_map = { 

775 "final_mass": "FinalMass", "final_spin": "FinalSpin", 

776 "final_kick": "RecoilKick" 

777 } 

778 inverse_fits_map = {item: key for key, item in fits_map.items()} 

779 description = "Evaluating NRSurrogate fit for {}".format(", ".join(fits)) 

780 converted_fits = copy.deepcopy(fits) 

781 for fit, conversion in fits_map.items(): 

782 if fit in converted_fits: 

783 ind = fits.index(fit) 

784 converted_fits[ind] = conversion 

785 

786 spins = np.array( 

787 component_spins( 

788 theta_jn, phi_jl, tilt_1, tilt_2, phi_12, a_1, a_2, mass_1, mass_2, 

789 f_ref, phi_ref 

790 ) 

791 ) 

792 a_1_vec = np.array([spins.T[1], spins.T[2], spins.T[3]]).T 

793 a_2_vec = np.array([spins.T[4], spins.T[5], spins.T[6]]).T 

794 mass_1 *= MSUN_SI 

795 mass_2 *= MSUN_SI 

796 try: 

797 _fits = [ 

798 eval_nrfit( 

799 mass_1[num], mass_2[num], a_1_vec[num], a_2_vec[num], model, 

800 converted_fits, f_low=f_low, f_ref=f_ref[num], 

801 approximant=approximant, extra_params_dict=kwargs 

802 ) for num in iterator( 

803 range(len(mass_1)), desc=description, tqdm=True, total=len(mass_1), 

804 logger=logger 

805 ) 

806 ] 

807 except ValueError as e: 

808 base = ( 

809 "Failed to generate remnant quantities with the NRSurrogate " 

810 "remnant model. {}" 

811 ) 

812 if "symbol not found" in str(e): 

813 raise NameError( 

814 base.format( 

815 "This could be because the 'LAL_DATA_PATH' has not been " 

816 "set." 

817 ) 

818 ) 

819 raise ValueError(base.format("")) 

820 nr_fits = {key: np.array([dic[key] for dic in _fits]) for key in _fits[0]} 

821 if fits_map["final_mass"] in nr_fits.keys(): 

822 nr_fits[fits_map["final_mass"]] = np.array( 

823 [final_mass[0] for final_mass in nr_fits[fits_map["final_mass"]]] 

824 ) / MSUN_SI 

825 if fits_map["final_kick"] in nr_fits.keys(): 

826 nr_fits[fits_map["final_kick"]] *= C_SI / 1000 

827 final_kick_abs = magnitude_from_vector( 

828 nr_fits[fits_map["final_kick"]] 

829 ) 

830 nr_fits[fits_map["final_kick"]] = final_kick_abs 

831 if fits_map["final_spin"] in nr_fits.keys(): 

832 final_spin_abs = magnitude_from_vector( 

833 nr_fits[fits_map["final_spin"]] 

834 ) 

835 nr_fits[fits_map["final_spin"]] = final_spin_abs 

836 return { 

837 key if key not in inverse_fits_map.keys() else inverse_fits_map[key]: 

838 item for key, item in nr_fits.items() 

839 }