Coverage for /home/andrew/Documents/Research/gwent/gwent/waveform.py: 99%

# Coverage for /home/andrew/Documents/Research/gwent/gwent/waveform.py : 99%

## 161 statements 159 run 2 missing 0 excluded

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1 import numpy as np

2

3

4 def Get_Waveform(source, pct_of_peak=0.01):

5 """Uses Mass Ratio (q <= 18), aligned spins (abs(a/m)~0.85 or when q=1 abs(a/m)<0.98),

6 fitting coefficients for QNM type, and sampling rate

7 Returns the frequency, the Phenom amplitude of the inspiral-merger-ringdown

8 Uses methods found in <https://arxiv.org/abs/1508.07253> and <https://arxiv.org/abs/1508.07250>

9

10 Parameters

11 ----------

12 source : object

13 source object from StrainandNoise, contains all source parameters

14 pct_of_peak : float, optional

15 the percentange of the strain at merger that dictates the maximum frequency the waveform is calculated at in geometrized units (G=c=1)

16

17 Returns

18 -------

19 Mf : numpy array of floats

20 the waveform frequencies in geometrized units (G=c=1)

21 fullwaveform : numpy array of floats

22 the waveform strain in geometrized units (G=c=1)

23

24 """

25 f_low = source.f_low

26 N = source.nfreqs

27 q = source.q

28 x1 = source.chi1

29 x2 = source.chi2

30 fitcoeffs = source._fitcoeffs

31

32 # M = m1+m2 #Total Mass

33 # q = m2/m1 #Mass Ratio: Paper tested up to 18

34 # eta = m1*m2/M**2 reduced mass: Paper tested up to 0.05 (q=18)

35 eta = q / (q + 1) ** 2

36 x_PN = chi_PN(eta, x1, x2) # PN reduced spin parameter

37 a_f = a_final(x1, x2, q, eta) # dimensionless spin

38

39 ##################

40 # Finds f_ringdown and f_damp from fit taken from <https://arxiv.org/abs/gr-qc/0512160>

41 n = 0 # QNM indices

42 l = 2

43 m = 2

44 numn = 3 # number of n's included in the table

45

46 index = (l - 2) * (2 * l + 1) * numn + (l - m) * numn + n

47 f_fit = fitcoeffs[index][3:6]

48 q_fit = fitcoeffs[index][6:9]

49

50 omega_RD = f_fit[0] + f_fit[1] * (1 - a_f) ** f_fit[2] # M omega_{lmn}

51 tau = (

52 2 * (q_fit[0] + q_fit[1] * (1 - a_f) ** q_fit[2]) / omega_RD

53 ) # tau_{lmn}/M = 2 Q_{lmn}/(M omega_{lmn})

54 ########################

55 f_RD = omega_RD / 2 / np.pi

56 f_damp = 1 / tau / 2 / np.pi

57

58 Gamma1 = Lambda(eta, x_PN, 4)

59 Gamma2 = Lambda(eta, x_PN, 5)

60 Gamma3 = Lambda(eta, x_PN, 6)

61

62 f_peak = Calc_f_peak(f_RD, f_damp, [Gamma1, Gamma2, Gamma3])

63

64 f1 = 0.014

65 f3 = f_peak

66 f2 = (f1 + f3) / 2

67

68 cutoffFreq = Find_Cutoff_Freq(

69 f_RD, f_damp, [Gamma1, Gamma2, Gamma3], pct_of_peak=pct_of_peak

70 )

71

72 # If lowest frequency is greater than cutoffFreq, then raise error.

73 if f_low >= cutoffFreq:

74 raise ValueError(

75 "Lower frequency bound (ie. f_low) must be lower than that of the merger ringdown."

76 )

77

78 Mf = np.logspace(np.log10(f_low), np.log10(cutoffFreq), N)

79

80 v1 = A_insp(f1, eta, x1, x2, x_PN)

81 v2 = Lambda(eta, x_PN, 3)

82 v3 = A_MR(f3, f_RD, f_damp, [Gamma1, Gamma2, Gamma3])

83 fund1 = DA_insp(f1, eta, x1, x2, x_PN)

84 fund3 = DA_MR(f3, f_RD, f_damp, [Gamma1, Gamma2, Gamma3])

85

86 #############################

87 # Calculate Solutions to eqn 21 in intermediate region

