Coverage for pesummary/gw/conversions/remnant.py: 27.8%

205 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 

4 

5from pesummary.utils.utils import logger, iterator 

6from pesummary.utils.decorators import array_input 

7from .spins import chi_p 

8 

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

10 

11try: 

12 import lalsimulation 

13 from lalsimulation import ( 

14 FLAG_SEOBNRv4P_HAMILTONIAN_DERIVATIVE_NUMERICAL, 

15 FLAG_SEOBNRv4P_EULEREXT_QNM_SIMPLE_PRECESSION, 

16 FLAG_SEOBNRv4P_ZFRAME_L 

17 ) 

18 from lal import MSUN_SI 

19except ImportError: 

20 pass 

21 

22DEFAULT_SEOBFLAGS = { 

23 "SEOBNRv4P_SpinAlignedEOBversion": 4, 

24 "SEOBNRv4P_SymmetrizehPlminusm": 1, 

25 "SEOBNRv4P_HamiltonianDerivative": FLAG_SEOBNRv4P_HAMILTONIAN_DERIVATIVE_NUMERICAL, 

26 "SEOBNRv4P_euler_extension": FLAG_SEOBNRv4P_EULEREXT_QNM_SIMPLE_PRECESSION, 

27 "SEOBNRv4P_Zframe": FLAG_SEOBNRv4P_ZFRAME_L, 

28 "SEOBNRv4P_debug": 0 

29} 

30 

31 

32@array_input() 

33def final_mass_of_merger_from_NSBH( 

34 mass_1, mass_2, spin_1z, lambda_2, approximant="IMRPhenomNSBH" 

35): 

36 """Calculate the final mass resulting from an NSBH merger using NSBH 

37 waveform models given samples for mass_1, mass_2, spin_1z and lambda_2. 

38 mass_1 and mass_2 should be in solar mass units. 

39 """ 

40 from .tidal import _check_NSBH_approximant 

41 return _check_NSBH_approximant( 

42 approximant, mass_1, mass_2, spin_1z, lambda_2 

43 )[4] 

44 

45 

46@array_input() 

47def final_spin_of_merger_from_NSBH( 

48 mass_1, mass_2, spin_1z, lambda_2, approximant="IMRPhenomNSBH" 

49): 

50 """Calculate the final spin resulting from an NSBH merger using NSBH 

51 waveform models given samples for mass_1, mass_2, spin_1z and lambda_2. 

52 mass_1 and mass_2 should be in solar mass units. 

53 """ 

54 from .tidal import _check_NSBH_approximant 

55 return _check_NSBH_approximant( 

56 approximant, mass_1, mass_2, spin_1z, lambda_2 

57 )[5] 

58 

59 

60@array_input() 

61def _final_from_initial_NSBH(*args, **kwargs): 

62 """Calculate the final mass and final spin given the initial parameters 

63 of the binary using the approximant directly 

64 """ 

65 return [ 

66 final_mass_of_merger_from_NSBH(*args, **kwargs), 

67 final_spin_of_merger_from_NSBH(*args, **kwargs) 

68 ] 

69 

70 

71def _wrapper_return_final_mass_and_final_spin_from_waveform(args): 

72 """Wrapper function to calculate the remnant properties for a given waveform 

73 for a pool of workers 

74 

75 Parameters 

76 ---------- 

77 args: np.ndarray 

78 2 dimensional array giving arguments to pass to 

79 _return_final_mass_and_final_spin_from_waveform. The first argument 

80 in each sublist is the keyword and the second argument in each sublist 

81 is the item you wish to pass 

82 """ 

83 kwargs = {arg[0]: arg[1] for arg in args} 

84 return _return_final_mass_and_final_spin_from_waveform(**kwargs) 

85 

86 

87def _return_final_mass_and_final_spin_from_waveform( 

88 mass_function=None, spin_function=None, mass_function_args=[], 

89 spin_function_args=[], mass_function_return_function=None, 

90 mass_function_return_index=None, spin_function_return_function=None, 

91 spin_function_return_index=None, mass_1_index=0, mass_2_index=1, 

92 nsamples=0, approximant=None, default_SEOBNRv4P_kwargs=False 

93): 

