Coverage for pesummary/tests/conversion_test.py: 99.6%

559 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 os 

4import socket 

5import shutil 

6 

7import numpy as np 

8import h5py 

9 

10import deepdish 

11 

12from pesummary.gw.conversions import * 

13from pesummary.gw.conversions.nrutils import * 

14from pycbc import conversions 

15import pytest 

16import tempfile 

17 

18tmpdir = tempfile.TemporaryDirectory(prefix=".", dir=".").name 

19 

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

21 

22 

23def conversion_check( 

24 pesummary_function, pesummary_args, other_function, other_args, 

25 dp=8 

26): 

27 """Check that the conversions made by PESummary are the same as those 

28 from pycbc 

29 """ 

30 _pesummary = pesummary_function(*pesummary_args) 

31 _other = other_function(*other_args) 

32 assert np.testing.assert_almost_equal(_pesummary, _other, dp) is None 

33 

34 

35class TestConversions(object): 

36 def setup_method(self): 

37 class Arguments(object): 

38 mass1 = 10. 

39 mass2 = 5. 

40 mtotal = 30. 

41 mchirp = 10. 

42 q = 1. / 4. 

43 eta = 0.214 

44 iota = 0.5 

45 spin1x = 0.75 

46 spin1y = 0. 

47 spin1z = 0.5 

48 spin2x = 0. 

49 spin2y = 0. 

50 spin2z = 0.5 

51 lambda1 = 500. 

52 lambda2 = 500. 

53 lambda_tilde = 1000. 

54 

55 theta_jn = [0.5, 0.5] 

56 phi_jl = [0.3, 0.3] 

57 tilt_1 = [0.5, 0.] 

58 tilt_2 = [0., 0.] 

59 phi_12 = [0., 0.] 

60 a_1 = [0.5, 0.5] 

61 a_2 = [0., 0.] 

62 f_ref = [20., 20.] 

63 phase = [0., 0.] 

64 

65 redshift = 0.5 

66 l_distance = 500. 

67 

68 self.opts = Arguments() 

69 

70 def test_z_from_dL(self): 

71 from bilby.gw.conversion import luminosity_distance_to_redshift 

72 

73 l_distance = np.random.randint(100, 5000, 20) 

74 bilby_function = luminosity_distance_to_redshift 

75 pesummary_function = z_from_dL_exact 

76 conversion_check( 

77 pesummary_function, [l_distance], bilby_function, [l_distance] 

78 ) 

79 pesummary_function = z_from_dL_approx 

80 conversion_check( 

81 pesummary_function, [l_distance], bilby_function, [l_distance], 

82 dp=4 

83 ) 

84 

85 def test_change_of_cosmology_for_z_from_dL(self): 

86 from lalinference.bayespputils import calculate_redshift 

87 

88 l_distance = np.random.randint(100, 5000, 20) 

89 lal_redshift = calculate_redshift( 

90 np.atleast_2d(l_distance).T 

91 ).T[0] 

92 redshift = z_from_dL_exact( 

93 l_distance, cosmology="Planck15_lal" 

94 ) 

95 np.testing.assert_almost_equal(lal_redshift, redshift, 8) 

96 

97 def test_dL_from_z(self): 

98 from bilby.gw.conversion import redshift_to_luminosity_distance 

99 

100 redshift = np.random.randint(1, 5, 100) 

101 bilby_function = redshift_to_luminosity_distance 

102 pesummary_function = dL_from_z 

103 conversion_check( 

104 pesummary_function, [redshift], bilby_function, [redshift] 

105 ) 

106 

107 def test_comoving_distance_from_z(self): 

108 from bilby.gw.conversion import redshift_to_comoving_distance 

109 

110 redshift = np.random.randint(1, 5, 100) 

111 bilby_function = redshift_to_comoving_distance 

112 pesummary_function = comoving_distance_from_z 

113 conversion_check( 

114 pesummary_function, [redshift], bilby_function, [redshift] 

115 ) 

116 

117 def test_m1_source_from_m1_z(self): 

118 from bilby.gw.conversion import generate_source_frame_parameters 

119 

120 mass_1 = np.random.randint(5, 100, 100) 

121 mass_2 = np.random.randint(2, mass_1, 100) 

122 luminosity_distance = np.random.randint(100, 500, 100) 

123 sample = generate_source_frame_parameters( 

124 {"mass_1": mass_1, "mass_2": mass_2, 

125 "luminosity_distance": luminosity_distance} 

126 ) 

127 source_frame = generate_source_frame_parameters(sample) 

128 assert np.testing.assert_almost_equal( 

129 m1_source_from_m1_z(mass_1, sample["redshift"]), sample["mass_1_source"], 

130 8 

131 ) is None 

132 

133 def test_m1_m2_from_m1_source_m2_source_z(self): 

134 from bilby.gw.conversion import generate_source_frame_parameters 

135 

136 mass_1_source = np.random.randint(5, 100, 100) 

137 mass_2_source = np.random.randint(2, mass_1_source, 100) 

138 redshift = np.random.randint(1, 10, 100) 

139 luminosity_distance = dL_from_z(redshift) 

140 

141 # calculate mass_1 and mass_2 using pesummary 

142 mass_1 = m1_from_m1_source_z(mass_1_source, redshift) 

143 mass_2 = m2_from_m2_source_z(mass_2_source, redshift) 

144 # use calculated mass_1/mass_2 to calculate mass_1_source/mass_2_source using 

145 # bilby 

146 sample = generate_source_frame_parameters( 

147 {"mass_1": mass_1, "mass_2": mass_2, 

148 "luminosity_distance": luminosity_distance} 

149 ) 

150 source_frame = generate_source_frame_parameters(sample) 

151 # confirm that bilby's mass_1_source/mass_2_source is the same as 

152 # mass_1_source/mass_2_source that pesummary used 

153 np.testing.assert_almost_equal(sample["mass_1_source"], mass_1_source, 6) 

154 np.testing.assert_almost_equal(sample["mass_2_source"], mass_2_source, 6) 

155 

156 def test_m2_source_from_m2_z(self): 

157 from bilby.gw.conversion import generate_source_frame_parameters 

158 

159 mass_1 = np.random.randint(5, 100, 100) 

160 mass_2 = np.random.randint(2, mass_1, 100) 

161 luminosity_distance = np.random.randint(100, 500, 100) 

162 sample = generate_source_frame_parameters( 

163 {"mass_1": mass_1, "mass_2": mass_2, 

164 "luminosity_distance": luminosity_distance} 

165 ) 

166 source_frame = generate_source_frame_parameters(sample) 

167 assert np.testing.assert_almost_equal( 

168 m2_source_from_m2_z(mass_1, sample["redshift"]), sample["mass_1_source"], 

169 8 

170 ) is None 

171 

172 def test_m_total_source_from_mtotal_z(self): 

173 from bilby.gw.conversion import generate_source_frame_parameters 

174 

175 total_mass = np.random.randint(5, 100, 100) 

176 luminosity_distance = np.random.randint(100, 500, 100) 

177 sample = generate_source_frame_parameters( 

178 {"total_mass": total_mass, "luminosity_distance": luminosity_distance} 

179 ) 

180 source_frame = generate_source_frame_parameters(sample) 

