Coverage for pesummary/gw/waveform.py: 44.3%

262 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-12-09 22:34 +0000

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

2 

3import numpy as np 

4import lalsimulation as lalsim 

5from lalsimulation import ( 

6 SimInspiralGetSpinFreqFromApproximant, SIM_INSPIRAL_SPINS_CASEBYCASE, 

7 SIM_INSPIRAL_SPINS_FLOW 

8) 

9from pesummary.utils.utils import iterator, logger 

10from pesummary.utils.exceptions import EvolveSpinError 

11 

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

13_python_module_from_approximant_name = { 

14 "SEOBNRv5": "lalsimulation.gwsignal.models.pyseobnr_model" 

15} 

16 

17def _get_spin_freq_from_approximant(approximant): 

18 """Determine whether the reference frequency is the starting frequency 

19 for a given approximant string. 

20 

21 Parameters 

22 ---------- 

23 approximant: str 

24 Name of the approximant you wish to check 

25 """ 

26 try: 

27 # default to using LAL code 

28 approx = getattr(lalsim, approximant) 

29 return SimInspiralGetSpinFreqFromApproximant(approx) 

30 except AttributeError: 

31 from lalsimulation import ( 

32 SIM_INSPIRAL_SPINS_NONPRECESSING, SIM_INSPIRAL_SPINS_F_REF 

33 ) 

34 # check to see if approximant is in gwsignal 

35 approx = _gwsignal_generator_from_string(approximant) 

36 meta = approx.metadata 

37 if meta["type"] == "aligned_spin": 

38 return SIM_INSPIRAL_SPINS_NONPRECESSING 

39 elif meta["type"] == "precessing_spin": 

40 if meta["f_ref_spin"]: 

41 return SIM_INSPIRAL_SPINS_F_REF 

42 return SIM_INSPIRAL_SPINS_FLOW 

43 raise EvolveSpinError( 

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

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

46 ) 

47 

48 

49def _get_start_freq_from_approximant(approximant, f_low, f_ref): 

50 """Determine the starting frequency to use when evolving the spins for 

51 a given approximant string. 

52 

53 Parameters 

54 ---------- 

55 approximant: str 

56 Name of the approximant you wish to check 

57 f_low: float 

58 Low frequency used when generating the posterior samples 

59 f_ref: float 

60 Reference frequency used when generating the posterior samples 

61 """ 

62 try: 

63 spinfreq_enum = _get_spin_freq_from_approximant(approximant) 

64 except ValueError: # raised when approximant is not in gwsignal 

65 raise EvolveSpinError( 

66 "Unable to evolve spins as '{}' is unknown to lalsimulation " 

67 "and gwsignal".format(approximant) 

68 ) 

69 if spinfreq_enum == SIM_INSPIRAL_SPINS_CASEBYCASE: 

70 _msg = ( 

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

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

73 ) 

74 logger.warning(_msg) 

75 raise EvolveSpinError(_msg) 

76 return float(np.where( 

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

78 )) 

79 

80 

81def _check_approximant_from_string(approximant): 

82 """Check to see if the approximant is known to lalsimulation and/or 

83 gwsignal 

84 

85 Parameters 

86 ---------- 

87 approximant: str 

88 approximant you wish to check 

89 """ 

90 if hasattr(lalsim, approximant): 

91 return True 

92 else: 

93 try: 

94 _ = _gwsignal_generator_from_string(approximant) 

95 except (ValueError, NameError): 

96 return False 

97 return True 

98 

99 

100def _lal_approximant_from_string(approximant): 

101 """Return the LAL approximant number given an approximant string 

102 

103 Parameters 

104 ---------- 

105 approximant: str 

106 approximant you wish to convert 

107 """ 

108 return lalsim.GetApproximantFromString(approximant) 

109 

110 

111def _gwsignal_generator_from_string(approximant, version="v1", **kwargs): 