94 """Return the final mass and final spin given functions to use 

95 

96 Parameters 

97 ---------- 

98 mass_function: func 

99 function you wish to use to calculate the final mass 

100 spin_function: func 

101 function you wish to use to calculate the final spin 

102 mass_function_args: list 

103 list of arguments you wish to pass to mass_function 

104 spin_function_args: list 

105 list of arguments you wish to pass to spin_function 

106 mass_function_return_function: str, optional 

107 function used to extract the final mass from the quantity returned from 

108 mass_function. For example, if mass_function returns a list and the 

109 final_mass is a property of the 3 arg of this list, 

110 mass_function_return_function='[3].final_mass' 

111 mass_function_return_index: str, optional 

112 if mass_function returns a list of parameters, 

113 mass_function_return_index indicates the index of `final_mass` in the 

114 list 

115 spin_function_return_function: str, optional 

116 function used to extract the final spin from the quantity returned from 

117 spin_function. For example, if spin_function returns a list and the 

118 final_spin is a property of the 3 arg of this list, 

119 spin_function_return_function='[3].final_spin' 

120 spin_function_return_index: str, optional 

121 if spin_function returns a list of parameters, 

122 spin_function_return_index indicates the index of `final_spin` in the 

123 list 

124 mass_1_index: int, optional 

125 the index of mass_1 in mass_function_args. Default is 0 

126 mass_2_index: int, optional 

127 the index of mass_2 in mass_function_args. Default is 1 

128 nsamples: int, optional 

129 the total number of samples 

130 approximant: str, optional 

131 the approximant used 

132 default_SEOBNRv4P_kwargs: Bool, optional 

133 if True, use the default SEOBNRv4P flags 

134 """ 

135 if default_SEOBNRv4P_kwargs: 

136 mode_array, seob_flags = _setup_SEOBNRv4P_args() 

137 mass_function_args += [mode_array, seob_flags] 

138 spin_function_args += [mode_array, seob_flags] 

139 fm = mass_function(*mass_function_args) 

140 if mass_function_return_function is not None: 

141 fm = eval("fm{}".format(mass_function_return_function)) 

142 elif mass_function_return_index is not None: 

143 fm = fm[mass_function_return_index] 

144 fs = spin_function(*spin_function_args) 

145 if spin_function_return_function is not None: 

146 fs = eval("fs{}".format(spin_function_return_function)) 

147 elif spin_function_return_index is not None: 

148 fs = fs[spin_function_return_index] 

149 final_mass = fm * ( 

150 mass_function_args[mass_1_index] + mass_function_args[mass_2_index] 

151 ) / MSUN_SI 

152 final_spin = fs 

153 return final_mass, final_spin 

154 

155 

156def _setup_SEOBNRv4P_args(mode=[2, 2], seob_flags=DEFAULT_SEOBFLAGS): 

157 """Setup the SEOBNRv4P[HM] kwargs 

158 """ 

159 from lalsimulation import ( 

160 SimInspiralCreateModeArray, SimInspiralModeArrayActivateMode 

161 ) 

162 from lal import DictInsertINT4Value, CreateDict 

163 

164 mode_array = SimInspiralCreateModeArray() 

165 SimInspiralModeArrayActivateMode(mode_array, mode[0], mode[1]) 

166 _seob_flags = CreateDict() 

167 for key, item in seob_flags.items(): 

168 DictInsertINT4Value(_seob_flags, key, item) 

169 return mode_array, _seob_flags 

170 

171 

172@array_input() 

173def _final_from_initial_BBH( 

174 mass_1, mass_2, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z, 

175 approximant="SEOBNRv4", iota=None, luminosity_distance=None, f_ref=None, 

176 phi_ref=None, mode=[2, 2], delta_t=1. / 4096, seob_flags=DEFAULT_SEOBFLAGS, 

177 return_fits_used=False, multi_process=None 

178): 