88 Del_solns = A_intermediate(

89 f1, f2, f3, v1, v2, v3, fund1, fund3

90 ) # Solutions to eqn 21

91

92 ##############################

93 # Calculate all sections of waveform and Paste together

94 indxf1 = np.argmin(np.abs(Mf - f1))

95 indxfpeak = np.argmin(np.abs(Mf - f_peak))

96

97 tmpinspiral = A_norm(Mf[0 : indxf1 + 1], eta) * A_insp(

98 Mf[0 : indxf1 + 1], eta, x1, x2, x_PN

99 )

100 tmpintermediate = A_norm(Mf[indxf1 + 1 : indxfpeak], eta) * A_int(

101 Mf[indxf1 + 1 : indxfpeak], Del_solns

102 )

103 tmpmergerringdown = A_norm(Mf[indxfpeak:], eta) * A_MR(

104 Mf[indxfpeak:], f_RD, f_damp, [Gamma1, Gamma2, Gamma3]

105 )

106 fullwaveform = np.hstack((tmpinspiral, tmpintermediate, tmpmergerringdown))

107

108 return [Mf, fullwaveform]

109

110

111 def A_norm(freqs, eta):

112 """Calculates the constant scaling factor A_0

113

114 Parameters

115 ----------

116 freqs : array

117 The frequencies in Natural units (Mf, G=c=1) of the waveform

118 eta : float

119 The reduced mass ratio

120

121 """

122 const = np.sqrt(2 * eta / 3 / np.pi ** (1 / 3))

123 return const * freqs ** -(7 / 6)

124

125

126 def A_insp(freqs, eta, x1, x2, X_PN):

127 """Calculates the Inspiral Amplitude

128

129 Parameters

130 ----------

131 freqs : array

132 The frequencies in Natural units (Mf, G=c=1) of the waveform

133 eta : float

134 The reduced mass ratio

135 x1 : float

136 The dimensionless spin parameter abs(a/m) for black hole m1.

137 x2 : float

138 The dimensionless spin parameter abs(a/m) for black hole m2.

139 x_PN : float

140 The PN reduced spin parameter

141

142 """

143 A_PN = 0.0

144 A_higher = 0.0

145 for i in range(7):

146 A_PN = A_PN + PN_coeffs(eta, x1, x2, i) * (np.pi * freqs) ** (i / 3)

147 if i >= 1 and i <= 3:

148 A_higher = A_higher + Lambda(eta, X_PN, i - 1) * freqs ** ((6 + i) / 3)

149 return A_PN + A_higher

150

151

152 def DA_insp(freqs, eta, x1, x2, X_PN):

153 """Calculates Derivative of the inspiral amplitude.

154

155 Parameters

156 ----------

157 freqs : array

158 The frequencies in Natural units (Mf, G=c=1) of the waveform

159 eta : float

160 The reduced mass ratio

161 x1 : float

162 The dimensionless spin parameter abs(a/m) for black hole m1.

163 x2 : float

164 The dimensionless spin parameter abs(a/m) for black hole m2.

165 x_PN : float

166 The PN reduced spin parameter

167

168 """

169 DA_PN = 0.0

170 DA_higher = 0.0

171 for i in range(7):

172 PN_const = np.pi ** (i / 3) * (i / 3) * PN_coeffs(eta, x1, x2, i)

173 DA_PN = DA_PN + PN_const * (freqs) ** ((i - 3) / 3)

174 if i >= 1 and i <= 3:

175 higher_const = ((6 + i) / 3) * Lambda(eta, X_PN, i - 1)

176 DA_higher = DA_higher + higher_const * freqs ** ((i + 3) / 3)

177

178 return DA_PN + DA_higher

179

180

181 def A_MR(freqs, f_RD, f_damp, Gammas):

182 """Calculates the Normalized Merger-Ringdown Amplitude

183

184 Parameters

185 ----------

186 freqs : array

187 The frequencies in Natural units (Mf, G=c=1) of the waveform

188 f_RD : float

189 Frequency of the Ringdown transition

190 f_damp : float

191 Damping frequency

192 Gammas : array-like

193 Normalizes lorentzian to correct shape

194

195 """

196 varf = freqs - f_RD

197 fg_d = Gammas[2] * f_damp