112 """Return the GW signal generator given an approximant string 

113 

114 Parameters 

115 ---------- 

116 approximant: str 

117 approximant you wish to convert 

118 version: str, optional 

119 version of gwsignal_get_waveform_generator to use. 'v1' ignores 

120 all kwargs and simply passes the approximant name to 

121 gwsignal_get_waveform_generator. 'v2' passes the approximant name 

122 and python_module to gwsignal_get_waveform_generator. The python_module 

123 can either be provided to _gwsignal_generator_from_string directly or if 

124 not provided, PESummary will use the stored default values. Default 'v1' 

125 kwargs: dict, optional 

126 optional kwargs to pass to gwsignal_get_waveform_generator 

127 """ 

128 from lalsimulation.gwsignal.models import gwsignal_get_waveform_generator 

129 if version == "v1": 

130 return gwsignal_get_waveform_generator(approximant) 

131 elif version == "v2": 

132 fallback = [ 

133 value for key, value in _python_module_from_approximant_name.items() 

134 if key in approximant 

135 ] 

136 if len(fallback): 

137 fallback = fallback[0] 

138 else: 

139 fallback = None 

140 module = kwargs.get("python_module", fallback) 

141 if module is None: 

142 raise ValueError( 

143 "Please provide a python_module defining the gwsignal " 

144 "generator as pesummary does not have a default value" 

145 ) 

146 return gwsignal_get_waveform_generator( 

147 approximant, python_module=module 

148 ) 

149 else: 

150 raise ValueError( 

151 "Unknown version {}. Please use either v1 or v2".format(version) 

152 ) 

153 

154 

155def _insert_flags(flags, LAL_parameters=None): 

156 """Insert waveform specific flags to a LAL dictionary 

157 

158 Parameters 

159 ---------- 

160 flags: dict 

161 dictionary of flags you wish to add to a LAL dictionary 

162 LAL_parameters: LALDict, optional 

163 An existing LAL dictionary to add mode array to. If not provided, a new 

164 LAL dictionary is created. Default None. 

165 """ 

166 if LAL_parameters is None: 

167 import lal 

168 LAL_parameters = lal.CreateDict() 

169 for key, item in flags.items(): 

170 func = getattr(lalsim, "SimInspiralWaveformParamsInsert" + key, None) 

171 if func is not None: 

172 func(LAL_parameters, int(item)) 

173 else: 

174 logger.warning( 

175 "Unable to add flag {} to LAL dictionary as no function " 

176 "SimInspiralWaveformParamsInsert{} found in " 

177 "LALSimulation.".format(key, key) 

178 ) 

179 return LAL_parameters 

180 

181 

182def _insert_mode_array(modes, LAL_parameters=None): 

183 """Add a mode array to a LAL dictionary 

184 

185 Parameters 

186 ---------- 

187 modes: 2d list 

188 2d list of modes you wish to add to a LAL dictionary. Must be of the 

189 form [[l1, m1], [l2, m2]] 

190 LAL_parameters: LALDict, optional 

191 An existing LAL dictionary to add mode array to. If not provided, a new 

192 LAL dictionary is created. Default None. 

193 """ 

194 if LAL_parameters is None: 

195 import lal 

196 LAL_parameters = lal.CreateDict() 

197 _mode_array = lalsim.SimInspiralCreateModeArray() 

198 for l, m in modes: 

199 lalsim.SimInspiralModeArrayActivateMode(_mode_array, l, m) 

200 lalsim.SimInspiralWaveformParamsInsertModeArray(LAL_parameters, _mode_array) 

201 return LAL_parameters 

202 

203 

204def _waveform_args(samples, f_ref=20., ind=0, longAscNodes=0., eccentricity=0.): 

205 """Arguments to be passed to waveform generation 

206 

207 Parameters 

208 ---------- 

209 f_ref: float, optional 

210 reference frequency to use when converting spherical spins to 

211 cartesian spins 

212 ind: int, optional 

213 index for the sample you wish to plot 

214 longAscNodes: float, optional 

215 longitude of ascending nodes, degenerate with the polarization 

216 angle. Default 0. 

217 eccentricity: float, optional 

218 eccentricity at reference frequency. Default 0. 

219 """ 