181 assert np.testing.assert_almost_equal( 

182 m_total_source_from_mtotal_z(total_mass, sample["redshift"]), 

183 sample["total_mass_source"], 8 

184 ) is None 

185 

186 def test_m_total_from_mtotal_source_z(self): 

187 total_mass_source, redshift = 20., self.opts.redshift 

188 m_total = mtotal_from_mtotal_source_z(total_mass_source, redshift) 

189 assert np.round(m_total, 4) == self.opts.mtotal 

190 

191 def test_mchirp_source_from_mchirp_z(self): 

192 from bilby.gw.conversion import generate_source_frame_parameters 

193 

194 chirp_mass = np.random.randint(5, 100, 100) 

195 luminosity_distance = np.random.randint(100, 500, 100) 

196 sample = generate_source_frame_parameters( 

197 {"chirp_mass": chirp_mass, 

198 "luminosity_distance": luminosity_distance} 

199 ) 

200 source_frame = generate_source_frame_parameters(sample) 

201 assert np.testing.assert_almost_equal( 

202 mchirp_source_from_mchirp_z(chirp_mass, sample["redshift"]), 

203 sample["chirp_mass_source"], 

204 8 

205 ) is None 

206 

207 def test_mchirp_from_mchirp_source_z(self): 

208 mchirp_source, redshift = 20./3., self.opts.redshift 

209 mchirp = mchirp_from_mchirp_source_z(mchirp_source, redshift) 

210 assert np.round(mchirp, 4) == self.opts.mchirp 

211 

212 def test_mchirp_from_m1_m2(self): 

213 mass1 = np.random.randint(5, 100, 100) 

214 mass2 = np.random.randint(2, mass1, 100) 

215 pycbc_function = conversions.mchirp_from_mass1_mass2 

216 pesummary_function = mchirp_from_m1_m2 

217 conversion_check( 

218 pesummary_function, [mass1, mass2], pycbc_function, [mass1, mass2] 

219 ) 

220 

221 def test_m_total_from_m1_m2(self): 

222 mass1 = np.random.randint(5, 100, 100) 

223 mass2 = np.random.randint(2, mass1, 100) 

224 pycbc_function = conversions.mtotal_from_mass1_mass2 

225 pesummary_function = m_total_from_m1_m2 

226 conversion_check( 

227 pesummary_function, [mass1, mass2], pycbc_function, [mass1, mass2] 

228 ) 

229 

230 def test_m1_from_mchirp_q(self): 

231 mchirp = np.random.randint(5, 100, 100) 

232 q = np.random.random(100) 

233 mchirp, q = self.opts.mchirp, self.opts.q 

234 pycbc_function = conversions.mass1_from_mchirp_q 

235 pesummary_function = m1_from_mchirp_q 

236 conversion_check( 

237 pesummary_function, [mchirp, q], pycbc_function, [mchirp, 1./q] 

238 ) 

239 

240 def test_m2_from_mchirp_q(self): 

241 mchirp = np.random.randint(5, 100, 100) 

242 q = np.random.random(100) 

243 pycbc_function = conversions.mass2_from_mchirp_q 

244 pesummary_function = m2_from_mchirp_q 

245 conversion_check( 

246 pesummary_function, [mchirp, q], pycbc_function, [mchirp, 1./q] 

247 ) 

248 

249 def test_m1_from_mtotal_q(self): 

250 mtotal = np.random.randint(5, 100, 100) 

251 q = np.random.random(100) 

252 pycbc_function = conversions.mass1_from_mtotal_q 

253 pesummary_function = m1_from_mtotal_q 

254 conversion_check( 

255 pesummary_function, [mtotal, q], pycbc_function, [mtotal, 1./q] 

256 ) 

257 

258 def test_m2_from_mtotal_q(self): 

259 mtotal = np.random.randint(5, 100, 100) 

260 q = np.random.random(100) 

261 pycbc_function = conversions.mass2_from_mtotal_q 

262 pesummary_function = m2_from_mtotal_q 

263 conversion_check( 

264 pesummary_function, [mtotal, q], pycbc_function, [mtotal, 1./q] 

265 ) 

266 

267 def test_eta_from_m1_m2(self): 

268 mass1 = np.random.randint(5, 100, 100) 

269 mass2 = np.random.randint(2, mass1, 100) 

270 pycbc_function = conversions.eta_from_mass1_mass2 

271 pesummary_function = eta_from_m1_m2 

272 conversion_check( 

273 pesummary_function, [mass1, mass2], pycbc_function, [mass1, mass2] 

274 ) 

275 

276 def test_q_from_m1_m2(self): 

277 mass1 = np.random.randint(5, 100, 100) 

278 mass2 = np.random.randint(2, mass1, 100) 

279 pycbc_function = conversions.invq_from_mass1_mass2 

280 pesummary_function = q_from_m1_m2 

281 conversion_check( 

282 pesummary_function, [mass1, mass2], pycbc_function, [mass1, mass2] 

283 ) 

284 

285 def test_q_from_eta(self): 

286 from bilby.gw.conversion import symmetric_mass_ratio_to_mass_ratio 

287 

288 eta = np.random.uniform(0, 0.25, 100) 

289 bilby_function = symmetric_mass_ratio_to_mass_ratio 

290 pesummary_function = q_from_eta 

291 conversion_check( 

292 pesummary_function, [eta], bilby_function, [eta] 

293 ) 

294 

295 def test_mchirp_from_mtotal_q(self): 

296 mtotal, q = self.opts.mtotal, self.opts.q 

297 mchirp = mchirp_from_mtotal_q(mtotal, q) 

298 assert np.round(mchirp, 4) == 9.9906 

299 

300 def test_chi_p(self): 

301 mass1 = np.random.randint(5, 100, 100) 

302 mass2 = np.random.randint(2, mass1, 100) 

303 spin1_mag = np.random.random(100) 

304 spin1_ang = np.random.random(100) 

305 spin2_mag = np.random.random(100) 

306 spin2_ang = np.random.random(100) 

307 spin1x = spin1_mag * np.cos(spin1_ang) 

308 spin1y = spin1_mag * np.sin(spin1_ang) 

309 spin2x = spin2_mag * np.cos(spin2_ang) 

310 spin2y = spin2_mag * np.sin(spin2_ang) 

311 pycbc_function = conversions.chi_p 

312 pesummary_function = chi_p 

313 conversion_check( 

314 pesummary_function, [mass1, mass2, spin1x, spin1y, spin2x, spin2y], 

315 pycbc_function, [mass1, mass2, spin1x, spin1y, spin2x, spin2y] 

316 ) 

317 

318 from lalsimulation import SimPhenomUtilsChiP 

319 

320 mass1, mass2 = self.opts.mass1, self.opts.mass2 

321 spin1x, spin1y = self.opts.spin1x, self.opts.spin1y 

322 spin1z, spin2x = self.opts.spin1z, self.opts.spin2x 

323 spin2y, spin2z = self.opts.spin2y, self.opts.spin2z 

324 chi_p_value = chi_p(mass1, mass2, spin1x, spin1y, spin2y, spin2y) 

325 assert chi_p_value == 0.75 

326 for i in range(100): 

327 mass_1 = np.random.randint(10, 100) 

328 mass_2 = np.random.randint(5, mass_1) 

329 spin1 = np.random.random(3) 