198 return (

199 (Gammas[0] * fg_d)

200 / (varf ** 2 + fg_d ** 2)

201 * np.exp(-(Gammas[1] / fg_d) * varf)

202 )

203

204

205 def DA_MR(freqs, f_RD, f_damp, Gammas):

206 """Calculates Derivative of the Merger-Ringdown Amplitude

207

208 Parameters

209 ----------

210 freqs : array

211 The frequencies in Natural units (Mf, G=c=1) of the waveform

212 f_RD : float

213 Frequency of the Ringdown transition

214 f_damp : float

215 Damping frequency

216 Gammas : array-like

217 Normalizes lorentzian to correct shape

218

219 """

220 varf = freqs - f_RD

221 fg_d = Gammas[2] * f_damp

222 A_MR_0 = A_MR(freqs, f_RD, f_damp, Gammas)

223 return -A_MR_0 * (2 * varf / (varf ** 2 + fg_d ** 2) + Gammas[1] / fg_d)

224

225

226 def A_intermediate(f1, f2, f3, v1, v2, v3, d1, d3):

227 """Solves system of equations for intermediate amplitude matching"""

228 Mat = np.array(

229 [

230 [1.0, f1, f1 ** 2, f1 ** 3, f1 ** 4],

231 [1.0, f2, f2 ** 2, f2 ** 3, f2 ** 4],

232 [1.0, f3, f3 ** 2, f3 ** 3, f3 ** 4],

233 [0.0, 1.0, 2 * f1, 3 * f1 ** 2, 4 * f1 ** 3],

234 [0.0, 1.0, 2 * f3, 3 * f3 ** 2, 4 * f3 ** 3],

235 ],

236 dtype="float",

237 )

238 a = np.array([v1, v2, v3, d1, d3], dtype="float")

239 return np.linalg.solve(Mat, a)

240

241

242 def A_int(freqs, delt):

243 """Calculates the Intermediate Amplitude

244

245 Parameters

246 ----------

247 freqs : array

248 The frequencies in Natural units (Mf, G=c=1) of the waveform

249 delt : array

250 Coefficient solutions to match the inspiral to the merger-ringdown portion of the waveform

251

252 """

253 return (

254 delt[0]

255 + delt[1] * freqs

256 + delt[2] * freqs ** 2

257 + delt[3] * freqs ** 3

258 + delt[4] * freqs ** 4

259 )

260

261

262 def Lambda(eta, x_PN, lmbda):

263 """Gets the Lambdas from Eqn 31 in <https://arxiv.org/abs/1508.07253>

264

265 Parameters

266 ----------

267 eta : float

268 The reduced mass ratio

269 x_PN : float

270 The PN reduced spin parameter

271 lmbda : int

272 Iterator for different Lambda variables using the zeta function

273

274 """

275 xi = x_PN - 1

276 xi2 = xi * xi

277 xi3 = xi2 * xi

278 eta2 = eta * eta

279 if lmbda == 0: # rho1

280 coeffs = zeta(0)

281 elif lmbda == 1: # rho2

282 coeffs = zeta(1)

283 elif lmbda == 2: # rho3

284 coeffs = zeta(2)

285 elif lmbda == 3: # v2

286 coeffs = zeta(3)

287 elif lmbda == 4: # gamma1

288 coeffs = zeta(4)

289 elif lmbda == 5: # gamma2

290 coeffs = zeta(5)

291 elif lmbda == 6: # gamma3

292 coeffs = zeta(6)

293

294 return (

295 coeffs[0]

296 + coeffs[1] * eta

297 + (coeffs[2] + coeffs[3] * eta + coeffs[4] * eta2) * xi

298 + (coeffs[5] + coeffs[6] * eta + coeffs[7] * eta2) * xi2

299 + (coeffs[8] + coeffs[9] * eta + coeffs[10] * eta2) * xi3

300 )

301

302

303 def zeta(k):

304 """Coefficients in table 5 of <https://arxiv.org/abs/1508.07253>"""

305 if k == 0: # rho 1

306 coeffs = [

307 3931.9,

308 -17395.8,

309 3132.38,

310 343966.0,

311 -1.21626e6,

312 -70698.0,

313 1.38391e6,

314 -3.96628e6,

315 -60017.5,

316 803515.0,

317 -2.09171e6,

318 ]

319 elif k == 1: # rho 2