220 from lal import MSUN_SI, PC_SI 

221 

222 key = list(samples.keys())[0] 

223 if isinstance(samples[key], (list, np.ndarray)): 

224 _samples = {key: value[ind] for key, value in samples.items()} 

225 else: 

226 _samples = samples.copy() 

227 required = [ 

228 "mass_1", "mass_2", "luminosity_distance" 

229 ] 

230 if not all(param in _samples.keys() for param in required): 

231 raise ValueError( 

232 "Unable to generate a waveform. Please add samples for " 

233 + ", ".join(required) 

234 ) 

235 waveform_args = [ 

236 _samples["mass_1"] * MSUN_SI, _samples["mass_2"] * MSUN_SI 

237 ] 

238 spin_angles = [ 

239 "theta_jn", "phi_jl", "tilt_1", "tilt_2", "phi_12", "a_1", "a_2", 

240 "phase" 

241 ] 

242 spin_angles_condition = all( 

243 spin in _samples.keys() for spin in spin_angles 

244 ) 

245 cartesian_spins = [ 

246 "spin_1x", "spin_1y", "spin_1z", "spin_2x", "spin_2y", "spin_2z" 

247 ] 

248 cartesian_spins_condition = any( 

249 spin in _samples.keys() for spin in cartesian_spins 

250 ) 

251 if spin_angles_condition and not cartesian_spins_condition: 

252 from pesummary.gw.conversions import component_spins 

253 data = component_spins( 

254 _samples["theta_jn"], _samples["phi_jl"], _samples["tilt_1"], 

255 _samples["tilt_2"], _samples["phi_12"], _samples["a_1"], 

256 _samples["a_2"], _samples["mass_1"], _samples["mass_2"], 

257 f_ref, _samples["phase"] 

258 ) 

259 iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z = data.T 

260 spins = [spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z] 

261 else: 

262 iota = _samples["iota"] 

263 spins = [ 

264 _samples[param] if param in _samples.keys() else 0. for param in 

265 ["spin_1x", "spin_1y", "spin_1z", "spin_2x", "spin_2y", "spin_2z"] 

266 ] 

267 _zero_spins = np.isclose(spins, 0.) 

268 if sum(_zero_spins): 

269 spins = np.array(spins) 

270 spins[_zero_spins] = 0. 

271 spins = list(spins) 

272 waveform_args += spins 

273 phase = _samples["phase"] if "phase" in _samples.keys() else 0. 

274 waveform_args += [ 

275 _samples["luminosity_distance"] * PC_SI * 10**6, iota, phase 

276 ] 

277 waveform_args += [longAscNodes, eccentricity, 0.] 

278 return waveform_args, _samples 

279 

280 

281def antenna_response(samples, ifo): 

282 """ 

283 """ 

284 import importlib 

285 

286 mod = importlib.import_module("pesummary.gw.plots.plot") 

287 func = getattr(mod, "__antenna_response") 

288 antenna = func( 

289 ifo, samples["ra"], samples["dec"], samples["psi"], 

290 samples["geocent_time"] 

291 ) 

292 return antenna 

293 

294 

295def _project_waveform(ifo, hp, hc, ra, dec, psi, time): 

296 """Project a waveform onto a given detector 

297 

298 Parameters 

299 ---------- 

300 ifo: str 

301 name of the detector you wish to project the waveform onto 

302 hp: np.ndarray 

303 plus gravitational wave polarization 

304 hc: np.ndarray 

305 cross gravitational wave polarization 

306 ra: float 

307 right ascension to be passed to antenna response function 

308 dec: float 

309 declination to be passed to antenna response function 

310 psi: float 

311 polarization to be passed to antenna response function 

312 time: float 

313 time to be passed to antenna response function 

314 """ 