330 norm = np.sqrt(np.sum(np.square(spin1))) 

331 spin1 /= norm 

332 spin2 = np.random.random(3) 

333 norm = np.sqrt(np.sum(np.square(spin2))) 

334 spin2 /= norm 

335 chi_p_value = chi_p( 

336 mass_1, mass_2, spin1[0], spin1[1], spin2[0], spin2[1] 

337 ) 

338 lal_value = SimPhenomUtilsChiP( 

339 mass_1, mass_2, spin1[0], spin1[1], spin2[0], spin2[1] 

340 ) 

341 assert np.testing.assert_almost_equal(chi_p_value, lal_value, 9) is None 

342 

343 def test_chi_eff(self): 

344 mass1 = np.random.randint(5, 100, 100) 

345 mass2 = np.random.randint(2, mass1, 100) 

346 spin1z = np.random.uniform(-1, 1, 100) 

347 spin2z = np.random.uniform(-1, 1, 100) 

348 pycbc_function = conversions.chi_eff 

349 pesummary_function = chi_eff 

350 conversion_check( 

351 pesummary_function, [mass1, mass2, spin1z, spin2z], pycbc_function, 

352 [mass1, mass2, spin1z, spin2z] 

353 ) 

354 

355 def test_phi_12_from_phi1_phi2(self): 

356 data = phi_12_from_phi1_phi2(0.2, 0.5) 

357 assert data == 0.3 

358 data = phi_12_from_phi1_phi2(0.5, 0.2) 

359 rounded_data = np.round(data, 2) 

360 assert rounded_data == 5.98 

361 data = phi_12_from_phi1_phi2(np.array([0.5, 0.2]), np.array([0.3, 0.7])) 

362 rounded_data = np.round(data, 2) 

363 assert all(i == j for i,j in zip(rounded_data, [6.08, 0.5])) 

364 

365 def test_phi_from_spins(self): 

366 def cart2sph(x, y): 

367 return np.fmod(2 * np.pi + np.arctan2(y,x), 2 * np.pi) 

368 assert phi1_from_spins(0.5, 0.2) == cart2sph(0.5, 0.2) 

369 assert phi2_from_spins(0.1, 0.5) == cart2sph(0.1, 0.5) 

370 

371 def test_spin_angles(self): 

372 from lalsimulation import SimInspiralTransformPrecessingWvf2PE 

373 

374 mass1 = np.random.uniform(5., 100., 100) 

375 mass2 = np.random.uniform(2., mass1, 100) 

376 inc = np.random.uniform(0, np.pi, 100) 

377 spin1_mag = np.random.random(100) 

378 spin1_ang = np.random.random(100) 

379 spin2_mag = np.random.random(100) 

380 spin2_ang = np.random.random(100) 

381 spin1x = spin1_mag * np.cos(spin1_ang) 

382 spin1y = spin1_mag * np.sin(spin1_ang) 

383 spin2x = spin2_mag * np.cos(spin2_ang) 

384 spin2y = spin2_mag * np.sin(spin2_ang) 

385 spin1z = np.random.random(100) - (spin1x**2 + spin1y**2)**0.5 

386 spin2z = np.random.random(100) - (spin1x**2 + spin1y**2)**0.5 

387 f_ref = [20.0] * len(mass1) 

388 phase = [0.4] * len(mass1) 

389 lalsimulation_function = SimInspiralTransformPrecessingWvf2PE 

390 pesummary_function = spin_angles 

391 for ii in np.arange(len(mass1)): 

392 conversion_check( 

393 pesummary_function, 

394 [mass1[ii], mass2[ii], inc[ii], spin1x[ii], spin1y[ii], 

395 spin1z[ii], spin2x[ii], spin2y[ii], spin2z[ii], f_ref[ii], 

396 phase[ii]], 

397 lalsimulation_function, 

398 [inc[ii], spin1x[ii], spin1y[ii], spin1z[ii], spin2x[ii], 

399 spin2y[ii], spin2z[ii], mass1[ii], mass2[ii], f_ref[ii], 

400 phase[ii]] 

401 ) 

402 

403 def test_component_spins(self): 

404 from bilby.gw.conversion import bilby_to_lalsimulation_spins 

405 from lal import MSUN_SI 

406 

407 mass1 = np.random.uniform(5., 100., 100) 

408 mass2 = np.random.uniform(2., mass1, 100) 

409 theta_jn = np.random.uniform(0, np.pi, 100) 

410 phi_jl = np.random.uniform(0, np.pi, 100) 

411 phi_12 = np.random.uniform(0, np.pi, 100) 

412 a_1 = np.random.uniform(0, 1, 100) 

413 a_2 = np.random.uniform(0, 1, 100) 

414 tilt_1 = np.random.uniform(0, np.pi, 100) 

415 tilt_2 = np.random.uniform(0, np.pi, 100) 

416 f_ref = [20.] * len(mass1) 

417 phase = [0.5] * len(mass2) 

418 

419 bilby_function = bilby_to_lalsimulation_spins 

420 pesummary_function = component_spins 

421 for ii in np.arange(len(mass1)): 

422 conversion_check( 

423 pesummary_function, 

424 [theta_jn[ii], phi_jl[ii], tilt_1[ii], tilt_1[ii], phi_12[ii], 

425 a_1[ii], a_2[ii], mass1[ii], mass2[ii], f_ref[ii], phase[ii]], 

426 bilby_function, 

427 [theta_jn[ii], phi_jl[ii], tilt_1[ii], tilt_1[ii], phi_12[ii], 

428 a_1[ii], a_2[ii], mass1[ii]*MSUN_SI, mass2[ii]*MSUN_SI, 

429 f_ref[ii], phase[ii]] 

430 ) 

431 

432 def test_time_in_each_ifo(self): 

433 from pycbc.detector import Detector 

434 from lal import TimeDelayFromEarthCenter 

435 from lalsimulation import DetectorPrefixToLALDetector 

436 

437 optimal_ra, optimal_dec = -0.2559168059473027, 0.81079526383 

438 time = time_in_each_ifo("H1", optimal_ra, optimal_dec, 0) 

439 light_time = 6371*10**3 / (3.0*10**8) 

440 assert -np.round(light_time, 4) == np.round(time, 4) 

441 

442 ra = np.random.uniform(-np.pi, np.pi, 100) 

443 dec = np.random.uniform(0, 2*np.pi, 100) 

444 time = np.random.uniform(10000, 20000, 100) 

445 H1_time = time_in_each_ifo("H1", ra, dec, time) 

446 pycbc_time = time + Detector("H1").time_delay_from_earth_center( 

447 ra, dec, time 

448 ) 

449 lal_time = time + np.array([TimeDelayFromEarthCenter( 

450 DetectorPrefixToLALDetector('H1').location, ra[ii], dec[ii], 

451 time[ii]) for ii in range(len(ra)) 

452 ]) 

453 difference = np.abs(lal_time - pycbc_time) 

454 dp = np.floor(np.abs(np.log10(np.max(difference)))) 

455 assert np.testing.assert_almost_equal(H1_time, pycbc_time, dp) is None 

456 assert np.testing.assert_almost_equal(H1_time, lal_time, dp) is None 

457 

458 

459 def test_lambda_tilde_from_lambda1_lambda2(self): 