179 """Calculate the final mass and final spin given the initial parameters 

180 of the binary using the approximant directly 

181 

182 Parameters 

183 ---------- 

184 mass_1: float/np.ndarray 

185 primary mass of the binary 

186 mass_2: float/np.ndarray 

187 secondary mass of the binary 

188 spin_1x: float/np.ndarray 

189 x component of the primary spin 

190 spin_1y: float/np.ndarray 

191 y component of the primary spin 

192 spin_1z: float/np.ndarray 

193 z component of the primary spin 

194 spin_2x: float/np.ndarray 

195 x component of the secondary spin 

196 spin_2y: float/np.ndarray 

197 y component of the secondary spin 

198 spin_2z: float/np.ndarray 

199 z component of the seconday spin 

200 approximant: str 

201 name of the approximant you wish to use for the remnant fits 

202 iota: float/np.ndarray, optional 

203 the angle between the total orbital angular momentum and the line of 

204 sight of the source. Used when calculating the remnant fits for 

205 SEOBNRv4PHM. Since we only need the EOB dynamics here it does not matter 

206 what we pass 

207 luminosity_distance: float/np.ndarray, optional 

208 the luminosity distance of the source. Used when calculating the 

209 remnant fits for SEOBNRv4PHM. Since we only need the EOB dynamics here 

210 it does not matter what we pass. 

211 f_ref: float/np.ndarray, optional 

212 the reference frequency at which the spins are defined 

213 phi_ref: float/np.ndarray, optional 

214 the coalescence phase of the binary 

215 mode: list, optional 

216 specific mode to use when calculating the remnant fits for SEOBNRv4PHM. 

217 Since we only need the EOB dynamics here it does not matter what we 

218 pass. 

219 delta_t: float, optional 

220 the sampling rate used in the analysis, Used when calculating the 

221 remnant fits for SEOBNRv4PHM 

222 seob_flags: dict, optional 

223 dictionary containing the SEOB flags. Used when calculating the remnant 

224 fits for SEOBNRv4PHM 

225 return_fits_used: Bool, optional 

226 if True, return the approximant that was used. 

227 multi_process: int, optional 

228 the number of cores to use when calculating the remnant fits 

229 """ 

230 from lalsimulation import ( 

231 SimIMREOBFinalMassSpin, SimInspiralGetSpinSupportFromApproximant, 

232 SimIMRSpinPrecEOBWaveformAll, SimPhenomUtilsIMRPhenomDFinalMass, 

233 SimPhenomUtilsPhenomPv2FinalSpin 

234 ) 

235 import multiprocessing 

236 

237 def convert_args_for_multi_processing(kwargs): 

238 args = [] 

239 for n in range(kwargs["nsamples"]): 

240 _args = [] 

241 for key, item in kwargs.items(): 

242 if key == "mass_function_args" or key == "spin_function_args": 

243 _args.append([key, [arg[n] for arg in item]]) 

244 else: 

245 _args.append([key, item]) 

246 args.append(_args) 

247 return args 

248 

249 try: 

250 approx = getattr(lalsimulation, approximant) 

251 except AttributeError: 

252 raise ValueError( 

253 "The waveform '{}' is not supported by lalsimulation" 

254 ) 

255 

256 m1 = mass_1 * MSUN_SI 

257 m2 = mass_2 * MSUN_SI 

258 kwargs = {"nsamples": len(mass_1), "approximant": approximant} 

259 if approximant.lower() in ["seobnrv4p", "seobnrv4phm"]: 

260 if any(i is None for i in [iota, luminosity_distance, f_ref, phi_ref]): 

261 raise ValueError( 

262 "The approximant '{}' requires samples for iota, f_ref, " 

263 "phi_ref and luminosity_distance. Please pass these " 

264 "samples.".format(approximant) 

265 ) 

266 if len(delta_t) == 1: 

267 delta_t = [delta_t[0]] * len(mass_1) 

268 elif len(delta_t) != len(mass_1): 

269 raise ValueError( 

270 "Please provide either a single 'delta_t' that is is used for " 

271 "all samples, or a single 'delta_t' for each sample" 

272 ) 

273 mode_array, _seob_flags = _setup_SEOBNRv4P_args( 

274 mode=mode, seob_flags=seob_flags 

275 ) 

276 args = np.array([ 

277 phi_ref, delta_t, m1, m2, f_ref, luminosity_distance, iota, 

278 spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z, 

279 [mode_array] * len(mass_1), [_seob_flags] * len(mass_1) 

280 ]) 

281 kwargs.update( 

282 { 

283 "mass_function": SimIMRSpinPrecEOBWaveformAll, 

284 "spin_function": SimIMRSpinPrecEOBWaveformAll, 

285 "mass_function_args": args, 

286 "spin_function_args": args, 

287 "mass_function_return_function": "[21].data[6]", 

288 "spin_function_return_function": "[21].data[7]", 

289 "mass_1_index": 2, 

290 "mass_2_index": 3, 

291 } 

292 ) 

293 elif approximant.lower() in ["seobnrv4"]: 

294 spin1 = np.array([spin_1x, spin_1y, spin_1z]).T 