315 samples = { 

316 "ra": ra, "dec": dec, "psi": psi, "geocent_time": time 

317 } 

318 antenna = antenna_response(samples, ifo) 

319 ht = hp * antenna[0] + hc * antenna[1] 

320 return ht 

321 

322 

323def fd_waveform( 

324 samples, approximant, delta_f, f_low, f_high, f_ref=20., project=None, 

325 ind=0, longAscNodes=0., eccentricity=0., LAL_parameters=None, 

326 mode_array=None, pycbc=False, flen=None, flags={}, debug=False, 

327 **kwargs 

328): 

329 """Generate a gravitational wave in the frequency domain 

330 

331 Parameters 

332 ---------- 

333 approximant: str 

334 name of the approximant to use when generating the waveform 

335 delta_f: float 

336 spacing between frequency samples 

337 f_low: float 

338 frequency to start evaluating the waveform 

339 f_high: float 

340 frequency to stop evaluating the waveform 

341 f_ref: float, optional 

342 reference frequency 

343 project: str, optional 

344 name of the detector to project the waveform onto. If None, 

345 the plus and cross polarizations are returned. Default None 

346 ind: int, optional 

347 index for the sample you wish to plot 

348 longAscNodes: float, optional 

349 longitude of ascending nodes, degenerate with the polarization 

350 angle. Default 0. 

351 eccentricity: float, optional 

352 eccentricity at reference frequency. Default 0. 

353 LAL_parameters: LALDict, optional 

354 LAL dictionary containing accessory parameters. Default None 

355 mode_array: 2d list 

356 2d list of modes you wish to include in waveform. Must be of the form 

357 [[l1, m1], [l2, m2]] 

358 pycbc: Bool, optional 

359 return a the waveform as a pycbc.frequencyseries.FrequencySeries 

360 object 

361 flen: int 

362 Length of the frequency series in samples. Default is None. Only used 

363 when pycbc=True 

364 debug: bool, optional 

365 if True, return the model object used to generate the waveform. Only 

366 used when the waveform approximant is not in LALSimulation 

367 flags: dict, optional 

368 waveform specific flags to add to LAL dictionary 

369 **kwargs: dict, optional 

370 all kwargs passed to _calculate_hp_hc_fd 

371 """ 

372 from gwpy.frequencyseries import FrequencySeries 

373 

374 waveform_args, _samples = _waveform_args( 

375 samples, f_ref=f_ref, ind=ind, longAscNodes=longAscNodes, 

376 eccentricity=eccentricity 

377 ) 

378 if len(flags): 

379 LAL_parameters = _insert_flags( 

380 flags, LAL_parameters=LAL_parameters 

381 ) 

382 if mode_array is not None: 

383 LAL_parameters = _insert_mode_array( 

384 mode_array, LAL_parameters=LAL_parameters 

385 ) 

386 (hp, hc), model = _calculate_hp_hc_fd( 

387 waveform_args, delta_f, f_low, f_high, f_ref, LAL_parameters, approximant, 

388 debug=True, **kwargs 

389 ) 

390 hp = FrequencySeries(hp.data.data, df=hp.deltaF, f0=0.) 

391 hc = FrequencySeries(hc.data.data, df=hc.deltaF, f0=0.) 

392 if pycbc: 

393 hp, hc = hp.to_pycbc(), hc.to_pycbc() 

394 if flen is not None: 

395 hp.resize(flen) 

396 hc.resize(flen) 

397 if project is None and debug: 

398 return {"h_plus": hp, "h_cross": hc}, model 

399 elif project is None: 

400 return {"h_plus": hp, "h_cross": hc} 

401 ht = _project_waveform( 

402 project, hp, hc, _samples["ra"], _samples["dec"], _samples["psi"], 

403 _samples["geocent_time"] 

404 ) 

405 if debug: 

406 return ht, model 

407 return ht 

408 

409 

410def _wrapper_for_td_waveform(args): 