460 lambda1 = np.random.uniform(0, 5000, 100) 

461 lambda2 = np.random.uniform(0, 5000, 100) 

462 mass1 = np.random.randint(5, 100, 100) 

463 mass2 = np.random.randint(2, mass1, 100) 

464 pycbc_function = conversions.lambda_tilde 

465 pesummary_function = lambda_tilde_from_lambda1_lambda2 

466 conversion_check( 

467 pesummary_function, [lambda1, lambda2, mass1, mass2], 

468 pycbc_function, [mass1, mass2, lambda1, lambda2] 

469 ) 

470 

471 def test_delta_lambda_from_lambda1_lambda2(self): 

472 from bilby.gw.conversion import lambda_1_lambda_2_to_delta_lambda_tilde 

473 

474 lambda1 = np.random.uniform(0, 5000, 100) 

475 lambda2 = np.random.uniform(0, 5000, 100) 

476 mass1 = np.random.randint(5, 100, 100) 

477 mass2 = np.random.randint(2, mass1, 100) 

478 conversion_check( 

479 delta_lambda_from_lambda1_lambda2, 

480 [lambda1, lambda2, mass1, mass2], 

481 lambda_1_lambda_2_to_delta_lambda_tilde, 

482 [lambda1, lambda2, mass1, mass2] 

483 ) 

484 

485 def test_lambda1_from_lambda_tilde(self): 

486 from bilby.gw.conversion import lambda_tilde_to_lambda_1_lambda_2 

487 

488 mass1 = np.random.randint(5, 100, 100) 

489 mass2 = np.random.randint(2, mass1, 100) 

490 lambda_tilde = np.random.uniform(-100, 100, 100) 

491 lambda_1 = lambda_tilde_to_lambda_1_lambda_2( 

492 lambda_tilde, mass1, mass2 

493 )[0] 

494 lambda1 = lambda1_from_lambda_tilde(lambda_tilde, mass1, mass2) 

495 assert np.testing.assert_almost_equal(lambda1, lambda_1) is None 

496 #assert np.round(lambda1, 4) == 192.8101 

497 

498 def test_lambda2_from_lambda1(self): 

499 from bilby.gw.conversion import convert_to_lal_binary_neutron_star_parameters 

500 

501 mass1 = np.random.uniform(5, 50, 100) 

502 mass2 = np.random.uniform(2, mass1, 100) 

503 lambda_1 = np.random.uniform(0, 5000, 100) 

504 sample = {"mass_1": mass1, "mass_2": mass2, "lambda_1": lambda_1} 

505 convert = convert_to_lal_binary_neutron_star_parameters(sample)[0] 

506 lambda2 = lambda2_from_lambda1(lambda_1, mass1, mass2) 

507 diff = np.abs(lambda2 - convert["lambda_2"]) 

508 ind = np.argmax(diff) 

509 assert np.testing.assert_almost_equal(lambda2, convert["lambda_2"], 5) is None 

510 

511 def test_network_snr(self): 

512 snr_H1 = snr_L1 = snr_V1 = np.array([2., 3.]) 

513 assert network_snr([snr_H1[0], snr_L1[0], snr_V1[0]]) == np.sqrt(3) * 2 

514 print(snr_H1) 

515 network = network_snr([snr_H1, snr_L1, snr_V1]) 

516 print(network) 

517 assert network[0] == np.sqrt(3) * 2 

518 assert network[1] == np.sqrt(3) * 3 

519 

520 def test_network_matched_filter_snr(self): 

521 """Samples taken from a lalinference result file 

522 """ 

523 snr_mf_H1 = 7.950207935574794 

524 snr_mf_L1 = 19.232672412819483 

525 snr_mf_V1 = 3.666438738845737 

526 snr_opt_H1 = 9.668043620320788 

527 snr_opt_L1 = 19.0826463504282 

528 snr_opt_V1 = 3.5578582036515236 

529 network = network_matched_filter_snr( 

530 [snr_mf_H1, snr_mf_L1, snr_mf_V1], 

531 [snr_opt_H1, snr_opt_L1, snr_opt_V1] 

532 ) 

533 np.testing.assert_almost_equal(network, 21.06984787727566) 

534 network = network_matched_filter_snr( 

535 [[snr_mf_H1] * 2, [snr_mf_L1] * 2, [snr_mf_V1] * 2], 

536 [[snr_opt_H1] * 2, [snr_opt_L1] * 2, [snr_opt_V1] * 2] 

537 ) 

538 np.testing.assert_almost_equal( 

539 network, [21.06984787727566] * 2 

540 ) 

541 snr_mf_H1 = 7.950207935574794 - 1.004962343498161 * 1j 

542 snr_mf_L1 = 19.232672412819483 - 0.4646531569951501 * 1j 

543 snr_mf_V1 = 3.666438738845737 - 0.08177741915398137 * 1j 

544 network = network_matched_filter_snr( 

545 [snr_mf_H1, snr_mf_L1, snr_mf_V1], 

546 [snr_opt_H1, snr_opt_L1, snr_opt_V1] 

547 ) 

548 np.testing.assert_almost_equal(network, 21.06984787727566) 

549 

550 def test_full_conversion(self): 

551 from pesummary.utils.array import Array 

552 from pesummary.gw.conversions import convert 

553 from bilby.gw.conversion import convert_to_lal_binary_black_hole_parameters 

554 from pandas import DataFrame 

555 

556 dictionary = { 

557 "mass_1": [10.], 

558 "mass_2": [2.], 

559 "a_1": [0.2], 

560 "a_2": [0.2], 

561 "cos_theta_jn": [0.5], 

562 "cos_tilt_1": [0.2], 

563 "cos_tilt_2": [0.2], 

564 "luminosity_distance": [0.2], 

565 "geocent_time": [0.2], 

566 "ra": [0.2], 

567 "dec": [0.2], 

568 "psi": [0.2], 

569 "phase": [0.5], 

570 "phi_12": [0.7], 

571 "phi_jl": [0.25], 

572 "H1_matched_filter_abs_snr": [10.0], 

573 "H1_matched_filter_snr_angle": [0.], 

574 "H1_matched_filter_snr": [10.0], 

575 "H1_optimal_snr": [10.0] 

576 } 

577 data = convert(dictionary) 

578 true_params = [ 

579 'mass_1', 'mass_2', 'a_1', 'a_2', 'cos_theta_jn', 'cos_tilt_1', 

580 'cos_tilt_2', 'luminosity_distance', 'geocent_time', 'ra', 'dec', 

581 'psi', 'phase', 'phi_12', 'phi_jl', 'H1_matched_filter_abs_snr', 

582 'H1_matched_filter_snr_angle', 'H1_matched_filter_snr', 'theta_jn', 

583 'tilt_1', 'tilt_2', 'mass_ratio', 'inverted_mass_ratio', 

584 'total_mass', 'chirp_mass', 'symmetric_mass_ratio', 'iota', 

585 'spin_1x', 'spin_1y', 'spin_1z', 'spin_2x', 'spin_2y', 'spin_2z', 

586 'phi_1', 'phi_2', 'chi_eff', 'chi_p', 'final_spin_non_evolved', 

587 'peak_luminosity_non_evolved', 'final_mass_non_evolved', 'redshift', 

588 'comoving_distance', 'mass_1_source', 'mass_2_source', 

589 'total_mass_source', 'chirp_mass_source', 

590 'final_mass_source_non_evolved', 'radiated_energy_non_evolved', 

591 'network_matched_filter_snr', 'cos_iota' 

592 ] 