295 spin2 = np.array([spin_2x, spin_2y, spin_2z]).T 

296 app = np.array([approx] * len(mass_1)) 

297 kwargs.update( 

298 { 

299 "mass_function": SimIMREOBFinalMassSpin, 

300 "spin_function": SimIMREOBFinalMassSpin, 

301 "mass_function_args": [m1, m2, spin1, spin2, app], 

302 "spin_function_args": [m1, m2, spin1, spin2, app], 

303 "mass_function_return_index": 1, 

304 "spin_function_return_index": 2 

305 } 

306 ) 

307 elif "phenompv3" in approximant.lower(): 

308 kwargs.update( 

309 { 

310 "mass_function": SimPhenomUtilsIMRPhenomDFinalMass, 

311 "spin_function": SimPhenomUtilsPhenomPv2FinalSpin, 

312 "mass_function_args": [m1, m2, spin_1z, spin_2z], 

313 "spin_function_args": [m1, m2, spin_1z, spin_2z] 

314 } 

315 ) 

316 if SimInspiralGetSpinSupportFromApproximant(approx) > 2: 

317 # matches the waveform's internal usage as corrected in 

318 # https://git.ligo.org/lscsoft/lalsuite/-/merge_requests/1270 

319 _chi_p = chi_p(mass_1, mass_2, spin_1x, spin_1y, spin_2x, spin_2y) 

320 kwargs["spin_function_args"].append(_chi_p) 

321 else: 

322 kwargs["spin_function_args"].append(np.zeros_like(mass_1)) 

323 else: 

324 raise ValueError( 

325 "The waveform '{}' is not support by this function.".format( 

326 approximant 

327 ) 

328 ) 

329 

330 args = convert_args_for_multi_processing(kwargs) 

331 if multi_process is not None and multi_process[0] != 1: 

332 _multi_process = multi_process[0] 

333 if approximant.lower() in ["seobnrv4p", "seobnrv4phm"]: 

334 logger.warning( 

335 "Ignoring passed 'mode' and 'seob_flags' options. Defaults " 

336 "must be used with multiprocessing. If you wish to use custom " 

337 "options, please set `multi_process=None`" 

338 ) 

339 _kwargs = kwargs.copy() 

340 _kwargs["mass_function_args"] = kwargs["mass_function_args"][:-2] 

341 _kwargs["spin_function_args"] = kwargs["spin_function_args"][:-2] 

342 _kwargs["default_SEOBNRv4P_kwargs"] = True 

343 args = convert_args_for_multi_processing(_kwargs) 

344 with multiprocessing.Pool(_multi_process) as pool: 

345 data = np.array(list( 

346 iterator( 

347 pool.imap( 

348 _wrapper_return_final_mass_and_final_spin_from_waveform, 

349 args 

350 ), tqdm=True, desc="Evaluating {} fit".format(approximant), 

351 logger=logger, total=len(mass_1) 

352 ) 

353 )).T 

354 else: 

355 final_mass, final_spin = [], [] 

356 _iterator = iterator( 

357 range(kwargs["nsamples"]), tqdm=True, total=len(mass_1), 

358 desc="Evaluating {} fit".format(approximant), logger=logger 

359 ) 

360 for i in _iterator: 

361 data = _wrapper_return_final_mass_and_final_spin_from_waveform( 

362 args[i] 

363 ) 

364 final_mass.append(data[0]) 

365 final_spin.append(data[1]) 

366 data = [final_mass, final_spin] 

367 if return_fits_used: 

368 return data, [approximant] 

369 return data 

370 

371 

372def final_remnant_properties_from_NRSurrogate( 

373 *args, f_low=20., f_ref=20., model="NRSur7dq4Remnant", return_fits_used=False, 

374 properties=["final_mass", "final_spin", "final_kick"], approximant="SEOBNRv4PHM" 

375): 

376 """Return the properties of the final remnant resulting from a BBH merger using 

377 NRSurrogate fits 

378 

379 Parameters 

380 --------- 

381 f_low: float/np.ndarray 

382 The low frequency cut-off used in the analysis. Default is 20Hz 

383 f_ref: float/np.ndarray 

384 The reference frequency used in the analysis. Default is 20Hz 

385 model: str, optional 

386 The name of the NRSurrogate model you wish to use 

387 return_fits_used: Bool, optional 

388 if True, return the approximant that was used. 

389 properties: list, optional 

390 The list of properties you wish to calculate 

391 approximant: str, optional 

392 The approximant that was used to generate the posterior samples 

393 """ 