411 """Wrapper function for td_waveform for a pool of workers 

412 

413 Parameters 

414 ---------- 

415 args: tuple 

416 All args passed to td_waveform 

417 """ 

418 return td_waveform(*args[:-1], **args[-1]) 

419 

420 

421def td_waveform( 

422 samples, approximant, delta_t, f_low, f_ref=20., project=None, ind=0, 

423 longAscNodes=0., eccentricity=0., LAL_parameters=None, mode_array=None, 

424 pycbc=False, level=None, multi_process=1, flags={}, debug=False, 

425 **kwargs 

426): 

427 """Generate a gravitational wave in the time domain 

428 

429 Parameters 

430 ---------- 

431 approximant: str 

432 name of the approximant to use when generating the waveform 

433 delta_t: float 

434 spacing between frequency samples 

435 f_low: float 

436 frequency to start evaluating the waveform 

437 f_ref: float, optional 

438 reference frequency 

439 project: str, optional 

440 name of the detector to project the waveform onto. If None, 

441 the plus and cross polarizations are returned. Default None 

442 ind: int, optional 

443 index for the sample you wish to plot 

444 longAscNodes: float, optional 

445 longitude of ascending nodes, degenerate with the polarization 

446 angle. Default 0. 

447 eccentricity: float, optional 

448 eccentricity at reference frequency. Default 0. 

449 LAL_parameters: LALDict, optional 

450 LAL dictionary containing accessory parameters. Default None 

451 mode_array: 2d list 

452 2d list of modes you wish to include in waveform. Must be of the form 

453 [[l1, m1], [l2, m2]] 

454 pycbc: Bool, optional 

455 return a the waveform as a pycbc.timeseries.TimeSeries object 

456 level: list, optional 

457 the symmetric confidence interval of the time domain waveform. Level 

458 must be greater than 0 and less than 1 

459 multi_process: int, optional 

460 number of cores to run on when generating waveforms. Only used when 

461 level is not None, 

462 flags: dict, optional 

463 waveform specific flags to add to LAL dictionary. Default {} 

464 kwargs: dict, optional 

465 all kwargs passed to _td_waveform 

466 """ 

467 if len(flags): 

468 LAL_parameters = _insert_flags( 

469 flags, LAL_parameters=LAL_parameters 

470 ) 

471 if mode_array is not None: 

472 LAL_parameters = _insert_mode_array( 

473 mode_array, LAL_parameters=LAL_parameters 

474 ) 

475 if level is not None: 

476 import multiprocessing 

477 from pesummary.core.plots.interpolate import Bounded_interp1d 

478 td_waveform_list = [] 

479 _key = list(samples.keys())[0] 

480 N = len(samples[_key]) 

481 with multiprocessing.Pool(multi_process) as pool: 

482 args = np.array([ 

483 [samples] * N, [approximant] * N, [delta_t] * N, [f_low] * N, 

484 [f_ref] * N, [project] * N, np.arange(N), [longAscNodes] * N, 

485 [eccentricity] * N, [LAL_parameters] * N, [mode_array] * N, 

486 [pycbc] * N, [None] * N, [kwargs] * N 

487 ], dtype="object").T 

488 td_waveform_list = list( 

489 iterator( 

490 pool.imap(_wrapper_for_td_waveform, args), 

491 tqdm=True, logger=logger, total=N, 

492 desc="Generating waveforms" 

493 ) 

494 ) 

495 td_waveform_array = np.array(td_waveform_list, dtype=object) 

496 _level = (1 + np.array(level)) / 2 

497 if project is None: 

498 mint = np.min( 

499 [ 

500 np.min([_.times[0].value for _ in waveform.values()]) for 

501 waveform in td_waveform_array 

502 ] 

503 ) 

504 maxt = np.max( 

505 [ 

506 np.max([_.times[-1].value for _ in waveform.values()]) for 

507 waveform in td_waveform_array 

508 ] 

509 ) 

510 new_t = np.arange(mint, maxt, delta_t) 