320 coeffs = [

321 -40105.5,

322 112253.0,

323 23561.7,

324 -3.47618e6,

325 1.13759e7,

326 754313.0,

327 -1.30848e7,

328 3.64446e7,

329 596227.0,

330 -7.42779e6,

331 1.8929e7,

332 ]

333 elif k == 2: # rho 3

334 coeffs = [

335 83208.4,

336 -191238.0,

337 -210916.0,

338 8.71798e6,

339 -2.69149e7,

340 -1.98898e6,

341 3.0888e7,

342 -8.39087e7,

343 -1.4535e6,

344 1.70635e7,

345 -4.27487e7,

346 ]

347 elif k == 3: # v 2

348 coeffs = [

349 0.814984,

350 2.57476,

351 1.16102,

352 -2.36278,

353 6.77104,

354 0.757078,

355 -2.72569,

356 7.11404,

357 0.176693,

358 -0.797869,

359 2.11624,

360 ]

361 elif k == 4: # gamma 1

362 coeffs = [

363 0.0069274,

364 0.0302047,

365 0.00630802,

366 -0.120741,

367 0.262716,

368 0.00341518,

369 -0.107793,

370 0.27099,

371 0.000737419,

372 -0.0274962,

373 0.0733151,

374 ]

375 elif k == 5: # gamma 2

376 coeffs = [

377 1.01034,

378 0.000899312,

379 0.283949,

380 -4.04975,

381 13.2078,

382 0.103963,

383 -7.02506,

384 24.7849,

385 0.030932,

386 -2.6924,

387 9.60937,

388 ]

389 elif k == 6: # gamma 3

390 coeffs = [

391 1.30816,

392 -0.00553773,

393 -0.0678292,

394 -0.668983,

395 3.40315,

396 -0.0529658,

397 -0.992379,

398 4.82068,

399 -0.00613414,

400 -0.384293,

401 1.75618,

402 ]

403 return coeffs

404

405

406 def PN_coeffs(eta, x1, x2, i):

407 """Gets the PN Amplitude coefficients

408

409 Parameters

410 ----------

411 eta : float

412 The reduced mass ratio

413 x1 : float

414 The dimensionless spin parameter abs(a/m) for black hole m1.

415 x2 : float

416 The dimensionless spin parameter abs(a/m) for black hole m2.

417 q : float

418 The mass ratio m1/m2, m1<=m2

419 i : int

420 iterator to dictate which PN Amplitude to use

421

422 Notes

423 -----

424 Coefficients in appendix B (eqns B14-B20) of <https://arxiv.org/abs/1508.07253>

425

426 """

427 delta = np.sqrt(1.0 - 4.0 * eta)

428 chi_s = (x1 + x2) / 2.0

429 chi_a = (x1 - x2) / 2.0

430 if i == 0:

431 A_i = 1

432 elif i == 1:

433 A_i = 0

434 elif i == 2:

435 A_i = -323 / 224 + (451 / 168) * eta

436 elif i == 3:

437 A_i = (27 / 8) * delta * chi_a + (27 / 8 - (11 / 6) * eta) * chi_s

438 elif i == 4:

439 A_i = (

440 -27312085 / 8128512

441 - (1975055 / 338688) * eta

442 + (105271 / 24192) * eta ** 2

443 + (-81 / 32 + 8 * eta) * chi_a ** 2

444 - 81 / 16 * delta * chi_a * chi_s

445 + (-81 / 32 + 17 / 8 * eta) * chi_s ** 2

446 )

447 elif i == 5:

448 A_i = (

449 -85 * np.pi / 64

450 + 85 * np.pi / 16 * eta

451 + (285197 / 16128 - 1579 / 4032 * eta) * delta * chi_a

452 + (285197 / 16128 - 15317 / 672 * eta - 2227 / 1008 * eta ** 2) * chi_s

453 )

454 elif i == 6:

455 A_i = (

456 -177520268561 / 8583708672

457 + (545384828789 / 5007163392 - 205 * np.pi ** 2 / 48) * eta

458 - 3248849057 / 178827264 * eta ** 2

459 + 34473079 / 6386688 * eta ** 3

460 + (1614569 / 64512 - 1873643 / 16128 * eta + 2167 / 42 * eta ** 2)

461 * chi_a ** 2

462 + (31 * np.pi / 12 - 7 * np.pi / 3 * eta) * chi_s

463 + (1614569 / 64512 - 61391 / 1344 * eta + 57451 / 4032 * eta ** 2)

464 * chi_s ** 2

465 + delta

466 * chi_a

467 * (31 * np.pi / 12 + (1614569 / 32256 - 165961 / 2688 * eta) * chi_s)

468 )