593 assert all(i in data.parameters for i in true_params) 

594 assert len(data.parameters) == len(set(data.parameters)) 

595 true = { 

596 'mass_1': Array(dictionary["mass_1"]), 

597 'mass_2': Array(dictionary["mass_2"]), 

598 'a_1': Array(dictionary["a_1"]), 

599 'a_2': Array(dictionary["a_2"]), 

600 'cos_theta_jn': Array(dictionary["cos_theta_jn"]), 

601 'cos_tilt_1': Array(dictionary["cos_tilt_1"]), 

602 'cos_tilt_2': Array(dictionary["cos_tilt_2"]), 

603 'luminosity_distance': Array(dictionary["luminosity_distance"]), 

604 'geocent_time': Array(dictionary["geocent_time"]), 

605 'ra': Array(dictionary["ra"]), 'dec': Array(dictionary["dec"]), 

606 'psi': Array(dictionary["psi"]), 'phase': Array(dictionary["phase"]), 

607 'phi_12': Array(dictionary["phi_12"]), 

608 'phi_jl': Array(dictionary["phi_jl"]), 

609 'H1_matched_filter_abs_snr': Array(dictionary["H1_matched_filter_abs_snr"]), 

610 'H1_matched_filter_snr_angle': Array(dictionary["H1_matched_filter_snr_angle"]), 

611 'H1_matched_filter_snr': Array(dictionary["H1_matched_filter_snr"]), 

612 'H1_optimal_snr': Array(dictionary["H1_optimal_snr"]), 

613 'theta_jn': Array(np.arccos(dictionary["cos_theta_jn"])), 

614 'tilt_1': Array(np.arccos(dictionary["cos_tilt_1"])), 

615 'tilt_2': Array(np.arccos(dictionary["cos_tilt_2"])), 

616 'mass_ratio': Array([dictionary["mass_2"][0] / dictionary["mass_1"][0]]), 

617 'inverted_mass_ratio': Array([dictionary["mass_1"][0] / dictionary["mass_2"][0]]), 

618 'total_mass': Array([dictionary["mass_1"][0] + dictionary["mass_2"][0]]), 

619 'chirp_mass': Array([3.67097772]), 

620 'symmetric_mass_ratio': Array([0.13888889]), 

621 'iota': Array([1.01719087]), 'spin_1x': Array([-0.18338713]), 

622 'spin_1y': Array([0.06905911]), 'spin_1z': Array([0.04]), 

623 'spin_2x': Array([-0.18475131]), 'spin_2y': Array([-0.06532192]), 

624 'spin_2z': Array([0.04]), 'phi_1': Array([2.78144141]), 

625 'phi_2': Array([3.48144141]), 'chi_eff': Array([0.04]), 

626 'chi_p': Array([0.19595918]), 

627 'final_spin_non_evolved': Array([0.46198316]), 

628 'peak_luminosity_non_evolved': Array([1.01239394]), 

629 'final_mass_non_evolved': Array([11.78221674]), 

630 'redshift': Array([4.5191183e-05]), 

631 'comoving_distance': Array([0.19999755]), 

632 'mass_1_source': Array([9.99954811]), 

633 'mass_2_source': Array([1.99990962]), 

634 'total_mass_source': Array([11.99945773]), 

635 'chirp_mass_source': Array([3.67081183]), 

636 'final_mass_source_non_evolved': Array([11.78168431]), 

637 'radiated_energy_non_evolved': Array([0.21777342]), 

638 'network_matched_filter_snr': Array([10.0]), 

639 'cos_iota': Array([0.52575756]) 

640 } 

641 for key in true.keys(): 

642 np.testing.assert_almost_equal( 

643 true[key][0], data[key][0], 5 

644 ) 

645 convert = convert_to_lal_binary_black_hole_parameters(dictionary)[0] 

646 for key, item in convert.items(): 

647 assert np.testing.assert_almost_equal( 

648 item, true[key], 5 

649 ) is None 

650 

651 def test_remove_parameter(self): 

652 from pesummary.gw.conversions import convert 

653 

654 dictionary = { 

655 "mass_1": np.random.uniform(5, 100, 100), 

656 "mass_ratio": [0.1] * 100 

657 } 

658 dictionary["mass_2"] = np.random.uniform(2, dictionary["mass_1"], 100) 

659 incorrect_mass_ratio = convert(dictionary) 

660 data = convert(dictionary, regenerate=["mass_ratio"]) 

661 assert all(i != j for i, j in zip( 

662 incorrect_mass_ratio["mass_ratio"], data["mass_ratio"] 

663 )) 

664 assert np.testing.assert_almost_equal( 

665 data["mass_ratio"], 

666 q_from_m1_m2(dictionary["mass_1"], dictionary["mass_2"]), 8 

667 ) is None 

668 

669 

670class TestPrecessingSNR(object): 

671 """Test the precessing_snr conversion 

672 """ 

673 def setup_method(self): 

674 """Setup the testing class 

675 """ 

676 np.random.seed(1234) 

677 self.n_samples = 20 

678 self.approx = "IMRPhenomPv2" 

679 self.mass_1 = np.random.uniform(20, 100, self.n_samples) 

680 self.mass_2 = np.random.uniform(5, self.mass_1, self.n_samples) 

681 self.a_1 = np.random.uniform(0, 1, self.n_samples) 

682 self.a_2 = np.random.uniform(0, 1, self.n_samples) 

683 self.tilt_1 = np.arccos(np.random.uniform(-1, 1, self.n_samples)) 

684 self.tilt_2 = np.arccos(np.random.uniform(-1, 1, self.n_samples)) 

685 self.phi_12 = np.random.uniform(0, 1, self.n_samples) 

686 self.theta_jn = np.arccos(np.random.uniform(-1, 1, self.n_samples)) 

687 self.phi_jl = np.random.uniform(0, 1, self.n_samples) 

688 self.f_low = [20.] * self.n_samples 

689 self.f_final = [1024.] * self.n_samples 

690 self.phase = np.random.uniform(0, 1, self.n_samples) 

691 self.distance = np.random.uniform(100, 500, self.n_samples) 

692 self.ra = np.random.uniform(0, np.pi, self.n_samples) 

693 self.dec = np.arccos(np.random.uniform(-1, 1, self.n_samples)) 

694 self.psi_l = np.random.uniform(0, 1, self.n_samples) 

695 self.time = [10000.] * self.n_samples 

696 self.beta = opening_angle( 

697 self.mass_1, self.mass_2, self.phi_jl, self.tilt_1, 

698 self.tilt_2, self.phi_12, self.a_1, self.a_2, 

699 [20.] * self.n_samples, self.phase 

700 ) 

701 self.spin_1z = self.a_1 * np.cos(self.tilt_1) 

702 self.spin_2z = self.a_2 * np.cos(self.tilt_2) 

703 self.psi_J = psi_J(self.psi_l, self.theta_jn, self.phi_jl, self.beta) 

704 

705 def test_harmonic_overlap(self): 