394 from .nrutils import NRSur_fit 

395 

396 fit = NRSur_fit( 

397 *args, f_low=f_low, f_ref=f_ref, model=model, fits=properties, 

398 approximant=approximant 

399 ) 

400 if return_fits_used: 

401 return fit, [model] 

402 return fit 

403 

404 

405def final_mass_of_merger_from_NR( 

406 *args, NRfit="average", final_spin=None, return_fits_used=False 

407): 

408 """Return the final mass resulting from a BBH merger using NR fits 

409 

410 Parameters 

411 ---------- 

412 NRfit: str 

413 Name of the fit you wish to use. If you wish to use a precessing fit 

414 please use the syntax 'precessing_{}'.format(fit_name). If you wish 

415 to have an average NR fit, then pass 'average' 

416 final_spin: float/np.ndarray, optional 

417 precomputed final spin of the remnant. 

418 return_fits_used: Bool, optional 

419 if True, return the fits that were used. Only used when NRfit='average' 

420 """ 

421 from pesummary.gw.conversions import nrutils 

422 

423 if NRfit.lower() == "average": 

424 func = getattr(nrutils, "bbh_final_mass_average") 

425 elif "panetal" in NRfit.lower(): 

426 func = getattr( 

427 nrutils, "bbh_final_mass_non_spinning_Panetal" 

428 ) 

429 else: 

430 func = getattr( 

431 nrutils, "bbh_final_mass_non_precessing_{}".format(NRfit) 

432 ) 

433 if "healy" in NRfit.lower(): 

434 return func(*args, final_spin=final_spin) 

435 if NRfit.lower() == "average": 

436 return func(*args, return_fits_used=return_fits_used) 

437 return func(*args) 

438 

439 

440def final_mass_of_merger_from_NRSurrogate( 

441 *args, model="NRSur7dq4Remnant", return_fits_used=False, approximant="SEOBNRv4PHM" 

442): 

443 """Return the final mass resulting from a BBH merger using NRSurrogate 

444 fits 

445 """ 

446 data = final_remnant_properties_from_NRSurrogate( 

447 *args, model=model, properties=["final_mass"], 

448 return_fits_used=return_fits_used, 

449 approximant=approximant 

450 ) 

451 if return_fits_used: 

452 return data[0]["final_mass"], data[1] 

453 return data["final_mass"] 

454 

455 

456def final_mass_of_merger_from_waveform(*args, NSBH=False, **kwargs): 

457 """Return the final mass resulting from a BBH/NSBH merger using a given 

458 approximant 

459 

460 Parameters 

461 ---------- 

462 NSBH: Bool, optional 

463 if True, use NSBH waveform fits. Default False 

464 """ 

465 if NSBH or "nsbh" in kwargs.get("approximant", "").lower(): 

466 return _final_from_initial_NSBH(*args, **kwargs)[1] 

467 return _final_from_initial_BBH(*args, **kwargs)[0] 

468 

469 

470def final_spin_of_merger_from_NR( 

471 *args, NRfit="average", return_fits_used=False 

472): 

473 """Return the final spin resulting from a BBH merger using NR fits 

474 

475 Parameters 

476 ---------- 

477 NRfit: str 

478 Name of the fit you wish to use. If you wish to use a precessing fit 

479 please use the syntax 'precessing_{}'.format(fit_name). If you wish 

480 to have an average NR fit, then pass 'average' 

481 return_fits_used: Bool, optional 

482 if True, return the fits that were used. Only used when NRfit='average' 

483 """ 

484 from pesummary.gw.conversions import nrutils 

485 

486 if NRfit.lower() == "average": 

487 func = getattr(nrutils, "bbh_final_spin_average_precessing") 

488 elif "pan" in NRfit.lower(): 

489 func = getattr( 

490 nrutils, "bbh_final_spin_non_spinning_Panetal" 

491 ) 

492 elif "precessing" in NRfit.lower(): 

493 func = getattr( 

494 nrutils, "bbh_final_spin_precessing_{}".format( 

495 NRfit.split("precessing_")[1] 

496 ) 

497 ) 

498 else: 

499 func = getattr( 

500 nrutils, "bbh_final_spin_non_precessing_{}".format(NRfit) 

501 ) 

502 if NRfit.lower() == "average": 