469 return A_i

470

471

472 def Calc_f_peak(f_RD, f_damp, Gammas):

473 """Calculates the frequency at the peak of the merger

474

475 Parameters

476 ----------

477 f_RD : float

478 Frequency of the Ringdown transition

479 f_damp : float

480 Damping frequency

481 Gammas : array-like

482 Normalizes lorentzian to correct shape

483

484 Notes

485 -----

486 There is a problem with this expression from the paper becoming imaginary if gamma2 >= 1

487 so if gamma2 >= 1 then set the square root term to zero.

488

489 """

490 if Gammas[1] <= 1:

491 f_max = np.abs(

492 f_RD + f_damp * Gammas[2] * (np.sqrt(1 - Gammas[1] ** 2) - 1) / Gammas[1]

493 )

494 else:

495 f_max = np.abs(f_RD + (f_damp * Gammas[2] * -1) / Gammas[1])

496 return f_max

497

498

499 def Find_Cutoff_Freq(f_RD, f_damp, Gammas, pct_of_peak=0.0001):

500 """Cutoff signal when the amplitude is a factor of 10 below the value at f_RD

501

502 Parameters

503 ----------

504 f_RD : float

505 Frequency of the Ringdown transition

506 f_damp : float

507 Damping frequency

508 Gammas : array-like

509 Normalizes lorentzian to correct shape

510

511 pct_of_peak : float, optional

512 the percentange of the strain at merger that dictates the maximum

513 frequency the waveform is calculated at in geometrized units (G=c=1)

514

515 """

516 tempfreqs = np.logspace(np.log10(f_RD), np.log10(10 * f_RD), 100)

517 cutoffAmp = pct_of_peak * A_MR(

518 f_RD, f_RD, f_damp, [Gammas[0], Gammas[1], Gammas[2]]

519 )

520 merger_ringdown_Amp = A_MR(

521 tempfreqs, f_RD, f_damp, [Gammas[0], Gammas[1], Gammas[2]]

522 )

523 cutoffindex = np.argmin(np.abs(cutoffAmp - merger_ringdown_Amp))

524 return tempfreqs[cutoffindex]

525

526

527 def a_final(x1, x2, q, eta):

528 """The Final spin of the binary remnant black hole

529

530 Parameters

531 ----------

532 x1 : float

533 The dimensionless spin parameter abs(a/m) for black hole m1.

534 x2 : float

535 The dimensionless spin parameter abs(a/m) for black hole m2.

536 q : float

537 The mass ratio m1/m2, m1<=m2

538 eta : float

539 The reduced mass ratio

540

541 Notes

542 -----

543 Uses eq. 3 in <https://arxiv.org/abs/0904.2577>, changed to match our q convention

544 a=J/M**2 where J = x1*m1**2 + x2*m2**2

545

546 """

547 a = (q ** 2 * x1 + x2) / (q ** 2 + 1)

548 s4 = -0.1229

549 s5 = 0.4537

550 t0 = -2.8904

551 t2 = -3.5171

552 t3 = 2.5763

553 return (

554 a

555 + s4 * a ** 2 * eta

556 + s5 * a * eta ** 2

557 + t0 * a * eta

558 + 2 * np.sqrt(3) * eta

559 + t2 * eta ** 2

560 + t3 * eta ** 3

561 )

562

563

564 def chi_PN(eta, x1, x2):

565 """Calculates the PN reduced spin parameter

566

567 Parameters

568 ----------

569 eta : float

570 The reduced mass ratio

571 x1 : float

572 The dimensionless spin parameter abs(a/m) for black hole m1.

573 x2 : float

574 The dimensionless spin parameter abs(a/m) for black hole m2.

575

576 Notes

577 -----

578 See Eq 5.9 in <https://arxiv.org/abs/1107.1267v2>

579

580 """

581 delta = np.sqrt(1.0 - 4.0 * eta)

582 chi_s = (x1 + x2) / 2.0

583 chi_a = (x1 - x2) / 2.0

584 return chi_s * (1.0 - eta * 76.0 / 113.0) + delta * chi_a