706 """Test that the sum of 5 precessing harmonics matches exactly to the 

707 original precessing waveform. 

708 """ 

709 from pycbc import pnutils 

710 from pycbc.psd import aLIGOZeroDetHighPower 

711 from pycbc.detector import Detector 

712 from pesummary.gw.conversions.snr import ( 

713 _calculate_precessing_harmonics, _dphi, 

714 _make_waveform_from_precessing_harmonics 

715 ) 

716 from pesummary.gw.waveform import fd_waveform 

717 

718 for i in range(self.n_samples): 

719 duration = pnutils.get_imr_duration( 

720 self.mass_1[i], self.mass_2[i], self.spin_1z[i], 

721 self.spin_2z[i], self.f_low[i], "IMRPhenomD" 

722 ) 

723 t_len = 2**np.ceil(np.log2(duration) + 1) 

724 df = 1./t_len 

725 flen = int(self.f_final[i] / df) + 1 

726 aLIGOpsd = aLIGOZeroDetHighPower(flen, df, self.f_low[i]) 

727 psd = aLIGOpsd 

728 h = fd_waveform( 

729 { 

730 "theta_jn": self.theta_jn[i], "phase": self.phase[i], 

731 "phi_jl": self.phi_jl[i], "psi": self.psi_l[i], 

732 "mass_1": self.mass_1[i], "mass_2": self.mass_2[i], 

733 "tilt_1": self.tilt_1[i], "tilt_2": self.tilt_2[i], 

734 "phi_12": self.phi_12[i], "a_1": self.a_1[i], 

735 "a_2": self.a_2[i], "luminosity_distance": self.distance[i], 

736 "ra": self.ra[i], "dec": self.dec[i], 

737 "geocent_time": self.time[i] 

738 }, self.approx, df, self.f_low[i], self.f_final[i], 

739 f_ref=self.f_low[i], flen=flen, pycbc=True, project="L1" 

740 ) 

741 harmonics = _calculate_precessing_harmonics( 

742 self.mass_1[i], self.mass_2[i], self.a_1[i], 

743 self.a_2[i], self.tilt_1[i], self.tilt_2[i], 

744 self.phi_12[i], self.beta[i], self.distance[i], 

745 approx=self.approx, f_final=self.f_final[i], 

746 flen=flen, f_ref=self.f_low[i], f_low=self.f_low[i], 

747 df=df, harmonics=[0, 1, 2, 3, 4] 

748 ) 

749 dphi = _dphi( 

750 self.theta_jn[i], self.phi_jl[i], self.beta[i] 

751 ) 

752 f_plus_j, f_cross_j = Detector("L1").antenna_pattern( 

753 self.ra[i], self.dec[i], self.psi_J[i], self.time[i] 

754 ) 

755 h_all = _make_waveform_from_precessing_harmonics( 

756 harmonics, self.theta_jn[i], self.phi_jl[i], 

757 self.phase[i] - dphi, f_plus_j, f_cross_j 

758 ) 

759 overlap = compute_the_overlap( 

760 h, h_all, psd, low_frequency_cutoff=self.f_low[i], 

761 high_frequency_cutoff=self.f_final[i], normalized=True 

762 ) 

763 np.testing.assert_almost_equal(np.abs(overlap), 1.0) 

764 np.testing.assert_almost_equal(np.angle(overlap), 0.0) 

765 

766 def test_precessing_snr(self): 

767 """Test the pesummary.gw.conversions.snr.precessing_snr function 

768 """ 

769 # Use default PSD 

770 rho_p = precessing_snr( 

771 self.mass_1, self.mass_2, self.beta, self.psi_J, self.a_1, self.a_2, 

772 self.tilt_1, self.tilt_2, self.phi_12, self.theta_jn, 

773 self.ra, self.dec, self.time, self.phi_jl, self.distance, 

774 self.phase, f_low=self.f_low, spin_1z=self.spin_1z, 

775 spin_2z=self.spin_2z, multi_process=1, debug=False, df=1./8 

776 ) 

777 assert len(rho_p) == len(self.mass_1) 

778 np.testing.assert_almost_equal( 

779 rho_p, [ 

780 0.68388587, 4.44970478, 1.50271423, 16.38278551, 8.36573961, 

781 10.76045275, 5.56389142, 38.75092545, 19.04936648, 

782 46.08235298, 11.04476225, 22.15809274, 21.83931449, 

783 0.52940244, 18.51671749, 60.36654189, 20.90566175, 7.59963947, 

784 28.81494433, 4.80448464 

785 ] 

786 ) 

787 

788 

789class TestMultipoleSNR(TestPrecessingSNR): 

790 """Test the multipole_snr conversion 

791 """ 

792 def setup_method(self): 

793 super(TestMultipoleSNR, self).setup_method() 

794 

795 @pytest.mark.skip(reason="Inherited test") 

796 def test_harmonic_overlap(self): 

797 pass 

798 

799 @pytest.mark.skip(reason="Inherited test") 

800 def test_precessing_snr(self): 

801 pass 

802 

803 def test_multipole_snr(self): 

804 """Test the pesummary.gw.conversions.multipole_snr function 

805 """ 

806 rho = multipole_snr( 

807 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z, self.psi_l, 

808 self.theta_jn, self.ra, self.dec, self.time, self.distance, 

809 self.phase, multi_process=1, df=1./8, multipole=[21, 33, 44] 

810 ) 

811 assert rho.shape[0] == 3 

812 np.testing.assert_almost_equal( 

813 rho[0], [ 

814 0.65751988, 2.1414448, 1.92966995, 1.36583827, 0.91202824, 

815 3.76485092, 7.65392604, 1.16167184, 10.25460489, 9.33561953, 

816 0.21747679, 4.44893768, 1.4904289, 4.70318423, 2.76921055, 

817 1.90083035, 3.42330456, 1.12028809, 15.47861815, 0.15595173] 

818 ) 

819 np.testing.assert_almost_equal( 

820 rho[1], [ 

821 2.20813922, 5.19271057, 7.4048816, 3.81037071, 1.62958146, 

822 4.28125682, 22.54228317, 14.46282899, 21.05195085, 26.81466547, 

823 2.33247145, 11.60509714, 6.4064751, 16.00204021, 6.21510341, 

824 14.83850082, 12.07577107, 0.7150659, 14.36457899, 3.02642992 

825 ] 

826 ) 

827 np.testing.assert_almost_equal( 

828 rho[2], [ 

829 0.18102321, 2.20770255, 0.93261726, 1.7573492, 4.60563054, 

830 3.2601658, 9.10279199, 13.04963478, 9.7026812, 15.3289052, 

831 1.58697352, 5.36571405, 8.15103903, 4.51177119, 4.90159264, 

832 11.23723456, 4.3787358, 1.02301209, 7.38082088, 9.24657289 

833 ] 

834 ) 

835 with pytest.raises(ValueError): 

836 rho = multipole_snr( 

837 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z, self.psi_l, 

838 self.theta_jn, self.ra, self.dec, self.time, self.distance, 

839 self.phase, multi_process=1, df=1./8, multipole=[21, 33, 44, 55] 

840 ) 

841 

842 

843class TestNRutils(object): 

844 

845 def setup_method(self): 

846 self.mass_1 = 100 

