def calculate_ethinca_metric_comps(metricParams, ethincaParams, mass1, mass2, spin1z=0., spin2z=0., full_ethinca=True): """ Calculate the Gamma components needed to use the ethinca metric. At present this outputs the standard TaylorF2 metric over the end time and chirp times \tau_0 and \tau_3. A desirable upgrade might be to use the \chi coordinates [defined WHERE?] for metric distance instead of \tau_0 and \tau_3. The lower frequency cutoff is currently hard-coded to be the same as the bank layout options fLow and f0 (which must be the same as each other). Parameters ----------- metricParams : metricParameters instance Structure holding all the options for construction of the metric and the eigenvalues, eigenvectors and covariance matrix needed to manipulate the space. ethincaParams : ethincaParameters instance Structure holding options relevant to the ethinca metric computation. mass1 : float Mass of the heavier body in the considered template. mass2 : float Mass of the lighter body in the considered template. spin1z : float (optional, default=0) Spin of the heavier body in the considered template. spin2z : float (optional, default=0) Spin of the lighter body in the considered template. full_ethinca : boolean (optional, default=True) If True calculate the ethinca components in all 3 directions (mass1, mass2 and time). If False calculate only the time component (which is stored in Gamma0). Returns -------- fMax_theor : float Value of the upper frequency cutoff given by the template parameters and the cutoff formula requested. gammaVals : numpy_array Array holding 6 independent metric components in (end_time, tau_0, tau_3) coordinates to be stored in the Gamma0-5 slots of a SnglInspiral object. """ if (float(spin1z) != 0. or float(spin2z) != 0.) and full_ethinca: raise NotImplementedError("Ethinca cannot at present be calculated " "for nonzero component spins!") f0 = metricParams.f0 if f0 != metricParams.fLow: raise ValueError("If calculating ethinca the bank f0 value must be " "equal to f-low!") if ethincaParams.fLow is not None and (ethincaParams.fLow != metricParams.fLow): raise NotImplementedError("An ethinca metric f-low different from the" " bank metric f-low is not supported!") twicePNOrder = ethinca_order_from_string(ethincaParams.pnOrder) piFl = PI * f0 totalMass, eta = pnutils.mass1_mass2_to_mtotal_eta(mass1, mass2) totalMass = totalMass * MTSUN_SI v0cube = totalMass * piFl v0 = v0cube**(1. / 3.) # Get theoretical cutoff frequency and work out the closest # frequency for which moments were calculated fMax_theor = pnutils.frequency_cutoff_from_name(ethincaParams.cutoff, mass1, mass2, spin1z, spin2z) fMaxes = metricParams.moments['J4'].keys() fMaxIdx = abs(numpy.array(fMaxes, dtype=float) - fMax_theor).argmin() fMax = fMaxes[fMaxIdx] # Set the appropriate moments Js = numpy.zeros([18, 3], dtype=float) for i in range(18): Js[i, 0] = metricParams.moments['J%d' % (i)][fMax] Js[i, 1] = metricParams.moments['log%d' % (i)][fMax] Js[i, 2] = metricParams.moments['loglog%d' % (i)][fMax] # Compute the time-dependent metric term. two_pi_flower_sq = TWOPI * f0 * TWOPI * f0 gammaVals = numpy.zeros([6], dtype=float) gammaVals[0] = 0.5 * two_pi_flower_sq * \ ( Js[(1,0)] - (Js[(4,0)]*Js[(4,0)]) ) # If mass terms not required stop here if not full_ethinca: return fMax_theor, gammaVals # 3pN is a mess, so split it into pieces a0 = 11583231236531 / 200286535680 - 5 * PI * PI - 107 * GAMMA / 14 a1 = (-15737765635 / 130056192 + 2255 * PI * PI / 512) * eta a2 = (76055 / 73728) * eta * eta a3 = (-127825 / 55296) * eta * eta * eta alog = numpy.log(4 * v0) # Log terms are tricky - be careful # Get the Psi coefficients Psi = [{}, {}] #Psi = numpy.zeros([2,8,2],dtype=float) Psi[0][0, 0] = 3 / 5 Psi[0][2, 0] = (743 / 756 + 11 * eta / 3) * v0 * v0 Psi[0][3, 0] = 0. Psi[0][4,0] = (-3058673/508032 + 5429*eta/504 + 617*eta*eta/24)\ *v0cube*v0 Psi[0][5, 1] = (-7729 * PI / 126) * v0cube * v0 * v0 / 3 Psi[0][6,0] = (128/15)*(-3*a0 - a1 + a2 + 3*a3 + 107*(1+3*alog)/14)\ *v0cube*v0cube Psi[0][6, 1] = (6848 / 35) * v0cube * v0cube / 3 Psi[0][7, 0] = (-15419335 / 63504 - 75703 * eta / 756) * PI * v0cube * v0cube * v0 Psi[1][0, 0] = 0. Psi[1][2, 0] = (3715 / 12096 - 55 * eta / 96) / PI / v0 Psi[1][3, 0] = -3 / 2 Psi[1][4,0] = (15293365/4064256 - 27145*eta/16128 - 3085*eta*eta/384)\ *v0/PI Psi[1][5, 1] = (193225 / 8064) * v0 * v0 / 3 Psi[1][6,0] = (4/PI)*(2*a0 + a1/3 - 4*a2/3 - 3*a3 -107*(1+6*alog)/42)\ *v0cube Psi[1][6, 1] = (-428 / PI / 7) * v0cube / 3 Psi[1][7,0] = (77096675/1161216 + 378515*eta/24192 + 74045*eta*eta/8064)\ *v0cube*v0 # Set the appropriate moments Js = numpy.zeros([18, 3], dtype=float) for i in range(18): Js[i, 0] = metricParams.moments['J%d' % (i)][fMax] Js[i, 1] = metricParams.moments['log%d' % (i)][fMax] Js[i, 2] = metricParams.moments['loglog%d' % (i)][fMax] # Calculate the g matrix PNterms = [(0, 0), (2, 0), (3, 0), (4, 0), (5, 1), (6, 0), (6, 1), (7, 0)] PNterms = [term for term in PNterms if term[0] <= twicePNOrder] # Now can compute the mass-dependent gamma values for m in [0, 1]: for k in PNterms: gammaVals[1+m] += 0.5 * two_pi_flower_sq * Psi[m][k] * \ ( Js[(9-k[0],k[1])] - Js[(12-k[0],k[1])] * Js[(4,0)] ) g = numpy.zeros([2, 2], dtype=float) for (m, n) in [(0, 0), (0, 1), (1, 1)]: for k in PNterms: for l in PNterms: g[m,n] += Psi[m][k] * Psi[n][l] * \ ( Js[(17-k[0]-l[0], k[1]+l[1])] - Js[(12-k[0],k[1])] * Js[(12-l[0],l[1])] ) g[m, n] = 0.5 * two_pi_flower_sq * g[m, n] g[n, m] = g[m, n] gammaVals[3] = g[0, 0] gammaVals[4] = g[0, 1] gammaVals[5] = g[1, 1] return fMax_theor, gammaVals
def calculate_ethinca_metric_comps(metricParams, ethincaParams, mass1, mass2, spin1z=0., spin2z=0., full_ethinca=True): """ Calculate the Gamma components needed to use the ethinca metric. At present this outputs the standard TaylorF2 metric over the end time and chirp times \tau_0 and \tau_3. A desirable upgrade might be to use the \chi coordinates [defined WHERE?] for metric distance instead of \tau_0 and \tau_3. The lower frequency cutoff is currently hard-coded to be the same as the bank layout options fLow and f0 (which must be the same as each other). Parameters ----------- metricParams : metricParameters instance Structure holding all the options for construction of the metric and the eigenvalues, eigenvectors and covariance matrix needed to manipulate the space. ethincaParams : ethincaParameters instance Structure holding options relevant to the ethinca metric computation. mass1 : float Mass of the heavier body in the considered template. mass2 : float Mass of the lighter body in the considered template. spin1z : float (optional, default=0) Spin of the heavier body in the considered template. spin2z : float (optional, default=0) Spin of the lighter body in the considered template. full_ethinca : boolean (optional, default=True) If True calculate the ethinca components in all 3 directions (mass1, mass2 and time). If False calculate only the time component (which is stored in Gamma0). Returns -------- fMax_theor : float Value of the upper frequency cutoff given by the template parameters and the cutoff formula requested. gammaVals : numpy_array Array holding 6 independent metric components in (end_time, tau_0, tau_3) coordinates to be stored in the Gamma0-5 slots of a SnglInspiral object. """ if (float(spin1z) != 0. or float(spin2z) != 0.) and full_ethinca: raise NotImplementedError("Ethinca cannot at present be calculated " "for nonzero component spins!") f0 = metricParams.f0 if f0 != metricParams.fLow: raise ValueError("If calculating ethinca the bank f0 value must be " "equal to f-low!") if ethincaParams.fLow is not None and ( ethincaParams.fLow != metricParams.fLow): raise NotImplementedError("An ethinca metric f-low different from the" " bank metric f-low is not supported!") twicePNOrder = ethinca_order_from_string(ethincaParams.pnOrder) piFl = PI * f0 totalMass, eta = pnutils.mass1_mass2_to_mtotal_eta(mass1, mass2) totalMass = totalMass * MTSUN_SI v0cube = totalMass*piFl v0 = v0cube**(1./3.) # Get theoretical cutoff frequency and work out the closest # frequency for which moments were calculated fMax_theor = pnutils.frequency_cutoff_from_name( ethincaParams.cutoff, mass1, mass2, spin1z, spin2z) fMaxes = metricParams.moments['J4'].keys() fMaxIdx = abs(numpy.array(fMaxes,dtype=float) - fMax_theor).argmin() fMax = fMaxes[fMaxIdx] # Set the appropriate moments Js = numpy.zeros([18,3],dtype=float) for i in range(18): Js[i,0] = metricParams.moments['J%d'%(i)][fMax] Js[i,1] = metricParams.moments['log%d'%(i)][fMax] Js[i,2] = metricParams.moments['loglog%d'%(i)][fMax] # Compute the time-dependent metric term. two_pi_flower_sq = TWOPI * f0 * TWOPI * f0 gammaVals = numpy.zeros([6],dtype=float) gammaVals[0] = 0.5 * two_pi_flower_sq * \ ( Js[(1,0)] - (Js[(4,0)]*Js[(4,0)]) ) # If mass terms not required stop here if not full_ethinca: return fMax_theor, gammaVals # 3pN is a mess, so split it into pieces a0 = 11583231236531/200286535680 - 5*PI*PI - 107*GAMMA/14 a1 = (-15737765635/130056192 + 2255*PI*PI/512)*eta a2 = (76055/73728)*eta*eta a3 = (-127825/55296)*eta*eta*eta alog = numpy.log(4*v0) # Log terms are tricky - be careful # Get the Psi coefficients Psi = [{},{}] #Psi = numpy.zeros([2,8,2],dtype=float) Psi[0][0,0] = 3/5 Psi[0][2,0] = (743/756 + 11*eta/3)*v0*v0 Psi[0][3,0] = 0. Psi[0][4,0] = (-3058673/508032 + 5429*eta/504 + 617*eta*eta/24)\ *v0cube*v0 Psi[0][5,1] = (-7729*PI/126)*v0cube*v0*v0/3 Psi[0][6,0] = (128/15)*(-3*a0 - a1 + a2 + 3*a3 + 107*(1+3*alog)/14)\ *v0cube*v0cube Psi[0][6,1] = (6848/35)*v0cube*v0cube/3 Psi[0][7,0] = (-15419335/63504 - 75703*eta/756)*PI*v0cube*v0cube*v0 Psi[1][0,0] = 0. Psi[1][2,0] = (3715/12096 - 55*eta/96)/PI/v0; Psi[1][3,0] = -3/2 Psi[1][4,0] = (15293365/4064256 - 27145*eta/16128 - 3085*eta*eta/384)\ *v0/PI Psi[1][5,1] = (193225/8064)*v0*v0/3 Psi[1][6,0] = (4/PI)*(2*a0 + a1/3 - 4*a2/3 - 3*a3 -107*(1+6*alog)/42)\ *v0cube Psi[1][6,1] = (-428/PI/7)*v0cube/3 Psi[1][7,0] = (77096675/1161216 + 378515*eta/24192 + 74045*eta*eta/8064)\ *v0cube*v0 # Set the appropriate moments Js = numpy.zeros([18,3],dtype=float) for i in range(18): Js[i,0] = metricParams.moments['J%d'%(i)][fMax] Js[i,1] = metricParams.moments['log%d'%(i)][fMax] Js[i,2] = metricParams.moments['loglog%d'%(i)][fMax] # Calculate the g matrix PNterms = [(0,0),(2,0),(3,0),(4,0),(5,1),(6,0),(6,1),(7,0)] PNterms = [term for term in PNterms if term[0] <= twicePNOrder] # Now can compute the mass-dependent gamma values for m in [0, 1]: for k in PNterms: gammaVals[1+m] += 0.5 * two_pi_flower_sq * Psi[m][k] * \ ( Js[(9-k[0],k[1])] - Js[(12-k[0],k[1])] * Js[(4,0)] ) g = numpy.zeros([2,2],dtype=float) for (m,n) in [(0,0),(0,1),(1,1)]: for k in PNterms: for l in PNterms: g[m,n] += Psi[m][k] * Psi[n][l] * \ ( Js[(17-k[0]-l[0], k[1]+l[1])] - Js[(12-k[0],k[1])] * Js[(12-l[0],l[1])] ) g[m,n] = 0.5 * two_pi_flower_sq * g[m,n] g[n,m] = g[m,n] gammaVals[3] = g[0,0] gammaVals[4] = g[0,1] gammaVals[5] = g[1,1] return fMax_theor, gammaVals