511 td_waveform_array = { 

512 polarization: np.array( 

513 [ 

514 Bounded_interp1d( 

515 np.array(waveform[polarization].times, dtype=np.float64), 

516 waveform[polarization], xlow=mint, xhigh=maxt 

517 )(new_t) for waveform in td_waveform_array 

518 ] 

519 ) for polarization in ["h_plus", "h_cross"] 

520 } 

521 else: 

522 mint = np.min([_.times[0].value for _ in td_waveform_array]) 

523 maxt = np.max([_.times[-1].value for _ in td_waveform_array]) 

524 new_t = np.arange(mint, maxt, delta_t) 

525 td_waveform_array = { 

526 "h_t": [ 

527 Bounded_interp1d( 

528 np.array(waveform.times, dtype=np.float64), waveform, 

529 xlow=mint, xhigh=maxt 

530 )(new_t) for waveform in td_waveform_array 

531 ] 

532 } 

533 

534 upper = { 

535 polarization: np.percentile( 

536 td_waveform_array[polarization], _level * 100, axis=0 

537 ) for polarization in td_waveform_array.keys() 

538 } 

539 lower = { 

540 polarization: np.percentile( 

541 td_waveform_array[polarization], (1 - _level) * 100, axis=0 

542 ) for polarization in td_waveform_array.keys() 

543 } 

544 if len(upper) == 1: 

545 upper = upper["h_t"] 

546 lower = lower["h_t"] 

547 

548 waveform_args, _samples = _waveform_args( 

549 samples, ind=ind, longAscNodes=longAscNodes, eccentricity=eccentricity, 

550 f_ref=f_ref 

551 ) 

552 waveform, model = _td_waveform( 

553 waveform_args, approximant, delta_t, f_low, f_ref, LAL_parameters, 

554 _samples, pycbc=pycbc, project=project, debug=True, **kwargs 

555 ) 

556 if level is not None and debug: 

557 return waveform, upper, lower, new_t, model 

558 elif level is not None: 

559 return waveform, upper, lower, new_t 

560 elif debug: 

561 return waveform, model 

562 return waveform 

563 

564 

565def _td_waveform( 

566 waveform_args, approximant, delta_t, f_low, f_ref, LAL_parameters, samples, 

567 pycbc=False, project=None, debug=False, **kwargs 

568): 

569 """Generate a gravitational wave in the time domain 

570 

571 Parameters 

572 ---------- 

573 waveform_args: tuple 

574 args to pass to lalsimulation.SimInspiralChooseTDWaveform 

575 approximant: str 

576 lalsimulation approximant number to use when generating a waveform 

577 delta_t: float 

578 spacing between time samples 

579 f_low: float 

580 frequency to start evaluating the waveform 

581 f_ref: float, optional 

582 reference frequency 

583 LAL_parameters: LALDict 

584 LAL dictionary containing accessory parameters. Default None 

585 samples: dict 

586 dictionary of posterior samples to use when projecting the waveform 

587 onto a given detector 

588 pycbc: Bool, optional 

589 return a the waveform as a pycbc.timeseries.TimeSeries object 

590 project: str, optional 

591 name of the detector to project the waveform onto. If None, 

592 the plus and cross polarizations are returned. Default None 

593 debug: bool, optional 

594 if True, return the model object used to generate the waveform. Only 

595 used when the waveform approximant is not in LALSimulation 

596 kwargs: dict, optional 

597 all kwargs passed to _calculate_hp_hc_td 

598 """ 

599 from gwpy.timeseries import TimeSeries 

600 from astropy.units import Quantity 

601 

602 (hp, hc), model = _calculate_hp_hc_td( 

603 waveform_args, delta_t, f_low, f_ref, LAL_parameters, approximant, 

604 debug=True, **kwargs 

605 ) 

606 hp = TimeSeries(hp.data.data, dt=hp.deltaT, t0=hp.epoch) 

607 hc = TimeSeries(hc.data.data, dt=hc.deltaT, t0=hc.epoch) 

608 if pycbc: 