847 self.mass_2 = 5 

848 self.total_mass = m_total_from_m1_m2(self.mass_1, self.mass_2) 

849 self.eta = eta_from_m1_m2(self.mass_1, self.mass_2) 

850 self.spin_1z = 0.3 

851 self.spin_2z = 0.5 

852 self.chi_eff = chi_eff( 

853 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z 

854 ) 

855 self.final_spin = bbh_final_spin_non_precessing_Healyetal( 

856 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z, 

857 version="2016" 

858 ) 

859 

860 def test_bbh_peak_luminosity_non_precessing_Healyetal(self): 

861 from lalinference.imrtgr.nrutils import \ 

862 bbh_peak_luminosity_non_precessing_Healyetal as lal_Healyetal 

863 

864 assert np.round( 

865 bbh_peak_luminosity_non_precessing_Healyetal( 

866 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z 

867 ), 8 

868 ) == np.round( 

869 lal_Healyetal( 

870 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z 

871 ), 8 

872 ) 

873 

874 def test_bbh_peak_luminosity_non_precessing_T1600018(self): 

875 from lalinference.imrtgr.nrutils import \ 

876 bbh_peak_luminosity_non_precessing_T1600018 as lal_T1600018 

877 

878 assert np.round( 

879 bbh_peak_luminosity_non_precessing_T1600018( 

880 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z 

881 ), 8 

882 ) == np.round( 

883 lal_T1600018( 

884 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z 

885 ), 8 

886 ) 

887 

888 def test_bbh_peak_luminosity_non_precessing_UIB2016(self): 

889 from lalinference.imrtgr.nrutils import \ 

890 bbh_peak_luminosity_non_precessing_UIB2016 as lal_UIB2016 

891 

892 assert np.round( 

893 bbh_peak_luminosity_non_precessing_UIB2016( 

894 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z 

895 ), 8 

896 ) == np.round( 

897 lal_UIB2016( 

898 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z 

899 ), 8 

900 ) 

901 

902 def test_bbh_final_spin_non_precessing_Healyetal(self): 

903 from lalinference.imrtgr.nrutils import \ 

904 bbh_final_spin_non_precessing_Healyetal as lal_Healyetal 

905 

906 assert np.round(self.final_spin, 8) == np.round( 

907 lal_Healyetal( 

908 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z, 

909 version="2016" 

910 ), 8 

911 ) 

912 

913 def test_bbh_final_mass_non_precessing_Healyetal(self): 

914 from lalinference.imrtgr.nrutils import \ 

915 bbh_final_mass_non_precessing_Healyetal as lal_Healyetal 

916 

917 assert np.round( 

918 bbh_final_mass_non_precessing_Healyetal( 

919 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z, 

920 final_spin=self.final_spin, version="2016" 

921 ), 8 

922 ) == np.round( 

923 lal_Healyetal( 

924 self.mass_1, self.mass_2, self.spin_1z, self.spin_2z, 

925 chif=self.final_spin, version="2016" 

926 ), 8 

927 ) 

928 

929 

930class TestConvert(object): 

931 """Test the pesummary.gw.conversions._Conversion class 

932 """ 

933 def setup_method(self): 

934 """Setup the TestConvert class 

935 """ 

936 self.dirs = [tmpdir] 

937 for dd in self.dirs: 

938 if not os.path.isdir(dd): 

939 os.mkdir(dd) 

940 

941 def teardown_method(self): 

942 """Remove the files and directories created from this class 

943 """ 

944 for dd in self.dirs: 

945 if os.path.isdir(dd): 

946 shutil.rmtree(dd) 

947 

948 @staticmethod 

949 def _convert(resume_file=None, **kwargs): 

950 """ 

951 """ 

952 np.random.seed(100) 

953 data = { 

954 "mass_1": np.random.uniform(10, 1000, 10000), 

955 "mass_2": np.random.uniform(2, 10, 10000), 

956 "spin_1z": np.random.uniform(0, 1, 10000), 

957 "spin_2z": np.random.uniform(0, 1, 10000) 

958 } 

959 converted = convert(data, resume_file=resume_file, **kwargs) 

960 return converted 

961 

962 def test_from_checkpoint(self): 

963 """Check that when restarted from checkpoint, the output is the same 

964 """ 

965 import time 

966 import multiprocessing 

967 from pesummary.io import read 

968 

969 t0 = time.time() 

970 no_checkpoint = self._convert() 

971 t1 = time.time() 

972 # check that when interrupted and restarted, the results are the same 

973 process = multiprocessing.Process( 

974 target=self._convert, args=["{}/checkpoint.pickle".format(tmpdir)] 

975 ) 

976 process.start() 

977 time.sleep(5) 

978 process.terminate() 

979 # check that not all samples have been made 

980 _checkpoint = read("{}/checkpoint.pickle".format(tmpdir), checkpoint=True) 

981 assert os.path.isfile("{}/checkpoint.pickle".format(tmpdir)) 

982 assert not all( 

983 param in _checkpoint.parameters for param in no_checkpoint.keys() 

984 ) 

985 # restart from checkpoint 

986 checkpoint = self._convert(resume_file="{}/checkpoint.pickle".format(tmpdir)) 

987 for param, value in no_checkpoint.items(): 

988 np.testing.assert_almost_equal(value, checkpoint[param]) 

989 

990 

991def test_evolve_angles_forwards(): 

992 """Check that the pesummary.gw.conversions.evolve.evolve_angles_forwards 

993 function works as expected 

994 """ 

995 from pesummary.gw.conversions.evolve import ( 

996 evolve_spins, _wrapper_for_evolve_angles_forwards 

997 ) 

998 from lalsimulation import ( 

999 SimInspiralSpinTaylorPNEvolveOrbit, SIM_INSPIRAL_SPINS_FLOW 

1000 ) 

1001 from lal import MTSUN_SI, MSUN_SI 

1002 import lalsimulation 

1003 input_data = [ 

1004 ( 

1005 146.41677334700145, 123.72830811599943, 0.4788630899579355, 

1006 0.4486656607260327, 2.121165143160338, 0.5194005460918241, 

1007 3.0144238369366736, 11., 11., "IMRPhenomPv3HM" 

1008 ), 

1009 ( 

1010 145.60646217583144, 97.70678957171464, 0.418373390477266, 

1011 0.22414039975174402, 2.2857994587400494, 2.730311388309907, 

1012 3.47318438014925, 11., 11., "IMRPhenomPv3HM" 

1013 ), 

1014 ( 

1015 144.27815417432444, 99.32482850107179, 0.3413842190485782, 

1016 0.12003981467617035, 2.4429395586527884, 0.9993057630904596, 

1017 1.422769967575501, 11., 11., "IMRPhenomPv3HM" 

1018 ), 

1019 ] 

1020 for num, sample in enumerate(input_data): 

1021 tilt_1_evol, tilt_2_evol, phi_12_evol = evolve_spins( 

1022 *sample, multi_process=1, evolve_limit="ISCO", dt=0.1, 

1023 evolution_approximant="SpinTaylorT5" 

1024 ) 

1025 _tilt_1_evol, _tilt_2_evol, _phi_12_evol = _wrapper_for_evolve_angles_forwards( 

1026 list(sample)[:7] + [11., 6 ** -0.5, 1e-3, 0.1, "SpinTaylorT5"] 

1027 ) 