503 return func(*args, return_fits_used=return_fits_used) 

504 return func(*args) 

505 

506 

507def final_spin_of_merger_from_NRSurrogate( 

508 *args, model="NRSur7dq4Remnant", return_fits_used=False, approximant="SEOBNRv4PHM" 

509): 

510 """Return the final spin resulting from a BBH merger using NRSurrogate 

511 fits 

512 """ 

513 data = final_remnant_properties_from_NRSurrogate( 

514 *args, model=model, properties=["final_spin"], 

515 return_fits_used=return_fits_used, approximant=approximant 

516 ) 

517 if return_fits_used: 

518 return data[0]["final_spin"], data[1] 

519 return data["final_spin"] 

520 

521 

522def final_spin_of_merger_from_waveform(*args, NSBH=False, **kwargs): 

523 """Return the final spin resulting from a BBH/NSBH merger using a given 

524 approximant. 

525 

526 Parameters 

527 ---------- 

528 NSBH: Bool, optional 

529 if True, use NSBH waveform fits. Default False 

530 """ 

531 if NSBH or "nsbh" in kwargs.get("approximant", "").lower(): 

532 return _final_from_initial_NSBH(*args, **kwargs)[1] 

533 return _final_from_initial_BBH(*args, **kwargs)[1] 

534 

535 

536def final_kick_of_merger_from_NRSurrogate( 

537 *args, model="NRSur7dq4Remnant", return_fits_used=False, approximant="SEOBNRv4PHM" 

538): 

539 """Return the final kick of the remnant resulting from a BBH merger 

540 using NRSurrogate fits 

541 """ 

542 data = final_remnant_properties_from_NRSurrogate( 

543 *args, model=model, properties=["final_kick"], 

544 return_fits_used=return_fits_used, approximant=approximant 

545 ) 

546 if return_fits_used: 

547 return data[0]["final_kick"], data[1] 

548 return data["final_kick"] 

549 

550 

551def final_mass_of_merger( 

552 *args, method="NR", approximant="SEOBNRv4", NRfit="average", 

553 final_spin=None, return_fits_used=False, model="NRSur7dq4Remnant" 

554): 

555 """Return the final mass resulting from a BBH merger 

556 

557 Parameters 

558 ---------- 

559 mass_1: float/np.ndarray 

560 float/array of masses for the primary object 

561 mass_2: float/np.ndarray 

562 float/array of masses for the secondary object 

563 spin_1z: float/np.ndarray 

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

565 spin_2z: float/np.ndarray 

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

567 method: str 

568 The method you wish to use to calculate the final mass of merger. Either 

569 NR, NRSurrogate or waveform 

570 approximant: str 

571 Name of the approximant you wish to use if the chosen method is waveform 

572 or NRSurrogate 

573 NRFit: str 

574 Name of the NR fit you wish to use if chosen method is NR 

575 return_fits_used: Bool, optional 

576 if True, return the NR fits that were used. Only used when 

577 NRFit='average' or when method='NRSurrogate' 

578 model: str, optional 

579 The NRSurrogate model to use when evaluating the fits 

580 """ 

581 if method.lower() == "nr": 

582 mass_func = final_mass_of_merger_from_NR 

583 kwargs = { 

584 "NRfit": NRfit, "final_spin": final_spin, 

585 "return_fits_used": return_fits_used 

586 } 

587 elif "nrsur" in method.lower(): 

588 mass_func = final_mass_of_merger_from_NRSurrogate 

589 kwargs = { 

590 "approximant": approximant, "return_fits_used": return_fits_used, 

591 "model": model 

592 } 

593 else: 

594 mass_func = final_mass_of_merger_from_waveform 

595 kwargs = {"approximant": approximant} 

596 

597 return mass_func(*args, **kwargs) 

598 

599 

600def final_spin_of_merger( 

601 *args, method="NR", approximant="SEOBNRv4", NRfit="average", 

602 return_fits_used=False, model="NRSur7dq4Remnant" 

603): 