609 hp, hc = hp.to_pycbc(), hc.to_pycbc() 

610 if project is None and debug: 

611 return {"h_plus": hp, "h_cross": hc}, model 

612 elif project is None: 

613 return {"h_plus": hp, "h_cross": hc} 

614 ht = _project_waveform( 

615 project, hp, hc, samples["ra"], samples["dec"], samples["psi"], 

616 samples["geocent_time"] 

617 ) 

618 if "{}_time".format(project) not in samples.keys(): 

619 from pesummary.gw.conversions import time_in_each_ifo 

620 try: 

621 _detector_time = time_in_each_ifo( 

622 project, samples["ra"], samples["dec"], samples["geocent_time"] 

623 ) 

624 except Exception: 

625 logger.warning( 

626 "Unable to calculate samples for '{}_time' using the provided " 

627 "posterior samples. Unable to shift merger to merger time in " 

628 "the detector".format(project) 

629 ) 

630 if debug: 

631 return ht, model 

632 return ht 

633 else: 

634 _detector_time = samples["{}_time".format(project)] 

635 ht.times = ( 

636 Quantity(ht.times, unit="s") + Quantity(_detector_time, unit="s") 

637 ) 

638 if debug: 

639 return ht, model 

640 return ht 

641 

642 

643def _calculate_hp_hc_td( 

644 waveform_args, delta_t, f_low, f_ref, LAL_parameters, approximant, 

645 debug=False, **kwargs 

646): 

647 """Calculate the plus and cross polarizations in the time domain. 

648 If the approximant is in LALSimulation, the 

649 SimInspiralChooseTDWaveform function is used. If the approximant is 

650 not in LALSimulation, gwsignal is used 

651 

652 Parameters 

653 ---------- 

654 waveform_args: tuple 

655 args to pass to lalsimulation.SimInspiralChooseTDWaveform 

656 delta_t: float 

657 spacing between time samples 

658 f_low: float 

659 frequency to start evaluating the waveform 

660 f_ref: float, optional 

661 reference frequency 

662 LAL_parameters: LALDict 

663 LAL dictionary containing accessory parameters. Default None 

664 approximant: str 

665 lalsimulation approximant number to use when generating a waveform 

666 debug: bool, optional 

667 if True, return the model object used to generate the waveform. Only 

668 used when the waveform approximant is not in LALSimulation 

669 **kwargs: dict, optional 

670 all kwargs passed to _setup_gwsignal 

671 """ 

672 if hasattr(lalsim, approximant): 

673 approx = _lal_approximant_from_string(approximant) 

674 if lalsim.SimInspiralImplementedFDApproximants(approx): 

675 # if the waveform is defined in the frequency domain, use 

676 # SimInspiralTD as it appropiately conditions the waveform so it 

677 # is correct at f_low in the time domain 

678 _func = lalsim.SimInspiralTD 

679 else: 

680 # if the waveform is defined in the time domain, use 

681 # SimInspiralChooseTDWaveform as it doesnt taper the start of the 

682 # waveform 

683 _func = lalsim.SimInspiralChooseTDWaveform 

684 result = _func( 

685 *waveform_args, delta_t, f_low, f_ref, LAL_parameters, approx 

686 ) 

687 if debug: 

688 return result, None 

689 return result 

690 wfm_gen = _setup_gwsignal( 

691 waveform_args, f_low, f_ref, approximant, delta_t=delta_t, **kwargs 

692 ) 

693 hpc = wfm_gen.generate_td_polarizations() 

694 if debug: 

695 return hpc, wfm_gen 

696 return hpc 

697 

698 

699def _calculate_hp_hc_fd( 

700 waveform_args, delta_f, f_low, f_high, f_ref, LAL_parameters, approximant, 

701 debug=False, **kwargs 

702): 