1028 np.testing.assert_almost_equal(tilt_1_evol, _tilt_1_evol) 

1029 np.testing.assert_almost_equal(tilt_2_evol, _tilt_2_evol) 

1030 np.testing.assert_almost_equal(phi_12_evol, _phi_12_evol) 

1031 total_mass = (sample[0] + sample[1]) * MTSUN_SI 

1032 spinfreq_enum = SimInspiralGetSpinFreqFromApproximant( 

1033 getattr(lalsimulation, sample[-1]) 

1034 ) 

1035 f_start = float(np.where( 

1036 np.array(spinfreq_enum == SIM_INSPIRAL_SPINS_FLOW), sample[-3], 

1037 sample[-2] 

1038 )) 

1039 f_final = (6 ** -0.5) ** 3 / (total_mass * np.pi) 

1040 _approx = lalsimulation.SpinTaylorT5 

1041 data = SimInspiralSpinTaylorPNEvolveOrbit( 

1042 deltaT=0.1 * total_mass, m1=sample[0] * MSUN_SI, 

1043 m2=sample[1] * MSUN_SI, fStart=f_start, fEnd=f_final, 

1044 s1x=sample[2] * np.sin(sample[4]), s1y=0., 

1045 s1z=sample[2] * np.cos(sample[4]), 

1046 s2x=sample[3] * np.sin(sample[5]) * np.cos(sample[6]), 

1047 s2y=sample[3] * np.sin(sample[5]) * np.sin(sample[6]), 

1048 s2z=sample[3] * np.cos(sample[5]), lnhatx=0., lnhaty=0., lnhatz=1., 

1049 e1x=1., e1y=0., e1z=0., lambda1=0., lambda2=0., quadparam1=1., 

1050 quadparam2=1., spinO=6, tideO=0, phaseO=7, lscorr=0, 

1051 approx=_approx 

1052 ) 

1053 a_1_evolve = np.array( 

1054 [ 

1055 data[2].data.data[-1], data[3].data.data[-1], 

1056 data[4].data.data[-1] 

1057 ] 

1058 ) 

1059 a_2_evolve = np.array( 

1060 [ 

1061 data[5].data.data[-1], data[6].data.data[-1], 

1062 data[7].data.data[-1] 

1063 ] 

1064 ) 

1065 Ln_evolve = np.array( 

1066 [ 

1067 data[8].data.data[-1], data[9].data.data[-1], 

1068 data[10].data.data[-1] 

1069 ] 

1070 ) 

1071 # code taken from https://git.ligo.org/lscsoft/lalsuite/-/blob/master/ 

1072 # lalinference/bin/lalinference_evolve_spins_and_append_samples.py#L26-53 

1073 # to test 

1074 # pesummary.gw.conversions.tilt_angles_and_phi_12_from_spin_vectors_and_L 

1075 chi1_v_norm = np.linalg.norm(a_1_evolve) 

1076 chi2_v_norm = np.linalg.norm(a_2_evolve) 

1077 Ln_evolve /= np.linalg.norm(Ln_evolve) 

1078 chi1dL_v = np.dot(a_1_evolve, Ln_evolve) 

1079 chi2dL_v = np.dot(a_2_evolve, Ln_evolve) 

1080 chi1inplane = a_1_evolve - chi1dL_v * Ln_evolve 

1081 chi2inplane = a_2_evolve - chi2dL_v * Ln_evolve 

1082 cos_tilt1 = chi1dL_v / chi1_v_norm 

1083 cos_tilt2 = chi2dL_v / chi2_v_norm 

1084 cos_phi12 = np.dot(chi1inplane, chi2inplane) / ( 

1085 np.linalg.norm(chi1inplane) * np.linalg.norm(chi2inplane) 

1086 ) 

1087 phi12_evol_i = np.arccos(cos_phi12) 

1088 if np.sign(np.dot(Ln_evolve, np.cross(a_1_evolve, a_2_evolve))) < 0: 

1089 phi12_evol_i = 2. * np.pi - phi12_evol_i 

1090 tilt_1_evol_true = np.arccos(cos_tilt1) 

1091 tilt_2_evol_true = np.arccos(cos_tilt2) 

1092 phi_12_evol_true = phi12_evol_i 

1093 np.testing.assert_almost_equal(tilt_1_evol, tilt_1_evol_true) 

1094 np.testing.assert_almost_equal(tilt_2_evol, tilt_2_evol_true) 

1095 np.testing.assert_almost_equal(phi_12_evol, phi_12_evol_true) 

1096 

1097 

1098def test_evolve_angles_backwards(): 

1099 """Check that the pesummary.gw.conversions.evolve.evolve_angles_backwards 

1100 function works as expected 

1101 """ 

1102 from pesummary.gw.conversions.evolve import evolve_spins 

1103 from lal import MSUN_SI 

1104 from packaging import version 

1105 import lalsimulation 

1106 input_data = [ 

1107 ( 

1108 1.3862687342652575e+32, 1.5853186050191907e+31, 0.8768912154180827, 

1109 0.9635416612042661, 2.8861591668037119, 2.7423707262813442, 

1110 4.750537251642867, 8.000000000000000, 8.000000000000000, "IMRPhenomXPHM", 

1111 2.8861301463160993, 2.7425208816155378, 2.8861542118177956, 

1112 2.7426347054696230, 'v1' 

1113 ), 

1114 ( 

1115 4.0380177255695994e+31, 2.1111685497317552e+31, 0.9442047756726544, 

1116 0.2197148251155545, 2.7060072810080551, 0.8920951236808333, 

1117 1.7330264974887994, 14.0000000000000, 14.0000000000000, "IMRPhenomXPHM", 

1118 2.7082295817672812, 0.8821772303625787, 2.7084623305332132, 

1119 0.8811491121799003, 'v1' 

1120 ), 

1121 ( 

1122 1.4778236544770486e+32, 2.6197742077777032e+31, 0.4650532384488123, 

1123 0.4135203147241133, 2.5477872046486589, 1.3374887745402186, 

1124 5.8300235171959054, 15.0000000000000, 15.0000000000000, "IMRPhenomXPHM", 

1125 2.5307614455226255, 1.3999636874375283, 2.5310639813744329, 

1126 1.4020896531141123, 'v1' 

1127 ) 

1128 ] 

1129 for num, method in enumerate(["precession_averaged", "hybrid_orbit_averaged"]): 

1130 for sample in input_data: 

1131 _sample = np.array(sample[:9]) 

1132 _sample[:2] = _sample[:2] / MSUN_SI 

1133 tilt_1_inf, tilt_2_inf, phi_12 = evolve_spins( 

1134 *_sample, sample[9], method=method, multi_process=1, 

1135 evolve_limit="infinite_separation", version=sample[-1], 

1136 approx="SpinTaylorT5" 

1137 ) 

1138 if num == 0: 

1139 np.testing.assert_almost_equal(tilt_1_inf, sample[10], 5) 

1140 np.testing.assert_almost_equal(tilt_2_inf, sample[11], 5) 

1141 else: 

1142 np.testing.assert_almost_equal(tilt_1_inf, sample[12], 5) 

1143 np.testing.assert_almost_equal(tilt_2_inf, sample[13], 5)