604 """Return the final mass resulting from a BBH merger 

605 

606 Parameters 

607 ---------- 

608 mass_1: float/np.ndarray 

609 float/array of masses for the primary object 

610 mass_2: float/np.ndarray 

611 float/array of masses for the secondary object 

612 a_1: float/np.ndarray 

613 float/array of primary spin magnitudes 

614 a_2: float/np.ndarray 

615 float/array of secondary spin magnitudes 

616 tilt_1: float/np.ndarray 

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

618 tilt_2: float/np.ndarray 

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

620 momentum 

621 phi_12: float/np.ndarray 

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

623 components 

624 method: str 

625 The method you wish to use to calculate the final mass of merger. Either 

626 NR, NRSurrogate or waveform 

627 approximant: str 

628 Name of the approximant you wish to use if the chosen method is waveform 

629 or NRSurrogate 

630 NRFit: str 

631 Name of the NR fit you wish to use if chosen method is NR 

632 return_fits_used: Bool, optional 

633 if True, return the NR fits that were used. Only used when 

634 NRFit='average' or when method='NRSurrogate' 

635 model: str, optional 

636 The NRSurrogate model to use when evaluating the fits 

637 """ 

638 if method.lower() == "nr": 

639 spin_func = final_spin_of_merger_from_NR 

640 kwargs = {"NRfit": NRfit, "return_fits_used": return_fits_used} 

641 elif "nrsur" in method.lower(): 

642 spin_func = final_spin_of_merger_from_NRSurrogate 

643 kwargs = { 

644 "approximant": approximant, "return_fits_used": return_fits_used, 

645 "model": model 

646 } 

647 else: 

648 spin_func = final_spin_of_merger_from_waveform 

649 kwargs = {"approximant": approximant} 

650 

651 return spin_func(*args, **kwargs) 

652 

653 

654def final_kick_of_merger( 

655 *args, method="NR", approximant="SEOBNRv4", NRfit="average", 

656 return_fits_used: False, model="NRSur7dq4Remnant" 

657): 

658 """Return the final kick velocity of the remnant resulting from a BBH merger 

659 

660 Parameters 

661 ---------- 

662 mass_1: float/np.ndarray 

663 float/array of masses for the primary object 

664 mass_2: float/np.ndarray 

665 float/array of masses for the secondary object 

666 a_1: float/np.ndarray 

667 float/array of primary spin magnitudes 

668 a_2: float/np.ndarray 

669 float/array of secondary spin magnitudes 

670 tilt_1: float/np.ndarray 

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

672 tilt_2: float/np.ndarray 

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

674 momentum 

675 phi_12: float/np.ndarray 

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

677 components 

678 method: str 

679 The method you wish to use to calculate the final kick of merger. Either 

680 NR, NRSurrogate or waveform 

681 approximant: str 

682 Name of the approximant you wish to use if the chosen method is waveform 

683 or NRSurrogate 

684 NRFit: str 

685 Name of the NR fit you wish to use if chosen method is NR 

686 return_fits_used: Bool, optional 

687 if True, return the NR fits that were used. Only used when 

688 NRFit='average' or when method='NRSurrogate' 

689 model: str, optional 

690 The NRSurrogate model to use when evaluating the fits 

691 """ 

692 if "nrsur" not in method.lower(): 

693 raise NotImplementedError( 

694 "Currently you can only work out the final kick velocity using " 

695 "NRSurrogate fits." 

696 ) 

697 velocity_func = final_kick_of_merger_from_NRSurrogate 

698 kwargs = { 

699 "approximant": approximant, "return_fits_used": return_fits_used, 

700 "model": model 

701 } 

702 return velocity_func(*args, **kwargs) 

703 

704 

705def peak_luminosity_of_merger(*args, NRfit="average", return_fits_used=False): 

706 """Return the peak luminosity of an aligned-spin BBH using NR fits 

707 

708 Parameters 

709 ---------- 

710 mass_1: float/np.ndarray 

711 float/array of masses for the primary object 

712 mass_2: float/np.ndarray 

713 float/array of masses for the secondary object 

714 spin_1z: float/np.ndarray 

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

716 spin_2z: float/np.ndarray 

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

718 NRFit: str 

719 Name of the NR fit you wish to use if chosen method is NR 

720 return_fits_used: Bool, optional 

721 if True, return the NR fits that were used. Only used when 

722 NRFit='average' 

723 """ 

724 from pesummary.gw.conversions import nrutils 

725 

726 if NRfit.lower() == "average": 

727 func = getattr(nrutils, "bbh_peak_luminosity_average") 

728 else: 

729 func = getattr( 

730 nrutils, "bbh_peak_luminosity_non_precessing_{}".format(NRfit) 

731 ) 

732 if NRfit.lower() == "average": 

733 return func(*args, return_fits_used=return_fits_used) 

734 return func(*args)