703 """Calculate the plus and cross polarizations in the frequency domain. 

704 If the approximant is in LALSimulation, the 

705 SimInspiralChooseFDWaveform function is used. If the approximant is 

706 not in LALSimulation, gwsignal is used 

707 

708 Parameters 

709 ---------- 

710 waveform_args: tuple 

711 args to pass to lalsimulation.SimInspiralChooseFDWaveform 

712 delta_f: float 

713 spacing between frequency samples 

714 f_low: float 

715 frequency to start evaluating the waveform 

716 f_high: float, optional 

717 frequency to stop evaluating the waveform 

718 f_ref: float, optional 

719 reference frequency 

720 LAL_parameters: LALDict 

721 LAL dictionary containing accessory parameters. Default None 

722 approximant: str 

723 lalsimulation approximant number to use when generating a waveform 

724 debug: bool, optional 

725 if True, return the model object used to generate the waveform. Only 

726 used when the waveform approximant is not in LALSimulation 

727 """ 

728 if hasattr(lalsim, approximant): 

729 approx = _lal_approximant_from_string(approximant) 

730 for func in [lalsim.SimInspiralChooseFDWaveform, lalsim.SimInspiralFD]: 

731 try: 

732 result = func( 

733 *waveform_args, delta_f, f_low, f_high, f_ref, 

734 LAL_parameters, approx 

735 ) 

736 if debug: 

737 return result, None 

738 return result 

739 except Exception: 

740 continue 

741 break 

742 wfm_gen = _setup_gwsignal( 

743 waveform_args, f_low, f_ref, approximant, delta_f=delta_f, 

744 f_high=f_high, **kwargs 

745 ) 

746 hpc = wfm_gen.generate_fd_polarizations() 

747 if debug: 

748 return hpc, wfm_gen 

749 return hpc 

750 

751 

752def _setup_gwsignal( 

753 waveform_args, f_low, f_ref, approximant, delta_t=None, delta_f=None, 

754 f_high=None, **kwargs 

755): 

756 """Setup a waveform generator with gwsignal 

757 

758 Parameters 

759 ---------- 

760 waveform_args: tuple 

761 args to pass to lalsimulation.SimInspiralChoose*DWaveform 

762 f_low: float 

763 frequency to start evaluating the waveform 

764 f_ref: float, optional 

765 reference frequency 

766 approximant: str 

767 lalsimulation approximant number to use when generating a waveform 

768 delta_t: float, optional 

769 spacing between frequency samples 

770 delta_f: float, optional 

771 spacing between frequency samples 

772 f_high: float, optional 

773 frequency to stop evaluating the waveform 

774 kwargs: dict, optional 

775 all kwargs passed to _gwsignal_generator_from_string 

776 """ 

777 from lalsimulation.gwsignal.core.parameter_conventions import ( 

778 common_units_dictionary 

779 ) 

780 from astropy import units 

781 names = [ 

782 "mass1", "mass2", "spin1x", "spin1y", "spin1z", "spin2x", "spin2y", 

783 "spin2z", "distance", "inclination", "phi_ref", "longAscNodes", 

784 "eccentricity", "meanPerAno" 

785 ] 

786 params_dict = {name: waveform_args[ii] for ii, name in enumerate(names)} 

787 if delta_t is not None: 

788 params_dict["deltaT"] = delta_t 

789 if delta_f is not None: 

790 params_dict["deltaF"] = delta_f 

791 params_dict["f22_start"] = f_low 

792 params_dict["f22_ref"] = f_ref 

793 if f_high is None: 

794 params_dict["f_max"] = 0.5 / params_dict["deltaT"] 

795 else: 

796 params_dict["f_max"] = f_high 

797 wf_gen = _gwsignal_generator_from_string(approximant, **kwargs) 

798 for key, item in params_dict.items(): 

799 params_dict[key] = item * common_units_dictionary.get(key, 1) 

800 params_dict["mass1"] *= units.kg 

801 params_dict["mass2"] *= units.kg 

802 params_dict["distance"] *= units.m 

803 wfm_gen = wf_gen._generate_waveform_class(**params_dict) 

804 return wfm_gen